vue+elementui搭建後臺管理界面(6登陸和菜單權限控制[二])

根據權限計算路由的代碼

/**
 * 經過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 中ios

動態顯示頂部導航和側邊欄

根據 state.addRouters 中的路由,動態生成頂部導航和側邊欄菜單axios

// 在有權限的路由表裏,查找是否有到目標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}
            })
        }
    }
})
相關文章
相關標籤/搜索