修复查看帖子、评论、回复评论、点赞、收藏,添加用户等级
Change-Id: Ida9590d7ccee08dcd787a36c7e5cb39a3e26cd0d
diff --git a/src/pages/AuthPage/AuthPage.jsx b/src/pages/AuthPage/AuthPage.jsx
index bfc8e43..1aa7b24 100644
--- a/src/pages/AuthPage/AuthPage.jsx
+++ b/src/pages/AuthPage/AuthPage.jsx
@@ -25,7 +25,7 @@
import "./AuthPage.css";
import CryptoJS from "crypto-js";
-const API_BASE = process.env.REACT_APP_API_BASE;
+
function AuthPage() {
const [activeTab, setActiveTab] = useState("login");
@@ -78,7 +78,7 @@
const hashedPassword = hashPassword(loginData.password);
try {
- const response = await fetch(`${API_BASE}/echo/user/login`, {
+ const response = await fetch(`/echo/user/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -148,7 +148,7 @@
const hashedPassword = hashPassword(registerData.password);
try {
- const response = await fetch(`${API_BASE}/echo/user/register`, {
+ const response = await fetch(`/echo/user/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
diff --git a/src/pages/Forum/posts-create/CreatePost.jsx b/src/pages/Forum/posts-create/CreatePost.jsx
index b8e8c2a..07f3a0c 100644
--- a/src/pages/Forum/posts-create/CreatePost.jsx
+++ b/src/pages/Forum/posts-create/CreatePost.jsx
@@ -2,7 +2,7 @@
// import axios from 'axios';
// import './CreatePost.css'; // 如果你打算加样式
-// const API_BASE = process.env.REACT_APP_API_BASE;
+//
// const CreatePost = ({ user_id }) => {
// const [title, setTitle] = useState('');
@@ -22,7 +22,7 @@
// }
// try {
-// const res = await axios.post(`${API_BASE}/echo/forum/posts/${user_id}/createPost`, {
+// const res = await axios.post(`/echo/forum/posts/${user_id}/createPost`, {
// title,
// post_content: content,
// image_url: imageUrl
@@ -83,7 +83,7 @@
import axios from 'axios';
import './CreatePost.css'; // 如果你打算加样式
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const CreatePost = ({ user_id }) => {
const [title, setTitle] = useState('');
@@ -103,7 +103,7 @@
}
try {
- const res = await axios.post(`${API_BASE}/echo/forum/posts/${user_id}/createPost`, {
+ const res = await axios.post(`/echo/forum/posts/${user_id}/createPost`, {
title,
post_content: content,
image_url: imageUrl
diff --git a/src/pages/Forum/posts-detail/PostDetailPage.jsx b/src/pages/Forum/posts-detail/PostDetailPage.jsx
index c58ed33..a505d88 100644
--- a/src/pages/Forum/posts-detail/PostDetailPage.jsx
+++ b/src/pages/Forum/posts-detail/PostDetailPage.jsx
@@ -1,284 +1,3 @@
-// import React, { useEffect, useState } from 'react';
-// import { useParams } from 'wouter';
-// import { GoodTwo, Star } from '@icon-park/react';
-// import { getPostDetail, getPostComments, likePost, unlikePost, addCommentToPost, collectPost } from './api'; // 引入你的 API 函数
-// import './PostDetailPage.css';
-// import { useUser } from '../../../context/UserContext'; // 注意路径
-// import Header from '../../../components/Header';
-
-// const PostDetailPage = () => {
-// const { postId } = useParams(); // 获取帖子ID
-// const [postDetail, setPostDetail] = useState(null);
-// const [comments, setComments] = useState([]);
-// const [loading, setLoading] = useState(true);
-// const [errorMsg, setErrorMsg] = useState('');
-// const [newComment, setNewComment] = useState(''); // 新评论内容
-// const [isAnonymous, setIsAnonymous] = useState(false); // 是否匿名
-// const [isLiked, setIsLiked] = useState(false); // 是否已点赞
-// const [isCollected, setIsCollected] = useState(false); // 是否已收藏
-// const [replyToCommentId, setReplyToCommentId] = useState(null); // 回复的评论ID
-
-// // 获取当前用户ID(假设从上下文中获取)
-// const { user } = useUser(); // 你需要从用户上下文获取用户 ID
-
-// useEffect(() => {
-// const fetchPostDetail = async () => {
-// setLoading(true);
-// setErrorMsg('');
-// try {
-// // 获取帖子详情
-// const postData = await getPostDetail(postId);
-// setPostDetail(postData);
-
-// // 获取帖子评论
-// const commentsData = await getPostComments(postId);
-// setComments(commentsData);
-
-// // 设置是否已经点赞
-// if (postData.likedByUser) {
-// setIsLiked(true);
-// } else {
-// setIsLiked(false);
-// }
-
-// // 设置是否已经收藏
-// if (postData.collectedByUser) {
-// setIsCollected(true);
-// } else {
-// setIsCollected(false);
-// }
-// } catch (err) {
-// console.error('加载失败:', err);
-// setErrorMsg('加载失败,请稍后重试');
-// } finally {
-// setLoading(false);
-// }
-// };
-
-// fetchPostDetail();
-// }, [postId]);
-
-// // 点赞功能
-// const toggleLike = async () => {
-// if (!user) {
-// alert('请先登录');
-// return;
-// }
-
-// try {
-// if (isLiked) {
-// // 取消点赞
-// await unlikePost(postId, user.id);
-// setIsLiked(false);
-// setPostDetail((prev) => ({
-// ...prev,
-// postLikeNum: prev.postLikeNum - 1,
-// }));
-// } else {
-// // 点赞
-// await likePost(postId, user.id);
-// setIsLiked(true);
-// setPostDetail((prev) => ({
-// ...prev,
-// postLikeNum: prev.postLikeNum + 1,
-// }));
-// }
-// } catch (err) {
-// console.error('点赞失败:', err);
-// alert('点赞失败,请稍后再试');
-// }
-// };
-
-// // 收藏功能
-// const toggleCollect = async () => {
-// if (!user) {
-// alert('请先登录');
-// return;
-// }
-
-// try {
-// const action = isCollected ? 'cancel' : 'collect';
-// // 调用收藏 API
-// await collectPost(postId, user.id, action);
-// setIsCollected(!isCollected);
-// setPostDetail((prev) => ({
-// ...prev,
-// postCollectNum: isCollected ? prev.postCollectNum - 1 : prev.postCollectNum + 1,
-// }));
-// } catch (err) {
-// console.error('收藏失败:', err);
-// alert('收藏失败,请稍后再试');
-// }
-// };
-
-// // 添加评论
-// const handleAddComment = async () => {
-// if (!newComment.trim()) {
-// alert('评论内容不能为空');
-// return;
-// }
-
-// try {
-// // 调用 API 添加评论,若为回复评论则传递父评论ID(com_comment_id)
-// const commentData = await addCommentToPost(postId, user.id, newComment, isAnonymous, replyToCommentId);
-// // 更新评论列表
-// setComments((prev) => [
-// ...prev,
-// {
-// commentId: commentData.commentId,
-// post_id: postId,
-// userId: user.id,
-// content: newComment,
-// isAnonymous,
-// commentTime: new Date().toISOString(),
-// comCommentId: replyToCommentId, // 回复评论时传递父评论ID
-// },
-// ]);
-// // 清空评论框和回复状态
-// setNewComment('');
-// setReplyToCommentId(null);
-// } catch (err) {
-// console.error('评论添加失败:', err);
-// alert('评论失败,请稍后再试');
-// }
-// };
-
-// // 回复评论
-// const handleReply = (commentId) => {
-// setReplyToCommentId(commentId); // 设置父评论ID为当前评论的ID
-// };
-
-// return (
-// <div className="post-detail-page">
-// <Header />
-// {loading ? (
-// <p>加载中...</p>
-// ) : errorMsg ? (
-// <p className="error-text">{errorMsg}</p>
-// ) : postDetail ? (
-// <div className="post-detail">
-// <h1>{postDetail.title}</h1>
-// <div className="post-meta">
-// <span className="post-user">用户ID: {postDetail.user_id}</span>
-// <span className="post-time">
-// 发布时间:{new Date(postDetail.postTime).toLocaleString()}
-// </span>
-// </div>
-// <div className="post-content">
-// <p>{postDetail.postContent}</p>
-// {Array.isArray(postDetail.imgUrl) ? (
-// <div className="post-images">
-// {postDetail.imgUrl.map((url, idx) => (
-// <img key={idx} src={url} alt={`图片${idx}`} />
-// ))}
-// </div>
-// ) : (
-// postDetail.imgUrl && (
-// <img className="post-image" src={postDetail.imgUrl} alt="帖子图片" />
-// )
-// )}
-
-// </div>
-
-// {/* 点赞和收藏 */}
-// <div className="post-actions">
-// <button
-// className="icon-btn"
-// onClick={toggleLike} // 点赞操作
-// >
-// <GoodTwo
-// theme="outline"
-// size="20"
-// fill={isLiked ? '#f00' : '#ccc'} // 如果已点赞,显示红色
-// />
-// <span>{postDetail.postLikeNum}</span>
-// </button>
-// <button
-// className="icon-btn"
-// onClick={toggleCollect} // 收藏操作
-// >
-// <Star
-// theme="outline"
-// size="20"
-// fill={isCollected ? '#ffd700' : '#ccc'} // 如果已收藏,显示金色
-// />
-// <span>{postDetail.postCollectNum}</span>
-// </button>
-// </div>
-
-// <hr className="divider" />
-// {/* 评论部分 */}
-// <h3>评论区</h3>
-// <div className="comments-section">
-// {comments.length ? (
-// comments.map((comment) => (
-// <div key={comment.commentId} className="comment">
-// <div className="comment-header">
-// <span className="comment-user">用户 ID: {comment.userId}</span>
-// <button className="reply-btn" onClick={() => handleReply(comment.commentId)}>回复</button>
-// </div>
-// <p className="comment-content">{comment.content}</p>
-// <div className="comment-time">
-// {new Date(comment.commentTime).toLocaleString()}
-// </div>
-
-// {/* 回复框,只有在当前评论是正在回复的评论时显示 */}
-// {replyToCommentId === comment.commentId && (
-// <div className="reply-form">
-// <textarea
-// placeholder="输入你的回复..."
-// value={newComment}
-// onChange={(e) => setNewComment(e.target.value)}
-// />
-// <div className="comment-options">
-// <label>
-// <input
-// type="checkbox"
-// checked={isAnonymous}
-// onChange={() => setIsAnonymous(!isAnonymous)}
-// />
-// 匿名评论
-// </label>
-// <button onClick={handleAddComment}>发布回复</button>
-// </div>
-// </div>
-// )}
-// </div>
-// ))
-// ) : (
-// <p>暂无评论</p>
-// )}
-
-// {/* 添加评论表单 */}
-// <div className="add-comment-form">
-// <textarea
-// placeholder="输入你的评论..."
-// value={newComment}
-// onChange={(e) => setNewComment(e.target.value)}
-// />
-// <div className="comment-options">
-// <label>
-// <input
-// type="checkbox"
-// checked={isAnonymous}
-// onChange={() => setIsAnonymous(!isAnonymous)}
-// />
-// 匿名评论
-// </label>
-// <button onClick={handleAddComment}>发布评论</button>
-// </div>
-// </div>
-// </div>
-// </div>
-// ) : (
-// <p>帖子不存在</p>
-// )}
-// </div>
-// );
-// };
-
-// export default PostDetailPage;
-
import React, { useEffect, useState } from 'react';
import { useParams } from 'wouter';
import { GoodTwo, Star } from '@icon-park/react';
@@ -298,6 +17,7 @@
const [isLiked, setIsLiked] = useState(false); // 是否已点赞
const [isCollected, setIsCollected] = useState(false); // 是否已收藏
const [replyToCommentId, setReplyToCommentId] = useState(null); // 回复的评论ID
+ const [replyToUsername, setReplyToUsername] = useState(null);
// 获取当前用户ID(假设从上下文中获取)
const { user } = useUser(); // 你需要从用户上下文获取用户 ID
@@ -402,41 +122,61 @@
};
// 添加评论
- const handleAddComment = async () => {
- if (!newComment.trim()) {
- alert('评论内容不能为空');
- return;
- }
+const handleAddComment = async () => {
+ if (!newComment.trim()) {
+ alert('评论内容不能为空');
+ return;
+ }
- try {
- // 调用 API 添加评论,若为回复评论则传递父评论ID(com_comment_id)
- const commentData = await addCommentToPost(postId, user.id, newComment, replyToCommentId);
- // 更新评论列表
- setComments((prev) => [
- ...prev,
- {
- commentId: commentData.commentId,
- post_id: postId,
- userId: user.id,
- content: newComment,
- // isAnonymous,
- commentTime: new Date().toISOString(),
- comCommentId: replyToCommentId, // 回复评论时传递父评论ID
- },
- ]);
- // 清空评论框和回复状态
- setNewComment('');
- setReplyToCommentId(null);
- } catch (err) {
- console.error('评论添加失败:', err);
- alert('评论失败,请稍后再试');
- }
- };
+ try {
+ const commentPayload = {
+ content: newComment,
+ userId: user.id,
+ isAnonymous: false,
+ com_comment_id: replyToCommentId || null,
+ };
+
+ const commentData = await addCommentToPost(postId, commentPayload);
+
+ setComments((prev) => [
+ ...prev,
+ {
+ commentId: (commentData && commentData.commentId) || Date.now(),
+ post_id: postId,
+ userId: user.id,
+ content: newComment,
+ commentTime: new Date().toISOString(),
+ comCommentId: replyToCommentId,
+ },
+ ]);
+
+ setNewComment('');
+ setReplyToCommentId(null);
+ } catch (err) {
+ console.error('评论添加失败:', err);
+ alert('评论失败,请稍后再试');
+ }
+};
+
+
// 回复评论
const handleReply = (commentId) => {
- setReplyToCommentId(commentId); // 设置父评论ID为当前评论的ID
- };
+ setReplyToCommentId(commentId);
+ const comment = comments.find(c => c.commentId === commentId);
+ if (comment) {
+ // 这里用用户名或者用户ID
+ setReplyToUsername(comment.username || comment.userId);
+ } else {
+ setReplyToUsername(null);
+ }
+};
+
+const findUsernameByCommentId = (id) => {
+ const comment = comments.find(c => c.commentId === id);
+ return comment ? (comment.username || comment.userId) : '未知用户';
+};
+
return (
<div className="post-detail-page">
@@ -499,7 +239,7 @@
<hr className="divider" />
{/* 评论部分 */}
<h3>评论区</h3>
- <div className="comments-section">
+ <div className="comments-section">
{comments.length ? (
comments.map((comment) => (
<div key={comment.commentId} className="comment">
@@ -507,28 +247,34 @@
<span className="comment-user">用户 ID: {comment.userId}</span>
<button className="reply-btn" onClick={() => handleReply(comment.commentId)}>回复</button>
</div>
- <p className="comment-content">{comment.content}</p>
+
+ <p className="comment-content">
+ {comment.comCommentId ? (
+ <>
+ <span className="reply-to">回复 {findUsernameByCommentId(comment.comCommentId)}:</span>
+ {comment.content}
+ </>
+ ) : (
+ comment.content
+ )}
+ </p>
+
<div className="comment-time">
{new Date(comment.commentTime).toLocaleString()}
</div>
- {/* 回复框,只有在当前评论是正在回复的评论时显示 */}
+ {/* 回复框 */}
{replyToCommentId === comment.commentId && (
<div className="reply-form">
+ <div className="replying-to">
+ 回复 <strong>{replyToUsername}</strong>:
+ </div>
<textarea
placeholder="输入你的回复..."
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
/>
<div className="comment-options">
- {/* <label> */}
- {/* <input */}
- {/* type="checkbox" */}
- {/* checked={isAnonymous} */}
- {/* onChange={() => setIsAnonymous(!isAnonymous)} */}
- {/* /> */}
- {/* 匿名评论 */}
- {/* </label> */}
<button onClick={handleAddComment}>发布回复</button>
</div>
</div>
@@ -539,6 +285,7 @@
<p>暂无评论</p>
)}
+
{/* 添加评论表单 */}
<div className="add-comment-form">
<textarea
diff --git a/src/pages/Forum/posts-detail/api.js b/src/pages/Forum/posts-detail/api.js
index 27c3a18..53dbfd3 100644
--- a/src/pages/Forum/posts-detail/api.js
+++ b/src/pages/Forum/posts-detail/api.js
@@ -1,46 +1,60 @@
import axios from 'axios';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
// 获取帖子详情
export const getPostDetail = async (post_id) => {
- const response = await axios.get(`${API_BASE}/echo/forum/posts/${post_id}/getPost`);
+ const response = await axios.get(`/echo/forum/posts/${post_id}/getPost`);
return response.data;
};
// 获取帖子评论
export const getPostComments = async (post_id) => {
- const response = await axios.get(`${API_BASE}/echo/forum/posts/${post_id}/getAllComments`);
+ const response = await axios.get(`/echo/forum/posts/${post_id}/getAllComments`);
+ return response.data;
+};
+
+// 添加评论
+export const addCommentToPost = async (postId, commentPayload) => {
+ const res = await fetch(`/echo/forum/posts/${postId}/comments`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(commentPayload),
+ });
+
+ if (!res.ok) {
+ throw new Error('请求失败,状态码:' + res.status);
+ }
+
+ // 试着解析 JSON,如果失败则返回 null(可能是空响应)
+ try {
+ const data = await res.json();
+ return data;
+ } catch {
+ return null;
+ }
+};
+
+
+
+// 点赞评论
+export const likeComment = async (commentId) => {
+ const response = await axios.post(`/echo/forum/comments/${commentId}/like`);
+ return response.data;
+};
+
+// 取消点赞评论
+export const unlikeComment = async (commentId) => {
+ const response = await axios.delete(`/echo/forum/comments/${commentId}/unlike`);
return response.data;
};
// 点赞帖子
export const likePost = async (post_id, userId) => {
try {
- const response = await axios.post(`${API_BASE}/echo/forum/posts/${post_id}/like`, {
- user_id: userId, // 用户 ID
- });
- return response.data;
- } catch (error) {
- return handleApiError(error);
- }
-};
-
-// 取消点赞帖子
-export const unlikePost = async (post_id) => {
- const response = await axios.delete(`${API_BASE}/echo/forum/posts/${post_id}/unlike`);
- return response.data;
-};
-
-// 添加评论
-export const addCommentToPost = async (post_id, userId, content, isAnonymous, comCommentId = null) => {
- try {
- const response = await axios.post(`${API_BASE}/echo/forum/posts/${post_id}/comments`, {
- content,
+ const response = await axios.post(`/echo/forum/posts/${post_id}/like`, {
user_id: userId,
- is_anonymous: isAnonymous,
- com_comment_id: comCommentId, // 如果是回复评论,传递 com_comment_id
});
return response.data;
} catch (error) {
@@ -48,24 +62,23 @@
}
};
-// 点赞评论
-export const likeComment = async (commentId) => {
- const response = await axios.post(`${API_BASE}/echo/forum/comments/${commentId}/like`);
- return response.data;
-};
-
-// 取消点赞评论
-export const unlikeComment = async (commentId) => {
- const response = await axios.delete(`${API_BASE}/echo/forum/comments/${commentId}/unlike`);
- return response.data;
+// 取消点赞帖子(改为 POST 请求,携带 user_id)
+export const unlikePost = async (post_id, userId) => {
+ try {
+ const response = await axios.post(`/echo/forum/posts/${post_id}/unlike`, {
+ user_id: userId,
+ });
+ return response.data;
+ } catch (error) {
+ return handleApiError(error);
+ }
};
// 收藏帖子
-export const collectPost = async (post_id, userId, action) => {
+export const collectPost = async (post_id, userId) => {
try {
- const response = await axios.post(`${API_BASE}/echo/forum/posts/${post_id}/collect`, {
+ const response = await axios.post(`/echo/forum/posts/${post_id}/collect`, {
user_id: userId,
- action: action, // "collect" 或 "cancel"
});
return response.data;
} catch (error) {
@@ -73,17 +86,21 @@
}
};
-// // 取消收藏帖子
-// export const uncollectPost = async (post_id, userId) => {
-// const response = await axios.post(`${API_BASE}/echo/forum/posts/${post_id}/uncollect`, {
-// user_id: userId, // 用户 ID
-// });
-// return response.data;
-// };
+// 取消收藏帖子(使用 POST 请求)
+export const uncollectPost = async (post_id, userId) => {
+ try {
+ const response = await axios.post(`/echo/forum/posts/${post_id}/uncollect`, {
+ user_id: userId,
+ });
+ return response.data;
+ } catch (error) {
+ return handleApiError(error);
+ }
+};
-// 获取用户信息
+
export const getUserInfo = async (userId) => {
- const response = await axios.get(`${API_BASE}/user/${userId}/info`);
+ const response = await axios.get(`/user/${userId}/info`);
return response.data;
};
diff --git a/src/pages/Forum/posts-main/ForumPage.jsx b/src/pages/Forum/posts-main/ForumPage.jsx
index f3866ff..e714415 100644
--- a/src/pages/Forum/posts-main/ForumPage.jsx
+++ b/src/pages/Forum/posts-main/ForumPage.jsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import Header from '../../../components/Header';
-import SearchBar from './components/SearchBar';
+// import SearchBar from './components/SearchBar';
import CreatePostButton from './components/CreatePostButton';
import PostList from './components/PostList';
import './ForumPage.css';
@@ -20,7 +20,7 @@
<Promotion />
<div className="toolbar">
<CreatePostButton />
- <SearchBar onSearch={handleSearch} />
+ {/* <SearchBar onSearch={handleSearch} /> */}
</div>
<PostList search={searchQuery} />
</div>
diff --git a/src/pages/Forum/posts-main/components/CreatePostButton.jsx b/src/pages/Forum/posts-main/components/CreatePostButton.jsx
index 4f7d7b1..0f23e82 100644
--- a/src/pages/Forum/posts-main/components/CreatePostButton.jsx
+++ b/src/pages/Forum/posts-main/components/CreatePostButton.jsx
@@ -3,67 +3,56 @@
import { Edit } from '@icon-park/react';
import './CreatePostButton.css';
-const API_BASE = process.env.REACT_APP_API_BASE;
-const user_id = 456;
+const user = JSON.parse(localStorage.getItem('user')); // user = { user_id: 123, ... }
+const userId = user?.user_id;
+
const CreatePostButton = () => {
const [showModal, setShowModal] = useState(false);
- // 表单字段
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [previewUrls, setPreviewUrls] = useState([]);
- const [imageUrls, setImageUrls] = useState([]);
+ const [files, setFiles] = useState([]);
- // 处理文件选中:预览 & 上传
- const handleImageChange = async (e) => {
- const files = Array.from(e.target.files);
- if (!files.length) return;
- // 本地预览
- setPreviewUrls(files.map(f => URL.createObjectURL(f)));
-
- // 并发上传,假设 /upload 接口返回 { url: '...' }
- try {
- const uploaded = await Promise.all(
- files.map(file => {
- const fd = new FormData();
- fd.append('file', file);
- return axios.post(`${API_BASE}/upload`, fd, {
- headers: { 'Content-Type': 'multipart/form-data' }
- }).then(res => res.data.url);
- })
- );
- setImageUrls(uploaded);
- } catch (err) {
- console.error('图片上传失败:', err);
- alert('封面图上传失败,请重试');
- }
+ // 选择图片并预览
+ const handleImageChange = (e) => {
+ const selectedFiles = Array.from(e.target.files);
+ if (!selectedFiles.length) return;
+ setFiles(selectedFiles);
+ setPreviewUrls(selectedFiles.map(f => URL.createObjectURL(f)));
};
- // 提交发帖
const handleSubmit = async () => {
if (!title.trim() || !content.trim()) {
alert('标题和内容均为必填项');
return;
}
+ const formData = new FormData();
+ formData.append('title', title.trim());
+ formData.append('postContent', content.trim());
+
+ files.forEach(file => {
+ formData.append('imageUrl', file); // 多文件使用同一个字段名
+ });
+
try {
await axios.post(
- `${API_BASE}/echo/forum/posts/${user_id}/createPost`,
+ `/echo/forum/posts/${userId}/createPost`,
+ formData,
{
- title: title.trim(),
- post_content: content.trim(),
- image_url: imageUrls
+ headers: { 'Content-Type': 'multipart/form-data' }
}
);
- // 重置状态并关闭
+
+ // 清空表单
setTitle('');
setContent('');
+ setFiles([]);
setPreviewUrls([]);
- setImageUrls([]);
setShowModal(false);
alert('发帖成功');
- // 如需刷新帖子列表,可在这里触发外部回调
} catch (err) {
console.error('发帖失败:', err.response?.data || err);
alert(err.response?.data?.error || '发帖失败,请稍后重试');
@@ -110,7 +99,7 @@
<div className="cp-preview">
{previewUrls.map((url, i) => (
- <img key={i} src={url} alt={`封面预览 ${i}`} />
+ <img key={i} src={url} alt={`预览 ${i}`} />
))}
</div>
@@ -130,3 +119,4 @@
};
export default CreatePostButton;
+
diff --git a/src/pages/Forum/posts-main/components/PostList.jsx b/src/pages/Forum/posts-main/components/PostList.jsx
index 707ff15..9e088ea 100644
--- a/src/pages/Forum/posts-main/components/PostList.jsx
+++ b/src/pages/Forum/posts-main/components/PostList.jsx
@@ -1,12 +1,10 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Link } from 'wouter';
-import { GoodTwo, Comment, Star, Delete } from '@icon-park/react';
-import { likePost, unlikePost } from '../../posts-detail/api';
+import { GoodTwo, Star, Delete } from '@icon-park/react';
+import { likePost } from '../../posts-detail/api';
import './PostList.css';
-const API_BASE = process.env.REACT_APP_API_BASE;
-
const PostList = ({ search }) => {
const [posts, setPosts] = useState([]);
const [page, setPage] = useState(1);
@@ -22,7 +20,7 @@
setLoading(true);
setErrorMsg('');
try {
- const res = await axios.get(`${API_BASE}/echo/forum/posts/getAllPosts`, {
+ const res = await axios.get(`/echo/forum/posts/getAllPosts`, {
params: {
page,
pageSize: size,
@@ -31,13 +29,18 @@
}
});
+ // 检查响应结构是否符合预期
+ if (!res.data || !Array.isArray(res.data.posts)) {
+ throw new Error('API返回格式不正确');
+ }
+
const postsData = res.data.posts || [];
const userIds = [...new Set(postsData.map(post => post.user_id))];
const profiles = await Promise.all(userIds.map(async id => {
try {
- const r = await axios.get(`${API_BASE}/echo/user/profile`, {
+ const r = await axios.get(`/echo/user/profile`, {
params: { user_id: id }
});
return { id, profile: r.data };
@@ -74,61 +77,73 @@
fetchPosts();
}, [page, search]);
- const toggleLike = async (postId, liked, userId) => {
- try {
- if (liked) {
- await unlikePost(postId);
- } else {
- await likePost(postId, userId);
- }
-
- setPosts(posts =>
- posts.map(post =>
- post.postNo === postId
- ? { ...post, liked: !liked, likeCount: liked ? post.likeCount - 1 : post.likeCount + 1 }
- : post
- )
- );
- } catch (err) {
- console.error('点赞失败:', err);
+ const toggleLike = async (postNo, liked, userId) => {
+ try {
+ if (liked) {
+ // 修改为 POST 请求,并带上 user_id 参数
+ await axios.post(`/echo/forum/posts/${postNo}/unlike`, {
+ user_id: userId
+ });
+ } else {
+ await likePost(postNo, userId); // 你已有的点赞逻辑
}
- };
+
+ setPosts(posts =>
+ posts.map(post =>
+ post.postNo === postNo
+ ? { ...post, liked: !liked, likeCount: liked ? post.likeCount - 1 : post.likeCount + 1 }
+ : post
+ )
+ );
+ } catch (err) {
+ console.error('点赞失败:', err);
+ alert('点赞操作失败,请稍后重试');
+ }
+};
+
// 收藏帖子
- const toggleCollect = async (postId, collected, userId) => {
- try {
- if (collected) {
- // 取消收藏
- await axios.get(`${API_BASE}/echo/forum/posts/${postId}/uncollect`, {
- data: { user_id: userId }
- });
- } else {
- // 收藏帖子
- await axios.post(`${API_BASE}/echo/forum/posts/${postId}/collect`, {
- user_id: userId
- });
- }
-
- setPosts(posts =>
- posts.map(post =>
- post.postNo === postId
- ? { ...post, collected: !collected, collectCount: collected ? post.collectCount - 1 : post.collectCount + 1 }
- : post
- )
- );
- } catch (err) {
- console.error('收藏操作失败:', err);
+ const toggleCollect = async (postNo, collected, userId) => {
+ try {
+ if (collected) {
+ // 取消收藏:DELETE 请求 + JSON 请求体
+ await axios.delete(`/echo/forum/posts/${postNo}/uncollect`, {
+ data: { user_id: userId } // 注意:DELETE 请求的请求体需放在 data 字段中
+ });
+ } else {
+ // 收藏:POST 请求 + JSON 请求体
+ await axios.post(`/echo/forum/posts/${postNo}/collect`, {
+ user_id: userId
+ });
}
- };
+
+ setPosts(posts =>
+ posts.map(post =>
+ post.postNo === postNo
+ ? {
+ ...post,
+ collected: !collected,
+ collectCount: collected ? post.collectCount - 1 : post.collectCount + 1
+ }
+ : post
+ )
+ );
+ } catch (err) {
+ console.error('收藏操作失败:', err.response?.data || err.message);
+ alert('收藏操作失败,请稍后重试');
+ }
+};
+
+
// 删除帖子
- const handleDeletePost = async (postId) => {
+ const handleDeletePost = async (postNo) => {
if (window.confirm('确定要删除这篇帖子吗?')) {
try {
- await axios.delete(`${API_BASE}/echo/forum/posts/${postId}/deletePost`);
+ await axios.delete(`/echo/forum/posts/${postNo}/deletePost`);
// 从列表中移除已删除的帖子
- setPosts(posts => posts.filter(post => post.postNo !== postId));
+ setPosts(posts => posts.filter(post => post.postNo !== postNo));
// 如果删除后当前页没有帖子了,尝试加载上一页
if (posts.length === 1 && page > 1) {
@@ -172,11 +187,6 @@
<Star theme="outline" size="24" fill={post.collected ? '#ffd700' : '#fff'} />
<span>{post.collectCount}</span>
</button>
-
- <div className="icon-btn">
- <Comment theme="outline" size="24" fill="#fff" />
- <span>{post.commentCount}</span>
- </div>
<button className="icon-btn" onClick={() => handleDeletePost(post.postNo)}>
<Delete theme="outline" size="24" fill="#333" />
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
index 9af56bc..fd79cec 100644
--- a/src/pages/Forum/promotion-part/Promotion.jsx
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -1,18 +1,159 @@
+// import React, { useEffect, useState, useRef } from 'react';
+// import './Promotion.css';
+
+
+
+// const Promotion = () => {
+// const [promotions, setPromotions] = useState([]);
+// const [coldResources, setColdResources] = useState([]);
+// const [loading, setLoading] = useState(true);
+
+// // 轮播索引
+// const [promoIndex, setPromoIndex] = useState(0);
+// const [coldIndex, setColdIndex] = useState(0);
+
+// // 计时器引用,用于清理
+// const promoTimerRef = useRef(null);
+// const coldTimerRef = useRef(null);
+
+// useEffect(() => {
+// fetchData();
+// }, []);
+
+// // 自动轮播:促销活动
+// useEffect(() => {
+// if (promotions.length === 0) return;
+// // 清理旧的计时器
+// clearInterval(promoTimerRef.current);
+// promoTimerRef.current = setInterval(() => {
+// setPromoIndex(prev => (prev + 1) % promotions.length);
+// }, 5000);
+// return () => clearInterval(promoTimerRef.current);
+// }, [promotions]);
+
+// // 自动轮播:冷门资源
+// useEffect(() => {
+// if (coldResources.length === 0) return;
+// clearInterval(coldTimerRef.current);
+// coldTimerRef.current = setInterval(() => {
+// setColdIndex(prev => (prev + 1) % coldResources.length);
+// }, 5000);
+// return () => clearInterval(coldTimerRef.current);
+// }, [coldResources]);
+
+// const fetchData = async () => {
+// try {
+// const promoResponse = await fetch(`/echo/promotions/active`);
+// const promoData = await promoResponse.json();
+// setPromotions(promoData);
+
+// const coldResponse = await fetch(`/echo/resources/cold`);
+// const coldData = await coldResponse.json();
+// setColdResources(coldData);
+// } catch (error) {
+// console.error('获取数据失败:', error);
+// } finally {
+// setLoading(false);
+// }
+// };
+
+// if (loading) {
+// return <div className="promotion-container">加载中...</div>;
+// }
+
+// // 手动切换
+// const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
+// const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+// const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
+// const nextCold = () => setColdIndex((coldIndex + 1) % coldResources.length);
+
+// const currentPromo = promotions[promoIndex];
+// const currentCold = coldResources[coldIndex];
+
+// return (
+// <div className="promotion-container carousel-container">
+// {/* 促销活动轮播 */}
+// <section className="carousel-section">
+// <h2>当前促销活动</h2>
+// {promotions.length === 0 ? (
+// <div className="empty-state">暂无促销活动</div>
+// ) : (
+// <div
+// className="carousel"
+// onMouseEnter={() => clearInterval(promoTimerRef.current)}
+// onMouseLeave={() => {
+// promoTimerRef.current = setInterval(() => {
+// setPromoIndex(prev => (prev + 1) % promotions.length);
+// }, 3000);
+// }}
+// >
+// <button className="arrow left" onClick={prevPromo}><</button>
+// <div className="slide">
+// <div><strong>种子类型:</strong>{currentPromo.category}</div>
+// <div><strong>促销时间:</strong>
+// {new Date(currentPromo.promotion_start_time).toLocaleString()} ~{' '}
+// {new Date(currentPromo.promotion_end_time).toLocaleString()}
+// </div>
+// <div><strong>下载折扣:</strong>{currentPromo.download_discount ?? '无'}</div>
+// <div><strong>上传奖励:</strong>{currentPromo.upload_reward ?? '无'}</div>
+// {currentPromo.description && (
+// <div><strong>详细描述:</strong>{currentPromo.description}</div>
+// )}
+// </div>
+// <button className="arrow right" onClick={nextPromo}>></button>
+// </div>
+// )}
+// </section>
+
+// {/* 冷门资源轮播 */}
+// <section className="carousel-section">
+// <h2>冷门资源推荐</h2>
+// {coldResources.length === 0 ? (
+// <div className="empty-state">暂无冷门资源推荐</div>
+// ) : (
+// <div
+// className="carousel"
+// onMouseEnter={() => clearInterval(coldTimerRef.current)}
+// onMouseLeave={() => {
+// coldTimerRef.current = setInterval(() => {
+// setColdIndex(prev => (prev + 1) % coldResources.length);
+// }, 3000);
+// }}
+// >
+// <button className="arrow left" onClick={prevCold}><</button>
+// <div className="slide cold-slide">
+// <img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
+// <div className="resource-info">
+// <div><strong>标题:</strong>{currentCold.title}</div>
+// <div><strong>下载量:</strong>{currentCold.download_count} | <strong>种子数:</strong>{currentCold.seed_count}</div>
+// <div><strong>激励:</strong>
+// {currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
+// {currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
+// {!(currentCold.incentives?.download_exempt || currentCold.incentives?.extra_seed_bonus) && '无'}
+// </div>
+// </div>
+// </div>
+// <button className="arrow right" onClick={nextCold}>></button>
+// </div>
+// )}
+// </section>
+// </div>
+// );
+// };
+
+// export default Promotion;
+
import React, { useEffect, useState, useRef } from 'react';
import './Promotion.css';
-const API_BASE = process.env.REACT_APP_API_BASE;
-
const Promotion = () => {
const [promotions, setPromotions] = useState([]);
const [coldResources, setColdResources] = useState([]);
const [loading, setLoading] = useState(true);
- // 轮播索引
const [promoIndex, setPromoIndex] = useState(0);
const [coldIndex, setColdIndex] = useState(0);
- // 计时器引用,用于清理
const promoTimerRef = useRef(null);
const coldTimerRef = useRef(null);
@@ -20,10 +161,8 @@
fetchData();
}, []);
- // 自动轮播:促销活动
useEffect(() => {
if (promotions.length === 0) return;
- // 清理旧的计时器
clearInterval(promoTimerRef.current);
promoTimerRef.current = setInterval(() => {
setPromoIndex(prev => (prev + 1) % promotions.length);
@@ -31,7 +170,6 @@
return () => clearInterval(promoTimerRef.current);
}, [promotions]);
- // 自动轮播:冷门资源
useEffect(() => {
if (coldResources.length === 0) return;
clearInterval(coldTimerRef.current);
@@ -43,11 +181,11 @@
const fetchData = async () => {
try {
- const promoResponse = await fetch(`${API_BASE}/echo/promotions/active`);
+ const promoResponse = await fetch(`/echo/promotions/active`);
const promoData = await promoResponse.json();
setPromotions(promoData);
- const coldResponse = await fetch(`${API_BASE}/echo/resources/cold`);
+ const coldResponse = await fetch(`/echo/resources/cold`);
const coldData = await coldResponse.json();
setColdResources(coldData);
} catch (error) {
@@ -61,7 +199,6 @@
return <div className="promotion-container">加载中...</div>;
}
- // 手动切换
const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
@@ -75,7 +212,7 @@
{/* 促销活动轮播 */}
<section className="carousel-section">
<h2>当前促销活动</h2>
- {promotions.length === 0 ? (
+ {promotions.length === 0 || !currentPromo ? (
<div className="empty-state">暂无促销活动</div>
) : (
<div
@@ -89,14 +226,15 @@
>
<button className="arrow left" onClick={prevPromo}><</button>
<div className="slide">
- <div><strong>种子类型:</strong>{currentPromo.category}</div>
+ <div><strong>种子类型:</strong>{currentPromo?.category ?? '未知'}</div>
<div><strong>促销时间:</strong>
- {new Date(currentPromo.promotion_start_time).toLocaleString()} ~{' '}
- {new Date(currentPromo.promotion_end_time).toLocaleString()}
+ {currentPromo?.promotion_start_time && currentPromo?.promotion_end_time
+ ? `${new Date(currentPromo.promotion_start_time).toLocaleString()} ~ ${new Date(currentPromo.promotion_end_time).toLocaleString()}`
+ : '未知'}
</div>
- <div><strong>下载折扣:</strong>{currentPromo.download_discount ?? '无'}</div>
- <div><strong>上传奖励:</strong>{currentPromo.upload_reward ?? '无'}</div>
- {currentPromo.description && (
+ <div><strong>下载折扣:</strong>{currentPromo?.download_discount ?? '无'}</div>
+ <div><strong>上传奖励:</strong>{currentPromo?.upload_reward ?? '无'}</div>
+ {currentPromo?.description && (
<div><strong>详细描述:</strong>{currentPromo.description}</div>
)}
</div>
@@ -108,7 +246,7 @@
{/* 冷门资源轮播 */}
<section className="carousel-section">
<h2>冷门资源推荐</h2>
- {coldResources.length === 0 ? (
+ {coldResources.length === 0 || !currentCold ? (
<div className="empty-state">暂无冷门资源推荐</div>
) : (
<div
@@ -125,11 +263,11 @@
<img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
<div className="resource-info">
<div><strong>标题:</strong>{currentCold.title}</div>
- <div><strong>下载量:</strong>{currentCold.download_count} | <strong>种子数:</strong>{currentCold.seed_count}</div>
+ <div><strong>下载量:</strong>{currentCold.download_count ?? 0} | <strong>种子数:</strong>{currentCold.seed_count ?? 0}</div>
<div><strong>激励:</strong>
{currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
{currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
- {!(currentCold.incentives?.download_exempt || currentCold.incentives?.extra_seed_bonus) && '无'}
+ {!currentCold.incentives?.download_exempt && !currentCold.incentives?.extra_seed_bonus && '无'}
</div>
</div>
</div>
@@ -142,3 +280,4 @@
};
export default Promotion;
+
diff --git a/src/pages/FriendMoments/CreateMoment.jsx b/src/pages/FriendMoments/CreateMoment.jsx
index 4aca455..1b98a44 100644
--- a/src/pages/FriendMoments/CreateMoment.jsx
+++ b/src/pages/FriendMoments/CreateMoment.jsx
@@ -4,7 +4,7 @@
// // import './CreateMoment.css';
// // import Header from '../../components/Header';
-// // const API_BASE = process.env.REACT_APP_API_BASE;
+// //
// // const USER_ID = 456;
// // const CreateMoment = () => {
@@ -34,7 +34,7 @@
// // const uploadImageToServer = async (file) => {
// // const formData = new FormData();
// // formData.append('file', file);
-// // const res = await axios.post(`${API_BASE}/upload`, formData, {
+// // const res = await axios.post(`/upload`, formData, {
// // headers: {'Content-Type': 'multipart/form-data'}
// // });
// // return res.data.url;
@@ -47,7 +47,7 @@
// // }
// // try {
// // await axios.post(
-// // `${API_BASE}/echo/users/${USER_ID}/CreateMoment`,
+// // `/echo/users/${USER_ID}/CreateMoment`,
// // {
// // title: title.trim() || undefined,
// // friend_content: content.trim(),
@@ -107,7 +107,7 @@
// import './CreateMoment.css';
// import Header from '../../components/Header';
-// const API_BASE = process.env.REACT_APP_API_BASE;
+//
// const USER_ID = 456;
// const CreateMoment = ({ onClose, fetchFeeds }) => {
@@ -135,7 +135,7 @@
// const uploadImageToServer = async (file) => {
// const formData = new FormData();
// formData.append('file', file);
-// const res = await axios.post(`${API_BASE}/upload`, formData, {
+// const res = await axios.post(`/upload`, formData, {
// headers: { 'Content-Type': 'multipart/form-data' }
// });
// return res.data.url;
@@ -148,7 +148,7 @@
// }
// try {
// await axios.post(
-// `${API_BASE}/echo/users/${USER_ID}/CreateMoment`,
+// `/echo/users/${USER_ID}/CreateMoment`,
// {
// title: title.trim() || undefined,
// friend_content: content.trim(),
diff --git a/src/pages/FriendMoments/FriendMoments.jsx b/src/pages/FriendMoments/FriendMoments.jsx
index fdf101c..6d9def6 100644
--- a/src/pages/FriendMoments/FriendMoments.jsx
+++ b/src/pages/FriendMoments/FriendMoments.jsx
@@ -4,7 +4,7 @@
import Header from '../../components/Header';
import { Edit } from '@icon-park/react';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const USER_ID = 456;
const FriendMoments = () => {
@@ -22,7 +22,7 @@
// 拉取好友动态列表
const fetchFeeds = async () => {
try {
- const res = await axios.get(`${API_BASE}/echo/users/${USER_ID}/feeds`);
+ const res = await axios.get(`/echo/users/${USER_ID}/feeds`);
setFeeds(res.data.feeds);
setFilteredFeeds(res.data.feeds);
} catch (err) {
@@ -63,7 +63,7 @@
const uploadImageToServer = async (file) => {
const fd = new FormData();
fd.append('file', file);
- const res = await axios.post(`${API_BASE}/upload`, fd, {
+ const res = await axios.post(`/upload`, fd, {
headers: {'Content-Type':'multipart/form-data'}
});
return res.data.url;
@@ -77,7 +77,7 @@
}
try {
await axios.post(
- `${API_BASE}/echo/users/${USER_ID}/createFeed`,
+ `/echo/users/${USER_ID}/createFeed`,
{ title: title.trim() || undefined, friend_content: content.trim(), images }
);
// 重置表单
@@ -97,7 +97,7 @@
const handleDelete = async (feedId) => {
if (!window.confirm('确定要删除这条动态吗?')) return;
try {
- await axios.delete(`${API_BASE}/echo/users/me/feed/${feedId}`);
+ await axios.delete(`/echo/users/me/feed/${feedId}`);
fetchFeeds();
} catch (err) {
console.error('删除失败', err);
diff --git a/src/pages/InterestGroup/InterestGroup.jsx b/src/pages/InterestGroup/InterestGroup.jsx
index eb9ed41..a68f20c 100644
--- a/src/pages/InterestGroup/InterestGroup.jsx
+++ b/src/pages/InterestGroup/InterestGroup.jsx
@@ -3,7 +3,7 @@
import './InterestGroup.css';
import Header from '../../components/Header'; // 导入 Header 组件
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const InterestGroup = () => {
const [groups, setGroups] = useState([]);
@@ -23,7 +23,7 @@
try {
setLoading(true);
setError(null);
- const response = await axios.get(`${API_BASE}/echo/groups`, {
+ const response = await axios.get(`/echo/groups`, {
params: {
category,
name,
@@ -74,7 +74,7 @@
const userId = 1; // 假设用户ID为1,可以根据实际情况获取
try {
- const response = await axios.post(`${API_BASE}/echo/groups/${groupId}/join`, {
+ const response = await axios.post(`/echo/groups/${groupId}/join`, {
user_id: userId
});
diff --git a/src/pages/LevelPage/LevelPage.jsx b/src/pages/LevelPage/LevelPage.jsx
new file mode 100644
index 0000000..55ce6b6
--- /dev/null
+++ b/src/pages/LevelPage/LevelPage.jsx
@@ -0,0 +1,100 @@
+import React, { useEffect, useState } from 'react';
+import { getExperience, updateExperience, checkUpgrade, upgradeUserLevel } from '../../api/level';
+import LevelCard from '../../components/LevelCard';
+
+const user_id = 1; // 实际项目中请从用户上下文获取
+
+const LevelPage = () => {
+ const [levelInfo, setLevelInfo] = useState(null);
+ const [upgradeStatus, setUpgradeStatus] = useState(null);
+ const [message, setMessage] = useState('');
+
+ const fetchExperience = async () => {
+ try {
+ const res = await getExperience(user_id);
+ setLevelInfo(res.data);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const handleUpdate = async () => {
+ try {
+ const res = await updateExperience({ user_id, experience: 50, source: '签到' });
+ setMessage(res.data.message || '经验更新成功');
+ fetchExperience();
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const handleCheckUpgrade = async () => {
+ try {
+ const res = await checkUpgrade(user_id);
+ setUpgradeStatus(res.data);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const handleUpgrade = async () => {
+ try {
+ const res = await upgradeUserLevel({ user_id, can_upgrade: true });
+ setMessage(res.data.message || '升级成功');
+ fetchExperience();
+ } catch (error) {
+ setMessage(error.response?.data?.message || '升级失败');
+ }
+ };
+
+ useEffect(() => {
+ fetchExperience();
+ }, []);
+
+ return (
+ <div className="max-w-xl mx-auto p-4">
+ <h1 className="text-2xl font-bold mb-4">我的等级</h1>
+ {levelInfo && (
+ <LevelCard
+ level={levelInfo.level}
+ current={levelInfo.current_experience}
+ next={levelInfo.next_level_experience}
+ />
+ )}
+
+ <div className="space-x-2 mb-4">
+ <button
+ onClick={handleUpdate}
+ className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
+ >
+ 模拟签到加经验
+ </button>
+ <button
+ onClick={handleCheckUpgrade}
+ className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"
+ >
+ 检查是否可升级
+ </button>
+ </div>
+
+ {upgradeStatus && (
+ <div className="mb-4">
+ {upgradeStatus.can_upgrade ? (
+ <button
+ onClick={handleUpgrade}
+ className="px-4 py-2 bg-purple-600 text-white rounded hover:bg-purple-700"
+ >
+ 升级到下一级
+ </button>
+ ) : (
+ <p className="text-gray-700">当前还不能升级。</p>
+ )}
+ </div>
+ )}
+
+ {message && <div className="text-sm text-green-700 mt-2">{message}</div>}
+ </div>
+ );
+};
+
+export default LevelPage;
diff --git a/src/pages/MessagePage/MessagePage.jsx b/src/pages/MessagePage/MessagePage.jsx
index ae95dca..3428912 100644
--- a/src/pages/MessagePage/MessagePage.jsx
+++ b/src/pages/MessagePage/MessagePage.jsx
@@ -2,7 +2,7 @@
import './MessagePage.css';
import { useUser } from '../../context/UserContext';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const mockMessages = [
{
@@ -39,7 +39,7 @@
await new Promise((r) => setTimeout(r, 500)); // 模拟延迟
setMessages(mockMessages);
} else {
- const response = await fetch(`${API_BASE}/echo/messages/${currentUserId}`);
+ const response = await fetch(`/echo/messages/${currentUserId}`);
const data = await response.json();
if (data.status === 'success') {
setMessages(data.messages);
diff --git a/src/pages/PromotionsPage/PromotionsPage.jsx b/src/pages/PromotionsPage/PromotionsPage.jsx
index bb50d72..834ad73 100644
--- a/src/pages/PromotionsPage/PromotionsPage.jsx
+++ b/src/pages/PromotionsPage/PromotionsPage.jsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import './PromotionsPage.css';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
function PromotionsPage() {
const [promotions, setPromotions] = useState([]);
const [currentPromotion, setCurrentPromotion] = useState(null);
@@ -33,7 +33,7 @@
const fetchPromotions = async (page = 1) => {
setIsLoading(true);
try {
- const response = await fetch(`${API_BASE}/promotions/list?page=${page}&per_page=${perPage}`, {
+ const response = await fetch(`/promotions/list?page=${page}&per_page=${perPage}`, {
headers: getAuthHeaders()
});
@@ -59,7 +59,7 @@
const fetchPromotionDetails = async (promoId) => {
setIsLoading(true);
try {
- const response = await fetch(`${API_BASE}/promotions/${promoId}`, {
+ const response = await fetch(`/promotions/${promoId}`, {
headers: getAuthHeaders()
});
@@ -94,7 +94,7 @@
setIsLoading(true);
try {
- const response = await fetch('${API_BASE}/promotions/add', {
+ const response = await fetch('/promotions/add', {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify(formData)
@@ -136,7 +136,7 @@
setIsLoading(true);
try {
- const response = await fetch(`${API_BASE}/promotions/delete/${promoId}`, {
+ const response = await fetch(`/promotions/delete/${promoId}`, {
method: 'DELETE',
headers: getAuthHeaders()
});
diff --git a/src/pages/PublishSeed/PublishSeed.jsx b/src/pages/PublishSeed/PublishSeed.jsx
index c57242a..09dcc67 100644
--- a/src/pages/PublishSeed/PublishSeed.jsx
+++ b/src/pages/PublishSeed/PublishSeed.jsx
@@ -3,7 +3,7 @@
import Header from '../../components/Header'; // 导入 Header 组件
import './PublishSeed.css'
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const PublishSeed = () => {
const [title, setTitle] = useState('');
@@ -37,7 +37,7 @@
formData.append('image_url', imageUrl);
try {
- const response = await axios.post(`${API_BASE}/echo/seeds/upload`, formData, {
+ const response = await axios.post(`/echo/seeds/upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
diff --git a/src/pages/SeedList/Recommend/Recommend.jsx b/src/pages/SeedList/Recommend/Recommend.jsx
index 97e702b..dd38184 100644
--- a/src/pages/SeedList/Recommend/Recommend.jsx
+++ b/src/pages/SeedList/Recommend/Recommend.jsx
@@ -9,8 +9,8 @@
useEffect(() => {
const fetchPaidLists = async () => {
try {
- const API_BASE = process.env.REACT_APP_API_BASE;
- const response = await fetch(`${API_BASE}/echo/recommendations/paid?user_id=1`);
+
+ const response = await fetch(`/echo/recommendations/paid?user_id=1`);
console.log('请求地址:', `${process.env.REACT_APP_API_BASE}/echo/recommendations/paid?user_id=1`);
diff --git a/src/pages/SeedList/SeedDetail/SeedDetail.jsx b/src/pages/SeedList/SeedDetail/SeedDetail.jsx
index b99fcc5..0a3ba4d 100644
--- a/src/pages/SeedList/SeedDetail/SeedDetail.jsx
+++ b/src/pages/SeedList/SeedDetail/SeedDetail.jsx
@@ -4,7 +4,7 @@
import Header from '../../../components/Header';
import './SeedDetail.css';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const SeedDetail = () => {
const { seed_id } = useParams();
@@ -15,7 +15,7 @@
useEffect(() => {
axios
- .get(`${API_BASE}/echo/seeds/${seed_id}`)
+ .get(`/echo/seeds/${seed_id}`)
.then((res) => {
if (res.data.status === 'success') {
setSeed(res.data.seed);
@@ -28,7 +28,7 @@
});
// 模拟获取评论数据,实际需要调用 API
- axios.get(`${API_BASE}/echo/seeds/${seed_id}/comments`)
+ axios.get(`/echo/seeds/${seed_id}/comments`)
.then((res) => {
if (res.data.status === 'success') {
setComments(res.data.comments);
@@ -49,7 +49,7 @@
const left = 0;
try {
- const response = await axios.get(`${API_BASE}/echo/seeds/${seed.seed_id}/download`, {
+ const response = await axios.get(`/echo/seeds/${seed.seed_id}/download`, {
params: { peer_id, ip, port, uploaded, downloaded, left },
responseType: 'blob'
});
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index 09ea5dd..eade11d 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -6,7 +6,7 @@
import Header from '../../components/Header'; // 引入 Header 组件
import './SeedList.css';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const SeedList = () => {
const [seeds, setSeeds] = useState([]);
@@ -66,7 +66,7 @@
try {
const params = buildQueryParams();
const queryString = new URLSearchParams(params).toString();
- const response = await fetch(`${API_BASE}/echo/seeds?${queryString}`);
+ const response = await fetch(`/echo/seeds?${queryString}`);
const data = await response.json();
if (data.status!== 'success') throw new Error(data.message || '获取失败');
@@ -87,7 +87,7 @@
if (activeTab === '猜你喜欢') return;
const category = CATEGORY_MAP[activeTab];
try {
- const res = await axios.get(`${API_BASE}/echo/seed-filters?category=${category}`);
+ const res = await axios.get(`/echo/seed-filters?category=${category}`);
const filterData = res.data || {};
setFilters(filterData);
@@ -124,7 +124,7 @@
const left = 0;
try {
- const response = await axios.get(`${API_BASE}/echo/seeds/${seedId}/download`, {
+ const response = await axios.get(`/echo/seeds/${seedId}/download`, {
params: { peer_id, ip, port, uploaded, downloaded, left },
responseType: 'blob'
});
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index 78701f1..5598308 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -5,7 +5,7 @@
import Header from '../../components/Header';
-const API_BASE = process.env.REACT_APP_API_BASE;
+
const UserProfile = () => {
const [userProfile, setUserProfile] = useState(null);
@@ -20,7 +20,7 @@
const fetchUserProfile = async () => {
try {
setLoading(true);
- const response = await axios.get(`${API_BASE}/echo/user/profile`, {
+ const response = await axios.get(`/echo/user/profile`, {
params: { user_id: userId }
});