blob: d9f7db95a92222652db7530116c0b5d6fbbd8912 [file] [log] [blame]
wht30587822025-06-09 23:33:09 +08001import React, { useState, useEffect } from 'react';
2import { useNavigate } from 'react-router-dom';
3import './SharedStyles.css';
223011330f9623f2025-06-06 00:22:05 +08004import { API_BASE_URL } from "./config";
wht30587822025-06-09 23:33:09 +08005import HomeIcon from "@mui/icons-material/Home";
6import MovieIcon from "@mui/icons-material/Movie";
7import TvIcon from "@mui/icons-material/Tv";
8import MusicNoteIcon from "@mui/icons-material/MusicNote";
9import AnimationIcon from "@mui/icons-material/Animation";
10import SportsEsportsIcon from "@mui/icons-material/SportsEsports";
11import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";
12import PersonIcon from "@mui/icons-material/Person";
13import AccountCircleIcon from "@mui/icons-material/AccountCircle";
14import ForumIcon from "@mui/icons-material/Forum";
15import HelpIcon from "@mui/icons-material/Help";
16import CloudUploadIcon from "@mui/icons-material/CloudUpload";
17import PublishIcon from "@mui/icons-material/Publish";
18import CategoryIcon from "@mui/icons-material/Category";
19import TitleIcon from "@mui/icons-material/Title";
20import DescriptionIcon from "@mui/icons-material/Description";
21import CheckCircleIcon from "@mui/icons-material/CheckCircle";
22
23const navItems = [
24 { label: "首页", icon: <HomeIcon className="emerald-nav-icon" />, path: "/home", type: "home" },
25 { label: "电影", icon: <MovieIcon className="emerald-nav-icon" />, path: "/movie", type: "movie" },
26 { label: "剧集", icon: <TvIcon className="emerald-nav-icon" />, path: "/tv", type: "tv" },
27 { label: "音乐", icon: <MusicNoteIcon className="emerald-nav-icon" />, path: "/music", type: "music" },
28 { label: "动漫", icon: <AnimationIcon className="emerald-nav-icon" />, path: "/anime", type: "anime" },
29 { label: "游戏", icon: <SportsEsportsIcon className="emerald-nav-icon" />, path: "/game", type: "game" },
30 { label: "体育", icon: <SportsMartialArtsIcon className="emerald-nav-icon" />, path: "/sport", type: "sport" },
31 { label: "资料", icon: <PersonIcon className="emerald-nav-icon" />, path: "/info", type: "info" },
32 { label: "论坛", icon: <ForumIcon className="emerald-nav-icon" />, path: "/forum", type: "forum" },
33 { label: "发布", icon: <AccountCircleIcon className="emerald-nav-icon" />, path: "/publish", type: "publish" },
34 { label: "求种", icon: <HelpIcon className="emerald-nav-icon" />, path: "/begseed", type: "help" },
35];
36
37// 发布页面专用文字雨内容
38const publishTexts = [
39 "分享", "上传", "种子", "资源", "贡献", "精品", "原创", "高清",
40 "无损", "蓝光", "1080P", "4K", "HDR", "珍藏", "首发", "独家"
41];
9563036699e95ae32025-06-02 21:42:11 +080042
43const PublishPage = () => {
wht30587822025-06-09 23:33:09 +080044 const navigate = useNavigate();
9563036699e95ae32025-06-02 21:42:11 +080045 const [formData, setFormData] = useState({
46 type: '',
47 torrentFile: '',
48 title: '',
49 subtitle: ''
50 });
rhjc6a4ee02025-06-06 00:45:18 +080051 const [subType, setSubType] = useState('');
wht30587822025-06-09 23:33:09 +080052 const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' }); const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });
53 const [uploadProgress, setUploadProgress] = useState(0);
54 const [isUploading, setIsUploading] = useState(false);
55 const [isDragOver, setIsDragOver] = useState(false);
56
57 useEffect(() => {
58 // 获取用户信息
59 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
60 const userId = match ? match[2] : null;
61 if (userId) {
62 fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)
63 .then(res => res.json())
64 .then(data => {
65 setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });
66 setUserPT({
67 magic: data.magic_value || data.magic || 0,
68 ratio: data.share_ratio || data.share || 0,
69 upload: data.upload_amount || data.upload || 0,
70 download: data.download_amount || data.download || 0,
71 });
72 })
73 .catch(err => console.error('Fetching user profile failed', err));
74 }
75 }, []);
rhjc6a4ee02025-06-06 00:45:18 +080076
77 const typeOptions = {
78 '电影': ['大陆', '港台', '欧美', '日韩'],
79 '剧集': ['国产电视剧', '港剧', '欧美剧', '日韩剧'],
80 '音乐': ['古典音乐', '流行音乐', '摇滚', '电子音乐', '说唱'],
81 '动漫': ['国创', '日漫', '欧美动漫', '韩漫'],
82 '游戏': ['PC', '主机', '移动', '掌机', '视频'],
83 '体育': ['篮球', '足球', '羽毛球', '排球', '电竞'],
84 '资料': ['出版物', '学习教程', '素材模板', '演讲交流', '日常娱乐'],
85 };
9563036699e95ae32025-06-02 21:42:11 +080086
87 const handleChange = (e) => {
88 const { name, value } = e.target;
89 setFormData({ ...formData, [name]: value });
90 };
9563036699e95ae32025-06-02 21:42:11 +080091 const handleFileChange = (e) => {
92 const file = e.target.files[0];
wht30587822025-06-09 23:33:09 +080093 validateAndSetFile(file);
94 };
95
96 const validateAndSetFile = (file) => {
97 if (file && file.name.split('.').pop().toLowerCase() !== 'torrent') {
9563036699e95ae32025-06-02 21:42:11 +080098 alert('仅能上传.torrent类型文件');
wht30587822025-06-09 23:33:09 +080099 return false;
100 } else if (file) {
9563036699e95ae32025-06-02 21:42:11 +0800101 setFormData({ ...formData, torrentFile: file });
wht30587822025-06-09 23:33:09 +0800102 return true;
103 }
104 return false;
105 };
106
107 // 拖拽上传功能
108 const handleDragEnter = (e) => {
109 e.preventDefault();
110 e.stopPropagation();
111 setIsDragOver(true);
112 };
113
114 const handleDragLeave = (e) => {
115 e.preventDefault();
116 e.stopPropagation();
117 // 只有当鼠标真正离开拖拽区域时才设置为false
118 if (!e.currentTarget.contains(e.relatedTarget)) {
119 setIsDragOver(false);
9563036699e95ae32025-06-02 21:42:11 +0800120 }
121 };
122
wht30587822025-06-09 23:33:09 +0800123 const handleDragOver = (e) => {
124 e.preventDefault();
125 e.stopPropagation();
126 };
127
128 const handleDrop = (e) => {
129 e.preventDefault();
130 e.stopPropagation();
131 setIsDragOver(false);
132
133 const files = e.dataTransfer.files;
134 if (files.length > 0) {
135 const file = files[0];
136 if (validateAndSetFile(file)) {
137 // 如果文件验证成功,清空文件输入框并重新设置
138 const fileInput = document.getElementById('torrentFile');
139 if (fileInput) {
140 // 创建一个新的FileList对象来模拟文件选择
141 const dt = new DataTransfer();
142 dt.items.add(file);
143 fileInput.files = dt.files;
144 }
145 }
146 }
147 };
223011330f9623f2025-06-06 00:22:05 +0800148 const handleSubmit = async (e) => {
9563036699e95ae32025-06-02 21:42:11 +0800149 e.preventDefault();
wht338fc032025-06-09 17:16:22 +0800150 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
151 const userId = match ? match[2] : null;
152
rhjc6a4ee02025-06-06 00:45:18 +0800153 if (!formData.torrentFile) {
154 alert('请上传.torrent文件');
155 return;
156 }
wht30587822025-06-09 23:33:09 +0800157
158 setIsUploading(true);
159 setUploadProgress(0);
160
161 // 模拟上传进度
162 const progressInterval = setInterval(() => {
163 setUploadProgress(prev => {
164 if (prev >= 90) {
165 clearInterval(progressInterval);
166 return prev;
167 }
168 return prev + Math.random() * 15;
169 });
170 }, 200);
171
rhjc6a4ee02025-06-06 00:45:18 +0800172 const data = new FormData();
wht338fc032025-06-09 17:16:22 +0800173 data.append('userid', userId);
rhjc6a4ee02025-06-06 00:45:18 +0800174 data.append('title', formData.title);
175 data.append('tag', subType);
176 data.append('file', formData.torrentFile);
177 data.append('subtitle', formData.subtitle);
rhjc6a4ee02025-06-06 00:45:18 +0800178
179 try {
223011330f9623f2025-06-06 00:22:05 +0800180 const response = await fetch(`${API_BASE_URL}/api/save-torrent`, {
rhjc6a4ee02025-06-06 00:45:18 +0800181 method: 'POST',
182 body: data,
183 });
wht30587822025-06-09 23:33:09 +0800184
185 clearInterval(progressInterval);
186 setUploadProgress(100);
187
rhjc6a4ee02025-06-06 00:45:18 +0800188 if (response.ok) {
wht30587822025-06-09 23:33:09 +0800189 setTimeout(() => {
190 alert('上传成功!');
191 setIsUploading(false);
192 setUploadProgress(0);
193 // 重置表单
194 setFormData({
195 type: '',
196 torrentFile: '',
197 title: '',
198 subtitle: ''
199 });
200 setSubType('');
201 }, 500);
rhjc6a4ee02025-06-06 00:45:18 +0800202 } else {
wht30587822025-06-09 23:33:09 +0800203 setIsUploading(false);
204 setUploadProgress(0);
rhjc6a4ee02025-06-06 00:45:18 +0800205 alert('上传失败');
206 }
207 } catch (err) {
wht30587822025-06-09 23:33:09 +0800208 clearInterval(progressInterval);
209 setIsUploading(false);
210 setUploadProgress(0);
rhjc6a4ee02025-06-06 00:45:18 +0800211 alert('网络错误');
212 }
9563036699e95ae32025-06-02 21:42:11 +0800213 };
9563036699e95ae32025-06-02 21:42:11 +0800214 return (
wht30587822025-06-09 23:33:09 +0800215 <div className="emerald-home-container">
216 {/* 发布页面专用文字雨 */}
217 <div className="publish-text-rain">
218 {publishTexts.map((text, index) => (
219 <div key={index} className="text-drop" style={{
220 left: `${(index * 4) % 100}%`,
221 animationDelay: `${(index * 0.9) % 12}s`,
222 animationDuration: `${7 + (index % 6)}s`,
223 color: index % 3 === 0 ? '#90ee90' : index % 3 === 1 ? '#2d5016' : '#4a7c59'
224 }}>
225 {text}
rhjc6a4ee02025-06-06 00:45:18 +0800226 </div>
wht30587822025-06-09 23:33:09 +0800227 ))}
228 </div>
9563036699e95ae32025-06-02 21:42:11 +0800229
wht30587822025-06-09 23:33:09 +0800230 {/* 浮动园林装饰元素 */}
231 <div className="floating-garden-elements">
232 <div className="garden-element">📤</div>
233 <div className="garden-element">🌟</div>
234 <div className="garden-element">💎</div>
235 <div className="garden-element">🎯</div>
236 </div>
237
238 <div className="emerald-content">
239 {/* NeuraFlux用户栏 */}
240 <div className="emerald-user-bar">
241 <div className="emerald-user-avatar" onClick={() => navigate('/user')}>
242 {userInfo.avatar_url ? (
243 <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />
244 ) : (
245 <AccountCircleIcon style={{ fontSize: 38, color: 'white' }} />
246 )}
247 </div>
248 <div className="emerald-brand-section">
249 <div className="emerald-brand-icon">⚡</div>
250 <div className="emerald-user-label">NeuraFlux</div>
251 </div>
252 <div className="emerald-user-stats">
253 <span className="emerald-stat-item">
254 魔力值: <span className="emerald-stat-value">{userPT.magic}</span>
255 </span>
256 <span className="emerald-stat-item">
257 分享率: <span className="emerald-stat-value">{userPT.ratio}</span>
258 </span>
259 <span className="emerald-stat-item">
260 上传: <span className="emerald-stat-value">{userPT.upload}GB</span>
261 </span>
262 <span className="emerald-stat-item">
263 下载: <span className="emerald-stat-value">{userPT.download}GB</span>
264 </span>
265 </div>
9563036699e95ae32025-06-02 21:42:11 +0800266 </div>
267
wht30587822025-06-09 23:33:09 +0800268 {/* NeuraFlux导航栏 */}
269 <nav className="emerald-nav-bar">
270 {navItems.map((item) => (
271 <div
272 key={item.label}
273 className={`emerald-nav-item ${item.label === "发布" ? "active" : ""}`}
274 data-type={item.type}
275 onClick={() => navigate(item.path)}
276 >
277 {item.icon}
278 <span className="emerald-nav-label">{item.label}</span>
279 </div>
280 ))}
281 </nav>
9563036699e95ae32025-06-02 21:42:11 +0800282
wht30587822025-06-09 23:33:09 +0800283 {/* 发布页面内容 */}
284 <div className="emerald-content-section">
285 <h1 className="emerald-page-title">
286 <PublishIcon style={{ marginRight: '15px', fontSize: '36px', color: '#2d5016' }} />
287 🌟 NeuraFlux种子发布
288 </h1>
289 <p style={{ textAlign: 'center', color: '#2d5016', fontSize: '18px', marginBottom: '40px', fontStyle: 'italic' }}>
290 分享优质资源,成就精彩社区 每一次上传都是对知识传承的贡献
291 </p>
9563036699e95ae32025-06-02 21:42:11 +0800292
wht30587822025-06-09 23:33:09 +0800293 {/* 发布表单 */}
294 <div className="publish-form-container">
295 <form onSubmit={handleSubmit} className="publish-form-advanced">
296 {/* 资源类型选择 */}
297 <div className="form-section">
298 <div className="section-header">
299 <CategoryIcon style={{ color: '#2d5016', marginRight: '10px' }} />
300 <h3>资源分类</h3>
301 </div>
302 <div className="form-grid">
303 <div className="form-field">
304 <label htmlFor="type">主要类型</label>
305 <div className="select-wrapper">
306 <select
307 name="type"
308 id="type"
309 value={formData.type}
310 onChange={e => { handleChange(e); setSubType(''); }}
311 required
312 className="modern-select"
313 >
314 <option value="">请选择资源类型</option>
315 <option value="电影">🎬 电影</option>
316 <option value="剧集">📺 剧集</option>
317 <option value="音乐">🎵 音乐</option>
318 <option value="动漫">🎨 动漫</option>
319 <option value="游戏">🎮 游戏</option>
320 <option value="体育">⚽ 体育</option>
321 <option value="资料">📚 资料</option>
322 </select>
323 </div>
324 </div>
325
326 {formData.type && typeOptions[formData.type] && (
327 <div className="form-field">
328 <label htmlFor="subtype">具体分类</label>
329 <div className="select-wrapper">
330 <select
331 name="subtype"
332 id="subtype"
333 value={subType}
334 onChange={e => setSubType(e.target.value)}
335 required
336 className="modern-select"
337 >
338 <option value="">请选择具体分类</option>
339 {typeOptions[formData.type].map(opt => (
340 <option key={opt} value={opt}>{opt}</option>
341 ))}
342 </select>
343 </div>
344 </div>
345 )}
346 </div>
347 </div>
348
349 {/* 种子文件上传 */}
350 <div className="form-section">
351 <div className="section-header">
352 <CloudUploadIcon style={{ color: '#2d5016', marginRight: '10px' }} />
353 <h3>种子文件</h3>
354 </div> <div
355 className={`file-upload-area ${isDragOver ? 'drag-over' : ''}`}
356 onDragEnter={handleDragEnter}
357 onDragLeave={handleDragLeave}
358 onDragOver={handleDragOver}
359 onDrop={handleDrop}
360 >
361 <input
362 type="file"
363 id="torrentFile"
364 name="torrentFile"
365 onChange={handleFileChange}
366 required
367 accept=".torrent"
368 className="file-input-hidden"
369 />
370 <label htmlFor="torrentFile" className="file-upload-label">
371 <CloudUploadIcon
372 style={{
373 fontSize: '48px',
374 color: isDragOver ? '#2d5016' : '#90ee90',
375 marginBottom: '10px',
376 transition: 'all 0.3s ease'
377 }}
378 />
379 <div className="upload-text">
380 <span className="upload-main-text">
381 {isDragOver ?
382 '松开鼠标完成上传' :
383 (formData.torrentFile ? formData.torrentFile.name : '点击选择或拖拽.torrent文件')
384 }
385 </span>
386 <span className="upload-sub-text">
387 {isDragOver ?
388 '🎯 即将上传文件到NeuraFlux' :
389 '✨ 支持拖拽上传 • 仅接受.torrent格式文件'
390 }
391 </span>
392 </div>
393 </label>
394 </div>
395 </div>
396
397 {/* 标题和描述 */}
398 <div className="form-section">
399 <div className="section-header">
400 <TitleIcon style={{ color: '#2d5016', marginRight: '10px' }} />
401 <h3>资源信息</h3>
402 </div>
403 <div className="form-field">
404 <label htmlFor="title">资源标题</label>
405 <input
406 type="text"
407 id="title"
408 name="title"
409 value={formData.title}
410 onChange={handleChange}
411 required
412 className="modern-input"
413 placeholder="请输入简洁明确的资源标题..."
414 />
415 </div>
416
417 <div className="form-field">
418 <label htmlFor="subtitle">
419 <DescriptionIcon style={{ marginRight: '5px', fontSize: '18px' }} />
420 资源简介
421 </label>
422 <textarea
423 id="subtitle"
424 name="subtitle"
425 value={formData.subtitle}
426 onChange={handleChange}
427 className="modern-textarea"
428 rows="4"
429 placeholder="详细描述资源内容、质量、特色等信息..."
430 />
431 </div>
432 </div>
433
434 {/* 上传进度条 */}
435 {isUploading && (
436 <div className="upload-progress-section">
437 <div className="progress-header">
438 <span>正在上传资源...</span>
439 <span>{Math.round(uploadProgress)}%</span>
440 </div>
441 <div className="progress-bar">
442 <div
443 className="progress-fill"
444 style={{ width: `${uploadProgress}%` }}
445 />
446 </div>
447 </div>
448 )}
449
450 {/* 提交按钮 */}
451 <div className="form-submit-section">
452 <button
453 type="submit"
454 className="publish-submit-btn"
455 disabled={isUploading}
456 >
457 {isUploading ? (
458 <>
459 <div className="loading-spinner" />
460 上传中...
461 </>
462 ) : (
463 <>
464 <CheckCircleIcon style={{ marginRight: '8px', fontSize: '20px' }} />
465 发布资源
466 </>
467 )}
468 </button>
469 <div className="submit-tips">
470 💡 请确保上传的资源合法合规,感谢您的贡献!
471 </div>
472 </div>
473 </form>
474 </div>
9563036699e95ae32025-06-02 21:42:11 +0800475 </div>
wht30587822025-06-09 23:33:09 +0800476 </div>
9563036699e95ae32025-06-02 21:42:11 +0800477 </div>
478 );
479};
480
481export default PublishPage;