新增前端接口,修改种子发布页面

Change-Id: I9d9f4bde524caeaab9cbadcd0e14f44e521b0abb
diff --git a/front/src/AnimePage.js b/front/src/AnimePage.js
index 97bf453..d17f3cc 100644
--- a/front/src/AnimePage.js
+++ b/front/src/AnimePage.js
@@ -30,16 +30,17 @@
 ];

 

 const areaTabs = [

-  { label: "大陆", icon: <EmojiPeopleIcon fontSize="small" /> },

-  { label: "港台", icon: <EmailIcon fontSize="small" /> },

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

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

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

+  { label: "国创", icon: <EmojiPeopleIcon fontSize="small" /> },

+  { 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([]);

 

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

   const animeTypesList = [

@@ -51,6 +52,15 @@
   ];

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

 

+  React.useEffect(() => {

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

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => setAnimeList(data))

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

+  }, [activeTab]);

+

   return (

     <div className="container">

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

@@ -108,21 +118,39 @@
             </tr>

           </thead>

           <tbody>

-            {animeTypes.map((type, index) => (

-              <tr key={type}>

-                <td>

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

-                    {type}

-                  </a>

-                </td>

-                <td>

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

-                    种子{index + 1}

-                  </a>

-                </td>

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

-              </tr>

-            ))}

+            {animeList.length > 0 ? (

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

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

+                  <td>

+                    <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' }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

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

+                      {type}

+                    </a>

+                  </td>

+                  <td>

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

+                      种子{index + 1}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/GamePage.js b/front/src/GamePage.js
index 23383b6..2528acd 100644
--- a/front/src/GamePage.js
+++ b/front/src/GamePage.js
@@ -21,7 +21,13 @@
   { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

 ];

 

-const gameTypes = ["PC", "主机", "移动", "掌机", "视频"];

+const gameTypesList = [

+  ["PC"],

+  ["主机"],

+  ["移动"],

+  ["掌机"],

+  ["视频"]

+];

 

 const areaTabs = [

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

@@ -31,15 +37,20 @@
   { label: "视频", icon: <PersonIcon fontSize="small" /> },

 ];

 

-const exampleTorrents = [

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

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

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

-];

-

 export default function GamePage() {

   const navigate = useNavigate();

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

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

+

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

+

+  React.useEffect(() => {

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => setGameList(data))

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

+  }, [activeTab]);

 

   return (

     <div className="container">

@@ -153,27 +164,39 @@
             </tr>

           </thead>

           <tbody>

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

-              <tr key={type}>

-                <td>

-                  <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>

-                </td>

-                <td></td>

-              </tr>

-            ))}

+            {gameList.length > 0 ? (

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

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

+                  <td>

+                    <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" }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

+                    <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>

+                  </td>

+                  <td></td>

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/InfoPage.js b/front/src/InfoPage.js
index 1e00527..755d922 100644
--- a/front/src/InfoPage.js
+++ b/front/src/InfoPage.js
@@ -40,6 +40,7 @@
 export default function InfoPage() {

   const navigate = useNavigate();

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

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

 

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

   const infoTypesList = [

@@ -51,6 +52,18 @@
   ];

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

 

+  React.useEffect(() => {

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

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => {

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

+        setInfoList(data);

+      })

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

+  }, [activeTab]);

+

   return (

     <div className="container">

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

@@ -163,27 +176,39 @@
             </tr>

           </thead>

           <tbody>

-            {infoTypes.map((type, index) => (

-              <tr key={type}>

-                <td>

-                  <a

-                    href={`/torrent/${type}`}

-                    style={{ color: "#1a237e", textDecoration: "none" }}

-                  >

-                    {type}

-                  </a>

-                </td>

-                <td>

-                  <a

-                    href={`/torrent/${type}`}

-                    style={{ color: "#1a237e", textDecoration: "none" }}

-                  >

-                    种子{index + 1}

-                  </a>

-                </td>

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

-              </tr>

-            ))}

+            {infoList.length > 0 ? (

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

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

+                  <td>

+                    <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' }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

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

+                      {type}

+                    </a>

+                  </td>

+                  <td>

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

+                      种子{index + 1}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/MoviePage.js b/front/src/MoviePage.js
index 20e4a4f..c84797b 100644
--- a/front/src/MoviePage.js
+++ b/front/src/MoviePage.js
@@ -26,7 +26,7 @@
   { label: "港台", icon: <EmailIcon fontSize="small" /> },

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

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

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

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

 ];

 

 const exampleTorrents = [

@@ -38,6 +38,7 @@
 export default function MoviePage() {

   const navigate = useNavigate();

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

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

 

   // 每个tab对应的电影类型

   const movieTypesList = [

@@ -49,6 +50,18 @@
   ];

   const movieTypes = movieTypesList[activeTab] || [];

 

+  React.useEffect(() => {

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

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => {

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

+        setMovieList(data);

+      })

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

+  }, [activeTab]);

+

   return (

     <div className="container">

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

@@ -106,21 +119,39 @@
             </tr>

           </thead>

           <tbody>

-            {movieTypes.map((type, index) => (

-              <tr key={type}>

-                <td>

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

-                    {type}

-                  </a>

-                </td>

-                <td>

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

-                    种子{index + 1}

-                  </a>

-                </td>

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

-              </tr>

-            ))}

+            {movieList.length > 0 ? (

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

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

+                  <td>

+                    <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' }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

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

+                      {type}

+                    </a>

+                  </td>

+                  <td>

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

+                      种子{index + 1}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/MusicPage.js b/front/src/MusicPage.js
index 4a9c465..b39b0ee 100644
--- a/front/src/MusicPage.js
+++ b/front/src/MusicPage.js
@@ -22,11 +22,11 @@
 ];

 

 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" /> },

+  { 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" /> },

 ];

 

 const exampleTorrents = [

@@ -38,6 +38,7 @@
 export default function MusicPage() {

   const navigate = useNavigate();

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

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

 

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

   const musicTypesList = [

@@ -49,6 +50,15 @@
   ];

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

 

+  React.useEffect(() => {

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

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => setMusicList(data))

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

+  }, [activeTab]);

+

   return (

     <div className="container music-bg">

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

@@ -106,21 +116,39 @@
             </tr>

           </thead>

           <tbody>

-            {musicTypes.map((type, index) => (

-              <tr key={type}>

-                <td>

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

-                    {type}

-                  </a>

-                </td>

-                <td>

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

-                    种子{index + 1}

-                  </a>

-                </td>

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

-              </tr>

-            ))}

+            {musicList.length > 0 ? (

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

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

+                  <td>

+                    <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' }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

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

+                      {type}

+                    </a>

+                  </td>

+                  <td>

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

+                      种子{index + 1}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/PublishPage.js b/front/src/PublishPage.js
index 4b23285..a1a0e8f 100644
--- a/front/src/PublishPage.js
+++ b/front/src/PublishPage.js
@@ -9,6 +9,17 @@
     title: '',

     subtitle: ''

   });

+  const [subType, setSubType] = useState('');

+

+  const typeOptions = {

+    '电影': ['大陆', '港台', '欧美', '日韩'],

+    '剧集': ['国产电视剧', '港剧', '欧美剧', '日韩剧'],

+    '音乐': ['古典音乐', '流行音乐', '摇滚', '电子音乐', '说唱'],

+    '动漫': ['国创', '日漫', '欧美动漫', '韩漫'],

+    '游戏': ['PC', '主机', '移动', '掌机', '视频'],

+    '体育': ['篮球', '足球', '羽毛球', '排球', '电竞'],

+    '资料': ['出版物', '学习教程', '素材模板', '演讲交流', '日常娱乐'],

+  };

 

   const handleChange = (e) => {

     const { name, value } = e.target;

@@ -28,7 +39,7 @@
   const handleSubmit = async (e) => {

     e.preventDefault();

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

-    const userid = '123456';

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

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

     if (!formData.torrentFile) {

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

@@ -37,8 +48,11 @@
     const data = new FormData();

     data.append('userid', userid);

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

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

+    data.append('tag', subType);

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

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

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

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

 

     try {

       const response = await fetch('http://192.168.5.9:8080/api/save-torrent', {

@@ -61,7 +75,7 @@
       <form onSubmit={handleSubmit} className="publish-form">

         <div className="form-row">

           <label htmlFor="type">类型</label>

-          <select name="type" id="type" value={formData.type} onChange={handleChange} required>

+          <select name="type" id="type" value={formData.type} onChange={e => { handleChange(e); setSubType(''); }} required>

             <option value="">请选择类型</option>

             <option value="电影">电影</option>

             <option value="剧集">剧集</option>

@@ -72,6 +86,17 @@
             <option value="资料">资料</option>

           </select>

         </div>

+        {formData.type && typeOptions[formData.type] && (

+          <div className="form-row">

+            <label htmlFor="subtype">具体类型</label>

+            <select name="subtype" id="subtype" value={subType} onChange={e => setSubType(e.target.value)} required>

+              <option value="">请选择具体类型</option>

+              {typeOptions[formData.type].map(opt => (

+                <option key={opt} value={opt}>{opt}</option>

+              ))}

+            </select>

+          </div>

+        )}

 

         <div className="form-row">

           <label htmlFor="torrentFile">种子文件</label>

diff --git a/front/src/SportPage.js b/front/src/SportPage.js
index 569f022..3299a93 100644
--- a/front/src/SportPage.js
+++ b/front/src/SportPage.js
@@ -40,6 +40,7 @@
 export default function SportPage() {

   const navigate = useNavigate();

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

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

 

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

   const sportTypesList = [

@@ -51,6 +52,15 @@
   ];

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

 

+  React.useEffect(() => {

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

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => setSportList(data))

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

+  }, [activeTab]);

+

   return (

     <div className="container">

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

@@ -163,27 +173,39 @@
             </tr>

           </thead>

           <tbody>

-            {sportTypes.map((type, index) => (

-              <tr key={type}>

-                <td>

-                  <a

-                    href={`/torrent/${type}`}

-                    style={{ color: "#1a237e", textDecoration: "none" }}

-                  >

-                    {type}

-                  </a>

-                </td>

-                <td>

-                  <a

-                    href={`/torrent/${type}`}

-                    style={{ color: "#1a237e", textDecoration: "none" }}

-                  >

-                    种子{index + 1}

-                  </a>

-                </td>

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

-              </tr>

-            ))}

+            {sportList.length > 0 ? (

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

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

+                  <td>

+                    <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" }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

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

+                      {type}

+                    </a>

+                  </td>

+                  <td>

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

+                      种子{index + 1}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/TVPage.js b/front/src/TVPage.js
index 13b7391..bf1db6c 100644
--- a/front/src/TVPage.js
+++ b/front/src/TVPage.js
@@ -21,40 +21,37 @@
   { label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option

 ];

 

-const tvTypes = [

-  "华语剧集(大陆)",

-  "欧美剧集",

-  "日韩剧集",

-  "港台剧集",

-  "其他"

+const tvTypesList = [

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

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

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

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

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

 ];

 

 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" /> },

-];

-

-const exampleTorrents = [

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

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

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

+  { 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" /> },

 ];

 

 export default function TVPage() {

   const navigate = useNavigate();

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

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

+

+  React.useEffect(() => {

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

+    const area = areaTabs[activeTab].label;

+    fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)

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

+      .then(data => setTvList(data))

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

+  }, [activeTab]);

 

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

-  const tvTypesList = [

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

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

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

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

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

-  ];

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

 

   return (

@@ -114,21 +111,39 @@
             </tr>

           </thead>

           <tbody>

-            {tvTypes.map((type, index) => (

-              <tr key={type}>

-                <td>

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

-                    {type}

-                  </a>

-                </td>

-                <td>

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

-                    种子{index + 1}

-                  </a>

-                </td>

-                <td></td>

-              </tr>

-            ))}

+            {tvList.length > 0 ? (

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

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

+                  <td>

+                    <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' }}>

+                      {item.title}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            ) : (

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

+                <tr key={type}>

+                  <td>

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

+                      {type}

+                    </a>

+                  </td>

+                  <td>

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

+                      种子{index + 1}

+                    </a>

+                  </td>

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

+                </tr>

+              ))

+            )}

           </tbody>

         </table>

       </div>

diff --git a/front/src/TorrentDetailPage.js b/front/src/TorrentDetailPage.js
index a23dce7..8d3ee43 100644
--- a/front/src/TorrentDetailPage.js
+++ b/front/src/TorrentDetailPage.js
@@ -4,14 +4,81 @@
 

 export default function TorrentDetailPage() {

   const { torrentId } = useParams();

+  const [detail, setDetail] = React.useState(null);

+  const [loading, setLoading] = React.useState(true);

+  const [error, setError] = React.useState(null);

+  // 假设你从某个地方获取了 userId(例如登录状态、localStorage 等)

+  const [userId] = React.useState('user1550e8400-e29b-41d4-a716-44665544000023'); // 替换为实际的用户 ID

+

+  const handleClick = () => {

+    // 构造下载 URL,包含 userId 和 torrentId 参数

+    console.log(torrentId)

+    const downloadUrl = `http://192.168.5.9:8080/api/get-torrent?userId=${encodeURIComponent(userId)}&torrentId=${encodeURIComponent(torrentId)}`;

+    

+    // 发起 GET 请求下载文件

+    fetch(downloadUrl)

+      .then(response => {

+        if (!response.ok) {

+          throw new Error('下载失败');

+        }

+        return response.blob();

+      })

+      .then(blob => {

+        // 创建下载链接并触发下载

+        const url = window.URL.createObjectURL(blob);

+        const a = document.createElement('a');

+        a.href = url;

+        a.download = `torrent-${torrentId}.torrent`;

+        document.body.appendChild(a);

+        a.click();

+        window.URL.revokeObjectURL(url);

+        document.body.removeChild(a);

+      })

+      .catch(error => {

+        console.error('下载错误:', error);

+        alert('下载失败: ' + error.message);

+      });

+  };

+

+  React.useEffect(() => {

+    setLoading(true);

+    setError(null);

+    fetch(`http://192.168.5.9:8080/api/torrent-detail?id=${encodeURIComponent(torrentId)}`)

+      .then(res => {

+        if (!res.ok) throw new Error('网络错误');

+        return res.json();

+      })

+      .then(data => {

+        setDetail(data);

+        setLoading(false);

+      })

+      .catch(err => {

+        setError(err.message);

+        setLoading(false);

+      });

+  }, [torrentId]);

+

+  if (loading) return <div className="container"><h1>加载中...</h1></div>;

+  if (error) return <div className="container"><h1>加载失败: {error}</h1></div>;

+  if (!detail) return <div className="container"><h1>未找到详情</h1></div>;

 

   return (

     <div className="container">

       <h1>种子详情页</h1>

-      <h2 style={{ fontSize: 'inherit', fontWeight: 'normal', textAlign: 'left' }}>标题: 种子{torrentId}</h2>

-      <p style={{ fontSize: 'inherit', textAlign: 'left' }}>简介: 这是种子{torrentId}的详细信息。</p>

+      <h2 style={{ fontSize: 'inherit', fontWeight: 'normal', textAlign: 'left' }}>标题: {detail.title || `种子${torrentId}`}</h2>

+      <p style={{ fontSize: 'inherit', textAlign: 'left' }}>简介: {detail.description || `这是种子${torrentId}的详细信息。`}</p>

       <div style={{ textAlign: 'center', marginTop: '20px' }}>

-        <button style={{ padding: '10px 20px', fontSize: '16px', cursor: 'pointer', backgroundColor: '#d3f0ff', border: 'none', borderRadius: '4px' }}>

+        <button 

+          style={{ 

+            padding: '10px 20px', 

+            fontSize: '16px', 

+            cursor: 'pointer', 

+            backgroundColor: '#d3f0ff', 

+            border: 'none', 

+            borderRadius: '4px' 

+          }} 

+          onClick={handleClick}

+        >

           下载

         </button>

       </div>

diff --git a/front/src/UserProfile.js b/front/src/UserProfile.js
index 0269723..4737033 100644
--- a/front/src/UserProfile.js
+++ b/front/src/UserProfile.js
@@ -1,4 +1,4 @@
-import React, { useState } from "react";

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

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

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

 import "./App.css";

@@ -6,21 +6,109 @@
 export default function UserProfile() {

   const navigate = useNavigate();

   const [userInfo, setUserInfo] = useState({

+    avatar_url: "",

     username: "示例用户",

     email: "user@example.com",

-    company: "",

+    invite_left: "",

     school: "",

-    birthday: "",

+    account_status: "",

+    gender: "",

   });

   const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });

+  const [userSeeds, setUserSeeds] = useState([]);

+  const [userStats, setUserStats] = useState({

+    magic: 0,

+    upload: 0,

+    download: 0,

+    ratio: 0,

+  });

+

+  // 新增:根据userid从后端获取用户信息

+  useEffect(() => {

+    const fetchUserInfo = async () => {

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

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

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

+      if (!userid) return;

+      try {

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

+        if (res.ok) {

+          const data = await res.json();

+          setUserInfo(data);

+          setTempUserInfo(data);

+        }

+      } catch (err) {

+        // 可以根据需要处理错误

+        console.error("获取用户信息失败", err);

+      }

+    };

+    fetchUserInfo();

+  }, []);

+

+  // 动态加载用户上传种子列表

+  useEffect(() => {

+    const fetchUserSeeds = async () => {

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

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

+      if (!userid) return;

+      try {

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

+        if (res.ok) {

+          const data = await res.json();

+          setUserSeeds(data);

+        }

+      } catch (err) {

+        console.error("获取种子列表失败", err);

+      }

+    };

+    fetchUserSeeds();

+  }, []);

+

+  // 动态加载用户活跃度信息

+  useEffect(() => {

+    const fetchUserStats = async () => {

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

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

+      if (!userid) return;

+      try {

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

+        if (res.ok) {

+          const data = await res.json();

+          setUserStats(data);

+        }

+      } catch (err) {

+        console.error("获取活跃度信息失败", err);

+      }

+    };

+    fetchUserStats();

+  }, []);

 

   const handleInputChange = (field, value) => {

     setTempUserInfo({ ...tempUserInfo, [field]: value });

   };

 

-  const handleSave = () => {

+  const handleSave = async () => {

     setUserInfo({ ...tempUserInfo });

-    alert("信息已保存!");

+    // 获取userid

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

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

+    try {

+      const res = await fetch('/api/change-profile', {

+        method: 'POST',

+        headers: {

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

+        },

+        body: JSON.stringify({ userid, ...tempUserInfo }),

+      });

+      if (res.ok) {

+        alert("信息已保存!");

+      } else {

+        alert("保存失败,请重试。");

+      }

+    } catch (err) {

+      alert("保存失败,请检查网络连接。");

+      console.error("保存用户信息失败", err);

+    }

   };

 

   const handleAvatarClick = () => {

@@ -39,7 +127,7 @@
             <AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />

             {tempUserInfo.avatar && (

               <img

-                src={tempUserInfo.avatar}

+                src={tempUserInfo.avatar_url}

                 alt="用户头像"

                 style={{

                   position: 'absolute',

@@ -67,21 +155,19 @@
           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

             <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>

-            <input

-              type="email"

-              value={tempUserInfo.email}

-              onChange={(e) => handleInputChange("email", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

+            <span

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}

+            >

+              {tempUserInfo.email}

+            </span>

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

-            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>公司:</b>

-            <input

-              type="text"

-              value={tempUserInfo.company}

-              onChange={(e) => handleInputChange("company", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>

+            <span

+              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}

+            >

+              {tempUserInfo.invite_left}

+            </span>

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

             <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>

@@ -93,22 +179,21 @@
             />

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

-            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>生日:</b>

-            <input

-              type="date"

-              value={tempUserInfo.birthday}

-              onChange={(e) => handleInputChange("birthday", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

-          </div>

-          <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

-            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>密码:</b>

-            <input

-              type="password"

-              value={tempUserInfo.password || ""}

-              onChange={(e) => handleInputChange("password", e.target.value)}

-              style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}

-            />

+            <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>

+            <span

+              style={{ flex: 1, display: 'flex', alignItems: 'center', padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}

+            >

+              {tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}

+              <span style={{

+                display: 'inline-block',

+                width: 12,

+                height: 12,

+                borderRadius: '50%',

+                backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',

+                marginLeft: 10,

+                border: '1px solid #b2b2b2',

+              }} />

+            </span>

           </div>

           <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>

             <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>

@@ -187,15 +272,61 @@
       {/* 上传种子列表 */}

       <div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

         <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>

-        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>

-          (此处显示上传种子列表)

+        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 12 }}>

+          {userSeeds.length === 0 ? (

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

+          ) : (

+            <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>

+              {userSeeds.map((seed, idx) => (

+                <li

+                  key={seed.seed_id || idx}

+                  style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid #e0e7ff', cursor: 'pointer' }}

+                  onClick={e => {

+                    // 阻止点击删除按钮时跳转

+                    if (e.target.classList.contains('delete-btn')) return;

+                    navigate(`/torrent/${seed.seed_id}`);

+                  }}

+                >

+                  <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>

+                  <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>

+                  <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.popularity}</span>

+                  <button

+                    className="delete-btn"

+                    style={{ marginLeft: 18, background: '#e53935', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 14px', cursor: 'pointer', fontSize: 14 }}

+                    onClick={async (e) => {

+                      e.stopPropagation();

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

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

+                      try {

+                        const res = await fetch('/api/delete-seed', {

+                          method: 'POST',

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

+                          body: JSON.stringify({ seed_id: seed.seed_id, userid }),

+                        });

+                        if (res.ok) {

+                          setUserSeeds(userSeeds.filter((s, i) => (s.seed_id || i) !== (seed.seed_id || idx)));

+                        } else {

+                          alert('删除失败,请重试');

+                        }

+                      } catch (err) {

+                        alert('删除失败,请检查网络');

+                      }

+                    }}

+                  >删除</button>

+                </li>

+              ))}

+            </ul>

+          )}

         </div>

       </div>

       {/* 活跃度模块 */}

       <div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>

         <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>

-        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>

-          (此处显示活跃度信息)

+        <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 18, display: 'flex', flexDirection: 'column', gap: 12, fontSize: 18 }}>

+          <div>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></div>

+          <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload} GB</b></div>

+          <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download} GB</b></div>

+          <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.ratio}</b></div>

         </div>

       </div>

     </div>