diff --git a/src/components/NotificationCenter.jsx b/src/components/NotificationCenter.jsx index fb04eff..2d8a298 100644 --- a/src/components/NotificationCenter.jsx +++ b/src/components/NotificationCenter.jsx @@ -40,7 +40,7 @@ export default function NotificationCenter({ show, onClose }) { // 初始化WebSocket连接 useEffect(() => { // 只有在用户已登录的情况下才连接WebSocket - if (isAuthenticated && !isConnected) { + if (isAuthenticated) { initWebSocket() .then(() => { dispatch(setWebSocketConnected(true)); @@ -59,14 +59,7 @@ export default function NotificationCenter({ show, onClose }) { }); } - // 组件卸载时关闭WebSocket连接 - return () => { - if (isConnected) { - closeWebSocket(); - dispatch(setWebSocketConnected(false)); - } - }; - }, [isAuthenticated, isConnected, dispatch]); + }, [isAuthenticated, dispatch]); // 当通知中心显示时,获取最新通知 useEffect(() => { @@ -125,6 +118,7 @@ export default function NotificationCenter({ show, onClose }) { }; const handleViewDetail = (notification) => { + onClose(); navigate('/permissions'); // setSelectedRequest(notification); // setShowSlideOver(true); diff --git a/src/layouts/HeaderWithNav.jsx b/src/layouts/HeaderWithNav.jsx index 2578e59..73a0963 100644 --- a/src/layouts/HeaderWithNav.jsx +++ b/src/layouts/HeaderWithNav.jsx @@ -88,7 +88,6 @@ export default function HeaderWithNav() {
diff --git a/src/services/websocket.js b/src/services/websocket.js index 095d875..9aa8e5a 100644 --- a/src/services/websocket.js +++ b/src/services/websocket.js @@ -1,4 +1,8 @@ -import { addNotification, markNotificationAsRead } from '../store/notificationCenter/notificationCenter.slice'; +import { + addNotification, + markNotificationAsRead, + setWebSocketConnected, +} from '../store/notificationCenter/notificationCenter.slice'; import store from '../store/store'; // 修改为默认导出 import CryptoJS from 'crypto-js'; @@ -38,6 +42,7 @@ export const initWebSocket = () => { let token = ''; if (!encryptedToken) { console.error('No token found, cannot connect to notification service'); + store.dispatch(setWebSocketConnected(false)); reject(new Error('No token found')); return; } @@ -53,6 +58,9 @@ export const initWebSocket = () => { console.log('WebSocket connection established'); reconnectAttempts = 0; // 连接成功后重置重连计数器 + // 更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(true)); + // 订阅通知频道 subscribeToNotifications(); @@ -79,6 +87,8 @@ export const initWebSocket = () => { // 错误处理 socket.onerror = (error) => { console.error('WebSocket error:', error); + // 更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(false)); reject(error); }; @@ -86,6 +96,9 @@ export const initWebSocket = () => { socket.onclose = (event) => { console.log(`WebSocket connection closed: ${event.code} ${event.reason}`); + // 更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(false)); + // 清除ping定时器 if (pingInterval) clearInterval(pingInterval); @@ -99,15 +112,21 @@ export const initWebSocket = () => { console.log('Attempting to reconnect WebSocket...'); initWebSocket().catch((err) => { console.error('Failed to reconnect WebSocket:', err); + // 重连失败时更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(false)); }); }, RECONNECT_DELAY); } else { console.log('Maximum reconnection attempts reached. Giving up.'); + // 达到最大重连次数时更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(false)); } } }; } catch (error) { console.error('Error initializing WebSocket:', error); + // 更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(false)); reject(error); } }); @@ -173,6 +192,9 @@ export const closeWebSocket = () => { clearInterval(pingInterval); pingInterval = null; } + + // 更新Redux中的连接状态 + store.dispatch(setWebSocketConnected(false)); }; /** @@ -235,7 +257,7 @@ const processNotification = (data) => { } else { timeDisplay = `${diffDays}天前`; } - + return { id: notificationData.id, type: notificationData.category, diff --git a/src/store/notificationCenter/notificationCenter.thunks.js b/src/store/notificationCenter/notificationCenter.thunks.js index bedac39..b9ce17f 100644 --- a/src/store/notificationCenter/notificationCenter.thunks.js +++ b/src/store/notificationCenter/notificationCenter.thunks.js @@ -9,7 +9,7 @@ export const fetchNotifications = createAsyncThunk( async (_, { rejectWithValue }) => { try { const response = await get('/notifications/'); - return response; + return processNotification(response); } catch (error) { const errorMessage = error.response?.data?.message || 'Failed to fetch notifications'; return rejectWithValue(errorMessage); @@ -27,7 +27,7 @@ export const markNotificationRead = createAsyncThunk( const response = await post(`/notifications/${notificationId}/mark-as-read/`); return { id: notificationId, ...response }; } catch (error) { - const errorMessage = error.response?.data?.message || 'Failed to mark notification as read'; + const errorMessage = error.response?.data?.message || 'Failed to mark notification as read'; return rejectWithValue(errorMessage); } } @@ -48,3 +48,88 @@ export const markAllNotificationsRead = createAsyncThunk( } } ); + +/** + * 处理通知数据,转换为应用内通知格式 + * @param {Object|Array} data 通知数据或通知数组 + * @returns {Object|Array} 处理后的通知数据 + */ +export const processNotification = (data) => { + // 处理数组类型的通知数据 + if (Array.isArray(data)) { + return data.map((item) => processNotificationItem(item)); + } + + // 处理WebSocket格式的通知数据 (带有data字段的对象) + if (data && data.data) { + return processNotificationItem(data.data); + } + + // 处理单个通知对象 + return processNotificationItem(data); +}; + +/** + * 处理单个通知项 + * @param {Object} notification 单个通知数据 + * @returns {Object} 处理后的通知数据 + */ +const processNotificationItem = (notification) => { + // 确保我们有一个有效的通知对象 + if (!notification) return null; + + // 提取通知数据,兼容不同的API格式 + const notificationData = notification.data || notification; + + // 设置图标 + let icon = 'bi-info-circle'; + const type = notificationData.category || notificationData.type; + if (type === 'system') { + icon = 'bi-info-circle'; + } else if (type === 'permission' || type === 'permission_request') { + icon = 'bi-shield'; + } + + // 计算时间显示 + const createdAt = new Date(notificationData.created_at); + const now = new Date(); + + // 检查是否是今天 + const isToday = + createdAt.getDate() === now.getDate() && + createdAt.getMonth() === now.getMonth() && + createdAt.getFullYear() === now.getFullYear(); + + // 如果是今天,只显示时间;否则显示年月日和时间 + let timeDisplay; + if (isToday) { + timeDisplay = createdAt.toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit', + }); + } else { + timeDisplay = createdAt.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); + } + + return { + id: notificationData.id, + type: type, + icon, + title: notificationData.title, + content: notificationData.content, + time: timeDisplay, + hasDetail: true, + isRead: notificationData.is_read, + created_at: notificationData.created_at, + sender: notificationData.sender, + receiver: notificationData.receiver, + related_resource: notificationData.related_resource, + metadata: notificationData.metadata || {}, + }; +};