事件中心UI优化
This commit is contained in:
@@ -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);
|
||||
};
|
||||
|
||||
// 根据涨跌幅获取背景色(永远比文字色浅)
|
||||
|
||||
@@ -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
98
src/utils/colorUtils.js
Normal 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';
|
||||
};
|
||||
@@ -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"
|
||||
>
|
||||
<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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
74
src/views/Community/components/EventCard/ImportanceStamp.js
Normal file
74
src/views/Community/components/EventCard/ImportanceStamp.js
Normal 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;
|
||||
Reference in New Issue
Block a user