blob: 3e6a45b38088c8207bb81a66aa927b8b0f78a91f [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';
Jiarenxiang56cbf662025-06-09 21:46:47 +08005import { getTorrentInfo,auditTorrent,downloadTorrent,getCategories } from '../../services/bt/index';
Jiarenxiang2e3c2672025-06-08 20:46:35 +08006
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();
Jiarenxiang56cbf662025-06-09 21:46:47 +080020 const [categories, setCategories] = useState<any[]>([]);
Jiarenxiang24d681b2025-06-08 19:27:05 +080021 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));
Jiarenxiang56cbf662025-06-09 21:46:47 +080036 getCategories().then((res) => setCategories(res.data || []));
Jiarenxiang24d681b2025-06-08 19:27:05 +080037 }, [id, navigate]);
Jiarenxiang2e3c2672025-06-08 20:46:35 +080038const handleDownload = async () => {
39 try {
40 const res = await downloadTorrent({ id: id! });
41 const blob = new Blob([res], { type: 'application/x-bittorrent' });
42 const url = window.URL.createObjectURL(blob);
43 const a = document.createElement('a');
44 a.href = url;
45 a.download = `${torrent?.title || 'torrent'}.torrent`;
46 a.click();
47 window.URL.revokeObjectURL(url);
48 } catch {
49 message.error('下载失败');
50 }
51};
Jiarenxiang24d681b2025-06-08 19:27:05 +080052 const handleAudit = async (status: 1 | 2) => {
53 if (status === 2 && !rejectReason.trim()) {
54 message.warning('请填写拒绝理由');
55 return;
56 }
57 setAuditLoading(true);
58 try {
59 await auditTorrent({
60 id: Number(id),
61 status,
62 remark: status === 2 ? rejectReason.trim() : undefined,
63 });
64 message.success(status === 1 ? '审核通过' : '审核拒绝');
65 navigate(-1);
66 } catch {
67 message.error('操作失败');
68 } finally {
69 setAuditLoading(false);
70 }
71 };
Jiarenxiang56cbf662025-06-09 21:46:47 +080072const getCategoryName = (catId: number) => {
73 console.log('categories', catId, categories);
74 return categories.find((c) => c.id === catId)?.name || '未知分类';
75};
Jiarenxiang24d681b2025-06-08 19:27:05 +080076 return (
77 <div style={{ minHeight: '100vh', background: '#f4f8ff', padding: 32 }}>
78 <div style={{ maxWidth: 800, margin: '0 auto' }}>
79 <Button
80 icon={<ArrowLeftOutlined />}
81 type="link"
82 onClick={() => navigate(-1)}
83 style={{ marginBottom: 16 }}
84 >
85 返回
86 </Button>
87 <Card
88 loading={loading}
89 title={
90 <span>
91 审核种子
92 {torrent && (
93 <Tag color={torrent.status === 0 ? 'orange' : torrent.status === 1 ? 'green' : 'red'} style={{ marginLeft: 16 }}>
94 {statusMap[torrent.status]}
95 </Tag>
96 )}
97 </span>
98 }
99 >
100 {loading ? (
101 <Spin />
102 ) : !torrent ? (
103 <div>未找到种子信息</div>
104 ) : (
105 <>
106 <h2>{torrent.title}</h2>
107 <div style={{ marginBottom: 8 }}>
Jiarenxiang56cbf662025-06-09 21:46:47 +0800108 <Tag color="blue">{getCategoryName(torrent.category) || '未知分类'}</Tag>
Jiarenxiang24d681b2025-06-08 19:27:05 +0800109 {torrent.tags?.map((tag: string) => (
110 <Tag key={tag}>{tag}</Tag>
111 ))}
112 </div>
113 <div style={{ color: '#888', marginBottom: 8 }}>
114 上传者:{torrent.owner} | 上传时间:{torrent.createdAt}
115 </div>
116 <div style={{ marginBottom: 16 }}>
117 <span>文件大小:{torrent.size}</span>
118 <span style={{ marginLeft: 24 }}>做种人数:{torrent.seeders}</span>
119 <span style={{ marginLeft: 24 }}>下载人数:{torrent.leechers}</span>
120 <span style={{ marginLeft: 24 }}>完成次数:{torrent.completed}</span>
121 </div>
Jiarenxiang2e3c2672025-06-08 20:46:35 +0800122 <div style={{ marginBottom: 16 }}>
123 <Button
124 type="primary"
125 icon={<CheckOutlined />}
126 onClick={() => downloadTorrent(torrent.id)}
127 disabled={!torrent.infoHash}
128 >
129 下载种子
130 </Button>
131 </div>
Jiarenxiang24d681b2025-06-08 19:27:05 +0800132 <div
133 style={{
134 background: '#f6f8fa',
135 padding: 16,
136 borderRadius: 8,
137 marginBottom: 24,
138 minHeight: 80,
Jiarenxiang56cbf662025-06-09 21:46:47 +0800139 color: '#333',
Jiarenxiang24d681b2025-06-08 19:27:05 +0800140 }}
141 dangerouslySetInnerHTML={{ __html: torrent.description || '' }}
142 />
143 {torrent.status === 0 ? (
144 <>
145 <div style={{ display: 'flex', gap: 16, alignItems: 'center', marginBottom: 16 }}>
146 <Button
147 type="primary"
148 icon={<CheckOutlined />}
149 loading={auditLoading}
150 onClick={() => handleAudit(1)}
151 >
152 审核通过
153 </Button>
154 <Button
155 danger
156 icon={<CloseOutlined />}
157 loading={auditLoading}
158 onClick={() => handleAudit(2)}
159 >
160 审核拒绝
161 </Button>
162 <Input.TextArea
163 value={rejectReason}
164 onChange={e => setRejectReason(e.target.value)}
165 placeholder="拒绝理由(仅拒绝时填写)"
166 autoSize={{ minRows: 1, maxRows: 3 }}
167 style={{ width: 260 }}
168 disabled={auditLoading}
169 />
170 </div>
171 </>
172 ) : (
173 <div style={{ color: '#888', marginTop: 16 }}>
174 当前状态:{statusMap[torrent.status]}
175 {torrent.status === 2 && torrent.reason && (
176 <div style={{ color: '#d4380d', marginTop: 8 }}>拒绝理由:{torrent.reason}</div>
177 )}
178 </div>
179 )}
180 </>
181 )}
182 </Card>
183 </div>
184 </div>
185 );
186};
187
188export default TorrentAudit;