拖拽排序和列表上下排序


假設有一個這樣的需求: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彈框也封裝在一起,后續再優化吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM