diff --git a/novalon-manage-web/src/__tests__/directives/permission.test.ts b/novalon-manage-web/src/__tests__/directives/permission.test.ts new file mode 100644 index 0000000..9dfc020 --- /dev/null +++ b/novalon-manage-web/src/__tests__/directives/permission.test.ts @@ -0,0 +1,124 @@ +import { describe, it, expect, beforeEach } from 'vitest' +import { mount } from '@vue/test-utils' +import { createPinia, setActivePinia } from 'pinia' +import { permissionDirective } from '@/directives/permission' +import { usePermissionStore } from '@/stores/permission' + +describe('v-permission 指令', () => { + beforeEach(() => { + setActivePinia(createPinia()) + localStorage.clear() + }) + + describe('角色检查', () => { + it('有角色时应该显示元素', () => { + const store = usePermissionStore() + store.setPermissionData({ + roles: ['admin'], + permissions: [], + menus: [] + }) + + const wrapper = mount({ + template: '', + directives: { + permission: permissionDirective + } + }) + + expect(wrapper.find('button').isVisible()).toBe(true) + }) + + it('无角色时应该隐藏元素', () => { + const store = usePermissionStore() + store.setPermissionData({ + roles: ['user'], + permissions: [], + menus: [] + }) + + const wrapper = mount({ + template: '', + directives: { + permission: permissionDirective + } + }) + + expect(wrapper.find('button').isVisible()).toBe(false) + }) + + it('支持数组参数(满足任一即可)', () => { + const store = usePermissionStore() + store.setPermissionData({ + roles: ['user'], + permissions: [], + menus: [] + }) + + const wrapper = mount({ + template: '', + directives: { + permission: permissionDirective + } + }) + + expect(wrapper.find('button').isVisible()).toBe(true) + }) + }) + + describe('权限检查', () => { + it('有权限时应该显示元素', () => { + const store = usePermissionStore() + store.setPermissionData({ + roles: [], + permissions: ['user:delete'], + menus: [] + }) + + const wrapper = mount({ + template: '', + directives: { + permission: permissionDirective + } + }) + + expect(wrapper.find('button').isVisible()).toBe(true) + }) + + it('无权限时应该隐藏元素', () => { + const store = usePermissionStore() + store.setPermissionData({ + roles: [], + permissions: ['user:read'], + menus: [] + }) + + const wrapper = mount({ + template: '', + directives: { + permission: permissionDirective + } + }) + + expect(wrapper.find('button').isVisible()).toBe(false) + }) + + it('支持简写形式(默认权限检查)', () => { + const store = usePermissionStore() + store.setPermissionData({ + roles: [], + permissions: ['user:create'], + menus: [] + }) + + const wrapper = mount({ + template: '', + directives: { + permission: permissionDirective + } + }) + + expect(wrapper.find('button').isVisible()).toBe(true) + }) + }) +}) diff --git a/novalon-manage-web/src/directives/permission.ts b/novalon-manage-web/src/directives/permission.ts new file mode 100644 index 0000000..d2533e4 --- /dev/null +++ b/novalon-manage-web/src/directives/permission.ts @@ -0,0 +1,33 @@ +import type { Directive, DirectiveBinding } from 'vue' +import { usePermissionStore } from '@/stores/permission' + +export const permissionDirective: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const permissionStore = usePermissionStore() + + const { arg, value } = binding + const checkType = arg || 'permission' + + if (!value) { + console.warn('v-permission 指令需要提供权限值') + el.style.display = 'none' + return + } + + let hasAccess = false + + if (checkType === 'role') { + hasAccess = permissionStore.hasRole(value) + } else if (checkType === 'permission') { + hasAccess = permissionStore.hasPermission(value) + } else { + console.warn(`未知的权限检查类型: ${checkType}`) + el.style.display = 'none' + return + } + + if (!hasAccess) { + el.style.display = 'none' + } + } +} diff --git a/novalon-manage-web/src/main.ts b/novalon-manage-web/src/main.ts index 8774c23..d539656 100644 --- a/novalon-manage-web/src/main.ts +++ b/novalon-manage-web/src/main.ts @@ -6,6 +6,7 @@ import 'element-plus/dist/index.css' import router from './router' import App from './App.vue' import './assets/styles.css' +import { permissionDirective } from './directives/permission' const app = createApp(App) const pinia = createPinia() @@ -16,4 +17,6 @@ app.use(ElementPlus, { locale: zhCn, }) +app.directive('permission', permissionDirective) + app.mount('#app')