概要
项目要实现树形穿梭框的效果,没事哒 没事哒~难不倒打工人
此处直接放弃用el-transfer,使用el-tree造出一个穿梭框!
话不多说,上链接 代码
实现效果
代码
1.创建TreeTransfer.vue文件
此处根据checkList过滤左右穿梭框中的内容。
<template>
<div class="main-div">
<div class="transfer-div">
<div class="title-div">
<el-checkbox
v-model="checkedLeft"
:label="props.titles[0]"
@change="changeChecked(dataLeft, treeRef)"
/>
</div>
<div class="body-div">
<el-input
:prefix-icon="Search"
v-model="filterTextLeft"
style="margin-bottom: 9px"
placeholder="请输入搜索内容"
/>
<el-tree
ref="treeRef"
class="filter-tree"
:data="dataLeft"
:props="defaultProps"
node-key="id"
@node-click="handleNodeClick"
default-expand-all
show-checkbox
:check-on-click-node="true"
:filter-node-method="filterNode"
/>
</div>
</div>
<div class="button-transfer">
<el-button type="primary" class="button-div" @click="setCheckedKeysLeft"
><</el-button
>
<el-button type="primary" class="button-div" @click="setCheckedKeys"
>></el-button
>
</div>
<div class="transfer-div">
<div class="title-div">
<el-checkbox
v-model="checkedRight"
:label="props.titles[1]"
@change="changeChecked(dataRight, treeRefRight)"
/>
</div>
<div class="body-div">
<el-input
:prefix-icon="Search"
v-model="filterTextRight"
style="margin-bottom: 9px"
placeholder="请输入搜索内容"
/>
<el-tree
ref="treeRefRight"
class="filter-tree"
:data="dataRight"
:props="defaultProps"
@node-click="handleNodeClick"
default-expand-all
node-key="id"
:check-on-click-node="true"
show-checkbox
:filter-node-method="filterNode"
/>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, watch, defineProps, defineEmits } from "vue";
import { Search } from "@element-plus/icons-vue";
const props = defineProps(["dataAll", "checkedIds", "titles"]);
const filterTextLeft = ref("");
const filterTextRight = ref("");
const treeRef = ref();
const treeRefRight = ref();
const checkedLeft = ref(false);
const checkedRight = ref(false);
const defaultProps = {
children: "children",
label: "label",
};
watch(filterTextLeft, (val) => {
treeRef.value?.filter(val);
});
watch(filterTextRight, (val) => {
treeRefRight.value?.filter(val);
});
const filterNode = (value, data) => {
if (!value) return true;
return data.label.includes(value);
};
const checkList = ref([]);
const dataLeft = ref([]);
const dataRight = ref([]);
const emit = defineEmits(["changeChecked"]);
watch(
checkList,
() => {
let data = JSON.parse(JSON.stringify(props.dataAll));
data = filterTreeData(data, checkList.value, false);
dataLeft.value = data;
let dataR = JSON.parse(JSON.stringify(props.dataAll));
dataR = filterTreeData(dataR, checkList.value, true);
dataRight.value = dataR;
emit("changeChecked", checkList.value);
},
{ deep: true }
);
//穿梭
const filterTreeData = (treeData, checkList, flag) => {
return treeData.filter((node) => {
if (checkList.includes(node.id)) {
return flag;
} else if (node.children) {
node.children = filterTreeData(node.children, checkList, flag);
return node.children.length > 0;
}
return !flag;
});
};
//添加到右边
const setCheckedKeys = () => {
let checks = checkList.value;
treeRef.value.getCheckedNodes(true).map((item) => checks.push(item.id));
checkList.value = [...new Set(checks)];
};
//添加到左边
const setCheckedKeysLeft = () => {
let checks = [];
treeRefRight.value.getCheckedNodes(true).map((item) => checks.push(item.id));
checkList.value = checkList.value.filter((value) => !checks.includes(value));
};
//选中
const handleNodeClick = (nodeData) => {
nodeData.selected = !nodeData.selected;
};
const changeChecked = (treeData, ref) => {
// 遍历树的每个节点,设置所有子节点选中
if (checkedLeft.value || checkedRight.value) {
// 初始化存储所有 id 的数组
let allIds = [];
// 遍历树的每个节点,获取所有节点的 id
treeData.forEach((node) => {
getAllNodeIds(node, allIds);
});
ref?.setCheckedKeys(allIds);
} else {
ref?.setCheckedKeys([], false);
}
};
// 递归获取所有节点的 id
const getAllNodeIds = (node, ids) => {
ids.push(node.id);
if (node.children) {
node.children.forEach((child) => {
getAllNodeIds(child, ids);
});
}
};
onMounted(() => {
checkList.value = [...props.checkedIds];
});
</script>
<style scoped lang="less">
.selected {
background-color: rgb(14, 27, 31);
}
.main-div {
display: flex;
width: 100%;
height: 400px;
align-items: center;
}
.transfer-div {
display: inline-block;
width: calc(50% - 80px);
height: 100%;
border-radius: 4px;
box-sizing: border-box;
border: 1px solid #e4e7ed;
}
.title-div {
background: #f5f7fa;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 4px 16px;
}
.body-div {
padding: 12px;
height: calc(100% - 40px);
}
.button-transfer {
display: inline-block;
width: 160px;
text-align: center;
.button-div {
width: 46px;
height: 36px;
border-radius: 2px;
}
.el-button + .el-button {
margin-left: 8px;
}
}
.filter-tree {
height: calc(100% - 70px);
overflow: auto;
}
/* 滚动条 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-thumb {
background-color: #0003;
border-radius: 10px;
transition: all 0.2s ease-in-out;
}
::-webkit-scrollbar-track {
border-radius: 10px;
}
:deep .is-penultimate > .el-tree-node__content {
color: #626aef;
}
</style>
2.调用TreeTransfer组件
<template>
<!-- dialog -->
<el-dialog
v-model="dialogVisible"
title="标题"
width="800"
style="max-height: 80%"
>
<tree-transfer
:dataAll="dataAll"
:checkedIds="checkedIds"
:titles="titles"
@changeChecked="changeChecked"
/>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">
保存
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, defineExpose, onMounted } from "vue";
import TreeTransfer from "./TreeTransfer.vue";
const checkedIds = ref([]);
const titles = ref(["左标题", "右标题"]);
const dataAll = ref([
{ id: 100, label: "Level one 100" },
{
id: 1,
label: "Level one 1",
children: [
{
id: 4,
label: "Level two 1-1",
children: [
{
id: 14,
label: "Level three 1-1-1",
},
{
id: 10,
label: "Level three 1-1-2",
},
],
},
],
},
{
id: 2,
label: "Level one 2",
children: [
{
id: 5,
label: "Level two 2-1",
},
{
id: 6,
label: "Level two 2-2",
},
],
},
{
id: 3,
label: "Level one 3",
children: [
{
id: 7,
label: "Level two 3-1",
},
{
id: 8,
label: "Level two 3-2",
},
],
},
{
id: 9,
label: "Level one 4",
children: [
{
id: 12,
label: "Level two 4-1",
},
{
id: 13,
label: "Level two 4-2",
},
],
},
]);
const dialogVisible = ref(true);
const openDia = () => {
dialogVisible.value = true;
};
//改变选中项
const changeChecked = (val) => {
console.log("选中项:", val);
};
onMounted(() => {
checkedIds.value = [5, 6];
});
defineExpose({
openDia,
});
</script>
<style></style>
over,欢迎指导交流~