diff --git a/.gitignore b/.gitignore index b0b615528..28be725f0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ dist/ docker/**/storage cache/diskstore-* *.nbdump +storage/duplicator/* \ No newline at end of file diff --git a/packages/app/client/src/plugins/multi-app-manager.ts b/packages/app/client/src/plugins/multi-app-manager.ts new file mode 100644 index 000000000..2faddb857 --- /dev/null +++ b/packages/app/client/src/plugins/multi-app-manager.ts @@ -0,0 +1 @@ +export { default } from '@nocobase/plugin-multi-app-manager/client'; \ No newline at end of file diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index b0b8e0720..2b7112abd 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -3,14 +3,13 @@ import { i18n as i18next } from 'i18next'; import React, { useEffect, useState } from 'react'; import { I18nextProvider } from 'react-i18next'; import { Link, NavLink } from 'react-router-dom'; -import { ACLProvider, ACLShortcut } from '../acl'; +import { ACLProvider } from '../acl'; import { AntdConfigProvider } from '../antd-config-provider'; import { APIClient, APIClientProvider } from '../api-client'; import { BlockSchemaComponentProvider } from '../block-provider'; -import { CollectionManagerShortcut } from '../collection-manager'; import { RemoteDocumentTitleProvider } from '../document-title'; import { i18n } from '../i18n'; -import { PluginManagerProvider } from '../plugin-manager'; +import { PinnedPluginListProvider } from '../plugin-manager'; import PMProvider, { PluginManagerLink, SettingsCenterDropdown } from '../pm'; import { AdminLayout, @@ -18,17 +17,17 @@ import { RemoteRouteSwitchProvider, RouteSchemaComponent, RouteSwitch, - useRoutes, + useRoutes } from '../route-switch'; import { AntdSchemaComponentProvider, DesignableSwitch, MenuItemInitializers, - SchemaComponentProvider, + SchemaComponentProvider } from '../schema-component'; import { SchemaInitializerProvider } from '../schema-initializer'; -import { BlockTemplateDetails, BlockTemplatePage, SchemaTemplateShortcut } from '../schema-templates'; -import { SystemSettingsProvider, SystemSettingsShortcut } from '../system-settings'; +import { BlockTemplateDetails, BlockTemplatePage } from '../schema-templates'; +import { SystemSettingsProvider } from '../system-settings'; import { SigninPage, SignupPage } from '../user'; import { SigninPageExtensionProvider } from '../user/SigninPageExtension'; import { compose } from './compose'; @@ -94,18 +93,16 @@ export class Application { }, }); this.use(SystemSettingsProvider); - this.use(PluginManagerProvider, { - components: { - ACLShortcut, - DesignableSwitch, - CollectionManagerShortcut, - SystemSettingsShortcut, - SchemaTemplateShortcut, - PluginManagerLink, - SettingsCenterDropdown, + this.use(PinnedPluginListProvider, { + items: { + ui: { order: 100, component: 'DesignableSwitch', pin: true, snippet: 'ui.*' }, + pm: { order: 200, component: 'PluginManagerLink', pin: true, snippet: 'pm' }, + sc: { order: 300, component: 'SettingsCenterDropdown', pin: true, snippet: 'pm.*' }, }, }); - this.use(SchemaComponentProvider, { components: { Link, NavLink } }); + this.use(SchemaComponentProvider, { + components: { Link, NavLink, DesignableSwitch, PluginManagerLink, SettingsCenterDropdown }, + }); this.use(SchemaInitializerProvider, { initializers: { MenuItemInitializers, diff --git a/packages/core/client/src/collection-manager/CollectionHistoryProvider.tsx b/packages/core/client/src/collection-manager/CollectionHistoryProvider.tsx index c0bb92f43..956ab63be 100644 --- a/packages/core/client/src/collection-manager/CollectionHistoryProvider.tsx +++ b/packages/core/client/src/collection-manager/CollectionHistoryProvider.tsx @@ -21,7 +21,7 @@ export const CollectionHistoryProvider: React.FC = (props) => { action: 'list', params: { paginate: false, - appends: ['fields', 'fields.uiSchema'], + appends: ['fields'], filter: { // inherit: false, }, diff --git a/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx b/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx index 0d93e6635..46ac952c7 100644 --- a/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx +++ b/packages/core/client/src/collection-manager/CollectionManagerProvider.tsx @@ -38,7 +38,7 @@ export const RemoteCollectionManagerProvider = (props: any) => { action: 'list', params: { paginate: false, - appends: ['fields', 'fields.uiSchema', 'category'], + appends: ['fields', 'category'], filter: { // inherit: false, }, diff --git a/packages/core/client/src/collection-manager/Configuration/EditFieldAction.tsx b/packages/core/client/src/collection-manager/Configuration/EditFieldAction.tsx index 656288ff7..401a09e79 100644 --- a/packages/core/client/src/collection-manager/Configuration/EditFieldAction.tsx +++ b/packages/core/client/src/collection-manager/Configuration/EditFieldAction.tsx @@ -6,7 +6,7 @@ import set from 'lodash/set'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAPIClient, useRequest } from '../../api-client'; -import { useRecord, RecordProvider } from '../../record-provider'; +import { RecordProvider, useRecord } from '../../record-provider'; import { ActionContext, SchemaComponent, useActionContext, useCompile } from '../../schema-component'; import { useCancelAction, useUpdateAction } from '../action-hooks'; import { useCollectionManager } from '../hooks'; @@ -131,7 +131,7 @@ export const EditFieldAction = (props) => { onClick={async () => { const { data } = await api.resource('collections.fields', record.collectionName).get({ filterByTk: record.name, - appends: ['uiSchema', 'reverseField'], + appends: ['reverseField'], }); setData(data?.data); const interfaceConf = getInterface(record.interface); diff --git a/packages/core/client/src/collection-manager/Configuration/OverridingCollectionField.tsx b/packages/core/client/src/collection-manager/Configuration/OverridingCollectionField.tsx index 7016a56b6..a7245cd33 100644 --- a/packages/core/client/src/collection-manager/Configuration/OverridingCollectionField.tsx +++ b/packages/core/client/src/collection-manager/Configuration/OverridingCollectionField.tsx @@ -156,7 +156,7 @@ export const OverridingFieldAction = (props) => { if (!disabled) { const { data } = await api.resource('collections.fields', record.collectionName).get({ filterByTk: record.name, - appends: ['uiSchema', 'reverseField'], + appends: ['reverseField'], }); setData(data?.data); const interfaceConf = getInterface(record.interface); diff --git a/packages/core/client/src/collection-manager/Configuration/ViewInheritedField.tsx b/packages/core/client/src/collection-manager/Configuration/ViewInheritedField.tsx index 5a8c0492a..1921c69a5 100644 --- a/packages/core/client/src/collection-manager/Configuration/ViewInheritedField.tsx +++ b/packages/core/client/src/collection-manager/Configuration/ViewInheritedField.tsx @@ -6,7 +6,7 @@ import set from 'lodash/set'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAPIClient, useRequest } from '../../api-client'; -import { useRecord, RecordProvider } from '../../record-provider'; +import { RecordProvider, useRecord } from '../../record-provider'; import { ActionContext, SchemaComponent, useCompile } from '../../schema-component'; import { useCollectionManager } from '../hooks'; import { IField } from '../interfaces/types'; @@ -82,7 +82,7 @@ export const ViewFieldAction = (props) => { onClick={async () => { const { data } = await api.resource('collections.fields', record.collectionName).get({ filterByTk: record.name, - appends: ['uiSchema', 'reverseField'], + appends: ['reverseField'], }); setData(data?.data); const interfaceConf = getInterface(record.interface); diff --git a/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts b/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts index 4ab4bef1f..fdfbdef60 100644 --- a/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts +++ b/packages/core/client/src/collection-manager/Configuration/schemas/collectionFields.ts @@ -76,7 +76,7 @@ export const collectionFieldSchema: ISchema = { 'interface.$not': null, }, sort: ['sort'], - appends: ['uiSchema'], + // appends: ['uiSchema'], }, }, }, diff --git a/packages/core/client/src/collection-manager/action-hooks.ts b/packages/core/client/src/collection-manager/action-hooks.ts index 3fe8d3036..b50556804 100644 --- a/packages/core/client/src/collection-manager/action-hooks.ts +++ b/packages/core/client/src/collection-manager/action-hooks.ts @@ -1,4 +1,4 @@ -import { useForm } from '@formily/react'; +import { useField, useForm } from '@formily/react'; import { message } from 'antd'; import { useEffect } from 'react'; import { useCollection, useCollectionManager } from '.'; @@ -185,15 +185,19 @@ export const useFilterAction = () => { export const useCreateAction = () => { const form = useForm(); + const field = useField(); const ctx = useActionContext(); const { refresh } = useResourceActionContext(); const { resource } = useResourceContext(); return { async run() { await form.submit(); + field.data = field.data || {}; + field.data.loading = true; await resource.create({ values: form.values }); ctx.setVisible(false); await form.reset(); + field.data.loading = false; refresh(); }, }; @@ -242,6 +246,7 @@ export const useMoveAction = () => { }; export const useUpdateAction = () => { + const field = useField(); const form = useForm(); const ctx = useActionContext(); const { refresh } = useResourceActionContext(); @@ -250,9 +255,12 @@ export const useUpdateAction = () => { return { async run() { await form.submit(); + field.data = field.data || {}; + field.data.loading = true; await resource.update({ filterByTk, values: form.values }); ctx.setVisible(false); await form.reset(); + field.data.loading = false; refresh(); }, }; diff --git a/packages/core/client/src/collection-manager/sub-table.tsx b/packages/core/client/src/collection-manager/sub-table.tsx index e78fc8329..32394c7be 100644 --- a/packages/core/client/src/collection-manager/sub-table.tsx +++ b/packages/core/client/src/collection-manager/sub-table.tsx @@ -8,7 +8,7 @@ import { useCollectionManager, useRecord, useRecordIndex, - useRequest, + useRequest } from '../'; import { useAPIClient } from '../api-client'; @@ -183,7 +183,7 @@ export const SubFieldDataSourceProvider = observer((props) => { .resource('fields') .list({ paginate: false, - appends: ['uiSchema'], + // appends: ['uiSchema'], sort: 'sort', filter: { parentKey: record.key, diff --git a/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx b/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx new file mode 100644 index 000000000..a4a2c9fc2 --- /dev/null +++ b/packages/core/client/src/plugin-manager/PinnedPluginListProvider.tsx @@ -0,0 +1,51 @@ +import { css } from '@emotion/css'; +import { SchemaOptionsContext } from '@formily/react'; +import { get } from 'lodash'; +import React, { useContext } from 'react'; +import { useACLRoleContext } from '../acl/ACLProvider'; +import { PinnedPluginListContext } from './context'; + +export const PinnedPluginListProvider: React.FC<{ items: any }> = (props) => { + const { children, items } = props; + const ctx = useContext(PinnedPluginListContext); + return ( + + {children} + + ); +}; + +export const PinnedPluginList = () => { + const { allowAll, snippets } = useACLRoleContext(); + const getSnippetsAllow = (aclKey) => { + return allowAll || snippets?.includes(aclKey); + }; + const ctx = useContext(PinnedPluginListContext); + const { components } = useContext(SchemaOptionsContext); + return ( +
+ {Object.values(ctx.items) + .sort((a, b) => a.order - b.order) + .filter((v) => getSnippetsAllow(v.snippet)) + .map((item) => { + const Action = get(components, item.component); + return Action ? : null; + })} +
+ ); +}; diff --git a/packages/core/client/src/plugin-manager/context.ts b/packages/core/client/src/plugin-manager/context.ts index 3220782c6..11d864925 100644 --- a/packages/core/client/src/plugin-manager/context.ts +++ b/packages/core/client/src/plugin-manager/context.ts @@ -1,3 +1,4 @@ import { createContext } from 'react'; export const PluginManagerContext = createContext({}); +export const PinnedPluginListContext = createContext({ items: {} }); diff --git a/packages/core/client/src/plugin-manager/index.ts b/packages/core/client/src/plugin-manager/index.ts index dfbf7596f..74f96aa39 100644 --- a/packages/core/client/src/plugin-manager/index.ts +++ b/packages/core/client/src/plugin-manager/index.ts @@ -1,3 +1,5 @@ export * from './context'; +export * from './PinnedPluginListProvider'; export * from './PluginManager'; export * from './PluginManagerProvider'; + diff --git a/packages/core/client/src/pm/PluginManagerLink.tsx b/packages/core/client/src/pm/PluginManagerLink.tsx index bf06c0332..4192d3250 100644 --- a/packages/core/client/src/pm/PluginManagerLink.tsx +++ b/packages/core/client/src/pm/PluginManagerLink.tsx @@ -1,27 +1,23 @@ -import { AppstoreAddOutlined, SettingOutlined } from '@ant-design/icons'; -import { Dropdown, Menu } from 'antd'; +import { ApiOutlined, SettingOutlined } from '@ant-design/icons'; +import { Button, Dropdown, Menu } from 'antd'; import React, { useContext, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router-dom'; import { useACLRoleContext } from '../acl/ACLProvider'; -import { PluginManager } from '../plugin-manager'; import { ActionContext, useCompile } from '../schema-component'; import { getPluginsTabs, SettingsCenterContext } from './index'; export const PluginManagerLink = () => { - const [visible, setVisible] = useState(false); const { t } = useTranslation(); const history = useHistory(); return ( - - } - title={t('Plugin manager')} - onClick={() => { - history.push('/admin/pm/list'); - }} - /> - +