| 'use client'; |
| |
| import React, { useEffect, useRef, useState } from 'react'; |
| import axios from 'axios'; |
| import { DataTable } from 'primereact/datatable'; |
| import { Column } from 'primereact/column'; |
| import { Sidebar } from 'primereact/sidebar'; |
| import { Button } from 'primereact/button'; |
| import { Tag } from 'primereact/tag'; |
| import { Toast } from 'primereact/toast'; |
| import { Paginator, PaginatorPageChangeEvent } from 'primereact/paginator'; |
| import { Card } from 'primereact/card'; |
| import { TabView, TabPanel } from 'primereact/tabview'; // ✅ TabView 导入 |
| |
| import './notification.scss'; |
| |
| interface Notification { |
| notificationId: number; |
| title: string; |
| content: string; |
| createAt: string; |
| isRead: boolean; |
| triggeredBy: number; |
| relatedId: number; |
| } |
| |
| export default function NotificationPage() { |
| const toast = useRef<Toast>(null); |
| const [notifications, setNotifications] = useState<Notification[]>([]); |
| const [selectedNotification, setSelectedNotification] = useState<Notification | null>(null); |
| const [visible, setVisible] = useState<boolean>(false); |
| const [activeTab, setActiveTab] = useState<number>(0); // ✅ 当前 Tab 下标 |
| |
| // 分页相关 |
| const [first, setFirst] = useState<number>(0); |
| const [rows, setRows] = useState<number>(5); |
| const [totalRecords, setTotalRecords] = useState<number>(0); |
| |
| // 加载数据 |
| const fetchNotifications = async () => { |
| try { |
| const pageNumber = first / rows + 1; |
| const res = await axios.get(process.env.PUBLIC_URL + '/notification', { |
| params: { pageNumber, rows, userId: 22301145 }, |
| }); |
| const { records, total } = res.data; |
| setNotifications(records); |
| setTotalRecords(total); |
| } catch (error) { |
| console.error('无法获取通知数据', error); |
| toast.current?.show({ severity: 'error', summary: '加载失败', detail: '无法获取通知数据' }); |
| } |
| }; |
| |
| useEffect(() => { |
| fetchNotifications(); |
| }, [first, rows]); // ✅ 加入分页依赖 |
| |
| const handleRead = async (id: number) => { |
| try { |
| await axios.post(process.env.PUBLIC_URL + '/notification/read', { notificationId: id }); |
| setNotifications(prev => |
| prev.map(n => (n.notificationId === id ? { ...n, isRead: true } : n)) |
| ); |
| } catch (error) { |
| console.error('已读失败', error); |
| toast.current?.show({ severity: 'error', summary: '设置已读失败' }); |
| } |
| }; |
| |
| const handleDelete = async (id: number) => { |
| try { |
| await axios.delete(process.env.PUBLIC_URL + '/notification', { data: { notificationId: id } }); |
| setNotifications(prev => prev.filter(n => n.notificationId !== id)); |
| toast.current?.show({ severity: 'success', summary: '删除成功' }); |
| } catch (error) { |
| console.error('删除失败', error); |
| toast.current?.show({ severity: 'error', summary: '删除失败' }); |
| } |
| }; |
| |
| const openSidebar = (notification: Notification) => { |
| if (!notification.isRead) handleRead(notification.notificationId); |
| setSelectedNotification(notification); |
| setVisible(true); |
| }; |
| |
| const readTemplate = (rowData: Notification) => ( |
| <Tag value={rowData.isRead ? '已读' : '未读'} severity={rowData.isRead ? undefined : 'warning'} /> |
| ); |
| |
| const actionTemplate = (rowData: Notification) => ( |
| <div className="actions"> |
| {!rowData.isRead && ( |
| <Button |
| icon="pi pi-check" |
| text |
| severity="success" |
| onClick={() => handleRead(rowData.notificationId)} |
| tooltip="标记为已读" |
| /> |
| )} |
| <Button icon="pi pi-times" severity="danger" text onClick={() => handleDelete(rowData.notificationId)} /> |
| </div> |
| ); |
| |
| const onPageChange = (e: PaginatorPageChangeEvent) => { |
| setFirst(e.first); |
| setRows(e.rows); |
| }; |
| |
| const unreadNotifications = notifications.filter(n => !n.isRead); |
| const readNotifications = notifications.filter(n => n.isRead); |
| |
| return ( |
| <div className="notification-page"> |
| <Toast ref={toast} /> |
| <h2>系统通知</h2> |
| |
| <TabView activeIndex={activeTab} onTabChange={(e) => setActiveTab(e.index)}> |
| <TabPanel header="未读通知"> |
| <DataTable value={unreadNotifications} paginator={false} emptyMessage="暂无未读通知"> |
| <Column field="title" header="标题" body={(row) => ( |
| <span className="title-link" onClick={() => openSidebar(row)}>{row.title}</span> |
| )} /> |
| <Column field="createAt" header="时间" /> |
| <Column field="isRead" header="状态" body={readTemplate} /> |
| <Column header="操作" style={{ width: '120px', textAlign: 'center' }} body={actionTemplate} /> |
| </DataTable> |
| </TabPanel> |
| <TabPanel header="已读通知"> |
| <DataTable value={readNotifications} paginator={false} emptyMessage="暂无已读通知"> |
| <Column field="title" header="标题" body={(row) => ( |
| <span className="title-link" onClick={() => openSidebar(row)}>{row.title}</span> |
| )} /> |
| <Column field="createAt" header="时间" /> |
| <Column field="isRead" header="状态" body={readTemplate} /> |
| <Column header="操作" style={{ width: '120px', textAlign: 'center' }} body={actionTemplate} /> |
| </DataTable> |
| </TabPanel> |
| </TabView> |
| |
| <Paginator |
| className="Paginator" |
| first={first} |
| rows={rows} |
| totalRecords={totalRecords} |
| onPageChange={onPageChange} |
| rowsPerPageOptions={[5, 10, 20]} |
| /> |
| |
| <Sidebar visible={visible} position="right" onHide={() => setVisible(false)} className="p-sidebar-md"> |
| {selectedNotification && ( |
| <Card title={selectedNotification.title} subTitle={selectedNotification.createAt}> |
| <p className="m-0">{selectedNotification.content}</p> |
| </Card> |
| )} |
| </Sidebar> |
| </div> |
| ); |
| } |