權限問題:用戶和管理員進入管理系統看到的模塊是不一樣的,管理員看的的要比用戶看到的多。需要用到動態加載路由,router.addRouters()來動態的掛載路由
// 1.先登錄將獲取到的用戶的token存儲起來 // store/index.js // 封裝的api接口方法 import { getLogin, getUserInfo } from '../api/index' state: { roles: [], token: window.localStorage.getItem('token') || '' }, mutations: { // 將獲取到的token存儲到本地 SET_TOKEN (state, token) { state.token = token window.localStorage.setItem('token', state.token) } }, actions: { userLogin ({ commit }, userInfo) { // 返回一個異步回調,promise return new Promise((resolve, reject) => { // 調用封裝好的請求數據的方法 getLogin(userInfo).then(res => { const data = res.data if (data.code === 200) { // 調用mutations中的方法,將token存儲到本地中 commit('SET_TOKEN', data.token) } resolve(res.data) }).catch(error => { reject(error) }) }) }, // 根據用戶的token獲取用戶的個人信息,里面包含了權限信息 getUserInfo ({ state }) { // 返回一個異步回調,promise return new Promise((resolve, reject) => { // 調用接口方法獲取數據 getUserInfo(state).then(res => { // 將獲取到的信息放到數組中存儲 state.roles.push(res.data) resolve(res.data) }).catch(error => { reject(error) }) }) } } // 在login/index.vue中使用,點擊按鈕觸發 userLogin () { this.$store.dispatch('userLogin', this.userInfo).then(res => { // 登陸成功跳轉主頁 this.$router.push('/home') }).catch(error => { console.log(error) }) } // vuex的計算屬性 getters: { // 將用戶數據放到計算屬性中,一旦數據發生變化,可以重新計算 roles (state) { return state.roles }, token (state) { return state.token } }, // 權限模塊的處理 // router/index.js // 路由中的配置 // 靜態路由,沒有權限的所有用戶都可以看到 // 引入登陸頁面 import Login from '../views/Login' export const staticRoutes = [ { path: '/login', name: 'login', component: Login }, { path: '/home', name: 'home', component: () => import('../views/Home') } ] // 異步(動態)掛載的路由,根據權限展示 export const asyncRouteMap = [ { path: '/details', name: 'details', component: () => import('../views/Details'), meta: { role: ['admin', 'super_editor'] // 頁面需要的權限 }, children: [ { path: 'index', name: 'index', component: () => import('../views/Details/Child'), meta: { role: ['admin', 'super_editor'] // 頁面需要的權限 }, children: [ { path: '/next', name: 'next', meta: { role: ['admin', 'super_editor'] // 頁面需要的權限 } } ] } ] }, { path: '/error', component: () => import('../views/404'), name: 404 }, { path: '*', redirect: '/error', hidden: true } ] // 因為可以動態的掛載路由,但是不能動態刪除路由。所以才考略到, // 在需要動態清空動態掛載路由的話,直接將一個新的路由對象賦值給舊的路由對象,這樣就可以達到動態清除的工作 const createRouter = () => new VueRouter({ scrollBehavior: () => ({ y: 0 }), routes: staticRoutes }) const router = createRouter() // 調用該方法動態清除動態掛載路由 export function resetRouter () { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router } export default router // store/permission.js // 引入權限路由,和公共路由 import { staticRoutes, asyncRouteMap } from '../router/index' // 用來篩選后端返回來的權限數據,和權限路由中的meta里面的數據匹配,匹配成功返回true,失敗為false function hasPerMission (roles, route) { if (route && route.meta.role) { return roles.some(role => route.meta.role.indexOf(role) >= 0) } else { return true } } const permission = { state: { routers: staticRoutes, addRouters: [] }, mutations: { // 將匹配成功的權限路由拼接到公共路由中 SET_ROUTERS (state, routers) { state.addRouters = routers state.routers = staticRoutes.concat(routers) } }, actions: { // 對后台返回來的權限和動態路由權限匹配 GenerateRoutes ({ commit }, data) { // 返回一個異步回調promise return new Promise((resolve, reject) => { // 遍歷權限路由數組 const accessedRoutes = asyncRouteMap.filter(v => { // 判斷如果后台返回的權限中包含admin就是管理員,可以進入權限路由頁面 if (data.indexOf('admin') >= 0) return true // 之后就是調用hasPerMission函數對象權限動態路由和后台返回的用戶權限進行嚴格匹配 if (hasPerMission(data, v)) { // 判斷是否有權限路由是否有子路由,有子路由繼續遍歷 if (v.children && v.children.length > 0) { v.children = v.children.filter(child => { // 對權限子路由和后台返回的用戶權限數據,在進行匹配,匹配成功返回 if (hasPerMission(data, child)) { return child } // 失敗返回false return false }) // 並且要把權限的父路由返回來,不光要把權限子路由返回,無論權限子路有還是沒有,都應該把權限父路由返回來 return v } else { // 權限父路由匹配成功返回 return v } } // 如果每一個判斷都沒有進,說明該用戶沒有任何權限,返回false return false }) commit('SET_ROUTERS', accessedRoutes) resolve() }) } }, getters: { // 只要權限路由數組發生變化就重新計算 addRouters (state) { return state.addRouters } } } export default permission // 在store/index.js // 引入另一個模塊使用 import permission from './permission' modules: { // 我們還需要用到另一個模塊配合完成 permission } // 之后創建一個js文件單獨放到全局路由導航,之后在main.js中引入執行 // gloab/index.js // 先把路由和vuex引進來使用 import router from '../router' import store from '../store' const whiteList = ['/login'] // 不重定向白名單 router.beforeEach((to, from, next) => { if (store.getters.token) { // 判斷如果是去登陸頁面的話,返回主頁,不讓他返回登錄頁 if (to.path === '/login') { next({ path: '/home' }) } else { // 否則判斷一下用戶的個人信息是否已經拉取完畢 if (store.getters.roles.length === 0) { // 拉取用戶個人信息 store.dispatch('getUserInfo').then(res => { // 拿到用戶后台返回的權限數據 const roles = res.role // 調用 permission.js方法中的GenerateRoutes方法,將后台返回的用戶的權限數據,傳遞回去進行篩選處理 store.dispatch('GenerateRoutes', roles).then(() => { // 生成可訪問的路由表 // 將篩選的權限路由數組動態掛載 router.addRoutes(store.getters.addRouters) // 動態添加可訪問路由表 next({ ...to, replace: true }) // hack方法 確保addRoutes添加完成 }) }).catch(error => { console.log(error) // 驗證失敗重新登陸 next({ path: '/login' }) }) } else { next() // 當有用戶權限的時候,說明所有可訪問路由已生成 如訪問沒權限的頁面會自動進入404頁面 } } } else { // 如果已經去了登陸頁面了,就不需要再next到登陸頁面了,這就是重定向白名單 if (whiteList.indexOf(to.path) !== -1) { next() } else { next('/login') // 否則全部重定向到登錄頁 } } })