8.1 KiB
8.1 KiB
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 注册表单:
<n-card :bordered="false" class="pt-3 mb-3 proCard">
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
</n-card>
querySchemas 定义
// 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 注册
import { schemas } from './querySchemas';
import { useForm } from '@/components/Form/index';
const formParams = reactive({ name: '', status: '' });
const [register, {}] = useForm({
labelWidth: 80,
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
submitOnReset: true,
schemas,
});
function handleSubmit(values: Recordable) {
handleReset();
for (const key in values) { formParams[key] = values[key]; }
reloadTable();
}
function handleReset() {
for (const key in formParams) { formParams[key] = ''; }
}
useForm 返回 [register, formActionType],register 传给 @register 事件完成表单绑定。
业务表单(编辑弹窗)
编辑弹窗中直接使用 n-form + n-form-item 构建表单:
<!-- src/views/system/position/edit.vue -->
<template>
<basicModal @register="modalRegister" ref="modalRef" @on-ok="handleSubmit" @on-close="handleClose">
<template #default>
<n-form class="ls-form" ref="formRef" :model="formData" label-placement="left" label-width="85px">
<n-form-item
label="岗位名称"
path="name"
:rule="{ required: true, message: '请输入岗位名称', trigger: 'blur' }"
>
<n-input v-model:value="formData.name" placeholder="请输入岗位名称" clearable />
</n-form-item>
<n-form-item label="岗位状态" path="status">
<n-radio-group v-model:value="formData.status" name="status">
<n-radio :value="1">正常</n-radio>
<n-radio :value="2">停用</n-radio>
</n-radio-group>
</n-form-item>
<n-form-item label="排序" path="sort">
<n-input-number v-model:value="formData.sort" />
</n-form-item>
</n-form>
</template>
</basicModal>
</template>
BasicForm 内部渲染机制
BasicForm.vue 根据 schema.component 动态渲染对应的 NaiveUI 组件:
<!-- src/components/Form/src/BasicForm.vue 核心逻辑 -->
<template v-for="schema in getSchema" :key="schema.field">
<!-- Checkbox -->
<template v-if="schema.component === 'NCheckbox'">
<n-checkbox-group v-model:value="formModel[schema.field]">
<n-checkbox v-for="item in schema.componentProps?.options" :value="item.value">
{{ item.label }}
</n-checkbox>
</n-checkbox-group>
</template>
<!-- RadioGroup -->
<template v-else-if="schema.component === 'NRadioGroup'">
<n-radio-group v-model:value="formModel[schema.field]">
<n-radio v-for="item in schema.componentProps?.options" :value="item.value">
{{ item.label }}
</n-radio>
</n-radio-group>
</template>
<!-- Select -->
<template v-else-if="schema.component === 'NSelect'">
<n-select v-model:value="formModel[schema.field]" :options="schema.componentProps?.options" />
</template>
<!-- Daterange -->
<template v-else-if="schema.component === 'NDatePicker'">
<n-date-picker v-model:value="formModel[schema.field]" type="daterange" value-format="yyyy-MM-dd" />
</template>
<!-- 其他组件:动态渲染 n-{component} -->
<component v-else :is="schema.component" v-model:value="formModel[schema.field]" v-bind="schema.componentProps" />
</template>
FormSchema 类型
interface FormSchema {
field: string; // 字段名
label: string; // 标签文本
component: string; // 组件类型:NInput / NSelect / NRadioGroup / NCheckbox / NDatePicker 等
componentProps?: any; // 组件属性(placeholder、options、clearable 等)
required?: boolean; // 是否必填
rules?: any[]; // 自定义校验规则
defaultValue?: any; // 默认值
hidden?: boolean | (({ schema, values, model }) => boolean); // 是否隐藏
slot?: string; // 自定义插槽名
suffix?: string; // 后缀插槽名
labelMessage?: string; // 标签提示信息
isFull?: boolean; // 是否全宽
type?: string; // 组件子类型
}
表单校验
// 手动校验
const handleSubmit = async () => {
await formRef.value?.validate();
// 校验通过,提交数据
};
// 清除校验
formRef.value?.clearValidate();
Schema 中的 required: true 会自动生成必填校验规则,也可以通过 rules 自定义:
{
field: 'name',
label: '岗位名称',
component: 'NInput',
rules: [
{ required: true, message: '请输入岗位名称', trigger: 'blur' },
{ min: 1, max: 150, message: '长度在 1 到 150 个字符', trigger: 'blur' },
],
}
表单赋值
编辑时需要将行数据赋值给表单:
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();
});
:::tip 温馨提示
查询表单的字段名需要与后端 Repository 的 page() 方法中 filters / like_filters 对应,确保查询参数能正确传递到后端。useForm 返回的 register 函数传给 @register 事件完成表单绑定,formActionType 提供 setFieldsValue、resetFields、validate 等方法。
:::
总结
BasicForm 通过 schemas 配置化定义表单字段,内部根据 component 动态渲染对应的 NaiveUI 组件。查询表单使用 useForm 注册并绑定 @submit/@reset 事件,业务表单直接使用 n-form + reactive 管理数据。支持多种组件类型、表单校验、数据绑定、条件隐藏等能力。