修改促销、优化页面布局
Change-Id: Iae813b5b6557efa7059fe6d94bc32e96c984e4ea
diff --git a/src/pages/UserCenter/UserLevelExperience.jsx b/src/pages/UserCenter/UserLevelExperience.jsx
index 60dcd55..81135c1 100644
--- a/src/pages/UserCenter/UserLevelExperience.jsx
+++ b/src/pages/UserCenter/UserLevelExperience.jsx
@@ -7,6 +7,9 @@
// 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;
@@ -14,14 +17,16 @@
// }, [userId]);
// useEffect(() => {
-// // 自动触发升级判断
// if (
// experienceInfo &&
-// experienceInfo.current_experience >= experienceInfo.next_level_experience
+// experienceInfo.current_experience >= experienceInfo.next_level_experience &&
+// !isUpgrading &&
+// !justUpgraded &&
+// (!lastUpgradeTime || Date.now() - lastUpgradeTime > 2000)
// ) {
// checkUpgrade();
// }
-// }, [experienceInfo]);
+// }, [experienceInfo, isUpgrading, lastUpgradeTime, justUpgraded]);
// const fetchAllLevelData = async () => {
// try {
@@ -81,8 +86,11 @@
// };
// const checkUpgrade = async () => {
+// if (isUpgrading) return;
+
// try {
// setIsLoading(true);
+// setIsUpgrading(true);
// setError(null);
// const { data } = await axios.get('/echo/level/upgrade-check', {
@@ -90,46 +98,66 @@
// });
// if (data.can_upgrade) {
-// await performUpgrade(); // 自动触发
+// 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 {
-// 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,
+// }));
-// setExperienceInfo((prev) => ({
-// ...prev,
-// current_level: data.new_level, // 修复:使用正确的字段名
-// current_experience: 0,
-// 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);
-// alert(`恭喜!您已升级到等级 ${data.new_level}!`); // 修复:使用正确的字段名
-// } catch (err) {
-// console.error('升级失败:', err);
-// setError(err.response?.data?.message || '升级失败');
-// } finally {
-// setIsLoading(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>;
@@ -141,11 +169,11 @@
// (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> */}
+// <h3>等级与经验</h3>
// <p><strong>当前等级:</strong>{current_level || '未知'}</p>
// <p><strong>当前经验:</strong>{current_experience}</p>
// <p><strong>距离下一等级还需:</strong>{expToNextLevel} 经验值</p>
@@ -155,14 +183,17 @@
// </div>
// <p className="exp-progress-text">{progressPercent}%</p>
-
// {upgradeResult && (
// <div className="upgrade-success">
-// {/* 使用与状态一致的字段名 */}
-// <p>恭喜!您已成功升级到等级 {upgradeResult.new_level}!</p>
+// <p>恭喜!您已成功升级到等级 {upgradeResult.new_level}!</p>
// </div>
-// )}
-
+// )}
+
+// {error && (
+// <div className="upgrade-error">
+// <p>{error}</p>
+// </div>
+// )}
// <div className="level-actions">
// <button onClick={() => updateExperience('check-in', 15)} disabled={hasCheckedIn}>
@@ -170,7 +201,9 @@
// </button>
// <button onClick={() => updateExperience('task', 30)}>完成任务 (+30经验)</button>
// <button onClick={() => updateExperience('upload', 50)}>上传种子 (+50经验)</button>
-// {/* <button onClick={checkUpgrade}>检查升级</button> */}
+// <button onClick={checkUpgrade} disabled={isUpgrading}>
+// {isUpgrading ? '升级中...' : '检查升级'}
+// </button>
// </div>
// </div>
// );
@@ -178,198 +211,3 @@
// export default UserLevelExperience;
-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);
-
- useEffect(() => {
- if (!userId) return;
- fetchAllLevelData();
- }, [userId]);
-
- useEffect(() => {
- // 自动触发升级判断
- if (
- experienceInfo &&
- experienceInfo.current_experience >= experienceInfo.next_level_experience
- ) {
- checkUpgrade();
- }
- }, [experienceInfo]);
-
- const fetchAllLevelData = async () => {
- try {
- setIsLoading(true);
- setError(null);
-
- const { data } = await axios.get('/echo/level/getExperience', {
- params: { user_id: userId },
- });
-
- const normalizedData = {
- ...data,
- current_level: data.current_level || data.level,
- };
-
- 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 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,
- });
-
- setExperienceInfo((prev) => ({
- ...prev,
- current_experience: data.current_experience,
- }));
-
- 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);
- }
- };
-
- const checkUpgrade = async () => {
- try {
- setIsLoading(true);
- setError(null);
-
- 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);
- }
- };
-
- const performUpgrade = async () => {
- try {
- setIsLoading(true);
- setError(null);
-
- const { data } = await axios.post('/echo/level/upgrades', {
- user_id: userId,
- can_upgrade: true,
- });
-
- console.log('升级响应数据:', data);
-
- // 正确处理升级结果
- if (data.status === 'success') {
- setExperienceInfo((prev) => ({
- ...prev,
- current_level: data.new_level,
- current_experience: 0,
- next_level_experience: prev.next_level_experience * 2,
- }));
-
- setUpgradeResult(data);
- alert(`恭喜!您已升级到等级 ${data.new_level}!`);
- } else {
- throw new Error(data.message || '升级失败');
- }
- } catch (err) {
- console.error('升级失败:', err);
- setError(err.message || '升级失败');
- alert(err.message || '升级失败,请稍后再试');
- } finally {
- setIsLoading(false);
- }
- };
-
- 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 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>
-
- <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>
- )}
-
- {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}>检查升级</button>
- </div>
- </div>
- );
-};
-
-export default UserLevelExperience;
diff --git a/src/pages/UserCenter/UserNav.jsx b/src/pages/UserCenter/UserNav.jsx
index 63b83ca..7e09e2c 100644
--- a/src/pages/UserCenter/UserNav.jsx
+++ b/src/pages/UserCenter/UserNav.jsx
@@ -10,7 +10,7 @@
{ to: '/user/profile', label: '个人资料' },
{ to: '/user/dynamics', label: '我的动态' },
{ to: '/user/friends', label: '我的好友' },
- { to: '/user/groups', label: '我的群组' },
+ // { to: '/user/groups', label: '我的群组' },
{ to: '/user/collections', label: '我的收藏' },
// { to: '/user/newbie-tasks', label: '用户考核' },
{ to: '/user/invite', label: '邀请新用户' },
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index bcfbf0b..79d8313 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -1,7 +1,8 @@
import React from 'react';
import UserProfileBase from './UserProfileBase';
-import UserLevelExperience from './UserLevelExperience';
+// import UserLevelExperience from './UserLevelExperience';
import './UserProfile.css';
+import UserStatusChecker from './UserStatusChecker';
const UserProfile = () => {
const [userId, setUserId] = React.useState(null);
@@ -13,7 +14,8 @@
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 791baca..c90cb63 100644
--- a/src/pages/UserCenter/UserProfileBase.jsx
+++ b/src/pages/UserCenter/UserProfileBase.jsx
@@ -9,7 +9,7 @@
const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
const UserProfileBase = ({ onLoadExperienceInfo }) => {
- const { user, loading, logout } = useUser();
+ const { user, loading, logout ,saveUser} = useUser();
const [userProfile, setUserProfile] = useState(null);
const [error, setError] = useState(null);
@@ -66,34 +66,44 @@
fetchUserProfile();
}, [user, loading, onLoadExperienceInfo]);
- const handleAvatarUpload = async (e) => {
- const file = e.target.files[0];
- if (!file) return;
+const handleAvatarUpload = async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
- const formData = new FormData();
- formData.append('file', file);
+ const formData = new FormData();
+ formData.append('file', file);
- try {
- const { data } = await axios.post(
- `/echo/user/${user.userId}/uploadAvatar`,
- formData,
- { headers: { 'Content-Type': 'multipart/form-data' } }
- );
+ try {
+ const { data } = await axios.post(
+ `/echo/user/${user.userId}/uploadAvatar`,
+ formData,
+ { headers: { 'Content-Type': 'multipart/form-data' } }
+ );
- if (data?.avatarUrl) {
- setUserProfile((prev) => ({
- ...prev,
- avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
- }));
- toast.success('头像上传成功');
- } else {
- toast.success('头像上传成功,但未返回新头像地址');
- }
- } catch (err) {
- console.error('上传失败:', err);
- toast.error('头像上传失败,请重试');
+ if (data?.avatarUrl) {
+ // 加时间戳避免缓存
+ const newAvatarUrl = `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}?t=${Date.now()}`;
+
+ setUserProfile((prev) => ({
+ ...prev,
+ avatarUrl: newAvatarUrl,
+ }));
+
+ saveUser({
+ ...user,
+ avatarUrl: newAvatarUrl,
+ });
+
+ toast.success('头像上传成功');
+ } else {
+ toast.success('头像上传成功,但未返回新头像地址');
}
- };
+ } catch (err) {
+ console.error('上传失败:', err);
+ toast.error('头像上传失败,请重试');
+ }
+};
+
const handleLogout = () => {
logout();
diff --git a/src/pages/UserCenter/UserStatusChecker.jsx b/src/pages/UserCenter/UserStatusChecker.jsx
new file mode 100644
index 0000000..7b8b303
--- /dev/null
+++ b/src/pages/UserCenter/UserStatusChecker.jsx
@@ -0,0 +1,58 @@
+import React, { useEffect, useState, useContext } from 'react';
+import axios from 'axios';
+import { UserContext } from '../../context/UserContext';
+
+const UserStatusChecker = () => {
+ const { user } = useContext(UserContext); // 假设你在上下文中已经有 user 信息
+ const [statusMessage, setStatusMessage] = useState('');
+ const [statusType, setStatusType] = useState('normal'); // normal / warning / danger
+
+ useEffect(() => {
+ if (user?.id) {
+ checkUserShareRate(user.id);
+ }
+ }, [user]);
+
+ const checkUserShareRate = async (userId) => {
+ try {
+ const response = await axios.get(`/users/${userId}/share-rate`);
+ const message = response.data;
+
+ setStatusMessage(message);
+
+ if (message.includes('账号已被注销')) {
+ setStatusType('danger');
+ } else if (message.includes('警告')) {
+ setStatusType('warning');
+ } else {
+ setStatusType('normal');
+ }
+
+ } catch (error) {
+ console.error('获取用户状态失败:', error);
+ setStatusMessage('无法获取用户状态');
+ setStatusType('danger');
+ }
+ };
+
+ const getStatusStyle = () => {
+ switch (statusType) {
+ case 'warning':
+ return { backgroundColor: '#fff3cd', color: '#856404', padding: '10px', borderRadius: '8px' };
+ case 'danger':
+ return { backgroundColor: '#f8d7da', color: '#721c24', padding: '10px', borderRadius: '8px' };
+ default:
+ return { backgroundColor: '#d4edda', color: '#155724', padding: '10px', borderRadius: '8px' };
+ }
+ };
+
+ return (
+ <div style={{ marginTop: '20px' }}>
+ <div style={getStatusStyle()}>
+ <strong>账号状态提示:</strong> {statusMessage}
+ </div>
+ </div>
+ );
+};
+
+export default UserStatusChecker;