<template>
  <el-form
    ref="formRef"
    :model="modelValue"
    :rules="mergedRules"
    :inline="inline"
    :label-width="labelWidth"
    :label-position="labelPosition"
    :disabled="disabled"
    :size="size"
    :class="['basic-form', { 'basic-form--grid': !inline }]"
  >
    <!-- 行内布局：直接平铺 -->
    <template v-if="inline">
      <template v-for="item in visibleSchemas" :key="item.prop">
        <el-form-item
          :label="item.label"
          :prop="item.prop"
          :label-width="item.labelWidth"
          v-bind="item.formItemProps"
        >
          <slot :name="`form-${item.prop}`" :model="modelValue" :item="item">
            <form-control :model="modelValue" :item="item" :dict-map="dictMap" />
          </slot>
        </el-form-item>
      </template>
      <el-form-item v-if="$slots.default">
        <slot />
      </el-form-item>
    </template>

    <!-- 栅格布局：el-row + el-col -->
    <template v-else>
      <el-row :gutter="gutter">
        <el-col
          v-for="item in visibleSchemas"
          :key="item.prop"
          :span="item.span || 24"
          v-bind="item.colProps"
        >
          <el-form-item
            :label="item.label"
            :prop="item.prop"
            :label-width="item.labelWidth"
            v-bind="item.formItemProps"
          >
            <slot :name="`form-${item.prop}`" :model="modelValue" :item="item">
              <form-control :model="modelValue" :item="item" :dict-map="dictMap" />
            </slot>
          </el-form-item>
        </el-col>
        <el-col :span="actionSpan" v-if="$slots.default">
          <el-form-item :label-width="actionLabelWidth">
            <slot />
          </el-form-item>
        </el-col>
      </el-row>
    </template>
  </el-form>
</template>

<script setup>
/**
 * 公共表单组件 BasicForm
 * 基于 schema 配置驱动，支持常见控件、字典自动加载、校验、插槽扩展。
 *
 * 数据流：与 Element Plus el-form 一致，传入的对象为引用传递，控件直接修改其字段属性。
 * 用法：v-model 或 :model-value 均可（两者等价，组件内部不替换对象，而是原地修改字段）。
 *
 * 用法示例：
 * <basic-form ref="formRef" v-model="form" :schemas="schemas" :rules="rules" label-width="100px" />
 *
 * schema 项结构：
 * {
 *   label: '岗位名称',          // 标签
 *   prop: 'postName',           // 字段名
 *   component: 'input',         // 控件类型：input/textarea/inputNumber/select/radio/checkbox/switch/date/datetime/daterange/datetimerange/time/cascader
 *   placeholder: '请输入',       // 占位文本
 *   options: [],                 // 静态选项（select/radio/checkbox）
 *   dict: 'sys_normal_disable',  // 字典类型，自动加载选项
 *   valueKey: 'value',           // 选项值字段（默认 value）
 *   labelKey: 'label',           // 选项标签字段（默认 label）
 *   span: 12,                    // 栅格占位（grid 布局，默认 24）
 *   width: '200px',               // 控件宽度
 *   show: true,                   // 是否显示
 *   disabled: false,
 *   rules: [],                    // 该项校验规则
 *   defaultValue: undefined,      // resetToDefault 时该项的默认值
 *   props: {},                    // 透传给底层控件的属性
 *   formItemProps: {},            // 透传给 el-form-item 的属性
 *   colProps: {}                  // 透传给 el-col 的属性
 * }
 */

defineOptions({ name: 'BasicForm' })

const props = defineProps({
  // 表单数据对象（引用传递，控件原地修改其字段）
  modelValue: {
    type: Object,
    required: true
  },
  // 表单项配置数组
  schemas: {
    type: Array,
    default: () => []
  },
  // 校验规则（可与 schema 内 rules 合并，schema 优先）
  rules: {
    type: Object,
    default: () => ({})
  },
  // 是否行内布局（搜索场景）
  inline: {
    type: Boolean,
    default: false
  },
  // 标签宽度
  labelWidth: {
    type: [String, Number],
    default: '80px'
  },
  // 标签位置
  labelPosition: {
    type: String,
    default: 'right'
  },
  // 整体禁用
  disabled: {
    type: Boolean,
    default: false
  },
  // 表单尺寸（large/default/small）
  size: {
    type: String,
    default: undefined
  },
  // 栅格间距（grid 布局）
  gutter: {
    type: Number,
    default: 16
  },
  // 操作区栅格占位（grid 布局）
  actionSpan: {
    type: Number,
    default: 24
  },
  // 操作区 label-width（grid 布局）
  actionLabelWidth: {
    type: [String, Number],
    default: '0'
  }
})

const { proxy } = getCurrentInstance()
const formRef = ref()

// 收集所有 schema 中用到的字典类型，统一加载
const dictMap = reactive({})
const dictTypes = computed(() => {
  const set = new Set()
  props.schemas.forEach((s) => s.dict && set.add(s.dict))
  return [...set]
})

watch(
  dictTypes,
  (types) => {
    if (!types.length) return
    const dictRefs = proxy.useDict(...types)
    types.forEach((t) => {
      // useDict 返回的是 toRefs，每项是 ref
      const d = dictRefs[t]
      dictMap[t] = isRef(d) ? d : ref(d)
    })
  },
  { immediate: true }
)

// 合并 schema 内 rules 与外部 rules（schema 优先）
const mergedRules = computed(() => {
  const result = { ...props.rules }
  props.schemas.forEach((s) => {
    if (s.rules) result[s.prop] = s.rules
  })
  return result
})

// 过滤出可见的 schema
const visibleSchemas = computed(() => props.schemas.filter((s) => s.show !== false))

// ====== 暴露 el-form 方法 ======
function validate(callback) {
  return formRef.value?.validate(callback)
}
function validateField(p, callback) {
  return formRef.value?.validateField(p, callback)
}
function resetFields(p) {
  return formRef.value?.resetFields(p)
}
function clearValidate(p) {
  return formRef.value?.clearValidate(p)
}
function scrollToField(prop) {
  return formRef.value?.scrollToField(prop)
}
function getFormRef() {
  return formRef.value
}
// 重置为 schema defaultValue 定义的状态
function resetToDefault() {
  props.schemas.forEach((s) => {
    props.modelValue[s.prop] = s.defaultValue !== undefined ? s.defaultValue : undefined
  })
  nextTick(() => clearValidate())
}

defineExpose({
  validate,
  validateField,
  resetFields,
  clearValidate,
  scrollToField,
  getFormRef,
  resetToDefault
})
</script>

<style scoped>
.basic-form--grid :deep(.el-form-item) {
  margin-bottom: 18px;
}
</style>
