假设有一个这样的需求:table表头排序,用户可以将关心的列头排在前面。
我们都知道,使用第三方组件库时,一般需要设置参数 dataSource(table数据源,是一个数组,指定每一行字段的值) 和 columns(表头,是一个数组,各个列的属性),dataSource里面的值会自动赋值到与之字段相同的列上去。
所以dataSource还是dataSource,不用改,按正常逻辑请求接口获取列表数据即可,接下来要动的就是columns,既然要实现用户可以自己调整列,那么表头columns就不能写死了,而是需要跟后端交互动态存储和获取表头,改变完成后调接口取新的值赋值给columns就可以,具体接口如何调用,参数,返回值什么的跟你的后端商量好就行。
接下来就要封装一个用于让用户设置表头显示顺序的UI界面以及交互逻辑组件了,毕竟可能会有很多页面的table需要此功能。
第一种方法:列表上下排序(用户可以通过点击相应的移动按钮来调整顺序)
import React, { Component } from ‘react‘; import { connect } from ‘dva‘; import { Button, Card, Checkbox, Col, Row } from ‘antd‘; import styles from ‘./index.less‘; import { TableListItem } from ‘@/utils/TableData‘; interface TableFieldsOrderProps { initialTags: TableListItem<any>[], renderOnChange: (value: any) => void; renderIsVisible: (value: any) => void; } interface TableFieldsOrderState { initialTags?: TableListItem<any>[], currentSelectedTag?: TableListItem<any>, } @connect() class TableFieldsOrder extends Component<TableFieldsOrderProps> { state: TableFieldsOrderState = { initialTags: [], currentSelectedTag: {}, }; static getDerivedStateFromProps(props: any, state: any) { const { initialTags = [], } = props; return { initialTags, } } private onChange = (value: any) => { const { renderOnChange } = this.props; renderOnChange(value); }; private isVisible = (value: any) => { const { renderIsVisible } = this.props; renderIsVisible(value); }; // 设置显示列顺序——记录当前选中的表头字段 private handleBlockSelected = (value: any) => { this.setState({ currentSelectedTag: value }) } // 设置显示列顺序——字段移到最上 private tableFieldMoveTop = () => { const { initialTags = [], currentSelectedTag = {} } = this.state; const currentIndex = initialTags.findIndex(tag => tag.id === currentSelectedTag.id) initialTags.splice(currentIndex, 1) initialTags.splice(0, 0, currentSelectedTag) this.onChange(initialTags) } // 设置显示列顺序——字段上移n条 private tableFieldMoveUp = (num: number) => { const { initialTags = [], currentSelectedTag = {} } = this.state; const currentIndex = initialTags.findIndex(tag => tag.id === currentSelectedTag.id) initialTags.splice(currentIndex, 1) if (currentIndex - num < 0) { initialTags.splice(0, 0, currentSelectedTag) } else { initialTags.splice(currentIndex - num, 0, currentSelectedTag) } this.onChange(initialTags) } // 设置显示列顺序——字段下移n条 private tableFieldMoveDown = (num: number) => { const { initialTags = [], currentSelectedTag = {} } = this.state; const maxIndex = initialTags.length - 1 const currentIndex = initialTags.findIndex(tag => tag.id === currentSelectedTag.id) initialTags.splice(currentIndex, 1) if (currentIndex + num > maxIndex) { initialTags.splice(maxIndex, 0, currentSelectedTag) } else { initialTags.splice(currentIndex + num, 0, currentSelectedTag) } this.onChange(initialTags) } // 设置显示列顺序——字段移到最下 private tableFieldMoveBottom = () => { const { initialTags = [], currentSelectedTag = {} } = this.state; const maxIndex = initialTags.length - 1 const currentIndex = initialTags.findIndex(tag => tag.id === currentSelectedTag.id) initialTags.splice(currentIndex, 1) initialTags.splice(maxIndex, 0, currentSelectedTag) this.onChange(initialTags) } render() { const { initialTags = [], currentSelectedTag = {}, } = this.state; return ( <div> <Card className="content-scroll-bar" bodyStyle={{ padding: ‘2px‘ }} style={{ width: ‘80%‘, display: ‘inline-block‘ }}> { initialTags.map(tag => ( <div className={tag.id === currentSelectedTag.id ? styles.tagClick : styles.tag} onClick={() => this.handleBlockSelected(tag)} key={tag.id}> <Checkbox disabled={tag.disabled} checked={tag.isVisible} onChange={() => this.isVisible(tag)}> <span>{tag.title}</span> </Checkbox> </div> )) } </Card> <div style={{ width: ‘20%‘, display: ‘inline-block‘ }}> <Row className={styles.tagBlockFirst}> <Col span={24} className="move-top"> <Button key="moveTop" type="primary" onClick={this.tableFieldMoveTop} >最上</Button> </Col> </Row> <Row className={styles.tagBlock}> <Col span={24} className="move-up-ten"> <Button key="moveUpTen" type="primary" onClick={() => { this.tableFieldMoveUp(10) }} >上移十条</Button> </Col> </Row> <Row className={styles.tagBlock}> <Col span={24} className="move-up-one"> <Button key="moveUpOne" type="primary" onClick={() => { this.tableFieldMoveUp(1) }} >上移一条</Button> </Col> </Row> <Row className={styles.tagBlock}> <Col span={24} className="move-down-one"> <Button key="moveDownOne" type="primary" onClick={() => { this.tableFieldMoveDown(1) }} >下移一条</Button> </Col> </Row> <Row className={styles.tagBlock}> <Col span={24} className="move-down-ten"> <Button key="moveDownTen" type="primary" onClick={() => { this.tableFieldMoveDown(10) }} >下移十条</Button> </Col> </Row> <Row className={styles.tagBlock}> <Col span={24} className="move-bottom"> <Button key="moveBottom" type="primary" onClick={this.tableFieldMoveBottom} >最下</Button> </Col> </Row> </div> </div> ) } } export default TableFieldsOrder
.tag { margin: 3px; padding: 0 8px; color: #666; font-size: 13px; line-height: 28px; background: rgba(255, 255, 255, 0.7); border: 1px solid #ccc; border-radius: 4px; } .tagClick { margin: 3px; padding: 0 8px; color: #666; font-size: 13px; line-height: 28px; background-color: beige; border: 1px solid #ccc; border-radius: 4px; } .tagBlockFirst { text-align: center; } .tagBlock { margin-top: 8px; text-align: center; }
显示的界面如下图:
CheckBox用于让用户设置要不要显示这一列。
第二种方法:拖拽排序(借助了一个基于react实现的库:react-draggable-tags)
import React, { Component } from ‘react‘; import { connect } from ‘dva‘; // @ts-ignore DraggableAreasGroup import { DraggableArea } from ‘react-draggable-tags‘; import { Checkbox } from ‘antd‘; import styles from ‘./index.less‘; import { TableListItem } from ‘@/utils/TableData‘; interface DraggableAreaProps { draggableTags: TableListItem<any>[], renderOnChange: (value: any) => void; renderIsVisible: (value: any) => void; } interface DraggableAreaState { draggableTags?: TableListItem<any>[], } @connect() class DraggableAreaView extends Component<DraggableAreaProps> { state: DraggableAreaState = { draggableTags: [], }; static getDerivedStateFromProps(props: any, state: any) { const { draggableTags = [], } = props; return { draggableTags, } } private onChange = (value: any) => { const { renderOnChange } = this.props; renderOnChange(value); }; private isVisible = (value: any) => { const { renderIsVisible } = this.props; renderIsVisible(value); }; render() { const { draggableTags = [], } = this.state; return ( // className={styles.crossArea} <div> <div className={styles.square}> <DraggableArea tags={draggableTags} // @ts-ignore render={({ tag, index }) => ( <div className={styles.tag} key={index}> <Checkbox disabled={tag.disabled} checked={tag.isVisible} onChange={() => this.isVisible(tag)}> <span>{tag.title}</span> </Checkbox> </div> )} onChange={(tags: any) => this.onChange(tags)} /> </div> </div> ) } } export default DraggableAreaView
.square { width: 100%; height: 100%; padding: 5px; //border: 1px solid #E9E9E9; //border-radius: 4px; } .tag { margin: 3px; padding: 0 8px; color: #666; font-size: 13px; line-height: 30px; background: rgba(255, 255, 255, 0.7); border: 1px solid #ccc; border-radius: 4px; } .crossArea { display: flex; .square { width: 50%; //height: 300px; &:first-child { margin-right: 10px; } } }
显示的界面如下:
这两种方法都接收同样的三个props参数,props1:initialTags(表头数组),props2:renderOnChange(调整字段顺序后的回调函数,返回一个新的顺序的表头数组),props3:renderIsVisible(checkbox变化后的回调函数,返回值为当前触发check change的表头对象)。
下面来看下父组件中是如何使用的:
<Modal maskClosable={false} width={800} style={{ top: ‘10px‘ }} title="字段显示顺序" visible={fieldOrderVisible} onCancel={this.handleCancel} destroyOnClose footer={ <div key="btn" style={{ textAlign: ‘center‘ }}> <Button onClick={this.handleCancel} key="cancel">取消</Button> <Button key="ok" loading={filedOrderOkLoading || false} onClick={this.handleOk} type="primary">确定</Button> </div> } > <TableFieldsOrder initialTags={initialTags} renderOnChange={value => this.renderOnChange(value)} renderIsVisible={value => this.renderIsVisible(value)} /> </Modal>
/** * 打开字段顺序设置弹框 */ fieldDisplayOrder = () => { const { dispatch, } = this.props; dispatch({ type: ‘FieldOrder/getListHeader‘, payload: { pageId: ‘RTDAuditDetailStat‘, }, callback: (data: any) => { this.setState({ fieldOrderVisible: true, initialTags: data, }) }, }); }; /** * 取消改动 */ private handleCancel = () => { this.getListHeader(); this.setState({ fieldOrderVisible: false }); }; /** * 确定改动 */ handleOk = () => { const { dispatch } = this.props; dispatch({ type: ‘FieldOrder/updListHeader‘, payload: { listHeaders: this.state.initialTags, pageId: ‘RTDAuditDetailStat‘, }, callback: (status: any) => { if (status) { this.setState({ fieldOrderVisible: false, }) } }, }); }; private renderOnChange = (value: any) => { this.setState({ initialTags: value, }) }; private renderIsVisible = (value: any) => { const { initialTags = [] } = this.state; const node = findListNode(initialTags, ‘id‘, value.id); node.isVisible = !node.isVisible; this.setState({ initialTags, }) };
这个功能到此就结束了,其实应该把Model弹框也封装在一起,后续再优化吧。
原文:https://www.cnblogs.com/chenbeibei520/p/13321395.html