From 3e06cfde7610bd2b2f0cb1ddc9a8586cd3c0cd02 Mon Sep 17 00:00:00 2001 From: lafay <2021211506@stu.hit.edu.cn> Date: Mon, 30 Mar 2026 18:01:38 +0800 Subject: [PATCH] Enhance Reports page with safe date formatting, improved status colors and icons, and add statistics display for report statuses. --- src/pages/Reports.tsx | 686 +++++++++++++++++++++++++++++------------- 1 file changed, 472 insertions(+), 214 deletions(-) diff --git a/src/pages/Reports.tsx b/src/pages/Reports.tsx index 539f6e4..bfbad67 100644 --- a/src/pages/Reports.tsx +++ b/src/pages/Reports.tsx @@ -43,16 +43,30 @@ import { FileText, MessageSquare, Mail, + AlertTriangle, + Clock, + CheckCircle2, + XCircle, + Filter, + ChevronRight, } from 'lucide-react' import { format } from 'date-fns' import type { PaginatedResponse } from '@/types' +// 安全的日期格式化函数,处理无效日期 +function safeFormat(dateValue: string | number | Date | null | undefined, formatStr: string): string { + if (!dateValue) return '-' + const date = new Date(dateValue) + if (isNaN(date.getTime())) return '-' + return format(date, formatStr) +} + // 状态颜色映射 const statusColors: Record = { - pending: 'bg-yellow-500', - processing: 'bg-blue-500', - resolved: 'bg-green-500', - rejected: 'bg-gray-500', + pending: 'bg-yellow-100 text-yellow-800 border-yellow-200', + processing: 'bg-blue-100 text-blue-800 border-blue-200', + resolved: 'bg-emerald-100 text-emerald-800 border-emerald-200', + rejected: 'bg-gray-100 text-gray-600 border-gray-200', } // 状态文本映射 @@ -63,18 +77,28 @@ const statusText: Record = { rejected: '已驳回', } -// 目标类型图标 -const targetTypeIcons: Record = { - post: , - comment: , - message: , +// 状态图标映射 +const statusIcons: Record = { + pending: , + processing: , + resolved: , + rejected: , } -// 目标类型文本 -const targetTypeText: Record = { - post: '帖子', - comment: '评论', - message: '消息', +// 举报原因图标和颜色 +const reasonConfig: Record = { + spam: { icon: , color: 'bg-purple-100 text-purple-700' }, + inappropriate: { icon: , color: 'bg-red-100 text-red-700' }, + harassment: { icon: , color: 'bg-orange-100 text-orange-700' }, + misinformation: { icon: , color: 'bg-amber-100 text-amber-700' }, + other: { icon: , color: 'bg-gray-100 text-gray-600' }, +} + +// 目标类型配置 +const targetTypeConfig: Record = { + post: { icon: , color: 'text-blue-600 bg-blue-50', label: '帖子' }, + comment: { icon: , color: 'text-green-600 bg-green-50', label: '评论' }, + message: { icon: , color: 'text-purple-600 bg-purple-50', label: '消息' }, } // 举报原因文本 @@ -97,6 +121,14 @@ export default function Reports() { const [statusFilter, setStatusFilter] = useState('all') const [typeFilter, setTypeFilter] = useState('all') + // 统计数据 + const [stats, setStats] = useState({ + pending: 0, + processing: 0, + resolved: 0, + rejected: 0, + }) + // 批量选择 const [selectedIds, setSelectedIds] = useState([]) const [isAllSelected, setIsAllSelected] = useState(false) @@ -131,6 +163,15 @@ export default function Reports() { const data = response.data.data as PaginatedResponse setReports(data.list || []) setTotal(data.total || 0) + + // 更新统计数据(仅当无筛选时) + if (!keyword && statusFilter === 'all' && typeFilter === 'all') { + const newStats = { pending: 0, processing: 0, resolved: 0, rejected: 0 } + data.list?.forEach((report: ReportListItem) => { + newStats[report.status] = (newStats[report.status] || 0) + 1 + }) + setStats(newStats) + } } catch (error) { console.error('Failed to load reports:', error) } finally { @@ -235,36 +276,115 @@ export default function Reports() { } return ( -
- {/* 标题 */} -
+
+ {/* 页面标题 */} +
-

举报管理

-

处理用户举报的帖子、评论和消息

+
+
+ +
+
+

举报管理

+

处理用户举报的帖子、评论和消息,维护社区环境

+
+
-
+ {/* 统计卡片 */} +
+ + +
+
+

待处理

+

{stats.pending}

+
+
+ +
+
+
+
+ + + +
+
+

处理中

+

{stats.processing}

+
+
+ +
+
+
+
+ + + +
+
+

已确认

+

{stats.resolved}

+
+
+ +
+
+
+
+ + + +
+
+

已驳回

+

{stats.rejected}

+
+
+ +
+
+
+
+
+ {/* 筛选卡片 */} - - - 筛选条件 + + + + + 筛选条件 + -
-
- setKeyword(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleSearch()} - /> +
+
+
+ + setKeyword(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSearch()} + className="pl-9" + /> +
- @@ -295,11 +415,18 @@ export default function Reports() { {/* 举报列表 */} - - + +
- 举报列表 - 共 {total} 条记录 + 举报列表 + + 共 {total} 条记录 + {selectedIds.length > 0 && ( + + · 已选择 {selectedIds.length} 条 + + )} +
{selectedIds.length > 0 && (
@@ -308,15 +435,21 @@ export default function Reports() { size="sm" onClick={() => handleBatchAction('approve')} disabled={batchLoading} + className="border-emerald-200 text-emerald-700 hover:bg-emerald-50" > - {batchLoading ? : } - 批量确认 ({selectedIds.length}) + {batchLoading ? ( + + ) : ( + + )} + 批量确认 + {report.status === 'pending' && ( + + )} + +
+ + + ) + }) + )} + + +
+
+
+ 显示第 {(page - 1) * pageSize + 1} - {Math.min(page * pageSize, total)} 条 +
+ +
{/* 详情对话框 */} - + - 举报详情 + + + 举报详情 + {detailLoading ? ( -
- +
+
+ + 加载中... +
) : selectedReport ? ( -
-
+
+ {/* 基本信息 */} +
+

基本信息

+
+
+ +
+ + + {selectedReport.reporter?.nickname?.[0]} + + + {selectedReport.reporter?.nickname} + +
+
+
+ +

+ {safeFormat(selectedReport.created_at, 'yyyy-MM-dd HH:mm:ss')} +

+
- -
- - - {selectedReport.reporter?.nickname?.[0]} - - {selectedReport.reporter?.nickname} + + {(() => { + const detailTypeCfg = targetTypeConfig[selectedReport.target_type] || { + icon: , + color: 'text-slate-600 bg-slate-50', + label: selectedReport.target_type || '未知', + } + return ( +
+ {detailTypeCfg.icon} + {detailTypeCfg.label} +
+ ) + })()} +
+
+ +
+ + {statusIcons[selectedReport.status]} + {statusText[selectedReport.status]} + +
-
- -

{format(new Date(selectedReport.created_at), 'yyyy-MM-dd HH:mm:ss')}

-
-
- -

{targetTypeText[selectedReport.target_type]}

-
-
- - - {statusText[selectedReport.status]} - -
-
- -

{reasonText[selectedReport.reason] || selectedReport.reason}

+
+ + {/* 举报原因 */} +
+ +
+ {reasonConfig[selectedReport.reason]?.icon || reasonConfig.other.icon} + + {reasonText[selectedReport.reason] || selectedReport.reason} +
{selectedReport.description && ( -
- -

{selectedReport.description}

+
+ +

{selectedReport.description}

)}
+ {/* 被举报内容 */} {selectedReport.target_content && ( - - - 被举报内容 - - - {selectedReport.target_content.title && ( -

{selectedReport.target_content.title}

- )} -

{selectedReport.target_content.content}

- {selectedReport.target_content.images && selectedReport.target_content.images.length > 0 && ( -
- {selectedReport.target_content.images.map((img, i) => ( - - ))} -
- )} -
-
- )} - - {selectedReport.handler && ( -
- -
- - - {selectedReport.handler.nickname?.[0]} - - {selectedReport.handler.nickname} - {selectedReport.handled_at && ( - - {format(new Date(selectedReport.handled_at), 'yyyy-MM-dd HH:mm')} - - )} -
+
+

被举报内容

+ {selectedReport.target_content.title && ( +

{selectedReport.target_content.title}

+ )} +

{selectedReport.target_content.content}

+ {selectedReport.target_content.images && selectedReport.target_content.images.length > 0 && ( +
+ {selectedReport.target_content.images.map((img, i) => ( + + ))} +
+ )}
)} - {selectedReport.result && ( -
- -

{selectedReport.result}

+ {/* 处理信息 */} + {selectedReport.handler && ( +
+

处理信息

+
+ + + {selectedReport.handler.nickname?.[0]} + +
+

{selectedReport.handler.nickname}

+ {selectedReport.handled_at && ( +

+ {safeFormat(selectedReport.handled_at, 'yyyy-MM-dd HH:mm')} +

+ )} +
+
+ {selectedReport.result && ( +
+ +

{selectedReport.result}

+
+ )}
)}
@@ -520,43 +758,63 @@ export default function Reports() { {/* 处理对话框 */} - + - 处理举报 - 请选择处理方式 + + + 处理举报 + + + 请根据举报内容选择处理方式 +
-
+
- +