Files
vf_react/src/views/EventDetail/components/EventHeader.js
zdl 2207a680b5 refactor(icons): 迁移其他 views 目录图标到 lucide-react
- views/Center, views/Community, views/DataBrowser 等
- views/EventDetail, views/LimitAnalyse, views/StockOverview
- views/TradingSimulation, views/Pages, views/Authentication
- views/Profile, views/Settings
- 处理 Tag/TagIcon 命名冲突
- 涉及 52 个组件文件

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-25 13:00:41 +08:00

367 lines
14 KiB
JavaScript

// src/views/EventDetail/components/EventHeader.js - 改进的事件头部信息组件
import React from 'react';
import {
Box,
Flex,
HStack,
VStack,
Text,
Button,
Badge,
Avatar,
Icon,
Tag,
TagLabel,
Wrap,
WrapItem,
useColorModeValue,
Divider,
SimpleGrid,
Stat,
StatLabel,
StatNumber,
StatHelpText,
StatArrow
} from '@chakra-ui/react';
import {
Heart,
Eye,
MessageCircle,
Users,
Clock,
Calendar,
LineChart
} from 'lucide-react';
const EventHeader = ({ event, onFollowToggle }) => {
// 颜色主题
const bgGradient = useColorModeValue(
'linear(to-r, blue.50, purple.50)',
'linear(to-r, blue.900, purple.900)'
);
const textPrimary = useColorModeValue('gray.800', 'white');
const textSecondary = useColorModeValue('gray.600', 'gray.300');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const statBg = useColorModeValue('white', 'gray.700');
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return '--';
return new Date(dateString).toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
};
// 格式化数字
const formatNumber = (num) => {
if (!num && num !== 0) return '0';
if (num >= 10000) {
return (num / 10000).toFixed(1) + 'w';
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + 'k';
}
return num.toString();
};
// 获取超预期分数颜色
const getScoreColor = (score) => {
if (score >= 80) return 'red';
if (score >= 60) return 'orange';
if (score >= 40) return 'yellow';
return 'green';
};
if (!event) {
return <Box>加载中...</Box>;
}
return (
<Box>
{/* 主要信息区域 */}
<Box
bgGradient={bgGradient}
borderRadius="xl"
p={6}
mb={6}
position="relative"
overflow="hidden"
>
{/* 装饰性背景图案 */}
<Box
position="absolute"
top="-50px"
right="-50px"
width="200px"
height="200px"
bg="whiteAlpha.100"
borderRadius="full"
opacity={0.3}
/>
<Flex direction={{ base: 'column', lg: 'row' }} gap={6} position="relative">
{/* 左侧内容 */}
<VStack align="flex-start" spacing={4} flex="1">
{/* 事件类型和状态 */}
<HStack spacing={3} flexWrap="wrap">
<Badge
colorScheme="blue"
size="lg"
px={4}
py={2}
borderRadius="full"
fontSize="sm"
fontWeight="bold"
>
{event.event_type || '未分类'}
</Badge>
<Badge
colorScheme={event.status === 'active' ? 'green' : 'gray'}
size="lg"
px={4}
py={2}
borderRadius="full"
fontSize="sm"
>
{event.status === 'active' ? '进行中' : '已结束'}
</Badge>
{event.expectation_surprise_score && (
<Badge
colorScheme={getScoreColor(event.expectation_surprise_score)}
size="lg"
px={4}
py={2}
borderRadius="full"
fontSize="sm"
fontWeight="bold"
>
超预期: {event.expectation_surprise_score}
</Badge>
)}
</HStack>
{/* 事件标题 */}
<Text
fontSize={{ base: '2xl', md: '3xl' }}
fontWeight="bold"
lineHeight="1.2"
color={textPrimary}
>
{event.title}
</Text>
{/* 创建者和时间信息 */}
<HStack spacing={6} flexWrap="wrap">
<HStack spacing={3}>
<Avatar
size="sm"
src={event.creator?.avatar_url}
name={event.creator?.username || '未知用户'}
/>
<VStack align="flex-start" spacing={0}>
<Text fontWeight="medium" fontSize="sm" color={textPrimary}>
{event.creator?.username || '系统'}
</Text>
<Text fontSize="xs" color={textSecondary}>
创建者
</Text>
</VStack>
</HStack>
<HStack spacing={3}>
<Icon as={Calendar} color={textSecondary} />
<VStack align="flex-start" spacing={0}>
<Text fontSize="sm" color={textPrimary}>
{formatDate(event.created_at)}
</Text>
<Text fontSize="xs" color={textSecondary}>
发布时间
</Text>
</VStack>
</HStack>
</HStack>
</VStack>
{/* 右侧关注按钮 */}
<Flex align="flex-start" justify="center">
<Button
leftIcon={<Icon as={Heart} fill={event.is_following ? "currentColor" : "none"} />}
colorScheme={event.is_following ? "red" : "blue"}
variant={event.is_following ? "solid" : "outline"}
size="lg"
onClick={onFollowToggle}
borderRadius="full"
px={8}
py={6}
fontSize="md"
fontWeight="bold"
bg={event.is_following ? undefined : 'white'}
_hover={{
transform: 'translateY(-2px)',
shadow: 'lg'
}}
transition="all 0.2s"
>
{event.is_following ? '已关注' : '关注事件'}
</Button>
</Flex>
</Flex>
</Box>
{/* 统计数据卡片 */}
<SimpleGrid columns={{ base: 2, md: 4 }} spacing={4} mb={6}>
<Stat
px={4}
py={3}
bg={statBg}
borderRadius="lg"
border="1px solid"
borderColor={borderColor}
textAlign="center"
>
<StatLabel fontSize="xs" color={textSecondary}>
<HStack justify="center" spacing={1}>
<Icon as={Eye} />
<Text>浏览量</Text>
</HStack>
</StatLabel>
<StatNumber fontSize="lg" color={textPrimary}>
{formatNumber(event.view_count)}
</StatNumber>
</Stat>
<Stat
px={4}
py={3}
bg={statBg}
borderRadius="lg"
border="1px solid"
borderColor={borderColor}
textAlign="center"
>
<StatLabel fontSize="xs" color={textSecondary}>
<HStack justify="center" spacing={1}>
<Icon as={MessageCircle} />
<Text>回复数</Text>
</HStack>
</StatLabel>
<StatNumber fontSize="lg" color={textPrimary}>
{formatNumber(event.post_count)}
</StatNumber>
</Stat>
<Stat
px={4}
py={3}
bg={statBg}
borderRadius="lg"
border="1px solid"
borderColor={borderColor}
textAlign="center"
>
<StatLabel fontSize="xs" color={textSecondary}>
<HStack justify="center" spacing={1}>
<Icon as={Users} />
<Text>关注数</Text>
</HStack>
</StatLabel>
<StatNumber fontSize="lg" color={textPrimary}>
{formatNumber(event.follower_count)}
</StatNumber>
</Stat>
<Stat
px={4}
py={3}
bg={statBg}
borderRadius="lg"
border="1px solid"
borderColor={borderColor}
textAlign="center"
>
<StatLabel fontSize="xs" color={textSecondary}>
<HStack justify="center" spacing={1}>
<Icon as={LineChart} />
<Text>热度分</Text>
</HStack>
</StatLabel>
<StatNumber fontSize="lg" color={textPrimary}>
{event.hot_score ? event.hot_score.toFixed(1) : '0.0'}
</StatNumber>
</Stat>
</SimpleGrid>
{/* 事件描述 */}
{event.description && (
<Box mb={6}>
<Text fontSize="md" fontWeight="bold" mb={3} color={textPrimary}>
事件描述
</Text>
<Box
p={4}
bg={useColorModeValue('gray.50', 'gray.700')}
borderRadius="lg"
borderLeft="4px solid"
borderLeftColor="blue.500"
>
<Box
dangerouslySetInnerHTML={{ __html: event.description }}
sx={{
'& p': { mb: 2, lineHeight: '1.6' },
'& p:last-child': { mb: 0 },
'& h1, & h2, & h3, & h4, & h5, & h6': {
fontWeight: 'bold',
mb: 2,
color: textPrimary
},
'& ul, & ol': {
pl: 4,
mb: 2
},
'& li': {
mb: 1
},
color: textSecondary
}}
/>
</Box>
</Box>
)}
{/* 相关标签 */}
{event.keywords_list && event.keywords_list.length > 0 && (
<Box>
<Text fontSize="md" fontWeight="bold" mb={3} color={textPrimary}>
相关标签
</Text>
<Wrap spacing={2}>
{event.keywords_list.map((tag, index) => (
<WrapItem key={index}>
<Tag
size="md"
variant="subtle"
colorScheme="blue"
cursor="pointer"
borderRadius="full"
_hover={{
bg: useColorModeValue('blue.100', 'blue.700'),
transform: 'translateY(-1px)'
}}
transition="all 0.2s"
>
<TagLabel px={2}>{tag}</TagLabel>
</Tag>
</WrapItem>
))}
</Wrap>
</Box>
)}
</Box>
);
};
export default EventHeader;