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