'use client'; import { useState, useEffect } from 'react'; import { PageInfo, SiteHeader } from "@/components/site-header"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@nice/ui/components/card'; import { Badge } from '@nice/ui/components/badge'; import { Button } from '@nice/ui/components/button'; import { Checkbox } from '@nice/ui/components/checkbox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@nice/ui/components/select'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@nice/ui/components/dropdown-menu'; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PaginationEllipsis } from '@nice/ui/components/pagination'; import { IconBell, IconBellRinging, IconHeart, IconFileText, IconShare, IconSettings, IconClock, IconCheck, IconChecks, IconTrash, IconFilter, IconDots, IconX } from '@tabler/icons-react'; import { useSetPageInfo } from '@/components/providers/dashboard-provider'; // 通知类型定义 type NotificationType = 'like' | 'comment' | 'follow' | 'system' | 'article' | 'security'; interface Notification { id: string; type: NotificationType; title: string; message: string; time: string; isRead: boolean; isSelected?: boolean; data?: { url?: string; articleId?: string; userId?: string; }; } // 模拟通知数据 const mockNotifications: Notification[] = [ { id: '1', type: 'like', title: '文章获得点赞', message: '您的文章《Next.js 最佳实践》收到了新的点赞', time: '5分钟前', isRead: false, data: { articleId: '123' } }, { id: '2', type: 'comment', title: '新评论', message: '用户李四评论了您的文章《TypeScript 进阶指南》:这篇文章写得非常详细,帮助很大!', time: '1小时前', isRead: false, data: { articleId: '124', userId: 'user_123' } }, { id: '3', type: 'follow', title: '新关注者', message: '用户王五关注了您,快去看看他的主页吧', time: '2小时前', isRead: true, data: { userId: 'user_124' } }, { id: '4', type: 'system', title: '存储空间提醒', message: '您的存储空间使用已达到60%,建议及时清理不必要的文件', time: '6小时前', isRead: false }, { id: '5', type: 'article', title: '文章发布成功', message: '您的文章《React Hooks 深度解析》已成功发布', time: '1天前', isRead: true, data: { articleId: '125' } }, { id: '6', type: 'security', title: '登录提醒', message: '检测到您的账号在新设备上登录,如非本人操作请及时修改密码', time: '2天前', isRead: false }, // 更多模拟数据... ...Array.from({ length: 20 }, (_, i) => ({ id: `${i + 7}`, type: ['like', 'comment', 'follow', 'system'][i % 4] as NotificationType, title: `通知标题 ${i + 7}`, message: `这是第 ${i + 7} 条通知的详细内容`, time: `${i + 3}天前`, isRead: Math.random() > 0.5 })) ]; useSetPageInfo({ title: '通知中心', subtitle: '查看系统通知、消息以及系统更新等', }) // 获取通知类型的图标 function getNotificationIcon(type: NotificationType, isRead: boolean) { const iconClass = `h-5 w-5 ${isRead ? 'opacity-60' : ''}`; switch (type) { case 'like': return ; case 'comment': return ; case 'follow': return ; case 'system': return ; case 'article': return ; case 'security': return ; default: return ; } } // 获取通知类型的标签 function getNotificationTypeLabel(type: NotificationType) { const typeMap: Record = { like: '点赞', comment: '评论', follow: '关注', system: '系统', article: '文章', security: '安全' }; return typeMap[type]; } // 获取通知类型的颜色 function getNotificationTypeBadgeVariant(type: NotificationType) { const variantMap: Record = { like: 'default', comment: 'secondary', follow: 'outline', system: 'secondary', article: 'outline', security: 'destructive' }; return variantMap[type]; } export default function NotificationPage() { const [notifications, setNotifications] = useState(mockNotifications); const [selectedNotifications, setSelectedNotifications] = useState>(new Set()); const [filterType, setFilterType] = useState<'all' | NotificationType>('all'); const [filterRead, setFilterRead] = useState<'all' | 'read' | 'unread'>('all'); const [currentPage, setCurrentPage] = useState(1); const [isAllSelected, setIsAllSelected] = useState(false); const pageSize = 7; // 当筛选条件改变时重置选择状态 useEffect(() => { setSelectedNotifications(new Set()); setIsAllSelected(false); setCurrentPage(1); }, [filterType, filterRead]); // 过滤通知 const filteredNotifications = notifications.filter(notification => { const typeMatch = filterType === 'all' || notification.type === filterType; const readMatch = filterRead === 'all' || (filterRead === 'read' && notification.isRead) || (filterRead === 'unread' && !notification.isRead); return typeMatch && readMatch; }); // 分页 const totalPages = Math.ceil(filteredNotifications.length / pageSize); const startIndex = (currentPage - 1) * pageSize; const currentNotifications = filteredNotifications.slice(startIndex, startIndex + pageSize); // 统计数据 const unreadCount = notifications.filter(n => !n.isRead).length; const selectedCount = isAllSelected ? filteredNotifications.length : selectedNotifications.size; // 批量操作 const handleSelectAll = () => { if (isAllSelected || selectedCount === filteredNotifications.length) { setSelectedNotifications(new Set()); setIsAllSelected(false); } else { setSelectedNotifications(new Set(filteredNotifications.map(n => n.id))); setIsAllSelected(true); } }; const handleSelectNotification = (id: string) => { const newSelected = new Set(selectedNotifications); if (newSelected.has(id)) { newSelected.delete(id); setIsAllSelected(false); } else { newSelected.add(id); if (newSelected.size === filteredNotifications.length) { setIsAllSelected(true); } } setSelectedNotifications(newSelected); }; const handleMarkAsRead = (ids: string[]) => { const targetIds = isAllSelected ? filteredNotifications.map(n => n.id) : ids; setNotifications(prev => prev.map(n => targetIds.includes(n.id) ? { ...n, isRead: true } : n )); setSelectedNotifications(new Set()); setIsAllSelected(false); }; const handleMarkAsUnread = (ids: string[]) => { const targetIds = isAllSelected ? filteredNotifications.map(n => n.id) : ids; setNotifications(prev => prev.map(n => targetIds.includes(n.id) ? { ...n, isRead: false } : n )); setSelectedNotifications(new Set()); setIsAllSelected(false); }; const handleDelete = (ids: string[]) => { const targetIds = isAllSelected ? filteredNotifications.map(n => n.id) : ids; setNotifications(prev => prev.filter(n => !targetIds.includes(n.id))); setSelectedNotifications(new Set()); setIsAllSelected(false); }; const handleNotificationClick = (notification: Notification) => { // 标记为已读 if (!notification.isRead) { handleMarkAsRead([notification.id]); } console.log('Navigate to:', notification.data); }; return ( {/* 左右布局容器 */} {/* 左侧:通知统计卡片 */} {/* 通知统计卡片 */} 通知统计 {/* 总通知 */} 总通知 {notifications.length} {/* 未读通知 */} 未读通知 {unreadCount} {/* 已读通知 */} 已读通知 {notifications.length - unreadCount} {/* 右侧:通知列表 */} 通知列表 {/* 筛选器 */} setFilterType(value)}> 全部类型 点赞 评论 关注 系统 文章 安全 setFilterRead(value)}> 全部状态 未读 已读 {/* 批量操作工具栏 */} {selectedCount > 0 && ( 已选择 {selectedCount} 项 handleMarkAsRead(isAllSelected ? filteredNotifications.map(n => n.id) : Array.from(selectedNotifications))} className="h-7 px-2" > 已读 handleMarkAsUnread(isAllSelected ? filteredNotifications.map(n => n.id) : Array.from(selectedNotifications))} className="h-7 px-2" > 未读 handleDelete(isAllSelected ? filteredNotifications.map(n => n.id) : Array.from(selectedNotifications))} className="h-7 px-2 text-destructive hover:text-destructive" > 删除 )} {/* 操作按钮 */} {isAllSelected ? '取消全选' : '选择全部'} handleMarkAsRead(notifications.filter(n => !n.isRead).map(n => n.id))} disabled={unreadCount === 0} > 全部已读 {currentNotifications.length === 0 ? ( 暂无通知 ) : ( {currentNotifications.map((notification) => ( handleNotificationClick(notification)} > {/* 选择框 */} handleSelectNotification(notification.id)} onClick={(e) => e.stopPropagation()} className="mt-1" /> {/* 通知图标 */} {getNotificationIcon(notification.type, notification.isRead)} {/* 通知内容 */} {notification.title} {getNotificationTypeLabel(notification.type)} {!notification.isRead && ( )} {notification.message} {notification.time} {/* 操作菜单 */} e.stopPropagation()}> { e.stopPropagation(); handleMarkAsRead([notification.id]); }} disabled={notification.isRead} > 标记已读 { e.stopPropagation(); handleMarkAsUnread([notification.id]); }} disabled={!notification.isRead} > 标记未读 { e.stopPropagation(); handleDelete([notification.id]); }} variant="destructive" > 删除 ))} )} {/* 分页 */} {totalPages > 1 && ( { e.preventDefault(); if (currentPage > 1) setCurrentPage(currentPage - 1); }} aria-disabled={currentPage === 1} className={currentPage === 1 ? 'pointer-events-none opacity-50' : ''} /> {/* 页码 */} {Array.from({ length: Math.min(totalPages, 7) }, (_, i) => { let pageNum; if (totalPages <= 7) { pageNum = i + 1; } else if (currentPage <= 4) { pageNum = i + 1; } else if (currentPage >= totalPages - 3) { pageNum = totalPages - 6 + i; } else { pageNum = currentPage - 3 + i; } if (pageNum === currentPage - 2 && currentPage > 4 && totalPages > 7) { return ( ); } if (pageNum === currentPage + 2 && currentPage < totalPages - 3 && totalPages > 7) { return ( ); } return ( { e.preventDefault(); setCurrentPage(pageNum); }} isActive={pageNum === currentPage} > {pageNum} ); })} { e.preventDefault(); if (currentPage < totalPages) setCurrentPage(currentPage + 1); }} aria-disabled={currentPage === totalPages} className={currentPage === totalPages ? 'pointer-events-none opacity-50' : ''} /> )} ); }
总通知
{notifications.length}
未读通知
{unreadCount}
已读通知
{notifications.length - unreadCount}
暂无通知
{notification.message}