| import React, { useState, useEffect } from 'react'; |
| import { useRoute } from 'wouter'; |
| import { |
| getPostDetail, |
| getPostComments, |
| likePost, |
| unlikePost, |
| addCommentToPost, |
| replyToComment, |
| likeComment, |
| unlikeComment, |
| getUserInfo |
| } from './api'; |
| import './PostDetailPage.css'; |
| import axios from 'axios'; |
| |
| const API_BASE = process.env.REACT_APP_API_BASE; |
| |
| const PostHeader = ({ post }) => { |
| const anonymousAvatar = '/assets/img/anonymous.jpg'; |
| return ( |
| <div className="post-header"> |
| <div className="author-info"> |
| <img className="avatar" src={post.isAnonymous? anonymousAvatar : post.userProfile.avatar_url} alt="头像" /> |
| <span className="author-name">{post.isAnonymous? '某同学' : post.userProfile.nickname}</span> |
| </div> |
| <h1 className="post-title">{post.title}</h1> |
| </div> |
| ); |
| }; |
| |
| const PostContent = ({ content }) => { |
| return ( |
| <div className="post-content" dangerouslySetInnerHTML={{ __html: content }} /> |
| ); |
| }; |
| |
| const PostActions = ({ post, onLike, onFavorite }) => { |
| return ( |
| <div className="post-actions"> |
| <div className="action-item" onClick={onLike}> |
| <i className={post.liked? 'liked' : 'unliked'} /> |
| <span>{post.likeCount || 0}</span> |
| </div> |
| <div className="action-item" onClick={onFavorite}> |
| <i className={post.favorited? 'favorited' : 'unfavorited'} /> |
| <span>{post.favorites || 0}</span> |
| </div> |
| </div> |
| ); |
| }; |
| |
| const CommentInput = ({ onSubmitComment, isFlag }) => { |
| const [content, setContent] = useState(''); |
| |
| useEffect(() => { |
| if (isFlag) { |
| setContent(''); |
| } |
| }, [isFlag]); |
| |
| const handleSubmit = () => { |
| if (content) { |
| onSubmitComment(content); |
| } |
| }; |
| |
| return ( |
| <div className="comment-input"> |
| <textarea |
| value={content} |
| onChange={(e) => setContent(e.target.value)} |
| placeholder="写下你的评论..." |
| className="comment-textarea" |
| /> |
| <div className="button-container"> |
| <button |
| type="button" |
| onClick={handleSubmit} |
| disabled={!content} |
| className="submit-button" |
| > |
| 发布评论 |
| </button> |
| </div> |
| </div> |
| ); |
| }; |
| |
| const CommentItem = ({ comment, onLikeComment, onReplyComment }) => { |
| const [showReplyInput, setShowReplyInput] = useState(false); |
| const [replyContent, setReplyContent] = useState(''); |
| |
| const queryUserInfo = async (id) => { |
| if (!id) { |
| return; |
| } |
| try { |
| const userData = await getUserInfo(id); |
| console.log(userData); |
| // 这里可以添加跳转逻辑等,比如根据用户ID跳转到对应个人信息页面 |
| } catch (error) { |
| console.error('获取用户信息失败:', error); |
| } |
| }; |
| |
| const handleLike = () => { |
| onLikeComment(comment); |
| }; |
| |
| const handleReply = () => { |
| setShowReplyInput(!showReplyInput); |
| }; |
| |
| const handleSubmitReply = () => { |
| if (replyContent) { |
| onReplyComment(comment, replyContent); |
| setReplyContent(''); |
| setShowReplyInput(false); |
| } |
| }; |
| |
| return ( |
| <div className="comment-item"> |
| <img className="avatar" src={comment.author.avatar_url} alt="头像" onClick={() => queryUserInfo(comment.author.userId)} /> |
| <div className="comment-content"> |
| <div className="comment-author">{comment.author.nickname}</div> |
| <div className="comment-text">{comment.content}</div> |
| <div className="comment-actions"> |
| <div className="action-item" onClick={handleLike}> |
| <i className={comment.liked? 'liked' : 'unliked'} /> |
| <span>{comment.likeCount || 0}</span> |
| </div> |
| <button type="button" onClick={handleReply}>回复</button> |
| </div> |
| {showReplyInput && ( |
| <div className="reply-input"> |
| <textarea |
| value={replyContent} |
| onChange={(e) => setReplyContent(e.target.value)} |
| placeholder="写下你的回复..." |
| className="reply-textarea" |
| /> |
| <button |
| type="button" |
| onClick={handleSubmitReply} |
| disabled={!replyContent} |
| className="submit-button" |
| > |
| 发布回复 |
| </button> |
| </div> |
| )} |
| {comment.replies && comment.replies.length > 0 && ( |
| <div className="reply-list"> |
| {comment.replies.map(reply => ( |
| <CommentItem |
| key={reply.id} |
| comment={reply} |
| onLikeComment={onLikeComment} |
| onReplyComment={onReplyComment} |
| /> |
| ))} |
| </div> |
| )} |
| </div> |
| </div> |
| ); |
| }; |
| |
| const CommentsList = ({ comments, onLikeComment, onReplyComment }) => { |
| return ( |
| <div className="comments-list"> |
| <h2>评论</h2> |
| {comments.map(comment => ( |
| <CommentItem |
| key={comment.id} |
| comment={comment} |
| onLikeComment={onLikeComment} |
| onReplyComment={onReplyComment} |
| /> |
| ))} |
| </div> |
| ); |
| }; |
| |
| const PostDetailPage = () => { |
| const [post, setPost] = useState(null); |
| const [loading, setLoading] = useState(true); |
| const [errorMsg, setErrorMsg] = useState(''); |
| const [comments, setComments] = useState([]); |
| const [isFlag, setIsFlag] = useState(false); |
| |
| const { params } = useRoute('/forum/post/:postId'); |
| const postId = params?.postId; |
| |
| useEffect(() => { |
| const fetchPostDetail = async () => { |
| setLoading(true); |
| setErrorMsg(''); |
| try { |
| const postDetail = await getPostDetail(postId); |
| const postComments = await getPostComments(postId); |
| setPost(postDetail); |
| setComments(postComments); |
| } catch (error) { |
| console.error('获取帖子详情失败:', error); |
| setErrorMsg('加载失败,请稍后重试'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| if (postId) { |
| fetchPostDetail(); |
| } |
| }, [postId]); |
| |
| const handleLike = async () => { |
| if (!post.liked) { |
| try { |
| await likePost(postId); |
| const newPost = { ...post, liked: true, likeCount: post.likeCount + 1 }; |
| setPost(newPost); |
| } catch (err) { |
| console.error('点赞失败:', err); |
| } |
| } else { |
| try { |
| await unlikePost(postId); |
| const newPost = { ...post, liked: false, likeCount: post.likeCount - 1 }; |
| setPost(newPost); |
| } catch (err) { |
| console.error('取消点赞失败:', err); |
| } |
| } |
| }; |
| |
| const handleFavorite = () => { |
| const newPost = { ...post, favorited: !post.favorited, favorites: post.favorited? post.favorites - 1 : post.favorites + 1 }; |
| setPost(newPost); |
| if (newPost.favorited) { |
| axios.post(`${API_BASE}/echo/forum/posts/${postId}/favorite`).catch(err => { |
| console.error('收藏失败:', err); |
| }); |
| } else { |
| axios.delete(`${API_BASE}/echo/forum/posts/${postId}/unfavorite`).catch(err => { |
| console.error('取消收藏失败:', err); |
| }); |
| } |
| }; |
| |
| const likeCommentAction = async (comment) => { |
| if (!comment.liked) { |
| try { |
| await likeComment(comment.id); |
| const newComment = { ...comment, liked: true, likeCount: comment.likeCount + 1 }; |
| setComments(comments.map(c => c.id === comment.id? newComment : c)); |
| } catch (err) { |
| console.error('点赞评论失败:', err); |
| } |
| } else { |
| try { |
| await unlikeComment(comment.id); |
| const newComment = { ...comment, liked: false, likeCount: comment.likeCount - 1 }; |
| setComments(comments.map(c => c.id === comment.id? newComment : c)); |
| } catch (err) { |
| console.error('取消点赞评论失败:', err); |
| } |
| } |
| }; |
| |
| const replyCommentAction = async (comment, replyContent) => { |
| try { |
| await replyToComment(comment.id, replyContent); |
| setIsFlag(true); |
| const commentResponse = await getPostComments(postId); |
| setComments(commentResponse.data); |
| } catch (error) { |
| console.error('回复评论失败:', error); |
| } |
| }; |
| |
| const addCommentAction = async (content) => { |
| try { |
| await addCommentToPost(postId, content); |
| setIsFlag(true); |
| const commentResponse = await getPostComments(postId); |
| setComments(commentResponse.data); |
| } catch (error) { |
| console.error('添加评论失败:', error); |
| } |
| }; |
| |
| if (loading) return <p>加载中...</p>; |
| if (errorMsg) return <p className="error-text">{errorMsg}</p>; |
| if (!post) return <p>没有找到该帖子。</p>; |
| |
| return ( |
| <div className="post-detail-page"> |
| <PostHeader post={post} /> |
| <PostContent content={post.content} /> |
| <PostActions post={post} onLike={handleLike} onFavorite={handleFavorite} /> |
| <CommentInput onSubmitComment={addCommentAction} isFlag={isFlag} /> |
| <CommentsList comments={comments} onLikeComment={likeCommentAction} onReplyComment={replyCommentAction} /> |
| </div> |
| ); |
| }; |
| |
| export default PostDetailPage; |