恢复到没有加载效果的 除了效果别的还都是新的

This commit is contained in:
ylj20011123 2025-09-16 09:41:45 +08:00
parent 02b1425689
commit b07d192d20
14 changed files with 88 additions and 407 deletions

View File

@ -20,9 +20,9 @@ export default defineConfig({
hash: true,
mock: false,
antd: {},
// dva: {
// hmr: true
// },
dva: {
hmr: true
},
history: {
type: REACT_APP_ENV === 'dev' ? "hash" : "memory",
// type: "hash"
@ -36,7 +36,7 @@ export default defineConfig({
baseNavigator: true,
},
dynamicImport: {
loading: '@/components/SmartLoading/index',
loading: '@/components/PageLoading/index',
},
targets: {
ie: 11,

View File

@ -7,7 +7,6 @@
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import globalState from './globalState';
import { routePreloader } from './utils/routePreloader';
// import { getMicroAppRouteComponent } from 'umi';
@ -30,22 +29,6 @@ export const qiankun = {
}
// 应用启动时的初始化配置
export async function getInitialState() {
// 预加载关键路由以优化首屏加载
setTimeout(() => {
routePreloader.preloadCriticalRoutes().then(() => {
console.log('关键路由预加载完成');
}).catch(error => {
console.warn('关键路由预加载失败:', error);
});
}, 1000); // 延迟1秒后开始预加载避免影响首屏渲染
return {
preloadEnabled: true,
};
}
// export const patchRoutes = ({ routes }: any) => {
// console.info('routes', routes);
// routes[0].routes[1].routes[0].routes.forEach((item: any, index: number) => {

View File

@ -1,7 +0,0 @@
.page-loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100%;
}

View File

@ -1,13 +1,5 @@
// import { PageLoading } from '@ant-design/pro-layout';
// // loading components from code split
// // https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
// export default PageLoading;
import React from 'react';
import SkeletonLoading from '../SkeletonLoading';
const PageLoading: React.FC = () => {
return <SkeletonLoading type="page" />;
};
import { PageLoading } from '@ant-design/pro-layout';
// loading components from code split
// https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
export default PageLoading;

View File

@ -3,7 +3,13 @@
// 默认不播放动画只有带animate类时才播放
&.animate {
animation: pageEnter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
will-change: opacity, transform;
// 移除will-change避免HMR时样式冲突
// will-change: opacity, transform;
}
// 动画结束后重置will-change避免持续占用GPU资源
&.animate:not(:hover):not(:focus) {
animation-fill-mode: forwards;
}
}
@ -11,12 +17,12 @@
from {
opacity: 0;
transform: translateY(30px) scale(0.96);
filter: blur(4px);
// filter: blur(4px); // 注释掉filter属性可能导致HMR问题
}
to {
opacity: 1;
transform: translateY(0) scale(1);
filter: blur(0);
// filter: blur(0); // 注释掉filter属性
}
}

View File

@ -41,7 +41,7 @@ const PageTransition: React.FC<PageTransitionProps> = ({
);
};
// 简化版本 - 仅使用CSS动画
// 简化版本 - 禁用所有动画
export const SimplePageTransition: React.FC<{
children: React.ReactNode;
className?: string;
@ -50,20 +50,25 @@ export const SimplePageTransition: React.FC<{
}> = ({
children,
className = '',
enableAnimation = true,
enableAnimation = false, // 强制禁用动画
onAnimationEnd,
}) => {
// 立即执行回调,不等待动画
const handleAnimationEnd = () => {
if (enableAnimation && onAnimationEnd) {
if (onAnimationEnd) {
onAnimationEnd();
}
};
// 立即执行动画结束回调
React.useEffect(() => {
if (onAnimationEnd) {
onAnimationEnd();
}
}, [onAnimationEnd]);
return (
<div
className={`simple-page-transition ${enableAnimation ? 'animate' : ''} ${className}`}
onAnimationEnd={handleAnimationEnd}
>
<div className={`simple-page-transition ${className}`}>
{children}
</div>
);

View File

@ -1,5 +1,4 @@
// @import '~antd/es/style/themes/default.less';
@import '~antd/dist/antd.less';
@import '~antd/es/style/themes/default.less';
html,
body,
@ -25,51 +24,6 @@ body {
-moz-osx-font-smoothing: grayscale;
}
// 骨架屏优化样式
.ant-skeleton {
.ant-skeleton-content {
.ant-skeleton-title,
.ant-skeleton-paragraph>li {
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 50%, #f2f2f2 75%);
background-size: 200% 100%;
animation: loading 1.4s ease-in-out infinite;
}
}
.ant-skeleton-avatar {
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 50%, #f2f2f2 75%);
background-size: 200% 100%;
animation: loading 1.4s ease-in-out infinite;
}
.ant-skeleton-input,
.ant-skeleton-button {
background: linear-gradient(90deg, #f2f2f2 25%, #e6e6e6 50%, #f2f2f2 75%);
background-size: 200% 100%;
animation: loading 1.4s ease-in-out infinite;
}
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
// 页面切换动画优化
.ant-layout-content {
transition: opacity 0.2s ease-in-out;
&.loading {
opacity: 0.7;
}
}
ul,
ol {
list-style: none;
@ -102,17 +56,3 @@ ol {
min-height: 100vh;
}
}
// 页面过渡优化
* {
box-sizing: border-box;
}
// 增强页面切换体验
.ant-tabs-content-holder {
overflow: hidden;
}
.ant-tabs-tabpane {
outline: none;
}

View File

@ -1,5 +1,4 @@
@import '~antd/es/style/themes/default.less';
@import '../components/PageTransition/index.less';
.ant-pro-global-header {
box-shadow: none !important;

View File

@ -28,9 +28,6 @@ import * as Icon from '@ant-design/icons'
import IconFont from '@/components/IconFont';
import type { CurrentUser } from '@/models/user'
import session from '@/utils/session';
import { SimplePageTransition } from '@/components/PageTransition';
import TabVirtualizer from '@/components/TabVirtualizer';
import { tabPerformanceManager, DEFAULT_CONFIG } from '@/utils/tabPerformanceManager';
import upMenu from '../assets/tab/upMenu.png'
import { getFieldEnum, getFieldEnumTravel, getFieldEnumTree, getFieldGetFieEnumList, getTravelFieldEnumTree, handleGetFieldEnumTreeTravel, handleGetNestingFIELDENUMList } from "@/services/options";
import { handleGetServerpartTree } from '@/pages/basicManage/serverpartAssets/service';
@ -95,10 +92,7 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
const [activeKey, setActiveKey] = useState<string>(location?.pathname || '/')
const [animatedPages, setAnimatedPages] = useState<Set<string>>(new Set());
const [reloadingTabs, setReloadingTabs] = useState<Set<string>>(new Set());
const menuDataRef = useRef<MenuDataItem[]>([]);
const checkTimerRef = useRef<NodeJS.Timeout | null>(null);
const { formatMessage } = useIntl();
useEffect(() => {
@ -146,57 +140,13 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
// 改变panes
const handleTabsPanes = (payload: any): void => {
if (dispatch) {
// 检查是否为新页面(从未打开过的页面才需要动画)
const isNewPage = !tabsPanes.some(tab => tab.path === payload.path);
if (isNewPage) {
// 标记新页面需要播放动画
setAnimatedPages(prev => new Set(prev).add(payload.path));
}
// 更新访问时间
const updatedPayload = {
...payload,
lastAccessTime: Date.now(),
isLoaded: true,
isLoading: false,
};
dispatch({
type: 'global/changeTabsRoutes',
payload: { data: updatedPayload, action: 'add' },
payload: { data: payload, action: 'add' },
});
}
};
// 处理标签页重新加载
const handleTabReload = (tabPath: string): void => {
if (dispatch) {
// 标记为加载中
setReloadingTabs(prev => new Set(prev).add(tabPath));
// 重新加载时也要播放动画
setAnimatedPages(prev => new Set(prev).add(tabPath));
dispatch({
type: 'global/reloadTab',
payload: { tabPath },
});
// 模拟加载过程
setTimeout(() => {
setReloadingTabs(prev => {
const newSet = new Set(prev);
newSet.delete(tabPath);
return newSet;
});
// 切换到该标签页
history.push(tabPath);
setActiveKey(tabPath);
}, 800); // 800ms的加载时间
}
};
// 关闭当前标签
const handleEdit = (targetKey: string | React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent<Element>, action: "add" | "remove"): void => {
@ -210,13 +160,6 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
history.push(nextkey || '/')
setActiveKey(nextkey)
// 从动画记录中移除关闭的页面
setAnimatedPages(prev => {
const newSet = new Set(prev);
newSet.delete(targetKey as string);
return newSet;
});
// 缓存路由栈数据
dispatch({
type: 'global/changeTabsRoutes',
@ -927,31 +870,6 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
})
}
// 启动标签页性能管理
useEffect(() => {
// 启动定时检查
checkTimerRef.current = setInterval(() => {
if (dispatch && tabsPanes.length > 0) {
const tabsToUnload = tabPerformanceManager.getTabsToUnload(tabsPanes, activeKey);
if (tabsToUnload.length > 0) {
// 批量卸载标签页
tabsToUnload.forEach(tabPath => {
dispatch({
type: 'global/unloadTab',
payload: { tabPath },
});
});
}
}
}, DEFAULT_CONFIG.checkIntervalMinutes * 60 * 1000);
return () => {
if (checkTimerRef.current) {
clearInterval(checkTimerRef.current);
}
};
}, [dispatch, tabsPanes, activeKey]);
// 显示就调用
useEffect(() => {
handleGetAllFieldEnum()
@ -1064,14 +982,6 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
onChange={(value) => {
history.push(value)
setActiveKey(value)
// 更新标签页访问时间
if (dispatch) {
dispatch({
type: 'global/updateTabAccessTime',
payload: { tabPath: value },
});
}
}}
activeKey={activeKey}
onEdit={handleEdit}
@ -1096,13 +1006,6 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
break;
}
// 从动画记录中移除关闭的页面
setAnimatedPages(prev => {
const newSet = new Set(prev);
closeTabKeys.forEach(key => newSet.delete(key));
return newSet;
});
dispatch({
type: 'global/changeTabsRoutes',
payload: { data: closeTabKeys, action: 'remove' },
@ -1132,53 +1035,17 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
}
moreIcon={<DoubleRightOutlined style={{ color: "#7b828c" }} />}
>
{tabsPanes && tabsPanes.map((item: tabsRoute) => {
const shouldAnimate = animatedPages.has(item.path);
const isActive = activeKey === item.path;
const isLoaded = item.isLoaded !== false; // 默认为已加载状态
const isLoading = reloadingTabs.has(item.path) || item.isLoading === true;
return (
{tabsPanes && tabsPanes.map((item: tabsRoute) =>
<TabPane
tab={item.title}
key={item?.path}
style={{ padding: 24, paddingTop: 0 }}
>
{/* 如果页面未加载或正在加载显示TabVirtualizer */}
{(!isLoaded || isLoading) ? (
<TabVirtualizer
tabKey={item.path}
isActive={isActive}
isLoaded={isLoaded}
isLoading={isLoading}
lastAccessTime={item.lastAccessTime}
onReload={handleTabReload}
>
{/* 空内容TabVirtualizer会处理显示 */}
</TabVirtualizer>
) : (
/* 页面已加载,直接显示内容,根据需要播放动画 */
<SimplePageTransition
enableAnimation={shouldAnimate}
onAnimationEnd={() => {
// 动画播放完成后,移除动画标记
setAnimatedPages(prev => {
const newSet = new Set(prev);
newSet.delete(item.path);
return newSet;
});
}}
>
<Suspense fallback={null}>
tab={item.title} key={item?.path}
style={{ padding: 24, paddingTop: 0 }}>
<Suspense fallback={<div>Loading...</div>}>
<Authorized authority={authorized!.authority} noMatch={noMatch}>
{item.children}
</Authorized>
</Suspense>
</SimplePageTransition>
)}
</TabPane>
);
})}
</TabPane>)
}
</Tabs>
{/* 不要标签栏删除tabs的代码使用下方的代码就可以 */}
{/* <Authorized authority={authorized!.authority} noMatch={noMatch}>

View File

@ -15,101 +15,68 @@ type SecurityLayoutProps = {
type SecurityLayoutState = {
isReady: boolean;
shouldShowLoading: boolean;
initialLoadComplete: boolean;
};
class SecurityLayout extends React.Component<SecurityLayoutProps, SecurityLayoutState> {
state: SecurityLayoutState = {
isReady: false,
shouldShowLoading: false,
initialLoadComplete: false,
};
private loadingTimer?: NodeJS.Timeout;
componentDidMount() {
const { location } = history;
const { location } = history
const { dispatch } = this.props;
// 检查是否有缓存的用户信息,避免不必要的加载状态
const cachedUser = session.get("currentUser");
// 设置防闪烁定时器只有加载时间超过300ms才显示loading
this.loadingTimer = setTimeout(() => {
if (!this.state.isReady && !this.state.initialLoadComplete) {
this.setState({
shouldShowLoading: true,
});
}
}, 300);
if (dispatch) {
dispatch({
type: 'user/fetch',
callback: (user) => {
// 清除定时器
if (this.loadingTimer) {
clearTimeout(this.loadingTimer);
}
type: 'user/fetch', callback: (user) => {
if (user.code && location.pathname !== '/user/login') {
history.push('/user/login');
return;
return
}
console.log('secur')
dispatch({
type: 'global/getMenuData',
payload: user.ID,
callback: (menu) => {
type: 'global/getMenuData', payload: user.ID, callback: (menu) => {
if (menu) {
this.setState({
isReady: true,
shouldShowLoading: false,
initialLoadComplete: true,
});
}
}
});
})
}
});
})
} else {
// 清除定时器
if (this.loadingTimer) {
clearTimeout(this.loadingTimer);
}
this.setState({
isReady: true,
shouldShowLoading: false,
initialLoadComplete: true,
});
}
}
componentWillUnmount() {
if (this.loadingTimer) {
clearTimeout(this.loadingTimer);
}
}
render() {
const { isReady, shouldShowLoading, initialLoadComplete } = this.state;
const { children, currentUser } = this.props;
const { isReady } = this.state;
const { children, loading, currentUser } = this.props;
// const { location } = history;
// 用户认证规则
// You can replace it to your authentication rule (such as check token exists)
// You can replace it with your own login authentication rules (such as judging whether the token exists)
const isLogin = currentUser && currentUser.ID;
// 如果初始化未完成且需要显示加载状态,才显示骨架屏
if (!isReady && shouldShowLoading) {
if ((!isLogin && loading) || !isReady) {
return <PageLoading />;
}
// 如果还在初始化过程中但不需要显示loading返回null避免闪烁
if (!isReady && !shouldShowLoading) {
return null;
}
// if (!isLogin && location.pathname !== '/user/login') {
// history.push('/user/login');
// }
return children;
}
}

View File

@ -20,9 +20,6 @@ export type tabsRoute = {
path: string;
key: string;
children: any;
lastAccessTime?: number; // 最后访问时间
isLoaded?: boolean; // 是否已加载
isLoading?: boolean; // 是否正在加载
}
export type GlobalModelState = {
@ -42,9 +39,6 @@ export type GlobalModelType = {
changeNoticeReadState: Effect;
changeTabsRoutes: Effect;
getMenuData: Effect;
updateTabAccessTime: Effect;
unloadTab: Effect;
reloadTab: Effect;
};
reducers: {
changeLayoutCollapsed: Reducer<GlobalModelState>;
@ -185,26 +179,10 @@ const GlobalModel: GlobalModelType = {
const index = state.global.tabsRoutes.findIndex(n => n.path === data.path)
if (index === -1) { // 没缓存 则添加
return [...state.global.tabsRoutes, {
...data,
index: state.global.tabsRoutes.length,
lastAccessTime: data.lastAccessTime || Date.now(),
isLoaded: data.isLoaded !== false,
isLoading: data.isLoading || false,
}]
return [...state.global.tabsRoutes, { ...data, index: state.global.tabsRoutes.length }]
}
// 否则更新现有标签页信息
return state.global.tabsRoutes.map(tab =>
tab.path === data.path
? {
...tab,
...data,
lastAccessTime: data.lastAccessTime || Date.now(),
isLoaded: data.isLoaded !== false,
isLoading: data.isLoading || false,
}
: tab
);
// 否则不操作
return [...state.global.tabsRoutes]
}
if (payload.action === 'removeAll') {
return []
@ -221,57 +199,8 @@ const GlobalModel: GlobalModelType = {
type: 'saveTabsRoutes',
payload: tabsRoutes,
});
},
// 更新标签页访问时间
* updateTabAccessTime({ payload }, { put, select }) {
const { tabPath } = payload;
const tabsRoutes: tabsRoute[] = yield select((state: ConnectState) =>
state.global.tabsRoutes.map(tab =>
tab.path === tabPath
? { ...tab, lastAccessTime: Date.now(), isLoaded: true, isLoading: false }
: tab
)
);
yield put({
type: 'saveTabsRoutes',
payload: tabsRoutes,
});
},
// 卸载标签页
* unloadTab({ payload }, { put, select }) {
const { tabPath } = payload;
const tabsRoutes: tabsRoute[] = yield select((state: ConnectState) =>
state.global.tabsRoutes.map(tab =>
tab.path === tabPath
? { ...tab, isLoaded: false, isLoading: false }
: tab
)
);
yield put({
type: 'saveTabsRoutes',
payload: tabsRoutes,
});
},
// 重新加载标签页
* reloadTab({ payload }, { put, select }) {
const { tabPath } = payload;
const tabsRoutes: tabsRoute[] = yield select((state: ConnectState) =>
state.global.tabsRoutes.map(tab =>
tab.path === tabPath
? { ...tab, isLoaded: true, isLoading: false, lastAccessTime: Date.now() }
: tab
)
);
yield put({
type: 'saveTabsRoutes',
payload: tabsRoutes,
});
}
},