From 7df7e1f47b735b0f3d94f21ae6bfc2ecc6b902f1 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Tue, 20 May 2025 10:34:44 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20:recycle:=20=E6=8F=90=E5=8F=96=20RO?= =?UTF-8?q?LE=5FROOT=20=E5=B8=B8=E9=87=8F=E5=88=B0=20constants=20=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E5=B9=B6=E5=85=A8=E5=B1=80=E7=BB=9F=E4=B8=80=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/NoticeDropdown/index.vue | 2 +- src/composables/index.ts | 11 +++++ .../services => composables}/useDictSync.ts | 6 +-- .../useOnlineCount.ts | 4 +- .../core => composables}/useStomp.ts | 45 ++++++++++--------- src/constants/index.ts | 1 + src/directive/permission/index.ts | 5 ++- src/hooks/index.ts | 7 --- src/hooks/websocket/index.ts | 11 ----- src/plugins/permission.ts | 3 +- src/plugins/permission.ts.bak | 0 src/plugins/websocket.ts | 2 +- src/store/modules/permission.store.ts | 6 ++- src/views/dashboard/index.vue | 2 +- src/views/demo/dict-sync.vue | 2 +- src/views/demo/websocket.vue | 20 ++++----- 16 files changed, 63 insertions(+), 64 deletions(-) create mode 100644 src/composables/index.ts rename src/{hooks/websocket/services => composables}/useDictSync.ts (97%) rename src/{hooks/websocket/services => composables}/useOnlineCount.ts (98%) rename src/{hooks/websocket/core => composables}/useStomp.ts (90%) create mode 100644 src/constants/index.ts delete mode 100644 src/hooks/index.ts delete mode 100644 src/hooks/websocket/index.ts delete mode 100644 src/plugins/permission.ts.bak diff --git a/src/components/NoticeDropdown/index.vue b/src/components/NoticeDropdown/index.vue index 0333ed88..541524fe 100644 --- a/src/components/NoticeDropdown/index.vue +++ b/src/components/NoticeDropdown/index.vue @@ -94,7 +94,7 @@ const noticeList = ref([]); const noticeDialogVisible = ref(false); const noticeDetail = ref(null); -import { useStomp } from "@/hooks/websocket/core/useStomp"; +import { useStomp } from "@/composables/useStomp"; const { subscribe, unsubscribe, isConnected } = useStomp(); watch( diff --git a/src/composables/index.ts b/src/composables/index.ts new file mode 100644 index 00000000..c848f9ef --- /dev/null +++ b/src/composables/index.ts @@ -0,0 +1,11 @@ +/** + * 全局组合式函数入口文件 + * 导出所有可用的组合式函数 + */ + +// 导出核心组合式函数 +export { useStomp } from "./useStomp"; + +// 导出业务服务组合式函数 +export { useDictSync } from "./useDictSync"; +export { useOnlineCount } from "./useOnlineCount"; diff --git a/src/hooks/websocket/services/useDictSync.ts b/src/composables/useDictSync.ts similarity index 97% rename from src/hooks/websocket/services/useDictSync.ts rename to src/composables/useDictSync.ts index d5546a3d..bf7f3c7e 100644 --- a/src/hooks/websocket/services/useDictSync.ts +++ b/src/composables/useDictSync.ts @@ -1,5 +1,5 @@ import { useDictStoreHook } from "@/store/modules/dict.store"; -import { useStomp } from "../core/useStomp"; +import { useStomp } from "./useStomp"; import { IMessage } from "@stomp/stompjs"; import { ref } from "vue"; @@ -16,7 +16,7 @@ export type DictMessageCallback = (_message: DictMessage) => void; let instance: ReturnType | null = null; /** - * 创建字典同步Hook + * 创建字典同步组合式函数 * 负责监听后端字典变更并同步到前端 */ function createDictSyncHook() { @@ -186,7 +186,7 @@ function createDictSyncHook() { } /** - * 字典同步Hook + * 字典同步组合式函数 * 用于监听后端字典变更并同步到前端 */ export function useDictSync() { diff --git a/src/hooks/websocket/services/useOnlineCount.ts b/src/composables/useOnlineCount.ts similarity index 98% rename from src/hooks/websocket/services/useOnlineCount.ts rename to src/composables/useOnlineCount.ts index f4d697bf..fac73274 100644 --- a/src/hooks/websocket/services/useOnlineCount.ts +++ b/src/composables/useOnlineCount.ts @@ -1,11 +1,11 @@ import { ref, onMounted, onUnmounted, watch } from "vue"; -import { useStomp } from "../core/useStomp"; +import { useStomp } from "./useStomp"; import { ElMessage } from "element-plus"; import { Storage } from "@/utils/storage"; import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys"; /** - * 在线用户计数Hook + * 在线用户计数组合式函数 * 用于订阅后端推送的在线用户数量变化 */ export function useOnlineCount() { diff --git a/src/hooks/websocket/core/useStomp.ts b/src/composables/useStomp.ts similarity index 90% rename from src/hooks/websocket/core/useStomp.ts rename to src/composables/useStomp.ts index 5e603041..2e507461 100644 --- a/src/hooks/websocket/core/useStomp.ts +++ b/src/composables/useStomp.ts @@ -23,7 +23,7 @@ export interface UseStompOptions { } /** - * STOMP WebSocket连接Hook + * STOMP WebSocket连接组合式函数 * 用于管理WebSocket连接的建立、断开、重连和消息订阅 */ export function useStomp(options: UseStompOptions = {}) { @@ -244,70 +244,73 @@ export function useStomp(options: UseStompOptions = {}) { * @returns 返回订阅 id,用于后续取消订阅 */ const subscribe = (destination: string, callback: (_message: IMessage) => void): string => { - if (!client.value) { - return ""; - } - - if (!client.value.connected) { + if (!client.value || !client.value.connected) { + console.warn(`尝试订阅 ${destination} 失败: 客户端未连接`); return ""; } try { const subscription = client.value.subscribe(destination, callback); - subscriptions.set(subscription.id, subscription); - console.log(`订阅成功: ${destination}, ID: ${subscription.id}`); - return subscription.id; + const subscriptionId = subscription.id; + subscriptions.set(subscriptionId, subscription); + console.log(`订阅成功: ${destination}, ID: ${subscriptionId}`); + return subscriptionId; } catch (error) { - console.error(`订阅失败(${destination}):`, error); + console.error(`订阅 ${destination} 失败:`, error); return ""; } }; /** - * 取消指定订阅 - * @param subscriptionId 要取消的订阅 id + * 取消订阅 + * @param subscriptionId 订阅 id */ const unsubscribe = (subscriptionId: string) => { const subscription = subscriptions.get(subscriptionId); if (subscription) { subscription.unsubscribe(); subscriptions.delete(subscriptionId); + console.log(`已取消订阅: ${subscriptionId}`); } }; /** - * 主动断开连接(如果未连接则不执行) + * 断开WebSocket连接 */ const disconnect = () => { - if (client.value && !(client.value.connected || client.value.active)) { - console.log("Already disconnected, skipping disconnect() call."); - return; + if (client.value && client.value.connected) { + // 清除所有订阅 + for (const [id, subscription] of subscriptions.entries()) { + subscription.unsubscribe(); + subscriptions.delete(id); + } + + // 断开连接 + client.value.deactivate(); + console.log("WebSocket连接已断开"); } - // 清除所有计时器 + // 清除重连计时器 if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; } + // 清除连接超时计时器 if (connectionTimeoutTimer) { clearTimeout(connectionTimeoutTimer); connectionTimeoutTimer = null; } - client.value?.deactivate(); isConnected.value = false; reconnectCount.value = 0; }; return { - client, isConnected, - reconnectCount, connect, subscribe, unsubscribe, disconnect, - brokerURL, }; } diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 00000000..83e52a3c --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1 @@ +export const ROLE_ROOT = "ROOT"; diff --git a/src/directive/permission/index.ts b/src/directive/permission/index.ts index 2683d22e..b2d856a4 100644 --- a/src/directive/permission/index.ts +++ b/src/directive/permission/index.ts @@ -1,6 +1,7 @@ import type { Directive, DirectiveBinding } from "vue"; import { useUserStore } from "@/store"; +import { ROLE_ROOT } from "@/constants"; /** * 按钮权限 @@ -18,8 +19,8 @@ export const hasPerm: Directive = { const { roles, perms } = useUserStore().userInfo; - // 超级管理员拥有所有权限,如果是”*:*:*”权限标识,则不需要进行权限校验 - if (roles.includes("ROOT") || requiredPerms.includes("*:*:*")) { + // 超级管理员拥有所有权限,如果是"*:*:*"权限标识,则不需要进行权限校验 + if (roles.includes(ROLE_ROOT) || requiredPerms.includes("*:*:*")) { return; } diff --git a/src/hooks/index.ts b/src/hooks/index.ts deleted file mode 100644 index abab537d..00000000 --- a/src/hooks/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 全局Hooks入口文件 - * 导出所有可用的Hooks - */ - -// 导出WebSocket相关Hook -export * from "./websocket"; diff --git a/src/hooks/websocket/index.ts b/src/hooks/websocket/index.ts deleted file mode 100644 index 3d44e748..00000000 --- a/src/hooks/websocket/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * WebSocket相关Hook入口文件 - * 统一导出所有WebSocket相关Hook - */ - -// 核心基础Hook -export { useStomp } from "./core/useStomp"; - -// 业务服务Hook -export { useOnlineCount } from "./services/useOnlineCount"; -export { useDictSync } from "./services/useDictSync"; diff --git a/src/plugins/permission.ts b/src/plugins/permission.ts index f9bff189..4d934fcc 100644 --- a/src/plugins/permission.ts +++ b/src/plugins/permission.ts @@ -4,6 +4,7 @@ import { Storage } from "@/utils/storage"; import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys"; import router from "@/router"; import { usePermissionStore, useUserStore } from "@/store"; +import { ROLE_ROOT } from "@/constants"; export function setupPermission() { // 白名单路由 @@ -78,7 +79,7 @@ export function hasAuth(value: string | string[], type: "button" | "role" = "but const { roles, perms } = useUserStore().userInfo; // 超级管理员 拥有所有权限 - if (type === "button" && roles.includes("ROOT")) { + if (type === "button" && roles.includes(ROLE_ROOT)) { return true; } diff --git a/src/plugins/permission.ts.bak b/src/plugins/permission.ts.bak deleted file mode 100644 index e69de29b..00000000 diff --git a/src/plugins/websocket.ts b/src/plugins/websocket.ts index 1c2ad754..a2a6f78c 100644 --- a/src/plugins/websocket.ts +++ b/src/plugins/websocket.ts @@ -1,4 +1,4 @@ -import { useDictSync } from "@/hooks/websocket/services/useDictSync"; +import { useDictSync } from "@/composables/useDictSync"; import { Storage } from "@/utils/storage"; import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys"; diff --git a/src/store/modules/permission.store.ts b/src/store/modules/permission.store.ts index db1ca5f6..647d3e75 100644 --- a/src/store/modules/permission.store.ts +++ b/src/store/modules/permission.store.ts @@ -103,8 +103,12 @@ const parseDynamicRoutes = (rawRoutes: RouteVO[]): RouteRecordRaw[] => { return parsedRoutes; }; + /** - * 在组件外使用 Pinia store 实例 @see https://pinia.vuejs.org/core-concepts/outside-component-usage.html + * 导出此hook函数用于在非组件环境(如其他store、工具函数等)中获取权限store实例 + * + * 在组件中可直接使用usePermissionStore(),但在组件外部需要传入store实例 + * 此函数简化了这个过程,避免每次都手动传入store参数 */ export function usePermissionStoreHook() { return usePermissionStore(store); diff --git a/src/views/dashboard/index.vue b/src/views/dashboard/index.vue index 125c0dd6..4fd0323a 100644 --- a/src/views/dashboard/index.vue +++ b/src/views/dashboard/index.vue @@ -359,7 +359,7 @@ import { useUserStore } from "@/store/modules/user.store"; import { formatGrowthRate } from "@/utils"; import { useTransition, useDateFormat } from "@vueuse/core"; import { Connection, Failed } from "@element-plus/icons-vue"; -import { useOnlineCount } from "@/hooks/websocket/services/useOnlineCount"; +import { useOnlineCount } from "@/composables/useOnlineCount"; // 在线用户数量组件相关 const { onlineUserCount, lastUpdateTime, isConnected } = useOnlineCount(); diff --git a/src/views/demo/dict-sync.vue b/src/views/demo/dict-sync.vue index 6e10da0a..31bc55b8 100644 --- a/src/views/demo/dict-sync.vue +++ b/src/views/demo/dict-sync.vue @@ -142,7 +142,7 @@ import { useDictStoreHook } from "@/store/modules/dict.store"; import { useDateFormat } from "@vueuse/core"; import DictAPI, { DictItemForm } from "@/api/system/dict.api"; -import { useDictSync, DictMessage } from "@/hooks/websocket/services/useDictSync"; +import { useDictSync, DictMessage } from "@/composables/useDictSync"; // 性别字典编码 const DICT_CODE = "gender"; diff --git a/src/views/demo/websocket.vue b/src/views/demo/websocket.vue index 32ddaaf8..bf40b163 100644 --- a/src/views/demo/websocket.vue +++ b/src/views/demo/websocket.vue @@ -97,7 +97,7 @@