feat: UI调整

This commit is contained in:
zdl
2025-11-05 23:54:43 +08:00
parent adf6fc7780
commit ad933e9fb2
2 changed files with 152 additions and 75 deletions

View File

@@ -90,13 +90,15 @@ const StockChangeIndicators = ({
borderWidth={isLarge ? "2px" : "1px"}
borderColor={borderColor}
borderRadius="md"
px={isLarge ? 4 : (isDefault ? 2 : (isComfortable ? 3 : 2))}
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" : (isDefault ? "120px" : "none")}
maxW={isLarge ? "200px" : "none"}
flex="0 1 auto"
minW="0"
>
{/* Large 和 Default 模式:标签单独一行 */}
{(isLarge || isDefault) && (
@@ -111,14 +113,14 @@ const StockChangeIndicators = ({
{value !== 0 && (
value > 0 ? (
<TriangleUpIcon
w={isLarge ? 4 : (isDefault ? 3 : 2)}
h={isLarge ? 4 : (isDefault ? 3 : 2)}
w={2}
h={2}
color={numberColor}
/>
) : (
<TriangleDownIcon
w={isLarge ? 4 : (isDefault ? 3 : 2)}
h={isLarge ? 4 : (isDefault ? 3 : 2)}
w={2}
h={2}
color={numberColor}
/>
)
@@ -131,6 +133,8 @@ const StockChangeIndicators = ({
color={numberColor}
lineHeight="1.2"
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
>
{/* Comfortable 模式:标签和数字在同一行 */}
{!isLarge && !isDefault && (
@@ -138,7 +142,8 @@ const StockChangeIndicators = ({
{label}
</Text>
)}
{sign}{numStr}%
{sign}{numStr}
<Text as="span" fontWeight="medium" fontSize="sm">%</Text>
</Text>
</Flex>
</Box>
@@ -151,7 +156,7 @@ const StockChangeIndicators = ({
}
return (
<Flex width="fit-content" justify="flex-start" align="center" gap={isLarge ? 4 : (isDefault ? 2 : 1)}>
<Flex width="100%" justify="flex-start" align="center" gap={isLarge ? 4 : (isDefault ? 2 : 1)}>
{renderIndicator('平均涨幅', avgChange)}
{renderIndicator('最大涨幅', maxChange)}
{renderIndicator('周涨幅', weekChange)}

View File

@@ -48,9 +48,9 @@ const DynamicNewsEventCard = ({
const importance = getImportanceConfig(event.importance);
/**
* 判断交易时段(盘前、盘中、盘后)
* 判断交易时段(盘前、盘中上午、午休、盘中下午、盘后)
* @param {string} timestamp - 事件时间戳
* @returns {'pre-market' | 'trading' | 'after-market'}
* @returns {'pre-market' | 'morning-trading' | 'lunch-break' | 'afternoon-trading' | 'after-market'}
*/
const getTradingPeriod = (timestamp) => {
const eventTime = moment(timestamp);
@@ -58,18 +58,30 @@ const DynamicNewsEventCard = ({
const minute = eventTime.minute();
const timeInMinutes = hour * 60 + minute;
// 盘中时间09:30-11:30, 13:00-15:00
const morningStart = 9 * 60 + 30; // 09:30 = 570分钟
const morningEnd = 11 * 60 + 30; // 11:30 = 690分钟
const afternoonStart = 13 * 60; // 13:00 = 780分钟
const afternoonEnd = 15 * 60; // 15:00 = 900分钟
// 时间常量
const morningStart = 9 * 60 + 30; // 09:30 = 570分钟
const morningEnd = 11 * 60 + 30; // 11:30 = 690分钟
const lunchEnd = 13 * 60; // 13:00 = 780分钟
const afternoonEnd = 15 * 60; // 15:00 = 900分钟
if ((timeInMinutes >= morningStart && timeInMinutes < morningEnd) ||
(timeInMinutes >= afternoonStart && timeInMinutes < afternoonEnd)) {
return 'trading';
} else if (timeInMinutes < morningStart) {
// 盘中上午09:30-11:30
if (timeInMinutes >= morningStart && timeInMinutes < morningEnd) {
return 'morning-trading';
}
// 午休11:30-13:00
else if (timeInMinutes >= morningEnd && timeInMinutes < lunchEnd) {
return 'lunch-break';
}
// 盘中下午13:00-15:00
else if (timeInMinutes >= lunchEnd && timeInMinutes < afternoonEnd) {
return 'afternoon-trading';
}
// 盘前00:00-09:30
else if (timeInMinutes < morningStart) {
return 'pre-market';
} else {
}
// 盘后15:00-23:59
else {
return 'after-market';
}
};
@@ -82,26 +94,34 @@ const DynamicNewsEventCard = ({
const getTimeLabelStyle = (period) => {
switch (period) {
case 'pre-market':
// 盘前:蓝色系
// 盘前:粉红色系(浅红)
return {
bg: useColorModeValue('blue.50', 'blue.900'),
borderColor: useColorModeValue('blue.400', 'blue.500'),
textColor: useColorModeValue('blue.600', 'blue.300'),
bg: useColorModeValue('pink.50', 'pink.900'),
borderColor: useColorModeValue('pink.300', 'pink.500'),
textColor: useColorModeValue('pink.600', 'pink.300'),
};
case 'trading':
// 盘中:绿色系(表示交易中)
case 'morning-trading':
case 'afternoon-trading':
// 盘中:红色系(强烈,表示交易活跃)
return {
bg: useColorModeValue('green.50', 'green.900'),
borderColor: useColorModeValue('green.400', 'green.500'),
textColor: useColorModeValue('green.600', 'green.300'),
bg: useColorModeValue('red.50', 'red.900'),
borderColor: useColorModeValue('red.400', 'red.500'),
textColor: useColorModeValue('red.700', 'red.300'),
};
case 'after-market':
// 盘后:灰色系(市场关闭
case 'lunch-break':
// 午休:灰色系(中性
return {
bg: useColorModeValue('gray.100', 'gray.800'),
borderColor: useColorModeValue('gray.400', 'gray.500'),
textColor: useColorModeValue('gray.600', 'gray.400'),
};
case 'after-market':
// 盘后:橙色系(暖色但区别于盘中红色)
return {
bg: useColorModeValue('orange.50', 'orange.900'),
borderColor: useColorModeValue('orange.400', 'orange.500'),
textColor: useColorModeValue('orange.600', 'orange.300'),
};
default:
return {
bg: useColorModeValue('gray.100', 'gray.800'),
@@ -111,61 +131,75 @@ const DynamicNewsEventCard = ({
}
};
/**
* 获取交易时段文字标签
* @param {string} period - 交易时段
* @returns {string} 时段文字标签
*/
const getPeriodLabel = (period) => {
switch (period) {
case 'pre-market':
return '盘前';
case 'morning-trading':
case 'afternoon-trading':
return '盘中';
case 'lunch-break':
return '午休';
case 'after-market':
return '盘后';
default:
return '';
}
};
/**
* 根据平均涨幅计算背景色(分级策略)
* @param {number} avgChange - 平均涨跌幅
* @returns {string} Chakra UI 颜色值
*/
const getChangeBasedBgColor = (avgChange) => {
// 如果没有涨跌幅数据,使用默认的奇偶行背景
if (avgChange == null) {
// 转换为数字类型(处理可能的字符串类型数据)
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');
}
// 根据涨跌幅分级返回背景色
if (avgChange >= 5) {
if (numChange >= 5) {
return useColorModeValue('red.100', 'red.900');
} else if (avgChange >= 3) {
return useColorModeValue('red.75', 'red.800');
} else if (avgChange > 0) {
} else if (numChange >= 3) {
return useColorModeValue('red.100', 'red.800');
} else if (numChange > 0) {
return useColorModeValue('red.50', 'red.700');
} else if (avgChange > -3) {
} else if (numChange > -3) {
return useColorModeValue('green.50', 'green.700');
} else if (avgChange > -5) {
return useColorModeValue('green.75', 'green.800');
} else if (numChange > -5) {
return useColorModeValue('green.100', 'green.800');
} else {
return useColorModeValue('green.100', 'green.900');
}
};
// 获取当前事件的交易时段样式
// 获取当前事件的交易时段样式和文字标签
const tradingPeriod = getTradingPeriod(event.created_at);
const timeLabelStyle = getTimeLabelStyle(tradingPeriod);
const periodLabel = getPeriodLabel(tradingPeriod);
return (
<VStack align="stretch" spacing={2} w="100%">
{/* 时间标签 - 在卡片上方,宽度自适应,左对齐 */}
<Box
bg={timeLabelStyle.bg}
borderWidth="2px"
borderColor={timeLabelStyle.borderColor}
borderRadius="md"
px={3}
py={1.5}
width="fit-content"
alignSelf="flex-start"
boxShadow="sm"
transition="all 0.3s ease"
>
<Text
fontSize="xs"
fontWeight="bold"
color={timeLabelStyle.textColor}
lineHeight="1.3"
>
{moment(event.created_at).format('YYYY-MM-DD HH:mm')}
</Text>
</Box>
<VStack align="stretch" spacing={2} w="100%" pt={3}>
{/* 事件卡片 */}
<Card
@@ -181,7 +215,7 @@ const DynamicNewsEventCard = ({
}
borderRadius="md"
boxShadow={isSelected ? "lg" : "sm"}
overflow="hidden"
// overflow="hidden"
_hover={{
boxShadow: 'xl',
transform: 'translateY(-2px)',
@@ -193,7 +227,41 @@ const DynamicNewsEventCard = ({
>
<CardBody p={3}>
{/* 左上角:重要性标签 */}
<ImportanceBadge importance={event.importance} />
<ImportanceBadge importance={event.importance} position={{ top:-1, left: 0}} />
{/* 时间标签 - 在卡片上方,宽度自适应,左对齐 */}
<Box
bg={timeLabelStyle.bg}
borderWidth="2px"
borderColor={timeLabelStyle.borderColor}
borderRadius="md"
px={0.5}
py={0.5}
width="fit-content"
alignSelf="flex-start"
boxShadow="sm"
transition="all 0.3s ease"
position="absolute"
top={-2}
left={8}
>
<Text
fontSize="xs"
fontWeight="bold"
color={timeLabelStyle.textColor}
lineHeight="1.3"
>
{moment(event.created_at).format('YYYY-MM-DD HH:mm')}
{periodLabel && (
<>
{' • '}
<Text as="span" fontWeight="extrabold">
{periodLabel}
</Text>
</>
)}
</Text>
</Box>
{/* 右上角:关注按钮 */}
<Box position="absolute" top={2} right={2} zIndex={1}>
@@ -207,7 +275,7 @@ const DynamicNewsEventCard = ({
</Box>
<VStack align="stretch" spacing={2}>
{/* 标题 - 最多两行hover 显示完整内容 */}
{/* 标题 - 固定两行高度,保持卡片高度一致 */}
<Tooltip
label={event.title}
placement="top"
@@ -223,26 +291,30 @@ const DynamicNewsEventCard = ({
onClick={(e) => onTitleClick?.(e, event)}
mt={1}
paddingRight="10px"
minHeight="2.8em"
display="flex"
alignItems="center"
>
<Text
fontSize="md"
fontWeight="semibold"
color={linkColor}
lineHeight="1.4"
noOfLines={3}
noOfLines={2}
_hover={{ textDecoration: 'underline' }}
>
{event.title}
</Text>
</Box>
</Tooltip>
{/* 第二行:涨跌幅数据 */}
<StockChangeIndicators
avgChange={event.related_avg_chg}
maxChange={event.related_max_chg}
weekChange={event.related_week_chg}
/>
<Box position='relative'>
{/* 第二行:涨跌幅数据 */}
<StockChangeIndicators
avgChange={event.related_avg_chg}
maxChange={event.related_max_chg}
weekChange={event.related_week_chg}
/>
</Box>
</VStack>
</CardBody>
</Card>