blob: f6ef9f406744d413a26d5cf20fd4cf9f7f47036c [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";
wht15642182025-06-08 00:16:52 +08003import Button from '@mui/material/Button';
4import TextField from '@mui/material/TextField';
5import MenuItem from '@mui/material/MenuItem';
6import Dialog from '@mui/material/Dialog';
7import DialogTitle from '@mui/material/DialogTitle';
8import DialogContent from '@mui/material/DialogContent';
9import DialogActions from '@mui/material/DialogActions';
956303669a32fc2c2025-06-02 19:45:53 +080010import { useNavigate } from "react-router-dom";
223011330f9623f2025-06-06 00:22:05 +080011import { API_BASE_URL } from "./config";
956303669a32fc2c2025-06-02 19:45:53 +080012import "./App.css";
13
14export default function UserProfile() {
15 const navigate = useNavigate();
16 const [userInfo, setUserInfo] = useState({
rhjc6a4ee02025-06-06 00:45:18 +080017 avatar_url: "",
956303669a32fc2c2025-06-02 19:45:53 +080018 username: "示例用户",
19 email: "user@example.com",
223011339e292152025-06-08 00:34:37 +080020 invitetimes: "",
956303669a32fc2c2025-06-02 19:45:53 +080021 school: "",
rhjc6a4ee02025-06-06 00:45:18 +080022 account_status: "",
23 gender: "",
956303669a32fc2c2025-06-02 19:45:53 +080024 });
25 const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });
rhjc6a4ee02025-06-06 00:45:18 +080026 const [userSeeds, setUserSeeds] = useState([]);
wht15642182025-06-08 00:16:52 +080027 const [userFavorites, setUserFavorites] = useState([]);
rhjc6a4ee02025-06-06 00:45:18 +080028 const [userStats, setUserStats] = useState({
29 magic: 0,
30 upload: 0,
31 download: 0,
32 ratio: 0,
33 });
34
wht15642182025-06-08 00:16:52 +080035 // 邀请相关
36 const [inviteEmail, setInviteEmail] = useState('');
37 const [inviteStatus, setInviteStatus] = useState('');
38
39 // 兑换相关
40 const [exchangeType, setExchangeType] = useState('uploaded');
41 const [exchangeMagic, setExchangeMagic] = useState('');
42 const [exchangeResult, setExchangeResult] = useState(0);
43
44 // 兑换比例
45 const exchangeRate = { uploaded: 10, downloaded: 10, vip_downloads: 100 };
46
47 // 用户申诉相关
48 const [appealOpen, setAppealOpen] = useState(false);
49 const [appealTitle, setAppealTitle] = useState('');
50 const [appealFile, setAppealFile] = useState(null);
51
52 // 兑换结果计算
53 React.useEffect(() => {
54 if (!exchangeMagic || isNaN(exchangeMagic)) {
55 setExchangeResult(0);
56 return;
57 }
58 setExchangeResult(Number(exchangeMagic) / exchangeRate[exchangeType]);
59 }, [exchangeMagic, exchangeType]);
60
61 // 获取用户信息
rhjc6a4ee02025-06-06 00:45:18 +080062 useEffect(() => {
63 const fetchUserInfo = async () => {
223011339e292152025-06-08 00:34:37 +080064 // 假设userid存储在localStorage或其他地方
65 // const userid = localStorage.getItem("userid");
66 // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
67 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
68 const userid = match ? match[2] : null;
rhjc6a4ee02025-06-06 00:45:18 +080069 if (!userid) return;
70 try {
223011330f9623f2025-06-06 00:22:05 +080071 const res = await fetch(`${API_BASE_URL}/api/user-profile?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +080072 if (res.ok) {
73 const data = await res.json();
223011339e292152025-06-08 00:34:37 +080074 console.log("获取用户信息:", data);
rhjc6a4ee02025-06-06 00:45:18 +080075 setUserInfo(data);
76 setTempUserInfo(data);
77 }
78 } catch (err) {
rhjc6a4ee02025-06-06 00:45:18 +080079 console.error("获取用户信息失败", err);
80 }
81 };
82 fetchUserInfo();
83 }, []);
84
wht15642182025-06-08 00:16:52 +080085 // 获取上传种子
rhjc6a4ee02025-06-06 00:45:18 +080086 useEffect(() => {
87 const fetchUserSeeds = async () => {
223011339e292152025-06-08 00:34:37 +080088 // const userid = localStorage.getItem("userid");
89 // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
90 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
91 const userid = match ? match[2] : null;
rhjc6a4ee02025-06-06 00:45:18 +080092 if (!userid) return;
93 try {
223011330f9623f2025-06-06 00:22:05 +080094 const res = await fetch(`${API_BASE_URL}/api/user-seeds?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +080095 if (res.ok) {
96 const data = await res.json();
97 setUserSeeds(data);
98 }
99 } catch (err) {
100 console.error("获取种子列表失败", err);
101 }
102 };
103 fetchUserSeeds();
104 }, []);
105
wht15642182025-06-08 00:16:52 +0800106 // 收藏种子(示例数据)
107 useEffect(() => {
108 setUserFavorites([
109 { seedid: 'fav1', title: '收藏种子1', tags: '标签A', downloadtimes: 10 },
110 ]);
111 }, []);
112
113 // 获取活跃度
rhjc6a4ee02025-06-06 00:45:18 +0800114 useEffect(() => {
115 const fetchUserStats = async () => {
wht15642182025-06-08 00:16:52 +0800116 const userid = "550e8400-e29b-41d4-a716-446655440000";
rhjc6a4ee02025-06-06 00:45:18 +0800117 if (!userid) return;
118 try {
223011330f9623f2025-06-06 00:22:05 +0800119 const res = await fetch(`${API_BASE_URL}/api/user-stats?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +0800120 if (res.ok) {
121 const data = await res.json();
122 setUserStats(data);
123 }
124 } catch (err) {
125 console.error("获取活跃度信息失败", err);
126 }
127 };
128 fetchUserStats();
129 }, []);
956303669a32fc2c2025-06-02 19:45:53 +0800130
131 const handleInputChange = (field, value) => {
132 setTempUserInfo({ ...tempUserInfo, [field]: value });
133 };
134
rhjc6a4ee02025-06-06 00:45:18 +0800135 const handleSave = async () => {
223011339e292152025-06-08 00:34:37 +0800136 if (tempUserInfo.gender === "男"){
223011330f9623f2025-06-06 00:22:05 +0800137 tempUserInfo.gender = "m";
223011339e292152025-06-08 00:34:37 +0800138 }else if (tempUserInfo.gender === "女"){
223011330f9623f2025-06-06 00:22:05 +0800139 tempUserInfo.gender = "f";
140 }
956303669a32fc2c2025-06-02 19:45:53 +0800141 setUserInfo({ ...tempUserInfo });
223011339e292152025-06-08 00:34:37 +0800142 // 获取userid
143 // const userid = localStorage.getItem("userid");
144 // const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
145 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
146 const userid = match ? match[2] : null;
rhjc6a4ee02025-06-06 00:45:18 +0800147 try {
223011330f9623f2025-06-06 00:22:05 +0800148 const res = await fetch(`${API_BASE_URL}/api/change-profile`, {
rhjc6a4ee02025-06-06 00:45:18 +0800149 method: 'POST',
150 headers: {
151 'Content-Type': 'application/json',
152 },
153 body: JSON.stringify({ userid, ...tempUserInfo }),
154 });
155 if (res.ok) {
156 alert("信息已保存!");
157 } else {
158 alert("保存失败,请重试。");
159 }
160 } catch (err) {
161 alert("保存失败,请检查网络连接。");
162 console.error("保存用户信息失败", err);
163 }
956303669a32fc2c2025-06-02 19:45:53 +0800164 };
165
166 const handleAvatarClick = () => {
223011339e292152025-06-08 00:34:37 +0800167 const avatarUrl = prompt("请输入头像的URL:");
168 if (avatarUrl) {
169 setTempUserInfo({ ...tempUserInfo, avatar_url: avatarUrl });
956303669a32fc2c2025-06-02 19:45:53 +0800170 }
171 };
172
wht15642182025-06-08 00:16:52 +0800173 // 邀请
174 const handleInvite = () => {
175 if (!inviteEmail) return;
176 if (userInfo.invite_left <= 0) {
177 setInviteStatus("邀请次数已用完");
178 return;
179 }
180 setInviteStatus("邀请成功!(示例,无后端)");
181 setUserInfo((prev) => ({
182 ...prev,
183 invite_left: prev.invite_left - 1,
184 }));
185 setTempUserInfo((prev) => ({
186 ...prev,
187 invite_left: prev.invite_left - 1,
188 }));
189 setInviteEmail('');
190 };
191
192 // 兑换
193 const handleExchange = () => {
194 const magic = Number(exchangeMagic);
195 if (!magic || isNaN(magic) || magic <= 0) return;
196 if (magic > userStats.magic) {
197 alert("魔力值不足!");
198 return;
199 }
200 let newStats = { ...userStats };
201 if (exchangeType === "uploaded") {
202 newStats.upload += magic / exchangeRate.uploaded;
203 } else if (exchangeType === "downloaded") {
204 newStats.download = Math.max(0, newStats.download - magic / exchangeRate.downloaded);
205 } else if (exchangeType === "vip_downloads") {
206 newStats.vip_downloads += magic / exchangeRate.vip_downloads;
207 }
208 newStats.magic -= magic;
209 setUserStats(newStats);
210 setExchangeMagic('');
211 alert("兑换成功!(示例,无后端)");
212 };
213
214 // 删除种子
215 const handleDeleteSeed = (seedid) => {
216 setUserSeeds(userSeeds.filter((s) => s.seedid !== seedid));
217 };
218
219 // 申诉提交逻辑
220 const handleAppealSubmit = () => {
221 alert('申诉已提交!(示例,无后端)');
222 setAppealOpen(false);
223 setAppealTitle('');
224 setAppealFile(null);
225 };
226
956303669a32fc2c2025-06-02 19:45:53 +0800227 return (
wht15642182025-06-08 00:16:52 +0800228 <div
229 className="container"
230 style={{
231 minHeight: '100vh',
232 background: 'linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%)',
233 display: 'grid',
234 gridTemplateColumns: '1.1fr 1.9fr',
235 gridTemplateRows: 'auto auto',
236 gap: '12px',
237 padding: '24px 3vw',
238 boxSizing: 'border-box'
239 }}
240 >
241 {/* 左上:用户资料 */}
242 <div style={{
243 gridColumn: '1 / 2',
244 gridRow: '1 / 2',
245 display: 'flex',
246 flexDirection: 'column',
247 alignItems: 'center',
248 background: '#fff',
249 borderRadius: 20,
250 boxShadow: '0 6px 32px #e0e7ff',
251 padding: '32px 28px',
252 minWidth: 320,
253 minHeight: 420,
254 transition: 'box-shadow 0.2s',
255 }}>
256 <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 18 }}>
956303669a32fc2c2025-06-02 19:45:53 +0800257 <div onClick={handleAvatarClick} style={{ cursor: 'pointer', position: 'relative' }}>
wht15642182025-06-08 00:16:52 +0800258 <AccountCircleIcon style={{ fontSize: 96, color: '#1a237e', marginBottom: 12 }} />
223011330f9623f2025-06-06 00:22:05 +0800259 {tempUserInfo.avatar_url && (
956303669a32fc2c2025-06-02 19:45:53 +0800260 <img
rhjc6a4ee02025-06-06 00:45:18 +0800261 src={tempUserInfo.avatar_url}
956303669a32fc2c2025-06-02 19:45:53 +0800262 alt="用户头像"
263 style={{
264 position: 'absolute',
265 top: 0,
266 left: 0,
wht15642182025-06-08 00:16:52 +0800267 width: 96,
268 height: 96,
956303669a32fc2c2025-06-02 19:45:53 +0800269 borderRadius: '50%',
270 objectFit: 'cover',
wht15642182025-06-08 00:16:52 +0800271 border: '2px solid #e0e7ff',
272 boxShadow: '0 2px 8px #bfcfff'
956303669a32fc2c2025-06-02 19:45:53 +0800273 }}
274 />
275 )}
276 </div>
wht15642182025-06-08 00:16:52 +0800277 <h2 style={{ color: '#1a237e', marginBottom: 0, fontSize: 26, letterSpacing: 1 }}>用户个人资料</h2>
956303669a32fc2c2025-06-02 19:45:53 +0800278 </div>
wht15642182025-06-08 00:16:52 +0800279 <div className="card" style={{
280 padding: 32,
281 width: '100%',
282 background: '#fff',
283 borderRadius: 18,
284 boxShadow: '0 2px 12px #e0e7ff',
285 flex: 1,
286 minWidth: 0
287 }}>
288 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800289 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>用户名:</b>
wht15642182025-06-08 00:16:52 +0800290 <TextField
291 variant="outlined"
292 size="small"
956303669a32fc2c2025-06-02 19:45:53 +0800293 value={tempUserInfo.username}
294 onChange={(e) => handleInputChange("username", e.target.value)}
wht15642182025-06-08 00:16:52 +0800295 sx={{ flex: 1, minWidth: 0 }}
956303669a32fc2c2025-06-02 19:45:53 +0800296 />
297 </div>
wht15642182025-06-08 00:16:52 +0800298 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800299 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>
wht15642182025-06-08 00:16:52 +0800300 <TextField
301 variant="outlined"
302 size="small"
303 value={tempUserInfo.email}
304 InputProps={{ readOnly: true }}
305 sx={{ flex: 1, minWidth: 0, background: '#f5f5f5' }}
306 />
956303669a32fc2c2025-06-02 19:45:53 +0800307 </div>
wht15642182025-06-08 00:16:52 +0800308 {/* 邀请功能 */}
309 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
rhjc6a4ee02025-06-06 00:45:18 +0800310 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>
wht15642182025-06-08 00:16:52 +0800311 <TextField
312 type="email"
313 size="small"
314 placeholder="被邀请邮箱"
315 value={inviteEmail}
316 onChange={e => setInviteEmail(e.target.value)}
317 sx={{ flex: 2, marginRight: 1, minWidth: 120 }}
318 disabled={Number(tempUserInfo.invite_left) === 0}
319 />
320 <Button
321 variant="contained"
322 color="primary"
323 onClick={handleInvite}
324 disabled={Number(tempUserInfo.invite_left) === 0 || !inviteEmail}
325 sx={{ marginRight: 1, minWidth: 80 }}
326 >邀请</Button>
327 <span style={{ color: '#888', fontSize: 15 }}>剩余:{tempUserInfo.invite_left || "0"}</span>
956303669a32fc2c2025-06-02 19:45:53 +0800328 </div>
wht15642182025-06-08 00:16:52 +0800329 {inviteStatus && <div style={{ color: '#e53935', fontSize: 14, marginBottom: 8 }}>{inviteStatus}</div>}
330 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800331 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>
wht15642182025-06-08 00:16:52 +0800332 <TextField
333 variant="outlined"
334 size="small"
956303669a32fc2c2025-06-02 19:45:53 +0800335 value={tempUserInfo.school}
336 onChange={(e) => handleInputChange("school", e.target.value)}
wht15642182025-06-08 00:16:52 +0800337 sx={{ flex: 1, minWidth: 0 }}
956303669a32fc2c2025-06-02 19:45:53 +0800338 />
339 </div>
wht15642182025-06-08 00:16:52 +0800340 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
rhjc6a4ee02025-06-06 00:45:18 +0800341 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>
wht15642182025-06-08 00:16:52 +0800342 <TextField
343 variant="outlined"
344 size="small"
345 value={tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}
346 InputProps={{ readOnly: true }}
347 sx={{ flex: 1, minWidth: 0, background: '#f5f5f5' }}
348 />
349 <span style={{
350 display: 'inline-block',
351 width: 12,
352 height: 12,
353 borderRadius: '50%',
354 backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',
355 marginLeft: 10,
356 border: '1px solid #b2b2b2',
357 }} />
956303669a32fc2c2025-06-02 19:45:53 +0800358 </div>
wht15642182025-06-08 00:16:52 +0800359 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800360 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>
wht15642182025-06-08 00:16:52 +0800361 <TextField
362 select
363 variant="outlined"
364 size="small"
365 value={tempUserInfo.gender}
366 onChange={e => handleInputChange("gender", e.target.value)}
367 sx={{ flex: 1, minWidth: 0 }}
368 >
369 <MenuItem value="m">男性</MenuItem>
370 <MenuItem value="f">女性</MenuItem>
371 </TextField>
956303669a32fc2c2025-06-02 19:45:53 +0800372 </div>
wht15642182025-06-08 00:16:52 +0800373 <div style={{ display: 'flex', gap: 16, marginTop: 24, justifyContent: 'flex-end' }}>
374 <Button
375 variant="contained"
376 color="primary"
377 onClick={handleSave}
378 sx={{ fontSize: 16, borderRadius: 2, padding: '10px 24px' }}
379 >保存</Button>
380 <Button
381 variant="contained"
382 color="error"
383 onClick={() => setAppealOpen(true)}
384 sx={{ fontSize: 16, borderRadius: 2, padding: '10px 24px' }}
385 >用户申诉</Button>
386 </div>
956303669a32fc2c2025-06-02 19:45:53 +0800387 </div>
388 </div>
wht15642182025-06-08 00:16:52 +0800389 {/* 左下:活跃度模块 */}
390 <div style={{
391 gridColumn: '1 / 2',
392 gridRow: '2 / 3',
393 background: '#fff',
394 borderRadius: 20,
395 boxShadow: '0 6px 32px #e0e7ff',
396 padding: '32px 28px',
397 minWidth: 320,
398 minHeight: 320,
399 display: 'flex',
400 flexDirection: 'column',
401 justifyContent: 'center'
402 }}>
403 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18, letterSpacing: 1 }}>活跃度</h3>
404 <div style={{
405 border: '1.5px dashed #b2b2b2',
406 borderRadius: 14,
407 minHeight: 80,
408 padding: 22,
409 display: 'flex',
410 flexDirection: 'column',
411 gap: 14,
412 fontSize: 18,
413 background: '#f8faff'
414 }}>
415 <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
416 <span>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></span>
417 <TextField
418 type="number"
419 size="small"
420 placeholder="输入兑换魔力值"
421 value={exchangeMagic}
422 onChange={e => setExchangeMagic(e.target.value)}
423 sx={{ width: 100, marginLeft: 2, marginRight: 1 }}
424 />
425 <TextField
426 select
427 size="small"
428 value={exchangeType}
429 onChange={e => setExchangeType(e.target.value)}
430 sx={{ minWidth: 120 }}
431 >
432 <MenuItem value="uploaded">上传量(增加)</MenuItem>
433 <MenuItem value="downloaded">下载量(减少)</MenuItem>
434 <MenuItem value="vip_downloads">VIP下载次数(增加)</MenuItem>
435 </TextField>
436 <span style={{ marginLeft: 8, color: '#43a047' }}>
437 可兑换:<b>{exchangeResult}</b> {exchangeType === 'vip_downloads' ? '次' : 'GB'}
438 </span>
439 <Button
440 variant="contained"
441 color="primary"
442 onClick={handleExchange}
443 disabled={
444 !exchangeMagic ||
445 isNaN(exchangeMagic) ||
446 Number(exchangeMagic) <= 0 ||
447 Number(exchangeMagic) > userStats.magic
448 }
449 sx={{
450 marginLeft: 2,
451 minWidth: 80,
452 background: (!exchangeMagic || isNaN(exchangeMagic) || Number(exchangeMagic) <= 0 || Number(exchangeMagic) > userStats.magic) ? '#ccc' : undefined
453 }}
454 >兑换</Button>
455 </div>
456 <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload?.toFixed(2)} GB</b></div>
457 <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download?.toFixed(2)} GB</b></div>
458 <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.download === 0 ? "∞" : (userStats.upload / userStats.download).toFixed(2)}</b></div>
459 <div>VIP下载次数:<b style={{ color: '#1976d2' }}>{userStats.vip_downloads}</b></div>
460 </div>
461 </div>
462 {/* 右上:个人上传种子列表 */}
463 <div style={{
464 gridColumn: '2 / 3',
465 gridRow: '1 / 2',
466 background: '#fff',
467 borderRadius: 20,
468 boxShadow: '0 6px 32px #e0e7ff',
469 padding: '32px 36px',
470 minHeight: 420,
471 display: 'flex',
472 flexDirection: 'column'
473 }}>
474 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18, letterSpacing: 1 }}>个人上传种子列表</h3>
475 <div style={{
476 border: '1.5px dashed #b2b2b2',
477 borderRadius: 14,
478 minHeight: 80,
479 padding: 16,
480 background: '#f8faff'
481 }}>
rhjc6a4ee02025-06-06 00:45:18 +0800482 {userSeeds.length === 0 ? (
483 <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>
484 ) : (
485 <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
486 {userSeeds.map((seed, idx) => (
487 <li
wht15642182025-06-08 00:16:52 +0800488 key={seed.seedid || idx}
489 style={{
490 display: 'flex',
491 alignItems: 'center',
492 padding: '12px 0',
493 borderBottom: idx === userSeeds.length - 1 ? 'none' : '1px solid #e0e7ff',
494 cursor: 'pointer',
495 transition: 'background 0.15s'
496 }}
rhjc6a4ee02025-06-06 00:45:18 +0800497 onClick={e => {
rhjc6a4ee02025-06-06 00:45:18 +0800498 if (e.target.classList.contains('delete-btn')) return;
223011339e292152025-06-08 00:34:37 +0800499 navigate(`/torrent/${seed.seed_id}`);
rhjc6a4ee02025-06-06 00:45:18 +0800500 }}
wht15642182025-06-08 00:16:52 +0800501 onMouseOver={e => e.currentTarget.style.background = '#f3f6ff'}
502 onMouseOut={e => e.currentTarget.style.background = ''}
rhjc6a4ee02025-06-06 00:45:18 +0800503 >
504 <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
505 <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
223011330f9623f2025-06-06 00:22:05 +0800506 <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.downloadtimes}</span>
wht15642182025-06-08 00:16:52 +0800507 <Button
rhjc6a4ee02025-06-06 00:45:18 +0800508 className="delete-btn"
wht15642182025-06-08 00:16:52 +0800509 variant="contained"
510 color="error"
511 size="small"
512 sx={{ marginLeft: 2, borderRadius: 1, minWidth: 60 }}
223011339e292152025-06-08 00:34:37 +0800513 onClick={async e => {
rhjc6a4ee02025-06-06 00:45:18 +0800514 e.stopPropagation();
223011339e292152025-06-08 00:34:37 +0800515 // const userid = localStorage.getItem("userid");
516 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
517 try {
518 const res = await fetch(`${API_BASE_URL}/api/delete-seed`, {
519 method: 'POST',
520 headers: { 'Content-Type': 'application/json' },
521 body: JSON.stringify({ seed_id: seed.seed_id, userid }),
522 });
523 if (res.ok) {
524 setUserSeeds(userSeeds.filter((s, i) => (s.seed_id || i) !== (seed.seed_id || idx)));
525 } else {
526 alert('删除失败,请重试');
527 }
528 } catch (err) {
529 alert('删除失败,请检查网络');
530 }
rhjc6a4ee02025-06-06 00:45:18 +0800531 }}
wht15642182025-06-08 00:16:52 +0800532 >删除</Button>
rhjc6a4ee02025-06-06 00:45:18 +0800533 </li>
534 ))}
535 </ul>
536 )}
956303669a32fc2c2025-06-02 19:45:53 +0800537 </div>
538 </div>
wht15642182025-06-08 00:16:52 +0800539 {/* 右下:个人收藏种子列表 */}
540 <div style={{
541 gridColumn: '2 / 3',
542 gridRow: '2 / 3',
543 background: '#fff',
544 borderRadius: 20,
545 boxShadow: '0 6px 32px #e0e7ff',
546 padding: '32px 36px',
547 minHeight: 320,
548 display: 'flex',
549 flexDirection: 'column'
550 }}>
551 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18, letterSpacing: 1 }}>个人收藏种子列表</h3>
552 <div style={{
553 border: '1.5px dashed #b2b2b2',
554 borderRadius: 14,
555 minHeight: 80,
556 padding: 16,
557 background: '#f8faff'
558 }}>
559 {userFavorites.length === 0 ? (
560 <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无收藏种子)</div>
561 ) : (
562 <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
563 {userFavorites.map((seed, idx) => (
564 <li
565 key={seed.seedid || idx}
566 style={{
567 display: 'flex',
568 alignItems: 'center',
569 padding: '12px 0',
570 borderBottom: idx === userFavorites.length - 1 ? 'none' : '1px solid #e0e7ff',
571 cursor: 'pointer',
572 transition: 'background 0.15s'
573 }}
574 onClick={e => {
575 navigate(`/torrent/${seed.seedid}`);
576 }}
577 onMouseOver={e => e.currentTarget.style.background = '#f3f6ff'}
578 onMouseOut={e => e.currentTarget.style.background = ''}
579 >
580 <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
581 <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
582 <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.downloadtimes}</span>
583 </li>
584 ))}
585 </ul>
586 )}
956303669a32fc2c2025-06-02 19:45:53 +0800587 </div>
588 </div>
wht15642182025-06-08 00:16:52 +0800589 {/* 申诉弹窗 */}
590 <Dialog open={appealOpen} onClose={() => setAppealOpen(false)}>
591 <DialogTitle>提交申诉</DialogTitle>
592 <DialogContent>
593 <div style={{ marginBottom: 16 }}>
594 <TextField
595 label="申诉主题"
596 fullWidth
597 value={appealTitle}
598 onChange={e => setAppealTitle(e.target.value)}
599 size="small"
600 />
601 </div>
602 <div>
603 <input
604 type="file"
605 onChange={e => setAppealFile(e.target.files[0])}
606 style={{ marginTop: 8 }}
607 />
608 </div>
609 </DialogContent>
610 <DialogActions>
611 <Button onClick={handleAppealSubmit} variant="contained" color="primary" disabled={!appealTitle || !appealFile}>提交</Button>
612 <Button onClick={() => setAppealOpen(false)} variant="outlined">取消</Button>
613 </DialogActions>
614 </Dialog>
956303669a32fc2c2025-06-02 19:45:53 +0800615 </div>
616 );
617}