refactor(EventCard): 工具栏 UI 重构为上下结构
- 投票按钮改用 TrendingUp/TrendingDown 折线图标 - 投票按钮改为上图标下数字的垂直布局 - 浏览量、收藏按钮改为上下结构 - 工具栏间距从 2 减为 1 - 传递超预期得分到 StockChangeIndicators 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,7 @@ const VerticalModeLayout = React.memo(({
|
||||
flex={isMobile ? '1' : leftFlex}
|
||||
minWidth={0}
|
||||
w={isMobile ? '100%' : 'auto'}
|
||||
overflowX="hidden"
|
||||
overflowY="auto"
|
||||
h="100%"
|
||||
data-event-list-container="true"
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Box,
|
||||
Text,
|
||||
Tooltip,
|
||||
useColorModeValue,
|
||||
Badge,
|
||||
useBreakpointValue,
|
||||
} from '@chakra-ui/react';
|
||||
import { getImportanceConfig } from '@constants/importanceLevels';
|
||||
@@ -20,14 +20,20 @@ import dayjs from 'dayjs';
|
||||
|
||||
// 导入子组件
|
||||
import {
|
||||
ImportanceStamp,
|
||||
EventTimeline,
|
||||
EventFollowButton,
|
||||
EventEngagement,
|
||||
KeywordsCarousel,
|
||||
} from './atoms';
|
||||
import StockChangeIndicators from '@components/StockChangeIndicators';
|
||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||
import { TrendingUp, TrendingDown } from 'lucide-react';
|
||||
|
||||
// 数字格式化(8600 → 8.6k)
|
||||
const formatCompactNumber = (num) => {
|
||||
if (num == null) return '0';
|
||||
if (num >= 10000) return (num / 10000).toFixed(1) + 'w';
|
||||
if (num >= 1000) return (num / 1000).toFixed(1) + 'k';
|
||||
return String(num);
|
||||
};
|
||||
|
||||
/**
|
||||
* 横向布局的动态新闻事件卡片组件
|
||||
@@ -78,9 +84,11 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
||||
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: '16px', md: '120px' }); // 桌面端为关键词留空间,移动端不显示关键词
|
||||
const spacing = useBreakpointValue({ base: 1, md: 3 }); // 间距(移动端更紧凑)
|
||||
|
||||
// 获取概念标签(优先使用 keywords,fallback 到 industry)
|
||||
const conceptLabel = event.keywords?.[0]?.concept || event.industry;
|
||||
|
||||
/**
|
||||
* 根据平均涨幅计算背景色(专业配色 - 深色主题)
|
||||
* @param {number} avgChange - 平均涨跌幅
|
||||
@@ -148,7 +156,6 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
||||
overflow="visible"
|
||||
_hover={{
|
||||
boxShadow: '2xl',
|
||||
transform: 'translateY(-3px)',
|
||||
borderColor: isSelected ? 'blue.600' : importance.color,
|
||||
}}
|
||||
_before={{
|
||||
@@ -165,7 +172,7 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
||||
borderTopRightRadius: 'xl',
|
||||
opacity: 0.8,
|
||||
}}
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
transition="box-shadow 0.3s, border-color 0.3s"
|
||||
cursor="pointer"
|
||||
onClick={() => onEventClick?.(event)}
|
||||
>
|
||||
@@ -197,31 +204,9 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* 右上角:关注按钮 */}
|
||||
<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梦幻轮播 - 绝对定位在卡片右侧空白处(移动端隐藏) */}
|
||||
{!isMobile && 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 显示完整内容 */}
|
||||
<VStack align="stretch" spacing={2}>
|
||||
{/* 第一行:概念标签 + 标题(内联布局) */}
|
||||
<Tooltip
|
||||
label={event.title}
|
||||
placement="top"
|
||||
@@ -237,15 +222,36 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
||||
cursor="pointer"
|
||||
onClick={(e) => onTitleClick?.(e, event)}
|
||||
mt={1}
|
||||
paddingRight={titlePaddingRight}
|
||||
paddingLeft={isMobile ? '70px' : undefined}
|
||||
lineHeight="1.6"
|
||||
css={{
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* 概念标签(内联) */}
|
||||
{conceptLabel && (
|
||||
<Badge
|
||||
as="span"
|
||||
colorScheme="purple"
|
||||
fontSize="xs"
|
||||
borderRadius="md"
|
||||
px={1.5}
|
||||
py={0.5}
|
||||
mr={1.5}
|
||||
verticalAlign="middle"
|
||||
>
|
||||
{conceptLabel}
|
||||
</Badge>
|
||||
)}
|
||||
{/* 标题(内联) */}
|
||||
<Text
|
||||
as="span"
|
||||
fontSize={titleFontSize}
|
||||
fontWeight="semibold"
|
||||
color={linkColor}
|
||||
lineHeight="1.4"
|
||||
noOfLines={2}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
>
|
||||
{event.title}
|
||||
@@ -253,33 +259,74 @@ const HorizontalDynamicNewsEventCard = React.memo(({
|
||||
</Box>
|
||||
</Tooltip>
|
||||
|
||||
{/* 第二行:涨跌幅数据(左) + 互动指标(右) */}
|
||||
<HStack justify="space-between" align="center" w="full">
|
||||
{/* 左侧:涨跌幅数据,没有时用空盒子占位 */}
|
||||
<Box flex="1">
|
||||
<StockChangeIndicators
|
||||
maxChange={event.related_max_chg}
|
||||
avgChange={event.related_avg_chg}
|
||||
expectationScore={event.expectation_surprise_score}
|
||||
size={indicatorSize}
|
||||
/>
|
||||
</Box>
|
||||
{/* 底部:左侧涨跌幅 + 右侧工具栏 */}
|
||||
<HStack justify="space-between" align="center" w="full" pt={1}>
|
||||
{/* 左侧:涨跌幅指标(保持默认样式) */}
|
||||
<StockChangeIndicators
|
||||
avgChange={event.related_avg_chg}
|
||||
maxChange={event.related_max_chg}
|
||||
expectationScore={event.expectation_score || event.expectation_surprise_score}
|
||||
size="default"
|
||||
/>
|
||||
|
||||
{/* 右侧:互动指标,始终靠右 */}
|
||||
{showEngagement && (
|
||||
<Box flexShrink={0}>
|
||||
<EventEngagement
|
||||
eventId={event.id}
|
||||
viewCount={event.view_count}
|
||||
followerCount={event.follower_count}
|
||||
bullishCount={event.bullish_count}
|
||||
bearishCount={event.bearish_count}
|
||||
userVote={event.user_vote}
|
||||
size="md"
|
||||
onVoteChange={onVoteChange}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{/* 右侧:工具栏 */}
|
||||
<HStack spacing={1} align="center" onClick={(e) => e.stopPropagation()}>
|
||||
{/* 浏览量 - 上下结构 */}
|
||||
<VStack spacing={0} align="center">
|
||||
<Text fontSize="xs" color="gray.400">👁</Text>
|
||||
<Text fontSize="2xs" color="gray.400">{formatCompactNumber(event.view_count)}</Text>
|
||||
</VStack>
|
||||
|
||||
{/* 收藏按钮 - 上下结构 */}
|
||||
<EventFollowButton
|
||||
isFollowing={isFollowing}
|
||||
followerCount={followerCount}
|
||||
onToggle={() => onToggleFollow?.(event.id)}
|
||||
size="sm"
|
||||
showCount={true}
|
||||
variant="minimal"
|
||||
layout="vertical"
|
||||
/>
|
||||
|
||||
{/* 合并投票按钮 - 左红右绿,上图标下文字 */}
|
||||
<HStack spacing={0} borderRadius="md" overflow="hidden">
|
||||
{/* 看涨 - 红色左半边 */}
|
||||
<Tooltip label="看涨" placement="top" hasArrow>
|
||||
<VStack
|
||||
as="button"
|
||||
bg="#C53030"
|
||||
color="white"
|
||||
px={2}
|
||||
py={1}
|
||||
spacing={0}
|
||||
_hover={{ bg: '#9B2C2C' }}
|
||||
transition="all 0.2s"
|
||||
onClick={() => onVoteChange?.({ eventId: event.id, voteType: 'bullish' })}
|
||||
>
|
||||
<TrendingUp size={14} />
|
||||
<Text fontSize="2xs" fontWeight="semibold">{formatCompactNumber(event.bullish_count)}</Text>
|
||||
</VStack>
|
||||
</Tooltip>
|
||||
|
||||
{/* 看跌 - 绿色右半边 */}
|
||||
<Tooltip label="看跌" placement="top" hasArrow>
|
||||
<VStack
|
||||
as="button"
|
||||
bg="#38A169"
|
||||
color="white"
|
||||
px={2}
|
||||
py={1}
|
||||
spacing={0}
|
||||
_hover={{ bg: '#2F855A' }}
|
||||
transition="all 0.2s"
|
||||
onClick={() => onVoteChange?.({ eventId: event.id, voteType: 'bearish' })}
|
||||
>
|
||||
<TrendingDown size={14} />
|
||||
<Text fontSize="2xs" fontWeight="semibold">{formatCompactNumber(event.bearish_count)}</Text>
|
||||
</VStack>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
|
||||
Reference in New Issue
Block a user