zoukankan      html  css  js  c++  java
  • 拖拽排序和列表上下排序

    假设有一个这样的需求: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={{  '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={{  '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弹框也封装在一起,后续再优化吧。

  • 相关阅读:
    如何自己搭一个脚手架
    vue脚手架搭建流程
    深入浅出ES6教程『async函数』
    在微信小程序中使用 async/await
    理解 JavaScript 的 async/await
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/chenbeibei520/p/13321395.html
Copyright © 2011-2022 走看看