From f8f194c13cd196add814a0b10bc496fa4e717151 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Thu, 27 Nov 2025 22:07:07 +0800 Subject: [PATCH] refactor: decouple notification logic and align my notice endpoint --- mock/notice.mock.ts | 2 +- src/api/system/notice-api.ts | 2 +- src/components/Notification/index.vue | 80 +++---------- src/composables/{ => ai}/useAiAction.ts | 1 - src/composables/index.ts | 6 +- .../notice/useNotificationCenter.ts | 106 ++++++++++++++++++ .../{ => table}/useTableSelection.ts | 0 src/router/index.ts | 2 +- src/views/login/index.vue | 3 +- .../MyNotice.vue => profile/notice/index.vue} | 57 +++++----- 10 files changed, 160 insertions(+), 99 deletions(-) rename src/composables/{ => ai}/useAiAction.ts (99%) create mode 100644 src/composables/notice/useNotificationCenter.ts rename src/composables/{ => table}/useTableSelection.ts (100%) rename src/views/{system/notice/components/MyNotice.vue => profile/notice/index.vue} (82%) diff --git a/mock/notice.mock.ts b/mock/notice.mock.ts index c140c163..840010bc 100644 --- a/mock/notice.mock.ts +++ b/mock/notice.mock.ts @@ -211,7 +211,7 @@ export default defineMock([ // 我的通知分页列表 { - url: "notices/my-page", + url: "notices/my", method: ["GET"], body: { code: "00000", diff --git a/src/api/system/notice-api.ts b/src/api/system/notice-api.ts index fbac939e..c8eb7140 100644 --- a/src/api/system/notice-api.ts +++ b/src/api/system/notice-api.ts @@ -46,7 +46,7 @@ const NoticeAPI = { /** 获取我的通知分页列表 */ getMyNoticePage(queryParams?: NoticePageQuery) { return request>({ - url: `${NOTICE_BASE_URL}/my-page`, + url: `${NOTICE_BASE_URL}/my`, method: "get", params: queryParams, }); diff --git a/src/components/Notification/index.vue b/src/components/Notification/index.vue index 8d3babaf..bd2ee0fe 100644 --- a/src/components/Notification/index.vue +++ b/src/components/Notification/index.vue @@ -79,84 +79,32 @@ diff --git a/src/composables/useAiAction.ts b/src/composables/ai/useAiAction.ts similarity index 99% rename from src/composables/useAiAction.ts rename to src/composables/ai/useAiAction.ts index c3383052..1b505716 100644 --- a/src/composables/useAiAction.ts +++ b/src/composables/ai/useAiAction.ts @@ -265,6 +265,5 @@ export function useAiAction(options: UseAiActionOptions = {}) { executeAiAction, executeCommand, handleAutoSearch, - init, }; } diff --git a/src/composables/index.ts b/src/composables/index.ts index 4bff69b9..3fb168b6 100644 --- a/src/composables/index.ts +++ b/src/composables/index.ts @@ -8,7 +8,7 @@ export { useLayout } from "./layout/useLayout"; export { useLayoutMenu } from "./layout/useLayoutMenu"; export { useDeviceDetection } from "./layout/useDeviceDetection"; -export { useAiAction } from "./useAiAction"; -export type { UseAiActionOptions, AiActionHandler } from "./useAiAction"; +export { useAiAction } from "./ai/useAiAction"; +export type { UseAiActionOptions, AiActionHandler } from "./ai/useAiAction"; -export { useTableSelection } from "./useTableSelection"; +export { useTableSelection } from "./table/useTableSelection"; diff --git a/src/composables/notice/useNotificationCenter.ts b/src/composables/notice/useNotificationCenter.ts new file mode 100644 index 00000000..e6e48e67 --- /dev/null +++ b/src/composables/notice/useNotificationCenter.ts @@ -0,0 +1,106 @@ +import { ref, onMounted, onBeforeUnmount } from "vue"; +import NoticeAPI, { + type NoticePageVO, + type NoticeDetailVO, + type NoticePageQuery, +} from "@/api/system/notice-api"; +import { useStomp } from "@/composables/websocket/useStomp"; +import router from "@/router"; + +const DEFAULT_PAGE_SIZE = 5; + +const noticeList = ref([]); +const noticeDialogVisible = ref(false); +const noticeDetail = ref(null); + +const { subscribe, unsubscribe, isConnected } = useStomp(); +let subscribed = false; + +function normalizeQuery(params?: Partial): NoticePageQuery { + return { + pageNum: 1, + pageSize: DEFAULT_PAGE_SIZE, + isRead: 0, + ...(params || {}), + } as NoticePageQuery; +} + +async function fetchMyNotices(params?: Partial) { + const query = normalizeQuery(params); + const page = await NoticeAPI.getMyNoticePage(query); + noticeList.value = page.list || []; +} + +async function readNotice(id: string) { + const data = await NoticeAPI.getDetail(id); + noticeDetail.value = data; + noticeDialogVisible.value = true; + + const index = noticeList.value.findIndex((item) => item.id === id); + if (index >= 0) { + noticeList.value.splice(index, 1); + } +} + +async function markAllAsRead() { + await NoticeAPI.readAll(); + noticeList.value = []; +} + +function viewMore() { + router.push({ name: "MyNotice" }); +} + +function setupStompSubscription() { + if (subscribed || !isConnected.value) return; + + subscribe("/user/queue/message", (message: any) => { + try { + const data = JSON.parse(message.body || "{}"); + const id = data.id; + if (!id) return; + + if (!noticeList.value.some((item) => item.id === id)) { + noticeList.value.unshift({ + id, + title: data.title, + type: data.type, + publishTime: data.publishTime, + } as NoticePageVO); + + ElNotification({ + title: "您收到一条新的通知消息!", + message: data.title, + type: "success", + position: "bottom-right", + }); + } + } catch (e) { + console.error("解析通知消息失败", e); + } + }); + + subscribed = true; +} + +export function useNotificationCenter() { + onMounted(() => { + fetchMyNotices(); + setupStompSubscription(); + }); + + onBeforeUnmount(() => { + unsubscribe("/user/queue/message"); + subscribed = false; + }); + + return { + noticeList, + noticeDialogVisible, + noticeDetail, + fetchMyNotices, + readNotice, + markAllAsRead, + viewMore, + }; +} diff --git a/src/composables/useTableSelection.ts b/src/composables/table/useTableSelection.ts similarity index 100% rename from src/composables/useTableSelection.ts rename to src/composables/table/useTableSelection.ts diff --git a/src/router/index.ts b/src/router/index.ts index 2f156190..572a2234 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -61,7 +61,7 @@ export const constantRoutes: RouteRecordRaw[] = [ { path: "my-notice", name: "MyNotice", - component: () => import("@/views/system/notice/components/MyNotice.vue"), + component: () => import("@/views/profile/notice/index.vue"), meta: { title: "我的通知", icon: "user", hidden: true }, }, { diff --git a/src/views/login/index.vue b/src/views/login/index.vue index d2f044bf..a7677f90 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -323,7 +323,8 @@ onBeforeUnmount(() => { flex-direction: column; gap: 1.5rem; justify-content: flex-start; - width: min(560px, 100%); + justify-self: end; + width: min(520px, 100%); padding: clamp(2rem, 3vw, 2.75rem); margin-inline: auto; background: rgba(255, 255, 255, 0.95); diff --git a/src/views/system/notice/components/MyNotice.vue b/src/views/profile/notice/index.vue similarity index 82% rename from src/views/system/notice/components/MyNotice.vue rename to src/views/profile/notice/index.vue index 4bde78d3..208b8618 100644 --- a/src/views/system/notice/components/MyNotice.vue +++ b/src/views/profile/notice/index.vue @@ -8,18 +8,18 @@ v-model="queryParams.title" placeholder="关键字" clearable - @keyup.enter="handleQuery()" + @keyup.enter="handleQuery" /> - + 搜索 - + @@ -44,7 +44,6 @@ -