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 | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 7 | |
| 8 | const PostDetailPage = () => { |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 9 | 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 |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 19 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 20 | // 获取当前用户ID(假设从上下文中获取) |
| 21 | const { user } = useUser(); // 你需要从用户上下文获取用户 ID |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 22 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 23 | useEffect(() => { |
| 24 | const fetchPostDetail = async () => { |
| 25 | setLoading(true); |
| 26 | setErrorMsg(''); |
| 27 | try { |
| 28 | // 获取帖子详情 |
| 29 | const postData = await getPostDetail(postId); |
| 30 | setPostDetail(postData); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 31 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 32 | // 获取帖子评论 |
| 33 | const commentsData = await getPostComments(postId); |
| 34 | setComments(commentsData); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 35 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 36 | // 设置是否已经点赞 |
| 37 | if (postData.likedByUser) { |
| 38 | setIsLiked(true); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 39 | } else { |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 40 | setIsLiked(false); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 41 | } |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 42 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 43 | // 设置是否已经收藏 |
| 44 | if (postData.collectedByUser) { |
| 45 | setIsCollected(true); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 46 | } else { |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 47 | setIsCollected(false); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 48 | } |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 49 | } catch (err) { |
| 50 | console.error('加载失败:', err); |
| 51 | setErrorMsg('加载失败,请稍后重试'); |
| 52 | } finally { |
| 53 | setLoading(false); |
| 54 | } |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 55 | }; |
| 56 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 57 | fetchPostDetail(); |
| 58 | }, [postId]); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 59 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 60 | // 点赞功能 |
| 61 | const toggleLike = async () => { |
| 62 | if (!user) { |
| 63 | alert('请先登录'); |
| 64 | return; |
| 65 | } |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 66 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 67 | 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 | }; |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 90 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 91 | // 收藏功能 |
| 92 | const toggleCollect = async () => { |
| 93 | if (!user) { |
| 94 | alert('请先登录'); |
| 95 | return; |
| 96 | } |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 97 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 98 | 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> |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 265 | </div> |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 266 | ) : ( |
| 267 | <p>帖子不存在</p> |
| 268 | )} |
| 269 | </div> |
| 270 | ); |
Krishya | 7ec1dd0 | 2025-04-19 15:29:03 +0800 | [diff] [blame] | 271 | }; |
| 272 | |
22301009 | 237217b | 2025-04-20 15:15:25 +0800 | [diff] [blame^] | 273 | export default PostDetailPage; |