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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user