# 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
{{ item.label }}
```
## 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` 指定校验规则。支持多种组件类型、表单校验、数据绑定、条件隐藏、响应式栅格等能力。