vue-admin-template角色許可權設定(2)
阿新 • • 發佈:2022-05-19
在vue-admin-template角色許可權設定(1)中提到了如何根據使用者角色進行訪問控制
但在專案實際使用過程中發現了一些存在的問題,下面主要針對這些存在的問題進行修改和完善
1 頁面重新整理後發現頭像等個人資訊消失
- 原因:在頁面登入成功後,專案是通過getInfo方法獲取個人資訊儲存在vuex中使用的,但是在頁面重新整理後,vuex中的資料會丟失。這是因為js程式碼是執行在記憶體中的,程式碼執行時的所有變數、函式也都是儲存在記憶體中的。進行重新整理頁面的操作,以前申請的記憶體被釋放,重新載入指令碼程式碼,變數重新賦值,所以這些資料要想儲存就必須儲存在外部。
- 解決方法:獲取資料後在Storage中儲存一份,這裡採用localStorage
- 需要注意的是:儘量不要在localStorage中儲存過大的資料,否則會影響頁面載入效能。
import { login, logout, getInfo } from '@/api/user' import { getToken, setToken, removeToken } from '@/utils/auth' import { resetRouter } from '@/router' const getDefaultState = () => { return { // 利用localStorage保證重新整理後仍存在 token: getToken(), name: localStorage.getItem('name') ? JSON.parse(localStorage.getItem('name')) : '', avatar: localStorage.getItem('avatar') ? JSON.parse(localStorage.getItem('avatar')) : '', roles: localStorage.getItem('roles') ? JSON.parse(localStorage.getItem('roles')) : [] } } const state = getDefaultState() const mutations = { RESET_STATE: (state) => { Object.assign(state, getDefaultState()) }, SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { state.name = name }, SET_ROLES: (state, roles) => { state.roles = roles }, SET_AVATAR: (state, avatar) => { state.avatar = avatar } } const actions = { // user login login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => { const { data } = response commit('SET_TOKEN', data.token) setToken(data.token) resolve() }).catch(error => { reject(error) }) }) }, // get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo(state.token).then(response => { const { data } = response; if (!data) { return reject('驗證失敗,請重新登入!') } const { name, roles, avatar } = data // 將個人資訊備份在localStorage中 localStorage.setItem('roles', JSON.stringify(roles)) localStorage.setItem('roles', JSON.stringify(roles)) localStorage.setItem('avatar', JSON.stringify(avatar)) if (!roles || roles.length <= 0) { reject('您不是有效的身份!') } // 將頁面級的操作許可權存在localStorage裡 if (roles.includes('店長')) { localStorage.setItem('permissions', JSON.stringify(['add', 'editAndDelete'])); } else if (roles.includes('收銀員') || roles.includes('技師') || roles.includes('髮型師')) { localStorage.setItem('permissions', JSON.stringify([])); } commit('SET_NAME', name) commit('SET_ROLES', roles) commit('SET_AVATAR', avatar) resolve(data) }).catch(error => { reject(error) }) }) }, // user logout logout({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { removeToken() // must remove token first resetRouter() // 記得清除 localStorage.removeItem('permissions') localStorage.removeItem('name') localStorage.removeItem('roles') localStorage.removeItem('avatar') commit('RESET_STATE') resolve() }).catch(error => { reject(error) }) }) }, // remove token resetToken({ commit }) { return new Promise(resolve => { removeToken() // must remove token first // 記得清除 localStorage.removeItem('permissions') localStorage.removeItem('name') localStorage.removeItem('roles') localStorage.removeItem('avatar') commit('RESET_STATE') resolve() }) } } export default { namespaced: true, state, mutations, actions }
這樣在重新整理之後vuex中的值仍存在~
2 頁面重新整理後動態路由失效跳轉/404頁面的問題
- 原因:一方面是重新整理後相應的vuex存在的值失效,另一方面是即使值仍有效,但是addRoutes動態新增路由還沒完成,頁面就從靜態找了,導致一直跳轉404。
- 解決方法:可以把routes存在localStorage中,這裡因為之前把roles持久了,可以直接在路由前置守衛中對重新整理這種情況進行特殊處理,主要思想是當動態路由表新增進去路由以後,再將404路由也動態新增進去!
- 需要注意的是:多次呼叫router.addRoutes方法,會重複新增路由並警告路由名字重複,可以重新封裝一個方法。
- 在router/index.js中重新封裝addRoutes方法
// 解決路由命名重複的問題
router.$addRoutes = params => {
router.matcher = new Router({
routes: router.options.routes
// 關鍵程式碼
}).matcher;
router.addRoutes(params);
};
- 刪除router/index.js靜態路由中的用*來找404
{ path: '*', redirect: '/404', hidden: true }
- 在src/permission.js中對重新整理情況單獨處理
router.beforeEach(async (to, from, next) => {
// start progress bar
NProgress.start()
// set page title
document.title = getPageTitle(to.meta.title)
// determine whether the user has logged in
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
// if is logged in, redirect to the home page
next({ path: '/' })
NProgress.done()
} else {
// 在addRoutes()之後第一次訪問被新增的路由會白屏,這是因為剛剛addRoutes()就立刻訪問被新增的路由
// 然而此時addRoutes()沒有執行結束,因而找不到剛剛被新增的路由導致白屏。因此需要從新訪問一次路由才行。
// 此時就要使用next({ …to, replace: true })來確保addRoutes()時動態新增的路由已經被完全載入上去。
// next({ …to, replace: true })中的replace: true只是一個設定資訊,告訴VUE本次操作後,不能通過瀏覽器後退按鈕,返回前一個路由。
// 通過getInfo判斷使用者是否獲得了許可權角色
const hasRoles = store.getters.roles && store.getters.roles.length > 0;
if (hasRoles) {
// 如果是重新整理
if (store.state.permission.routes.length == 0) {
// 根據角色生成可訪問的路由
const accessRoutess = await store.dispatch('permission/generateRoutes', store.getters.roles);
// 動態新增路由
router.$addRoutes(accessRoutess);
// 404頁面重定向必須放在最後面!否則一重新整理和輸入地址 就出問題跳404頁面!
// 因為addRoutes動態新增路由還沒完成,頁面就從靜態找了。
// 解決:當動態路由表新增進去路由以後,再將404路由也動態新增進去!
router.options.routes.push({ path: '*', redirect: '/404', hidden: true })
console.log(router.options.routes)
next({ ...to, replace: true });
} else {
next()
}
} else {
try {
// get user info
// 注意:roles必須是陣列形式,如['admin'] or ,['developer','editor']
const { roles } = await store.dispatch('user/getInfo')
// 根據角色生成可訪問的路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles);
// 動態新增路由
router.$addRoutes(accessRoutes);
// 404頁面重定向必須放在最後面!否則一重新整理和輸入地址 就出問題跳404頁面!
// 因為addRoutes動態新增路由還沒完成,頁面就從靜態找了。
// 解決:當動態路由表新增進去路由以後,再將404路由也動態新增進去!
router.options.routes.push({ path: '*', redirect: '/404', hidden: true })
// 確保addRoutes完成
// 設定替換為true 這樣導航不會留下歷史記錄
next({ ...to, replace: true });
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next()
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
至此再刷新發現沒有問題啦!!The end.