AiForm 表单组件
通过 JSON Schema 配置动态渲染表单,支持多种字段类型、嵌套验证规则、条件显示、分组布局、折叠等功能。AiForm 是 AiCrudPage、AiSearch 等组件的底层表单引擎。
基础用法
vue
<template>
<AiForm
ref="formRef"
v-model:value="formData"
:schema="formSchema"
:grid-cols="2"
label-width="100px"
@submit="handleSubmit"
@cancel="handleCancel"
/>
</template>
<script setup>
import { ref } from 'vue'
import { AiForm } from '@/components/ai-form'
const formRef = ref(null)
const formData = ref({})
const formSchema = [
{ field: 'name', label: '姓名', type: 'input', required: true,
rules: [{ required: true, message: '请输入姓名', trigger: 'blur' }] },
{ field: 'age', label: '年龄', type: 'number', span: 1 },
{ field: 'gender', label: '性别', type: 'select', span: 1,
options: [
{ label: '男', value: 1 },
{ label: '女', value: 0 }
] },
{ field: 'email', label: '邮箱', type: 'input', span: 2,
rules: [{ type: 'email', message: '邮箱格式不正确' }] },
{ field: 'status', label: '状态', type: 'switch',
activeText: '启用', inactiveText: '禁用', defaultValue: 1 },
{ field: 'remark', label: '备注', type: 'textarea', span: 2,
placeholder: '请输入备注信息' }
]
function handleSubmit(data) {
console.log('提交数据:', data)
}
function handleCancel() {
console.log('取消')
}
</script>Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| schema | Array<object> | [] | 必填,表单字段配置数组 |
| value | Object | {} | 表单数据(支持 v-model:value 双向绑定) |
| gridCols | Number | 1 | 表单栅格列数 |
| labelPlacement | String | 'left' | 标签位置:'left' | 'top' |
| labelAlign | String | 'right' | 标签对齐:'left' | 'right' |
| labelWidth | String | Number | 'auto' | 标签宽度,如 '100px' 或 120 |
| size | String | 'medium' | 表单项尺寸:'small' | 'medium' | 'large' |
| xGap | Number | 16 | 表单项列间距(px) |
| yGap | Number | 8 | 表单项行间距(px) |
| showFeedback | Boolean | true | 是否显示校验反馈信息 |
| showActions | Boolean | true | 是否显示底部操作按钮区 |
| showSubmit | Boolean | true | 是否显示提交按钮 |
| showReset | Boolean | true | 是否显示重置按钮 |
| submitText | String | '提交' | 提交按钮文本 |
| resetText | String | '重置' | 重置按钮文本 |
| enableCollapse | Boolean | false | 是否启用折叠(超过最大可见字段数时折叠) |
| maxVisibleFields | Number | 6 | 折叠前最大可见字段数 |
| context | Object | {} | 表单上下文数据,可在字段配置中通过 vIf 或 onChange 使用 |
| formAssets | Array<object> | [] | 表单资产列表,用于动态加载独立表单弹窗 |
| formClass | String | Object | Array | '' | 表单容器自定义 class |
| formStyle | String | Object | Array | - | 表单容器自定义 style |
Schema 字段配置
每个 schema 项支持以下属性:
通用属性
| 属性 | 类型 | 说明 |
|---|---|---|
| field | String | 必填,字段名,对应 formData 中的 key |
| label | String | 标签文本 |
| type | String | 字段类型(详见下方类型列表) |
| span | Number | 栅格列占位(基于 gridCols),默认为 1 |
| required | Boolean | 是否必填(显示红色星号) |
| rules | Array | 验证规则数组,格式同 Naive UI FormItem rule |
| defaultValue | Any | 默认值 |
| placeholder | String | 占位提示文本 |
| tip | String | 提示信息(显示在字段下方) |
| disabled | Boolean | Function | 是否禁用,支持函数 (formData, context) => boolean |
| hidden | Boolean | Function | 是否隐藏,支持函数 (formData, context) => boolean |
| vIf | Boolean | Function | 条件显示,支持函数 (formData, context) => boolean |
| vShow | Boolean | Function | 条件可见(DOM 保留),支持函数 |
| onChange | Function | 值变化回调:(value, formData, context) => void |
| slot | String | 自定义插槽名(通过父组件 <template #slotName> 渲染) |
| renderLabel | Function | 自定义标签渲染:(fieldConfig) => VNode | string |
| group | String | 所属分组标识(配合 AiFormGroupTitle 使用) |
分组标题
schema 中可插入分组标题项来组织表单布局:
js
const formSchema = [
{ type: 'group-title', label: '基本信息', field: 'basicGroup' },
{ field: 'name', label: '姓名', type: 'input' },
{ field: 'age', label: '年龄', type: 'number' },
{ type: 'group-title', label: '联系方式', field: 'contactGroup' },
{ field: 'email', label: '邮箱', type: 'input' },
{ field: 'phone', label: '电话', type: 'input' }
]区块标题
区块标题用于更细粒度的表单分区:
js
{ type: 'section-title', label: '高级配置', field: 'advancedSection' }支持的字段类型
基础输入
| type 值 | 说明 | 额外属性 |
|---|---|---|
input | 文本输入框 | maxlength, showPassword, prefix, suffix |
textarea | 多行文本域 | rows, maxlength, showCount |
number | 数字输入框 | min, max, step, precision |
选择类
| type 值 | 说明 | 额外属性 |
|---|---|---|
select | 下拉选择 | options, filterable, clearable, multiple, remote, remoteMethod |
radio | 单选框组 | options, buttonMode |
checkbox | 多选框组 | options |
switch | 开关 | activeText, inactiveText, activeValue, inactiveValue |
tree-select | 树形选择 | treeData, checkable, cascade, filterable |
日期时间
| type 值 | 说明 | 额外属性 |
|---|---|---|
date | 日期选择 | format, valueFormat |
datetime | 日期时间选择 | format, valueFormat |
time | 时间选择 | format |
year | 年份选择 | - |
month | 月份选择 | format |
文件上传
| type 值 | 说明 | 额外属性 |
|---|---|---|
file-upload | 文件上传 | accept, max, maxSize, listType |
image-upload | 图片上传 | max, maxSize, listType, crop |
avatar-upload | 头像上传 | max, maxSize |
高级组件
| type 值 | 说明 | 额外属性 |
|---|---|---|
dict-select | 字典下拉选择 | dictType(字典类型编码) |
dict-radio | 字典单选 | dictType |
dict-checkbox | 字典多选 | dictType |
region-select | 行政区划选择 | level, multiple |
user-select | 用户选择 | multiple, deptId |
dept-select | 部门选择 | multiple |
icon-select | 图标选择器 | - |
editor | 富文本编辑器 | height, toolbarConfig |
slot | 自定义插槽 | 需配合父组件 <template #slotName> 使用 |
custom-select | 自定义下拉 | configKey(动态 CRUD 配置键), labelField, valueField, searchFields |
验证规则
支持 Naive UI FormItem 的完整验证规则体系:
js
const formSchema = [
{
field: 'username',
label: '用户名',
type: 'input',
required: true,
rules: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度在 3 到 20 个字符', trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: '用户名只能包含字母、数字和下划线', trigger: 'blur' }
]
},
{
field: 'email',
label: '邮箱',
type: 'input',
rules: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
]
},
{
field: 'age',
label: '年龄',
type: 'number',
rules: [
{ required: true, message: '请输入年龄' },
{ type: 'number', min: 0, max: 150, message: '年龄范围 0-150', trigger: 'blur' }
]
}
]条件显示与联动
通过 vIf 和 vShow 实现字段间的联动显示:
js
const formSchema = [
{ field: 'type', label: '证件类型', type: 'select',
options: [
{ label: '身份证', value: 'idcard' },
{ label: '护照', value: 'passport' },
{ label: '其他', value: 'other' }
] },
// 仅当选择「身份证」时显示
{ field: 'idCard', label: '身份证号', type: 'input',
vIf: (formData) => formData.type === 'idcard' },
// 仅当选择「护照」时显示
{ field: 'passport', label: '护照号', type: 'input',
vIf: (formData) => formData.type === 'passport' }
]onChange 回调中也可以通过 context 动态修改表单状态:
js
const formSchema = [
{
field: 'province',
label: '省份',
type: 'select',
options: provinceOptions,
onChange: (value, formData, context) => {
// 省份变化时清空城市
formData.city = undefined
// 通过 context 访问表单引用
}
}
]方法
通过 ref 获取组件实例:
js
const formRef = ref(null)
// 表单验证(返回 Promise,验证失败 reject)
await formRef.value.validate()
// 恢复验证状态(清除错误提示)
formRef.value.restoreValidation()
// 重置表单到初始值(根据 schema 中的 defaultValue)
formRef.value.reset()
// 手动重置为指定值
formRef.value.resetFields({ name: '', age: 0 })
// 获取当前表单数据
const data = formRef.value.getFormData()
// 设置表单数据(替换所有字段值)
formRef.value.setFormData({ name: '张三', age: 25 })
// 设置单个字段值
formRef.value.setFieldValue('name', '李四')
// 获取单个字段值
const name = formRef.value.getFieldValue('name')事件
| 事件名 | 参数 | 说明 |
|---|---|---|
submit | (formData) | 点击提交按钮并通过验证后触发 |
reset | (formData) | 点击重置按钮后触发 |
cancel | - | 点击取消按钮 |
field-change | (field, value, formData) | 任意字段值变化 |
插槽
表单字段插槽
使用 type: 'slot' 声明插槽字段,然后在父组件中通过插槽渲染:
vue
<template>
<AiForm ref="formRef" v-model:value="formData" :schema="formSchema">
<template #customField="{ field, value, formData }">
<CustomRichEditor v-model:value="formData.content" />
</template>
<template #formAction="{ formData }">
<n-space>
<n-button type="primary" @click="handleCustomSubmit">自定义提交</n-button>
<n-button @click="handlePreview">预览</n-button>
</n-space>
</template>
</AiForm>
</template>
<script setup>
const formSchema = [
{ field: 'title', label: '标题', type: 'input' },
{ field: 'content', label: '内容', type: 'slot', slot: 'customField' }
]
</script>