前端简单界面
Change-Id: I7df9774daf4df8d92b13e659effe426ab0b6180b
diff --git a/pt--frontend/src/components/torrentlist.jsx b/pt--frontend/src/components/torrentlist.jsx
new file mode 100644
index 0000000..be4b76b
--- /dev/null
+++ b/pt--frontend/src/components/torrentlist.jsx
@@ -0,0 +1,1319 @@
+// import { useState, useEffect } from 'react';
+// import { Link } from 'react-router-dom';
+// import axios from 'axios';
+
+// function TorrentList() {
+// const [torrents, setTorrents] = useState([]);
+// const [categories, setCategories] = useState([]);
+// const [selectedCategory, setSelectedCategory] = useState('');
+
+// // 获取所有分类
+// useEffect(() => {
+// axios.get('http://localhost:8080/categories') // 假设这个接口返回所有分类
+// .then(res => setCategories(res.data))
+// .catch(err => console.error('获取分类失败', err));
+// }, []);
+
+// // 获取种子(根据分类筛选)
+// useEffect(() => {
+// const url = selectedCategory
+// ? `http://localhost:8080/torrent/listByCategory?categoryid=${selectedCategory}`
+// : 'http://localhost:8080/torrent/list';
+
+// axios.get(url)
+// .then(res => setTorrents(res.data))
+// .catch(err => console.error('获取种子失败', err));
+// }, [selectedCategory]);
+// console.log(torrents);
+
+
+// return (
+// <div className="p-4">
+// <div className="mb-4">
+// <label className="mr-2 font-medium">选择分类:</label>
+// <select
+// value={selectedCategory}
+// onChange={e => setSelectedCategory(e.target.value)}
+// className="border rounded px-2 py-1"
+// >
+// <option value="">全部</option>
+// {categories.map(cat => (
+// <option key={cat.categoryid} value={cat.categoryid}>
+// {cat.category_name}
+// </option>
+// ))}
+// </select>
+// </div>
+// <table className="w-full border-collapse">
+// <thead>
+// <tr className="bg-gray-200">
+// <th className="p-2 border">名称</th>
+// <th className="p-2 border">上传者</th>
+// <th className="p-2 border">描述</th>
+// <th className="p-2 border">上传时间</th>
+// <th className="p-2 border">下载次数</th>
+// <th className="p-2 border">促销方式</th>
+// <th className="p-2 border">操作</th>
+// </tr>
+// </thead>
+// <tbody>
+// {torrents.map(t => (
+// <tr key={t.torrentid} className="border-t hover:bg-gray-100">
+// <td className="p-2 border">{t.filename}</td>
+// <td className="p-2 border">{t.uploader_id}</td>
+// <td className="p-2 border">{t.description}</td>
+// <td className="p-2 border">{new Date(t.uploadTime).toLocaleString()}</td>
+// <td className="p-2 border">{t.downloadCount}</td>
+// <td className="p-2 border">
+// {(() => {
+// switch(t.promotionid) {
+// case 1: return '上传加倍';
+// case 2: return '下载免费';
+// case 3: return '下载减半';
+// case 0: return '没有促销';
+// default: return '没有促销';
+// }
+// })()}
+// </td>
+// <td className="p-2 border">
+// <a
+// href={`http://localhost:8080/torrent/download/${t.torrentid}`}
+// className="text-blue-500 hover:underline"
+// target="_blank"
+// rel="noreferrer"
+// >
+// 下载
+// </a>
+// </td>
+// <Link
+// to={`/torrent/${t.torrentid}`}
+// className="text-green-600 hover:underline"
+// >
+// 查看详情
+// </Link>
+// </tr>
+// ))}
+// </tbody>
+// </table>
+// </div>
+// );
+// }
+
+// export default TorrentList;
+// import { useState, useEffect } from 'react';
+// import axios from 'axios';
+
+// function TorrentList() {
+// const [torrents, setTorrents] = useState([]);
+// const [categories, setCategories] = useState([]);
+// const [selectedCategory, setSelectedCategory] = useState('');
+// const [filters, setFilters] = useState({});
+// const [showSuccess, setShowSuccess] = useState(false);
+// const softwaregenres = [
+// { value: '系统软件', label: '系统软件' },
+// { value: '应用软件', label: '应用软件' },
+// { value: '游戏软件', label: '游戏软件' },
+// { value: '驱动程序', label: '驱动程序' },
+// { value: '办公软件', label: '办公软件' },
+// { value: '其他', label: '其他' },
+// ]
+// const softwareplatforms = [
+// { value: 'Windows', label: 'Windows' },
+// { value: 'Mac', label: 'Mac' },
+// { value: 'Linux', label: 'Linux' },
+// { value: 'Android', label: 'Android' },
+// { value: 'iOS', label: 'iOS' },
+// { value: '其他', label: '其他' },
+// ]
+// const softwareformats = [
+// { value: 'EXE', label: 'EXE' },
+// { value: 'DMG', label: 'DMG' },
+// { value: '光盘镜像', label: '光盘镜像' },
+// { value: 'APK', label: 'APK' },
+// { value: 'IPA', label: 'IPA' },
+// { value: '其他', label: '其他' },
+// ]
+// const sourceTypes = [
+// { value: 'CCTV', label: 'CCTV' },
+// { value: '卫视', label: '卫视' },
+// { value: '国家地理', label: '国家地理' },
+// { value: 'BBC', label: 'BBC' },
+// { value: 'Discovery', label: 'Discovery' },
+// { value: '其他', label: '其他' },
+// ]
+// const othergenres = [
+// { value: '电子书', label: '电子书' },
+// { value: '视频', label: '视频' },
+// { value: 'MP3', label: 'MP3' },
+// { value: '图片', label: '图片' },
+// { value: '其他', label: '其他' },
+// ]
+// const resolutions = [
+// { value: '720p', label: '720p' },
+// { value: '1080p', label: '1080p' },
+// { value: '2K', label: '2K' },
+// { value: '4K', label: '4K' },
+// { value: '8K', label: '8K' },
+// { value: '其他', label: '其他' },
+// ];
+
+// // 每个分类的筛选字段配置
+// const categoryFiltersConfig = {
+// 1: [ // Movie 电影
+// { id: 'resolution', label: '分辨率', type: 'select', options: ['1080p', '4K', '720p', '其他'] },
+// { id: 'codecFormat', label: '编码格式', type: 'select', options: ['H.264', 'H.265', 'AV1', 'VC1', 'X264', '其他'] },
+// { id: 'region', label: '地区', type: 'select', options: ['大陆', '港台', '欧美', '日韩', '其他'] },
+// { id: 'genre', label: '类型', type: 'select', options: ['动作', '喜剧', '爱情', '科幻', '恐怖','动作', '冒险', '历史', '悬疑', '其他'] },
+// ],
+// 2: [ // TV 剧集
+// { id: 'region', label: '地区', type: 'select', options: ['大陆', '港台', '欧美', '日韩', '其他'] },
+// { id: 'format', label: '格式', type: 'select', options: resolutions },
+// { id: 'genre', label: '类型', type: 'select', options: ['动作', '喜剧', '爱情', '科幻', '恐怖','动作', '冒险', '历史', '悬疑', '其他'] },
+// ],
+// 3: [ // Music 音乐
+// { id: 'genre', label: '类型', type: 'select', options: ['专辑', '单曲', 'EP', '现场', '其他'] },
+// { id: 'style', label: '风格', type: 'select', options: ['流行', '摇滚', '电子', '古典', '爵士', '民谣', '说唱', '其他'] },
+// ],
+// 4: [ // Anime 动漫
+// { id: 'genre', label: '类型', type: 'select', options: ['新番连载', '剧场版', 'OVA', '完结动漫', '其他'] },
+// { id: 'format', label: '格式', type: 'select', options: ['ZIP', 'RAR', '7Z', 'MKV', 'MP4', '其他'] },
+// { id: 'resolution', label: '分辨率', type: 'select', options: ['720P', '1080P', '4K', '其他'] },
+// ],
+// 5: [ // Game 游戏
+// { id: 'platform', label: '平台', type: 'select', options: ['PC', 'PS5', 'Xbox', 'Switch', '手机', '其他'] },
+// { id: 'genre', label: '类型', type: 'select', options: ['角色扮演', '射击', '冒险', '策略', '体育', '桌面游戏', '其他'] },
+// { id: 'language', label: '语言', type: 'select', options: ['中文', '英文', '日文', '其他'] },
+// { id: 'dataType', label: '数据类型', type: 'select', options: ['压缩包', '补丁', '安装包', 'nds', '其他'] },
+// ],
+// 6: [ // 综艺
+// { id: 'isMainland', label: '是否为大陆综艺', type: 'select', options: ['是','不是'] },
+// { id: 'format', label: '格式', type: 'select', options: ['1080P', '4K', 'HD', '其他'] },
+// { id: 'genre', label: '类型', type: 'select', options: ['真人秀', '选秀','访谈', '音乐', '游戏', '其他'] },
+// ],
+// 7: [ // 学习
+// { id: 'genre', label: '类型', type: 'select', options: ['计算机','软件','人文','外语','理工科','其他'] },
+// { id: 'learningformat', label: '格式', type: 'select', options: ['PDF','EPUB','视频','音频','PPT','其他'] },
+// ],
+// 8: [ // 体育
+// { id: 'eventType', label: '赛事类型', type: 'select', options: ['足球', '篮球', '网球', '乒乓球', '羽毛球', '其他'] },
+// { id: 'region', label: '地区', type: 'select', options: ['亚洲', '欧洲', '美洲', '其他'] },
+// ],
+// 9: [ // 其他
+// { id: 'otherGenre', label: '类型', type: 'select', options: othergenres },
+// ],
+// 10: [ // 纪录片
+// { id: 'source', label: '来源', type: 'select', options: sourceTypes },
+// { id: 'resolution', label: '分辨率', type: 'select', options: resolutions },
+// ],
+// 11: [ // 软件
+// { id: 'softwsreplatform', label: '平台', type: 'select', options: softwareplatforms },
+// { id: 'softwareGenre', label: '软件类型', type: 'select', options: softwaregenres },
+// { id: 'softwareFormat', label: '软件格式', type: 'select', options: softwareformats },
+// ],
+// // 其他分类...
+// };
+
+// // 获取所有分类
+// useEffect(() => {
+// axios.get('http://localhost:8080/categories')
+// .then(res => setCategories(res.data))
+// .catch(err => console.error('加载分类失败', err));
+// }, []);
+
+// // 根据选择的分类显示不同的表单字段
+// useEffect(() => {
+// setFilters({}); // 清空筛选条件
+// }, [selectedCategory]);
+
+// const handleFilterChange = (e) => {
+// const { name, value } = e.target;
+// setFilters(prev => ({ ...prev, [name]: value }));
+// };
+
+// const filteredTorrents = (torrents) => {
+// return torrents.filter(torrent => {
+// if (!selectedCategory) return true; // 如果没有选择分类,显示所有
+// if (torrent.categoryid !== parseInt(selectedCategory)) return false;
+
+// // 根据筛选条件过滤
+// for (const [key, value] of Object.entries(filters)) {
+// if (value && torrent[key] !== value) {
+// return false;
+// }
+// }
+// return true;
+// });
+// };
+
+// // 获取种子(根据分类筛选)
+// useEffect(() => {
+// let url = selectedCategory
+// ? `http://localhost:8080/torrent/listByCategory?categoryid=${selectedCategory}`
+// : 'http://localhost:8080/torrent/list';
+
+// // 添加筛选条件到 URL
+// Object.entries(filters).forEach(([key, value]) => {
+// if (value) {
+// url += `&${key}=${encodeURIComponent(value)}`;
+// }
+// });
+
+// axios.get(url)
+// .then(res => {
+// setTorrents(res.data);
+// })
+// .catch(err => console.error('获取种子失败', err));
+// }, [selectedCategory, filters]);
+
+// const handleDownload = (torrentId) => {
+// window.open(`http://localhost:8080/torrent/download/${torrentId}`, '_blank');
+// };
+
+// return (
+// <div className="p-4">
+// {/* 分类选择 */}
+// <div className="mb-4">
+// <label className="mr-2 font-medium">选择分类:</label>
+// <select
+// value={selectedCategory}
+// onChange={(e) => setSelectedCategory(e.target.value)}
+// className="border rounded px-2 py-1 mr-4"
+// >
+// <option value="">全部</option>
+// {categories.map(cat => (
+// <option key={cat.categoryid} value={cat.categoryid}>
+// {cat.category_name}
+// </option>
+// ))}
+// </select>
+
+// {/* 动态渲染筛选表单 */}
+// {selectedCategory && categoryFiltersConfig[selectedCategory] && (
+// <div className="flex flex-wrap gap-2">
+// {categoryFiltersConfig[selectedCategory].map(filter => {
+// if (filter.type === 'select') {
+// // 根据筛选字段选择对应的选项列表
+// let options;
+// switch (filter.id) {
+// case 'softwareplatform':
+// options = softwareplatforms;
+// break;
+// case 'softwareGenre':
+// options = softwaregenres;
+// break;
+// case 'softwareFormat':
+// options = softwareformats;
+// break;
+// case 'resolution':
+// options = [
+// { value: '720p', label: '720p' },
+// { value: '1080p', label: '1080p' },
+// { value: '2K', label: '2K' },
+// { value: '4K', label: '4K' },
+// { value: '8K', label: '8K' },
+// { value: '其他', label: '其他' },
+// ];
+// break;
+// case 'region':
+// if (selectedCategory === '1' || selectedCategory === '2') { // Movie or TV
+// options = [
+// { value: '大陆', label: '大陆' },
+// { value: '港台', label: '港台' },
+// { value: '欧美', label: '欧美' },
+// { value: '日韩', label: '日韩' },
+// { value: '其他', label: '其他' },
+// ];
+// }
+// else if (selectedCategory === '8') { // Sports
+// options = [
+// { value: '亚洲', label: '亚洲' },
+// { value: '欧洲', label: '欧洲' },
+// { value: '美洲', label: '美洲' },
+// { value: '其他', label: '其他' },
+// ];
+// }
+// break;
+// case 'isMainland':
+// options = [
+// { value: 'true', label: '是' },
+// { value: 'false', label: '不是' },
+// ];
+// break;
+// case 'codecFormat':
+// options = [
+// { value: 'H.264', label: 'H.264' },
+// { value: 'H.265', label: 'H.265' },
+// { value: 'AV1', label: 'AV1' },
+// { value: 'VC1', label: 'VC1' },
+// { value: 'X264', label: 'X264' },
+// { value: '其他', label: '其他' },
+// ];
+// break;
+// case 'platform':
+// options = [
+// { value: 'PC', label: 'PC' },
+// { value: 'PS5', label: 'PS5' },
+// { value: 'Xbox', label: 'Xbox' },
+// { value: 'Switch', label: 'Switch' },
+// { value: '手机', label: '手机' },
+// { value: '其他', label: '其他' },
+// ];
+// break;
+// case 'language':
+// options = [
+// { value: '中文', label: '中文' },
+// { value: '英文', label: '英文' },
+// { value: '日文', label: '日文' },
+// { value: '其他', label: '其他' },
+// ];
+// break;
+// case'EventType':
+// options = [
+// { value: '足球', label: '足球' },
+// { value: '篮球', label: '篮球' },
+// { value: '网球', label: '网球' },
+// { value: '乒乓球', label: '乒乓球' },
+// { value: '羽毛球', label: '羽毛球' },
+// { value: '其他', label: '其他' },
+// ];
+// break;
+// case 'genre':
+// if (selectedCategory === '3') { // Music
+// options = [
+// { value: '专辑', label: '专辑' },
+// { value: '单曲', label: '单曲' },
+// { value: 'EP', label: 'EP' },
+// { value: '现场', label: '现场' },
+// { value: '其他', label: '其他' },
+// ];
+// } else if (selectedCategory === '4') { // Anime
+// options = [
+// { value: '新番连载', label: '新番连载' },
+// { value: '剧场版', label: '剧场版' },
+// { value: 'OVA', label: 'OVA' },
+// { value: '完结动漫', label: '完结动漫' },
+// { value: '其他', label: '其他' },
+// ];
+// } else if (selectedCategory === '5') { // Game
+// options = [
+// { value: '角色扮演', label: '角色扮演' },
+// { value: '射击', label: '射击' },
+// { value: '冒险', label: '冒险' },
+// { value: '策略', label: '策略' },
+// { value: '体育', label: '体育' },
+// { value: '桌面游戏', label: '桌面游戏' },
+// { value: '其他', label: '其他' },
+// ];
+// } else if (selectedCategory === '1') { // TV
+// options = [
+// { value: '动作', label: '动作' },
+// { value: '喜剧', label: '喜剧' },
+// { value: '爱情', label: '爱情' },
+// { value: '科幻', label: '科幻' },
+// { value: '恐怖', label: '恐怖' },
+// { value: '动作', label: '动作' },
+// { value: '冒险', label: '冒险' },
+// { value: '历史', label: '历史' },
+// { value: '悬疑', label: '悬疑' },
+// { value: '其他', label: '其他' },
+// ];
+// }
+// else if(selectedCategory === '2') {
+// options = [
+// { value: '动作', label: '动作' },
+// { value: '喜剧', label: '喜剧' },
+// { value: '爱情', label: '爱情' },
+// { value: '科幻', label: '科幻' },
+// { value: '恐怖', label: '恐怖' },
+// { value: '动作', label: '动作' },
+// { value: '冒险', label: '冒险' },
+// { value: '历史', label: '历史' },
+// { value: '悬疑', label: '悬疑' },
+// { value: '其他', label: '其他' },
+// ]; // Movie
+// }
+// else if(selectedCategory === '6') {
+// options = [
+// { value: '真人秀', label: '真人秀' },
+// { value: '选秀', label: '选秀' },
+// { value: '访谈', label: '访谈' },
+// { value: '音乐', label: '音乐' },
+// { value: '游戏', label: '游戏' },
+// { value: '其他', label: '其他' },
+// ];
+// }
+// break;
+// case 'style':
+// if (selectedCategory === '3') { // Music
+// options = [
+// { value: '流行', label: '流行' },
+// { value: '摇滚', label: '摇滚' },
+// { value: '电子', label: '电子' },
+// { value: '古典', label: '古典' },
+// { value: '爵士', label: '爵士' },
+// { value: '民谣', label: '民谣' },
+// { value: '说唱', label: '说唱' },
+// { value: '其他', label: '其他' },
+// ];
+// } else {
+// options = []; // 根据需要定义
+// }
+// break;
+// case 'dataType':
+// if (selectedCategory === '5') { // Game
+// options = [
+// { value: '压缩包', label: '压缩包' },
+// { value: '补丁', label: '补丁' },
+// { value: '安装包', label: '安装包' },
+// { value: 'nds', label: 'nds' },
+// { value: '其他', label: '其他' },
+// ];
+// } else {
+// options = []; // 根据需要定义
+// }
+// break;
+// case 'format': if(selectedCategory === '4') {
+// options = [
+// { value: 'ZIP', label: 'ZIP' },
+// { value: 'RAR', label: 'RAR' },
+// { value: '7Z', label: '7Z' },
+// { value: 'MKV', label: 'MKV' },
+// { value: 'MP4', label: 'MP4' },
+// { value: '其他', label: '其他' },
+// ];
+// } else if(selectedCategory === '6') {
+// options = [
+// { value: '1080P', label: '1080P' },
+// { value: '4K', label: '4K' },
+// { value: 'HD', label: 'HD' },
+// { value: '其他', label: '其他' },
+// ];
+// } else if(selectedCategory === '2') {
+// options = [ { value: '720p', label: '720p' },
+// { value: '1080p', label: '1080p' },
+// { value: '2K', label: '2K' },
+// { value: '4K', label: '4K' },
+// { value: '8K', label: '8K' },
+// { value: '其他', label: '其他' },
+// ];
+// } else {
+// options = []; // 根据需要定义
+// }
+// break;
+// case 'eventType':
+// if (selectedCategory === '7') { // Sports
+// options = [
+// { value: '足球', label: '足球' },
+// { value: '篮球', label: '篮球' },
+// { value: '网球', label: '网球' },
+// { value: '乒乓球', label: '乒乓球' },
+// { value: '羽毛球', label: '羽毛球' },
+// { value: '其他', label: '其他' },
+// ];
+// } else {
+// options = []; // 根据需要定义
+// }
+// break;
+// case 'source':
+// if (selectedCategory === '10') { // Documentary
+// options = [
+// { value: 'CCTV', label: 'CCTV' },
+// { value: '卫视', label: '卫视' },
+// { value: '国家地理', label: '国家地理' },
+// { value: 'BBC', label: 'BBC' },
+// { value: 'Discovery', label: 'Discovery' },
+// { value: '其他', label: '其他' },
+// ];
+// } else {
+// options = []; // 根据需要定义
+// }
+// break;
+// default:
+// options = []; // 默认无选项
+// }
+
+// return (
+// <select
+// key={filter.id}
+// name={filter.id}
+// value={filters[filter.id] || ''}
+// onChange={handleFilterChange}
+// className="border rounded px-2 py-1"
+// >
+// <option value="">全部</option>
+// {options.map(option => (
+// <option key={option.value} value={option.value}>
+// {option.label}
+// </option>
+// ))}
+// </select>
+// );
+// }
+// // 其他类型(如输入框)可以类似实现
+// return null;
+// })}
+// </div>
+// )}
+// </div>
+
+// {/* 筛选按钮 */}
+// <button
+// onClick={() => {
+// // 这里可以根据需要添加额外的筛选逻辑
+// // 例如,如果某些筛选需要联动,可以在这里处理
+// }}
+// className="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600 ml-4"
+// >
+// 应用筛选
+// </button>
+
+// {/* 种子列表 */}
+// <table className="w-full border-collapse mt-4">
+// <thead>
+// <tr className="bg-gray-200">
+// <th className="p-2 border">名称</th>
+// <th className="p-2 border">上传者</th>
+// <th className="p-2 border">描述</th>
+// <th className="p-2 border">上传时间</th>
+// <th className="p-2 border">下载次数</th>
+// <th className="p-2 border">促销方式</th>
+// <th className="p-2 border">操作</th>
+// </tr>
+// </thead>
+// <tbody>
+// {torrents.map(t => (
+// <tr key={t.torrentid} className="border-t hover:bg-gray-100">
+// <td className="p-2 border">{t.filename}</td>
+// <td className="p-2 border">{t.uploader_id}</td>
+// <td className="p-2 border">{t.description}</td>
+// <td className="p-2 border">{new Date(t.uploadTime).toLocaleString()}</td>
+// <td className="p-2 border">{t.downloadCount}</td>
+// <td className="p-2 border">
+// {(() => {
+// switch(t.promotionid) {
+// case 1: return '上传加倍';
+// case 2: return '下载免费';
+// case 3: return '下载减半';
+// case 0: return '没有促销';
+// default: return '没有促销';
+// }
+// })()}
+// </td>
+// <td className="p-2 border">
+// <button
+// onClick={() => handleDownload(t.torrentid)}
+// className="text-blue-500 hover:underline mr-2"
+// >
+// 下载
+// </button>
+// <a
+// href={`/torrent/${t.torrentid}`}
+// className="text-green-600 hover:underline"
+// >
+// 查看详情
+// </a>
+// </td>
+// </tr>
+// ))}
+// </tbody>
+// </table>
+
+// {/* 成功提示 */}
+// {showSuccess && (
+// <div className="mt-4 p-3 bg-green-100 text-green-800 border border-green-300 rounded">
+// 上传成功!
+// </div>
+// )}
+// </div>
+// );
+// }
+
+// export default TorrentList;
+import { useState, useEffect } from 'react';
+import axios from 'axios';
+
+// 常量配置集中管理
+const FILTER_OPTIONS = {
+ // 通用选项
+ common: {
+ resolution: [
+ { value: '720p', label: '720p' },
+ { value: '1080p', label: '1080p' },
+ { value: '2K', label: '2K' },
+ { value: '4K', label: '4K' },
+ { value: '8K', label: '8K' },
+ { value: '其他', label: '其他' },
+ ],
+ region: {
+ movie: [
+ { value: '大陆', label: '大陆' },
+ { value: '港台', label: '港台' },
+ { value: '欧美', label: '欧美' },
+ { value: '日韩', label: '日韩' },
+ { value: '其他', label: '其他' },
+ ],
+ variety: [
+ { value: '大陆', label: '大陆' },
+ { value: '港台', label: '港台' },
+ { value: '欧美', label: '欧美' },
+ { value: '日韩', label: '日韩' },
+ { value: '其他', label: '其他' },
+ ],
+ sports: [
+ { value: '亚洲', label: '亚洲' },
+ { value: '欧洲', label: '欧洲' },
+ { value: '美洲', label: '美洲' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ genre: {
+ movie: [
+ { value: '动作', label: '动作' },
+ { value: '喜剧', label: '喜剧' },
+ { value: '爱情', label: '爱情' },
+ { value: '科幻', label: '科幻' },
+ { value: '恐怖', label: '恐怖' },
+ { value: '冒险', label: '冒险' },
+ { value: '历史', label: '历史' },
+ { value: '悬疑', label: '悬疑' },
+ { value: '其他', label: '其他' },
+ ],
+ music: [
+ { value: '流行', label: '流行' },
+ { value: '摇滚', label: '摇滚' },
+ { value: '电子', label: '电子' },
+ { value: '古典', label: '古典' },
+ { value: '爵士', label: '爵士' },
+ { value: '民谣', label: '民谣' },
+ { value: '说唱', label: '说唱' },
+ { value: '其他', label: '其他' },
+ ],
+ anime: [
+ { value: '新番连载', label: '新番连载' },
+ { value: '剧场版', label: '剧场版' },
+ { value: 'OVA', label: 'OVA' },
+ { value: '完结动漫', label: '完结动漫' },
+ { value: '其他', label: '其他' },
+ ],
+ game: [
+ { value: '角色扮演', label: '角色扮演' },
+ { value: '射击', label: '射击' },
+ { value: '冒险', label: '冒险' },
+ { value: '策略', label: '策略' },
+ { value: '体育', label: '体育' },
+ { value: '桌面游戏', label: '桌面游戏' },
+ { value: '其他', label: '其他' },
+ ],
+ variety: [
+ { value: '真人秀', label: '真人秀' },
+ { value: '选秀', label: '选秀' },
+ { value: '访谈', label: '访谈' },
+ { value: '音乐', label: '音乐' },
+ { value: '游戏', label: '游戏' },
+ { value: '其他', label: '其他' },
+ ],
+ learning: [
+ { value: '计算机', label: '计算机' },
+ { value: '软件', label: '软件' },
+ { value: '人文', label: '人文' },
+ { value: '外语', label: '外语' },
+ { value: '理工科', label: '理工科' },
+ { value: '其他', label: '其他' },
+ ],
+ sports: [
+ { value: '足球', label: '足球' },
+ { value: '篮球', label: '篮球' },
+ { value: '网球', label: '网球' },
+ { value: '乒乓球', label: '乒乓球' },
+ { value: '羽毛球', label: '羽毛球' },
+ { value: '其他', label: '其他' },
+ ],
+ // 其他类型...
+ }
+ },
+
+ // 分类特定选项
+ categories: {
+ 1: { // 电影
+ name: '电影',
+ filters: [
+ { id: 'resolution', label: '分辨率', type: 'select' },
+ { id: 'codec_format', label: '编码格式', type: 'select',
+ options: [
+ { value: 'H.264', label: 'H.264' },
+ { value: 'H.265', label: 'H.265' },
+ { value: 'AV1', label: 'AV1' },
+ { value: 'VC1', label: 'VC1' },
+ { value: 'X264', label: 'X264' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'region', label: '地区', type: 'select' },
+ { id: 'genre', label: '类型', type: 'select' }
+ ]
+ },
+ 2: { // 电视剧
+ name: '剧集',
+ filters: [
+ { id: 'region', label: '地区', type: 'select',
+ options: [
+ { value: '大陆', label: '大陆' },
+ { value: '港台', label: '港台' },
+ { value: '欧美', label: '欧美' },
+ { value: '日韩', label: '日韩' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'format', label: '分辨率', type: 'select',
+ options: [
+ { value: '720p', label: '720p' },
+ { value: '1080p', label: '1080p' },
+ { value: '2K', label: '2K' },
+ { value: '4K', label: '4K' },
+ { value: '8K', label: '8K' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'genre', label: '类型', type: 'select' ,
+ options:[
+ { value: '真人秀', label: '真人秀' },
+ { value: '选秀', label: '选秀' },
+ { value: '访谈', label: '访谈' },
+ { value: '游戏', label: '游戏' },
+ { value: '音乐', label: '音乐' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 3: { // 音乐
+ name: '音乐',
+ filters: [
+ { id: 'genre', label: '类型', type: 'select',
+ options: [
+ { value: '专辑', label: '专辑' },
+ { value: '单曲', label: '单曲' },
+ { value: 'EP', label: 'EP' },
+ { value: '现场', label: '现场' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'style', label: '风格', type: 'select',
+ options: [
+ { value: '流行', label: '流行' },
+ { value: '摇滚', label: '摇滚' },
+ { value: '电子', label: '电子' },
+ { value: '古典', label: '古典' },
+ { value: '爵士', label: '爵士' },
+ { value: '民谣', label: '民谣' },
+ { value: '说唱', label: '说唱' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ {id:'format', label: '格式', type: 'select',
+ options:[
+ { value: 'MP3', label: 'MP3' },
+ { value: 'FLAC', label: 'FLAC' },
+ { value: 'WAV', label: 'WAV' },
+ { value: 'AAC', label: 'AAC' },
+ { value: 'OGG', label: 'OGG' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 4: { // 动漫
+ name: '动漫',
+ filters: [
+ { id: 'genre', label: '类型', type: 'select' ,
+ options: [
+ { value: '新番连载', label: '新番连载' },
+ { value: '剧场版', label: '剧场版' },
+ { value: 'OVA', label: 'OVA' },
+ { value: '完结动漫', label: '完结动漫' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'format', label: '格式', type: 'select' ,
+ options:[
+ { value: 'ZIP', label: 'ZIP' },
+ { value: 'RAR', label: 'RAR' },
+ { value: '7Z', label: '7Z' },
+ { value: 'MKV', label: 'MKV' },
+ { value: 'MP4', label: 'MP4' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'resolution', label: '分辨率', type: 'select',
+ options:[
+ { value: '720p', label: '720p' },
+ { value: '1080p', label: '1080p' },
+ { value: '2K', label: '2K' },
+ { value: '4K', label: '4K' },
+ { value: '8K', label: '8K' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 5: { // 游戏
+ name: '游戏',
+ filters: [
+ { id: 'platform', label: '平台', type: 'select',
+ options: [
+ { value: 'PC', label: 'PC' },
+ { value: 'PS5', label: 'PS5' },
+ { value: 'Xbox', label: 'Xbox' },
+ { value: 'Switch', label: 'Switch' },
+ { value: '手机', label: '手机' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'genre', label: '类型', type: 'select',
+ options: [
+ { value: '角色扮演', label: '角色扮演' },
+ { value: '射击', label: '射击' },
+ { value: '冒险', label: '冒险' },
+ { value: '策略', label: '策略' },
+ { value: '体育', label: '体育' },
+ { value: '桌面游戏', label: '桌面游戏' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'data_format', label: '数据类型', type: 'select' ,
+ options: [
+ { value: '压缩包', label: '压缩包' },
+ { value: '补丁', label: '补丁' },
+ { value: '安装包', label: '安装包' },
+ { value: 'nds', label: 'nds' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'language', label: '语言', type: 'select',
+ options: [
+ { value: '中文', label: '中文' },
+ { value: '英文', label: '英文' },
+ { value: '日文', label: '日文' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 6: { // 综艺
+ name: '综艺',
+ filters: [
+ { id: 'is_mainland', label: '是否大陆综艺', type: 'select' ,
+ options:[
+ { value: 'true', label: '是' },
+ { value: 'false', label: ' 不是' },
+ ]
+ },
+ { id: 'format', label: '分辨率', type: 'select',
+ options:[
+ { value: '720p', label: '720p' },
+ { value: '1080p', label: '1080p' },
+ { value: '2K', label: '2K' },
+ { value: '4K', label: '4K' },
+ { value: '8K', label: '8K' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ {id: 'genre', label: '类型', type: 'select',
+ options:[
+ { value: '真人秀', label: '真人秀' },
+ { value: '选秀', label: '选秀' },
+ { value: '访谈', label: '访谈' },
+ { value: '游戏', label: '游戏' },
+ { value: '音乐', label: '音乐' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 7: { // 体育
+ name: '体育',
+ filters: [
+ { id: 'genre', label: '体育类型', type: 'select' ,
+ options:[
+ { value: '足球', label: '足球' },
+ { value: '篮球', label: '篮球' },
+ { value: '网球', label: '网球' },
+ { value: '乒乓球', label: '乒乓球' },
+ { value: '羽毛球', label: '羽毛球' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'event_type', label: '赛事类型', type: 'select',
+ options:[
+ { value: '足球', label: '足球' },
+ { value: '篮球', label: '篮球' },
+ { value: '网球', label: '网球' },
+ { value: '乒乓球', label: '乒乓球' },
+ { value: '羽毛球', label: '羽毛球' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'format', label: '分辨率', type: 'select',
+ options:[
+ { value: '720p', label: '720p' },
+ { value: '1080p', label: '1080p' },
+ { value: '2K', label: '2K' },
+ { value: '4K', label: '4K' },
+ { value: '8K', label: '8K' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 8: { // 软件
+ name: '软件',
+ filters: [
+ { id: 'platform', label: '平台', type: 'select' ,
+ options:[
+ { value: 'Windows', label: 'Windows' },
+ { value: 'Mac', label: 'Mac' },
+ { value: 'Linux', label: 'Linux' },
+ { value: 'Android', label: 'Android' },
+ { value: 'iOS', label: 'iOS' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'format', label: '格式', type: 'select',
+ options:[
+ { value: 'EXE', label: 'EXE' },
+ { value: 'DMG', label: 'DMG' },
+ { value: '光盘镜像', label: '光盘镜像' },
+ { value: 'APK', label: 'APK' },
+ { value: 'IPA', label: 'IPA' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'genre', label: '类型', type: 'select',
+ options:[
+ { value: '系统软件', label: '系统软件' },
+ { value: '应用软件', label: '应用软件' },
+ { value: '游戏软件', label: '游戏软件' },
+ { value: '驱动程序', label: '驱动程序' },
+ { value: '办公软件', label: '办公软件' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ ]
+ },
+ 9: { // 学习
+ name: '学习',
+ filters: [
+ { id: 'genre', label: '类型', type: 'select' ,
+ options:[
+ { value: '计算机', label: '计算机' },
+ { value: '软件', label: '软件' },
+ { value: '人文', label: '人文' },
+ { value: '外语', label: '外语' },
+ { value: '理工科', label: '理工科' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'format', label: '格式', type: 'select',
+ options:[
+ { value: 'PDF', label: 'PDF' },
+ { value: 'EPUB', label: 'EPUB' },
+ { value: '视频', label: '视频' },
+ { value: '音频', label: '音频' },
+ { value: 'PPT', label: 'PPT' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 10: { // 纪录片
+ name: '纪录片',
+ filters: [
+ { id: 'source', label: '视频源', type: 'select',
+ options:[
+ { value: 'CCTV', label: 'CCTV' },
+ { value: '卫视', label: '卫视' },
+ { value: '国家地理', label: '国家地理' },
+ { value: 'BBC', label: 'BBC' },
+ { value: 'Discovery', label: 'Discovery' },
+ { value: '其他', label: '其他' },
+ ]
+ },
+ { id: 'format', label: '格式', type: 'select',
+ options:[
+ { value: '720p', label: '720p' },
+ { value: '1080p', label: '1080p' },
+ { value: '2K', label: '2K' },
+ { value: '4K', label: '4K' },
+ { value: '8K', label: '8K' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ },
+ 11: { // 其他
+ name: '其他',
+ filters: [
+ { id: 'gener', label: '类型', type: 'select',
+ options:[
+ { value: '电子书', label: '电子书' },
+ { value: '视频', label: '视频' },
+ { value: 'MP3', label: 'MP3' },
+ { value: '图片', label: '图片' },
+ { value: '其他', label: '其他' },
+ ]
+ }
+ ]
+ }
+ // 其他分类配置...
+ }
+};
+
+// 获取分类筛选配置
+const getCategoryFilters = (categoryId) => {
+ const category = FILTER_OPTIONS.categories[categoryId];
+ if (!category) return [];
+
+ return category.filters.map(filter => {
+ // 自动填充通用选项
+ if (filter.id === 'resolution' && !filter.options) {
+ return { ...filter, options: FILTER_OPTIONS.common.resolution };
+ }
+ if (filter.id === 'region' && !filter.options) {
+ const regionType = categoryId === 8 ? 'sports' : 'movie';
+ return { ...filter, options: FILTER_OPTIONS.common.region[regionType] };
+ }
+ if (filter.id === 'genre' && !filter.options) {
+ const genreType = categoryId === 3 ? 'music' : 'movie';
+ return { ...filter, options: FILTER_OPTIONS.common.genre[genreType] };
+ }
+ return filter;
+ });
+};
+
+// 格式化日期显示
+const formatDate = (dateString) => {
+ const options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' };
+ return new Date(dateString).toLocaleString('zh-CN', options);
+};
+
+// 获取促销方式名称
+const getPromotionName = (promotionId) => {
+ switch(promotionId) {
+ case 1: return '上传加倍';
+ case 2: return '下载免费';
+ case 3: return '下载减半';
+ default: return '没有促销';
+ }
+};
+
+function TorrentList() {
+ const [torrents, setTorrents] = useState([]);
+ const [categories, setCategories] = useState([]);
+ const [selectedCategory, setSelectedCategory] = useState('');
+ const [filters, setFilters] = useState({});
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ // 获取所有分类
+ useEffect(() => {
+ const fetchCategories = async () => {
+ try {
+ const res = await axios.get('http://localhost:8080/categories');
+ setCategories(res.data);
+ } catch (err) {
+ console.error('加载分类失败', err);
+ setError('加载分类失败,请稍后重试');
+ }
+ };
+ fetchCategories();
+ }, []);
+
+ // 获取种子数据
+ useEffect(() => {
+ const fetchTorrents = async () => {
+ setIsLoading(true);
+ setError(null);
+ try {
+ let url = selectedCategory
+ // ? `http://localhost:8080/torrent/listByCategory?categoryid=${selectedCategory}`
+ ? `http://localhost:8080/torrent/listByCategorywithfilter?categoryid=${selectedCategory}`
+ : 'http://localhost:8080/torrent/list';
+
+ // 添加筛选参数
+ const params = new URLSearchParams();
+ Object.entries(filters).forEach(([key, value]) => {
+ if (value) params.append(key, value);
+ });
+
+ const res = await axios.get(`${url}&${params.toString()}`);
+ setTorrents(res.data);
+ console.log('torrents:', torrents);
+ } catch (err) {
+ console.error('获取种子失败', err);
+ setError('获取种子列表失败,请稍后重试');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+ console.log('Fetching torrents with filters:', filters);
+ console.log('Selected category:', selectedCategory);
+ console.log(torrents);
+ const timer = setTimeout(fetchTorrents, 300); // 防抖
+ return () => clearTimeout(timer);
+ }, [selectedCategory, filters]);
+
+ // 切换分类时重置筛选条件
+ const handleCategoryChange = (categoryId) => {
+ setSelectedCategory(categoryId);
+ setFilters({});
+ };
+
+ // 处理筛选条件变化
+ const handleFilterChange = (e) => {
+ const { name, value } = e.target;
+ setFilters(prev => ({ ...prev, [name]: value }));
+ };
+
+ // 下载种子
+ const handleDownload = (torrentId) => {
+ window.open(`http://localhost:8080/torrent/download/${torrentId}`, '_blank');
+ };
+
+ // 获取当前分类的筛选配置
+ const currentFilters = getCategoryFilters(selectedCategory);
+
+ return (
+ <div className="p-4 max-w-7xl mx-auto">
+ <h1 className="text-2xl font-bold mb-6">种子列表</h1>
+
+ {/* 分类选择 */}
+ <div className="mb-6 bg-white p-4 rounded-lg shadow">
+ <div className="flex flex-wrap items-center gap-4">
+ <div className="flex items-center">
+ <label className="mr-2 font-medium whitespace-nowrap">选择分类:</label>
+ <select
+ value={selectedCategory}
+ onChange={(e) => handleCategoryChange(e.target.value)}
+ className="border rounded px-3 py-2 min-w-[150px]"
+ >
+ <option value="">全部分类</option>
+ {categories.map(cat => (
+ <option key={cat.categoryid} value={cat.categoryid}>
+ {cat.category_name}
+ </option>
+ ))}
+ </select>
+ </div>
+
+ {/* 动态筛选表单 */}
+ {currentFilters.length > 0 && (
+ <div className="flex flex-wrap gap-4">
+ {currentFilters.map(filter => (
+ <div key={filter.id} className="flex items-center">
+ <label className="mr-2 text-sm whitespace-nowrap">{filter.label}:</label>
+ <select
+ name={filter.id}
+ value={filters[filter.id] || ''}
+ onChange={handleFilterChange}
+ className="border rounded px-3 py-2 min-w-[120px]"
+ >
+ <option value="">全部</option>
+ {filter.options.map(option => (
+ <option key={option.value} value={option.value}>
+ {option.label}
+ </option>
+ ))}
+ </select>
+ </div>
+ ))}
+ </div>
+ )}
+ </div>
+ </div>
+
+
+ {/* 错误提示 */}
+ {error && (
+ <div className="mb-4 p-3 bg-red-100 text-red-700 rounded border border-red-200">
+ {error}
+ </div>
+ )}
+
+
+ {/* 加载状态 */}
+ {isLoading ? (
+ <div className="flex justify-center items-center h-64">
+ <div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div>
+ </div>
+ ) : (
+ /* 种子列表 */
+ <div className="bg-white rounded-lg shadow overflow-hidden">
+ <div className="overflow-x-auto">
+ <table className="w-full">
+ <thead className="bg-gray-50">
+ <tr>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">名称</th>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">大小</th>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">上传者</th>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">上传时间</th>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">下载次数</th>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">促销</th>
+ <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
+ </tr>
+ </thead>
+ <tbody className="bg-white divide-y divide-gray-200">
+ {torrents.length > 0 ? (
+ torrents.map(torrent => (
+ <tr key={torrent.torrentid} className="hover:bg-gray-50">
+ <td className="px-6 py-4 whitespace-nowrap">
+ <div className="text-sm font-medium text-gray-900">{torrent.filename}</div>
+ <div className="text-sm text-gray-500">{torrent.description}</div>
+ </td>
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+ {torrent.torrentSize} B
+ </td>
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+ {torrent.uploader_id}
+ </td>
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+ {new Date(torrent.uploadTime).toLocaleString()}
+ </td>
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+ {torrent.downloadCount}
+ </td>
+ <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
+ {getPromotionName(torrent.promotionid)}
+ </td>
+ <td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
+ <button
+ onClick={() => handleDownload(torrent.torrentid)}
+ className="text-blue-600 hover:text-blue-900 mr-4"
+ >
+ 下载
+ </button>
+ <a
+ href={`/torrent/${torrent.torrentid}`}
+ className="text-green-600 hover:text-green-900"
+ >
+ 详情
+ </a>
+ </td>
+ </tr>
+ ))
+ ) : (
+ <tr>
+ <td colSpan="7" className="px-6 py-4 text-center text-sm text-gray-500">
+ 没有找到符合条件的种子
+ </td>
+ </tr>
+ )}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+}
+
+export default TorrentList;
\ No newline at end of file