| import React, { useState, useEffect } from 'react'; |
| import { |
| createPost, |
| findPinnedPosts, |
| likePost, |
| unlikePost, |
| searchPosts, |
| getAllPostsSorted, |
| findPostsByUserId, |
| } from '../api/post'; |
| import Comment from './Comment'; |
| import RequestBoard from './RequestBoard'; |
| import './Post.css'; |
| |
| const Post = () => { |
| const [title, setTitle] = useState(''); |
| const [content, setContent] = useState(''); |
| const [tags, setTags] = useState(''); |
| const [photo, setPhoto] = useState(null); |
| const [posts, setPosts] = useState([]); |
| const [searchKeyword, setSearchKeyword] = useState(''); |
| const [imagePreview, setImagePreview] = useState(null); |
| const [showCreateForm, setShowCreateForm] = useState(false); |
| const [currentView, setCurrentView] = useState('posts'); |
| const [currentUser, setCurrentUser] = useState(null); |
| const [userDecorations, setUserDecorations] = useState({}); |
| |
| useEffect(() => { |
| const storedUser = JSON.parse(localStorage.getItem('user')); |
| if (storedUser) setCurrentUser(storedUser); |
| }, []); |
| |
| useEffect(() => { |
| loadPinnedPosts(); |
| }, []); |
| |
| useEffect(() => { |
| if (posts.length > 0) { |
| fetchUserDecorations(posts); |
| } |
| }, [posts]); |
| |
| const fetchUserDecorations = async (posts) => { |
| const decorations = {}; |
| await Promise.all( |
| posts.map(async (post) => { |
| if (!decorations[post.userid]) { |
| try { |
| const res = await fetch(`http://localhost:8080/user/getDecoration?userid=${post.userid}`); |
| const json = await res.json(); |
| if (json.success) { |
| decorations[post.userid] = json.data; |
| } |
| } catch (error) { |
| console.error(`获取用户 ${post.userid} 的装饰信息失败`, error); |
| } |
| } |
| }) |
| ); |
| setUserDecorations(decorations); |
| }; |
| |
| const loadPinnedPosts = async () => { |
| const data = await findPinnedPosts(); |
| setPosts(data); |
| setCurrentView('posts'); |
| }; |
| |
| const loadAllPosts = async () => { |
| const data = await getAllPostsSorted(); |
| setPosts(data); |
| setCurrentView('posts'); |
| }; |
| |
| const loadMyPosts = async () => { |
| if (!currentUser) return; |
| const data = await findPostsByUserId(currentUser.userid); |
| setPosts(data); |
| setCurrentView('posts'); |
| }; |
| |
| const handleCreate = async () => { |
| if (!currentUser) { |
| alert('请先登录再发帖'); |
| return; |
| } |
| |
| const formData = new FormData(); |
| formData.append('userid', currentUser.userid); |
| formData.append('post_title', title); |
| formData.append('post_content', content); |
| formData.append('tags', tags); |
| formData.append('rannge', 'public'); |
| if (photo) formData.append('photo', photo); |
| |
| const success = await createPost(formData); |
| if (success) { |
| alert('帖子创建成功'); |
| loadPinnedPosts(); |
| setTitle(''); |
| setContent(''); |
| setTags(''); |
| setPhoto(null); |
| setShowCreateForm(false); |
| } else { |
| alert('创建失败'); |
| } |
| }; |
| |
| const handleLike = async (postid) => { |
| await likePost(postid); |
| loadPinnedPosts(); |
| }; |
| |
| const handleUnlike = async (postid) => { |
| await unlikePost(postid); |
| loadPinnedPosts(); |
| }; |
| |
| const handleSearch = async () => { |
| const result = await searchPosts(searchKeyword); |
| setPosts(result); |
| setCurrentView('posts'); |
| }; |
| |
| const showRequestBoard = () => { |
| setCurrentView('requests'); |
| }; |
| |
| return ( |
| <div className="post-container"> |
| <div className="post-actions"> |
| <div className="action-group"> |
| {currentUser && ( |
| <button |
| className="post-button primary" |
| onClick={() => setShowCreateForm(!showCreateForm)} |
| > |
| {showCreateForm ? '收起发布表单' : '创建新帖子'} |
| </button> |
| )} |
| <input |
| type="text" |
| placeholder="搜索关键词" |
| value={searchKeyword} |
| onChange={(e) => setSearchKeyword(e.target.value)} |
| className="post-input" |
| /> |
| <button className="post-button primary-outline" onClick={handleSearch}>搜索</button> |
| </div> |
| |
| <div className="action-group"> |
| <button className="post-button primary-outline" onClick={loadPinnedPosts}>置顶帖子</button> |
| <button className="post-button primary-outline" onClick={loadAllPosts}>所有帖子</button> |
| {currentUser && ( |
| <button className="post-button primary-outline" onClick={loadMyPosts}>我的帖子</button> |
| )} |
| <button className="post-button primary-outline" onClick={showRequestBoard}>需求帖子</button> |
| </div> |
| </div> |
| |
| {showCreateForm && currentUser && currentView !== 'requests' && ( |
| <div className="post-form-card"> |
| <div className="post-form-user-info"> |
| {currentUser.image ? ( |
| <img |
| src={currentUser.image} |
| alt="头像" |
| className="post-form-user-avatar" |
| /> |
| ) : ( |
| <div className="post-form-user-avatar-placeholder"> |
| <div className="avatar-initial"> |
| {currentUser.username?.charAt(0) || 'U'} |
| </div> |
| </div> |
| )} |
| <div className="post-form-username">{currentUser.username}</div> |
| <div className="post-form-user-label">发布者</div> |
| </div> |
| |
| <h2 className="post-form-title">发布新帖子</h2> |
| <input |
| type="text" |
| placeholder="标题" |
| value={title} |
| onChange={(e) => setTitle(e.target.value)} |
| className="post-input" |
| /> |
| <textarea |
| placeholder="内容" |
| value={content} |
| onChange={(e) => setContent(e.target.value)} |
| className="post-textarea" |
| /> |
| <input |
| type="text" |
| placeholder="标签(逗号分隔)" |
| value={tags} |
| onChange={(e) => setTags(e.target.value)} |
| className="post-input" |
| /> |
| <input |
| type="file" |
| onChange={(e) => setPhoto(e.target.files[0])} |
| className="post-file" |
| /> |
| <button className="post-button primary" onClick={handleCreate}> |
| 发布 |
| </button> |
| </div> |
| )} |
| |
| {currentView === 'requests' ? ( |
| <RequestBoard |
| currentUserId={currentUser?.userid} |
| onBack={() => setCurrentView('posts')} |
| /> |
| ) : ( |
| posts.length > 0 ? ( |
| <div className="post-list"> |
| {posts.map((post) => { |
| const decoration = userDecorations[post.userid] || {}; |
| const avatar = decoration.image || ''; |
| const username = decoration.username || '匿名用户'; |
| |
| return ( |
| <div className="post-card" key={post.postid}> |
| <div className="post-author-info"> |
| {avatar ? ( |
| <img |
| src={avatar} |
| alt="头像" |
| className="post-author-avatar" |
| /> |
| ) : ( |
| <div className="post-author-avatar-placeholder"> |
| <div className="avatar-initial">{username.charAt(0)}</div> |
| </div> |
| )} |
| <div className="post-author-details"> |
| <div className="post-author-name">{username}</div> |
| <div className="post-meta">发布时间: {post.postCreatedTime}</div> |
| </div> |
| </div> |
| |
| <div className="post-header"> |
| <div className="post-info"> |
| <h3 className="post-title">{post.postTitle}</h3> |
| <p className="post-content">{post.postContent}</p> |
| <div className="post-meta">标签: {post.tags || '无标签'}</div> |
| <div className="post-actions-inline"> |
| <span>👍 {post.likes}</span> |
| <button className="post-link like" onClick={() => handleLike(post.postid)}>点赞</button> |
| <button className="post-link unlike" onClick={() => handleUnlike(post.postid)}>取消点赞</button> |
| </div> |
| </div> |
| {post.photo && ( |
| <img |
| src={`http://localhost:8080${post.photo}`} |
| alt="post" |
| className="post-image" |
| onClick={() => setImagePreview(`http://localhost:8080${post.photo}`)} |
| /> |
| )} |
| </div> |
| |
| <div className="post-comment"> |
| <Comment postId={post.postid} currentUser={currentUser} /> |
| </div> |
| </div> |
| ); |
| })} |
| </div> |
| ) : ( |
| <div className="post-empty-state"> |
| <p className="post-empty-message">暂无内容</p> |
| </div> |
| ) |
| )} |
| |
| {imagePreview && ( |
| <div className="image-modal" onClick={() => setImagePreview(null)}> |
| <div className="image-modal-content" onClick={(e) => e.stopPropagation()}> |
| <img src={imagePreview} alt="preview" className="image-modal-img" /> |
| <button className="image-modal-close" onClick={() => setImagePreview(null)}>×</button> |
| </div> |
| </div> |
| )} |
| </div> |
| ); |
| }; |
| |
| export default Post; |