创作中心模块包含首页展示、个人中心、帖子审核。
“首页展示”支持广告轮播展示、推广帖子优先展示、分页显示所有帖子、导航栏便捷标签筛选帖子、全局标题模糊搜索帖子、点击帖子“查看更多”进入帖子详情页。帖子详情页展示帖子封面图片、作者时间、详细内容(可以插入种子链接对种子进行介绍与推广)等基本信息、对帖子点赞收藏举报评论回复、查看相关推荐帖子。相关推荐会推荐当前帖子作者的其他帖子(最多推荐5篇),还会推荐具有相似标签的其他帖子,两者总共最多推荐9篇帖子。
“个人中心”包含“我的中心”和“我的收藏”。
“我的中心”中可以管理已经成功发布的帖子(编辑、删除帖子),还可以发布新帖子。发布新帖子时除了填写帖子基本信息以外,帖子标签支持下拉多项选择,用户还可以选择帖子推广项目并进行支付。设置了多种推广项目,包含广告轮播推广、帖子置顶展示、限时优先展示、分类页首条展示。系统后台执行自动定时任务,每小时对帖子的推广时效性进行检查,如超出推广时限,则取消帖子的推广显示特权。用户点击发布帖子后帖子处于待审核状态,需要管理员审核通过才能正常发布在首页展示页面。编辑帖子时用户可以追加帖子推广,但如果帖子处于推广状态,则禁止修改推广项目。
“我的收藏”中可以便捷查看所有已收藏的帖子。
“帖子审核”包含“帖子发布管理”和“帖子举报管理”。“帖子审核”板块具有权限管理,只有管理员界面能够进入。
“帖子发布管理”对所有待审核帖子进行处理,支持预览待审核帖子详细内容,批准通过和拒绝通过选项。
“帖子举报管理”对所有用户的举报请求进行人工审核,如果举报内容属实,则将帖子下架处理,如果举报内容不属实,驳回举报请求。所有举报请求的处理结果均留存显示,方便后续再次审查。
Change-Id: If822351183e9d55a5a56ff5cf1e13b313fdbe231
diff --git a/src/pages/PostReview/ReportManagement.tsx b/src/pages/PostReview/ReportManagement.tsx
new file mode 100644
index 0000000..f5c0e99
--- /dev/null
+++ b/src/pages/PostReview/ReportManagement.tsx
@@ -0,0 +1,325 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Card,
+ Table,
+ Button,
+ Modal,
+ Tag,
+ Space,
+ message,
+ Typography,
+ Input
+} from 'antd';
+import {
+ EyeOutlined,
+ CheckOutlined,
+ CloseOutlined
+} from '@ant-design/icons';
+import { getReportList, handleReport } from '@/services/post';
+import styles from './index.module.css';
+
+const { Title, Paragraph } = Typography;
+const { TextArea } = Input;
+
+interface ReportInfo {
+ reportId: number;
+ postId: number;
+ postTitle: string;
+ reportUserId: number;
+ reportUserName: string;
+ reportReason: string;
+ status: string;
+ handleResult?: string;
+ handleTime?: string;
+ handleBy?: string;
+ createTime: string;
+}
+
+const ReportManagement: React.FC = () => {
+ const [reports, setReports] = useState<ReportInfo[]>([]);
+ const [loading, setLoading] = useState(false);
+ const [detailModalVisible, setDetailModalVisible] = useState(false);
+ const [handleModalVisible, setHandleModalVisible] = useState(false);
+ const [currentReport, setCurrentReport] = useState<ReportInfo | null>(null);
+ const [currentAction, setCurrentAction] = useState<'approve' | 'reject' | null>(null);
+ const [handleReason, setHandleReason] = useState('');
+
+ useEffect(() => {
+ fetchReports();
+ }, []);
+
+ const fetchReports = async () => {
+ setLoading(true);
+ try {
+ const response = await getReportList({
+ pageNum: 1,
+ pageSize: 100
+ });
+
+ if (response.code === 200) {
+ setReports(response.rows || []);
+ } else {
+ message.error(response.msg || '获取举报列表失败');
+ }
+ } catch (error) {
+ message.error('获取举报列表失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleReportAction = async (report: ReportInfo, action: 'approve' | 'reject') => {
+ setCurrentReport(report);
+ setCurrentAction(action);
+ setHandleReason('');
+ setHandleModalVisible(true);
+ };
+
+ const submitHandle = async () => {
+ if (!currentReport || !currentAction) return;
+
+ try {
+ const response = await handleReport(
+ currentReport.reportId,
+ currentAction,
+ currentReport.postId,
+ handleReason
+ );
+
+ if (response.code === 200) {
+ message.success(currentAction === 'approve' ? '举报处理成功,帖子已下架' : '举报已驳回');
+ fetchReports();
+ } else {
+ message.error(response.msg || '处理失败');
+ }
+ } catch (error) {
+ message.error('处理失败');
+ }
+
+ setHandleModalVisible(false);
+ setCurrentReport(null);
+ setCurrentAction(null);
+ setHandleReason('');
+ };
+
+ const showDetail = (report: ReportInfo) => {
+ setCurrentReport(report);
+ setDetailModalVisible(true);
+ };
+
+ const columns = [
+ {
+ title: '举报帖子',
+ dataIndex: 'postTitle',
+ key: 'postTitle',
+ width: 180,
+ render: (text: string, record: ReportInfo) => (
+ <a onClick={() => showDetail(record)} style={{ color: '#1890ff' }}>
+ {text}
+ </a>
+ ),
+ },
+ {
+ title: '举报人',
+ dataIndex: 'reportUserName',
+ key: 'reportUserName',
+ width: 100,
+ },
+ {
+ title: '举报时间',
+ dataIndex: 'createTime',
+ key: 'createTime',
+ width: 140,
+ },
+ {
+ title: '举报理由',
+ dataIndex: 'reportReason',
+ key: 'reportReason',
+ width: 150,
+ ellipsis: true,
+ render: (text: string) => (
+ <span title={text}>{text}</span>
+ ),
+ },
+ {
+ title: '状态',
+ dataIndex: 'status',
+ key: 'status',
+ width: 80,
+ render: (status: string) => {
+ const statusMap: Record<string, { color: string; text: string }> = {
+ '0': { color: 'orange', text: '待处理' },
+ '1': { color: 'green', text: '已处理' },
+ '2': { color: 'red', text: '已驳回' }
+ };
+ const statusInfo = statusMap[status] || { color: 'gray', text: '未知' };
+ return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
+ },
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 300,
+ fixed: 'right' as const,
+ render: (text: any, record: ReportInfo) => (
+ <Space size="small">
+ <Button
+ type="link"
+ icon={<EyeOutlined />}
+ onClick={() => showDetail(record)}
+ size="small"
+ >
+ 查看
+ </Button>
+ {record.status === '0' && (
+ <>
+ <Button
+ type="link"
+ icon={<CheckOutlined />}
+ style={{ color: 'green' }}
+ onClick={() => handleReportAction(record, 'approve')}
+ size="small"
+ >
+ 确认下架
+ </Button>
+ <Button
+ type="link"
+ danger
+ icon={<CloseOutlined />}
+ onClick={() => handleReportAction(record, 'reject')}
+ size="small"
+ >
+ 驳回举报
+ </Button>
+ </>
+ )}
+ </Space>
+ ),
+ },
+ ];
+
+ return (
+ <div className={styles.postReviewContainer}>
+ <Card title="帖子举报管理">
+ <Table
+ columns={columns}
+ dataSource={reports}
+ loading={loading}
+ rowKey="reportId"
+ scroll={{ x: 880 }}
+ pagination={{
+ pageSize: 10,
+ showTotal: (total) => `共 ${total} 条记录`,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ }}
+ />
+ </Card>
+
+ {/* 举报详情弹窗 */}
+ <Modal
+ title="举报详情"
+ open={detailModalVisible}
+ onCancel={() => {
+ setDetailModalVisible(false);
+ setCurrentReport(null);
+ }}
+ footer={null}
+ width={600}
+ >
+ {currentReport && (
+ <div>
+ <div style={{ marginBottom: 16 }}>
+ <strong>举报帖子:</strong>{currentReport.postTitle}
+ </div>
+ <div style={{ marginBottom: 16 }}>
+ <strong>举报人:</strong>{currentReport.reportUserName}
+ </div>
+ <div style={{ marginBottom: 16 }}>
+ <strong>举报时间:</strong>{currentReport.createTime}
+ </div>
+ <div style={{ marginBottom: 16 }}>
+ <strong>举报理由:</strong>
+ <Paragraph>{currentReport.reportReason}</Paragraph>
+ </div>
+ {currentReport.status !== '0' && (
+ <>
+ <div style={{ marginBottom: 16 }}>
+ <strong>处理结果:</strong>{currentReport.handleResult}
+ </div>
+ <div style={{ marginBottom: 16 }}>
+ <strong>处理时间:</strong>{currentReport.handleTime}
+ </div>
+ <div style={{ marginBottom: 16 }}>
+ <strong>处理人:</strong>{currentReport.handleBy}
+ </div>
+ </>
+ )}
+ {currentReport.status === '0' && (
+ <div style={{ marginTop: 16 }}>
+ <Space>
+ <Button
+ type="primary"
+ icon={<CheckOutlined />}
+ onClick={() => {
+ handleReportAction(currentReport, 'approve');
+ setDetailModalVisible(false);
+ }}
+ >
+ 确认下架
+ </Button>
+ <Button
+ danger
+ icon={<CloseOutlined />}
+ onClick={() => {
+ handleReportAction(currentReport, 'reject');
+ setDetailModalVisible(false);
+ }}
+ >
+ 驳回举报
+ </Button>
+ </Space>
+ </div>
+ )}
+ </div>
+ )}
+ </Modal>
+
+ {/* 处理举报弹窗 */}
+ <Modal
+ title={currentAction === 'approve' ? '确认下架帖子' : '驳回举报'}
+ open={handleModalVisible}
+ onOk={submitHandle}
+ onCancel={() => {
+ setHandleModalVisible(false);
+ setCurrentReport(null);
+ setCurrentAction(null);
+ setHandleReason('');
+ }}
+ okText="确定"
+ cancelText="取消"
+ >
+ <div style={{ marginBottom: 16 }}>
+ <strong>帖子:</strong>{currentReport?.postTitle}
+ </div>
+ <div>
+ <strong>
+ {currentAction === 'approve' ? '下架理由' : '驳回理由'}
+ (可选):
+ </strong>
+ <TextArea
+ value={handleReason}
+ onChange={(e) => setHandleReason(e.target.value)}
+ placeholder={
+ currentAction === 'approve' ? '请输入下架理由...' : '请输入驳回理由...'
+ }
+ rows={4}
+ style={{ marginTop: 8 }}
+ />
+ </div>
+ </Modal>
+ </div>
+ );
+};
+
+export default ReportManagement;
\ No newline at end of file