修改等级、新手指南
Change-Id: If60f5e1164f16fdabc8a5648a78c40fdd93c83a8
diff --git a/src/pages/AuthPage/AuthPage.css b/src/pages/AuthPage/AuthPage.css
index 6e8b965..00fb5d7 100644
--- a/src/pages/AuthPage/AuthPage.css
+++ b/src/pages/AuthPage/AuthPage.css
@@ -1,245 +1,210 @@
- .auth-container {
+.auth-container {
display: flex;
align-items: center;
- /* justify-content: flex-end; */
- justify-content: space-between;
+ justify-content: space-between;
min-height: 100vh;
- font-family: Arial, sans-serif;
- background: #5F4437;
- /* background: linear-gradient(180deg, #5F4437, #823c3c) */
- padding: 0 2rem; /* 添加左右内边距 */
+ font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
+ /* background: linear-gradient(135deg, #be8e9d 0%, #c48d74 100%); */
+ background: url('a.jpg') no-repeat center center fixed;
+ background-size: cover;
+ padding: 0 5%;
+ position: relative;
+ overflow: hidden;
}
- .logo-container {
- flex: 1;
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 2rem;
- }
+.auth-container::before {
+ content: '';
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
+ transform: rotate(30deg);
+}
- .site-logo {
- width: 300px; /* 设置固定宽度 */
- height: auto; /* 高度自适应,保持比例 */
+.logo-container {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 2rem;
+ z-index: 1;
+}
+
+.site-logo {
+ width: 350px;
+ margin-left: -15%;
+ height: auto;
+ filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.3));
+}
+
+.glass-card {
+ width: 35%;
+ margin-right: 10%;
+ background: rgba(255, 255, 255, 0.15);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border-radius: 16px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ overflow: hidden;
+ z-index: 2;
+ transition: all 0.3s ease;
+}
+
+.glass-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
+}
+
+.auth-header {
+ display: flex;
+ background: rgba(255, 255, 255, 0.1);
+}
+
+.auth-tab {
+ flex: 1;
+ padding: 20px;
+ text-align: center;
+ font-size: 18px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ color: rgba(255, 255, 255, 0.7);
+ position: relative;
+}
+
+.auth-tab.active {
+ color: white;
+ font-weight: 600;
+}
+
+.auth-tab.active::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 60%;
+ height: 3px;
+ background: white;
+ border-radius: 3px;
+}
+
+.auth-tab:not(.active):hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.auth-content {
+ padding: 30px;
+}
+
+.auth-form {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.form-group {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.form-group label {
+ font-weight: 500;
+ color: rgba(255, 255, 255, 0.9);
+ text-align: left;
+ align-self: flex-start;
+}
+
+.form-group input[type="text"],
+.form-group input[type="email"],
+.form-group input[type="password"] {
+ padding: 14px 16px;
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 8px;
+ font-size: 16px;
+ color: white;
+ transition: all 0.3s ease;
+}
+
+.form-group input::placeholder {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.form-group input:focus {
+ background: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.4);
+ outline: none;
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
+}
+
+.auth-button {
+ background: rgba(255, 255, 255, 0.2);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ padding: 16px;
+ font-size: 16px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ margin-top: 10px;
+ backdrop-filter: blur(5px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.auth-button:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: translateY(-2px);
+}
+
+.forgot-password {
+ color: rgba(255, 255, 255, 0.7);
+ text-decoration: none;
+ font-size: 14px;
+ transition: all 0.3s ease;
+}
+
+.forgot-password:hover {
+ color: white;
+ text-decoration: underline;
+}
+
+.error-message {
+ color: #ff6b6b;
+ font-size: 14px;
+ margin-top: 4px;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+}
+
+/* 响应式调整 */
+@media (max-width: 768px) {
+ .auth-container {
+ flex-direction: column;
+ padding: 40px 20px;
+ justify-content: flex-start;
}
- .auth-card {
- width: 100%;
- max-width: 480px;
- background-color: #E4D8C9;
- border-radius: 8px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
- overflow: hidden;
+ .logo-container {
+ margin-bottom: 40px;
}
- .auth-header {
- display: flex;
- border-bottom: 1px solid #eee;
+ .glass-card {
+ max-width: 100%;
+ }
+}
+
+@media (max-width: 480px) {
+ .auth-content {
+ padding: 20px;
}
.auth-tab {
- flex: 1;
padding: 16px;
- text-align: center;
- font-size: 18px;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.3s ease;
- }
-
- .auth-tab.active {
- color: #BA929A;
- border-bottom: 2px solid #BA929A;
- }
-
- .auth-tab:not(.active) {
- color: #888;
- }
-
- .auth-tab:hover:not(.active) {
- background-color: #f9f9f9;
- }
-
- .auth-content {
- padding: 30px;
- }
-
- .auth-form {
- display: flex;
- flex-direction: column;
- gap: 20px;
- }
-
- .form-group {
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
-
- .form-group label {
- font-weight: 500;
- color: #333;
- text-align: left;
- align-self: flex-start;
- }
-
- .form-group input[type="text"],
- .form-group input[type="email"],
- .form-group input[type="password"] {
- padding: 12px 16px;
- border: 1px solid #ddd;
- border-radius: 4px;
font-size: 16px;
- transition: border-color 0.3s;
}
-
- .form-group input:focus {
- border-color: #BA929A;
- outline: none;
- }
-
- .form-group-inline {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
-
- .checkbox-container {
- display: flex;
- align-items: center;
- gap: 8px;
- }
-
- .checkbox-container input[type="checkbox"] {
- width: 18px;
- height: 18px;
- cursor: pointer;
- }
-
- .forgot-password {
- color: #BA929A;
- text-decoration: none;
- font-size: 14px;
- }
-
- .forgot-password:hover {
- text-decoration: underline;
- }
-
- .auth-button {
- background-color: #BA929A;
- color: white;
- border: none;
- border-radius: 4px;
- padding: 14px;
- font-size: 16px;
- font-weight: 500;
- cursor: pointer;
- transition: background-color 0.3s;
- }
-
- .auth-button:hover {
- background-color: #BA929A;
- }
-
- .social-login {
- margin-top: 20px;
- text-align: center;
- }
-
- .social-login p {
- color: #666;
- margin-bottom: 15px;
- position: relative;
- }
-
- .social-login p::before,
- .social-login p::after {
- content: "";
- position: absolute;
- top: 50%;
- width: 30%;
- height: 1px;
- background-color: #ddd;
- }
-
- .social-login p::before {
- left: 0;
- }
-
- .social-login p::after {
- right: 0;
- }
-
- .social-icons {
- display: flex;
- justify-content: center;
- gap: 20px;
- }
-
- .social-icon {
- width: 40px;
- height: 40px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- font-size: 12px;
- cursor: pointer;
- }
-
- .social-icon.wechat {
- background-color: #07c160;
- }
-
- .social-icon.weibo {
- background-color: #e6162d;
- }
-
- .social-icon.qq {
- background-color: #12b7f5;
- }
-
- .terms-container {
- flex-direction: column;
- align-items: flex-start;
- }
-
- .terms-link {
- color: #BA929A;
- text-decoration: none;
- }
-
- .terms-link:hover {
- text-decoration: underline;
- }
-
- .error-message {
- color: #e53e3e;
- font-size: 14px;
- margin-top: 4px;
- }
-
- /* Responsive adjustments */
- @media (max-width: 576px) {
- .auth-card {
- box-shadow: none;
- border-radius: 0;
- }
-
- .auth-content {
- padding: 20px;
- }
-
- .form-group-inline {
- flex-direction: column;
- align-items: flex-start;
- gap: 10px;
- }
-
- .social-icons {
- flex-wrap: wrap;
- }
- }
-
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/pages/AuthPage/AuthPage.jsx b/src/pages/AuthPage/AuthPage.jsx
index fe1b42b..51f323e 100644
--- a/src/pages/AuthPage/AuthPage.jsx
+++ b/src/pages/AuthPage/AuthPage.jsx
@@ -1,7 +1,9 @@
import { useState } from "react";
import { Link } from "wouter";
import "./AuthPage.css";
-import logo from '../../assets/logo.png';
+// import logo from '../../assets/logo.png';
+import { ReactComponent as LogoIcon } from '../../assets/logo.svg';
+// import backgroundImage from '../../assets/b.png';
function AuthPage() {
const [activeTab, setActiveTab] = useState("login");
@@ -157,10 +159,11 @@
return (
<div className="auth-container">
<div className="logo-container">
- <img src={logo} alt="Logo" className="site-logo" />
+ <LogoIcon className="site-logo" />
</div>
- <div className="auth-card">
+ {/* <div className="auth-card"> */}
+ <div className="glass-card">
<div className="auth-header">
<div className={`auth-tab ${activeTab === "login" ? "active" : ""}`} onClick={() => setActiveTab("login")}>
登录
diff --git a/src/pages/AuthPage/a.jpg b/src/pages/AuthPage/a.jpg
new file mode 100644
index 0000000..5c55df3
--- /dev/null
+++ b/src/pages/AuthPage/a.jpg
Binary files differ
diff --git a/src/pages/AuthPage/b.png b/src/pages/AuthPage/b.png
new file mode 100644
index 0000000..f650e96
--- /dev/null
+++ b/src/pages/AuthPage/b.png
Binary files differ
diff --git a/src/pages/Forum/posts-detail/PostDetailPage.css b/src/pages/Forum/posts-detail/PostDetailPage.css
index 570b65f..9ca9298 100644
--- a/src/pages/Forum/posts-detail/PostDetailPage.css
+++ b/src/pages/Forum/posts-detail/PostDetailPage.css
@@ -115,12 +115,12 @@
.reply-btn {
background: none;
border: none;
- color: #000000;
+ color: #2196F3;
cursor: pointer;
}
.reply-btn:hover {
- color: #000000; /* 不变色 */
+ color: #2196F3; /* 不变色 */
background: none; /* 确保没有背景色 */
text-decoration: underline; /* 你可以保留这个,或也删除 */
}
diff --git a/src/pages/Forum/posts-main/components/PostList.css b/src/pages/Forum/posts-main/components/PostList.css
index f6d6c2a..81b1132 100644
--- a/src/pages/Forum/posts-main/components/PostList.css
+++ b/src/pages/Forum/posts-main/components/PostList.css
@@ -16,6 +16,7 @@
background-color: #e9ded2;
padding: 16px;
border-radius: 8px;
+ margin-left: 1%;
/* 移除固定高度 */
/* height: 230px; */
/* 添加阴影效果增强视觉层次感 */
diff --git a/src/pages/Forum/posts-main/components/PostList.jsx b/src/pages/Forum/posts-main/components/PostList.jsx
index cf0a424..a941f49 100644
--- a/src/pages/Forum/posts-main/components/PostList.jsx
+++ b/src/pages/Forum/posts-main/components/PostList.jsx
@@ -214,11 +214,11 @@
})
}
- <div className="pagination">
+ {/* <div className="pagination">
<button disabled={page === 1} onClick={() => setPage(page - 1)}>上一页</button>
<span>第 {page} 页 / 共 {totalPages} 页</span>
<button disabled={page === totalPages} onClick={() => setPage(page + 1)}>下一页</button>
- </div>
+ </div> */}
</div>
);
};
diff --git a/src/pages/Forum/promotion-part/a.jpg b/src/pages/Forum/promotion-part/a.jpg
new file mode 100644
index 0000000..f8bf405
--- /dev/null
+++ b/src/pages/Forum/promotion-part/a.jpg
Binary files differ
diff --git a/src/pages/InterestGroup/InterestGroup.jsx b/src/pages/InterestGroup/InterestGroup.jsx
index b3ae332..3842aad 100644
--- a/src/pages/InterestGroup/InterestGroup.jsx
+++ b/src/pages/InterestGroup/InterestGroup.jsx
@@ -79,7 +79,7 @@
)}
<GroupList />
- <GroupPagination />
+ {/* <GroupPagination /> */}
</div>
</div>
);
diff --git a/src/pages/NewUserGuide/NewUserGuide.jsx b/src/pages/NewUserGuide/NewUserGuide.jsx
index 7a3dad1..e677db7 100644
--- a/src/pages/NewUserGuide/NewUserGuide.jsx
+++ b/src/pages/NewUserGuide/NewUserGuide.jsx
@@ -69,7 +69,7 @@
className={activeTab === 'tasks' ? 'active' : ''}
onClick={() => setActiveTab('tasks')}
>
- 成长任务
+ 用户等级
</button>
</div>
<div className="guide-main">
diff --git a/src/pages/NewUserGuide/NewbieTasks.css b/src/pages/NewUserGuide/NewbieTasks.css
index 4d64389..cd6d4b6 100644
--- a/src/pages/NewUserGuide/NewbieTasks.css
+++ b/src/pages/NewUserGuide/NewbieTasks.css
@@ -1,4 +1,4 @@
-.newbie-tasks {
+/* .newbie-tasks {
font-size: 16px;
line-height: 1.6;
}
@@ -61,4 +61,29 @@
margin-bottom: 20px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid #eee;
+} */
+
+.newbie-tasks-container {
+ padding: 20px;
+}
+
+.page-title {
+ text-align: center;
+ margin-bottom: 20px;
+}
+
+.tasks-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.tasks-table th,
+.tasks-table td {
+ border: 1px solid #ccc;
+ padding: 10px;
+ text-align: left;
+}
+
+.tasks-table th {
+ background-color: #f4f4f4;
}
diff --git a/src/pages/NewUserGuide/NewbieTasks.jsx b/src/pages/NewUserGuide/NewbieTasks.jsx
index 5ff6a9c..5fbd5ea 100644
--- a/src/pages/NewUserGuide/NewbieTasks.jsx
+++ b/src/pages/NewUserGuide/NewbieTasks.jsx
@@ -1,154 +1,208 @@
+// // export default NewbieTasks;
+// import React, { useEffect, useState } from 'react';
+// import axios from 'axios';
+
+// const NewbieTasks = () => {
+// const [tasks, setTasks] = useState([]);
+// const [experience, setExperience] = useState({});
+// const [currentStep, setCurrentStep] = useState({});
+// const [loading, setLoading] = useState(true);
+
+// const baseURL = '/echo/task/tutorial';
+// const userId = 1;
+
+// const fetchTasks = async () => {
+// try {
+// const response = await axios.get(`${baseURL}/getAllTasks`, {
+// params: { user_id: userId },
+// });
+// setTasks(response.data.tasks || []);
+// } catch (error) {
+// console.error('获取任务失败:', error);
+// }
+// };
+
+// const fetchExperience = async () => {
+// try {
+// const response = await axios.get(`${baseURL}/getAllTasks`, {
+// params: { user_id: userId },
+// });
+// setExperience(response.data || {});
+// } catch (error) {
+// console.error('获取经验失败:', error);
+// }
+// };
+
+// const fetchCurrentStep = async () => {
+// try {
+// const response = await axios.get(`${baseURL}/getNewStep`, {
+// params: { user_id: userId },
+// });
+// setCurrentStep(response.data || {});
+// } catch (error) {
+// console.error('获取引导步骤失败:', error);
+// }
+// };
+
+// const updateStatus = async (taskId) => {
+// try {
+// await axios.post(`${baseURL}/updateStatus`, {
+// user_id: userId,
+// task_id: taskId,
+// });
+// await fetchTasks();
+// await fetchExperience();
+// } catch (error) {
+// console.error('更新状态失败:', error);
+// }
+// };
+
+// const updateProgress = async (taskId, progress) => {
+// try {
+// await axios.post(`${baseURL}/updateProgress`, {
+// user_id: userId,
+// task_id: taskId,
+// progress,
+// });
+// await fetchTasks();
+// } catch (error) {
+// console.error('更新进度失败:', error);
+// }
+// };
+
+// const claimReward = async (taskId) => {
+// try {
+// await axios.post(`${baseURL}/rewardClaim`, {
+// user_id: userId,
+// task_id: taskId,
+// });
+// await fetchTasks();
+// await fetchExperience();
+// } catch (error) {
+// console.error('领取奖励失败:', error);
+// }
+// };
+
+// const checkRewardStatus = async (taskId) => {
+// try {
+// const response = await axios.post(`${baseURL}/rewardReview`, {
+// user_id: userId,
+// task_id: taskId,
+// });
+// alert(response.data.message || '已完成');
+// } catch (error) {
+// console.error('查看失败:', error);
+// }
+// };
+
+// useEffect(() => {
+// const init = async () => {
+// await fetchTasks();
+// await fetchExperience();
+// await fetchCurrentStep();
+// setLoading(false);
+// };
+// init();
+// }, []);
+
+// if (loading) return <p className="loading">加载中...</p>;
+
+// return (
+// <div className="common-card right-content">
+// <div className="profile-header">
+// <h1>🎯 用户等级</h1>
+// </div>
+
+// <div className="profile-details">
+// <p><strong>当前经验:</strong>{experience.current_experience}</p>
+// <p><strong>等级:</strong>{experience.level}</p>
+// <p><strong>奖励经验:</strong>{experience.reward?.experience || 0}</p>
+// <p><strong>奖励积分:</strong>{experience.reward?.points || 0}</p>
+// </div>
+
+// <div className="profile-details">
+// <p><strong>🧭 当前引导步骤:</strong>{currentStep.current_step}</p>
+// <p>{currentStep.step_description}</p>
+// </div>
+
+// <div className="profile-details">
+// <h2>任务列表</h2>
+// <ul>
+// {tasks.map(task => (
+// <li key={task.task_id} style={{ marginBottom: '20px', borderBottom: '1px solid #eee', paddingBottom: '10px' }}>
+// <p><strong>任务:</strong>{task.title}</p>
+// <p><strong>描述:</strong>{task.description}</p>
+// <p><strong>进度:</strong>{task.progress}%</p>
+// <p><strong>状态:</strong>{task.status}</p>
+// <p><strong>奖励:</strong>经验 {task.reward.experience},积分 {task.reward.points}</p>
+
+// <div className="task-btn-group">
+// <button className="task-btn" onClick={() => updateStatus(task.task_id)}>更新状态</button>
+// <button className="task-btn" onClick={() => updateProgress(task.task_id, 100)}>提交进度</button>
+// <button className="task-btn" onClick={() => claimReward(task.task_id)}>领取奖励</button>
+// <button className="task-btn" onClick={() => checkRewardStatus(task.task_id)}>查看奖励状态</button>
+// </div>
+// </li>
+// ))}
+// </ul>
+// </div>
+// </div>
+// );
+// };
+
// export default NewbieTasks;
-import React, { useEffect, useState } from 'react';
-import axios from 'axios';
+
+
+import React from'react';
+import './NewbieTasks.css';
const NewbieTasks = () => {
- const [tasks, setTasks] = useState([]);
- const [experience, setExperience] = useState({});
- const [currentStep, setCurrentStep] = useState({});
- const [loading, setLoading] = useState(true);
-
- const baseURL = '/echo/task/tutorial';
- const userId = 1;
-
- const fetchTasks = async () => {
- try {
- const response = await axios.get(`${baseURL}/getAllTasks`, {
- params: { user_id: userId },
- });
- setTasks(response.data.tasks || []);
- } catch (error) {
- console.error('获取任务失败:', error);
- }
- };
-
- const fetchExperience = async () => {
- try {
- const response = await axios.get(`${baseURL}/getAllTasks`, {
- params: { user_id: userId },
- });
- setExperience(response.data || {});
- } catch (error) {
- console.error('获取经验失败:', error);
- }
- };
-
- const fetchCurrentStep = async () => {
- try {
- const response = await axios.get(`${baseURL}/getNewStep`, {
- params: { user_id: userId },
- });
- setCurrentStep(response.data || {});
- } catch (error) {
- console.error('获取引导步骤失败:', error);
- }
- };
-
- const updateStatus = async (taskId) => {
- try {
- await axios.post(`${baseURL}/updateStatus`, {
- user_id: userId,
- task_id: taskId,
- });
- await fetchTasks();
- await fetchExperience();
- } catch (error) {
- console.error('更新状态失败:', error);
- }
- };
-
- const updateProgress = async (taskId, progress) => {
- try {
- await axios.post(`${baseURL}/updateProgress`, {
- user_id: userId,
- task_id: taskId,
- progress,
- });
- await fetchTasks();
- } catch (error) {
- console.error('更新进度失败:', error);
- }
- };
-
- const claimReward = async (taskId) => {
- try {
- await axios.post(`${baseURL}/rewardClaim`, {
- user_id: userId,
- task_id: taskId,
- });
- await fetchTasks();
- await fetchExperience();
- } catch (error) {
- console.error('领取奖励失败:', error);
- }
- };
-
- const checkRewardStatus = async (taskId) => {
- try {
- const response = await axios.post(`${baseURL}/rewardReview`, {
- user_id: userId,
- task_id: taskId,
- });
- alert(response.data.message || '已完成');
- } catch (error) {
- console.error('查看失败:', error);
- }
- };
-
- useEffect(() => {
- const init = async () => {
- await fetchTasks();
- await fetchExperience();
- await fetchCurrentStep();
- setLoading(false);
- };
- init();
- }, []);
-
- if (loading) return <p className="loading">加载中...</p>;
-
- return (
- <div className="common-card right-content">
- <div className="profile-header">
- <h1>🎯 成长任务</h1>
- </div>
-
- <div className="profile-details">
- <p><strong>当前经验:</strong>{experience.current_experience}</p>
- <p><strong>等级:</strong>{experience.level}</p>
- <p><strong>奖励经验:</strong>{experience.reward?.experience || 0}</p>
- <p><strong>奖励积分:</strong>{experience.reward?.points || 0}</p>
- </div>
-
- <div className="profile-details">
- <p><strong>🧭 当前引导步骤:</strong>{currentStep.current_step}</p>
- <p>{currentStep.step_description}</p>
- </div>
-
- <div className="profile-details">
- <h2>任务列表</h2>
- <ul>
- {tasks.map(task => (
- <li key={task.task_id} style={{ marginBottom: '20px', borderBottom: '1px solid #eee', paddingBottom: '10px' }}>
- <p><strong>任务:</strong>{task.title}</p>
- <p><strong>描述:</strong>{task.description}</p>
- <p><strong>进度:</strong>{task.progress}%</p>
- <p><strong>状态:</strong>{task.status}</p>
- <p><strong>奖励:</strong>经验 {task.reward.experience},积分 {task.reward.points}</p>
-
- <div className="task-btn-group">
- <button className="task-btn" onClick={() => updateStatus(task.task_id)}>更新状态</button>
- <button className="task-btn" onClick={() => updateProgress(task.task_id, 100)}>提交进度</button>
- <button className="task-btn" onClick={() => claimReward(task.task_id)}>领取奖励</button>
- <button className="task-btn" onClick={() => checkRewardStatus(task.task_id)}>查看奖励状态</button>
- </div>
- </li>
- ))}
- </ul>
- </div>
- </div>
- );
+ const tableData = [
+ {
+ level: 'lv0【糖果】',
+ newPermissions: '查看种子分类和评论,查看其他用户主页',
+ upgradeConditions: '无'
+ },
+ {
+ level: 'lv1【曲奇】',
+ newPermissions: '上传免费种子,下载免费种子,发布评论,私信/关注其他用户,收藏种子',
+ upgradeConditions: '经验≥50,分享率≥0.5,上传量≥10G,做种时长≥72h'
+ },
+ {
+ level: 'lv2【巧克力】',
+ newPermissions: '下载付费种子,成立和加入兴趣小组',
+ upgradeConditions: '经验≥200,分享率≥0.75,上传量≥50G,做种时长≥240h'
+ },
+ {
+ level: 'lv3【冰激凌】',
+ newPermissions: '上传付费种子',
+ upgradeConditions: '经验≥500,分享率≥1,上传量≥100G,做种时长≥600h'
+ }
+ ];
+ return (
+ <div className="newbie-tasks-container">
+ <h2 className="page-title">🎯 用户等级</h2>
+ <p>各个等级的权限在下面啦~ 经验值可以通过上传种子获得哦!</p>
+ <table className="tasks-table">
+ <thead>
+ <tr>
+ <th>用户等级</th>
+ <th>新增权限</th>
+ <th>升级条件</th>
+ </tr>
+ </thead>
+ <tbody>
+ {tableData.map((item, index) => (
+ <tr key={index}>
+ <td>{item.level}</td>
+ <td>{item.newPermissions}</td>
+ <td>{item.upgradeConditions}</td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </div>
+ );
};
export default NewbieTasks;
-
diff --git a/src/pages/UserCenter/UserLevelExperience.jsx b/src/pages/UserCenter/UserLevelExperience.jsx
index 81135c1..4269c44 100644
--- a/src/pages/UserCenter/UserLevelExperience.jsx
+++ b/src/pages/UserCenter/UserLevelExperience.jsx
@@ -1,213 +1,213 @@
-// import React, { useState, useEffect } from 'react';
-// import axios from 'axios';
+import React, { useState, useEffect } from 'react';
+import axios from 'axios';
-// const UserLevelExperience = ({ userId }) => {
-// const [experienceInfo, setExperienceInfo] = useState(null);
-// const [error, setError] = useState(null);
-// const [isLoading, setIsLoading] = useState(false);
-// const [upgradeResult, setUpgradeResult] = useState(null);
-// const [hasCheckedIn, setHasCheckedIn] = useState(false);
-// const [isUpgrading, setIsUpgrading] = useState(false);
-// const [lastUpgradeTime, setLastUpgradeTime] = useState(null);
-// const [justUpgraded, setJustUpgraded] = useState(false); // 新增
+const UserLevelExperience = ({ userId }) => {
+ const [experienceInfo, setExperienceInfo] = useState(null);
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [upgradeResult, setUpgradeResult] = useState(null);
+ const [hasCheckedIn, setHasCheckedIn] = useState(false);
+ const [isUpgrading, setIsUpgrading] = useState(false);
+ const [lastUpgradeTime, setLastUpgradeTime] = useState(null);
+ const [justUpgraded, setJustUpgraded] = useState(false); // 新增
-// useEffect(() => {
-// if (!userId) return;
-// fetchAllLevelData();
-// }, [userId]);
+ useEffect(() => {
+ if (!userId) return;
+ fetchAllLevelData();
+ }, [userId]);
-// useEffect(() => {
-// if (
-// experienceInfo &&
-// experienceInfo.current_experience >= experienceInfo.next_level_experience &&
-// !isUpgrading &&
-// !justUpgraded &&
-// (!lastUpgradeTime || Date.now() - lastUpgradeTime > 2000)
-// ) {
-// checkUpgrade();
-// }
-// }, [experienceInfo, isUpgrading, lastUpgradeTime, justUpgraded]);
+ useEffect(() => {
+ if (
+ experienceInfo &&
+ experienceInfo.current_experience >= experienceInfo.next_level_experience &&
+ !isUpgrading &&
+ !justUpgraded &&
+ (!lastUpgradeTime || Date.now() - lastUpgradeTime > 2000)
+ ) {
+ }
+ }, [experienceInfo, isUpgrading, lastUpgradeTime, justUpgraded]);
-// const fetchAllLevelData = async () => {
-// try {
-// setIsLoading(true);
-// setError(null);
+ const fetchAllLevelData = async () => {
+ try {
+ setIsLoading(true);
+ setError(null);
-// const { data } = await axios.get('/echo/level/getExperience', {
-// params: { user_id: userId },
-// });
+ const { data } = await axios.get('/echo/level/getExperience', {
+ params: { user_id: userId },
+ });
-// const normalizedData = {
-// ...data,
-// current_level: data.current_level || data.level,
-// };
+ const normalizedData = {
+ ...data,
+ current_level: data.current_level || data.level,
+ };
-// setExperienceInfo(normalizedData);
+ setExperienceInfo(normalizedData);
-// const today = new Date().toDateString();
-// const lastCheckIn = localStorage.getItem('lastCheckIn');
-// setHasCheckedIn(lastCheckIn === today);
-// } catch (err) {
-// console.error('经验信息获取失败:', err);
-// setError('获取经验信息失败');
-// } finally {
-// setIsLoading(false);
-// }
-// };
+ const today = new Date().toDateString();
+ const lastCheckIn = localStorage.getItem('lastCheckIn');
+ setHasCheckedIn(lastCheckIn === today);
+ } catch (err) {
+ console.error('经验信息获取失败:', err);
+ setError('获取经验信息失败');
+ } finally {
+ setIsLoading(false);
+ }
+ };
-// const updateExperience = async (source, amount = 10) => {
-// try {
-// setIsLoading(true);
-// setError(null);
+ const updateExperience = async (source, amount = 10) => {
+ try {
+ setIsLoading(true);
+ setError(null);
-// const { data } = await axios.post('/echo/level/updateExperience', {
-// user_id: userId,
-// experience: amount,
-// source: source,
-// });
+ const { data } = await axios.post('/echo/level/updateExperience', {
+ user_id: userId,
+ experience: amount,
+ source: source,
+ });
-// setExperienceInfo((prev) => ({
-// ...prev,
-// current_experience: data.current_experience,
-// }));
+ setExperienceInfo((prev) => ({
+ ...prev,
+ current_experience: data.current_experience,
+ }));
-// alert(`获得${amount}点经验值!来源:${source}`);
+ alert(`获得${amount}点经验值!来源:${source}`);
-// if (source === 'check-in') {
-// localStorage.setItem('lastCheckIn', new Date().toDateString());
-// setHasCheckedIn(true);
-// }
-// } catch (err) {
-// console.error('更新经验失败:', err);
-// setError(err.response?.data?.message || '更新经验失败');
-// } finally {
-// setIsLoading(false);
-// }
-// };
+ if (source === 'check-in') {
+ localStorage.setItem('lastCheckIn', new Date().toDateString());
+ setHasCheckedIn(true);
+ }
+ } catch (err) {
+ console.error('更新经验失败:', err);
+ setError(err.response?.data?.message || '更新经验失败');
+ } finally {
+ setIsLoading(false);
+ }
+ };
-// const checkUpgrade = async () => {
-// if (isUpgrading) return;
+ const checkUpgrade = async () => {
+ if (isUpgrading) return;
-// try {
-// setIsLoading(true);
-// setIsUpgrading(true);
-// setError(null);
+ try {
+ setIsLoading(true);
+ setIsUpgrading(true);
+ setError(null);
-// const { data } = await axios.get('/echo/level/upgrade-check', {
-// params: { user_id: userId },
-// });
+ const { data } = await axios.get('/echo/level/upgrade-check', {
+ params: { user_id: userId },
+ });
-// if (data.can_upgrade) {
-// if (window.confirm('您已满足升级条件,是否要升级?')) {
-// await performUpgrade();
-// }
-// } else {
-// if (data.is_max_level) {
-// alert('您已达到最高等级!');
-// } else {
-// alert(`还不能升级,还需要${data.next_level_experience - data.current_experience}点经验值`);
-// }
-// }
-// } catch (err) {
-// console.error('检查升级失败:', err);
-// setError(err.response?.data?.message || '检查升级失败');
-// } finally {
-// setIsLoading(false);
-// setIsUpgrading(false);
-// }
-// };
+ if (data.can_upgrade) {
+ if (window.confirm('您已满足升级条件,是否要升级?')) {
+ await performUpgrade();
+ }
+ } else {
+ if (data.is_max_level) {
+ alert('您已达到最高等级!');
+ } else {
+ alert(`还不能升级,还需要${data.next_level_experience - data.current_experience}点经验值`);
+ }
+ }
+ } catch (err) {
+ console.error('检查升级失败:', err);
+ setError(err.response?.data?.message || '检查升级失败');
+ } finally {
+ setIsLoading(false);
+ setIsUpgrading(false);
+ }
+ };
-// const performUpgrade = async () => {
-// try {
-// setIsUpgrading(true);
-// setIsLoading(true);
-// setError(null);
+ const performUpgrade = async () => {
+ try {
+ setIsUpgrading(true);
+ setIsLoading(true);
+ setError(null);
-// const { data } = await axios.post('/echo/level/upgrades', {
-// user_id: userId,
-// can_upgrade: true,
-// });
+ const { data } = await axios.post('/echo/level/upgrades', {
+ user_id: userId,
+ can_upgrade: true,
+ });
-// console.log('升级响应数据:', data);
+ console.log('升级响应数据:', data);
-// if (data.status === 'success') {
-// setExperienceInfo((prev) => ({
-// ...prev,
-// current_level: data.new_level,
-// current_experience: data.current_experience || 0,
-// next_level_experience: data.next_level_experience || prev.next_level_experience * 2,
-// }));
+ if (data.status === 'success') {
+ setExperienceInfo((prev) => ({
+ // 更新就会进入useEffect,又执行了checkUpgrade,循环
+ ...prev,
+ current_level: data.new_level,
+ current_experience: data.current_experience || 0,
+ next_level_experience: data.next_level_experience || prev.next_level_experience * 2,
+ }));
-// setUpgradeResult(data);
-// setLastUpgradeTime(Date.now());
-// setJustUpgraded(true); // 标记为刚升级过
-// setTimeout(() => setJustUpgraded(false), 3000); // 3 秒冷却期
-// alert(`恭喜!您已升级到等级 ${data.new_level}!`);
+ setUpgradeResult(data);
+ setLastUpgradeTime(Date.now());
+ setJustUpgraded(true); // 标记为刚升级过
+ setTimeout(() => setJustUpgraded(false), 3000); // 3 秒冷却期
+ alert(`恭喜!您已升级到等级 ${data.new_level}!`);
-// // 再次拉取最新经验数据,避免经验值仍然满足升级条件
-// await fetchAllLevelData();
-// } else {
-// throw new Error(data.message || '升级失败');
-// }
-// } catch (err) {
-// console.error('升级失败:', err);
-// setError(err.message || '升级失败');
-// alert(err.message || '升级失败,请稍后再试');
-// } finally {
-// setIsLoading(false);
-// setIsUpgrading(false);
-// }
-// };
+ // 再次拉取最新经验数据,避免经验值仍然满足升级条件
+ await fetchAllLevelData();
+ } else {
+ throw new Error(data.message || '升级失败');
+ }
+ } catch (err) {
+ console.error('升级失败:', err);
+ setError(err.message || '升级失败');
+ alert(err.message || '升级失败,请稍后再试');
+ } finally {
+ setIsLoading(false);
+ setIsUpgrading(false);
+ }
+ };
-// if (error) return <p className="error">{error}</p>;
-// if (isLoading) return <p>加载中...</p>;
-// if (!experienceInfo) return <p>加载经验信息中...</p>;
+ if (error) return <p className="error">{error}</p>;
+ if (isLoading) return <p>加载中...</p>;
+ if (!experienceInfo) return <p>加载经验信息中...</p>;
-// const { current_experience, next_level_experience, current_level } = experienceInfo;
-// const progressPercent = Math.min(
-// 100,
-// (current_experience / (next_level_experience || 1)) * 100
-// ).toFixed(2);
+ const { current_experience, next_level_experience, current_level } = experienceInfo;
+ const progressPercent = Math.min(
+ 100,
+ (current_experience / (next_level_experience || 1)) * 100
+ ).toFixed(2);
-// const expToNextLevel = Math.max(0, next_level_experience - current_experience);
+ const expToNextLevel = Math.max(0, next_level_experience - current_experience);
-// return (
-// <div className="level-experience-section">
-// <h3>等级与经验</h3>
-// <p><strong>当前等级:</strong>{current_level || '未知'}</p>
-// <p><strong>当前经验:</strong>{current_experience}</p>
-// <p><strong>距离下一等级还需:</strong>{expToNextLevel} 经验值</p>
+ return (
+ <div style={{ padding: '20px', marginTop: '-80px', marginLeft: '70px' }}>
+ {/* <h3>等级与经验</h3> */}
+ <div style={{ fontSize: '20px', marginBottom: '10px' }}><strong>当前等级:</strong>lv{current_level || '未知'}</div>
+ <div style={{ fontSize: '20px', marginBottom: '10px' }}><strong>当前经验:</strong>{current_experience}</div>
+ {/* <p><strong>距离下一等级还需:</strong>{expToNextLevel} 经验值</p> */}
-// <div className="exp-bar-wrapper">
-// <div className="exp-bar" style={{ width: `${progressPercent}%` }} />
-// </div>
-// <p className="exp-progress-text">{progressPercent}%</p>
+ <div className="exp-bar-wrapper">
+ <div className="exp-bar" style={{ width: `${progressPercent}%` }} />
+ </div>
+ <p className="exp-progress-text">{progressPercent}%</p>
-// {upgradeResult && (
-// <div className="upgrade-success">
-// <p>恭喜!您已成功升级到等级 {upgradeResult.new_level}!</p>
-// </div>
-// )}
+ {upgradeResult && (
+ <div className="upgrade-success">
+ <p>恭喜!您已成功升级到等级 {upgradeResult.new_level}!</p>
+ </div>
+ )}
-// {error && (
-// <div className="upgrade-error">
-// <p>{error}</p>
-// </div>
-// )}
+ {error && (
+ <div className="upgrade-error">
+ <p>{error}</p>
+ </div>
+ )}
-// <div className="level-actions">
-// <button onClick={() => updateExperience('check-in', 15)} disabled={hasCheckedIn}>
-// {hasCheckedIn ? '今日已签到' : '每日签到 (+15经验)'}
-// </button>
-// <button onClick={() => updateExperience('task', 30)}>完成任务 (+30经验)</button>
-// <button onClick={() => updateExperience('upload', 50)}>上传种子 (+50经验)</button>
-// <button onClick={checkUpgrade} disabled={isUpgrading}>
-// {isUpgrading ? '升级中...' : '检查升级'}
-// </button>
-// </div>
-// </div>
-// );
-// };
+ <div className="level-actions">
+ {/* <button onClick={() => updateExperience('check-in', 15)} disabled={hasCheckedIn}>
+ {hasCheckedIn ? '今日已签到' : '每日签到 (+15经验)'}
+ </button> */}
+ {/* <button onClick={() => updateExperience('task', 30)}>完成任务 (+30经验)</button> */}
+ {/* <button onClick={() => updateExperience('upload', 50)}>上传种子 (+20经验)</button> */}
+ {/* <button onClick={checkUpgrade} disabled={isUpgrading}>
+ {isUpgrading ? '升级中...' : '检查升级'}
+ </button> */}
+ </div>
+ </div>
+ );
+};
-// export default UserLevelExperience;
+export default UserLevelExperience;
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index 79d8313..5c088c9 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import UserProfileBase from './UserProfileBase';
-// import UserLevelExperience from './UserLevelExperience';
+import UserLevelExperience from './UserLevelExperience';
import './UserProfile.css';
import UserStatusChecker from './UserStatusChecker';
@@ -14,7 +14,7 @@
return (
<div>
<UserProfileBase onLoadExperienceInfo={loadExperienceInfo} />
- {/* {userId && <UserLevelExperience userId={userId} />} */}
+ {userId && <UserLevelExperience userId={userId} />}
{/* <UserStatusChecker /> */}
</div>
);
diff --git a/src/pages/UserCenter/UserProfileBase.jsx b/src/pages/UserCenter/UserProfileBase.jsx
index c90cb63..dd28dc2 100644
--- a/src/pages/UserCenter/UserProfileBase.jsx
+++ b/src/pages/UserCenter/UserProfileBase.jsx
@@ -5,6 +5,8 @@
import toast from 'react-hot-toast';
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
+import UserLevelExperience from './UserLevelExperience';
+
const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
@@ -203,6 +205,7 @@
<p><strong>分享率:</strong>{(shareRate * 100).toFixed(2)}%</p>
<p><strong>加入时间:</strong>{new Date(joinedDate).toLocaleDateString()}</p>
+
<div className="profile-actions">
<button onClick={() => setShowPwdModal(true)}>修改密码</button>
<button onClick={handleLogout}>退出登录</button>