feat(client): plugin manager toolbar
This commit is contained in:
		
							parent
							
								
									e7918b7887
								
							
						
					
					
						commit
						d1f9d3e25d
					
				
							
								
								
									
										65
									
								
								packages/client/src/acl/ACLAction.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								packages/client/src/acl/ACLAction.tsx
									
									
									
									
									
										Normal file
									
								
							@ -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 (
 | 
				
			||||||
 | 
					    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
 | 
					      <Menu.Item
 | 
				
			||||||
 | 
					        eventKey={'ACLAction'}
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          setVisible(true);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <LockOutlined />
 | 
				
			||||||
 | 
					      </Menu.Item>
 | 
				
			||||||
 | 
					      <SchemaComponent scope={{ useCloseAction }} schema={schema} />
 | 
				
			||||||
 | 
					    </VisibleContext.Provider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,2 +1,3 @@
 | 
				
			|||||||
export * from './ACLProvider';
 | 
					export * from './ACLProvider';
 | 
				
			||||||
export * from './RolePermissionManager';
 | 
					export * from './RolePermissionManager';
 | 
				
			||||||
 | 
					export * from './ACLAction';
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ import {
 | 
				
			|||||||
  AntdConfigProvider,
 | 
					  AntdConfigProvider,
 | 
				
			||||||
  SchemaComponentProvider,
 | 
					  SchemaComponentProvider,
 | 
				
			||||||
  Menu,
 | 
					  Menu,
 | 
				
			||||||
 | 
					  Action,
 | 
				
			||||||
} from '@nocobase/client';
 | 
					} from '@nocobase/client';
 | 
				
			||||||
import { I18nextProvider } from 'react-i18next';
 | 
					import { I18nextProvider } from 'react-i18next';
 | 
				
			||||||
import { Spin } from 'antd';
 | 
					import { Spin } from 'antd';
 | 
				
			||||||
@ -23,7 +24,7 @@ const providers = [
 | 
				
			|||||||
  [APIClientProvider, { apiClient }],
 | 
					  [APIClientProvider, { apiClient }],
 | 
				
			||||||
  [I18nextProvider, { i18n }],
 | 
					  [I18nextProvider, { i18n }],
 | 
				
			||||||
  [AntdConfigProvider, { remoteLocale: true }],
 | 
					  [AntdConfigProvider, { remoteLocale: true }],
 | 
				
			||||||
  [SchemaComponentProvider, { components: { Menu } }],
 | 
					  [SchemaComponentProvider, { components: { Menu, Action } }],
 | 
				
			||||||
  [RouteSwitchProvider, { components: { AuthLayout, AdminLayout } }],
 | 
					  [RouteSwitchProvider, { components: { AuthLayout, AdminLayout } }],
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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 (
 | 
				
			||||||
 | 
					    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
 | 
					      <Menu.Item
 | 
				
			||||||
 | 
					        eventKey={'CollectionManagerAction'}
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          setVisible(true);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <DatabaseOutlined />
 | 
				
			||||||
 | 
					      </Menu.Item>
 | 
				
			||||||
 | 
					      <SchemaComponent scope={{ useCloseAction }} schema={schema} />
 | 
				
			||||||
 | 
					    </VisibleContext.Provider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -5,3 +5,4 @@ export * from './context';
 | 
				
			|||||||
export * from './hooks';
 | 
					export * from './hooks';
 | 
				
			||||||
export * from './types';
 | 
					export * from './types';
 | 
				
			||||||
export * from './CollectionField';
 | 
					export * from './CollectionField';
 | 
				
			||||||
 | 
					export * from './CollectionManagerAction';
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										65
									
								
								packages/client/src/current-user/ProfileAction.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								packages/client/src/current-user/ProfileAction.tsx
									
									
									
									
									
										Normal file
									
								
							@ -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 (
 | 
				
			||||||
 | 
					    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
 | 
					      <Menu.Item
 | 
				
			||||||
 | 
					        eventKey={'ProfileAction'}
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          setVisible(true);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        个人资料
 | 
				
			||||||
 | 
					      </Menu.Item>
 | 
				
			||||||
 | 
					      <SchemaComponent scope={{ useCloseAction }} schema={schema} />
 | 
				
			||||||
 | 
					    </VisibleContext.Provider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -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
 | 
				
			||||||
 | 
					    <Menu.SubMenu eventKey={'CurrentUser.Dropdown'} title={'超级管理员'}>
 | 
				
			||||||
 | 
					      <ProfileAction />
 | 
				
			||||||
 | 
					      <Menu.Item>切换角色</Menu.Item>
 | 
				
			||||||
 | 
					      <Menu.Item>语言设置</Menu.Item>
 | 
				
			||||||
 | 
					      <Menu.Divider />
 | 
				
			||||||
 | 
					      <Menu.Item>注销</Menu.Item>
 | 
				
			||||||
 | 
					    </Menu.SubMenu>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -14,3 +14,4 @@ export * from './system-settings';
 | 
				
			|||||||
export * from './application';
 | 
					export * from './application';
 | 
				
			||||||
export * from './record-provider';
 | 
					export * from './record-provider';
 | 
				
			||||||
export * from './async-data-provider';
 | 
					export * from './async-data-provider';
 | 
				
			||||||
 | 
					export * from './plugin-manager';
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								packages/client/src/plugin-manager/PluginManager.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								packages/client/src/plugin-manager/PluginManager.tsx
									
									
									
									
									
										Normal file
									
								
							@ -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 (
 | 
				
			||||||
 | 
					    <Menu selectable={false} mode={'horizontal'} theme={'dark'}>
 | 
				
			||||||
 | 
					      {items
 | 
				
			||||||
 | 
					        .filter((item) => item.pin)
 | 
				
			||||||
 | 
					        .map((item) => {
 | 
				
			||||||
 | 
					          const Action = get(components, item.action);
 | 
				
			||||||
 | 
					          return Action && <Action />;
 | 
				
			||||||
 | 
					        })}
 | 
				
			||||||
 | 
					      <Menu.SubMenu key={'more'} title={<MoreOutlined />}>
 | 
				
			||||||
 | 
					        {items
 | 
				
			||||||
 | 
					          .filter((item) => !item.pin)
 | 
				
			||||||
 | 
					          .map((item) => {
 | 
				
			||||||
 | 
					            const Action = get(components, item.action);
 | 
				
			||||||
 | 
					            return Action && <Action />;
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        <Menu.Divider></Menu.Divider>
 | 
				
			||||||
 | 
					        <Menu.Item icon={<SettingOutlined />}>管理插件</Menu.Item>
 | 
				
			||||||
 | 
					      </Menu.SubMenu>
 | 
				
			||||||
 | 
					      <CurrentUserDropdown />
 | 
				
			||||||
 | 
					    </Menu>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { PluginManagerContext } from './context';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const PluginManagerProvider: React.FC<any> = (props) => {
 | 
				
			||||||
 | 
					  const { components, children } = props;
 | 
				
			||||||
 | 
					  return <PluginManagerContext.Provider value={{ components }}>{children}</PluginManagerContext.Provider>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										3
									
								
								packages/client/src/plugin-manager/context.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/client/src/plugin-manager/context.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					import { createContext } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const PluginManagerContext = createContext<any>({});
 | 
				
			||||||
							
								
								
									
										55
									
								
								packages/client/src/plugin-manager/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								packages/client/src/plugin-manager/index.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					nav:
 | 
				
			||||||
 | 
					  path: /client
 | 
				
			||||||
 | 
					group:
 | 
				
			||||||
 | 
					  path: /client
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PluginManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```tsx | pure
 | 
				
			||||||
 | 
					<PluginManager.Provider components={{
 | 
				
			||||||
 | 
					  CurrentUser,
 | 
				
			||||||
 | 
					  DesignableSwitch,
 | 
				
			||||||
 | 
					  CollectionManagerAction,
 | 
				
			||||||
 | 
					  ACLAction,
 | 
				
			||||||
 | 
					  SystemSettings,
 | 
				
			||||||
 | 
					}}>
 | 
				
			||||||
 | 
					  <PluginManager.Toolbar items={[
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      action: 'DesignableSwitch',
 | 
				
			||||||
 | 
					      pin: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      action: 'CollectionManagerAction',
 | 
				
			||||||
 | 
					      pin: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      action: 'ACLAction',
 | 
				
			||||||
 | 
					      pin: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      action: 'SystemSettings.Action',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ]}/>
 | 
				
			||||||
 | 
					</PluginManager.Provider>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 扩展
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					如何扩展插件的 `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 (
 | 
				
			||||||
 | 
					    <Menu.Item key={'DesignableSwitch'} eventKey={'DesignableSwitch'}>
 | 
				
			||||||
 | 
					      <HighlightOutlined />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										3
									
								
								packages/client/src/plugin-manager/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/client/src/plugin-manager/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					export * from './context';
 | 
				
			||||||
 | 
					export * from './PluginManager';
 | 
				
			||||||
 | 
					export * from './PluginManagerProvider';
 | 
				
			||||||
@ -14,12 +14,12 @@ group:
 | 
				
			|||||||
- Layout.Header
 | 
					- Layout.Header
 | 
				
			||||||
  - SiteTitle
 | 
					  - SiteTitle
 | 
				
			||||||
  - Menu(SchemaComponent)
 | 
					  - Menu(SchemaComponent)
 | 
				
			||||||
  - PluginActionBar
 | 
					  - PluginManager.Toolbar
 | 
				
			||||||
    - Designable.Action(引用)
 | 
					    - DesignableAction
 | 
				
			||||||
    - CollectionManager.Action(引用)
 | 
					    - CollectionManager.Action
 | 
				
			||||||
    - ACL.Action(引用)
 | 
					    - ACL.Action
 | 
				
			||||||
    - SystemSettings.Action(引用)
 | 
					    - SystemSettings.Action
 | 
				
			||||||
  - CurrentUser.Dropdown(引用)
 | 
					    - CurrentUser.Dropdown
 | 
				
			||||||
- Layout.Sider
 | 
					- Layout.Sider
 | 
				
			||||||
  - SideMenu(随 Menu 联动的)
 | 
					  - SideMenu(随 Menu 联动的)
 | 
				
			||||||
- Layout.Content
 | 
					- Layout.Content
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
import React, { useRef, useState } from 'react';
 | 
					import React, { useRef, useState } from 'react';
 | 
				
			||||||
import { Layout, Spin } from 'antd';
 | 
					import { Layout } from 'antd';
 | 
				
			||||||
import { useRoute } from '../..';
 | 
					import { useRoute } from '../../hooks';
 | 
				
			||||||
import { findMenuItem, RemoteSchemaComponent } from '../../../schema-component';
 | 
					import { useHistory, useRouteMatch } from 'react-router-dom';
 | 
				
			||||||
import { Redirect, useHistory, useRouteMatch } from 'react-router-dom';
 | 
					import { findMenuItem, RemoteSchemaComponent, PluginManager } from '../../../';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function AdminLayout(props: any) {
 | 
					export function AdminLayout(props: any) {
 | 
				
			||||||
  const route = useRoute();
 | 
					  const route = useRoute();
 | 
				
			||||||
@ -19,28 +19,33 @@ export function AdminLayout(props: any) {
 | 
				
			|||||||
  const [hidden, setHidden] = useState(false);
 | 
					  const [hidden, setHidden] = useState(false);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Layout>
 | 
					    <Layout>
 | 
				
			||||||
      <Layout.Header>
 | 
					      <Layout.Header style={{ position: 'relative' }}>
 | 
				
			||||||
        <RemoteSchemaComponent
 | 
					        <div>
 | 
				
			||||||
          hidden={hidden}
 | 
					          <RemoteSchemaComponent
 | 
				
			||||||
          uid={route.uiSchemaUid}
 | 
					            hidden={hidden}
 | 
				
			||||||
          scope={{ onSelect, sideMenuRef, defaultSelectedUid }}
 | 
					            uid={route.uiSchemaUid}
 | 
				
			||||||
          schemaTransform={(data) => {
 | 
					            scope={{ onSelect, sideMenuRef, defaultSelectedUid }}
 | 
				
			||||||
            data['x-component-props']['defaultSelectedUid'] = defaultSelectedUid;
 | 
					            schemaTransform={(data) => {
 | 
				
			||||||
            return data;
 | 
					              data['x-component-props']['defaultSelectedUid'] = defaultSelectedUid;
 | 
				
			||||||
          }}
 | 
					              return data;
 | 
				
			||||||
          onSuccess={(data) => {
 | 
					            }}
 | 
				
			||||||
            if (defaultSelectedUid) {
 | 
					            onSuccess={(data) => {
 | 
				
			||||||
              return;
 | 
					              if (defaultSelectedUid) {
 | 
				
			||||||
            }
 | 
					                return;
 | 
				
			||||||
            setHidden(true);
 | 
					              }
 | 
				
			||||||
            setTimeout(() => setHidden(false), 11);
 | 
					              setHidden(true);
 | 
				
			||||||
            const s = findMenuItem(data?.data);
 | 
					              setTimeout(() => setHidden(false), 11);
 | 
				
			||||||
            if (s) {
 | 
					              const s = findMenuItem(data?.data);
 | 
				
			||||||
              setSchema(s);
 | 
					              if (s) {
 | 
				
			||||||
              history.push(`/admin/${s['x-uid']}`);
 | 
					                setSchema(s);
 | 
				
			||||||
            }
 | 
					                history.push(`/admin/${s['x-uid']}`);
 | 
				
			||||||
          }}
 | 
					              }
 | 
				
			||||||
        />
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div style={{ position: 'absolute', top: 0, right: 0 }}>
 | 
				
			||||||
 | 
					          <PluginManager.Toolbar />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </Layout.Header>
 | 
					      </Layout.Header>
 | 
				
			||||||
      <Layout>
 | 
					      <Layout>
 | 
				
			||||||
        <Layout.Sider style={{ display: 'none' }} theme={'light'} ref={sideMenuRef}></Layout.Sider>
 | 
					        <Layout.Sider style={{ display: 'none' }} theme={'light'} ref={sideMenuRef}></Layout.Sider>
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					import { observer, ISchema, useForm } from '@formily/react';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  SchemaComponent,
 | 
				
			||||||
 | 
					  SchemaComponentProvider,
 | 
				
			||||||
 | 
					  Form,
 | 
				
			||||||
 | 
					  Action,
 | 
				
			||||||
 | 
					  useActionVisible,
 | 
				
			||||||
 | 
					  VisibleContext,
 | 
				
			||||||
 | 
					} from '@nocobase/client';
 | 
				
			||||||
 | 
					import { FormItem, Input } from '@formily/antd';
 | 
				
			||||||
 | 
					import { Button } from 'antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 default observer(() => {
 | 
				
			||||||
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SchemaComponentProvider components={{ Form, Action, Input, FormItem }}>
 | 
				
			||||||
 | 
					      <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
 | 
					        <a onClick={() => setVisible(true)}>Open</a>
 | 
				
			||||||
 | 
					        <SchemaComponent scope={{ useCloseAction }} schema={schema} />
 | 
				
			||||||
 | 
					      </VisibleContext.Provider>
 | 
				
			||||||
 | 
					    </SchemaComponentProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -30,4 +30,12 @@ group:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Examples
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Action + Action.Drawer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<code src="./demos/demo1.tsx"/>
 | 
					<code src="./demos/demo1.tsx"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### VisibleContext + Action.Drawer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					只配置 Action.Drawer,而不需要 Action,通过 VisibleContext 自定义按钮。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<code src="./demos/demo2.tsx"/>
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { Button, Menu } from 'antd';
 | 
				
			||||||
 | 
					import { HighlightOutlined } from '@ant-design/icons';
 | 
				
			||||||
 | 
					import cls from 'classnames';
 | 
				
			||||||
 | 
					import { useDesignable } from '..';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DesignableSwitch = () => {
 | 
				
			||||||
 | 
					  const { designable, setDesignable } = useDesignable();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Menu.Item key={'DesignableSwitch'} eventKey={'DesignableSwitch'}>
 | 
				
			||||||
 | 
					      <HighlightOutlined />
 | 
				
			||||||
 | 
					    </Menu.Item>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -3,3 +3,4 @@ export * from './SchemaComponent';
 | 
				
			|||||||
export * from './SchemaComponentProvider';
 | 
					export * from './SchemaComponentProvider';
 | 
				
			||||||
export * from './SchemaOptionsExpressionScopeProvider';
 | 
					export * from './SchemaOptionsExpressionScopeProvider';
 | 
				
			||||||
export * from './RemoteSchemaComponent';
 | 
					export * from './RemoteSchemaComponent';
 | 
				
			||||||
 | 
					export * from './DesignableSwitch';
 | 
				
			||||||
 | 
				
			|||||||
@ -216,7 +216,7 @@ export class Designable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// TODO
 | 
					// TODO
 | 
				
			||||||
export function useDesignable() {
 | 
					export function useDesignable() {
 | 
				
			||||||
  const { designable, refresh, reset } = useContext(SchemaComponentContext);
 | 
					  const { designable, setDesignable, refresh, reset } = useContext(SchemaComponentContext);
 | 
				
			||||||
  const DesignableBar = () => {
 | 
					  const DesignableBar = () => {
 | 
				
			||||||
    return <></>;
 | 
					    return <></>;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
@ -229,6 +229,7 @@ export function useDesignable() {
 | 
				
			|||||||
    designable,
 | 
					    designable,
 | 
				
			||||||
    reset,
 | 
					    reset,
 | 
				
			||||||
    refresh,
 | 
					    refresh,
 | 
				
			||||||
 | 
					    setDesignable,
 | 
				
			||||||
    DesignableBar,
 | 
					    DesignableBar,
 | 
				
			||||||
    on: dn.on.bind(dn),
 | 
					    on: dn.on.bind(dn),
 | 
				
			||||||
    // TODO
 | 
					    // TODO
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										65
									
								
								packages/client/src/system-settings/SystemSettingsAction.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								packages/client/src/system-settings/SystemSettingsAction.tsx
									
									
									
									
									
										Normal file
									
								
							@ -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 SystemSettingsAction = () => {
 | 
				
			||||||
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
				
			||||||
 | 
					      <Menu.Item
 | 
				
			||||||
 | 
					        eventKey={'ACLAction'}
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          setVisible(true);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <LockOutlined /> 系统设置
 | 
				
			||||||
 | 
					      </Menu.Item>
 | 
				
			||||||
 | 
					      <SchemaComponent scope={{ useCloseAction }} schema={schema} />
 | 
				
			||||||
 | 
					    </VisibleContext.Provider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,3 +1,5 @@
 | 
				
			|||||||
export function SystemSettings() {
 | 
					import { SystemSettingsAction } from './SystemSettingsAction';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					export const SystemSettings = () => null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SystemSettings.Action = SystemSettingsAction;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user