import { useEffect, useRef } from 'react'; import { Badge, List, Popover, Button, Empty, Typography, theme } from 'antd'; import { BellOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import { useMessageStore } from '../stores/message'; 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 { token } = theme.useToken(); const isDark = token.colorBgContainer === '#111827' || token.colorBgContainer === 'rgb(17, 24, 39)'; const initializedRef = useRef(false); useEffect(() => { // 防止 StrictMode 双重 mount 和路由切换导致的重复初始化 if (initializedRef.current) return; initializedRef.current = true; const { fetchUnreadCount, fetchRecentMessages } = useMessageStore.getState(); fetchUnreadCount(); fetchRecentMessages(); const interval = setInterval(() => { fetchUnreadCount(); fetchRecentMessages(); }, 60000); return () => { clearInterval(interval); initializedRef.current = false; }; }, []); const content = (
通知 {unreadCount > 0 && ( )}
{recentMessages.length === 0 ? ( ) : ( ( { if (!item.is_read) { markAsRead(item.id); } }} onMouseEnter={(e) => { if (item.is_read) { e.currentTarget.style.background = isDark ? '#1E293B' : '#F8FAFC'; } }} onMouseLeave={(e) => { if (item.is_read) { e.currentTarget.style.background = 'transparent'; } }} >
{item.title} {!item.is_read && ( )}
{item.body}
)} /> )} {recentMessages.length > 0 && (
)}
); return (
{ e.currentTarget.style.background = isDark ? '#1E293B' : '#F1F5F9'; }} onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }} >
); }