增加了悬赏,标签查看,评论页面,标签上传后端有问题,评论还没跟后端连,优化了一些小界面
Change-Id: I44f5ef2eb0a8ebd91a4b3b3b446f897bea41435f
diff --git a/react-ui/src/pages/Torrent/index.tsx b/react-ui/src/pages/Torrent/index.tsx
index bb058ae..30b7ef3 100644
--- a/react-ui/src/pages/Torrent/index.tsx
+++ b/react-ui/src/pages/Torrent/index.tsx
@@ -1,5 +1,5 @@
-import React, { useRef, useState } from 'react';
-import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, UploadOutlined } from '@ant-design/icons';
+import React, { useRef, useState, useEffect } from 'react';
+import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, UploadOutlined, DownloadOutlined, CommentOutlined } from '@ant-design/icons';
import {
Button,
Modal,
@@ -13,21 +13,29 @@
Layout,
Upload,
UploadProps,
+ Select,
+ Tag, // 导入Tag组件用于显示标签
} from 'antd';
import { ProTable, ActionType, ProColumns, ProDescriptions, ProDescriptionsItemProps } from '@ant-design/pro-components';
-import type { BtTorrent } from './data';
+import { useNavigate } from 'react-router-dom';
+import type { BtTorrent, BtTorrentTag } from './data';
import {
listBtTorrent,
getBtTorrent,
addBtTorrent,
updateBtTorrent,
removeBtTorrent,
- uploadTorrent, // Function to handle torrent upload
+ uploadTorrent,
+ downloadTorrent,
+ addBtTorrentTag,
+ listBtTorrentTags,
+ getBtTorrentTag
} from './service';
const { Content } = Layout;
const BtTorrentPage: React.FC = () => {
+ const navigate = useNavigate();
const actionRef = useRef<ActionType>();
const [form] = Form.useForm();
const [modalVisible, setModalVisible] = useState(false);
@@ -35,19 +43,38 @@
const [current, setCurrent] = useState<Partial<BtTorrent>>({});
const [uploadModalVisible, setUploadModalVisible] = useState(false); // State for upload modal
const [uploadFile, setUploadFile] = useState<File | null>(null); // State to store selected file
+ const [uploadForm] = Form.useForm(); // Form for upload modal
+ const [torrentTags, setTorrentTags] = useState<BtTorrentTag[]>([]); // 修改为数组类型来存储多个标签
// Columns for the ProTable (the table displaying torrents)
const columns: ProColumns<BtTorrent>[] = [
{
title: '种子ID',
dataIndex: 'torrentId',
- hideInForm: true,
render: (dom, entity) => (
<a
onClick={async () => {
- const res = await getBtTorrent(entity.torrentId!);
- setCurrent(res);
- setDrawerVisible(true);
+ try {
+ // 获取详细信息
+ const res = await getBtTorrent(entity.torrentId!);
+ console.log('获取的种子详情:', res); // 调试用
+
+ // 确保res是对象类型并且包含数据
+ if (res && typeof res === 'object') {
+ // 如果API返回了data包装,则提取data
+ const torrentData = res.data ? res.data : res;
+ setCurrent(torrentData);
+
+ // 先设置当前种子,然后获取标签
+ await handleGetTags(entity.torrentId!);
+ setDrawerVisible(true);
+ } else {
+ message.error('获取种子详情格式错误');
+ }
+ } catch (error) {
+ console.error('获取种子详情出错:', error);
+ message.error('获取种子详情失败');
+ }
}}
>
{dom}
@@ -55,36 +82,56 @@
),
},
{ title: '名称', dataIndex: 'name' },
- { title: 'infoHash', dataIndex: 'infoHash' },
+ // { title: 'infoHash', dataIndex: 'infoHash' },
{ title: '大小 (bytes)', dataIndex: 'length', valueType: 'digit' },
- { title: '分片大小', dataIndex: 'pieceLength', valueType: 'digit' },
- { title: '片段数', dataIndex: 'piecesCount', valueType: 'digit' },
- { title: '创建工具', dataIndex: 'createdBy', hideInSearch: true },
+ // { title: '分片大小', dataIndex: 'pieceLength', valueType: 'digit' },
+ // { title: '片段数', dataIndex: 'piecesCount', valueType: 'digit' },
+ { title: '创建人', dataIndex: 'createdBy', hideInSearch: true },
{ title: '上传时间', dataIndex: 'uploadTime', valueType: 'dateTime', hideInSearch: true },
{
title: '操作',
valueType: 'option',
render: (_, record) => [
- <Button key="view" type="link" icon={<EyeOutlined />} onClick={() => {
- setCurrent(record);
- setDrawerVisible(true);
+ <Button key="view" type="link" icon={<EyeOutlined />} onClick={async () => {
+ try {
+ // 获取详细信息
+ const res = await getBtTorrent(record.torrentId!);
+ console.log('获取的种子详情:', res); // 调试用
+
+ // 确保res是对象类型并且包含数据
+ if (res && typeof res === 'object') {
+ // 如果API返回了data包装,则提取data
+ const torrentData = res.data ? res.data : res;
+ setCurrent(torrentData);
+
+ // 获取标签
+ await handleGetTags(record.torrentId!);
+ setDrawerVisible(true);
+ } else {
+ message.error('获取种子详情格式错误');
+ }
+ } catch (error) {
+ console.error('获取种子详情出错:', error);
+ message.error('获取详情失败');
+ }
}}>查看</Button>,
- <Button key="edit" type="link" icon={<EditOutlined />} onClick={() => {
- setCurrent(record);
- form.setFieldsValue(record);
- setModalVisible(true);
- }}>编辑</Button>,
- <Button key="delete" type="link" icon={<DeleteOutlined />} danger onClick={() => {
- Modal.confirm({
- title: '删除确认',
- content: '确定删除该种子?',
- onOk: async () => {
- await removeBtTorrent([record.torrentId!]);
- message.success('删除成功');
- actionRef.current?.reload();
- },
- });
- }}>删除</Button>,
+ <Button key="download" type="link" icon={<DownloadOutlined />} onClick={async () => {
+ try {
+ const blob = await downloadTorrent(record.torrentId!);
+ const url = window.URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `${record.name}.torrent`;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+ message.success('下载成功');
+ } catch (error: any) {
+ message.error('下载失败');
+ }
+ }}>下载</Button>,
+
],
},
];
@@ -115,16 +162,19 @@
throw new Error('请选择一个文件');
}
- // Call the uploadTorrent function to upload the file
+ const values = await uploadForm.validateFields();
+ console.log(file);
+ // Call the uploadTorrent function to upload the file with additional info
await uploadTorrent(file);
// Show a success message
message.success('文件上传成功');
- // Close the upload modal
+ // Close the upload modal and reset form
setUploadModalVisible(false);
+ uploadForm.resetFields();
- // Optionally reload the table or perform other actions (e.g., refresh list)
+ // Reload the table
actionRef.current?.reload();
} catch (error) {
@@ -132,6 +182,64 @@
}
};
+ // 修改获取标签的函数,处理API特定的响应格式
+ const handleGetTags = async (id: number) => {
+ try {
+ // 根据API的响应格式,获取rows数组中的标签
+ const response = await listBtTorrentTags({ torrentId: id });
+ console.log('API标签响应:', response);
+
+ // 检查响应格式并提取rows数组
+ if (response && response.rows && Array.isArray(response.rows)) {
+ setTorrentTags(response.rows);
+ console.log('设置标签:', response.rows);
+ } else {
+ console.log('未找到标签或格式不符');
+ setTorrentTags([]);
+ }
+ } catch (error) {
+ console.error('获取标签失败:', error);
+ message.error('获取标签失败');
+ setTorrentTags([]);
+ }
+ };
+
+ useEffect(() => {
+ if (current?.torrentId) {
+ handleGetTags(current.torrentId);
+ } else {
+ setTorrentTags([]); // 清空标签当没有选中种子时
+ }
+ }, [current]);
+
+ // 渲染标签列表的函数
+ const renderTags = () => {
+ if (!torrentTags || torrentTags.length === 0) {
+ return <span style={{ color: '#999' }}>暂无标签</span>;
+ }
+
+ // 定义一些可能的标签颜色
+ const tagColors = ['blue', 'green', 'cyan', 'purple', 'magenta', 'orange', 'gold', 'lime'];
+
+ return (
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
+ {torrentTags.map((tag, index) => {
+ // 根据索引轮换颜色
+ const colorIndex = index % tagColors.length;
+ return (
+ <Tag
+ key={tag.id || tag.torrentId || index}
+ color={tagColors[colorIndex]}
+ style={{ margin: '0 4px 4px 0', padding: '2px 8px' }}
+ >
+ {tag.tag}
+ </Tag>
+ );
+ })}
+ </div>
+ );
+ };
+
return (
<Content>
<Card bordered={false}>
@@ -142,22 +250,10 @@
search={{ labelWidth: 100 }}
toolBarRender={() => [
<Button
- key="add"
- type="primary"
- icon={<PlusOutlined />}
- onClick={() => {
- form.resetFields();
- setCurrent({});
- setModalVisible(true);
- }}
- >
- 新增
- </Button>,
- <Button
key="upload"
type="primary"
icon={<UploadOutlined />}
- onClick={() => setUploadModalVisible(true)} // Show the upload modal
+ onClick={() => setUploadModalVisible(true)}
>
上传种子文件
</Button>
@@ -202,37 +298,155 @@
{/* 上传种子文件的Modal */}
<Modal
title="上传种子文件"
- visible={uploadModalVisible}
- onCancel={() => setUploadModalVisible(false)}
- footer={null}
+ open={uploadModalVisible}
+ onCancel={() => {
+ setUploadModalVisible(false);
+ uploadForm.resetFields();
+ }}
+ onOk={() => {
+ if (uploadFile) {
+ handleFileUpload(uploadFile);
+ } else {
+ message.error('请选择文件');
+ }
+ }}
>
- <Upload
- customRequest={({ file, onSuccess, onError }) => {
- setUploadFile(file);
- handleFileUpload(file);
- onSuccess?.();
- }}
- showUploadList={false}
- accept=".torrent"
- >
- <Button icon={<UploadOutlined />}>点击上传 .torrent 文件</Button>
- </Upload>
+ <Form form={uploadForm} layout="vertical">
+ <Form.Item
+ name="file"
+ label="种子文件"
+ rules={[{ required: true, message: '请选择种子文件' }]}
+ >
+ <Upload
+ customRequest={({ file, onSuccess }) => {
+ setUploadFile(file as File);
+ onSuccess?.();
+ }}
+ showUploadList={true}
+ maxCount={1}
+ accept=".torrent"
+ onRemove={() => setUploadFile(null)}
+ >
+ <Button icon={<UploadOutlined />}>选择 .torrent 文件</Button>
+ </Upload>
+ </Form.Item>
+ <Form.Item
+ name="description"
+ label="介绍"
+ rules={[{ required: true, message: '请输入种子介绍' }]}
+ >
+ <Input.TextArea rows={4} placeholder="请输入种子文件的详细介绍" />
+ </Form.Item>
+ <Form.Item
+ name="tags"
+ label="标签"
+ rules={[{ required: true, message: '请输入标签' }]}
+ >
+ <Select
+ mode="tags"
+ style={{ width: '100%' }}
+ placeholder="请输入标签,按回车键确认"
+ tokenSeparators={[',']}
+ />
+ </Form.Item>
+ </Form>
</Modal>
{/* 详情抽屉 */}
<Drawer
- width={500}
+ width={600}
open={drawerVisible}
onClose={() => setDrawerVisible(false)}
title="种子详情"
+ extra={
+ <Button
+ type="primary"
+ icon={<CommentOutlined />}
+ onClick={() => navigate(`/torrent/comments/${current.torrentId}`)}
+ >
+ 查看评论
+ </Button>
+ }
>
{current && (
- <ProDescriptions<BtTorrent>
- column={1}
- title={current.name}
- request={async () => ({ data: current })}
- columns={columns as ProDescriptionsItemProps<BtTorrent>[]}
- />
+ <>
+ {/* 不要使用request属性,直接使用dataSource */}
+ <ProDescriptions<BtTorrent>
+ column={1}
+ title={current.name}
+ dataSource={current}
+ columns={[
+ {
+ title: '种子ID',
+ dataIndex: 'torrentId',
+ valueType: 'text'
+ },
+ {
+ title: '名称',
+ dataIndex: 'name',
+ valueType: 'text'
+ },
+ {
+ title: 'infoHash',
+ dataIndex: 'infoHash',
+ valueType: 'text',
+ copyable: true
+ },
+ {
+ title: '大小 (bytes)',
+ dataIndex: 'length',
+ valueType: 'digit',
+
+ },
+ {
+ title: '分片大小',
+ dataIndex: 'pieceLength',
+ valueType: 'digit'
+ },
+ {
+ title: '片段数',
+ dataIndex: 'piecesCount',
+ valueType: 'digit'
+ },
+ {
+ title: '创建人',
+ dataIndex: 'createdBy',
+ valueType: 'text'
+ },
+ {
+ title: '上传时间',
+ dataIndex: 'uploadTime',
+ valueType: 'dateTime'
+ },
+ {
+ title: '标签',
+ dataIndex: 'tags',
+ render: () => renderTags()
+ }
+ ] as ProDescriptionsItemProps<BtTorrent>[]}
+ />
+
+ {/* 如果需要显示额外信息,可以在这里添加 */}
+ {current.description && (
+ <div style={{ marginTop: 16 }}>
+ <h3>介绍</h3>
+ <p>{current.description}</p>
+ </div>
+ )}
+
+ {/* 添加调试信息 - 开发时使用,生产环境可以移除 */}
+ {/* <div style={{ marginTop: 20, background: '#f5f5f5', padding: 10, borderRadius: 4 }}>
+ <h4>调试信息:</h4>
+ <p>当前种子ID: {current.torrentId}</p>
+ <p>标签数量: {torrentTags?.length || 0}</p>
+ <details>
+ <summary>查看完整数据</summary>
+ <pre style={{ maxHeight: 200, overflow: 'auto' }}>{JSON.stringify(current, null, 2)}</pre>
+ <h5>标签数据:</h5>
+ <pre style={{ maxHeight: 200, overflow: 'auto' }}>{JSON.stringify(torrentTags, null, 2)}</pre>
+ </details>
+ </div> */}
+ </>
)}
</Drawer>
</Card>
@@ -240,4 +454,4 @@
);
};
-export default BtTorrentPage;
+export default BtTorrentPage;
\ No newline at end of file