blob: f51e679334406ea17c71674da841ab852b2b901d [file] [log] [blame]
Krishyae71688a2025-04-10 21:25:17 +08001import React, { useState, useEffect } from 'react';
2import { Link } from 'wouter';
3import axios from 'axios';
4import { GoodTwo, Comment } from '@icon-park/react';
22301009e8513282025-04-15 21:57:12 +08005import Header from '../../components/Header';
Krishyae71688a2025-04-10 21:25:17 +08006import './ForumPage.css';
7
8const API_BASE = process.env.REACT_APP_API_BASE;
9
10const ForumPage = () => {
11 const [posts, setPosts] = useState([]);
12 const [total, setTotal] = useState(0);
13 const [page, setPage] = useState(1);
14 const [size, setSize] = useState(10);
15 const [loading, setLoading] = useState(true);
16 const [errorMsg, setErrorMsg] = useState('');
17
18 const totalPages = Math.ceil(total / size);
19
20 useEffect(() => {
21 const fetchPosts = async () => {
22 setLoading(true);
23 setErrorMsg('');
24 try {
25 const response = await axios.get(`${API_BASE}/echo/forum/posts/getAllPost`, {
26 params: { page, size }
27 });
28 const postsData = response.data.posts || [];
29
30 const userIds = [...new Set(postsData.map(post => post.user_id))];
31 const userProfiles = await Promise.all(
32 userIds.map(async id => {
33 try {
34 const res = await axios.get(`${API_BASE}/echo/user/profile`, {
35 params: { user_id: id }
36 });
37 return { id, profile: res.data };
38 } catch {
39 return { id, profile: { nickname: '未知用户', avatar_url: 'default-avatar.png' } };
40 }
41 })
42 );
43
44 const userMap = {};
45 userProfiles.forEach(({ id, profile }) => {
46 userMap[id] = profile;
47 });
48
49 const postsWithProfiles = postsData.map(post => ({
50 ...post,
51 userProfile: userMap[post.user_id] || { nickname: '未知用户', avatar_url: 'default-avatar.png' }
52 }));
53
54 setPosts(postsWithProfiles);
55 setTotal(response.data.total || 0);
56 } catch (error) {
57 console.error('获取帖子失败:', error);
58 setErrorMsg('加载失败,请稍后重试');
59 } finally {
60 setLoading(false);
61 }
62 };
63 fetchPosts();
64 }, [page, size]);
65
66 const toggleLike = async (postId, liked) => {
67 try {
68 if (liked) {
69 await axios.delete(`${API_BASE}/echo/forum/posts/${postId}/unlike`);
70 } else {
71 await axios.post(`${API_BASE}/echo/forum/posts/${postId}/like`);
72 }
73
74 setPosts(prevPosts =>
75 prevPosts.map(post =>
76 post.id === postId
77 ? {
78 ...post,
79 liked: !liked,
80 likeCount: liked ? post.likeCount - 1 : post.likeCount + 1
81 }
82 : post
83 )
84 );
85 } catch (error) {
86 console.error('点赞操作失败:', error);
87 }
88 };
89
90 return (
91 <div className="forum-page">
22301009e8513282025-04-15 21:57:12 +080092 <Header /> {/* 使用 Header 组件 */}
Krishyae71688a2025-04-10 21:25:17 +080093 <div className="forum-content">
94 <h2>论坛帖子列表</h2>
95
96 {loading ? (
97 <p>加载中...</p>
98 ) : errorMsg ? (
99 <p className="error-text">{errorMsg}</p>
100 ) : posts.length === 0 ? (
101 <p>暂无帖子。</p>
102 ) : (
103 <div className="post-list">
104 {posts.map(post => (
105 <div key={post.id} className="post-card">
106 <div className="post-card-top">
107 <div className="user-info">
108 <img className="avatar" src={post.userProfile.avatar_url} alt="头像" />
109 <span className="nickname">{post.userProfile.nickname}</span>
110 </div>
111 {post.cover_image_url && (
112 <img className="cover-image" src={post.cover_image_url} alt="封面" />
113 )}
114 </div>
115 <h3>{post.title}</h3>
116 <p className="post-meta">
117 发布时间:{new Date(post.created_at).toLocaleString()}
118 </p>
119 <div className="post-actions">
120 <button
121 className="icon-btn"
122 onClick={() => toggleLike(post.id, post.liked)}
123 >
124 <GoodTwo theme="outline" size="24" fill={post.liked ? '#f00' : '#fff'} />
125 <span>{post.likeCount || 0}</span>
126 </button>
127 <Link href={`/forum/post/${post.id}`} className="icon-btn">
128 <Comment theme="outline" size="24" fill="#fff" />
129 <span>{post.commentCount || 0}</span>
130 </Link>
131 </div>
132 <Link href={`/forum/post/${post.id}`} className="btn-secondary">查看详情</Link>
133 </div>
134 ))}
135 </div>
136 )}
137
138 <div className="pagination">
139 <button disabled={page === 1} onClick={() => setPage(page - 1)}>上一页</button>
140 <span> {page} / {totalPages} 页</span>
141 <button disabled={page === totalPages} onClick={() => setPage(page + 1)}>下一页</button>
142 </div>
143 </div>
144 </div>
145 );
146};
147
148export default ForumPage;