概要
项目要实现树形穿梭框的效果,没事哒 没事哒~难不倒打工人
此处直接放弃用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,欢迎指导交流~