meisiyu | 1d4aade | 2025-06-02 20:10:36 +0800 | [diff] [blame] | 1 | import React, { useState, useEffect } from 'react'; |
| 2 | import { |
| 3 | Card, |
| 4 | Table, |
| 5 | Button, |
| 6 | Modal, |
| 7 | Tag, |
| 8 | Space, |
| 9 | message, |
| 10 | Typography, |
| 11 | Input |
| 12 | } from 'antd'; |
| 13 | import { |
| 14 | EyeOutlined, |
| 15 | CheckOutlined, |
| 16 | CloseOutlined |
| 17 | } from '@ant-design/icons'; |
| 18 | import { getReportList, handleReport } from '@/services/post'; |
| 19 | import styles from './index.module.css'; |
| 20 | |
| 21 | const { Title, Paragraph } = Typography; |
| 22 | const { TextArea } = Input; |
| 23 | |
| 24 | interface 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 | |
| 38 | const 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 | |
| 325 | export default ReportManagement; |