From c9742c171044edb51af0d9459a829ff3228f349c Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 18 Jan 2022 15:19:54 +0800 Subject: [PATCH] feat(client-menu): improve code --- .../src/application/demos/demo2/index.tsx | 4 +- .../src/application/demos/demo2/mock.ts | 149 ++++++++++-------- .../route-switch/antd/admin-layout/index.tsx | 31 +++- .../src/schema-component/antd/menu/Menu.tsx | 104 +++++++++--- .../src/schema-component/antd/menu/index.ts | 1 + .../src/schema-component/antd/menu/util.ts | 16 ++ .../components/RemoteSchemaComponent.tsx | 13 +- 7 files changed, 219 insertions(+), 99 deletions(-) diff --git a/packages/client/src/application/demos/demo2/index.tsx b/packages/client/src/application/demos/demo2/index.tsx index 64fe435dd9..4a31f8b42e 100644 --- a/packages/client/src/application/demos/demo2/index.tsx +++ b/packages/client/src/application/demos/demo2/index.tsx @@ -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 }], diff --git a/packages/client/src/application/demos/demo2/mock.ts b/packages/client/src/application/demos/demo2/mock.ts index 51a762918e..3572d9c8ae 100644 --- a/packages/client/src/application/demos/demo2/mock.ts +++ b/packages/client/src/application/demos/demo2/mock.ts @@ -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, { diff --git a/packages/client/src/route-switch/antd/admin-layout/index.tsx b/packages/client/src/route-switch/antd/admin-layout/index.tsx index f9a6f258d7..b8cc84c96c 100644 --- a/packages/client/src/route-switch/antd/admin-layout/index.tsx +++ b/packages/client/src/route-switch/antd/admin-layout/index.tsx @@ -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(); + 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 ( - + + diff --git a/packages/client/src/schema-component/antd/menu/Menu.tsx b/packages/client/src/schema-component/antd/menu/Menu.tsx index 6fe8e26e85..b9547a6539 100644 --- a/packages/client/src/schema-component/antd/menu/Menu.tsx +++ b/packages/client/src/schema-component/antd/menu/Menu.tsx @@ -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 & { Item?: React.FC; @@ -13,14 +13,32 @@ type ComposedMenu = React.FC & { 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(() => { 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 ( { + 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) => { > - {mode === 'mix' && - sideMenuSchema.properties && - sideMenuRef.current?.firstChild && - createPortal( - - - - - , - sideMenuRef.current.firstChild, - )} + {loading + ? null + : mode === 'mix' && + sideMenuSchema?.properties && + sideMenuRef.current?.firstChild && + createPortal( + + { + onSelect && onSelect(info); + }} + > + + + , + sideMenuRef.current.firstChild, + )} ); }); diff --git a/packages/client/src/schema-component/antd/menu/index.ts b/packages/client/src/schema-component/antd/menu/index.ts index 629d3d0aa1..66cca849f6 100644 --- a/packages/client/src/schema-component/antd/menu/index.ts +++ b/packages/client/src/schema-component/antd/menu/index.ts @@ -1 +1,2 @@ export * from './Menu'; +export { findMenuItem } from './util'; diff --git a/packages/client/src/schema-component/antd/menu/util.ts b/packages/client/src/schema-component/antd/menu/util.ts index 33528990be..5b03e3b364 100644 --- a/packages/client/src/schema-component/antd/menu/util.ts +++ b/packages/client/src/schema-component/antd/menu/util.ts @@ -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; diff --git a/packages/client/src/schema-component/components/RemoteSchemaComponent.tsx b/packages/client/src/schema-component/components/RemoteSchemaComponent.tsx index 54ac22912e..bff72ab84c 100644 --- a/packages/client/src/schema-component/components/RemoteSchemaComponent.tsx +++ b/packages/client/src/schema-component/components/RemoteSchemaComponent.tsx @@ -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 = (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 = (prop }, { refreshDeps: [uid], + onSuccess, }, ); if (loading) { return ; } - return ; + if (hidden) { + return ; + } + return ; };