refactor(StockOverview): 优化布局与数据展示

- 头部统计卡片从 4 列精简为 3 列,移除冗余下跌家数
- 涨跌家数改为"多空对比"卡片,双色数值 + 进度条
- 各卡片新增环比趋势指示(放量/缩量等)
- 日期选择器移至 HotspotOverview 头部右侧
- 大盘分时图调整至统计卡片上方
- 异动标签支持点击筛选

🤖 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 15:55:53 +08:00
parent 91d89fb958
commit eaf11713e8
2 changed files with 196 additions and 113 deletions

View File

@@ -48,7 +48,7 @@ import {
Skeleton,
SkeletonText,
} from '@chakra-ui/react';
import { Search, X, ArrowRight, TrendingUp, Info, ChevronRight, Calendar, LineChart, Flame, Rocket, Brain, ArrowUp, ArrowDown, BarChart2, Tag as TagIcon, Layers, Zap } from 'lucide-react';
import { Search, X, ArrowRight, TrendingUp, Info, ChevronRight, Calendar, LineChart, Flame, Rocket, Brain, ArrowUp, ArrowDown, BarChart2, Tag as TagIcon, Layers, Zap, Wallet, Banknote, Scale } from 'lucide-react';
import ConceptStocksModal from '@components/ConceptStocksModal';
import TradeDatePicker from '@components/TradeDatePicker';
import HotspotOverview from './components/HotspotOverview';
@@ -265,12 +265,14 @@ const StockOverview = () => {
const newStats = {
...(prevStats || {}), // 先保留所有现有字段(包括 rising_count/falling_count
...data.summary, // 然后覆盖 summary 字段
yesterday: data.yesterday, // 保存昨日对比数据
date: data.trade_date
};
return newStats;
});
const newStats = {
...data.summary,
yesterday: data.yesterday,
date: data.trade_date
};
// 日期和可选日期列表由 fetchTopConcepts 统一设置,这里不再设置
@@ -674,42 +676,69 @@ const StockOverview = () => {
onResultSelect: (item, index) => handleSelectStock(item.raw, index),
}}
stats={{
columns: { base: 2, md: 4 },
columns: { base: 1, sm: 3, md: 3 },
items: [
{
key: 'marketCap',
label: 'A股总市值',
value: marketStats ? `${(marketStats.total_market_cap / 10000).toFixed(1)}万亿` : null,
// 市值趋势对比
trend: marketStats?.yesterday?.total_market_cap ? (() => {
const change = ((marketStats.total_market_cap - marketStats.yesterday.total_market_cap) / marketStats.yesterday.total_market_cap) * 100;
return {
direction: change > 0.01 ? 'up' : change < -0.01 ? 'down' : 'flat',
percent: Math.abs(change),
};
})() : undefined,
// 水印背景图标 - 钱袋图标
watermark: { icon: Wallet, color: goldColor, opacity: 0.1 },
},
{
key: 'amount',
label: '今日成交额',
value: marketStats ? `${(marketStats.total_amount / 10000).toFixed(1)}万亿` : null,
// 成交额趋势对比(放量/缩量)
trend: marketStats?.yesterday?.total_amount ? (() => {
const change = ((marketStats.total_amount - marketStats.yesterday.total_amount) / marketStats.yesterday.total_amount) * 100;
return {
direction: change > 0.01 ? 'up' : change < -0.01 ? 'down' : 'flat',
percent: Math.abs(change),
label: change > 5 ? '放量' : change < -5 ? '缩量' : undefined,
};
})() : undefined,
// 水印背景图标 - 钞票图标
watermark: { icon: Banknote, color: goldColor, opacity: 0.1 },
},
{
key: 'rising',
label: '上涨家数',
value: marketStats?.rising_count,
valueColor: '#ff4d4d',
},
{
key: 'falling',
label: '下跌家数',
value: marketStats?.falling_count,
valueColor: 'green.400',
label: '多空对比',
// 显示为 "上涨/下跌" 格式,使用自定义渲染
value: (marketStats?.rising_count && marketStats?.falling_count)
? `${marketStats.rising_count}/${marketStats.falling_count}`
: marketStats?.rising_count,
// 涨跌进度条(不显示底部标签)
progressBar: (marketStats?.rising_count && marketStats?.falling_count) ? {
value: marketStats.rising_count,
total: marketStats.falling_count,
positiveColor: '#ff4d4d',
negativeColor: '#22c55e',
} : undefined,
// 水印背景图标 - 天平图标
watermark: { icon: Scale, color: goldColor, opacity: 0.1 },
},
],
}}
/>
{/* 主内容区 */}
<Box py={10} px={6} position="relative" zIndex={1}>
{/* 日期选择器 */}
<Box mb={6}>
<Flex align="center" gap={4} flexWrap="wrap">
<TradeDatePicker
value={selectedDate}
onChange={(date) => {
{/* 主内容区 - 负 margin 使卡片向上浮动,与 Hero 产生重叠纵深感 */}
<Box pt={6} pb={10} px={6} mt={-6} position="relative" zIndex={2}>
{/* 热点概览 - 大盘走势 + 概念异动 */}
{/* 只在 selectedDate 确定后渲染,避免 null → 日期 的双重请求 */}
<Box mb={10}>
{selectedDate ? (
<HotspotOverview
selectedDate={selectedDate}
onDateChange={(date) => {
const dateStr = date.toISOString().split('T')[0];
const previousDateStr = selectedDate ? selectedDate.toISOString().split('T')[0] : null;
trackDateChanged(dateStr, previousDateStr);
@@ -718,25 +747,9 @@ const StockOverview = () => {
fetchMarketStats(dateStr);
fetchTopConcepts(dateStr);
}}
latestTradeDate={null}
minDate={tradingDays.length > 0 ? new Date(tradingDays[0]) : undefined}
maxDate={tradingDays.length > 0 ? new Date(tradingDays[tradingDays.length - 1]) : undefined}
label="交易日期"
isDarkMode={true}
/>
</Flex>
{selectedDate && (
<Text fontSize="sm" color={subTextColor} mt={2}>
当前显示 {selectedDate.toISOString().split('T')[0]} 的市场数据
</Text>
)}
</Box>
{/* 热点概览 - 大盘走势 + 概念异动 */}
{/* 只在 selectedDate 确定后渲染,避免 null → 日期 的双重请求 */}
<Box mb={10}>
{selectedDate ? (
<HotspotOverview selectedDate={selectedDate} />
) : (
<Card
bg={cardBg}