<template>
  <el-form @submit.prevent :model="formModel" v-bind="_options" ref="formRef">
    <el-row :gutter="5">
      <template v-for="(item, index) in fieldList" :key="index">
        <el-col :span="item.span || 24">
          <el-form-item :rules="item.rules" :title="item.label" :prop="[item.field]">
            <template #label>
              <el-space :size="1">
                <span>{{ `${item.label}` }}</span>
                <el-tooltip v-if="item?.tooltip" effect="dark" :content="item.tooltip" placement="top">
                  <i :class="'iconfont icon-yiwen'"></i>
                </el-tooltip>
              </el-space>
            </template>
            <template #default>
              <!-- 单选框 -->
              <el-radio-group v-if="item.type === 'radio'" v-model="formModel[item.field]" :disabled="item.disabled">
                <el-radio
                  :label="val[item.options?.valueKey || 'value']"
                  v-for="val in item.options?.data"
                  :key="val[item.options?.valueKey || 'value']"
                >
                  {{ val[item.options?.labelKey || 'label'] }}
                </el-radio>
              </el-radio-group>
              <!-- 复选框 -->
              <el-checkbox-group v-else-if="item.type === 'checkbox'" v-model="formModel[item.field]" :disabled="item.disabled">
                <el-checkbox
                  v-for="val in item.options?.data"
                  :key="val[item.options?.valueKey || 'value']"
                  :label="val[item.options?.valueKey || 'value']"
                  >{{ val[item.options?.labelKey || 'label'] }}
                </el-checkbox>
              </el-checkbox-group>
              <!-- 下拉框 -->
              <el-select
                v-else-if="item.type === 'select'"
                v-model="formModel[item.field]"
                :placeholder="item.options?.placeholder || '请选择'"
                :clearable="item.clearable"
                :multiple="item.options?.multiple"
                :filterable="item.options?.filterable"
                :allow-create="item.options?.allowCreate"
                :disabled="item.disabled"
              >
                <el-option
                  v-for="val in item.options?.data"
                  :key="val[item.options?.valueKey || 'value']"
                  :label="val[item.options?.labelKey || 'label']"
                  :value="val[item.options?.valueKey || 'value']"
                />
              </el-select>
              <el-tree-select
                v-else-if="item.type === 'select-tree'"
                v-model="formModel[item.field]"
                :data="item.options?.data"
                :props="{
                  label: `${item.options?.labelKey || 'label'}`,
                  children: `${item.options?.children || 'children'}`
                }"
                :node-key="`${item.options?.valueKey || 'id'}`"
                placeholder="请选择归属部门"
                clearable
                check-strictly
              />
              <!-- 时间 -->
              <el-date-picker
                v-else-if="item.type === 'date-picker'"
                v-model="formModel[item.field]"
                :readonly="item.readonly"
                :type="item.options?.type ?? 'data'"
                :placeholder="item.placeholder || item.label"
                :disabled="item.disabled"
                :value-format="item.options?.valueFormat ?? 'YYYY-MM-DD HH:mm:ss'"
                :clearable="item.clearable"
              />
              <el-input-number
                v-else-if="item.type === 'input-number'"
                v-model="formModel[item.field]"
                :min="item.options?.min"
                :max="item.options?.max"
                controls-position="right"
              />
              <SelectIcon v-else-if="item.type === 'select-icon'" v-model:icon-value="formModel[item.field]" />
              <!-- 默认输入框 -->
              <el-input
                v-else
                v-model="formModel[item.field]"
                :readonly="item.readonly"
                :type="item.type ?? 'text'"
                :placeholder="item.placeholder || item.label"
                :disabled="item.disabled"
                :show-password="item.showPassword"
                :clearable="item.clearable"
                @keyup.enter="handleKeyUp(item.enterable)"
              />
            </template>
          </el-form-item>
        </el-col>
      </template>
    </el-row>
    <el-form-item v-if="_options.hasFooter">
      <slot name="buttons" :model="formModel" :form-ref="formRef">
        <el-button type="primary" @click="onSubmit(formRef)">{{ _options.submitButtonText }}</el-button>
        <el-button v-if="_options.showResetButton" type="info" @click="resetForm(formRef)">
          {{ _options.resetButtonText }}
        </el-button>
        <el-button v-if="_options.showCancelButton" @click="emit('cancel')">
          {{ _options.cancelButtonText }}
        </el-button>
      </slot>
    </el-form-item>
  </el-form>
</template>
<script setup lang="ts" name="ProFormOld">
import type { FormInstance } from 'element-plus'
import { ComputedRef, ref, computed, watch, unref } from 'vue'
import SelectIcon from '@/components/SelectIcon/index.vue'
// 父组件传递的值
export interface ProFormProps {
  fieldList: Form.FieldItem[]
  model?: Record<Form.FieldItem['field'], Form.FieldItem['value']>
  options?: Form.Options
}

// 表单的数据
const formModel = ref<Record<string, any>>({})
const formRef = ref<FormInstance>()
const props = defineProps<ProFormProps>()

// 设置option默认值，如果传入自定义的配置则合并option配置项
const _options: ComputedRef<Form.Options> = computed(() => {
  const option = {
    labelPosition: 'right',
    disabled: false,
    hasFooter: true,
    submitButtonText: '提交',
    resetButtonText: '重置',
    cancelButtonText: '取消'
  }
  return Object.assign(option, props?.options)
})
interface EmitEvent {
  (e: 'submit', params: any): void
  (e: 'reset'): void
  (e: 'cancel'): void
}
const enumMap = ref(new Map<string, { [key: string]: any }[]>())
const setEnumMap = async ({ field, enum: enumValue }: Form.FieldItem) => {
  if (!enumValue) return

  // 如果当前 enumMap 存在相同的值 return
  if (enumMap.value.has(field!) && (typeof enumValue === 'function' || enumMap.value.get(field!) === enumValue)) return

  // 当前 enum 为静态数据，则直接存储到 enumMap
  if (typeof enumValue !== 'function') return enumMap.value.set(field!, unref(enumValue!))

  // 为了防止接口执行慢，而存储慢，导致重复请求，所以预先存储为[]，接口返回后再二次存储
  enumMap.value.set(field!, [])

  // 当前 enum 为后台数据需要请求数据，则调用该请求接口，并存储到 enumMap
  let { data } = await enumValue()
  enumMap.value.set(field!, data)
}

// 处理枚举
const handelEnum = (item: Form.FieldItem) => {
  let enumData = enumMap.value.get(item.field)
  if (!enumData) return []
  // 返回对象类型
  if (item.type === 'select-tree' && item.options?.labelKey && item.options?.valueKey) {
    enumData = enumData.map((el: { [key: string]: any }) => {
      return {
        ...el,
        label: el[item.options?.labelKey || 'label'],
        children: el[item.options?.children || 'children'] || []
      }
    })
  }
  return enumData
}
// 处理表单项需要的参数
props.fieldList.forEach(async item => {
  await setEnumMap(item)
  if (item.options) item.options.data = handelEnum(item)
})
const emit = defineEmits<EmitEvent>()
// 根据fieldList初始化model， 如果formModel有传值就用传递的model数据模型，否则就给上面声明的formModel设置相应的(key,value) [item.field]， item.value是表单的默认值（选填）
watch(
  () => props.model,
  () => {
    props.fieldList.map((item: Form.FieldItem) => {
      // 如果类型为checkbox，默认值需要设置一个空数组
      const value = item.type === 'checkbox' ? [] : ''
      props.model ? (formModel.value = props.model) : (formModel.value[item.field] = item.value || value)
    })
  },
  { immediate: true }
)

// 提交按钮
const onSubmit = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.validate(valid => {
    if (valid) {
      emit('submit', formModel.value)
    } else {
      return false
    }
  })
}
// 输入框回车事件
const handleKeyUp = (enterable: boolean | undefined) => {
  if (!enterable) return
  onSubmit(formRef.value)
}
// 重置
const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}
defineExpose({
  formRef,
  resetForm,
  onSubmit
})
</script>
