Vue 新手學習筆記:vue-element-admin 之登陸及目錄權限控制


登陸
萬事開頭難,做什么事都要有個起點,后面才能更好的進行下去,因此我選擇的起點就是最為直觀的登陸頁面 /login/index.vue

/src/views/login/index
去除那些無關的東西,比如什么 rules 校驗啊,默認的賬號密碼之類的東西,直接看核心登陸方法 handleLogin

handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
# 請求 store 中的方法
this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
this.loading = false
this.$router.push({ path: this.redirect || '/' })
}).catch((errorMsg) => {
this.loading = false
this.$message({
type: 'error',
message: errorMsg
})
})
} else {
console.log('error submit!!')
return false
}
})
},
store 主要事做一些緩存之類的處理,這里調用了 store 中的 LoginByUsername 把表單內容傳過去,這里我就改了點 catch 把我自己需要的錯誤信息進行提示,其他東西不需要改動

/src/store/modules/user
上面登陸中請求的 LoginByUsername 正是在這

// 用戶名登錄
LoginByUsername({ commit }, userInfo) {
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
// 請求后台登陸
loginByUsername(username, userInfo.password).then(response => {
const data = response.data
if (response.data.errorCode !== 200) {
// 登陸失敗,回傳提示信息
reject(data.errorMsg)
}
// 設置 token,作為用戶已登陸的前端標識,存在 cookie 中
commit('SET_TOKEN', data.retData)
setToken(data.retData)
resolve()
}).catch(error => {
reject(error)
})
})
},
setToken() 方法會把 token 保存到 cookie 里,很重要

下面有個 GetUserInfo 方法,在你登陸的時候會去獲取你的權限數據

// 獲取用戶信息
GetUserInfo({ commit, state }) {
return new Promise((resolve, reject) => {
// 請求獲取權限
getUserInfo(state.token).then(response => {
if (response.status !== 200) { // 由於mockjs 不支持自定義狀態碼只能這樣hack
reject('error')
}
const data = response.data

if (data.retData.module && data.retData.module.length > 0) { // 驗證返回的roles是否是一個非空數組
commit('SET_ROLES', data.retData.module)
} else {
// 當用戶無任何權限時設置
commit('SET_ROLES', ['普通用戶'])
}

commit('SET_NAME', data.retData.username)
commit('SET_AVATAR', 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif')
commit('SET_INTRODUCTION', data.retData.username)
resolve(response)
}).catch(error => {
reject(error)
})
})
},
不需要知道那個 new Promise 啥的干啥用,反正我不知道,只要知道 getUserInfo 這個方法就行了,這個方法會以上面之前保存的 token 為參數去請求獲取你的用戶權限,原邏輯是沒有權限就跳登陸頁面,我這系統需要,沒權限也有個首頁可看,所以
SET_ROLES 參數給了個“普通用戶”,反正什么值無所謂有值,沒權限就行。
下面 commit 里的參數,分別為,用戶名,頭像,介紹,保留就好,也可自定義。
PS:在每次頁面跳轉時候頁面都會走一個 GetUserInfo ,以此來判斷用戶有沒有失效,因此,我的做法是直接把用戶信息包括權限保存在 redis 里取,不要每次都從庫里查

/src/api/login
接口的請求都會從這里去請求后台,就不多贅述,基本只需要改動下請求路徑就ok了。
登出 logout 也是,請求保證后台注銷,前端處理部分也是在 store/modules/user 里的 LogOut 方法,基本不需要改動。

 

權限
大多數系統都有根據用戶權限,或者說角色,展示相應頁面的要求

/src/router/index
結合框架來實現目錄的控制,如果是傳統項目,一般是把目錄結構整個存在后台數據庫里,然后前端循環展示出來,但是這個在這里不需要這么麻煩,只需要把每個頁面對應的權限保存起來就行了

目錄在分為兩個部分 constantRouterMap 與 asyncRouterMap
constantRouterMap:主要是通用部分,每個用戶都有的頁面
asyncRouterMap:需要進行權限過濾的頁面

所以像首頁這種都丟在 constantRouterMap 里,而像權限管理頁面這種放在 asyncRouterMap 里
在目錄上加上 permission 標識,這個是我自定義的唯一標識,用於區分每個頁面的權限,我這里直接以頁面的路徑為值,因為路徑肯定唯一

{
path: '',
component: Layout,
alwaysShow: true,
redirect: '/xxx/xxx',
name: 'xxx',
meta: {
title: 'xxx',
icon: 'xxx'
},
children: [
{
path: '/xxx/xxx',
component: () => import('@/views/xxx/xxx'),
name: 'xxx',
meta: {
title: 'xxx',
icon: 'message',
# 權限標識,每個目錄唯一
permission: '/xxx/xxx',
noCache: false }
}
]
},
然后就只需要在加載目錄時對目錄進行判斷就可以了

/src/permission
這個文件就是整個路由邏輯在這

 

從這里的邏輯可以看到,登陸后,現在判斷了用戶權限,如果沒權限就會進入之前說到的 GetUserInfo 方法去獲取權限,由於要對目錄進行控制,所以在 GetUserInfo 里我們也需要獲取到目錄的權限列表,只需要獲取到有的就行了,沒有權限的目錄就不需要獲取。
在 GetUserInfo 的最后通過 resolve 方法把返回值返回這個頁面,截圖中  module 就是我獲取到的有權限的目錄列表,然后通過
GenerateRoutes 來生成要加載的目錄,接下來就是改這了

/src/store/modules/permission
找到 GenerateRoutes

const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { roles } = data
// 權限對列表進行過濾
const accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
commit('SET_ROUTERS', accessedRouters)
resolve(http://www.my516.com)
})
}
}
}
對於通用目錄我們不需要管,在原邏輯中,這里通過判斷如果用戶是管理員就直接把整個權限目錄都加載,如果不是再進行過濾,因為我們這完全就通過后台控制,包括管理員所以就把判斷去掉了,直接對目錄進行過濾

找到 filterAsyncRouter 方法

/**
* 遞歸過濾異步路由表,返回符合用戶角色權限的路由表
* @param routes asyncRouterMap
* @param roles
*/
function filterAsyncRouter(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
// 是否存在子節點,存在子節點說明當前節點為展開欄,並非頁面
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, roles)
// forCheck() 方法遞歸判斷該節點下是否存在頁面,不存在則隱藏
// true:不存在,false:存在
tmp.hidden = forCheck(tmp.children)
}
res.push(tmp)
}
})

return res
}

/**
* 遞歸子節點,判斷是否存在展示頁面,存在返回 false,不存在返回 true
* @param routes
*/
function forCheck(routes) {
// 設置默認為隱藏
let isHidden = true
// 判斷是否存頁面,不存在說明該節點下不存在頁面
if (routes.length > 0) {
// 循環子目錄,如果子目錄中不存在需要權限頁面
// 說明子頁面全是展開欄,隱藏
for (const route of routes) {
// 存在 permission 說明為頁面,不存在說明為展開欄,將子頁面列表繼續遞歸
if (route.meta && route.meta.permission) {
isHidden = false
return
} else {
isHidden = forCheck(route.children)
}
}
}
return isHidden
}
通過循環權限目錄通過 hasPermission 方法進行判斷
forCheck 是我自己封裝的方法,因為項目中不只存在二級目錄,所以,通過遞歸的方式,判斷子節點下是否有展示頁,也就是帶 permission 的頁面,如果有,返回 false,沒有返回 true

/**
* 通過meta.role判斷是否與當前用戶權限匹配
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.permission) {
return roles.some(role => route.meta.permission.includes(role.permission))
} else {
return true
}
}
hasPermission 方法,這里,我們之前加的 permission 參數就起到作用了
如果有 permission 就進行判斷, roles 參數就是 /src/permission 里傳過來的 module 目錄。
通過 roles.some 循環 roles 別名 role,如果目錄里 includes(包含)這個目錄權限,就返回true,否則 false,為 true 的會被顯示,當然如果直接沒有 permission 就說明不需要后台管控,就直接 true,這樣目錄就被加載出來了。
---------------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM