blob: 31aa23db4d66056d7edfb5e046da31ed7d38870e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react";
import PropTypes from "prop-types";
import {Layout, message} from "antd";
import DocumentTitle from "react-document-title";
import {connect} from "dva";
import {Redirect, Route, Switch} from "dva/router";
import {ContainerQuery} from "react-container-query";
import classNames from "classnames";
import pathToRegexp from "path-to-regexp";
import GlobalHeader from "../components/GlobalHeader";
import SiderMenu from "../components/SiderMenu";
import NotFound from "../routes/Exception/404";
import {getRoutes} from "../utils/utils";
import AuthRoute, {checkMenuAuth, getAuthMenus} from "../utils/AuthRoute";
import {getMenuData} from "../common/menu";
import logo from "../assets/logo.svg";
import TitleLogo from "../assets/TitleLogo.svg";
import {getIntlContent} from "../utils/IntlUtils";
const MyContext = React.createContext();
message.config({
top: 200,
duration: 2,
maxCount: 3
});
const { Content, Header } = Layout;
/**
* Get the redirect address from the menu.
*/
const redirectData = [];
const getRedirect = item => {
if (item && item.children) {
if (item.children[0] && item.children[0].path) {
redirectData.push({
from: `${item.path}`,
to: `${item.children[0].path}`
});
item.children.forEach(children => {
getRedirect(children);
});
}
}
};
getMenuData().forEach(getRedirect);
/**
* Gets the breadcrumb map
*
* @param {Object} menuData
* @param {Object} routerData
*/
const getBreadcrumbNameMap = (menuData, routerData) => {
const result = {};
const childResult = {};
for (const i of menuData) {
if (!routerData[i.path]) {
result[i.path] = i;
}
if (i.children) {
Object.assign(childResult, getBreadcrumbNameMap(i.children, routerData));
}
}
return Object.assign({}, routerData, result, childResult);
};
const query = {
"screen-lg": {
minWidth: 992,
maxWidth: 1199
},
"screen-xl": {
minWidth: 1200,
maxWidth: 1599
},
"screen-xxl": {
minWidth: 1600
}
};
@connect(({ global, resource, loading }) => ({
plugins: global.plugins,
menuTree: resource.menuTree,
permissions: global.permissions,
loading: loading.effects["resource/fetchMenuTree"] || loading.effects["global/fetchPlugins"]
}))
class BasicLayout extends React.PureComponent {
static childContextTypes = {
location: PropTypes.object,
breadcrumbNameMap: PropTypes.object
};
constructor(props) {
super(props);
this.state = {
localeName: window.sessionStorage.getItem("locale")
? window.sessionStorage.getItem("locale")
: "en-US",
pluginsLoaded: false
};
}
getChildContext() {
const { location, routerData } = this.props;
return {
location,
breadcrumbNameMap: getBreadcrumbNameMap(getMenuData(), routerData)
};
}
componentDidMount() {
const token = window.sessionStorage.getItem("token");
if (!token) {
this.props.history.push({
pathname: "/user/login"
});
return;
}
const { dispatch } = this.props;
dispatch({
type: "global/fetchPlatform"
});
dispatch({
type: "resource/fetchMenuTree"
})
dispatch({
type: "global/fetchPlugins",
payload: {
callback: () => {
this.setState({
pluginsLoaded: true
});
}
}
});
}
componentWillUnmount() {
this.setState = () => {}
}
getPageTitle() {
const { routerData, location } = this.props;
const { pathname } = location;
let title = "Apache ShenYu - Gateway Management";
let currRouterData = null;
// match params path
Object.keys(routerData).forEach(key => {
if (pathToRegexp(key).test(pathname)) {
currRouterData = routerData[key];
}
});
if (currRouterData && currRouterData.name) {
title = `Apache ShenYu - ${currRouterData.name}`;
}
return title;
}
getBaseRedirect = () => {
// According to the url parameter to redirect
const urlParams = new URL(window.location.href);
const redirect = urlParams.searchParams.get("redirect");
// Remove the parameters in the url
if (redirect) {
urlParams.searchParams.delete("redirect");
window.history.replaceState(null, "redirect", urlParams.href);
} else {
const { routerData, permissions } = this.props;
// get the first authorized route path in routerData
return Object.keys(routerData).find(
item => checkMenuAuth(item, permissions) && item !== "/"
);
}
return redirect;
};
handleLogout = () => {
const { dispatch } = this.props;
dispatch({
type: "login/logout"
});
dispatch({
type: "global/resetPermission"
});
};
changeLocalName = value => {
const { dispatch } = this.props;
this.setState({
localeName: value
});
dispatch({
type: "global/changeLanguage",
payload: value
});
};
render() {
const {
collapsed,
routerData,
match,
location,
plugins,
menuTree,
permissions,
dispatch
} = this.props;
const { localeName, pluginsLoaded } = this.state;
const bashRedirect = this.getBaseRedirect();
let menus = getMenuData();
if (menuTree.length > 0) {
menus = menus.slice(0, 1);
menuTree.forEach(item => {
if (item.name !== 'plug') {
let title = getIntlContent(item.meta.title);
menus.push({
name : title === '' ? item.meta.title : title,
icon : item.meta.icon,
path : item.url,
locale : item.meta.title,
children: item.children.map(child => {
let childTitle = getIntlContent(child.meta.title);
return {
name : childTitle === '' ? child.meta.title : childTitle,
icon : child.meta.icon,
path : child.url,
locale : child.meta.title,
};
})
});
}
});
}
const menuMap = {};
plugins.forEach(item => {
if (menuMap[item.role] === undefined) {
menuMap[item.role] = [];
}
menuMap[item.role].push(item);
});
Object.keys(menuMap).forEach((key) => {
menus[0].children.push({
name: key,
path: `/plug/${menuMap[key][0].role}`,
authority: undefined,
icon: "unordered-list",
children: menuMap[key].map(item => {
const { name } = item;
return {
name: name.replace(name[0], name[0].toUpperCase()),
path: `/plug/${item.role}/${item.name}`,
authority: undefined,
id: item.id,
locale: `SHENYU.MENU.PLUGIN.${item.name.toUpperCase()}`,
exact: true
};
})
});
});
menus = getAuthMenus(menus, permissions, pluginsLoaded);
// Filter empty menu
function removeEmptyMenu(menuArr) {
return menuArr.filter(menu => {
if (Array.isArray(menu.children)) {
if (menu.children.length === 0) {
return false;
} else {
menu.children = removeEmptyMenu(menu.children);
}
}
return true;
});
}
if (Array.isArray(menus) && menus.length) {
removeEmptyMenu(menus);
}
const layout = (
<Layout>
<SiderMenu
logo={logo}
TitleLogo={TitleLogo}
dispatch={dispatch}
menuData={menus}
collapsed={collapsed}
location={location}
onCollapse={this.handleMenuCollapse}
/>
<Layout>
<Header style={{ padding: 0 }}>
<GlobalHeader
logo={logo}
collapsed={collapsed}
onCollapse={this.handleMenuCollapse}
onLogout={this.handleLogout}
changeLocalName={this.changeLocalName}
/>
</Header>
<Content
className="content-wrap"
style={{ height: "100%", position: "relative" }}
>
<Switch>
{redirectData.map(item => (
<Redirect key={item.from} exact from={item.from} to={item.to} />
))}
{getRoutes(match.path, routerData).map(item => (
<AuthRoute
key={item.key}
path={item.path}
component={item.component}
exact={item.exact}
authority={item.authority}
redirectPath="/exception/403"
/>
))}
<Redirect exact from="/" to={bashRedirect} />
<Route render={NotFound} />
</Switch>
</Content>
</Layout>
</Layout>
);
return (
<MyContext.Provider value={localeName}>
<DocumentTitle title={this.getPageTitle()}>
<ContainerQuery query={query}>
{params => (
<div style={{ minWidth: 1200 }} className={classNames(params)}>
{layout}
</div>
)}
</ContainerQuery>
</DocumentTitle>
</MyContext.Provider>
);
}
}
export default connect(({ global = {} }) => ({
collapsed: global.collapsed
}))(BasicLayout);