# ProTableForm 对比表表单

用于在统一指标列下录入多个行信息，支持**内置组件**（input / 千分位数字 / select / switch 等）、**列级插槽**与**操作列完全由外部实现**。

## 数据模型（v-model）

`modelValue` 直接为数组：

```ts
[
  { [col.key]: ..., [col.key]: ... },
  { [col.key]: ..., [col.key]: ... },
]
```

## 列配置 `columns`

`columns` 为 `ProTableFormColumn[]` 类型，支持多级表头（通过 `children` 嵌套）。

### `ProTableFormColumn` 字段说明

| 字段 | 类型 | 说明 |
|------|------|------|
| `key` | `string` | 字段名，对应 model 中对象字段名 |
| `title` | `string` | 表头文字 |
| `required` | `boolean` | 是否必填（自动生成必填规则） |
| `component` | `ProTableFormBuiltInComponent` | 单元格渲染方式，见下方内置组件列表 |
| `componentProps` | `Record<string, unknown>` | 透传给单元格组件的配置参数 |
| `slotName` | `string` | `component === 'slot'` 时必填，对应父组件插槽 **`#cell-{slotName}`** |
| `render` | `ProTableFormColumnRender` | 自定义渲染函数，返回 string（文本展示，参与校验）或 VNode（自定义组件，直接渲染） |
| `rules` | `unknown[]` | 覆盖该列默认必填规则（Element 表单 rules） |
| `minWidth` / `width` | `number` | 列宽 |
| `align` | `'left' \| 'center' \| 'right'` | 单元格对齐 |
| `headerAlign` | `'left' \| 'center' \| 'right'` | 表头对齐 |
| `fixed` | `boolean \| 'left' \| 'right'` | 固定列 |
| `cellStyle` | `Record<string, string \| number>` | 单元格样式 |
| `headerCellStyle` | `Record<string, string \| number>` | 表头单元格样式 |
| `cellClassName` | `string` | 单元格 className |
| `headerCellClassName` | `string` | 表头单元格 className |
| `sortable` | `boolean` | 是否可排序 |
| `resizable` | `boolean` | 是否可拖拽调整宽度（默认 true） |
| `hideInTable` | `boolean` | 是否隐藏该列 |
| `children` | `ProTableFormColumn[]` | 多级表头子列 |

### `ProTableFormBuiltInComponent` 内置组件类型

| component 值 | 渲染组件 | 特殊说明 |
|---|---|---|
| `input` | `el-input` | 默认，可不写 |
| `input-number` | `el-input-number` | - |
| `formatted-number` | `FormattedNumberInput` | 千分位数字，默认整数位 5、小数位 6、舍入 round、inputLimit true；可通过 `componentProps` 覆盖 |
| `select` | `el-select` | `componentProps.options` 传入选项数组 `Array<{ label, value }>` |
| `checkbox` | `el-checkbox-group` | `componentProps.options` 传入选项数组，多选 |
| `radio` | `el-radio-group` | `componentProps.options` 传入选项数组，单选 |
| `date-picker` | `el-date-picker` | - |
| `date-range` | `el-date-picker[type=range]` | 默认 format `yyyy-MM-dd`，默认占位符「开始日期」/「结束日期」 |
| `switch` | `el-switch` | - |
| `cascader` | `el-cascader` | `componentProps.options` 传入树形选项数组 |
| `api-select` | `ApiSelect` | 远程 API 加载选项，`componentProps` 支持 `api`、`labelField`、`valueField` |
| `tree-select` | `TreeSelect` | 树形下拉，`componentProps` 支持 `data`、`props` |
| `slot` | 自定义插槽 | 配合 `slotName`，通过 `#cell-{slotName}` 插槽完全自定义单元格内容 |

> `select` / `checkbox` / `radio` 的选项统一通过 `componentProps.options` 传入：
> ```ts
> componentProps: {
>   options: [
>     { label: '启用', value: 1 },
>     { label: '禁用', value: 0 },
>   ],
> }
> ```

## 表头插槽（按列）

- **`#header-{column.key}`**：自定义某一列表头，作用域参数 `{ column }`。
- 不传则使用默认标题与必填星号。

## 自定义单元格插槽（`component: 'slot'`）

插槽名：**`cell-{slotName}`**

作用域参数：

| 属性 | 说明 |
|------|------|
| `column` | 当前列配置 |
| `row` | 当前行（`_index: number`） |
| `index` | 行索引 |
| `value` | 当前单元格值 |
| `updateValue` | `(v: unknown) => void` 写回表单 |

## 操作列

操作列完全由外部通过 **`#action`** 作用域插槽实现，ProTableForm 不内置任何操作列 UI。

作用域参数：

| 属性 | 说明 |
|------|------|
| `addRow` | 新增一行 |
| `removeRow` | 删除指定行，签名 `(index: number) => void` |

```vue
<ProTableForm>
  <template #action="{ addRow, removeRow }">
    <el-table-column width="120" fixed="right" align="center">
      <template #header>
        <el-button type="text" @click="addRow">+新增</el-button>
      </template>
      <template #default="slotProps">
        <el-button type="text" size="small" @click="removeRow(slotProps.$index)">删除</el-button>
      </template>
    </el-table-column>
  </template>
</ProTableForm>
```

## 校验规则

ProTableForm 复用 `el-form` 的校验体系，支持三种配置方式，优先级：**列级 `rules` > 列级 `required` > 全局 `rules` > 表单级 rules**。

### 三种配置方式

**方式一：`required: true`（最常用）**

自动生成必填规则，无需手动编写：

```ts
{ key: 'name', title: '名称', required: true }
```

**方式二：列级 `rules`（覆盖 `required` 默认规则）**

传入 Element UI 标准 rules 数组，支持正则、数值范围、自定义 validator 等：

```ts
{
  key: 'email',
  title: '邮箱',
  required: true,
  rules: [
    { required: true, message: '请输入邮箱' },
    { pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, message: '邮箱格式不正确' },
  ],
}
```

```ts
// 数值范围示例
{
  key: 'price',
  title: '价格',
  component: 'input-number',
  rules: [
    { required: true, message: '请输入价格' },
    { type: 'number', min: 0, message: '价格不能为负数' },
  ],
}
```

**方式三：全局 `rules`（统一规则，适用于所有同名 key）**

```ts
<ProTableForm
  :columns="columns"
  :rules="{
    inputNumber: [{ type: 'number', min: 0, message: '数量不能为负数' }],
  }"
/>
```

## 组件实例 `defineExpose`

| 方法/属性 | 说明 |
|------|------|
| `validate()` | 表单校验，返回 Promise\<boolean\> |
| `clearValidate(props?)` | 清除校验 |
| `addRow()` | 新增一行 |
| `removeRow(index)` | 删除指定行 |
| `getRows()` | 获取行数组 |
| `getRowCount()` | 获取行数 |
| `getTable()` | 获取 el-table 实例 |
| `getModelValue()` | 获取整个 modelValue |
| `setModelValue(val)` | 设置整个 modelValue |
| `getFormRef()` | 获取 el-form 实例 |

## 其它 Props

- **rules**：合并进 `el-form` 的全局规则。
- **metricPlaceholder**：指标列（默认 `input`）的 placeholder，默认 `"请输入"`
- **minRows**：最少行数，初始化时自动补足
- **bordered**：是否显示边框，默认 `true`
- **stripe**：是否斑马纹，默认 `false`
- **size**：单元格尺寸，默认 `medium`

## 示例

| 示例 | 说明 |
|------|------|
| `examples/pages/ProTableFormPage/Basic.vue` | 基础用法 + 自定义操作列 |
| `examples/pages/ProTableFormPage/BuiltInComponents.vue` | 全部 12 种内置组件一览 |
| `examples/pages/ProTableFormPage/Rules.vue` | 三种校验规则配置方式 |
| `examples/pages/ProTableFormPage/MultiHeader.vue` | 多级表头（children 嵌套） |
| `examples/pages/ProTableFormPage/MixedData.vue` | 混用：展示列（render） + 可编辑列 |
| `examples/pages/ProTableFormPage/Combo.vue` | 与 `el-form` 组合提交分段校验 |

示例站：`/protable?tab=basic`、`/protable?tab=builtIn`、`/protable?tab=rules`、`/protable?tab=multiHeader`、`/protable?tab=mixed`、`/protable?tab=combo`
