feat: UI调整
This commit is contained in:
@@ -90,13 +90,15 @@ const StockChangeIndicators = ({
|
|||||||
borderWidth={isLarge ? "2px" : "1px"}
|
borderWidth={isLarge ? "2px" : "1px"}
|
||||||
borderColor={borderColor}
|
borderColor={borderColor}
|
||||||
borderRadius="md"
|
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))}
|
py={isLarge ? 3 : (isDefault ? 1.5 : (isComfortable ? 2 : 1))}
|
||||||
display="flex"
|
display="flex"
|
||||||
flexDirection={(isLarge || isDefault) ? "column" : "row"}
|
flexDirection={(isLarge || isDefault) ? "column" : "row"}
|
||||||
alignItems={(isLarge || isDefault) ? "flex-start" : "center"}
|
alignItems={(isLarge || isDefault) ? "flex-start" : "center"}
|
||||||
gap={(isLarge || isDefault) ? (isLarge ? 2 : 1) : 1}
|
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 模式:标签单独一行 */}
|
{/* Large 和 Default 模式:标签单独一行 */}
|
||||||
{(isLarge || isDefault) && (
|
{(isLarge || isDefault) && (
|
||||||
@@ -111,14 +113,14 @@ const StockChangeIndicators = ({
|
|||||||
{value !== 0 && (
|
{value !== 0 && (
|
||||||
value > 0 ? (
|
value > 0 ? (
|
||||||
<TriangleUpIcon
|
<TriangleUpIcon
|
||||||
w={isLarge ? 4 : (isDefault ? 3 : 2)}
|
w={2}
|
||||||
h={isLarge ? 4 : (isDefault ? 3 : 2)}
|
h={2}
|
||||||
color={numberColor}
|
color={numberColor}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TriangleDownIcon
|
<TriangleDownIcon
|
||||||
w={isLarge ? 4 : (isDefault ? 3 : 2)}
|
w={2}
|
||||||
h={isLarge ? 4 : (isDefault ? 3 : 2)}
|
h={2}
|
||||||
color={numberColor}
|
color={numberColor}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@@ -131,6 +133,8 @@ const StockChangeIndicators = ({
|
|||||||
color={numberColor}
|
color={numberColor}
|
||||||
lineHeight="1.2"
|
lineHeight="1.2"
|
||||||
whiteSpace="nowrap"
|
whiteSpace="nowrap"
|
||||||
|
overflow="hidden"
|
||||||
|
textOverflow="ellipsis"
|
||||||
>
|
>
|
||||||
{/* Comfortable 模式:标签和数字在同一行 */}
|
{/* Comfortable 模式:标签和数字在同一行 */}
|
||||||
{!isLarge && !isDefault && (
|
{!isLarge && !isDefault && (
|
||||||
@@ -138,7 +142,8 @@ const StockChangeIndicators = ({
|
|||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{sign}{numStr}%
|
{sign}{numStr}
|
||||||
|
<Text as="span" fontWeight="medium" fontSize="sm">%</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -151,7 +156,7 @@ const StockChangeIndicators = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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('平均涨幅', avgChange)}
|
||||||
{renderIndicator('最大涨幅', maxChange)}
|
{renderIndicator('最大涨幅', maxChange)}
|
||||||
{renderIndicator('周涨幅', weekChange)}
|
{renderIndicator('周涨幅', weekChange)}
|
||||||
|
|||||||
@@ -48,9 +48,9 @@ const DynamicNewsEventCard = ({
|
|||||||
const importance = getImportanceConfig(event.importance);
|
const importance = getImportanceConfig(event.importance);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断交易时段(盘前、盘中、盘后)
|
* 判断交易时段(盘前、盘中上午、午休、盘中下午、盘后)
|
||||||
* @param {string} timestamp - 事件时间戳
|
* @param {string} timestamp - 事件时间戳
|
||||||
* @returns {'pre-market' | 'trading' | 'after-market'}
|
* @returns {'pre-market' | 'morning-trading' | 'lunch-break' | 'afternoon-trading' | 'after-market'}
|
||||||
*/
|
*/
|
||||||
const getTradingPeriod = (timestamp) => {
|
const getTradingPeriod = (timestamp) => {
|
||||||
const eventTime = moment(timestamp);
|
const eventTime = moment(timestamp);
|
||||||
@@ -58,18 +58,30 @@ const DynamicNewsEventCard = ({
|
|||||||
const minute = eventTime.minute();
|
const minute = eventTime.minute();
|
||||||
const timeInMinutes = hour * 60 + minute;
|
const timeInMinutes = hour * 60 + minute;
|
||||||
|
|
||||||
// 盘中时间:09:30-11:30, 13:00-15:00
|
// 时间常量
|
||||||
const morningStart = 9 * 60 + 30; // 09:30 = 570分钟
|
const morningStart = 9 * 60 + 30; // 09:30 = 570分钟
|
||||||
const morningEnd = 11 * 60 + 30; // 11:30 = 690分钟
|
const morningEnd = 11 * 60 + 30; // 11:30 = 690分钟
|
||||||
const afternoonStart = 13 * 60; // 13:00 = 780分钟
|
const lunchEnd = 13 * 60; // 13:00 = 780分钟
|
||||||
const afternoonEnd = 15 * 60; // 15:00 = 900分钟
|
const afternoonEnd = 15 * 60; // 15:00 = 900分钟
|
||||||
|
|
||||||
if ((timeInMinutes >= morningStart && timeInMinutes < morningEnd) ||
|
// 盘中上午:09:30-11:30
|
||||||
(timeInMinutes >= afternoonStart && timeInMinutes < afternoonEnd)) {
|
if (timeInMinutes >= morningStart && timeInMinutes < morningEnd) {
|
||||||
return 'trading';
|
return 'morning-trading';
|
||||||
} else if (timeInMinutes < morningStart) {
|
}
|
||||||
|
// 午休: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';
|
return 'pre-market';
|
||||||
} else {
|
}
|
||||||
|
// 盘后:15:00-23:59
|
||||||
|
else {
|
||||||
return 'after-market';
|
return 'after-market';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -82,26 +94,34 @@ const DynamicNewsEventCard = ({
|
|||||||
const getTimeLabelStyle = (period) => {
|
const getTimeLabelStyle = (period) => {
|
||||||
switch (period) {
|
switch (period) {
|
||||||
case 'pre-market':
|
case 'pre-market':
|
||||||
// 盘前:蓝色系
|
// 盘前:粉红色系(浅红)
|
||||||
return {
|
return {
|
||||||
bg: useColorModeValue('blue.50', 'blue.900'),
|
bg: useColorModeValue('pink.50', 'pink.900'),
|
||||||
borderColor: useColorModeValue('blue.400', 'blue.500'),
|
borderColor: useColorModeValue('pink.300', 'pink.500'),
|
||||||
textColor: useColorModeValue('blue.600', 'blue.300'),
|
textColor: useColorModeValue('pink.600', 'pink.300'),
|
||||||
};
|
};
|
||||||
case 'trading':
|
case 'morning-trading':
|
||||||
// 盘中:绿色系(表示交易中)
|
case 'afternoon-trading':
|
||||||
|
// 盘中:红色系(强烈,表示交易活跃)
|
||||||
return {
|
return {
|
||||||
bg: useColorModeValue('green.50', 'green.900'),
|
bg: useColorModeValue('red.50', 'red.900'),
|
||||||
borderColor: useColorModeValue('green.400', 'green.500'),
|
borderColor: useColorModeValue('red.400', 'red.500'),
|
||||||
textColor: useColorModeValue('green.600', 'green.300'),
|
textColor: useColorModeValue('red.700', 'red.300'),
|
||||||
};
|
};
|
||||||
case 'after-market':
|
case 'lunch-break':
|
||||||
// 盘后:灰色系(市场关闭)
|
// 午休:灰色系(中性)
|
||||||
return {
|
return {
|
||||||
bg: useColorModeValue('gray.100', 'gray.800'),
|
bg: useColorModeValue('gray.100', 'gray.800'),
|
||||||
borderColor: useColorModeValue('gray.400', 'gray.500'),
|
borderColor: useColorModeValue('gray.400', 'gray.500'),
|
||||||
textColor: useColorModeValue('gray.600', 'gray.400'),
|
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:
|
default:
|
||||||
return {
|
return {
|
||||||
bg: useColorModeValue('gray.100', 'gray.800'),
|
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 - 平均涨跌幅
|
* @param {number} avgChange - 平均涨跌幅
|
||||||
* @returns {string} Chakra UI 颜色值
|
* @returns {string} Chakra UI 颜色值
|
||||||
*/
|
*/
|
||||||
const getChangeBasedBgColor = (avgChange) => {
|
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');
|
return index % 2 === 0 ? cardBg : useColorModeValue('gray.50', 'gray.750');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据涨跌幅分级返回背景色
|
// 根据涨跌幅分级返回背景色
|
||||||
if (avgChange >= 5) {
|
if (numChange >= 5) {
|
||||||
return useColorModeValue('red.100', 'red.900');
|
return useColorModeValue('red.100', 'red.900');
|
||||||
} else if (avgChange >= 3) {
|
} else if (numChange >= 3) {
|
||||||
return useColorModeValue('red.75', 'red.800');
|
return useColorModeValue('red.100', 'red.800');
|
||||||
} else if (avgChange > 0) {
|
} else if (numChange > 0) {
|
||||||
return useColorModeValue('red.50', 'red.700');
|
return useColorModeValue('red.50', 'red.700');
|
||||||
} else if (avgChange > -3) {
|
} else if (numChange > -3) {
|
||||||
return useColorModeValue('green.50', 'green.700');
|
return useColorModeValue('green.50', 'green.700');
|
||||||
} else if (avgChange > -5) {
|
} else if (numChange > -5) {
|
||||||
return useColorModeValue('green.75', 'green.800');
|
return useColorModeValue('green.100', 'green.800');
|
||||||
} else {
|
} else {
|
||||||
return useColorModeValue('green.100', 'green.900');
|
return useColorModeValue('green.100', 'green.900');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前事件的交易时段和样式
|
// 获取当前事件的交易时段、样式和文字标签
|
||||||
const tradingPeriod = getTradingPeriod(event.created_at);
|
const tradingPeriod = getTradingPeriod(event.created_at);
|
||||||
const timeLabelStyle = getTimeLabelStyle(tradingPeriod);
|
const timeLabelStyle = getTimeLabelStyle(tradingPeriod);
|
||||||
|
const periodLabel = getPeriodLabel(tradingPeriod);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack align="stretch" spacing={2} w="100%">
|
<VStack align="stretch" spacing={2} w="100%" pt={3}>
|
||||||
{/* 时间标签 - 在卡片上方,宽度自适应,左对齐 */}
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* 事件卡片 */}
|
{/* 事件卡片 */}
|
||||||
<Card
|
<Card
|
||||||
@@ -181,7 +215,7 @@ const DynamicNewsEventCard = ({
|
|||||||
}
|
}
|
||||||
borderRadius="md"
|
borderRadius="md"
|
||||||
boxShadow={isSelected ? "lg" : "sm"}
|
boxShadow={isSelected ? "lg" : "sm"}
|
||||||
overflow="hidden"
|
// overflow="hidden"
|
||||||
_hover={{
|
_hover={{
|
||||||
boxShadow: 'xl',
|
boxShadow: 'xl',
|
||||||
transform: 'translateY(-2px)',
|
transform: 'translateY(-2px)',
|
||||||
@@ -193,7 +227,41 @@ const DynamicNewsEventCard = ({
|
|||||||
>
|
>
|
||||||
<CardBody p={3}>
|
<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}>
|
<Box position="absolute" top={2} right={2} zIndex={1}>
|
||||||
@@ -207,7 +275,7 @@ const DynamicNewsEventCard = ({
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<VStack align="stretch" spacing={2}>
|
<VStack align="stretch" spacing={2}>
|
||||||
{/* 标题 - 最多两行,hover 显示完整内容 */}
|
{/* 标题 - 固定两行高度,保持卡片高度一致 */}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
label={event.title}
|
label={event.title}
|
||||||
placement="top"
|
placement="top"
|
||||||
@@ -223,26 +291,30 @@ const DynamicNewsEventCard = ({
|
|||||||
onClick={(e) => onTitleClick?.(e, event)}
|
onClick={(e) => onTitleClick?.(e, event)}
|
||||||
mt={1}
|
mt={1}
|
||||||
paddingRight="10px"
|
paddingRight="10px"
|
||||||
|
minHeight="2.8em"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
fontSize="md"
|
fontSize="md"
|
||||||
fontWeight="semibold"
|
fontWeight="semibold"
|
||||||
color={linkColor}
|
color={linkColor}
|
||||||
lineHeight="1.4"
|
lineHeight="1.4"
|
||||||
noOfLines={3}
|
noOfLines={2}
|
||||||
_hover={{ textDecoration: 'underline' }}
|
_hover={{ textDecoration: 'underline' }}
|
||||||
>
|
>
|
||||||
{event.title}
|
{event.title}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Box position='relative'>
|
||||||
{/* 第二行:涨跌幅数据 */}
|
{/* 第二行:涨跌幅数据 */}
|
||||||
<StockChangeIndicators
|
<StockChangeIndicators
|
||||||
avgChange={event.related_avg_chg}
|
avgChange={event.related_avg_chg}
|
||||||
maxChange={event.related_max_chg}
|
maxChange={event.related_max_chg}
|
||||||
weekChange={event.related_week_chg}
|
weekChange={event.related_week_chg}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
</VStack>
|
</VStack>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user