import React, { useState, useEffect } from 'react'; import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, Box, Text, VStack, HStack, Avatar, Textarea, Button, Divider, useToast, Badge, Flex, IconButton, Menu, MenuButton, MenuList, MenuItem, useColorModeValue, Spinner, Center, Collapse, Input, } from '@chakra-ui/react'; import { ChatIcon, TimeIcon, DeleteIcon, EditIcon, ChevronDownIcon, TriangleDownIcon, TriangleUpIcon, } from '@chakra-ui/icons'; import { FaHeart, FaRegHeart, FaComment } from 'react-icons/fa'; import { format } from 'date-fns'; import { zhCN } from 'date-fns/locale'; import { eventService } from '../../../services/eventService'; // 获取 API 基础地址 const API_BASE_URL = process.env.NODE_ENV === 'production' ? '' : (process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001'); const EventDiscussionModal = ({ isOpen, onClose, eventId, eventTitle, discussionType = '事件讨论' }) => { const [posts, setPosts] = useState([]); const [newPostContent, setNewPostContent] = useState(''); const [newPostTitle, setNewPostTitle] = useState(''); const [loading, setLoading] = useState(false); const [submitting, setSubmitting] = useState(false); const [expandedPosts, setExpandedPosts] = useState({}); const [postComments, setPostComments] = useState({}); const [replyContents, setReplyContents] = useState({}); const [loadingComments, setLoadingComments] = useState({}); const toast = useToast(); const bgColor = useColorModeValue('white', 'gray.800'); const borderColor = useColorModeValue('gray.200', 'gray.600'); const hoverBg = useColorModeValue('gray.50', 'gray.700'); // 加载帖子列表 const loadPosts = async () => { if (!eventId) return; setLoading(true); try { const response = await fetch(`${API_BASE_URL}/api/events/${eventId}/posts?sort=latest&page=1&per_page=20`, { method: 'GET', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); const result = await response.json(); if (response.ok && result.success) { setPosts(result.data || []); } else { toast({ title: '加载帖子失败', status: 'error', duration: 3000, isClosable: true, }); } } catch (error) { console.error('Failed to load posts:', error); toast({ title: '加载帖子失败', status: 'error', duration: 3000, isClosable: true, }); } finally { setLoading(false); } }; // 加载帖子的评论 const loadPostComments = async (postId) => { setLoadingComments(prev => ({ ...prev, [postId]: true })); try { const response = await fetch(`${API_BASE_URL}/api/posts/${postId}/comments?sort=latest`, { method: 'GET', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); const result = await response.json(); if (response.ok && result.success) { setPostComments(prev => ({ ...prev, [postId]: result.data || [] })); } } catch (error) { console.error('Failed to load comments:', error); } finally { setLoadingComments(prev => ({ ...prev, [postId]: false })); } }; // 切换展开/收起评论 const togglePostComments = async (postId) => { const isExpanded = expandedPosts[postId]; if (!isExpanded) { // 展开时加载评论 await loadPostComments(postId); } setExpandedPosts(prev => ({ ...prev, [postId]: !isExpanded })); }; // 提交新帖子 const handleSubmitPost = async () => { if (!newPostContent.trim()) return; setSubmitting(true); try { const response = await fetch(`${API_BASE_URL}/api/events/${eventId}/posts`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ title: newPostTitle.trim(), content: newPostContent.trim(), content_type: 'text', }) }); const result = await response.json(); if (response.ok && result.success) { setNewPostContent(''); setNewPostTitle(''); loadPosts(); toast({ title: '帖子发布成功', status: 'success', duration: 2000, isClosable: true, }); } else { toast({ title: result.message || '帖子发布失败', status: 'error', duration: 3000, isClosable: true, }); } } catch (error) { console.error('Failed to submit post:', error); toast({ title: '帖子发布失败', status: 'error', duration: 3000, isClosable: true, }); } finally { setSubmitting(false); } }; // 删除帖子 const handleDeletePost = async (postId) => { if (!window.confirm('确定要删除这个帖子吗?')) return; try { const response = await fetch(`${API_BASE_URL}/api/posts/${postId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); const result = await response.json(); if (response.ok && result.success) { loadPosts(); toast({ title: '帖子已删除', status: 'success', duration: 2000, isClosable: true, }); } else { toast({ title: result.message || '删除失败', status: 'error', duration: 3000, isClosable: true, }); } } catch (error) { console.error('Failed to delete post:', error); toast({ title: '删除失败', status: 'error', duration: 3000, isClosable: true, }); } }; // 点赞帖子 const handleLikePost = async (postId) => { try { const response = await fetch(`${API_BASE_URL}/api/posts/${postId}/like`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); const result = await response.json(); if (response.ok && result.success) { // 更新帖子列表中的点赞状态 setPosts(prev => prev.map(post => post.id === postId ? { ...post, likes_count: result.likes_count, liked: result.liked } : post )); } } catch (error) { console.error('Failed to like post:', error); toast({ title: '操作失败', status: 'error', duration: 2000, isClosable: true, }); } }; // 提交评论 const handleSubmitComment = async (postId) => { const content = replyContents[postId]; if (!content?.trim()) return; try { const response = await fetch(`${API_BASE_URL}/api/posts/${postId}/comments`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ content: content.trim(), }) }); const result = await response.json(); if (response.ok && result.success) { setReplyContents(prev => ({ ...prev, [postId]: '' })); // 重新加载该帖子的评论 await loadPostComments(postId); // 更新帖子的评论数 setPosts(prev => prev.map(post => post.id === postId ? { ...post, comments_count: (post.comments_count || 0) + 1 } : post )); toast({ title: '评论发布成功', status: 'success', duration: 2000, isClosable: true, }); } } catch (error) { console.error('Failed to submit comment:', error); toast({ title: '评论发布失败', status: 'error', duration: 3000, isClosable: true, }); } }; // 删除评论 const handleDeleteComment = async (commentId, postId) => { if (!window.confirm('确定要删除这条评论吗?')) return; try { const response = await fetch(`${API_BASE_URL}/api/comments/${commentId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); const result = await response.json(); if (response.ok && result.success) { // 重新加载该帖子的评论 await loadPostComments(postId); // 更新帖子的评论数 setPosts(prev => prev.map(post => post.id === postId ? { ...post, comments_count: Math.max(0, (post.comments_count || 0) - 1) } : post )); toast({ title: '评论已删除', status: 'success', duration: 2000, isClosable: true, }); } } catch (error) { console.error('Failed to delete comment:', error); toast({ title: '删除失败', status: 'error', duration: 3000, isClosable: true, }); } }; useEffect(() => { if (isOpen) { loadPosts(); } }, [isOpen, eventId]); return ( {discussionType} {eventTitle && ( {eventTitle} )} {/* 发布新帖子 */} setNewPostTitle(e.target.value)} placeholder="帖子标题(可选)" size="sm" mb={2} />