diff --git a/src/components/StockChangeIndicators.js b/src/components/StockChangeIndicators.js index b9cc3039..a787239f 100644 --- a/src/components/StockChangeIndicators.js +++ b/src/components/StockChangeIndicators.js @@ -4,6 +4,7 @@ import React from 'react'; import { Flex, Box, Text, useColorModeValue } from '@chakra-ui/react'; import { TriangleUpIcon, TriangleDownIcon } from '@chakra-ui/icons'; +import { getChangeColor } from '../utils/colorUtils'; /** * 股票涨跌幅指标组件(3分天下布局) @@ -23,19 +24,14 @@ const StockChangeIndicators = ({ const isComfortable = size === 'comfortable'; const isDefault = size === 'default'; - // 根据涨跌幅获取数字颜色(统一颜色,不分级) + // 根据涨跌幅获取数字颜色(动态深浅) const getNumberColor = (value) => { if (value == null) { return useColorModeValue('gray.700', 'gray.400'); } - // 0值使用中性灰色 - if (value === 0) { - return 'gray.700'; - } - - // 统一颜色:上涨红色,下跌绿色 - return value > 0 ? 'red.500' : 'green.500'; + // 使用动态颜色函数 + return getChangeColor(value); }; // 根据涨跌幅获取背景色(永远比文字色浅) diff --git a/src/constants/importanceLevels.js b/src/constants/importanceLevels.js index 13f2f606..f53d1e40 100644 --- a/src/constants/importanceLevels.js +++ b/src/constants/importanceLevels.js @@ -23,6 +23,7 @@ export const IMPORTANCE_LEVELS = { badgeColor: 'white', // 圆形徽章文字颜色 - 白色 icon: WarningIcon, label: '极高', + stampText: '极', // 印章文字 dotBg: 'red.800', description: '重大事件,市场影响深远', antdColor: '#cf1322', @@ -37,6 +38,7 @@ export const IMPORTANCE_LEVELS = { badgeColor: 'white', // 圆形徽章文字颜色 - 白色 icon: WarningTwoIcon, label: '高', + stampText: '高', // 印章文字 dotBg: 'red.600', description: '重要事件,影响较大', antdColor: '#ff4d4f', @@ -51,6 +53,7 @@ export const IMPORTANCE_LEVELS = { badgeColor: 'white', // 圆形徽章文字颜色 - 白色 icon: InfoIcon, label: '中', + stampText: '中', // 印章文字 dotBg: 'red.500', description: '普通事件,有一定影响', antdColor: '#ff7875', @@ -65,6 +68,7 @@ export const IMPORTANCE_LEVELS = { badgeColor: 'white', // 圆形徽章文字颜色 - 白色 icon: CheckCircleIcon, label: '低', + stampText: '低', // 印章文字 dotBg: 'red.400', description: '参考事件,影响有限', antdColor: '#ffa39e', diff --git a/src/utils/colorUtils.js b/src/utils/colorUtils.js new file mode 100644 index 00000000..884a8f8f --- /dev/null +++ b/src/utils/colorUtils.js @@ -0,0 +1,98 @@ +// src/utils/colorUtils.js +// 颜色工具函数 - 根据涨跌幅动态计算颜色深浅 + +/** + * 根据涨跌幅获取颜色(深浅动态变化) + * @param {number} change - 涨跌幅百分比 + * @returns {string} Chakra UI 颜色值 + */ +export const getChangeColor = (change) => { + if (change === null || change === undefined || isNaN(change)) { + return 'gray.500'; + } + + const absChange = Math.abs(change); + + if (change > 0) { + // 涨:红色系,根据涨幅深浅 + if (absChange >= 9) return 'red.900'; // 涨停或接近涨停 + if (absChange >= 7) return 'red.800'; + if (absChange >= 5) return 'red.700'; + if (absChange >= 3) return 'red.600'; + if (absChange >= 2) return 'red.500'; + if (absChange >= 1) return 'red.400'; + return 'red.300'; // 微涨 + } else if (change < 0) { + // 跌:绿色系,根据跌幅深浅 + if (absChange >= 9) return 'green.900'; // 跌停或接近跌停 + if (absChange >= 7) return 'green.800'; + if (absChange >= 5) return 'green.700'; + if (absChange >= 3) return 'green.600'; + if (absChange >= 2) return 'green.500'; + if (absChange >= 1) return 'green.400'; + return 'green.300'; // 微跌 + } + + return 'gray.500'; // 平盘 +}; + +/** + * 获取涨跌幅背景渐变色(用于精简模式卡片) + * @param {number} change - 涨跌幅百分比 + * @param {boolean} useDark - 是否使用深色模式 + * @returns {string} Chakra UI bgGradient 值 + */ +export const getChangeBackgroundGradient = (change, useDark = false) => { + if (change === null || change === undefined || isNaN(change)) { + return 'linear(to-br, gray.50, gray.100)'; + } + + const absChange = Math.abs(change); + + if (change > 0) { + // 涨:红色渐变背景 + if (absChange >= 9) return 'linear(to-br, red.100, red.200)'; + if (absChange >= 7) return 'linear(to-br, red.50, red.150)'; + if (absChange >= 5) return 'linear(to-br, red.50, red.100)'; + if (absChange >= 3) return 'linear(to-br, red.50, red.100)'; + return 'linear(to-br, red.50, red.50)'; + } else if (change < 0) { + // 跌:绿色渐变背景 + if (absChange >= 9) return 'linear(to-br, green.100, green.200)'; + if (absChange >= 7) return 'linear(to-br, green.50, green.150)'; + if (absChange >= 5) return 'linear(to-br, green.50, green.100)'; + if (absChange >= 3) return 'linear(to-br, green.50, green.100)'; + return 'linear(to-br, green.50, green.50)'; + } + + return 'linear(to-br, gray.50, gray.100)'; +}; + +/** + * 获取涨跌幅边框颜色 + * @param {number} change - 涨跌幅百分比 + * @returns {string} Chakra UI 颜色值 + */ +export const getChangeBorderColor = (change) => { + if (change === null || change === undefined || isNaN(change)) { + return 'gray.300'; + } + + const absChange = Math.abs(change); + + if (change > 0) { + if (absChange >= 9) return 'red.700'; + if (absChange >= 7) return 'red.600'; + if (absChange >= 5) return 'red.500'; + if (absChange >= 3) return 'red.400'; + return 'red.300'; + } else if (change < 0) { + if (absChange >= 9) return 'green.700'; + if (absChange >= 7) return 'green.600'; + if (absChange >= 5) return 'green.500'; + if (absChange >= 3) return 'green.400'; + return 'green.300'; + } + + return 'gray.300'; +}; diff --git a/src/views/Community/components/DynamicNewsDetail/CompactStockItem.js b/src/views/Community/components/DynamicNewsDetail/CompactStockItem.js index f3fd5e90..2eb1ea34 100644 --- a/src/views/Community/components/DynamicNewsDetail/CompactStockItem.js +++ b/src/views/Community/components/DynamicNewsDetail/CompactStockItem.js @@ -8,6 +8,7 @@ import { Tooltip, useColorModeValue, } from '@chakra-ui/react'; +import { getChangeColor, getChangeBackgroundGradient, getChangeBorderColor } from '../../../../utils/colorUtils'; /** * 精简模式股票卡片组件 @@ -30,31 +31,6 @@ const CompactStockItem = ({ stock, quote = null }) => { return `${prefix}${parseFloat(value).toFixed(2)}%`; }; - // 获取涨跌幅颜色(涨红跌绿) - const getChangeColor = (value) => { - const num = parseFloat(value); - if (isNaN(num) || num === 0) return 'gray.500'; - return num > 0 ? 'red.500' : 'green.500'; - }; - - // 获取背景渐变色(涨红跌绿) - const getBackgroundGradient = (value) => { - const num = parseFloat(value); - if (isNaN(num) || num === 0) { - return 'linear(to-br, gray.50, gray.100)'; - } - return num > 0 - ? 'linear(to-br, red.50, red.100)' - : 'linear(to-br, green.50, green.100)'; - }; - - // 获取边框颜色 - const getBorderColor = (value) => { - const num = parseFloat(value); - if (isNaN(num) || num === 0) return 'gray.300'; - return num > 0 ? 'red.300' : 'green.300'; - }; - // 获取涨跌幅数据(优先使用 quote,fallback 到 stock) const change = quote?.change ?? stock.daily_change ?? null; @@ -68,9 +44,9 @@ const CompactStockItem = ({ stock, quote = null }) => { fontSize="xs" > { left: 0, right: 0, height: '4px', - bg: getBorderColor(change), + bg: getChangeBorderColor(change), }} _hover={{ boxShadow: '2xl', diff --git a/src/views/Community/components/DynamicNewsDetail/StockListItem.js b/src/views/Community/components/DynamicNewsDetail/StockListItem.js index 29f54a83..107594c9 100644 --- a/src/views/Community/components/DynamicNewsDetail/StockListItem.js +++ b/src/views/Community/components/DynamicNewsDetail/StockListItem.js @@ -18,6 +18,7 @@ import { StarIcon } from '@chakra-ui/icons'; import MiniTimelineChart from '../StockDetailPanel/components/MiniTimelineChart'; import MiniKLineChart from './MiniKLineChart'; import StockChartModal from '../../../../components/StockChart/StockChartModal'; +import { getChangeColor } from '../../../../utils/colorUtils'; /** * 股票卡片组件 @@ -66,12 +67,7 @@ const StockListItem = ({ return `${prefix}${parseFloat(value).toFixed(2)}%`; }; - // 获取涨跌幅颜色 - const getChangeColor = (value) => { - const num = parseFloat(value); - if (isNaN(num) || num === 0) return 'gray.500'; - return num > 0 ? 'red.500' : 'green.500'; - }; + // 使用工具函数获取涨跌幅颜色(已从 colorUtils 导入) // 获取涨跌幅数据(优先使用 quote,fallback 到 stock) const change = quote?.change ?? stock.daily_change ?? null; diff --git a/src/views/Community/components/EventCard/DynamicNewsEventCard.js b/src/views/Community/components/EventCard/DynamicNewsEventCard.js index 3153bdcc..71806135 100644 --- a/src/views/Community/components/EventCard/DynamicNewsEventCard.js +++ b/src/views/Community/components/EventCard/DynamicNewsEventCard.js @@ -13,9 +13,10 @@ import { } from '@chakra-ui/react'; import moment from 'moment'; import { getImportanceConfig } from '../../../../constants/importanceLevels'; +import { getChangeColor } from '../../../../utils/colorUtils'; // 导入子组件 -import ImportanceBadge from './ImportanceBadge'; +import ImportanceStamp from './ImportanceStamp'; import EventFollowButton from './EventFollowButton'; import StockChangeIndicators from '../../../../components/StockChangeIndicators'; @@ -153,43 +154,37 @@ const DynamicNewsEventCard = ({ }; /** - * 根据平均涨幅计算背景色(分级策略) + * 根据平均涨幅计算背景色(分级策略)- 使用毛玻璃效果 * @param {number} avgChange - 平均涨跌幅 * @returns {string} Chakra UI 颜色值 */ const getChangeBasedBgColor = (avgChange) => { - // 转换为数字类型(处理可能的字符串类型数据) const numChange = Number(avgChange); - // 🔍 调试日志:排查背景色计算问题 - console.log('📊 [背景色计算]', { - rawValue: avgChange, - numValue: numChange, - type: typeof avgChange, - isNull: avgChange == null, - isNaN: isNaN(numChange), - title: event.title.substring(0, 30) + '...' - }); - - // 如果没有涨跌幅数据或转换失败,使用默认的奇偶行背景 + // 如果没有涨跌幅数据,使用半透明背景 if (avgChange == null || isNaN(numChange)) { - return index % 2 === 0 ? cardBg : useColorModeValue('gray.50', 'gray.750'); + return useColorModeValue('rgba(255, 255, 255, 0.8)', 'rgba(26, 32, 44, 0.8)'); } - // 根据涨跌幅分级返回背景色 - if (numChange >= 5) { - return useColorModeValue('red.100', 'red.900'); - } else if (numChange >= 3) { - return useColorModeValue('red.100', 'red.800'); - } else if (numChange > 0) { - return useColorModeValue('red.50', 'red.700'); - } else if (numChange > -3) { - return useColorModeValue('green.50', 'green.700'); - } else if (numChange > -5) { - return useColorModeValue('green.100', 'green.800'); - } else { - return useColorModeValue('green.100', 'green.900'); + // 根据涨跌幅分级返回半透明背景色(毛玻璃效果) + const absChange = Math.abs(numChange); + if (numChange > 0) { + // 涨:红色系半透明 + if (absChange >= 9) return useColorModeValue('rgba(254, 202, 202, 0.9)', 'rgba(127, 29, 29, 0.9)'); + if (absChange >= 7) return useColorModeValue('rgba(254, 202, 202, 0.8)', 'rgba(153, 27, 27, 0.8)'); + if (absChange >= 5) return useColorModeValue('rgba(254, 226, 226, 0.8)', 'rgba(185, 28, 28, 0.8)'); + if (absChange >= 3) return useColorModeValue('rgba(254, 226, 226, 0.7)', 'rgba(220, 38, 38, 0.7)'); + return useColorModeValue('rgba(254, 242, 242, 0.7)', 'rgba(239, 68, 68, 0.7)'); + } else if (numChange < 0) { + // 跌:绿色系半透明 + if (absChange >= 9) return useColorModeValue('rgba(187, 247, 208, 0.9)', 'rgba(20, 83, 45, 0.9)'); + if (absChange >= 7) return useColorModeValue('rgba(187, 247, 208, 0.8)', 'rgba(22, 101, 52, 0.8)'); + if (absChange >= 5) return useColorModeValue('rgba(209, 250, 229, 0.8)', 'rgba(21, 128, 61, 0.8)'); + if (absChange >= 3) return useColorModeValue('rgba(209, 250, 229, 0.7)', 'rgba(22, 163, 74, 0.7)'); + return useColorModeValue('rgba(240, 253, 244, 0.7)', 'rgba(34, 197, 94, 0.7)'); } + + return useColorModeValue('rgba(255, 255, 255, 0.8)', 'rgba(26, 32, 44, 0.8)'); }; // 获取当前事件的交易时段、样式和文字标签 @@ -208,17 +203,18 @@ const DynamicNewsEventCard = ({ ? useColorModeValue('blue.50', 'blue.900') : getChangeBasedBgColor(event.related_avg_chg) } + backdropFilter="blur(10px)" // 毛玻璃效果 borderWidth={isSelected ? "2px" : "1px"} borderColor={isSelected ? useColorModeValue('blue.500', 'blue.400') : borderColor } - borderRadius="md" - boxShadow={isSelected ? "lg" : "sm"} - // overflow="hidden" + borderRadius="lg" + boxShadow={isSelected ? "xl" : "md"} + overflow="visible" _hover={{ - boxShadow: 'xl', - transform: 'translateY(-2px)', + boxShadow: '2xl', + transform: 'translateY(-4px)', borderColor: isSelected ? 'blue.600' : importance.color, }} transition="all 0.3s ease" @@ -226,8 +222,10 @@ const DynamicNewsEventCard = ({ onClick={() => onEventClick?.(event)} > - {/* 左上角:重要性标签 */} - + {/* 右上角:重要性印章 */} + + + {/* 时间标签 - 在卡片上方,宽度自适应,左对齐 */} { + const config = getImportanceConfig(importance); + + // 印章颜色 + const stampColor = useColorModeValue(config.badgeBg, config.badgeBg); + + return ( + + {/* 外层圆形边框(双圈) */} + + {/* 内层圆形边框 */} + + + {/* 印章文字 */} + + {config.stampText} + + + + ); +}; + +export default ImportanceStamp;