From eaf11713e88bc88cf5770d46eefa1b9cdb3d3844 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Tue, 30 Dec 2025 15:55:53 +0800 Subject: [PATCH] =?UTF-8?q?refactor(StockOverview):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=B8=8E=E6=95=B0=E6=8D=AE=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 头部统计卡片从 4 列精简为 3 列,移除冗余下跌家数 - 涨跌家数改为"多空对比"卡片,双色数值 + 进度条 - 各卡片新增环比趋势指示(放量/缩量等) - 日期选择器移至 HotspotOverview 头部右侧 - 大盘分时图调整至统计卡片上方 - 异动标签支持点击筛选 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/HotspotOverview/index.js | 226 ++++++++++++------ src/views/StockOverview/index.js | 83 ++++--- 2 files changed, 196 insertions(+), 113 deletions(-) diff --git a/src/views/StockOverview/components/HotspotOverview/index.js b/src/views/StockOverview/components/HotspotOverview/index.js index f93b4535..17092fc1 100644 --- a/src/views/StockOverview/components/HotspotOverview/index.js +++ b/src/views/StockOverview/components/HotspotOverview/index.js @@ -3,8 +3,9 @@ * 展示大盘分时走势 + 概念异动标注 * * 布局设计: - * - 顶部:统计摘要(指数信息 + 异动统计) + * - 顶部:标题 + 日期选择器 + 异动数量 * - 中部:大尺寸分时图(主要展示区域) + * - 下方:统计卡片(指数信息 + 异动统计) * - 底部:异动列表(横向滚动卡片) */ import React, { useState, useCallback } from 'react'; @@ -39,6 +40,7 @@ import { import { useHotspotData } from './hooks'; import { IndexMinuteChart, AlertDetailDrawer } from './components'; import { ALERT_TYPE_CONFIG, getAlertTypeLabel } from './utils/chartHelpers'; +import TradeDatePicker from '@components/TradeDatePicker'; import { glassEffect, colors, @@ -192,10 +194,15 @@ const CompactAlertCard = ({ alert, onClick, isSelected }) => { * 热点概览主组件 * @param {Object} props * @param {Date|null} props.selectedDate - 选中的交易日期 + * @param {Function} props.onDateChange - 日期变更回调 + * @param {Date} props.minDate - 最小可选日期 + * @param {Date} props.maxDate - 最大可选日期 */ -const HotspotOverview = ({ selectedDate }) => { +const HotspotOverview = ({ selectedDate, onDateChange, minDate, maxDate }) => { const [selectedAlert, setSelectedAlert] = useState(null); const [drawerAlertData, setDrawerAlertData] = useState(null); + // 选中的异动类型过滤器(null 表示全部) + const [selectedAlertType, setSelectedAlertType] = useState(null); // 右边栏抽屉控制 const { isOpen: isDrawerOpen, onOpen: onDrawerOpen, onClose: onDrawerClose } = useDisclosure(); @@ -231,6 +238,11 @@ const HotspotOverview = ({ selectedDate }) => { onDrawerOpen(); }, [onDrawerOpen]); + // 点击异动类型标签 - 切换过滤器 + const handleAlertTypeClick = useCallback((type) => { + setSelectedAlertType(prevType => prevType === type ? null : type); + }, []); + // 渲染加载状态 - Glassmorphism 风格 if (loading) { return ( @@ -335,6 +347,11 @@ const HotspotOverview = ({ selectedDate }) => { const { index, alerts, alert_summary } = data; + // 根据选中的类型过滤异动列表 + const filteredAlerts = selectedAlertType + ? alerts.filter(alert => alert.alert_type === selectedAlertType) + : alerts; + // 计算市场颜色 const marketColor = getMarketColor(index?.change_pct || 0); const marketGlow = getMarketGlow(index?.change_pct || 0); @@ -415,6 +432,18 @@ const HotspotOverview = ({ selectedDate }) => { + {/* 日期选择器 */} + {onDateChange && ( + + )} {alerts.length > 0 && ( { )} - + @@ -449,7 +478,65 @@ const HotspotOverview = ({ selectedDate }) => { - {/* 统计摘要 - Glassmorphism Bento Grid */} + {/* 大尺寸分时图 - Glassmorphism(移到统计卡片前面) */} + + {/* 图表区域背景光晕 */} + + + + + + + 大盘分时走势 + + + + + + + + + {/* 统计摘要 - Glassmorphism Bento Grid(移到分时图后面) */} {/* 指数信息卡片 */} { }} > - 今日异动 - - {alerts.length} 次 - + + 今日异动 + (点击筛选) + + + {selectedAlertType && ( + setSelectedAlertType(null)} + _hover={{ textDecoration: 'underline' }} + > + 清除筛选 + + )} + + {selectedAlertType ? `${filteredAlerts.length}/${alerts.length}` : alerts.length} 次 + + {Object.entries(alert_summary || {}) @@ -563,6 +666,7 @@ const HotspotOverview = ({ selectedDate }) => { .map(([type, count]) => { const config = ALERT_TYPE_CONFIG[type]; if (!config) return null; + const isSelected = selectedAlertType === type; return ( { px={3} py={1.5} borderRadius="full" - bg={`${config.color}15`} - border={`1px solid ${config.color}25`} + bg={isSelected ? `${config.color}35` : `${config.color}15`} + border={isSelected ? `2px solid ${config.color}` : `1px solid ${config.color}25`} + cursor="pointer" transition="all 0.2s" + transform={isSelected ? 'scale(1.05)' : 'scale(1)'} + boxShadow={isSelected ? `0 0 20px ${config.color}40` : 'none'} + onClick={() => handleAlertTypeClick(type)} _hover={{ bg: `${config.color}25`, boxShadow: `0 0 15px ${config.color}30`, + transform: 'scale(1.02)', }} > - {config.label} + {config.label} { - {/* 大尺寸分时图 - Glassmorphism */} - - {/* 图表区域背景光晕 */} - - - - - - - 大盘分时走势 - - - - - - - - {/* 异动列表 - Glassmorphism 横向滚动 */} {alerts.length > 0 && ( @@ -670,7 +721,11 @@ const HotspotOverview = ({ selectedDate }) => { /> 异动记录 - (点击卡片查看详情) + + {selectedAlertType + ? `(已筛选 ${ALERT_TYPE_CONFIG[selectedAlertType]?.label || selectedAlertType},共 ${filteredAlerts.length} 条)` + : '(点击卡片查看详情)'} + {/* 横向滚动卡片 */} @@ -688,7 +743,7 @@ const HotspotOverview = ({ selectedDate }) => { }} > - {[...alerts] + {[...filteredAlerts] .sort((a, b) => (b.time || '').localeCompare(a.time || '')) .map((alert, idx) => ( { )} {/* 无异动提示 - Glassmorphism */} - {alerts.length === 0 && ( + {filteredAlerts.length === 0 && (
{ css={css`filter: drop-shadow(0 0 10px rgba(139, 92, 246, 0.3));`} /> - 当日暂无概念异动数据 + + {selectedAlertType + ? `未找到「${ALERT_TYPE_CONFIG[selectedAlertType]?.label || selectedAlertType}」类型的异动` + : '当日暂无概念异动数据'} + + {selectedAlertType && ( + setSelectedAlertType(null)} + _hover={{ textDecoration: 'underline' }} + > + 查看全部异动 + + )}
)} diff --git a/src/views/StockOverview/index.js b/src/views/StockOverview/index.js index 97a099d3..4cb1130b 100644 --- a/src/views/StockOverview/index.js +++ b/src/views/StockOverview/index.js @@ -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 }, }, ], }} /> - {/* 主内容区 */} - - {/* 日期选择器 */} - - - { + {/* 主内容区 - 负 margin 使卡片向上浮动,与 Hero 产生重叠纵深感 */} + + {/* 热点概览 - 大盘走势 + 概念异动 */} + {/* 只在 selectedDate 确定后渲染,避免 null → 日期 的双重请求 */} + + {selectedDate ? ( + { 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} /> - - {selectedDate && ( - - 当前显示 {selectedDate.toISOString().split('T')[0]} 的市场数据 - - )} - - - {/* 热点概览 - 大盘走势 + 概念异动 */} - {/* 只在 selectedDate 确定后渲染,避免 null → 日期 的双重请求 */} - - {selectedDate ? ( - ) : (