blob: 51863392d237dd03e3648f092d07e31b95125527 [file] [log] [blame]
22301009237217b2025-04-20 15:15:25 +08001import React, { useEffect, useState } from 'react';
2import { useParams } from 'wouter';
3import { GoodTwo, Star } from '@icon-park/react';
4import { getPostDetail, getPostComments, likePost, unlikePost, addCommentToPost, collectPost } from './api'; // 引入你的 API 函数
Krishya7ec1dd02025-04-19 15:29:03 +08005import './PostDetailPage.css';
22301009237217b2025-04-20 15:15:25 +08006import { useUser } from '../../../context/UserContext'; // 注意路径
Krishya7ec1dd02025-04-19 15:29:03 +08007
8const PostDetailPage = () => {
22301009237217b2025-04-20 15:15:25 +08009 const { postId } = useParams(); // 获取帖子ID
10 const [postDetail, setPostDetail] = useState(null);
11 const [comments, setComments] = useState([]);
12 const [loading, setLoading] = useState(true);
13 const [errorMsg, setErrorMsg] = useState('');
14 const [newComment, setNewComment] = useState(''); // 新评论内容
15 const [isAnonymous, setIsAnonymous] = useState(false); // 是否匿名
16 const [isLiked, setIsLiked] = useState(false); // 是否已点赞
17 const [isCollected, setIsCollected] = useState(false); // 是否已收藏
18 const [replyToCommentId, setReplyToCommentId] = useState(null); // 回复的评论ID
Krishya7ec1dd02025-04-19 15:29:03 +080019
22301009237217b2025-04-20 15:15:25 +080020 // 获取当前用户ID(假设从上下文中获取)
21 const { user } = useUser(); // 你需要从用户上下文获取用户 ID
Krishya7ec1dd02025-04-19 15:29:03 +080022
22301009237217b2025-04-20 15:15:25 +080023 useEffect(() => {
24 const fetchPostDetail = async () => {
25 setLoading(true);
26 setErrorMsg('');
27 try {
28 // 获取帖子详情
29 const postData = await getPostDetail(postId);
30 setPostDetail(postData);
Krishya7ec1dd02025-04-19 15:29:03 +080031
22301009237217b2025-04-20 15:15:25 +080032 // 获取帖子评论
33 const commentsData = await getPostComments(postId);
34 setComments(commentsData);
Krishya7ec1dd02025-04-19 15:29:03 +080035
22301009237217b2025-04-20 15:15:25 +080036 // 设置是否已经点赞
37 if (postData.likedByUser) {
38 setIsLiked(true);
Krishya7ec1dd02025-04-19 15:29:03 +080039 } else {
22301009237217b2025-04-20 15:15:25 +080040 setIsLiked(false);
Krishya7ec1dd02025-04-19 15:29:03 +080041 }
Krishya7ec1dd02025-04-19 15:29:03 +080042
22301009237217b2025-04-20 15:15:25 +080043 // 设置是否已经收藏
44 if (postData.collectedByUser) {
45 setIsCollected(true);
Krishya7ec1dd02025-04-19 15:29:03 +080046 } else {
22301009237217b2025-04-20 15:15:25 +080047 setIsCollected(false);
Krishya7ec1dd02025-04-19 15:29:03 +080048 }
22301009237217b2025-04-20 15:15:25 +080049 } catch (err) {
50 console.error('加载失败:', err);
51 setErrorMsg('加载失败,请稍后重试');
52 } finally {
53 setLoading(false);
54 }
Krishya7ec1dd02025-04-19 15:29:03 +080055 };
56
22301009237217b2025-04-20 15:15:25 +080057 fetchPostDetail();
58 }, [postId]);
Krishya7ec1dd02025-04-19 15:29:03 +080059
22301009237217b2025-04-20 15:15:25 +080060 // 点赞功能
61 const toggleLike = async () => {
62 if (!user) {
63 alert('请先登录');
64 return;
65 }
Krishya7ec1dd02025-04-19 15:29:03 +080066
22301009237217b2025-04-20 15:15:25 +080067 try {
68 if (isLiked) {
69 // 取消点赞
70 await unlikePost(postId, user.id);
71 setIsLiked(false);
72 setPostDetail((prev) => ({
73 ...prev,
74 postLikeNum: prev.postLikeNum - 1,
75 }));
76 } else {
77 // 点赞
78 await likePost(postId, user.id);
79 setIsLiked(true);
80 setPostDetail((prev) => ({
81 ...prev,
82 postLikeNum: prev.postLikeNum + 1,
83 }));
84 }
85 } catch (err) {
86 console.error('点赞失败:', err);
87 alert('点赞失败,请稍后再试');
88 }
89 };
Krishya7ec1dd02025-04-19 15:29:03 +080090
22301009237217b2025-04-20 15:15:25 +080091 // 收藏功能
92 const toggleCollect = async () => {
93 if (!user) {
94 alert('请先登录');
95 return;
96 }
Krishya7ec1dd02025-04-19 15:29:03 +080097
22301009237217b2025-04-20 15:15:25 +080098 try {
99 const action = isCollected ? 'cancel' : 'collect';
100 // 调用收藏 API
101 await collectPost(postId, user.id, action);
102 setIsCollected(!isCollected);
103 setPostDetail((prev) => ({
104 ...prev,
105 postCollectNum: isCollected ? prev.postCollectNum - 1 : prev.postCollectNum + 1,
106 }));
107 } catch (err) {
108 console.error('收藏失败:', err);
109 alert('收藏失败,请稍后再试');
110 }
111 };
112
113 // 添加评论
114 const handleAddComment = async () => {
115 if (!newComment.trim()) {
116 alert('评论内容不能为空');
117 return;
118 }
119
120 try {
121 // 调用 API 添加评论,若为回复评论则传递父评论ID(com_comment_id)
122 const commentData = await addCommentToPost(postId, user.id, newComment, isAnonymous, replyToCommentId);
123 // 更新评论列表
124 setComments((prev) => [
125 ...prev,
126 {
127 commentId: commentData.commentId,
128 post_id: postId,
129 userId: user.id,
130 content: newComment,
131 isAnonymous,
132 commentTime: new Date().toISOString(),
133 comCommentId: replyToCommentId, // 回复评论时传递父评论ID
134 },
135 ]);
136 // 清空评论框和回复状态
137 setNewComment('');
138 setReplyToCommentId(null);
139 } catch (err) {
140 console.error('评论添加失败:', err);
141 alert('评论失败,请稍后再试');
142 }
143 };
144
145 // 回复评论
146 const handleReply = (commentId) => {
147 setReplyToCommentId(commentId); // 设置父评论ID为当前评论的ID
148 };
149
150 return (
151 <div className="post-detail-page">
152 {loading ? (
153 <p>加载中...</p>
154 ) : errorMsg ? (
155 <p className="error-text">{errorMsg}</p>
156 ) : postDetail ? (
157 <div className="post-detail">
158 <h1>{postDetail.title}</h1>
159 <div className="post-meta">
160 <span className="post-time">
161 {new Date(postDetail.postTime).toLocaleString()}
162 </span>
163 <span className="post-user">用户 ID: {postDetail.user_id}</span>
164 </div>
165 <div className="post-content">
166 <p>{postDetail.postContent}</p>
167 {postDetail.imgUrl && (
168 <img
169 className="post-image"
170 src={postDetail.imgUrl}
171 alt="帖子图片"
172 />
173 )}
174 </div>
175
176 {/* 点赞和收藏 */}
177 <div className="post-actions">
178 <button
179 className="icon-btn"
180 onClick={toggleLike} // 点赞操作
181 >
182 <GoodTwo
183 theme="outline"
184 size="24"
185 fill={isLiked ? '#f00' : '#ccc'} // 如果已点赞,显示红色
186 />
187 <span>{postDetail.postLikeNum}</span>
188 </button>
189 <button
190 className="icon-btn"
191 onClick={toggleCollect} // 收藏操作
192 >
193 <Star
194 theme="outline"
195 size="24"
196 fill={isCollected ? '#ffd700' : '#ccc'} // 如果已收藏,显示金色
197 />
198 <span>{postDetail.postCollectNum}</span>
199 </button>
200 </div>
201
202 {/* 评论部分 */}
203 <div className="comments-section">
204 <h3>评论区</h3>
205 {comments.length ? (
206 comments.map((comment) => (
207 <div key={comment.commentId} className="comment">
208 <p>{comment.content}</p>
209 <div className="comment-meta">
210 <span className="comment-time">
211 {new Date(comment.commentTime).toLocaleString()}
212 </span>
213 <span className="comment-user">用户 ID: {comment.userId}</span>
214 </div>
215 {/* 回复按钮 */}
216 <button onClick={() => handleReply(comment.commentId)}>回复</button>
217
218 {/* 回复框,只有在当前评论是正在回复的评论时显示 */}
219 {replyToCommentId === comment.commentId && (
220 <div className="reply-form">
221 <textarea
222 placeholder="输入你的回复..."
223 value={newComment}
224 onChange={(e) => setNewComment(e.target.value)}
225 />
226 <div className="comment-options">
227 <label>
228 <input
229 type="checkbox"
230 checked={isAnonymous}
231 onChange={() => setIsAnonymous(!isAnonymous)}
232 />
233 匿名评论
234 </label>
235 <button onClick={handleAddComment}>发布回复</button>
236 </div>
237 </div>
238 )}
239 </div>
240 ))
241 ) : (
242 <p>暂无评论</p>
243 )}
244
245 {/* 添加评论表单 */}
246 <div className="add-comment-form">
247 <textarea
248 placeholder="输入你的评论..."
249 value={newComment}
250 onChange={(e) => setNewComment(e.target.value)}
251 />
252 <div className="comment-options">
253 <label>
254 <input
255 type="checkbox"
256 checked={isAnonymous}
257 onChange={() => setIsAnonymous(!isAnonymous)}
258 />
259 匿名评论
260 </label>
261 <button onClick={handleAddComment}>发布评论</button>
262 </div>
263 </div>
264 </div>
Krishya7ec1dd02025-04-19 15:29:03 +0800265 </div>
22301009237217b2025-04-20 15:15:25 +0800266 ) : (
267 <p>帖子不存在</p>
268 )}
269 </div>
270 );
Krishya7ec1dd02025-04-19 15:29:03 +0800271};
272
22301009237217b2025-04-20 15:15:25 +0800273export default PostDetailPage;