From 6d66a392dbb6cea012bea9108b8566426e38f039 Mon Sep 17 00:00:00 2001 From: iven Date: Fri, 1 May 2026 16:37:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20NotificationPanel=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=BE=85=E5=8A=9E=E9=A2=84=E8=A7=88=E5=8C=BA=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 底部新增"待办事项"区域,显示最近 3 条 pending 行动项 - 角标数字改为 unreadCount + pendingActionCount - 点击待办项跳转 /health/action-inbox --- apps/web/src/components/NotificationPanel.tsx | 74 +++++++++++++++++-- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/NotificationPanel.tsx b/apps/web/src/components/NotificationPanel.tsx index 0133288..82f9013 100644 --- a/apps/web/src/components/NotificationPanel.tsx +++ b/apps/web/src/components/NotificationPanel.tsx @@ -1,37 +1,47 @@ -import { useEffect, useRef } from 'react'; -import { Badge, List, Popover, Button, Empty, Typography } from 'antd'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Badge, Divider, List, Popover, Button, Empty, Typography } from 'antd'; import { BellOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import { useMessageStore } from '../stores/message'; import { useThemeMode } from '../hooks/useThemeMode'; +import { actionInboxApi, type ActionItem } from '../api/health/actionInbox'; const { Text } = Typography; export default function NotificationPanel() { const navigate = useNavigate(); - // 使用独立 selector:数据订阅和函数引用分离,避免 effect 重复触发 const unreadCount = useMessageStore((s) => s.unreadCount); const recentMessages = useMessageStore((s) => s.recentMessages); const markAsRead = useMessageStore((s) => s.markAsRead); const isDark = useThemeMode(); const initializedRef = useRef(false); + const [pendingActions, setPendingActions] = useState([]); + + const fetchPendingActions = useCallback(async () => { + try { + const resp = await actionInboxApi.list({ status: 'pending', page_size: 3 }); + setPendingActions(resp.data); + } catch { + // 静默失败,不影响通知面板 + } + }, []); + useEffect(() => { - // 防止 StrictMode 双重 mount 和路由切换导致的重复初始化 if (initializedRef.current) return; initializedRef.current = true; const { fetchUnreadCount, fetchRecentMessages, connectSSE } = useMessageStore.getState(); fetchUnreadCount(); fetchRecentMessages(); + fetchPendingActions(); - // SSE 实时推送,收到消息即刷新 const disconnectSSE = connectSSE(); - // 降级轮询(SSE 断开时兜底) const interval = setInterval(() => { fetchUnreadCount(); fetchRecentMessages(); + fetchPendingActions(); }, 60000); return () => { @@ -39,7 +49,9 @@ export default function NotificationPanel() { disconnectSSE(); initializedRef.current = false; }; - }, []); + }, [fetchPendingActions]); + + const totalBadge = unreadCount + pendingActions.length; const content = (
@@ -149,6 +161,52 @@ export default function NotificationPanel() {
)} + + {/* 待办预览区域 */} + +
+
+ 待办事项 + +
+ {pendingActions.length === 0 ? ( + + 暂无待办 + + ) : ( + ( + navigate('/health/action-inbox')} + > + {item.title}} + description={ + + {item.patient_name} + + } + /> + + )} + /> + )} +
); @@ -178,7 +236,7 @@ export default function NotificationPanel() { e.currentTarget.style.background = 'transparent'; }} > - +