创作中心模块包含首页展示、个人中心、帖子审核。

“首页展示”支持广告轮播展示、推广帖子优先展示、分页显示所有帖子、导航栏便捷标签筛选帖子、全局标题模糊搜索帖子、点击帖子“查看更多”进入帖子详情页。帖子详情页展示帖子封面图片、作者时间、详细内容(可以插入种子链接对种子进行介绍与推广)等基本信息、对帖子点赞收藏举报评论回复、查看相关推荐帖子。相关推荐会推荐当前帖子作者的其他帖子(最多推荐5篇),还会推荐具有相似标签的其他帖子,两者总共最多推荐9篇帖子。

“个人中心”包含“我的中心”和“我的收藏”。
“我的中心”中可以管理已经成功发布的帖子(编辑、删除帖子),还可以发布新帖子。发布新帖子时除了填写帖子基本信息以外,帖子标签支持下拉多项选择,用户还可以选择帖子推广项目并进行支付。设置了多种推广项目,包含广告轮播推广、帖子置顶展示、限时优先展示、分类页首条展示。系统后台执行自动定时任务,每小时对帖子的推广时效性进行检查,如超出推广时限,则取消帖子的推广显示特权。用户点击发布帖子后帖子处于待审核状态,需要管理员审核通过才能正常发布在首页展示页面。编辑帖子时用户可以追加帖子推广,但如果帖子处于推广状态,则禁止修改推广项目。
“我的收藏”中可以便捷查看所有已收藏的帖子。

“帖子审核”包含“帖子发布管理”和“帖子举报管理”。“帖子审核”板块具有权限管理,只有管理员界面能够进入。
“帖子发布管理”对所有待审核帖子进行处理,支持预览待审核帖子详细内容,批准通过和拒绝通过选项。
“帖子举报管理”对所有用户的举报请求进行人工审核,如果举报内容属实,则将帖子下架处理,如果举报内容不属实,驳回举报请求。所有举报请求的处理结果均留存显示,方便后续再次审查。

Change-Id: If822351183e9d55a5a56ff5cf1e13b313fdbe231
diff --git a/src/pages/PostCenter/PostCard.module.css b/src/pages/PostCenter/PostCard.module.css
new file mode 100644
index 0000000..3f0623f
--- /dev/null
+++ b/src/pages/PostCenter/PostCard.module.css
@@ -0,0 +1,192 @@
+.postCardWrapper {
+  width: 100%;
+  height: 100%;
+}
+
+.postCard {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  transition: all 0.3s ease;
+  background: white;
+}
+
+.postCard:hover {
+  transform: translateY(-4px);
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
+}
+
+.coverContainer {
+  width: 100%;
+  height: 200px;
+  overflow: hidden;
+  position: relative;
+  background: #f5f5f5;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.coverImage {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+  object-position: center;
+  transition: transform 0.3s ease;
+  background: #f5f5f5;
+}
+
+.postCard:hover .coverImage {
+  transform: scale(1.02);
+}
+
+.promotionBadge {
+  position: absolute;
+  top: 8px;
+  right: 8px;
+  background: linear-gradient(45deg, #ff6b6b, #ffa500);
+  color: white;
+  padding: 4px 8px;
+  border-radius: 12px;
+  font-size: 11px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  z-index: 10;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+.cardContent {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+.postTitle {
+  font-size: 16px;
+  font-weight: 600;
+  margin: 0 0 12px 0;
+  line-height: 1.4;
+  color: #262626;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  height: 44px;
+}
+
+.postMeta {
+  display: flex;
+  align-items: center;
+  font-size: 12px;
+  color: #8c8c8c;
+  margin-bottom: 12px;
+  height: 20px;
+}
+
+.authorName {
+  font-weight: 500;
+  color: #595959;
+}
+
+.publishTime {
+  color: #8c8c8c;
+}
+
+.tagsContainer {
+  margin-bottom: 12px;
+  height: 24px;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 4px;
+  overflow: hidden;
+}
+
+.tag {
+  font-size: 11px;
+  padding: 2px 6px;
+  margin: 0;
+  border-radius: 4px;
+}
+
+.postSummary {
+  flex: 1;
+  font-size: 13px;
+  color: #595959;
+  line-height: 1.5;
+  margin-bottom: 16px;
+  display: -webkit-box;
+  -webkit-line-clamp: 3;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  height: 60px;
+}
+
+.postFooter {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: auto;
+  padding-top: 12px;
+  border-top: 1px solid #f0f0f0;
+  height: 32px;
+}
+
+.stats {
+  display: flex;
+  gap: 16px;
+}
+
+.statItem {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  font-size: 12px;
+  color: #8c8c8c;
+}
+
+.statItem .anticon {
+  font-size: 12px;
+}
+
+.readMoreBtn {
+  padding: 0;
+  font-size: 12px;
+  color: #1890ff;
+  font-weight: 500;
+}
+
+.readMoreBtn:hover {
+  color: #40a9ff;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .coverContainer {
+    height: 160px;
+  }
+  
+  .postTitle {
+    font-size: 14px;
+    height: 40px;
+  }
+  
+  .postSummary {
+    font-size: 12px;
+    -webkit-line-clamp: 2;
+    height: 36px;
+  }
+  
+  .stats {
+    gap: 12px;
+  }
+  
+  .statItem {
+    font-size: 11px;
+  }
+} 
\ No newline at end of file
diff --git a/src/pages/PostCenter/PostCard.tsx b/src/pages/PostCenter/PostCard.tsx
new file mode 100644
index 0000000..947ecc3
--- /dev/null
+++ b/src/pages/PostCenter/PostCard.tsx
@@ -0,0 +1,107 @@
+import React from 'react';
+import { Card, Tag, Button, Avatar, Badge } from 'antd';
+import { EyeOutlined, CommentOutlined, UserOutlined, ClockCircleOutlined, HeartOutlined, CrownOutlined } from '@ant-design/icons';
+import { history } from 'umi';
+import styles from './PostCard.module.css';
+import { Post } from '../PostCenter/types';
+
+interface PostCardProps {
+  post: Post;
+}
+
+const PostCard: React.FC<PostCardProps> = ({ post }) => {
+  const {
+    id,
+    title,
+    author,
+    publishTime,
+    tags,
+    views,
+    comments,
+    favorites,
+    likes,
+    coverImage,
+    summary,
+    promotionPlanId,
+    isPromoted
+  } = post;
+
+  const goToDetail = () => {
+    history.push(`/post-detail/${id}`);
+  };
+
+  return (
+    <div className={styles.postCardWrapper}>
+      <Card
+        hoverable
+        cover={
+          <div className={styles.coverContainer}>
+            {isPromoted && (
+              <div className={styles.promotionBadge}>
+                <CrownOutlined />
+                <span>推广</span>
+              </div>
+            )}
+            <img 
+              alt={title} 
+              src={coverImage} 
+              className={styles.coverImage}
+              onError={(e) => {
+                e.currentTarget.src = '/images/404.png';
+              }}
+            />
+          </div>
+        }
+        className={styles.postCard}
+        bodyStyle={{ padding: '16px', height: '240px', display: 'flex', flexDirection: 'column' }}
+      >
+        <div className={styles.cardContent}>
+          <h3 className={styles.postTitle} title={title}>{title}</h3>
+          
+          <div className={styles.postMeta}>
+            <Avatar size="small" style={{ marginRight: 6 }} icon={<UserOutlined />}>
+              {author && author[0]}
+            </Avatar>
+            <span className={styles.authorName}>{author}</span>
+            <ClockCircleOutlined style={{ marginLeft: 12, marginRight: 4 }} />
+            <span className={styles.publishTime}>{publishTime}</span>
+          </div>
+          
+          <div className={styles.tagsContainer}>
+            {(Array.isArray(tags) ? tags : []).slice(0, 3).map(tag => (
+              <Tag color="blue" key={tag} className={styles.tag}>{tag}</Tag>
+            ))}
+            {tags && tags.length > 3 && (
+              <Tag color="default" className={styles.tag}>+{tags.length - 3}</Tag>
+            )}
+          </div>
+          
+          <div className={styles.postSummary} title={summary}>{summary}</div>
+          
+          <div className={styles.postFooter}>
+            <div className={styles.stats}>
+              <span className={styles.statItem}>
+                <EyeOutlined /> {views || 0}
+              </span>
+              <span className={styles.statItem}>
+                <CommentOutlined /> {comments || 0}
+              </span>
+              <span className={styles.statItem}>
+                <HeartOutlined /> {favorites || 0}
+              </span>
+            </div>
+            <Button 
+              type="link" 
+              className={styles.readMoreBtn}
+              onClick={goToDetail}
+            >
+              查看更多 »
+            </Button>
+          </div>
+        </div>
+      </Card>
+    </div>
+  );
+};
+
+export default PostCard; 
\ No newline at end of file
diff --git a/src/pages/PostCenter/PostDetail.module.css b/src/pages/PostCenter/PostDetail.module.css
new file mode 100644
index 0000000..da072d1
--- /dev/null
+++ b/src/pages/PostCenter/PostDetail.module.css
@@ -0,0 +1,233 @@
+.postDetailContainer {
+  max-width: 1200px;
+  margin: 0 auto;
+  background: #f5f5f5;
+  min-height: 100vh;
+}
+
+.postCoverSection {
+  margin-bottom: 24px;
+  border-radius: 0 0 16px 16px;
+  overflow: hidden;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.coverImageContainer {
+  position: relative;
+  width: 100%;
+  height: 400px;
+  overflow: hidden;
+  background: #f0f0f0;
+}
+
+.coverImage {
+  width: 100% !important;
+  height: 100% !important;
+  object-fit: cover !important;
+  transition: transform 0.3s ease;
+}
+
+.coverImage:hover {
+  transform: scale(1.02);
+}
+
+.coverOverlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  pointer-events: none;
+}
+
+.coverGradient {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 100px;
+  background: linear-gradient(transparent, rgba(0, 0, 0, 0.3));
+}
+
+.previewMask {
+  background: rgba(0, 0, 0, 0.5);
+  color: white;
+  padding: 8px 16px;
+  border-radius: 4px;
+  font-size: 14px;
+}
+
+.postDetailHeader {
+  margin: 0 24px 24px;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.titleContainer {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  margin-bottom: 16px;
+}
+
+.titleContainer h2 {
+  margin: 0;
+  flex: 1;
+}
+
+.promotionBadge {
+  background: linear-gradient(45deg, #ff6b6b, #ffa500);
+  color: white;
+  padding: 6px 12px;
+  border-radius: 16px;
+  font-size: 12px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+  white-space: nowrap;
+}
+
+.postMeta {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 24px;
+  margin: 16px 0;
+  padding: 16px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.postAuthor,
+.postTime,
+.postViews,
+.postTags {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.postActions {
+  display: flex;
+  gap: 12px;
+  flex-wrap: wrap;
+  margin-top: 16px;
+}
+
+.postContent {
+  margin: 0 24px 24px;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.postContent img {
+  max-width: 100%;
+  height: auto;
+}
+
+.commentSection {
+  margin: 0 24px 24px;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.commentInput {
+  margin-bottom: 24px;
+}
+
+.commentActions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+  margin-top: 12px;
+}
+
+.commentList {
+  margin-top: 24px;
+}
+
+.replyList {
+  margin-top: 16px;
+  padding-left: 24px;
+  border-left: 2px solid #f0f0f0;
+}
+
+.relatedPosts {
+  margin: 0 24px 24px;
+  padding: 24px;
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.recommendHeader {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 24px;
+}
+
+.recommendHeader h4 {
+  margin: 0;
+}
+
+.postDetailLoading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 400px;
+  color: #666;
+}
+
+.postDetailError {
+  text-align: center;
+  padding: 60px 24px;
+  color: #999;
+  font-size: 16px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .postDetailContainer {
+    margin: 0;
+  }
+  
+  .coverImageContainer {
+    height: 250px;
+  }
+  
+  .postDetailHeader,
+  .postContent,
+  .commentSection,
+  .relatedPosts {
+    margin: 0 12px 16px;
+    border-radius: 8px;
+  }
+  
+  .postMeta {
+    flex-direction: column;
+    gap: 12px;
+  }
+  
+  .postActions {
+    flex-direction: column;
+  }
+  
+  .postActions button {
+    width: 100%;
+  }
+}
+
+@media (max-width: 480px) {
+  .coverImageContainer {
+    height: 200px;
+  }
+  
+  .postDetailHeader,
+  .postContent,
+  .commentSection,
+  .relatedPosts {
+    margin: 0 8px 12px;
+  }
+} 
\ No newline at end of file
diff --git a/src/pages/PostCenter/PostDetail.tsx b/src/pages/PostCenter/PostDetail.tsx
new file mode 100644
index 0000000..c6aedcc
--- /dev/null
+++ b/src/pages/PostCenter/PostDetail.tsx
@@ -0,0 +1,547 @@
+import React, { useState, useEffect } from 'react';
+import { useParams, Link } from 'umi';
+import { Card, Avatar, Tag, Row, Col, Divider, List, Input, Button, Typography, message, Spin, Image, Modal, Badge, Pagination } from 'antd';
+import { Comment } from '@ant-design/compatible';
+import { 
+  UserOutlined, 
+  ClockCircleOutlined, 
+  EyeOutlined, 
+  TagOutlined,
+  LikeOutlined,
+  DislikeOutlined,
+  ShareAltOutlined,
+  HeartOutlined,
+  HeartFilled,
+  ExclamationCircleOutlined,
+  CrownOutlined
+} from '@ant-design/icons';
+import { getPostDetail, addComment, toggleFavorite, toggleLike, toggleCommentLike, reportPost } from '@/services/post';
+import PostCard from '../PostCenter/PostCard';
+import styles from './PostDetail.module.css';
+import { Post, CommentType } from '../PostCenter/types';
+
+const { TextArea } = Input;
+const { Title, Paragraph, Text } = Typography;
+
+const PostDetail: React.FC = () => {
+  const { id } = useParams<{ id: string }>();
+  const [post, setPost] = useState<Post | null>(null);
+  const [recommendedPosts, setRecommendedPosts] = useState<Post[]>([]);
+  const [currentRecommendPage, setCurrentRecommendPage] = useState<number>(1);
+  const [comments, setComments] = useState<CommentType[]>([]);
+  const [replyTo, setReplyTo] = useState<number | null>(null);
+  const [commentText, setCommentText] = useState<string>('');
+  const [loading, setLoading] = useState<boolean>(true);
+  const [favorited, setFavorited] = useState<boolean>(false);
+  const [liked, setLiked] = useState<boolean>(false);
+  const [submittingComment, setSubmittingComment] = useState<boolean>(false);
+  const [commentLikes, setCommentLikes] = useState<Record<number, boolean>>({});
+  const [reportModalVisible, setReportModalVisible] = useState<boolean>(false);
+  const [reportReason, setReportReason] = useState<string>('');
+  const [submittingReport, setSubmittingReport] = useState<boolean>(false);
+
+  const recommendPageSize = 3; // 每页显示3个推荐帖子
+
+  useEffect(() => {
+    if (id) {
+      fetchPostDetail(Number(id));
+    }
+  }, [id]);
+
+  const fetchPostDetail = async (postId: number) => {
+    try {
+      setLoading(true);
+      const response = await getPostDetail(postId);
+      
+      if (response.code === 200 && response.data) {
+        const { post: postData, tags, comments: commentsData, recommendedPosts, favorited: isFavorited } = response.data;
+        
+        // 转换帖子数据格式
+        const formattedPost: Post = {
+          ...postData,
+          id: postData.postId,
+          title: postData.title || '无标题',
+          author: postData.author || '未知作者',
+          publishTime: postData.publishTime || postData.createTime || '',
+          tags: postData.tags ? postData.tags.split(',') : [],
+          views: postData.views || 0,
+          comments: postData.comments || 0,
+          favorites: postData.favorites || 0,
+          likes: postData.likes || 0,
+          coverImage: postData.coverImage || '/images/404.png',
+          summary: postData.summary || '暂无摘要',
+          isPromoted: postData.promotionPlanId != null && postData.promotionPlanId > 0,
+        };
+        setPost(formattedPost);
+        setFavorited(isFavorited);
+
+        // 转换评论数据格式
+        const formattedComments = commentsData.map((commentItem: any) => {
+          const comment = commentItem.comment;
+          const replies = commentItem.replies || [];
+          
+          return {
+            ...comment,
+            id: comment.commentId,
+            author: comment.userName || '匿名用户',
+            avatar: comment.userAvatar || '/images/404.png',
+            datetime: comment.createTime,
+            likes: comment.likes || 0,
+            replies: replies.map((reply: any) => ({
+              ...reply,
+              id: reply.commentId,
+              author: reply.userName || '匿名用户',
+              avatar: reply.userAvatar || '/images/404.png',
+              datetime: reply.createTime,
+              likes: reply.likes || 0,
+              replies: []
+            }))
+          };
+        });
+        setComments(formattedComments);
+
+        // 转换推荐帖子数据格式
+        const formatPosts = (posts: any[]) => posts.map((p: any) => ({
+          ...p,
+          id: p.postId,
+          title: p.title || '无标题',
+          author: p.author || '未知作者',
+          publishTime: p.publishTime || p.createTime || '',
+          tags: p.tags ? p.tags.split(',') : [],
+          views: p.views || 0,
+          comments: p.comments || 0,
+          favorites: p.favorites || 0,
+          likes: p.likes || 0,
+          coverImage: p.coverImage || '/images/404.png',
+          summary: p.summary || '暂无摘要',
+          isPromoted: p.promotionPlanId != null && p.promotionPlanId > 0, // 添加推广标识
+        }));
+
+        // 设置推荐帖子
+        const formattedRecommendedPosts = formatPosts(recommendedPosts || []);
+        setRecommendedPosts(formattedRecommendedPosts);
+      } else {
+        message.error(response.msg || '获取帖子详情失败');
+      }
+    } catch (error) {
+      console.error('获取帖子详情失败:', error);
+      message.error('获取帖子详情失败,请稍后重试');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleCommentSubmit = async () => {
+    if (!commentText.trim()) {
+      message.warning('评论内容不能为空');
+      return;
+    }
+
+    if (!post) return;
+
+    try {
+      setSubmittingComment(true);
+      const response = await addComment({
+        postId: Number(post.id),
+        content: commentText,
+        parentId: replyTo || 0,
+      });
+
+      if (response.code === 200) {
+        message.success('评论发表成功');
+        setCommentText('');
+        setReplyTo(null);
+        // 重新获取帖子详情以更新评论列表
+        fetchPostDetail(Number(post.id));
+      } else {
+        message.error(response.msg || '评论发表失败');
+      }
+    } catch (error) {
+      console.error('评论发表失败:', error);
+      message.error('评论发表失败,请稍后重试');
+    } finally {
+      setSubmittingComment(false);
+    }
+  };
+
+  const handleFavoriteToggle = async () => {
+    if (!post) return;
+
+    try {
+      const response = await toggleFavorite(Number(post.id), !favorited);
+      
+      if (response.code === 200) {
+        setFavorited(!favorited);
+        message.success(favorited ? '取消收藏成功' : '收藏成功');
+        // 更新帖子收藏数
+        setPost(prev => prev ? {
+          ...prev,
+          favorites: (prev.favorites || 0) + (favorited ? -1 : 1)
+        } : null);
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      console.error('收藏操作失败:', error);
+      message.error('操作失败,请稍后重试');
+    }
+  };
+
+  const handleLikeToggle = async () => {
+    if (!post) return;
+
+    try {
+      const response = await toggleLike(Number(post.id), !liked);
+      
+      if (response.code === 200) {
+        setLiked(!liked);
+        message.success(liked ? '取消点赞成功' : '点赞成功');
+        // 更新帖子点赞数
+        setPost(prev => prev ? {
+          ...prev,
+          likes: (prev.likes || 0) + (liked ? -1 : 1)
+        } : null);
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      console.error('点赞操作失败:', error);
+      message.error('操作失败,请稍后重试');
+    }
+  };
+
+  const handleReply = (commentId: number) => {
+    setReplyTo(commentId);
+  };
+
+  const cancelReply = () => {
+    setReplyTo(null);
+  };
+
+  const handleCommentLike = async (commentId: number) => {
+    try {
+      const isLiked = commentLikes[commentId] || false;
+      const response = await toggleCommentLike(commentId, !isLiked);
+      
+      if (response.code === 200) {
+        setCommentLikes(prev => ({
+          ...prev,
+          [commentId]: !isLiked
+        }));
+        
+        // 更新评论点赞数
+        setComments(prev => prev.map(comment => {
+          if (comment.id === commentId) {
+            return {
+              ...comment,
+              likes: (comment.likes || 0) + (isLiked ? -1 : 1)
+            };
+          }
+          // 检查回复
+          if (comment.replies) {
+            const updatedReplies = comment.replies.map(reply => {
+              if (reply.id === commentId) {
+                return {
+                  ...reply,
+                  likes: (reply.likes || 0) + (isLiked ? -1 : 1)
+                };
+              }
+              return reply;
+            });
+            return { ...comment, replies: updatedReplies };
+          }
+          return comment;
+        }));
+        
+        message.success(isLiked ? '取消点赞成功' : '点赞成功');
+      } else {
+        message.error(response.msg || '操作失败');
+      }
+    } catch (error) {
+      console.error('评论点赞操作失败:', error);
+      message.error('操作失败,请稍后重试');
+    }
+  };
+
+  const handleReport = async () => {
+    if (!post) return;
+    
+    if (!reportReason.trim()) {
+      message.warning('请填写举报理由');
+      return;
+    }
+
+    try {
+      setSubmittingReport(true);
+      const response = await reportPost(Number(post.id), reportReason);
+      
+      if (response.code === 200) {
+        message.success('举报提交成功,我们会尽快处理');
+        setReportModalVisible(false);
+        setReportReason('');
+      } else {
+        message.error(response.msg || '举报提交失败');
+      }
+    } catch (error) {
+      console.error('举报提交失败:', error);
+      message.error('举报提交失败,请稍后重试');
+    } finally {
+      setSubmittingReport(false);
+    }
+  };
+
+  if (loading) {
+    return (
+      <div className={styles.postDetailLoading}>
+        <Spin size="large" />
+        <div style={{ marginTop: 16 }}>加载中...</div>
+      </div>
+    );
+  }
+
+  if (!post) {
+    return <div className={styles.postDetailError}>帖子不存在或已被删除</div>;
+  }
+
+  return (
+    <div className={styles.postDetailContainer}>
+      {/* 帖子封面图片 */}
+      {post.coverImage && (
+        <div className={styles.postCoverSection}>
+          <div className={styles.coverImageContainer}>
+            <Image
+              src={post.coverImage}
+              alt={post.title}
+              className={styles.coverImage}
+              preview={{
+                mask: <div className={styles.previewMask}>点击预览</div>
+              }}
+              onError={(e) => {
+                e.currentTarget.src = '/images/404.png';
+              }}
+            />
+            <div className={styles.coverOverlay}>
+              <div className={styles.coverGradient}></div>
+            </div>
+          </div>
+        </div>
+      )}
+
+      {/* 帖子头部信息 */}
+      <Card className={styles.postDetailHeader}>
+        <div className={styles.titleContainer}>
+        <Title level={2}>{post.title}</Title>
+          {post.isPromoted && (
+            <div className={styles.promotionBadge}>
+              <CrownOutlined />
+              <span>推广</span>
+            </div>
+          )}
+        </div>
+        
+        <div className={styles.postMeta}>
+          <div className={styles.postAuthor}>
+            <Avatar size="small" icon={<UserOutlined />} />
+            <Text strong style={{ marginLeft: 8 }}>{post.author}</Text>
+          </div>
+          
+          <div className={styles.postTime}>
+            <ClockCircleOutlined />
+            <Text type="secondary" style={{ marginLeft: 8 }}>{post.publishTime}</Text>
+          </div>
+          
+          <div className={styles.postViews}>
+            <EyeOutlined />
+            <Text type="secondary" style={{ marginLeft: 8 }}>{post.views} 查看</Text>
+          </div>
+          
+          <div className={styles.postTags}>
+            <TagOutlined />
+            <span style={{ marginLeft: 8 }}>
+              {post.tags.map(tag => (
+                <Tag key={tag} color="blue">{tag}</Tag>
+              ))}
+            </span>
+          </div>
+        </div>
+
+        {/* 操作按钮 */}
+        <div className={styles.postActions}>
+          <Button 
+            type={favorited ? "primary" : "default"}
+            icon={favorited ? <HeartFilled /> : <HeartOutlined />}
+            onClick={handleFavoriteToggle}
+          >
+            {favorited ? '已收藏' : '收藏'} ({post.favorites || 0})
+          </Button>
+          <Button 
+            type={liked ? "primary" : "default"}
+            icon={<LikeOutlined />}
+            onClick={handleLikeToggle}
+          >
+            {liked ? '已点赞' : '点赞'} ({post.likes || 0})
+          </Button>
+          {/* <Button icon={<ShareAltOutlined />}>
+            分享
+          </Button> */}
+          <Button 
+            icon={<ExclamationCircleOutlined />}
+            onClick={() => setReportModalVisible(true)}
+          >
+            举报
+          </Button>
+        </div>
+      </Card>
+
+      {/* 帖子内容 */}
+      <Card className={styles.postContent}>
+        <div dangerouslySetInnerHTML={{ __html: post.content || post.summary || '' }} />
+      </Card>
+
+      {/* 评论区 */}
+      <Card className={styles.commentSection}>
+        <Title level={4}>评论 ({comments.length})</Title>
+        
+        <div className={styles.commentInput}>
+          <TextArea
+            value={commentText}
+            onChange={e => setCommentText(e.target.value)}
+            placeholder={replyTo ? '回复评论...' : '写下你的评论...'}
+            rows={4}
+          />
+          <div className={styles.commentActions}>
+            {replyTo && (
+              <Button onClick={cancelReply}>取消回复</Button>
+            )}
+            <Button 
+              type="primary" 
+              onClick={handleCommentSubmit}
+              loading={submittingComment}
+            >
+              发表{replyTo ? '回复' : '评论'}
+            </Button>
+          </div>
+        </div>
+
+        <List
+          className={styles.commentList}
+          itemLayout="horizontal"
+          dataSource={comments}
+          renderItem={comment => (
+            <li>
+              <Comment
+                author={comment.author}
+                avatar={comment.avatar}
+                content={comment.content}
+                datetime={comment.datetime}
+                actions={[
+                  <span key="like">
+                    <Button 
+                      type={commentLikes[comment.id] ? "primary" : "text"}
+                      size="small" 
+                      icon={<LikeOutlined />}
+                      onClick={() => handleCommentLike(comment.id)}
+                    >
+                      {comment.likes || 0}
+                    </Button>
+                  </span>,
+                  <span key="reply" onClick={() => handleReply(comment.id)}>回复</span>
+                ]}
+              >
+                {comment.replies && comment.replies.length > 0 && (
+                  <List
+                    className={styles.replyList}
+                    itemLayout="horizontal"
+                    dataSource={comment.replies}
+                    renderItem={reply => (
+                      <li>
+                        <Comment
+                          author={reply.author}
+                          avatar={reply.avatar}
+                          content={reply.content}
+                          datetime={reply.datetime}
+                          actions={[
+                            <span key="like">
+                              <Button 
+                                type={commentLikes[reply.id] ? "primary" : "text"}
+                                size="small" 
+                                icon={<LikeOutlined />}
+                                onClick={() => handleCommentLike(reply.id)}
+                              >
+                                {reply.likes || 0}
+                              </Button>
+                            </span>
+                          ]}
+                        />
+                      </li>
+                    )}
+                  />
+                )}
+              </Comment>
+            </li>
+          )}
+        />
+      </Card>
+
+      {/* 举报弹窗 */}
+      <Modal
+        title="举报帖子"
+        open={reportModalVisible}
+        onOk={handleReport}
+        onCancel={() => {
+          setReportModalVisible(false);
+          setReportReason('');
+        }}
+        confirmLoading={submittingReport}
+        okText="提交举报"
+        cancelText="取消"
+      >
+        <div style={{ marginBottom: 16 }}>
+          <strong>帖子:</strong>{post?.title}
+        </div>
+        <div>
+          <strong>举报理由:</strong>
+          <TextArea
+            value={reportReason}
+            onChange={(e) => setReportReason(e.target.value)}
+            placeholder="请详细描述举报理由..."
+            rows={4}
+            style={{ marginTop: 8 }}
+          />
+        </div>
+      </Modal>
+
+      {/* 相关推荐 */}
+      {recommendedPosts.length > 0 && (
+        <div className={styles.relatedPosts}>
+          <div className={styles.recommendHeader}>
+          <Title level={4}>相关推荐</Title>
+            {recommendedPosts.length > recommendPageSize && (
+              <Pagination
+                current={currentRecommendPage}
+                total={recommendedPosts.length}
+                pageSize={recommendPageSize}
+                onChange={(page) => setCurrentRecommendPage(page)}
+                showSizeChanger={false}
+                showQuickJumper={false}
+                showTotal={(total, range) => `${range[0]}-${range[1]} / ${total}`}
+                size="small"
+              />
+            )}
+          </div>
+          <Row gutter={[24, 24]}>
+            {recommendedPosts
+              .slice((currentRecommendPage - 1) * recommendPageSize, currentRecommendPage * recommendPageSize)
+              .map(post => (
+              <Col xs={24} sm={12} md={8} key={post.id}>
+                <PostCard post={post} />
+              </Col>
+            ))}
+          </Row>
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default PostDetail; 
\ No newline at end of file
diff --git a/src/pages/PostCenter/index.module.css b/src/pages/PostCenter/index.module.css
new file mode 100644
index 0000000..2b262e2
--- /dev/null
+++ b/src/pages/PostCenter/index.module.css
@@ -0,0 +1,189 @@
+.postCenterContainer {
+  min-height: 100vh;
+  background-color: #f5f5f5;
+}
+
+.headerNav {
+  background: white;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  padding: 0 24px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 64px;
+  position: sticky;
+  top: 0;
+  z-index: 100;
+}
+
+.categoryMenu {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.categoryButton {
+  border: none;
+  box-shadow: none;
+  font-weight: 500;
+  padding: 8px 16px;
+  border-radius: 6px;
+  transition: all 0.3s ease;
+}
+
+.categoryButton:hover {
+  background-color: #f0f0f0;
+}
+
+.searchContainer {
+  flex: 1;
+  display: flex;
+  justify-content: center;
+  max-width: 400px;
+  margin: 0 24px;
+}
+
+.userCenter {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.carouselContainer {
+  margin: 24px;
+  border-radius: 12px;
+  overflow: hidden;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.carouselSlide {
+  position: relative;
+  display: flex !important;
+  align-items: center;
+  justify-content: center;
+  min-height: 300px;
+}
+
+.carouselOverlay {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
+  color: white;
+  padding: 40px 40px 24px;
+  text-align: left;
+}
+
+.carouselTitle {
+  font-size: 28px;
+  font-weight: bold;
+  margin: 0 0 12px 0;
+  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
+}
+
+.carouselSummary {
+  font-size: 16px;
+  margin: 0 0 16px 0;
+  opacity: 0.9;
+  line-height: 1.5;
+}
+
+.carouselMeta {
+  display: flex;
+  gap: 24px;
+  font-size: 14px;
+  opacity: 0.8;
+}
+
+.postsSection {
+  padding: 24px;
+}
+
+.categoryTitle {
+  margin-bottom: 24px;
+  text-align: center;
+}
+
+.categoryTitle h2 {
+  font-size: 24px;
+  margin: 0 0 8px 0;
+  color: #1890ff;
+}
+
+.categoryTitle p {
+  margin: 0;
+  color: #666;
+  font-size: 14px;
+}
+
+.postsRow {
+  margin-bottom: 32px;
+}
+
+.postCol {
+  display: flex;
+  height: 480px; /* 固定卡片高度 */
+}
+
+.postCol > * {
+  width: 100%;
+}
+
+.emptyState {
+  text-align: center;
+  padding: 60px 0;
+  color: #999;
+  font-size: 16px;
+}
+
+.paginationContainer {
+  display: flex;
+  justify-content: center;
+  margin-top: 32px;
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+  .headerNav {
+    flex-direction: column;
+    height: auto;
+    padding: 12px;
+    gap: 12px;
+  }
+  
+  .categoryMenu {
+    flex-wrap: wrap;
+    justify-content: center;
+  }
+  
+  .searchContainer {
+    margin: 0;
+    max-width: 100%;
+  }
+  
+  .carouselContainer {
+    margin: 12px;
+  }
+  
+  .carouselOverlay {
+    padding: 20px;
+  }
+  
+  .carouselTitle {
+    font-size: 20px;
+  }
+  
+  .carouselSummary {
+    font-size: 14px;
+  }
+  
+  .carouselMeta {
+    flex-direction: column;
+    gap: 8px;
+  }
+  
+  .postsSection {
+    padding: 12px;
+  }
+} 
\ No newline at end of file
diff --git a/src/pages/PostCenter/index.tsx b/src/pages/PostCenter/index.tsx
new file mode 100644
index 0000000..b81efb0
--- /dev/null
+++ b/src/pages/PostCenter/index.tsx
@@ -0,0 +1,301 @@
+import React, { useEffect, useState } from 'react';
+import { Row, Col, Pagination, Input, Carousel, Menu, Card, message, Button } from 'antd';
+import { SearchOutlined, AuditOutlined } from '@ant-design/icons';
+import { getPostList, getPromotionPosts } from '@/services/post';
+import PostCard from '../PostCenter/PostCard';
+import styles from './index.module.css';
+import { Post } from '../PostCenter/types';
+import { useNavigate } from 'react-router-dom';
+import { useModel } from 'umi';
+
+const { Search } = Input;
+
+const PostCenter: React.FC = () => {
+  const [posts, setPosts] = useState<Post[]>([]);
+  const [promotionPosts, setPromotionPosts] = useState<Post[]>([]);
+  const [total, setTotal] = useState<number>(0);
+  const [page, setPage] = useState<number>(1);
+  const [loading, setLoading] = useState<boolean>(false);
+  const [selectedCategory, setSelectedCategory] = useState<string>('all');
+  const [searchKeyword, setSearchKeyword] = useState<string>('');
+  const pageSize = 12;
+  const navigate = useNavigate();
+  const { initialState } = useModel('@@initialState');
+
+  // 检查是否为管理员 - 用户名包含admin
+  const isAdmin = initialState?.currentUser?.userName?.toLowerCase().includes('admin') || false;
+
+  const fetchPosts = async (current: number = 1, category?: string, searchTitle?: string) => {
+    try {
+      setLoading(true);
+      const params: any = {
+        pageNum: current,
+        pageSize: pageSize,
+        status: '1', // 只查询正常状态的帖子
+      };
+
+      // 根据分类筛选
+      if (category && category !== 'all') {
+        params.tags = category;
+      }
+
+      // 搜索关键词
+      if (searchTitle) {
+        params.title = searchTitle;
+      }
+
+      const response = await getPostList(params);
+      
+      if (response.code === 200) {
+        // 确保返回的数据符合 Post 类型
+        const formattedPosts = (response.rows || []).map((post: API.Post.PostInfo) => ({
+          ...post,
+          id: post.postId, // 确保id字段映射正确
+          tags: post.tags ? post.tags.split(',') : [],
+          views: post.views || 0,
+          comments: post.comments || 0,
+          favorites: post.favorites || 0,
+          likes: post.likes || 0,
+          coverImage: post.coverImage || '/images/404.png', // 使用本地默认图片
+          isPromoted: post.promotionPlanId != null && post.promotionPlanId > 0, // 添加推广标识
+        }));
+        setPosts(formattedPosts);
+        setTotal(response.total || 0);
+      } else {
+        message.error(response.msg || '获取帖子列表失败');
+        setPosts([]);
+        setTotal(0);
+      }
+    } catch (error) {
+      console.error('获取帖子失败:', error);
+      message.error('获取帖子列表失败,请稍后重试');
+      setPosts([]);
+      setTotal(0);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const fetchPromotionPosts = async () => {
+    try {
+      const response = await getPromotionPosts();
+      if (response.code === 200) {
+        const formattedPosts = (response.data || []).map((post: API.Post.PostInfo) => ({
+          ...post,
+          id: post.postId,
+          tags: post.tags ? post.tags.split(',') : [],
+          views: post.views || 0,
+          comments: post.comments || 0,
+          favorites: post.favorites || 0,
+          likes: post.likes || 0,
+          coverImage: post.coverImage || '/images/404.png',
+          isPromoted: post.promotionPlanId != null && post.promotionPlanId > 0, // 添加推广标识
+        }));
+        setPromotionPosts(formattedPosts);
+      }
+    } catch (error) {
+      console.error('获取推广帖子失败:', error);
+    }
+  };
+
+  useEffect(() => {
+    fetchPosts(page, selectedCategory, searchKeyword);
+    fetchPromotionPosts();
+  }, [page, selectedCategory]);
+
+  const handleSearch = (value: string) => {
+    console.log('搜索:', value);
+    setSearchKeyword(value);
+    setPage(1); // 重置页码
+    fetchPosts(1, selectedCategory, value);
+  };
+
+  const handlePageChange = (newPage: number) => {
+    setPage(newPage);
+    fetchPosts(newPage, selectedCategory, searchKeyword);
+  };
+
+  const handleCategoryChange = (category: string) => {
+    setSelectedCategory(category);
+    setPage(1); // 重置页码
+    setSearchKeyword(''); // 清空搜索
+    fetchPosts(1, category, '');
+  };
+
+  return (
+    <div className={styles.postCenterContainer}>
+      {/* 顶部导航 */}
+      <div className={styles.headerNav}>
+        <div className={styles.categoryMenu}>
+          <Button 
+            type={selectedCategory === 'all' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('all')}
+            className={styles.categoryButton}
+          >
+            首页
+          </Button>
+          <Button 
+            type={selectedCategory === '日剧' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('日剧')}
+            className={styles.categoryButton}
+          >
+            日剧
+          </Button>
+          <Button 
+            type={selectedCategory === '电影' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('电影')}
+            className={styles.categoryButton}
+          >
+            电影
+          </Button>
+          <Button 
+            type={selectedCategory === '音乐' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('音乐')}
+            className={styles.categoryButton}
+          >
+            音乐
+          </Button>
+          <Button 
+            type={selectedCategory === '合集' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('合集')}
+            className={styles.categoryButton}
+          >
+            合集
+          </Button>
+          <Button 
+            type={selectedCategory === '动漫' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('动漫')}
+            className={styles.categoryButton}
+          >
+            动漫
+          </Button>
+          <Button 
+            type={selectedCategory === '游戏' ? 'primary' : 'text'}
+            onClick={() => handleCategoryChange('游戏')}
+            className={styles.categoryButton}
+          >
+            游戏
+          </Button>
+        </div>
+        
+        <div className={styles.searchContainer}>
+          <Search
+            placeholder="搜索帖子..."
+            onSearch={handleSearch}
+            style={{ width: 300 }}
+            enterButton={<SearchOutlined />}
+            value={searchKeyword}
+            onChange={(e) => setSearchKeyword(e.target.value)}
+          />
+        </div>
+        
+        <div className={styles.userCenter}>
+          {isAdmin && (
+            <Button 
+              icon={<AuditOutlined />}
+              onClick={() => navigate('/post-review')}
+              style={{ marginRight: 16 }}
+            >
+              帖子审核
+            </Button>
+          )}
+          <Button 
+            type="primary" 
+            onClick={() => navigate('/user-center')}
+          >
+            个人中心
+          </Button>
+        </div>
+      </div>
+
+      {/* 轮播推荐图 */}
+      <div className={styles.carouselContainer}>
+        <Carousel autoplay>
+          {promotionPosts.length > 0 ? (
+            promotionPosts.map((post) => (
+              <div key={post.id} onClick={() => navigate(`/post-detail/${post.id}`)}>
+                <div 
+                  className={styles.carouselSlide}
+                  style={{
+                    backgroundImage: `linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url(${post.coverImage})`,
+                    backgroundSize: 'cover',
+                    backgroundPosition: 'center',
+                    height: '300px',
+                    position: 'relative',
+                    cursor: 'pointer'
+                  }}
+                >
+                  <div className={styles.carouselOverlay}>
+                    <h2 className={styles.carouselTitle}>{post.title}</h2>
+                    <p className={styles.carouselSummary}>{post.summary}</p>
+                    <div className={styles.carouselMeta}>
+                      <span>作者: {post.author}</span>
+                      <span>浏览: {post.views}</span>
+                      <span>点赞: {post.likes}</span>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            ))
+          ) : (
+            // 默认轮播图
+            <>
+              <div>
+                <div 
+                  className={styles.carouselSlide}
+                  style={{
+                    backgroundImage: `url(/images/flower.jpg)`,
+                    backgroundSize: 'cover',
+                    backgroundPosition: 'center',
+                    height: '300px',
+                  }}
+                >
+                  <div className={styles.carouselOverlay}>
+                    <h2 className={styles.carouselTitle}>欢迎来到ThunderHub</h2>
+                    <p className={styles.carouselSummary}>发现精彩内容,分享美好时光</p>
+                  </div>
+                </div>
+              </div>
+            </>
+          )}
+        </Carousel>
+      </div>
+
+      {/* 卡片帖子区 */}
+      <div className={styles.postsSection}>
+        {selectedCategory !== 'all' && (
+          <div className={styles.categoryTitle}>
+            <h2>{selectedCategory} 分类</h2>
+            <p>共找到 {total} 篇相关帖子</p>
+          </div>
+        )}
+        
+        <Row gutter={[24, 24]} className={styles.postsRow}>
+          {posts.map((post) => (
+            <Col xs={24} sm={12} lg={8} xl={6} key={post.id} className={styles.postCol}>
+              <PostCard post={post} />
+            </Col>
+          ))}
+        </Row>
+
+        {posts.length === 0 && !loading && (
+          <div className={styles.emptyState}>
+            <p>暂无相关帖子</p>
+          </div>
+        )}
+
+        <div className={styles.paginationContainer}>
+          <Pagination
+            current={page}
+            pageSize={pageSize}
+            total={total}
+            onChange={handlePageChange}
+            showTotal={(total) => `共 ${total} 条帖子`}
+          />
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default PostCenter;
\ No newline at end of file
diff --git a/src/pages/PostCenter/types.ts b/src/pages/PostCenter/types.ts
new file mode 100644
index 0000000..4cfe237
--- /dev/null
+++ b/src/pages/PostCenter/types.ts
@@ -0,0 +1,55 @@
+export interface Post {
+  postId?: number;
+  id: string | number;
+  title: string;
+  author: string;
+  publishTime: string;
+  tags: string[];
+  views: number;
+  comments?: number;
+  favorites?: number;
+  likes?: number;
+  coverImage?: string;
+  summary?: string;
+  content?: string;
+  status?: string;
+  authorId?: number;
+  createTime?: string;
+  updateTime?: string;
+  promotionPlanId?: number;
+  isPromoted?: boolean;
+}
+
+export interface CommentType {
+  commentId?: number;
+  id: number;
+  author: string;
+  avatar: string;
+  content: string;
+  datetime: string;
+  replies: CommentType[];
+  likes?: number;
+  parentId?: number;
+  postId?: number;
+  userId?: number;
+  userName?: string;
+  userAvatar?: string;
+  status?: string;
+}
+
+export interface PostTag {
+  tagId: number;
+  tagName: string;
+  tagColor?: string;
+  postCount: number;
+  status?: string;
+}
+
+export interface PostFavorite {
+  favoriteId: number;
+  postId: number;
+  userId: number;
+  postTitle?: string;
+  postCover?: string;
+  status: string;
+} 
\ No newline at end of file