<template>
  <el-form ref="form" v-loading="loading" :model="formData" v-bind="{ 'label-width': '120px', ...$attrs }" @submit.native.prevent>
    <el-form-item v-for="(item, i) in formModel" :key="i" :label="showLabel ? `${item.label}：` : ''" :prop="item.prop" :rules="item.rules || []" :class="{ 'form-item-hidden': i + 1 > showConditionNum }">
      <h4 v-if="showBlockLabel || false" style="margin: 4px 0">
        {{ item.label }}
      </h4>
      <!-- input -->
      <el-input v-if="item.type === 'input'" v-model="formData[item.prop]" v-bind="item.attrs" :placeholder="`${item.placeholder ? item.placeholder : '请输入' + item.label}`" @keyup.native="handleKeyup($event, item)" @input="handleInput($event, item)" @blur="handleBlur($event, item)" @focus="handleFocus($event, item)">
        <template v-if="item.append" slot="append">
          {{ item.append }}
        </template>
      </el-input>
      <!-- select -->
      <el-select
        v-if="item.type === 'select'"
        v-model="formData[item.prop]"
        v-bind="item.attrs"
        :remote-method="
          //重写远程方法
          (v) => {
            if (!item.attrs.remoteMethods || !item.attrs.remote) return
            item.attrs.loading = true
            item.attrs
              .remoteMethods(v, item)
              .then((res) => {
                item.options = res || []
                item.attrs.loading = false
              })
              .catch((err) => {
                item.options = []
                item.attrs.loading = false
              })
          }
        "
        :placeholder="`请选择 ${item.label}`"
        @focus="autoInit(item)"
        @change="handleSelectChange($event, item)"
      >
        <el-option v-for="t in item.options || []" :key="t[item.optionAttrs ? item.optionAttrs.valueKey : 'value']" :label="item.optionAttrs ? (typeof item.optionAttrs.labelKey === 'function' ? item.optionAttrs.labelKey(t) : t[item.optionAttrs.labelKey]) : t.label" :value="t[item.optionAttrs ? item.optionAttrs.valueKey : 'value']" />
      </el-select>
      <!-- date -->
      <el-date-picker v-if="item.type === 'daterange' || item.type === 'date'" v-model="formData[item.prop]" value-format="yyyy-MM-dd" :start-placeholder="!item.attrs['start-placeholder'] ? `请选择开始时间` : item.attrs['start-placeholder']" :end-placeholder="!item.attrs['end-placeholder'] ? `请选择结束时间` : item.attrs['end-placeholder']" :placeholder="`请选择时间`" :type="item.type" v-bind="item.attrs" @change="datePickerChange($event, item)" />
      <!-- switch -->
      <el-switch v-if="item.type === 'switch'" v-model="formData[item.prop]" v-bind="item.attrs" />
      <!-- text -->
      <span v-if="item.type === 'text'">
        <template v-if="item.textMap && Array.isArray(item.textMap)">
          {{ handleTextMap(item) | formatEmpty }}
        </template>
        <template v-else>{{ formData[item.prop] | formatEmpty }}</template>
      </span>
      <!-- template -->
      <template v-if="item.template">
        <slot :name="item.template" :scope="item" />
      </template>
    </el-form-item>
    <slot />
  </el-form>
</template>

<script>
import { integerFormat, decimalFormat } from '~/utils'

export default {
  name: 'GenForm',
  filters: {
    formatEmpty(val) {
      return val === '' || val === null || val === undefined ? '-' : val
    }
  },
  props: {
    showLabel: {
      type: Boolean,
      default: true
    },
    formModel: {
      type: Array,
      required: true,
      default: () => []
    },
    formData: {
      type: Object,
      default: () => ({})
    },
    showConditionNum: {
      type: Number,
      default: Infinity
    },
    showMore: {
      type: Boolean,
      default: true
    },
    showBlockLabel: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      loading: false,
      optionsPromises: []
    }
  },
  watch: {
    formModel: {
      handler: 'loadFormOptions'
    }
  },
  mounted() {
    this.loadFormOptions()
    // 该事件是从BasicPage的handleReset重置事件触发而来。主要是为了解决搜索栏两个form表单产生联动时数据重置问题
    this.$bus.$on('searchReset', () => {
      for (const key in this.formData) {
        this.formModel.forEach(async (item) => {
          if (item.prop == key) {
            // 找到被关联的form表单
            const relationTarget = this.formModel.filter((tem) => tem.prop === item.relation)
            if (relationTarget.length > 0 && item.change) {
              this.$set(relationTarget[0], 'options', await item.change(item.defaultValue))
            }
          }
        })
      }
    })
  },
  beforeDestroy() {
    window.removeEventListener('keypress', this.handleKeyboard)
    this.$bus.$off('searchReset')
  },
  methods: {
    /**
     * @description: 当某个表单组件需要和根据另一个表单组建的数据变化产生联动时执行，具体用法请参考views/fameManage/gameStore/index.vue中的searchModel搜索配置项
     * @param {*} val 被联动表单变化时的参数
     * @param {*} item 被联动表单的具体属性
     * @return {*}
     */
    async handleSelectChange(val, item) {
      if (item.type === 'select' && item.change && item.relation) {
        // 根据设置的relation找到需要联动的表单项，以更改数据项。
        const relationTarget = this.formModel.filter((tem) => tem.prop === item.relation)
        if (relationTarget.length > 0) {
          const tempResult = await item.change(val)
          // 关联的组件变动以后清理掉被关联的数据。
          this.$set(this.formData, relationTarget[0].prop, relationTarget[0].defaultValue || '')
          this.$set(relationTarget[0], 'options', tempResult)
          this.$forceUpdate()
        }
      }
    },
    //@备注:自动初始化表单选项
    //@取值:
    autoInit(item) {
      // if (!item.attrs.remoteMethods || !item.attrs.remote) return
      // if (!this.formData[item.prop] && item.options) return
      // item.attrs.loading = true
      // item.attrs
      //   .remoteMethods('', item)
      //   .then((res) => {
      //     item.options = res || []
      //     item.attrs.loading = false
      //   })
      //   .catch((err) => {
      //     item.options = []
      //     item.attrs.loading = false
      //   })
    },
    remoteMethods(e, v) {
      console.log(123123123, e, v)
    },

    loadFormOptions() {
      this.loading = true
      this.optionsPromises = []
      const promises = []
      this.formModel.forEach((m) => {
        if (m.type === 'select' && typeof m.optionsPromise === 'function') {
          this.optionsPromises.push({
            prop: m.prop,
            target: m,
            promise: m.optionsPromise.bind(this, m)
          })
          promises.push(
            m.optionsPromise(m).then((res) => {
              m.options = res
            })
          )
        }
        // else if (m.type.includes('range') && m.rangeKeys && m.rangeKeys.length == 2) {
        //   //初试值判定
        //   this.formData[m.rangeKeys[0]] = m.value ? m.value[0] : ''
        //   this.formData[m.rangeKeys[1]] = m.value ? m.value[1] : ''
        // }
        else if (m.type === 'select' && typeof m.defaultValue !== 'undefined' && typeof this.formData[m.prop] == 'undefined') {
          // 如果是没有通过接口拿下拉数据，而是自己定义的枚举类型，需要设置默认值的情况下，就执行这里
          this.formData[m.prop] = m.defaultValue
        }
      })
      if (promises.length > 0) {
        Promise.all(promises).finally(() => {
          this.loading = false
        })
      } else {
        this.loading = false
      }
    },
    // 重新获取选项options数据
    refreshItemOptions(propName) {
      const options = this.optionsPromises.filter((o) => o.prop === propName)
      if (!options.length && typeof options[0] !== 'function') return
      const t = options[0]
      t.promise().then((res) => {
        t.target['options'] = res
      })
    },
    handleTextMap(item) {
      const map = {}
      item.textMap.forEach((t) => {
        map[t.value] = t.label
      })
      return map[this.formData[item.prop]]
    },
    handleKeyup(e, item) {
      if (item.customProps && typeof item.customProps.keyup === 'function') {
        item.customProps.keyup.call(this, this.formData[item.prop], item)
      }
    },
    handleInput(val, item) {
      const attrs = item.attrs || {}
      let value = this.formData[item.prop]
      let max = attrs.maxlength || 30
      if (attrs.type === 'number' && attrs.decimal && val.indexOf('.') !== -1) {
        max += 3
      }
      if (val.length > max) value = val.slice(0, max)
      if (attrs.type === 'number') {
        if (value.startsWith('.')) value = `0${value}`
        if (attrs.int) value = integerFormat(value)
        if (attrs.decimal) value = decimalFormat(value)
        if (value.substring(value.length - 1) === '.') {
          value = value.substring(0, value.length - 3)
        }
      }
      this.formData[item.prop] = value
      if (item.customProps && typeof item.customProps.input === 'function') {
        item.customProps.input.call(this, value, item)
      }
    },
    handleBlur(e, item) {
      if (item.attrs && item.attrs.type === 'number') {
        window.removeEventListener('keypress', this.handleKeyboard)
      }
      let value = this.formData[item.prop]
      const attrs = item.attrs || {}
      if (attrs.type === 'number') {
        if (value !== '' && value !== null && value !== undefined && !isNaN(Number(value))) {
          value = Number(value)
          if (attrs.abs) value = Math.abs(value)
          const min = attrs.min || 0
          const max = attrs.max
          value = value < min ? min : value
          value = max && value > max ? max : value
          this.formData[item.prop] = value
        }
      }
      if (item.customProps && typeof item.customProps.blur === 'function') {
        item.customProps.blur.call(this, e, item)
      }
    },
    handleFocus(e, item) {
      if (item.attrs && item.attrs.type === 'number') {
        window.addEventListener('keypress', this.handleKeyboard)
      }
      if (item.customProps && typeof item.customProps.focus === 'function') {
        item.customProps.focus.call(this, e, item)
      }
    },
    handleKeyboard(e) {
      const keys = ['+', '-', '*', '/', 'e']
      if (keys.includes(e.key)) {
        e.preventDefault()
      }
    },
    handleLoadMore(item) {
      if (item.type !== 'select') return
      if (!item.loadMore || typeof item.loadMore !== 'function') return
      const pageInfo = {
        currentPage: 1,
        pageSize: 20
      }
      return item.loadMore.bind(this, pageInfo, item)
    },
    datePickerChange(e, item) {
      // console.log(e, 112233)
      if (
        //range  类型自动回归值
        item.type.includes('range') &&
        item.rangeKeys &&
        item.rangeKeys.length == 2
      ) {
        // item.customProps.change.call(this, e, this.formData)
        this.formData[item.rangeKeys[0]] = e ? e[0] : ''
        this.formData[item.rangeKeys[1]] = e ? e[1] : ''
      }
      // console.log('item', item, e, this.formData)
      if (item.customProps && typeof item.customProps.change === 'function') {
        item.customProps.change.call(this, e, this.formData)
      }
    },
    validate(cb) {
      this.$refs.form.validate(cb)
    },
    validateField() {
      this.$refs.form.validateField()
    },
    resetFields() {
      this.$refs.form.resetFields()
    },
    clearValidate() {
      this.$refs.form.clearValidate()
    }
  }
}
</script>

<style lang="scss" scoped>
::v-deep {
  input::-webkit-outer-spin-button,
  input::-webkit-inner-spin-button {
    -webkit-appearance: none;
  }

  input[type='number'] {
    -moz-appearance: textfield;
  }

  .el-form-item__content {
    .el-select {
      width: 100%;
    }
  }

  .gen-form__input-number {
    width: 100%;

    .el-input__inner {
      text-align: left;
    }
  }
}

.form-item-hidden {
  display: none !important;
}
</style>
