fix-img
Change-Id: Ida77fc6aed06b28e41e2abcb6ae09d5f63d016f2
diff --git a/src/pages/UserCenter/UserProfile.css b/src/pages/UserCenter/UserProfile.css
index b63c408..d2f848b 100644
--- a/src/pages/UserCenter/UserProfile.css
+++ b/src/pages/UserCenter/UserProfile.css
@@ -55,4 +55,25 @@
padding: 10% 20%;
margin-left: 5%;
margin-right: 5%;
-}
\ No newline at end of file
+}
+.avatar-wrapper {
+ position: relative;
+ display: inline-block;
+}
+
+.avatar-upload-label {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ background: #3498db;
+ color: white;
+ padding: 4px 8px;
+ font-size: 12px;
+ cursor: pointer;
+ border-radius: 4px;
+ opacity: 0.85;
+}
+
+.avatar-upload-label:hover {
+ opacity: 1;
+}
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index a10657c..fb472c8 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -3,17 +3,17 @@
import './UserProfile.css';
import UserNav from './UserNav';
import Header from '../../components/Header';
-import { useUser } from '../../context/UserContext';
+import { useUser } from '../../context/UserContext';
+
+const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
const UserProfile = () => {
- const { user, loading } = useUser(); // 从上下文拿用户和加载状态
+ const { user, loading } = useUser();
const [userProfile, setUserProfile] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
- if (loading) return; // 用户信息还没加载完,先不请求
- console.log('用户:', user, '加载:', loading);
-
+ if (loading) return;
if (!user || !user.userId) {
setError('未登录或用户信息缺失');
setUserProfile(null);
@@ -23,11 +23,7 @@
const fetchUserProfile = async () => {
try {
setError(null);
- const response = await axios.get(`/echo/user/${user.userId}/getProfile`);
-
- console.log('响应数据:', response); // 调试用
- const raw = response.data;
- console.log('raw:', raw); // 调试用
+ const { data: raw } = await axios.get(`/echo/user/${user.userId}/getProfile`);
if (!raw) {
setError('用户数据为空');
@@ -36,7 +32,9 @@
}
const profile = {
- avatar_url: raw.avatarUrl || 'https://example.com/default-avatar.jpg',
+ avatarUrl: raw.avatarUrl
+ ? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
+ : DEFAULT_AVATAR_URL,
nickname: raw.username || '未知用户',
email: raw.email || '未填写',
gender: raw.gender || '保密',
@@ -44,19 +42,15 @@
interests: raw.hobbies ? raw.hobbies.split(',') : [],
level: raw.level || '未知',
experience: raw.experience ?? 0,
- upload_amount: raw.uploadCount ?? 0,
- download_amount: raw.downloadCount ?? 0,
- share_rate: raw.shareRate ?? 0,
- joined_date: raw.registrationTime,
+ uploadAmount: raw.uploadCount ?? 0,
+ downloadAmount: raw.downloadCount ?? 0,
+ shareRate: raw.shareRate ?? 0,
+ joinedDate: raw.registrationTime,
};
setUserProfile(profile);
} catch (err) {
- if (err.response?.status === 404) {
- setError('用户不存在');
- } else {
- setError('请求失败,请稍后再试');
- }
+ setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
setUserProfile(null);
}
};
@@ -64,10 +58,54 @@
fetchUserProfile();
}, [user, loading]);
+ const handleAvatarUpload = async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ 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' } }
+ );
+
+ if (data?.avatarUrl) {
+ setUserProfile((prev) => ({
+ ...prev,
+ avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
+ }));
+ alert('头像上传成功');
+ } else {
+ alert('头像上传成功,但未返回新头像地址');
+ }
+ } catch (err) {
+ console.error('上传失败:', err);
+ alert('头像上传失败,请重试');
+ }
+ };
+
if (loading) return <p>正在加载用户信息...</p>;
if (error) return <p className="error">{error}</p>;
if (!userProfile) return null;
+ const {
+ avatarUrl,
+ nickname,
+ email,
+ gender,
+ bio,
+ interests,
+ level,
+ experience,
+ uploadAmount,
+ downloadAmount,
+ shareRate,
+ joinedDate,
+ } = userProfile;
+
return (
<div className="user-profile-container">
<Header />
@@ -78,25 +116,33 @@
<div className="common-card">
<div className="right-content">
<div className="profile-header">
- <img
- src={userProfile.avatar_url}
- alt={userProfile.nickname}
- className="avatar"
- />
- <h1>{userProfile.nickname}</h1>
+ <div className="avatar-wrapper">
+ <img src={avatarUrl} alt={nickname} className="avatar" />
+ <label htmlFor="avatar-upload" className="avatar-upload-label">
+ 上传头像
+ </label>
+ <input
+ type="file"
+ id="avatar-upload"
+ accept="image/*"
+ style={{ display: 'none' }}
+ onChange={handleAvatarUpload}
+ />
+ </div>
+ <h1>{nickname}</h1>
</div>
<div className="profile-details">
- <p><strong>邮箱:</strong>{userProfile.email}</p>
- <p><strong>性别:</strong>{userProfile.gender}</p>
- <p><strong>个人简介:</strong>{userProfile.bio}</p>
- <p><strong>兴趣:</strong>{userProfile.interests.length > 0 ? userProfile.interests.join(', ') : '无'}</p>
- <p><strong>等级:</strong>{userProfile.level}</p>
- <p><strong>经验:</strong>{userProfile.experience}</p>
- <p><strong>上传量:</strong>{userProfile.upload_amount}</p>
- <p><strong>下载量:</strong>{userProfile.download_amount}</p>
- <p><strong>分享率:</strong>{(userProfile.share_rate * 100).toFixed(2)}%</p>
- <p><strong>加入时间:</strong>{new Date(userProfile.joined_date).toLocaleDateString()}</p>
+ <p><strong>邮箱:</strong>{email}</p>
+ <p><strong>性别:</strong>{gender}</p>
+ <p><strong>个人简介:</strong>{bio}</p>
+ <p><strong>兴趣:</strong>{interests.length > 0 ? interests.join(', ') : '无'}</p>
+ <p><strong>等级:</strong>{level}</p>
+ <p><strong>经验:</strong>{experience}</p>
+ <p><strong>上传量:</strong>{uploadAmount}</p>
+ <p><strong>下载量:</strong>{downloadAmount}</p>
+ <p><strong>分享率:</strong>{(shareRate * 100).toFixed(2)}%</p>
+ <p><strong>加入时间:</strong>{new Date(joinedDate).toLocaleDateString()}</p>
</div>
</div>
</div>