cloudNew/src/layouts/index.tsx
2025-02-26 14:09:23 +08:00

346 lines
11 KiB
TypeScript

import type { FC } from 'react';
import { useState, useEffect } from 'react';
import { Dropdown, Layout, Menu, Tabs, Tooltip } from 'antd';
import type { MenuProps } from 'antd';
import { Outlet, Link, useLocation, connect } from 'umi';
import PageAccess from '@/components/PageAccess';
import type { UserConnectedProps, UserModelState } from '@/models/user';
import LayoutWrapper from '@/components/LayoutWrapper';
import Nav from '@/components/Nav';
import Page404 from '@/pages/404';
import handleRecursiveNestedData from '@/utils/handleRecursiveNestedData';
import handleGetCurrentLocation from '@/utils/handleGetCurrentLocation';
import { MenuDataItem, ProLayout } from '@ant-design/pro-components';
import './index.less'
import logo from '../assets/logo.svg';
import Icon, { DoubleRightOutlined, SmileOutlined } from '@ant-design/icons';
import { ProfileModelState } from '@/models/global';
import React from 'react';
const { Header, Content } = Layout;
/**
* 获取openKeys的方法
* @param currentLocation 当前位置, 由handleGetCurrentLocation方法返回
* @returns openKeys
*/
const handleGetOpenKeys = (currentLocation: API.MenuItem[] | []): string[] => {
//currentLocation为空
if (!currentLocation.length) return [''];
//currentLocation只有一项
if (currentLocation.length === 1) {
return currentLocation.map((item: API.MenuItem) => `${item.key}`);
}
const res = [];
//currentLocation有多项, 只要前n-1项
for (let i = 0; i < currentLocation.length - 1; i++) {
res.push(`${currentLocation[i].key}`);
}
return res;
};
//自定义的layout页面, 顶部导航通栏+侧边栏(菜单)布局, 可根据需要做调整
const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatch: any }> = (props) => {
const [collapsed, setCollapsed] = useState(false);
const [openKeys, setOpenKeys] = useState(['']);
const { pathname } = useLocation();
const {
dispatch,
user: {
menu, rootSubmenuKeys, indexAllMenuItemById,
indexValidMenuItemByPath
},
global: {
}
} = props;
console.log('props', props);
const validMenuItem = indexValidMenuItemByPath[pathname];
const selectedKeys = validMenuItem?.key;
useEffect(
() => {
//每次页面重新渲染都要设置openKeys
setOpenKeys(
handleGetOpenKeys(
handleGetCurrentLocation(indexValidMenuItemByPath[pathname], indexAllMenuItemById),
),
);
},
[pathname, indexAllMenuItemById, indexValidMenuItemByPath],
);
//Menu中的selectedKeys和openKeys不是一回事:
//openKeys:
//当前展开的SubMenu菜单项key数组, 有子菜单的父菜单, 当selectedKeys为没子菜单的父菜单时该值应该设为[''],
//也就是关闭所有有子菜单的父菜单;
//selectedKeys:
//当前选中的菜单项key数组, 有子菜单则是子菜单(叶子节点), 没有子菜单则是父菜单(一级菜单), 始终是可选中的
//点击有子菜单的父菜单的回调
// const onOpenChange: MenuProps['onOpenChange'] = (keys) => {
// setOpenKeys(keys)
// console.log('keys', keys);
// dispatch({
// namespace: "global/setProfileData",
// payload: {
// keys
// }
// })
// };
//所有MenuItem:
//有children的: 一定都有path, lable不动, children下的label修改为<Link to={path} />
//无children的: 有path的label修改为<Link to={path} />, 没path的label不动
const consumableMenu = handleRecursiveNestedData(
menu,
(item: API.MenuItem) => ({
...item,
name: item.path
? (
<Link
to={item.path}
style={{ color: 'inherit' }}
>{item.name}</Link>
)
: item.name,
}),
);
// const consumableMenu: MenuDataItem[] = [
// {
// path: '/',
// name: 'Dashboard',
// icon: <SmileOutlined />,
// },
// {
// path: '/about',
// name: 'Users',
// icon: <SmileOutlined />,
// children: [
// {
// path: '/about/u',
// name: 'User List',
// },
// {
// path: '/about/m',
// name: 'User Profile',
// },
// ],
// },
// ];
console.log('consumableMenu', consumableMenu);
const location = useLocation();
// 改变panes
const handleTabsPanes = (payload: any): void => {
dispatch({
type: "global/saveTabsRoutes",
payload: {
data: payload,
action: 'add'
}
})
};
return (
<LayoutWrapper>
<ProLayout
logo={logo}
contentWidth={"Fluid"}
fixedHeader={true}
fixSiderbar={true}
colorWeak={false}
title={"驿商云平台"}
pwa={false}
iconfontUrl={'//at.alicdn.com/t/font_2794551_djdgwbunsvg.js'}
footerRender={false}
location={location}
style={{ minHeight: '100vh' }}
siderWidth={208}
menuDataRender={() =>
consumableMenu
// menu
}
subMenuItemRender={(menuItemProps) => {
if (menuItemProps.icon && typeof menuItemProps.icon === 'string') {
const ele = React.createElement(Icon[menuItemProps.icon])
return <div className="ant-pro-menu-item">
<span style={{ marginRight: 10 }} className="ant-pro-menu-item">{ele}</span>
<span className='ant-pro-menu-item-title'>{menuItemProps.name}</span>
</div>;
}
return <div>
{menuItemProps.name}
</div>;
}}
menuItemRender={(menuItemProps, defaultDom) => {
if (
menuItemProps.isUrl ||
!menuItemProps.path ||
location.pathname === menuItemProps.path
) {
return defaultDom;
}
return <Link to={menuItemProps.path || '/'} onClick={() => {
}}>
{defaultDom}
{/* {menuItemProps.name} */}
</Link>;
}}
breadcrumbRender={false}
itemRender={(route, params, routes, paths) => {
const first = routes.indexOf(route) === 0;
return first ? (
<Link to={paths.join('/')}>{route.breadcrumbName}</Link>
) : (
<span>{route.breadcrumbName}</span>
);
}}
actionsRender={() => <nav />}
onPageChange={(location) => {
console.log('location', location);
if (location?.pathname && location?.pathname !== '/') {
const nextModule = consumableMenu.find(n => location?.pathname && n?.path && location?.pathname.match(n?.path))
console.log('nextModule', nextModule);
}
handleTabsPanes({ path: location?.pathname, key: location?.pathname })
}}
>
{/* menuExtraRender={() =>
<Layout>
<Menu
mode="inline"
openKeys={openKeys}
items={consumableMenu}
onOpenChange={(e: any, dom: any) => {
onOpenChange(e)
}}
onSelect={(item: any, key: any) => {
console.log('item', item);
console.log('key', key);
}}
selectedKeys={[selectedKeys]}
/>
</Layout>
} */}
<Layout>
<Header style={{ background: '#fff', height: '48px', padding: '0 16px' }}>
<Nav />
</Header>
<Content>
<Tabs
hideAdd
type="editable-card"
size="small"
className='main-tab'
tabBarExtraContent={
<Tooltip title="关闭选项卡" placement="topRight">
<Dropdown overlay={
<Menu onClick={(targetKey) => {
}}>
<Menu.Item key="other"></Menu.Item>
<Menu.Item key="now"></Menu.Item>
<Menu.Item key="all"></Menu.Item>
</Menu>
}
trigger={['click']}
placement="bottomRight"
arrow>
<div onClick={(e) => {
e.preventDefault()
}}>
<img style={{ width: '20px', height: '20px', cursor: 'pointer' }} />
</div>
</Dropdown >
</Tooltip>
}
moreIcon={<DoubleRightOutlined style={{ color: "#7b828c" }} />}
>
{/* {tabsPanes && tabsPanes.map((item: any) =>
<TabPane
tab={item.title} key={item?.path}
style={{ padding: 24, paddingTop: 0 }}>
{
//统一对所有有效路由做页面鉴权的处理
validMenuItem
? (
<PageAccess>
<Outlet />
</PageAccess>
)
: <Page404 />
}
</TabPane>)
} */}
</Tabs>
</Content>
</Layout>
</ProLayout>
</LayoutWrapper >
// <LayoutWrapper>
// <Layout style={{ minHeight: '100vh' }}>
// <Header
// style={{
// padding: '0',
// height: 'auto',
// }}
// >
// <Nav />
// </Header>
// <Layout>
// <Sider
// collapsible
// theme="light"
// collapsed={collapsed}
// onCollapse={(value) => setCollapsed(value)}
// >
// <Menu
// mode="inline"
// openKeys={openKeys}
// items={consumableMenu}
// onOpenChange={onOpenChange}
// selectedKeys={[selectedKeys]}
// />
// </Sider>
// <Layout>
// <Content style={{ margin: '0 16px' }}>
// {
// //统一对所有有效路由做页面鉴权的处理
// validMenuItem
// ? (
// <PageAccess>
// <Outlet />
// </PageAccess>
// )
// //这里不再使用<Outlet />是因为当一条路由在菜单接口中没有返回, 但实际项目中创建了,
// //此时访问应该显示404页面; 菜单中没有, 实际项目中有表示这条路由/页面当前登录用户无法访问,
// //而如果继续使用<Outlet />则那条路由/页面依旧会被渲染, 因为对于umi来说这条路由/页面是有效的,
// //是存在的, 而对于本项目来说则不是
// : <Page404 />
// }
// </Content>
// </Layout>
// </Layout>
// </Layout>
// </LayoutWrapper>
);
};
export default connect(
({ user, global }: { user: UserConnectedProps['user'], global: any }) => ({
user,
global
}),
)(BasicLayout);