分组表单

Framework:

概述

分组表单功能允许您将表单字段分组显示,使复杂的表单更加清晰和易于管理。本章节将详细介绍分组表单的配置和使用方法。

基础用法

GroupSchema 类型

type GroupSchema = {
  key: string;              // 分组唯一标识
  fields: FieldSchema[];    // 分组内的字段配置
  component?: string;       // 自定义分组容器组件
  props?: Record<string, unknown>;  // 分组容器的额外属性
};

说明GroupSchema 没有内置的 label 属性。如需显示分组标题,可通过 props 传递,例如 props: { title: '分组标题' },然后在自定义 DefaultGroup 组件中读取并渲染。

基础示例

const schema: GroupSchema[] = [
  {
    key: 'basic',
    props: { title: '基本信息' },
    fields: [
      { type: 'string', name: 'name', label: '姓名', required: true },
      { type: 'string', name: 'email', label: '邮箱', required: true }
    ]
  },
  {
    key: 'address',
    props: { title: '地址信息' },
    fields: [
      { type: 'string', name: 'province', label: '省份' },
      { type: 'string', name: 'city', label: '城市' },
      { type: 'string', name: 'address', label: '详细地址' }
    ]
  }
];

分组表单示例

分组配置

分组标题

通过 props 传递分组标题,在自定义 DefaultGroup 组件中读取并渲染:

{
  key: 'personal',
  props: { title: '个人信息' },  // 通过 props 传递标题
  fields: [...]
}

分组标识

key 属性用于唯一标识分组,也可以用于布局配置:

const layout = {
  groups: {
    basic: {
      groupClassName: 'basic-group',
      groupStyle: { backgroundColor: '#f0f8ff' }
    },
    address: {
      groupClassName: 'address-group',
      groupStyle: { backgroundColor: '#fff0f0' }
    }
  }
};

自定义分组容器

全局自定义

通过 components 属性注册自定义分组容器:

const CustomGroup = ({ title, children, className, style }) => (
  <div className={className} style={style}>
    <h3 style={{ 
      margin: '0 0 16px 0', 
      padding: '12px',
      backgroundColor: '#1890ff',
      color: 'white',
      borderRadius: '4px'
    }}>
      {title}
    </h3>
    <div style={{ padding: '16px' }}>
      {children}
    </div>
  </div>
);

const InputComponent = ({ value, onChange, ...props }) => (
  <input
    value={value || ''}
    onChange={(e) => onChange(e.target.value)}
    style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}
    {...props}
  />
);

const components = {
  DefaultControl: InputComponent,
  CustomGroup: CustomGroup  // 注册自定义分组组件
};

// 在 schema 中按名称引用已注册的组件
const schema: GroupSchema[] = [
  {
    key: 'special',
    component: 'CustomGroup',  // 按名称引用已注册的组件
    props: { title: '特殊信息' },
    fields: [...]
  }
];

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

说明GroupSchema.component 用于按名称引用已注册的组件,而非注册组件本身。所有自定义组件必须通过 components 对象注册。

分组布局配置

统一配置

通过 layout 统一配置所有分组的样式:

const layout = {
  groupClassName: 'form-group',
  groupStyle: {
    marginBottom: '24px',
    padding: '20px',
    border: '1px solid #e0e0e0',
    borderRadius: '8px'
  }
};

按分组配置

通过 groups 为特定分组配置样式:

const layout = {
  groups: {
    basic: {
      groupClassName: 'basic-group',
      groupStyle: { backgroundColor: '#f0f8ff' }
    },
    advanced: {
      groupClassName: 'advanced-group',
      groupStyle: { backgroundColor: '#fff8e1' }
    }
  }
};

分组顺序

分组按照数组顺序渲染:

const schema: GroupSchema[] = [
  { key: 'first', props: { title: '第一组' }, fields: [...] },   // 第一个显示
  { key: 'second', props: { title: '第二组' }, fields: [...] },  // 第二个显示
  { key: 'third', props: { title: '第三组' }, fields: [...] }    // 第三个显示
];

分组配置约束

Schema FormX 对分组配置有以下约束:

  • FieldSchema[](字段数组)和 GroupSchema[](分组数组)不能混合使用
  • 如果 schema 中包含 name 属性的项,会被识别为 FieldSchema[],自动包裹在默认分组中
  • 如果 schema 中包含 fields 属性的项,会被识别为 GroupSchema[],按分组渲染
// ✅ 正确:纯字段数组(自动包裹在默认分组中)
const fieldSchema: FieldSchema[] = [
  { type: 'string', name: 'name', label: '姓名' },
  { type: 'string', name: 'email', label: '邮箱' }
];

// ✅ 正确:纯分组数组
const groupSchema: GroupSchema[] = [
  {
    key: 'basic',
    props: { title: '基本信息' },
    fields: [
      { type: 'string', name: 'name', label: '姓名' }
    ]
  }
];

// ❌ 错误:混合字段和分组
const mixedSchema = [
  { type: 'string', name: 'name', label: '姓名' },  // 字段
  { key: 'details', fields: [...] }                   // 分组
];

分组验证

分组内的字段独立进行验证,验证规则与普通字段相同:

const schema: GroupSchema[] = [
  {
    key: 'basic',
    props: { title: '基本信息' },
    fields: [
      { 
        type: 'string', 
        name: 'name', 
        label: '姓名', 
        required: true  // 必填验证
      },
      {
        type: 'string',
        name: 'email',
        label: '邮箱',
        required: true,
        validator: ({ value }) => {
          if (!value.includes('@')) return '请输入有效的邮箱地址';
        }
      }
    ]
  }
];

说明:Schema FormX 没有内置的分组级验证机制。所有验证都是字段级的。如需实现分组级的视觉反馈(如分组边框变红),可通过自定义分组容器结合 validate() 返回的 errors 对象来实现。

动态分组

条件显示分组内的字段

GroupSchema 没有 hidden 属性。如需实现分组内字段的条件显示,应使用字段级的 hidden 属性配合依赖联动:

const schema: GroupSchema[] = [
  {
    key: 'userType',
    props: { title: '用户类型' },
    fields: [
      {
        type: 'string',
        name: 'userType',
        label: '用户类型',
        props: { options: ['个人', '企业'] }
      }
    ]
  },
  {
    key: 'personal',
    props: { title: '个人信息' },
    fields: [
      { type: 'string', name: 'name', label: '姓名' },
      {
        type: 'string',
        name: 'company',
        label: '公司名称',
        hidden: true,  // 默认隐藏
        deps: ['userType'],
        onDepsChange: ({ deps, schema }) => ({
          schema: { ...schema, hidden: deps[0] !== '企业' }
        })
      }
    ]
  }
];

最佳实践:通过字段级 hidden + deps + onDepsChange 实现条件显示,而不是动态修改 schema 结构。这种方式更稳定,且能正确处理数据清理。

常见用例

多步骤表单

使用分组实现多步骤表单:

const schema: GroupSchema[] = [
  {
    key: 'step1',
    props: { title: '第一步:基本信息' },
    fields: [
      { type: 'string', name: 'name', label: '姓名' },
      { type: 'string', name: 'email', label: '邮箱' }
    ]
  },
  {
    key: 'step2',
    props: { title: '第二步:详细信息' },
    fields: [
      { type: 'string', name: 'address', label: '地址' },
      { type: 'string', name: 'phone', label: '电话' }
    ]
  }
];

分类表单

将相关字段分组:

const schema: GroupSchema[] = [
  {
    key: 'personal',
    props: { title: '个人信息' },
    fields: [
      { type: 'string', name: 'name', label: '姓名' },
      { type: 'number', name: 'age', label: '年龄' }
    ]
  },
  {
    key: 'contact',
    props: { title: '联系方式' },
    fields: [
      { type: 'string', name: 'email', label: '邮箱' },
      { type: 'string', name: 'phone', label: '电话' }
    ]
  },
  {
    key: 'address',
    props: { title: '地址信息' },
    fields: [
      { type: 'string', name: 'province', label: '省份' },
      { type: 'string', name: 'city', label: '城市' },
      { type: 'string', name: 'address', label: '详细地址' }
    ]
  }
];

最佳实践

  1. 合理分组:将相关字段放在同一分组,提高表单可读性
  2. 清晰标题:为每个分组设置简洁明了的标题
  3. 统一风格:保持分组样式的一致性
  4. 避免过度分组:不要创建过多的分组,保持表单简洁
  5. 响应式设计:确保分组在不同屏幕尺寸下都能正常显示