blob: c90cb631f5513e1b873886d0cabc2494b42eaa85 [file] [log] [blame]
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { useUser } from '../../context/UserContext';
import { useLocation } from 'wouter';
import toast from 'react-hot-toast';
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
const UserProfileBase = ({ onLoadExperienceInfo }) => {
const { user, loading, logout ,saveUser} = useUser();
const [userProfile, setUserProfile] = useState(null);
const [error, setError] = useState(null);
const [showPwdModal, setShowPwdModal] = useState(false);
const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [, setLocation] = useLocation();
useEffect(() => {
if (loading) return;
if (!user || !user.userId) {
setError('未登录或用户信息缺失');
setUserProfile(null);
return;
}
const fetchUserProfile = async () => {
try {
setError(null);
const { data: raw } = await axios.get(`/echo/user/${user.userId}/getProfile`);
if (!raw) {
setError('用户数据为空');
setUserProfile(null);
return;
}
const profile = {
avatarUrl: raw.avatarUrl
? `${process.env.REACT_APP_AVATAR_BASE_URL}${raw.avatarUrl}`
: DEFAULT_AVATAR_URL,
nickname: raw.username || '未知用户',
email: raw.email || '未填写',
gender: raw.gender || '保密',
bio: raw.description || '无',
interests: raw.hobbies ? raw.hobbies.split(',') : [],
level: raw.level || '未知',
experience: raw.experience ?? 0,
uploadAmount: raw.uploadCount ?? 0,
downloadAmount: raw.downloadCount ?? 0,
shareRate: raw.shareRate ?? 0,
joinedDate: raw.registrationTime,
};
setUserProfile(profile);
if (onLoadExperienceInfo) onLoadExperienceInfo(user.userId);
} catch (err) {
setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
setUserProfile(null);
}
};
fetchUserProfile();
}, [user, loading, onLoadExperienceInfo]);
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) {
// 加时间戳避免缓存
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();
setLocation('/auth');
};
const confirmPasswordChange = () => {
confirmAlert({
title: '确认修改密码',
message: '确定要修改密码吗?修改成功后将自动登出。',
buttons: [
{
label: '确认',
onClick: handleChangePassword,
},
{
label: '取消',
},
],
});
};
const handleChangePassword = async () => {
if (!oldPassword || !newPassword || !confirmPassword) {
toast.error('请填写所有字段');
return;
}
if (newPassword !== confirmPassword) {
toast.error('两次输入的新密码不一致');
return;
}
try {
await axios.post('/echo/user/password', {
user_id: user.userId,
old_password: oldPassword,
new_password: newPassword,
confirm_password: confirmPassword,
});
toast.success('密码修改成功,请重新登录');
logout();
setTimeout(() => {
window.location.reload();
}, 1500);
} catch (err) {
toast.error(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
}
};
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="common-card">
<div className="right-content">
<div className="profile-header">
<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>{email}</p>
<p><strong>性别:</strong>{gender}</p>
<p><strong>个人简介:</strong>{bio}</p>
<p><strong>兴趣:</strong>{interests.length > 0 ? interests.join(', ') : '无'}</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 className="profile-actions">
<button onClick={() => setShowPwdModal(true)}>修改密码</button>
<button onClick={handleLogout}>退出登录</button>
</div>
{showPwdModal && (
<div className="user-modal">
<div className="user-modal-content">
<h3>修改密码</h3>
<input
type="password"
placeholder="原密码"
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
<input
type="password"
placeholder="新密码"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
<input
type="password"
placeholder="确认新密码"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<div className="user-modal-buttons">
<button onClick={confirmPasswordChange}>确认修改</button>
<button onClick={() => setShowPwdModal(false)}>取消</button>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
};
export default UserProfileBase;