reset the rebase code
Change-Id: I50bb95e75cd5679667fb6e4053e0d1e60e28f6a5
diff --git a/src/pages/Torrent/torrentUpload.tsx b/src/pages/Torrent/torrentUpload.tsx
new file mode 100644
index 0000000..6cb5a41
--- /dev/null
+++ b/src/pages/Torrent/torrentUpload.tsx
@@ -0,0 +1,379 @@
+import React, { useEffect, useState } from 'react';
+import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
+import {
+Form,
+Input,
+Button,
+Select,
+Upload,
+message,
+Tag,
+Space,
+Typography,
+Modal,
+} from 'antd';
+import{
+ getCategories,
+ addTorrent,
+ uploadTorrentFile
+} from '../../services/bt/index';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title } = Typography;
+
+const TorrentUpload: React.FC = () => {
+const [categories, setCategories] = useState<{ id: number; name: string }[]>([]);
+const [tagOptions, setTagOptions] = useState<string[]>([]);
+const [customTags, setCustomTags] = useState<string[]>([]);
+const [fileList, setFileList] = useState<any[]>([]);
+const [uploading, setUploading] = useState(false);
+const [form] = Form.useForm();
+const [tagInputVisible, setTagInputVisible] = useState(false);
+const [tagInputValue, setTagInputValue] = useState('');
+
+useEffect(() => {
+ getCategories().then((res) => {
+ if (Array.isArray(res.data)) {
+ setCategories(res.data);
+ setTagOptions(res.data.map((cat: { name: string }) => cat.name));
+ }
+ });
+}, []);
+
+const handleTagClose = (removedTag: string) => {
+ setCustomTags(customTags.filter(tag => tag !== removedTag));
+};
+
+const showTagInput = () => setTagInputVisible(true);
+
+const handleTagInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ setTagInputValue(e.target.value);
+};
+
+const handleTagInputConfirm = () => {
+ if (
+ tagInputValue &&
+ !customTags.includes(tagInputValue) &&
+ !tagOptions.includes(tagInputValue)
+ ) {
+ setCustomTags([...customTags, tagInputValue]);
+ }
+ setTagInputVisible(false);
+ setTagInputValue('');
+};
+
+const beforeUpload = (file: File) => {
+ const isTorrent = file.name.endsWith('.torrent');
+ if (!isTorrent) {
+ message.error('只能上传.torrent文件');
+ }
+ setFileList([file]);
+ return false;
+};
+
+const handleSubmit = async (values: any) => {
+ if (fileList.length === 0) {
+ message.error('请上传.torrent文件');
+ return;
+ }
+ setUploading(true);
+ try {
+ // 1. 添加种子基本信息
+ const addRes = await addTorrent({
+ ...values,
+ category: Number(values.category),
+ description: values.description,
+ name: values.name,
+ title: values.title,
+ subheading: values.subheading || '',
+ remark: values.remark || '',
+ });
+ if (!addRes?.id) {
+ throw new Error('种子信息添加失败');
+ }
+ // 2. 上传.torrent文件
+ await uploadTorrentFile(fileList[0], addRes.id);
+
+ message.success('种子上传成功');
+ form.resetFields();
+ setFileList([]);
+ setCustomTags([]);
+ } catch (err: any) {
+ message.error(err.message || '上传失败');
+ } finally {
+ setUploading(false);
+ }
+};
+
+return (
+ <div
+ style={{
+ width: '100%',
+ margin: '64px auto',
+ padding: '56px 64px 48px 64px',
+ background: 'linear-gradient(135deg, #232526 0%, #414345 100%)',
+ borderRadius: 24,
+ boxShadow: '0 16px 48px 0 rgba(31, 38, 135, 0.18)',
+ color: '#fff',
+ position: 'relative',
+ overflow: 'hidden',
+ }}
+ >
+ <div
+ style={{
+ position: 'absolute',
+ top: -100,
+ right: -100,
+ width: 260,
+ height: 260,
+ background: 'radial-gradient(circle, #667eea55 0%, transparent 80%)',
+ zIndex: 0,
+ }}
+ />
+ <div
+ style={{
+ position: 'absolute',
+ bottom: -80,
+ left: -80,
+ width: 200,
+ height: 200,
+ background: 'radial-gradient(circle, #764ba255 0%, transparent 80%)',
+ zIndex: 0,
+ }}
+ />
+ <Title
+ level={2}
+ style={{
+ color: '#fff',
+ textAlign: 'center',
+ marginBottom: 48,
+ letterSpacing: 2,
+ fontWeight: 700,
+ zIndex: 1,
+ position: 'relative',
+ fontSize: 32,
+ }}
+ >
+ 星空PT - 上传资源
+ </Title>
+ <Form
+ form={form}
+ layout="horizontal"
+ labelCol={{ span: 5 }}
+ wrapperCol={{ span: 16 }}
+ onFinish={handleSubmit}
+ initialValues={{ anonymous: 0 }}
+ style={{ zIndex: 1, position: 'relative' }}
+ >
+ <Form.Item
+ label={<span style={{ fontWeight: 500 }}>主标题</span>}
+ name="title"
+ rules={[{ required: true, message: '请输入主标题' }]}
+ >
+ <Input
+ placeholder="请输入主标题"
+ size="large"
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ }}
+ />
+ </Form.Item>
+ <Form.Item label={<span style={{ fontWeight: 500 }}>副标题</span>} name="subheading">
+ <Input
+ placeholder="可选,副标题"
+ size="large"
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ }}
+ />
+ </Form.Item>
+ <Form.Item
+ label={<span style={{ fontWeight: 500 }}>种子名称</span>}
+ name="name"
+ rules={[{ required: true, message: '请输入种子名称' }]}
+ >
+ <Input
+ placeholder="请输入种子名称"
+ size="large"
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ }}
+ />
+ </Form.Item>
+ <Form.Item
+ label={<span style={{ fontWeight: 500 }}>分类</span>}
+ name="category"
+ rules={[{ required: true, message: '请选择分类' }]}
+ >
+ <Select
+ placeholder="请选择分类"
+ size="large"
+ dropdownStyle={{ background: '#232526', color: '#fff' }}
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ }}
+ >
+ {categories.map((cat) => (
+ <Option key={cat.id} value={cat.id}>
+ {cat.name}
+ </Option>
+ ))}
+ </Select>
+ </Form.Item>
+ <Form.Item label={<span style={{ fontWeight: 500 }}>标签</span>}>
+ <Space wrap>
+ {customTags.map((tag) => (
+ <Tag
+ key={tag}
+ closable
+ color="geekblue"
+ onClose={() => handleTagClose(tag)}
+ style={{
+ marginBottom: 4,
+ fontSize: 15,
+ padding: '4px 12px',
+ borderRadius: 8,
+ }}
+ >
+ {tag}
+ </Tag>
+ ))}
+ {tagInputVisible ? (
+ <Input
+ size="small"
+ style={{
+ width: 120,
+ background: 'rgba(255,255,255,0.08)',
+ color: '#fff',
+ border: '1px solid #333',
+ }}
+ value={tagInputValue}
+ onChange={handleTagInputChange}
+ onBlur={handleTagInputConfirm}
+ onPressEnter={handleTagInputConfirm}
+ autoFocus
+ />
+ ) : (
+ <Tag
+ onClick={showTagInput}
+ style={{
+ background: 'rgba(255,255,255,0.08)',
+ border: '1px dashed #1890ff',
+ cursor: 'pointer',
+ color: '#1890ff',
+ fontSize: 15,
+ borderRadius: 8,
+ padding: '4px 12px',
+ }}
+ >
+ <PlusOutlined /> 自定义标签
+ </Tag>
+ )}
+ </Space>
+ </Form.Item>
+ <Form.Item
+ label={<span style={{ fontWeight: 500 }}>描述</span>}
+ name="description"
+ rules={[{ required: true, message: '请输入描述' }]}
+ >
+ <TextArea
+ rows={6}
+ placeholder="请输入种子描述,支持Markdown"
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ fontSize: 15,
+ }}
+ />
+ </Form.Item>
+ <Form.Item label={<span style={{ fontWeight: 500 }}>备注</span>} name="remark">
+ <Input
+ placeholder="可选,备注信息"
+ size="large"
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ }}
+ />
+ </Form.Item>
+ <Form.Item
+ label={<span style={{ fontWeight: 500 }}>匿名上传</span>}
+ name="anonymous"
+ valuePropName="checked"
+ >
+ <Select
+ size="large"
+ style={{
+ background: 'rgba(255,255,255,0.06)',
+ border: '1px solid #333',
+ color: '#fff',
+ }}
+ >
+ <Option value={0}>否</Option>
+ <Option value={1}>是</Option>
+ </Select>
+ </Form.Item>
+ <Form.Item
+ label={<span style={{ fontWeight: 500 }}>上传.torrent文件</span>}
+ required
+ >
+ <Upload
+ beforeUpload={beforeUpload}
+ fileList={fileList}
+ onRemove={() => setFileList([])}
+ accept=".torrent"
+ maxCount={1}
+ showUploadList={{ showRemoveIcon: true }}
+ customRequest={() => {}}
+ >
+ <Button
+ icon={<UploadOutlined />}
+ size="large"
+ style={{
+ background: 'linear-gradient(90deg, #667eea 0%, #764ba2 100%)',
+ border: 'none',
+ color: '#fff',
+ fontWeight: 500,
+ }}
+ >
+ 选择.torrent文件
+ </Button>
+ </Upload>
+ </Form.Item>
+ <Form.Item wrapperCol={{ span: 16, offset: 5 }}>
+ <Button
+ type="primary"
+ htmlType="submit"
+ loading={uploading}
+ block
+ size="large"
+ style={{
+ background: 'linear-gradient(90deg, #667eea 0%, #764ba2 100%)',
+ border: 'none',
+ fontWeight: 600,
+ letterSpacing: 2,
+ fontSize: 18,
+ marginTop: 8,
+ boxShadow: '0 4px 16px 0 rgba(118,75,162,0.15)',
+ }}
+ >
+ 上传
+ </Button>
+ </Form.Item>
+ </Form>
+ </div>
+);
+};
+
+export default TorrentUpload;
\ No newline at end of file