当前位置: 首页 > news >正文

vue2+el的树形穿梭框

请添加图片描述
组件

<template><!-- 自定义树形穿梭框组件、现在左侧和右侧均为树形结构 --><div class="tree-transfer"><!-- 穿梭框左侧 --><div class="tree-transfer-left"><!-- 左侧采用element-ui的el-tree --><el-treeref="treeLeft":data="dataLeft"show-checkboxnode-key="key":props="defaultProps"></el-tree></div><!-- 穿梭框中间按钮区域 --><div class="tree-transfer-middle"><el-button circle type="info" icon="el-icon-arrow-left" @click="remove"></el-button><el-button circle type="info" icon="el-icon-arrow-right" @click="add"></el-button></div><!-- 穿梭框右侧 --><div class="tree-transfer-right"><!-- 右侧现在也保持树形结构 --><el-treeref="treeRight":data="dataRight"show-checkboxnode-key="key":props="defaultProps"></el-tree></div></div>
</template><script>export default{props:['datas','defaultProps'],data(){  return{yuansiData:[],dataLeft:[],dataRight:[]}},mounted() {this.dataLeft = JSON.parse(JSON.stringify(this.datas))this.yuansiData = JSON.parse(JSON.stringify(this.datas))},methods:{// 查找节点及其所有子节点findNodeAndChildren(key, tree) {let result = nullfor (let node of tree) {if (node.key === key) {return JSON.parse(JSON.stringify(node))}if (node.parameterInfoList && node.parameterInfoList.length > 0) {result = this.findNodeAndChildren(key, node.parameterInfoList)if (result) return result}}return null},// 查找节点在原始数据中的完整路径(所有祖先节点)findNodePath(key, tree, path = []) {for (let node of tree) {if (node.key === key) {return [...path, node]}if (node.parameterInfoList && node.parameterInfoList.length > 0) {const foundPath = this.findNodePath(key, node.parameterInfoList, [...path, node])if (foundPath) return foundPath}}return null},// 查找目标树中是否已存在指定key的节点findNodeInTree(key, tree) {for (let node of tree) {if (node.key === key) {return node}if (node.parameterInfoList && node.parameterInfoList.length > 0) {const foundNode = this.findNodeInTree(key, node.parameterInfoList)if (foundNode) return foundNode}}return null},// 从树中删除节点removeNodeFromTree(key, tree) {for (let i = 0; i < tree.length; i++) {if (tree[i].key === key) {tree.splice(i, 1)return true}if (tree[i].parameterInfoList && tree[i].parameterInfoList.length > 0) {if (this.removeNodeFromTree(key, tree[i].parameterInfoList)) {// 如果删除子节点后父节点的子节点列表为空,可以选择是否保留父节点// 这里选择保留父节点return true}}}return false},// 添加节点到右侧树 - 优化版本:带祖先节点add() {// 获取用户直接选中的节点(不包含半选状态的父节点)const checkedNodes = this.$refs.treeLeft.getCheckedNodes(false, false)// 对于每个选中的节点checkedNodes.forEach(node => {// 查找节点在原始数据中的完整路径(所有祖先节点)const nodePath = this.findNodePath(node.key, this.yuansiData)if (nodePath) {// 从原始数据中查找完整的节点及其子节点const fullNodeWithChildren = this.findNodeAndChildren(node.key, this.yuansiData)// 1. 将节点从左侧树中删除this.removeNodeFromTree(node.key, this.dataLeft)// 2. 确保所有祖先节点都存在于右侧树中let currentTree = this.dataRightlet parentNode = null// 遍历除最后一个节点(目标节点)外的所有祖先节点for (let i = 0; i < nodePath.length - 1; i++) {const ancestorNode = nodePath[i]let existingNode = this.findNodeInTree(ancestorNode.key, currentTree)if (!existingNode) {// 如果祖先节点不存在,创建一个新的(只包含基本信息)existingNode = {key: ancestorNode.key,name: ancestorNode.name,parameterInfoList: []}currentTree.push(existingNode)}// 确保存在子节点数组if (!existingNode.parameterInfoList) {existingNode.parameterInfoList = []}parentNode = existingNodecurrentTree = existingNode.parameterInfoList}// 3. 将目标节点添加到正确的位置if (parentNode) {// 检查目标节点是否已经存在于父节点的子节点列表中const existingTargetNode = this.findNodeInTree(fullNodeWithChildren.key, parentNode.parameterInfoList)if (!existingTargetNode) {parentNode.parameterInfoList.push(fullNodeWithChildren)}} else {// 如果没有祖先节点,直接添加到右侧树的根节点const existingTargetNode = this.findNodeInTree(fullNodeWithChildren.key, this.dataRight)if (!existingTargetNode) {this.dataRight.push(fullNodeWithChildren)}}}})// 清空选中状态this.$refs.treeLeft.setCheckedNodes([])},// 从右侧移除节点到左侧 - 优化版本:带祖先节点remove() {// 获取用户直接选中的节点(不包含半选状态的父节点)const checkedNodes = this.$refs.treeRight.getCheckedNodes(false, false)// 对于每个选中的节点checkedNodes.forEach(node => {// 从右侧树中删除节点this.removeNodeFromTree(node.key, this.dataRight)// 检查是否还有该节点的其他子节点在右侧树中const hasRemainingDescendants = this.checkDescendantsExist(node.key, this.yuansiData, this.dataRight)if (!hasRemainingDescendants) {// 如果没有剩余的子节点在右侧树中,将节点添加回左侧树const nodeToAddBack = this.findNodeAndChildren(node.key, this.yuansiData)if (nodeToAddBack) {// 确保左侧树中存在所有祖先节点let currentTree = this.dataLeftlet parentNode = null// 查找节点在原始数据中的完整路径const nodePath = this.findNodePath(node.key, this.yuansiData)if (nodePath) {// 遍历除最后一个节点(目标节点)外的所有祖先节点for (let i = 0; i < nodePath.length - 1; i++) {const ancestorNode = nodePath[i]let existingNode = this.findNodeInTree(ancestorNode.key, currentTree)if (!existingNode) {// 如果祖先节点不存在,创建一个新的existingNode = {key: ancestorNode.key,name: ancestorNode.name,parameterInfoList: []}currentTree.push(existingNode)}// 确保存在子节点数组if (!existingNode.parameterInfoList) {existingNode.parameterInfoList = []}parentNode = existingNodecurrentTree = existingNode.parameterInfoList}// 将目标节点添加到正确的位置if (parentNode) {// 检查目标节点是否已经存在const existingTargetNode = this.findNodeInTree(nodeToAddBack.key, parentNode.parameterInfoList)if (!existingTargetNode) {parentNode.parameterInfoList.push(nodeToAddBack)}} else {// 如果没有祖先节点,直接添加到左侧树的根节点const existingTargetNode = this.findNodeInTree(nodeToAddBack.key, this.dataLeft)if (!existingTargetNode) {this.dataLeft.push(nodeToAddBack)}}}}}})// 清空选中状态this.$refs.treeRight.setCheckedNodes([])},// 检查节点的后代是否还存在于目标树中checkDescendantsExist(key, sourceTree, targetTree) {// 查找原始树中该节点的所有后代const node = this.findNodeAndChildren(key, sourceTree)if (!node || !node.parameterInfoList) return false// 递归检查后代是否存在const hasDescendant = (node, targetTree) => {for (let child of node.parameterInfoList) {if (this.findNodeInTree(child.key, targetTree)) {return true}if (child.parameterInfoList && child.parameterInfoList.length > 0 && hasDescendant(child, targetTree)) {return true}}return false}return hasDescendant(node, targetTree)},// 获取右侧结果getResult() {return this.dataRight}}}
</script><style scoped lang="less">.tree-transfer{display: flex;min-height: 250px;.tree-transfer-left{min-width: 200px;border:1px #E5E5E5 solid;border-radius: 10px;padding: 10px;}.tree-transfer-middle{display: flex;justify-content: center;align-items: center;min-width: 120px;}.tree-transfer-right{min-width: 200px;border:1px #E5E5E5 solid;border-radius: 10px;padding: 10px;}}
</style>

使用

<template><div><tree-transfer ref="treeTransfer" :datas="selectItem" :defaultProps="defaultProps"></tree-transfer></div>
</template><script>
import treeTransfer from '@/treeTransfer.vue'
export default {components: {treeTransfer},defaultProps: {children: 'parameterInfoList',label: 'name'},data() {return {selectItem: [{key: 1,name: 'Node 1',parameterInfoList: [{ key: 4, name: 'Node 1-1' },{ key: 5, name: 'Node 1-2' },],},{key: 2,name: 'Node 2',parameterInfoList: [{ key: 6, name: 'Node 2-1' },{ key: 7, name: 'Node 2-2' },],},{key: 3,name: 'Node 3',parameterInfoList: [{ key: 8, name: 'Node 3-1' },{ key: 9, name: 'Node 3-2' },],},],value: [],defaultProps: {children: 'parameterInfoList',label: 'name'}};},methods: {}
};
</script><style scoped>
/* 自定义样式 */
</style>

文章转载自:

http://GDLXcc6D.srprm.cn
http://H3J5dPgo.srprm.cn
http://dqZtlxkP.srprm.cn
http://1mogVnG1.srprm.cn
http://wjiqTVmT.srprm.cn
http://QXMx1ch0.srprm.cn
http://PgOg6e8f.srprm.cn
http://8utYq0hU.srprm.cn
http://brUwQBmw.srprm.cn
http://9KFuzDeZ.srprm.cn
http://gd9EMwv7.srprm.cn
http://mCv8bF4X.srprm.cn
http://UUAvHB1N.srprm.cn
http://xlmVbXAa.srprm.cn
http://jxot9Uxl.srprm.cn
http://jwk4tCu7.srprm.cn
http://pzSQ0m9c.srprm.cn
http://SJrNxhBh.srprm.cn
http://TinntC16.srprm.cn
http://moMA3AOH.srprm.cn
http://kjkPCTxP.srprm.cn
http://gwxdFarT.srprm.cn
http://wjHSQ5jS.srprm.cn
http://FAnbw1bK.srprm.cn
http://BN32MW6k.srprm.cn
http://zW3yDSa5.srprm.cn
http://gJHWoxRt.srprm.cn
http://gIrH5lgs.srprm.cn
http://pjmhbXHC.srprm.cn
http://dGd1VrH4.srprm.cn
http://www.dtcms.com/a/373812.html

相关文章:

  • JuiceFS分布式文件系统
  • 【数据结构】简介
  • MindShow AI:高效生成思维导图的实用AI工具
  • python 通过selenium调用chrome浏览器
  • Spring Cloud Alibaba快速入门02-Nacos(中)
  • Redis集群(redis cluster (去中心化))
  • 无人机航拍数据集|第39期 无人机玉米雄穗目标检测YOLO数据集776张yolov11/yolov8/yolov5可训练
  • PCB下单厂家有哪些?可pcb在线下单厂家
  • 安卓服务的两种启动方式有什么区别
  • Spring Cloud Alibaba 是什么,怎么简单搭建
  • ARM-寄存器与异常处理全解析
  • 2024年6月GESPC++三级真题解析(含视频)
  • 【面试题】Transformer应用实践专题
  • 借助Wisdom SSH,轻松搭建私有云盘
  • Linux基础知识(三)
  • Flink KeyedProcessFunction为什么能为每个key定义State和Timer?
  • 【ARDUINO】通过ESP8266连接WIFI,启动TCP,接受TCP客户端指令【待测试】
  • Azure Data Factory (ADF) vs Azure Logic Apps: 对比分析
  • 软考-系统架构设计师 企业资源规划(ERP)详细讲解
  • 农产品运输与调度服务平台的设计与实现
  • Dart → `.exe`:Flutter 桌面与纯命令行双轨编译完全指南
  • 栈专题之每日温度
  • 远场学习_FDTD_dipole(1)
  • 编译缓存工具 sccache 效果对比
  • 【MFC典型类和函数:CString的字符串魔法与Afx全局函数的便利店】
  • 【MFC】对话框属性:字体 (Font Name) 和 大小 (Font Size)
  • 搜索框设计实用指南:规范、模板与工具全解析
  • Python调用MCP:无需重构,快速为现有应用注入AI与外部服务能力!
  • HTTPS 抓包难点分析,从端口到工具的实战应对
  • 构建第二大脑的两种范式:Notion与Obsidian的终极哲学对决与实践指南