feat: block templates (#240)
* feat: block templates * feat: improve code * feat: improve code * feat: schema settings for block templates * feat: improve code * feat: transition * feat: transition
This commit is contained in:
		
							parent
							
								
									5c92d3ba46
								
							
						
					
					
						commit
						2fb019c392
					
				@ -5,6 +5,8 @@ import {
 | 
				
			|||||||
  AntdSchemaComponentProvider,
 | 
					  AntdSchemaComponentProvider,
 | 
				
			||||||
  APIClientProvider,
 | 
					  APIClientProvider,
 | 
				
			||||||
  AuthLayout,
 | 
					  AuthLayout,
 | 
				
			||||||
 | 
					  BlockTemplateDetails,
 | 
				
			||||||
 | 
					  BlockTemplatePage,
 | 
				
			||||||
  ChinaRegionProvider,
 | 
					  ChinaRegionProvider,
 | 
				
			||||||
  CollectionManagerShortcut,
 | 
					  CollectionManagerShortcut,
 | 
				
			||||||
  compose,
 | 
					  compose,
 | 
				
			||||||
@ -13,19 +15,20 @@ import {
 | 
				
			|||||||
  i18n,
 | 
					  i18n,
 | 
				
			||||||
  MenuItemInitializers,
 | 
					  MenuItemInitializers,
 | 
				
			||||||
  PluginManagerProvider,
 | 
					  PluginManagerProvider,
 | 
				
			||||||
 | 
					  RemoteRouteSwitchProvider,
 | 
				
			||||||
  // RemoteCollectionManagerProvider,
 | 
					  // RemoteCollectionManagerProvider,
 | 
				
			||||||
  RouteSchemaComponent,
 | 
					  RouteSchemaComponent,
 | 
				
			||||||
  RouteSwitch,
 | 
					  RouteSwitch,
 | 
				
			||||||
  RouteSwitchProvider,
 | 
					 | 
				
			||||||
  SchemaComponentProvider,
 | 
					  SchemaComponentProvider,
 | 
				
			||||||
  SchemaInitializerProvider,
 | 
					  SchemaInitializerProvider,
 | 
				
			||||||
 | 
					  SchemaTemplateShortcut,
 | 
				
			||||||
  SigninPage,
 | 
					  SigninPage,
 | 
				
			||||||
  SignupPage,
 | 
					  SignupPage,
 | 
				
			||||||
  SystemSettingsProvider,
 | 
					  SystemSettingsProvider,
 | 
				
			||||||
  SystemSettingsShortcut,
 | 
					  SystemSettingsShortcut,
 | 
				
			||||||
  useRequest
 | 
					  useRoutes
 | 
				
			||||||
} from '@nocobase/client';
 | 
					} from '@nocobase/client';
 | 
				
			||||||
import { notification, Spin } from 'antd';
 | 
					import { notification } from 'antd';
 | 
				
			||||||
import 'antd/dist/antd.css';
 | 
					import 'antd/dist/antd.css';
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { I18nextProvider } from 'react-i18next';
 | 
					import { I18nextProvider } from 'react-i18next';
 | 
				
			||||||
@ -50,6 +53,20 @@ const providers = [
 | 
				
			|||||||
  [APIClientProvider, { apiClient }],
 | 
					  [APIClientProvider, { apiClient }],
 | 
				
			||||||
  [I18nextProvider, { i18n }],
 | 
					  [I18nextProvider, { i18n }],
 | 
				
			||||||
  [AntdConfigProvider, { remoteLocale: true }],
 | 
					  [AntdConfigProvider, { remoteLocale: true }],
 | 
				
			||||||
 | 
					  [
 | 
				
			||||||
 | 
					    RemoteRouteSwitchProvider,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      components: {
 | 
				
			||||||
 | 
					        AuthLayout,
 | 
				
			||||||
 | 
					        AdminLayout,
 | 
				
			||||||
 | 
					        RouteSchemaComponent,
 | 
				
			||||||
 | 
					        SigninPage,
 | 
				
			||||||
 | 
					        SignupPage,
 | 
				
			||||||
 | 
					        BlockTemplatePage,
 | 
				
			||||||
 | 
					        BlockTemplateDetails,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  SystemSettingsProvider,
 | 
					  SystemSettingsProvider,
 | 
				
			||||||
  [
 | 
					  [
 | 
				
			||||||
    PluginManagerProvider,
 | 
					    PluginManagerProvider,
 | 
				
			||||||
@ -59,39 +76,30 @@ const providers = [
 | 
				
			|||||||
        DesignableSwitch,
 | 
					        DesignableSwitch,
 | 
				
			||||||
        CollectionManagerShortcut,
 | 
					        CollectionManagerShortcut,
 | 
				
			||||||
        SystemSettingsShortcut,
 | 
					        SystemSettingsShortcut,
 | 
				
			||||||
 | 
					        SchemaTemplateShortcut,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  [SchemaComponentProvider, { components: { Link, NavLink } }],
 | 
					  [SchemaComponentProvider, { components: { Link, NavLink } }],
 | 
				
			||||||
  // RemoteCollectionManagerProvider,
 | 
					  // RemoteCollectionManagerProvider,
 | 
				
			||||||
  [SchemaInitializerProvider, { initializers: { MenuItemInitializers } }],
 | 
					 | 
				
			||||||
  AntdSchemaComponentProvider,
 | 
					 | 
				
			||||||
  ChinaRegionProvider,
 | 
					 | 
				
			||||||
  [DocumentTitleProvider, { addonAfter: 'NocoBase' }],
 | 
					 | 
				
			||||||
  [
 | 
					  [
 | 
				
			||||||
    RouteSwitchProvider,
 | 
					    SchemaInitializerProvider,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      components: {
 | 
					      initializers: {
 | 
				
			||||||
        AuthLayout,
 | 
					        MenuItemInitializers,
 | 
				
			||||||
        AdminLayout,
 | 
					 | 
				
			||||||
        RouteSchemaComponent,
 | 
					 | 
				
			||||||
        SigninPage,
 | 
					 | 
				
			||||||
        SignupPage,
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
 | 
					  AntdSchemaComponentProvider,
 | 
				
			||||||
 | 
					  ChinaRegionProvider,
 | 
				
			||||||
 | 
					  [DocumentTitleProvider, { addonAfter: 'NocoBase' }],
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const App = compose(...providers)(() => {
 | 
					const App = compose(...providers)(() => {
 | 
				
			||||||
  const { data, loading } = useRequest({
 | 
					  const routes = useRoutes();
 | 
				
			||||||
    url: 'uiRoutes:getAccessible',
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  if (loading) {
 | 
					 | 
				
			||||||
    return <Spin />;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <RouteSwitch routes={data?.data || []} />
 | 
					      <RouteSwitch routes={routes} />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ export * from './route-switch';
 | 
				
			|||||||
export * from './schema-component';
 | 
					export * from './schema-component';
 | 
				
			||||||
export * from './schema-initializer';
 | 
					export * from './schema-initializer';
 | 
				
			||||||
export * from './schema-settings';
 | 
					export * from './schema-settings';
 | 
				
			||||||
 | 
					export * from './schema-templates';
 | 
				
			||||||
export * from './settings-form';
 | 
					export * from './settings-form';
 | 
				
			||||||
export * from './system-settings';
 | 
					export * from './system-settings';
 | 
				
			||||||
export * from './user';
 | 
					export * from './user';
 | 
				
			||||||
 | 
				
			|||||||
@ -279,5 +279,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  "Add card": "添加卡片",
 | 
					  "Add card": "添加卡片",
 | 
				
			||||||
  "edit title": "修改标题",
 | 
					  "edit title": "修改标题",
 | 
				
			||||||
  "turn page": "翻页"
 | 
					  "turn page": "翻页",
 | 
				
			||||||
 | 
					  "Save as template": "保存为模板",
 | 
				
			||||||
 | 
					  "Block templates": "区块模板",
 | 
				
			||||||
 | 
					  "Convert reference to duplicate": "模板引用转为复制",
 | 
				
			||||||
 | 
					  "Template name": "模板名称",
 | 
				
			||||||
 | 
					  "Block type": "区块类型"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,20 @@
 | 
				
			|||||||
 | 
					import { Spin } from 'antd';
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { useRequest } from '../api-client';
 | 
				
			||||||
import { RouteSwitchContext } from './context';
 | 
					import { RouteSwitchContext } from './context';
 | 
				
			||||||
import { RouteSwitchProviderProps } from './types';
 | 
					import { RouteSwitchProviderProps } from './types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function RouteSwitchProvider(props: RouteSwitchProviderProps) {
 | 
					export function RouteSwitchProvider(props: RouteSwitchProviderProps) {
 | 
				
			||||||
  const { children, components } = props;
 | 
					  const { children, components, routes } = props;
 | 
				
			||||||
  return <RouteSwitchContext.Provider value={{ components }}>{children}</RouteSwitchContext.Provider>;
 | 
					  return <RouteSwitchContext.Provider value={{ routes, components }}>{children}</RouteSwitchContext.Provider>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function RemoteRouteSwitchProvider(props: RouteSwitchProviderProps) {
 | 
				
			||||||
 | 
					  const { data, loading } = useRequest({
 | 
				
			||||||
 | 
					    url: 'uiRoutes:getAccessible',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  if (loading) {
 | 
				
			||||||
 | 
					    return <Spin />;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return <RouteSwitchProvider {...props} routes={data?.data || []} />;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ import {
 | 
				
			|||||||
  PluginManager,
 | 
					  PluginManager,
 | 
				
			||||||
  RemoteCollectionManagerProvider,
 | 
					  RemoteCollectionManagerProvider,
 | 
				
			||||||
  RemoteSchemaComponent,
 | 
					  RemoteSchemaComponent,
 | 
				
			||||||
 | 
					  RemoteSchemaTemplateManagerProvider,
 | 
				
			||||||
  useDocumentTitle,
 | 
					  useDocumentTitle,
 | 
				
			||||||
  useRoute,
 | 
					  useRoute,
 | 
				
			||||||
  useSystemSettings
 | 
					  useSystemSettings
 | 
				
			||||||
@ -94,6 +95,7 @@ const InternalAdminLayout = (props: any) => {
 | 
				
			|||||||
              { component: 'DesignableSwitch', pin: true },
 | 
					              { component: 'DesignableSwitch', pin: true },
 | 
				
			||||||
              { component: 'CollectionManagerShortcut', pin: true },
 | 
					              { component: 'CollectionManagerShortcut', pin: true },
 | 
				
			||||||
              { component: 'ACLShortcut', pin: true },
 | 
					              { component: 'ACLShortcut', pin: true },
 | 
				
			||||||
 | 
					              { component: 'SchemaTemplateShortcut', pin: true },
 | 
				
			||||||
              { component: 'SystemSettingsShortcut' },
 | 
					              { component: 'SystemSettingsShortcut' },
 | 
				
			||||||
            ]}
 | 
					            ]}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
@ -110,11 +112,13 @@ const InternalAdminLayout = (props: any) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const AdminLayout = (props) => {
 | 
					export const AdminLayout = (props) => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <RemoteCollectionManagerProvider>
 | 
					    <RemoteSchemaTemplateManagerProvider>
 | 
				
			||||||
      <CurrentUserProvider>
 | 
					      <RemoteCollectionManagerProvider>
 | 
				
			||||||
        <InternalAdminLayout {...props} />
 | 
					        <CurrentUserProvider>
 | 
				
			||||||
      </CurrentUserProvider>
 | 
					          <InternalAdminLayout {...props} />
 | 
				
			||||||
    </RemoteCollectionManagerProvider>
 | 
					        </CurrentUserProvider>
 | 
				
			||||||
 | 
					      </RemoteCollectionManagerProvider>
 | 
				
			||||||
 | 
					    </RemoteSchemaTemplateManagerProvider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ import { RouteProps } from './types';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const RouteSwitchContext = createContext({
 | 
					export const RouteSwitchContext = createContext({
 | 
				
			||||||
  components: {},
 | 
					  components: {},
 | 
				
			||||||
 | 
					  routes: [],
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const RouteContext = createContext<RouteProps>(null);
 | 
					export const RouteContext = createContext<RouteProps>(null);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
import { useContext } from 'react';
 | 
					 | 
				
			||||||
import get from 'lodash/get';
 | 
					import get from 'lodash/get';
 | 
				
			||||||
 | 
					import { useContext } from 'react';
 | 
				
			||||||
import { RouteContext, RouteSwitchContext } from './context';
 | 
					import { RouteContext, RouteSwitchContext } from './context';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function useRouteComponent(name?: string) {
 | 
					export function useRouteComponent(name?: string) {
 | 
				
			||||||
@ -13,3 +13,8 @@ export function useRouteComponent(name?: string) {
 | 
				
			|||||||
export function useRoute() {
 | 
					export function useRoute() {
 | 
				
			||||||
  return useContext(RouteContext);
 | 
					  return useContext(RouteContext);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useRoutes() {
 | 
				
			||||||
 | 
					  const { routes } = useContext(RouteSwitchContext);
 | 
				
			||||||
 | 
					  return routes || [];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -23,6 +23,7 @@ export interface RouteProps {
 | 
				
			|||||||
export interface RouteSwitchProviderProps {
 | 
					export interface RouteSwitchProviderProps {
 | 
				
			||||||
  components?: any;
 | 
					  components?: any;
 | 
				
			||||||
  children?: any;
 | 
					  children?: any;
 | 
				
			||||||
 | 
					  routes?: RouteRedirectProps[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type RouteRedirectProps = RedirectProps | RouteProps;
 | 
					export type RouteRedirectProps = RedirectProps | RouteProps;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import { useCompile, useDesignable } from '../..';
 | 
				
			|||||||
import { useCollection, useResourceActionContext } from '../../../collection-manager';
 | 
					import { useCollection, useResourceActionContext } from '../../../collection-manager';
 | 
				
			||||||
import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
 | 
					import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
 | 
				
			||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
					import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
				
			||||||
 | 
					import { useSchemaTemplate } from '../../../schema-templates';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CalendarDesigner = () => {
 | 
					export const CalendarDesigner = () => {
 | 
				
			||||||
  const field = useField();
 | 
					  const field = useField();
 | 
				
			||||||
@ -13,6 +14,7 @@ export const CalendarDesigner = () => {
 | 
				
			|||||||
  const ctx = useResourceActionContext();
 | 
					  const ctx = useResourceActionContext();
 | 
				
			||||||
  const { dn } = useDesignable();
 | 
					  const { dn } = useDesignable();
 | 
				
			||||||
  const compile = useCompile();
 | 
					  const compile = useCompile();
 | 
				
			||||||
 | 
					  const template = useSchemaTemplate();
 | 
				
			||||||
  const defaultFilter = fieldSchema?.['x-decorator-props']?.request?.params?.filter || {};
 | 
					  const defaultFilter = fieldSchema?.['x-decorator-props']?.request?.params?.filter || {};
 | 
				
			||||||
  const options = fields?.map((field) => {
 | 
					  const options = fields?.map((field) => {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
@ -23,7 +25,7 @@ export const CalendarDesigner = () => {
 | 
				
			|||||||
  const calendarSchema = fieldSchema.properties.calendar;
 | 
					  const calendarSchema = fieldSchema.properties.calendar;
 | 
				
			||||||
  const fieldNames = calendarSchema?.['x-component-props']?.['fieldNames'] || {};
 | 
					  const fieldNames = calendarSchema?.['x-component-props']?.['fieldNames'] || {};
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <GeneralSchemaDesigner title={title || name}>
 | 
					    <GeneralSchemaDesigner template={template} title={title || name}>
 | 
				
			||||||
      <SchemaSettings.SelectItem
 | 
					      <SchemaSettings.SelectItem
 | 
				
			||||||
        title={'标题字段'}
 | 
					        title={'标题字段'}
 | 
				
			||||||
        value={fieldNames.title}
 | 
					        value={fieldNames.title}
 | 
				
			||||||
@ -128,6 +130,8 @@ export const CalendarDesigner = () => {
 | 
				
			|||||||
        }}
 | 
					        }}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <SchemaSettings.Divider />
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
 | 
					      <SchemaSettings.Template componentName={'Calendar'} collectionName={name} />
 | 
				
			||||||
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
      <SchemaSettings.Remove
 | 
					      <SchemaSettings.Remove
 | 
				
			||||||
        removeParentsIfNoChildren
 | 
					        removeParentsIfNoChildren
 | 
				
			||||||
        breakRemoveOn={{
 | 
					        breakRemoveOn={{
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,8 @@ export const FormDesigner = () => {
 | 
				
			|||||||
  const { name, title } = useCollection();
 | 
					  const { name, title } = useCollection();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <GeneralSchemaDesigner title={title || name}>
 | 
					    <GeneralSchemaDesigner title={title || name}>
 | 
				
			||||||
 | 
					      <SchemaSettings.Template componentName={'Form'} collectionName={name} />
 | 
				
			||||||
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
      <SchemaSettings.Remove
 | 
					      <SchemaSettings.Remove
 | 
				
			||||||
        removeParentsIfNoChildren
 | 
					        removeParentsIfNoChildren
 | 
				
			||||||
        breakRemoveOn={{
 | 
					        breakRemoveOn={{
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import { useAttach, useComponent } from '../..';
 | 
				
			|||||||
import { useRequest } from '../../../api-client';
 | 
					import { useRequest } from '../../../api-client';
 | 
				
			||||||
import { useCollection } from '../../../collection-manager';
 | 
					import { useCollection } from '../../../collection-manager';
 | 
				
			||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
					import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
				
			||||||
 | 
					import { useSchemaTemplate } from '../../../schema-templates';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Opts = Options<any, any> & { uid?: string };
 | 
					type Opts = Options<any, any> & { uid?: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -113,8 +114,11 @@ export const Form: React.FC<FormProps> & { Designer?: any } = observer((props) =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Form.Designer = () => {
 | 
					Form.Designer = () => {
 | 
				
			||||||
  const { name, title } = useCollection();
 | 
					  const { name, title } = useCollection();
 | 
				
			||||||
 | 
					  const template = useSchemaTemplate();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <GeneralSchemaDesigner title={title || name}>
 | 
					    <GeneralSchemaDesigner template={template} title={title || name}>
 | 
				
			||||||
 | 
					      <SchemaSettings.Template componentName={'Form'} collectionName={name} />
 | 
				
			||||||
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
      <SchemaSettings.Remove
 | 
					      <SchemaSettings.Remove
 | 
				
			||||||
        removeParentsIfNoChildren
 | 
					        removeParentsIfNoChildren
 | 
				
			||||||
        breakRemoveOn={{
 | 
					        breakRemoveOn={{
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
 | 
				
			|||||||
import { useCollection, useResourceActionContext } from '../../../collection-manager';
 | 
					import { useCollection, useResourceActionContext } from '../../../collection-manager';
 | 
				
			||||||
import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
 | 
					import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
 | 
				
			||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
					import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
				
			||||||
 | 
					import { useSchemaTemplate } from '../../../schema-templates';
 | 
				
			||||||
import { useDesignable } from '../../hooks';
 | 
					import { useDesignable } from '../../hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const KanbanDesigner = () => {
 | 
					export const KanbanDesigner = () => {
 | 
				
			||||||
@ -15,8 +16,9 @@ export const KanbanDesigner = () => {
 | 
				
			|||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
  const { dn } = useDesignable();
 | 
					  const { dn } = useDesignable();
 | 
				
			||||||
  const defaultFilter = fieldSchema?.['x-decorator-props']?.request?.params?.filter || {};
 | 
					  const defaultFilter = fieldSchema?.['x-decorator-props']?.request?.params?.filter || {};
 | 
				
			||||||
 | 
					  const template = useSchemaTemplate();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <GeneralSchemaDesigner title={title || name}>
 | 
					    <GeneralSchemaDesigner template={template} title={title || name}>
 | 
				
			||||||
      <SchemaSettings.ModalItem
 | 
					      <SchemaSettings.ModalItem
 | 
				
			||||||
        title={'设置数据范围'}
 | 
					        title={'设置数据范围'}
 | 
				
			||||||
        schema={
 | 
					        schema={
 | 
				
			||||||
@ -49,6 +51,8 @@ export const KanbanDesigner = () => {
 | 
				
			|||||||
        }}
 | 
					        }}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <SchemaSettings.Divider />
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
 | 
					      <SchemaSettings.Template componentName={'Kanban'} collectionName={name} />
 | 
				
			||||||
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
      <SchemaSettings.Remove
 | 
					      <SchemaSettings.Remove
 | 
				
			||||||
        removeParentsIfNoChildren
 | 
					        removeParentsIfNoChildren
 | 
				
			||||||
        breakRemoveOn={{
 | 
					        breakRemoveOn={{
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
 | 
				
			|||||||
import { useCollection, useResourceActionContext } from '../../../collection-manager';
 | 
					import { useCollection, useResourceActionContext } from '../../../collection-manager';
 | 
				
			||||||
import { useCollectionFilterOptions, useSortFields } from '../../../collection-manager/action-hooks';
 | 
					import { useCollectionFilterOptions, useSortFields } from '../../../collection-manager/action-hooks';
 | 
				
			||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
					import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
 | 
				
			||||||
 | 
					import { useSchemaTemplate } from '../../../schema-templates';
 | 
				
			||||||
import { useDesignable } from '../../hooks';
 | 
					import { useDesignable } from '../../hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const TableVoidDesigner = () => {
 | 
					export const TableVoidDesigner = () => {
 | 
				
			||||||
@ -29,8 +30,9 @@ export const TableVoidDesigner = () => {
 | 
				
			|||||||
          direction: 'asc',
 | 
					          direction: 'asc',
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					  const template = useSchemaTemplate();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <GeneralSchemaDesigner title={title || name}>
 | 
					    <GeneralSchemaDesigner template={template} title={title || name}>
 | 
				
			||||||
      <SchemaSettings.SwitchItem
 | 
					      <SchemaSettings.SwitchItem
 | 
				
			||||||
        title={t('Enable drag and drop sorting')}
 | 
					        title={t('Enable drag and drop sorting')}
 | 
				
			||||||
        checked={field.decoratorProps.dragSort}
 | 
					        checked={field.decoratorProps.dragSort}
 | 
				
			||||||
@ -193,6 +195,8 @@ export const TableVoidDesigner = () => {
 | 
				
			|||||||
        }}
 | 
					        }}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      <SchemaSettings.Divider />
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
 | 
					      <SchemaSettings.Template componentName={'Table'} collectionName={name}/>
 | 
				
			||||||
 | 
					      <SchemaSettings.Divider />
 | 
				
			||||||
      <SchemaSettings.Remove
 | 
					      <SchemaSettings.Remove
 | 
				
			||||||
        removeParentsIfNoChildren
 | 
					        removeParentsIfNoChildren
 | 
				
			||||||
        breakRemoveOn={{
 | 
					        breakRemoveOn={{
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ interface CreateDesignableProps {
 | 
				
			|||||||
  current: Schema;
 | 
					  current: Schema;
 | 
				
			||||||
  api?: APIClient;
 | 
					  api?: APIClient;
 | 
				
			||||||
  refresh?: () => void;
 | 
					  refresh?: () => void;
 | 
				
			||||||
 | 
					  onSuccess?: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function createDesignable(options: CreateDesignableProps) {
 | 
					export function createDesignable(options: CreateDesignableProps) {
 | 
				
			||||||
@ -26,6 +27,7 @@ interface InsertAdjacentOptions {
 | 
				
			|||||||
  wrap?: (s: ISchema) => ISchema;
 | 
					  wrap?: (s: ISchema) => ISchema;
 | 
				
			||||||
  removeParentsIfNoChildren?: boolean;
 | 
					  removeParentsIfNoChildren?: boolean;
 | 
				
			||||||
  breakRemoveOn?: ISchema | BreakFn;
 | 
					  breakRemoveOn?: ISchema | BreakFn;
 | 
				
			||||||
 | 
					  onSuccess?: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BreakFn = (s: ISchema) => boolean;
 | 
					type BreakFn = (s: ISchema) => boolean;
 | 
				
			||||||
@ -80,7 +82,7 @@ export class Designable {
 | 
				
			|||||||
    if (!api) {
 | 
					    if (!api) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.on('insertAdjacent', async ({ current, position, schema, removed }) => {
 | 
					    this.on('insertAdjacent', async ({ onSuccess, current, position, schema, removed }) => {
 | 
				
			||||||
      refresh();
 | 
					      refresh();
 | 
				
			||||||
      await api.request({
 | 
					      await api.request({
 | 
				
			||||||
        url: `/uiSchemas:insertAdjacent/${current['x-uid']}?position=${position}`,
 | 
					        url: `/uiSchemas:insertAdjacent/${current['x-uid']}?position=${position}`,
 | 
				
			||||||
@ -93,6 +95,7 @@ export class Designable {
 | 
				
			|||||||
          method: 'post',
 | 
					          method: 'post',
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      onSuccess?.();
 | 
				
			||||||
      message.success('配置保存成功!', 0.2);
 | 
					      message.success('配置保存成功!', 0.2);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    this.on('patch', async ({ schema }) => {
 | 
					    this.on('patch', async ({ schema }) => {
 | 
				
			||||||
@ -229,6 +232,19 @@ export class Designable {
 | 
				
			|||||||
    this.emit('remove', { removed });
 | 
					    this.emit('remove', { removed });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  removeWithoutEmit(schema?: Schema, options: RemoveOptions = {}) {
 | 
				
			||||||
 | 
					    const { breakRemoveOn, removeParentsIfNoChildren } = options;
 | 
				
			||||||
 | 
					    let s = schema || this.current;
 | 
				
			||||||
 | 
					    let removed = s.parent.removeProperty(s.name);
 | 
				
			||||||
 | 
					    if (removeParentsIfNoChildren) {
 | 
				
			||||||
 | 
					      const parent = this.recursiveRemoveIfNoChildren(s.parent, { breakRemoveOn });
 | 
				
			||||||
 | 
					      if (parent) {
 | 
				
			||||||
 | 
					        removed = parent;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return removed;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  insertBeforeBeginOrAfterEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
 | 
					  insertBeforeBeginOrAfterEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
 | 
				
			||||||
    if (!Schema.isSchemaInstance(this.current)) {
 | 
					    if (!Schema.isSchemaInstance(this.current)) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
@ -393,7 +409,7 @@ export class Designable {
 | 
				
			|||||||
    if (!Schema.isSchemaInstance(this.current)) {
 | 
					    if (!Schema.isSchemaInstance(this.current)) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const opts = {};
 | 
					    const opts = { onSuccess: options?.onSuccess };
 | 
				
			||||||
    const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
 | 
					    const { wrap = defaultWrap, breakRemoveOn, removeParentsIfNoChildren } = options;
 | 
				
			||||||
    if (Schema.isSchemaInstance(schema)) {
 | 
					    if (Schema.isSchemaInstance(schema)) {
 | 
				
			||||||
      if (this.parentsIn(schema)) {
 | 
					      if (this.parentsIn(schema)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,9 @@ import React, { useContext } from 'react';
 | 
				
			|||||||
import { useTranslation } from 'react-i18next';
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { useCollectionManager } from '../../../collection-manager';
 | 
					import { useCollectionManager } from '../../../collection-manager';
 | 
				
			||||||
import { SchemaComponent, SchemaComponentOptions } from '../../../schema-component';
 | 
					import { SchemaComponent, SchemaComponentOptions } from '../../../schema-component';
 | 
				
			||||||
 | 
					import { useSchemaTemplateManager } from '../../../schema-templates';
 | 
				
			||||||
import { SchemaInitializer } from '../../SchemaInitializer';
 | 
					import { SchemaInitializer } from '../../SchemaInitializer';
 | 
				
			||||||
 | 
					import { useCollectionDataSourceItems } from '../utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createSchema = (collectionName, { title, start, end }) => {
 | 
					const createSchema = (collectionName, { title, start, end }) => {
 | 
				
			||||||
  const schema: ISchema = {
 | 
					  const schema: ISchema = {
 | 
				
			||||||
@ -102,11 +104,17 @@ export const CalendarBlockInitializer = (props) => {
 | 
				
			|||||||
  const { collections, getCollection } = useCollectionManager();
 | 
					  const { collections, getCollection } = useCollectionManager();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
  const options = useContext(SchemaOptionsContext);
 | 
					  const options = useContext(SchemaOptionsContext);
 | 
				
			||||||
 | 
					  const { getTemplateSchemaByMode } = useSchemaTemplateManager();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SchemaInitializer.Item
 | 
					    <SchemaInitializer.Item
 | 
				
			||||||
      {...props}
 | 
					      {...props}
 | 
				
			||||||
      icon={<FormOutlined />}
 | 
					      icon={<FormOutlined />}
 | 
				
			||||||
      onClick={async ({ item }) => {
 | 
					      onClick={async ({ item }) => {
 | 
				
			||||||
 | 
					        if (item.template) {
 | 
				
			||||||
 | 
					          const s = await getTemplateSchemaByMode(item);
 | 
				
			||||||
 | 
					          insert(s);
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        const collection = getCollection(item.name);
 | 
					        const collection = getCollection(item.name);
 | 
				
			||||||
        const stringFields = collection?.fields
 | 
					        const stringFields = collection?.fields
 | 
				
			||||||
          ?.filter((field) => field.type === 'string')
 | 
					          ?.filter((field) => field.type === 'string')
 | 
				
			||||||
@ -161,23 +169,10 @@ export const CalendarBlockInitializer = (props) => {
 | 
				
			|||||||
        }).open({
 | 
					        }).open({
 | 
				
			||||||
          initialValues: {},
 | 
					          initialValues: {},
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        insert(createSchema(item.name, values));
 | 
					        insert(createSchema(item.name, values));
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
      items={[
 | 
					      items={useCollectionDataSourceItems('Calendar')}
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          type: 'itemGroup',
 | 
					 | 
				
			||||||
          title: t('Select data source'),
 | 
					 | 
				
			||||||
          children: collections
 | 
					 | 
				
			||||||
            ?.filter((item) => !item.inherit)
 | 
					 | 
				
			||||||
            ?.map((item) => {
 | 
					 | 
				
			||||||
              return {
 | 
					 | 
				
			||||||
                type: 'item',
 | 
					 | 
				
			||||||
                name: item.name,
 | 
					 | 
				
			||||||
                title: item.title,
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ]}
 | 
					 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,8 @@ import React from 'react';
 | 
				
			|||||||
import { useTranslation } from 'react-i18next';
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { SchemaInitializer } from '../..';
 | 
					import { SchemaInitializer } from '../..';
 | 
				
			||||||
import { useCollectionManager } from '../../../collection-manager';
 | 
					import { useCollectionManager } from '../../../collection-manager';
 | 
				
			||||||
 | 
					import { useSchemaTemplateManager } from '../../../schema-templates';
 | 
				
			||||||
 | 
					import { useCollectionDataSourceItems } from '../utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createSchema = (collectionName) => {
 | 
					const createSchema = (collectionName) => {
 | 
				
			||||||
  const schema: ISchema = {
 | 
					  const schema: ISchema = {
 | 
				
			||||||
@ -55,28 +57,20 @@ export const FormBlockInitializer = (props) => {
 | 
				
			|||||||
  const { insert } = props;
 | 
					  const { insert } = props;
 | 
				
			||||||
  const { collections } = useCollectionManager();
 | 
					  const { collections } = useCollectionManager();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const { getTemplateSchemaByMode } = useSchemaTemplateManager();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SchemaInitializer.Item
 | 
					    <SchemaInitializer.Item
 | 
				
			||||||
      {...props}
 | 
					      {...props}
 | 
				
			||||||
      icon={<FormOutlined />}
 | 
					      icon={<FormOutlined />}
 | 
				
			||||||
      onClick={({ item }) => {
 | 
					      onClick={async ({ item }) => {
 | 
				
			||||||
        insert(createSchema(item.name));
 | 
					        if (item.template) {
 | 
				
			||||||
 | 
					          const s = await getTemplateSchemaByMode(item);
 | 
				
			||||||
 | 
					          insert(s);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          insert(createSchema(item.name));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
      items={[
 | 
					      items={useCollectionDataSourceItems('Form')}
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          type: 'itemGroup',
 | 
					 | 
				
			||||||
          title: t('Select data source'),
 | 
					 | 
				
			||||||
          children: collections
 | 
					 | 
				
			||||||
            ?.filter((item) => !item.inherit)
 | 
					 | 
				
			||||||
            ?.map((item) => {
 | 
					 | 
				
			||||||
              return {
 | 
					 | 
				
			||||||
                type: 'item',
 | 
					 | 
				
			||||||
                name: item.name,
 | 
					 | 
				
			||||||
                title: item.title,
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ]}
 | 
					 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,9 @@ import { useTranslation } from 'react-i18next';
 | 
				
			|||||||
import { useAPIClient } from '../../../api-client';
 | 
					import { useAPIClient } from '../../../api-client';
 | 
				
			||||||
import { useCollectionManager } from '../../../collection-manager';
 | 
					import { useCollectionManager } from '../../../collection-manager';
 | 
				
			||||||
import { SchemaComponent, SchemaComponentOptions } from '../../../schema-component';
 | 
					import { SchemaComponent, SchemaComponentOptions } from '../../../schema-component';
 | 
				
			||||||
 | 
					import { useSchemaTemplateManager } from '../../../schema-templates';
 | 
				
			||||||
import { SchemaInitializer } from '../../SchemaInitializer';
 | 
					import { SchemaInitializer } from '../../SchemaInitializer';
 | 
				
			||||||
 | 
					import { useCollectionDataSourceItems } from '../utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createSchema = (collectionName, { groupField, sortName }) => {
 | 
					const createSchema = (collectionName, { groupField, sortName }) => {
 | 
				
			||||||
  const schema: ISchema = {
 | 
					  const schema: ISchema = {
 | 
				
			||||||
@ -158,6 +160,7 @@ export const KanbanBlockInitializer = (props) => {
 | 
				
			|||||||
  const { insert } = props;
 | 
					  const { insert } = props;
 | 
				
			||||||
  const { collections, getCollection } = useCollectionManager();
 | 
					  const { collections, getCollection } = useCollectionManager();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const { getTemplateSchemaByMode } = useSchemaTemplateManager();
 | 
				
			||||||
  const options = useContext(SchemaOptionsContext);
 | 
					  const options = useContext(SchemaOptionsContext);
 | 
				
			||||||
  const api = useAPIClient();
 | 
					  const api = useAPIClient();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
@ -165,6 +168,11 @@ export const KanbanBlockInitializer = (props) => {
 | 
				
			|||||||
      {...props}
 | 
					      {...props}
 | 
				
			||||||
      icon={<FormOutlined />}
 | 
					      icon={<FormOutlined />}
 | 
				
			||||||
      onClick={async ({ item }) => {
 | 
					      onClick={async ({ item }) => {
 | 
				
			||||||
 | 
					        if (item.template) {
 | 
				
			||||||
 | 
					          const s = await getTemplateSchemaByMode(item);
 | 
				
			||||||
 | 
					          insert(s);
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        const collection = getCollection(item.name);
 | 
					        const collection = getCollection(item.name);
 | 
				
			||||||
        const fields = collection?.fields
 | 
					        const fields = collection?.fields
 | 
				
			||||||
          ?.filter((field) => ['select', 'radioGroup'].includes(field.interface))
 | 
					          ?.filter((field) => ['select', 'radioGroup'].includes(field.interface))
 | 
				
			||||||
@ -219,21 +227,7 @@ export const KanbanBlockInitializer = (props) => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        insert(createSchema(item.name, { ...values, sortName }));
 | 
					        insert(createSchema(item.name, { ...values, sortName }));
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
      items={[
 | 
					      items={useCollectionDataSourceItems('Kanban')}
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          type: 'itemGroup',
 | 
					 | 
				
			||||||
          title: t('Select data source'),
 | 
					 | 
				
			||||||
          children: collections
 | 
					 | 
				
			||||||
            ?.filter((item) => !item.inherit)
 | 
					 | 
				
			||||||
            ?.map((item) => {
 | 
					 | 
				
			||||||
              return {
 | 
					 | 
				
			||||||
                type: 'item',
 | 
					 | 
				
			||||||
                name: item.name,
 | 
					 | 
				
			||||||
                title: item.title,
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ]}
 | 
					 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import { TableOutlined } from '@ant-design/icons';
 | 
					import { TableOutlined } from '@ant-design/icons';
 | 
				
			||||||
import { ISchema } from '@formily/react';
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import { useTranslation } from 'react-i18next';
 | 
					 | 
				
			||||||
import { SchemaInitializer } from '../..';
 | 
					import { SchemaInitializer } from '../..';
 | 
				
			||||||
import { useCollectionManager } from '../../../collection-manager';
 | 
					import { useSchemaTemplateManager } from '../../../schema-templates';
 | 
				
			||||||
 | 
					import { useCollectionDataSourceItems } from '../utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createSchema = (collectionName) => {
 | 
					export const createTableBlockSchema = (collectionName) => {
 | 
				
			||||||
  const schema: ISchema = {
 | 
					  const schema: ISchema = {
 | 
				
			||||||
    type: 'void',
 | 
					    type: 'void',
 | 
				
			||||||
    'x-collection': 'collections',
 | 
					    'x-collection': 'collections',
 | 
				
			||||||
@ -79,30 +79,20 @@ const createSchema = (collectionName) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const TableBlockInitializer = (props) => {
 | 
					export const TableBlockInitializer = (props) => {
 | 
				
			||||||
  const { insert } = props;
 | 
					  const { insert } = props;
 | 
				
			||||||
  const { collections } = useCollectionManager();
 | 
					  const { getTemplateSchemaByMode } = useSchemaTemplateManager();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SchemaInitializer.Item
 | 
					    <SchemaInitializer.Item
 | 
				
			||||||
      {...props}
 | 
					      {...props}
 | 
				
			||||||
      icon={<TableOutlined />}
 | 
					      icon={<TableOutlined />}
 | 
				
			||||||
      onClick={({ item }) => {
 | 
					      onClick={async ({ item }) => {
 | 
				
			||||||
        insert(createSchema(item.name));
 | 
					        if (item.template) {
 | 
				
			||||||
 | 
					          const s = await getTemplateSchemaByMode(item);
 | 
				
			||||||
 | 
					          insert(s);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          insert(createTableBlockSchema(item.name));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
      items={[
 | 
					      items={useCollectionDataSourceItems('Table')}
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          type: 'itemGroup',
 | 
					 | 
				
			||||||
          title: t('Select data source'),
 | 
					 | 
				
			||||||
          children: collections
 | 
					 | 
				
			||||||
            ?.filter((item) => !item.inherit)
 | 
					 | 
				
			||||||
            ?.map((item) => {
 | 
					 | 
				
			||||||
              return {
 | 
					 | 
				
			||||||
                type: 'item',
 | 
					 | 
				
			||||||
                name: item.name,
 | 
					 | 
				
			||||||
                title: item.title,
 | 
					 | 
				
			||||||
              };
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      ]}
 | 
					 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
import { ISchema, Schema, useFieldSchema } from '@formily/react';
 | 
					import { ISchema, Schema, useFieldSchema } from '@formily/react';
 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { SchemaInitializerItemOptions } from '../';
 | 
					import { SchemaInitializerItemOptions } from '../';
 | 
				
			||||||
import { useCollection } from '../../collection-manager';
 | 
					import { useCollection, useCollectionManager } from '../../collection-manager';
 | 
				
			||||||
import { useDesignable } from '../../schema-component';
 | 
					import { useDesignable } from '../../schema-component';
 | 
				
			||||||
 | 
					import { useSchemaTemplateManager } from '../../schema-templates';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const gridRowColWrap = (schema: ISchema) => {
 | 
					export const gridRowColWrap = (schema: ISchema) => {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
@ -203,3 +205,76 @@ export const useCurrentSchema = (action: string, key: string, find = findSchema,
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useCollectionDataSourceItems = (componentName) => {
 | 
				
			||||||
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const { collections } = useCollectionManager();
 | 
				
			||||||
 | 
					  const { getTemplatesByCollection } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  return [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      key: 'tableBlock',
 | 
				
			||||||
 | 
					      type: 'itemGroup',
 | 
				
			||||||
 | 
					      title: t('Select data source'),
 | 
				
			||||||
 | 
					      children: collections
 | 
				
			||||||
 | 
					        ?.filter((item) => !item.inherit)
 | 
				
			||||||
 | 
					        ?.map((item, index) => {
 | 
				
			||||||
 | 
					          const templates = getTemplatesByCollection(item.name).filter((template) => {
 | 
				
			||||||
 | 
					            return componentName && template.componentName === componentName;
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          if (!templates.length) {
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					              type: 'item',
 | 
				
			||||||
 | 
					              name: item.name,
 | 
				
			||||||
 | 
					              title: item.title,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            key: `${componentName}_table_subMenu_${index}`,
 | 
				
			||||||
 | 
					            type: 'subMenu',
 | 
				
			||||||
 | 
					            name: `${item.name}_${index}`,
 | 
				
			||||||
 | 
					            title: item.title,
 | 
				
			||||||
 | 
					            children: [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                type: 'item',
 | 
				
			||||||
 | 
					                name: item.name,
 | 
				
			||||||
 | 
					                title: '空白区块',
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                type: 'divider',
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                key: `${componentName}_table_subMenu_${index}_copy`,
 | 
				
			||||||
 | 
					                type: 'subMenu',
 | 
				
			||||||
 | 
					                name: 'copy',
 | 
				
			||||||
 | 
					                title: '复制模板',
 | 
				
			||||||
 | 
					                children: templates.map((template) => {
 | 
				
			||||||
 | 
					                  return {
 | 
				
			||||||
 | 
					                    type: 'item',
 | 
				
			||||||
 | 
					                    mode: 'copy',
 | 
				
			||||||
 | 
					                    name: item.name,
 | 
				
			||||||
 | 
					                    template,
 | 
				
			||||||
 | 
					                    title: template.name || '未命名',
 | 
				
			||||||
 | 
					                  };
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                key: `${componentName}_table_subMenu_${index}_ref`,
 | 
				
			||||||
 | 
					                type: 'subMenu',
 | 
				
			||||||
 | 
					                name: 'ref',
 | 
				
			||||||
 | 
					                title: '引用模板',
 | 
				
			||||||
 | 
					                children: templates.map((template) => {
 | 
				
			||||||
 | 
					                  return {
 | 
				
			||||||
 | 
					                    type: 'item',
 | 
				
			||||||
 | 
					                    mode: 'reference',
 | 
				
			||||||
 | 
					                    name: item.name,
 | 
				
			||||||
 | 
					                    template,
 | 
				
			||||||
 | 
					                    title: template.name || '未命名',
 | 
				
			||||||
 | 
					                  };
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -42,76 +42,56 @@ SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
 | 
				
			|||||||
      insertAdjacent(insertPosition, wrap(schema));
 | 
					      insertAdjacent(insertPosition, wrap(schema));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  const menu = (
 | 
					  const renderItems = (items: any) => {
 | 
				
			||||||
    <Menu>
 | 
					    return items?.map((item, indexA) => {
 | 
				
			||||||
      {items?.map((item, indexA) => {
 | 
					      if (item.type === 'divider') {
 | 
				
			||||||
        if (item.type === 'divider') {
 | 
					        return <Menu.Divider key={item.key || `item-${indexA}`} />;
 | 
				
			||||||
          return <Menu.Divider key={`item-${indexA}`} />;
 | 
					      }
 | 
				
			||||||
        }
 | 
					      if (item.type === 'item' && item.component) {
 | 
				
			||||||
        if (item.type === 'item' && item.component) {
 | 
					        const Component = findComponent(item.component);
 | 
				
			||||||
          const Component = findComponent(item.component);
 | 
					 | 
				
			||||||
          return (
 | 
					 | 
				
			||||||
            Component && (
 | 
					 | 
				
			||||||
              <SchemaInitializerItemContext.Provider
 | 
					 | 
				
			||||||
                key={`item-${indexA}`}
 | 
					 | 
				
			||||||
                value={{
 | 
					 | 
				
			||||||
                  index: indexA,
 | 
					 | 
				
			||||||
                  item,
 | 
					 | 
				
			||||||
                  info: item,
 | 
					 | 
				
			||||||
                  insert: insertSchema,
 | 
					 | 
				
			||||||
                }}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                <Component
 | 
					 | 
				
			||||||
                  {...item}
 | 
					 | 
				
			||||||
                  item={{
 | 
					 | 
				
			||||||
                    ...item,
 | 
					 | 
				
			||||||
                    title: compile(item.title),
 | 
					 | 
				
			||||||
                  }}
 | 
					 | 
				
			||||||
                  insert={insertSchema}
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
              </SchemaInitializerItemContext.Provider>
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
          item?.children?.length > 0 && (
 | 
					          Component && (
 | 
				
			||||||
            <Menu.ItemGroup key={`item-group-${indexA}`} title={compile(item.title)}>
 | 
					            <SchemaInitializerItemContext.Provider
 | 
				
			||||||
              {item?.children?.map((child, indexB) => {
 | 
					              key={`item-${indexA}`}
 | 
				
			||||||
                if (!child.component) {
 | 
					              value={{
 | 
				
			||||||
                  return null;
 | 
					                index: indexA,
 | 
				
			||||||
                }
 | 
					                item,
 | 
				
			||||||
                const Component = findComponent(child.component);
 | 
					                info: item,
 | 
				
			||||||
                return (
 | 
					                insert: insertSchema,
 | 
				
			||||||
                  Component && (
 | 
					              }}
 | 
				
			||||||
                    <SchemaInitializerItemContext.Provider
 | 
					            >
 | 
				
			||||||
                      key={`item-${indexB}`}
 | 
					              <Component
 | 
				
			||||||
                      value={{
 | 
					                {...item}
 | 
				
			||||||
                        index: indexB,
 | 
					                item={{
 | 
				
			||||||
                        item: child,
 | 
					                  ...item,
 | 
				
			||||||
                        info: child,
 | 
					                  title: compile(item.title),
 | 
				
			||||||
                        insert: insertSchema,
 | 
					                }}
 | 
				
			||||||
                      }}
 | 
					                insert={insertSchema}
 | 
				
			||||||
                    >
 | 
					              />
 | 
				
			||||||
                      <Component
 | 
					            </SchemaInitializerItemContext.Provider>
 | 
				
			||||||
                        {...child}
 | 
					 | 
				
			||||||
                        item={{
 | 
					 | 
				
			||||||
                          ...child,
 | 
					 | 
				
			||||||
                          title: compile(child.title),
 | 
					 | 
				
			||||||
                        }}
 | 
					 | 
				
			||||||
                        insert={insertSchema}
 | 
					 | 
				
			||||||
                      />
 | 
					 | 
				
			||||||
                    </SchemaInitializerItemContext.Provider>
 | 
					 | 
				
			||||||
                  )
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
              })}
 | 
					 | 
				
			||||||
            </Menu.ItemGroup>
 | 
					 | 
				
			||||||
          )
 | 
					          )
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      })}
 | 
					      }
 | 
				
			||||||
    </Menu>
 | 
					      if (item.type === 'itemGroup') {
 | 
				
			||||||
  );
 | 
					        return (
 | 
				
			||||||
 | 
					          <Menu.ItemGroup key={item.key || `item-group-${indexA}`} title={compile(item.title)}>
 | 
				
			||||||
 | 
					            {renderItems(item.children)}
 | 
				
			||||||
 | 
					          </Menu.ItemGroup>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (item.type === 'subMenu') {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					          <Menu.SubMenu key={item.key || `item-group-${indexA}`} title={compile(item.title)}>
 | 
				
			||||||
 | 
					            {renderItems(item.children)}
 | 
				
			||||||
 | 
					          </Menu.SubMenu>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!designable) {
 | 
					  const menu = <Menu>{renderItems(items)}</Menu>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!designable && props.designable !== true) {
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -124,15 +104,17 @@ SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
 | 
				
			|||||||
      {...dropdown}
 | 
					      {...dropdown}
 | 
				
			||||||
      overlay={menu}
 | 
					      overlay={menu}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      {component ? component : (
 | 
					      {component ? (
 | 
				
			||||||
 | 
					        component
 | 
				
			||||||
 | 
					      ) : (
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          {...others}
 | 
					 | 
				
			||||||
          type={'dashed'}
 | 
					          type={'dashed'}
 | 
				
			||||||
          style={{
 | 
					          style={{
 | 
				
			||||||
            ...style,
 | 
					 | 
				
			||||||
            borderColor: '#f18b62',
 | 
					            borderColor: '#f18b62',
 | 
				
			||||||
            color: '#f18b62',
 | 
					            color: '#f18b62',
 | 
				
			||||||
 | 
					            ...style,
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
 | 
					          {...others}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          {compile(props.children || props.title)}
 | 
					          {compile(props.children || props.title)}
 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
@ -156,16 +138,24 @@ SchemaInitializer.Item = (props: SchemaInitializerItemProps) => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        if (item.type === 'itemGroup') {
 | 
					        if (item.type === 'itemGroup') {
 | 
				
			||||||
          return (
 | 
					          return (
 | 
				
			||||||
            // @ts-ignore
 | 
					            <Menu.ItemGroup
 | 
				
			||||||
            <Menu.ItemGroup eventKey={`item-group-${indexA}`} key={`item-group-${indexA}`} title={compile(item.title)}>
 | 
					              // @ts-ignore
 | 
				
			||||||
 | 
					              eventKey={item.key || `item-group-${indexA}`}
 | 
				
			||||||
 | 
					              key={item.key || `item-group-${indexA}`}
 | 
				
			||||||
 | 
					              title={compile(item.title)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
              {renderMenuItem(item.children)}
 | 
					              {renderMenuItem(item.children)}
 | 
				
			||||||
            </Menu.ItemGroup>
 | 
					            </Menu.ItemGroup>
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (item.type === 'subMenu') {
 | 
					        if (item.type === 'subMenu') {
 | 
				
			||||||
          return (
 | 
					          return (
 | 
				
			||||||
            // @ts-ignore
 | 
					            <Menu.SubMenu
 | 
				
			||||||
            <Menu.SubMenu eventKey={`sub-menu-${indexA}`} key={`sub-menu-${indexA}`} title={compile(item.title)}>
 | 
					              // @ts-ignore
 | 
				
			||||||
 | 
					              eventKey={item.key || `sub-menu-${indexA}`}
 | 
				
			||||||
 | 
					              key={item.key || `sub-menu-${indexA}`}
 | 
				
			||||||
 | 
					              title={compile(item.title)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
              {renderMenuItem(item.children)}
 | 
					              {renderMenuItem(item.children)}
 | 
				
			||||||
            </Menu.SubMenu>
 | 
					            </Menu.SubMenu>
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
 | 
				
			|||||||
@ -8,12 +8,14 @@ export interface SchemaInitializerButtonProps extends ButtonProps {
 | 
				
			|||||||
  items?: SchemaInitializerItemOptions[];
 | 
					  items?: SchemaInitializerItemOptions[];
 | 
				
			||||||
  dropdown?: DropDownProps;
 | 
					  dropdown?: DropDownProps;
 | 
				
			||||||
  component?: any;
 | 
					  component?: any;
 | 
				
			||||||
 | 
					  designable?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SchemaInitializerItemOptions = ItemGroupOptions | SubMenuOptions | ItemOptions | DividerOptions;
 | 
					export type SchemaInitializerItemOptions = ItemGroupOptions | SubMenuOptions | ItemOptions | DividerOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ItemCommonOptions {
 | 
					interface ItemCommonOptions {
 | 
				
			||||||
  title?: any;
 | 
					  title?: any;
 | 
				
			||||||
 | 
					  key?: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ItemGroupOptions extends ItemCommonOptions {
 | 
					interface ItemGroupOptions extends ItemCommonOptions {
 | 
				
			||||||
 | 
				
			|||||||
@ -11,19 +11,26 @@ const titleCss = css`
 | 
				
			|||||||
  pointer-events: none;
 | 
					  pointer-events: none;
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  font-size: 12px;
 | 
					  font-size: 12px;
 | 
				
			||||||
  background: #f18b62;
 | 
					  /* background: #f18b62;
 | 
				
			||||||
  color: #fff;
 | 
					  color: #fff; */
 | 
				
			||||||
  padding: 0 5px;
 | 
					  padding: 0;
 | 
				
			||||||
  line-height: 16px;
 | 
					  line-height: 16px;
 | 
				
			||||||
  height: 16px;
 | 
					  height: 16px;
 | 
				
			||||||
  border-bottom-right-radius: 2px;
 | 
					  border-bottom-right-radius: 2px;
 | 
				
			||||||
  border-radius: 2px;
 | 
					  border-radius: 2px;
 | 
				
			||||||
  top: 2px;
 | 
					  top: 2px;
 | 
				
			||||||
  left: 2px;
 | 
					  left: 2px;
 | 
				
			||||||
 | 
					  .title-tag {
 | 
				
			||||||
 | 
					    padding: 0 3px;
 | 
				
			||||||
 | 
					    border-radius: 2px;
 | 
				
			||||||
 | 
					    background: #f18b62;
 | 
				
			||||||
 | 
					    color: #fff;
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const GeneralSchemaDesigner = (props: any) => {
 | 
					export const GeneralSchemaDesigner = (props: any) => {
 | 
				
			||||||
  const { title, draggable = true } = props;
 | 
					  const { title, template, draggable = true } = props;
 | 
				
			||||||
  const { dn, designable } = useDesignable();
 | 
					  const { dn, designable } = useDesignable();
 | 
				
			||||||
  const field = useField();
 | 
					  const field = useField();
 | 
				
			||||||
  const fieldSchema = useFieldSchema();
 | 
					  const fieldSchema = useFieldSchema();
 | 
				
			||||||
@ -38,7 +45,14 @@ export const GeneralSchemaDesigner = (props: any) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={'general-schema-designer'}>
 | 
					    <div className={'general-schema-designer'}>
 | 
				
			||||||
      {title && <div className={classNames('general-schema-designer-title', titleCss)}>{compile(title)}</div>}
 | 
					      {title && (
 | 
				
			||||||
 | 
					        <div className={classNames('general-schema-designer-title', titleCss)}>
 | 
				
			||||||
 | 
					          <Space size={2}>
 | 
				
			||||||
 | 
					            <span className={'title-tag'}>{compile(title)}</span>
 | 
				
			||||||
 | 
					            {template && <span className={'title-tag'}>引用模板: {template?.name || '未命名'}</span>}
 | 
				
			||||||
 | 
					          </Space>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
      <div className={'general-schema-designer-icons'}>
 | 
					      <div className={'general-schema-designer-icons'}>
 | 
				
			||||||
        <Space size={2} align={'center'}>
 | 
					        <Space size={2} align={'center'}>
 | 
				
			||||||
          {draggable && (
 | 
					          {draggable && (
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,22 @@
 | 
				
			|||||||
import { FormDialog, FormLayout } from '@formily/antd';
 | 
					import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
 | 
				
			||||||
import { GeneralField } from '@formily/core';
 | 
					import { GeneralField } from '@formily/core';
 | 
				
			||||||
import { ISchema, Schema, SchemaOptionsContext } from '@formily/react';
 | 
					import { ISchema, Schema, SchemaOptionsContext } from '@formily/react';
 | 
				
			||||||
import { uid } from '@formily/shared';
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
import { Dropdown, Menu, MenuItemProps, Modal, Select, Switch } from 'antd';
 | 
					import { Dropdown, Menu, MenuItemProps, Modal, Select, Switch } from 'antd';
 | 
				
			||||||
import React, { createContext, useContext, useState } from 'react';
 | 
					import React, { createContext, useContext, useState } from 'react';
 | 
				
			||||||
import { useTranslation } from 'react-i18next';
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
import { ActionContext, Designable, SchemaComponent, SchemaComponentOptions, useActionContext } from '..';
 | 
					import {
 | 
				
			||||||
 | 
					  ActionContext,
 | 
				
			||||||
 | 
					  createDesignable,
 | 
				
			||||||
 | 
					  Designable,
 | 
				
			||||||
 | 
					  SchemaComponent,
 | 
				
			||||||
 | 
					  SchemaComponentOptions,
 | 
				
			||||||
 | 
					  useActionContext,
 | 
				
			||||||
 | 
					  useAPIClient,
 | 
				
			||||||
 | 
					  useCollection
 | 
				
			||||||
 | 
					} from '..';
 | 
				
			||||||
 | 
					import { useSchemaTemplateManager } from '../schema-templates';
 | 
				
			||||||
 | 
					import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface SchemaSettingsProps {
 | 
					interface SchemaSettingsProps {
 | 
				
			||||||
  title?: any;
 | 
					  title?: any;
 | 
				
			||||||
@ -20,6 +31,8 @@ interface SchemaSettingsContextProps {
 | 
				
			|||||||
  fieldSchema?: Schema;
 | 
					  fieldSchema?: Schema;
 | 
				
			||||||
  setVisible?: any;
 | 
					  setVisible?: any;
 | 
				
			||||||
  visible?: any;
 | 
					  visible?: any;
 | 
				
			||||||
 | 
					  template?: any;
 | 
				
			||||||
 | 
					  collectionName?: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SchemaSettingsContext = createContext<SchemaSettingsContextProps>(null);
 | 
					const SchemaSettingsContext = createContext<SchemaSettingsContextProps>(null);
 | 
				
			||||||
@ -48,19 +61,24 @@ interface SchemaSettingsProviderProps {
 | 
				
			|||||||
  fieldSchema?: Schema;
 | 
					  fieldSchema?: Schema;
 | 
				
			||||||
  setVisible?: any;
 | 
					  setVisible?: any;
 | 
				
			||||||
  visible?: any;
 | 
					  visible?: any;
 | 
				
			||||||
 | 
					  template?: any;
 | 
				
			||||||
 | 
					  collectionName?: any;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (props) => {
 | 
					export const SchemaSettingsProvider: React.FC<SchemaSettingsProviderProps> = (props) => {
 | 
				
			||||||
  const { visible, setVisible, dn, field, fieldSchema, children } = props;
 | 
					  const { children, fieldSchema, ...others } = props;
 | 
				
			||||||
 | 
					  const { getTemplateBySchemaId } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  const { name } = useCollection();
 | 
				
			||||||
 | 
					  const template = getTemplateBySchemaId(fieldSchema['x-uid']);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SchemaSettingsContext.Provider value={{ visible, setVisible, dn, field, fieldSchema }}>
 | 
					    <SchemaSettingsContext.Provider value={{ collectionName: name, template, fieldSchema, ...others }}>
 | 
				
			||||||
      {children}
 | 
					      {children}
 | 
				
			||||||
    </SchemaSettingsContext.Provider>
 | 
					    </SchemaSettingsContext.Provider>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNested = (props) => {
 | 
					export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNested = (props) => {
 | 
				
			||||||
  const { title, dn, field, fieldSchema } = props;
 | 
					  const { title, dn, ...others } = props;
 | 
				
			||||||
  const [visible, setVisible] = useState(false);
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
  const DropdownMenu = (
 | 
					  const DropdownMenu = (
 | 
				
			||||||
    <Dropdown
 | 
					    <Dropdown
 | 
				
			||||||
@ -75,7 +93,7 @@ export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNeste
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
  if (dn) {
 | 
					  if (dn) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={dn} field={field} fieldSchema={fieldSchema}>
 | 
					      <SchemaSettingsProvider visible={visible} setVisible={setVisible} dn={dn} {...others}>
 | 
				
			||||||
        {DropdownMenu}
 | 
					        {DropdownMenu}
 | 
				
			||||||
      </SchemaSettingsProvider>
 | 
					      </SchemaSettingsProvider>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
@ -83,6 +101,86 @@ export const SchemaSettings: React.FC<SchemaSettingsProps> & SchemaSettingsNeste
 | 
				
			|||||||
  return DropdownMenu;
 | 
					  return DropdownMenu;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SchemaSettings.Template = (props) => {
 | 
				
			||||||
 | 
					  const { componentName, collectionName } = props;
 | 
				
			||||||
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const { dn, setVisible, template, fieldSchema } = useSchemaSettings();
 | 
				
			||||||
 | 
					  const api = useAPIClient();
 | 
				
			||||||
 | 
					  const { dn: tdn } = useBlockTemplateContext();
 | 
				
			||||||
 | 
					  const { saveAsTemplate, copyTemplateSchema } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  if (!collectionName) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (template) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <SchemaSettings.Item
 | 
				
			||||||
 | 
					        onClick={async () => {
 | 
				
			||||||
 | 
					          const schema = await copyTemplateSchema(template);
 | 
				
			||||||
 | 
					          const removed = tdn.removeWithoutEmit();
 | 
				
			||||||
 | 
					          tdn.insertAfterEnd(schema, {
 | 
				
			||||||
 | 
					            async onSuccess() {
 | 
				
			||||||
 | 
					              await api.request({
 | 
				
			||||||
 | 
					                url: `/uiSchemas:remove/${removed['x-uid']}`,
 | 
				
			||||||
 | 
					              });
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        {t('Convert reference to duplicate')}
 | 
				
			||||||
 | 
					      </SchemaSettings.Item>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SchemaSettings.Item
 | 
				
			||||||
 | 
					      onClick={async () => {
 | 
				
			||||||
 | 
					        setVisible(false);
 | 
				
			||||||
 | 
					        const values = await FormDialog('Save as template', () => {
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <FormLayout layout={'vertical'}>
 | 
				
			||||||
 | 
					              <SchemaComponent
 | 
				
			||||||
 | 
					                components={{ Input, FormItem }}
 | 
				
			||||||
 | 
					                schema={{
 | 
				
			||||||
 | 
					                  type: 'object',
 | 
				
			||||||
 | 
					                  properties: {
 | 
				
			||||||
 | 
					                    name: {
 | 
				
			||||||
 | 
					                      title: '模板名称',
 | 
				
			||||||
 | 
					                      required: true,
 | 
				
			||||||
 | 
					                      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					                      'x-component': 'Input',
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </FormLayout>
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }).open({});
 | 
				
			||||||
 | 
					        const sdn = createDesignable({
 | 
				
			||||||
 | 
					          api,
 | 
				
			||||||
 | 
					          refresh: dn.refresh.bind(dn),
 | 
				
			||||||
 | 
					          current: fieldSchema.parent,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        sdn.loadAPIClientEvents();
 | 
				
			||||||
 | 
					        const { key } = await saveAsTemplate({
 | 
				
			||||||
 | 
					          collectionName,
 | 
				
			||||||
 | 
					          componentName,
 | 
				
			||||||
 | 
					          name: values.name,
 | 
				
			||||||
 | 
					          uid: fieldSchema['x-uid'],
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        sdn.removeWithoutEmit(fieldSchema);
 | 
				
			||||||
 | 
					        sdn.insertBeforeEnd({
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          'x-component': 'BlockTemplate',
 | 
				
			||||||
 | 
					          'x-component-props': {
 | 
				
			||||||
 | 
					            templateId: key,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {t('Save as template')}
 | 
				
			||||||
 | 
					    </SchemaSettings.Item>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SchemaSettings.Item = (props) => {
 | 
					SchemaSettings.Item = (props) => {
 | 
				
			||||||
  const { eventKey } = props;
 | 
					  const { eventKey } = props;
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
@ -116,8 +214,9 @@ SchemaSettings.Divider = (props) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
SchemaSettings.Remove = (props: any) => {
 | 
					SchemaSettings.Remove = (props: any) => {
 | 
				
			||||||
  const { confirm, removeParentsIfNoChildren, breakRemoveOn } = props;
 | 
					  const { confirm, removeParentsIfNoChildren, breakRemoveOn } = props;
 | 
				
			||||||
  const { dn } = useSchemaSettings();
 | 
					  const { dn, template } = useSchemaSettings();
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const ctx = useBlockTemplateContext();
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <SchemaSettings.Item
 | 
					    <SchemaSettings.Item
 | 
				
			||||||
      onClick={() => {
 | 
					      onClick={() => {
 | 
				
			||||||
@ -126,10 +225,15 @@ SchemaSettings.Remove = (props: any) => {
 | 
				
			|||||||
          content: t('Are you sure you want to delete it?'),
 | 
					          content: t('Are you sure you want to delete it?'),
 | 
				
			||||||
          ...confirm,
 | 
					          ...confirm,
 | 
				
			||||||
          onOk() {
 | 
					          onOk() {
 | 
				
			||||||
            dn.remove(null, {
 | 
					            const options = {
 | 
				
			||||||
              removeParentsIfNoChildren,
 | 
					              removeParentsIfNoChildren,
 | 
				
			||||||
              breakRemoveOn,
 | 
					              breakRemoveOn,
 | 
				
			||||||
            });
 | 
					            };
 | 
				
			||||||
 | 
					            if (template && ctx?.dn) {
 | 
				
			||||||
 | 
					              ctx?.dn.remove(null, options);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              dn.remove(null, options);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								packages/client/src/schema-templates/AddBlockTemplate.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								packages/client/src/schema-templates/AddBlockTemplate.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
 | 
					import { Button, Dropdown, Menu } from 'antd';
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
 | 
					import { useHistory } from 'react-router-dom';
 | 
				
			||||||
 | 
					import { useAPIClient, useCollectionManager, useCompile, useSchemaTemplateManager } from '..';
 | 
				
			||||||
 | 
					import { createTableBlockSchema } from '../schema-initializer/Initializers/Items';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const AddBlockTemplate = (props: any) => {
 | 
				
			||||||
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const history = useHistory();
 | 
				
			||||||
 | 
					  const api = useAPIClient();
 | 
				
			||||||
 | 
					  const { refresh } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  const { collections } = useCollectionManager();
 | 
				
			||||||
 | 
					  const compile = useCompile();
 | 
				
			||||||
 | 
					  const insert = ({ uiSchema, collectionName }) => {
 | 
				
			||||||
 | 
					    const key = uid();
 | 
				
			||||||
 | 
					    api
 | 
				
			||||||
 | 
					      .resource('uiSchemaTemplates')
 | 
				
			||||||
 | 
					      .create({
 | 
				
			||||||
 | 
					        values: {
 | 
				
			||||||
 | 
					          key,
 | 
				
			||||||
 | 
					          collectionName,
 | 
				
			||||||
 | 
					          uiSchema,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .then((res) => {
 | 
				
			||||||
 | 
					        refresh();
 | 
				
			||||||
 | 
					        history.push(`/admin/block-templates/${key}`);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <Dropdown
 | 
				
			||||||
 | 
					        overlay={
 | 
				
			||||||
 | 
					          <Menu>
 | 
				
			||||||
 | 
					            <Menu.SubMenu title={'Table'}>
 | 
				
			||||||
 | 
					              {collections?.map((collection) => {
 | 
				
			||||||
 | 
					                return (
 | 
				
			||||||
 | 
					                  <Menu.Item
 | 
				
			||||||
 | 
					                    key={collection.name}
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      const uiSchema = createTableBlockSchema(collection.name);
 | 
				
			||||||
 | 
					                      insert({ uiSchema, collectionName: collection.name });
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    {compile(collection.title)}
 | 
				
			||||||
 | 
					                  </Menu.Item>
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              })}
 | 
				
			||||||
 | 
					            </Menu.SubMenu>
 | 
				
			||||||
 | 
					          </Menu>
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Button type={'primary'}>{t('Add block template')}</Button>
 | 
				
			||||||
 | 
					      </Dropdown>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										26
									
								
								packages/client/src/schema-templates/BlockTemplate.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/client/src/schema-templates/BlockTemplate.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					import { observer, useField, useFieldSchema } from '@formily/react';
 | 
				
			||||||
 | 
					import React, { createContext, useContext, useMemo } from 'react';
 | 
				
			||||||
 | 
					import { RemoteSchemaComponent, useDesignable } from '..';
 | 
				
			||||||
 | 
					import { useSchemaTemplateManager } from './SchemaTemplateManagerProvider';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const BlockTemplateContext = createContext<any>({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useBlockTemplateContext = () => {
 | 
				
			||||||
 | 
					  return useContext(BlockTemplateContext);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BlockTemplate = observer((props: any) => {
 | 
				
			||||||
 | 
					  const { templateId } = props;
 | 
				
			||||||
 | 
					  const { getTemplateById } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  const field = useField();
 | 
				
			||||||
 | 
					  const fieldSchema = useFieldSchema();
 | 
				
			||||||
 | 
					  const { dn } = useDesignable();
 | 
				
			||||||
 | 
					  const template = useMemo(() => getTemplateById(templateId), [templateId]);
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}>
 | 
				
			||||||
 | 
					        <RemoteSchemaComponent uid={template?.uid} />
 | 
				
			||||||
 | 
					      </BlockTemplateContext.Provider>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					import { Input, PageHeader as AntdPageHeader, Spin } from 'antd';
 | 
				
			||||||
 | 
					import React, { useContext, useState } from 'react';
 | 
				
			||||||
 | 
					import { useHistory, useRouteMatch } from 'react-router-dom';
 | 
				
			||||||
 | 
					import { useAPIClient, useRequest, useSchemaTemplateManager } from '..';
 | 
				
			||||||
 | 
					import { RemoteSchemaComponent, SchemaComponentContext } from '../schema-component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const EditableTitle = (props) => {
 | 
				
			||||||
 | 
					  const [title, setTitle] = useState(props.title);
 | 
				
			||||||
 | 
					  const [visible, setVisible] = useState(false);
 | 
				
			||||||
 | 
					  const { refresh } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  const api = useAPIClient();
 | 
				
			||||||
 | 
					  const { run } = useRequest(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      resource: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					      action: 'update',
 | 
				
			||||||
 | 
					      params: {
 | 
				
			||||||
 | 
					        filterByTk: props.filterByTk,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      manual: true,
 | 
				
			||||||
 | 
					      debounceWait: 500,
 | 
				
			||||||
 | 
					      onSuccess() {
 | 
				
			||||||
 | 
					        refresh();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      {visible ? (
 | 
				
			||||||
 | 
					        <Input
 | 
				
			||||||
 | 
					          defaultValue={title}
 | 
				
			||||||
 | 
					          size={'large'}
 | 
				
			||||||
 | 
					          onBlur={() => {
 | 
				
			||||||
 | 
					            setVisible(false);
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          onChange={(e) => {
 | 
				
			||||||
 | 
					            setTitle(e.target.value);
 | 
				
			||||||
 | 
					            run({
 | 
				
			||||||
 | 
					              filterByTk: props.filterByTk,
 | 
				
			||||||
 | 
					              values: {
 | 
				
			||||||
 | 
					                name: e.target.value,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      ) : (
 | 
				
			||||||
 | 
					        <div
 | 
				
			||||||
 | 
					          onClick={() => {
 | 
				
			||||||
 | 
					            setVisible(true);
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          {title || <span style={{ color: '#bbb' }}>未命名</span>}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BlockTemplateDetails = () => {
 | 
				
			||||||
 | 
					  const history = useHistory();
 | 
				
			||||||
 | 
					  const match = useRouteMatch<any>();
 | 
				
			||||||
 | 
					  const key = match?.params?.key;
 | 
				
			||||||
 | 
					  const value = useContext(SchemaComponentContext);
 | 
				
			||||||
 | 
					  const { data, loading } = useRequest({
 | 
				
			||||||
 | 
					    resource: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					    action: 'get',
 | 
				
			||||||
 | 
					    params: {
 | 
				
			||||||
 | 
					      filterByTk: key,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  if (loading) {
 | 
				
			||||||
 | 
					    return <Spin />;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <AntdPageHeader
 | 
				
			||||||
 | 
					        onBack={() => {
 | 
				
			||||||
 | 
					          history.push('/admin/block-templates');
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        ghost={false}
 | 
				
			||||||
 | 
					        title={<EditableTitle filterByTk={key} title={data?.data?.name} />}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					      <div style={{ margin: 24 }}>
 | 
				
			||||||
 | 
					        <SchemaComponentContext.Provider value={{ ...value, designable: true }}>
 | 
				
			||||||
 | 
					          <RemoteSchemaComponent uid={data?.data?.uid} />
 | 
				
			||||||
 | 
					        </SchemaComponentContext.Provider>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										56
									
								
								packages/client/src/schema-templates/BlockTemplatePage.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								packages/client/src/schema-templates/BlockTemplatePage.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					import { Card, PageHeader as AntdPageHeader, Table } from 'antd';
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
 | 
					import { useCompile, useRequest } from '..';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const BlockTemplatePage = () => {
 | 
				
			||||||
 | 
					  const { data, loading } = useRequest({
 | 
				
			||||||
 | 
					    resource: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					    action: 'list',
 | 
				
			||||||
 | 
					    params: {
 | 
				
			||||||
 | 
					      appends: ['collection'],
 | 
				
			||||||
 | 
					      sort: ['-createdAt'],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const compile = useCompile();
 | 
				
			||||||
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <AntdPageHeader ghost={false} title={t('Block templates')} />
 | 
				
			||||||
 | 
					      <div style={{ margin: 24 }}>
 | 
				
			||||||
 | 
					        <Card bordered={false}>
 | 
				
			||||||
 | 
					          <Table
 | 
				
			||||||
 | 
					            rowSelection={{
 | 
				
			||||||
 | 
					              type: 'checkbox',
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            columns={[
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                dataIndex: 'name',
 | 
				
			||||||
 | 
					                title: t('Template name'),
 | 
				
			||||||
 | 
					                render: (value) => <>{value || '未命名'}</>,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                dataIndex: ['collection', 'title'],
 | 
				
			||||||
 | 
					                title: t('Collection display name'),
 | 
				
			||||||
 | 
					                render: (value) => compile(value),
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                dataIndex: 'componentName',
 | 
				
			||||||
 | 
					                title: t('Block type'),
 | 
				
			||||||
 | 
					                render: (value) => value,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                dataIndex: 'actions',
 | 
				
			||||||
 | 
					                title: t('Actions'),
 | 
				
			||||||
 | 
					                render: (_, record) => <Link to={`/admin/block-templates/${record.key}`}>查看</Link>,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            ]}
 | 
				
			||||||
 | 
					            loading={loading}
 | 
				
			||||||
 | 
					            dataSource={data?.data}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,149 @@
 | 
				
			|||||||
 | 
					import { ISchema, useFieldSchema } from '@formily/react';
 | 
				
			||||||
 | 
					import { uid } from '@formily/shared';
 | 
				
			||||||
 | 
					import { Spin } from 'antd';
 | 
				
			||||||
 | 
					import { cloneDeep } from 'lodash';
 | 
				
			||||||
 | 
					import React, { createContext, useContext, useMemo } from 'react';
 | 
				
			||||||
 | 
					import { useAPIClient, useRequest } from '../api-client';
 | 
				
			||||||
 | 
					import { RouteSwitchContext } from '../route-switch';
 | 
				
			||||||
 | 
					import { SchemaComponentOptions } from '../schema-component';
 | 
				
			||||||
 | 
					import { BlockTemplate } from './BlockTemplate';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SchemaTemplateManagerContext = createContext<any>({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SchemaTemplateRouteProvider = (props) => {
 | 
				
			||||||
 | 
					  const { routes, ...others } = useContext(RouteSwitchContext);
 | 
				
			||||||
 | 
					  routes[1].routes.unshift(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'route',
 | 
				
			||||||
 | 
					      path: '/admin/block-templates/:key',
 | 
				
			||||||
 | 
					      component: 'BlockTemplateDetails',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'route',
 | 
				
			||||||
 | 
					      path: '/admin/block-templates',
 | 
				
			||||||
 | 
					      component: 'BlockTemplatePage',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  return <RouteSwitchContext.Provider value={{ ...others, routes }}>{props.children}</RouteSwitchContext.Provider>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SchemaTemplateManagerProvider: React.FC<any> = (props) => {
 | 
				
			||||||
 | 
					  const { templates, refresh } = props;
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SchemaTemplateManagerContext.Provider value={{ templates, refresh }}>
 | 
				
			||||||
 | 
					      <SchemaTemplateRouteProvider>
 | 
				
			||||||
 | 
					        <SchemaComponentOptions components={{ BlockTemplate }}>{props.children}</SchemaComponentOptions>
 | 
				
			||||||
 | 
					      </SchemaTemplateRouteProvider>
 | 
				
			||||||
 | 
					    </SchemaTemplateManagerContext.Provider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const regenerateUid = (s: ISchema) => {
 | 
				
			||||||
 | 
					  s['x-uid'] = uid();
 | 
				
			||||||
 | 
					  Object.keys(s.properties || {}).forEach((key) => {
 | 
				
			||||||
 | 
					    regenerateUid(s.properties[key]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useSchemaTemplate = () => {
 | 
				
			||||||
 | 
					  const { getTemplateBySchemaId } = useSchemaTemplateManager();
 | 
				
			||||||
 | 
					  const fieldSchema = useFieldSchema();
 | 
				
			||||||
 | 
					  const schemaId = fieldSchema['x-uid'];
 | 
				
			||||||
 | 
					  return useMemo(() => getTemplateBySchemaId(schemaId), [schemaId]);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useSchemaTemplateManager = () => {
 | 
				
			||||||
 | 
					  const { refresh, templates = [] } = useContext(SchemaTemplateManagerContext);
 | 
				
			||||||
 | 
					  const api = useAPIClient();
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    templates,
 | 
				
			||||||
 | 
					    refresh,
 | 
				
			||||||
 | 
					    async getTemplateSchemaByMode(options) {
 | 
				
			||||||
 | 
					      const { mode, template } = options;
 | 
				
			||||||
 | 
					      if (mode === 'copy') {
 | 
				
			||||||
 | 
					        const { data } = await api.request({
 | 
				
			||||||
 | 
					          url: `/uiSchemas:getJsonSchema/${template.uid}`,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        const s = data?.data || {};
 | 
				
			||||||
 | 
					        regenerateUid(s);
 | 
				
			||||||
 | 
					        return cloneDeep(s);
 | 
				
			||||||
 | 
					      } else if (mode === 'reference') {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          'x-component': 'BlockTemplate',
 | 
				
			||||||
 | 
					          'x-component-props': {
 | 
				
			||||||
 | 
					            templateId: template.key,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async copyTemplateSchema(template) {
 | 
				
			||||||
 | 
					      const { data } = await api.request({
 | 
				
			||||||
 | 
					        url: `/uiSchemas:getJsonSchema/${template.uid}`,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      const s = data?.data || {};
 | 
				
			||||||
 | 
					      regenerateUid(s);
 | 
				
			||||||
 | 
					      return cloneDeep(s);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async saveAsTemplate(values) {
 | 
				
			||||||
 | 
					      const { uid: schemaId } = values;
 | 
				
			||||||
 | 
					      const key = uid();
 | 
				
			||||||
 | 
					      await api.resource('uiSchemaTemplates').create({
 | 
				
			||||||
 | 
					        values: {
 | 
				
			||||||
 | 
					          key,
 | 
				
			||||||
 | 
					          ...values,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await api.request({
 | 
				
			||||||
 | 
					        url: `/uiSchemas:clearAncestor/${schemaId}`,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await refresh();
 | 
				
			||||||
 | 
					      return { key };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async duplicate(template) {
 | 
				
			||||||
 | 
					      const { data } = await api.request({
 | 
				
			||||||
 | 
					        url: `/uiSchemas:getJsonSchema/${template.uid}`,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      const s = data?.data || {};
 | 
				
			||||||
 | 
					      regenerateUid(s);
 | 
				
			||||||
 | 
					      return s;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getTemplateBySchemaId(schemaId) {
 | 
				
			||||||
 | 
					      return templates?.find((template) => template.uid === schemaId);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getTemplateById(key) {
 | 
				
			||||||
 | 
					      return templates?.find((template) => template.key === key);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    getTemplatesByCollection(collectionName: string) {
 | 
				
			||||||
 | 
					      const items = templates?.filter?.((template) => template.collectionName === collectionName);
 | 
				
			||||||
 | 
					      return items || [];
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const RemoteSchemaTemplateManagerProvider: React.FC = (props) => {
 | 
				
			||||||
 | 
					  const api = useAPIClient();
 | 
				
			||||||
 | 
					  const options = {
 | 
				
			||||||
 | 
					    resource: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					    action: 'list',
 | 
				
			||||||
 | 
					    params: {
 | 
				
			||||||
 | 
					      appends: ['collection'],
 | 
				
			||||||
 | 
					      paginate: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  const service = useRequest(options);
 | 
				
			||||||
 | 
					  if (service.loading) {
 | 
				
			||||||
 | 
					    return <Spin />;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <SchemaTemplateManagerProvider
 | 
				
			||||||
 | 
					      refresh={async () => {
 | 
				
			||||||
 | 
					        const { data } = await api.request(options);
 | 
				
			||||||
 | 
					        service.mutate(data);
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      templates={service?.data?.data}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      {props.children}
 | 
				
			||||||
 | 
					    </SchemaTemplateManagerProvider>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { LayoutOutlined } from '@ant-design/icons';
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
 | 
					import { useHistory } from 'react-router-dom';
 | 
				
			||||||
 | 
					import { PluginManager } from '../plugin-manager';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SchemaTemplateShortcut = () => {
 | 
				
			||||||
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					  const history = useHistory();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <PluginManager.Toolbar.Item
 | 
				
			||||||
 | 
					      icon={<LayoutOutlined />}
 | 
				
			||||||
 | 
					      title={t('Block templates')}
 | 
				
			||||||
 | 
					      onClick={() => {
 | 
				
			||||||
 | 
					        history.push('/admin/block-templates');
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										5
									
								
								packages/client/src/schema-templates/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								packages/client/src/schema-templates/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					export * from './BlockTemplateDetails';
 | 
				
			||||||
 | 
					export * from './BlockTemplatePage';
 | 
				
			||||||
 | 
					export * from './SchemaTemplateManagerProvider';
 | 
				
			||||||
 | 
					export * from './SchemaTemplateShortcut';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,153 @@
 | 
				
			|||||||
 | 
					import { ISchema } from '@formily/react';
 | 
				
			||||||
 | 
					import { CollectionOptions } from '../../collection-manager';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const collection: CollectionOptions = {
 | 
				
			||||||
 | 
					  name: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					  filterTargetKey: 'name',
 | 
				
			||||||
 | 
					  targetKey: 'name',
 | 
				
			||||||
 | 
					  fields: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'integer',
 | 
				
			||||||
 | 
					      name: 'name',
 | 
				
			||||||
 | 
					      interface: 'input',
 | 
				
			||||||
 | 
					      uiSchema: {
 | 
				
			||||||
 | 
					        title: '{{ t("Template name") }}',
 | 
				
			||||||
 | 
					        type: 'number',
 | 
				
			||||||
 | 
					        'x-component': 'Input',
 | 
				
			||||||
 | 
					        required: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const uiSchemaTemplatesSchema: ISchema = {
 | 
				
			||||||
 | 
					  type: 'object',
 | 
				
			||||||
 | 
					  properties: {
 | 
				
			||||||
 | 
					    block1: {
 | 
				
			||||||
 | 
					      type: 'void',
 | 
				
			||||||
 | 
					      'x-collection': 'collections',
 | 
				
			||||||
 | 
					      'x-decorator': 'ResourceActionProvider',
 | 
				
			||||||
 | 
					      'x-decorator-props': {
 | 
				
			||||||
 | 
					        collection,
 | 
				
			||||||
 | 
					        request: {
 | 
				
			||||||
 | 
					          resource: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					          action: 'list',
 | 
				
			||||||
 | 
					          params: {
 | 
				
			||||||
 | 
					            pageSize: 50,
 | 
				
			||||||
 | 
					            filter: {},
 | 
				
			||||||
 | 
					            // sort: ['sort'],
 | 
				
			||||||
 | 
					            appends: [],
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      // 'x-component': 'CollectionProvider',
 | 
				
			||||||
 | 
					      // 'x-component-props': {
 | 
				
			||||||
 | 
					      //   collection,
 | 
				
			||||||
 | 
					      // },
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        actions: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          'x-component': 'ActionBar',
 | 
				
			||||||
 | 
					          'x-component-props': {
 | 
				
			||||||
 | 
					            style: {
 | 
				
			||||||
 | 
					              marginBottom: 16,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            delete: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: '{{ t("Delete") }}',
 | 
				
			||||||
 | 
					              'x-component': 'Action',
 | 
				
			||||||
 | 
					              'x-component-props': {
 | 
				
			||||||
 | 
					                useAction: '{{ cm.useBulkDestroyActionAndRefreshCM }}',
 | 
				
			||||||
 | 
					                confirm: {
 | 
				
			||||||
 | 
					                  title: "{{t('Delete record')}}",
 | 
				
			||||||
 | 
					                  content: "{{t('Are you sure you want to delete it?')}}",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            create: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: '{{ t("Add block template") }}',
 | 
				
			||||||
 | 
					              'x-component': 'Action',
 | 
				
			||||||
 | 
					              'x-component-props': {
 | 
				
			||||||
 | 
					                type: 'primary',
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        table: {
 | 
				
			||||||
 | 
					          type: 'void',
 | 
				
			||||||
 | 
					          'x-uid': 'input',
 | 
				
			||||||
 | 
					          'x-component': 'Table.Void',
 | 
				
			||||||
 | 
					          'x-component-props': {
 | 
				
			||||||
 | 
					            rowKey: 'name',
 | 
				
			||||||
 | 
					            rowSelection: {
 | 
				
			||||||
 | 
					              type: 'checkbox',
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            useDataSource: '{{ cm.useDataSourceFromRAC }}',
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          properties: {
 | 
				
			||||||
 | 
					            column1: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              'x-decorator': 'Table.Column.Decorator',
 | 
				
			||||||
 | 
					              'x-component': 'Table.Column',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                title: {
 | 
				
			||||||
 | 
					                  'x-component': 'CollectionField',
 | 
				
			||||||
 | 
					                  'x-read-pretty': true,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            column2: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              'x-decorator': 'Table.Column.Decorator',
 | 
				
			||||||
 | 
					              'x-component': 'Table.Column',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                name: {
 | 
				
			||||||
 | 
					                  type: 'string',
 | 
				
			||||||
 | 
					                  'x-component': 'CollectionField',
 | 
				
			||||||
 | 
					                  'x-read-pretty': true,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            column3: {
 | 
				
			||||||
 | 
					              type: 'void',
 | 
				
			||||||
 | 
					              title: '{{ t("Actions") }}',
 | 
				
			||||||
 | 
					              'x-component': 'Table.Column',
 | 
				
			||||||
 | 
					              properties: {
 | 
				
			||||||
 | 
					                actions: {
 | 
				
			||||||
 | 
					                  type: 'void',
 | 
				
			||||||
 | 
					                  'x-component': 'Space',
 | 
				
			||||||
 | 
					                  'x-component-props': {
 | 
				
			||||||
 | 
					                    split: '|',
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  properties: {
 | 
				
			||||||
 | 
					                    view: {
 | 
				
			||||||
 | 
					                      type: 'void',
 | 
				
			||||||
 | 
					                      title: '{{ t("Configure fields") }}',
 | 
				
			||||||
 | 
					                      'x-component': 'Action.Link',
 | 
				
			||||||
 | 
					                      'x-component-props': {},
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    delete: {
 | 
				
			||||||
 | 
					                      type: 'void',
 | 
				
			||||||
 | 
					                      title: '{{ t("Delete") }}',
 | 
				
			||||||
 | 
					                      'x-component': 'Action.Link',
 | 
				
			||||||
 | 
					                      'x-component-props': {
 | 
				
			||||||
 | 
					                        confirm: {
 | 
				
			||||||
 | 
					                          title: "{{t('Delete record')}}",
 | 
				
			||||||
 | 
					                          content: "{{t('Are you sure you want to delete it?')}}",
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        useAction: '{{ cm.useDestroyActionAndRefreshCM }}',
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -40,6 +40,16 @@ export class UiRoutesStoragePlugin extends Plugin {
 | 
				
			|||||||
            path: '/admin/workflow',
 | 
					            path: '/admin/workflow',
 | 
				
			||||||
            component: 'WorkflowPage',
 | 
					            component: 'WorkflowPage',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 | 
					          // {
 | 
				
			||||||
 | 
					          //   type: 'route',
 | 
				
			||||||
 | 
					          //   path: '/admin/block-templates/:key',
 | 
				
			||||||
 | 
					          //   component: 'BlockTemplateDetails',
 | 
				
			||||||
 | 
					          // },
 | 
				
			||||||
 | 
					          // {
 | 
				
			||||||
 | 
					          //   type: 'route',
 | 
				
			||||||
 | 
					          //   path: '/admin/block-templates',
 | 
				
			||||||
 | 
					          //   component: 'BlockTemplatePage',
 | 
				
			||||||
 | 
					          // },
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            type: 'route',
 | 
					            type: 'route',
 | 
				
			||||||
            path: '/admin/:name(.+)?',
 | 
					            path: '/admin/:name(.+)?',
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import { defineCollection } from '@nocobase/database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineCollection({
 | 
				
			||||||
 | 
					  name: 'uiSchemaTemplates',
 | 
				
			||||||
 | 
					  autoGenId: false,
 | 
				
			||||||
 | 
					  fields: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'uid',
 | 
				
			||||||
 | 
					      name: 'key',
 | 
				
			||||||
 | 
					      primaryKey: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      name: 'name',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      name: 'componentName',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'belongsTo',
 | 
				
			||||||
 | 
					      name: 'uiSchema',
 | 
				
			||||||
 | 
					      target: 'uiSchemas',
 | 
				
			||||||
 | 
					      foreignKey: 'uid',
 | 
				
			||||||
 | 
					      onDelete: 'CASCADE',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      type: 'belongsTo',
 | 
				
			||||||
 | 
					      name: 'collection',
 | 
				
			||||||
 | 
					      target: 'collections',
 | 
				
			||||||
 | 
					      foreignKey: 'collectionName',
 | 
				
			||||||
 | 
					      targetKey: 'name',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user