OfficeSystem/directives/permission.js

205 lines
7.0 KiB
JavaScript
Raw Normal View History

2025-11-26 17:56:24 +08:00
/**
* @file permission.js
* @description Vue权限指令实现用于根据用户权限控制DOM元素显示/隐藏
* 支持Vue 2和Vue 3提供v-permission指令进行灵活的权限控制
*/
2025-11-26 15:59:57 +08:00
import { useUserStore } from '@/store/user'
import { ADMIN_ROLES, normalizePermissions } from '@/utils/permission'
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
/**
* 解析所需权限列表
* 将指令值解析为标准权限数组
* @param {Array|string|null} value - 指令绑定的值
* @returns {Array} 解析后的权限数组
*/
2025-11-26 15:59:57 +08:00
const resolveRequiredPermissions = (value) => {
if (!value) return []
if (Array.isArray(value)) return value.filter(Boolean)
if (typeof value === 'string') {
return value
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
return []
2025-11-26 15:59:57 +08:00
}
2025-11-26 17:56:24 +08:00
/**
* 评估用户权限
* 检查用户是否拥有所需的权限
* @param {Object|null} userStore - 用户状态存储
* @param {Array|string} requiredPermissions - 需要验证的权限
* @param {Object} modifiers - 指令修饰符用于控制权限验证策略
* @returns {boolean} 用户是否拥有所需权限
*/
2025-11-26 15:59:57 +08:00
const evaluatePermission = (userStore, requiredPermissions, modifiers = {}) => {
2025-11-26 17:56:24 +08:00
// 用户存储不可用时,默认为无权限
if (!userStore) return false
2025-11-26 17:56:24 +08:00
// 解析权限验证策略支持requireAll和all两种修饰符
const {requireAll = modifiers.all} = modifiers
2025-11-26 17:56:24 +08:00
// 解析所需权限
const permissionsToCheck = resolveRequiredPermissions(requiredPermissions)
2025-11-26 17:56:24 +08:00
// 空权限列表表示不需要权限直接返回true
if (permissionsToCheck.length === 0) return true
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
// 获取用户信息
const userInfo = userStore.userInfo
const userRoles = userInfo?.roles || []
2025-11-26 17:56:24 +08:00
// 检查用户是否为管理员,管理员拥有所有权限
const isAdmin = Array.isArray(userRoles) && userRoles.some((role) => ADMIN_ROLES.includes(role))
if (isAdmin) return true
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
// 获取并规范化用户权限
const userPermissions = normalizePermissions(userInfo?.permissions)
2025-11-26 17:56:24 +08:00
// 用户无权限时返回false
if (userPermissions.length === 0) return false
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
// 根据验证策略进行权限检查
if (requireAll) {
2025-11-26 17:56:24 +08:00
// all修饰符用户必须拥有所有指定权限
return permissionsToCheck.every((permission) => userPermissions.includes(permission))
}
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
// 默认策略:用户只需拥有任一指定权限即可
return permissionsToCheck.some((permission) => userPermissions.includes(permission))
2025-11-26 15:59:57 +08:00
}
2025-11-26 17:56:24 +08:00
/**
* 获取用户状态存储
* 通过多种途径尝试获取Pinia实例和用户存储
* @param {Object} binding - 指令绑定对象
* @param {Object} vnode - 虚拟节点对象
* @returns {Object|null} 用户状态存储或null
*/
2025-11-26 15:59:57 +08:00
const getUserStore = (binding, vnode) => {
2025-11-26 17:56:24 +08:00
// 尝试从多个可能的路径获取Pinia实例
// 这是为了兼容不同的使用场景和组件层次结构
const piniaInstance =
binding?.instance?.proxy?.$pinia ||
binding?.instance?.appContext?.app?.$pinia ||
vnode?.context?.$pinia ||
binding?.instance?.appContext?.appContext?.provides?.pinia
try {
2025-11-26 17:56:24 +08:00
// 如果获取到Pinia实例使用它来创建userStore
if (piniaInstance) {
return useUserStore(piniaInstance)
}
2025-11-26 17:56:24 +08:00
// 否则尝试使用全局Pinia实例
return useUserStore()
} catch (error) {
console.warn('[v-permission] failed to access pinia instance:', error)
return null
2025-11-26 15:59:57 +08:00
}
}
2025-11-26 17:56:24 +08:00
/**
* 切换元素可见性
* 根据权限验证结果控制元素的显示或隐藏
* 保留原始的display样式以便需要时恢复
* @param {HTMLElement} el - DOM元素
* @param {boolean} shouldShow - 是否显示元素
*/
2025-11-26 15:59:57 +08:00
const toggleElementVisibility = (el, shouldShow) => {
if (shouldShow) {
2025-11-26 17:56:24 +08:00
// 显示元素恢复原始display样式或使用默认值
el.style.display = el.__vOriginalDisplay || ''
} else {
2025-11-26 17:56:24 +08:00
// 隐藏元素先保存当前display样式然后设置为none
if (el.style.display !== 'none') {
el.__vOriginalDisplay = el.style.display
}
el.style.display = 'none'
2025-11-26 15:59:57 +08:00
}
2025-11-26 17:56:24 +08:00
// 记录权限控制状态,用于后续更新
el.__vPermissionVisible = shouldShow
2025-11-26 15:59:57 +08:00
}
2025-11-26 17:56:24 +08:00
/**
* 处理指令逻辑
* 指令的核心处理函数连接权限验证和DOM操作
* @param {HTMLElement} el - DOM元素
* @param {Object} binding - 指令绑定对象
* @param {Object} vnode - 虚拟节点
*/
2025-11-26 15:59:57 +08:00
const handleDirective = (el, binding, vnode) => {
2025-11-26 17:56:24 +08:00
// 获取用户状态存储
const userStore = getUserStore(binding, vnode)
2025-11-26 17:56:24 +08:00
// 评估用户是否有权限
const shouldShow = evaluatePermission(userStore, binding.value, binding.modifiers)
2025-11-26 17:56:24 +08:00
// 根据权限控制元素可见性
toggleElementVisibility(el, shouldShow)
2025-11-26 15:59:57 +08:00
}
2025-11-26 17:56:24 +08:00
/**
* 注册权限指令
* 在Vue应用中全局注册v-permission指令
* 支持Vue 2和Vue 3的不同API
* @param {Object} appOrVue - Vue应用实例或Vue构造函数
*/
2025-11-26 15:59:57 +08:00
export const registerPermissionDirective = (appOrVue) => {
2025-11-26 17:56:24 +08:00
// 参数验证
if (!appOrVue) return
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
// 定义Vue 3的指令配置
const directiveConfig = {
2025-11-26 17:56:24 +08:00
mounted: handleDirective, // 元素挂载时
updated: handleDirective, // 元素更新时
beforeMount: handleDirective, // 元素挂载前
beforeUpdate: handleDirective // 元素更新前
}
2025-11-26 15:59:57 +08:00
2025-11-26 17:56:24 +08:00
// 根据Vue版本进行兼容处理
if (typeof appOrVue.directive === 'function') {
2025-11-26 17:56:24 +08:00
// Vue 3 使用 app.directive 注册
appOrVue.directive('permission', directiveConfig)
} else if (typeof appOrVue.prototype?.directive === 'function' || typeof appOrVue.directive === 'function') {
2025-11-26 17:56:24 +08:00
// Vue 2 使用 Vue.directive 注册
const VueCtor = appOrVue.prototype?.constructor || appOrVue
VueCtor.directive('permission', {
2025-11-26 17:56:24 +08:00
inserted: handleDirective, // 元素插入父节点时
update: handleDirective, // 元素更新时
componentUpdated: handleDirective // 组件更新后
})
}
2025-11-26 15:59:57 +08:00
}
2025-11-26 17:56:24 +08:00
/**
* 使用示例
*
* // 1. 注册指令在main.js中
* import { registerPermissionDirective } from '@/directives/permission'
* import { createApp } from 'vue'
* const app = createApp(App)
* registerPermissionDirective(app)
*
* // 2. 在组件中使用
*
* // 2.1 基础用法:用户需要任一权限
* <button v-permission="['user:view', 'user:edit']">编辑用户</button>
*
* // 2.2 使用all修饰符用户需要拥有所有权限
* <button v-permission.all="['user:view', 'user:edit']">高级编辑</button>
*
* // 2.3 使用requireAll修饰符效果同all
* <button v-permission.requireAll="['user:view', 'user:edit']">高级编辑</button>
*
* // 2.4 字符串权限
* <button v-permission="'user:view'">查看用户</button>
*
* // 2.5 逗号分隔的权限字符串
* <button v-permission="'user:view,user:edit'">编辑用户</button>
*/
// 默认导出
2025-11-26 15:59:57 +08:00
export default registerPermissionDirective