diff --git a/src/views/Community/components/EventCard/DynamicNewsEventCard.js b/src/views/Community/components/EventCard/DynamicNewsEventCard.js index 58ee4f35..07ac4a17 100644 --- a/src/views/Community/components/EventCard/DynamicNewsEventCard.js +++ b/src/views/Community/components/EventCard/DynamicNewsEventCard.js @@ -8,19 +8,14 @@ import { CardBody, Box, Text, - HStack, - Popover, - PopoverTrigger, - PopoverContent, - PopoverBody, - PopoverArrow, - Portal, + Tooltip, useColorModeValue, } from '@chakra-ui/react'; import moment from 'moment'; -import { getImportanceConfig, getAllImportanceLevels } from '../../../../constants/importanceLevels'; +import { getImportanceConfig } from '../../../../constants/importanceLevels'; // 导入子组件 +import ImportanceBadge from './ImportanceBadge'; import EventFollowButton from './EventFollowButton'; import StockChangeIndicators from '../../../../components/StockChangeIndicators'; @@ -35,7 +30,6 @@ import StockChangeIndicators from '../../../../components/StockChangeIndicators' * @param {Function} props.onEventClick - 卡片点击事件 * @param {Function} props.onTitleClick - 标题点击事件 * @param {Function} props.onToggleFollow - 切换关注事件 - * @param {Object} props.timelineStyle - 时间轴样式配置 * @param {string} props.borderColor - 边框颜色 */ const DynamicNewsEventCard = ({ @@ -47,31 +41,126 @@ const DynamicNewsEventCard = ({ onEventClick, onTitleClick, onToggleFollow, - timelineStyle, borderColor, }) => { - const importance = getImportanceConfig(event.importance); const cardBg = useColorModeValue('white', 'gray.800'); const linkColor = useColorModeValue('blue.600', 'blue.400'); + const importance = getImportanceConfig(event.importance); + + /** + * 判断交易时段(盘前、盘中、盘后) + * @param {string} timestamp - 事件时间戳 + * @returns {'pre-market' | 'trading' | 'after-market'} + */ + const getTradingPeriod = (timestamp) => { + const eventTime = moment(timestamp); + const hour = eventTime.hour(); + const minute = eventTime.minute(); + const timeInMinutes = hour * 60 + minute; + + // 盘中时间:09:30-11:30, 13:00-15:00 + const morningStart = 9 * 60 + 30; // 09:30 = 570分钟 + const morningEnd = 11 * 60 + 30; // 11:30 = 690分钟 + const afternoonStart = 13 * 60; // 13:00 = 780分钟 + const afternoonEnd = 15 * 60; // 15:00 = 900分钟 + + if ((timeInMinutes >= morningStart && timeInMinutes < morningEnd) || + (timeInMinutes >= afternoonStart && timeInMinutes < afternoonEnd)) { + return 'trading'; + } else if (timeInMinutes < morningStart) { + return 'pre-market'; + } else { + return 'after-market'; + } + }; + + /** + * 获取时间标签样式(根据交易时段) + * @param {string} period - 交易时段 + * @returns {Object} Chakra UI 样式对象 + */ + const getTimeLabelStyle = (period) => { + switch (period) { + case 'pre-market': + // 盘前:蓝色系 + return { + bg: useColorModeValue('blue.50', 'blue.900'), + borderColor: useColorModeValue('blue.400', 'blue.500'), + textColor: useColorModeValue('blue.600', 'blue.300'), + }; + case 'trading': + // 盘中:绿色系(表示交易中) + return { + bg: useColorModeValue('green.50', 'green.900'), + borderColor: useColorModeValue('green.400', 'green.500'), + textColor: useColorModeValue('green.600', 'green.300'), + }; + case 'after-market': + // 盘后:灰色系(市场关闭) + return { + bg: useColorModeValue('gray.100', 'gray.800'), + borderColor: useColorModeValue('gray.400', 'gray.500'), + textColor: useColorModeValue('gray.600', 'gray.400'), + }; + default: + return { + bg: useColorModeValue('gray.100', 'gray.800'), + borderColor: useColorModeValue('gray.400', 'gray.500'), + textColor: useColorModeValue('gray.600', 'gray.400'), + }; + } + }; + + /** + * 根据平均涨幅计算背景色(分级策略) + * @param {number} avgChange - 平均涨跌幅 + * @returns {string} Chakra UI 颜色值 + */ + const getChangeBasedBgColor = (avgChange) => { + // 如果没有涨跌幅数据,使用默认的奇偶行背景 + if (avgChange == null) { + return index % 2 === 0 ? cardBg : useColorModeValue('gray.50', 'gray.750'); + } + + // 根据涨跌幅分级返回背景色 + if (avgChange >= 5) { + return useColorModeValue('red.100', 'red.900'); + } else if (avgChange >= 3) { + return useColorModeValue('red.75', 'red.800'); + } else if (avgChange > 0) { + return useColorModeValue('red.50', 'red.700'); + } else if (avgChange > -3) { + return useColorModeValue('green.50', 'green.700'); + } else if (avgChange > -5) { + return useColorModeValue('green.75', 'green.800'); + } else { + return useColorModeValue('green.100', 'green.900'); + } + }; + + // 获取当前事件的交易时段和样式 + const tradingPeriod = getTradingPeriod(event.created_at); + const timeLabelStyle = getTimeLabelStyle(tradingPeriod); return ( - - {/* 时间标签 - 在卡片上方 */} + + {/* 时间标签 - 在卡片上方,宽度自适应,左对齐 */} {moment(event.created_at).format('YYYY-MM-DD HH:mm')} @@ -83,7 +172,7 @@ const DynamicNewsEventCard = ({ position="relative" bg={isSelected ? useColorModeValue('blue.50', 'blue.900') - : (index % 2 === 0 ? cardBg : useColorModeValue('gray.50', 'gray.750')) + : getChangeBasedBgColor(event.related_avg_chg) } borderWidth={isSelected ? "2px" : "1px"} borderColor={isSelected @@ -103,72 +192,8 @@ const DynamicNewsEventCard = ({ onClick={() => onEventClick?.(event)} > - {/* 左上角:重要性矩形角标(镂空边框样式) */} - - - - {importance.label} - - - - - - - - - 重要性等级说明 - - {getAllImportanceLevels().map(item => ( - - - {item.level} - - - - {item.label}: - - {item.description} - - - ))} - - - - - + {/* 左上角:重要性标签 */} + {/* 右上角:关注按钮 */} @@ -182,24 +207,35 @@ const DynamicNewsEventCard = ({ - {/* 标题 - 最多两行,添加上边距避免与角标重叠 */} - onTitleClick?.(e, event)} - mt={1} - paddingRight="10px" + {/* 标题 - 最多两行,hover 显示完整内容 */} + - onTitleClick?.(e, event)} + mt={1} + paddingRight="10px" > - {event.title} - - + + {event.title} + + + {/* 第二行:涨跌幅数据 */} onEventClick?.(event)} > - {/* 左上角:重要性横向徽章 */} - - - - {importance.label} - - - - - - - - - 重要性等级说明 - - {getAllImportanceLevels().map(item => ( - - - {item.label} - - - - {item.label}: - - {item.description} - - - ))} - - - - - + {/* 左上角:重要性标签 */} + {/* 右上角:关注按钮 */} diff --git a/src/views/Community/components/EventCard/ImportanceBadge.js b/src/views/Community/components/EventCard/ImportanceBadge.js new file mode 100644 index 00000000..e9437f34 --- /dev/null +++ b/src/views/Community/components/EventCard/ImportanceBadge.js @@ -0,0 +1,95 @@ +// src/views/Community/components/EventCard/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 ( + + + + {importanceConfig.label} + + + + + + + + + 重要性等级说明 + + {getAllImportanceLevels().map(item => ( + + + {item.label} + + + + {item.label}: + + {item.description} + + + ))} + + + + + + ); +}; + +export default ImportanceBadge;