diff --git a/packages/client/src/acl/ACLAction.tsx b/packages/client/src/acl/ACLAction.tsx new file mode 100644 index 000000000..81fa48321 --- /dev/null +++ b/packages/client/src/acl/ACLAction.tsx @@ -0,0 +1,65 @@ +import { Menu } from 'antd'; +import React, { useState } from 'react'; +import { LockOutlined } from '@ant-design/icons'; +import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component'; +import { ISchema, useForm } from '@formily/react'; + +const useCloseAction = () => { + const { setVisible } = useActionVisible(); + const form = useForm(); + return { + async run() { + setVisible(false); + form.submit((values) => { + console.log(values); + }); + }, + }; +}; + +const schema: ISchema = { + type: 'object', + properties: { + drawer1: { + 'x-component': 'Action.Drawer', + type: 'void', + title: 'Drawer Title', + properties: { + hello1: { + 'x-content': 'Hello', + title: 'T1', + }, + footer1: { + 'x-component': 'Action.Drawer.Footer', + type: 'void', + properties: { + close1: { + title: 'Close', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ useCloseAction }}', + }, + }, + }, + }, + }, + }, + }, +}; + +export const ACLAction = () => { + const [visible, setVisible] = useState(false); + return ( + + { + setVisible(true); + }} + > + + + + + ); +}; diff --git a/packages/client/src/acl/index.tsx b/packages/client/src/acl/index.tsx index 332e3cb46..c5f671c00 100644 --- a/packages/client/src/acl/index.tsx +++ b/packages/client/src/acl/index.tsx @@ -1,2 +1,3 @@ export * from './ACLProvider'; -export * from './RolePermissionManager'; \ No newline at end of file +export * from './RolePermissionManager'; +export * from './ACLAction'; diff --git a/packages/client/src/application/demos/demo2/index.tsx b/packages/client/src/application/demos/demo2/index.tsx index 4a31f8b42..da6d304b7 100644 --- a/packages/client/src/application/demos/demo2/index.tsx +++ b/packages/client/src/application/demos/demo2/index.tsx @@ -12,6 +12,7 @@ import { AntdConfigProvider, SchemaComponentProvider, Menu, + Action, } from '@nocobase/client'; import { I18nextProvider } from 'react-i18next'; import { Spin } from 'antd'; @@ -23,7 +24,7 @@ const providers = [ [APIClientProvider, { apiClient }], [I18nextProvider, { i18n }], [AntdConfigProvider, { remoteLocale: true }], - [SchemaComponentProvider, { components: { Menu } }], + [SchemaComponentProvider, { components: { Menu, Action } }], [RouteSwitchProvider, { components: { AuthLayout, AdminLayout } }], ]; diff --git a/packages/client/src/collection-manager/CollectionManagerAction.tsx b/packages/client/src/collection-manager/CollectionManagerAction.tsx new file mode 100644 index 000000000..819be2203 --- /dev/null +++ b/packages/client/src/collection-manager/CollectionManagerAction.tsx @@ -0,0 +1,65 @@ +import { Menu } from 'antd'; +import React, { useState } from 'react'; +import { DatabaseOutlined } from '@ant-design/icons'; +import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component'; +import { ISchema, useForm } from '@formily/react'; + +const useCloseAction = () => { + const { setVisible } = useActionVisible(); + const form = useForm(); + return { + async run() { + setVisible(false); + form.submit((values) => { + console.log(values); + }); + }, + }; +}; + +const schema: ISchema = { + type: 'object', + properties: { + drawer1: { + 'x-component': 'Action.Drawer', + type: 'void', + title: 'Drawer Title', + properties: { + hello1: { + 'x-content': 'Hello', + title: 'T1', + }, + footer1: { + 'x-component': 'Action.Drawer.Footer', + type: 'void', + properties: { + close1: { + title: 'Close', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ useCloseAction }}', + }, + }, + }, + }, + }, + }, + }, +}; + +export const CollectionManagerAction = () => { + const [visible, setVisible] = useState(false); + return ( + + { + setVisible(true); + }} + > + + + + + ); +}; diff --git a/packages/client/src/collection-manager/index.tsx b/packages/client/src/collection-manager/index.tsx index 9e8059b38..a0962f948 100644 --- a/packages/client/src/collection-manager/index.tsx +++ b/packages/client/src/collection-manager/index.tsx @@ -5,3 +5,4 @@ export * from './context'; export * from './hooks'; export * from './types'; export * from './CollectionField'; +export * from './CollectionManagerAction'; diff --git a/packages/client/src/current-user/ProfileAction.tsx b/packages/client/src/current-user/ProfileAction.tsx new file mode 100644 index 000000000..cc850e212 --- /dev/null +++ b/packages/client/src/current-user/ProfileAction.tsx @@ -0,0 +1,65 @@ +import { Menu } from 'antd'; +import React, { useState } from 'react'; +import { DatabaseOutlined } from '@ant-design/icons'; +import { SchemaComponent, useActionVisible, VisibleContext } from '../schema-component'; +import { ISchema, useForm } from '@formily/react'; + +const useCloseAction = () => { + const { setVisible } = useActionVisible(); + const form = useForm(); + return { + async run() { + setVisible(false); + form.submit((values) => { + console.log(values); + }); + }, + }; +}; + +const schema: ISchema = { + type: 'object', + properties: { + drawer1: { + 'x-component': 'Action.Drawer', + type: 'void', + title: 'Drawer Title', + properties: { + hello1: { + 'x-content': 'Hello', + title: 'T1', + }, + footer1: { + 'x-component': 'Action.Drawer.Footer', + type: 'void', + properties: { + close1: { + title: 'Close', + 'x-component': 'Action', + 'x-component-props': { + useAction: '{{ useCloseAction }}', + }, + }, + }, + }, + }, + }, + }, +}; + +export const ProfileAction = () => { + const [visible, setVisible] = useState(false); + return ( + + { + setVisible(true); + }} + > + 个人资料 + + + + ); +}; diff --git a/packages/client/src/current-user/index.tsx b/packages/client/src/current-user/index.tsx index 6ceeda644..616b8fb13 100644 --- a/packages/client/src/current-user/index.tsx +++ b/packages/client/src/current-user/index.tsx @@ -1,3 +1,19 @@ -export function CurrentUser() { - -} +import { Menu } from 'antd'; +import React from 'react'; +import { MoreOutlined, DesktopOutlined } from '@ant-design/icons'; +import { ProfileAction } from './ProfileAction'; + +export const CurrentUser = () => null; + +CurrentUser.Dropdown = () => { + return ( + // @ts-ignore + + + 切换角色 + 语言设置 + + 注销 + + ); +}; diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx index a7be8354d..e85c14eb3 100644 --- a/packages/client/src/index.tsx +++ b/packages/client/src/index.tsx @@ -14,3 +14,4 @@ export * from './system-settings'; export * from './application'; export * from './record-provider'; export * from './async-data-provider'; +export * from './plugin-manager'; diff --git a/packages/client/src/plugin-manager/PluginManager.tsx b/packages/client/src/plugin-manager/PluginManager.tsx new file mode 100644 index 000000000..56a119ee9 --- /dev/null +++ b/packages/client/src/plugin-manager/PluginManager.tsx @@ -0,0 +1,53 @@ +import React, { createContext } from 'react'; +import { Menu, Space } from 'antd'; +import { SettingOutlined, MoreOutlined, DesktopOutlined } from '@ant-design/icons'; +import { CurrentUser, DesignableSwitch, CollectionManagerAction, ACLAction, SystemSettings } from '../'; +import { get } from 'lodash'; +import { PluginManagerProvider } from '.'; + +export const PluginManager = () => null; + +PluginManager.Provider = PluginManagerProvider; + +PluginManager.Toolbar = (props: any) => { + const components = { CurrentUser, DesignableSwitch, CollectionManagerAction, ACLAction, SystemSettings }; + const items = [ + { + action: 'DesignableSwitch', + pin: true, + }, + { + action: 'CollectionManagerAction', + pin: true, + }, + { + action: 'ACLAction', + pin: true, + }, + { + action: 'SystemSettings.Action', + }, + ]; + const CurrentUserDropdown = get(components, 'CurrentUser.Dropdown'); + return ( + + {items + .filter((item) => item.pin) + .map((item) => { + const Action = get(components, item.action); + return Action && ; + })} + }> + {items + .filter((item) => !item.pin) + .map((item) => { + const Action = get(components, item.action); + return Action && ; + })} + + }>管理插件 + + + + ); +}; diff --git a/packages/client/src/plugin-manager/PluginManagerProvider.tsx b/packages/client/src/plugin-manager/PluginManagerProvider.tsx new file mode 100644 index 000000000..35a28f5b2 --- /dev/null +++ b/packages/client/src/plugin-manager/PluginManagerProvider.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import { PluginManagerContext } from './context'; + +export const PluginManagerProvider: React.FC = (props) => { + const { components, children } = props; + return {children}; +}; diff --git a/packages/client/src/plugin-manager/context.ts b/packages/client/src/plugin-manager/context.ts new file mode 100644 index 000000000..3220782c6 --- /dev/null +++ b/packages/client/src/plugin-manager/context.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const PluginManagerContext = createContext({}); diff --git a/packages/client/src/plugin-manager/index.md b/packages/client/src/plugin-manager/index.md new file mode 100644 index 000000000..fd2687e1b --- /dev/null +++ b/packages/client/src/plugin-manager/index.md @@ -0,0 +1,55 @@ +--- +nav: + path: /client +group: + path: /client +--- + +# PluginManager + +```tsx | pure + + + +``` + +## 扩展 + +如何扩展插件的 `PluginManager.Toolbar.Item` + +```tsx | pure +import React from 'react'; +import { Button, Menu } from 'antd'; +import { HighlightOutlined } from '@ant-design/icons'; + +export const DesignableSwitch = () => { + const { designable, setDesignable } = useDesignable(); + return ( + + + + ); +}; +``` \ No newline at end of file diff --git a/packages/client/src/plugin-manager/index.ts b/packages/client/src/plugin-manager/index.ts new file mode 100644 index 000000000..dfbf7596f --- /dev/null +++ b/packages/client/src/plugin-manager/index.ts @@ -0,0 +1,3 @@ +export * from './context'; +export * from './PluginManager'; +export * from './PluginManagerProvider'; diff --git a/packages/client/src/route-switch/antd/admin-layout/index.md b/packages/client/src/route-switch/antd/admin-layout/index.md index b98d998ec..229736389 100644 --- a/packages/client/src/route-switch/antd/admin-layout/index.md +++ b/packages/client/src/route-switch/antd/admin-layout/index.md @@ -14,12 +14,12 @@ group: - Layout.Header - SiteTitle - Menu(SchemaComponent) - - PluginActionBar - - Designable.Action(引用) - - CollectionManager.Action(引用) - - ACL.Action(引用) - - SystemSettings.Action(引用) - - CurrentUser.Dropdown(引用) + - PluginManager.Toolbar + - DesignableAction + - CollectionManager.Action + - ACL.Action + - SystemSettings.Action + - CurrentUser.Dropdown - Layout.Sider - SideMenu(随 Menu 联动的) - Layout.Content 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 b8cc84c96..ff5601eaa 100644 --- a/packages/client/src/route-switch/antd/admin-layout/index.tsx +++ b/packages/client/src/route-switch/antd/admin-layout/index.tsx @@ -1,8 +1,8 @@ import React, { useRef, useState } from 'react'; -import { Layout, Spin } from 'antd'; -import { useRoute } from '../..'; -import { findMenuItem, RemoteSchemaComponent } from '../../../schema-component'; -import { Redirect, useHistory, useRouteMatch } from 'react-router-dom'; +import { Layout } from 'antd'; +import { useRoute } from '../../hooks'; +import { useHistory, useRouteMatch } from 'react-router-dom'; +import { findMenuItem, RemoteSchemaComponent, PluginManager } from '../../../'; export function AdminLayout(props: any) { const route = useRoute(); @@ -19,28 +19,33 @@ export function AdminLayout(props: any) { const [hidden, setHidden] = useState(false); return ( - -