2025-09-05 21:00:10 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="message-center">
|
|
|
|
|
<!-- 顶部Tab导航 -->
|
|
|
|
|
<div class="tab-container">
|
2025-09-16 19:28:31 +08:00
|
|
|
|
<n-tabs v-model:value="activeTab" type="line" size="large" class="message-tabs" @update:value="handleTabChange">
|
2025-09-05 21:00:10 +08:00
|
|
|
|
<n-tab-pane name="notification" tab="即时消息">
|
|
|
|
|
<template #tab>
|
|
|
|
|
<div class="tab-item">
|
|
|
|
|
<span>即时消息</span>
|
|
|
|
|
<n-badge :value="notificationCount" :show="notificationCount > 0" class="tab-badge">
|
|
|
|
|
</n-badge>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</n-tab-pane>
|
2025-09-16 19:28:31 +08:00
|
|
|
|
|
2025-09-05 21:00:10 +08:00
|
|
|
|
<n-tab-pane name="comment" tab="评论和@">
|
|
|
|
|
<template #tab>
|
|
|
|
|
<div class="tab-item">
|
|
|
|
|
<span>评论和@</span>
|
|
|
|
|
<n-badge :value="commentCount" :show="commentCount > 0" class="tab-badge">
|
|
|
|
|
</n-badge>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</n-tab-pane>
|
2025-09-16 19:28:31 +08:00
|
|
|
|
|
2025-09-05 21:00:10 +08:00
|
|
|
|
<n-tab-pane name="favorite" tab="赞和收藏">
|
|
|
|
|
<template #tab>
|
|
|
|
|
<div class="tab-item">
|
|
|
|
|
<span>赞和收藏</span>
|
|
|
|
|
<n-badge :value="favoriteCount" :show="favoriteCount > 0" class="tab-badge">
|
|
|
|
|
</n-badge>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</n-tab-pane>
|
2025-09-16 19:28:31 +08:00
|
|
|
|
|
2025-09-05 21:00:10 +08:00
|
|
|
|
<n-tab-pane name="system" tab="系统消息">
|
|
|
|
|
<template #tab>
|
|
|
|
|
<div class="tab-item">
|
|
|
|
|
<span>系统消息</span>
|
|
|
|
|
<n-badge :value="systemCount" :show="systemCount > 0" class="tab-badge">
|
|
|
|
|
</n-badge>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</n-tab-pane>
|
|
|
|
|
</n-tabs>
|
|
|
|
|
</div>
|
2025-09-16 19:28:31 +08:00
|
|
|
|
|
2025-09-05 21:00:10 +08:00
|
|
|
|
<!-- Tab内容区域 -->
|
|
|
|
|
<div class="tab-content">
|
|
|
|
|
<div v-show="activeTab === 'notification'">
|
|
|
|
|
<NotificationMessages />
|
|
|
|
|
</div>
|
|
|
|
|
<div v-show="activeTab === 'comment'">
|
|
|
|
|
<CommentLikes />
|
|
|
|
|
</div>
|
|
|
|
|
<div v-show="activeTab === 'favorite'">
|
|
|
|
|
<FavoriteMessages />
|
|
|
|
|
</div>
|
|
|
|
|
<div v-show="activeTab === 'system'">
|
|
|
|
|
<SystemMessages />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-09-16 19:28:31 +08:00
|
|
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
2025-09-19 20:15:10 +08:00
|
|
|
|
import { NBadge, NTabs, NTabPane } from 'naive-ui'
|
|
|
|
|
import { ChatApi, MessageApi } from '@/api'
|
2025-09-05 21:00:10 +08:00
|
|
|
|
|
|
|
|
|
// 导入子组件
|
|
|
|
|
import NotificationMessages from './components/NotificationMessages.vue'
|
|
|
|
|
import CommentLikes from './components/CommentLikes.vue'
|
|
|
|
|
import FavoriteMessages from './components/FavoriteMessages.vue'
|
|
|
|
|
import SystemMessages from './components/SystemMessages.vue'
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
const activeTab = ref('notification') // 当前激活的tab
|
|
|
|
|
|
|
|
|
|
// 各类消息数量(角标显示)
|
2025-09-15 19:34:25 +08:00
|
|
|
|
const notificationCount = ref(0) // 即时消息数量
|
|
|
|
|
const commentCount = ref(0) // 评论和@数量
|
2025-09-05 21:00:10 +08:00
|
|
|
|
const favoriteCount = ref(0) // 赞和收藏数量
|
|
|
|
|
const systemCount = ref(0) // 系统消息数量
|
|
|
|
|
|
2025-09-15 19:34:25 +08:00
|
|
|
|
// 加载状态
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
2025-09-16 19:28:31 +08:00
|
|
|
|
// 缓存和防抖
|
|
|
|
|
const cacheTimestamp = ref(0)
|
|
|
|
|
const CACHE_DURATION = 30000 // 30秒缓存
|
|
|
|
|
let refreshTimer: NodeJS.Timeout | null = null
|
|
|
|
|
|
2025-09-05 21:00:10 +08:00
|
|
|
|
// 生命周期钩子
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
// 初始化逻辑
|
|
|
|
|
loadMessageCounts()
|
|
|
|
|
})
|
|
|
|
|
|
2025-09-16 19:28:31 +08:00
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
// 清理定时器
|
|
|
|
|
if (refreshTimer) {
|
|
|
|
|
clearTimeout(refreshTimer)
|
|
|
|
|
refreshTimer = null
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2025-09-05 21:00:10 +08:00
|
|
|
|
// 加载各类消息数量
|
2025-09-16 19:28:31 +08:00
|
|
|
|
const loadMessageCounts = async (forceRefresh = false) => {
|
|
|
|
|
// 检查缓存
|
|
|
|
|
const now = Date.now()
|
|
|
|
|
if (!forceRefresh && now - cacheTimestamp.value < CACHE_DURATION) {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('📋 使用缓存的消息数量,跳过加载')
|
2025-09-16 19:28:31 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('🔄 开始加载消息数量...')
|
2025-09-15 19:34:25 +08:00
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
// 首先尝试使用统一的未读消息数接口
|
|
|
|
|
const unreadCountResult = await Promise.allSettled([loadTotalUnreadCount()])
|
|
|
|
|
console.log('🔍 统一接口结果:', unreadCountResult[0])
|
|
|
|
|
|
|
|
|
|
// 如果统一接口失败,则使用原有的分别加载方式
|
|
|
|
|
if (unreadCountResult[0].status === 'rejected') {
|
|
|
|
|
console.log('⚠️ 统一接口失败,使用分别加载方式')
|
|
|
|
|
// 并行加载各类消息数量,使用 Promise.allSettled 确保即使某个接口失败也不影响其他接口
|
|
|
|
|
const results = await Promise.allSettled([
|
|
|
|
|
loadNotificationCount(),
|
|
|
|
|
loadCommentCount(),
|
|
|
|
|
loadFavoriteCount(),
|
|
|
|
|
loadSystemCount()
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
console.log('📊 分别加载结果:', results)
|
|
|
|
|
// 检查是否有接口失败
|
|
|
|
|
const failedCount = results.filter(result => result.status === 'rejected').length
|
|
|
|
|
if (failedCount > 0) {
|
|
|
|
|
console.log(`⚠️ ${failedCount} 个接口加载失败`)
|
|
|
|
|
// 静默处理接口失败,不影响用户体验
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('📈 最终消息数量:', {
|
|
|
|
|
notification: notificationCount.value,
|
|
|
|
|
comment: commentCount.value,
|
|
|
|
|
favorite: favoriteCount.value,
|
|
|
|
|
system: systemCount.value
|
|
|
|
|
})
|
2025-09-15 19:34:25 +08:00
|
|
|
|
|
2025-09-16 19:28:31 +08:00
|
|
|
|
cacheTimestamp.value = now
|
2025-09-15 19:34:25 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('加载消息数量失败:', error)
|
2025-09-19 20:15:10 +08:00
|
|
|
|
// 不显示错误消息,因为可能只是部分接口失败
|
2025-09-15 19:34:25 +08:00
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-16 19:28:31 +08:00
|
|
|
|
// 防抖刷新
|
|
|
|
|
const debouncedRefresh = () => {
|
|
|
|
|
if (refreshTimer) {
|
|
|
|
|
clearTimeout(refreshTimer)
|
|
|
|
|
}
|
|
|
|
|
refreshTimer = setTimeout(() => {
|
|
|
|
|
loadMessageCounts(true)
|
|
|
|
|
}, 1000)
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-19 20:15:10 +08:00
|
|
|
|
// 加载总的未读消息数
|
|
|
|
|
const loadTotalUnreadCount = async () => {
|
2025-09-15 19:34:25 +08:00
|
|
|
|
try {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('🔄 尝试调用统一未读消息数接口...')
|
|
|
|
|
const response = await MessageApi.getUnreadMessageCount()
|
|
|
|
|
console.log('🔍 统一接口响应:', response)
|
|
|
|
|
|
|
|
|
|
if (response.code === 200) {
|
2025-09-19 20:26:43 +08:00
|
|
|
|
const result = response.data as any || {}
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('📊 统一接口返回数据:', result)
|
|
|
|
|
|
|
|
|
|
// 使用统一接口返回的数据
|
2025-09-19 20:26:43 +08:00
|
|
|
|
// 检查result.result是否存在,如果存在则使用其中的数据
|
|
|
|
|
if (result.result) {
|
|
|
|
|
const countData = result.result
|
|
|
|
|
if (countData.commentsUnreadCount !== undefined) {
|
|
|
|
|
commentCount.value = countData.commentsUnreadCount || 0
|
|
|
|
|
console.log('✅ 评论未读数设置为:', commentCount.value)
|
|
|
|
|
}
|
|
|
|
|
if (countData.likesUnreadCount !== undefined) {
|
|
|
|
|
favoriteCount.value = countData.likesUnreadCount || 0
|
|
|
|
|
console.log('✅ 赞和收藏未读数设置为:', favoriteCount.value)
|
|
|
|
|
}
|
|
|
|
|
if (countData.systemUnreadCount !== undefined) {
|
|
|
|
|
systemCount.value = countData.systemUnreadCount || 0
|
|
|
|
|
console.log('✅ 系统消息未读数设置为:', systemCount.value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 注意:统一接口没有返回即时消息的未读数,保持为0
|
2025-09-19 20:15:10 +08:00
|
|
|
|
notificationCount.value = 0
|
|
|
|
|
|
|
|
|
|
return true
|
2025-09-15 19:34:25 +08:00
|
|
|
|
}
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('❌ 统一接口响应格式不正确')
|
|
|
|
|
return false
|
2025-09-15 19:34:25 +08:00
|
|
|
|
} catch (error) {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.error('❌ 获取总未读消息数失败:', error)
|
|
|
|
|
return false
|
2025-09-16 19:28:31 +08:00
|
|
|
|
}
|
2025-09-19 20:15:10 +08:00
|
|
|
|
}
|
2025-09-16 19:28:31 +08:00
|
|
|
|
|
2025-09-19 20:15:10 +08:00
|
|
|
|
// 加载即时消息数量
|
|
|
|
|
const loadNotificationCount = async () => {
|
|
|
|
|
// 由于 /aiol/aiolChat/unread-count 接口不存在,直接使用备用方案
|
2025-09-16 19:28:31 +08:00
|
|
|
|
try {
|
|
|
|
|
const chatsResponse = await ChatApi.getMyChats()
|
|
|
|
|
if (chatsResponse.data?.success) {
|
|
|
|
|
notificationCount.value = chatsResponse.data.result.reduce((total: number, chat: any) => {
|
|
|
|
|
return total + (chat.unreadCount || 0)
|
|
|
|
|
}, 0)
|
2025-09-19 20:15:10 +08:00
|
|
|
|
} else {
|
|
|
|
|
notificationCount.value = 0
|
2025-09-15 19:34:25 +08:00
|
|
|
|
}
|
2025-09-16 19:28:31 +08:00
|
|
|
|
} catch (chatError) {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
// 静默处理错误,设置为0
|
2025-09-16 19:28:31 +08:00
|
|
|
|
notificationCount.value = 0
|
2025-09-15 19:34:25 +08:00
|
|
|
|
}
|
2025-09-05 21:00:10 +08:00
|
|
|
|
}
|
2025-09-16 19:28:31 +08:00
|
|
|
|
|
|
|
|
|
// 加载评论和@数量
|
|
|
|
|
const loadCommentCount = async () => {
|
|
|
|
|
try {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
const response = await MessageApi.getCommentsAtMessageCount()
|
2025-09-19 20:26:43 +08:00
|
|
|
|
if (response.data && response.data.result) {
|
|
|
|
|
commentCount.value = response.data.result.unread || 0
|
|
|
|
|
console.log('✅ 评论和@未读数量设置为:', commentCount.value)
|
2025-09-19 20:15:10 +08:00
|
|
|
|
} else {
|
|
|
|
|
commentCount.value = 0
|
2025-09-19 20:26:43 +08:00
|
|
|
|
console.log('❌ 评论和@接口响应失败,设置为0')
|
2025-09-19 20:15:10 +08:00
|
|
|
|
}
|
2025-09-16 19:28:31 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取评论数量失败:', error)
|
|
|
|
|
commentCount.value = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载赞和收藏数量
|
|
|
|
|
const loadFavoriteCount = async () => {
|
|
|
|
|
try {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
const response = await MessageApi.getLikesMessageCount()
|
|
|
|
|
console.log('🔍 loadFavoriteCount 响应数据:', response)
|
|
|
|
|
|
2025-09-19 20:26:43 +08:00
|
|
|
|
if (response.data && response.data.result) {
|
|
|
|
|
favoriteCount.value = response.data.result.unread || 0
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('✅ 赞和收藏未读数量设置为:', favoriteCount.value)
|
|
|
|
|
} else {
|
|
|
|
|
favoriteCount.value = 0
|
|
|
|
|
console.log('❌ 赞和收藏接口响应失败,设置为0')
|
|
|
|
|
}
|
2025-09-16 19:28:31 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取收藏数量失败:', error)
|
2025-09-19 20:15:10 +08:00
|
|
|
|
// 如果接口不存在,设置为0,不显示错误消息
|
2025-09-16 19:28:31 +08:00
|
|
|
|
favoriteCount.value = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载系统消息数量
|
|
|
|
|
const loadSystemCount = async () => {
|
|
|
|
|
try {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.log('🔍 开始加载系统消息数量')
|
|
|
|
|
const response = await MessageApi.getSystemMessageCount()
|
|
|
|
|
console.log('🔍 系统消息数量API响应:', response)
|
|
|
|
|
|
|
|
|
|
if (response.code === 200 && response.data) {
|
|
|
|
|
systemCount.value = response.data.unread || 0
|
|
|
|
|
console.log('✅ 系统消息数量:', systemCount.value)
|
|
|
|
|
} else {
|
|
|
|
|
console.warn('⚠️ 系统消息数量API返回错误:', response)
|
|
|
|
|
systemCount.value = 0
|
|
|
|
|
}
|
2025-09-16 19:28:31 +08:00
|
|
|
|
} catch (error) {
|
2025-09-19 20:15:10 +08:00
|
|
|
|
console.error('❌ 获取系统消息数量失败:', error)
|
2025-09-16 19:28:31 +08:00
|
|
|
|
systemCount.value = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tab切换处理
|
|
|
|
|
const handleTabChange = (tabName: string) => {
|
|
|
|
|
activeTab.value = tabName
|
|
|
|
|
// 切换Tab时刷新对应数据
|
|
|
|
|
debouncedRefresh()
|
|
|
|
|
}
|
2025-09-05 21:00:10 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.message-center {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-container {
|
2025-09-18 22:35:37 +08:00
|
|
|
|
margin-bottom: 9px;
|
2025-09-05 21:00:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-tabs {
|
|
|
|
|
--n-tab-text-color: #666;
|
|
|
|
|
--n-tab-text-color-active: #1890ff;
|
|
|
|
|
--n-bar-color: #1890ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-item {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-badge {
|
|
|
|
|
position: relative;
|
|
|
|
|
top: -2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-content {
|
|
|
|
|
min-height: 400px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 调整badge样式 */
|
|
|
|
|
:deep(.n-badge) {
|
|
|
|
|
--n-color: #ff4d4f;
|
|
|
|
|
--n-text-color: #fff;
|
|
|
|
|
--n-font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 调整tabs整体样式 */
|
|
|
|
|
:deep(.n-tabs .n-tabs-nav .n-tabs-nav-scroll-content) {
|
|
|
|
|
gap: 32px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.n-tabs .n-tabs-tab) {
|
|
|
|
|
padding: 12px 0;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
</style>
|