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