vue实现自定义树形穿梭框功能

2024-03-01 20:04

本文主要是介绍vue实现自定义树形穿梭框功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

需求:

我们在开发过程中,会遇到需要将一个数据选择做成穿梭框,但是要求穿梭框左侧为树形结构、右侧为无层级结构的数据展示,ElementUI自身无法在穿梭框中添加树形结构,网上搜到了大佬封装的插件但是对于右侧的无树形结构一点还是不太满足。以下是我们简单的封装写的组件可以实现此功能

在这里插入图片描述

1,封装的treeTransfetr组件如下:
<template><div class="treeTransfer"><!-- 左边 --><div class="leftTree"><div class="list"><div class="left_lowline"><div class="leftcheck_con"><el-checkbox v-model="checkedLeft" :disabled="leftTreeData.length < 1" label="" size="large"style="margin-right: 12px" @change="leftAllCheck" /><p class="left_title">{{ props.title[0] }}</p></div><div>{{ leftOperation.length }}/{{ leftAllselectIdarry.length }}</div></div><el-tree ref="leftTreeRef" :data="leftTreeData" show-checkboxnode-key="id" :props="props.defaultProps" v-slot="{ node, data }" accordion:check-strictly="true"@check="onCheckLeft" default-expand-all><div>{{ data.label }}</div></el-tree></div></div><!-- 中间按钮 --><div class="btnDiv"><div class="mg10"><el-button @click="toRight()" icon="ele-Right" type="primary"  circle :disabled="leftOperation.length < 1"/></div><div class="mg10"><el-button @click="toLeft()" icon="ele-Back" type="primary" circle :disabled="rightOperation.length < 1"/></div></div><!-- 右边 --><div class="rightTree"><div class="list"><div class="left_lowline"><div class="leftcheck_con"><el-checkbox v-model="checkedRight" :disabled="rightTreeData.length < 1" label="" size="large"style="margin-right: 12px" @change="rightAllCheck" /><p class="left_title">{{ props.title[1] }}</p></div><div>{{ rightOperation.length }}/{{ rightAllselectIdarry.length }}</div></div><el-tree ref="rightTreeRef" :data="rightTreeData" show-checkbox node-key="id":props="props.defaultProps" v-slot="{ node, data }" accordion :check-strictly="true"@check="onCheckRight" default-expand-all><div>{{ data.label }}</div></el-tree></div></div></div></template><script setup lang="ts">import { ref, onMounted, watch, nextTick } from 'vue';import lodash from 'lodash-es'const props = defineProps(['fromData', 'toData', 'defaultProps', 'title', 'visible']);const checkedLeft = ref(false)const checkedRight = ref(false)const leftOperation = ref<any[]>([])const rightOperation = ref<any[]>([])// 定义emitconst emits = defineEmits(['addStaffchange']);const leftTreeRef = ref();const rightTreeRef = ref();// 左侧数据const leftTreeData = ref([] as any);// 右侧数据const rightTreeData = ref([] as any);// 左侧可以选中id集合const leftAllselectIdarry = ref([] as any)// 右侧可以选中id集合const rightAllselectIdarry = ref([] as any)watch(props,newVal => {leftTreeData.value = lodash.cloneDeep(newVal.fromData)rightTreeData.value = lodash.cloneDeep(newVal.toData)// 获取左侧的全选中的idleftAllselectIdarry.value = getAllIds(leftTreeData.value, [])if (newVal.visible) {checkedLeft.value = falsecheckedRight.value = falseleftOperation.value = []rightOperation.value = []nextTick(() => {leftTreeRef?.value.setCheckedKeys([])})}},{ immediate: true })defineExpose({leftTreeData,rightTreeData})onMounted(() => {})// 去右边const toRight = async () => {const leftTree = leftTreeRef.value;if (!leftTree) {return}const leftNodes = leftTree.getCheckedNodes(false, false)const checkedKeys = leftTree.getCheckedKeys(false)const rightTree = rightTreeRef.valueconst newArr = rightTreeData.value.concat(leftNodes)let obj = {};let peon = newArr.reduce((cur,next) => {obj[next['id']] ? "" : obj[next['id']] = true && cur.push(next);return cur;},[]) //设置cur默认类型为数组,并且初始值为空的数组const getnewleftArry = peon.map(item => {return {id: item.id,label: item.label,pid: item.pid,children: [],}})rightTreeData.value = getnewleftArryleftOperation.value = leftTreeRef.value?.getCheckedKeys(false)emits('addStaffchange', checkedKeys)setTimeout(() => {rightTree?.setCheckedNodes(leftNodes);rightOperation.value = rightTreeRef.value?.getCheckedKeys(false)rightAllcheckChange()}, 500)};// 去左边const toLeft = async () => {const rightTree = rightTreeRef.valueif (!rightTree) {return}const checkedKeys = rightTree.getCheckedKeys(false)for(var i=0; i<rightTreeData.value.length;i++){if(checkedKeys.includes(rightTreeData.value[i].id)){rightTreeData.value.splice(i,1)i-=1}}rightOperation.value = rightTree?.getCheckedKeys(false)if (rightTreeData.value && rightTreeData.value.length === 0) {checkedRight.value = false}emits('addStaffchange', getAllIds(rightTreeData.value, []))};//左侧选中const onCheckLeft = () => {leftOperation.value = leftTreeRef.value?.getCheckedKeys(false)if (leftOperation.value.length === leftAllselectIdarry.value.length) {checkedLeft.value = true} else {checkedLeft.value = false}}// 右侧选中const onCheckRight = () => {rightOperation.value = rightTreeRef.value?.getCheckedKeys(false)rightAllselectIdarry.value.length = getAllIds(rightTreeData.value, []).lengthrightAllcheckChange()}// 右侧是否全选获取function rightAllcheckChange () {rightAllselectIdarry.value.length = getAllIds(rightTreeData.value, []).lengthif (rightOperation.value.length === rightAllselectIdarry.value.length) {checkedRight.value = true} else {checkedRight.value = false}return checkedRight.value}// 左侧全选中值 const leftAllCheck = () => {if (checkedLeft.value) {leftTreeRef.value.setCheckedKeys(getAllIds(leftTreeData.value, []))checkedLeft.value = true;} else {leftTreeRef.value.setCheckedKeys([])checkedLeft.value = false}leftOperation.value = leftTreeRef.value?.getCheckedKeys(false)}// 右侧全选中值 const rightAllCheck = () => {if (checkedRight.value) {rightTreeRef.value.setCheckedKeys(getAllIds(rightTreeData.value, []))checkedRight.value = true} else {rightTreeRef.value.setCheckedKeys([])checkedRight.value = false}rightOperation.value = rightTreeRef.value?.getCheckedKeys(false)}// 递归获取所有id数据function getAllIds(tree, result) {//遍历树获取id数组for (const i in tree) {if (!tree[i].disabled) {result.push(tree[i].id); // 遍历项目满足条件后的操作}if (tree[i].children) {// 存在子节点就递归getAllIds(tree[i].children, result);}}return result;}</script><style scoped lang="scss">.treeTransfer {display: flex;justify-content: center;.el-tree {overflow: auto;max-height: 360px;}.leftTree {border: 1px solid #ebeef5;width: 40%;height: calc(100% - 60px);overflow: auto;}.left_lowline {display: flex;align-items: center;justify-content: space-between;background: #f5f7fa;padding: 0 23px 0 10px;.leftcheck_con {display: flex;align-items: center;}}.btnDiv {width: 20%;height: calc(100% - 60px);text-align: center;margin: auto 0;line-height: 40px;position: relative;}.rightTree {width: 40%;height: calc(100% - 60px);}}</style>
2,具体使用如下
<treeTransfetr ref="treeTransferRef" :fromData="fromData":toData="toData":visible="visible":defaultProps="transferProps" @addStaffchange="addStaffchange" :title="['筛选结果', '添加人员']"></treeTransfetr>let treeTransferRef = ref(); // 树形穿梭框
let fromData = ref([{id: "1",pid: 0,    //自定义pid的参数名,默认为"pid" 必填:falselabel: "张三-D1-DM",disabled: false,children: [{id: "1-1",pid: "1",label: "李四-D1-TL",disabled: false,children: []},{id: "1-2",pid: "1",label: "王五-D2-TL",disabled: false,children: [{id: "1-2-1",pid: "1-2",children: [],label: "赵六-D3-TL",disabled: true,},{id: "1-2-2",pid: "1-2",children: [],label: "李明-D4-TL",disabled: false,},{id: "1-2-3",pid: "1-2",children: [],label: "王三明-D5-TL",disabled: false,}]}]}
]); // 树形数据
let toData = ref([]); // 选中的ids数据
const transferProps = ref({label: 'label',children: 'children',disabled: 'disabled',
});

如果我们想要用插件实现,推荐使用el-tree-transfer

这篇关于vue实现自定义树形穿梭框功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763644

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time