<template>
  <template v-for="(option, index) in fnVerifyType(options)" :key="option.key || index">
    <template v-if="!option.hidden">
      <el-col
        v-if="!option.root"
        v-show="!collapse || index < 3"
        :span="option.type === formItemTypeEnum.DIVIDER ? 24 : option.span || span || 12"
      >
        <el-form-item
          v-if="option.type !== formItemTypeEnum.DIVIDER"
          :key="option.formItemKey"
          :label-width="option.labelWidth"
          :prop="option.key"
          :rules="fnGetRules(option)"
        >
          <template #label>
            <div style="display: flex; align-items: center">
              {{ option.label }}
              <el-tooltip v-if="option.tip" :content="option.tip">
                <el-icon :size="12" class="ml-2"><info-filled /></el-icon>
              </el-tooltip>
            </div>
          </template>
          <slot :name="option.key">
            <el-input
              v-if="option.type === formItemTypeEnum.INPUT || !option.type"
              v-model="modelValue[option.key]"
              :placeholder="'请输入' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-input
              v-else-if="option.type === formItemTypeEnum.TEXTAREA"
              v-model="modelValue[option.key]"
              :placeholder="'请输入' + option.label"
              type="textarea"
              :autosize="{ minRows: 3 }"
              v-bind="fnGetProps(option)"
            />
            <el-input-number
              v-else-if="
                option.type === formItemTypeEnum.INPUT_NUMBER || option.type === formItemTypeEnum.NUMBER
              "
              v-model="modelValue[option.key]"
              :placeholder="'请输入' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-select
              v-else-if="option.type === formItemTypeEnum.SELECT"
              v-model="modelValue[option.key]"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            >
              <el-option
                v-for="item in unref(option.options)"
                :key="
                  item[option.valueKey] || item['dictionariesId'] || item['id']
                "
                :label="item[option.labelKey] || item['name']"
                :value="
                  item[option.valueKey] || item['dictionariesId'] || item['id']
                "
              />
            </el-select>
            <el-checkbox-group
              v-else-if="option.type === formItemTypeEnum.CHECKBOX"
              v-model="modelValue[option.key]"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            >
              <el-checkbox
                v-for="item in unref(option.options)"
                :key="
                  item[option.valueKey] || item['dictionariesId'] || item['id']
                "
                :value="
                  item[option.valueKey] || item['dictionariesId'] || item['id']
                "
              >
                {{ item[option.labelKey] || item["name"] }}
              </el-checkbox>
            </el-checkbox-group>
            <el-radio-group
              v-else-if="option.type === formItemTypeEnum.RADIO"
              v-model="modelValue[option.key]"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            >
              <el-radio
                v-for="item in unref(option.options)"
                :key="
                  item[option.valueKey] || item['dictionariesId'] || item['id']
                "
                :value="
                  item[option.valueKey] || item['dictionariesId'] || item['id']
                "
              >
                {{ item[option.labelKey] || item["name"] }}
              </el-radio>
            </el-radio-group>
            <el-date-picker
              v-else-if="option.type === formItemTypeEnum.DATE"
              v-model="modelValue[option.key]"
              value-format="YYYY-MM-DD"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-date-picker
              v-else-if="option.type === formItemTypeEnum.DATE_MONTH"
              v-model="modelValue[option.key]"
              type="month"
              value-format="YYYY-MM"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-date-picker
              v-else-if="option.type === formItemTypeEnum.DATE_YEAR"
              v-model="modelValue[option.key]"
              type="year"
              value-format="YYYY"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-date-picker
              v-else-if="option.type === formItemTypeEnum.DATE_RANGE"
              v-model="modelValue[option.key]"
              type="daterange"
              value-format="YYYY-MM-DD"
              :start-placeholder="'请选择开始' + option.label"
              :end-placeholder="'请选择结束' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-date-picker
              v-else-if="option.type === formItemTypeEnum.DATETIME"
              v-model="modelValue[option.key]"
              type="datetime"
              value-format="YYYY-MM-DD HH:mm:ss"
              :placeholder="'请选择' + option.label"
              v-bind="fnGetProps(option)"
            />
            <el-date-picker
              v-else-if="option.type === formItemTypeEnum.DATETIME_RANGE"
              v-model="modelValue[option.key]"
              type="datetimerange"
              value-format="YYYY-MM-DD HH:mm:ss"
              :start-placeholder="'请选择开始' + option.label"
              :end-placeholder="'请选择结束' + option.label"
              :default-time="[
                new Date(2000, 1, 1, 0, 0, 0),
                new Date(2000, 2, 1, 23, 59, 59),
              ]"
              v-bind="fnGetProps(option)"
            />
            <component :is="option.type" v-else v-model="modelValue[option.key]" v-bind="fnGetProps(option)" />
          </slot>
        </el-form-item>
        <el-divider v-if="option.type === formItemTypeEnum.DIVIDER" content-position="left">
          {{ option.label }}
        </el-divider>
      </el-col>
      <template v-if="option.root">
        <slot :name="option.key">
          <component :is="option.type" v-model="modelValue[option.key]" v-bind="fnGetProps(option)" />
        </slot>
      </template>
    </template>
  </template>
</template>

<script setup>
import { omit } from "lodash-es";
import { unref } from "vue";
import { InfoFilled } from "@element-plus/icons-vue";
import {
  ElCol,
  ElFormItem,
  ElTooltip,
  ElIcon,
  ElInput,
  ElInputNumber,
  ElSelect,
  ElOption,
  ElCheckboxGroup,
  ElCheckbox,
  ElRadioGroup,
  ElRadio,
  ElDatePicker,
  ElDivider,
} from "element-plus";
import "element-plus/es/components/col/style/css";
import "element-plus/es/components/form-item/style/css";
import "element-plus/es/components/tooltip/style/css";
import "element-plus/es/components/icon/style/css";
import "element-plus/es/components/input/style/css";
import "element-plus/es/components/input-number/style/css";
import "element-plus/es/components/select/style/css";
import "element-plus/es/components/option/style/css";
import "element-plus/es/components/checkbox-group/style/css";
import "element-plus/es/components/checkbox/style/css";
import "element-plus/es/components/radio-group/style/css";
import "element-plus/es/components/radio/style/css";
import "element-plus/es/components/date-picker/style/css";
import "element-plus/es/components/divider/style/css";
import formItemTypeEnum from "../../enum/formItemType/index.js";

const props = defineProps({
  options: { type: Array, required: true },
  span: { type: Number, default: 0 },
  collapse: { type: Boolean, default: false },
  autoGenerateRequired: { type: Boolean, default: true },
});

const modelValue = defineModel({ type: Object, required: true });
const rootProps = [
  "label", // 表单项的标签
  "key", // 唯一值，对应form里的key
  "type", // 要渲染的组件，默认input
  "span", // 栅格占据的列数，默认12
  "hidden", // 是否隐藏
  "rules", // 表单验证规则
  "options", // select，radio，checkbox 的数据源
  "valueKey", // select，radio，checkbox 的 value字段
  "labelKey", // select，radio，checkbox 的 label字段
  "attrs", // 传给组件的属性
  "root", // 是否和el-col同级
  "labelWidth", // label的宽度
  "tip", // 跟在label后面的提示语
  "formItemKey", // el-form-item的key
  "required", // el-form-item是否必填，默认true
  // ... // 任意参数，全部当成组件属性，不能和attrs同时存在，优先使用attrs
];
const fnGetProps = (option) => {
  if (option.attrs) return option.attrs;
  return omit(option, rootProps);
};
const fnGetRules = (option) => {
  if (props.autoGenerateRequired === false) return {};
  if (option.type === formItemTypeEnum.DIVIDER) return {};
  if (option.required !== false) {
    const blur = option.type
      ? [
        formItemTypeEnum.INPUT,
        formItemTypeEnum.TEXTAREA,
        formItemTypeEnum.INPUT_NUMBER,
        formItemTypeEnum.NUMBER,
      ].includes(option.type)
      : true;
    const rules = [
      {
        required: true,
        message: `${blur ? "请输入" : "请选择"}${option.label}`,
        trigger: blur ? "blur" : "change",
      },
    ];
    if (option.rules) {
      if (Array.isArray(option.rules)) rules.push(...option.rules);
      else rules.push(option.rules);
    }
    return rules;
  }
  return {};
};
const fnVerifyType = (options) => {
  for (let i = 0; i < options.length; i++) {
    const option = options[i];
    if (option.type && typeof option.type === 'string' && !formItemTypeEnum[option.type])
      throw new Error(`${option.type} 类型枚举不存在，请参照并使用 formItemTypeEnum 类型枚举`);
  }
  return options;
};
</script>

<style scoped lang="scss"></style>
