refactor(StockOverview): 迁移至 HeroSection 组件

- 使用通用 HeroSection 替换原有 Hero 区域代码
- 配置 purple 主题预设和自定义金色渐变
- 统计区显示市值、成交额、上涨/下跌家数
- 搜索框支持下拉结果选择

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-30 11:11:32 +08:00
parent 6c10d420a1
commit bc6d5fd222

View File

@@ -53,6 +53,7 @@ import ConceptStocksModal from '@components/ConceptStocksModal';
import TradeDatePicker from '@components/TradeDatePicker'; import TradeDatePicker from '@components/TradeDatePicker';
import HotspotOverview from './components/HotspotOverview'; import HotspotOverview from './components/HotspotOverview';
import FlexScreen from './components/FlexScreen'; import FlexScreen from './components/FlexScreen';
import { HeroSection } from '@components/HeroSection';
import { echarts } from '@lib/echarts'; import { echarts } from '@lib/echarts';
import { logger } from '../../utils/logger'; import { logger } from '../../utils/logger';
import { getConceptHtmlUrl } from '../../utils/textUtils'; import { getConceptHtmlUrl } from '../../utils/textUtils';
@@ -623,240 +624,83 @@ const StockOverview = () => {
zIndex={0} zIndex={0}
/> />
{/* Hero Section */} {/* Hero Section - 使用通用 HeroSection 组件 */}
<Box <HeroSection
position="relative" icon={TrendingUp}
bgGradient={heroBg} title="个股中心"
color="white" subtitle="实时追踪市场动态,洞察投资机会"
overflow="visible" themePreset="purple"
pt={{ base: 20, md: 24 }} themeColors={{
pb={{ base: 16, md: 20 }} titleGradient: `linear(to-r, ${goldColor}, white)`,
borderBottom={`1px solid rgba(139, 92, 246, 0.3)`} iconColor: goldColor,
borderRadius="xl" statLabelColor: goldColor,
zIndex={1} statCardBorder: `${goldColor}50`,
> }}
{/* 背景装饰 */} decorations={[
<Box {
position="absolute" type: 'glowOrbs',
top="-20%" intensity: 0.4,
right="-10%" orbs: [
width="40%" { top: '-20%', right: '-10%', size: '40%', color: `${goldColor}15`, blur: '60px' },
height="120%" ],
bg={`${goldColor}15`} },
transform="rotate(12deg)" ]}
borderRadius="full" search={{
filter="blur(60px)" placeholder: '搜索股票代码、名称或拼音首字母...',
/> showSearchButton: true,
searchButtonText: '搜索',
<Box px={6} position="relative"> maxWidth: '2xl',
<VStack spacing={8} align="center"> // 受控模式
<VStack spacing={4} textAlign="center" maxW="3xl"> value: searchQuery,
<HStack spacing={3}> onChange: (value) => {
<Icon as={TrendingUp} boxSize={12} color={colorMode === 'dark' ? goldColor : 'white'} /> setSearchQuery(value);
<Heading if (value && !searchQuery) {
as="h1" trackSearchInitiated();
size="2xl" }
fontWeight="bold" debounceSearch(value);
bgGradient={colorMode === 'dark' ? `linear(to-r, ${goldColor}, white)` : 'none'} },
bgClip={colorMode === 'dark' ? 'text' : 'none'} onClear: handleClearSearch,
> results: searchResults.map((stock) => ({
个股中心 id: stock.stock_code,
</Heading> label: stock.stock_name,
</HStack> subLabel: stock.stock_code,
extra: stock.pinyin_abbr?.toUpperCase(),
<Text fontSize="xl" opacity={0.9} color={colorMode === 'dark' ? 'gray.300' : 'white'}> tags: stock.exchange ? [{ text: stock.exchange }] : [],
实时追踪市场动态洞察投资机会 raw: stock,
</Text> })),
</VStack> isSearching: isSearching,
showDropdown: showResults,
{/* 搜索框 */} onSearch: async () => [],
<Box w="100%" maxW="2xl" position="relative"> onResultSelect: (item, index) => handleSelectStock(item.raw, index),
<InputGroup }}
size="lg" stats={{
bg={searchBg} columns: { base: 2, md: 4 },
borderRadius="full" items: [
boxShadow="2xl" {
border="2px solid" key: 'marketCap',
borderColor={colorMode === 'dark' ? goldColor : 'transparent'} label: 'A股总市值',
> value: marketStats ? `${(marketStats.total_market_cap / 10000).toFixed(1)}万亿` : null,
<InputLeftElement pointerEvents="none"> },
<Search size={16} color={colorMode === 'dark' ? goldColor : 'gray.400'} /> {
</InputLeftElement> key: 'amount',
<Input label: '今日成交额',
placeholder="搜索股票代码、名称或拼音首字母..." value: marketStats ? `${(marketStats.total_amount / 10000).toFixed(1)}万亿` : null,
value={searchQuery} },
onChange={handleSearchChange} {
borderRadius="full" key: 'rising',
border="none" label: '上涨家数',
color={textColor} value: marketStats?.rising_count,
bg="transparent" valueColor: '#ff4d4d',
_placeholder={{ color: colorMode === 'dark' ? 'gray.500' : 'gray.400' }} },
_focus={{ {
boxShadow: 'none', key: 'falling',
borderColor: 'transparent', label: '下跌家数',
bg: colorMode === 'dark' ? 'whiteAlpha.50' : 'transparent' value: marketStats?.falling_count,
}} valueColor: 'green.400',
pr={searchQuery ? "3rem" : "1rem"} },
/> ],
{searchQuery && ( }}
<InputRightElement> />
<IconButton
size="sm"
icon={<X size={16} />}
variant="ghost"
onClick={handleClearSearch}
aria-label="清空搜索"
color={colorMode === 'dark' ? goldColor : 'gray.600'}
_hover={{
bg: colorMode === 'dark' ? 'whiteAlpha.100' : 'gray.100'
}}
/>
</InputRightElement>
)}
</InputGroup>
{/* 搜索结果下拉 */}
<Collapse in={showResults} animateOpacity>
<Box
position="absolute"
top="100%"
left={0}
right={0}
mt={2}
bg={searchBg}
borderRadius="xl"
boxShadow="2xl"
border="1px solid"
borderColor={borderColor}
maxH="400px"
overflowY="auto"
zIndex={10}
>
{isSearching ? (
<Center p={4}>
<Spinner color={accentColor} />
</Center>
) : searchResults.length > 0 ? (
<List spacing={0}>
{searchResults.map((stock, index) => (
<ListItem
key={stock.stock_code}
p={4}
cursor="pointer"
_hover={{ bg: hoverBg }}
onClick={() => handleSelectStock(stock, index)}
borderBottomWidth={index < searchResults.length - 1 ? "1px" : "0"}
borderColor={borderColor}
>
<Flex align="center" justify="space-between">
<VStack align="start" spacing={1} flex={1}>
<Text fontWeight="bold" color={textColor}>{stock.stock_name}</Text>
<HStack spacing={2}>
<Text fontSize="sm" color={subTextColor}>{stock.stock_code}</Text>
{stock.pinyin_abbr && (
<Text fontSize="xs" color={subTextColor}>({stock.pinyin_abbr.toUpperCase()})</Text>
)}
{stock.exchange && (
<Tag
size="sm"
bg={colorMode === 'dark' ? '#2a2a2a' : 'blue.50'}
color={colorMode === 'dark' ? goldColor : 'blue.600'}
border="1px solid"
borderColor={colorMode === 'dark' ? goldColor : 'blue.200'}
>
{stock.exchange}
</Tag>
)}
</HStack>
</VStack>
<Button
size="sm"
rightIcon={<ArrowRight size={16} />}
variant="ghost"
colorScheme={colorMode === 'dark' ? 'yellow' : 'purple'}
_hover={{
bg: colorMode === 'dark' ? 'whiteAlpha.100' : 'purple.50'
}}
>
查看
</Button>
</Flex>
</ListItem>
))}
</List>
) : (
<Center p={4}>
<Text color={subTextColor}>未找到相关股票</Text>
</Center>
)}
</Box>
</Collapse>
</Box>
{/* 统计数据 - 使用市场统计API数据 */}
<SimpleGrid columns={{ base: 2, md: 4 }} spacing={6} w="100%" maxW="4xl">
<Stat
textAlign="center"
bg={colorMode === 'dark' ? 'whiteAlpha.100' : 'whiteAlpha.800'}
p={4}
borderRadius="lg"
border={colorMode === 'dark' ? `1px solid ${goldColor}50` : 'none'}
>
<StatLabel color={colorMode === 'dark' ? goldColor : 'purple.700'} fontWeight="bold">A股总市值</StatLabel>
<StatNumber fontSize="2xl" color={colorMode === 'dark' ? 'white' : 'purple.800'}>
{marketStats ?
`${(marketStats.total_market_cap / 10000).toFixed(1)}万亿`
: '-'
}
</StatNumber>
</Stat>
<Stat
textAlign="center"
bg={colorMode === 'dark' ? 'whiteAlpha.100' : 'whiteAlpha.800'}
p={4}
borderRadius="lg"
border={colorMode === 'dark' ? `1px solid ${goldColor}50` : 'none'}
>
<StatLabel color={colorMode === 'dark' ? goldColor : 'purple.700'} fontWeight="bold">今日成交额</StatLabel>
<StatNumber fontSize="2xl" color={colorMode === 'dark' ? 'white' : 'purple.800'}>
{marketStats ?
`${(marketStats.total_amount / 10000).toFixed(1)}万亿`
: '-'
}
</StatNumber>
</Stat>
<Stat
textAlign="center"
bg={colorMode === 'dark' ? 'whiteAlpha.100' : 'whiteAlpha.800'}
p={4}
borderRadius="lg"
border={colorMode === 'dark' ? `1px solid ${goldColor}50` : 'none'}
>
<StatLabel color={colorMode === 'dark' ? goldColor : 'purple.700'} fontWeight="bold">上涨家数</StatLabel>
<StatNumber fontSize="2xl" color={colorMode === 'dark' ? '#ff4d4d' : 'red.500'}>
{marketStats && marketStats.rising_count !== undefined && marketStats.rising_count !== null ?
marketStats.rising_count.toLocaleString() : '-'
}
</StatNumber>
</Stat>
<Stat
textAlign="center"
bg={colorMode === 'dark' ? 'whiteAlpha.100' : 'whiteAlpha.800'}
p={4}
borderRadius="lg"
border={colorMode === 'dark' ? `1px solid ${goldColor}50` : 'none'}
>
<StatLabel color={colorMode === 'dark' ? goldColor : 'purple.700'} fontWeight="bold">下跌家数</StatLabel>
<StatNumber fontSize="2xl" color="green.400">
{marketStats && marketStats.falling_count !== undefined && marketStats.falling_count !== null ?
marketStats.falling_count.toLocaleString() : '-'
}
</StatNumber>
</Stat>
</SimpleGrid>
</VStack>
</Box>
</Box>
{/* 主内容区 */} {/* 主内容区 */}
<Box py={10} px={6} position="relative" zIndex={1}> <Box py={10} px={6} position="relative" zIndex={1}>