| import React, { useState, useEffect } from 'react'; |
| import { |
| Card, |
| Table, |
| Button, |
| Modal, |
| Tag, |
| Space, |
| message, |
| Popconfirm, |
| Typography, |
| Image, |
| Row, |
| Col, |
| Input, |
| Tabs |
| } from 'antd'; |
| import { |
| EyeOutlined, |
| CheckOutlined, |
| CloseOutlined, |
| DeleteOutlined |
| } from '@ant-design/icons'; |
| import { getReviewPosts, reviewPost, takeDownPost } from '@/services/post'; |
| import ReportManagement from './ReportManagement'; |
| import styles from './index.module.css'; |
| |
| const { Title, Paragraph } = Typography; |
| const { TextArea } = Input; |
| const { TabPane } = Tabs; |
| |
| interface PostReviewProps {} |
| |
| const PostReview: React.FC<PostReviewProps> = () => { |
| const [activeTab, setActiveTab] = useState('review'); |
| const [posts, setPosts] = useState<API.Post.PostInfo[]>([]); |
| const [loading, setLoading] = useState(false); |
| const [detailModalVisible, setDetailModalVisible] = useState(false); |
| const [reasonModalVisible, setReasonModalVisible] = useState(false); |
| const [currentPost, setCurrentPost] = useState<API.Post.PostInfo | null>(null); |
| const [currentAction, setCurrentAction] = useState<'approve' | 'reject' | 'takedown' | null>(null); |
| const [reason, setReason] = useState(''); |
| |
| useEffect(() => { |
| if (activeTab === 'review') { |
| fetchPendingPosts(); |
| } |
| }, [activeTab]); |
| |
| const fetchPendingPosts = async () => { |
| setLoading(true); |
| try { |
| const response = await getReviewPosts({ |
| pageNum: 1, |
| pageSize: 100, |
| status: '0' // 只查询待审核的帖子 |
| }); |
| |
| if (response.code === 200) { |
| setPosts(response.rows || []); |
| } else { |
| message.error(response.msg || '获取待审核帖子失败'); |
| } |
| } catch (error) { |
| message.error('获取待审核帖子失败'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| const handleAction = async (postId: number, action: 'approve' | 'reject', reason?: string) => { |
| try { |
| const response = await reviewPost(postId, action, reason); |
| |
| if (response.code === 200) { |
| message.success(action === 'approve' ? '帖子审核通过' : '帖子已拒绝'); |
| fetchPendingPosts(); |
| } else { |
| message.error(response.msg || '操作失败'); |
| } |
| } catch (error) { |
| message.error('操作失败'); |
| } |
| }; |
| |
| const handleTakeDown = async (postId: number, reason?: string) => { |
| try { |
| const response = await takeDownPost(postId, reason); |
| |
| if (response.code === 200) { |
| message.success('帖子已下架'); |
| fetchPendingPosts(); |
| } else { |
| message.error(response.msg || '操作失败'); |
| } |
| } catch (error) { |
| message.error('操作失败'); |
| } |
| }; |
| |
| const showReasonModal = (post: API.Post.PostInfo, action: 'approve' | 'reject' | 'takedown') => { |
| setCurrentPost(post); |
| setCurrentAction(action); |
| setReason(''); |
| setReasonModalVisible(true); |
| }; |
| |
| const handleReasonSubmit = () => { |
| if (!currentPost || !currentAction) return; |
| |
| if (currentAction === 'takedown') { |
| handleTakeDown(currentPost.postId || 0, reason); |
| } else { |
| handleAction(currentPost.postId || 0, currentAction, reason); |
| } |
| |
| setReasonModalVisible(false); |
| setCurrentPost(null); |
| setCurrentAction(null); |
| setReason(''); |
| }; |
| |
| const handleViewDetail = (post: API.Post.PostInfo) => { |
| setCurrentPost(post); |
| setDetailModalVisible(true); |
| }; |
| |
| const columns = [ |
| { |
| title: '帖子标题', |
| dataIndex: 'title', |
| key: 'title', |
| width: 200, |
| render: (text: string, record: API.Post.PostInfo) => ( |
| <a onClick={() => handleViewDetail(record)}>{text}</a> |
| ), |
| }, |
| { |
| title: '作者', |
| dataIndex: 'author', |
| key: 'author', |
| width: 100, |
| }, |
| { |
| title: '发布时间', |
| dataIndex: 'publishTime', |
| key: 'publishTime', |
| width: 150, |
| }, |
| { |
| title: '状态', |
| dataIndex: 'status', |
| key: 'status', |
| width: 100, |
| render: (status: string) => { |
| const statusMap: Record<string, { color: string; text: string }> = { |
| '0': { color: 'orange', text: '待审核' }, |
| '1': { color: 'green', text: '已发布' }, |
| '2': { color: 'red', text: '已拒绝' }, |
| '3': { color: 'gray', text: '已下架' } |
| }; |
| const statusInfo = statusMap[status] || { color: 'gray', text: '未知' }; |
| return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>; |
| }, |
| }, |
| { |
| title: '标签', |
| dataIndex: 'tags', |
| key: 'tags', |
| width: 150, |
| render: (tags: string) => { |
| if (!tags) return '-'; |
| return tags.split(',').map(tag => ( |
| <Tag key={tag} color="blue">{tag}</Tag> |
| )); |
| }, |
| }, |
| { |
| title: '操作', |
| key: 'action', |
| width: 250, |
| render: (text: any, record: API.Post.PostInfo) => ( |
| <Space size="small"> |
| <Button |
| type="link" |
| icon={<EyeOutlined />} |
| onClick={() => handleViewDetail(record)} |
| > |
| 查看 |
| </Button> |
| {record.status === '0' && ( |
| <> |
| <Button |
| type="link" |
| icon={<CheckOutlined />} |
| style={{ color: 'green' }} |
| onClick={() => showReasonModal(record, 'approve')} |
| > |
| 通过 |
| </Button> |
| <Button |
| type="link" |
| danger |
| icon={<CloseOutlined />} |
| onClick={() => showReasonModal(record, 'reject')} |
| > |
| 拒绝 |
| </Button> |
| </> |
| )} |
| {record.status === '1' && ( |
| <Button |
| type="link" |
| danger |
| icon={<DeleteOutlined />} |
| onClick={() => showReasonModal(record, 'takedown')} |
| > |
| 下架 |
| </Button> |
| )} |
| </Space> |
| ), |
| }, |
| ]; |
| |
| return ( |
| <div className={styles.postReviewContainer}> |
| <Card title="帖子审核管理"> |
| <Tabs activeKey={activeTab} onChange={setActiveTab}> |
| <TabPane tab="帖子发布管理" key="review"> |
| <Table |
| columns={columns} |
| dataSource={posts} |
| loading={loading} |
| rowKey="postId" |
| pagination={{ |
| pageSize: 10, |
| showTotal: (total) => `共 ${total} 条记录`, |
| }} |
| /> |
| </TabPane> |
| |
| <TabPane tab="帖子举报管理" key="report"> |
| <ReportManagement /> |
| </TabPane> |
| </Tabs> |
| </Card> |
| |
| {/* 帖子详情弹窗 */} |
| <Modal |
| title="帖子详情" |
| open={detailModalVisible} |
| onCancel={() => { |
| setDetailModalVisible(false); |
| setCurrentPost(null); |
| }} |
| footer={null} |
| width={800} |
| > |
| {currentPost && ( |
| <div className={styles.postDetail}> |
| <Title level={3}>{currentPost.title}</Title> |
| |
| <div className={styles.postMeta}> |
| <Row gutter={16}> |
| <Col span={12}> |
| <p><strong>作者:</strong>{currentPost.author}</p> |
| <p><strong>发布时间:</strong>{currentPost.publishTime}</p> |
| </Col> |
| <Col span={12}> |
| <p><strong>浏览量:</strong>{currentPost.views || 0}</p> |
| <p><strong>点赞数:</strong>{currentPost.likes || 0}</p> |
| </Col> |
| </Row> |
| </div> |
| |
| {currentPost.coverImage && ( |
| <div className={styles.postCover}> |
| <Image |
| src={currentPost.coverImage} |
| alt="封面图片" |
| style={{ maxWidth: '100%', maxHeight: '200px' }} |
| /> |
| </div> |
| )} |
| |
| <div className={styles.postTags}> |
| <strong>标签:</strong> |
| {currentPost.tags ? ( |
| currentPost.tags.split(',').map(tag => ( |
| <Tag key={tag} color="blue">{tag}</Tag> |
| )) |
| ) : ( |
| <span>无标签</span> |
| )} |
| </div> |
| |
| <div className={styles.postSummary}> |
| <strong>摘要:</strong> |
| <Paragraph>{currentPost.summary}</Paragraph> |
| </div> |
| |
| <div className={styles.postContent}> |
| <strong>内容:</strong> |
| <div dangerouslySetInnerHTML={{ __html: currentPost.content || '' }} /> |
| </div> |
| |
| <div className={styles.postActions}> |
| <Space> |
| {currentPost.status === '0' && ( |
| <> |
| <Button |
| type="primary" |
| icon={<CheckOutlined />} |
| onClick={() => { |
| showReasonModal(currentPost, 'approve'); |
| setDetailModalVisible(false); |
| }} |
| > |
| 通过审核 |
| </Button> |
| <Button |
| danger |
| icon={<CloseOutlined />} |
| onClick={() => { |
| showReasonModal(currentPost, 'reject'); |
| setDetailModalVisible(false); |
| }} |
| > |
| 拒绝审核 |
| </Button> |
| </> |
| )} |
| {currentPost.status === '1' && ( |
| <Button |
| danger |
| icon={<DeleteOutlined />} |
| onClick={() => { |
| showReasonModal(currentPost, 'takedown'); |
| setDetailModalVisible(false); |
| }} |
| > |
| 强制下架 |
| </Button> |
| )} |
| </Space> |
| </div> |
| </div> |
| )} |
| </Modal> |
| |
| {/* 审核理由弹窗 */} |
| <Modal |
| title={ |
| currentAction === 'approve' ? '审核通过' : |
| currentAction === 'reject' ? '审核拒绝' : '强制下架' |
| } |
| open={reasonModalVisible} |
| onOk={handleReasonSubmit} |
| onCancel={() => { |
| setReasonModalVisible(false); |
| setCurrentPost(null); |
| setCurrentAction(null); |
| setReason(''); |
| }} |
| okText="确定" |
| cancelText="取消" |
| > |
| <div style={{ marginBottom: 16 }}> |
| <strong>帖子:</strong>{currentPost?.title} |
| </div> |
| <div> |
| <strong> |
| {currentAction === 'approve' ? '通过理由' : |
| currentAction === 'reject' ? '拒绝理由' : '下架理由'} |
| (可选): |
| </strong> |
| <TextArea |
| value={reason} |
| onChange={(e) => setReason(e.target.value)} |
| placeholder={ |
| currentAction === 'approve' ? '请输入审核通过的理由...' : |
| currentAction === 'reject' ? '请输入审核拒绝的理由...' : '请输入强制下架的理由...' |
| } |
| rows={4} |
| style={{ marginTop: 8 }} |
| /> |
| </div> |
| </Modal> |
| </div> |
| ); |
| }; |
| |
| export default PostReview; |