<template>
  <component :is="'el-form'" v-bind="_formOptions" ref="proFormRef" :model="formModel">
    <el-row :gutter="5">
      <template v-for="item in itemsOptions" :key="item.prop">
        <el-col :span="item.span || 24" g>
          <component :is="'el-form-item'" v-bind="item" v-if="show(item.show)">
            <template #label>
              <el-space :size="4">
                <span class="label-span">{{ `${item.label}` }}</span>
                <!-- <el-tooltip :content="item.formItem.label" placement="top" :disabled="isShowTooltip">
                  <span class="label-span" @mouseenter="visibleTooltip">{{ `${item.formItem.label}` }}</span>
                </el-tooltip> -->
                <el-tooltip v-if="item?.tooltip" effect="dark" :content="item?.tooltip" placement="top">
                  <i :class="'iconfont icon-yiwen'"></i>
                </el-tooltip>
              </el-space>
              <span v-if="item.hideLabelSuffix">{{ `${item.hideLabelSuffix ? '' : ':'}` }}</span>
              <span v-else>{{ `${_formOptions.labelSuffix}` }}</span>
            </template>
            <!-- <template v-if="item.compOptions.elTagName === 'slot'"> -->
            <slot :name="item.prop" :form-model="formModel"></slot>
            <slot name="default" :form-model="formModel"></slot>
            <!-- </template> -->
            <template v-if="item.compOptions.elTagName === 'icon'">
              <SelectIcon v-model:icon-value="formModel[item.prop]" />
            </template>
            <template v-else-if="item.compOptions.elTagName === 'file-upload'">
              <FileUpload v-model:model-value="formModel[item.prop]" />
            </template>
            <Item v-else :item="item" :form-model="formModel" />
          </component>
        </el-col>
      </template>
    </el-row>
    <el-form-item v-if="_formOptions.hasFooter">
      <slot name="operation" :model="formModel" :pro-form-ref="proFormRef">
        <el-button type="primary" @click="onSubmit(proFormRef)">{{ _formOptions.submitButtonText }}</el-button>
        <el-button v-if="_formOptions.showResetButton" type="info" @click="resetForm(proFormRef)">
          {{ _formOptions.resetButtonText }}
        </el-button>
        <el-button v-if="_formOptions.showCancelButton" @click="emits('cancel')">
          {{ _formOptions.cancelButtonText }}
        </el-button>
      </slot>
    </el-form-item>
  </component>
</template>

<script setup lang="ts" name="ProForm">
import { ref, computed, ComputedRef, watch, unref, provide } from 'vue'
import type { FormInstance } from 'element-plus'
import Item from '@/components/ProForm/components/Item.vue'
import SelectIcon from '@/components/SelectIcon/index.vue'
import FileUpload from '@/components/Upload/File.vue'
// import { handleProp } from '@/utils'
// 表单整体配置项
export interface ProFormProps {
  formOptions?: ProForm.FormOptions
  itemsOptions: ProForm.ItemsOptions[]
  model?: Record<ProForm.FormItem['prop'], ProForm.FormItem['value']>
}

// 表单的数据
const formModel = ref<Record<string, any>>({})
// 表单ref
const proFormRef = ref<FormInstance>()
// 默认值
const props = withDefaults(defineProps<ProFormProps>(), {
  items: () => [],
  model: () => ({})
})
const show = (showFunction: any) => {
  if (!showFunction) return true
  if (typeof showFunction == 'function') {
    // 直接调用 showFunction 函数，并传入 formModel.value
    return showFunction(formModel.value)
  }
  // 如果 showFunction 不是函数，直接返回 true 显示该表单项
  return true
}
// 设置option默认值，如果传入自定义的配置则合并form配置项
const _formOptions: ComputedRef<ProForm.FormOptions> = computed(() => {
  const form = {
    inline: false,
    labelPosition: 'right',
    labelWidth: 120,
    disabled: false,
    hasFooter: true,
    labelSuffix: ': ',
    submitButtonText: '提交',
    resetButtonText: '重置',
    cancelButtonText: '取消'
  }
  return Object.assign(form, props?.formOptions)
})
// 事件传递定义
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 ({ prop, compOptions }: ProForm.ItemsOptions) => {
  const props = compOptions?.enumKey || prop
  const enumValue = compOptions?.enum
  if (!enumValue) return

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

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

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

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

  enumMap.value.set(props!, data)
}

// 注入 enumMap
provide('enumMap', enumMap)

// 处理表单项需要的参数
props.itemsOptions.forEach(async item => {
  await setEnumMap(item)
})
const emits = defineEmits<EmitEvent>()
// 根据items初始化model， 如果items有传值就用传递的model数据模型，否则就给上面声明的formModel设置相应的(key,value) [item.prop]， item.value是表单的默认值（选填）
watch(
  () => props.model,
  () => {
    props.itemsOptions.map((item: ProForm.ItemsOptions) => {
      // 如果类型为checkbox，默认值需要设置一个空数组
      const value = ['checkbox', 'transfer'].includes(item.compOptions.elTagName!) ? [] : ''
      props.model ? (formModel.value = props.model) : (formModel.value[item.prop] = item.value || value)
    })
  },
  { immediate: true }
)

// 提交按钮
const onSubmit = (formEl: FormInstance | undefined) => {
  console.log('表单提交数据', formModel.value)
  if (!formEl) return
  formEl.validate(valid => {
    if (valid) {
      emits('submit', formModel.value)
    } else {
    }
  })
}

// 重置
const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return
  formEl.resetFields()
}
// let isShowTooltip = ref(false)
// const visibleTooltip = e => {
//   console.log('e', e)

//   isShowTooltip.value = e.target.offsetWidth + 18 - e.fromElement.clientWidth > 0 ? false : true //18为required *号引起的偏差。
//   console.log('isShowTooltip', isShowTooltip.value)
// }
// 暴露方法给父组件使用
defineExpose({
  proFormRef,
  formModel,
  resetForm,
  onSubmit
})
</script>
<style scoped lang="scss">
@import './index.scss';
</style>
