update ui
This commit is contained in:
@@ -22,8 +22,8 @@ export const IMPORTANCE_LEVELS = {
|
||||
badgeBg: '#dc2626', // 圆形徽章背景色 - 红色
|
||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||
icon: WarningIcon,
|
||||
label: '极高',
|
||||
stampText: '极', // 印章文字
|
||||
label: 'S级',
|
||||
stampText: 'S', // 印章文字
|
||||
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
||||
dotBg: 'red.800',
|
||||
description: '重大事件,市场影响深远',
|
||||
@@ -38,8 +38,8 @@ export const IMPORTANCE_LEVELS = {
|
||||
badgeBg: '#ea580c', // 圆形徽章背景色 - 橙色
|
||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||
icon: WarningTwoIcon,
|
||||
label: '高',
|
||||
stampText: '高', // 印章文字
|
||||
label: 'A级',
|
||||
stampText: 'A', // 印章文字
|
||||
stampFont: "'STXingkai', 'FangSong', 'STFangsong', cursive", // 行楷/草书
|
||||
dotBg: 'red.600',
|
||||
description: '重要事件,影响较大',
|
||||
@@ -54,8 +54,8 @@ export const IMPORTANCE_LEVELS = {
|
||||
badgeBg: '#2563eb', // 圆形徽章背景色 - 蓝色
|
||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||
icon: InfoIcon,
|
||||
label: '中',
|
||||
stampText: '中', // 印章文字
|
||||
label: 'B级',
|
||||
stampText: 'B', // 印章文字
|
||||
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
||||
dotBg: 'red.500',
|
||||
description: '普通事件,有一定影响',
|
||||
@@ -70,8 +70,8 @@ export const IMPORTANCE_LEVELS = {
|
||||
badgeBg: '#10b981', // 圆形徽章背景色 - 青绿色(替代灰色)
|
||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||
icon: CheckCircleIcon,
|
||||
label: '低',
|
||||
stampText: '低', // 印章文字
|
||||
label: 'C级',
|
||||
stampText: 'C', // 印章文字
|
||||
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
||||
dotBg: 'red.400',
|
||||
description: '参考事件,影响有限',
|
||||
|
||||
@@ -497,7 +497,7 @@ const CompactSearchBox = ({
|
||||
</Tooltip>
|
||||
|
||||
{/* 重要性筛选 */}
|
||||
<Tooltip title="重要性筛选">
|
||||
<Tooltip title="事件等级筛选">
|
||||
<AntSelect
|
||||
mode="multiple"
|
||||
value={importance}
|
||||
@@ -506,14 +506,14 @@ const CompactSearchBox = ({
|
||||
width: 120,
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
placeholder="重要性"
|
||||
placeholder="事件等级"
|
||||
maxTagCount={0}
|
||||
maxTagPlaceholder={(omittedValues) => `已选 ${omittedValues.length} 项`}
|
||||
>
|
||||
<Option value="S">极高</Option>
|
||||
<Option value="A">高</Option>
|
||||
<Option value="B">中</Option>
|
||||
<Option value="C">低</Option>
|
||||
<Option value="S">S级</Option>
|
||||
<Option value="A">A级</Option>
|
||||
<Option value="B">B级</Option>
|
||||
<Option value="C">C级</Option>
|
||||
</AntSelect>
|
||||
</Tooltip>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// src/views/Community/components/EventCard/EventTimeline.js
|
||||
import React from 'react';
|
||||
import { Box, VStack, Text, useColorModeValue } from '@chakra-ui/react';
|
||||
import { Box, VStack, Text, useColorModeValue, Badge } from '@chakra-ui/react';
|
||||
import moment from 'moment';
|
||||
|
||||
/**
|
||||
@@ -10,10 +10,11 @@ import moment from 'moment';
|
||||
* @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' }) => {
|
||||
const EventTimeline = ({ createdAt, timelineStyle, borderColor, minHeight = '40px', importance }) => {
|
||||
return (
|
||||
<VStack spacing={0} align="center" minW="65px">
|
||||
<VStack spacing={0} align="center" minW="65px" position="relative">
|
||||
{/* 时间长方形卡片 - 更紧凑 */}
|
||||
<Box
|
||||
{...(timelineStyle.bgGradient ? { bgGradient: timelineStyle.bgGradient } : { bg: timelineStyle.bg })}
|
||||
@@ -26,7 +27,28 @@ const EventTimeline = ({ createdAt, timelineStyle, borderColor, minHeight = '40p
|
||||
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"
|
||||
|
||||
@@ -19,6 +19,7 @@ import ImportanceStamp from './ImportanceStamp';
|
||||
import EventTimeline from './EventTimeline';
|
||||
import EventFollowButton from './EventFollowButton';
|
||||
import StockChangeIndicators from '../../../../components/StockChangeIndicators';
|
||||
import KeywordsCarousel from './KeywordsCarousel';
|
||||
|
||||
/**
|
||||
* 横向布局的动态新闻事件卡片组件
|
||||
@@ -101,16 +102,12 @@ const HorizontalDynamicNewsEventCard = ({
|
||||
timelineStyle={timelineStyle}
|
||||
borderColor={borderColor}
|
||||
minHeight={layout === 'four-row' ? '60px' : 0}
|
||||
importance={importance}
|
||||
/>
|
||||
|
||||
{/* 右侧事件卡片容器(带印章) */}
|
||||
<Box flex="1" position="relative" pt={6}>
|
||||
{/* 右上角:重要性印章(放在卡片外层) */}
|
||||
<Box position="absolute" top={-2} right={4} zIndex={10}>
|
||||
<ImportanceStamp importance={event.importance} size="sm" />
|
||||
</Box>
|
||||
|
||||
{/* 事件卡片 */}
|
||||
{/* 右侧事件卡片容器 */}
|
||||
<Box flex="1" position="relative">
|
||||
{/* 事件卡片 - 增强美化效果 */}
|
||||
<Card
|
||||
position="relative"
|
||||
bg={isSelected
|
||||
@@ -123,15 +120,27 @@ const HorizontalDynamicNewsEventCard = ({
|
||||
? selectedBorderColor
|
||||
: borderColor
|
||||
}
|
||||
borderRadius="lg"
|
||||
borderRadius="xl"
|
||||
boxShadow={isSelected ? "xl" : "md"}
|
||||
overflow="visible"
|
||||
_hover={{
|
||||
boxShadow: '2xl',
|
||||
transform: 'translateY(-2px)',
|
||||
transform: 'translateY(-3px)',
|
||||
borderColor: isSelected ? 'blue.600' : importance.color,
|
||||
}}
|
||||
transition="all 0.3s ease"
|
||||
_before={{
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: '4px',
|
||||
bgGradient: `linear(to-r, ${importance.color}, ${importance.borderColor})`,
|
||||
borderTopLeftRadius: 'xl',
|
||||
borderTopRightRadius: 'xl',
|
||||
opacity: isSelected ? 1 : 0.7,
|
||||
}}
|
||||
transition="all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
|
||||
cursor="pointer"
|
||||
onClick={() => onEventClick?.(event)}
|
||||
>
|
||||
@@ -179,13 +188,37 @@ const HorizontalDynamicNewsEventCard = ({
|
||||
</Box>
|
||||
</Tooltip>
|
||||
|
||||
{/* 第二行:涨跌幅数据 */}
|
||||
<StockChangeIndicators
|
||||
avgChange={event.related_avg_chg}
|
||||
maxChange={event.related_max_chg}
|
||||
weekChange={event.related_week_chg}
|
||||
size={indicatorSize}
|
||||
/>
|
||||
{/* 第二行:涨跌幅数据 + Keywords轮播 */}
|
||||
<HStack spacing={3} align="center" justify="space-between" w="full">
|
||||
{/* 左侧:涨跌幅数据 */}
|
||||
<StockChangeIndicators
|
||||
avgChange={event.related_avg_chg}
|
||||
maxChange={event.related_max_chg}
|
||||
weekChange={event.related_week_chg}
|
||||
size={indicatorSize}
|
||||
/>
|
||||
|
||||
{/* 右侧:Keywords轮播(半透明效果) */}
|
||||
{event.keywords && event.keywords.length > 0 && (
|
||||
<Box
|
||||
flex="1"
|
||||
minW={0}
|
||||
opacity={0.85}
|
||||
_hover={{ opacity: 1 }}
|
||||
transition="opacity 0.2s"
|
||||
>
|
||||
<KeywordsCarousel
|
||||
keywords={event.keywords}
|
||||
displayCount={3}
|
||||
interval={3500}
|
||||
onKeywordClick={(keyword) => {
|
||||
console.log('Keyword clicked:', keyword);
|
||||
// TODO: 实现关键词筛选功能
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</HStack>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
133
src/views/Community/components/EventCard/KeywordsCarousel.js
Normal file
133
src/views/Community/components/EventCard/KeywordsCarousel.js
Normal file
@@ -0,0 +1,133 @@
|
||||
// src/views/Community/components/EventCard/KeywordsCarousel.js
|
||||
// Keywords标签轮播组件
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box, Tag, HStack, useColorModeValue, Tooltip } from '@chakra-ui/react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
/**
|
||||
* Keywords标签轮播组件
|
||||
* @param {Array} keywords - 关键词数组
|
||||
* @param {number} displayCount - 显示的标签数量(默认3个)
|
||||
* @param {number} interval - 轮播间隔(毫秒,默认3000ms)
|
||||
* @param {Function} onKeywordClick - 关键词点击回调
|
||||
*/
|
||||
const KeywordsCarousel = ({
|
||||
keywords = [],
|
||||
displayCount = 3,
|
||||
interval = 3000,
|
||||
onKeywordClick
|
||||
}) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
|
||||
// 颜色配置
|
||||
const tagBg = useColorModeValue('rgba(66, 153, 225, 0.1)', 'rgba(66, 153, 225, 0.2)');
|
||||
const tagColor = useColorModeValue('blue.600', 'blue.300');
|
||||
const tagBorder = useColorModeValue('blue.200', 'blue.600');
|
||||
|
||||
// 如果没有keywords,不渲染
|
||||
if (!keywords || keywords.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 自动轮播
|
||||
useEffect(() => {
|
||||
if (isPaused || keywords.length <= displayCount) return;
|
||||
|
||||
const timer = setInterval(() => {
|
||||
setCurrentIndex((prev) => (prev + 1) % keywords.length);
|
||||
}, interval);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, [isPaused, keywords.length, displayCount, interval]);
|
||||
|
||||
// 获取当前显示的关键词
|
||||
const getVisibleKeywords = () => {
|
||||
if (keywords.length <= displayCount) {
|
||||
return keywords;
|
||||
}
|
||||
|
||||
const visible = [];
|
||||
for (let i = 0; i < displayCount; i++) {
|
||||
const index = (currentIndex + i) % keywords.length;
|
||||
visible.push(keywords[index]);
|
||||
}
|
||||
return visible;
|
||||
};
|
||||
|
||||
const visibleKeywords = getVisibleKeywords();
|
||||
|
||||
return (
|
||||
<Box
|
||||
onMouseEnter={() => setIsPaused(true)}
|
||||
onMouseLeave={() => setIsPaused(false)}
|
||||
position="relative"
|
||||
>
|
||||
<HStack spacing={1.5} flexWrap="wrap">
|
||||
<AnimatePresence mode="wait">
|
||||
{visibleKeywords.map((keyword, index) => (
|
||||
<MotionBox
|
||||
key={`${keyword}-${currentIndex}-${index}`}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.8 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<Tooltip
|
||||
label={`点击筛选"${keyword}"`}
|
||||
placement="top"
|
||||
hasArrow
|
||||
>
|
||||
<Tag
|
||||
size="sm"
|
||||
variant="subtle"
|
||||
bg={tagBg}
|
||||
color={tagColor}
|
||||
borderWidth="1px"
|
||||
borderColor={tagBorder}
|
||||
cursor="pointer"
|
||||
fontSize="10px"
|
||||
fontWeight="medium"
|
||||
px={2}
|
||||
py={0.5}
|
||||
borderRadius="full"
|
||||
_hover={{
|
||||
bg: useColorModeValue('blue.100', 'blue.700'),
|
||||
transform: 'scale(1.05)',
|
||||
boxShadow: 'md'
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onKeywordClick?.(keyword);
|
||||
}}
|
||||
>
|
||||
#{keyword}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</MotionBox>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</HStack>
|
||||
|
||||
{/* 如果有更多关键词,显示指示器 */}
|
||||
{keywords.length > displayCount && (
|
||||
<Box
|
||||
position="absolute"
|
||||
right={-2}
|
||||
top="50%"
|
||||
transform="translateY(-50%)"
|
||||
fontSize="10px"
|
||||
color="gray.400"
|
||||
fontWeight="bold"
|
||||
>
|
||||
+{keywords.length - displayCount}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default KeywordsCarousel;
|
||||
Reference in New Issue
Block a user