调试种子列表、下载种子
Change-Id: Iaf8fc868ae0f26af06e7e1e0ef4af436cd4f959d
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index eade11d..be8d13f 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -1,93 +1,99 @@
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 Header from '../../components/Header'; // 引入 Header 组件
import './SeedList.css';
-
-
+import { useUser } from '../../context/UserContext';
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 [tagMode, setTagMode] = useState('any'); // 与接口对应,any / all
const [errorMsg, setErrorMsg] = useState('');
+ const { user } = useUser();
+
const TAGS = ['猜你喜欢', '电影', '电视剧', '动漫', '音乐', '游戏', '综艺', '软件', '体育', '学习', '纪录片', '其他'];
const CATEGORY_MAP = {
- '电影': 'movie',
- '电视剧': 'tv',
- '动漫': 'anime',
- '音乐': 'music',
- '游戏': 'game',
- '综艺': 'variety',
- '软件': 'software',
- '体育': 'sports',
- '学习': 'education',
- '纪录片': 'documentary',
- '其他': 'other'
+ '电影': '电影',
+ '电视剧': '电视剧',
+ '动漫': '动漫',
+ '音乐': '音乐',
+ '游戏': '游戏',
+ '综艺': '综艺',
+ '软件': '软件',
+ '体育': '体育',
+ '学习': '学习',
+ '纪录片': '纪录片',
+ '其他': '其他',
+ '猜你喜欢': '',
+ '种子列表': '',
};
const buildQueryParams = () => {
- const category = CATEGORY_MAP[activeTab];
+ const category = CATEGORY_MAP[activeTab] || '';
+ const orderKey = sortOption === '最新' ? ['upload_time'] : (sortOption === '最热' ? ['downloads'] : ['upload_time']);
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',
+ size: 20,
+ orderKey,
+ orderDesc: true,
};
- if (searchTerm.trim()) params.search = searchTerm.trim();
+ if (searchTerm.trim()) {
+ params.title = searchTerm.trim();
+ }
+ if (category) {
+ params.category = category;
+ }
const tags = Object.entries(selectedFilters)
- .filter(([_, value]) => value!== '不限')
- .map(([_, value]) => value);
+ .filter(([_, value]) => value !== '不限')
+ .map(([_, value]) => value);
if (tags.length > 0) {
- params.tags = tags.join(',');
- params.tag_mode = tagMode;
+ params.tags = tags;
+ params.tagMode = tagMode; // any 或 all
}
return params;
};
const fetchSeeds = async () => {
+ if (activeTab === '猜你喜欢') return;
setLoading(true);
setErrorMsg('');
try {
const params = buildQueryParams();
- const queryString = new URLSearchParams(params).toString();
- const response = await fetch(`/echo/seeds?${queryString}`);
- const data = await response.json();
+ const response = await axios.post('/seeds/list', params);
+ const data = response.data;
- if (data.status!== 'success') throw new Error(data.message || '获取失败');
- const seeds = data.seeds || [];
- setSeeds(seeds);
- setFilteredSeeds(seeds);
+ if (data.code !== 0) {
+ throw new Error(data.msg || '获取失败');
+ }
+
+ setSeeds(data.data || []);
} catch (error) {
console.error('获取种子列表失败:', error);
setErrorMsg(error.message || '获取失败,请稍后再试。');
setSeeds([]);
- setFilteredSeeds([]);
} finally {
setLoading(false);
}
};
const fetchFilterOptions = async () => {
- if (activeTab === '猜你喜欢') return;
+ if (activeTab === '猜你喜欢' || !CATEGORY_MAP[activeTab]) return;
const category = CATEGORY_MAP[activeTab];
try {
- const res = await axios.get(`/echo/seed-filters?category=${category}`);
+ const res = await axios.get(`/seed-filters?category=${category}`);
const filterData = res.data || {};
setFilters(filterData);
@@ -104,28 +110,25 @@
};
useEffect(() => {
- if (activeTab!== '猜你喜欢') {
- fetchFilterOptions();
- }
+ fetchFilterOptions();
}, [activeTab]);
useEffect(() => {
- if (activeTab!== '猜你喜欢') {
- fetchSeeds();
- }
+ 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;
+ if (!user || !user.userId) {
+ alert('请先登录再下载种子文件');
+ return;
+ }
try {
- const response = await axios.get(`/echo/seeds/${seedId}/download`, {
- params: { peer_id, ip, port, uploaded, downloaded, left },
+ const response = await axios.get(`/seeds/${seedId}/download`, {
+ params: {
+ passkey: user.userId,
+ },
responseType: 'blob'
});
@@ -143,22 +146,22 @@
};
const handleFilterChange = (key, value) => {
- setSelectedFilters((prev) => ({
- ...prev,
+ setSelectedFilters(prev => ({
+ ...prev,
[key]: value
}));
};
const clearFilter = (key) => {
- setSelectedFilters((prev) => ({
- ...prev,
+ setSelectedFilters(prev => ({
+ ...prev,
[key]: '不限'
}));
};
return (
<div className="seed-list-container">
- <Header /> {/* 引用 Header 组件 */}
+ <Header />
<div className="controls">
<input
@@ -168,21 +171,29 @@
onChange={(e) => setSearchTerm(e.target.value)}
className="search-input"
/>
- <select value={sortOption} onChange={(e) => setSortOption(e.target.value)} className="sort-select">
+ <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">
+ <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) => (
+ {TAGS.map(tag => (
<button
key={tag}
- className={`tag-button ${activeTab === tag? 'active-tag' : ''}`}
+ className={`tag-button ${activeTab === tag ? 'active-tag' : ''}`}
onClick={() => {
setActiveTab(tag);
setFilters({});
@@ -194,7 +205,7 @@
))}
</div>
- {activeTab!== '猜你喜欢' && Object.keys(filters).length > 0 && (
+ {activeTab !== '猜你喜欢' && Object.keys(filters).length > 0 && (
<div className="filter-bar">
{Object.entries(filters).map(([key, options]) => (
<div className="filter-group" key={key}>
@@ -203,12 +214,17 @@
value={selectedFilters[key]}
onChange={(e) => handleFilterChange(key, e.target.value)}
>
- {options.map((opt) => (
+ {options.map(opt => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
- {selectedFilters[key]!== '不限' && (
- <button className="clear-filter-btn" onClick={() => clearFilter(key)}>✕</button>
+ {selectedFilters[key] !== '不限' && (
+ <button
+ className="clear-filter-btn"
+ onClick={() => clearFilter(key)}
+ >
+ ✕
+ </button>
)}
</div>
))}
@@ -216,13 +232,13 @@
)}
<div className="seed-list-content">
- {activeTab === '猜你喜欢'? (
+ {activeTab === '猜你喜欢' ? (
<Recommend />
- ) : loading? (
+ ) : loading ? (
<p>加载中...</p>
- ) : errorMsg? (
+ ) : errorMsg ? (
<p className="error-text">{errorMsg}</p>
- ) : filteredSeeds.length === 0? (
+ ) : seeds.length === 0 ? (
<p>未找到符合条件的种子。</p>
) : (
<div className="seed-list-card">
@@ -235,53 +251,99 @@
<div className="seed-header-actions">操作</div>
</div>
<div className="seed-list-body">
- {filteredSeeds.map((seed, index) => (
- <Link href={`/seed/${seed.seed_id}`} key={index} className="seed-item-link">
- <div className="seed-item">
- {seed.image_url && (
- <img src={seed.image_url} alt={seed.title} className="seed-item-cover" />
- )}
- <div className="seed-item-title">
- <div className="seed-title-row">
- <h3 className="seed-title">{seed.title}</h3>
- <div className="seed-tags">
- {seed.tags && seed.tags.map((tag, i) => (
- <span key={i} className="tag-label">{tag}</span>
- ))}
+ {seeds.map((seed, index) => {
+ // 处理 tags 字段,兼容字符串和数组
+ let tagsArray = [];
+ if (seed.tags) {
+ if (Array.isArray(seed.tags)) {
+ tagsArray = seed.tags;
+ } else if (typeof seed.tags === 'string') {
+ try {
+ tagsArray = JSON.parse(seed.tags);
+ if (!Array.isArray(tagsArray)) {
+ // 解析后不是数组,按逗号分割
+ tagsArray = seed.tags.split(',').map(t => t.trim()).filter(t => t);
+ }
+ } catch {
+ // 解析失败,按逗号分割
+ tagsArray = seed.tags.split(',').map(t => t.trim()).filter(t => t);
+ }
+ }
+ }
+
+ return (
+
+ <Link to={`/seed/${seed.id}`} key={index} className="seed-item-link">
+ <div className="seed-item">
+ {seed.image_url && (
+ <img
+ src={seed.image_url}
+ alt={seed.title}
+ className="seed-item-cover"
+ />
+ )}
+ <div className="seed-item-title">
+ <div className="seed-title-row">
+ <h3 className="seed-title">{seed.title}</h3>
+ <div className="seed-tags">
+ {tagsArray.map((tag, i) => (
+ <span key={i} className="tag-label">{tag}</span>
+ ))}
+ </div>
</div>
</div>
- </div>
- <div className="seed-item-size">{seed.size || '未知'}</div>
- <div className="seed-item-upload-time">{seed.upload_time?.split('T')[0] || '未知'}</div>
- <div className="seed-item-downloads">{seed.downloads?? 0} 次下载</div>
- <div
- className="seed-item-actions"
- onClick={(e) => e.stopPropagation()} // 阻止事件冒泡,避免跳转
- >
- <button
- className="btn-primary"
- onClick={(e) => {
- e.preventDefault(); // 阻止跳转
- e.stopPropagation();
- handleDownload(seed.seed_id);
- }}
+ <div className="seed-item-size">{seed.size || '未知'}</div>
+ <div className="seed-item-upload-time">{seed.upload_time?.split('T')[0] || '未知'}</div>
+ <div className="seed-item-downloads">{seed.downloads ?? 0} 次下载</div>
+ <div
+ className="seed-item-actions"
+ onClick={e => e.stopPropagation()} // 阻止事件冒泡,避免跳转
>
- 下载
- </button>
- <button
- className="btn-outline"
- onClick={(e) => {
- e.preventDefault();
- e.stopPropagation();
- // TODO: 收藏操作
- }}
- >
- 收藏
- </button>
+ <button
+ className="btn-primary"
+ onClick={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ handleDownload(seed.id);
+ }}
+ >
+ 下载
+ </button>
+ <button
+ className="btn-outline"
+ onClick={async (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (!user || !user.userId) {
+ alert('请先登录再收藏');
+ return;
+ }
+
+ try {
+ const res = await axios.post(`/seeds/${seed.id}/favorite-toggle`, null, {
+ params: { user_id: user.userId },
+ });
+
+ if (res.data.code === 0) {
+ alert('操作成功'); // 你可以改成 toast 或 icon 状态提示
+ } else {
+ alert(res.data.msg || '操作失败');
+ }
+ } catch (err) {
+ console.error('收藏失败:', err);
+ alert('收藏失败,请稍后再试。');
+ }
+ }}
+>
+ 收藏
+</button>
+
+ </div>
</div>
- </div>
- </Link>
- ))}
+ </Link>
+ );
+ })}
</div>
</div>
)}
@@ -291,4 +353,3 @@
};
export default SeedList;
-