Merge "修改登录注册"
diff --git a/src/App.js b/src/App.js
index 1892de8..770c7f9 100644
--- a/src/App.js
+++ b/src/App.js
@@ -15,6 +15,7 @@
import CreateMoment from './pages/FriendMoments/CreateMoment';
import PromotionsPage from './pages/PromotionsPage/PromotionsPage';
import LevelPage from './pages/LevelPage/LevelPage';
+import NewbieTasks from './pages/UserCenter/NewbieTasks';
function App() {
return (
@@ -35,6 +36,7 @@
<Route path="/messages" component={MessagePage}/>
<Route path="/promotions" component={PromotionsPage}/>
<Route path="/level" component={LevelPage}/>
+ <Route path="/user/newbie-tasks" component={NewbieTasks} />
</>
</UserProvider>
);
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
index fd79cec..36d6319 100644
--- a/src/pages/Forum/promotion-part/Promotion.jsx
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -1,148 +1,3 @@
-// import React, { useEffect, useState, useRef } from 'react';
-// import './Promotion.css';
-
-
-
-// const Promotion = () => {
-// const [promotions, setPromotions] = useState([]);
-// const [coldResources, setColdResources] = useState([]);
-// const [loading, setLoading] = useState(true);
-
-// // 轮播索引
-// const [promoIndex, setPromoIndex] = useState(0);
-// const [coldIndex, setColdIndex] = useState(0);
-
-// // 计时器引用,用于清理
-// const promoTimerRef = useRef(null);
-// const coldTimerRef = useRef(null);
-
-// useEffect(() => {
-// fetchData();
-// }, []);
-
-// // 自动轮播:促销活动
-// useEffect(() => {
-// if (promotions.length === 0) return;
-// // 清理旧的计时器
-// clearInterval(promoTimerRef.current);
-// promoTimerRef.current = setInterval(() => {
-// setPromoIndex(prev => (prev + 1) % promotions.length);
-// }, 5000);
-// return () => clearInterval(promoTimerRef.current);
-// }, [promotions]);
-
-// // 自动轮播:冷门资源
-// useEffect(() => {
-// if (coldResources.length === 0) return;
-// clearInterval(coldTimerRef.current);
-// coldTimerRef.current = setInterval(() => {
-// setColdIndex(prev => (prev + 1) % coldResources.length);
-// }, 5000);
-// return () => clearInterval(coldTimerRef.current);
-// }, [coldResources]);
-
-// const fetchData = async () => {
-// try {
-// const promoResponse = await fetch(`/echo/promotions/active`);
-// const promoData = await promoResponse.json();
-// setPromotions(promoData);
-
-// const coldResponse = await fetch(`/echo/resources/cold`);
-// const coldData = await coldResponse.json();
-// setColdResources(coldData);
-// } catch (error) {
-// console.error('获取数据失败:', error);
-// } finally {
-// setLoading(false);
-// }
-// };
-
-// if (loading) {
-// return <div className="promotion-container">加载中...</div>;
-// }
-
-// // 手动切换
-// const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-// const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-// const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
-// const nextCold = () => setColdIndex((coldIndex + 1) % coldResources.length);
-
-// const currentPromo = promotions[promoIndex];
-// const currentCold = coldResources[coldIndex];
-
-// return (
-// <div className="promotion-container carousel-container">
-// {/* 促销活动轮播 */}
-// <section className="carousel-section">
-// <h2>当前促销活动</h2>
-// {promotions.length === 0 ? (
-// <div className="empty-state">暂无促销活动</div>
-// ) : (
-// <div
-// className="carousel"
-// onMouseEnter={() => clearInterval(promoTimerRef.current)}
-// onMouseLeave={() => {
-// promoTimerRef.current = setInterval(() => {
-// setPromoIndex(prev => (prev + 1) % promotions.length);
-// }, 3000);
-// }}
-// >
-// <button className="arrow left" onClick={prevPromo}><</button>
-// <div className="slide">
-// <div><strong>种子类型:</strong>{currentPromo.category}</div>
-// <div><strong>促销时间:</strong>
-// {new Date(currentPromo.promotion_start_time).toLocaleString()} ~{' '}
-// {new Date(currentPromo.promotion_end_time).toLocaleString()}
-// </div>
-// <div><strong>下载折扣:</strong>{currentPromo.download_discount ?? '无'}</div>
-// <div><strong>上传奖励:</strong>{currentPromo.upload_reward ?? '无'}</div>
-// {currentPromo.description && (
-// <div><strong>详细描述:</strong>{currentPromo.description}</div>
-// )}
-// </div>
-// <button className="arrow right" onClick={nextPromo}>></button>
-// </div>
-// )}
-// </section>
-
-// {/* 冷门资源轮播 */}
-// <section className="carousel-section">
-// <h2>冷门资源推荐</h2>
-// {coldResources.length === 0 ? (
-// <div className="empty-state">暂无冷门资源推荐</div>
-// ) : (
-// <div
-// className="carousel"
-// onMouseEnter={() => clearInterval(coldTimerRef.current)}
-// onMouseLeave={() => {
-// coldTimerRef.current = setInterval(() => {
-// setColdIndex(prev => (prev + 1) % coldResources.length);
-// }, 3000);
-// }}
-// >
-// <button className="arrow left" onClick={prevCold}><</button>
-// <div className="slide cold-slide">
-// <img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
-// <div className="resource-info">
-// <div><strong>标题:</strong>{currentCold.title}</div>
-// <div><strong>下载量:</strong>{currentCold.download_count} | <strong>种子数:</strong>{currentCold.seed_count}</div>
-// <div><strong>激励:</strong>
-// {currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
-// {currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
-// {!(currentCold.incentives?.download_exempt || currentCold.incentives?.extra_seed_bonus) && '无'}
-// </div>
-// </div>
-// </div>
-// <button className="arrow right" onClick={nextCold}>></button>
-// </div>
-// )}
-// </section>
-// </div>
-// );
-// };
-
-// export default Promotion;
-
import React, { useEffect, useState, useRef } from 'react';
import './Promotion.css';
@@ -181,10 +36,13 @@
const fetchData = async () => {
try {
- const promoResponse = await fetch(`/echo/promotions/active`);
- const promoData = await promoResponse.json();
+ // ✅ 获取促销活动列表(新接口)
+ const promoResponse = await fetch(`seeds/promotions`);
+ const promoJson = await promoResponse.json();
+ const promoData = Array.isArray(promoJson?.result) ? promoJson.result : [];
setPromotions(promoData);
+ // 冷门资源(接口保持不变,若已更换请提供文档)
const coldResponse = await fetch(`/echo/resources/cold`);
const coldData = await coldResponse.json();
setColdResources(coldData);
@@ -226,16 +84,16 @@
>
<button className="arrow left" onClick={prevPromo}><</button>
<div className="slide">
- <div><strong>种子类型:</strong>{currentPromo?.category ?? '未知'}</div>
+ <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
<div><strong>促销时间:</strong>
- {currentPromo?.promotion_start_time && currentPromo?.promotion_end_time
- ? `${new Date(currentPromo.promotion_start_time).toLocaleString()} ~ ${new Date(currentPromo.promotion_end_time).toLocaleString()}`
+ {currentPromo?.pStartTime && currentPromo?.pEndTime
+ ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
: '未知'}
</div>
- <div><strong>下载折扣:</strong>{currentPromo?.download_discount ?? '无'}</div>
- <div><strong>上传奖励:</strong>{currentPromo?.upload_reward ?? '无'}</div>
+ <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
+ <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
{currentPromo?.description && (
- <div><strong>详细描述:</strong>{currentPromo.description}</div>
+ <div><strong>描述:</strong>{currentPromo.description}</div>
)}
</div>
<button className="arrow right" onClick={nextPromo}>></button>
@@ -263,7 +121,7 @@
<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 ?? 0} | <strong>种子数:</strong>{currentCold.seed_count ?? 0}</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>}
diff --git a/src/pages/UserCenter/NewUserTodo/NewUserTodo.css b/src/pages/UserCenter/NewUserTodo/NewUserTodo.css
deleted file mode 100644
index e69de29..0000000
--- a/src/pages/UserCenter/NewUserTodo/NewUserTodo.css
+++ /dev/null
diff --git a/src/pages/UserCenter/NewUserTodo/NewUserTodo.jsx b/src/pages/UserCenter/NewUserTodo/NewUserTodo.jsx
deleted file mode 100644
index e69de29..0000000
--- a/src/pages/UserCenter/NewUserTodo/NewUserTodo.jsx
+++ /dev/null
diff --git a/src/pages/UserCenter/NewbieTasks.css b/src/pages/UserCenter/NewbieTasks.css
new file mode 100644
index 0000000..e5594de
--- /dev/null
+++ b/src/pages/UserCenter/NewbieTasks.css
@@ -0,0 +1,20 @@
+/* 可加到 UserProfile.css 最底部 */
+.task-btn {
+ margin-right: 8px;
+ padding: 6px 12px;
+ border: none;
+ border-radius: 6px;
+ background-color: #4a90e2;
+ color: white;
+ cursor: pointer;
+}
+
+.task-btn:hover {
+ background-color: #357abd;
+}
+
+.loading {
+ text-align: center;
+ font-size: 18px;
+ color: #333;
+}
diff --git a/src/pages/UserCenter/NewbieTasks.jsx b/src/pages/UserCenter/NewbieTasks.jsx
new file mode 100644
index 0000000..fd1e44e
--- /dev/null
+++ b/src/pages/UserCenter/NewbieTasks.jsx
@@ -0,0 +1,172 @@
+import React, { useEffect, useState } from 'react';
+import axios from 'axios';
+import Header from '../../components/Header';
+import './UserProfile.css'; // 复用个人资料页面的样式
+import UserNav from './UserNav';
+
+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="user-profile-container">
+ <Header />
+ <div className="user-center">
+ <div className="user-nav-container">
+ <UserNav activeKey="newbieTasks" onSelect={() => {}} />
+ </div>
+
+ <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' }}>
+ <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 style={{ marginTop: '10px' }}>
+ <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>
+ </div>
+ </div>
+ );
+};
+
+export default NewbieTasks;
diff --git a/src/pages/UserCenter/UserCenterPage.jsx b/src/pages/UserCenter/UserCenterPage.jsx
new file mode 100644
index 0000000..b6fb20e
--- /dev/null
+++ b/src/pages/UserCenter/UserCenterPage.jsx
@@ -0,0 +1,35 @@
+import React, { useState } from 'react';
+import UserNav from './UserNav.jsx'; // 显式添加.jsx扩展名
+import UserProfile from './UserProfile.jsx';
+import NewbieTasks from './NewbieTasks.jsx';
+import Header from '../../components/Header.jsx'; // 假设Header也是JSX组件
+import './UserProfile.css';
+
+const UserCenterPage = () => {
+ const [activeTab, setActiveTab] = useState('profile');
+
+ const renderContent = () => {
+ switch (activeTab) {
+ case 'profile':
+ return <UserProfile />;
+ case 'newbieTasks':
+ return <NewbieTasks />;
+ default:
+ return null;
+ }
+ };
+
+ return (
+ <div className="user-profile-container">
+ <Header />
+ <div className="user-center" style={{ display: 'flex' }}>
+ <UserNav activeKey={activeTab} onSelect={setActiveTab} />
+ <div className="common-card right-content" style={{ flex: 1, marginLeft: 20 }}>
+ {renderContent()}
+ </div>
+ </div>
+ </div>
+ );
+};
+
+export default UserCenterPage;
\ No newline at end of file
diff --git a/src/pages/UserCenter/UserNav/UserNav.css b/src/pages/UserCenter/UserNav.css
similarity index 100%
rename from src/pages/UserCenter/UserNav/UserNav.css
rename to src/pages/UserCenter/UserNav.css
diff --git a/src/pages/UserCenter/UserNav.jsx b/src/pages/UserCenter/UserNav.jsx
new file mode 100644
index 0000000..11d08f8
--- /dev/null
+++ b/src/pages/UserCenter/UserNav.jsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { Link, useLocation } from 'react-router-dom';
+import './UserNav.css'; // 导入 UserNav 样式文件
+
+const UserNav = () => {
+ const location = useLocation();
+
+ // 竖直导航栏的链接项(已添加“新手任务”)
+ const navLinks = [
+ { to: '/user/profile', label: '个人资料' },
+ { to: '/user/dynamics', label: '我的动态' },
+ { to: '/user/friends', label: '我的好友' },
+ { to: '/user/groups', label: '我的群组' },
+ { to: '/user/collections', label: '我的收藏' },
+ { to: '/user/newbie-tasks', label: '新手任务' },
+ ];
+
+ // 判断路径是否是当前活动页面
+ const isActive = (path) => location.pathname.startsWith(path);
+
+ return (
+ <div className="user-nav-container">
+ <nav className="user-nav">
+ {navLinks.map(({ to, label }) => (
+ <Link
+ key={to}
+ to={to}
+ className={`user-nav-item ${isActive(to) ? 'active' : ''}`}
+ >
+ {label}
+ </Link>
+ ))}
+ </nav>
+ </div>
+ );
+};
+
+export default UserNav;
+
+// import React from 'react';
+// import './UserNav.css';
+
+// const UserNav = ({ activeKey, onSelect }) => {
+// const navLinks = [
+// { key: 'profile', label: '个人资料' },
+// // { key: 'dynamics', label: '我的动态' },
+// // { key: 'friends', label: '我的好友' },
+// // { key: 'groups', label: '我的群组' },
+// // { key: 'collections', label: '我的收藏' },
+// { key: 'newbieTasks', label: '新手任务' },
+// ];
+
+// return (
+// <div className="user-nav-container">
+// <nav className="user-nav">
+// {navLinks.map(({ key, label }) => (
+// <div
+// key={key}
+// className={`user-nav-item ${activeKey === key ? 'active' : ''}`}
+// onClick={() => onSelect(key)}
+// style={{ cursor: 'pointer' }}
+// >
+// {label}
+// </div>
+// ))}
+// </nav>
+// </div>
+// );
+// };
+
+// export default UserNav;
+
diff --git a/src/pages/UserCenter/UserNav/UserNav.jsx b/src/pages/UserCenter/UserNav/UserNav.jsx
deleted file mode 100644
index 4bd76db..0000000
--- a/src/pages/UserCenter/UserNav/UserNav.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import { Link, useLocation } from 'react-router-dom';
-import './UserNav.css'; // 导入 UserNav 样式文件
-
-const UserNav = () => {
- const location = useLocation();
-
- // 竖直导航栏的链接项
- const navLinks = [
- { to: '/user/profile', label: '个人资料' },
- { to: '/user/dynamics', label: '我的动态' },
- { to: '/user/friends', label: '我的好友' },
- { to: '/user/groups', label: '我的群组' },
- { to: '/user/collections', label: '我的收藏' },
- ];
-
- // 判断路径是否是当前活动页面
- const isActive = (path) => location.pathname.startsWith(path);
-
- return (
- <div className="user-nav-container">
- <nav className="user-nav">
- {navLinks.map(({ to, label }) => (
- <Link
- key={to}
- to={to}
- className={`user-nav-item ${isActive(to) ? 'active' : ''}`}
- >
- {label}
- </Link>
- ))}
- </nav>
- </div>
- );
-};
-
-export default UserNav;
\ No newline at end of file
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index 5598308..6f9694c 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './UserProfile.css';
-import UserNav from './UserNav/UserNav'; // 导入 UserNav 组件
+import UserNav from './UserNav'; // 导入 UserNav 组件
import Header from '../../components/Header';
-
+const API_BASE = 'http://127.0.0.1:4523/m1/6139971-5831803-default';
const UserProfile = () => {
const [userProfile, setUserProfile] = useState(null);
@@ -20,7 +20,7 @@
const fetchUserProfile = async () => {
try {
setLoading(true);
- const response = await axios.get(`/echo/user/profile`, {
+ const response = await axios.get(`${API_BASE}/echo/user/profile`, {
params: { user_id: userId }
});