blob: 9e83252cf7766f06483b5b4fa64b3bf8476afb1a [file] [log] [blame]
LaoeGaoci85307e62025-05-30 23:28:42 +08001'use client';
LaoeGaoci85307e62025-05-30 23:28:42 +08002
LaoeGaoci502ecae2025-06-04 17:54:24 +08003import React, { useEffect, useRef, useState } from 'react';
4import axios from 'axios';
5import { DataTable } from 'primereact/datatable';
6import { Column } from 'primereact/column';
7import { Sidebar } from 'primereact/sidebar';
8import { Button } from 'primereact/button';
9import { Tag } from 'primereact/tag';
10import { Toast } from 'primereact/toast';
11import { Paginator, PaginatorPageChangeEvent } from 'primereact/paginator';
12import { Card } from 'primereact/card';
13import { TabView, TabPanel } from 'primereact/tabview'; // ✅ TabView 导入
14
15import './notification.scss';
16
17interface Notification {
18 notificationId: number;
19 title: string;
20 content: string;
21 createAt: string;
22 isRead: boolean;
23 triggeredBy: number;
24 relatedId: number;
25}
26
27export default function NotificationPage() {
28 const toast = useRef<Toast>(null);
29 const [notifications, setNotifications] = useState<Notification[]>([]);
30 const [selectedNotification, setSelectedNotification] = useState<Notification | null>(null);
31 const [visible, setVisible] = useState<boolean>(false);
32 const [activeTab, setActiveTab] = useState<number>(0); // ✅ 当前 Tab 下标
33
34 // 分页相关
35 const [first, setFirst] = useState<number>(0);
36 const [rows, setRows] = useState<number>(5);
37 const [totalRecords, setTotalRecords] = useState<number>(0);
38
39 // 加载数据
40 const fetchNotifications = async () => {
41 try {
42 const pageNumber = first / rows + 1;
43 const res = await axios.get(process.env.PUBLIC_URL + '/notification', {
44 params: { pageNumber, rows, userId: 22301145 },
45 });
46 const { records, total } = res.data;
47 setNotifications(records);
48 setTotalRecords(total);
49 } catch (error) {
50 console.error('无法获取通知数据', error);
51 toast.current?.show({ severity: 'error', summary: '加载失败', detail: '无法获取通知数据' });
52 }
53 };
54
55 useEffect(() => {
56 fetchNotifications();
57 }, [first, rows]); // ✅ 加入分页依赖
58
59 const handleRead = async (id: number) => {
60 try {
61 await axios.post(process.env.PUBLIC_URL + '/notification/read', { notificationId: id });
62 setNotifications(prev =>
63 prev.map(n => (n.notificationId === id ? { ...n, isRead: true } : n))
64 );
65 } catch (error) {
66 console.error('已读失败', error);
67 toast.current?.show({ severity: 'error', summary: '设置已读失败' });
68 }
69 };
70
71 const handleDelete = async (id: number) => {
72 try {
73 await axios.delete(process.env.PUBLIC_URL + '/notification', { data: { notificationId: id } });
74 setNotifications(prev => prev.filter(n => n.notificationId !== id));
75 toast.current?.show({ severity: 'success', summary: '删除成功' });
76 } catch (error) {
77 console.error('删除失败', error);
78 toast.current?.show({ severity: 'error', summary: '删除失败' });
79 }
80 };
81
82 const openSidebar = (notification: Notification) => {
83 if (!notification.isRead) handleRead(notification.notificationId);
84 setSelectedNotification(notification);
85 setVisible(true);
86 };
87
88 const readTemplate = (rowData: Notification) => (
89 <Tag value={rowData.isRead ? '已读' : '未读'} severity={rowData.isRead ? undefined : 'warning'} />
90 );
91
92 const actionTemplate = (rowData: Notification) => (
93 <div className="actions">
94 {!rowData.isRead && (
95 <Button
96 icon="pi pi-check"
97 text
98 severity="success"
99 onClick={() => handleRead(rowData.notificationId)}
100 tooltip="标记为已读"
101 />
102 )}
103 <Button icon="pi pi-times" severity="danger" text onClick={() => handleDelete(rowData.notificationId)} />
LaoeGaoci85307e62025-05-30 23:28:42 +0800104 </div>
105 );
LaoeGaoci85307e62025-05-30 23:28:42 +0800106
LaoeGaoci502ecae2025-06-04 17:54:24 +0800107 const onPageChange = (e: PaginatorPageChangeEvent) => {
108 setFirst(e.first);
109 setRows(e.rows);
110 };
111
112 const unreadNotifications = notifications.filter(n => !n.isRead);
113 const readNotifications = notifications.filter(n => n.isRead);
114
115 return (
116 <div className="notification-page">
117 <Toast ref={toast} />
118 <h2>系统通知</h2>
119
120 <TabView activeIndex={activeTab} onTabChange={(e) => setActiveTab(e.index)}>
121 <TabPanel header="未读通知">
122 <DataTable value={unreadNotifications} paginator={false} emptyMessage="暂无未读通知">
123 <Column field="title" header="标题" body={(row) => (
124 <span className="title-link" onClick={() => openSidebar(row)}>{row.title}</span>
125 )} />
126 <Column field="createAt" header="时间" />
127 <Column field="isRead" header="状态" body={readTemplate} />
128 <Column header="操作" style={{ width: '120px', textAlign: 'center' }} body={actionTemplate} />
129 </DataTable>
130 </TabPanel>
131 <TabPanel header="已读通知">
132 <DataTable value={readNotifications} paginator={false} emptyMessage="暂无已读通知">
133 <Column field="title" header="标题" body={(row) => (
134 <span className="title-link" onClick={() => openSidebar(row)}>{row.title}</span>
135 )} />
136 <Column field="createAt" header="时间" />
137 <Column field="isRead" header="状态" body={readTemplate} />
138 <Column header="操作" style={{ width: '120px', textAlign: 'center' }} body={actionTemplate} />
139 </DataTable>
140 </TabPanel>
141 </TabView>
142
143 <Paginator
144 className="Paginator"
145 first={first}
146 rows={rows}
147 totalRecords={totalRecords}
148 onPageChange={onPageChange}
149 rowsPerPageOptions={[5, 10, 20]}
150 />
151
152 <Sidebar visible={visible} position="right" onHide={() => setVisible(false)} className="p-sidebar-md">
153 {selectedNotification && (
154 <Card title={selectedNotification.title} subTitle={selectedNotification.createAt}>
155 <p className="m-0">{selectedNotification.content}</p>
156 </Card>
157 )}
158 </Sidebar>
159 </div>
160 );
161}