refactor(WatchSidebar): 优化三个子组件

- FollowingEventsPanel: 添加滚动区域容器,移除数量限制
- MyCommentsTab: 添加滚动区域,移除 maxDisplay 限制
- WatchlistPanel: 优化滚动区域样式和布局

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-23 10:55:21 +08:00
parent dcba97a121
commit 600d9cc846
3 changed files with 186 additions and 187 deletions

View File

@@ -112,61 +112,62 @@ const EventsTabContent = ({ events, onEventClick, onAddEvent }) => {
} }
return ( return (
<VStack spacing={1.5} align="stretch"> <Box
{events.slice(0, 6).map((event) => { maxH="280px"
const avgChg = event.related_avg_chg; overflowY="auto"
const isUp = avgChg > 0; css={{
const changeColor = isUp ? '#EF4444' : avgChg < 0 ? '#22C55E' : 'rgba(255, 255, 255, 0.6)'; '&::-webkit-scrollbar': { width: '0px' },
'&:hover::-webkit-scrollbar': { width: '4px' },
'&::-webkit-scrollbar-track': { background: 'transparent' },
'&::-webkit-scrollbar-thumb': {
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: '2px',
},
}}
>
<VStack spacing={1.5} align="stretch">
{events.map((event) => {
const avgChg = event.related_avg_chg;
const isUp = avgChg > 0;
const changeColor = isUp ? '#EF4444' : avgChg < 0 ? '#22C55E' : 'rgba(255, 255, 255, 0.6)';
return ( return (
<Box <Box
key={event.id} key={event.id}
py={2} py={2}
px={2} px={2}
cursor="pointer" cursor="pointer"
borderRadius="md" borderRadius="md"
bg="rgba(37, 37, 64, 0.3)" bg="rgba(37, 37, 64, 0.3)"
_hover={{ bg: 'rgba(37, 37, 64, 0.6)' }} _hover={{ bg: 'rgba(37, 37, 64, 0.6)' }}
onClick={() => onEventClick?.(event)} onClick={() => onEventClick?.(event)}
>
<Text
fontSize="xs"
fontWeight="medium"
color="rgba(255, 255, 255, 0.9)"
noOfLines={2}
mb={1}
lineHeight="1.4"
> >
{event.title} <Text
</Text> fontSize="xs"
<HStack justify="space-between" fontSize="10px"> fontWeight="medium"
<HStack spacing={1} color="rgba(255, 255, 255, 0.4)"> color="rgba(255, 255, 255, 0.9)"
<Icon as={Users} boxSize={2.5} /> noOfLines={2}
<Text>{event.follower_count || 0}</Text> mb={1}
lineHeight="1.4"
>
{event.title}
</Text>
<HStack justify="space-between" fontSize="10px">
<HStack spacing={1} color="rgba(255, 255, 255, 0.4)">
<Icon as={Users} boxSize={2.5} />
<Text>{event.follower_count || 0}</Text>
</HStack>
{avgChg !== undefined && avgChg !== null && (
<Text color={changeColor} fontWeight="medium">
{isUp ? '+' : ''}{Number(avgChg).toFixed(2)}%
</Text>
)}
</HStack> </HStack>
{avgChg !== undefined && avgChg !== null && ( </Box>
<Text color={changeColor} fontWeight="medium"> );
{isUp ? '+' : ''}{Number(avgChg).toFixed(2)}% })}
</Text> </VStack>
)} </Box>
</HStack>
</Box>
);
})}
{events.length > 6 && (
<Text
fontSize="xs"
color="rgba(212, 175, 55, 0.7)"
textAlign="center"
cursor="pointer"
py={1}
_hover={{ color: 'rgba(212, 175, 55, 0.9)' }}
onClick={onAddEvent}
>
查看全部 ({events.length})
</Text>
)}
</VStack>
); );
}; };

View File

@@ -33,10 +33,7 @@ const truncateText = (text, maxLength = 50) => {
const MyCommentsTab = ({ const MyCommentsTab = ({
comments = [], comments = [],
onCommentClick, onCommentClick,
maxDisplay = 5,
}) => { }) => {
const displayComments = comments.slice(0, maxDisplay);
if (comments.length === 0) { if (comments.length === 0) {
return ( return (
<Box py={4} textAlign="center"> <Box py={4} textAlign="center">
@@ -49,72 +46,72 @@ const MyCommentsTab = ({
} }
return ( return (
<VStack spacing={1.5} align="stretch"> <Box
{displayComments.map((comment) => ( maxH="280px"
<Box overflowY="auto"
key={comment.id} css={{
py={2} '&::-webkit-scrollbar': { width: '0px' },
px={2} '&:hover::-webkit-scrollbar': { width: '4px' },
cursor="pointer" '&::-webkit-scrollbar-track': { background: 'transparent' },
borderRadius="md" '&::-webkit-scrollbar-thumb': {
bg="rgba(37, 37, 64, 0.3)" background: 'rgba(255, 255, 255, 0.2)',
_hover={{ bg: 'rgba(37, 37, 64, 0.6)' }} borderRadius: '2px',
onClick={() => onCommentClick?.(comment)} },
> }}
{/* 评论内容 */} >
<Text <VStack spacing={1.5} align="stretch">
fontSize="xs" {comments.map((comment) => (
color="rgba(255, 255, 255, 0.85)" <Box
noOfLines={2} key={comment.id}
mb={1} py={2}
lineHeight="1.4" px={2}
cursor="pointer"
borderRadius="md"
bg="rgba(37, 37, 64, 0.3)"
_hover={{ bg: 'rgba(37, 37, 64, 0.6)' }}
onClick={() => onCommentClick?.(comment)}
> >
{truncateText(comment.content, 60)} {/* 评论内容 */}
</Text>
{/* 关联事件 */}
{comment.event_title && (
<Text <Text
fontSize="10px" fontSize="xs"
color="rgba(212, 175, 55, 0.7)" color="rgba(255, 255, 255, 0.85)"
noOfLines={1} noOfLines={2}
mb={1} mb={1}
lineHeight="1.4"
> >
📌 {truncateText(comment.event_title, 30)} {truncateText(comment.content, 60)}
</Text> </Text>
)}
{/* 底部信息:点赞、回复、时间 */} {/* 关联事件 */}
<HStack justify="space-between" fontSize="10px" color="rgba(255, 255, 255, 0.4)"> {comment.event_title && (
<HStack spacing={2}> <Text
<HStack spacing={0.5}> fontSize="10px"
<Icon as={ThumbsUp} boxSize={2.5} /> color="rgba(212, 175, 55, 0.7)"
<Text>{comment.like_count || 0}</Text> noOfLines={1}
</HStack> mb={1}
<HStack spacing={0.5}> >
<Icon as={MessageCircle} boxSize={2.5} /> 📌 {truncateText(comment.event_title, 30)}
<Text>{comment.reply_count || 0}</Text> </Text>
)}
{/* 底部信息:点赞、回复、时间 */}
<HStack justify="space-between" fontSize="10px" color="rgba(255, 255, 255, 0.4)">
<HStack spacing={2}>
<HStack spacing={0.5}>
<Icon as={ThumbsUp} boxSize={2.5} />
<Text>{comment.like_count || 0}</Text>
</HStack>
<HStack spacing={0.5}>
<Icon as={MessageCircle} boxSize={2.5} />
<Text>{comment.reply_count || 0}</Text>
</HStack>
</HStack> </HStack>
<Text>{formatRelativeTime(comment.created_at)}</Text>
</HStack> </HStack>
<Text>{formatRelativeTime(comment.created_at)}</Text> </Box>
</HStack> ))}
</Box> </VStack>
))} </Box>
{/* 查看更多 */}
{comments.length > maxDisplay && (
<Text
fontSize="xs"
color="rgba(212, 175, 55, 0.7)"
textAlign="center"
cursor="pointer"
py={1}
_hover={{ color: 'rgba(212, 175, 55, 0.9)' }}
>
查看全部 ({comments.length})
</Text>
)}
</VStack>
); );
}; };

View File

@@ -32,81 +32,82 @@ const WatchlistPanel = ({
/> />
</HStack> </HStack>
{/* 股票列表 */} {/* 股票列表 - 固定高度可滚动 */}
<VStack spacing={1} align="stretch"> {watchlist.length === 0 ? (
{watchlist.length === 0 ? ( <Box
<Box py={4}
py={4} textAlign="center"
textAlign="center" cursor="pointer"
cursor="pointer" onClick={onAddStock}
onClick={onAddStock} _hover={{ bg: 'rgba(255, 255, 255, 0.05)' }}
_hover={{ bg: 'rgba(255, 255, 255, 0.05)' }} borderRadius="md"
borderRadius="md" >
> <Icon as={BarChart2} boxSize={6} color="rgba(255, 255, 255, 0.2)" mb={1} />
<Icon as={BarChart2} boxSize={6} color="rgba(255, 255, 255, 0.2)" mb={1} /> <Text fontSize="xs" color="rgba(255, 255, 255, 0.4)">
<Text fontSize="xs" color="rgba(255, 255, 255, 0.4)"> 添加自选股
添加自选股
</Text>
</Box>
) : (
watchlist.slice(0, 8).map((stock) => {
const quote = realtimeQuotes[stock.stock_code];
const changePercent = quote?.change_percent ?? stock.change_percent;
const isUp = changePercent > 0;
const changeColor = isUp ? '#EF4444' : changePercent < 0 ? '#22C55E' : 'rgba(255, 255, 255, 0.6)';
return (
<HStack
key={stock.stock_code}
py={1.5}
px={2}
justify="space-between"
cursor="pointer"
borderRadius="md"
_hover={{ bg: 'rgba(255, 255, 255, 0.05)' }}
onClick={() => onStockClick?.(stock)}
>
<VStack align="start" spacing={0} flex={1} minW={0}>
<Text
fontSize="xs"
fontWeight="medium"
color="rgba(255, 255, 255, 0.9)"
noOfLines={1}
>
{stock.stock_name || stock.stock_code}
</Text>
<Text fontSize="10px" color="rgba(255, 255, 255, 0.4)">
{stock.stock_code}
</Text>
</VStack>
<VStack align="end" spacing={0}>
<Text fontSize="xs" fontWeight="bold" color={changeColor}>
{quote?.current_price?.toFixed(2) || stock.current_price || '--'}
</Text>
<Text fontSize="10px" color={changeColor}>
{changePercent !== undefined && changePercent !== null
? `${isUp ? '+' : ''}${Number(changePercent).toFixed(2)}%`
: '--'}
</Text>
</VStack>
</HStack>
);
})
)}
{watchlist.length > 8 && (
<Text
fontSize="xs"
color="rgba(212, 175, 55, 0.7)"
textAlign="center"
cursor="pointer"
py={1}
_hover={{ color: 'rgba(212, 175, 55, 0.9)' }}
onClick={onAddStock}
>
查看全部 ({watchlist.length})
</Text> </Text>
)} </Box>
</VStack> ) : (
<Box
maxH="240px"
overflowY="auto"
css={{
'&::-webkit-scrollbar': { width: '0px' },
'&:hover::-webkit-scrollbar': { width: '4px' },
'&::-webkit-scrollbar-track': { background: 'transparent' },
'&::-webkit-scrollbar-thumb': {
background: 'rgba(255, 255, 255, 0.2)',
borderRadius: '2px',
},
}}
>
<VStack spacing={1} align="stretch">
{watchlist.map((stock) => {
const quote = realtimeQuotes[stock.stock_code];
const changePercent = quote?.change_percent ?? stock.change_percent;
const isUp = changePercent > 0;
const changeColor = isUp ? '#EF4444' : changePercent < 0 ? '#22C55E' : 'rgba(255, 255, 255, 0.6)';
return (
<HStack
key={stock.stock_code}
py={1.5}
px={2}
justify="space-between"
cursor="pointer"
borderRadius="md"
_hover={{ bg: 'rgba(255, 255, 255, 0.05)' }}
onClick={() => onStockClick?.(stock)}
>
<VStack align="start" spacing={0} flex={1} minW={0}>
<Text
fontSize="xs"
fontWeight="medium"
color="rgba(255, 255, 255, 0.9)"
noOfLines={1}
>
{stock.stock_name || stock.stock_code}
</Text>
<Text fontSize="10px" color="rgba(255, 255, 255, 0.4)">
{stock.stock_code}
</Text>
</VStack>
<VStack align="end" spacing={0}>
<Text fontSize="xs" fontWeight="bold" color={changeColor}>
{quote?.current_price?.toFixed(2) || stock.current_price || '--'}
</Text>
<Text fontSize="10px" color={changeColor}>
{changePercent !== undefined && changePercent !== null
? `${isUp ? '+' : ''}${Number(changePercent).toFixed(2)}%`
: '--'}
</Text>
</VStack>
</HStack>
);
})}
</VStack>
</Box>
)}
</Box> </Box>
); );
}; };