列表表单是与可编辑表格相同的功能,虽然使用频次不多,但是每次写起来都相当麻烦,虽然 antd 提供了 Form.List, 但是其就像毛坯房,每次开发都要精装修一番。所以我们 ProForm 也对其进行了标准化。
在我们的项目中我们一般都需要事先一个这样的列表,接下来我们就带着大家一起来一起实现一下这个需求,顺便展示一下 ProForm 的优越性。
🔱 基本开发
首先我们需要准备一个 ProForm,看起来是这样的:
import React from "react";
import ProForm from "@ant-design/pro-form";
export default () => (
<ProForm
onFinish={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 () => (
<ProForm
onFinish={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 () => (
<ProForm
onFinish={async (values) => {
console.log('Received values of form:', values)
}}>
<ProFormList name='users' label='用户信息'>
<ProFormGroup>
<ProFormText
name='name'
label='姓名'
rules={[
{
required: true,
},
]}
/>
<ProFormText name='nickName' label='昵称' />
<ProFormSelect
label='性别'
name='sex'
width='xs'
valueEnum={{
man: '男性',
woman: '女性',
}}
/>
<ProFormDatePicker name='birth' label='出生日期' />
<ProFormFieldSet name='addr' label='地址'>
<ProFormSelect
valueEnum={{
taiyuan: '山西',
hangzhou: '杭州',
}}
/>
<ProFormSelect
valueEnum={{
changfeng: '长风街',
gongzhuan: '工专路',
}}
/>
</ProFormFieldSet>
</ProFormGroup>
</ProFormList>
</ProForm>
)
这里,我就基本完成了一个 FormList 的开发:
如果我们需要默认值,可以通过 initialValue
来注入,如果需要新建一行的默认值,可以通过 creatorRecord
来配置。
initialValue 只会在初始化的时候生效,并且与 initialValues 冲突,如果存在优先使用 initialValues 。
💠 互相依赖的表单
表单的中的互相依赖总是很常见的,但是在 FormList 中的依赖总是很麻烦,我们希望依赖当前行的组件就需要计算当前第几行,同时计算出相应的 name,再用 getFieldValue 获取到相应的值。ProForm 中为这种情况提供了一个快捷方式。ProFormDependency 会自动根据当前行帮你自动计算相关的值。
<ProFormList
name='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}/>
/** 新建一行按钮放在顶部 */
<ProFormList
name='users'
label='用户信息'
creatorButtonProps={{
position: 'top',
}}
/>
/** 自定义新建按钮文案 */
<ProFormList
name='users'
label='用户信息'
creatorButtonProps={{
position: 'top',
creatorButtonText: '在顶部新建一行',
}}
/>
/** 新建按钮改为 antd 的主色 */
<ProFormList
name='users'
label='用户信息'
creatorButtonProps={{
type: 'primary',
}}
/>
actionRender
自定义操作按钮
/** 调换操作按钮的位置 */
<ProFormList
actionRender={(props, action, defaultActionDom) => {
return defaultActionDom.reverse()
}}
name='users'
label='用户信息'
/>
/** 增加一个新建按钮 */
<ProFormList
actionRender={(props, action, defaultActionDom) => {
return [<PlusCircleOutlined key='add' onClick={() => action.add()} />, ...defaultActionDom]
}}
name='users'
label='用户信息'
/>
/** 完全自定义 */
<ProFormList
actionRender={(props, action) => {
return [
<Button
key='delete'
onClick={() => {
action.remove(props.key)
}}>
删除
</Button>,
]
}}
name='users'
label='用户信息'
/>
itemRender
渲染列表项
/** 子项渲染为卡片 */
<ProFormList
name='columns'
itemRender={({ listDom, action }) => {
return (
<ProCard
bordered
style={{
marginBottom: 8,
position: 'relative',
}}
extra={action}>
{listDom}
</ProCard>
)
}}
/>
/** 完全重现渲染 action */
<ProFormList
name='columns'
itemRender={({ listDom }, { field, record, operation }) => {
return (
<ProCard
title={record.title}
bordered
style={{
marginBottom: 8,
position: 'relative',
}}
extra={<Button onClick={() => operation.remove(field.key)}>删除</Button>}>
{listDom}
</ProCard>
)
}}
/>