<template>
  <el-dialog
    v-model="dialogVisible"
    v-bind="modalConfig.dialog"
    @close="closeDialog"
  >
    <!-- 表单 -->
    <el-form
      ref="formRef"
      label-width="80px"
      v-bind="modalConfig.form"
      :model="formData"
      :rules="formRules"
    >
      <template v-for="item in modalConfig.formItems" :key="item.prop">
        <el-form-item :label="item.label" :prop="item.prop">
          <!-- Input 输入框 -->
          <template v-if="item.type === 'input'">
            <template v-if="item.attrs?.type === 'number'">
              <el-input
                v-model.number="formData[item.prop]"
                v-bind="item.attrs"
              />
            </template>
            <template v-else>
              <el-input v-model="formData[item.prop]" v-bind="item.attrs" />
            </template>
          </template>
          <!-- Select 选择器 -->
          <template v-else-if="item.type === 'select'">
            <el-select v-model="formData[item.prop]" v-bind="item.attrs">
              <template v-for="option in item.options" :key="option.value">
                <el-option v-bind="option" />
              </template>
            </el-select>
          </template>
          <!-- Radio 单选框 -->
          <template v-else-if="item.type === 'radio'">
            <el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
              <template v-for="option in item.options" :key="option.value">
                <el-radio v-bind="option" />
              </template>
            </el-radio-group>
          </template>
          <!-- Checkbox 多选框 -->
          <template v-else-if="item.type === 'checkbox'">
            <el-checkbox-group
              v-model="formData[item.prop]"
              v-bind="item.attrs"
            >
              <template v-for="option in item.options" :key="option.value">
                <el-checkbox v-bind="option" />
              </template>
            </el-checkbox-group>
          </template>
          <!-- Input Number 数字输入框 -->
          <template v-else-if="item.type === 'input-number'">
            <el-input-number
              v-model="formData[item.prop]"
              v-bind="item.attrs"
            />
          </template>
          <!-- TreeSelect 树形选择 -->
          <template v-else-if="item.type === 'tree-select'">
            <el-tree-select v-model="formData[item.prop]" v-bind="item.attrs" />
          </template>
          <!-- DatePicker 日期选择器 -->
          <template v-else-if="item.type === 'date-picker'">
            <el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
          </template>
          <!-- 自定义 -->
          <template v-else-if="item.type === 'custom'">
            <slot
              :name="item.slotName ?? item.prop"
              :prop="item.prop"
              :formData="formData"
              v-bind="item.attrs"
            ></slot>
          </template>
          <!-- Input 输入框 -->
          <template v-else>
            <template v-if="item.attrs?.type === 'number'">
              <el-input
                v-model.number="formData[item.prop]"
                v-bind="item.attrs"
              />
            </template>
            <template v-else>
              <el-input v-model="formData[item.prop]" v-bind="item.attrs" />
            </template>
          </template>
        </el-form-item>
      </template>
    </el-form>
    <!-- 弹窗底部操作按钮 -->
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
        <el-button @click="closeDialog">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useThrottleFn } from "@vueuse/core";
import type {
  DialogProps,
  FormProps,
  FormRules,
  FormItemRule,
} from "element-plus";

// 对象类型
type IObject = Record<string, any>;
// 定义接收的属性
export interface IModalConfig<T = any> {
  // 页面名称
  pageName?: string;
  // 主键名(主要用于编辑数据,默认为id)
  pk?: string;
  // dialog组件属性
  dialog?: Partial<Omit<DialogProps, "modelValue">>;
  // form组件属性
  form?: Partial<Omit<FormProps, "model" | "rules">>;
  // 提交的网络请求函数(需返回promise)
  formAction: (data: T) => Promise<any>;
  // 提交之前处理
  beforeSubmit?: (data: T) => void;
  // 表单项
  formItems: Array<{
    // 组件类型(如input,select,radio,custom等，默认input)
    type?:
      | "input"
      | "select"
      | "radio"
      | "checkbox"
      | "tree-select"
      | "date-picker"
      | "input-number"
      | "custom";
    // 组件属性
    attrs?: IObject;
    // 组件可选项(适用于select,radio,checkbox组件)
    options?: Array<{
      label: string;
      value: any;
      disabled?: boolean;
      [key: string]: any;
    }>;
    // 插槽名(适用于组件类型为custom)
    slotName?: string;
    // 标签文本
    label: string;
    // 键名
    prop: string;
    // 验证规则
    rules?: FormItemRule[];
    // 初始值
    initialValue?: any;
    // 监听函数
    watch?: (newValue: any, oldValue: any, data: T) => void;
  }>;
}
const props = defineProps<{
  modalConfig: IModalConfig;
}>();
// 自定义事件
const emit = defineEmits<{
  submitClick: [];
}>();
// 暴露的属性和方法
defineExpose({ setModalVisible });

const pk = props.modalConfig.pk ?? "id";
const dialogVisible = ref(false);
const formRef = ref<InstanceType<typeof ElForm>>();
const formData = reactive<IObject>({});
const formRules: FormRules = {};
// 初始化
for (const item of props.modalConfig.formItems) {
  formData[item.prop] = item.initialValue ?? "";
  formRules[item.prop] = item.rules ?? [];
  if (item.watch !== undefined) {
    watch(
      () => formData[item.prop],
      (newValue, oldValue) => {
        item.watch?.(newValue, oldValue, formData);
      }
    );
  }
}
// 显示dialog
function setModalVisible(initData: IObject = {}) {
  dialogVisible.value = true;
  for (const key in formData) {
    if (Object.hasOwn(formData, key) && key in initData) {
      formData[key] = initData[key];
    }
  }
  if (Object.hasOwn(initData, pk)) {
    formData[pk] = initData[pk];
  }
}
// 表单提交
const handleSubmit = useThrottleFn(() => {
  formRef.value?.validate((valid: boolean) => {
    if (valid) {
      if (typeof props.modalConfig.beforeSubmit === "function") {
        props.modalConfig.beforeSubmit(formData);
      }
      props.modalConfig.formAction(formData).then(() => {
        ElMessage.success(
          props.modalConfig.dialog?.title
            ? `${props.modalConfig.dialog.title}成功`
            : "操作成功"
        );
        emit("submitClick");
        closeDialog();
      });
    }
  });
}, 3000);
// 关闭弹窗
function closeDialog() {
  dialogVisible.value = false;
  formRef.value?.resetFields();
  formRef.value?.clearValidate();
}
</script>

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