目录
1.补充模块,在src/store/modules下补充menu.js
在permission.js中获取action的返回值并过滤
上文说到,角色分配权限
,以及提到权限两个应用
动态生成左侧菜单 以及按钮级控制
登录成功(页面跳转),进入导航守卫:


vue-router对象中的addRoutes,用它来动态添加路由配置
因为用户访问到的页面(路由配置)必须是动态的,所有要先掌握下可以动态添加路由地址的api
router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])
1.在views下创建页面
abc
2.通过代码添加路由配置
// 按钮
// 回调
hAddRoute() {
this.$router.addRoutes([{
path: '/abc',
component: () => import('@/views/abc'),
}])
},
点击了按钮之后,就可以在地址中访问/abc了
1.在router/index.js中的路由配置中删除动态路由的部分
- const createRouter = () => new Router({
- // mode: 'history', // require service support
- scrollBehavior: () => ({ y: 0 }),
- // routes: constantRoutes
- // 合并动态和静态的路由 , ...asyncRoutes
- - routes: [...constantRoutes, ...asyncRoutes]
- + routes: [...constantRoutes]
- })
2.在permission.js中引入,并使用addRoutes动态添加
把之前在router中直接静态写死的动态路由表改造成通过addRoutes 方法调用添加的形式
- // 引入所有的动态路由表(未经过筛选)
- + import router, { asyncRoutes } from '@/router'
-
- const whiteList = ['/login', '/404']
- router.beforeEach(async(to, from, next) => {
- // 开启进度条
- NProgress.start()
- // 获取本地token 全局getter
- const token = store.getters.token
- if (token) {
- // 有token
- if (to.path === '/login') {
- next('/')
- } else {
- if (!store.getters.userId) {
- await store.dispatch('user/getUserInfo')
- // 改写成动态添加的方式
- + router.addRoutes(asyncRoutes)
- }
- next()
- }
- } else {
- // 没有token
- if (whiteList.includes(to.path)) {
- next()
- } else {
- next('/login')
- }
- }
- // 结束进度条
- NProgress.done()
- })
效果
1.左侧的菜单只剩下静态的首页了
2.浏览器手动输入某一个动态路由地址,依旧是可用的,这证明了已经把动态路由添加到我们的路由系统了
当前的菜单渲染(src\layout\components\Sidebar\index.vue)使用的数据:this.$router.options.routes 这个数据是固定,我们通过addRoutes添加的路由表只存在内存中,并不会改变this.$router.options.routes
如果我们希望在调用addRoutes方法之后,要路由数据立刻反映到菜单中,我们需要想一个额外的方法,vue开发中,哪个技术可以保证响应式特性还可以动态修改? vuex
因此在vuex中保存菜单数据

- // 导入静态路由
- import { constantRoutes } from '@/router'
- export default {
- namespaced: true,
- state: {
- // 先以静态路由作为菜单数据的初始值
- menuList: [...constantRoutes]
- },
- mutations: {
- setMenuList(state, asyncRoutes) {
- // 将动态路由和静态路由组合起来
- state.menuList = [...constantRoutes, ...asyncRoutes]
- }
- }
- }
当然,要在src/store/index.js中注册这个模块
- + import menu from './modules/menu'
-
- Vue.use(Vuex)
-
- const store = new Vuex.Store({
- modules: {
- app,
- settings,
- user,
- + menu
- },
- getters
- })
修改src/permission.js中的代码
- if (!store.getters.userId) {
- await store.dispatch('user/getUserInfo')
-
- // 动态添加可以访问的路由设置
- router.addRoutes(asyncRoutes)
-
- // 根据用户实际能访问几个页面来决定从整体8个路由设置
- // 中,过滤中出来几个,然后保存到vuex中
- + store.commit('menu/setMenuList', asyncRoutes)
- }
在src\layout\components\Sidebar\index.vue文件中,修改
routes() {
// 拿到的是一个完整的包含了静态路由和动态路由的数据结构
// return this.$router.options.routes
return this.$store.state.menu.menuList
}
上一步我们实现了
但是我们没有和权限数据做搭配,接下来我们通过接口返回的权限数据对动态菜单(8个页面)做过滤处理,以确定完成菜单与用户权限相关。

过滤使用name作为标识,对照下标检查路由name是否一致

后端的接口约定如下:
用户能访问哪些页面是通过actions获取到的,只需要从action中返回即可。
修改 store/modules/user.js ,补充return语句。
- // 用来获取用户信息的action
- async getUserInfo(context) {
- // 1. ajax获取基本信息,包含用户id
- const rs = await getUserInfoApi()
- console.log('用来获取用户信息的,', rs)
- // 2. 根据用户id(rs.data.userId)再发请求,获取详情(包含头像)
- const info = await getUserDetailById(rs.data.userId)
- console.log('获取详情', info.data)
- // 把上边获取的两份合并在一起,保存到vuex中
- context.commit('setUserInfo', { ...info.data, ...rs.data })
- // 当前用户可以看到的菜单 res.data.roles.menus
- + return rs.data.roles.menus
- },
在src/permission.js中
- if (!store.getters.userId) {
- // 有token,要去的不是login,就直接放行
- // 进一步获取用户信息
- // 发ajax---派发action来做
- + const menus = await store.dispatch('user/getUserInfo')
- console.log('当前用户能访问的页面', menus) // ['salarys', 'settings']
- console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
- // 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
-
- + const filterRoutes = asyncRoutes.filter(route => {
- + const routeName = route.children[0].name
- + return menus.includes(routeName)
- + })
-
- // 一定要在进入主页之前去获取用户信息
-
- // addRoutes用来动态添加路由配置
- // 只有在这里设置了补充了路由配置,才可能去访问页面
- // 它们不会出现左侧
- + router.addRoutes(filterRoutes)
-
- // 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
- // 生成左侧菜单时,也应该去vuex中拿
- + store.commit('menu/setMenuList', filterRoutes)
- }
asyncRoutes.filter
(1)如果我们刷新浏览器,会发现跳到了404页面
(2)对于addRoute添加的路由,在刷新时会白屏
刷新浏览器,会发现跳到了404页面
现在我们的路由设置中的404页处在中间位置而不是所有路由的末尾了。
把404页改到路由配置的最末尾就可以了
1.从route/index.js中的静态路由中删除path:'*'这一项
- // 不需要特殊的权限控制就可以访问的页面
- export const constantRoutes = [
- {
- path: '/login',
- component: () => import('@/views/login/index'),
- hidden: true
- },
-
- // 404 page must be placed at the end !!!
- - { path: '*', redirect: '/404', hidden: true }
- ]
2.在permission.js中补充在最后
- // if(没有userInfo) {
- if (!store.getters.userId) {
- // 有token,要去的不是login,就直接放行
- // 进一步获取用户信息
- // 发ajax---派发action来做
- const menus = await store.dispatch('user/getUserInfo')
- console.log('当前用户能访问的页面', menus)
- console.log('当前系统功能中提供的所有的动态路由页面是', asyncRoutes)
- // 根据本用户实际的权限menus去 asyncRoutes 中做过滤,选出本用户能访问的页面
-
- const filterRoutes = asyncRoutes.filter(route => {
- const routeName = route.children[0].name
- return menus.includes(routeName)
- })
-
- // 一定要在进入主页之前去获取用户信息
-
- // 把404加到最后一条
- + filterRoutes.push( // 404 page must be placed at the end !!!
- { path: '*', redirect: '/404', hidden: true })
-
- // addRoutes用来动态添加路由配置
- // 只有在这里设置了补充了路由配置,才可能去访问页面
- // 它们不会出现左侧
- router.addRoutes(filterRoutes)
-
- // 把它们保存在vuex中,在src\layout\components\Sidebar\index.vue
- // 生成左侧菜单时,也应该去vuex中拿
- store.commit('menu/setMenuList', filterRoutes)
- }
if (!store.getters.userId) {
// 省略其他...
// 解决刷新出现的白屏bug
next({
...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
replace: true // 重进一次, 不保留重复历史
})
} else {
next()
}
退出后,再次登陆,发现菜单异常 (控制台有输出说路由重复);

路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。
需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加
我们的router/index.js文件,发现一个重置路由方法
// 重置路由
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}
这个方法就是将路由重新实例化,相当于换了一个新的路由,之前加的路由就不存在了,需要在登出的时候, 调用一下即可
在store/modules/user.js
- import { resetRouter } from '@/router'
- // 退出的action操作
- logout(context) {
- // 1. 移除vuex个人信息
- context.commit('removeUserInfo')
- // 2. 移除token信息
- context.commit('removeToken')
- // 3. 重置路由
- resetRouter()
- }