Files
vf_react/src/views/ValueForum/components/PostCard.js
2025-11-15 09:10:26 +08:00

204 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 帖子卡片组件 - 类似小红书风格
* 用于论坛主页的帖子展示
*/
import React from 'react';
import {
Box,
Image,
Text,
HStack,
VStack,
Avatar,
Badge,
IconButton,
Flex,
useColorModeValue,
} from '@chakra-ui/react';
import { motion } from 'framer-motion';
import { Heart, MessageCircle, Eye, TrendingUp } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { forumColors } from '@theme/forumTheme';
const MotionBox = motion(Box);
const PostCard = ({ post }) => {
const navigate = useNavigate();
// 处理卡片点击
const handleCardClick = () => {
navigate(`/value-forum/post/${post.id}`);
};
// 格式化数字1000 -> 1k
const formatNumber = (num) => {
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
return num;
};
// 格式化时间
const formatTime = (dateString) => {
const date = new Date(dateString);
const now = new Date();
const diff = now - date;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return '刚刚';
if (minutes < 60) return `${minutes}分钟前`;
if (hours < 24) return `${hours}小时前`;
if (days < 7) return `${days}天前`;
return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' });
};
return (
<MotionBox
bg={forumColors.background.card}
borderRadius="xl"
overflow="hidden"
border="1px solid"
borderColor={forumColors.border.default}
cursor="pointer"
onClick={handleCardClick}
whileHover={{ y: -8, scale: 1.02 }}
transition={{ duration: 0.3 }}
_hover={{
borderColor: forumColors.border.gold,
boxShadow: forumColors.shadows.gold,
}}
>
{/* 封面图片区域 */}
{post.images && post.images.length > 0 && (
<Box position="relative" overflow="hidden" h="200px">
<Image
src={post.images[0]}
alt={post.title}
w="100%"
h="100%"
objectFit="cover"
transition="transform 0.3s"
_groupHover={{ transform: 'scale(1.1)' }}
/>
{/* 置顶标签 */}
{post.is_pinned && (
<Badge
position="absolute"
top="12px"
right="12px"
bg={forumColors.gradients.goldPrimary}
color={forumColors.background.main}
px="3"
py="1"
borderRadius="full"
fontWeight="bold"
fontSize="xs"
display="flex"
alignItems="center"
gap="1"
>
<TrendingUp size={12} />
置顶
</Badge>
)}
</Box>
)}
{/* 内容区域 */}
<VStack align="stretch" p="4" spacing="3">
{/* 标题 */}
<Text
fontSize="md"
fontWeight="600"
color={forumColors.text.primary}
noOfLines={2}
lineHeight="1.4"
>
{post.title}
</Text>
{/* 内容预览 */}
{post.content && (
<Text
fontSize="sm"
color={forumColors.text.secondary}
noOfLines={2}
lineHeight="1.6"
>
{post.content}
</Text>
)}
{/* 标签 */}
{post.tags && post.tags.length > 0 && (
<HStack spacing="2" flexWrap="wrap">
{post.tags.slice(0, 3).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="xs"
fontWeight="500"
>
#{tag}
</Badge>
))}
</HStack>
)}
{/* 底部信息栏 */}
<Flex justify="space-between" align="center" pt="2">
{/* 作者信息 */}
<HStack spacing="2">
<Avatar
size="xs"
name={post.author_name}
src={post.author_avatar}
bg={forumColors.gradients.goldPrimary}
color={forumColors.background.main}
/>
<Text fontSize="xs" color={forumColors.text.tertiary}>
{post.author_name}
</Text>
</HStack>
{/* 互动数据 */}
<HStack spacing="4" fontSize="xs" color={forumColors.text.tertiary}>
<HStack spacing="1">
<Heart size={14} />
<Text>{formatNumber(post.likes_count || 0)}</Text>
</HStack>
<HStack spacing="1">
<MessageCircle size={14} />
<Text>{formatNumber(post.comments_count || 0)}</Text>
</HStack>
<HStack spacing="1">
<Eye size={14} />
<Text>{formatNumber(post.views_count || 0)}</Text>
</HStack>
</HStack>
</Flex>
{/* 时间 */}
<Text fontSize="xs" color={forumColors.text.muted} textAlign="right">
{formatTime(post.created_at)}
</Text>
</VStack>
</MotionBox>
);
};
export default PostCard;