reset the rebase code

Change-Id: I50bb95e75cd5679667fb6e4053e0d1e60e28f6a5
diff --git a/src/pages/Torrent/torrentDetail.tsx b/src/pages/Torrent/torrentDetail.tsx
new file mode 100644
index 0000000..d841b04
--- /dev/null
+++ b/src/pages/Torrent/torrentDetail.tsx
@@ -0,0 +1,334 @@
+import React, { useEffect, useState } from 'react';
+import { useParams, useNavigate } from 'react-router-dom';
+import { Button, Input, message, Spin, Tag, Card, Avatar, List, Pagination } from 'antd';
+import { DownloadOutlined, ArrowLeftOutlined, SendOutlined } from '@ant-design/icons';
+import {
+    getTorrentInfo,
+    downloadTorrent,
+    addComment,
+    getComments,
+    getCategories,
+} from '../../services/bt/index';
+
+const PAGE_SIZE = 10;
+
+const TorrentDetail: React.FC = () => {
+const { id } = useParams<{ id: string }>();
+const navigate = useNavigate();
+
+const [loading, setLoading] = useState(true);
+const [torrent, setTorrent] = useState<any>(null);
+const [categories, setCategories] = useState<any[]>([]);
+const [comments, setComments] = useState<any[]>([]);
+const [commentLoading, setCommentLoading] = useState(false);
+const [comment, setComment] = useState('');
+const [commentsTotal, setCommentsTotal] = useState(0);
+const [pageNum, setPageNum] = useState(1);
+
+useEffect(() => {
+    setLoading(true);
+    getTorrentInfo({ id: id! })
+        .then((res) => setTorrent(res.data))
+        .finally(() => setLoading(false));
+    getCategories().then((res) => setCategories(res.data || []));
+}, [id]);
+
+useEffect(() => {
+    fetchComments(pageNum);
+    // eslint-disable-next-line
+}, [id, pageNum]);
+
+const fetchComments = (page: number) => {
+    setCommentLoading(true);
+    getComments({ torrentId: Number(id), pageNum: page, pageSize: PAGE_SIZE })
+        .then((res) => {0
+            setComments(res.data?.list || []);
+            setCommentsTotal(res.data?.total || 0);
+        })
+        .finally(() => setCommentLoading(false));
+};
+
+const handleDownload = async () => {
+    try {
+        const res = await downloadTorrent({ id: id! });
+        const blob = new Blob([res], { type: 'application/x-bittorrent' });
+        const url = window.URL.createObjectURL(blob);
+        const a = document.createElement('a');
+        a.href = url;
+        a.download = `${torrent?.title || 'torrent'}.torrent`;
+        a.click();
+        window.URL.revokeObjectURL(url);
+    } catch {
+        message.error('下载失败');
+    }
+};
+
+const handleAddComment = async () => {
+    if (!comment.trim()) return;
+    await addComment({ torrentId: Number(id), comment });
+    setComment('');
+    fetchComments(1);
+    setPageNum(1);
+    message.success('评论成功');
+};
+
+const getCategoryName = (catId: number) => {
+    return categories.find((c) => c.id === catId)?.name || '未知分类';
+};
+
+const statusMap: Record<number, string> = {
+    0: '候选中',
+    1: '已发布',
+    2: '审核不通过',
+    3: '已上架修改重审中',
+    10: '已下架',
+};
+
+// 星球主题样式
+const planetBg = {
+    background: 'radial-gradient(circle at 60% 40%, #2b6cb0 0%, #1a202c 100%)',
+    minHeight: '100vh',
+    padding: '32px 0',
+};
+
+return (
+    <div style={planetBg}>
+        <div style={{ maxWidth: 900, margin: '0 auto', background: 'rgba(255,255,255,0.08)', borderRadius: 16, boxShadow: '0 8px 32px rgba(0,0,0,0.2)', padding: 24 }}>
+            <Button
+                icon={<ArrowLeftOutlined />}
+                type="link"
+                onClick={() => navigate(-1)}
+                style={{ color: '#fff', marginBottom: 16 }}
+            >
+                返回
+            </Button>
+            {loading ? (
+                <Spin size="large" />
+            ) : (
+                <>
+                    {torrent?.status !== 1 ? (
+                        <div style={{ color: '#fff', fontSize: 24, textAlign: 'center', padding: '80px 0' }}>
+                            当前状态:{statusMap[torrent?.status] || '未知状态'}
+                        </div>
+                    ) : (
+                        <>
+                            <div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>
+                                <Avatar
+                                    size={96}
+                                    src={torrent?.cover || 'https://img.icons8.com/color/96/planet.png'}
+                                    style={{ boxShadow: '0 0 24px #4299e1' }}
+                                />
+                                <div>
+                                    <h1 style={{ color: '#fff', fontSize: 32, marginBottom: 8 }}>
+                                        {torrent?.title}
+                                    </h1>
+                                    <div style={{ marginBottom: 8 }}>
+                                        <Tag color="geekblue">{getCategoryName(torrent?.categoryId)}</Tag>
+                                        {torrent?.tags?.map((tag: string) => (
+                                            <Tag key={tag} color="blue">{tag}</Tag>
+                                        ))}
+                                    </div>
+                                    <div style={{ color: '#cbd5e1', marginBottom: 8 }}>
+                                        上传者:{torrent?.owner} | 上传时间:{torrent?.createdAt}
+                                    </div>
+                                    <Button
+                                        type="primary"
+                                        icon={<DownloadOutlined />}
+                                        onClick={handleDownload}
+                                        style={{ background: 'linear-gradient(90deg,#4299e1,#805ad5)', border: 'none' }}
+                                    >
+                                        下载种子
+                                    </Button>
+                                </div>
+                            </div>
+                            <Card
+                                style={{
+                                    marginTop: 32,
+                                    background: 'rgba(255,255,255,0.12)',
+                                    border: 'none',
+                                    borderRadius: 12,
+                                    color: '#fff',
+                                }}
+                                title={<span style={{ color: '#fff' }}>种子详情</span>}
+                            >
+                                <div dangerouslySetInnerHTML={{ __html: torrent?.description || '' }} />
+                                <div style={{ marginTop: 16, color: '#cbd5e1' }}>
+                                    <span>文件大小:{torrent?.size}</span>
+                                    <span style={{ marginLeft: 24 }}>做种人数:{torrent?.seeders}</span>
+                                    <span style={{ marginLeft: 24 }}>下载人数:{torrent?.leechers}</span>
+                                    <span style={{ marginLeft: 24 }}>完成次数:{torrent?.completed}</span>
+                                </div>
+                            </Card>
+                            <Card
+                                style={{
+                                    marginTop: 32,
+                                    background: 'rgba(255,255,255,0.10)',
+                                    border: 'none',
+                                    borderRadius: 12,
+                                    color: '#fff',
+                                }}
+                                title={<span style={{ color: '#fff' }}>星球评论</span>}
+                            >
+                                <List
+                                    loading={commentLoading}
+                                    dataSource={comments}
+                                    locale={{ emptyText: '暂无评论' }}
+                                    renderItem={(item: any) => {
+                                        // 只渲染顶级评论(pid为null)
+                                        if (item.pid !== null) return null;
+                                        const [expanded, setExpanded] = useState(false);
+                                        const [replyContent, setReplyContent] = useState('');
+                                        const [replying, setReplying] = useState(false);
+
+                                        const handleReply = async () => {
+                                            if (!replyContent.trim()) return;
+                                            setReplying(true);
+                                            await addComment({ torrentId: Number(id), comment: replyContent, pid: item.id });
+                                            setReplyContent('');
+                                            setReplying(false);
+                                            fetchComments(pageNum);
+                                            message.success('回复成功');
+                                        };
+
+                                        return (
+                                            <List.Item
+                                                style={{
+                                                    alignItems: 'flex-start',
+                                                    border: 'none',
+                                                    background: 'transparent',
+                                                    padding: '20px 0',
+                                                    borderBottom: '1px solid rgba(255,255,255,0.08)',
+                                                }}
+                                            >
+                                                <List.Item.Meta
+                                                    avatar={
+                                                        <Avatar
+                                                            src={item.avatar ? item.avatar.startsWith('http') ? item.avatar : `${item.avatar}` : 'https://img.icons8.com/color/48/planet.png'}
+                                                            size={48}
+                                                            style={{ boxShadow: '0 2px 8px #4299e1' }}
+                                                        />
+                                                    }
+                                                    title={
+                                                        <span style={{ color: '#fff', fontWeight: 500 }}>
+                                                            {item.username || '匿名用户'}
+                                                        </span>
+                                                    }
+                                                    description={
+                                                        <span style={{ color: '#cbd5e1', fontSize: 16 }}>
+                                                            {item.comment}
+                                                            <span style={{ marginLeft: 16, fontSize: 12, color: '#a0aec0' }}>
+                                                                {item.createTime}
+                                                            </span>
+                                                            <Button
+                                                                type="link"
+                                                                size="small"
+                                                                style={{ marginLeft: 16, color: '#4299e1' }}
+                                                                onClick={() => setExpanded((v) => !v)}
+                                                            >
+                                                                {expanded ? '收起回复' : `展开回复${item.children?.length ? ` (${item.children.length})` : ''}`}
+                                                            </Button>
+                                                        </span>
+                                                    }
+                                                />
+                                                {expanded && (
+                                                    <div style={{ width: '100%', marginTop: 12, marginLeft: 56 }}>
+                                                        <List
+                                                            dataSource={item.children}
+                                                            itemLayout="horizontal"
+                                                            locale={{ emptyText: '暂无子评论' }}
+                                                            renderItem={(child: any) => (
+                                                                <List.Item
+                                                                    style={{
+                                                                        border: 'none',
+                                                                        background: 'transparent',
+                                                                        padding: '12px 0 0 0',
+                                                                        marginLeft: 0,
+                                                                    }}
+                                                                >
+                                                                    <List.Item.Meta
+                                                                        avatar={
+                                                                            <Avatar
+                                                                                src={child.avatar ? child.avatar.startsWith('http') ? child.avatar : `${child.avatar}` : 'https://img.icons8.com/color/48/planet.png'}
+                                                                                size={36}
+                                                                                style={{ boxShadow: '0 1px 4px #805ad5' }}
+                                                                            />
+                                                                        }
+                                                                        title={
+                                                                            <span style={{ color: '#c3bfff', fontWeight: 500, fontSize: 15 }}>
+                                                                                {child.username || '匿名用户'}
+                                                                            </span>
+                                                                        }
+                                                                        description={
+                                                                            <span style={{ color: '#e0e7ef', fontSize: 15 }}>
+                                                                                {child.comment}
+                                                                                <span style={{ marginLeft: 12, fontSize: 12, color: '#a0aec0' }}>
+                                                                                    {child.createTime}
+                                                                                </span>
+                                                                            </span>
+                                                                        }
+                                                                    />
+                                                                </List.Item>
+                                                            )}
+                                                        />
+                                                        <div style={{ display: 'flex', marginTop: 12, gap: 8 }}>
+                                                            <Input.TextArea
+                                                                value={replyContent}
+                                                                onChange={e => setReplyContent(e.target.value)}
+                                                                placeholder="回复该评论"
+                                                                autoSize={{ minRows: 1, maxRows: 3 }}
+                                                                style={{ background: 'rgba(255,255,255,0.15)', color: '#fff', border: 'none' }}
+                                                            />
+                                                            <Button
+                                                                type="primary"
+                                                                icon={<SendOutlined />}
+                                                                loading={replying}
+                                                                onClick={handleReply}
+                                                                disabled={!replyContent.trim()}
+                                                                style={{ background: 'linear-gradient(90deg,#4299e1,#805ad5)', border: 'none', height: 40 }}
+                                                            >
+                                                                发送
+                                                            </Button>
+                                                        </div>
+                                                    </div>
+                                                )}
+                                            </List.Item>
+                                        );
+                                    }}
+                                />
+                                <Pagination
+                                    style={{ marginTop: 16, textAlign: 'right' }}
+                                    current={pageNum}
+                                    pageSize={PAGE_SIZE}
+                                    total={commentsTotal}
+                                    onChange={setPageNum}
+                                    showSizeChanger={false}
+                                />
+                                <div style={{ display: 'flex', marginTop: 24, gap: 8 }}>
+                                    <Input.TextArea
+                                        value={comment}
+                                        onChange={e => setComment(e.target.value)}
+                                        placeholder="在星球上留下你的评论吧~"
+                                        autoSize={{ minRows: 2, maxRows: 4 }}
+                                        style={{ background: 'rgba(255,255,255,0.15)', color: '#fff', border: 'none' }}
+                                    />
+                                    <Button
+                                        type="primary"
+                                        icon={<SendOutlined />}
+                                        onClick={handleAddComment}
+                                        disabled={!comment.trim()}
+                                        style={{ background: 'linear-gradient(90deg,#4299e1,#805ad5)', border: 'none', height: 48 }}
+                                    >
+                                        发送
+                                    </Button>
+                                </div>
+                            </Card>
+                        </>
+                    )}
+                </>
+            )}
+        </div>
+    </div>
+);
+};
+
+export default TorrentDetail;
\ No newline at end of file
diff --git a/src/pages/Torrent/torrentList.tsx b/src/pages/Torrent/torrentList.tsx
new file mode 100644
index 0000000..b881aea
--- /dev/null
+++ b/src/pages/Torrent/torrentList.tsx
@@ -0,0 +1,325 @@
+import React, { useEffect, useState } from "react";
+import { styled } from "@mui/material/styles";
+import axios from "axios";
+import {
+  Box,
+  Button,
+  Card,
+  CardContent,
+  Chip,
+  CircularProgress,
+  Container,
+  MenuItem,
+  Pagination,
+  Select,
+  Typography,
+} from "@mui/material";
+import { getCategories, getTorrentList } from "../../services/bt/index";
+// 优化后的星空背景
+const StarBg = styled("div")({
+  minHeight: "100vh",
+  width: "auto",
+  background: "radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%)",
+  overflow: "auto",
+  position: "relative",
+  top: 0,
+  left: 0,
+
+
+  
+  // 使用CSS动画实现星空
+  "&:before, &:after": {
+    content: '""',
+    position: "absolute",
+    top: 0,
+    left: 0,
+    width: "100%",
+    height: "100%",
+    pointerEvents: "none",
+  },
+  "&:before": {
+    background: `
+      radial-gradient(1px 1px at 20% 30%, rgba(255,255,255,0.8), rgba(255,255,255,0)),
+      radial-gradient(1px 1px at 40% 70%, rgba(255,255,255,0.8), rgba(255,255,255,0)),
+      radial-gradient(1.5px 1.5px at 60% 20%, rgba(255,255,255,0.9), rgba(255,255,255,0)),
+      radial-gradient(1.5px 1.5px at 80% 90%, rgba(255,255,255,0.9), rgba(255,255,255,0))
+    `,
+    backgroundRepeat: "repeat",
+    backgroundSize: "200px 200px",
+    animation: "twinkle 10s infinite ease-in-out",
+  },
+  "&:after": {
+    background: `
+      radial-gradient(1px 1px at 70% 40%, rgba(255,255,255,0.7), rgba(255,255,255,0)),
+      radial-gradient(1.2px 1.2px at 10% 80%, rgba(255,255,255,0.7), rgba(255,255,255,0)),
+      radial-gradient(1.5px 1.5px at 30% 60%, rgba(255,255,255,0.8), rgba(255,255,255,0))
+    `,
+    backgroundRepeat: "repeat",
+    backgroundSize: "300px 300px",
+    animation: "twinkle 15s infinite 5s ease-in-out",
+  },
+  
+  "@keyframes twinkle": {
+    "0%, 100%": { opacity: 0.3 },
+    "50%": { opacity: 0.8 },
+  }
+});
+
+// 分类标签栏
+const CategoryBar = styled(Box)({
+display: "flex",
+gap: 12,
+alignItems: "center",
+marginBottom: 24,
+flexWrap: "wrap",
+});
+
+// 种子卡片
+const TorrentCard = styled(Card)({
+background: "rgba(30, 41, 59, 0.85)",
+color: "#fff",
+borderRadius: 16,
+boxShadow: "0 4px 24px 0 rgba(0,0,0,0.4)",
+border: "1px solid #334155",
+transition: "transform 0.2s",
+"&:hover": {
+    transform: "scale(1.025)",
+    boxShadow: "0 8px 32px 0 #0ea5e9",
+},
+});
+
+const sortOptions = [
+{ label: "最新", value: { sortField: "createTime", sortDirection: "desc" } },
+{ label: "下载量", value: { sortField: "completions", sortDirection: "desc" } },
+{ label: "推荐", value: { sortField: "seeders", sortDirection: "desc" } },
+];
+
+const PAGE_SIZE = 20;
+
+const TorrentListPage: React.FC = () => {
+    const [categories, setCategories] = useState<
+        { id: number; name: string; remark?: string | null; type?: number | null }[]
+    >([]);
+    const [selectedCat, setSelectedCat] = useState<number | null>(null);
+    const [sort, setSort] = useState(sortOptions[0].value);
+    const [torrents, setTorrents] = useState<any[]>([]);
+    const [loading, setLoading] = useState(false);
+    const [page, setPage] = useState(1);
+    const [total, setTotal] = useState(0);
+
+    // 获取分类
+    useEffect(() => {
+        getCategories().then((res) => {
+            // 直接使用返回的data数组
+            setCategories(res.data || []);
+        });
+    }, []);
+
+    // 获取种子列表
+    useEffect(() => {
+        setLoading(true);
+        getTorrentList({
+            category: selectedCat !== null ? String(selectedCat) : undefined,
+            sortField: sort.sortField,
+            sortDirection: sort.sortDirection,
+            pageNum: page,
+            pageSize: PAGE_SIZE,
+        })
+            .then((res) => {
+                // 直接使用返回的data数组和page对象
+                setTorrents(res.data || []);
+                setTotal(res.page?.total || 0);
+            })
+            .finally(() => setLoading(false));
+    }, [selectedCat, sort, page]);
+
+return (
+    <StarBg>
+        <Container maxWidth="lg" sx={{ pt: 6, pb: 6, position: "relative" }}>
+            {/* 原有内容 */}
+            <Typography
+                variant="h3"
+                sx={{
+                    color: "#fff",
+                    fontWeight: 700,
+                    mb: 3,
+                    letterSpacing: 2,
+                    textShadow: "0 2px 16px #0ea5e9",
+                }}
+            >
+                ThunderHub 星空PT种子广场
+            </Typography>
+            <CategoryBar>
+                <Chip
+                    label="全部"
+                    color={selectedCat === null ? "primary" : "default"}
+                    onClick={() => {
+                        setSelectedCat(null);
+                        setPage(1);
+                    }}
+                    sx={{
+                        fontWeight: 600,
+                        fontSize: 16,
+                        color: selectedCat === null ? undefined : "#fff",
+                    }}
+                />
+                {categories.map((cat) => (
+                    <Chip
+                        key={cat.id}
+                        label={cat.name}
+                        color={selectedCat === cat.id ? "primary" : "default"}
+                        onClick={() => {
+                            setSelectedCat(cat.id);
+                            setPage(1);
+                        }}
+                        sx={{
+                            fontWeight: 600,
+                            fontSize: 16,
+                            color: selectedCat === cat.id ? undefined : "#fff",
+                        }}
+                    />
+                ))}
+                <Box sx={{ flex: 1 }} />
+                <Select
+                    size="small"
+                    value={JSON.stringify(sort)}
+                    onChange={(e) => {
+                        setSort(JSON.parse(e.target.value));
+                        setPage(1);
+                    }}
+                    sx={{
+                        color: "#fff",
+                        background: "#1e293b",
+                        borderRadius: 2,
+                        ".MuiOutlinedInput-notchedOutline": { border: 0 },
+                        minWidth: 120,
+                    }}
+                >
+                    {sortOptions.map((opt) => (
+                        <MenuItem key={opt.label} value={JSON.stringify(opt.value)}>
+                            {opt.label}
+                        </MenuItem>
+                    ))}
+                </Select>
+            </CategoryBar>
+            {loading ? (
+                <Box sx={{ display: "flex", justifyContent: "center", mt: 8 }}>
+                    <CircularProgress color="info" />
+                </Box>
+            ) : (
+                <>
+                    <Box
+                        sx={{
+                            display: "flex",
+                            flexWrap: "wrap",
+                            gap: 3,
+                            justifyContent: { xs: "center", md: "flex-start" },
+                        }}
+                    >
+                        {torrents.map((torrent) => (
+                            <Box
+                                key={torrent.id}
+                                sx={{
+                                    flex: "1 1 320px",
+                                    maxWidth: { xs: "100%", sm: "48%", md: "32%" },
+                                    minWidth: 300,
+                                    mb: 3,
+                                    display: "flex",
+                                    cursor: "pointer",
+                                }}
+                                onClick={() => window.open(`/torrent-detail/${torrent.id}`, "_self")}
+                            >
+                                <TorrentCard sx={{ width: "100%" }}>
+                                    <CardContent>
+                                        <Typography
+                                            variant="h6"
+                                            sx={{
+                                                color: "#38bdf8",
+                                                fontWeight: 700,
+                                                mb: 1,
+                                                textOverflow: "ellipsis",
+                                                overflow: "hidden",
+                                                whiteSpace: "nowrap",
+                                            }}
+                                            title={torrent.title}
+                                        >
+                                            {torrent.title}
+                                        </Typography>
+                                        <Box
+                                            sx={{
+                                                display: "flex",
+                                                gap: 1,
+                                                alignItems: "center",
+                                                mb: 1,
+                                                flexWrap: "wrap",
+                                            }}
+                                        >
+                                            <Chip
+                                                size="small"
+                                                label={
+                                                    categories.find((c) => c.id === torrent.category)?.name ||
+                                                    "未知"
+                                                }
+                                                sx={{
+                                                    background: "#0ea5e9",
+                                                    color: "#fff",
+                                                    fontWeight: 600,
+                                                }}
+                                            />
+                                            {torrent.free === "1" && (
+                                                <Chip
+                                                    size="small"
+                                                    label="促销"
+                                                    sx={{
+                                                        background: "#fbbf24",
+                                                        color: "#1e293b",
+                                                        fontWeight: 600,
+                                                    }}
+                                                />
+                                            )}
+                                        </Box>
+                                        <Typography variant="body2" sx={{ color: "#cbd5e1", mb: 1 }}>
+                                            上传时间:{torrent.createTime}
+                                        </Typography>
+                                        <Box sx={{ display: "flex", gap: 2, mt: 1 }}>
+                                            <Typography variant="body2" sx={{ color: "#38bdf8" }}>
+                                                做种:{torrent.seeders}
+                                            </Typography>
+                                            <Typography variant="body2" sx={{ color: "#f472b6" }}>
+                                                下载量:{torrent.completions}
+                                            </Typography>
+                                            <Typography variant="body2" sx={{ color: "#fbbf24" }}>
+                                                下载中:{torrent.leechers}
+                                            </Typography>
+                                        </Box>
+                                    </CardContent>
+                                </TorrentCard>
+                            </Box>
+                        ))}
+                    </Box>
+                    <Box sx={{ display: "flex", justifyContent: "center", mt: 4 }}>
+                        <Pagination
+                            count={Math.ceil(total / PAGE_SIZE)}
+                            page={page}
+                            onChange={(_, v) => setPage(v)}
+                            color="primary"
+                            sx={{
+                                ".MuiPaginationItem-root": {
+                                    color: "#fff",
+                                    background: "#1e293b",
+                                    border: "1px solid #334155",
+                                },
+                                ".Mui-selected": {
+                                    background: "#0ea5e9 !important",
+                                },
+                            }}
+                        />
+                    </Box>
+                </>
+            )}
+        </Container>
+    </StarBg>
+);
+};
+
+export default TorrentListPage;
\ No newline at end of file
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