问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

前端如何配合后端实现Vue路由权限

发布网友 发布时间:2024-09-09 15:30

我来回答

1个回答

热心网友 时间:2024-11-21 01:42

前言

在开发管理后台时,都会存在多个角色登录,登录成功后,不同的角色会展示不同的菜单路由。这就是我们通常所说的动态路由权限,实现路由权限的方案有多种,比较常用的是由前端使用addRoutes(V3版本改成了addRoute)动态挂载路由和服务端返回可访问的路由菜单这两种。上一篇文章讲了纯前端实现路由权限,没看过的可以点击文章链接纯前端实现Vue路由权限。今天主要是基于后端返回路由菜单的基础上,实现路由权限功能。

实现思路

后端返回路由菜单主要是在我们登录之后,后端接口会直接返回当前用户可访问的完整路由菜单,相当于前端基于RBAC模型筛选出了前端可访问的路由列表。

需要注意的是,后端返回的路由菜单是不包括login、404等页面的。前端这边还是需要写一份完整的路由列表,基于后端返回的可访问路由菜单去筛选出需要挂载在router上的路由列表。

代码实现登录

首先是登录,登录成功后,服务端会返回登录用户可访问的路由菜单userMenus,我们一般会将这些信息保存到Vuex里。

登录方法:

constlogin=()=>{ruleFormRef.value?.validate((valid:boolean)=>{if(valid){store.dispatch('userMole/login',{...accountForm})}else{console.log('errorsubmit!')}})}

Vuex对应异步操作:

asynclogin({commit},payload:IRequest){//登录获取tokenconst{data}=awaitaccountLogin(payload)commit('SET_TOKEN',data.token)localCache.setCache('token',data.token)//获取用户信息constuserInfo=awaitgetUserInfo(data.id)commit('SET_USERINFO',userInfo.data)localCache.setCache('userInfo',userInfo.data)//获取菜单constuserMenu=awaitgetUserMenu(userInfo.data.role.id)commit('SET_USERMENU',userMenu.data)localCache.setCache('userMenu',userMenu.data)router.replace('/main/analysis/dashboard')},

接口返回的路由菜单信息:

路由菜单

可以看到,返回的userMenus是一个数组,包含了图标icon、路由名称name、路由地址、子路由children、路由type等重要信息。前面这些信息主要是用于遍历生成页面左侧的菜单列表,路由type则是用于后面筛选出需要挂载在router上的路由列表。

本地路由列表

前端这边还是需要写一份完整的路由列表,我这里打算在router/index.ts里面写入接口不返回的菜单,如login、404等页面。将接口可能返回的菜单单独放在router/main下面。

router/index.ts:

import{createRouter,createWebHashHistory,RouteRecordRaw}from'vue-router'constroutes:RouteRecordRaw[]=[{path:'/',redirect:'/main'},{path:'/login',name:'login',component:()=>import('@/views/login/index.vue'),meta:{title:'登录'}},{path:'/main',name:'main',redirect:'/main/analysis/dashboard',component:()=>import('@/views/main/index.vue'),meta:{title:'核心技术'}},{path:'/:pathMatch(.*)*',name:'notFound',component:()=>import('@/views/404.vue'),meta:{title:'页面找不到~'}}]

router/main下面就是写入所有菜单列表:

单个菜单内容,如dashboard.ts:

constdashboard=()=>import('@/views/main/analysis/dashboard/dashboard.vue')exportdefault{path:'/main/analysis/dashboard',name:'dashboard',component:dashboard,meta:{title:'商品统计'},children:[]}

整个router目录:

router目录

接下来,我们就需要根据userMenus去过滤我们写好的router/main下面的路由,也就是接口返回的菜单列表对应一份路由列表,然后将路由列表挂载在router上,这样就能访问路由了。

生成路由

现在我们需要根据userMenus生成对应的路由。

首先我们需要去加载所有的路由,也就是router/main下面的路由文件内容。这里我使用的是require.context方法来加载所有的路由。

这里简单介绍下require.context这个api:

require.context是webpack的一个api,通过执行require.context()函数,来获取指定的文件夹内的特定文件,在需要多次从同一个文件夹内导入的模块,使用这个函数可以自动导入,不用每个都显示的写import来引入。

require.context(directory,useSubdirectories,regExp)需要的参数:

directory:要搜索文件的相对路径

useSubdirectories:是否查询其子目录

regExp:匹配基础组件文件名的正则表达式

我们就通过这个api来加载router/main下面的路由。

constrouteFiles=require.context('../router/main',true,/.ts/)

我们对routeFiles进行打印:

routeFiles

得到了一个对象,我们需要对这个对象进行遍历拿到文件内容:

routeFiles.keys().forEach((key)=>{constroute=require('../router/main'+key.split('.')[1]).defaultconsole.log(route)allRoutes.push(route)})

打印得到route

route

这样我们就得到了所有的路由,放在allRoutes里面。

接下来我们需要根据userMenus获取需要添加的routes。

开始我们提到过路由type,这个字段主要是区分菜单下是否还有子菜单,1表示有子菜单,2表示没有子菜单。

接口返回的菜单

我们将allRoutes进行遍历,然后根据path与接口返回的菜单列表userMenus里的path进行比较,如果相同就是匹配到了,那我们就需要这条路由,否则就将这条路由过滤掉。由于allRoutes下的每一项都还可能存在子路由,所以这里我们也需要进行递归筛选。具体的方法如下:

const_recurseGetRoute=(menus:any[])=>{for(constmenuofmenus){if(menu.type===2){constroute=allRoutes.find((route)=>route.path===menu.url)if(route)routes.push(route)}else{_recurseGetRoute(menu.children)}}}

最终,routes就是我们得到的userMenus所对应的路由列表。

将生成对应的路由的逻辑整理如下:

import{RouteRecordRaw}from'vue-router'exportfunctiongenerateRoutes(userMenus:any[]):RouteRecordRaw[]{constroutes:RouteRecordRaw[]=[]//1.先去加载默认所有的routesconstallRoutes:RouteRecordRaw[]=[]constrouteFiles=require.context('../router/main',true,/.ts/)routeFiles.keys().forEach((key)=>{constroute=require('../router/main'+key.split('.')[1]).defaultconsole.log(route)allRoutes.push(route)})//2.根据菜单获取需要添加的routes//userMenus://type===1->children->type===1//type===2->url->routeconst_recurseGetRoute=(menus:any[])=>{for(constmenuofmenus){if(menu.type===2){constroute=allRoutes.find((route)=>route.path===menu.url)if(route)routes.push(route)}else{_recurseGetRoute(menu.children)}}}_recurseGetRoute(userMenus)returnroutes}挂载路由

最后,需要将我们得到的routes挂载er上面。

还是将挂载路由的时机放在全局路由守卫这里,我们在router文件夹下创建一个permission.ts,用于写全局路由守卫相关逻辑:

importrouterfrom'@/router'import{RouteLocationNormalized}from'vue-router'importlocalCachefrom'@/utils/cache'importNProgressfrom'nprogress'import'nprogress/nprogress.css'importstorefrom'@/store'NProgress.configure({showSpinner:false})constwhiteList=['/login']constuserMenu=store.state.userMole.userMenurouter.beforeEach(async(to:RouteLocationNormalized,from:RouteLocationNormalized,next:any)=>{document.title=to.meta.titleasstringconsttoken:string=localCache.getCache('token')NProgress.start()//判断该用户是否登录if(token){if(to.path==='/login'){//如果登录,并准备进入login页面,则重定向到主页next({path:'/'})NProgress.done()}else{store.dispatch('routesMole/generateRoutes',{userMenu})//确保添加路由已完成//设置replace:true,因此导航将不会留下历史记录next({...to,replace:true})}}else{//如果没有tokenif(whiteList.includes(to.path)){//如果在免登录的白名单中,则直接进入next()}else{//其他没有访问权限的页面将被重定向到登录页面next('/login')NProgress.done()}}})router.afterEach(()=>{NProgress.done()})

routesMole文件下的代码:

//引入generateRoutesimport{generateRoutes}from'@/utils/generateRoutes'actions:{generateRoutes({commit},{userMenu}){constroutes=generateRoutes(userMenu)//将routes=>router.main.childrenroutes.forEach((route)=>{router.addRoute('main',route)})}}

这样,完整的路由权限功能就完成了。我们可以看一下页面:

系统界面

总结

相比纯前端实现路由权限,这种基于后端返回路由菜单的方式会显得简单一些。我们不需要经过RBAC去过滤出用户可以访问的路由,而是接口直接返回给了我们。我们只需要将路由菜单对应生成一份路由,然后将路由进行挂载。

原文:https://juejin.cn/post/7096393921034453006
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
八月中国最凉快的地方 八月份哪里最凉快,去哪旅游好?美丽的地方 乱字同韵字是什么意思 华硕笔记本电脑触摸板怎么开笔记本电脑触摸板怎么开启和关闭_百度知 ... 陕西职务侵占案立案准则 结婚后我的恋情维系了十年,怎么做到的? 玉米仁子饭产自哪里 中国期货交易所的交易品种有哪些? 历史要怎么读,有啥诀窍 高中历史诀窍 Vue + Element Plus 实现权限管理系统(四):动态添加路由 (五)Vue实用框架-Ruoyi(权限控制和页面渲染) 荷兰风格派的代表人物是谁 荷兰风格派的代表人物是 赛博侦探喜欢哪个女角色数码宝贝 荷兰风格派运动基本信息 数码宝贝故事赛博侦探hard难度全BOSS打法攻略介绍_数码宝贝故事赛博侦 ... 数码宝贝故事:赛博侦探的介绍 数码宝贝故事:赛博侦探的剧情介绍 电影名字关于哥斯拉的。。。 淮北邮件处理车间在哪 想知道: 淮北市 118公交线路的路线,途径哪些地点 租号玩怎么把自己号租出去 消除安全隐患,福特领界把微信搬上车! 疯暴突鸡爪剧情简介 梦见福禄寿还有人家死人了 梦见福禄寿还有人家死人了什么情况 街道办跟社区有什么区别 做了心脏支架后长期服药饮食需要忌口吗 心梗病人忌口 流式抗体选择5要素 Excel如何设置横轴和纵轴 vuerouter根据路由区分用户权限并生成动态侧边栏 小米公司的企业文化价值观 这个配置使命召唤12,古墓丽影崛起还有其他大型单机怎么样 双语动画之爆笑喜剧动画片《咱们裸熊》(We Bare Bears )1-4季+电... 山里的孩子像大山一样结实健壮是什么修辞手法 英雄联盟什么英雄玩不腻? 英雄联盟里那个英雄有爆发伤害,有逃跑技能,有趣味性玩不腻,的是那个... 英雄联盟有什么英雄可以长久的玩还不腻? 英雄联盟哪个英雄玩不腻?现在偏爱战士了 英雄联盟里哪个英雄玩不腻,我一般一个英雄玩3把就不想碰了,求解 梦见自己喜欢的女孩染头发了是什么寓意?染成紫红色的了,喜欢的女孩,现 ... 拍婚纱把头发染成紫红色,效果会好吗 kindle都有哪些型号 3.5镍材质用什么牌号焊条? kindle哪个型号好 kindle所有型号对比 kindle是什么?有哪些型号? 潘珊梅专家简介 这样算误诊吗?