blob: 8ffecb3df4f6af83132159bb528eb447f2a71775 [file] [log] [blame] [edit]
// 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="friend-moments">
// <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';
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 [tagMode, setTagMode] = useState('all');
const [errorMsg, setErrorMsg] = useState('');
const TAGS = ['猜你喜欢', '电影', '电视剧', '动漫', '音乐', '游戏', '综艺', '软件', '体育', '学习', '纪录片', '其他'];
const CATEGORY_MAP = {
'电影': 'movie',
'电视剧': 'tv',
'动漫': 'anime',
'音乐': 'music',
'游戏': 'game',
'综艺': 'variety',
'软件': 'software',
'体育': 'sports',
'学习': 'education',
'纪录片': 'documentary',
'其他': 'other'
};
const buildQueryParams = () => {
const category = CATEGORY_MAP[activeTab];
const params = {
category,
sort_by: sortOption === '最新' ? 'newest' : sortOption === '最热' ? 'downloads' : undefined,
page: 1,
limit: 20,
include_fields: 'seed_id,title,category,tags,size,upload_time,downloads,image_url',
};
if (searchTerm.trim()) params.search = searchTerm.trim();
const tags = Object.entries(selectedFilters)
.filter(([_, value]) => value !== '不限')
.map(([_, value]) => value);
if (tags.length > 0) {
params.tags = tags.join(',');
params.tag_mode = tagMode;
}
return params;
};
const fetchSeeds = async () => {
setLoading(true);
setErrorMsg('');
try {
const params = buildQueryParams();
const queryString = new URLSearchParams(params).toString();
const response = await fetch(`${API_BASE}/echo/seeds?${queryString}`);
const data = await response.json();
if (data.status !== 'success') throw new Error(data.message || '获取失败');
const seeds = data.seeds || [];
setSeeds(seeds);
setFilteredSeeds(seeds);
} catch (error) {
console.error('获取种子列表失败:', error);
setErrorMsg(error.message || '获取失败,请稍后再试。');
setSeeds([]);
setFilteredSeeds([]);
} 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}`);
const filterData = res.data || {};
setFilters(filterData);
const defaultSelections = {};
for (const key in filterData) {
defaultSelections[key] = '不限';
}
setSelectedFilters(defaultSelections);
} catch (err) {
console.error('获取筛选项失败:', err);
setFilters({});
setSelectedFilters({});
}
};
useEffect(() => {
if (activeTab !== '猜你喜欢') {
fetchFilterOptions();
}
}, [activeTab]);
useEffect(() => {
if (activeTab !== '猜你喜欢') {
fetchSeeds();
}
}, [activeTab, sortOption, selectedFilters, tagMode, searchTerm]);
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('下载失败,请稍后再试。');
}
};
const handleFilterChange = (key, value) => {
setSelectedFilters((prev) => ({
...prev,
[key]: value
}));
};
const clearFilter = (key) => {
setSelectedFilters((prev) => ({
...prev,
[key]: '不限'
}));
};
return (
<div className="friend-moments">
<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>
<select value={tagMode} onChange={(e) => setTagMode(e.target.value)} className="tag-mode-select">
<option value="any">包含任意标签</option>
<option value="all">包含所有标签</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) => 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>
)}
<div className="seed-list-content">
{activeTab === '猜你喜欢' ? (
<Recommend />
) : loading ? (
<p>加载中...</p>
) : errorMsg ? (
<p className="error-text">{errorMsg}</p>
) : filteredSeeds.length === 0 ? (
<p>未找到符合条件的种子。</p>
) : (
<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">
<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>
<button className="btn-outline">收藏</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
};
export default SeedList;