perf(DeepAnalysis): 优化初始加载,只请求 comprehensive 接口

- 移除初始加载时的 industryRank 请求
- 只加载默认 Tab(战略分析)需要的核心数据
- 其他数据按需懒加载

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-18 18:27:57 +08:00
parent eaa65b2328
commit 50d59fd2ad
12 changed files with 410 additions and 107 deletions

View File

@@ -0,0 +1,81 @@
/**
* 环境光效果组件
* James Turrell 风格的背景光晕效果
*/
import React, { memo } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
export interface AmbientGlowProps extends Omit<BoxProps, 'bg'> {
/** 预设主题 */
variant?: 'default' | 'gold' | 'blue' | 'purple' | 'warm';
/** 自定义渐变(覆盖 variant */
customGradient?: string;
}
// 预设光效配置
const GLOW_VARIANTS = {
default: `
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(212, 175, 55, 0.08), transparent 50%),
radial-gradient(ellipse 60% 50% at 0% 50%, rgba(100, 200, 255, 0.04), transparent 40%),
radial-gradient(ellipse 60% 50% at 100% 50%, rgba(255, 200, 100, 0.04), transparent 40%)
`,
gold: `
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(212, 175, 55, 0.12), transparent 50%),
radial-gradient(ellipse 80% 60% at 20% 80%, rgba(212, 175, 55, 0.06), transparent 40%),
radial-gradient(ellipse 80% 60% at 80% 80%, rgba(255, 200, 100, 0.05), transparent 40%)
`,
blue: `
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(100, 200, 255, 0.1), transparent 50%),
radial-gradient(ellipse 60% 50% at 0% 50%, rgba(60, 160, 255, 0.06), transparent 40%),
radial-gradient(ellipse 60% 50% at 100% 50%, rgba(140, 220, 255, 0.05), transparent 40%)
`,
purple: `
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(160, 100, 255, 0.1), transparent 50%),
radial-gradient(ellipse 60% 50% at 0% 50%, rgba(200, 150, 255, 0.05), transparent 40%),
radial-gradient(ellipse 60% 50% at 100% 50%, rgba(120, 80, 255, 0.05), transparent 40%)
`,
warm: `
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(255, 150, 100, 0.1), transparent 50%),
radial-gradient(ellipse 60% 50% at 0% 50%, rgba(255, 200, 150, 0.05), transparent 40%),
radial-gradient(ellipse 60% 50% at 100% 50%, rgba(255, 180, 120, 0.05), transparent 40%)
`,
};
/**
* 环境光效果组件
* 创建 James Turrell 风格的微妙背景光晕
*
* @example
* ```tsx
* <Box position="relative">
* <AmbientGlow variant="gold" />
* {children}
* </Box>
* ```
*/
const AmbientGlow = memo<AmbientGlowProps>(({
variant = 'default',
customGradient,
...boxProps
}) => {
const gradient = customGradient || GLOW_VARIANTS[variant];
return (
<Box
position="fixed"
top={0}
left={0}
right={0}
bottom={0}
pointerEvents="none"
zIndex={0}
bg={gradient}
{...boxProps}
/>
);
});
AmbientGlow.displayName = 'AmbientGlow';
export default AmbientGlow;

View File

@@ -0,0 +1,93 @@
/**
* FUI 毛玻璃容器组件
* 科幻风格的 Glassmorphism 容器,带角落装饰
*/
import React, { memo, ReactNode } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import FuiCorners, { FuiCornersProps } from './FuiCorners';
export interface FuiContainerProps extends Omit<BoxProps, 'children'> {
children: ReactNode;
/** 是否显示角落装饰 */
showCorners?: boolean;
/** 角落装饰配置 */
cornersProps?: FuiCornersProps;
/** 预设主题 */
variant?: 'default' | 'gold' | 'blue' | 'dark';
}
// 预设主题配置
const VARIANTS = {
default: {
bg: 'linear-gradient(145deg, rgba(26, 26, 46, 0.95) 0%, rgba(15, 15, 26, 0.98) 100%)',
borderColor: 'rgba(212, 175, 55, 0.15)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.05)',
cornerColor: 'rgba(212, 175, 55, 0.4)',
},
gold: {
bg: 'linear-gradient(145deg, rgba(26, 26, 46, 0.95) 0%, rgba(15, 15, 26, 0.98) 100%)',
borderColor: 'rgba(212, 175, 55, 0.2)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(212, 175, 55, 0.1)',
cornerColor: 'rgba(212, 175, 55, 0.5)',
},
blue: {
bg: 'linear-gradient(145deg, rgba(20, 30, 48, 0.95) 0%, rgba(10, 15, 26, 0.98) 100%)',
borderColor: 'rgba(100, 200, 255, 0.15)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(100, 200, 255, 0.05)',
cornerColor: 'rgba(100, 200, 255, 0.4)',
},
dark: {
bg: 'linear-gradient(145deg, rgba(18, 18, 28, 0.98) 0%, rgba(8, 8, 16, 0.99) 100%)',
borderColor: 'rgba(255, 255, 255, 0.08)',
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.03)',
cornerColor: 'rgba(255, 255, 255, 0.2)',
},
};
/**
* FUI 毛玻璃容器组件
* 带有科幻风格角落装饰的 Glassmorphism 容器
*
* @example
* ```tsx
* <FuiContainer variant="gold">
* <YourContent />
* </FuiContainer>
* ```
*/
const FuiContainer = memo<FuiContainerProps>(({
children,
showCorners = true,
cornersProps,
variant = 'default',
...boxProps
}) => {
const theme = VARIANTS[variant];
return (
<Box
position="relative"
bg={theme.bg}
borderRadius="xl"
border="1px solid"
borderColor={theme.borderColor}
overflow="hidden"
backdropFilter="blur(16px)"
boxShadow={theme.boxShadow}
{...boxProps}
>
{showCorners && (
<FuiCorners
borderColor={theme.cornerColor}
{...cornersProps}
/>
)}
{children}
</Box>
);
});
FuiContainer.displayName = 'FuiContainer';
export default FuiContainer;

View File

@@ -0,0 +1,126 @@
/**
* FUI 角落装饰组件
* Ash Thorp 风格的科幻 UI 角落装饰
*/
import React, { memo } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
export interface FuiCornersProps {
/** 装饰框大小 */
size?: number;
/** 边框宽度 */
borderWidth?: number;
/** 边框颜色 */
borderColor?: string;
/** 透明度 */
opacity?: number;
/** 距离容器边缘的距离 */
offset?: number;
}
interface CornerBoxProps extends BoxProps {
position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
size: number;
borderWidth: number;
borderColor: string;
opacity: number;
offset: number;
}
const CornerBox = memo<CornerBoxProps>(({
position,
size,
borderWidth,
borderColor,
opacity,
offset,
}) => {
const positionStyles: Record<string, BoxProps> = {
'top-left': {
top: `${offset}px`,
left: `${offset}px`,
borderTop: `${borderWidth}px solid`,
borderLeft: `${borderWidth}px solid`,
},
'top-right': {
top: `${offset}px`,
right: `${offset}px`,
borderTop: `${borderWidth}px solid`,
borderRight: `${borderWidth}px solid`,
},
'bottom-left': {
bottom: `${offset}px`,
left: `${offset}px`,
borderBottom: `${borderWidth}px solid`,
borderLeft: `${borderWidth}px solid`,
},
'bottom-right': {
bottom: `${offset}px`,
right: `${offset}px`,
borderBottom: `${borderWidth}px solid`,
borderRight: `${borderWidth}px solid`,
},
};
return (
<Box
position="absolute"
w={`${size}px`}
h={`${size}px`}
borderColor={borderColor}
opacity={opacity}
pointerEvents="none"
{...positionStyles[position]}
/>
);
});
CornerBox.displayName = 'CornerBox';
/**
* FUI 角落装饰组件
* 在容器四角添加科幻风格的装饰边框
*
* @example
* ```tsx
* <Box position="relative">
* <FuiCorners />
* {children}
* </Box>
* ```
*/
const FuiCorners = memo<FuiCornersProps>(({
size = 16,
borderWidth = 2,
borderColor = 'rgba(212, 175, 55, 0.4)',
opacity = 0.6,
offset = 12,
}) => {
const positions: CornerBoxProps['position'][] = [
'top-left',
'top-right',
'bottom-left',
'bottom-right',
];
return (
<>
{positions.map((position) => (
<CornerBox
key={position}
position={position}
size={size}
borderWidth={borderWidth}
borderColor={borderColor}
opacity={opacity}
offset={offset}
/>
))}
</>
);
});
FuiCorners.displayName = 'FuiCorners';
export default FuiCorners;

View File

@@ -0,0 +1,12 @@
/**
* FUI (Futuristic UI) 组件集合
* Ash Thorp 风格的科幻 UI 组件
*/
export { default as FuiCorners } from './FuiCorners';
export { default as FuiContainer } from './FuiContainer';
export { default as AmbientGlow } from './AmbientGlow';
export type { FuiCornersProps } from './FuiCorners';
export type { FuiContainerProps } from './FuiContainer';
export type { AmbientGlowProps } from './AmbientGlow';

View File

@@ -0,0 +1,80 @@
/**
* 动态设置网页标题的 Hook
*/
import { useEffect } from 'react';
export interface UseDocumentTitleOptions {
/** 基础标题(默认:价值前沿) */
baseTitle?: string;
/** 是否在组件卸载时恢复基础标题 */
restoreOnUnmount?: boolean;
}
/**
* 动态设置网页标题
*
* @param title - 要显示的标题(会与 baseTitle 组合)
* @param options - 配置选项
*
* @example
* ```tsx
* // 基础用法
* useDocumentTitle('我的页面');
* // 结果: "我的页面 - 价值前沿"
*
* // 股票页面
* useDocumentTitle(stockName ? `${stockName}(${stockCode})` : stockCode);
* // 结果: "平安银行(000001) - 价值前沿"
*
* // 自定义基础标题
* useDocumentTitle('Dashboard', { baseTitle: 'My App' });
* // 结果: "Dashboard - My App"
* ```
*/
export const useDocumentTitle = (
title?: string | null,
options: UseDocumentTitleOptions = {}
): void => {
const { baseTitle = '价值前沿', restoreOnUnmount = true } = options;
useEffect(() => {
if (title) {
document.title = `${title} - ${baseTitle}`;
} else {
document.title = baseTitle;
}
// 组件卸载时恢复默认标题
if (restoreOnUnmount) {
return () => {
document.title = baseTitle;
};
}
}, [title, baseTitle, restoreOnUnmount]);
};
/**
* 股票页面专用的标题 Hook
*
* @param stockCode - 股票代码
* @param stockName - 股票名称(可选)
*
* @example
* ```tsx
* useStockDocumentTitle('000001', '平安银行');
* // 结果: "平安银行(000001) - 价值前沿"
* ```
*/
export const useStockDocumentTitle = (
stockCode: string,
stockName?: string | null
): void => {
const title = stockName
? `${stockName}(${stockCode})`
: stockCode || null;
useDocumentTitle(title);
};
export default useDocumentTitle;

View File

@@ -1,7 +1,7 @@
// src/views/Company/components/CompanyOverview/components/shareholder/ConcentrationCard.tsx
// 股权集中度卡片组件
import React, { useMemo, useRef, useEffect } from "react";
import React, { useMemo, useRef, useEffect, memo } from "react";
import {
Box,
VStack,
@@ -233,4 +233,4 @@ const ConcentrationCard: React.FC<ConcentrationCardProps> = ({ concentration = [
);
};
export default ConcentrationCard;
export default memo(ConcentrationCard);

View File

@@ -1,7 +1,7 @@
// src/views/Company/components/CompanyOverview/components/shareholder/ShareholdersTable.tsx
// 股东表格组件(合并版)- 支持十大股东和十大流通股东
import React, { useMemo } from "react";
import React, { useMemo, memo } from "react";
import { Box, HStack, Heading, Badge, Icon, useBreakpointValue } from "@chakra-ui/react";
import { Table, Tag, Tooltip, ConfigProvider } from "antd";
import type { ColumnsType } from "antd/es/table";
@@ -225,4 +225,4 @@ const ShareholdersTable: React.FC<ShareholdersTableProps> = ({
);
};
export default ShareholdersTable;
export default memo(ShareholdersTable);

View File

@@ -189,9 +189,8 @@ const DeepAnalysis = ({ stockCode }) => {
// 重置为默认 Tab 并加载数据
setActiveTab("strategy");
// 加载默认 Tab 的数据(战略分析需要 comprehensive 和 industryRank
// 加载默认 Tab 的核心数据comprehensive),其他数据按需加载
loadApiData("comprehensive");
loadApiData("industryRank");
}
}, [stockCode, loadApiData]);

View File

@@ -1,5 +1,5 @@
// 指标卡片组件 - FUI 科幻风格
import React from 'react';
import React, { memo } from 'react';
import { Box, VStack } from '@chakra-ui/react';
import { DarkGoldCard, CardTitle, MetricValue } from './atoms';
import { darkGoldTheme } from '../../constants';
@@ -125,4 +125,4 @@ const MetricCard: React.FC<MetricCardProps> = ({
);
};
export default MetricCard;
export default memo(MetricCard);

View File

@@ -1,5 +1,5 @@
// 股票信息卡片组件4列布局版本- FUI 科幻风格
import React from 'react';
import React, { memo } from 'react';
import { Box, HStack, VStack, Text, Icon, Badge } from '@chakra-ui/react';
import { TrendingUp, TrendingDown, Activity } from 'lucide-react';
import { DarkGoldCard } from './atoms';
@@ -206,4 +206,4 @@ const StockHeaderCard: React.FC<StockHeaderCardProps> = ({
);
};
export default StockHeaderCard;
export default memo(StockHeaderCard);

View File

@@ -1,5 +1,5 @@
// StockSummaryCard 主组件
import React from 'react';
import React, { memo } from 'react';
import { SimpleGrid, HStack, Text, VStack } from '@chakra-ui/react';
import { Flame, Coins, DollarSign, Shield } from 'lucide-react';
import StockHeaderCard from './StockHeaderCard';
@@ -111,4 +111,4 @@ const StockSummaryCard: React.FC<StockSummaryCardProps> = ({ summary }) => {
);
};
export default StockSummaryCard;
export default memo(StockSummaryCard);

View File

@@ -16,6 +16,8 @@ import './theme/fui-animations.css';
import { useSearchParams } from 'react-router-dom';
import { Box } from '@chakra-ui/react';
import SubTabContainer from '@components/SubTabContainer';
import { FuiContainer, AmbientGlow } from '@components/FUI';
import { useStockDocumentTitle } from '@hooks/useDocumentTitle';
import { useCompanyEvents } from './hooks/useCompanyEvents';
import { useCompanyData } from './hooks/useCompanyData';
import CompanyHeader from './components/CompanyHeader';
@@ -52,63 +54,8 @@ const CompanyContent = memo<CompanyContentProps>(({
/>
</Box>
{/* Tab 内容区 */}
<Box
position="relative"
bg={`linear-gradient(145deg, rgba(26, 26, 46, 0.95) 0%, rgba(15, 15, 26, 0.98) 100%)`}
borderRadius="xl"
border="1px solid"
borderColor="rgba(212, 175, 55, 0.15)"
overflow="hidden"
backdropFilter="blur(16px)"
boxShadow="0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.05)"
>
{/* 角落装饰 - FUI 风格 */}
<Box
position="absolute"
top="12px"
left="12px"
w="16px"
h="16px"
borderTop="2px solid"
borderLeft="2px solid"
borderColor="rgba(212, 175, 55, 0.4)"
opacity={0.6}
/>
<Box
position="absolute"
top="12px"
right="12px"
w="16px"
h="16px"
borderTop="2px solid"
borderRight="2px solid"
borderColor="rgba(212, 175, 55, 0.4)"
opacity={0.6}
/>
<Box
position="absolute"
bottom="12px"
left="12px"
w="16px"
h="16px"
borderBottom="2px solid"
borderLeft="2px solid"
borderColor="rgba(212, 175, 55, 0.4)"
opacity={0.6}
/>
<Box
position="absolute"
bottom="12px"
right="12px"
w="16px"
h="16px"
borderBottom="2px solid"
borderRight="2px solid"
borderColor="rgba(212, 175, 55, 0.4)"
opacity={0.6}
/>
{/* Tab 内容区 - 使用 FuiContainer */}
<FuiContainer variant="default">
<SubTabContainer
tabs={TAB_CONFIG}
componentProps={{ stockCode }}
@@ -117,34 +64,12 @@ const CompanyContent = memo<CompanyContentProps>(({
contentPadding={0}
isLazy={true}
/>
</Box>
</FuiContainer>
</Box>
));
CompanyContent.displayName = 'CompanyContent';
// ============================================
// 网页标题 Hook
// ============================================
const useDocumentTitle = (stockCode: string, stockName?: string) => {
useEffect(() => {
const baseTitle = '价值前沿';
if (stockName) {
document.title = `${stockName}(${stockCode}) - ${baseTitle}`;
} else if (stockCode) {
document.title = `${stockCode} - ${baseTitle}`;
} else {
document.title = baseTitle;
}
// 组件卸载时恢复默认标题
return () => {
document.title = baseTitle;
};
}, [stockCode, stockName]);
};
// ============================================
// 主页面组件
// ============================================
@@ -175,7 +100,7 @@ const CompanyIndex: React.FC = () => {
const { trackStockSearched, trackTabChanged, trackWatchlistAdded, trackWatchlistRemoved } = companyEvents;
// 设置网页标题
useDocumentTitle(stockCode, stockInfo?.stock_name);
useStockDocumentTitle(stockCode, stockInfo?.stock_name);
// 股票代码变化追踪
useEffect(() => {
@@ -220,20 +145,7 @@ const CompanyIndex: React.FC = () => {
overflow="hidden"
>
{/* 全局环境光效果 - James Turrell 风格 */}
<Box
position="fixed"
top={0}
left={0}
right={0}
bottom={0}
pointerEvents="none"
zIndex={0}
bg={`
radial-gradient(ellipse 100% 80% at 50% -20%, rgba(212, 175, 55, 0.08), transparent 50%),
radial-gradient(ellipse 60% 50% at 0% 50%, rgba(100, 200, 255, 0.04), transparent 40%),
radial-gradient(ellipse 60% 50% at 100% 50%, rgba(255, 200, 100, 0.04), transparent 40%)
`}
/>
<AmbientGlow variant="default" />
{/* 顶部搜索栏 */}
<Box position="relative" zIndex={1}>