列表表单是与可编辑表格相同的功能,虽然使用频次不多,但是每次写起来都相当麻烦,虽然 antd 提供了 Form.List, 但是其就像毛坯房,每次开发都要精装修一番。所以我们 ProForm 也对其进行了标准化。

在我们的项目中我们一般都需要事先一个这样的列表,接下来我们就带着大家一起来一起实现一下这个需求,顺便展示一下 ProForm 的优越性。
🔱 基本开发
首先我们需要准备一个 ProForm,看起来是这样的:
import React from "react";import ProForm from "@ant-design/pro-form";export default () => (<ProFormonFinish={async (values) => {console.log("Received values of form:", values);}}></ProForm>);
ProForm 的 onFinish 必须是一个的 Promise,ProForm 会根据 Promise 的状态来进行提交按钮的加载状态控制,为了简单起见 我们可以直接使用 async 关键字。
接下来我们需要设置 ProFormList,ProFormList 与 普通的 Field 用法基本相同,也需要配置 name 和 label。
import React from 'react';import ProForm, { ProFormList, ProFormText } from '@ant-design/pro-form';export default () => (<ProFormonFinish={async (values) => {console.log('Received values of form:', values);}}><ProFormList name="users" label="用户信息"><ProFormText name="labels" label="姓名" /></ProFormList></ProForm>);
我们会得到如下的界面,点击添加一行,会出现一个文本框。
与 antd 不同的是,由于 ProForm 设计的时候把 field 和 组件做了封装,所以我们不需要专门去绑定 name,只需要将其放到 children 中就可以自动绑定,同时还会根据组件的不同自动格式化数据。
接下来我们根据设计图,增加不同的组件。同时引入 ProFormGroup 来进行排版,最后的代码是这样的:
import React from 'react'import ProForm, {ProFormList,ProFormDatePicker,ProFormText,ProFormGroup,ProFormFieldSet,ProFormSelect,} from '@ant-design/pro-form'export default () => (<ProFormonFinish={async (values) => {console.log('Received values of form:', values)}}><ProFormList name='users' label='用户信息'><ProFormGroup><ProFormTextname='name'label='姓名'rules={[{required: true,},]}/><ProFormText name='nickName' label='昵称' /><ProFormSelectlabel='性别'name='sex'width='xs'valueEnum={{man: '男性',woman: '女性',}}/><ProFormDatePicker name='birth' label='出生日期' /><ProFormFieldSet name='addr' label='地址'><ProFormSelectvalueEnum={{taiyuan: '山西',hangzhou: '杭州',}}/><ProFormSelectvalueEnum={{changfeng: '长风街',gongzhuan: '工专路',}}/></ProFormFieldSet></ProFormGroup></ProFormList></ProForm>)
这里,我就基本完成了一个 FormList 的开发:

如果我们需要默认值,可以通过 initialValue 来注入,如果需要新建一行的默认值,可以通过 creatorRecord 来配置。
initialValue 只会在初始化的时候生效,并且与 initialValues 冲突,如果存在优先使用 initialValues 。
💠 互相依赖的表单
表单的中的互相依赖总是很常见的,但是在 FormList 中的依赖总是很麻烦,我们希望依赖当前行的组件就需要计算当前第几行,同时计算出相应的 name,再用 getFieldValue 获取到相应的值。ProForm 中为这种情况提供了一个快捷方式。ProFormDependency 会自动根据当前行帮你自动计算相关的值。
<ProFormListname='users'label='用户信息'initialValue={[{name: '1111',},]}><ProFormText name='nickName' label='昵称' /><ProFormDependency name={['nickName']}>{({ nickName }) => {if (!nickName) {return null}return <ProFormText name='names' label='昵称详情' />}}</ProFormDependency></ProFormList>
当然如果你想监听表单的值,不是监听的 ProFormList 中的,也可以配置 ignoreFormListField ,配置为 true 之后 ProFormDependency 就会忽略 ProFormList 的影响。
🎎 更复杂的样式
ProFormList 自带了一个新增按钮和两个操作按钮,为了满足更多人的需求这些自带的按钮都支持自自定义。
creatorButtonProps提供了新建一行相关的配置,如果不需要可以配置 false,直接不显示按钮。actionRender可以自定义操作按钮, 此方法要求返回一个数组, ProForm 会自动为其增加间距。itemRender可以自定义整个子项,如果你想把 ProFormList 子项渲染为一个 Card 或其他样式可以选择这个 API 。
creatorButtonProps 新建一行配置
/** 不显示新建一行按钮 */<ProFormList name="users" label="用户信息" creatorButtonProps={false}/>/** 新建一行按钮放在顶部 */<ProFormListname='users'label='用户信息'creatorButtonProps={{position: 'top',}}/>/** 自定义新建按钮文案 */<ProFormListname='users'label='用户信息'creatorButtonProps={{position: 'top',creatorButtonText: '在顶部新建一行',}}/>/** 新建按钮改为 antd 的主色 */<ProFormListname='users'label='用户信息'creatorButtonProps={{type: 'primary',}}/>
actionRender 自定义操作按钮
/** 调换操作按钮的位置 */<ProFormListactionRender={(props, action, defaultActionDom) => {return defaultActionDom.reverse()}}name='users'label='用户信息'/>/** 增加一个新建按钮 */<ProFormListactionRender={(props, action, defaultActionDom) => {return [<PlusCircleOutlined key='add' onClick={() => action.add()} />, ...defaultActionDom]}}name='users'label='用户信息'/>/** 完全自定义 */<ProFormListactionRender={(props, action) => {return [<Buttonkey='delete'onClick={() => {action.remove(props.key)}}>删除</Button>,]}}name='users'label='用户信息'/>
itemRender 渲染列表项
/** 子项渲染为卡片 */<ProFormListname='columns'itemRender={({ listDom, action }) => {return (<ProCardborderedstyle={{marginBottom: 8,position: 'relative',}}extra={action}>{listDom}</ProCard>)}}/>/** 完全重现渲染 action */<ProFormListname='columns'itemRender={({ listDom }, { field, record, operation }) => {return (<ProCardtitle={record.title}borderedstyle={{marginBottom: 8,position: 'relative',}}extra={<Button onClick={() => operation.remove(field.key)}>删除</Button>}>{listDom}</ProCard>)}}/>
