這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
最近的面試中有一個面試官問我按鈕級別的權限怎么控制,我說直接v-if啊,他說不夠好,我說我們專案中按鈕級別的權限控制情況不多,所以v-if就夠了,他說不夠通用,最后他對我的評價是做過很多東西,但是都不夠深入,好吧,那今天我們就來深入深入,
因為我自己沒有相關實踐,所以接下來就從這個有16.2k星星的后臺管理系統專案Vue vben admin中看看它是如何做的,
獲取權限碼
要做權限控制,肯定需要一個code,無論是權限碼還是角色碼都可以,一般后端會一次性回傳,然后全域存盤起來就可以了,Vue vben admin是在登錄成功以后獲取并保存到全域的store中:
import { defineStore } from 'pinia';
export const usePermissionStore = defineStore({
state: () => ({
// 權限代碼串列
permCodeList: [],
}),
getters: {
// 獲取
getPermCodeList(){
return this.permCodeList;
},
},
actions: {
// 存盤
setPermCodeList(codeList) {
this.permCodeList = codeList;
},
// 請求權限碼
async changePermissionCode() {
const codeList = await getPermCode();
this.setPermCodeList(codeList);
}
}
})
接下來它提供了三種按鈕級別的權限控制方式,一一來看,
函式方式
使用示例如下:
<template>
<a-button v-if="hasPermission(['20000', '2000010'])" color="error" >
擁有[20000,2000010]code可見
</a-button>
</template>
<script lang="ts">
import { usePermission } from '/@/hooks/web/usePermission';
export default defineComponent({
setup() {
const { hasPermission } = usePermission();
return { hasPermission };
},
});
</script>
本質上就是通過v-if,只不過是通過一個統一的權限判斷方法hasPermission:
export function usePermission() {
function hasPermission(value, def = true) {
// 默認視為有權限
if (!value) {
return def;
}
const allCodeList = permissionStore.getPermCodeList;
if (!isArray(value)) {
return allCodeList.includes(value);
}
// intersection是lodash提供的一個方法,用于回傳一個所有給定陣列都存在的元素組成的陣列
return (intersection(value, allCodeList)).length > 0;
return true;
}
}
很簡單,從全域store中獲取當前用戶的權限碼串列,然后判斷其中是否存在當前按鈕需要的權限碼,如果有多個權限碼,只要滿足其中一個就可以,
組件方式
除了通過函式方式使用,也可以使用組件方式,Vue vben admin提供了一個Authority組件,使用示例如下:
<template>
<div>
<Authority :value="https://www.cnblogs.com/smileZAZ/archive/2023/05/24/RoleEnum.ADMIN">
<a-button type="primary" block> 只有admin角色可見 </a-button>
</Authority>
</div>
</template>
<script>
import { Authority } from '/@/components/Authority';
import { defineComponent } from 'vue';
export default defineComponent({
components: { Authority },
});
</script>
使用Authority包裹需要權限控制的按鈕即可,該按鈕需要的權限碼通過value屬性傳入,接下來看看Authority組件的實作,
<script lang="ts">
import { defineComponent } from 'vue';
import { usePermission } from '/@/hooks/web/usePermission';
import { getSlot } from '/@/utils/helper/tsxHelper';
export default defineComponent({
name: 'Authority',
props: {
value: {
type: [Number, Array, String],
default: '',
},
},
setup(props, { slots }) {
const { hasPermission } = usePermission();
function renderAuth() {
const { value } = props;
if (!value) {
return getSlot(slots);
}
return hasPermission(value) ? getSlot(slots) : null;
}
return () => {
return renderAuth();
};
},
});
</script>
同樣還是使用hasPermission方法,如果當前用戶存在按鈕需要的權限碼時就原封不動渲染Authority包裹的內容,否則就啥也不渲染,
指令方式
最后一種就是指令方式,使用示例如下:
<a-button v-auth="'1000'" type="primary" > 擁有code ['1000']權限可見 </a-button>
實作如下:
import { usePermission } from '/@/hooks/web/usePermission';
function isAuth(el, binding) {
const { hasPermission } = usePermission();
const value = https://www.cnblogs.com/smileZAZ/archive/2023/05/24/binding.value;
if (!value) return;
if (!hasPermission(value)) {
el.parentNode?.removeChild(el);
}
}
const mounted = (el, binding) => {
isAuth(el, binding);
};
const authDirective = {
// 在系結元素的父組件
// 及他自己的所有子節點都掛載完成后呼叫
mounted,
};
// 注冊全域指令
export function setupPermissionDirective(app) {
app.directive('auth', authDirective);
}
只定義了一個mounted鉤子,也就是在系結元素掛載后呼叫,依舊是使用hasPermission方法,判斷當前用戶是否存在通過指令插入的按鈕需要的權限碼,如果不存在,直接移除系結的元素,
很明顯,Vue vben admin的實作有兩個問題,一是不能動態更改按鈕的權限,二是動態更改當前用戶的權限也不會生效,
解決第一個問題很簡單,因為上述只有洗掉元素的邏輯,沒有加回來的邏輯,那么增加一個updated鉤子:
app.directive("auth", {
mounted: (el, binding) => {
const value = https://www.cnblogs.com/smileZAZ/archive/2023/05/24/binding.value
if (!value) return
if (!hasPermission(value)) {
// 掛載的時候沒有權限把元素洗掉
removeEl(el)
}
},
updated(el, binding) {
// 按鈕權限碼沒有變化,不做處理
if (binding.value === binding.oldValue) return
// 判斷用戶本次和上次權限狀態是否一樣,一樣也不用做處理
let oldHasPermission = hasPermission(binding.oldValue)
let newHasPermission = hasPermission(binding.value)
if (oldHasPermission === newHasPermission) return
// 如果變成有權限,那么把元素添加回來
if (newHasPermission) {
addEl(el)
} else {
// 如果變成沒有權限,則把元素洗掉
removeEl(el)
}
},
})
const hasPermission = (value) => {
return [1, 2, 3].includes(value)
}
const removeEl = (el) => {
// 在系結元素上存盤父級元素
el._parentNode = el.parentNode
// 在系結元素上存盤一個注釋節點
el._placeholderNode = document.createComment("auth")
// 使用注釋節點來占位
el.parentNode?.replaceChild(el._placeholderNode, el)
}
const addEl = (el) => {
// 替換掉給自己占位的注釋節點
el._parentNode?.replaceChild(el, el._placeholderNode)
}
主要就是要把父節點保存起來,不然想再添加回去的時候獲取不到原來的父節點,另外洗掉的時候創建一個注釋節點給自己占位,這樣下次想要回去能知道自己原來在哪,
第二個問題的原因是修改了用戶權限資料,但是不會觸發按鈕的重新渲染,那么我們就需要想辦法能讓它觸發,這個可以使用watchEffect方法,我們可以在updated鉤子里通過這個方法將用戶權限資料和按鈕的更新方法關聯起來,這樣當用戶權限資料改變了,可以自動觸發按鈕的重新渲染:
import { createApp, reactive, watchEffect } from "vue"
const codeList = reactive([1, 2, 3])
const hasPermission = (value) => {
return codeList.includes(value)
}
app.directive("auth", {
updated(el, binding) {
let update = () => {
let valueNotChange = binding.value =https://www.cnblogs.com/smileZAZ/archive/2023/05/24/== binding.oldValue
let oldHasPermission = hasPermission(binding.oldValue)
let newHasPermission = hasPermission(binding.value)
let permissionNotChange = oldHasPermission === newHasPermission
if (valueNotChange && permissionNotChange) return
if (newHasPermission) {
addEl(el)
} else {
removeEl(el)
}
};
if (el._watchEffect) {
update()
} else {
el._watchEffect = watchEffect(() => {
update()
})
}
},
})
將updated鉤子里更新的邏輯提取成一個update方法,然后第一次更新在watchEffect中執行,這樣用戶權限的回應式資料就可以和update方法關聯起來,后續用戶權限資料改變了,可以自動觸發update方法的重新運行,
本文轉載于:
https://juejin.cn/post/7209648356530896953
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/553342.html
標籤:其他
上一篇:Velocity 不用愁!Velocity 系統的前端工程化之路
下一篇:返回列表

