vue实现动态路由菜单!!!
目录
- 总结
- 一、步骤
- 1.编写静态路由
- 编写router.js
- main.js注册
- 2.编写permisstions.js权限文件
- 编写permisstions.js
- axios封装的API
- store.js状态库
- system.js Axios-API
- request.js axios请求实例封装
- 3.编写菜单树组件
- MenuTree.vue
- 4.主页中使用菜单树组件
总结
递归处理后端响应的菜单树,后依次通过addRoute方法往静态父路由,添加动态子路由,添加完使用el-menu渲染并添加router属性实现路由菜单模式
addRoute:https://router.vuejs.org/zh/api/interfaces/Router.html#Methods-addRoute
后端数据库树菜单:
一、步骤
1.编写静态路由
- 创建router.js文件默认导出静态路由,后在main.js加载注册
编写router.js
//静态路由配置文件
// eslint-disable-next-line no-unused-vars
import Router from "vue-router"
// eslint-disable-next-line no-unused-vars
import Vue from "vue"
//在Vue中加载路由模块
Vue.use(Router)//写路由表
// eslint-disable-next-line no-unused-vars
// const Foo = { template: '<div>foo</div>' }
const routes = [// 进入vue项目默认进入登录页面{path: "/",redirect: "/Login"},{path: "/Login",component: () => import("../view/Login"),meta: {skipAuthCheck: true // 添加一个标记,表示不需要进行身份验证检查}},{path: "/index",name: 'index',component: () => import("../components/index"),children: [// 默认显示hello页面{path: "/",redirect: "/hello"},{path: "/hello",meta: { requiresAuth: true },component: () => import("../components/hello"),},],},
]export default new Router({routes
});// 防止连续点击多次路由报错
let routerPush = Router.prototype.push;
let routerReplace = Router.prototype.replace;
// push
Router.prototype.push = function push(location) {return routerPush.call(this, location).catch(err => err)
}
// replace
Router.prototype.replace = function push(location) {return routerReplace.call(this, location).catch(err => err)
}
main.js注册
import Vue from 'vue'
import App from './App.vue'
//引入一个router模块
import router from "@/router/router"
import routers from "@/router/permissions"
import element from 'element-ui';
import axiosInstance from '@/request/request'
import { createPinia } from 'pinia';
import 'element-ui/lib/theme-chalk/index.css';
// 在生产环境中禁用警告信息和启用构建优化
Vue.config.productionTip = false// 将 Axios 实例添加到 Vue 原型中,以便在组件中使用
// Vue.prototype.axios axios便在组件中使用如:this.$axios
Vue.prototype.axios = axiosInstanceconst pinia = createPinia();
Vue.use(pinia)Vue.use(element)
new Vue({router,routers,render: h => h(App),
}).$mount('#app')
2.编写permisstions.js权限文件
- 结合axios封装API于permisstions中配置的全局前置守卫中获取菜单树存入sessionStorage缓存
编写permisstions.js
// 导入默认导出的路由对象用于跳转路由
// import router from '@/router/router';
//导入路由表
import routers from "@/router/router"
//路由配置文件
import { tokenStore } from "@/store/store"// 全局前置守卫
// to当前即将要进入的路由对象
routers.beforeEach((to, from, next) => {//如果当前的访问的请求是Login放行if (to.path === '/Login') {next();}else {//其余访问请求判断用户是否登录if (!isLoggedIn()) {console.log("抱歉你未登录");next('/Login'); // 如果用户未登录,则重定向到登录页面} else {// console.log(to);next();}}})
//登录验证函数
function isLoggedIn() {console.log("进入路由守卫");// 在这里实现检查用户是否已登录的逻辑,例如检查是否有有效的令牌或会话// 如果已登录,返回true,否则返回falseconst jwtToken = sessionStorage.getItem('jwtToken'); // 从本地缓存中获取会话信息// console.log(jwtToken);let userId = sessionStorage.getItem('user_name_id');//userId存在获取动态路由信息if (userId && jwtToken) {// if (tokenStore().flag) {tokenStore().getRouters(userId).then((res) => {if (res.status == 201) {// console.log(res.data);//动态路由源信息let r = res.data;// 过滤动态路由菜单let menu = fnAddDynamicMenuRoutes(r)console.log(menu);menu.forEach(element => {element.children.forEach(s => {// console.log(s);//index为父路由的name属性值 s是需添加的路由routers.addRoute('index', s);})});// console.log(routers);// 动态路由得到后修改标记为false表示已执行过无需在执行tokenStore().flag = false;// 保存路由到会话sessionStorage.setItem('menu', JSON.stringify(menu));}if (res.status == 501) {//未获取到动态路由重新登录routers.push("/Login");}})// }}return jwtToken && routers; // 如果登录令牌存在,则用户已登录
}// 用于处理动态菜单数据,将其转为 route 形式
function fnAddDynamicMenuRoutes(menuList = [], routes = []) {// 用于保存普通路由数据let temp = []// 用于保存存在子路由的路由数据let route = []// 遍历数据for (let i = 0; i < menuList.length; i++) {// 存在子路由,则递归遍历,并返回数据作为 children 保存if (menuList[i].childMenus && menuList[i].childMenus.length > 0) {// 获取路由的基本格式route = getRoute(menuList[i])// 递归处理子路由数据,并返回,将其作为路由的 childMenus 保存route.children = fnAddDynamicMenuRoutes(menuList[i].childMenus)// 保存存在子路由的路由routes.push(route)} else {// 保存普通路由temp.push(getRoute(menuList[i]))}}// 返回路由结果return routes.concat(temp)
}// 返回路由的基本格式
function getRoute(item) {// 路由基本格式let route = {// 路由的路径path: item.path,// 路由名name: item.menuName,// 路由所在组件 必须有一段已定义好的组件名字// component: (resolve) => require([`@/layout/Index`], resolve),component: (resolve) => require([`../components${item.menuUrl}.vue`], resolve),meta: {id: item.menuType,// icon: item.icon},// 路由的子路由children: []}// 返回 routereturn route
}export default routers
axios封装的API
store.js状态库
// 导入pinia库
import { defineStore } from 'pinia';
// 导入api
import { login, logOut, getRouters } from '@/request/api/system';
// 导入jwt解析器
import jwtDecode from "jwt-decode";
// 导入默认导出的路由对象用于跳转路由
import router from '@/router/router';export const tokenStore = defineStore({id: 'myStore',state: () => ({jwtToken: null,user_name: null,user_name_id: null,user_type: null,menu: null,}),actions: {getRouters(userId) {return new Promise((resolve) => {getRouters(userId).then(res => {console.log(res);resolve(res)})})},doLogin(params) {login(params).then((res) => {if (res.status == 200) {const jwtToken = res.data; // 从响应中获取JWTsessionStorage.setItem('jwtToken', jwtToken);this.jwtToken = jwtToken; // pinia存储JWT// 解码JWT令牌以获取载荷信息const decodedToken = jwtDecode(jwtToken);console.log(decodedToken);//访问包含在JWT令牌中的用户信息//保存用户类型的id便于门诊医生问诊var user_name_id = decodedToken.user_name_id;sessionStorage.setItem('user_name_id', user_name_id);this.user_name_id = user_name_id;//保存用户类型便于控制导航栏的显示与隐藏const userType = decodedToken.user_type;this.user_type =userType == 1? "系统管理员": userType == 2? "挂号员": "门诊医生";//跳转到主页router.push("/index");}});},LogOut() {return logOut();}},
});
system.js Axios-API
import axiosInstance from "@/request/request"export function login(data) {return axiosInstance({url : "/Login",method : "POST",data})
}export function logOut() {return axiosInstance({url : "/LogOut",method : "get",})
}export function getUserInfo(data) {return axiosInstance({url : "/User/select",method : "post",data})
}export function getRouters(userId) {return axiosInstance({url : `/UserTreeInfo${userId}`,method : "get",})
}
request.js axios请求实例封装
import axios from 'axios'
import { Message } from 'element-ui'
import {tokenStore} from "@/store/store";// 创建一个 Axios 实例
const axiosInstance = axios.create({baseURL: 'http://localhost:8080/qy', // 通用后端 Url 地址timeout: 5000, // 请求最大等待时间,headers: { 'Content-Type': 'application/json' },
})// 添加请求拦截器
axiosInstance.interceptors.request.use((config) => {// 获取请求的URLconst requestUrl = config.url;console.log(requestUrl);// console.log(config);// 提取URL路径部分/qy/Login// const urlPath = new URL(requestUrl).pathname;// 如果是post请求将参数data转成json字符串// 检查请求方法是否为 POSTif (config.method === 'post' || config.method === 'POST') {// 将请求数据转换为 JSON 字符串config.data = JSON.stringify(config.data);}if (config.method === 'get' || config.method === 'GET') {config.headers['Content-Type'] = 'x-www-form-urlencoded';}// 在请求头中获取令牌信息const jwtToken = tokenStore().jwtToken // 从pinia中获取令牌// 检查是否是登录请求,这里假设登录请求的URL是 '/Login'if (requestUrl !== '/Login' && requestUrl !== '/LogOut') {console.log(requestUrl);// 如果不是登录请求,添加令牌到请求头if (jwtToken) {config.headers.Authorization = `${jwtToken}`}}return config},(error) => {return Promise.reject(error)}
)//添加响应拦截器
axiosInstance.interceptors.response.use((response) => {var res = response.data// console.log(res);// 设置请求状态弹窗提示if (res.status == 200) {//请求成功提示Message.success(res.msg);}else if(res.msg === "菜单载入成功") {return res}else {Message.error(res.msg);}// 后端响应Resbody的data数据return res
},(error) => {return Promise.reject(error)}
)export default axiosInstance
3.编写菜单树组件
- 接受父组件菜单树,递归遍历渲染树菜单
MenuTree.vue
<template><div><!-- :default-active 一进页面默认显示的页面unique-opened 保持一个子菜单的打开router 采用路由模式 菜单上的index就是点击跳转的页面text-color 菜单文字的颜色active-text-color 菜单激活后文字的颜色--><el-menudefault-activebackground-color="#2b333e"routertext-color="#fff"active-text-color="#ffd04b"><template v-for="item in menuData"><el-submenuv-if="item.children && item.children.length > 0":key="item.id":index="item.path"><template slot="title"><i class="el-icon-menu"></i><span>{{ item.name }}</span></template><!-- 若有子菜单递归渲染 --><menu-tree :menuData="item.children" /></el-submenu><el-menu-item v-else :key="item.id" :index="item.path">{{item.name}}</el-menu-item></template></el-menu></div>
</template><script>
export default {props: {menuData: {},},name: "MenuTree",
};
</script>
4.主页中使用菜单树组件
- 导入组件并注册MenuTree.vue,通过JSON.parse()转换菜单树对象menuData,后父传子menuData渲染菜单树
<!-- eslint-disable vue/multi-word-component-names -->
<template><!-- 整个页面 --><div class="index"><!-- 左导航 --><divclass="leftNav":style="{width: leftNavWidth,visibility: show,transition: transitionParam,}"><!-- 标题 --><h2 style="color: #fff; margin: 20px 0">青芽在线医疗</h2><!-- 动态导航 --><!-- {{ menuData }} --><menu-tree :menuData="menuData"></menu-tree></div><!-- 主界面 --><divclass="mainSection":style="{ width: mainSectionWidth, transition: transitionParam }"><!-- 标题头部 --><div class="QYheader"><span class="el-icon-s-operation" @click="controlWidth"></span><span class="QYheaderFont"><el-button type="primary" @click="LogOut">退出登录</el-button></span><div class="QYheaderRight"><span class="el-icon-user-solid"></span><span class="QYheaderRightFont">{{ user_name }}</span></div></div><!-- 二级路由部分 --><div class="QYcontent"><router-view></router-view></div><!-- QYcontent --></div><!-- mainSection --></div><!-- index -->
</template><script>
import { tokenStore } from "@/store/store";
import MenuTree from "../components/MenuTree.vue";
export default {// eslint-disable-next-line vue/multi-word-component-namesname: "mainSection",components: {MenuTree,},data() {return {menuData: JSON.parse(sessionStorage.getItem("menu")),tokenStore: tokenStore(),//接收从Login页传来的登录用户名user_name: tokenStore().user_name,//接收从Login页传来的用户类型user_type: tokenStore().user_type,//设置导航和主界面默认宽高leftNavWidth: "16%",mainSectionWidth: "84%",show: "visible",transitionParam: "width 0.5s ease",};},methods: {//控制导航和主界面的宽和高controlWidth() {console.log("已进入控制宽度方法");this.leftNavWidth = this.leftNavWidth === "16%" ? "0%" : "16%";//控制左导航的显示与隐藏 同时设置mainSectionWidth的宽和高if (this.leftNavWidth === "16%") {this.show = "visible";this.mainSectionWidth = "84%";} else if (this.leftNavWidth === "0%") {this.show = "hidden";this.mainSectionWidth = "100%";}},LogOut() {// 删除所有本地缓存包括令牌信息// localStorage.clear();this.tokenStore.LogOut().then((res) => {if (res.status == 200) {// 删除所有本地缓存包括令牌信息sessionStorage.clear();// 重置获取路由的标记tokenStore.flag = false;// 跳转到登录页面this.$router.push({ path: "/Login" });}});// localStorage.removeItem("user_name");// localStorage.removeItem("user_type");},},
};
</script><style>
@import url(../css/index.css);
</style>
相关文章:

vue实现动态路由菜单!!!
目录 总结一、步骤1.编写静态路由编写router.jsmain.js注册 2.编写permisstions.js权限文件编写permisstions.jsaxios封装的APIstore.js状态库system.js Axios-APIrequest.js axios请求实例封装 3.编写菜单树组件MenuTree.vue 4.主页中使用菜单树组件 总结 递归处理后端响应的…...

企业如何选择安全又快速的大文件传输平台
在现代信息化社会,数据已经成为各个行业的重要资源,而数据的传输和交换则是数据价值的体现。在很多场合,企业需要传输或接收大文件,例如设计图纸、视频素材、软件开发包、数据库备份等。这些文件的大小通常在几百兆字节到几十个字…...

springboot 自定义starter逐级抽取
自定义starter 背景:各个组件需要引入starter 还有自己的配置风格 –基本配置原理 (1)自定义配置文件 导入配置可以在配置文件中自动识别,提示 导入依赖后可以发现提示 (2)配置文件实现 –让配置文件对其他模块生…...

GAN:ImprovedGAN-训练GAN的改进策略
论文:https://arxiv.org/abs/1606.03498 代码:https://github.com/openai/improved_gan 发表:NIPS 2016 一、文章创新 1:Feature matching:特征匹配通过为生成器指定新目标来解决GANs的不稳定性,从而防止…...

docker限制容器内存的方法
在服务器中使用 docker 时,如果不对 docker 的可调用内存进行限制,当 docker 内的程序出现不可预测的问题时,就很有可能因为内存爆炸导致服务器主机的瘫痪。而对 docker 进行限制后,可以将瘫痪范围控制在 docker 内。 因此&#…...

阿里达摩院裁撤量子实验室
我是卢松松,点点上面的头像,欢迎关注我哦! 马云的达摩院也不搞量子计算了,因为缺钱,整体裁掉了达摩院量子实验室,把所有的设备都赠送给了浙江大学。 达摩院量子实验室:总共30个研究员…...
mysql数据库基础知识,Mysql的索引和主键区别,数据库的事务的基本特性
文章目录 数据库基础知识Mysql的索引和主键的区别数据库的事务的基本特性 数据库基础知识 为什么要使用数据库 数据保存在内存 优点: 存取速度快 缺点: 数据不能永久保存 数据保存在文件 优点: 数据永久保存 缺点:1…...

解决Vscode使用git提交卡住的问题
使用Vscode的git提交代码经常会很慢/卡住。 先点击左下角,进入设置 找到git的配置(建议直接搜索),把use Editor As commit input的勾选去掉即可解决。...
Linux C语言 32-网络编程之UDP例程
Linux C语言 32-网络编程之UDP例程 本节关键字:C语言 网络编程 UDP协议 套接字操作 服务端 客户端 相关C库函数:setsockopt, socket, bind, recvfrom, sendto, close 相关接口介绍 Linux C语言 30-套接字操作 例程执行任务说明 本例程中服务端的任务…...

ubuntu22.04系统下载程序和依赖,并拷贝到指定路径下
脚本1 apt install aptitude apt-get -d install xxx #xxx是待下载的安装包 mv /var/cache/apt/archives/* /home/tuners/1apt install aptitude apt-get -d install xxx mv /var/cache/apt/archives/*.deb /home/tuners/1 xxx 为程序包名称 /home/tuners/1为保存程序包的…...
Kafka KRaft 版本集群部署详细教程(附配置文件详细解释)
版本说明 Ubuntu 18.04.6Kafka 3.6.0JDK8 集群配置 操作系统ip域名Kafka Broker 端口Kafka Controller 端口Ubuntu 18.04.6192.168.50.131kafka1.com90929093Ubuntu 18.04.6192.168.50.132kafka2.com90929093Ubuntu 18.04.6192.168.50.133kafka3.com90929093 安装 vim, cur…...

在龙蜥 anolis os 23 上 源码安装 PostgreSQL 16.1
在龙蜥 OS 23上,本来想使用二进制安装,结果发现没有针对龙蜥的列表: 于是想到了源码安装,下面我们列出了PG源码安装的步骤: 1.安装准备 1.1.创建操作系统组及用户 groupadd postgres useradd -g postgres -m postgr…...
UDP的不可靠性可以用来做什么
User Datagram Protocol(UDP,用户数据报协议)是互联网协议套件中的一种传输层协议。与TCP不同,UDP是一种无连接的、不可靠的协议。 要知道UDP可以用来做什么,首先我们要知道它有何特点: 1,无连接: UDP是一…...

vue3还用this吗?getCurrentInstance获取当前组件实例
在 Vue 2 中,this 关键字代表当前组件实例。在组件的选项对象中,this 可以用于访问组件实例的属性、方法以及 Vue 实例的一些特定方法。 在Vue3中,我们发现this是undefined,那我们真的没法使用this了吗?vu3给我们提供…...

高校学生宿舍公寓报修维修生活管理系统 微信小程序b2529
本课题要求实现一套基于微信小程序宿舍生活管理系统,系统主要包括(管理员,学生、维修员和卫检员)四个模块等功能。 使用基于微信小程序宿舍生活管理系统相对传统宿舍生活管理系统信息管理方式具备很多优点:首先可以大幅…...

C++类与对象(7)—友元、内部类、匿名对象、拷贝对象时编译器优化
目录 一、友元 1、定义 2、友元函数 3、友元类 二、内部类 1、定义 2、特性: 三、匿名对象 四、拷贝对象时的一些编译器优化 1、传值&传引用返回优化对比 2、匿名对象作为函数返回对象 3、接收返回值方式对比 总结: 一、友元 1、定义…...

Django回顾2
目录 一.HTTP 1.URL介绍 2.格式: 3.补充: 二.web框架 1.什么是框架 2.什么是web框架 3.wsgi协议 基于wsgi协议的web服务器: 4.协议是怎么规定的 三.Django 1.MVC与MTV模型(所有框架其实都遵循MVC架构) 2.…...

<JavaDS> 二叉树遍历各种遍历方式的代码实现 -- 前序、中序、后序、层序遍历
目录 有以下二叉树: 一、递归 1.1 前序遍历-递归 1.2 中序遍历-递归 1.3 后序遍历-递归 二、递归--使用链表 2.1 前序遍历-递归-返回链表 2.2 中序遍历-递归-返回链表 2.3 后序遍历-递归-返回链表 三、迭代--使用栈 3.1 前序遍历-迭代-使用栈 3.2 中序遍…...

如何控制Spring工厂创建对象的次数?详解Spring对象的声明周期!
😉😉 学习交流群: ✅✅1:这是孙哥suns给大家的福利! ✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 🥭🥭3:QQ群:583783…...

计算机杂谈系列精讲100篇-【计算机应用】PyTorch部署及分布式训练
目录 C平台PyTorch模型部署流程 1.模型转换 1. 不支持的操作 2. 指定数据类型 2.保存序列化模型 3.C load训练好的模型 4. 执行Script Module PyTorch分布式训练 分布式并行训练概述 Pytorch分布式数据并行 手把手渐进式实战 A. 单机单卡 B. 单机多卡DP C. 多机多卡DDP D. L…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...