diff --git a/src/views/Company/components/DynamicTracking/components/DynamicTrackingNavSkeleton.tsx b/src/views/Company/components/DynamicTracking/components/DynamicTrackingNavSkeleton.tsx new file mode 100644 index 00000000..8c545ed6 --- /dev/null +++ b/src/views/Company/components/DynamicTracking/components/DynamicTrackingNavSkeleton.tsx @@ -0,0 +1,158 @@ +/** + * 动态跟踪 - 导航骨架屏组件 + * + * 用于懒加载时显示,让二级导航立即可见 + * 导航使用真实 UI,内容区域显示骨架屏 + */ + +import React from 'react'; +import { + Box, + Flex, + HStack, + Text, + Icon, + Skeleton, + VStack, + Card, + CardBody, +} from '@chakra-ui/react'; +import { FaNewspaper, FaBullhorn, FaCalendarAlt, FaChartBar } from 'react-icons/fa'; + +// 深空 FUI 主题配置(与 SubTabContainer 保持一致) +const DEEP_SPACE = { + bgGlass: 'rgba(12, 14, 28, 0.6)', + borderGold: 'rgba(212, 175, 55, 0.2)', + borderGoldHover: 'rgba(212, 175, 55, 0.5)', + glowGold: '0 0 30px rgba(212, 175, 55, 0.25), 0 4px 20px rgba(0, 0, 0, 0.3)', + innerGlow: 'inset 0 1px 0 rgba(255, 255, 255, 0.08)', + textWhite: 'rgba(255, 255, 255, 0.95)', + textDark: '#0A0A14', + selectedBg: 'linear-gradient(135deg, rgba(212, 175, 55, 0.95) 0%, rgba(184, 150, 12, 0.95) 100%)', + radius: '12px', + radiusLG: '16px', +}; + +// 导航配置(与主组件保持同步) +const TRACKING_TABS = [ + { key: 'news', name: '新闻动态', icon: FaNewspaper }, + { key: 'announcements', name: '公司公告', icon: FaBullhorn }, + { key: 'disclosure', name: '财报披露日程', icon: FaCalendarAlt }, + { key: 'forecast', name: '业绩预告', icon: FaChartBar }, +]; + +/** + * 新闻动态内容骨架屏 + */ +const NewsContentSkeleton: React.FC = () => ( + + {[1, 2, 3, 4, 5].map((i) => ( + + + + + + + + + + + + ))} + +); + +/** + * DynamicTracking 导航骨架屏 + * + * 显示真实的导航 Tab(默认选中第一个),内容区域显示骨架屏 + */ +const DynamicTrackingNavSkeleton: React.FC = () => { + return ( + + {/* 导航栏容器 */} + + {/* 顶部金色光条 */} + + + {/* Tab 列表 */} + + + {TRACKING_TABS.map((tab, idx) => { + const isSelected = idx === 0; + + return ( + + + + {tab.name} + + + ); + })} + + + + + {/* 内容区域骨架屏 */} + + + ); +}; + +export default DynamicTrackingNavSkeleton; diff --git a/src/views/Company/components/DynamicTracking/components/ForecastPanel.js b/src/views/Company/components/DynamicTracking/components/ForecastPanel.js index efabc111..d43f13f7 100644 --- a/src/views/Company/components/DynamicTracking/components/ForecastPanel.js +++ b/src/views/Company/components/DynamicTracking/components/ForecastPanel.js @@ -7,9 +7,9 @@ import { Box, Flex, Text, - Spinner, Center, } from '@chakra-ui/react'; +import { ForecastPanelSkeleton } from './DynamicTrackingSkeleton'; import { Tag } from 'antd'; import { logger } from '@utils/logger'; import axios from '@utils/axiosConfig'; @@ -42,9 +42,17 @@ const getForecastTypeStyle = (type) => { return styles[type] || { color: THEME.gold, bg: THEME.goldLight, border: THEME.goldBorder }; }; -const ForecastPanel = ({ stockCode }) => { +/** + * 业绩预告面板 + * @param {Object} props + * @param {string} props.stockCode - 股票代码 + * @param {boolean} props.isActive - SubTabContainer 传递的激活状态,控制是否加载数据 + * @param {number} props.activationKey - 激活次数,变化时触发重新请求 + */ +const ForecastPanel = ({ stockCode, isActive = true, activationKey }) => { const [forecast, setForecast] = useState(null); - const [loading, setLoading] = useState(false); + // 智能初始化 loading:当需要加载数据时,初始值为 true,避免首次渲染闪现空状态 + const [loading, setLoading] = useState(() => isActive && !!stockCode); const loadForecast = useCallback(async () => { if (!stockCode) return; @@ -65,16 +73,15 @@ const ForecastPanel = ({ stockCode }) => { } }, [stockCode]); + // 加载数据 - activationKey 变化时也会触发重新请求(实现切换刷新) useEffect(() => { - loadForecast(); - }, [loadForecast]); + if (isActive) { + loadForecast(); + } + }, [isActive, activationKey, loadForecast]); if (loading) { - return ( -
- -
- ); + return ; } if (!forecast?.forecasts?.length) { diff --git a/src/views/Company/components/DynamicTracking/components/NewsPanel.js b/src/views/Company/components/DynamicTracking/components/NewsPanel.js index 708df46b..e59d3aeb 100644 --- a/src/views/Company/components/DynamicTracking/components/NewsPanel.js +++ b/src/views/Company/components/DynamicTracking/components/NewsPanel.js @@ -6,9 +6,17 @@ import { logger } from '@utils/logger'; import axios from '@utils/axiosConfig'; import NewsEventsTab from '../NewsEventsTab'; -const NewsPanel = ({ stockCode }) => { +/** + * 新闻动态面板 + * @param {Object} props + * @param {string} props.stockCode - 股票代码 + * @param {boolean} props.isActive - SubTabContainer 传递的激活状态,控制是否加载数据 + * @param {number} props.activationKey - 激活次数,变化时触发重新请求 + */ +const NewsPanel = ({ stockCode, isActive = true, activationKey }) => { const [newsEvents, setNewsEvents] = useState([]); - const [loading, setLoading] = useState(false); + // 智能初始化 loading:当需要加载数据时,初始值为 true,避免首次渲染闪现空状态 + const [loading, setLoading] = useState(() => isActive && !!stockCode); const [pagination, setPagination] = useState({ page: 1, per_page: 10, @@ -53,12 +61,12 @@ const NewsPanel = ({ stockCode }) => { [stockCode] ); - // 首次加载 - 直接用股票代码搜索 + // 加载数据 - activationKey 变化时也会触发重新请求(实现切换刷新) useEffect(() => { - if (stockCode) { + if (isActive && stockCode) { loadNewsEvents(null, 1); } - }, [stockCode, loadNewsEvents]); + }, [stockCode, isActive, activationKey, loadNewsEvents]); // 搜索处理 const handleSearchChange = (value) => { diff --git a/src/views/Company/components/DynamicTracking/index.js b/src/views/Company/components/DynamicTracking/index.js index 3110220a..ba15d544 100644 --- a/src/views/Company/components/DynamicTracking/index.js +++ b/src/views/Company/components/DynamicTracking/index.js @@ -3,7 +3,7 @@ // 优化:子组件懒加载,骨架屏即时反馈 import React, { useState, useEffect, useMemo, useCallback, memo, lazy } from 'react'; -import { Box } from '@chakra-ui/react'; +import { Card, CardBody } from '@chakra-ui/react'; import { FaNewspaper, FaBullhorn, FaCalendarAlt, FaChartBar } from 'react-icons/fa'; import SubTabContainer from '@components/SubTabContainer'; @@ -92,17 +92,19 @@ const DynamicTracking = memo(({ stockCode: propStockCode }) => { }, []); return ( - - - + + + + + ); });