react+antd封装表格组件2.0
1.0版本 仅仅封装组件,不涉及方法
此方法把所用方法集体封装,以后就可以无脑开发拉!
- 只需传入路径,组件列表请求、增删改后更新属性均在组件内发生
- 当前页不是第一页且删除后无数据,则自动跳转到上一页
- 查询时防止多次点击,点击一次后加遮罩,数据返回后放开
- 已有查询条件时,再次分页带上查询条件
需要掌握知识点
useImperativeHandle
一个用于暴露自定义ref属性和自定义方法的钩子函数。可以使得父组件可以通过ref访问子组件中定义的方法和属性,从而实现对子组件的精细控制
!!使用useImperativeHandle时必须与forwardRef搭配使用,否则会报错
- 具体使用步骤如下
javascript
//在子组件中定义需要暴露给父组件的方法和属性,并将这些方法和属性放在一个对象中
import { useImperativeHandle, forwardRef } from 'react';
const Child = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
// 定义需要暴露给父组件的方法和属性
useImperativeHandle(ref, () => ({
increment,
count,
}));
return <div>{count}</div>;
});
javascript
//在父组件中使用子组件,并给子组件传递一个ref属性,
import { useRef } from 'react';
import Child from './Child';
const Parent = () => {
// 创建一个ref
const childRef = useRef();
// 在父组件中通过ref访问子组件的方法和属性
const handleClick = () => {
childRef.current.increment();
};
return (
<div>
<button onClick={handleClick}>Click me</button>
<Child ref={childRef} />
</div>
);
};
//父组件中的handleClick方法通过childRef.current访问了Child组件中的increment方法和count属性。
组件代码
javascript
import { Table, Pagination, Button, Dropdown, Checkbox, message } from 'antd';
import { useDispatch } from 'umi';
import { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
import { PicRightOutlined } from '@ant-design/icons';
import './index.less';
const TableComponent = forwardRef((props, ref) => {
const dispatch = useDispatch();
const [open, setOpen] = useState(false); //动态控制列的下拉显隐
const [items, setItems] = useState([]); //当前的下拉选择状态
const [loading, setLoading] = useState(true); //当前的加载状态
const [dataSource, setDataSource] = useState([]); //列表数据
const [total, setTotal] = useState(0); //分页总数
const [size, setSize] = useState(10); //分页页数
const [current, setCurrent] = useState(1); //分页当前页
// 抛出获取列表方法
useImperativeHandle(ref, () => ({
getList,
}));
// 表格列表
const [columns, setColumns] = useState(
props.columns.map((item) => {
return {
...item,
align: 'center',
ellipsis: {
showTitle: false,
},
};
}),
);
//控制列的初始值
const [starCol, setStarCol] = useState(
props.columns.map((item) => {
return {
...item,
check: true,
align: 'center',
ellipsis: {
showTitle: false,
},
};
}),
);
useEffect(() => {
changItem(starCol);
}, [props]);
useEffect(() => {
if (props.columns || props.columns.length > 0) {
setColumns(
props.columns.map((item) => {
return {
...item,
align: 'center',
ellipsis: {
showTitle: false,
},
};
}),
);
setStarCol(
props.columns.map((item) => {
return {
...item,
check: true,
align: 'center',
ellipsis: {
showTitle: false,
},
};
}),
);
}
}, [props.columns]);
useEffect(() => {
// 初始请求列表
if (props.url) {
getList();
}
}, [props.url]);
useEffect(() => {
// 新增编辑后列表刷新
if (props.list) {
const list = props.list?.map((item, index) => {
return {
index: serialNumber(current, size, index + 1),
...item,
};
});
setDataSource(list);
}
}, [props.list]);
useEffect(() => {
if (props.search) {
dispatch({
type: 'utils/searchLoadingEvent',
payload: true,
});
// 搜索请求列表
getList(1, size, props.search, true);
}
}, [props.search]); //监听列表传入查询条件
const getList = (pageCurrent, pageSize, values, isClickSearch) => {
if (!isClickSearch) {
// 获取列表时 没点击查询按钮 加载列表
setLoading(true);
}
const payload = {
current: pageCurrent || current,
size: pageSize || size,
...values,
};
dispatch({
type: props.url,
payload: payload,
callback: (res) => {
if (res) {
setTotal(res.total);
setSize(res.size);
setCurrent(res.current);
const list = res.data?.map((item, index) => {
return {
index: serialNumber(res.current, res.size, index + 1),
...item,
};
});
setDataSource(list);
setLoading(false);
props.listPaginationChange(payload); //更新接口条件
// 处理列表最后一条数据删除时请求上一页数据
if (res.data?.length == 0 && res.total > 0) {
getList(res.current - 1, res.size, props.search);
}
//点击查询按钮 获取到数据后 关闭局部加载
if (isClickSearch) {
dispatch({
type: 'utils/searchLoadingEvent',
payload: false,
});
}
}
},
});
};
// 分页查询列表
const paginationChange = (pageCurrent, pageSize) => {
getList(pageCurrent, pageSize, props.search || null);
};
//这个方法可加可不加,是因为前端需要展示序号,而后端不返回,所以就自己计算啦!
const serialNumber = (pageIndex, pageSize, index) => {
return (pageIndex - 1) * pageSize + index;
};
// 控制显示列的操作
const onClick = ({ key }) => {
if (key == 'all') {
const newDrop = starCol;
newDrop.map((o, index) => {
o.check = true;
});
changItem(starCol);
setColumns(
props.columns.map((item) => {
return {
...item,
check: true,
align: 'center',
ellipsis: {
showTitle: false,
},
};
}),
);
} else {
const newDrop = starCol;
newDrop.map((o, index) => {
if (index == key) {
o.check = !o.check;
}
});
let newColumns = newDrop.filter((o) => o.check);
if (newColumns.length == 0) {
message.warning('请至少选择一列');
} else {
setStarCol(newDrop);
changItem(newDrop);
setColumns(newColumns); //列状态
}
}
};
// 列的选中状态改变
const changItem = (data) => {
let opitems = data.map((item, index) => {
return {
key: index,
label: <Checkbox checked={item.check}>{item.title}</Checkbox>,
};
});
opitems.unshift(
{
key: 'all',
label: <Button>{'全选列'}</Button>,
},
{
type: 'divider',
},
);
setItems(opitems); //当前的下拉状态
};
return (
<div className="table">
{!props?.selectIf && (
<Dropdown
menu={{
items,
onClick,
}}
overlayClassName="drop"
trigger={['click']}
onOpenChange={() => setOpen(!open)}
open={open}
arrow
placement="bottomRight"
>
<a onClick={(e) => e.preventDefault()}>
<Button icon={<PicRightOutlined />} title="显示/隐藏列"></Button>
</a>
</Dropdown>
)}
<Table
bordered
columns={columns}
rowSelection={props?.rowSelection}
// dataSource={isShow ? dataSource : null}
dataSource={dataSource}
rowKey={(item) => item.id || item.categoryId || item.modelId}
pagination={false}
className="insiadeTable"
scroll={{
y: 450,
...props?.scroll,
}}
onRow={props?.onRow}
rowClassName={props?.rowClassName}
summary={props?.summary}
loading={loading}
/>
<div className="pagination">
<Pagination
showQuickJumper
defaultCurrent={current}
total={total}
showTotal={(total) => `共 ${total} 条`}
size={size}
current={current}
onChange={paginationChange}
pageSizeOptions={[10, 50, 100, 500]}
/>
</div>
</div>
);
});
export default TableComponent;
引用
javascript
···
const [search, setSearch] = useState(null); //搜索条件
const tableComponentRef = useRef(null);
const list = useSelector((state) =>state.roleManagement.roleManagementList);
const [search, setSearch] = useState(null);
const [listChange, setListChange] = useState(list);
const [listPaginationChange, setListPaginationChange] = useState(null);
···
useEffect(() => {
if (list) {
setListChange(list); //组件内数据响应后此处更新
}
}, [list]);
···
const onFinish = (values) => {
setSearch(values); //点击搜索后设置值
};
···
<Form onFinish={onFinish} form={form} requiredMark={'Hidden'}>
<Row gutter={16}>
<Col span={6}>
<Form.Item
name="keyword"
label="关键字"
>
<Input allowClear />
</Form.Item>
</Col>
<Col span={4}>
<Space>
<Button
type="primary"
icon={<SearchOutlined />}
htmlType="submit"
>
搜索
</Button>
<Button
icon={<SyncOutlined />}
onClick={() => {
form.resetFields();
}}
>
重置
</Button>
</Space>
</Col>
</Row>
</Form>
<TableComponent
ref={tableComponentRef} //组件实例
url="roleManagement/getRoleManagementList"
search={search} //搜索条件
list={listChange} //数据源
listPaginationChange={(e) => {
//{current:1,size:10,..查询条件} 格式
setListPaginationChange(e);
}}
className="list"
columns={columns} //列
scroll={{
x: 1600,
}}
/>
- 新增修改组件
javascript
<Edit
fillingForm={fillingForm}
open={isModalOpen}
setIsModalOpen={setIsModalOpen}
//修改新增组件只需传入listPaginationChange,便可保留分页及查询条件
listPaginationChange={listPaginationChange}
/>
//修改新增后调用即可
const getRoleList = () => {
dispatch({
type: 'roleManagement/getRoleManagementList',
payload: props.listPaginationChange,
};