使用hook封装表格常用功能(react)
实现内容
- 配置分页:usePagination
- 生成过滤项:useFilter
- 获取表格选择配置:useSelect
- 生成批量删除按钮:useDelete
- 生成模态框:useModal
基于react@18.2.0,antd@5.12.5
示例
render部分:
<React.Fragment>
<Form layout="inline">
{DeleteEle}
{FilterEles}
</Form>
<Table
{...{
columns,
dataSource: list || [],
rowSelection,
pagination: paginationConfig,
rowKey: 'inform_uuid',
}}
/>
{ModalEle}
</React.Fragment>
效果:
完整实例
import React, { useState, useEffect } from 'react';
import { Form, Table, Tag } from 'antd';
import Detail from './Detail'; // 详情弹框的内容
import { LIST_RES } from '../store'; // 模拟请求数据
import TableGenerator from '../TableGenerator'; // 封装的钩子
const { useFilter, usePagination, useSelect, useDelete, useModal } =
TableGenerator;
const TestTable = () => {
// 1. 生成过滤项。
// 传入配置,可以自行扩展。目前支持输入框、日期范围选择器、普通选择器
// FilterEles:过滤项表单项
// filterRefresh:暴露出来告诉外层需要重新获取数据了
const { FilterEles, filter, filterRefresh } = useFilter([
{
name: 'name', // 字段
label: '平台名称', // placeholder
type: 'inputString', // 组件类型:字符串输入框
},
{
name: 'status',
label: '状态',
type: 'select', // 组件类型:选择器
initValue: 0, // 默认数据(可选)
options: [
{ value: 0, label: '全部状态' },
{ value: 1, label: '成功' },
{ value: 2, label: '失败' },
],
},
{
name: ['start_date', 'end_date'],
label: ['开始日期', '结束日期'],
type: 'dateRange', // 组件类型:日期选择器
},
]);
// 2. 获取分页配置。
// pagination:{page,pageSize,total}
// pageRefresh:暴露出来告诉外层需要重新获取数据了
// paginationConfig:作为表格的分页属性
const { pagination, setPagination, pageRefresh, paginationConfig } =
usePagination();
// 3. 表格选中配置
// selectedKeys: Array<string>
// rowSelection: 作为表格的选择属性
const { selectedKeys, setSelectedKeys, rowSelection } = useSelect();
// 4. 配置模态框。
// 传入数组,用于配置所有弹框类型(详情、添加、编辑等),目前只支持详情,可自行扩展
// ModalEle: 模态框组件
// modal: {isShow:boolean, action:string, record:any}
const { ModalEle, modal, setModal } = useModal([
{
title: '详情', // 弹框标题
action: 'detail', // 弹框类型
getComponent: (record) => <Detail data={record} />, // 弹框数据
// ... 可以继续添加属性,其他属性将会应用到Modal中 比如footer、handleOk
},
]);
// 5. 批量删除按钮,需要搭配useSelect使用
const [DeleteEle] = useDelete({
url: 'ajax/deleteItem.json',
data: {
uuids: selectedKeys,
},
success: () => { // 可选,删除成功后还需要调用的内容
setSelectedKeys([]);
getList();
},
keys: selectedKeys, // 用于判断按钮是否可点击
});
const [list, setList] = useState([]);
const getList = () => {
// 请求数据, 可传入filter和pagination作为参数
Ajax.ajax({
url: 'GetSmsInformList',
data: {
page: pagination.page,
pageSize: pagination.pageSize,
filter
},
success: (data) => {
setList(data.list);
setPagination({
...pagination,
page: data.page,
total: data.total
});
}
});
// 模拟数据
// const data = LIST_RES.data;
// setList(data.list);
// setPagination({
// ...pagination,
// page: data.page,
// total: data.total,
// });
};
// 触发刷新:修改page、pageSize、点击查询、重置时将会触发
useEffect(() => {
getList();
}, [pageRefresh, filterRefresh]);
const columns = [
{
title: '序号',
width: 50,
align: 'center',
render: (v, r, index) =>
index + 1 + (pagination.page - 1) * pagination.pageSize,
},
{
title: '平台名称',
width: 180,
dataIndex: 'req_psname',
},
{
title: '请求时间',
width: 140,
dataIndex: 'inform_time',
},
{
title: '通知对象',
width: 180,
dataIndex: 'inform_pnumbers',
render: (v) => v.join(', '),
},
{
title: '通知内容',
width: 200,
dataIndex: 'inform_content',
},
{
title: '通知结果',
width: 80,
dataIndex: 'inform_result',
render: (v) =>
v === 0 ? (
<Tag color="#87d068">成功</Tag>
) : (
<Tag color="#f50">失败</Tag>
),
},
{
title: '操作',
width: 60,
render: (v, record) => (
<React.Fragment>
<a
onClick={() => {
setModal({
action: 'detail',
isShow: true,
record,
});
}}
>
详情
</a>
</React.Fragment>
),
},
];
return (
<React.Fragment>
<Form layout="inline">
{DeleteEle}
{FilterEles}
</Form>
<Table
{...{
scroll: { x: 1170, y: '70vh' },
columns,
dataSource: list || [],
rowSelection,
pagination: paginationConfig,
rowKey: 'inform_uuid',
}}
/>
{ModalEle}
</React.Fragment>
);
};
export default TestTable;
Hook封装
usePagination
配置表格的分页功能
const usePagination = () => {
// 默认数据
const [pagination, setPagination] = useState({
page: 1,
pageSize: 10,
total: 0,
});
// 用于触发外层的刷新
const [pageRefresh, setPageRefresh] = useState(false);
const config = {
size: "small",
showQuickJumper: false,
total: pagination.total,
current: pagination.page,
showSizeChanger: true,
onChange: (current) => {
setPagination({
...pagination,
page: current,
});
setPageRefresh(() => !pageRefresh); // 修改页数后,通知外层刷新
},
pageSize: pagination.pageSize,
onShowSizeChange: (page, pageSize) => {
setPagination({
page,
pageSize,
});
setPageRefresh(() => !pageRefresh);// 修改页码后,通知外层刷新
},
};
return {
pagination,
pageRefresh,
setPagination,
paginationConfig: config,
};
};
useSelect
配置表格的选择功能
const useSelect = () => {
const [selectedKeys, setSelectedKeys] = useState([]);
const rowSelection = {
onChange: (selectedKeys) => {
setSelectedKeys(selectedKeys);
},
selectedRowKeys: selectedKeys,
};
return { selectedKeys, rowSelection, setSelectedKeys };
};
useDelete
配置表格的批量删除按钮
const useDelete = ({ url = "", data = {}, success = null, keys = [] }) => {
const handleDel = () => {
// 请求..
message.success("删除成功");
success && success(); //
};
const DeleteEle = (
<React.Fragment>
{ /**OPER.isDelete : 当有权限时才显示 */}
{OPER.isDelete && (
<Form.Item>
{keys.length === 0 ? (
<Button icon={<DeleteOutlined />} size="small" disabled>
批量删除
</Button>
) : (
<Popconfirm
title="批量删除"
description="你确定要删除吗?"
okText="确定"
cancelText="取消"
onConfirm={handleDel}
>
<Button
type="primary"
danger
icon={<DeleteOutlined />}
size="small"
>
批量删除
</Button>
</Popconfirm>
)}
</Form.Item>
)}
</React.Fragment>
);
return [DeleteEle];
};
useFilter
配置过滤项,附带查询、重置按钮
import { initFilter, getFormItem } from "./utils"; // 根据配置配置初始化filter数据 ; 根据配置获取组件
const useFilter = (config = []) => {
const [filter, setFilter] = useState(initFilter(config));
const [refresh, setRefresh] = useState(false);
// 重置过滤项
const handleResetFilter = () => {
setFilter(initFilter(config));
setRefresh(!refresh); // 通知外层刷新
};
// 触发查询
const handleSearch = () => {
setRefresh(!refresh); // 通知外层刷新
};
// 修改日期类型
const handleDateChange = ([startField, endField], [startTime, endTime]) => {
setFilter({ ...filter, [startField]: startTime, [endField]: endTime });
};
// 修改input 类型
const handleInputChange = (e) => {
const { name, value } = e.target;
setFilter({ ...filter, [name]: value });
};
// 修改select 类型
const handleSelectChange = (name, value) => {
setFilter({ ...filter, [name]: value });
};
// 根据配置生成表单项
const FormItemEles = getFormItem(config, {
filter,
handleInputChange,
handleSelectChange,
handleDateChange,
});
const HandlerEles = [
<Form.Item>
<Button
type="primary"
size="small"
onClick={handleSearch}
icon={<SearchOutlined />}
>
查询
</Button>
</Form.Item>,
<Form.Item>
<Button
type="primary"
size="small"
onClick={handleResetFilter}
icon={<ReloadOutlined />}
>
重置
</Button>
</Form.Item>,
];
return {
filter,
FilterEles: [...FormItemEles, ...HandlerEles],
filterRefresh: refresh,
};
};
initFilter:根据不同类型初始化filter数据
const initFilter = (arr) => {
const obj = {};
arr.forEach((item) => {
switch (item.type) {
case "inputString":
obj[item.name] = item.initValue || "";
break;
case "select":
obj[item.name] = item.initValue || 0;
break;
case "dateRange":
item.name.forEach((name) => {
obj[name] = "";
});
break;
default:
break;
}
});
return obj;
};
getFormItem:根据不同的类型生成表单项组件
const getFormItem = (
arr,
{ filter, handleInputChange, handleSelectChange, handleDateChange }
) => {
const Eles = [];
arr.forEach((item) => {
const name = item.name;
const label = item.label;
const value = filter[name];
switch (item.type) {
case "inputString":
Eles.push(
<Form.Item>
<Input
size="small"
placeholder={label}
name={name}
value={value}
onChange={handleInputChange}
/>
</Form.Item>
);
break;
case "select":
Eles.push(
<Form.Item>
<Select
size="small"
style={{ width: 120 }}
onChange={(v) => {
handleSelectChange(name, v);
}}
name={name}
value={value}
options={item.options}
/>
</Form.Item>
);
break;
case "dateRange":
Eles.push(
<Form.Item>
<RangePicker
onChange={(dates) => {
handleDateChange(name, dates);
}}
value={[filter[name[0]], filter[name[1]]]}
placeholder={label}
size="small"
/>
</Form.Item>
);
default:
break;
}
});
return Eles;
};
useModal
配置模态框
const useModal = (
config = [
{
title: "详情",
action: "detail",
getComponent: (record)=><div>详情</div>,
},
]
) => {
const [modal, setModal] = useState({
isShow: false,
record: null,
action: "",
});
const handleCancel = () => {
setModal({
isShow: false,
record: null,
action: "",
});
};
//对话框信息
let modalProps = {
open: modal.isShow,
onCancel: handleCancel,
maskClosable: false,
footer: null,
};
const currentModal = config.find((item) => item.action === modal.action); // 找到当前模态框数据
if (currentModal) {
const { action, getComponent, ...other } = currentModal;
// 配置传进来的其余属性
modalProps = {
...modalProps,
...other,
};
}
const ModalEle = (
<Modal width="80%" {...modalProps}>
{currentModal && currentModal.getComponent(modal.record)}
</Modal>
);
return { ModalEle, modal, setModal };
};
源码
后记:起因是写表格写烦了,于是快速封装了一个,因为仅针对于当前的需求,所以写的比较简单,还有很多地方需要扩展。如果有其他思路,还请多多赐教~