|
|
|
@ -1,78 +1,59 @@
|
|
|
|
|
import * as React from 'react';
|
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
|
|
import { history } from 'umi';
|
|
|
|
|
import ProLayout, { MenuDataItem, WaterMark } from '@ant-design/pro-layout';
|
|
|
|
|
import styles from './index.less';
|
|
|
|
|
import { IAction } from '@/api/Types';
|
|
|
|
|
import { urlToList } from '@/utils/pathTools';
|
|
|
|
|
import { AppStore } from '@/store/AppStore';
|
|
|
|
|
import { useMenu } from './useMenu';
|
|
|
|
|
import { observer } from 'mobx-react';
|
|
|
|
|
import { Text, Util, Storage, Session } from '@/utils';
|
|
|
|
|
import { buildIcon, SearchBar } from '@/lib';
|
|
|
|
|
import defaultLogo from '@/assets/logo.svg';
|
|
|
|
|
import {
|
|
|
|
|
UnorderedListOutlined,
|
|
|
|
|
CaretDownOutlined,
|
|
|
|
|
CaretRightOutlined,
|
|
|
|
|
} from '@ant-design/icons';
|
|
|
|
|
import {
|
|
|
|
|
MenuFooter,
|
|
|
|
|
MenuExpander,
|
|
|
|
|
} from '@/layouts/BaseLayout/Sider';
|
|
|
|
|
import Header from '@/layouts/BaseLayout/Header';
|
|
|
|
|
import { getOpenKeysFromSelectedMenu } from '@/layouts/BaseLayout/utils/utils';
|
|
|
|
|
import { Breadcrumb, Button, ConfigProvider, Tooltip, Space } from 'antd';
|
|
|
|
|
import API from '@/api';
|
|
|
|
|
import classNames from 'classnames';
|
|
|
|
|
import { baseLayoutStore as store } from './Store';
|
|
|
|
|
import { Base64 } from 'js-base64';
|
|
|
|
|
import { HTML_ID_LAYOUT_CONTENT } from '@/constant/ID';
|
|
|
|
|
import * as React from "react";
|
|
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
|
|
import { history } from "umi";
|
|
|
|
|
import { observer } from "mobx-react";
|
|
|
|
|
import ProLayout, { MenuDataItem, WaterMark } from "@ant-design/pro-layout";
|
|
|
|
|
import { IAction } from "@/api/Types";
|
|
|
|
|
import { urlToList } from "@/utils/pathTools";
|
|
|
|
|
import { AppStore } from "@/store/AppStore";
|
|
|
|
|
import { useMenu } from "./useMenu";
|
|
|
|
|
import { Session, Storage, Text, Util } from "@/utils";
|
|
|
|
|
import { buildIcon, SearchBar } from "@/lib";
|
|
|
|
|
import defaultLogo from "@/assets/logo.svg";
|
|
|
|
|
import { CaretDownOutlined, CaretRightOutlined, UnorderedListOutlined } from "@ant-design/icons";
|
|
|
|
|
import { MenuExpander, MenuFooter } from "@/layouts/BaseLayout/Sider";
|
|
|
|
|
import Header from "@/layouts/BaseLayout/Header";
|
|
|
|
|
import { getOpenKeysFromSelectedMenu } from "@/layouts/BaseLayout/utils/utils";
|
|
|
|
|
import { Breadcrumb, Button, ConfigProvider, Space, Tooltip } from "antd";
|
|
|
|
|
import classNames from "classnames";
|
|
|
|
|
import { baseLayoutStore as store } from "./Store";
|
|
|
|
|
import { Base64 } from "js-base64";
|
|
|
|
|
import { HTML_ID_LAYOUT_CONTENT } from "@/constant/ID";
|
|
|
|
|
import styles from "./index.less";
|
|
|
|
|
|
|
|
|
|
const BaseLayout = observer(({ children, location }: any) => {
|
|
|
|
|
const { pathname, query } = location;
|
|
|
|
|
const session: any = Session.get();
|
|
|
|
|
const [collapsed, setCollapse] = useState(false);
|
|
|
|
|
const [selectMenuKey, setSelectMenuKey] = useState([] as Array<string>);
|
|
|
|
|
const [openMenuKey, setOpenMenuKey] = useState([] as Array<string>);
|
|
|
|
|
const [filterMenuData, setFilterMenuData] = useState<any[]>([])
|
|
|
|
|
const session: any = Session.get();
|
|
|
|
|
|
|
|
|
|
const [
|
|
|
|
|
menuData,
|
|
|
|
|
breadCrumbMap,
|
|
|
|
|
defaultCollapseMenus,
|
|
|
|
|
defaultSelectMenus,
|
|
|
|
|
originalMenu,
|
|
|
|
|
filterMenu,
|
|
|
|
|
] = useMenu();
|
|
|
|
|
|
|
|
|
|
const [filterMenuData, setFilterMenuData] = useState<any[]>([]);
|
|
|
|
|
const [breadCrumbs, setBreadCrumbs] = useState<any[]>([]);
|
|
|
|
|
const expandMenuRef = useRef<any>();
|
|
|
|
|
|
|
|
|
|
const [menuData, breadCrumbMap, defaultCollapseMenus, defaultSelectMenus, originalMenu, filterMenu] = useMenu();
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
ConfigProvider.config({
|
|
|
|
|
theme: {
|
|
|
|
|
primaryColor: store.primaryColor,
|
|
|
|
|
},
|
|
|
|
|
primaryColor: store.primaryColor
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}, [store.primaryColor]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setCollapse(false)
|
|
|
|
|
}, [store.layout])
|
|
|
|
|
setCollapse(false);
|
|
|
|
|
}, [store.layout]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (_.isEmpty(menuData) || _.isEmpty(breadCrumbMap)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setOpenMenuKey(store.layout === 'side' ? defaultCollapseMenus : []);
|
|
|
|
|
if (_.isEmpty(menuData) || _.isEmpty(breadCrumbMap)) return;
|
|
|
|
|
setOpenMenuKey(store.layout === "side" ? defaultCollapseMenus : []);
|
|
|
|
|
setSelectMenuKey(defaultSelectMenus);
|
|
|
|
|
setFilterMenuData(menuData)
|
|
|
|
|
setFilterMenuData(menuData);
|
|
|
|
|
}, [menuData, breadCrumbMap, defaultCollapseMenus, defaultSelectMenus, store.layout]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!collapsed) {
|
|
|
|
|
//菜单数据中父子级关系必须有pid/id
|
|
|
|
|
const openKeys = getOpenKeysFromSelectedMenu(originalMenu, selectMenuKey);
|
|
|
|
|
if (!_.isEmpty(openKeys)) {
|
|
|
|
|
setOpenMenuKey(openKeys);
|
|
|
|
@ -81,46 +62,39 @@ const BaseLayout = observer(({ children, location }: any) => {
|
|
|
|
|
}, [collapsed]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (_.isEmpty(breadCrumbMap)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (_.isEmpty(breadCrumbMap)) return;
|
|
|
|
|
const paths = urlToList(pathname);
|
|
|
|
|
let extraBreadcrumbs = _.reduce(
|
|
|
|
|
paths,
|
|
|
|
|
(ret: any[], path: string) => {
|
|
|
|
|
let extraBreadcrumbs = _.reduce(paths, (ret: any[], path: string) => {
|
|
|
|
|
const menu = breadCrumbMap[path];
|
|
|
|
|
if (!_.isEmpty(menu)) {
|
|
|
|
|
ret.push({ ...menu });
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
},
|
|
|
|
|
[],
|
|
|
|
|
[]
|
|
|
|
|
);
|
|
|
|
|
const home = breadCrumbMap['/'] || breadCrumbMap['/home'];
|
|
|
|
|
const home = breadCrumbMap["/"] || breadCrumbMap["/home"];
|
|
|
|
|
const breadcrumbs = _.isEmpty(home)
|
|
|
|
|
? extraBreadcrumbs
|
|
|
|
|
: [home].concat(
|
|
|
|
|
_.filter(extraBreadcrumbs, (data: any) => data.key !== home.key),
|
|
|
|
|
_.filter(extraBreadcrumbs, (data: any) => data.key !== home.key)
|
|
|
|
|
);
|
|
|
|
|
setBreadCrumbs(breadcrumbs);
|
|
|
|
|
}, [pathname, breadCrumbMap]);
|
|
|
|
|
|
|
|
|
|
const onMenuOpen = useCallback((openKeys: string[]) => {
|
|
|
|
|
setOpenMenuKey(openKeys);
|
|
|
|
|
Storage.session.set('OPEN_MENU_KEY', openKeys)
|
|
|
|
|
Storage.session.set("OPEN_MENU_KEY", openKeys);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const onMenuSelect = useCallback((menu: any) => {
|
|
|
|
|
if (_.isEmpty(menu)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_.isEmpty(menu)) return;
|
|
|
|
|
if (Array.isArray(menu)) {
|
|
|
|
|
setSelectMenuKey(menu);
|
|
|
|
|
} else {
|
|
|
|
|
setSelectMenuKey([menu.key]);
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
setOpenMenuKey((openMenuKey) => [].concat(openMenuKey).concat(menu.keyPath),
|
|
|
|
|
setOpenMenuKey((openMenuKey) => [].concat(openMenuKey).concat(menu.keyPath)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
@ -134,37 +108,35 @@ const BaseLayout = observer(({ children, location }: any) => {
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onMenuSelect({
|
|
|
|
|
key: item.id,
|
|
|
|
|
keyPath: [item.id, item.pid],
|
|
|
|
|
keyPath: [item.id, item.pid]
|
|
|
|
|
});
|
|
|
|
|
history.push(item.path);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<a>
|
|
|
|
|
{buildIcon('', item.icon)} {item.name}
|
|
|
|
|
</a>
|
|
|
|
|
<a>{buildIcon("", item.icon)} {item.name}</a>
|
|
|
|
|
</Breadcrumb.Item>
|
|
|
|
|
))}
|
|
|
|
|
</Breadcrumb>
|
|
|
|
|
)
|
|
|
|
|
}, [breadCrumbs])
|
|
|
|
|
);
|
|
|
|
|
}, [breadCrumbs]);
|
|
|
|
|
|
|
|
|
|
const classes = useMemo(() => {
|
|
|
|
|
return classNames({
|
|
|
|
|
[styles.root]: true,
|
|
|
|
|
[styles[store.navTheme]]: true,
|
|
|
|
|
})
|
|
|
|
|
}, [store.navTheme])
|
|
|
|
|
[styles[store.navTheme]]: true
|
|
|
|
|
});
|
|
|
|
|
}, [store.navTheme]);
|
|
|
|
|
|
|
|
|
|
const onMenuSearch = useCallback((key: any) => {
|
|
|
|
|
if (_.isEmpty(key)) {
|
|
|
|
|
setFilterMenuData(menuData);
|
|
|
|
|
} else {
|
|
|
|
|
setFilterMenuData(filterMenu(originalMenu, key))
|
|
|
|
|
setFilterMenuData(filterMenu(originalMenu, key));
|
|
|
|
|
}
|
|
|
|
|
}, [menuData])
|
|
|
|
|
}, [menuData]);
|
|
|
|
|
|
|
|
|
|
// 内嵌模式直接渲染子路由
|
|
|
|
|
if (query?.embeded && query?.embeded == 'T') {
|
|
|
|
|
if (query?.embeded && query?.embeded == "T") {
|
|
|
|
|
return <>{children}</>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -172,35 +144,28 @@ const BaseLayout = observer(({ children, location }: any) => {
|
|
|
|
|
// const appName = API.SettingService.getSystemParam('appName') || '管理系统';
|
|
|
|
|
// const globalWaterMark = API.SettingService.getSystemParam('globalWaterMark') || 'F';
|
|
|
|
|
const logo = defaultLogo;
|
|
|
|
|
const appName = '管理系统';
|
|
|
|
|
const globalWaterMark = 'F';
|
|
|
|
|
const appName = "管理系统";
|
|
|
|
|
const globalWaterMark = "F";
|
|
|
|
|
|
|
|
|
|
let layout = (
|
|
|
|
|
<div
|
|
|
|
|
className={classes}
|
|
|
|
|
style={{
|
|
|
|
|
width: '100vw',
|
|
|
|
|
height: '100vh',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className={classes}>
|
|
|
|
|
<ProLayout
|
|
|
|
|
className='__layout_main__'
|
|
|
|
|
className="__layout_main__"
|
|
|
|
|
disableMobile
|
|
|
|
|
fixedHeader
|
|
|
|
|
fixSiderbar
|
|
|
|
|
location={location}
|
|
|
|
|
layout={store.layout}
|
|
|
|
|
splitMenus={store.layout === 'mix' && store.splitMenus}
|
|
|
|
|
navTheme={store.layout === 'top' ? 'dark' : 'light'}
|
|
|
|
|
splitMenus={store.layout === "mix" && store.splitMenus}
|
|
|
|
|
navTheme={store.layout === "top" ? "dark" : "light"}
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
iconfontUrl={`${Util.getPublicPath()}css/iconfont/iconfont.js`}
|
|
|
|
|
title={Text.toShortText(appName, store.layout === 'side' ? 7 : 11)}
|
|
|
|
|
logo={<span onClick={() => history.push('/')}><img src={logo} /></span>}
|
|
|
|
|
title={Text.toShortText(appName, store.layout === "side" ? 7 : 11)}
|
|
|
|
|
logo={<span onClick={() => history.push("/")}><img src={logo} alt=""/></span>}
|
|
|
|
|
collapsed={collapsed}
|
|
|
|
|
style={{ transition: 'all 0.5s ease 0s' }}
|
|
|
|
|
style={{ transition: "all 0.5s ease 0s" }}
|
|
|
|
|
headerContentRender={() =>
|
|
|
|
|
(store.layout === 'side' || (store.layout === 'mix' && !store.splitMenus)) ? (
|
|
|
|
|
(store.layout === "side" || (store.layout === "mix" && !store.splitMenus)) ? (
|
|
|
|
|
<div className={styles.headContent}>
|
|
|
|
|
{breadCrumbComp}
|
|
|
|
|
</div>
|
|
|
|
@ -211,51 +176,51 @@ const BaseLayout = observer(({ children, location }: any) => {
|
|
|
|
|
return (
|
|
|
|
|
<a
|
|
|
|
|
onClick={() => {
|
|
|
|
|
const external = /^https?:\/\//.test(item.path || '');
|
|
|
|
|
Storage.session.set('ACTIVE_MENU_KEY', item.key)
|
|
|
|
|
const external = /^https?:\/\//.test(item.path || "");
|
|
|
|
|
Storage.session.set("ACTIVE_MENU_KEY", item.key);
|
|
|
|
|
if (external) {
|
|
|
|
|
const embedUrl = Base64.encode(item.path || '', true);
|
|
|
|
|
const embedUrl = Base64.encode(item.path || "", true);
|
|
|
|
|
history.push(`/external?_q=${embedUrl}`);
|
|
|
|
|
} else {
|
|
|
|
|
history.push(item.path || '/');
|
|
|
|
|
history.push(item.path || "/");
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{typeof item.icon === 'string'
|
|
|
|
|
? buildIcon('FONT', item.icon)
|
|
|
|
|
: item.icon}{' '}
|
|
|
|
|
{typeof item.icon === "string"
|
|
|
|
|
? buildIcon("FONT", item.icon)
|
|
|
|
|
: item.icon}{" "}
|
|
|
|
|
<label>{item.name}</label>
|
|
|
|
|
</a>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
menuProps={{
|
|
|
|
|
theme: 'light',
|
|
|
|
|
theme: "light",
|
|
|
|
|
selectedKeys: selectMenuKey,
|
|
|
|
|
openKeys: openMenuKey,
|
|
|
|
|
onOpenChange: onMenuOpen,
|
|
|
|
|
onSelect: onMenuSelect,
|
|
|
|
|
expandIcon: (props) => {
|
|
|
|
|
return props.isOpen ? (
|
|
|
|
|
<CaretDownOutlined style={{ color: '#999' }} />
|
|
|
|
|
<CaretDownOutlined style={{ color: "#999" }}/>
|
|
|
|
|
) : (
|
|
|
|
|
<CaretRightOutlined style={{ color: '#999' }} />
|
|
|
|
|
<CaretRightOutlined style={{ color: "#999" }}/>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
menuExtraRender={() =>
|
|
|
|
|
store.layout === 'mix' && store.splitMenus ? null : (
|
|
|
|
|
store.layout === "mix" && store.splitMenus ? null : (
|
|
|
|
|
<div className={styles.expandButton}>
|
|
|
|
|
<Space>
|
|
|
|
|
{collapsed ? null : <SearchBar fillOnSearch onSearch={onMenuSearch} size='small' placeholder='搜索功能' />}
|
|
|
|
|
<Button size='small' onClick={() => expandMenuRef.current.toggle()}>
|
|
|
|
|
<Tooltip title='查看全部'><UnorderedListOutlined /></Tooltip>
|
|
|
|
|
{collapsed ? null : <SearchBar fillOnSearch onSearch={onMenuSearch} size="small" placeholder="搜索功能"/>}
|
|
|
|
|
<Button size="small" onClick={() => expandMenuRef.current.toggle()}>
|
|
|
|
|
<Tooltip title="查看全部"><UnorderedListOutlined/></Tooltip>
|
|
|
|
|
</Button>
|
|
|
|
|
</Space>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
rightContentRender={() => (
|
|
|
|
|
<>
|
|
|
|
|
<Header originalMenu={originalMenu} onMenuSelect={onMenuSelect} />
|
|
|
|
|
<Header originalMenu={originalMenu} onMenuSelect={onMenuSelect}/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
collapsedButtonRender={() => (
|
|
|
|
@ -264,11 +229,15 @@ const BaseLayout = observer(({ children, location }: any) => {
|
|
|
|
|
onCollapse={() => setCollapse((collapsed) => !collapsed)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
contentStyle={{ padding: 0, margin: 0, height: 'calc(100vh - 48px)' }}
|
|
|
|
|
contentStyle={{ padding: 0, margin: 0, height: "calc(100vh - 48px)" }}
|
|
|
|
|
>
|
|
|
|
|
<div className={styles.content} id={HTML_ID_LAYOUT_CONTENT}>
|
|
|
|
|
<div className={styles.main}>
|
|
|
|
|
{globalWaterMark === 'T' ? <WaterMark content={`${session?.name}${session?.userNo || ''}`}>{children}</WaterMark> : children}
|
|
|
|
|
{
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
globalWaterMark === "T" ?
|
|
|
|
|
<WaterMark content={`${session?.name}${session?.userNo || ""}`}>{children}</WaterMark> : children
|
|
|
|
|
}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</ProLayout>
|
|
|
|
@ -289,7 +258,7 @@ export default (props: any) => <BaseLayout {...props} />;
|
|
|
|
|
|
|
|
|
|
export const resetManageLayoutBreadcrumbs = (
|
|
|
|
|
store: AppStore,
|
|
|
|
|
breadcrumbs: [],
|
|
|
|
|
breadcrumbs: []
|
|
|
|
|
) => {
|
|
|
|
|
if (_.isEmpty(store.breadcrumbs)) {
|
|
|
|
|
store.setBreadcrumbs(breadcrumbs);
|
|
|
|
@ -298,7 +267,7 @@ export const resetManageLayoutBreadcrumbs = (
|
|
|
|
|
|
|
|
|
|
export const resetManageLayoutTopActions = (
|
|
|
|
|
store: AppStore,
|
|
|
|
|
actions: Array<IAction>,
|
|
|
|
|
actions: Array<IAction>
|
|
|
|
|
) => {
|
|
|
|
|
if (_.isEmpty(store.topActions)) {
|
|
|
|
|
store.setTopActions(actions);
|
|
|
|
|