antd 高级组件 ProComponents 之 高级表格 ProTable
- 一般后台管理系统,大部分页面功能都是列表和表单的形式。即便使用了组件<Table />、<Form />等,依旧需要写大量高度重复性的代码,比如列表页通常会有筛选栏、操作栏、表格区域、和分页栏四个部分,新增/编辑页通常有N+表单项(eg: 输入框、下拉框、级联框、单选按钮、多选按钮等等)。之前也考虑将高重复性的代码抽离成组件,通过配置参数,来实现相对应的功能。在组件设计阶段,将组件的灵活性和易用性作为首要目标(当然终极目标肯定是要实现基础功能,不然做组件的意义何在),因此将每一个模块作为单独的组件,最终实现了筛选栏、操作栏、表单项三个组件,后来随着业务需求的增加,也在不断扩展组件的能力,目前相对比较成熟,可以满足基础的列表/表单页面功能。
- 最近在做 react 项目,发现 antd 的 ProComponents 挺有意思的,ProComponents 提供了更高程度的抽象,很适合于不需要过多设计的中后台类应用,ProComponents 的设计思路是「一个状态加一系列行为」「一个组件≈一个页面」。
- 一个状态加一系列行为:开发者只需要定义一个状态,重型组件会自动生成一系列行为。以 table 为例,需要有一个状态 dataSource 来存储请求的数据,为了更好的体验,还需要一个 loading,这样最基础的功能就有一系列的行为,需要先设置
loading=true
,然后发送请求,请求完成后设置dataSource
和loading=false
,再加上分页、搜索等功能,行为就更加繁杂,而这系列行为绝大部分属于重复性工作。我们来看一下 ProTable 对此做了什么工作,ProTable 通过抽象出一个 request 的 API,将 dataSource 和 loading 等状态以及其行为进行封装,这种封装模式可以让前端减少重复性工作,专注于业务开发。 - 一个组件≈一个页面:重型组件的思想是提供页面级别的抽象。还是以 table 为例,一个列表页,需要有筛选相关的表单组件、表格组件、加载中组件等等。而 ProTable 一个组件满足以上所有功能,支持接口请求和自动生成筛选表单。
- 一个状态加一系列行为:开发者只需要定义一个状态,重型组件会自动生成一系列行为。以 table 为例,需要有一个状态 dataSource 来存储请求的数据,为了更好的体验,还需要一个 loading,这样最基础的功能就有一系列的行为,需要先设置
- 不过使用 ProComponents 的前提是,没有个性化特别强的需求,虽然说 ProTable 支持完全降级为 table 使用,但会有一种“杀鸡用牛刀”的感觉,而且还很增加开发人员的理解成本,没有必要。
ProTable
1. 常用API
(1)request:ProTable 最重要的 API,request
会接收一个对象。对象中必须要有 data
和 success
,如果需要手动分页 total
也是必需的。request
会接管 loading
的设置,同时在查询表单查询和 params
参数发生修改时重新执行。同时 查询表单的值和 params
参数也会带入。
(2)columns:表格列的配置描述。
(3)toolBarRender:渲染工具栏,支持返回一个 dom 数组
,会自动增加 margin-right
。
(4)search:是否显示搜索表单,传入对象时为搜索表单的配置。
(5)actionRef:Table action
的引用,便于自定义触发。
(6)formRef:可以获取到查询表单的 form
实例,用于一些灵活的配置。
(7)rowKey:表格行 key 的取值,可以是字符串或一个函数。
(8)scroll:表格是否可滚动,也可以指定滚动区域的宽、高,参考 配置项 文档。
(9)pagination:分页器,设为 false 时不展示和进行分页,参考 配置项 或 pagination 文档。
(10)options:table 工具栏,设为 false 时不显示,传入 function 会点击时触发({{ density?: boolean, fullScreen: boolean | function, reload: boolean | function, setting: boolean |
SettingOptionType }}
)。
(11)editable:可编辑表格的相关配置。
示例:
<ProTable
columns={columns}
actionRef={actionRef}
options={false}
request={async (params: any = {}, sort, filter) => {
const data = {
...params,
page: params.current,
per_page: params.pageSize
};
const res: any = await companyList(data);
return {
data: res.data.data,
success: res?.code === 1,
total: +res.data.total
};
}}
rowKey="id"
search={{
labelWidth: 100,
defaultCollapsed: false
}}
scroll={{ x: "100%", y: "calc(100vh - 300px)" }}
pagination={{
showSizeChanger: true
}}
toolBarRender={() => [
<Search placeholder="请输入关键字"></Search>,
<Button type="primary" onClick={handleExportAll}>
导出全部
</Button>
]}
/>;
2. Search 搜索表单配置
(1)labelWidth:标签的宽度,可以设置具体的数值,也可以设置为auto
,根据标签字数进行调整。
(2)defaultCollapsed:默认是否收起。
(3)span:配置查询表单的列数,可以设置具体的数值,也可以设置为'ColConfig'
类型(defaultColConfig = { xs:24, sm:24, md:12, lg:12, xl:8, xxl:6 }
)。
(4)optionRender:自定义操作栏。
(5)showHiddenNum:是否显示收起之后显示隐藏个数。
(6)filterType:过滤表单类型('query'
| 'light'
),默认使用'query'
。
3. Columns 列定义
(1)title:列头显示文字。
(2)dataIndex:列数据在数据项中对应的路径,支持通过数组查询嵌套路径。
(3)width:列宽度。
(4)fixed:列是否固定,可选 true (等效于 left) left
right
。
(5)valueType:值的类型,会生成不同的渲染器。(后面详细说一下valueType
)
(6)valueEnum:值的枚举,支持传入一个 Object
或者是 Map
,它会自动转化把值当成 key 来取出要显示的内容,可以配合 valueType 使用。
(7)order:查询表单中的权重,权重大排序靠前。
(8)search:配置列的搜索相关,false 为隐藏。
(9)editable:在编辑表格中是否可编辑的,函数的参数和 table 的 render 一样。
(10)hideInSearch:在查询表单中不展示此项。
(11)hideInTable:在 Table 中不展示此列。
(12)request:从服务器请求枚举。
(13)render:类似 table 的 render,第一个参数变成了 dom,增加了第四个参数 action。
示例:
const columns = [
{
title: "单位简称",
dataIndex: "short_name",
search: false,
width: "100px"
},
{
title: "是否名企",
dataIndex: "famous",
hideInTable: true,
valueType: "select",
valueEnum: {
0: { text: "普通单位" },
1: { text: "名企" }
}
},
{
title: "操作",
width: "140px",
fixed: "right",
valueType: "option",
render: (text: string, row: any) => (
<Space>
<Button
type="link"
size="small"
onClick={() => history.push(`?id=${row.id}`)}
>
编辑
</Button>
<Button type="link" size="small" onClick={() => setRemark(row)}>
备注
</Button>
</Space>
)
}
];
4. valueType 列表
valueType 是 ProComponents 的灵魂,ProComponents 会根据 valueType 来映射成不同的表单项。
ProTable 封装了一些常用的值类型来减少重复的 render
操作,配置一个 valueType
即可展示格式化响应的数据。
ProTable 中常用的:
(1)date:日期。
(2)dateTime:日期时间。
(3)dateRange:日期区间。
(4)dateTimeRange:日期时间区间。
(5)text:文本框。
(6)select:下拉框。
(7)treeSelect:树形下拉框。
(8)cascader:级联选择器。
valueType 也支持自定义,参考 demo。
除了可以使用 toolBarRender 渲染工具栏,还可以使用 toolbar 灵活定义工具栏,包括标题、描述、搜索、操作、设置等配置。
6. editable 编辑行配置
(1)type:可编辑表格的类型,单行编辑或者多行编辑,single
| multiple
。
(2)editableKeys:正在编辑的行,受控属性。 默认 key
会使用 rowKey
的配置,如果没有配置会使用 index
,建议使用 rowKey。
(3)onChange:行数据被修改的时候触发。
(4)onSave:保存一行的时候触发。
(5)onDelete:删除一行的时候触发。
(6)onCancel:取消编辑一行时触发。
(7)actionRender:自定义编辑模式的操作栏,默认提供了三种操作:保存,删除 和 取消。
示例:
editable={{
type: "multiple",
actionRender: (row, config, defaultDom) => [
defaultDom.save,
defaultDom.cancel
]
//onSave: (_, row: any) => editData(row)
}}
7. recordCreatorProps 新建行配置
(1)position:添加的位置,顶部添加还是末尾添加,top
| bottom
。
(2)record:新增的记录,需要设置 rowKey,不写的话,会使用 index 当行 id。
(3)style:按钮的样式设置,可以设置按钮是否显示,这样可以做最大行限制和最小行限制之类的功能。
示例:
recordCreatorProps={{
position: "top",
record: () => ({ id: (Math.random() * 1000000).toFixed(0) })
}}
8. ActionRef 自定义触发(TODO:后面有时间再补充)
遇到的问题
1. 引入组件的时候按需引入即可,原因主要有两个:一个是整个包引入后体积比较大,一个是整包引入后有些地方的国际化会有点问题。
// 按需引入
"@ant-design/pro-card": "^1.20.11",
"@ant-design/pro-descriptions": "^1.11.7",
"@ant-design/pro-form": "^1.70.0",
"@ant-design/pro-table": "2.77.0",
// 整包引入
"@ant-design/pro-components": "^1.1.7",
2. 在360等浏览器中打开,react 报错,解决方案如下:
// public/index.html
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
// src/index.tsx
import "core-js/es";
import "react-app-polyfill/ie9";
import "react-app-polyfill/stable";
// package.json
{
"dependencies": {
"core-js": "^3.23.3",
"react-app-polyfill": "^3.0.0",
},
"browserslist": {
"production": [
"not op_mini all",
"ie > 9"
],
"development": [
"ie > 9"
]
}
}