blob: 01e16482de96b5034762f247086ceb0136253d88 [file] [log] [blame]
22301009237217b2025-04-20 15:15:25 +08001import React, { useEffect, useState } from 'react';
2import { useParams } from 'wouter';
3import { GoodTwo, Star } from '@icon-park/react';
4import { getPostDetail, getPostComments, likePost, unlikePost, addCommentToPost, collectPost } from './api'; // 引入你的 API 函数
Krishya7ec1dd02025-04-19 15:29:03 +08005import './PostDetailPage.css';
22301009237217b2025-04-20 15:15:25 +08006import { useUser } from '../../../context/UserContext'; // 注意路径
Krishyac0f7e9b2025-04-22 15:28:28 +08007import Header from '../../../components/Header';
Krishya7ec1dd02025-04-19 15:29:03 +08008
9const PostDetailPage = () => {
22301009237217b2025-04-20 15:15:25 +080010 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(''); // 新评论内容
16 const [isAnonymous, setIsAnonymous] = useState(false); // 是否匿名
17 const [isLiked, setIsLiked] = useState(false); // 是否已点赞
18 const [isCollected, setIsCollected] = useState(false); // 是否已收藏
19 const [replyToCommentId, setReplyToCommentId] = useState(null); // 回复的评论ID
Krishya7ec1dd02025-04-19 15:29:03 +080020
22301009237217b2025-04-20 15:15:25 +080021 // 获取当前用户ID(假设从上下文中获取)
22 const { user } = useUser(); // 你需要从用户上下文获取用户 ID
Krishya7ec1dd02025-04-19 15:29:03 +080023
22301009237217b2025-04-20 15:15:25 +080024 useEffect(() => {
25 const fetchPostDetail = async () => {
26 setLoading(true);
27 setErrorMsg('');
28 try {
29 // 获取帖子详情
30 const postData = await getPostDetail(postId);
31 setPostDetail(postData);
Krishya7ec1dd02025-04-19 15:29:03 +080032
22301009237217b2025-04-20 15:15:25 +080033 // 获取帖子评论
34 const commentsData = await getPostComments(postId);
35 setComments(commentsData);
Krishya7ec1dd02025-04-19 15:29:03 +080036
22301009237217b2025-04-20 15:15:25 +080037 // 设置是否已经点赞
38 if (postData.likedByUser) {
39 setIsLiked(true);
Krishya7ec1dd02025-04-19 15:29:03 +080040 } else {
22301009237217b2025-04-20 15:15:25 +080041 setIsLiked(false);
Krishya7ec1dd02025-04-19 15:29:03 +080042 }
Krishya7ec1dd02025-04-19 15:29:03 +080043
22301009237217b2025-04-20 15:15:25 +080044 // 设置是否已经收藏
45 if (postData.collectedByUser) {
46 setIsCollected(true);
Krishya7ec1dd02025-04-19 15:29:03 +080047 } else {
22301009237217b2025-04-20 15:15:25 +080048 setIsCollected(false);
Krishya7ec1dd02025-04-19 15:29:03 +080049 }
22301009237217b2025-04-20 15:15:25 +080050 } catch (err) {
51 console.error('加载失败:', err);
52 setErrorMsg('加载失败,请稍后重试');
53 } finally {
54 setLoading(false);
55 }
Krishya7ec1dd02025-04-19 15:29:03 +080056 };
57
22301009237217b2025-04-20 15:15:25 +080058 fetchPostDetail();
59 }, [postId]);
Krishya7ec1dd02025-04-19 15:29:03 +080060
22301009237217b2025-04-20 15:15:25 +080061 // 点赞功能
62 const toggleLike = async () => {
63 if (!user) {
64 alert('请先登录');
65 return;
66 }
Krishya7ec1dd02025-04-19 15:29:03 +080067
22301009237217b2025-04-20 15:15:25 +080068 try {
69 if (isLiked) {
70 // 取消点赞
71 await unlikePost(postId, user.id);
72 setIsLiked(false);
73 setPostDetail((prev) => ({
74 ...prev,
75 postLikeNum: prev.postLikeNum - 1,
76 }));
77 } else {
78 // 点赞
79 await likePost(postId, user.id);
80 setIsLiked(true);
81 setPostDetail((prev) => ({
82 ...prev,
83 postLikeNum: prev.postLikeNum + 1,
84 }));
85 }
86 } catch (err) {
87 console.error('点赞失败:', err);
88 alert('点赞失败,请稍后再试');
89 }
90 };
Krishya7ec1dd02025-04-19 15:29:03 +080091
22301009237217b2025-04-20 15:15:25 +080092 // 收藏功能
93 const toggleCollect = async () => {
94 if (!user) {
95 alert('请先登录');
96 return;
97 }
Krishya7ec1dd02025-04-19 15:29:03 +080098
22301009237217b2025-04-20 15:15:25 +080099 try {
100 const action = isCollected ? 'cancel' : 'collect';
101 // 调用收藏 API
102 await collectPost(postId, user.id, action);
103 setIsCollected(!isCollected);
104 setPostDetail((prev) => ({
105 ...prev,
106 postCollectNum: isCollected ? prev.postCollectNum - 1 : prev.postCollectNum + 1,
107 }));
108 } catch (err) {
109 console.error('收藏失败:', err);
110 alert('收藏失败,请稍后再试');
111 }
112 };
113
114 // 添加评论
115 const handleAddComment = async () => {
116 if (!newComment.trim()) {
117 alert('评论内容不能为空');
118 return;
119 }
120
121 try {
122 // 调用 API 添加评论,若为回复评论则传递父评论ID(com_comment_id)
123 const commentData = await addCommentToPost(postId, user.id, newComment, isAnonymous, replyToCommentId);
124 // 更新评论列表
125 setComments((prev) => [
126 ...prev,
127 {
128 commentId: commentData.commentId,
129 post_id: postId,
130 userId: user.id,
131 content: newComment,
132 isAnonymous,
133 commentTime: new Date().toISOString(),
134 comCommentId: replyToCommentId, // 回复评论时传递父评论ID
135 },
136 ]);
137 // 清空评论框和回复状态
138 setNewComment('');
139 setReplyToCommentId(null);
140 } catch (err) {
141 console.error('评论添加失败:', err);
142 alert('评论失败,请稍后再试');
143 }
144 };
145
146 // 回复评论
147 const handleReply = (commentId) => {
148 setReplyToCommentId(commentId); // 设置父评论ID为当前评论的ID
149 };
150
151 return (
152 <div className="post-detail-page">
Krishyac0f7e9b2025-04-22 15:28:28 +0800153 <Header />
22301009237217b2025-04-20 15:15:25 +0800154 {loading ? (
155 <p>加载中...</p>
156 ) : errorMsg ? (
157 <p className="error-text">{errorMsg}</p>
158 ) : postDetail ? (
159 <div className="post-detail">
160 <h1>{postDetail.title}</h1>
161 <div className="post-meta">
Krishya1300cad2025-04-20 22:16:45 +0800162 <span className="post-user">用户ID: {postDetail.user_id}</span>
22301009237217b2025-04-20 15:15:25 +0800163 <span className="post-time">
Krishya1300cad2025-04-20 22:16:45 +0800164 发布时间:{new Date(postDetail.postTime).toLocaleString()}
22301009237217b2025-04-20 15:15:25 +0800165 </span>
22301009237217b2025-04-20 15:15:25 +0800166 </div>
167 <div className="post-content">
168 <p>{postDetail.postContent}</p>
Krishya1300cad2025-04-20 22:16:45 +0800169 {Array.isArray(postDetail.imgUrl) ? (
170 <div className="post-images">
171 {postDetail.imgUrl.map((url, idx) => (
172 <img key={idx} src={url} alt={`图片${idx}`} />
173 ))}
174 </div>
175 ) : (
176 postDetail.imgUrl && (
177 <img className="post-image" src={postDetail.imgUrl} alt="帖子图片" />
178 )
22301009237217b2025-04-20 15:15:25 +0800179 )}
Krishya1300cad2025-04-20 22:16:45 +0800180
22301009237217b2025-04-20 15:15:25 +0800181 </div>
182
183 {/* 点赞和收藏 */}
184 <div className="post-actions">
185 <button
186 className="icon-btn"
187 onClick={toggleLike} // 点赞操作
188 >
189 <GoodTwo
190 theme="outline"
Krishya1300cad2025-04-20 22:16:45 +0800191 size="20"
22301009237217b2025-04-20 15:15:25 +0800192 fill={isLiked ? '#f00' : '#ccc'} // 如果已点赞,显示红色
193 />
194 <span>{postDetail.postLikeNum}</span>
195 </button>
196 <button
197 className="icon-btn"
198 onClick={toggleCollect} // 收藏操作
199 >
200 <Star
201 theme="outline"
Krishya1300cad2025-04-20 22:16:45 +0800202 size="20"
22301009237217b2025-04-20 15:15:25 +0800203 fill={isCollected ? '#ffd700' : '#ccc'} // 如果已收藏,显示金色
204 />
205 <span>{postDetail.postCollectNum}</span>
206 </button>
207 </div>
Krishya1300cad2025-04-20 22:16:45 +0800208
209 <hr className="divider" />
22301009237217b2025-04-20 15:15:25 +0800210 {/* 评论部分 */}
Krishya1300cad2025-04-20 22:16:45 +0800211 <h3>评论区</h3>
22301009237217b2025-04-20 15:15:25 +0800212 <div className="comments-section">
22301009237217b2025-04-20 15:15:25 +0800213 {comments.length ? (
214 comments.map((comment) => (
215 <div key={comment.commentId} className="comment">
Krishya1300cad2025-04-20 22:16:45 +0800216 <div className="comment-header">
22301009237217b2025-04-20 15:15:25 +0800217 <span className="comment-user">用户 ID: {comment.userId}</span>
Krishya1300cad2025-04-20 22:16:45 +0800218 <button className="reply-btn" onClick={() => handleReply(comment.commentId)}>回复</button>
22301009237217b2025-04-20 15:15:25 +0800219 </div>
Krishya1300cad2025-04-20 22:16:45 +0800220 <p className="comment-content">{comment.content}</p>
221 <div className="comment-time">
222 {new Date(comment.commentTime).toLocaleString()}
223 </div>
22301009237217b2025-04-20 15:15:25 +0800224
225 {/* 回复框,只有在当前评论是正在回复的评论时显示 */}
226 {replyToCommentId === comment.commentId && (
227 <div className="reply-form">
228 <textarea
229 placeholder="输入你的回复..."
230 value={newComment}
231 onChange={(e) => setNewComment(e.target.value)}
232 />
233 <div className="comment-options">
234 <label>
235 <input
236 type="checkbox"
237 checked={isAnonymous}
238 onChange={() => setIsAnonymous(!isAnonymous)}
239 />
240 匿名评论
241 </label>
242 <button onClick={handleAddComment}>发布回复</button>
243 </div>
244 </div>
245 )}
246 </div>
247 ))
248 ) : (
249 <p>暂无评论</p>
250 )}
251
252 {/* 添加评论表单 */}
253 <div className="add-comment-form">
254 <textarea
255 placeholder="输入你的评论..."
256 value={newComment}
257 onChange={(e) => setNewComment(e.target.value)}
258 />
259 <div className="comment-options">
260 <label>
261 <input
262 type="checkbox"
263 checked={isAnonymous}
264 onChange={() => setIsAnonymous(!isAnonymous)}
265 />
266 匿名评论
267 </label>
268 <button onClick={handleAddComment}>发布评论</button>
269 </div>
270 </div>
271 </div>
Krishya7ec1dd02025-04-19 15:29:03 +0800272 </div>
22301009237217b2025-04-20 15:15:25 +0800273 ) : (
274 <p>帖子不存在</p>
275 )}
276 </div>
277 );
Krishya7ec1dd02025-04-19 15:29:03 +0800278};
279
22301009237217b2025-04-20 15:15:25 +0800280export default PostDetailPage;