diff --git a/frontend/my-app/src/pages/UploadTorrent.jsx b/frontend/my-app/src/pages/UploadTorrent.jsx
new file mode 100644
index 0000000..493cdce
--- /dev/null
+++ b/frontend/my-app/src/pages/UploadTorrent.jsx
@@ -0,0 +1,192 @@
+import React, { useState } from 'react';
+import {
+  Box, Container, Typography, Paper,
+  TextField, Button, Stack
+} from '@mui/material';
+import CloudUploadIcon from '@mui/icons-material/CloudUpload';
+// Assuming your base.css is in a relative path like src/styles/base/base.css
+// If your component is in src/components/Upload.js, this path would be ../styles/base/base.css
+// Adjust if your project structure is different.
+// For this example, I'll assume it's correctly linked.
+// import '../styles/base/base.css'; 
+
+function Upload() {
+  const [title, setTitle] = useState('');
+  const [file, setFile] = useState(null);
+
+  const handleUpload = () => {
+    const formData = new FormData();
+    if (file) {
+      formData.append('file', file);
+    }
+    formData.append('title', title);
+
+    // Replace with your actual API endpoint and error handling
+    fetch('/api/torrents/upload', {
+      method: 'POST',
+      body: formData
+    })
+    .then(response => {
+      if (response.ok) {
+        // Use a more modern way to show messages, e.g., a Snackbar
+        console.log('上传成功'); 
+        alert('上传成功 (建议使用Snackbar等UI组件替代alert)');
+      } else {
+        console.error('上传失败');
+        alert('上传失败 (建议使用Snackbar等UI组件替代alert)');
+      }
+    })
+    .catch(error => {
+      console.error('上传出错:', error);
+      alert('上传出错 (建议使用Snackbar等UI组件替代alert)');
+    });
+  };
+
+  return (
+    <Box sx={{ minHeight: '100vh', py: 4, background: 'linear-gradient(135deg, #2c3e50, #4ca1af)', color: 'white' }}> {/* Moved body styles here for self-containment */}
+      <Container maxWidth="sm" sx={{ position: 'relative', zIndex: 10 }}>
+        <Typography variant="h4" sx={{ textAlign: 'center', mb: 3, fontWeight: 'bold' }}> {/* Replaced className with sx */}
+          ⬆️ 上传种子 · Mini-Tracker
+        </Typography>
+
+        <Paper sx={{ position: 'relative', zIndex: 20, padding: '2rem', backgroundColor: 'rgba(30, 30, 30, 0.9)' }}> {/* Replaced className with sx */}
+          <Stack spacing={3}> {/* Increased spacing a bit */}
+            <TextField
+              label="资源名称"
+              variant="outlined"
+              fullWidth
+              value={title}
+              onChange={e => setTitle(e.target.value)}
+              placeholder="请输入资源名称"
+              InputLabelProps={{ 
+                sx: { 
+                  color: '#ccc',
+                  '&.Mui-focused': { // Ensure label color remains consistent when focused
+                    color: '#ccc',
+                  }
+                } 
+              }}
+              sx={{
+                // Root element of the TextField
+                backgroundColor: 'transparent', 
+                
+                // Styles for the HTML input element itself
+                '& .MuiInputBase-input': {
+                  color: 'white', // Text color
+                  backgroundColor: 'transparent', // **** THIS IS THE FIX **** Ensure input background is transparent
+                  caretColor: 'white', // Cursor color
+                  '&::placeholder': {
+                    color: '#bbb',
+                    opacity: 1,
+                  },
+                },
+                // Styles for the outlined border
+                '& .MuiOutlinedInput-root': {
+                  '& fieldset': {
+                    borderColor: '#888', // Default border color
+                  },
+                  '&:hover fieldset': {
+                    borderColor: '#aaa', // Border color on hover
+                  },
+                  '&.Mui-focused fieldset': {
+                    borderColor: '#fff', // Border color when focused
+                  },
+                },
+              }}
+            />
+
+            <Button
+              variant="contained"
+              component="label" // Allows the button to act as a label for a hidden input
+              startIcon={<CloudUploadIcon />}
+              sx={{ backgroundColor: '#00bcd4', color: 'white', '&:hover': { backgroundColor: '#0097a7' } }} // Replaced className with sx
+            >
+              选择种子文件
+              <input
+                hidden
+                type="file"
+                accept=".torrent"
+                onChange={e => {
+                  if (e.target.files && e.target.files[0]) {
+                    setFile(e.target.files[0]);
+                  }
+                }}
+              />
+            </Button>
+            
+            {file && (
+              <Typography variant="body2" sx={{ color: '#eee', textAlign: 'center' }}>
+                已选择文件: {file.name}
+              </Typography>
+            )}
+
+            <Button
+              variant="outlined"
+              onClick={handleUpload}
+              disabled={!file || !title}
+              sx={{ borderColor: 'white', color: 'white', '&:hover': { borderColor: '#ddd', backgroundColor: 'rgba(255,255,255,0.1)'}, '&.Mui-disabled': { borderColor: '#777', color: '#777' } }} // Replaced className with sx
+            >
+              提交上传
+            </Button>
+          </Stack>
+        </Paper>
+      </Container>
+
+      {/* Background bubbles animation - assuming CSS for .bubbles and .bubble is globally available or defined in an imported CSS file */}
+      {/* For self-containment, these styles would ideally be JSS or defined in a <style> tag if not using styled-components or similar */}
+      <Box className="bubbles" sx={{
+         pointerEvents: 'none',
+         position: 'fixed', 
+         top: 0,
+         left: 0,
+         width: '100%',
+         height: '100%',
+         overflow: 'hidden',
+         zIndex: 1, // Ensure it's behind the content paper but above the main background
+      }}>
+        {[...Array(40)].map((_, i) => (
+          <Box
+            key={i}
+            className="bubble" // Assuming .bubble and @keyframes rise are defined in your CSS
+            sx={{
+              position: 'absolute',
+              bottom: '-100px',
+              background: 'rgba(255, 255, 255, 0.15)',
+              borderRadius: '50%',
+              animation: 'rise 20s infinite ease-in', // Make sure @keyframes rise is defined
+              width: `${Math.random() * 20 + 10}px`,
+              height: `${Math.random() * 20 + 10}px`,
+              left: `${Math.random() * 100}%`,
+              animationDuration: `${10 + Math.random() * 20}s`,
+              animationDelay: `${Math.random() * 5}s`
+            }}
+          />
+        ))}
+      </Box>
+      {/* Define keyframes if not in external CSS */}
+      <style>
+        {`
+          @keyframes rise {
+            0% {
+              transform: translateY(0) scale(1);
+              opacity: 0.5;
+            }
+            50% {
+              opacity: 1;
+            }
+            100% {
+              transform: translateY(-120vh) scale(0.5);
+              opacity: 0;
+            }
+          }
+          body { /* Basic reset if base.css is not loaded */
+            margin: 0;
+            font-family: 'Roboto', sans-serif;
+          }
+        `}
+      </style>
+    </Box>
+  );
+}
+
+export default Upload;
