React使用antd Table生成层级多选组件

一、需求

  用户对不同的应用需要有不同的权限,用户一般和角色关联在一起,新建角色的时候会选择该角色对应的应用,然后对应用分配权限。于是写了一种实现的方式。首先应用是一个二级树,一级表示的是应用分组,二级表示的是应用,这是table的最左边的数据。然后是按钮的数据,这里显示在table的头部。

二、效果图如下

  

三、具体代码

  1.RoleApplicationTable.js

import React from ‘react‘;
import RoleCheckbox from ‘components/role/RoleCheckbox‘;
import {Menu, Table, message} from ‘antd‘;
import Btn from ‘components/public/BaseBtn‘;
import {connect} from ‘react-redux‘;
import ‘styles/less/personType.less‘;
import ‘styles/less/basebtn.less‘;
import Map from ‘components/role/Map‘;
import { operationRoleAppBtn, queryRoleAppBtnData} from ‘actions/role‘;

var mapStateToProps = function(state){
  return {
    roleData: state.getRole
  }
};
//规范属性类型
var propTypes = {
  personTypes: React.PropTypes.object,
  dispatch : React.PropTypes.func
};
class RoleApplicationTable extends React.Component {
    constructor(props) {
    super(props);
    this.state = {

    };
    this.chooseApp = this.chooseApp.bind(this);
    this.addColName = this.addColName.bind(this);
    this.addDataPid = this.addDataPid.bind(this);
    this.onChecked = this.onChecked.bind(this);
    this.addChildrenRow = this.addChildrenRow.bind(this);
    this.addData = this.addData.bind(this);
    this.isGroupRow = this.isGroupRow.bind(this);
    this.checkGroupAndColumnState = this.checkGroupAndColumnState.bind(this);//确保 组全选 和 列 全选

    this.cid = 0;
    this.rowNum = 0;
    this.colNum = 0;

    //map
    this.checkboxIdMapState= new Map();//checkboxId 映射 State
    this.parentRow = new Map();//每个checkboxId节点 对应最左边的哪个应用
    this.parentCol = new Map();//每个checkboxId节点 对应最上边的哪个按钮
    this.childrenRow = new Map();//当前行的所有子行
    this.checkboxIdMapData = new Map();//每个checkbox对应的 appid,btnGroupId

    //保存数据
    this.checked = null;//标识数据是 新增 还是 删除
    this.dataQueue = [];// appid,btngroupId队列

    //测试数据
    this.appData = [{name: ‘报表‘,id: "456",key: ‘5‘, children: [{ name: ‘合同价款‘, id: "45xx61", key: ‘6‘, },{ name: ‘合同台账‘, id: "45xf61", key: ‘7‘, }], }, { name: ‘图标‘, id: "789", key: ‘1‘, children: [{ name: ‘小图标‘, id: "45xx60", key: ‘4‘ },{ name: ‘大图标‘,  id: "4xx560", key: ‘8‘ }] }];
    this.btnGroupColumns = [{id: ‘12xx3‘, name: ‘小部件‘, colname: ‘name‘}, {id:‘43xx5‘, name:‘显示‘}, {id:‘43xfffx5‘, name:‘test‘}];
    }

  componentDidMount() {
    //const roleId = ‘4028968156b025da0156b027d0180000‘;
    const roleId = this.props.roleId;
    if(roleId) {//通过角色id加载 数据
      const { dispatch } = this.props;
      const querydata = {roleId: roleId};
      dispatch(queryRoleAppBtnData(querydata));
    }
  }

  componentWillReceiveProps(nextProps) {
    const {roleData} = nextProps;
    if (roleData.msg) {
      if(roleData.msg.indexOf(‘成功‘) >= 0)
        message.success(roleData.msg, 5);
      else if(roleData.msg.indexOf(‘失败‘) >= 0)
        message.error(roleData.msg, 5);
      else
        message.info(roleData.msg, 5);
      // if (roleData.msg == ‘保存成功‘) {//角色保存成功后 仍然留在当前页面, 继续 角色按钮组权限
      //   this.props.history.pushState(null, ‘rolecenter‘);
      // }
    }
  }

  chooseApp(){
    this.props.chooseApp();
  }

  sendCheckData(){
    const { dispatch } = this.props;
    const queryData = {
      vos: this.dataQueue,//对应后端的字段
    };
    dispatch(operationRoleAppBtn(this.checked, queryData));
  }

  ////////////////////////////////////////////////////////////////////////////////

  addChildrenRow(appData){//添加所有子行 标识
    if(!appData) return;
    for(var i=0; i<appData.length; ++i) {//获取行头的checkboxId
      this.rowNum++;//获取行号
      var curRowHeadCheckboxId = appData[i].name.split(‘_‘)[1];
      var childrenRow = this.childrenRow;
      if(!childrenRow.get(curRowHeadCheckboxId)) childrenRow.put(curRowHeadCheckboxId, []);
      this.addChildrenRow(appData[i].children);
      childrenRow.get(curRowHeadCheckboxId).push(curRowHeadCheckboxId);//加入当前行
      if(appData[i].children) {//加入子行
        for(var j=0; j<appData[i].children.length; ++j) {
          var childCurRowHeadCheckboxId = appData[i].children[j].name.split(‘_‘)[1];
          var descendants = childrenRow.get(childCurRowHeadCheckboxId);//孙子们节点
          for(var k=0; k<descendants.length; ++k){
            childrenRow.get(curRowHeadCheckboxId).push(descendants[k]);
          }
        }
      }
    }
  }

  addDataPid(btnGroupColumns, appData) {//生成新的列, 并且为非表头的每一个单元格设置固定 id,(防止表格渲染时 id发生变化)
    if(!appData) return;
    for(var i=0; i<appData.length; ++i) {
      for(var j=0; j<btnGroupColumns.length; ++j) {
        if(!appData[i][btnGroupColumns[j].colname]) {
          appData[i][btnGroupColumns[j].colname] = btnGroupColumns[j].id + ‘_‘ + (++this.cid);//为这一行数据添加新的列

          //判断应用对应的按钮是否已经选择上, judgeDefaultChecked

          if(appData[i].select && appData[i].select[btnGroupColumns[j].id]) {//btnGroupColumns[j].id == btnGroupId
            this.checkboxIdMapState.put(this.cid, true);
          } else {
            this.checkboxIdMapState.put(this.cid, false);
          }
        } else if(btnGroupColumns[j].colname == ‘name‘){
          if(appData[i][btnGroupColumns[j].colname].indexOf(‘_‘) >= 0) continue;
          appData[i][btnGroupColumns[j].colname] += ‘_‘ + (++this.cid);
          this.checkboxIdMapState.put(this.cid, false);
        }
      }
      this.addDataPid(btnGroupColumns, appData[i].children);
    }
  }

  addColName(btnGroupColumns, appData){
    if(btnGroupColumns) {
      btnGroupColumns.map((elem, index)=> {
        if(!elem.colname) {
          elem.colname = elem.id;
        }
        elem.cid = ++this.cid;
      });
    }

    if(appData) {
      this.addDataPid(btnGroupColumns, appData);
      /////清空数据
      var keySet = this.childrenRow.keySet();
      for(var key in keySet){
        if(this.childrenRow.get(keySet[key]) && this.childrenRow.get(keySet[key]).length)
          this.childrenRow.get(keySet[key]).length = 0;
      }
      /////总行数
      this.rowNum = 0;
      this.addChildrenRow(appData);
      ++this.rowNum;
      /////判断应用对应的checkbox是否选中,列头对应的checkbox是否选中
      this.checkGroupAndColumnState();
    }
  }

  addData(cid, checked){
    var curCheckboxData = this.checkboxIdMapData.get(cid);
    if(curCheckboxData) {
      var curQueueData = {
        roleId: this.props.roleId,
        btnGroupId: curCheckboxData.btnGroupId,
        appId: curCheckboxData.appId,
      };
      this.dataQueue.push(curQueueData);
    }
  }

  isGroupRow(cid){//判断是否为分组
    //第一行当做分组
    if(parseInt((cid-1)/this.colNum)*this.colNum+1 == 1) return true;

    const parentRow = this.parentRow;
    const childrenRow = this.childrenRow;
    var curRowHeadCheckboxId = parentRow.get(cid) ? parentRow.get(cid) : parseInt((cid-1)/this.colNum)*this.colNum+1;//通过cid 和 curRowHeadCheckboxId获取到cid对应的checkbox到左边的距离
    var rowIds = childrenRow.get(curRowHeadCheckboxId);//所有子行的行头的 checkboxId
    return rowIds.length > 1 ? true : false;
  }

  checkGroupAndColumnState() {
    const childrenRow = this.childrenRow;
    const checkboxIdMapState = this.checkboxIdMapState;
    const colNum = this.colNum;
    const rowNum = this.rowNum;

    const rowState = [];

    for(var i=0; i<=rowNum; ++i)
      rowState.push(true)//默认所有的行全选
    rowState[1] = false;

    for(var row=2; row <= rowNum; ++row) {
      var cb = (row-1)*colNum+2;//这一行从第2个 checkbox 开始
      if(this.isGroupRow(cb)) {//分组行,不算入
        rowState[row] = false;
        continue;
      }
      var ce = row*colNum;
      var curRowState = true;//默认这一行全选
      for(var cid=cb; cid<=ce; ++cid) {//遍历这一行
        if(checkboxIdMapState.get(cid) == false) {
          curRowState = false;
          break;
        }
      }
      rowState[row] = curRowState;
      if(rowState[row] == true) {//应用对应的checkbox选中
        checkboxIdMapState.put((row-1)*colNum+1, true);
      } else {
        checkboxIdMapState.put((row-1)*colNum+1, false);
      }
    }

    //判断分组是否选中
    for(var row=2; row <= rowNum; ++row) {
      const cid = (row-1)*colNum+1;//每一行的第一个
      if(!this.isGroupRow(cid)) continue;
      //计算分组行
      var cids = childrenRow.get(cid);
      var groupState = true;//默认这个分组被选中
      for(var i=0; i<cids.length; ++i){
        if(cids[i] != cid) {//不是分组行
          var cur_row = (cids[i]-1)/this.colNum+1;
          if(rowState[cur_row] == false) {
            groupState = false;
            break;
          }
        }
      }
      for(var cur_cid=cid; cur_cid <= row*colNum; ++cur_cid){//当前分组行的 checkbox 状态
        checkboxIdMapState.put(cur_cid, groupState);
      }
      if(groupState == false) {//如果当前分组行没有状态改变,查看这一行的某一个分组列是否有变化
        const childRowNum = cids.length-1;
        for(var curRowCid = cid; curRowCid<cid+this.colNum; ++curRowCid) {//遍历这一分组行的checkboxId
          var curColState = true;
          for(var childRowCid = curRowCid+this.colNum, cnt = 0; cnt < childRowNum; childRowCid += this.colNum, ++cnt) {
            if(checkboxIdMapState.get(childRowCid) == false) {
              curColState = false;
              break;
            }
          }
          checkboxIdMapState.put(curRowCid, curColState);
        }
      }
    }

    // 判断列 是否被选中
    if(rowNum > 1) {
      for(var col=1; col<=colNum; ++col) {
        var curColState = true;
        for(var cid=col+colNum; cid<=colNum*rowNum; cid+=colNum){
          if(checkboxIdMapState.get(cid) == false) {
            curColState = false;
            break;
          }
        }
        var cid = col;
        checkboxIdMapState.put(cid, curColState);//这一列的状态
      }
    }

  }

  onChecked(cid, btnGroupId, appId, checked){//checkboxId, 按钮id,应用id
    const checkboxIdMapState = this.checkboxIdMapState;
    const parentRow = this.parentRow;
    const parentCol = this.parentCol;
    const childrenRow = this.childrenRow;
    const colNum = this.colNum;
    const rowNum = this.rowNum;
    //清空数据队列
    this.dataQueue.length = 0;
    //标识当前的操作
    this.checked = checked;

    if(btnGroupId == null && appId == null) {
      for(var cur_cid=1; cur_cid<=colNum*rowNum; ++cur_cid) {
        checkboxIdMapState.put(cur_cid, checked);
        if(!this.isGroupRow(cur_cid))
          this.addData(cur_cid, checked);
      }
    } else if(btnGroupId == null) {//appId 不为null, 这一行全选
      var rowHeadCheckboxIds = childrenRow.get(cid);//所有子行的行头的 checkboxId
      for(var i=0; i<rowHeadCheckboxIds.length; ++i) {
        var cur_cid = rowHeadCheckboxIds[i];
        var cur_row_max_cid = parseInt(cur_cid) + colNum;
        while(cur_cid < cur_row_max_cid){
          checkboxIdMapState.put(cur_cid, checked);
          if(!this.isGroupRow(cur_cid))
            this.addData(cur_cid, checked);
          ++cur_cid;
        }
      }
    } else if(appId == null) {//btnId不为null,这一列全部check
      var cur_cid = cid;
      while(cur_cid <= rowNum*colNum) {
        checkboxIdMapState.put(cur_cid, checked);
        if(!this.isGroupRow(cur_cid))
          this.addData(cur_cid, checked);
        cur_cid += colNum;
      }
    } else {//都不为null
      var curRowHeadCheckboxId = parentRow.get(cid);//通过cid 和 curRowHeadCheckboxId获取到cid对应的checkbox到左边的距离
      var rowIds = childrenRow.get(curRowHeadCheckboxId);//所有子行的行头的 checkboxId
      for(var i=0; i<rowIds.length; ++i) {//这一列全部check
        var cur_cid = parseInt(rowIds[i]) + (cid-curRowHeadCheckboxId);
        checkboxIdMapState.put(cur_cid, checked);
        if(!this.isGroupRow(cur_cid))
          this.addData(cur_cid, checked);
      }

    }
    this.setState({});
    this.sendCheckData();//发送数据
  }

  ////////////////////////////////////////////////////////////////////////////////

    render() {
    const appData = this.appData;
    const btnGroupColumns = this.btnGroupColumns;
    console.log(appData)
    let self = this;
    this.cid = 0;
    this.colNum = btnGroupColumns.length;//获得列宽
    const checkboxIdMapState = this.checkboxIdMapState;
    const parentRow = this.parentRow;
    const parentCol = this.parentCol
    if(btnGroupColumns) {
      this.addColName(btnGroupColumns, appData);//对应用的数据进行一个简单的处理

      btnGroupColumns.map((elem, index)=> {
        //elem.colname==‘name‘ ? null : elem.id, 默认左上角的id 没有 appId 和 btnGroupId
        elem.title= <RoleCheckbox btnGroupId={elem.colname==‘name‘ ? null : elem.id} appId={null} cid={elem.cid} onChecked={self.onChecked} checked={checkboxIdMapState.get(elem.cid)} title={elem.name}/>,
        elem.key = elem.dataIndex = elem.colname;
        elem.render = function(text, record, index){// text的值 == 对应表头列的Id == elem.id
          var contents = text.split(‘_‘);
          text = contents[0];
          var cur_cid = contents[1];//当前列顶端 checkboxId

          //判断是否是第一列
          if(record.name.split(‘_‘)[0] != text) {//不是第一列
            var leftCheckBoxId = record.name.split(‘_‘)[1];
            parentRow.put(cur_cid, leftCheckBoxId);//该 checkboxId 对应的 (应用Id == leftCheckBoxId)

            //加入每个checkbox  要传输的数据(appId, btnGroupId)
            self.checkboxIdMapData.put(cur_cid, {appId: record.id, btnGroupId: elem.id})
          }
          //该 checkboxId 对应的 最上边的 checkboxId
          parentCol.put(cur_cid, elem.cid);//该 checkboxId 对应的 (按钮Id == elem.cid)

          //record.name.split(‘_‘)[0] 最原始的 name 的value
          return <RoleCheckbox btnGroupId={record.name.split(‘_‘)[0] == text ? null : elem.id} appId={record.id} cid={cur_cid} onChecked={self.onChecked} checked={checkboxIdMapState.get(cur_cid)} title={text==elem.id ? null : text}/>
        }
      });
    }

    return (
      <div>
        <Btn iconName="icon-add" onClick={this.chooseApp} btnClass="add-btn" btnName="选择应用"/>
        <Table
          indentSize={15}
          className="personType-table"
          columns={btnGroupColumns}
          dataSource={appData}
          pagination={false}
        />
      </div>
    );
  }
}
module.exports = RoleApplicationTable;
RoleApplicationTable.propTypes = propTypes;
module.exports = connect(mapStateToProps)(RoleApplicationTable);

  利用antd table实现层级多选组件。

  具体思路:

addDataPid(btnGroupColumns, appData) {//生成新的列, 并且为非表头的每一个单元格设置固定 id,(防止表格渲染时 id发生变化)
  if(!appData) return;
  for(var i=0; i<appData.length; ++i) {
    for(var j=0; j<btnGroupColumns.length; ++j) {
      if(!appData[i][btnGroupColumns[j].colname]) {
        appData[i][btnGroupColumns[j].colname] = btnGroupColumns[j].id + ‘_‘ + (++this.cid);//为这一行数据添加新的列

        //判断应用对应的按钮是否已经选择上, judgeDefaultChecked

        if(appData[i].select && appData[i].select[btnGroupColumns[j].id]) {//btnGroupColumns[j].id == btnGroupId
          this.checkboxIdMapState.put(this.cid, true);
        } else {
          this.checkboxIdMapState.put(this.cid, false);
        }
      } else if(btnGroupColumns[j].colname == ‘name‘){
        if(appData[i][btnGroupColumns[j].colname].indexOf(‘_‘) >= 0) continue;
        appData[i][btnGroupColumns[j].colname] += ‘_‘ + (++this.cid);
        this.checkboxIdMapState.put(this.cid, false);
      }
    }
    this.addDataPid(btnGroupColumns, appData[i].children);
  }
}

addColName(btnGroupColumns, appData){//为每一列添加 映射字段 colname
  if(btnGroupColumns) {
    btnGroupColumns.map((elem, index)=> {
      if(!elem.colname) {
        elem.colname = elem.id;
      }
      elem.cid = ++this.cid;
    });
  }

  if(appData) {
    this.addDataPid(btnGroupColumns, appData);
    /////清空数据
    var keySet = this.childrenRow.keySet();
    for(var key in keySet){
      if(this.childrenRow.get(keySet[key]) && this.childrenRow.get(keySet[key]).length)
        this.childrenRow.get(keySet[key]).length = 0;
    }
    /////总行数
    this.rowNum = 0;
    this.addChildrenRow(appData);
    ++this.rowNum;
    /////判断应用对应的checkbox是否选中,列头对应的checkbox是否选中
    this.checkGroupAndColumnState();
  }
}

  2.RoleCheckbox.js

import {Checkbox} from ‘antd‘;
import React from ‘react‘;
class RoleCheckbox extends React.Component{
  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
  }

  onChange(e){
    const cid = this.props.cid;
    const btnGroupId = this.props.btnGroupId;
    const appId = this.props.appId;
    this.props.onChecked(cid, btnGroupId, appId, e.target.checked);
  }

    render() {
    const checked = this.props.checked;
    const title = this.props.title;
    const cid = this.props.cid;
        return(
        <div>
          <Checkbox checked={checked} onChange={this.onChange}/>{title}
        </div>
        );
    }
}
module.exports = RoleCheckbox;

  封装antd 的Checkbox组件

  3.Map.js

class Map {
    constructor(){
        this.container = new Object();
    }

    put(key, value){
        this.container[key] = value;
    }

    get(key){
        return this.container[key];
    }

    keySet() {
        var keyset = new Array();
        var count = 0;
        for (var key in this.container) {
            // 跳过object的extend函数
            if (key == ‘extend‘) {
            continue;
        }
            keyset[count] = key;
            count++;
        }
        return keyset;
    }

    size() {
        var count = 0;
        for (var key in this.container) {
            // 跳过object的extend函数
            if (key == ‘extend‘){
                continue;
            }
            count++;
        }
        return count;
    }

    remove(key) {
        delete this.container[key];
    }

    toString(){
        var str = "";
        for (var i = 0, keys = this.keySet(), len = keys.length; i < len; i++) {
            str = str + keys[i] + "=" + this.container[keys[i]] + ";\n";
        }
        return str;
    }
}

module.exports = Map;

  js实现的Map工具类。

四、需求变更

  功能虽然完成了,但是总是避免不了需求的变更。要求选择左边应用对应的checkbox时,不在操作该应用对应的按钮的checkbox,也就是整个行不是全选了。应用对应的checkbox用来进行删除操作。

  1.改变后的Table效果

  

  2.RoleApplicationTable.js

import React from ‘react‘;
import RoleCheckbox from ‘components/role/RoleCheckbox‘;
import {Menu, Table, message, Modal} from ‘antd‘;
const confirm = Modal.confirm;
import Btn from ‘components/public/BaseBtn‘;
import {connect} from ‘react-redux‘;
import ‘styles/less/personType.less‘;
import ‘styles/less/basebtn.less‘;
import Map from ‘components/role/Map‘;
import { operationRoleAppBtn, queryRoleAppBtnData, deleteAppAction} from ‘actions/role‘;

var mapStateToProps = function(state){
  return {
    roleData: state.getRole
  }
};
//规范属性类型
var propTypes = {
  personTypes: React.PropTypes.object,
  dispatch : React.PropTypes.func
};
class RoleApplicationTable extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      isEdit: true,
    };
    this.chooseApp = this.chooseApp.bind(this);
    this.addColName = this.addColName.bind(this);
    this.addDataPid = this.addDataPid.bind(this);
    this.onChecked = this.onChecked.bind(this);
    this.addChildrenRow = this.addChildrenRow.bind(this);
    this.addAppBtnData = this.addAppBtnData.bind(this);
    this.addAppData = this.addAppData.bind(this);
    this.isGroupRow = this.isGroupRow.bind(this);
    this.checkGroupAndColumnState = this.checkGroupAndColumnState.bind(this);//确保 组全选 和 列 全选
    this.deleteApp = this.deleteApp.bind(this);
    this.showConfirm = this.showConfirm.bind(this);
    this.initRoleAppBtnData = this.initRoleAppBtnData.bind(this);
    this.cancelChooseState = this.cancelChooseState.bind(this);
    this.saveCheckedAppBtn = this.saveCheckedAppBtn.bind(this);
    this.afterSaveCheckedAppBtn = this.afterSaveCheckedAppBtn.bind(this);

    this.cid = 0;
    this.rowNum = 0;
    this.colNum = 0;

    //map
    this.checkboxIdMapState= new Map();//checkboxId 映射 State
    this.parentRow = new Map();//每个checkboxId节点 对应最左边的哪个应用
    this.parentCol = new Map();//每个checkboxId节点 对应最上边的哪个按钮
    this.childrenRow = new Map();//当前行的所有子行
    this.checkboxIdMapAppBtnData = new Map();//每个checkbox对应的 appid,btnGroupId
    this.checkboxIdMapAppData = new Map();//记录被选中的应用

    //保存数据
    this.dataQueue = [];// appid,btngroupId队列
    //删除应用
    this.deleteAppIds = [];

    //测试数据
    this.appData = [{name: ‘报表‘,id: "456",key: ‘5‘, children: [{ name: ‘合同价款‘, id: "45xx61", key: ‘6‘, },{ name: ‘合同台账‘, id: "45xf61", key: ‘7‘, }], }, { name: ‘图标‘, id: "789", key: ‘1‘, children: [{ name: ‘小图标‘, id: "45xx60", key: ‘4‘ },{ name: ‘大图标‘,  id: "4xx560", key: ‘8‘ }] }];
    this.btnGroupColumns = [{id: ‘12xx3‘, name: ‘小部件‘, colname: ‘name‘}, {id:‘43xx5‘, name:‘显示‘}, {id:‘43xfffx5‘, name:‘test‘}];
    }

  //确认提示框
  showConfirm(title,message,dispatch,functionT,functionQueryData) {
    confirm({
      title: title,
      content: message,
      onOk() {
        dispatch(functionT(functionQueryData));
      },
      onCancel() {

      }
    });
  }

  componentDidMount() {
    //const roleId = ‘4028968156b025da0156b027d0180000‘;
    this.initRoleAppBtnData();
  }

  initRoleAppBtnData(){
    const roleId = this.props.roleId;
    if(roleId) {//通过角色id加载 数据
      const { dispatch } = this.props;
      const querydata = {roleId: roleId};
      dispatch(queryRoleAppBtnData(querydata));
    }
  }

  cancelChooseState(){//取消权限的更改
    this.initRoleAppBtnData();
  }

  componentWillReceiveProps(nextProps) {
    const {roleData} = nextProps;
    if (roleData.msg) {
      if(roleData.msg.indexOf(‘成功‘) >= 0)
        message.success(roleData.msg, 5);
      else if(roleData.msg.indexOf(‘失败‘) >= 0)
        message.error(roleData.msg, 5);
      else
        message.info(roleData.msg, 5);
      // if (roleData.msg == ‘保存成功‘) {//角色保存成功后 仍然留在当前页面, 继续 角色按钮组权限
      //   this.props.history.pushState(null, ‘rolecenter‘);
      // }
    }
  }

  chooseApp(){
    this.props.chooseApp();
  }

  sendCheckData(){
    const { dispatch } = this.props;
    const queryData = {
      ‘vos‘: this.dataQueue,//对应后端的字段
      ‘roleId‘: this.props.roleId,
    };
    dispatch(operationRoleAppBtn(queryData, this.afterSaveCheckedAppBtn));
  }

  ////////////////////////////////////////////////////////////////////////////////

  addChildrenRow(appData){//添加所有子行 标识
    if(!appData) return;
    for(var i=0; i<appData.length; ++i) {//获取行头的checkboxId
      this.rowNum++;//获取行号
      var curRowHeadCheckboxId = appData[i].name.split(‘_‘)[1];
      var childrenRow = this.childrenRow;
      if(!childrenRow.get(curRowHeadCheckboxId)) childrenRow.put(curRowHeadCheckboxId, []);
      this.addChildrenRow(appData[i].children);
      childrenRow.get(curRowHeadCheckboxId).push(curRowHeadCheckboxId);//加入当前行
      if(appData[i].children) {//加入子行
        for(var j=0; j<appData[i].children.length; ++j) {
          var childCurRowHeadCheckboxId = appData[i].children[j].name.split(‘_‘)[1];
          var descendants = childrenRow.get(childCurRowHeadCheckboxId);//孙子们节点
          for(var k=0; k<descendants.length; ++k){
            childrenRow.get(curRowHeadCheckboxId).push(descendants[k]);
          }
        }
      }
    }
  }

  addDataPid(btnGroupColumns, appData) {//生成新的列, 并且为非表头的每一个单元格设置固定 id,(防止表格渲染时 id发生变化)
    if(!appData) return;
    for(var i=0; i<appData.length; ++i) {
      for(var j=0; j<btnGroupColumns.length; ++j) {
        if(!appData[i][btnGroupColumns[j].colname]) {
          appData[i][btnGroupColumns[j].colname] = btnGroupColumns[j].id + ‘_‘ + (++this.cid);//为这一行数据添加新的列

          //判断应用对应的按钮是否已经选择上, judgeDefaultChecked

          if(appData[i].select && appData[i].select[btnGroupColumns[j].id]) {//btnGroupColumns[j].id == btnGroupId
            this.checkboxIdMapState.put(this.cid, true);
          } else {
            this.checkboxIdMapState.put(this.cid, false);
          }
        } else if(btnGroupColumns[j].colname == ‘name‘){
          if(appData[i][btnGroupColumns[j].colname].indexOf(‘_‘) >= 0) continue;
          appData[i][btnGroupColumns[j].colname] += ‘_‘ + (++this.cid);
          this.checkboxIdMapState.put(this.cid, false);
        }
      }
      this.addDataPid(btnGroupColumns, appData[i].children);
    }
  }

  addColName(btnGroupColumns, appData){
    if(btnGroupColumns) {
      btnGroupColumns.map((elem, index)=> {
        if(!elem.colname) {
          elem.colname = elem.id;
        }
        elem.cid = ++this.cid;
      });
    }

    if(appData) {
      this.addDataPid(btnGroupColumns, appData);
      /////清空数据
      var keySet = this.childrenRow.keySet();
      for(var key in keySet){
        if(this.childrenRow.get(keySet[key]) && this.childrenRow.get(keySet[key]).length)
          this.childrenRow.get(keySet[key]).length = 0;
      }
      /////总行数
      this.rowNum = 0;
      this.addChildrenRow(appData);
      ++this.rowNum;
      /////判断应用对应的checkbox是否选中,列头对应的checkbox是否选中
      this.checkGroupAndColumnState();
    }
  }

  addAppBtnData(cid){
    var curCheckboxData = this.checkboxIdMapAppBtnData.get(cid);
    if(curCheckboxData) {
      var curQueueData = {
        roleId: this.props.roleId,
        btnGroupId: curCheckboxData.btnGroupId,
        appId: curCheckboxData.appId,
      };
      this.dataQueue.push(curQueueData);
    }
  }

  addAppData(cid){
    var checked = this.checkboxIdMapState.get(cid);
    if(checked == false) return;
    var curAppId = this.checkboxIdMapAppData.get(cid);
    if(curAppId) {
      var curQueueData = {
        roleId: this.props.roleId,
        appId: curAppId,
      };
      this.deleteAppIds.push(curQueueData);
    }
  }

  isGroupRow(cid){//判断是否为分组
    //第一行当做分组
    if(parseInt((cid-1)/this.colNum)*this.colNum+1 == 1) return true;

    const parentRow = this.parentRow;
    const childrenRow = this.childrenRow;
    var curRowHeadCheckboxId = parentRow.get(cid) ? parentRow.get(cid) : parseInt((cid-1)/this.colNum)*this.colNum+1;//通过cid 和 curRowHeadCheckboxId获取到cid对应的checkbox到左边的距离
    var rowIds = childrenRow.get(curRowHeadCheckboxId);//所有子行的行头的 checkboxId
    return rowIds.length > 1 ? true : false;
  }

  checkGroupAndColumnState() {
    const childrenRow = this.childrenRow;
    const checkboxIdMapState = this.checkboxIdMapState;
    const colNum = this.colNum;
    const rowNum = this.rowNum;

    const rowState = [];

    for(var i=0; i<=rowNum; ++i)
      rowState.push(true)//默认所有的行全选
    rowState[1] = false;

    //判断分组列
    for(var row=2; row <= rowNum; ++row) {
      const cid = (row-1)*colNum+1;//每一行的第一个
      if(!this.isGroupRow(cid)) continue;
      var cids = childrenRow.get(cid);
      const childRowNum = cids.length-1;
      for(var curRowCid = cid; curRowCid<cid+this.colNum; ++curRowCid) {//遍历这一分组行的checkboxId
        var curColState = true;
        for(var childRowCid = curRowCid+this.colNum, cnt = 0; cnt < childRowNum; childRowCid += this.colNum, ++cnt) {
          if(checkboxIdMapState.get(childRowCid) == false) {
            curColState = false;
            break;
          }
        }
        checkboxIdMapState.put(curRowCid, curColState);
      }
    }

    // 判断列 是否被选中
    if(rowNum > 1) {
      for(var col=1; col<=colNum; ++col) {
        var curColState = true;
        for(var cid=col+colNum; cid<=colNum*rowNum; cid+=colNum){
          if(checkboxIdMapState.get(cid) == false) {
            curColState = false;
            break;
          }
        }
        var cid = col;
        checkboxIdMapState.put(cid, curColState);//这一列的状态
      }
    } else if(rowNum == 1) {//每一列的状态清空
      for(var cid = 1; cid <= this.colNum; ++cid)
        checkboxIdMapState.put(cid, false);
    }

  }

  onChecked(cid, btnGroupId, appId, checked){//checkboxId, 按钮id,应用id
    if(this.state.isEdit == true && cid%this.colNum != 1) {//第一列为应用列,随时可以编辑
      message.info(‘请进入编辑状态‘, 2);
      return ;
    }
    const checkboxIdMapState = this.checkboxIdMapState;
    const parentRow = this.parentRow;
    const parentCol = this.parentCol;
    const childrenRow = this.childrenRow;
    const colNum = this.colNum;
    const rowNum = this.rowNum;

    if(btnGroupId == null && appId == null) {
      for(var cur_cid=1; cur_cid<=colNum*rowNum; cur_cid+=colNum) {
        checkboxIdMapState.put(cur_cid, checked);
      }
    } else if(btnGroupId == null) {//appId 不为null, 所有的子应用全选
      var rowHeadCheckboxIds = childrenRow.get(cid);//所有子行的行头的 checkboxId(对应应用)
      for(var i=0; i<rowHeadCheckboxIds.length; ++i) {
        var cur_cid = rowHeadCheckboxIds[i];
        checkboxIdMapState.put(cur_cid, checked);
      }
    } else if(appId == null) {//btnId不为null,这一列全部check
      var cur_cid = cid;
      while(cur_cid <= rowNum*colNum) {
        checkboxIdMapState.put(cur_cid, checked);
        cur_cid += colNum;
      }
    } else {//都不为null
      var curRowHeadCheckboxId = parentRow.get(cid);//通过cid 和 curRowHeadCheckboxId获取到cid对应的checkbox到左边的距离
      var rowIds = childrenRow.get(curRowHeadCheckboxId);//所有子行的行头的 checkboxId
      for(var i=0; i<rowIds.length; ++i) {//这一列全部check
        var cur_cid = parseInt(rowIds[i]) + (cid-curRowHeadCheckboxId);
        checkboxIdMapState.put(cur_cid, checked);
      }

    }
    this.setState({});
  }

  deleteApp(){
    this.deleteAppIds.length = 0;//清空数据
    const {dispatch} = this.props;
    for(var cid = 1; cid <= this.rowNum*this.colNum; cid += this.colNum) {
      if(!this.isGroupRow(cid)) {
        this.addAppData(cid);
      }
    }

    if(this.deleteAppIds.length == 0) {
      message.success(‘请选择应用‘, 5);
      return;
    }

    const queryData = {
      vos: this.deleteAppIds,
    }

    this.showConfirm(‘删除应用‘, ‘确定删除应用?‘, dispatch, deleteAppAction, queryData);
  }

  afterSaveCheckedAppBtn(){
    this.setState({
      isEdit: true,
    });
  }

  saveCheckedAppBtn(){
    if(this.state.isEdit == true) {
      this.setState({
        isEdit: false,
      });
      return ;
    }
    //清空数据队列
    this.dataQueue.length = 0;
    for(var cid = this.colNum+1; cid <= this.colNum*this.rowNum; ++cid) {//从第二行的checkbox 开始
      if(this.isGroupRow(cid)) {
        cid += this.colNum;
      }
      if(cid%this.colNum != 1) {//第一列为 应用列
        if(this.checkboxIdMapState.get(cid) == true)
          this.addAppBtnData(cid);
      }
    }

    this.sendCheckData();
  }

  ////////////////////////////////////////////////////////////////////////////////

    render() {
    let {roleData} = this.props;
    var appData = [];
    var btnGroupColumns = [];
    if(roleData.permissiondData) {
      if(roleData.permissiondData.listAppBtnGroup) {
        btnGroupColumns = roleData.permissiondData.listAppBtnGroup;
      }
      if(roleData.permissiondData.listPermissionApp) {
        appData = roleData.permissiondData.listPermissionApp;
      }
    }
    // const appData = this.appData;
    // const btnGroupColumns = this.btnGroupColumns;
    // console.log(appData)
    let self = this;
    this.cid = 0;
    this.colNum = btnGroupColumns.length;//获得列宽
    const checkboxIdMapState = this.checkboxIdMapState;
    const parentRow = this.parentRow;
    const parentCol = this.parentCol
    if(btnGroupColumns) {
      this.addColName(btnGroupColumns, appData);//对应用的数据进行一个简单的处理

      btnGroupColumns.map((elem, index)=> {
        //elem.colname==‘name‘ ? null : elem.id, 默认左上角的id 没有 appId 和 btnGroupId
        elem.title= <RoleCheckbox btnGroupId={elem.colname==‘name‘ ? null : elem.id} appId={null} cid={elem.cid} onChecked={self.onChecked} checked={checkboxIdMapState.get(elem.cid)} title={elem.name}/>,
        elem.key = elem.dataIndex = elem.colname;
        elem.render = function(text, record, index){// text的值 == 对应表头列的Id == elem.id
          var contents = text.split(‘_‘);
          text = contents[0];
          var cur_cid = contents[1];//当前列顶端 checkboxId

          //判断是否是第一列
          if(record.name.split(‘_‘)[0] != text) {//不是第一列
            var leftCheckBoxId = record.name.split(‘_‘)[1];
            parentRow.put(cur_cid, leftCheckBoxId);//该 checkboxId 对应的 (应用Id == leftCheckBoxId)

            //加入每个checkbox  要传输的数据(appId, btnGroupId)
            self.checkboxIdMapAppBtnData.put(cur_cid, {appId: record.id, btnGroupId: elem.id})
          } else {//应用列
            self.checkboxIdMapAppData.put(cur_cid, record.id);
          }
          //该 checkboxId 对应的 最上边的 checkboxId
          parentCol.put(cur_cid, elem.cid);//该 checkboxId 对应的 (按钮Id == elem.cid)

          //record.name.split(‘_‘)[0] 最原始的 name 的value
          return <RoleCheckbox btnGroupId={record.name.split(‘_‘)[0] == text ? null : elem.id} appId={record.id} cid={cur_cid} onChecked={self.onChecked} checked={checkboxIdMapState.get(cur_cid)} title={text==elem.id ? null : text}/>
        }
      });
    }

    return (
      <div>
        <Btn iconName="icon-add" isdisabled={self.props.roleId ? false : true} onClick={this.chooseApp} btnClass="add-btn" btnName="选择应用"/>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <Btn iconName="icon-jianhao" isdisabled={self.props.roleId ? false : true} btnClass="delete-btn" btnName="删除应用" onClick={self.deleteApp}/>
        <Table style={{marginTop: "10px", marginBottom: "10px"}}
          indentSize={15}
          className="personType-table"
          columns={btnGroupColumns}
          dataSource={appData}
          pagination={false}
        />
        <div style={{display: self.rowNum > 1 ? ‘‘ : ‘none‘}}>
          <Btn btnClass="save-btn" btnName={self.state.isEdit == true ? "编辑" : "保存"} onClick={this.saveCheckedAppBtn}/>
          &nbsp;&nbsp;&nbsp;&nbsp;
          <Btn btnClass="cancel-btn" btnName="取消" onClick={self.cancelChooseState}/>
        </div>
      </div>
    );
  }
}
module.exports = RoleApplicationTable;
RoleApplicationTable.propTypes = propTypes;
module.exports = connect(mapStateToProps)(RoleApplicationTable);

五、心得体会

  最近使用react + redux + webpack进行web开发,感觉进步很快,已经熟悉了基本的流程。后续要研究一下webpack。

时间: 2024-10-10 18:31:49

React使用antd Table生成层级多选组件的相关文章

【共享单车】—— React后台管理系统开发手记:AntD Table基础表格

前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. 一.基础表格 Table组件基础Api bordered属性:是否展示外边框和列边框 columns属性:表格列的配置描述(即表头) dataSource属性:数据数组 pagination属性:分页器,设为 false 时不展示和进行分页 <Card title="基础表格"&g

【共享单车】—— React后台管理系统开发手记:AntD Table高级表格

前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. 一.头部固定 scroll属性:设置横向或纵向滚动,也可用于指定滚动区域的宽和高 <Card title="头部固定"> <Table bordered columns={columns} dataSource={this.state.dataSource} pagin

放弃antd table,基于React手写一个虚拟滚动的表格

缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反而有所上升. 客观地说,antd是开源的,UI设计得比较美观(甩出其他组件库一条街),而且是蚂蚁金服的体验技术部(一堆p7,p8,p9,基本都是大牛级的)在持续地开发维护,质量可以信任. 不过,antd虽好,但一些组件在某一些场景下,是很不适用的.例如,以表格形式无限滚动地展示大量数据(1w+)时,

使用 antd Table组件, 异步获取数据

使用React.js + Redux + antd 制作CMS 后台内容管理系统,分享一点点积累,欢迎讨论. 在this.state中初始化数据: this.state = { pageNum:1, /*翻页查询*/ pageSize:10, /*分页查询*/ activePage: 1, /*默认显示一页*/ selectedRowKeys: [], // 这里配置默认勾选列 loading: false, /*antd*/ selectedRow:[] } 在制作过程中,根据需要把antd的

react 使用antd的TreeSelect树选择组件实现多个树选择循环

需求说明,一个帐号角色可以设置管理多个项目的菜单权限 且菜单接口每次只能查询特定项目的菜单数据[无法查全部] 开发思路: 1,获取项目接口数组,得到项目数据 2,循环项目数据,以此为参数递归查询菜单数据[递归查询是为保证循环时数据异步请求顺序 不稳定] 3,将菜单数组组装成一个二维数组,以待循环树选择组件作展示 数据使用 4,循环树选择组件,实现树选择菜单功能 5,读取某条用户信息的菜单权限,将返回字符串菜单编码处理成与菜单数据相同的二维数组 6,奖该用户信息的菜单权限数组加载到循环树选择组件作

新闻预览,动态生成层级ul-li

1 <div class="row" id="add-withinfosortId-row" style="display: none"> 2 <div class="col-md-12" id="add-withinfosortId-divId"></div> 3 </div> 4 <div class="sticky-header"

vue table中使用多选的问题(翻页后如何保存已选项),联动echarts图表实现流量监控

流量监控项目需求: 根据表格数据,添加多选功能,默认全选,根据已选项更新图表视图 1.表格需要多选 2.要联动图表,所以关键是要利用表格多选的触发回调函数 vue table中使用多选: 很简单,只需要在table中增开一项,type定义为selection即可: 如何默认列表全选呢? 先关联table: 数据加载完成以后,获取列表数据长度,手动循环切换状态(toggleRowSelection是关键,通过这个方法来触发echarts的刷新): OK,现在可以实现默认全选状态了: 如何与echa

vue2.0在table中实现全选和反选

其实在去年小颖已经写过一篇:Vue.js实现checkbox的全选和反选 小颖今天在跟着慕课网学习vue的过程中,顺便试试如何在table中实现全选和反选,页面的css样式是直接参考慕课网的样式写的,js是小颖自己写哒,欢迎大家吐槽和点赞,嘻嘻 慕课网demo的  git 地址:ShoppingCart 页面效果: 具体怎么实现的呢? 因为上篇文章:使用localstorage来存储页面信息 中已经有写项目是怎么创建的所以小颖在这里就不重复了,其实只是在上篇文章的基础上稍微做了改动: App.v

react+mobx+antd按需加载 出现Support for the experimental syntax &#39;decorators-legacy&#39; isn&#39;t currently enabled

baidu上面的说法大多是在 项目的package.json 中添加decorators-legacy 因为引入了antd的按需加载 所以只需要在config-overrides.js中添加addDecoratorsLegacy() const { override, fixBabelImports,addDecoratorsLegacy } = require('customize-cra'); module.exports = override( fixBabelImports('impor