参考文章:我把他的弹框单拉出来一个独立文件作为组件方便使用,遇到一些问题记录一下。
testComponet.vue
<template>
<div class="per_container">
<div class="per_con_left">
<div class="per_con_title">未选</div>
<div class="check_all">
<el-checkbox
:indeterminate="config.left.isIndeterminate"
v-model="config.left.checkAll"
@change="handleCheckAll($event, 'left')"
>全选/全不选</el-checkbox>
</div>
<div class="tree">
<el-tree
ref="treeLeft"
:data="treeDataArr"
:props="propsDefault"
node-key="id"
show-checkbox
:filter-node-method="filterNodeLeft"
@check-change="handleCheckChange('left')"
/>
</div>
</div>
<div class="operation">
<el-button type="primary" @click="toRight()">
移入
<i class="el-icon-d-arrow-right"></i>
</el-button>
<el-button type="primary" icon="el-icon-d-arrow-left" @click="toLeft()">
移除
</el-button>
</div>
<div class="per_con_right">
<div class="per_con_title">已选</div>
<div class="check_all">
<el-checkbox
:indeterminate="config.right.isIndeterminate"
v-model="config.right.checkAll"
@change="handleCheckAll($event, 'right')"
>全选/全不选</el-checkbox>
</div>
<div class="tree">
<el-tree
ref="treeRight"
:data="treeDataArr"
:props="propsDefault"
node-key="id"
show-checkbox
:filter-node-method="filterNodeRight"
@check-change="handleCheckChange('right')"
/>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['treeData'],
mounted() {
this.treeDataArr = this.treeData;
this.allParentKeys = this.treeDataArr.map((item) => {
return item.id;
});
if (this.$refs.treeLeft && this.$refs.treeRight) {
this.$nextTick(() => {
this.setTreeFilter();
});
}
},
created() {
// this.treeDataArr = this.treeData;
// this.allParentKeys = this.treeDataArr.map((item) => {
// return item.id;
// });
// if (this.$refs.treeLeft && this.$refs.treeRight) {
// this.$nextTick(() => {
// this.setTreeFilter();
// });
// }
},
data() {
return {
propsDefault: {
label: "name"
},
isIndeterminateL: false,
isIndeterminateR: false,
checkAllLeft: false,
checkAllRight: false,
treeDataArr: [],
checkedKeys: [],
halfCheckedKeys: [],
checkedNodes: [],
config: {
left: {
isIndeterminate: false,
checkAll: false,
ref: "treeLeft"
},
right: {
isIndeterminate: false,
checkAll: false,
ref: "treeRight"
}
}
};
},
methods: {
setTreeFilter() {
this.$refs.treeLeft.filter();
this.$refs.treeRight.filter();
},
toLeft() {
this.checkedKeys = this.$refs.treeRight.getCheckedKeys();
this.halfCheckedKeys = this.$refs.treeRight.getHalfCheckedKeys();
this.settreeDataArr(this.treeDataArr, false);
this.setTreeFilter();
this.$refs.treeLeft.setCheckedKeys(this.checkedKeys);
this.$refs.treeRight.setCheckedKeys([]);
},
toRight() {
this.checkedKeys = this.$refs.treeLeft.getCheckedKeys();
this.halfCheckedKeys = this.$refs.treeLeft.getHalfCheckedKeys();
this.settreeDataArr(this.treeDataArr, true);
this.setTreeFilter();
this.$refs.treeRight.setCheckedKeys(this.checkedKeys);
this.$refs.treeLeft.setCheckedKeys([]);
},
filterNodeLeft(value, data) {
console.log('filterNodeLeft',data);
return !data.selected;
},
filterNodeRight(value, data) {
console.log('filterNodeRight',data);
return data.selected;
},
// 递归设置数据选中状态
settreeDataArr(tree, type) {
const setTree = (treeDataArr) => {
treeDataArr.forEach((item, index) => {
if (this.checkedKeys.includes(item.id)) {
treeDataArr[index].selected = type;
}
if (item.children && item.children.length) {
setTree(item.children);
// 判断半选框是否需要移动
if (this.halfCheckedKeys.includes(item.id)) {
if (type) {
treeDataArr[index].selected = type;
} else {
const target = treeDataArr[index].children.find((it) => {
return it.selected;
});
if (!target) {
treeDataArr[index].selected = type;
}
}
}
}
});
};
setTree(tree);
},
submitEdit() {
this.$emit("permissionData", this.treeDataArr);
},
// 勾选树结构时判断是否勾选上面的全选
handleCheckChange(type) {
this.checkedNodes = this.$refs[this.config[type].ref].getCheckedNodes();
const pIds = this.checkedNodes.filter((item) => {
return !item.pId;
});
if (!pIds.length) {
this.config[type].checkAll = false;
this.config[type].isIndeterminate = false;
return;
}
if (pIds.length === this.allParentKeys.length) {
this.config[type].checkAll = true;
this.config[type].isIndeterminate = false;
} else {
this.config[type].isIndeterminate = true;
this.config[type].checkAll = false;
}
},
// 全选
handleCheckAll(value, type) {
const keys = value
? this.treeDataArr.map((item) => {
return item.id;
})
: [];
this.$refs[this.config[type].ref].setCheckedKeys(keys, false);
}
}
};
</script>
<style lang="scss" scoped>
.per_container {
display: flex;
height: 500px;
justify-content: space-between;
align-items: center;
}
.per_con_left,
.per_con_right {
width: 45%;
height: 100%;
}
.operation {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.operation .el-button {
margin-left: 0;
margin-bottom: 10px;
}
.per_con_title {
height: 42px;
line-height: 26px;
border-radius: 8px 8px 0 0;
padding: 8px;
align-self: stretch;
background: #f2f6f9;
font-size: 16px;
box-sizing: border-box;
border: 1px solid #d8d8d8;
font-weight: 700;
text-align: left;
}
.check_all {
height: 42px;
line-height: 42px;
padding: 0 5px;
border: 1px solid #d8d8d8;
border-top: none;
text-align: left;
}
.tree {
height: calc(100% - 82px);
border: 1px solid #d8d8d8;
border-top: none;
overflow: auto;
}
</style>
思路:
(1)思路二:利用elementUI的filter API对选中节点进行筛选,左侧筛选出未选中的,右侧筛选出选中的,用的还是同一棵树,用一个属性来区分是否选择,好处是子节点选中,父节点会跟随保存,不用重新构建树结构。
(2)通过监听treeData值变化,调用setTreeFilter也就是this.$refs.treeLeft.filter(); this.$refs.treeRight.filter();
里的filter 方法,filter 是 el-tree 组件用于动态过滤树节点,配合 filter-node-method 属性可以实现节点过滤,自定义左右侧树过滤的规则分别是是filterNodeLeft(value, data) { return !data.selected;},
和filterNodeRight(value, data) {return data.selected;},
是否被选中(左移右移来设置selected属性)
testIndex.vue
<template>
<div>
<!-- 其他页面内容 -->
<TestComponent :treeData="myTreeData" />
</div>
</template>
<script>
import TestComponent from '@/views/test/test.vue';
export default {
components: {
TestComponent
},
data() {
return {
myTreeData: [
// 这里放置你的树形数据
{
id: 1,
name: '父节点1',
children: [
{
id: 2,
name: '子节点1'
}
]
}
]
};
},
// ... existing code ...
};
</script>
关于监听treeData变化后给树赋数据这里遇到问题:我把原文监听treeData对树进行初始化一开始写在created里,导致左右都有树,然后改为mounted就没有这个问题。
原因:(操作DOM元素用mounted
)
依赖 DOM 元素 : setTreeFilter 方法可能依赖于 this.$refs.treeLeft
和 this.$refs.treeRight
这两个 DOM 引用。在 created 阶段,DOM 元素还未挂载, this.$refs
无法正确获取到对应的 DOM 元素,从而导致过滤逻辑无法正常执行。而在 mounted 阶段,DOM 元素已经挂载完成, this.$refs
可以正常获取到对应的 DOM 元素,过滤逻辑就能正常工作了。
treeData: {
handler(val) {
this.treeDataArr = val;
this.allParentKeys = this.treeDataArr.map((item) => {
return item.id;
});
if (this.$refs.treeLeft && this.$refs.treeRight) {
this.$nextTick(() => {
this.setTreeFilter();
});
}
},
deep: true,
},
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Error_ABC/article/details/136877442
created() {
// this.treeDataArr = this.treeData;
// this.allParentKeys = this.treeDataArr.map((item) => {
// return item.id;
// });
// if (this.$refs.treeLeft && this.$refs.treeRight) {
// this.$nextTick(() => {
// this.setTreeFilter();
// });
// }
},