更新论坛功能

Change-Id: I0efd26d35cc4abb0c6d51ff1bff2cfd738f986dd
diff --git a/src/pages/Forum/posts-detail/PostDetailPage.jsx b/src/pages/Forum/posts-detail/PostDetailPage.jsx
new file mode 100644
index 0000000..c0f218f
--- /dev/null
+++ b/src/pages/Forum/posts-detail/PostDetailPage.jsx
@@ -0,0 +1,310 @@
+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;
\ No newline at end of file