后台管理系统中根据用户权限显示不同菜单是很基础的需求,使用vue开发通常我们是遍历
路由(this.$router.options.routes)
生成用户菜单,vue中提供了addRouter()
方法用于动态挂载路由,我们可以根据用户权限结合该方法实现用户路由动态生成
。代码如下:
(1) router.js:该文件中初始化一个VueRouter
对象,并挂载一些无需权限校验即可访问的路由:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* 初始化路由(无需权限)
*/
export const ConstantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: {
title: '系统首页',
icon: 'dashboard'
}
}
]
}
]
// 定义一个函数来创建router
export const createRouter = routes => new Router({
mode: 'history',
routes
})
const router = createRouter(ConstantRoutes)
/**
* 路由重置,防止router未初始化导致重复挂载
*/
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
(2) dynamicRoutes.js:该文件中定义需要根据用户权限动态挂载显示的路由:
import Layout from '@/layout'
/**
* 动态路由,需要根据用户权限动态挂载
*/
const DynamicRoutes = [
{
path: '/system',
component: Layout,
redirect: '/system/user',
name: 'System',
meta: {
title: '系统管理',
icon: 'example',
permission: 'MENU_SYSTEM'
},
children: [
{
path: 'user',
name: 'User',
component: () => import('@/views/system/user/index'),
meta: {
title: '用户管理',
icon: 'table',
permission: 'MENU_SYSTEM_USER'
}
},
{
path: 'role',
name: 'Role',
component: () => import('@/views/system/role/index'),
meta: {
title: '角色管理',
icon: 'table',
permission: 'MENU_SYSTEM_ROLE'
}
},
{
path: 'dict',
name: 'Dict',
component: () => import('@/views/system/dict/index'),
meta: {
title: '字典管理',
icon: 'table',
permission: 'MENU_SYSTEM_DICT'
}
}
]
},
]
export default DynamicRoutes
(3)permission.js:该文件用于路由跳转前的权限校验,如:token校验、获取用户信息生成用户动态菜单等
import router from './router'
import DynamicRoutes from './router/dynamicRoutes'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
import Layout from '@/layout/index'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
/**
* 根据用户权限生成路由
* @param DynamicRoutes 动态路由表
* @param permissions 用户权限
*/
export function generateMenu(DynamicRoutes, permissions) {
const routers = []
DynamicRoutes.forEach((route) => {
if (route.meta.permission == undefined || route.meta.permission == null || permissions.indexOf(route.meta.permission) > -1) {
let child = route.children
route.children = []
if (child !== undefined && child.length > 0) {
route.children = generateMenu(child, permissions)
}
routers.push(route)
}
})
return routers;
}
router.beforeEach(async(to, from, next) => {
NProgress.start()
// 判断是否登录
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done()
} else {
const hasGetUserInfo = store.getters.name
if (hasGetUserInfo) {
next()
} else {
try {
// 获取用户信息
store.dispatch('getInfo').then((data) => {
// data.permissions是请求后台获取到的权限列表
const routers = generateMenu(DynamicRoutes, data.permissions)
let constantRouters = router.options.routes
constantRouters = constantRouters.concat(routers)
// 注意:'/404'必须挂载在路由的最后位置
constantRouters.push({
path: '*', redirect: '/404', hidden: true
})
// addRouter是让挂载的路由生效,但是挂载后'router.options.routes'并未刷新(八成是个bug),所以还需要手动将路由加入'router.options.routes'
router.addRoutes(routers)
router.options.routes = constantRouters
next()
})
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
易错点总结:
(1)动态挂载路由时addRouter
不会导致’router.options.routes’的刷新,需要我们手动将生成的路由列表设置到router.options.routes
中;
(2)/404
路由必须放在路由表的最后。