add forum
This commit is contained in:
370
src/views/ValueForum/PostDetail.js
Normal file
370
src/views/ValueForum/PostDetail.js
Normal file
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* 帖子详情页
|
||||
* 展示帖子完整内容、事件时间轴、评论区
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
Heading,
|
||||
Text,
|
||||
HStack,
|
||||
VStack,
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
Image,
|
||||
SimpleGrid,
|
||||
Spinner,
|
||||
Center,
|
||||
Flex,
|
||||
IconButton,
|
||||
Divider,
|
||||
} from '@chakra-ui/react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Heart,
|
||||
MessageCircle,
|
||||
Eye,
|
||||
Share2,
|
||||
Bookmark,
|
||||
} from 'lucide-react';
|
||||
import { forumColors } from '@theme/forumTheme';
|
||||
import {
|
||||
getPostById,
|
||||
likePost,
|
||||
getEventsByPostId,
|
||||
} from '@services/elasticsearchService';
|
||||
import EventTimeline from './components/EventTimeline';
|
||||
import CommentSection from './components/CommentSection';
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
const PostDetail = () => {
|
||||
const { postId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [post, setPost] = useState(null);
|
||||
const [events, setEvents] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isLiked, setIsLiked] = useState(false);
|
||||
const [likes, setLikes] = useState(0);
|
||||
|
||||
// 加载帖子数据
|
||||
useEffect(() => {
|
||||
const loadPostData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// 并行加载帖子和事件
|
||||
const [postData, eventsData] = await Promise.all([
|
||||
getPostById(postId),
|
||||
getEventsByPostId(postId),
|
||||
]);
|
||||
|
||||
setPost(postData);
|
||||
setLikes(postData.likes_count || 0);
|
||||
setEvents(eventsData);
|
||||
} catch (error) {
|
||||
console.error('加载帖子失败:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadPostData();
|
||||
}, [postId]);
|
||||
|
||||
// 处理点赞
|
||||
const handleLike = async () => {
|
||||
try {
|
||||
if (!isLiked) {
|
||||
await likePost(postId);
|
||||
setLikes((prev) => prev + 1);
|
||||
setIsLiked(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('点赞失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Box minH="100vh" bg={forumColors.background.main} pt="80px">
|
||||
<Center py="20">
|
||||
<VStack spacing="4">
|
||||
<Spinner
|
||||
size="xl"
|
||||
thickness="4px"
|
||||
speed="0.8s"
|
||||
color={forumColors.primary[500]}
|
||||
/>
|
||||
<Text color={forumColors.text.secondary}>加载中...</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<Box minH="100vh" bg={forumColors.background.main} pt="80px">
|
||||
<Center py="20">
|
||||
<VStack spacing="4">
|
||||
<Text color={forumColors.text.secondary} fontSize="lg">
|
||||
帖子不存在或已被删除
|
||||
</Text>
|
||||
<Button
|
||||
leftIcon={<ArrowLeft size={18} />}
|
||||
onClick={() => navigate('/value-forum')}
|
||||
bg={forumColors.gradients.goldPrimary}
|
||||
color={forumColors.background.main}
|
||||
_hover={{ opacity: 0.9 }}
|
||||
>
|
||||
返回论坛
|
||||
</Button>
|
||||
</VStack>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box minH="100vh" bg={forumColors.background.main} pt="80px" pb="20">
|
||||
<Container maxW="container.xl">
|
||||
{/* 返回按钮 */}
|
||||
<Button
|
||||
leftIcon={<ArrowLeft size={18} />}
|
||||
onClick={() => navigate('/value-forum')}
|
||||
variant="ghost"
|
||||
color={forumColors.text.secondary}
|
||||
_hover={{ color: forumColors.text.primary }}
|
||||
mb="6"
|
||||
>
|
||||
返回论坛
|
||||
</Button>
|
||||
|
||||
<SimpleGrid columns={{ base: 1, lg: 3 }} spacing="6">
|
||||
{/* 左侧:帖子内容 + 评论 */}
|
||||
<Box gridColumn={{ base: '1', lg: '1 / 3' }}>
|
||||
<VStack spacing="6" align="stretch">
|
||||
{/* 帖子主体 */}
|
||||
<MotionBox
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
bg={forumColors.background.card}
|
||||
borderRadius="xl"
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
overflow="hidden"
|
||||
>
|
||||
{/* 作者信息 */}
|
||||
<Box p="6" borderBottomWidth="1px" borderColor={forumColors.border.default}>
|
||||
<Flex justify="space-between" align="start">
|
||||
<HStack spacing="3">
|
||||
<Avatar
|
||||
size="md"
|
||||
name={post.author_name}
|
||||
src={post.author_avatar}
|
||||
bg={forumColors.gradients.goldPrimary}
|
||||
color={forumColors.background.main}
|
||||
/>
|
||||
<VStack align="start" spacing="1">
|
||||
<Text fontSize="md" fontWeight="600" color={forumColors.text.primary}>
|
||||
{post.author_name}
|
||||
</Text>
|
||||
<Text fontSize="xs" color={forumColors.text.muted}>
|
||||
发布于 {formatTime(post.created_at)}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<HStack spacing="2">
|
||||
<IconButton
|
||||
icon={<Share2 size={18} />}
|
||||
variant="ghost"
|
||||
color={forumColors.text.tertiary}
|
||||
_hover={{ color: forumColors.primary[500] }}
|
||||
aria-label="分享"
|
||||
/>
|
||||
<IconButton
|
||||
icon={<Bookmark size={18} />}
|
||||
variant="ghost"
|
||||
color={forumColors.text.tertiary}
|
||||
_hover={{ color: forumColors.primary[500] }}
|
||||
aria-label="收藏"
|
||||
/>
|
||||
</HStack>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
{/* 帖子内容 */}
|
||||
<Box p="6">
|
||||
{/* 标题 */}
|
||||
<Heading
|
||||
as="h1"
|
||||
fontSize="2xl"
|
||||
fontWeight="bold"
|
||||
color={forumColors.text.primary}
|
||||
mb="4"
|
||||
>
|
||||
{post.title}
|
||||
</Heading>
|
||||
|
||||
{/* 标签 */}
|
||||
{post.tags && post.tags.length > 0 && (
|
||||
<HStack spacing="2" mb="6" flexWrap="wrap">
|
||||
{post.tags.map((tag, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
bg={forumColors.gradients.goldSubtle}
|
||||
color={forumColors.primary[500]}
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.gold}
|
||||
borderRadius="full"
|
||||
px="3"
|
||||
py="1"
|
||||
fontSize="sm"
|
||||
>
|
||||
#{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
{/* 正文 */}
|
||||
<Text
|
||||
fontSize="md"
|
||||
color={forumColors.text.secondary}
|
||||
lineHeight="1.8"
|
||||
whiteSpace="pre-wrap"
|
||||
mb="6"
|
||||
>
|
||||
{post.content}
|
||||
</Text>
|
||||
|
||||
{/* 图片 */}
|
||||
{post.images && post.images.length > 0 && (
|
||||
<SimpleGrid columns={{ base: 1, sm: 2, md: 3 }} spacing="4" mb="6">
|
||||
{post.images.map((img, index) => (
|
||||
<Image
|
||||
key={index}
|
||||
src={img}
|
||||
alt={`图片 ${index + 1}`}
|
||||
borderRadius="md"
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.default}
|
||||
cursor="pointer"
|
||||
_hover={{
|
||||
transform: 'scale(1.05)',
|
||||
boxShadow: forumColors.shadows.gold,
|
||||
}}
|
||||
transition="all 0.3s"
|
||||
/>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 互动栏 */}
|
||||
<Box
|
||||
p="4"
|
||||
borderTopWidth="1px"
|
||||
borderColor={forumColors.border.default}
|
||||
bg={forumColors.background.secondary}
|
||||
>
|
||||
<Flex justify="space-between" align="center">
|
||||
<HStack spacing="6">
|
||||
<HStack
|
||||
spacing="2"
|
||||
cursor="pointer"
|
||||
onClick={handleLike}
|
||||
color={isLiked ? forumColors.primary[500] : forumColors.text.tertiary}
|
||||
_hover={{ color: forumColors.primary[500] }}
|
||||
>
|
||||
<Heart size={20} fill={isLiked ? 'currentColor' : 'none'} />
|
||||
<Text fontSize="sm" fontWeight="500">
|
||||
{likes}
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<HStack spacing="2" color={forumColors.text.tertiary}>
|
||||
<MessageCircle size={20} />
|
||||
<Text fontSize="sm" fontWeight="500">
|
||||
{post.comments_count || 0}
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
<HStack spacing="2" color={forumColors.text.tertiary}>
|
||||
<Eye size={20} />
|
||||
<Text fontSize="sm" fontWeight="500">
|
||||
{post.views_count || 0}
|
||||
</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
|
||||
<Button
|
||||
leftIcon={<Heart size={18} />}
|
||||
onClick={handleLike}
|
||||
bg={isLiked ? forumColors.primary[500] : 'transparent'}
|
||||
color={isLiked ? forumColors.background.main : forumColors.text.primary}
|
||||
border="1px solid"
|
||||
borderColor={forumColors.border.gold}
|
||||
_hover={{
|
||||
bg: forumColors.gradients.goldPrimary,
|
||||
color: forumColors.background.main,
|
||||
}}
|
||||
>
|
||||
{isLiked ? '已点赞' : '点赞'}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</MotionBox>
|
||||
|
||||
{/* 评论区 */}
|
||||
<MotionBox
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<CommentSection postId={postId} />
|
||||
</MotionBox>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
{/* 右侧:事件时间轴 */}
|
||||
<Box gridColumn={{ base: '1', lg: '3' }}>
|
||||
<MotionBox
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
position="sticky"
|
||||
top="100px"
|
||||
>
|
||||
<EventTimeline events={events} />
|
||||
</MotionBox>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostDetail;
|
||||
Reference in New Issue
Block a user