修改前端页面

Change-Id: I344849fc3dfca5725e0ef2a107e2c89a14fce8da
diff --git a/front/src/AnimePage.js b/front/src/AnimePage.js
index 118d8fb..03ad8b2 100644
--- a/front/src/AnimePage.js
+++ b/front/src/AnimePage.js
@@ -1,4 +1,5 @@
-import React from "react";

+import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -7,11 +8,14 @@
 import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

 import PersonIcon from "@mui/icons-material/Person";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import ForumIcon from "@mui/icons-material/Forum";

+import HelpIcon from "@mui/icons-material/Help";

 import "./App.css";

 import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -19,15 +23,9 @@
   { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

   { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

   { label: "资料", icon: <PersonIcon />, path: "/info" },

-  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

-];

-

-const animeTypes = [

-  "华语动漫(大陆)",

-  "欧美动漫",

-  "日韩动漫",

-  "港台动漫",

-  "其他"

+  { label: "论坛", icon: <ForumIcon />, path: "/forum" },

+  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" },

+  { label: "求种", icon: <HelpIcon />, path: "/begseed" },

 ];

 

 const areaTabs = [

@@ -35,26 +33,45 @@
   { label: "日漫", icon: <EmailIcon fontSize="small" /> },

   { label: "欧美动漫", icon: <PersonIcon fontSize="small" /> },

   { label: "韩漫", icon: <EmojiPeopleIcon fontSize="small" /> },

-  // { label: "其他", icon: <PersonIcon fontSize="small" /> },

 ];

 

 export default function AnimePage() {

   const navigate = useNavigate();

-  const [activeTab, setActiveTab] = React.useState(0);

-  const [animeList, setAnimeList] = React.useState([]);

+  const [searchText, setSearchText] = useState('');

+  const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

+  const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

+  const [activeTab, setActiveTab] = useState(0);

+  const [animeList, setAnimeList] = useState([]);

+

+  useEffect(() => {

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+    if (!userId) return;

+    fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

+      .then(res => res.json())

+      .then(data => {

+        setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

+        setUserPT({

+          magic: data.magic_value || data.magic || 0,

+          ratio: data.share_ratio || data.share || 0,

+          upload: data.upload_amount || data.upload || 0,

+          download: data.download_amount || data.download || 0,

+        });

+      })

+      .catch(err => console.error('Fetching user profile failed', err));

+  }, []);

 

   // 每个tab对应的动漫类型

   const animeTypesList = [

-    ["华语动漫(大陆)", "欧美动漫", "日韩动漫", "港台动漫", "其他"], // 大陆

-    ["港台热血", "港台搞笑", "港台其他"], // 港台

-    ["欧美冒险", "欧美科幻", "欧美其他"], // 欧美

-    ["日韩热血", "日韩恋爱", "日韩其他"], // 日韩

+    ["华语动漫(大陆)", "欧美动漫", "日韩动漫", "港台动漫", "其他"], // 国创

+    ["日漫热血", "日漫搞笑", "日漫其他"], // 日漫

+    ["欧美冒险", "欧美科幻", "欧美其他"], // 欧美动漫

+    ["韩漫爱情", "韩漫奇幻", "韩漫其他"], // 韩漫

     ["其他类型1", "其他类型2"] // 其他

   ];

   const animeTypes = animeTypesList[activeTab] || [];

 

-  React.useEffect(() => {

-    // 假设后端接口为 /api/animes?area=大陆

+  useEffect(() => {

     const area = areaTabs[activeTab].label;

     fetch(`${API_BASE_URL}/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

       .then(res => res.json())

@@ -62,25 +79,40 @@
       .catch(() => setAnimeList([]));

   }, [activeTab]);

 

+  // 搜索按钮处理

+  const handleSearch = () => {

+    const area = areaTabs[activeTab].label;

+    fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

+      .then(res => res.json())

+      .then(data => {

+        setAnimeList(data);

+      })

+      .catch(() => setAnimeList([]));

+  };

+

   return (

     <div className="container">

-      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      {/* 顶部空白与动漫界面一致,用户栏绝对定位在页面右上角 */}

       <div style={{ height: 80 }} />

       <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

         <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

-          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+          {userInfo.avatar_url ? (

+            <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />

+          ) : (

+            <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+          )}

         </div>

-        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>{userInfo.username || '用户栏'}</div>

         <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>{userPT.magic}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>{userPT.ratio}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>{userPT.upload}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>{userPT.download}</b></span>

         </div>

       </div>

-      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      {/* 下方内容整体下移,留出与动漫界面一致的间距 */}

       <div style={{ height: 32 }} />

-      <nav className="nav-bar">

+      <nav className="nav-bar card">

         {navItems.map((item) => (

           <div

             key={item.label}

@@ -93,8 +125,13 @@
         ))}

       </nav>

       <div className="search-section card">

-        <input className="search-input" placeholder="输入搜索关键词" />

-        <button className="search-btn">

+        <input

+          className="search-input"

+          placeholder="输入搜索关键词"

+          value={searchText}

+          onChange={e => setSearchText(e.target.value)}

+        />

+        <button className="search-btn" onClick={handleSearch}>

           <span role="img" aria-label="search">🔍</span>

         </button>

       </div>

@@ -116,6 +153,9 @@
               <th>动漫类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

@@ -132,7 +172,10 @@
                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

@@ -149,6 +192,9 @@
                     </a>

                   </td>

                   <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

@@ -172,4 +218,4 @@
       <span className="page-info">第 <b>{page}</b> 页</span>

     </div>

   );

-}

+}
\ No newline at end of file
diff --git a/front/src/GamePage.js b/front/src/GamePage.js
index 1de4d30..cae28cd 100644
--- a/front/src/GamePage.js
+++ b/front/src/GamePage.js
@@ -1,4 +1,5 @@
-import React, { useState } from "react";

+import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -7,11 +8,14 @@
 import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

 import PersonIcon from "@mui/icons-material/Person";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import ForumIcon from "@mui/icons-material/Forum";

+import HelpIcon from "@mui/icons-material/Help";

 import "./App.css";

 import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -19,15 +23,9 @@
   { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

   { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

   { label: "资料", icon: <PersonIcon />, path: "/info" },

-  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

-];

-

-const gameTypesList = [

-  ["PC"],

-  ["主机"],

-  ["移动"],

-  ["掌机"],

-  ["视频"]

+  { label: "论坛", icon: <ForumIcon />, path: "/forum" },

+  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" },

+  { label: "求种", icon: <HelpIcon />, path: "/begseed" },

 ];

 

 const areaTabs = [

@@ -38,14 +36,45 @@
   { label: "视频", icon: <PersonIcon fontSize="small" /> },

 ];

 

+// 每个tab对应的游戏类型

+const gameTypesList = [

+  ["PC单机", "PC网络", "PC独立", "PC其他"], // PC

+  ["主机动作", "主机RPG", "主机其他"], // 主机

+  ["移动休闲", "移动竞技", "移动其他"], // 移动

+  ["掌机冒险", "掌机其他"], // 掌机

+  ["游戏视频", "赛事视频", "其他视频"], // 视频

+  ["其他类型1", "其他类型2"] // 其他

+];

+

 export default function GamePage() {

   const navigate = useNavigate();

+  const [searchText, setSearchText] = useState('');

+  const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

+  const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

   const [activeTab, setActiveTab] = useState(0);

   const [gameList, setGameList] = useState([]);

 

+  useEffect(() => {

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+    if (!userId) return;

+    fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

+      .then(res => res.json())

+      .then(data => {

+        setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

+        setUserPT({

+          magic: data.magic_value || data.magic || 0,

+          ratio: data.share_ratio || data.share || 0,

+          upload: data.upload_amount || data.upload || 0,

+          download: data.download_amount || data.download || 0,

+        });

+      })

+      .catch(err => console.error('Fetching user profile failed', err));

+  }, []);

+

   const gameTypes = gameTypesList[activeTab] || [];

 

-  React.useEffect(() => {

+  useEffect(() => {

     const area = areaTabs[activeTab].label;

     fetch(`${API_BASE_URL}/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

       .then(res => res.json())

@@ -53,68 +82,38 @@
       .catch(() => setGameList([]));

   }, [activeTab]);

 

+  // 搜索按钮处理

+  const handleSearch = () => {

+    const area = areaTabs[activeTab].label;

+    fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

+      .then(res => res.json())

+      .then(data => {

+        setGameList(data);

+      })

+      .catch(() => setGameList([]));

+  };

+

   return (

     <div className="container">

-      {/* 预留顶部空间用于放图片 */}

+      {/* 顶部空白与游戏界面一致,用户栏绝对定位在页面右上角 */}

       <div style={{ height: 80 }} />

-      <div

-        className="user-bar"

-        style={{

-          position: "fixed",

-          top: 18,

-          right: 42,

-          zIndex: 100,

-          display: "flex",

-          alignItems: "center",

-          background: "#e0f3ff",

-          borderRadius: 12,

-          padding: "6px 18px",

-          boxShadow: "0 2px 8px #b2d8ea",

-          minWidth: 320,

-          minHeight: 48,

-          width: 420,

-        }}

-      >

-        <div

-          style={{ cursor: "pointer", marginRight: 16 }}

-          onClick={() => navigate("/user")}

-        >

-          <AccountCircleIcon

-            style={{

-              fontSize: 38,

-              color: "#1a237e",

-              background: "#e0f3ff",

-              borderRadius: "50%",

-            }}

-          />

+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

+          {userInfo.avatar_url ? (

+            <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />

+          ) : (

+            <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+          )}

         </div>

-        <div style={{ color: "#222", fontWeight: 500, marginRight: 24 }}>

-          用户栏

-        </div>

-        <div

-          style={{

-            display: "flex",

-            gap: 28,

-            flex: 1,

-            justifyContent: "flex-end",

-            alignItems: "center",

-          }}

-        >

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            魔力值: <b>12345</b>

-          </span>

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            分享率: <b>2.56</b>

-          </span>

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            上传量: <b>100GB</b>

-          </span>

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            下载量: <b>50GB</b>

-          </span>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>{userInfo.username || '用户栏'}</div>

+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>{userPT.magic}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>{userPT.ratio}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>{userPT.upload}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>{userPT.download}</b></span>

         </div>

       </div>

-      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      {/* 下方内容整体下移,留出与游戏界面一致的间距 */}

       <div style={{ height: 32 }} />

       <nav className="nav-bar card">

         {navItems.map((item) => (

@@ -129,22 +128,17 @@
         ))}

       </nav>

       <div className="search-section card">

-        <input className="search-input" placeholder="输入搜索关键词" />

-        <button className="search-btn">

-          <span role="img" aria-label="search">

-            🔍

-          </span>

+        <input

+          className="search-input"

+          placeholder="输入搜索关键词"

+          value={searchText}

+          onChange={e => setSearchText(e.target.value)}

+        />

+        <button className="search-btn" onClick={handleSearch}>

+          <span role="img" aria-label="search">🔍</span>

         </button>

       </div>

-      <div

-        className="area-tabs"

-        style={{

-          display: "flex",

-          justifyContent: "center",

-          gap: 24,

-          margin: "18px 0",

-        }}

-      >

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

         {areaTabs.map((tab, idx) => (

           <div

             key={tab.label}

@@ -162,39 +156,48 @@
               <th>游戏类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

             {gameList.length > 0 ? (

-              gameList.map((item, idx) => (

-                <tr key={item.id || idx}>

+              gameList.map((item, index) => (

+                <tr key={item.id || index}>

                   <td>

-                    <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>

+                    <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>

                       {item.seedtag}

                     </a>

                   </td>

                   <td>

-                    <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>

+                    <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>

                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

-              gameTypes.map((type, idx) => (

+              gameTypes.map((type, index) => (

                 <tr key={type}>

                   <td>

-                    <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>

+                    <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>

                       {type}

                     </a>

                   </td>

                   <td>

-                    <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>

-                      种子{idx + 1}

+                    <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>

+                      种子{index + 1}

                     </a>

                   </td>

-                  <td></td>

+                  <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

@@ -212,24 +215,10 @@
   const total = 5;

   return (

     <div className="pagination">

-      <button

-        onClick={() => setPage((p) => Math.max(1, p - 1))}

-        disabled={page === 1}

-      >

-        上一页

-      </button>

-      <span className="page-num">

-        {page}/{total}

-      </span>

-      <button

-        onClick={() => setPage((p) => Math.min(total, p + 1))}

-        disabled={page === total}

-      >

-        下一页

-      </button>

-      <span className="page-info">

-        第 <b>{page}</b> 页

-      </span>

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

     </div>

   );

-}

+}
\ No newline at end of file
diff --git a/front/src/InfoPage.js b/front/src/InfoPage.js
index 0ed6ade..cec0b4d 100644
--- a/front/src/InfoPage.js
+++ b/front/src/InfoPage.js
@@ -1,4 +1,5 @@
-import React from "react";

+import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -7,11 +8,14 @@
 import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

 import PersonIcon from "@mui/icons-material/Person";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import ForumIcon from "@mui/icons-material/Forum";

+import HelpIcon from "@mui/icons-material/Help";

 import "./App.css";

 import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -19,29 +23,44 @@
   { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

   { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

   { label: "资料", icon: <PersonIcon />, path: "/info" },

-  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

+  { label: "论坛", icon: <ForumIcon />, path: "/forum" },

+  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" },

+  { label: "求种", icon: <HelpIcon />, path: "/begseed" },

 ];

 

-const infoTypes = [""];

-

 const areaTabs = [

   { label: "出版物", icon: <MovieIcon fontSize="small" /> },

   { label: "学习教程", icon: <EmailIcon fontSize="small" /> },

   { label: "素材模板", icon: <PersonIcon fontSize="small" /> },

   { label: "演讲交流", icon: <EmojiPeopleIcon fontSize="small" /> },

-  { label: "日常娱乐", icon: <PersonIcon fontSize="small" />, active: true },

-];

-

-const exampleTorrents = [

-  { type: "Documentary", title: "实例1", id: 1 },

-  { type: "Biography", title: "实例2", id: 2 },

-  { type: "History", title: "实例3", id: 3 },

+  { label: "日常娱乐", icon: <PersonIcon fontSize="small" /> },

 ];

 

 export default function InfoPage() {

   const navigate = useNavigate();

-  const [activeTab, setActiveTab] = React.useState(0);

-  const [infoList, setInfoList] = React.useState(0);

+  const [searchText, setSearchText] = useState('');

+  const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

+  const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

+  const [activeTab, setActiveTab] = useState(0);

+  const [infoList, setInfoList] = useState([]);

+

+  useEffect(() => {

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+    if (!userId) return;

+    fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

+      .then(res => res.json())

+      .then(data => {

+        setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

+        setUserPT({

+          magic: data.magic_value || data.magic || 0,

+          ratio: data.share_ratio || data.share || 0,

+          upload: data.upload_amount || data.upload || 0,

+          download: data.download_amount || data.download || 0,

+        });

+      })

+      .catch(err => console.error('Fetching user profile failed', err));

+  }, []);

 

   // 每个tab对应的资料类型

   const infoTypesList = [

@@ -50,83 +69,52 @@
     ["模板A", "模板B"], // 素材模板

     ["演讲A", "演讲B"], // 演讲交流

     ["娱乐A", "娱乐B"], // 日常娱乐

+    ["其他类型1", "其他类型2"] // 其他

   ];

   const infoTypes = infoTypesList[activeTab] || [];

 

-  React.useEffect(() => {

-    // 这里假设后端接口为 /api/get-seed-list-by-tag?tag=大陆

+  useEffect(() => {

     const area = areaTabs[activeTab].label;

     fetch(`${API_BASE_URL}/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

       .then(res => res.json())

       .then(data => {

-        console.log('资料区返回数据:', data);

         setInfoList(data);

       })

       .catch(() => setInfoList([]));

   }, [activeTab]);

 

+  // 搜索按钮处理

+  const handleSearch = () => {

+    const area = areaTabs[activeTab].label;

+    fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

+      .then(res => res.json())

+      .then(data => {

+        setInfoList(data);

+      })

+      .catch(() => setInfoList([]));

+  };

+

   return (

     <div className="container">

-      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      {/* 顶部空白与资料界面一致,用户栏绝对定位在页面右上角 */}

       <div style={{ height: 80 }} />

-      <div

-        className="user-bar"

-        style={{

-          position: "fixed",

-          top: 18,

-          right: 42,

-          zIndex: 100,

-          display: "flex",

-          alignItems: "center",

-          background: "#e0f3ff",

-          borderRadius: 12,

-          padding: "6px 18px",

-          boxShadow: "0 2px 8px #b2d8ea",

-          minWidth: 320,

-          minHeight: 48,

-          width: 420,

-        }}

-      >

-        <div

-          style={{ cursor: "pointer", marginRight: 16 }}

-          onClick={() => navigate("/user")}

-        >

-          <AccountCircleIcon

-            style={{

-              fontSize: 38,

-              color: "#1a237e",

-              background: "#e0f3ff",

-              borderRadius: "50%",

-            }}

-          />

+      <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

+        <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

+          {userInfo.avatar_url ? (

+            <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />

+          ) : (

+            <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+          )}

         </div>

-        <div style={{ color: "#222", fontWeight: 500, marginRight: 24 }}>

-          用户栏

-        </div>

-        <div

-          style={{

-            display: "flex",

-            gap: 28,

-            flex: 1,

-            justifyContent: "flex-end",

-            alignItems: "center",

-          }}

-        >

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            魔力值: <b>12345</b>

-          </span>

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            分享率: <b>2.56</b>

-          </span>

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            上传量: <b>100GB</b>

-          </span>

-          <span style={{ color: "#1976d2", fontWeight: 500 }}>

-            下载量: <b>50GB</b>

-          </span>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>{userInfo.username || '用户栏'}</div>

+        <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>{userPT.magic}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>{userPT.ratio}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>{userPT.upload}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>{userPT.download}</b></span>

         </div>

       </div>

-      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      {/* 下方内容整体下移,留出与资料界面一致的间距 */}

       <div style={{ height: 32 }} />

       <nav className="nav-bar card">

         {navItems.map((item) => (

@@ -141,22 +129,17 @@
         ))}

       </nav>

       <div className="search-section card">

-        <input className="search-input" placeholder="输入搜索关键词" />

-        <button className="search-btn">

-          <span role="img" aria-label="search">

-            🔍

-          </span>

+        <input

+          className="search-input"

+          placeholder="输入搜索关键词"

+          value={searchText}

+          onChange={e => setSearchText(e.target.value)}

+        />

+        <button className="search-btn" onClick={handleSearch}>

+          <span role="img" aria-label="search">🔍</span>

         </button>

       </div>

-      <div

-        className="area-tabs"

-        style={{

-          display: "flex",

-          justifyContent: "center",

-          gap: 24,

-          margin: "18px 0",

-        }}

-      >

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

         {areaTabs.map((tab, idx) => (

           <div

             key={tab.label}

@@ -174,6 +157,9 @@
               <th>资料类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

@@ -190,11 +176,14 @@
                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

-              infoTypesList.map((type, index) => (

+              infoTypes.map((type, index) => (

                 <tr key={type}>

                   <td>

                     <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>

@@ -207,6 +196,9 @@
                     </a>

                   </td>

                   <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

@@ -224,24 +216,10 @@
   const total = 5;

   return (

     <div className="pagination">

-      <button

-        onClick={() => setPage((p) => Math.max(1, p - 1))}

-        disabled={page === 1}

-      >

-        上一页

-      </button>

-      <span className="page-num">

-        {page}/{total}

-      </span>

-      <button

-        onClick={() => setPage((p) => Math.min(total, p + 1))}

-        disabled={page === total}

-      >

-        下一页

-      </button>

-      <span className="page-info">

-        第 <b>{page}</b> 页

-      </span>

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

     </div>

   );

-}

+}
\ No newline at end of file
diff --git a/front/src/MigrationPage.js b/front/src/MigrationPage.js
index 1a9f79b..b8235e4 100644
--- a/front/src/MigrationPage.js
+++ b/front/src/MigrationPage.js
@@ -24,6 +24,7 @@
     const [selectedId, setSelectedId] = useState(null);
     const [loading, setLoading] = useState(true);
     const [error, setError] = useState(null);
+    const [grantedUploadInput, setGrantedUploadInput] = useState(0);
 
     // Helper to load migrations list
     const fetchMigrations = async () => {
@@ -32,7 +33,6 @@
             const res = await fetch(`${API_BASE_URL}/api/migrations`);
             if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
             const data = await res.json();
-            console.log("Fetched migrations:", data);
             const formatted = data.map(item => ({
                 migration_id: item.profileurl,
                 user_id: item.user.userid,
@@ -57,18 +57,34 @@
         fetchMigrations();
     }, []);
 
-    if (loading) return <div>加载中...</div>;
-    if (error) return <div>加载失败:{error}</div>;
-
+    // 获取当前选中的迁移申请
     const selectedMigration = migrations.find(m => m.migration_id === selectedId) || {};
 
-    // Approve selected migration and refresh
+    // 每次切换迁移申请时,重置输入框为0或当前已迁移量
+    useEffect(() => {
+        // 审核通过后,输入框应为已迁移量,否则为0
+        if (selectedMigration.approved === 1) {
+            setGrantedUploadInput(selectedMigration.granted_uploaded || 0);
+        } else {
+            setGrantedUploadInput(0);
+        }
+    }, [selectedId, migrations, selectedMigration.approved, selectedMigration.granted_uploaded]);
+
+    // 审核通过
     const handleApprove = async () => {
+        const max = selectedMigration.pending_uploaded || 0;
+        let value = Number(grantedUploadInput);
+        if (isNaN(value) || value < 0) value = 0;
+        if (value > max) value = max;
+
         try {
             const res = await fetch(`${API_BASE_URL}/api/migrations-approve`, {
                 method: "POST",
                 headers: { "Content-Type": "application/json" },
-                body: JSON.stringify({ migration_id: selectedMigration.migration_id })
+                body: JSON.stringify({
+                    migration_id: selectedMigration.migration_id,
+                    granted_uploaded: value
+                })
             });
             if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
             alert("已通过迁移");
@@ -77,13 +93,17 @@
             alert(`操作失败:${err.message}`);
         }
     };
-    // Reject selected migration and refresh
+
+    // 审核不通过
     const handleReject = async () => {
         try {
             const res = await fetch(`${API_BASE_URL}/api/migrations-reject`, {
                 method: "POST",
                 headers: { "Content-Type": "application/json" },
-                body: JSON.stringify({ migration_id: selectedMigration.migration_id })
+                body: JSON.stringify({
+                    migration_id: selectedMigration.migration_id,
+                    granted_uploaded: 0
+                })
             });
             if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
             alert("已拒绝迁移");
@@ -93,6 +113,9 @@
         }
     };
 
+    if (loading) return <div>加载中...</div>;
+    if (error) return <div>加载失败:{error}</div>;
+
     return (
         <div style={{ display: "flex", minHeight: "100vh", background: "#f7faff" }}>
             {/* 侧栏 */}
@@ -143,12 +166,32 @@
                         <FileViewer url={selectedMigration.application_url} />
                     </div>
                     <div style={{ marginBottom: 18 }}>
-                        <b>待迁移魔法值:</b>{selectedMigration.pending_magic},
-                        <b>已迁移魔法值:</b>{selectedMigration.granted_magic}
-                    </div>
-                    <div style={{ marginBottom: 18 }}>
                         <b>待迁移上传量:</b>{selectedMigration.pending_uploaded},
-                        <b>已迁移上传量:</b>{selectedMigration.granted_uploaded}
+                        <b>已迁移上传量:</b>
+                        {selectedMigration.approved === 1 ? (
+                            <span>{selectedMigration.granted_uploaded}</span>
+                        ) : (
+                            <input
+                                type="number"
+                                min={0}
+                                max={selectedMigration.pending_uploaded}
+                                value={grantedUploadInput}
+                                onChange={e => {
+                                    let val = Number(e.target.value);
+                                    if (isNaN(val) || val < 0) val = 0;
+                                    if (val > selectedMigration.pending_uploaded) val = selectedMigration.pending_uploaded;
+                                    setGrantedUploadInput(val);
+                                }}
+                                style={{
+                                    width: 100,
+                                    marginLeft: 8,
+                                    padding: "4px 8px",
+                                    borderRadius: 4,
+                                    border: "1px solid #bdbdbd"
+                                }}
+                                disabled={selectedMigration.approved === 1}
+                            />
+                        )}
                     </div>
                 </div>
                 {/* 审核按钮 */}
diff --git a/front/src/MoviePage.js b/front/src/MoviePage.js
index cdc8112..79f2c8b 100644
--- a/front/src/MoviePage.js
+++ b/front/src/MoviePage.js
@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -9,11 +10,12 @@
 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

 import ForumIcon from "@mui/icons-material/Forum";

 import HelpIcon from "@mui/icons-material/Help";

-import { useNavigate } from "react-router-dom";

 import "./App.css";

+import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -31,13 +33,6 @@
   { label: "港台", icon: <EmailIcon fontSize="small" /> },

   { label: "欧美", icon: <PersonIcon fontSize="small" /> },

   { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },

-  // { label: "其他", icon: <PersonIcon fontSize="small" /> },

-];

-

-const exampleTorrents = [

-  { type: "Action", title: "实例1", id: 1 },

-  { type: "Drama", title: "实例2", id: 2 },

-  { type: "Comedy", title: "实例3", id: 3 },

 ];

 

 export default function MoviePage() {

@@ -47,17 +42,14 @@
   const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

   const [activeTab, setActiveTab] = React.useState(0);

   const [movieList, setMovieList] = React.useState([]);

-  

 

   useEffect(() => {

     const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

     const userId = match ? match[2] : null;

-    console.log("User ID from cookie:", userId);

     if (!userId) return;

     fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

       .then(res => res.json())

       .then(data => {

-        console.log('用户PT信息:', data); 

         setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

         setUserPT({

           magic: data.magic_value || data.magic || 0,

@@ -80,12 +72,10 @@
   const movieTypes = movieTypesList[activeTab] || [];

 

   React.useEffect(() => {

-    // 这里假设后端接口为 /api/get-seed-list-by-tag?tag=大陆

     const area = areaTabs[activeTab].label;

     fetch(`${API_BASE_URL}/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

       .then(res => res.json())

       .then(data => {

-        console.log('电影区返回数据:', data);

         setMovieList(data);

       })

       .catch(() => setMovieList([]));

@@ -97,7 +87,6 @@
     fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

       .then(res => res.json())

       .then(data => {

-        console.log('搜索返回数据:', data);

         setMovieList(data);

       })

       .catch(() => setMovieList([]));

@@ -166,6 +155,9 @@
               <th>电影类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

@@ -182,7 +174,10 @@
                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

@@ -199,6 +194,9 @@
                     </a>

                   </td>

                   <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

diff --git a/front/src/MusicPage.js b/front/src/MusicPage.js
index fb03fe1..097cf3c 100644
--- a/front/src/MusicPage.js
+++ b/front/src/MusicPage.js
@@ -1,4 +1,5 @@
-import React from "react";

+import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -7,11 +8,14 @@
 import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

 import PersonIcon from "@mui/icons-material/Person";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import ForumIcon from "@mui/icons-material/Forum";

+import HelpIcon from "@mui/icons-material/Help";

 import "./App.css";

 import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -19,7 +23,9 @@
   { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

   { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

   { label: "资料", icon: <PersonIcon />, path: "/info" },

-  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

+  { label: "论坛", icon: <ForumIcon />, path: "/forum" },

+  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" },

+  { label: "求种", icon: <HelpIcon />, path: "/begseed" },

 ];

 

 const areaTabs = [

@@ -30,29 +36,43 @@
   { label: "说唱", icon: <PersonIcon fontSize="small" /> },

 ];

 

-const exampleTorrents = [

-  { type: "Pop", title: "实例1", id: 1 },

-  { type: "Rock", title: "实例2", id: 2 },

-  { type: "Jazz", title: "实例3", id: 3 },

-];

-

 export default function MusicPage() {

   const navigate = useNavigate();

-  const [activeTab, setActiveTab] = React.useState(0);

-  const [musicList, setMusicList] = React.useState([]);

+  const [searchText, setSearchText] = useState('');

+  const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

+  const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

+  const [activeTab, setActiveTab] = useState(0);

+  const [musicList, setMusicList] = useState([]);

+

+  useEffect(() => {

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+    if (!userId) return;

+    fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

+      .then(res => res.json())

+      .then(data => {

+        setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

+        setUserPT({

+          magic: data.magic_value || data.magic || 0,

+          ratio: data.share_ratio || data.share || 0,

+          upload: data.upload_amount || data.upload || 0,

+          download: data.download_amount || data.download || 0,

+        });

+      })

+      .catch(err => console.error('Fetching user profile failed', err));

+  }, []);

 

   // 每个tab对应的音乐类型

   const musicTypesList = [

-    ["华语音乐(大陆)", "欧美音乐", "日韩音乐", "港台音乐", "其他"], // 大陆

-    ["港台流行", "港台摇滚", "港台其他"], // 港台

-    ["欧美流行", "欧美摇滚", "欧美其他"], // 欧美

-    ["日韩流行", "日韩其他"], // 日韩

-    ["其他类型1", "其他类型2"] // 其他

+    ["华语音乐(大陆)", "欧美音乐", "日韩音乐", "港台音乐", "其他"], // 古典音乐

+    ["港台流行", "港台摇滚", "港台其他"], // 流行音乐

+    ["欧美流行", "欧美摇滚", "欧美其他"], // 摇滚

+    ["日韩流行", "日韩其他"], // 电子音乐

+    ["其他类型1", "其他类型2"] // 说唱

   ];

   const musicTypes = musicTypesList[activeTab] || [];

 

-  React.useEffect(() => {

-    // 假设后端接口为 /api/musics?area=大陆

+  useEffect(() => {

     const area = areaTabs[activeTab].label;

     fetch(`${API_BASE_URL}/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

       .then(res => res.json())

@@ -60,23 +80,38 @@
       .catch(() => setMusicList([]));

   }, [activeTab]);

 

+  // 搜索按钮处理

+  const handleSearch = () => {

+    const area = areaTabs[activeTab].label;

+    fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

+      .then(res => res.json())

+      .then(data => {

+        setMusicList(data);

+      })

+      .catch(() => setMusicList([]));

+  };

+

   return (

-    <div className="container music-bg">

+    <div className="container">

       {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

       <div style={{ height: 80 }} />

       <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

         <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

-          <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+          {userInfo.avatar_url ? (

+            <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />

+          ) : (

+            <AccountCircleIcon style={{ fontSize: 38, color: '#1a237e', background: '#e0f3ff', borderRadius: '50%' }} />

+          )}

         </div>

-        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>用户栏</div>

+        <div style={{ color: '#222', fontWeight: 500, marginRight: 24 }}>{userInfo.username || '用户栏'}</div>

         <div style={{ display: 'flex', gap: 28, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>12345</b></span>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>2.56</b></span>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>100GB</b></span>

-          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>50GB</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>魔力值: <b>{userPT.magic}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>分享率: <b>{userPT.ratio}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>上传量: <b>{userPT.upload}</b></span>

+          <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>{userPT.download}</b></span>

         </div>

       </div>

-      {/* 下方内容整体下移,留出与其他页面一致的间距 */}

+      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

       <div style={{ height: 32 }} />

       <nav className="nav-bar card">

         {navItems.map((item) => (

@@ -91,12 +126,17 @@
         ))}

       </nav>

       <div className="search-section card">

-        <input className="search-input" placeholder="输入搜索关键词" />

-        <button className="search-btn">

+        <input

+          className="search-input"

+          placeholder="输入搜索关键词"

+          value={searchText}

+          onChange={e => setSearchText(e.target.value)}

+        />

+        <button className="search-btn" onClick={handleSearch}>

           <span role="img" aria-label="search">🔍</span>

         </button>

       </div>

-      <div className="area-tabs card" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

         {areaTabs.map((tab, idx) => (

           <div

             key={tab.label}

@@ -114,6 +154,9 @@
               <th>音乐类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

@@ -130,7 +173,10 @@
                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

@@ -147,6 +193,9 @@
                     </a>

                   </td>

                   <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

@@ -170,4 +219,4 @@
       <span className="page-info">第 <b>{page}</b> 页</span>

     </div>

   );

-}

+}
\ No newline at end of file
diff --git a/front/src/PublishPage.js b/front/src/PublishPage.js
index 52eb956..df03cae 100644
--- a/front/src/PublishPage.js
+++ b/front/src/PublishPage.js
@@ -31,7 +31,7 @@
     const file = e.target.files[0];

     if (file && file.name.split('.').pop() !== 'torrent') {

       alert('仅能上传.torrent类型文件');

-      e.target.value = null; // Clear the input

+      e.target.value = null;

     } else {

       setFormData({ ...formData, torrentFile: file });

     }

@@ -39,25 +39,21 @@
 

   const handleSubmit = async (e) => {

     e.preventDefault();

-    // 假设userid和tag可以从表单或用户信息中获取,这里用示例数据

-    const userid = '550e8400-e29b-41d4-a716-446655440000';

-    const tag = formData.type ? formData.type : '高清';

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+

     if (!formData.torrentFile) {

       alert('请上传.torrent文件');

       return;

     }

     const data = new FormData();

-    data.append('userid', userid);

+    data.append('userid', userId);

     data.append('title', formData.title);

     data.append('tag', subType);

     data.append('file', formData.torrentFile);

     data.append('subtitle', formData.subtitle);

-    // data.append('subtype', subType);

-    // console.log(data.get('tag'));

 

     try {

-      // console.log(data.get('userid'));

-      // console.log(data.get('title'));

       const response = await fetch(`${API_BASE_URL}/api/save-torrent`, {

         method: 'POST',

         body: data,

diff --git a/front/src/SeedPromotionPage.js b/front/src/SeedPromotionPage.js
index 249533c..6e09f62 100644
--- a/front/src/SeedPromotionPage.js
+++ b/front/src/SeedPromotionPage.js
@@ -1,45 +1,73 @@
 import React, { useEffect, useState } from "react";
 import { API_BASE_URL } from "./config";
 
-// 示例数据,实际应从后端获取
-const mockSeeds = [
-    {
-        seed_id: "seed001",
-        title: "三体 1080P 蓝光",
-        tags: "科幻,电影",
-        popularity: 123,
-        promotion: {
-            start_time: "",
-            end_time: "",
-            discount: 1,
-        },
-    },
-    {
-        seed_id: "seed002",
-        title: "灌篮高手 国语配音",
-        tags: "动画,体育",
-        popularity: 88,
-        promotion: {
-            start_time: "",
-            end_time: "",
-            discount: 1,
-        },
-    },
-];
-
 export default function SeedPromotionPage() {
     const [seeds, setSeeds] = useState([]);
     const [currentTime, setCurrentTime] = useState("");
+    const [loading, setLoading] = useState(true);
 
+    // 时间戳转datetime-local字符串
+    const tsToDatetimeLocal = (ts) => {
+        if (!ts) return "";
+        const d = new Date(ts);
+        const pad = (n) => n.toString().padStart(2, "0");
+        return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
+    };
+
+    // 加载所有种子和促销信息
     useEffect(() => {
-        // 获取当前时间,格式为 yyyy-MM-ddTHH:mm
         const now = new Date();
         const pad = (n) => n.toString().padStart(2, "0");
         const localISOTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
         setCurrentTime(localISOTime);
 
-        // 实际应从后端获取种子及促销信息
-        setSeeds(mockSeeds);
+        async function fetchData() {
+            setLoading(true);
+            try {
+                // 获取所有种子
+                const seedsRes = await fetch(`${API_BASE_URL}/api/all-seeds`);
+                if (!seedsRes.ok) throw new Error("获取种子列表失败");
+                const seedsData = await seedsRes.json();
+
+                // 获取所有促销信息
+                const promoRes = await fetch(`${API_BASE_URL}/api/all-seed-promotions`);
+                if (!promoRes.ok) throw new Error("获取促销信息失败");
+                const promoData = await promoRes.json();
+
+                // 构建促销信息映射
+                const promoMap = {};
+                promoData.forEach(p => {
+                    const seedid = p.seed?.seedid;
+                    promoMap[seedid] = {
+                        start_time: p.startTime ? tsToDatetimeLocal(p.startTime) : "",
+                        end_time: p.endTime ? tsToDatetimeLocal(p.endTime) : "",
+                        discount: typeof p.discount === "number" ? p.discount : 1,
+                    };
+                });
+
+                // 合并数据
+                const mergedSeeds = seedsData.map(seed => ({
+                    seed_id: seed.seedid,
+                    title: seed.title,
+                    tags: seed.seedtag,
+                    popularity: seed.downloadtimes,
+                    promotion: promoMap[seed.seedid] || {
+                        start_time: "",
+                        end_time: "",
+                        discount: 1,
+                    },
+                }));
+
+                setSeeds(mergedSeeds);
+            } catch (err) {
+                setSeeds([]);
+                alert(err.message || "加载失败");
+            } finally {
+                setLoading(false);
+            }
+        }
+
+        fetchData();
     }, []);
 
     // 输入框变更处理
@@ -64,101 +92,126 @@
         return start && end && end < start;
     };
 
+    // 提交促销设置
+    const handleStartPromotion = async (seed) => {
+        const { start_time, end_time, discount } = seed.promotion;
+        try {
+            const res = await fetch(`${API_BASE_URL}/api/set-seed-promotion`, {
+                method: "POST",
+                headers: { "Content-Type": "application/json" },
+                body: JSON.stringify({
+                    seed_id: seed.seed_id,
+                    start_time,
+                    end_time,
+                    discount,
+                }),
+            });
+            if (!res.ok) {
+                const errData = await res.json();
+                throw new Error(errData.message || "促销设置失败");
+            }
+            alert(`已为「${seed.title}」开启促销!`);
+        } catch (err) {
+            alert(err.message || "促销设置失败");
+        }
+    };
+
     return (
         <div style={{ padding: 40, maxWidth: 900, margin: "0 auto" }}>
             <h1 style={{ textAlign: "center", marginBottom: 32 }}>种子促销管理</h1>
-            <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff" }}>
-                <thead>
-                    <tr style={{ background: "#f5f5f5" }}>
-                        <th>标题</th>
-                        <th>标签</th>
-                        <th>热度</th>
-                        <th>促销开始时间</th>
-                        <th>促销结束时间</th>
-                        <th>促销倍率</th>
-                        <th>操作</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    {seeds.map((seed) => {
-                        const { start_time, end_time, discount } = seed.promotion;
-                        const endTimeInvalid = isEndTimeInvalid(start_time, end_time);
-                        const canStartPromotion = start_time && end_time && !endTimeInvalid && discount >= 1;
-                        return (
-                            <tr key={seed.seed_id}>
-                                <td>{seed.title}</td>
-                                <td>{seed.tags}</td>
-                                <td>{seed.popularity}</td>
-                                <td>
-                                    <input
-                                        type="datetime-local"
-                                        value={start_time}
-                                        min={currentTime}
-                                        onChange={(e) =>
-                                            handlePromotionChange(seed.seed_id, "start_time", e.target.value)
-                                        }
-                                    />
-                                </td>
-                                <td>
-                                    <input
-                                        type="datetime-local"
-                                        value={end_time}
-                                        min={start_time || currentTime}
-                                        onChange={(e) =>
-                                            handlePromotionChange(seed.seed_id, "end_time", e.target.value)
-                                        }
-                                        style={endTimeInvalid ? { border: "1.5px solid #e53935" } : {}}
-                                    />
-                                    {endTimeInvalid && (
-                                        <div style={{ color: "#e53935", fontSize: 12 }}>
-                                            结束时间不能早于开始时间
+            {loading ? (
+                <div style={{ textAlign: "center", color: "#666", margin: 40 }}>正在加载...</div>
+            ) : (
+                <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff" }}>
+                    <thead>
+                        <tr style={{ background: "#f5f5f5" }}>
+                            <th>标题</th>
+                            <th>标签</th>
+                            <th>热度</th>
+                            <th>促销开始时间</th>
+                            <th>促销结束时间</th>
+                            <th>促销倍率</th>
+                            <th>操作</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        {seeds.map((seed) => {
+                            const { start_time, end_time, discount } = seed.promotion;
+                            const endTimeInvalid = isEndTimeInvalid(start_time, end_time);
+                            const canStartPromotion = start_time && end_time && !endTimeInvalid && discount >= 1;
+                            return (
+                                <tr key={seed.seed_id}>
+                                    <td>{seed.title}</td>
+                                    <td>{seed.tags}</td>
+                                    <td>{seed.popularity}</td>
+                                    <td>
+                                        <input
+                                            type="datetime-local"
+                                            value={start_time}
+                                            min={currentTime}
+                                            onChange={(e) =>
+                                                handlePromotionChange(seed.seed_id, "start_time", e.target.value)
+                                            }
+                                        />
+                                    </td>
+                                    <td>
+                                        <input
+                                            type="datetime-local"
+                                            value={end_time}
+                                            min={start_time || currentTime}
+                                            onChange={(e) =>
+                                                handlePromotionChange(seed.seed_id, "end_time", e.target.value)
+                                            }
+                                            style={endTimeInvalid ? { border: "1.5px solid #e53935" } : {}}
+                                        />
+                                        {endTimeInvalid && (
+                                            <div style={{ color: "#e53935", fontSize: 12 }}>
+                                                结束时间不能早于开始时间
+                                            </div>
+                                        )}
+                                    </td>
+                                    <td>
+                                        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
+                                            <button
+                                                style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: discount > 1 ? "pointer" : "not-allowed" }}
+                                                onClick={() =>
+                                                    discount > 1 &&
+                                                    handlePromotionChange(seed.seed_id, "discount", discount - 1)
+                                                }
+                                                disabled={discount <= 1}
+                                            >-</button>
+                                            <span style={{ minWidth: 24, textAlign: "center" }}>{discount}</span>
+                                            <button
+                                                style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: "pointer" }}
+                                                onClick={() =>
+                                                    handlePromotionChange(seed.seed_id, "discount", discount + 1)
+                                                }
+                                            >+</button>
                                         </div>
-                                    )}
-                                </td>
-                                <td>
-                                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
+                                    </td>
+                                    <td>
                                         <button
-                                            style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: discount > 1 ? "pointer" : "not-allowed" }}
-                                            onClick={() =>
-                                                discount > 1 &&
-                                                handlePromotionChange(seed.seed_id, "discount", discount - 1)
-                                            }
-                                            disabled={discount <= 1}
-                                        >-</button>
-                                        <span style={{ minWidth: 24, textAlign: "center" }}>{discount}</span>
-                                        <button
-                                            style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: "pointer" }}
-                                            onClick={() =>
-                                                handlePromotionChange(seed.seed_id, "discount", discount + 1)
-                                            }
-                                        >+</button>
-                                    </div>
-                                </td>
-                                <td>
-                                    <button
-                                        style={{
-                                            background: canStartPromotion ? "#1976d2" : "#ccc",
-                                            color: "#fff",
-                                            border: "none",
-                                            borderRadius: 6,
-                                            padding: "4px 16px",
-                                            cursor: canStartPromotion ? "pointer" : "not-allowed",
-                                            fontWeight: 600,
-                                        }}
-                                        disabled={!canStartPromotion}
-                                        onClick={() => {
-                                            // 这里可调用后端API开启促销
-                                            alert(`已为「${seed.title}」开启促销!`);
-                                        }}
-                                    >
-                                        开启促销
-                                    </button>
-                                </td>
-                            </tr>
-                        );
-                    })}
-                </tbody>
-            </table>
+                                            style={{
+                                                background: canStartPromotion ? "#1976d2" : "#ccc",
+                                                color: "#fff",
+                                                border: "none",
+                                                borderRadius: 6,
+                                                padding: "4px 16px",
+                                                cursor: canStartPromotion ? "pointer" : "not-allowed",
+                                                fontWeight: 600,
+                                            }}
+                                            disabled={!canStartPromotion}
+                                            onClick={() => handleStartPromotion(seed)}
+                                        >
+                                            开启促销
+                                        </button>
+                                    </td>
+                                </tr>
+                            );
+                        })}
+                    </tbody>
+                </table>
+            )}
         </div>
     );
 }
\ No newline at end of file
diff --git a/front/src/SportPage.js b/front/src/SportPage.js
index 7dc452d..2874495 100644
--- a/front/src/SportPage.js
+++ b/front/src/SportPage.js
@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -7,12 +8,14 @@
 import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

 import PersonIcon from "@mui/icons-material/Person";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

+import ForumIcon from "@mui/icons-material/Forum";

+import HelpIcon from "@mui/icons-material/Help";

 import "./App.css";

 import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

-import ForumIcon from "@mui/icons-material/Forum";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -21,65 +24,56 @@
   { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

   { label: "资料", icon: <PersonIcon />, path: "/info" },

   { label: "论坛", icon: <ForumIcon />, path: "/forum" },

-  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

+  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" },

+  { label: "求种", icon: <HelpIcon />, path: "/begseed" },

 ];

 

-const sportTypes = [""];

-

 const areaTabs = [

   { label: "篮球", icon: <MovieIcon fontSize="small" /> },

   { label: "足球", icon: <EmailIcon fontSize="small" /> },

   { label: "羽毛球", icon: <EmojiPeopleIcon fontSize="small" /> },

   { label: "排球", icon: <PersonIcon fontSize="small" /> },

-  { label: "电竞", icon: <PersonIcon fontSize="small" />, active: true },

-];

-

-const exampleTorrents = [

-  { type: "Soccer", title: "实例1", id: 1 },

-  { type: "Basketball", title: "实例2", id: 2 },

-  { type: "Tennis", title: "实例3", id: 3 },

+  { label: "电竞", icon: <PersonIcon fontSize="small" /> },

 ];

 

 export default function SportPage() {

   const navigate = useNavigate();

-    const [searchText, setSearchText] = React.useState('');

-    const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

-    const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

-  const [activeTab, setActiveTab] = React.useState(0);

-  const [sportList, setSportList] = React.useState([]);

+  const [searchText, setSearchText] = useState('');

+  const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

+  const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

+  const [activeTab, setActiveTab] = useState(0);

+  const [sportList, setSportList] = useState([]);

 

-    useEffect(() => {

-      const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

-      const userId = match ? match[2] : null;

-      console.log("User ID from cookie:", userId);

-      if (!userId) return;

-      fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

-        .then(res => res.json())

-        .then(data => {

-          console.log('用户PT信息:', data); 

-          setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

-          setUserPT({

-            magic: data.magic_value || data.magic || 0,

-            ratio: data.share_ratio || data.share || 0,

-            upload: data.upload_amount || data.upload || 0,

-            download: data.download_amount || data.download || 0,

-          });

-        })

-        .catch(err => console.error('Fetching user profile failed', err));

-    }, []);

+  useEffect(() => {

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+    if (!userId) return;

+    fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

+      .then(res => res.json())

+      .then(data => {

+        setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

+        setUserPT({

+          magic: data.magic_value || data.magic || 0,

+          ratio: data.share_ratio || data.share || 0,

+          upload: data.upload_amount || data.upload || 0,

+          download: data.download_amount || data.download || 0,

+        });

+      })

+      .catch(err => console.error('Fetching user profile failed', err));

+  }, []);

 

-  // 每个tab对应的运动类型

+  // 每个tab对应的体育类型

   const sportTypesList = [

     ["篮球赛事", "篮球技巧", "篮球明星"], // 篮球

     ["足球赛事", "足球技巧", "足球明星"], // 足球

     ["羽毛球赛事", "羽毛球技巧"], // 羽毛球

     ["排球赛事", "排球技巧"], // 排球

     ["电竞赛事", "电竞技巧"], // 电竞

+    ["其他类型1", "其他类型2"] // 其他

   ];

   const sportTypes = sportTypesList[activeTab] || [];

 

-  React.useEffect(() => {

-    // 假设后端接口为 /api/sports?area=篮球

+  useEffect(() => {

     const area = areaTabs[activeTab].label;

     fetch(`${API_BASE_URL}/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

       .then(res => res.json())

@@ -87,21 +81,20 @@
       .catch(() => setSportList([]));

   }, [activeTab]);

 

-    // 搜索按钮处理

-    const handleSearch = () => {

-      const area = areaTabs[activeTab].label;

-      fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

-        .then(res => res.json())

-        .then(data => {

-          console.log('搜索返回数据:', data);

-          setSportList(data);

-        })

-        .catch(() => setSportList([]));

-    };

+  // 搜索按钮处理

+  const handleSearch = () => {

+    const area = areaTabs[activeTab].label;

+    fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

+      .then(res => res.json())

+      .then(data => {

+        setSportList(data);

+      })

+      .catch(() => setSportList([]));

+  };

 

   return (

     <div className="container">

-      {/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}

+      {/* 顶部空白与体育界面一致,用户栏绝对定位在页面右上角 */}

       <div style={{ height: 80 }} />

       <div className="user-bar" style={{ position: 'fixed', top: 18, right: 42, zIndex: 100, display: 'flex', alignItems: 'center', background: '#e0f3ff', borderRadius: 12, padding: '6px 18px', boxShadow: '0 2px 8px #b2d8ea', minWidth: 320, minHeight: 48, width: 420 }}>

         <div style={{ cursor: 'pointer', marginRight: 16 }} onClick={() => navigate('/user')}>

@@ -119,7 +112,7 @@
           <span style={{ color: '#1976d2', fontWeight: 500 }}>下载量: <b>{userPT.download}</b></span>

         </div>

       </div>

-      {/* 下方内容整体下移,留出与音乐界面一致的间距 */}

+      {/* 下方内容整体下移,留出与体育界面一致的间距 */}

       <div style={{ height: 32 }} />

       <nav className="nav-bar card">

         {navItems.map((item) => (

@@ -144,15 +137,7 @@
           <span role="img" aria-label="search">🔍</span>

         </button>

       </div>

-      <div

-        className="area-tabs"

-        style={{

-          display: "flex",

-          justifyContent: "center",

-          gap: 24,

-          margin: "18px 0",

-        }}

-      >

+      <div className="area-tabs" style={{ display: 'flex', justifyContent: 'center', gap: 24, margin: '18px 0' }}>

         {areaTabs.map((tab, idx) => (

           <div

             key={tab.label}

@@ -170,6 +155,9 @@
               <th>体育类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

@@ -186,7 +174,10 @@
                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

@@ -203,6 +194,9 @@
                     </a>

                   </td>

                   <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

@@ -220,24 +214,10 @@
   const total = 5;

   return (

     <div className="pagination">

-      <button

-        onClick={() => setPage((p) => Math.max(1, p - 1))}

-        disabled={page === 1}

-      >

-        上一页

-      </button>

-      <span className="page-num">

-        {page}/{total}

-      </span>

-      <button

-        onClick={() => setPage((p) => Math.min(total, p + 1))}

-        disabled={page === total}

-      >

-        下一页

-      </button>

-      <span className="page-info">

-        第 <b>{page}</b> 页

-      </span>

+      <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page === 1}>上一页</button>

+      <span className="page-num">{page}/{total}</span>

+      <button onClick={() => setPage(p => Math.min(total, p + 1))} disabled={page === total}>下一页</button>

+      <span className="page-info">第 <b>{page}</b> 页</span>

     </div>

   );

-}

+}
\ No newline at end of file
diff --git a/front/src/TVPage.js b/front/src/TVPage.js
index 7e33a87..d75a172 100644
--- a/front/src/TVPage.js
+++ b/front/src/TVPage.js
@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from "react";

+import HomeIcon from "@mui/icons-material/Home";

 import MovieIcon from "@mui/icons-material/Movie";

 import EmailIcon from "@mui/icons-material/Email";

 import MusicNoteIcon from "@mui/icons-material/MusicNote";

@@ -7,11 +8,14 @@
 import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";

 import PersonIcon from "@mui/icons-material/Person";

 import AccountCircleIcon from "@mui/icons-material/AccountCircle";

-import { useNavigate } from "react-router-dom";

+import ForumIcon from "@mui/icons-material/Forum";

+import HelpIcon from "@mui/icons-material/Help";

 import "./App.css";

+import { useNavigate } from "react-router-dom";

 import { API_BASE_URL } from "./config";

 

 const navItems = [

+  { label: "首页", icon: <HomeIcon />, path: "/home" },

   { label: "电影", icon: <MovieIcon />, path: "/movie" },

   { label: "剧集", icon: <EmailIcon />, path: "/tv" },

   { label: "音乐", icon: <MusicNoteIcon />, path: "/music" },

@@ -19,15 +23,9 @@
   { label: "游戏", icon: <SportsEsportsIcon />, path: "/game" },

   { label: "体育", icon: <SportsMartialArtsIcon />, path: "/sport" },

   { label: "资料", icon: <PersonIcon />, path: "/info" },

-  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

-];

-

-const tvTypesList = [

-  ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆

-  ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台

-  ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美

-  ["日韩青春", "日韩家庭", "日韩其他"], // 日韩

-  ["其他类型1", "其他类型2"] // 其他

+  { label: "论坛", icon: <ForumIcon />, path: "/forum" },

+  { label: "发布", icon: <AccountCircleIcon />, path: "/publish" },

+  { label: "求种", icon: <HelpIcon />, path: "/begseed" },

 ];

 

 const areaTabs = [

@@ -35,36 +33,33 @@
   { label: "港剧", icon: <EmailIcon fontSize="small" /> },

   { label: "欧美剧", icon: <PersonIcon fontSize="small" /> },

   { label: "日韩剧", icon: <EmojiPeopleIcon fontSize="small" /> },

-  // { label: "其他", icon: <PersonIcon fontSize="small" /> },

 ];

 

 export default function TVPage() {

   const navigate = useNavigate();

   const [searchText, setSearchText] = React.useState('');

-    const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

-    const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

+  const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });

+  const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });

   const [activeTab, setActiveTab] = React.useState(0);

   const [tvList, setTvList] = React.useState([]);

 

-    useEffect(() => {

-      const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

-      const userId = match ? match[2] : null;

-      console.log("User ID from cookie:", userId);

-      if (!userId) return;

-      fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

-        .then(res => res.json())

-        .then(data => {

-          console.log('用户PT信息:', data); 

-          setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

-          setUserPT({

-            magic: data.magic_value || data.magic || 0,

-            ratio: data.share_ratio || data.share || 0,

-            upload: data.upload_amount || data.upload || 0,

-            download: data.download_amount || data.download || 0,

-          });

-        })

-        .catch(err => console.error('Fetching user profile failed', err));

-    }, []);

+  useEffect(() => {

+    const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

+    const userId = match ? match[2] : null;

+    if (!userId) return;

+    fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)

+      .then(res => res.json())

+      .then(data => {

+        setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });

+        setUserPT({

+          magic: data.magic_value || data.magic || 0,

+          ratio: data.share_ratio || data.share || 0,

+          upload: data.upload_amount || data.upload || 0,

+          download: data.download_amount || data.download || 0,

+        });

+      })

+      .catch(err => console.error('Fetching user profile failed', err));

+  }, []);

 

   React.useEffect(() => {

     // 假设后端接口为 /api/tvs?area=大陆

@@ -75,19 +70,25 @@
       .catch(() => setTvList([]));

   }, [activeTab]);

 

-    // 搜索按钮处理

-    const handleSearch = () => {

-      const area = areaTabs[activeTab].label;

-      fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

-        .then(res => res.json())

-        .then(data => {

-          console.log('搜索返回数据:', data);

-          setTvList(data);

-        })

-        .catch(() => setTvList([]));

-    };

+  // 搜索按钮处理

+  const handleSearch = () => {

+    const area = areaTabs[activeTab].label;

+    fetch(`${API_BASE_URL}/api/search-seeds?tag=${encodeURIComponent(area)}&keyword=${encodeURIComponent(searchText)}`)

+      .then(res => res.json())

+      .then(data => {

+        console.log('搜索返回数据:', data);

+        setTvList(data);

+      })

+      .catch(() => setTvList([]));

+  };

 

-  // 每个tab对应的剧集类型

+  const tvTypesList = [

+    ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆

+    ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台

+    ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美

+    ["日韩青春", "日韩家庭", "日韩其他"], // 日韩

+    ["其他类型1", "其他类型2"] // 其他

+  ];

   const tvTypes = tvTypesList[activeTab] || [];

 

   return (

@@ -153,6 +154,9 @@
               <th>剧集类型</th>

               <th>标题</th>

               <th>发布者</th>

+              <th>大小</th>

+              <th>热度</th>

+              <th>折扣倍率</th>

             </tr>

           </thead>

           <tbody>

@@ -169,7 +173,10 @@
                       {item.title}

                     </a>

                   </td>

-                  <td>{item.user.username}</td>

+                  <td>{item.username}</td>

+                  <td>{item.seedsize}</td>

+                  <td>{item.downloadtimes}</td>

+                  <td>{item.discount == null ? 1 : item.discount}</td>

                 </tr>

               ))

             ) : (

@@ -186,6 +193,9 @@
                     </a>

                   </td>

                   <td>发布者{index + 1}</td>

+                  <td>--</td>

+                  <td>--</td>

+                  <td>1</td>

                 </tr>

               ))

             )}

diff --git a/front/src/UserProfile.js b/front/src/UserProfile.js
index eed84da..fb44b43 100644
--- a/front/src/UserProfile.js
+++ b/front/src/UserProfile.js
@@ -51,9 +51,11 @@
 

   // 账号迁移相关

   const [migrationOpen, setMigrationOpen] = useState(false);

+  const [migrationUpload, setMigrationUpload] = useState('');

   const [migrationEmail, setMigrationEmail] = useState('');

   const [migrationPassword, setMigrationPassword] = useState('');

   const [migrationStatus, setMigrationStatus] = useState('');

+

   // 兑换结果计算

   React.useEffect(() => {

     if (!exchangeMagic || isNaN(exchangeMagic)) {

@@ -66,17 +68,13 @@
   // 获取用户信息

   useEffect(() => {

     const fetchUserInfo = async () => {

-      // 假设userid存储在localStorage或其他地方

-      // const userid = localStorage.getItem("userid");

-      // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

       const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

-    const userid = match ? match[2] : null;

+      const userid = match ? match[2] : null;

       if (!userid) return;

       try {

         const res = await fetch(`${API_BASE_URL}/api/user-profile?userid=${userid}`);

         if (res.ok) {

           const data = await res.json();

-          // console.log("获取用户信息:", data);

           setUserInfo(data);

           setTempUserInfo(data);

         }

@@ -87,13 +85,10 @@
     fetchUserInfo();

   }, []);

 

-  // 获取上传种子

   useEffect(() => {

     const fetchUserSeeds = async () => {

-      // const userid = localStorage.getItem("userid");

-      // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

       const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

-    const userid = match ? match[2] : null;

+      const userid = match ? match[2] : null;

       if (!userid) return;

       try {

         const res = await fetch(`${API_BASE_URL}/api/user-seeds?userid=${userid}`);

@@ -107,7 +102,7 @@
     };

     fetchUserSeeds();

   }, []);

-  // 获取收藏种子

+

   useEffect(() => {

     const fetchUserFavorites = async () => {

       const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

@@ -126,7 +121,7 @@
     };

     fetchUserFavorites();

   }, []);

-  // 获取活跃度

+

   useEffect(() => {

     const fetchUserStats = async () => {

       const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

@@ -150,15 +145,13 @@
   };

 

   const handleSave = async () => {

-    if (tempUserInfo.gender === "男"){

+    if (tempUserInfo.gender === "男") {

       tempUserInfo.gender = "m";

-    }else if (tempUserInfo.gender === "女"){

+    } else if (tempUserInfo.gender === "女") {

       tempUserInfo.gender = "f";

     }

     setUserInfo({ ...tempUserInfo });

-    // 获取userid

-    // const userid = localStorage.getItem("userid");

-    // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid

+

     const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

     const userid = match ? match[2] : null;

     try {

@@ -205,7 +198,7 @@
       return;

     }

     try {

-        const res = await fetch(`${API_BASE_URL}/api/invite`, {

+      const res = await fetch(`${API_BASE_URL}/api/invite`, {

         method: 'POST',

         headers: { 'Content-Type': 'application/json' },

         body: JSON.stringify({ userid, invite_email: inviteEmail }),

@@ -226,7 +219,9 @@
       console.error("邀请失败", err);

       setInviteStatus("邀请失败,请检查网络");

     }

-  };  // 兑换

+  };

+

+  // 兑换魔力值

   const handleExchange = async () => {

     const magic = Number(exchangeMagic);

     if (!magic || isNaN(magic) || magic <= 0) return;

@@ -234,14 +229,14 @@
       alert("魔力值不足!");

       return;

     }

-    

+

     // 检查兑换结果是否为整数

     const calculatedExchangeResult = magic / exchangeRate[exchangeType];

     if (!Number.isInteger(calculatedExchangeResult)) {

       alert("兑换结果必须为整数,请调整魔力值!");

       return;

     }

-    

+

     // 获取userid

     const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

     const userid = match ? match[2] : null;

@@ -256,9 +251,9 @@
       const res = await fetch(`${API_BASE_URL}/api/exchange`, {

         method: 'POST',

         headers: { 'Content-Type': 'application/json' },

-        body: JSON.stringify({ 

-          userid, 

-          magic, 

+        body: JSON.stringify({

+          userid,

+          magic,

           exchangeType,

           exchangeResult: calculatedExchangeResult

         }),

@@ -300,7 +295,7 @@
         method: 'POST',

         headers: { 'Content-Type': 'application/json' },

         body: JSON.stringify({ userid, seedid }),

-      });      if (res.ok) {

+      }); if (res.ok) {

         setUserFavorites(userFavorites.filter((s) => (s.seedid || s.seed_id) !== seedid));

         alert('已取消收藏');

       } else {

@@ -315,14 +310,13 @@
   // 申诉提交逻辑

   const handleAppealSubmit = async () => {

     if (!appealTitle || !appealFile) return;

-    // 获取userid

     const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

     const userid = match ? match[2] : null;

     if (!userid) {

       alert('未获取到用户ID');

       return;

     }

-    // 构建表单数据

+

     const formData = new FormData();

     formData.append('userid', userid);

     formData.append('content', appealTitle);

@@ -346,37 +340,42 @@
       alert('申诉失败,请检查网络');

     }

   };

-  // 账号迁移提交逻辑

+

+  // 账号迁移

   const handleMigrationSubmit = async () => {

     if (!appealFile) {

       setMigrationStatus('请选择PDF文件');

       return;

     }

-    

-    // 获取当前用户ID

+    if (!migrationUpload || isNaN(migrationUpload) || Number(migrationUpload) <= 0) {

+      setMigrationStatus('请输入有效的待发放上传量');

+      return;

+    }

+

     const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

     const currentUserId = match ? match[2] : null;

     if (!currentUserId) {

       setMigrationStatus('未获取到当前用户ID');

       return;

     }

-    

+

     try {

-      // 构建表单数据

       const formData = new FormData();

       formData.append('userid', currentUserId);

       formData.append('file', appealFile);

-      

+      formData.append('uploadtogive', migrationUpload);

+

       const res = await fetch(`${API_BASE_URL}/api/migrate-account`, {

         method: 'POST',

         body: formData,

       });

-      

+

       if (res.ok) {

         setMigrationStatus('账号迁移申请已提交,请等待管理员审核');

         setTimeout(() => {

           setMigrationOpen(false);

           setAppealFile(null);

+          setMigrationUpload('');

           setMigrationStatus('');

         }, 2000);

       } else {

@@ -682,7 +681,7 @@
                     variant="contained"

                     color="error"

                     size="small"

-                    sx={{ marginLeft: 2, borderRadius: 1, minWidth: 60 }}                    onClick={async e => {

+                    sx={{ marginLeft: 2, borderRadius: 1, minWidth: 60 }} onClick={async e => {

                       e.stopPropagation();

                       const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');

                       const userid = match ? match[2] : null;

@@ -734,39 +733,40 @@
         }}>

           {userFavorites.length === 0 ? (

             <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无收藏种子)</div>

-          ) : (            <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>

-              {userFavorites.map((seed, idx) => (

-                <li

-                  key={seed.seedid || idx}

-                  style={{

-                    display: 'flex',

-                    alignItems: 'center',

-                    padding: '12px 0',

-                    borderBottom: idx === userFavorites.length - 1 ? 'none' : '1px solid #e0e7ff',

-                    cursor: 'pointer',

-                    transition: 'background 0.15s'

-                  }}                  onClick={e => {                    if (e.target.classList.contains('remove-favorite-btn')) return;

-                    navigate(`/torrent/${seed.seedid || seed.seed_id}`);

+          ) : (<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>

+            {userFavorites.map((seed, idx) => (

+              <li

+                key={seed.seedid || idx}

+                style={{

+                  display: 'flex',

+                  alignItems: 'center',

+                  padding: '12px 0',

+                  borderBottom: idx === userFavorites.length - 1 ? 'none' : '1px solid #e0e7ff',

+                  cursor: 'pointer',

+                  transition: 'background 0.15s'

+                }} onClick={e => {

+                  if (e.target.classList.contains('remove-favorite-btn')) return;

+                  navigate(`/torrent/${seed.seedid || seed.seed_id}`);

+                }}

+                onMouseOver={e => e.currentTarget.style.background = '#f3f6ff'}

+                onMouseOut={e => e.currentTarget.style.background = ''}

+              >

+                <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline', cursor: 'pointer' }}>{seed.seed.title}</span>

+                <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.seed.tags}</span>

+                <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.seed.downloadtimes}</span>

+                <Button

+                  className="remove-favorite-btn"

+                  variant="contained"

+                  color="warning"

+                  size="small"

+                  sx={{ marginLeft: 2, borderRadius: 1, minWidth: 80 }} onClick={e => {

+                    e.stopPropagation();

+                    handleRemoveFavorite(seed.seedid || seed.seed_id);

                   }}

-                  onMouseOver={e => e.currentTarget.style.background = '#f3f6ff'}

-                  onMouseOut={e => e.currentTarget.style.background = ''}

-                >

-                  <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline', cursor: 'pointer' }}>{seed.seed.title}</span>

-                  <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.seed.tags}</span>

-                  <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.seed.downloadtimes}</span>

-                  <Button

-                    className="remove-favorite-btn"

-                    variant="contained"

-                    color="warning"

-                    size="small"

-                    sx={{ marginLeft: 2, borderRadius: 1, minWidth: 80 }}                    onClick={e => {

-                      e.stopPropagation();

-                      handleRemoveFavorite(seed.seedid || seed.seed_id);

-                    }}

-                  >取消收藏</Button>

-                </li>

-              ))}

-            </ul>

+                >取消收藏</Button>

+              </li>

+            ))}

+          </ul>

           )}

         </div>

       </div>

@@ -807,12 +807,24 @@
           <Button onClick={handleAppealSubmit} variant="contained" color="primary" disabled={!appealTitle || !appealFile}>提交</Button>

           <Button onClick={() => setAppealOpen(false)} variant="outlined">取消</Button>

         </DialogActions>

-      </Dialog>      {/* 账号迁移弹窗 */}

+      </Dialog>

+      {/* 账号迁移弹窗 */}

       <Dialog open={migrationOpen} onClose={() => setMigrationOpen(false)}>

         <DialogTitle>账号迁移</DialogTitle>

         <DialogContent>

           <div style={{ marginBottom: 16 }}>

-          </div>          <div>

+            <TextField

+              label="待发放上传量"

+              type="number"

+              fullWidth

+              value={migrationUpload}

+              onChange={e => setMigrationUpload(e.target.value)}

+              size="small"

+              inputProps={{ min: 1 }}

+              style={{ marginBottom: 18 }}

+            />

+          </div>

+          <div>

             <input

               type="file"

               accept=".pdf"