一、表格 Column 的数据格式
我们需要这个Column的属性来分别去渲染搜索表单和表格,这里参考了一下 proColumn 中,表格 Column 的常用属性。
- search: 是否需要搜索,默认为true;
- hideInTable: 是否在表格内不展示,默认为 false;
- type: 搜索的类型,这里可以在做搜索表单时自定义,如 'date' 为日期搜索, 'select'为下拉搜索;
- valueEnum: 下拉搜索的选项;
二、基础表格的实现
首先要明确实现什么样的功能,对于表格数据的展示来说,必不可少的就是表格列、表格的数据、是否分页、是否每行可选、表格加载数据的 loading 等等基本的功能,那同时,我们也在表格上方留出一部分作为工具区域,放置一些其他的按钮。
基于我们所需的这些基本的功能,我们就可先做出一个基本的表格来。
const {toolBar = [], optional, isCheck. ...} = props;
<div>
{toolBar.map((item) =>
// 暂时没考虑按钮发生改变
React.cloneElement(item, { key: item.index, className: styles.toolButton }, item.props.children),
)}
<Table
pagination={optional ? paginationProps : false}
rowSelection={isCheck ? rowSelection : undefined}
rowKey="id"
...
/>
</div>
toolBar 即上面所说到的,表格上方的工作区,optional、isCheck 分别判断是否需要分页和选中列的功能。
使用 React.cloneElement 可以对每个元素混入props,这里是加了样式让按钮之间存在距离。
三、搜索表单的实现
和表格一样,先要明确我们需要什么,对于表单来说,我们需要的就是搜索项、搜索的方法、以及根据上文提到的Column的各种属性来去渲染,另外,除了搜索和重置的功能外,再留出自定义按钮的部分。
1、根据 type 动态匹配搜索类型
const getInput = (item) => {
switch (item.type) {
case 'select':
return ...;
case 'date':
return ...;
default:
return <Input placeholder="请输入" allowClear className={styles.inputWidth} />;
}
};
这里我们用一个方法来根据 type 去动态判断他是什么类型的搜索,这里我用的 switch case,默认的情况下就是输入框即可。 至于自定义的部分,只要在查询重置按钮后面,遍历即可,这里我用的 children 来传值。
<Form form={form} layout="inline" onFinish={handleSubmit} className={styles.basicForm}>
{searchFormList.map((item) => (
<Form.Item name={item.dataIndex} key={item.dataIndex} label={item.title}>
{getInput(item)}
</Form.Item>
))}
<Form.Item>
<Button className={styles.searchButton} type="primary" htmlType="submit">
查询
</Button>
<Button onClick={handleResetForm} className={styles.searchButton}>
重置
</Button>
</Form.Item>
<div className={styles.extraButton}>
{children.map((item: any) => (
<Form.Item key={item.index}>{item}</Form.Item>
))}
</div>
</Form>
这里直接遍历父组件传来的搜索表单列表,通过判断类型的方法去进行动态渲染。
四、整合
最后我们将表单和表格整合即可,搜索表单的list我们过滤掉 search 为 false 的无需搜索项和key为'action'的操作列,表格展示过滤掉 hideInTable 属性为 true 的隐藏项。然后分别传值就好。
// 搜索项
const searchColum = colums.filter((item) => item.search !== false && item.key !== 'action');
// 展示列
const tableColum = colums.filter((item) => !item.hideInTable);
写到这里又有一个新的问题,我们没有重置页面数据的方法暴露出去,如果进行删除或者更新等操作,在外面是没办法及进行刷新的。由于这个组件使用函数式组件进行编写的,我们使用 forwardRef 搭配 useImperativeHandle 就可以暴露外部需要访问的方法。
...
// 重置页面
const resetPage = (type: 'delete' | 'update' | 'reset' = 'reset') => {
let current = page.page; // 当前页
switch (type) {
// 如果是删除且是本页最后一条,则重置取上一页
case 'delete':
current = listData.data.length === 1 ? page.page - 1 : page.page;
break;
// 整体重置回到第一页
case 'reset':
current = 1;
break;
default:
break;
}
changePage({ current, pageSize: page.page_size });
};
// 暴露重置页面数据方法
seImperativeHandle(ref, () => ({
resetPage,
}));
...
export default forwardRef(BasicPage);
另外,对于自定义按钮的部分,我采用的是数组的形式来传值。
const formTool = [<Button type='primary' key="btn">自定义</Button>, ...]
最后看下大致的效果