| import React, { useState, useEffect } from "react"; |
| import { useParams } from "react-router-dom"; |
| import { API_BASE_URL } from "./config"; |
| |
| export default function PostDetailPage() { |
| const { postId } = useParams(); |
| const [post, setPost] = useState(null); |
| const [replies, setReplies] = useState([]); |
| const [newReply, setNewReply] = useState(''); |
| |
| // function to load post details and its replies |
| const fetchDetail = () => { |
| fetch(`${API_BASE_URL}/api/forum-detail?postid=${postId}`) |
| .then(res => res.json()) |
| .then(data => { |
| console.log("Fetched post detail:", data); |
| const p = data.post || data; |
| const formattedPost = { |
| post_id: p.postid, |
| title: p.posttitle, |
| content: p.postcontent, |
| author_id: p.postuserid, |
| author_name: p.author?.username || '', |
| created_at: new Date(p.posttime).toLocaleString(), |
| reply_count: p.replytime, |
| view_count: p.readtime, |
| }; |
| const formattedReplies = (data.replies || []).map(r => ({ |
| reply_id: r.replyid, |
| post_id: r.postid || postId, |
| content: r.content, |
| author_id: r.authorid, |
| author_name: r.author?.username || '', |
| created_at: new Date(r.createdAt).toLocaleString(), |
| })); |
| setPost(formattedPost); |
| setReplies(formattedReplies); |
| }) |
| .catch(err => console.error(err)); |
| }; |
| |
| useEffect(() => { |
| fetchDetail(); |
| }, [postId]); |
| |
| // post a new reply to backend |
| const handleReply = () => { |
| const match = document.cookie.match('(^|;)\\s*userId=([^;]+)'); |
| const userId = match ? match[2] : null; |
| if (!userId) { |
| alert('请先登录后再回复'); |
| return; |
| } |
| fetch(`${API_BASE_URL}/api/forum-reply`, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ postid: postId, replycontent: newReply, replyuserid: userId }), |
| }) |
| .then(res => res.json()) |
| .then(() => { |
| setNewReply(''); |
| fetchDetail(); |
| }) |
| .catch(err => console.error(err)); |
| }; |
| |
| if (!post) return <div>加载中...</div>; |
| return ( |
| <div style={{ maxWidth: 700, margin: "40px auto" }}> |
| {/* 原帖 */} |
| <div style={{ |
| background: "#fff", |
| borderRadius: 12, |
| boxShadow: "0 2px 8px #e0e7ff", |
| padding: 24, |
| marginBottom: 32, |
| }}> |
| <div style={{ fontSize: 15, color: "#1976d2", marginBottom: 6 }}> |
| {post.author_name} · {post.created_at} |
| </div> |
| <div style={{ fontWeight: 600, fontSize: 20, marginBottom: 8, color: "#222" }}> |
| {post.title} |
| </div> |
| <div style={{ fontSize: 16, color: "#444", marginBottom: 18 }}> |
| {post.content} |
| </div> |
| <div style={{ display: "flex", justifyContent: "flex-start", gap: 32, fontSize: 14, color: "#888" }}> |
| <span>回复数: {post.reply_count}</span> |
| <span>观看数: {post.view_count}</span> |
| </div> |
| </div> |
| {/* 评论区 */} |
| <div> |
| {replies.map(reply => ( |
| <div key={reply.reply_id} style={{ |
| display: "flex", |
| alignItems: "center", |
| background: "#f8faff", |
| borderRadius: 8, |
| padding: "12px 18px", |
| marginBottom: 16, |
| fontSize: 15, |
| }}> |
| <span style={{ color: "#1976d2", fontWeight: 500, minWidth: 80 }}>{reply.author_name}</span> |
| <span style={{ flex: 1, marginLeft: 18 }}>{reply.content}</span> |
| <span style={{ color: "#888", fontSize: 13, marginLeft: 18 }}>{reply.created_at}</span> |
| </div> |
| ))} |
| </div> |
| {/* 回复框 */} |
| <div style={{ |
| marginTop: 32, |
| background: "#fff", |
| borderRadius: 8, |
| boxShadow: "0 2px 8px #e0e7ff", |
| padding: 18, |
| display: "flex", |
| gap: 12, |
| alignItems: "center" |
| }}> |
| <input |
| type="text" |
| placeholder="写下你的回复..." |
| value={newReply} |
| onChange={e => setNewReply(e.target.value)} |
| style={{ |
| flex: 1, |
| border: "1px solid #bfcfff", |
| borderRadius: 6, |
| padding: "8px 12px", |
| fontSize: 15, |
| }} |
| /> |
| <button onClick={handleReply} style={{ |
| background: "#1976d2", |
| color: "#fff", |
| border: "none", |
| borderRadius: 6, |
| padding: "8px 24px", |
| fontSize: 15, |
| cursor: "pointer" |
| }}>回复</button> |
| </div> |
| </div> |
| ); |
| } |