<template>
  <!--
    page-dialog-form — 增删改查弹框组件
    ===================================
    这是一个「带表单验证的弹框」组件，配合 page-table 使用。
    负责：弹框显隐控制 + 表单渲染 + 表单验证 + 确定/取消按钮。

    不支持复杂表单联动——如有需要，通过默认插槽自定义内容。

    依赖：element-ui 的 <el-dialog> <el-form> <el-input> <el-select>
    i18n：组件内部通过 $t() 翻译传入的 i18n key，切换语言时自动响应

    @author 前端团队
    @since  2026-05
  -->

  <!--
    el-dialog：
      visible.sync → 通过 .sync 修饰符双向绑定父组件的 visible prop
      destroy-on-close → 每次关闭销毁 DOM 重建，避免表单残留上次数据
      close-on-click-modal → 禁止点击遮罩关闭，防止误操作丢失填写数据
  -->
  <el-dialog
    :visible.sync="visibleProxy"
    :title="$t(title)"
    :width="width"
    :close-on-click-modal="false"
    :destroy-on-close="true"
    @close="onClose"
  >
    <!-- ==================== 表单区 ==================== -->
    <!--
      el-form：
        rules 由父组件传入，字段名与 formData 的 key 一一对应
        label-width 默认 100px，可自定义
    -->
    <el-form
      ref="form"
      :model="formData"
      :rules="translatedRules"
      :label-width="labelWidth || '100px'"
    >
      <!--
        遍历 formCols 中所有行 → 每行中的每个字段 → 渲染对应的表单项
        当前支持两种字段类型：
          - type='input'  → <el-input>（支持 textarea、密码等）
          - type='select' → <el-select> + <el-option>

        由调用方负责翻译，传已翻译的文本即可
      -->
      <el-form-item
        v-for="col in flatFormCols"
        :key="col.prop"
        :label="$t(col.label)"
        :prop="col.prop"
      >
        <!-- ===== 输入框类型 ===== -->
        <!--
          input：
            inputType='textarea' → 多行文本框
            不传 inputType → 普通文本输入框
            clearable → 默认 true，传 false 可关闭
        -->
        <el-input
          v-if="col.type === 'input'"
          v-model="formData[col.prop]"
          :placeholder="$t(col.placeholder)"
          :type="col.inputType || 'text'"
          :autosize="col.autosize"
          :clearable="col.clearable !== false"
          :disabled="!!col.disabled"
          :style="col.style"
          @focus="handleFieldEvent(col, 'focus', $event)"
          @blur="handleFieldEvent(col, 'blur', $event)"
        />
        <!-- ===== 下拉选择类型 ===== -->
        <!--
          select：
            options → [{ label: '苹果', value: 1 }]
            filterable → 默认 true，支持搜索过滤
        -->
        <el-select
          v-else-if="col.type === 'select'"
          v-model="formData[col.prop]"
          :placeholder="$t(col.placeholder)"
          :clearable="col.clearable !== false"
          :style="col.style"
          :filterable="col.filterable !== false"
          :disabled="!!col.disabled"
          @change="handleFieldEvent(col, 'change', $event)"
          @focus="handleFieldEvent(col, 'focus', $event)"
        >
          <el-option
            v-for="opt in col.options"
            :key="opt.value"
            :label="opt.label"
            :value="opt.value"
          />
        </el-select>
      </el-form-item>
      <!-- ===== 自定义表单内容插槽 ===== -->
      <!--
        如果需要超出 input/select 的复杂表单控件
        父组件可以用此插槽添加任意内容
      -->
      <slot />
    </el-form>

    <!-- ==================== 底部按钮 ==================== -->
    <!--
      确定：type='primary' + submitting loading 状态
      取消：调用 onClose → 重置表单 + 关闭弹框
      由调用方负责翻译，传已翻译的文本即可
    -->
    <template #footer>
      <el-button @click="onClose">{{ $t(cancelText) }}</el-button>
      <el-button type="primary" :loading="submitting" @click="onSubmit">{{ $t(confirmText) }}</el-button>
    </template>
  </el-dialog>
</template>

<script>
/**
 * PageDialogForm — 增删改查弹框组件
 *
 * 【核心职责】
 *   1. 管理弹框显隐
 *   2. 根据 formCols 声明式渲染表单
 *   3. 表单验证 + 验证失败提示
 *   4. 提交/取消事件通知父组件
 *
 * 【典型用法】
 *   <page-dialog-form
 *     ref="dialogForm"
 *     :visible.sync="dialogVisible"
 *     :title="dialogTitle"
 *     width="35%"
 *     :form-cols="formCols"
 *     :form-data="formData"
 *     :rules="rules"
 *     :submitting="submitting"
 *     @submit="onDialogSubmit"
 *     @close="onDialogClose"
 *   />
 *
 * 【父组件调用的方法（通过 ref）】
 *   this.$refs.dialogForm.reset()     — 重置表单
 *   this.$refs.dialogForm.validate()  — 手动验证，返回 Promise<boolean>
 *
 * 【formCols 数据结构说明】
 *   二维数组：外层是行，里层是该行的字段。大多数场景每行一个字段即可。
 *
 *   例：
 *   formCols: [
 *     [ { type: 'input', prop: 'code', label: T+'.code', placeholder: T+'.enter_code' } ],
 *     [ { type: 'input', prop: 'remark', label: T+'.remark', inputType: 'textarea', autosize: { minRows: 2 } } ],
 *     [ { type: 'select', prop: 'area_id', label: T+'.area', options: [{ label: 'A区', value: 1 }] } ]
 *   ]
 *
 * 【表单字段支持的属性】
 *   基础：type / prop / label / placeholder
 *   input：inputType（'textarea' | 'text'）/ autosize / clearable
 *   select：options / filterable
 *   通用：style（如 { width: '90%' }）
 *
 * 【表单验证（rules）】
 *   rules 与 el-form 的 rules 完全一致：
 *   rules: {
 *     code: [{ required: true, message: '请输入编码', trigger: 'blur' }]
 *   }
 *   验证失败时，组件会用 $message.warning 显示第一条错误信息
 *   message 字段支持 i18n key，父组件用 this.$t() 传值即可
 */
export default {
  name: 'PageDialogForm',
  props: {
    /**
     * 弹框显隐状态，父组件用 .sync 绑定
     * 例：:visible.sync="dialogVisible"
     */
    visible: { type: Boolean, default: false },

    /**
     * 弹框标题，由调用方负责翻译
     */
    title: { type: String, default: '' },

    /**
     * 弹框宽度，可以是百分比或像素
     * 例：'35%' / '600px'
     */
    width: { type: String, default: '35%' },

    /**
     * 表单字段结构，二维数组
     * 外层数组每一个元素代表表单的一行
     * 内层数组每个元素代表该行中的一个字段
     *
     * 每个字段对象支持的属性见组件顶部的注释
     */
    formCols: { type: Array, default: () => [] },

    /**
     * 表单数据对象，key 与 formCols 中的 prop 一一对应
     * 编辑时父组件将 row 数据赋值给 formData
     */
    formData: { type: Object, default: () => ({}) },

    /**
     * 表单验证规则，与 el-form 的 rules 完全一致
     * 例：{ code: [{ required: true, message: '请输入编码', trigger: 'blur' }] }
     */
    rules: { type: Object, default: () => ({}) },

    /**
     * 表单 label 宽度，默认 '100px'
     */
    labelWidth: { type: String, default: '100px' },

    /**
     * 提交 loading 状态，提交期间显示转圈防止重复点击
     */
    submitting: { type: Boolean, default: false },

    /**
     * 确定按钮文字，默认 '确定'，由调用方负责 i18n 翻译
     */
    confirmText: { type: String, default: '确定' },

    /**
     * 取消按钮文字，默认 '取消'，由调用方负责 i18n 翻译
     */
    cancelText: { type: String, default: '取消' }
  },
  computed: {
    /**
     * visible 代理：用于 .sync 双向绑定
     * get → 返回父组件传入的 visible
     * set → emit update:visible 通知父组件
     */
    visibleProxy: {
      get () { return this.visible },
      set (val) { this.$emit('update:visible', val) }
    },

    /**
     * 将二维 formCols 打平为一维数组，方便 v-for 遍历
     * 二维数组 → 一维数组：[ [{a}], [{b},{c}] ] → [a, b, c]
     */
    flatFormCols () {
      return this.formCols.flat()
    },

    translatedRules () {
      const rules = this.rules || {}
      const translated = {}
      for (const [field, validators] of Object.entries(rules)) {
        translated[field] = validators.map(v => ({ ...v, message: this.$t(v.message) }))
      }
      return translated
    }
  },
  methods: {
    handleFieldEvent (col, eventName, value) {
      if (!col) return
      const handlerKey = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1)
      const handler = col[handlerKey]
      if (typeof handler === 'function') {
        handler(value, col, this.formData)
      }
    },
    /**
     * 点击确定按钮
     * 1. 调用 el-form 的 validate 方法验证所有字段
     * 2. 验证通过 → emit('submit') 通知父组件执行提交逻辑
     * 3. 验证失败 → 用 $message.warning 显示第一条错误信息
     *
     * 注意：rules 中的 message 由父组件传入时已用 $t() 翻译
     * 这里只负责显示，翻译在父组件或 i18n 语言包中完成
     */
    onSubmit () {
      this.$refs.form.validate((valid, invalidFields) => {
        if (!valid) {
          const firstKey = Object.keys(invalidFields)[0]
          if (firstKey && invalidFields[firstKey].length) {
            const msg = invalidFields[firstKey][0].message
            this.$message.warning(msg)
          }
          return
        }
        this.$emit('submit')
      })
    },

    /**
     * 关闭弹框
     * 1. 重置表单清除验证状态和输入内容
     * 2. emit update:visible 关闭弹框（.sync 机制）
     * 3. emit close 通知父组件执行清理逻辑
     */
    onClose () {
      this.$refs.form && this.$refs.form.resetFields()
      this.$emit('update:visible', false)
      this.$emit('close')
    },

    /**
     * 手动验证表单，返回 Promise<boolean>
     * 主要用于父组件需要自行控制验证时机的场景
     *
     * 用法：const ok = await this.$refs.dialogForm.validate()
     */
    validate () {
      return new Promise(resolve => {
        this.$refs.form.validate(valid => resolve(valid))
      })
    },

    /**
     * 重置表单（清除验证状态和输入值）
     * 通常在打开新增弹框时调用
     */
    reset () {
      this.$refs.form && this.$refs.form.resetFields()
    }
  }
}
</script>

<style scoped>
/* 表单项间距固定 22px，不受 Element UI 状态覆盖 */
/deep/ .el-form-item {
  margin-bottom: 22px !important;
}
/* 错误文字下沉到 margin 间隙中，不挤占表单项自身高度 */
/deep/ .el-form-item__error {
  position: absolute;
  top: auto;
  bottom: -18px;
}
/* textarea 对齐顶部 */
/deep/ .el-textarea {
  vertical-align: top;
}
</style>
