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

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.lqws.cn/news/563977.html

相关文章:

  • git常见问题汇总-重复提交/删除已提交文件等问题
  • RabbitMQ 工作模式
  • 海量数据存储与分析:HBase、ClickHouse、Doris三款数据库对比
  • 用celery作为信息中间件
  • AlpineLinux安装部署MariaDB
  • 如何撰写有价值的项目复盘报告
  • 将iso镜像文件格式转换为云平台支持的镜像文件格式
  • lv_font_conv转换自定义symbol
  • 志愿填报深度解析与专业导向推荐-AI生成
  • SATA信号基础介绍
  • python基础23(2025.6.29)分布式爬虫(增量式爬虫去重)redis应用_(未完成!)
  • DOP数据开放平台(真实线上项目)
  • c++ 学习(二、结构体)
  • 非阻塞 IO
  • 卸载Modelsim/Qustasim方法
  • matplotlib 绘制水平柱状图
  • 买卖股票的最佳时机 II
  • 开源3D 动态银河系特效:Vue 与 THREE.JS 的奇幻之旅
  • 【面板数据】上市公司企业代理成本数据(四项代理成本) 2000-2024年
  • 设备树引入
  • kubectl exec 原理
  • Python 数据分析:numpy,抽提,整数数组索引。听故事学知识点怎么这么容易?
  • AD22以上的基础操作
  • 基于WOA鲸鱼优化算法的圆柱体容器最大体积优化设计matlab仿真
  • 星际争霸数据集指南
  • 数据结构与算法总概
  • Rust代码规范之蛇形命名法和驼峰命名法
  • AUTOSAR图解==>AUTOSAR_AP_EXP_SOVD
  • 关于ubuntu 20.04系统安装分区和重复登录无法加载桌面的问题解决
  • 力扣 刷题(第七十一天)