修复帖子点赞和收藏功能
Change-Id: If66f9065607d97fb9527f0905b3465e6cd5b5995
diff --git a/src/pages/Forum/posts-detail/PostDetailPage.jsx b/src/pages/Forum/posts-detail/PostDetailPage.jsx
index c0f218f..5186339 100644
--- a/src/pages/Forum/posts-detail/PostDetailPage.jsx
+++ b/src/pages/Forum/posts-detail/PostDetailPage.jsx
@@ -1,310 +1,273 @@
-import React, { useState, useEffect } from 'react';
-import { useRoute } from 'wouter';
-import {
- getPostDetail,
- getPostComments,
- likePost,
- unlikePost,
- addCommentToPost,
- replyToComment,
- likeComment,
- unlikeComment,
- getUserInfo
-} from './api';
+import React, { useEffect, useState } from 'react';
+import { useParams } from 'wouter';
+import { GoodTwo, Star } from '@icon-park/react';
+import { getPostDetail, getPostComments, likePost, unlikePost, addCommentToPost, collectPost } from './api'; // 引入你的 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>
- );
-};
+import { useUser } from '../../../context/UserContext'; // 注意路径
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 { 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 { params } = useRoute('/forum/post/:postId');
- const postId = params?.postId;
+ // 获取当前用户ID(假设从上下文中获取)
+ const { user } = useUser(); // 你需要从用户上下文获取用户 ID
- 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);
- }
- };
+ useEffect(() => {
+ const fetchPostDetail = async () => {
+ setLoading(true);
+ setErrorMsg('');
+ try {
+ // 获取帖子详情
+ const postData = await getPostDetail(postId);
+ setPostDetail(postData);
- if (postId) {
- fetchPostDetail();
- }
- }, [postId]);
+ // 获取帖子评论
+ const commentsData = await getPostComments(postId);
+ setComments(commentsData);
- 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);
- }
+ // 设置是否已经点赞
+ if (postData.likedByUser) {
+ setIsLiked(true);
} else {
- try {
- await unlikePost(postId);
- const newPost = { ...post, liked: false, likeCount: post.likeCount - 1 };
- setPost(newPost);
- } catch (err) {
- console.error('取消点赞失败:', err);
- }
+ setIsLiked(false);
}
- };
- 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);
- });
+ // 设置是否已经收藏
+ if (postData.collectedByUser) {
+ setIsCollected(true);
} else {
- axios.delete(`${API_BASE}/echo/forum/posts/${postId}/unfavorite`).catch(err => {
- console.error('取消收藏失败:', err);
- });
+ setIsCollected(false);
}
+ } catch (err) {
+ console.error('加载失败:', err);
+ setErrorMsg('加载失败,请稍后重试');
+ } finally {
+ setLoading(false);
+ }
};
- 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);
- }
- }
- };
+ fetchPostDetail();
+ }, [postId]);
- 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 toggleLike = async () => {
+ if (!user) {
+ alert('请先登录');
+ return;
+ }
- const addCommentAction = async (content) => {
- try {
- await addCommentToPost(postId, content);
- setIsFlag(true);
- const commentResponse = await getPostComments(postId);
- setComments(commentResponse.data);
- } catch (error) {
- console.error('添加评论失败:', error);
- }
- };
+ try {
+ if (isLiked) {
+ // 取消点赞
+ await unlikePost(postId, user.id);
+ setIsLiked(false);
+ setPostDetail((prev) => ({
+ ...prev,
+ postLikeNum: prev.postLikeNum - 1,
+ }));
+ } else {
+ // 点赞
+ await likePost(postId, user.id);
+ setIsLiked(true);
+ setPostDetail((prev) => ({
+ ...prev,
+ postLikeNum: prev.postLikeNum + 1,
+ }));
+ }
+ } catch (err) {
+ console.error('点赞失败:', err);
+ alert('点赞失败,请稍后再试');
+ }
+ };
- if (loading) return <p>加载中...</p>;
- if (errorMsg) return <p className="error-text">{errorMsg}</p>;
- if (!post) return <p>没有找到该帖子。</p>;
+ // 收藏功能
+ const toggleCollect = async () => {
+ if (!user) {
+ alert('请先登录');
+ return;
+ }
- 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} />
+ try {
+ const action = isCollected ? 'cancel' : 'collect';
+ // 调用收藏 API
+ await collectPost(postId, user.id, action);
+ setIsCollected(!isCollected);
+ setPostDetail((prev) => ({
+ ...prev,
+ postCollectNum: isCollected ? prev.postCollectNum - 1 : prev.postCollectNum + 1,
+ }));
+ } catch (err) {
+ console.error('收藏失败:', err);
+ alert('收藏失败,请稍后再试');
+ }
+ };
+
+ // 添加评论
+ const handleAddComment = async () => {
+ if (!newComment.trim()) {
+ alert('评论内容不能为空');
+ return;
+ }
+
+ try {
+ // 调用 API 添加评论,若为回复评论则传递父评论ID(com_comment_id)
+ const commentData = await addCommentToPost(postId, user.id, newComment, isAnonymous, replyToCommentId);
+ // 更新评论列表
+ setComments((prev) => [
+ ...prev,
+ {
+ commentId: commentData.commentId,
+ post_id: postId,
+ userId: user.id,
+ content: newComment,
+ isAnonymous,
+ commentTime: new Date().toISOString(),
+ comCommentId: replyToCommentId, // 回复评论时传递父评论ID
+ },
+ ]);
+ // 清空评论框和回复状态
+ setNewComment('');
+ setReplyToCommentId(null);
+ } catch (err) {
+ console.error('评论添加失败:', err);
+ alert('评论失败,请稍后再试');
+ }
+ };
+
+ // 回复评论
+ const handleReply = (commentId) => {
+ setReplyToCommentId(commentId); // 设置父评论ID为当前评论的ID
+ };
+
+ return (
+ <div className="post-detail-page">
+ {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-time">
+ {new Date(postDetail.postTime).toLocaleString()}
+ </span>
+ <span className="post-user">用户 ID: {postDetail.user_id}</span>
+ </div>
+ <div className="post-content">
+ <p>{postDetail.postContent}</p>
+ {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="24"
+ fill={isLiked ? '#f00' : '#ccc'} // 如果已点赞,显示红色
+ />
+ <span>{postDetail.postLikeNum}</span>
+ </button>
+ <button
+ className="icon-btn"
+ onClick={toggleCollect} // 收藏操作
+ >
+ <Star
+ theme="outline"
+ size="24"
+ fill={isCollected ? '#ffd700' : '#ccc'} // 如果已收藏,显示金色
+ />
+ <span>{postDetail.postCollectNum}</span>
+ </button>
+ </div>
+
+ {/* 评论部分 */}
+ <div className="comments-section">
+ <h3>评论区</h3>
+ {comments.length ? (
+ comments.map((comment) => (
+ <div key={comment.commentId} className="comment">
+ <p>{comment.content}</p>
+ <div className="comment-meta">
+ <span className="comment-time">
+ {new Date(comment.commentTime).toLocaleString()}
+ </span>
+ <span className="comment-user">用户 ID: {comment.userId}</span>
+ </div>
+ {/* 回复按钮 */}
+ <button onClick={() => handleReply(comment.commentId)}>回复</button>
+
+ {/* 回复框,只有在当前评论是正在回复的评论时显示 */}
+ {replyToCommentId === comment.commentId && (
+ <div className="reply-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>
+ ))
+ ) : (
+ <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;
\ No newline at end of file
+export default PostDetailPage;