refactor: decouple notification logic and align my notice endpoint
This commit is contained in:
@@ -46,7 +46,7 @@ const NoticeAPI = {
|
||||
/** 获取我的通知分页列表 */
|
||||
getMyNoticePage(queryParams?: NoticePageQuery) {
|
||||
return request<any, PageResult<NoticePageVO[]>>({
|
||||
url: `${NOTICE_BASE_URL}/my-page`,
|
||||
url: `${NOTICE_BASE_URL}/my`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
|
||||
@@ -79,84 +79,32 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import NoticeAPI, { NoticePageVO, NoticeDetailVO } from "@/api/system/notice-api";
|
||||
import router from "@/router";
|
||||
import { useNotificationCenter } from "@/composables/notice/useNotificationCenter";
|
||||
|
||||
const noticeList = ref<NoticePageVO[]>([]);
|
||||
const noticeDialogVisible = ref(false);
|
||||
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
||||
const {
|
||||
noticeList,
|
||||
noticeDialogVisible,
|
||||
noticeDetail,
|
||||
fetchMyNotices,
|
||||
readNotice,
|
||||
markAllAsRead,
|
||||
viewMore,
|
||||
} = useNotificationCenter();
|
||||
|
||||
import { useStomp } from "@/composables/websocket/useStomp";
|
||||
const { subscribe, unsubscribe, isConnected } = useStomp();
|
||||
|
||||
watch(
|
||||
() => isConnected.value,
|
||||
(connected) => {
|
||||
if (connected) {
|
||||
subscribe("/user/queue/message", (message: any) => {
|
||||
console.log("收到通知消息:", message);
|
||||
const data = JSON.parse(message.body);
|
||||
const id = data.id;
|
||||
if (!noticeList.value.some((notice) => notice.id == id)) {
|
||||
noticeList.value.unshift({
|
||||
id,
|
||||
title: data.title,
|
||||
type: data.type,
|
||||
publishTime: data.publishTime,
|
||||
});
|
||||
|
||||
ElNotification({
|
||||
title: "您收到一条新的通知消息!",
|
||||
message: data.title,
|
||||
type: "success",
|
||||
position: "bottom-right",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取我的通知公告
|
||||
*/
|
||||
function featchMyNotice() {
|
||||
NoticeAPI.getMyNoticePage({ pageNum: 1, pageSize: 5, isRead: 0 }).then((data) => {
|
||||
noticeList.value = data.list;
|
||||
});
|
||||
}
|
||||
|
||||
// 阅读通知公告
|
||||
function handleReadNotice(id: string) {
|
||||
NoticeAPI.getDetail(id).then((data) => {
|
||||
noticeDialogVisible.value = true;
|
||||
noticeDetail.value = data;
|
||||
// 标记为已读
|
||||
const index = noticeList.value.findIndex((notice) => notice.id === id);
|
||||
if (index >= 0) {
|
||||
noticeList.value.splice(index, 1);
|
||||
}
|
||||
});
|
||||
readNotice(id);
|
||||
}
|
||||
|
||||
// 查看更多
|
||||
function handleViewMoreNotice() {
|
||||
router.push({ name: "MyNotice" });
|
||||
viewMore();
|
||||
}
|
||||
|
||||
// 全部已读
|
||||
function handleMarkAllAsRead() {
|
||||
NoticeAPI.readAll().then(() => {
|
||||
noticeList.value = [];
|
||||
});
|
||||
markAllAsRead();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
featchMyNotice();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
unsubscribe("/user/queue/message");
|
||||
fetchMyNotices();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -265,6 +265,5 @@ export function useAiAction(options: UseAiActionOptions = {}) {
|
||||
executeAiAction,
|
||||
executeCommand,
|
||||
handleAutoSearch,
|
||||
init,
|
||||
};
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
106
src/composables/notice/useNotificationCenter.ts
Normal file
106
src/composables/notice/useNotificationCenter.ts
Normal file
@@ -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<NoticePageVO[]>([]);
|
||||
const noticeDialogVisible = ref(false);
|
||||
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
||||
|
||||
const { subscribe, unsubscribe, isConnected } = useStomp();
|
||||
let subscribed = false;
|
||||
|
||||
function normalizeQuery(params?: Partial<NoticePageQuery>): NoticePageQuery {
|
||||
return {
|
||||
pageNum: 1,
|
||||
pageSize: DEFAULT_PAGE_SIZE,
|
||||
isRead: 0,
|
||||
...(params || {}),
|
||||
} as NoticePageQuery;
|
||||
}
|
||||
|
||||
async function fetchMyNotices(params?: Partial<NoticePageQuery>) {
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -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 },
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,18 +8,18 @@
|
||||
v-model="queryParams.title"
|
||||
placeholder="关键字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery()"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button type="primary" @click="handleQuery()">
|
||||
<el-button type="primary" @click="handleQuery">
|
||||
<template #icon>
|
||||
<Search />
|
||||
</template>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleResetQuery()">
|
||||
<el-button @click="handleResetQuery">
|
||||
<template #icon>
|
||||
<Refresh />
|
||||
</template>
|
||||
@@ -44,7 +44,6 @@
|
||||
<DictLabel v-model="scope.row.type" code="notice_type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="发布人" prop="publisherName" width="100" />
|
||||
<el-table-column align="center" label="通知等级" width="100">
|
||||
<template #default="scope">
|
||||
<DictLabel v-model="scope.row.level" code="notice_level" />
|
||||
@@ -57,7 +56,6 @@
|
||||
prop="publishTime"
|
||||
width="150"
|
||||
/>
|
||||
|
||||
<el-table-column align="center" label="发布人" prop="publisherName" width="150" />
|
||||
<el-table-column align="center" label="状态" width="100">
|
||||
<template #default="scope">
|
||||
@@ -79,7 +77,7 @@
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="handleQuery()"
|
||||
@pagination="handleQuery"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
@@ -115,11 +113,16 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import NoticeAPI, { NoticePageVO, NoticePageQuery, NoticeDetailVO } from "@/api/system/notice-api";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import NoticeAPI, {
|
||||
type NoticePageVO,
|
||||
type NoticePageQuery,
|
||||
type NoticeDetailVO,
|
||||
} from "@/api/system/notice-api";
|
||||
|
||||
const queryFormRef = ref();
|
||||
const pageData = ref<NoticePageVO[]>([]);
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
|
||||
@@ -131,32 +134,35 @@ const queryParams = reactive<NoticePageQuery>({
|
||||
const noticeDialogVisible = ref(false);
|
||||
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
||||
|
||||
// 查询通知公告
|
||||
function handleQuery() {
|
||||
async function handleQuery() {
|
||||
loading.value = true;
|
||||
NoticeAPI.getMyNoticePage(queryParams)
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
try {
|
||||
const data = await NoticeAPI.getMyNoticePage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total;
|
||||
} catch (error) {
|
||||
ElMessage.error("获取通知列表失败");
|
||||
console.error("获取我的通知失败", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 重置通知公告查询
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value!.resetFields();
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.pageNum = 1;
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
// 阅读通知公告
|
||||
function handleReadNotice(id: string) {
|
||||
NoticeAPI.getDetail(id).then((data) => {
|
||||
noticeDialogVisible.value = true;
|
||||
async function handleReadNotice(id: string) {
|
||||
try {
|
||||
const data = await NoticeAPI.getDetail(id);
|
||||
noticeDetail.value = data;
|
||||
});
|
||||
noticeDialogVisible.value = true;
|
||||
} catch (error) {
|
||||
ElMessage.error("获取通知详情失败");
|
||||
console.error("获取通知详情失败", error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -168,6 +174,7 @@ onMounted(() => {
|
||||
:deep(.el-dialog__header) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notice-detail {
|
||||
&__wrapper {
|
||||
padding: 0 20px;
|
||||
Reference in New Issue
Block a user