feat: StockChangeIndicators 组件优化
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Flex, Box, Text, useColorModeValue } from '@chakra-ui/react';
|
import { Flex, Box, Text, useColorModeValue } from '@chakra-ui/react';
|
||||||
|
import { TriangleUpIcon, TriangleDownIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 股票涨跌幅指标组件(3分天下布局)
|
* 股票涨跌幅指标组件(3分天下布局)
|
||||||
@@ -10,13 +11,18 @@ import { Flex, Box, Text, useColorModeValue } from '@chakra-ui/react';
|
|||||||
* @param {number} props.avgChange - 平均涨跌幅
|
* @param {number} props.avgChange - 平均涨跌幅
|
||||||
* @param {number} props.maxChange - 最大涨跌幅
|
* @param {number} props.maxChange - 最大涨跌幅
|
||||||
* @param {number} props.weekChange - 周涨跌幅
|
* @param {number} props.weekChange - 周涨跌幅
|
||||||
|
* @param {'default'|'comfortable'|'large'} props.size - 尺寸模式:default=紧凑,comfortable=舒适(事件列表),large=大卡片(详情面板)
|
||||||
*/
|
*/
|
||||||
const StockChangeIndicators = ({
|
const StockChangeIndicators = ({
|
||||||
avgChange,
|
avgChange,
|
||||||
maxChange,
|
maxChange,
|
||||||
weekChange,
|
weekChange,
|
||||||
|
size = 'default',
|
||||||
}) => {
|
}) => {
|
||||||
// 根据涨跌幅获取数字颜色(多颜色梯度:5级分级)
|
const isLarge = size === 'large';
|
||||||
|
const isComfortable = size === 'comfortable';
|
||||||
|
|
||||||
|
// 根据涨跌幅获取数字颜色(统一颜色,不分级)
|
||||||
const getNumberColor = (value) => {
|
const getNumberColor = (value) => {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return useColorModeValue('gray.700', 'gray.400');
|
return useColorModeValue('gray.700', 'gray.400');
|
||||||
@@ -27,24 +33,8 @@ const StockChangeIndicators = ({
|
|||||||
return 'gray.700';
|
return 'gray.700';
|
||||||
}
|
}
|
||||||
|
|
||||||
const absValue = Math.abs(value);
|
// 统一颜色:上涨红色,下跌绿色
|
||||||
const isPositive = value > 0;
|
return value > 0 ? 'red.500' : 'green.500';
|
||||||
|
|
||||||
if (isPositive) {
|
|
||||||
// 上涨:红色系 → 橙色系
|
|
||||||
if (absValue >= 10) return 'red.900'; // 10%以上:最深红
|
|
||||||
if (absValue >= 5) return 'red.700'; // 5-10%:深红
|
|
||||||
if (absValue >= 3) return 'red.500'; // 3-5%:中红
|
|
||||||
if (absValue >= 1) return 'orange.600'; // 1-3%:橙色
|
|
||||||
return 'orange.400'; // 0-1%:浅橙
|
|
||||||
} else {
|
|
||||||
// 下跌:绿色系 → 青色系
|
|
||||||
if (absValue >= 10) return 'green.900'; // -10%以下:最深绿
|
|
||||||
if (absValue >= 5) return 'green.700'; // -10% ~ -5%:深绿
|
|
||||||
if (absValue >= 3) return 'green.500'; // -5% ~ -3%:中绿
|
|
||||||
if (absValue >= 1) return 'teal.600'; // -3% ~ -1%:青色
|
|
||||||
return 'teal.400'; // -1% ~ 0%:浅青
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 根据涨跌幅获取背景色(永远比文字色浅)
|
// 根据涨跌幅获取背景色(永远比文字色浅)
|
||||||
@@ -58,24 +48,10 @@ const StockChangeIndicators = ({
|
|||||||
return useColorModeValue('gray.50', 'gray.800');
|
return useColorModeValue('gray.50', 'gray.800');
|
||||||
}
|
}
|
||||||
|
|
||||||
const absValue = Math.abs(value);
|
// 统一背景色:上涨红色系,下跌绿色系
|
||||||
const isPositive = value > 0;
|
return value > 0
|
||||||
|
? useColorModeValue('red.50', 'red.900')
|
||||||
if (isPositive) {
|
: useColorModeValue('green.50', 'green.900');
|
||||||
// 上涨背景:红色系 → 橙色系(统一使用 50 最浅色)
|
|
||||||
if (absValue >= 10) return useColorModeValue('red.50', 'red.900');
|
|
||||||
if (absValue >= 5) return useColorModeValue('red.50', 'red.900');
|
|
||||||
if (absValue >= 3) return useColorModeValue('red.50', 'red.900');
|
|
||||||
if (absValue >= 1) return useColorModeValue('orange.50', 'orange.900');
|
|
||||||
return useColorModeValue('orange.50', 'orange.900');
|
|
||||||
} else {
|
|
||||||
// 下跌背景:绿色系 → 青色系(统一使用 50 最浅色)
|
|
||||||
if (absValue >= 10) return useColorModeValue('green.50', 'green.900');
|
|
||||||
if (absValue >= 5) return useColorModeValue('green.50', 'green.900');
|
|
||||||
if (absValue >= 3) return useColorModeValue('green.50', 'green.900');
|
|
||||||
if (absValue >= 1) return useColorModeValue('teal.50', 'teal.900');
|
|
||||||
return useColorModeValue('teal.50', 'teal.900');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 根据涨跌幅获取边框色(比背景深,比文字浅)
|
// 根据涨跌幅获取边框色(比背景深,比文字浅)
|
||||||
@@ -89,64 +65,74 @@ const StockChangeIndicators = ({
|
|||||||
return useColorModeValue('gray.200', 'gray.700');
|
return useColorModeValue('gray.200', 'gray.700');
|
||||||
}
|
}
|
||||||
|
|
||||||
const absValue = Math.abs(value);
|
// 统一边框色:上涨红色系,下跌绿色系
|
||||||
const isPositive = value > 0;
|
return value > 0
|
||||||
|
? useColorModeValue('red.200', 'red.700')
|
||||||
if (isPositive) {
|
: useColorModeValue('green.200', 'green.700');
|
||||||
// 上涨边框:红色系 → 橙色系(跟随文字深浅)
|
|
||||||
if (absValue >= 10) return useColorModeValue('red.200', 'red.800'); // 文字 red.900
|
|
||||||
if (absValue >= 5) return useColorModeValue('red.200', 'red.700'); // 文字 red.700
|
|
||||||
if (absValue >= 3) return useColorModeValue('red.100', 'red.600'); // 文字 red.500
|
|
||||||
if (absValue >= 1) return useColorModeValue('orange.200', 'orange.700'); // 文字 orange.600
|
|
||||||
return useColorModeValue('orange.100', 'orange.600'); // 文字 orange.400
|
|
||||||
} else {
|
|
||||||
// 下跌边框:绿色系 → 青色系(跟随文字深浅)
|
|
||||||
if (absValue >= 10) return useColorModeValue('green.200', 'green.800'); // 文字 green.900
|
|
||||||
if (absValue >= 5) return useColorModeValue('green.200', 'green.700'); // 文字 green.700
|
|
||||||
if (absValue >= 3) return useColorModeValue('green.100', 'green.600'); // 文字 green.500
|
|
||||||
if (absValue >= 1) return useColorModeValue('teal.200', 'teal.700'); // 文字 teal.600
|
|
||||||
return useColorModeValue('teal.100', 'teal.600'); // 文字 teal.400
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染单个指标
|
// 渲染单个指标
|
||||||
const renderIndicator = (label, value) => {
|
const renderIndicator = (label, value) => {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
|
|
||||||
const sign = value > 0 ? '+' : '';
|
const sign = value > 0 ? '+' : '-';
|
||||||
// 0值显示为 "0",其他值显示一位小数
|
// 0值显示为 "0",其他值显示两位小数
|
||||||
const numStr = value === 0 ? '0' : Math.abs(value).toFixed(1);
|
const numStr = value === 0 ? '0' : Math.abs(value).toFixed(2);
|
||||||
const numberColor = getNumberColor(value);
|
const numberColor = getNumberColor(value);
|
||||||
const bgColor = getBgColor(value);
|
const bgColor = getBgColor(value);
|
||||||
const borderColor = getBorderColor(value);
|
const borderColor = getBorderColor(value);
|
||||||
const labelColor = useColorModeValue('gray.700', 'gray.400');
|
const labelColor = useColorModeValue('gray.600', 'gray.400');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
bg={bgColor}
|
bg={bgColor}
|
||||||
borderWidth="2px"
|
borderWidth={isLarge ? "2px" : "1px"}
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
px={1.5}
|
px={isLarge ? 4 : (isComfortable ? 3 : 2)}
|
||||||
py={0.5}
|
py={isLarge ? 3 : (isComfortable ? 2 : 1)}
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
flexDirection={isLarge ? "column" : "row"}
|
||||||
justifyContent="center"
|
alignItems={isLarge ? "flex-start" : "center"}
|
||||||
|
gap={isLarge ? 2 : 1}
|
||||||
|
flex="1"
|
||||||
|
maxW={isLarge ? "200px" : "none"}
|
||||||
>
|
>
|
||||||
<Text fontSize="xs" lineHeight="1.2">
|
{/* Large 模式:标签单独一行 */}
|
||||||
<Text as="span" color={labelColor}>
|
{isLarge && (
|
||||||
|
<Text fontSize="sm" color={labelColor} fontWeight="medium">
|
||||||
|
{label.trim()}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 数值 + 图标 */}
|
||||||
|
<Flex align="center" gap={isLarge ? 2 : 1}>
|
||||||
|
{/* 三角形图标 */}
|
||||||
|
{value !== 0 && (
|
||||||
|
value > 0 ? (
|
||||||
|
<TriangleUpIcon w={isLarge ? 4 : 2} h={isLarge ? 4 : 2} color={numberColor} />
|
||||||
|
) : (
|
||||||
|
<TriangleDownIcon w={isLarge ? 4 : 2} h={isLarge ? 4 : 2} color={numberColor} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 数字 */}
|
||||||
|
<Text
|
||||||
|
fontSize={isLarge ? "2xl" : "xs"}
|
||||||
|
fontWeight="bold"
|
||||||
|
color={numberColor}
|
||||||
|
lineHeight="1.2"
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
>
|
||||||
|
{/* Default 模式:标签和数字在同一行 */}
|
||||||
|
{!isLarge && (
|
||||||
|
<Text as="span" color={labelColor} fontWeight="medium" fontSize="xs">
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<Text as="span" color={labelColor}>
|
)}
|
||||||
{sign}
|
{sign}{numStr}%
|
||||||
</Text>
|
|
||||||
<Text as="span" fontWeight="bold" color={numberColor} fontSize="sm">
|
|
||||||
{value < 0 ? '-' : ''}{numStr}
|
|
||||||
</Text>
|
|
||||||
<Text as="span" color={labelColor}>
|
|
||||||
%
|
|
||||||
</Text>
|
|
||||||
</Text>
|
</Text>
|
||||||
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -157,10 +143,10 @@ const StockChangeIndicators = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex width="100%" justify="space-between" align="center" gap={1}>
|
<Flex width="100%" justify="space-between" align="center" gap={isLarge ? 4 : 1}>
|
||||||
{renderIndicator('平均 ', avgChange)}
|
{renderIndicator('平均涨幅', avgChange)}
|
||||||
{renderIndicator('最大 ', maxChange)}
|
{renderIndicator('最大涨幅', maxChange)}
|
||||||
{renderIndicator('周涨 ', weekChange)}
|
{renderIndicator('周涨幅', weekChange)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user