refactor(DataBlock): details block (#3776)
* refactor: remove useless code related 'TableField' * refactor: add details block UI schema and hooks * refactor: update details block schema and create new hook for details block without paging props * refactor: add useDetailsBlockWithoutPagingDecoratorProps and useDetailsBlockWithPagingDecoratorProps * test: add unit tests * chore: fix build * chore: fix import path for withDynamicSchemaProps * fix: fix import typo in createDetailsBlockWithoutPagingUISchema test * chore: fix unit test * refactor: should not get collection on getting association in UISchema * chore: fix unit test * refactor: use x-use-component-props instead of useProps * chore: fix Pagination * chore: skip weird tests * chore: remove weird test * refactor: update details block initializers and associated schemas * refactor: rename to useDetailsWithPaginationProps * refactor: rename to useDetailsWithPaginationDecoratorProps * refactor: rename to createDetailsWithPaginationUISchema * chore: fix unit test * refactor: rename to createDetailsUISchema * refactor: rename to useDetailsDecoratorProps * refactor: rename to useDetailsProps * refactor: rename to 'blockSettings:details' * chore: fix e2e
This commit is contained in:
parent
7566a7b357
commit
2468caeeb9
@ -27,6 +27,10 @@ import { useCollapseBlockDecoratorProps } from '../modules/blocks/filter-blocks/
|
|||||||
import { useFilterFormBlockProps } from '../modules/blocks/filter-blocks/form/hooks/useFilterFormBlockProps';
|
import { useFilterFormBlockProps } from '../modules/blocks/filter-blocks/form/hooks/useFilterFormBlockProps';
|
||||||
import { useFilterFormBlockDecoratorProps } from '../modules/blocks/filter-blocks/form/hooks/useFilterFormBlockDecoratorProps';
|
import { useFilterFormBlockDecoratorProps } from '../modules/blocks/filter-blocks/form/hooks/useFilterFormBlockDecoratorProps';
|
||||||
import { useGridCardBlockDecoratorProps } from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps';
|
import { useGridCardBlockDecoratorProps } from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps';
|
||||||
|
import { useDetailsProps } from '../modules/blocks/data-blocks/details-single/hooks/useDetailsProps';
|
||||||
|
import { useDetailsWithPaginationProps } from '../modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationProps';
|
||||||
|
import { useDetailsDecoratorProps } from '../modules/blocks/data-blocks/details-single/hooks/useDetailsDecoratorProps';
|
||||||
|
import { useDetailsWithPaginationDecoratorProps } from '../modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationDecoratorProps';
|
||||||
|
|
||||||
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
// TODO: delete this, replaced by `BlockSchemaComponentPlugin`
|
||||||
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
export const BlockSchemaComponentProvider: React.FC = (props) => {
|
||||||
@ -54,6 +58,10 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
|
|||||||
useEditFormBlockProps,
|
useEditFormBlockProps,
|
||||||
useFormFieldProps,
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
|
useDetailsProps,
|
||||||
|
useDetailsWithPaginationProps,
|
||||||
|
useDetailsDecoratorProps,
|
||||||
|
useDetailsWithPaginationDecoratorProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
useTableSelectorProps,
|
useTableSelectorProps,
|
||||||
@ -108,6 +116,10 @@ export class BlockSchemaComponentPlugin extends Plugin {
|
|||||||
useEditFormBlockProps,
|
useEditFormBlockProps,
|
||||||
useFormFieldProps,
|
useFormFieldProps,
|
||||||
useDetailsBlockProps,
|
useDetailsBlockProps,
|
||||||
|
useDetailsProps,
|
||||||
|
useDetailsWithPaginationProps,
|
||||||
|
useDetailsDecoratorProps,
|
||||||
|
useDetailsWithPaginationDecoratorProps,
|
||||||
useTableFieldProps,
|
useTableFieldProps,
|
||||||
useTableBlockProps,
|
useTableBlockProps,
|
||||||
useTableSelectorProps,
|
useTableSelectorProps,
|
||||||
|
@ -7,6 +7,7 @@ import { useCollectionParentRecord } from '../data-source/collection-record/Coll
|
|||||||
import { RecordProvider } from '../record-provider';
|
import { RecordProvider } from '../record-provider';
|
||||||
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
import { BlockProvider, useBlockRequestContext } from './BlockProvider';
|
||||||
import { useParsedFilter } from './hooks';
|
import { useParsedFilter } from './hooks';
|
||||||
|
import { withDynamicSchemaProps } from '../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const DetailsBlockContext = createContext<any>({});
|
export const DetailsBlockContext = createContext<any>({});
|
||||||
DetailsBlockContext.displayName = 'DetailsBlockContext';
|
DetailsBlockContext.displayName = 'DetailsBlockContext';
|
||||||
@ -57,13 +58,13 @@ const InternalDetailsBlockProvider = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DetailsBlockProvider = (props) => {
|
export const DetailsBlockProvider = withDynamicSchemaProps((props) => {
|
||||||
return (
|
return (
|
||||||
<BlockProvider name="details" {...props}>
|
<BlockProvider name="details" {...props}>
|
||||||
<InternalDetailsBlockProvider {...props} />
|
<InternalDetailsBlockProvider {...props} />
|
||||||
</BlockProvider>
|
</BlockProvider>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -72,6 +73,11 @@ export const useDetailsBlockContext = () => {
|
|||||||
return useContext(DetailsBlockContext);
|
return useContext(DetailsBlockContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 即将废弃,请用 useDetailsWithPaginationProps 或者 useDetailsProps
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export const useDetailsBlockProps = () => {
|
export const useDetailsBlockProps = () => {
|
||||||
const ctx = useDetailsBlockContext();
|
const ctx = useDetailsBlockContext();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -53,6 +53,10 @@ export const detailsActionInitializers_deprecated = new CompatibleSchemaInitiali
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 已弃用,请使用 readPrettyFormActionInitializers 代替
|
||||||
|
*/
|
||||||
export const detailsActionInitializers = new CompatibleSchemaInitializer(
|
export const detailsActionInitializers = new CompatibleSchemaInitializer(
|
||||||
{
|
{
|
||||||
name: 'detailsWithPaging:configureActions',
|
name: 'detailsWithPaging:configureActions',
|
||||||
|
@ -2,9 +2,9 @@ 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 { useCollectionManager_deprecated } from '../../../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||||
import { createDetailsBlockSchema } from '../../../../schema-initializer/utils';
|
|
||||||
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';
|
||||||
|
import { createDetailsWithPaginationUISchema } from './createDetailsWithPaginationUISchema';
|
||||||
|
|
||||||
export const DetailsBlockInitializer = ({
|
export const DetailsBlockInitializer = ({
|
||||||
filterCollections,
|
filterCollections,
|
||||||
@ -51,15 +51,14 @@ export const DetailsBlockInitializer = ({
|
|||||||
|
|
||||||
const { item } = options;
|
const { item } = options;
|
||||||
const collection = getCollection(item.name, item.dataSource);
|
const collection = getCollection(item.name, item.dataSource);
|
||||||
const schema = createDetailsBlockSchema({
|
const schema = createDetailsWithPaginationUISchema({
|
||||||
collection: item.name,
|
collectionName: item.name,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
actionInitializers:
|
hideActionInitializer: !(
|
||||||
(collection.template !== 'view' || collection?.writableView) &&
|
(collection.template !== 'view' || collection?.writableView) &&
|
||||||
collection.template !== 'sql' &&
|
collection.template !== 'sql'
|
||||||
'detailsWithPaging:configureActions',
|
),
|
||||||
settings: 'blockSettings:multiDataDetails',
|
|
||||||
});
|
});
|
||||||
insert(schema);
|
insert(schema);
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { createDetailsWithPaginationUISchema } from '../createDetailsWithPaginationUISchema';
|
||||||
|
|
||||||
|
vi.mock('@formily/shared', () => ({
|
||||||
|
uid: vi.fn().mockReturnValue('mocked-uid'),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('createDetailsBlockWithPaginationUISchema', () => {
|
||||||
|
it('should return the correct schema', () => {
|
||||||
|
const schema = createDetailsWithPaginationUISchema({
|
||||||
|
collectionName: 'users',
|
||||||
|
dataSource: 'users',
|
||||||
|
rowKey: 'id',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(schema).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {
|
||||||
|
"grid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "details:configureFields",
|
||||||
|
},
|
||||||
|
"mocked-uid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "details:configureActions",
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Pagination",
|
||||||
|
"x-use-component-props": "useDetailsPaginationProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Details",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useDetailsWithPaginationProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "users:view",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "DetailsBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "list",
|
||||||
|
"association": undefined,
|
||||||
|
"collection": "users",
|
||||||
|
"dataSource": "users",
|
||||||
|
"params": {
|
||||||
|
"pageSize": 1,
|
||||||
|
},
|
||||||
|
"readPretty": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:detailsWithPagination",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useDetailsWithPaginationDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,70 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export function createDetailsWithPaginationUISchema(options: {
|
||||||
|
dataSource: string;
|
||||||
|
rowKey: string;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
hideActionInitializer?: boolean;
|
||||||
|
}): ISchema {
|
||||||
|
const { collectionName, dataSource, association, templateSchema, hideActionInitializer, rowKey } = options;
|
||||||
|
const resourceName = association || collectionName;
|
||||||
|
|
||||||
|
if (!dataSource || !rowKey) {
|
||||||
|
throw new Error('dataSource and rowKey are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'void',
|
||||||
|
'x-acl-action': `${resourceName}:view`,
|
||||||
|
'x-decorator': 'DetailsBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useDetailsWithPaginationDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
dataSource,
|
||||||
|
collection: collectionName,
|
||||||
|
association,
|
||||||
|
readPretty: true,
|
||||||
|
action: 'list',
|
||||||
|
params: {
|
||||||
|
pageSize: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:detailsWithPagination',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Details',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useDetailsWithPaginationProps',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': hideActionInitializer ? undefined : 'details:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Pagination',
|
||||||
|
'x-use-component-props': 'useDetailsPaginationProps',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
import { ArrayItems } from '@formily/antd-v5';
|
||||||
|
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
||||||
|
import { useFormBlockContext } from '../../../../block-provider';
|
||||||
|
import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider';
|
||||||
|
import { useCollection_deprecated, useSortFields } from '../../../../collection-manager';
|
||||||
|
import { removeNullCondition, useDesignable } from '../../../../schema-component';
|
||||||
|
import { SchemaSettingsBlockTitleItem, SchemaSettingsTemplate } from '../../../../schema-settings';
|
||||||
|
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
|
||||||
|
import { setDataLoadingModeSettingsItem, useDataLoadingMode } from './setDataLoadingModeSettingsItem';
|
||||||
|
import { SchemaSettingsItemType } from '../../../../application/schema-settings/types';
|
||||||
|
|
||||||
|
const commonItems: SchemaSettingsItemType[] = [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dataScope',
|
||||||
|
Component: SchemaSettingsDataScope,
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const { form } = useFormBlockContext();
|
||||||
|
const field = useField();
|
||||||
|
const { service } = useDetailsBlockContext();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
const dataLoadingMode = useDataLoadingMode();
|
||||||
|
return {
|
||||||
|
collectionName: name,
|
||||||
|
defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {},
|
||||||
|
form,
|
||||||
|
onSubmit: ({ filter }) => {
|
||||||
|
filter = removeNullCondition(filter);
|
||||||
|
const params = field.decoratorProps.params || {};
|
||||||
|
params.filter = filter;
|
||||||
|
field.decoratorProps.params = params;
|
||||||
|
fieldSchema['x-decorator-props']['params'] = params;
|
||||||
|
|
||||||
|
if (dataLoadingMode === 'auto') {
|
||||||
|
service.run({ ...service.params?.[0], filter });
|
||||||
|
}
|
||||||
|
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sortingRules',
|
||||||
|
type: 'modal',
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const field = useField();
|
||||||
|
const { service } = useDetailsBlockContext();
|
||||||
|
const { dn } = useDesignable();
|
||||||
|
const sortFields = useSortFields(name);
|
||||||
|
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
||||||
|
const sort = defaultSort?.map((item: string) => {
|
||||||
|
return item.startsWith('-')
|
||||||
|
? {
|
||||||
|
field: item.substring(1),
|
||||||
|
direction: 'desc',
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
field: item,
|
||||||
|
direction: 'asc',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
title: t('Set default sorting rules'),
|
||||||
|
components: {
|
||||||
|
ArrayItems,
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
title: t('Set default sorting rules'),
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'array',
|
||||||
|
default: sort,
|
||||||
|
'x-component': 'ArrayItems',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
space: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Space',
|
||||||
|
properties: {
|
||||||
|
sort: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayItems.SortHandle',
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
type: 'string',
|
||||||
|
enum: sortFields,
|
||||||
|
required: true,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
width: 260,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: 'string',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Radio.Group',
|
||||||
|
'x-component-props': {
|
||||||
|
optionType: 'button',
|
||||||
|
},
|
||||||
|
enum: [
|
||||||
|
{
|
||||||
|
label: t('ASC'),
|
||||||
|
value: 'asc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('DESC'),
|
||||||
|
value: 'desc',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
type: 'void',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'ArrayItems.Remove',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
add: {
|
||||||
|
type: 'void',
|
||||||
|
title: t('Add sort field'),
|
||||||
|
'x-component': 'ArrayItems.Addition',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ISchema,
|
||||||
|
onSubmit({ sort }) {
|
||||||
|
const sortArr = sort.map((item) => {
|
||||||
|
return item.direction === 'desc' ? `-${item.field}` : item.field;
|
||||||
|
});
|
||||||
|
const params = field.decoratorProps.params || {};
|
||||||
|
params.sort = sortArr;
|
||||||
|
field.decoratorProps.params = params;
|
||||||
|
fieldSchema['x-decorator-props']['params'] = params;
|
||||||
|
dn.emit('patch', {
|
||||||
|
schema: {
|
||||||
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
'x-decorator-props': fieldSchema['x-decorator-props'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
service.run({ ...service.params?.[0], sort: sortArr });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setDataLoadingModeSettingsItem,
|
||||||
|
{
|
||||||
|
name: 'template',
|
||||||
|
Component: SchemaSettingsTemplate,
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const defaultResource =
|
||||||
|
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
||||||
|
return {
|
||||||
|
componentName: 'Details',
|
||||||
|
collectionName: name,
|
||||||
|
resourceName: defaultResource,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
breakRemoveOn: {
|
||||||
|
'x-component': 'Grid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 已弃用,请使用 detailsWithPaginationSettings 代替
|
||||||
|
*/
|
||||||
|
export const multiDataDetailsBlockSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:multiDataDetails',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const detailsWithPaginationSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:detailsWithPagination',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
|
||||||
|
export function useDetailsWithPaginationDecoratorProps(props) {
|
||||||
|
let sourceId;
|
||||||
|
|
||||||
|
// association 的值是固定不变的,所以可以在条件中使用 hooks
|
||||||
|
if (props.association) {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
sourceId = useDataBlockSourceId({ association: props.association });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sourceId,
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { useDetailsBlockProps } from '../../../../../block-provider/DetailsBlockProvider';
|
||||||
|
|
||||||
|
export function useDetailsWithPaginationProps() {
|
||||||
|
return useDetailsBlockProps();
|
||||||
|
}
|
@ -1,204 +0,0 @@
|
|||||||
import { ArrayItems } from '@formily/antd-v5';
|
|
||||||
import { ISchema, useField, useFieldSchema } from '@formily/react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
|
||||||
import { useFormBlockContext } from '../../../../block-provider';
|
|
||||||
import { useDetailsBlockContext } from '../../../../block-provider/DetailsBlockProvider';
|
|
||||||
import { useCollection_deprecated, useSortFields } from '../../../../collection-manager';
|
|
||||||
import { removeNullCondition, useDesignable } from '../../../../schema-component';
|
|
||||||
import { SchemaSettingsBlockTitleItem, SchemaSettingsTemplate } from '../../../../schema-settings';
|
|
||||||
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
|
|
||||||
import { setDataLoadingModeSettingsItem, useDataLoadingMode } from './setDataLoadingModeSettingsItem';
|
|
||||||
|
|
||||||
export const multiDataDetailsBlockSettings = new SchemaSettings({
|
|
||||||
name: 'blockSettings:multiDataDetails',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
Component: SchemaSettingsBlockTitleItem,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'dataScope',
|
|
||||||
Component: SchemaSettingsDataScope,
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const { form } = useFormBlockContext();
|
|
||||||
const field = useField();
|
|
||||||
const { service } = useDetailsBlockContext();
|
|
||||||
const { dn } = useDesignable();
|
|
||||||
const dataLoadingMode = useDataLoadingMode();
|
|
||||||
return {
|
|
||||||
collectionName: name,
|
|
||||||
defaultFilter: fieldSchema?.['x-decorator-props']?.params?.filter || {},
|
|
||||||
form,
|
|
||||||
onSubmit: ({ filter }) => {
|
|
||||||
filter = removeNullCondition(filter);
|
|
||||||
const params = field.decoratorProps.params || {};
|
|
||||||
params.filter = filter;
|
|
||||||
field.decoratorProps.params = params;
|
|
||||||
fieldSchema['x-decorator-props']['params'] = params;
|
|
||||||
|
|
||||||
if (dataLoadingMode === 'auto') {
|
|
||||||
service.run({ ...service.params?.[0], filter });
|
|
||||||
}
|
|
||||||
|
|
||||||
dn.emit('patch', {
|
|
||||||
schema: {
|
|
||||||
['x-uid']: fieldSchema['x-uid'],
|
|
||||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'sortingRules',
|
|
||||||
type: 'modal',
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const field = useField();
|
|
||||||
const { service } = useDetailsBlockContext();
|
|
||||||
const { dn } = useDesignable();
|
|
||||||
const sortFields = useSortFields(name);
|
|
||||||
const defaultSort = fieldSchema?.['x-decorator-props']?.params?.sort || [];
|
|
||||||
const sort = defaultSort?.map((item: string) => {
|
|
||||||
return item.startsWith('-')
|
|
||||||
? {
|
|
||||||
field: item.substring(1),
|
|
||||||
direction: 'desc',
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
field: item,
|
|
||||||
direction: 'asc',
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
title: t('Set default sorting rules'),
|
|
||||||
components: {
|
|
||||||
ArrayItems,
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
type: 'object',
|
|
||||||
title: t('Set default sorting rules'),
|
|
||||||
properties: {
|
|
||||||
sort: {
|
|
||||||
type: 'array',
|
|
||||||
default: sort,
|
|
||||||
'x-component': 'ArrayItems',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
space: {
|
|
||||||
type: 'void',
|
|
||||||
'x-component': 'Space',
|
|
||||||
properties: {
|
|
||||||
sort: {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'ArrayItems.SortHandle',
|
|
||||||
},
|
|
||||||
field: {
|
|
||||||
type: 'string',
|
|
||||||
enum: sortFields,
|
|
||||||
required: true,
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Select',
|
|
||||||
'x-component-props': {
|
|
||||||
style: {
|
|
||||||
width: 260,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
direction: {
|
|
||||||
type: 'string',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Radio.Group',
|
|
||||||
'x-component-props': {
|
|
||||||
optionType: 'button',
|
|
||||||
},
|
|
||||||
enum: [
|
|
||||||
{
|
|
||||||
label: t('ASC'),
|
|
||||||
value: 'asc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('DESC'),
|
|
||||||
value: 'desc',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
remove: {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'ArrayItems.Remove',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
add: {
|
|
||||||
type: 'void',
|
|
||||||
title: t('Add sort field'),
|
|
||||||
'x-component': 'ArrayItems.Addition',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as ISchema,
|
|
||||||
onSubmit({ sort }) {
|
|
||||||
const sortArr = sort.map((item) => {
|
|
||||||
return item.direction === 'desc' ? `-${item.field}` : item.field;
|
|
||||||
});
|
|
||||||
const params = field.decoratorProps.params || {};
|
|
||||||
params.sort = sortArr;
|
|
||||||
field.decoratorProps.params = params;
|
|
||||||
fieldSchema['x-decorator-props']['params'] = params;
|
|
||||||
dn.emit('patch', {
|
|
||||||
schema: {
|
|
||||||
['x-uid']: fieldSchema['x-uid'],
|
|
||||||
'x-decorator-props': fieldSchema['x-decorator-props'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
service.run({ ...service.params?.[0], sort: sortArr });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setDataLoadingModeSettingsItem,
|
|
||||||
{
|
|
||||||
name: 'template',
|
|
||||||
Component: SchemaSettingsTemplate,
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const defaultResource =
|
|
||||||
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
|
||||||
return {
|
|
||||||
componentName: 'Details',
|
|
||||||
collectionName: name,
|
|
||||||
resourceName: defaultResource,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'divider',
|
|
||||||
type: 'divider',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'remove',
|
|
||||||
type: 'remove',
|
|
||||||
componentProps: {
|
|
||||||
removeParentsIfNoChildren: true,
|
|
||||||
breakRemoveOn: {
|
|
||||||
'x-component': 'Grid',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
@ -47,6 +47,7 @@ const AssociatedFields = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
|
* 已弃用,请使用 readPrettyFormItemInitializers 代替
|
||||||
*/
|
*/
|
||||||
export const readPrettyFormItemInitializers_deprecated = new CompatibleSchemaInitializer({
|
export const readPrettyFormItemInitializers_deprecated = new CompatibleSchemaInitializer({
|
||||||
name: 'ReadPrettyFormItemInitializers',
|
name: 'ReadPrettyFormItemInitializers',
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { FormOutlined } from '@ant-design/icons';
|
import { FormOutlined } from '@ant-design/icons';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||||
import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider';
|
import { useBlockAssociationContext } from '../../../../block-provider';
|
||||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||||
import { useSchemaTemplateManager } from '../../../../schema-templates';
|
import { useSchemaTemplateManager } from '../../../../schema-templates';
|
||||||
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
|
import { useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
|
||||||
|
import { createDetailsUISchema } from './createDetailsUISchema';
|
||||||
|
|
||||||
export const RecordReadPrettyFormBlockInitializer = () => {
|
export const RecordReadPrettyFormBlockInitializer = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -24,29 +25,27 @@ export const RecordReadPrettyFormBlockInitializer = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function useCreateSingleDetailsSchema() {
|
export function useCreateSingleDetailsSchema() {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||||
const association = useBlockAssociationContext();
|
const association = useBlockAssociationContext();
|
||||||
const { block } = useBlockRequestContext();
|
|
||||||
const actionInitializers =
|
|
||||||
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
|
|
||||||
|
|
||||||
const templateWrap = useCallback(
|
const templateWrap = useCallback(
|
||||||
(templateSchema, options) => {
|
(templateSchema, options) => {
|
||||||
const { item } = options;
|
const { item } = options;
|
||||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||||
const blockSchema = createDetailsBlockSchema({
|
const blockSchema = createDetailsUISchema(
|
||||||
actionInitializers,
|
association
|
||||||
association,
|
? {
|
||||||
collection: item.collectionName || item.name,
|
association,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
action: 'get',
|
templateSchema: templateSchema,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
}
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
: {
|
||||||
template: templateSchema,
|
collectionName: item.collectionName || item.name,
|
||||||
settings: 'blockSettings:singleDataDetails',
|
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;
|
||||||
}
|
}
|
||||||
@ -55,7 +54,7 @@ export function useCreateSingleDetailsSchema() {
|
|||||||
return templateSchema;
|
return templateSchema;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actionInitializers, association],
|
[association],
|
||||||
);
|
);
|
||||||
|
|
||||||
const createSingleDetailsSchema = useCallback(
|
const createSingleDetailsSchema = useCallback(
|
||||||
@ -65,20 +64,21 @@ export function useCreateSingleDetailsSchema() {
|
|||||||
insert(templateWrap(template, { item }));
|
insert(templateWrap(template, { item }));
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsUISchema(
|
||||||
actionInitializers,
|
association
|
||||||
association,
|
? {
|
||||||
collection: item.collectionName || item.name,
|
association,
|
||||||
dataSource: item.dataSource,
|
dataSource: item.dataSource,
|
||||||
action: 'get',
|
}
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
: {
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
collectionName: item.collectionName || item.name,
|
||||||
settings: 'blockSettings:singleDataDetails',
|
dataSource: item.dataSource,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actionInitializers, association, getTemplateSchemaByMode, insert, templateWrap],
|
[association, getTemplateSchemaByMode, insert, templateWrap],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { createSingleDetailsSchema, templateWrap };
|
return { createSingleDetailsSchema, templateWrap };
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
import { createDetailsUISchema } from '../createDetailsUISchema';
|
||||||
|
|
||||||
|
// Mock uid to always return a fixed value
|
||||||
|
vi.mock('@formily/shared', () => ({
|
||||||
|
uid: () => 'fixed-uid',
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('createDetailsBlockWithoutPagingUISchema', () => {
|
||||||
|
it('should create a valid schema with mandatory fields', () => {
|
||||||
|
const options = {
|
||||||
|
collectionName: 'users',
|
||||||
|
dataSource: 'usersDataSource',
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = createDetailsUISchema(options);
|
||||||
|
expect(result).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"fixed-uid": {
|
||||||
|
"properties": {
|
||||||
|
"fixed-uid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "ActionBar",
|
||||||
|
"x-component-props": {
|
||||||
|
"style": {
|
||||||
|
"marginBottom": 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"x-initializer": "details:configureActions",
|
||||||
|
},
|
||||||
|
"grid": {
|
||||||
|
"properties": {},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Grid",
|
||||||
|
"x-initializer": "details:configureFields",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-component": "Details",
|
||||||
|
"x-read-pretty": true,
|
||||||
|
"x-use-component-props": "useDetailsProps",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": "void",
|
||||||
|
"x-acl-action": "users:get",
|
||||||
|
"x-component": "CardItem",
|
||||||
|
"x-decorator": "DetailsBlockProvider",
|
||||||
|
"x-decorator-props": {
|
||||||
|
"action": "get",
|
||||||
|
"association": undefined,
|
||||||
|
"collection": "users",
|
||||||
|
"dataSource": "usersDataSource",
|
||||||
|
"readPretty": true,
|
||||||
|
},
|
||||||
|
"x-settings": "blockSettings:details",
|
||||||
|
"x-toolbar": "BlockSchemaToolbar",
|
||||||
|
"x-use-decorator-props": "useDetailsDecoratorProps",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,60 @@
|
|||||||
|
import { ISchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
|
export function createDetailsUISchema(options: {
|
||||||
|
dataSource: string;
|
||||||
|
collectionName?: string;
|
||||||
|
association?: string;
|
||||||
|
templateSchema?: ISchema;
|
||||||
|
}): 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': `${resourceName}:get`,
|
||||||
|
'x-decorator': 'DetailsBlockProvider',
|
||||||
|
'x-use-decorator-props': 'useDetailsDecoratorProps',
|
||||||
|
'x-decorator-props': {
|
||||||
|
dataSource,
|
||||||
|
collection: collectionName,
|
||||||
|
association,
|
||||||
|
readPretty: true,
|
||||||
|
action: 'get',
|
||||||
|
},
|
||||||
|
'x-toolbar': 'BlockSchemaToolbar',
|
||||||
|
'x-settings': 'blockSettings:details',
|
||||||
|
'x-component': 'CardItem',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Details',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-use-component-props': 'useDetailsProps',
|
||||||
|
properties: {
|
||||||
|
[uid()]: {
|
||||||
|
type: 'void',
|
||||||
|
'x-initializer': 'details:configureActions',
|
||||||
|
'x-component': 'ActionBar',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
grid: templateSchema || {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'Grid',
|
||||||
|
'x-initializer': 'details:configureFields',
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
import { useFieldSchema } from '@formily/react';
|
||||||
|
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
||||||
|
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||||
|
import { SchemaSettingsBlockTitleItem, SchemaSettingsFormItemTemplate } from '../../../../schema-settings';
|
||||||
|
import { SchemaSettingsItemType } from '../../../../application/schema-settings/types';
|
||||||
|
|
||||||
|
const commonItems: SchemaSettingsItemType[] = [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
Component: SchemaSettingsBlockTitleItem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'formItemTemplate',
|
||||||
|
Component: SchemaSettingsFormItemTemplate,
|
||||||
|
useComponentProps() {
|
||||||
|
const { name } = useCollection_deprecated();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const defaultResource =
|
||||||
|
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
||||||
|
return {
|
||||||
|
insertAdjacentPosition: 'beforeEnd',
|
||||||
|
componentName: 'ReadPrettyFormItem',
|
||||||
|
collectionName: name,
|
||||||
|
resourceName: defaultResource,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'divider',
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'remove',
|
||||||
|
type: 'remove',
|
||||||
|
componentProps: {
|
||||||
|
removeParentsIfNoChildren: true,
|
||||||
|
breakRemoveOn: {
|
||||||
|
'x-component': 'Grid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* 已弃用,请使用 detailsBlockSettings 代替
|
||||||
|
*/
|
||||||
|
export const singleDataDetailsBlockSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:singleDataDetails',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const detailsBlockSettings = new SchemaSettings({
|
||||||
|
name: 'blockSettings:details',
|
||||||
|
items: commonItems,
|
||||||
|
});
|
@ -0,0 +1,18 @@
|
|||||||
|
import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider';
|
||||||
|
import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId';
|
||||||
|
|
||||||
|
export function useDetailsDecoratorProps(props) {
|
||||||
|
const params = useParamsFromRecord();
|
||||||
|
let sourceId;
|
||||||
|
|
||||||
|
// 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,5 @@
|
|||||||
|
import { useDetailsBlockProps } from '../../../../../block-provider/DetailsBlockProvider';
|
||||||
|
|
||||||
|
export function useDetailsProps() {
|
||||||
|
return useDetailsBlockProps();
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
import { useFieldSchema } from '@formily/react';
|
|
||||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
|
||||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
|
||||||
import { SchemaSettingsBlockTitleItem, SchemaSettingsFormItemTemplate } from '../../../../schema-settings';
|
|
||||||
|
|
||||||
export const singleDataDetailsBlockSettings = new SchemaSettings({
|
|
||||||
name: 'blockSettings:singleDataDetails',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
Component: SchemaSettingsBlockTitleItem,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'formItemTemplate',
|
|
||||||
Component: SchemaSettingsFormItemTemplate,
|
|
||||||
useComponentProps() {
|
|
||||||
const { name } = useCollection_deprecated();
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const defaultResource =
|
|
||||||
fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association;
|
|
||||||
return {
|
|
||||||
insertAdjacentPosition: 'beforeEnd',
|
|
||||||
componentName: 'ReadPrettyFormItem',
|
|
||||||
collectionName: name,
|
|
||||||
resourceName: defaultResource,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'divider',
|
|
||||||
type: 'divider',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'remove',
|
|
||||||
type: 'remove',
|
|
||||||
componentProps: {
|
|
||||||
removeParentsIfNoChildren: true,
|
|
||||||
breakRemoveOn: {
|
|
||||||
'x-component': 'Grid',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
@ -88,7 +88,7 @@ test.describe('where to open a popup and what can be added to it', () => {
|
|||||||
// 删除已创建的 blocks,腾出页面空间
|
// 删除已创建的 blocks,腾出页面空间
|
||||||
// delete details block
|
// delete details block
|
||||||
await page.getByText('GeneralConfigure actionsConfigure fields').hover();
|
await page.getByText('GeneralConfigure actionsConfigure fields').hover();
|
||||||
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:singleDataDetails-general').hover();
|
await page.getByLabel('designer-schema-settings-CardItem-blockSettings:details-general').hover();
|
||||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||||
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
await page.getByRole('button', { name: 'OK', exact: true }).click();
|
||||||
// delete form block
|
// delete form block
|
||||||
|
@ -3,8 +3,9 @@ import { FormV2 } from '../form-v2';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Empty } from 'antd';
|
import { Empty } from 'antd';
|
||||||
import { useDataBlockRequest } from '../../../data-source';
|
import { useDataBlockRequest } from '../../../data-source';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const Details = (props) => {
|
export const Details = withDynamicSchemaProps((props) => {
|
||||||
const request = useDataBlockRequest();
|
const request = useDataBlockRequest();
|
||||||
|
|
||||||
if (!request?.loading && _.isEmpty(request?.data?.data)) {
|
if (!request?.loading && _.isEmpty(request?.data?.data)) {
|
||||||
@ -12,4 +13,4 @@ export const Details = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <FormV2 {...props} />;
|
return <FormV2 {...props} />;
|
||||||
};
|
});
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { render } from '@nocobase/test/client';
|
|
||||||
import React from 'react';
|
|
||||||
import { Pagination } from '../index';
|
|
||||||
|
|
||||||
describe('Pagination', () => {
|
|
||||||
it('should render correctly', () => {
|
|
||||||
render(<Pagination />);
|
|
||||||
expect(document.querySelector('.ant-pagination')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('hidden', () => {
|
|
||||||
render(<Pagination hidden />);
|
|
||||||
expect(document.querySelector('.ant-pagination')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
@ -2,24 +2,29 @@ import { observer } from '@formily/react';
|
|||||||
import { Pagination as AntdPagination } from 'antd';
|
import { Pagination as AntdPagination } from 'antd';
|
||||||
import React, { KeyboardEventHandler } from 'react';
|
import React, { KeyboardEventHandler } from 'react';
|
||||||
import { useProps } from '../../hooks/useProps';
|
import { useProps } from '../../hooks/useProps';
|
||||||
|
import { withDynamicSchemaProps } from '../../../application/hoc/withDynamicSchemaProps';
|
||||||
|
|
||||||
export const Pagination = observer(
|
export const Pagination = withDynamicSchemaProps(
|
||||||
(props: any) => {
|
observer(
|
||||||
const { hidden, ...others } = useProps(props);
|
(props: any) => {
|
||||||
if (hidden) {
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
return null;
|
const { hidden, ...others } = useProps(props);
|
||||||
}
|
|
||||||
const onKeypress: KeyboardEventHandler<HTMLDivElement> = (e) => {
|
if (hidden) {
|
||||||
if (e.key === 'Enter') {
|
return null;
|
||||||
e.preventDefault();
|
|
||||||
}
|
}
|
||||||
};
|
const onKeypress: KeyboardEventHandler<HTMLDivElement> = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onKeyPress={onKeypress}>
|
<div onKeyPress={onKeypress}>
|
||||||
<AntdPagination {...others} />
|
<AntdPagination {...others} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ displayName: 'Pagination' },
|
{ displayName: 'Pagination' },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -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 { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||||
|
import { createDetailsWithPaginationUISchema } from '../../modules/blocks/data-blocks/details-multi/createDetailsWithPaginationUISchema';
|
||||||
|
|
||||||
export const RecordAssociationDetailsBlockInitializer = () => {
|
export const RecordAssociationDetailsBlockInitializer = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -25,13 +26,10 @@ export const RecordAssociationDetailsBlockInitializer = () => {
|
|||||||
insert(s);
|
insert(s);
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsWithPaginationUISchema({
|
||||||
collection: field.target,
|
|
||||||
resource,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: resource,
|
association: resource,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
settings: 'blockSettings:multiDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -51,12 +49,10 @@ export function useCreateAssociationDetailsBlock() {
|
|||||||
const collection = getCollection(field.target);
|
const collection = getCollection(field.target);
|
||||||
|
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsWithPaginationUISchema({
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
rowKey: collection.filterTargetKey || 'id',
|
rowKey: collection.filterTargetKey || 'id',
|
||||||
settings: 'blockSettings:multiDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -2,10 +2,10 @@ import { FormOutlined } from '@ant-design/icons';
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
|
||||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
|
||||||
import { useBlockRequestContext } from '../../block-provider';
|
|
||||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||||
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
|
import { useRecordCollectionDataSourceItems } from '../utils';
|
||||||
import { useCollectionManager_deprecated } from '../../collection-manager';
|
import { useCollectionManager_deprecated } from '../../collection-manager';
|
||||||
|
import { createDetailsUISchema } from '../../modules/blocks/data-blocks/details-single/createDetailsUISchema';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -22,8 +22,6 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
const collection = getCollection(collectionName);
|
const collection = getCollection(collectionName);
|
||||||
|
|
||||||
const resource = `${field.collectionName}.${field.name}`;
|
const resource = `${field.collectionName}.${field.name}`;
|
||||||
const { block } = useBlockRequestContext();
|
|
||||||
const actionInitializers = block !== 'TableField' ? 'details:configureActions' : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaInitializerItem
|
<SchemaInitializerItem
|
||||||
@ -33,17 +31,10 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
if (item.template) {
|
if (item.template) {
|
||||||
const s = await getTemplateSchemaByMode(item);
|
const s = await getTemplateSchemaByMode(item);
|
||||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||||
const blockSchema = createDetailsBlockSchema({
|
const blockSchema = createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: collectionName,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
resource,
|
|
||||||
association: resource,
|
association: resource,
|
||||||
action: 'get',
|
templateSchema: s,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
template: s,
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
});
|
});
|
||||||
if (item.mode === 'reference') {
|
if (item.mode === 'reference') {
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
@ -54,16 +45,9 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: collectionName,
|
|
||||||
resource,
|
|
||||||
association: resource,
|
association: resource,
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
action: 'get',
|
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -76,8 +60,6 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
|
|||||||
export function useCreateAssociationDetailsWithoutPagination() {
|
export function useCreateAssociationDetailsWithoutPagination() {
|
||||||
const { insert } = useSchemaInitializer();
|
const { insert } = useSchemaInitializer();
|
||||||
const { getCollection } = useCollectionManager_deprecated();
|
const { getCollection } = useCollectionManager_deprecated();
|
||||||
const { block } = useBlockRequestContext();
|
|
||||||
const actionInitializers = block !== 'TableField' ? 'details:configureActions' : null;
|
|
||||||
|
|
||||||
const createAssociationDetailsWithoutPagination = useCallback(
|
const createAssociationDetailsWithoutPagination = useCallback(
|
||||||
({ item }) => {
|
({ item }) => {
|
||||||
@ -85,19 +67,13 @@ export function useCreateAssociationDetailsWithoutPagination() {
|
|||||||
const collection = getCollection(field.target);
|
const collection = getCollection(field.target);
|
||||||
|
|
||||||
insert(
|
insert(
|
||||||
createDetailsBlockSchema({
|
createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
action: 'get',
|
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[actionInitializers, getCollection, insert],
|
[getCollection, insert],
|
||||||
);
|
);
|
||||||
|
|
||||||
const templateWrap = useCallback(
|
const templateWrap = useCallback(
|
||||||
@ -106,16 +82,10 @@ export function useCreateAssociationDetailsWithoutPagination() {
|
|||||||
const collection = getCollection(field.target);
|
const collection = getCollection(field.target);
|
||||||
|
|
||||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||||
const blockSchema = createDetailsBlockSchema({
|
const blockSchema = createDetailsUISchema({
|
||||||
actionInitializers,
|
|
||||||
collection: field.target,
|
|
||||||
dataSource: collection.dataSource,
|
dataSource: collection.dataSource,
|
||||||
association: `${field.collectionName}.${field.name}`,
|
association: `${field.collectionName}.${field.name}`,
|
||||||
action: 'get',
|
templateSchema: templateSchema,
|
||||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
|
||||||
useParams: '{{ useParamsFromRecord }}',
|
|
||||||
template: templateSchema,
|
|
||||||
settings: 'blockSettings:singleDataDetails',
|
|
||||||
});
|
});
|
||||||
if (item.mode === 'reference') {
|
if (item.mode === 'reference') {
|
||||||
blockSchema['x-template-key'] = item.template.key;
|
blockSchema['x-template-key'] = item.template.key;
|
||||||
@ -125,7 +95,7 @@ export function useCreateAssociationDetailsWithoutPagination() {
|
|||||||
return templateSchema;
|
return templateSchema;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actionInitializers, getCollection],
|
[getCollection],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { createAssociationDetailsWithoutPagination, templateWrap };
|
return { createAssociationDetailsWithoutPagination, templateWrap };
|
||||||
|
@ -893,7 +893,18 @@ export const useCollectionDataSourceItems = ({
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createDetailsBlockSchema = (options) => {
|
export const createDetailsBlockSchema = (options: {
|
||||||
|
collection: string;
|
||||||
|
dataSource: string;
|
||||||
|
rowKey?: string;
|
||||||
|
formItemInitializers?: string;
|
||||||
|
actionInitializers?: string;
|
||||||
|
association?: string;
|
||||||
|
template?: any;
|
||||||
|
settings?: string;
|
||||||
|
action?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}) => {
|
||||||
const {
|
const {
|
||||||
formItemInitializers = 'details:configureFields',
|
formItemInitializers = 'details:configureFields',
|
||||||
actionInitializers = 'detailsWithPaging:configureActions',
|
actionInitializers = 'detailsWithPaging:configureActions',
|
||||||
|
@ -15,8 +15,14 @@ import { customizeUpdateRecordActionSettings } from '../modules/actions/update-r
|
|||||||
import { customizePopupActionSettings } from '../modules/actions/view-edit-popup/customizePopupActionSettings';
|
import { customizePopupActionSettings } from '../modules/actions/view-edit-popup/customizePopupActionSettings';
|
||||||
import { editActionSettings } from '../modules/actions/view-edit-popup/editActionSettings';
|
import { editActionSettings } from '../modules/actions/view-edit-popup/editActionSettings';
|
||||||
import { viewActionSettings } from '../modules/actions/view-edit-popup/viewActionSettings';
|
import { viewActionSettings } from '../modules/actions/view-edit-popup/viewActionSettings';
|
||||||
import { multiDataDetailsBlockSettings } from '../modules/blocks/data-blocks/details-multi/multiDataDetailsBlockSettings';
|
import {
|
||||||
import { singleDataDetailsBlockSettings } from '../modules/blocks/data-blocks/details-single/singleDataDetailsBlockSettings';
|
detailsWithPaginationSettings,
|
||||||
|
multiDataDetailsBlockSettings,
|
||||||
|
} from '../modules/blocks/data-blocks/details-multi/detailsWithPaginationSettings';
|
||||||
|
import {
|
||||||
|
detailsBlockSettings,
|
||||||
|
singleDataDetailsBlockSettings,
|
||||||
|
} from '../modules/blocks/data-blocks/details-single/detailsBlockSettings';
|
||||||
import {
|
import {
|
||||||
createFormBlockSettings,
|
createFormBlockSettings,
|
||||||
creationFormBlockSettings,
|
creationFormBlockSettings,
|
||||||
@ -56,7 +62,9 @@ export class SchemaSettingsPlugin extends Plugin {
|
|||||||
this.schemaSettingsManager.add(filterFormBlockSettings);
|
this.schemaSettingsManager.add(filterFormBlockSettings);
|
||||||
this.schemaSettingsManager.add(filterFormItemFieldSettings);
|
this.schemaSettingsManager.add(filterFormItemFieldSettings);
|
||||||
this.schemaSettingsManager.add(multiDataDetailsBlockSettings);
|
this.schemaSettingsManager.add(multiDataDetailsBlockSettings);
|
||||||
|
this.schemaSettingsManager.add(detailsWithPaginationSettings);
|
||||||
this.schemaSettingsManager.add(singleDataDetailsBlockSettings);
|
this.schemaSettingsManager.add(singleDataDetailsBlockSettings);
|
||||||
|
this.schemaSettingsManager.add(detailsBlockSettings);
|
||||||
this.schemaSettingsManager.add(tableSelectorBlockSettings);
|
this.schemaSettingsManager.add(tableSelectorBlockSettings);
|
||||||
this.schemaSettingsManager.add(listBlockSettings);
|
this.schemaSettingsManager.add(listBlockSettings);
|
||||||
this.schemaSettingsManager.add(gridCardBlockSettings);
|
this.schemaSettingsManager.add(gridCardBlockSettings);
|
||||||
|
Loading…
Reference in New Issue
Block a user