修复查看帖子、评论、回复评论、点赞、收藏,添加用户等级

Change-Id: Ida9590d7ccee08dcd787a36c7e5cb39a3e26cd0d
diff --git a/.env.development b/.env.development
index 32e9bf1..a4fa618 100644
--- a/.env.development
+++ b/.env.development
@@ -1,2 +1,3 @@
 # REACT_APP_API_BASE=http://127.0.0.1:4523/m1/6139971-5831803-default
-REACT_APP_API_BASE=http://localhost:8080
+# REACT_APP_API_BASE=http://localhost:8080
+# REACT_APP_API_BASE=''  
diff --git a/package.json b/package.json
index 32849de..ba6ad52 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
   "name": "echo-frontend",
   "version": "0.1.0",
   "private": true,
+  "proxy": "http://localhost:8080",
   "dependencies": {
     "@icon-park/react": "^1.4.2",
     "@testing-library/dom": "^10.4.0",
diff --git a/src/App.js b/src/App.js
index 27775b1..1892de8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -14,6 +14,7 @@
 import MessagePage from './pages/MessagePage/MessagePage';
 import CreateMoment from './pages/FriendMoments/CreateMoment';
 import PromotionsPage from './pages/PromotionsPage/PromotionsPage';
+import LevelPage from './pages/LevelPage/LevelPage';
 
 function App() {
   return (
@@ -33,6 +34,7 @@
         <Route path="/user/profile" component={UserProfile}/>
         <Route path="/messages" component={MessagePage}/>
         <Route path="/promotions" component={PromotionsPage}/>
+        <Route path="/level" component={LevelPage}/>
       </>
     </UserProvider>
   );
diff --git a/src/__tests__/CreatePost.test.js b/src/__tests__/CreatePost.test.js
index 2695c5e..8307b1e 100644
--- a/src/__tests__/CreatePost.test.js
+++ b/src/__tests__/CreatePost.test.js
@@ -52,7 +52,7 @@
 
     await waitFor(() =>
       expect(axios.post).toHaveBeenCalledWith(
-        `http://localhost:8080/echo/forum/posts/${mockUserId}/createPost`,
+        `/echo/forum/posts/${mockUserId}/createPost`,
         {
           title: '测试标题',
           post_content: '测试内容',
diff --git a/src/api/level.js b/src/api/level.js
new file mode 100644
index 0000000..c091f81
--- /dev/null
+++ b/src/api/level.js
@@ -0,0 +1,15 @@
+import axios from 'axios';
+
+const BASE_URL = '/echo/level';
+
+export const getExperience = (user_id) =>
+  axios.get(`${BASE_URL}/getExperience`, { params: { user_id } });
+
+export const updateExperience = (data) =>
+  axios.post(`${BASE_URL}/updateExperience`, data);
+
+export const checkUpgrade = (user_id) =>
+  axios.get(`${BASE_URL}/upgrade-check`, { params: { user_id } });
+
+export const upgradeUserLevel = (data) =>
+  axios.post(`${BASE_URL}/upgrades`, data);
diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx
index d635223..19d161d 100644
--- a/src/components/Auth/Login.jsx
+++ b/src/components/Auth/Login.jsx
@@ -99,7 +99,7 @@
 // import '../../pages/AuthPage/AuthPage.css';
 // import image from './logo.svg';
 
-// const API_BASE = process.env.REACT_APP_API_BASE;
+// 
 
 // const Login = ({ onRegisterClick }) => {
 //   const [formData, setFormData] = useState({
@@ -115,7 +115,7 @@
 //     setError('');
 
 //     try {
-//       const response = await fetch(`${API_BASE}/user/login`, {
+//       const response = await fetch(`/user/login`, {
 //         method: 'POST',
 //         headers: {
 //           'Content-Type': 'application/json',
diff --git a/src/components/Auth/Register.jsx b/src/components/Auth/Register.jsx
index 16b2313..d98a449 100644
--- a/src/components/Auth/Register.jsx
+++ b/src/components/Auth/Register.jsx
@@ -143,7 +143,7 @@
 //     setError('');
 
 //     try {
-//       const response = await fetch(`${API_BASE}/user/register`, {
+//       const response = await fetch(`/user/register`, {
 //         method: 'POST',
 //         headers: {
 //           'Content-Type': 'application/json',
diff --git a/src/components/LevelCard.jsx b/src/components/LevelCard.jsx
new file mode 100644
index 0000000..3b16c38
--- /dev/null
+++ b/src/components/LevelCard.jsx
@@ -0,0 +1,14 @@
+import React from 'react';
+
+const LevelCard = ({ level, current, next }) => {
+  return (
+    <div className="border p-4 rounded shadow mb-4 bg-white">
+      <h2 className="text-xl font-bold mb-2">当前等级:Lv.{level}</h2>
+      <p>当前经验:{current}</p>
+      <p>升级所需经验:{next}</p>
+      <progress value={current} max={next} className="w-full mt-2" />
+    </div>
+  );
+};
+
+export default LevelCard;
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}>&lt;</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}>&gt;</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}>&lt;</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}>&gt;</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}>&lt;</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 }
         });