1.实现一下功能
将左侧数据移到右侧,将右侧数据移到左侧
设计思路,
1.获取左侧选中的树(过滤掉根节点),点中间的箭头时,遍历树,将选中的数据通过匹配id,进行删除,同时将选中的数据,push到右边的树,此时:将左边选中的数据添加到右边的树
2.将右边选中部分,在点击中间左侧箭头时,删除右侧选中数据,同时,将左侧树遍历删除右侧剩余数据,此时:将右边选中的数据添加到左边的树
3.向后台返回数据时,使用dataRight就可以了
<template>
<!-- 自定义树形穿梭框组件、理论上左右均可选择是否为树形结构,目前固定为左侧树形、右侧无层级结构 -->
<div class="tree-transfer">
<!-- 穿梭框左侧 -->
<div class="tree-transfer-left">
<!-- 左侧采用element-ui的el-tree -->
<el-input placeholder="输入关键字进行过滤" v-model="filterText" style="padding:10px;" />
<div v-show="dataLeft.length>0" style="width:95%;height:230px;overflow-y:auto;margin:0 auto;">
<el-tree ref="treeLeft" :data="dataLeft" show-checkbox node-key="id" :props="defaultProps" :filter-node-method="filterNode" @check-change="leftCheckChange" default-expand-all>
</el-tree>
</div>
<div v-show="!dataLeft.length>0" style="margin:120px auto 0;width:100%;height:30px;text-align:center;">
暂无数据
</div>
</div>
<!-- 穿梭框中间按钮区域 -->
<div class="tree-transfer-middle">
<!-- -->
<el-button circle type="info" icon="el-icon-arrow-left" :style="selectRightList.length>0?'background:#409EFF;border-color:#409EFF':''" @click="removeClick"></el-button>
<el-button circle type="info" icon="el-icon-arrow-right" :style="selectLeftList.length>0?'background:#409EFF;border-color:#409EFF':''" @click="addClick"></el-button>
</div>
<!-- 穿梭框右侧 -->
<div class="tree-transfer-right">
<!-- 右侧直接放置结果 -->
<!-- 这里也采用tree结构,默认是对数据进行处理使得没有树形结构,也可以选择有树形结构 -->
<div style="height:50px;width:100%;background:#f2f2f2;line-height:50px;padding-left:10px;margin-bottom:10px;"> 已选择 </div>
<div v-show="dataRight.length>0">
<el-tree ref="treeRight" :data="dataRight" show-checkbox node-key="id" :props="defaultProps" @check-change="rightCheckChange">
</el-tree>
</div>
<div v-show="!dataRight.length>0" style="margin:120px auto 0;width:100%;height:30px;text-align:center;">
暂无数据
</div>
</div>
</div>
</template>
<script>
export default {
props: [],
data() {
return {
defaultProps: {
children: "child",
label: "name",
},
yuanlaiData: [], //保存一个原始树
dataLeft: [], //左侧树--数据
selectLeftList: [], //左侧树-勾选
filterText: "", //左侧的树进行过滤
dataRight: [], //右侧树--数据
selectRightList: [], //右侧树-勾选
setUserstreeData: [], //获取的一个完整树
currentData: [], //这个树是剔除右侧数据的树
dataListData: {
id: "1036304891099578311",
name: "美丽集团有限公司",
child: [
{
id: "1363068427315736522",
name: "测试部门",
child: null,
},
{
id: "1406942927553146833",
name: "12412412",
child: null,
},
{
id: "1353979515791900644",
name: "漂亮工程有限公司",
child: [
{
id: "1451382359171833855",
name: "测试",
child: null,
},
],
},
{
id: "1353981813561643066",
name: "美容研究院",
child: [
{
id: "1368809908881891377",
name: "健康西分院",
child: null,
},
{
id: "1406942760036839488",
name: "财富统计处",
child: [
{
id: "1353985339042029599",
name: "财务有限公司",
child: [
{
id: "1425622408449269700",
pid: "1353985339042029569",
name: "富强",
child: null,
},
],
},
],
},
],
},
{
id: "1353982603181314012",
name: "民主技术研究院",
child: null,
},
],
},
};
},
mounted() {
this.getReViewOfficeTree();
},
// 监控data中的数据变化
watch: {
filterText(val) {
this.$nextTick(() => {
this.$refs.treeLeft.filter(val);
});
},
},
methods: {
// 获取左侧的树
getReViewOfficeTree() {
this.setUserstreeData = this.dataListData;
if (Object.keys(this.setUserstreeData).length > 0) {
this.dataLeft = [this.setUserstreeData];
this.dataLeft.forEach((item, index) => {
if (item.child && item.child.length > 0) {
item.disabled = true;
this.getTreeDisable(item.child);
}
});
let data = JSON.parse(JSON.stringify(this.setUserstreeData));
this.yuanlaiData = [data];
}
},
addClick() {
// 定义一个递归过滤的方法,用来删掉父级中给的元素
// 获取所有选中的项并且去掉父级
let list = this.$refs.treeLeft.getCheckedNodes(); //勾选的节点
// 走原始数据中删掉已经选择的
// 1.父级的删除
const parList = list.filter((item) => {
return item.child;
});
parList.forEach((item) => {
let index = this.dataLeft.findIndex((itema) => {
return itema.id == item.id;
});
if (index >= 0) {
this.dataLeft.splice(index, 1);
}
});
// 2.子级的删除
list = list.filter((item) => {
return !item.child || !item.child.length;
//return !item.child||!item.child.length;//当发现返回的孩子是一个为空的数组时,使用这个返回
});
// 选中的数据list,遍历树,找到对应的id,将这条数据进行删除
list.forEach((item, index) => {
this.getTreeName(this.dataLeft, item.id);
});
// 3.将选择的项添加到右侧
this.dataRight.push(...list);
this.selectLeftList = this.$refs.treeLeft.getCheckedNodes(); //用于控制右侧按钮颜色
this.selectRightList = this.$refs.treeRight.getCheckedNodes(); //用于控制左侧按钮颜色
},
// 循环遍历树形结构,删除符合条件的叶子节点
getTreeName(list, id) {
let _this = this;
for (let i = 0; i < list.length; i++) {
let a = list[i];
if (a.id === id) {
const index = list.findIndex((item) => item.id === id);
list.splice(index, 1);
return a.name;
} else {
if (a.child && a.child.length > 0) {
let res = _this.getTreeName(a.child, id);
if (res) {
return res;
}
}
}
}
},
// 将所有根节点禁选
getTreeDisable(list) {
let _this = this;
for (let i = 0; i < list.length; i++) {
let a = list[i];
if (a.child && a.child.length > 0) {
a.disabled = true;
_this.getTreeDisable(a.child);
}
}
},
removeClick() {
// 从右侧移除时的方法
// 1.从右侧删除选中的数据
let list = this.$refs.treeRight.getCheckedNodes();
for (let item of list) {
let index = this.dataRight.findIndex((item2) => {
return item.id == item2.id;
});
if (index >= 0) {
this.dataRight.splice(index, 1);
}
}
// 2.遍历右边的数据,将左边树对应的数据删除---右边存在的数据不能在左边树存在
if (this.dataRight.length > 0) {
this.currentData = JSON.parse(JSON.stringify(this.yuanlaiData));
this.dataRight.forEach((item, index) => {
this.getTreeName(this.currentData, item.id);
this.dataLeft = this.currentData;
});
} else {
this.dataLeft = JSON.parse(JSON.stringify(this.yuanlaiData));
}
this.selectLeftList = this.$refs.treeLeft.getCheckedNodes(); //用于控制右侧按钮颜色
this.selectRightList = this.$refs.treeRight.getCheckedNodes(); //用于控制左侧按钮颜色
},
getResult() {
return this.dataRight;
},
leftCheckChange(data, checked, indeterminate) {
console.log("1212dsageddsa", data, checked, indeterminate);
this.selectLeftList = this.$refs.treeLeft.getCheckedNodes(); //用于控制右侧按钮颜色
this.selectRightList = this.$refs.treeRight.getCheckedNodes(); //用于控制左侧按钮颜色
},
rightCheckChange(data, checked, indeterminate) {
this.selectRightList = this.$refs.treeRight.getCheckedNodes(); //用于控制左侧按钮颜色
this.selectLeftList = this.$refs.treeLeft.getCheckedNodes(); //用于控制右侧按钮颜色
},
// 左侧树 支持过滤
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
},
};
</script>
<style scoped lang="scss">
.tree-transfer{
display: flex;
min-height: 250px;
.tree-transfer-left{
width:45%;
border:1px #E5E5E5 solid;
border-radius: 10px;
height:330px;
overflow-y:auto;
}
.tree-transfer-middle{
display: flex;
justify-content: center;
align-items: center;
width:14%;
}
.tree-transfer-right{
width:40%;
border:1px #E5E5E5 solid;
border-radius: 10px;
height:330px;
overflow-y:auto;
}
}
</style>
2.关于树形结构数据的知识点
实现一下功能
表单—树形结构进行多选,选择器与树形结构两两进行绑定
2.1获取节点
<el-tree class="filter-tree" show-checkbox node-key="id" :default-expanded-keys="[0]" :data="treeData" :props="defaultProps" ref="popoverTree" :highlight-current="true" @check-change='getCheckedNode'>
</el-tree>
getCheckedNode(e) {
// 获取勾选的所有关联节点(所有的半选节点除外)
let res = this.$refs.popoverTree.getCheckedNodes();
console.log("res", res);
// 获取勾选的所有关联节点(所有的半选节点也一并获取)
let res2 = this.$refs.popoverTree.getCheckedNodes(false, true);
//获取所有叶子节点
var allNodes = this.$refs.popoverTree.store._getAllNodes();
var leafNodes = allNodes.filter((item) => item.isLeaf);
var leafNodesChecked = allNodes.filter((item) => item.isLeaf&&item.checked);//获取所有叶子节点,同时被选中
console.log("leafNodes",leafNodes);
//获取被选中的id List
this.ruleForm.taskId2 = leafNodesChecked .map((item, index) => {
return item.key;
});
},
2.2获取所有的叶子节点,将数据转换成选择器所需要的数据
clickQuery() {
let res = {
code: "200",
data: {
id: "1036304891099571",
name: "集团有限公司",
child: [
{
id: "13630684273157318",
name: "测试部门",
child: null,
},
{
id: "1368809263143937",
name: "124",
child: null,
},
{
id: "140694353146882",
name: "12412412",
child: null,
},
{
id: "135397941900674",
name: "美丽公司",
child: [
{
id: "14517171833857",
name: "测试",
child: null,
},
],
},
{
id: "13539818443010",
name: "技术研究院",
child: [
{
id: "136886881891329",
name: "分院",
child: null,
},
{
id: "14069427839426",
name: "统计处",
child: null,
},
{
id: "140713948787842",
name: "分析处",
child: null,
},
{
id: "14071089877249",
name: "审核处",
child: null,
},
],
},
{
id: "1353982671314049",
name: "技术研究院",
child: null,
},
{
id: "135396971778",
name: "测试",
child: null,
},
{
id: "13539862029569",
name: "有限公司",
child: [
{
id: "14255449269761",
name: "三江二级",
child: null,
},
],
},
{
id: "13539855312897",
name: "动力技术研究院",
child: null,
},
{
id: "1353986959042",
name: "投资有限公司",
child: [
{
id: "1354868555010",
name: "测试部门",
child: null,
},
],
},
],
},
};
this.treeData = [res.data];
this.treeData.forEach((item, index) => {
if (item.child && item.child.length > 0) {
item.disabled = true;
this.getTreeDisable(item.child);
}
});
this.treeDataList = [];
this.getTreeList(this.treeData);
},
// 将所有根节点禁选
getTreeDisable(list) {
let _this = this;
for (let i = 0; i < list.length; i++) {
let a = list[i];
if (a.child && a.child.length > 0) {
a.disabled = true;
_this.getTreeDisable(a.child);
}
}
},
//获取树形数据转换成选择列表需要的数据
getTreeList(list) {
let _this = this;
for (let i = 0; i < list.length; i++) {
let a = list[i];
if (a.child && a.child.length > 0) {
_this.getTreeList(a.child);
} else {
this.treeDataList.push({ name: a.name, id: a.id });
}
}
},
2.3
<el-form-item label="所属单位名称">
<el-popover placement="bottom-start" trigger="click">
<el-select v-model="ruleForm.taskId2" slot="reference" style="width: 100%" placeholder="" multiple @change="treeDataListChange" @remove-tag="treeDataListRemoveTag" popper-class="riskDoubtsListClass">
//下拉框列表必须存在,只能用v-show="false"
<div v-show="false">
<el-option v-for="item in treeDataList" :key="item.id" :label="item.name" :value="item.id"> </el-option>
</div>
</el-select>
<el-tree class="filter-tree" show-checkbox node-key="id" :default-expanded-keys="[0]" :data="treeData" :props="defaultProps" ref="popoverTree" :highlight-current="true" @node-click="officeIdHandleNodeClick" @check-change='getCheckedNode'>
</el-tree>
</el-popover>
</el-form-item>
// 多选 选择器 e为删除后剩下的列表
treeDataListChange(e) {
console.log("eChange", e, this.ruleForm.taskId2);
this.$refs.popoverTree.setCheckedKeys(this.ruleForm.taskId2);//此操作是将保留的数据在树形结构上进行选中
},
// 多选 选择器 e为删除的某项,此操作仅供学习
treeDataListRemoveTag(e) {
console.log("eTag", e);
},
//此操作将在树形结构选中的项,在选择器上进行回显
getCheckedNode(e) {
var allNodes = this.$refs.popoverTree.store._getAllNodes();
var leafNodes = allNodes.filter((item) => item.isLeaf && item.checked);
this.ruleForm.taskId2 = leafNodes.map((item, index) => {
return item.key;
});
},
// 项目单位点击数回显到input,此操作仅供学习
officeIdHandleNodeClick(data) {
console.log("datadatadata", data);
},
<style lang='scss'>
//将选择器的下拉框列表进行隐藏,但是必须存在
.riskDoubtsListClass {
display: none;
}
</style>
将选择器上的数据同步到树形结构上
this.$refs.popoverTree.setCheckedKeys(this.ruleForm.taskId2);//此操作是将保留的数据在树形结构上进行选中
将树形结构上的数据同步到选择器上
var allNodes = this.$refs.popoverTree.store._getAllNodes();
var leafNodes = allNodes.filter((item) => item.isLeaf && item.checked);
this.ruleForm.taskId2 = leafNodes.map((item, index) => {
return item.key;
});