从 0 实现一个 vue3 的权限指令 v-permission
在开发过程中会经常遇到一些权限控制,比如路由的权限控制、按钮的权限控制 🤔
这里利用 vue 的自定义指令功能,实现一个按钮级别的权限控制指令 💡
基础版本的权限控制
假设我们开发一个掘金,里面有文章编辑、发布、删除等功能,但不是每一个用户都可以有删除权限,除了用 v-if/v-show
外,可以用一个简单的权限控制指令实现
// permission.js
export const permission = {
mounted(el, binding) {
const { value } = binding
const currentUser = getCurrentUser() // 获取当前用户的信息
if (!currentUser.roles.includes(value)) {
el.style.display = 'none'
}
}
}
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { permission } from './permission'
const app = createApp(App)
app.directive('permission', permission)
app.mount('#app')
使用:
<button v-permission="'admin'">删除文章</button>
上面的内容实现了一个基础版本的角色权限功能,比如 admin
用户才能进行删除文章操作
假设现在需求是需要多个角色权限控制,比如管理员及文章所属的用户?
多角色支持
上面实现的功能比较简单,遇到多角色的场景就不太行了,继续扩展下,让我们的指令支持多个角色👻
// permission.js
export const permission = {
mounted(el, binding) {
const { value } = binding
const currentUser = getCurrentUser()
if (Array.isArray(value)) {
if (!value.some(role => currentUser.roles.includes(role))) {
el.style.display = 'none'
}
} else if (typeof value === 'string') {
if (!currentUser.roles.includes(value)) {
el.style.display = 'none'
}
}
}
}
现在可以这样用:
<button v-permission="['admin', 'editor']">编辑文章</button>
这样管理员跟文章所属的用户就可以对文章进行编辑处理
使用修饰符
继续扩展一下,使用一些修饰符实现更加灵活控制指令,比如删除 remove
、隐藏 hidden
、禁用 disable
按钮
// permission.js
export const permission = {
mounted(el, binding) {
const { value, modifiers } = binding
const currentUser = getCurrentUser()
let hasPermission = Array.isArray(value)
? value.some(role => currentUser.roles.includes(role))
: currentUser.roles.includes(value)
if (!hasPermission) {
if (modifiers.remove) {
el.parentElement && el.parentElement.removeChild(el)
} else if (modifiers.disable) {
el.disabled = true
el.classList.add('disabled')
} else {
el.style.display = 'none'
}
}
}
}
现在默认还是隐藏元素,但添加了两个修饰符
.remove
直接从 DOM 中删除元素.disable
禁用元素
<button v-permission.remove="'superadmin'">删除所有用户数据</button>
<button v-permission.disable="'editor'">发布文章</button>
假设是 superadmin
角色可以 删除所有用户数据
,同时这个指令如果不是该角色的话,是直接从 DOM 中 删除 remove
的
对于 editor
角色,能发布文章,如果不是该角色的话,该按钮则处于 禁用 disable
状态
添加更多的权限控制
在上面的基础上,添加更多功能,比如复杂权限检查(如果是 Object 类型,这里待定)、使用函数进行动态权限检查、错误处理及日志记录
import { getCurrentUser } from './userService' // 获取当前用户的信息
// import { checkComplexPermission } from './permissionService' // 复杂的权限控制
import { logger } from './logger' // 记录日志
export const permission = {
mounted(el, binding, vnode) {
updateElementVisibility(el, binding, vnode)
},
updated(el, binding, vnode) {
updateElementVisibility(el, binding, vnode)
}
}
function updateElementVisibility(el, binding, vnode) {
try {
const { value, modifiers } = binding
const currentUser = getCurrentUser()
let hasPermission = false
// 根据不同类型的权限值进行检查
if (typeof value === 'function') {
hasPermission = value(currentUser)
} else if (Array.isArray(value)) {
hasPermission = value.some(role => currentUser.roles.includes(role))
} else if (typeof value === 'string') {
hasPermission = currentUser.roles.includes(value)
}
// else if (typeof value === 'object' && value !== null) {
// // 如果是对象,使用复杂权限检查服务
// hasPermission = checkComplexPermission(currentUser, value)
// }
else {
throw new Error(`Invalid value for v-permission directive: ${value}`)
}
// 根据权限检查结果和修饰符来处理元素
if (!hasPermission) {
if (modifiers.remove) {
// 如果使用 remove 修饰符,则从 DOM 中移除元素
el.parentElement && el.parentElement.removeChild(el)
} else if (modifiers.disable) {
// 如果使用 disable 修饰符,则禁用元素
el.disabled = true
el.classList.add('disabled')
} else {
// 默认行为是隐藏元素
el.style.display = 'none'
}
} else {
// 如果有权限,确保元素可见且启用
el.style.display = ''
el.disabled = false
el.classList.remove('disabled')
}
// 记录权限检查结果
logger.info(`Permission check for ${vnode.type.name || 'element'}: ${hasPermission ? 'granted' : 'denied'}`)
} catch (error) {
// 错误处理和日志记录
console.error('Error in permission directive:', error)
logger.error('Permission directive error', { error: error.message, element: vnode.type.name })
}
}
使用:
<template>
<!-- 基本角色检查 -->
<button v-permission="'admin'">管理系统设置</button>
<!-- 多角色检查 -->
<button v-permission="['manager', 'hr']">查看员工报告</button>
<!-- 使用修饰符 -->
<button v-permission.remove="'owner'">删除公司数据</button>
<button v-permission.disable="'editor'">发布文章</button>
<!-- 复杂权限检查 -->
<!-- <button v-permission="{ action: 'approve', resource: 'expense' }">批准报销</button> -->
<!-- 使用函数进行动态权限检查 -->
<button v-permission="(user) => user.experience > 5">访问高级功能</button>
</template>
上面的代码中实现了以下功能:
- 使用
logger
进行日志记录(这里面需要实现一下) - 在
mounted
和updated
钩子中都调用updateElementVisibility
,确保在元素首次渲染和数据更新时都进行权限检查 - 对字符串、数组、函数类型进行权限检测(这里面可以扩充一下对象类型,实现更复杂的权限校验)
- 根据修饰符决定无权限的 DOM 的处理方式是
删除 remove
、禁用 disable
还是隐藏 hidden
完结,撒花🎉🎉🎉