实战:构建可配置化表单系统
在企业级应用开发中,表单是最常见也是最复杂的交互界面之一。构建一个可配置化的表单系统可以大大提高开发效率,减少重复代码。本文将带领大家实现一个完整的可配置化表单系统。
1. 核心架构设计
1.1 表单配置数据结构
首先定义表单配置的核心数据结构:
// types/form.ts
exportinterfaceFieldConfig {
type: 'input' | 'select' | 'radio' | 'checkbox' | 'date' | 'upload';
name: string;
label: string;
defaultValue?: any;
placeholder?: string;
rules?: ValidationRule[];
options?: Array<{
label: string;
value: any;
}>;
props?: Record<string, any>;
dependencies?: string[]; // 字段依赖
visible?: boolean | ((values: any) =>boolean);
disabled?: boolean | ((values: any) =>boolean);
}
exportinterfaceFormConfig {
name: string;
fields: FieldConfig[];
layout?: 'horizontal' | 'vertical';
labelWidth?: number | string;
submitText?: string;
onSubmit?: (values: any) =>void | Promise<void>;
}
exportinterfaceValidationRule {
required?: boolean;
message?: string;
pattern?: RegExp;
validator?: (value: any) =>boolean | Promise<boolean>;
}
1.2 核心表单组件
// components/ConfigurableForm.tsx
importReactfrom'react';
import { Form, message } from'antd';
import { FormConfig } from'../types/form';
import { useFormFields } from'../hooks/useFormFields';
import { createValidator } from'../utils/validator';
interfaceConfigurableFormProps {
config: FormConfig;
initialValues?: Record<string, any>;
onSubmit?: (values: any) =>void | Promise<void>;
}
exportconstConfigurableForm: React.FC<ConfigurableFormProps> = ({
config,
initialValues,
onSubmit
}) => {
const [form] = Form.useForm();
const fields = useFormFields(config.fields, form);
consthandleSubmit = async (values: any) => {
try {
if (config.onSubmit) {
await config.onSubmit(values);
}
if (onSubmit) {
awaitonSubmit(values);
}
message.success('提交成功');
} catch (error) {
message.error('提交失败:' + error.message);
}
};
return (
<Form
form={form}
layout={config.layout || 'horizontal'}
labelCol={{ span: 6 }}
wrapperCol={{ span: 14 }}
initialValues={initialValues}
onFinish={handleSubmit}
>
{fields}
<Form.Item wrapperCol={{ offset: 6, span: 14 }}>
<Button type="primary" htmlType="submit">
{config.submitText || '提交'}
</Button>
</Form.Item>
</Form>
);
};
2. 动态表单字段实现
2.1 字段渲染钩子
// hooks/useFormFields.ts
import { useEffect } from'react';
import { Form } from'antd';
import { FieldConfig } from'../types/form';
import { renderField } from'../utils/fieldRenderer';
exportfunctionuseFormFields(fields: FieldConfig[], form: FormInstance) {
useEffect(() => {
// 监听字段值变化,处理联动逻辑
const subscription = form.subscribe(
({ values, forms }) => {
fields.forEach(field => {
if (field.dependencies) {
const visible = evaluateFieldVisibility(field, values);
const disabled = evaluateFieldDisabled(field, values);
form.setFields([
{
name: field.name,
hidden: !visible,
disabled: disabled
}
]);
}
});
},
['values']
);
return() => subscription.unsubscribe();
}, [fields, form]);
return fields.map(field =>renderField(field, form));
}
2.2 字段渲染器
// utils/fieldRenderer.tsx
importReactfrom'react';
import { Form, Input, Select, Radio, Checkbox, DatePicker, Upload } from'antd';
import { FieldConfig } from'../types/form';
import { createValidator } from'./validator';
exportfunctionrenderField(field: FieldConfig, form: FormInstance) {
const {
type,
name,
label,
rules = [],
placeholder,
options,
props = {},
visible = true,
disabled = false
} = field;
const baseProps = {
placeholder,
disabled: typeof disabled === 'function' ? disabled(form.getFieldsValue()) : disabled,
...props
};
letfieldComponent: React.ReactNode;
switch (type) {
case'input':
fieldComponent = <Input {...baseProps} />;
break;
case'select':
fieldComponent = (
<Select {...baseProps}>
{options?.map(opt => (
<Select.Option key={opt.value} value={opt.value}>
{opt.label}
</Select.Option>
))}
</Select>
);
break;
case'radio':
fieldComponent = (
<Radio.Group {...baseProps}>
{options?.map(opt => (
<Radio key={opt.value} value={opt.value}>
{opt.label}
</Radio>
))}
</Radio.Group>
);
break;
// ... 其他字段类型的渲染逻辑
}
return (
<Form.Item
key={name}
name={name}
label={label}
rules={rules.map(createValidator)}
hidden={typeof visible === 'function' ? !visible(form.getFieldsValue()) : !visible}
>
{fieldComponent}
</Form.Item>
);
}
3. 表单验证系统
3.1 验证规则生成器
// utils/validator.ts
import { Rule } from'antd/lib/form';
import { ValidationRule } from'../types/form';
exportfunctioncreateValidator(rule: ValidationRule): Rule {
constbaseRule: Rule = {};
if (rule.required) {
baseRule.required = true;
baseRule.message = rule.message || '此字段为必填项';
}
if (rule.pattern) {
baseRule.pattern = rule.pattern;
baseRule.message = rule.message || '格式不正确';
}
if (rule.validator) {
baseRule.validator = async (_, value) => {
const result = await rule.validator(value);
if (!result) {
thrownewError(rule.message || '验证失败');
}
};
}
return baseRule;
}
3.2 自定义验证规则
// utils/customValidators.ts
exportconst customValidators = {
// 手机号验证
phone: {
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号'
},
// 邮箱验证
email: {
pattern: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/,
message: '请输入正确的邮箱地址'
},
// 异步验证示例
asyncuniqueUsername(value: string) {
const response = awaitfetch(`/api/check-username?username=${value}`);
const result = await response.json();
return !result.exists;
}
};
4. 表单联动实现
4.1 字段依赖处理
// utils/dependencyHandler.ts
exportfunctionevaluateFieldVisibility(
field: FieldConfig,
formValues: Record<string, any>
): boolean {
if (!field.dependencies || !field.visible) {
returntrue;
}
if (typeof field.visible === 'function') {
return field.visible(formValues);
}
return field.visible;
}
exportfunctionevaluateFieldDisabled(
field: FieldConfig,
formValues: Record<string, any>
): boolean {
if (!field.dependencies || !field.disabled) {
returnfalse;
}
if (typeof field.disabled === 'function') {
return field.disabled(formValues);
}
return field.disabled;
}
4.2 联动配置示例
// examples/formConfig.ts
exportconstformConfig: FormConfig = {
name: 'userProfile',
fields: [
{
type: 'select',
name: 'userType',
label: '用户类型',
options: [
{ label: '个人', value: 'personal' },
{ label: '企业', value: 'business' }
]
},
{
type: 'input',
name: 'companyName',
label: '公司名称',
dependencies: ['userType'],
visible: (values) => values.userType === 'business',
rules: [{ required: true, message: '请输入公司名称' }]
},
{
type: 'checkbox',
name: 'needInvoice',
label: '是否需要发票',
defaultValue: false
},
{
type: 'input',
name: 'taxNumber',
label: '税号',
dependencies: ['needInvoice'],
visible: (values) => values.needInvoice,
rules: [{ required: true, message: '请输入税号' }]
}
]
};
5. 实际应用示例
5.1 完整表单示例
// examples/UserProfileForm.tsx
importReactfrom'react';
import { ConfigurableForm } from'../components/ConfigurableForm';
import { message } from'antd';
constuserProfileConfig: FormConfig = {
name: 'userProfile',
layout: 'horizontal',
labelWidth: '120px',
fields: [
{
type: 'input',
name: 'username',
label: '用户名',
rules: [
{ required: true, message: '请输入用户名' },
{ validator: customValidators.uniqueUsername, message: '用户名已存在' }
]
},
{
type: 'input',
name: 'phone',
label: '手机号',
rules: [customValidators.phone]
},
{
type: 'select',
name: 'region',
label: '所在地区',
options: [
{ label: '中国', value: 'CN' },
{ label: '美国', value: 'US' },
{ label: '日本', value: 'JP' }
]
}
],
submitText: '保存信息'
};
exportfunctionUserProfilePage() {
consthandleSubmit = async (values: any) => {
try {
awaitsaveUserProfile(values);
message.success('保存成功');
} catch (error) {
message.error('保存失败:' + error.message);
}
};
return (
<div className="user-profile-page">
<h1>个人信息设置</h1>
<ConfigurableForm
config={userProfileConfig}
onSubmit={handleSubmit}
/>
</div>
);
}
5.2 表单数据处理
// services/formDataService.ts
exportclassFormDataService {
staticasyncvalidateFormData(config: FormConfig, values: any) {
consterrors: Record<string, string> = {};
for (const field of config.fields) {
if (!field.rules) continue;
for (const rule of field.rules) {
try {
const validator = createValidator(rule);
await validator.validator(null, values[field.name]);
} catch (error) {
errors[field.name] = error.message;
break;
}
}
}
return {
valid: Object.keys(errors).length === 0,
errors
};
}
statictransformFormData(config: FormConfig, values: any) {
constresult: Record<string, any> = {};
for (const field of config.fields) {
if (values[field.name] !== undefined) {
result[field.name] = values[field.name];
}
}
return result;
}
}
总结
构建可配置化表单系统需要考虑以下几个关键点:
1. 清晰的配置数据结构设计 2. 灵活的字段渲染机制 3. 强大的表单验证系统 4. 完善的字段联动处理 5. 友好的错误提示 6. 可扩展的接口设计
通过以上实现,我们获得了一个功能完备的可配置化表单系统,它能够:
• 通过配置快速生成复杂表单 • 支持多种表单字段类型 • 实现灵活的字段联动 • 提供完善的验证机制 • 易于扩展和维护
在实际项目中,可以基于这个基础框架,根据具体需求进行扩展和定制,以满足不同场景的表单需求。