根據權限計算路由的代碼
/**
* 通過meta.role判斷是否與當前用戶權限匹配
* @param roles
* @param route
*/
function hasRoles (roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return false
}
}
/**
* 遞歸過濾異步路由表,返回符合用戶角色權限的路由表
* @param routes asyncRouterMap
* @param roles
*/
function filterAsyncRouter(asyncRouterMap, roles) {
const accessedRouters = asyncRouterMap.filter(route => {
// 404
if(route.path === '*'){
return true
}else if (hasRoles(roles, route)) {
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, roles)
}
return true
}
return false
})
return accessedRouters
}
GenerateRoutes ({ commit }, data) {
return new Promise(resolve => {
const { roles } = data
let accessedRouters
if (roles.includes('admin')) {
accessedRouters = asyncRouterMap
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
}
commit('SET_ROUTERS', accessedRouters)
resolve()
})
},
以上函數接收異步路由表、權限列表,返回在權限列表中的路由,保存在 state.addRouters 中
動態顯示頂部導航和側邊欄
根據 state.addRouters 中的路由,動態生成頂部導航和側邊欄菜單
// 在有權限的路由表里,查找是否有到目標path的路由
// 為了保持路由唯一性,拼接父子路由
function hasDestRoute (froute, permitRouterMap, to) {
let r = froute === '/' ? '' : froute
return permitRouterMap.some(route => {
let path = r + '/' + route.path
if (to.path.indexOf(path) !== -1) {
return true;
}
if (route.children && route.children.length) { //如果有孩子就遍歷孩子
return hasDestRoute(path, route.children, to)
}
})
}
/** ...省略的代碼 */
SET_NOW_ROUTERS: (state, to) => {
// 由於首頁重定向到 /dashboard,並且不參與權限控制,特殊處理
if(to.path === '/dashboard'){
let dashboard = state.routers.filter(v => v.path === '/' )
state.sidebar_routers = dashboard[0]
}else{
// 遞歸訪問 accessedRouters,找到包含to 的那個路由對象,設置給 sidebar_routers
state.addRouters.forEach(e => {
if (e.children && e.children.length) {
if ( hasDestRoute2(e.path, e.children, to)){
if(state.sidebar_routers.path){
// 存在 sidebar_routers 且與目標路由不同
if(state.sidebar_routers.path !== e.path){
state.sidebar_routers = e;
}
}else{
state.sidebar_routers = e;
}
}
}
})
}
}
關鍵的控制代碼
在路由跳轉前,判斷是否登錄、拉取權限、生成菜單等
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) {
return true // admin權限 直接通過
}
// 沒有配置權限的菜單直接進入
if (!permissionRoles){
return true
}
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
/** ...省略的代碼 */
const whiteList = ['/login',] // 不重定向白名單
router.beforeEach((to, from, next) => {
// 切換路由時清空上個路由未完成的所有請求
const cancelToken = axios.CancelToken
clearRequest.source.cancel && clearRequest.source.cancel('CANCELD_BY_USER')
clearRequest.source = cancelToken.source()
// 在免登錄白名單,直接進入
if(whiteList.indexOf(to.path) !== -1){
next()
}else{
if(store.getters.token) {
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() //
}else{
// 判斷當前用戶是否已拉取完user_info信息
if(store.getters.roles.length === 0){
// 拉取用戶信息
store.dispatch('GetUserInfo')
.then(resp => {
const roles = resp.data.roles
store.dispatch('GenerateRoutes', {roles})
.then(()=>{
// 根據roles權限生成可訪問的路由表
// 動態添加可訪問路由表
router.addRoutes(store.getters.addRouters)
next({...to, replace: true})
})
})
.catch((err) => {
store.dispatch('FedLogOut').then(()=>{
Message.error({
message: err || '認證失敗,請重新登錄',
duration: 2000,
})
next({ path: '/login' })
})
})
}else{
console.log('call GenSidebarRoutes')
store.dispatch('GenSidebarRoutes', to)
.then(()=> {
if(hasPermission(store.getters.roles, to.meta.role)){
next()
}else{
next({
path: '/',
query: {noGoBack: true}
})
}
})
}
}
}else{
// 重定向到登錄頁
next({
path: '/login',
query: {redirect: to.fullpath}
})
}
}
})