项目源码地址:GitHub - PanJiaChen/vue-admin-template: a vue2.0 minimal admin template
注意:项目中的路由均写在 src\router\index.js 中,其中默认包含 constantRoutes 数组,这是固定路由,无论用户是什么角色,都需要显示这些路由内容(即导航栏)。
export const constantRoutes = [
...
]
为了根据权限来动态的生成路由,我们需要添加 asyncRoutes 数组,这里面的内容将根据用户角色权限动态生成路由内容(即导航栏)。
export const asyncRoutes = [
...
]
实现方法:
① 在 src\store\modules\permission.js 中,通过分析 asyncRoutes 里面的角色权限和当前用户所拥有的角色权限,生成用户可查看到的路由列表 permission_routes 。
② 在 src\layout\components\Sidebar\index.vue 中,遍历 permission_routes 显示菜单栏。
配置步骤
1、在 src\router\index.js 文件中
固定路由的写法,不需要任何权限,所有角色都可以访问
/**
* 固定路由
* 没有权限要求的基页
* 所有角色都可以访问
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/dashboard',
component: () => import('@/views/dashboard/index'),
hidden: true
}
/* {
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '首页' }
}]
} */
]
动态路由的写法,需要我们添加的地方,根据权限显示的路由
/**
* 异步路由
* 需要根据用户角色动态加载的路由
*/
export const asyncRoutes = [
// 用户端 start
{
path: '/rescueActivity',
redirect: '/rescueActivity',
component: Layout,
meta: { roles: ['user'] },
children: [
{
path: '/rescueActivity',
name: 'rescueActivity',
component: () => import('@/views/user/rescue-activity'),
meta: { title: '志愿救助活动' }
}
]
},
// 用户端 end
// 管理员端 start
{
path: '/userManage',
redirect: '/userManage',
component: Layout,
meta: { roles: ['admin'] },
children: [
{
path: '/userManage',
name: 'userManage',
component: () => import('@/views/admin/user-manage'),
meta: { title: '用户管理' }
}
]
},
// 管理员端 end
// 404页一定要放在最后!!!
{ path: '*', redirect: '/404', hidden: true }
]
通过修改 meta 属性中 roles 值
只有 admin 角色可以查看:meta: { roles: ['admin'] }
admin 和 user 都可以查看:meta: { roles: ['admin', 'user'] }
注意:{ path: '*', redirect: '/404', hidden: true } 必须放在最后面,并且只能放在 asyncRoutes 中,如果放在 constantRoutes 中的话,刷新页面会报 404 错误
2、在 src\store\modules\user.js 文件中
① 在 getDefaultState 里面的 avatar: '', 后面添加 roles: [] 值,如图:
roles: []
② 在 mutations 里面的 SET_AVATAR: (state, avatar) => { state.avatar = avatar }, 后面添加 SET_ROLES: (state, roles) => { state.roles = roles } 值,如图:
SET_ROLES: (state, roles) => {
state.roles = roles
}
③ 在 getInfo 方法里面的 commit('SET_AVATAR', avatar) 后面添加 commit('SET_ROLES', roles) 值,如图:
const { name, avatar, roles } = data
commit('SET_ROLES', roles)
④ 在 logout、resetToken 方法里面的 commit('RESET_STATE') 后面添加 commit('SET_ROLES', []) 值,如图:
commit('SET_ROLES', [])
3、添加 src\store\modules\permission.js 文件
在 src\store\modules 目录下面没有 permission.js 文件,需要添加
permission.js
import { asyncRoutes, constantRoutes } from '@/router'
/**
* 使用 meta.role 来确定当前用户是否具有权限
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* 通过递归过滤异步路由表
* @param routes 异步路由
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
// 这个地方维护了两个状态一个是addRouters,一个是routes
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
4、在 src\store\getters.js 文件中
在 getters 里面的 name: state => state.user.name, 后面添加 roles: state => state.user.roles, permission_routes: state => state.permission.routes 值,如图:
roles: state => state.user.roles,
permission_routes: state => state.permission.routes
5、将 src\permission.js 文件里面的内容全部替换掉
import router, { constantRoutes } from './router'
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'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// get user info
// note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
// generate accessible routes map based on roles
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// dynamically add accessible routes
router.options.routes = constantRoutes.concat(accessRoutes)
router.addRoutes(accessRoutes)
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true })
} 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()
})
6、在 src\layout\components\Sidebar\index.vue 文件中
将原来的 <sidebar-item ... /> 替换掉
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
在 ...mapGetters 里面的 'sidebar', 后面添加 'permission_routes' 值,如图:
'permission_routes'
7、在 src\store\index.js 文件中
在 import user from './modules/user' 下面添加 import permission from './modules/permission' 导入 permission 值;在 new Vuex.Store 里面的 user, 后面添加 permission 值,如图:
import permission from './modules/permission'
permission