Merge remote-tracking branch 'upstream/main' into fix_new_version
This commit is contained in:
commit
86ffb8f4a9
@ -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,8 +20,10 @@ 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';
|
||||||
|
import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps';
|
||||||
|
import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps';
|
||||||
|
import { useCollapseBlockDecoratorProps } from '../modules/blocks/filter-blocks/collapse/hooks/useCollapseBlockDecoratorProps';
|
||||||
|
|
||||||
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
||||||
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
||||||
@ -38,12 +45,19 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
|||||||
useSourceIdFromParentRecord,
|
useSourceIdFromParentRecord,
|
||||||
useParamsFromRecord,
|
useParamsFromRecord,
|
||||||
useFormBlockProps,
|
useFormBlockProps,
|
||||||
|
useCreateFormBlockProps,
|
||||||
|
useCreateFormBlockDecoratorProps,
|
||||||
|
useEditFormBlockDecoratorProps,
|
||||||
|
useEditFormBlockProps,
|
||||||
useFormFieldProps,
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
useTableSelectorProps,
|
useTableSelectorProps,
|
||||||
useTableBlockDecoratorProps,
|
useTableBlockDecoratorProps,
|
||||||
|
useListBlockDecoratorProps,
|
||||||
|
useTableSelectorDecoratorProps,
|
||||||
|
useCollapseBlockDecoratorProps,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
@ -82,12 +96,19 @@ export class BlockSchemaComponentPlugin extends Plugin {
|
|||||||
useSourceIdFromParentRecord,
|
useSourceIdFromParentRecord,
|
||||||
useParamsFromRecord,
|
useParamsFromRecord,
|
||||||
useFormBlockProps,
|
useFormBlockProps,
|
||||||
|
useCreateFormBlockProps,
|
||||||
|
useCreateFormBlockDecoratorProps,
|
||||||
|
useEditFormBlockDecoratorProps,
|
||||||
|
useEditFormBlockProps,
|
||||||
useFormFieldProps,
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
useTableSelectorProps,
|
useTableSelectorProps,
|
||||||
useTableBlockDecoratorProps,
|
useTableBlockDecoratorProps,
|
||||||
|
useListBlockDecoratorProps,
|
||||||
|
useTableSelectorDecoratorProps,
|
||||||
|
useCollapseBlockDecoratorProps,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -11,6 +11,7 @@ import { RecordProvider, useRecord } from '../record-provider';
|
|||||||
import { SchemaComponentOptions } from '../schema-component';
|
import { SchemaComponentOptions } from '../schema-component';
|
||||||
import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider';
|
||||||
import { useParsedFilter } from './hooks';
|
import { useParsedFilter } from './hooks';
|
||||||
|
import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
filter?: any;
|
filter?: any;
|
||||||
@ -141,7 +142,7 @@ const useAssociationNames = (collection) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TableSelectorProvider = (props: TableSelectorProviderProps) => {
|
export const TableSelectorProvider = withDynamicSchemaProps((props: TableSelectorProviderProps) => {
|
||||||
const parentParams = useTableSelectorParams();
|
const parentParams = useTableSelectorParams();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { getCollectionJoinField, getCollectionFields } = useCollectionManager_deprecated();
|
const { getCollectionJoinField, getCollectionFields } = useCollectionManager_deprecated();
|
||||||
@ -263,7 +264,7 @@ export const TableSelectorProvider = (props: TableSelectorProviderProps) => {
|
|||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
</SchemaComponentOptions>
|
</SchemaComponentOptions>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export const useTableSelectorContext = () => {
|
export const useTableSelectorContext = () => {
|
||||||
return useContext(TableSelectorContext);
|
return useContext(TableSelectorContext);
|
||||||
|
@ -36,6 +36,7 @@ import { TableFieldResource } from '../TableFieldProvider';
|
|||||||
|
|
||||||
export * from './useFormActiveFields';
|
export * from './useFormActiveFields';
|
||||||
export * from './useParsedFilter';
|
export * from './useParsedFilter';
|
||||||
|
export * from './useDataBlockSourceId';
|
||||||
|
|
||||||
export const usePickActionProps = () => {
|
export const usePickActionProps = () => {
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
} from '../..';
|
} from '../..';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @internal
|
||||||
* 注意:这里有一个需要更改 schema 才能解决的问题,就是在获取 sourceId 的时候无法确定(在关系字段和当前表同表时)
|
* 注意:这里有一个需要更改 schema 才能解决的问题,就是在获取 sourceId 的时候无法确定(在关系字段和当前表同表时)
|
||||||
* 是需要从 recordData 还是 parentRecordData 中获取;解决方法是通过更改 schema,在不同类型的关系区块中
|
* 是需要从 recordData 还是 parentRecordData 中获取;解决方法是通过更改 schema,在不同类型的关系区块中
|
||||||
* (`通过点击关系字段按钮打开的弹窗中创建的非关系字段区块`和`关系字段区块`)使用不同的 hook。
|
* (`通过点击关系字段按钮打开的弹窗中创建的非关系字段区块`和`关系字段区块`)使用不同的 hook。
|
||||||
|
@ -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,
|
? {
|
||||||
dataSource: collection.dataSource,
|
association,
|
||||||
collection: collection.name,
|
dataSource: collection.dataSource,
|
||||||
template: s,
|
templateSchema: 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,
|
? {
|
||||||
dataSource: collection.dataSource,
|
association,
|
||||||
collection: collection.name,
|
dataSource: collection.dataSource,
|
||||||
settings: 'blockSettings:createForm',
|
}
|
||||||
}),
|
: {
|
||||||
|
collectionName: collection.name,
|
||||||
|
dataSource: collection.dataSource,
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
collection: item.collectionName || item.name,
|
? {
|
||||||
dataSource: item.dataSource,
|
association,
|
||||||
action: 'get',
|
dataSource: item.dataSource,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
}
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
: {
|
||||||
actionInitializers: 'editForm:configureActions',
|
collectionName: item.collectionName || item.name,
|
||||||
settings: 'blockSettings:editForm',
|
dataSource: item.dataSource,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[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
|
||||||
collection: item.collectionName || item.name,
|
? {
|
||||||
dataSource: item.dataSource,
|
association,
|
||||||
action: 'get',
|
dataSource: item.dataSource,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
templateSchema: templateSchema,
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
}
|
||||||
actionInitializers: 'editForm:configureActions',
|
: {
|
||||||
template: templateSchema,
|
collectionName: item.collectionName || item.name,
|
||||||
settings: 'blockSettings:editForm',
|
dataSource: item.dataSource,
|
||||||
});
|
templateSchema: templateSchema,
|
||||||
|
},
|
||||||
|
);
|
||||||
if (item.mode === 'reference') {
|
if (item.mode === 'reference') {
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -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",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -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;
|
||||||
|
}
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useFormBlockProps } from '../../../../../block-provider/FormBlockProvider';
|
||||||
|
|
||||||
|
export function useCreateFormBlockProps() {
|
||||||
|
return useFormBlockProps();
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useFormBlockProps } from '../../../../../block-provider/FormBlockProvider';
|
||||||
|
|
||||||
|
export function useEditFormBlockProps() {
|
||||||
|
return useFormBlockProps();
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
|
||||||
|
export function useFormBlockSourceId(props) {
|
||||||
|
return useDataBlockSourceId(props);
|
||||||
|
}
|
@ -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';
|
||||||
|
@ -2,7 +2,7 @@ import { OrderedListOutlined } from '@ant-design/icons';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||||
import { createListBlockSchema } from '../../../../schema-initializer/utils';
|
import { createListBlockSchema } from './createListBlockSchema';
|
||||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||||
|
|
||||||
@ -48,10 +48,9 @@ export const ListBlockInitializer = ({
|
|||||||
|
|
||||||
const collection = getCollection(item.name, item.dataSource);
|
const collection = getCollection(item.name, item.dataSource);
|
||||||
const schema = createListBlockSchema({
|
const schema = createListBlockSchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
settings: 'blockSettings:list',
|
|
||||||
});
|
});
|
||||||
insert(schema);
|
insert(schema);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
import { createListBlockSchema } from '../createListBlockSchema';
|
||||||
|
|
||||||
|
describe('createListBlockSchema', () => {
|
||||||
|
test('should return the correct schema', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'users',
|
||||||
|
dataSource: 'users',
|
||||||
|
association: 'user',
|
||||||
|
templateSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
age: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowKey: 'id',
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = createListBlockSchema(options);
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "list:configureActions",
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"properties": {
|
||||||
|
"item": {
|
||||||
|
"properties": {
|
||||||
|
"actionBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-align": "left",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"layout": "one-column",
|
||||||
|
},
|
||||||
|
"x-initializer": "list:configureItemActions",
|
||||||
|
"x-use-component-props": "useListActionBarProps",
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"properties": {
|
||||||
|
"age": {
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"x-component": "List.Item",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useListItemProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "List",
|
||||||
|
"x-use-component-props": "useListBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "user:view",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "List.Decorator",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"association": "user",
|
||||||
|
"collection": "users",
|
||||||
|
"dataSource": "users",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 10,
|
||||||
|
},
|
||||||
|
"readPretty": true,
|
||||||
|
"rowKey": "id",
|
||||||
|
"runWhenParamsChanged": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:list",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useListBlockDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,76 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
|
||||||
|
export const createListBlockSchema = (options: {
|
||||||
|
dataSource: string;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
rowKey?: string;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, dataSource, association, templateSchema, rowKey } = options;
|
||||||
|
const resourceName = association || collectionName;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${resourceName}:view`,
|
||||||
|
'x-decorator': 'List.Decorator',
|
||||||
|
'x-use-decorator-props': 'useListBlockDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: collectionName,
|
||||||
|
dataSource,
|
||||||
|
association,
|
||||||
|
readPretty: true,
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 10,
|
||||||
|
},
|
||||||
|
runWhenParamsChanged: true,
|
||||||
|
rowKey,
|
||||||
|
},
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:list',
|
||||||
|
properties: {
|
||||||
|
actionBar: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'list:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 'var(--nb-spacing)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
type: 'array',
|
||||||
|
'x-component': 'List',
|
||||||
|
'x-use-component-props': 'useListBlockProps',
|
||||||
|
properties: {
|
||||||
|
item: {
|
||||||
|
type: 'object',
|
||||||
|
'x-component': 'List.Item',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useListItemProps',
|
||||||
|
properties: {
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
},
|
||||||
|
actionBar: {
|
||||||
|
type: 'void',
|
||||||
|
'x-align': 'left',
|
||||||
|
'x-initializer': 'list:configureItemActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-use-component-props': 'useListActionBarProps',
|
||||||
|
'x-component-props': {
|
||||||
|
layout: 'one-column',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,15 @@
|
|||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
|
||||||
|
export function useListBlockDecoratorProps(props) {
|
||||||
|
let sourceId;
|
||||||
|
|
||||||
|
// 因为 association 的值是固定的,所以这里可以使用 hooks
|
||||||
|
if (props.association) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
sourceId = useDataBlockSourceId({ association: props.association });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
}
|
@ -2,7 +2,7 @@ import { FormOutlined } from '@ant-design/icons';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||||
import { createTableSelectorSchema } from '../../../../schema-initializer/utils';
|
import { createTableSelectorUISchema } from './createTableSelectorUISchema';
|
||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
|
|
||||||
export const TableSelectorInitializer = () => {
|
export const TableSelectorInitializer = () => {
|
||||||
@ -16,11 +16,10 @@ export const TableSelectorInitializer = () => {
|
|||||||
{...others}
|
{...others}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
insert(
|
insert(
|
||||||
createTableSelectorSchema({
|
createTableSelectorUISchema({
|
||||||
rowKey: collection.filterTargetKey,
|
rowKey: collection.filterTargetKey,
|
||||||
collection: collection.name,
|
collectionName: collection.name,
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
resource: collection.name,
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -2,7 +2,7 @@ import { Page, expect, test } from '@nocobase/test/e2e';
|
|||||||
import { createTable } from './utils';
|
import { createTable } from './utils';
|
||||||
|
|
||||||
test.describe('where table data selector can be added', () => {
|
test.describe('where table data selector can be added', () => {
|
||||||
test('popup', async ({ page, mockPage, mockRecord }) => {
|
test('popup', async ({ page, mockPage }) => {
|
||||||
await createTable({ page, mockPage, fieldName: 'manyToOne' });
|
await createTable({ page, mockPage, fieldName: 'manyToOne' });
|
||||||
|
|
||||||
// 选中一行数据之后,弹窗自动关闭,且数据被填充到关联字段中
|
// 选中一行数据之后,弹窗自动关闭,且数据被填充到关联字段中
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
import { createTableSelectorUISchema } from '../createTableSelectorUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => {
|
||||||
|
return {
|
||||||
|
uid: () => 'mocked-uid',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createTableSelectorSchema', () => {
|
||||||
|
test('should return the correct schema', () => {
|
||||||
|
const schema = createTableSelectorUISchema({
|
||||||
|
collectionName: 'example',
|
||||||
|
dataSource: 'example',
|
||||||
|
rowKey: 'id',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "table:configureActions",
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "TableV2.Selector",
|
||||||
|
"x-component-props": {
|
||||||
|
"rowSelection": {
|
||||||
|
"type": "checkbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "table:configureColumns",
|
||||||
|
"x-use-component-props": "useTableSelectorProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "example:list",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "TableSelectorProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"collection": "example",
|
||||||
|
"dataSource": "example",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 20,
|
||||||
|
},
|
||||||
|
"rowKey": "id",
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:tableSelector",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useTableSelectorDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export const createTableSelectorUISchema = (options: {
|
||||||
|
collectionName: string;
|
||||||
|
dataSource: string;
|
||||||
|
rowKey: string;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, dataSource, rowKey } = options;
|
||||||
|
|
||||||
|
if (!collectionName || !dataSource || !rowKey) {
|
||||||
|
throw new Error('collectionName, dataSource, rowKey is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${collectionName}:list`,
|
||||||
|
'x-decorator': 'TableSelectorProvider',
|
||||||
|
'x-use-decorator-props': 'useTableSelectorDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: collectionName,
|
||||||
|
dataSource,
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
rowKey,
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:tableSelector',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'table:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 'var(--nb-spacing)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: 'array',
|
||||||
|
'x-initializer': 'table:configureColumns',
|
||||||
|
'x-component': 'TableV2.Selector',
|
||||||
|
'x-use-component-props': 'useTableSelectorProps',
|
||||||
|
'x-component-props': {
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export function useTableSelectorDecoratorProps() {}
|
@ -2,7 +2,7 @@ import { TableOutlined } from '@ant-design/icons';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { createCollapseBlockSchema } from '../../../../schema-initializer/utils';
|
import { createCollapseBlockSchema } from './createFilterCollapseBlockSchema';
|
||||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||||
import { Collection, CollectionFieldOptions } from '../../../../data-source';
|
import { Collection, CollectionFieldOptions } from '../../../../data-source';
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export const FilterCollapseBlockInitializer = ({
|
|||||||
onCreateBlockSchema={async ({ item }) => {
|
onCreateBlockSchema={async ({ item }) => {
|
||||||
const schema = createCollapseBlockSchema({
|
const schema = createCollapseBlockSchema({
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
collection: item.collectionName || item.name,
|
collectionName: item.collectionName || item.name,
|
||||||
// 与数据区块做区分
|
// 与数据区块做区分
|
||||||
blockType: 'filter',
|
blockType: 'filter',
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import { createCollapseBlockSchema } from '../createFilterCollapseBlockSchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => ({
|
||||||
|
uid: vi.fn().mockReturnValue('mocked-uid'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('createCollapseBlockSchema', () => {
|
||||||
|
it('should return the correct schema', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'testCollection',
|
||||||
|
dataSource: 'testDataSource',
|
||||||
|
blockType: 'testBlockType',
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = createCollapseBlockSchema(options);
|
||||||
|
|
||||||
|
expect(schema).toEqual({
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'AssociationFilter.Provider',
|
||||||
|
'x-use-decorator-props': 'useCollapseBlockDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: 'testCollection',
|
||||||
|
dataSource: 'testDataSource',
|
||||||
|
blockType: 'testBlockType',
|
||||||
|
associationFilterStyle: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
name: 'filter-collapse',
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:filterCollapse',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-filter-targets': [],
|
||||||
|
properties: {
|
||||||
|
'mocked-uid': {
|
||||||
|
type: 'void',
|
||||||
|
'x-action': 'associateFilter',
|
||||||
|
'x-initializer': 'filterCollapse:configureFields',
|
||||||
|
'x-component': 'AssociationFilter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,37 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export const createCollapseBlockSchema = (options: {
|
||||||
|
collectionName: string;
|
||||||
|
dataSource: string;
|
||||||
|
blockType: string;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, dataSource, blockType } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'AssociationFilter.Provider',
|
||||||
|
'x-use-decorator-props': 'useCollapseBlockDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: collectionName,
|
||||||
|
dataSource,
|
||||||
|
blockType,
|
||||||
|
associationFilterStyle: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
name: 'filter-collapse',
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:filterCollapse',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-filter-targets': [],
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-action': 'associateFilter',
|
||||||
|
'x-initializer': 'filterCollapse:configureFields',
|
||||||
|
'x-component': 'AssociationFilter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export function useCollapseBlockDecoratorProps() {}
|
@ -2,11 +2,7 @@ import { useCollection_deprecated } from '../../../..';
|
|||||||
import { CompatibleSchemaInitializer } from '../../../../application/schema-initializer/CompatibleSchemaInitializer';
|
import { CompatibleSchemaInitializer } from '../../../../application/schema-initializer/CompatibleSchemaInitializer';
|
||||||
import { gridRowColWrap } from '../../../../schema-initializer/utils';
|
import { gridRowColWrap } from '../../../../schema-initializer/utils';
|
||||||
|
|
||||||
/**
|
const commonOptions = {
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitializer({
|
|
||||||
name: 'TableSelectorInitializers',
|
|
||||||
wrap: gridRowColWrap,
|
wrap: gridRowColWrap,
|
||||||
title: '{{t("Add block")}}',
|
title: '{{t("Add block")}}',
|
||||||
icon: 'PlusOutlined',
|
icon: 'PlusOutlined',
|
||||||
@ -84,88 +80,20 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitializer({
|
||||||
|
name: 'TableSelectorInitializers',
|
||||||
|
...commonOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const tableSelectorInitializers = new CompatibleSchemaInitializer(
|
export const tableSelectorInitializers = new CompatibleSchemaInitializer(
|
||||||
{
|
{
|
||||||
name: 'popup:tableSelector:addBlock',
|
name: 'popup:tableSelector:addBlock',
|
||||||
wrap: gridRowColWrap,
|
...commonOptions,
|
||||||
title: '{{t("Add block")}}',
|
|
||||||
icon: 'PlusOutlined',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: '{{t("Selector")}}',
|
|
||||||
name: 'selector',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
title: 'Table',
|
|
||||||
Component: 'TableSelectorInitializer',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: '{{t("Filter blocks")}}',
|
|
||||||
name: 'filterBlocks',
|
|
||||||
useChildren() {
|
|
||||||
const { name, dataSource } = useCollection_deprecated();
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'filterFormBlockInTableSelector',
|
|
||||||
title: '{{t("Form")}}',
|
|
||||||
Component: 'FilterFormBlockInitializer',
|
|
||||||
componentProps: {
|
|
||||||
filterCollections() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
onlyCurrentDataSource: true,
|
|
||||||
},
|
|
||||||
collectionName: name,
|
|
||||||
dataSource,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'filterCollapseBlockInTableSelector',
|
|
||||||
title: '{{t("Collapse")}}',
|
|
||||||
Component: 'FilterCollapseBlockInitializer',
|
|
||||||
componentProps: {
|
|
||||||
filterCollections() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
onlyCurrentDataSource: true,
|
|
||||||
},
|
|
||||||
collectionName: name,
|
|
||||||
dataSource,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'itemGroup',
|
|
||||||
title: '{{t("Other blocks")}}',
|
|
||||||
name: 'otherBlocks',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: '{{t("Add text")}}',
|
|
||||||
Component: 'BlockItemInitializer',
|
|
||||||
name: 'addText',
|
|
||||||
schema: {
|
|
||||||
type: 'void',
|
|
||||||
'x-editable': false,
|
|
||||||
'x-decorator': 'BlockItem',
|
|
||||||
// 'x-designer': 'Markdown.Void.Designer',
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
'x-settings': 'blockSettings:markdown',
|
|
||||||
'x-component': 'Markdown.Void',
|
|
||||||
'x-component-props': {
|
|
||||||
content: '{{t("This is a demo text, **supports Markdown syntax**.")}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
tableSelectorInitializers_deprecated,
|
tableSelectorInitializers_deprecated,
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import { createPortal } from 'react-dom';
|
|||||||
import { DndContext } from '../../common';
|
import { DndContext } from '../../common';
|
||||||
import { useDesignable, useProps } from '../../hooks';
|
import { useDesignable, useProps } from '../../hooks';
|
||||||
import { useSchemaInitializerRender } from '../../../application';
|
import { useSchemaInitializerRender } from '../../../application';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
interface ActionBarContextForceProps {
|
interface ActionBarContextForceProps {
|
||||||
layout?: 'one-column' | 'tow-columns';
|
layout?: 'one-column' | 'tow-columns';
|
||||||
@ -46,10 +47,13 @@ const Portal: React.FC = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ActionBar = observer(
|
export const ActionBar = withDynamicSchemaProps(
|
||||||
(props: any) => {
|
observer((props: any) => {
|
||||||
const { forceProps = {} } = useActionBarContext();
|
const { forceProps = {} } = useActionBarContext();
|
||||||
|
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { layout = 'tow-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any;
|
const { layout = 'tow-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any;
|
||||||
|
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
@ -128,6 +132,6 @@ export const ActionBar = observer(
|
|||||||
{render()}
|
{render()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}),
|
||||||
{ displayName: 'ActionBar' },
|
{ displayName: 'ActionBar' },
|
||||||
);
|
);
|
||||||
|
@ -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';
|
||||||
|
@ -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,14 +198,18 @@ 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>();
|
||||||
|
|
||||||
|
// 新版 UISchema(1.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}>
|
||||||
<form
|
<form
|
||||||
|
onSubmit={(e) => e.preventDefault()}
|
||||||
className={css`
|
className={css`
|
||||||
.ant-formily-item-feedback-layout-loose {
|
.ant-formily-item-feedback-layout-loose {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
@ -221,7 +226,7 @@ export const Form: React.FC<FormProps> & {
|
|||||||
</form>
|
</form>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
);
|
);
|
||||||
},
|
}),
|
||||||
{ displayName: 'Form' },
|
{ displayName: 'Form' },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { FormContext, useField } from '@formily/react';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
import { BlockProvider, useBlockRequestContext, useParsedFilter } from '../../../block-provider';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const ListBlockContext = createContext<any>({});
|
export const ListBlockContext = createContext<any>({});
|
||||||
ListBlockContext.displayName = 'ListBlockContext';
|
ListBlockContext.displayName = 'ListBlockContext';
|
||||||
@ -52,7 +53,7 @@ const InternalListBlockProvider = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ListBlockProvider = (props) => {
|
export const ListBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
const { params } = props;
|
const { params } = props;
|
||||||
const { filter: parsedFilter } = useParsedFilter({
|
const { filter: parsedFilter } = useParsedFilter({
|
||||||
filterOption: params?.filter,
|
filterOption: params?.filter,
|
||||||
@ -75,7 +76,7 @@ export const ListBlockProvider = (props) => {
|
|||||||
<InternalListBlockProvider {...props} />
|
<InternalListBlockProvider {...props} />
|
||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export const useListBlockContext = () => {
|
export const useListBlockContext = () => {
|
||||||
return useContext(ListBlockContext);
|
return useContext(ListBlockContext);
|
||||||
|
@ -7,8 +7,9 @@ import { useDesignable } from '../../hooks';
|
|||||||
|
|
||||||
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
|
import { useCollectionParentRecordData } from '../../../data-source/collection-record/CollectionRecordProvider';
|
||||||
import { RecordProvider } from '../../../record-provider';
|
import { RecordProvider } from '../../../record-provider';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const ListItem = (props) => {
|
export const ListItem = withDynamicSchemaProps((props) => {
|
||||||
const field = useField<ObjectField>();
|
const field = useField<ObjectField>();
|
||||||
const { designable } = useDesignable();
|
const { designable } = useDesignable();
|
||||||
const parentRecordData = useCollectionParentRecordData();
|
const parentRecordData = useCollectionParentRecordData();
|
||||||
@ -29,4 +30,4 @@ export const ListItem = (props) => {
|
|||||||
</RecordProvider>
|
</RecordProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -11,6 +11,7 @@ import { ListDesigner } from './List.Designer';
|
|||||||
import { ListItem } from './List.Item';
|
import { ListItem } from './List.Item';
|
||||||
import useStyles from './List.style';
|
import useStyles from './List.style';
|
||||||
import { useListActionBarProps } from './hooks';
|
import { useListActionBarProps } from './hooks';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
const InternalList = (props) => {
|
const InternalList = (props) => {
|
||||||
const { service } = useListBlockContext();
|
const { service } = useListBlockContext();
|
||||||
@ -93,7 +94,7 @@ const InternalList = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const List = InternalList as typeof InternalList & {
|
export const List = withDynamicSchemaProps(InternalList) as typeof InternalList & {
|
||||||
Item: typeof ListItem;
|
Item: typeof ListItem;
|
||||||
Designer: typeof ListDesigner;
|
Designer: typeof ListDesigner;
|
||||||
Decorator: typeof ListBlockProvider;
|
Decorator: typeof ListBlockProvider;
|
||||||
|
@ -8,6 +8,12 @@ interface Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const useDef = () => ({});
|
const useDef = () => ({});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
|
* @param originalProps
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export const useProps = (originalProps: any = {}) => {
|
export const useProps = (originalProps: any = {}) => {
|
||||||
const { useProps: useDynamicHook = useDef, ...others } = originalProps;
|
const { useProps: useDynamicHook = useDef, ...others } = originalProps;
|
||||||
let useDynamicProps = useDynamicHook;
|
let useDynamicProps = useDynamicHook;
|
||||||
|
@ -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;
|
||||||
|
@ -4,7 +4,8 @@ import React, { useCallback } from 'react';
|
|||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
||||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../collection-manager';
|
||||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||||
import { createListBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||||
|
import { createListBlockSchema } from '../../modules/blocks/data-blocks/list/createListBlockSchema';
|
||||||
|
|
||||||
export const RecordAssociationListBlockInitializer = () => {
|
export const RecordAssociationListBlockInitializer = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -28,11 +29,8 @@ export const RecordAssociationListBlockInitializer = () => {
|
|||||||
insert(
|
insert(
|
||||||
createListBlockSchema({
|
createListBlockSchema({
|
||||||
rowKey: collection.filterTargetKey,
|
rowKey: collection.filterTargetKey,
|
||||||
collection: field.target,
|
|
||||||
resource,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: resource,
|
association: resource,
|
||||||
settings: 'blockSettings:list',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -54,10 +52,8 @@ export function useCreateAssociationListBlock() {
|
|||||||
insert(
|
insert(
|
||||||
createListBlockSchema({
|
createListBlockSchema({
|
||||||
rowKey: collection.filterTargetKey,
|
rowKey: collection.filterTargetKey,
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
settings: 'blockSettings:list',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
@ -972,94 +972,6 @@ export const createDetailsBlockSchema = (options) => {
|
|||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createListBlockSchema = (options) => {
|
|
||||||
const {
|
|
||||||
formItemInitializers = 'details:configureFields',
|
|
||||||
actionInitializers = 'list:configureActions',
|
|
||||||
itemActionInitializers = 'list:configureItemActions',
|
|
||||||
collection,
|
|
||||||
dataSource,
|
|
||||||
association,
|
|
||||||
template,
|
|
||||||
settings,
|
|
||||||
...others
|
|
||||||
} = options;
|
|
||||||
const resourceName = association || collection;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-acl-action': `${resourceName}:view`,
|
|
||||||
'x-decorator': 'List.Decorator',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection,
|
|
||||||
dataSource,
|
|
||||||
association,
|
|
||||||
readPretty: true,
|
|
||||||
action: 'list',
|
|
||||||
params: {
|
|
||||||
pageSize: 10,
|
|
||||||
},
|
|
||||||
runWhenParamsChanged: true,
|
|
||||||
...others,
|
|
||||||
},
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
'x-settings': settings,
|
|
||||||
properties: {
|
|
||||||
actionBar: {
|
|
||||||
type: 'void',
|
|
||||||
'x-initializer': actionInitializers,
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
marginBottom: 'var(--nb-spacing)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
list: {
|
|
||||||
type: 'array',
|
|
||||||
'x-component': 'List',
|
|
||||||
'x-component-props': {
|
|
||||||
props: '{{ useListBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
item: {
|
|
||||||
type: 'object',
|
|
||||||
'x-component': 'List.Item',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useListItemProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
grid: template || {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-initializer': formItemInitializers,
|
|
||||||
'x-initializer-props': {
|
|
||||||
useProps: '{{ useListItemInitializerProps }}',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
actionBar: {
|
|
||||||
type: 'void',
|
|
||||||
'x-align': 'left',
|
|
||||||
'x-initializer': itemActionInitializers,
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useListActionBarProps }}',
|
|
||||||
layout: 'one-column',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createGridCardBlockSchema = (options) => {
|
export const createGridCardBlockSchema = (options) => {
|
||||||
const {
|
const {
|
||||||
formItemInitializers = 'details:configureFields',
|
formItemInitializers = 'details:configureFields',
|
||||||
@ -1150,7 +1062,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',
|
||||||
@ -1453,87 +1386,6 @@ export const createTableBlockSchema = (options) => {
|
|||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createCollapseBlockSchema = (options) => {
|
|
||||||
const { collection, dataSource, blockType } = options;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'AssociationFilter.Provider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection,
|
|
||||||
dataSource,
|
|
||||||
blockType,
|
|
||||||
associationFilterStyle: {
|
|
||||||
width: '100%',
|
|
||||||
},
|
|
||||||
name: 'filter-collapse',
|
|
||||||
},
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
'x-settings': 'blockSettings:filterCollapse',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
'x-filter-targets': [],
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-action': 'associateFilter',
|
|
||||||
'x-initializer': 'filterCollapse:configureFields',
|
|
||||||
'x-component': 'AssociationFilter',
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createTableSelectorSchema = (options) => {
|
|
||||||
const { collection, dataSource, resource, rowKey, ...others } = options;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-acl-action': `${resource || collection}:list`,
|
|
||||||
'x-decorator': 'TableSelectorProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection,
|
|
||||||
resource: resource || collection,
|
|
||||||
dataSource,
|
|
||||||
action: 'list',
|
|
||||||
params: {
|
|
||||||
pageSize: 20,
|
|
||||||
},
|
|
||||||
rowKey,
|
|
||||||
...others,
|
|
||||||
},
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
'x-settings': 'blockSettings:tableSelector',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-initializer': 'table:configureActions',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
marginBottom: 'var(--nb-spacing)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: 'array',
|
|
||||||
'x-initializer': 'table:configureColumns',
|
|
||||||
'x-component': 'TableV2.Selector',
|
|
||||||
'x-component-props': {
|
|
||||||
rowSelection: {
|
|
||||||
type: 'checkbox',
|
|
||||||
},
|
|
||||||
useProps: '{{ useTableSelectorProps }}',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getChildren = ({
|
const getChildren = ({
|
||||||
collections,
|
collections,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
@ -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,
|
? {
|
||||||
collection: collection.name,
|
association,
|
||||||
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,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
import { test, expect } from '@nocobase/test/e2e';
|
||||||
|
import { emptyPageWithCalendarCollection, oneTableWithCalendarCollection } from './templates';
|
||||||
|
|
||||||
|
test.describe('where can be added', () => {
|
||||||
|
test('page', async ({ page, mockPage }) => {
|
||||||
|
await mockPage(emptyPageWithCalendarCollection).goto();
|
||||||
|
|
||||||
|
await page.getByLabel('schema-initializer-Grid-page:').hover();
|
||||||
|
await page.getByRole('menuitem', { name: 'form Calendar right' }).hover();
|
||||||
|
await page.getByRole('menuitem', { name: 'calendar', exact: true }).click();
|
||||||
|
|
||||||
|
await page.getByLabel('block-item-Select-Title field').getByTestId('select-single').click();
|
||||||
|
await page.getByRole('option', { name: 'Repeats' }).click();
|
||||||
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
|
|
||||||
|
await expect(page.getByLabel('block-item-CardItem-calendar-').getByText('Sun', { exact: true })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('association block in popup', async ({ page, mockPage, mockRecord }) => {
|
||||||
|
await mockPage(oneTableWithCalendarCollection).goto();
|
||||||
|
await mockRecord('toManyCalendar');
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
await page.getByLabel('action-Action.Link-View-view-').first().click();
|
||||||
|
await page.getByLabel('schema-initializer-Grid-popup').hover();
|
||||||
|
await page.getByRole('menuitem', { name: 'form Calendar right' }).hover();
|
||||||
|
await page.getByRole('menuitem', { name: 'manyToMany -> calendar' }).click();
|
||||||
|
|
||||||
|
await page.getByLabel('block-item-Select-Title field').getByTestId('select-single').click();
|
||||||
|
await page.getByRole('option', { name: 'Repeats' }).click();
|
||||||
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
|
|
||||||
|
await expect(page.getByLabel('block-item-CardItem-calendar-').getByText('Sun', { exact: true })).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,238 @@
|
|||||||
|
import { PageConfig } from '@nocobase/test/e2e';
|
||||||
|
|
||||||
|
const calendarCollection = {
|
||||||
|
name: 'calendar',
|
||||||
|
template: 'calendar',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const emptyPageWithCalendarCollection: PageConfig = {
|
||||||
|
collections: [calendarCollection],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const oneTableWithCalendarCollection: PageConfig = {
|
||||||
|
collections: [
|
||||||
|
calendarCollection,
|
||||||
|
{
|
||||||
|
name: 'toManyCalendar',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'manyToMany',
|
||||||
|
interface: 'm2m',
|
||||||
|
target: 'calendar',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pageSchema: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Page',
|
||||||
|
properties: {
|
||||||
|
lqs2pzl6li1: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'page:addBlock',
|
||||||
|
properties: {
|
||||||
|
viawniezd4p: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Row',
|
||||||
|
properties: {
|
||||||
|
s0nef2zgi5m: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
properties: {
|
||||||
|
ibwqgtls50q: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'TableBlockProvider',
|
||||||
|
'x-acl-action': 'toManyCalendar:list',
|
||||||
|
'x-use-decorator-props': 'useTableBlockDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: 'toManyCalendar',
|
||||||
|
dataSource: 'main',
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
rowKey: 'id',
|
||||||
|
showIndex: true,
|
||||||
|
dragSort: false,
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:table',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
'x-filter-targets': [],
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'table:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 'var(--nb-spacing)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'xi12fv3arso',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
v931jk5mpmg: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'array',
|
||||||
|
'x-initializer': 'table:configureColumns',
|
||||||
|
'x-component': 'TableV2',
|
||||||
|
'x-use-component-props': 'useTableBlockProps',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Actions") }}',
|
||||||
|
'x-action-column': 'actions',
|
||||||
|
'x-decorator': 'TableV2.Column.ActionBar',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||||
|
'x-initializer': 'table:configureItemActions',
|
||||||
|
properties: {
|
||||||
|
w4rc8u7s5q0: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'DndContext',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
hd95fsevokf: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("View") }}',
|
||||||
|
'x-action': 'view',
|
||||||
|
'x-toolbar': 'ActionSchemaToolbar',
|
||||||
|
'x-settings': 'actionSettings:view',
|
||||||
|
'x-component': 'Action.Link',
|
||||||
|
'x-component-props': {
|
||||||
|
openMode: 'drawer',
|
||||||
|
},
|
||||||
|
'x-decorator': 'ACLActionProvider',
|
||||||
|
'x-designer-props': {
|
||||||
|
linkageAction: true,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("View record") }}',
|
||||||
|
'x-component': 'Action.Container',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
tabs: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-initializer': 'TabPaneInitializers',
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Details")}}',
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-designer': 'Tabs.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
_isJSONSchemaObject: true,
|
||||||
|
version: '2.0',
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'popup:common:addBlock',
|
||||||
|
'x-uid': 'wfpwj2q55xi',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'tvwvyrlvpv6',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'bys1tnlre1o',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'ml2scl3y6se',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'q9gwy03bevt',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'f8i1npjyidq',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'h26hp47w83k',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'bjbs9yvab1k',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': '5i6112zy12n',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': '6vh9ncefvs2',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'u4b441tpuzq',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'mworqfx7jaf',
|
||||||
|
'x-async': false,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-uid': 'kgor3l32s12',
|
||||||
|
'x-async': true,
|
||||||
|
'x-index': 1,
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,116 @@
|
|||||||
|
import { createCalendarBlockUISchema } from '../schema-initializer/createCalendarBlockUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', async () => {
|
||||||
|
const actual = await vi.importActual('@formily/shared');
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
uid: () => 'mocked-uid',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createCalendarBlockSchema', () => {
|
||||||
|
it('should return the correct schema', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'users',
|
||||||
|
dataSource: 'events',
|
||||||
|
fieldNames: {
|
||||||
|
title: 'title',
|
||||||
|
startDate: 'start_date',
|
||||||
|
endDate: 'end_date',
|
||||||
|
},
|
||||||
|
association: 'users.roles',
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = createCalendarBlockUISchema(options);
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"event": {
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "popup:common:addBlock",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"actionInitializers": "details:configureActions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{t('Details', { ns: 'calendar' })}}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
"x-initializer-props": {
|
||||||
|
"gridInitializer": "popup:common:addBlock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{t('View record', { ns: 'calendar' })}}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CalendarV2.Event",
|
||||||
|
},
|
||||||
|
"toolBar": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CalendarV2.ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "calendar:configureActions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "CalendarV2",
|
||||||
|
"x-use-component-props": "useCalendarBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "users.roles:list",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "CalendarBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"association": "users.roles",
|
||||||
|
"collection": "users",
|
||||||
|
"dataSource": "events",
|
||||||
|
"fieldNames": {
|
||||||
|
"endDate": "end_date",
|
||||||
|
"id": "id",
|
||||||
|
"startDate": "start_date",
|
||||||
|
"title": "title",
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"paginate": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:calendar",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useCalendarBlockDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,12 @@
|
|||||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
import { RecursionField, Schema, observer, useFieldSchema } from '@formily/react';
|
import { RecursionField, Schema, observer, useFieldSchema } from '@formily/react';
|
||||||
import { ActionContextProvider, RecordProvider, useCollectionParentRecordData, useProps } from '@nocobase/client';
|
import {
|
||||||
|
ActionContextProvider,
|
||||||
|
RecordProvider,
|
||||||
|
useCollectionParentRecordData,
|
||||||
|
useProps,
|
||||||
|
withDynamicSchemaProps,
|
||||||
|
} from '@nocobase/client';
|
||||||
import { parseExpression } from 'cron-parser';
|
import { parseExpression } from 'cron-parser';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@ -171,100 +177,104 @@ const CalendarRecordViewer = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Calendar: any = observer(
|
export const Calendar: any = withDynamicSchemaProps(
|
||||||
(props: any) => {
|
observer(
|
||||||
const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props);
|
(props: any) => {
|
||||||
const [date, setDate] = useState<Date>(new Date());
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const [view, setView] = useState<View>('month');
|
const { dataSource, fieldNames, showLunar, fixedBlock } = useProps(props);
|
||||||
const events = useEvents(dataSource, fieldNames, date, view);
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const [record, setRecord] = useState<any>({});
|
|
||||||
const { wrapSSR, hashId, componentCls: containerClassName } = useStyle();
|
|
||||||
|
|
||||||
const components = useMemo(() => {
|
const [date, setDate] = useState<Date>(new Date());
|
||||||
return {
|
const [view, setView] = useState<View>('month');
|
||||||
toolbar: (props) => <Toolbar {...props} showLunar={showLunar}></Toolbar>,
|
const events = useEvents(dataSource, fieldNames, date, view);
|
||||||
// week: {
|
const [visible, setVisible] = useState(false);
|
||||||
// header: (props) => <Header {...props} type="week" showLunar={showLunar}></Header>,
|
const [record, setRecord] = useState<any>({});
|
||||||
// },
|
const { wrapSSR, hashId, componentCls: containerClassName } = useStyle();
|
||||||
month: {
|
|
||||||
dateHeader: (props) => <Header {...props} showLunar={showLunar}></Header>,
|
const components = useMemo(() => {
|
||||||
},
|
return {
|
||||||
|
toolbar: (props) => <Toolbar {...props} showLunar={showLunar}></Toolbar>,
|
||||||
|
// week: {
|
||||||
|
// header: (props) => <Header {...props} type="week" showLunar={showLunar}></Header>,
|
||||||
|
// },
|
||||||
|
month: {
|
||||||
|
dateHeader: (props) => <Header {...props} showLunar={showLunar}></Header>,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, [showLunar]);
|
||||||
|
|
||||||
|
const messages: any = {
|
||||||
|
allDay: '',
|
||||||
|
previous: (
|
||||||
|
<div>
|
||||||
|
<LeftOutlined />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
next: (
|
||||||
|
<div>
|
||||||
|
<RightOutlined />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
today: i18nt('Today'),
|
||||||
|
month: i18nt('Month'),
|
||||||
|
week: i18nt('Week'),
|
||||||
|
work_week: i18nt('Work week'),
|
||||||
|
day: i18nt('Day'),
|
||||||
|
agenda: i18nt('Agenda'),
|
||||||
|
date: i18nt('Date'),
|
||||||
|
time: i18nt('Time'),
|
||||||
|
event: i18nt('Event'),
|
||||||
|
noEventsInRange: i18nt('None'),
|
||||||
|
showMore: (count) => i18nt('{{count}} more items', { count }),
|
||||||
};
|
};
|
||||||
}, [showLunar]);
|
return wrapSSR(
|
||||||
|
<div className={`${hashId} ${containerClassName}`} style={{ height: fixedBlock ? '100%' : 700 }}>
|
||||||
const messages: any = {
|
<GlobalStyle />
|
||||||
allDay: '',
|
<CalendarRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
||||||
previous: (
|
<BigCalendar
|
||||||
<div>
|
popup
|
||||||
<LeftOutlined />
|
selectable
|
||||||
</div>
|
events={events}
|
||||||
),
|
view={view}
|
||||||
next: (
|
views={Weeks}
|
||||||
<div>
|
date={date}
|
||||||
<RightOutlined />
|
step={60}
|
||||||
</div>
|
showMultiDayTimes
|
||||||
),
|
messages={messages}
|
||||||
today: i18nt('Today'),
|
onNavigate={setDate}
|
||||||
month: i18nt('Month'),
|
onView={setView}
|
||||||
week: i18nt('Week'),
|
onSelectSlot={(slotInfo) => {
|
||||||
work_week: i18nt('Work week'),
|
console.log('onSelectSlot', slotInfo);
|
||||||
day: i18nt('Day'),
|
}}
|
||||||
agenda: i18nt('Agenda'),
|
onDoubleClickEvent={() => {
|
||||||
date: i18nt('Date'),
|
console.log('onDoubleClickEvent');
|
||||||
time: i18nt('Time'),
|
}}
|
||||||
event: i18nt('Event'),
|
onSelectEvent={(event) => {
|
||||||
noEventsInRange: i18nt('None'),
|
const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
|
||||||
showMore: (count) => i18nt('{{count}} more items', { count }),
|
if (!record) {
|
||||||
};
|
return;
|
||||||
return wrapSSR(
|
|
||||||
<div className={`${hashId} ${containerClassName}`} style={{ height: fixedBlock ? '100%' : 700 }}>
|
|
||||||
<GlobalStyle />
|
|
||||||
<CalendarRecordViewer visible={visible} setVisible={setVisible} record={record} />
|
|
||||||
<BigCalendar
|
|
||||||
popup
|
|
||||||
selectable
|
|
||||||
events={events}
|
|
||||||
view={view}
|
|
||||||
views={Weeks}
|
|
||||||
date={date}
|
|
||||||
step={60}
|
|
||||||
showMultiDayTimes
|
|
||||||
messages={messages}
|
|
||||||
onNavigate={setDate}
|
|
||||||
onView={setView}
|
|
||||||
onSelectSlot={(slotInfo) => {
|
|
||||||
console.log('onSelectSlot', slotInfo);
|
|
||||||
}}
|
|
||||||
onDoubleClickEvent={() => {
|
|
||||||
console.log('onDoubleClickEvent');
|
|
||||||
}}
|
|
||||||
onSelectEvent={(event) => {
|
|
||||||
const record = dataSource?.find((item) => item[fieldNames.id] === event.id);
|
|
||||||
if (!record) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
record.__event = { ...event, start: formatDate(dayjs(event.start)), end: formatDate(dayjs(event.end)) };
|
|
||||||
|
|
||||||
setRecord(record);
|
|
||||||
setVisible(true);
|
|
||||||
}}
|
|
||||||
formats={{
|
|
||||||
monthHeaderFormat: 'YYYY-M',
|
|
||||||
agendaDateFormat: 'M-DD',
|
|
||||||
dayHeaderFormat: 'YYYY-M-DD',
|
|
||||||
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
|
||||||
if (dates.eq(start, end, 'month')) {
|
|
||||||
return local.format(start, 'YYYY-M', culture);
|
|
||||||
}
|
}
|
||||||
return `${local.format(start, 'YYYY-M', culture)} - ${local.format(end, 'YYYY-M', culture)}`;
|
record.__event = { ...event, start: formatDate(dayjs(event.start)), end: formatDate(dayjs(event.end)) };
|
||||||
},
|
|
||||||
}}
|
setRecord(record);
|
||||||
components={components}
|
setVisible(true);
|
||||||
localizer={localizer}
|
}}
|
||||||
/>
|
formats={{
|
||||||
</div>,
|
monthHeaderFormat: 'YYYY-M',
|
||||||
);
|
agendaDateFormat: 'M-DD',
|
||||||
},
|
dayHeaderFormat: 'YYYY-M-DD',
|
||||||
{ displayName: 'Calendar' },
|
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
||||||
|
if (dates.eq(start, end, 'month')) {
|
||||||
|
return local.format(start, 'YYYY-M', culture);
|
||||||
|
}
|
||||||
|
return `${local.format(start, 'YYYY-M', culture)} - ${local.format(end, 'YYYY-M', culture)}`;
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
components={components}
|
||||||
|
localizer={localizer}
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Calendar' },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import { useDataBlockSourceId } from '@nocobase/client';
|
||||||
|
import { useCalendarBlockParams } from './useCalendarBlockParams';
|
||||||
|
|
||||||
|
export function useCalendarBlockDecoratorProps(props) {
|
||||||
|
const params = useCalendarBlockParams(props);
|
||||||
|
let sourceId: string;
|
||||||
|
|
||||||
|
// 因为 association 是一个固定的值,所以可以在 hooks 中直接使用
|
||||||
|
if (props.association) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
sourceId = useDataBlockSourceId({ association: props.association });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
params,
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
export function useCalendarBlockParams(props) {
|
||||||
|
const appends = useMemo(() => {
|
||||||
|
const arr: string[] = [];
|
||||||
|
const start = props.fieldNames?.start;
|
||||||
|
const end = props.fieldNames?.end;
|
||||||
|
|
||||||
|
if (Array.isArray(start) && start.length >= 2) {
|
||||||
|
arr.push(start[0]);
|
||||||
|
}
|
||||||
|
if (Array.isArray(end) && end.length >= 2) {
|
||||||
|
arr.push(end[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}, [props.fieldNames]);
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return { ...props.params, appends: [...appends, ...(props.params.appends || [])], paginate: false };
|
||||||
|
}, [appends, props.params]);
|
||||||
|
}
|
@ -16,6 +16,7 @@ import {
|
|||||||
useCreateAssociationCalendarBlock,
|
useCreateAssociationCalendarBlock,
|
||||||
} from './schema-initializer/items';
|
} from './schema-initializer/items';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { useCalendarBlockDecoratorProps } from './hooks/useCalendarBlockDecoratorProps';
|
||||||
|
|
||||||
export class PluginCalendarClient extends Plugin {
|
export class PluginCalendarClient extends Plugin {
|
||||||
async load() {
|
async load() {
|
||||||
@ -60,7 +61,7 @@ export class PluginCalendarClient extends Plugin {
|
|||||||
RecordAssociationCalendarBlockInitializer,
|
RecordAssociationCalendarBlockInitializer,
|
||||||
CalendarV2,
|
CalendarV2,
|
||||||
});
|
});
|
||||||
this.app.addScopes({ useCalendarBlockProps });
|
this.app.addScopes({ useCalendarBlockProps, useCalendarBlockDecoratorProps });
|
||||||
this.schemaSettingsManager.add(calendarBlockSettings);
|
this.schemaSettingsManager.add(calendarBlockSettings);
|
||||||
this.app.schemaInitializerManager.add(CalendarActionInitializers_deprecated);
|
this.app.schemaInitializerManager.add(CalendarActionInitializers_deprecated);
|
||||||
this.app.schemaInitializerManager.add(calendarActionInitializers);
|
this.app.schemaInitializerManager.add(calendarActionInitializers);
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import { ArrayField } from '@formily/core';
|
import { ArrayField } from '@formily/core';
|
||||||
import { useField } from '@formily/react';
|
import { useField, useFieldSchema } from '@formily/react';
|
||||||
import { BlockProvider, FixedBlockWrapper, useBlockRequestContext, useParsedFilter } from '@nocobase/client';
|
import {
|
||||||
|
BlockProvider,
|
||||||
|
FixedBlockWrapper,
|
||||||
|
useBlockRequestContext,
|
||||||
|
useParsedFilter,
|
||||||
|
withDynamicSchemaProps,
|
||||||
|
} from '@nocobase/client';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||||
|
import { useCalendarBlockParams } from '../hooks/useCalendarBlockParams';
|
||||||
|
|
||||||
export const CalendarBlockContext = createContext<any>({});
|
export const CalendarBlockContext = createContext<any>({});
|
||||||
CalendarBlockContext.displayName = 'CalendarBlockContext';
|
CalendarBlockContext.displayName = 'CalendarBlockContext';
|
||||||
@ -38,31 +45,26 @@ const InternalCalendarBlockProvider = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CalendarBlockProvider = (props) => {
|
const useCompatCalendarBlockParams = (props) => {
|
||||||
const appends = useMemo(() => {
|
const fieldSchema = useFieldSchema();
|
||||||
const arr: string[] = [];
|
|
||||||
const start = props.fieldNames?.start;
|
|
||||||
const end = props.fieldNames?.end;
|
|
||||||
|
|
||||||
if (Array.isArray(start) && start.length >= 2) {
|
// 因为 x-use-decorator-props 的值是固定不变的,所以可以在条件中使用 hooks
|
||||||
arr.push(start[0]);
|
if (fieldSchema['x-use-decorator-props']) {
|
||||||
}
|
return props.params;
|
||||||
if (Array.isArray(end) && end.length >= 2) {
|
} else {
|
||||||
arr.push(end[0]);
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
}
|
return useCalendarBlockParams(props);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return arr;
|
export const CalendarBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
}, [props.fieldNames]);
|
const params = useCompatCalendarBlockParams(props);
|
||||||
return (
|
return (
|
||||||
<BlockProvider
|
<BlockProvider name="calendar" {...props} params={params}>
|
||||||
name="calendar"
|
|
||||||
{...props}
|
|
||||||
params={{ ...props.params, appends: [...appends, ...(props.params.appends || [])], paginate: false }}
|
|
||||||
>
|
|
||||||
<InternalCalendarBlockProvider {...props} />
|
<InternalCalendarBlockProvider {...props} />
|
||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export const useCalendarBlockContext = () => {
|
export const useCalendarBlockContext = () => {
|
||||||
return useContext(CalendarBlockContext);
|
return useContext(CalendarBlockContext);
|
||||||
|
@ -2,16 +2,23 @@ import { ISchema } from '@formily/react';
|
|||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { generateNTemplate } from '../../locale';
|
import { generateNTemplate } from '../../locale';
|
||||||
|
|
||||||
export const createCalendarBlockSchema = (options) => {
|
export const createCalendarBlockUISchema = (options: {
|
||||||
const { collection, dataSource, resource, fieldNames, settings, ...others } = options;
|
dataSource: string;
|
||||||
const schema: ISchema = {
|
fieldNames: object;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, dataSource, fieldNames, association } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-acl-action': `${resource || collection}:list`,
|
'x-acl-action': `${association || collectionName}:list`,
|
||||||
'x-decorator': 'CalendarBlockProvider',
|
'x-decorator': 'CalendarBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useCalendarBlockDecoratorProps',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
collection: collection,
|
collection: collectionName,
|
||||||
dataSource,
|
dataSource,
|
||||||
resource: resource || collection,
|
association,
|
||||||
action: 'list',
|
action: 'list',
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
@ -20,18 +27,15 @@ export const createCalendarBlockSchema = (options) => {
|
|||||||
params: {
|
params: {
|
||||||
paginate: false,
|
paginate: false,
|
||||||
},
|
},
|
||||||
...others,
|
|
||||||
},
|
},
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
'x-settings': settings,
|
'x-settings': 'blockSettings:calendar',
|
||||||
'x-component': 'CardItem',
|
'x-component': 'CardItem',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'CalendarV2',
|
'x-component': 'CalendarV2',
|
||||||
'x-component-props': {
|
'x-use-component-props': 'useCalendarBlockProps',
|
||||||
useProps: '{{ useCalendarBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
properties: {
|
||||||
toolBar: {
|
toolBar: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -42,7 +46,6 @@ export const createCalendarBlockSchema = (options) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
'x-initializer': 'calendar:configureActions',
|
'x-initializer': 'calendar:configureActions',
|
||||||
properties: {},
|
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -79,7 +82,6 @@ export const createCalendarBlockSchema = (options) => {
|
|||||||
actionInitializers: 'details:configureActions',
|
actionInitializers: 'details:configureActions',
|
||||||
},
|
},
|
||||||
'x-initializer': 'popup:common:addBlock',
|
'x-initializer': 'popup:common:addBlock',
|
||||||
properties: {},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -93,6 +95,4 @@ export const createCalendarBlockSchema = (options) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return schema;
|
|
||||||
};
|
};
|
@ -14,7 +14,7 @@ import {
|
|||||||
useSchemaInitializerItem,
|
useSchemaInitializerItem,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { createCalendarBlockSchema } from '../utils';
|
import { createCalendarBlockUISchema } from '../createCalendarBlockUISchema';
|
||||||
import { useTranslation } from '../../../locale';
|
import { useTranslation } from '../../../locale';
|
||||||
|
|
||||||
export const CalendarBlockInitializer = ({
|
export const CalendarBlockInitializer = ({
|
||||||
@ -95,13 +95,12 @@ export const CalendarBlockInitializer = ({
|
|||||||
initialValues: {},
|
initialValues: {},
|
||||||
});
|
});
|
||||||
insert(
|
insert(
|
||||||
createCalendarBlockSchema({
|
createCalendarBlockUISchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
...values,
|
...values,
|
||||||
},
|
},
|
||||||
settings: 'blockSettings:calendar',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
useSchemaInitializer,
|
useSchemaInitializer,
|
||||||
SchemaInitializerItem,
|
SchemaInitializerItem,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { createCalendarBlockSchema } from '../utils';
|
import { createCalendarBlockUISchema } from '../createCalendarBlockUISchema';
|
||||||
import { useTranslation } from '../../../locale';
|
import { useTranslation } from '../../../locale';
|
||||||
|
|
||||||
export const RecordAssociationCalendarBlockInitializer = () => {
|
export const RecordAssociationCalendarBlockInitializer = () => {
|
||||||
@ -98,10 +98,9 @@ export const RecordAssociationCalendarBlockInitializer = () => {
|
|||||||
initialValues: {},
|
initialValues: {},
|
||||||
});
|
});
|
||||||
insert(
|
insert(
|
||||||
createCalendarBlockSchema({
|
createCalendarBlockUISchema({
|
||||||
collection: field.target,
|
|
||||||
resource,
|
|
||||||
association: resource,
|
association: resource,
|
||||||
|
dataSource: item.dataSource,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
...values,
|
...values,
|
||||||
},
|
},
|
||||||
@ -183,13 +182,12 @@ export function useCreateAssociationCalendarBlock() {
|
|||||||
initialValues: {},
|
initialValues: {},
|
||||||
});
|
});
|
||||||
insert(
|
insert(
|
||||||
createCalendarBlockSchema({
|
createCalendarBlockUISchema({
|
||||||
collection: field.target,
|
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
|
dataSource: item.dataSource,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
...values,
|
...values,
|
||||||
},
|
},
|
||||||
settings: 'blockSettings:calendar',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
DataBlockInitializer,
|
DataBlockInitializer,
|
||||||
SchemaComponentOptions,
|
SchemaComponentOptions,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { createGanttBlockSchema } from './utils';
|
import { createGanttBlockUISchema } from './createGanttBlockUISchema';
|
||||||
|
|
||||||
export const GanttBlockInitializer = () => {
|
export const GanttBlockInitializer = () => {
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
@ -120,8 +120,8 @@ export const GanttBlockInitializer = () => {
|
|||||||
initialValues: {},
|
initialValues: {},
|
||||||
});
|
});
|
||||||
insert(
|
insert(
|
||||||
createGanttBlockSchema({
|
createGanttBlockUISchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
...values,
|
...values,
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
import { createGanttBlockUISchema } from '../createGanttBlockUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => {
|
||||||
|
return {
|
||||||
|
uid: () => 'mocked-uid',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('createGanttBlockSchema', () => {
|
||||||
|
it('should generate schema correctly', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'TestCollection',
|
||||||
|
fieldNames: {
|
||||||
|
label: 'field1',
|
||||||
|
value: 'field2',
|
||||||
|
},
|
||||||
|
dataSource: 'TestDataSource',
|
||||||
|
};
|
||||||
|
const schema = createGanttBlockUISchema(options);
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "popup:common:addBlock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{t("Details")}}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{ t("View record") }}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Gantt.Event",
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Space",
|
||||||
|
"x-component-props": {
|
||||||
|
"split": "|",
|
||||||
|
},
|
||||||
|
"x-decorator": "DndContext",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{ t("Actions") }}",
|
||||||
|
"type": "void",
|
||||||
|
"x-action-column": "actions",
|
||||||
|
"x-component": "TableV2.Column",
|
||||||
|
"x-decorator": "TableV2.Column.ActionBar",
|
||||||
|
"x-designer": "TableV2.ActionColumnDesigner",
|
||||||
|
"x-initializer": "table:configureItemActions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "TableV2",
|
||||||
|
"x-component-props": {
|
||||||
|
"pagination": false,
|
||||||
|
"rowKey": "id",
|
||||||
|
"rowSelection": {
|
||||||
|
"type": "checkbox",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-decorator": "div",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"style": {
|
||||||
|
"float": "left",
|
||||||
|
"maxWidth": "35%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "table:configureColumns",
|
||||||
|
"x-use-component-props": "useTableBlockProps",
|
||||||
|
},
|
||||||
|
"toolBar": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "gantt:configureActions",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Gantt",
|
||||||
|
"x-use-component-props": "useGanttBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "TestCollection:list",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "GanttBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"collection": "TestCollection",
|
||||||
|
"dataSource": "TestDataSource",
|
||||||
|
"fieldNames": {
|
||||||
|
"label": "field1",
|
||||||
|
"value": "field2",
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"paginate": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:gantt",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -9,6 +9,8 @@ import {
|
|||||||
useCollectionParentRecordData,
|
useCollectionParentRecordData,
|
||||||
useTableBlockContext,
|
useTableBlockContext,
|
||||||
useToken,
|
useToken,
|
||||||
|
withDynamicSchemaProps,
|
||||||
|
useProps,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
@ -81,7 +83,8 @@ const debounceHandleProcessChange = debounce(async (task: Task, resource, fieldN
|
|||||||
message.success(t('Saved successfully'));
|
message.success(t('Saved successfully'));
|
||||||
await service?.refresh();
|
await service?.refresh();
|
||||||
}, 300);
|
}, 300);
|
||||||
export const Gantt: any = (props: any) => {
|
|
||||||
|
export const Gantt: any = withDynamicSchemaProps((props: any) => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
@ -116,12 +119,13 @@ export const Gantt: any = (props: any) => {
|
|||||||
viewDate,
|
viewDate,
|
||||||
TooltipContent = StandardTooltipContent,
|
TooltipContent = StandardTooltipContent,
|
||||||
onDoubleClick,
|
onDoubleClick,
|
||||||
onClick,
|
|
||||||
onDelete,
|
onDelete,
|
||||||
onSelect,
|
onSelect,
|
||||||
useProps,
|
onExpanderClick,
|
||||||
} = props;
|
tasks,
|
||||||
const { onExpanderClick, tasks, expandAndCollapseAll } = useProps();
|
expandAndCollapseAll,
|
||||||
|
fieldNames,
|
||||||
|
} = useProps(props); // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const ctx = useGanttBlockContext();
|
const ctx = useGanttBlockContext();
|
||||||
const appInfo = useCurrentAppInfo();
|
const appInfo = useCurrentAppInfo();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -129,7 +133,6 @@ export const Gantt: any = (props: any) => {
|
|||||||
const tableCtx = useTableBlockContext();
|
const tableCtx = useTableBlockContext();
|
||||||
const { resource, service } = useBlockRequestContext();
|
const { resource, service } = useBlockRequestContext();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { fieldNames } = useProps(props);
|
|
||||||
const viewMode = fieldNames.range || 'day';
|
const viewMode = fieldNames.range || 'day';
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
const taskListRef = useRef<HTMLDivElement>(null);
|
const taskListRef = useRef<HTMLDivElement>(null);
|
||||||
@ -559,4 +562,4 @@ export const Gantt: any = (props: any) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export const createGanttBlockUISchema = (options: {
|
||||||
|
collectionName: string;
|
||||||
|
fieldNames: object;
|
||||||
|
dataSource: string;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, fieldNames, dataSource } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${collectionName}:list`,
|
||||||
|
'x-decorator': 'GanttBlockProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: collectionName,
|
||||||
|
dataSource,
|
||||||
|
action: 'list',
|
||||||
|
fieldNames,
|
||||||
|
params: {
|
||||||
|
paginate: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:gantt',
|
||||||
|
// 'x-designer': 'Gantt.Designer',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Gantt',
|
||||||
|
'x-use-component-props': 'useGanttBlockProps',
|
||||||
|
properties: {
|
||||||
|
toolBar: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-initializer': 'gantt:configureActions',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
type: 'array',
|
||||||
|
'x-decorator': 'div',
|
||||||
|
'x-decorator-props': {
|
||||||
|
style: {
|
||||||
|
float: 'left',
|
||||||
|
maxWidth: '35%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'x-initializer': 'table:configureColumns',
|
||||||
|
'x-component': 'TableV2',
|
||||||
|
'x-use-component-props': 'useTableBlockProps',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
rowSelection: {
|
||||||
|
type: 'checkbox',
|
||||||
|
},
|
||||||
|
pagination: false,
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{ t("Actions") }}',
|
||||||
|
'x-action-column': 'actions',
|
||||||
|
'x-decorator': 'TableV2.Column.ActionBar',
|
||||||
|
'x-component': 'TableV2.Column',
|
||||||
|
'x-designer': 'TableV2.ActionColumnDesigner',
|
||||||
|
'x-initializer': 'table:configureItemActions',
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'DndContext',
|
||||||
|
'x-component': 'Space',
|
||||||
|
'x-component-props': {
|
||||||
|
split: '|',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Gantt.Event',
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
title: '{{ t("View record") }}',
|
||||||
|
properties: {
|
||||||
|
tabs: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-initializer': 'TabPaneInitializers',
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Details")}}',
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-designer': 'Tabs.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'popup:common:addBlock',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -41,6 +41,10 @@ export class GanttPlugin extends Plugin {
|
|||||||
title: "{{t('Gantt')}}",
|
title: "{{t('Gantt')}}",
|
||||||
Component: 'GanttBlockInitializer',
|
Component: 'GanttBlockInitializer',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.app.addScopes({
|
||||||
|
useGanttBlockProps,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { ISchema } from '@formily/react';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { useCollection_deprecated, useCompile } from '@nocobase/client';
|
import { useCollection_deprecated, useCompile } from '@nocobase/client';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -19,131 +17,3 @@ export const useOptions = (type = 'string') => {
|
|||||||
});
|
});
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
export const createGanttBlockSchema = (options) => {
|
|
||||||
const { collection, resource, fieldNames, ...others } = options;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-acl-action': `${resource || collection}:list`,
|
|
||||||
'x-decorator': 'GanttBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: collection,
|
|
||||||
resource: resource || collection,
|
|
||||||
action: 'list',
|
|
||||||
fieldNames: {
|
|
||||||
...fieldNames,
|
|
||||||
},
|
|
||||||
params: {
|
|
||||||
paginate: false,
|
|
||||||
},
|
|
||||||
...others,
|
|
||||||
},
|
|
||||||
'x-designer': 'Gantt.Designer',
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Gantt',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useGanttBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
toolBar: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
marginBottom: 24,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'x-initializer': 'gantt:configureActions',
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
type: 'array',
|
|
||||||
'x-decorator': 'div',
|
|
||||||
'x-decorator-props': {
|
|
||||||
style: {
|
|
||||||
float: 'left',
|
|
||||||
maxWidth: '35%',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
'x-initializer': 'table:configureColumns',
|
|
||||||
'x-component': 'TableV2',
|
|
||||||
'x-component-props': {
|
|
||||||
rowKey: 'id',
|
|
||||||
rowSelection: {
|
|
||||||
type: 'checkbox',
|
|
||||||
},
|
|
||||||
useProps: '{{ useTableBlockProps }}',
|
|
||||||
pagination: false,
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
title: '{{ t("Actions") }}',
|
|
||||||
'x-action-column': 'actions',
|
|
||||||
'x-decorator': 'TableV2.Column.ActionBar',
|
|
||||||
'x-component': 'TableV2.Column',
|
|
||||||
'x-designer': 'TableV2.ActionColumnDesigner',
|
|
||||||
'x-initializer': 'table:configureItemActions',
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'DndContext',
|
|
||||||
'x-component': 'Space',
|
|
||||||
'x-component-props': {
|
|
||||||
split: '|',
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
detail: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Gantt.Event',
|
|
||||||
properties: {
|
|
||||||
drawer: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Action.Drawer',
|
|
||||||
'x-component-props': {
|
|
||||||
className: 'nb-action-popup',
|
|
||||||
},
|
|
||||||
title: '{{ t("View record") }}',
|
|
||||||
properties: {
|
|
||||||
tabs: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Tabs',
|
|
||||||
'x-component-props': {},
|
|
||||||
'x-initializer': 'TabPaneInitializers',
|
|
||||||
properties: {
|
|
||||||
tab1: {
|
|
||||||
type: 'void',
|
|
||||||
title: '{{t("Details")}}',
|
|
||||||
'x-component': 'Tabs.TabPane',
|
|
||||||
'x-designer': 'Tabs.Designer',
|
|
||||||
'x-component-props': {},
|
|
||||||
properties: {
|
|
||||||
grid: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-initializer': 'popup:common:addBlock',
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
useCreateActionProps as useCAP,
|
useCreateActionProps as useCAP,
|
||||||
useCollectionParentRecordData,
|
useCollectionParentRecordData,
|
||||||
useProps,
|
useProps,
|
||||||
|
withDynamicSchemaProps,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Spin, Tag } from 'antd';
|
import { Spin, Tag } from 'antd';
|
||||||
import React, { useContext, useMemo, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
@ -56,100 +57,105 @@ export const toColumns = (groupField: any, dataSource: Array<any> = [], primaryK
|
|||||||
return Object.values(columns);
|
return Object.values(columns);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Kanban: any = observer(
|
export const Kanban: any = withDynamicSchemaProps(
|
||||||
(props: any) => {
|
observer(
|
||||||
const { styles } = useStyles();
|
(props: any) => {
|
||||||
const { groupField, onCardDragEnd, dataSource, setDataSource, ...restProps } = useProps(props);
|
const { styles } = useStyles();
|
||||||
const parentRecordData = useCollectionParentRecordData();
|
|
||||||
const field = useField<ArrayField>();
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const fieldSchema = useFieldSchema();
|
const { groupField, onCardDragEnd, dataSource, setDataSource, ...restProps } = useProps(props);
|
||||||
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
|
||||||
const schemas = useMemo(
|
const parentRecordData = useCollectionParentRecordData();
|
||||||
() =>
|
const field = useField<ArrayField>();
|
||||||
fieldSchema.reduceProperties(
|
const fieldSchema = useFieldSchema();
|
||||||
(buf, current) => {
|
const [disableCardDrag, setDisableCardDrag] = useState(false);
|
||||||
if (current['x-component'].endsWith('.Card')) {
|
const schemas = useMemo(
|
||||||
buf.card = current;
|
() =>
|
||||||
} else if (current['x-component'].endsWith('.CardAdder')) {
|
fieldSchema.reduceProperties(
|
||||||
buf.cardAdder = current;
|
(buf, current) => {
|
||||||
} else if (current['x-component'].endsWith('.CardViewer')) {
|
if (current['x-component'].endsWith('.Card')) {
|
||||||
buf.cardViewer = current;
|
buf.card = current;
|
||||||
}
|
} else if (current['x-component'].endsWith('.CardAdder')) {
|
||||||
return buf;
|
buf.cardAdder = current;
|
||||||
},
|
} else if (current['x-component'].endsWith('.CardViewer')) {
|
||||||
{ card: null, cardAdder: null, cardViewer: null },
|
buf.cardViewer = current;
|
||||||
),
|
}
|
||||||
[],
|
return buf;
|
||||||
);
|
},
|
||||||
const handleCardRemove = (card, column) => {
|
{ card: null, cardAdder: null, cardViewer: null },
|
||||||
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
),
|
||||||
field.value = updatedBoard.columns;
|
[],
|
||||||
setDataSource(updatedBoard.columns);
|
);
|
||||||
};
|
const handleCardRemove = (card, column) => {
|
||||||
const handleCardDragEnd = (card, fromColumn, toColumn) => {
|
const updatedBoard = Board.removeCard({ columns: field.value }, column, card);
|
||||||
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
field.value = updatedBoard.columns;
|
||||||
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
setDataSource(updatedBoard.columns);
|
||||||
field.value = updatedBoard.columns;
|
};
|
||||||
setDataSource(updatedBoard.columns);
|
const handleCardDragEnd = (card, fromColumn, toColumn) => {
|
||||||
};
|
onCardDragEnd?.({ columns: field.value, groupField }, fromColumn, toColumn);
|
||||||
return (
|
const updatedBoard = Board.moveCard({ columns: field.value }, fromColumn, toColumn);
|
||||||
<Spin wrapperClassName={styles.nbKanban} spinning={field.loading || false}>
|
field.value = updatedBoard.columns;
|
||||||
<Board
|
setDataSource(updatedBoard.columns);
|
||||||
{...restProps}
|
};
|
||||||
allowAddCard={!!schemas.cardAdder}
|
return (
|
||||||
disableColumnDrag
|
<Spin wrapperClassName={styles.nbKanban} spinning={field.loading || false}>
|
||||||
cardAdderPosition={'bottom'}
|
<Board
|
||||||
disableCardDrag={restProps.disableCardDrag || disableCardDrag}
|
{...restProps}
|
||||||
onCardRemove={handleCardRemove}
|
allowAddCard={!!schemas.cardAdder}
|
||||||
onCardDragEnd={handleCardDragEnd}
|
disableColumnDrag
|
||||||
renderColumnHeader={({ title, color }) => (
|
cardAdderPosition={'bottom'}
|
||||||
<div className={'react-kanban-column-header'}>
|
disableCardDrag={restProps.disableCardDrag || disableCardDrag}
|
||||||
<Tag color={color}>{title}</Tag>
|
onCardRemove={handleCardRemove}
|
||||||
</div>
|
onCardDragEnd={handleCardDragEnd}
|
||||||
)}
|
renderColumnHeader={({ title, color }) => (
|
||||||
renderCard={(card, { column, dragging }) => {
|
<div className={'react-kanban-column-header'}>
|
||||||
const columnIndex = dataSource?.indexOf(column);
|
<Tag color={color}>{title}</Tag>
|
||||||
const cardIndex = column?.cards?.indexOf(card);
|
</div>
|
||||||
return (
|
)}
|
||||||
schemas.card && (
|
renderCard={(card, { column, dragging }) => {
|
||||||
<RecordProvider record={card} parent={parentRecordData}>
|
const columnIndex = dataSource?.indexOf(column);
|
||||||
<KanbanCardContext.Provider
|
const cardIndex = column?.cards?.indexOf(card);
|
||||||
value={{
|
return (
|
||||||
setDisableCardDrag,
|
schemas.card && (
|
||||||
cardViewerSchema: schemas.cardViewer,
|
<RecordProvider record={card} parent={parentRecordData}>
|
||||||
cardField: field,
|
<KanbanCardContext.Provider
|
||||||
card,
|
value={{
|
||||||
column,
|
setDisableCardDrag,
|
||||||
dragging,
|
cardViewerSchema: schemas.cardViewer,
|
||||||
columnIndex,
|
cardField: field,
|
||||||
cardIndex,
|
card,
|
||||||
}}
|
column,
|
||||||
>
|
dragging,
|
||||||
<RecursionField name={schemas.card.name} schema={schemas.card} />
|
columnIndex,
|
||||||
</KanbanCardContext.Provider>
|
cardIndex,
|
||||||
</RecordProvider>
|
}}
|
||||||
)
|
>
|
||||||
);
|
<RecursionField name={schemas.card.name} schema={schemas.card} />
|
||||||
}}
|
</KanbanCardContext.Provider>
|
||||||
renderCardAdder={({ column }) => {
|
</RecordProvider>
|
||||||
if (!schemas.cardAdder) {
|
)
|
||||||
return null;
|
);
|
||||||
}
|
}}
|
||||||
return (
|
renderCardAdder={({ column }) => {
|
||||||
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
if (!schemas.cardAdder) {
|
||||||
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
return null;
|
||||||
<RecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
}
|
||||||
</SchemaComponentOptions>
|
return (
|
||||||
</KanbanColumnContext.Provider>
|
<KanbanColumnContext.Provider value={{ column, groupField }}>
|
||||||
);
|
<SchemaComponentOptions scope={{ useCreateActionProps }}>
|
||||||
}}
|
<RecursionField name={schemas.cardAdder.name} schema={schemas.cardAdder} />
|
||||||
>
|
</SchemaComponentOptions>
|
||||||
{{
|
</KanbanColumnContext.Provider>
|
||||||
columns: dataSource || [],
|
);
|
||||||
}}
|
}}
|
||||||
</Board>
|
>
|
||||||
</Spin>
|
{{
|
||||||
);
|
columns: dataSource || [],
|
||||||
},
|
}}
|
||||||
{ displayName: 'Kanban' },
|
</Board>
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ displayName: 'Kanban' },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
useSchemaInitializerItem,
|
useSchemaInitializerItem,
|
||||||
useAPIClient,
|
useAPIClient,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { createKanbanBlockSchema } from './utils';
|
import { createKanbanBlockUISchema } from './createKanbanBlockUISchema';
|
||||||
import { CreateAndSelectSort } from './CreateAndSelectSort';
|
import { CreateAndSelectSort } from './CreateAndSelectSort';
|
||||||
import { NAMESPACE } from './locale';
|
import { NAMESPACE } from './locale';
|
||||||
|
|
||||||
@ -157,14 +157,13 @@ export const KanbanBlockInitializer = () => {
|
|||||||
initialValues: {},
|
initialValues: {},
|
||||||
});
|
});
|
||||||
insert(
|
insert(
|
||||||
createKanbanBlockSchema({
|
createKanbanBlockUISchema({
|
||||||
sortField: values.dragSortBy,
|
sortField: values.dragSortBy,
|
||||||
groupField: values.groupField.value,
|
groupField: values.groupField.value,
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
params: {
|
params: {
|
||||||
sort: [values.dragSortBy],
|
sort: [values.dragSortBy],
|
||||||
paginate: false,
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
import { createKanbanBlockUISchema } from '../createKanbanBlockUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => {
|
||||||
|
return {
|
||||||
|
uid: vi.fn(() => 'mocked-uid'),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createKanbanBlockSchema should return an object with expected properties', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'testCollection',
|
||||||
|
groupField: 'testGroupField',
|
||||||
|
sortField: 'testSortField',
|
||||||
|
dataSource: 'testDataSource',
|
||||||
|
params: { testParam: 'testValue' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = createKanbanBlockUISchema(options);
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": "var(--nb-spacing)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "kanban:configureActions",
|
||||||
|
},
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"card": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-component-props": {
|
||||||
|
"dndContext": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Kanban.Card",
|
||||||
|
"x-component-props": {
|
||||||
|
"openMode": "drawer",
|
||||||
|
},
|
||||||
|
"x-decorator": "BlockItem",
|
||||||
|
"x-designer": "Kanban.Card.Designer",
|
||||||
|
"x-label-disabled": true,
|
||||||
|
"x-read-pretty": true,
|
||||||
|
},
|
||||||
|
"cardViewer": {
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "popup:common:addBlock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{t("Details")}}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{ t("View record") }}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Container",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{ t("View") }}",
|
||||||
|
"type": "void",
|
||||||
|
"x-action": "view",
|
||||||
|
"x-component": "Kanban.CardViewer",
|
||||||
|
"x-component-props": {
|
||||||
|
"openMode": "drawer",
|
||||||
|
},
|
||||||
|
"x-designer": "Action.Designer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "array",
|
||||||
|
"x-component": "Kanban",
|
||||||
|
"x-use-component-props": "useKanbanBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "testCollection:list",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "KanbanBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"collection": "testCollection",
|
||||||
|
"dataSource": "testDataSource",
|
||||||
|
"groupField": "testGroupField",
|
||||||
|
"params": {
|
||||||
|
"paginate": false,
|
||||||
|
"testParam": "testValue",
|
||||||
|
},
|
||||||
|
"sortField": "testSortField",
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:kanban",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
@ -1,24 +1,33 @@
|
|||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
export const createKanbanBlockSchema = (options) => {
|
export const createKanbanBlockUISchema = (options: {
|
||||||
const { collection, resource, groupField, sortField, ...others } = options;
|
collectionName: string;
|
||||||
const schema: ISchema = {
|
groupField: string;
|
||||||
|
sortField: string;
|
||||||
|
dataSource: string;
|
||||||
|
params?: Record<string, any>;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, groupField, sortField, dataSource, params } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-acl-action': `${resource || collection}:list`,
|
'x-acl-action': `${collectionName}:list`,
|
||||||
'x-decorator': 'KanbanBlockProvider',
|
'x-decorator': 'KanbanBlockProvider',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
collection: collection,
|
collection: collectionName,
|
||||||
resource: resource || collection,
|
|
||||||
action: 'list',
|
action: 'list',
|
||||||
groupField,
|
groupField,
|
||||||
sortField,
|
sortField,
|
||||||
params: {
|
params: {
|
||||||
paginate: false,
|
paginate: false,
|
||||||
|
...params,
|
||||||
},
|
},
|
||||||
...others,
|
dataSource,
|
||||||
},
|
},
|
||||||
'x-designer': 'Kanban.Designer',
|
// 'x-designer': 'Kanban.Designer',
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:kanban',
|
||||||
'x-component': 'CardItem',
|
'x-component': 'CardItem',
|
||||||
properties: {
|
properties: {
|
||||||
actions: {
|
actions: {
|
||||||
@ -35,9 +44,7 @@ export const createKanbanBlockSchema = (options) => {
|
|||||||
[uid()]: {
|
[uid()]: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
'x-component': 'Kanban',
|
'x-component': 'Kanban',
|
||||||
'x-component-props': {
|
'x-use-component-props': 'useKanbanBlockProps',
|
||||||
useProps: '{{ useKanbanBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
properties: {
|
||||||
card: {
|
card: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -106,5 +113,4 @@ export const createKanbanBlockSchema = (options) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return schema;
|
|
||||||
};
|
};
|
@ -0,0 +1,95 @@
|
|||||||
|
import { createMapBlockUISchema } from '../../block/createMapBlockUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => {
|
||||||
|
return {
|
||||||
|
uid: () => 'mocked-uid',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createMapBlockSchema should return an object with expected properties', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'testCollection',
|
||||||
|
dataSource: 'testDataSource',
|
||||||
|
fieldNames: {
|
||||||
|
label: 'field1',
|
||||||
|
value: 'field2',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = createMapBlockUISchema(options);
|
||||||
|
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"actions": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "map:configureActions",
|
||||||
|
},
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"drawer": {
|
||||||
|
"properties": {
|
||||||
|
"tabs": {
|
||||||
|
"properties": {
|
||||||
|
"tab1": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "popup:common:addBlock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{t("Details")}}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs.TabPane",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-designer": "Tabs.Designer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Tabs",
|
||||||
|
"x-component-props": {},
|
||||||
|
"x-initializer": "TabPaneInitializers",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"title": "{{ t("View record") }}",
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Action.Drawer",
|
||||||
|
"x-component-props": {
|
||||||
|
"className": "nb-action-popup",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "MapBlock",
|
||||||
|
"x-use-component-props": "useMapBlockProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "testCollection:list",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "MapBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"collection": "testCollection",
|
||||||
|
"dataSource": "testDataSource",
|
||||||
|
"fieldNames": {
|
||||||
|
"label": "field1",
|
||||||
|
"value": "field2",
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"paginate": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-filter-targets": [],
|
||||||
|
"x-settings": "blockSettings:map",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
@ -1,9 +1,16 @@
|
|||||||
import { useCollection_deprecated, useCollectionManager_deprecated, useProps } from '@nocobase/client';
|
import {
|
||||||
|
useCollection_deprecated,
|
||||||
|
useCollectionManager_deprecated,
|
||||||
|
useProps,
|
||||||
|
withDynamicSchemaProps,
|
||||||
|
} from '@nocobase/client';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { MapBlockComponent } from '../components';
|
import { MapBlockComponent } from '../components';
|
||||||
|
|
||||||
export const MapBlock = (props) => {
|
export const MapBlock = withDynamicSchemaProps((props) => {
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { fieldNames } = useProps(props);
|
const { fieldNames } = useProps(props);
|
||||||
|
|
||||||
const { getCollectionJoinField } = useCollectionManager_deprecated();
|
const { getCollectionJoinField } = useCollectionManager_deprecated();
|
||||||
const { name } = useCollection_deprecated();
|
const { name } = useCollection_deprecated();
|
||||||
const collectionField = useMemo(() => {
|
const collectionField = useMemo(() => {
|
||||||
@ -12,4 +19,4 @@ export const MapBlock = (props) => {
|
|||||||
|
|
||||||
const fieldComponentProps = collectionField?.uiSchema?.['x-component-props'];
|
const fieldComponentProps = collectionField?.uiSchema?.['x-component-props'];
|
||||||
return <MapBlockComponent {...fieldComponentProps} {...props} collectionField={collectionField} />;
|
return <MapBlockComponent {...fieldComponentProps} {...props} collectionField={collectionField} />;
|
||||||
};
|
});
|
||||||
|
@ -13,7 +13,8 @@ import {
|
|||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { useMapTranslation } from '../locale';
|
import { useMapTranslation } from '../locale';
|
||||||
import { createMapBlockSchema, findNestedOption } from './utils';
|
import { findNestedOption } from './utils';
|
||||||
|
import { createMapBlockUISchema } from './createMapBlockUISchema';
|
||||||
|
|
||||||
export const MapBlockInitializer = () => {
|
export const MapBlockInitializer = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -84,13 +85,12 @@ export const MapBlockInitializer = () => {
|
|||||||
initialValues: {},
|
initialValues: {},
|
||||||
});
|
});
|
||||||
insert(
|
insert(
|
||||||
createMapBlockSchema({
|
createMapBlockUISchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
...values,
|
...values,
|
||||||
},
|
},
|
||||||
settings: 'blockSettings:map',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export const createMapBlockUISchema = (options: {
|
||||||
|
collectionName: string;
|
||||||
|
dataSource: string;
|
||||||
|
fieldNames: object;
|
||||||
|
}): ISchema => {
|
||||||
|
const { collectionName, fieldNames, dataSource } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${collectionName}:list`,
|
||||||
|
'x-decorator': 'MapBlockProvider',
|
||||||
|
'x-decorator-props': {
|
||||||
|
collection: collectionName,
|
||||||
|
dataSource,
|
||||||
|
action: 'list',
|
||||||
|
fieldNames,
|
||||||
|
params: {
|
||||||
|
paginate: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:map',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
// 保存当前筛选区块所能过滤的数据区块
|
||||||
|
'x-filter-targets': [],
|
||||||
|
properties: {
|
||||||
|
actions: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'map:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'MapBlock',
|
||||||
|
'x-use-component-props': 'useMapBlockProps',
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-component-props': {
|
||||||
|
className: 'nb-action-popup',
|
||||||
|
},
|
||||||
|
title: '{{ t("View record") }}',
|
||||||
|
properties: {
|
||||||
|
tabs: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Tabs',
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-initializer': 'TabPaneInitializers',
|
||||||
|
properties: {
|
||||||
|
tab1: {
|
||||||
|
type: 'void',
|
||||||
|
title: '{{t("Details")}}',
|
||||||
|
'x-component': 'Tabs.TabPane',
|
||||||
|
'x-designer': 'Tabs.Designer',
|
||||||
|
'x-component-props': {},
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'popup:common:addBlock',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -1,86 +1,3 @@
|
|||||||
import { ISchema } from '@formily/react';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
|
|
||||||
export const createMapBlockSchema = (options) => {
|
|
||||||
const { collection, resource, fieldNames, settings, ...others } = options;
|
|
||||||
const schema: ISchema = {
|
|
||||||
type: 'void',
|
|
||||||
'x-acl-action': `${resource || collection}:list`,
|
|
||||||
'x-decorator': 'MapBlockProvider',
|
|
||||||
'x-decorator-props': {
|
|
||||||
collection: collection,
|
|
||||||
resource: resource || collection,
|
|
||||||
action: 'list',
|
|
||||||
fieldNames,
|
|
||||||
params: {
|
|
||||||
paginate: false,
|
|
||||||
},
|
|
||||||
...others,
|
|
||||||
},
|
|
||||||
'x-toolbar': 'BlockSchemaToolbar',
|
|
||||||
'x-settings': settings,
|
|
||||||
'x-component': 'CardItem',
|
|
||||||
// 保存当前筛选区块所能过滤的数据区块
|
|
||||||
'x-filter-targets': [],
|
|
||||||
properties: {
|
|
||||||
actions: {
|
|
||||||
type: 'void',
|
|
||||||
'x-initializer': 'map:configureActions',
|
|
||||||
'x-component': 'ActionBar',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
marginBottom: 16,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'MapBlock',
|
|
||||||
'x-component-props': {
|
|
||||||
useProps: '{{ useMapBlockProps }}',
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
drawer: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Action.Drawer',
|
|
||||||
'x-component-props': {
|
|
||||||
className: 'nb-action-popup',
|
|
||||||
},
|
|
||||||
title: '{{ t("View record") }}',
|
|
||||||
properties: {
|
|
||||||
tabs: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Tabs',
|
|
||||||
'x-component-props': {},
|
|
||||||
'x-initializer': 'TabPaneInitializers',
|
|
||||||
properties: {
|
|
||||||
tab1: {
|
|
||||||
type: 'void',
|
|
||||||
title: '{{t("Details")}}',
|
|
||||||
'x-component': 'Tabs.TabPane',
|
|
||||||
'x-designer': 'Tabs.Designer',
|
|
||||||
'x-component-props': {},
|
|
||||||
properties: {
|
|
||||||
grid: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-initializer': 'popup:common:addBlock',
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return schema;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findNestedOption = (value: string[] | string, options = []) => {
|
export const findNestedOption = (value: string[] | string, options = []) => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
value = [value];
|
value = [value];
|
||||||
|
@ -20,6 +20,7 @@ import { getSource } from '../../utils';
|
|||||||
import { AMapComponent, AMapForwardedRefProps } from './Map';
|
import { AMapComponent, AMapForwardedRefProps } from './Map';
|
||||||
|
|
||||||
export const AMapBlock = (props) => {
|
export const AMapBlock = (props) => {
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { collectionField, fieldNames, dataSource, fixedBlock, zoom, setSelectedRecordKeys, lineSort } =
|
const { collectionField, fieldNames, dataSource, fixedBlock, zoom, setSelectedRecordKeys, lineSort } =
|
||||||
useProps(props);
|
useProps(props);
|
||||||
const { name, getPrimaryKey } = useCollection_deprecated();
|
const { name, getPrimaryKey } = useCollection_deprecated();
|
||||||
|
@ -38,6 +38,7 @@ const pointClass = css`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const GoogleMapsBlock = (props) => {
|
export const GoogleMapsBlock = (props) => {
|
||||||
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { collectionField, fieldNames, dataSource, fixedBlock, zoom, setSelectedRecordKeys, lineSort } =
|
const { collectionField, fieldNames, dataSource, fixedBlock, zoom, setSelectedRecordKeys, lineSort } =
|
||||||
useProps(props);
|
useProps(props);
|
||||||
const { getPrimaryKey } = useCollection_deprecated();
|
const { getPrimaryKey } = useCollection_deprecated();
|
||||||
|
@ -6,6 +6,7 @@ import { mapBlockSettings } from './block/MapBlock.Settings';
|
|||||||
import { Configuration, Map } from './components';
|
import { Configuration, Map } from './components';
|
||||||
import { fields } from './fields';
|
import { fields } from './fields';
|
||||||
import { NAMESPACE, generateNTemplate } from './locale';
|
import { NAMESPACE, generateNTemplate } from './locale';
|
||||||
|
import { useMapBlockProps } from './block/MapBlockProvider';
|
||||||
const MapProvider = React.memo((props) => {
|
const MapProvider = React.memo((props) => {
|
||||||
return (
|
return (
|
||||||
<CurrentAppInfoProvider>
|
<CurrentAppInfoProvider>
|
||||||
@ -44,6 +45,10 @@ export class MapPlugin extends Plugin {
|
|||||||
Component: Configuration,
|
Component: Configuration,
|
||||||
aclSnippet: 'pm.map.configuration',
|
aclSnippet: 'pm.map.configuration',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.app.addScopes({
|
||||||
|
useMapBlockProps,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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: {
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import { createFormBlockSchema } from '@nocobase/client';
|
||||||
|
|
||||||
|
export function createManualFormBlockUISchema(options) {
|
||||||
|
return createFormBlockSchema(options);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user