feat: UI调整
This commit is contained in:
@@ -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)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user