final
Change-Id: Icf4ec3950a6bd1e066fa4a1976af36721af62a06
diff --git a/front/src/PublishPage.js b/front/src/PublishPage.js
index df03cae..d9f7db9 100644
--- a/front/src/PublishPage.js
+++ b/front/src/PublishPage.js
@@ -1,9 +1,47 @@
-import React, { useState } from 'react';
-import './App.css';
-import './PublishPage.css';
+import React, { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './SharedStyles.css';
import { API_BASE_URL } from "./config";
+import HomeIcon from "@mui/icons-material/Home";
+import MovieIcon from "@mui/icons-material/Movie";
+import TvIcon from "@mui/icons-material/Tv";
+import MusicNoteIcon from "@mui/icons-material/MusicNote";
+import AnimationIcon from "@mui/icons-material/Animation";
+import SportsEsportsIcon from "@mui/icons-material/SportsEsports";
+import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";
+import PersonIcon from "@mui/icons-material/Person";
+import AccountCircleIcon from "@mui/icons-material/AccountCircle";
+import ForumIcon from "@mui/icons-material/Forum";
+import HelpIcon from "@mui/icons-material/Help";
+import CloudUploadIcon from "@mui/icons-material/CloudUpload";
+import PublishIcon from "@mui/icons-material/Publish";
+import CategoryIcon from "@mui/icons-material/Category";
+import TitleIcon from "@mui/icons-material/Title";
+import DescriptionIcon from "@mui/icons-material/Description";
+import CheckCircleIcon from "@mui/icons-material/CheckCircle";
+
+const navItems = [
+ { label: "首页", icon: <HomeIcon className="emerald-nav-icon" />, path: "/home", type: "home" },
+ { label: "电影", icon: <MovieIcon className="emerald-nav-icon" />, path: "/movie", type: "movie" },
+ { label: "剧集", icon: <TvIcon className="emerald-nav-icon" />, path: "/tv", type: "tv" },
+ { label: "音乐", icon: <MusicNoteIcon className="emerald-nav-icon" />, path: "/music", type: "music" },
+ { label: "动漫", icon: <AnimationIcon className="emerald-nav-icon" />, path: "/anime", type: "anime" },
+ { label: "游戏", icon: <SportsEsportsIcon className="emerald-nav-icon" />, path: "/game", type: "game" },
+ { label: "体育", icon: <SportsMartialArtsIcon className="emerald-nav-icon" />, path: "/sport", type: "sport" },
+ { label: "资料", icon: <PersonIcon className="emerald-nav-icon" />, path: "/info", type: "info" },
+ { label: "论坛", icon: <ForumIcon className="emerald-nav-icon" />, path: "/forum", type: "forum" },
+ { label: "发布", icon: <AccountCircleIcon className="emerald-nav-icon" />, path: "/publish", type: "publish" },
+ { label: "求种", icon: <HelpIcon className="emerald-nav-icon" />, path: "/begseed", type: "help" },
+];
+
+// 发布页面专用文字雨内容
+const publishTexts = [
+ "分享", "上传", "种子", "资源", "贡献", "精品", "原创", "高清",
+ "无损", "蓝光", "1080P", "4K", "HDR", "珍藏", "首发", "独家"
+];
const PublishPage = () => {
+ const navigate = useNavigate();
const [formData, setFormData] = useState({
type: '',
torrentFile: '',
@@ -11,6 +49,30 @@
subtitle: ''
});
const [subType, setSubType] = useState('');
+ const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' }); const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });
+ const [uploadProgress, setUploadProgress] = useState(0);
+ const [isUploading, setIsUploading] = useState(false);
+ const [isDragOver, setIsDragOver] = useState(false);
+
+ useEffect(() => {
+ // 获取用户信息
+ const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
+ const userId = match ? match[2] : null;
+ if (userId) {
+ fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)
+ .then(res => res.json())
+ .then(data => {
+ setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });
+ setUserPT({
+ magic: data.magic_value || data.magic || 0,
+ ratio: data.share_ratio || data.share || 0,
+ upload: data.upload_amount || data.upload || 0,
+ download: data.download_amount || data.download || 0,
+ });
+ })
+ .catch(err => console.error('Fetching user profile failed', err));
+ }
+ }, []);
const typeOptions = {
'电影': ['大陆', '港台', '欧美', '日韩'],
@@ -26,17 +88,63 @@
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
-
const handleFileChange = (e) => {
const file = e.target.files[0];
- if (file && file.name.split('.').pop() !== 'torrent') {
+ validateAndSetFile(file);
+ };
+
+ const validateAndSetFile = (file) => {
+ if (file && file.name.split('.').pop().toLowerCase() !== 'torrent') {
alert('仅能上传.torrent类型文件');
- e.target.value = null;
- } else {
+ return false;
+ } else if (file) {
setFormData({ ...formData, torrentFile: file });
+ return true;
+ }
+ return false;
+ };
+
+ // 拖拽上传功能
+ const handleDragEnter = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragOver(true);
+ };
+
+ const handleDragLeave = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ // 只有当鼠标真正离开拖拽区域时才设置为false
+ if (!e.currentTarget.contains(e.relatedTarget)) {
+ setIsDragOver(false);
}
};
+ const handleDragOver = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ const handleDrop = (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragOver(false);
+
+ const files = e.dataTransfer.files;
+ if (files.length > 0) {
+ const file = files[0];
+ if (validateAndSetFile(file)) {
+ // 如果文件验证成功,清空文件输入框并重新设置
+ const fileInput = document.getElementById('torrentFile');
+ if (fileInput) {
+ // 创建一个新的FileList对象来模拟文件选择
+ const dt = new DataTransfer();
+ dt.items.add(file);
+ fileInput.files = dt.files;
+ }
+ }
+ }
+ };
const handleSubmit = async (e) => {
e.preventDefault();
const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
@@ -46,6 +154,21 @@
alert('请上传.torrent文件');
return;
}
+
+ setIsUploading(true);
+ setUploadProgress(0);
+
+ // 模拟上传进度
+ const progressInterval = setInterval(() => {
+ setUploadProgress(prev => {
+ if (prev >= 90) {
+ clearInterval(progressInterval);
+ return prev;
+ }
+ return prev + Math.random() * 15;
+ });
+ }, 200);
+
const data = new FormData();
data.append('userid', userId);
data.append('title', formData.title);
@@ -58,84 +181,299 @@
method: 'POST',
body: data,
});
+
+ clearInterval(progressInterval);
+ setUploadProgress(100);
+
if (response.ok) {
- alert('上传成功!');
+ setTimeout(() => {
+ alert('上传成功!');
+ setIsUploading(false);
+ setUploadProgress(0);
+ // 重置表单
+ setFormData({
+ type: '',
+ torrentFile: '',
+ title: '',
+ subtitle: ''
+ });
+ setSubType('');
+ }, 500);
} else {
+ setIsUploading(false);
+ setUploadProgress(0);
alert('上传失败');
}
} catch (err) {
+ clearInterval(progressInterval);
+ setIsUploading(false);
+ setUploadProgress(0);
alert('网络错误');
}
};
-
return (
- <div className="publish-page">
- <h1 className="page-title">发布种子</h1>
- <form onSubmit={handleSubmit} className="publish-form">
- <div className="form-row">
- <label htmlFor="type">类型</label>
- <select name="type" id="type" value={formData.type} onChange={e => { handleChange(e); setSubType(''); }} required>
- <option value="">请选择类型</option>
- <option value="电影">电影</option>
- <option value="剧集">剧集</option>
- <option value="音乐">音乐</option>
- <option value="动漫">动漫</option>
- <option value="游戏">游戏</option>
- <option value="体育">体育</option>
- <option value="资料">资料</option>
- </select>
- </div>
- {formData.type && typeOptions[formData.type] && (
- <div className="form-row">
- <label htmlFor="subtype">具体类型</label>
- <select name="subtype" id="subtype" value={subType} onChange={e => setSubType(e.target.value)} required>
- <option value="">请选择具体类型</option>
- {typeOptions[formData.type].map(opt => (
- <option key={opt} value={opt}>{opt}</option>
- ))}
- </select>
+ <div className="emerald-home-container">
+ {/* 发布页面专用文字雨 */}
+ <div className="publish-text-rain">
+ {publishTexts.map((text, index) => (
+ <div key={index} className="text-drop" style={{
+ left: `${(index * 4) % 100}%`,
+ animationDelay: `${(index * 0.9) % 12}s`,
+ animationDuration: `${7 + (index % 6)}s`,
+ color: index % 3 === 0 ? '#90ee90' : index % 3 === 1 ? '#2d5016' : '#4a7c59'
+ }}>
+ {text}
</div>
- )}
+ ))}
+ </div>
- <div className="form-row">
- <label htmlFor="torrentFile">种子文件</label>
- <input
- type="file"
- id="torrentFile"
- name="torrentFile"
- onChange={handleFileChange}
- required
- />
- <span style={{ fontSize: '12px', color: '#666' }}>需上传.torrent类型文件</span>
+ {/* 浮动园林装饰元素 */}
+ <div className="floating-garden-elements">
+ <div className="garden-element">📤</div>
+ <div className="garden-element">🌟</div>
+ <div className="garden-element">💎</div>
+ <div className="garden-element">🎯</div>
+ </div>
+
+ <div className="emerald-content">
+ {/* NeuraFlux用户栏 */}
+ <div className="emerald-user-bar">
+ <div className="emerald-user-avatar" onClick={() => navigate('/user')}>
+ {userInfo.avatar_url ? (
+ <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />
+ ) : (
+ <AccountCircleIcon style={{ fontSize: 38, color: 'white' }} />
+ )}
+ </div>
+ <div className="emerald-brand-section">
+ <div className="emerald-brand-icon">⚡</div>
+ <div className="emerald-user-label">NeuraFlux</div>
+ </div>
+ <div className="emerald-user-stats">
+ <span className="emerald-stat-item">
+ 魔力值: <span className="emerald-stat-value">{userPT.magic}</span>
+ </span>
+ <span className="emerald-stat-item">
+ 分享率: <span className="emerald-stat-value">{userPT.ratio}</span>
+ </span>
+ <span className="emerald-stat-item">
+ 上传: <span className="emerald-stat-value">{userPT.upload}GB</span>
+ </span>
+ <span className="emerald-stat-item">
+ 下载: <span className="emerald-stat-value">{userPT.download}GB</span>
+ </span>
+ </div>
</div>
- <div className="form-row">
- <label htmlFor="title">标题</label>
- <input
- type="text"
- id="title"
- name="title"
- value={formData.title}
- onChange={handleChange}
- required
- />
- </div>
+ {/* NeuraFlux导航栏 */}
+ <nav className="emerald-nav-bar">
+ {navItems.map((item) => (
+ <div
+ key={item.label}
+ className={`emerald-nav-item ${item.label === "发布" ? "active" : ""}`}
+ data-type={item.type}
+ onClick={() => navigate(item.path)}
+ >
+ {item.icon}
+ <span className="emerald-nav-label">{item.label}</span>
+ </div>
+ ))}
+ </nav>
- <div className="form-row">
- <label htmlFor="subtitle">种子简介</label>
- <input
- type="text"
- id="subtitle"
- name="subtitle"
- value={formData.subtitle}
- onChange={handleChange}
- />
- </div>
+ {/* 发布页面内容 */}
+ <div className="emerald-content-section">
+ <h1 className="emerald-page-title">
+ <PublishIcon style={{ marginRight: '15px', fontSize: '36px', color: '#2d5016' }} />
+ 🌟 NeuraFlux种子发布
+ </h1>
+ <p style={{ textAlign: 'center', color: '#2d5016', fontSize: '18px', marginBottom: '40px', fontStyle: 'italic' }}>
+ 分享优质资源,成就精彩社区 • 每一次上传都是对知识传承的贡献
+ </p>
- <div className="form-row submit-row">
- <button type="submit" className="submit-button">提交</button>
+ {/* 发布表单 */}
+ <div className="publish-form-container">
+ <form onSubmit={handleSubmit} className="publish-form-advanced">
+ {/* 资源类型选择 */}
+ <div className="form-section">
+ <div className="section-header">
+ <CategoryIcon style={{ color: '#2d5016', marginRight: '10px' }} />
+ <h3>资源分类</h3>
+ </div>
+ <div className="form-grid">
+ <div className="form-field">
+ <label htmlFor="type">主要类型</label>
+ <div className="select-wrapper">
+ <select
+ name="type"
+ id="type"
+ value={formData.type}
+ onChange={e => { handleChange(e); setSubType(''); }}
+ required
+ className="modern-select"
+ >
+ <option value="">请选择资源类型</option>
+ <option value="电影">🎬 电影</option>
+ <option value="剧集">📺 剧集</option>
+ <option value="音乐">🎵 音乐</option>
+ <option value="动漫">🎨 动漫</option>
+ <option value="游戏">🎮 游戏</option>
+ <option value="体育">⚽ 体育</option>
+ <option value="资料">📚 资料</option>
+ </select>
+ </div>
+ </div>
+
+ {formData.type && typeOptions[formData.type] && (
+ <div className="form-field">
+ <label htmlFor="subtype">具体分类</label>
+ <div className="select-wrapper">
+ <select
+ name="subtype"
+ id="subtype"
+ value={subType}
+ onChange={e => setSubType(e.target.value)}
+ required
+ className="modern-select"
+ >
+ <option value="">请选择具体分类</option>
+ {typeOptions[formData.type].map(opt => (
+ <option key={opt} value={opt}>{opt}</option>
+ ))}
+ </select>
+ </div>
+ </div>
+ )}
+ </div>
+ </div>
+
+ {/* 种子文件上传 */}
+ <div className="form-section">
+ <div className="section-header">
+ <CloudUploadIcon style={{ color: '#2d5016', marginRight: '10px' }} />
+ <h3>种子文件</h3>
+ </div> <div
+ className={`file-upload-area ${isDragOver ? 'drag-over' : ''}`}
+ onDragEnter={handleDragEnter}
+ onDragLeave={handleDragLeave}
+ onDragOver={handleDragOver}
+ onDrop={handleDrop}
+ >
+ <input
+ type="file"
+ id="torrentFile"
+ name="torrentFile"
+ onChange={handleFileChange}
+ required
+ accept=".torrent"
+ className="file-input-hidden"
+ />
+ <label htmlFor="torrentFile" className="file-upload-label">
+ <CloudUploadIcon
+ style={{
+ fontSize: '48px',
+ color: isDragOver ? '#2d5016' : '#90ee90',
+ marginBottom: '10px',
+ transition: 'all 0.3s ease'
+ }}
+ />
+ <div className="upload-text">
+ <span className="upload-main-text">
+ {isDragOver ?
+ '松开鼠标完成上传' :
+ (formData.torrentFile ? formData.torrentFile.name : '点击选择或拖拽.torrent文件')
+ }
+ </span>
+ <span className="upload-sub-text">
+ {isDragOver ?
+ '🎯 即将上传文件到NeuraFlux' :
+ '✨ 支持拖拽上传 • 仅接受.torrent格式文件'
+ }
+ </span>
+ </div>
+ </label>
+ </div>
+ </div>
+
+ {/* 标题和描述 */}
+ <div className="form-section">
+ <div className="section-header">
+ <TitleIcon style={{ color: '#2d5016', marginRight: '10px' }} />
+ <h3>资源信息</h3>
+ </div>
+ <div className="form-field">
+ <label htmlFor="title">资源标题</label>
+ <input
+ type="text"
+ id="title"
+ name="title"
+ value={formData.title}
+ onChange={handleChange}
+ required
+ className="modern-input"
+ placeholder="请输入简洁明确的资源标题..."
+ />
+ </div>
+
+ <div className="form-field">
+ <label htmlFor="subtitle">
+ <DescriptionIcon style={{ marginRight: '5px', fontSize: '18px' }} />
+ 资源简介
+ </label>
+ <textarea
+ id="subtitle"
+ name="subtitle"
+ value={formData.subtitle}
+ onChange={handleChange}
+ className="modern-textarea"
+ rows="4"
+ placeholder="详细描述资源内容、质量、特色等信息..."
+ />
+ </div>
+ </div>
+
+ {/* 上传进度条 */}
+ {isUploading && (
+ <div className="upload-progress-section">
+ <div className="progress-header">
+ <span>正在上传资源...</span>
+ <span>{Math.round(uploadProgress)}%</span>
+ </div>
+ <div className="progress-bar">
+ <div
+ className="progress-fill"
+ style={{ width: `${uploadProgress}%` }}
+ />
+ </div>
+ </div>
+ )}
+
+ {/* 提交按钮 */}
+ <div className="form-submit-section">
+ <button
+ type="submit"
+ className="publish-submit-btn"
+ disabled={isUploading}
+ >
+ {isUploading ? (
+ <>
+ <div className="loading-spinner" />
+ 上传中...
+ </>
+ ) : (
+ <>
+ <CheckCircleIcon style={{ marginRight: '8px', fontSize: '20px' }} />
+ 发布资源
+ </>
+ )}
+ </button>
+ <div className="submit-tips">
+ 💡 请确保上传的资源合法合规,感谢您的贡献!
+ </div>
+ </div>
+ </form>
+ </div>
</div>
- </form>
+ </div>
</div>
);
};