React Admin KitReact Admin Kit
  • 简介
  • 组件
  • 更新日志
⌘ K
核心组件
SchemaForm - JSON 表单
ModalForm - 弹窗表单
ProTable - 高级表格
衍生组件
BusinessSelect - 业务下拉
BusinessTreeSelect - 业务树下拉
Button - 按钮
LinkButton - 链接按钮
FormUpload - 表单上传
默认设置
SettingProvider - 默认设置
最后更新时间:
帮助改进此文档
Copyright © 2024 | Powered by dumi

TABLE OF CONTENTS

SchemaForm - JSON 表单

通过一个配置式的 schema 来生成表单. schema 与 antd table 的 columns 非常类似.

一个基本的 Schema 表单

在开始深入之前, 让我们先来了解几个重要的概念.

SchemaForm 的底层还是 antd form 组件. 它通过遍历 schema 数组 来生成表单项.

以这个例子来说, columns 数组 中的每一个对象都是一个 schema.

{
title: '用户名',
dataIndex: 'username',
fieldProps: {
Placeholder: '请输入用户名'
},
formItemProps: {
rules: [
{
required: true,
},
],
},
},

如果用 antd form 的方式来书写, 那么这个 schema 生成的表单项是这样的:

import { Form, Input } from 'antd'
<Form>
<Form.Item
label='用户名'
name='username'
rules={[
{
required: true
}
]}
>
<Input placeholder='请输入用户名' />
</Form.Item>
</Form>

不难看出, 在 schema 中:

  • title 对应的 Form.Item 中的 label.
  • dataIndex 对应的 Form.Item 中的 name.
  • formItemProps 的值会传递给 Form.Item 组件.
  • fieldProps 的值会传递给被 Form.Item 包裹的组件 (对这个例子来说这个组件是 Input).

我们再来看一下第二个 schema:

{
title: "性别",
dataIndex: "sex",
valueType: "radio",
fieldProps: {
options: [
{ label: "男", value: "男" },
{ label: "女", value: "女" },
],
},
};

与第一个 schema 不同的地方在于, 它多了一个 valueType 字段, 很显然 SchemaForm 通过 valueType 字段渲染出了不同的组件, 以这个例子来说 valueType: 'radio' 对应的是一个 Radio.Group 组件, 并且通过 fieldProps 给它传入了 options 属性.

这是使用 SchemaForm 的优点之一, 大部分常用的组件都能通过 valueType 来渲染.

schema 部分介绍完了, 我们再来看一下 表单实例 FormInstance.

表单实例 是 antd form 中一个比较重要的概念, 通过实例中的方法, 我们可以完成很多操作, 比如: 提交表单, 重置表单, 给表单赋值等.

在 SchemaForm 中, 我们通过 formRef 属性来获取 表单实例.

import { useRef } from 'react';
import { ProFormInstance, SchemaForm } from 'react-admin-kit';
const formRef = useRef<ProFormInstance>() // 注意这里使用的类型定义是 ProFormInstance. 它在 antd form instance 的基础上扩展了一些方法
<SchemaForm
formRef={formRef}
>
formRef.current?.submit() // 提交
formRef.current?.resetFields() // 重置
formRef.current?.setFieldsValue() // 赋值

在后文中还会详细介绍 表单实例 中的其它一些方法.

以上就是 SchemaForm 的基本概念, 看到这你已经可以开始书写一些简单的表单了✨.

随着你使用的不断深入, 你可以不断查看下面的各种例子来了解 SchemaForm 的其它属性, 相信你会越来越得心应手的.

valueType

valueType 是 schema 里的一个非常重要的字段, 通过指定 valueType 就能映射出不同的表单项. 常用的 valueType 有 money digit textarea date dateRange select radio 等, 完整的列表见这里. 当 valueType 没有指定时, 默认渲染的表单项是 Input 组件.

如果 valueType 不能满足你的需求, 可以使用 renderFormItem 完全自定义表单项.

自定义的组件需要满足 Form.Item 的规范, 即组件能接受 value 和 onChange 属性.

{
renderFormItem: (schema, config, form) => {
return <MyComp />;
};
}

required

由于表单中设置必选的频率比较高,schema 中新增了 required 字段,作为 formItemProps: { rules: [{ required: true }]} 的简写. 当然 formItemProps 的优先级更高.

水平模式 horizontal

⭐ grid 模式 (推荐)

grid 模式可以任意控制每个 FormItem 所占的空间

grid 模式 (水平方向)

当设成水平方向时, 如果不同行所占的空间不同, 表单项 label 的宽度可能会不一致.

以下面这个例子为例, 第一行表单项独占 24, 第二行每个表单项占 8, 第三行表单项占 16. 默认状态下他们的 label 宽度会不一致.

如果要实现 label 宽度固定就需要对每个表单项单独指定 labelCol.

labelCol可以在 SchemaForm 上指定, 也可以在 schema 的 formItemProps 里指定, 后者的优先级更高.

labelCol的值是以所在表单项的宽度为 24 分来算出的比例值. 所以如果表单项的宽度不同, labelCol的值需要经过一定的计算. 一般先以最窄的表单项为基准, 设定好它的labelCol, 再计算其它表单项的labelCol, 来使他们的 label 宽保持相等.

以这个例子为例, 先设定第二行的labelCol为{span: 9}, 可算出 label 的宽度为 9/24 * 8 = 3. 代表在整行 24 份中占 3 份.

所以第一行的labelCol可以设成{span: 3}

第三行因为表单项占 16 份, 所以labelCol应该设成{span: 4.5}. 4.5/24 * 16 = 3. 但是 span 值只能是整数. 可以改传 flex, flex: 0 0 18.75%. 3/16 = 0.1875. 参考 grid 组件

空白占位

需要强制换行时可用空白占位.

只读模式

只读模式下自定义显示走的render方法. 空白占位请使用render: () => null

render 函数的第二个参数 record 里不仅包含了表单里的所有值, 还包含了 initialValues 里除表单项外的其它值和 setFieldsValue 里除表单项外的其它值, 提高了实用性.

表单项联动

可以使用 valueType='dependency' 来做显示控制, name里的值是需要监控的表单项.

初始值和表单项赋值

提交按钮 submitter

submitter 属性默认为 false. 开启后可自动生成提交按钮.

还可以给生成的按钮传递属性.

⭐ 内嵌模式 (Embed)

对于复杂表单, 内嵌模式可以让每个区块单独设置布局, 同时通过设置 valueBaseName 属性, 数据也可以收集在各自的对象里.

以这个例子为, 基本信息区块和业务信息区块可以单独设置布局. 同时表单提交时的数据会收集成 { base: { username: 'xx'}, business: { company: 'xx'}}

这个例子同时还展示了表单项的赋值和表单项的联动控制.

  • 选择了公司后, 地址会自动带出.
  • 办理业务选择第一项, 基本信息中的身份证项会隐藏.

👉 需要注意的是, 在 embed 模式下, valueBaseName 的实现仅仅只是把 schema 中的 dataIndex 转换成数组. 见 antd 的这个例子.

所以在 setFieldsValue 的时候, 需要把 valueBaseName 的值也考虑进去.

setFieldsValue({ business: { company: 'xxx' } });

同时在做联动控制时, 当 valueType='dependency'并且 valueBaseName 有值时, name 里的值应该是套嵌数组.

{ valueType: 'dependency', name: [['business', 'serviceName']] } 👈

数据处理和收集 - ConvertValue-Transform

有的时候后端返回的数据并不能直接用于表单控件, 需要先对数据进行处理. (ConvertValue)

还有的时候, 在提交表单时, 表单收集到的数据并不能直接提交给后台, 需要先对数据进行处理. (Transform)

很典型的场景就是附件上传.

// 假设有一个附件上传组件
{
title: '附件列表',
dataIndex: 'fileList',
renderFormItem: () => <FormUpload />
}
// 该组件需要接收的是一个对象数组, 对象的字段是 name 和 url.
[
{ name: '文件A', url: 'www.xx.com/xx' },
{ name: '文件B', url: 'www.xx.com/xx' },
];
// 而后端返回的数据是 fileName 和 filePath:
[
{ id: 1, fileName: '文件A', filePath: 'www.xx.com/xx' },
{ id: 2, fileName: '文件B', filePath: 'www.xx.com/xx' },
];
// 提交给后端的数据需要是 fileIds: '1,2'

Schema 中的 convertValue 和 tranform 字段就可以应对这个场景.

附件上传场景可以使用衍生组件中的 FormUpload 组件.

⭐ 数据处理和收集 - 约定式

有一类组件我们除了要获取其 value 外还要获取它的显示文本(label), 比如 Select, TreeSelect. 特别是当 Select 组件有分页时, 光靠 value 并不能回显出下拉内容, 这类场景我们可以开启组件的 labelInValue 属性来解决, 开启后组件将不再接收字符串而是需要接收一个对象 { label: string, value: string}

但是后台一般会把值分开给到前端, 比如{userId: 1, userName: 'jack'}, 前端会把值拼成对象{value: 1, label: 'jack'}给到 Select, 在表单提交时再把该对象手动拆成 {userId: 1, userName: 'jack'} 给到后端.

由于这一类场景在实际应用中还是比较常见的. RAK 想通过对dataIndex的约定来简化这一流程, 约定如下:

  • 👉 如果dataIndex中包含逗号,, RAK 会根据逗号前后的字段来自动拼接成一个对象, 提交时又会把该对象拆分. 逗号前的字段映射成 value, 逗号后的字段映射成 label.
// 比如有一个用户下拉开启了 labelInValue
{
title: '用户',
dataIndex: 'userId,userName',
valueType: 'select',
fieldProps: {
labelInValue: true,
options: [
{ value: 1, label: 'jack' },
{ value: 2, label: 'tom' },
]
}
}
// 未约定时表单拿到的值是
{'userId,userName': { value: 1, label: 'jack' }}
// 约定后值会被转化成:
{
userId: 1,
userName: 'jack'
}
// 同时在表单回显时无需对后端数据进行处理, 直接使用 initialValues 或 setFieldsValue 进行设值即能回显数据.
initialValues={{ userId: 1, userName: 'jack' }}
formRef.current?.setFieldsValue({ userId: 1, userName: 'jack' })
  • 如果组件接受对象的键值不是 value 和 label, 还可以通过下划线自定义. 比如userId,userName_id,name, RAK 会拆分下划线, 下划线后面的字段同样用逗号隔开. 以这个例子来说, 当给组件赋值{userId: 1, userName: 'jack'}时, 值会被转换成{ id: 1, name: 'jack' } 传给组件.

表单取值

SchemaForm 提供了多种方法来获取表单值. 最常用的是通过 onFinish 属性来取值.

除了 onFinish 以外还可以通过 表单实例 提供的方法来获取表单值.

我们列举了几个常用的取值方法:

这些方法在取值上有细微的差异, 理解这些差异对处理时间, 数值转化等场景会很有帮助.

简而言之, 如果你的表单中用到了时间类控件, 又或者用到了 transform, 那么请使用后三种方法.

利用 innerRef 存储额外信息

innerRef是 RAK 提供的一个工具类 ref, 里面包含了一些实用的方法可以用来简化一些特殊的场景.

比如对于表单来说有这样一个场景:

表单里有一个人员的下拉框组件, 下拉数据里除了人员信息外还携带了部门数据 deptName 和 deptId. 当选中某个人员时部门名称会被自动带出, 同时当 deptId 为 '1' 时, 部门输入框需要置灰. 可参看例子.

通常这样的需求我们会把 deptId 存在一个额外的变量里, 然后在部门输入框的 fieldProps 里做判断.

innerRef 就可以减化这一流程, 在 fieldProps 的第二个参数里默认提供了 innerRef, 可以用 innerRef.current?.setData()来存储额外的信息, 然后在其它的表单项里消费 innerRef.

innerRef 中的 setData 和 react 的 setState 一样, 只需要传入关心的字段就可以, 不会覆盖其它的字段.

对于内嵌模式 (embed), innerRef 可以传到 ProForm 上.

import { ProForm } from 'react-admin-kit';
import { useRef } from 'react'
const innerRef = useRef();
...
<ProForm
innerRef={innerRef}
...
>
...
</>

高级布局

分组 (Group)

当 valueType 为 group 时即开启分组模式. 每个 group 相当于是一个区块, columns里的内容会生成表单项.

默认情况下这些表单项是以 Space 组件包裹的. 所以你可以在 fieldProps 里传入 Space 的 api

分组 (Grid 模式)

在分组模式下同样也可以自定义表单项的布局, 即开启 grid 模式.

分组模式下布局分为两层, 外层(区块标题)和内层(columns 里的表单项), 所以 grid 的设置也分两层.

分组 (Grid 模式) 左右布局

表单数组 FormList

当 valueType 为 formList 时能够生成表单数组, 这对于收集数组信息非常有用.

比如下面的例子能够添加多个店铺.

formList 实际上是一个组件, api 见这里, 可以通过 fieldProps 给这个组件传递属性.

表单数组 Grid 排列

要让表单数组内的表单项 grid 排列, 除了开启 grid 属性以外, 表单项还需要用 valueType='group' 包裹.

表单数组样式和操作按钮自定义

还可以通过 itemRender 属性自定义样式和操作按钮.

itemRender 的参数是 ({ listDom, action }, options), 其中 listDom 是表单项合集, action 是操作按钮合集, options 是对象 {name, field, index, record, fields, operations, meta}

API

SchemaForm

属性名描述类型默认值
embed是否为内嵌模式booleanfalse
valueBaseName开启embed后处理套嵌数据结构; 在onFinish收集数据时, 会挂在该字段下. 仅适用于embed模式stringfalse
readonly是否为只读模式booleanfalse
columns表单项的配置描述;FormColumnType<any, "text">[](必选)
onFinish表单提交时的回调;(values: any) => void | Promise<boolean | void>--
formRef用于获取form实例; 请使用formRef而不要通过form属性传入一个form实例来获取实例. 因为RAK组件对form实例进行了额外的封装, 一定要通过formRef来获取.RefObject<ProFormInstance>--
innerRefRAK特有的ref, 用于存放一些工具类函数和数据.RefObject<SchemaFormInnerRefType>--
submitter提交按钮相关配置.boolean | SubmitterProps & { style: React.CSSProperties }false

除了以上这些属性, 还可以透传 Ant Design 的属性, 更多可参考 ProForm 的文档 以及 Antd Form 的文档, 以下列取一些常用的:

参数说明类型默认值
layout表单布局horizontal | vertical | inlinevertical
grid开启 grid 模式(推荐), 可参考上面的例子. 开启了 grid 后可以通过 rowProps 和 colProps 对任意表单项调整布局Booleanfalse
rowProps开启 grid 模式后传递给 Row的属性. 例如 {gutter: [16, 0]}RowProps{ gutter: 8 }
colProps开启 grid 模式后传递给全局表单项的属性. 例如 {span: 8}代表每行三项. 同时在 columns 里也可以单独指定 colProps, columns 里的 colProps 会覆盖 form 上的 colPropsColProps--
labelCol传递给表单项中 label 的属性. 如{span: 3}. 同上面的 colProps, 该属性也可以在 columns 里的 formItemProps 里单独指定. 如 {formItemProps: {labelCol: {span: 3}}}LabelColProps--

InnerRef

参数说明类型默认值
data可以存储表单中的额外数据Record<string, any>{}
setData存入数据; setData 和 react 的 setState 一样, 只需要传入关心的字段就可以, 不会覆盖其它的字段.(Record<string, any>) => void--

Column

字段名称说明类型
key确定这个列的唯一值, 一般用于 dataIndex 重复的情况React.key
dataIndex可使用约定模式自动处理值: userId, userName 或 userId, userName_id, name; 可参考约定式例子string
valueType数据的渲渲染方式,我们自带了一部分,你也可以自定义 valueTypeProFieldValueType
title标题的内容,在 form 中是 labelReactNode | (props,type,dom)=> ReactNode
tooltip会在 title 旁边展示一个 icon,鼠标浮动之后展示string
valueEnum支持 object 和 Map,Map 是支持其他基础类型作为 key(Entity)=> ValueEnum | ValueEnum
fieldProps传给渲染的组件的 props,自定义的时候也会传递(form,config)=>fieldProps | fieldProps
formItemProps传递给 Form. Item 的配置(form,config)=>formItemProps | formItemProps
readonly只读模式; 优先级更高boolean
render只读模式下自定义显示. render 方法只管理只读模式,编辑模式需要使用 renderFormItem(dom,record,index, action, schema) => React.ReactNode
renderFormItem自定义编辑模式, 返回一个 ReactNode,会自动包裹 value 和 onChange(schema,config,form) => React.ReactNode
hideInForm在 Form 中隐藏boolean
rowProps在开启 grid 模式时传递给 RowRowProps
colProps在开启 grid 模式时传递给 ColColProps
required表单是否必选boolean
选择1
-
-
-
-
hi undefined

无初始值

有初始值

基本信息
业务信息
请选择
请选择
请选择
表单实例方法描述备注来源
 
 
 
 
getFieldsValue获取一组或所有的表单值.-antd form
validateFields表单验证通过后得到所有的表单值-antd form
getFieldsFormatValue获取一组或所有转化后的值会根据 valueType 处理时间格式, 会处理 transformproComponent
onFinish同上同上proComponent
validateFieldsReturnFormatValue表单验证通过后得到所有的转化后的值同上proComponent
 
请选择
表单实例方法结果
 
 
getFieldsValue{ "tags": [ "tag01", "tag02" ], "validDate": [ "dayjs对象", "dayjs对象" ] }
validateFields"同上"
getFieldsFormatValue{ "tagIds": "tag01,tag02", "validDate": [ "2024-10-01", "2024-10-07" ] }
onFinish"同上"
validateFieldsReturnFormatValue"同上"
请选择
基本信息
额外信息
请选择
基本信息
额外信息
请选择
基本信息
额外信息
请选择
请选择
1