@ovine/core
Version:
Build flexible admin system with json.
151 lines (150 loc) • 6.83 kB
JavaScript
/**
* App Aside Menu布局
* TODO: 添加 RouteTab/Aside/Header 的默认 loading 状态
* 1. 动态页面 loading 明显,可能与 table loading 状态同步。
*/
import { Layout } from 'amis';
import { cloneDeep } from 'lodash';
import React, { useEffect, useMemo } from 'react';
import { app } from "../../app";
import { withAppTheme } from "../../app/theme";
import { breakpoints, message, storage } from "../../constants";
import { setRoutesConfig } from "../../routes/config";
import { getCurrRoutePath } from "../../routes/exports";
import { getAuthRoutes, getAsideMenus, clearRouteStore } from "../../routes/limit";
import { AppMenuRoutes } from "../../routes/route";
import { useImmer, useSubscriber } from "../../utils/hooks";
import logger from "../../utils/logger";
import { publish } from "../../utils/message";
import { setGlobal } from "../../utils/store";
import { Amis } from "../amis/schema";
import { filterSchemaLimit } from "../amis/schema/func";
import { useAppContext } from "../app/context";
import RouteTabs from "../route_tabs";
import Aside from "./aside";
import Header from "./header";
import { LayoutLoading, LayoutLazyFallback } from "./loading";
import { StyledLayout } from "./styled";
const log = logger.getLogger('lib:components:asideLayout');
const { asideLayoutCtrl } = message;
const defaultHeader = {
brand: {
logo: '',
title: '',
},
};
// TODO: 获取APi路由时有 短暂的 404 页面,需要改成loading 状态
export default withAppTheme((props) => {
const { enableRouteTabs } = useAppContext();
const { children, theme, api, debounceRoute } = props;
const [state, setState] = useImmer({
asideFolded: false,
offScreen: false,
resetRoute: props.resetRoute,
rootRoute: props.rootRoute || '/',
routeTabs: props.routeTabs || {},
header: props.header || defaultHeader,
footer: props.footer,
routes: props.routes || [],
});
const { routes, header, routeTabs, footer, asideFolded, rootRoute, resetRoute, offScreen } = state;
const { ns: themeNs, name: themeName } = theme;
// TODO: 使用 styled media 来检查 媒体查询
const supportTabs = routeTabs && window.innerWidth >= breakpoints.md && routeTabs.enable !== false;
const withTabs = enableRouteTabs && supportTabs;
const requestLayoutApi = (data) => {
if (!api) {
return;
}
const reqOpt = Object.assign(Object.assign({}, api), { data: Object.assign(Object.assign({}, api.data), data) });
app.request(reqOpt).then((source) => {
const resData = (source === null || source === void 0 ? void 0 : source.data) || {};
setState((d) => {
d.resetRoute = !!resData.resetRoute;
if (resData.header) {
d.header = resData.header || defaultHeader;
}
if (resData.rootRoute) {
d.rootRoute = resData.rootRoute;
}
if (resData.routeTabs) {
d.routeTabs = resData.routeTabs;
}
if (resData.footer) {
d.footer = resData.footer;
}
// TODO: 校验 routes 的合法性及默认值
if (resData.routes) {
d.routes = resData.routes;
}
});
});
};
// 过滤 layout 权限
const layoutConf = useMemo(() => {
const conf = cloneDeep({ header, footer });
filterSchemaLimit(conf);
return conf;
}, [header, footer]);
const { authRoutes, AuthRoutes, asideMenus } = useMemo(() => {
clearRouteStore();
setRoutesConfig(routes);
const configs = {
authRoutes: getAuthRoutes(),
asideMenus: getAsideMenus(),
};
log.log('routeConfig', configs);
return Object.assign(Object.assign({}, configs), {
// renderAside: (t: string) => <Aside theme={t} asideMenus={configs.asideMenus} />,
AuthRoutes: (React.createElement(AppMenuRoutes, { debounceRoute: debounceRoute, fallback: LayoutLazyFallback, authRoutes: configs.authRoutes })) });
}, [routes]);
const LayoutAside = useMemo(() => {
return React.createElement(Aside, { theme: themeName, asideMenus: asideMenus });
}, [themeName, routes]);
useSubscriber(asideLayoutCtrl.msg, (msgData = {}) => {
const { key, toggle, data } = msgData;
setState((d) => {
switch (key) {
case asideLayoutCtrl.toggleScreen:
d.offScreen = typeof toggle === 'boolean' ? toggle : !d.offScreen;
break;
case asideLayoutCtrl.toggleFold:
d.asideFolded = typeof toggle === 'boolean' ? toggle : !d.asideFolded;
break;
case asideLayoutCtrl.reload:
requestLayoutApi(data);
break;
default:
}
});
});
useEffect(() => {
requestLayoutApi();
}, []);
useEffect(() => {
setGlobal(storage.supportRouteTabs, supportTabs);
}, [supportTabs]);
useEffect(() => {
if (resetRoute) {
const refreshRoot = getCurrRoutePath() !== rootRoute;
// 防止首次进入页面 首页直接刷新两次
if (supportTabs) {
publish(message.clearRouteTabs, { refreshRoot });
}
else if (refreshRoot) {
app.routerHistory.push(rootRoute);
}
}
}, [AuthRoutes, rootRoute, resetRoute]);
const headerProps = Object.assign(Object.assign(Object.assign({}, layoutConf.header), state), { setLayout: setState, withRouteTabs: withTabs });
const routeTabsProps = Object.assign(Object.assign({}, routeTabs), { rootRoute,
themeNs, routes: authRoutes });
const HeaderComponent = (React.createElement(Header, Object.assign({}, headerProps, { themeNs: themeNs }), withTabs && !!routes.length && React.createElement(RouteTabs, Object.assign({}, routeTabsProps))));
// TODO: 切换 route 时,默认渲染了 404 页面,需要天添加 loading
return (React.createElement(StyledLayout, { id: "app-layout", className: withTabs ? 'with-route-tabs' : '' },
React.createElement(Layout, { headerFixed: true, theme: themeName, folded: asideFolded, offScreen: offScreen, header: HeaderComponent, aside: LayoutAside, footer: layoutConf.footer && React.createElement(Amis, { schema: layoutConf.footer }) },
React.createElement("div", { className: "app-layout-body" },
React.createElement(LayoutLoading, { theme: themeName }),
AuthRoutes,
children))));
});