Fork me on GitHub

Vue+Element-Ui搭建后台管理界面(登录和菜单权限控制[二])

权限管理时发现一篇好的博文

1.根据权限计算路由的代码

/**
 * 通过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 中

 

2.动态显示顶部导航和侧边栏

根据 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;
                }
            }
        }
        })
    }
}

 

3.关键的控制代码

在路由跳转前,判断是否登录、拉取权限、生成菜单等

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}
            })
        }
    }
})

 

原文转载于https://www.cnblogs.com/wbjxxzx/p/10081491.html

posted @ 2021-08-31 01:00  Hui_Li  阅读(846)  评论(0编辑  收藏  举报