refactor: decouple notification logic and align my notice endpoint
This commit is contained in:
@@ -211,7 +211,7 @@ export default defineMock([
|
|||||||
|
|
||||||
// 我的通知分页列表
|
// 我的通知分页列表
|
||||||
{
|
{
|
||||||
url: "notices/my-page",
|
url: "notices/my",
|
||||||
method: ["GET"],
|
method: ["GET"],
|
||||||
body: {
|
body: {
|
||||||
code: "00000",
|
code: "00000",
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const NoticeAPI = {
|
|||||||
/** 获取我的通知分页列表 */
|
/** 获取我的通知分页列表 */
|
||||||
getMyNoticePage(queryParams?: NoticePageQuery) {
|
getMyNoticePage(queryParams?: NoticePageQuery) {
|
||||||
return request<any, PageResult<NoticePageVO[]>>({
|
return request<any, PageResult<NoticePageVO[]>>({
|
||||||
url: `${NOTICE_BASE_URL}/my-page`,
|
url: `${NOTICE_BASE_URL}/my`,
|
||||||
method: "get",
|
method: "get",
|
||||||
params: queryParams,
|
params: queryParams,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -79,84 +79,32 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NoticeAPI, { NoticePageVO, NoticeDetailVO } from "@/api/system/notice-api";
|
import { useNotificationCenter } from "@/composables/notice/useNotificationCenter";
|
||||||
import router from "@/router";
|
|
||||||
|
|
||||||
const noticeList = ref<NoticePageVO[]>([]);
|
const {
|
||||||
const noticeDialogVisible = ref(false);
|
noticeList,
|
||||||
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
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) {
|
function handleReadNotice(id: string) {
|
||||||
NoticeAPI.getDetail(id).then((data) => {
|
readNotice(id);
|
||||||
noticeDialogVisible.value = true;
|
|
||||||
noticeDetail.value = data;
|
|
||||||
// 标记为已读
|
|
||||||
const index = noticeList.value.findIndex((notice) => notice.id === id);
|
|
||||||
if (index >= 0) {
|
|
||||||
noticeList.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看更多
|
|
||||||
function handleViewMoreNotice() {
|
function handleViewMoreNotice() {
|
||||||
router.push({ name: "MyNotice" });
|
viewMore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全部已读
|
|
||||||
function handleMarkAllAsRead() {
|
function handleMarkAllAsRead() {
|
||||||
NoticeAPI.readAll().then(() => {
|
markAllAsRead();
|
||||||
noticeList.value = [];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
featchMyNotice();
|
fetchMyNotices();
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
unsubscribe("/user/queue/message");
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,5 @@ export function useAiAction(options: UseAiActionOptions = {}) {
|
|||||||
executeAiAction,
|
executeAiAction,
|
||||||
executeCommand,
|
executeCommand,
|
||||||
handleAutoSearch,
|
handleAutoSearch,
|
||||||
init,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ export { useLayout } from "./layout/useLayout";
|
|||||||
export { useLayoutMenu } from "./layout/useLayoutMenu";
|
export { useLayoutMenu } from "./layout/useLayoutMenu";
|
||||||
export { useDeviceDetection } from "./layout/useDeviceDetection";
|
export { useDeviceDetection } from "./layout/useDeviceDetection";
|
||||||
|
|
||||||
export { useAiAction } from "./useAiAction";
|
export { useAiAction } from "./ai/useAiAction";
|
||||||
export type { UseAiActionOptions, AiActionHandler } from "./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",
|
path: "my-notice",
|
||||||
name: "MyNotice",
|
name: "MyNotice",
|
||||||
component: () => import("@/views/system/notice/components/MyNotice.vue"),
|
component: () => import("@/views/profile/notice/index.vue"),
|
||||||
meta: { title: "我的通知", icon: "user", hidden: true },
|
meta: { title: "我的通知", icon: "user", hidden: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -323,7 +323,8 @@ onBeforeUnmount(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
width: min(560px, 100%);
|
justify-self: end;
|
||||||
|
width: min(520px, 100%);
|
||||||
padding: clamp(2rem, 3vw, 2.75rem);
|
padding: clamp(2rem, 3vw, 2.75rem);
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
|||||||
@@ -8,18 +8,18 @@
|
|||||||
v-model="queryParams.title"
|
v-model="queryParams.title"
|
||||||
placeholder="关键字"
|
placeholder="关键字"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter="handleQuery()"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item class="search-buttons">
|
<el-form-item class="search-buttons">
|
||||||
<el-button type="primary" @click="handleQuery()">
|
<el-button type="primary" @click="handleQuery">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Search />
|
<Search />
|
||||||
</template>
|
</template>
|
||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="handleResetQuery()">
|
<el-button @click="handleResetQuery">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Refresh />
|
<Refresh />
|
||||||
</template>
|
</template>
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
<DictLabel v-model="scope.row.type" code="notice_type" />
|
<DictLabel v-model="scope.row.type" code="notice_type" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="发布人" prop="publisherName" width="100" />
|
|
||||||
<el-table-column align="center" label="通知等级" width="100">
|
<el-table-column align="center" label="通知等级" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<DictLabel v-model="scope.row.level" code="notice_level" />
|
<DictLabel v-model="scope.row.level" code="notice_level" />
|
||||||
@@ -57,7 +56,6 @@
|
|||||||
prop="publishTime"
|
prop="publishTime"
|
||||||
width="150"
|
width="150"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-table-column align="center" label="发布人" prop="publisherName" width="150" />
|
<el-table-column align="center" label="发布人" prop="publisherName" width="150" />
|
||||||
<el-table-column align="center" label="状态" width="100">
|
<el-table-column align="center" label="状态" width="100">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -79,7 +77,7 @@
|
|||||||
v-model:total="total"
|
v-model:total="total"
|
||||||
v-model:page="queryParams.pageNum"
|
v-model:page="queryParams.pageNum"
|
||||||
v-model:limit="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
@pagination="handleQuery()"
|
@pagination="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
@@ -115,11 +113,16 @@ defineOptions({
|
|||||||
inheritAttrs: false,
|
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 queryFormRef = ref();
|
||||||
const pageData = ref<NoticePageVO[]>([]);
|
const pageData = ref<NoticePageVO[]>([]);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
|
|
||||||
@@ -131,32 +134,35 @@ const queryParams = reactive<NoticePageQuery>({
|
|||||||
const noticeDialogVisible = ref(false);
|
const noticeDialogVisible = ref(false);
|
||||||
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
const noticeDetail = ref<NoticeDetailVO | null>(null);
|
||||||
|
|
||||||
// 查询通知公告
|
async function handleQuery() {
|
||||||
function handleQuery() {
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
NoticeAPI.getMyNoticePage(queryParams)
|
try {
|
||||||
.then((data) => {
|
const data = await NoticeAPI.getMyNoticePage(queryParams);
|
||||||
pageData.value = data.list;
|
pageData.value = data.list;
|
||||||
total.value = data.total;
|
total.value = data.total;
|
||||||
})
|
} catch (error) {
|
||||||
.finally(() => {
|
ElMessage.error("获取通知列表失败");
|
||||||
loading.value = false;
|
console.error("获取我的通知失败", error);
|
||||||
});
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重置通知公告查询
|
|
||||||
function handleResetQuery() {
|
function handleResetQuery() {
|
||||||
queryFormRef.value!.resetFields();
|
queryFormRef.value?.resetFields();
|
||||||
queryParams.pageNum = 1;
|
queryParams.pageNum = 1;
|
||||||
handleQuery();
|
handleQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 阅读通知公告
|
async function handleReadNotice(id: string) {
|
||||||
function handleReadNotice(id: string) {
|
try {
|
||||||
NoticeAPI.getDetail(id).then((data) => {
|
const data = await NoticeAPI.getDetail(id);
|
||||||
noticeDialogVisible.value = true;
|
|
||||||
noticeDetail.value = data;
|
noticeDetail.value = data;
|
||||||
});
|
noticeDialogVisible.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error("获取通知详情失败");
|
||||||
|
console.error("获取通知详情失败", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -168,6 +174,7 @@ onMounted(() => {
|
|||||||
:deep(.el-dialog__header) {
|
:deep(.el-dialog__header) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-detail {
|
.notice-detail {
|
||||||
&__wrapper {
|
&__wrapper {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
Reference in New Issue
Block a user