修复种子详情下载
Change-Id: I5673279933d639e1bf425fcaea3eabd447625bfc
diff --git a/src/App.js b/src/App.js
index 2b87baf..fe3c582 100644
--- a/src/App.js
+++ b/src/App.js
@@ -17,7 +17,7 @@
import MessagePage from './pages/MessagePage/MessagePage';
import CreateMoment from './pages/FriendMoments/CreateMoment';
// import LevelPage from './pages/LevelPage/LevelPage';
-// import NewbieTasks from './pages/UserCenter/NewbieTasks';
+import NewbieTasks from './pages/NewUserGuide/NewbieTasks';
import UserDynamics from './pages/UserCenter/UserDynamics';
import UserFriends from './pages/UserCenter/UserFriends';
import UserCollect from './pages/UserCenter/UserCollect';
@@ -25,7 +25,7 @@
import UserInfo from './pages/UserInfo/UserInfo';
import UserLayout from './pages/UserCenter/UserLayout';
import NewUserGuide from './pages/NewUserGuide/NewUserGuide';
-import Recharge from './pages/Recharge/Recharge';
+import UserRecharge from './pages/UserCenter/UserRecharge';
function RedirectToAuth() {
if (typeof window !== 'undefined') {
@@ -81,9 +81,9 @@
<Route path="/user/profile" component={() => (
<PrivateRoute component={() => <UserLayout><UserProfile /></UserLayout>} />
)} />
- {/* <Route path="/user/newbie-tasks" component={() => (
+ <Route path="/user/newbie-tasks" component={() => (
<PrivateRoute component={() => <UserLayout><NewbieTasks /></UserLayout>} />
- )} /> */}
+ )} />
<Route path="/user/dynamics" component={() => (
<PrivateRoute component={() => <UserLayout><UserDynamics /></UserLayout>} />
)} />
@@ -96,11 +96,10 @@
<Route path="/user/invite" component={() => (
<PrivateRoute component={() => <UserLayout><UserInvite /></UserLayout>} />
)} />
- <Route path ="user/recharge" component={() => (
- <PrivateRoute component={() => <UserLayout><Recharge /></UserLayout>} />
+ <Route path="/user/recharge" component={() => (
+ <PrivateRoute component={() => <UserLayout><UserRecharge /></UserLayout>} />
)} />
-
- </>
+ </>
</GroupProvider>
</UserProvider>
);
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;