blob: 16fdcd267b466dbb2d1e5b1bc3b257aba9b22735 [file] [log] [blame]
rhjc6a4ee02025-06-06 00:45:18 +08001import React, { useState, useEffect } from "react";
956303669a32fc2c2025-06-02 19:45:53 +08002import AccountCircleIcon from "@mui/icons-material/AccountCircle";
3import { useNavigate } from "react-router-dom";
223011330f9623f2025-06-06 00:22:05 +08004import { API_BASE_URL } from "./config";
956303669a32fc2c2025-06-02 19:45:53 +08005import "./App.css";
6
7export default function UserProfile() {
8 const navigate = useNavigate();
9 const [userInfo, setUserInfo] = useState({
rhjc6a4ee02025-06-06 00:45:18 +080010 avatar_url: "",
956303669a32fc2c2025-06-02 19:45:53 +080011 username: "示例用户",
12 email: "user@example.com",
rhjc6a4ee02025-06-06 00:45:18 +080013 invite_left: "",
956303669a32fc2c2025-06-02 19:45:53 +080014 school: "",
rhjc6a4ee02025-06-06 00:45:18 +080015 account_status: "",
16 gender: "",
956303669a32fc2c2025-06-02 19:45:53 +080017 });
18 const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });
rhjc6a4ee02025-06-06 00:45:18 +080019 const [userSeeds, setUserSeeds] = useState([]);
20 const [userStats, setUserStats] = useState({
21 magic: 0,
22 upload: 0,
23 download: 0,
24 ratio: 0,
25 });
26
27 // 新增:根据userid从后端获取用户信息
28 useEffect(() => {
29 const fetchUserInfo = async () => {
30 // 假设userid存储在localStorage或其他地方
31 // const userid = localStorage.getItem("userid");
32 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
33 if (!userid) return;
34 try {
223011330f9623f2025-06-06 00:22:05 +080035 const res = await fetch(`${API_BASE_URL}/api/user-profile?userid=${userid}`);
whtb1e79592025-06-07 16:03:09 +080036
rhjc6a4ee02025-06-06 00:45:18 +080037 if (res.ok) {
38 const data = await res.json();
39 setUserInfo(data);
40 setTempUserInfo(data);
223011330f9623f2025-06-06 00:22:05 +080041 // console.log(data);
rhjc6a4ee02025-06-06 00:45:18 +080042 }
43 } catch (err) {
44 // 可以根据需要处理错误
45 console.error("获取用户信息失败", err);
46 }
47 };
48 fetchUserInfo();
49 }, []);
50
51 // 动态加载用户上传种子列表
52 useEffect(() => {
53 const fetchUserSeeds = async () => {
54 // const userid = localStorage.getItem("userid");
55 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
56 if (!userid) return;
57 try {
223011330f9623f2025-06-06 00:22:05 +080058 const res = await fetch(`${API_BASE_URL}/api/user-seeds?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +080059 if (res.ok) {
60 const data = await res.json();
61 setUserSeeds(data);
62 }
63 } catch (err) {
64 console.error("获取种子列表失败", err);
65 }
66 };
67 fetchUserSeeds();
68 }, []);
69
70 // 动态加载用户活跃度信息
71 useEffect(() => {
72 const fetchUserStats = async () => {
73 // const userid = localStorage.getItem("userid");
74 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
75 if (!userid) return;
76 try {
223011330f9623f2025-06-06 00:22:05 +080077 const res = await fetch(`${API_BASE_URL}/api/user-stats?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +080078 if (res.ok) {
79 const data = await res.json();
80 setUserStats(data);
81 }
82 } catch (err) {
83 console.error("获取活跃度信息失败", err);
84 }
85 };
86 fetchUserStats();
87 }, []);
956303669a32fc2c2025-06-02 19:45:53 +080088
89 const handleInputChange = (field, value) => {
90 setTempUserInfo({ ...tempUserInfo, [field]: value });
91 };
92
rhjc6a4ee02025-06-06 00:45:18 +080093 const handleSave = async () => {
whtb1e79592025-06-07 16:03:09 +080094 if (tempUserInfo.gender === "男性") {
223011330f9623f2025-06-06 00:22:05 +080095 tempUserInfo.gender = "m";
whtb1e79592025-06-07 16:03:09 +080096 } else if (tempUserInfo.gender === "女性") {
223011330f9623f2025-06-06 00:22:05 +080097 tempUserInfo.gender = "f";
98 }
956303669a32fc2c2025-06-02 19:45:53 +080099 setUserInfo({ ...tempUserInfo });
223011330f9623f2025-06-06 00:22:05 +0800100 console.log("保存的用户信息:", tempUserInfo);
rhjc6a4ee02025-06-06 00:45:18 +0800101 // 获取userid
102 // const userid = localStorage.getItem("userid");
103 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
104 try {
223011330f9623f2025-06-06 00:22:05 +0800105 const res = await fetch(`${API_BASE_URL}/api/change-profile`, {
rhjc6a4ee02025-06-06 00:45:18 +0800106 method: 'POST',
107 headers: {
108 'Content-Type': 'application/json',
109 },
110 body: JSON.stringify({ userid, ...tempUserInfo }),
111 });
112 if (res.ok) {
113 alert("信息已保存!");
114 } else {
115 alert("保存失败,请重试。");
116 }
117 } catch (err) {
118 alert("保存失败,请检查网络连接。");
119 console.error("保存用户信息失败", err);
120 }
956303669a32fc2c2025-06-02 19:45:53 +0800121 };
122
123 const handleAvatarClick = () => {
223011330f9623f2025-06-06 00:22:05 +0800124 const pictureUrl = prompt("请输入头像的URL:");
125 if (pictureUrl) {
126 setTempUserInfo({ ...tempUserInfo, avatar_url: pictureUrl });
956303669a32fc2c2025-06-02 19:45:53 +0800127 }
128 };
129
130 return (
131 <div className="container" style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%)', display: 'grid', gridTemplateColumns: '1fr 2fr', gridTemplateRows: 'auto 1fr', gap: '20px', padding: '40px' }}>
132 {/* 左侧:用户资料 */}
133 <div style={{ gridColumn: '1 / 2', gridRow: '1 / 3', display: 'flex', flexDirection: 'column', alignItems: 'center', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
134 <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 16 }}>
135 <div onClick={handleAvatarClick} style={{ cursor: 'pointer', position: 'relative' }}>
136 <AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />
223011330f9623f2025-06-06 00:22:05 +0800137 {tempUserInfo.avatar_url && (
956303669a32fc2c2025-06-02 19:45:53 +0800138 <img
rhjc6a4ee02025-06-06 00:45:18 +0800139 src={tempUserInfo.avatar_url}
956303669a32fc2c2025-06-02 19:45:53 +0800140 alt="用户头像"
141 style={{
142 position: 'absolute',
143 top: 0,
144 left: 0,
145 width: 90,
146 height: 90,
147 borderRadius: '50%',
148 objectFit: 'cover',
149 }}
150 />
151 )}
152 </div>
153 <h2 style={{ color: '#1a237e', marginBottom: 0, fontSize: 24 }}>用户个人资料</h2>
154 </div>
155 <div className="card" style={{ padding: 28, width: '100%', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', flex: 1 }}>
156 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
157 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>用户名:</b>
158 <input
159 type="text"
160 value={tempUserInfo.username}
161 onChange={(e) => handleInputChange("username", e.target.value)}
162 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
163 />
164 </div>
165 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
166 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>
rhjc6a4ee02025-06-06 00:45:18 +0800167 <span
168 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
169 >
170 {tempUserInfo.email}
171 </span>
956303669a32fc2c2025-06-02 19:45:53 +0800172 </div>
173 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
rhjc6a4ee02025-06-06 00:45:18 +0800174 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>
175 <span
176 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
177 >
223011330f9623f2025-06-06 00:22:05 +0800178 {tempUserInfo.invite_left || "0"}
rhjc6a4ee02025-06-06 00:45:18 +0800179 </span>
956303669a32fc2c2025-06-02 19:45:53 +0800180 </div>
181 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
182 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>
183 <input
184 type="text"
185 value={tempUserInfo.school}
186 onChange={(e) => handleInputChange("school", e.target.value)}
187 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
188 />
189 </div>
190 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
rhjc6a4ee02025-06-06 00:45:18 +0800191 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>
192 <span
193 style={{ flex: 1, display: 'flex', alignItems: 'center', padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
194 >
195 {tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}
196 <span style={{
197 display: 'inline-block',
198 width: 12,
199 height: 12,
200 borderRadius: '50%',
201 backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',
202 marginLeft: 10,
203 border: '1px solid #b2b2b2',
204 }} />
205 </span>
956303669a32fc2c2025-06-02 19:45:53 +0800206 </div>
207 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
208 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>
209 <div style={{ position: 'relative', flex: 1 }}>
210 <button
211 onClick={() => setTempUserInfo({ ...tempUserInfo, showGenderOptions: !tempUserInfo.showGenderOptions })}
212 style={{
213 width: '100%',
214 padding: '6px 10px',
215 borderRadius: 7,
216 border: '1px solid #b2b2b2',
217 textAlign: 'left',
218 backgroundColor: '#fff',
219 fontSize: 15,
220 cursor: 'pointer',
221 }}
222 >
223011330f9623f2025-06-06 00:22:05 +0800223 {tempUserInfo.gender === 'm' ? '男性'
224 : tempUserInfo.gender === 'f' ? '女性'
whtb1e79592025-06-07 16:03:09 +0800225 : '性别'}
956303669a32fc2c2025-06-02 19:45:53 +0800226 </button>
227 {tempUserInfo.showGenderOptions && (
228 <ul
229 style={{
230 position: 'absolute',
231 top: '100%',
232 left: 0,
233 right: 0,
234 backgroundColor: '#fff',
235 border: '1px solid #b2b2b2',
236 borderRadius: 7,
237 listStyle: 'none',
238 margin: 0,
239 padding: 0,
240 zIndex: 10,
241 }}
242 >
223011330f9623f2025-06-06 00:22:05 +0800243 {[{ value: 'm', label: '男性' }, { value: 'f', label: '女性' }].map(opt => (
956303669a32fc2c2025-06-02 19:45:53 +0800244 <li
223011330f9623f2025-06-06 00:22:05 +0800245 key={opt.value}
246 onClick={() => setTempUserInfo({ ...tempUserInfo, gender: opt.value, showGenderOptions: false })}
956303669a32fc2c2025-06-02 19:45:53 +0800247 style={{
248 padding: '6px 10px',
249 cursor: 'pointer',
250 borderBottom: '1px solid #e0e0e0',
223011330f9623f2025-06-06 00:22:05 +0800251 backgroundColor: tempUserInfo.gender === opt.value ? '#f0f0f0' : '#fff',
956303669a32fc2c2025-06-02 19:45:53 +0800252 }}
253 >
223011330f9623f2025-06-06 00:22:05 +0800254 {opt.label}
956303669a32fc2c2025-06-02 19:45:53 +0800255 </li>
256 ))}
257 </ul>
258 )}
259 </div>
260 </div>
261 <button
262 onClick={handleSave}
263 style={{
264 marginTop: 20,
265 padding: '10px 20px',
266 backgroundColor: '#1a237e',
267 color: '#fff',
268 border: 'none',
269 borderRadius: 7,
270 cursor: 'pointer',
271 fontSize: 16,
272 alignSelf: 'center', // Center the button horizontally
273 }}
274 onMouseOver={(e) => (e.target.style.backgroundColor = '#0d1b5e')}
275 onMouseOut={(e) => (e.target.style.backgroundColor = '#1a237e')}
276 >
277 保存
278 </button>
279 </div>
280 </div>
281 {/* 上传种子列表 */}
282 <div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
283 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>
rhjc6a4ee02025-06-06 00:45:18 +0800284 <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 12 }}>
285 {userSeeds.length === 0 ? (
286 <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>
287 ) : (
288 <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
289 {userSeeds.map((seed, idx) => (
290 <li
291 key={seed.seed_id || idx}
292 style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid #e0e7ff', cursor: 'pointer' }}
293 onClick={e => {
294 // 阻止点击删除按钮时跳转
295 if (e.target.classList.contains('delete-btn')) return;
223011330f9623f2025-06-06 00:22:05 +0800296 navigate(`/torrent/${seed.seedid}`);
rhjc6a4ee02025-06-06 00:45:18 +0800297 }}
298 >
299 <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
300 <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
223011330f9623f2025-06-06 00:22:05 +0800301 <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.downloadtimes}</span>
rhjc6a4ee02025-06-06 00:45:18 +0800302 <button
303 className="delete-btn"
304 style={{ marginLeft: 18, background: '#e53935', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 14px', cursor: 'pointer', fontSize: 14 }}
305 onClick={async (e) => {
306 e.stopPropagation();
307 // const userid = localStorage.getItem("userid");
223011330f9623f2025-06-06 00:22:05 +0800308 // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
rhjc6a4ee02025-06-06 00:45:18 +0800309 try {
whtb1e79592025-06-07 16:03:09 +0800310
223011330f9623f2025-06-06 00:22:05 +0800311 const res = await fetch(`${API_BASE_URL}/api/delete-seed`, {
rhjc6a4ee02025-06-06 00:45:18 +0800312 method: 'POST',
313 headers: { 'Content-Type': 'application/json' },
223011330f9623f2025-06-06 00:22:05 +0800314 body: JSON.stringify({ seedid: seed.seedid }),
rhjc6a4ee02025-06-06 00:45:18 +0800315 });
223011330f9623f2025-06-06 00:22:05 +0800316 // console.log(seed.seedid);
rhjc6a4ee02025-06-06 00:45:18 +0800317 if (res.ok) {
223011330f9623f2025-06-06 00:22:05 +0800318 setUserSeeds(userSeeds.filter((s, i) => (s.seedid || i) !== (seed.seedid || idx)));
rhjc6a4ee02025-06-06 00:45:18 +0800319 } else {
320 alert('删除失败,请重试');
321 }
322 } catch (err) {
323 alert('删除失败,请检查网络');
324 }
325 }}
326 >删除</button>
327 </li>
328 ))}
329 </ul>
330 )}
956303669a32fc2c2025-06-02 19:45:53 +0800331 </div>
332 </div>
333 {/* 活跃度模块 */}
334 <div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
335 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>
rhjc6a4ee02025-06-06 00:45:18 +0800336 <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 18, display: 'flex', flexDirection: 'column', gap: 12, fontSize: 18 }}>
337 <div>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></div>
338 <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload} GB</b></div>
339 <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download} GB</b></div>
340 <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.ratio}</b></div>
956303669a32fc2c2025-06-02 19:45:53 +0800341 </div>
342 </div>
343 </div>
344 );
345}