create-seed-detail
Change-Id: I7dcce81a7510b6fa97781d3ce509a8dc2ac229d4
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index 3519f17..f32224e 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -1,246 +1,3 @@
-// import React, { useState, useEffect } from 'react';
-// import { Link } from 'wouter';
-// import axios from 'axios';
-// import logo from '../../assets/logo.png';
-// import Recommend from './Recommend/Recommend';
-// import './SeedList.css';
-
-// const API_BASE = process.env.REACT_APP_API_BASE;
-
-// const SeedList = () => {
-// const [seeds, setSeeds] = useState([]);
-// const [filteredSeeds, setFilteredSeeds] = useState([]);
-// const [loading, setLoading] = useState(true);
-// const [searchTerm, setSearchTerm] = useState('');
-// const [sortOption, setSortOption] = useState('最新');
-// const [activeTab, setActiveTab] = useState('种子列表');
-
-// const [filters, setFilters] = useState({});
-// const [selectedFilters, setSelectedFilters] = useState({});
-
-// const TAGS = ['猜你喜欢', '电影', '电视剧', '动漫', '音乐', '游戏', '综艺', '软件', '体育', '学习', '纪录片', '其他'];
-
-// const CATEGORY_MAP = {
-// '电影': 'movie',
-// '电视剧': 'tv',
-// '动漫': 'anime',
-// '音乐': 'music',
-// '游戏': 'game',
-// '综艺': 'variety',
-// '软件': 'software',
-// '体育': 'sports',
-// '学习': 'study',
-// '纪录片': 'documentary',
-// '其他': 'other'
-// };
-
-// const buildQueryParams = () => {
-// const category = CATEGORY_MAP[activeTab];
-
-// const params = {
-// category,
-// sort_by: sortOption === '最新' ? 'newest'
-// : sortOption === '最热' ? 'downloads'
-// : undefined,
-// page: 1,
-// limit: 20,
-// };
-
-// const tags = Object.entries(selectedFilters)
-// .filter(([_, value]) => value !== '不限')
-// .map(([_, value]) => value);
-
-// if (tags.length > 0) params.tags = tags.join(',');
-
-// return params;
-// };
-
-// const fetchSeeds = async () => {
-// setLoading(true);
-// try {
-// const params = buildQueryParams();
-// const queryString = new URLSearchParams(params).toString();
-// const response = await fetch(`${API_BASE}/echo/seeds?${queryString}`);
-// const data = await response.json();
-// const seeds = data?.seeds || [];
-// setSeeds(seeds);
-// setFilteredSeeds(seeds);
-// } catch (error) {
-// console.error('获取种子列表失败:', error);
-// } finally {
-// setLoading(false);
-// }
-// };
-
-// const fetchFilterOptions = async () => {
-// if (activeTab === '猜你喜欢') return;
-// const category = CATEGORY_MAP[activeTab];
-// try {
-// const res = await axios.get(`${API_BASE}/echo/seed-filters?category=${category}`);
-// setFilters(res.data || {});
-// const defaultSelections = {};
-// for (const key in res.data) {
-// defaultSelections[key] = '不限';
-// }
-// setSelectedFilters(defaultSelections);
-// } catch (err) {
-// console.error('获取筛选项失败:', err);
-// setFilters({});
-// setSelectedFilters({});
-// }
-// };
-
-// useEffect(() => {
-// if (activeTab !== '猜你喜欢') {
-// fetchFilterOptions();
-// }
-// }, [activeTab]);
-
-// useEffect(() => {
-// if (activeTab !== '猜你喜欢') {
-// fetchSeeds();
-// }
-// }, [activeTab, sortOption, selectedFilters]);
-
-// const handleDownload = async (seedId) => {
-// const peer_id = 'echo-' + Math.random().toString(36).substring(2, 10);
-// const ip = '127.0.0.1';
-// const port = 6881;
-// const uploaded = 0;
-// const downloaded = 0;
-// const left = 0;
-
-// try {
-// const response = await axios.get(`${API_BASE}/echo/seeds/${seedId}/download`, {
-// params: {
-// peer_id,
-// ip,
-// port,
-// uploaded,
-// downloaded,
-// left,
-// },
-// responseType: 'blob'
-// });
-
-// const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
-// const downloadUrl = URL.createObjectURL(blob);
-// const a = document.createElement('a');
-// a.href = downloadUrl;
-// a.download = `${seedId}.torrent`;
-// a.click();
-// URL.revokeObjectURL(downloadUrl);
-// } catch (error) {
-// console.error('下载失败:', error);
-// alert('下载失败,请稍后再试。');
-// }
-// };
-
-// return (
-// <div className="main-page">
-// <header className="header">
-// <div className="logo-and-name">
-// <img src={logo} alt="网站logo" className="logo" />
-// <span className="site-name">Echo</span>
-// </div>
-// <div className="user-and-message">
-// <img src="user-avatar.png" alt="用户头像" className="user-avatar" />
-// <span className="message-center">消息</span>
-// </div>
-// </header>
-
-// <nav className="nav">
-// <Link to="/friend-moments" className="nav-item">好友动态</Link>
-// <Link to="/forum" className="nav-item">论坛</Link>
-// <Link to="/interest-groups" className="nav-item">兴趣小组</Link>
-// <Link to="/seed-list" className="nav-item active">种子列表</Link>
-// <Link to="/publish-seed" className="nav-item">发布种子</Link>
-// </nav>
-
-// <div className="controls">
-// <input
-// type="text"
-// placeholder="搜索种子..."
-// value={searchTerm}
-// onChange={(e) => setSearchTerm(e.target.value)}
-// className="search-input"
-// />
-// <select value={sortOption} onChange={(e) => setSortOption(e.target.value)} className="sort-select">
-// <option value="最新">最新</option>
-// <option value="最热">最热</option>
-// </select>
-// </div>
-
-// <div className="tag-filters">
-// {TAGS.map((tag) => (
-// <button
-// key={tag}
-// className={`tag-button ${activeTab === tag ? 'active-tag' : ''}`}
-// onClick={() => {
-// setActiveTab(tag);
-// setFilters({});
-// setSelectedFilters({});
-// }}
-// >
-// {tag}
-// </button>
-// ))}
-// </div>
-
-// {activeTab !== '猜你喜欢' && Object.keys(filters).length > 0 && (
-// <div className="filter-bar">
-// {Object.entries(filters).map(([key, options]) => (
-// <div className="filter-group" key={key}>
-// <label>{key}:</label>
-// <select
-// value={selectedFilters[key]}
-// onChange={(e) =>
-// setSelectedFilters({ ...selectedFilters, [key]: e.target.value })
-// }
-// >
-// {options.map((opt) => (
-// <option key={opt} value={opt}>{opt}</option>
-// ))}
-// </select>
-// </div>
-// ))}
-// </div>
-// )}
-
-// <div className="seed-list-content">
-// {activeTab === '猜你喜欢' ? (
-// <Recommend />
-// ) : loading ? (
-// <p>加载中...</p>
-// ) : filteredSeeds.length === 0 ? (
-// <p>未找到符合条件的种子。</p>
-// ) : (
-// <div className="seed-cards">
-// {filteredSeeds.map((seed, index) => (
-// <div key={index} className="seed-card">
-// <div className="seed-card-header">
-// <h3>{seed.title}</h3>
-// </div>
-// <div className="seed-card-body">
-// <p><strong>大小:</strong> {seed.size || '未知'} GB</p>
-// <p><strong>时间:</strong> {seed.upload_time?.split('T')[0] || '未知'}</p>
-// <p><strong>下载数:</strong> {seed.downloads ?? 0}</p>
-// </div>
-// <div className="seed-card-actions">
-// <button className="btn-primary" onClick={() => handleDownload(seed.seed_id)}>下载</button>
-// <Link href={`/seed/${seed.seed_id}`} className="btn-secondary">详情</Link>
-// <button className="btn-outline">收藏</button>
-// </div>
-// </div>
-// ))}
-// </div>
-// )}
-// </div>
-// </div>
-// );
-// };
-
-// export default SeedList;
import React, { useState, useEffect } from 'react';
import { Link } from 'wouter';
import axios from 'axios';
@@ -259,7 +16,7 @@
const [activeTab, setActiveTab] = useState('种子列表');
const [filters, setFilters] = useState({});
const [selectedFilters, setSelectedFilters] = useState({});
- const [tagMode, setTagMode] = useState('all'); // 支持 tag_mode 参数
+ const [tagMode, setTagMode] = useState('all');
const [errorMsg, setErrorMsg] = useState('');
const TAGS = ['猜你喜欢', '电影', '电视剧', '动漫', '音乐', '游戏', '综艺', '软件', '体育', '学习', '纪录片', '其他'];
@@ -285,12 +42,10 @@
sort_by: sortOption === '最新' ? 'newest' : sortOption === '最热' ? 'downloads' : undefined,
page: 1,
limit: 20,
- include_fields: 'seed_id,title,category,tags,size,upload_time,downloads',
+ include_fields: 'seed_id,title,category,tags,size,upload_time,downloads,image_url',
};
- if (searchTerm.trim()) {
- params.search = searchTerm.trim();
- }
+ if (searchTerm.trim()) params.search = searchTerm.trim();
const tags = Object.entries(selectedFilters)
.filter(([_, value]) => value !== '不限')
@@ -332,9 +87,11 @@
const category = CATEGORY_MAP[activeTab];
try {
const res = await axios.get(`${API_BASE}/echo/seed-filters?category=${category}`);
- setFilters(res.data || {});
+ const filterData = res.data || {};
+ setFilters(filterData);
+
const defaultSelections = {};
- for (const key in res.data) {
+ for (const key in filterData) {
defaultSelections[key] = '不限';
}
setSelectedFilters(defaultSelections);
@@ -384,6 +141,20 @@
}
};
+ const handleFilterChange = (key, value) => {
+ setSelectedFilters((prev) => ({
+ ...prev,
+ [key]: value
+ }));
+ };
+
+ const clearFilter = (key) => {
+ setSelectedFilters((prev) => ({
+ ...prev,
+ [key]: '不限'
+ }));
+ };
+
return (
<div className="main-page">
<header className="header">
@@ -446,14 +217,15 @@
<label>{key}:</label>
<select
value={selectedFilters[key]}
- onChange={(e) =>
- setSelectedFilters({ ...selectedFilters, [key]: e.target.value })
- }
+ onChange={(e) => handleFilterChange(key, e.target.value)}
>
{options.map((opt) => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
+ {selectedFilters[key] !== '不限' && (
+ <button className="clear-filter-btn" onClick={() => clearFilter(key)}>✕</button>
+ )}
</div>
))}
</div>
@@ -472,14 +244,28 @@
<div className="seed-cards">
{filteredSeeds.map((seed, index) => (
<div key={index} className="seed-card">
+ {seed.image_url && (
+ <img src={seed.image_url} alt={seed.title} className="seed-cover" />
+ )}
<div className="seed-card-header">
<h3>{seed.title}</h3>
</div>
+
<div className="seed-card-body">
- <p><strong>大小:</strong> {seed.size || '未知'} GB</p>
- <p><strong>时间:</strong> {seed.upload_time?.split('T')[0] || '未知'}</p>
- <p><strong>下载数:</strong> {seed.downloads ?? 0}</p>
+ <div className="seed-info">
+ <span>{seed.size || '未知'} GB</span>
+ <span>{seed.upload_time?.split('T')[0] || '未知'}</span>
+ <span>{seed.downloads ?? 0} 次下载</span>
+ </div>
+ {seed.tags && seed.tags.length > 0 && (
+ <div className="seed-card-tags">
+ {seed.tags.map((tag, i) => (
+ <span key={i} className="tag-label">{tag}</span>
+ ))}
+ </div>
+ )}
</div>
+
<div className="seed-card-actions">
<button className="btn-primary" onClick={() => handleDownload(seed.seed_id)}>下载</button>
<Link href={`/seed/${seed.seed_id}`} className="btn-secondary">详情</Link>