blob: f5c0e99fd4806f26d7e27271a2397b339f2a3287 [file] [log] [blame]
meisiyu1d4aade2025-06-02 20:10:36 +08001import React, { useState, useEffect } from 'react';
2import {
3 Card,
4 Table,
5 Button,
6 Modal,
7 Tag,
8 Space,
9 message,
10 Typography,
11 Input
12} from 'antd';
13import {
14 EyeOutlined,
15 CheckOutlined,
16 CloseOutlined
17} from '@ant-design/icons';
18import { getReportList, handleReport } from '@/services/post';
19import styles from './index.module.css';
20
21const { Title, Paragraph } = Typography;
22const { TextArea } = Input;
23
24interface ReportInfo {
25 reportId: number;
26 postId: number;
27 postTitle: string;
28 reportUserId: number;
29 reportUserName: string;
30 reportReason: string;
31 status: string;
32 handleResult?: string;
33 handleTime?: string;
34 handleBy?: string;
35 createTime: string;
36}
37
38const ReportManagement: React.FC = () => {
39 const [reports, setReports] = useState<ReportInfo[]>([]);
40 const [loading, setLoading] = useState(false);
41 const [detailModalVisible, setDetailModalVisible] = useState(false);
42 const [handleModalVisible, setHandleModalVisible] = useState(false);
43 const [currentReport, setCurrentReport] = useState<ReportInfo | null>(null);
44 const [currentAction, setCurrentAction] = useState<'approve' | 'reject' | null>(null);
45 const [handleReason, setHandleReason] = useState('');
46
47 useEffect(() => {
48 fetchReports();
49 }, []);
50
51 const fetchReports = async () => {
52 setLoading(true);
53 try {
54 const response = await getReportList({
55 pageNum: 1,
56 pageSize: 100
57 });
58
59 if (response.code === 200) {
60 setReports(response.rows || []);
61 } else {
62 message.error(response.msg || '获取举报列表失败');
63 }
64 } catch (error) {
65 message.error('获取举报列表失败');
66 } finally {
67 setLoading(false);
68 }
69 };
70
71 const handleReportAction = async (report: ReportInfo, action: 'approve' | 'reject') => {
72 setCurrentReport(report);
73 setCurrentAction(action);
74 setHandleReason('');
75 setHandleModalVisible(true);
76 };
77
78 const submitHandle = async () => {
79 if (!currentReport || !currentAction) return;
80
81 try {
82 const response = await handleReport(
83 currentReport.reportId,
84 currentAction,
85 currentReport.postId,
86 handleReason
87 );
88
89 if (response.code === 200) {
90 message.success(currentAction === 'approve' ? '举报处理成功,帖子已下架' : '举报已驳回');
91 fetchReports();
92 } else {
93 message.error(response.msg || '处理失败');
94 }
95 } catch (error) {
96 message.error('处理失败');
97 }
98
99 setHandleModalVisible(false);
100 setCurrentReport(null);
101 setCurrentAction(null);
102 setHandleReason('');
103 };
104
105 const showDetail = (report: ReportInfo) => {
106 setCurrentReport(report);
107 setDetailModalVisible(true);
108 };
109
110 const columns = [
111 {
112 title: '举报帖子',
113 dataIndex: 'postTitle',
114 key: 'postTitle',
115 width: 180,
116 render: (text: string, record: ReportInfo) => (
117 <a onClick={() => showDetail(record)} style={{ color: '#1890ff' }}>
118 {text}
119 </a>
120 ),
121 },
122 {
123 title: '举报人',
124 dataIndex: 'reportUserName',
125 key: 'reportUserName',
126 width: 100,
127 },
128 {
129 title: '举报时间',
130 dataIndex: 'createTime',
131 key: 'createTime',
132 width: 140,
133 },
134 {
135 title: '举报理由',
136 dataIndex: 'reportReason',
137 key: 'reportReason',
138 width: 150,
139 ellipsis: true,
140 render: (text: string) => (
141 <span title={text}>{text}</span>
142 ),
143 },
144 {
145 title: '状态',
146 dataIndex: 'status',
147 key: 'status',
148 width: 80,
149 render: (status: string) => {
150 const statusMap: Record<string, { color: string; text: string }> = {
151 '0': { color: 'orange', text: '待处理' },
152 '1': { color: 'green', text: '已处理' },
153 '2': { color: 'red', text: '已驳回' }
154 };
155 const statusInfo = statusMap[status] || { color: 'gray', text: '未知' };
156 return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
157 },
158 },
159 {
160 title: '操作',
161 key: 'action',
162 width: 300,
163 fixed: 'right' as const,
164 render: (text: any, record: ReportInfo) => (
165 <Space size="small">
166 <Button
167 type="link"
168 icon={<EyeOutlined />}
169 onClick={() => showDetail(record)}
170 size="small"
171 >
172 查看
173 </Button>
174 {record.status === '0' && (
175 <>
176 <Button
177 type="link"
178 icon={<CheckOutlined />}
179 style={{ color: 'green' }}
180 onClick={() => handleReportAction(record, 'approve')}
181 size="small"
182 >
183 确认下架
184 </Button>
185 <Button
186 type="link"
187 danger
188 icon={<CloseOutlined />}
189 onClick={() => handleReportAction(record, 'reject')}
190 size="small"
191 >
192 驳回举报
193 </Button>
194 </>
195 )}
196 </Space>
197 ),
198 },
199 ];
200
201 return (
202 <div className={styles.postReviewContainer}>
203 <Card title="帖子举报管理">
204 <Table
205 columns={columns}
206 dataSource={reports}
207 loading={loading}
208 rowKey="reportId"
209 scroll={{ x: 880 }}
210 pagination={{
211 pageSize: 10,
212 showTotal: (total) => `共 ${total} 条记录`,
213 showSizeChanger: true,
214 showQuickJumper: true,
215 }}
216 />
217 </Card>
218
219 {/* 举报详情弹窗 */}
220 <Modal
221 title="举报详情"
222 open={detailModalVisible}
223 onCancel={() => {
224 setDetailModalVisible(false);
225 setCurrentReport(null);
226 }}
227 footer={null}
228 width={600}
229 >
230 {currentReport && (
231 <div>
232 <div style={{ marginBottom: 16 }}>
233 <strong>举报帖子:</strong>{currentReport.postTitle}
234 </div>
235 <div style={{ marginBottom: 16 }}>
236 <strong>举报人:</strong>{currentReport.reportUserName}
237 </div>
238 <div style={{ marginBottom: 16 }}>
239 <strong>举报时间:</strong>{currentReport.createTime}
240 </div>
241 <div style={{ marginBottom: 16 }}>
242 <strong>举报理由:</strong>
243 <Paragraph>{currentReport.reportReason}</Paragraph>
244 </div>
245 {currentReport.status !== '0' && (
246 <>
247 <div style={{ marginBottom: 16 }}>
248 <strong>处理结果:</strong>{currentReport.handleResult}
249 </div>
250 <div style={{ marginBottom: 16 }}>
251 <strong>处理时间:</strong>{currentReport.handleTime}
252 </div>
253 <div style={{ marginBottom: 16 }}>
254 <strong>处理人:</strong>{currentReport.handleBy}
255 </div>
256 </>
257 )}
258 {currentReport.status === '0' && (
259 <div style={{ marginTop: 16 }}>
260 <Space>
261 <Button
262 type="primary"
263 icon={<CheckOutlined />}
264 onClick={() => {
265 handleReportAction(currentReport, 'approve');
266 setDetailModalVisible(false);
267 }}
268 >
269 确认下架
270 </Button>
271 <Button
272 danger
273 icon={<CloseOutlined />}
274 onClick={() => {
275 handleReportAction(currentReport, 'reject');
276 setDetailModalVisible(false);
277 }}
278 >
279 驳回举报
280 </Button>
281 </Space>
282 </div>
283 )}
284 </div>
285 )}
286 </Modal>
287
288 {/* 处理举报弹窗 */}
289 <Modal
290 title={currentAction === 'approve' ? '确认下架帖子' : '驳回举报'}
291 open={handleModalVisible}
292 onOk={submitHandle}
293 onCancel={() => {
294 setHandleModalVisible(false);
295 setCurrentReport(null);
296 setCurrentAction(null);
297 setHandleReason('');
298 }}
299 okText="确定"
300 cancelText="取消"
301 >
302 <div style={{ marginBottom: 16 }}>
303 <strong>帖子:</strong>{currentReport?.postTitle}
304 </div>
305 <div>
306 <strong>
307 {currentAction === 'approve' ? '下架理由' : '驳回理由'}
308 (可选):
309 </strong>
310 <TextArea
311 value={handleReason}
312 onChange={(e) => setHandleReason(e.target.value)}
313 placeholder={
314 currentAction === 'approve' ? '请输入下架理由...' : '请输入驳回理由...'
315 }
316 rows={4}
317 style={{ marginTop: 8 }}
318 />
319 </div>
320 </Modal>
321 </div>
322 );
323};
324
325export default ReportManagement;