feat(message): 优化消息通知功能

- 修改标题为"IOT设备管理系统"
- 优化消息通知组件,增加未读消息数量显示- 添加消息详情查看功能
- 更新消息列表展示,增加操作按钮
- 调整消息接口路径
This commit is contained in:
Kven 2025-03-26 21:19:40 +08:00
parent 69a0144ebd
commit 13360113ef
8 changed files with 144 additions and 65 deletions

View File

@ -8,7 +8,7 @@
href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>基于MQTT的IOT设备管理系统</title>
<title>IOT设备管理系统</title>
</head>
<body>
<div id="app"></div>

View File

@ -1,39 +0,0 @@
import axios from 'axios';
export interface MessageRecord {
id: number;
type: string;
title: string;
subTitle: string;
avatar?: string;
content: string;
time: string;
status: 0 | 1;
messageType?: number;
}
export type MessageListType = MessageRecord[];
export function queryMessageList() {
return axios.post<MessageListType>('/api/message/list');
}
interface MessageStatus {
ids: string[];
}
// 批量设置消息已读
export function setMessageStatus(data: MessageStatus) {
return axios.post<MessageListType>('/api/message/read', data);
}
export interface ChatRecord {
id: number;
username: string;
content: string;
time: string;
isCollect: boolean;
}
export function queryChatList() {
return axios.post<ChatRecord[]>('/api/chat/list');
}

View File

@ -68,7 +68,7 @@ export function queryMessagesList(data: MessagesRecord) {
}
// 未读消息数量
export function queryMessagesCount() {
return axios.get('/api/rest/notice/count-unread');
return axios.get('/api/rest/notice/countUnread');
}
// 已读消息数量

View File

@ -69,12 +69,11 @@
<script lang="ts" setup>
import { PropType } from 'vue';
import { MessageRecord, MessageListType } from '@/api/message';
import dayjs from 'dayjs';
const props = defineProps({
renderList: {
type: Array as PropType<MessageListType>,
type: Array as PropType<any>,
required: true,
},
unreadCount: {
@ -87,7 +86,7 @@
// emit('itemClick', [...props.renderList]);
// };
const onItemClick = (item: MessageRecord) => {
const onItemClick = (item: any) => {
if (!item.status) {
emit('itemClick', [item]);
}

View File

@ -43,7 +43,7 @@
<li>
<a-tooltip content="消息通知">
<div class="message-box-trigger">
<a-badge :count="9" dot>
<a-badge v-if="messageCount > 0" :count="9" dot>
<a-button
class="nav-btn"
type="outline"
@ -53,6 +53,15 @@
<icon-notification />
</a-button>
</a-badge>
<a-button
v-else
class="nav-btn"
type="outline"
:shape="'circle'"
@click="setPopoverVisible"
>
<icon-notification />
</a-button>
</div>
</a-tooltip>
<a-popover
@ -204,7 +213,7 @@
</template>
<script lang="ts" setup>
import { computed, ref, inject } from 'vue';
import { computed, ref, inject, onMounted } from 'vue';
import { useDark, useToggle, useFullscreen } from '@vueuse/core';
import { useAppStore, useUserStore } from '@/store';
import { LOCALE_OPTIONS } from '@/locale';
@ -215,6 +224,7 @@
import { useRouter } from 'vue-router';
import { clearToken } from '@/utils/auth';
import messageBox from '@/components/message-box/index.vue';
import { queryMessagesCount } from '@/api/messages';
const router = useRouter();
const appStore = useAppStore();
@ -222,6 +232,7 @@
const { changeLocale, currentLocale } = useLocale();
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
const messageCount = ref(0);
const locales = [...LOCALE_OPTIONS];
const avatar = computed(() => {
return userStore.avatar ? userStore.avatar : userIcon;
@ -275,12 +286,22 @@
triggerBtn.value.dispatchEvent(event);
};
const fetchMessageCount = async () => {
const response = await queryMessagesCount();
messageCount.value = response.data;
};
//
const handleSwitchRole = (roleId: number) => {
userStore.switchRole(roleId);
};
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void;
//
onMounted(() => {
fetchMessageCount();
});
</script>
<style scoped lang="less">

View File

@ -152,8 +152,13 @@
<template #createTime="{ record }">
{{ dayjs(record.createTime).format('YYYY-MM-DD HH:mm') }}
</template>
<template #isRead="{ record }">
{{ record.isRead == true ? '已读' : '未读' }}
<template #operation="{ record }">
<a-button type="outline" size="small" @click="handleViewDetail(record)">
<template #icon>
<icon-eye />
</template>
详情
</a-button>
</template>
</a-table>
<a-pagination
@ -167,6 +172,31 @@
@change="onPageChange"
/>
</a-card>
<template>
<a-modal
width="900px"
:visible="visible"
:footer="false"
@cancel="handleCancel"
>
<template #title>消息详情</template>
<a-descriptions :column="1">
<a-descriptions-item label="标题">
{{ selectedRecord.title }}
</a-descriptions-item>
<a-descriptions-item label="时间">
{{ dayjs(selectedRecord.createTime).format('YYYY-MM-DD HH:mm') }}
</a-descriptions-item>
<a-descriptions-item label="备注">
<div v-html="selectedRecord.remark"></div>
</a-descriptions-item>
<a-descriptions-item label="内容">
<div v-html="selectedRecord.content"></div>
</a-descriptions-item>
</a-descriptions>
</a-modal>
</template>
</div>
</template>
@ -193,6 +223,13 @@
const renderData = ref<MessageRecord[]>([]);
const formModel = ref(generateFormModel());
const selectedIds = ref<number[]>([]);
const visible = ref(false);
const selectedRecord = ref({
title: '',
createTime: '',
remark: '',
content: '',
});
const {
cloneColumns,
showColumns,
@ -209,10 +246,6 @@
const { pagination, setPagination } = usePagination();
const columns = computed<TableColumnData[]>(() => [
// {
// title: '',
// dataIndex: 'userId',
// },
{
title: '标题',
dataIndex: 'title',
@ -231,6 +264,11 @@
dataIndex: 'remark',
slotName: 'remark',
},
{
title: '操作',
dataIndex: 'operation',
slotName: 'operation',
},
]);
//
const fetchData = async (params = { size: 10, current: 1 }) => {
@ -274,6 +312,15 @@
search();
};
const handleViewDetail = (record: any) => {
selectedRecord.value = record;
visible.value = true;
};
const handleCancel = () => {
visible.value = false;
};
//
const reset = () => {
formModel.value = generateFormModel();

View File

@ -103,8 +103,8 @@
style="margin-bottom: 40px"
@page-change="onPageChange"
>
<template #operationTime="{ record }">
{{ dayjs(record.operationTime).format('YYYY-MM-DD HH:mm') }}
<template #makeTime="{ record }">
{{ dayjs(record.makeTime).format('YYYY-MM-DD HH:mm') }}
</template>
</a-table>
<a-pagination
@ -158,8 +158,8 @@
},
{
title: '操作时间',
dataIndex: 'operationTime',
slotName: 'operationTime',
dataIndex: 'makeTime',
slotName: 'makeTime',
sortable: {
sortDirections: ['ascend', 'descend'],
},

View File

@ -76,12 +76,23 @@
@change="handleSortChange"
@page-change="onPageChange"
>
<template #publishTime="{ record }">
{{ dayjs(record.publishTime).format('YYYY-MM-DD HH:mm') }}
<template #createTime="{ record }">
{{ dayjs(record.createTime).format('YYYY-MM-DD HH:mm') }}
</template>
<template #isRead="{ record }">
{{ record.isRead == true ? '已读' : '未读' }}
</template>
<template #operation="{ record }">
<a-button
type="outline"
size="small"
status="success"
@click="handleViewDetail(record)"
style="padding: 7px; margin-right: 10px"
>
<template #icon><icon-list /></template>详情
</a-button>
</template>
<!-- <template #operations="{ record }">-->
<!-- <a-button-->
<!-- type="outline"-->
@ -106,6 +117,29 @@
@change="onPageChange"
/>
</a-card>
<a-modal
width="900px"
:visible="visible"
:footer="false"
@cancel="handleCancel"
>
<template #title>消息详情</template>
<a-descriptions :column="1">
<a-descriptions-item label="标题">
{{ selectedRecord.title }}
</a-descriptions-item>
<a-descriptions-item label="时间">
{{ dayjs(selectedRecord.createTime).format('YYYY-MM-DD HH:mm') }}
</a-descriptions-item>
<a-descriptions-item label="备注">
<div v-html="selectedRecord.remark"></div>
</a-descriptions-item>
<a-descriptions-item label="内容">
<div v-html="selectedRecord.content"></div>
</a-descriptions-item>
</a-descriptions>
</a-modal>
</div>
</template>
@ -153,8 +187,8 @@
},
{
title: '时间',
dataIndex: 'publishTime',
slotName: 'publishTime',
dataIndex: 'createTime',
slotName: 'createTime',
sortable: {
sortDirections: ['ascend', 'descend'],
},
@ -164,11 +198,11 @@
dataIndex: 'isRead',
slotName: 'isRead',
},
// {
// title: '',
// dataIndex: 'operations',
// slotName: 'operations',
// },
{
title: '操作',
dataIndex: 'operation',
slotName: 'operation',
},
]);
const statusOptions = computed<SelectOptionData[]>(() => [
{
@ -181,6 +215,14 @@
},
]);
const visible = ref(false);
const selectedRecord = ref({
title: '',
createTime: '',
remark: '',
content: '',
});
//
const fetchData = async (
params: BulletinsRecord = { size: 10, current: 1 }
@ -234,6 +276,15 @@
search();
};
const handleViewDetail = (record: any) => {
selectedRecord.value = record;
visible.value = true;
};
const handleCancel = () => {
visible.value = false;
};
search();
//