blob: fb472c8480c08c102d41b720a75e74a06feb6f6e [file] [log] [blame]
2230100977253112025-04-15 21:30:39 +08001import React, { useEffect, useState } from 'react';
2import axios from 'axios';
3import './UserProfile.css';
2230100937ebec12025-06-03 21:17:04 +08004import UserNav from './UserNav';
Krishyac0f7e9b2025-04-22 15:28:28 +08005import Header from '../../components/Header';
22301009df48f962025-06-05 13:40:44 +08006import { useUser } from '../../context/UserContext';
7
8const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
2230100977253112025-04-15 21:30:39 +08009
223010097ff51f22025-04-15 21:35:28 +080010const UserProfile = () => {
22301009df48f962025-06-05 13:40:44 +080011 const { user, loading } = useUser();
2230100977253112025-04-15 21:30:39 +080012 const [userProfile, setUserProfile] = useState(null);
2230100977253112025-04-15 21:30:39 +080013 const [error, setError] = useState(null);
14
2230100977253112025-04-15 21:30:39 +080015 useEffect(() => {
22301009df48f962025-06-05 13:40:44 +080016 if (loading) return;
2230100937ebec12025-06-03 21:17:04 +080017 if (!user || !user.userId) {
18 setError('未登录或用户信息缺失');
19 setUserProfile(null);
20 return;
21 }
22
2230100977253112025-04-15 21:30:39 +080023 const fetchUserProfile = async () => {
24 try {
2230100937ebec12025-06-03 21:17:04 +080025 setError(null);
22301009df48f962025-06-05 13:40:44 +080026 const { data: raw } = await axios.get(`/echo/user/${user.userId}/getProfile`);
2230100937ebec12025-06-03 21:17:04 +080027
28 if (!raw) {
29 setError('用户数据为空');
30 setUserProfile(null);
31 return;
2230100977253112025-04-15 21:30:39 +080032 }
2230100937ebec12025-06-03 21:17:04 +080033
34 const profile = {
22301009df48f962025-06-05 13:40:44 +080035 avatarUrl: raw.avatarUrl
36 ? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
37 : DEFAULT_AVATAR_URL,
2230100937ebec12025-06-03 21:17:04 +080038 nickname: raw.username || '未知用户',
39 email: raw.email || '未填写',
40 gender: raw.gender || '保密',
41 bio: raw.description || '无',
42 interests: raw.hobbies ? raw.hobbies.split(',') : [],
43 level: raw.level || '未知',
44 experience: raw.experience ?? 0,
22301009df48f962025-06-05 13:40:44 +080045 uploadAmount: raw.uploadCount ?? 0,
46 downloadAmount: raw.downloadCount ?? 0,
47 shareRate: raw.shareRate ?? 0,
48 joinedDate: raw.registrationTime,
2230100937ebec12025-06-03 21:17:04 +080049 };
50
51 setUserProfile(profile);
2230100977253112025-04-15 21:30:39 +080052 } catch (err) {
22301009df48f962025-06-05 13:40:44 +080053 setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
2230100937ebec12025-06-03 21:17:04 +080054 setUserProfile(null);
2230100977253112025-04-15 21:30:39 +080055 }
56 };
57
58 fetchUserProfile();
2230100937ebec12025-06-03 21:17:04 +080059 }, [user, loading]);
2230100977253112025-04-15 21:30:39 +080060
22301009df48f962025-06-05 13:40:44 +080061 const handleAvatarUpload = async (e) => {
62 const file = e.target.files[0];
63 if (!file) return;
64
65 const formData = new FormData();
66 formData.append('file', file);
67
68 try {
69 const { data } = await axios.post(
70 `/echo/user/${user.userId}/uploadAvatar`,
71 formData,
72 { headers: { 'Content-Type': 'multipart/form-data' } }
73 );
74
75 if (data?.avatarUrl) {
76 setUserProfile((prev) => ({
77 ...prev,
78 avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
79 }));
80 alert('头像上传成功');
81 } else {
82 alert('头像上传成功,但未返回新头像地址');
83 }
84 } catch (err) {
85 console.error('上传失败:', err);
86 alert('头像上传失败,请重试');
87 }
88 };
89
2230100937ebec12025-06-03 21:17:04 +080090 if (loading) return <p>正在加载用户信息...</p>;
91 if (error) return <p className="error">{error}</p>;
92 if (!userProfile) return null;
2230100977253112025-04-15 21:30:39 +080093
22301009df48f962025-06-05 13:40:44 +080094 const {
95 avatarUrl,
96 nickname,
97 email,
98 gender,
99 bio,
100 interests,
101 level,
102 experience,
103 uploadAmount,
104 downloadAmount,
105 shareRate,
106 joinedDate,
107 } = userProfile;
108
2230100977253112025-04-15 21:30:39 +0800109 return (
Krishyac0f7e9b2025-04-22 15:28:28 +0800110 <div className="user-profile-container">
2230100937ebec12025-06-03 21:17:04 +0800111 <Header />
112 <div className="user-center">
113 <div className="user-nav-container">
114 <UserNav />
Krishyac0f7e9b2025-04-22 15:28:28 +0800115 </div>
2230100937ebec12025-06-03 21:17:04 +0800116 <div className="common-card">
117 <div className="right-content">
118 <div className="profile-header">
22301009df48f962025-06-05 13:40:44 +0800119 <div className="avatar-wrapper">
120 <img src={avatarUrl} alt={nickname} className="avatar" />
121 <label htmlFor="avatar-upload" className="avatar-upload-label">
122 上传头像
123 </label>
124 <input
125 type="file"
126 id="avatar-upload"
127 accept="image/*"
128 style={{ display: 'none' }}
129 onChange={handleAvatarUpload}
130 />
131 </div>
132 <h1>{nickname}</h1>
2230100937ebec12025-06-03 21:17:04 +0800133 </div>
2230100977253112025-04-15 21:30:39 +0800134
2230100937ebec12025-06-03 21:17:04 +0800135 <div className="profile-details">
22301009df48f962025-06-05 13:40:44 +0800136 <p><strong>邮箱:</strong>{email}</p>
137 <p><strong>性别:</strong>{gender}</p>
138 <p><strong>个人简介:</strong>{bio}</p>
139 <p><strong>兴趣:</strong>{interests.length > 0 ? interests.join(', ') : '无'}</p>
140 <p><strong>等级:</strong>{level}</p>
141 <p><strong>经验:</strong>{experience}</p>
142 <p><strong>上传量:</strong>{uploadAmount}</p>
143 <p><strong>下载量:</strong>{downloadAmount}</p>
144 <p><strong>分享率:</strong>{(shareRate * 100).toFixed(2)}%</p>
145 <p><strong>加入时间:</strong>{new Date(joinedDate).toLocaleDateString()}</p>
2230100937ebec12025-06-03 21:17:04 +0800146 </div>
147 </div>
Krishyac0f7e9b2025-04-22 15:28:28 +0800148 </div>
149 </div>
2230100977253112025-04-15 21:30:39 +0800150 </div>
151 );
152};
153
2230100937ebec12025-06-03 21:17:04 +0800154export default UserProfile;