Vue3 根据路由配置实现动态菜单

前言

最近在学习Vue3的相关语法,在阅读官方文档的时候觉得官方文档的菜单栏比较简洁美观,于是想着能不能自己实现一个类似的多级菜单。代码大部分由AI所做(感谢活在这个人工智能时代)。
在这里插入图片描述

设计

主要就是路由设计以及菜单设计

路由设计

src/router/intex.js 中进行路由配置,配置的信息示例如下:

import Home from "@/app/Home.vue"
import {createRouter, createWebHistory} from "vue-router";
import Layout from "@/app/Layout.vue";
import Demo1 from "@/app/Demo1.vue";
import Demo2 from "@/app/Demo2.vue";
import Demo3 from "@/app/Demo3.vue";
import About from "@/app/About.vue";const routes = [{path: '/',name: '首页',component: Home,},{path: '/demo',name: '演示',component: Layout,children: [{path: 'demo1',name: '演示1',component: Demo1},{path: 'demo-sub',name: '演示子菜单1',component: Layout,children: [{path: 'demo2',name: '演示2',component: Demo2},{path: 'demo-sub2',name: '演示子菜单2',component: Layout,children: [{path: 'demo3',name: '演示3',component: Demo3}]},]},]},{path: '/about',name: '关于',component: About,},
];const router = createRouter({history: createWebHistory(),routes,
});export default router;

各参数解释如下:

  • path:跳转的路由信息
  • name:菜单项名称
  • component:菜单显示页面组件
  • children:所包含子路由

菜单设计

菜单由两个组件实现:src/components/MenuDropdown.vuesrc/components/AdvancedMenu.vue,同时使用递归思想实现多级菜单渲染。

MenuDropdown 组件

递归子菜单组件,负责:

  • 渲染子菜单项
  • 支持无限层级嵌套
  • 处理子菜单的悬停事件
  • 提供动画效果

显示的的子菜单,主菜单 AdvancedMenu组件中调用,如果有子菜单,则显示子菜单下拉框,效果如下图(具体显示位置有css控制):
在这里插入图片描述

组件主要代码如下:

<script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import MenuDropdown from './MenuDropdown.vue'// 定义props
const props = defineProps({routes: {type: Array,required: true},level: {type: Number,default: 1},parentPath: {type: String,default: ''}
})const route = useRoute()
const activeSubmenu = ref(null)// 检查是否有子菜单
const hasChildren = (route) => {return route.children && route.children.length > 0
}// 获取完整的路由路径
const getFullPath = (route) => {if (props.parentPath) {// 如果有父路径,拼接完整路径return `${props.parentPath}/${route.path}`}return route.path
}// 检查当前路由是否匹配
const isCurrentRoute = (routePath) => {const fullPath = getFullPath(routePath)return route.path === fullPath
}// 检查当前路由是否属于某个父菜单
const isCurrentRouteInParent = (parentRoute) => {if (!hasChildren(parentRoute)) return falseconst currentPath = route.pathconst parentFullPath = getFullPath(parentRoute)return currentPath.startsWith(parentFullPath)
}// 处理鼠标进入子菜单
const handleMouseEnter = (route) => {if (hasChildren(route)) {activeSubmenu.value = route.name}
}// 处理鼠标离开子菜单
const handleMouseLeave = (route) => {setTimeout(() => {if (activeSubmenu.value === route.name) {activeSubmenu.value = null}}, 200)
}
</script>
<template><divclass="dropdown-menu":class="[`level-${level}`]"><ul class="submenu-list"><liv-for="route in routes":key="route.path"class="submenu-item"@mouseenter="handleMouseEnter(route)"@mouseleave="handleMouseLeave(route)"><!-- 有子菜单的父菜单项 --><divv-if="hasChildren(route)"class="submenu-link parent-submenu":class="{ 'active': activeSubmenu === route.name || isCurrentRouteInParent(route)}"><span class="submenu-text">{{ route.name }}</span><span class="submenu-arrow"></span></div><!-- 没有子菜单的菜单项 --><router-linkv-else:to="getFullPath(route)"class="submenu-link":class="{ 'active': isCurrentRoute(route) }"><span class="submenu-text">{{ route.name }}</span></router-link><!-- 递归渲染子菜单 --><Transition name="submenu"><MenuDropdownv-if="hasChildren(route) && activeSubmenu === route.name":routes="route.children":level="level + 1":parent-path="getFullPath(route)"/></Transition></li></ul></div>
</template>
AdvancedMenu 组件

主要的菜单组件,负责:

  • 读取路由配置
  • 渲染顶级菜单项
  • 处理鼠标悬停事件
  • 管理菜单状态

显示完整的顶部菜单项(不含子菜单),效果如图:
在这里插入图片描述

组件主要代码如下:

<script setup>
// 获取路由配置
import {computed, ref} from "vue";
import {useRouter, useRoute} from "vue-router";
import MenuDropdown from "@/components/MenuDropdown.vue";const router = useRouter()
const route = useRoute()
const activeDropdown = ref(null)// 获取路由配置
const menuRoutes = computed(() => {return router.options.routes;
})// 检查是否有子菜单
const hasChildren = (route) => {return route.children && route.children.length > 0
}// 检查当前路由是否属于某个父菜单
const isCurrentRouteInParent = (parentRoute) => {if (!hasChildren(parentRoute)) return false// 检查当前路由路径是否以父路由路径开头const currentPath = route.pathconst parentPath = parentRoute.path// 如果父路径是根路径,需要特殊处理if (parentPath === '/') {return currentPath !== '/' && currentPath.startsWith('/')}return currentPath.startsWith(parentPath)
}// 处理鼠标进入主菜单
const handleMouseEnter = (route) => {if (hasChildren(route)) {activeDropdown.value = route.name}
}// 处理鼠标离开主菜单
const handleMouseLeave = (route) => {setTimeout(() => {if (activeDropdown.value === route.name) {activeDropdown.value = null}}, 200)
}</script><template><nav class="advanced-menu"><div class="menu-container"><ul class="menu-list"><liv-for="route in menuRoutes":key="route.path"class="menu-item"@mouseenter="handleMouseEnter(route)"@mouseleave="handleMouseLeave(route)"><!-- 有子菜单的父菜单项--><divv-if="hasChildren(route)"class="menu-link parent-menu":class="{'active': activeDropdown === route.name || isCurrentRouteInParent(route)}"><span class="menu-text">{{ route.name }}</span><span class="dropdown-arrow"></span></div><!-- 没有子菜单的菜单项--><router-linkv-else:to="route.path"class="menu-link"active-class="active"><span class="menu-text">{{ route.name }}</span></router-link><!--  递归渲染子菜单--><Transition name="dropdown"><MenuDropdownv-if="hasChildren(route) && activeDropdown === route.name":routes="route.children":level="1":parent-path="route.path"/></Transition></li></ul></div></nav>
</template>
递归思路

1、首先在AdvancedMenu 组件中根据配置的路由信息routes 进行循环渲染。如果路由route 没有children属性即子路由,直接使用router-link渲染即可(可点击);如果存在子路由,使用div渲染(不可点击),并在右侧加入箭头表示有子菜单。

<template><nav class="advanced-menu"><div class="menu-container"><ul class="menu-list"><liv-for="route in menuRoutes":key="route.path"class="menu-item"@mouseenter="handleMouseEnter(route)"@mouseleave="handleMouseLeave(route)"><!-- 有子菜单的父菜单项--><divv-if="hasChildren(route)"class="menu-link parent-menu":class="{'active': activeDropdown === route.name || isCurrentRouteInParent(route)}"><span class="menu-text">{{ route.name }}</span><span class="dropdown-arrow"></span></div><!-- 没有子菜单的菜单项--><router-linkv-else:to="route.path"class="menu-link"active-class="active"><span class="menu-text">{{ route.name }}</span></router-link><!--  递归渲染子菜单--><Transition name="dropdown"><MenuDropdownv-if="hasChildren(route) && activeDropdown === route.name":routes="route.children":level="1":parent-path="route.path"/></Transition></li></ul></div></nav>
</template>

2、如果在AdvancedMenu 组件中渲染的路由route有子路由,则将该route.children信息传递给MenuDropdown子菜单组件。同时传递该路由的层级level、路由的地址path,便于子菜单的渲染和路由的正确跳转。

 <!--  递归渲染子菜单--><Transition name="dropdown"><MenuDropdownv-if="hasChildren(route) && activeDropdown === route.name":routes="route.children":level="1":parent-path="route.path"/></Transition>

3、MenuDropdown子菜单组件与AdvancedMenu 主菜单组件类似,根据传来的routes、level、path信息,进行对应的渲染。如果路由route 没有children属性即子路由,直接使用router-link渲染即可(可点击);如果存在子路由,使用div渲染(不可点击),并在右侧加入箭头表示有子菜单。

 <!-- 递归渲染子菜单 --><Transition name="submenu"><MenuDropdownv-if="hasChildren(route) && activeSubmenu === route.name":routes="route.children":level="level + 1":parent-path="getFullPath(route)"/></Transition>

效果图

在这里插入图片描述

总结

本项目基本实现了基于Vue3根据路由配置动态生成多级菜单的功能,且UI仿照Vue官方文档设计简洁美观,动画流畅。不过作者表述能力不佳,具体实现可参考完整代码。

项目完整代码

https://github.com/Seven11111/vue-menu

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#中 Winform如何实现跨页面调用

设计页面 如下 其中Form2为启动项 当点击订阅消息的时候 会创建Form1页面 当 Form1页面的点击发送的时候 自动将发送的消息同步到label1中 Form2的代码如下 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using S…

一个完整的Python解决方案,用于使用机器学习筛选最优模型并分析纳米酶特征与Km/Vmax的相关性

以下是一个完整的Python解决方案,用于使用机器学习筛选最优模型并分析纳米酶特征与Km/Vmax的相关性。代码包含数据预处理、模型选择、特征相关性分析和结果可视化,并附带详细注释。 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn…

用python可视化南方大暴雨及洪水数据分析

用python可视化南方大暴雨及洪水数据分析 截至20250621,南方地区(特别是广东、广西、湖南等地)遭遇的极端暴雨和洪水灾害&#xff0c;斑点鱼将使用Python进行数据分析和可视化&#xff0c;展示洪水影响区域、雨势强度以及经济损失等情况。 数据搜集如下&#xff1a; import …

DeepSPV:一个用于从二维超声图像进行三维脾脏体积估算的深度学习流程|文献速递-最新论文分享

Title 题目 DeepSPV: A deep learning pipeline for 3D spleen volume estimation from 2D ultrasound images DeepSPV&#xff1a;一个用于从二维超声图像进行三维脾脏体积估算的深度学习流程 01 文献速递介绍 1.1 临床背景 脾肿大&#xff0c;即脾脏增大&#xff0c;是…

PHP在做爬虫时的解决方案

爬虫不是一个小众的场景,所以无论是哪个语言,都有很多相应的生态库.这里介绍一下PHP的技术方案和代码量。 关键能力 对页面的解析能力 PHP的官方扩展中有Dom扩展,但是我建议使用electrolinux/phpquery这个库,他相当于一个PHP版的JQ. 算上加载文件,只要三行就能获取title标签…

zookeeper Curator(2):Curator的节点操作

文章目录 zookeeper 的安装Curator 介绍Curator API 常用操作本章必要的相关依赖和配置建立连接第一种方式第二种方式 关闭连接添加节点创建节点创建节点并设置值和类型创建多级节点 查询节点查询数据查询所有子节点查询节点信息 修改节点修改节点数据修改节点(乐观锁修改&…

计算机视觉的定义及应用方向

近两年来&#xff0c;机器学习、深度学习、人工智能等方向逐步成为很多同学最关注的研究和就业方向。不可否认&#xff0c;计算机视觉作为深度学习的应用领域之一&#xff0c;在工业界取得了蓬勃的发展&#xff0c;也是目前最热门的方向之一。 计算机视觉是什么 计算机视觉主要…

低代码开发实战:使用活字格快速创建 Java Web API

引言 在当今快速发展的软件开发领域&#xff0c;低代码平台因其高效性和易用性而备受关注。Java Web API作为企业级应用开发的重要组成部分&#xff0c;与低代码平台的结合能够显著提升开发效率。本文将以葡萄城活字格平台为例&#xff0c;详细介绍如何利用其低代码特性快速创…

当后端接口返回格式不是easyui默认的格式,怎么办

方法:可以增加一个数据过滤方法对后端的接口结构进行转换。 分析:easyui默认情况下,只能接收如下格式的接口: {"total": 1,"rows": [{"id": 1,"userName": "jiao","unionId": 1,"phone": "2…

第一弹 AC Module: 和编程语言无关以AI为中心的自包含模块化理念和实现

背景和问题 说起模块/包&#xff0c;几乎是大部分语言都有的概念&#xff0c;因为一个项目会很庞大&#xff0c;如果单纯只用文件做隔离&#xff0c;文件可能几千上万个&#xff0c;所以需要在项目和文件之间获得一个平衡&#xff0c;这个时候就有包和模块的概念。比如python 你…

Edge(chrome)右键插件的右键菜单怎么设置

主要使用 contextMenus 右键菜单功能&#xff0c;正常用它给页面设置右键菜单&#xff0c;其实它也可以给插件还有其它功能设置右键菜单&#xff0c;是根据 contexts 选项来设置给哪个功能设置右键菜单。 首先添加权限 "permissions": ["contextMenus"], …

后端设计笔记13 FM

1.理论 2.实践 LAB 报错以后可以双击错误&#xff0c;便可以定位到错误&#xff0c;或者打开pattern查看 还可以analyse

闲庭信步使用SV搭建图像测试平台:第十九课——YCbCr图像转RGB图像

&#xff08;本系列只需要modelsim即可完成数字图像的处理&#xff0c;每个工程都搭建了全自动化的仿真环境&#xff0c;只需要双击文件就可以完成整个的仿真&#xff0c;大大降低了初学者的门槛&#xff01;&#xff01;&#xff01;&#xff01;如需要该系列的工程文件请关注…

C++(智能指针)

智能指针 1.基础&#xff1a; 1.1 概念 智能指针是用于自动管理动态分配内存的RAII&#xff08;Resource Acquisition Is Initialization&#xff09;对象。它们通过自动释放内存来防止内存泄漏&#xff0c;是替代裸指针的安全工具。 1.2 解析 众所周知&#xff0c;堆内存对象…

Sentinel 授权规则详解与自定义异常处理

Sentinel 授权规则详解与自定义异常处理 在微服务系统中&#xff0c;权限控制和访问保护是至关重要的一环。本文将详细介绍如何通过 Sentinel 的 授权规则&#xff08;AuthorityRule&#xff09; 控制资源访问权限&#xff0c;并结合实际案例说明如何设置白名单与黑名单&#…

LeetCode Hot 100 最大子数组和

给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xff1a;6…

Python UI自动化演进格局:从传统库到AI驱动的智能代理

引言 UI自动化的持久需求 在现代软件工程和业务流程管理中&#xff0c;图形用户界面&#xff08;GUI&#xff09;自动化扮演着至关重要的角色。它不仅仅局限于软件测试领域&#xff0c;更是机器人流程自动化&#xff08;RPA&#xff09;、自动化数据录入、遗留系统集成以及在AP…

【Java面试】如何解决MQ死信队列?

如何解决MQ死信队列&#xff1f; 一、预防死信产生&#xff08;从源头减少死信&#xff09; 消费者端健壮性优化 捕获所有可能的异常&#xff0c;区分可恢复异常&#xff08;如网络超时&#xff09;和不可恢复异常&#xff08;如数据格式错误&#xff09;。对可恢复异常实现自…

RGB+EVS视觉融合相机:事件相机的革命性突破​

一、单一EVS事件相机的原理 事件相机&#xff08;EVS&#xff09;是一种新型的视觉传感器&#xff0c;其设计灵感来源于生物视觉系统。与传统相机不同&#xff0c;事件相机并不以固定的帧率捕获整个图像&#xff0c;而是每个像素独立工作&#xff0c;当检测到亮度变化超过预设…

DBeaver 设置阿里云中央仓库地址的操作步骤

DBeaver 设置阿里云中央仓库地址的操作步骤&#xff08;适用于解决驱动下载缓慢或失败的问题&#xff09; 一、最新阿里云 Maven 仓库地址 主仓库地址&#xff08;推荐&#xff09;&#xff1a; http://maven.aliyun.com/nexus/content/groups/public/ 123 备用地址&#xff…