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