vue3后台系统动态路由实现

动态路由的流程:用户登录之后拿到用户信息和token,再去请求后端给的动态路由表,前端处理路由格式为vue路由格式。

1)拿到用户信息里面的角色之后再去请求路由表,返回的路由为tree格式

后端返回路由如下:

前端处理:

共识:动态路由在路由守卫 beforeEach 里面进行处理,每次跳转路由都会走这里。

1.src下新建permission.js文件,main.js中引入

// main.js
import './permission'

2.permission.js里面重要的一点是:要确保路由已经被添加进去才跳转,否则页面会404或白屏

import router from "./router";
import { ElMessage } from "element-plus";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { getToken } from "@/utils/auth";
import usePermissionStore from "@/store/modules/permission";
NProgress.configure({ showSpinner: false });const whiteList = ["/login", "/register"];const isWhiteList = (path) => {return whiteList.some((pattern) => isPathMatch(pattern, path));
};router.beforeEach((to, from, next) => {NProgress.start();if (getToken()) {/* has token*/if (to.path === "/login") {next({ path: "/" });NProgress.done();} else if (isWhiteList(to.path)) {next();} else {// 如果已经请求过路由表,直接进入const hasRefresh = usePermissionStore().hasRefreshif (!hasRefresh) {next()}else{try {// getRoutes 方法用来获取动态路由usePermissionStore().getRoutes().then(routes => {           const hasRoute = router.hasRoute(to.name)routes.forEach(route => {router.addRoute(route) // 动态添加可访问路由表})if (!hasRoute) {// 如果该路由不存在,可能是动态注册的路由,它还没准备好,需要再重定向一次到该路由next({ ...to, replace: true }) // 确保addRoutes已完成} else {next()}}).catch((err)=>{next(`/login?redirect=${to.path}`)})} catch (error) {ElMessage.error(error || 'Has Error')next(`/login?redirect=${to.path}`)NProgress.done()}}}} else {// 没有tokenif (isWhiteList(to.path)) {// 在免登录白名单,直接进入next();} else {next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页NProgress.done();}}
});router.afterEach(() => {NProgress.done();
});

3.store/modules/permission.js

async getRoutes() {this.hasRefresh = false;const roleId = JSON.parse(localStorage.getItem("user")).roldId;return new Promise((resolve, reject)=>{if (roleId) {getRouters({ roleId: roleId }).then((res) => {let routes = [];routes = generaRoutes(routes, res.data);console.log('routes',routes);this.setRoutes(routes)this.setSidebarRouters(routes)resolve(routes);});} else {this.$router.push(`/login`);}})  }//添加动态路由
setRoutes(routes) {this.addRoutes = routes;this.routes = constantRoutes.concat(routes);
},// 设置侧边栏路由
setSidebarRouters(routes) {this.sidebarRouters = routes;
}
// 匹配views里面所有的.vue文件
const modules = import.meta.glob("./../../views/**/*.vue");//将后端给的路由处理成vue路由格式,这个方法不是固定的,根据后端返回的数据做处理
//这段代码是若依框架里的,原来的代码不支持三级路由,我改了下
function generaRoutes(routes, data, parentPath = "") {data.forEach((item) => {if (item.isAccredit == true) {if (item.category.toLowerCase() == "moudle" ||item.category.toLowerCase() == "menu") {const fullPath = parentPath ? `${parentPath}/${item.path}` : item.path;const menu = {path:item.category.toLowerCase() == "moudle"? "/" + item.path: item.path,name: item.path,component:item.category.toLowerCase() == "moudle"? Layout: loadView(`${fullPath}/index`),hidden: false,children: [],meta: {icon: item.icon,title: item.name,},};if (item.children) {generaRoutes(menu.children, item.children, fullPath);}routes.push(menu);}}});return routes;
}export const loadView = (view) => {let res;for (const path in modules) {const dir = path.split("views/")[1].split(".vue")[0];// 将路径转换为数组以便逐级匹配const pathArray = dir.split('/');const viewArray = view.split('/');if (pathArray.length === viewArray.length && pathArray.every((part, index) => part === viewArray[index])) {res = () => modules[path]();break; // 找到匹配项后退出循环}}return res;
};

2)登录接口里后端返回路由表,返回的路由格式为对象数组,不为tree格式

这种情况下需要将后端返回的路由处理成tree格式后,再处理成vue的路由格式,我是分两步处理的。(有来技术框架基础上改的)

后端返回路由如下:这个数据格式比较简陋,但没关系,只要能拿到url或path就没问题

1.登录逻辑里面将数据处理成tree格式,store/modules/user.ts

  const menuList = useStorage<TreeNode[]>("menuList", [] as TreeNode[]);function login(loginData: LoginData) {return new Promise<void>((resolve, reject) => {AuthAPI.login(loginData).then((data) => {const { accessToken, info, menus, welcome } = data;setToken("Bearer" + " " + accessToken); // Bearer eyJhbGciOiJIUzI1NiJ9.xxx.xxxmenuList.value = transRouteTree(menus);// 生成路由和侧边栏usePermissionStoreHook().generateRoutes(menuList.value);resolve();}).catch((error) => {reject(error);});});}// 将后端返回的路由转为tree结构function transRouteTree(data: RouteNode[]): TreeNode[] {if (!data || !Array.isArray(data)) {return [];}const map: { [id: number]: TreeNode } = {};const roots: TreeNode[] = [];data.forEach((node) => {if (!node || typeof node !== "object") {return [];}map[node.id] = {path: node.url ? node.url : "/",component: node.url ? node.url + "/index" : "Layout",name: node.url,meta: {title: node.menuName,icon: "system",hidden: false,alwaysShow: false,params: null,},children: [],};if (node.parentId === 0) {roots.push(map[node.id]);} else {if (map[node.parentId]) {map[node.parentId].children.push(map[node.id]);}}});return roots;}

2.src下的permission.ts 

router.beforeEach(async (to, from, next) => {NProgress.start();const isLogin = !!getToken(); // 判断是否登录if (isLogin) {if (to.path === "/login") {// 已登录,访问登录页,跳转到首页next({ path: "/" });} else {const permissionStore = usePermissionStore();// 判断路由是否加载完成if (permissionStore.isRoutesLoaded) {console.log(to, "to000");if (to.matched.length === 0) {// 路由未匹配,跳转到404next("/404");} else {// 动态设置页面标题const title = (to.params.title as string) || (to.query.title as string);if (title) {to.meta.title = title;}next();}} else {try {// 生成动态路由const list = userStore.menuList || [];await permissionStore.generateRoutes(list);next({ ...to, replace: true });} catch (error) {// 路由加载失败,重置 token 并重定向到登录页await useUserStore().clearUserData();redirectToLogin(to, next);NProgress.done();}}}} else {// 未登录,判断是否在白名单中if (whiteList.includes(to.path)) {next();} else {// 不在白名单,重定向到登录页redirectToLogin(to, next);NProgress.done();}}});// 后置守卫,保证每次路由跳转结束时关闭进度条router.afterEach(() => {NProgress.done();});// 重定向到登录页
function redirectToLogin(to: RouteLocationNormalized, next: NavigationGuardNext) {const params = new URLSearchParams(to.query as Record<string, string>);const queryString = params.toString();const redirect = queryString ? `${to.path}?${queryString}` : to.path;next(`/login?redirect=${encodeURIComponent(redirect)}`);
}

3.store/modules/permission.ts

  /*** 生成动态路由*/function generateRoutes(data: RouteVO[]) {return new Promise<RouteRecordRaw[]>((resolve) => {const dynamicRoutes = transformRoutes(data);routes.value = constantRoutes.concat(dynamicRoutes); // 侧边栏dynamicRoutes.forEach((route: RouteRecordRaw) => router.addRoute(route));isRoutesLoaded.value = true;resolve(dynamicRoutes);});}/*** 转换路由数据为组件*/
const transformRoutes = (routes: RouteVO[]) => {const asyncRoutes: RouteRecordRaw[] = [];routes.forEach((route) => {const tmpRoute = { ...route } as RouteRecordRaw;// 顶级目录,替换为 Layout 组件if (tmpRoute.component?.toString() == "Layout") {tmpRoute.component = Layout;} else {// 其他菜单,根据组件路径动态加载组件const component = modules[`../../views${tmpRoute.component}.vue`];if (component) {tmpRoute.component = component;} else {tmpRoute.component = modules["../../views/error-page/404.vue"];}}if (tmpRoute.children) {tmpRoute.children = transformRoutes(route.children);}asyncRoutes.push(tmpRoute);});return asyncRoutes;
};

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

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

相关文章

贪心算法笔记

贪心算法笔记 大概内容 贪心就是对于一个问题有很多个步骤,我们在每一个步骤中都选取最优的那一个,最后得出答案。就是在一些函数中可行,但是有些比如二次函数,因为它的转折点不一定最优,就是不可行的。那么如何判断贪心呢?有这么几种 看时间复杂度,一般的就是 O ( n…

CVE-2025-22777 (CVSS 9.8):WordPress | GiveWP 插件的严重漏洞

漏洞描述 GiveWP 插件中发现了一个严重漏洞&#xff0c;该插件是 WordPress 最广泛使用的在线捐赠和筹款工具之一。该漏洞的编号为 CVE-2025-22777&#xff0c;CVSS 评分为 9.8&#xff0c;表明其严重性。 GiveWP 插件拥有超过 100,000 个活跃安装&#xff0c;为全球无数捐赠平…

ubuntu官方软件包网站 字体设置

在https://ubuntu.pkgs.org/22.04/ubuntu-universe-amd64/xl2tpd_1.3.16-1_amd64.deb.html搜索找到需要的软件后&#xff0c;点击&#xff0c;下滑&#xff0c; 即可在Links和Download找到相关链接&#xff0c;下载即可&#xff0c; 但是找不到ros的安装包&#xff0c; 字体设…

细说STM32F407单片机以DMA方式读写外部SRAM的方法

目录 一、工程配置 1、时钟、DEBUG、GPIO、CodeGenerator 2、USART3 3、NVIC 4、 FSMC 5、DMA 2 &#xff08;1&#xff09;创建MemToMem类型DMA流 &#xff08;2&#xff09;开启DMA流的中断 二、软件设计 1、KEYLED 2、fsmc.h、fsmc.c、dma.h、dma.c 3、main.h…

二分查找算法——山脉数组的峰顶索引

一.题目描述 852. 山脉数组的峰顶索引 - 力扣&#xff08;LeetCode&#xff09; 二.题目解析 题目给了我们一个山脉数组&#xff0c;山脉数组的值分布就如下面的样子&#xff1a; 然后我们只需要返回数组的峰值元素的下标即可。 三.算法原理 1.暴力解法 因为题目明确说明…

重塑视频创作的格局!ComfyUI-Mochi本地部署教程

一、介绍 mochi是近期Genmo公司开源的先进视频生成模型&#xff0c;具有高保真运动和强大的提示遵循性。此模型的发布极大的缩小了闭源和开源视频生成系统之间的差距。 目前&#xff0c;视频生成模型与现实之间存在巨大差距。其中最影响视频生成的两个关键功能也就是运动质量和…

Docker 安装开源的IT资产管理系统Snipe-IT

一、安装 1、创建docker-compose.yaml version: 3services:snipeit:container_name: snipeitimage: snipe/snipe-it:v6.1.2restart: alwaysports:- "8000:80"volumes:- ./logs:/var/www/html/storage/logsdepends_on:- mysqlenv_file:- .env.dockernetworks:- snip…

Oracle重启后业务连接大量library cache lock

一、现象 数据库和前段应用重启后&#xff0c;出现大量library cache lock等待事件。 二、分析解决 本次异常原因是&#xff1a;原因定位3&#xff1a; 库缓存对象无效 Library cache object Invalidations 三、各类情况具体分析如下 原因定位1&#xff1a;由于文字导致的非…

硬件设计-七位半电压表硬件方案(下)

目录 摘要 简介 解决方案和评估系统简介 应用聚焦&#xff1a;高准确度数据采集器 结论 摘要 本文探讨了为仪器仪表应用设计高准确度设备所涉及的挑战&#xff0c;并介绍了由低INL SAR ADC、全集成式超低温漂精密基准电压源、四通道匹配电阻网络和零漂移低噪声放大器构建的…

基于springboot+vue+微信小程序的宠物领养系统

基于springbootvue微信小程序的宠物领养系统 一、介绍 本项目利用SpringBoot、Vue和微信小程序技术&#xff0c;构建了一个宠物领养系统。 本系统的设计分为两个层面&#xff0c;分别为管理层面与用户层面&#xff0c;也就是管理者与用户&#xff0c;管理权限与用户权限是不…

Termora 一个开源的 SSH 跨平台客户端工具

Termora 是一个终端模拟器和 SSH 客户端&#xff0c;支持 Windows&#xff0c;macOS 和 Linux。 功能特性 支持 SSH 和本地终端支持 SFTP 文件传输支持 Windows、macOS、Linux 平台支持 Zmodem 协议支持 SSH 端口转发支持配置同步到 Gist支持宏&#xff08;录制脚本并回放&…

TypeScript Jest 单元测试 搭建

NPM TypeScript 项目搭建 创建目录 mkdir mockprojectcd mockproject初始化NPM项目 npm init -y安装TypeScript npm i -D typescript使用VSCode 打开项目 创建TS配置文件tsconfig.json {"compilerOptions": {"target": "es5","module&…

sql模糊关联匹配

需求目标&#xff1a; 建立临时表 drop table grafana_bi.zbj_gift_2024;USE grafana_bi; CREATE TABLE zbj_gift_2024 (id INT AUTO_INCREMENT PRIMARY KEY,userName VARCHAR(255),giftName VARCHAR(255),giftNum INT,points INT,teacher VARCHAR(255),sendDate DATETIME,…

Web前端:JavaScript标识符与变量

JavaScript介绍 JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供的核心语法不算…

mac homebrew配置使用

本文介绍mac上homebrew工具的安装、配置过程。homebrew功能类似于centos的yum&#xff0c;用于软件包的管理&#xff0c;使用上有命令的差异。 本次配置过程使用mac&#xff0c;看官方文档&#xff0c;在linux上也可以用&#xff0c;但我没试过&#xff0c;有兴趣的同学可以试试…

对话新晋 Apache SeaTunnel Committer:张圣航的开源之路与技术洞察

近日&#xff0c;张圣航被推选为 Apache SeaTunnel 的 Committer成员。带着对技术的热情和社区的责任&#xff0c;他将如何跟随 Apache SeaTunnel 社区迈向新的高度&#xff1f;让我们一起来聆听他的故事。 自我介绍 请您简单介绍一下自己&#xff0c;包括职业背景、当前的工作…

智慧公厕大数据驱动下的公共卫生管理与优化

在快速发展的城市化进程中&#xff0c;公共卫生问题日益凸显&#xff0c;成为城市管理的重要议题。智慧公厕&#xff0c;作为公共卫生设施的一次革命性创新&#xff0c;正借助物联网技术的东风&#xff0c;引领公共卫生进入一个全新的生态时代。本文将深入探讨智慧公厕如何利用…

后盾人JS--JS值类型使用(终章)

数值类型转换技巧与NaN类型 什么是NaN呢&#xff1f;顾名思义就是&#xff0c;not a number <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,…

EFK采集k8s日志

在 Kubernetes 集群中&#xff0c;需要全面了解各个 pod 应用运行状态、故障排查和性能分析。但由于 Pod 是动态创建和销毁的&#xff0c;其日志分散且存储不持久&#xff0c;因此需要通过集中式日志采集方案&#xff0c;将日志收集到统一的平台并配置日志可视化分析和监控告警…

探索网络安全:浅析文件上传漏洞

前言 在数字化时代&#xff0c;网络安全已成为我们每个人都需要关注的重要议题。无论是个人隐私保护&#xff0c;还是企业数据安全&#xff0c;网络威胁无处不在。了解网络安全的基本知识和防护措施&#xff0c;对我们每个人来说都至关重要。 网络安全 网络安全并非只是对网…