blob: 493cdcec3f33f63bd264dafe01fee4d296ad13ce [file] [log] [blame]
ZBDff4d40a2025-05-27 17:05:20 +08001import React, { useState } from 'react';
2import {
3 Box, Container, Typography, Paper,
4 TextField, Button, Stack
5} from '@mui/material';
6import CloudUploadIcon from '@mui/icons-material/CloudUpload';
7// Assuming your base.css is in a relative path like src/styles/base/base.css
8// If your component is in src/components/Upload.js, this path would be ../styles/base/base.css
9// Adjust if your project structure is different.
10// For this example, I'll assume it's correctly linked.
11// import '../styles/base/base.css';
12
13function Upload() {
14 const [title, setTitle] = useState('');
15 const [file, setFile] = useState(null);
16
17 const handleUpload = () => {
18 const formData = new FormData();
19 if (file) {
20 formData.append('file', file);
21 }
22 formData.append('title', title);
23
24 // Replace with your actual API endpoint and error handling
25 fetch('/api/torrents/upload', {
26 method: 'POST',
27 body: formData
28 })
29 .then(response => {
30 if (response.ok) {
31 // Use a more modern way to show messages, e.g., a Snackbar
32 console.log('上传成功');
33 alert('上传成功 (建议使用Snackbar等UI组件替代alert)');
34 } else {
35 console.error('上传失败');
36 alert('上传失败 (建议使用Snackbar等UI组件替代alert)');
37 }
38 })
39 .catch(error => {
40 console.error('上传出错:', error);
41 alert('上传出错 (建议使用Snackbar等UI组件替代alert)');
42 });
43 };
44
45 return (
46 <Box sx={{ minHeight: '100vh', py: 4, background: 'linear-gradient(135deg, #2c3e50, #4ca1af)', color: 'white' }}> {/* Moved body styles here for self-containment */}
47 <Container maxWidth="sm" sx={{ position: 'relative', zIndex: 10 }}>
48 <Typography variant="h4" sx={{ textAlign: 'center', mb: 3, fontWeight: 'bold' }}> {/* Replaced className with sx */}
49 ⬆️ 上传种子 · Mini-Tracker
50 </Typography>
51
52 <Paper sx={{ position: 'relative', zIndex: 20, padding: '2rem', backgroundColor: 'rgba(30, 30, 30, 0.9)' }}> {/* Replaced className with sx */}
53 <Stack spacing={3}> {/* Increased spacing a bit */}
54 <TextField
55 label="资源名称"
56 variant="outlined"
57 fullWidth
58 value={title}
59 onChange={e => setTitle(e.target.value)}
60 placeholder="请输入资源名称"
61 InputLabelProps={{
62 sx: {
63 color: '#ccc',
64 '&.Mui-focused': { // Ensure label color remains consistent when focused
65 color: '#ccc',
66 }
67 }
68 }}
69 sx={{
70 // Root element of the TextField
71 backgroundColor: 'transparent',
72
73 // Styles for the HTML input element itself
74 '& .MuiInputBase-input': {
75 color: 'white', // Text color
76 backgroundColor: 'transparent', // **** THIS IS THE FIX **** Ensure input background is transparent
77 caretColor: 'white', // Cursor color
78 '&::placeholder': {
79 color: '#bbb',
80 opacity: 1,
81 },
82 },
83 // Styles for the outlined border
84 '& .MuiOutlinedInput-root': {
85 '& fieldset': {
86 borderColor: '#888', // Default border color
87 },
88 '&:hover fieldset': {
89 borderColor: '#aaa', // Border color on hover
90 },
91 '&.Mui-focused fieldset': {
92 borderColor: '#fff', // Border color when focused
93 },
94 },
95 }}
96 />
97
98 <Button
99 variant="contained"
100 component="label" // Allows the button to act as a label for a hidden input
101 startIcon={<CloudUploadIcon />}
102 sx={{ backgroundColor: '#00bcd4', color: 'white', '&:hover': { backgroundColor: '#0097a7' } }} // Replaced className with sx
103 >
104 选择种子文件
105 <input
106 hidden
107 type="file"
108 accept=".torrent"
109 onChange={e => {
110 if (e.target.files && e.target.files[0]) {
111 setFile(e.target.files[0]);
112 }
113 }}
114 />
115 </Button>
116
117 {file && (
118 <Typography variant="body2" sx={{ color: '#eee', textAlign: 'center' }}>
119 已选择文件: {file.name}
120 </Typography>
121 )}
122
123 <Button
124 variant="outlined"
125 onClick={handleUpload}
126 disabled={!file || !title}
127 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
128 >
129 提交上传
130 </Button>
131 </Stack>
132 </Paper>
133 </Container>
134
135 {/* Background bubbles animation - assuming CSS for .bubbles and .bubble is globally available or defined in an imported CSS file */}
136 {/* For self-containment, these styles would ideally be JSS or defined in a <style> tag if not using styled-components or similar */}
137 <Box className="bubbles" sx={{
138 pointerEvents: 'none',
139 position: 'fixed',
140 top: 0,
141 left: 0,
142 width: '100%',
143 height: '100%',
144 overflow: 'hidden',
145 zIndex: 1, // Ensure it's behind the content paper but above the main background
146 }}>
147 {[...Array(40)].map((_, i) => (
148 <Box
149 key={i}
150 className="bubble" // Assuming .bubble and @keyframes rise are defined in your CSS
151 sx={{
152 position: 'absolute',
153 bottom: '-100px',
154 background: 'rgba(255, 255, 255, 0.15)',
155 borderRadius: '50%',
156 animation: 'rise 20s infinite ease-in', // Make sure @keyframes rise is defined
157 width: `${Math.random() * 20 + 10}px`,
158 height: `${Math.random() * 20 + 10}px`,
159 left: `${Math.random() * 100}%`,
160 animationDuration: `${10 + Math.random() * 20}s`,
161 animationDelay: `${Math.random() * 5}s`
162 }}
163 />
164 ))}
165 </Box>
166 {/* Define keyframes if not in external CSS */}
167 <style>
168 {`
169 @keyframes rise {
170 0% {
171 transform: translateY(0) scale(1);
172 opacity: 0.5;
173 }
174 50% {
175 opacity: 1;
176 }
177 100% {
178 transform: translateY(-120vh) scale(0.5);
179 opacity: 0;
180 }
181 }
182 body { /* Basic reset if base.css is not loaded */
183 margin: 0;
184 font-family: 'Roboto', sans-serif;
185 }
186 `}
187 </style>
188 </Box>
189 );
190}
191
192export default Upload;