tachybase_todo/packages/client/src/components/admin-layout/index.tsx
2021-09-14 16:28:11 +08:00

265 lines
7.2 KiB
TypeScript

import React, { useContext, useEffect, useRef, useState } from 'react';
import {
Button,
Spin,
Layout,
PageHeader,
Modal,
Menu,
Collapse,
Dropdown,
Tooltip,
} from 'antd';
import isEmpty from 'lodash/isEmpty';
import {
Link,
useLocation,
useRouteMatch,
useHistory,
Redirect,
} from 'react-router-dom';
import { SchemaRenderer } from '../../schemas';
import { useRequest } from 'ahooks';
import './style.less';
import { ISchema, Schema } from '@formily/react';
import Database, { useAsyncDataSource } from './datatable';
import { HighlightOutlined } from '@ant-design/icons';
import cls from 'classnames';
import {
DesignableSwitchProvider,
useDesignableSwitchContext,
PageTitleProvider,
usePageTitleContext,
CollectionsProvider,
useCollectionsContext,
} from '../../constate';
import { uid } from '@formily/shared';
import { Permissions } from './Permissions';
import { Collections } from './Collections'
import { More } from './More';
import { UserInfo } from './UserInfo';
import {
SiteTitle,
SystemSettingsProvider,
useSystemSettings,
} from './SiteTitle';
import { AuthProvider } from './Auth';
import { Helmet } from 'react-helmet';
import { MenuSelectedKeysContext } from '../../schemas/menu';
function DesignableToggle() {
const { designable, setDesignable } = useDesignableSwitchContext();
return (
<Tooltip title={'界面配置'}>
<Button
className={cls('nb-designable-toggle', { active: designable })}
type={'primary'}
style={{ height: 46, border: 0 }}
onClick={() => {
setDesignable(!designable);
}}
>
<HighlightOutlined />
</Button>
</Tooltip>
);
}
interface LayoutWithMenuProps {
schema: Schema;
[key: string]: any;
}
function LayoutWithMenu(props: LayoutWithMenuProps) {
const { schema, defaultSelectedKeys } = props;
const match = useRouteMatch<any>();
const sideMenuRef = useRef();
const history = useHistory();
const { title } = useSystemSettings();
const [activeKey, setActiveKey] = useState(match.params.name);
const { setPageTitle } = usePageTitleContext();
const [selectedKeys, setSelectedKeys] = useState(defaultSelectedKeys);
const onSelect = (info) => {
if (!info.schema) {
setActiveKey(null);
} else if (info.schema['x-component'] === 'Menu.SubMenu') {
const findMenuItem = (schema: Schema) => {
const first = Object.values(schema.properties || {}).shift();
if (!first) {
return null;
}
if (['Menu.Link', 'Menu.URL'].includes(first['x-component'])) {
return first;
}
return findMenuItem(first);
};
console.log('info.schema', findMenuItem(info.schema));
// 实际应该取第一个子元素
const node = findMenuItem(info.schema);
setActiveKey(node?.key);
if (node?.key) {
history.push(`/admin/${node?.key}`);
const keys = [node?.name];
let parent = node?.parent;
while (parent) {
if (parent['x-component'] === 'Menu') {
break;
}
keys.unshift(parent.name);
parent = parent.parent;
}
setSelectedKeys(keys);
if (node.title) {
setPageTitle(node.title);
}
}
} else {
setActiveKey(info.schema.key);
history.push(`/admin/${info.schema.key}`);
if (info.schema.title) {
setPageTitle(info.schema.title);
const keys = [info.schema?.name];
let parent = info.schema?.parent;
while (parent) {
if (parent['x-component'] === 'Menu') {
break;
}
keys.unshift(parent.name);
parent = parent.parent;
}
setSelectedKeys(keys);
// setSelectedKeys([info.schema.name]);
}
}
};
useEffect(() => {
setActiveKey(match.params.name);
}, [match.params.name]);
const onMenuItemRemove = () => {
history.push(`/admin`);
};
console.log({ activeKey, selectedKeys });
return (
<Layout>
<Layout.Header className={'site-header'} style={{ display: 'flex' }}>
<SiteTitle />
<MenuSelectedKeysContext.Provider value={selectedKeys}>
<SchemaRenderer
schema={schema}
scope={{
sideMenuRef,
onSelect,
onMenuItemRemove,
getSelectedKeys: () => {
console.log('getSelectedKeys', schema);
return selectedKeys.filter(Boolean);
},
selectedKeys: selectedKeys.filter(Boolean),
}}
/>
</MenuSelectedKeysContext.Provider>
<DesignableToggle />
<Collections />
<Permissions />
<More />
<UserInfo />
</Layout.Header>
<Layout>
<Layout.Sider
ref={sideMenuRef}
theme={'light'}
width={200}
></Layout.Sider>
<Layout.Content style={{ minHeight: 'calc(100vh - 46px)' }}>
{activeKey ? (
<Content activeKey={activeKey} />
) : (
<Helmet>
<title>{title}</title>
</Helmet>
)}
</Layout.Content>
</Layout>
</Layout>
);
}
function Content({ activeKey }) {
const { designable } = useDesignableSwitchContext();
const { collections } = useCollectionsContext();
const { data = {}, loading } = useRequest(
`ui_schemas:getTree?filter[parentKey]=${activeKey}`,
{
refreshDeps: [activeKey, collections, designable],
formatResult: (result) => result?.data,
},
);
if (loading) {
return <Spin size={'large'} className={'nb-spin-center'} />;
}
return <SchemaRenderer schema={data} />;
}
export function AdminLayout({ route, ...others }: any) {
const match = useRouteMatch<any>();
const { data = {}, loading } = useRequest(
`ui_schemas:getTree/${route.uiSchemaKey}`,
{
refreshDeps: [route],
formatResult: (result) => result?.data,
},
);
if (loading) {
return <Spin size={'large'} className={'nb-spin-center'} />;
}
const findProperties = (schema: Schema): Schema[] => {
if (!schema) {
return [];
}
return schema.reduceProperties((items, current) => {
if (current['key'] == match.params.name) {
return [...items, current];
}
return [...items, ...findProperties(current)];
}, []);
};
const current = findProperties(new Schema(data)).shift();
const defaultSelectedKeys = [current?.name];
let parent = current?.parent;
while (parent) {
if (parent['x-component'] === 'Menu') {
break;
}
defaultSelectedKeys.unshift(parent.name);
parent = parent.parent;
}
console.log('current?.title', current, current?.title, defaultSelectedKeys);
return (
<AuthProvider>
<SystemSettingsProvider>
<DesignableSwitchProvider>
<CollectionsProvider>
<PageTitleProvider defaultPageTitle={current?.title}>
<LayoutWithMenu
defaultSelectedKeys={defaultSelectedKeys}
current={current}
schema={data}
/>
</PageTitleProvider>
</CollectionsProvider>
</DesignableSwitchProvider>
</SystemSettingsProvider>
</AuthProvider>
);
}
export default AdminLayout;