Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 1 | // Personal.jsx
|
| 2 | import React, { useState, useEffect } from 'react';
|
| 3 | import { useNavigate, useLocation, Outlet } from 'react-router-dom';
|
| 4 | import { getUserInfo, getDownloadQuota, getDownloadProgress } from '../../api/personal';
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 5 | import './Personal.css';
|
| 6 | import ActionCard from './ActionCard';
|
| 7 |
|
| 8 | const Personal = () => {
|
| 9 | const navigate = useNavigate();
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 10 | const location = useLocation();
|
| 11 | const [userData, setUserData] = useState(null);
|
| 12 | const [loading, setLoading] = useState(true);
|
| 13 | const [error, setError] = useState(null);
|
| 14 | const [downloadProgress, setDownloadProgress] = useState({});
|
| 15 |
|
| 16 | useEffect(() => {
|
| 17 | const fetchData = async () => {
|
| 18 | try {
|
| 19 | // 并行获取用户信息和下载额度
|
| 20 | const [userInfo, downloadQuota] = await Promise.all([
|
| 21 | getUserInfo(),
|
| 22 | getDownloadQuota()
|
| 23 | ]);
|
| 24 |
|
| 25 | setUserData({
|
| 26 | username: userInfo.username,
|
| 27 | avatar: 'https://via.placeholder.com/150',
|
| 28 | joinDate: userInfo.registTime,
|
| 29 | level: userInfo.level, // 可以根据userInfo.level设置不同等级
|
| 30 | points: userInfo.magicPoints,
|
| 31 | upload: userInfo.upload,
|
| 32 | download: userInfo.download,
|
| 33 | ratio: userInfo.shareRate,
|
| 34 | downloadQuota: {
|
| 35 | total: downloadQuota.total,
|
| 36 | used: downloadQuota.used,
|
| 37 | remaining: downloadQuota.remaining
|
| 38 | }
|
| 39 | });
|
| 40 | } catch (err) {
|
| 41 | setError(err.message);
|
| 42 | } finally {
|
| 43 | setLoading(false);
|
| 44 | }
|
| 45 | };
|
| 46 |
|
| 47 | fetchData();
|
| 48 | }, []);
|
| 49 |
|
| 50 | // 获取下载进度数据
|
| 51 | const fetchDownloadProgress = async () => {
|
| 52 | try {
|
| 53 | const progressData = await getDownloadProgress();
|
| 54 | setDownloadProgress(progressData);
|
| 55 | } catch (err) {
|
| 56 | console.error('获取下载进度失败:', err);
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 57 | }
|
| 58 | };
|
| 59 |
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 60 | useEffect(() => {
|
| 61 | // 初始获取下载进度
|
| 62 | fetchDownloadProgress();
|
| 63 |
|
| 64 | // 设置定时器,每10秒获取一次下载进度
|
| 65 | const intervalId = setInterval(fetchDownloadProgress, 10000);
|
| 66 |
|
| 67 | // 组件卸载时清除定时器
|
| 68 | return () => clearInterval(intervalId);
|
| 69 | }, []);
|
| 70 |
|
| 71 |
|
| 72 | if (loading) {
|
| 73 | return <div className="loading">加载中...</div>;
|
| 74 | }
|
| 75 |
|
| 76 | if (error) {
|
| 77 | return <div className="error">错误: {error}</div>;
|
| 78 | }
|
| 79 |
|
| 80 | if (!userData) {
|
| 81 | return <div className="error">未获取到用户数据</div>;
|
| 82 | }
|
| 83 |
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 84 | const features = [
|
| 85 | {
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 86 | title: '兑换区',
|
| 87 | description: '下载量/邀请码',
|
| 88 | path: '/personal/Exchange' // 相对路径
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 89 | },
|
| 90 | {
|
| 91 | title: '上传记录',
|
| 92 | description: '管理上传的资源',
|
| 93 | path: '/personal/Upload'
|
| 94 | },
|
| 95 | {
|
| 96 | title: '消息通知',
|
| 97 | description: '查看系统消息',
|
| 98 | path: '/personal/Notice'
|
| 99 | },
|
| 100 | {
|
| 101 | title: '设置',
|
| 102 | description: '修改个人资料',
|
| 103 | path: '/personal/Setting'
|
| 104 | }
|
| 105 | ];
|
| 106 |
|
| 107 | const handleCardClick = (path) => {
|
| 108 | navigate(path); // 相对导航
|
| 109 | };
|
| 110 |
|
| 111 | const handleBack = () => {
|
| 112 | if (location.state?.fromSubpage) {
|
| 113 | // 如果是从子页面返回,则继续返回到Dashboard
|
| 114 | navigate(`/dashboard/${location.state.dashboardTab || ''}`, {
|
| 115 | replace: true
|
| 116 | });
|
| 117 | } else {
|
| 118 | // 普通返回逻辑
|
| 119 | navigate(-1);
|
| 120 | }
|
| 121 | };
|
| 122 |
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 123 | const formatSize = (bytes) => {
|
| 124 | if (bytes < 1024) return `${bytes} B`;
|
| 125 | const kb = bytes / 1024;
|
| 126 | if (kb < 1024) return `${kb.toFixed(2)} KB`;
|
| 127 | const mb = kb / 1024;
|
| 128 | if (mb < 1024) return `${mb.toFixed(2)} MB`;
|
| 129 | const gb = mb / 1024;
|
| 130 | return `${gb.toFixed(2)} GB`;
|
| 131 | };
|
| 132 |
|
| 133 | // 添加进度条颜色函数
|
| 134 | const getProgressColor = (percentage) => {
|
| 135 | if (percentage < 0.3) return '#4CAF50'; // 绿色
|
| 136 | if (percentage < 0.7) return '#FFC107'; // 黄色
|
| 137 | return '#F44336'; // 红色
|
| 138 | };
|
| 139 |
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 140 | return (
|
| 141 | <div className="personal-container">
|
| 142 | {/* 返回按钮 */}
|
| 143 | <button className="back-button" onClick={handleBack}>
|
DREW | 5b1883e | 2025-06-07 10:41:32 +0800 | [diff] [blame^] | 144 | 返回
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 145 | </button>
|
| 146 |
|
| 147 | {/* 用户基本信息卡片 */}
|
| 148 | <div className="profile-card">
|
| 149 | <div className="profile-header">
|
| 150 | <img
|
| 151 | src={userData.avatar}
|
| 152 | alt={userData.username}
|
| 153 | className="profile-avatar"
|
| 154 | />
|
| 155 | <div className="profile-info">
|
| 156 | <h2 className="username">{userData.username}</h2>
|
| 157 | <div className="user-meta">
|
| 158 | <span>加入时间: {userData.joinDate}</span>
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 159 | <span>会员等级: Lv.{userData.level}</span>
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 160 | </div>
|
| 161 | </div>
|
| 162 | </div>
|
| 163 |
|
| 164 | {/* 用户数据统计 */}
|
| 165 | <div className="stats-grid">
|
| 166 | <div className="stat-item">
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 167 | <div className="stat-label">保种积分</div>
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 168 | <div className="stat-value">{userData.points}</div>
|
| 169 | </div>
|
| 170 | <div className="stat-item">
|
| 171 | <div className="stat-label">上传量</div>
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 172 | <div className="stat-value">{formatSize(userData.upload)}</div>
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 173 | </div>
|
| 174 | <div className="stat-item">
|
| 175 | <div className="stat-label">下载量</div>
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 176 | <div className="stat-value">{formatSize(userData.download)}</div>
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 177 | </div>
|
| 178 | <div className="stat-item">
|
| 179 | <div className="stat-label">分享率</div>
|
| 180 | <div className="stat-value">{userData.ratio}</div>
|
| 181 | </div>
|
| 182 | </div>
|
| 183 | </div>
|
| 184 |
|
| 185 | {/* 下载额度卡片 */}
|
| 186 | <div className="quota-card">
|
| 187 | <h3>下载额度</h3>
|
| 188 | <div className="quota-info">
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 189 | <span className="quota-used">
|
| 190 | {formatSize(userData.downloadQuota.used)} 已使用
|
| 191 | </span>
|
| 192 | <span className="quota-remaining">
|
| 193 | {formatSize(userData.downloadQuota.remaining)} 剩余
|
| 194 | </span>
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 195 | </div>
|
| 196 | <div className="progress-bar">
|
| 197 | <div
|
| 198 | className="progress-fill"
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 199 | style={{
|
| 200 | width: `${(userData.downloadQuota.used / userData.downloadQuota.total) * 100}%`,
|
| 201 | backgroundColor: getProgressColor(userData.downloadQuota.used / userData.downloadQuota.total)
|
| 202 | }}
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 203 | ></div>
|
| 204 | </div>
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 205 | <div className="quota-total">
|
| 206 | 总额度: {formatSize(userData.downloadQuota.total)}
|
| 207 | </div>
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 208 | </div>
|
Akane1217 | 3a7bb97 | 2025-06-01 01:05:27 +0800 | [diff] [blame] | 209 |
|
| 210 | {Object.keys(downloadProgress).length > 0 && (
|
| 211 | <div className="progress-card">
|
| 212 | <h3>当前下载进度</h3>
|
| 213 | {Object.entries(downloadProgress).map(([taskId, progress]) => (
|
| 214 | <div key={taskId} className="download-task">
|
| 215 | <div className="task-info">
|
| 216 | <span className="task-id">任务: {taskId.substring(0, 8)}...</span>
|
| 217 | <span className="task-progress">{Math.round(progress * 100)}%</span>
|
| 218 | </div>
|
| 219 | <div className="progress-bar">
|
| 220 | <div
|
| 221 | className="progress-fill"
|
| 222 | style={{ width: `${progress * 100}%` }}
|
| 223 | ></div>
|
| 224 | </div>
|
| 225 | </div>
|
| 226 | ))}
|
| 227 | </div>
|
| 228 | )}
|
Akane1217 | 65b61a7 | 2025-05-17 13:52:25 +0800 | [diff] [blame] | 229 |
|
| 230 | {/* 功能卡片区 */}
|
| 231 | <div className="action-cards">
|
| 232 | {features.map((feature) => (
|
| 233 | <div
|
| 234 | key={feature.path}
|
| 235 | className="action-card"
|
| 236 | onClick={() => handleCardClick(feature.path)}
|
| 237 | >
|
| 238 | <h3>{feature.title}</h3>
|
| 239 | <p>{feature.description}</p>
|
| 240 | </div>
|
| 241 | ))}
|
| 242 | </div>
|
| 243 |
|
| 244 | {/* 子路由出口 */}
|
| 245 | <div className="subpage-container">
|
| 246 | <Outlet />
|
| 247 | </div>
|
| 248 | </div>
|
| 249 | );
|
| 250 | };
|
| 251 |
|
| 252 | export default Personal; |