Files
fe_supplier_frontend/src/layouts/BasicLayout.tsx

228 lines
6.7 KiB
TypeScript
Raw Normal View History

2025-07-01 14:18:23 +08:00
// src/layouts/BasicLayout.tsx
2025-08-04 16:06:23 +08:00
import React, { useState, useEffect } from 'react';
2025-07-01 14:18:23 +08:00
import ProLayout, { PageContainer } from '@ant-design/pro-layout';
2025-08-04 16:06:23 +08:00
import { Link, useLocation, useIntl } from 'umi';
2025-07-03 14:40:22 +08:00
import { connect } from 'dva';
2025-07-01 14:18:23 +08:00
import defaultSettings from '../../config/defaultSettings';
import routes from '../../config/router.config';
2025-07-01 14:18:23 +08:00
import { ConfigProvider, Breadcrumb } from 'antd';
import HeaderComponent from './Header';
import IconFont from '@/components/IconFont/IconFont';
2025-07-15 16:58:54 +08:00
import type { BreadcrumbState } from '@/models/breadcrumb';
2025-08-04 16:06:23 +08:00
import type { TabModelState } from '@/models/tab';
2025-07-01 14:18:23 +08:00
const MenuRender = (item: any, isSubMenu: boolean) => {
const intl = useIntl();
return (
<>
{isSubMenu ? (
<span className="ant-pro-menu-item">
<IconFont type={item.icon as string} />
<span className="ant-pro-menu-item-title">
{intl.formatMessage({ id: `menu.${item.name}` || '' })}
2025-07-01 14:18:23 +08:00
</span>
</span>
) : (
<Link className="ant-pro-menu-item" key={item.path} to={item.path || '/'} innerRef={null}>
<IconFont type={item.icon as string} />
<span className="ant-pro-menu-item-title">
{intl.formatMessage({ id: `menu.${item.name}` || '' })}
2025-07-01 14:18:23 +08:00
</span>
</Link>
)}
</>
);
};
2025-07-15 16:58:54 +08:00
const BreadcrumbRender = (
routeBreadcrumb: any,
intl: any,
history: any,
dynamicBreadcrumbName: string | null,
) => {
2025-07-03 14:40:22 +08:00
const breadcrumbRoutes = routeBreadcrumb?.routes;
2025-07-01 14:18:23 +08:00
return (
<Breadcrumb>
<Breadcrumb.Item
onClick={() => {
history.push('/');
}}
>
2025-07-15 16:58:54 +08:00
<span style={{ cursor: 'pointer' }}>{intl.formatMessage({ id: 'menu.首页' })}</span>
2025-07-01 14:18:23 +08:00
</Breadcrumb.Item>
2025-07-03 14:40:22 +08:00
{breadcrumbRoutes?.map((item: any, index: number) => {
// 判断是否是最后一个面包屑项且存在动态名称
2025-07-15 16:58:54 +08:00
const isLastItem = index === breadcrumbRoutes.length - 1;
const displayName =
isLastItem && dynamicBreadcrumbName
? dynamicBreadcrumbName
: intl.formatMessage({ id: `menu.${item.breadcrumbName}` || '' });
2025-07-03 14:40:22 +08:00
2025-07-15 16:58:54 +08:00
return <Breadcrumb.Item key={item.path}>{displayName}</Breadcrumb.Item>;
2025-07-01 14:18:23 +08:00
})}
</Breadcrumb>
);
};
2025-07-03 14:40:22 +08:00
interface BasicLayoutProps {
children: React.ReactNode;
breadcrumb: BreadcrumbState;
2025-08-04 16:06:23 +08:00
tab: TabModelState;
dispatch: any;
2025-07-03 14:40:22 +08:00
}
2025-08-04 16:06:23 +08:00
function convertMenuData(menus: any[]): any[] {
return menus.map(item => {
// 保留 icon 字段,如果没有就给默认
const icon = item.icon || undefined;
// 递归 children->routes
let routes;
if (item.routes && item.routes.length > 0) {
routes = convertMenuData(item.routes);
2025-08-04 16:06:23 +08:00
}
// 国际化优先找 menu.name否则直接显示 name
return {
...item,
icon,
routes, // prolayout 只认 routes
name: item.name, // 不要转 menu.xxxMenuRender 时才转
};
});
}
// 新增递归过滤函数
function filterMenusByLocalConfig(localMenus: any[], remoteMenus: any[]): any[] {
if (!Array.isArray(localMenus) || !Array.isArray(remoteMenus)) return [];
// remoteMenus 可能有 children 字段,也可能有 routes 字段
const remoteMap = new Map();
remoteMenus.forEach(r => {
if (r.path) remoteMap.set(r.path, r);
});
// filter 和 map 后的结果直接 return
localMenus
.filter(local => local.path && remoteMap.has(local.path))
.map(local => {
const remote = remoteMap.get(local.path);
// 兼容 children 或 routes 字段
const localChildren = local.children || local.routes;
const remoteChildren = remote.children || remote.routes;
let newChildren: any[] = [];
if (
localChildren && localChildren.length > 0 &&
remoteChildren && remoteChildren.length > 0
) {
newChildren = filterMenusByLocalConfig(localChildren, remoteChildren);
}
// 保留原有结构,只替换 children 或 routes
let node = { ...local };
if (local.children) {
node.children = newChildren;
} else if (local.routes) {
node.routes = newChildren;
}
return node;
});
return localMenus
}
2025-08-04 16:06:23 +08:00
2025-07-03 14:40:22 +08:00
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
2025-08-04 16:06:23 +08:00
const { children, tab, dispatch } = props;
2025-07-01 14:18:23 +08:00
const location = useLocation();
const intl = useIntl();
2025-08-04 16:06:23 +08:00
const handleTabChange = (key: string) => {
dispatch({
type: 'tab/switchTab',
payload: { key },
});
};
const handleTabEdit = (targetKey: any, action: string) => {
if (action === 'remove') {
dispatch({
type: 'tab/closeTab',
payload: { key: targetKey },
});
}
};
2025-07-15 09:07:43 +08:00
const [menuRoutes, setMenuRoutes] = useState<any[]>([]);
useEffect(() => {
const menuStr = sessionStorage.getItem('menuList');
if (menuStr) {
2025-08-04 16:06:23 +08:00
const menus = JSON.parse(menuStr);
const filteredMenus = filterMenusByLocalConfig(routes, menus);
setMenuRoutes(convertMenuData(filteredMenus));
2025-07-15 09:07:43 +08:00
}
}, []);
2025-08-04 16:06:23 +08:00
if (menuRoutes.length === 0) return null;
2025-07-01 14:18:23 +08:00
return (
<ConfigProvider>
2025-08-04 16:06:23 +08:00
<ProLayout
{...defaultSettings}
route={{ path: '/', routes: menuRoutes }}
breadcrumbRender={() => undefined}
subMenuItemRender={(menuItemProps, defaultDom) => {
return MenuRender(menuItemProps, true);
}}
menuItemRender={(item, dom) => {
return MenuRender(item, false);
}}
location={location}
fixSiderbar
layout="mix"
headerRender={() => {
return <HeaderComponent />;
}}
>
<PageContainer
ghost={true}
header={{
title: false,
breadcrumbRender: () => null,
// breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
// BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
2025-07-15 16:58:54 +08:00
}}
2025-08-04 16:06:23 +08:00
// 将tab.tabList转换为需要的格式添加国际化处理
tabList={tab.tabList.map((item) => ({
...item,
tab:
typeof item.tab === 'string'
? intl.formatMessage({ id: `menu.${item.tab}` })
: item.tab,
}))}
tabProps={{
type: 'editable-card',
hideAdd: true,
activeKey: tab.activeKey,
onChange: handleTabChange,
onEdit: handleTabEdit,
size: 'small',
tabBarGutter: 6,
renderTabBar: (propsTab, DefaultTabBar) => (
<DefaultTabBar {...propsTab} className="custom-tab-bar" />
),
2025-07-01 14:18:23 +08:00
}}
>
2025-08-04 16:06:23 +08:00
{children}
</PageContainer>
</ProLayout>
2025-07-01 14:18:23 +08:00
</ConfigProvider>
);
};
2025-08-04 16:06:23 +08:00
export default connect(
({ breadcrumb, tab }: { breadcrumb: BreadcrumbState; tab: TabModelState }) => ({
breadcrumb,
tab,
}),
)(BasicLayout);