feat: improve schema Initializer
This commit is contained in:
parent
78e89b76ef
commit
b656f69565
@ -5,17 +5,18 @@ import {
|
||||
AntdSchemaComponentProvider,
|
||||
APIClientProvider,
|
||||
AuthLayout,
|
||||
CollectionManagerProvider,
|
||||
CollectionManagerShortcut,
|
||||
compose,
|
||||
DesignableSwitch,
|
||||
DocumentTitleProvider,
|
||||
i18n,
|
||||
PluginManagerProvider,
|
||||
RemoteCollectionManagerProvider,
|
||||
RouteSchemaComponent,
|
||||
RouteSwitch,
|
||||
RouteSwitchProvider,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializerProvider,
|
||||
SigninPage,
|
||||
SignupPage,
|
||||
SystemSettingsProvider,
|
||||
@ -38,13 +39,32 @@ const providers = [
|
||||
SystemSettingsProvider,
|
||||
[
|
||||
PluginManagerProvider,
|
||||
{ components: { ACLShortcut, DesignableSwitch, CollectionManagerShortcut, SystemSettingsShortcut } },
|
||||
{
|
||||
components: {
|
||||
ACLShortcut,
|
||||
DesignableSwitch,
|
||||
CollectionManagerShortcut,
|
||||
SystemSettingsShortcut,
|
||||
},
|
||||
},
|
||||
],
|
||||
[SchemaComponentProvider, { components: { Link, NavLink } }],
|
||||
CollectionManagerProvider,
|
||||
RemoteCollectionManagerProvider,
|
||||
SchemaInitializerProvider,
|
||||
AntdSchemaComponentProvider,
|
||||
[DocumentTitleProvider, { addonAfter: 'NocoBase' }],
|
||||
[RouteSwitchProvider, { components: { AuthLayout, AdminLayout, RouteSchemaComponent, SigninPage, SignupPage } }],
|
||||
[
|
||||
RouteSwitchProvider,
|
||||
{
|
||||
components: {
|
||||
AuthLayout,
|
||||
AdminLayout,
|
||||
RouteSchemaComponent,
|
||||
SigninPage,
|
||||
SignupPage,
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const App = compose(...providers)(() => {
|
||||
|
@ -2,6 +2,7 @@ import { ISchema } from '@formily/react';
|
||||
|
||||
const collection = {
|
||||
name: 'collections',
|
||||
targetKey: 'name',
|
||||
filterTargetKey: 'name',
|
||||
fields: [
|
||||
{
|
||||
@ -42,6 +43,7 @@ export const roleCollectionsSchema: ISchema = {
|
||||
type: 'void',
|
||||
'x-decorator': 'ResourceActionProvider',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
association: {
|
||||
sourceKey: 'name',
|
||||
targetKey: 'name',
|
||||
@ -58,10 +60,6 @@ export const roleCollectionsSchema: ISchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
'x-component': 'CollectionProvider',
|
||||
'x-component-props': {
|
||||
collection,
|
||||
},
|
||||
properties: {
|
||||
table1: {
|
||||
type: 'void',
|
||||
|
@ -38,9 +38,7 @@ export const roleSchema: ISchema = {
|
||||
type: 'void',
|
||||
'x-decorator': 'ResourceActionProvider',
|
||||
'x-decorator-props': {
|
||||
collection: {
|
||||
targetKey: 'name',
|
||||
},
|
||||
collection,
|
||||
resourceName: 'roles',
|
||||
request: {
|
||||
resource: 'roles',
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
RouteSwitch,
|
||||
RouteSwitchProvider,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializerProvider,
|
||||
SigninPage,
|
||||
SignupPage,
|
||||
SystemSettingsProvider,
|
||||
@ -42,6 +43,7 @@ const providers = [
|
||||
[SchemaComponentProvider, { components: { Link, NavLink } }],
|
||||
CollectionManagerProvider,
|
||||
AntdSchemaComponentProvider,
|
||||
SchemaInitializerProvider,
|
||||
[DocumentTitleProvider, { addonAfter: 'NocoBase' }],
|
||||
[RouteSwitchProvider, { components: { AuthLayout, AdminLayout, RouteSchemaComponent, SigninPage, SignupPage } }],
|
||||
];
|
||||
|
@ -1,15 +1,54 @@
|
||||
import { Spin } from 'antd';
|
||||
import React from 'react';
|
||||
import { useAPIClient, useRequest } from '../api-client';
|
||||
import { CollectionManagerSchemaComponentProvider } from './CollectionManagerSchemaComponentProvider';
|
||||
import { CollectionManagerContext } from './context';
|
||||
import * as defaultInterfaces from './interfaces';
|
||||
import { CollectionManagerOptions } from './types';
|
||||
|
||||
export const CollectionManagerProvider: React.FC<CollectionManagerOptions> = (props) => {
|
||||
const { interfaces, collections } = props;
|
||||
const { service, interfaces, collections = [], refreshCM } = props;
|
||||
return (
|
||||
<CollectionManagerContext.Provider value={{ interfaces, collections }}>
|
||||
<CollectionManagerSchemaComponentProvider>
|
||||
{props.children}
|
||||
</CollectionManagerSchemaComponentProvider>
|
||||
<CollectionManagerContext.Provider
|
||||
value={{
|
||||
service,
|
||||
interfaces: { ...defaultInterfaces, ...interfaces },
|
||||
collections,
|
||||
refreshCM: async () => {
|
||||
if (refreshCM) {
|
||||
await refreshCM();
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CollectionManagerSchemaComponentProvider>{props.children}</CollectionManagerSchemaComponentProvider>
|
||||
</CollectionManagerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const RemoteCollectionManagerProvider = (props: any) => {
|
||||
const api = useAPIClient();
|
||||
const options = {
|
||||
resource: 'collections',
|
||||
action: 'list',
|
||||
params: {
|
||||
perPage: 999,
|
||||
appends: ['fields', 'fields.uiSchema'],
|
||||
},
|
||||
};
|
||||
const service = useRequest(options);
|
||||
if (service.loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<CollectionManagerProvider
|
||||
service={service}
|
||||
collections={service?.data?.data}
|
||||
refreshCM={async () => {
|
||||
const { data } = await api.request(options);
|
||||
service.mutate(data);
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,23 +1,10 @@
|
||||
import { DatabaseOutlined } from '@ant-design/icons';
|
||||
import { ISchema, useForm } from '@formily/react';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React, { useState } from 'react';
|
||||
import { PluginManager } from '../plugin-manager';
|
||||
import { ActionContext, SchemaComponent, useActionContext } from '../schema-component';
|
||||
import { ConfigurationTable } from './Configuration';
|
||||
|
||||
const useCloseAction = () => {
|
||||
const { setVisible } = useActionContext();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
setVisible(false);
|
||||
form.submit((values) => {
|
||||
console.log(values);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
import { ActionContext, SchemaComponent } from '../schema-component';
|
||||
import { AddFieldAction, ConfigurationTable, EditFieldAction } from './Configuration';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'object',
|
||||
@ -46,7 +33,7 @@ export const CollectionManagerShortcut = () => {
|
||||
setVisible(true);
|
||||
}}
|
||||
/>
|
||||
<SchemaComponent scope={{ useCloseAction }} schema={schema} components={{ ConfigurationTable }} />
|
||||
<SchemaComponent schema={schema} components={{ ConfigurationTable, AddFieldAction, EditFieldAction }} />
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,134 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { Button, Dropdown, Menu } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useAPIClient } from '../../api-client';
|
||||
import { useRecord } from '../../record-provider';
|
||||
import { ActionContext, SchemaComponent } from '../../schema-component';
|
||||
import { useCollectionManager } from '../hooks';
|
||||
import { IField } from '../interfaces/types';
|
||||
|
||||
const getSchema = (schema: IField, useAction): ISchema => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
type: 'object',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
initialValue: {
|
||||
...schema.default,
|
||||
name: `f_${uid()}`,
|
||||
},
|
||||
},
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
type: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
title: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
name: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
// @ts-ignore
|
||||
...schema.properties,
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
properties: {
|
||||
action1: {
|
||||
title: 'Cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCancelAction }}',
|
||||
},
|
||||
},
|
||||
action2: {
|
||||
title: 'Submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const AddFieldAction = () => {
|
||||
const { getInterface } = useCollectionManager();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [schema, setSchema] = useState({});
|
||||
return (
|
||||
<ActionContext.Provider value={{ visible, setVisible }}>
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu
|
||||
onClick={(info) => {
|
||||
const schema = getSchema(getInterface(info.key), '{{ useCreateActionAndRefreshCM }}');
|
||||
setSchema(schema);
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
<Menu.SubMenu title={'基本类型'}>
|
||||
<Menu.Item key={'input'}>input</Menu.Item>
|
||||
<Menu.Item key={'textarea'}>textarea</Menu.Item>
|
||||
<Menu.Item key={'chinaRegion'}>China region</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button icon={<PlusOutlined />} type={'primary'}>
|
||||
添加字段
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<SchemaComponent schema={schema} />
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const EditFieldAction = (props) => {
|
||||
const record = useRecord();
|
||||
const { getInterface } = useCollectionManager();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [schema, setSchema] = useState({});
|
||||
const api = useAPIClient();
|
||||
return (
|
||||
<ActionContext.Provider value={{ visible, setVisible }}>
|
||||
<a
|
||||
onClick={async () => {
|
||||
const { data } = await api.resource('collections.fields', record.collectionName).get({
|
||||
filterByTk: record.name,
|
||||
appends: ['uiSchema'],
|
||||
});
|
||||
const schema = getSchema(
|
||||
{
|
||||
...getInterface(record.interface),
|
||||
default: data?.data,
|
||||
},
|
||||
'{{ useUpdateActionAndRefreshCM }}',
|
||||
);
|
||||
setSchema(schema);
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</a>
|
||||
<SchemaComponent schema={schema} />
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
};
|
@ -5,9 +5,7 @@ import { collectionSchema } from './schemas/collections';
|
||||
export const ConfigurationTable = () => {
|
||||
return (
|
||||
<div>
|
||||
<SchemaComponent
|
||||
schema={collectionSchema}
|
||||
/>
|
||||
<SchemaComponent schema={collectionSchema} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1 +1,3 @@
|
||||
export * from './AddFieldAction';
|
||||
export * from './ConfigurationTable';
|
||||
|
||||
|
@ -20,6 +20,16 @@ const collection = {
|
||||
required: true,
|
||||
} as ISchema,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'interface',
|
||||
interface: 'input',
|
||||
uiSchema: {
|
||||
title: '字段类型',
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
@ -54,6 +64,7 @@ export const collectionFieldSchema: ISchema = {
|
||||
sourceKey: 'name',
|
||||
targetKey: 'name',
|
||||
},
|
||||
collection,
|
||||
request: {
|
||||
resource: 'collections.fields',
|
||||
action: 'list',
|
||||
@ -65,10 +76,10 @@ export const collectionFieldSchema: ISchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
'x-component': 'CollectionProvider',
|
||||
'x-component-props': {
|
||||
collection,
|
||||
},
|
||||
// 'x-component': 'CollectionProvider',
|
||||
// 'x-component-props': {
|
||||
// collection,
|
||||
// },
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
@ -82,57 +93,14 @@ export const collectionFieldSchema: ISchema = {
|
||||
create: {
|
||||
type: 'void',
|
||||
title: '创建',
|
||||
'x-component': 'Action',
|
||||
'x-component': 'AddFieldAction',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
type: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
title: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
name: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
properties: {
|
||||
action1: {
|
||||
title: 'Cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCancelAction }}',
|
||||
},
|
||||
},
|
||||
action2: {
|
||||
title: 'Submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useCreateAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
table1: {
|
||||
table: {
|
||||
type: 'void',
|
||||
'x-uid': 'input',
|
||||
'x-component': 'VoidTable',
|
||||
@ -169,6 +137,18 @@ export const collectionFieldSchema: ISchema = {
|
||||
},
|
||||
},
|
||||
column3: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableColumnDecorator',
|
||||
'x-component': 'VoidTable.Column',
|
||||
properties: {
|
||||
interface: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
column4: {
|
||||
type: 'void',
|
||||
title: 'Actions',
|
||||
'x-component': 'VoidTable.Column',
|
||||
@ -183,60 +163,17 @@ export const collectionFieldSchema: ISchema = {
|
||||
update: {
|
||||
type: 'void',
|
||||
title: '编辑',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component': 'EditFieldAction',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
useValues: '{{ useValues }}',
|
||||
},
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
title: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
name: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-disabled': true,
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
properties: {
|
||||
action1: {
|
||||
title: 'Cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCancelAction }}',
|
||||
},
|
||||
},
|
||||
action2: {
|
||||
title: 'Submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useUpdateAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
type: 'void',
|
||||
title: '删除',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useDestroyAction }}',
|
||||
useAction: '{{ useDestroyActionAndRefreshCM }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { CollectionOptions } from '../../types';
|
||||
import { collectionFieldSchema } from './collectionFields';
|
||||
|
||||
const collection = {
|
||||
const collection: CollectionOptions = {
|
||||
name: 'collections',
|
||||
filterTargetKey: 'name',
|
||||
targetKey: 'name',
|
||||
fields: [
|
||||
{
|
||||
type: 'integer',
|
||||
@ -14,7 +16,7 @@ const collection = {
|
||||
type: 'number',
|
||||
'x-component': 'Input',
|
||||
required: true,
|
||||
} as ISchema,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
@ -25,7 +27,7 @@ const collection = {
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
description: '使用英文',
|
||||
} as ISchema,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
@ -47,9 +49,7 @@ export const collectionSchema: ISchema = {
|
||||
'x-collection': 'collections',
|
||||
'x-decorator': 'ResourceActionProvider',
|
||||
'x-decorator-props': {
|
||||
collection: {
|
||||
targetKey: 'name',
|
||||
},
|
||||
collection,
|
||||
request: {
|
||||
resource: 'collections',
|
||||
action: 'list',
|
||||
@ -61,10 +61,10 @@ export const collectionSchema: ISchema = {
|
||||
},
|
||||
},
|
||||
},
|
||||
'x-component': 'CollectionProvider',
|
||||
'x-component-props': {
|
||||
collection,
|
||||
},
|
||||
// 'x-component': 'CollectionProvider',
|
||||
// 'x-component-props': {
|
||||
// collection,
|
||||
// },
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
@ -113,7 +113,7 @@ export const collectionSchema: ISchema = {
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useCreateAction }}',
|
||||
useAction: '{{ useCreateActionAndRefreshCM }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -201,7 +201,7 @@ export const collectionSchema: ISchema = {
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
useValues: '{{ useValues }}',
|
||||
useValues: '{{ useValuesFromRecord }}',
|
||||
},
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
@ -230,7 +230,7 @@ export const collectionSchema: ISchema = {
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useUpdateAction }}',
|
||||
useAction: '{{ useUpdateActionAndRefreshCM }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -244,7 +244,7 @@ export const collectionSchema: ISchema = {
|
||||
title: '删除',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useDestroyAction }}',
|
||||
useAction: '{{ useDestroyActionAndRefreshCM }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Result } from 'ahooks/lib/useRequest/src/types';
|
||||
import React, { createContext, useContext, useEffect } from 'react';
|
||||
import { useRecord } from '..';
|
||||
import { CollectionProvider, useRecord } from '..';
|
||||
import { useAPIClient, useRequest } from '../api-client';
|
||||
|
||||
export const ResourceActionContext = createContext<Result<any, any>>(null);
|
||||
@ -18,18 +18,20 @@ const CollectionResourceActionProvider = (props) => {
|
||||
const api = useAPIClient();
|
||||
const service = useRequest(request, {
|
||||
uid,
|
||||
refreshDeps: [request],
|
||||
// refreshDeps: [request],
|
||||
});
|
||||
const resource = api.resource(request.resource);
|
||||
return (
|
||||
<ResourceContext.Provider value={{ type: 'collection', resource, collection }}>
|
||||
<ResourceActionContext.Provider value={service}>{props.children}</ResourceActionContext.Provider>
|
||||
<ResourceActionContext.Provider value={service}>
|
||||
<CollectionProvider collection={collection}>{props.children}</CollectionProvider>
|
||||
</ResourceActionContext.Provider>
|
||||
</ResourceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const AssociationResourceActionProvider = (props) => {
|
||||
const { association, request, uid } = props;
|
||||
const { collection, association, request, uid } = props;
|
||||
const api = useAPIClient();
|
||||
const record = useRecord();
|
||||
const resourceOf = record[association.sourceKey];
|
||||
@ -37,13 +39,15 @@ const AssociationResourceActionProvider = (props) => {
|
||||
{ resourceOf, ...request },
|
||||
{
|
||||
uid,
|
||||
refreshDeps: [request, resourceOf],
|
||||
// refreshDeps: [request, resourceOf],
|
||||
},
|
||||
);
|
||||
const resource = api.resource(request.resource, resourceOf);
|
||||
return (
|
||||
<ResourceContext.Provider value={{ type: 'association', resource, association }}>
|
||||
<ResourceActionContext.Provider value={service}>{props.children}</ResourceActionContext.Provider>
|
||||
<ResourceActionContext.Provider value={service}>
|
||||
<CollectionProvider collection={collection}>{props.children}</CollectionProvider>
|
||||
</ResourceActionContext.Provider>
|
||||
</ResourceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useForm } from '@formily/react';
|
||||
import { useCollectionManager } from '.';
|
||||
import { useRequest } from '../api-client';
|
||||
import { useRecord } from '../record-provider';
|
||||
import { useActionContext } from '../schema-component';
|
||||
@ -60,10 +61,43 @@ export const useDestroyAction = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useValues = (options) => {
|
||||
export const useValuesFromRecord = (options) => {
|
||||
const record = useRecord();
|
||||
return useRequest(() => Promise.resolve({ data: record }), {
|
||||
...options,
|
||||
refreshDeps: [record],
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateActionAndRefreshCM = () => {
|
||||
const { run } = useCreateAction();
|
||||
const { refreshCM } = useCollectionManager();
|
||||
return {
|
||||
async run() {
|
||||
await run();
|
||||
await refreshCM();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const useUpdateActionAndRefreshCM = () => {
|
||||
const { run } = useUpdateAction();
|
||||
const { refreshCM } = useCollectionManager();
|
||||
return {
|
||||
async run() {
|
||||
await run();
|
||||
await refreshCM();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const useDestroyActionAndRefreshCM = () => {
|
||||
const { run } = useDestroyAction();
|
||||
const { refreshCM } = useCollectionManager();
|
||||
return {
|
||||
async run() {
|
||||
await run();
|
||||
await refreshCM();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -11,7 +11,6 @@ export const useCollectionField = () => {
|
||||
const ctx = useContext(CollectionFieldContext);
|
||||
const resourceName = `${ctx?.collectinName || collection?.name}.${ctx.name}`;
|
||||
const resource = api?.resource(resourceName, record[ctx.sourceKey]);
|
||||
console.log({ resourceName });
|
||||
return {
|
||||
...ctx,
|
||||
resource,
|
||||
|
@ -1,11 +1,19 @@
|
||||
import { clone } from '@formily/shared';
|
||||
import { useContext } from 'react';
|
||||
import { CollectionManagerContext } from '../context';
|
||||
|
||||
export const useCollectionManager = () => {
|
||||
const { collections } = useContext(CollectionManagerContext);
|
||||
const { refreshCM, service, interfaces, collections } = useContext(CollectionManagerContext);
|
||||
return {
|
||||
service,
|
||||
interfaces,
|
||||
collections,
|
||||
refreshCM: () => refreshCM?.(),
|
||||
get(name: string) {
|
||||
return collections?.find((collection) => collection.name === name);
|
||||
},
|
||||
getInterface(name) {
|
||||
return interfaces[name] ? clone(interfaces[name]) : null;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,73 @@
|
||||
import { uid } from '@formily/shared';
|
||||
import { IField } from './types';
|
||||
|
||||
export const chinaRegion: IField = {
|
||||
name: 'chinaRegion',
|
||||
type: 'object',
|
||||
group: 'choices',
|
||||
order: 7,
|
||||
title: '{{t("China region")}}',
|
||||
isAssociation: true,
|
||||
default: {
|
||||
interface: 'chinaRegion',
|
||||
type: 'belongsToMany',
|
||||
target: 'chinaRegions',
|
||||
targetKey: 'code',
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'array',
|
||||
// title,
|
||||
'x-component': 'Cascader',
|
||||
'x-component-props': {
|
||||
changeOnSelectLast: false,
|
||||
labelInValue: true,
|
||||
maxLevel: 3,
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
initialize: (values: any) => {
|
||||
if (!values.through) {
|
||||
values.through = `t_${uid()}`;
|
||||
}
|
||||
if (!values.foreignKey) {
|
||||
values.foreignKey = `f_${uid()}`;
|
||||
}
|
||||
if (!values.otherKey) {
|
||||
values.otherKey = `f_${uid()}`;
|
||||
}
|
||||
if (!values.sourceKey) {
|
||||
values.sourceKey = 'id';
|
||||
}
|
||||
if (!values.targetKey) {
|
||||
values.targetKey = 'id';
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
'uiSchema.x-component-props.maxLevel': {
|
||||
type: 'number',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
title: '{{t("Select level")}}',
|
||||
default: 3,
|
||||
enum: [
|
||||
{ value: 1, label: '{{t("Province")}}' },
|
||||
{ value: 2, label: '{{t("City")}}' },
|
||||
{ value: 3, label: '{{t("Area")}}' },
|
||||
{ value: 4, label: '{{t("Street")}}' },
|
||||
{ value: 5, label: '{{t("Village")}}' },
|
||||
],
|
||||
},
|
||||
'uiSchema.x-component-props.changeOnSelectLast': {
|
||||
type: 'boolean',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Must select to the last level")}}',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
},
|
||||
operators: [{ label: '{{t("is")}}', value: 'code.in' }],
|
||||
};
|
@ -0,0 +1,4 @@
|
||||
export * from './chinaRegion';
|
||||
export * from './input';
|
||||
export * from './textarea';
|
||||
|
28
packages/client/src/collection-manager/interfaces/input.ts
Normal file
28
packages/client/src/collection-manager/interfaces/input.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { IField } from './types';
|
||||
|
||||
export const input: IField = {
|
||||
name: 'string',
|
||||
type: 'object',
|
||||
group: 'basic',
|
||||
order: 1,
|
||||
title: '{{t("Single line text")}}',
|
||||
sortable: true,
|
||||
default: {
|
||||
interface: 'input',
|
||||
type: 'string',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
},
|
||||
operators: [
|
||||
{ label: '{{t("contains")}}', value: '$includes', selected: true },
|
||||
{ label: '{{t("does not contain")}}', value: '$notIncludes' },
|
||||
{ label: '{{t("is")}}', value: 'eq' },
|
||||
{ label: '{{t("is not")}}', value: 'ne' },
|
||||
{ label: '{{t("is empty")}}', value: '$null', noValue: true },
|
||||
{ label: '{{t("is not empty")}}', value: '$notNull', noValue: true },
|
||||
],
|
||||
};
|
@ -0,0 +1,198 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
|
||||
export const type: ISchema = {
|
||||
type: 'string',
|
||||
title: '{{t("Storage type")}}',
|
||||
required: true,
|
||||
'x-disabled': true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
enum: [
|
||||
{ label: 'Boolean', value: 'boolean' },
|
||||
{ label: 'String', value: 'string' },
|
||||
{ label: 'Text', value: 'text' },
|
||||
{ label: 'Integer', value: 'integer' },
|
||||
{ label: 'Float', value: 'float' },
|
||||
{ label: 'Decimal', value: 'decimal' },
|
||||
{ label: 'Date', value: 'date' },
|
||||
{ label: 'DateOnly', value: 'dateonly' },
|
||||
{ label: 'Time', value: 'time' },
|
||||
{ label: 'Virtual', value: 'virtual' },
|
||||
{ label: 'JSON', value: 'json' },
|
||||
{ label: 'Password', value: 'password' },
|
||||
{ label: 'HasOne', value: 'hasOne' },
|
||||
{ label: 'HasMany', value: 'hasMany' },
|
||||
{ label: 'BelongsTo', value: 'belongsTo' },
|
||||
{ label: 'BelongsToMany', value: 'belongsToMany' },
|
||||
],
|
||||
};
|
||||
|
||||
export const dateTimeProps: { [key: string]: ISchema } = {
|
||||
'uiSchema.x-component-props.dateFormat': {
|
||||
type: 'string',
|
||||
title: '{{t("Date format")}}',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
default: 'YYYY-MM-DD',
|
||||
enum: [
|
||||
{
|
||||
label: '{{t("Year/Month/Day")}}',
|
||||
value: 'YYYY/MM/DD',
|
||||
},
|
||||
{
|
||||
label: '{{t("Year-Month-Day")}}',
|
||||
value: 'YYYY-MM-DD',
|
||||
},
|
||||
{
|
||||
label: '{{t("Day/Month/Year")}}',
|
||||
value: 'DD/MM/YYYY',
|
||||
},
|
||||
],
|
||||
},
|
||||
'uiSchema.x-component-props.showTime': {
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
'x-content': '{{t("Show time")}}',
|
||||
'x-reactions': [
|
||||
`{{(field) => {
|
||||
field.query('..[].timeFormat').take(f => {
|
||||
f.display = field.value ? 'visible' : 'none';
|
||||
});
|
||||
}}}`,
|
||||
],
|
||||
},
|
||||
'uiSchema.x-component-props.timeFormat': {
|
||||
type: 'string',
|
||||
title: '{{t("Time format")}}',
|
||||
'x-component': 'Radio.Group',
|
||||
'x-decorator': 'FormItem',
|
||||
default: 'HH:mm:ss',
|
||||
enum: [
|
||||
{
|
||||
label: '{{t("12 hour")}}',
|
||||
value: 'hh:mm:ss a',
|
||||
},
|
||||
{
|
||||
label: '{{t("24 hour")}}',
|
||||
value: 'HH:mm:ss',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const dataSource: ISchema = {
|
||||
type: 'array',
|
||||
title: '{{t("Options")}}',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayTable',
|
||||
'x-component-props': {
|
||||
pagination: false,
|
||||
// scroll: { x: '100%' },
|
||||
},
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
column1: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
'x-component-props': { width: 50, title: '', align: 'center' },
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.SortHandle',
|
||||
},
|
||||
},
|
||||
},
|
||||
column2: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
'x-component-props': { title: '{{t("Option value")}}' },
|
||||
'x-hidden': true,
|
||||
properties: {
|
||||
value: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
},
|
||||
column3: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
'x-component-props': { title: '{{t("Option label")}}' },
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
},
|
||||
column4: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
'x-component-props': { title: '{{t("Color")}}' },
|
||||
properties: {
|
||||
color: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ColorSelect',
|
||||
},
|
||||
},
|
||||
},
|
||||
column5: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
'x-component-props': {
|
||||
title: '',
|
||||
dataIndex: 'operations',
|
||||
fixed: 'right',
|
||||
},
|
||||
properties: {
|
||||
item: {
|
||||
type: 'void',
|
||||
'x-component': 'FormItem',
|
||||
properties: {
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Remove',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
add: {
|
||||
type: 'void',
|
||||
'x-component': 'ArrayTable.Addition',
|
||||
'x-component-props': {
|
||||
randomValue: true,
|
||||
},
|
||||
title: "{{t('Add option')}}",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const defaultProps = {
|
||||
'uiSchema.title': {
|
||||
type: 'string',
|
||||
title: '{{t("Field display name")}}',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
title: '{{t("Field name")}}',
|
||||
required: true,
|
||||
// 'x-disabled': true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
description:
|
||||
"{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
|
||||
},
|
||||
type,
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
import { IField } from './types';
|
||||
|
||||
export const textarea: IField = {
|
||||
name: 'textarea',
|
||||
type: 'object',
|
||||
group: 'basic',
|
||||
order: 2,
|
||||
title: '{{t("Long text")}}',
|
||||
default: {
|
||||
interface: 'textarea',
|
||||
type: 'text',
|
||||
// name,
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
'x-component': 'Input.TextArea',
|
||||
},
|
||||
},
|
||||
properties: {},
|
||||
};
|
12
packages/client/src/collection-manager/interfaces/types.ts
Normal file
12
packages/client/src/collection-manager/interfaces/types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
|
||||
interface IDefault {
|
||||
type: string;
|
||||
uiSchema?: ISchema;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface IField extends ISchema {
|
||||
default?: IDefault;
|
||||
[key: string]: any;
|
||||
}
|
@ -1,14 +1,24 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
|
||||
export interface CollectionManagerOptions {
|
||||
service?: any;
|
||||
interfaces?: any;
|
||||
collections?: any[];
|
||||
refreshCM?: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface FieldOptions {
|
||||
type: string;
|
||||
interface?: string;
|
||||
uiSchema?: ISchema;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface CollectionOptions {
|
||||
name?: string;
|
||||
filterTargetKey?: string;
|
||||
fields?: any[];
|
||||
targetKey?: string;
|
||||
fields?: FieldOptions[];
|
||||
}
|
||||
|
||||
export interface ICollectionProviderProps {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ActionContext } from './context';
|
||||
@ -23,7 +23,7 @@ export const ActionLink: ComposedAction = observer((props: any) => {
|
||||
>
|
||||
{field.title}
|
||||
</Link>
|
||||
<RecursionField basePath={field.address} schema={schema} onlyRenderProperties />
|
||||
{props.children}
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { observer, RecursionField, useField, useFieldSchema } from '@formily/react';
|
||||
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||
import { Button } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import ActionContainer from './Action.Container';
|
||||
@ -28,7 +28,7 @@ export const Action: ComposedAction = observer((props: any) => {
|
||||
>
|
||||
{field.title}
|
||||
</Button>
|
||||
<RecursionField basePath={field.address} schema={schema} onlyRenderProperties />
|
||||
{props.children}
|
||||
</ActionContext.Provider>
|
||||
);
|
||||
});
|
||||
|
@ -1,58 +0,0 @@
|
||||
import { observer } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../../schema-initializer';
|
||||
|
||||
const useTableColumnInitializerFields = () => {
|
||||
const fields = [
|
||||
{
|
||||
title: 'Field1',
|
||||
name: 'field1',
|
||||
component: ArrayTableColumnInitializer,
|
||||
},
|
||||
{
|
||||
title: 'Field2',
|
||||
name: 'field2',
|
||||
component: ArrayTableColumnInitializer,
|
||||
},
|
||||
].filter((field) => field.component);
|
||||
return fields;
|
||||
};
|
||||
|
||||
export const TableColumnInitializeButton = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Display fields',
|
||||
children: useTableColumnInitializerFields(),
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure columns
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
|
||||
export const ArrayTableColumnInitializer = SchemaInitializer.itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
title: 'Name',
|
||||
'x-component': 'ArrayTable.Column',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { createDesignable, useDesignable } from '../..';
|
||||
import { useAPIClient } from '../../../api-client';
|
||||
|
||||
export const TableActionDesignable = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const api = useAPIClient();
|
||||
const { refresh } = useDesignable();
|
||||
return (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
const spaceSchema = fieldSchema.reduceProperties((buf, schema) => {
|
||||
if (schema['x-component'] === 'Space') {
|
||||
return schema;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
const dn = createDesignable({
|
||||
current: spaceSchema,
|
||||
refresh,
|
||||
api,
|
||||
});
|
||||
dn.loadAPIClientEvents();
|
||||
dn.insertBeforeEnd({
|
||||
type: 'void',
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useDestroyAction }}',
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</span>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
});
|
@ -0,0 +1,4 @@
|
||||
import React from 'react';
|
||||
export const TableActionDesignableBar = () => {
|
||||
return <div>Edit</div>;
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { useComponent } from '../..';
|
||||
|
||||
export const TableColumnActionBar = observer((props) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const ActionInitializer = useComponent(fieldSchema['x-action-initializer']);
|
||||
return (
|
||||
<div>
|
||||
{ActionInitializer && <ActionInitializer />}
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
});
|
@ -1,4 +1,5 @@
|
||||
export * from './ArrayTable';
|
||||
export * from './Initializer';
|
||||
export * from './TableColumnActionBar';
|
||||
export * from './TableColumnDecorator';
|
||||
|
||||
|
||||
|
@ -1,9 +1,18 @@
|
||||
import React from 'react';
|
||||
import { useDesignable } from '../..';
|
||||
import { SortableItem } from '../../common';
|
||||
|
||||
export const BlockItem: React.FC<any> = (props) => {
|
||||
const { remove } = useDesignable();
|
||||
return (
|
||||
<SortableItem className={'nb-block-item'} style={{ position: 'relative' }}>
|
||||
<a
|
||||
onClick={() => {
|
||||
remove();
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</a>
|
||||
{props.children}
|
||||
</SortableItem>
|
||||
);
|
||||
|
@ -6,8 +6,6 @@ import cls from 'classnames';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { useComponent } from '../..';
|
||||
import { DndContext } from '../../common/dnd-context';
|
||||
import { AddGridBlockItem } from './AddBlockItem';
|
||||
import { AddGridFormItem } from './AddFormItem';
|
||||
|
||||
const GridRowContext = createContext(null);
|
||||
const GridColContext = createContext(null);
|
||||
@ -209,6 +207,3 @@ Grid.Col = observer((props: any) => {
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Grid.AddFormItem = AddGridFormItem;
|
||||
Grid.AddBlockItem = AddGridBlockItem;
|
||||
|
@ -1,6 +1,15 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { Form, FormItem, Grid, Input, Markdown, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||
import {
|
||||
Form,
|
||||
FormItem,
|
||||
Grid,
|
||||
Input,
|
||||
Markdown,
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializerProvider
|
||||
} from '@nocobase/client';
|
||||
import React from 'react';
|
||||
|
||||
const schema: ISchema = {
|
||||
@ -8,7 +17,7 @@ const schema: ISchema = {
|
||||
name: 'grid1',
|
||||
'x-decorator': 'Form',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'Grid.AddFormItem',
|
||||
'x-item-initializer': 'AddFormItem',
|
||||
'x-uid': uid(),
|
||||
properties: {
|
||||
row1: {
|
||||
@ -56,7 +65,9 @@ const schema: ISchema = {
|
||||
export default function App() {
|
||||
return (
|
||||
<SchemaComponentProvider components={{ Markdown, Form, Grid, Input, FormItem }}>
|
||||
<SchemaComponent schema={schema} />
|
||||
<SchemaInitializerProvider>
|
||||
<SchemaComponent schema={schema} />
|
||||
</SchemaInitializerProvider>
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
Markdown,
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializerProvider,
|
||||
VoidTable
|
||||
} from '@nocobase/client';
|
||||
import React from 'react';
|
||||
@ -29,7 +30,7 @@ const schema: ISchema = {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'Grid.AddBlockItem',
|
||||
'x-item-initializer': 'AddBlockItem',
|
||||
'x-uid': uid(),
|
||||
properties: {},
|
||||
},
|
||||
@ -39,7 +40,9 @@ const schema: ISchema = {
|
||||
export default function App() {
|
||||
return (
|
||||
<SchemaComponentProvider components={{ BlockItem, Block, Grid, CardItem, Markdown, Form, VoidTable }}>
|
||||
<SchemaComponent schema={schema} />
|
||||
<SchemaInitializerProvider>
|
||||
<SchemaComponent schema={schema} />
|
||||
</SchemaInitializerProvider>
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -475,7 +475,7 @@ export function useDesignable() {
|
||||
update(key);
|
||||
refresh();
|
||||
},
|
||||
remove(schema: any, options?: RemoveOptions) {
|
||||
remove(schema?: any, options?: RemoveOptions) {
|
||||
dn.remove(schema, options);
|
||||
},
|
||||
insertAdjacent(position: Position, schema: ISchema, options?: InsertAdjacentOptions) {
|
||||
|
@ -0,0 +1,260 @@
|
||||
import { TableOutlined } from '@ant-design/icons';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { clone } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../';
|
||||
import { CollectionOptions, useCollectionManager } from '../../../collection-manager';
|
||||
|
||||
const collection: CollectionOptions = {
|
||||
name: 'collections',
|
||||
filterTargetKey: 'name',
|
||||
targetKey: 'name',
|
||||
fields: [
|
||||
{
|
||||
type: 'integer',
|
||||
name: 'title',
|
||||
interface: 'input',
|
||||
uiSchema: {
|
||||
title: '数据表名称',
|
||||
type: 'number',
|
||||
'x-component': 'Input',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
interface: 'input',
|
||||
uiSchema: {
|
||||
title: '数据表标识',
|
||||
type: 'string',
|
||||
'x-component': 'Input',
|
||||
description: '使用英文',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'fields',
|
||||
target: 'fields',
|
||||
collectionName: 'collections',
|
||||
sourceKey: 'name',
|
||||
targetKey: 'name',
|
||||
uiSchema: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const collectionSchema: ISchema = {
|
||||
type: 'void',
|
||||
'x-collection': 'collections',
|
||||
'x-component': 'CardItem',
|
||||
'x-decorator': 'ResourceActionProvider',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
request: {
|
||||
resource: 'collections',
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 5,
|
||||
filter: {},
|
||||
sort: ['sort'],
|
||||
appends: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'ActionBar',
|
||||
'x-action-initializer': 'AddActionButton',
|
||||
properties: {
|
||||
delete: {
|
||||
type: 'void',
|
||||
title: '删除',
|
||||
'x-component': 'Action',
|
||||
},
|
||||
create: {
|
||||
type: 'void',
|
||||
title: '创建',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
title: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
name: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
properties: {
|
||||
action1: {
|
||||
title: 'Cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCancelAction }}',
|
||||
},
|
||||
},
|
||||
action2: {
|
||||
title: 'Submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useCreateAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
table1: {
|
||||
type: 'void',
|
||||
'x-component': 'VoidTable',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
useDataSource: '{{ useDataSourceFromRAC }}',
|
||||
},
|
||||
'x-column-initializer': 'AddTableColumn',
|
||||
properties: {
|
||||
column3: {
|
||||
type: 'void',
|
||||
title: 'Actions',
|
||||
'x-component': 'VoidTable.Column',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
properties: {
|
||||
view: {
|
||||
type: 'void',
|
||||
title: '配置字段',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
title: 'Drawer Title',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {
|
||||
type: 'void',
|
||||
title: '编辑',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
useValues: '{{ useValues }}',
|
||||
},
|
||||
title: 'Drawer Title',
|
||||
properties: {
|
||||
title: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
name: {
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-disabled': true,
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer.Footer',
|
||||
properties: {
|
||||
action1: {
|
||||
title: 'Cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useCancelAction }}',
|
||||
},
|
||||
},
|
||||
action2: {
|
||||
title: 'Submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ useUpdateAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
type: 'void',
|
||||
title: '删除',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
useAction: '{{ useDestroyAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
export const FormBlockInitializerItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
const { collections } = useCollectionManager();
|
||||
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<TableOutlined />}
|
||||
onClick={() => {
|
||||
insert(clone(collectionSchema));
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'select a data source',
|
||||
children: collections?.map((item) => {
|
||||
return {
|
||||
type: 'item',
|
||||
name: item.name,
|
||||
title: item.title,
|
||||
};
|
||||
}),
|
||||
},
|
||||
]}
|
||||
>
|
||||
Form
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
@ -0,0 +1,135 @@
|
||||
import { TableOutlined } from '@ant-design/icons';
|
||||
import { ISchema } from '@formily/react';
|
||||
import { clone } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { CollectionOptions, useCollectionManager } from '../../../collection-manager';
|
||||
|
||||
const collection: CollectionOptions = {
|
||||
name: 'collections',
|
||||
filterTargetKey: 'name',
|
||||
targetKey: 'name',
|
||||
fields: [
|
||||
{
|
||||
type: 'integer',
|
||||
name: 'title',
|
||||
interface: 'input',
|
||||
uiSchema: {
|
||||
title: '数据表名称',
|
||||
type: 'number',
|
||||
required: true,
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
interface: 'input',
|
||||
uiSchema: {
|
||||
title: '数据表标识',
|
||||
type: 'string',
|
||||
description: '使用英文',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'fields',
|
||||
target: 'fields',
|
||||
collectionName: 'collections',
|
||||
sourceKey: 'name',
|
||||
targetKey: 'name',
|
||||
uiSchema: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const collectionSchema: ISchema = {
|
||||
type: 'void',
|
||||
'x-collection': 'collections',
|
||||
'x-component': 'CardItem',
|
||||
'x-decorator': 'ResourceActionProvider',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
request: {
|
||||
resource: 'collections',
|
||||
action: 'list',
|
||||
params: {
|
||||
pageSize: 5,
|
||||
filter: {},
|
||||
sort: ['sort'],
|
||||
appends: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'ActionBar',
|
||||
'x-action-initializer': 'TableActionInitializer',
|
||||
properties: {},
|
||||
},
|
||||
table1: {
|
||||
type: 'void',
|
||||
'x-component': 'VoidTable',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
},
|
||||
useDataSource: '{{ useDataSourceFromRAC }}',
|
||||
},
|
||||
'x-column-initializer': 'TableColumnInitializer',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
title: 'Actions',
|
||||
'x-decorator': 'TableColumnActionBar',
|
||||
'x-component': 'VoidTable.Column',
|
||||
'x-action-initializer': 'TableColumnActionInitializer',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
split: '|',
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
export const TableBlockInitializerItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
const { collections } = useCollectionManager();
|
||||
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<TableOutlined />}
|
||||
onClick={() => {
|
||||
insert(clone(collectionSchema));
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'select a data source',
|
||||
children: collections?.map((item) => {
|
||||
return {
|
||||
type: 'item',
|
||||
name: item.name,
|
||||
title: item.title,
|
||||
};
|
||||
}),
|
||||
},
|
||||
]}
|
||||
>
|
||||
Table
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
@ -1,8 +1,10 @@
|
||||
import { FormOutlined, TableOutlined } from '@ant-design/icons';
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import { ISchema, observer } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../../schema-initializer';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { FormBlockInitializerItem } from './FormBlockInitializerItem';
|
||||
import { TableBlockInitializerItem } from './TableBlockInitializerItem';
|
||||
|
||||
const gridRowColWrap = (schema: ISchema) => {
|
||||
return {
|
||||
@ -22,87 +24,6 @@ const gridRowColWrap = (schema: ISchema) => {
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
const TableBlockInitializerItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<TableOutlined />}
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'VoidTable',
|
||||
'x-decorator': 'CardItem',
|
||||
});
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'select a data source',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
name: 'users',
|
||||
title: 'Users',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
name: 'posts',
|
||||
title: 'Posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Table
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const FormBlockInitializerItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<FormOutlined />}
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-uid': uid(),
|
||||
'x-component': 'Form',
|
||||
'x-decorator': 'CardItem',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'Grid.AddFormItem',
|
||||
'x-uid': uid(),
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'select a data source',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
name: 'users',
|
||||
title: 'Users',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
name: 'posts',
|
||||
title: 'Posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Form
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const TestInitializerItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
@ -193,7 +114,7 @@ const TestInitializerItem = itemWrap((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
export const AddGridBlockItem = observer((props: any) => {
|
||||
export const BlockInitializer = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
||||
@ -213,11 +134,6 @@ export const AddGridBlockItem = observer((props: any) => {
|
||||
title: 'Form',
|
||||
component: FormBlockInitializerItem,
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Test',
|
||||
component: TestInitializerItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
@ -0,0 +1,84 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
const useCurrentActionSchema = (action: string) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { remove } = useDesignable();
|
||||
const schema = fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-action'] === action) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return {
|
||||
schema,
|
||||
exists: !!schema,
|
||||
remove() {
|
||||
schema && remove(schema);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const DetailsActionInitializer = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
style={{ marginLeft: 8 }}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Create',
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: 'Create',
|
||||
'x-action': 'posts:create',
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Update',
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: 'Update',
|
||||
'x-action': 'posts:update',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure actions
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -2,8 +2,8 @@ import { ISchema, observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer, SchemaInitializerItemOptions } from '../../../schema-initializer';
|
||||
import { useDesignable } from '../../hooks';
|
||||
import { SchemaInitializer, SchemaInitializerItemOptions } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
const useFormItemInitializerFields = () => {
|
||||
return [
|
||||
@ -122,7 +122,7 @@ const InitializeTextFormItem = itemWrap((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
export const AddGridFormItem = observer((props: any) => {
|
||||
export const FormItemInitializer = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
@ -0,0 +1,142 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import { ISchema, observer } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../..';
|
||||
|
||||
const gridRowColWrap = (schema: ISchema) => {
|
||||
return {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[schema.name || uid()]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
const TestInitializerItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<FormOutlined />}
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
name: uid(),
|
||||
'x-decorator': 'CardItem',
|
||||
'x-component': 'Grid',
|
||||
'x-uid': uid(),
|
||||
properties: {
|
||||
row1: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-uid': uid(),
|
||||
properties: {
|
||||
col11: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
block1: {
|
||||
type: 'void',
|
||||
title: '1',
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Block',
|
||||
},
|
||||
block2: {
|
||||
type: 'void',
|
||||
title: '2',
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Block',
|
||||
},
|
||||
},
|
||||
},
|
||||
col12: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
block3: {
|
||||
type: 'void',
|
||||
title: '3',
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Block',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
row2: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
'x-uid': uid(),
|
||||
properties: {
|
||||
col21: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
block4: {
|
||||
type: 'void',
|
||||
title: '4',
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Block',
|
||||
},
|
||||
},
|
||||
},
|
||||
col22: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
block5: {
|
||||
type: 'void',
|
||||
title: '5',
|
||||
'x-decorator': 'BlockItem',
|
||||
'x-component': 'Block',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Test
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const RecordBlockInitializer = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Data blocks',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Form',
|
||||
component: TestInitializerItem,
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Details',
|
||||
component: TestInitializerItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Add block
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,96 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
const useCurrentActionSchema = (action: string) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { remove } = useDesignable();
|
||||
const schema = fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-action'] === action) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
return {
|
||||
schema,
|
||||
exists: !!schema,
|
||||
remove() {
|
||||
schema && remove(schema);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title}
|
||||
<Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const TableActionInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
style={{ marginLeft: 8 }}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Enable actions'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Filter'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Filter") }}',
|
||||
'x-action': 'filter',
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Add new'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Add new") }}',
|
||||
'x-action': 'create',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Delete'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-action': 'destroy',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure actions')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,145 @@
|
||||
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useAPIClient } from '../../../api-client';
|
||||
import { createDesignable, useDesignable } from '../../../schema-component';
|
||||
|
||||
const useCurrentActionSchema = (action: string) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { remove } = useDesignable();
|
||||
const findActionSchema = (schema: Schema) => {
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s['x-action'] === action) {
|
||||
return s;
|
||||
}
|
||||
const c = findActionSchema(s);
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
};
|
||||
const schema = findActionSchema(fieldSchema);
|
||||
return {
|
||||
schema,
|
||||
exists: !!schema,
|
||||
remove() {
|
||||
schema && remove(schema);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
console.log('InitializeAction', insert);
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'Action.Link',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const TableColumnActionInitializer = observer((props: any) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const api = useAPIClient();
|
||||
const { refresh } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
insert={(schema) => {
|
||||
const spaceSchema = fieldSchema.reduceProperties((buf, schema) => {
|
||||
if (schema['x-component'] === 'Space') {
|
||||
return schema;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
if (!spaceSchema) {
|
||||
return;
|
||||
}
|
||||
const dn = createDesignable({
|
||||
api,
|
||||
refresh,
|
||||
current: spaceSchema,
|
||||
});
|
||||
dn.loadAPIClientEvents();
|
||||
dn.insertBeforeEnd(schema);
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Enable actions'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('View'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("View") }}',
|
||||
type: 'void',
|
||||
'x-action': 'view',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
title: '{{ t("View record") }}',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Edit'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Edit") }}',
|
||||
type: 'void',
|
||||
'x-action': 'update',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
title: '{{ t("Edit record") }}',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Delete'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-action': 'destroy',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,99 @@
|
||||
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer, SchemaInitializerItemOptions } from '../..';
|
||||
import { useCollection, useDesignable } from '../../..';
|
||||
|
||||
const useTableColumnInitializerFields = () => {
|
||||
const { name, fields } = useCollection();
|
||||
return fields
|
||||
.filter((field) => field?.uiSchema?.title)
|
||||
.map((field) => {
|
||||
return {
|
||||
type: 'item',
|
||||
title: field?.uiSchema?.title,
|
||||
schema: {
|
||||
name: field.name,
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
'x-component': 'CollectionField',
|
||||
},
|
||||
component: ColumnInitializerItem,
|
||||
} as SchemaInitializerItemOptions;
|
||||
});
|
||||
};
|
||||
|
||||
const useCurrentColumnSchema = (path: string) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { remove } = useDesignable();
|
||||
const findFieldSchema = (schema: Schema) => {
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s['x-collection-field'] === path) {
|
||||
return s;
|
||||
}
|
||||
const child = findFieldSchema(s);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
};
|
||||
const schema = findFieldSchema(fieldSchema);
|
||||
return {
|
||||
schema,
|
||||
exists: !!schema,
|
||||
remove() {
|
||||
schema && remove(schema.parent);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ColumnInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentColumnSchema(item.schema['x-collection-field']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-decorator': 'TableColumnDecorator',
|
||||
'x-component': 'VoidTable.Column',
|
||||
properties: {
|
||||
[item.schema.name]: {
|
||||
'x-read-pretty': true,
|
||||
...item.schema,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* 表格列配置器
|
||||
*/
|
||||
export const TableColumnInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Display fields'),
|
||||
children: useTableColumnInitializerFields(),
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure columns')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
export * from './BlockInitializer';
|
||||
export * from './DetailsActionInitializer';
|
||||
export * from './FormItemInitializer';
|
||||
export * from './RecordBlockInitializer';
|
||||
export * from './TableActionInitializer';
|
||||
export * from './TableColumnActionInitializer';
|
||||
export * from './TableColumnInitializer';
|
||||
|
@ -16,10 +16,16 @@ export const SchemaInitializerItemContext = createContext(null);
|
||||
export const SchemaInitializer = () => null;
|
||||
|
||||
SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
||||
const { wrap = defaultWrap, items = [], insertPosition, dropdown, ...others } = props;
|
||||
const { insertAdjacent, findComponent } = useDesignable();
|
||||
const { insert, wrap = defaultWrap, items = [], insertPosition, dropdown, ...others } = props;
|
||||
let { insertAdjacent, findComponent } = useDesignable();
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const insertSchema = (schema) => {
|
||||
if (props.insert) {
|
||||
props.insert(wrap(schema));
|
||||
} else {
|
||||
insertAdjacent(insertPosition, wrap(schema));
|
||||
}
|
||||
};
|
||||
const menu = (
|
||||
<Menu>
|
||||
{items?.map((item, indexA) => {
|
||||
@ -36,18 +42,10 @@ SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
||||
index: indexA,
|
||||
item,
|
||||
info: item,
|
||||
insert: (schema) => {
|
||||
insertAdjacent(insertPosition, wrap(schema));
|
||||
},
|
||||
insert: insertSchema,
|
||||
}}
|
||||
>
|
||||
<Component
|
||||
{...item}
|
||||
item={item}
|
||||
insert={(schema) => {
|
||||
insertAdjacent(insertPosition, wrap(schema));
|
||||
}}
|
||||
/>
|
||||
<Component {...item} item={item} insert={insertSchema} />
|
||||
</SchemaInitializerItemContext.Provider>
|
||||
)
|
||||
);
|
||||
@ -68,18 +66,10 @@ SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
||||
index: indexB,
|
||||
item: child,
|
||||
info: child,
|
||||
insert: (schema) => {
|
||||
insertAdjacent(insertPosition, wrap(schema));
|
||||
},
|
||||
insert: insertSchema,
|
||||
}}
|
||||
>
|
||||
<Component
|
||||
{...child}
|
||||
item={child}
|
||||
insert={(schema) => {
|
||||
insertAdjacent(insertPosition, wrap(schema));
|
||||
}}
|
||||
/>
|
||||
<Component {...child} item={child} insert={insertSchema} />
|
||||
</SchemaInitializerItemContext.Provider>
|
||||
)
|
||||
);
|
||||
@ -91,6 +81,8 @@ SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
||||
</Menu>
|
||||
);
|
||||
|
||||
console.log('others', others);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
visible={visible}
|
||||
|
@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import { SchemaComponentOptions } from '../schema-component';
|
||||
import * as initializers from './Initializers';
|
||||
|
||||
export const SchemaInitializerProvider: React.FC = (props) => {
|
||||
return <SchemaComponentOptions components={initializers}>{props.children}</SchemaComponentOptions>;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
export * from './SchemaInitializer';
|
||||
export * from './SchemaInitializerProvider';
|
||||
export * from './types';
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { ISchema } from '@formily/react';
|
||||
import { ButtonProps, DropDownProps, MenuItemProps } from 'antd';
|
||||
|
||||
export interface SchemaInitializerButtonProps extends ButtonProps {
|
||||
insert?: (s: ISchema) => void;
|
||||
wrap?: (s: ISchema) => ISchema;
|
||||
insertPosition?: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
|
||||
items?: SchemaInitializerItemOptions[];
|
||||
|
@ -54,12 +54,12 @@ export class UiRoutesStoragePlugin extends Plugin {
|
||||
grid1: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'Grid.AddBlockItem',
|
||||
'x-item-initializer': 'BlockInitializer',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
item8: {
|
||||
type: 'void',
|
||||
|
Loading…
Reference in New Issue
Block a user