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 (
-
-
-
+
+
+
+
+
);
});