保存本地对routes.ts的修改

Change-Id: I4f4dbd8069893d7363e251130791dc0594be44e1
diff --git a/src/api/categoryApi.ts b/src/api/categoryApi.ts
new file mode 100644
index 0000000..816ac71
--- /dev/null
+++ b/src/api/categoryApi.ts
@@ -0,0 +1,90 @@
+import axios from 'axios';
+import type { CategoryDTO, Category, WorkResponse } from './otherType';
+
+const API_BASE_URL = '/api';
+
+class CategoryAPI {
+  /**
+   * 获取全部分区(树形结构)
+   * @returns 返回分区的树形结构数据
+   */
+  static getCategoryTree(): Promise<CategoryDTO[]> {
+    return axios.get<CategoryDTO[]>(`${API_BASE_URL}/categories`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取单个分区详情
+   * @param id 分区ID
+   * @returns 返回单个分区的详细信息
+   */
+  static getCategory(id: number): Promise<Category> {
+    return axios.get<Category>(`${API_BASE_URL}/categories/${id}`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 创建新分区
+   * @param category 分区数据(不包含ID)
+   * @returns 返回创建成功的消息
+   */
+  static createCategory(category: Omit<Category, 'id'>): Promise<string> {
+    return axios.post<string>(`${API_BASE_URL}/categories`, category)
+      .then(response => response.data);
+  }
+
+  /**
+   * 更新分区信息
+   * @param id 分区ID
+   * @param category 更新后的分区数据
+   * @returns 返回更新成功的消息
+   */
+  static updateCategory(id: number, category: Partial<Category>): Promise<string> {
+    return axios.put<string>(`${API_BASE_URL}/categories/${id}`, category)
+      .then(response => response.data);
+  }
+
+  /**
+   * 删除分区
+   * @param id 分区ID
+   * @returns 返回删除成功的消息
+   */
+  static deleteCategory(id: number): Promise<string> {
+    return axios.delete<string>(`${API_BASE_URL}/categories/${id}`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取分区下的作品列表(带分页)
+   * @param categoryId 分区ID
+   * @param params 分页和排序参数
+   * @returns 返回作品列表和分页信息
+   */
+  static getWorksByCategory(
+    categoryId: number,
+    params?: {
+      page?: number;
+      size?: number;
+      sort?: string;
+    }
+  ): Promise<WorkResponse> {
+    return axios.get<WorkResponse>(`${API_BASE_URL}/works`, {
+      params: {
+        categoryId,
+        ...params
+      }
+    }).then(response => response.data);
+  }
+
+  /**
+   * 获取子分区列表
+   * @param parentId 父分区ID
+   * @returns 返回子分区列表
+   */
+  static getSubCategories(parentId: number): Promise<CategoryDTO[]> {
+    return axios.get<CategoryDTO[]>(`${API_BASE_URL}/categories/${parentId}/children`)
+      .then(response => response.data);
+  }
+}
+
+export default CategoryAPI;
\ No newline at end of file
diff --git a/src/api/categoryTypes.ts b/src/api/categoryTypes.ts
new file mode 100644
index 0000000..4a46034
--- /dev/null
+++ b/src/api/categoryTypes.ts
@@ -0,0 +1,67 @@
+import type { ReactNode } from "react";
+
+// 基础分类接口
+export interface CategoryDTO {
+  id: number;
+  name: string;
+  description?: string;
+  parentId?: number;  // 移除了 null 类型,只保留 number | undefined
+  children?: CategoryDTO[];
+  workCount?: number;
+}
+
+
+// 基础资源接口
+interface BaseResource {
+  id: number;
+  title: string;
+  categoryId: number;
+  categoryName: string;
+  uploadDate: string;
+  uploader: string;
+  size: string;
+  seeders: number;
+  leechers: number;
+  downloadCount: number;
+  hash: string;
+}
+
+// 音乐资源
+export interface MusicResource extends BaseResource {
+  category: ReactNode;
+  artist: string;
+  quality: 'MP3 128k' | 'MP3 320k' | 'FLAC' | 'APE' | 'Hi-Res' | 'DSD';
+  genre: string[];
+}
+
+// 影视资源
+export interface MovieResource extends BaseResource {
+  director: string;
+  actors: string[];
+  resolution: '1080p' | '720p' | '4K' | '8K';
+  duration: string;
+}
+
+// 游戏资源
+export interface GameResource extends BaseResource {
+  developer: string;
+  publisher: string;
+  platform: 'PC' | 'PS' | 'Xbox' | 'Switch';
+  releaseDate: string;
+}
+
+// 其他资源
+export interface OtherResource extends BaseResource {
+  description: string;
+  fileType: string;
+}
+
+// 分类DTO
+export interface CategoryDTO {
+  id: number;
+  name: string;
+  description?: string;
+  parentId?: number;
+  children?: CategoryDTO[];
+  workCount?: number;
+}
\ No newline at end of file
diff --git a/src/api/commentApi.ts b/src/api/commentApi.ts
new file mode 100644
index 0000000..360e659
--- /dev/null
+++ b/src/api/commentApi.ts
@@ -0,0 +1,77 @@
+import axios from 'axios';
+import type { Comment, CommentFormData, CommentReply, ReplyFormData } from './otherType';
+
+const API_BASE_URL = '/api';
+
+class CommentAPI {
+  /**
+   * 添加评论
+   */
+  static addComment(data: CommentFormData): Promise<Comment> {
+    return axios.post<Comment>(`${API_BASE_URL}/comments`, data)
+      .then(response => response.data);
+  }
+
+  /**
+   * 删除评论
+   */
+  static deleteComment(commentId: number): Promise<void> {
+    return axios.delete(`${API_BASE_URL}/comments/${commentId}`);
+  }
+
+  /**
+   * 更新评论
+   */
+  static updateComment(commentId: number, data: CommentFormData): Promise<Comment> {
+    return axios.put<Comment>(`${API_BASE_URL}/comments/${commentId}`, data)
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取单个评论
+   */
+  static getCommentById(commentId: number): Promise<Comment> {
+    return axios.get<Comment>(`${API_BASE_URL}/comments/${commentId}`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取帖子下的所有评论
+   */
+  static getCommentsByPostId(postId: number): Promise<Comment[]> {
+    return axios.get<Comment[]>(`${API_BASE_URL}/comments/post/${postId}`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 添加回复
+   */
+  static addReply(commentId: number, data: ReplyFormData): Promise<CommentReply> {
+    return axios.post<CommentReply>(`${API_BASE_URL}/comments/${commentId}/replies`, data)
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取评论的所有回复
+   */
+  static getReplies(commentId: number): Promise<CommentReply[]> {
+    return axios.get<CommentReply[]>(`${API_BASE_URL}/comments/${commentId}/replies`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 点赞评论
+   */
+  static likeComment(commentId: number): Promise<void> {
+    return axios.post(`${API_BASE_URL}/comments/${commentId}/like`);
+  }
+
+  /**
+   * 举报评论
+   */
+  static reportComment(commentId: number, reason: string): Promise<void> {
+    return axios.post(`${API_BASE_URL}/comments/${commentId}/report`, { reason });
+  }
+}
+
+export default CommentAPI;
\ No newline at end of file
diff --git a/src/api/otherType.ts b/src/api/otherType.ts
new file mode 100644
index 0000000..60523d0
--- /dev/null
+++ b/src/api/otherType.ts
@@ -0,0 +1,164 @@
+// 认证相关类型
+export interface LoginRequest {
+  email: string;
+  password: string;
+}
+
+export interface LoginResponse {
+  user: string;
+  token: string;
+  refreshToken: string;
+}
+
+export interface CommonResponse<T= null> {
+    code: number;       
+    message: string;     
+    data: T;          
+}
+
+// 作品相关类型
+export interface Work {
+  size: string;
+  data(data: any): unknown;
+  artist: string;
+  quality: string;
+  genre: string[];
+  id: number;
+  title: string;
+  author: string;
+  authorId: number;
+  views: number;
+  likes: number;
+  categoryId: number;
+  categoryName: string;
+  description: string;
+  content: string;
+  coverUrl: string;
+  createTime: string;
+  attachments: Attachment[];
+  bugCount: number;
+  discussionCount: number;
+}
+
+export interface WorkResponse {
+  content: Work[];
+  totalElements: number;
+  totalPages: number;
+}
+
+export interface WorkFormData {
+  title: string;
+  categoryId: number;
+  description: string;
+  content: string;
+  coverFile?: File;
+  attachments?: File[];
+}
+
+// Bug反馈相关类型
+export interface BugReport {
+  id: number;
+  title: string;
+  description: string;
+  severity: 'low' | 'medium' | 'high';
+  status: 'open' | 'resolved' | 'wontfix';
+  reporter: string;
+  reporterId: number;
+  workId: number;
+  createTime: string;
+}
+
+// 讨论区相关类型
+export interface Discussion {
+  id: number;
+  title: string;
+  content: string;
+  author: string;
+  authorId: number;
+  workId: number;
+  isPinned: boolean;
+  commentCount: number;
+  createTime: string;
+  lastUpdateTime: string;
+}
+
+export interface DiscussionFormData {
+  workId: number;
+  title: string;
+  content: string;
+}
+
+// 评论相关类型
+export interface Comment {
+  id: number;
+  author: string;
+  avatar: string;
+  content: string;
+  likes: number;
+  replyCount: number;
+  createTime: string;
+  replies?: CommentReply[];
+}
+
+export interface CommentFormData {
+  postId: number; // 可以是作品ID或讨论区ID
+  content: string;
+  parentId?: number; // 用于回复评论
+}
+
+export interface CommentReply {
+  id: number;
+  author: string;
+  avatar: string;
+  content: string;
+  likes: number;
+  createTime: string;
+}
+
+export interface ReplyFormData {
+  content: string;
+}
+
+// 分区相关类型
+export interface Category {
+  id: number;
+  name: string;
+  description?: string;
+  parentId?: number | null;
+  coverUrl?: string;
+  createdAt?: string;
+  updatedAt?: string;
+}
+
+export interface CategoryDTO extends Category {
+  children?: CategoryDTO[];
+  workCount?: number;
+}
+
+// 用户相关类型
+export interface User {
+  id: number;
+  username: string;
+  email: string;
+  avatar?: string;
+  role: 'user' | 'admin';
+  createdAt: string;
+  worksCount: number;
+  completedWorks: number;
+}
+
+// 通用类型
+export interface Attachment {
+  id: number;
+  name: string;
+  url: string;
+  size: string;
+}
+
+// API响应通用类型
+export interface ApiResponse<T> {
+  success: boolean;
+  data?: T;
+  message?: string;
+  error?: string;
+}
\ No newline at end of file
diff --git a/src/api/workApi.ts b/src/api/workApi.ts
new file mode 100644
index 0000000..d624821
--- /dev/null
+++ b/src/api/workApi.ts
@@ -0,0 +1,105 @@
+import axios from 'axios';
+import type { Work, WorkResponse, WorkFormData, BugReport, Discussion } from './otherType';
+
+const API_BASE_URL = '/api';
+
+class WorkAPI {
+  /**
+   * 获取作品列表(支持分页、分类筛选和搜索)
+   */
+  static getWorks(params?: {
+    categoryId?: number;
+    page?: number;
+    size?: number;
+    search?: string;
+  }): Promise<WorkResponse> {
+    return axios.get<WorkResponse>(`${API_BASE_URL}/works`, { params })
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取单个作品详情
+   */
+  static getWorkById(id: number): Promise<Work> {
+    return axios.get<Work>(`${API_BASE_URL}/works/${id}`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 创建新作品
+   */
+  static createWork(data: WorkFormData): Promise<Work> {
+    return axios.post<Work>(`${API_BASE_URL}/works`, data)
+      .then(response => response.data);
+  }
+
+  /**
+   * 更新作品信息
+   */
+  static updateWork(id: number, data: WorkFormData): Promise<Work> {
+    return axios.put<Work>(`${API_BASE_URL}/works/${id}`, data)
+      .then(response => response.data);
+  }
+
+  /**
+   * 删除作品
+   */
+  static deleteWork(id: number): Promise<void> {
+    return axios.delete(`${API_BASE_URL}/works/${id}`);
+  }
+
+  /**
+   * 点赞作品
+   */
+  static likeWork(id: number): Promise<void> {
+    return axios.post(`${API_BASE_URL}/works/${id}/like`);
+  }
+
+  /**
+   * 根据作者查询作品
+   */
+  static getWorksByAuthor(author: string): Promise<Work[]> {
+    return axios.get<Work[]>(`${API_BASE_URL}/works/byAuthor`, { params: { author } })
+      .then(response => response.data);
+  }
+
+  /**
+   * 获取作品Bug反馈列表
+   */
+  static getBugReports(workId: number): Promise<BugReport[]> {
+    return axios.get<BugReport[]>(`${API_BASE_URL}/works/${workId}/bugs`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 提交Bug反馈
+   */
+  static reportBug(workId: number, data: {
+    title: string;
+    description: string;
+    severity: 'low' | 'medium' | 'high';
+  }): Promise<void> {
+    return axios.post(`${API_BASE_URL}/works/${workId}/bugs`, data);
+  }
+
+  /**
+   * 获取作品讨论区列表
+   */
+  static getDiscussions(workId: number): Promise<Discussion[]> {
+    return axios.get<Discussion[]>(`${API_BASE_URL}/works/${workId}/discussions`)
+      .then(response => response.data);
+  }
+
+  /**
+   * 创建新讨论
+   */
+  static createDiscussion(data: {
+    workId: number;
+    title: string;
+    content: string;
+  }): Promise<void> {
+    return axios.post(`${API_BASE_URL}/discussions`, data);
+  }
+}
+
+export default WorkAPI;
\ No newline at end of file
diff --git a/src/assets/categories/film.jpg b/src/assets/categories/film.jpg
new file mode 100644
index 0000000..3d05b4d
--- /dev/null
+++ b/src/assets/categories/film.jpg
Binary files differ
diff --git a/src/assets/categories/game.jpg b/src/assets/categories/game.jpg
new file mode 100644
index 0000000..15571fe
--- /dev/null
+++ b/src/assets/categories/game.jpg
Binary files differ
diff --git a/src/assets/categories/music.jpg b/src/assets/categories/music.jpg
new file mode 100644
index 0000000..47dda2c
--- /dev/null
+++ b/src/assets/categories/music.jpg
Binary files differ
diff --git a/src/assets/categories/other.jpg b/src/assets/categories/other.jpg
new file mode 100644
index 0000000..b7385c0
--- /dev/null
+++ b/src/assets/categories/other.jpg
Binary files differ
diff --git a/src/components/BugReportSection.tsx b/src/components/BugReportSection.tsx
new file mode 100644
index 0000000..a512fc6
--- /dev/null
+++ b/src/components/BugReportSection.tsx
@@ -0,0 +1,159 @@
+import React, { useState, useEffect } from 'react';
+import { List, Button, Form, Input, Select, message, Typography, Tag, Divider, Pagination } from 'antd';
+import { BugOutlined } from '@ant-design/icons';
+import WorkAPI from '../api/workApi';
+import type { BugReport } from '../api/otherType';
+
+const { Text } = Typography;
+const { Option } = Select;
+const { TextArea } = Input;
+
+interface BugReportForm {
+  title: string;
+  description: string;
+  severity: 'low' | 'medium' | 'high';
+}
+
+const BugReportSection: React.FC<{ workId: number }> = ({ workId }) => {
+  const [bugs, setBugs] = useState<BugReport[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [form] = Form.useForm();
+  const [currentPage, setCurrentPage] = useState(1);
+  const [pageSize, setPageSize] = useState(5); // 每页显示5条
+
+  // 加载Bug反馈列表
+  useEffect(() => {
+    const loadBugs = async () => {
+      try {
+        setLoading(true);
+        const bugList = await WorkAPI.getBugReports(workId);
+        setBugs(bugList);
+      } catch (error) {
+        message.error('加载Bug反馈失败');
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    loadBugs();
+  }, [workId]);
+
+  // 获取当前页的Bug数据
+  const getCurrentPageBugs = () => {
+    const startIndex = (currentPage - 1) * pageSize;
+    const endIndex = startIndex + pageSize;
+    return bugs.slice(startIndex, endIndex);
+  };
+
+  // 提交Bug反馈
+  const handleSubmit = async (values: BugReportForm) => {
+    try {
+      await WorkAPI.reportBug(workId, values);
+      message.success('Bug反馈提交成功');
+      form.resetFields();
+      // 重新加载Bug列表
+      const bugList = await WorkAPI.getBugReports(workId);
+      setBugs(bugList);
+      // 重置到第一页
+      setCurrentPage(1);
+    } catch (error) {
+      message.error('Bug反馈提交失败');
+    }
+  };
+
+  const getSeverityColor = (severity: string) => {
+    switch (severity) {
+      case 'high': return 'red';
+      case 'medium': return 'orange';
+      default: return 'green';
+    }
+  };
+
+  return (
+    <div className="bug-report-section">
+      <Divider orientation="left">
+        <BugOutlined /> Bug反馈
+      </Divider>
+
+      <Form form={form} onFinish={handleSubmit} layout="vertical">
+        <Form.Item
+          name="title"
+          label="Bug标题"
+          rules={[{ required: true, message: '请输入Bug标题' }]}
+        >
+          <Input placeholder="简要描述Bug" />
+        </Form.Item>
+
+        <Form.Item
+          name="severity"
+          label="严重程度"
+          initialValue="medium"
+          rules={[{ required: true }]}
+        >
+          <Select>
+            <Option value="low">低</Option>
+            <Option value="medium">中</Option>
+            <Option value="high">高</Option>
+          </Select>
+        </Form.Item>
+
+        <Form.Item
+          name="description"
+          label="详细描述"
+          rules={[{ required: true, message: '请详细描述Bug' }]}
+        >
+          <TextArea rows={4} placeholder="请详细描述Bug出现的步骤、现象和预期结果" />
+        </Form.Item>
+
+        <Form.Item>
+          <Button type="primary" htmlType="submit">
+            提交Bug
+          </Button>
+        </Form.Item>
+      </Form>
+
+      <Divider orientation="left">Bug列表</Divider>
+
+      <List
+        dataSource={getCurrentPageBugs()}
+        loading={loading}
+        renderItem={bug => (
+          <List.Item>
+            <div className="bug-item">
+              <div className="bug-header">
+                <Text strong>{bug.title}</Text>
+                <Tag color={getSeverityColor(bug.severity)}>
+                  {bug.severity === 'high' ? '严重' : bug.severity === 'medium' ? '中等' : '轻微'}
+                </Tag>
+              </div>
+              <Text type="secondary">报告人: {bug.reporter} | 时间: {new Date(bug.createTime).toLocaleString()}</Text>
+              <div className="bug-content">
+                <Text>{bug.description}</Text>
+              </div>
+              <Tag color={bug.status === 'open' ? 'red' : bug.status === 'resolved' ? 'green' : 'gray'}>
+                {bug.status === 'open' ? '未解决' : bug.status === 'resolved' ? '已解决' : '不予解决'}
+              </Tag>
+            </div>
+          </List.Item>
+        )}
+      />
+
+      <div style={{ textAlign: 'right', marginTop: 16 }}>
+        <Pagination
+          current={currentPage}
+          pageSize={pageSize}
+          total={bugs.length}
+          onChange={(page, size) => {
+            setCurrentPage(page);
+            setPageSize(size);
+          }}
+          showSizeChanger
+          showQuickJumper
+          showTotal={total => `共 ${total} 条`}
+        />
+      </div>
+    </div>
+  );
+};
+
+export default BugReportSection;
\ No newline at end of file
diff --git a/src/components/CustomComment.css b/src/components/CustomComment.css
new file mode 100644
index 0000000..8f9a012
--- /dev/null
+++ b/src/components/CustomComment.css
@@ -0,0 +1,54 @@
+/* src/components/CustomComment.css */
+.custom-comment {
+  margin-bottom: 16px;
+  padding: 16px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.custom-comment:last-child {
+  border-bottom: none;
+}
+
+.custom-comment-inner {
+  display: flex;
+}
+
+.custom-comment-avatar {
+  margin-right: 12px;
+}
+
+.custom-comment-content {
+  flex: 1;
+}
+
+.custom-comment-header {
+  margin-bottom: 8px;
+  display: flex;
+  align-items: center;
+}
+
+.custom-comment-author {
+  font-weight: 500;
+  color: rgba(0, 0, 0, 0.85);
+}
+
+.custom-comment-datetime {
+  margin-left: 12px;
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.custom-comment-text {
+  margin-bottom: 8px;
+  color: rgba(0, 0, 0, 0.85);
+  white-space: pre-line;
+}
+
+.custom-comment-actions {
+  margin-top: 8px;
+}
+
+.custom-comment-action {
+  padding: 0 4px;
+  font-size: 12px;
+}
\ No newline at end of file
diff --git a/src/components/DiscussionSection.tsx b/src/components/DiscussionSection.tsx
new file mode 100644
index 0000000..7fd39c4
--- /dev/null
+++ b/src/components/DiscussionSection.tsx
@@ -0,0 +1,190 @@
+import React, { useState, useEffect } from 'react';
+import { List, Button, Form, Input, message, Avatar, Typography, Modal, Pagination } from 'antd';
+import { LikeOutlined, MessageOutlined } from '@ant-design/icons';
+import CommentAPI from '../api/commentApi';
+import type { Comment } from '../api/otherType';
+
+const { Text } = Typography;
+const { TextArea } = Input;
+
+interface DiscussionSectionProps {
+  workId: number;
+}
+
+const DiscussionSection: React.FC<DiscussionSectionProps> = ({ workId }) => {
+  const [comments, setComments] = useState<Comment[]>([]);
+  const [loading, setLoading] = useState(false);
+  const [modalVisible, setModalVisible] = useState(false);
+  const [form] = Form.useForm();
+  const [currentPage, setCurrentPage] = useState(1);
+  const [pageSize, setPageSize] = useState(5); // 每页显示5条
+
+  // 加载评论
+  useEffect(() => {
+    const loadComments = async () => {
+      try {
+        setLoading(true);
+        const commentList = await CommentAPI.getCommentsByPostId(workId);
+        setComments(commentList);
+      } catch (error) {
+        message.error('加载评论失败');
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    loadComments();
+  }, [workId]);
+
+  // 获取当前页的评论数据
+  const getCurrentPageComments = () => {
+    const startIndex = (currentPage - 1) * pageSize;
+    const endIndex = startIndex + pageSize;
+    return comments.slice(startIndex, endIndex);
+  };
+
+  // 提交新评论
+  const handleSubmit = async (values: { title: string; content: string }) => {
+    try {
+      await CommentAPI.addComment({
+        postId: workId,
+        content: values.content
+      });
+      message.success('评论添加成功');
+      form.resetFields();
+      setModalVisible(false);
+      // 重新加载评论
+      const commentList = await CommentAPI.getCommentsByPostId(workId);
+      setComments(commentList);
+      // 重置到第一页
+      setCurrentPage(1);
+    } catch (error) {
+      message.error('评论添加失败');
+    }
+  };
+
+  // 点赞评论
+  const handleLikeComment = async (commentId: number) => {
+    try {
+      await CommentAPI.likeComment(commentId);
+      setComments(prev => prev.map(comment => 
+        comment.id === commentId 
+          ? { ...comment, likes: (comment.likes || 0) + 1 } 
+          : comment
+      ));
+    } catch (error) {
+      message.error('点赞失败');
+    }
+  };
+
+  return (
+    <div className="discussion-section">
+      <Button 
+        type="primary" 
+        icon={<MessageOutlined />}
+        onClick={() => setModalVisible(true)}
+        style={{ marginBottom: 16 }}
+      >
+        新建讨论
+      </Button>
+
+      <List
+        dataSource={getCurrentPageComments()}
+        loading={loading}
+        renderItem={comment => (
+          <List.Item className="comment-item">
+            <div className="comment-content">
+              <div className="comment-header">
+                <Avatar size="small">
+                  {comment.author?.charAt(0) || '?'}
+                </Avatar>
+                <Text strong>{comment.author || '匿名用户'}</Text>
+                <Text type="secondary">
+                  {comment.createTime ? new Date(comment.createTime).toLocaleString() : '未知时间'}
+                </Text>
+              </div>
+              <div className="comment-body">
+                <Text>{comment.content}</Text>
+              </div>
+              <div className="comment-actions">
+                <Button 
+                  type="text" 
+                  icon={<LikeOutlined />} 
+                  onClick={() => handleLikeComment(comment.id)}
+                >
+                  {comment.likes || 0}
+                </Button>
+              </div>
+            </div>
+          </List.Item>
+        )}
+      />
+
+      <div style={{ textAlign: 'right', marginTop: 16 }}>
+        <Pagination
+          current={currentPage}
+          pageSize={pageSize}
+          total={comments.length}
+          onChange={(page, size) => {
+            setCurrentPage(page);
+            setPageSize(size);
+          }}
+          showSizeChanger
+          showQuickJumper
+          showTotal={total => `共 ${total} 条`}
+        />
+      </div>
+
+      <Modal
+        title={
+          <span>
+            <MessageOutlined style={{ marginRight: 8 }} />
+            新建讨论
+          </span>
+        }
+        visible={modalVisible}
+        onCancel={() => setModalVisible(false)}
+        footer={[
+          <Button key="cancel" onClick={() => setModalVisible(false)}>
+            取消
+          </Button>,
+          <Button 
+            key="submit" 
+            type="primary" 
+            onClick={() => {
+              form.validateFields()
+                .then(values => handleSubmit(values))
+                .catch(() => message.error('请填写完整信息'));
+            }}
+          >
+            创建讨论
+          </Button>,
+        ]}
+      >
+        <Form form={form} layout="vertical">
+          <Form.Item
+            name="title"
+            label="讨论主题"
+            rules={[{ required: true, message: '请输入讨论主题' }]}
+          >
+            <Input placeholder="输入讨论主题" />
+          </Form.Item>
+          
+          <Form.Item
+            name="content"
+            label="讨论内容"
+            rules={[{ required: true, message: '请输入讨论内容' }]}
+          >
+            <TextArea 
+              rows={6} 
+              placeholder="详细描述你想讨论的内容..."
+            />
+          </Form.Item>
+        </Form>
+      </Modal>
+    </div>
+  );
+};
+
+
+export default DiscussionSection;
\ No newline at end of file
diff --git a/src/feature/categories/GameCategory.tsx b/src/feature/categories/GameCategory.tsx
new file mode 100644
index 0000000..3f5c78d
--- /dev/null
+++ b/src/feature/categories/GameCategory.tsx
@@ -0,0 +1,173 @@
+import React, { useState, useEffect } from 'react';
+import { Card, List, message, Spin, Input, Tag, Typography} from 'antd';
+import { SearchOutlined, PlayCircleOutlined } from '@ant-design/icons';
+import CategoryAPI from '../../api/categoryApi';
+import type { GameResource } from '../../api/categoryTypes';
+import { useNavigate } from 'react-router';
+
+const { Text } = Typography;
+const { Search } = Input;
+
+const GameCategory: React.FC = () => {
+  const [games, setGames] = useState<GameResource[]>([]);
+  const [filteredGames, setFilteredGames] = useState<GameResource[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [searchQuery, setSearchQuery] = useState('');
+  
+  // 加载数据
+useEffect(() => {
+  const loadGames = async () => {
+    try {
+      const response = await CategoryAPI.getWorksByCategory(3);
+      
+      // 类型转换或断言
+      const gameResources = response.content.map((work: any) => ({
+        ...work,
+        developer: (work as any).developer || '未知开发商',
+        publisher: (work as any).publisher || '未知发行商',
+        platform: (work as any).platform || 'PC',
+        releaseDate: (work as any).releaseDate || '未知日期'
+      })) as unknown as GameResource[];
+      
+      setGames(gameResources);
+      setFilteredGames(gameResources);
+    } catch (error) {
+      message.error('加载游戏资源失败');
+    } finally {
+      setIsLoading(false);
+    }
+  };
+  
+  loadGames();
+}, []);
+
+  // 筛选逻辑
+  useEffect(() => {
+    if (!searchQuery) {
+      setFilteredGames(games);
+      return;
+    }
+    
+    const filtered = games.filter(game => 
+      game.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+      game.developer.toLowerCase().includes(searchQuery.toLowerCase())
+    );
+    
+    setFilteredGames(filtered);
+  }, [searchQuery, games]);
+
+
+  // // 在加载数据部分添加虚拟数据
+  // useEffect(() => {
+  //   const loadGames = async () => {
+  //     try {
+  //       // 模拟API请求
+  //       const mockGames: GameResource[] = [
+  //         {
+  //           id: 5,
+  //           title: '赛博朋克2077',
+  //           developer: 'CD Projekt Red',
+  //           publisher: 'CD Projekt',
+  //           categoryId: 3,
+  //           categoryName: '角色扮演',
+  //           platform: 'PC',
+  //           releaseDate: '2020-12-10',
+  //           seeders: 156,
+  //           leechers: 34,
+  //           uploadDate: '2023-06-01',
+  //           uploader: 'gameLover',
+  //           downloadCount: 2876,
+  //           hash: 'm3n4o5p6q7r8',
+  //           size: '123Mb'
+  //         },
+  //         {
+  //           id: 6,
+  //           title: '艾尔登法环',
+  //           developer: 'FromSoftware',
+  //           publisher: '万代南梦宫',
+  //           categoryId: 3,
+  //           categoryName: '动作冒险',
+  //           releaseDate: '2022-02-25',
+  //           seeders: 201,
+  //           leechers: 45,
+  //           uploadDate: '2023-05-20',
+  //           uploader: 'hardcoreGamer',
+  //           downloadCount: 3542,
+  //           hash: 's9t0u1v2w3x4',
+  //           platform: 'PC',
+  //           size: '234Mb'
+  //         }
+  //       ];
+
+  //       setGames(mockGames);
+  //       setFilteredGames(mockGames);
+  //     } catch (error) {
+  //       message.error('加载游戏资源失败');
+  //     } finally {
+  //       setIsLoading(false);
+  //     }
+  //   };
+    
+  //   loadGames();
+  // }, []);
+  
+  return (
+    <div className="game-category-container">
+      <div className="category-header">
+        <h1><PlayCircleOutlined /> 游戏资源分区</h1>
+        <p>PC、主机游戏资源分享</p>
+      </div>
+      
+      <Search
+        placeholder="搜索游戏名称或开发商"
+        allowClear
+        enterButton={<SearchOutlined />}
+        value={searchQuery}
+        onChange={e => setSearchQuery(e.target.value)}
+        className="search-bar"
+      />
+      
+      {isLoading ? (
+        <Spin tip="加载游戏资源..." size="large" />
+      ) : (
+        <List
+          grid={{ gutter: 16, column: 1 }}
+          dataSource={filteredGames}
+          renderItem={game => (
+            <List.Item>
+              <GameCard game={game} />
+            </List.Item>
+          )}
+        />
+      )}
+    </div>
+  );
+};
+
+const GameCard: React.FC<{ game: GameResource }> = ({ game }) => {
+  const navigate = useNavigate();
+  
+  const handleClick = () => {
+    navigate(`/works/${game.id}`);
+  };
+
+  return (
+    <Card className="game-card" onClick={handleClick} hoverable>
+      <div className="card-content">
+        <div className="card-header">
+          <Text strong className="resource-title">{game.title}</Text>
+          <Text type="secondary" className="resource-developer">开发商: {game.developer}</Text>
+        </div>
+        
+        <div className="card-meta">
+          <Tag color="blue">{game.platform}</Tag>
+          <Tag color="geekblue">{game.categoryName}</Tag>
+          <Tag color="green">{game.seeders} 做种</Tag>
+          <Tag color="orange">{game.leechers} 下载</Tag>
+        </div>
+      </div>
+    </Card>
+  );
+};
+
+export default GameCategory;
\ No newline at end of file
diff --git a/src/feature/categories/MovieCategory.tsx b/src/feature/categories/MovieCategory.tsx
new file mode 100644
index 0000000..262ebd4
--- /dev/null
+++ b/src/feature/categories/MovieCategory.tsx
@@ -0,0 +1,172 @@
+import React, { useState, useEffect } from 'react';
+import { Card, List, message, Spin, Input, Tag, Typography} from 'antd';
+import { SearchOutlined, VideoCameraOutlined } from '@ant-design/icons';
+import CategoryAPI from '../../api/categoryApi';
+import type { MovieResource } from '../../api/categoryTypes';
+import { useNavigate } from 'react-router';
+
+const { Text } = Typography;
+const { Search } = Input;
+
+const MovieCategory: React.FC = () => {
+  const [movies, setMovies] = useState<MovieResource[]>([]);
+  const [filteredMovies, setFilteredMovies] = useState<MovieResource[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [searchQuery, setSearchQuery] = useState('');
+  
+  // 加载数据
+useEffect(() => {
+  const loadMovies = async () => {
+    try {
+      const response = await CategoryAPI.getWorksByCategory(2);
+      // 添加类型断言或转换
+      const movies = response.content.map((work: any) => ({
+        ...work,
+        director: (work as any).director || '未知导演',
+        actors: (work as any).actors || [],
+        resolution: (work as any).resolution || '1080p',
+        duration: (work as any).duration || '0分钟'
+      })) as unknown as MovieResource[];
+      
+      setMovies(movies);
+      setFilteredMovies(movies);
+    } catch (error) {
+      message.error('加载影视资源失败');
+    } finally {
+      setIsLoading(false);
+    }
+  };
+  
+  loadMovies();
+}, []);
+
+  // 筛选逻辑
+  useEffect(() => {
+    if (!searchQuery) {
+      setFilteredMovies(movies);
+      return;
+    }
+    
+    const filtered = movies.filter(movie => 
+      movie.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+      movie.director.toLowerCase().includes(searchQuery.toLowerCase())
+    );
+    
+    setFilteredMovies(filtered);
+  }, [searchQuery, movies]);
+
+
+//   // 在加载数据部分添加虚拟数据
+// useEffect(() => {
+//   const loadMovies = async () => {
+//     try {
+//       // 模拟API请求
+//       const mockMovies: MovieResource[] = [
+//         {
+//           id: 3,
+//           title: '盗梦空间',
+//           director: '克里斯托弗·诺兰',
+//           categoryId: 2,
+//           categoryName: '科幻电影',
+//           resolution: '1080p',
+//           duration: '148分钟',
+//           actors: ['莱昂纳多·迪卡普里奥', '约瑟夫·高登-莱维特'],
+//           seeders: 215,
+//           leechers: 42,
+//           uploadDate: '2023-06-10',
+//           uploader: 'movieFan',
+//           downloadCount: 3256,
+//           hash: 'a1b2c3d4e5f6',
+//           size: '234Mb'
+//         },
+//         {
+//           id: 4,
+//           title: '肖申克的救赎',
+//           director: '弗兰克·德拉邦特',
+//           categoryId: 2,
+//           categoryName: '剧情片',
+//           resolution: '4K',
+//           duration: '142分钟',
+//           actors: ['蒂姆·罗宾斯', '摩根·弗里曼'],
+//           seeders: 189,
+//           leechers: 28,
+//           uploadDate: '2023-05-15',
+//           uploader: 'classicMovie',
+//           downloadCount: 4123,
+//           hash: 'g7h8i9j0k1l2',
+//           size: '123Mb'
+//         }
+//       ];
+
+//       setMovies(mockMovies);
+//       setFilteredMovies(mockMovies);
+//     } catch (error) {
+//       message.error('加载影视资源失败');
+//     } finally {
+//       setIsLoading(false);
+//     }
+//   };
+  
+//   loadMovies();
+// }, []);
+
+  return (
+    <div className="movie-category-container">
+      <div className="category-header">
+        <h1><VideoCameraOutlined /> 影视资源分区</h1>
+        <p>高清电影、电视剧、纪录片资源分享</p>
+      </div>
+      
+      <Search
+        placeholder="搜索电影或导演"
+        allowClear
+        enterButton={<SearchOutlined />}
+        value={searchQuery}
+        onChange={e => setSearchQuery(e.target.value)}
+        className="search-bar"
+      />
+      
+      {isLoading ? (
+        <Spin tip="加载影视资源..." size="large" />
+      ) : (
+        <List
+          grid={{ gutter: 16, column: 1 }}
+          dataSource={filteredMovies}
+          renderItem={movie => (
+            <List.Item>
+              <MovieCard movie={movie} />
+            </List.Item>
+          )}
+        />
+      )}
+    </div>
+  );
+};
+
+const MovieCard: React.FC<{ movie: MovieResource }> = ({ movie }) => {
+  const navigate = useNavigate();
+  
+  const handleClick = () => {
+    navigate(`/works/${movie.id}`);
+  };
+
+  return (
+    <Card className="movie-card" onClick={handleClick} hoverable>
+      <div className="card-content">
+        <div className="card-header">
+          <Text strong className="resource-title">{movie.title}</Text>
+          <Text type="secondary" className="resource-director">导演: {movie.director}</Text>
+        </div>
+        
+        <div className="card-meta">
+          <Tag color="blue">{movie.resolution}</Tag>
+          <Tag color="geekblue">{movie.categoryName}</Tag>
+          <Tag color="green">{movie.seeders} 做种</Tag>
+          <Tag color="orange">{movie.leechers} 下载</Tag>
+        </div>
+      </div>
+    </Card>
+  );
+};
+
+export default MovieCategory;
\ No newline at end of file
diff --git a/src/feature/categories/MusicCategory.tsx b/src/feature/categories/MusicCategory.tsx
new file mode 100644
index 0000000..f9057ee
--- /dev/null
+++ b/src/feature/categories/MusicCategory.tsx
@@ -0,0 +1,191 @@
+import React, { useState, useEffect } from 'react';
+import { 
+  Card, Button, List, Tag, Typography, 
+  message, Spin, 
+  Input} from 'antd';
+import { 
+  SearchOutlined
+} from '@ant-design/icons';
+import CategoryAPI from '../../api/categoryApi';
+import type { CategoryDTO, MusicResource } from '../../api/categoryTypes';
+import { useNavigate } from 'react-router';
+
+const { Search } = Input;
+const { Text } = Typography;
+
+const MusicCategory: React.FC = () => {
+  const [categories, setCategories] = useState<CategoryDTO[]>([]);
+  const [resources, setResources] = useState<MusicResource[]>([]);
+  const [filteredResources, setFilteredResources] = useState<MusicResource[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [selectedCategory, setSelectedCategory] = useState<number | null>(null);
+  
+  // 筛选状态
+  const [searchQuery, setSearchQuery] = useState('');
+  const [qualityFilter, setQualityFilter] = useState<string>('all');
+
+  // 加载分类数据
+  useEffect(() => {
+    const loadData = async () => {
+      try {
+        const [categoryData, resourceData] = await Promise.all([
+          CategoryAPI.getCategoryTree(),
+          CategoryAPI.getWorksByCategory(1)
+        ]);
+        
+        // 确保 parentId 不包含 null
+        const normalizedCategories = categoryData.map(cat => ({
+          ...cat,
+          parentId: cat.parentId === null ? undefined : cat.parentId
+        })) as CategoryDTO[];
+        
+        // 音乐资源类型转换
+        const musicResources = resourceData.content.map(work => ({
+          ...work,
+          artist: work.artist || '未知艺术家',
+          quality: work.quality || 'MP3 320k',
+          genre: work.genre || ['未知流派'],
+          categoryName: work.categoryName || '未知分类',
+          size: work.size || '0MB'
+        })) as unknown as MusicResource[];
+        
+        setCategories(normalizedCategories);
+        setResources(musicResources);
+        setFilteredResources(musicResources);
+      } catch (error) {
+        message.error('加载数据失败');
+      } finally {
+        setIsLoading(false);
+      }
+    };
+    
+    loadData();
+  }, []);
+
+  // 筛选逻辑
+  useEffect(() => {
+    let filtered = resources;
+    
+    // 搜索筛选
+    if (searchQuery) {
+      filtered = filtered.filter(resource => 
+        resource.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+        resource.artist.toLowerCase().includes(searchQuery.toLowerCase())
+      );
+    }
+    
+    // 音质筛选
+    if (qualityFilter !== 'all') {
+      filtered = filtered.filter(resource => resource.quality === qualityFilter);
+    }
+    
+    // 分类筛选
+    if (selectedCategory) {
+      filtered = filtered.filter(resource => resource.categoryId === selectedCategory);
+    }
+    
+    setFilteredResources(filtered);
+  }, [searchQuery, qualityFilter, selectedCategory, resources]);
+
+  // 分类选择处理
+  const handleCategorySelect = (categoryId: number) => {
+    setSelectedCategory(prev => prev === categoryId ? null : categoryId);
+  };
+
+  return (
+    <div className="music-category-container">
+      {/* 头部区域 */}
+      <div className="category-header">
+        <h1>音乐资源分区</h1>
+        <p>高质量音乐资源共享,保持分享率,共建良好PT环境</p>
+      </div>
+      
+      {/* 搜索和筛选 */}
+      <div className="filter-section">
+        <Search
+          placeholder="搜索音乐名称或艺术家"
+          allowClear
+          enterButton={<SearchOutlined />}
+          value={searchQuery}
+          onChange={e => setSearchQuery(e.target.value)}
+          className="search-bar"
+        />
+        
+        <div className="quality-filter">
+          <span>音质筛选:</span>
+          <Button.Group>
+            {['all', 'FLAC', 'Hi-Res', 'MP3 320k'].map(quality => (
+              <Button 
+                key={quality}
+                type={qualityFilter === quality ? 'primary' : 'default'}
+                onClick={() => setQualityFilter(quality)}
+              >
+                {quality === 'all' ? '全部' : quality}
+              </Button>
+            ))}
+          </Button.Group>
+        </div>
+      </div>
+      
+      {/* 分类导航 */}
+      <div className="category-navigation">
+        {categories.map(category => (
+          <Tag 
+            key={category.id}
+            color={selectedCategory === category.id ? 'blue' : undefined}
+            onClick={() => handleCategorySelect(category.id)}
+            className="category-tag"
+          >
+            {category.name} ({category.workCount || 0})
+          </Tag>
+        ))}
+      </div>
+      
+      {/* 内容展示 */}
+      {isLoading ? (
+        <Spin tip="加载中..." size="large" />
+      ) : (
+        <div className="resource-list">
+          <List
+            grid={{ gutter: 16, column: 1 }}
+            dataSource={filteredResources}
+            renderItem={resource => (
+              <List.Item>
+                <ResourceCard resource={resource} />
+              </List.Item>
+            )}
+          />
+        </div>
+      )}
+    </div>
+  );
+};
+
+// 资源卡片组件
+const ResourceCard: React.FC<{ resource: MusicResource }> = ({ resource }) => {
+  const navigate = useNavigate();
+  
+  const handleClick = () => {
+    navigate(`/works/${resource.id}`);
+  };
+
+  return (
+    <Card className="resource-card" onClick={handleClick} hoverable>
+      <div className="card-content">
+        <div className="card-header">
+          <Text strong className="resource-title">{resource.title}</Text>
+          <Text type="secondary" className="resource-artist">{resource.artist}</Text>
+        </div>
+        
+        <div className="card-meta">
+          <Tag color="blue">{resource.quality}</Tag>
+          <Tag color="geekblue">{resource.categoryName}</Tag>
+          <Tag color="green">{resource.seeders} 做种</Tag>
+          <Tag color="orange">{resource.leechers} 下载</Tag>
+        </div>
+      </div>
+    </Card>
+  );
+};
+
+export default MusicCategory;
\ No newline at end of file
diff --git a/src/feature/categories/OtherCategory.tsx b/src/feature/categories/OtherCategory.tsx
new file mode 100644
index 0000000..6c3318a
--- /dev/null
+++ b/src/feature/categories/OtherCategory.tsx
@@ -0,0 +1,178 @@
+import React, { useState, useEffect } from 'react';
+import { Card, List, message, Spin, Input, Tag, Typography} from 'antd';
+import { SearchOutlined, AppstoreOutlined } from '@ant-design/icons';
+import CategoryAPI from '../../api/categoryApi';
+import type { MovieResource, OtherResource } from '../../api/categoryTypes';
+import { useNavigate } from 'react-router';
+
+const { Text } = Typography;
+const { Search } = Input;
+
+const OtherCategory: React.FC = () => {
+  const [resources, setResources] = useState<OtherResource[]>([]);
+  const [filteredResources, setFilteredResources] = useState<OtherResource[]>([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [searchQuery, setSearchQuery] = useState('');
+  
+  // 加载数据
+useEffect(() => {
+  const loadResources = async () => {
+    try {
+      const response = await CategoryAPI.getWorksByCategory(4);
+      
+      // 其他资源类型转换
+      const otherResources = response.content.map(work => ({
+        ...work,
+        description: (work as any).description || '暂无描述',
+        fileType: (work as any).fileType || '未知类型'
+      })) as unknown as OtherResource[];
+      
+      setResources(otherResources);
+      setFilteredResources(otherResources);
+    } catch (error) {
+      message.error('加载资源失败');
+    } finally {
+      setIsLoading(false);
+    }
+  };
+  
+  loadResources();
+}, []);
+
+  // 筛选逻辑
+  useEffect(() => {
+    if (!searchQuery) {
+      setFilteredResources(resources);
+      return;
+    }
+    
+    const filtered = resources.filter(resource => 
+      resource.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
+      resource.description.toLowerCase().includes(searchQuery.toLowerCase())
+    );
+    
+    setFilteredResources(filtered);
+  }, [searchQuery, resources]);
+
+    // 在加载数据部分添加虚拟数据
+  useEffect(() => {
+    const loadMovies = async () => {
+      try {
+        // 模拟API请求
+        const mockMovies: MovieResource[] = [
+          {
+            id: 3,
+            title: '盗梦空间',
+            director: '克里斯托弗·诺兰',
+            categoryId: 2,
+            categoryName: '科幻电影',
+            resolution: '1080p',
+            duration: '148分钟',
+            actors: ['莱昂纳多·迪卡普里奥', '约瑟夫·高登-莱维特'],
+            seeders: 215,
+            leechers: 42,
+            uploadDate: '2023-06-10',
+            uploader: 'movieFan',
+            downloadCount: 3256,
+            hash: 'a1b2c3d4e5f6',
+            size: '123Mb'
+          },
+          {
+            id: 4,
+            title: '肖申克的救赎',
+            director: '弗兰克·德拉邦特',
+            categoryId: 2,
+            categoryName: '剧情片',
+            resolution: '4K',
+            duration: '142分钟',
+            actors: ['蒂姆·罗宾斯', '摩根·弗里曼'],
+            seeders: 189,
+            leechers: 28,
+            uploadDate: '2023-05-15',
+            uploader: 'classicMovie',
+            downloadCount: 4123,
+            hash: 'g7h8i9j0k1l2',
+            size: '123Mb'
+          }
+        ];
+
+        setMovies(mockMovies);
+        setFilteredMovies(mockMovies);
+      } catch (error) {
+        message.error('加载影视资源失败');
+      } finally {
+        setIsLoading(false);
+      }
+    };
+    
+    loadMovies();
+  }, []);
+
+  return (
+    <div className="other-category-container">
+      <div className="category-header">
+        <h1><AppstoreOutlined /> 其他资源分区</h1>
+        <p>软件、电子书等其他类型资源</p>
+      </div>
+      
+      <Search
+        placeholder="搜索资源名称或描述"
+        allowClear
+        enterButton={<SearchOutlined />}
+        value={searchQuery}
+        onChange={e => setSearchQuery(e.target.value)}
+        className="search-bar"
+      />
+      
+      {isLoading ? (
+        <Spin tip="加载资源..." size="large" />
+      ) : (
+        <List
+          grid={{ gutter: 16, column: 1 }}
+          dataSource={filteredResources}
+          renderItem={resource => (
+            <List.Item>
+              <OtherResourceCard resource={resource} />
+            </List.Item>
+          )}
+        />
+      )}
+    </div>
+  );
+};
+
+const OtherResourceCard: React.FC<{ resource: OtherResource }> = ({ resource }) => {
+  const navigate = useNavigate();
+  
+  const handleClick = () => {
+    navigate(`/works/${resource.id}`);
+  };
+
+  return (
+    <Card className="other-resource-card" onClick={handleClick} hoverable>
+      <div className="card-content">
+        <div className="card-header">
+          <Text strong className="resource-title">{resource.title}</Text>
+          <Text type="secondary" className="resource-type">类型: {resource.fileType}</Text>
+        </div>
+        
+        <div className="card-meta">
+          <Tag color="geekblue">{resource.categoryName}</Tag>
+          <Tag color="green">{resource.seeders} 做种</Tag>
+          <Tag color="orange">{resource.leechers} 下载</Tag>
+        </div>
+      </div>
+    </Card>
+  );
+};
+
+export default OtherCategory;
+
+function setMovies(_mockMovies: MovieResource[]) {
+  throw new Error('Function not implemented.');
+}
+
+
+function setFilteredMovies(_mockMovies: MovieResource[]) {
+  throw new Error('Function not implemented.');
+}
diff --git a/src/feature/home/Home.tsx b/src/feature/home/Home.tsx
index fc2c982..80e44eb 100644
--- a/src/feature/home/Home.tsx
+++ b/src/feature/home/Home.tsx
@@ -1,12 +1,116 @@
+import { NavLink } from 'react-router';
+import filmImg from '../../assets/categories/film.jpg';
+import musicImg from '../../assets/categories/music.jpg';
+import gameImg from '../../assets/categories/game.jpg';
+import otherImg from '../../assets/categories/other.jpg';
+// 假设你有排行榜数据,这里模拟一下,实际根据接口等替换
+const filmRankList = [
+  { id: 1, name: '影视作品1', view: '1.2万' },
+  { id: 2, name: '影视作品2', view: '1.1万' },
+  { id: 3, name: '影视作品3', view: '1.0万' },
+];
+const musicRankList = [
+  { id: 1, name: '音乐作品1', view: '1.5万' },
+  { id: 2, name: '音乐作品2', view: '1.3万' },
+  { id: 3, name: '音乐作品3', view: '1.2万' },
+];
+const gameRankList = [
+  { id: 1, name: '游戏作品1', view: '2.0万' },
+  { id: 2, name: '游戏作品2', view: '1.8万' },
+  { id: 3, name: '游戏作品3', view: '1.6万' },
+];
+const otherRankList = [
+  { id: 1, name: '其他作品1', view: '0.8万' },
+  { id: 2, name: '其他作品2', view: '0.7万' },
+  { id: 3, name: '其他作品3', view: '0.6万' },
+];
+
 function Home() {
+  const categories = [
+    { id: 'film', name: '影视', image: filmImg, rankList: filmRankList },
+    { id: 'music', name: '音乐', image: musicImg, rankList: musicRankList },
+    { id: 'game', name: '游戏', image: gameImg, rankList: gameRankList },
+    { id: 'other', name: '其他', image: otherImg, rankList: otherRankList },
+  ];
 
   return (
-    <>
-      主页
-    </>
-  )
+    <div className="max-w-7xl mx-auto px-4 py-16 flex flex-col items-center">
+      {/* 页面标题 */}
+      <h1 className="text-[clamp(2rem,5vw,3.5rem)] font-bold text-center mb-12 text-gray-800 tracking-tight">
+        欢迎访问创意协作平台
+      </h1>
+
+      {/* 分区 + 排行榜 容器,改为 flex 布局 */}
+      <div className="w-full flex max-w-6xl mx-auto">
+        {/* 分区卡片容器 - 响应式网格布局 */}
+        <div className="grid grid-cols-4 gap-6 w-3/4">
+          {categories.map((category) => (
+            <div 
+              key={category.id}
+              className="group relative overflow-hidden rounded-xl shadow-lg transition-all duration-300 hover:-translate-y-2 hover:shadow-xl"
+            >
+              <NavLink to={`/categories/${category.id}`} className="block">
+                {/* 图片容器 */}
+                <div className="aspect-[4/3] relative overflow-hidden">
+                  {/* 背景图片 - 带模糊和亮度调整 */}
+                  <img 
+                    src={category.image}
+                    alt={category.name}
+                    className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
+                    style={{ filter: 'blur(1px) brightness(0.85)' }}
+                  />
+
+                  {/* 渐变遮罩层 - 增强文字可读性 */}
+                  <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent z-10"></div>
+
+                  {/* 文字内容 */}
+                  <div className="absolute inset-0 flex flex-col items-center justify-center p-4 z-20">
+                    {/* 分类标题 - 艺术字体 */}
+                    <h2 className="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-white text-center mb-2" style={{
+                      fontFamily: '"Playfair Display", serif',
+                      textShadow: '0 4px 12px rgba(0,0,0,0.8)',
+                      transform: 'translateY(10px)',
+                      transition: 'transform 0.3s ease-out',
+                      letterSpacing: '2px',
+                      borderBottom: '2px solid rgba(255,255,255,0.6)',
+                      paddingBottom: '4px'
+                    }}>
+                      {category.name}
+                    </h2>
+
+                    {/* 描述文本 - 悬停显示 */}
+                    <p className="text-indigo-100 text-center opacity-0 transform translate-y-4 transition-all duration-300 group-hover:opacity-100 group-hover:translate-y-0 mt-2" style={{
+                      fontFamily: '"Merriweather", serif',
+                      textShadow: '0 2px 6px rgba(0,0,0,0.6)'
+                    }}>
+                      探索{category.name}创意项目
+                    </p>
+                  </div>
+                </div>
+              </NavLink>
+            </div>
+          ))}
+        </div>
+
+        {/* 排行榜区域 - 右侧 */}
+        <div className="w-1/4 pl-6">
+          {categories.map((category) => (
+            <div key={category.id} className="mb-8">
+              <h3 className="text-xl font-bold text-gray-800 mb-4">{category.name}排行榜</h3>
+              <ul>
+                {category.rankList.map((item) => (
+                  <li key={item.id} className="flex justify-between items-center mb-2">
+                    <span>{item.name}</span>
+                    <span className="text-gray-600">{item.view}</span>
+                  </li>
+                ))}
+              </ul>
+            </div>
+          ))}
+        </div>
+      </div>
+    </div>
+  );
 }
 
-
-
-export default Home
+export default Home;
\ No newline at end of file
diff --git a/src/feature/work/WorkPage.css b/src/feature/work/WorkPage.css
new file mode 100644
index 0000000..14b815a
--- /dev/null
+++ b/src/feature/work/WorkPage.css
@@ -0,0 +1,155 @@
+/* 作品页面容器 */
+.work-page-container {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 20px;
+}
+
+/* 返回按钮 */
+.back-button {
+  margin-bottom: 20px;
+}
+
+/* 作品头部 */
+.work-header {
+  margin-bottom: 20px;
+}
+
+.work-header .ant-typography {
+  margin-bottom: 8px;
+}
+
+.work-meta {
+  margin: 16px 0;
+}
+
+.like-button {
+  margin-top: 10px;
+}
+
+/* 内容区域 */
+.work-content {
+  padding: 20px;
+  background: #f9f9f9;
+  border-radius: 4px;
+}
+
+/* Bug反馈区域 */
+.bug-report-section {
+  margin-top: 20px;
+}
+
+.bug-item {
+  width: 100%;
+  padding: 15px;
+  border: 1px solid #f0f0f0;
+  border-radius: 4px;
+  margin-bottom: 10px;
+}
+
+.bug-header {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 8px;
+}
+
+.bug-content {
+  margin: 10px 0;
+}
+
+/* 讨论区 */
+.discussion-section {
+  margin-top: 20px;
+}
+
+.discussion-container {
+  display: flex;
+  gap: 20px;
+}
+
+.discussion-list {
+  width: 300px;
+  border-right: 1px solid #f0f0f0;
+  padding-right: 20px;
+}
+
+.discussion-detail {
+  flex: 1;
+}
+
+.discussion-item {
+  padding: 15px;
+  cursor: pointer;
+  border-radius: 4px;
+  margin-bottom: 10px;
+  transition: all 0.3s;
+}
+
+.discussion-item:hover {
+  background: #f5f5f5;
+}
+
+.discussion-item.active {
+  background: #e6f7ff;
+  border-left: 3px solid #1890ff;
+}
+
+.discussion-detail-content {
+  padding: 20px;
+  background: #f9f9f9;
+  border-radius: 4px;
+  margin-bottom: 20px;
+}
+
+.discussion-title {
+  font-size: 18px;
+  display: block;
+  margin-bottom: 10px;
+}
+
+.discussion-meta {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 15px;
+}
+
+.discussion-body {
+  margin-bottom: 20px;
+}
+
+.comment-item {
+  padding: 15px 0;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.comment-header {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 10px;
+}
+
+.comment-actions {
+  margin-top: 10px;
+}
+
+.new-discussion-btn {
+  margin-bottom: 20px;
+}
+
+/* 响应式调整 */
+@media (max-width: 768px) {
+  .discussion-container {
+    flex-direction: column;
+  }
+  
+  .discussion-list {
+    width: 100%;
+    border-right: none;
+    padding-right: 0;
+    border-bottom: 1px solid #f0f0f0;
+    padding-bottom: 20px;
+    margin-bottom: 20px;
+  }
+}
\ No newline at end of file
diff --git a/src/feature/work/WorkPage.tsx b/src/feature/work/WorkPage.tsx
new file mode 100644
index 0000000..3a108a7
--- /dev/null
+++ b/src/feature/work/WorkPage.tsx
@@ -0,0 +1,131 @@
+import React, { useState, useEffect } from 'react';
+import { useParams, useNavigate } from 'react-router';
+import { Button, Tabs, message, Spin, Tag, Typography, Space, Divider } from 'antd';
+import { ArrowLeftOutlined, LikeOutlined, BugOutlined, CommentOutlined } from '@ant-design/icons';
+import WorkAPI from '../../api/workApi';
+import BugReportSection from '../../components/BugReportSection';
+import DiscussionSection from '../../components/DiscussionSection';
+import type { Work } from '../../api/otherType';
+
+const { Title, Text, Paragraph } = Typography;
+const { TabPane } = Tabs;
+
+const WorkPage: React.FC = () => {
+  const { id } = useParams<{ id: string }>();
+  const navigate = useNavigate();
+  const [work, setWork] = useState<Work | null>(null);
+  const [loading, setLoading] = useState(true);
+  const [activeTab, setActiveTab] = useState('details');
+
+  // 加载作品数据
+  useEffect(() => {
+    const loadWork = async () => {
+      try {
+        const workData = await WorkAPI.getWorkById(Number(id));
+        setWork(workData);
+      } catch (error) {
+        message.error('加载作品失败');
+        navigate('/');
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    loadWork();
+  }, [id, navigate]);
+
+  // 点赞处理
+  const handleLike = async () => {
+    try {
+      await WorkAPI.likeWork(Number(id));
+      setWork(prev => prev ? { ...prev, likes: prev.likes + 1 } : null);
+      message.success('点赞成功');
+    } catch (error) {
+      message.error('点赞失败');
+    }
+  };
+
+  if (loading) {
+    return <Spin size="large" className="center-spinner" />;
+  }
+
+  if (!work) {
+    return <div>作品不存在</div>;
+  }
+
+  return (
+    <div className="work-page-container">
+      <Button 
+        type="text" 
+        icon={<ArrowLeftOutlined />} 
+        onClick={() => navigate(-1)}
+        className="back-button"
+      >
+        返回
+      </Button>
+
+      <div className="work-header">
+        <Title level={2}>{work.title}</Title>
+        <Text type="secondary">作者: {work.author}</Text>
+        
+        <div className="work-meta">
+          <Space size="middle">
+            <Tag color="blue">{work.categoryName}</Tag>
+            <Text>浏览: {work.views}</Text>
+            <Text>点赞: {work.likes}</Text>
+            <Text>上传时间: {new Date(work.createTime).toLocaleDateString()}</Text>
+          </Space>
+        </div>
+
+        <Button 
+          type="primary" 
+          icon={<LikeOutlined />} 
+          onClick={handleLike}
+          className="like-button"
+        >
+          点赞
+        </Button>
+      </div>
+
+      <Divider />
+
+      <Tabs activeKey={activeTab} onChange={setActiveTab}>
+        <TabPane tab="作品详情" key="details">
+          <div className="work-content">
+            <Title level={4}>作品描述</Title>
+            <Paragraph>{work.description}</Paragraph>
+            
+            <Title level={4}>作品内容</Title>
+            <div className="content-container">
+              {work.content}
+            </div>
+          </div>
+        </TabPane>
+
+        <TabPane 
+          tab={
+            <span>
+              <BugOutlined /> Bug反馈
+            </span>
+          } 
+          key="bugs"
+        >
+          <BugReportSection workId={work.id} />
+        </TabPane>
+
+        <TabPane 
+          tab={
+            <span>
+              <CommentOutlined /> 交流区
+            </span>
+          } 
+          key="discussions"
+        >
+          <DiscussionSection workId={work.id} />
+        </TabPane>
+      </Tabs>
+    </div>
+  );
+};
+
+export default WorkPage;
\ No newline at end of file
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index 3bb3135..401b6bb 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -15,6 +15,12 @@
 const ProtectedHome = withProtect(Home);
 const ProtectedWork = withProtect(Work);
 const ProtectedCreatWork = withProtect(CreateWork)
+import FilmCategory from '../feature/categories/MovieCategory';
+import MusicCategory from '../feature/categories/MusicCategory';
+import GameCategory from '../feature/categories/GameCategory';
+import OtherCategory from '../feature/categories/OtherCategory';
+import WorkPage from '../feature/work/WorkPage';
+
 export default createBrowserRouter([
   {
     path: "/",
@@ -24,6 +30,11 @@
         path: "/",
         Component: ProtectedHome,
       },
+        {path: 'categories/film', Component: FilmCategory,},
+        {path: 'categories/music', Component: MusicCategory,},
+        {path: 'categories/game', Component: GameCategory,},
+        {path: 'categories/other', Component: OtherCategory,},
+        {path: '/works/:id', Component: WorkPage, },
       {
         Component: AuthLayout,
         children: [
diff --git a/src/test/category/GameCategory.test.tsx b/src/test/category/GameCategory.test.tsx
new file mode 100644
index 0000000..ad42353
--- /dev/null
+++ b/src/test/category/GameCategory.test.tsx
@@ -0,0 +1,33 @@
+import { render, screen } from '@testing-library/react';
+import GameCategory from '../../feature/categories/GameCategory';
+import { Provider } from 'react-redux';
+import { store } from '../../store/store';
+import { MemoryRouter } from 'react-router';
+
+describe('GameCategory Component', () => {
+  it('renders game category page with search and game cards', () => {
+    render(
+      <MemoryRouter>
+        <Provider store={store}>
+          <GameCategory />
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 验证标题和描述
+    expect(screen.getByText('游戏资源分区')).toBeInTheDocument();
+    expect(screen.getByText('PC、主机游戏资源分享')).toBeInTheDocument();
+    
+    // 验证搜索框
+    const searchInput = screen.getByPlaceholderText('搜索游戏名称或开发商');
+    expect(searchInput).toBeInTheDocument();
+    
+    // 验证游戏卡片
+    expect(screen.getByText('赛博朋克2077')).toBeInTheDocument();
+    expect(screen.getByText('艾尔登法环')).toBeInTheDocument();
+    
+    // 验证开发商信息
+    expect(screen.getByText('开发商: CD Projekt Red')).toBeInTheDocument();
+    expect(screen.getByText('开发商: FromSoftware')).toBeInTheDocument();
+  });
+});
\ No newline at end of file
diff --git a/src/test/category/MovieCategory.test.tsx b/src/test/category/MovieCategory.test.tsx
new file mode 100644
index 0000000..be1ed01
--- /dev/null
+++ b/src/test/category/MovieCategory.test.tsx
@@ -0,0 +1,33 @@
+import { render, screen } from '@testing-library/react';
+import MovieCategory from '../../feature/categories/MovieCategory';
+import { Provider } from 'react-redux';
+import { store } from '../../store/store';
+import { MemoryRouter } from 'react-router';
+
+describe('MovieCategory Component', () => {
+  it('renders movie category page with search and movie cards', () => {
+    render(
+      <MemoryRouter>
+        <Provider store={store}>
+          <MovieCategory />
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 验证标题和描述
+    expect(screen.getByText('影视资源分区')).toBeInTheDocument();
+    expect(screen.getByText('高清电影、电视剧、纪录片资源分享')).toBeInTheDocument();
+    
+    // 验证搜索框
+    const searchInput = screen.getByPlaceholderText('搜索电影或导演');
+    expect(searchInput).toBeInTheDocument();
+    
+    // 验证电影卡片
+    expect(screen.getByText('盗梦空间')).toBeInTheDocument();
+    expect(screen.getByText('肖申克的救赎')).toBeInTheDocument();
+    
+    // 验证导演信息
+    expect(screen.getByText('导演: 克里斯托弗·诺兰')).toBeInTheDocument();
+    expect(screen.getByText('导演: 弗兰克·德拉邦特')).toBeInTheDocument();
+  });
+});
\ No newline at end of file
diff --git a/src/test/category/MusicCategory.test.tsx b/src/test/category/MusicCategory.test.tsx
new file mode 100644
index 0000000..4de1bdf
--- /dev/null
+++ b/src/test/category/MusicCategory.test.tsx
@@ -0,0 +1,38 @@
+import { render, screen } from '@testing-library/react';
+import MusicCategory from '../../feature/categories/MusicCategory';
+import { Provider } from 'react-redux';
+import { store } from '../../store/store';
+import { MemoryRouter } from 'react-router';
+
+describe('MusicCategory Component', () => {
+  it('renders music category page with search, filters and music cards', () => {
+    render(
+      <MemoryRouter>
+        <Provider store={store}>
+          <MusicCategory />
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 验证标题和描述
+    expect(screen.getByText('音乐资源分区')).toBeInTheDocument();
+    expect(screen.getByText('高质量音乐资源共享,保持分享率,共建良好PT环境')).toBeInTheDocument();
+    
+    // 验证搜索框
+    const searchInput = screen.getByPlaceholderText('搜索音乐名称或艺术家');
+    expect(searchInput).toBeInTheDocument();
+    
+    // 验证音质筛选
+    expect(screen.getByText('音质筛选:')).toBeInTheDocument();
+    expect(screen.getByText('全部')).toBeInTheDocument();
+    expect(screen.getByText('FLAC')).toBeInTheDocument();
+    
+    // 验证分类标签
+    expect(screen.getByText('欧美流行 (15)')).toBeInTheDocument();
+    expect(screen.getByText('华语流行 (23)')).toBeInTheDocument();
+    
+    // 验证音乐卡片
+    expect(screen.getByText('Blinding Lights')).toBeInTheDocument();
+    expect(screen.getByText('Bohemian Rhapsody')).toBeInTheDocument();
+  });
+});
\ No newline at end of file
diff --git a/src/test/category/OtherCategory.test.tsx b/src/test/category/OtherCategory.test.tsx
new file mode 100644
index 0000000..dd1e793
--- /dev/null
+++ b/src/test/category/OtherCategory.test.tsx
@@ -0,0 +1,32 @@
+import { render, screen } from '@testing-library/react';
+import OtherCategory from '../../feature/categories/OtherCategory';
+import { Provider } from 'react-redux';
+import { store } from '../../store/store';
+import { MemoryRouter } from 'react-router';
+
+describe('OtherCategory Component', () => {
+  it('renders other resources category page with search and resource cards', () => {
+    render(
+      <MemoryRouter>
+        <Provider store={store}>
+          <OtherCategory />
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 验证标题和描述
+    expect(screen.getByText('其他资源分区')).toBeInTheDocument();
+    expect(screen.getByText('软件、电子书等其他类型资源')).toBeInTheDocument();
+    
+    // 验证搜索框
+    const searchInput = screen.getByPlaceholderText('搜索资源名称或描述');
+    expect(searchInput).toBeInTheDocument();
+    
+    // 验证资源卡片
+    expect(screen.getByText('盗梦空间')).toBeInTheDocument();
+    expect(screen.getByText('肖申克的救赎')).toBeInTheDocument();
+    
+    // 验证类型信息
+    expect(screen.getByText('类型: 未知类型')).toBeInTheDocument();
+  });
+});
\ No newline at end of file
diff --git a/src/test/work/WorkPage.test.tsx b/src/test/work/WorkPage.test.tsx
new file mode 100644
index 0000000..4dc5036
--- /dev/null
+++ b/src/test/work/WorkPage.test.tsx
@@ -0,0 +1,268 @@
+import { render, screen } from '@testing-library/react';
+import WorkPage from '../../feature/work/WorkPage';
+import { Provider } from 'react-redux';
+import { store } from '../../store/store';
+import { MemoryRouter, Route, Routes, useNavigate } from 'react-router';
+import { act } from 'react-dom/test-utils';
+import WorkAPI from '../../api/workApi';
+import type { Work } from '../../api/otherType';
+import '@testing-library/jest-dom';
+
+// 模拟整个WorkAPI类
+jest.mock('../../api/workApi', () => {
+  return {
+    __esModule: true,
+    default: {
+      getWorkById: jest.fn(),
+      likeWork: jest.fn(),
+      // 添加其他可能用到的方法
+      getBugReports: jest.fn(),
+      getDiscussions: jest.fn()
+    }
+  }
+});
+
+// 模拟子组件
+jest.mock('../../components/BugReportSection', () => () => (
+  <div data-testid="bug-report-mock">BugReportSection Mock</div>
+));
+
+jest.mock('../../components/DiscussionSection', () => () => (
+  <div data-testid="discussion-mock">DiscussionSection Mock</div>
+));
+
+// 模拟react-router-dom的useNavigate
+jest.mock('react-router-dom', () => ({
+  ...jest.requireActual('react-router-dom'),
+  useNavigate: () => jest.fn()
+}));
+
+describe('WorkPage Component', () => {
+  const mockWork: Work = {
+      id: 1,
+      title: '测试作品',
+      author: '测试作者',
+      categoryName: '测试分类',
+      views: 100,
+      likes: 50,
+      createTime: '2023-01-01T00:00:00Z',
+      description: '测试描述',
+      content: '测试内容',
+      size: '',
+      data: function (): unknown {
+          throw new Error('Function not implemented.');
+      },
+      artist: '',
+      quality: '',
+      genre: [],
+      authorId: 0,
+      categoryId: 0,
+      coverUrl: '',
+      attachments: [],
+      bugCount: 0,
+      discussionCount: 0
+  };
+
+  beforeEach(() => {
+    // 重置所有模拟
+    jest.clearAllMocks();
+    
+    // 设置默认模拟实现
+    (WorkAPI.getWorkById as jest.Mock).mockResolvedValue(mockWork);
+    (WorkAPI.likeWork as jest.Mock).mockResolvedValue({});
+  });
+
+  it('renders loading spinner initially', async () => {
+    // 延迟API响应以测试加载状态
+    let resolvePromise: (value: Work) => void;
+    (WorkAPI.getWorkById as jest.Mock).mockImplementation(
+      () => new Promise<Work>((resolve) => {
+        resolvePromise = resolve;
+      })
+    );
+
+    render(
+      <MemoryRouter initialEntries={['/works/1']}>
+        <Provider store={store}>
+          <Routes>
+            <Route path="/works/:id" element={<WorkPage />} />
+          </Routes>
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 验证加载状态
+    expect(screen.getByRole('status')).toBeInTheDocument();
+    expect(screen.getByText(/加载/)).toBeInTheDocument();
+    
+    // 完成API请求
+    await act(async () => {
+      resolvePromise(mockWork);
+    });
+
+    // 验证加载完成后内容
+    expect(await screen.findByText('测试作品')).toBeInTheDocument();
+  });
+
+  it('renders work details after loading', async () => {
+    render(
+      <MemoryRouter initialEntries={['/works/1']}>
+        <Provider store={store}>
+          <Routes>
+            <Route path="/works/:id" element={<WorkPage />} />
+          </Routes>
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 等待数据加载完成
+    expect(await screen.findByText('测试作品')).toBeInTheDocument();
+    
+    // 验证头部信息
+    expect(screen.getByText('作者: 测试作者')).toBeInTheDocument();
+    expect(screen.getByText('测试分类')).toHaveClass('ant-tag');
+    expect(screen.getByText('浏览: 100')).toBeInTheDocument();
+    expect(screen.getByText('点赞: 50')).toBeInTheDocument();
+    expect(screen.getByText(/2023/)).toBeInTheDocument();
+    
+    // 验证作品内容
+    expect(screen.getByRole('heading', { level: 4, name: '作品描述' })).toBeInTheDocument();
+    expect(screen.getByText('测试描述')).toBeInTheDocument();
+    expect(screen.getByRole('heading', { level: 4, name: '作品内容' })).toBeInTheDocument();
+    expect(screen.getByText('测试内容')).toBeInTheDocument();
+    
+    // 验证标签页
+    expect(screen.getByRole('tab', { name: '作品详情' })).toBeInTheDocument();
+    expect(screen.getByRole('tab', { name: /Bug反馈/ })).toBeInTheDocument();
+    expect(screen.getByRole('tab', { name: /交流区/ })).toBeInTheDocument();
+  });
+
+  it('handles back button click', async () => {
+    const mockNavigate = jest.fn();
+    (useNavigate as jest.Mock).mockReturnValue(mockNavigate);
+
+    render(
+      <MemoryRouter initialEntries={['/works/1']}>
+        <Provider store={store}>
+          <Routes>
+            <Route path="/works/:id" element={<WorkPage />} />
+          </Routes>
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 等待数据加载完成
+    await screen.findByText('测试作品');
+    
+    // 点击返回按钮
+    const backButton = screen.getByRole('button', { name: '返回' });
+    await act(async () => {
+      backButton.click();
+    });
+    
+    // 验证导航被调用
+    expect(mockNavigate).toHaveBeenCalledWith(-1);
+  });
+
+  it('handles like button click', async () => {
+    render(
+      <MemoryRouter initialEntries={['/works/1']}>
+        <Provider store={store}>
+          <Routes>
+            <Route path="/works/:id" element={<WorkPage />} />
+          </Routes>
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 等待数据加载完成
+    await screen.findByText('测试作品');
+    
+    // 点击点赞按钮
+    const likeButton = screen.getByRole('button', { name: '点赞' });
+    await act(async () => {
+      likeButton.click();
+    });
+    
+    // 验证API调用
+    expect(WorkAPI.likeWork).toHaveBeenCalledTimes(1);
+    expect(WorkAPI.likeWork).toHaveBeenCalledWith(1);
+    
+    // 验证UI反馈
+    expect(await screen.findByText('点赞成功')).toBeInTheDocument();
+  });
+
+  it('shows error message when work loading fails', async () => {
+    const errorMessage = '加载失败';
+    (WorkAPI.getWorkById as jest.Mock).mockRejectedValue(new Error(errorMessage));
+
+    const mockNavigate = jest.fn();
+    (useNavigate as jest.Mock).mockReturnValue(mockNavigate);
+
+    render(
+      <MemoryRouter initialEntries={['/works/1']}>
+        <Provider store={store}>
+          <Routes>
+            <Route path="/works/:id" element={<WorkPage />} />
+          </Routes>
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 验证错误消息
+    expect(await screen.findByText('作品不存在')).toBeInTheDocument();
+    
+    // 验证导航到首页
+    expect(mockNavigate).toHaveBeenCalledWith('/');
+  });
+
+  it('switches between tabs correctly', async () => {
+    render(
+      <MemoryRouter initialEntries={['/works/1']}>
+        <Provider store={store}>
+          <Routes>
+            <Route path="/works/:id" element={<WorkPage />} />
+          </Routes>
+        </Provider>
+      </MemoryRouter>
+    );
+
+    // 等待数据加载完成
+    await screen.findByText('测试作品');
+    
+    // 初始显示作品详情
+    expect(screen.getByText('测试描述')).toBeInTheDocument();
+    expect(screen.queryByTestId('bug-report-mock')).not.toBeInTheDocument();
+    expect(screen.queryByTestId('discussion-mock')).not.toBeInTheDocument();
+    
+    // 点击Bug反馈标签
+    const bugTab = screen.getByRole('tab', { name: /Bug反馈/ });
+    await act(async () => {
+      bugTab.click();
+    });
+    
+    // 验证Bug反馈内容显示
+    expect(screen.queryByText('测试描述')).not.toBeInTheDocument();
+    expect(screen.getByTestId('bug-report-mock')).toBeInTheDocument();
+    
+    // 点击交流区标签
+    const discussionTab = screen.getByRole('tab', { name: /交流区/ });
+    await act(async () => {
+      discussionTab.click();
+    });
+    
+    // 验证交流区内容显示
+    expect(screen.queryByTestId('bug-report-mock')).not.toBeInTheDocument();
+    expect(screen.getByTestId('discussion-mock')).toBeInTheDocument();
+    
+    // 切换回作品详情
+    const detailsTab = screen.getByRole('tab', { name: '作品详情' });
+    await act(async () => {
+      detailsTab.click();
+    });
+    
+    // 验证作品详情再次显示
+    expect(screen.getByText('测试描述')).toBeInTheDocument();
+    expect(screen.queryByTestId('discussion-mock')).not.toBeInTheDocument();
+  });
+});
\ No newline at end of file