blob: e90af3ce96750c850639b7e174400488e2b2db41 [file] [log] [blame]
Jiarenxiang24d681b2025-06-08 19:27:05 +08001import React, { useEffect, useState } from 'react';
Jiarenxiang2e3c2672025-06-08 20:46:35 +08002import { useParams,useSearchParams, useNavigate } from 'react-router-dom';
Jiarenxiang24d681b2025-06-08 19:27:05 +08003import { Card, Button, Spin, Tag, message, Input } from 'antd';
4import { ArrowLeftOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons';
Jiarenxiang2e3c2672025-06-08 20:46:35 +08005import { getTorrentInfo,auditTorrent,downloadTorrent } from '../../services/bt/index';
6
Jiarenxiang24d681b2025-06-08 19:27:05 +08007
8// 状态映射
9const statusMap: Record<number, string> = {
10 0: '审核中',
11 1: '已发布',
12 2: '审核不通过',
13 3: '已上架修改重审中',
14 10: '已下架',
15};
16
17const TorrentAudit: React.FC = () => {
Jiarenxiang2e3c2672025-06-08 20:46:35 +080018 const { id } = useParams<{ id: string }>();
Jiarenxiang24d681b2025-06-08 19:27:05 +080019 const navigate = useNavigate();
20
21 const [loading, setLoading] = useState(true);
22 const [torrent, setTorrent] = useState<any>(null);
23 const [auditLoading, setAuditLoading] = useState(false);
24 const [rejectReason, setRejectReason] = useState('');
25
26 useEffect(() => {
27 if (!id) {
28 message.error('未指定种子ID');
29 navigate(-1);
30 return;
31 }
32 setLoading(true);
33 getTorrentInfo({ id })
34 .then(res => setTorrent(res.data))
35 .finally(() => setLoading(false));
36 }, [id, navigate]);
Jiarenxiang2e3c2672025-06-08 20:46:35 +080037const handleDownload = async () => {
38 try {
39 const res = await downloadTorrent({ id: id! });
40 const blob = new Blob([res], { type: 'application/x-bittorrent' });
41 const url = window.URL.createObjectURL(blob);
42 const a = document.createElement('a');
43 a.href = url;
44 a.download = `${torrent?.title || 'torrent'}.torrent`;
45 a.click();
46 window.URL.revokeObjectURL(url);
47 } catch {
48 message.error('下载失败');
49 }
50};
Jiarenxiang24d681b2025-06-08 19:27:05 +080051 const handleAudit = async (status: 1 | 2) => {
52 if (status === 2 && !rejectReason.trim()) {
53 message.warning('请填写拒绝理由');
54 return;
55 }
56 setAuditLoading(true);
57 try {
58 await auditTorrent({
59 id: Number(id),
60 status,
61 remark: status === 2 ? rejectReason.trim() : undefined,
62 });
63 message.success(status === 1 ? '审核通过' : '审核拒绝');
64 navigate(-1);
65 } catch {
66 message.error('操作失败');
67 } finally {
68 setAuditLoading(false);
69 }
70 };
71
72 return (
73 <div style={{ minHeight: '100vh', background: '#f4f8ff', padding: 32 }}>
74 <div style={{ maxWidth: 800, margin: '0 auto' }}>
75 <Button
76 icon={<ArrowLeftOutlined />}
77 type="link"
78 onClick={() => navigate(-1)}
79 style={{ marginBottom: 16 }}
80 >
81 返回
82 </Button>
83 <Card
84 loading={loading}
85 title={
86 <span>
87 审核种子
88 {torrent && (
89 <Tag color={torrent.status === 0 ? 'orange' : torrent.status === 1 ? 'green' : 'red'} style={{ marginLeft: 16 }}>
90 {statusMap[torrent.status]}
91 </Tag>
92 )}
93 </span>
94 }
95 >
96 {loading ? (
97 <Spin />
98 ) : !torrent ? (
99 <div>未找到种子信息</div>
100 ) : (
101 <>
102 <h2>{torrent.title}</h2>
103 <div style={{ marginBottom: 8 }}>
104 <Tag color="blue">{torrent.categoryName || '未知分类'}</Tag>
105 {torrent.tags?.map((tag: string) => (
106 <Tag key={tag}>{tag}</Tag>
107 ))}
108 </div>
109 <div style={{ color: '#888', marginBottom: 8 }}>
110 上传者:{torrent.owner} | 上传时间:{torrent.createdAt}
111 </div>
112 <div style={{ marginBottom: 16 }}>
113 <span>文件大小:{torrent.size}</span>
114 <span style={{ marginLeft: 24 }}>做种人数:{torrent.seeders}</span>
115 <span style={{ marginLeft: 24 }}>下载人数:{torrent.leechers}</span>
116 <span style={{ marginLeft: 24 }}>完成次数:{torrent.completed}</span>
117 </div>
Jiarenxiang2e3c2672025-06-08 20:46:35 +0800118 <div style={{ marginBottom: 16 }}>
119 <Button
120 type="primary"
121 icon={<CheckOutlined />}
122 onClick={() => downloadTorrent(torrent.id)}
123 disabled={!torrent.infoHash}
124 >
125 下载种子
126 </Button>
127 </div>
Jiarenxiang24d681b2025-06-08 19:27:05 +0800128 <div
129 style={{
130 background: '#f6f8fa',
131 padding: 16,
132 borderRadius: 8,
133 marginBottom: 24,
134 minHeight: 80,
135 }}
136 dangerouslySetInnerHTML={{ __html: torrent.description || '' }}
137 />
138 {torrent.status === 0 ? (
139 <>
140 <div style={{ display: 'flex', gap: 16, alignItems: 'center', marginBottom: 16 }}>
141 <Button
142 type="primary"
143 icon={<CheckOutlined />}
144 loading={auditLoading}
145 onClick={() => handleAudit(1)}
146 >
147 审核通过
148 </Button>
149 <Button
150 danger
151 icon={<CloseOutlined />}
152 loading={auditLoading}
153 onClick={() => handleAudit(2)}
154 >
155 审核拒绝
156 </Button>
157 <Input.TextArea
158 value={rejectReason}
159 onChange={e => setRejectReason(e.target.value)}
160 placeholder="拒绝理由(仅拒绝时填写)"
161 autoSize={{ minRows: 1, maxRows: 3 }}
162 style={{ width: 260 }}
163 disabled={auditLoading}
164 />
165 </div>
166 </>
167 ) : (
168 <div style={{ color: '#888', marginTop: 16 }}>
169 当前状态:{statusMap[torrent.status]}
170 {torrent.status === 2 && torrent.reason && (
171 <div style={{ color: '#d4380d', marginTop: 8 }}>拒绝理由:{torrent.reason}</div>
172 )}
173 </div>
174 )}
175 </>
176 )}
177 </Card>
178 </div>
179 </div>
180 );
181};
182
183export default TorrentAudit;