<template>
  <component :is="SdConfigProvider" :get-popup-container="(a, b) => getPopupContainer(a, b)">
    <a-form-model ref="form" :class="$style.form" :model="model" :rules="rulesComputed">
      <!--
          @slot 详情表单内容
          @binding {object} model 整个表单的数据，可用于 v-model 绑定
          @binding {object} fields 后台返回的字段定义
        -->
      <portal v-for="s in selectors" :key="s.field" :to="`sd-selector-${s.field}${_uid}`">
        <a-button :class="$style.selectorBtn" @click="() => pickerData(s)">
          选择
        </a-button>
      </portal>
      <slot :model="model" :fields="fields"></slot>
      <sd-quill-reader v-if="description" :value="description" />
      <!-- 隐藏的form，给getDisplayValues使用 -->
      <sd-form
        v-if="!disableExtraForm && showExtraForm"
        ref="extraForm"
        :class="$style.extraForm"
        disable-extra-form
        :read-only="readOnly"
        :init-values="initValues"
      >
        <slot :model="model" :fields="fields"></slot>
      </sd-form>
    </a-form-model>
  </component>
</template>

<script>
import Vue from 'vue'
import moment from 'moment'
import booleanUtil from '@/common/services/boolean-util'
import { Input, InputNumber, Select, Radio, DatePicker, TimePicker, Checkbox } from 'ant-design-vue'
import SdUserPicker from '@/common/components/sd-user-picker.vue'
import SdGroupPicker from '@/common/components/sd-group-picker.vue'
import SdRolePicker from '@/common/components/sd-role-picker.vue'
import SdAttachment from '@/common/components/sd-attachment.vue'
import SdChildTable from '@/common/components/sd-child-table.vue'
import SdQuillReader from '@/common/components/sd-quill-reader.vue'
import sdOpinionView from '@/webflow/sd-opinion-view'
import SdSelectTree, { getLabelByValue } from '@/common/components/sd-select-tree'
import processArrayInStr from '@/common/components/sd-form/process-array-in-str'
import asyncComponent from '@/common/services/async-component'
import debounce from 'lodash.debounce'
import SdConfigProvider from '@/common/components/sd-config-provider.vue'
import sdRelationPicker from '@/common/components/sd-relation-picker.vue'
import pickValues from '@/common/services/pick-values'
import sdListPickerVue from '@/common/components/sd-list-picker.vue'
import axios from '../services/axios-instance'
import sdFormPublicMethodsNew from './sd-form/sd-form-public-methods-new'
import components from './_import-components/sd-form-import'

// 把自动适配的组件都全局注册一下，避免用component属性时找不到组件
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Select)
Vue.use(Radio)
Vue.use(DatePicker)
Vue.use(TimePicker)
Vue.use(Checkbox)

// 从字段信息中检测应该使用哪种组件显示
function getComponentSpec(field) {
  if (!field.attr) field.attr = {}
  if (field.attr.selectListItem) {
    // 数据字典的处理
    const spec = {
      props: {
        options: field.attr.selectListItem.map((item) => ({
          ...item,
          key: item.value,
        })),
      },
      getDisplayValue(value) {
        if (!Array.isArray(value)) value = [value]
        const opts = value.map((v) => {
          const opt = this.props.options.find((opt) => opt.value === v)
          return opt?.label
        })
        return opts.join(', ')
      },
    }
    const isMultiple = field.attr.multiple === 'true'
    if (isMultiple) {
      spec.dataType = 'array'
      spec.component = Checkbox.Group
      spec.props.mode = 'multiple'
      spec.parseBackendValue = (v) => v?.split(',')
      spec.toBackendValue = (v) => v?.join(',')
    } else {
      spec.component = Radio.Group
    }

    if (field.attr.selectListItem.length > (isMultiple ? 10 : 2)) {
      // 选择项过多时使用select
      spec.component = Select
    }

    return spec
  }

  switch (field.dataType.toLowerCase()) {
    case 'selectperson': {
      let hierarchical = field.attr.hierarchical
      if (field.attr.hierarchical !== undefined) {
        hierarchical = booleanUtil.isTruthy(field.attr.hierarchical)
      }
      if (field.attr.selectionType.toLowerCase() === 'group')
        return {
          component: SdGroupPicker,
          dataType: 'array',
          ...processArrayInStr,
          props: {
            single: field.attr?.multiple === 'false',
            rootNode: field.attr?.roots?.map((root) => {
              return { code: root.id, ...root }
            }),
            hierarchical,
          },
        }
      else if (field.attr.selectionType.toLowerCase() === 'role')
        return {
          component: SdRolePicker,
          dataType: 'array',
          ...processArrayInStr,
          props: {
            single: field.attr?.multiple === 'false',
          },
        }
      else
        return {
          component: SdUserPicker,
          dataType: 'array',
          ...processArrayInStr,
          props: {
            single: field.attr?.multiple === 'false',
            rootNode: field.attr?.roots?.map((root) => {
              return { code: root.id, ...root }
            }),
            hierarchical,
          },
        }
    }
    case 'int':
    case 'number':
    case 'long':
      return {
        component: InputNumber,
        dataType: 'number',
        props: {
          max: isNaN(parseFloat(field.attr.maxValue)) ? Infinity : parseFloat(field.attr.maxValue),
          min: isNaN(parseFloat(field.attr.minValue)) ? -Infinity : parseFloat(field.attr.minValue),
          precision: isNaN(parseInt(field.attr.decimalPrecision))
            ? null
            : parseInt(field.attr.decimalPrecision),
        },
        parseBackendValue: (v) => {
          if (isNaN(v)) {
            return undefined
          }
          return Number(v)
        },
        toBackendValue(v) {
          if (v === '') {
            return null
          }
          return v
        },
      }
    case 'date': {
      const showDate = field.attr.showDate !== 'false'
      const showTime = field.attr.showTime === 'true'
      let formatStr
      let props = {}
      if (!showDate) {
        formatStr = 'HH:mm'
        props = { minuteStep: 5, format: 'HH:mm' }
      } else if (showTime) {
        formatStr = 'YYYY-MM-DD HH:mm'
        props = {
          format: formatStr,
          showTime: { minuteStep: 5, format: 'HH:mm' },
        }
      } else {
        formatStr = 'YYYY-MM-DD'
      }
      return {
        component: showDate ? DatePicker : TimePicker,
        props,
        parseBackendValue: (v) => moment(Number(v)),
        toBackendValue: (v) => v.valueOf(),
        getDisplayValue(v, opt) {
          return v.format(opt?.inputProps.format || formatStr)
        },
        dataType: 'any',
      }
    }
    case 'attachment': {
      const {
        maxLength,
        maxFileSize,
        filenameMaxLength,
        allowType,
        uploadable,
        zwUploadable,
        sortable,
        attachments: initValue,
        infoProperties,
      } = field.attr
      return {
        component: SdAttachment,
        componentForDisplay: SdAttachment,
        props: {
          groupId: JSON.parse(field?.value || '{}').value,
          max: parseInt(maxLength) || Infinity,
          maxFileSize: parseInt(maxFileSize) || Infinity,
          filenameMaxLength: parseInt(filenameMaxLength) || Infinity,
          accept: allowType,
          uploadable: uploadable !== 'false',
          zwUploadable: zwUploadable !== 'false',
          sortable: sortable !== 'false',
          initValue,
          infoProperties,
          downloadBatch: true,
        },
        getItemText(v) {
          return v
            .filter((field) => field.CatNum !== '-3' && field.CatNum !== '-4')
            .map((field) => field.FileName)
            .join('\n')
        },
        dataType: 'array',
        parseBackendValue: () => [], // 附件接口返回值默认改成数组，防止校验类型错误
        toBackendValue: (v) => {
          return field.value
        },
      }
    }

    case 'opinion':
      return {
        parseBackendValue: (v) => {
          return JSON.parse(v).value.signContent || ''
        },
        toBackendValue: (v) => {
          let opinion = {
            attrs: {
              attachment: JSON.parse(field.value).value?.attrs?.attachment,
              replace: field.attr?.replace,
            },
          }
          if (v) {
            opinion = {
              signContent: v,
              ...opinion,
            }
          }
          return JSON.stringify({ value: opinion })
        },
      }
    case 'collection':
      return {
        component: SdChildTable,
        componentForDisplay: SdChildTable,
        dataType: 'array',
        parseBackendValue: (v) => {
          return (JSON.parse(v) || []).map((record) => {
            return { ...record, id: record.id || 'fakeId' + Math.random() }
          })
        },
        toBackendValue: (v) => {
          v.forEach((record) => {
            if (record.id?.toString().startsWith('fakeId')) {
              record.id = undefined
            }
          })
          return JSON.stringify(v)
        },
        props: {
          fields: field.attr.dync,
          label: field.caption,
          max: parseInt(field.attr.maxLength) || Infinity,
          min: parseInt(field.attr.minLength) || 0,
          formRelations: field.attr.formRelations,
          disableAd: field.attr.disableAd === 'true',
        },
      }
    case 'opinionview': {
      field.value = field.attr.opinions // 因为value默认为null值所以parsePackEndValue不执行，在这里直接改下，toBackendValue再弄回去
      const showDate = field.attr.showDate + '' !== 'false'
      const showTime = field.attr.showTime + '' === 'true'
      let formatStr
      if (!showDate) {
        formatStr = 'HH:mm'
      } else if (showTime) {
        formatStr = 'YYYY-MM-DD HH:mm'
      } else {
        formatStr = 'YYYY-MM-DD'
      }
      const props = { signTimeFormat: formatStr, showField: field.attr.showField }
      return {
        props,
        component: sdOpinionView,
        componentForDisplay: sdOpinionView,
        toBackendValue: () => null,
        dataType: 'any',
      }
    }
    case 'tinymce': {
      return {
        component: asyncComponent(() =>
          import(
            /* webpackChunkName: "src-quill-editor" */ '@/common/components/sd-quill-editor.vue'
          )
        ),
        componentForDisplay: SdQuillReader,
        dataType: 'string', // 数据类型是any的话，空字符也会通过必填校验
      }
    }
    case 'selecttree': {
      const spec = {
        getDisplayValue(value) {
          return value
            .map((item) => {
              return getLabelByValue(item, field.attr.selectTreeItem, field.attr.showLevel)
            })
            .join(',')
        },
        dataType: 'array',
      }
      spec.parseBackendValue = (v) => (v ? v.split(',') : [])
      spec.toBackendValue = (v) => v?.join(',')
      return {
        ...spec,
        component: SdSelectTree,
        props: {
          single: field.attr?.multiple === 'false',
          dataSource: field.attr.selectTreeItem,
          showLevel: field.attr.showLevel,
        },
      }
    }
  }
  return {
    component: Input,
  }
}

function getFieldDisplayExpr(field) {
  // 根据后端返回的 DisplayExpr，动态隐藏字段。例如 #bean.contractDeadlineType != "NO_FIXED_DEADLINE"
  const displayExpr = field.attr?.displayExpr
  if (!displayExpr) return {}

  // 支持的所有操作符
  const operators = [
    {
      str: '==',
      evaluator: (a, b) => a === b,
    },
    {
      str: '!=',
      evaluator: (a, b) => {
        return a !== b
      },
    },
  ]

  let leftValue = ''
  let rightValue
  let evaluator
  operators.some((operator) => {
    const index = displayExpr.indexOf(operator.str)
    if (index !== -1) {
      // 表达式中包含当前操作符
      evaluator = operator.evaluator
      // 取出操作符两侧的值
      leftValue = displayExpr.substring(0, index).trim()
      rightValue = displayExpr.substring(index + operator.str.length)
      return true
    } else {
      return false
    }
  })
  if (leftValue) {
    // 左值类似 #bean.contractDeadlineType，替换#bean.
    leftValue = leftValue.replace('#bean.', '')
    // 右值类似 "NO_FIXED_DEADLINE"，通过JSON.parse解析成字符串或者数值
    rightValue = JSON.parse(rightValue)
    return {
      leftValue,
      rightValue,
      evaluator,
    }
  }
  return {}
}
function getValuesByFormulas(formulaDatas, inputs) {
  return axios
    .post(
      'api/framework/v1/page/computeFieldValWithQLExpr',
      inputs.concat(
        formulaDatas.map((formulaData) => ({
          attr: { formulaData },
          name: 'justgetformulavalue' + formulaData,
        }))
      )
    )
    .then((res) => {
      return formulaDatas.map((formulaData) => {
        let value = res.data.find((item) => item.name === 'justgetformulavalue' + formulaData).value
        if (value && typeof value === 'object') {
          value = JSON.stringify(value)
        }
        return value
      })
    })
}
export { getComponentSpec, getFieldDisplayExpr, getValuesByFormulas }

/**
 * 自动生成表单界面，包括校验、不同类型字段的显示
 * @displayName SdForm 表单
 */
export default {
  name: 'SdForm',
  components,
  mixins: [sdFormPublicMethodsNew],
  props: {
    /**
     * 表单初始的字段列表，包括value、各种校验规则
     */
    initValues: {
      type: Array,
      required: true,
    },
    /**
     * 整个表单是否只读
     */
    readOnly: {
      type: Boolean,
      default: false,
    },
    /**
     * 内部使用，用于获取只读模式的值
     * @ignore
     */
    disableExtraForm: {
      type: Boolean,
      default: false,
    },
    /**
     * 表单填写说明，html格式
     * @since 0.20
     */
    description: {
      type: String,
      default: undefined,
    },
    formulaValidator: {
      type: Array,
      default: undefined,
    },
    /**
     * @ignore
     */
    getPopupContainer: {
      type: Function,
      default: function(triggerNode, dialogContext) {
        return this._getPopupContainer(triggerNode, dialogContext)
      },
    },
    /**
     * @ignore
     */ formRelations: {
      type: Array,
      default: undefined,
    },
  },
  provide() {
    return {
      SdFormContext: this,
    }
  },
  inject: {
    dialogContext: { default: null },
    childTable: { default: () => ({}) },
  },
  data() {
    return {
      model: {},
      initModelToBackendValue: undefined, // 初始的表单值，用于检查数据是否发生了修改
      fieldOptions: {},
      hiddenStatus: {},
      displayExprResults: {},
      labels: {},
      showExtraForm: false,
      watching: false,
      SdConfigProvider: SdConfigProvider,
      transientFields: [], // 纯前端字段
    }
  },
  computed: {
    fields() {
      // 把数组型的字段列表，转成object格式，方便查找
      const fields = {}
      this.initValues.forEach((field) => {
        fields[field.name] = field
      })
      return fields
    },
    frontendAndBackendFields() {
      // 前端 + 后端字段
      return this.initValues.concat(this.transientFields)
    },
    rulesComputed() {
      const rules = {}
      // 获取校验信息
      this.initValues.forEach((field, index) => {
        // 过滤掉隐藏的字段
        if (this.hiddenStatus[field.name]) {
          return
        }
        if (this.displayExprResults[field.name] === false) {
          return
        }
        // 处理字段中文名
        const fullField = this.labels[field.name] || field.caption
        const trigger = ['change', 'blur']

        const spec = this.fieldOptions[field.name]

        const rulesForField = []
        // 必填

        const type = spec.dataType || 'string'
        const baseRule = {
          type,
          required: field.required,
          trigger,
          fullField,
        }

        if (type === 'string' && field.required) {
          baseRule.whitespace = true
        }
        rulesForField.push(baseRule)

        // 正则
        if (field.attr.regex) {
          rulesForField.push({
            type,
            message: field.attr.regexMessage || field.attr.invalidText,
            pattern: field.attr.regex,
            trigger,
            fullField,
          })
        }
        // 最大小长度
        if (field.attr.maxLength || field.attr.minLength) {
          rulesForField.push({
            type,
            max: parseInt(field.attr.maxLength) || null,
            min: parseInt(field.attr.minLength) || null,
            trigger,
            fullField,
          })
        }
        if (field.attr.unique === 'true') {
          rulesForField.push({
            validator: (rule, value, callback) => {
              if (!value) {
                callback()
                return
              }
              axios
                .get('api/framework/v1/page/checkFieldUnique', {
                  params: {
                    ...(field.attr.uniqueProperties || {}),
                    value,
                  },
                })
                .then((res) => {
                  if (res.data) {
                    callback()
                  } else {
                    callback(new Error('该值不可用，系统中已存在'))
                  }
                })
            },
            trigger: 'blur',
          })
        }
        if (field.dataType === 'collection') {
          rulesForField.push({
            validator: (rule, value, callback) => {
              if (value?.length) {
                import('./sd-child-table/sd-child-table.js').then(({ validateData }) => {
                  validateData(field.attr.dync, value).then((data) => {
                    let m = []
                    data.forEach((item, index) => {
                      if (item.length) {
                        m.push('第' + (index + 1) + '行' + data[index].join(','))
                      }
                    })
                    if (m.length) {
                      m = m.join(',') + '，请重新编辑'
                      callback(m)
                    } else {
                      callback()
                    }
                  })
                })
              } else {
                callback()
              }
            },
            trigger,
          })
        }
        rules[field.name] = rulesForField
      })
      return rules
    },
    selectors() {
      if (this.readOnly) {
        return []
      }
      const a = []
      this.formRelations?.forEach((item) => {
        const fieldsMap = JSON.parse(item.fieldsMap)
          .map((item) => {
            return {
              ...item,
              fromField: item.fromField[0].id,
              toField: item.toField[0].id,
            }
          })
          .filter((item) => !item.toField.includes('.')) // 过滤掉子表的字段
        fieldsMap.forEach((i) => {
          if (!this.fields[i.toField]) {
            // eslint-disable-next-line no-console
            console.error('目标表单关联字段' + i.toField + '不存在，检查关联配置')
            return
          }
          if (i.selector && this.fields[i.toField].readonly !== true) {
            // 找到不是只读的作为选择器的字段
            a.push({
              field: i.toField,
              fieldsMap,
              fromForm: item.fromForm,
              fromFormList: item.fromFormList,
              frontData: item.frontData,
            })
          }
        })
      })
      return a
    },
  },
  created() {
    this.funs = {}
    this.debounce = function(name) {
      if (!this.funs[name]) {
        this.funs[name] = debounce((callback) => {
          callback()
        }, 500)
      }
      return this.funs[name]
    }

    this.initValues.forEach((field, index) => {
      // 获取字段选项
      if (field.dataType === 'collection') {
        field.attr.formRelations = this.formRelations
          ?.map((item) => {
            // 过滤出设置的子表关联，再丢给子表的form
            item = JSON.parse(JSON.stringify(item))
            item.fieldsMap = JSON.stringify(
              JSON.parse(item.fieldsMap)
                .filter((i) => i.toField[0].id.startsWith(field.name + '.'))
                .map((i) => {
                  i.toField[0].id = i.toField[0].id.replace(field.name + '.', '')
                  return i
                })
            )
            return item
          })
          .filter((item) => {
            return item.fieldsMap !== '[]'
          })
      }
      const spec = getComponentSpec(field)
      this.fieldOptions[field.name] = spec

      // 设置model值
      if (field.value === null) {
        this.model[field.name] = undefined
      } else {
        this.model[field.name] = field.value
      }

      this.parseBackendValue(field.name)
      this.processDisplayExpr(field)
      if (field.attr?.displayFormula) {
        this.$set(this.displayExprResults, field.name, field.attr?.displayFormulaValue !== 'false')
      }
    })
    this.model = { ...this.model }
    this.$nextTick(() => {
      this.initValues.forEach((field, index) => {
        this.processFormulaValue(field)
      })
      this.getTransientFieldsDisplayExpr()
    })
    if (!this.getPopupContainer) {
      this.SdConfigProvider = 'div'
    }
  },
  mounted() {
    this.initModelToBackendValue = JSON.stringify(this.getBackendValues())
  },
  methods: {
    getTransientFieldsDisplayExpr() {
      // 获取前端字段默认的隐藏状态
      const formulas = []
      const fieldNames = []
      this.transientFields.forEach((field) => {
        const formula = field.attr.displayFormula
        if (formula) {
          this.processFormulaValue(field)
          // 有隐藏条件的，先设置为隐藏
          this.$set(this.displayExprResults, field.name, false)
          formulas.push(formula)
          fieldNames.push(field.name)
        }
      })
      if (formulas.length) {
        // 所有前端公式，打包成一个请求计算
        this.getValuesByFormulas(formulas).then((results) => {
          results.forEach((result, index) => {
            this.$set(this.displayExprResults, fieldNames[index], result)
          })
        })
      }
    },
    _getPopupContainer(triggerNode, dialogContext) {
      if (typeof triggerNode === 'undefined') {
        // modal弹出时，triggerNode是undefined。让modal弹在body上
        return document.body
      } else {
        if (dialogContext && dialogContext !== this.dialogContext) {
          // dialogContext 表示当前弹出框所属的 dialog
          // 在modal框内，而且这个框不是sd-form所在的，也就是子modal框，
          return document.body
        } else {
          return this.$el
        }
      }
    },
    parseBackendValue(field) {
      // 字段按要求转换格式
      const initObject = this.fields[field]
      let initValue = initObject.value
      if (typeof initValue === 'undefined' || initValue === null) {
        initValue = initObject.attr?.defaultValue
      }
      if (typeof initValue !== 'undefined' && initValue !== null) {
        const parseBackendValue = this.fieldOptions[field].parseBackendValue
        if (parseBackendValue) {
          this.$set(this.model, field, parseBackendValue(initValue))
        }
      }
    },
    processDisplayExpr(field) {
      if (field.attr?.displayFormula) {
        return
      }
      const { leftValue, rightValue, evaluator } = getFieldDisplayExpr(field)
      if (leftValue) {
        this.$watch(
          `model.${leftValue}`,
          (newVal, oldVal) => {
            const result = evaluator(newVal, rightValue)
            // 保存计算后的结果
            this.$set(this.displayExprResults, field.name, result)
          },
          { immediate: true }
        )
      }
    },
    processFormulaValue(field) {
      // 更新表单字段
      const formulaData = field.attr?.formulaDataEdit || field.attr?.formulaData
      const displayFormula = field.attr?.displayFormula
      if (formulaData || displayFormula) {
        ;[
          ...this.getFieldsInFormulaData(formulaData),
          ...this.getFieldsInFormulaData(displayFormula),
        ].forEach((name) => {
          this.$watch('model.' + name, () => {
            if (this.watching) return
            this.debounce(name)(() => this.updateValueByFormula(name))
          })
        })
      }
    },
    getFieldsInFormulaData(formulaData) {
      return (
        formulaData
          ?.match(/\{([^{:])*?\}/g)
          ?.map((item) => item.replace(/{|}/g, '').split('.')[0]) || []
      )
    },
    getFieldsFormluaHasField(field) {
      // 获取公式包含field字段的字段
      const reg = new RegExp(`\\{${field}(\\..*)?\\}`)
      return this.initValues
        .filter((o) => {
          const formulaData = o.attr.formulaData || o.attr?.formulaDataEdit
          return reg.test(formulaData)
        })
        .map((item) => item.name)
    },
    getFieldsDependentField(field) {
      // 获取直接或间接依赖某字段的所有字段
      const fields = []
      const f = (field) => {
        if (fields.length > 1000) {
          // eslint-disable-next-line no-console
          console.warn('循环次数太多，可能有循环依赖')
          return
        }
        this.getFieldsFormluaHasField(field).forEach((item) => {
          fields.push(item)
          f(item)
        })
      }
      f(field)
      return fields
    },
    _inputs(initValues, field) {
      // 计算拼参数
      initValues = JSON.parse(JSON.stringify(initValues))
      return initValues.map((o) => {
        const i = this.getBackendValues().find((item) => item.name === o.name)
        // 当前编辑的值如果是前端计算的，触发计算时不用计算自己
        o.attr.formulaData =
          o.attr.formulaData || (o.name !== field ? o.attr?.formulaDataEdit : undefined)
        return {
          ...o,
          value: i?.value,
        }
      })
    },
    updateValueByFormula(field) {
      axios
        .post(
          'api/framework/v1/page/computeFieldValWithQLExpr',
          this._inputs(this.frontendAndBackendFields, field)
        )
        .then((res) => {
          this.watching = true
          res.data.forEach((item) => {
            // 更新变化的值
            const dependentFields = this.getFieldsDependentField(field)
            if (dependentFields.includes(item.name)) {
              const value = getComponentSpec(item)?.parseBackendValue?.(item.value) || item.value
              this.setFieldValue(item.name, value)
            }
            // 同时处理下隐藏的,如果某字段的隐藏公式包含了这次值变化的字段就更新下
            const displayFormulaFields = this.getFieldsInFormulaData(item.attr?.displayFormula)
            const valueChangedFields = [...dependentFields, field]
            if (displayFormulaFields.some((item) => valueChangedFields.includes(item))) {
              this.$set(
                this.displayExprResults,
                item.name,
                item.attr?.displayFormulaValue !== 'false'
              )
            }
          })
          this.$nextTick(() => {
            this.watching = false
          })
        })
    },
    getValueByFormula(formulaData) {
      return this.getValuesByFormulas([formulaData]).then((datas) => datas[0])
    },
    getValuesByFormulas(formulaDatas, inputs) {
      return getValuesByFormulas(formulaDatas, inputs || this._inputs(this.initValues))
    },
    pickerData(selector) {
      // 给选择器拼上自定义变量
      let cinputs = this._inputs(this.initValues)
      if (this.childTable.name) {
        // 当前是子表的编辑表单，拼上主表的数据
        const inputs = this.childTable.dadForm?._inputs?.(this.childTable.dadForm.initValues)
        cinputs.forEach((item) => {
          // 当前是子表的字段，字段名拼上字段
          item.name = this.childTable.name + '_' + item.name
        })
        cinputs = cinputs.concat(inputs)
      }
      // 根据自定义变量的公式获取计算的值
      const customFields = JSON.parse(selector.frontData || '{}').customFields?.filter(
        (i) => i.value?.formulaData
      )
      const customValues = {}
      const customFieldsNames = customFields?.map((i) => i.name)
      const formulaDatas = customFields?.map((i) => i.value.formulaData)
      let p = Promise.resolve()
      if (formulaDatas?.length) {
        p = this.getValuesByFormulas(formulaDatas, cinputs).then((values) => {
          values.forEach((v, index) => {
            customValues[customFieldsNames[index]] = v
          })
        })
      }
      const fromForm = JSON.parse(selector.fromForm)[0]
      p.then(() =>
        pickValues(sdRelationPicker, {
          single: true,
          formId: fromForm.id,
          title: fromForm.name,
          isFlow: fromForm.formType === 1,
          formListId: JSON.parse(selector.fromFormList || '[]')[0]?.id,
          customValues,
        })
      ).then((data) => {
        if (!data?.length) return
        axios
          .get(`api/framework/v1/page/wp/onl:${fromForm.id}`, {
            params: { id: data[0].id },
          })
          .then((res) => {
            // 来源于子表里的字段
            const fromChildFields = selector.fieldsMap.filter((item) =>
              item.fromField.includes('.')
            )
            let p = Promise.resolve()
            if (fromChildFields.length) {
              // 有子表的字段，弹个选择器选子表数据
              const childtable = res.data.pageFormData?.pageFieldInfos.find(
                (i) => i.name === fromChildFields[0].fromField.split('.')[0]
              )
              let p1
              if (JSON.parse(childtable.value).length === 1) {
                // 子表只有一条数据默认选中
                p1 = Promise.resolve(JSON.parse(childtable.value))
              } else {
                p1 = pickValues(sdListPickerVue, {
                  // todo先直接用valuepicker选子表数据
                  single: true,
                  optionLabel: 'id',
                  loadListData: () => {
                    return JSON.parse(childtable.value)
                  },
                  title: childtable.caption,
                  render: (item) => {
                    const childtableField = childtable.attr.dync[1]
                    const childtableField2 = childtable.attr.dync[2]
                    const c1 = getComponentSpec(childtableField)
                    const c2 = getComponentSpec(childtableField2)
                    return [
                      <span>
                        {childtableField.caption}: 【
                        {c1?.getDisplayValue?.(
                          c1?.parseBackendValue?.(item[childtableField.name]) ||
                            item[childtableField.name]
                        ) ?? item[childtableField.name]}
                        】
                      </span>,
                      <span>, </span>,
                      <span>
                        {childtableField2.caption}: 【
                        {c2?.getDisplayValue?.(
                          c2?.parseBackendValue?.(item[childtableField2.name]) ||
                            item[childtableField2.name]
                        ) ?? item[childtableField2.name]}
                        】
                      </span>,
                    ]
                  },
                })
              }
              p = p1.then((data) => {
                if (!data?.length) return 'notselectchilddata'
                // 子表字段赋值
                fromChildFields.forEach((item) => {
                  const childtableField = childtable.attr.dync.find(
                    (i) => i.name === item.fromField.split('.').pop()
                  )
                  if (!childtableField) {
                    // eslint-disable-next-line no-console
                    console.error('来源表单关联字段' + item.fromField + '不存在，检查关联配置')
                    return
                  }
                  let value = data[0][childtableField.name]
                  if (childtableField.dataType === 'attachment') {
                    // 如果是附件字段，则拷贝附件
                    const toid = JSON.parse(this.fields[item.toField].value).value
                    const formid = JSON.parse(value).value
                    this._replaceAtt(toid, formid, item.toField)
                  } else {
                    if (value !== undefined && value !== null) {
                      value = getComponentSpec(childtableField)?.parseBackendValue?.(value) || value
                    }
                    this.setFieldValue(item.toField, value)
                  }
                })
              })
            }
            p.then((v) => {
              if (v === 'notselectchilddata') return
              selector.fieldsMap.forEach((item) => {
                // 主表里的字段赋值
                if (item.fromField.includes('.')) return
                const fromField = res.data.pageFormData?.pageFieldInfos.find(
                  (i) => i.name === item.fromField
                )
                if (!fromField) {
                  // eslint-disable-next-line no-console
                  console.error('来源表单关联字段' + item.fromField + '不存在，检查关联配置')
                  return
                }
                let value = fromField.value
                if (fromField.dataType === 'attachment') {
                  // 如果是附件字段，则拷贝附件
                  const toid = JSON.parse(this.fields[item.toField].value).value
                  const formid = JSON.parse(value).value
                  this._replaceAtt(toid, formid, item.toField)
                } else {
                  if (value !== undefined && value !== null) {
                    value = getComponentSpec(fromField)?.parseBackendValue?.(value) || value
                  }
                  this.setFieldValue(item.toField, value)
                }
              })
            })
          })
      })
    },
    _replaceAtt(toid, fromid, toField) {
      // 替换附件并更新
      return axios
        .get(`api/framework/v1/attachment-extend/attachments-info-perm/${toid}`)
        .then((res) => {
          return Promise.all(
            res.data.map((i) => {
              return axios.delete(`api/framework/v1/attachment-extend/attachments-delete/${toid}`, {
                params: {
                  attachmentId: i.code,
                  ...this.fields[toField].attr?.infoProperties,
                },
              })
            })
          )
        })
        .then(() => {
          return axios.post(`api/framework/v1/attachment-extend/attachments-copy/${fromid}/${toid}`)
        })
        .then(() => {
          // 只是为了触发附件组件的更新，并不是要赋值
          this.setFieldValue(toField, ['updateAttachmentList'])
        })
    },
  },
}
</script>

<style module lang="scss">
@use '@/common/design' as *;
@use "@/common/components/sd-form/sd-form-table.scss" as formTable;
:global(.browser-ie) {
  .form::before {
    /*
    修复下拉框导致页面闪动的问题
    参考https://github.com/ant-design/ant-design/issues/21039
   */
    position: fixed;
    content: '';
  }
}
.form {
  position: relative;
  :global .sd-readonly {
    .ant-form-item-required::before {
      content: none;
    }
    .ant-form-item-control {
      // 用 padding 保证单行的高度是40，用line-height多行时有问题
      padding: (40px - $font-size-base * 1.5) / 2 0;
      line-height: 1.5;
    }
  }
  > :global(.ant-form-item) {
    &:global(.sd-readonly) {
      margin-bottom: 0;
    }
    :global .ant-form-item-label {
      float: left;
      width: 19%;
    }
    > :global(.ant-form-item-control-wrapper) {
      float: left;
      width: 75%;
    }
    &:global(.sd-no-label) {
      > :global(.ant-form-item-control-wrapper) {
        width: 100%;
      }
    }
  }
  > table,
  > :global(.sd-table-wrapper) > table {
    width: 100%;
    textarea {
      margin-top: 4px;
    }

    td {
      display: table-cell;
      &:global(.ant-form-item-label):not(:global(.sd-readonly)) {
        padding-bottom: 24px;
      }
      :global(.ant-form-item.sd-readonly) {
        margin-bottom: 0;
      }
      &:global(.ant-form-item-label) {
        text-overflow: ellipsis;
      }
    }
  }
  :global(.sd-form-table) {
    @include formTable.table;
  }
  :global .sd-multi-input {
    margin-bottom: 0;
    line-height: $input-height-lg;
    .ant-form-item {
      display: inline-block;
    }
  }
  .selector-btn {
    margin-left: 5px;
  }
}
.extra-form {
  display: none;
}
</style>
