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