Update on frontend pages May 27th
Change-Id: I452e5a0ae089114f5b68920e507e56db897856f8
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;