作为React技术栈的坚定拥护者,一定离不开阿里爸爸的大怀抱。作为国内最好用的组件库【之一】的Ant Design迎来个跨版本大更新,年后复工后我在第一时间便进行了尝鲜,刚好公司有老项目重构,在初步了解之后,决定尝试在新项目中使用And Design 4.0进行开发,与之对应的Ant Design Pro也更新到了4.0版本,Ant Design Pro是给予Ant Design的开箱即用中台前端/设计解决方案。简单的说,Ant Dsign是砖头,那Ant Design Pro就是用这些砖头垒起来的框架,你可以基于这个框架来按照你自己的想法重新装修,也可以在不破坏主体结构的情况下拆除部分内容重建。举个例子,对于我来说,原框架集成的国际化部分是无用的,那我就需要剔除。本系列文章主要针对在项目中使用Ant Design 4.0中遇到的一下值得注意的点~
本片先来说一下让我很是喜欢的ProTable这个新组件,让我们一起来看看它到底Pro在哪里。
以往,我们在做列表功能之类,需要重复的去手动的在componentDidMount钩子写请求数据,然后把数据重model中取出来,然后再放到Table组件的dataSource属性中,然后还要处理pageChange事件,然后还要手动的去渲染搜索项,然后就又得自己去引入各种组件,然后又各种堆接节点...这个过程下来,人都要惆怅咯。再加之一般做这种中后台管理系统,类似的功能很多,重复且低效的工作方式简直无法忍受。
import React, { PureComponent, Fragment } from 'react';
import { connect } from 'dva';
import { Row, Col, Card, Form, Input, Button, DatePicker, Divider, message, Table } from 'antd';
import router from 'umi/router';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import { formatDate, getStartTimeStamp, getEndTimeStamp } from '@/utils/utils';
import styles from './style.less';
const FormItem = Form.Item;
const { RangePicker } = DatePicker;
const getValue = obj =>
Object.keys(obj)
.map(key => obj[key])
.join(',');
/* eslint react/no-multi-comp:0 */
@connect(({ approval, studentManagment, loading }) => ({
approval,
studentManagment,
loading: loading.effects['studentManagment/fetch'],
}))
@Form.create()
class StudentManagement extends PureComponent {
state = {
selectedRows: [],
formValues: {},
};
columns = [
{
title: '名称',
dataIndex: 'name',
render: val => val || '-',
},
{
title: '创建日期',
dataIndex: 'createTime',
render: val => val && formatDate(val),
}, {
title: '性别',
dataIndex: 'sex'
},
{
title: '年龄',
dataIndex: 'age'
},
{
title: '学校',
dataIndex: 'school'
},
{
title: '年级',
dataIndex: 'grade'
},
{
title: '操作',
render: (text, record) => (
<Fragment>
<a
onClick={() =>
router.push({
pathname: '/common/landing-page-management/edit',
query: {
id: record.id,
},
})
}
>
编辑
</a>
<Divider type="vertical" />
<a onClick={() => this.onCopyLink(record)}>复制链接</a>
</Fragment>
),
},
];
componentWillMount() {
this.getData();
}
getData = params => {
const { dispatch } = this.props;
dispatch({
type: 'studentManagment/fetch',
payload: params,
});
};
onCopyLink = record => {
const input = document.createElement('input');
input.value = record.shortUrl;
document.body.appendChild(input);
input.select();
input.setSelectionRange(0, input.value.length);
document.execCommand('Copy');
document.body.removeChild(input);
message.success('复制成功!');
};
handleStandardTableChange = (pagination, filtersArg, sorter) => {
const { formValues } = this.state;
const filters = Object.keys(filtersArg).reduce((obj, key) => {
const newObj = { ...obj };
newObj[key] = getValue(filtersArg[key]);
return newObj;
}, {});
const params = {
page: pagination.current,
pageSize: pagination.pageSize,
...formValues,
...filters,
};
if (sorter.field) {
params.sorter = `${sorter.field}_${sorter.order}`;
}
this.getData(params);
};
handleFormReset = () => {
const { form } = this.props;
form.resetFields();
this.setState({
formValues: {},
});
this.getData();
};
handleSelectRows = rows => {
this.setState({
selectedRows: rows,
});
};
handleSearch = e => {
e.preventDefault();
const { form } = this.props;
form.validateFields((err, fieldsValue) => {
if (err) return;
const values = {
name: fieldsValue.name,
startDate: fieldsValue.date && getStartTimeStamp(fieldsValue.date[0]),
endDate: fieldsValue.date && getEndTimeStamp(fieldsValue.date[1]),
};
this.setState({
formValues: values,
});
this.getData(values);
});
};
renderForm() {
const {
form: { getFieldDecorator },
} = this.props;
return (
<Form onSubmit={this.handleSearch} layout="inline">
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={8} sm={24}>
<FormItem label="名称">
{getFieldDecorator('name')(<Input placeholder="请输入" />)}
</FormItem>
</Col>
<Col md={8} sm={24}>
<FormItem label="创建日期">
{getFieldDecorator('date')(<RangePicker style={{ width: '100%' }} />)}
</FormItem>
</Col>
<Col md={8} sm={24}>
<Button type="primary" htmlType="submit">
查询
</Button>
<Button style={{ marginLeft: 8 }} onClick={this.handleFormReset}>
重置
</Button>
</Col>
</Row>
<div style={{ overflow: 'hidden' }}>
<div style={{ float: 'left', marginBottom: 24 }}>
<Button
icon="plus"
type="primary"
onClick={() => router.push({ pathname: '/common/landing-page-management/add' })}
>
新增
</Button>
</div>
</div>
</Form>
);
}
render() {
const {
studentManagment: { list },
loading,
} = this.props;
const { selectedRows } = this.state;
return (
<PageHeaderWrapper>
<Card bordered={false}>
<div className={styles.tableList}>
<div className={styles.tableListForm}>{this.renderForm()}</div>
<Table
selectedRows={selectedRows}
loading={loading}
data={list}
columns={this.columns}
onSelectRow={this.handleSelectRows}
onChange={this.handleStandardTableChange}
/>
</div>
</Card>
</PageHeaderWrapper>
);
}
}
export default StudentManagement;
上面是一个用Table来做查询列表功能的一个小demo,大致流程如下:
- 引入Table组件,编写页面jsx;
- 编写columns数据;
- 根据自己的需求编写搜索表单的jsx;
- 处理查询事件,pageChange事件等
可以发现这其实是一个重复的工作,大部分情况下只需要按照固定熟悉来写差不多一样的代码就好了。
现在,我们有了ProTable,再来看看怎么做呢~
import { PlusOutlined } from '@ant-design/icons';
import { Button, Divider } from 'antd';
import React, { Component, Fragment } from 'react';
import router from 'umi/router';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import ProTable from '@ant-design/pro-table';
import { copyString, formatDate, getStartTimeStamp, getEndTimeStamp } from '@/utils/utils';
import { connect } from 'dva';
import { queryRule } from './service';
class StudentManagement extends Component {
columns = [
{
title: '名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '创建日期',
dataIndex: 'createTime',
valueType: 'dateRange',
render: val => val && formatDate(val),
},
{
title: '性别',
dataIndex: 'sex',
hideInSearch: true,
},
{
title: '年龄',
dataIndex: 'age',
hideInSearch: true,
},
{
title: '学校',
dataIndex: 'school',
hideInSearch: true,
},
{
title: '年级',
dataIndex: 'grade',
hideInSearch: true,
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => (
<Fragment>
<a
onClick={() =>
router.push({
pathname: '/public-resource/landing-page-management/edit',
query: {
id: record.id,
},
})
}
>
编辑
</a>
<Divider type="vertical" />
<a onClick={() => copyString(record.shortUrl)}>复制链接</a>
</Fragment>
),
},
];
render() {
return (
<PageHeaderWrapper>
<ProTable
toolBarRender={() => [
<Button
type="primary"
onClick={() => router.push('/public-resource/landing-page-management/add')}
>
<PlusOutlined />
新建
</Button>,
]}
rowKey="id"
dateFormatter="number"
request={params =>
queryRule({
...params,
current: undefined,
startDate: params.createTime && getStartTimeStamp(params.createTime[0]),
endDate: params.createTime && getEndTimeStamp(params.createTime[1]),
})
}
columns={this.columns}
/>
</PageHeaderWrapper>
);
}
}
export default connect(({ studentManagement }) => ({
studentManagement,
}))(StudentManagement);
写完之后最直观的感受就是代码量足足少了一半!!ProTable将请求数据、处理数据、渲染节点的这个过程封装了起来,使得我们只需要按照他的约定来进行配置即可完成大部分操作。现在:我们流程就变成了:
- 引入ProTable组件和请求数据的方法并配置好;
- 按照ProTable的规则维护columns即可。
在新的ProTable中,与以往不同的是,我们需要维护的columns中新增了一个valueType属性,按照约定值,ProTable将根据这个属性来渲染表单项和表头。剩下的Search事件、Reset事件、pageChange事件等等等等...ProTable都会帮你做好。
ProTable大法好,上车保平安。学会用ProTable,从此列表一把梭~
官方文档地址:protable.ant.design/
文章写得比较简略,有问题欢迎在评论区留言~