基于elementui实现部门树结构 ,一个用户可能在多个部门(即树结构下存在多个id相同的用户)
要求
1、点击左边部门节点选中添加或删除该节点下的所有用户,点击用户,若用户在其他部门也存在,同样也要勾选上。并将选中的用户在右边展示。
2、点击右边的删除,左边的tree树形控件对应取消选中该用户。
3、当有默认选中的用户,左边对应选中该用户
难点
elementui-tree中如果出现id相同时会被覆盖,导致所有方法都只能操作到相同用户的最后一个
解决方案
通过elementui自定义节点内容实现
代码
<div class="selectPersonnel">
<div class="tree">
<div class="content">
<el-tree :data="treeData" :props="defaultProps" :expand-on-click-node="false" class="eltree">
<span class="custom-tree-node" slot-scope="{ node, data }">
<el-checkbox :value="checked(data)" @change="chengeChecked(data)" :class="{'ishals':data.children&&ishals(data),'checkbox':true}">{{node.label}}</el-checkbox>
</span>
</el-tree>
</div>
</div>
<div class="personnel">
<div class="title">已选</div>
<div class="content">
<div v-for="(item,index) in selectData" :key="item.id">
<div>{{item.label}}</div>
<i class="el-icon-close" @click="remove(index)"></i>
</div>
</div>
</div>
</div>
</div>
data() {
return {
treeData: [{
label: '总公司',
id: 0,
children: [{
label: '子公司A',
id: 1,
children: [{
label: '部门A',
id: 4,
children: [{
label: '用户A',
id: 9,
}]
}]
}, {
label: '子公司B',
id: 2,
children: [{
label: '部门B',
id: 5,
children: [{
label: '用户B',
id: 10,
}, {
label: '用户C',
id: 20,
}, {
label: '用户D',
id: 21,
}]
}, {
label: '部门C',
id: 6,
children: [{
label: '用户E',
id: 11,
}]
}]
}, {
label: '子公司C',
id: 3,
children: [{
label: '部门D',
id: 7,
children: [{
label: '用户F',
id: 13,
}, {
label: '用户G',
id: 12,
}]
}, {
label: '部门E',
id: 8,
children: [{
label: '用户F',
id: 13,
}]
}]
}]
}],
defaultProps: {
children: 'children',
label: 'orgName'
},
selectData: []
};
},
methods: {
//每个节点的选择状态,false为未选择,true为选中
checked(data) {
let flag = false //默认为选择
if (data.children) { //当不为用户层节点时
flag = this.mapChildren(data, true) //遍历子集,默认为true,当节点下的某一个用户没有被选中时返回false
} else { //当为用户层节点时
flag = this.isInArr(this.selectData, 'id', data.id) > -1 //判断用户是否已选择
}
return flag
},
//判断非用户层的节点下的用户是否全部为选择状态,是则返回true,不是则返回false
mapChildren(data, flag) {
if (data.children) { //若当前层下还有层级
data.children.forEach(item => { //遍历回调
let isCheck = this.mapChildren(item, true)
if (!isCheck) { //某个用户没有被选中赋值flag为false
flag = false
}
})
} else { //若当前层是用户层
let ischeckout = this.isInArr(this.selectData, 'id', data.id) == -1
if (ischeckout) { //若这个用户没有被选中赋值flag为false
flag = false
}
}
return flag
},
//点击多选框触发
chengeChecked(data) {
if (!data.children) { //点击用户层的多选框
let index = this.isInArr(this.selectData, 'id', data.id) //判断之前有没有选择当前用户
if (index == -1) { //之前没有选择当前用户,则推入
this.selectData.push(data)
} else { //之前有选择当前用户,则删除
this.selectData.splice(index, 1)
}
} else { //点击不是用户层的多选框
let allUser = this.getAllUser(data) //获取到这个节点下的所有用户
if (this.isAllSelect(data)) { //之前已全部选中所有用户
allUser.forEach(item => {
let index = this.isInArr(this.selectData, 'id', item.id)
this.selectData.splice(index, 1) //取消这个节点所有选中的用户
})
} else { //之前有的用户没有选中
allUser.forEach(item => {
let index = this.isInArr(this.selectData, 'id', item.id)
if (index == -1) {
this.selectData.push(item) //添加这个节点所有未选中的用户
}
})
}
}
},
//判断当前arr数组的某一项的key键的值对应keyValue,存在则返回下标,否则放回-1
isInArr(arr, key, keyValue) {
let flag = -1
arr.forEach((item, index) => {
if (item[key] == keyValue) {
flag = index
}
})
return flag
},
//判断是否为全选
isAllSelect(data) {
let copyData = this.$Tools.deepCopy(data)
let allUser = this.getAllUser(copyData)
let statue = []
allUser.forEach(item => {
let ischeckout = this.isInArr(this.selectData, 'id', item.id) == -1
if (ischeckout) {
statue.push(-1)
} else {
statue.push(0)
}
})
// console.log(statue.indexOf(0) > -1,)
if (statue.indexOf(0) > -1 && statue.indexOf(-1) == -1) {
return true
} else {
return false
}
},
//判断是否为半选
ishals(data) {
let copyData = this.$Tools.deepCopy(data)
let allUser = this.getAllUser(copyData)
let statue = []
allUser.forEach(item => {
let ischeckout = this.isInArr(this.selectData, 'id', item.id) == -1
if (ischeckout) {
statue.push(-1)
} else {
statue.push(0)
}
})
return statue.indexOf(-1) > -1 && statue.indexOf(0) > -1
},
//获取所有的用户
getAllUser(data) {
let allData = []
if (data.children) {
data.children.forEach(item => {
let itemData = this.$Tools.deepCopy(this.getAllUser(item))
let preData = this.$Tools.deepCopy(allData)
allData = [...preData, ...itemData]
})
} else {
allData.push(data)
}
return allData
},
//点击X删除对应的
remove(index) {
this.selectData.splice(index, 1)
},
},
<style scoped lang="less">
@deep: ~">>>";
.selectPersonnel {
display: flex;
justify-content: space-between;
padding-bottom: 20px;
.tree {
width: 40%;
background: #2B2B2B;
height: 400px;
margin-right: 5px;
box-shadow: 0px 3px 17px rgba(0, 0, 0, 0.48);
border-radius: 5px;
.content {
height: calc(100% - 20px);
padding: 10px;
overflow-x: hidden;
overflow-y: auto;
.customTree {
.treeList {
height: 25px;
line-height: 25px;
color: #fff;
}
.treeList1 {
margin-left: 10px;
}
.treeList2 {
margin-left: 20px;
}
.treeList3 {
margin-left: 30px;
}
}
}
.content::-webkit-scrollbar {
width: 6px; // 设置为0则不显示 不会影响页面宽度
}
.content::-webkit-scrollbar-track {
border-radius: 5px;
background-color: #2B2B2B;
}
.content::-webkit-scrollbar-thumb {
background-color: #999;
border-radius: 10px;
}
.content::-webkit-scrollbar-thumb:hover {
background-color: #666;
}
.content::-webkit-scrollbar-corner {
background-color: #2B2B2B;
}
.eltree {
// width: 100%;
background: transparent;
color: #FFFFFF;
@{deep} .el-tree-node__content {
background: transparent;
&:hover {
background: transparent;
}
}
@{deep} .el-tree-node__label {
color: rgba(255, 255, 255, 0.6);
}
}
}
.personnel {
flex: 1;
height: 400px;
background: #2B2B2B;
box-shadow: 0px 3px 17px rgba(0, 0, 0, 0.48);
font-size: 14px;
font-family: PingFang SC;
font-weight: 400;
line-height: 30px;
color: rgba(255, 255, 255, 0.6);
.title {
padding-left: 20px;
color: rgba(255, 255, 255, 1);
}
.content {
height: calc(100% - 50px);
padding: 10px;
overflow-x: hidden;
overflow-y: auto;
}
.content::-webkit-scrollbar {
width: 6px; // 设置为0则不显示 不会影响页面宽度
}
.content::-webkit-scrollbar-track {
border-radius: 5px;
background-color: #2B2B2B;
}
.content::-webkit-scrollbar-thumb {
background-color: #999;
border-radius: 10px;
}
.content::-webkit-scrollbar-thumb:hover {
background-color: #666;
}
.content::-webkit-scrollbar-corner {
background-color: #2B2B2B;
}
.labelContent {
display: flex;
>div {
padding-left: 20px;
width: calc(100% - 60px);
}
.el-icon-close {
transition: none;
font-size: 20px;
width: 30px;
line-height: 30px;
text-align: center;
}
&:hover {
background: #434343;
color: rgba(255, 255, 255, 1);
}
}
}
}
.ishals {
// #04a3ff
@{deep} .el-checkbox__inner {
background: #409EFF;
// #04a3ff;
}
@{deep} .el-checkbox__inner::before {
content: '';
position: absolute;
display: block;
background-color: #FFF;
height: 2px;
-webkit-transform: scale(.5);
transform: scale(.5);
left: 0;
right: 0;
top: 0.35714rem;
}
}
.checkbox {
@{deep} .el-checkbox__label {
color: #fff
}
}
</style>