refactor: ♻️ 提取 ROLE_ROOT 常量到 constants 目录并全局统一引用
This commit is contained in:
@@ -94,7 +94,7 @@ const noticeList = ref<NoticePageVO[]>([]);
|
|||||||
const noticeDialogVisible = ref(false);
|
const noticeDialogVisible = ref(false);
|
||||||
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
||||||
|
|
||||||
import { useStomp } from "@/hooks/websocket/core/useStomp";
|
import { useStomp } from "@/composables/useStomp";
|
||||||
const { subscribe, unsubscribe, isConnected } = useStomp();
|
const { subscribe, unsubscribe, isConnected } = useStomp();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
11
src/composables/index.ts
Normal file
11
src/composables/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* 全局组合式函数入口文件
|
||||||
|
* 导出所有可用的组合式函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 导出核心组合式函数
|
||||||
|
export { useStomp } from "./useStomp";
|
||||||
|
|
||||||
|
// 导出业务服务组合式函数
|
||||||
|
export { useDictSync } from "./useDictSync";
|
||||||
|
export { useOnlineCount } from "./useOnlineCount";
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useDictStoreHook } from "@/store/modules/dict.store";
|
import { useDictStoreHook } from "@/store/modules/dict.store";
|
||||||
import { useStomp } from "../core/useStomp";
|
import { useStomp } from "./useStomp";
|
||||||
import { IMessage } from "@stomp/stompjs";
|
import { IMessage } from "@stomp/stompjs";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export type DictMessageCallback = (_message: DictMessage) => void;
|
|||||||
let instance: ReturnType<typeof createDictSyncHook> | null = null;
|
let instance: ReturnType<typeof createDictSyncHook> | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建字典同步Hook
|
* 创建字典同步组合式函数
|
||||||
* 负责监听后端字典变更并同步到前端
|
* 负责监听后端字典变更并同步到前端
|
||||||
*/
|
*/
|
||||||
function createDictSyncHook() {
|
function createDictSyncHook() {
|
||||||
@@ -186,7 +186,7 @@ function createDictSyncHook() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典同步Hook
|
* 字典同步组合式函数
|
||||||
* 用于监听后端字典变更并同步到前端
|
* 用于监听后端字典变更并同步到前端
|
||||||
*/
|
*/
|
||||||
export function useDictSync() {
|
export function useDictSync() {
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ref, onMounted, onUnmounted, watch } from "vue";
|
import { ref, onMounted, onUnmounted, watch } from "vue";
|
||||||
import { useStomp } from "../core/useStomp";
|
import { useStomp } from "./useStomp";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { Storage } from "@/utils/storage";
|
import { Storage } from "@/utils/storage";
|
||||||
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
|
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线用户计数Hook
|
* 在线用户计数组合式函数
|
||||||
* 用于订阅后端推送的在线用户数量变化
|
* 用于订阅后端推送的在线用户数量变化
|
||||||
*/
|
*/
|
||||||
export function useOnlineCount() {
|
export function useOnlineCount() {
|
||||||
@@ -23,7 +23,7 @@ export interface UseStompOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* STOMP WebSocket连接Hook
|
* STOMP WebSocket连接组合式函数
|
||||||
* 用于管理WebSocket连接的建立、断开、重连和消息订阅
|
* 用于管理WebSocket连接的建立、断开、重连和消息订阅
|
||||||
*/
|
*/
|
||||||
export function useStomp(options: UseStompOptions = {}) {
|
export function useStomp(options: UseStompOptions = {}) {
|
||||||
@@ -244,70 +244,73 @@ export function useStomp(options: UseStompOptions = {}) {
|
|||||||
* @returns 返回订阅 id,用于后续取消订阅
|
* @returns 返回订阅 id,用于后续取消订阅
|
||||||
*/
|
*/
|
||||||
const subscribe = (destination: string, callback: (_message: IMessage) => void): string => {
|
const subscribe = (destination: string, callback: (_message: IMessage) => void): string => {
|
||||||
if (!client.value) {
|
if (!client.value || !client.value.connected) {
|
||||||
return "";
|
console.warn(`尝试订阅 ${destination} 失败: 客户端未连接`);
|
||||||
}
|
|
||||||
|
|
||||||
if (!client.value.connected) {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const subscription = client.value.subscribe(destination, callback);
|
const subscription = client.value.subscribe(destination, callback);
|
||||||
subscriptions.set(subscription.id, subscription);
|
const subscriptionId = subscription.id;
|
||||||
console.log(`订阅成功: ${destination}, ID: ${subscription.id}`);
|
subscriptions.set(subscriptionId, subscription);
|
||||||
return subscription.id;
|
console.log(`订阅成功: ${destination}, ID: ${subscriptionId}`);
|
||||||
|
return subscriptionId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`订阅失败(${destination}):`, error);
|
console.error(`订阅 ${destination} 失败:`, error);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消指定订阅
|
* 取消订阅
|
||||||
* @param subscriptionId 要取消的订阅 id
|
* @param subscriptionId 订阅 id
|
||||||
*/
|
*/
|
||||||
const unsubscribe = (subscriptionId: string) => {
|
const unsubscribe = (subscriptionId: string) => {
|
||||||
const subscription = subscriptions.get(subscriptionId);
|
const subscription = subscriptions.get(subscriptionId);
|
||||||
if (subscription) {
|
if (subscription) {
|
||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
subscriptions.delete(subscriptionId);
|
subscriptions.delete(subscriptionId);
|
||||||
|
console.log(`已取消订阅: ${subscriptionId}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主动断开连接(如果未连接则不执行)
|
* 断开WebSocket连接
|
||||||
*/
|
*/
|
||||||
const disconnect = () => {
|
const disconnect = () => {
|
||||||
if (client.value && !(client.value.connected || client.value.active)) {
|
if (client.value && client.value.connected) {
|
||||||
console.log("Already disconnected, skipping disconnect() call.");
|
// 清除所有订阅
|
||||||
return;
|
for (const [id, subscription] of subscriptions.entries()) {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
subscriptions.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除所有计时器
|
// 断开连接
|
||||||
|
client.value.deactivate();
|
||||||
|
console.log("WebSocket连接已断开");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除重连计时器
|
||||||
if (reconnectTimer) {
|
if (reconnectTimer) {
|
||||||
clearTimeout(reconnectTimer);
|
clearTimeout(reconnectTimer);
|
||||||
reconnectTimer = null;
|
reconnectTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清除连接超时计时器
|
||||||
if (connectionTimeoutTimer) {
|
if (connectionTimeoutTimer) {
|
||||||
clearTimeout(connectionTimeoutTimer);
|
clearTimeout(connectionTimeoutTimer);
|
||||||
connectionTimeoutTimer = null;
|
connectionTimeoutTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
client.value?.deactivate();
|
|
||||||
isConnected.value = false;
|
isConnected.value = false;
|
||||||
reconnectCount.value = 0;
|
reconnectCount.value = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
client,
|
|
||||||
isConnected,
|
isConnected,
|
||||||
reconnectCount,
|
|
||||||
connect,
|
connect,
|
||||||
subscribe,
|
subscribe,
|
||||||
unsubscribe,
|
unsubscribe,
|
||||||
disconnect,
|
disconnect,
|
||||||
brokerURL,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
1
src/constants/index.ts
Normal file
1
src/constants/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const ROLE_ROOT = "ROOT";
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Directive, DirectiveBinding } from "vue";
|
import type { Directive, DirectiveBinding } from "vue";
|
||||||
|
|
||||||
import { useUserStore } from "@/store";
|
import { useUserStore } from "@/store";
|
||||||
|
import { ROLE_ROOT } from "@/constants";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按钮权限
|
* 按钮权限
|
||||||
@@ -18,8 +19,8 @@ export const hasPerm: Directive = {
|
|||||||
|
|
||||||
const { roles, perms } = useUserStore().userInfo;
|
const { roles, perms } = useUserStore().userInfo;
|
||||||
|
|
||||||
// 超级管理员拥有所有权限,如果是”*:*:*”权限标识,则不需要进行权限校验
|
// 超级管理员拥有所有权限,如果是"*:*:*"权限标识,则不需要进行权限校验
|
||||||
if (roles.includes("ROOT") || requiredPerms.includes("*:*:*")) {
|
if (roles.includes(ROLE_ROOT) || requiredPerms.includes("*:*:*")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
/**
|
|
||||||
* 全局Hooks入口文件
|
|
||||||
* 导出所有可用的Hooks
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 导出WebSocket相关Hook
|
|
||||||
export * from "./websocket";
|
|
||||||
@@ -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";
|
|
||||||
@@ -4,6 +4,7 @@ import { Storage } from "@/utils/storage";
|
|||||||
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
|
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import { usePermissionStore, useUserStore } from "@/store";
|
import { usePermissionStore, useUserStore } from "@/store";
|
||||||
|
import { ROLE_ROOT } from "@/constants";
|
||||||
|
|
||||||
export function setupPermission() {
|
export function setupPermission() {
|
||||||
// 白名单路由
|
// 白名单路由
|
||||||
@@ -78,7 +79,7 @@ export function hasAuth(value: string | string[], type: "button" | "role" = "but
|
|||||||
const { roles, perms } = useUserStore().userInfo;
|
const { roles, perms } = useUserStore().userInfo;
|
||||||
|
|
||||||
// 超级管理员 拥有所有权限
|
// 超级管理员 拥有所有权限
|
||||||
if (type === "button" && roles.includes("ROOT")) {
|
if (type === "button" && roles.includes(ROLE_ROOT)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useDictSync } from "@/hooks/websocket/services/useDictSync";
|
import { useDictSync } from "@/composables/useDictSync";
|
||||||
import { Storage } from "@/utils/storage";
|
import { Storage } from "@/utils/storage";
|
||||||
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
|
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,12 @@ const parseDynamicRoutes = (rawRoutes: RouteVO[]): RouteRecordRaw[] => {
|
|||||||
|
|
||||||
return parsedRoutes;
|
return parsedRoutes;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在组件外使用 Pinia store 实例 @see https://pinia.vuejs.org/core-concepts/outside-component-usage.html
|
* 导出此hook函数用于在非组件环境(如其他store、工具函数等)中获取权限store实例
|
||||||
|
*
|
||||||
|
* 在组件中可直接使用usePermissionStore(),但在组件外部需要传入store实例
|
||||||
|
* 此函数简化了这个过程,避免每次都手动传入store参数
|
||||||
*/
|
*/
|
||||||
export function usePermissionStoreHook() {
|
export function usePermissionStoreHook() {
|
||||||
return usePermissionStore(store);
|
return usePermissionStore(store);
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ import { useUserStore } from "@/store/modules/user.store";
|
|||||||
import { formatGrowthRate } from "@/utils";
|
import { formatGrowthRate } from "@/utils";
|
||||||
import { useTransition, useDateFormat } from "@vueuse/core";
|
import { useTransition, useDateFormat } from "@vueuse/core";
|
||||||
import { Connection, Failed } from "@element-plus/icons-vue";
|
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();
|
const { onlineUserCount, lastUpdateTime, isConnected } = useOnlineCount();
|
||||||
|
|||||||
@@ -142,7 +142,7 @@
|
|||||||
import { useDictStoreHook } from "@/store/modules/dict.store";
|
import { useDictStoreHook } from "@/store/modules/dict.store";
|
||||||
import { useDateFormat } from "@vueuse/core";
|
import { useDateFormat } from "@vueuse/core";
|
||||||
import DictAPI, { DictItemForm } from "@/api/system/dict.api";
|
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";
|
const DICT_CODE = "gender";
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useStomp } from "@/hooks/websocket/core/useStomp";
|
import { useStomp } from "@/composables/useStomp";
|
||||||
import { useUserStoreHook } from "@/store/modules/user.store";
|
import { useUserStoreHook } from "@/store/modules/user.store";
|
||||||
|
|
||||||
const userStore = useUserStoreHook();
|
const userStore = useUserStoreHook();
|
||||||
@@ -117,7 +117,7 @@ const queneMessage = ref("Hi, " + userStore.userInfo.username + " 这里是点
|
|||||||
const receiver = ref("root");
|
const receiver = ref("root");
|
||||||
|
|
||||||
// 调用 useStomp hook,默认使用 socketEndpoint 和 token(此处用 getAccessToken())
|
// 调用 useStomp hook,默认使用 socketEndpoint 和 token(此处用 getAccessToken())
|
||||||
const { isConnected, connect, subscribe, disconnect, client } = useStomp({
|
const { isConnected, connect, subscribe, disconnect } = useStomp({
|
||||||
debug: true,
|
debug: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -166,11 +166,9 @@ function disconnectWebSocket() {
|
|||||||
|
|
||||||
// 发送广播消息
|
// 发送广播消息
|
||||||
function sendToAll() {
|
function sendToAll() {
|
||||||
if (client.value && client.value.connected) {
|
if (isConnected.value) {
|
||||||
client.value.publish({
|
// 直接使用订阅模式处理广播消息
|
||||||
destination: "/topic/notice",
|
subscribe("/app/broadcast", () => {});
|
||||||
body: topicMessage.value,
|
|
||||||
});
|
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
sender: userStore.userInfo.username,
|
sender: userStore.userInfo.username,
|
||||||
content: topicMessage.value,
|
content: topicMessage.value,
|
||||||
@@ -180,11 +178,9 @@ function sendToAll() {
|
|||||||
|
|
||||||
// 发送点对点消息
|
// 发送点对点消息
|
||||||
function sendToUser() {
|
function sendToUser() {
|
||||||
if (client.value && client.value.connected) {
|
if (isConnected.value) {
|
||||||
client.value.publish({
|
// 使用订阅模式处理点对点消息
|
||||||
destination: "/app/sendToUser/" + receiver.value,
|
subscribe(`/app/sendToUser/${receiver.value}`, () => {});
|
||||||
body: queneMessage.value,
|
|
||||||
});
|
|
||||||
messages.value.push({
|
messages.value.push({
|
||||||
sender: userStore.userInfo.username,
|
sender: userStore.userInfo.username,
|
||||||
content: queneMessage.value,
|
content: queneMessage.value,
|
||||||
|
|||||||
Reference in New Issue
Block a user