blob: 8ea3f6b5d213397c96089ccbb04c8fb730e141d8 [file] [log] [blame]
wht2bf8f802025-06-08 15:52:18 +08001import React, { useState, useEffect } from "react";
whtdc90a032025-06-08 03:03:52 +08002import { useParams } from "react-router-dom";
wht2bf8f802025-06-08 15:52:18 +08003import { API_BASE_URL } from "./config";
whtdc90a032025-06-08 03:03:52 +08004
wht2bf8f802025-06-08 15:52:18 +08005// 求种任务示例数据(作为后备数据)
whtdc90a032025-06-08 03:03:52 +08006const begSeedList = [
7 {
8 beg_id: "beg001",
9 info: "求《三体》高清资源",
10 beg_count: 5,
11 reward_magic: 100,
12 deadline: "2025-06-10T23:59:59",
13 has_match: 0,
14 },
15 {
16 beg_id: "beg002",
17 info: "求《灌篮高手》国语配音版",
18 beg_count: 3,
19 reward_magic: 50,
20 deadline: "2024-05-01T23:59:59",
21 has_match: 1,
22 },
23 {
24 beg_id: "beg003",
25 info: "求《黑暗之魂3》PC版种子",
26 beg_count: 2,
27 reward_magic: 80,
28 deadline: "2024-04-01T23:59:59",
29 has_match: 0,
30 },
31];
32
33// SubmitSeed表示例数据
34const submitSeedList = [
35 { beg_id: "beg001", seed_id: "seed001", votes: 3 },
36 { beg_id: "beg001", seed_id: "seed002", votes: 1 },
37 { beg_id: "beg002", seed_id: "seed003", votes: 2 },
38];
39
40// 种子信息映射
41const seedInfoMap = {
42 seed001: { title: "三体 1080P 蓝光", subtitle: "高码率无水印" },
43 seed002: { title: "三体 720P", subtitle: "清晰版" },
44 seed003: { title: "灌篮高手 国语配音", subtitle: "全剧集" },
45};
46
47export default function BegInfo() {
48 const { begid } = useParams();
wht2bf8f802025-06-08 15:52:18 +080049 const [beg, setBeg] = useState(null);
50 const [loading, setLoading] = useState(true);
51 const [error, setError] = useState(null);
52 const [seeds, setSeeds] = useState([]);
53 const [seedInfoMap, setSeedInfoMap] = useState({});
whtdc90a032025-06-08 03:03:52 +080054 const [showForm, setShowForm] = useState(false);
wht2bf8f802025-06-08 15:52:18 +080055 const [userSeeds, setUserSeeds] = useState([]);
56 const [loadingUserSeeds, setLoadingUserSeeds] = useState(false);
whtdc90a032025-06-08 03:03:52 +080057 const [formData, setFormData] = useState({
wht2bf8f802025-06-08 15:52:18 +080058 selectedSeedId: "",
whtdc90a032025-06-08 03:03:52 +080059 });
60
wht2bf8f802025-06-08 15:52:18 +080061 // 从后端获取求种详情
62 const fetchBegSeedDetail = async () => {
63 setLoading(true);
64 try {
65 const response = await fetch(`${API_BASE_URL}/api/begseed-detail?begid=${begid}`);
66 if (!response.ok) {
67 throw new Error(`请求失败,状态码: ${response.status}`);
68 }
69 const data = await response.json();
70
71
72 // 格式化数据以匹配前端期望的格式
73 const formattedBeg = {
74 beg_id: data.beg_id || data.begid || data.id,
75 info: data.info || data.description || data.content,
76 beg_count: data.beg_count || data.begCount || 1,
77 reward_magic: data.reward_magic || data.rewardMagic || data.magic,
78 deadline: data.deadline || data.endtime,
79 has_match: data.has_match || data.hasMatch || data.completed || 0,
80 };
81
82 setBeg(formattedBeg);
83 setError(null);
84 } catch (err) {
85 console.error('获取求种详情失败:', err);
86 setError(err.message);
87 // 如果API调用失败,使用默认数据
88 const fallbackBeg = begSeedList.find((b) => b.beg_id === begid);
89 setBeg(fallbackBeg || null);
90 } finally {
91 setLoading(false);
92 }
93 };
94
95 // 从后端获取已提交的种子列表
96 const fetchSubmittedSeeds = async () => {
97 try {
98 const response = await fetch(`${API_BASE_URL}/api/begseed-submissions?begid=${begid}`);
99 if (!response.ok) {
100 throw new Error(`请求失败,状态码: ${response.status}`);
101 }
102 const data = await response.json();
103 console.log('获取到的种子提交数据:', data);
104
105 // 新的数据结构:数组,每个元素包含seed对象和votes字段
106 const submissions = Array.isArray(data) ? data : [];
107
108 // 格式化种子数据
109 const formattedSeeds = submissions.map(item => ({
110 seed_id: item.seed?.seedid || item.seedid,
111 beg_id: begid,
112 votes: item.votes || 0, // 每个种子单独的投票数
113 title: item.seed?.title || item.title || "未知标题",
114 subtitle: item.seed?.subtitle || item.subtitle || "无简介",
115 seedsize: item.seed?.seedsize || item.seedsize,
116 downloadtimes: item.seed?.downloadtimes || item.downloadtimes || 0,
117 url: item.seed?.url || item.url,
118 user: item.seed?.user || item.user
119 }));
120
121 // 构建种子信息映射
122 const newSeedInfoMap = {};
123 submissions.forEach(item => {
124 const seedId = item.seed?.seedid || item.seedid;
125 if (seedId) {
126 newSeedInfoMap[seedId] = {
127 title: item.seed?.title || item.title || "未知标题",
128 subtitle: item.seed?.subtitle || item.subtitle || "无简介",
129 };
130 }
131 });
132
133 setSeeds(formattedSeeds);
134 setSeedInfoMap(newSeedInfoMap);
135 } catch (err) {
136 console.error('获取种子提交列表失败:', err);
137 // 如果API调用失败,使用默认数据
138 const fallbackSeeds = submitSeedList.filter((s) => s.beg_id === begid);
139 setSeeds(fallbackSeeds);
140 setSeedInfoMap(seedInfoMap);
141 }
142 };
143
144 // 组件挂载时获取数据
145 useEffect(() => {
146 fetchBegSeedDetail();
147 fetchSubmittedSeeds();
148 }, [begid]);
149
150 // 加载状态
151 if (loading) {
152 return (
153 <div className="container">
154 <div style={{ textAlign: "center", margin: "40px 0", color: "#666" }}>
155 正在加载求种详情...
156 </div>
157 </div>
158 );
159 }
160
161 // 未找到求种信息
162 if (!beg) {
163 return (
164 <div className="container">
165 <div style={{ padding: 40, textAlign: "center", color: "#666" }}>
166 未找到该求种信息
167 </div>
168 </div>
169 );
170 }
whtdc90a032025-06-08 03:03:52 +0800171
172 const isExpired = new Date(beg.deadline) < new Date();
173 const isFinished = beg.has_match === 1;
174 const isActive = !isExpired && !isFinished;
175
wht2bf8f802025-06-08 15:52:18 +0800176 // 获取用户的所有种子
177 const fetchUserSeeds = async () => {
178 setLoadingUserSeeds(true);
179 try {
180 // 获取用户ID
181 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
182 const userId = match ? match[2] : null;
183
184 if (!userId) {
185 alert("请先登录后再获取种子列表");
186 setLoadingUserSeeds(false);
187 return;
188 }
189
190 const response = await fetch(`${API_BASE_URL}/api/user-seeds?userid=${userId}`);
191 if (!response.ok) {
192 throw new Error(`请求失败,状态码: ${response.status}`);
193 }
194 const data = await response.json();
195
196 // 格式化种子数据
197 const formattedSeeds = Array.isArray(data) ? data.map(seed => ({
198 seedid: seed.seedid || seed.id,
199 title: seed.title || "未知标题",
200 subtitle: seed.subtitle || "无简介",
201 seedsize: seed.seedsize,
202 downloadtimes: seed.downloadtimes || 0,
203 url: seed.url
204 })) : [];
205
206 setUserSeeds(formattedSeeds);
207 } catch (err) {
208 console.error('获取用户种子失败:', err);
209 alert(`获取种子列表失败: ${err.message}`);
210 } finally {
211 setLoadingUserSeeds(false);
212 }
213 };
214
215 // 投票功能(发送到后端)
216 const handleVote = async (seed_id) => {
217 try {
218 // 获取用户ID
219 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
220 const userId = match ? match[2] : null;
221
222 if (!userId) {
223 alert("请先登录后再投票");
224 return;
225 }
226
227 const response = await fetch(`${API_BASE_URL}/api/vote-seed`, {
228 method: 'POST',
229 headers: {
230 'Content-Type': 'application/json',
231 },
232 body: JSON.stringify({
233 userid: userId,
234 seedid: seed_id,
235 begid: begid,
236 }),
237 });
238
239
240 if (response.ok) {
241 // 投票成功,重新获取数据以更新投票计数
242 await fetchSubmittedSeeds();
243 alert("投票成功!");
244 } else if (response.status === 409) {
245 alert("您已投过票,不能重复投票");
246 }
247 else {
248 const errorData = await response.json();
249 alert(`投票失败: ${errorData.message || '未知错误'}`);
250 }
251 } catch (err) {
252 console.error('投票失败:', err);
253 // 如果后端调用失败,更新本地状态作为后备
254 setSeeds((prev) =>
255 prev.map((s) =>
256 s.seed_id === seed_id ? { ...s, votes: s.votes + 1 } : s
257 )
258 );
259 alert("投票成功(前端演示)");
260 }
whtdc90a032025-06-08 03:03:52 +0800261 };
262
263 // 上传表单处理
264 const handleFormChange = (e) => {
wht2bf8f802025-06-08 15:52:18 +0800265 const { name, value } = e.target;
266 setFormData((f) => ({ ...f, [name]: value }));
whtdc90a032025-06-08 03:03:52 +0800267 };
268
wht2bf8f802025-06-08 15:52:18 +0800269 const handleSubmitSeed = async (e) => {
whtdc90a032025-06-08 03:03:52 +0800270 e.preventDefault();
wht2bf8f802025-06-08 15:52:18 +0800271
272 if (!formData.selectedSeedId) {
273 alert("请选择一个种子");
274 return;
275 }
276
277 try {
278 // 获取用户ID
279 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
280 const userId = match ? match[2] : null;
281
282 if (!userId) {
283 alert("请先登录后再提交种子");
284 return;
285 }
286 // console.log('提交种子数据:', {
287 // userid: userId,
288 // begid: begid,
289 // seedid: formData.selectedSeedId,
290 // });
291
292 const response = await fetch(`${API_BASE_URL}/api/submit-seed`, {
293 method: 'POST',
294 headers: {
295 'Content-Type': 'application/json',
296 },
297 body: JSON.stringify({
298 userid: userId,
299 begid: begid,
300 seedid: formData.selectedSeedId,
301 }),
302 });
303
304 if (response.ok) {
305 // 提交成功,重新获取所有数据以刷新页面
306 await Promise.all([
307 fetchBegSeedDetail(),
308 fetchSubmittedSeeds()
309 ]);
310 setShowForm(false);
311 setFormData({ selectedSeedId: "" });
312 setUserSeeds([]);
313 alert("提交成功!");
314 } else {
315 const errorData = await response.json();
316 alert(`提交失败: ${errorData.message || '未知错误'}`);
317 }
318 } catch (err) {
319 console.error('提交种子失败:', err);
320 // 如果后端调用失败,使用前端演示逻辑
321 const newSeedId = "seed" + Math.floor(Math.random() * 10000);
322
323 // 从用户种子列表中找到选中种子的信息
324 const selectedSeed = userSeeds.find(seed => seed.seedid === formData.selectedSeedId);
325
326 setSeeds((prev) => [
327 ...prev,
328 {
329 beg_id: begid,
330 seed_id: newSeedId,
331 votes: 0,
332 title: selectedSeed?.title || "未知标题",
333 subtitle: selectedSeed?.subtitle || "无简介",
334 seedsize: selectedSeed?.seedsize,
335 downloadtimes: selectedSeed?.downloadtimes || 0,
336 url: selectedSeed?.url,
337 user: { username: "当前用户" }
338 },
339 ]);
340
341 setSeedInfoMap(prev => ({
342 ...prev,
343 [newSeedId]: {
344 title: selectedSeed?.title || "未知标题",
345 subtitle: selectedSeed?.subtitle || "无简介",
346 }
347 }));
348
349 setShowForm(false);
350 setFormData({ selectedSeedId: "" });
351 setUserSeeds([]);
352 alert("提交成功(前端演示)");
353 }
whtdc90a032025-06-08 03:03:52 +0800354 };
355
356 return (
357 <div className="container">
358 <h1 style={{ margin: "24px 0 32px 0", color: "#1976d2" }}>
359 求种详情
360 </h1>
wht2bf8f802025-06-08 15:52:18 +0800361
362 {/* 错误状态 */}
363 {error && (
364 <div style={{
365 textAlign: "center",
366 margin: "20px 0",
367 padding: "10px",
368 background: "#ffebee",
369 color: "#c62828",
370 borderRadius: "4px"
371 }}>
372 加载失败: {error} (已显示默认数据)
373 </div>
374 )}
375
whtdc90a032025-06-08 03:03:52 +0800376 <div
377 style={{
378 background: "#e3f7e7",
379 border: "1.5px solid #b2d8ea",
380 borderRadius: 12,
381 padding: 24,
382 maxWidth: 600,
383 margin: "0 auto 32px auto",
384 boxShadow: "0 2px 8px #e0e7ff",
385 }}
386 >
387 <div style={{ fontWeight: 600, fontSize: 20, marginBottom: 12 }}>
388 {beg.info}
389 </div>
390 <div>求种人数:{beg.beg_count}</div>
391 <div>悬赏魔力值:{beg.reward_magic}</div>
392 <div>截止时间:{new Date(beg.deadline).toLocaleString()}</div>
393 <div>
394 状态:
395 {isFinished
396 ? "已完成"
397 : isExpired
398 ? "已过期"
399 : "进行中"}
400 </div>
401 </div>
402
403 <h2 style={{ margin: "24px 0 12px 0" }}>已提交种子</h2>
wht2bf8f802025-06-08 15:52:18 +0800404 <table className="movie-table" style={{ maxWidth: 1000, margin: "0 auto" }}>
whtdc90a032025-06-08 03:03:52 +0800405 <thead>
406 <tr>
407 <th>标题</th>
408 <th>简介</th>
wht2bf8f802025-06-08 15:52:18 +0800409 <th>文件大小</th>
410 <th>下载次数</th>
whtdc90a032025-06-08 03:03:52 +0800411 <th>投票数</th>
wht2bf8f802025-06-08 15:52:18 +0800412 <th>上传者</th>
whtdc90a032025-06-08 03:03:52 +0800413 <th>操作</th>
414 </tr>
415 </thead>
416 <tbody>
417 {seeds.length === 0 ? (
418 <tr>
wht2bf8f802025-06-08 15:52:18 +0800419 <td colSpan={7} style={{ textAlign: "center" }}>暂无提交的种子</td>
whtdc90a032025-06-08 03:03:52 +0800420 </tr>
421 ) : (
422 seeds.map((s) => (
423 <tr key={s.seed_id}>
wht2bf8f802025-06-08 15:52:18 +0800424 <td>
425 <a href={`/torrent/${s.seed_id}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
426 {s.title}
427 </a>
428 </td>
429 <td>{s.subtitle || "无简介"}</td>
430 <td>{s.seedsize ? `${s.seedsize} MB` : "未知"}</td>
431 <td>{s.downloadtimes || 0}</td>
432 <td style={{ fontWeight: 'bold', color: '#1976d2' }}>{s.votes || 0}</td>
433 <td>{s.user?.username || "未知用户"}</td>
whtdc90a032025-06-08 03:03:52 +0800434 <td>
435 {isActive ? (
436 <button
437 onClick={() => handleVote(s.seed_id)}
438 style={{
439 background: "#1976d2",
440 color: "#fff",
441 border: "none",
442 borderRadius: 6,
443 padding: "6px 18px",
444 fontWeight: 500,
445 cursor: "pointer",
446 transition: "background 0.2s",
447 }}
448 >
449 投票
450 </button>
451 ) : (
452 <span style={{ color: "#b0b0b0" }}>不可投票</span>
453 )}
454 </td>
455 </tr>
456 ))
457 )}
458 </tbody>
459 </table>
460
wht2bf8f802025-06-08 15:52:18 +0800461 {/* 显示总投票数 */}
462 {seeds.length > 0 && (
463 <div style={{ textAlign: "center", margin: "16px 0", color: "#666" }}>
464 总投票数: {seeds.reduce((total, seed) => total + (seed.votes || 0), 0)}
465 </div>
466 )}
467
whtdc90a032025-06-08 03:03:52 +0800468 {isActive && (
469 <div style={{ margin: "32px 0", textAlign: "center" }}>
470 <button
wht2bf8f802025-06-08 15:52:18 +0800471 onClick={() => {
472 setShowForm(true);
473 fetchUserSeeds();
474 }}
whtdc90a032025-06-08 03:03:52 +0800475 style={{
476 fontSize: 18,
477 padding: "12px 36px",
478 background: "linear-gradient(90deg, #42a5f5 0%, #1976d2 100%)",
479 color: "#fff",
480 border: "none",
481 borderRadius: 8,
482 fontWeight: 600,
483 boxShadow: "0 2px 8px #b2d8ea",
484 cursor: "pointer",
485 transition: "background 0.2s",
486 }}
487 >
wht2bf8f802025-06-08 15:52:18 +0800488 提交种子
whtdc90a032025-06-08 03:03:52 +0800489 </button>
490 </div>
491 )}
492
493 {showForm && isActive && (
494 <div
495 style={{
496 background: "#fff",
497 border: "1.5px solid #b2d8ea",
498 borderRadius: 12,
499 padding: 24,
500 maxWidth: 480,
501 margin: "0 auto",
502 boxShadow: "0 2px 8px #e0e7ff",
503 }}
504 >
wht2bf8f802025-06-08 15:52:18 +0800505 <h3 style={{ color: "#1976d2", marginBottom: 18 }}>选择种子</h3>
506
507 {/* 加载用户种子状态 */}
508 {loadingUserSeeds && (
509 <div style={{ textAlign: "center", margin: "16px 0", color: "#666" }}>
510 正在加载您的种子列表...
511 </div>
512 )}
513
514 {/* 选择已有种子 */}
515 {userSeeds.length > 0 ? (
516 <div style={{ marginBottom: 24 }}>
517 <div style={{ marginBottom: 16 }}>
518 <label style={{ display: "inline-block", width: 80, fontWeight: 500 }}>选择种子:</label>
519 <select
520 name="selectedSeedId"
521 value={formData.selectedSeedId}
522 onChange={handleFormChange}
523 style={{
524 padding: "8px 12px",
525 borderRadius: 6,
526 border: "1px solid #b2d8ea",
527 width: 300,
528 background: "#fff",
529 fontSize: 14,
530 }}
531 >
532 <option value="">请选择一个种子</option>
533 {userSeeds.map((seed) => (
534 <option key={seed.seedid} value={seed.seedid}>
535 {seed.title} - {seed.subtitle || "无简介"} ({seed.seedsize ? `${seed.seedsize} MB` : "未知大小"})
536 </option>
537 ))}
538 </select>
539 </div>
540 {formData.selectedSeedId && (
541 <div style={{
542 padding: 12,
543 background: "#e8f5e8",
544 borderRadius: 6,
545 border: "1px solid #4caf50",
546 color: "#2e7d32"
547 }}>
548 已选择种子,点击提交即可使用此种子
549 </div>
550 )}
551 </div>
552 ) : (
553 !loadingUserSeeds && (
554 <div style={{
555 textAlign: "center",
556 margin: "20px 0",
557 padding: "16px",
558 background: "#fff3cd",
559 color: "#856404",
560 border: "1px solid #ffeaa7",
561 borderRadius: 6
562 }}>
563 您还没有上传过种子,无法参与悬赏
564 </div>
565 )
566 )}
567
whtdc90a032025-06-08 03:03:52 +0800568 <form onSubmit={handleSubmitSeed}>
whtdc90a032025-06-08 03:03:52 +0800569 <div style={{ marginTop: 18 }}>
570 <button
571 type="submit"
wht2bf8f802025-06-08 15:52:18 +0800572 disabled={!formData.selectedSeedId}
whtdc90a032025-06-08 03:03:52 +0800573 style={{
wht2bf8f802025-06-08 15:52:18 +0800574 background: formData.selectedSeedId ? "#1976d2" : "#b0b0b0",
whtdc90a032025-06-08 03:03:52 +0800575 color: "#fff",
576 border: "none",
577 borderRadius: 6,
578 padding: "8px 28px",
579 fontWeight: 500,
580 fontSize: 16,
581 marginRight: 18,
wht2bf8f802025-06-08 15:52:18 +0800582 cursor: formData.selectedSeedId ? "pointer" : "not-allowed",
whtdc90a032025-06-08 03:03:52 +0800583 }}
584 >
wht2bf8f802025-06-08 15:52:18 +0800585 提交种子
whtdc90a032025-06-08 03:03:52 +0800586 </button>
587 <button
588 type="button"
wht2bf8f802025-06-08 15:52:18 +0800589 onClick={() => {
590 setShowForm(false);
591 setFormData({ selectedSeedId: "" });
592 setUserSeeds([]);
593 }}
whtdc90a032025-06-08 03:03:52 +0800594 style={{
595 background: "#b0b0b0",
596 color: "#fff",
597 border: "none",
598 borderRadius: 6,
599 padding: "8px 28px",
600 fontWeight: 500,
601 fontSize: 16,
602 cursor: "pointer",
603 }}
604 >
605 取消
606 </button>
607 </div>
608 </form>
609 </div>
610 )}
611 </div>
612 );
613}