refactor: Community 目录结构重组 + 修复导入路径 + 添加 Mock 数据

## 目录重构
- DynamicNewsCard/ → DynamicNews/(含 layouts/, hooks/ 子目录)
- EventCard 原子组件 → EventCard/atoms/
- EventDetailModal 独立目录化
- HotEvents 独立目录化(含 CSS)
- SearchFilters 独立目录化(CompactSearchBox, TradingTimeFilter)

## 导入路径修复
- EventCard/*.js: 统一使用 @constants/, @utils/, @components/ 别名
- atoms/*.js: 修复移动后的相对路径问题
- DynamicNewsCard.js: 更新 contexts, store, constants 导入
- EventHeaderInfo.js, CompactMetaBar.js: 修复 EventFollowButton 导入

## Mock Handler 添加
- /api/events/:eventId/expectation-score - 事件超预期得分
- /api/index/:indexCode/realtime - 指数实时行情

## 警告修复
- CitationMark.js: overlayInnerStyle → styles (Antd 5.x)
- CitedContent.js: 移除不支持的 jsx 属性

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-09 13:16:43 +08:00
parent aac031f17c
commit 44fcef5eae
47 changed files with 409 additions and 76 deletions

View File

@@ -0,0 +1,56 @@
// src/views/Community/components/EventCard/EventDescription.js
import React, { useState } from 'react';
import { Box, Text, Button } from '@chakra-ui/react';
/**
* 事件描述组件(支持展开/收起)
* @param {Object} props
* @param {string} props.description - 描述文本
* @param {string} props.textColor - 文字颜色
* @param {number} props.minLength - 触发展开/收起的最小长度(默认 120
* @param {number} props.noOfLines - 未展开时显示的行数(默认 3
*/
const EventDescription = ({
description,
textColor,
minLength = 120,
noOfLines = 3
}) => {
const [isExpanded, setIsExpanded] = useState(false);
// 如果没有描述,不渲染
if (!description) {
return null;
}
const handleToggle = (e) => {
e.stopPropagation();
setIsExpanded(!isExpanded);
};
return (
<Box>
<Text
color={textColor}
fontSize="sm"
lineHeight="tall"
noOfLines={isExpanded ? undefined : noOfLines}
>
{description}
</Text>
{description.length > minLength && (
<Button
variant="link"
size="xs"
colorScheme="blue"
onClick={handleToggle}
mt={1}
>
{isExpanded ? '收起' : '...展开'}
</Button>
)}
</Box>
);
};
export default EventDescription;

View File

@@ -0,0 +1,64 @@
// src/views/Community/components/EventCard/atoms/EventFollowButton.js
import React from 'react';
import { IconButton, Box } from '@chakra-ui/react';
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
/**
* 事件关注按钮组件
* @param {Object} props
* @param {boolean} props.isFollowing - 是否已关注
* @param {number} props.followerCount - 关注数
* @param {Function} props.onToggle - 切换关注状态的回调函数
* @param {string} props.size - 按钮尺寸('xs' | 'sm' | 'md',默认 'sm'
* @param {boolean} props.showCount - 是否显示关注数(默认 true
*/
const EventFollowButton = ({
isFollowing,
followerCount = 0,
onToggle,
size = 'sm',
showCount = true
}) => {
const iconSize = size === 'xs' ? '16px' : size === 'sm' ? '18px' : '22px';
const handleClick = (e) => {
e.stopPropagation();
onToggle?.();
};
return (
<Box display="inline-flex" alignItems="center" gap={1}>
<IconButton
size={size}
colorScheme="yellow"
variant="ghost"
bg="rgba(113, 128, 150, 0.6)"
boxShadow="sm"
_hover={{
bg: 'rgba(113, 128, 150, 0.8)',
boxShadow: 'md'
}}
icon={
isFollowing ? (
<AiFillStar
size={iconSize}
color="gold"
/>
) : (
<AiOutlineStar
size={iconSize}
color="gold"
/>
)
}
onClick={handleClick}
aria-label={isFollowing ? '取消关注' : '关注'}
/>
{/* <Box fontSize="xs" color="gray.500">
{followerCount || 0}
</Box> */}
</Box>
);
};
export default EventFollowButton;

View File

@@ -0,0 +1,100 @@
// src/views/Community/components/EventCard/EventHeader.js
import React from 'react';
import { Box, Text, Heading, Tooltip, HStack } from '@chakra-ui/react';
import EventImportanceBadge from './EventImportanceBadge';
import EventPriceDisplay from './EventPriceDisplay';
/**
* 事件标题头部组件
* @param {Object} props
* @param {string} props.title - 事件标题
* @param {string} props.importance - 重要性等级
* @param {Function} props.onTitleClick - 标题点击事件
* @param {string} props.linkColor - 链接颜色
* @param {boolean} props.compact - 是否紧凑模式(默认 false
* @param {number|null} props.avgChange - 平均涨跌幅(紧凑模式下使用)
* @param {string} props.size - 标题大小('sm' | 'md' | 'lg',默认 'md'
*/
const EventHeader = ({
title,
importance,
onTitleClick,
linkColor,
compact = false,
avgChange = null,
size = 'md'
}) => {
const handleClick = (e) => {
e.preventDefault();
e.stopPropagation();
onTitleClick?.(e);
};
// 紧凑模式:标题 + 标签内联
if (compact) {
return (
<Box flex="1" minW="150px">
<Text
fontSize={size}
fontWeight="bold"
color={linkColor}
lineHeight="1.4"
noOfLines={2}
display="inline"
_hover={{ textDecoration: 'underline', color: 'blue.500' }}
onClick={handleClick}
cursor="pointer"
>
{title}
</Text>
{' '}
{/* 重要性标签 - 内联 */}
<EventImportanceBadge
importance={importance}
showTooltip={false}
size="xs"
/>
{' '}
{/* 价格标签 - 内联 */}
{avgChange != null && (
<EventPriceDisplay
avgChange={avgChange}
compact={true}
inline={true}
/>
)}
</Box>
);
}
// 详细模式:标题 + 提示框的重要性标签
return (
<HStack spacing={2} flex="1" align="center">
<Tooltip
label="点击查看事件详情"
placement="top"
hasArrow
openDelay={500}
>
<Heading
size={size}
color={linkColor}
_hover={{ textDecoration: 'underline', color: 'blue.500' }}
onClick={handleClick}
cursor="pointer"
>
{title}
</Heading>
</Tooltip>
<EventImportanceBadge
importance={importance}
showTooltip={true}
showIcon={true}
size="xs"
/>
</HStack>
);
};
export default EventHeader;

View File

@@ -0,0 +1,96 @@
// src/views/Community/components/EventCard/atoms/EventImportanceBadge.js
import React from 'react';
import { Badge, Tooltip, VStack, HStack, Text, Divider, Circle } from '@chakra-ui/react';
import { InfoIcon } from '@chakra-ui/icons';
import { getImportanceConfig, getAllImportanceLevels } from '@constants/importanceLevels';
/**
* 事件重要性等级标签组件
* @param {Object} props
* @param {string} props.importance - 重要性等级S/A/B/C/D
* @param {boolean} props.showTooltip - 是否显示详细提示框(默认 false
* @param {boolean} props.showIcon - 是否显示信息图标(默认 false
* @param {string} props.size - 标签大小xs/sm/md/lg默认 xs
*/
const EventImportanceBadge = ({
importance,
showTooltip = false,
showIcon = false,
size = 'xs'
}) => {
const importanceConfig = getImportanceConfig(importance);
// 简单模式:只显示标签
if (!showTooltip) {
return (
<Badge
colorScheme={importanceConfig.color.split('.')[0]}
fontSize={size}
px={size === 'xs' ? 2 : size === 'sm' ? 2.5 : 3}
py={size === 'xs' ? 1 : size === 'sm' ? 1 : 1.5}
borderRadius="md"
fontWeight="bold"
display="inline-flex"
alignItems="center"
verticalAlign="middle"
>
{importance || 'C'}
</Badge>
);
}
// 详细模式:带提示框的标签
return (
<Tooltip
label={
<VStack align="start" spacing={1} maxW="320px">
<Text fontWeight="bold" fontSize="sm" mb={1}>
重要性等级说明
</Text>
<Divider borderColor="gray.300" />
{getAllImportanceLevels().map((level) => (
<HStack key={level.level} spacing={2} align="center" w="full" py={0.5}>
<Circle
size="8px"
bg={level.dotBg}
flexShrink={0}
/>
<Text fontSize="xs" color="gray.700" lineHeight="1.5">
<Text as="span" fontWeight="bold">{level.level}</Text>
{level.description}
</Text>
</HStack>
))}
</VStack>
}
placement="top"
hasArrow
bg="white"
color="gray.800"
fontSize="md"
p={3}
borderRadius="lg"
borderWidth="1px"
borderColor="gray.200"
boxShadow="lg"
>
<Badge
colorScheme={importanceConfig.color.split('.')[0]}
px={1.5}
py={0.5}
borderRadius="md"
fontSize={size}
cursor="help"
display="flex"
alignItems="center"
gap={1}
flexShrink={0}
>
{showIcon && <InfoIcon boxSize={2.5} />}
{importance || 'C'}
</Badge>
</Tooltip>
);
};
export default EventImportanceBadge;

View File

@@ -0,0 +1,155 @@
// src/views/Community/components/EventCard/atoms/EventPriceDisplay.js
import React, { useState } from 'react';
import { HStack, Box, Text, Tooltip, Progress } from '@chakra-ui/react';
import { PriceArrow } from '@utils/priceFormatters';
/**
* 事件价格变动显示组件
* @param {Object} props
* @param {number|null} props.avgChange - 平均涨跌幅
* @param {number|null} props.maxChange - 最大涨跌幅
* @param {number|null} props.expectationScore - 超预期得分满分100
* @param {boolean} props.compact - 是否为紧凑模式(只显示平均值,默认 false
* @param {boolean} props.inline - 是否内联显示(默认 false
*/
const EventPriceDisplay = ({
avgChange,
maxChange,
expectationScore,
compact = false,
inline = false
}) => {
// 点击切换显示最大超额/平均超额
const [showAvg, setShowAvg] = useState(false);
// 获取颜色方案
const getColorScheme = (value) => {
if (value == null) return 'gray';
return value > 0 ? 'red' : value < 0 ? 'green' : 'gray';
};
// 格式化百分比
const formatPercent = (value) => {
if (value == null) return '--';
return `${value > 0 ? '+' : ''}${value.toFixed(2)}%`;
};
// 获取超预期得分的颜色(渐变色系)
const getScoreColor = (score) => {
if (score == null) return { bg: 'gray.100', color: 'gray.500', progressColor: 'gray' };
if (score >= 80) return { bg: 'red.50', color: 'red.600', progressColor: 'red' };
if (score >= 60) return { bg: 'orange.50', color: 'orange.600', progressColor: 'orange' };
if (score >= 40) return { bg: 'yellow.50', color: 'yellow.700', progressColor: 'yellow' };
if (score >= 20) return { bg: 'blue.50', color: 'blue.600', progressColor: 'blue' };
return { bg: 'gray.50', color: 'gray.600', progressColor: 'gray' };
};
// 紧凑模式:只显示平均值,内联在标题后
if (compact && avgChange != null) {
return (
<Tooltip label="平均超额" placement="top">
<Box
bg={avgChange > 0 ? 'red.50' : avgChange < 0 ? 'green.50' : 'gray.100'}
color={avgChange > 0 ? 'red.600' : avgChange < 0 ? 'green.600' : 'gray.500'}
fontSize="xs"
px={2}
py={1}
borderRadius="md"
fontWeight="bold"
display={inline ? "inline-flex" : "flex"}
alignItems="center"
gap={1}
verticalAlign="middle"
>
<PriceArrow value={avgChange} />
{formatPercent(avgChange)}
</Box>
</Tooltip>
);
}
const displayValue = showAvg ? avgChange : maxChange;
const displayLabel = showAvg ? '平均超额' : '最大超额';
const scoreColors = getScoreColor(expectationScore);
// 详细模式:显示最大超额(可点击切换)+ 超预期得分
return (
<HStack spacing={3} flexWrap="wrap">
{/* 最大超额/平均超额 - 点击切换 */}
<Tooltip
label={showAvg ? "点击查看最大超额" : "点击查看平均超额"}
placement="top"
hasArrow
>
<Box
bg={displayValue > 0 ? 'red.50' : displayValue < 0 ? 'green.50' : 'gray.100'}
color={displayValue > 0 ? 'red.600' : displayValue < 0 ? 'green.600' : 'gray.500'}
fontSize="xs"
px={2.5}
py={1}
borderRadius="md"
cursor="pointer"
onClick={(e) => {
e.stopPropagation();
setShowAvg(!showAvg);
}}
_hover={{
transform: 'scale(1.02)',
boxShadow: 'sm',
opacity: 0.9
}}
transition="all 0.2s"
border="1px solid"
borderColor={displayValue > 0 ? 'red.200' : displayValue < 0 ? 'green.200' : 'gray.200'}
>
<HStack spacing={1.5}>
<Text fontSize="xs" opacity={0.7} fontWeight="medium">{displayLabel}</Text>
<Text fontWeight="bold" fontSize="sm">
{formatPercent(displayValue)}
</Text>
</HStack>
</Box>
</Tooltip>
{/* 超预期得分 - 精致的进度条样式 */}
{expectationScore != null && (
<Tooltip
label={`超预期得分:${expectationScore.toFixed(0)}满分100分`}
placement="top"
hasArrow
>
<Box
bg={scoreColors.bg}
px={2.5}
py={1}
borderRadius="md"
border="1px solid"
borderColor={`${scoreColors.progressColor}.200`}
minW="90px"
>
<HStack spacing={2}>
<Text fontSize="xs" color={scoreColors.color} fontWeight="medium" opacity={0.8}>
超预期
</Text>
<Box flex={1} minW="40px">
<Progress
value={expectationScore}
max={100}
size="xs"
colorScheme={scoreColors.progressColor}
borderRadius="full"
bg={`${scoreColors.progressColor}.100`}
/>
</Box>
<Text fontSize="xs" fontWeight="bold" color={scoreColors.color}>
{expectationScore.toFixed(0)}
</Text>
</HStack>
</Box>
</Tooltip>
)}
</HStack>
);
};
export default EventPriceDisplay;

View File

@@ -0,0 +1,58 @@
// src/views/Community/components/EventCard/EventStats.js
import React from 'react';
import { HStack, Text, Tooltip } from '@chakra-ui/react';
import { ViewIcon, ChatIcon, StarIcon } from '@chakra-ui/icons';
/**
* 事件统计信息组件(浏览量、帖子数、关注数)
* @param {Object} props
* @param {number} props.viewCount - 浏览量
* @param {number} props.postCount - 帖子数
* @param {number} props.followerCount - 关注数
* @param {string} props.size - 尺寸('sm' | 'md',默认 'sm'
* @param {number} props.spacing - 间距(默认 3
* @param {Object} props.display - 响应式显示控制(默认 { base: 'none', md: 'flex' }
* @param {string} props.mutedColor - 文字颜色(可选)
*/
const EventStats = ({
viewCount = 0,
postCount = 0,
followerCount = 0,
size = 'sm',
spacing = 3,
display = { base: 'none', md: 'flex' },
mutedColor
}) => {
const fontSize = size === 'sm' ? 'xs' : 'sm';
const iconSize = size === 'sm' ? '12px' : '16px';
return (
<HStack spacing={spacing} display={display} color={mutedColor}>
{/* 浏览量 */}
<Tooltip label="浏览量" placement="top">
<HStack spacing={1}>
<ViewIcon boxSize={iconSize} />
<Text fontSize={fontSize}>{viewCount}</Text>
</HStack>
</Tooltip>
{/* 帖子数 */}
<Tooltip label="帖子数" placement="top">
<HStack spacing={1}>
<ChatIcon boxSize={iconSize} />
<Text fontSize={fontSize}>{postCount}</Text>
</HStack>
</Tooltip>
{/* 关注数 */}
<Tooltip label="关注数" placement="top">
<HStack spacing={1}>
<StarIcon boxSize={iconSize} />
<Text fontSize={fontSize}>{followerCount}</Text>
</HStack>
</Tooltip>
</HStack>
);
};
export default EventStats;

View File

@@ -0,0 +1,84 @@
// src/views/Community/components/EventCard/EventTimeline.js
import React from 'react';
import { Box, VStack, Text, useColorModeValue, Badge } from '@chakra-ui/react';
import dayjs from 'dayjs';
/**
* 事件时间轴组件
* @param {Object} props
* @param {string} props.createdAt - 事件创建时间
* @param {Object} props.timelineStyle - 时间轴样式配置
* @param {string} props.borderColor - 竖线边框颜色
* @param {string} props.minHeight - 竖线最小高度(例如:'40px' 或 '80px'
* @param {Object} props.importance - 重要性配置对象(包含 level, badgeBg, badgeColor 等)
*/
const EventTimeline = ({ createdAt, timelineStyle, borderColor, minHeight = '40px', importance }) => {
return (
<VStack spacing={0} align="center" minW="65px" position="relative">
{/* 时间长方形卡片 - 更紧凑 */}
<Box
{...(timelineStyle.bgGradient ? { bgGradient: timelineStyle.bgGradient } : { bg: timelineStyle.bg })}
borderWidth={timelineStyle.borderWidth}
borderColor={timelineStyle.borderColor}
borderRadius="md"
px={1.5}
py={1.5}
minW="60px"
textAlign="center"
boxShadow={timelineStyle.boxShadow}
transition="all 0.3s ease"
position="relative"
>
{/* 重要性等级徽章 - 左上角 */}
{importance && (
<Badge
position="absolute"
top="-6px"
left="-6px"
bg={importance.badgeBg}
color={importance.badgeColor}
fontSize="10px"
fontWeight="bold"
borderRadius="full"
px={1.5}
py={0.5}
boxShadow="md"
zIndex={2}
>
{importance.level}
</Badge>
)}
{/* 日期 MM-DD */}
<Text
fontSize="10px"
fontWeight="bold"
color={timelineStyle.textColor}
lineHeight="1.2"
>
{dayjs(createdAt).format('MM-DD')}
</Text>
{/* 时间 HH:mm */}
<Text
fontSize="10px"
fontWeight="bold"
color={timelineStyle.textColor}
lineHeight="1.2"
mt={0.5}
>
{dayjs(createdAt).format('HH:mm')}
</Text>
</Box>
{/* 时间轴竖线 */}
<Box
w="2px"
flex="1"
bg={borderColor}
minH={minHeight}
mt={1}
/>
</VStack>
);
};
export default EventTimeline;

View File

@@ -0,0 +1,95 @@
// src/views/Community/components/EventCard/atoms/ImportanceBadge.js
// 重要性标签通用组件
import React from 'react';
import {
Box,
Text,
HStack,
VStack,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
PopoverArrow,
Portal,
} from '@chakra-ui/react';
import { getImportanceConfig, getAllImportanceLevels } from '@constants/importanceLevels';
/**
* 重要性标签组件(实心样式)
* @param {Object} props
* @param {string} props.importance - 重要性等级 ('S' | 'A' | 'B' | 'C')
* @param {Object} props.position - 定位配置 { top, left, right, bottom },默认为 { top: 0, left: 0 }
*/
const ImportanceBadge = ({ importance, position = { top: 0, left: 0 } }) => {
const importanceConfig = getImportanceConfig(importance);
return (
<Popover trigger="hover" placement="right" isLazy>
<PopoverTrigger>
<Box
position="absolute"
top={position.top}
left={position.left}
right={position.right}
bottom={position.bottom}
zIndex={1}
bg={importanceConfig.badgeBg}
color={importanceConfig.badgeColor || 'white'}
fontSize="11px"
fontWeight="bold"
w="30px"
h="15px"
borderRadius="xl"
display="flex"
alignItems="center"
justifyContent="center"
cursor="help"
boxShadow="sm"
>
{importanceConfig.label}
</Box>
</PopoverTrigger>
<Portal>
<PopoverContent width="auto" maxW="350px">
<PopoverArrow />
<PopoverBody p={3}>
<VStack align="stretch" spacing={2}>
<Text fontSize="sm" fontWeight="bold" mb={1}>
重要性等级说明
</Text>
{getAllImportanceLevels().map(item => (
<HStack key={item.level} spacing={2} align="center">
<Box
w="30px"
h="15px"
bg={item.badgeBg}
color={item.badgeColor || 'white'}
fontSize="9px"
fontWeight="bold"
display="flex"
alignItems="center"
justifyContent="center"
borderRadius="lg"
flexShrink={0}
>
{item.label}
</Box>
<Text fontSize="xs" flex={1}>
<Text as="span" fontWeight="bold">
{item.label}
</Text>
{item.description}
</Text>
</HStack>
))}
</VStack>
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
);
};
export default ImportanceBadge;

View File

@@ -0,0 +1,86 @@
// src/views/Community/components/EventCard/atoms/ImportanceStamp.js
// 重要性印章组件
import React from 'react';
import {
Box,
Text,
useColorModeValue,
} from '@chakra-ui/react';
import { getImportanceConfig } from '@constants/importanceLevels';
/**
* 重要性印章组件(模拟盖章效果)
* @param {Object} props
* @param {string} props.importance - 重要性等级 (S/A/B/C)
* @param {string} props.size - 印章尺寸 ('sm' | 'md' | 'lg')
*/
const ImportanceStamp = ({ importance, size = 'md' }) => {
const config = getImportanceConfig(importance);
// 印章颜色
const stampColor = useColorModeValue(config.badgeBg, config.badgeBg);
// 尺寸配置
const sizeConfig = {
sm: { outer: '40px', inner: '34px', fontSize: 'md', borderOuter: '2px', borderInner: '1.5px' },
md: { outer: '50px', inner: '42px', fontSize: 'xl', borderOuter: '3px', borderInner: '2px' },
lg: { outer: '60px', inner: '52px', fontSize: '2xl', borderOuter: '4px', borderInner: '2.5px' },
};
const currentSize = sizeConfig[size];
return (
<Box
position="relative"
display="inline-block"
>
{/* 外层圆形边框(双圈) */}
<Box
position="relative"
w={currentSize.outer}
h={currentSize.outer}
borderRadius="50%"
borderWidth={currentSize.borderOuter}
borderColor={stampColor}
display="flex"
alignItems="center"
justifyContent="center"
transform="rotate(-15deg)"
opacity={0.9}
boxShadow="0 3px 12px rgba(0,0,0,0.2)"
bg={useColorModeValue('white', 'gray.800')}
_hover={{
opacity: 1,
transform: "rotate(-15deg) scale(1.15)",
boxShadow: "0 4px 16px rgba(0,0,0,0.3)",
}}
transition="all 0.3s ease"
>
{/* 内层圆形边框 */}
<Box
position="absolute"
w={currentSize.inner}
h={currentSize.inner}
borderRadius="50%"
borderWidth={currentSize.borderInner}
borderColor={stampColor}
/>
{/* 印章文字 */}
<Text
fontSize={currentSize.fontSize}
fontWeight="black"
color={stampColor}
fontFamily={config.stampFont}
letterSpacing="2px"
textShadow="0 0 2px currentColor"
>
{config.stampText}
</Text>
</Box>
</Box>
);
};
export default ImportanceStamp;

View File

@@ -0,0 +1,86 @@
// src/views/Community/components/EventCard/atoms/KeywordsCarousel.js
// Keywords标签组件点击切换
import React, { useState } from 'react';
import { Box, Text, Tooltip } from '@chakra-ui/react';
import { PROFESSIONAL_COLORS } from '@constants/professionalTheme';
/**
* Keywords标签组件点击切换下一个
* @param {Array} keywords - 关键词数组
* @param {Function} onKeywordClick - 关键词点击回调
*/
const KeywordsCarousel = ({
keywords = [],
onKeywordClick
}) => {
const [currentIndex, setCurrentIndex] = useState(0);
// 如果没有keywords不渲染
if (!keywords || keywords.length === 0) {
return null;
}
const currentKeyword = keywords[currentIndex];
// 点击切换到下一个关键词
const handleClick = (e) => {
e.stopPropagation();
// 切换到下一个
const nextIndex = (currentIndex + 1) % keywords.length;
setCurrentIndex(nextIndex);
// 触发回调
if (onKeywordClick) {
onKeywordClick(keywords[nextIndex]);
}
};
return (
<Tooltip
label={keywords.length > 1
? `点击切换下一个标签 (${currentIndex + 1}/${keywords.length})`
: '事件标签'}
placement="top"
hasArrow
bg="rgba(20, 20, 20, 0.95)"
color={PROFESSIONAL_COLORS.gold[500]}
fontSize="sm"
>
<Box
position="absolute"
right={4}
top="50%"
transform="translateY(-50%)"
pointerEvents="auto"
zIndex={1}
cursor={keywords.length > 1 ? "pointer" : "default"}
onClick={keywords.length > 1 ? handleClick : undefined}
px={3}
py={1.5}
borderRadius="full"
bg="transparent"
transition="all 0.3s ease"
_hover={keywords.length > 1 ? {
bg: 'rgba(255, 195, 0, 0.15)',
transform: 'translateY(-50%) scale(1.05)',
} : {}}
>
<Text
color={PROFESSIONAL_COLORS.gold[500]}
fontSize="md"
fontWeight="bold"
letterSpacing="wide"
whiteSpace="nowrap"
textShadow="0 0 20px rgba(255, 195, 0, 0.3)"
>
{typeof currentKeyword === 'string'
? currentKeyword
: currentKeyword?.concept || '未知标签'}
</Text>
</Box>
</Tooltip>
);
};
export default KeywordsCarousel;

View File

@@ -0,0 +1,13 @@
// src/views/Community/components/EventCard/atoms/index.js
// 事件卡片原子组件
export { default as EventDescription } from './EventDescription';
export { default as EventFollowButton } from './EventFollowButton';
export { default as EventHeader } from './EventHeader';
export { default as EventImportanceBadge } from './EventImportanceBadge';
export { default as EventPriceDisplay } from './EventPriceDisplay';
export { default as EventStats } from './EventStats';
export { default as EventTimeline } from './EventTimeline';
export { default as ImportanceBadge } from './ImportanceBadge';
export { default as ImportanceStamp } from './ImportanceStamp';
export { default as KeywordsCarousel } from './KeywordsCarousel';