修改提示框样式、完成付费片单、推荐跳转
Change-Id: Ie84c53d4e306435144b1f26ceb39cc182e99d57a
diff --git a/src/pages/UserCenter/UserNav.css b/src/pages/UserCenter/UserNav.css
index 6a1b405..baf3cb9 100644
--- a/src/pages/UserCenter/UserNav.css
+++ b/src/pages/UserCenter/UserNav.css
@@ -35,11 +35,11 @@
/* 鼠标悬浮时的背景颜色 */
.user-nav-item:hover {
- background-color: #5a1414;
+ background-color: #fffaf7;
}
/* 激活项的样式 */
.user-nav-item.active {
- background-color: #BA929A;
+ background-color: #eecfc1;
color: white;
}
\ No newline at end of file
diff --git a/src/pages/UserCenter/UserProfile.css b/src/pages/UserCenter/UserProfile.css
index edc04e1..18f793d 100644
--- a/src/pages/UserCenter/UserProfile.css
+++ b/src/pages/UserCenter/UserProfile.css
@@ -4,7 +4,7 @@
font-family: Arial, sans-serif;
display: flex;
gap: 10%;
- background: #333;
+ background: #f8f3ef;
}
.right-content {
@@ -47,7 +47,7 @@
}
.common-card {
- background-color: #e9ded2;
+ background-color: #fffaf7;
border-radius: 16px;
margin: 0 auto;
margin-top: 40px;
@@ -72,7 +72,7 @@
position: absolute;
bottom: 0;
right: 0;
- background: #3498db;
+ background: #4b3325;
color: white;
padding: 4px 8px;
font-size: 12px;
@@ -135,7 +135,7 @@
position: absolute;
bottom: 0;
right: 0;
- background: #3498db;
+ background: #4b3325;
color: white;
padding: 4px 8px;
font-size: 12px;
@@ -201,7 +201,7 @@
padding: 6px 12px;
border: none;
border-radius: 6px;
- background-color: #4a90e2;
+ background-color: #4b3325;
color: white;
cursor: pointer;
}
@@ -245,14 +245,14 @@
.profile-actions button {
padding: 8px 16px;
- background-color: #4677f5;
+ background-color: #4b3325;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
}
-.modal {
+.user-modal {
position: fixed;
top: 0;
left: 0;
@@ -265,21 +265,21 @@
z-index: 99;
}
-.modal-content {
+.user-modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
width: 300px;
}
-.modal-content input {
+.user-modal-content input {
display: block;
width: 100%;
margin: 10px 0;
padding: 8px;
}
-.modal-buttons {
+.user-modal-buttons {
display: flex;
justify-content: space-between;
}
diff --git a/src/pages/UserCenter/UserProfile.jsx b/src/pages/UserCenter/UserProfile.jsx
index cf52d99..bcfbf0b 100644
--- a/src/pages/UserCenter/UserProfile.jsx
+++ b/src/pages/UserCenter/UserProfile.jsx
@@ -1,266 +1,3 @@
-// import React, { useEffect, useState } from 'react';
-// import axios from 'axios';
-// import './UserProfile.css';
-// import { useUser } from '../../context/UserContext';
-// import { useLocation } from 'wouter';
-
-// const DEFAULT_AVATAR_URL = `${process.env.PUBLIC_URL}/default-avatar.png`;
-
-// const UserProfile = () => {
-// const { user, loading, logout } = useUser();
-// const [userProfile, setUserProfile] = useState(null);
-// const [experienceInfo, setExperienceInfo] = 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);
-// } catch (err) {
-// setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
-// setUserProfile(null);
-// }
-// };
-
-// const fetchExperienceInfo = async () => {
-// try {
-// const { data } = await axios.get('/echo/level/getExperience', {
-// params: { user_id: user.userId },
-// });
-// setExperienceInfo(data);
-// } catch (err) {
-// console.error('经验信息获取失败:', err);
-// }
-// };
-
-// fetchUserProfile();
-// fetchExperienceInfo();
-// }, [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('头像上传失败,请重试');
-// }
-// };
-
-// const handleLogout = () => {
-// logout();
-// setLocation('/auth'); // 退出后跳转登录页
-// // window.location.reload(); // 或跳转登录页
-// };
-
-// const handleChangePassword = async () => {
-// if (!oldPassword || !newPassword || !confirmPassword) {
-// alert('请填写所有字段');
-// return;
-// }
-// if (newPassword !== confirmPassword) {
-// alert('两次输入的新密码不一致');
-// return;
-// }
-
-// try {
-// // await axios.post('/echo/user/password', {
-// // user_id: user.userId,
-// // oldPassword,
-// // newPassword,
-// // });
-// await axios.post('/echo/user/password', {
-// user_id: user.userId,
-// old_password: oldPassword,
-// new_password: newPassword,
-// confirm_password: confirmPassword,
-// });
-// alert('密码修改成功,请重新登录');
-// logout();
-// window.location.reload();
-// } catch (err) {
-// alert(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;
-
-// const progressPercent = experienceInfo
-// ? Math.min(
-// 100,
-// ((experienceInfo.current_experience || 0) /
-// (experienceInfo.next_level_experience || 1)) *
-// 100
-// ).toFixed(2)
-// : 0;
-
-// const expToNextLevel = experienceInfo
-// ? (experienceInfo.next_level_experience - experienceInfo.current_experience)
-// : null;
-
-// 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>{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>
-
-// {experienceInfo && (
-// <>
-// <p><strong>距离下一等级还需:</strong>{expToNextLevel} 经验值</p>
-// <div className="exp-bar-wrapper">
-// <div className="exp-bar" style={{ width: `${progressPercent}%` }} />
-// </div>
-// <p className="exp-progress-text">{progressPercent}%</p>
-// </>
-// )}
-
-// {/* 修改密码与退出登录按钮 */}
-// <div className="profile-actions">
-// <button onClick={() => setShowPwdModal(true)}>修改密码</button>
-// <button onClick={handleLogout}>退出登录</button>
-// </div>
-
-// {/* 修改密码弹窗 */}
-// {showPwdModal && (
-// <div className="modal">
-// <div className="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="modal-buttons">
-// <button onClick={handleChangePassword}>确认修改</button>
-// <button onClick={() => setShowPwdModal(false)}>取消</button>
-// </div>
-// </div>
-// </div>
-// )}
-// </div>
-// </div>
-// </div>
-// );
-// };
-
-// export default UserProfile;
-
import React from 'react';
import UserProfileBase from './UserProfileBase';
import UserLevelExperience from './UserLevelExperience';
diff --git a/src/pages/UserCenter/UserProfileBase.jsx b/src/pages/UserCenter/UserProfileBase.jsx
index 7a1f726..791baca 100644
--- a/src/pages/UserCenter/UserProfileBase.jsx
+++ b/src/pages/UserCenter/UserProfileBase.jsx
@@ -2,6 +2,9 @@
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`;
@@ -10,13 +13,11 @@
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(() => {
@@ -55,7 +56,6 @@
};
setUserProfile(profile);
- // 加载经验信息
if (onLoadExperienceInfo) onLoadExperienceInfo(user.userId);
} catch (err) {
setError(err.response?.status === 404 ? '用户不存在' : '请求失败,请稍后再试');
@@ -85,28 +85,44 @@
...prev,
avatarUrl: `${process.env.REACT_APP_AVATAR_BASE_URL}${data.avatarUrl}`,
}));
- alert('头像上传成功');
+ toast.success('头像上传成功');
} else {
- alert('头像上传成功,但未返回新头像地址');
+ toast.success('头像上传成功,但未返回新头像地址');
}
} catch (err) {
console.error('上传失败:', err);
- alert('头像上传失败,请重试');
+ toast.error('头像上传失败,请重试');
}
};
const handleLogout = () => {
logout();
- setLocation('/auth'); // 退出后跳转登录页
+ setLocation('/auth');
+ };
+
+ const confirmPasswordChange = () => {
+ confirmAlert({
+ title: '确认修改密码',
+ message: '确定要修改密码吗?修改成功后将自动登出。',
+ buttons: [
+ {
+ label: '确认',
+ onClick: handleChangePassword,
+ },
+ {
+ label: '取消',
+ },
+ ],
+ });
};
const handleChangePassword = async () => {
if (!oldPassword || !newPassword || !confirmPassword) {
- alert('请填写所有字段');
+ toast.error('请填写所有字段');
return;
}
if (newPassword !== confirmPassword) {
- alert('两次输入的新密码不一致');
+ toast.error('两次输入的新密码不一致');
return;
}
@@ -117,11 +133,14 @@
new_password: newPassword,
confirm_password: confirmPassword,
});
- alert('密码修改成功,请重新登录');
+
+ toast.success('密码修改成功,请重新登录');
logout();
- window.location.reload();
+ setTimeout(() => {
+ window.location.reload();
+ }, 1500);
} catch (err) {
- alert(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
+ toast.error(err.response?.data?.message || '密码修改失败,请检查原密码是否正确');
}
};
@@ -169,23 +188,19 @@
<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 className="profile-actions">
<button onClick={() => setShowPwdModal(true)}>修改密码</button>
<button onClick={handleLogout}>退出登录</button>
</div>
- {/* 修改密码弹窗 */}
{showPwdModal && (
- <div className="modal">
- <div className="modal-content">
+ <div className="user-modal">
+ <div className="user-modal-content">
<h3>修改密码</h3>
<input
type="password"
@@ -205,8 +220,8 @@
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
- <div className="modal-buttons">
- <button onClick={handleChangePassword}>确认修改</button>
+ <div className="user-modal-buttons">
+ <button onClick={confirmPasswordChange}>确认修改</button>
<button onClick={() => setShowPwdModal(false)}>取消</button>
</div>
</div>
@@ -218,4 +233,4 @@
);
};
-export default UserProfileBase;
\ No newline at end of file
+export default UserProfileBase;
diff --git a/src/pages/UserCenter/UserRecharge.jsx b/src/pages/UserCenter/UserRecharge.jsx
index 7a2f9b3..b1dff24 100644
--- a/src/pages/UserCenter/UserRecharge.jsx
+++ b/src/pages/UserCenter/UserRecharge.jsx
@@ -1,42 +1,183 @@
-// export default Recharge;
+// // export default Recharge;
+// import React, { useState } from 'react';
+// import axios from 'axios';
+// import { useUser } from '../../context/UserContext';
+// import toast from 'react-hot-toast';
+// import { confirmAlert } from 'react-confirm-alert';
+// import 'react-confirm-alert/src/react-confirm-alert.css';
+
+// const UserRecharge = () => {
+// const [amount, setAmount] = useState('');
+// const [loading, setLoading] = useState(false);
+// const [message, setMessage] = useState('');
+// const [showPayDialog, setShowPayDialog] = useState(false);
+// const [payProcessing, setPayProcessing] = useState(false);
+
+// const { user } = useUser();
+// const userId = user?.userId;
+
+// // 点击提交充值,打开模拟支付弹窗
+// const handleSubmit = () => {
+// if (!userId) {
+// setMessage('未登录,无法充值');
+// return;
+// }
+// if (!amount || isNaN(amount) || Number(amount) <= 0) {
+// setMessage('请输入有效的充值金额');
+// return;
+// }
+// setMessage('');
+// setShowPayDialog(true);
+// };
+
+// // 模拟支付弹窗点击“确认支付”
+// const handlePayConfirm = async () => {
+// setPayProcessing(true);
+// setMessage('');
+
+// // 模拟支付等待 2 秒
+// setTimeout(async () => {
+// try {
+// // 支付成功后调用后端充值接口
+// const response = await axios.post('/echo/user/recharge', null, {
+// params: {
+// userId,
+// amount: Number(amount),
+// },
+// });
+
+// setMessage(response.data.message || '充值成功!');
+// setAmount('');
+// } catch (error) {
+// setMessage(error.response?.data?.message || '充值失败,请重试');
+// } finally {
+// setPayProcessing(false);
+// setShowPayDialog(false);
+// }
+// }, 2000);
+// };
+
+// // 取消支付弹窗
+// const handlePayCancel = () => {
+// setShowPayDialog(false);
+// setMessage('支付已取消');
+// };
+
+// return (
+// <div className="recharge-page" style={{ maxWidth: 400, margin: 'auto', padding: 20 }}>
+// <h2>充值服务</h2>
+// <div style={{ marginBottom: 12 }}>
+// <label>
+// 充值金额:
+// <input
+// type="number"
+// min="1"
+// value={amount}
+// onChange={(e) => setAmount(e.target.value)}
+// disabled={loading || payProcessing}
+// style={{ marginLeft: 8, width: 120 }}
+// />
+// </label>
+// </div>
+// <button onClick={handleSubmit} disabled={loading || payProcessing}>
+// {loading || payProcessing ? '处理中...' : '提交充值'}
+// </button>
+
+// {message && <p style={{ marginTop: 12 }}>{message}</p>}
+
+// {/* 模拟支付弹窗 */}
+// {showPayDialog && (
+// <div
+// style={{
+// position: 'fixed',
+// top: 0, left: 0, right: 0, bottom: 0,
+// backgroundColor: 'rgba(0,0,0,0.5)',
+// display: 'flex',
+// justifyContent: 'center',
+// alignItems: 'center',
+// zIndex: 1000,
+// }}
+// >
+// <div
+// style={{
+// backgroundColor: '#fff',
+// padding: 20,
+// borderRadius: 8,
+// width: 300,
+// textAlign: 'center',
+// boxShadow: '0 0 10px rgba(0,0,0,0.25)',
+// }}
+// >
+// <h3>模拟支付</h3>
+// <p>支付金额:{amount} 元</p>
+
+// {payProcessing ? (
+// <p>支付处理中,请稍候...</p>
+// ) : (
+// <>
+// <button onClick={handlePayConfirm} style={{ marginRight: 10 }}>
+// 确认支付
+// </button>
+// <button onClick={handlePayCancel}>取消</button>
+// </>
+// )}
+// </div>
+// </div>
+// )}
+// </div>
+// );
+// };
+
+// export default UserRecharge;
import React, { useState } from 'react';
import axios from 'axios';
import { useUser } from '../../context/UserContext';
+import toast from 'react-hot-toast';
+import { confirmAlert } from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
const UserRecharge = () => {
const [amount, setAmount] = useState('');
- const [loading, setLoading] = useState(false);
- const [message, setMessage] = useState('');
- const [showPayDialog, setShowPayDialog] = useState(false);
const [payProcessing, setPayProcessing] = useState(false);
const { user } = useUser();
const userId = user?.userId;
- // 点击提交充值,打开模拟支付弹窗
+ // 点击提交充值
const handleSubmit = () => {
if (!userId) {
- setMessage('未登录,无法充值');
+ toast.error('未登录,无法充值');
return;
}
if (!amount || isNaN(amount) || Number(amount) <= 0) {
- setMessage('请输入有效的充值金额');
+ toast.error('请输入有效的充值金额');
return;
}
- setMessage('');
- setShowPayDialog(true);
+
+ confirmAlert({
+ title: '确认支付',
+ message: `确认充值 ${amount} 元吗?`,
+ buttons: [
+ {
+ label: '确认支付',
+ onClick: () => handlePayConfirm(),
+ },
+ {
+ label: '取消',
+ onClick: () => toast('已取消充值'),
+ },
+ ],
+ });
};
- // 模拟支付弹窗点击“确认支付”
const handlePayConfirm = async () => {
setPayProcessing(true);
- setMessage('');
- // 模拟支付等待 2 秒
+ toast.loading('正在处理支付...', { id: 'recharge' });
+
setTimeout(async () => {
try {
- // 支付成功后调用后端充值接口
const response = await axios.post('/echo/user/recharge', null, {
params: {
userId,
@@ -44,23 +185,16 @@
},
});
- setMessage(response.data.message || '充值成功!');
+ toast.success(response.data.message || '充值成功!', { id: 'recharge' });
setAmount('');
} catch (error) {
- setMessage(error.response?.data?.message || '充值失败,请重试');
+ toast.error(error.response?.data?.message || '充值失败,请重试', { id: 'recharge' });
} finally {
setPayProcessing(false);
- setShowPayDialog(false);
}
}, 2000);
};
- // 取消支付弹窗
- const handlePayCancel = () => {
- setShowPayDialog(false);
- setMessage('支付已取消');
- };
-
return (
<div className="recharge-page" style={{ maxWidth: 400, margin: 'auto', padding: 20 }}>
<h2>充值服务</h2>
@@ -72,56 +206,14 @@
min="1"
value={amount}
onChange={(e) => setAmount(e.target.value)}
- disabled={loading || payProcessing}
+ disabled={payProcessing}
style={{ marginLeft: 8, width: 120 }}
/>
</label>
</div>
- <button onClick={handleSubmit} disabled={loading || payProcessing}>
- {loading || payProcessing ? '处理中...' : '提交充值'}
+ <button onClick={handleSubmit} disabled={payProcessing}>
+ {payProcessing ? '处理中...' : '提交充值'}
</button>
-
- {message && <p style={{ marginTop: 12 }}>{message}</p>}
-
- {/* 模拟支付弹窗 */}
- {showPayDialog && (
- <div
- style={{
- position: 'fixed',
- top: 0, left: 0, right: 0, bottom: 0,
- backgroundColor: 'rgba(0,0,0,0.5)',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- zIndex: 1000,
- }}
- >
- <div
- style={{
- backgroundColor: '#fff',
- padding: 20,
- borderRadius: 8,
- width: 300,
- textAlign: 'center',
- boxShadow: '0 0 10px rgba(0,0,0,0.25)',
- }}
- >
- <h3>模拟支付</h3>
- <p>支付金额:{amount} 元</p>
-
- {payProcessing ? (
- <p>支付处理中,请稍候...</p>
- ) : (
- <>
- <button onClick={handlePayConfirm} style={{ marginRight: 10 }}>
- 确认支付
- </button>
- <button onClick={handlePayCancel}>取消</button>
- </>
- )}
- </div>
- </div>
- )}
</div>
);
};