背景:
最近 vue3 项目要做按钮权限控制,于是综合利弊决定采用自定义指令方式实现。项目架构vue3 + pinia + pinia-plugin-persist(持久化)。
思路:
登录的时候请求按钮权限列表,同时用pinia做持久化存储,刷新页面时不再重复请求,退出登录时清除按钮权限缓存。
难点:
1.可能需要动态更改按钮DOM的权限值
2.动态更改当前用户的按钮权限数据,如重新请求按钮权限数据
解决方案:增加一个updated钩子,并用watchEffect收集按钮权限数据依赖。
具体代码:
src/stores/use-permission.ts
import { defineStore } from 'pinia'
interface usePermission {
buttonPermission: string[]
}
export const usePermissionStore = defineStore('permission', {
state: (): usePermission => {
return {
buttonPermission: []
}
},
actions: {
getButtonPermission() {
// 请求按钮权限TODO
this.buttonPermission = ['aaa', 'bbb', 'ccc']
},
clearButtonPermission() {
this.buttonPermission = []
globalThis.localStorage.removeItem('buttonPermission')
},
// 额外提供是否存在权限的方法(有些地方指令不能用,只能用if判断)
hasPermission(code: string) {
if (!code) return true
return this.buttonPermission.includes(code)
}
},
// 添加持久化选项
persist: {
enabled: true, // 启用持久化
strategies: [
{
key: 'buttonPermission', // 本地存储的键
storage: localStorage // 使用localStorage存储
}
]
}
})
src/directives/permission.ts
import { watchEffect, type DirectiveBinding } from 'vue'
import { usePermissionStore } from '~/stores/use-permission'
const hasPermission = (value: string) => {
const permissionStore = usePermissionStore()
return permissionStore.buttonPermission.includes(value)
}
const removeEl = (el: Record<string, any>) => {
el._parentNode = el.parentNode
el._placeholderNode = document.createComment('auth')
el.parentNode?.replaceChild(el._placeholderNode, el)
}
const addEl = (el: Record<string, any>) => {
el._parentNode?.replaceChild(el, el._placeholderNode)
}
function mounted(el: Record<string, any>, binding: DirectiveBinding<any>) {
const value = binding.value
const flag = hasPermission(value)
el._oldHasPermission = !value ? true : flag;
if (!value) return
if (!flag) {
removeEl(el)
}
}
function updated(el: Record<string, any>, binding: DirectiveBinding<any>) {
el._binding = binding
const update = () => {
const oldHasPermission = el._oldHasPermission
const newHasPermission = hasPermission(el._binding.value)
if (oldHasPermission === newHasPermission) return
if (newHasPermission) {
addEl(el)
} else {
removeEl(el)
}
el._oldHasPermission = newHasPermission
}
if (el._watchEffect) {
update()
} else {
el._watchEffect = watchEffect(() => {
update()
})
}
}
export default {
mounted,
updated
}
src/directives/index.ts
import type { Directive, App } from 'vue'
import permission from '~/directives/permission'
const directives = {
permission
}
const registerDirectives = (app: App<Element>) => {
Object.keys(directives).forEach((key) => {
app.directive(key, (directives as Record<string, Directive>)[key])
})
}
export default registerDirectives
然后再入口main.ts引入自定义指令:registerDirectives(app)
补充:
有些特殊权限控制是数组,请自行将入参code值兼容数组类型并判断是否有权限。