查看种子详细信息用户端和管理员端界面
Change-Id: I29e761d67a1eab741a91feb3f4c686055bb1b382
diff --git a/src/components/Torrentdetail.jsx b/src/components/Torrentdetail.jsx
new file mode 100644
index 0000000..a955b7b
--- /dev/null
+++ b/src/components/Torrentdetail.jsx
@@ -0,0 +1,206 @@
+import React, { useEffect, useState } from 'react';
+import { useParams, useNavigate } from 'react-router-dom'; // 添加 useNavigate
+import axios from 'axios';
+import {
+ Row,
+ Col,
+ Card,
+ Descriptions,
+ Table,
+ Typography,
+ Spin,
+ Tag,
+ Button, // 添加 Button 组件
+} from 'antd';
+import { Image as AntdImage } from 'antd';
+import '../TorrentDetail.css'; // 引入样式
+
+const { Title, Text } = Typography;
+
+const TorrentDetail = () => {
+ const { id } = useParams();
+ const navigate = useNavigate(); // 获取导航函数
+ const [torrent, setTorrent] = useState(null);
+ const [seeders, setSeeders] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [torrentLoading, setTorrentLoading] = useState(true);
+ const [seedersLoading, setSeedersLoading] = useState(false);
+
+ useEffect(() => {
+ const fetchTorrentDetails = async () => {
+ try {
+ setTorrentLoading(true);
+ const torrentRes = await axios.get(`http://localhost:8080/torrent/${id}`);
+ setTorrent(torrentRes.data);
+
+ setSeedersLoading(true);
+ const seedersRes = await axios.get(`http://localhost:8080/torrent/${torrentRes.data.infoHash}/seeders`);
+ setSeeders(seedersRes.data);
+ } catch (err) {
+ console.error('获取数据失败', err);
+ } finally {
+ setTorrentLoading(false);
+ setSeedersLoading(false);
+ setLoading(false);
+ }
+ };
+
+ fetchTorrentDetails();
+ }, [id]);
+
+ const formatSize = (bytes) => {
+ if (bytes === 0) return '0 B';
+ const k = 1024;
+ const sizes = ['B', '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 formatSpeed = (bytesPerSec) => {
+ if (bytesPerSec < 1024) return bytesPerSec.toFixed(2) + ' B/s';
+ if (bytesPerSec < 1024 * 1024) return (bytesPerSec / 1024).toFixed(2) + ' KB/s';
+ return (bytesPerSec / (1024 * 1024)).toFixed(2) + ' MB/s';
+ };
+
+ const columns = [
+ {
+ title: '用户名',
+ dataIndex: 'username',
+ key: 'username',
+ align: 'center',
+ render: (text) => <Tag color="orange">{text}</Tag>,
+ },
+ {
+ title: '已上传',
+ dataIndex: 'uploaded',
+ key: 'uploaded',
+ align: 'center',
+ render: (text) => <Text>{formatSize(text)}</Text>,
+ },
+ {
+ title: '上传速度',
+ dataIndex: 'uploadSpeed',
+ key: 'uploadSpeed',
+ align: 'center',
+ render: (text) => <Text>{formatSpeed(text)}</Text>,
+ },
+ {
+ title: '已下载',
+ dataIndex: 'downloaded',
+ key: 'downloaded',
+ align: 'center',
+ render: (text) => <Text>{formatSize(text)}</Text>,
+ },
+ {
+ title: '下载速度',
+ dataIndex: 'downloadSpeed',
+ key: 'downloadSpeed',
+ align: 'center',
+ render: (text) => text > 0 ? <Text>{formatSpeed(text)}</Text> : <Text>-</Text>,
+ },
+ {
+ title: '客户端',
+ dataIndex: 'client',
+ key: 'client',
+ align: 'center',
+ },
+ {
+ title: '最后活动',
+ dataIndex: 'lastEvent',
+ key: 'lastEvent',
+ align: 'center',
+ },
+ ];
+
+ if (loading) return <div className="page-wrapper"><Spin size="large" /></div>;
+
+ return (
+ <div className="page-wrapper">
+ {/* 添加返回按钮 */}
+ <div className="mb-4">
+ <Button
+ type="primary"
+ onClick={() => navigate(-1)} // 返回上一页
+ style={{ marginBottom: '16px' }}
+ >
+ 返回列表
+ </Button>
+ </div>
+
+ <Row gutter={[16, 16]}>
+ <Col xs={24} md={8}>
+ <Card bordered={false} className="custom-card h-full">
+ {torrent.coverImagePath ? (
+ <AntdImage
+ src={torrent.coverImagePath}
+ alt="Torrent Cover"
+ className="w-full h-64 object-cover rounded"
+ placeholder={
+ <div className="w-full h-64 flex items-center justify-center bg-gray-100">
+ <Spin size="small" />
+ </div>
+ }
+ preview={false}
+ />
+ ) : (
+ <div className="w-full h-64 flex items-center justify-center bg-gray-100 rounded">
+ <Text type="secondary">无封面图片</Text>
+ </div>
+ )}
+ </Card>
+ </Col>
+
+ <Col xs={24} md={16}>
+ <Card className="info-card">
+ <Title level={1} className="info-title">
+ {torrent?.torrentTitle || '加载中...'}
+ </Title>
+
+ <Descriptions
+ bordered
+ column={{ xs: 1, sm: 2 }}
+ size="middle"
+ className="custom-descriptions"
+ labelStyle={{ fontWeight: 'bold', color: '#a15c00', fontSize: '16px' }}
+ contentStyle={{ fontSize: '15px' }}
+ >
+ <Descriptions.Item label="简介">{torrent.description || '暂无简介'}</Descriptions.Item>
+ <Descriptions.Item label="上传人">{torrent.uploader_id || '未知用户'}</Descriptions.Item>
+ <Descriptions.Item label="上传时间">{new Date(torrent.uploadTime).toLocaleString()}</Descriptions.Item>
+ <Descriptions.Item label="文件大小"><Text>{formatSize(torrent.torrentSize)}</Text></Descriptions.Item>
+ <Descriptions.Item label="下载数"><Text>{torrent.downloadCount || 0}</Text></Descriptions.Item>
+ <Descriptions.Item label="做种数"><Text>{seeders.length}</Text></Descriptions.Item>
+ <Descriptions.Item label="文件分辨率">{torrent.dpi || '未知'}</Descriptions.Item>
+ <Descriptions.Item label="文件字幕">{torrent.caption || '无'}</Descriptions.Item>
+ <Descriptions.Item label="最后做种时间">
+ {torrent.lastseed ? new Date(torrent.lastseed).toLocaleString() : '暂无'}
+ </Descriptions.Item>
+ </Descriptions>
+ </Card>
+ </Col>
+ </Row>
+
+ <Card className="custom-card mt-4">
+ <Title level={4} style={{ color: '#d46b08' }}>当前做种用户 ({seeders.length})</Title>
+ {seedersLoading ? (
+ <div className="p-4 text-center"><Spin size="small" /></div>
+ ) : seeders.length > 0 ? (
+ <Table
+ columns={columns}
+ dataSource={seeders}
+ rowKey={(record, index) => index}
+ pagination={false}
+ size="small"
+ className="custom-table"
+ />
+ ) : (
+ <div className="p-4 text-center text-gray-500 bg-gray-50 rounded">
+ 当前没有用户在做种
+ </div>
+ )}
+ </Card>
+ </div>
+ );
+};
+
+export default TorrentDetail;
\ No newline at end of file