feat(WatchSidebar): 新增右侧边栏组件
- 关注股票面板(独立模块) - 关注事件面板(独立模块) - 固定200px宽度,粘性定位 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
|||||||
|
// 关注事件面板 - 紧凑版
|
||||||
|
import React from 'react';
|
||||||
|
import { Box, Text, VStack, HStack, Icon } from '@chakra-ui/react';
|
||||||
|
import { Star, Plus, Users } from 'lucide-react';
|
||||||
|
|
||||||
|
const FollowingEventsPanel = ({
|
||||||
|
events = [],
|
||||||
|
onEventClick,
|
||||||
|
onAddEvent,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{/* 标题 */}
|
||||||
|
<HStack justify="space-between" mb={2}>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
<Icon as={Star} boxSize={3.5} color="rgba(234, 179, 8, 0.9)" />
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="rgba(255, 255, 255, 0.9)">
|
||||||
|
关注事件
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="xs" color="rgba(255, 255, 255, 0.5)">
|
||||||
|
({events.length})
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
<Icon
|
||||||
|
as={Plus}
|
||||||
|
boxSize={3.5}
|
||||||
|
color="rgba(255, 255, 255, 0.5)"
|
||||||
|
cursor="pointer"
|
||||||
|
_hover={{ color: 'rgba(212, 175, 55, 0.9)' }}
|
||||||
|
onClick={onAddEvent}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{/* 事件列表 */}
|
||||||
|
<VStack spacing={1.5} align="stretch">
|
||||||
|
{events.length === 0 ? (
|
||||||
|
<Box
|
||||||
|
py={4}
|
||||||
|
textAlign="center"
|
||||||
|
cursor="pointer"
|
||||||
|
onClick={onAddEvent}
|
||||||
|
_hover={{ bg: 'rgba(255, 255, 255, 0.05)' }}
|
||||||
|
borderRadius="md"
|
||||||
|
>
|
||||||
|
<Icon as={Star} boxSize={6} color="rgba(255, 255, 255, 0.2)" mb={1} />
|
||||||
|
<Text fontSize="xs" color="rgba(255, 255, 255, 0.4)">
|
||||||
|
关注事件
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
events.slice(0, 6).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 (
|
||||||
|
<Box
|
||||||
|
key={event.id}
|
||||||
|
py={2}
|
||||||
|
px={2}
|
||||||
|
cursor="pointer"
|
||||||
|
borderRadius="md"
|
||||||
|
bg="rgba(37, 37, 64, 0.3)"
|
||||||
|
_hover={{ bg: 'rgba(37, 37, 64, 0.6)' }}
|
||||||
|
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>
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FollowingEventsPanel;
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// 关注股票面板 - 紧凑版
|
||||||
|
import React from 'react';
|
||||||
|
import { Box, Text, VStack, HStack, Icon } from '@chakra-ui/react';
|
||||||
|
import { BarChart2, Plus } from 'lucide-react';
|
||||||
|
|
||||||
|
const WatchlistPanel = ({
|
||||||
|
watchlist = [],
|
||||||
|
realtimeQuotes = {},
|
||||||
|
onStockClick,
|
||||||
|
onAddStock,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{/* 标题 */}
|
||||||
|
<HStack justify="space-between" mb={2}>
|
||||||
|
<HStack spacing={1}>
|
||||||
|
<Icon as={BarChart2} boxSize={3.5} color="rgba(59, 130, 246, 0.9)" />
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="rgba(255, 255, 255, 0.9)">
|
||||||
|
关注股票
|
||||||
|
</Text>
|
||||||
|
<Text fontSize="xs" color="rgba(255, 255, 255, 0.5)">
|
||||||
|
({watchlist.length})
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
<Icon
|
||||||
|
as={Plus}
|
||||||
|
boxSize={3.5}
|
||||||
|
color="rgba(255, 255, 255, 0.5)"
|
||||||
|
cursor="pointer"
|
||||||
|
_hover={{ color: 'rgba(212, 175, 55, 0.9)' }}
|
||||||
|
onClick={onAddStock}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{/* 股票列表 */}
|
||||||
|
<VStack spacing={1} align="stretch">
|
||||||
|
{watchlist.length === 0 ? (
|
||||||
|
<Box
|
||||||
|
py={4}
|
||||||
|
textAlign="center"
|
||||||
|
cursor="pointer"
|
||||||
|
onClick={onAddStock}
|
||||||
|
_hover={{ bg: 'rgba(255, 255, 255, 0.05)' }}
|
||||||
|
borderRadius="md"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WatchlistPanel;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// 侧边栏子组件导出
|
||||||
|
export { default as WatchlistPanel } from './WatchlistPanel';
|
||||||
|
export { default as FollowingEventsPanel } from './FollowingEventsPanel';
|
||||||
50
src/views/Profile/components/WatchSidebar/index.js
Normal file
50
src/views/Profile/components/WatchSidebar/index.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// 右侧边栏 - 关注股票和关注事件(两个独立模块)
|
||||||
|
import React from 'react';
|
||||||
|
import { VStack } from '@chakra-ui/react';
|
||||||
|
import GlassCard from '@components/GlassCard';
|
||||||
|
import { WatchlistPanel, FollowingEventsPanel } from './components';
|
||||||
|
|
||||||
|
const WatchSidebar = ({
|
||||||
|
watchlist = [],
|
||||||
|
realtimeQuotes = {},
|
||||||
|
followingEvents = [],
|
||||||
|
onStockClick,
|
||||||
|
onEventClick,
|
||||||
|
onAddStock,
|
||||||
|
onAddEvent,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<VStack spacing={4} align="stretch">
|
||||||
|
{/* 关注股票 - 独立模块 */}
|
||||||
|
<GlassCard
|
||||||
|
variant="transparent"
|
||||||
|
rounded="xl"
|
||||||
|
padding="sm"
|
||||||
|
hoverable={false}
|
||||||
|
>
|
||||||
|
<WatchlistPanel
|
||||||
|
watchlist={watchlist}
|
||||||
|
realtimeQuotes={realtimeQuotes}
|
||||||
|
onStockClick={onStockClick}
|
||||||
|
onAddStock={onAddStock}
|
||||||
|
/>
|
||||||
|
</GlassCard>
|
||||||
|
|
||||||
|
{/* 关注事件 - 独立模块 */}
|
||||||
|
<GlassCard
|
||||||
|
variant="transparent"
|
||||||
|
rounded="xl"
|
||||||
|
padding="sm"
|
||||||
|
hoverable={false}
|
||||||
|
>
|
||||||
|
<FollowingEventsPanel
|
||||||
|
events={followingEvents}
|
||||||
|
onEventClick={onEventClick}
|
||||||
|
onAddEvent={onAddEvent}
|
||||||
|
/>
|
||||||
|
</GlassCard>
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WatchSidebar;
|
||||||
Reference in New Issue
Block a user