| import React, { useContext, useEffect, useState } from 'react'; |
| import { useParams } from 'wouter'; |
| import { GoodTwo, Star } from '@icon-park/react'; |
| import { getPostDetail, getPostComments, likePost, unlikePost, addCommentToPost, collectPost, uncollectPost } from './api'; // 引入你的 API 函数 |
| import './PostDetailPage.css'; |
| import { UserContext, useUser } from '../../../context/UserContext'; // 注意路径 |
| import Header from '../../../components/Header'; |
| |
| const PostDetailPage = () => { |
| const { postId } = useParams(); // 获取帖子ID |
| const [postDetail, setPostDetail] = useState(null); |
| const [comments, setComments] = useState([]); |
| const [loading, setLoading] = useState(true); |
| const [errorMsg, setErrorMsg] = useState(''); |
| const [newComment, setNewComment] = useState(''); // 新评论内容 |
| // const [isAnonymous, setIsAnonymous] = useState(false); // 是否匿名 |
| const [isLiked, setIsLiked] = useState(false); // 是否已点赞 |
| const [isCollected, setIsCollected] = useState(false); // 是否已收藏 |
| const [replyToCommentId, setReplyToCommentId] = useState(null); // 回复的评论ID |
| const [replyToUsername, setReplyToUsername] = useState(null); |
| |
| // 获取当前用户ID(假设从上下文中获取) |
| const { user } = useContext(UserContext); |
| // const { user } = useUser(); // 你需要从用户上下文获取用户 ID |
| |
| useEffect(() => { |
| const fetchPostDetail = async () => { |
| setLoading(true); |
| setErrorMsg(''); |
| try { |
| // 获取帖子详情 |
| const postData = await getPostDetail(postId); |
| setPostDetail(postData); |
| |
| // 获取帖子评论 |
| const commentsData = await getPostComments(postId); |
| setComments(commentsData); |
| |
| // 设置是否已经点赞 |
| if (postData.likedByUser) { |
| setIsLiked(true); |
| } else { |
| setIsLiked(false); |
| } |
| |
| // 设置是否已经收藏 |
| if (postData.collectedByUser) { |
| setIsCollected(true); |
| } else { |
| setIsCollected(false); |
| } |
| } catch (err) { |
| console.error('加载失败:', err); |
| setErrorMsg('加载失败,请稍后重试'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| fetchPostDetail(); |
| }, [postId]); |
| |
| // 点赞功能 |
| const toggleLike = async () => { |
| if (!user) { |
| alert('请先登录'); |
| return; |
| } |
| |
| try { |
| if (isLiked) { |
| // 取消点赞 |
| await unlikePost(postId, user.userId); |
| setIsLiked(false); |
| setPostDetail((prev) => ({ |
| ...prev, |
| postLikeNum: prev.postLikeNum - 1, |
| })); |
| } else { |
| // 点赞 |
| await likePost(postId, user.userId); |
| setIsLiked(true); |
| setPostDetail((prev) => ({ |
| ...prev, |
| postLikeNum: prev.postLikeNum + 1, |
| })); |
| } |
| } catch (err) { |
| console.error('点赞失败:', err); |
| alert('点赞失败,请稍后再试'); |
| } |
| }; |
| |
| // 收藏功能 |
| const toggleCollect = async () => { |
| if (!user) { |
| alert('请先登录'); |
| return; |
| } |
| |
| try { |
| if (isCollected) { |
| // 取消收藏 - 使用原有的collectPost函数,传递action: "cancel" |
| await collectPost(postId, user.userId, "cancel"); |
| setIsCollected(false); |
| setPostDetail((prev) => ({ |
| ...prev, |
| postCollectNum: prev.postCollectNum - 1, |
| })); |
| } else { |
| // 收藏 |
| await collectPost(postId, user.userId, "collect"); |
| setIsCollected(true); |
| setPostDetail((prev) => ({ |
| ...prev, |
| postCollectNum: prev.postCollectNum + 1, |
| })); |
| } |
| } catch (err) { |
| console.error('收藏操作失败:', err); |
| alert('收藏操作失败,请稍后再试'); |
| } |
| }; |
| |
| // 添加评论 |
| const handleAddComment = async () => { |
| // 直接使用组件顶层获取的 user |
| if (!user || !user.userId) { |
| alert('请先登录后再评论'); |
| return; |
| } |
| |
| if (!newComment.trim()) { |
| alert('评论内容不能为空'); |
| return; |
| } |
| |
| try { |
| // 构建评论数据 |
| const commentPayload = { |
| content: newComment, |
| userId: user.userId, // 使用已获取的用户ID |
| isAnonymous: false, |
| com_comment_id: replyToCommentId || null, |
| }; |
| |
| // 发送评论请求 |
| const commentData = await addCommentToPost(postId, commentPayload); |
| |
| // 更新评论列表 |
| const newCommentItem = { |
| commentId: commentData?.commentId || Date.now(), |
| post_id: postId, |
| userId: user.userId, |
| content: newComment, |
| commentTime: new Date().toISOString(), |
| comCommentId: replyToCommentId, |
| }; |
| |
| setComments((prevComments) => [newCommentItem, ...prevComments]); |
| |
| // 重置表单 |
| setNewComment(''); |
| setReplyToCommentId(null); |
| |
| // alert('评论成功!'); |
| } catch (error) { |
| console.error('评论失败:', error); |
| |
| const errorMessage = |
| error.response?.data?.message || |
| error.message || |
| '评论失败,请稍后再试'; |
| |
| alert(errorMessage); |
| } |
| }; |
| |
| |
| |
| // 回复评论 |
| const handleReply = (commentId) => { |
| setReplyToCommentId(commentId); |
| const comment = comments.find(c => c.commentId === commentId); |
| if (comment) { |
| // 这里用用户名或者用户ID |
| setReplyToUsername(comment.username || comment.userId); |
| } else { |
| setReplyToUsername(null); |
| } |
| }; |
| |
| const findUsernameByCommentId = (id) => { |
| const comment = comments.find(c => c.commentId === id); |
| return comment ? (comment.username || comment.userId) : '未知用户'; |
| }; |
| |
| |
| return ( |
| <div className="post-detail-page"> |
| <Header /> |
| {loading ? ( |
| <p>加载中...</p> |
| ) : errorMsg ? ( |
| <p className="error-text">{errorMsg}</p> |
| ) : postDetail ? ( |
| <div className="post-detail"> |
| <h1>{postDetail.title}</h1> |
| <div className="post-meta"> |
| <span className="post-user">用户ID: {postDetail.user_id}</span> |
| <span className="post-time"> |
| 发布时间:{new Date(postDetail.postTime).toLocaleString()} |
| </span> |
| </div> |
| <div className="post-content"> |
| <p>{postDetail.postContent}</p> |
| {Array.isArray(postDetail.imgUrl) ? ( |
| <div className="post-images"> |
| {postDetail.imgUrl.map((url, idx) => ( |
| <img key={idx} src={url} alt={`图片${idx}`} /> |
| ))} |
| </div> |
| ) : ( |
| postDetail.imgUrl && ( |
| <img className="post-image" src={postDetail.imgUrl} alt="帖子图片" /> |
| ) |
| )} |
| |
| </div> |
| |
| {/* 点赞和收藏 */} |
| <div className="post-actions"> |
| <button |
| className="icon-btn" |
| onClick={toggleLike} // 点赞操作 |
| > |
| <GoodTwo |
| theme="outline" |
| size="20" |
| fill={isLiked ? '#f00' : '#ccc'} // 如果已点赞,显示红色 |
| /> |
| <span>{postDetail.postLikeNum}</span> |
| </button> |
| <button |
| className="icon-btn" |
| onClick={toggleCollect} // 收藏操作 |
| > |
| <Star |
| theme="outline" |
| size="20" |
| fill={isCollected ? '#ffd700' : '#ccc'} // 如果已收藏,显示金色 |
| /> |
| <span>{postDetail.postCollectNum}</span> |
| </button> |
| </div> |
| |
| <hr className="divider" /> |
| {/* 评论部分 */} |
| <h3>评论区</h3> |
| <div className="comments-section"> |
| {comments.length ? ( |
| comments.map((comment) => ( |
| <div key={comment.commentId} className="comment"> |
| <div className="comment-header"> |
| <span className="comment-user">用户 ID: {comment.userId}</span> |
| <button className="reply-btn" onClick={() => handleReply(comment.commentId)}>回复</button> |
| </div> |
| |
| <p className="comment-content"> |
| {comment.comCommentId ? ( |
| <> |
| <span className="reply-to">回复 {findUsernameByCommentId(comment.comCommentId)}:</span> |
| {comment.content} |
| </> |
| ) : ( |
| comment.content |
| )} |
| </p> |
| |
| <div className="comment-time"> |
| {new Date(comment.commentTime).toLocaleString()} |
| </div> |
| |
| {/* 回复框 */} |
| {replyToCommentId === comment.commentId && ( |
| <div className="reply-form"> |
| <div className="replying-to"> |
| 回复 <strong>{replyToUsername}</strong>: |
| </div> |
| <textarea |
| placeholder="输入你的回复..." |
| value={newComment} |
| onChange={(e) => setNewComment(e.target.value)} |
| /> |
| <div className="comment-options"> |
| <button onClick={handleAddComment}>发布回复</button> |
| </div> |
| </div> |
| )} |
| </div> |
| )) |
| ) : ( |
| <p>暂无评论</p> |
| )} |
| |
| |
| {/* 添加评论表单 */} |
| <div className="add-comment-form"> |
| <textarea |
| placeholder="输入你的评论..." |
| value={newComment} |
| onChange={(e) => setNewComment(e.target.value)} |
| /> |
| <div className="comment-options"> |
| {/* <label> |
| <input |
| type="checkbox" |
| checked={isAnonymous} |
| onChange={() => setIsAnonymous(!isAnonymous)} |
| /> |
| 匿名评论 |
| </label> */} |
| <button onClick={handleAddComment}>发布评论</button> |
| </div> |
| </div> |
| </div> |
| </div> |
| ) : ( |
| <p>帖子不存在</p> |
| )} |
| </div> |
| ); |
| }; |
| |
| export default PostDetailPage; |