Appearance
表单弹层配置(formConfig)
ProTable 通过 form-config prop 接收表单配置,自动管理新增 / 编辑 / 复制弹层。本页说明 formConfig 的完整配置项以及 add / edit / save / copy 各场景的行为差异。
如需独立使用表单(不与 ProTable 联动),见 ProForm 表单。
基本用法
vue
<template>
<pro-table
:search-config="searchConfig"
:table-config="tableConfig"
:form-config="formConfig"
/>
</template>
<script setup>
const formConfig = reactive({
mode: 'dialog',
width: '500px',
model: { name: '', status: 1 },
fields: [
{ label: '姓名', key: 'name', component: 'input' },
{ label: '状态', key: 'status', component: 'select',
options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }] }
],
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
},
add: {
title: '新增用户',
api: createUser
},
edit: {
title: '编辑用户',
api: updateUser
}
})
</script>formConfig 完整属性
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| mode | 弹层模式:dialog(弹窗)/ drawer(抽屉)/ page(整页) | String | 'dialog' |
| width | 弹窗宽度 / 抽屉宽度 | String | '500px' |
| model | 表单初始数据,key 与 fields 的 key 对应 | Object | - |
| fields | 表单项配置数组,见 字段配置 | Array | [] |
| tabs | 分 tab 展示,见 ProForm tabs | Array | - |
| rules | 表单通用校验规则(add / edit 场景若自身有 rules 则优先用自身的,否则回退到这里;save 场景不回退) | Object | - |
| labelWidth | label 宽度 | String | '100px' |
| labelPosition | label 对齐:left / right / top | String | 'left' |
| formAttrs | 透传给 el-form 的属性(size、disabled、labelSuffix 等) | Object | - |
| span | 表单项栅格占位(每行 24 格),可在 field 上单独覆盖 | Number | 24 |
| formWidth | 表单内容区宽度,如 '600px'、'80%' | String | - |
| uploadConfig | 上传配置,见 上传配置 | Object | - |
| readonly | 表单级只读:true 时所有字段只读;支持 ({ formType, formData }) => boolean | Boolean | Function | false |
| params | 传给 fetchInitialData 的参数(编辑=当前行;复制打开时 params 已去主键) | Object | {} |
| fetchInitialData | 编辑 / 复制打开时请求回填:(params) => Promise<Object>;新增不会调用 | Function | - |
| openBefore | 打开表单前钩子:({ type, row, params }) => boolean | Promise<boolean>,返回 false 取消 | Function | - |
| openAfter | 打开表单后钩子:({ type, row, params }) => void | Function | - |
| beforeSubmit | 提交前钩子:({ formData, action }) => boolean | Promise<boolean>,返回 false 取消 | Function | - |
| afterSubmit | 提交成功后回调:{ res, formData, action };res 为 responseFormat 转换后的响应 | Function | - |
| filterEmptyParams | 提交前是否过滤空值;设为 true 时过滤 ''、null、undefined、空数组 | Boolean | false |
| closeOnSubmit | 提交成功后是否关闭弹窗;接口返回失败码或请求抛错时不会关闭 | Boolean | true |
| closeOnClickModal | 点击遮罩是否关闭弹窗 | Boolean | true |
| popupAttrs | 透传给 el-dialog / el-drawer 的额外属性 | Object | - |
| footer | 底部按钮区配置,见 Footer 配置 | Object | - |
| add | 新增配置,见 add 配置 | Object | - |
| edit | 编辑配置,见 edit 配置 | Object | - |
| save | 保存配置,见 save 配置 | Object | - |
提交流程与钩子职责
提交时会先基于当前表单数据生成参数:日期范围字段会按 startKey / endKey 拆分,title / text 展示字段不会提交,nestedKeys 字段会在提交时展平。
如果配置了 filterEmptyParams: true,会在 paramsFormat 之前过滤 ''、null、undefined 和空数组。默认不启用,避免清空字段时无法把空值提交给后端。
执行顺序:
text
表单校验 → beforeSubmit → 生成提交参数 → filterEmptyParams → add/edit/save.paramsFormat → add/edit/save.api → add/edit/save.responseFormat → afterSubmit → submit/save 事件beforeSubmit用于提交前拦截、二次确认或业务校验,返回false会取消本次提交。paramsFormat用于转换最终提交参数,例如字段改名、类型转换、补充固定字段。responseFormat用于转换接口响应,它的返回值会作为afterSubmit和submit/save事件接收到的响应。afterSubmit用于提交成功后的副作用,例如刷新外部数据、跳转、记录日志等。
提交失败处理
add.api / edit.api / save.api 返回对象中包含 codeKey 字段时,组件会根据全局 successCode 判断是否成功:
String(res[codeKey]) === String(successCode):视为成功,提示res[messageKey],并按closeOnSubmit决定是否关闭弹窗。- 不相等:视为失败,提示
res[messageKey]或res.message,并且不会关闭弹窗。 - 接口请求抛错:提示错误信息,并且不会关闭弹窗。
js
app.use(AdminProTable, {
successCode: 0,
codeKey: 'code',
messageKey: 'message'
})
const formConfig = {
add: {
api: async (params) => {
// 返回 { code: 500, message: '题目已存在' } 时会提示错误,弹窗保持打开
return addQuestion(params)
}
}
}提交参数处理
提交 add.api / edit.api / save.api 前,组件会先对当前表单数据做统一处理:
date-range/date-time-range会按startKey/endKey拆成两个字段,并移除原 range 字段。title/text仅展示字段不会提交。- 配置了
nestedKeys的字段会在提交时展平。 - 编辑场景会根据
idKey/idValueKey补上主键字段。 paramsFormat接收到的是上述处理后的参数。
add 配置
用于新增表单提交,点击工具栏"添加"后触发。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| title | 弹窗 / 抽屉标题 | String | - |
| api | 提交接口:(params) => Promise | Function | - |
| rules | 新增场景专属校验规则,优先于 formConfig.rules | Object | - |
| paramsFormat | 提交前格式化参数:(data) => any | Function | - |
| responseFormat | 接口返回格式化:(res) => any | Function | - |
js
add: {
title: '新增用户',
api: createUser,
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
},
paramsFormat: (data) => ({
...data,
source: 'admin'
})
}add 校验优先级
- 有
add.rules→ 用add.rules - 没有
add.rules→ 回退到formConfig.rules
edit 配置
用于编辑表单提交,点击操作列"编辑"后触发。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| title | 弹窗 / 抽屉标题 | String | - |
| api | 提交接口:(params) => Promise | Function | - |
| rules | 编辑场景专属校验规则,优先于 formConfig.rules | Object | - |
| idKey | 提交给后端的主键字段名 | String | 'id' |
| idValueKey | 当前表单数据中取主键值的字段名 | String | 'id' |
| paramsFormat | 提交前格式化参数:(data) => any | Function | - |
| responseFormat | 接口返回格式化:(res) => any | Function | - |
js
edit: {
title: '编辑用户',
api: updateUser,
idKey: 'userId',
idValueKey: 'id',
rules: {
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
}
}edit 校验优先级
- 有
edit.rules→ 用edit.rules - 没有
edit.rules→ 回退到formConfig.rules
edit / copy 回显优先级
点击操作列「编辑」或「复制」时:
- 配置了顶层
formConfig.fetchInitialData:调用fetchInitialData请求详情并回显。- 编辑:传入当前行(含主键)
- 复制:传入原始行(含主键)请求详情,回填前自动去掉主键字段
- 未配置顶层
formConfig.fetchInitialData:直接使用当前行数据回显(复制场景会去掉主键) - 新增不会调用
fetchInitialData(无 id,避免详情接口报错)
edit 中不需要配置 fetchInitialData;详情请求统一放在顶层 formConfig.fetchInitialData。
save 配置
用于"保存草稿""临时保存"等场景,点击底部"保存"按钮触发。
需要在
footer里配置showSave: true才会显示保存按钮。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| api | 保存接口:(params) => Promise | Function | - |
| rules | 保存场景专属校验规则 | Object | - |
| paramsFormat | 提交前格式化参数:(data) => any | Function | - |
| responseFormat | 接口返回格式化:(res) => any | Function | - |
js
save: {
api: saveDraft,
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
}
}save 校验优先级
- 有
save.rules→ 只用save.rules - 没有
save.rules→ 不校验 - 不会回退到
formConfig.rules
save 标题说明
save.title 不生效。弹窗 / 抽屉标题只看 formConfig.title、add.title、edit.title。
显示保存按钮
js
formConfig: {
footer: { showSave: true, saveText: '保存草稿' },
save: { api: saveDraft }
}copy 场景
点击操作列"复制"时,组件内部以 copy 类型打开表单,但提交逻辑走新增:
- 提交接口走
add.api - 校验规则走
add.rules - 标题走
add.title - 表单数据预填当前行数据(去除主键)
需在 operationConfig 里开启复制按钮:
js
tableConfig: {
operationConfig: { showCopy: true }
}标题优先级
弹窗 / 抽屉 / page 模式下的标题取值:
formConfig.title(如果配了,所有场景统一使用)- 当前为新增 / 复制时:
add.title - 当前为编辑时:
edit.title - 未配置以上标题时,新增 / 复制默认显示
新增,编辑默认显示编辑
save.title 不参与标题计算。
Footer 配置
底部按钮区域配置。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| show | 是否显示底部区域 | Boolean | true |
| showCancel | 是否显示取消按钮 | Boolean | true |
| showSubmit | 是否显示提交按钮 | Boolean | true |
| showSave | 是否显示保存按钮(需配合 save.api) | Boolean | false |
| cancelText | 取消按钮文案 | String | '取消' |
| submitText | 提交按钮文案 | String | '提交' |
| saveText | 保存按钮文案 | String | '保存' |
| align | 按钮对齐方式:left / center / right;page 模式默认 'left',dialog/drawer 默认 'right' | String | 见说明 |
js
footer: {
show: true,
showCancel: true,
showSubmit: true,
showSave: true,
cancelText: '取消',
submitText: '确认提交',
saveText: '保存草稿',
align: 'right'
}与表格联动流程
传入 form-config 后,ProTable 自动管理表单弹层:
- 点击工具栏"添加" → 以
add配置打开新增表单(不会调用fetchInitialData) - 点击操作列"编辑" → 以
edit配置打开编辑表单,自动传入当前行数据 - 点击操作列"复制" → 以
copy方式打开,预填当前行(去主键),提交走add.api - 配置了
formConfig.fetchInitialData→ 打开编辑 / 复制弹层会自动请求详情接口并回填 - 提交成功后自动刷新列表
表单插槽
表单相关插槽需加 form- 前缀:
vue
<pro-table :table-config="tableConfig" :form-config="formConfig">
<!-- 自定义表单字段 -->
<template #form-custom-avatar="{ modelValue, formData }">
<el-upload v-model="formData.avatar" />
</template>
<!-- 表单头部 / 底部 -->
<template #form-header="{ formData }">
<el-alert title="提示信息" type="info" />
</template>
</pro-table>外部控制表单
通过 ref 可以在外部控制表单的打开、关闭和数据:
js
const tableRef = ref(null)
// 打开新增,可传初始数据
tableRef.value?.openAdd({ type: 'admin' })
// 打开编辑 / 复制
tableRef.value?.openEdit(row)
tableRef.value?.openCopy(row)
// 关闭表单
tableRef.value?.closeForm()
// 读取 / 操作表单数据
const data = tableRef.value?.getFormData()
tableRef.value?.patchFormData({ status: 1 })
await tableRef.value?.validateForm()更多实例方法见 实例方法与事件。
完整示例
js
const formConfig = reactive({
mode: 'dialog',
width: '500px',
model: { name: '', status: 1, type: 'A', remark: '' },
fields: [
{ label: '姓名', key: 'name', component: 'input' },
{ label: '状态', key: 'status', component: 'select',
options: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }] },
{ label: '类型', key: 'type', component: 'select',
options: [{ label: '类型A', value: 'A' }, { label: '类型B', value: 'B' }] },
{ label: '备注', key: 'remark', component: 'textarea', rows: 3, span: 24 }
],
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
},
add: {
title: '新增用户',
api: createUser,
paramsFormat: (data) => ({ ...data, source: 'web' })
},
edit: {
title: '编辑用户',
api: updateUser,
idKey: 'id',
fetchInitialData: async (row) => {
const res = await getUserDetail(row.id)
return res.data
}
},
save: {
api: saveDraft,
rules: {}
},
footer: {
showSave: true,
saveText: '保存草稿',
submitText: '确认提交',
align: 'right'
}
})