This commit is contained in:
2026-02-18 19:41:03 +08:00
parent a0c2350662
commit 6543e2ccdd
18 changed files with 4885 additions and 1196 deletions
@@ -8,7 +8,7 @@
</a-tooltip>
<!-- 消息通知 -->
<a-dropdown v-model:open="messageVisible" :trigger="['click']" placement="bottomRight">
<a-dropdown v-model:open="messageVisible" :trigger="['click']" placement="bottomRight" @openChange="handleMessageDropdownOpen">
<a-badge :count="messageCount" :offset="[-5, 5]">
<a-button type="text" class="action-btn">
<BellOutlined />
@@ -51,9 +51,9 @@
<div class="message-content">
<div class="message-title">{{ msg.title }}</div>
<div class="message-content-text">{{ msg.content }}</div>
<div class="message-time">{{ messageStore.formatMessageTime(msg.timestamp) }}</div>
<div class="message-time">{{ notificationStore.formatNotificationTime(msg.created_at) }}</div>
</div>
<a-badge v-if="!msg.read" dot />
<a-badge v-if="!msg.is_read" dot />
<a-button
type="text"
size="small"
@@ -160,11 +160,12 @@ import { useRouter } from 'vue-router'
import { message, Modal } from 'ant-design-vue'
import { useUserStore } from '@/stores/modules/user'
import { useI18nStore } from '@/stores/modules/i18n'
import { useMessageStore, MessageType } from '@/stores/modules/message'
import { useNotificationStore } from '@/stores/modules/notification'
import { DownOutlined, UserOutlined, LogoutOutlined, FullscreenOutlined, FullscreenExitOutlined, BellOutlined, CheckSquareOutlined, GlobalOutlined, SearchOutlined, SettingOutlined, DeleteOutlined } from '@ant-design/icons-vue'
import { useI18n } from 'vue-i18n'
import search from './search.vue'
import task from './task.vue'
import { useWebSocket } from '@/composables/useWebSocket'
// 定义组件名称(多词命名)
defineOptions({
@@ -175,7 +176,7 @@ const { t } = useI18n()
const router = useRouter()
const userStore = useUserStore()
const i18nStore = useI18nStore()
const messageStore = useMessageStore()
const notificationStore = useNotificationStore()
const isFullscreen = ref(false)
const searchVisible = ref(false)
@@ -184,28 +185,16 @@ const messageVisible = ref(false)
const currentMessageType = ref('all')
const messagesPage = ref(1)
const messagesPageSize = ref(10)
// 从 store 获取消息数据
const messages = computed(() => {
const result = messageStore.getMessages({
page: messagesPage.value,
pageSize: messagesPageSize.value,
type: currentMessageType.value === 'all' ? null : currentMessageType.value
})
return result.list
})
// 消息总数(用于分页)
const messagesTotal = computed(() => {
return messageStore.getMessages({
page: messagesPage.value,
pageSize: messagesPageSize.value,
type: currentMessageType.value === 'all' ? null : currentMessageType.value
}).total
})
const notificationsList = ref([])
// 未读消息数量
const messageCount = computed(() => messageStore.unreadCount)
const messageCount = computed(() => notificationStore.unreadCount)
// 消息总数(用于分页)
const messagesTotal = computed(() => notificationStore.total)
// 从 store 获取消息数据
const messages = computed(() => notificationsList.value)
// 任务数据
const tasks = ref([
@@ -232,8 +221,58 @@ const handleFullscreenChange = () => {
isFullscreen.value = !!document.fullscreenElement
}
// 加载未读通知
const loadNotifications = async () => {
try {
await notificationStore.fetchUnreadCount()
await loadUnreadNotifications()
} catch (error) {
console.error('加载通知失败:', error)
}
}
// 加载未读通知列表
const loadUnreadNotifications = async () => {
try {
const res = await notificationStore.fetchUnreadNotifications({
page: messagesPage.value,
page_size: messagesPageSize.value,
type: currentMessageType.value === 'all' ? null : currentMessageType.value
})
notificationsList.value = res.list || []
} catch (error) {
console.error('加载未读通知列表失败:', error)
}
}
// 加载所有通知
const loadAllNotifications = async () => {
try {
await notificationStore.fetchNotifications({
page: messagesPage.value,
page_size: messagesPageSize.value,
type: currentMessageType.value === 'all' ? null : currentMessageType.value
})
notificationsList.value = notificationStore.notifications
} catch (error) {
console.error('加载通知列表失败:', error)
}
}
// WebSocket 消息处理
const handleWebSocketMessage = (data) => {
notificationStore.handleWebSocketMessage(data)
}
onMounted(() => {
document.addEventListener('fullscreenchange', handleFullscreenChange)
// 加载通知数据
loadNotifications()
// 连接 WebSocket
const { initWebSocket } = useWebSocket()
initWebSocket()
})
onUnmounted(() => {
@@ -246,46 +285,113 @@ const showSearch = () => {
}
// 清除消息
const clearMessages = () => {
const clearMessages = async () => {
Modal.confirm({
title: t('common.confirmClear'),
content: t('common.confirmClearMessages'),
okText: t('common.confirm'),
cancelText: t('common.cancel'),
onOk: () => {
messageStore.clearAll()
message.success(t('common.cleared'))
onOk: async () => {
try {
await notificationStore.clearReadNotifications()
message.success(t('common.cleared'))
notificationsList.value = []
} catch (error) {
console.error('清空消息失败:', error)
}
},
})
}
// 标记消息为已读
const handleMessageRead = (msg) => {
if (!msg.read) {
messageStore.markAsRead(msg.id)
const handleMessageRead = async (msg) => {
if (!msg.is_read) {
try {
await notificationStore.markAsRead(msg.id)
// 更新本地状态
const notification = notificationsList.value.find(n => n.id === msg.id)
if (notification) {
notification.is_read = true
notification.read_at = new Date().toISOString()
}
} catch (error) {
console.error('标记已读失败:', error)
}
}
}
// 标记所有消息为已读
const markAllAsRead = () => {
messageStore.markAllAsRead()
message.success(t('common.markedAsRead'))
const markAllAsRead = async () => {
Modal.confirm({
title: '确认全部已读',
content: '确定要将所有消息标记为已读吗?',
okText: '确定',
cancelText: '取消',
onOk: async () => {
try {
await notificationStore.markAllAsRead()
message.success(t('common.markedAsRead'))
// 更新本地状态
notificationsList.value.forEach(n => {
n.is_read = true
n.read_at = n.read_at || new Date().toISOString()
})
} catch (error) {
console.error('标记全部已读失败:', error)
}
}
})
}
// 删除消息
const handleDeleteMessage = (msgId) => {
messageStore.removeMessage(msgId)
const handleDeleteMessage = async (msgId) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除这条消息吗?',
okText: '确定',
cancelText: '取消',
onOk: async () => {
try {
await notificationStore.deleteNotification(msgId)
message.success('删除成功')
// 更新本地状态
const index = notificationsList.value.findIndex(n => n.id === msgId)
if (index !== -1) {
notificationsList.value.splice(index, 1)
}
} catch (error) {
console.error('删除消息失败:', error)
}
}
})
}
// 切换消息类型
const changeMessageType = (type) => {
const changeMessageType = async (type) => {
currentMessageType.value = type
messagesPage.value = 1
if (type === 'all') {
await loadAllNotifications()
} else {
await loadUnreadNotifications()
}
}
// 分页变化
const handleMessagePageChange = (page) => {
const handleMessagePageChange = async (page) => {
messagesPage.value = page
if (currentMessageType.value === 'all') {
await loadAllNotifications()
} else {
await loadUnreadNotifications()
}
}
// 下拉框打开时加载数据
const handleMessageDropdownOpen = async (open) => {
if (open) {
await loadNotifications()
}
}
// 显示任务抽屉