import React, { useEffect, useState, useCallback } from 'react'; import { Tag, Typography, Button } from 'antd'; import { BellOutlined, ThunderboltOutlined, GiftOutlined, StarOutlined, CloseOutlined } from '@ant-design/icons'; import { getNotifications, markNotificationRead } from '../api'; import { useAuthStore } from '../store/useAuthStore'; interface Notification { id: string; title: string; content: string; type: string; isRead: boolean; createdAt: string; } const typeConfig: Record = { system: { gradient: 'linear-gradient(135deg, #6366f1, #818cf8)', icon: , label: '系统通知' }, credit: { gradient: 'linear-gradient(135deg, #f59e0b, #fbbf24)', icon: , label: '积分通知' }, promo: { gradient: 'linear-gradient(135deg, #8b5cf6, #a78bfa)', icon: , label: '活动通知' }, }; const NotificationPopup: React.FC = () => { const [visible, setVisible] = useState(false); const [notifications, setNotifications] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const refreshUser = useAuthStore((state) => state.refreshUser); const fetchNotifications = useCallback(async () => { try { const result = await getNotifications(); const data = result.items || []; const unread = data.filter((n: Notification) => !n.isRead); setNotifications(unread); if (unread.length > 0 && !visible) { setVisible(true); setCurrentIndex(0); } } catch { /* ignore */ } }, [visible]); useEffect(() => { fetchNotifications(); const timer = setInterval(fetchNotifications, 30000); return () => clearInterval(timer); }, []); const handleAcknowledge = async () => { const current = notifications[currentIndex]; if (current) { try { await markNotificationRead(current.id); } catch { /* ignore */ } } if (currentIndex < notifications.length - 1) { setCurrentIndex(currentIndex + 1); } else { setVisible(false); setNotifications([]); setCurrentIndex(0); // 刷新用户信息(包括积分) try { await refreshUser(); } catch { /* ignore */ } } }; const handleClose = async () => { const current = notifications[currentIndex]; if (current) { try { await markNotificationRead(current.id); } catch { /* ignore */ } } setVisible(false); setNotifications([]); setCurrentIndex(0); }; if (!visible || notifications.length === 0) return null; const current = notifications[currentIndex]; const tc = typeConfig[current.type] || typeConfig.system; return (
e.stopPropagation()} style={{ width: 420, borderRadius: 20, overflow: 'hidden', boxShadow: '0 32px 80px rgba(0,0,0,0.35)', animation: 'slideUp 0.35s cubic-bezier(0.16,1,0.3,1)', }}> {/* Header */}
{tc.icon}
新消息通知
{current.title}
{ e.currentTarget.style.background = 'rgba(255,255,255,0.25)'; }} onMouseLeave={e => { e.currentTarget.style.background = 'rgba(255,255,255,0.15)'; }} >
{/* Body */}
{current.content}
{tc.label} {notifications.length > 1 && ( {currentIndex + 1} / {notifications.length} )}
{/* Dots */} {notifications.length > 1 && (
{notifications.map((_, i) => (
))}
)}
); }; export default NotificationPopup;