update ui
This commit is contained in:
@@ -22,8 +22,8 @@ export const IMPORTANCE_LEVELS = {
|
|||||||
badgeBg: '#dc2626', // 圆形徽章背景色 - 红色
|
badgeBg: '#dc2626', // 圆形徽章背景色 - 红色
|
||||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||||
icon: WarningIcon,
|
icon: WarningIcon,
|
||||||
label: '极高',
|
label: 'S级',
|
||||||
stampText: '极', // 印章文字
|
stampText: 'S', // 印章文字
|
||||||
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
||||||
dotBg: 'red.800',
|
dotBg: 'red.800',
|
||||||
description: '重大事件,市场影响深远',
|
description: '重大事件,市场影响深远',
|
||||||
@@ -38,8 +38,8 @@ export const IMPORTANCE_LEVELS = {
|
|||||||
badgeBg: '#ea580c', // 圆形徽章背景色 - 橙色
|
badgeBg: '#ea580c', // 圆形徽章背景色 - 橙色
|
||||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||||
icon: WarningTwoIcon,
|
icon: WarningTwoIcon,
|
||||||
label: '高',
|
label: 'A级',
|
||||||
stampText: '高', // 印章文字
|
stampText: 'A', // 印章文字
|
||||||
stampFont: "'STXingkai', 'FangSong', 'STFangsong', cursive", // 行楷/草书
|
stampFont: "'STXingkai', 'FangSong', 'STFangsong', cursive", // 行楷/草书
|
||||||
dotBg: 'red.600',
|
dotBg: 'red.600',
|
||||||
description: '重要事件,影响较大',
|
description: '重要事件,影响较大',
|
||||||
@@ -54,8 +54,8 @@ export const IMPORTANCE_LEVELS = {
|
|||||||
badgeBg: '#2563eb', // 圆形徽章背景色 - 蓝色
|
badgeBg: '#2563eb', // 圆形徽章背景色 - 蓝色
|
||||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||||
icon: InfoIcon,
|
icon: InfoIcon,
|
||||||
label: '中',
|
label: 'B级',
|
||||||
stampText: '中', // 印章文字
|
stampText: 'B', // 印章文字
|
||||||
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
||||||
dotBg: 'red.500',
|
dotBg: 'red.500',
|
||||||
description: '普通事件,有一定影响',
|
description: '普通事件,有一定影响',
|
||||||
@@ -70,8 +70,8 @@ export const IMPORTANCE_LEVELS = {
|
|||||||
badgeBg: '#10b981', // 圆形徽章背景色 - 青绿色(替代灰色)
|
badgeBg: '#10b981', // 圆形徽章背景色 - 青绿色(替代灰色)
|
||||||
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
badgeColor: 'white', // 圆形徽章文字颜色 - 白色
|
||||||
icon: CheckCircleIcon,
|
icon: CheckCircleIcon,
|
||||||
label: '低',
|
label: 'C级',
|
||||||
stampText: '低', // 印章文字
|
stampText: 'C', // 印章文字
|
||||||
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
stampFont: "'STKaiti', 'KaiTi', 'SimKai', serif", // 楷体
|
||||||
dotBg: 'red.400',
|
dotBg: 'red.400',
|
||||||
description: '参考事件,影响有限',
|
description: '参考事件,影响有限',
|
||||||
|
|||||||
@@ -497,7 +497,7 @@ const CompactSearchBox = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{/* 重要性筛选 */}
|
{/* 重要性筛选 */}
|
||||||
<Tooltip title="重要性筛选">
|
<Tooltip title="事件等级筛选">
|
||||||
<AntSelect
|
<AntSelect
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
value={importance}
|
value={importance}
|
||||||
@@ -506,14 +506,14 @@ const CompactSearchBox = ({
|
|||||||
width: 120,
|
width: 120,
|
||||||
borderRadius: '8px'
|
borderRadius: '8px'
|
||||||
}}
|
}}
|
||||||
placeholder="重要性"
|
placeholder="事件等级"
|
||||||
maxTagCount={0}
|
maxTagCount={0}
|
||||||
maxTagPlaceholder={(omittedValues) => `已选 ${omittedValues.length} 项`}
|
maxTagPlaceholder={(omittedValues) => `已选 ${omittedValues.length} 项`}
|
||||||
>
|
>
|
||||||
<Option value="S">极高</Option>
|
<Option value="S">S级</Option>
|
||||||
<Option value="A">高</Option>
|
<Option value="A">A级</Option>
|
||||||
<Option value="B">中</Option>
|
<Option value="B">B级</Option>
|
||||||
<Option value="C">低</Option>
|
<Option value="C">C级</Option>
|
||||||
</AntSelect>
|
</AntSelect>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/views/Community/components/EventCard/EventTimeline.js
|
// src/views/Community/components/EventCard/EventTimeline.js
|
||||||
import React from 'react';
|
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';
|
import moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,10 +10,11 @@ import moment from 'moment';
|
|||||||
* @param {Object} props.timelineStyle - 时间轴样式配置
|
* @param {Object} props.timelineStyle - 时间轴样式配置
|
||||||
* @param {string} props.borderColor - 竖线边框颜色
|
* @param {string} props.borderColor - 竖线边框颜色
|
||||||
* @param {string} props.minHeight - 竖线最小高度(例如:'40px' 或 '80px')
|
* @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 (
|
return (
|
||||||
<VStack spacing={0} align="center" minW="65px">
|
<VStack spacing={0} align="center" minW="65px" position="relative">
|
||||||
{/* 时间长方形卡片 - 更紧凑 */}
|
{/* 时间长方形卡片 - 更紧凑 */}
|
||||||
<Box
|
<Box
|
||||||
{...(timelineStyle.bgGradient ? { bgGradient: timelineStyle.bgGradient } : { bg: timelineStyle.bg })}
|
{...(timelineStyle.bgGradient ? { bgGradient: timelineStyle.bgGradient } : { bg: timelineStyle.bg })}
|
||||||
@@ -26,7 +27,28 @@ const EventTimeline = ({ createdAt, timelineStyle, borderColor, minHeight = '40p
|
|||||||
textAlign="center"
|
textAlign="center"
|
||||||
boxShadow={timelineStyle.boxShadow}
|
boxShadow={timelineStyle.boxShadow}
|
||||||
transition="all 0.3s ease"
|
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 */}
|
{/* 日期 MM-DD */}
|
||||||
<Text
|
<Text
|
||||||
fontSize="10px"
|
fontSize="10px"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import ImportanceStamp from './ImportanceStamp';
|
|||||||
import EventTimeline from './EventTimeline';
|
import EventTimeline from './EventTimeline';
|
||||||
import EventFollowButton from './EventFollowButton';
|
import EventFollowButton from './EventFollowButton';
|
||||||
import StockChangeIndicators from '../../../../components/StockChangeIndicators';
|
import StockChangeIndicators from '../../../../components/StockChangeIndicators';
|
||||||
|
import KeywordsCarousel from './KeywordsCarousel';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 横向布局的动态新闻事件卡片组件
|
* 横向布局的动态新闻事件卡片组件
|
||||||
@@ -101,16 +102,12 @@ const HorizontalDynamicNewsEventCard = ({
|
|||||||
timelineStyle={timelineStyle}
|
timelineStyle={timelineStyle}
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
minHeight={layout === 'four-row' ? '60px' : 0}
|
minHeight={layout === 'four-row' ? '60px' : 0}
|
||||||
|
importance={importance}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 右侧事件卡片容器(带印章) */}
|
{/* 右侧事件卡片容器 */}
|
||||||
<Box flex="1" position="relative" pt={6}>
|
<Box flex="1" position="relative">
|
||||||
{/* 右上角:重要性印章(放在卡片外层) */}
|
{/* 事件卡片 - 增强美化效果 */}
|
||||||
<Box position="absolute" top={-2} right={4} zIndex={10}>
|
|
||||||
<ImportanceStamp importance={event.importance} size="sm" />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* 事件卡片 */}
|
|
||||||
<Card
|
<Card
|
||||||
position="relative"
|
position="relative"
|
||||||
bg={isSelected
|
bg={isSelected
|
||||||
@@ -123,15 +120,27 @@ const HorizontalDynamicNewsEventCard = ({
|
|||||||
? selectedBorderColor
|
? selectedBorderColor
|
||||||
: borderColor
|
: borderColor
|
||||||
}
|
}
|
||||||
borderRadius="lg"
|
borderRadius="xl"
|
||||||
boxShadow={isSelected ? "xl" : "md"}
|
boxShadow={isSelected ? "xl" : "md"}
|
||||||
overflow="visible"
|
overflow="visible"
|
||||||
_hover={{
|
_hover={{
|
||||||
boxShadow: '2xl',
|
boxShadow: '2xl',
|
||||||
transform: 'translateY(-2px)',
|
transform: 'translateY(-3px)',
|
||||||
borderColor: isSelected ? 'blue.600' : importance.color,
|
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"
|
cursor="pointer"
|
||||||
onClick={() => onEventClick?.(event)}
|
onClick={() => onEventClick?.(event)}
|
||||||
>
|
>
|
||||||
@@ -179,13 +188,37 @@ const HorizontalDynamicNewsEventCard = ({
|
|||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{/* 第二行:涨跌幅数据 */}
|
{/* 第二行:涨跌幅数据 + Keywords轮播 */}
|
||||||
<StockChangeIndicators
|
<HStack spacing={3} align="center" justify="space-between" w="full">
|
||||||
avgChange={event.related_avg_chg}
|
{/* 左侧:涨跌幅数据 */}
|
||||||
maxChange={event.related_max_chg}
|
<StockChangeIndicators
|
||||||
weekChange={event.related_week_chg}
|
avgChange={event.related_avg_chg}
|
||||||
size={indicatorSize}
|
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>
|
</VStack>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</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