修复种子详情下载

Change-Id: I5673279933d639e1bf425fcaea3eabd447625bfc
diff --git a/src/pages/PublishSeed/PublishSeed.css b/src/pages/PublishSeed/PublishSeed.css
index 9b33ad4..7525fb7 100644
--- a/src/pages/PublishSeed/PublishSeed.css
+++ b/src/pages/PublishSeed/PublishSeed.css
@@ -1,84 +1,118 @@
+.publish-seed-container {
+  background: #333;
+  color: #333;
+  min-height: 100vh;
+  padding: 20px 0;
+  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
 .pub-card {
   background-color: #e9ded2;
   border-radius: 16px;
-  max-width: 70%;
-  max-height: 100%;
-  /* 卡片居中 */
+  max-width: 800px;
   margin: 0 auto;
   margin-top: 40px;
-  /* padding: 24px 32px; */
-  padding: 3% 3%
-}  
-
-.pub-categoty select {
-  padding: 1%;
-  width: 20%;
-  border-radius: 6px;
-  border: 1px solid #e0c4a1; /* 浅棕色 */
-  background-color: #fff5f5; /* 浅粉色 */
-  color: #5F4437;
-  font-size: 1rem;
-  margin-bottom: 2%;
+  padding: 40px 48px;
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
 }
 
-.title-tag input {
-  padding: 1%;
+form > div {
+  margin-bottom: 20px;
+}
+
+label {
+  display: block;
+  font-weight: bold;
+  margin-bottom: 8px;
+  color: #5F4437;
+}
+
+input[type="text"],
+textarea,
+select {
   width: 100%;
+  padding: 10px 12px;
   border-radius: 6px;
-  border: 1px solid #e0c4a1; 
+  border: 1px solid #e0c4a1;
   background-color: #fff5f5;
   color: #5F4437;
   font-size: 1rem;
-  margin-bottom: 2%;
-}
-.discription textarea {
-  padding: 1%;
-  width: 100%;
-  border-radius: 6px;
-  border: 1px solid #e0c4a1; 
-  background-color: #fff5f5;
-  color: #5F4437;
-  font-size: 1rem;
-  margin-bottom: 2%;
-}
-.seed-file input {
-  width: 100%;
-  color: #5F4437;
-  font-size: 1rem;
-  margin-bottom: 2%;
+  transition: border-color 0.3s;
 }
 
-.cover-image input {
-  padding: 1%;
-  width: 100%;
-  border-radius: 6px;
-  border: 1px solid #e0c4a1; 
-  background-color: #fff5f5;
-  color: #5F4437;
-  font-size: 1rem;
-  margin-bottom: 2%;
+input[type="text"]:focus,
+textarea:focus,
+select:focus {
+  outline: none;
+  border-color: #c49c6b;
 }
 
-.publish-seed-container {
-  background: #333;
-  color: white;
-  border-radius: 12px;
-  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+textarea {
+  resize: vertical;
+  min-height: 80px;
+}
+
+.seed-file-label,
+.cover-upload button {
+  display: inline-block;
+  padding: 8px 16px;
+  border: 1px solid #e0c4a1;
+  background-color: #fff5f5;
+  border-radius: 6px;
+  cursor: pointer;
+  font-size: 14px;
+  font-weight: 500;
+  color: #5F4437;
+  transition: all 0.2s ease;
+}
+
+.seed-file-label:hover,
+.cover-upload button:hover {
+  background-color: #f1e0d0;
+}
+
+.seed-file input,
+.cover-upload input {
+  display: none;
+}
+
+.message {
+  background-color: #fff3cd;
+  color: #856404;
+  padding: 12px;
+  border-radius: 8px;
+  margin-bottom: 20px;
+  border: 1px solid #ffeeba;
 }
 
 .upload-button {
-  width: 100%;
+  text-align: center;
 }
 
-/* 文件选择按钮保持 label 方式,不变 */
-.seed-file-label {
-  display: inline-block;
-  padding: 6px 8px;
-  border: 1px solid #e0c4a1; 
-  background-color: #fff5f5;
-  border-radius: 4px;
+.upload-button button {
+  background-color: #5F4437;
+  color: #fff;
+  padding: 12px 32px;
+  border: none;
+  border-radius: 8px;
+  font-size: 16px;
   cursor: pointer;
-  font-size: 14px;
-  user-select: none;
-  width : 9%;
-}
\ No newline at end of file
+  transition: background-color 0.2s ease;
+}
+
+.upload-button button:hover {
+  background-color: #472f23;
+}
+
+.upload-button button:disabled {
+  background-color: #9c8c84;
+  cursor: not-allowed;
+}
+
+img {
+  border-radius: 8px;
+  border: 1px solid #ccc;
+  max-width: 100%;
+  max-height: 200px;
+  object-fit: contain;
+}
diff --git a/src/pages/PublishSeed/PublishSeed.jsx b/src/pages/PublishSeed/PublishSeed.jsx
index f064265..78b93bc 100644
--- a/src/pages/PublishSeed/PublishSeed.jsx
+++ b/src/pages/PublishSeed/PublishSeed.jsx
@@ -6,8 +6,6 @@
 import { useUser } from '../../context/UserContext';
 
 const PublishSeed = () => {
-  console.log('[DEBUG] PublishSeed 组件渲染');
-
   const [title, setTitle] = useState('');
   const [description, setDescription] = useState('');
   const [tags, setTags] = useState([]);
diff --git a/src/pages/SeedList/SeedDetail/SeedDetail.css b/src/pages/SeedList/SeedDetail/SeedDetail.css
index 594cb8e..9e1a10e 100644
--- a/src/pages/SeedList/SeedDetail/SeedDetail.css
+++ b/src/pages/SeedList/SeedDetail/SeedDetail.css
@@ -1,29 +1,35 @@
 .seed-detail-page {
   background: #333;
+  min-height: 100vh;
+  padding-bottom: 40px;
   font-family: 'Helvetica Neue', sans-serif;
   color: #333;
 }
 
 .seed-detail {
-  background-color: #e9ded2;
+  background-color: #fff9f3;
   border-radius: 16px;
   max-width: 960px;
-  margin: 0 auto;
-  margin-top: 40px;
-  padding: 24px 32px;
+  margin: 40px auto 0;
+  padding: 32px;
+  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
 }
 
 .seed-detail h1 {
-  font-size: 24px;
-  font-weight: bold;
-  margin-bottom: 16px;
-  color: #4A3B34;
+  font-size: 28px;
+  font-weight: 700;
+  margin-bottom: 24px;
+  color: #3d2e2a;
+  border-left: 4px solid #ba929a;
+  padding-left: 12px;
 }
 
 .seed-header-container {
   display: flex;
   justify-content: space-between;
   align-items: flex-start;
+  gap: 32px;
+  flex-wrap: wrap;
 }
 
 .seed-info {
@@ -33,89 +39,86 @@
 .seed-basic-info p,
 .seed-media-info p {
   font-size: 16px;
+  line-height: 1.6;
   margin: 6px 0;
+  color: #4a3b34;
 }
 
-.cover-image {
-  width: 200px;
+/* .cover-image {
+  width: 240px;
   height: auto;
-  margin-left: 20px;
-  border-radius: 8px;
+  border-radius: 12px;
+  object-fit: cover;
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
+} */
+ .cover-image {
+  max-width: 100%;
+  width: 240px;
+  max-height: 320px;
+  height: auto;
+  border-radius: 12px;
+  object-fit: contain; /* 从 cover 改为 contain */
+  background-color: #f6eae3; /* 给 contain 留白背景 */
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
 }
 
-.comment-options {
-  margin-top: 8px;
-  display: flex;
-  justify-content: flex-end; /* 将 space-between 改为 flex-end */
-}
 
 .action-buttons {
-  justify-content: flex-end; /*靠右对齐*/
+  margin-top: 24px;
   display: flex;
-  gap: 10px;
-  margin-bottom: 20px;
+  justify-content: flex-end;
+  gap: 12px;
+}
+
+.btn, .btn-outline {
+  padding: 10px 18px;
+  font-size: 15px;
+  border-radius: 8px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.2s ease-in-out;
 }
 
 .btn {
-  justify-content: flex-end; /*靠右对齐*/
-  padding: 6px 14px;
-  background-color: #BA929A;
-  color: #fff;
+  background-color: #ba929a;
+  color: white;
   border: none;
-  border-radius: 6px;
-  font-size: 14px;
-  cursor: pointer;
 }
 
 .btn:hover {
-  background-color: #5F4437;
+  background-color: #5f4437;
 }
 
-.comments-section {
-  margin-top: 20px;
+.btn-outline {
+  background-color: transparent;
+  color: #5f4437;
+  border: 2px solid #5f4437;
 }
 
-.comments-section h3 {
-  margin-bottom: 10px;
-  font-size: 20px;
-  color: #4A3B34;
-}
-
-.comments-list {
-  border-top: 1px solid #ccc;
-  padding-top: 10px;
-}
-
-.comment {
-  border-bottom: 1px solid #ccc;
-  padding: 10px 0;
-}
-
-.comment-user {
-  font-weight: bold;
-  margin-bottom: 5px;
-}
-
-.comment-content {
-  margin: 0;
-}
-
-.add-comment-form {
-  margin-top: 10px;
-}
-
-.add-comment-form textarea {
-  width: 100%;
-  padding: 10px;
-  resize: vertical;
-  min-height: 80px;
-  border: 1px solid #bbb;
-  border-radius: 6px;
-  margin-bottom: 10px;
+.btn-outline:hover {
+  background-color: #f6eae3;
 }
 
 .error-text {
-  color: #f00;
+  color: #e74c3c;
+  font-size: 18px;
   text-align: center;
+  margin-top: 60px;
 }
-  
\ No newline at end of file
+
+@media (max-width: 768px) {
+  .seed-header-container {
+    flex-direction: column;
+    align-items: center;
+  }
+
+  .cover-image {
+    width: 100%;
+    max-width: 320px;
+    margin-top: 20px;
+  }
+
+  .action-buttons {
+    justify-content: center;
+  }
+}
diff --git a/src/pages/SeedList/SeedDetail/SeedDetail.jsx b/src/pages/SeedList/SeedDetail/SeedDetail.jsx
index 16c6d01..0cb738c 100644
--- a/src/pages/SeedList/SeedDetail/SeedDetail.jsx
+++ b/src/pages/SeedList/SeedDetail/SeedDetail.jsx
@@ -5,7 +5,6 @@
 import './SeedDetail.css';
 import { useUser } from '../../../context/UserContext';
 
-
 const SeedDetail = () => {
   const params = useParams();
   const seed_id = params.id;
@@ -18,8 +17,6 @@
 
   const { user } = useUser();
 
-
-  // 格式化图片 URL
   const formatImageUrl = (url) => {
     if (!url) return '';
     const filename = url.split('/').pop();
@@ -37,8 +34,6 @@
         const res = await axios.post(`/seeds/info/${seed_id}`);
         if (res.data.code === 0) {
           const seedData = res.data.data;
-
-          // 处理封面图
           let cover = seedData.imageUrl;
           if (!cover && seedData.imgUrl) {
             const imgs = seedData.imgUrl
@@ -76,42 +71,76 @@
     fetchComments();
   }, [seed_id]);
 
- const handleDownload = async (seedId) => {
-        if (!user || !user.userId) {
-            alert('请先登录再下载种子文件');
-            return;
-        }
+  const handleDownload = async (seedId) => {
+    if (!user || !user.userId) {
+      alert('请先登录再下载种子文件');
+      return;
+    }
 
-        try {
-            const response = await axios.get(`/seeds/${seedId}/download`, {
-                params: {
-                    passkey: user.userId,
-                },
-                responseType: 'blob'
-            });
+    try {
+      const response = await axios.get(`/seeds/${seedId}/download`, {
+        params: { passkey: user.userId },
+        responseType: 'blob',
+      });
 
-            const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
-            const downloadUrl = URL.createObjectURL(blob);
-            const a = document.createElement('a');
-            a.href = downloadUrl;
-            a.download = `${seedId}.torrent`;
-            a.click();
-            URL.revokeObjectURL(downloadUrl);
-        } catch (error) {
-            console.error('下载失败:', error);
-            alert('下载失败,请稍后再试。');
-        }
-    };
-
-
-  const handleCollect = () => {
-    alert('已收藏');
+      const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
+      const downloadUrl = URL.createObjectURL(blob);
+      const a = document.createElement('a');
+      a.href = downloadUrl;
+      a.download = `${seedId}.torrent`;
+      a.click();
+      URL.revokeObjectURL(downloadUrl);
+    } catch (error) {
+      console.error('下载失败:', error);
+      alert('下载失败,请稍后再试。');
+    }
   };
 
-  const handleAddComment = () => {
+  const handleCollect = async () => {
+    if (!user || !user.userId) {
+      alert('请先登录再收藏');
+      return;
+    }
+
+    try {
+      const res = await axios.post(`/seeds/${seed.id}/favorite-toggle`, null, {
+        params: { user_id: user.userId },
+      });
+
+      if (res.data.code === 0) {
+        alert('操作成功');
+      } else {
+        alert(res.data.msg || '操作失败');
+      }
+    } catch (err) {
+      console.error('收藏失败:', err);
+      alert('收藏失败,请稍后再试。');
+    }
+  };
+
+  const handleAddComment = async () => {
+    if (!user || !user.userId) {
+      alert('请登录后发表评论');
+      return;
+    }
+
     if (newComment.trim()) {
-      setComments([...comments, { content: newComment, user: '用户' }]);
-      setNewComment('');
+      try {
+        const res = await axios.post(`/seeds/${seed_id}/comments`, {
+          user_id: user.userId,
+          content: newComment,
+        });
+
+        if (res.data.code === 0) {
+          setComments([...comments, { content: newComment, user: user.username || '匿名用户' }]);
+          setNewComment('');
+        } else {
+          alert(res.data.msg || '评论失败');
+        }
+      } catch (err) {
+        console.error('评论提交失败:', err);
+        alert('评论失败,请稍后重试');
+      }
     }
   };
 
@@ -137,20 +166,16 @@
     );
   }
 
-  const tags = seed.tags
-    ? Array.isArray(seed.tags)
-      ? seed.tags
-      : typeof seed.tags === 'string'
-      ? seed.tags.split(',').map((t) => t.trim())
-      : []
+  const tags = Array.isArray(seed.tags)
+    ? seed.tags
+    : typeof seed.tags === 'string'
+    ? seed.tags.split(',').map((t) => t.trim())
     : [];
 
-  const actors = seed.actors
-    ? Array.isArray(seed.actors)
-      ? seed.actors
-      : typeof seed.actors === 'string'
-      ? seed.actors.split(',').map((a) => a.trim())
-      : []
+  const actors = Array.isArray(seed.actors)
+    ? seed.actors
+    : typeof seed.actors === 'string'
+    ? seed.actors.split(',').map((a) => a.trim())
     : [];
 
   return (
@@ -185,20 +210,26 @@
             className="cover-image"
           />
         </div>
+
         <div className="action-buttons">
           <button className="btn" onClick={() => handleDownload(seed.id)}>下载</button>
-          <button className="btn" onClick={handleCollect}>收藏</button>
+          <button className="btn-outline" onClick={handleCollect}>收藏</button>
         </div>
+
         <hr className="divider" />
         <h3>评论区</h3>
         <div className="comments-section">
           <div className="comments-list">
-            {comments.map((comment, index) => (
-              <div key={index} className="comment">
-                <p className="comment-user">{comment.user}</p>
-                <p className="comment-content">{comment.content}</p>
-              </div>
-            ))}
+            {comments.length === 0 ? (
+              <p className="no-comments">暂无评论</p>
+            ) : (
+              comments.map((comment, index) => (
+                <div key={index} className="comment">
+                  <p className="comment-user">{comment.user}</p>
+                  <p className="comment-content">{comment.content}</p>
+                </div>
+              ))
+            )}
           </div>
           <div className="add-comment-form">
             <textarea
diff --git a/src/pages/UserCenter/UserCenterPage.jsx b/src/pages/UserCenter/UserCenterPage.jsx
index b6fb20e..471d6c8 100644
--- a/src/pages/UserCenter/UserCenterPage.jsx
+++ b/src/pages/UserCenter/UserCenterPage.jsx
@@ -1,8 +1,8 @@
 import React, { useState } from 'react';
-import UserNav from './UserNav.jsx'; // 显式添加.jsx扩展名
+import UserNav from './UserNav.jsx'; 
 import UserProfile from './UserProfile.jsx';
 import NewbieTasks from './NewbieTasks.jsx';
-import Header from '../../components/Header.jsx'; // 假设Header也是JSX组件
+import Header from '../../components/Header.jsx'; 
 import './UserProfile.css';
 
 const UserCenterPage = () => {
diff --git a/src/pages/UserCenter/UserNav.jsx b/src/pages/UserCenter/UserNav.jsx
index c272424..63b83ca 100644
--- a/src/pages/UserCenter/UserNav.jsx
+++ b/src/pages/UserCenter/UserNav.jsx
@@ -5,14 +5,14 @@
 const UserNav = () => {
   const location = useLocation();
 
-  // 竖直导航栏的链接项(已添加“新手考核”)
+  // 竖直导航栏的链接项
   const navLinks = [
     { to: '/user/profile', label: '个人资料' },
     { to: '/user/dynamics', label: '我的动态' },
     { to: '/user/friends', label: '我的好友' },
     { to: '/user/groups', label: '我的群组' },
     { to: '/user/collections', label: '我的收藏' },
-    // { to: '/user/newbie-tasks', label: '新手考核' },
+    // { to: '/user/newbie-tasks', label: '用户考核' },
     { to: '/user/invite', label: '邀请新用户' },
     { to: '/user/recharge', label: '充值服务' },
   ];
diff --git a/src/pages/UserCenter/UserProfile.css b/src/pages/UserCenter/UserProfile.css
index cb71be7..edc04e1 100644
--- a/src/pages/UserCenter/UserProfile.css
+++ b/src/pages/UserCenter/UserProfile.css
@@ -7,7 +7,6 @@
   background: #333;
 }
 
-
 .right-content {
   flex: 3;
 }
@@ -63,15 +62,6 @@
   /* padding: 10% 20%; */
 }  
 
-/* .common-card {
-  background-color: #e9ded2;
-  border-radius: 16px;
-  margin: 0 auto;
-  margin-top: 40px;
-  padding: 10% 20%;
-  margin-left: 5%;
-  margin-right: 5%;
-} */
    
 .avatar-wrapper {
   position: relative;
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index bd87c99..cf52d99 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -264,6 +264,7 @@
 import React from 'react';
 import UserProfileBase from './UserProfileBase';
 import UserLevelExperience from './UserLevelExperience';
+import './UserProfile.css';
 
 const UserProfile = () => {
   const [userId, setUserId] = React.useState(null);
diff --git a/src/pages/Recharge/Recharge.css b/src/pages/UserCenter/UserRecharge.css
similarity index 100%
rename from src/pages/Recharge/Recharge.css
rename to src/pages/UserCenter/UserRecharge.css
diff --git a/src/pages/Recharge/Recharge.jsx b/src/pages/UserCenter/UserRecharge.jsx
similarity index 64%
rename from src/pages/Recharge/Recharge.jsx
rename to src/pages/UserCenter/UserRecharge.jsx
index 0df1837..7a2f9b3 100644
--- a/src/pages/Recharge/Recharge.jsx
+++ b/src/pages/UserCenter/UserRecharge.jsx
@@ -1,73 +1,10 @@
-// import React, { useState } from 'react';
-// import axios from 'axios';
-// import { useUser } from '../../context/UserContext';
-
-// const Recharge = () => {
-//   const [amount, setAmount] = useState('');
-//   const [loading, setLoading] = useState(false);
-//   const [message, setMessage] = useState('');
-
-//   // 从上下文获取当前登录用户
-//   const { user } = useUser();
-//   const userId = user?.userId;
-
-//   const handleRecharge = async () => {
-//     if (!userId) {
-//       setMessage('未登录,无法充值');
-//       return;
-//     }
-//     if (!amount || isNaN(amount) || Number(amount) <= 0) {
-//       setMessage('请输入有效的充值金额');
-//       return;
-//     }
-//     setLoading(true);
-//     setMessage('');
-//     try {
-//       // 充值接口调用,注意后端用的是请求参数形式传递 userId 和 amount
-//       const response = await axios.post('/echo/user/recharge', null, {
-//         params: {
-//           userId,
-//           amount: Number(amount),
-//         },
-//       });
-//       setMessage(response.data.message || '充值成功!');
-//       setAmount('');
-//     } catch (error) {
-//       setMessage(error.response?.data?.message || '充值失败,请重试');
-//     } finally {
-//       setLoading(false);
-//     }
-//   };
-
-//   return (
-//     <div className="recharge-page">
-//       <h2>充值服务</h2>
-//       <div>
-//         <label>
-//           充值金额:
-//           <input
-//             type="number"
-//             min="1"
-//             value={amount}
-//             onChange={(e) => setAmount(e.target.value)}
-//             disabled={loading}
-//           />
-//         </label>
-//       </div>
-//       <button onClick={handleRecharge} disabled={loading}>
-//         {loading ? '处理中...' : '提交充值'}
-//       </button>
-//       {message && <p>{message}</p>}
-//     </div>
-//   );
-// };
 
 // export default Recharge;
 import React, { useState } from 'react';
 import axios from 'axios';
 import { useUser } from '../../context/UserContext';
 
-const Recharge = () => {
+const UserRecharge = () => {
   const [amount, setAmount] = useState('');
   const [loading, setLoading] = useState(false);
   const [message, setMessage] = useState('');
@@ -189,4 +126,4 @@
   );
 };
 
-export default Recharge;
+export default UserRecharge;