fix: 恢复原有涨跌幅样式,将周涨幅改为超预期得分

- 恢复HorizontalDynamicNewsEventCard使用StockChangeIndicators组件
- 修改StockChangeIndicators:周涨幅→超预期得分,平均涨幅→平均超额,最大涨幅→最大超额
- 超预期得分显示为分数形式(如60分),根据分数显示不同颜色
This commit is contained in:
2025-12-03 08:38:17 +08:00
parent 1f592b6775
commit f7f9774caa
2 changed files with 91 additions and 13 deletions

View File

@@ -9,15 +9,15 @@ import { getChangeColor } from '../utils/colorUtils';
/**
* 股票涨跌幅指标组件3分天下布局
* @param {Object} props
* @param {number} props.avgChange - 平均涨
* @param {number} props.maxChange - 最大涨
* @param {number} props.weekChange - 周涨跌幅
* @param {number} props.avgChange - 平均超额涨幅
* @param {number} props.maxChange - 最大超额涨幅
* @param {number} props.expectationScore - 超预期得分0-100
* @param {'default'|'comfortable'|'large'} props.size - 尺寸模式default=紧凑comfortable=舒适事件列表large=大卡片(详情面板)
*/
const StockChangeIndicators = ({
avgChange,
maxChange,
weekChange,
expectationScore,
size = 'default',
}) => {
const isLarge = size === 'large';
@@ -146,16 +146,92 @@ const StockChangeIndicators = ({
);
};
// 渲染超预期得分指标(特殊样式,分数而非百分比)
const renderScoreIndicator = (label, score) => {
if (score == null) return null;
const labelColor = useColorModeValue('gray.600', 'gray.400');
// 根据分数确定颜色:>=60红色>=40橙色>=20蓝色其他灰色
const getScoreColor = (s) => {
if (s >= 60) return useColorModeValue('red.600', 'red.400');
if (s >= 40) return useColorModeValue('orange.600', 'orange.400');
if (s >= 20) return useColorModeValue('blue.600', 'blue.400');
return useColorModeValue('gray.600', 'gray.400');
};
const getScoreBgColor = (s) => {
if (s >= 60) return useColorModeValue('red.50', 'red.900');
if (s >= 40) return useColorModeValue('orange.50', 'orange.900');
if (s >= 20) return useColorModeValue('blue.50', 'blue.900');
return useColorModeValue('gray.50', 'gray.800');
};
const getScoreBorderColor = (s) => {
if (s >= 60) return useColorModeValue('red.200', 'red.700');
if (s >= 40) return useColorModeValue('orange.200', 'orange.700');
if (s >= 20) return useColorModeValue('blue.200', 'blue.700');
return useColorModeValue('gray.200', 'gray.700');
};
const scoreColor = getScoreColor(score);
const bgColor = getScoreBgColor(score);
const borderColor = getScoreBorderColor(score);
return (
<Box
bg={bgColor}
borderWidth={isLarge ? "2px" : "1px"}
borderColor={borderColor}
borderRadius="md"
px={isLarge ? 4 : (isDefault ? 1.5 : (isComfortable ? 3 : 2))}
py={isLarge ? 3 : (isDefault ? 1.5 : (isComfortable ? 2 : 1))}
display="flex"
flexDirection={(isLarge || isDefault) ? "column" : "row"}
alignItems={(isLarge || isDefault) ? "flex-start" : "center"}
gap={(isLarge || isDefault) ? (isLarge ? 2 : 1) : 1}
maxW={isLarge ? "200px" : "none"}
flex="0 1 auto"
minW="0"
>
{/* Large 和 Default 模式:标签单独一行 */}
{(isLarge || isDefault) && (
<Text fontSize={isLarge ? "sm" : "xs"} color={labelColor} fontWeight="medium">
{label}
</Text>
)}
{/* 数值 */}
<Text
fontSize={isLarge ? "2xl" : (isDefault ? "md" : "lg")}
fontWeight="bold"
color={scoreColor}
lineHeight="1.2"
whiteSpace="nowrap"
>
{/* Comfortable 模式:标签和数字在同一行 */}
{!isLarge && !isDefault && (
<Text as="span" color={labelColor} fontWeight="medium" fontSize="sm">
{label}
</Text>
)}
{Math.round(score)}
<Text as="span" fontWeight="medium" fontSize="sm"></Text>
</Text>
</Box>
);
};
// 如果没有任何数据,不渲染
if (avgChange == null && maxChange == null && weekChange == null) {
if (avgChange == null && maxChange == null && expectationScore == null) {
return null;
}
return (
<Flex width="100%" justify="flex-start" align="center" gap={isLarge ? 4 : (isDefault ? 2 : 1)}>
{renderIndicator('平均涨幅', avgChange)}
{renderIndicator('最大涨幅', maxChange)}
{renderIndicator('周涨幅', weekChange)}
{renderIndicator('最大超额', maxChange)}
{renderIndicator('平均超额', avgChange)}
{renderScoreIndicator('超预期', expectationScore)}
</Flex>
);
};

View File

@@ -22,7 +22,7 @@ import dayjs from 'dayjs';
import ImportanceStamp from './ImportanceStamp';
import EventTimeline from './EventTimeline';
import EventFollowButton from './EventFollowButton';
import EventPriceDisplay from './EventPriceDisplay';
import StockChangeIndicators from '../../../../components/StockChangeIndicators';
import KeywordsCarousel from './KeywordsCarousel';
/**
@@ -38,6 +38,7 @@ import KeywordsCarousel from './KeywordsCarousel';
* @param {Function} props.onToggleFollow - 切换关注事件
* @param {Object} props.timelineStyle - 时间轴样式配置
* @param {string} props.borderColor - 边框颜色
* @param {string} props.indicatorSize - 涨幅指标尺寸 ('default' | 'comfortable' | 'large')
* @param {string} props.layout - 布局模式 ('vertical' | 'four-row'),影响时间轴竖线高度
*/
const HorizontalDynamicNewsEventCard = React.memo(({
@@ -51,6 +52,7 @@ const HorizontalDynamicNewsEventCard = React.memo(({
onToggleFollow,
timelineStyle,
borderColor: timelineBorderColor,
indicatorSize = 'comfortable',
layout = 'vertical',
}) => {
const importance = getImportanceConfig(event.importance);
@@ -243,12 +245,12 @@ const HorizontalDynamicNewsEventCard = React.memo(({
</Box>
</Tooltip>
{/* 第二行:最大超额 + 超预期得分 */}
<EventPriceDisplay
avgChange={event.related_avg_chg}
{/* 第二行:涨跌幅数据 */}
<StockChangeIndicators
maxChange={event.related_max_chg}
avgChange={event.related_avg_chg}
expectationScore={event.expectation_surprise_score}
compact={false}
size={indicatorSize}
/>
</VStack>
</CardBody>