Vue3入门 - ElementPlus中左侧菜单和Tabs菜单组合联动效果

        在Vue3中,ElementPlus是使用比较广泛的UI组件库,提供了丰富的界面元素支持项目开发需求。在后台管理系统中,左侧或顶部的菜单栏通常包含多个子菜单项,通过菜单的展开和收缩功能,用户可以方便地查看或隐藏不需要的菜单项,从而优化界面布局,提供用户体验。

        在ElementPlus中,可通过修改属性default-openeds和unique-opened来实现菜单项的展开和收缩.、默认展开等功能;并使用JS数组中reduce方法来完成default-openeds数组读取。

一、界面搭建

       Vue3的项目创建之前篇幅已讲过,这里就不再讲Vue3项目创建相关基础内容了。

        先在项目中创建一个菜单组件(Navigation.vue),代码如下:

<template><el-row><el-col :span="5"><el-menu:default-active="defaultActive":default-openeds="defaultOpeneds"class="el-menu-vertical-demo"><el-sub-menu :index="item.id+''" v-for="(item, index) in menuList" :key="item.id"><template #title><span>{{item.name}}</span></template><el-sub-menu :index="sub.id+''" v-for="(sub, idx) in item.children" :key="sub.id"><template #title>{{sub.name}}</template><el-menu-item :index="third.id+''" v-for="(third, i) in sub.children" :key="third.id"@click="selectItemEvent(third)">{{third.name}}</el-menu-item></el-sub-menu></el-sub-menu></el-menu></el-col><el-col :span="1">&nbsp;</el-col><el-col :span="18"><el-tabs v-model="defaultActive" class="demo-tabs"><el-tab-pane :label="item.name" :name="item.id+''" v-for="(item, index) in tabsList" :key="item.id">{{item.name}}</el-tab-pane></el-tabs></el-col></el-row>
</template><script lang="ts" setup>
import { reactive, ref } from 'vue'
type MenuType = {id: numbername: stringpath?: stringchildren?: Array<MenuType>
}
const menuList = reactive([{"id": 1000,"name": "商品模块","path": "goods/index","children": [{"id": 10001000,"name": "基本信息","path": "goods/base/index","children": [{"id": 1000100101,"name": "年份信息","path": "goods/base/year"},{"id": 1000100102,"name": "颜色信息","path": "goods/base/color"},{"id": 1000100103,"name": "商品信息","path": "goods/base/info"}]}]},{"id": 2000,"name": "人事信息","path": "personnel/index","children": [{"id": 20001000,"name": "基本信息","path": "personnel/base/index","children": [{"id": 2000100101,"name": "部门信息","path": "personnel/base/unit"},{"id": 2000100102,"name": "社保信息","path": "personnel/base/social"},{"id": 2000100103,"name": "人事信息","path": "personnel/base/info"}]}]},{"id": 3000,"name": "会员信息","path": "account/index","children": [{"id": 30001000,"name": "基础信息","path": "account/base/index","children": [{"id": 3000100101,"name": "会员等级","path": "account/base/level"},{"id": 3000100102,"name": "会员信息","path": "account/base/info"}]}]}
])
const defaultOpeneds = reactive([])    // 默认展开项
const defaultActive = ref('1000100101') // 当前选择菜单的index
const tabsList = reactive([])            // tab菜单数据列表// 菜单点击事件
const selectItemEvent = (item: MenuType) => {}
</script>

        组件完成后,将其在App.vue中引入注入到html代码中。

<script setup lang="ts">
import { RouterView } from 'vue-router'
import Navigation from '@/components/Navigation.vue'
</script><template><Navigation></Navigation><RouterView />
</template>

二、保持一个子菜单的展开

        当将Menu菜单的属性unique-opened设置为true(是否只保持一个子菜单的展开,默认为false),则始终保持一个分类的展开,其他分类会自动收缩的效果。

        代码如下:

<template><el-row><el-col :span="5"><el-menu:default-active="defaultActive":default-openeds="defaultOpeneds":unique-opened="true"class="el-menu-vertical-demo"><el-sub-menu :index="item.id+''" v-for="(item, index) in menuList" :key="item.id"><template #title><span>{{item.name}}</span></template><el-sub-menu :index="sub.id+''" v-for="(sub, idx) in item.children" :key="sub.id"><template #title>{{sub.name}}</template><el-menu-item :index="third.id+''" v-for="(third, i) in sub.children" :key="third.id"@click="selectItemEvent(third)">{{third.name}}</el-menu-item></el-sub-menu></el-sub-menu></el-menu></el-col><el-col :span="1">&nbsp;</el-col><el-col :span="18"><el-tabs v-model="defaultActive" class="demo-tabs"><el-tab-pane :label="item.name" :name="item.id+''" v-for="(item, index) in tabsList" :key="item.id">{{item.name}}</el-tab-pane></el-tabs></el-col></el-row>
</template>

三、Tabs菜单追加数据

        当点击左侧菜单里,判断右侧tabList是否存在该项,不存在追加即可。并修正当前选中项的index,代码如下:

<script lang="ts" setup>
import { reactive, ref } from 'vue'
type MenuType = {id: numbername: stringpath?: stringchildren?: Array<MenuType>
}
const menuList = reactive([// 略....
])
let defaultOpeneds = reactive([])   // 默认展开项
const defaultActive = ref('') // 当前选择菜单的index
const tabsList = reactive([]) // tab菜单数据列表// 菜单点击事件
const selectItemEvent = (item: MenuType) => {defaultActive.value = item.id + ''// tabList中不存在则追加if(!tabsList.some((sub) => sub.id == item.id)) {tabsList.push(item)}
}
</script>

        上述代码完成后,点击左侧菜单,则横向tabs菜单就显示出来了。并且则于el-tabs组件上是通过v-model="defaultActive"进行双向数据绑定的,所以tabs组件点击后,左侧菜单也会跟期一起联动显示出对应的菜单项。

三、默认展开菜单项

        在系统加载完毕后,可以通过default-openeds属性默认展开某个菜单分类;另外,系统一般会缓存上次菜单选择项的index数据,此时我们就可以读取该数据,通过它向上反查要默认打开菜单分类。

        如下图,要是默认展开第一个分类”商品模块“,则default-openeds的值应为['1000', '10001000'];上次打开如是”年份信息“项菜单,如何通过它获取到['1000', '10001000']呢?这个就使用到万能的Array.reduce()方法了,下面将通过它提取出default-openeds的值。

        通过当前菜单信息id,反查所有上级id数据;在setup中,增加Recursive()弟归函数,用于反查上级id数组并返回default-openeds属性需要的数组index。代码如下:

<script lang="ts" setup>
import { reactive, ref } from 'vue'
type MenuType = {id: numbername: stringpath?: stringchildren?: Array<MenuType>
}
const menuList = reactive([// 略...
])
const defaultOpeneds = ref<string[]>([])   // 默认展开项
const defaultActive = ref('') // 当前选择菜单的index
const tabsList = reactive([]) // tab菜单数据列表// 递归数据
const Recursive = (menu: Array<MenuType>, key: number) : string[] => {return menu.reduce((alls: string[], item) => {// 判断其是否为数组,并且存在子项开始查询if(Array.isArray(item.children) && item.children.length > 0) {// 查询是否有符合其id项,有则为本元素id追加进数组中if(item.children.some(sub => sub.id == key)) {return alls.concat([item.id+''])}// 未查询到,则递归继续查询else {const data: string[] = Recursive(item.children, key)// 递归查询到数组存在,则将本元素id追加进数组,并放在首位if(data.length>0) return [item.id+''].concat(data)}} return alls}, [])
}
// 默认展开项(这里1000100101先写,实际项目中在缓存中读取)
defaultOpeneds.value = Recursive(menuList, 1000100101)// 菜单点击事件
const selectItemEvent = (item: MenuType) => {// 修正当前选中的index标识(通过vuex或pinia维护该数据时,可将其缓存到本地)defaultActive.value = item.id + ''// tabList中不存在则追加if(!tabsList.some((sub) => sub.id == item.id)) {tabsList.push(item)}
}
</script>

        此时,则可以通过当前菜单id(即html渲染时的index)向上反查上级菜单的index数据了,如下图:

        页面加载完毕后,则会显示默认展开项了,如下图:

        当然,这里只是演示demo。项目中需要将菜单数据、tabs菜单数据、默认展开的菜单、默认选中等,写入Vuex或Pinia中,根据自己项目需求进行调整即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1541726.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

数字世界中的浪漫:揭秘会跳动的爱心

在编程的世界里&#xff0c;复杂的技术可以与艺术产生美妙的碰撞。无论是通过代码实现动态效果&#xff0c;还是用算法绘制图案&#xff0c;程序员都可以成为数字艺术的创作者。而今天&#xff0c;我们将通过 Python 的强大 GUI 工具库 Tkinter&#xff0c;用简单的代码生成一颗…

U-Boot顶层Makefile详解

直接参考【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81 本文仅作为个人笔记使用&#xff0c;方便进一步记录自己的实践总结。 上一章我们详细的讲解了 uboot 的使用方法&#xff0c;其实就是各种命令的使用&#xff0c;学会 uboot 使用以后就可以尝试移植 uboot 到自己的开发…

linux操作系统的基本命令

1.linux下的文件系统 在linux操作目录下没有像window操作系统下盘符的概念,只有一个根目录/,所有文件目录都在它的下面 linux的目录结构: 在Linux系统中: 文件都从跟目录开始的,用/表示文件名称区分大小写路径都是以/俩进行分隔(windown用\分隔)以.开头的文件为隐藏文件 Li…

鸿蒙开发之ArkUI 界面篇 十七 购物综合案例

layoutWeight:子元素与兄弟元素主轴方向按照权重进行分配,参数是联合类型&#xff0c;数字或者是字符串&#xff0c;在指定的空间占多少份额&#xff0c;数字越大&#xff0c;表示在空间中占用的份额越多&#xff0c;如果父容器的子组件没有别的指定&#xff0c;剩下的空间全部…

10分钟一条童装走秀爆款Ai视频,小白轻松上手,新蓝海赛道,竞争小机会多!

今天我要给大家带来一个超级有趣的项目——童装走秀AI视频制作。 这不仅是一个充满创意的项目&#xff0c;而且操作简单&#xff0c;即使是视频制作的新手也能轻松上手。 更重要的是&#xff0c;这个项目竞争小&#xff0c;变现机会多&#xff0c;是进入新蓝海赛道的绝佳机会…

C++类之set与get理解

在类中&#xff0c;我们尝尝将一些变量设置为private或者protect里面&#xff0c;而我们经常会遇到在主函数&#xff08;main.cpp&#xff09;使用到这些private变量&#xff0c;而往往我们会下意识地在主函数直接调用在private里面的变量&#xff0c;但现实比较残酷&#xff0…

【linux】nice命令

Linux中的nice命令是一个强大的工具&#xff0c;用于调整进程的优先级&#xff0c;进而影响它们在CPU上的资源分配和执行顺序。以下是关于nice命令的详细解释&#xff0c;包括其用途、语法、参数、示例以及使用建议。 一、用途 nice命令主要用于控制进程在CPU上的调度优先级&…

K8S介绍+集群部署

Kubernetes介绍 官网&#xff1a;https://kubernetes.io/ 一、应用部署方式演变 1、传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其他技术的参与 缺点&#xff1a;不能为应用程序定义资源使用边界&a…

【论文笔记】BEVNeXt: Reviving Dense BEV Frameworks for 3D Object Detection

原文链接&#xff1a;https://arxiv.org/pdf/2312.01696 简介&#xff1a;最近&#xff0c;在摄像头3D目标检测任务中&#xff0c;基于查询的Transformer解码器正在超越传统密集BEV方法。但密集BEV框架有着更好的深度估计和目标定位能力&#xff0c;能全面精确地描绘3D场景。本…

初始网络编程(下)

所属专栏&#xff1a;Java学习 1. TCP 的简单示例 同时&#xff0c;由于 TCP 是面向字节流的传输&#xff0c;所以说传输的基本单位是字节&#xff0c;接受发送都是使用的字节流 方法签名 方法说明 Socket accept() 开始监听指定端口&#xff08;创建时绑定的端口&…

Navicat导入Sql文件至Mysql数据库,事务失效

Mysql 版本&#xff1a;8.0.39 Navicat 版本&#xff1a;17.x、16.x 结论&#xff1a; Navicat 导入sql文件&#xff0c;事务不会生效&#xff0c;无论怎么设置 mysql.exe 导入sql文件&#xff0c;事务生效 测试 准备一张表 name约束不能为空&#xff0c;用于测试事务失败…

SpringBoot 整合 Caffeine 实现本地缓存

目录 1、Caffeine 简介1.1、Caffeine 简介1.2、对比 Guava cache 的性能主要优化项1.3、常见的缓存淘汰算法1.4、SpringBoot 集成 Caffeine 两种方式 2、SpringBoot 集成 Caffeine 方式一2.1、缓存加载策略2.1.1、手动加载2.1.2、自动加载【Loading Cache】2.1.3、异步加载【As…

7、论等保的必要性

数据来源&#xff1a;7.论等保的必要性_哔哩哔哩_bilibili 等级保护必要性 降低信息安全风险 等级保护旨在降低信息安全风险&#xff0c;提高信息系统的安全防护能力。 风险发现与整改 开展等级保护的最重要原因是通过测评工作&#xff0c;发现单位系统内外部的安全风险和脆弱…

Linux启动流程,0,1,2进程,init进程,idle进程,内核态到用户态的kernel_execve(一)

&#xff1f;是&#xff0c;如果定义了&#xff0c;就按Makefile的&#xff0c;如果如下make编译时&#xff0c;就按如下 linux内核入口 进程0在用户空间看不到&#xff0c;因为他是内核进程 进程2就是守护进程&#xff0c;维护内涵运转的 一生二&#xff0c;二生三&#xff…

Navicate 链接Oracle 提示 Oracle Library is not loaded ,账号密码都正确地址端口也对

Navicate 链接Oracle 提示 Oracle Library is not loaded ,账号密码都正确地址端口也对的问题 解决办法 出现 Oracle Library is not loaded 错误提示&#xff0c;通常是因为 Navicat 无法找到或加载 Oracle 客户端库&#xff08;OCI.dll&#xff09;。要解决这个问题&#x…

IntelliJ IDEA 2024.1.4 (Ultimate Edition)找不到Add Framework Support解决方法

目录 背景: 解决方法&#xff1a; 步骤1: 步骤2&#xff1a; 步骤3&#xff1a; 创建Web项目的完整流程&#xff1a; 步骤1: 步骤2: 步骤3&#xff1a; 步骤4&#xff1a; Web优点: 背景: 我的IDE版本是IntelliJ IDEA 2024.1.4 (Ultimate Edition)&#xff0c;当我…

Java调用数据库 笔记05(查询篇)

一. 数据库&#xff08;通过各种驱动来实现调用&#xff09;&#xff1a; &#xff08;应用程序通过接口控制的各种数据库驱动来调用数据库-->jdbc方法&#xff09; 1.创建Java的普通class类 2.加载驱动 Class.forName("com.mysql.jdbc.Driver"); 3.驱动管理类…

nginx upstream转发连接错误情况研究

本次测试用到3台服务器&#xff1a; 192.168.10.115&#xff1a;转发服务器A 192.168.10.209&#xff1a;upstream下服务器1 192.168.10.210&#xff1a;upstream下服务器2 1台客户端&#xff1a;192.168.10.112 服务器A中nginx主要配置如下&#xff1a; log_format main…

C++之职工管理系统(细节Q)

指针初始化类 && 普通变量初始化类 抽象基类worker&#xff0c;只需编写 .h &#xff0c;无需 .cpp 底层实现 类 记得声明权限public&#xff01;&#xff01;&#xff01;不然默认private&#xff0c;主函数访问不了 记得继承父类 Worker * worker&#xff1a;指向Wo…

山东潍坊戴尔存储服务器维修 md3800f raid恢复

山东戴尔存储故障维修 存储型号&#xff1a;DELL PowerVault md3800f 故障问题&#xff1a;存储除尘后通电开机&#xff0c;发现有物理硬盘没有插到位&#xff0c;用户带电拔插了多块物理盘&#xff0c;导致关连的磁盘阵列掉线&#xff0c;卷失败&#xff1b; 处理方式&#xf…