import { join } from 'node:path';
import { defineConfig } from '@umijs/max';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
import routes from './routes';
const { REACT_APP_ENV = 'dev' } = process.env;
const PUBLIC_PATH: string = '/';
export default defineConfig({
hash: true,
publicPath: PUBLIC_PATH,
routes,
ignoreMomentLocale: true,
proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
fastRefresh: true,
model: {},
initialState: {},
title: 'Ant Design Pro',
layout: {
locale: false,
...defaultSettings,
// ==================== 在这里添加多标签页配置 ====================
multiTabs: { // 这里修改为复数形式
enabled: true, // 启用多标签页
keepAlive: true, // 启用页面缓存
multiTabMode: 'chrome', // 标签页样式
fixedMultiTab: true, // 固定标签头
},
},
moment2dayjs: {
preset: 'antd',
plugins: ['duration'],
},
locale: {
default: 'zh-CN',
antd: true,
baseNavigator: true,
},
antd: {
appConfig: {},
configProvider: {
theme: {
cssVar: true,
token: {
fontFamily: 'AlibabaSans, sans-serif',
},
},
},
},
request: {},
access: {},
headScripts: [
{ src: join(PUBLIC_PATH, 'scripts/loading.js'), async: true },
],
presets: ['umi-presets-pro'],
openAPI: [
{
requestLibPath: "import { request } from '@umijs/max'",
schemaPath: join(__dirname, 'oneapi.json'),
mock: false,
},
{
requestLibPath: "import { request } from '@umijs/max'",
schemaPath:
'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
projectName: 'swagger',
},
],
mock: {
include: ['mock/**/*', 'src/pages/**/_mock.ts'],
},
mako: {},
esbuildMinifyIIFE: true,
requestRecord: {},
exportStatic: {},
history: { type: 'hash' },
});
import type { ProLayoutProps } from '@ant-design/pro-components';
/**
* @name
*/
const Settings: ProLayoutProps & {
pwa?: boolean;
logo?: string;
} = {
navTheme: 'light',
// 拂晓蓝
colorPrimary: '#1890ff',
layout: 'mix',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
title: '框架',
pwa: true,
logo: './logo.svg', // 项目logo
iconfontUrl: '',
token: {
// 参见ts声明,demo 见文档,通过token 修改样式
//https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F
},
// 侧边栏宽度
siderWidth: 220,
};
export default Settings;
/**
* @name umi 的路由配置
* @description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置
* @param path path 只支持两种占位符配置,第一种是动态参数 :id 的形式,第二种是 * 通配符,通配符只能出现路由字符串的最后。
* @param component 配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。
* @param routes 配置子路由,通常在需要为多个路径增加 layout 组件时使用。
* @param redirect 配置路由跳转
* @param wrappers 配置路由组件的包装组件,通过包装组件可以为当前的路由组件组合进更多的功能。 比如,可以用于路由级别的权限校验
* @param name 配置路由的标题,默认读取国际化文件 menu.ts 中 menu.xxxx 的值,如配置 name 为 login,则读取 menu.ts 中 menu.login 的取值作为标题
* @param icon 配置路由的图标,取值参考 https://ant.design/components/icon-cn, 注意去除风格后缀和大小写,如想要配置图标为 <StepBackwardOutlined /> 则取值应为 stepBackward 或 StepBackward,如想要配置图标为 <UserOutlined /> 则取值应为 user 或者 User
* @doc https://umijs.org/docs/guides/routes
*/
export default [
{
path: '/user',
layout: false,
routes: [
{
name: 'login',
path: '/user/login',
component: './user/login',
},
],
},
{
path: '/company',
name: '组织管理',
icon: 'smile',
component: './company',
access: 'hasRoute',
// 移除冗余的 multiTab 配置
},
{
path: '/permissions',
name: '权限管理',
icon: 'lock',
access: 'hasRoute',
// 移除冗余的 multiTab 配置
routes: [
{
path: 'admin',
name: '账户管理',
component: './permissions/account',
access: 'hasRoute',
// 移除冗余的 multiTab 配置
},
{
path: 'menu',
name: '菜单管理',
component: './permissions/menu',
access: 'hasRoute',
// 移除冗余的 multiTab 配置
},
{
path: 'role',
name: '角色管理',
component: './permissions/role',
access: 'hasRoute',
// 移除冗余的 multiTab 配置
},
],
},
{
path: '/staff',
name: '员工管理',
icon: 'smile',
component: './staff',
access: 'hasRoute',
// 移除冗余的 multiTab 配置
},
{
path: '/',
redirect: '/staff',
},
{
path: '*',
layout: false,
component: './404',
},
];
import { LinkOutlined, FullscreenOutlined, FullscreenExitOutlined, RedoOutlined, LoadingOutlined } from '@ant-design/icons';
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
import { SettingDrawer } from '@ant-design/pro-components';
import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
import { history, Link, useLocation } from '@umijs/max';
import React, { useState, useEffect, useCallback } from 'react';
import {
AvatarDropdown,
AvatarName,
Footer,
Question,
SelectLang,
} from '@/components';
import { currentUser as queryCurrentUser } from '@/services/ant-design-pro/api';
import defaultSettings from '../config/defaultSettings';
import { errorConfig } from './requestErrorConfig';
import '@ant-design/v5-patch-for-react-19';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
// 全屏钩子保持不变
const useFullscreen = () => {
const [isFullscreen, setIsFullscreen] = useState(false);
const checkFullscreen = useCallback(() => {
setIsFullscreen(
!!(document as any).fullscreenElement ||
!!(document as any).webkitFullscreenElement ||
!!(document as any).mozFullScreenElement ||
!!(document as any).msFullscreenElement
);
}, []);
const toggleFullscreen = useCallback(() => {
if (isFullscreen) {
if ((document as any).exitFullscreen) (document as any).exitFullscreen();
else if ((document as any).webkitExitFullscreen) (document as any).webkitExitFullscreen();
else if ((document as any).mozCancelFullScreen) (document as any).mozCancelFullScreen();
else if ((document as any).msExitFullscreen) (document as any).msExitFullscreen();
} else {
const docEl = document.documentElement;
if ((docEl as any).requestFullscreen) (docEl as any).requestFullscreen();
else if ((docEl as any).webkitRequestFullscreen) (docEl as any).webkitRequestFullscreen();
else if ((docEl as any).mozRequestFullScreen) (docEl as any).mozRequestFullScreen();
else if ((docEl as any).msRequestFullscreen) (docEl as any).msRequestFullscreen();
}
}, [isFullscreen]);
useEffect(() => {
const fullscreenChangeHandler = () => checkFullscreen();
document.addEventListener('fullscreenchange', fullscreenChangeHandler);
document.addEventListener('webkitfullscreenchange', fullscreenChangeHandler);
document.addEventListener('mozfullscreenchange', fullscreenChangeHandler);
document.addEventListener('MSFullscreenChange', fullscreenChangeHandler);
return () => {
document.removeEventListener('fullscreenchange', fullscreenChangeHandler);
document.removeEventListener('webkitfullscreenchange', fullscreenChangeHandler);
document.removeEventListener('mozfullscreenchange', fullscreenChangeHandler);
document.removeEventListener('MSFullscreenChange', fullscreenChangeHandler);
};
}, [checkFullscreen]);
return { isFullscreen, toggleFullscreen };
};
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: API.CurrentUser;
loading?: boolean;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
const fetchUserInfo = async () => {
try {
const msg = await queryCurrentUser({
skipErrorHandler: true,
});
return msg.data;
} catch (_error) {
history.push(loginPath);
}
return undefined;
};
const { location } = history;
if (
![loginPath, '/user/register', '/user/register-result'].includes(
location.pathname,
)
) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
return {
fetchUserInfo,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
export const layout: RunTimeLayoutConfig = ({
initialState,
setInitialState,
}) => {
const { isFullscreen, toggleFullscreen } = useFullscreen();
const [routeKey, setRouteKey] = useState(Date.now());
const [isRefreshing, setIsRefreshing] = useState(false);
const location = useLocation();
// 刷新方法
const refreshPage = useCallback(() => {
setIsRefreshing(true);
setRouteKey(Date.now());
setTimeout(() => setIsRefreshing(false), 300);
}, []);
return {
// 全局开启多标签功能(复数形式,与ProLayout属性一致)
multiTabs: true,
actionsRender: () => [
// 全屏按钮
isFullscreen ? (
<FullscreenExitOutlined
key="exit-fullscreen"
onClick={toggleFullscreen}
title="退出全屏"
style={{ cursor: 'pointer', marginRight: 8 }}
/>
) : (
<FullscreenOutlined
key="fullscreen"
onClick={toggleFullscreen}
title="全屏显示"
style={{ cursor: 'pointer', marginRight: 8 }}
/>
),
// 刷新按钮
isRefreshing ? (
<LoadingOutlined key="refresh-loading"
spin
style={{ fontSize: '16px', cursor: 'pointer', marginRight: 8 }}
title="刷新中..."
onClick={refreshPage}
/>
) : (
<RedoOutlined
key="refresh"
style={{ fontSize: '16px', cursor: 'pointer', marginRight: 8 }}
title="刷新页面"
onClick={refreshPage}
/>
),
],
avatarProps: {
src: initialState?.currentUser?.avatar,
title: <AvatarName />,
render: (_, avatarChildren) => {
return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
},
},
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
menuHeaderRender: undefined,
childrenRender: (children) => {
return (
<div key={routeKey}>
{children}
</div>
);
},
...initialState?.settings,
};
};
export const request: RequestConfig = { ...errorConfig };怎么修复多标签页
最新发布