<template>
  <div class="comp-content">
    <div class="form">
      <el-form ref="formRef" :model="formData" :rules="rules" label-width="auto">
        <el-row :gutter="20">
          <el-col v-for="(item, index) in tableData" :key="index" v-show="item.inForm" :span="24 / colNum">
            <el-form-item :label="item.title" :prop="item.key">
              <template v-if="item.inForm">
                <FormItem
                  :item="item"
                  :form="formData"
                  :showDisabled="showDisabled"
                  :attrs="formAttrs[item.key]"
                  @change="(val) => changeFun(val)"
                  @remove="(val) => removeFun(val)"
                >
                  <template v-for="(name) in Object.keys($slots)" :key="name" #[name]="scope">
                    <slot :name="name" v-bind="scope" />
                  </template>
                </FormItem>
              </template>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch } from 'vue';
import FormItem from './formItem.vue';
import { getFormConfigureApi } from '@/api/system/systemData';
import { isPhone, isEmail, hasNoSpecialChar, length20, length100 } from '@/utils/validate';
import { formOptionsFormat } from './lib';

const emit = defineEmits(['change', 'remove', 'load']);

interface Props {
  tableName: string;
  colNum?: number;
  showDisabled?: boolean;
  ruleData?: any;
  data?: any;
  formAttrs?: any;
}

const props = withDefaults(defineProps<Props>(), {
  tableName: '',
  colNum: 1,
  showDisabled: false,
  ruleData: {},
  data: {},
  formAttrs: {},
});

watch(() => props.data, (data: any) => {
  formData.value = formatDataFun(data);
});

onMounted(() => {
  getHeadDataFun();
});

const formatDataFun = (data: any) => {
  data.extras?.forEach((item: any) => {
    const { propertyName, propertyValue } = item;
    data[propertyName] = propertyValue;
  });
  return data;
};

const formData = ref<any>(formatDataFun(props.data));
const tableData = ref<any>([]);
const rules = ref<any>({});
const formRef = ref<any>();

const getHeadDataFun = () => {
  const params = {
    formName: props.tableName,
  };
  getFormConfigureApi(params).then((res: any) => {
    if (res.code === 200) {
      const data = JSON.parse(res.data.formConfig);
      const rulesObj: any = {};
      data.forEach((item: any) => {
        if (item.validate && item.inForm) {
          rulesObj[item.key] = [];
          item.validate.forEach((rule: any) => {
            if (rule === 'required') {
              rulesObj[item.key].push({ required: true, message: `请输入${item.title}`, trigger: 'change' });
            }
            if (rule === 'phone') {
              rulesObj[item.key].push({
                message: '请输入正确的手机号',
                trigger: 'change',
                validator: (rule: any, value: any, callback: any) => validatorFun(rule, value, callback, isPhone),
              });
            }
            if (rule === 'email') {
              rulesObj[item.key].push({
                message: '请输入正确的邮箱',
                trigger: 'change',
                validator: (rule: any, value: any, callback: any) => validatorFun(rule, value, callback, isEmail),
              });
            }
            if (rule === 'hasNoSpecialChar') {
              rulesObj[item.key].push({
                message: '不能包含特殊字符',
                trigger: 'change',
                validator: (rule: any, value: any, callback: any) => validatorFun(rule, value, callback, hasNoSpecialChar),
              });
            }
            if (rule === 'length20') {
              rulesObj[item.key].push({
                message: '长度不能大于20',
                trigger: 'change',
                validator: (rule: any, value: any, callback: any) => validatorFun(rule, value, callback, length20),
              });
            }
            if (rule === 'length100') {
              rulesObj[item.key].push({
                message: '长度不能大于100',
                trigger: 'change',
                validator: (rule: any, value: any, callback: any) => validatorFun(rule, value, callback, length100),
              });
            }
          });
        }
      });
      for (const key in props.ruleData) {
        if (!rulesObj[key]) {
          rulesObj[key] = [];
        }
        props.ruleData[key].forEach((rule: any) => {
          const validator = rule.validator;
          rulesObj[key].push({
            message: rule.message,
            trigger: rule.trigger,
            validator: (rule: any, value: any, callback: any) => validatorFun(rule, value, callback, validator),
          });
        });
      }
      rules.value = rulesObj;
      tableData.value = data;
      formOptionsFormat(tableData.value);
      emit('load');
    }
  });
};

const changeFun = (val: any) => {
  emit('change', val);
};

const removeFun = (val: any) => {
  emit('remove', val);
};

const validatorFun = (rule: any, value: any, callback: any, validator: any) => {
  if (!value) {
    callback();
  }
  if (validator(value)) {
    callback();
  } else {
    callback('');
  }
};

const validateFun = () => {
  return new Promise((resolve) => {
    formRef.value.validate((valid: any) => {
      resolve(valid);
    });
  });
};

const getFormDataFun = () => {
  const objData: any = formData.value || {};
  const hasExtras = !!formData.value.extras;
  objData.extras = hasExtras ? formData.value.extras : [];
  tableData.value.forEach((item: any) => {
    const { key, type } = item;
    if (type === 1) {
      objData[key] = formData.value[key];
    } else {
      if (hasExtras) {
        objData.extras.some((val: any) => {
          if (val.propertyName === key) {
            val.propertyValue = formData.value[key];
            return true;
          }
        });
      } else {
        objData.extras.push({
          propertyClass: 'default',
          propertyName: key,
          propertyValue: formData.value[key],
        });
      }
    }
  });
  return objData;
};

const setFormDataFun = (data: any) => {
  formData.value = formatDataFun(data);
};

const resetFun = () => {
  if (formRef.value) {
    formRef.value.resetFields();
  }
  formData.value = {};
};

const setOptionsFun = (key: string, options: any[]) => {
  tableData.value.some((item: any) => {
    if (item.key === key) {
      item.options = options;
      return true;
    }
  });
};

watch(() => props.tableName, () => {
  getHeadDataFun();
});

defineExpose({
  getFormDataFun,
  setFormDataFun,
  resetFun,
  validateFun,
  setOptionsFun,
});
</script>

<style lang="scss" scoped>
.comp-content {
  width: 100%;
  .form {
    width: 100%;
    overflow: hidden;
    .upload-btn {
      width: 100%;
    }
  }
}
</style>
