blob: 7a1f726ed7ca9349f18a6985c2528a016a44f5e6 [file] [log] [blame]
Krishya73cd8822025-06-07 15:48:41 +08001import React, { useEffect, useState } from 'react';
2import axios from 'axios';
3import { useUser } from '../../context/UserContext';
4import { useLocation } from 'wouter';
5
6const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
7
8const UserProfileBase = ({ onLoadExperienceInfo }) => {
9 const { user, loading, logout } = useUser();
10 const [userProfile, setUserProfile] = useState(null);
11 const [error, setError] = useState(null);
12
13 // 修改密码状态
14 const [showPwdModal, setShowPwdModal] = useState(false);
15 const [oldPassword, setOldPassword] = useState('');
16 const [newPassword, setNewPassword] = useState('');
17 const [confirmPassword, setConfirmPassword] = useState('');
18
19 // 退出登录
20 const [, setLocation] = useLocation();
21
22 useEffect(() => {
23 if (loading) return;
24 if (!user || !user.userId) {
25 setError('未登录或用户信息缺失');
26 setUserProfile(null);
27 return;
28 }
29
30 const fetchUserProfile = async () => {
31 try {
32 setError(null);
33 const { data: raw } = await axios.get(`/echo/user/${user.userId}/getProfile`);
34 if (!raw) {
35 setError('用户数据为空');
36 setUserProfile(null);
37 return;
38 }
39
40 const profile = {
41 avatarUrl: raw.avatarUrl
42 ? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
43 : DEFAULT_AVATAR_URL,
44 nickname: raw.username || '未知用户',
45 email: raw.email || '未填写',
46 gender: raw.gender || '保密',
47 bio: raw.description || '无',
48 interests: raw.hobbies ? raw.hobbies.split(',') : [],
49 level: raw.level || '未知',
50 experience: raw.experience ?? 0,
51 uploadAmount: raw.uploadCount ?? 0,
52 downloadAmount: raw.downloadCount ?? 0,
53 shareRate: raw.shareRate ?? 0,
54 joinedDate: raw.registrationTime,
55 };
56
57 setUserProfile(profile);
58 // 加载经验信息
59 if (onLoadExperienceInfo) onLoadExperienceInfo(user.userId);
60 } catch (err) {
61 setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
62 setUserProfile(null);
63 }
64 };
65
66 fetchUserProfile();
67 }, [user, loading, onLoadExperienceInfo]);
68
69 const handleAvatarUpload = async (e) => {
70 const file = e.target.files[0];
71 if (!file) return;
72
73 const formData = new FormData();
74 formData.append('file', file);
75
76 try {
77 const { data } = await axios.post(
78 `/echo/user/${user.userId}/uploadAvatar`,
79 formData,
80 { headers: { 'Content-Type': 'multipart/form-data' } }
81 );
82
83 if (data?.avatarUrl) {
84 setUserProfile((prev) => ({
85 ...prev,
86 avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
87 }));
88 alert('头像上传成功');
89 } else {
90 alert('头像上传成功,但未返回新头像地址');
91 }
92 } catch (err) {
93 console.error('上传失败:', err);
94 alert('头像上传失败,请重试');
95 }
96 };
97
98 const handleLogout = () => {
99 logout();
100 setLocation('/auth'); // 退出后跳转登录页
101 };
102
103 const handleChangePassword = async () => {
104 if (!oldPassword || !newPassword || !confirmPassword) {
105 alert('请填写所有字段');
106 return;
107 }
108 if (newPassword !== confirmPassword) {
109 alert('两次输入的新密码不一致');
110 return;
111 }
112
113 try {
114 await axios.post('/echo/user/password', {
115 user_id: user.userId,
116 old_password: oldPassword,
117 new_password: newPassword,
118 confirm_password: confirmPassword,
119 });
120 alert('密码修改成功,请重新登录');
121 logout();
122 window.location.reload();
123 } catch (err) {
124 alert(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
125 }
126 };
127
128 if (loading) return <p>正在加载用户信息...</p>;
129 if (error) return <p className="error">{error}</p>;
130 if (!userProfile) return null;
131
132 const {
133 avatarUrl,
134 nickname,
135 email,
136 gender,
137 bio,
138 interests,
139 level,
140 experience,
141 uploadAmount,
142 downloadAmount,
143 shareRate,
144 joinedDate,
145 } = userProfile;
146
147 return (
148 <div className="common-card">
149 <div className="right-content">
150 <div className="profile-header">
151 <div className="avatar-wrapper">
152 <img src={avatarUrl} alt={nickname} className="avatar" />
153 <label htmlFor="avatar-upload" className="avatar-upload-label">
154 上传头像
155 </label>
156 <input
157 type="file"
158 id="avatar-upload"
159 accept="image/*"
160 style={{ display: 'none' }}
161 onChange={handleAvatarUpload}
162 />
163 </div>
164 <h1>{nickname}</h1>
165 </div>
166
167 <div className="profile-details">
168 <p><strong>邮箱:</strong>{email}</p>
169 <p><strong>性别:</strong>{gender}</p>
170 <p><strong>个人简介:</strong>{bio}</p>
171 <p><strong>兴趣:</strong>{interests.length > 0 ? interests.join(', ') : '无'}</p>
172 {/* <p><strong>等级:</strong>{level}</p>
173 <p><strong>经验:</strong>{experience}</p> */}
174 <p><strong>上传量:</strong>{uploadAmount}</p>
175 <p><strong>下载量:</strong>{downloadAmount}</p>
176 <p><strong>分享率:</strong>{(shareRate * 100).toFixed(2)}%</p>
177 <p><strong>加入时间:</strong>{new Date(joinedDate).toLocaleDateString()}</p>
178
179 {/* 修改密码与退出登录按钮 */}
180 <div className="profile-actions">
181 <button onClick={() => setShowPwdModal(true)}>修改密码</button>
182 <button onClick={handleLogout}>退出登录</button>
183 </div>
184
185 {/* 修改密码弹窗 */}
186 {showPwdModal && (
187 <div className="modal">
188 <div className="modal-content">
189 <h3>修改密码</h3>
190 <input
191 type="password"
192 placeholder="原密码"
193 value={oldPassword}
194 onChange={(e) => setOldPassword(e.target.value)}
195 />
196 <input
197 type="password"
198 placeholder="新密码"
199 value={newPassword}
200 onChange={(e) => setNewPassword(e.target.value)}
201 />
202 <input
203 type="password"
204 placeholder="确认新密码"
205 value={confirmPassword}
206 onChange={(e) => setConfirmPassword(e.target.value)}
207 />
208 <div className="modal-buttons">
209 <button onClick={handleChangePassword}>确认修改</button>
210 <button onClick={() => setShowPwdModal(false)}>取消</button>
211 </div>
212 </div>
213 </div>
214 )}
215 </div>
216 </div>
217 </div>
218 );
219};
220
221export default UserProfileBase;