简介: antd 本身的表格组件十分强大,十分灵活,这里基于 antd 的 Table 组件做业务封装,支持功能:表格索引,loading,分页,按钮操作,数据格式化,数据选择等。
项目主要依赖:(先安装,步骤略)
create-react-app:3.0.0
{
"react": "16.8.6",
"react-router-dom": "5.0.0",
"antd": "^3.19.2",
"axios": "^0.19.0",
"prop-types": "^15.7.2"
}
1.组件
src/components/AppTable/index.jsx
import React, { Component, Fragment } from 'react';
import { Table, Button, Modal, Layout, Icon } from 'antd';
import PropTypes from 'prop-types';
const { Content } = Layout;
class AppTable extends Component {
state = {
// 大图数据
imgSrc: ''
};
// props类型检查
static propTypes = {
// 操作栏宽度
operationWidth: PropTypes.number,
// 配置
config: PropTypes.object.isRequired,
// 是否需要操作栏
hasOperation: PropTypes.bool,
// 是否需要索引
hasIndex: PropTypes.bool,
// 是否需要选择
hasSelect: PropTypes.bool,
// 监听分页改变
handlePageChange: PropTypes.func,
// 监听选择表格
handleSelecet: PropTypes.func,
// 监听操作栏
handleTableOption: PropTypes.func,
// 点击表格单元格
handleClickCell: PropTypes.func,
// 上边距
marginTop: PropTypes.number
};
// 默认的props
static defaultProps = {
// 操作栏宽度
operationWidth: 100,
// 是否需要操作栏
hasOperation: true,
// 是否需要索引
hasIndex: true,
// 是否需要选择
hasSelect: false,
// 监听分页改变
handlePageChange: null,
// 监听选择表格
handleSelecet: null,
// 监听操作栏
handleTableOption: null,
// 点击表格单元格
handleClickCell: null,
// 上边距
marginTop: 20
};
componentWillUnmount() {
this.setState = (state, callback) => {
return;
};
}
// 点击查看大图
handleClickImage(src) {
Modal.info({
title: '大图',
okText: '确定',
style: { top: '15vh' },
content: (
<div style={{ textAlign: 'center' }}>
<img src={src} alt='大图' />
</div>
)
});
}
render() {
let {
config,
operationWidth,
hasOperation,
hasIndex,
hasSelect,
handlePageChange,
handleSelecet,
handleTableOption,
marginTop
} = this.props;
let { columns, pagination } = config;
// 特殊表格处理(如需更多特殊显示,添加case即可解决)
columns.forEach(item => {
if (item.format) {
switch (item.format) {
// 图片显示
case 'image':
item.render = (value, row, index) => {
return (
<img
key={row.key}
onClick={this.handleClickImage.bind(this, value)}
width={30}
src={value}
alt='图片损坏'
/>
);
};
break;
// 金额显示
case 'money':
item.render = value => '¥' + value / 100; // 显示为金额,返回的单位是分,处理时除以100换算成元
break;
default:
}
}
});
// 默认添加索引
if (hasIndex && columns.length) {
const Index = columns[0];
const { current, pageSize } = pagination;
const indexItem = {
title: '#',
key: 'index',
fixed: 'left',
format: 'index',
width: 60,
render: (value, row, index) => (
<span key={index}>{pageSize * (current - 1) + index + 1}</span>
)
};
if (Index.title !== '#') {
columns.unshift(indexItem);
} else {
columns[0] = indexItem;
}
}
// 默认添加操作栏
const buttonContent = option => {
let type = '';
switch (option) {
case '查看':
type = 'eye';
break;
case '显示':
type = 'eye';
break;
case '隐藏':
type = 'eye-invisible';
break;
case '编辑':
type = 'edit';
break;
case '删除':
type = 'delete';
break;
case '排序':
type = 'sort-ascending';
break;
default:
break;
}
if (type) {
return (
<Fragment>
<Icon type={type} style={{ paddingRight: 5 }} /> {option}
</Fragment>
);
} else {
return option;
}
};
const operation = columns[columns.length - 1];
if (columns.length && hasOperation && operation.title !== '操作') {
columns.push({
title: '操作',
dataIndex: 'operation',
width: operationWidth,
format: 'operation',
key: 'operation',
fixed: 'right',
render: (value, row, index) => {
const buttons = row.buttonsName ? row.buttonsName : [];
return (
<div>
{buttons.map((option, index) => {
return (
<Button
size='small'
type={index === 0 ? 'primary' : ''}
key={index}
style={{ marginLeft: 10 }}
onClick={() => handleTableOption(row, option)}
>
{buttonContent(option)}
</Button>
);
})}
</div>
);
}
});
}
// 添加可以是否选择表格
const rowSelection = {
onChange: (key, rows) => {
handleSelecet(key, rows);
}
};
const selectConfig = hasSelect ? { rowSelection } : {};
// 默认表格配置
const defaultConfig = {
bordered: true,
scroll: { x: 960 }
};
// 默认分页配置
const defaultPagination = {
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ['20', '40', '100']
};
config.pagination = { ...defaultPagination, ...pagination };
return (
<Content
style={{
background: '#fff',
padding: 24,
marginTop
}}
>
{this.props.children}
<Table
{...defaultConfig}
{...selectConfig}
{...config}
onChange={handlePageChange}
/>
</Content>
);
}
}
export default AppTable;
补充全局less(需添加到项目中)
:global(#root .ant-table-thead > tr > th) {
padding: 8px 7px 7px 22px;
height: 50px;
box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
}
:global(#root .ant-table-tbody > tr > td) {
padding: 8px 7px 7px 22px;
height: 50px;
box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
}
2.使用
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import AppTable from '@/components/AppTable';
// 后端接口,根据自己项目更换
import UserServe from '@/api/user';
@withRouter // 修饰器需添加babel插件
class User extends Component {
state = {
// 用户列表
userList: [],
// 分页总数
total: 0,
// 表格loading
loading: false,
// 分页参数
params: {
perPage: 5,
page: 1
},
// 操作栏按钮
buttonsName: {
normal: ['查看', '排序']
// special: ['排序']
},
// 表格操作栏宽度
operationWidth: 100,
// 表格配置
columns: [
{
title: '头像',
dataIndex: 'avatarUrl',
width: 80,
key: 'avatarUrl',
format: 'image' // 显示为图片
},
{
title: '昵称',
dataIndex: 'nickname',
key: 'nickname',
width: 140
},
{
title: '真实姓名',
dataIndex: 'userName',
key: 'userName',
width: 140
},
{
title: '电话',
dataIndex: 'userPhone',
key: 'userPhone',
width: 140
},
{
title: '用户身份',
dataIndex: 'userType',
key: 'userType',
width: 140
},
{
title: '账户余额',
dataIndex: 'lastAmount',
key: 'lastAmount',
width: 140,
format: 'money' // 显示为金额,返回的单位是分,处理时除以100换算成元
},
{
title: '预订次数',
dataIndex: 'reserveTimes',
key: 'reserveTimes',
width: 140
},
{
title: '上次预订时间',
dataIndex: 'lastReserveTime',
key: 'lastReserveTime'
}
]
};
componentDidMount() {
this.getList();
}
componentWillUnmount() {
this.handleTableOption = null;
this.handlePageChange = null;
this.handleSelecet = null;
this.setState = (state, callback) => {
return;
};
}
// 获取用户列表
getList = async () => {
try {
this.setState({ loading: true });
const {
buttonsName: { normal }
} = this.state;
// 拿到的服务端数据格式见步骤3
let {
items,
page: { total }
} = await UserServe.getUserList(this.state.params);
items.forEach(item => {
item.buttonsName = normal;
item.key = item.userUuid;
});
this.setState({ userList: items, total, loading: false });
} catch (error) {
console.log(error);
}
};
// 操作栏
handleTableOption = (row, option) => {
if (option === '查看') {
const { history } = this.props;
history.push(`/user/details?userUuid=${row.userUuid}`);
}
};
// 分页监听
handlePageChange = pageInfo => {
const { current, pageSize } = pageInfo;
this.setState(
() => {
return { params: { perPage: pageSize, page: current } };
},
() => {
this.getList();
}
);
};
render() {
const {
userList: dataSource,
total,
loading,
params,
columns,
operationWidth
} = this.state;
const tableConfig = {
columns,
dataSource,
pagination: {
total,
pageSize: params.perPage,
current: params.page
},
loading
};
return (
<div>
<AppTable
config={tableConfig}
operationWidth={operationWidth}
handleTableOption={this.handleTableOption}
handlePageChange={this.handlePageChange}
/>
</div>
);
}
}
export default User;
3.补充表格数据
{
"data": {
"items": [
{
"avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/H40abp83zlOAiaqGanY4VpH7kzy6uyOr30Gm634ru0obw9UfSLic4OBxeuH7Oosud15BCwfiazBoCALBIbdsCSjsg/132",
"lastAmount": 0,
"lastReserveTime": "2019-08-28 14:38:00",
"nickname": "\u65e5\u4e45\u751f\u60c5",
"reserveTimes": 0,
"userName": null,
"userPhone": null,
"userType": "\u666e\u901a\u7528\u6237",
"userUuid": "60C8546BC95E11E9BD27525400AE34BF"
},
{
"avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTIia1wXh7HxQ2C2CkVyjQpH7zOK2hpDeN75yH0C0wS9z8U8eUt8uibK9ckKAekWSPibDScicaMaoPlqHA/132",
"lastAmount": 0,
"lastReserveTime": "2019-08-23 11:46:53",
"nickname": "\u8def\u4eba\u7532",
"reserveTimes": 0,
"userName": null,
"userPhone": null,
"userType": "\u666e\u901a\u7528\u6237",
"userUuid": "A561122FC55811E9BD27525400AE34BF"
},
{
"avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/uJpqs0geoUyTbKHdibj3VL05wUn0IgVXosu4D3WdqdcwjhoJ75ukt6an5nSZRYfQzfPvLkhk9e26Ol4ufrkibqvg/132",
"lastAmount": 0,
"lastReserveTime": "2019-08-21 14:44:52",
"nickname": "\u5c0f\u871c\u8702",
"reserveTimes": 0,
"userName": null,
"userPhone": null,
"userType": "\u666e\u901a\u7528\u6237",
"userUuid": "2DC231A1C3DF11E9BD27525400AE34BF"
},
{
"avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKD8ffFgoFh3BXZtyP032lhBWJaBUUXzGxgl4IXHf01bLicvPlb4nUpqU2JpcpFRicgW9p0X8S5OcQw/132",
"lastAmount": 0,
"lastReserveTime": "2019-07-20 20:58:52",
"nickname": "\u5e03\u5170\u742a",
"reserveTimes": 0,
"userName": null,
"userPhone": null,
"userType": "\u666e\u901a\u7528\u6237",
"userUuid": "1FD14EFBAAEE11E9BD27525400AE34BF"
},
{
"avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/U10j5gX0UTCI5Sjbh7wakujtcofvHS4FWhHToJSqrFCSe7bUN1YuX1qkaEWbxQ0B09PXlUabeLC7bAN0rYlXkA/132",
"lastAmount": 0,
"lastReserveTime": "2019-07-17 14:44:43",
"nickname": "\u7f57\u4f1fHalo",
"reserveTimes": 0,
"userName": null,
"userPhone": "13684001024",
"userType": "\u666e\u901a\u7528\u6237",
"userUuid": "5B67D4BDA85E11E9BD27525400AE34BF"
}
],
"page": {
"currentPage": 1,
"firstPage": 1,
"hasNext": true,
"hasPrev": false,
"lastPage": 5,
"nextPage": 2,
"pageCount": 5,
"pages": [1, 2, 3, 4, 5],
"perPage": 5,
"prevPage": 0,
"total": 24,
"totalPages": 5
}
},
"status": 1
}