權限問題:用戶和管理員進入管理系統看到的模塊是不一樣的,管理員看的的要比用戶看到的多。需要用到動態加載路由,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') // 否則全部重定向到登錄頁
}
}
})

