# BasicForm 表单 :::tip 说明 `BasicForm` 位于 `src/components/Form/src/BasicForm.vue`,基于 NaiveUI 的 `n-form` 二次封装,通过 schemas 配置化定义表单字段,支持查询表单和业务表单两种模式。 ::: ## 组件架构 ``` src/components/Form/ ├── src/ │ ├── BasicForm.vue # 主组件 │ ├── props.ts # 属性定义 │ ├── helper.ts # 工具函数(placeholder 生成) │ ├── types/form.ts # 类型定义(FormSchema、FormProps) │ └── hooks/ │ ├── useFormEvents.ts # 表单事件(submit、reset、validate) │ └── useFormValues.ts # 表单值处理(默认值初始化) └── index.ts # 导出 useForm、FormSchema 等 ``` ## 查询表单(列表页搜索) 列表页的搜索区域使用 `useForm` 注册表单: ```vue ``` ### querySchemas 定义 ```typescript // src/views/system/position/querySchemas.ts import { FormSchema } from '@/components/Form/index'; export const schemas: FormSchema[] = [ { field: 'name', component: 'NInput', label: '岗位名称', componentProps: { placeholder: '请输入岗位名称', }, }, { field: 'status', component: 'NSelect', label: '状态', componentProps: { placeholder: '请选择状态', clearable: true, options: [ { label: '正常', value: '1' }, { label: '停用', value: '2' }, ], }, }, ]; ``` ### useForm 注册 ```typescript import { schemas } from './querySchemas'; import { useForm } from '@/components/Form/index'; const formParams = reactive({ name: '', status: '' }); const [register, {}] = useForm({ gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' }, labelWidth: 80, schemas, }); function handleSubmit(values: Recordable) { for (const key in formParams) { formParams[key] = ''; } for (const key in values) { formParams[key] = values[key]; } reloadTable(); } function handleReset(values: Recordable) { for (const key in formParams) { formParams[key] = ''; } for (const key in values) { formParams[key] = values[key]; } reloadTable(); } ``` `useForm` 返回 `[register, formActionType]`,`register` 传给 `@register` 事件完成表单绑定。 ## 业务表单(编辑弹窗) 编辑弹窗使用 `basicModal` + `useModal` + `n-form` 构建表单: ```vue ``` ### useModal 注册 ```typescript import { useModal } from '@/components/Modal'; const [modalRegister, { openModal, setSubLoading }] = useModal({ title: props.positionId ? '编辑岗位' : '添加岗位', subBtuText: '确定', width: 600, }); ``` ### 完整 script 示例 ```typescript import { getPositionDetail, positionAdd, positionUpdate } from '@/api/system/position'; import { onMounted, reactive, ref } from 'vue'; import { useMessage } from 'naive-ui'; import { useModal } from '@/components/Modal'; const emit = defineEmits(['success', 'update:visible']); const formRef = ref(); const message = useMessage(); const formData = reactive({ id: '', name: '', status: 1, sort: 0, }); const props = defineProps({ visible: { type: Boolean, required: true, default: false }, positionId: { type: Number, required: true, default: 0 }, }); const [modalRegister, { openModal, setSubLoading }] = useModal({ title: props.positionId ? '编辑岗位' : '添加岗位', subBtuText: '确定', width: 600, }); const handleSubmit = async () => { formRef.value .validate() .then(async () => { props.positionId ? await positionUpdate(formData) : await positionAdd(formData); message.success('操作成功'); setSubLoading(false); emit('update:visible', false); emit('success'); }) .catch(() => { setSubLoading(false); }); }; const handleClose = () => { emit('update:visible', false); }; const setFormData = async () => { const data = await getPositionDetail(props.positionId); for (const key in formData) { if (data[key] != null && data[key] != undefined) { formData[key] = data[key]; } } }; onMounted(() => { if (props.positionId) setFormData(); }); defineExpose({ openModal }); ``` ## BasicForm 内部渲染机制 `BasicForm.vue` 根据 `schema.component` 动态渲染对应的 NaiveUI 组件: ```vue ``` ## FormSchema 类型 ```typescript interface FormSchema { field: string; // 字段名 label?: string; // 标签文本 component?: ComponentType; // 组件类型:NInput / NSelect / NRadioGroup / NCheckbox / NDatePicker 等 componentProps?: any; // 组件属性(placeholder、options、clearable 等) rules?: object | object[]; // 自定义校验规则 defaultValue?: any; // 默认值 hidden?: boolean | (({ schema, values, model }) => boolean); // 是否隐藏 slot?: string; // 自定义插槽名 suffix?: string; // 后缀插槽名 labelMessage?: string; // 标签提示信息 isFull?: boolean; // 是否全宽 type?: string; // 组件子类型 giProps?: GridItemProps; // 栅格子项属性 showFeedback?: boolean; // 是否显示反馈 labelWidth?: number | string; // 标签宽度 } ``` ## 表单校验 ```typescript // 手动校验 const handleSubmit = async () => { formRef.value .validate() .then(async () => { // 校验通过,提交数据 }) .catch(() => { // 校验失败 }); }; // 清除校验 formRef.value?.clearValidate(); ``` Schema 中的 `required: true` 会自动生成必填校验规则,也可以通过 `rules` 自定义: ```typescript { field: 'name', label: '案例名称', component: 'NInput', rules: [ { required: true, message: '请输入案例名称', trigger: 'blur' }, { min: 1, max: 150, message: '长度在 1 到 150 个字符', trigger: 'blur' }, ], } ``` 在模板中使用 `n-form-item` 的 `rule` 属性也可以直接定义校验规则: ```vue ``` ## 表单赋值 编辑时需要将行数据赋值给表单: ```typescript const formData = reactive({ id: '', name: '', status: 1, sort: 0 }); const setFormData = async () => { const data = await getPositionDetail(props.positionId); for (const key in formData) { if (data[key] != null && data[key] != undefined) { formData[key] = data[key]; } } }; onMounted(() => { if (props.positionId) setFormData(); }); ``` ## n-select 用法 NaiveUI 的 `n-select` 使用 `:options` 传递选项数据,无需嵌套 `el-option`: ```vue ``` 选项数据格式: ```typescript const optionsData = reactive({ typeList: [ { label: '类型1', value: 1 }, { label: '类型2', value: 2 }, { label: '类型3', value: 3 }, { label: '类型4', value: 4 }, ], }); ``` ## 查询表单完整示例(带响应式栅格) ```typescript const [register, {}] = useForm({ gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' }, labelWidth: 80, schemas, }); ``` `gridProps` 使用 NaiveUI 的响应式栅格断点: - `1` — 默认每行 1 列 - `s:1` — 小屏 1 列 - `m:2` — 中屏 2 列 - `l:3` — 大屏 3 列 - `xl:4` — 超大屏 4 列 - `2xl:4` — 超超大屏 4 列 :::tip 温馨提示 查询表单的字段名需要与后端 `page_like_fields` 和 `page_eq_fields` 对应,确保查询参数能正确传递到后端。`useForm` 返回的 `register` 函数传给 `@register` 事件完成表单绑定,`formActionType` 提供 `setFieldsValue`、`resetFields`、`validate` 等方法。业务弹窗使用 `basicModal` + `useModal` 管理弹窗状态,通过 `@on-ok` 事件处理提交,通过 `setSubLoading(false)` 关闭提交按钮的 loading 状态。 ::: ## 总结 BasicForm 通过 schemas 配置化定义表单字段,内部根据 `component` 动态渲染对应的 NaiveUI 组件(`NInput`、`NSelect`、`NRadioGroup`、`NCheckbox`、`NDatePicker` 等)。查询表单使用 `useForm` 注册并绑定 `@submit`/`@reset` 事件,业务表单使用 `basicModal` + `useModal` + `n-form` + `reactive` 管理数据。NaiveUI 的表单组件统一使用 `v-model:value` 进行双向绑定,`n-form-item` 使用 `path` 代替 `prop` 指定校验字段,使用 `rule` 代替 `rules` 指定校验规则。支持多种组件类型、表单校验、数据绑定、条件隐藏、响应式栅格等能力。