21、springboot3 vue3开发平台-前端-自定义树形穿梭框,用于角色权限分配

embedded/2024/9/22 22:56:21/

文章目录

  • 1. 使用原因
  • 2. 实现
  • 3. 使用

1. 使用原因

elemenutplus 有穿梭框,但是不支持树状数据的操作,所以这里自定义树状穿梭框,用于菜单权限分配, 如下:
在这里插入图片描述

2. 实现

这里主要是将菜单列表树解构后添加修改组合再恢复
src\components\TransferTree\index.vue

<template><el-dialog v-model="transferVisible" title="权限分配" width="800"><div class="transfer"><div class="contaner"><!----><div class="item"><div class="title">已分配权限</div><div class="tree"><el-tree ref="treeRef" :data="transData" :props="transProps" show-checkbox /></div></div><div class="transfercenter"><!----><div class="transbutton"><el-button type="primary" :icon="ArrowLeft" @click="leftButtonClick"></el-button><el-button type="primary"><el-icon class="el-icon--right" @click="rightButtonClick"><ArrowRight /></el-icon></el-button></div></div><!----><div class="item"><div class="title">未分配权限</div><div class="tree"><el-tree ref="treeRef1" :data="transData1" :props="transProps" show-checkbox /></div></div></div><div><div class="footer"><el-button type="primary" @click="onDialogFormConfirmTransfer">确 定</el-button><el-button @click="onDialogFormCancelTransFer">取 消</el-button></div></div></div></el-dialog>
</template><script lang="ts" setup>
import {ref } from 'vue'
import {ArrowLeft, ArrowRight  } from '@element-plus/icons-vue'const props = defineProps<{transDataAuth: Array<any>,transDataNoAuth:  Array<any>
}>()// 传回的菜单ids
const emit = defineEmits(['returnIds', "close"])const transferVisible = ref(true)
const transData = ref(props.transDataAuth)
const transData1 = ref(props.transDataNoAuth)
const treeRef = ref() 
const treeRef1= ref() const transProps = {id: "roleId",children: 'children',label: 'menuName',
}// 提交对话框表单按钮事件
const onDialogFormConfirmTransfer = async () => {let authMenus = flattenMenuTree(props.transDataAuth)let ids = authMenus.map((item: any) => item.id)emit('returnIds', ids)
}// 取消对话表单框按钮事件
const onDialogFormCancelTransFer = () => {emit('close')
}// 授权按钮
const leftButtonClick = () => {let selctedMenus = treeRef1.value.getCheckedNodes(false, false)let ids = selctedMenus.map((item: any) => item.id)// 解构菜单树let authList = flattenMenuTree(transData.value)let notAuthList = flattenMenuTree(transData1.value)// 添加授权,删除未授权菜单authList.push(...selctedMenus)notAuthList = notAuthList.filter((item: any) => !ids.includes(item.id))// 重新构建菜单树transData.value = []transData1.value = []transData.value = buildMenuTree(filterRepetition(authList))transData1.value = buildMenuTree(filterRepetition(notAuthList))
}// 取消授权按钮
const rightButtonClick = () => {let selctedMenus = treeRef.value.getCheckedNodes(false, false)let ids = selctedMenus.map((item: any) => item.id)// 解构菜单树let authList = flattenMenuTree(transData.value)let notAuthList = flattenMenuTree(transData1.value)// 添加授权,删除未授权菜单notAuthList.push(...selctedMenus)authList = authList.filter((item: any) => !ids.includes(item.id))// 重新构建菜单树transData.value = buildMenuTree(filterRepetition(authList))transData1.value = buildMenuTree(filterRepetition(notAuthList))
}// 构建列表树
function buildMenuTree(menuList: any) {  // 创建一个映射,以便快速通过ID查找菜单项  const map = new Map(menuList.map((item: any) => [item.id, { ...item, children: [] }]));  // 遍历菜单列表,构建树结构  function attachChildren(parentId = 0) {  // 查找所有具有指定parentId的菜单项  return menuList.filter((item: any) => item.parentId === parentId).map((item: any) => {  const currentNode: any = map.get(item.id)// 递归地为当前节点附加子节点  currentNode.children = attachChildren(item.id)return currentNode;  });  }  // 注意:顶级菜单项(没有parentId或parentId为null/undefined)  const tree = attachChildren();  return tree;  
} function flattenMenuTree(tree: any) {  let result: any = [] // 递归函数来遍历树并收集节点  function traverse(nodes: any) { if (nodes) {nodes.forEach((node: any) => {  // 将当前节点添加到结果数组中  result.push({ ...node});if (node.children && node.children.length > 0) {  traverse(node.children);}  })}}    // 开始遍历树  traverse(tree);  // 如果不需要层级信息,直接返回结果数组  return result;  
}  // 过滤重复的菜单
const filterRepetition = (menulist: any) => {// console.log("menulist: " + menulist)let seenIds = new Set();  // 过滤菜单项  let menus = menulist.filter((item: any) => {  // 如果当前项的id在Set中不存在,则添加到Set中,并返回true(保留该项)  // 否则,返回false(移除该项)  if (!seenIds.has(item.id)) {  seenIds.add(item.id);  return true;  }  return false;  })//console.log("menulist: " + menulist)return menus
}</script><style lang="scss">
.transfer {display: flex;flex-direction: column;.contaner {display: flex;flex-direction: row;justify-content: space-between;height: 500px;.item {display: flex;flex-direction: column;width: 40%;height: 100%;.title {text-align: center;background-color: #F5F7FA;height: 6%;font-size: 16px;}.tree {overflow-y: scroll;height: 94%;}}.transfercenter {display: flex;padding-top: 30px;box-sizing: border-box;width: 20%;height: 100%;// border: 10px solid black; /* 边框宽度、样式、颜色 */// background-color: #FF0000;.transbutton {margin-top: 200px;}}}.footer {display: flex;flex-direction: row;justify-content: center;align-items: center;}
}
</style>

3. 使用

在这里插入图片描述
在这里插入图片描述


http://www.ppmy.cn/embedded/98488.html

相关文章

Vue3通信方式 provide与inject

父子组件传参可以通过props和emit来实现&#xff0c;但是当组件的层次结构比较深时&#xff0c;props和emit就没什么作用了。vue为了解决这个提出了Provide / Inject //vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据 import { ref, provide } from "vue…

LLVM - 编译器后端-指令选择

一:概述 任何后端的核心都是指令选择。LLVM 实现了几种方法;在本篇文章中,我们将通过选择有向无环图(DAG)和全局指令选择来实现指令选择。 在本篇文章中,我们将学习以下主题: • 定义调用约定规则:本节展示如何在目标描述中设置调用约定的规则。 • 通过选择 D…

应用界面设计(原生,自定义控件,设计与交互-小白必看)

目录 一图概览 界面实现方式 XML布局方式 JAVA或Kotlin代码布局 Android定制控件 Android原生核心控件 为什么不用原生&#xff1f; 定制控件三大方法 定制控件流程 触摸事件&#xff08;input事件处理&#xff09;流程 分发 (dispatchTouchEvent) 拦截 (onInterce…

基于x86_64系统构建并运行aarch64架构docker镜像

基于x86_64系统构建并运行aarch64架构docker镜像 1.安装qemu模拟器2.编写Dockerfile3.查看镜像架构4.启动容器 1.安装qemu模拟器 docker run --privileged --rm tonistiigi/binfmt --install all如果出现invalid argument等信息&#xff0c;表示qemu安装失败。可能是内核版本问…

RabbitMQ消息队列:概念、单节点和集群示例

目录 消息队列 概念 主流的消息队列 消息队列名词 &#xff08;1&#xff09;Broker &#xff08;2&#xff09;Topic &#xff08;3&#xff09;Producer &#xff08;4&#xff09;Consumer &#xff08;5&#xff09;Queue &#xff08;6&#xff09;Message 消息…

TilesetLaye存在时,使用mask遮罩层,会出现锯齿的解决方案

TilesetLaye存在时&#xff0c;使用mask遮罩层&#xff0c;会出现锯齿 function addDemoGeoJsonLayer1() {const tiles3dLayer new mars3d.layer.TilesetLayer({name: "合肥市建筑物",url: "//data.mars3d.cn/3dtiles/jzw-hefei/tileset.json",maximumSc…

场外个股期权是不是个股期权?场外个股期权和个股期权的区别

今天带你了解场外个股期权是不是个股期权&#xff1f;场外个股期权和个股期权的区别。场外个股期权是指在沪深交易所之外交易的个股期权&#xff0c;其本质是一种金融衍生品&#xff0c;允许投资者在股票交易场所外以特定价格买进或卖出证券。 个股期权作为一种重要的投资工具…

微信小程序如何自定义一个组件

微信小程序支持组件化开发&#xff0c;这有助于我们复用代码&#xff0c;提高开发效率。下面我将给出一个简单的微信小程序组件化示例&#xff0c;包括一个自定义组件的创建和使用。 1. 创建自定义组件 首先&#xff0c;在项目的 components 目录下创建一个新的组件文件夹&am…