diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auth.vue b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auth.vue
index 05858885e..9b398c041 100644
--- a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auth.vue
+++ b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auth.vue
@@ -4,8 +4,7 @@
diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/authAll.vue b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/authAll.vue
index 54c8d5859..55404479b 100644
--- a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/authAll.vue
+++ b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/authAll.vue
@@ -4,9 +4,7 @@
diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auths.vue b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auths.vue
index 41b8b2786..98a28f665 100644
--- a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auths.vue
+++ b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/components/auth/auths.vue
@@ -4,8 +4,7 @@
diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/directive/authDirective.ts b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/directive/authDirective.ts
index 30c39fd93..e864f488c 100644
--- a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/directive/authDirective.ts
+++ b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/directive/authDirective.ts
@@ -1,44 +1,82 @@
-import type { App } from 'vue';
-import { useUserInfo } from '/@/stores/userInfo';
-import { judementSameArr } from '/@/utils/arrayOperation';
-
-/**
- * 用户权限指令
- * @directive 单个权限验证(v-auth="xxx")
- * @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
- * @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
- */
-export function authDirective(app: App) {
- // 单个权限验证(v-auth="xxx")
- app.directive('auth', {
- mounted(el, binding) {
- const stores = useUserInfo();
- if(stores.userInfos.isDebug == false){
- if (!stores.userInfos.authBtnList.some((v: string) => {
- return v.toLowerCase() === binding.value.toLowerCase()
- })) el.parentNode.removeChild(el);
- }
- },
- });
- // 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
- app.directive('auths', {
- mounted(el, binding) {
- let flag = false;
- const stores = useUserInfo();
- stores.userInfos.authBtnList.map((val: string) => {
- binding.value.map((v: string) => {
- if (val === v) flag = true;
- });
- });
- if (!flag) el.parentNode.removeChild(el);
- },
- });
- // 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
- app.directive('auth-all', {
- mounted(el, binding) {
- const stores = useUserInfo();
- const flag = judementSameArr(binding.value, stores.userInfos.authBtnList);
- if (!flag) el.parentNode.removeChild(el);
- },
- });
-}
+import { watch } from 'vue';
+import type { App, ObjectDirective } from 'vue';
+import { useUserInfo } from '/@/stores/userInfo';
+import { auth, authAll, auths } from '/@/utils/authFunction';
+
+type PermissionCheck = (value: unknown) => boolean;
+
+interface PermissionElementState {
+ bindingValue: unknown;
+ checkPermission: PermissionCheck;
+ wasInitiallyHidden: HTMLElement['hidden'];
+}
+
+// 注册所有使用权限指令的元素。这里只保存元素状态,不为每个元素创建 watcher。
+// Map 需要在 unmounted 中主动删除,避免继续持有已经卸载的 DOM。
+const permissionElements = new Map();
+let stopWatchingPermissions: (() => void) | undefined;
+
+function updateElementVisibility(el: HTMLElement, state: PermissionElementState) {
+ const shouldHide = state.wasInitiallyHidden || !state.checkPermission(state.bindingValue);
+ if (el.hidden !== shouldHide) el.hidden = shouldHide;
+}
+
+function updateAllPermissionElements() {
+ permissionElements.forEach((state, el) => updateElementVisibility(el, state));
+}
+
+function startWatchingPermissions() {
+ if (stopWatchingPermissions) return;
+
+ const stores = useUserInfo();
+ stopWatchingPermissions = watch(
+ [() => stores.userInfos.isDebug, () => stores.authBtnSet],
+ updateAllPermissionElements
+ );
+}
+
+function createPermissionDirective(checkPermission: PermissionCheck): ObjectDirective {
+ return {
+ mounted(el, binding) {
+ const state = {
+ bindingValue: binding.value,
+ checkPermission,
+ wasInitiallyHidden: el.hidden,
+ };
+ permissionElements.set(el, state);
+ updateElementVisibility(el, state);
+ startWatchingPermissions();
+ },
+ updated(el, binding) {
+ const state = permissionElements.get(el);
+ if (!state) return;
+
+ state.bindingValue = binding.value;
+ updateElementVisibility(el, state);
+ },
+ unmounted(el) {
+ permissionElements.delete(el);
+
+ // 页面上已经没有权限指令时,连唯一的共享 watcher 也一起释放。
+ if (permissionElements.size === 0) {
+ stopWatchingPermissions?.();
+ stopWatchingPermissions = undefined;
+ }
+ },
+ };
+}
+
+/**
+ * 用户权限指令
+ * @directive 单个权限验证(v-auth="xxx")
+ * @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
+ * @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
+ */
+export function authDirective(app: App) {
+ // 单个权限验证(v-auth="xxx")
+ app.directive('auth', createPermissionDirective(auth));
+ // 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
+ app.directive('auths', createPermissionDirective(auths));
+ // 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
+ app.directive('auth-all', createPermissionDirective(authAll));
+}
diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/stores/userInfo.ts b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/stores/userInfo.ts
index 045f3ae6e..2899c1e38 100644
--- a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/stores/userInfo.ts
+++ b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/stores/userInfo.ts
@@ -22,8 +22,15 @@ export const useUserInfo = defineStore('userInfo', {
menu:[],
roles: [],
authBtnList: [],
+ },
+ }),
+ getters: {
+ // Pinia getter 会缓存结果,仅在 authBtnList 变化时重新生成。
+ // 权限指令和组件可以复用同一个 Set,避免每个按钮重复遍历权限数组。
+ authBtnSet: (state): Set => {
+ return new Set(state.userInfos.authBtnList.map((value) => value.trim().toLowerCase()).filter(Boolean));
},
- }),
+ },
actions: {
async setUserInfos() {
// 存储用户信息到浏览器缓存
diff --git a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/utils/authFunction.ts b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/utils/authFunction.ts
index 84c0ab468..ee5df5ee8 100644
--- a/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/utils/authFunction.ts
+++ b/demo/WalkingTec.Mvvm.Vue3Demo/ClientApp/src/utils/authFunction.ts
@@ -1,14 +1,15 @@
import { useUserInfo } from '/@/stores/userInfo';
-import { judementSameArr } from '/@/utils/arrayOperation';
/**
* 单个权限验证
* @param value 权限值
* @returns 有权限,返回 `true`,反之则反
*/
-export function auth(value: string): boolean {
+export function auth(value: unknown): boolean {
+ if (typeof value !== 'string') return false;
const stores = useUserInfo();
- return stores.userInfos.authBtnList.some((v: string) => v === value);
+ if (stores.userInfos.isDebug === true) return true;
+ return stores.authBtnSet.has(value.trim().toLowerCase());
}
/**
@@ -16,15 +17,11 @@ export function auth(value: string): boolean {
* @param value 权限值
* @returns 有权限,返回 `true`,反之则反
*/
-export function auths(value: Array): boolean {
- let flag = false;
+export function auths(value: unknown): boolean {
+ if (!Array.isArray(value) || !value.every((item) => typeof item === 'string')) return false;
const stores = useUserInfo();
- stores.userInfos.authBtnList.map((val: string) => {
- value.map((v: string) => {
- if (val === v) flag = true;
- });
- });
- return flag;
+ if (stores.userInfos.isDebug === true) return true;
+ return value.some((permission) => stores.authBtnSet.has(permission.trim().toLowerCase()));
}
/**
@@ -32,7 +29,9 @@ export function auths(value: Array): boolean {
* @param value 权限值
* @returns 有权限,返回 `true`,反之则反
*/
-export function authAll(value: Array): boolean {
+export function authAll(value: unknown): boolean {
+ if (!Array.isArray(value) || !value.every((item) => typeof item === 'string')) return false;
const stores = useUserInfo();
- return judementSameArr(value, stores.userInfos.authBtnList);
+ if (stores.userInfos.isDebug === true) return true;
+ return value.length > 0 && value.every((permission) => stores.authBtnSet.has(permission.trim().toLowerCase()));
}