<template>
  <div>
    <slot
      v-for="beforeSlotName in getPropArrayValue(field.beforeSlotName)"
      :formData="formData"
      :field="field"
      :options="options"
      :isView="isView"
      :disabled="_disabled"
      :viewValue="typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer()"
      :getPropValue="getPropValue"
      :name="beforeSlotName"
    />
    <template v-for="beforeSlot in getPropArrayValue(field.beforeSlot)">
      <component
        :is="beforeSlot"
        :key="beforeSlot"
        :form-data="formData"
        :field="field"
        :options="options"
        :is-view="isView"
        :disabled="_disabled"
        :view-value="typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer()"
        :get-prop-value="getPropValue"
      />
    </template>

    <div
      class="easy-form-item"
      :class="{
        'default-display': !['slot','component'].includes(fieldType) || getPropValue(field.useDefaultDisplay),
        ['easy-form-item-' + fieldType]: true,
        ['easy-form-item-' + (field.prop || field.key)]: true
      }"
    >
      <slot
        v-if="fieldType === 'slot'"
        :formData="formData"
        :field="field"
        :options="options"
        :isView="isView"
        :disabled="_disabled"
        :viewValue="typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer()"
        :getPropValue="getPropValue"
        :name="getPropValue(field.slotName)"
      />
      <component
        :is="getPropValue(field.is)"
        v-else-if="fieldType === 'component'"
        v-model="componentModel"
        v-bind="getPropValue(field.formProps)"
        v-on="getPropValue(field.formListeners)"
      />
      <span
        v-else-if="isView"
        class="el-form-item-view"
        :style="{
          'white-space': fieldType === 'textarea' ? 'pre-wrap' : undefined
        }"
      >
        {{ typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer() }}
      </span>
      <component
        :is="(optionsFilterShow.length > 0 && fieldType !== 'textarea') ? 'el-autocomplete' : 'el-input'"
        v-else-if="['input', 'textarea'].includes(fieldType)"
        v-model="componentModel"
        :placeholder="placeholder"
        :disabled="_disabled"
        :type="getFormProps('type') || ( fieldType === 'textarea' ? 'textarea' : undefined )"
        v-bind="{
          rows: 5,
          fetchSuggestions: (queryString, cb)=>{
            cb(optionsFilterShow.filter((item)=>{
              if (item.filter === undefined) {
                return !queryString || item.label.includes(queryString)
              } else {
                return getPropValue(item.filter, queryString, item)
              }
            }))
          },
          ...(getPropValue(field.formProps) || {})
        }"
        v-on="getPropValue(field.formListeners)"
      >
        <template v-slot="{ item }">
          <div class="name">{{ item.label }}</div>
        </template>
        <template
          v-if="getPropValue(field.prepend)"
          slot="prepend"
        >{{ getPropValue(field.prepend) }}</template>
        <template
          v-if="getPropValue(field.autoAppend) || getPropValue(field.append)"
          slot="append"
        >
          <el-select
            v-if="Array.isArray(getPropValue(field.autoAppend))"
            v-model="appendModel"
            :loading="optionsLoading"
            :loading-text="getFormProps('loadingText') || '正在加载远程数据'"
          >
            <el-option
              v-for="option in getOptions(getPropValue(field.autoAppend)).filter((item) => {
                return item.show !== false
              })"
              :key="option.key || option.value"
              :label="option.label"
              :value="option.value"
            />
          </el-select>
          <span v-else>{{ getPropValue(field.autoAppend) || getPropValue(field.append) }}</span>
        </template>
      </component>
      <component
        :is="ComponentConfig.ComponentCover['select'] || 'el-select'"
        v-else-if="fieldType === 'select'"
        ref="ElComponent"
        v-model="componentModel"
        :placeholder="placeholder"
        :value-key="'_key'"
        :disabled="_disabled"
        :filterable="getValueByLevel(getFormProps('filterable'), optionsFilterShow.length > 7)"
        :popper-class="[
          'easy-form-item-select',
          'easy-form-item-' + field.prop,
          getFormProps('popperClass') || ''
        ].join(' ')
        "
        :multiple="isMultipleValue"
        :loading="optionsLoading"
        :loading-text="getFormProps('loadingText') || '正在加载远程数据'"
        v-bind="{
          clearable: true,
          ...(getPropValue(field.formProps, options) || {})
        }"
        v-on="getPropValue(field.formListeners, options)"
      >
        <template
          v-if="getValueByLevel(getFormProps('filterable'), optionsFilterShow.length > 7) || optionsLoading"
          v-slot:prefix
        >
          <i v-if="optionsLoading" class="el-icon-loading" />
          <i v-else class="el-icon-search" />
        </template>
        <template
          v-for="option in optionsFilterShow"
        >
          <el-option
            :key="option.key || option.value"
            :label="option.label"
            :value="option.value"
            :disabled="option.disabled || (isMultipleValue && option.required)"
          >
            <div class="select-option-item" v-on="getPropValue(option.listeners || field.optionListeners, option)">
              <el-checkbox
                v-if="isMultipleValue"
                v-model="modelValue"
                class="select-option-item-label"
                :label="option.value"
                :disabled="option.disabled || option.required"
              >
                {{ option.label }}
              </el-checkbox>
              <div v-else class="select-option-item-label">{{ option.label }}</div>
              <div
                v-if="option.subLabel"
                class="select-option-item-subLabel"
              >
                {{ option.subLabel }}
              </div>
            </div>
          </el-option>
        </template>
      </component>
      <component
        :is="ComponentConfig.ComponentCover['selectv2'] || 'el-select-v2'"
        v-else-if="fieldType === 'selectv2'"
        ref="ElComponent"
        v-model="componentModel"
        :options="optionsFilterShow"
        :placeholder="placeholder"
        :value-key="'_key'"
        :disabled="_disabled"
        :filterable="getValueByLevel(getFormProps('filterable'), optionsFilterShow.length > 7)"
        :popper-class="[
          'easy-form-item-select',
          'easy-form-item-' + field.prop,
          getFormProps('popperClass') || ''
        ].join(' ')
        "
        :multiple="isMultipleValue"
        :loading="optionsLoading"
        :loading-text="getFormProps('loadingText') || '正在加载远程数据'"
        v-bind="{
          clearable: true,
          ...(getPropValue(field.formProps, options) || {})
        }"
        v-on="getPropValue(field.formListeners, options)"
      >
        <template v-slot:default="{item : option}">
          <div class="select-option-item" v-on="getPropValue(option.listeners || field.optionListeners, option)">
            <el-checkbox
              v-if="isMultipleValue"
              v-model="modelValue"
              class="select-option-item-label"
              :label="option.value"
              :disabled="option.disabled || option.required"
            >
              {{ option.label }}
            </el-checkbox>
            <div v-else class="select-option-item-label">{{ option.label }}</div>
            <div
              v-if="option.subLabel"
              class="select-option-item-subLabel"
            >
              {{ option.subLabel }}
            </div>
          </div>
        </template>
      </component>
      <component
        :is="ComponentConfig.ComponentCover['cascader'] || 'el-cascader'"
        v-else-if="fieldType === 'cascader'"
        ref="ElComponent"
        v-model="componentModel"
        v-loading="optionsLoading"
        :placeholder="placeholder"
        :options="optionsFilterShow"
        :disabled="_disabled"
        v-bind="{
          clearable: true,
          filterable: true,
          showAllLevels: false,
          ...getPropValue(field.formProps),
          props: {
            multiple: isMultipleValue,
            emitPath: false,
            ...(getFormProps('props') || {})
          }
        }"
        v-on="getPropValue(field.formListeners, options)"
      />
      <component
        :is="ComponentConfig.ComponentCover['date-picker'] || 'el-date-picker'"
        v-else-if="fieldType === 'date-picker'"
        v-model="componentModel"
        :placeholder="placeholder"
        :disabled="_disabled"
        v-bind="{
          valueFormat: 'yyyy-MM-dd hh:mm:ss',
          format: 'yyyy-MM-dd',
          ...getPropValue(field.formProps)
        }"
        v-on="getPropValue(field.formListeners)"
      />
      <component
        :is="ComponentConfig.ComponentCover['radio'] || 'el-radio-group'"
        v-else-if="fieldType === 'radio'"
        v-model="componentModel"
        v-loading="optionsLoading"
        :placeholder="placeholder"
        :disabled="_disabled"
        v-bind="getPropValue(field.formProps, options)"
        v-on="getPropValue(field.formListeners, options)"
      >
        <component
          :is="field.useRadioButton?'el-radio-button':'el-radio'"
          v-for="option in optionsFilterShow"
          :key="option.key || option.value"
          :label="option.value"
          :disabled="option.disabled"
          :border="field.border"
        >{{ option.label }}
        </component>
      </component>
      <component
        :is="ComponentConfig.ComponentCover['checkbox'] || 'el-checkbox-group'"
        v-else-if="fieldType === 'checkbox'"
        v-model="componentModel"
        v-loading="optionsLoading"
        :disabled="_disabled"
        v-bind="getPropValue(field.formProps, options)"
        v-on="getPropValue(field.formListeners, options)"
      >
        <el-checkbox
          v-for="option in optionsFilterShow"
          :key="option.key || option.value"
          :label="option.value"
          :disabled="option.disabled || option.required"
        >
          {{ option.label }}
        </el-checkbox>
      </component>
      <component
        :is="ComponentConfig.ComponentCover['confirm'] || 'el-checkbox'"
        v-else-if="fieldType === 'confirm'"
        v-model="componentModel"
        :disabled="_disabled"
        v-bind="getPropValue(field.formProps)"
        v-on="getPropValue(field.formListeners)"
      />
      <component
        :is="ComponentConfig.ComponentCover['switch'] || 'el-switch'"
        v-else-if="fieldType === 'switch'"
        v-model="componentModel"
        :disabled="_disabled"
        v-bind="getPropValue(field.formProps)"
        v-on="getPropValue(field.formListeners)"
      />
      <component
        :is="ComponentConfig.ComponentCover['rate'] || 'el-rate'"
        v-else-if="fieldType === 'rate'"
        v-model="componentModel"
        :disabled="_disabled"
        v-bind="getPropValue(field.formProps)"
        v-on="getPropValue(field.formListeners)"
      />
      <component
        :is="ComponentConfig.ComponentCover['upload'] || 'el-upload'"
        v-else-if="fieldType === 'upload'"
        :class="{ 'el-upload-readonly': configReadOnly }"
        :file-list="componentModel"
        :on-change="(file, fileList)=>{
          componentModel = fileList
          const formProps = getPropValue(field.formProps)
          if(formProps && typeof formProps.onChange === 'function'){
            formProps.onChange(file, fileList)
          }
        }"
        :on-remove="(file, fileList)=>{
          componentModel = fileList
          const formProps = getPropValue(field.formProps)
          if(formProps && typeof formProps.onRemove === 'function'){
            formProps.onRemove(file, fileList)
          }
        }"
        v-bind="{
          action: '',
          autoUpload: !!getFormProps('action'),
          ...(getPropValue(field.formProps) || {})
        }"
        v-on="getPropValue(field.formListeners)"
      >
        <template v-if="!getFormProps('listType')" slot="trigger">
          <el-button v-if="!configReadOnly" :disabled="_disabled" size="small" type="primary">
            选取文件
          </el-button>
        </template>
        <span v-if="!configReadOnly && field.tips">{{ field.tips }}</span>
      </component>
      <component
        :is="ComponentConfig.ComponentCover[fieldType] "
        v-else-if="ComponentConfig.ComponentCover[fieldType]"
        v-model="componentModel"
        :form-data="formDataRef"
        :field="field"
        :options="options"
        :options-loading="optionsLoading"
        :placeholder="placeholder"
        :is-view="isView"
        :disabled="_disabled"
        :view-value="typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer()"
        :get-prop-value="getPropValue"
        v-bind="getPropValue(field.formProps)"
        v-on="getPropValue(field.formListeners) || {}"
      />
      <div v-if="buttons.length > 0" style="margin-left: 10px;flex-shrink: 0;">
        <el-button
          v-for="button in buttons"
          :key="getPropValue(button.label)"
          :loading="getPropValue(button.loading)"
          :type="getPropValue(button.type)"
          :size="getPropValue(button.size)"
          :disabled="getPropValue(button.disabled)"
          :icon="getPropValue(button.leftIcon) || getPropValue(button['icon'])"
          v-bind="getPropValue(button.buttonProps)"
          @click="callButtonHandler(button)"
        >
          {{ getPropValue(button.label) }}
          <i v-if="getPropValue(button.rightIcon)" class="el-icon--right" :class="getPropValue(button.rightIcon)" />
        </el-button>
      </div>
    </div>
    <template v-for="afterSlot in getPropArrayValue(field.afterSlot)">
      <component
        :is="afterSlot"
        :key="afterSlot"
        :form-data="formData"
        :field="field"
        :options="options"
        :is-view="isView"
        :disabled="_disabled"
        :view-value="typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer()"
        :get-prop-value="getPropValue"
      />
    </template>
    <slot
      v-for="afterSlotName in getPropArrayValue(field.afterSlotName)"
      :formData="formData"
      :field="field"
      :options="options"
      :isView="isView"
      :disabled="_disabled"
      :viewValue="typeof field.viewer === 'function' ? field.viewer(_componentScope, modelValue) : defaultViewer()"
      :getPropValue="getPropValue"
      :name="afterSlotName"
    />
  </div>
</template>

<script>
import { deepClone, filterDeepOption, findDeepOption, findDeepOptionIndex, parseDate, showWarning } from '../utils'
import { getOptionsByBaseCode, OptionsMap } from '../optionsManager'
import { ComponentConfig } from '../globalConfig'

export default {
  name: 'EasyFormItem',
  props: {
    field: {
      type: Object,
      default: () => {
        return {}
      }
    },
    componentScope: {
      type: Object,
      default: () => {
        return {}
      }
    },
    formData: {
      type: Object,
      default: () => {
        return {}
      }
    },
    configReadOnly: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    configIsView: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    configTrim: {
      type: Boolean,
      default: () => {
        return true
      }
    },
    configDisableValidates: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    dontValidateRequired: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    useSubmitValidate: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    itemController: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  data() {
    return {
      ComponentConfig,
      isEasyFormItem: true,
      onChangeTimer: null,
      onChangeIsFromUser: false,
      appendModel: '',
      OptionsMap // 此处引用只是为了能让vue检测到变动以更新视图
    }
  },
  computed: {
    _componentScope() {
      return {
        formData: this.formData,
        field: this.field,
        getOptionValue: this.getOptionValue,
        fieldInstance: this,
        ...this.componentScope
      }
    },
    /**
     * 绑定给组件的值，此处可做组件值和表单对象值之间的转换
     */
    componentModel: {
      set(value) {
        this.modelValue = this.applyComputed(value, 'set')
      },
      get() {
        return this.applyComputed(this.modelValue, 'get')
      }
    },
    modelValue: {
      set(value) {
        this.formData[this.field.prop] = value
        this.callOnChange(value, true)
      },
      get() {
        this.initModelValue()
        return this.formData[this.field.prop]
      }
    },
    fieldType() {
      return this.getPropValue(this.field.type)
    },
    placeholder() {
      if (this.getPropValue(this.configReadOnly)) {
        return '未录入'
      } else {
        const placeholder = this.getValueByLevel(this.getPropValue(this.field.placeholder), this.getFormProps('placeholder'))
        if (placeholder) {
          return placeholder
        } else {
          return `${this.fieldTips}${this.getPropValue(this.field.label) || ''}`
        }
      }
    },
    isView() {
      return this.fieldType === 'view' || this.getValueByLevel(this.getPropValue(this.field.isView), this.getPropValue(this.configIsView))
    },
    isMultipleValue() {
      return ['checkbox'].includes(this.fieldType) ||
      (this.fieldType === 'select' && this.getFormProps('multiple') === true) ||
      (this.fieldType === 'cascader' && (this.getFormProps('props') || {}).multiple === true) ||
      this.getPropValue(this.field.multiple)
    },
    fieldTips() {
      if (this.field.fieldTips) {
        return this.field.fieldTips
      }
      switch (this.fieldType) {
        case 'textarea':
        case 'input': {
          return '请输入'
        }
        case 'select':
        case 'radio':
        case 'checkbox':
        case 'date-picker': {
          return '请选择'
        }
        default: {
          return '请录入'
        }
      }
    },
    options() {
      return this.getOptions(this.field.options, this.field.optionsBaseCode)
    },
    optionsFilterShow() {
      return this.optionsFilterShowMethod(this.options)
    },
    rules() {
      let result = []
      const disableValidates = this.getValueByLevel(this.getPropValue(this.field.disableValidates), this.getPropValue(this.configDisableValidates))
      if (this.field.prop && !this.isView && !disableValidates) {
        const defaultTrigger = ({
          'input': 'blur',
          'textarea': 'blur',
          'select': 'change',
          'date-picker': 'change',
          'radio': 'change',
          'checkbox': 'change'
        })[this.field.type] || 'submit'
        result = (this.getPropValue(this.field.rules) || []).filter((item) => { return !!item }).map((item) => {
          if (item.trigger === undefined) {
            item.trigger = item.trigger || defaultTrigger
          }
          if (item.trigger === 'submit') {
            const originValidator = item.validator
            item.validator = (rule, value, callback) => {
              if (this.useSubmitValidate) {
                originValidator(rule, value, callback)
              } else {
                callback()
              }
            }
          }
          return item
        })

        if (this.getPropValue(this.field.required)) {
          result.push({
            required: true,
            message: this.getPropValue(this.field.requiredMessage) || (this.fieldTips + (this.getPropValue(this.field.label) || '')),
            trigger: defaultTrigger,
            validator: (rule, value, callback) => {
              if (this.dontValidateRequired && !this.getPropValue(this.field.forceRequired)) {
                callback()
              } else {
                if (this.isMultipleValue) {
                  callback((Array.isArray(value) && value.length > 0) ? undefined : rule.message)
                } else {
                  callback([undefined, null, ''].includes(value) ? rule.message : undefined)
                }
              }
            }
          })
        }
        result.push({
          trigger: 'submit',
          validator: (rule, value, callback) => {
            if (!this.modelValue) {
              callback()
              return
            }
            if (!this.useSubmitValidate) {
              callback()
              return
            }
            if (this.fieldType === 'select' && this.getFormProps('remote') === true) {
              callback()
              return
            }
            if (this.getPropValue(this.field.dontValidateOption)) {
              callback()
              return
            }
            if (['radio', 'select', 'cascader'].includes(this.fieldType) && !this.isMultipleValue) {
              const option = this.findOption(null, {
                predicate: (item) => {
                  const valueIsObject = typeof item.value === 'object' && item.value !== null
                  if (valueIsObject) {
                    const modelValueKey = this.modelValue ? this.modelValue._key : ''
                    const flag = modelValueKey === item.value._key
                    if (!flag && String(modelValueKey) && String(modelValueKey) === String(item.value._key)) {
                      console.warn(`所选值[${modelValueKey}](${typeof modelValueKey})与选项值[${item.value._key}](${typeof item.value._key})类型不一致`)
                    }
                    return flag
                  } else {
                    const flag = this.modelValue === item.value
                    if (!flag && String(this.modelValue) && String(this.modelValue) === String(item.value)) {
                      console.warn(`所选值[${this.modelValue}](${typeof this.modelValue})与选项值[${item.value}](${typeof item.value})类型不一致`)
                    }
                    return flag
                  }
                }
              })
              if (!option) {
                let selectedLabel = ''
                if (this.$refs.ElComponent) {
                  selectedLabel = this.$refs.ElComponent.selectedLabel
                }
                callback(`选择的[${selectedLabel || this.modelValue}]不存在于选项中，请重新选择。`)
                this.$set(this, 'modelValue', '')
                return
              } else if (option.show === false) {
                const userNotFoundReason = this.getPropValue(option.notFoundReason, value, option, this.options)
                callback(userNotFoundReason || `选择的[${this.modelValue}]不存在于选项中，请重新选择。`)
                this.$set(this, 'modelValue', '')
                return
              } else if (option.disabled === true) {
                const userDisabledReason = this.getPropValue(option.disabledReason, value, option, this.options)
                callback(userDisabledReason || `当前选择的[${option.label}]已禁用，请重新选择。`)
                this.$set(this, 'modelValue', '')
                return
              }
            } else if (this.isMultipleValue && Array.isArray(this.modelValue)) {
              for (const item of this.modelValue) {
                const option = this.findOption(item)
                if (!option) {
                  callback(`选择的[${item}]不存在于选项中，此项已重置，请重新选择。`)
                  this.$set(this, 'modelValue', [])
                  return
                } else if (option.show === false) {
                  callback(`选择的[${option.label}]不存在于选项中，此项已重置，请重新选择。`)
                  this.$set(this, 'modelValue', [])
                  return
                } else if (option.disabled === true) {
                  callback(`选择的[${option.label}]已禁用，此项已重置，请重新选择。`)
                  this.$set(this, 'modelValue', [])
                  return
                }
              }
            }
            callback()
          }
        })
      }
      return result
    },
    _disabled() {
      return this.configReadOnly || this.getFormProps('disabled') || this.getPropValue(this.field.disabled) || this.itemController['disabled']
    },
    buttons() {
      return (this.getPropValue(this.field.buttons) || []).filter((button) => { return this.getPropValue(button.show) !== false })
    },
    optionsLoading() {
      if (this.itemController['userOptionsLoading']) {
        return true
      }
      const optionsBaseCode = this.getPropValue(this.field.optionsBaseCode)
      if (optionsBaseCode) {
        return this.OptionsMap[optionsBaseCode] instanceof Promise
      }
      return false
    }
  },
  watch: {
    fieldType: {
      immediate: true,
      handler(value) {
        if (value === 'cascader') {
          showWarning('cascader为测试中的组件，建议使用type: slot 自行实现')
        }
      }
    },
    modelValue(value, oldValue) {
      this.callOnChange(value, false)
    },
    rules(value) {
      this.$emit('update:rules', value)
    }
  },
  created() {
    this.initModelValue()
    this.$emit('update:rules', this.rules)
    this.$emit('created', this)
    this.callOnChange(this.modelValue, false)
    this.callOnCreated()
  },
  mounted() {
    this.callOnMounted()
  },
  beforeDestroy() {
    this.$emit('beforeDestroy', this)
  },
  methods: {
    getOptions(originOptions, optionsBaseCode, parentItem) {
      const baseCodeOptions = optionsBaseCode ? getOptionsByBaseCode(this.getPropValue(optionsBaseCode)) : []
      const options = originOptions ? (this.getPropValue(originOptions, baseCodeOptions) || []) : baseCodeOptions
      const {
        labelKeyName = 'label',
        valueKeyName = 'value',
        childrenKeyName = 'children',
        leafKeyName = 'leaf',
        isLeaf,
        subLabel
      } = this.getPropValue(this.field.optionConfig) || {}
      return options.map((item) => {
        if (typeof item === 'object') {
          return {
            ...item,
            label: [undefined, null].includes(item[labelKeyName]) ? item[valueKeyName] : item[labelKeyName],
            value: [undefined, null].includes(item[valueKeyName]) ? item[labelKeyName] : item[valueKeyName],
            children: item[childrenKeyName],
            leaf: item[leafKeyName]
          }
        } else {
          return { value: item, label: item }
        }
      }).map((item) => {
        const copy = {}
        for (const key in item) {
          copy[key] = this.getPropValue(item[key])
        }
        return copy
      }).map((item) => {
        const _parent = []
        if (parentItem) {
          if (Array.isArray(parentItem._parent) && parentItem._parent.length > 0) {
            _parent.push(...parentItem._parent)
          }
          _parent.push({
            ...parentItem,
            children: undefined
          })
        }
        const currentItem = {
          ...item,
          _parent
        }
        let children
        if (typeof isLeaf === 'function') {
          children = isLeaf(item) ? undefined : this.getOptions(item.children || [], undefined, currentItem)
        } else {
          children = item.children ? this.getOptions(item.children, undefined, currentItem) : undefined
        }
        currentItem.children = children
        return currentItem
      }).map((item) => {
        const valueIsObject = typeof item.value === 'object' && item.value !== null
        if (valueIsObject && !item.key) {
          console.error('当value为Object时，必须为Options的每一个对象设置一个key值', options)
        }
        return valueIsObject ? {
          ...item,
          value: {
            _key: item.key,
            ...item.value
          }
        } : {
          _key: item.key ?? item.value,
          ...item
        }
      }).map((item) => {
        const configSubLabel = typeof subLabel === 'function' ? subLabel(item) : subLabel
        return {
          ...item,
          subLabel: (configSubLabel === undefined && item.subLabel === undefined) ? (item._parent || []).map((parent) => {
            return parent.label
          }).join(this.getFormProps('separator') || '/') : (item.subLabel || configSubLabel)
        }
      })
    },
    optionsFilterShowMethod(options) {
      return options.filter((item) => {
        return item.show !== false
      }).map((item) => {
        return {
          ...item,
          children: Array.isArray(item.children) ? this.optionsFilterShowMethod(item.children) : undefined
        }
      })
    },
    /**
     * 获取选项对象
     * @param {*} findOptions.predicate 自定义查找逻辑
     * @param {*} findOptions.deepLimit  往下查找深度限制 1表示仅在顶层查找
     * @returns
     */
    getOptionValue(prop = '', findOptions) {
      if (prop && prop !== this.field.prop) {
        throw new Error('组件上层未提供getOptionValue，无法查询其他项的值')
      }
      if (this.isMultipleValue) {
        return this.modelValue.map((item) => {
          return this.findOption(item, findOptions)
        }).filter((item) => {
          return !!item
        })
      } else {
        return this.findOption(this.modelValue, findOptions)
      }
    },
    getFormProps(name) {
      return (this.getPropValue(this.field.formProps) || {})[name]
    },
    initModelValue() {
      if (!this.field.prop) {
        return
      }
      if (this.formData && !(this.field.prop in this.formData)) {
        this.formData[this.field.prop] = undefined
      }
      if (this.formData[this.field.prop] === undefined || this.formData[this.field.prop] === null) {
        if (this.field.defaultValue !== undefined) {
          if (typeof this.field.defaultValue === 'function') {
            try {
              this.formData[this.field.prop] = this.field.defaultValue(
                this._componentScope,
                this.formData[this.field.prop]
              )
            } catch (e) {
              console.error(e)
            }
          } else {
            this.formData[this.field.prop] = this.field.defaultValue
          }
        } else if (this.isMultipleValue) {
          this.formData[this.field.prop] = []
        } else if (this.fieldType === 'date-picker' && this.getFormProps('type') === 'daterange') {
          this.formData[this.field.prop] = []
        }
      } else if (this.field.praseValue) {
        if (typeof this.field.praseValue === 'function') {
          try {
            this.formData[this.field.prop] = this.field.praseValue(
              this._componentScope,
              this.formData[this.field.prop]
            )
          } catch (e) {
            console.error(e)
          }
        } else {
          this.formData[this.field.prop] = this.field.praseValue
        }
      } else {
        // 默认praseValue
        if (this.isMultipleValue && typeof this.formData[this.field.prop] === 'string' && this.getPropValue(this.field.valueSplitChar)) {
          this.formData[this.field.prop] = this.formData[this.field.prop].split(this.getPropValue(this.field.valueSplitChar))
        }
      }
      if (this.isMultipleValue && Array.isArray(this.formData[this.field.prop])) {
        for (const option of filterDeepOption(this.options, (option) => { return option.required })) {
          if (!this.formData[this.field.prop].includes(option.value)) {
            this.formData[this.field.prop].push(option.value)
          }
        }
      }
    },
    callButtonHandler(button) {
      this.$set(button, 'loading', true)
      try {
        const result = button.handler(this._componentScope, button)
        if (result instanceof Promise) {
          result.finally(() => {
            this.$set(button, 'loading', false)
          })
        } else {
          this.$set(button, 'loading', false)
        }
      } catch (e) {
        console.error(e)
        this.$set(button, 'loading', false)
      }
    },
    callOnChange(value, isFromUser) {
      this.onChangeIsFromUser = this.onChangeIsFromUser || isFromUser
      if (this.onChangeTimer === null) {
        this.onChangeTimer = setTimeout(() => {
          try {
            // default onChange
            if (this.fieldType === 'date-picker') {
              if (this.getFormProps('type') === 'daterange' && Array.isArray(this.modelValue)) {
                const valueFormat = this.getFormProps('valueFormat') || 'yyyy-MM-dd hh:mm:ss'
                const startDate = new Date(this.modelValue[0])
                startDate.setHours(0, 0, 0, 0)
                const endDate = new Date(this.modelValue[1])
                endDate.setHours(23, 59, 59, 999)
                this.modelValue[0] = parseDate(startDate, valueFormat)
                this.modelValue[1] = parseDate(endDate, valueFormat)
              }
              if (!Array.isArray(this.modelValue) && this.modelValue) {
                this.modelValue = parseDate(this.modelValue, this.getFormProps('valueFormat') || 'yyyy-MM-dd HH:mm:ss')
              }
            }
            if (this.field.cnProp) {
              this.formData[this.field.cnProp] = this.options.find((item) => {
                return this.modelValue === item.value
              })?.label
            }
            if (typeof this.field.onChange === 'function') {
              this.field.onChange(this._componentScope, this.modelValue, this.onChangeIsFromUser)
            }
            this.$emit('change', this._componentScope, this.modelValue, this.onChangeIsFromUser)
          } catch (e) {
            console.error(e)
          }
          this.onChangeTimer = null
          this.onChangeIsFromUser = false
        })
      }
    },
    callOnCreated() {
      if (typeof this.field.onCreated === 'function') {
        try {
          this.field.onCreated(this._componentScope)
        } catch (e) {
          console.error(e)
        }
      }
    },
    callOnMounted() {
      if (typeof this.field.onMounted === 'function') {
        try {
          this.field.onMounted(this._componentScope)
        } catch (e) {
          console.error(e)
        }
      }
    },
    getPropValue(prop, ...args) {
      return typeof prop === 'function' ? prop(this._componentScope, ...args) : prop
    },
    getPropArrayValue(prop, ...args) {
      const propValue = this.getPropValue(prop, ...args)
      if ([null, undefined].includes(propValue)) {
        return []
      } else {
        return Array.isArray(propValue) ? propValue : [propValue]
      }
    },
    getValueByLevel(...args) {
      for (const value of args) {
        if (value !== undefined) {
          return value
        }
      }
      return undefined
    },
    defaultViewer() {
      if (this.isMultipleValue && Array.isArray(this.modelValue)) {
        return this.modelValue.map((item) => {
          const target = this.findOption(item)
          return target ? target.label : item
        }).join('、')
      } else {
        let result = this.modelValue
        const target = this.findOption(result)
        if (target) {
          return target.label
        }
        if (result && this.fieldType === 'date-picker' && this.getFormProps('format')) {
          return parseDate(result, this.getFormProps('format')) || result
        }
        if (this.getPropValue(this.field.append)) {
          result = `${result}${this.getPropValue(this.field.append)}`
        }
        if (this.getPropValue(this.field.prepend)) {
          result = `${this.getPropValue(this.field.prepend)}${result}`
        }
        return result
      }
    },
    parseData() {
      let value = typeof this.modelValue === 'object' ? deepClone(this.modelValue) : this.modelValue
      const type = 'value'
      if (this.isMultipleValue) {
        value = value.sort((a, b) => {
          return findDeepOptionIndex(this.options, (item) => {
            return a === item.value
          }) - findDeepOptionIndex(this.options, (item) => {
            return b === item.value
          })
        })
      }
      if (typeof this.field.valueFormat === 'function') {
        try {
          value = this.field.valueFormat(this._componentScope, value)
        } catch (e) {
          console.error(e)
        }
      } else if (this.isMultipleValue) {
        if (this.getPropValue(this.field.valueSplitChar)) {
          value = value.join(this.getPropValue(this.field.valueSplitChar))
        }
      }
      value = this.trimValue(value)
      return {
        value,
        type
      }
    },
    getComputer() {
      const userComputer = this.getPropValue(this.field.computer)
      if (userComputer) {
        return userComputer
      } else {
        const autoAppend = this.getPropValue(this.field.autoAppend)
        return {
          get: (value) => {
            // 表单 -> 输入组件
            if (autoAppend && value) {
              if (Array.isArray(autoAppend)) {
                for (const a of this.getOptions(autoAppend)) {
                  if (value.endsWith(a.value)) {
                    return value.slice(0, value.length - a.value.length)
                  }
                }
              } else {
                return value.slice(0, value.length - autoAppend.length)
              }
            }
            return this.trimValue(value)
          },
          set: (value) => {
            // 输入组件 -> 表单
            if (autoAppend && value) {
              if (Array.isArray(autoAppend)) {
                return value + this.appendModel
              } else {
                return value + autoAppend
              }
            }
            if (this.isMultipleValue && Array.isArray(value)) {
              return value.sort((a, b) => {
                return findDeepOptionIndex(this.options, (item) => {
                  return a === item.value
                }) - findDeepOptionIndex(this.options, (item) => {
                  return b === item.value
                })
              })
            }
            return this.trimValue(value)
          }
        }
      }
    },
    applyComputed(value, type) {
      const computer = this.getComputer()
      if (type === 'get') {
        return computer.get(value)
      } else if (type === 'set') {
        return computer.set(value)
      }
    },
    trimValue(value) {
      if (this.getValueByLevel(this.field.trim, this.configTrim) !== false && typeof value === 'string') {
        return value.trim()
      } else {
        return value
      }
    },
    findOption(value, { predicate, deepLimit } = {}) {
      return findDeepOption(this.options, predicate || ((option) => {
        return option.value === value
      }), deepLimit || (this.fieldType === 'cascader' ? undefined : 1))
    }
  }
}
</script>

<style lang="scss" scoped>
.easy-form-item{
  width: 100%;
  display: inline-block;
  &.default-display{
    display: flex;
    align-items: center;
  }
  &>.el-select,&>.el-range-editor,&>.el-date-editor,&>.el-cascader{
    width: 100%;
    /deep/ .el-input__prefix{
      display: flex;
      align-items: center;
      i{
        font-size: 18px;
      }
    }
  }
  &>.el-input /deep/{
    &>.el-input-group__append{
      width: unset;
    }
  }
}
.el-upload-readonly{
  /deep/ .el-upload{
    display: none;
  }
}
.select-option-item{
  display: flex;
  max-width: 100vw;
  align-items: center;
  justify-content: space-between;
  .select-option-item-label{
    pointer-events: none;
    min-width: 120px;
  }
  .select-option-item-subLabel{
    color: #8492a6;
    font-size: 13px;
  }
}
</style>
<style lang="scss">
.easy-form-item-select.el-select-dropdown{
  max-width: 80vw;
  .el-select-dropdown__item{
    height: unset;
    min-height: 34px;
    white-space: normal;
  }
}
.easy-form-item-select.el-select-dropdown.is-multiple{
  .el-select-dropdown__item{
    padding-right: 20px;
  }
  .el-select-dropdown__item.selected::after{
    display: none;
  }
}
</style>
