diff --git a/src/directives/permission/index.ts b/src/directives/permission/index.ts index e3d629ee..164b319c 100644 --- a/src/directives/permission/index.ts +++ b/src/directives/permission/index.ts @@ -1,10 +1,10 @@ import type { Directive, DirectiveBinding } from "vue"; import { useUserStore } from "@/stores"; -import { ROLE_ROOT } from "@/constants"; +import { hasPerm as hasPermFn } from "@/utils/auth"; /** - * 按钮权限 + * 按钮权限(复用 utils/auth 的 hasPerm 函数,保持指令与函数行为一致) */ export const hasPerm: Directive = { mounted(el: HTMLElement, binding: DirectiveBinding) { @@ -17,20 +17,8 @@ export const hasPerm: Directive = { ); } - const { roles, perms } = useUserStore().userInfo; - - // 超级管理员拥有所有权限,如果是"*:*:*"权限标识,则不需要进行权限校验 - if (roles.includes(ROLE_ROOT) || requiredPerms.includes("*:*:*")) { - return; - } - - // 检查权限 - const hasAuth = Array.isArray(requiredPerms) - ? requiredPerms.some((perm) => perms.includes(perm)) - : perms.includes(requiredPerms); - // 如果没有权限,移除该元素 - if (!hasAuth && el.parentNode) { + if (!hasPermFn(requiredPerms) && el.parentNode) { el.parentNode.removeChild(el); } }, diff --git a/src/utils/auth.ts b/src/utils/auth.ts index b94b6171..0443e790 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,7 +1,6 @@ import { Storage } from "./storage"; import { STORAGE_KEYS, ROLE_ROOT } from "@/constants"; import { useUserStoreHook } from "@/stores/user"; -import router from "@/router"; // 负责本地凭证与偏好的读写 export const AuthStorage = { @@ -45,58 +44,21 @@ export const AuthStorage = { }; /** - * 权限判断 + * 权限判断(v-hasPerm 指令共用本实现) */ -export function hasPerm(value: string | string[], type: "button" | "role" = "button"): boolean { +export function hasPerm(value: string | string[]): boolean { const { roles, perms } = useUserStoreHook().userInfo; if (!roles || !perms) { return false; } - // 超级管理员拥有所有权限 - if (type === "button" && roles.includes(ROLE_ROOT)) { + const requiredPerms = Array.isArray(value) ? value : [value]; + + // 超级管理员或通配权限直接放行 + if (roles.includes(ROLE_ROOT) || requiredPerms.includes("*:*:*")) { return true; } - const auths = type === "button" ? perms : roles; - return typeof value === "string" - ? auths.includes(value) - : value.some((perm) => auths.includes(perm)); -} - -let redirectingToLogin = false; - -/** - * 重定向到登录页面 - */ -export async function redirectToLogin( - message: string = "请重新登录", - notify: boolean = true -): Promise { - if (redirectingToLogin) return; - redirectingToLogin = true; - - if (notify) { - ElNotification({ - title: "提示", - message, - type: "warning", - duration: 3000, - }); - } - - await useUserStoreHook().resetAllState(); - - try { - // 跳转到登录页,保留当前路由用于登录后跳转 - const currentPath = router.currentRoute.value.fullPath; - await router.push(`/login?redirect=${encodeURIComponent(currentPath)}`); - } catch (error) { - console.error("Redirect to login error:", error); - // 强制跳转,即使路由重定向失败 - window.location.href = "/login"; - } finally { - redirectingToLogin = false; - } + return requiredPerms.some((perm) => perms.includes(perm)); } diff --git a/src/utils/request.ts b/src/utils/request.ts index 7ed55a9c..961dd22f 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -4,7 +4,7 @@ import qs from "qs"; import { ApiCodeEnum } from "@/enums/api"; import { useUserStoreHook } from "@/stores/user"; import { usePermissionStoreHook } from "@/stores/permission"; -import { AuthStorage, redirectToLogin } from "@/utils/auth"; +import { AuthStorage } from "@/utils/auth"; import type { ApiResult } from "@/api/common"; // 防止同一请求在 token 刷新后重复进入重试,导致死循环 @@ -65,15 +65,15 @@ http.interceptors.response.use( // Token 过期 if (code === ApiCodeEnum.ACCESS_TOKEN_INVALID) { + const userStore = useUserStoreHook(); if (!config || retriedRequests.has(config)) { - await redirectToLogin("登录已过期,请重新登录"); + await userStore.redirectToLogin("登录已过期,请重新登录"); return Promise.reject(new Error("Token Invalid")); } retriedRequests.add(config); try { - const userStore = useUserStoreHook(); await userStore.refreshTokenOnce(); const token = AuthStorage.getAccessToken(); @@ -83,14 +83,14 @@ http.interceptors.response.use( return http(config); } catch { - await redirectToLogin("登录已过期,请重新登录"); + await userStore.redirectToLogin("登录已过期,请重新登录"); return Promise.reject(new Error("Token refresh failed")); } } // Refresh token 失效 if (code === ApiCodeEnum.REFRESH_TOKEN_INVALID) { - await redirectToLogin("登录已过期,请重新登录", false); + await useUserStoreHook().redirectToLogin("登录已过期,请重新登录", false); return Promise.reject(new Error("Token Invalid")); } diff --git a/src/views/profile/index.vue b/src/views/profile/index.vue index e2d1e31c..5cde0aa1 100644 --- a/src/views/profile/index.vue +++ b/src/views/profile/index.vue @@ -314,7 +314,6 @@ import type { Component } from "vue"; import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue"; import FileAPI from "@/api/file"; import { useUserStoreHook } from "@/stores"; -import { redirectToLogin } from "@/utils/auth"; import { Calendar, @@ -856,7 +855,7 @@ const handleSubmit = async () => { await UserAPI.changePassword(passwordChangeForm); dialogState.visible = false; - await redirectToLogin("密码已修改,请重新登录"); + await userStore.redirectToLogin("密码已修改,请重新登录"); } else if (dialogState.type === DialogType.MOBILE) { const valid = await mobileBindingFormRef.value?.validate(); if (!valid) return;