blob: 4269c44569506a8a45d1881107ae8986df653628 [file] [log] [blame]
Krishya34493be2025-06-09 22:45:46 +08001import React, { useState, useEffect } from 'react';
2import axios from 'axios';
Krishya73cd8822025-06-07 15:48:41 +08003
Krishya34493be2025-06-09 22:45:46 +08004const UserLevelExperience = ({ userId }) => {
5 const [experienceInfo, setExperienceInfo] = useState(null);
6 const [error, setError] = useState(null);
7 const [isLoading, setIsLoading] = useState(false);
8 const [upgradeResult, setUpgradeResult] = useState(null);
9 const [hasCheckedIn, setHasCheckedIn] = useState(false);
10 const [isUpgrading, setIsUpgrading] = useState(false);
11 const [lastUpgradeTime, setLastUpgradeTime] = useState(null);
12 const [justUpgraded, setJustUpgraded] = useState(false); // 新增
Krishya73cd8822025-06-07 15:48:41 +080013
Krishya34493be2025-06-09 22:45:46 +080014 useEffect(() => {
15 if (!userId) return;
16 fetchAllLevelData();
17 }, [userId]);
Krishya73cd8822025-06-07 15:48:41 +080018
Krishya34493be2025-06-09 22:45:46 +080019 useEffect(() => {
20 if (
21 experienceInfo &&
22 experienceInfo.current_experience >= experienceInfo.next_level_experience &&
23 !isUpgrading &&
24 !justUpgraded &&
25 (!lastUpgradeTime || Date.now() - lastUpgradeTime > 2000)
26 ) {
27 }
28 }, [experienceInfo, isUpgrading, lastUpgradeTime, justUpgraded]);
Krishya73cd8822025-06-07 15:48:41 +080029
Krishya34493be2025-06-09 22:45:46 +080030 const fetchAllLevelData = async () => {
31 try {
32 setIsLoading(true);
33 setError(null);
Krishya73cd8822025-06-07 15:48:41 +080034
Krishya34493be2025-06-09 22:45:46 +080035 const { data } = await axios.get('/echo/level/getExperience', {
36 params: { user_id: userId },
37 });
Krishya73cd8822025-06-07 15:48:41 +080038
Krishya34493be2025-06-09 22:45:46 +080039 const normalizedData = {
40 ...data,
41 current_level: data.current_level || data.level,
42 };
Krishya73cd8822025-06-07 15:48:41 +080043
Krishya34493be2025-06-09 22:45:46 +080044 setExperienceInfo(normalizedData);
Krishya73cd8822025-06-07 15:48:41 +080045
Krishya34493be2025-06-09 22:45:46 +080046 const today = new Date().toDateString();
47 const lastCheckIn = localStorage.getItem('lastCheckIn');
48 setHasCheckedIn(lastCheckIn === today);
49 } catch (err) {
50 console.error('经验信息获取失败:', err);
51 setError('获取经验信息失败');
52 } finally {
53 setIsLoading(false);
54 }
55 };
Krishya73cd8822025-06-07 15:48:41 +080056
Krishya34493be2025-06-09 22:45:46 +080057 const updateExperience = async (source, amount = 10) => {
58 try {
59 setIsLoading(true);
60 setError(null);
Krishya73cd8822025-06-07 15:48:41 +080061
Krishya34493be2025-06-09 22:45:46 +080062 const { data } = await axios.post('/echo/level/updateExperience', {
63 user_id: userId,
64 experience: amount,
65 source: source,
66 });
Krishya73cd8822025-06-07 15:48:41 +080067
Krishya34493be2025-06-09 22:45:46 +080068 setExperienceInfo((prev) => ({
69 ...prev,
70 current_experience: data.current_experience,
71 }));
Krishya73cd8822025-06-07 15:48:41 +080072
Krishya34493be2025-06-09 22:45:46 +080073 alert(`获得${amount}点经验值!来源:${source}`);
Krishya73cd8822025-06-07 15:48:41 +080074
Krishya34493be2025-06-09 22:45:46 +080075 if (source === 'check-in') {
76 localStorage.setItem('lastCheckIn', new Date().toDateString());
77 setHasCheckedIn(true);
78 }
79 } catch (err) {
80 console.error('更新经验失败:', err);
81 setError(err.response?.data?.message || '更新经验失败');
82 } finally {
83 setIsLoading(false);
84 }
85 };
Krishya73cd8822025-06-07 15:48:41 +080086
Krishya34493be2025-06-09 22:45:46 +080087 const checkUpgrade = async () => {
88 if (isUpgrading) return;
Krishyadbfadaa2025-06-09 20:33:15 +080089
Krishya34493be2025-06-09 22:45:46 +080090 try {
91 setIsLoading(true);
92 setIsUpgrading(true);
93 setError(null);
Krishya73cd8822025-06-07 15:48:41 +080094
Krishya34493be2025-06-09 22:45:46 +080095 const { data } = await axios.get('/echo/level/upgrade-check', {
96 params: { user_id: userId },
97 });
Krishya73cd8822025-06-07 15:48:41 +080098
Krishya34493be2025-06-09 22:45:46 +080099 if (data.can_upgrade) {
100 if (window.confirm('您已满足升级条件,是否要升级?')) {
101 await performUpgrade();
102 }
103 } else {
104 if (data.is_max_level) {
105 alert('您已达到最高等级!');
106 } else {
107 alert(`还不能升级,还需要${data.next_level_experience - data.current_experience}点经验值`);
108 }
109 }
110 } catch (err) {
111 console.error('检查升级失败:', err);
112 setError(err.response?.data?.message || '检查升级失败');
113 } finally {
114 setIsLoading(false);
115 setIsUpgrading(false);
116 }
117 };
Krishya73cd8822025-06-07 15:48:41 +0800118
Krishya34493be2025-06-09 22:45:46 +0800119 const performUpgrade = async () => {
120 try {
121 setIsUpgrading(true);
122 setIsLoading(true);
123 setError(null);
Krishya73cd8822025-06-07 15:48:41 +0800124
Krishya34493be2025-06-09 22:45:46 +0800125 const { data } = await axios.post('/echo/level/upgrades', {
126 user_id: userId,
127 can_upgrade: true,
128 });
Krishya73cd8822025-06-07 15:48:41 +0800129
Krishya34493be2025-06-09 22:45:46 +0800130 console.log('升级响应数据:', data);
Krishya73cd8822025-06-07 15:48:41 +0800131
Krishya34493be2025-06-09 22:45:46 +0800132 if (data.status === 'success') {
133 setExperienceInfo((prev) => ({
134 // 更新就会进入useEffect,又执行了checkUpgrade,循环
135 ...prev,
136 current_level: data.new_level,
137 current_experience: data.current_experience || 0,
138 next_level_experience: data.next_level_experience || prev.next_level_experience * 2,
139 }));
Krishya73cd8822025-06-07 15:48:41 +0800140
Krishya34493be2025-06-09 22:45:46 +0800141 setUpgradeResult(data);
142 setLastUpgradeTime(Date.now());
143 setJustUpgraded(true); // 标记为刚升级过
144 setTimeout(() => setJustUpgraded(false), 3000); // 3 秒冷却期
145 alert(`恭喜!您已升级到等级 ${data.new_level}!`);
Krishya73cd8822025-06-07 15:48:41 +0800146
Krishya34493be2025-06-09 22:45:46 +0800147 // 再次拉取最新经验数据,避免经验值仍然满足升级条件
148 await fetchAllLevelData();
149 } else {
150 throw new Error(data.message || '升级失败');
151 }
152 } catch (err) {
153 console.error('升级失败:', err);
154 setError(err.message || '升级失败');
155 alert(err.message || '升级失败,请稍后再试');
156 } finally {
157 setIsLoading(false);
158 setIsUpgrading(false);
159 }
160 };
Krishya73cd8822025-06-07 15:48:41 +0800161
Krishya34493be2025-06-09 22:45:46 +0800162 if (error) return <p className="error">{error}</p>;
163 if (isLoading) return <p>加载中...</p>;
164 if (!experienceInfo) return <p>加载经验信息中...</p>;
Krishya73cd8822025-06-07 15:48:41 +0800165
Krishya34493be2025-06-09 22:45:46 +0800166 const { current_experience, next_level_experience, current_level } = experienceInfo;
167 const progressPercent = Math.min(
168 100,
169 (current_experience / (next_level_experience || 1)) * 100
170 ).toFixed(2);
Krishya73cd8822025-06-07 15:48:41 +0800171
Krishya34493be2025-06-09 22:45:46 +0800172 const expToNextLevel = Math.max(0, next_level_experience - current_experience);
Krishya73cd8822025-06-07 15:48:41 +0800173
Krishya34493be2025-06-09 22:45:46 +0800174 return (
175 <div style={{ padding: '20px', marginTop: '-80px', marginLeft: '70px' }}>
176 {/* <h3>等级与经验</h3> */}
177 <div style={{ fontSize: '20px', marginBottom: '10px' }}><strong>当前等级:</strong>lv{current_level || '未知'}</div>
178 <div style={{ fontSize: '20px', marginBottom: '10px' }}><strong>当前经验:</strong>{current_experience}</div>
179 {/* <p><strong>距离下一等级还需:</strong>{expToNextLevel} 经验值</p> */}
Krishya73cd8822025-06-07 15:48:41 +0800180
Krishya34493be2025-06-09 22:45:46 +0800181 <div className="exp-bar-wrapper">
182 <div className="exp-bar" style={{ width: `${progressPercent}%` }} />
183 </div>
184 <p className="exp-progress-text">{progressPercent}%</p>
Krishya73cd8822025-06-07 15:48:41 +0800185
Krishya34493be2025-06-09 22:45:46 +0800186 {upgradeResult && (
187 <div className="upgrade-success">
188 <p>恭喜!您已成功升级到等级 {upgradeResult.new_level}!</p>
189 </div>
190 )}
Krishyadbfadaa2025-06-09 20:33:15 +0800191
Krishya34493be2025-06-09 22:45:46 +0800192 {error && (
193 <div className="upgrade-error">
194 <p>{error}</p>
195 </div>
196 )}
Krishya73cd8822025-06-07 15:48:41 +0800197
Krishya34493be2025-06-09 22:45:46 +0800198 <div className="level-actions">
199 {/* <button onClick={() => updateExperience('check-in', 15)} disabled={hasCheckedIn}>
200 {hasCheckedIn ? '今日已签到' : '每日签到 (+15经验)'}
201 </button> */}
202 {/* <button onClick={() => updateExperience('task', 30)}>完成任务 (+30经验)</button> */}
203 {/* <button onClick={() => updateExperience('upload', 50)}>上传种子 (+20经验)</button> */}
204 {/* <button onClick={checkUpgrade} disabled={isUpgrading}>
205 {isUpgrading ? '升级中...' : '检查升级'}
206 </button> */}
207 </div>
208 </div>
209 );
210};
Krishya73cd8822025-06-07 15:48:41 +0800211
Krishya34493be2025-06-09 22:45:46 +0800212export default UserLevelExperience;
Krishya73cd8822025-06-07 15:48:41 +0800213