refactor(DataBlock): form block (#3771)

* refactor: extract to createCreateFormBBlockUISchema

* refactor: extract to createEditFormBlockUISchema

* refactor: use new function to create UISchema

* refactor: add x-use-decorator-props

* fix: add withDynamicSchemaProps to FormBlockProvider

* chore: update import path for withDynamicSchemaProps

* refactor: should not get collection on getting association in UISchema

* refactor: use x-use-component-props instead of useProps

* fix: fix withDynamicSchemaProps

* chore: fix unit test
This commit is contained in:
Zeke Zhang 2024-03-27 17:32:26 +08:00 committed by GitHub
parent cfa6930d70
commit c6922b071d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 536 additions and 133 deletions

View File

@ -1,9 +1,14 @@
import React from 'react'; import React from 'react';
import { Plugin } from '../application/Plugin'; import { Plugin } from '../application/Plugin';
import { ActionSchemaToolbar } from '../modules/actions/ActionSchemaToolbar'; import { ActionSchemaToolbar } from '../modules/actions/ActionSchemaToolbar';
import { CollapseItemSchemaToolbar } from '../modules/blocks/filter-blocks/collapse/CollapseItemSchemaToolbar'; import { BlockSchemaToolbar } from '../modules/blocks/BlockSchemaToolbar';
import { FormItemSchemaToolbar } from '../modules/blocks/data-blocks/form/FormItemSchemaToolbar'; import { FormItemSchemaToolbar } from '../modules/blocks/data-blocks/form/FormItemSchemaToolbar';
import { useCreateFormBlockDecoratorProps } from '../modules/blocks/data-blocks/form/hooks/useCreateFormBlockDecoratorProps';
import { useCreateFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useCreateFormBlockProps';
import { useEditFormBlockDecoratorProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps';
import { useEditFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockProps';
import { TableColumnSchemaToolbar } from '../modules/blocks/data-blocks/table/TableColumnSchemaToolbar'; import { TableColumnSchemaToolbar } from '../modules/blocks/data-blocks/table/TableColumnSchemaToolbar';
import { CollapseItemSchemaToolbar } from '../modules/blocks/filter-blocks/collapse/CollapseItemSchemaToolbar';
import { SchemaComponentOptions } from '../schema-component'; import { SchemaComponentOptions } from '../schema-component';
import { RecordLink, useParamsFromRecord, useSourceIdFromParentRecord, useSourceIdFromRecord } from './BlockProvider'; import { RecordLink, useParamsFromRecord, useSourceIdFromParentRecord, useSourceIdFromRecord } from './BlockProvider';
import { DetailsBlockProvider, useDetailsBlockProps } from './DetailsBlockProvider'; import { DetailsBlockProvider, useDetailsBlockProps } from './DetailsBlockProvider';
@ -15,7 +20,6 @@ import { useTableBlockProps } from '../modules/blocks/data-blocks/table/hooks/us
import { TableFieldProvider, useTableFieldProps } from './TableFieldProvider'; import { TableFieldProvider, useTableFieldProps } from './TableFieldProvider';
import { TableSelectorProvider, useTableSelectorProps } from './TableSelectorProvider'; import { TableSelectorProvider, useTableSelectorProps } from './TableSelectorProvider';
import * as bp from './hooks'; import * as bp from './hooks';
import { BlockSchemaToolbar } from '../modules/blocks/BlockSchemaToolbar';
import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps'; import { useTableBlockDecoratorProps } from '../modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps';
// TODO: delete this, replaced by `BlockSchemaComponentPlugin` // TODO: delete this, replaced by `BlockSchemaComponentPlugin`
@ -38,6 +42,10 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
useSourceIdFromParentRecord, useSourceIdFromParentRecord,
useParamsFromRecord, useParamsFromRecord,
useFormBlockProps, useFormBlockProps,
useCreateFormBlockProps,
useCreateFormBlockDecoratorProps,
useEditFormBlockDecoratorProps,
useEditFormBlockProps,
useFormFieldProps, useFormFieldProps,
useDetailsBlockProps, useDetailsBlockProps,
useTableFieldProps, useTableFieldProps,
@ -82,6 +90,10 @@ export class BlockSchemaComponentPlugin extends Plugin {
useSourceIdFromParentRecord, useSourceIdFromParentRecord,
useParamsFromRecord, useParamsFromRecord,
useFormBlockProps, useFormBlockProps,
useCreateFormBlockProps,
useCreateFormBlockDecoratorProps,
useEditFormBlockDecoratorProps,
useEditFormBlockProps,
useFormFieldProps, useFormFieldProps,
useDetailsBlockProps, useDetailsBlockProps,
useTableFieldProps, useTableFieldProps,

View File

@ -10,6 +10,7 @@ import { Templates as DataTemplateSelect } from '../schema-component/antd/form-v
import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, useBlockRequestContext } from './BlockProvider';
import { TemplateBlockProvider } from './TemplateBlockProvider'; import { TemplateBlockProvider } from './TemplateBlockProvider';
import { FormActiveFieldsProvider } from './hooks/useFormActiveFields'; import { FormActiveFieldsProvider } from './hooks/useFormActiveFields';
import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps';
export const FormBlockContext = createContext<{ export const FormBlockContext = createContext<{
form?: any; form?: any;
@ -29,7 +30,7 @@ FormBlockContext.displayName = 'FormBlockContext';
const InternalFormBlockProvider = (props) => { const InternalFormBlockProvider = (props) => {
const ctx = useFormBlockContext(); const ctx = useFormBlockContext();
const { action, readPretty, params, association, collection } = props; const { action, readPretty, params, collection } = props;
const field = useField(); const field = useField();
const form = useMemo( const form = useMemo(
() => () =>
@ -89,7 +90,7 @@ export const useIsDetailBlock = () => {
return ctx.type !== 'create' && fieldSchema?.['x-acl-action'] !== 'create' && fieldSchema?.['x-action'] !== 'create'; return ctx.type !== 'create' && fieldSchema?.['x-acl-action'] !== 'create' && fieldSchema?.['x-action'] !== 'create';
}; };
export const FormBlockProvider = (props) => { export const FormBlockProvider = withDynamicSchemaProps((props) => {
const record = useRecord(); const record = useRecord();
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
const { collection, isCusomeizeCreate } = props; const { collection, isCusomeizeCreate } = props;
@ -120,7 +121,7 @@ export const FormBlockProvider = (props) => {
</BlockProvider> </BlockProvider>
</TemplateBlockProvider> </TemplateBlockProvider>
); );
}; });
export const useFormBlockContext = () => { export const useFormBlockContext = () => {
return useContext(FormBlockContext); return useContext(FormBlockContext);

View File

@ -4,8 +4,9 @@ import React from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext } from '../../../../block-provider'; import { useBlockAssociationContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
import { useSchemaTemplateManager } from '../../../../schema-templates'; import { useSchemaTemplateManager } from '../../../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; import { createCreateFormBlockUISchema } from './createCreateFormBlockUISchema';
// TODO: `SchemaInitializerItem` items // TODO: `SchemaInitializerItem` items
export const CreateFormBlockInitializer = () => { export const CreateFormBlockInitializer = () => {
@ -23,14 +24,19 @@ export const CreateFormBlockInitializer = () => {
if (item.template) { if (item.template) {
const s = await getTemplateSchemaByMode(item); const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') { if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({ const blockSchema = createCreateFormBlockUISchema(
actionInitializers: 'createForm:configureActions', association
? {
association, association,
dataSource: collection.dataSource, dataSource: collection.dataSource,
collection: collection.name, templateSchema: s,
template: s, }
settings: 'blockSettings:createForm', : {
}); collectionName: collection.name,
dataSource: collection.dataSource,
templateSchema: s,
},
);
if (item.mode === 'reference') { if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key; blockSchema['x-template-key'] = item.template.key;
} }
@ -40,13 +46,17 @@ export const CreateFormBlockInitializer = () => {
} }
} else { } else {
insert( insert(
createFormBlockSchema({ createCreateFormBlockUISchema(
actionInitializers: 'createForm:configureActions', association
? {
association, association,
dataSource: collection.dataSource, dataSource: collection.dataSource,
collection: collection.name, }
settings: 'blockSettings:createForm', : {
}), collectionName: collection.name,
dataSource: collection.dataSource,
},
),
); );
} }
}} }}

View File

@ -1,9 +1,9 @@
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { createFormBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createCreateFormBlockUISchema } from './createCreateFormBlockUISchema';
export const FormBlockInitializer = ({ export const FormBlockInitializer = ({
filterCollections, filterCollections,
@ -44,11 +44,10 @@ export const FormBlockInitializer = ({
} }
insert( insert(
createFormBlockSchema({ createCreateFormBlockUISchema({
collection: item.collectionName || item.name, collectionName: item.collectionName || item.name,
dataSource: item.dataSource, dataSource: item.dataSource,
isCusomeizeCreate, isCusomeizeCreate,
settings: 'blockSettings:createForm',
}), }),
); );
}, },
@ -65,12 +64,11 @@ export const FormBlockInitializer = ({
return templateWrap(templateSchema, { item }); return templateWrap(templateSchema, { item });
} }
const schema = createFormBlockSchema({ const schema = createCreateFormBlockUISchema({
isCusomeizeCreate, isCusomeizeCreate,
dataSource: item.dataSource, dataSource: item.dataSource,
template: templateSchema, templateSchema: templateSchema,
collection: item.name, collectionName: item.name,
settings: 'blockSettings:createForm',
}); });
if (item.template && item.mode === 'reference') { if (item.template && item.mode === 'reference') {
schema['x-template-key'] = item.template.key; schema['x-template-key'] = item.template.key;

View File

@ -3,8 +3,9 @@ import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext } from '../../../../block-provider'; import { useBlockAssociationContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
import { useSchemaTemplateManager } from '../../../../schema-templates'; import { useSchemaTemplateManager } from '../../../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; import { createEditFormBlockUISchema } from './createEditFormBlockUISchema';
/** /**
* @deprecated * @deprecated
@ -43,16 +44,17 @@ export function useCreateEditFormBlock() {
const createEditFormBlock = useCallback( const createEditFormBlock = useCallback(
({ item }) => { ({ item }) => {
insert( insert(
createFormBlockSchema({ createEditFormBlockUISchema(
association
? {
association, association,
collection: item.collectionName || item.name,
dataSource: item.dataSource, dataSource: item.dataSource,
action: 'get', }
useSourceId: '{{ useSourceIdFromParentRecord }}', : {
useParams: '{{ useParamsFromRecord }}', collectionName: item.collectionName || item.name,
actionInitializers: 'editForm:configureActions', dataSource: item.dataSource,
settings: 'blockSettings:editForm', },
}), ),
); );
}, },
[association, insert], [association, insert],
@ -61,17 +63,19 @@ export function useCreateEditFormBlock() {
const templateWrap = useCallback( const templateWrap = useCallback(
(templateSchema, { item }) => { (templateSchema, { item }) => {
if (item.template.componentName === 'FormItem') { if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({ const blockSchema = createEditFormBlockUISchema(
association
? {
association, association,
collection: item.collectionName || item.name,
dataSource: item.dataSource, dataSource: item.dataSource,
action: 'get', templateSchema: templateSchema,
useSourceId: '{{ useSourceIdFromParentRecord }}', }
useParams: '{{ useParamsFromRecord }}', : {
actionInitializers: 'editForm:configureActions', collectionName: item.collectionName || item.name,
template: templateSchema, dataSource: item.dataSource,
settings: 'blockSettings:editForm', templateSchema: templateSchema,
}); },
);
if (item.mode === 'reference') { if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key; blockSchema['x-template-key'] = item.template.key;
} }

View File

@ -0,0 +1,64 @@
import { CreateFormBlockUISchemaOptions, createCreateFormBlockUISchema } from '../createCreateFormBlockUISchema';
vi.mock('@formily/shared', () => {
return {
uid: () => 'mocked-uid',
};
});
describe('createCreateFormBlockUISchema', () => {
it('should create a schema with all options', () => {
const options: CreateFormBlockUISchemaOptions = {
collectionName: 'users',
dataSource: 'userDataSource',
association: 'userAssociation',
templateSchema: { type: 'string' },
isCusomeizeCreate: true,
};
const result = createCreateFormBlockUISchema(options);
expect(result).toMatchInlineSnapshot(`
{
"properties": {
"mocked-uid": {
"properties": {
"grid": {
"type": "string",
},
"mocked-uid": {
"type": "void",
"x-component": "ActionBar",
"x-component-props": {
"layout": "one-column",
"style": {
"marginTop": 24,
},
},
"x-initializer": "createForm:configureActions",
},
},
"type": "void",
"x-component": "FormV2",
"x-use-component-props": "useCreateFormBlockProps",
},
},
"type": "void",
"x-acl-action": "userAssociation:create",
"x-acl-action-props": {
"skipScopeCheck": true,
},
"x-component": "CardItem",
"x-decorator": "FormBlockProvider",
"x-decorator-props": {
"association": "userAssociation",
"collection": "users",
"dataSource": "userDataSource",
"isCusomeizeCreate": true,
},
"x-settings": "blockSettings:createForm",
"x-toolbar": "BlockSchemaToolbar",
"x-use-decorator-props": "useCreateFormBlockDecoratorProps",
}
`);
});
});

View File

@ -0,0 +1,62 @@
import { ISchema } from '@formily/react';
import { vi } from 'vitest';
import { createEditFormBlockUISchema } from '../createEditFormBlockUISchema';
vi.mock('@formily/shared', () => ({
uid: () => 'uniqueId',
}));
describe('createEditFormBlockUISchema', () => {
it('should create a schema with all options', () => {
const schema = createEditFormBlockUISchema({
collectionName: 'users',
dataSource: 'UserDataSource',
association: 'users:update',
templateSchema: { type: 'string' },
});
expect(schema).toMatchInlineSnapshot(`
{
"properties": {
"uniqueId": {
"properties": {
"grid": {
"type": "string",
},
"uniqueId": {
"type": "void",
"x-component": "ActionBar",
"x-component-props": {
"layout": "one-column",
"style": {
"marginTop": 24,
},
},
"x-initializer": "editForm:configureActions",
},
},
"type": "void",
"x-component": "FormV2",
"x-use-component-props": "useEditFormBlockProps",
},
},
"type": "void",
"x-acl-action": "users:update:update",
"x-acl-action-props": {
"skipScopeCheck": false,
},
"x-component": "CardItem",
"x-decorator": "FormBlockProvider",
"x-decorator-props": {
"action": "get",
"association": "users:update",
"collection": "users",
"dataSource": "UserDataSource",
},
"x-settings": "blockSettings:editForm",
"x-toolbar": "BlockSchemaToolbar",
"x-use-decorator-props": "useEditFormBlockDecoratorProps",
}
`);
});
});

View File

@ -0,0 +1,70 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
export interface CreateFormBlockUISchemaOptions {
dataSource: string;
/** 如果传了 association 就不需要再传 collectionName 了 */
collectionName?: string;
association?: string;
templateSchema?: ISchema;
isCusomeizeCreate?: boolean;
}
/**
* UI Schema
* @returns
*/
export function createCreateFormBlockUISchema(options: CreateFormBlockUISchemaOptions): ISchema {
const { collectionName, association, dataSource, templateSchema, isCusomeizeCreate } = options;
const resourceName = association || collectionName;
if (!dataSource) {
throw new Error('dataSource are required');
}
const schema: ISchema = {
type: 'void',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-acl-action': `${resourceName}:create`,
'x-decorator': 'FormBlockProvider',
'x-use-decorator-props': 'useCreateFormBlockDecoratorProps',
'x-decorator-props': {
dataSource,
collection: collectionName,
association,
isCusomeizeCreate,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:createForm',
'x-component': 'CardItem',
properties: {
[uid()]: {
type: 'void',
'x-component': 'FormV2',
'x-use-component-props': 'useCreateFormBlockProps',
properties: {
grid: templateSchema || {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'form:configureFields',
properties: {},
},
[uid()]: {
type: 'void',
'x-initializer': 'createForm:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
},
},
},
},
};
return schema;
}

View File

@ -0,0 +1,64 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
interface EditFormBlockOptions {
dataSource: string;
/** 如果传了 association 就不需要再传 collectionName 了 */
collectionName?: string;
association?: string;
templateSchema?: ISchema;
}
export function createEditFormBlockUISchema(options: EditFormBlockOptions): ISchema {
const { collectionName, dataSource, association, templateSchema } = options;
const resourceName = association || collectionName;
if (!dataSource) {
throw new Error('dataSource are required');
}
return {
type: 'void',
'x-acl-action-props': {
skipScopeCheck: false,
},
'x-acl-action': `${resourceName}:update`,
'x-decorator': 'FormBlockProvider',
'x-use-decorator-props': 'useEditFormBlockDecoratorProps',
'x-decorator-props': {
action: 'get',
dataSource,
collection: collectionName,
association,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:editForm',
'x-component': 'CardItem',
properties: {
[uid()]: {
type: 'void',
'x-component': 'FormV2',
'x-use-component-props': 'useEditFormBlockProps',
properties: {
grid: templateSchema || {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'form:configureFields',
properties: {},
},
[uid()]: {
type: 'void',
'x-initializer': 'editForm:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
},
},
},
},
};
}

View File

@ -0,0 +1,15 @@
import { useFormBlockSourceId } from './useFormBlockSourceId';
export function useCreateFormBlockDecoratorProps(props) {
let sourceId;
// association 的值是固定不变的,所以这里可以使用 hooks
if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useFormBlockSourceId(props);
}
return {
sourceId,
};
}

View File

@ -0,0 +1,5 @@
import { useFormBlockProps } from '../../../../../block-provider/FormBlockProvider';
export function useCreateFormBlockProps() {
return useFormBlockProps();
}

View File

@ -0,0 +1,22 @@
import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider';
import { useFormBlockSourceId } from './useFormBlockSourceId';
export function useEditFormBlockDecoratorProps(props) {
const params = useFormBlockParams();
let sourceId;
// association 的值是固定不变的,所以这里可以使用 hooks
if (props.association) {
// eslint-disable-next-line react-hooks/rules-of-hooks
sourceId = useFormBlockSourceId(props);
}
return {
params,
sourceId,
};
}
function useFormBlockParams() {
return useParamsFromRecord();
}

View File

@ -0,0 +1,5 @@
import { useFormBlockProps } from '../../../../../block-provider/FormBlockProvider';
export function useEditFormBlockProps() {
return useFormBlockProps();
}

View File

@ -0,0 +1,5 @@
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
export function useFormBlockSourceId(props) {
return useDataBlockSourceId(props);
}

View File

@ -1,11 +1,12 @@
export * from './createFormBlockSettings';
export * from './CreateFormBlockInitializer'; export * from './CreateFormBlockInitializer';
export * from './FormBlockInitializer'; export * from './FormBlockInitializer';
export * from './FormItemSchemaToolbar'; export * from './FormItemSchemaToolbar';
export * from './RecordFormBlockInitializer'; export * from './RecordFormBlockInitializer';
export * from './editFormBlockSettings'; export * from './createCreateFormBlockUISchema';
export * from './createFormActionInitializers'; export * from './createFormActionInitializers';
export * from './createFormBlockSettings';
export * from './editFormBlockSettings';
export * from './fieldSettingsFormItem';
export * from './formActionInitializers'; export * from './formActionInitializers';
export * from './formItemInitializers'; export * from './formItemInitializers';
export * from './fieldSettingsFormItem';
export * from './updateFormActionInitializers'; export * from './updateFormActionInitializers';

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { FormV2 } from '../form-v2'; import { FormV2 } from '../form-v2';
import { useDetailsBlockContext } from '../../../block-provider/DetailsBlockProvider';
import _ from 'lodash'; import _ from 'lodash';
import { Empty } from 'antd'; import { Empty } from 'antd';
import { useDataBlockRequest } from '../../../data-source'; import { useDataBlockRequest } from '../../../data-source';

View File

@ -18,6 +18,7 @@ import { isVariable, REGEX_OF_VARIABLE } from '../../../variables/utils/isVariab
import { getInnermostKeyAndValue, getTargetField } from '../../common/utils/uitls'; import { getInnermostKeyAndValue, getTargetField } from '../../common/utils/uitls';
import { useProps } from '../../hooks/useProps'; import { useProps } from '../../hooks/useProps';
import { collectFieldStateOfLinkageRules, getTempFieldState } from './utils'; import { collectFieldStateOfLinkageRules, getTempFieldState } from './utils';
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
export interface FormProps { export interface FormProps {
[key: string]: any; [key: string]: any;
@ -197,10 +198,13 @@ export const Form: React.FC<FormProps> & {
FilterDesigner?: any; FilterDesigner?: any;
ReadPrettyDesigner?: any; ReadPrettyDesigner?: any;
Templates?: any; Templates?: any;
} = observer( } = withDynamicSchemaProps(
(props) => { observer((props) => {
const field = useField<Field>(); const field = useField<Field>();
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { form, disabled, ...others } = useProps(props); const { form, disabled, ...others } = useProps(props);
const formDisabled = disabled || field.disabled; const formDisabled = disabled || field.disabled;
return ( return (
<ConfigProvider componentDisabled={formDisabled}> <ConfigProvider componentDisabled={formDisabled}>
@ -222,7 +226,7 @@ export const Form: React.FC<FormProps> & {
</form> </form>
</ConfigProvider> </ConfigProvider>
); );
}, }),
{ displayName: 'Form' }, { displayName: 'Form' },
); );

View File

@ -1,10 +1,11 @@
import React, { useCallback, useMemo } from 'react';
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import React, { useCallback, useMemo } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useSchemaTemplateManager } from '../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
import { createCreateFormBlockUISchema } from '../../modules/blocks/data-blocks/form/createCreateFormBlockUISchema';
import { useSchemaTemplateManager } from '../../schema-templates';
import { useRecordCollectionDataSourceItems } from '../utils';
/** /**
* @deprecated * @deprecated
@ -18,31 +19,18 @@ export const RecordAssociationFormBlockInitializer = () => {
const field = itemConfig.field; const field = itemConfig.field;
const collectionName = field.target; const collectionName = field.target;
const collection = useMemo(() => getCollection(collectionName), [collectionName]); const collection = useMemo(() => getCollection(collectionName), [collectionName]);
const resource = `${field.collectionName}.${field.name}`;
return ( return (
<SchemaInitializerItem <SchemaInitializerItem
icon={<FormOutlined />} icon={<FormOutlined />}
{...others} {...others}
onClick={async ({ item }) => { onClick={async ({ item }) => {
const action = ['hasOne', 'belongsTo'].includes(field.type) ? 'get' : null;
const actionInitializers = ['hasOne', 'belongsTo'].includes(field.type)
? 'editForm:configureActions'
: 'createForm:configureActions';
if (item.template) { if (item.template) {
const template = await getTemplateSchemaByMode(item); const template = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') { if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({ const blockSchema = createCreateFormBlockUISchema({
collection: collectionName,
dataSource: collection.dataSource, dataSource: collection.dataSource,
resource, association: `${field.collectionName}.${field.name}`,
association: resource, templateSchema: template,
action,
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
template: template,
settings: 'blockSettings:createForm',
}); });
if (item.mode === 'reference') { if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key; blockSchema['x-template-key'] = item.template.key;
@ -53,21 +41,19 @@ export const RecordAssociationFormBlockInitializer = () => {
} }
} else { } else {
insert( insert(
createFormBlockSchema({ createCreateFormBlockUISchema({
collection: collectionName,
resource,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: resource, association: `${field.collectionName}.${field.name}`,
action,
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
settings: 'blockSettings:createForm',
}), }),
); );
} }
}} }}
items={useRecordCollectionDataSourceItems('FormItem', itemConfig, collection, resource)} items={useRecordCollectionDataSourceItems(
'FormItem',
itemConfig,
collection,
`${field.collectionName}.${field.name}`,
)}
/> />
); );
}; };
@ -80,21 +66,11 @@ export function useCreateAssociationFormBlock() {
({ item }) => { ({ item }) => {
const field = item.associationField; const field = item.associationField;
const collection = getCollection(field.target); const collection = getCollection(field.target);
const action = ['hasOne', 'belongsTo'].includes(field.type) ? 'get' : null;
const actionInitializers = ['hasOne', 'belongsTo'].includes(field.type)
? 'editForm:configureActions'
: 'createForm:configureActions';
insert( insert(
createFormBlockSchema({ createCreateFormBlockUISchema({
collection: field.target,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`, association: `${field.collectionName}.${field.name}`,
action,
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
settings: 'blockSettings:createForm',
}), }),
); );
}, },
@ -104,23 +80,13 @@ export function useCreateAssociationFormBlock() {
const templateWrap = useCallback( const templateWrap = useCallback(
(templateSchema, { item }) => { (templateSchema, { item }) => {
const field = item.associationField; const field = item.associationField;
const action = ['hasOne', 'belongsTo'].includes(field.type) ? 'get' : null;
const collection = getCollection(field.target); const collection = getCollection(field.target);
const actionInitializers = ['hasOne', 'belongsTo'].includes(field.type)
? 'editForm:configureActions'
: 'createForm:configureActions';
if (item.template.componentName === 'FormItem') { if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({ const blockSchema = createCreateFormBlockUISchema({
collection: field.target,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`, association: `${field.collectionName}.${field.name}`,
action, templateSchema: templateSchema,
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
template: templateSchema,
settings: 'blockSettings:createForm',
}); });
if (item.mode === 'reference') { if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key; blockSchema['x-template-key'] = item.template.key;

View File

@ -1,6 +1,7 @@
import { Field, Form } from '@formily/core'; import { Field, Form } from '@formily/core';
import { ISchema, Schema, useFieldSchema, useForm } from '@formily/react'; import { ISchema, Schema, useFieldSchema, useForm } from '@formily/react';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import _ from 'lodash';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
@ -12,13 +13,12 @@ import {
useFormActiveFields, useFormActiveFields,
useFormBlockContext, useFormBlockContext,
} from '../'; } from '../';
import { FieldOptions, useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager'; import { FieldOptions, useCollectionManager_deprecated, useCollection_deprecated } from '../collection-manager';
import { Collection, CollectionFieldOptions } from '../data-source/collection/Collection';
import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
import { isAssocField } from '../filter-provider/utils'; import { isAssocField } from '../filter-provider/utils';
import { useActionContext, useCompile, useDesignable } from '../schema-component'; import { useActionContext, useCompile, useDesignable } from '../schema-component';
import { useSchemaTemplateManager } from '../schema-templates'; import { useSchemaTemplateManager } from '../schema-templates';
import { Collection, CollectionFieldOptions } from '../data-source/collection/Collection';
import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
import _ from 'lodash';
export const itemsMerge = (items1) => { export const itemsMerge = (items1) => {
return items1; return items1;
@ -1150,7 +1150,28 @@ export const createGridCardBlockSchema = (options) => {
}; };
return schema; return schema;
}; };
export const createFormBlockSchema = (options) => {
/**
* @deprecated
* 使 createCreateFormBlockUISchema createEditFormBlockUISchema
* @param options
* @returns
*/
export const createFormBlockSchema = (options: {
formItemInitializers?: string;
actionInitializers?: string;
collection: string;
resource?: string;
dataSource?: string;
association?: string;
action?: string;
actions?: Record<string, any>;
template?: any;
title?: string;
settings?: any;
'x-designer'?: string;
[key: string]: any;
}) => {
const { const {
formItemInitializers = 'form:configureFields', formItemInitializers = 'form:configureFields',
actionInitializers = 'createForm:configureActions', actionInitializers = 'createForm:configureActions',

View File

@ -1,15 +1,16 @@
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import { import {
SchemaInitializerItem,
createCreateFormBlockUISchema,
useBlockAssociationContext, useBlockAssociationContext,
useCollection_deprecated, useCollection_deprecated,
useSchemaTemplateManager,
createFormBlockSchema,
useRecordCollectionDataSourceItems, useRecordCollectionDataSourceItems,
SchemaInitializerItem,
useSchemaInitializer, useSchemaInitializer,
useSchemaInitializerItem, useSchemaInitializerItem,
useSchemaTemplateManager,
} from '@nocobase/client'; } from '@nocobase/client';
import React from 'react';
import { createBulkEditBlockUISchema } from './createBulkEditBlockUISchema';
export const CreateFormBulkEditBlockInitializer = () => { export const CreateFormBulkEditBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
@ -26,13 +27,19 @@ export const CreateFormBulkEditBlockInitializer = () => {
if (item.template) { if (item.template) {
const s = await getTemplateSchemaByMode(item); const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') { if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({ const blockSchema = createCreateFormBlockUISchema(
actionInitializers: 'createForm:configureActions', association
? {
association, association,
collection: collection.name,
dataSource: collection.dataSource, dataSource: collection.dataSource,
template: s, templateSchema: s,
}); }
: {
collectionName: collection.name,
dataSource: collection.dataSource,
templateSchema: s,
},
);
if (item.mode === 'reference') { if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key; blockSchema['x-template-key'] = item.template.key;
} }
@ -42,11 +49,9 @@ export const CreateFormBulkEditBlockInitializer = () => {
} }
} else { } else {
insert( insert(
createFormBlockSchema({ createBulkEditBlockUISchema({
formItemInitializers: 'bulkEditForm:configureFields',
actionInitializers: 'bulkEditForm:configureActions',
association, association,
collection: collection.name, collectionName: collection.name,
dataSource: collection.dataSource, dataSource: collection.dataSource,
}), }),
); );

View File

@ -0,0 +1,65 @@
import { ISchema } from '@formily/react';
import { uid } from '@formily/shared';
/**
* UI Schema
* @returns
*/
export function createBulkEditBlockUISchema(options: {
collectionName: string;
dataSource: string;
association?: string;
}): ISchema {
const { collectionName, association, dataSource } = options;
const resourceName = association || collectionName;
if (!collectionName || !dataSource) {
throw new Error('collectionName and dataSource are required');
}
const schema: ISchema = {
type: 'void',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-acl-action': `${resourceName}:create`,
'x-decorator': 'FormBlockProvider',
'x-decorator-props': {
dataSource,
collection: collectionName,
association,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:createForm',
'x-component': 'CardItem',
properties: {
[uid()]: {
type: 'void',
'x-component': 'FormV2',
'x-component-props': {
useProps: '{{ useCreateFormBlockProps }}',
},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'bulkEditForm:configureFields',
properties: {},
},
[uid()]: {
type: 'void',
'x-initializer': 'bulkEditForm:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
},
},
},
},
};
return schema;
}

View File

@ -4,7 +4,6 @@ import {
CollectionProvider_deprecated, CollectionProvider_deprecated,
SchemaInitializerItem, SchemaInitializerItem,
SchemaInitializerItemType, SchemaInitializerItemType,
createFormBlockSchema,
useRecordCollectionDataSourceItems, useRecordCollectionDataSourceItems,
useSchemaInitializer, useSchemaInitializer,
useSchemaInitializerItem, useSchemaInitializerItem,
@ -14,6 +13,7 @@ import {
import { JOB_STATUS, traverseSchema } from '@nocobase/plugin-workflow/client'; import { JOB_STATUS, traverseSchema } from '@nocobase/plugin-workflow/client';
import { NAMESPACE } from '../../locale'; import { NAMESPACE } from '../../locale';
import { createManualFormBlockUISchema } from './createManualFormBlockUISchema';
function InternalFormBlockInitializer({ schema, ...others }) { function InternalFormBlockInitializer({ schema, ...others }) {
const { getTemplateSchemaByMode } = useSchemaTemplateManager(); const { getTemplateSchemaByMode } = useSchemaTemplateManager();
@ -21,7 +21,7 @@ function InternalFormBlockInitializer({ schema, ...others }) {
const items = useRecordCollectionDataSourceItems('FormItem') as SchemaInitializerItemType[]; const items = useRecordCollectionDataSourceItems('FormItem') as SchemaInitializerItemType[];
async function onConfirm({ item }) { async function onConfirm({ item }) {
const template = item.template ? await getTemplateSchemaByMode(item) : null; const template = item.template ? await getTemplateSchemaByMode(item) : null;
const result = createFormBlockSchema({ const result = createManualFormBlockUISchema({
actionInitializers: 'workflowManual:form:configureActions', actionInitializers: 'workflowManual:form:configureActions',
actions: { actions: {
resolve: { resolve: {

View File

@ -0,0 +1,5 @@
import { createFormBlockSchema } from '@nocobase/client';
export function createManualFormBlockUISchema(options) {
return createFormBlockSchema(options);
}