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';
|
import apiClient from './apiClient';
|
||||||
|
|
||||||
const providers = [
|
const providers = [
|
||||||
[HashRouter],
|
// [HashRouter],
|
||||||
// [MemoryRouter, { initialEntries: ['/'] }],
|
[MemoryRouter, { initialEntries: ['/'] }],
|
||||||
[APIClientProvider, { apiClient }],
|
[APIClientProvider, { apiClient }],
|
||||||
[I18nextProvider, { i18n }],
|
[I18nextProvider, { i18n }],
|
||||||
[AntdConfigProvider, { remoteLocale: true }],
|
[AntdConfigProvider, { remoteLocale: true }],
|
||||||
|
@ -8,91 +8,90 @@ export default (apiClient: APIClient) => {
|
|||||||
data: { lang: 'en-US' },
|
data: { lang: 'en-US' },
|
||||||
});
|
});
|
||||||
|
|
||||||
mock.onGet('/ui_schemas:getJsonSchema/item1').reply(200, {
|
const jsonSchema = {
|
||||||
data: {
|
qqzzjakwkwl: {
|
||||||
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',
|
|
||||||
name: 'qqzzjakwkwl',
|
name: 'qqzzjakwkwl',
|
||||||
'x-uid': 'qqzzjakwkwl',
|
type: 'void',
|
||||||
'x-component': 'Menu',
|
'x-component': 'Menu',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
mode: 'horizontal',
|
mode: 'mix',
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
|
// defaultSelectedUid: 'u8',
|
||||||
onSelect: '{{ onSelect }}',
|
onSelect: '{{ onSelect }}',
|
||||||
defaultSelectedUid: '{{ defaultSelectedUid }}',
|
sideMenuRef: '{{ sideMenuRef }}',
|
||||||
},
|
},
|
||||||
properties: {
|
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: {
|
item3: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: 'SubMenu 1',
|
title: 'SubMenu u3',
|
||||||
'x-uid': 'item3',
|
'x-uid': 'u3',
|
||||||
'x-component': 'Menu.SubMenu',
|
'x-component': 'Menu.SubMenu',
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
properties: {
|
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: {
|
item4: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: 'Menu Item 4',
|
title: 'Menu Item u4',
|
||||||
'x-uid': 'item4',
|
'x-uid': 'u4',
|
||||||
'x-component': 'Menu.Item',
|
'x-component': 'Menu.Item',
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
},
|
},
|
||||||
item5: {
|
item5: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: 'Menu Item 5',
|
title: 'Menu Item u5',
|
||||||
'x-uid': 'item5',
|
'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': 'Menu.Item',
|
||||||
'x-component-props': {},
|
'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, {
|
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 { Layout, Spin } from 'antd';
|
||||||
import { useRoute } from '../..';
|
import { useRoute } from '../..';
|
||||||
import { RemoteSchemaComponent } from '../../../schema-component';
|
import { findMenuItem, RemoteSchemaComponent } from '../../../schema-component';
|
||||||
import { useHistory, useRouteMatch } from 'react-router-dom';
|
import { Redirect, useHistory, useRouteMatch } from 'react-router-dom';
|
||||||
|
|
||||||
export function AdminLayout(props: any) {
|
export function AdminLayout(props: any) {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const match = useRouteMatch<any>();
|
const match = useRouteMatch<any>();
|
||||||
|
const sideMenuRef = useRef();
|
||||||
const defaultSelectedUid = match.params.name;
|
const defaultSelectedUid = match.params.name;
|
||||||
const [schema, setSchema] = useState({});
|
const [schema, setSchema] = useState({});
|
||||||
const onSelect = ({ item }) => {
|
const onSelect = ({ item }) => {
|
||||||
@ -15,12 +16,34 @@ export function AdminLayout(props: any) {
|
|||||||
setSchema(schema);
|
setSchema(schema);
|
||||||
history.push(`/admin/${schema['x-uid']}`);
|
history.push(`/admin/${schema['x-uid']}`);
|
||||||
};
|
};
|
||||||
|
const [hidden, setHidden] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Layout.Header>
|
<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.Header>
|
||||||
<Layout>
|
<Layout>
|
||||||
|
<Layout.Sider style={{ display: 'none' }} theme={'light'} ref={sideMenuRef}></Layout.Sider>
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
<RemoteSchemaComponent uid={match.params.name} />
|
<RemoteSchemaComponent uid={match.params.name} />
|
||||||
</Layout.Content>
|
</Layout.Content>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
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 { 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> & {
|
type ComposedMenu = React.FC<any> & {
|
||||||
Item?: React.FC<any>;
|
Item?: React.FC<any>;
|
||||||
@ -13,14 +13,32 @@ type ComposedMenu = React.FC<any> & {
|
|||||||
const MenuModeContext = createContext(null);
|
const MenuModeContext = createContext(null);
|
||||||
|
|
||||||
export const Menu: ComposedMenu = observer((props) => {
|
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();
|
const schema = useFieldSchema();
|
||||||
if (defaultSelectedUid) {
|
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
||||||
defaultSelectedKeys = findKeysByUid(schema, defaultSelectedUid);
|
if (dSelectedKeys) {
|
||||||
if (['inline', 'mix'].includes(mode)) {
|
return dSelectedKeys;
|
||||||
defaultOpenKeys = defaultSelectedKeys;
|
|
||||||
}
|
}
|
||||||
}
|
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>(() => {
|
const [sideMenuSchema, setSideMenuSchema] = useState<Schema>(() => {
|
||||||
if (mode === 'mix' && defaultSelectedKeys[0]) {
|
if (mode === 'mix' && defaultSelectedKeys[0]) {
|
||||||
const s = schema.properties?.[defaultSelectedKeys[0]];
|
const s = schema.properties?.[defaultSelectedKeys[0]];
|
||||||
@ -30,6 +48,11 @@ export const Menu: ComposedMenu = observer((props) => {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
if (['inline', 'mix'].includes(mode)) {
|
||||||
|
setDefaultOpenKeys(defaultSelectedKeys);
|
||||||
|
}
|
||||||
|
}, [defaultSelectedKeys]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
const sideMenuElement = sideMenuRef?.current as HTMLElement;
|
||||||
if (!sideMenuElement) {
|
if (!sideMenuElement) {
|
||||||
@ -37,17 +60,41 @@ export const Menu: ComposedMenu = observer((props) => {
|
|||||||
}
|
}
|
||||||
sideMenuElement.style.display = sideMenuSchema?.properties ? 'block' : 'none';
|
sideMenuElement.style.display = sideMenuSchema?.properties ? 'block' : 'none';
|
||||||
}, [sideMenuSchema?.properties, sideMenuRef]);
|
}, [sideMenuSchema?.properties, sideMenuRef]);
|
||||||
console.log({ sideMenuRef, defaultSelectedKeys, sideMenuSchema });
|
|
||||||
return (
|
return (
|
||||||
<MenuModeContext.Provider value={mode}>
|
<MenuModeContext.Provider value={mode}>
|
||||||
<AntdMenu
|
<AntdMenu
|
||||||
{...others}
|
{...others}
|
||||||
onSelect={(info) => {
|
onSelect={(info: any) => {
|
||||||
const s = schema.properties[info.key];
|
const s = schema.properties[info.key];
|
||||||
if (mode === 'mix') {
|
if (mode === 'mix') {
|
||||||
setSideMenuSchema(s);
|
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}
|
mode={mode === 'mix' ? 'horizontal' : mode}
|
||||||
defaultOpenKeys={defaultOpenKeys}
|
defaultOpenKeys={defaultOpenKeys}
|
||||||
@ -55,17 +102,26 @@ export const Menu: ComposedMenu = observer((props) => {
|
|||||||
>
|
>
|
||||||
<RecursionField schema={schema} onlyRenderProperties />
|
<RecursionField schema={schema} onlyRenderProperties />
|
||||||
</AntdMenu>
|
</AntdMenu>
|
||||||
{mode === 'mix' &&
|
{loading
|
||||||
sideMenuSchema.properties &&
|
? null
|
||||||
sideMenuRef.current?.firstChild &&
|
: mode === 'mix' &&
|
||||||
createPortal(
|
sideMenuSchema?.properties &&
|
||||||
<MenuModeContext.Provider value={'inline'}>
|
sideMenuRef.current?.firstChild &&
|
||||||
<AntdMenu mode={'inline'} defaultOpenKeys={defaultOpenKeys} defaultSelectedKeys={defaultSelectedKeys}>
|
createPortal(
|
||||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
<MenuModeContext.Provider value={'inline'}>
|
||||||
</AntdMenu>
|
<AntdMenu
|
||||||
</MenuModeContext.Provider>,
|
mode={'inline'}
|
||||||
sideMenuRef.current.firstChild,
|
defaultOpenKeys={defaultOpenKeys}
|
||||||
)}
|
defaultSelectedKeys={defaultSelectedKeys}
|
||||||
|
onSelect={(info) => {
|
||||||
|
onSelect && onSelect(info);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
||||||
|
</AntdMenu>
|
||||||
|
</MenuModeContext.Provider>,
|
||||||
|
sideMenuRef.current.firstChild,
|
||||||
|
)}
|
||||||
</MenuModeContext.Provider>
|
</MenuModeContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './Menu';
|
export * from './Menu';
|
||||||
|
export { findMenuItem } from './util';
|
||||||
|
@ -13,6 +13,22 @@ function findByUid(schema: Schema, uid: string) {
|
|||||||
}, null);
|
}, 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) {
|
function findKeys(schema: Schema) {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return;
|
return;
|
||||||
|
@ -7,13 +7,16 @@ import { Schema } from '@formily/react';
|
|||||||
export interface RemoteSchemaComponentProps {
|
export interface RemoteSchemaComponentProps {
|
||||||
scope?: any;
|
scope?: any;
|
||||||
uid?: string;
|
uid?: string;
|
||||||
transform?: (schema: Schema) => Schema;
|
onSuccess?: any;
|
||||||
|
schemaTransform?: (schema: Schema) => Schema;
|
||||||
|
render?: any;
|
||||||
|
hidden?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultTransform = (s: Schema) => s;
|
const defaultTransform = (s: Schema) => s;
|
||||||
|
|
||||||
export const RemoteSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) => {
|
export const RemoteSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) => {
|
||||||
const { scope, uid, transform = defaultTransform } = props;
|
const { hidden, scope, uid, onSuccess, schemaTransform = defaultTransform } = props;
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -23,10 +26,14 @@ export const RemoteSchemaComponent: React.FC<RemoteSchemaComponentProps> = (prop
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
refreshDeps: [uid],
|
refreshDeps: [uid],
|
||||||
|
onSuccess,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spin />;
|
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