blob: 941210cb53acddfaa8d1f136886722b1103d299c [file] [log] [blame]
Akane121765b61a72025-05-17 13:52:25 +08001import React, { useEffect, useState } from 'react';
2import { useNavigate, useLocation,useParams } from 'react-router-dom';
3// import { getUserInfo } from '../api'; // 保留import但注释掉
4import { createTorrent, getTorrents} from '../api/torrent';
5import './Dashboard.css';
6import { createPost, getPosts } from '../api/helpPost';
7
8
9const Dashboard = ({ onLogout }) => {
10 const location = useLocation();
11 const [userInfo, setUserInfo] = useState(null);
12 const [loading, setLoading] = useState(false);
13 const [error, setError] = useState('');
14 const [currentSlide, setCurrentSlide] = useState(0);
15 const navigate = useNavigate();
16 const {tab} = useParams();
17 const [showUploadModal, setShowUploadModal] = useState(false);
18 const [isUploading, setIsUploading] = useState(false);
19 const [uploadData, setUploadData] = useState({
20 name: '',
21 type: '',
22 region: '',
23 subtitle: '',
24 resolution: '', // 新增分辨率字段
25 file: null,
26 description: ''
27 });
28 const [showPostModal, setShowPostModal] = useState(false);
29 const [postTitle, setPostTitle] = useState('');
30 const [postContent, setPostContent] = useState('');
31 const [selectedImage, setSelectedImage] = useState(null);
32 const [helpPosts, setHelpPosts] = useState([]);
33 const [helpLoading, setHelpLoading] = useState(false);
34 const [helpError, setHelpError] = useState(null);
35 const [currentPage, setCurrentPage] = useState(1);
36 const [totalPages, setTotalPages] = useState(1);
37
38 // 添加状态
39 const [torrentPosts, setTorrentPosts] = useState([]);
40 const [torrentLoading, setTorrentLoading] = useState(false);
41 const [torrentError, setTorrentError] = useState(null);
42
43 const [filteredResources, setFilteredResources] = useState(torrentPosts);
44
45 const activeTab = tab || 'announcement'; // 如果没有tab参数,则默认为announcement
46 // 从location.state中初始化状态
47
48
49 const handleTabChange = (tabName) => {
50 navigate(`/dashboard/${tabName}`, {
51 state: {
52 savedFilters: selectedFilters, // 使用新的筛选状态 // 保留现有状态
53 activeTab: tabName // 可选,如果其他组件依赖这个 state
54 }
55 });
56 };
57
58 //公告区
59 const [announcements] = useState([
60 {
61 id: 1,
62 title: '系统维护与更新',
63 content: '2023-10-15 02:00-06:00将进行系统维护升级,期间网站将无法访问。本次更新包含:\n1. 数据库服务器迁移\n2. 安全补丁更新\n3. CDN节点优化\n\n请提前做好下载安排。',
64 author: '系统管理员',
65 date: '2023-10-10',
66 excerpt: '2023-10-15 02:00-06:00将进行系统维护,期间无法访问',
67 category: '系统'
68 },
69 {
70 id: 2,
71 title: '资源上新',
72 content: '最新热门电影《奥本海默》4K REMUX资源已上线,包含:\n- 4K HDR版本 (56.8GB)\n- 1080P标准版 (12.3GB)\n- 中英双语字幕\n\n欢迎下载保种!',
73 author: '资源组',
74 date: '2023-10-08',
75 excerpt: '最新热门电影《奥本海默》4K资源已上线',
76 category: '资源'
77 },
78 {
79 id: 3,
80 title: '积分规则调整',
81 content: '自11月1日起,上传资源积分奖励提升20%,具体规则如下:\n- 上传电影资源:每GB 10积分\n- 上传电视剧资源:每GB 8积分\n- 上传动漫资源:每GB 6积分\n\n感谢大家的支持与贡献!',
82 author: '管理员',
83 date: '2023-10-05',
84 excerpt: '自11月1日起,上传资源积分奖励提升20%',
85 category: '公告'
86 },
87 {
88 id: 4,
89 title: '违规处理公告',
90 content: '用户user123因发布虚假资源已被封禁,相关资源已删除。请大家遵守社区规则,维护良好的分享环境。',
91 author: '管理员',
92 date: '2023-10-03',
93 excerpt: '用户user123因发布虚假资源已被封禁',
94 category: '违规'
95 },
96 {
97 id: 5,
98 title: '节日活动',
99 content: '国庆期间所有资源下载积分减半,活动时间:2023年10月1日至2023年10月7日。',
100 author: '活动组',
101 date: '2023-09-30',
102 excerpt: '国庆期间所有资源下载积分减半',
103 category: '活动'
104 },
105 {
106 id: 6,
107 title: '客户端更新',
108 content: 'PT客户端v2.5.0已发布,修复多个BUG,新增资源搜索功能。请尽快更新到最新版本以获得更好的使用体验。',
109 author: '开发组',
110 date: '2023-09-28',
111 excerpt: 'PT客户端v2.5.0已发布,修复多个BUG',
112 category: '更新'
113 },
114 // 其他公告...
115 ]);
116
117
118 const handleAnnouncementClick = (announcement,e) => {
119 if (!e.target.closest('.exclude-click')) {
120 navigate(`/announcement/${announcement.id}`, {
121 state: {
122 announcement,
123 returnPath: `/dashboard/${activeTab}`,
124 scrollPosition: window.scrollY,
125 activeTab
126 }
127 });
128 }
129 };
130
131
132 //资源区
133
134
135 const handleFileChange = (e) => {
136 setUploadData({...uploadData, file: e.target.files[0]});
137 };
138
139 const handleUploadSubmit = async (e) => {
140 e.preventDefault();
141
142 try {
143 setIsUploading(true);
144 const username = localStorage.getItem('username');
145
146 // 创建种子数据
147 const torrentData = {
148 torrentName: uploadData.name,
149 category: uploadData.type,
150 regin: uploadData.region,
151 resolution: uploadData.resolution,
152 subtitle: uploadData.subtitle,
153 description: uploadData.description,
154 username: username,
155 filePath: "D:\\大学\\大三_下\\实训BT服务器下载\\G2-ptPlatform-backend\\torrents\\[电影天堂www.dytt89.com]两杆大烟枪BD中英双字.mp4.torrent" // 默认路径
156 };
157
158 // 调用API创建种子
159 await createTorrent(
160 torrentData.torrentName,
161 torrentData.description,
162 torrentData.username,
163 torrentData.category,
164 torrentData.regin,
165 torrentData.resolution,
166 torrentData.subtitle,
167 torrentData.filePath
168 );
169
170 // 上传成功处理
171 setShowUploadModal(false);
172 setUploadData({
173 name: '',
174 type: '',
175 region: '',
176 subtitle: '',
177 resolution: '',
178 file: null,
179 description: ''
180 });
181 alert('种子创建成功!');
182
183 // 刷新列表
184 fetchTorrentPosts(currentPage);
185
186 } catch (error) {
187 console.error('创建失败:', error);
188 alert('创建失败: ' + (error.response?.data?.message || error.message));
189 } finally {
190 setIsUploading(false);
191 }
192 };
193
194 const handlePostSubmit = async (e) => {
195 e.preventDefault();
196 try {
197 const username = localStorage.getItem('username');
198 const response = await createPost(
199 postTitle,
200 postContent,
201 username
202 );
203
204 if (response.data.code === 200) {
205 // 刷新帖子列表
206 await fetchHelpPosts();
207 // 重置表单
208 setShowPostModal(false);
209 setPostTitle('');
210 setPostContent('');
211 setSelectedImage(null);
212 } else {
213 setHelpError(response.data.message || '发帖失败');
214 }
215 } catch (err) {
216 setHelpError(err.message || '发帖失败');
217 }
218 };
219
220 // 获取Torrent帖子列表
221 const fetchTorrentPosts = async (page = 1) => {
222 setTorrentLoading(true);
223 try {
224 const response = await getTorrents(page);
225 setTorrentPosts(response.data.data.records);
226 setTotalPages(Math.ceil(response.data.data.total / 5)); // 假设每页5条
227 setCurrentPage(page);
228 } catch (err) {
229 setTorrentError(err.message);
230 } finally {
231 setTorrentLoading(false);
232 }
233};
234
235 // 在useEffect中调用
236 useEffect(() => {
237 if (activeTab === 'share') {
238 fetchTorrentPosts();
239 }
240 }, [activeTab]);
241
242 const handleImageUpload = (e) => {
243 setSelectedImage(e.target.files[0]);
244 };
245
246
247
248
249 const fetchHelpPosts = async (page = 1) => {
250 setHelpLoading(true);
251 try {
252 const response = await getPosts(page);
253 if (response.data.code === 200) {
254 setHelpPosts(response.data.data.records);
255 setTotalPages(Math.ceil(response.data.data.total / 5)); // 假设每页5条
256 setCurrentPage(page);
257 } else {
258 setHelpError(response.data.message || '获取求助帖失败');
259 }
260 } catch (err) {
261 setHelpError(err.message || '获取求助帖失败');
262 } finally {
263 setHelpLoading(false);
264 }
265 };
266 useEffect(() => {
267 if (activeTab === 'help') {
268 fetchHelpPosts(currentPage);
269 }
270 }, [activeTab, currentPage]); // 添加 currentPage 作为依赖
271
272
273
274
275 // 分类维度配置
276 const filterCategories = {
277 type: {
278 label: '类型',
279 options: {
280 'all': '全部',
281 '电影': '电影',
282 '电视剧': '电视剧',
283 '动漫': '动漫',
284 '综艺': '综艺'
285 }
286 },
287 subtitle: {
288 label: '字幕',
289 options: {
290 'all': '全部',
291 'yes': '有字幕',
292 'no': '无字幕'
293 }
294 },
295 region: {
296 label: '地区',
297 options: {
298 'all': '全部',
299 'cn': '大陆',
300 'us': '欧美',
301 'jp': '日本'
302 }
303 }
304 };
305
306 const [selectedFilters, setSelectedFilters] = useState(
307 location.state?.savedFilters ||
308 Object.keys(filterCategories).reduce((acc, category) => {
309 acc[category] = 'all';
310 return acc;
311 }, {})
312 );
313
314
315
316// 处理筛选条件变更
317const handleFilterSelect = (category, value) => {
318 setSelectedFilters(prev => ({
319 ...prev,
320 [category]: prev[category] === value ? null : value // 点击已选中的则取消
321 }));
322};
323
324//应用筛选条件
325const applyFilters = () => {
326 const result = torrentPosts.filter(resource => {
327 return Object.entries(selectedFilters).every(([category, selectedValue]) => {
328 if (selectedValue === 'all') return true;
329 if (category === 'subtitle') {
330 return resource.subtitle === (selectedValue === 'yes');
331 }
332 return resource[category] === selectedValue;
333 });
334 });
335 setFilteredResources(result);
336};
337
338
339 // 恢复滚动位置
340 useEffect(() => {
341 if (location.state?.scrollPosition) {
342 window.scrollTo(0, location.state.scrollPosition);
343 }
344 }, [location.state]);
345
346
347 useEffect(() => {
348 const token = localStorage.getItem('token');
349 if (!token) {
350 navigate('/login');
351 return;
352 }
353
354 /* 保留但注释掉实际的用户信息获取
355 const fetchUserInfo = async () => {
356 try {
357 const response = await getUserInfo(token);
358 if (response.data.code === 200) {
359 setUserInfo(response.data.data);
360 } else {
361 setError('获取用户信息失败');
362 }
363 } catch (err) {
364 setError('获取用户信息失败');
365 } finally {
366 setLoading(false);
367 }
368 };
369
370 fetchUserInfo();
371 */
372
373 // 模拟用户信息
374 setUserInfo({
375 name: localStorage.getItem('username') || '演示用户', // 确保这里读取的是最新值
376 avatar: 'https://via.placeholder.com/40'
377 });
378 setLoading(false);
379 }, [navigate]);
380
381 // 轮播图自动切换效果
382 useEffect(() => {
383 if (activeTab === 'announcement') {
384 const timer = setInterval(() => {
385 setCurrentSlide(prev => (prev + 1) % 3); // 3张轮播图循环
386 }, 3000);
387 return () => clearInterval(timer);
388 }
389 }, [activeTab]);
390
391 useEffect(() => {
392 if (activeTab === 'help') {
393 fetchHelpPosts();
394 }
395 }, [activeTab]);
396
397 const renderContent = () => {
398 switch(activeTab) {
399 case 'announcement':
400 return (
401 <div className="content-area" data-testid="announcement-section">
402 {/* 轮播图区域 */}
403 <div className="carousel-container">
404 <div className={`carousel-slide ${currentSlide === 0 ? 'active' : ''}`}>
405 <div className="carousel-image gray-bg">促销活动1</div>
406 </div>
407 <div className={`carousel-slide ${currentSlide === 1 ? 'active' : ''}`}>
408 <div className="carousel-image gray-bg">促销活动2</div>
409 </div>
410 <div className={`carousel-slide ${currentSlide === 2 ? 'active' : ''}`}>
411 <div className="carousel-image gray-bg">促销活动3</div>
412 </div>
413 <div className="carousel-dots">
414 <span className={`dot ${currentSlide === 0 ? 'active' : ''}`}></span>
415 <span className={`dot ${currentSlide === 1 ? 'active' : ''}`}></span>
416 <span className={`dot ${currentSlide === 2 ? 'active' : ''}`}></span>
417 </div>
418 </div>
419
420 {/* 公告区块区域 */}
421 <div className="announcement-grid">
422 {announcements.map(announcement => (
423 <div
424 key={announcement.id}
425 className="announcement-card"
426 onClick={(e) => handleAnnouncementClick(announcement, e)}
427 >
428 <h3>{announcement.title}</h3>
429 <p>{announcement.excerpt}</p>
430 <div className="announcement-footer exclude-click">
431 <span>{announcement.author}</span>
432 <span>{announcement.date}</span>
433 </div>
434 </div>
435 ))}
436 </div>
437 </div>
438 );
439 case 'share':
440 return (
441 <div className="content-area" data-testid="share-section">
442 {/* 上传按钮 - 添加在筛选区上方 */}
443 <div className="upload-header">
444 <button
445 className="upload-btn"
446 onClick={() => setShowUploadModal(true)}
447 >
448 上传种子
449 </button>
450 </div>
451 {/* 分类筛选区 */}
452 <div className="filter-section">
453 {Object.entries(filterCategories).map(([category, config]) => (
454 <div key={category} className="filter-group">
455 <h4>{config.label}:</h4>
456 <div className="filter-options">
457 {Object.entries(config.options).map(([value, label]) => (
458 <button
459 key={value}
460 className={`filter-btn ${
461 selectedFilters[category] === value ? 'active' : ''
462 }`}
463 onClick={() => handleFilterSelect(category, value)}
464 >
465 {label}
466 </button>
467 ))}
468 </div>
469 </div>
470 ))}
471
472 <button
473 className="confirm-btn"
474 onClick={applyFilters}
475 >
476 确认筛选
477 </button>
478 </div>
479
480 {/* 上传弹窗 */}
481 {showUploadModal && (
482 <div className="modal-overlay">
483 <div className="upload-modal">
484 <h3>上传新种子</h3>
485 <button
486 className="close-btn"
487 onClick={() => setShowUploadModal(false)}
488 >
489 ×
490 </button>
491
492 <form onSubmit={handleUploadSubmit}>
493 <div className="form-group">
494 <label>种子名称</label>
495 <input
496 type="text"
497 value={uploadData.name}
498 onChange={(e) => setUploadData({...uploadData, name: e.target.value})}
499 required
500 />
501 </div>
502
503 <div className="form-group">
504 <label>资源类型</label>
505 <select
506 value={uploadData.type}
507 onChange={(e) => setUploadData({...uploadData, type: e.target.value})}
508 required
509 >
510 <option value="">请选择</option>
511 <option value="电影">电影</option>
512 <option value="电视剧">电视剧</option>
513 <option value="动漫">动漫</option>
514 <option value="综艺">综艺</option>
515 <option value="音乐">音乐</option>
516 </select>
517 </div>
518
519 {/* 新增地区输入框 */}
520 <div className="form-group">
521 <label>地区</label>
522 <input
523 type="text"
524 value={uploadData.region || ''}
525 onChange={(e) => setUploadData({...uploadData, region: e.target.value})}
526 placeholder="例如: 美国, 中国, 日本等"
527 required
528 />
529 </div>
530
531 {/* 添加分辨率下拉框 */}
532 <div className="form-group">
533 <label>分辨率</label>
534 <select
535 value={uploadData.resolution || ''}
536 onChange={(e) => setUploadData({...uploadData, resolution: e.target.value})}
537 required
538 >
539 <option value="">请选择</option>
540 <option value="4K">4K</option>
541 <option value="2K">2K</option>
542 <option value="1080P">1080P</option>
543 <option value="720P">720P</option>
544 <option value="SD">SD</option>
545 <option value="无损音源">无损音源</option>
546 <option value="杜比全景声">杜比全景声</option>
547 <option value="其他">其他</option>
548 </select>
549 </div>
550
551
552
553 {/* 新增字幕语言下拉框 */}
554 <div className="form-group">
555 <label>字幕语言</label>
556 <select
557 value={uploadData.subtitle || ''}
558 onChange={(e) => setUploadData({...uploadData, subtitle: e.target.value})}
559 required
560 >
561 <option value="">请选择</option>
562 <option value="无需字幕">无需字幕</option>
563 <option value="暂无字幕">暂无字幕</option>
564 <option value="自带中文字幕">自带中文字幕</option>
565 <option value="自带双语字幕(含中文)">自带双语字幕(含中文)</option>
566 <option value="附件中文字幕">附件中文字幕</option>
567 <option value="附件双语字幕">附件双语字幕</option>
568 </select>
569 </div>
570
571 <div className="form-group">
572 <label>种子文件</label>
573 <input
574 type="file"
575 accept=".torrent"
576 onChange={handleFileChange}
577
578 />
579 </div>
580
581 <div className="form-group">
582 <label>简介</label>
583 <textarea
584 value={uploadData.description}
585 onChange={(e) => setUploadData({...uploadData, description: e.target.value})}
586 />
587 </div>
588
589 <div className="form-actions">
590 <button
591 type="button"
592 className="cancel-btn"
593 onClick={() => setShowUploadModal(false)}
594 >
595 取消
596 </button>
597 <button
598 type="submit"
599 className="confirm-btn"
600 disabled={isUploading}
601 >
602 {isUploading ? '上传中...' : '确认上传'}
603 </button>
604 </div>
605 </form>
606 </div>
607 </div>
608 )}
609
610 <div className="resource-list">
611 {torrentPosts.map(torrent => (
612 <div
613 key={torrent.id}
614 className="resource-item"
615 onClick={() => navigate(`/torrent/${torrent.id}`)}
616 >
617 <div className="resource-poster">
618 <div className="poster-image gray-bg">{torrent.torrentName.charAt(0)}</div>
619 </div>
620 <div className="resource-info">
621 <h3 className="resource-title">{torrent.torrentName}</h3>
622 <p className="resource-meta">
623 {torrent.resolution} | {torrent.region} | {torrent.category}
624 </p>
625 <p className="resource-subtitle">字幕: {torrent.subtitle}</p>
626 </div>
627 <div className="resource-stats">
628 <span className="stat">{torrent.size}</span>
629 <span className="stat">发布者: {torrent.username}</span>
630 </div>
631 <button
632 className="download-btn"
633 onClick={(e) => {
634 e.stopPropagation();
635 // 下载逻辑
636 }}
637 >
638 立即下载
639 </button>
640 </div>
641 ))}
642 </div>
643
644 {/* 分页控件 */}
645 <div className="pagination">
646 <button
647 onClick={() => fetchTorrentPosts(currentPage - 1)}
648 disabled={currentPage === 1}
649 >
650 上一页
651 </button>
652
653 {Array.from({ length: totalPages }, (_, i) => i + 1).map(page => (
654 <button
655 key={page}
656 onClick={() => fetchTorrentPosts(page)}
657 className={currentPage === page ? 'active' : ''}
658 >
659 {page}
660 </button>
661 ))}
662
663 <button
664 onClick={() => fetchTorrentPosts(currentPage + 1)}
665 disabled={currentPage === totalPages}
666 >
667 下一页
668 </button>
669 </div>
670 </div>
671 );
672 // 在Dashboard.jsx的renderContent函数中修改case 'request'部分
673 case 'request':
674 return (
675 <div className="content-area" data-testid="request-section">
676 {/* 求种区帖子列表 */}
677 <div className="request-list">
678 {[
679 {
680 id: 1,
681 title: '求《药屋少女的呢喃》第二季全集',
682 content: '求1080P带中文字幕版本,最好是内嵌字幕不是外挂的',
683 author: '动漫爱好者',
684 authorAvatar: 'https://via.placeholder.com/40',
685 date: '2023-10-15',
686 likeCount: 24,
687 commentCount: 8
688 },
689 {
690 id: 2,
691 title: '求《奥本海默》IMAX版',
692 content: '最好是原盘或者高码率的版本,谢谢各位大佬',
693 author: '电影收藏家',
694 authorAvatar: 'https://via.placeholder.com/40',
695 date: '2023-10-14',
696 likeCount: 15,
697 commentCount: 5
698 }
699 ].map(post => (
700 <div
701 key={post.id}
702 className="request-post"
703 onClick={() => navigate(`/request/${post.id}`)}
704 >
705 <div className="post-header">
706 <img src={post.authorAvatar} alt={post.author} className="post-avatar" />
707 <div className="post-author">{post.author}</div>
708 <div className="post-date">{post.date}</div>
709 </div>
710 <h3 className="post-title">{post.title}</h3>
711 <p className="post-content">{post.content}</p>
712 <div className="post-stats">
713 <span className="post-likes">👍 {post.likeCount}</span>
714 <span className="post-comments">💬 {post.commentCount}</span>
715 </div>
716 </div>
717 ))}
718 </div>
719 </div>
720 );
721 // 在Dashboard.jsx的renderContent函数中修改case 'help'部分
722 case 'help':
723 return (
724 <div className="content-area" data-testid="help-section">
725 {/* 新增发帖按钮 */}
726 <div className="post-header">
727 <button
728 className="create-post-btn"
729 onClick={() => setShowPostModal(true)}
730 >
731 发帖求助
732 </button>
733 </div>
734
735 {/* 加载状态和错误提示 */}
736 {helpLoading && <div className="loading">加载中...</div>}
737 {helpError && <div className="error">{helpError}</div>}
738
739 {/* 求助区帖子列表 */}
740 <div className="help-list">
741 {helpPosts.map(post => (
742 <div
743 key={post.id}
744 className={`help-post ${post.isSolved ? 'solved' : ''}`}
745 onClick={() => navigate(`/help/${post.id}`)}
746 >
747 <div className="post-header">
748 <img
749 src={post.authorAvatar || 'https://via.placeholder.com/40'}
750 alt={post.authorId}
751 className="post-avatar"
752 />
753 <div className="post-author">{post.authorId}</div>
754 <div className="post-date">
755 {new Date(post.createTime).toLocaleDateString()}
756 </div>
757 {post.isSolved && <span className="solved-badge">已解决</span>}
758 </div>
759 <h3 className="post-title">{post.title}</h3>
760 <p className="post-content">{post.content}</p>
761 <div className="post-stats">
762 <span className="post-likes">👍 {post.likeCount || 0}</span>
763 <span className="post-comments">💬 {post.replyCount || 0}</span>
764 </div>
765 </div>
766 ))}
767 </div>
768
769 {/* 在帖子列表后添加分页控件 */}
770 <div className="pagination">
771 <button
772 onClick={() => fetchHelpPosts(currentPage - 1)}
773 disabled={currentPage === 1}
774 >
775 上一页
776 </button>
777
778 {Array.from({ length: totalPages }, (_, i) => i + 1).map(page => (
779 <button
780 key={page}
781 onClick={() => fetchHelpPosts(page)}
782 className={currentPage === page ? 'active' : ''}
783 >
784 {page}
785 </button>
786 ))}
787
788 <button
789 onClick={() => fetchHelpPosts(currentPage + 1)}
790 disabled={currentPage === totalPages}
791 >
792 下一页
793 </button>
794 </div>
795
796 {/* 新增发帖弹窗 */}
797 {showPostModal && (
798 <div className="post-modal-overlay">
799 <div className="post-modal">
800 <h3>发布求助帖</h3>
801 <button
802 className="modal-close-btn"
803 onClick={() => setShowPostModal(false)}
804 >
805 ×
806 </button>
807
808 <form onSubmit={handlePostSubmit}>
809 <div className="form-group">
810 <label>帖子标题</label>
811 <input
812 type="text"
813 value={postTitle}
814 onChange={(e) => setPostTitle(e.target.value)}
815 placeholder="请输入标题"
816 required
817 />
818 </div>
819
820 <div className="form-group">
821 <label>帖子内容</label>
822 <textarea
823 value={postContent}
824 onChange={(e) => setPostContent(e.target.value)}
825 placeholder="详细描述你的问题"
826 required
827 />
828 </div>
829
830 <div className="form-group">
831 <label>上传图片</label>
832 <div className="upload-image-btn">
833 <input
834 type="file"
835 id="image-upload"
836 accept="image/*"
837 onChange={handleImageUpload}
838 style={{ display: 'none' }}
839 />
840 <label htmlFor="image-upload">
841 {selectedImage ? '已选择图片' : '选择图片'}
842 </label>
843 {selectedImage && (
844 <span className="image-name">{selectedImage.name}</span>
845 )}
846 </div>
847 </div>
848
849 <div className="form-actions">
850 <button
851 type="button"
852 className="cancel-btn"
853 onClick={() => setShowPostModal(false)}
854 >
855 取消
856 </button>
857 <button
858 type="submit"
859 className="submit-btn"
860 >
861 确认发帖
862 </button>
863 </div>
864 </form>
865 </div>
866 </div>
867 )}
868 </div>
869 );
870 default:
871 return <div className="content-area" data-testid="default-section">公告区内容</div>;
872 }
873 };
874
875 if (loading) return <div className="loading">加载中...</div>;
876 if (error) return <div className="error">{error}</div>;
877
878 return (
879 <div className="dashboard-container" data-testid="dashboard-container">
880 {/* 顶部栏 */}
881 <div className="top-bar" data-testid="top-bar">
882 {/* 搜索框 */}
883 <div className="search-container">
884 <input
885 type="text"
886 placeholder="搜索种子、用户..."
887 className="search-input"
888 />
889 <button className="search-button">搜索</button>
890 </div>
891
892 <div className="user-info" data-testid="user-info">
893 <img
894 src={userInfo?.avatar || 'https://via.placeholder.com/40'}
895 alt="用户头像"
896 className="user-avatar"
897 onClick={() => navigate('/personal')} // 添加点击跳转
898 style={{ cursor: 'pointer' }} // 添加手型指针
899 />
900 <span className="username">{userInfo?.name || '用户'}</span>
901 <button onClick={onLogout} className="logout-button">退出</button>
902 </div>
903 </div>
904
905 {/* 导航栏 */}
906 {/* handleTabchange函数替换了原本的setactivetab函数 */}
907 <div className="nav-tabs">
908 <button
909 className={`tab-button ${activeTab === 'announcement' ? 'active' : ''}`}
910 onClick={() => handleTabChange('announcement')}
911 >
912 公告区
913 </button>
914 <button
915 className={`tab-button ${activeTab === 'share' ? 'active' : ''}`}
916 onClick={() => handleTabChange('share')}
917 >
918 分享区
919 </button>
920 <button
921 className={`tab-button ${activeTab === 'request' ? 'active' : ''}`}
922 onClick={() => handleTabChange('request')}
923 >
924 求种区
925 </button>
926 <button
927 className={`tab-button ${activeTab === 'help' ? 'active' : ''}`}
928 onClick={() => handleTabChange('help')}
929 >
930 求助区
931 </button>
932 </div>
933
934 {/* 内容区 */}
935 {renderContent()}
936 </div>
937 );
938};
939
940export default Dashboard;