Files
vf_react/src/views/Community/components/EventCard/HorizontalDynamicNewsEventCard.js
2025-11-14 06:39:29 +08:00

233 lines
9.9 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.

// src/views/Community/components/EventCard/HorizontalDynamicNewsEventCard.js
// 横向布局的动态新闻事件卡片组件(时间在左,卡片在右)
import React from 'react';
import {
HStack,
Card,
CardBody,
VStack,
Box,
Text,
Tooltip,
useColorModeValue,
useBreakpointValue,
} from '@chakra-ui/react';
import { getImportanceConfig } from '../../../../constants/importanceLevels';
import { PROFESSIONAL_COLORS } from '../../../../constants/professionalTheme';
// 导入子组件
import ImportanceStamp from './ImportanceStamp';
import EventTimeline from './EventTimeline';
import EventFollowButton from './EventFollowButton';
import StockChangeIndicators from '../../../../components/StockChangeIndicators';
import KeywordsCarousel from './KeywordsCarousel';
/**
* 横向布局的动态新闻事件卡片组件
* @param {Object} props
* @param {Object} props.event - 事件对象
* @param {number} props.index - 事件索引
* @param {boolean} props.isFollowing - 是否已关注
* @param {number} props.followerCount - 关注数
* @param {boolean} props.isSelected - 是否被选中
* @param {Function} props.onEventClick - 卡片点击事件
* @param {Function} props.onTitleClick - 标题点击事件
* @param {Function} props.onToggleFollow - 切换关注事件
* @param {Object} props.timelineStyle - 时间轴样式配置
* @param {string} props.borderColor - 边框颜色
* @param {string} props.indicatorSize - 涨幅指标尺寸 ('default' | 'comfortable' | 'large')
* @param {string} props.layout - 布局模式 ('vertical' | 'four-row'),影响时间轴竖线高度
*/
const HorizontalDynamicNewsEventCard = ({
event,
index,
isFollowing,
followerCount,
isSelected = false,
onEventClick,
onTitleClick,
onToggleFollow,
timelineStyle,
borderColor: timelineBorderColor,
indicatorSize = 'comfortable',
layout = 'vertical',
}) => {
const importance = getImportanceConfig(event.importance);
// 专业配色 - 黑色、灰色、金色主题
const cardBg = PROFESSIONAL_COLORS.background.card;
const cardBgHover = PROFESSIONAL_COLORS.background.cardHover;
const selectedBg = 'rgba(255, 195, 0, 0.1)'; // 金色半透明
const selectedBorderColor = PROFESSIONAL_COLORS.gold[500];
const linkColor = PROFESSIONAL_COLORS.text.primary;
const borderColor = PROFESSIONAL_COLORS.border.default;
// 响应式布局
const showTimeline = useBreakpointValue({ base: false, md: true }); // 移动端隐藏时间轴
const cardPadding = useBreakpointValue({ base: 2, md: 3 }); // 移动端减小内边距
const titleFontSize = useBreakpointValue({ base: 'sm', md: 'md' }); // 移动端减小标题字体
const titlePaddingRight = useBreakpointValue({ base: '80px', md: '120px' }); // 为关键词留空间
const spacing = useBreakpointValue({ base: 2, md: 3 }); // 间距
/**
* 根据平均涨幅计算背景色(专业配色 - 深色主题)
* @param {number} avgChange - 平均涨跌幅
* @returns {string} CSS 颜色值
*/
const getChangeBasedBgColor = (avgChange) => {
const numChange = Number(avgChange);
// 如果没有涨跌幅数据,使用默认卡片背景
if (avgChange == null || isNaN(numChange)) {
return PROFESSIONAL_COLORS.background.card;
}
// 根据涨跌幅分级返回深色主题背景色
const absChange = Math.abs(numChange);
if (numChange > 0) {
// 涨:深红色系
if (absChange >= 9) return 'rgba(239, 68, 68, 0.15)';
if (absChange >= 7) return 'rgba(239, 68, 68, 0.12)';
if (absChange >= 5) return 'rgba(239, 68, 68, 0.1)';
if (absChange >= 3) return 'rgba(239, 68, 68, 0.08)';
return 'rgba(239, 68, 68, 0.05)';
} else if (numChange < 0) {
// 跌:深绿色系
if (absChange >= 9) return 'rgba(16, 185, 129, 0.15)';
if (absChange >= 7) return 'rgba(16, 185, 129, 0.12)';
if (absChange >= 5) return 'rgba(16, 185, 129, 0.1)';
if (absChange >= 3) return 'rgba(16, 185, 129, 0.08)';
return 'rgba(16, 185, 129, 0.05)';
}
return PROFESSIONAL_COLORS.background.card;
};
return (
<HStack align="stretch" spacing={spacing} w="full">
{/* 左侧时间轴 - 移动端隐藏 */}
{showTimeline && (
<EventTimeline
createdAt={event.created_at}
timelineStyle={timelineStyle}
borderColor={timelineBorderColor}
minHeight={layout === 'four-row' ? '60px' : 0}
importance={importance}
/>
)}
{/* 右侧事件卡片容器 */}
<Box flex="1" position="relative">
{/* 事件卡片 - 增强美化效果 */}
<Card
position="relative"
bg={isSelected
? selectedBg
: getChangeBasedBgColor(event.related_avg_chg)
}
backdropFilter="blur(10px)" // 毛玻璃效果
borderWidth={isSelected ? "2px" : "1px"}
borderColor={isSelected
? selectedBorderColor
: borderColor
}
borderRadius="xl"
boxShadow={isSelected ? "xl" : "md"}
overflow="visible"
_hover={{
boxShadow: '2xl',
transform: 'translateY(-3px)',
borderColor: isSelected ? 'blue.600' : importance.color,
}}
_before={{
content: '""',
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: '3px',
background: isSelected
? PROFESSIONAL_COLORS.gradients.gold
: `linear-gradient(90deg, ${PROFESSIONAL_COLORS.importance[importance.level]?.border || PROFESSIONAL_COLORS.gold[500]} 0%, transparent 100%)`,
borderTopLeftRadius: 'xl',
borderTopRightRadius: 'xl',
opacity: 0.8,
}}
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
cursor="pointer"
onClick={() => onEventClick?.(event)}
>
<CardBody p={cardPadding} pb={2}>
{/* 右上角:关注按钮 */}
<Box position="absolute" top={{ base: 1, md: 2 }} right={{ base: 1, md: 2 }} zIndex={2}>
<EventFollowButton
isFollowing={isFollowing}
followerCount={followerCount}
onToggle={() => onToggleFollow?.(event.id)}
size="xs"
showCount={false}
/>
</Box>
{/* Keywords梦幻轮播 - 绝对定位在卡片右侧空白处 */}
{event.keywords && event.keywords.length > 0 && (
<KeywordsCarousel
keywords={event.keywords}
interval={4000}
onKeywordClick={(keyword) => {
console.log('Keyword clicked:', keyword);
// TODO: 实现关键词筛选功能
}}
/>
)}
<VStack align="stretch" spacing={1.5}>
{/* 标题 - 最多两行hover 显示完整内容 */}
<Tooltip
label={event.title}
placement="top"
hasArrow
bg="gray.700"
color="white"
fontSize="sm"
p={2}
borderRadius="md"
isDisabled={event.title.length < 40}
>
<Box
cursor="pointer"
onClick={(e) => onTitleClick?.(e, event)}
mt={1}
paddingRight={titlePaddingRight}
>
<Text
fontSize={titleFontSize}
fontWeight="semibold"
color={linkColor}
lineHeight="1.4"
noOfLines={2}
_hover={{ textDecoration: 'underline' }}
>
{event.title}
</Text>
</Box>
</Tooltip>
{/* 第二行:涨跌幅数据 */}
<StockChangeIndicators
avgChange={event.related_avg_chg}
maxChange={event.related_max_chg}
weekChange={event.related_week_chg}
size={indicatorSize}
/>
</VStack>
</CardBody>
</Card>
</Box>
</HStack>
);
};
export default HorizontalDynamicNewsEventCard;