107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
||
import { useLocation } from 'umi';
|
||
import SkeletonLoading from '../SkeletonLoading';
|
||
import { routePreloader } from '@/utils/routePreloader';
|
||
|
||
interface SmartLoadingProps {
|
||
fallback?: React.ReactNode;
|
||
enablePreload?: boolean;
|
||
}
|
||
|
||
/**
|
||
* 智能加载组件
|
||
* 根据路由类型自动选择合适的骨架屏,并支持路由预加载
|
||
*/
|
||
const SmartLoading: React.FC<SmartLoadingProps> = ({
|
||
fallback,
|
||
enablePreload = true
|
||
}) => {
|
||
const [loadingType, setLoadingType] = useState<'page' | 'table' | 'form' | 'card'>('page');
|
||
const [shouldShow, setShouldShow] = useState(false);
|
||
const [location, setLocation] = useState<any>(null);
|
||
|
||
// 安全获取location,避免在路由未准备好时出错
|
||
try {
|
||
const currentLocation = useLocation();
|
||
if (!location) {
|
||
setLocation(currentLocation);
|
||
}
|
||
} catch (error) {
|
||
// 如果useLocation失败,使用window.location作为fallback
|
||
if (!location && typeof window !== 'undefined') {
|
||
setLocation({ pathname: window.location.pathname });
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
// 对于页面切换,立即显示骨架屏,但对于初始加载延迟显示
|
||
const isInitialLoad = !location?.pathname || location.pathname === '/';
|
||
|
||
if (isInitialLoad) {
|
||
// 初始加载时延迟显示,避免刷新时闪烁
|
||
const timer = setTimeout(() => {
|
||
setShouldShow(true);
|
||
}, 200);
|
||
return () => clearTimeout(timer);
|
||
} else {
|
||
// 页面切换时立即显示,保持骨架屏效果
|
||
setShouldShow(true);
|
||
}
|
||
}, [location?.pathname]);
|
||
|
||
useEffect(() => {
|
||
if (!location?.pathname) return;
|
||
|
||
// 根据路径判断页面类型,选择合适的骨架屏
|
||
const path = location.pathname;
|
||
|
||
if (path.includes('list') || path.includes('table')) {
|
||
setLoadingType('table');
|
||
} else if (path.includes('form') || path.includes('edit') || path.includes('add')) {
|
||
setLoadingType('form');
|
||
} else if (path.includes('card') || path.includes('dashboard')) {
|
||
setLoadingType('card');
|
||
} else {
|
||
setLoadingType('page');
|
||
}
|
||
|
||
// 预加载相关路由(降低优先级,避免影响主流程)
|
||
if (enablePreload) {
|
||
setTimeout(() => {
|
||
routePreloader.preloadBasedOnUserBehavior(path);
|
||
}, 500);
|
||
}
|
||
}, [location?.pathname, enablePreload]);
|
||
|
||
// 如果提供了自定义fallback,使用它
|
||
if (fallback) {
|
||
return <>{fallback}</>;
|
||
}
|
||
|
||
// 延迟显示,避免闪烁
|
||
if (!shouldShow) {
|
||
return null;
|
||
}
|
||
|
||
// 根据页面类型返回对应的骨架屏
|
||
return <SkeletonLoading type={loadingType} rows={getRowsByType(loadingType)} />;
|
||
};
|
||
|
||
/**
|
||
* 根据加载类型获取合适的行数
|
||
*/
|
||
function getRowsByType(type: string): number {
|
||
switch (type) {
|
||
case 'table':
|
||
return 8; // 表格通常显示更多行
|
||
case 'form':
|
||
return 5; // 表单通常5-6个字段
|
||
case 'card':
|
||
return 6; // 卡片网格通常6个
|
||
case 'page':
|
||
default:
|
||
return 4; // 默认4行
|
||
}
|
||
}
|
||
|
||
export default SmartLoading; |