登录的接口请求的三个内容:
1. token
2. 用户信息、角色信息
3. 菜单信息
第一种:基于角色Role的动态路由管理 (不推荐,但市场用的比较多)
首先列出枚举每个角色对应几个路由,然后根据用户登录的角色遍历枚举出来的角色动态注册对应的路由
const roles = {
admin:['所有路由'],
'经理':['10个路由'],
'运营':['5个路由'],
'前台':['2个路由']
'新增一个角色':[xxx] ===>改代码重新发布版本
}
这种做法一个弊端:每添加一个角色,都要手动添加代码key:value。最后前端又要发布新的版本(有解决方案如下)
这种方法大致流程如下:
登录:登录验证通过之后后台会返回一个token给前端,前端会保存在vuex和本地(防止刷新丢失登录状态),然后拿token去后台请求一个userInfo的接口获取用户信息(用户名,权限信息等等)
权限验证:通过token获取用户role信息,然后根据用户role算出对应应有权限信息的路由,最后用router.addRotes动态挂载计算出的路由。
代码的实现:
根据token拿到用户信息(权限信息)
这里代码过于简单就不写了,就是请求后台的数据而已。
请求回来的数据保存在Vuex当中(actions发请求----mutations传数据给state,过程不书写了)
//箭头函数
const getDefaultState = () => {
return {
//获取token
token: getToken(),
//存储用户名
name: '',
//存储用户头像
avatar: '',
//服务器返回的菜单信息【根据不同的角色:返回的标记信息,数组里面的元素是字符串】
routes: [],
//角色信息
roles: [],
//按钮权限的信息
buttons: [],
}
}
vuex查看数据:保存成功
Router中路由的拆分(现在路由是全部注册好的,写死的,不管什么用户看的都是一样的页面。)
分为三类路由:
常量路由,不管什么角色都可以看得路由
任意路由:路径出错的时候重定向404
异步路由:不同角色需要过滤选出路由(根据后台的返回的角色计算出对呀的路由)
store/user.js中对比路由
首先要在state定一个计算好的路由变量resultAsyncRoutes:[] 和一个最终合并展示路由变量resultAllRputes:[]
在获取用户信息的时候异步路由和服务器返回的路由进行对比。commit要提交对比好的路由,所有写一个对比路由函数。computedAsyncRoutes()函数第一个参数是异步路由,第二个是服务器返回的路由。
在actions外面定义一个对比路由的computedAsyncRoutes函数:
//定义一个函数:两个数组进行对比,对比出当前用户到底显示哪些异步路由
const computedAsyncRoutes = (asyncRoutes, routes) => {
//过滤出当前用户【超级管理|普通员工】需要展示的异步路由
return asyncRoutes.filter(item => {
//数组当中没有这个元素返回索引值-1,如果有这个元素返回的索引值一定不是-1
if (routes.indexOf(item.name) != -1) {
//递归:别忘记还有2、3、4、5、6级路由
if (item.children && item.children.length) {
item.children = computedAsyncRoutes(item.children, routes);
}
return true;
}
})
}
commit的时候对比好路由(需要深拷贝一下,不然对比后数据影响原数据)
打印异步路由和服务器返回的路由:
然后在mutations中合并路由(计算出来的异步路由,常量路由,任意路由进行合并)用router.addRotes动态挂载计算出的路由。
根据计算出路由展示菜单
目前为止路由都准备好了,但是侧边栏展示还是常量路由,侧边栏应该遍历vuex合并好的路由resultAllRputes
在Sidebar组件中:
这样侧边栏就这根据权限动态展示了。
按钮权限:
菜单权限:不同的用户(角色),能操作、能观看的菜单是不同的。
按钮的权限:不同的用户(角色),有的用户的是可见按钮、当然有的用户不可见。
实现方式也比较简单:根据后台返回button是否有这个按钮的权限值。
用v-if去判断后台返回buttons是否包含该按钮的权限,没有就不显示。
<template>
<div>
<el-button type="primary" v-if="$store.state.user.buttons.indexOf('btn.Add1')!=-1">添加按钮1</el-button>
<el-button type="primary" v-if="$store.state.user.buttons.indexOf('btn.Add2')!=-1">添加按钮2</el-button>
</div>
</template>
第二种:基于菜单Menu的动态路由管理(推荐)
我这里是从0搭建的,很多细节需要自己处理,看起来比上面的方法麻烦其实不是这样的,上面是使用vue-admin很多细节作者已经处理好了,我这里需要自己处理,也更好的理解各种细节问题。
userMenus =>动态展示菜单
用户管理 ----商品管理----角色管理等等这些路由模块映射成一个对象然后和后端返回这个角色的菜单信息进行对比,然后用Router.addRotes的API动态注册路由。
大致流程:(Vue3+TS+pinia实现)
这里的后台和上面的返回格式不一样,问题也不大。首页还是拿到token,用token拿用户信息,用户信息中有用户id,那个用户id去后台拿菜单信息。
后台返回的菜单信息:
然后对路由进行划分:
对比一下他们的路由信息:
然后进行路由对比,对比好就动态注册路由,要点击登录前就要计算好路由,也就是说push到首页就要算好路由,所有在login中actions进行对比,但是这个逻辑比较多,为了方便管理对比路由函数写在utils
在utils中:
这里用了两个for循环因为这里明确知道两次目录,要是children两层以上可以用递归
在login中actions使用:
计算好的路由放到routes数组中,然后forEach遍历用router.addRoute动态添加路由(addRoute第一个参数是目标路由,第二个参数就是路由信息,代码上的意思就是将遍历出来的路由信息添加main的children下面)
目前效果可以做到点击菜单路由也可以跟着改变了
但是目前有一个问题就是,我注册权限路由是pinia的actions在点击登录按钮的回调进行注册的,现在当我刷新的时候这些路由就访问不到了,只能访问不用权限的路由,为什么会这样?
因为刷新不会重新加载pinia的actions的登录的回调,只有点击登录才会执行,解决办法就是刷新让这个回调重新执行一遍。具体实现如下:
在actions在定一个函数
函数定义好了,要让他刷新执行一遍,需要在main执行(顺序比较重要批念必须先加载,然后再动态注册权限路由,最后加载router)
第一个页面的匹配方法:
现在做一个小细活,就是登录完立刻重定向到第一个页面(这个需要动态的,不是每个角色第一个页面都是一样的)
在那个utils中的mapMenusToRouters函数增加两句代码
然后再router里面的前置守卫使用
根据path匹配menu
现在点击部门管理刷新会跳转到一点页面,我们要的还是留在部门管理这个路由
在nav-menu组件组件中:
计算出defaultActive
在utils写工具函数
大致流程就是这样了。
文章有帮助到您帮忙点个赞赞~~~