/** * 經過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} }) } } })