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

image.png

在我们的项目中我们一般都需要事先一个这样的列表,接下来我们就带着大家一起来一起实现一下这个需求,顺便展示一下 ProForm 的优越性。

🔱 基本开发

首先我们需要准备一个 ProForm,看起来是这样的:

  1. import React from "react";
  2. import ProForm from "@ant-design/pro-form";
  3. export default () => (
  4. <ProForm
  5. onFinish={async (values) => {
  6. console.log("Received values of form:", values);
  7. }}
  8. ></ProForm>
  9. );

ProForm 的 onFinish 必须是一个的 Promise,ProForm 会根据 Promise 的状态来进行提交按钮的加载状态控制,为了简单起见 我们可以直接使用 async 关键字。

接下来我们需要设置 ProFormList,ProFormList 与 普通的 Field 用法基本相同,也需要配置 name 和 label。

  1. import React from 'react';
  2. import ProForm, { ProFormList, ProFormText } from '@ant-design/pro-form';
  3. export default () => (
  4. <ProForm
  5. onFinish={async (values) => {
  6. console.log('Received values of form:', values);
  7. }}
  8. >
  9. <ProFormList name="users" label="用户信息">
  10. <ProFormText name="labels" label="姓名" />
  11. </ProFormList>
  12. </ProForm>
  13. );

我们会得到如下的界面,点击添加一行,会出现一个文本框。
image.png

与 antd 不同的是,由于 ProForm 设计的时候把 field 和 组件做了封装,所以我们不需要专门去绑定 name,只需要将其放到 children 中就可以自动绑定,同时还会根据组件的不同自动格式化数据。

接下来我们根据设计图,增加不同的组件。同时引入 ProFormGroup 来进行排版,最后的代码是这样的:

  1. import React from 'react'
  2. import ProForm, {
  3. ProFormList,
  4. ProFormDatePicker,
  5. ProFormText,
  6. ProFormGroup,
  7. ProFormFieldSet,
  8. ProFormSelect,
  9. } from '@ant-design/pro-form'
  10. export default () => (
  11. <ProForm
  12. onFinish={async (values) => {
  13. console.log('Received values of form:', values)
  14. }}>
  15. <ProFormList name='users' label='用户信息'>
  16. <ProFormGroup>
  17. <ProFormText
  18. name='name'
  19. label='姓名'
  20. rules={[
  21. {
  22. required: true,
  23. },
  24. ]}
  25. />
  26. <ProFormText name='nickName' label='昵称' />
  27. <ProFormSelect
  28. label='性别'
  29. name='sex'
  30. width='xs'
  31. valueEnum={{
  32. man: '男性',
  33. woman: '女性',
  34. }}
  35. />
  36. <ProFormDatePicker name='birth' label='出生日期' />
  37. <ProFormFieldSet name='addr' label='地址'>
  38. <ProFormSelect
  39. valueEnum={{
  40. taiyuan: '山西',
  41. hangzhou: '杭州',
  42. }}
  43. />
  44. <ProFormSelect
  45. valueEnum={{
  46. changfeng: '长风街',
  47. gongzhuan: '工专路',
  48. }}
  49. />
  50. </ProFormFieldSet>
  51. </ProFormGroup>
  52. </ProFormList>
  53. </ProForm>
  54. )

这里,我就基本完成了一个 FormList 的开发:

image.png
如果我们需要默认值,可以通过 initialValue 来注入,如果需要新建一行的默认值,可以通过 creatorRecord 来配置。

initialValue 只会在初始化的时候生效,并且与 initialValues 冲突,如果存在优先使用 initialValues 。

💠 互相依赖的表单

表单的中的互相依赖总是很常见的,但是在 FormList 中的依赖总是很麻烦,我们希望依赖当前行的组件就需要计算当前第几行,同时计算出相应的 name,再用 getFieldValue 获取到相应的值。ProForm 中为这种情况提供了一个快捷方式。ProFormDependency 会自动根据当前行帮你自动计算相关的值。

  1. <ProFormList
  2. name='users'
  3. label='用户信息'
  4. initialValue={[
  5. {
  6. name: '1111',
  7. },
  8. ]}>
  9. <ProFormText name='nickName' label='昵称' />
  10. <ProFormDependency name={['nickName']}>
  11. {({ nickName }) => {
  12. if (!nickName) {
  13. return null
  14. }
  15. return <ProFormText name='names' label='昵称详情' />
  16. }}
  17. </ProFormDependency>
  18. </ProFormList>

当然如果你想监听表单的值,不是监听的 ProFormList 中的,也可以配置 ignoreFormListField ,配置为 true 之后 ProFormDependency 就会忽略 ProFormList 的影响。

🎎 更复杂的样式

ProFormList 自带了一个新增按钮和两个操作按钮,为了满足更多人的需求这些自带的按钮都支持自自定义。

  • creatorButtonProps 提供了新建一行相关的配置,如果不需要可以配置 false,直接不显示按钮。
  • actionRender 可以自定义操作按钮, 此方法要求返回一个数组, ProForm 会自动为其增加间距。
  • itemRender 可以自定义整个子项,如果你想把 ProFormList 子项渲染为一个 Card 或其他样式可以选择这个 API 。

creatorButtonProps 新建一行配置

  1. /** 不显示新建一行按钮 */
  2. <ProFormList name="users" label="用户信息" creatorButtonProps={false}/>
  3. /** 新建一行按钮放在顶部 */
  4. <ProFormList
  5. name='users'
  6. label='用户信息'
  7. creatorButtonProps={{
  8. position: 'top',
  9. }}
  10. />
  11. /** 自定义新建按钮文案 */
  12. <ProFormList
  13. name='users'
  14. label='用户信息'
  15. creatorButtonProps={{
  16. position: 'top',
  17. creatorButtonText: '在顶部新建一行',
  18. }}
  19. />
  20. /** 新建按钮改为 antd 的主色 */
  21. <ProFormList
  22. name='users'
  23. label='用户信息'
  24. creatorButtonProps={{
  25. type: 'primary',
  26. }}
  27. />

actionRender 自定义操作按钮

  1. /** 调换操作按钮的位置 */
  2. <ProFormList
  3. actionRender={(props, action, defaultActionDom) => {
  4. return defaultActionDom.reverse()
  5. }}
  6. name='users'
  7. label='用户信息'
  8. />
  9. /** 增加一个新建按钮 */
  10. <ProFormList
  11. actionRender={(props, action, defaultActionDom) => {
  12. return [<PlusCircleOutlined key='add' onClick={() => action.add()} />, ...defaultActionDom]
  13. }}
  14. name='users'
  15. label='用户信息'
  16. />
  17. /** 完全自定义 */
  18. <ProFormList
  19. actionRender={(props, action) => {
  20. return [
  21. <Button
  22. key='delete'
  23. onClick={() => {
  24. action.remove(props.key)
  25. }}>
  26. 删除
  27. </Button>,
  28. ]
  29. }}
  30. name='users'
  31. label='用户信息'
  32. />

itemRender 渲染列表项

  1. /** 子项渲染为卡片 */
  2. <ProFormList
  3. name='columns'
  4. itemRender={({ listDom, action }) => {
  5. return (
  6. <ProCard
  7. bordered
  8. style={{
  9. marginBottom: 8,
  10. position: 'relative',
  11. }}
  12. extra={action}>
  13. {listDom}
  14. </ProCard>
  15. )
  16. }}
  17. />
  18. /** 完全重现渲染 action */
  19. <ProFormList
  20. name='columns'
  21. itemRender={({ listDom }, { field, record, operation }) => {
  22. return (
  23. <ProCard
  24. title={record.title}
  25. bordered
  26. style={{
  27. marginBottom: 8,
  28. position: 'relative',
  29. }}
  30. extra={<Button onClick={() => operation.remove(field.key)}>删除</Button>}>
  31. {listDom}
  32. </ProCard>
  33. )
  34. }}
  35. />