| import React, { useState } from 'react'; |
| import { |
| Box, Container, Typography, Paper, |
| TextField, Button, Stack |
| } from '@mui/material'; |
| import CloudUploadIcon from '@mui/icons-material/CloudUpload'; |
| |
| |
| 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); |
| |
| const token = localStorage.getItem('jwt_token'); // 假设你登录后将 token 存在 localStorage |
| |
| fetch('http://localhost:8080/api/torrents/upload', { |
| method: 'POST', |
| headers: { |
| Authorization: `Bearer ${token}`, |
| }, |
| body: formData, |
| }) |
| .then(response => { |
| if (response.ok) { |
| console.log('上传成功'); |
| alert('上传成功'); |
| } else { |
| return response.text().then(text => { |
| console.error('上传失败:', text); |
| alert('上传失败: ' + text); |
| }); |
| } |
| }) |
| .catch(error => { |
| console.error('上传出错:', error); |
| alert('上传出错: ' + error.message); |
| }); |
| }; |
| |
| 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; |