blob: 009ba6c279a3b963b15b708214dffc6f7f239e44 [file] [log] [blame]
22301008ba662fe2025-06-20 18:10:20 +08001import React, { useState, useEffect, useCallback } from 'react'
95630366980c1f272025-06-20 14:08:54 +08002import { useParams, useNavigate } from 'react-router-dom'
3import { ArrowLeft, ThumbsUp, MessageCircle, Share2, BookmarkPlus, Heart, Eye } from 'lucide-react'
4import { searchAPI } from '../api/search_jwlll'
22301008ba662fe2025-06-20 18:10:20 +08005import { getUserInfo } from '../utils/auth'
95630366980c1f272025-06-20 14:08:54 +08006import '../style/PostDetail.css'
7
8export default function PostDetail() {
9 const { id } = useParams()
10 const navigate = useNavigate()
11 const [post, setPost] = useState(null)
12 const [loading, setLoading] = useState(true)
13 const [error, setError] = useState(null)
14 const [liked, setLiked] = useState(false)
15 const [bookmarked, setBookmarked] = useState(false)
16 const [likeCount, setLikeCount] = useState(0)
17 const [comments, setComments] = useState([])
18 const [newComment, setNewComment] = useState('')
19 const [showComments, setShowComments] = useState(false)
22301008ba662fe2025-06-20 18:10:20 +080020 // 获取当前用户ID
21 const getCurrentUserId = () => {
22 const userInfo = getUserInfo()
23 return userInfo?.id || '3' // 如果未登录或无用户信息,使用默认值3
24 }
95630366980c1f272025-06-20 14:08:54 +080025
22301008ba662fe2025-06-20 18:10:20 +080026 const fetchPostDetail = useCallback(async () => {
95630366980c1f272025-06-20 14:08:54 +080027 setLoading(true)
28 setError(null)
29 try {
30 const data = await searchAPI.getPostDetail(id)
31 setPost(data)
32 setLikeCount(data.heat || 0)
33 } catch (error) {
34 console.error('获取帖子详情失败:', error)
35 setError('帖子不存在或已被删除')
36 } finally {
37 setLoading(false)
38 }
22301008ba662fe2025-06-20 18:10:20 +080039 }, [id])
95630366980c1f272025-06-20 14:08:54 +080040
22301008ba662fe2025-06-20 18:10:20 +080041 const fetchComments = useCallback(async () => {
95630366980c1f272025-06-20 14:08:54 +080042 try {
43 const data = await searchAPI.getComments(id)
44 setComments(data.comments || [])
45 } catch (error) {
46 console.error('获取评论失败:', error)
47 }
22301008ba662fe2025-06-20 18:10:20 +080048 }, [id])
49
50 useEffect(() => {
51 fetchPostDetail()
52 fetchComments()
53 }, [fetchPostDetail, fetchComments])
95630366980c1f272025-06-20 14:08:54 +080054
55 const handleBack = () => {
56 navigate(-1)
57 }
95630366980c1f272025-06-20 14:08:54 +080058 const handleLike = async () => {
59 try {
22301008ba662fe2025-06-20 18:10:20 +080060 const currentUserId = getCurrentUserId()
95630366980c1f272025-06-20 14:08:54 +080061 const newLiked = !liked
62 if (newLiked) {
22301008ba662fe2025-06-20 18:10:20 +080063 await searchAPI.likePost(id, currentUserId)
95630366980c1f272025-06-20 14:08:54 +080064 } else {
22301008ba662fe2025-06-20 18:10:20 +080065 await searchAPI.unlikePost(id, currentUserId)
95630366980c1f272025-06-20 14:08:54 +080066 }
67 setLiked(newLiked)
68 setLikeCount(prev => newLiked ? prev + 1 : prev - 1)
69 } catch (error) {
70 console.error('点赞失败:', error)
71 // 回滚状态
72 setLiked(!liked)
73 setLikeCount(prev => liked ? prev + 1 : prev - 1)
74 }
75 }
76
77 const handleBookmark = () => {
78 setBookmarked(!bookmarked)
79 // 实际项目中这里应该调用后端API保存收藏状态
80 }
81
82 const handleShare = () => {
83 // 分享功能
84 if (navigator.share) {
85 navigator.share({
86 title: post?.title,
87 text: post?.content,
88 url: window.location.href,
89 })
90 } else {
91 // 复制链接到剪贴板
92 navigator.clipboard.writeText(window.location.href)
93 alert('链接已复制到剪贴板')
94 }
95 }
95630366980c1f272025-06-20 14:08:54 +080096 const handleAddComment = async (e) => {
97 e.preventDefault()
98 if (!newComment.trim()) return
99
100 try {
22301008ba662fe2025-06-20 18:10:20 +0800101 const currentUserId = getCurrentUserId()
102 await searchAPI.addComment(id, currentUserId, newComment)
95630366980c1f272025-06-20 14:08:54 +0800103 setNewComment('')
104 fetchComments() // 刷新评论列表
105 } catch (error) {
106 console.error('添加评论失败:', error)
107 alert('评论失败,请重试')
108 }
109 }
110
111 if (loading) {
112 return (
113 <div className="post-detail">
114 <div className="loading-container">
115 <div className="loading-spinner"></div>
116 <p>加载中...</p>
117 </div>
118 </div>
119 )
120 }
121
122 if (error) {
123 return (
124 <div className="post-detail">
125 <div className="error-container">
126 <h2>😔 出错了</h2>
127 <p>{error}</p>
128 <button onClick={handleBack} className="back-btn">
129 <ArrowLeft size={20} />
130 返回
131 </button>
132 </div>
133 </div>
134 )
135 }
136
137 if (!post) {
138 return (
139 <div className="post-detail">
140 <div className="error-container">
141 <h2>😔 帖子不存在</h2>
142 <p>该帖子可能已被删除或不存在</p>
143 <button onClick={handleBack} className="back-btn">
144 <ArrowLeft size={20} />
145 返回
146 </button>
147 </div>
148 </div>
149 )
150 }
151
152 return (
153 <div className="post-detail">
154 {/* 顶部导航栏 */}
155 <header className="post-header">
156 <button onClick={handleBack} className="back-btn">
157 <ArrowLeft size={20} />
158 返回
159 </button>
160 <div className="header-actions">
161 <button onClick={handleShare} className="action-btn">
162 <Share2 size={20} />
163 </button>
164 <button
165 onClick={handleBookmark}
166 className={`action-btn ${bookmarked ? 'active' : ''}`}
167 >
168 <BookmarkPlus size={20} />
169 </button>
170 </div>
171 </header>
172
173 {/* 主要内容区 */}
174 <main className="post-content">
175 {/* 帖子标题 */}
176 <h1 className="post-title">{post.title}</h1>
177
178 {/* 作者信息和元数据 */}
179 <div className="post-meta">
180 <div className="author-info">
181 <div className="avatar">
182 {post.author ? post.author.charAt(0).toUpperCase() : 'U'}
183 </div>
184 <div className="author-details">
185 <span className="author-name">{post.author || '匿名用户'}</span>
186 <span className="post-date">
187 {post.create_time ? new Date(post.create_time).toLocaleDateString('zh-CN') : '未知时间'}
188 </span>
189 </div>
190 </div>
191 <div className="post-stats">
192 <span className="stat-item">
193 <Eye size={16} />
194 {post.views || 0}
195 </span>
196 <span className="stat-item">
197 <Heart size={16} />
198 {likeCount}
199 </span>
200 </div>
201 </div>
202
203 {/* 标签 */}
204 {post.tags && post.tags.length > 0 && (
205 <div className="post-tags">
206 {post.tags.map((tag, index) => (
207 <span key={index} className="tag">{tag}</span>
208 ))}
209 </div>
210 )}
211
212 {/* 帖子正文 */}
213 <div className="post-body">
214 <p>{post.content}</p>
215 </div>
216
217 {/* 类别信息 */}
218 {(post.category || post.type) && (
219 <div className="post-category">
220 {post.category && (
221 <>
222 <span className="category-label">分类:</span>
223 <span className="category-name">{post.category}</span>
224 </>
225 )}
226 {post.type && (
227 <>
228 <span className="category-label" style={{marginLeft: '1em'}}>类型:</span>
229 <span className="category-name">{post.type}</span>
230 </>
231 )}
232 </div>
233 )}
234
235 {/* 评论区 */}
236 <div className="comments-section">
237 <div className="comments-header">
238 <button
239 onClick={() => setShowComments(!showComments)}
240 className="comments-toggle"
241 >
242 <MessageCircle size={20} />
243 评论 ({comments.length})
244 </button>
245 </div>
246
247 {showComments && (
248 <div className="comments-content">
249 {/* 添加评论 */}
250 <form onSubmit={handleAddComment} className="comment-form">
251 <textarea
252 value={newComment}
253 onChange={(e) => setNewComment(e.target.value)}
254 placeholder="写下你的评论..."
255 className="comment-input"
256 rows={3}
257 />
258 <button type="submit" className="comment-submit">
259 发布评论
260 </button>
261 </form>
262
263 {/* 评论列表 */}
264 <div className="comments-list">
265 {comments.length === 0 ? (
266 <p className="no-comments">暂无评论</p>
267 ) : (
268 comments.map((comment, index) => (
269 <div key={index} className="comment-item">
270 <div className="comment-author">
271 <div className="comment-avatar">
272 {comment.user_name ? comment.user_name.charAt(0).toUpperCase() : 'U'}
273 </div>
274 <span className="comment-name">{comment.user_name || '匿名用户'}</span>
275 <span className="comment-time">
276 {comment.create_time ? new Date(comment.create_time).toLocaleString('zh-CN') : ''}
277 </span>
278 </div>
279 <div className="comment-content">
280 {comment.content}
281 </div>
282 </div>
283 ))
284 )}
285 </div>
286 </div>
287 )}
288 </div>
289 </main>
290
291 {/* 底部操作栏 */}
292 <footer className="post-footer">
293 <div className="action-bar">
294 <button
295 onClick={handleLike}
296 className={`action-button ${liked ? 'liked' : ''}`}
297 >
298 <ThumbsUp size={20} />
299 <span>{likeCount}</span>
300 </button>
301
302 <button
303 onClick={() => setShowComments(!showComments)}
304 className="action-button"
305 >
306 <MessageCircle size={20} />
307 <span>评论</span>
308 </button>
309
310 <button onClick={handleShare} className="action-button">
311 <Share2 size={20} />
312 <span>分享</span>
313 </button>
314
315 <button
316 onClick={handleBookmark}
317 className={`action-button ${bookmarked ? 'bookmarked' : ''}`}
318 >
319 <BookmarkPlus size={20} />
320 <span>收藏</span>
321 </button>
322 </div>
323 </footer>
324 </div>
325 )
326}