Files
vf_react/src/views/StockCommunity/components/MessageArea/ForumChannel/PostCard.tsx
2026-01-06 15:15:14 +08:00

170 lines
5.1 KiB
TypeScript

/**
* 帖子卡片组件 - HeroUI 深色风格
*/
import React from 'react';
import {
Box,
Flex,
Text,
Avatar,
HStack,
Tag,
Icon,
} from '@chakra-ui/react';
import { MessageCircle, Eye, ThumbsUp, Pin, Lock } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale';
import { motion } from 'framer-motion';
import { ForumPost } from '../../../types';
interface PostCardProps {
post: ForumPost;
onClick: () => void;
}
const PostCard: React.FC<PostCardProps> = ({ post, onClick }) => {
// 格式化时间 - 将 UTC 时间转换为北京时间
const formatTime = (dateStr: string) => {
const date = new Date(dateStr);
// 后端存储的是 UTC 时间,需要加 8 小时转换为北京时间
const beijingDate = new Date(date.getTime() + 8 * 60 * 60 * 1000);
return formatDistanceToNow(beijingDate, {
addSuffix: true,
locale: zhCN,
});
};
return (
<motion.div whileHover={{ scale: 1.005 }} whileTap={{ scale: 0.995 }}>
<Box
bg="rgba(255, 255, 255, 0.03)"
border="1px solid"
borderColor="rgba(255, 255, 255, 0.08)"
borderRadius="xl"
p={4}
mb={3}
cursor="pointer"
transition="all 0.2s"
_hover={{
bg: 'rgba(255, 255, 255, 0.06)',
borderColor: 'rgba(139, 92, 246, 0.3)',
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.3)',
}}
onClick={onClick}
>
<Flex>
{/* 作者头像 */}
<Avatar
size="md"
name={post.authorName}
src={post.authorAvatar}
mr={3}
bg="linear-gradient(135deg, rgba(139, 92, 246, 0.6), rgba(59, 130, 246, 0.6))"
/>
{/* 帖子内容 */}
<Box flex={1}>
{/* 标题和标记 */}
<HStack spacing={2} mb={1}>
{post.isPinned && (
<Icon as={Pin} color="purple.400" boxSize={4} />
)}
{post.isLocked && (
<Icon as={Lock} color="orange.400" boxSize={4} />
)}
<Text
fontWeight="bold"
fontSize="md"
color="white"
noOfLines={1}
>
{post.title}
</Text>
</HStack>
{/* 内容预览 */}
<Text
color="gray.400"
fontSize="sm"
noOfLines={2}
mb={2}
>
{post.content
.replace(/<[^>]*>/g, '') // 移除 HTML 标签
.replace(/!\[[^\]]*\]\([^)]+\)/g, '[图片]') // 将 Markdown 图片替换为 [图片]
.slice(0, 150)}
</Text>
{/* 标签 */}
{post.tags && post.tags.length > 0 && (
<HStack spacing={1} mb={2} flexWrap="wrap">
{post.tags.slice(0, 3).map(tag => (
<Tag
key={tag}
size="sm"
bg="rgba(59, 130, 246, 0.15)"
color="blue.300"
border="1px solid"
borderColor="rgba(59, 130, 246, 0.3)"
>
{tag}
</Tag>
))}
{post.tags.length > 3 && (
<Text fontSize="xs" color="gray.500">
+{post.tags.length - 3}
</Text>
)}
</HStack>
)}
{/* 底部信息 */}
<Flex align="center" justify="space-between">
{/* 作者和时间 */}
<HStack spacing={2} fontSize="sm" color="gray.500">
<Text fontWeight="medium" color="purple.300">{post.authorName}</Text>
<Text>·</Text>
<Text>{formatTime(post.createdAt)}</Text>
{post.lastReplyAt && post.lastReplyAt !== post.createdAt && (
<>
<Text>·</Text>
<Text> {formatTime(post.lastReplyAt)}</Text>
</>
)}
</HStack>
{/* 统计数据 */}
<HStack spacing={4} fontSize="sm" color="gray.500">
<HStack spacing={1}>
<Icon as={MessageCircle} boxSize={4} />
<Text
color="purple.300"
bg="rgba(139, 92, 246, 0.15)"
px={1.5}
borderRadius="sm"
fontSize="xs"
fontWeight="bold"
>
{post.replyCount}
</Text>
</HStack>
<HStack spacing={1}>
<Icon as={Eye} boxSize={4} />
<Text>{post.viewCount}</Text>
</HStack>
<HStack spacing={1}>
<Icon as={ThumbsUp} boxSize={4} />
<Text>{post.likeCount}</Text>
</HStack>
</HStack>
</Flex>
</Box>
</Flex>
</Box>
</motion.div>
);
};
export default PostCard;