element UI table横向树结合checkbox进行多选,实现各个节点的[全选,半选,不选]状态,包含模拟数据
思路:
步骤一、后端返回tree格式数据,先结合element-ui的table的数据格式要求,将tree转换成table数据,进行行列的合并。
步骤二、拿到数据,递归遍历后将选中数据的id保存,进行回显操作。
步骤三、将每个checkbox进行绑定方法,此方法将作为分叉,如果当前checkbox属于父亲节点,判断当前的选中状态进行勾选,当前节点父亲节点,祖父节点和下级孩子节点进行相应的选中和状态修改,如果当前节点是最底层节点,则只需要选中上级及其上上级的节点和判断上级及上上级的状态和选中状态。重点:根据当前节点id,当前节点父节点id,子节点id,根据选中id存放的数组判树中checkbox的选中状态【半选,全选,不选】
步骤四、勾选后,调用 fatherSelectedState(i) 和 this.sonSelectedState(i) 设置当前节点下父节点,祖父节点,子节点,孙子节点的选中状态,重新刷新table树数据
table所需要的数据格式
{
id:"platform-1",
name:"基础信息模块1",
"menuType": "0",
children:[
{
id:"1-1",
name:"用户模块权限1-1",
menuType: "1",
children:[
{
id:"1-1-1",
name:"用户管理1-1-1",
menuType: "2",
children:[
{
id:"1-1-1-1",
name:"组织树管理1-1-1-1",
menuType: "3",
children:[
{
id:"1-1-1-1-1",
name:"新增组织树1-1-1-1-1",
menuType: "4",
checkFlag:true,
},
{
id:"1-1-1-1-2",
name:"编辑组织树信息1-1-1-1-2",
menuType: "4",
},
{
id:"1-1-1-1-3",
name:"删除组织树1-1-1-1-3",
menuType: "4",
}
]
},
{
id:"1-1-1-2",
name:"用户管理1-1-1-2",
menuType: "3",
children:[
{
id:"1-1-1-2-1",
name:"新增组织树1-1-1-2-1",
menuType: "4",
},
{
id:"1-1-1-2-2",
name:"编辑组织树信息1-1-1-2-2",
menuType: "4",
},
{
id:"1-1-1-2-3",
name:"删除组织树1-1-1-2-3",
menuType: "4",
}
]
}
]
},
{
id:"1-1-2",
name:"角色权限管理1-1-2",
menuType: "2",
children:[
{
id:"1-1-2-1",
name:"角色权限管理1-1-2-1",
menuType: "3",
children:[
{
id:"1-1-2-1-1",
name:"角色权限管理1-1-2-1-1",
menuType: "4",
},
{
id:"1-1-2-1-2",
name:"角色权限管理1-1-2-1-2",
menuType: "4",
},
{
id:"1-1-2-1-3",
name:"角色权限管理1-1-2-1-3",
menuType: "4",
}
]
},
],
}
]
},
{
id:"1-2",
name:"用户模块权限1-2",
menuType: "1",
children:[
{
id:"1-2-1",
name:"用户管理1-2-1",
menuType: "2",
children:[
{
id:"1-2-1-1",
name:"组织树管理1-2-1-1",
menuType: "3",
children:[
{
id:"1-2-1-1-1",
name:"新增组织树2",
menuType: "4",
},
{
id:"1-2-1-1-2",
name:"编辑组织树信息2",
menuType: "4",
},
{
id:"1-2-1-1-3",
name:"删除组织树2",
menuType: "4",
}
]
},
{
id:"1-2-1-2",
name:"用户管理1-2-1-2",
menuType: "3",
children:[
{
id:"1-2-1-2-1",
name:"新增组织树2",
menuType: "4",
},
{
id:"1-2-1-2-2",
name:"编辑组织树信息2",
menuType: "4",
},
{
id:"1-2-1-2-3",
name:"删除组织树2",
menuType: "4",
}
]
}
]
},
{
id:"1-2-2",
name:"角色权限管理1-2-2",
menuType: "2",
children:[
{
id:"1-2-2-1",
name:"角色权限管理",
menuType: "3",
children:[
{
id:"1-2-2-1-1",
name:"角色权限管理",
menuType: "4",
},
{
id:"1-2-2-1-2",
name:"角色权限管理",
menuType: "4",
},
{
id:"1-2-2-1-3",
name:"角色权限管理",
menuType: "4",
}
]
},
],
}
]
}
]
},
{
id:"platform-2",
name:"基础信息模块2",
menuType: "0",
children:[
{
id:"2-1",
name:"用户模块权限2-1",
menuType: "1",
children:[
{
id:"2-1-1",
name:"用户管理1",
menuType: "2",
children:[
{
id:"2-1-1-1",
name:"组织树管理2AAA",
menuType: "3",
children:[
{
id:"2-1-1-1-1",
name:"新增组织树2",
menuType: "4",
},
{
id:"2-1-1-1-2",
name:"编辑组织树信息2",
menuType: "4",
},
{
id:"2-1-1-1-3",
name:"删除组织树2",
menuType: "4",
}
]
},
{
id:"2-1-1-2A",
name:"用户管理3",
menuType: "4",
children:[]
},
{
id:"2-1-1-2B",
name:"用户管理3",
menuType: "4",
children:[]
},
{
id:"2-1-1-2C",
name:"用户管理3",
menuType: "4",
children:[]
},{
id:"2-1-1-2D",
name:"用户管理3",
menuType: "4",
children:[
]
}
]
},
{
id:"2-1-2",
name:"角色权限管理2-1-2",
menuType: "2",
children:[
{
id:"2-1-2-1",
menuType: "3",
name:"角色权限管理2",
children:[
{
id:"2-1-2-1-1",
name:"角色权限管理2",
menuType: "4",
},
{
id:"2-1-2-1-2",
name:"角色权限管理2",
menuType: "4",
},
{
id:"2-1-2-1-3",
name:"角色权限管理2aaaa",
menuType: "4",
}
]
},
],
}
]
}
]
}
其中“menuType”代表着当前树的各个层级,最多四层,如图所示,功能层是最后两层数据的信息,所需需要将最后两层的属性“children”改为“functionChildren”,并删除children
processingTreeData(tree){
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
//功能层和其他的children区分开来,使用functionChidlren来替代children,注意:删除children
if(tree[i].menuType==="2"){
tree[i].functionChildren = tree[i].children
delete tree[i].children;
}
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.processingTreeData(tree[i].children);
}
}
return
},
递归调用,拿到选中数据
开始拿到默认选中的数据,我的当前需求为【倒数第二层不做提交】,所以我要拿到倒数第二层的数据,提交时进行剔除
//拿到默认选中,和倒数第二层数据
processingTreeDataA(tree){
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
//根据需求提交的时候去除第一层和倒数第二层的id
if(tree[i].menuType==="3" || tree[i].menuType==="0"){
this.treeModelIds.push(tree[i].id)
}
//树中根据checkFlag来判断当前是否选中
if(tree[i].checkFlag===true){
this.treeSelectIds.push(tree[i].id)
}
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.processingTreeDataA(tree[i].children);
}
}
return
},
功能权限树默认选中
//功能权限默认选中
treeSekectDefault(){
this.treeSelectIds.forEach(item=>{
let node = this.searchTree(this.treeData,item)
this.fatherSelectedState(node)
this.sonSelectedState(node)
})
},
判断状态的主要方法[根据每个节点,判断当前节点的选中状态]
// 判断一个数组中是否包含另一个数组中的任意值(false不包含,true包含)
isInclude(data) {
let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) > -1) {
found = true
break
}
}
return found
},
// 判断一个数组中是否全部在另一个数组中(true不全部包含)
isIncludeArr(data) {
// let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) === -1) {
return true
}
}
return false
},
//根据id找到节点
searchTree(nodes, searchKey) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].id === searchKey) {
return nodes[i]
} else {
if (nodes[i].children && nodes[i].children.length > 0) {
let res = this.searchTree(nodes[i].children, searchKey);
if (res) {
return res
}
}else if(nodes[i].functionChildren && nodes[i].functionChildren.length > 0){
let res = this.searchTree(nodes[i].functionChildren, searchKey);
if (res) {
return res
}
}
}
}
return null
},
//找到当前节点的所有父节点 data:要遍历的数据, target:查找目标, result用于装查找结果的数组
findNodeAndParentsById(tree, id, parents = []) {
for (const node of tree) {
if (node.id === id) {
return [...parents, node.id];
}
if (node.children) {
const result = this.findNodeAndParentsById(node.children, id, [...parents,node.id,]);
if (result) {
return result;
}
}else if(node.functionChildren){
const result = this.findNodeAndParentsById(node.functionChildren, id, [...parents,node.id,]);
if (result) {
return result;
}
}
}
return null;
},
//遍历当前节点下的id
getAllIds(tree, result) {
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
result.push(tree[i].id)
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.getAllIds(tree[i].children, result);
}else if(typeof(tree[i].functionChildren)!=="undefined" && tree[i].functionChildren!==null && tree[i].functionChildren.length>0){
this.getAllIds(tree[i].functionChildren, result);
}
}
return result;
},
//断一个数组中是否包含另一个数组中的任意值(false不包含,true包含)
isInclude(data) {
let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) > -1) {
found = true
break
}
}
return found
},
// 判断一个数组中是否全部在另一个数组中(true不全部包含)
isIncludeArr(data) {
// let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) === -1) {
return false
}
}
return true
},
重点方法!!![checkbox 的选中方法],一开始checkbox进行修改后,数据改变但是table中的视图并未改变,
解决方法1:当选中时候,处理数据,从新刷新表格和数据,使用table中的:key=“itemKey”,itemKey:Math.random(),来进行刷新表格,但是每次刷新都会回滚到最顶层,解决每次刷新滚动回到顶部问题,使用了refreshTable方法,此方法可以刷新列表后回滚到上一次选中的位置,但是频繁出现闪屏问题,弃用
解决方法2:将el-checkbox中绑定key,惊讶的发现,数据变化了视图跟着变化!!!
果断使用方法二
![](https://img-blog.csdnimg.cn/219e14289dcb4a0fbeb1cf16e9c0189c.png文章来源:https://www.toymoban.com/news/detail-719574.html
checkboxChange(e,i){
this.$nextTick(()=>{
if((typeof(i.children)!=="undefined" && i.children.length>0) || (typeof(i.functionChildren)!=="undefined" && i.functionChildren.length>0)){ //如果当前节点为父节点
this.$nextTick(()=>{
if((i.checked===false && i.indeterminate===false) || (i.checked===true && i.indeterminate===true)){ //未选中:如果当前父节点未选中,则选中所有子节点
let cruuentNode = this.searchTree(this.treeData,i.id)
let fatherId = this.findNodeAndParentsById(this.treeData,i.id,[])
let temIdsArr = this.getAllIds([cruuentNode],[])
for(let i=0;i<fatherId.length;i++){
if(this.treeSelectIds.indexOf(fatherId[i])===-1){
this.treeSelectIds.push(fatherId[i])
let cruuentNodeB = this.searchTree(this.treeData,fatherId[i])
cruuentNodeB.checked = true
}
}
for(let i=0;i<temIdsArr.length;i++){
if(this.treeSelectIds.indexOf(temIdsArr[i])===-1){
this.treeSelectIds.push(temIdsArr[i])
let cruuentNodeA = this.searchTree(this.treeData,temIdsArr[i])
cruuentNodeA.checked = true
}
}
this.fatherSelectedState(i)
this.sonSelectedState(i)
}else if(i.checked===true && i.indeterminate===false){ //全选:如果当前是全选状态,点击后子节点全部去除
let temIdsArr = this.getAllIds([i],[]) //获取当前节点下的所有节点id
for(let i=0;i<temIdsArr.length;i++){
this.treeSelectIds.splice(this.treeSelectIds.indexOf(temIdsArr[i]), 1)
}
let fatherId = this.findNodeAndParentsById(this.treeData,i.id,[]) //拿到当前节点的所有父节点的id
for(let a=fatherId.length-1;a>=0;a--){ //从叶子节点开始遍历到最顶层
let cruuentNode = this.searchTree(this.treeData,fatherId[a]) //获取到父节点的内容
let temIdsArr = this.getAllIds([cruuentNode],[]) //获取当前父节点下的所有子节点id
let quchuFistId = temIdsArr.filter((element, index) => index > 0) //剔除当前父节点自己本身的节点id
if(this.isInclude(quchuFistId)===false && this.treeSelectIds.indexOf(fatherId[a])>-1){ //如果当前节点的子节点的所有id不在treeSelectIds中,并且当前父节点所属子节点有任意值存在,则剔除
this.treeSelectIds.splice(this.treeSelectIds.indexOf(fatherId[a]), 1)
}
}
this.fatherSelectedState(i)
this.sonSelectedState(i)
}
})
}else { //如果当前节点不是父节点
if(e){ //当前节点选中状态
// //根据ID获取所有 父节点,遍历父节点,编辑节点数据,如果treeSelectIds里面存在则不新增,如果不存在则新增
this.findNodeAndParentsById(this.treeData,i.id,[]).forEach(element => {
let cruuentNode = this.searchTree(this.treeData,element)
cruuentNode.checked = true
// 如果存在id则不新增
if(this.treeSelectIds.indexOf(cruuentNode.id)===-1){
this.treeSelectIds.push(element)
}
// this.searchTree(this.treeData,element) //
});
}else{ //当前节点未选中
let fatherId = this.findNodeAndParentsById(this.treeData,i.id,[]) //获取所有父亲节点id
let findNodeById = this.findNodeAndParentsById(this.treeData,i.id,[]).filter((element, index) => index > 0) //获取父节点id不包含本节点id
for(let i=fatherId.length-1;i>=0;i--){ //从底层节点开始遍历到顶层节点
let cruuentNode = this.searchTree(this.treeData,fatherId[i]) //根据父节点id找到该节点,拿到遍历的父节点
let temIdsArr = this.getAllIds([cruuentNode],[]) //获取当前选中节点父亲节点下面的所有id
let quchuFistId = temIdsArr.filter((element, index) => index > 0) //去除当前节点父节点下的所有id,并删除父节点本身id
if(this.isInclude(quchuFistId)===false){ // 判断父节点在treeSelectIds上的选中状态,如果一个都不在treeSelectIds里面,则去除当前节点的id
this.treeSelectIds.splice(this.treeSelectIds.indexOf(temIdsArr[0]), 1)
}
}
}
}
//获取当前节点下的所有id
this.fatherSelectedState(i) //设置该节点在父节点的选中状态
this.sonSelectedState(i) //设置该节点下所有父节点的所有选中状态
this.$nextTick(()=>{
this.treeToTableData() //重新编辑table树
this.refreshTable() //无感刷新table树结构,滚动记录
})
})
this.$forceUpdate()
},
//设置该节点下所有子节点的所有选中状态
sonSelectedState(i){
this.getAllIds([i],[]).forEach(element => {
let cruuentNode = this.searchTree(this.treeData,element)
let a = this.getAllIds([cruuentNode],[]).filter((element, index) => index > 0)
if(this.isIncludeArr(this.getAllIds([cruuentNode],[])) && this.isInclude(this.getAllIds([cruuentNode],[]))){ //全选
cruuentNode.indeterminate = false
cruuentNode.checked = true
}else if(this.isIncludeArr(this.getAllIds([cruuentNode],[]))===false && this.isInclude(this.getAllIds([cruuentNode],[]))===true){ //半选
cruuentNode.indeterminate = true
cruuentNode.checked = true
}else if(this.isIncludeArr(this.getAllIds([cruuentNode],[]))===false && this.isInclude(this.getAllIds([cruuentNode],[]))===false){ //不选
cruuentNode.indeterminate = false
cruuentNode.checked = false
}
});
},
//设置该节点在父节点的选中状态
fatherSelectedState(i){
this.findNodeAndParentsById(this.treeData,i.id,[]).forEach(element => {
let cruuentNode = this.searchTree(this.treeData,element)
let a = this.getAllIds([cruuentNode],[]).filter((element, index) => index > 0)
if(this.isIncludeArr(a) && this.isInclude(a)){ //全选
cruuentNode.indeterminate = false
cruuentNode.checked = true
}else if(this.isIncludeArr(a)===false && this.isInclude(a)===true){ //半选
cruuentNode.indeterminate = true
cruuentNode.checked = true
}else if(this.isIncludeArr(a)===false && this.isInclude(a)===false){ //不选
cruuentNode.indeterminate = false
cruuentNode.checked = false
}
});
},
将tree树状格式转换成el-table表格可识别格式,并进行单元格合并
- 将tree数据转换成二维表格数据
- 合并行或列的计算方法
- 表格单元格合并-----行
//tree数据处理
treeToTableData() {
//将树状结构格式转换成二维数组表格形式
let ewArr = this.parseTreeToRow(this.treeData);
let tableData = [];
ewArr.map((item,index) => {
if(item.length===2){
item.push({
label:item[1].label+item.length,
isChecked:false,
})
}else if(item.length===1){
item.push({label:item[0].label+"1",isChecked:false,})
item.push({label:item[0].label+"2",isChecked:false,})
}
let obj = {};
item.map((itemc, indexb) => {
// typeof(itemc.functionChildren)!=="undefined"?itemc.functionChildren:null
obj["index" + (indexb + 1)] = {
id: itemc.id,
label: itemc.label,
functionChildren:(itemc.functionChildren !== null)?itemc.functionChildren:[],
children:(itemc.children !== null)?itemc.children:[],
checked:(typeof itemc.checked !== "undefined")?itemc.checked:false,
isChecked:itemc.isChecked===false?itemc.isChecked:true,
indeterminate:( typeof itemc.indeterminate !== "undefined")?itemc.indeterminate:false
};
if (typeof itemc.children !== "undefined") {
obj.children = { data: itemc.children };
}
});
tableData.push(obj);
});
this.tableData = tableData;
},
/**
* 递归-----将树结构数据格式,转化为,二维数组 表格形式
* @param node 树的源数据
* @param data 树转化为二维数组的数据
* @param row 临时存储数据
* @returns {*[]}
*/
parseTreeToRow(node, data = [], row = []) {
node.map((item) => {
let obj = {
id: item.id,
label:item.name,
functionChildren:typeof(item.functionChildren)!=="undefined"?item.functionChildren:null,
children:(item.children !== null)?item.children:null,
checked:(typeof item.checked !== "undefined")?item.checked:false,
indeterminate:(typeof item.indeterminate !== "undefined")?item.indeterminate:false
};
if (typeof item.children !== "undefined") {
obj.children = item.children.length > 0 ? item.children : [];
}
if (item.children && item.children.length != 0) {
this.parseTreeToRow(item.children, data, [...row, obj]);
} else {
data.push([...row, obj]);
}
});
return data;
},
/**
* 合并行或列的计算方法
*/
tableSpanMethod({ row, column, rowIndex, columnIndex }) {
return {
rowspan:
columnIndex < 3
? this.mergeRows(
row[column.property],
this.tableData,
rowIndex,
column.property
)
: 1,
colspan: 1,
};
},
/**
* 表格单元格合并-----行
* @param {Object} value 当前单元格的值
* @param {Object} data 当前表格所有数据
* @param {Object} index 当前单元格的值所在 行 索引
* @param {Object} property 当前列的property
* @returns {number} 待合并单元格数量
*/
mergeRows(value, data, index, property) {
// 判断 当前行的该列数据 与 上一行的该列数据 是否相等
if (index !== 0 && value.label === data[index - 1][property].label) {
// 返回 0 使表格被跨 行 的那个单元格不会渲染
return 0;
}
// 判断 当前行的该列数据 与 下一行的该列数据 是否相等
let rowSpan = 1;
for (let i = index + 1; i < data.length; i++) {
if (value.label !== data[i][property].label) {
break;
}
rowSpan++;
}
return rowSpan;
},
完整代码粘贴,附带模拟数据:
<template>
<div>
<el-drawer class="el-drawer-role" title="我是标题" :visible.sync="drawer" size="70%" :with-header="false" :before-close="drawerClose">
<el-tabs style="padding: 20px;" v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="应用权限" name="first">
<el-table ref="multipleTable" border :data="appTableData" tooltip-effect="dark" style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column type="selection" align="center" width="55"></el-table-column>
<el-table-column prop="key" label="应用编码" align="center" width="120"></el-table-column>
<el-table-column prop="name" label="应用名称" align="center" show-overflow-tooltip></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="功能权限" name="second">
<el-input
v-model="searchContent"
placeholder="搜索"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
<el-table style="margin-bottom: 70px;" :key="itemKey" :height="contentHeight" class="ganteTable" ref="table" :span-method="tableSpanMethod" :data="tableData" border align="center" size="mini" >
<el-table-column align="center" prop="index1" label="应用" width="200px">
<template slot-scope="scope">
{{ scope.row.index1.checked }}
<el-checkbox @change="checkboxChange($event,scope.row.index1)" :key="scope.row.index1.checked+scope.row.index1.indeterminate+''" :indeterminate="scope.row.index1.indeterminate" :checked="scope.row.index1.checked" :label="scope.row.index1.label"></el-checkbox><br />
</template>
</el-table-column>
<el-table-column align="center" prop="index2" label="模块" width="200px">
<template slot-scope="scope">
{{ scope.row.index2.checked }}
<el-checkbox v-if="scope.row.index2.isChecked" :key="scope.row.index2.checked+scope.row.index2.indeterminate+''" @change="checkboxChange($event,scope.row.index2)" :indeterminate="scope.row.index2.indeterminate" :checked="scope.row.index2.checked" :label="scope.row.index2.label"></el-checkbox><br />
</template>
</el-table-column>
<el-table-column align="center" prop="index3" label="界面" width="200px">
<template slot-scope="scope">
{{ scope.row.index3.checked }}
<el-checkbox v-if="scope.row.index3.isChecked" :key="scope.row.index3.checked+scope.row.index3.indeterminate+''" @change="checkboxChange($event,scope.row.index3)" :indeterminate="scope.row.index3.indeterminate" :checked="scope.row.index3.checked" :label="scope.row.index3.label"></el-checkbox><br />
</template>
</el-table-column>
<el-table-column prop="children" label="功能" >
<template slot-scope="scope">
<span v-for="item in scope.row.index3.functionChildren" :key="item.id">
<div style="margin: 7px;" v-if="item.menuType==='3'">
{{ item.name }}<br />
<span style="margin: 7px;margin-left:10px;" v-for="item2 in item.children" :key="item2.id">
<el-checkbox :checked="item2.checked" :indeterminate="item2.indeterminate" :key="item2.checked+item2.indeterminate+''" @change="checkboxChange($event,item2)">
<span v-if="item2.name.indexOf(searchContent)>-1">
<span style="color: #f50">{{ searchContent }}</span>
{{ item2.name.substr(item2.name.indexOf(searchContent) + searchContent.length) }}
</span>
<span v-else>
{{ item2.name }}
</span>
</el-checkbox>
</span>
</div>
<span v-if="item.menuType==='4'" style="margin: 7px;margin-left:10px;">
<el-checkbox @change="checkboxChange($event,item)" :key="item.checked+item.indeterminate+''" :indeterminate="item.indeterminate" :checked="item.checked" :label="item.name"></el-checkbox>
</span>
</span>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<div class="demo-drawer__footer" style="position: fixed;bottom: 20px;right: 20px; background: white; ">
<el-button @click="drawerClose">取 消</el-button>
<el-button type="primary" @click="submitRole()">确定</el-button>
</div>
</el-drawer>
</div>
</template>
<script>
import { platformGetList,getPlatFormByRoleId,getRolePermissionTreeList,assignRolePermission } from "@/api/userInfo/role"
export default {
data(){
return {
temObj:{},
contentHeight:'0px',
searchContent:"",
defaultTreeSelect:[],
appTableDefaultSelect:[],
appTableData: [],
multipleSelection: [],
checkAll:true,
tableData:[],
treeSelectIds:[],
itemKey:Math.random(),
drawer:false, //抽屉打开关闭状态
activeName:"first", //当前tab激活的状态
treeModelIds:[], //模块ids不需要传递,和第一层次ID不需要传递
treeData: [
// {
// id:"platform-1",
// name:"基础信息模块1",
// "menuType": "0",
// children:[
// {
// id:"1-1",
// name:"用户模块权限1-1",
// menuType: "1",
// children:[
// {
// id:"1-1-1",
// name:"用户管理1-1-1",
// menuType: "2",
// children:[
// {
// id:"1-1-1-1",
// name:"组织树管理1-1-1-1",
// menuType: "3",
// children:[
// {
// id:"1-1-1-1-1",
// name:"新增组织树1-1-1-1-1",
// menuType: "4",
// checkFlag:true,
// },
// {
// id:"1-1-1-1-2",
// name:"编辑组织树信息1-1-1-1-2",
// menuType: "4",
// },
// {
// id:"1-1-1-1-3",
// name:"删除组织树1-1-1-1-3",
// menuType: "4",
// }
// ]
// },
// {
// id:"1-1-1-2",
// name:"用户管理1-1-1-2",
// menuType: "3",
// children:[
// {
// id:"1-1-1-2-1",
// name:"新增组织树1-1-1-2-1",
// menuType: "4",
// },
// {
// id:"1-1-1-2-2",
// name:"编辑组织树信息1-1-1-2-2",
// menuType: "4",
// },
// {
// id:"1-1-1-2-3",
// name:"删除组织树1-1-1-2-3",
// menuType: "4",
// }
// ]
// }
// ]
// },
// {
// id:"1-1-2",
// name:"角色权限管理1-1-2",
// menuType: "2",
// children:[
// {
// id:"1-1-2-1",
// name:"角色权限管理1-1-2-1",
// menuType: "3",
// children:[
// {
// id:"1-1-2-1-1",
// name:"角色权限管理1-1-2-1-1",
// menuType: "4",
// },
// {
// id:"1-1-2-1-2",
// name:"角色权限管理1-1-2-1-2",
// menuType: "4",
// },
// {
// id:"1-1-2-1-3",
// name:"角色权限管理1-1-2-1-3",
// menuType: "4",
// }
// ]
// },
// ],
// }
// ]
// },
// {
// id:"1-2",
// name:"用户模块权限1-2",
// menuType: "1",
// children:[
// {
// id:"1-2-1",
// name:"用户管理1-2-1",
// menuType: "2",
// children:[
// {
// id:"1-2-1-1",
// name:"组织树管理1-2-1-1",
// menuType: "3",
// children:[
// {
// id:"1-2-1-1-1",
// name:"新增组织树2",
// menuType: "4",
// },
// {
// id:"1-2-1-1-2",
// name:"编辑组织树信息2",
// menuType: "4",
// },
// {
// id:"1-2-1-1-3",
// name:"删除组织树2",
// menuType: "4",
// }
// ]
// },
// {
// id:"1-2-1-2",
// name:"用户管理1-2-1-2",
// menuType: "3",
// children:[
// {
// id:"1-2-1-2-1",
// name:"新增组织树2",
// menuType: "4",
// },
// {
// id:"1-2-1-2-2",
// name:"编辑组织树信息2",
// menuType: "4",
// },
// {
// id:"1-2-1-2-3",
// name:"删除组织树2",
// menuType: "4",
// }
// ]
// }
// ]
// },
// {
// id:"1-2-2",
// name:"角色权限管理1-2-2",
// menuType: "2",
// children:[
// {
// id:"1-2-2-1",
// name:"角色权限管理",
// menuType: "3",
// children:[
// {
// id:"1-2-2-1-1",
// name:"角色权限管理",
// menuType: "4",
// },
// {
// id:"1-2-2-1-2",
// name:"角色权限管理",
// menuType: "4",
// },
// {
// id:"1-2-2-1-3",
// name:"角色权限管理",
// menuType: "4",
// }
// ]
// },
// ],
// }
// ]
// }
// ]
// },
// {
// id:"platform-2",
// name:"基础信息模块2",
// menuType: "0",
// children:[
// {
// id:"2-1",
// name:"用户模块权限2-1",
// menuType: "1",
// children:[
// {
// id:"2-1-1",
// name:"用户管理1",
// menuType: "2",
// children:[
// {
// id:"2-1-1-1",
// name:"组织树管理2AAA",
// menuType: "3",
// children:[
// {
// id:"2-1-1-1-1",
// name:"新增组织树2",
// menuType: "4",
// },
// {
// id:"2-1-1-1-2",
// name:"编辑组织树信息2",
// menuType: "4",
// },
// {
// id:"2-1-1-1-3",
// name:"删除组织树2",
// menuType: "4",
// }
// ]
// },
// {
// id:"2-1-1-2A",
// name:"用户管理3",
// menuType: "4",
// children:[]
// },
// {
// id:"2-1-1-2B",
// name:"用户管理3",
// menuType: "4",
// children:[]
// },
// {
// id:"2-1-1-2C",
// name:"用户管理3",
// menuType: "4",
// children:[]
// },{
// id:"2-1-1-2D",
// name:"用户管理3",
// menuType: "4",
// children:[
// ]
// }
// ]
// },
// {
// id:"2-1-2",
// name:"角色权限管理2-1-2",
// menuType: "2",
// children:[
// {
// id:"2-1-2-1",
// menuType: "3",
// name:"角色权限管理2",
// children:[
// {
// id:"2-1-2-1-1",
// name:"角色权限管理2",
// menuType: "4",
// },
// {
// id:"2-1-2-1-2",
// name:"角色权限管理2",
// menuType: "4",
// },
// {
// id:"2-1-2-1-3",
// name:"角色权限管理2aaaa",
// menuType: "4",
// }
// ]
// },
// ],
// }
// ]
// }
// ]
// }
]
}
},
created(){
},
mounted(){
},
methods:{
//获取应用平台权限
platformGetList(roleId){
this.multipleSelection = []
getPlatFormByRoleId({roleId:roleId}).then(res=>{
this.appTableData = res.data
this.$nextTick(()=>{
console.log("this.appTableData",this.appTableData)
// this.defaultSelect()
this.appTableData.forEach(item=>{
if(item.selFlag){
//再遍历数组,将数据放入方法中
this.$refs.multipleTable.toggleRowSelection(item,true)
}
})
})
})
},
//最后一层加上functionChildren
processingTreeData(tree){
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
//功能层和其他的children区分开来,使用functionChidlren来替代children,注意:删除children
if(tree[i].menuType==="2"){
tree[i].functionChildren = tree[i].children
delete tree[i].children;
}
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.processingTreeData(tree[i].children);
}
}
return
},
//拿到默认选中,和倒数第二层数据
processingTreeDataA(tree){
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
//根据需求提交的时候去除第一层和倒数第二层的id
if(tree[i].menuType==="3" || tree[i].menuType==="0"){
this.treeModelIds.push(tree[i].id)
}
//树中根据checkFlag来判断当前是否选中
if(tree[i].checkFlag===true){
this.treeSelectIds.push(tree[i].id)
}
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.processingTreeDataA(tree[i].children);
}
}
return
},
//菜单层选中状态
processingTreeDataB(tree){
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
//判断菜单那一成是否需要选中
if(tree[i].menuType==="3"){
//遍历菜单层,判断菜单层下的所有按钮权限只要有一个选中,根据这一个选中判断菜单层的选中状态
for(let j=0;j<tree[i].children.length;j++){
//如果菜单下的按钮权限有一个包含在treeSelectIds中,则当前菜单被选中
if(this.treeSelectIds.indexOf(tree[i].children[j].id)!==-1){
//按钮id存在,并且treeSelectIds中没有当前id,则push操作
if(this.treeSelectIds.indexOf(tree[i].id)===-1){
//则当前菜单被选中后结束当前循环,拿到id,根据id的父节点,子节点判定当前菜单的选中状态
this.treeSelectIds.push(tree[i].id)
break
}
}
}
}
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.processingTreeDataB(tree[i].children);
}
}
return
},
//提交
submitRole(){
//去除第一层和倒数第二层的id
let newArr = this.treeSelectIds .filter((x) => !this.treeModelIds.some((item) => x === item));
let temObj = {}
temObj.departId = this.temObj.departId
temObj.roleId = this.temObj.roleId
temObj.permissionIdList = newArr
assignRolePermission(temObj).then(res=>{
this.$message({message: '角色权限授权成功', type: 'success'});
})
},
//应用列表默认多选
defaultSelect(){
// //创建一个空数组用来存放默认数据
// let list = []
// //遍历表格的数据,再遍历需要在表格中反显的数据,两者的id一致
// this.appTableData.forEach((item) => {
// this.appTableDefaultSelect.forEach(val => {
// if (val=== item.id) {
// // 把判断出来的默认表格数据push到创建的数组中
// list.push(item)
// }
// })
// })
// if (list) {
// //再遍历数组,将数据放入方法中
// list.forEach((row) => {
// this.$refs.multipleTable.toggleRowSelection(row,true)
// })
// }
},
//多选
handleSelectionChange(val) {
this.multipleSelection = []
val.forEach(item=>{
this.multipleSelection.push(item.id);
})
//如果有选中则请求选中的所属树
if(this.multipleSelection.length>0){
this.temObj.platformIdList = this.multipleSelection
this.treeModelIds = []
this.treeSelectIds = []
getRolePermissionTreeList(this.temObj).then(res=>{
this.treeData = res.data
this.refreshTable() //刷新tree表
this.processingTreeDataA(this.treeData) //拿到树中的已选中的id和第一层id和倒数第二次id
this.processingTreeDataB(this.treeData) //判断倒数第三层的选中状态,后端返回第三层没有返回选中状态,
this.processingTreeData(this.treeData) //给最后一层加上functionChidren
this.treeSekectDefault() //功能权限树默认选中
this.treeToTableData() //将功能权限树转换成表格形式并合并单元格
this.contentHeight = document.getElementsByClassName("el-drawer__body")[0].clientHeight-220+'px'
})
} else {
this.temObj.platformIdList = []
this.treeData = []
}
},
//功能权限默认选中
treeSekectDefault(){
this.treeSelectIds.forEach(item=>{
let node = this.searchTree(this.treeData,item)
this.fatherSelectedState(node)
this.sonSelectedState(node)
})
},
// 判断一个数组中是否包含另一个数组中的任意值(false不包含,true包含)
isInclude(data) {
let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) > -1) {
found = true
break
}
}
return found
},
// 判断一个数组中是否全部在另一个数组中(true不全部包含)
isIncludeArr(data) {
// let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) === -1) {
return true
}
}
return false
},
//判断当前节点的选中状态,0未选中,1选中,2半选
checkboxStatus(){
this.searchTree(this.treeData,i.id)
},
//根据id找到节点
searchTree(nodes, searchKey) {
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].id === searchKey) {
return nodes[i]
} else {
if (nodes[i].children && nodes[i].children.length > 0) {
let res = this.searchTree(nodes[i].children, searchKey);
if (res) {
return res
}
}else if(nodes[i].functionChildren && nodes[i].functionChildren.length > 0){
let res = this.searchTree(nodes[i].functionChildren, searchKey);
if (res) {
return res
}
}
}
}
return null
},
//找到当前节点的所有父节点 data:要遍历的数据, target:查找目标, result用于装查找结果的数组
findNodeAndParentsById(tree, id, parents = []) {
for (const node of tree) {
if (node.id === id) {
return [...parents, node.id];
}
if (node.children) {
const result = this.findNodeAndParentsById(node.children, id, [...parents,node.id,]);
if (result) {
return result;
}
}else if(node.functionChildren){
const result = this.findNodeAndParentsById(node.functionChildren, id, [...parents,node.id,]);
if (result) {
return result;
}
}
}
return null;
},
//遍历当前节点下的id
getAllIds(tree, result) {
//遍历树 获取id数组
for(let i=0;i<tree.length;i++){
result.push(tree[i].id)
if(typeof(tree[i].children)!=="undefined" && tree[i].children!==null && tree[i].children.length>0){
this.getAllIds(tree[i].children, result);
}else if(typeof(tree[i].functionChildren)!=="undefined" && tree[i].functionChildren!==null && tree[i].functionChildren.length>0){
this.getAllIds(tree[i].functionChildren, result);
}
}
return result;
},
//断一个数组中是否包含另一个数组中的任意值(false不包含,true包含)
isInclude(data) {
let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) > -1) {
found = true
break
}
}
return found
},
// 判断一个数组中是否全部在另一个数组中(true不全部包含)
isIncludeArr(data) {
// let found = false
for (let i = 0; i < data.length; i++) {
if (this.treeSelectIds.indexOf(data[i]) === -1) {
return false
}
}
return true
},
//选中
checkboxChange(e,i){
this.$nextTick(()=>{
if((typeof(i.children)!=="undefined" && i.children.length>0) || (typeof(i.functionChildren)!=="undefined" && i.functionChildren.length>0)){ //如果当前节点为父节点
this.$nextTick(()=>{
if((i.checked===false && i.indeterminate===false) || (i.checked===true && i.indeterminate===true)){ //未选中:如果当前父节点未选中,则选中所有子节点
let cruuentNode = this.searchTree(this.treeData,i.id)
let fatherId = this.findNodeAndParentsById(this.treeData,i.id,[])
let temIdsArr = this.getAllIds([cruuentNode],[])
for(let i=0;i<fatherId.length;i++){
if(this.treeSelectIds.indexOf(fatherId[i])===-1){
this.treeSelectIds.push(fatherId[i])
let cruuentNodeB = this.searchTree(this.treeData,fatherId[i])
cruuentNodeB.checked = true
}
}
for(let i=0;i<temIdsArr.length;i++){
if(this.treeSelectIds.indexOf(temIdsArr[i])===-1){
this.treeSelectIds.push(temIdsArr[i])
let cruuentNodeA = this.searchTree(this.treeData,temIdsArr[i])
cruuentNodeA.checked = true
}
}
this.fatherSelectedState(i)
this.sonSelectedState(i)
}else if(i.checked===true && i.indeterminate===false){ //全选:如果当前是全选状态,点击后子节点全部去除
let temIdsArr = this.getAllIds([i],[]) //获取当前节点下的所有节点id
for(let i=0;i<temIdsArr.length;i++){
this.treeSelectIds.splice(this.treeSelectIds.indexOf(temIdsArr[i]), 1)
}
let fatherId = this.findNodeAndParentsById(this.treeData,i.id,[]) //拿到当前节点的所有父节点的id
for(let a=fatherId.length-1;a>=0;a--){ //从叶子节点开始遍历到最顶层
let cruuentNode = this.searchTree(this.treeData,fatherId[a]) //获取到父节点的内容
let temIdsArr = this.getAllIds([cruuentNode],[]) //获取当前父节点下的所有子节点id
let quchuFistId = temIdsArr.filter((element, index) => index > 0) //剔除当前父节点自己本身的节点id
if(this.isInclude(quchuFistId)===false && this.treeSelectIds.indexOf(fatherId[a])>-1){ //如果当前节点的子节点的所有id不在treeSelectIds中,并且当前父节点所属子节点有任意值存在,则剔除
this.treeSelectIds.splice(this.treeSelectIds.indexOf(fatherId[a]), 1)
}
}
this.fatherSelectedState(i)
this.sonSelectedState(i)
}
})
}else { //如果当前节点不是父节点
if(e){ //当前节点选中状态
// //根据ID获取所有 父节点,遍历父节点,编辑节点数据,如果treeSelectIds里面存在则不新增,如果不存在则新增
this.findNodeAndParentsById(this.treeData,i.id,[]).forEach(element => {
let cruuentNode = this.searchTree(this.treeData,element)
cruuentNode.checked = true
// 如果存在id则不新增
if(this.treeSelectIds.indexOf(cruuentNode.id)===-1){
this.treeSelectIds.push(element)
}
// this.searchTree(this.treeData,element) //
});
}else{ //当前节点未选中
let fatherId = this.findNodeAndParentsById(this.treeData,i.id,[]) //获取所有父亲节点id
let findNodeById = this.findNodeAndParentsById(this.treeData,i.id,[]).filter((element, index) => index > 0) //获取父节点id不包含本节点id
for(let i=fatherId.length-1;i>=0;i--){ //从底层节点开始遍历到顶层节点
let cruuentNode = this.searchTree(this.treeData,fatherId[i]) //根据父节点id找到该节点,拿到遍历的父节点
let temIdsArr = this.getAllIds([cruuentNode],[]) //获取当前选中节点父亲节点下面的所有id
let quchuFistId = temIdsArr.filter((element, index) => index > 0) //去除当前节点父节点下的所有id,并删除父节点本身id
if(this.isInclude(quchuFistId)===false){ // 判断父节点在treeSelectIds上的选中状态,如果一个都不在treeSelectIds里面,则去除当前节点的id
this.treeSelectIds.splice(this.treeSelectIds.indexOf(temIdsArr[0]), 1)
}
}
}
}
//获取当前节点下的所有id
this.fatherSelectedState(i) //设置该节点在父节点的选中状态
this.sonSelectedState(i) //设置该节点下所有父节点的所有选中状态
this.$nextTick(()=>{
this.treeToTableData() //重新编辑table树
this.refreshTable() //无感刷新table树结构,滚动记录
})
})
this.$forceUpdate()
},
/**
* 刷新table时候,无感刷新,定位到上一次选中的滚动中
*/
refreshTable() {
// let beforeScrollTop = this.$refs.table.$el.querySelector('div.el-table__body-wrapper').scrollTop
// // this.itemKey = Math.random()
// this.$forceUpdate()
// this.$nextTick(()=> {
// setTimeout(() => {
// this.$refs.table.$el.querySelector('div.el-table__body-wrapper').scrollTop = beforeScrollTop
// },0)
// })
},
//设置该节点下所有子节点的所有选中状态
sonSelectedState(i){
this.getAllIds([i],[]).forEach(element => {
let cruuentNode = this.searchTree(this.treeData,element)
let a = this.getAllIds([cruuentNode],[]).filter((element, index) => index > 0)
if(this.isIncludeArr(this.getAllIds([cruuentNode],[])) && this.isInclude(this.getAllIds([cruuentNode],[]))){ //全选
cruuentNode.indeterminate = false
cruuentNode.checked = true
}else if(this.isIncludeArr(this.getAllIds([cruuentNode],[]))===false && this.isInclude(this.getAllIds([cruuentNode],[]))===true){ //半选
cruuentNode.indeterminate = true
cruuentNode.checked = true
}else if(this.isIncludeArr(this.getAllIds([cruuentNode],[]))===false && this.isInclude(this.getAllIds([cruuentNode],[]))===false){ //不选
cruuentNode.indeterminate = false
cruuentNode.checked = false
}
});
},
//设置该节点在父节点的选中状态
fatherSelectedState(i){
this.findNodeAndParentsById(this.treeData,i.id,[]).forEach(element => {
let cruuentNode = this.searchTree(this.treeData,element)
let a = this.getAllIds([cruuentNode],[]).filter((element, index) => index > 0)
if(this.isIncludeArr(a) && this.isInclude(a)){ //全选
cruuentNode.indeterminate = false
cruuentNode.checked = true
}else if(this.isIncludeArr(a)===false && this.isInclude(a)===true){ //半选
cruuentNode.indeterminate = true
cruuentNode.checked = true
}else if(this.isIncludeArr(a)===false && this.isInclude(a)===false){ //不选
cruuentNode.indeterminate = false
cruuentNode.checked = false
}
});
},
//tab选项卡
handleClick(tab, event) {
},
//分配权限
assignPermissions(data){
//获取平台列表
this.platformGetList(data.roleId)
this.temObj.departId = data.departId
this.temObj.roleId = data.roleId
this.drawer = true
},
//关闭窗口
drawerClose(){
this.activeName = "first"
this.tableData = []
this.treeSelectIds = []
this.treeModelIds = []
this.refreshTable()
this.drawer = false
},
//tree数据处理
treeToTableData() {
//将树状结构格式转换成二维数组表格形式
let ewArr = this.parseTreeToRow(this.treeData);
let tableData = [];
ewArr.map((item,index) => {
if(item.length===2){
item.push({
label:item[1].label+item.length,
isChecked:false,
})
}else if(item.length===1){
item.push({label:item[0].label+"1",isChecked:false,})
item.push({label:item[0].label+"2",isChecked:false,})
}
let obj = {};
item.map((itemc, indexb) => {
// typeof(itemc.functionChildren)!=="undefined"?itemc.functionChildren:null
obj["index" + (indexb + 1)] = {
id: itemc.id,
label: itemc.label,
functionChildren:(itemc.functionChildren !== null)?itemc.functionChildren:[],
children:(itemc.children !== null)?itemc.children:[],
checked:(typeof itemc.checked !== "undefined")?itemc.checked:false,
isChecked:itemc.isChecked===false?itemc.isChecked:true,
indeterminate:( typeof itemc.indeterminate !== "undefined")?itemc.indeterminate:false
};
if (typeof itemc.children !== "undefined") {
obj.children = { data: itemc.children };
}
});
tableData.push(obj);
});
this.tableData = tableData;
},
/**
* 递归-----将树结构数据格式,转化为,二维数组 表格形式
* @param node 树的源数据
* @param data 树转化为二维数组的数据
* @param row 临时存储数据
* @returns {*[]}
*/
parseTreeToRow(node, data = [], row = []) {
node.map((item) => {
let obj = {
id: item.id,
label:item.name,
functionChildren:typeof(item.functionChildren)!=="undefined"?item.functionChildren:null,
children:(item.children !== null)?item.children:null,
checked:(typeof item.checked !== "undefined")?item.checked:false,
indeterminate:(typeof item.indeterminate !== "undefined")?item.indeterminate:false
};
if (typeof item.children !== "undefined") {
obj.children = item.children.length > 0 ? item.children : [];
}
if (item.children && item.children.length != 0) {
this.parseTreeToRow(item.children, data, [...row, obj]);
} else {
data.push([...row, obj]);
}
});
return data;
},
/**
* 合并行或列的计算方法
*/
tableSpanMethod({ row, column, rowIndex, columnIndex }) {
return {
rowspan:
columnIndex < 3
? this.mergeRows(
row[column.property],
this.tableData,
rowIndex,
column.property
)
: 1,
colspan: 1,
};
},
/**
* 表格单元格合并-----行
* @param {Object} value 当前单元格的值
* @param {Object} data 当前表格所有数据
* @param {Object} index 当前单元格的值所在 行 索引
* @param {Object} property 当前列的property
* @returns {number} 待合并单元格数量
*/
mergeRows(value, data, index, property) {
// 判断 当前行的该列数据 与 上一行的该列数据 是否相等
if (index !== 0 && value.label === data[index - 1][property].label) {
// 返回 0 使表格被跨 行 的那个单元格不会渲染
return 0;
}
// 判断 当前行的该列数据 与 下一行的该列数据 是否相等
let rowSpan = 1;
for (let i = index + 1; i < data.length; i++) {
if (value.label !== data[i][property].label) {
break;
}
rowSpan++;
}
return rowSpan;
},
//递归遍历树数据
}
}
</script>
<style scoped>
.search{
color: red;
}
</style>
最终效果:
文章来源地址https://www.toymoban.com/news/detail-719574.html
到了这里,关于element UI table横向树结合checkbox进行多选,实现各个节点的[全选,半选,不选]状态附带模拟数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!