blob: cf38e7f2e521334a03fc435e3a72c9f7b0d9aec7 [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",
rhjc6a4ee02025-06-06 00:45:18 +080020 invite_left: "",
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 () => {
wht15642182025-06-08 00:16:52 +080064 const userid = "550e8400-e29b-41d4-a716-446655440000";
rhjc6a4ee02025-06-06 00:45:18 +080065 if (!userid) return;
66 try {
223011330f9623f2025-06-06 00:22:05 +080067 const res = await fetch(`${API_BASE_URL}/api/user-profile?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +080068 if (res.ok) {
69 const data = await res.json();
70 setUserInfo(data);
71 setTempUserInfo(data);
72 }
73 } catch (err) {
rhjc6a4ee02025-06-06 00:45:18 +080074 console.error("获取用户信息失败", err);
75 }
76 };
77 fetchUserInfo();
78 }, []);
79
wht15642182025-06-08 00:16:52 +080080 // 获取上传种子
rhjc6a4ee02025-06-06 00:45:18 +080081 useEffect(() => {
82 const fetchUserSeeds = async () => {
wht15642182025-06-08 00:16:52 +080083 const userid = "550e8400-e29b-41d4-a716-446655440000";
rhjc6a4ee02025-06-06 00:45:18 +080084 if (!userid) return;
85 try {
223011330f9623f2025-06-06 00:22:05 +080086 const res = await fetch(`${API_BASE_URL}/api/user-seeds?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +080087 if (res.ok) {
88 const data = await res.json();
89 setUserSeeds(data);
90 }
91 } catch (err) {
92 console.error("获取种子列表失败", err);
93 }
94 };
95 fetchUserSeeds();
96 }, []);
97
wht15642182025-06-08 00:16:52 +080098 // 收藏种子(示例数据)
99 useEffect(() => {
100 setUserFavorites([
101 { seedid: 'fav1', title: '收藏种子1', tags: '标签A', downloadtimes: 10 },
102 ]);
103 }, []);
104
105 // 获取活跃度
rhjc6a4ee02025-06-06 00:45:18 +0800106 useEffect(() => {
107 const fetchUserStats = async () => {
wht15642182025-06-08 00:16:52 +0800108 const userid = "550e8400-e29b-41d4-a716-446655440000";
rhjc6a4ee02025-06-06 00:45:18 +0800109 if (!userid) return;
110 try {
223011330f9623f2025-06-06 00:22:05 +0800111 const res = await fetch(`${API_BASE_URL}/api/user-stats?userid=${userid}`);
rhjc6a4ee02025-06-06 00:45:18 +0800112 if (res.ok) {
113 const data = await res.json();
114 setUserStats(data);
115 }
116 } catch (err) {
117 console.error("获取活跃度信息失败", err);
118 }
119 };
120 fetchUserStats();
121 }, []);
956303669a32fc2c2025-06-02 19:45:53 +0800122
123 const handleInputChange = (field, value) => {
124 setTempUserInfo({ ...tempUserInfo, [field]: value });
125 };
126
rhjc6a4ee02025-06-06 00:45:18 +0800127 const handleSave = async () => {
whtb1e79592025-06-07 16:03:09 +0800128 if (tempUserInfo.gender === "男性") {
223011330f9623f2025-06-06 00:22:05 +0800129 tempUserInfo.gender = "m";
whtb1e79592025-06-07 16:03:09 +0800130 } else if (tempUserInfo.gender === "女性") {
223011330f9623f2025-06-06 00:22:05 +0800131 tempUserInfo.gender = "f";
132 }
956303669a32fc2c2025-06-02 19:45:53 +0800133 setUserInfo({ ...tempUserInfo });
wht15642182025-06-08 00:16:52 +0800134 const userid = "550e8400-e29b-41d4-a716-446655440000";
rhjc6a4ee02025-06-06 00:45:18 +0800135 try {
223011330f9623f2025-06-06 00:22:05 +0800136 const res = await fetch(`${API_BASE_URL}/api/change-profile`, {
rhjc6a4ee02025-06-06 00:45:18 +0800137 method: 'POST',
138 headers: {
139 'Content-Type': 'application/json',
140 },
141 body: JSON.stringify({ userid, ...tempUserInfo }),
142 });
143 if (res.ok) {
144 alert("信息已保存!");
145 } else {
146 alert("保存失败,请重试。");
147 }
148 } catch (err) {
149 alert("保存失败,请检查网络连接。");
150 console.error("保存用户信息失败", err);
151 }
956303669a32fc2c2025-06-02 19:45:53 +0800152 };
153
154 const handleAvatarClick = () => {
223011330f9623f2025-06-06 00:22:05 +0800155 const pictureUrl = prompt("请输入头像的URL:");
156 if (pictureUrl) {
157 setTempUserInfo({ ...tempUserInfo, avatar_url: pictureUrl });
956303669a32fc2c2025-06-02 19:45:53 +0800158 }
159 };
160
wht15642182025-06-08 00:16:52 +0800161 // 邀请
162 const handleInvite = () => {
163 if (!inviteEmail) return;
164 if (userInfo.invite_left <= 0) {
165 setInviteStatus("邀请次数已用完");
166 return;
167 }
168 setInviteStatus("邀请成功!(示例,无后端)");
169 setUserInfo((prev) => ({
170 ...prev,
171 invite_left: prev.invite_left - 1,
172 }));
173 setTempUserInfo((prev) => ({
174 ...prev,
175 invite_left: prev.invite_left - 1,
176 }));
177 setInviteEmail('');
178 };
179
180 // 兑换
181 const handleExchange = () => {
182 const magic = Number(exchangeMagic);
183 if (!magic || isNaN(magic) || magic <= 0) return;
184 if (magic > userStats.magic) {
185 alert("魔力值不足!");
186 return;
187 }
188 let newStats = { ...userStats };
189 if (exchangeType === "uploaded") {
190 newStats.upload += magic / exchangeRate.uploaded;
191 } else if (exchangeType === "downloaded") {
192 newStats.download = Math.max(0, newStats.download - magic / exchangeRate.downloaded);
193 } else if (exchangeType === "vip_downloads") {
194 newStats.vip_downloads += magic / exchangeRate.vip_downloads;
195 }
196 newStats.magic -= magic;
197 setUserStats(newStats);
198 setExchangeMagic('');
199 alert("兑换成功!(示例,无后端)");
200 };
201
202 // 删除种子
203 const handleDeleteSeed = (seedid) => {
204 setUserSeeds(userSeeds.filter((s) => s.seedid !== seedid));
205 };
206
207 // 申诉提交逻辑
208 const handleAppealSubmit = () => {
209 alert('申诉已提交!(示例,无后端)');
210 setAppealOpen(false);
211 setAppealTitle('');
212 setAppealFile(null);
213 };
214
956303669a32fc2c2025-06-02 19:45:53 +0800215 return (
wht15642182025-06-08 00:16:52 +0800216 <div
217 className="container"
218 style={{
219 minHeight: '100vh',
220 background: 'linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%)',
221 display: 'grid',
222 gridTemplateColumns: '1.1fr 1.9fr',
223 gridTemplateRows: 'auto auto',
224 gap: '12px',
225 padding: '24px 3vw',
226 boxSizing: 'border-box'
227 }}
228 >
229 {/* 左上:用户资料 */}
230 <div style={{
231 gridColumn: '1 / 2',
232 gridRow: '1 / 2',
233 display: 'flex',
234 flexDirection: 'column',
235 alignItems: 'center',
236 background: '#fff',
237 borderRadius: 20,
238 boxShadow: '0 6px 32px #e0e7ff',
239 padding: '32px 28px',
240 minWidth: 320,
241 minHeight: 420,
242 transition: 'box-shadow 0.2s',
243 }}>
244 <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 18 }}>
956303669a32fc2c2025-06-02 19:45:53 +0800245 <div onClick={handleAvatarClick} style={{ cursor: 'pointer', position: 'relative' }}>
wht15642182025-06-08 00:16:52 +0800246 <AccountCircleIcon style={{ fontSize: 96, color: '#1a237e', marginBottom: 12 }} />
223011330f9623f2025-06-06 00:22:05 +0800247 {tempUserInfo.avatar_url && (
956303669a32fc2c2025-06-02 19:45:53 +0800248 <img
rhjc6a4ee02025-06-06 00:45:18 +0800249 src={tempUserInfo.avatar_url}
956303669a32fc2c2025-06-02 19:45:53 +0800250 alt="用户头像"
251 style={{
252 position: 'absolute',
253 top: 0,
254 left: 0,
wht15642182025-06-08 00:16:52 +0800255 width: 96,
256 height: 96,
956303669a32fc2c2025-06-02 19:45:53 +0800257 borderRadius: '50%',
258 objectFit: 'cover',
wht15642182025-06-08 00:16:52 +0800259 border: '2px solid #e0e7ff',
260 boxShadow: '0 2px 8px #bfcfff'
956303669a32fc2c2025-06-02 19:45:53 +0800261 }}
262 />
263 )}
264 </div>
wht15642182025-06-08 00:16:52 +0800265 <h2 style={{ color: '#1a237e', marginBottom: 0, fontSize: 26, letterSpacing: 1 }}>用户个人资料</h2>
956303669a32fc2c2025-06-02 19:45:53 +0800266 </div>
wht15642182025-06-08 00:16:52 +0800267 <div className="card" style={{
268 padding: 32,
269 width: '100%',
270 background: '#fff',
271 borderRadius: 18,
272 boxShadow: '0 2px 12px #e0e7ff',
273 flex: 1,
274 minWidth: 0
275 }}>
276 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800277 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>用户名:</b>
wht15642182025-06-08 00:16:52 +0800278 <TextField
279 variant="outlined"
280 size="small"
956303669a32fc2c2025-06-02 19:45:53 +0800281 value={tempUserInfo.username}
282 onChange={(e) => handleInputChange("username", e.target.value)}
wht15642182025-06-08 00:16:52 +0800283 sx={{ flex: 1, minWidth: 0 }}
956303669a32fc2c2025-06-02 19:45:53 +0800284 />
285 </div>
wht15642182025-06-08 00:16:52 +0800286 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800287 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>
wht15642182025-06-08 00:16:52 +0800288 <TextField
289 variant="outlined"
290 size="small"
291 value={tempUserInfo.email}
292 InputProps={{ readOnly: true }}
293 sx={{ flex: 1, minWidth: 0, background: '#f5f5f5' }}
294 />
956303669a32fc2c2025-06-02 19:45:53 +0800295 </div>
wht15642182025-06-08 00:16:52 +0800296 {/* 邀请功能 */}
297 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
rhjc6a4ee02025-06-06 00:45:18 +0800298 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>
wht15642182025-06-08 00:16:52 +0800299 <TextField
300 type="email"
301 size="small"
302 placeholder="被邀请邮箱"
303 value={inviteEmail}
304 onChange={e => setInviteEmail(e.target.value)}
305 sx={{ flex: 2, marginRight: 1, minWidth: 120 }}
306 disabled={Number(tempUserInfo.invite_left) === 0}
307 />
308 <Button
309 variant="contained"
310 color="primary"
311 onClick={handleInvite}
312 disabled={Number(tempUserInfo.invite_left) === 0 || !inviteEmail}
313 sx={{ marginRight: 1, minWidth: 80 }}
314 >邀请</Button>
315 <span style={{ color: '#888', fontSize: 15 }}>剩余:{tempUserInfo.invite_left || "0"}</span>
956303669a32fc2c2025-06-02 19:45:53 +0800316 </div>
wht15642182025-06-08 00:16:52 +0800317 {inviteStatus && <div style={{ color: '#e53935', fontSize: 14, marginBottom: 8 }}>{inviteStatus}</div>}
318 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800319 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>
wht15642182025-06-08 00:16:52 +0800320 <TextField
321 variant="outlined"
322 size="small"
956303669a32fc2c2025-06-02 19:45:53 +0800323 value={tempUserInfo.school}
324 onChange={(e) => handleInputChange("school", e.target.value)}
wht15642182025-06-08 00:16:52 +0800325 sx={{ flex: 1, minWidth: 0 }}
956303669a32fc2c2025-06-02 19:45:53 +0800326 />
327 </div>
wht15642182025-06-08 00:16:52 +0800328 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
rhjc6a4ee02025-06-06 00:45:18 +0800329 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>
wht15642182025-06-08 00:16:52 +0800330 <TextField
331 variant="outlined"
332 size="small"
333 value={tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}
334 InputProps={{ readOnly: true }}
335 sx={{ flex: 1, minWidth: 0, background: '#f5f5f5' }}
336 />
337 <span style={{
338 display: 'inline-block',
339 width: 12,
340 height: 12,
341 borderRadius: '50%',
342 backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',
343 marginLeft: 10,
344 border: '1px solid #b2b2b2',
345 }} />
956303669a32fc2c2025-06-02 19:45:53 +0800346 </div>
wht15642182025-06-08 00:16:52 +0800347 <div style={{ marginBottom: 20, display: 'flex', alignItems: 'center' }}>
956303669a32fc2c2025-06-02 19:45:53 +0800348 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>
wht15642182025-06-08 00:16:52 +0800349 <TextField
350 select
351 variant="outlined"
352 size="small"
353 value={tempUserInfo.gender}
354 onChange={e => handleInputChange("gender", e.target.value)}
355 sx={{ flex: 1, minWidth: 0 }}
356 >
357 <MenuItem value="m">男性</MenuItem>
358 <MenuItem value="f">女性</MenuItem>
359 </TextField>
956303669a32fc2c2025-06-02 19:45:53 +0800360 </div>
wht15642182025-06-08 00:16:52 +0800361 <div style={{ display: 'flex', gap: 16, marginTop: 24, justifyContent: 'flex-end' }}>
362 <Button
363 variant="contained"
364 color="primary"
365 onClick={handleSave}
366 sx={{ fontSize: 16, borderRadius: 2, padding: '10px 24px' }}
367 >保存</Button>
368 <Button
369 variant="contained"
370 color="error"
371 onClick={() => setAppealOpen(true)}
372 sx={{ fontSize: 16, borderRadius: 2, padding: '10px 24px' }}
373 >用户申诉</Button>
374 </div>
956303669a32fc2c2025-06-02 19:45:53 +0800375 </div>
376 </div>
wht15642182025-06-08 00:16:52 +0800377 {/* 左下:活跃度模块 */}
378 <div style={{
379 gridColumn: '1 / 2',
380 gridRow: '2 / 3',
381 background: '#fff',
382 borderRadius: 20,
383 boxShadow: '0 6px 32px #e0e7ff',
384 padding: '32px 28px',
385 minWidth: 320,
386 minHeight: 320,
387 display: 'flex',
388 flexDirection: 'column',
389 justifyContent: 'center'
390 }}>
391 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18, letterSpacing: 1 }}>活跃度</h3>
392 <div style={{
393 border: '1.5px dashed #b2b2b2',
394 borderRadius: 14,
395 minHeight: 80,
396 padding: 22,
397 display: 'flex',
398 flexDirection: 'column',
399 gap: 14,
400 fontSize: 18,
401 background: '#f8faff'
402 }}>
403 <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
404 <span>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></span>
405 <TextField
406 type="number"
407 size="small"
408 placeholder="输入兑换魔力值"
409 value={exchangeMagic}
410 onChange={e => setExchangeMagic(e.target.value)}
411 sx={{ width: 100, marginLeft: 2, marginRight: 1 }}
412 />
413 <TextField
414 select
415 size="small"
416 value={exchangeType}
417 onChange={e => setExchangeType(e.target.value)}
418 sx={{ minWidth: 120 }}
419 >
420 <MenuItem value="uploaded">上传量(增加)</MenuItem>
421 <MenuItem value="downloaded">下载量(减少)</MenuItem>
422 <MenuItem value="vip_downloads">VIP下载次数(增加)</MenuItem>
423 </TextField>
424 <span style={{ marginLeft: 8, color: '#43a047' }}>
425 可兑换:<b>{exchangeResult}</b> {exchangeType === 'vip_downloads' ? '次' : 'GB'}
426 </span>
427 <Button
428 variant="contained"
429 color="primary"
430 onClick={handleExchange}
431 disabled={
432 !exchangeMagic ||
433 isNaN(exchangeMagic) ||
434 Number(exchangeMagic) <= 0 ||
435 Number(exchangeMagic) > userStats.magic
436 }
437 sx={{
438 marginLeft: 2,
439 minWidth: 80,
440 background: (!exchangeMagic || isNaN(exchangeMagic) || Number(exchangeMagic) <= 0 || Number(exchangeMagic) > userStats.magic) ? '#ccc' : undefined
441 }}
442 >兑换</Button>
443 </div>
444 <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload?.toFixed(2)} GB</b></div>
445 <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download?.toFixed(2)} GB</b></div>
446 <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.download === 0 ? "∞" : (userStats.upload / userStats.download).toFixed(2)}</b></div>
447 <div>VIP下载次数:<b style={{ color: '#1976d2' }}>{userStats.vip_downloads}</b></div>
448 </div>
449 </div>
450 {/* 右上:个人上传种子列表 */}
451 <div style={{
452 gridColumn: '2 / 3',
453 gridRow: '1 / 2',
454 background: '#fff',
455 borderRadius: 20,
456 boxShadow: '0 6px 32px #e0e7ff',
457 padding: '32px 36px',
458 minHeight: 420,
459 display: 'flex',
460 flexDirection: 'column'
461 }}>
462 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18, letterSpacing: 1 }}>个人上传种子列表</h3>
463 <div style={{
464 border: '1.5px dashed #b2b2b2',
465 borderRadius: 14,
466 minHeight: 80,
467 padding: 16,
468 background: '#f8faff'
469 }}>
rhjc6a4ee02025-06-06 00:45:18 +0800470 {userSeeds.length === 0 ? (
471 <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>
472 ) : (
473 <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
474 {userSeeds.map((seed, idx) => (
475 <li
wht15642182025-06-08 00:16:52 +0800476 key={seed.seedid || idx}
477 style={{
478 display: 'flex',
479 alignItems: 'center',
480 padding: '12px 0',
481 borderBottom: idx === userSeeds.length - 1 ? 'none' : '1px solid #e0e7ff',
482 cursor: 'pointer',
483 transition: 'background 0.15s'
484 }}
rhjc6a4ee02025-06-06 00:45:18 +0800485 onClick={e => {
rhjc6a4ee02025-06-06 00:45:18 +0800486 if (e.target.classList.contains('delete-btn')) return;
223011330f9623f2025-06-06 00:22:05 +0800487 navigate(`/torrent/${seed.seedid}`);
rhjc6a4ee02025-06-06 00:45:18 +0800488 }}
wht15642182025-06-08 00:16:52 +0800489 onMouseOver={e => e.currentTarget.style.background = '#f3f6ff'}
490 onMouseOut={e => e.currentTarget.style.background = ''}
rhjc6a4ee02025-06-06 00:45:18 +0800491 >
492 <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
493 <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
223011330f9623f2025-06-06 00:22:05 +0800494 <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.downloadtimes}</span>
wht15642182025-06-08 00:16:52 +0800495 <Button
rhjc6a4ee02025-06-06 00:45:18 +0800496 className="delete-btn"
wht15642182025-06-08 00:16:52 +0800497 variant="contained"
498 color="error"
499 size="small"
500 sx={{ marginLeft: 2, borderRadius: 1, minWidth: 60 }}
501 onClick={e => {
rhjc6a4ee02025-06-06 00:45:18 +0800502 e.stopPropagation();
wht15642182025-06-08 00:16:52 +0800503 handleDeleteSeed(seed.seedid);
rhjc6a4ee02025-06-06 00:45:18 +0800504 }}
wht15642182025-06-08 00:16:52 +0800505 >删除</Button>
rhjc6a4ee02025-06-06 00:45:18 +0800506 </li>
507 ))}
508 </ul>
509 )}
956303669a32fc2c2025-06-02 19:45:53 +0800510 </div>
511 </div>
wht15642182025-06-08 00:16:52 +0800512 {/* 右下:个人收藏种子列表 */}
513 <div style={{
514 gridColumn: '2 / 3',
515 gridRow: '2 / 3',
516 background: '#fff',
517 borderRadius: 20,
518 boxShadow: '0 6px 32px #e0e7ff',
519 padding: '32px 36px',
520 minHeight: 320,
521 display: 'flex',
522 flexDirection: 'column'
523 }}>
524 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18, letterSpacing: 1 }}>个人收藏种子列表</h3>
525 <div style={{
526 border: '1.5px dashed #b2b2b2',
527 borderRadius: 14,
528 minHeight: 80,
529 padding: 16,
530 background: '#f8faff'
531 }}>
532 {userFavorites.length === 0 ? (
533 <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无收藏种子)</div>
534 ) : (
535 <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
536 {userFavorites.map((seed, idx) => (
537 <li
538 key={seed.seedid || idx}
539 style={{
540 display: 'flex',
541 alignItems: 'center',
542 padding: '12px 0',
543 borderBottom: idx === userFavorites.length - 1 ? 'none' : '1px solid #e0e7ff',
544 cursor: 'pointer',
545 transition: 'background 0.15s'
546 }}
547 onClick={e => {
548 navigate(`/torrent/${seed.seedid}`);
549 }}
550 onMouseOver={e => e.currentTarget.style.background = '#f3f6ff'}
551 onMouseOut={e => e.currentTarget.style.background = ''}
552 >
553 <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
554 <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
555 <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.downloadtimes}</span>
556 </li>
557 ))}
558 </ul>
559 )}
956303669a32fc2c2025-06-02 19:45:53 +0800560 </div>
561 </div>
wht15642182025-06-08 00:16:52 +0800562 {/* 申诉弹窗 */}
563 <Dialog open={appealOpen} onClose={() => setAppealOpen(false)}>
564 <DialogTitle>提交申诉</DialogTitle>
565 <DialogContent>
566 <div style={{ marginBottom: 16 }}>
567 <TextField
568 label="申诉主题"
569 fullWidth
570 value={appealTitle}
571 onChange={e => setAppealTitle(e.target.value)}
572 size="small"
573 />
574 </div>
575 <div>
576 <input
577 type="file"
578 onChange={e => setAppealFile(e.target.files[0])}
579 style={{ marginTop: 8 }}
580 />
581 </div>
582 </DialogContent>
583 <DialogActions>
584 <Button onClick={handleAppealSubmit} variant="contained" color="primary" disabled={!appealTitle || !appealFile}>提交</Button>
585 <Button onClick={() => setAppealOpen(false)} variant="outlined">取消</Button>
586 </DialogActions>
587 </Dialog>
956303669a32fc2c2025-06-02 19:45:53 +0800588 </div>
589 );
590}