update 消息盒子支持按类型分组切换展示
This commit is contained in:
@@ -1,12 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="state.loading" class="layout-navbars-breadcrumb-user-news">
|
<div v-loading="state.loading" class="layout-navbars-breadcrumb-user-news">
|
||||||
<div class="head-box">
|
<div class="head-box">
|
||||||
<div class="head-box-title">通知公告</div>
|
<div class="head-box-title">消息盒子</div>
|
||||||
<div class="head-box-btn" @click="readAll">全部已读</div>
|
<div class="head-box-btn" @click="readAll">全部已读</div>
|
||||||
</div>
|
</div>
|
||||||
|
<el-tabs v-model="activeTab" class="message-tabs" stretch>
|
||||||
|
<el-tab-pane :label="`系统 ${tabCount.system}`" name="system"></el-tab-pane>
|
||||||
|
<el-tab-pane :label="`通知 ${tabCount.notice}`" name="notice"></el-tab-pane>
|
||||||
|
<el-tab-pane :label="`工作 ${tabCount.workflow}`" name="workflow"></el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
<div v-loading="state.loading" class="content-box">
|
<div v-loading="state.loading" class="content-box">
|
||||||
<template v-if="newsList.length > 0">
|
<template v-if="currentNewsList.length > 0">
|
||||||
<div v-for="(v, k) in newsList" :key="k" class="content-box-item" @click="onNewsClick(k)">
|
<div v-for="(v, k) in currentNewsList" :key="k" class="content-box-item" @click="onNewsClick(k)">
|
||||||
<div class="item-conten">
|
<div class="item-conten">
|
||||||
<div class="content-box-title">{{ v.title || '消息' }}</div>
|
<div class="content-box-title">{{ v.title || '消息' }}</div>
|
||||||
<div>{{ v.message }}</div>
|
<div>{{ v.message }}</div>
|
||||||
@@ -18,15 +23,15 @@
|
|||||||
<span v-else class="el-tag el-tag--danger el-tag--mini read">未读</span>
|
<span v-else class="el-tag el-tag--danger el-tag--mini read">未读</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-empty v-else :description="'消息为空'"></el-empty>
|
<el-empty v-else :description="emptyDescription"></el-empty>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="newsList.length > 0" class="foot-box" @click="onGoToGiteeClick">前往gitee</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutBreadcrumbUserNews">
|
<script setup lang="ts" name="layoutBreadcrumbUserNews">
|
||||||
import { useNoticeStore } from '@/store/modules/notice';
|
import { useNoticeStore } from '@/store/modules/notice';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
|
import { NOTICE_GROUP } from '@/utils/push-message';
|
||||||
|
|
||||||
const noticeStore = useNoticeStore();
|
const noticeStore = useNoticeStore();
|
||||||
const { readAll } = useNoticeStore();
|
const { readAll } = useNoticeStore();
|
||||||
@@ -35,24 +40,35 @@ const { readAll } = useNoticeStore();
|
|||||||
const state = reactive({
|
const state = reactive({
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
const newsList = ref([]) as any;
|
const activeTab = ref<string>(NOTICE_GROUP.SYSTEM);
|
||||||
|
const newsList = computed(() => noticeStore.state.notices);
|
||||||
|
|
||||||
/**
|
const tabCount = computed(() => ({
|
||||||
* 初始化数据
|
system: newsList.value.filter((item: any) => (item.category || NOTICE_GROUP.SYSTEM) === NOTICE_GROUP.SYSTEM).length,
|
||||||
* @returns
|
notice: newsList.value.filter((item: any) => item.category === NOTICE_GROUP.NOTICE).length,
|
||||||
*/
|
workflow: newsList.value.filter((item: any) => item.category === NOTICE_GROUP.WORKFLOW).length
|
||||||
const getTableData = async () => {
|
}));
|
||||||
state.loading = true;
|
|
||||||
newsList.value = noticeStore.state.notices;
|
const currentNewsList = computed(() => {
|
||||||
state.loading = false;
|
return newsList.value.filter((item: any) => {
|
||||||
};
|
return (item.category || NOTICE_GROUP.SYSTEM) === activeTab.value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const emptyDescription = computed(() => {
|
||||||
|
if (activeTab.value === NOTICE_GROUP.NOTICE) {
|
||||||
|
return '暂无通知公告消息';
|
||||||
|
}
|
||||||
|
if (activeTab.value === NOTICE_GROUP.WORKFLOW) {
|
||||||
|
return '暂无工作流消息';
|
||||||
|
}
|
||||||
|
return '暂无系统消息';
|
||||||
|
});
|
||||||
|
|
||||||
//点击消息,写入已读
|
//点击消息,写入已读
|
||||||
const onNewsClick = async (item: any) => {
|
const onNewsClick = async (item: any) => {
|
||||||
newsList.value[item].read = true;
|
currentNewsList.value[item].read = true;
|
||||||
//并且写入pinia
|
const current = currentNewsList.value[item];
|
||||||
noticeStore.state.notices = newsList.value;
|
|
||||||
const current = newsList.value[item];
|
|
||||||
if (current?.path) {
|
if (current?.path) {
|
||||||
await router.push({
|
await router.push({
|
||||||
path: current.path,
|
path: current.path,
|
||||||
@@ -60,17 +76,6 @@ const onNewsClick = async (item: any) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 前往通知中心点击
|
|
||||||
const onGoToGiteeClick = () => {
|
|
||||||
window.open('https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/');
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
nextTick(() => {
|
|
||||||
getTableData();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -79,7 +84,7 @@ onMounted(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
.head-box {
|
.head-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
border-bottom: 1px solid rgba(148, 163, 184, 0.14);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -107,11 +112,34 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-tabs {
|
||||||
|
padding-top: 6px;
|
||||||
|
|
||||||
|
:deep(.el-tabs__header) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-wrap::after) {
|
||||||
|
background: rgba(148, 163, 184, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__item) {
|
||||||
|
font-size: 12px;
|
||||||
|
height: 34px;
|
||||||
|
color: var(--app-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__item.is-active) {
|
||||||
|
color: var(--app-accent-strong);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.content-box {
|
.content-box {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 8px 0;
|
padding: 8px 0 0;
|
||||||
|
|
||||||
.content-box-item {
|
.content-box-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -167,23 +195,6 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.foot-box {
|
|
||||||
height: 40px;
|
|
||||||
color: var(--app-accent-strong);
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.8;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
border-top: 1px solid rgba(148, 163, 184, 0.14);
|
|
||||||
margin-top: 4px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:deep(.el-empty__description p) {
|
:deep(.el-empty__description p) {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { reactive } from 'vue';
|
|||||||
|
|
||||||
export interface NoticeItem {
|
export interface NoticeItem {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
category?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
read: boolean;
|
read: boolean;
|
||||||
|
|||||||
@@ -5,6 +5,20 @@ export const PUSH_MESSAGE_TYPE = {
|
|||||||
CUSTOM: 'custom'
|
CUSTOM: 'custom'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const PUSH_MESSAGE_SOURCE = {
|
||||||
|
BACKEND: 'backend',
|
||||||
|
NOTICE: 'notice',
|
||||||
|
WORKFLOW: 'workflow',
|
||||||
|
LLM: 'llm',
|
||||||
|
CLIENT: 'client'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const NOTICE_GROUP = {
|
||||||
|
SYSTEM: 'system',
|
||||||
|
NOTICE: 'notice',
|
||||||
|
WORKFLOW: 'workflow'
|
||||||
|
} as const;
|
||||||
|
|
||||||
export interface PushMessagePayload {
|
export interface PushMessagePayload {
|
||||||
type?: string;
|
type?: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
@@ -45,3 +59,24 @@ export const parsePushMessage = (raw: string): PushMessagePayload => {
|
|||||||
export const shouldAppendNotice = (payload: PushMessagePayload) => {
|
export const shouldAppendNotice = (payload: PushMessagePayload) => {
|
||||||
return MESSAGE_CENTER_TYPES.has(payload.type ?? PUSH_MESSAGE_TYPE.MESSAGE);
|
return MESSAGE_CENTER_TYPES.has(payload.type ?? PUSH_MESSAGE_TYPE.MESSAGE);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const resolveNoticeGroup = (payload: PushMessagePayload) => {
|
||||||
|
if (payload.type === PUSH_MESSAGE_TYPE.NOTICE || payload.source === PUSH_MESSAGE_SOURCE.NOTICE) {
|
||||||
|
return NOTICE_GROUP.NOTICE;
|
||||||
|
}
|
||||||
|
if (payload.source === PUSH_MESSAGE_SOURCE.WORKFLOW) {
|
||||||
|
return NOTICE_GROUP.WORKFLOW;
|
||||||
|
}
|
||||||
|
return NOTICE_GROUP.SYSTEM;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resolveNoticeTitle = (payload: PushMessagePayload) => {
|
||||||
|
const group = resolveNoticeGroup(payload);
|
||||||
|
if (group === NOTICE_GROUP.NOTICE) {
|
||||||
|
return '通知公告消息';
|
||||||
|
}
|
||||||
|
if (group === NOTICE_GROUP.WORKFLOW) {
|
||||||
|
return '工作流消息';
|
||||||
|
}
|
||||||
|
return '系统消息';
|
||||||
|
};
|
||||||
|
|||||||
+5
-3
@@ -1,7 +1,7 @@
|
|||||||
import { getToken } from '@/utils/auth';
|
import { getToken } from '@/utils/auth';
|
||||||
import { ElNotification } from 'element-plus';
|
import { ElNotification } from 'element-plus';
|
||||||
import { useNoticeStore } from '@/store/modules/notice';
|
import { useNoticeStore } from '@/store/modules/notice';
|
||||||
import { parsePushMessage, PUSH_MESSAGE_TYPE, shouldAppendNotice } from '@/utils/push-message';
|
import { parsePushMessage, resolveNoticeGroup, resolveNoticeTitle, shouldAppendNotice } from '@/utils/push-message';
|
||||||
|
|
||||||
let closePushConnection: (() => void) | undefined;
|
let closePushConnection: (() => void) | undefined;
|
||||||
|
|
||||||
@@ -10,8 +10,10 @@ const appendNotice = (raw: string) => {
|
|||||||
if (!shouldAppendNotice(payload)) {
|
if (!shouldAppendNotice(payload)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const title = resolveNoticeTitle(payload);
|
||||||
useNoticeStore().addNotice({
|
useNoticeStore().addNotice({
|
||||||
title: payload.type === PUSH_MESSAGE_TYPE.NOTICE ? '通知公告' : '系统消息',
|
title,
|
||||||
|
category: resolveNoticeGroup(payload),
|
||||||
type: payload.type,
|
type: payload.type,
|
||||||
source: payload.source,
|
source: payload.source,
|
||||||
message: payload.message ?? '',
|
message: payload.message ?? '',
|
||||||
@@ -23,7 +25,7 @@ const appendNotice = (raw: string) => {
|
|||||||
time: new Date(payload.timestamp ?? Date.now()).toLocaleString()
|
time: new Date(payload.timestamp ?? Date.now()).toLocaleString()
|
||||||
});
|
});
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: payload.type === PUSH_MESSAGE_TYPE.NOTICE ? '通知公告' : '消息',
|
title,
|
||||||
message: payload.message ?? '',
|
message: payload.message ?? '',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
duration: 3000
|
duration: 3000
|
||||||
|
|||||||
Reference in New Issue
Block a user