mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
* [Fixed] 1. ts升级4.4.4 2. 解决升级formily类型不兼容问题 * [Fixed] 1. 修复菜单混合渲染问题 * [Fixed] 1. 修复useEffect对象深度依赖比较(订单表死循环) * [update]去掉debugger
330 lines
8.8 KiB
TypeScript
330 lines
8.8 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';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
function DesignableToggle() {
|
|
const { t } = useTranslation();
|
|
const { designable, setDesignable } = useDesignableSwitchContext();
|
|
const [visible, setVisible] = useState(false);
|
|
useEffect(() => {
|
|
if (designable) {
|
|
return;
|
|
}
|
|
if (localStorage.getItem('hide-tip')) {
|
|
return;
|
|
}
|
|
setTimeout(() => {
|
|
setVisible(true);
|
|
}, 2000);
|
|
}, []);
|
|
return (
|
|
<div>
|
|
<Tooltip
|
|
// color={'volcano'}
|
|
placement={'bottom'}
|
|
title={
|
|
<span>
|
|
{t('Click here to enter UI editor.')}{' '}
|
|
<a
|
|
onClick={() => {
|
|
localStorage.setItem('hide-tip', 'true');
|
|
setVisible(false);
|
|
}}
|
|
>
|
|
{t('Close')}
|
|
</a>
|
|
</span>
|
|
}
|
|
visible={visible}
|
|
onVisibleChange={(visible) => {
|
|
setVisible(visible);
|
|
}}
|
|
>
|
|
<span style={{ height: 45, width: 45, position: 'absolute' }}></span>
|
|
</Tooltip>
|
|
<Tooltip title={t('UI editor')}>
|
|
<Button
|
|
className={cls('nb-designable-toggle', {
|
|
'has-tip': visible,
|
|
active: designable,
|
|
})}
|
|
type={'primary'}
|
|
style={{ height: 46, border: 0 }}
|
|
onClick={() => {
|
|
setDesignable(!designable);
|
|
}}
|
|
>
|
|
<HighlightOutlined />
|
|
</Button>
|
|
</Tooltip>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
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('');
|
|
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/${uid()}`);
|
|
};
|
|
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,
|
|
},
|
|
);
|
|
|
|
const first: any = Object.values(data?.properties || {}).shift();
|
|
|
|
const findMenuLinkProperties = (schema: Schema): Schema[] => {
|
|
if (!schema) {
|
|
return [];
|
|
}
|
|
if (schema['x-component'] == 'Menu.Link') {
|
|
return [schema];
|
|
}
|
|
return schema.reduceProperties((items, current) => {
|
|
if (current['x-component'] == 'Menu.Link') {
|
|
return [...items, current];
|
|
}
|
|
return [...items, ...findMenuLinkProperties(current)];
|
|
}, []);
|
|
};
|
|
|
|
if (loading) {
|
|
return <Spin size={'large'} className={'nb-spin-center'} />;
|
|
}
|
|
|
|
const f = findMenuLinkProperties(new Schema(first || {})).shift();
|
|
|
|
if (f?.['key'] && !match.params.name) {
|
|
return <Redirect to={`/admin/${f?.['key']}`} />;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
return (
|
|
<AuthProvider>
|
|
<SystemSettingsProvider>
|
|
<DesignableSwitchProvider>
|
|
<CollectionsProvider>
|
|
<PageTitleProvider defaultPageTitle={current?.title}>
|
|
<LayoutWithMenu
|
|
defaultSelectedKeys={defaultSelectedKeys}
|
|
current={current}
|
|
schema={data}
|
|
/>
|
|
</PageTitleProvider>
|
|
</CollectionsProvider>
|
|
</DesignableSwitchProvider>
|
|
</SystemSettingsProvider>
|
|
</AuthProvider>
|
|
);
|
|
}
|
|
|
|
export default AdminLayout;
|