一、需求

  用户对不同的应用需要有不同的权限,用户一般和角色关联在一起,新建角色的时候会选择该角色对应的应用,然后对应用分配权限。于是写了一种实现的方式。首先应用是一个二级树,一级表示的是应用分组,二级表示的是应用,这是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。

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

  1. 解决react使用antd table组件固定表头后,表头和表体列不对齐以及配置fixed固定左右侧后行高度不对齐

    一.固定表头后表体列和表头不对齐 此问题可能在antd3.24.0版本之前都存在,反正3.16.2版本是存在这个问题的,如果是3.24.0之前的版本估计只能通过修改css样式解决. 按照官网说的: 1 ...

  2. REACT 使用antd Table 中rowSelection遇到的问题

    首先项目是尚硅谷的后台谷粒平台,在用到antd Table 中的 rowSelection时,出现了一个问题(P87时遇到的问题): 表格中的每一项前面有一个radio单选框可以选中,本来是想利用ro ...

  3. React之Antd table表格渲染按钮问题

    问题描述:table表格渲染表格数据时,会自动触发操作列中Button的onClick函数,表格渲染完成后,点击Button按钮,onClick函数不能被触发. // 定义表格表头数据 问题写法:  ...

  4. 用React实现一个自动生成文章目录的组件

    原文地址:小寒的博客 功能介绍 这个组件的效果呐,就是你在浏览这个页面的时候点击右上角的叉叉看到的那个文章目录. 功能很简单,就是根据文章内容自动生成这个目录,可以快速跳转. 需要的知识点 正则 do ...

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

    前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...

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

    缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...

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

    前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...

  8. react 使用antd的多选功能做一个单选与全选效果

    一个小而简单的单选全选功能,其实官网已经给出效果了,不过是我多做了些复合用法 addorupdatemodal.jsx import React from "react"; imp ...

  9. react antd Table动态合并单元格

    示例数据 原始数组 const data = [ { key: '0', name: 'John Brown', age:22, address: 'New York No. 1 Lake Park' ...

随机推荐

  1. 防御XSS攻击-encode用户输入内容的重要性

    一.开场先科普下XSS 跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶 ...

  2. Git Bash的一些命令和配置

    查看git版本号: git --version 如果是第一次使用Git,你需要设置署名和邮箱: $ git config --global user.name "用户名" $ gi ...

  3. 探索ASP.NET MVC5系列之~~~2.视图篇(上)---包含XSS防御和异步分部视图的处理

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  4. JavaScript动画-拖拽改变元素大小

    ▓▓▓▓▓▓ 大致介绍 拖拽改变元素大小是在模拟拖拽上增加了一些功能 效果:拖拽改变元素大小 ▓▓▓▓▓▓ 拖拽改变元素大小原理 首先这个方块得知道我们想要改变这个它的大小,所以我给它设定一个范围,当 ...

  5. js学习之变量、作用域和内存问题

    js学习之变量.作用域和内存问题 标签(空格分隔): javascript 变量 1.基本类型和引用类型: 基本类型值:Undefined, Null, Boolean, Number, String ...

  6. [原] KVM虚拟机网络闪断分析

    背景 公司云平台的机器时常会发生网络闪断,通常在10s-100s之间. 异常情况 VM出现问题时,表现出来的情况是外部监控系统无法访问,猜测可能是由于系统假死,OVS链路问题等等.但是在出现网络问题的 ...

  7. 基于SOA架构的TDD测试驱动开发模式

    以需求用例为基,Case&Coding两条线并行,服务(M)&消费(VC)分离,单元.接口.功能.集成四层质量管理,自动化集成.测试.交付全程支持. 3个大阶段(需求分析阶段.研发准备 ...

  8. 在开源中国(oschina)git中新建标签(tags)

    我今天提交代码到主干上面,本来想打个标签(tags)的. 因为我以前新建过标签(tags),但是我现在新建的时候不知道入库在哪了.怎么找也找不到了. 从网上找资料也没有,找客服没有人理我,看到一个交流 ...

  9. IP报头

      位字段的值设置为二进制的0100表示IP版本4(IPv4).设置为0110表示IP版本6(IPv6)   位,它表示32位字长的IP报头长度,设计报头长度的原因是数据包可选字段大小会发生变化.IP ...

  10. ReactNative入门(安卓)——API(下)

    LayoutAnimation - layout动画 当布局发生改变时的动画模块,它有两个方法: 1. 最常用的方法是 LayoutAnimation.configureNext(conf<Ob ...