1. 權限控制
1.1. 實現思想
基于rbac權限控制思想實現,給用戶分配角色,給角色分配權限
給用戶分配角色業務
注意:上方圖片是個示例圖,代表給用戶分配職位(角色),頁面中使用了Element-plus的el-? ? ? ? ?checkbox組件和el-checkbox-group組件
靜態結構(不完整)
<el-form-item label="用戶姓名"><el-input v-model="userParams.username" :disabled="true"></el-input></el-form-item><el-form-item label="職位列表"><el-checkbox>全選</el-checkbox><!-- 顯示職位的的復選框 --><el-checkbox-group><el-checkboxv-for="(role, index) in 10":key="index":label="index">{{ index }}</el-checkbox></el-checkbox-group></el-form-item>
獲取&&存儲數據
首先先獲取全部的職位(角色)的數據與當前用戶已有的職位(角色)的數據,分別進行保存
//收集頂部復選框全選數據
let checkAll = ref<boolean>(false)
//控制頂部全選復選框不確定的樣式
let isIndeterminate = ref<boolean>(true)
//存儲全部職位的數據
let allRole = ref<AllRole>([])
//當前用戶已有的職位
let userRole = ref<AllRole>([])
//分配角色按鈕的回調
const setRole = async (row: User) => {//存儲已有的用戶信息Object.assign(userParams, row)//獲取全部的職位的數據與當前用戶已有的職位的數據let result: AllRoleResponseData = await reqAllRole(userParams.id as number)if (result.code == 200) {//存儲全部的職位allRole.value = result.data.allRolesList//存儲當前用戶已有的職位userRole.value = result.data.assignRoles//抽屜顯示出來drawer1.value = true}
}
展示數據
<!-- 抽屜結構:用戶某一個已有的賬號進行職位分配 --><el-drawer v-model="drawer1"><template #header><h4>分配角色(職位)</h4></template><template #default><el-form><el-form-item label="用戶姓名"><el-input v-model="userParams.username" :disabled="true"></el-input></el-form-item><el-form-item label="職位列表"><el-checkbox@change="handleCheckAllChange"v-model="checkAll":indeterminate="isIndeterminate">全選</el-checkbox><!-- 顯示職位的的復選框 --><el-checkbox-groupv-model="userRole"@change="handleCheckedCitiesChange"><el-checkboxv-for="(role, index) in allRole":key="index":label="role">{{ role.roleName }}</el-checkbox></el-checkbox-group></el-form-item></el-form></template></el-drawer>
詳細解釋
全選部分:
@change:全選框點擊時的回調
v-model:綁定的數據,根據這個值決定是否全選(是一個布爾值)
:indeterminate:不確定狀態,既沒有全選也沒有全不選
?
復選框部分:
v-for="(role, index) in allRole"
:遍歷allRole。
:label="role"
:收集的數據(勾上的數據)
v-model="userRole"
:綁定收集的數據,也就是收集的數據存儲到userRole中。
@change:勾選變化時的回調(點擊每個復選框都會執行的回調)
?
全選框勾選的回調:
實現原理:函數會將勾選與否注入到val中,如果是,就將全部數據(allRole)賦值給選中的數據(userRole),選中的數據通過v-model實現頁面的同步變化
該函數會接收到一個布爾值,如果為真代表全選按鈕被勾選,在回調中需要將全部角色(職位)數據賦值給在el-checkbox-group使用v-model綁定的變量中(該變量表示被選中的數據)
//頂部的全部復選框的change事件
const handleCheckAllChange = (val: boolean) => {//val:true(全選)|false(沒有全選)userRole.value = val ? allRole.value : []//不確定的樣式(確定樣式)isIndeterminate.value = false
}
復選框
每勾選一個復選框就會執行該函數,并收集勾選的數據到數組中傳遞進該函數
在函數內部判斷數組的長度是否等于全部角色數據,如果等于需要將上方的權限按鈕勾上,因為已經使用v-model綁定了,所以直接給checkAll.value賦一個布爾值即可
//頂部全部的復選框的change事件
const handleCheckedCitiesChange = (value: string[]) => {//頂部復選框的勾選數據//代表:勾選上的項目個數與全部的職位個數相等,頂部的復選框勾選上checkAll.value = value.length === allRole.value.length//不確定的樣式isIndeterminate.value = value.length !== allRole.value.length
}
當點擊確定按鈕的回調
需要收集兩個參數:第一個:當前用戶的id標識,第二個:從收集好的userRole中使用map過濾出選中的角色的id標識(數組)
然后發起分配用戶角色的請求,請求成功,重新獲取更新完畢用戶的信息。
//確定按鈕的回調(分配職位)
const confirmClick = async () => {//收集參數let data: SetRoleData = {userId: userParams.id as number,roleIdList: userRole.value.map((item) => {return item.id as number}),}//分配用戶的職位let result: any = await reqSetUserRole(data)if (result.code == 200) {//提示信息ElMessage({ type: 'success', message: '分配職務成功' })//關閉抽屜drawer1.value = false//獲取更新完畢用戶的信息,更新完畢留在當前頁getHasUser(pageNo.value)}
}
?看到這里,相信你一定有收獲!!!
給角色分配權限業務
靜態結構
當點擊上圖中的分配權限按鈕時,會彈出一個抽屜組件(el-drawer),利用樹組件展示所有權限數據,并可以看到點擊的角色已有的權限數據(勾選的)
注意:上方圖片是個示例圖,代表給角色分配權限,頁面中使用了Element-plus的el-table和el-drawer,el-tree組件。
注意:因為這是在vue3和typescript項目中,所以進行了類型限制。
type這里MenuData與MenuList互相調用,適合這種樹狀的數據結構(type還可以定義多個復雜類型)
//菜單與按鈕數據的ts類型
export interface MenuData {id: numbercreateTime: stringupdateTime: stringpid: numbername: stringcode: stringtoCode: stringtype: numberstatus: nulllevel: numberchildren?: MenuListselect: boolean
}
export type MenuList = MenuData[]
分配權限按鈕
獲取&&存儲數據
根據角色id發送請求獲取全部角色權限數據(包含該角色已有的職位)
//準備一個數組:數組用于存儲勾選的節點的ID(四級的)
let selectArr = ref<number[]>([])
//已有的職位的數據
const setPermisstion = async (row: RoleData) => {//抽屜顯示出來drawer.value = true//收集當前要分類權限的職位的數據Object.assign(RoleParams, row)//根據職位獲取權限的數據let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)if (result.code == 200) {menuArr.value = result.data//下面這行代碼下面會具體講解,可以先往下看selectArr.value = filterSelectArr(menuArr.value, [])}
}
?使用樹組件展示全部權限數據(樹組件詳細使用說明)
我們重點關注el-tree組件,先介紹一些常用屬性和方法
const defaultProps = {//子樹為節點對象的childrenchildren: 'children',//節點標簽為節點對象的name屬性label: 'name',
}
常用屬性:?
1. data:展示的數據(數據源)
2. show-checkbox:節點是否可被選擇(點擊可以選中)
3. node-key:每個樹節點用來作為唯一標識的屬性,整棵樹應該是唯一的(如果樹中包含children子數據,該屬性不能省略)
4. default-expand-all:默認展開所有節點
5. default-checked-keys:默認勾選的節點的 key 的數組(是一個數組,數組中存放的就是上面node-key存放的唯一標識)
6. default-expanded-keys:默認展開的節點的 key 的數組(是一個數組,數組中存放的就是上面node-key存放的唯一標識)
7. current-node-key:當前選中的節點(可以是number或string類型)
8. props:接收一個對象,對象中可以包含以下兩個屬性(還可以包含其他屬性,這里只列舉了以下兩個)
label:指定節點標簽為節點對象的某個屬性值(就是代表了要在頁面中展示的節點名稱) ,children:指定子樹為節點對象的某個屬性值(就是代表去哪個字段下讀取數據當作子節點的數據)(注意:label和children這兩個屬性名是不變的,屬性值需要根據項目需要進行修改)
常用方法:
使用el-tree樹組件提供的方法時,需要先在el-tree組件標簽上利用ref打上標識(<el-tree ref="xxx"> </el-tree>),然后通過ref得到el-tree組件實例才能調用對應方法!
1. getCheckedKeys:如果在el-tree標簽上設置了show-checkbox屬性且被選中,通過樹組件實例.getCheckedKeys進行調用,它將返回當前選中節點key的數組(該數組由所有被選中的節點的id屬性組成【為什么是id屬性呢? ? ? ? ?因為:在el-tree標簽上設置了node-key="id"屬性。所以該方法會收集所有選中的節點對象的id屬性】)
?2. getHalfCheckedKeys:如果在el-tree標簽上設置了show-checkbox屬性且被選中,通過樹組件實例.getHalfCheckedKeys進行調用,它將返回當前半選中的節點的id屬性組成的數組
如遇這種情況該方法一般會和上面的getCheckedKeys配合使用
在樹組件中展示已分配的權限
為什么要使用遞歸函數?
因為不能直接根據1級,2級,3級的select屬性進行判斷,要判斷最內層的職位對象的select屬性是真是假,因為層級較深要判斷最內層的職位對象的select屬性,所以使用了遞歸函數
具體實現思路:
? ? 目的:要先對返回的全部權限數據進行過濾,找到最內層的職位對象,根據其select屬性判斷!
?? ?封裝一個遞歸函數,接收兩個參數,一個全部權限數據,一個空數組(用于存放滿足level為4級職位對象中的select屬性為真的職位的id),在遞歸函數?? ?中使用for Each遍歷全部權限數據,判斷item.select && item.level === 4如果滿足說明到最內層了,將當前這個對象的id屬性(item.id)push到空數組中,否則代表沒有到最內層,繼續判斷item.children && item.children.length > 0 滿足該條件遞歸調用?? ?filterSelectArr(item.children,initArr),最后返回這個過濾好的數組,交給el-tree組件的default-checked-keys屬性,那么就可以找出該角色已有的權限就可以在樹組件選中了
//分配權限按鈕的回調
//已有的職位的數據
const setPermisstion = async (row: RoleData) => {//抽屜顯示出來drawer.value = true//收集當前要分類權限的職位的數據Object.assign(RoleParams, row)//根據職位獲取權限的數據let result: MenuResponseData = await reqAllMenuList(RoleParams.id as number)if (result.code == 200) {menuArr.value = result.data/*最后返回這個過濾好的數組,交給el-tree組件的default-checked-keys屬性,那么就可以找出該角色已有的權限就可以在樹組件選中了*/selectArr.value = filterSelectArr(menuArr.value, [])}
}
// 遞歸函數
const filterSelectArr = (allData: any, initArr: any) => {allData.forEach((item: any) => {if (item.select && item.level == 4) {initArr.push(item.id)}if (item.children && item.children.length > 0) {//層層遞進,直到最內層的職位對象的level屬性===4filterSelectArr(item.children, initArr)}})//返回這個過濾好的數組return initArr
}
收集用戶分配的權限
我們這里收集主要用到了2個方法:getCheckedKeys、getHalfCheckedKeys。它們會返回已選擇以及半選擇用戶的id數組(這兩個方法上方都有詳細解釋)
//抽屜確定按鈕的回調
const handler = async () => {//職位(角色)的IDconst roleId = RoleParams.id as number//選中節點的ID getCheckedKeys方法會得到show-checkbox為true選中的全部節點對象的id組成的數組//為什么是能收集到id 因為el-tree配置了node-key="id"屬性let arr = tree.value.getCheckedKeys()//半選的IDlet arr1 = tree.value.getHalfCheckedKeys()let permissionId = arr.concat(arr1)//下發權限let result: any = await reqSetPermisstion(roleId, permissionId)if (result.code == 200) {//抽屜關閉drawer.value = false//提示信息ElMessage({ type: 'success', message: '分配權限成功' })//頁面刷新window.location.reload()}
}
繼續加油!
1.2. 實現深度
3級權限:模塊權限、頁面權限、按鈕權限
?