heropanel修改
This commit is contained in:
@@ -61,89 +61,175 @@ const getChgColor = (val) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 胜率仪表盘组件
|
||||
* 获取胜率颜色(>50%红色,<50%绿色)
|
||||
*/
|
||||
const WinRateGauge = ({ rate }) => {
|
||||
const getRateColor = (rate) => {
|
||||
if (rate >= 50) return '#FF4D4F'; // 红色(上涨多)
|
||||
return '#52C41A'; // 绿色(下跌多)
|
||||
};
|
||||
|
||||
/**
|
||||
* 半圆仪表盘组件 - 参考用户提供的设计
|
||||
* 使用渐变色:左侧绿色 -> 中间黄色 -> 右侧红色
|
||||
*/
|
||||
const SemiCircleGauge = ({ rate, label, size = 'normal' }) => {
|
||||
const validRate = Math.min(100, Math.max(0, rate || 0));
|
||||
const angle = (validRate / 100) * 180;
|
||||
// 角度:0% = -90deg (左), 50% = 0deg (上), 100% = 90deg (右)
|
||||
const needleAngle = (validRate / 100) * 180 - 90;
|
||||
|
||||
const getGaugeColor = (r) => {
|
||||
if (r >= 70) return '#52C41A';
|
||||
if (r >= 50) return '#FFD700';
|
||||
return '#FF4D4F';
|
||||
};
|
||||
|
||||
const gaugeColor = getGaugeColor(validRate);
|
||||
const gaugeColor = getRateColor(validRate);
|
||||
const isSmall = size === 'small';
|
||||
const gaugeWidth = isSmall ? 80 : 100;
|
||||
const gaugeHeight = gaugeWidth / 2;
|
||||
const needleLength = isSmall ? 30 : 38;
|
||||
|
||||
return (
|
||||
<Box position="relative" w="100%" h="80px" overflow="hidden">
|
||||
<Box position="relative" w={`${gaugeWidth}px`} h={`${gaugeHeight + 20}px`}>
|
||||
{/* 半圆背景(渐变色弧线) */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="0"
|
||||
left="50%"
|
||||
transform="translateX(-50%)"
|
||||
w="120px"
|
||||
h="60px"
|
||||
borderTopLeftRadius="60px"
|
||||
borderTopRightRadius="60px"
|
||||
bg="rgba(255,255,255,0.05)"
|
||||
border="3px solid rgba(255,255,255,0.1)"
|
||||
borderBottom="none"
|
||||
bottom="16px"
|
||||
left="0"
|
||||
w={`${gaugeWidth}px`}
|
||||
h={`${gaugeHeight}px`}
|
||||
borderTopLeftRadius={`${gaugeHeight}px`}
|
||||
borderTopRightRadius={`${gaugeHeight}px`}
|
||||
bg="transparent"
|
||||
overflow="hidden"
|
||||
>
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="0"
|
||||
left="0"
|
||||
w="100%"
|
||||
h="100%"
|
||||
bg={`conic-gradient(from 180deg, ${gaugeColor} 0deg, ${gaugeColor} ${angle}deg, transparent ${angle}deg)`}
|
||||
opacity="0.3"
|
||||
style={{ transformOrigin: 'center bottom' }}
|
||||
/>
|
||||
</Box>
|
||||
_before={{
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
bottom: '0',
|
||||
borderTopLeftRadius: `${gaugeHeight}px`,
|
||||
borderTopRightRadius: `${gaugeHeight}px`,
|
||||
border: `${isSmall ? '6px' : '8px'} solid transparent`,
|
||||
borderBottom: 'none',
|
||||
background: 'linear-gradient(90deg, #52C41A 0%, #FADB14 50%, #FF4D4F 100%) border-box',
|
||||
mask: 'linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)',
|
||||
maskComposite: 'exclude',
|
||||
WebkitMaskComposite: 'destination-out',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 内部遮罩(让弧线更细) */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="3px"
|
||||
bottom="16px"
|
||||
left={`${isSmall ? 10 : 12}px`}
|
||||
w={`${gaugeWidth - (isSmall ? 20 : 24)}px`}
|
||||
h={`${gaugeHeight - (isSmall ? 10 : 12)}px`}
|
||||
borderTopLeftRadius={`${gaugeHeight - (isSmall ? 10 : 12)}px`}
|
||||
borderTopRightRadius={`${gaugeHeight - (isSmall ? 10 : 12)}px`}
|
||||
bg="rgba(17, 24, 39, 0.95)"
|
||||
/>
|
||||
|
||||
{/* 指针 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="16px"
|
||||
left="50%"
|
||||
w="2px"
|
||||
h="42px"
|
||||
h={`${needleLength}px`}
|
||||
bg={gaugeColor}
|
||||
borderRadius="full"
|
||||
transformOrigin="bottom center"
|
||||
transform={`translateX(-50%) rotate(${angle - 90}deg)`}
|
||||
boxShadow={`0 0 10px ${gaugeColor}`}
|
||||
transform={`translateX(-50%) rotate(${needleAngle}deg)`}
|
||||
boxShadow={`0 0 6px ${gaugeColor}`}
|
||||
transition="transform 0.5s ease-out"
|
||||
/>
|
||||
|
||||
{/* 指针中心点 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="0"
|
||||
bottom="13px"
|
||||
left="50%"
|
||||
transform="translateX(-50%)"
|
||||
w="10px"
|
||||
h="10px"
|
||||
w="6px"
|
||||
h="6px"
|
||||
bg={gaugeColor}
|
||||
borderRadius="full"
|
||||
boxShadow={`0 0 8px ${gaugeColor}`}
|
||||
boxShadow={`0 0 4px ${gaugeColor}`}
|
||||
/>
|
||||
|
||||
{/* 刻度标记 */}
|
||||
<Text
|
||||
position="absolute"
|
||||
bottom="16px"
|
||||
left="0"
|
||||
fontSize="2xs"
|
||||
color="gray.600"
|
||||
transform="translateX(2px)"
|
||||
>
|
||||
0
|
||||
</Text>
|
||||
<Text
|
||||
position="absolute"
|
||||
bottom="16px"
|
||||
right="0"
|
||||
fontSize="2xs"
|
||||
color="gray.600"
|
||||
transform="translateX(-2px)"
|
||||
>
|
||||
100
|
||||
</Text>
|
||||
|
||||
{/* 数值和标签 */}
|
||||
<VStack
|
||||
position="absolute"
|
||||
bottom="12px"
|
||||
bottom="0"
|
||||
left="50%"
|
||||
transform="translateX(-50%)"
|
||||
spacing={0}
|
||||
>
|
||||
<Text fontSize="xl" fontWeight="bold" color={gaugeColor} lineHeight="1">
|
||||
{validRate.toFixed(0)}%
|
||||
<Text fontSize={isSmall ? 'sm' : 'md'} fontWeight="bold" color={gaugeColor} lineHeight="1">
|
||||
{validRate.toFixed(1)}%
|
||||
</Text>
|
||||
<Text fontSize="2xs" color="gray.500">胜率</Text>
|
||||
<Text fontSize="2xs" color="gray.500">{label}</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
<Text position="absolute" bottom="0" left="12px" fontSize="2xs" color="gray.600">0</Text>
|
||||
<Text position="absolute" bottom="0" right="12px" fontSize="2xs" color="gray.600">100</Text>
|
||||
/**
|
||||
* 胜率对比组件 - 双仪表盘
|
||||
*/
|
||||
const WinRateGauge = ({ eventRate, marketRate, marketStats }) => {
|
||||
const eventRateVal = eventRate || 0;
|
||||
const marketRateVal = marketRate || 0;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/* 双仪表盘对比 */}
|
||||
<HStack spacing={2} justify="center" mb={1}>
|
||||
<VStack spacing={0}>
|
||||
<SemiCircleGauge rate={eventRateVal} label="事件胜率" size="small" />
|
||||
</VStack>
|
||||
<Box w="1px" h="50px" bg="rgba(255,215,0,0.2)" />
|
||||
<VStack spacing={0}>
|
||||
<SemiCircleGauge rate={marketRateVal} label="大盘上涨率" size="small" />
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
{/* 市场统计 */}
|
||||
{marketStats && (
|
||||
<HStack justify="center" spacing={3} mt={1}>
|
||||
<HStack spacing={1}>
|
||||
<Box w="6px" h="6px" borderRadius="full" bg="#FF4D4F" />
|
||||
<Text fontSize="2xs" color="#FF4D4F">{marketStats.risingCount}涨</Text>
|
||||
</HStack>
|
||||
<HStack spacing={1}>
|
||||
<Box w="6px" h="6px" borderRadius="full" bg="gray.400" />
|
||||
<Text fontSize="2xs" color="gray.400">{marketStats.flatCount}平</Text>
|
||||
</HStack>
|
||||
<HStack spacing={1}>
|
||||
<Box w="6px" h="6px" borderRadius="full" bg="#52C41A" />
|
||||
<Text fontSize="2xs" color="#52C41A">{marketStats.fallingCount}跌</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -350,7 +436,7 @@ const EventDailyStats = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const { summary, topPerformers = [], topStocks = [] } = stats;
|
||||
const { summary, marketStats, topPerformers = [], topStocks = [] } = stats;
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -423,8 +509,12 @@ const EventDailyStats = () => {
|
||||
|
||||
{/* 内容区域 - 使用 flex: 1 填充剩余空间 */}
|
||||
<VStack spacing={2} align="stretch" flex="1">
|
||||
{/* 胜率仪表盘 */}
|
||||
<WinRateGauge rate={summary?.positiveRate || 0} />
|
||||
{/* 胜率对比仪表盘 */}
|
||||
<WinRateGauge
|
||||
eventRate={summary?.positiveRate || 0}
|
||||
marketRate={marketStats?.risingRate || 0}
|
||||
marketStats={marketStats}
|
||||
/>
|
||||
|
||||
{/* 核心指标 - 2x2 网格 */}
|
||||
<Box display="grid" gridTemplateColumns="repeat(2, 1fr)" gap={2}>
|
||||
|
||||
Reference in New Issue
Block a user