- {{
- unreadMessageCount
- }}
+
+ {{ messageUnreadCount }}
+
+
@@ -102,7 +106,10 @@ const props = defineProps({
const isLogin = computed(() => authState.loggedIn)
const isMobile = useIsMobile()
-const { count: unreadMessageCount, fetchUnreadCount } = useUnreadCount()
+const { count: totalUnreadCount, channelUnreadCount, fetchUnreadCount } = useUnreadCount()
+const messageUnreadCount = computed(() =>
+ Math.max(totalUnreadCount.value - channelUnreadCount.value, 0),
+)
const avatar = ref('')
const showSearch = ref(false)
const searchDropdown = ref(null)
@@ -413,6 +420,16 @@ onMounted(async () => {
box-sizing: border-box;
}
+.messages-unread-dot {
+ position: absolute;
+ top: -2px;
+ right: -4px;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: #ff4d4f;
+}
+
.rss-icon {
animation: rss-glow 2s 3;
}
diff --git a/frontend_nuxt/composables/useUnreadCount.js b/frontend_nuxt/composables/useUnreadCount.js
index 381b67d6a..58aeecbaa 100644
--- a/frontend_nuxt/composables/useUnreadCount.js
+++ b/frontend_nuxt/composables/useUnreadCount.js
@@ -1,93 +1,123 @@
-import { ref, watch, onMounted } from 'vue';
-import { useWebSocket } from './useWebSocket';
-import { getToken } from '~/utils/auth';
+import { ref, watch } from 'vue'
+import { useWebSocket } from './useWebSocket'
+import { getToken } from '~/utils/auth'
-const count = ref(0);
-let isInitialized = false;
-let wsSubscription = null;
+const count = ref(0)
+const channelUnreadCount = ref(0)
+let isInitialized = false
+let wsSubscription = null
export function useUnreadCount() {
- const config = useRuntimeConfig();
- const API_BASE_URL = config.public.apiBaseUrl;
- const { subscribe, isConnected, connect } = useWebSocket();
+ const config = useRuntimeConfig()
+ const API_BASE_URL = config.public.apiBaseUrl
+ const { subscribe, isConnected, connect } = useWebSocket()
- const fetchUnreadCount = async () => {
- const token = getToken();
+ const fetchTotalUnreadCount = async () => {
+ const token = getToken()
if (!token) {
- count.value = 0;
- return;
+ count.value = 0
+ return
}
try {
const response = await fetch(`${API_BASE_URL}/api/messages/unread-count`, {
headers: { Authorization: `Bearer ${token}` },
- });
+ })
if (response.ok) {
- const data = await response.json();
- count.value = data;
+ const data = await response.json()
+ count.value = data
}
} catch (error) {
- console.error('Failed to fetch unread count:', error);
+ console.error('Failed to fetch unread count:', error)
}
- };
+ }
+
+ const fetchChannelUnreadCount = async () => {
+ const token = getToken()
+ if (!token) {
+ channelUnreadCount.value = 0
+ return
+ }
+ try {
+ const response = await fetch(`${API_BASE_URL}/api/channels`, {
+ headers: { Authorization: `Bearer ${token}` },
+ })
+ if (response.ok) {
+ const channels = await response.json()
+ channelUnreadCount.value = channels.reduce((sum, ch) => sum + (ch.unreadCount || 0), 0)
+ }
+ } catch (error) {
+ console.error('Failed to fetch channel unread count:', error)
+ }
+ }
+
+ const fetchUnreadCount = async () => {
+ await Promise.all([fetchTotalUnreadCount(), fetchChannelUnreadCount()])
+ }
const initialize = async () => {
- const token = getToken();
+ const token = getToken()
if (!token) {
- count.value = 0;
- return;
+ count.value = 0
+ channelUnreadCount.value = 0
+ return
}
// 总是获取最新的未读数量
- fetchUnreadCount();
-
+ fetchUnreadCount()
+
// 确保WebSocket连接
if (!isConnected.value) {
- connect(token);
+ connect(token)
}
-
+
// 设置WebSocket监听
- await setupWebSocketListener();
- };
+ await setupWebSocketListener()
+ }
const setupWebSocketListener = async () => {
// 只有在还没有订阅的情况下才设置监听
if (!wsSubscription) {
-
- watch(isConnected, (newValue) => {
- if (newValue && !wsSubscription) {
- const destination = `/user/queue/unread-count`;
- wsSubscription = subscribe(destination, (message) => {
- const unreadCount = parseInt(message.body, 10);
- if (!isNaN(unreadCount)) {
- count.value = unreadCount;
- }
- });
- }
- }, { immediate: true });
+ watch(
+ isConnected,
+ (newValue) => {
+ if (newValue && !wsSubscription) {
+ const destination = `/user/queue/unread-count`
+ wsSubscription = subscribe(destination, (message) => {
+ const unreadCount = parseInt(message.body, 10)
+ if (!isNaN(unreadCount)) {
+ count.value = unreadCount
+ fetchChannelUnreadCount()
+ }
+ })
+ }
+ },
+ { immediate: true },
+ )
}
- };
+ }
// 自动初始化逻辑 - 确保每次调用都能获取到未读数量并设置监听
- const token = getToken();
+ const token = getToken()
if (token) {
if (!isInitialized) {
- isInitialized = true;
- initialize(); // 完整初始化,包括WebSocket监听
+ isInitialized = true
+ initialize() // 完整初始化,包括WebSocket监听
} else {
// 即使已经初始化,也要确保获取最新的未读数量并确保WebSocket监听存在
- fetchUnreadCount();
-
+ fetchUnreadCount()
+
// 确保WebSocket连接和监听都存在
if (!isConnected.value) {
- connect(token);
+ connect(token)
}
- setupWebSocketListener();
+ setupWebSocketListener()
}
}
return {
count,
+ channelUnreadCount,
fetchUnreadCount,
initialize,
- };
-}
\ No newline at end of file
+ }
+}