论坛,聊天室前后端对接

Change-Id: I90740329ab40dc050e8a791a382ab187900d673a
diff --git a/src/api/forum.js b/src/api/forum.js
new file mode 100644
index 0000000..b228024
--- /dev/null
+++ b/src/api/forum.js
@@ -0,0 +1,38 @@
+import request from './request'
+
+export function getAllForums() {
+    return request({
+        url: '/api/forums',
+        method: 'get'
+    })
+}
+
+export function getForumById(id) {
+    return request({
+        url: `/api/forums/${id}`,
+        method: 'get'
+    })
+}
+
+export function createForum(data) {
+    return request({
+        url: '/api/forums',
+        method: 'post',
+        data
+    })
+}
+
+export function updateForum(id, data) {
+    return request({
+        url: `/api/forums/${id}`,
+        method: 'put',
+        data
+    })
+}
+
+export function deleteForum(id) {
+    return request({
+        url: `/api/forums/${id}`,
+        method: 'delete'
+    })
+} 
\ No newline at end of file
diff --git a/src/api/forumTag.js b/src/api/forumTag.js
new file mode 100644
index 0000000..a63895e
--- /dev/null
+++ b/src/api/forumTag.js
@@ -0,0 +1,38 @@
+import request from './request'
+
+export function getAllTags() {
+    return request({
+        url: '/api/tags',
+        method: 'get'
+    })
+}
+
+export function createTag(data) {
+    return request({
+        url: '/api/tags',
+        method: 'post',
+        data
+    })
+}
+
+export function deleteTag(tagId) {
+    return request({
+        url: `/api/tags/${tagId}`,
+        method: 'delete'
+    })
+}
+
+export function getTopicTags(topicId) {
+    return request({
+        url: `/api/tags/topics/${topicId}`,
+        method: 'get'
+    })
+}
+
+export function assignTagsToTopic(topicId, tagNames) {
+    return request({
+        url: `/api/tags/topics/${topicId}/assign`,
+        method: 'post',
+        data: tagNames
+    })
+} 
\ No newline at end of file
diff --git a/src/api/post.js b/src/api/post.js
new file mode 100644
index 0000000..c49293b
--- /dev/null
+++ b/src/api/post.js
@@ -0,0 +1,52 @@
+import request from './request'
+
+export function getPostsByTopic(topicId) {
+    return request({
+        url: `/api/posts/topic/${topicId}`,
+        method: 'get'
+    })
+}
+
+export function getPostTreeByTopic(topicId) {
+    return request({
+        url: `/api/posts/topic/${topicId}/tree`,
+        method: 'get'
+    })
+}
+
+export function createPost(data) {
+    return request({
+        url: '/api/posts',
+        method: 'post',
+        data
+    })
+}
+
+export function getPostById(id) {
+    return request({
+        url: `/api/posts/${id}`,
+        method: 'get'
+    })
+}
+
+export function updatePost(id, data) {
+    return request({
+        url: `/api/posts/${id}`,
+        method: 'put',
+        data
+    })
+}
+
+export function deletePost(id) {
+    return request({
+        url: `/api/posts/${id}`,
+        method: 'delete'
+    })
+}
+
+export function getUserPosts(userId) {
+    return request({
+        url: `/api/posts/user/${userId}`,
+        method: 'get'
+    })
+} 
\ No newline at end of file
diff --git a/src/api/postLike.js b/src/api/postLike.js
new file mode 100644
index 0000000..9ad674a
--- /dev/null
+++ b/src/api/postLike.js
@@ -0,0 +1,22 @@
+import request from './request'
+
+export function likePost(postId, userId) {
+    return request({
+        url: `/api/post-likes/${postId}/like/${userId}`,
+        method: 'post'
+    })
+}
+
+export function unlikePost(postId, userId) {
+    return request({
+        url: `/api/post-likes/${postId}/unlike/${userId}`,
+        method: 'delete'
+    })
+}
+
+export function getPostLikeCount(postId) {
+    return request({
+        url: `/api/post-likes/${postId}/count`,
+        method: 'get'
+    })
+} 
\ No newline at end of file
diff --git a/src/api/posts.js b/src/api/posts.js
new file mode 100644
index 0000000..01a347e
--- /dev/null
+++ b/src/api/posts.js
@@ -0,0 +1,7 @@
+import request from './request';
+
+export const getTopicDetail = (id) => request.get(` /posts/${id}`);
+export const getTopicPosts = (topicId, page = 1, size = 20) => request.get(`/posts/topic/${topicId}`, { params: { page, size } });
+export const createPost = (data) => request.post('/posts', data);
+export const updatePost = (id, data) => request.put(`/posts/${id}`, data);
+export const deletePost = (id) => request.delete(`/posts/${id}`);
\ No newline at end of file
diff --git a/src/api/request.js b/src/api/request.js
index 0e06f0a..01da8a3 100644
--- a/src/api/request.js
+++ b/src/api/request.js
@@ -5,7 +5,7 @@
 const request = axios.create({

   // 关键:不要设置baseURL,或者设置为空字符串

   // 这样请求会发送到当前域名(8080),然后被代理转发到8081

-  baseURL: process.env.VUE_APP_BASE_API || '/api',

+  baseURL: process.env.VUE_APP_BASE_API || '',

   timeout: 10000,

   headers: {

     // 'Content-Type': 'application/json'

@@ -15,15 +15,27 @@
 request.interceptors.request.use(

   config => {

     const token = localStorage.getItem('token')

-    console.log('📤 添加 token 到请求头:', token)

+    console.log('📤 Token长度:', token ? token.length : 0)

+    console.log('📤 Token前50字符:', token ? token.substring(0, 50) + '...' : 'null')

+    

     if (token) {

+      // 检查token是否过大

+      if (token.length > 8000) {

+        console.warn('⚠️ Token过大,长度:', token.length)

+        // 清除过大的token

+        localStorage.removeItem('token')

+        localStorage.removeItem('tokenInfo')

+        localStorage.removeItem('userInfo')

+        localStorage.removeItem('isLoggedIn')

+        ElMessage.error('登录信息过大,请重新登录')

+        router.push('/login')

+        return Promise.reject(new Error('Token过大'))

+      }

       config.headers.Authorization = `Bearer ${token}`

     }

     

     console.log('🚀 发送请求:', config.method?.toUpperCase(), config.url)

     console.log('📤 请求数据:', config.data)

-    console.log('🔍 VUE_APP_BASE_API:', process.env.VUE_APP_BASE_API)

-    console.log('🔍 VUE_APP_BACKEND:', process.env.VUE_APP_BACKEND)

     

     return config

   },

@@ -59,6 +71,15 @@
           ElMessage.error('API接口不存在')

           console.error('❌ 请求的URL:', error.config.url)

           break

+        case 431:

+          console.error('❌ 请求头太大 (431),可能是Token过大')

+          localStorage.removeItem('token')

+          localStorage.removeItem('tokenInfo')

+          localStorage.removeItem('userInfo')

+          localStorage.removeItem('isLoggedIn')

+          ElMessage.error('登录信息过大,请重新登录')

+          router.push('/login')

+          break

         case 500:

           ElMessage.error('服务器内部错误')

           break

diff --git a/src/api/subscription.js b/src/api/subscription.js
new file mode 100644
index 0000000..0090fa4
--- /dev/null
+++ b/src/api/subscription.js
@@ -0,0 +1,32 @@
+import request from './request'
+
+export function subscribeTopic(data) {
+    return request({
+        url: '/api/subscriptions/subscribe',
+        method: 'post',
+        data
+    })
+}
+
+export function unsubscribeTopic(data) {
+    return request({
+        url: '/api/subscriptions/unsubscribe',
+        method: 'post',
+        data
+    })
+}
+
+export function checkSubscription(topicId) {
+    return request({
+        url: '/api/subscriptions/check',
+        method: 'get',
+        params: { topicId }
+    })
+}
+
+export function getSubscriptionList() {
+    return request({
+        url: '/api/subscriptions/list',
+        method: 'get'
+    })
+} 
\ No newline at end of file
diff --git a/src/api/topic.js b/src/api/topic.js
new file mode 100644
index 0000000..629b71f
--- /dev/null
+++ b/src/api/topic.js
@@ -0,0 +1,53 @@
+import request from './request'
+
+export function getAllTopics() {
+    return request({
+        url: '/api/topics',
+        method: 'get'
+    })
+}
+
+export function getTopicById(id) {
+    return request({
+        url: `/api/topics/${id}`,
+        method: 'get'
+    })
+}
+
+export function getTopicsByForum(forumId) {
+    return request({
+        url: `/api/topics/forum/${forumId}`,
+        method: 'get'
+    })
+}
+
+export function createTopic(data) {
+    return request({
+        url: '/api/topics',
+        method: 'post',
+        data
+    })
+}
+
+export function updateTopic(id, data) {
+    return request({
+        url: `/api/topics/${id}`,
+        method: 'put',
+        data
+    })
+}
+
+export function deleteTopic(id) {
+    return request({
+        url: `/api/topics/${id}`,
+        method: 'delete'
+    })
+}
+
+export function searchTopics(query, forumId, page = 0, size = 10) {
+    return request({
+        url: '/api/topics/search',
+        method: 'get',
+        params: { q: query, forumId, page, size }
+    })
+}
diff --git a/src/api/topicView.js b/src/api/topicView.js
new file mode 100644
index 0000000..f4410e1
--- /dev/null
+++ b/src/api/topicView.js
@@ -0,0 +1,16 @@
+import request from './request'
+
+export function recordTopicView(params) {
+    return request({
+        url: '/topic-views/record',
+        method: 'post',
+        params
+    })
+}
+
+export function getUserViewHistory() {
+    return request({
+        url: '/topic-views',
+        method: 'get'
+    })
+} 
\ No newline at end of file
diff --git a/src/api/user.js b/src/api/user.js
index 2cfb40c..32073b1 100644
--- a/src/api/user.js
+++ b/src/api/user.js
@@ -7,7 +7,18 @@
    */

   getCurrentUser() {

     return request({

-      url: '/user/profile',

+      url: '/auth/status',

+      method: 'get'

+    })

+  },

+

+  /**

+   * 获取用户分享率和统计信息

+   * @returns {Promise<Object>} 分享率统计信息

+   */

+  getUserRatioInfo() {

+    return request({

+      url: '/torrent/ratio/info',

       method: 'get'

     })

   },

diff --git a/src/api/userHistory.js b/src/api/userHistory.js
new file mode 100644
index 0000000..2853d92
--- /dev/null
+++ b/src/api/userHistory.js
@@ -0,0 +1,15 @@
+import request from './request'
+
+export function getUserHistory(userId) {
+    return request({
+        url: `/api/history/${userId}`,
+        method: 'get'
+    })
+}
+
+export function getUserHistoryByType(userId, type) {
+    return request({
+        url: `/api/history/${userId}/${type}`,
+        method: 'get'
+    })
+} 
\ No newline at end of file
diff --git a/src/components/ReplyTree.vue b/src/components/ReplyTree.vue
new file mode 100644
index 0000000..17620b0
--- /dev/null
+++ b/src/components/ReplyTree.vue
@@ -0,0 +1,144 @@
+<template>
+  <div class="reply-tree">
+    <div
+      v-for="reply in replies"
+      :key="reply.id"
+      class="reply-item"
+      :class="{ 'nested-reply': level > 0 }"
+      :style="{ marginLeft: level * 24 + 'px' }"
+    >
+      <div class="reply-header">
+        <div class="reply-author">
+          <el-avatar :size="Math.max(32 - level * 2, 22)">
+            {{ reply.user?.username?.charAt(0) || 'A' }}
+          </el-avatar>
+          <span class="author-name">{{ reply.user?.username || '匿名' }}</span>
+        </div>
+        <div class="reply-meta">
+          <div class="reply-actions">
+            <el-button 
+              type="text" 
+              :size="level > 0 ? 'small' : 'default'" 
+              @click="$emit('reply', reply)"
+            >
+              回复
+            </el-button>
+            <template v-if="reply.isAuthor">
+              <el-button 
+                type="text" 
+                :size="level > 0 ? 'small' : 'default'" 
+                @click="$emit('edit', reply)"
+              >
+                编辑
+              </el-button>
+              <el-button 
+                type="text" 
+                :size="level > 0 ? 'small' : 'default'" 
+                @click="$emit('delete', reply.id)"
+              >
+                删除
+              </el-button>
+            </template>
+          </div>
+        </div>
+      </div>
+      <div class="reply-content" v-html="reply.content"></div>
+      
+      <!-- 递归显示子回复 -->
+      <ReplyTree
+        v-if="reply.replies?.length"
+        :replies="reply.replies"
+        :level="level + 1"
+        @reply="$emit('reply', $event)"
+        @edit="$emit('edit', $event)"
+        @delete="$emit('delete', $event)"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+
+
+export default {
+  name: 'ReplyTree',
+  props: {
+    replies: {
+      type: Array,
+      required: true
+    },
+    level: {
+      type: Number,
+      default: 0
+    }
+  },
+  emits: ['reply', 'edit', 'delete'],
+  setup() {
+
+
+    return {}
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.reply-tree {
+  .reply-item {
+    padding: 12px 0;
+    border-bottom: 1px solid #f0f0f0;
+    
+    &:last-child {
+      border-bottom: none;
+    }
+    
+    &.nested-reply {
+      border-left: 2px solid #e0e0e0;
+      padding-left: 12px;
+      margin-top: 8px;
+      
+      &:hover {
+        border-left-color: #3498db;
+      }
+    }
+    
+    .reply-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 8px;
+      
+      .reply-author {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        
+        .author-name {
+          font-weight: 500;
+          color: #2c3e50;
+          font-size: 14px;
+        }
+      }
+      
+      .reply-meta {
+        display: flex;
+        align-items: center;
+        gap: 12px;
+        color: #7f8c8d;
+        font-size: 12px;
+        
+        .reply-actions {
+          display: flex;
+          gap: 6px;
+        }
+      }
+    }
+    
+    .reply-content {
+      font-size: 14px;
+      line-height: 1.6;
+      color: #2c3e50;
+      margin-bottom: 8px;
+    }
+  }
+}
+</style> 
\ No newline at end of file
diff --git a/src/router/index.js b/src/router/index.js
index 41af4c6..b2854a4 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -6,6 +6,9 @@
 import LoginView from '@/views/auth/LoginView.vue'

 import RegisterView from '@/views/auth/RegisterView.vue'

 import HomeView from '@/views/HomeView.vue'

+import ForumView from '@/views/forum/ForumView.vue'

+import TopicView from '@/views/forum/TopicView.vue'

+import ChatRoom from '@/views/ChatRoom.vue'

 

 const routes = [

   {

@@ -68,7 +71,7 @@
     }

   },

   {

-    path: '/torrent/:infoHash',

+    path: '/torrent/:id',

     name: 'TorrentDetail',

     component: () => import('@/views/torrent/TorrentDetailView.vue'),

     meta: {

@@ -79,12 +82,8 @@
   // 论坛相关路由

   {

     path: '/forum',

-    name: 'Forum',

-    component: () => import('@/views/forum/ForumView.vue'),

-    meta: {

-      title: 'PT Tracker - 论坛',

-      requiresAuth: true

-    }

+    name: 'forum',

+    component: ForumView

   },

   {

     path: '/forum/section/:id',

@@ -97,10 +96,15 @@
   },

   {

     path: '/forum/topic/:id',

-    name: 'ForumTopic',

-    component: () => import('@/views/forum/ForumTopicView.vue'),

+    name: 'topic',

+    component: TopicView

+  },

+  {

+    path: '/chat',

+    name: 'ChatRoom',

+    component: ChatRoom,

     meta: {

-      title: 'PT Tracker - 主题详情',

+      title: 'PT Tracker - 聊天室',

       requiresAuth: true

     }

   },

diff --git a/src/views/ChatRoom.vue b/src/views/ChatRoom.vue
new file mode 100644
index 0000000..ad63f9d
--- /dev/null
+++ b/src/views/ChatRoom.vue
@@ -0,0 +1,187 @@
+<template>
+  <div class="chat-room">
+    <h2>聊天室</h2>
+    <div class="chat-messages" ref="messagesRef">
+      <div v-for="(msg, idx) in messages" :key="idx" class="chat-message">
+        <span class="chat-user">{{ msg.username || '匿名' }}:</span>
+        <span class="chat-content">{{ msg.content }}</span>
+        <span class="chat-time" v-if="msg.timestamp">({{ formatTime(msg.timestamp) }})</span>
+      </div>
+    </div>
+    <div class="chat-input">
+      <el-input v-model="input" placeholder="输入消息..." @keyup.enter="sendMessage" />
+      <el-button type="primary" @click="sendMessage">发送</el-button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { useStore } from 'vuex'
+import axios from 'axios'
+
+const store = useStore()
+const wsUrl = 'ws://localhost:8081/api/ws/chat'
+const ws = ref(null)
+const messages = ref([])
+const input = ref('')
+const messagesRef = ref(null)
+
+function getToken() {
+  return localStorage.getItem('token')
+}
+
+function setCookie(name, value, days = 7) {
+  const expires = new Date()
+  expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000))
+  document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/`
+}
+
+// 获取聊天历史记录
+async function loadChatHistory() {
+  try {
+    const token = getToken()
+    if (!token) return
+
+    console.log('🔄 获取聊天历史记录...')
+    const response = await axios.get('http://localhost:8081/api/chat/history', {
+      params: { roomId: 1 },
+      headers: { 'sapling-token': token }
+    })
+    
+    if (response.data && Array.isArray(response.data)) {
+      messages.value = response.data
+      console.log('📚 获取到', response.data.length, '条历史消息')
+      
+      // 滚动到底部
+      nextTick(() => {
+        if (messagesRef.value) {
+          messagesRef.value.scrollTop = messagesRef.value.scrollHeight
+        }
+      })
+    }
+  } catch (error) {
+    console.error('❌ 获取聊天历史失败:', error)
+  }
+}
+
+function connect() {
+  const token = getToken()
+  if (!token) {
+    console.error('无法连接WebSocket:未找到token')
+    return
+  }
+
+  // 设置cookie,这样WebSocket握手时后端可以从cookie获取token
+  setCookie('sapling-token', token)
+  
+  console.log('尝试连接WebSocket:', wsUrl)
+  console.log('已设置cookie: sapling-token =', token)
+  
+  ws.value = new WebSocket(wsUrl)
+  
+  ws.value.onopen = async () => {
+    console.log('✅ WebSocket连接成功')
+    // 连接成功后立即获取聊天历史
+    await loadChatHistory()
+  }
+  
+  ws.value.onmessage = (event) => {
+    console.log('📥 收到消息:', event.data)
+    try {
+      const msg = JSON.parse(event.data)
+      console.log('📥 解析后的消息:', msg)
+      messages.value.push(msg)
+      nextTick(() => {
+        if (messagesRef.value) {
+          messagesRef.value.scrollTop = messagesRef.value.scrollHeight
+        }
+      })
+    } catch (e) {
+      console.error('❌ 消息解析失败:', e, event.data)
+    }
+  }
+  
+  ws.value.onerror = (err) => {
+    console.error('❌ WebSocket错误:', err)
+  }
+  
+  ws.value.onclose = (event) => {
+    console.log('❌ WebSocket已断开, 代码:', event.code, '原因:', event.reason)
+    setTimeout(() => {
+      console.log('🔄 尝试重连...')
+      connect()
+    }, 2000)
+  }
+}
+
+function sendMessage() {
+  if (!input.value.trim()) return
+  
+  if (!ws.value || ws.value.readyState !== WebSocket.OPEN) {
+    console.error('❌ WebSocket未连接')
+    return
+  }
+
+  const message = {
+    roomId: 1, // 默认房间ID为1
+    content: input.value.trim()
+  }
+  
+  console.log('📤 发送消息:', message)
+  ws.value.send(JSON.stringify(message))
+  input.value = ''
+}
+
+function formatTime(timestamp) {
+  return new Date(timestamp).toLocaleTimeString()
+}
+
+onMounted(() => {
+  console.log('🚀 聊天室组件已挂载')
+  connect()
+})
+
+onBeforeUnmount(() => {
+  console.log('🔌 组件卸载,关闭WebSocket连接')
+  if (ws.value) {
+    ws.value.close()
+  }
+})
+</script>
+
+<style scoped>
+.chat-room {
+  max-width: 600px;
+  margin: 40px auto;
+  border: 1px solid #eee;
+  border-radius: 8px;
+  padding: 24px;
+  background: #fff;
+}
+.chat-messages {
+  height: 320px;
+  overflow-y: auto;
+  border: 1px solid #f0f0f0;
+  border-radius: 4px;
+  padding: 12px;
+  margin-bottom: 16px;
+  background: #fafbfc;
+}
+.chat-message {
+  margin-bottom: 8px;
+}
+.chat-user {
+  font-weight: bold;
+  color: #409eff;
+}
+.chat-time {
+  color: #999;
+  font-size: 12px;
+  margin-left: 8px;
+}
+.chat-input {
+  display: flex;
+  gap: 8px;
+}
+</style> 
\ No newline at end of file
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index f339463..de56055 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -1,635 +1,640 @@
-<template>

-  <div class="home-page">

-    <!-- 导航栏 -->

-    <Navbar />

-

-    <!-- 主内容区 -->

-    <div class="main-content">

-      <!-- 欢迎卡片 -->

-      <div class="welcome-card">

-        <div class="welcome-header">

-          <h1>欢迎回来,{{ username }}!</h1>

-          <p>当前时间:{{ currentTime }}</p>

-        </div>

-        

-        <!-- 用户统计概览 -->

-        <div class="stats-overview" v-if="userInfo">

-          <div class="stat-item">

-            <div class="stat-icon upload">

-              <el-icon size="24"><Upload /></el-icon>

-            </div>

-            <div class="stat-content">

-              <h3>{{ formatBytes(userInfo.user.uploaded) }}</h3>

-              <p>总上传</p>

-            </div>

-          </div>

-          

-          <div class="stat-item">

-            <div class="stat-icon download">

-              <el-icon size="24"><Download /></el-icon>

-            </div>

-            <div class="stat-content">

-              <h3>{{ formatBytes(userInfo.user.downloaded) }}</h3>

-              <p>总下载</p>

-            </div>

-          </div>

-          

-          <div class="stat-item">

-            <div class="stat-icon ratio">

-              <el-icon size="24"><TrendCharts /></el-icon>

-            </div>

-            <div class="stat-content">

-              <h3>{{ calculateRatio(userInfo.user.uploaded, userInfo.user.downloaded) }}</h3>

-              <p>分享率</p>

-            </div>

-          </div>

-          

-          <div class="stat-item">

-            <div class="stat-icon points">

-              <el-icon size="24"><Star /></el-icon>

-            </div>

-            <div class="stat-content">

-              <h3>{{ userInfo.user.karma || '0' }}</h3>

-              <p>积分</p>

-            </div>

-          </div>

-        </div>

-      </div>

-

-      <!-- 功能快捷入口 -->

-      <div class="quick-actions">

-        <h2>快捷操作</h2>

-        <div class="actions-grid">

-          <div class="action-card" @click="$router.push('/upload')">

-            <el-icon size="32" color="#67c23a"><Upload /></el-icon>

-            <h3>上传种子</h3>

-            <p>分享你的资源</p>

-          </div>

-          

-          <div class="action-card" @click="$router.push('/torrents')">

-            <el-icon size="32" color="#409eff"><Search /></el-icon>

-            <h3>浏览种子</h3>

-            <p>发现新内容</p>

-          </div>

-          

-          <div class="action-card" @click="$router.push('/forum')">

-            <el-icon size="32" color="#e6a23c"><ChatDotRound /></el-icon>

-            <h3>社区论坛</h3>

-            <p>交流讨论</p>

-          </div>

-          

-          <div class="action-card" @click="$router.push('/profile')">

-            <el-icon size="32" color="#f56c6c"><User /></el-icon>

-            <h3>个人中心</h3>

-            <p>管理账户</p>

-          </div>

-        </div>

-      </div>

-

-      <!-- API连接状态测试 -->

-      <div class="api-status-card">

-        <h2>API连接状态</h2>

-        <div class="status-items">

-          <div class="status-item">

-            <el-icon :color="loginStatusColor"><CircleCheck /></el-icon>

-            <span>登录状态:{{ loginStatusText }}</span>

-            <el-button size="small" @click="checkLoginStatus">检查状态</el-button>

-          </div>

-          

-          <div class="status-item">

-            <el-icon :color="userInfoStatusColor"><User /></el-icon>

-            <span>用户信息:{{ userInfoStatusText }}</span>

-            <el-button size="small" @click="refreshUserInfo">刷新信息</el-button>

-          </div>

-        </div>

-        

-        <!-- 用户详细信息展示 -->

-        <div class="user-details" v-if="userInfo">

-          <h3>用户详细信息</h3>

-          <div class="details-grid">

-            <div class="detail-item">

-              <label>用户ID:</label>

-              <span>{{ userInfo.user.id }}</span>

-            </div>

-            <div class="detail-item">

-              <label>用户名:</label>

-              <span>{{ userInfo.user.username }}</span>

-            </div>

-            <div class="detail-item">

-              <label>邮箱:</label>

-              <span>{{ userInfo.user.email }}</span>

-            </div>

-            <div class="detail-item">

-              <label>用户组:</label>

-              <span>{{ userInfo.user.group?.displayName || '未知' }}</span>

-            </div>

-            <div class="detail-item">

-              <label>注册时间:</label>

-              <span>{{ formatDate(userInfo.user.createdAt) }}</span>

-            </div>

-            <div class="detail-item">

-              <label>个性签名:</label>

-              <span>{{ userInfo.user.signature || '这个用户很懒,还没有个性签名' }}</span>

-            </div>

-          </div>

-        </div>

-      </div>

-    </div>

-  </div>

-</template>

-

-<script>

-import Navbar from '@/components/Navbar.vue'

-import { ref, computed, onMounted, onUnmounted } from 'vue'

-import { useRouter } from 'vue-router'

-import { useStore } from 'vuex'

-import { ElMessage, ElMessageBox } from 'element-plus'

-import {

-  ArrowDown,

-  User,

-  Setting,

-  SwitchButton,

-  Upload,

-  Download,

-  TrendCharts,

-  Star,

-  Search,

-  ChatDotRound,

-  CircleCheck

-} from '@element-plus/icons-vue'

-

-export default {

-  name: 'HomeView',

-  components: {

-    ArrowDown,

-    User,

-    Setting,

-    SwitchButton,

-    Upload,

-    Download,

-    TrendCharts,

-    Star,

-    Search,

-    ChatDotRound,

-    CircleCheck,

-    Navbar

-  },

-  setup() {

-    const router = useRouter()

-    const store = useStore()

-    

-    const currentTime = ref('')

-    const timeInterval = ref(null)

-    

-    // 从store获取用户信息

-    const userInfo = computed(() => store.getters['auth/userInfo'])

-    const username = computed(() => store.getters['auth/username'])

-    const userAvatar = computed(() => store.getters['auth/avatar'])

-    const isAuthenticated = computed(() => store.getters['auth/isAuthenticated'])

-    

-    // API状态

-    const loginStatusColor = computed(() => isAuthenticated.value ? '#67c23a' : '#f56c6c')

-    const loginStatusText = computed(() => isAuthenticated.value ? '已登录' : '未登录')

-    

-    const userInfoStatusColor = computed(() => userInfo.value ? '#67c23a' : '#f56c6c')

-    const userInfoStatusText = computed(() => userInfo.value ? '已获取' : '未获取')

-    

-    // 更新当前时间

-    const updateCurrentTime = () => {

-      currentTime.value = new Date().toLocaleString('zh-CN')

-    }

-    

-    // 格式化字节数

-    const formatBytes = (bytes) => {

-      if (!bytes || bytes === 0) return '0 B'

-      

-      const sizes = ['B', 'KB', 'MB', 'GB', 'TB']

-      const i = Math.floor(Math.log(bytes) / Math.log(1024))

-      const size = (bytes / Math.pow(1024, i)).toFixed(2)

-      

-      return `${size} ${sizes[i]}`

-    }

-    

-    // 计算分享率

-    const calculateRatio = (uploaded, downloaded) => {

-      if (!uploaded || !downloaded || downloaded === 0) {

-        return uploaded > 0 ? '∞' : '0.00'

-      }

-      return (uploaded / downloaded).toFixed(2)

-    }

-    

-    // 格式化日期

-    const formatDate = (timestamp) => {

-      if (!timestamp) return '未知'

-      return new Date(timestamp).toLocaleDateString('zh-CN')

-    }

-    

-    // 检查登录状态

-    const checkLoginStatus = async () => {

-      try {

-        await store.dispatch('auth/checkLoginStatus')

-        ElMessage.success('登录状态检查完成')

-      } catch (error) {

-        console.error('检查登录状态失败:', error)

-        ElMessage.error('检查登录状态失败')

-      }

-    }

-    

-    // 刷新用户信息

-    const refreshUserInfo = async () => {

-      try {

-        await store.dispatch('auth/checkLoginStatus')

-        ElMessage.success('用户信息刷新成功')

-      } catch (error) {

-        console.error('刷新用户信息失败:', error)

-        ElMessage.error('刷新用户信息失败')

-      }

-    }

-    

-    // 处理用户菜单命令

-    const handleUserCommand = async (command) => {

-      switch (command) {

-        case 'profile':

-          router.push('/profile')

-          break

-        case 'settings':

-          ElMessage.info('设置功能开发中...')

-          break

-        case 'logout':

-          try {

-            await ElMessageBox.confirm('确定要退出登录吗?', '提示', {

-              confirmButtonText: '确定',

-              cancelButtonText: '取消',

-              type: 'warning'

-            })

-            

-            await store.dispatch('auth/logout')

-            router.push('/login')

-          } catch (error) {

-            // 用户取消操作

-            if (error !== 'cancel') {

-              console.error('退出登录失败:', error)

-            }

-          }

-          break

-      }

-    }

-    

-    onMounted(() => {

-      // 开始时间更新

-      updateCurrentTime()

-      timeInterval.value = setInterval(updateCurrentTime, 1000)

-      

-      // 检查登录状态

-      if (!isAuthenticated.value) {

-        checkLoginStatus()

-      }

-    })

-    

-    onUnmounted(() => {

-      // 清理定时器

-      if (timeInterval.value) {

-        clearInterval(timeInterval.value)

-      }

-    })

-    

-    return {

-      currentTime,

-      userInfo,

-      username,

-      userAvatar,

-      isAuthenticated,

-      loginStatusColor,

-      loginStatusText,

-      userInfoStatusColor,

-      userInfoStatusText,

-      formatBytes,

-      calculateRatio,

-      formatDate,

-      checkLoginStatus,

-      refreshUserInfo,

-      handleUserCommand

-    }

-  }

-}

-</script>

-

-<style lang="scss" scoped>

-.home-page {

-  min-height: 100vh;

-  background: #f5f5f5;

-}

-

-.navbar {

-  background: #fff;

-  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);

-  height: 60px;

-  display: flex;

-  align-items: center;

-  justify-content: space-between;

-  padding: 0 24px;

-  position: sticky;

-  top: 0;

-  z-index: 1000;

-}

-

-.navbar-brand {

-  font-size: 20px;

-  font-weight: 700;

-  color: #409eff;

-  text-decoration: none;

-}

-

-.navbar-nav {

-  display: flex;

-  align-items: center;

-  gap: 24px;

-}

-

-.navbar-item {

-  color: #606266;

-  text-decoration: none;

-  font-weight: 500;

-  transition: color 0.3s;

-  

-  &:hover {

-    color: #409eff;

-  }

-}

-

-.navbar-user {

-  display: flex;

-  align-items: center;

-  gap: 8px;

-  cursor: pointer;

-  padding: 8px;

-  border-radius: 6px;

-  transition: background-color 0.3s;

-  

-  &:hover {

-    background-color: #f5f7fa;

-  }

-  

-  .username {

-    font-weight: 500;

-    color: #303133;

-  }

-}

-

-.main-content {

-  max-width: 1200px;

-  margin: 0 auto;

-  padding: 24px;

-}

-

-.welcome-card {

-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

-  color: white;

-  border-radius: 12px;

-  padding: 32px;

-  margin-bottom: 24px;

-  

-  .welcome-header {

-    text-align: center;

-    margin-bottom: 24px;

-    

-    h1 {

-      font-size: 28px;

-      margin-bottom: 8px;

-    }

-    

-    p {

-      opacity: 0.9;

-      font-size: 14px;

-    }

-  }

-  

-  .stats-overview {

-    display: grid;

-    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));

-    gap: 16px;

-    

-    .stat-item {

-      background: rgba(255, 255, 255, 0.1);

-      backdrop-filter: blur(10px);

-      border-radius: 8px;

-      padding: 20px;

-      display: flex;

-      align-items: center;

-      gap: 16px;

-      

-      .stat-icon {

-        width: 48px;

-        height: 48px;

-        border-radius: 50%;

-        background: rgba(255, 255, 255, 0.2);

-        display: flex;

-        align-items: center;

-        justify-content: center;

-      }

-      

-      .stat-content {

-        h3 {

-          font-size: 20px;

-          font-weight: 600;

-          margin: 0 0 4px 0;

-        }

-        

-        p {

-          font-size: 14px;

-          opacity: 0.8;

-          margin: 0;

-        }

-      }

-    }

-  }

-}

-

-.quick-actions {

-  background: #fff;

-  border-radius: 12px;

-  padding: 24px;

-  margin-bottom: 24px;

-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);

-  

-  h2 {

-    font-size: 20px;

-    color: #303133;

-    margin: 0 0 20px 0;

-  }

-  

-  .actions-grid {

-    display: grid;

-    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));

-    gap: 16px;

-    

-    .action-card {

-      background: #f8f9fa;

-      border-radius: 8px;

-      padding: 24px;

-      text-align: center;

-      cursor: pointer;

-      transition: all 0.3s;

-      border: 2px solid transparent;

-      

-      &:hover {

-        transform: translateY(-2px);

-        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);

-        border-color: #409eff;

-      }

-      

-      h3 {

-        font-size: 16px;

-        color: #303133;

-        margin: 12px 0 8px 0;

-      }

-      

-      p {

-        font-size: 14px;

-        color: #909399;

-        margin: 0;

-      }

-    }

-  }

-}

-

-.api-status-card {

-  background: #fff;

-  border-radius: 12px;

-  padding: 24px;

-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);

-  

-  h2 {

-    font-size: 20px;

-    color: #303133;

-    margin: 0 0 20px 0;

-  }

-  

-  .status-items {

-    margin-bottom: 24px;

-    

-    .status-item {

-      display: flex;

-      align-items: center;

-      gap: 12px;

-      padding: 12px 0;

-      border-bottom: 1px solid #ebeef5;

-      

-      &:last-child {

-        border-bottom: none;

-      }

-      

-      span {

-        flex: 1;

-        font-size: 14px;

-        color: #606266;

-      }

-    }

-  }

-  

-  .user-details {

-    border-top: 1px solid #ebeef5;

-    padding-top: 20px;

-    

-    h3 {

-      font-size: 16px;

-      color: #303133;

-      margin: 0 0 16px 0;

-    }

-    

-    .details-grid {

-      display: grid;

-      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));

-      gap: 12px;

-      

-      .detail-item {

-        display: flex;

-        align-items: center;

-        padding: 8px 0;

-        

-        label {

-          font-weight: 500;

-          color: #909399;

-          min-width: 80px;

-          font-size: 14px;

-        }

-        

-        span {

-          color: #606266;

-          font-size: 14px;

-        }

-      }

-    }

-  }

-}

-

-@media (max-width: 768px) {

-  .navbar {

-    padding: 0 16px;

-    

-    .navbar-nav {

-      gap: 16px;

-    }

-    

-    .navbar-user .username {

-      display: none;

-    }

-  }

-  

-  .main-content {

-    padding: 16px;

-  }

-  

-  .welcome-card {

-    padding: 24px 16px;

-    

-    .welcome-header h1 {

-      font-size: 24px;

-    }

-    

-    .stats-overview {

-      grid-template-columns: repeat(2, 1fr);

-      gap: 12px;

-      

-      .stat-item {

-        padding: 16px;

-        

-        .stat-content h3 {

-          font-size: 16px;

-        }

-      }

-    }

-  }

-  

-  .actions-grid {

-    grid-template-columns: repeat(2, 1fr);

-    gap: 12px;

-    

-    .action-card {

-      padding: 16px;

-      

-      h3 {

-        font-size: 14px;

-      }

-      

-      p {

-        font-size: 12px;

-      }

-    }

-  }

-  

-  .details-grid {

-    grid-template-columns: 1fr;

-  }

-}

-

-@media (max-width: 480px) {

-  .stats-overview {

-    grid-template-columns: 1fr;

-  }

-  

-  .actions-grid {

-    grid-template-columns: 1fr;

-  }

-}

+<template>
+  <div class="home-page">
+    <!-- 导航栏 -->
+    <Navbar />
+
+    <!-- 主内容区 -->
+    <div class="main-content">
+      <!-- 欢迎卡片 -->
+      <div class="welcome-card">
+        <div class="welcome-header">
+          <h1>欢迎回来,{{ username }}!</h1>
+          <p>当前时间:{{ currentTime }}</p>
+        </div>
+        
+        <!-- 用户统计概览 -->
+        <div class="stats-overview" v-if="userInfo">
+          <div class="stat-item">
+            <div class="stat-icon upload">
+              <el-icon size="24"><Upload /></el-icon>
+            </div>
+            <div class="stat-content">
+              <h3>{{ formatBytes(userInfo.user.uploaded) }}</h3>
+              <p>总上传</p>
+            </div>
+          </div>
+          
+          <div class="stat-item">
+            <div class="stat-icon download">
+              <el-icon size="24"><Download /></el-icon>
+            </div>
+            <div class="stat-content">
+              <h3>{{ formatBytes(userInfo.user.downloaded) }}</h3>
+              <p>总下载</p>
+            </div>
+          </div>
+          
+          <div class="stat-item">
+            <div class="stat-icon ratio">
+              <el-icon size="24"><TrendCharts /></el-icon>
+            </div>
+            <div class="stat-content">
+              <h3>{{ calculateRatio(userInfo.user.uploaded, userInfo.user.downloaded) }}</h3>
+              <p>分享率</p>
+            </div>
+          </div>
+          
+          <div class="stat-item">
+            <div class="stat-icon points">
+              <el-icon size="24"><Star /></el-icon>
+            </div>
+            <div class="stat-content">
+              <h3>{{ userInfo.user.karma || '0' }}</h3>
+              <p>积分</p>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 功能快捷入口 -->
+      <div class="quick-actions">
+        <h2>快捷操作</h2>
+        <div class="actions-grid">
+          <div class="action-card" @click="$router.push('/upload')">
+            <el-icon size="32" color="#67c23a"><Upload /></el-icon>
+            <h3>上传种子</h3>
+            <p>分享你的资源</p>
+          </div>
+          
+          <div class="action-card" @click="$router.push('/torrents')">
+            <el-icon size="32" color="#409eff"><Search /></el-icon>
+            <h3>浏览种子</h3>
+            <p>发现新内容</p>
+          </div>
+          
+          <div class="action-card" @click="$router.push('/forum')">
+            <el-icon size="32" color="#e6a23c"><ChatDotRound /></el-icon>
+            <h3>社区论坛</h3>
+            <p>交流讨论</p>
+          </div>
+          
+          <div class="action-card" @click="$router.push('/profile')">
+            <el-icon size="32" color="#f56c6c"><User /></el-icon>
+            <h3>个人中心</h3>
+            <p>管理账户</p>
+          </div>
+          <div class="action-card" @click="$router.push('/chat')">
+            <el-icon size="32" color="#8e44ad"><ChatDotRound /></el-icon>
+            <h3>聊天室</h3>
+            <p>实时聊天互动</p>
+          </div>
+        </div>
+      </div>
+
+      <!-- API连接状态测试 -->
+      <div class="api-status-card">
+        <h2>API连接状态</h2>
+        <div class="status-items">
+          <div class="status-item">
+            <el-icon :color="loginStatusColor"><CircleCheck /></el-icon>
+            <span>登录状态:{{ loginStatusText }}</span>
+            <el-button size="small" @click="checkLoginStatus">检查状态</el-button>
+          </div>
+          
+          <div class="status-item">
+            <el-icon :color="userInfoStatusColor"><User /></el-icon>
+            <span>用户信息:{{ userInfoStatusText }}</span>
+            <el-button size="small" @click="refreshUserInfo">刷新信息</el-button>
+          </div>
+        </div>
+        
+        <!-- 用户详细信息展示 -->
+        <div class="user-details" v-if="userInfo">
+          <h3>用户详细信息</h3>
+          <div class="details-grid">
+            <div class="detail-item">
+              <label>用户ID:</label>
+              <span>{{ userInfo.user.id }}</span>
+            </div>
+            <div class="detail-item">
+              <label>用户名:</label>
+              <span>{{ userInfo.user.username }}</span>
+            </div>
+            <div class="detail-item">
+              <label>邮箱:</label>
+              <span>{{ userInfo.user.email }}</span>
+            </div>
+            <div class="detail-item">
+              <label>用户组:</label>
+              <span>{{ userInfo.user.group?.displayName || '未知' }}</span>
+            </div>
+            <div class="detail-item">
+              <label>注册时间:</label>
+              <span>{{ formatDate(userInfo.user.createdAt) }}</span>
+            </div>
+            <div class="detail-item">
+              <label>个性签名:</label>
+              <span>{{ userInfo.user.signature || '这个用户很懒,还没有个性签名' }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { ref, computed, onMounted, onUnmounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { useStore } from 'vuex'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+  ArrowDown,
+  User,
+  Setting,
+  SwitchButton,
+  Upload,
+  Download,
+  TrendCharts,
+  Star,
+  Search,
+  ChatDotRound,
+  CircleCheck
+} from '@element-plus/icons-vue'
+import Navbar from "@/components/Navbar.vue";
+
+export default {
+  name: 'HomeView',
+  components: {
+    ArrowDown,
+    User,
+    Setting,
+    SwitchButton,
+    Upload,
+    Download,
+    TrendCharts,
+    Star,
+    Search,
+    ChatDotRound,
+    CircleCheck,
+    Navbar
+  },
+  setup() {
+    const router = useRouter()
+    const store = useStore()
+    
+    const currentTime = ref('')
+    const timeInterval = ref(null)
+    
+    // 从store获取用户信息
+    const userInfo = computed(() => store.getters['auth/userInfo'])
+    const username = computed(() => store.getters['auth/username'])
+    const userAvatar = computed(() => store.getters['auth/avatar'])
+    const isAuthenticated = computed(() => store.getters['auth/isAuthenticated'])
+    
+    // API状态
+    const loginStatusColor = computed(() => isAuthenticated.value ? '#67c23a' : '#f56c6c')
+    const loginStatusText = computed(() => isAuthenticated.value ? '已登录' : '未登录')
+    
+    const userInfoStatusColor = computed(() => userInfo.value ? '#67c23a' : '#f56c6c')
+    const userInfoStatusText = computed(() => userInfo.value ? '已获取' : '未获取')
+    
+    // 更新当前时间
+    const updateCurrentTime = () => {
+      currentTime.value = new Date().toLocaleString('zh-CN')
+    }
+    
+    // 格式化字节数
+    const formatBytes = (bytes) => {
+      if (!bytes || bytes === 0) return '0 B'
+      
+      const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
+      const i = Math.floor(Math.log(bytes) / Math.log(1024))
+      const size = (bytes / Math.pow(1024, i)).toFixed(2)
+      
+      return `${size} ${sizes[i]}`
+    }
+    
+    // 计算分享率
+    const calculateRatio = (uploaded, downloaded) => {
+      if (!uploaded || !downloaded || downloaded === 0) {
+        return uploaded > 0 ? '∞' : '0.00'
+      }
+      return (uploaded / downloaded).toFixed(2)
+    }
+    
+    // 格式化日期
+    const formatDate = (timestamp) => {
+      if (!timestamp) return '未知'
+      return new Date(timestamp).toLocaleDateString('zh-CN')
+    }
+    
+    // 检查登录状态
+    const checkLoginStatus = async () => {
+      try {
+        await store.dispatch('auth/checkLoginStatus')
+        ElMessage.success('登录状态检查完成')
+      } catch (error) {
+        console.error('检查登录状态失败:', error)
+        ElMessage.error('检查登录状态失败')
+      }
+    }
+    
+    // 刷新用户信息
+    const refreshUserInfo = async () => {
+      try {
+        await store.dispatch('auth/checkLoginStatus')
+        ElMessage.success('用户信息刷新成功')
+      } catch (error) {
+        console.error('刷新用户信息失败:', error)
+        ElMessage.error('刷新用户信息失败')
+      }
+    }
+    
+    // 处理用户菜单命令
+    const handleUserCommand = async (command) => {
+      switch (command) {
+        case 'profile':
+          router.push('/profile')
+          break
+        case 'settings':
+          ElMessage.info('设置功能开发中...')
+          break
+        case 'logout':
+          try {
+            await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'warning'
+            })
+            
+            await store.dispatch('auth/logout')
+            router.push('/login')
+          } catch (error) {
+            // 用户取消操作
+            if (error !== 'cancel') {
+              console.error('退出登录失败:', error)
+            }
+          }
+          break
+      }
+    }
+    
+    onMounted(() => {
+      // 开始时间更新
+      updateCurrentTime()
+      timeInterval.value = setInterval(updateCurrentTime, 1000)
+      
+      // 检查登录状态
+      if (!isAuthenticated.value) {
+        checkLoginStatus()
+      }
+    })
+    
+    onUnmounted(() => {
+      // 清理定时器
+      if (timeInterval.value) {
+        clearInterval(timeInterval.value)
+      }
+    })
+    
+    return {
+      currentTime,
+      userInfo,
+      username,
+      userAvatar,
+      isAuthenticated,
+      loginStatusColor,
+      loginStatusText,
+      userInfoStatusColor,
+      userInfoStatusText,
+      formatBytes,
+      calculateRatio,
+      formatDate,
+      checkLoginStatus,
+      refreshUserInfo,
+      handleUserCommand
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.home-page {
+  min-height: 100vh;
+  background: #f5f5f5;
+}
+
+.navbar {
+  background: #fff;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+  height: 60px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 0 24px;
+  position: sticky;
+  top: 0;
+  z-index: 1000;
+}
+
+.navbar-brand {
+  font-size: 20px;
+  font-weight: 700;
+  color: #409eff;
+  text-decoration: none;
+}
+
+.navbar-nav {
+  display: flex;
+  align-items: center;
+  gap: 24px;
+}
+
+.navbar-item {
+  color: #606266;
+  text-decoration: none;
+  font-weight: 500;
+  transition: color 0.3s;
+  
+  &:hover {
+    color: #409eff;
+  }
+}
+
+.navbar-user {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  cursor: pointer;
+  padding: 8px;
+  border-radius: 6px;
+  transition: background-color 0.3s;
+  
+  &:hover {
+    background-color: #f5f7fa;
+  }
+  
+  .username {
+    font-weight: 500;
+    color: #303133;
+  }
+}
+
+.main-content {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 24px;
+}
+
+.welcome-card {
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  color: white;
+  border-radius: 12px;
+  padding: 32px;
+  margin-bottom: 24px;
+  
+  .welcome-header {
+    text-align: center;
+    margin-bottom: 24px;
+    
+    h1 {
+      font-size: 28px;
+      margin-bottom: 8px;
+    }
+    
+    p {
+      opacity: 0.9;
+      font-size: 14px;
+    }
+  }
+  
+  .stats-overview {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+    gap: 16px;
+    
+    .stat-item {
+      background: rgba(255, 255, 255, 0.1);
+      backdrop-filter: blur(10px);
+      border-radius: 8px;
+      padding: 20px;
+      display: flex;
+      align-items: center;
+      gap: 16px;
+      
+      .stat-icon {
+        width: 48px;
+        height: 48px;
+        border-radius: 50%;
+        background: rgba(255, 255, 255, 0.2);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+      
+      .stat-content {
+        h3 {
+          font-size: 20px;
+          font-weight: 600;
+          margin: 0 0 4px 0;
+        }
+        
+        p {
+          font-size: 14px;
+          opacity: 0.8;
+          margin: 0;
+        }
+      }
+    }
+  }
+}
+
+.quick-actions {
+  background: #fff;
+  border-radius: 12px;
+  padding: 24px;
+  margin-bottom: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  h2 {
+    font-size: 20px;
+    color: #303133;
+    margin: 0 0 20px 0;
+  }
+  
+  .actions-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+    gap: 16px;
+    
+    .action-card {
+      background: #f8f9fa;
+      border-radius: 8px;
+      padding: 24px;
+      text-align: center;
+      cursor: pointer;
+      transition: all 0.3s;
+      border: 2px solid transparent;
+      
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+        border-color: #409eff;
+      }
+      
+      h3 {
+        font-size: 16px;
+        color: #303133;
+        margin: 12px 0 8px 0;
+      }
+      
+      p {
+        font-size: 14px;
+        color: #909399;
+        margin: 0;
+      }
+    }
+  }
+}
+
+.api-status-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  h2 {
+    font-size: 20px;
+    color: #303133;
+    margin: 0 0 20px 0;
+  }
+  
+  .status-items {
+    margin-bottom: 24px;
+    
+    .status-item {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+      padding: 12px 0;
+      border-bottom: 1px solid #ebeef5;
+      
+      &:last-child {
+        border-bottom: none;
+      }
+      
+      span {
+        flex: 1;
+        font-size: 14px;
+        color: #606266;
+      }
+    }
+  }
+  
+  .user-details {
+    border-top: 1px solid #ebeef5;
+    padding-top: 20px;
+    
+    h3 {
+      font-size: 16px;
+      color: #303133;
+      margin: 0 0 16px 0;
+    }
+    
+    .details-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+      gap: 12px;
+      
+      .detail-item {
+        display: flex;
+        align-items: center;
+        padding: 8px 0;
+        
+        label {
+          font-weight: 500;
+          color: #909399;
+          min-width: 80px;
+          font-size: 14px;
+        }
+        
+        span {
+          color: #606266;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .navbar {
+    padding: 0 16px;
+    
+    .navbar-nav {
+      gap: 16px;
+    }
+    
+    .navbar-user .username {
+      display: none;
+    }
+  }
+  
+  .main-content {
+    padding: 16px;
+  }
+  
+  .welcome-card {
+    padding: 24px 16px;
+    
+    .welcome-header h1 {
+      font-size: 24px;
+    }
+    
+    .stats-overview {
+      grid-template-columns: repeat(2, 1fr);
+      gap: 12px;
+      
+      .stat-item {
+        padding: 16px;
+        
+        .stat-content h3 {
+          font-size: 16px;
+        }
+      }
+    }
+  }
+  
+  .actions-grid {
+    grid-template-columns: repeat(2, 1fr);
+    gap: 12px;
+    
+    .action-card {
+      padding: 16px;
+      
+      h3 {
+        font-size: 14px;
+      }
+      
+      p {
+        font-size: 12px;
+      }
+    }
+  }
+  
+  .details-grid {
+    grid-template-columns: 1fr;
+  }
+}
+
+@media (max-width: 480px) {
+  .stats-overview {
+    grid-template-columns: 1fr;
+  }
+  
+  .actions-grid {
+    grid-template-columns: 1fr;
+  }
+}
 </style>
\ No newline at end of file
diff --git a/src/views/auth/ProfileView.vue b/src/views/auth/ProfileView.vue
index 8e37d76..640f893 100644
--- a/src/views/auth/ProfileView.vue
+++ b/src/views/auth/ProfileView.vue
@@ -1,364 +1,1222 @@
-<template>

-  <div class="profile-page">

-    <Navbar />

-    <div class="page-container">

-      <!-- 个人信息卡片 -->

-      <div class="profile-header">

-        <div class="user-avatar-section">

-          <div class="avatar-container">

-            <el-avatar :size="120" :src="userProfile.avatar">

-              {{ userProfile.username.charAt(0).toUpperCase() }}

-            </el-avatar>

-            <el-button 

-              type="primary" 

-              size="small" 

-              class="change-avatar-btn"

-              @click="showAvatarDialog = true"

-            >

-              更换头像

-            </el-button>

-          </div>

-          

-          <div class="user-basic-info">

-            <h1 class="username">{{ userProfile.username }}</h1>

-            <div class="user-title">

-              <el-tag :type="getUserTitleType(userProfile.userLevel)" size="large">

-                {{ userProfile.userTitle }}

-              </el-tag>

-            </div>

-            <div class="join-info">

-              <el-icon><Calendar /></el-icon>

-              <span>加入时间:{{ formatDate(userProfile.joinDate) }}</span>

-            </div>

-            <div class="last-login">

-              <el-icon><Clock /></el-icon>

-              <span>最后登录:{{ formatTime(userProfile.lastLogin) }}</span>

-            </div>

-          </div>

-        </div>

-        

-        <div class="user-stats-overview">

-          <div class="stats-grid">

-            <div class="stat-card">

-              <div class="stat-icon upload">

-                <el-icon size="32"><Upload /></el-icon>

-              </div>

-              <div class="stat-info">

-                <h3>{{ userProfile.stats.uploaded }}</h3>

-                <p>上传量</p>

-              </div>

-            </div>

-            

-            <div class="stat-card">

-              <div class="stat-icon download">

-                <el-icon size="32"><Download /></el-icon>

-              </div>

-              <div class="stat-info">

-                <h3>{{ userProfile.stats.downloaded }}</h3>

-                <p>下载量</p>

-              </div>

-            </div>

-            

-            <div class="stat-card">

-              <div class="stat-icon ratio" :class="getRatioClass(userProfile.stats.ratio)">

-                <el-icon size="32"><TrendCharts /></el-icon>

-              </div>

-              <div class="stat-info">

-                <h3>{{ userProfile.stats.ratio }}</h3>

-                <p>分享率</p>

-              </div>

-            </div>

-            

-            <div class="stat-card">

-              <div class="stat-icon points">

-                <el-icon size="32"><Star /></el-icon>

-              </div>

-              <div class="stat-info">

-                <h3>{{ userProfile.stats.points }}</h3>

-                <p>积分</p>

-              </div>

-            </div>

-          </div>

-        </div>

-      </div>

-

-      <!-- 详细信息选项卡 -->

-      <div class="profile-content">

-        <el-tabs v-model="activeTab" type="border-card">

-          <!-- 个人信息 -->

-          <el-tab-pane label="个人信息" name="info">

-            <div class="info-section">

-              <el-form

-                ref="profileFormRef"

-                :model="editProfile"

-                :rules="profileRules"

-                label-width="120px"

-                size="large"

-              >

-                <div class="form-section">

-                  <h3>基本信息</h3>

-                  <el-form-item label="用户名">

-                    <el-input v-model="editProfile.username" disabled>

-                      <template #suffix>

-                        <el-tooltip content="用户名不可修改">

-                          <el-icon><QuestionFilled /></el-icon>

-                        </el-tooltip>

-                      </template>

-                    </el-input>

-                  </el-form-item>

-                  

-                  <el-form-item label="邮箱地址" prop="email">

-                    <el-input v-model="editProfile.email" type="email" />

-                  </el-form-item>

-                  

-                  <el-form-item label="真实姓名" prop="realName">

-                    <el-input v-model="editProfile.realName" placeholder="可选填写" />

-                  </el-form-item>

-                  

-                  <el-form-item label="所在地区">

-                    <el-cascader

-                      v-model="editProfile.location"

-                      :options="locationOptions"

-                      placeholder="请选择地区"

-                      clearable

-                    />

-                  </el-form-item>

-                </div>

-              </el-form>

-            </div>

-          </el-tab-pane>

-        </el-tabs>

-      </div>

-    </div>

-

-    <!-- 更换头像对话框 -->

-    <el-dialog v-model="showAvatarDialog" title="更换头像" width="400px">

-      <div class="avatar-upload">

-        <el-upload

-          ref="avatarUploadRef"

-          :auto-upload="false"

-          :limit="1"

-          accept="image/*"

-          :on-change="handleAvatarChange"

-          list-type="picture-card"

-          class="avatar-uploader"

-        >

-          <el-icon><Plus /></el-icon>

-        </el-upload>

-        <div class="upload-tips">

-          <p>支持 JPG、PNG 格式</p>

-          <p>建议尺寸 200x200 像素</p>

-          <p>文件大小不超过 2MB</p>

-        </div>

-      </div>

-      

-      <template #footer>

-        <el-button @click="showAvatarDialog = false">取消</el-button>

-        <el-button type="primary" @click="uploadAvatar" :loading="uploadingAvatar">

-          上传头像

-        </el-button>

-      </template>

-    </el-dialog>

-  </div>

-</template>

-

-<script>

-import { ref, reactive, computed, onMounted, nextTick } from 'vue'

-import { ElMessage } from 'element-plus'

-import {

-  Calendar,

-  Clock,

-  Upload,

-  Download,

-  TrendCharts,

-  Star,

-  QuestionFilled,

-  Plus

-} from '@element-plus/icons-vue'

-import Navbar from '@/components/Navbar.vue'

-

-export default {

-  name: 'ProfileView',

-  components: {

-    Navbar

-  },

-  setup() {

-    const activeTab = ref('info')

-    const showAvatarDialog = ref(false)

-    const saving = ref(false)

-    const uploadingAvatar = ref(false)

-

-    const userProfile = ref({

-      username: 'MovieExpert',

-      email: 'movieexpert@example.com',

-      realName: '',

-      avatar: '',

-      userLevel: 5,

-      userTitle: '资深会员',

-      joinDate: '2023-01-15T10:00:00',

-      lastLogin: '2025-06-03T14:30:00',

-      location: ['北京市', '朝阳区'],

-      signature: '热爱电影,分享快乐!',

-      website: 'https://movieblog.com',

-      interests: ['电影', '音乐', '科技', '摄影'],

-      emailPublic: false,

-      statsPublic: true,

-      activityPublic: true,

-      stats: {

-        uploaded: '256.8 GB',

-        downloaded: '89.6 GB',

-        ratio: '2.87',

-        points: '15,680'

-      },

-      detailedStats: {

-        totalUploaded: '256.8 GB',

-        uploadedTorrents: 45,

-        avgUploadSize: '5.7 GB',

-        totalDownloaded: '89.6 GB',

-        downloadedTorrents: 123,

-        completedTorrents: 118,

-        seeding: 32,

-        seedingTime: '1,245 小时',

-        seedingRank: 86,

-        totalEarnedPoints: '28,940',

-        totalSpentPoints: '13,260'

-      }

-    })

-

-    const editProfile = reactive({

-      username: '',

-      email: '',

-      realName: '',

-      location: [],

-      signature: '',

-      website: '',

-      interests: [],

-      emailPublic: false,

-      statsPublic: true,

-      activityPublic: true

-    })

-

-    const profileRules = {

-      email: [

-        { required: true, message: '请输入邮箱地址', trigger: 'blur' },

-        { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }

-      ]

-    }

-

-    const locationOptions = [

-      {

-        value: '北京市',

-        label: '北京市',

-        children: [

-          { value: '朝阳区', label: '朝阳区' },

-          { value: '海淀区', label: '海淀区' },

-          { value: '丰台区', label: '丰台区' }

-        ]

-      },

-      {

-        value: '上海市',

-        label: '上海市',

-        children: [

-          { value: '浦东新区', label: '浦东新区' },

-          { value: '黄浦区', label: '黄浦区' },

-          { value: '静安区', label: '静安区' }

-        ]

-      }

-    ]

-

-    onMounted(() => {

-      loadUserProfile()

-    })

-

-    const loadUserProfile = () => {

-      // 加载用户资料到编辑表单

-      Object.assign(editProfile, {

-        username: userProfile.value.username,

-        email: userProfile.value.email,

-        realName: userProfile.value.realName,

-        location: userProfile.value.location,

-        signature: userProfile.value.signature,

-        website: userProfile.value.website,

-        interests: [...userProfile.value.interests],

-        emailPublic: userProfile.value.emailPublic,

-        statsPublic: userProfile.value.statsPublic,

-        activityPublic: userProfile.value.activityPublic

-      })

-    }

-

-    const formatDate = (dateString) => {

-      const date = new Date(dateString)

-      return date.toLocaleDateString('zh-CN')

-    }

-

-    const formatTime = (timeString) => {

-      const date = new Date(timeString)

-      const now = new Date()

-      const diff = now - date

-      const hours = Math.floor(diff / (1000 * 60 * 60))

-

-      if (hours < 1) return '刚刚'

-      if (hours < 24) return `${hours}小时前`

-      const days = Math.floor(hours / 24)

-      if (days < 7) return `${days}天前`

-

-      return date.toLocaleDateString('zh-CN')

-    }

-

-    const getUserTitleType = (level) => {

-      if (level >= 8) return 'danger'  // 管理员

-      if (level >= 6) return 'warning' // 资深会员

-      if (level >= 4) return 'success' // 正式会员

-      if (level >= 2) return 'info'    // 初级会员

-      return 'default' // 新手

-    }

-

-    const getRatioClass = (ratio) => {

-      const r = parseFloat(ratio)

-      if (r >= 2) return 'excellent'

-      if (r >= 1) return 'good'

-      return 'warning'

-    }

-

-    const saveProfile = async () => {

-      try {

-        await profileFormRef.value?.validate()

-        

-        saving.value = true

-        

-        // 模拟保存过程

-        await new Promise(resolve => setTimeout(resolve, 1500))

-        

-        // 更新用户资料

-        Object.assign(userProfile.value, editProfile)

-        

-        ElMessage.success('个人资料保存成功')

-        

-      } catch (error) {

-        console.error('表单验证失败:', error)

-      } finally {

-        saving.value = false

-      }

-    }

-

-    return {

-      activeTab,

-      showAvatarDialog,

-      saving,

-      userProfile,

-      editProfile,

-      profileRules,

-      locationOptions,

-      formatDate,

-      formatTime,

-      getUserTitleType,

-      getRatioClass,

-      saveProfile

-    }

-  }

-}

-</script>

-

-<style scoped>

-/* 添加相关样式 */

-</style>

+<template>
+  <div class="profile-page">
+    <div class="page-container">
+      <!-- 个人信息卡片 -->
+      <div class="profile-header">
+        <div class="user-avatar-section">
+          <div class="avatar-container">
+            <el-avatar :size="120" :src="userProfile.avatar">
+              {{ userProfile.username.charAt(0).toUpperCase() }}
+            </el-avatar>
+            <el-button 
+              type="primary" 
+              size="small" 
+              class="change-avatar-btn"
+              @click="showAvatarDialog = true"
+            >
+              更换头像
+            </el-button>
+          </div>
+          
+          <div class="user-basic-info">
+            <h1 class="username">{{ userBase.username }}</h1>
+            <div class="user-title">
+              <el-tag :type="getUserTitleType(userProfile.userLevel)" size="large">
+                {{ userProfile.userTitle }}
+              </el-tag>
+            </div>
+            <div class="join-info">
+              <el-icon><Calendar /></el-icon>
+              <span>加入时间:{{ userBase.joinDate }}</span>
+            </div>
+            <!-- <div class="last-login">
+              <el-icon><Clock /></el-icon>
+              <span>最后登录:{{ formatTime(userProfile.lastLogin) }}</span>
+            </div> -->
+          </div>
+        </div>
+        
+        <div class="user-stats-overview">
+          <div class="stats-grid">
+            <div class="stat-card">
+              <div class="stat-icon upload">
+                <el-icon size="32"><Upload /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ stats.uploaded || '-' }}</h3>
+                <p>上传量</p>
+              </div>
+            </div>
+            
+            <div class="stat-card">
+              <div class="stat-icon download">
+                <el-icon size="32"><Download /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ stats.downloaded || '-' }}</h3>
+                <p>下载量</p>
+              </div>
+            </div>
+            
+            <div class="stat-card">
+              <div class="stat-icon ratio" :class="getRatioClass(calcRatio)">
+                <el-icon size="32"><TrendCharts /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ calcRatio }}</h3>
+                <p>分享率</p>
+              </div>
+            </div>
+            
+            <div class="stat-card">
+              <div class="stat-icon points">
+                <el-icon size="32"><Star /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ stats.points || '-' }}</h3>
+                <p>积分</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 详细信息选项卡 -->
+      <div class="profile-content">
+        <el-tabs v-model="activeTab" type="border-card">
+          <!-- 数据统计 -->
+          <el-tab-pane label="数据统计" name="stats">
+            <div class="stats-section" v-if="stats">
+              <div class="stats-overview">
+                <div class="overview-grid">
+                  <div class="overview-card">
+                    <h3>上传统计</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">总上传量:</span>
+                        <span class="value">{{ stats.uploaded || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">真实上传:</span>
+                        <span class="value">{{ stats.realUploaded || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">上传带宽:</span>
+                        <span class="value">{{ stats.uploadBandwidth || '-' }}</span>
+                      </div>
+                    </div>
+                  </div>
+                  
+                  <div class="overview-card">
+                    <h3>下载统计</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">总下载量:</span>
+                        <span class="value">{{ stats.downloaded || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">真实下载:</span>
+                        <span class="value">{{ stats.realDownloaded || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">下载带宽:</span>
+                        <span class="value">{{ stats.downloadBandwidth || '-' }}</span>
+                      </div>
+                    </div>
+                  </div>
+                  
+                  <div class="overview-card">
+                    <h3>做种统计</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">正在做种:</span>
+                        <span class="value">{{ stats.seeding || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">做种时间:</span>
+                        <span class="value">{{ stats.seedingTime || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">做种排名:</span>
+                        <span class="value">{{ stats.seedingRank || '-' }}</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <div class="overview-card">
+                    <h3>积分记录</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">当前积分:</span>
+                        <span class="value">{{ stats.points || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">邀请名额:</span>
+                        <span class="value">{{ stats.inviteSlot || '-' }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">ISP:</span>
+                        <span class="value">{{ stats.isp || '-' }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              
+              <!-- 数据图表 -->
+              <div class="charts-section">
+                <div class="chart-card">
+                  <h3>上传下载趋势</h3>
+                  <div class="chart-placeholder">
+                    <el-icon size="48" color="#e4e7ed"><TrendCharts /></el-icon>
+                    <p>图表功能开发中...</p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-tab-pane>
+          
+          <!-- 我的种子 -->
+          <el-tab-pane label="我的种子" name="torrents">
+            <div class="torrents-section">
+              <div class="section-header">
+                <h3>我上传的种子</h3>
+                <el-button type="primary" :icon="Upload" @click="$router.push('/upload')">
+                  上传新种子
+                </el-button>
+              </div>
+              
+              <el-table :data="userTorrents" stripe>
+                <el-table-column label="种子名称" min-width="300">
+                  <template #default="{ row }">
+                    <div class="torrent-info">
+                      <el-tag :type="getCategoryType(row.category)" size="small">
+                        {{ getCategoryName(row.category) }}
+                      </el-tag>
+                      <span class="torrent-title">{{ row.title }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                
+                <el-table-column label="大小" prop="size" width="100" />
+                <el-table-column label="做种" prop="seeders" width="80" align="center" />
+                <el-table-column label="下载" prop="leechers" width="80" align="center" />
+                <el-table-column label="完成" prop="downloads" width="80" align="center" />
+                <el-table-column label="上传时间" width="120">
+                  <template #default="{ row }">
+                    {{ formatDate(row.uploadTime) }}
+                  </template>
+                </el-table-column>
+                
+                <el-table-column label="操作" width="120" align="center">
+                  <template #default="{ row }">
+                    <el-button 
+                      type="primary" 
+                      size="small" 
+                      @click="$router.push(`/torrent/${row.id}`)"
+                    >
+                      查看
+                    </el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+              
+              <div class="pagination-wrapper">
+                <el-pagination
+                  :current-page="torrentsPage"
+                  :page-size="10"
+                  :total="userTorrents.length"
+                  layout="prev, pager, next"
+                  small
+                  @current-change="(page) => torrentsPage = page"
+                />
+              </div>
+            </div>
+          </el-tab-pane>
+          
+          <!-- 活动记录 -->
+          <el-tab-pane label="活动记录" name="activity">
+            <div class="activity-section">
+              <div class="activity-filters">
+                <el-select v-model="activityFilter" placeholder="活动类型">
+                  <el-option label="全部活动" value="" />
+                  <el-option label="上传种子" value="upload" />
+                  <el-option label="下载种子" value="download" />
+                  <el-option label="论坛发帖" value="post" />
+                  <el-option label="积分变动" value="points" />
+                </el-select>
+              </div>
+              
+              <div class="activity-timeline">
+                <el-timeline>
+                  <el-timeline-item
+                    v-for="activity in filteredActivities"
+                    :key="activity.id"
+                    :timestamp="formatTime(activity.time)"
+                    :type="getActivityType(activity.type)"
+                  >
+                    <div class="activity-content">
+                      <div class="activity-header">
+                        <el-icon>
+                          <component :is="getActivityIcon(activity.type)" />
+                        </el-icon>
+                        <span class="activity-title">{{ activity.title }}</span>
+                      </div>
+                      <div class="activity-description">{{ activity.description }}</div>
+                    </div>
+                  </el-timeline-item>
+                </el-timeline>
+              </div>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+
+    <!-- 更换头像对话框 -->
+    <el-dialog v-model="showAvatarDialog" title="更换头像" width="400px">
+      <div class="avatar-upload">
+        <el-upload
+          ref="avatarUploadRef"
+          :auto-upload="false"
+          :limit="1"
+          accept="image/*"
+          :on-change="handleAvatarChange"
+          list-type="picture-card"
+          class="avatar-uploader"
+        >
+          <el-icon><Plus /></el-icon>
+        </el-upload>
+        <div class="upload-tips">
+          <p>支持 JPG、PNG 格式</p>
+          <p>建议尺寸 200x200 像素</p>
+          <p>文件大小不超过 2MB</p>
+        </div>
+      </div>
+      
+      <template #footer>
+        <el-button @click="showAvatarDialog = false">取消</el-button>
+        <el-button type="primary" @click="uploadAvatar" :loading="uploadingAvatar">
+          上传头像
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { ref, reactive, computed, onMounted, nextTick } from 'vue'
+import { useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
+import {
+  Calendar,
+  Clock,
+  Upload,
+  Download,
+  TrendCharts,
+  Star,
+  QuestionFilled,
+  Plus,
+  ChatDotRound,
+  Flag,
+  Coin
+} from '@element-plus/icons-vue'
+import { userApi } from '@/api/user'
+
+export default {
+  name: 'ProfileView',
+  setup() {
+    const router = useRouter()
+    const profileFormRef = ref(null)
+    const passwordFormRef = ref(null)
+    const avatarUploadRef = ref(null)
+    const interestInputRef = ref(null)
+    
+    const activeTab = ref('info')
+    const showAvatarDialog = ref(false)
+    const saving = ref(false)
+    const changingPassword = ref(false)
+    const uploadingAvatar = ref(false)
+    const interestInputVisible = ref(false)
+    const interestInputValue = ref('')
+    const activityFilter = ref('')
+    const torrentsPage = ref(1)
+    
+    const userProfile = ref({
+      username: 'MovieExpert',
+      email: 'movieexpert@example.com',
+      realName: '',
+      avatar: '',
+      userLevel: 5,
+      userTitle: '资深会员',
+      joinDate: '2023-01-15T10:00:00',
+      lastLogin: '2025-06-03T14:30:00',
+      location: ['北京市', '朝阳区'],
+      signature: '热爱电影,分享快乐!',
+      website: 'https://movieblog.com',
+      interests: ['电影', '音乐', '科技', '摄影'],
+      emailPublic: false,
+      statsPublic: true,
+      activityPublic: true,
+      stats: {
+        uploaded: '256.8 GB',
+        downloaded: '89.6 GB',
+        ratio: '2.87',
+        points: '15,680'
+      },
+      detailedStats: {
+        totalUploaded: '256.8 GB',
+        uploadedTorrents: 45,
+        avgUploadSize: '5.7 GB',
+        totalDownloaded: '89.6 GB',
+        downloadedTorrents: 123,
+        completedTorrents: 118,
+        seeding: 32,
+        seedingTime: '1,245 小时',
+        seedingRank: 86,
+        totalEarnedPoints: '28,940',
+        totalSpentPoints: '13,260'
+      }
+    })
+    
+    const editProfile = reactive({
+      username: '',
+      email: '',
+      customTitle: '',
+      realName: '',
+      location: [],
+      signature: '',
+      website: '',
+      interests: [],
+      emailPublic: false,
+      statsPublic: true,
+      activityPublic: true
+    })
+    
+    const passwordForm = reactive({
+      currentPassword: '',
+      newPassword: '',
+      confirmPassword: ''
+    })
+    
+    const profileRules = {
+      email: [
+        { required: true, message: '请输入邮箱地址', trigger: 'blur' },
+        { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
+      ]
+    }
+    
+    const passwordRules = {
+      currentPassword: [
+        { required: true, message: '请输入当前密码', trigger: 'blur' }
+      ],
+      newPassword: [
+        { required: true, message: '请输入新密码', trigger: 'blur' },
+        { min: 6, message: '密码长度至少6个字符', trigger: 'blur' }
+      ],
+      confirmPassword: [
+        { required: true, message: '请确认新密码', trigger: 'blur' },
+        {
+          validator: (rule, value, callback) => {
+            if (value !== passwordForm.newPassword) {
+              callback(new Error('两次输入的密码不一致'))
+            } else {
+              callback()
+            }
+          },
+          trigger: 'blur'
+        }
+      ]
+    }
+    
+    const locationOptions = [
+      {
+        value: '北京市',
+        label: '北京市',
+        children: [
+          { value: '朝阳区', label: '朝阳区' },
+          { value: '海淀区', label: '海淀区' },
+          { value: '丰台区', label: '丰台区' }
+        ]
+      },
+      {
+        value: '上海市',
+        label: '上海市',
+        children: [
+          { value: '浦东新区', label: '浦东新区' },
+          { value: '黄浦区', label: '黄浦区' },
+          { value: '静安区', label: '静安区' }
+        ]
+      }
+    ]
+    
+    const userTorrents = ref([
+      {
+        id: 1,
+        title: '[4K蓝光原盘] 阿凡达:水之道',
+        category: 'movie',
+        size: '85.6 GB',
+        seeders: 45,
+        leechers: 12,
+        downloads: 234,
+        uploadTime: '2025-05-15T10:00:00'
+      },
+      {
+        id: 2,
+        title: '[FLAC] 古典音乐合集',
+        category: 'music',
+        size: '2.3 GB',
+        seeders: 23,
+        leechers: 5,
+        downloads: 89,
+        uploadTime: '2025-04-20T15:30:00'
+      }
+    ])
+    
+    const activities = ref([
+      {
+        id: 1,
+        type: 'upload',
+        title: '上传种子',
+        description: '上传了《阿凡达:水之道》4K蓝光原盘',
+        time: '2025-06-03T10:30:00'
+      },
+      {
+        id: 2,
+        type: 'download',
+        title: '下载种子',
+        description: '下载了《星际穿越》IMAX版本',
+        time: '2025-06-02T14:20:00'
+      },
+      {
+        id: 3,
+        type: 'post',
+        title: '发布主题',
+        description: '在电影讨论区发布了新主题',
+        time: '2025-06-01T16:45:00'
+      },
+      {
+        id: 4,
+        type: 'points',
+        title: '积分变动',
+        description: '做种奖励获得 +50 积分',
+        time: '2025-05-31T09:15:00'
+      }
+    ])
+    
+    const loginHistory = ref([
+      {
+        time: '2025-06-03T14:30:00',
+        ip: '192.168.1.100',
+        device: 'Windows 11 / Chrome 120',
+        success: true
+      },
+      {
+        time: '2025-06-02T09:15:00',
+        ip: '192.168.1.100',
+        device: 'Windows 11 / Chrome 120',
+        success: true
+      },
+      {
+        time: '2025-06-01T22:30:00',
+        ip: '192.168.1.100',
+        device: 'Android / Chrome Mobile',
+        success: true
+      }
+    ])
+    
+    const filteredActivities = computed(() => {
+      if (!activityFilter.value) return activities.value
+      return activities.value.filter(activity => activity.type === activityFilter.value)
+    })
+    
+    const stats = reactive({
+      uploaded: '-',
+      realUploaded: '-',
+      uploadBandwidth: '-',
+      downloaded: '-',
+      realDownloaded: '-',
+      downloadBandwidth: '-',
+      seeding: '-',
+      seedingTime: '-',
+      seedingRank: '-',
+      points: '-',
+      inviteSlot: '-',
+      isp: '-'
+    })
+    
+    const calcRatio = computed(() => {
+      if (!stats.uploaded || !stats.downloaded || stats.downloaded === '-' || stats.uploaded === '-') return '-'
+      const parseGB = (str) => {
+        if (typeof str !== 'string') return 0
+        if (str.endsWith('TB')) return parseFloat(str) * 1024
+        if (str.endsWith('GB')) return parseFloat(str)
+        if (str.endsWith('MB')) return parseFloat(str) / 1024
+        if (str.endsWith('KB')) return parseFloat(str) / 1024 / 1024
+        if (str.endsWith('B')) return parseFloat(str) / 1024 / 1024 / 1024
+        return parseFloat(str)
+      }
+      const up = parseGB(stats.uploaded)
+      const down = parseGB(stats.downloaded)
+      if (!down) return up > 0 ? '∞' : '0.00'
+      return (up / down).toFixed(2)
+    })
+    
+    const userBase = reactive({
+      username: '-',
+      joinDate: '-',
+    })
+    
+    onMounted(async () => {
+      try {
+        const res = await userApi.getCurrentUser()
+        if (res.user && res.user.user) {
+          const u = res.user.user
+          stats.uploaded = formatBytes(u.uploaded)
+          stats.realUploaded = formatBytes(u.realUploaded)
+          stats.uploadBandwidth = u.uploadBandwidth || '-'
+          stats.downloaded = formatBytes(u.downloaded)
+          stats.realDownloaded = formatBytes(u.realDownloaded)
+          stats.downloadBandwidth = u.downloadBandwidth || '-'
+          stats.seeding = u.seeding || '-'
+          stats.seedingTime = formatSeedingTime(u.seedingTime)
+          stats.seedingRank = u.seedingRank || '-'
+          stats.points = u.karma || '-'
+          stats.inviteSlot = u.inviteSlot || '-'
+          stats.isp = u.isp || '-'
+          userBase.username = u.username || '-'
+          userBase.joinDate = u.createdAt ? formatDate(u.createdAt) : '-'
+        }
+      } catch (e) {
+        ElMessage.error('获取数据失败')
+      }
+    })
+    
+    const getUserTitleByLevel = (level) => {
+      if (level == null) return '新手'
+      if (level >= 8) return '管理员'
+      if (level >= 6) return '资深会员'
+      if (level >= 4) return '正式会员'
+      if (level >= 2) return '初级会员'
+      return '新手'
+    }
+    
+    const formatBytes = (bytes) => {
+      if (!bytes || isNaN(bytes)) return '0 B'
+      const k = 1024
+      const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
+      const i = Math.floor(Math.log(bytes) / Math.log(k))
+      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
+    }
+    
+    const formatSeedingTime = (seconds) => {
+      if (!seconds || isNaN(seconds)) return '0 小时'
+      const hours = Math.floor(seconds / 3600)
+      const days = Math.floor(hours / 24)
+      if (days > 0) {
+        return `${days} 天 ${hours % 24} 小时`
+      } else {
+        return `${hours} 小时`
+      }
+    }
+    
+    const formatDate = (dateString) => {
+      const date = new Date(dateString)
+      return date.toLocaleDateString('zh-CN')
+    }
+    
+    const formatTime = (timeString) => {
+      const date = new Date(timeString)
+      const now = new Date()
+      const diff = now - date
+      const hours = Math.floor(diff / (1000 * 60 * 60))
+      
+      if (hours < 1) return '刚刚'
+      if (hours < 24) return `${hours}小时前`
+      const days = Math.floor(hours / 24)
+      if (days < 7) return `${days}天前`
+      
+      return date.toLocaleDateString('zh-CN')
+    }
+    
+    const formatDateTime = (dateString) => {
+      const date = new Date(dateString)
+      return date.toLocaleString('zh-CN')
+    }
+    
+    const getUserTitleType = (level) => {
+      if (level >= 8) return 'danger'  // 管理员
+      if (level >= 6) return 'warning' // 资深会员
+      if (level >= 4) return 'success' // 正式会员
+      if (level >= 2) return 'info'    // 初级会员
+      return 'default' // 新手
+    }
+    
+    const getRatioClass = (ratio) => {
+      const r = parseFloat(ratio)
+      if (r >= 2) return 'excellent'
+      if (r >= 1) return 'good'
+      return 'warning'
+    }
+    
+    const getCategoryType = (category) => {
+      const types = {
+        'movie': 'primary',
+        'tv': 'info',
+        'music': 'success',
+        'software': 'warning',
+        'game': 'danger'
+      }
+      return types[category] || 'default'
+    }
+    
+    const getCategoryName = (category) => {
+      const names = {
+        'movie': '电影',
+        'tv': '电视剧',
+        'music': '音乐',
+        'software': '软件',
+        'game': '游戏'
+      }
+      return names[category] || category
+    }
+
+    const getLanguageName = (language) => {
+      const languages = {
+        'zh-CN': '简体中文',
+        'zh-TW': '繁体中文',
+        'en-US': 'English',
+        'ja-JP': '日本語',
+        'ko-KR': '한국어'
+      }
+      return languages[language] || language
+    }
+    
+    const getActivityType = (type) => {
+      const types = {
+        'upload': 'success',
+        'download': 'primary',
+        'post': 'warning',
+        'points': 'info'
+      }
+      return types[type] || 'primary'
+    }
+    
+    const getActivityIcon = (type) => {
+      const icons = {
+        'upload': 'Upload',
+        'download': 'Download',
+        'post': 'ChatDotRound',
+        'points': 'Coin'
+      }
+      return icons[type] || 'Star'
+    }
+    
+    const showInterestInput = () => {
+      interestInputVisible.value = true
+      nextTick(() => {
+        interestInputRef.value?.focus()
+      })
+    }
+    
+    const addInterest = () => {
+      const interest = interestInputValue.value.trim()
+      if (interest && !editProfile.interests.includes(interest)) {
+        editProfile.interests.push(interest)
+      }
+      interestInputVisible.value = false
+      interestInputValue.value = ''
+    }
+    
+    const removeInterest = (interest) => {
+      const index = editProfile.interests.indexOf(interest)
+      if (index > -1) {
+        editProfile.interests.splice(index, 1)
+      }
+    }
+    
+    const saveProfile = async () => {
+      try {
+        const payload = {
+          username: editProfile.username,
+          email: editProfile.email
+        }
+        await userApi.updateProfile(payload)
+        ElMessage.success('保存成功')
+      } catch (error) {
+        ElMessage.error('保存失败')
+      }
+    }
+    
+    const resetProfile = () => {
+      loadUserProfile()
+      ElMessage.info('已重置为原始数据')
+    }
+    
+    const changePassword = async () => {
+      try {
+        await passwordFormRef.value?.validate()
+        
+        changingPassword.value = true
+        
+        // 模拟密码修改过程
+        await new Promise(resolve => setTimeout(resolve, 1500))
+        
+        // 重置表单
+        passwordFormRef.value?.resetFields()
+        Object.assign(passwordForm, {
+          currentPassword: '',
+          newPassword: '',
+          confirmPassword: ''
+        })
+        
+        ElMessage.success('密码修改成功')
+        
+      } catch (error) {
+        console.error('表单验证失败:', error)
+      } finally {
+        changingPassword.value = false
+      }
+    }
+    
+    const handleAvatarChange = (file) => {
+      const isImage = file.raw.type.startsWith('image/')
+      const isLt2M = file.raw.size / 1024 / 1024 < 2
+      
+      if (!isImage) {
+        ElMessage.error('只能上传图片文件!')
+        return false
+      }
+      if (!isLt2M) {
+        ElMessage.error('图片大小不能超过 2MB!')
+        return false
+      }
+      
+      return true
+    }
+    
+    const uploadAvatar = async () => {
+      const files = avatarUploadRef.value?.uploadFiles
+      if (!files || files.length === 0) {
+        ElMessage.warning('请选择头像文件')
+        return
+      }
+      
+      uploadingAvatar.value = true
+      try {
+        // 模拟上传过程
+        await new Promise(resolve => setTimeout(resolve, 2000))
+        
+        // 更新头像URL
+        userProfile.value.avatar = URL.createObjectURL(files[0].raw)
+        
+        ElMessage.success('头像上传成功')
+        showAvatarDialog.value = false
+        avatarUploadRef.value?.clearFiles()
+        
+      } catch (error) {
+        ElMessage.error('头像上传失败')
+      } finally {
+        uploadingAvatar.value = false
+      }
+    }
+    
+    return {
+      activeTab,
+      showAvatarDialog,
+      saving,
+      changingPassword,
+      uploadingAvatar,
+      interestInputVisible,
+      interestInputValue,
+      activityFilter,
+      torrentsPage,
+      userProfile,
+      editProfile,
+      passwordForm,
+      profileRules,
+      passwordRules,
+      locationOptions,
+      userTorrents,
+      filteredActivities,
+      loginHistory,
+      profileFormRef,
+      passwordFormRef,
+      avatarUploadRef,
+      interestInputRef,
+      formatDate,
+      formatTime,
+      formatDateTime,
+      getUserTitleType,
+      getRatioClass,
+      getCategoryType,
+      getCategoryName,
+      getActivityType,
+      getActivityIcon,
+      showInterestInput,
+      addInterest,
+      removeInterest,
+      saveProfile,
+      resetProfile,
+      changePassword,
+      handleAvatarChange,
+      uploadAvatar,
+      Calendar,
+      Clock,
+      Upload,
+      Download,
+      TrendCharts,
+      Star,
+      QuestionFilled,
+      Plus,
+      ChatDotRound,
+      Flag,
+      Coin,
+      stats,
+      calcRatio,
+      userBase
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.profile-page {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 24px;
+  background: #f5f5f5;
+  min-height: 100vh;
+}
+
+.profile-header {
+  background: #fff;
+  border-radius: 12px;
+  padding: 32px;
+  margin-bottom: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 32px;
+  
+  .user-avatar-section {
+    display: flex;
+    gap: 24px;
+    
+    .avatar-container {
+      text-align: center;
+      
+      .change-avatar-btn {
+        margin-top: 12px;
+      }
+    }
+    
+    .user-basic-info {
+      flex: 1;
+      
+      .username {
+        font-size: 28px;
+        font-weight: 600;
+        color: #2c3e50;
+        margin: 0 0 12px 0;
+      }
+      
+      .user-title {
+        margin-bottom: 16px;
+      }
+      
+      .join-info, .last-login {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        font-size: 14px;
+        color: #7f8c8d;
+        margin-bottom: 8px;
+      }
+    }
+  }
+  
+  .user-stats-overview {
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 16px;
+      
+      .stat-card {
+        background: #f8f9fa;
+        border-radius: 8px;
+        padding: 20px;
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        
+        .stat-icon {
+          width: 48px;
+          height: 48px;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          &.upload { background: rgba(103, 194, 58, 0.1); color: #67c23a; }
+          &.download { background: rgba(64, 158, 255, 0.1); color: #409eff; }
+          &.ratio {
+            &.excellent { background: rgba(103, 194, 58, 0.1); color: #67c23a; }
+            &.good { background: rgba(230, 162, 60, 0.1); color: #e6a23c; }
+            &.warning { background: rgba(245, 108, 108, 0.1); color: #f56c6c; }
+          }
+          &.points { background: rgba(245, 108, 108, 0.1); color: #f56c6c; }
+        }
+        
+        .stat-info {
+          h3 {
+            font-size: 20px;
+            font-weight: 600;
+            color: #2c3e50;
+            margin: 0 0 4px 0;
+          }
+          
+          p {
+            font-size: 14px;
+            color: #7f8c8d;
+            margin: 0;
+          }
+        }
+      }
+    }
+  }
+}
+
+.profile-content {
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  :deep(.el-tabs__content) {
+    padding: 24px;
+  }
+}
+
+.info-section {
+  .form-section {
+    margin-bottom: 32px;
+    
+    h3 {
+      font-size: 18px;
+      font-weight: 600;
+      color: #2c3e50;
+      margin: 0 0 20px 0;
+      padding-bottom: 8px;
+      border-bottom: 2px solid #f0f0f0;
+    }
+  }
+  
+  .interests-input {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    align-items: center;
+    
+    .interest-tag {
+      margin: 0;
+    }
+  }
+  
+  .setting-tip {
+    margin-left: 12px;
+    font-size: 12px;
+    color: #909399;
+  }
+  
+  .form-actions {
+    text-align: center;
+    margin-top: 32px;
+    
+    .el-button {
+      margin: 0 8px;
+      min-width: 100px;
+    }
+  }
+}
+
+.stats-section {
+  .stats-overview {
+    margin-bottom: 32px;
+    
+    .overview-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+      gap: 20px;
+      
+      .overview-card {
+        background: #f8f9fa;
+        border-radius: 8px;
+        padding: 24px;
+        
+        h3 {
+          font-size: 16px;
+          font-weight: 600;
+          color: #2c3e50;
+          margin: 0 0 16px 0;
+        }
+        
+        .stat-details {
+          .detail-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 12px;
+            
+            .label {
+              font-size: 14px;
+              color: #7f8c8d;
+            }
+            
+            .value {
+              font-size: 14px;
+              font-weight: 600;
+              color: #2c3e50;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .charts-section {
+    .chart-card {
+      background: #f8f9fa;
+      border-radius: 8px;
+      padding: 24px;
+      
+      h3 {
+        font-size: 16px;
+        font-weight: 600;
+        color: #2c3e50;
+        margin: 0 0 20px 0;
+      }
+      
+      .chart-placeholder {
+        text-align: center;
+        padding: 60px 0;
+        color: #909399;
+        
+        p {
+          margin: 12px 0 0 0;
+        }
+      }
+    }
+  }
+}
+
+.torrents-section {
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+    
+    h3 {
+      font-size: 18px;
+      font-weight: 600;
+      color: #2c3e50;
+      margin: 0;
+    }
+  }
+  
+  .torrent-info {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    
+    .torrent-title {
+      font-weight: 500;
+    }
+  }
+  
+  .pagination-wrapper {
+    margin-top: 16px;
+    text-align: center;
+  }
+}
+
+.activity-section {
+  .activity-filters {
+    margin-bottom: 24px;
+    
+    .el-select {
+      width: 150px;
+    }
+  }
+  
+  .activity-timeline {
+    .activity-content {
+      .activity-header {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        margin-bottom: 8px;
+        
+        .activity-title {
+          font-weight: 600;
+          color: #2c3e50;
+        }
+      }
+      
+      .activity-description {
+        font-size: 14px;
+        color: #7f8c8d;
+        line-height: 1.5;
+      }
+    }
+  }
+}
+
+.avatar-upload {
+  text-align: center;
+  
+  .avatar-uploader {
+    margin-bottom: 16px;
+  }
+  
+  .upload-tips {
+    font-size: 12px;
+    color: #909399;
+    
+    p {
+      margin: 4px 0;
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .profile-page {
+    padding: 16px;
+  }
+  
+  .profile-header {
+    grid-template-columns: 1fr;
+    gap: 24px;
+    
+    .user-avatar-section {
+      flex-direction: column;
+      text-align: center;
+    }
+    
+    .user-stats-overview .stats-grid {
+      grid-template-columns: 1fr;
+    }
+  }
+  
+  .stats-overview .overview-grid {
+    grid-template-columns: 1fr;
+  }
+  
+  .torrents-section .section-header {
+    flex-direction: column;
+    gap: 16px;
+    align-items: flex-start;
+  }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/forum/ForumSectionView.vue b/src/views/forum/ForumSectionView.vue
index d0b42ec..ccb88f5 100644
--- a/src/views/forum/ForumSectionView.vue
+++ b/src/views/forum/ForumSectionView.vue
@@ -1,5 +1,6 @@
 <template>

   <div class="section-page">

+    <Navbar />

     <div class="page-container">

       <!-- 面包屑导航 -->

       <div class="breadcrumb">

@@ -25,14 +26,6 @@
                 <el-icon><ChatDotRound /></el-icon>

                 <span>{{ sectionInfo.topics }} 主题</span>

               </div>

-              <div class="stat-item">

-                <el-icon><Comment /></el-icon>

-                <span>{{ sectionInfo.replies }} 回复</span>

-              </div>

-              <div class="stat-item">

-                <el-icon><User /></el-icon>

-                <span>{{ sectionInfo.members }} 成员</span>

-              </div>

             </div>

           </div>

         </div>

@@ -101,8 +94,8 @@
               

               <div class="topic-meta">

                 <div class="author-info">

-                  <el-avatar :size="24">{{ topic.author.charAt(0) }}</el-avatar>

-                  <span class="author-name">{{ topic.author }}</span>

+                  <el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar>

+                  <span class="author-name">{{ topic.user?.username || '匿名' }}</span>

                   <span class="create-time">{{ formatTime(topic.createTime) }}</span>

                 </div>

                 

@@ -166,8 +159,8 @@
               

               <div class="topic-meta">

                 <div class="author-info">

-                  <el-avatar :size="24">{{ topic.author.charAt(0) }}</el-avatar>

-                  <span class="author-name">{{ topic.author }}</span>

+                  <el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar>

+                  <span class="author-name">{{ topic.user?.username || '匿名' }}</span>

                   <span class="create-time">{{ formatTime(topic.createTime) }}</span>

                 </div>

                 

@@ -201,8 +194,8 @@
         <!-- 分页 -->

         <div class="pagination-wrapper">

           <el-pagination

-            v-model:current-page="currentPage"

-            v-model:page-size="pageSize"

+            v-model="currentPage"

+            :page-size="pageSize"

             :page-sizes="[20, 50, 100]"

             :total="totalTopics"

             layout="total, sizes, prev, pager, next, jumper"

@@ -313,9 +306,12 @@
   Bell,

   QuestionFilled

 } from '@element-plus/icons-vue'

+import { getTopicsByForum, createTopic } from '@/api/topic'

+import Navbar from "@/components/Navbar.vue";

 

 export default {

   name: 'ForumSectionView',

+  components: {Navbar},

   setup() {

     const route = useRoute()

     const router = useRouter()

@@ -381,60 +377,12 @@
       }

     ])

     

-    const topics = ref([

-      {

-        id: 2,

-        title: '2024年度最佳科幻电影推荐榜单',

-        author: 'SciFiFan',

-        createTime: '2025-06-03T10:30:00',

-        views: 1234,

-        replies: 45,

-        hot: true,

-        featured: false,

-        closed: false,

-        hasNewReplies: true,

-        lastReply: {

-          author: 'MovieLover',

-          time: '2025-06-03T14:25:00'

-        }

-      },

-      {

-        id: 3,

-        title: '阿凡达2:水之道 观影感受分享',

-        author: 'Avatar2Fan',

-        createTime: '2025-06-02T16:45:00',

-        views: 892,

-        replies: 67,

-        hot: false,

-        featured: true,

-        closed: false,

-        hasNewReplies: false,

-        lastReply: {

-          author: 'CinemaExpert',

-          time: '2025-06-03T12:10:00'

-        }

-      },

-      {

-        id: 4,

-        title: '求推荐几部好看的悬疑电影',

-        author: 'SuspenseLover',

-        createTime: '2025-06-01T09:20:00',

-        views: 456,

-        replies: 23,

-        hot: false,

-        featured: false,

-        closed: false,

-        hasNewReplies: true,

-        lastReply: {

-          author: 'ThrillerFan',

-          time: '2025-06-03T11:45:00'

-        }

-      }

-    ])

+    const topics = ref([])

     

     onMounted(() => {

       const sectionId = route.params.id

       fetchSectionData(sectionId)

+      fetchTopics()

     })

     

     const fetchSectionData = async (id) => {

@@ -457,9 +405,7 @@
         sectionInfo.value = {

           id: parseInt(id),

           ...sectionData,

-          topics: 3256,

-          replies: 18934,

-          members: 1234

+          topics: 0  // 初始化为0,会在fetchTopics中更新为真实数量

         }

         

         totalTopics.value = 156

@@ -507,9 +453,12 @@
     const fetchTopics = async () => {

       loading.value = true

       try {

-        // 模拟API调用

-        await new Promise(resolve => setTimeout(resolve, 500))

-        console.log('获取主题列表:', { searchQuery: searchQuery.value, sortBy: sortBy.value, filterType: filterType.value })

+        // 调用后端API获取主题列表

+        const res = await getTopicsByForum(sectionInfo.value.id)

+        topics.value = res.data || res // 兼容不同返回结构

+        totalTopics.value = topics.value.length

+        // 同时更新顶部显示的主题数量

+        sectionInfo.value.topics = topics.value.length

       } catch (error) {

         ElMessage.error('获取主题列表失败')

       } finally {

@@ -579,8 +528,20 @@
         

         submitting.value = true

         

-        // 模拟提交过程

-        await new Promise(resolve => setTimeout(resolve, 1500))

+        // 构建主题数据

+        const topicData = {

+          title: newTopic.title,

+          content: newTopic.content,

+          forumId: sectionInfo.value.id, // 使用当前版块ID

+          tags: newTopic.tags,

+          isPinned: newTopic.options.includes('hot'),

+          isLocked: false

+        }

+        

+        console.log('提交主题数据:', topicData)

+        

+        // 调用API创建主题

+        const response = await createTopic(topicData)

         

         ElMessage.success('主题发布成功!')

         resetForm()

@@ -590,7 +551,8 @@
         fetchTopics()

         

       } catch (error) {

-        console.error('表单验证失败:', error)

+        console.error('发布主题失败:', error)

+        ElMessage.error('发布主题失败,请重试')

       } finally {

         submitting.value = false

       }

diff --git a/src/views/forum/ForumTopicView.vue b/src/views/forum/ForumTopicView.vue
index e65b297..170e313 100644
--- a/src/views/forum/ForumTopicView.vue
+++ b/src/views/forum/ForumTopicView.vue
@@ -1,5 +1,6 @@
 <template>

   <div class="topic-detail-page">

+    <Navbar />

     <div class="page-container">

       <!-- 面包屑导航 -->

       <div class="breadcrumb">

@@ -41,7 +42,7 @@
               <el-avatar :size="32">{{ topic.author.charAt(0) }}</el-avatar>

               <div class="author-details">

                 <span class="author-name">{{ topic.author }}</span>

-                <span class="post-time">发表于 {{ formatDateTime(topic.createTime) }}</span>

+

               </div>

             </div>

             

@@ -73,9 +74,6 @@
             </el-button>

             <template #dropdown>

               <el-dropdown-menu>

-                <el-dropdown-item command="favorite">

-                  {{ isFavorited ? '取消收藏' : '收藏主题' }}

-                </el-dropdown-item>

                 <el-dropdown-item command="share">分享主题</el-dropdown-item>

                 <el-dropdown-item command="report" divided>举报主题</el-dropdown-item>

               </el-dropdown-menu>

@@ -101,9 +99,7 @@
                 </div>

               </div>

             </div>

-            <div class="post-time">

-              {{ formatDateTime(topic.createTime) }}

-            </div>

+

           </div>

           

           <div class="post-content">

@@ -111,10 +107,6 @@
           </div>

           

           <div class="post-actions">

-            <el-button type="text" size="small" @click="likePost(topic.id)">

-              <el-icon><Like /></el-icon>

-              {{ topic.likes || 0 }}

-            </el-button>

             <el-button type="text" size="small" @click="quotePost(topic)">

               <el-icon><ChatDotRound /></el-icon>

               引用

@@ -145,16 +137,14 @@
                 </div>

               </div>

             </div>

-            <div class="post-time">

-              {{ formatDateTime(reply.createTime) }}

-            </div>

+

           </div>

           

           <div class="post-content">

             <div v-if="reply.quotedPost" class="quoted-content">

               <div class="quote-header">

                 <el-icon><ChatDotRound /></el-icon>

-                <span>{{ reply.quotedPost.author }} 发表于 {{ formatDateTime(reply.quotedPost.time) }}</span>

+                <span>{{ reply.quotedPost.author }}</span>

               </div>

               <div class="quote-text">{{ reply.quotedPost.content }}</div>

             </div>

@@ -162,10 +152,6 @@
           </div>

           

           <div class="post-actions">

-            <el-button type="text" size="small" @click="likePost(reply.id)">

-              <el-icon><Like /></el-icon>

-              {{ reply.likes || 0 }}

-            </el-button>

             <el-button type="text" size="small" @click="quotePost(reply)">

               <el-icon><ChatDotRound /></el-icon>

               引用

@@ -262,127 +248,131 @@
 import { ref, reactive, onMounted } from 'vue'

 import { useRoute, useRouter } from 'vue-router'

 import { ElMessage, ElMessageBox } from 'element-plus'

+import axios from 'axios'

 import {

   Edit,

   More,

   View,

   Comment,

-  Like,

   ChatDotRound,

   Flag,

   ArrowDown

 } from '@element-plus/icons-vue'

+import Navbar from "@/components/Navbar.vue";

 

 export default {

   name: 'ForumTopicView',

+  components: {Navbar},

   setup() {

     const route = useRoute()

     const router = useRouter()

     const replyFormRef = ref(null)

-    

+

     const showReplyDialog = ref(false)

     const submittingReply = ref(false)

-    const isFavorited = ref(false)

+    const loading = ref(false)

     const currentPage = ref(1)

     const pageSize = ref(20)

     const totalReplies = ref(0)

     const quickReplyContent = ref('')

     const quotedContent = ref(null)

-    

+

     const topic = ref({

-      id: 1,

-      title: '2024年度最佳PT站点推荐与对比分析',

-      sectionId: 1,

-      sectionName: '站务讨论',

-      author: 'PTExpert',

-      authorTitle: '资深会员',

-      authorPosts: 1256,

-      authorReputation: 2890,

-      createTime: '2025-06-01T10:30:00',

-      content: `

-        <p>大家好,作为一个使用PT站点多年的老用户,我想和大家分享一下2024年各大PT站点的使用体验和对比分析。</p>

-        

-        <h3>评测标准</h3>

-        <ul>

-          <li>资源丰富度:种子数量、更新速度、稀有资源</li>

-          <li>用户体验:界面设计、功能完善度、响应速度</li>

-          <li>社区氛围:用户活跃度、互帮互助程度</li>

-          <li>规则友好性:考核难度、分享率要求、保种要求</li>

-        </ul>

-        

-        <h3>推荐站点</h3>

-        <p>经过综合评测,以下几个站点值得推荐:</p>

-        <ol>

-          <li><strong>站点A</strong>:资源最全,更新最快,适合影视爱好者</li>

-          <li><strong>站点B</strong>:音乐资源丰富,无损居多,音质发烧友首选</li>

-          <li><strong>站点C</strong>:软件资源全面,更新及时,开发者必备</li>

-        </ol>

-        

-        <p>具体的详细评测报告我会在后续回复中逐一介绍,欢迎大家讨论和补充!</p>

-      `,

-      views: 2856,

-      replies: 147,

-      likes: 89,

-      tags: ['PT站点', '推荐', '对比'],

-      pinned: true,

-      hot: true,

+      id: null,

+      title: '',

+      sectionId: null,

+      sectionName: '',

+      author: '',

+      authorTitle: '',

+      authorPosts: 0,

+      authorReputation: 0,

+      createTime: '',

+      content: '',

+      views: 0,

+      replies: 0,

+      likes: 0,

+      tags: [],

+      pinned: false,

+      hot: false,

       closed: false

     })

-    

-    const replies = ref([

-      {

-        id: 2,

-        author: 'MovieLover88',

-        authorTitle: '影视达人',

-        authorPosts: 567,

-        authorReputation: 1234,

-        createTime: '2025-06-01T11:15:00',

-        content: '感谢楼主的详细分析!特别期待站点A的详细评测,最近正在寻找好的影视资源站点。',

-        likes: 12

-      },

-      {

-        id: 3,

-        author: 'TechGuru',

-        authorTitle: '技术专家',

-        authorPosts: 890,

-        authorReputation: 2156,

-        createTime: '2025-06-01T12:30:00',

-        content: '站点C确实不错,软件资源很全面。不过楼主能不能也评测一下游戏类的PT站点?',

-        likes: 8,

-        quotedPost: {

-          author: 'PTExpert',

-          time: '2025-06-01T10:30:00',

-          content: '站点C:软件资源全面,更新及时,开发者必备'

-        }

-      }

-    ])

-    

+

+    const replies = ref([])

+

     const replyForm = reactive({

       content: ''

     })

-    

+

     const replyRules = {

       content: [

         { required: true, message: '请输入回复内容', trigger: 'blur' },

         { min: 5, max: 5000, message: '内容长度在 5 到 5000 个字符', trigger: 'blur' }

       ]

     }

-    

+

+    // 初始化数据

     onMounted(() => {

       const topicId = route.params.id

       fetchTopicDetail(topicId)

+      fetchReplies(topicId)

     })

-    

-    const fetchTopicDetail = async (id) => {

+

+    // 获取主题详情

+    const fetchTopicDetail = async (topicId) => {

+      loading.value = true

+      console.log(`[fetchTopicDetail] 请求主题详情: /api/posts/${topicId}`)

       try {

-        console.log('获取主题详情:', id)

-        totalReplies.value = 147

+        const response = await axios.get(`/api/posts/${topicId}`)

+        console.log('[fetchTopicDetail] 响应数据:', response.data)

+        topic.value = {

+          ...response.data,

+          tags: response.data.tags ? response.data.tags.split(',') : [],

+          authorTitle: response.data.authorTitle || '会员'

+        }

       } catch (error) {

+        console.error('[fetchTopicDetail] 请求失败:', error)

         ElMessage.error('获取主题详情失败')

         router.back()

+      } finally {

+        loading.value = false

       }

     }

-    

+

+

+    // 获取主题的所有回复(平铺结构)

+    const fetchReplies = async (topicId) => {

+      loading.value = true

+      const url = `/api/posts/topic/${topicId}`

+      const params = { page: currentPage.value, size: pageSize.value }

+      console.log(`[fetchReplies] 请求回复列表: ${url}`, params)

+      try {

+        const response = await axios.get(url, { params })

+        console.log('[fetchReplies] 响应数据:', response.data)

+        replies.value = response.data.posts.map(post => ({

+          id: post.id,

+          author: post.author,

+          authorTitle: post.authorTitle || '会员',

+          authorPosts: post.authorPosts || 0,

+          authorReputation: post.authorReputation || 0,

+          createTime: post.createTime,

+          content: post.content,

+          likes: post.likes || 0,

+          quotedPost: post.quotedPost ? {

+            author: post.quotedPost.author,

+            time: post.quotedPost.time,

+            content: post.quotedPost.content

+          } : null

+        }))

+        totalReplies.value = response.data.total

+      } catch (error) {

+        console.error('[fetchReplies] 请求失败:', error)

+        ElMessage.error('获取回复列表失败')

+      } finally {

+        loading.value = false

+      }

+    }

+

+

     const formatDateTime = (dateString) => {

       const date = new Date(dateString)

       return date.toLocaleString('zh-CN', {

@@ -393,17 +383,13 @@
         minute: '2-digit'

       })

     }

-    

+

     const formatContent = (content) => {

       return content.replace(/\n/g, '<br>')

     }

-    

+

     const handleTopicAction = (command) => {

       switch (command) {

-        case 'favorite':

-          isFavorited.value = !isFavorited.value

-          ElMessage.success(isFavorited.value ? '已收藏' : '已取消收藏')

-          break

         case 'share':

           navigator.clipboard.writeText(window.location.href)

           ElMessage.success('链接已复制到剪贴板')

@@ -413,19 +399,9 @@
           break

       }

     }

-    

-    const likePost = (postId) => {

-      if (postId === topic.value.id) {

-        topic.value.likes = (topic.value.likes || 0) + 1

-      } else {

-        const reply = replies.value.find(r => r.id === postId)

-        if (reply) {

-          reply.likes = (reply.likes || 0) + 1

-        }

-      }

-      ElMessage.success('点赞成功')

-    }

-    

+

+

+

     const quotePost = (post) => {

       quotedContent.value = {

         author: post.author,

@@ -434,36 +410,36 @@
       }

       showReplyDialog.value = true

     }

-    

+

     const reportPost = async (postId) => {

       try {

-        await ElMessageBox.prompt('请说明举报原因', '举报内容', {

+        const { value } = await ElMessageBox.prompt('请说明举报原因', '举报内容', {

           confirmButtonText: '提交举报',

           cancelButtonText: '取消',

           inputType: 'textarea',

           inputPlaceholder: '请详细说明举报原因...'

         })

-        

+        // 假设有举报API,实际需根据后端实现

         ElMessage.success('举报已提交,我们会尽快处理')

       } catch {

         // 用户取消

       }

     }

-    

+

     const clearQuote = () => {

       quotedContent.value = null

     }

-    

+

     const handleCloseReplyDialog = () => {

       if (replyForm.content) {

         ElMessageBox.confirm(

-          '确定要关闭吗?未保存的内容将会丢失。',

-          '提示',

-          {

-            confirmButtonText: '确定',

-            cancelButtonText: '取消',

-            type: 'warning'

-          }

+            '确定要关闭吗?未保存的内容将会丢失。',

+            '提示',

+            {

+              confirmButtonText: '确定',

+              cancelButtonText: '取消',

+              type: 'warning'

+            }

         ).then(() => {

           resetReplyForm()

           showReplyDialog.value = false

@@ -475,66 +451,76 @@
         showReplyDialog.value = false

       }

     }

-    

+

     const submitReply = async () => {

       try {

         await replyFormRef.value?.validate()

-        

         submittingReply.value = true

-        

-        await new Promise(resolve => setTimeout(resolve, 1500))

-        

-        const newReply = {

-          id: Date.now(),

-          author: localStorage.getItem('username') || '用户',

-          authorTitle: '会员',

-          authorPosts: 0,

-          authorReputation: 0,

-          createTime: new Date().toISOString(),

+

+        const postData = {

+          topicId: topic.value.id,

           content: replyForm.content,

-          likes: 0,

-          quotedPost: quotedContent.value

+          quotedPost: quotedContent.value ? {

+            author: quotedContent.value.author,

+            time: quotedContent.value.time,

+            content: quotedContent.value.content

+          } : null,

+          author: localStorage.getItem('username') || '用户'

         }

-        

-        replies.value.push(newReply)

+

+        const response = await axios.post('/api/posts', postData)

+        replies.value.push({

+          id: response.data.id,

+          author: response.data.author,

+          authorTitle: response.data.authorTitle || '会员',

+          authorPosts: response.data.authorPosts || 0,

+          authorReputation: response.data.authorReputation || 0,

+          createTime: response.data.createTime,

+          content: response.data.content,

+          likes: response.data.likes || 0,

+          quotedPost: response.data.quotedPost

+        })

         topic.value.replies += 1

-        

+

         ElMessage.success('回复发表成功!')

         resetReplyForm()

         showReplyDialog.value = false

-        

       } catch (error) {

-        console.error('表单验证失败:', error)

+        console.error('表单验证或提交失败:', error)

+        ElMessage.error('发表回复失败')

       } finally {

         submittingReply.value = false

       }

     }

-    

+

     const submitQuickReply = async () => {

       if (!quickReplyContent.value.trim()) {

         ElMessage.warning('请输入回复内容')

         return

       }

-      

+

       submittingReply.value = true

       try {

-        await new Promise(resolve => setTimeout(resolve, 1000))

-        

-        const newReply = {

-          id: Date.now(),

-          author: localStorage.getItem('username') || '用户',

-          authorTitle: '会员',

-          authorPosts: 0,

-          authorReputation: 0,

-          createTime: new Date().toISOString(),

+        const postData = {

+          topicId: topic.value.id,

           content: quickReplyContent.value,

-          likes: 0

+          author: localStorage.getItem('username') || '用户'

         }

-        

-        replies.value.push(newReply)

+

+        const response = await axios.post('/api/posts', postData)

+        replies.value.push({

+          id: response.data.id,

+          author: response.data.author,

+          authorTitle: response.data.authorTitle || '会员',

+          authorPosts: response.data.authorPosts || 0,

+          authorReputation: response.data.authorReputation || 0,

+          createTime: response.data.createTime,

+          content: response.data.content,

+          likes: response.data.likes || 0

+        })

         topic.value.replies += 1

         quickReplyContent.value = ''

-        

+

         ElMessage.success('回复发表成功!')

       } catch (error) {

         ElMessage.error('发表回复失败')

@@ -542,30 +528,32 @@
         submittingReply.value = false

       }

     }

-    

+

     const clearQuickReply = () => {

       quickReplyContent.value = ''

     }

-    

+

     const resetReplyForm = () => {

       replyFormRef.value?.resetFields()

       replyForm.content = ''

       quotedContent.value = null

     }

-    

+

     const handleSizeChange = (size) => {

       pageSize.value = size

       currentPage.value = 1

+      fetchReplies(topic.value.id)

     }

-    

+

     const handleCurrentChange = (page) => {

       currentPage.value = page

+      fetchReplies(topic.value.id)

     }

-    

+

     return {

       showReplyDialog,

       submittingReply,

-      isFavorited,

+      loading,

       currentPage,

       pageSize,

       totalReplies,

@@ -579,7 +567,6 @@
       formatDateTime,

       formatContent,

       handleTopicAction,

-      likePost,

       quotePost,

       reportPost,

       clearQuote,

@@ -593,7 +580,6 @@
       More,

       View,

       Comment,

-      Like,

       ChatDotRound,

       Flag,

       ArrowDown

diff --git a/src/views/forum/ForumView.vue b/src/views/forum/ForumView.vue
index 28cdff2..703e4f5 100644
--- a/src/views/forum/ForumView.vue
+++ b/src/views/forum/ForumView.vue
@@ -67,10 +67,10 @@
             <div class="section-info">

               <h3 class="section-name">{{ section.name }}</h3>

               <p class="section-description">{{ section.description }}</p>

-              <div class="section-stats">

-                <span class="stat">{{ section.topics }} 主题</span>

-                <span class="stat">{{ section.replies }} 回复</span>

-              </div>

+<!--              <div class="section-stats">-->

+<!--                <span class="stat">{{ section.topics }} 主题</span>-->

+<!--                <span class="stat">{{ section.replies }} 回复</span>-->

+<!--              </div>-->

             </div>

             <div class="section-latest">

               <div v-if="section.latestTopic" class="latest-topic">

@@ -270,13 +270,21 @@
   QuestionFilled,

   Bell

 } from '@element-plus/icons-vue'

-import Navbar from '@/components/Navbar.vue'

+import {

+  getAllTopics,

+  getTopicsByForum,

+  createTopic,

+  searchTopics

+} from '@/api/topic'

+import {

+  getAllForums,

+  getForumById

+} from '@/api/forum'

+import Navbar from "@/components/Navbar.vue";

 

 export default {

   name: 'ForumView',

-  components:{

-    Navbar

-  },

+  components: {Navbar},

   setup() {

     const router = useRouter()

     const topicFormRef = ref(null)

@@ -288,10 +296,10 @@
     const tagInputValue = ref('')

     

     const forumStats = reactive({

-      totalTopics: '15,268',

-      totalReplies: '89,456',

-      activeUsers: '2,341',

-      todayPosts: '156'

+      totalTopics: 0,

+      totalReplies: 0,

+      activeUsers: 0,

+      todayPosts: 0

     })

     

     const newTopic = reactive({

@@ -315,156 +323,83 @@
       ]

     }

     

-    const forumSections = ref([

-      {

-        id: 1,

-        name: '电影讨论',

-        description: '分享和讨论电影资源,交流观影心得',

-        icon: 'Film',

-        color: '#409eff',

-        topics: 3256,

-        replies: 18934,

-        latestTopic: {

-          title: '2024年最佳科幻电影推荐',

-          author: 'MovieFan',

-          time: '2025-06-03T14:30:00'

-        }

-      },

-      {

-        id: 2,

-        name: '音乐分享',

-        description: '音乐资源分享,音乐制作技术交流',

-        icon: 'Headphones',

-        color: '#67c23a',

-        topics: 1892,

-        replies: 9567,

-        latestTopic: {

-          title: '无损音乐格式对比分析',

-          author: 'AudioExpert',

-          time: '2025-06-03T13:45:00'

-        }

-      },

-      {

-        id: 3,

-        name: '软件技术',

-        description: '软件资源分享,技术问题讨论',

-        icon: 'Monitor',

-        color: '#e6a23c',

-        topics: 2134,

-        replies: 12456,

-        latestTopic: {

-          title: 'Adobe 2025 新功能体验分享',

-          author: 'TechGuru',

-          time: '2025-06-03T12:20:00'

-        }

-      },

-      {

-        id: 4,

-        name: '游戏天地',

-        description: '游戏资源分享,游戏攻略讨论',

-        icon: 'GamePad',

-        color: '#f56c6c',

-        topics: 1567,

-        replies: 8234,

-        latestTopic: {

-          title: '年度游戏大作盘点',

-          author: 'GameMaster',

-          time: '2025-06-03T11:50:00'

-        }

-      },

-      {

-        id: 5,

-        name: '站务公告',

-        description: '网站公告,规则说明,意见建议',

-        icon: 'Bell',

-        color: '#909399',

-        topics: 234,

-        replies: 1567,

-        latestTopic: {

-          title: '网站维护通知',

-          author: 'Admin',

-          time: '2025-06-03T10:00:00'

-        }

-      },

-      {

-        id: 6,

-        name: '新手求助',

-        description: '新手问题解答,使用教程分享',

-        icon: 'QuestionFilled',

-        color: '#606266',

-        topics: 456,

-        replies: 2890,

-        latestTopic: {

-          title: '新手如何提高分享率?',

-          author: 'Newbie123',

-          time: '2025-06-03T09:30:00'

-        }

-      }

-    ])

+    const forumSections = ref([])

     

-    const hotTopics = ref([

-      {

-        id: 1,

-        title: '2024年度最佳PT站点推荐与对比分析',

-        author: 'PTExpert',

-        views: 2856,

-        replies: 147,

-        lastReply: '2025-06-03T14:25:00',

-        tags: ['PT站点', '推荐', '对比'],

-        pinned: true,

-        hot: true

-      },

-      {

-        id: 2,

-        title: '如何安全高效地使用BT下载工具',

-        author: 'SafeDownloader',

-        views: 1932,

-        replies: 89,

-        lastReply: '2025-06-03T13:50:00',

-        tags: ['BT工具', '安全', '教程'],

-        hot: true

-      },

-      {

-        id: 3,

-        title: '分享率提升技巧与经验总结',

-        author: 'SeedMaster',

-        views: 1654,

-        replies: 76,

-        lastReply: '2025-06-03T12:40:00',

-        tags: ['分享率', '技巧', '经验']

-      }

-    ])

+    const hotTopics = ref([])

+    const recentReplies = ref([])

     

-    const recentReplies = ref([

-      {

-        id: 1,

-        author: 'MovieLover88',

-        topicId: 1,

-        topicTitle: '阿凡达2观影感受分享',

-        content: '画面效果确实震撼,特别是水下的场景...',

-        time: '2025-06-03T14:45:00'

-      },

-      {

-        id: 2,

-        author: 'TechEnthusiast',

-        topicId: 2,

-        topicTitle: '最新版Photoshop使用技巧',

-        content: '新的AI功能确实很强大,大大提高了工作效率...',

-        time: '2025-06-03T14:30:00'

-      },

-      {

-        id: 3,

-        author: 'GameFan2024',

-        topicId: 3,

-        topicTitle: '赛博朋克2077最新更新体验',

-        content: '修复了很多bug,现在游戏体验好多了...',

-        time: '2025-06-03T14:15:00'

+    const loadForumData = async () => {

+      try {

+        // 获取所有论坛

+        const forums = await getAllForums()

+        forumSections.value = forums.map(forum => ({

+          id: forum.id,

+          name: forum.name,

+          description: forum.description,

+          icon: getForumIcon(forum.name),

+          color: getForumColor(forum.name),

+          topics: forum.topicCount || 0,

+          replies: forum.replyCount || 0,

+          latestTopic: forum.latestTopic ? {

+            title: forum.latestTopic.title,

+            author: forum.latestTopic.user?.username || '匿名用户',

+            time: forum.latestTopic.createTime

+          } : null

+        }))

+        

+        // 获取所有主题

+        const topics = await getAllTopics()

+        hotTopics.value = topics.slice(0, 3).map(topic => ({

+          id: topic.id,

+          title: topic.title,

+          author: topic.user?.username || '匿名用户',

+          views: topic.views || 0,

+          replies: topic.replies || 0,

+          lastReply: topic.lastReplyTime,

+          tags: topic.tags || [],

+          pinned: topic.pinned || false,

+          hot: topic.views > 1000

+        }))

+        

+        // 更新统计数据

+        forumStats.totalTopics = topics.length

+        forumStats.totalReplies = topics.reduce((sum, topic) => sum + (topic.replies || 0), 0)

+        forumStats.activeUsers = new Set(topics.map(topic => topic.user?.id)).size

+        forumStats.todayPosts = topics.filter(topic => {

+          const topicDate = new Date(topic.createTime)

+          const today = new Date()

+          return topicDate.toDateString() === today.toDateString()

+        }).length

+        

+      } catch (error) {

+        console.error('加载论坛数据失败:', error)

+        ElMessage.error('加载论坛数据失败')

       }

-    ])

+    }

     

-    onMounted(() => {

-      // 初始化论坛数据

-    })

+    const getForumIcon = (name) => {

+      const iconMap = {

+        '电影讨论': 'Film',

+        '音乐分享': 'Headphones',

+        '软件技术': 'Monitor',

+        '游戏天地': 'GamePad',

+        '站务公告': 'Bell',

+        '新手求助': 'QuestionFilled'

+      }

+      return iconMap[name] || 'ChatLineRound'

+    }

+    

+    const getForumColor = (name) => {

+      const colorMap = {

+        '电影讨论': '#409eff',

+        '音乐分享': '#67c23a',

+        '软件技术': '#e6a23c',

+        '游戏天地': '#f56c6c',

+        '站务公告': '#909399',

+        '新手求助': '#606266'

+      }

+      return colorMap[name] || '#409eff'

+    }

     

     const formatTime = (timeString) => {

       const date = new Date(timeString)

@@ -537,18 +472,28 @@
         

         submitting.value = true

         

-        // 模拟提交过程

-        await new Promise(resolve => setTimeout(resolve, 1500))

+        const topicData = {

+          title: newTopic.title,

+          content: newTopic.content,

+          forumId: newTopic.sectionId,

+          tags: newTopic.tags

+        }

+        

+        const response = await createTopic(topicData)

         

         ElMessage.success('主题发布成功!')

         resetForm()

         showNewTopicDialog.value = false

         

+        // 刷新论坛数据

+        await loadForumData()

+        

         // 跳转到新创建的主题页面

-        router.push('/forum/topic/new')

+        router.push(`/forum/topic/${response.id}`)

         

       } catch (error) {

-        console.error('表单验证失败:', error)

+        console.error('发布主题失败:', error)

+        ElMessage.error('发布主题失败')

       } finally {

         submitting.value = false

       }

@@ -562,6 +507,10 @@
       newTopic.tags = []

     }

     

+    onMounted(() => {

+      loadForumData()

+    })

+    

     return {

       showNewTopicDialog,

       submitting,

@@ -594,7 +543,10 @@
       Monitor,

       GamePad,

       Bell,

-      QuestionFilled

+      QuestionFilled,

+      loadForumData,

+      getForumIcon,

+      getForumColor

     }

   }

 }

diff --git a/src/views/forum/TopicView.vue b/src/views/forum/TopicView.vue
new file mode 100644
index 0000000..c79bfa8
--- /dev/null
+++ b/src/views/forum/TopicView.vue
@@ -0,0 +1,666 @@
+<template>
+  <div class="topic-page">
+    <Navbar />
+    <div class="page-container">
+      <!-- 话题头部 -->
+      <div class="topic-header">
+        <div class="header-content">
+          <div class="topic-title-section">
+            <h1>{{ topic.title }}</h1>
+            <div class="topic-meta">
+              <span class="author">
+                <el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar>
+                <span class="author-name">{{ topic.user?.username || '匿名' }}</span>
+              </span>
+              <span class="time">{{ formatTime(topic.createTime) }}</span>
+              <span class="views">
+                <el-icon><View /></el-icon>
+                {{ topic.views }}
+              </span>
+              <el-button
+                :icon="isSubscribed ? StarFilled : Star"
+                :type="isSubscribed ? 'warning' : 'default'"
+                @click="handleSubscribe"
+              >
+                {{ isSubscribed ? '已订阅' : '订阅' }}
+              </el-button>
+            </div>
+          </div>
+          <div class="topic-actions" v-if="isAuthor">
+            <el-button type="primary" @click="showEditDialog = true">编辑</el-button>
+            <el-button type="danger" @click="handleDelete">删除</el-button>
+          </div>
+        </div>
+      </div>
+
+      <!-- 话题内容 -->
+      <div class="topic-content">
+        <div class="content-card">
+          <div class="content-body" v-html="topic.content"></div>
+          <div class="content-tags">
+            <el-tag
+              v-for="tag in topic.tags"
+              :key="tag.id"
+              :color="tag.color"
+              effect="light"
+            >
+              {{ tag.name }}
+            </el-tag>
+          </div>
+        </div>
+      </div>
+
+
+
+      <!-- 回复列表 -->
+      <div class="replies-section">
+        <h2>回复 ({{ getTotalReplyCount() }})</h2>
+        <div class="replies-list">
+          <!-- 使用递归组件显示所有回复 -->
+          <ReplyTree
+            :replies="replies"
+            :level="0"
+            @reply="replyToPost"
+            @edit="editReply"
+            @delete="deleteReply"
+          />
+        </div>
+      </div>
+
+      <!-- 回复框 -->
+      <div class="reply-box">
+        <h3>{{ replyingTo ? `回复 @${replyingTo.author}` : '发表回复' }}</h3>
+        <div v-if="replyingTo" class="replying-to">
+          <div class="original-content">{{ replyingTo.content }}</div>
+          <el-button size="small" @click="cancelReply">取消回复</el-button>
+        </div>
+        <el-input
+          v-model="newReply"
+          type="textarea"
+          :rows="4"
+          placeholder="请输入回复内容..."
+        />
+        <div class="reply-actions">
+          <el-button type="primary" @click="submitReply" :loading="submitting">
+            发表回复
+          </el-button>
+        </div>
+      </div>
+    </div>
+
+    <!-- 编辑话题对话框 -->
+    <el-dialog
+      v-model="showEditDialog"
+      title="编辑话题"
+      width="600px"
+    >
+      <el-form
+        ref="editFormRef"
+        :model="editForm"
+        :rules="editRules"
+        label-width="80px"
+      >
+        <el-form-item label="标题" prop="title">
+          <el-input v-model="editForm.title" />
+        </el-form-item>
+        <el-form-item label="内容" prop="content">
+          <el-input
+            v-model="editForm.content"
+            type="textarea"
+            :rows="8"
+          />
+        </el-form-item>
+        <el-form-item label="标签">
+          <div class="tags-input">
+            <el-tag
+              v-for="tag in editForm.tags"
+              :key="tag"
+              closable
+              @close="removeTag(tag)"
+            >
+              {{ tag }}
+            </el-tag>
+            <el-input
+              v-if="tagInputVisible"
+              ref="tagInputRef"
+              v-model="tagInputValue"
+              size="small"
+              @keyup.enter="addTag"
+              @blur="addTag"
+              style="width: 100px;"
+            />
+            <el-button
+              v-else
+              size="small"
+              @click="showTagInput"
+            >
+              + 添加标签
+            </el-button>
+          </div>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="showEditDialog = false">取消</el-button>
+        <el-button type="primary" @click="submitEdit" :loading="submitting">
+          保存
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { ref, reactive, onMounted, computed } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { View, Star, StarFilled } from '@element-plus/icons-vue'
+import {
+  getTopicById,
+  updateTopic,
+  deleteTopic
+} from '@/api/topic'
+import {
+  getPostsByTopic,
+  getPostTreeByTopic,
+  createPost,
+  updatePost,
+  deletePost
+} from '@/api/post'
+
+import {
+  getTopicTags,
+  assignTagsToTopic
+} from '@/api/forumTag'
+import {
+  recordTopicView,
+  getUserViewHistory
+} from '@/api/topicView'
+import {
+  subscribeTopic,
+  unsubscribeTopic,
+  checkSubscription,
+  getSubscriptionList
+} from '@/api/subscription'
+import Navbar from "@/components/Navbar.vue";
+import ReplyTree from "@/components/ReplyTree.vue";
+
+export default {
+  name: 'TopicView',
+  components: {Navbar, ReplyTree},
+  setup() {
+    const route = useRoute()
+    const router = useRouter()
+    const topicId = computed(() => route.params.id)
+    
+    const topic = ref({})
+    const replies = ref([])
+    const newReply = ref('')
+    const submitting = ref(false)
+    const showEditDialog = ref(false)
+    const tagInputVisible = ref(false)
+    const tagInputValue = ref('')
+    const replyingTo = ref(null)
+    
+    const editForm = reactive({
+      title: '',
+      content: '',
+      tags: []
+    })
+    
+    const editRules = {
+      title: [
+        { required: true, message: '请输入标题', trigger: 'blur' },
+        { min: 5, max: 100, message: '标题长度在 5 到 100 个字符', trigger: 'blur' }
+      ],
+      content: [
+        { required: true, message: '请输入内容', trigger: 'blur' },
+        { min: 10, max: 5000, message: '内容长度在 10 到 5000 个字符', trigger: 'blur' }
+      ]
+    }
+    
+    const isAuthor = computed(() => {
+      // 这里需要根据实际用户系统来判断
+      return true
+    })
+    
+    const isSubscribed = ref(false)
+    
+    const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
+    const token = localStorage.getItem('token')
+    
+    const loadTopicData = async () => {
+      try {
+        // 获取话题详情
+        const response = await getTopicById(topicId.value)
+        topic.value = response
+        editForm.title = response.title
+        editForm.content = response.content
+        
+        // 获取话题标签
+        const tags = await getTopicTags(topicId.value)
+        editForm.tags = tags.map(tag => tag.name)
+        
+        // 获取树形回复结构
+        const posts = await getPostTreeByTopic(topicId.value)
+        replies.value = posts
+        
+
+        
+        // 检查订阅状态
+        if (token && userInfo.id) {
+          const subscriptionStatus = await checkSubscription({
+            topicId: topicId.value,
+            userId: userInfo.id
+          })
+          isSubscribed.value = subscriptionStatus
+        }
+        
+        // 记录浏览
+        if (token && userInfo.id) {
+          await recordTopicView({
+            topicId: topicId.value,
+            userId: userInfo.id
+          })
+        }
+      } catch (error) {
+        console.error('加载话题数据失败:', error)
+        ElMessage.error('加载话题数据失败')
+      }
+    }
+    
+
+    
+    const handleSubscribe = async () => {
+      try {
+        if (isSubscribed.value) {
+          await unsubscribeTopic({ topicId: topicId.value })
+          isSubscribed.value = false
+          ElMessage.success('已取消订阅')
+        } else {
+          await subscribeTopic({ topicId: topicId.value })
+          isSubscribed.value = true
+          ElMessage.success('已订阅话题')
+        }
+      } catch (error) {
+        console.error('订阅操作失败:', error)
+        ElMessage.error('订阅操作失败')
+      }
+    }
+    
+    const formatTime = (timeString) => {
+      const date = new Date(timeString)
+      const now = new Date()
+      const diff = now - date
+      const hours = Math.floor(diff / (1000 * 60 * 60))
+      
+      if (hours < 1) return '刚刚'
+      if (hours < 24) return `${hours}小时前`
+      const days = Math.floor(hours / 24)
+      return `${days}天前`
+    }
+    
+    const handleDelete = async () => {
+      try {
+        await ElMessageBox.confirm(
+          '确定要删除这个话题吗?此操作不可恢复。',
+          '警告',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }
+        )
+        
+        await deleteTopic(topicId.value)
+        ElMessage.success('话题已删除')
+        router.push('/forum')
+      } catch (error) {
+        if (error !== 'cancel') {
+          console.error('删除话题失败:', error)
+          ElMessage.error('删除话题失败')
+        }
+      }
+    }
+    
+    const submitEdit = async () => {
+      try {
+        submitting.value = true
+        const updatedTopic = {
+          ...topic.value,
+          title: editForm.title,
+          content: editForm.content
+        }
+        
+        await updateTopic(topicId.value, updatedTopic)
+        
+        // 更新标签
+        await assignTagsToTopic(topicId.value, editForm.tags)
+        
+        ElMessage.success('话题已更新')
+        showEditDialog.value = false
+        await loadTopicData()
+      } catch (error) {
+        console.error('更新话题失败:', error)
+        ElMessage.error('更新话题失败')
+      } finally {
+        submitting.value = false
+      }
+    }
+    
+    const submitReply = async () => {
+      if (!newReply.value.trim()) {
+        ElMessage.warning('请输入回复内容')
+        return
+      }
+      
+      try {
+        submitting.value = true
+        const reply = {
+          topicId: topicId.value,
+          content: newReply.value,
+          parentId: replyingTo.value ? replyingTo.value.id : null
+        }
+        
+        await createPost(reply)
+        ElMessage.success('回复已发布')
+        newReply.value = ''
+        replyingTo.value = null
+        await loadTopicData()
+      } catch (error) {
+        console.error('发布回复失败:', error)
+        ElMessage.error('发布回复失败')
+      } finally {
+        submitting.value = false
+      }
+    }
+
+    const replyToPost = (post) => {
+      replyingTo.value = {
+        id: post.id,
+        author: post.user?.username || '匿名',
+        content: post.content.substring(0, 100) + (post.content.length > 100 ? '...' : '')
+      }
+      // 滚动到回复框
+      document.querySelector('.reply-box').scrollIntoView({ behavior: 'smooth' })
+    }
+
+    const cancelReply = () => {
+      replyingTo.value = null
+    }
+
+    // 递归计算所有回复的总数(包括嵌套回复)
+    const countReplies = (posts) => {
+      let count = 0
+      for (const post of posts) {
+        count += 1
+        if (post.replies && post.replies.length > 0) {
+          count += countReplies(post.replies)
+        }
+      }
+      return count
+    }
+
+    const getTotalReplyCount = () => {
+      return countReplies(replies.value)
+    }
+
+
+    
+    const editReply = (reply) => {
+      // 实现编辑回复的逻辑
+    }
+    
+    const deleteReply = async (replyId) => {
+      try {
+        await ElMessageBox.confirm(
+          '确定要删除这条回复吗?',
+          '警告',
+          {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }
+        )
+        
+        await softDeletePost(replyId)
+        ElMessage.success('回复已删除')
+        await loadTopicData()
+      } catch (error) {
+        if (error !== 'cancel') {
+          console.error('删除回复失败:', error)
+          ElMessage.error('删除回复失败')
+        }
+      }
+    }
+    
+    const showTagInput = () => {
+      tagInputVisible.value = true
+      nextTick(() => {
+        tagInputRef.value?.focus()
+      })
+    }
+    
+    const addTag = () => {
+      const tag = tagInputValue.value.trim()
+      if (tag && !editForm.tags.includes(tag)) {
+        editForm.tags.push(tag)
+      }
+      tagInputVisible.value = false
+      tagInputValue.value = ''
+    }
+    
+    const removeTag = (tag) => {
+      const index = editForm.tags.indexOf(tag)
+      if (index > -1) {
+        editForm.tags.splice(index, 1)
+      }
+    }
+    
+    onMounted(() => {
+      loadTopicData()
+    })
+    
+          return {
+      topic,
+      replies,
+      newReply,
+      submitting,
+      showEditDialog,
+      tagInputVisible,
+      tagInputValue,
+      replyingTo,
+      editForm,
+      editRules,
+      isAuthor,
+      formatTime,
+      handleDelete,
+      submitEdit,
+      submitReply,
+      replyToPost,
+      cancelReply,
+      getTotalReplyCount,
+      editReply,
+      deleteReply,
+      showTagInput,
+      addTag,
+      removeTag,
+      View,
+      Star,
+      StarFilled,
+      isSubscribed,
+      handleSubscribe
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.topic-page {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 24px;
+  background: #f5f5f5;
+  min-height: 100vh;
+}
+
+.topic-header {
+  background: #fff;
+  border-radius: 12px;
+  padding: 24px;
+  margin-bottom: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  .header-content {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    
+    .topic-title-section {
+      h1 {
+        font-size: 24px;
+        font-weight: 600;
+        color: #2c3e50;
+        margin: 0 0 16px 0;
+      }
+      
+      .topic-meta {
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        color: #7f8c8d;
+        font-size: 14px;
+        
+        .author {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+        }
+        
+        .views {
+          display: flex;
+          align-items: center;
+          gap: 4px;
+        }
+      }
+    }
+    
+    .topic-actions {
+      display: flex;
+      gap: 12px;
+    }
+  }
+}
+
+.topic-content {
+  margin-bottom: 24px;
+  
+  .content-card {
+    background: #fff;
+    border-radius: 12px;
+    padding: 24px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+    
+    .content-body {
+      font-size: 16px;
+      line-height: 1.6;
+      color: #2c3e50;
+      margin-bottom: 24px;
+    }
+    
+    .content-tags {
+      display: flex;
+      gap: 8px;
+      flex-wrap: wrap;
+    }
+  }
+}
+
+
+
+.replies-section {
+  background: #fff;
+  border-radius: 12px;
+  padding: 24px;
+  margin-bottom: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  h2 {
+    font-size: 20px;
+    font-weight: 600;
+    color: #2c3e50;
+    margin: 0 0 20px 0;
+  }
+}
+
+.reply-box {
+  background: #fff;
+  border-radius: 12px;
+  padding: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  h3 {
+    font-size: 18px;
+    font-weight: 600;
+    color: #2c3e50;
+    margin: 0 0 16px 0;
+  }
+  
+  .replying-to {
+    background: #f8f9fa;
+    border-left: 4px solid #3498db;
+    padding: 12px 16px;
+    margin-bottom: 16px;
+    border-radius: 4px;
+    
+    .original-content {
+      color: #666;
+      font-size: 14px;
+      margin-bottom: 8px;
+      font-style: italic;
+    }
+  }
+  
+  .reply-actions {
+    margin-top: 16px;
+    display: flex;
+    justify-content: flex-end;
+  }
+}
+
+.tags-input {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  align-items: center;
+  
+  .el-tag {
+    margin: 0;
+  }
+}
+
+
+
+.reply-actions {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+@media (max-width: 768px) {
+  .topic-page {
+    padding: 16px;
+  }
+  
+  .topic-header {
+    .header-content {
+      flex-direction: column;
+      
+      .topic-actions {
+        margin-top: 16px;
+        width: 100%;
+        justify-content: flex-end;
+      }
+    }
+  }
+}
+</style> 
\ No newline at end of file
diff --git a/src/views/torrent/TorrentDetailView.vue b/src/views/torrent/TorrentDetailView.vue
index 1780d68..046c00b 100644
--- a/src/views/torrent/TorrentDetailView.vue
+++ b/src/views/torrent/TorrentDetailView.vue
@@ -19,7 +19,7 @@
         <div class="torrent-header">
           <div class="header-content">
             <div class="torrent-cover">
-              <el-image
+              <!-- <el-image
                 :src="torrentInfo.coverImage || '/default-cover.jpg'"
                 :alt="torrentInfo.title"
                 fit="cover"
@@ -31,6 +31,19 @@
                     <span>暂无封面</span>
                   </div>
                 </template>
+              </el-image> -->
+              <el-image
+                :src="require('@/views/图片.png')"
+                :alt="torrentInfo.title"
+                fit="cover"
+                class="cover-image"
+              >
+                <template #error>
+                  <div class="image-placeholder">
+                    <el-icon size="48"><Picture /></el-icon>
+                    <span>暂无封面</span>
+                  </div>
+                </template>
               </el-image>
             </div>
             
@@ -70,10 +83,10 @@
                   <el-icon><Document /></el-icon>
                   <span>文件大小:{{ formatFileSize(torrentInfo.size) }}</span>
                 </div>
-                <div class="meta-item">
+                <!-- <div class="meta-item">
                   <el-icon><Files /></el-icon>
                   <span>完成次数:{{ torrentInfo.finishes }} 次</span>
-                </div>
+                </div> -->
                 <div class="meta-item">
                   <el-icon><Star /></el-icon>
                   <span>推广策略:{{ torrentInfo.promotionPolicy?.displayName || '默认' }}</span>
@@ -105,7 +118,7 @@
                 >
                   {{ downloading ? '准备中...' : '下载种子' }}
                 </el-button>
-                <el-button 
+                <!-- <el-button 
                   type="success" 
                   size="large" 
                   :icon="Star"
@@ -120,7 +133,7 @@
                   @click="handleReport"
                 >
                   举报
-                </el-button>
+                </el-button> -->
               </div>
             </div>
           </div>
diff --git a/src/views/torrent/UploadView.vue b/src/views/torrent/UploadView.vue
index 490a138..9807cd1 100644
--- a/src/views/torrent/UploadView.vue
+++ b/src/views/torrent/UploadView.vue
@@ -1,6 +1,5 @@
 <template>

   <div class="upload-page">

-    <Navbar />

     <div class="upload-container">

       <h2>上传种子</h2>

       

@@ -103,12 +102,9 @@
 import { useRouter } from 'vue-router'

 import { ElMessage } from 'element-plus'

 import { uploadTorrent, getCategories, getTags } from '@/api/torrent'

-import Navbar from '@/components/Navbar.vue'

+

 export default {

   name: 'UploadView',

-  components: {

-    Navbar

-  },

   setup() {

     const router = useRouter()

     const uploadFormRef = ref(null)

diff --git "a/src/views/\345\233\276\347\211\207.png" "b/src/views/\345\233\276\347\211\207.png"
new file mode 100644
index 0000000..59daad9
--- /dev/null
+++ "b/src/views/\345\233\276\347\211\207.png"
Binary files differ