blob: aae1874c56c45c484c2b4b98deea65d3a0ab4873 [file] [log] [blame]
223011339e292152025-06-08 00:34:37 +08001import React, { useState, useEffect } from "react";
TRM-codingfa3ffdf2025-06-09 22:47:42 +08002import { useParams, useNavigate } from "react-router-dom";
223011339e292152025-06-08 00:34:37 +08003import { API_BASE_URL } from "./config";
TRM-codingfa3ffdf2025-06-09 22:47:42 +08004import HomeIcon from "@mui/icons-material/Home";
5import MovieIcon from "@mui/icons-material/Movie";
6import TvIcon from "@mui/icons-material/Tv";
7import MusicNoteIcon from "@mui/icons-material/MusicNote";
8import AnimationIcon from "@mui/icons-material/Animation";
9import SportsEsportsIcon from "@mui/icons-material/SportsEsports";
10import SportsMartialArtsIcon from "@mui/icons-material/SportsMartialArts";
11import PersonIcon from "@mui/icons-material/Person";
12import AccountCircleIcon from "@mui/icons-material/AccountCircle";
13import ForumIcon from "@mui/icons-material/Forum";
14import HelpIcon from "@mui/icons-material/Help";
15import ArrowBackIcon from "@mui/icons-material/ArrowBack";
16import ReplyIcon from "@mui/icons-material/Reply";
17import VisibilityIcon from "@mui/icons-material/Visibility";
18import "./SharedStyles.css";
19
20const navItems = [
21 { label: "首页", icon: <HomeIcon className="emerald-nav-icon" />, path: "/home", type: "home" },
22 { label: "电影", icon: <MovieIcon className="emerald-nav-icon" />, path: "/movie", type: "movie" },
23 { label: "剧集", icon: <TvIcon className="emerald-nav-icon" />, path: "/tv", type: "tv" },
24 { label: "音乐", icon: <MusicNoteIcon className="emerald-nav-icon" />, path: "/music", type: "music" },
25 { label: "动漫", icon: <AnimationIcon className="emerald-nav-icon" />, path: "/anime", type: "anime" },
26 { label: "游戏", icon: <SportsEsportsIcon className="emerald-nav-icon" />, path: "/game", type: "game" },
27 { label: "体育", icon: <SportsMartialArtsIcon className="emerald-nav-icon" />, path: "/sport", type: "sport" },
28 { label: "资料", icon: <PersonIcon className="emerald-nav-icon" />, path: "/info", type: "info" },
29 { label: "论坛", icon: <ForumIcon className="emerald-nav-icon" />, path: "/forum", type: "forum" },
30 { label: "发布", icon: <AccountCircleIcon className="emerald-nav-icon" />, path: "/publish", type: "publish" },
31 { label: "求种", icon: <HelpIcon className="emerald-nav-icon" />, path: "/begseed", type: "help" },
32];
33
34// 论坛详情页文字雨内容
35const forumDetailTexts = [
36 "讨论", "回复", "交流", "观点", "见解", "思考", "分享", "互动",
37 "对话", "评论", "深度", "专业", "洞察", "分析", "探讨", "解答"
38];
wht6a1b6782025-06-06 19:14:59 +080039
40export default function PostDetailPage() {
41 const { postId } = useParams();
TRM-codingfa3ffdf2025-06-09 22:47:42 +080042 const navigate = useNavigate();
223011339e292152025-06-08 00:34:37 +080043 const [post, setPost] = useState(null);
44 const [replies, setReplies] = useState([]);
45 const [newReply, setNewReply] = useState('');
TRM-codingfa3ffdf2025-06-09 22:47:42 +080046 const [userInfo, setUserInfo] = useState({ avatar_url: '', username: '' });
47 const [userPT, setUserPT] = useState({ magic: 0, ratio: 0, upload: 0, download: 0 });
48
49 useEffect(() => {
50 // 获取用户信息
51 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
52 const userId = match ? match[2] : null;
53 if (userId) {
54 fetch(`${API_BASE_URL}/api/get-userpt?userid=${encodeURIComponent(userId)}`)
55 .then(res => res.json())
56 .then(data => {
57 setUserInfo({ avatar_url: data.user.avatar_url, username: data.user.username });
58 setUserPT({
59 magic: data.magic_value || data.magic || 0,
60 ratio: data.share_ratio || data.share || 0,
61 upload: data.upload_amount || data.upload || 0,
62 download: data.download_amount || data.download || 0,
63 });
64 })
65 .catch(err => console.error('Fetching user profile failed', err));
66 }
67 }, []);
223011339e292152025-06-08 00:34:37 +080068
69 // function to load post details and its replies
70 const fetchDetail = () => {
71 fetch(`${API_BASE_URL}/api/forum-detail?postid=${postId}`)
72 .then(res => res.json())
73 .then(data => {
74 console.log("Fetched post detail:", data);
75 const p = data.post || data;
76 const formattedPost = {
77 post_id: p.postid,
78 title: p.posttitle,
79 content: p.postcontent,
80 author_id: p.postuserid,
81 author_name: p.author?.username || '',
82 created_at: new Date(p.posttime).toLocaleString(),
83 reply_count: p.replytime,
84 view_count: p.readtime,
85 };
86 const formattedReplies = (data.replies || []).map(r => ({
87 reply_id: r.replyid,
88 post_id: r.postid || postId,
89 content: r.content,
90 author_id: r.authorid,
91 author_name: r.author?.username || '',
92 created_at: new Date(r.createdAt).toLocaleString(),
93 }));
94 setPost(formattedPost);
95 setReplies(formattedReplies);
96 })
97 .catch(err => console.error(err));
98 };
99
100 useEffect(() => {
101 fetchDetail();
102 }, [postId]);
103
104 // post a new reply to backend
105 const handleReply = () => {
106 const match = document.cookie.match('(^|;)\\s*userId=([^;]+)');
107 const userId = match ? match[2] : null;
108 if (!userId) {
109 alert('请先登录后再回复');
110 return;
111 }
112 fetch(`${API_BASE_URL}/api/forum-reply`, {
113 method: 'POST',
114 headers: { 'Content-Type': 'application/json' },
115 body: JSON.stringify({ postid: postId, replycontent: newReply, replyuserid: userId }),
116 })
117 .then(res => res.json())
118 .then(() => {
119 setNewReply('');
120 fetchDetail();
121 })
122 .catch(err => console.error(err));
123 };
124
TRM-codingfa3ffdf2025-06-09 22:47:42 +0800125 if (!post) return (
126 <div className="emerald-home-container">
127 <div className="emerald-content">
128 <div style={{
129 textAlign: 'center',
130 padding: '100px 20px',
131 color: '#2d5016',
132 fontSize: '18px',
133 background: 'rgba(255, 255, 255, 0.95)',
134 borderRadius: '25px',
135 margin: '50px auto',
136 maxWidth: '600px'
137 }}>
138 <ForumIcon style={{ fontSize: 64, marginBottom: '20px', color: '#90ee90' }} />
139 <div>加载帖子详情中...</div>
wht6a1b6782025-06-06 19:14:59 +0800140 </div>
141 </div>
TRM-codingfa3ffdf2025-06-09 22:47:42 +0800142 </div>
143 );
144
145 return (
146 <div className="emerald-home-container">
147 {/* 文字雨背景效果 */}
148 <div className="forum-text-rain">
149 {forumDetailTexts.map((text, index) => (
150 <div key={index} className="text-drop" style={{
151 left: `${(index * 3.5) % 100}%`,
152 animationDelay: `${(index * 0.8) % 10}s`,
153 animationDuration: `${8 + (index % 5)}s`
wht6a1b6782025-06-06 19:14:59 +0800154 }}>
TRM-codingfa3ffdf2025-06-09 22:47:42 +0800155 {text}
wht6a1b6782025-06-06 19:14:59 +0800156 </div>
157 ))}
158 </div>
TRM-codingfa3ffdf2025-06-09 22:47:42 +0800159
160 {/* 浮动园林装饰元素 */}
161 <div className="floating-garden-elements">
162 <div className="garden-element">💭</div>
163 <div className="garden-element">📖</div>
164 <div className="garden-element">💡</div>
165 <div className="garden-element">✨</div>
166 </div>
167
168 <div className="emerald-content">
169 {/* NeuraFlux用户栏 */}
170 <div className="emerald-user-bar">
171 <div className="emerald-user-avatar" onClick={() => navigate('/user')}>
172 {userInfo.avatar_url ? (
173 <img src={userInfo.avatar_url} alt="用户头像" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover' }} />
174 ) : (
175 <AccountCircleIcon style={{ fontSize: 38, color: 'white' }} />
176 )}
177 </div>
178 <div className="emerald-brand-section">
179 <div className="emerald-brand-icon">⚡</div>
180 <div className="emerald-user-label">NeuraFlux</div>
181 </div>
182 <div className="emerald-user-stats">
183 <span className="emerald-stat-item">
184 魔力值: <span className="emerald-stat-value">{userPT.magic}</span>
185 </span>
186 <span className="emerald-stat-item">
187 分享率: <span className="emerald-stat-value">{userPT.ratio}</span>
188 </span>
189 <span className="emerald-stat-item">
190 上传: <span className="emerald-stat-value">{userPT.upload}GB</span>
191 </span>
192 <span className="emerald-stat-item">
193 下载: <span className="emerald-stat-value">{userPT.download}GB</span>
194 </span>
195 </div>
196 </div>
197
198 {/* NeuraFlux导航栏 */}
199 <nav className="emerald-nav-bar">
200 {navItems.map((item) => (
201 <div
202 key={item.label}
203 className={`emerald-nav-item ${item.label === "论坛" ? "active" : ""}`}
204 data-type={item.type}
205 onClick={() => navigate(item.path)}
206 >
207 {item.icon}
208 <span className="emerald-nav-label">{item.label}</span>
209 </div>
210 ))}
211 </nav>
212
213 {/* 返回按钮 */}
214 <div className="post-detail-header">
215 <button className="back-to-forum-btn" onClick={() => navigate('/forum')}>
216 <ArrowBackIcon style={{ marginRight: '8px', fontSize: '20px' }} />
217 返回论坛
218 </button>
219 </div>
220
221 {/* 帖子详情内容区域 */}
222 <div className="emerald-content-section">
223 {/* 原帖内容 */}
224 <div className="post-detail-main">
225 <div className="post-detail-header-info">
226 <div className="post-author-section">
227 <div className="post-author-avatar">
228 <AccountCircleIcon style={{ fontSize: 48, color: '#2d5016' }} />
229 </div>
230 <div className="post-author-details">
231 <h2 className="post-author-name">{post.author_name}</h2>
232 <span className="post-publish-time">{post.created_at}</span>
233 </div>
234 </div>
235 <div className="post-stats-section">
236 <div className="stat-badge">
237 <VisibilityIcon style={{ fontSize: '18px', marginRight: '4px' }} />
238 <span>{post.view_count} 浏览</span>
239 </div>
240 <div className="stat-badge">
241 <ReplyIcon style={{ fontSize: '18px', marginRight: '4px' }} />
242 <span>{post.reply_count} 回复</span>
243 </div>
244 </div>
245 </div>
246
247 <div className="post-content-section">
248 <h1 className="post-detail-title">{post.title}</h1>
249 <div className="post-detail-content">
250 {post.content}
251 </div>
252 </div>
253 </div>
254
255 {/* 回复列表 */}
256 <div className="replies-section">
257 <h3 className="replies-title">
258 <ReplyIcon style={{ marginRight: '8px', color: '#2d5016' }} />
259 全部回复 ({replies.length})
260 </h3>
261
262 {replies.length > 0 ? (
263 <div className="replies-list">
264 {replies.map((reply, index) => (
265 <div key={reply.reply_id} className="reply-card">
266 <div className="reply-index">#{index + 1}</div>
267 <div className="reply-content-wrapper">
268 <div className="reply-author-info">
269 <AccountCircleIcon style={{ fontSize: 24, color: '#2d5016', marginRight: '8px' }} />
270 <span className="reply-author-name">{reply.author_name}</span>
271 <span className="reply-time">{reply.created_at}</span>
272 </div>
273 <div className="reply-content">{reply.content}</div>
274 </div>
275 </div>
276 ))}
277 </div>
278 ) : (
279 <div className="no-replies">
280 <ForumIcon style={{ fontSize: 48, color: '#90ee90', marginBottom: '16px' }} />
281 <p>还没有人回复,快来抢沙发吧!</p>
282 </div>
283 )}
284 </div>
285
286 {/* 回复输入框 */}
287 <div className="reply-input-section">
288 <h3 className="reply-input-title">发表回复</h3>
289 <div className="reply-input-wrapper">
290 <textarea
291 placeholder="写下你的想法和观点..."
292 value={newReply}
293 onChange={e => setNewReply(e.target.value)}
294 className="reply-textarea"
295 rows="4"
296 />
297 <div className="reply-actions">
298 <div className="reply-tips">
299 💡 支持理性讨论,拒绝恶意灌水
300 </div>
301 <button onClick={handleReply} className="submit-reply-btn">
302 <ReplyIcon style={{ marginRight: '6px', fontSize: '18px' }} />
303 发布回复
304 </button>
305 </div>
306 </div>
307 </div>
308 </div>
wht6a1b6782025-06-06 19:14:59 +0800309 </div>
310 </div>
311 );
312}