- 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>
367 lines
14 KiB
JavaScript
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; |