表单配置详解

Framework:

概述

表单配置是 Schema FormX 的核心,通过配置来定义表单的结构、行为和样式。本章节将详细介绍表单配置的各项参数。

基础配置

Form 组件属性

属性类型必填说明
schemaFieldSchema[] | GroupSchema[]表单字段配置
layoutLayout布局配置
componentsobject自定义组件映射
dataobject受控数据
defaultDataobject默认数据
onChangefunction数据变化回调

示例

Schema 配置

数据管理模式

Schema FormX 支持两种数据管理模式:

非受控模式(推荐)

使用 defaultData 设置初始值,表单内部自动管理数据状态:

<Form 
  schema={schema} 
  defaultData={{ name: '张三', age: 25 }}
  onChange={({ data }) => console.log('数据变化:', data)}
/>

特点

  • 简单易用,无需手动同步状态
  • 适合大多数场景
  • 通过 ref 调用 getData()/setData() 操作数据

受控模式

使用 data + onChange 完全控制表单数据:

const [formData, setFormData] = useState({ name: '', age: 0 });

<Form 
  schema={schema} 
  data={formData}
  onChange={({ data }) => setFormData(data)}
/>

特点

  • 外部完全控制数据流向
  • 适合需要与外部状态(如 Redux、URL 参数)联动的场景
  • onChange 回调中的 data 是冻结对象,需浅拷贝后修改

模式对比

对比项非受控模式受控模式
数据管理组件内部外部状态
属性defaultDatadata + onChange
复杂度
适用场景简单表单复杂业务

注意datadefaultData 可以同时使用。当两者同时提供时,data 优先级更高,defaultData 用于 isDirty() 的基准比较。例如,可以在受控模式下通过 defaultData 设置初始值基准,用于判断表单数据是否被修改。

关于 hidden 字段:设置 hidden: true 仅在视觉上隐藏字段(display: none)。隐藏字段仍会参与验证 —— 如果隐藏字段设置了 required: true,仍会触发验证错误。如需跳过隐藏字段的验证,请使用条件 required 或自定义 validator

FieldSchema 类型

每个字段配置项包含以下属性:

type FieldSchema = {
  type?: FieldType;        // 字段类型,默认为 string
  name: string;            // 字段名,用于数据绑定
  label: string;           // 显示名称
  required?: boolean;      // 是否必填
  component?: string;      // 自定义组件名称
  props?: Record<string, unknown>;  // 组件属性
  hidden?: boolean;        // 是否隐藏
  deps?: string[];         // 依赖字段
  onDepsChange?: OnDepsChange;  // 依赖变化回调
  validator?: Validator;   // 验证器
};

字段类型

类型说明默认组件
string字符串类型Input
number数字类型InputNumber
boolean布尔类型Checkbox
object对象类型自定义
array数组类型自定义

字段配置示例

const schema: FieldSchema[] = [
  {
    type: 'string',
    name: 'username',
    label: '用户名',
    required: true,
    props: {
      placeholder: '请输入用户名'
    }
  },
  {
    type: 'number',
    name: 'age',
    label: '年龄',
    props: {
      min: 0,
      max: 120
    }
  },
  {
    type: 'boolean',
    name: 'agree',
    label: '同意协议'
  }
];

Layout 配置

Layout 类型

type Layout = {
  formClassName?: string;
  formStyle?: CSSProperties;
  groupClassName?: string;
  groupStyle?: CSSProperties;
  fieldClassName?: string;
  fieldStyle?: CSSProperties;
  labelClassName?: string;
  labelStyle?: CSSProperties;
  tipClassName?: string;
  tipStyle?: CSSProperties;
  controlClassName?: string;
  controlStyle?: CSSProperties;
  groups?: Record<string, SetUI<'group'>>;
  fields?: Record<string, SetUI<'field' | 'label' | 'control' | 'tip'>>;
};

布局配置示例

const layout: Layout = {
  formClassName: 'my-form',
  formStyle: { maxWidth: '600px' },
  fieldClassName: 'form-field',
  fieldStyle: { marginBottom: '16px' },
  labelClassName: 'field-label',
  labelStyle: { 
    display: 'block', 
    marginBottom: '4px', 
    fontWeight: '500' 
  },
  controlClassName: 'field-control',
  controlStyle: { width: '100%' }
};

按字段配置样式

const layout: Layout = {
  fields: {
    username: {
      fieldClassName: 'username-field',
      labelClassName: 'username-label',
      controlClassName: 'username-control'
    },
    email: {
      fieldClassName: 'email-field',
      labelClassName: 'email-label',
      controlClassName: 'email-control'
    }
  }
};

组件配置

全局组件注册

通过 components 属性注册所有自定义组件:

const components = {
  DefaultControl: MyInputComponent,
  CustomSelect: MySelectComponent,
  CustomDatePicker: MyDatePicker
};

<Form components={components} schema={schema} />

字段指定组件

通过 component 属性为特定字段指定组件:

const schema: FieldSchema[] = [
  {
    type: 'string',
    name: 'status',
    label: '状态',
    component: 'CustomSelect',
    props: {
      options: ['启用', '禁用']
    }
  }
];

数据配置

默认数据

const defaultData = {
  username: '张三',
  email: 'zhangsan@example.com',
  age: 18
};

<Form defaultData={defaultData} schema={schema} />

受控数据

const [formData, setFormData] = useState({
  username: '',
  email: ''
});

<Form 
  data={formData} 
  schema={schema} 
  onChange={({ data }) => setFormData(data)} 
/>

数据变化回调

<Form 
  schema={schema} 
  onChange={({ data, name, value }) => {
    console.log('字段变化:', name, value);
    console.log('当前数据:', data);
  }} 
/>

触发时机onChange 在字段值每次变化时立即触发(非失焦触发)。每次值变化都会同步调用 onChange,同时触发依赖联动和字段验证。

最佳实践

  1. 使用 TypeScript:为表单数据定义类型,获得更好的类型提示
  2. 分离配置:将 schema 和 layout 配置独立出来,便于维护
  3. 组件复用:将常用的自定义组件注册为全局组件
  4. 样式分离:使用 className 而非内联样式,便于主题定制