blob: c7609997649c0178a09ff98bcc6d765173286c85 [file] [log] [blame]
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
//import { Layout, Menu, Button, Radio,Input, } from 'antd';
import { Layout, Input, Button, Radio, Spin, message, Modal, Pagination } from 'antd';
const { Header, Content, Sider } = Layout;
import '../filter.css';
import '../torrentlist.css';
import '../complain.css';
import axios from 'axios';
import Navbar from './Navbar';
import {createComplain} from '../api/complain'; // 假设举报API在这个路径
// 常量配置集中管理
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: '其他' },
]
}
]
}
// 其他分类配置...
}
};
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);
//const userId = localStorage.getItem('userId'); // 假设用户ID存储在localStorage中
const userId = 1; // 确保是数字类型
const [usernames, setUsernames] = useState({});
const [searchKeyword, setSearchKeyword] = useState('');
// [新增] 分页相关状态
const [currentPage, setCurrentPage] = useState(1); // 当前页码
const [itemsPerPage, setItemsPerPage] = useState(10); // 每页显示的项目数
const [totalItems, setTotalItems] = useState(0); // 确保这行存在
const [isReportModalVisible, setIsReportModalVisible] = useState(false);
const [currentTorrentId, setCurrentTorrentId] = useState(null);
const [reportContent, setReportContent] = useState('');
const [currentTorrent, setCurrentTorrent] = useState({});
const[currentTorrentUploaderId, setCurrentTorrentUploaderId] = 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();
}, []);
// 获取分类筛选配置
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 showReportModal = (torrentId) => {
setCurrentTorrentId(torrentId);
setReportContent('');
setIsReportModalVisible(true);
};
// 处理举报提交
const handleReportSubmit = async () => {
if (!reportContent.trim()) {
message.error('请输入举报内容');
return;
}
try {
const currentTorrentData = torrents.find(t => t.torrentid === currentTorrentId);
const duser = currentTorrentData ? currentTorrentData.uploader_id : null;
const complainData = {
puse: userId, // 举报人ID
duser: duser, // 被举报人ID
content: reportContent,
torrentid: currentTorrentId,
};
console.log('举报数据:', complainData)
await createComplain(complainData);
message.success('举报提交成功');
setIsReportModalVisible(false);
} catch (error) {
console.error('举报提交失败:', error);
message.error('举报提交失败,请稍后重试');
}
};
// 格式化日期显示
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 '没有促销';
}
};
// 搜索种子
const handleSearch = async () => {
if (!searchKeyword.trim()) {
fetchAllTorrents();
return;
}
setIsLoading(true);
setError(null);
try {
const res = await axios.get(`http://localhost:8080/torrent/search`, {
params: { keyword: searchKeyword },
});
setTorrents(res.data);
//setCurrentPage(1); // 搜索后重置为第一页
} catch (err) {
console.error('搜索失败', err);
setError('搜索失败,请稍后重试');
message.error('搜索失败');
} finally {
setIsLoading(false);
}
};
// 获取种子数据
// [修改] 获取种子数据
useEffect(() => {
const fetchTorrents = async () => {
setIsLoading(true);
setError(null);
try {
let url = selectedCategory
? `http://localhost:8080/torrent/listByCategorywithfilter?categoryid=${selectedCategory}`
: `http://localhost:8080/torrent/list`;
// [修改] 添加筛选参数(不再需要page和limit参数)
const params = new URLSearchParams();
Object.entries(filters).forEach(([key, value]) => {
if (value) params.append(key, value);
});
// [修改] 只有当有筛选参数时才添加
if (params.toString()) {
const separator = selectedCategory ? '&' : '?';
url += separator + params.toString();
}
const res = await axios.get(url);
setTorrents(res.data); // [修改] 存储所有数据,不再分页
setTotalItems(res.data.length); // [新增] 设置总数据量
} catch (err) {
console.error('获取种子失败', err);
setError('获取种子列表失败,请稍后重试');
} finally {
setIsLoading(false);
}
};
const timer = setTimeout(fetchTorrents, 300);
return () => clearTimeout(timer);
}, [selectedCategory, filters]); // [注意] 依赖项不变
// [新增] 前端分页处理函数
const paginateData = (data, currentPage, itemsPerPage) => {
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
return data.slice(startIndex, endIndex);
};
const currentTorrents = paginateData(torrents, currentPage, itemsPerPage);
useEffect(() => {
const fetchUsernames = async () => {
if (torrents.length === 0) return;
const usernamePromises = torrents.map(async (torrent) => {
if (torrent.uploader_id && !usernames[torrent.uploader_id]) {
try {
const response = await fetch(`http://localhost:8080/torrent/${torrent.uploader_id}/username`);
if (response.ok) {
const username = await response.text();
return { [torrent.uploader_id]: username };
}
} catch (error) {
console.error(`Failed to fetch username for uploader_id ${torrent.uploader_id}:`, error);
}
}
return {};
});
const results = await Promise.all(usernamePromises);
const mergedUsernames = results.reduce((acc, curr) => ({ ...acc, ...curr }), {});
setUsernames((prev) => ({ ...prev, ...mergedUsernames }));
};
fetchUsernames();
}, [torrents]);
// 切换分类时重置筛选条件
const handleCategoryChange = (categoryId) => {
setSelectedCategory(categoryId);
setFilters({});
};
// 格式化文件大小
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
// 处理筛选条件变化
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');
window.open(`http://localhost:8080/torrent/download/${torrentId}?userId=${userId}`, '_blank');
};
// 获取当前分类的筛选配置
const currentFilters = getCategoryFilters(selectedCategory);
return (
<div className="p-4 max-w-7xl mx-auto">
<Navbar />
{/* <h1 className="text-2xl font-bold mb-6">种子列表</h1> */}
{/* 搜索框 */}
<div className="mb-4 flex items-center">
<Input
placeholder="搜索种子..."
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
style={{ width: 300 }}
onPressEnter={handleSearch}
/>
<Button
type="primary"
onClick={handleSearch}
style={{ marginLeft: 8 }}
>
搜索
</Button>
</div>
<div className="filter-container">
<div className="filter-container">
<div className="filter-row">
<label className="filter-label">选择分类:</label>
<Radio.Group
onChange={(e) => handleCategoryChange(e.target.value)}
value={selectedCategory}
className="flex space-x-2" // 添加 flex 布局
>
<Radio.Button className="custom-radio-btn" value="">
全部分类
</Radio.Button>
{categories.map(cat => (
<Radio.Button key={cat.categoryid} className="custom-radio-btn" value={cat.categoryid}>
{cat.category_name}
</Radio.Button>
))}
</Radio.Group>
</div>
</div>
{currentFilters.length > 0 &&
currentFilters.map(filter => (
<div key={filter.id} className="filter-row">
<label className="filter-label">{filter.label}:</label>
<Radio.Group
onChange={(e) => handleFilterChange({ target: { name: filter.id, value: e.target.value } })}
value={filters[filter.id] || ''}
>
<Radio.Button className="custom-radio-btn" value="">全部</Radio.Button>
{filter.options.map(option => (
<Radio.Button key={option.value} className="custom-radio-btn" value={option.value}>
{option.label}
</Radio.Button>
))}
</Radio.Group>
</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="loading-container">
<div className="spinner"></div>
</div>
) : (
<div className="torrents-container">
{torrents.length > 0 ? (
<div className="torrents-grid">
{currentTorrents.map(torrent => (
<div key={torrent.torrentid} className="torrent-card">
<div className="cover">
{torrent.coverImagePath ? (
<img
src={torrent.coverImagePath}
alt="封面"
className="cover-image"
/>
) : (
<div className="no-cover">无封面</div>
)}
</div>
<div className="info">
<h3 className="title" title={torrent.filename}>
{torrent.filename}
</h3>
<p className="description" title={torrent.description}>
{torrent.description || '暂无描述'}
</p>
<div className="details">
<span>大小: {formatFileSize(torrent.torrentSize)}</span>
<span>上传者: {usernames[torrent.uploader_id]}</span>
<span>上传时间: {new Date(torrent.uploadTime).toLocaleDateString()}</span>
<span>下载次数: {torrent.downloadCount}</span>
<span>促销: {getPromotionName(torrent.promotionid)}</span>
</div>
<div className="actions">
<button
onClick={() => handleDownload(torrent.torrentid)}
className="btn btn-download"
>
下载
</button>
<Link
to={`/torrent/${torrent.torrentid}`}
className="btn btn-detail"
>
详情
</Link>
</div>
{/* 新增举报按钮 */}
<button
className="report-btn"
onClick={() => showReportModal(torrent.torrentid)}
>
举报
</button>
{/* 添加举报模态框 */}
</div>
</div>
))}
</div>
) : (
<div className="no-data">没有找到符合条件的种子</div>
)}
</div>
)}
<Modal
title="举报内容"
open={isReportModalVisible}
onOk={handleReportSubmit}
onCancel={() => setIsReportModalVisible(false)}
okText="提交"
cancelText="取消"
>
<p>您正在举报种子ID: {currentTorrentId}</p>
<Input.TextArea
rows={4}
value={reportContent}
onChange={(e) => setReportContent(e.target.value)}
placeholder="请输入举报原因..."
/>
</Modal>
{/* // [新增] 分页组件 */}
{totalItems > 0 && (
<div className="pagination-container mt-6 flex justify-center">
<Pagination
current={currentPage}
pageSize={itemsPerPage}
total={totalItems}
onChange={(page) => setCurrentPage(page)} // [新增] 页码变化处理
showSizeChanger={false} // [可选] 是否显示每页条数选择器
showTotal={(total) => `共 ${total} 条记录`} // [可选] 显示总条数
/>
</div>
)}
</div>
);
}
export default TorrentList;