blob: be4b76b5408deaeb63d3df9d7f2ceba22108104b [file] [log] [blame]
刘嘉昕9cdeca22025-06-03 16:54:30 +08001// import { useState, useEffect } from 'react';
2// import { Link } from 'react-router-dom';
3// import axios from 'axios';
4
5// function TorrentList() {
6// const [torrents, setTorrents] = useState([]);
7// const [categories, setCategories] = useState([]);
8// const [selectedCategory, setSelectedCategory] = useState('');
9
10// // 获取所有分类
11// useEffect(() => {
12// axios.get('http://localhost:8080/categories') // 假设这个接口返回所有分类
13// .then(res => setCategories(res.data))
14// .catch(err => console.error('获取分类失败', err));
15// }, []);
16
17// // 获取种子(根据分类筛选)
18// useEffect(() => {
19// const url = selectedCategory
20// ? `http://localhost:8080/torrent/listByCategory?categoryid=${selectedCategory}`
21// : 'http://localhost:8080/torrent/list';
22
23// axios.get(url)
24// .then(res => setTorrents(res.data))
25// .catch(err => console.error('获取种子失败', err));
26// }, [selectedCategory]);
27// console.log(torrents);
28
29
30// return (
31// <div className="p-4">
32// <div className="mb-4">
33// <label className="mr-2 font-medium">选择分类:</label>
34// <select
35// value={selectedCategory}
36// onChange={e => setSelectedCategory(e.target.value)}
37// className="border rounded px-2 py-1"
38// >
39// <option value="">全部</option>
40// {categories.map(cat => (
41// <option key={cat.categoryid} value={cat.categoryid}>
42// {cat.category_name}
43// </option>
44// ))}
45// </select>
46// </div>
47// <table className="w-full border-collapse">
48// <thead>
49// <tr className="bg-gray-200">
50// <th className="p-2 border">名称</th>
51// <th className="p-2 border">上传者</th>
52// <th className="p-2 border">描述</th>
53// <th className="p-2 border">上传时间</th>
54// <th className="p-2 border">下载次数</th>
55// <th className="p-2 border">促销方式</th>
56// <th className="p-2 border">操作</th>
57// </tr>
58// </thead>
59// <tbody>
60// {torrents.map(t => (
61// <tr key={t.torrentid} className="border-t hover:bg-gray-100">
62// <td className="p-2 border">{t.filename}</td>
63// <td className="p-2 border">{t.uploader_id}</td>
64// <td className="p-2 border">{t.description}</td>
65// <td className="p-2 border">{new Date(t.uploadTime).toLocaleString()}</td>
66// <td className="p-2 border">{t.downloadCount}</td>
67// <td className="p-2 border">
68// {(() => {
69// switch(t.promotionid) {
70// case 1: return '上传加倍';
71// case 2: return '下载免费';
72// case 3: return '下载减半';
73// case 0: return '没有促销';
74// default: return '没有促销';
75// }
76// })()}
77// </td>
78// <td className="p-2 border">
79// <a
80// href={`http://localhost:8080/torrent/download/${t.torrentid}`}
81// className="text-blue-500 hover:underline"
82// target="_blank"
83// rel="noreferrer"
84// >
85// 下载
86// </a>
87// </td>
88// <Link
89// to={`/torrent/${t.torrentid}`}
90// className="text-green-600 hover:underline"
91// >
92// 查看详情
93// </Link>
94// </tr>
95// ))}
96// </tbody>
97// </table>
98// </div>
99// );
100// }
101
102// export default TorrentList;
103// import { useState, useEffect } from 'react';
104// import axios from 'axios';
105
106// function TorrentList() {
107// const [torrents, setTorrents] = useState([]);
108// const [categories, setCategories] = useState([]);
109// const [selectedCategory, setSelectedCategory] = useState('');
110// const [filters, setFilters] = useState({});
111// const [showSuccess, setShowSuccess] = useState(false);
112// const softwaregenres = [
113// { value: '系统软件', label: '系统软件' },
114// { value: '应用软件', label: '应用软件' },
115// { value: '游戏软件', label: '游戏软件' },
116// { value: '驱动程序', label: '驱动程序' },
117// { value: '办公软件', label: '办公软件' },
118// { value: '其他', label: '其他' },
119// ]
120// const softwareplatforms = [
121// { value: 'Windows', label: 'Windows' },
122// { value: 'Mac', label: 'Mac' },
123// { value: 'Linux', label: 'Linux' },
124// { value: 'Android', label: 'Android' },
125// { value: 'iOS', label: 'iOS' },
126// { value: '其他', label: '其他' },
127// ]
128// const softwareformats = [
129// { value: 'EXE', label: 'EXE' },
130// { value: 'DMG', label: 'DMG' },
131// { value: '光盘镜像', label: '光盘镜像' },
132// { value: 'APK', label: 'APK' },
133// { value: 'IPA', label: 'IPA' },
134// { value: '其他', label: '其他' },
135// ]
136// const sourceTypes = [
137//     { value: 'CCTV', label: 'CCTV' },
138//     { value: '卫视', label: '卫视' },
139//     { value: '国家地理', label: '国家地理' },
140//     { value: 'BBC', label: 'BBC' },
141//     { value: 'Discovery', label: 'Discovery' },
142//     { value: '其他', label: '其他' },
143//   ]
144//   const othergenres = [
145//     { value: '电子书', label: '电子书' },
146//     { value: '视频', label: '视频' },
147//     { value: 'MP3', label: 'MP3' },
148//     { value: '图片', label: '图片' },
149//     { value: '其他', label: '其他' },
150//   ]
151// const resolutions = [
152// { value: '720p', label: '720p' },
153// { value: '1080p', label: '1080p' },
154// { value: '2K', label: '2K' },
155// { value: '4K', label: '4K' },
156// { value: '8K', label: '8K' },
157// { value: '其他', label: '其他' },
158// ];
159
160// // 每个分类的筛选字段配置
161// const categoryFiltersConfig = {
162// 1: [ // Movie 电影
163// { id: 'resolution', label: '分辨率', type: 'select', options: ['1080p', '4K', '720p', '其他'] },
164// { id: 'codecFormat', label: '编码格式', type: 'select', options: ['H.264', 'H.265', 'AV1', 'VC1', 'X264', '其他'] },
165// { id: 'region', label: '地区', type: 'select', options: ['大陆', '港台', '欧美', '日韩', '其他'] },
166// { id: 'genre', label: '类型', type: 'select', options: ['动作', '喜剧', '爱情', '科幻', '恐怖','动作', '冒险', '历史', '悬疑', '其他'] },
167// ],
168// 2: [ // TV 剧集
169// { id: 'region', label: '地区', type: 'select', options: ['大陆', '港台', '欧美', '日韩', '其他'] },
170// { id: 'format', label: '格式', type: 'select', options: resolutions },
171// { id: 'genre', label: '类型', type: 'select', options: ['动作', '喜剧', '爱情', '科幻', '恐怖','动作', '冒险', '历史', '悬疑', '其他'] },
172// ],
173// 3: [ // Music 音乐
174// { id: 'genre', label: '类型', type: 'select', options: ['专辑', '单曲', 'EP', '现场', '其他'] },
175// { id: 'style', label: '风格', type: 'select', options: ['流行', '摇滚', '电子', '古典', '爵士', '民谣', '说唱', '其他'] },
176// ],
177// 4: [ // Anime 动漫
178// { id: 'genre', label: '类型', type: 'select', options: ['新番连载', '剧场版', 'OVA', '完结动漫', '其他'] },
179// { id: 'format', label: '格式', type: 'select', options: ['ZIP', 'RAR', '7Z', 'MKV', 'MP4', '其他'] },
180// { id: 'resolution', label: '分辨率', type: 'select', options: ['720P', '1080P', '4K', '其他'] },
181// ],
182// 5: [ // Game 游戏
183// { id: 'platform', label: '平台', type: 'select', options: ['PC', 'PS5', 'Xbox', 'Switch', '手机', '其他'] },
184// { id: 'genre', label: '类型', type: 'select', options: ['角色扮演', '射击', '冒险', '策略', '体育', '桌面游戏', '其他'] },
185// { id: 'language', label: '语言', type: 'select', options: ['中文', '英文', '日文', '其他'] },
186// { id: 'dataType', label: '数据类型', type: 'select', options: ['压缩包', '补丁', '安装包', 'nds', '其他'] },
187// ],
188// 6: [ // 综艺
189// { id: 'isMainland', label: '是否为大陆综艺', type: 'select', options: ['是','不是'] },
190// { id: 'format', label: '格式', type: 'select', options: ['1080P', '4K', 'HD', '其他'] },
191// { id: 'genre', label: '类型', type: 'select', options: ['真人秀', '选秀','访谈', '音乐', '游戏', '其他'] },
192// ],
193// 7: [ // 学习
194// { id: 'genre', label: '类型', type: 'select', options: ['计算机','软件','人文','外语','理工科','其他'] },
195// { id: 'learningformat', label: '格式', type: 'select', options: ['PDF','EPUB','视频','音频','PPT','其他'] },
196// ],
197// 8: [ // 体育
198// { id: 'eventType', label: '赛事类型', type: 'select', options: ['足球', '篮球', '网球', '乒乓球', '羽毛球', '其他'] },
199// { id: 'region', label: '地区', type: 'select', options: ['亚洲', '欧洲', '美洲', '其他'] },
200// ],
201// 9: [ // 其他
202// { id: 'otherGenre', label: '类型', type: 'select', options: othergenres },
203// ],
204// 10: [ // 纪录片
205// { id: 'source', label: '来源', type: 'select', options: sourceTypes },
206// { id: 'resolution', label: '分辨率', type: 'select', options: resolutions },
207// ],
208// 11: [ // 软件
209// { id: 'softwsreplatform', label: '平台', type: 'select', options: softwareplatforms },
210// { id: 'softwareGenre', label: '软件类型', type: 'select', options: softwaregenres },
211// { id: 'softwareFormat', label: '软件格式', type: 'select', options: softwareformats },
212// ],
213// // 其他分类...
214// };
215
216// // 获取所有分类
217// useEffect(() => {
218// axios.get('http://localhost:8080/categories')
219// .then(res => setCategories(res.data))
220// .catch(err => console.error('加载分类失败', err));
221// }, []);
222
223// // 根据选择的分类显示不同的表单字段
224// useEffect(() => {
225// setFilters({}); // 清空筛选条件
226// }, [selectedCategory]);
227
228// const handleFilterChange = (e) => {
229// const { name, value } = e.target;
230// setFilters(prev => ({ ...prev, [name]: value }));
231// };
232
233// const filteredTorrents = (torrents) => {
234// return torrents.filter(torrent => {
235// if (!selectedCategory) return true; // 如果没有选择分类,显示所有
236// if (torrent.categoryid !== parseInt(selectedCategory)) return false;
237
238// // 根据筛选条件过滤
239// for (const [key, value] of Object.entries(filters)) {
240// if (value && torrent[key] !== value) {
241// return false;
242// }
243// }
244// return true;
245// });
246// };
247
248// // 获取种子(根据分类筛选)
249// useEffect(() => {
250// let url = selectedCategory
251// ? `http://localhost:8080/torrent/listByCategory?categoryid=${selectedCategory}`
252// : 'http://localhost:8080/torrent/list';
253
254// // 添加筛选条件到 URL
255// Object.entries(filters).forEach(([key, value]) => {
256// if (value) {
257// url += `&${key}=${encodeURIComponent(value)}`;
258// }
259// });
260
261// axios.get(url)
262// .then(res => {
263// setTorrents(res.data);
264// })
265// .catch(err => console.error('获取种子失败', err));
266// }, [selectedCategory, filters]);
267
268// const handleDownload = (torrentId) => {
269// window.open(`http://localhost:8080/torrent/download/${torrentId}`, '_blank');
270// };
271
272// return (
273// <div className="p-4">
274// {/* 分类选择 */}
275// <div className="mb-4">
276// <label className="mr-2 font-medium">选择分类:</label>
277// <select
278// value={selectedCategory}
279// onChange={(e) => setSelectedCategory(e.target.value)}
280// className="border rounded px-2 py-1 mr-4"
281// >
282// <option value="">全部</option>
283// {categories.map(cat => (
284// <option key={cat.categoryid} value={cat.categoryid}>
285// {cat.category_name}
286// </option>
287// ))}
288// </select>
289
290// {/* 动态渲染筛选表单 */}
291// {selectedCategory && categoryFiltersConfig[selectedCategory] && (
292// <div className="flex flex-wrap gap-2">
293// {categoryFiltersConfig[selectedCategory].map(filter => {
294// if (filter.type === 'select') {
295// // 根据筛选字段选择对应的选项列表
296// let options;
297// switch (filter.id) {
298// case 'softwareplatform':
299// options = softwareplatforms;
300// break;
301// case 'softwareGenre':
302// options = softwaregenres;
303// break;
304// case 'softwareFormat':
305// options = softwareformats;
306// break;
307// case 'resolution':
308// options = [
309// { value: '720p', label: '720p' },
310// { value: '1080p', label: '1080p' },
311// { value: '2K', label: '2K' },
312// { value: '4K', label: '4K' },
313// { value: '8K', label: '8K' },
314// { value: '其他', label: '其他' },
315// ];
316// break;
317// case 'region':
318// if (selectedCategory === '1' || selectedCategory === '2') { // Movie or TV
319// options = [
320// { value: '大陆', label: '大陆' },
321// { value: '港台', label: '港台' },
322// { value: '欧美', label: '欧美' },
323// { value: '日韩', label: '日韩' },
324// { value: '其他', label: '其他' },
325// ];
326// }
327// else if (selectedCategory === '8') { // Sports
328// options = [
329// { value: '亚洲', label: '亚洲' },
330// { value: '欧洲', label: '欧洲' },
331// { value: '美洲', label: '美洲' },
332// { value: '其他', label: '其他' },
333// ];
334// }
335// break;
336// case 'isMainland':
337// options = [
338// { value: 'true', label: '是' },
339// { value: 'false', label: '不是' },
340// ];
341// break;
342// case 'codecFormat':
343// options = [
344// { value: 'H.264', label: 'H.264' },
345// { value: 'H.265', label: 'H.265' },
346// { value: 'AV1', label: 'AV1' },
347// { value: 'VC1', label: 'VC1' },
348// { value: 'X264', label: 'X264' },
349// { value: '其他', label: '其他' },
350// ];
351// break;
352// case 'platform':
353// options = [
354// { value: 'PC', label: 'PC' },
355// { value: 'PS5', label: 'PS5' },
356// { value: 'Xbox', label: 'Xbox' },
357// { value: 'Switch', label: 'Switch' },
358// { value: '手机', label: '手机' },
359// { value: '其他', label: '其他' },
360// ];
361// break;
362// case 'language':
363// options = [
364// { value: '中文', label: '中文' },
365// { value: '英文', label: '英文' },
366// { value: '日文', label: '日文' },
367// { value: '其他', label: '其他' },
368// ];
369// break;
370// case'EventType':
371// options = [
372// { value: '足球', label: '足球' },
373// { value: '篮球', label: '篮球' },
374// { value: '网球', label: '网球' },
375// { value: '乒乓球', label: '乒乓球' },
376// { value: '羽毛球', label: '羽毛球' },
377// { value: '其他', label: '其他' },
378// ];
379// break;
380// case 'genre':
381// if (selectedCategory === '3') { // Music
382// options = [
383// { value: '专辑', label: '专辑' },
384// { value: '单曲', label: '单曲' },
385// { value: 'EP', label: 'EP' },
386// { value: '现场', label: '现场' },
387// { value: '其他', label: '其他' },
388// ];
389// } else if (selectedCategory === '4') { // Anime
390// options = [
391// { value: '新番连载', label: '新番连载' },
392// { value: '剧场版', label: '剧场版' },
393// { value: 'OVA', label: 'OVA' },
394// { value: '完结动漫', label: '完结动漫' },
395// { value: '其他', label: '其他' },
396// ];
397// } else if (selectedCategory === '5') { // Game
398// options = [
399// { value: '角色扮演', label: '角色扮演' },
400// { value: '射击', label: '射击' },
401// { value: '冒险', label: '冒险' },
402// { value: '策略', label: '策略' },
403// { value: '体育', label: '体育' },
404// { value: '桌面游戏', label: '桌面游戏' },
405// { value: '其他', label: '其他' },
406// ];
407// } else if (selectedCategory === '1') { // TV
408// options = [
409// { value: '动作', label: '动作' },
410//     { value: '喜剧', label: '喜剧' },
411//     { value: '爱情', label: '爱情' },
412//     { value: '科幻', label: '科幻' },
413//     { value: '恐怖', label: '恐怖' },
414//     { value: '动作', label: '动作' },
415//     { value: '冒险', label: '冒险' },
416//   { value: '历史', label: '历史' },
417//     { value: '悬疑', label: '悬疑' },
418//     { value: '其他', label: '其他' },
419// ];
420// }
421// else if(selectedCategory === '2') {
422// options = [
423// { value: '动作', label: '动作' },
424//     { value: '喜剧', label: '喜剧' },
425//     { value: '爱情', label: '爱情' },
426//     { value: '科幻', label: '科幻' },
427//     { value: '恐怖', label: '恐怖' },
428//     { value: '动作', label: '动作' },
429//     { value: '冒险', label: '冒险' },
430//   { value: '历史', label: '历史' },
431//     { value: '悬疑', label: '悬疑' },
432//     { value: '其他', label: '其他' },
433// ]; // Movie
434// }
435// else if(selectedCategory === '6') {
436// options = [
437// { value: '真人秀', label: '真人秀' },
438// { value: '选秀', label: '选秀' },
439// { value: '访谈', label: '访谈' },
440// { value: '音乐', label: '音乐' },
441// { value: '游戏', label: '游戏' },
442// { value: '其他', label: '其他' },
443// ];
444// }
445// break;
446// case 'style':
447// if (selectedCategory === '3') { // Music
448// options = [
449// { value: '流行', label: '流行' },
450// { value: '摇滚', label: '摇滚' },
451// { value: '电子', label: '电子' },
452// { value: '古典', label: '古典' },
453// { value: '爵士', label: '爵士' },
454// { value: '民谣', label: '民谣' },
455// { value: '说唱', label: '说唱' },
456// { value: '其他', label: '其他' },
457// ];
458// } else {
459// options = []; // 根据需要定义
460// }
461// break;
462// case 'dataType':
463// if (selectedCategory === '5') { // Game
464// options = [
465// { value: '压缩包', label: '压缩包' },
466// { value: '补丁', label: '补丁' },
467// { value: '安装包', label: '安装包' },
468// { value: 'nds', label: 'nds' },
469// { value: '其他', label: '其他' },
470// ];
471// } else {
472// options = []; // 根据需要定义
473// }
474// break;
475// case 'format': if(selectedCategory === '4') {
476// options = [
477// { value: 'ZIP', label: 'ZIP' },
478// { value: 'RAR', label: 'RAR' },
479// { value: '7Z', label: '7Z' },
480// { value: 'MKV', label: 'MKV' },
481// { value: 'MP4', label: 'MP4' },
482// { value: '其他', label: '其他' },
483// ];
484// } else if(selectedCategory === '6') {
485// options = [
486// { value: '1080P', label: '1080P' },
487// { value: '4K', label: '4K' },
488// { value: 'HD', label: 'HD' },
489// { value: '其他', label: '其他' },
490// ];
491// } else if(selectedCategory === '2') {
492// options = [ { value: '720p', label: '720p' },
493// { value: '1080p', label: '1080p' },
494// { value: '2K', label: '2K' },
495// { value: '4K', label: '4K' },
496// { value: '8K', label: '8K' },
497// { value: '其他', label: '其他' },
498// ];
499// } else {
500// options = []; // 根据需要定义
501// }
502// break;
503// case 'eventType':
504// if (selectedCategory === '7') { // Sports
505// options = [
506// { value: '足球', label: '足球' },
507// { value: '篮球', label: '篮球' },
508// { value: '网球', label: '网球' },
509// { value: '乒乓球', label: '乒乓球' },
510// { value: '羽毛球', label: '羽毛球' },
511// { value: '其他', label: '其他' },
512// ];
513// } else {
514// options = []; // 根据需要定义
515// }
516// break;
517// case 'source':
518// if (selectedCategory === '10') { // Documentary
519// options = [
520// { value: 'CCTV', label: 'CCTV' },
521// { value: '卫视', label: '卫视' },
522// { value: '国家地理', label: '国家地理' },
523// { value: 'BBC', label: 'BBC' },
524// { value: 'Discovery', label: 'Discovery' },
525// { value: '其他', label: '其他' },
526// ];
527// } else {
528// options = []; // 根据需要定义
529// }
530// break;
531// default:
532// options = []; // 默认无选项
533// }
534
535// return (
536// <select
537// key={filter.id}
538// name={filter.id}
539// value={filters[filter.id] || ''}
540// onChange={handleFilterChange}
541// className="border rounded px-2 py-1"
542// >
543// <option value="">全部</option>
544// {options.map(option => (
545// <option key={option.value} value={option.value}>
546// {option.label}
547// </option>
548// ))}
549// </select>
550// );
551// }
552// // 其他类型(如输入框)可以类似实现
553// return null;
554// })}
555// </div>
556// )}
557// </div>
558
559// {/* 筛选按钮 */}
560// <button
561// onClick={() => {
562// // 这里可以根据需要添加额外的筛选逻辑
563// // 例如,如果某些筛选需要联动,可以在这里处理
564// }}
565// className="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600 ml-4"
566// >
567// 应用筛选
568// </button>
569
570// {/* 种子列表 */}
571// <table className="w-full border-collapse mt-4">
572// <thead>
573// <tr className="bg-gray-200">
574// <th className="p-2 border">名称</th>
575// <th className="p-2 border">上传者</th>
576// <th className="p-2 border">描述</th>
577// <th className="p-2 border">上传时间</th>
578// <th className="p-2 border">下载次数</th>
579// <th className="p-2 border">促销方式</th>
580// <th className="p-2 border">操作</th>
581// </tr>
582// </thead>
583// <tbody>
584// {torrents.map(t => (
585// <tr key={t.torrentid} className="border-t hover:bg-gray-100">
586// <td className="p-2 border">{t.filename}</td>
587// <td className="p-2 border">{t.uploader_id}</td>
588// <td className="p-2 border">{t.description}</td>
589// <td className="p-2 border">{new Date(t.uploadTime).toLocaleString()}</td>
590// <td className="p-2 border">{t.downloadCount}</td>
591// <td className="p-2 border">
592// {(() => {
593// switch(t.promotionid) {
594// case 1: return '上传加倍';
595// case 2: return '下载免费';
596// case 3: return '下载减半';
597// case 0: return '没有促销';
598// default: return '没有促销';
599// }
600// })()}
601// </td>
602// <td className="p-2 border">
603// <button
604// onClick={() => handleDownload(t.torrentid)}
605// className="text-blue-500 hover:underline mr-2"
606// >
607// 下载
608// </button>
609// <a
610// href={`/torrent/${t.torrentid}`}
611// className="text-green-600 hover:underline"
612// >
613// 查看详情
614// </a>
615// </td>
616// </tr>
617// ))}
618// </tbody>
619// </table>
620
621// {/* 成功提示 */}
622// {showSuccess && (
623// <div className="mt-4 p-3 bg-green-100 text-green-800 border border-green-300 rounded">
624// 上传成功!
625// </div>
626// )}
627// </div>
628// );
629// }
630
631// export default TorrentList;
632import { useState, useEffect } from 'react';
633import axios from 'axios';
634
635// 常量配置集中管理
636const FILTER_OPTIONS = {
637 // 通用选项
638 common: {
639 resolution: [
640 { value: '720p', label: '720p' },
641 { value: '1080p', label: '1080p' },
642 { value: '2K', label: '2K' },
643 { value: '4K', label: '4K' },
644 { value: '8K', label: '8K' },
645 { value: '其他', label: '其他' },
646 ],
647 region: {
648 movie: [
649 { value: '大陆', label: '大陆' },
650 { value: '港台', label: '港台' },
651 { value: '欧美', label: '欧美' },
652 { value: '日韩', label: '日韩' },
653 { value: '其他', label: '其他' },
654 ],
655 variety: [
656 { value: '大陆', label: '大陆' },
657 { value: '港台', label: '港台' },
658 { value: '欧美', label: '欧美' },
659 { value: '日韩', label: '日韩' },
660 { value: '其他', label: '其他' },
661 ],
662 sports: [
663 { value: '亚洲', label: '亚洲' },
664 { value: '欧洲', label: '欧洲' },
665 { value: '美洲', label: '美洲' },
666 { value: '其他', label: '其他' },
667 ]
668 },
669 genre: {
670 movie: [
671 { value: '动作', label: '动作' },
672 { value: '喜剧', label: '喜剧' },
673 { value: '爱情', label: '爱情' },
674 { value: '科幻', label: '科幻' },
675 { value: '恐怖', label: '恐怖' },
676 { value: '冒险', label: '冒险' },
677 { value: '历史', label: '历史' },
678 { value: '悬疑', label: '悬疑' },
679 { value: '其他', label: '其他' },
680 ],
681 music: [
682 { value: '流行', label: '流行' },
683 { value: '摇滚', label: '摇滚' },
684 { value: '电子', label: '电子' },
685 { value: '古典', label: '古典' },
686 { value: '爵士', label: '爵士' },
687 { value: '民谣', label: '民谣' },
688 { value: '说唱', label: '说唱' },
689 { value: '其他', label: '其他' },
690 ],
691 anime: [
692 { value: '新番连载', label: '新番连载' },
693 { value: '剧场版', label: '剧场版' },
694 { value: 'OVA', label: 'OVA' },
695 { value: '完结动漫', label: '完结动漫' },
696 { value: '其他', label: '其他' },
697 ],
698 game: [
699 { value: '角色扮演', label: '角色扮演' },
700 { value: '射击', label: '射击' },
701 { value: '冒险', label: '冒险' },
702 { value: '策略', label: '策略' },
703 { value: '体育', label: '体育' },
704 { value: '桌面游戏', label: '桌面游戏' },
705 { value: '其他', label: '其他' },
706 ],
707 variety: [
708 { value: '真人秀', label: '真人秀' },
709 { value: '选秀', label: '选秀' },
710 { value: '访谈', label: '访谈' },
711 { value: '音乐', label: '音乐' },
712 { value: '游戏', label: '游戏' },
713 { value: '其他', label: '其他' },
714 ],
715 learning: [
716 { value: '计算机', label: '计算机' },
717 { value: '软件', label: '软件' },
718 { value: '人文', label: '人文' },
719 { value: '外语', label: '外语' },
720 { value: '理工科', label: '理工科' },
721 { value: '其他', label: '其他' },
722 ],
723 sports: [
724 { value: '足球', label: '足球' },
725 { value: '篮球', label: '篮球' },
726 { value: '网球', label: '网球' },
727 { value: '乒乓球', label: '乒乓球' },
728 { value: '羽毛球', label: '羽毛球' },
729 { value: '其他', label: '其他' },
730 ],
731 // 其他类型...
732 }
733 },
734
735 // 分类特定选项
736 categories: {
737 1: { // 电影
738 name: '电影',
739 filters: [
740 { id: 'resolution', label: '分辨率', type: 'select' },
741 { id: 'codec_format', label: '编码格式', type: 'select',
742 options: [
743 { value: 'H.264', label: 'H.264' },
744 { value: 'H.265', label: 'H.265' },
745 { value: 'AV1', label: 'AV1' },
746 { value: 'VC1', label: 'VC1' },
747 { value: 'X264', label: 'X264' },
748 { value: '其他', label: '其他' },
749 ]
750 },
751 { id: 'region', label: '地区', type: 'select' },
752 { id: 'genre', label: '类型', type: 'select' }
753 ]
754 },
755 2: { // 电视剧
756 name: '剧集',
757 filters: [
758 { id: 'region', label: '地区', type: 'select',
759 options: [
760 { value: '大陆', label: '大陆' },
761 { value: '港台', label: '港台' },
762 { value: '欧美', label: '欧美' },
763 { value: '日韩', label: '日韩' },
764 { value: '其他', label: '其他' },
765 ]
766 },
767 { id: 'format', label: '分辨率', type: 'select',
768 options: [
769 { value: '720p', label: '720p' },
770 { value: '1080p', label: '1080p' },
771 { value: '2K', label: '2K' },
772 { value: '4K', label: '4K' },
773 { value: '8K', label: '8K' },
774 { value: '其他', label: '其他' },
775 ]
776 },
777 { id: 'genre', label: '类型', type: 'select' ,
778 options:[
779 { value: '真人秀', label: '真人秀' },
780 { value: '选秀', label: '选秀' },
781 { value: '访谈', label: '访谈' },
782 { value: '游戏', label: '游戏' },
783 { value: '音乐', label: '音乐' },
784 { value: '其他', label: '其他' },
785 ]
786 }
787 ]
788 },
789 3: { // 音乐
790 name: '音乐',
791 filters: [
792 { id: 'genre', label: '类型', type: 'select',
793 options: [
794 { value: '专辑', label: '专辑' },
795 { value: '单曲', label: '单曲' },
796 { value: 'EP', label: 'EP' },
797 { value: '现场', label: '现场' },
798 { value: '其他', label: '其他' },
799 ]
800 },
801 { id: 'style', label: '风格', type: 'select',
802 options: [
803 { value: '流行', label: '流行' },
804 { value: '摇滚', label: '摇滚' },
805 { value: '电子', label: '电子' },
806 { value: '古典', label: '古典' },
807 { value: '爵士', label: '爵士' },
808 { value: '民谣', label: '民谣' },
809 { value: '说唱', label: '说唱' },
810 { value: '其他', label: '其他' },
811 ]
812 },
813 {id:'format', label: '格式', type: 'select',
814 options:[
815 { value: 'MP3', label: 'MP3' },
816 { value: 'FLAC', label: 'FLAC' },
817 { value: 'WAV', label: 'WAV' },
818 { value: 'AAC', label: 'AAC' },
819 { value: 'OGG', label: 'OGG' },
820 { value: '其他', label: '其他' },
821 ]
822 }
823 ]
824 },
825 4: { // 动漫
826 name: '动漫',
827 filters: [
828 { id: 'genre', label: '类型', type: 'select' ,
829 options: [
830 { value: '新番连载', label: '新番连载' },
831 { value: '剧场版', label: '剧场版' },
832 { value: 'OVA', label: 'OVA' },
833 { value: '完结动漫', label: '完结动漫' },
834 { value: '其他', label: '其他' },
835 ]
836 },
837 { id: 'format', label: '格式', type: 'select' ,
838 options:[
839 { value: 'ZIP', label: 'ZIP' },
840 { value: 'RAR', label: 'RAR' },
841 { value: '7Z', label: '7Z' },
842 { value: 'MKV', label: 'MKV' },
843 { value: 'MP4', label: 'MP4' },
844 { value: '其他', label: '其他' },
845 ]
846 },
847 { id: 'resolution', label: '分辨率', type: 'select',
848 options:[
849 { value: '720p', label: '720p' },
850 { value: '1080p', label: '1080p' },
851 { value: '2K', label: '2K' },
852 { value: '4K', label: '4K' },
853 { value: '8K', label: '8K' },
854 { value: '其他', label: '其他' },
855 ]
856 }
857 ]
858 },
859 5: { // 游戏
860 name: '游戏',
861 filters: [
862 { id: 'platform', label: '平台', type: 'select',
863 options: [
864 { value: 'PC', label: 'PC' },
865 { value: 'PS5', label: 'PS5' },
866 { value: 'Xbox', label: 'Xbox' },
867 { value: 'Switch', label: 'Switch' },
868 { value: '手机', label: '手机' },
869 { value: '其他', label: '其他' },
870 ]
871 },
872 { id: 'genre', label: '类型', type: 'select',
873 options: [
874 { value: '角色扮演', label: '角色扮演' },
875 { value: '射击', label: '射击' },
876 { value: '冒险', label: '冒险' },
877 { value: '策略', label: '策略' },
878 { value: '体育', label: '体育' },
879 { value: '桌面游戏', label: '桌面游戏' },
880 { value: '其他', label: '其他' },
881 ]
882 },
883 { id: 'data_format', label: '数据类型', type: 'select' ,
884 options: [
885 { value: '压缩包', label: '压缩包' },
886 { value: '补丁', label: '补丁' },
887 { value: '安装包', label: '安装包' },
888 { value: 'nds', label: 'nds' },
889 { value: '其他', label: '其他' },
890 ]
891 },
892 { id: 'language', label: '语言', type: 'select',
893 options: [
894 { value: '中文', label: '中文' },
895 { value: '英文', label: '英文' },
896 { value: '日文', label: '日文' },
897 { value: '其他', label: '其他' },
898 ]
899 }
900 ]
901 },
902 6: { // 综艺
903 name: '综艺',
904 filters: [
905 { id: 'is_mainland', label: '是否大陆综艺', type: 'select' ,
906 options:[
907 { value: 'true', label: '是' },
908 { value: 'false', label: ' 不是' },
909 ]
910 },
911 { id: 'format', label: '分辨率', type: 'select',
912 options:[
913 { value: '720p', label: '720p' },
914 { value: '1080p', label: '1080p' },
915 { value: '2K', label: '2K' },
916 { value: '4K', label: '4K' },
917 { value: '8K', label: '8K' },
918 { value: '其他', label: '其他' },
919 ]
920 },
921 {id: 'genre', label: '类型', type: 'select',
922 options:[
923 { value: '真人秀', label: '真人秀' },
924 { value: '选秀', label: '选秀' },
925 { value: '访谈', label: '访谈' },
926 { value: '游戏', label: '游戏' },
927 { value: '音乐', label: '音乐' },
928 { value: '其他', label: '其他' },
929 ]
930 }
931 ]
932 },
933 7: { // 体育
934 name: '体育',
935 filters: [
936 { id: 'genre', label: '体育类型', type: 'select' ,
937 options:[
938 { value: '足球', label: '足球' },
939 { value: '篮球', label: '篮球' },
940 { value: '网球', label: '网球' },
941 { value: '乒乓球', label: '乒乓球' },
942 { value: '羽毛球', label: '羽毛球' },
943 { value: '其他', label: '其他' },
944 ]
945 },
946 { id: 'event_type', label: '赛事类型', type: 'select',
947 options:[
948 { value: '足球', label: '足球' },
949 { value: '篮球', label: '篮球' },
950 { value: '网球', label: '网球' },
951 { value: '乒乓球', label: '乒乓球' },
952 { value: '羽毛球', label: '羽毛球' },
953 { value: '其他', label: '其他' },
954 ]
955 },
956 { id: 'format', label: '分辨率', type: 'select',
957 options:[
958 { value: '720p', label: '720p' },
959 { value: '1080p', label: '1080p' },
960 { value: '2K', label: '2K' },
961 { value: '4K', label: '4K' },
962 { value: '8K', label: '8K' },
963 { value: '其他', label: '其他' },
964 ]
965 }
966 ]
967 },
968 8: { // 软件
969 name: '软件',
970 filters: [
971 { id: 'platform', label: '平台', type: 'select' ,
972 options:[
973 { value: 'Windows', label: 'Windows' },
974 { value: 'Mac', label: 'Mac' },
975 { value: 'Linux', label: 'Linux' },
976 { value: 'Android', label: 'Android' },
977 { value: 'iOS', label: 'iOS' },
978 { value: '其他', label: '其他' },
979 ]
980 },
981 { id: 'format', label: '格式', type: 'select',
982 options:[
983 { value: 'EXE', label: 'EXE' },
984 { value: 'DMG', label: 'DMG' },
985 { value: '光盘镜像', label: '光盘镜像' },
986 { value: 'APK', label: 'APK' },
987 { value: 'IPA', label: 'IPA' },
988 { value: '其他', label: '其他' },
989 ]
990 },
991 { id: 'genre', label: '类型', type: 'select',
992 options:[
993 { value: '系统软件', label: '系统软件' },
994 { value: '应用软件', label: '应用软件' },
995 { value: '游戏软件', label: '游戏软件' },
996 { value: '驱动程序', label: '驱动程序' },
997 { value: '办公软件', label: '办公软件' },
998 { value: '其他', label: '其他' },
999 ]
1000 },
1001 ]
1002 },
1003 9: { // 学习
1004 name: '学习',
1005 filters: [
1006 { id: 'genre', label: '类型', type: 'select' ,
1007 options:[
1008 { value: '计算机', label: '计算机' },
1009 { value: '软件', label: '软件' },
1010 { value: '人文', label: '人文' },
1011 { value: '外语', label: '外语' },
1012 { value: '理工科', label: '理工科' },
1013 { value: '其他', label: '其他' },
1014 ]
1015 },
1016 { id: 'format', label: '格式', type: 'select',
1017 options:[
1018 { value: 'PDF', label: 'PDF' },
1019 { value: 'EPUB', label: 'EPUB' },
1020 { value: '视频', label: '视频' },
1021 { value: '音频', label: '音频' },
1022 { value: 'PPT', label: 'PPT' },
1023 { value: '其他', label: '其他' },
1024 ]
1025 }
1026 ]
1027 },
1028 10: { // 纪录片
1029 name: '纪录片',
1030 filters: [
1031 { id: 'source', label: '视频源', type: 'select',
1032 options:[
1033 { value: 'CCTV', label: 'CCTV' },
1034 { value: '卫视', label: '卫视' },
1035 { value: '国家地理', label: '国家地理' },
1036 { value: 'BBC', label: 'BBC' },
1037 { value: 'Discovery', label: 'Discovery' },
1038 { value: '其他', label: '其他' },
1039 ]
1040 },
1041 { id: 'format', label: '格式', type: 'select',
1042 options:[
1043 { value: '720p', label: '720p' },
1044 { value: '1080p', label: '1080p' },
1045 { value: '2K', label: '2K' },
1046 { value: '4K', label: '4K' },
1047 { value: '8K', label: '8K' },
1048 { value: '其他', label: '其他' },
1049 ]
1050 }
1051 ]
1052 },
1053 11: { // 其他
1054 name: '其他',
1055 filters: [
1056 { id: 'gener', label: '类型', type: 'select',
1057 options:[
1058 { value: '电子书', label: '电子书' },
1059 { value: '视频', label: '视频' },
1060 { value: 'MP3', label: 'MP3' },
1061 { value: '图片', label: '图片' },
1062 { value: '其他', label: '其他' },
1063 ]
1064 }
1065 ]
1066 }
1067 // 其他分类配置...
1068 }
1069};
1070
1071// 获取分类筛选配置
1072const getCategoryFilters = (categoryId) => {
1073 const category = FILTER_OPTIONS.categories[categoryId];
1074 if (!category) return [];
1075
1076 return category.filters.map(filter => {
1077 // 自动填充通用选项
1078 if (filter.id === 'resolution' && !filter.options) {
1079 return { ...filter, options: FILTER_OPTIONS.common.resolution };
1080 }
1081 if (filter.id === 'region' && !filter.options) {
1082 const regionType = categoryId === 8 ? 'sports' : 'movie';
1083 return { ...filter, options: FILTER_OPTIONS.common.region[regionType] };
1084 }
1085 if (filter.id === 'genre' && !filter.options) {
1086 const genreType = categoryId === 3 ? 'music' : 'movie';
1087 return { ...filter, options: FILTER_OPTIONS.common.genre[genreType] };
1088 }
1089 return filter;
1090 });
1091};
1092
1093// 格式化日期显示
1094const formatDate = (dateString) => {
1095 const options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };
1096 return new Date(dateString).toLocaleString('zh-CN', options);
1097};
1098
1099// 获取促销方式名称
1100const getPromotionName = (promotionId) => {
1101 switch(promotionId) {
1102 case 1: return '上传加倍';
1103 case 2: return '下载免费';
1104 case 3: return '下载减半';
1105 default: return '没有促销';
1106 }
1107};
1108
1109function TorrentList() {
1110 const [torrents, setTorrents] = useState([]);
1111 const [categories, setCategories] = useState([]);
1112 const [selectedCategory, setSelectedCategory] = useState('');
1113 const [filters, setFilters] = useState({});
1114 const [isLoading, setIsLoading] = useState(false);
1115 const [error, setError] = useState(null);
1116
1117 // 获取所有分类
1118 useEffect(() => {
1119 const fetchCategories = async () => {
1120 try {
1121 const res = await axios.get('http://localhost:8080/categories');
1122 setCategories(res.data);
1123 } catch (err) {
1124 console.error('加载分类失败', err);
1125 setError('加载分类失败,请稍后重试');
1126 }
1127 };
1128 fetchCategories();
1129 }, []);
1130
1131 // 获取种子数据
1132 useEffect(() => {
1133 const fetchTorrents = async () => {
1134 setIsLoading(true);
1135 setError(null);
1136 try {
1137 let url = selectedCategory
1138 // ? `http://localhost:8080/torrent/listByCategory?categoryid=${selectedCategory}`
1139 ? `http://localhost:8080/torrent/listByCategorywithfilter?categoryid=${selectedCategory}`
1140 : 'http://localhost:8080/torrent/list';
1141
1142 // 添加筛选参数
1143 const params = new URLSearchParams();
1144 Object.entries(filters).forEach(([key, value]) => {
1145 if (value) params.append(key, value);
1146 });
1147
1148 const res = await axios.get(`${url}&${params.toString()}`);
1149 setTorrents(res.data);
1150 console.log('torrents:', torrents);
1151 } catch (err) {
1152 console.error('获取种子失败', err);
1153 setError('获取种子列表失败,请稍后重试');
1154 } finally {
1155 setIsLoading(false);
1156 }
1157 };
1158 console.log('Fetching torrents with filters:', filters);
1159 console.log('Selected category:', selectedCategory);
1160 console.log(torrents);
1161 const timer = setTimeout(fetchTorrents, 300); // 防抖
1162 return () => clearTimeout(timer);
1163 }, [selectedCategory, filters]);
1164
1165 // 切换分类时重置筛选条件
1166 const handleCategoryChange = (categoryId) => {
1167 setSelectedCategory(categoryId);
1168 setFilters({});
1169 };
1170
1171 // 处理筛选条件变化
1172 const handleFilterChange = (e) => {
1173 const { name, value } = e.target;
1174 setFilters(prev => ({ ...prev, [name]: value }));
1175 };
1176
1177 // 下载种子
1178 const handleDownload = (torrentId) => {
1179 window.open(`http://localhost:8080/torrent/download/${torrentId}`, '_blank');
1180 };
1181
1182 // 获取当前分类的筛选配置
1183 const currentFilters = getCategoryFilters(selectedCategory);
1184
1185 return (
1186 <div className="p-4 max-w-7xl mx-auto">
1187 <h1 className="text-2xl font-bold mb-6">种子列表</h1>
1188
1189 {/* 分类选择 */}
1190 <div className="mb-6 bg-white p-4 rounded-lg shadow">
1191 <div className="flex flex-wrap items-center gap-4">
1192 <div className="flex items-center">
1193 <label className="mr-2 font-medium whitespace-nowrap">选择分类:</label>
1194 <select
1195 value={selectedCategory}
1196 onChange={(e) => handleCategoryChange(e.target.value)}
1197 className="border rounded px-3 py-2 min-w-[150px]"
1198 >
1199 <option value="">全部分类</option>
1200 {categories.map(cat => (
1201 <option key={cat.categoryid} value={cat.categoryid}>
1202 {cat.category_name}
1203 </option>
1204 ))}
1205 </select>
1206 </div>
1207
1208 {/* 动态筛选表单 */}
1209 {currentFilters.length > 0 && (
1210 <div className="flex flex-wrap gap-4">
1211 {currentFilters.map(filter => (
1212 <div key={filter.id} className="flex items-center">
1213 <label className="mr-2 text-sm whitespace-nowrap">{filter.label}:</label>
1214 <select
1215 name={filter.id}
1216 value={filters[filter.id] || ''}
1217 onChange={handleFilterChange}
1218 className="border rounded px-3 py-2 min-w-[120px]"
1219 >
1220 <option value="">全部</option>
1221 {filter.options.map(option => (
1222 <option key={option.value} value={option.value}>
1223 {option.label}
1224 </option>
1225 ))}
1226 </select>
1227 </div>
1228 ))}
1229 </div>
1230 )}
1231 </div>
1232 </div>
1233
1234
1235 {/* 错误提示 */}
1236 {error && (
1237 <div className="mb-4 p-3 bg-red-100 text-red-700 rounded border border-red-200">
1238 {error}
1239 </div>
1240 )}
1241
1242
1243 {/* 加载状态 */}
1244 {isLoading ? (
1245 <div className="flex justify-center items-center h-64">
1246 <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
1247 </div>
1248 ) : (
1249 /* 种子列表 */
1250 <div className="bg-white rounded-lg shadow overflow-hidden">
1251 <div className="overflow-x-auto">
1252 <table className="w-full">
1253 <thead className="bg-gray-50">
1254 <tr>
1255 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">名称</th>
1256 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">大小</th>
1257 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">上传者</th>
1258 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">上传时间</th>
1259 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">下载次数</th>
1260 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">促销</th>
1261 <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
1262 </tr>
1263 </thead>
1264 <tbody className="bg-white divide-y divide-gray-200">
1265 {torrents.length > 0 ? (
1266 torrents.map(torrent => (
1267 <tr key={torrent.torrentid} className="hover:bg-gray-50">
1268 <td className="px-6 py-4 whitespace-nowrap">
1269 <div className="text-sm font-medium text-gray-900">{torrent.filename}</div>
1270 <div className="text-sm text-gray-500">{torrent.description}</div>
1271 </td>
1272 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
1273 {torrent.torrentSize} B
1274 </td>
1275 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
1276 {torrent.uploader_id}
1277 </td>
1278 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
1279 {new Date(torrent.uploadTime).toLocaleString()}
1280 </td>
1281 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
1282 {torrent.downloadCount}
1283 </td>
1284 <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
1285 {getPromotionName(torrent.promotionid)}
1286 </td>
1287 <td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
1288 <button
1289 onClick={() => handleDownload(torrent.torrentid)}
1290 className="text-blue-600 hover:text-blue-900 mr-4"
1291 >
1292 下载
1293 </button>
1294 <a
1295 href={`/torrent/${torrent.torrentid}`}
1296 className="text-green-600 hover:text-green-900"
1297 >
1298 详情
1299 </a>
1300 </td>
1301 </tr>
1302 ))
1303 ) : (
1304 <tr>
1305 <td colSpan="7" className="px-6 py-4 text-center text-sm text-gray-500">
1306 没有找到符合条件的种子
1307 </td>
1308 </tr>
1309 )}
1310 </tbody>
1311 </table>
1312 </div>
1313 </div>
1314 )}
1315 </div>
1316 );
1317}
1318
1319export default TorrentList;