mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 09:17:23 +00:00
feat(client-menu): improve code
This commit is contained in:
parent
e48e70e82a
commit
c9742c1710
@ -18,8 +18,8 @@ import { Spin } from 'antd';
|
||||
import apiClient from './apiClient';
|
||||
|
||||
const providers = [
|
||||
[HashRouter],
|
||||
// [MemoryRouter, { initialEntries: ['/'] }],
|
||||
// [HashRouter],
|
||||
[MemoryRouter, { initialEntries: ['/'] }],
|
||||
[APIClientProvider, { apiClient }],
|
||||
[I18nextProvider, { i18n }],
|
||||
[AntdConfigProvider, { remoteLocale: true }],
|
||||
|
@ -8,91 +8,90 @@ export default (apiClient: APIClient) => {
|
||||
data: { lang: 'en-US' },
|
||||
});
|
||||
|
||||
mock.onGet('/ui_schemas:getJsonSchema/item1').reply(200, {
|
||||
data: {
|
||||
type: 'void',
|
||||
name: 'item1',
|
||||
'x-uid': 'item1',
|
||||
'x-component': 'div',
|
||||
'x-content': 'item1',
|
||||
},
|
||||
});
|
||||
|
||||
mock.onGet('/ui_schemas:getJsonSchema/item2').reply(200, {
|
||||
data: {
|
||||
type: 'void',
|
||||
name: 'item2',
|
||||
'x-uid': 'item2',
|
||||
'x-component': 'div',
|
||||
'x-content': 'item2',
|
||||
},
|
||||
});
|
||||
|
||||
mock.onGet('/ui_schemas:getJsonSchema/item4').reply(200, {
|
||||
data: {
|
||||
type: 'void',
|
||||
name: 'item4',
|
||||
'x-uid': 'item4',
|
||||
'x-component': 'div',
|
||||
'x-content': 'item4',
|
||||
},
|
||||
});
|
||||
|
||||
mock.onGet('/ui_schemas:getJsonSchema/item5').reply(200, {
|
||||
data: {
|
||||
type: 'void',
|
||||
name: 'item5',
|
||||
'x-uid': 'item5',
|
||||
'x-component': 'div',
|
||||
'x-content': 'item5',
|
||||
},
|
||||
});
|
||||
|
||||
mock.onGet('/ui_schemas:getJsonSchema/qqzzjakwkwl').reply(200, {
|
||||
data: {
|
||||
type: 'void',
|
||||
const jsonSchema = {
|
||||
qqzzjakwkwl: {
|
||||
name: 'qqzzjakwkwl',
|
||||
'x-uid': 'qqzzjakwkwl',
|
||||
type: 'void',
|
||||
'x-component': 'Menu',
|
||||
'x-component-props': {
|
||||
mode: 'horizontal',
|
||||
mode: 'mix',
|
||||
theme: 'dark',
|
||||
// defaultSelectedUid: 'u8',
|
||||
onSelect: '{{ onSelect }}',
|
||||
defaultSelectedUid: '{{ defaultSelectedUid }}',
|
||||
sideMenuRef: '{{ sideMenuRef }}',
|
||||
},
|
||||
properties: {
|
||||
item1: {
|
||||
type: 'void',
|
||||
title: 'Menu Item 1',
|
||||
'x-uid': 'item1',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
item2: {
|
||||
type: 'void',
|
||||
title: 'Menu Item 2',
|
||||
'x-uid': 'item2',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
item3: {
|
||||
type: 'void',
|
||||
title: 'SubMenu 1',
|
||||
'x-uid': 'item3',
|
||||
title: 'SubMenu u3',
|
||||
'x-uid': 'u3',
|
||||
'x-component': 'Menu.SubMenu',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
item6: {
|
||||
type: 'void',
|
||||
title: 'SubMenu u6',
|
||||
'x-uid': 'u6',
|
||||
'x-component': 'Menu.SubMenu',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
item7: {
|
||||
type: 'void',
|
||||
title: 'Menu Item u7',
|
||||
'x-uid': 'u7',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
item8: {
|
||||
type: 'void',
|
||||
title: 'Menu Item u8',
|
||||
'x-uid': 'u8',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
item4: {
|
||||
type: 'void',
|
||||
title: 'Menu Item 4',
|
||||
'x-uid': 'item4',
|
||||
title: 'Menu Item u4',
|
||||
'x-uid': 'u4',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
item5: {
|
||||
type: 'void',
|
||||
title: 'Menu Item 5',
|
||||
'x-uid': 'item5',
|
||||
title: 'Menu Item u5',
|
||||
'x-uid': 'u5',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
item1: {
|
||||
type: 'void',
|
||||
title: 'Menu Item u1',
|
||||
'x-uid': 'u1',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
item2: {
|
||||
type: 'void',
|
||||
title: 'Menu Item u2',
|
||||
'x-uid': 'u2',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
item9: {
|
||||
type: 'void',
|
||||
title: 'SubMenu u9',
|
||||
'x-uid': 'u9',
|
||||
'x-component': 'Menu.SubMenu',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
item10: {
|
||||
type: 'void',
|
||||
title: 'Menu Item u10',
|
||||
'x-uid': 'u10',
|
||||
'x-component': 'Menu.Item',
|
||||
'x-component-props': {},
|
||||
},
|
||||
@ -100,6 +99,24 @@ export default (apiClient: APIClient) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mock.onGet(/\/ui_schemas\:getJsonSchema\/(\w+)/).reply(function (config) {
|
||||
const name = config.url.split('/').pop();
|
||||
console.log(name);
|
||||
if (jsonSchema[name]) {
|
||||
return [200, { data: jsonSchema[name] }];
|
||||
}
|
||||
const response = {
|
||||
data: {
|
||||
type: 'void',
|
||||
name: name,
|
||||
'x-uid': name,
|
||||
'x-component': 'div',
|
||||
'x-content': name,
|
||||
},
|
||||
};
|
||||
return [200, response];
|
||||
});
|
||||
|
||||
mock.onGet('/routes:getAccessible').reply(200, {
|
||||
|
@ -1,13 +1,14 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Layout, Spin } from 'antd';
|
||||
import { useRoute } from '../..';
|
||||
import { RemoteSchemaComponent } from '../../../schema-component';
|
||||
import { useHistory, useRouteMatch } from 'react-router-dom';
|
||||
import { findMenuItem, RemoteSchemaComponent } from '../../../schema-component';
|
||||
import { Redirect, useHistory, useRouteMatch } from 'react-router-dom';
|
||||
|
||||
export function AdminLayout(props: any) {
|
||||
const route = useRoute();
|
||||
const history = useHistory();
|
||||
const match = useRouteMatch<any>();
|
||||
const sideMenuRef = useRef();
|
||||
const defaultSelectedUid = match.params.name;
|
||||
const [schema, setSchema] = useState({});
|
||||
const onSelect = ({ item }) => {
|
||||
@ -15,12 +16,34 @@ export function AdminLayout(props: any) {
|
||||
setSchema(schema);
|
||||
history.push(`/admin/${schema['x-uid']}`);
|
||||
};
|
||||
const [hidden, setHidden] = useState(false);
|
||||
return (
|
||||
<Layout>
|
||||
<Layout.Header>
|
||||
<RemoteSchemaComponent scope={{ onSelect, defaultSelectedUid }} uid={route.uiSchemaUid} />
|
||||
<RemoteSchemaComponent
|
||||
hidden={hidden}
|
||||
uid={route.uiSchemaUid}
|
||||
scope={{ onSelect, sideMenuRef, defaultSelectedUid }}
|
||||
schemaTransform={(data) => {
|
||||
data['x-component-props']['defaultSelectedUid'] = defaultSelectedUid;
|
||||
return data;
|
||||
}}
|
||||
onSuccess={(data) => {
|
||||
if (defaultSelectedUid) {
|
||||
return;
|
||||
}
|
||||
setHidden(true);
|
||||
setTimeout(() => setHidden(false), 11);
|
||||
const s = findMenuItem(data?.data);
|
||||
if (s) {
|
||||
setSchema(s);
|
||||
history.push(`/admin/${s['x-uid']}`);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Layout.Header>
|
||||
<Layout>
|
||||
<Layout.Sider style={{ display: 'none' }} theme={'light'} ref={sideMenuRef}></Layout.Sider>
|
||||
<Layout.Content>
|
||||
<RemoteSchemaComponent uid={match.params.name} />
|
||||
</Layout.Content>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { Menu as AntdMenu } from 'antd';
|
||||
import { Schema, observer, useFieldSchema, useField, RecursionField } from '@formily/react';
|
||||
import { DesktopOutlined } from '@ant-design/icons';
|
||||
import { findKeysByUid } from './util';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Menu as AntdMenu } from 'antd';
|
||||
import { Schema, observer, useFieldSchema, RecursionField } from '@formily/react';
|
||||
import { DesktopOutlined } from '@ant-design/icons';
|
||||
import { findKeysByUid, findMenuItem } from './util';
|
||||
|
||||
type ComposedMenu = React.FC<any> & {
|
||||
Item?: React.FC<any>;
|
||||
@ -13,14 +13,32 @@ type ComposedMenu = React.FC<any> & {
|
||||
const MenuModeContext = createContext(null);
|
||||
|
||||
export const Menu: ComposedMenu = observer((props) => {
|
||||
let { onSelect, sideMenuRef, mode, defaultSelectedUid, defaultSelectedKeys, defaultOpenKeys, ...others } = props;
|
||||
let {
|
||||
onSelect,
|
||||
sideMenuRef,
|
||||
mode,
|
||||
defaultSelectedUid,
|
||||
defaultSelectedKeys: dSelectedKeys,
|
||||
defaultOpenKeys: dOpenKeys,
|
||||
...others
|
||||
} = props;
|
||||
const schema = useFieldSchema();
|
||||
if (defaultSelectedUid) {
|
||||
defaultSelectedKeys = findKeysByUid(schema, defaultSelectedUid);
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
defaultOpenKeys = defaultSelectedKeys;
|
||||
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
||||
if (dSelectedKeys) {
|
||||
return dSelectedKeys;
|
||||
}
|
||||
}
|
||||
if (defaultSelectedUid) {
|
||||
return findKeysByUid(schema, defaultSelectedUid);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [defaultOpenKeys, setDefaultOpenKeys] = useState(() => {
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
return dOpenKeys || defaultSelectedKeys;
|
||||
}
|
||||
return dOpenKeys;
|
||||
});
|
||||
const [sideMenuSchema, setSideMenuSchema] = useState<Schema>(() => {
|
||||
if (mode === 'mix' && defaultSelectedKeys[0]) {
|
||||
const s = schema.properties?.[defaultSelectedKeys[0]];
|
||||
@ -30,6 +48,11 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
useEffect(() => {
|
||||
if (['inline', 'mix'].includes(mode)) {
|
||||
setDefaultOpenKeys(defaultSelectedKeys);
|
||||
}
|
||||
}, [defaultSelectedKeys]);
|
||||
useEffect(() => {
|
||||
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
||||
if (!sideMenuElement) {
|
||||
@ -37,17 +60,41 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
}
|
||||
sideMenuElement.style.display = sideMenuSchema?.properties ? 'block' : 'none';
|
||||
}, [sideMenuSchema?.properties, sideMenuRef]);
|
||||
console.log({ sideMenuRef, defaultSelectedKeys, sideMenuSchema });
|
||||
return (
|
||||
<MenuModeContext.Provider value={mode}>
|
||||
<AntdMenu
|
||||
{...others}
|
||||
onSelect={(info) => {
|
||||
onSelect={(info: any) => {
|
||||
const s = schema.properties[info.key];
|
||||
if (mode === 'mix') {
|
||||
setSideMenuSchema(s);
|
||||
if (!s?.properties) {
|
||||
onSelect && onSelect(info);
|
||||
} else {
|
||||
const menuItemSchema = findMenuItem(s);
|
||||
if (!menuItemSchema) {
|
||||
return;
|
||||
}
|
||||
// TODO
|
||||
setLoading(true);
|
||||
const keys = findKeysByUid(schema, menuItemSchema['x-uid']);
|
||||
setDefaultSelectedKeys(keys);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 100);
|
||||
onSelect &&
|
||||
onSelect({
|
||||
key: menuItemSchema.name,
|
||||
item: {
|
||||
props: {
|
||||
schema: menuItemSchema,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
onSelect && onSelect(info);
|
||||
}
|
||||
onSelect && onSelect(info);
|
||||
}}
|
||||
mode={mode === 'mix' ? 'horizontal' : mode}
|
||||
defaultOpenKeys={defaultOpenKeys}
|
||||
@ -55,17 +102,26 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
>
|
||||
<RecursionField schema={schema} onlyRenderProperties />
|
||||
</AntdMenu>
|
||||
{mode === 'mix' &&
|
||||
sideMenuSchema.properties &&
|
||||
sideMenuRef.current?.firstChild &&
|
||||
createPortal(
|
||||
<MenuModeContext.Provider value={'inline'}>
|
||||
<AntdMenu mode={'inline'} defaultOpenKeys={defaultOpenKeys} defaultSelectedKeys={defaultSelectedKeys}>
|
||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
||||
</AntdMenu>
|
||||
</MenuModeContext.Provider>,
|
||||
sideMenuRef.current.firstChild,
|
||||
)}
|
||||
{loading
|
||||
? null
|
||||
: mode === 'mix' &&
|
||||
sideMenuSchema?.properties &&
|
||||
sideMenuRef.current?.firstChild &&
|
||||
createPortal(
|
||||
<MenuModeContext.Provider value={'inline'}>
|
||||
<AntdMenu
|
||||
mode={'inline'}
|
||||
defaultOpenKeys={defaultOpenKeys}
|
||||
defaultSelectedKeys={defaultSelectedKeys}
|
||||
onSelect={(info) => {
|
||||
onSelect && onSelect(info);
|
||||
}}
|
||||
>
|
||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
||||
</AntdMenu>
|
||||
</MenuModeContext.Provider>,
|
||||
sideMenuRef.current.firstChild,
|
||||
)}
|
||||
</MenuModeContext.Provider>
|
||||
);
|
||||
});
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './Menu';
|
||||
export { findMenuItem } from './util';
|
||||
|
@ -13,6 +13,22 @@ function findByUid(schema: Schema, uid: string) {
|
||||
}, null);
|
||||
}
|
||||
|
||||
export function findMenuItem(schema: Schema) {
|
||||
if (!Schema.isSchemaInstance(schema)) {
|
||||
schema = new Schema(schema);
|
||||
}
|
||||
for (const { schema: s } of Schema.getOrderProperties(schema)) {
|
||||
if (s['x-component'] === 'Menu.Item') {
|
||||
return s;
|
||||
}
|
||||
const ss = findMenuItem(s);
|
||||
if (ss) {
|
||||
return ss;
|
||||
}
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
function findKeys(schema: Schema) {
|
||||
if (!schema) {
|
||||
return;
|
||||
|
@ -7,13 +7,16 @@ import { Schema } from '@formily/react';
|
||||
export interface RemoteSchemaComponentProps {
|
||||
scope?: any;
|
||||
uid?: string;
|
||||
transform?: (schema: Schema) => Schema;
|
||||
onSuccess?: any;
|
||||
schemaTransform?: (schema: Schema) => Schema;
|
||||
render?: any;
|
||||
hidden?: any;
|
||||
}
|
||||
|
||||
const defaultTransform = (s: Schema) => s;
|
||||
|
||||
export const RemoteSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) => {
|
||||
const { scope, uid, transform = defaultTransform } = props;
|
||||
const { hidden, scope, uid, onSuccess, schemaTransform = defaultTransform } = props;
|
||||
if (!uid) {
|
||||
return null;
|
||||
}
|
||||
@ -23,10 +26,14 @@ export const RemoteSchemaComponent: React.FC<RemoteSchemaComponentProps> = (prop
|
||||
},
|
||||
{
|
||||
refreshDeps: [uid],
|
||||
onSuccess,
|
||||
},
|
||||
);
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return <SchemaComponent scope={scope} schema={transform(data?.data || {})} />;
|
||||
if (hidden) {
|
||||
return <Spin />;
|
||||
}
|
||||
return <SchemaComponent scope={scope} schema={schemaTransform(data?.data || {})} />;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user