事件中心UI优化

This commit is contained in:
2025-11-07 10:56:08 +08:00
parent 2a122b0013
commit 3eff0554f9
7 changed files with 219 additions and 77 deletions

View File

@@ -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);
};
// 根据涨跌幅获取背景色(永远比文字色浅)

View File

@@ -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',

98
src/utils/colorUtils.js Normal file
View File

@@ -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';
};

View File

@@ -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';
};
// 获取涨跌幅数据(优先使用 quotefallback 到 stock
const change = quote?.change ?? stock.daily_change ?? null;
@@ -68,9 +44,9 @@ const CompactStockItem = ({ stock, quote = null }) => {
fontSize="xs"
>
<Box
bgGradient={getBackgroundGradient(change)}
bgGradient={getChangeBackgroundGradient(change)}
borderWidth="3px"
borderColor={getBorderColor(change)}
borderColor={getChangeBorderColor(change)}
borderRadius="2xl"
p={4}
onClick={handleViewDetail}
@@ -85,7 +61,7 @@ const CompactStockItem = ({ stock, quote = null }) => {
left: 0,
right: 0,
height: '4px',
bg: getBorderColor(change),
bg: getChangeBorderColor(change),
}}
_hover={{
boxShadow: '2xl',

View File

@@ -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 导入)
// 获取涨跌幅数据(优先使用 quotefallback 到 stock
const change = quote?.change ?? stock.daily_change ?? null;

View File

@@ -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)}
>
<CardBody p={3}>
{/* 上角:重要性标签 */}
<ImportanceBadge importance={event.importance} position={{ top:-1, left: 0}} />
{/* 上角:重要性印章 */}
<Box position="absolute" top={-8} right={8} zIndex={2}>
<ImportanceStamp importance={event.importance} />
</Box>
{/* 时间标签 - 在卡片上方,宽度自适应,左对齐 */}
<Box

View File

@@ -0,0 +1,74 @@
// src/views/Community/components/EventCard/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)
*/
const ImportanceStamp = ({ importance }) => {
const config = getImportanceConfig(importance);
// 印章颜色
const stampColor = useColorModeValue(config.badgeBg, config.badgeBg);
return (
<Box
position="relative"
display="inline-block"
>
{/* 外层圆形边框(双圈) */}
<Box
position="relative"
w="50px"
h="50px"
borderRadius="50%"
borderWidth="3px"
borderColor={stampColor}
display="flex"
alignItems="center"
justifyContent="center"
transform="rotate(-15deg)"
opacity={0.85}
boxShadow="0 2px 8px rgba(0,0,0,0.15)"
_hover={{
opacity: 1,
transform: "rotate(-15deg) scale(1.1)",
}}
transition="all 0.3s ease"
>
{/* 内层圆形边框 */}
<Box
position="absolute"
w="42px"
h="42px"
borderRadius="50%"
borderWidth="2px"
borderColor={stampColor}
/>
{/* 印章文字 */}
<Text
fontSize="xl"
fontWeight="black"
color={stampColor}
fontFamily="'STKaiti', 'KaiTi', 'SimKai', serif"
letterSpacing="2px"
textShadow="0 0 1px currentColor"
>
{config.stampText}
</Text>
</Box>
</Box>
);
};
export default ImportanceStamp;