feat(message): 优化消息通知功能
- 修改标题为"IOT设备管理系统" - 优化消息通知组件,增加未读消息数量显示- 添加消息详情查看功能 - 更新消息列表展示,增加操作按钮 - 调整消息接口路径
This commit is contained in:
parent
69a0144ebd
commit
13360113ef
@ -8,7 +8,7 @@
|
|||||||
href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico"
|
href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico"
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>基于MQTT的IOT设备管理系统</title>
|
<title>IOT设备管理系统</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
@ -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');
|
|
||||||
}
|
|
@ -68,7 +68,7 @@ export function queryMessagesList(data: MessagesRecord) {
|
|||||||
}
|
}
|
||||||
// 未读消息数量
|
// 未读消息数量
|
||||||
export function queryMessagesCount() {
|
export function queryMessagesCount() {
|
||||||
return axios.get('/api/rest/notice/count-unread');
|
return axios.get('/api/rest/notice/countUnread');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已读消息数量
|
// 已读消息数量
|
||||||
|
@ -69,12 +69,11 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
import { MessageRecord, MessageListType } from '@/api/message';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
renderList: {
|
renderList: {
|
||||||
type: Array as PropType<MessageListType>,
|
type: Array as PropType<any>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
unreadCount: {
|
unreadCount: {
|
||||||
@ -87,7 +86,7 @@
|
|||||||
// emit('itemClick', [...props.renderList]);
|
// emit('itemClick', [...props.renderList]);
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const onItemClick = (item: MessageRecord) => {
|
const onItemClick = (item: any) => {
|
||||||
if (!item.status) {
|
if (!item.status) {
|
||||||
emit('itemClick', [item]);
|
emit('itemClick', [item]);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<a-tooltip content="消息通知">
|
<a-tooltip content="消息通知">
|
||||||
<div class="message-box-trigger">
|
<div class="message-box-trigger">
|
||||||
<a-badge :count="9" dot>
|
<a-badge v-if="messageCount > 0" :count="9" dot>
|
||||||
<a-button
|
<a-button
|
||||||
class="nav-btn"
|
class="nav-btn"
|
||||||
type="outline"
|
type="outline"
|
||||||
@ -53,6 +53,15 @@
|
|||||||
<icon-notification />
|
<icon-notification />
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
|
<a-button
|
||||||
|
v-else
|
||||||
|
class="nav-btn"
|
||||||
|
type="outline"
|
||||||
|
:shape="'circle'"
|
||||||
|
@click="setPopoverVisible"
|
||||||
|
>
|
||||||
|
<icon-notification />
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-popover
|
<a-popover
|
||||||
@ -204,7 +213,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 { useDark, useToggle, useFullscreen } from '@vueuse/core';
|
||||||
import { useAppStore, useUserStore } from '@/store';
|
import { useAppStore, useUserStore } from '@/store';
|
||||||
import { LOCALE_OPTIONS } from '@/locale';
|
import { LOCALE_OPTIONS } from '@/locale';
|
||||||
@ -215,6 +224,7 @@
|
|||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { clearToken } from '@/utils/auth';
|
import { clearToken } from '@/utils/auth';
|
||||||
import messageBox from '@/components/message-box/index.vue';
|
import messageBox from '@/components/message-box/index.vue';
|
||||||
|
import { queryMessagesCount } from '@/api/messages';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
@ -222,6 +232,7 @@
|
|||||||
|
|
||||||
const { changeLocale, currentLocale } = useLocale();
|
const { changeLocale, currentLocale } = useLocale();
|
||||||
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
|
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
|
||||||
|
const messageCount = ref(0);
|
||||||
const locales = [...LOCALE_OPTIONS];
|
const locales = [...LOCALE_OPTIONS];
|
||||||
const avatar = computed(() => {
|
const avatar = computed(() => {
|
||||||
return userStore.avatar ? userStore.avatar : userIcon;
|
return userStore.avatar ? userStore.avatar : userIcon;
|
||||||
@ -275,12 +286,22 @@
|
|||||||
triggerBtn.value.dispatchEvent(event);
|
triggerBtn.value.dispatchEvent(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchMessageCount = async () => {
|
||||||
|
const response = await queryMessagesCount();
|
||||||
|
messageCount.value = response.data;
|
||||||
|
};
|
||||||
|
|
||||||
// 切换角色
|
// 切换角色
|
||||||
const handleSwitchRole = (roleId: number) => {
|
const handleSwitchRole = (roleId: number) => {
|
||||||
userStore.switchRole(roleId);
|
userStore.switchRole(roleId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void;
|
const toggleDrawerMenu = inject('toggleDrawerMenu') as () => void;
|
||||||
|
|
||||||
|
// 在组件挂载时调用
|
||||||
|
onMounted(() => {
|
||||||
|
fetchMessageCount();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
@ -152,8 +152,13 @@
|
|||||||
<template #createTime="{ record }">
|
<template #createTime="{ record }">
|
||||||
{{ dayjs(record.createTime).format('YYYY-MM-DD HH:mm') }}
|
{{ dayjs(record.createTime).format('YYYY-MM-DD HH:mm') }}
|
||||||
</template>
|
</template>
|
||||||
<template #isRead="{ record }">
|
<template #operation="{ record }">
|
||||||
{{ record.isRead == true ? '已读' : '未读' }}
|
<a-button type="outline" size="small" @click="handleViewDetail(record)">
|
||||||
|
<template #icon>
|
||||||
|
<icon-eye />
|
||||||
|
</template>
|
||||||
|
详情
|
||||||
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
<a-pagination
|
<a-pagination
|
||||||
@ -167,6 +172,31 @@
|
|||||||
@change="onPageChange"
|
@change="onPageChange"
|
||||||
/>
|
/>
|
||||||
</a-card>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -193,6 +223,13 @@
|
|||||||
const renderData = ref<MessageRecord[]>([]);
|
const renderData = ref<MessageRecord[]>([]);
|
||||||
const formModel = ref(generateFormModel());
|
const formModel = ref(generateFormModel());
|
||||||
const selectedIds = ref<number[]>([]);
|
const selectedIds = ref<number[]>([]);
|
||||||
|
const visible = ref(false);
|
||||||
|
const selectedRecord = ref({
|
||||||
|
title: '',
|
||||||
|
createTime: '',
|
||||||
|
remark: '',
|
||||||
|
content: '',
|
||||||
|
});
|
||||||
const {
|
const {
|
||||||
cloneColumns,
|
cloneColumns,
|
||||||
showColumns,
|
showColumns,
|
||||||
@ -209,10 +246,6 @@
|
|||||||
const { pagination, setPagination } = usePagination();
|
const { pagination, setPagination } = usePagination();
|
||||||
|
|
||||||
const columns = computed<TableColumnData[]>(() => [
|
const columns = computed<TableColumnData[]>(() => [
|
||||||
// {
|
|
||||||
// title: '用户编号',
|
|
||||||
// dataIndex: 'userId',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: '标题',
|
title: '标题',
|
||||||
dataIndex: 'title',
|
dataIndex: 'title',
|
||||||
@ -231,6 +264,11 @@
|
|||||||
dataIndex: 'remark',
|
dataIndex: 'remark',
|
||||||
slotName: 'remark',
|
slotName: 'remark',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
// 获取消息列表
|
// 获取消息列表
|
||||||
const fetchData = async (params = { size: 10, current: 1 }) => {
|
const fetchData = async (params = { size: 10, current: 1 }) => {
|
||||||
@ -274,6 +312,15 @@
|
|||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewDetail = (record: any) => {
|
||||||
|
selectedRecord.value = record;
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
formModel.value = generateFormModel();
|
formModel.value = generateFormModel();
|
||||||
|
@ -103,8 +103,8 @@
|
|||||||
style="margin-bottom: 40px"
|
style="margin-bottom: 40px"
|
||||||
@page-change="onPageChange"
|
@page-change="onPageChange"
|
||||||
>
|
>
|
||||||
<template #operationTime="{ record }">
|
<template #makeTime="{ record }">
|
||||||
{{ dayjs(record.operationTime).format('YYYY-MM-DD HH:mm') }}
|
{{ dayjs(record.makeTime).format('YYYY-MM-DD HH:mm') }}
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
<a-pagination
|
<a-pagination
|
||||||
@ -158,8 +158,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作时间',
|
title: '操作时间',
|
||||||
dataIndex: 'operationTime',
|
dataIndex: 'makeTime',
|
||||||
slotName: 'operationTime',
|
slotName: 'makeTime',
|
||||||
sortable: {
|
sortable: {
|
||||||
sortDirections: ['ascend', 'descend'],
|
sortDirections: ['ascend', 'descend'],
|
||||||
},
|
},
|
||||||
|
@ -76,12 +76,23 @@
|
|||||||
@change="handleSortChange"
|
@change="handleSortChange"
|
||||||
@page-change="onPageChange"
|
@page-change="onPageChange"
|
||||||
>
|
>
|
||||||
<template #publishTime="{ record }">
|
<template #createTime="{ record }">
|
||||||
{{ dayjs(record.publishTime).format('YYYY-MM-DD HH:mm') }}
|
{{ dayjs(record.createTime).format('YYYY-MM-DD HH:mm') }}
|
||||||
</template>
|
</template>
|
||||||
<template #isRead="{ record }">
|
<template #isRead="{ record }">
|
||||||
{{ record.isRead == true ? '已读' : '未读' }}
|
{{ record.isRead == true ? '已读' : '未读' }}
|
||||||
</template>
|
</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 }">-->
|
<!-- <template #operations="{ record }">-->
|
||||||
<!-- <a-button-->
|
<!-- <a-button-->
|
||||||
<!-- type="outline"-->
|
<!-- type="outline"-->
|
||||||
@ -106,6 +117,29 @@
|
|||||||
@change="onPageChange"
|
@change="onPageChange"
|
||||||
/>
|
/>
|
||||||
</a-card>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -153,8 +187,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '时间',
|
title: '时间',
|
||||||
dataIndex: 'publishTime',
|
dataIndex: 'createTime',
|
||||||
slotName: 'publishTime',
|
slotName: 'createTime',
|
||||||
sortable: {
|
sortable: {
|
||||||
sortDirections: ['ascend', 'descend'],
|
sortDirections: ['ascend', 'descend'],
|
||||||
},
|
},
|
||||||
@ -164,11 +198,11 @@
|
|||||||
dataIndex: 'isRead',
|
dataIndex: 'isRead',
|
||||||
slotName: 'isRead',
|
slotName: 'isRead',
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// title: '操作',
|
title: '操作',
|
||||||
// dataIndex: 'operations',
|
dataIndex: 'operation',
|
||||||
// slotName: 'operations',
|
slotName: 'operation',
|
||||||
// },
|
},
|
||||||
]);
|
]);
|
||||||
const statusOptions = computed<SelectOptionData[]>(() => [
|
const statusOptions = computed<SelectOptionData[]>(() => [
|
||||||
{
|
{
|
||||||
@ -181,6 +215,14 @@
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const selectedRecord = ref({
|
||||||
|
title: '',
|
||||||
|
createTime: '',
|
||||||
|
remark: '',
|
||||||
|
content: '',
|
||||||
|
});
|
||||||
|
|
||||||
// 获取消息列表
|
// 获取消息列表
|
||||||
const fetchData = async (
|
const fetchData = async (
|
||||||
params: BulletinsRecord = { size: 10, current: 1 }
|
params: BulletinsRecord = { size: 10, current: 1 }
|
||||||
@ -234,6 +276,15 @@
|
|||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewDetail = (record: any) => {
|
||||||
|
selectedRecord.value = record;
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
search();
|
search();
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
|
Loading…
Reference in New Issue
Block a user