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

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;
};

相关文章:

vue3后台系统动态路由实现

动态路由的流程&#xff1a;用户登录之后拿到用户信息和token&#xff0c;再去请求后端给的动态路由表&#xff0c;前端处理路由格式为vue路由格式。 1&#xff09;拿到用户信息里面的角色之后再去请求路由表&#xff0c;返回的路由为tree格式 后端返回路由如下&#xff1a; …...

解决idea中无法拖动tab标签页的问题

1、按 Ctrl Alt S 打开设置&#xff0c;找到路径 File | Settings | Appearance & Behavior | Appearance 2、去掉勾选 Drag-and-drop with Alt pressed only 即可...

WMS仓库管理系统,Vue前端开发,Java后端技术源码(源码学习)

一、项目背景和建设目标 随着企业业务的不断扩展&#xff0c;仓库管理成为影响生产效率、成本控制及客户满意度的重要环节。为了提升仓库作业的透明度、准确性和效率&#xff0c;本方案旨在构建一套全面、高效、易用的仓库管理系统&#xff08;WMS&#xff09;。该系统将涵盖库…...

25/1/12 嵌入式笔记 学习esp32

了解了一下位选线和段选线的知识&#xff1a; 位选线&#xff1a; 作用&#xff1a;用于选择数码管的某一位&#xff0c;例如4位数码管的第1位&#xff0c;第2位&#xff09; 通过控制位选线的电平&#xff08;高低电平&#xff09;&#xff0c;决定当前哪一位数码管处于激活状…...

【NLP】ELMO、GPT、BERT、BART模型解读及对比分析

文章目录 一、基础知识1.1 Word Embedding&#xff08;词嵌入&#xff09;1.2 词嵌入模型1.3 神经网络语言模型NNLM 二、ELMO2.1 ELMO的提出2.2 ELMO核心思想2.3 ELMO的优缺点 三、GPT3.1 Transformer3.2 GPT简介3.3 GPT模型架构3.4 预训练及微调3.5 GPT和ELMO对比 四、BERT4.1…...

go语言学习(数组,切片,字符串)

字符串 如果里面存储的是汉字,那么其实就是存储的是UTF--8编码,所以一个字会对应多个字节.如果想要获取汉字的个数,可以使用rune,来处理unicode字符 length: utf8.RuneCountInString( s) 如果只使用len()获取的是字节的个数, 字符串的功能 1,获取字节长度 len(xx) 2,获取字…...

PM 实战 - 智能药盒PRD + 市场规模分析

写在前面 智能硬件 PRD 实例资源很少&#xff0c;Po下个人作品&#xff0c;假定前提为to Boss需求&#xff0c;目标在于覆盖产品设计核心部分&#xff08;用户画像Persona、产品逻辑图、产品架构图、软件原型图、硬件低保真设计、用例Use Case、硬件标准&#xff09;。不是申请…...

SQL刷题快速入门(二)

其他章节&#xff1a;SQL刷题快速入门&#xff08;一&#xff09; 承接上一章节&#xff0c;本章主要讲SQL的运算符、聚合函数、SQL保留小数的几种方式三个部分 运算符 SQL 支持多种运算符&#xff0c;用于执行各种操作&#xff0c;如算术运算、比较、赋值、逻辑运算等。以下…...

hive迁移后修复分区慢,怎么办?

我有1个30TB的分区表&#xff0c;客户给的带宽只有600MB&#xff0c;按照150%的耗时来算&#xff0c;大概要迁移17小时。 使用hive自带的修复分区命令&#xff08;一般修复分区比迁移时间长一点&#xff09;&#xff0c;可能要花24小时。于是打算用前面黄大佬的牛B方案。 Hive增…...

代码随想录算法训练营day27

代码随想录算法训练营 —day27 文章目录 代码随想录算法训练营前言一、贪心算法理论基础二、455.分发饼干三、376. 摆动序列53. 最大子数组和总结 前言 今天是算法营的第27天&#xff0c;希望自己能够坚持下来&#xff01; 今日任务&#xff1a; ● 贪心算法理论基础 ● 455.…...

python 代码使用 DeepXDE 库实现了一个求解二维非线性偏微分方程(PDE)的功能

import deepxde as dde import numpy as np import matplotlib.pyplot as plt import tensorflow as tf# 设置时空计算域 Lx 1 # x 范围从 0 到 1 Ly 1 # y 范围从 0 到 1 Lt 0.05 # t 范围从 0 到 0.05 geom dde.geometry.Rectangle([0, 0], [Lx, Ly]) # 空间域 timed…...

【Go】:深入解析 Go 1.24:新特性、改进与最佳实践

前言 Go 1.24 尚未发布。这些是正在进行中的发布说明。Go 1.24 预计将于 2025 年 2 月发布。本文将深入探讨 Go 1.24 中引入的各项更新&#xff0c;并通过具体示例展示这些变化如何影响日常开发工作&#xff0c;确保为读者提供详尽而有价值的参考。 新特性及改进综述 HTTP/2 …...

VUE3 一些常用的 npm 和 cnpm 命令,涵盖了修改源、清理缓存、修改 SSL 协议设置等内容。

以下是一些常用的 npm 和 cnpm 命令&#xff0c;涵盖了修改源、清理缓存、修改 SSL 协议设置等内容。 npm 常用命令 1. 修改 npm 源 更改为淘宝的 npm 镜像源&#xff08;可以提高安装速度&#xff09;&#xff1a; bash复制代码 npm config set registry https://registry…...

【SpringBoot】@Value 没有注入预期的值

问题复现 在装配对象成员属性时&#xff0c;我们常常会使用 Autowired 来装配。但是&#xff0c;有时候我们也使用 Value 进行装配。不过这两种注解使用风格不同&#xff0c;使用 Autowired 一般都不会设置属性值&#xff0c;而 Value 必须指定一个字符串值&#xff0c;因为其…...

【STM32-学习笔记-6-】DMA

文章目录 DMAⅠ、DMA框图Ⅱ、DMA基本结构Ⅲ、不同外设的DMA请求Ⅳ、DMA函数Ⅴ、DMA_InitTypeDef结构体参数①、DMA_PeripheralBaseAddr②、DMA_PeripheralDataSize③、DMA_PeripheralInc④、DMA_MemoryBaseAddr⑤、DMA_MemoryDataSize⑥、DMA_MemoryInc⑦、DMA_DIR⑧、DMA_Buff…...

js实现一个可以自动重链的websocket客户端

class WebSocketClient {constructor(url, callback, options {}) {this.url url; // WebSocket 服务器地址this.options options; // 配置选项&#xff08;例如重试间隔、最大重试次数等&#xff09;this.retryInterval options.retryInterval || 1000; // 重试间隔&#…...

企业总部和分支通过GRE VPN互通

PC1可以ping通PC2 1、首先按照地址表配置ip地址 2、分别在AR1和AR3上配置nat 3、配置GRE a 创建tunnel接口&#xff0c;并选择tunnel协议为GRE&#xff0c;为隧道创建一个地址&#xff0c;用作互联 b 为隧道配置源地址或者源接口&#xff0c;这里选择源接口&#xff1b;再为…...

油猴支持阿里云自动登陆插件

遇到的以下问题&#xff0c;都已在脚本中解决&#xff1a; 获取到的元素赋值在页面显示&#xff0c;但是底层的value并没有改写&#xff0c;导致请求就是获取不到数据元素的加载时机不定&#xff0c;尤其是弱网情况下&#xff0c;只靠延迟还是有可能获取不到&#xff0c;且登陆…...

【2024年华为OD机试】(C卷,100分)- 字符串筛选排序 (Java JS PythonC/C++)

一、问题描述 题目描述 输入一个由N个大小写字母组成的字符串 按照ASCII码值从小到大进行排序 查找字符串中第K个最小ASCII码值的字母 (k > 1) 输出该字母所在字符串中的位置索引 (字符串的第一个位置索引为0) k如果大于字符串长度则输出最大ASCII码值的字母所在字符串…...

iOS - runtime总结

详细总结一下 Runtime 的核心内容&#xff1a; 1. 消息发送机制 // 消息发送的基本流程 id objc_msgSend(id self, SEL _cmd, ...) {// 1. 获取 isaClass cls object_getClass(self);// 2. 查找缓存IMP imp cache_getImp(cls, _cmd);if (imp) return imp(self, _cmd, ...);…...

第33 章 - ES 实战篇 - MySQL 与 Elasticsearch 的一致性问题

思维导图 0. 前言 MySQL 与 Elasticsearch 一致性问题是老生常谈了。网上有太多关于这方面的文章了&#xff0c;但是千篇一律&#xff0c;看了跟没看没有太大区别。 在生产中&#xff0c;我们往往会通过 DTS 工具将 binlog 导入到 Kafka&#xff0c;再通过 Kafka 消费 binlog&…...

Artec Leo 3D扫描仪与Ray助力野生水生动物法医鉴定【沪敖3D】

挑战&#xff1a;捕获大型水生哺乳动物&#xff08;如鲸鱼&#xff09;的数据&#xff0c;搭建全彩3D模型&#xff0c;用于水生野生动物的法医鉴定、研究和保护工作。 解决方案&#xff1a;Artec Eva、Artec Space Spider、Artec Leo、Artec Ray、Artec Studio、CT scans 效果&…...

PythonQT5打包exe线程使用

打包&#xff1a; pyinstaller --noconsole --onefile test.py–noconsole 表示不需要打开命令行 修改&#xff1a;test.spec 一般项目里面需要用的资源文件&#xff0c;比如lib、png、exe等。 需要单独修改spec文件 pathex[.],binaries[(D:/test.png, .),(D:/simsun.ttc, .…...

【Powershell】Windows大法powershell好(二)

PowerShell基础&#xff08;二&#xff09; 声明&#xff1a;该笔记为up主 泷羽的课程笔记&#xff0c;本节链接指路。 警告&#xff1a;本教程仅作学习用途&#xff0c;若有用于非法行为的&#xff0c;概不负责。 1. powershell 执行外部命令 powershell也可以执行一些外部的…...

前端学习-环境this对象以及回调函数(二十七)

目录 前言 目标 环境对象 作用 环境对象this是什么&#xff1f; 判断this指向的粗略规则是什么&#xff1f; 回调函数 目标 常见的使用场景 综合案例&#xff1a;Tab任务栏切换 总结 前言 男儿何不带吴钩&#xff0c;收取关山五十州 目标 能够分析判断函数运行在不…...

Element-plus、Element-ui之Tree 树形控件回显Bug问题。

需求&#xff1a;提交时&#xff0c;需要把选中状态和半选中状态 的数据id提交。如图所示&#xff1a; 数据回显时&#xff0c;会出现代码如下&#xff1a; <template><el-tree ref"treeRef" :data"tree" show-checkbox node-key"id" …...

互联网全景消息(10)之Kafka深度剖析(中)

一、深入应用 1.1 SpringBoot集成Kafka 引入对应的依赖。 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupI…...

Oracle Dataguard(主库为双节点集群)配置详解(5):将主库复制到备库并启动同步

Oracle Dataguard&#xff08;主库为双节点集群&#xff09;配置详解&#xff08;5&#xff09;&#xff1a;将主库复制到备库并启动同步 目录 Oracle Dataguard&#xff08;主库为双节点集群&#xff09;配置详解&#xff08;5&#xff09;&#xff1a;将主库复制到备库并启动…...

pytorch小记(一):pytorch矩阵乘法:torch.matmul(x, y)

pytorch小记&#xff08;一&#xff09;&#xff1a;pytorch矩阵乘法&#xff1a;torch.matmul&#xff08;x, y&#xff09;/ x y 代码代码 1&#xff1a;torch.matmul(x, y)输入张量&#xff1a;计算逻辑&#xff1a;输出结果&#xff1a; 代码 2&#xff1a;y y.view(4,1)…...

PyTorch环境配置常见报错的解决办法

目标 小白在最基础的环境配置里一般都会出现许多问题。 这里把一些常见的问题分享出来。希望可以节省大家一些时间。 最终目标是可以在cmd虚拟环境里进入jupyter notebook&#xff0c;new的时候有对应的环境&#xff0c;并且可以跑通所有的import code。 第一步&#xff1a;…...

wordpress 粘贴表格/如何制作一个网页页面

从2月13号开学到现在&#xff0c;感觉自己每天的生活都是恍恍惚惚的&#xff0c;缺乏动力&#xff0c;缺少恒心和毅力。 四月份&#xff0c;更是如此。4月1号&#xff0c;三年级的师兄师姐们毕业离校了&#xff0c;陪着师兄师姐high了两三天。 接下来是清明节&#xff0c;懒懒的…...

设计素材网站特点/产品推广渠道

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2022年低压电工练习题是低压电工考试题库的多种练习模式&#xff01;2022年低压电工操作证考试题及答案依据低压电工最新教材汇编。低压电工考试资料随时根据安全生产模拟考试一点通上手机同步练习。 1、【单选题】( …...

北京做网站公司/关键词优化是什么工作

文章目录前言动态表和动态查询的概念动态表的时间属性引用前言 Fink在新发布的1.7版本中&#xff0c;不断完善和加强了SQL&Table API方面的功能支持。这使得在流计算过程中&#xff0c;用户同样能够运用熟悉的SQL语句来做数据处理&#xff0c;查询。但是相比于窗体的RDBMS而…...

开公司建网站/网店如何引流与推广

示例代码&#xff1a; namespace ConsoleApp1 {//定义委托&#xff0c;它定义了可以代表的方法的类型public delegate void GreetingDelegate(string name);class Program{public static void GreetPeople(string name, GreetingDelegate MakeGreeting){MakeGreeting(name);}p…...

临沂市网站建设公司/通过qq群可以进行友情链接交换

建议优先选择centos系统&#xff0c;不过2个显然都是可以满足我们的日常建站需求的。国内其实centos和ubuntu使用的人都还是挺多的&#xff0c;一般用户而言基本没啥显著的区别的&#xff0c;阿里云基本上都可以使用。 不过不用担心的&#xff0c;因为我们的操作系统是可以在后…...

网站建设的关键/搜索引擎营销案例分析

Oracle导出空&#xff08;无数据&#xff09;表 exp name/pass fileD:/backup.dmp logD:/backup.log;其中table_1中有数据&#xff0c;table_2中没有数据&#xff1b;由于 ORACLE 11g中有个新特性&#xff0c;当表无数据时&#xff0c;不分配segment&#xff0c;以节省空间。所…...