Skip to content

BasicForm 表单

说明

BasicForm 位于 src/components/Form/src/BasicForm.vue,基于 ElementPlus 的 el-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
<el-card :bordered="false" class="pt-3 mb-3 proCard">
    <BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
</el-card>

querySchemas 定义

typescript
// src/views/system/position/querySchemas.ts

import { FormSchema } from '@/components/Form/index';

export const schemas: FormSchema[] = [
    {
        field: 'name',
        component: 'Input',
        label: '岗位名称',
        componentProps: {
            placeholder: '请输入岗位名称',
        },
    },
    {
        field: 'status',
        component: 'Select',
        label: '状态',
        componentProps: {
            placeholder: '请选择状态',
            clearable: true,
            options: [
                { label: '正常', value: '1' },
                { label: '禁用', value: '2' },
            ],
        },
    },
];

useForm 注册

typescript
import { schemas } from './querySchemas';
import { useForm } from '@/components/Form/index';
import { ColProps } from 'element-plus';

const formParams = reactive({ name: '', status: '' });

const [register, {}] = useForm({
    labelWidth: 80,
    layout: 'horizontal',
    colProps: { span: 6 } as ColProps,
    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 事件完成表单绑定。

业务表单(编辑弹窗)

编辑弹窗中直接使用 el-form + el-form-item 构建表单:

vue
<!-- src/views/system/position/edit.vue -->
<template>
    <el-dialog v-model="props.visible" :title="props.positionId ? '编辑' : '新增'" width="500">
        <el-form class="ls-form" ref="formRef" :model="formData" label-width="80px">
            <el-form-item
                label="岗位名称"
                prop="name"
                :rules="{ required: true, message: '请输入岗位名称', trigger: 'blur' }"
            >
                <el-input v-model="formData.name" placeholder="请输入岗位名称" clearable />
            </el-form-item>
            <el-form-item label="岗位状态" prop="status">
                <el-radio-group v-model="formData.status">
                    <el-radio :value="1">正常</el-radio>
                    <el-radio :value="2">停用</el-radio>
                </el-radio-group>
            </el-form-item>
            <el-form-item label="排序" prop="sort">
                <el-input-number v-model="formData.sort" />
            </el-form-item>
        </el-form>
    </el-dialog>
</template>

BasicForm 内部渲染机制

BasicForm.vue 根据 schema.component 动态渲染对应的 ElementPlus 组件:

vue
<!-- src/components/Form/src/BasicForm.vue 核心逻辑 -->
<template v-for="schema in getSchema" :key="schema.field">
    <!-- Checkbox -->
    <template v-if="schema.component === 'Checkbox'">
        <el-checkbox-group v-model="formModel[schema.field]">
            <el-checkbox v-for="item in schema.componentProps?.options" :value="item.value">
                {{ item.label }}
            </el-checkbox>
        </el-checkbox-group>
    </template>

    <!-- RadioGroup -->
    <template v-else-if="schema.component === 'RadioGroup'">
        <el-radio-group v-model="formModel[schema.field]">
            <el-radio v-for="item in schema.componentProps?.options" :value="item.value">
                {{ item.label }}
            </el-radio>
        </el-radio-group>
    </template>

    <!-- Select -->
    <template v-else-if="schema.component === 'Select'">
        <el-select v-model="formModel[schema.field]">
            <el-option v-for="item in schema.componentProps?.options" :value="item.value" :label="item.label" />
        </el-select>
    </template>

    <!-- Daterange -->
    <template v-else-if="schema.component === 'Daterange'">
        <el-date-picker v-model="formModel[schema.field]" type="daterange" value-format="YYYY-MM-DD" />
    </template>

    <!-- 其他组件:动态渲染 el-{component} -->
    <component v-else :is="`el-${schema.component}`" v-model="formModel[schema.field]" />
</template>

FormSchema 类型

typescript
interface FormSchema {
    field: string;                                                    // 字段名
    label: string;                                                   // 标签文本
    component: string;                                               // 组件类型:Input / Select / RadioGroup / Checkbox / DatePicker 等
    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;                                                   // 组件子类型
}

表单校验

typescript
// 手动校验
const handleSubmit = async () => {
    await formRef.value?.validate();
    // 校验通过,提交数据
};

// 清除校验
formRef.value?.clearValidate();

Schema 中的 required: true 会自动生成必填校验规则,也可以通过 rules 自定义:

typescript
{
    field: 'name',
    label: '岗位名称',
    component: 'Input',
    rules: [
        { required: true, message: '请输入岗位名称', trigger: 'blur' },
        { min: 1, max: 150, message: '长度在 1 到 150 个字符', trigger: 'blur' },
    ],
}

表单赋值

编辑时需要将行数据赋值给表单:

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();
});

温馨提示

查询表单的字段名需要与后端 page_like_fieldspage_eq_fields 对应,确保查询参数能正确传递到后端。useForm 返回的 register 函数传给 @register 事件完成表单绑定,formActionType 提供 setFieldsValueresetFieldsvalidate 等方法。

总结

BasicForm 通过 schemas 配置化定义表单字段,内部根据 component 动态渲染对应的 ElementPlus 组件。查询表单使用 useForm 注册并绑定 @submit/@reset 事件,业务表单直接使用 el-form + reactive 管理数据。支持多种组件类型、表单校验、数据绑定、条件隐藏等能力。

小蚂蚁云团队 · 提供技术支持