refactor: add blocks in a unified way (#3668)

* refactor: rename filterMenuItemChildren to filterCollections

* refactor: revert parameters to destructured object

* refactor(Details): unify details block between page and popup

* refactor(Form): refactor(Details): unify form block between page and popup

* refactor: table & list & grid card

* refactor: form

* refactor: association details

* chore: useVisible

* refactor: calendar

* chore: remove relationshipBlocks

* refactor: unify Details schema

* fix: avoid error

* chore: add translate

* chore: add tag to association fields

* fix: add block useChildren and children merge

* test: fix e2e

* test: fix e2e

* fix: fix Calendar

* fix: fix search

* test: add e2e for search

* fix: fix T-3554

* fix: fix T-3551

* feat: add no data for details block

* chore: fix unit test

* chore: fix unit test

* chore: change text

* chore: optimize text

* chore: add translation

* style: fix memu item style

* refactor: rename

* test: fix e2e

* test: fix e2e

---------

Co-authored-by: dream2023 <1098626505@qq.com>
This commit is contained in:
Zeke Zhang 2024-03-14 19:20:02 +08:00 committed by GitHub
parent ba1e44c527
commit 286af35ff8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 1511 additions and 573 deletions

View File

@ -49,7 +49,10 @@ export const SchemaInitializerChild: FC<SchemaInitializerItemType> = memo((props
const componentVal = Component || component;
const isBuiltType = !componentVal && type && typeComponentMap[type];
const componentChildren = useChildrenRes || children;
const componentChildren = useMemo(() => {
const res = [...(useChildrenRes || []), ...(children || [])];
return res.length === 0 ? undefined : res;
}, [useChildrenRes, children]);
const contextValue = useMemo(() => {
return {
...others,

View File

@ -50,6 +50,9 @@ export const SchemaInitializerMenu: FC<MenuProps> = (props) => {
.ant-menu-sub {
max-height: 50vh !important;
}
.ant-menu-item {
margin-block: 0;
}
.ant-menu-root {
margin: 0 -${token.margin}px;
.ant-menu-submenu-title {

View File

@ -41,6 +41,7 @@ export function useGetSchemaInitializerMenuItems(onClick?: (args: any) => void)
return {
key: item.key,
label: element,
associationField: item.associationField,
};
}
if (item.type === 'itemGroup') {
@ -73,6 +74,7 @@ export function useGetSchemaInitializerMenuItems(onClick?: (args: any) => void)
return {
key,
label,
associationField: item.associationField,
onClick: (info) => {
if (info.key !== key) return;
if (item.onClick) {

View File

@ -84,7 +84,10 @@ export const SchemaSettingsChild: FC<SchemaSettingsItemType> = memo((props) => {
const useChildrenRes = useChildren();
const useComponentPropsRes = useComponentProps();
const findComponent = useFindComponent();
const componentChildren = useChildrenRes || children;
const componentChildren = useMemo(() => {
const res = [...(useChildrenRes || []), ...(children || [])];
return res.length === 0 ? undefined : res;
}, [useChildrenRes, children]);
const visibleResult = useVisible();
const ComponentValue = useMemo(() => {
return !Component && type && typeComponentMap[type] ? typeComponentMap[type] : Component;

View File

@ -346,7 +346,7 @@ export const useParamsFromRecord = () => {
const obj = {
filterByTk: filterByTk,
};
if (record.__collection && !['oho', 'm2o', 'obo'].includes(collectionField?.interface)) {
if (record.__collection && collectionField && !['oho', 'm2o', 'obo'].includes(collectionField.interface)) {
obj['targetCollection'] = record.__collection;
}
if (!filterByTk) {

View File

@ -19,11 +19,11 @@ const InternalDetailsBlockProvider = (props) => {
createForm({
readPretty,
}),
[],
[readPretty],
);
const { resource, service } = useBlockRequestContext();
const parentRecord = useCollectionParentRecord();
const currentRecord = service?.data?.data?.[0] || {};
const currentRecord = (action === 'list' ? service?.data?.data?.[0] : service?.data?.data) || {};
const detailsBLockValue = useMemo(() => {
return {
action,
@ -65,6 +65,9 @@ export const DetailsBlockProvider = (props) => {
);
};
/**
* @deprecated
*/
export const useDetailsBlockContext = () => {
return useContext(DetailsBlockContext);
};
@ -73,14 +76,15 @@ export const useDetailsBlockProps = () => {
const ctx = useDetailsBlockContext();
useEffect(() => {
if (!ctx.service.loading) {
const data = ctx.action === 'list' ? ctx.service?.data?.data?.[0] : ctx.service?.data?.data;
ctx.form
.reset()
.then(() => {
ctx.form.setValues(ctx.service?.data?.data?.[0] || {});
ctx.form.setValues(data || {});
})
.catch(console.error);
}
}, [ctx.form, ctx.service?.data?.data, ctx.service.loading]);
}, [ctx.action, ctx.form, ctx.service?.data?.data, ctx.service.loading]);
return {
form: ctx.form,
};

View File

@ -133,9 +133,5 @@ export const BlockRequestProvider: FC = ({ children }) => {
export const useDataBlockRequest = <T extends {}>(): UseRequestResult<{ data: T }> => {
const context = useContext(BlockRequestContext);
if (!context) {
throw new Error('useDataBlockRequest() must be used within a DataBlockRequestProvider');
}
return context;
};

View File

@ -358,6 +358,10 @@
"Add tab": "Add tab",
"Disable tabs": "Disable tabs",
"Details": "Details",
"Edit form": "Edit form",
"Create form": "Create form",
"Form (Edit)": "Form (Edit)",
"Form (Add new)": "Form (Add new)",
"Edit tab": "Edit tab",
"Relationship blocks": "Relationship blocks",
"Select record": "Select record",

View File

@ -331,6 +331,10 @@
"Add tab": "Añadir pestaña",
"Disable tabs": "Desactivar pestañas",
"Details": "Detalles",
"Edit form": "Editar formulario",
"Create form": "Crear formulario",
"Form (Edit)": "Formulario (Editar)",
"Form (Add new)": "Formulario (Añadir nuevo)",
"Edit tab": "Editar pestaña",
"Relationship blocks": "Bloques de relación",
"Select record": "Seleccionar registro",

View File

@ -346,6 +346,10 @@
"Add tab": "Ajouter un onglet",
"Disable tabs": "Désactiver les onglets",
"Details": "Détails",
"Edit form": "Modifier le formulaire",
"Create form": "Créer un formulaire",
"Form (Edit)": "Formulaire (Modifier)",
"Form (Add new)": "Formulaire (Ajouter nouveau)",
"Edit tab": "Modifier l'onglet",
"Relationship blocks": "Blocs de relations",
"Select record": "Sélectionner un enregistrement",

View File

@ -293,6 +293,10 @@
"Add tab": "タブを追加",
"Disable tabs": "タブを無効にする",
"Details": "詳細",
"Edit form": "フォームを編集",
"Create form": "フォームを作成",
"Form (Edit)": "フォーム (編集)",
"Form (Add new)": "フォーム (新規追加)",
"Edit tab": "タブを編集",
"Relationship blocks": "関連付けされたブロック",
"Select record": "レコードを選択",

View File

@ -378,6 +378,10 @@
"Add tab": "탭 추가",
"Disable tabs": "탭 비활성화",
"Details": "세부 정보",
"Edit form": "폼 편집",
"Create form": "폼 생성",
"Form (Edit)": "폼 (편집)",
"Form (Add new)": "폼 (새로 추가)",
"Edit tab": "탭 편집",
"Relationship blocks": "관계 데이터 블록",
"Select record": "레코드 선택",

View File

@ -295,6 +295,10 @@
"Add tab": "Adicionar guia",
"Disable tabs": "Desativar guias",
"Details": "Detalhes",
"Edit form": "Editar formulário",
"Create form": "Criar formulário",
"Form (Edit)": "Formulário (Editar)",
"Form (Add new)": "Formulário (Adicionar novo)",
"Edit tab": "Editar guia",
"Relationship blocks": "Blocos de relacionamento",
"Select record": "Selecionar registro",

View File

@ -233,6 +233,10 @@
"Add tab": "Добавить вкладку",
"Disable tabs": "Запретить вкладки",
"Details": "Подробности",
"Edit form": "Изменить форму",
"Create form": "Создать форму",
"Form (Edit)": "Форма (Изменить)",
"Form (Add new)": "Форма (Добавить новый)",
"Edit tab": "Изменить вкладку",
"Relationship blocks": "Блоки отношений",
"Select record": "Выбрать запись",

View File

@ -233,6 +233,10 @@
"Add tab": "Sekme ekle",
"Disable tabs": "Sekmeleri pasifleştir",
"Details": "Detaylar",
"Edit form": "Formu düzenle",
"Create form": "Form oluştur",
"Form (Edit)": "Form (Düzenle)",
"Form (Add new)": "Form (Yeni ekle)",
"Edit tab": "Sekme düzenle",
"Relationship blocks": "İlişki blokları",
"Select record": "Kayıt seç",

View File

@ -348,6 +348,10 @@
"Add tab": "Додати вкладку",
"Disable tabs": "Вимкнути вкладки",
"Details": "Деталі",
"Edit form": "Редагувати форму",
"Create form": "Створити форму",
"Form (Edit)": "Форма (Редагувати)",
"Form (Add new)": "Форма (Додати новий)",
"Edit tab": "Редагувати вкладку",
"Relationship blocks": "Блоки відношень",
"Select record": "Вибрати запис",

View File

@ -379,6 +379,10 @@
"Add tab": "添加标签页",
"Disable tabs": "禁用标签页",
"Details": "详情",
"Edit form": "编辑表单",
"Create form": "创建表单",
"Form (Edit)": "表单(编辑)",
"Form (Add new)": "表单(添加)",
"Edit tab": "编辑标签页",
"Relationship blocks": "关系数据区块",
"Select record": "选择数据",

View File

@ -378,6 +378,10 @@
"Add tab": "新增標籤頁",
"Disable tabs": "停用標籤頁",
"Details": "詳情",
"Edit form": "編輯表單",
"Create form": "建立表單",
"Form (Edit)": "表單(編輯)",
"Form (Add new)": "表單(添加)",
"Edit tab": "編輯標籤頁",
"Relationship blocks": "關聯資料區塊",
"Select record": "選擇資料",

View File

@ -4,8 +4,36 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createDetailsBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
export const DetailsBlockInitializer = () => {
export const DetailsBlockInitializer = ({
filterCollections,
onlyCurrentDataSource,
hideSearch,
componentType = 'Details',
createBlockSchema,
templateWrap,
showAssociationFields,
}: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
hideSearch?: boolean;
/**
* Details ReadPrettyFormItem
* template
*/
componentType?: 'Details' | 'ReadPrettyFormItem';
createBlockSchema?: (options: any) => any;
templateWrap?: (
templateSchema: any,
{
item,
}: {
item: any;
},
) => any;
showAssociationFields?: boolean;
}) => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem();
@ -13,8 +41,13 @@ export const DetailsBlockInitializer = () => {
<DataBlockInitializer
{...itemConfig}
icon={<TableOutlined />}
componentType={'Details'}
onCreateBlockSchema={async ({ item }) => {
componentType={componentType}
onCreateBlockSchema={async (options) => {
if (createBlockSchema) {
return createBlockSchema(options);
}
const { item } = options;
const collection = getCollection(item.name, item.dataSource);
const schema = createDetailsBlockSchema({
collection: item.name,
@ -28,6 +61,11 @@ export const DetailsBlockInitializer = () => {
});
insert(schema);
}}
onlyCurrentDataSource={!!onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
templateWrap={templateWrap}
showAssociationFields={showAssociationFields}
/>
);
};

View File

@ -10,8 +10,9 @@ test.describe('where multi data details block can be added', () => {
});
test.describe('configure fields', () => {
test('display collection fields & display association fields & add text', async ({ page, mockPage }) => {
test('display collection fields & display association fields & add text', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyDetailsBlock).goto();
await mockRecord('general');
const formItemInitializer = page.getByLabel('schema-initializer-Grid-details:configureFields-general');
@ -60,8 +61,9 @@ test.describe('configure fields', () => {
});
test.describe('configure actions', () => {
test('edit & delete & duplicate', async ({ page, mockPage }) => {
test('edit & delete & duplicate', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyDetailsBlock).goto();
await mockRecord('general');
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click();

View File

@ -29,8 +29,9 @@ test.describe('multi data details block schema settings', () => {
});
test.describe('actions schema settings', () => {
test('edit & delete & duplicate', async ({ page, mockPage }) => {
test('edit & delete & duplicate', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyDetailsBlock).goto();
await mockRecord('general');
// 创建 Edit & Delete 两个按钮
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();

View File

@ -1,75 +1,85 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager';
import { useSchemaTemplateManager } from '../../../../schema-templates';
import {
createReadPrettyFormBlockSchema,
useRecordCollectionDataSourceItems,
} from '../../../../schema-initializer/utils';
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
export const RecordReadPrettyFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem();
const {
onCreateBlockSchema,
componentType,
createBlockSchema,
icon = true,
targetCollection,
...others
} = itemConfig;
const { insert } = useSchemaInitializer();
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const { icon = true, targetCollection, ...others } = itemConfig;
const currentCollection = useCollection_deprecated();
const collection = targetCollection || currentCollection;
const association = useBlockAssociationContext();
const { block } = useBlockRequestContext();
const actionInitializers =
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
const { createSingleDetailsSchema } = useCreateSingleDetailsSchema();
return (
<SchemaInitializerItem
icon={icon && <FormOutlined />}
{...others}
onClick={async ({ item }) => {
if (item.template) {
const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createReadPrettyFormBlockSchema({
actionInitializers,
association,
collection: collection.name,
dataSource: collection.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
template: s,
settings: 'blockSettings:singleDataDetails',
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
insert(blockSchema);
} else {
insert(s);
}
} else {
insert(
createReadPrettyFormBlockSchema({
actionInitializers,
association,
collection: collection.name,
dataSource: collection.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
settings: 'blockSettings:singleDataDetails',
}),
);
}
}}
onClick={(options) => createSingleDetailsSchema(options)}
items={useRecordCollectionDataSourceItems('ReadPrettyFormItem', null, collection?.name)}
/>
);
};
export function useCreateSingleDetailsSchema() {
const itemConfig = useSchemaInitializerItem();
const { insert } = useSchemaInitializer();
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const association = useBlockAssociationContext();
const { block } = useBlockRequestContext();
const actionInitializers =
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
const templateWrap = useCallback(
(templateSchema, options) => {
const { item } = options;
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createDetailsBlockSchema({
actionInitializers,
association,
collection: item.collectionName || item.name,
dataSource: item.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
template: templateSchema,
settings: 'blockSettings:singleDataDetails',
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
return blockSchema;
} else {
return templateSchema;
}
},
[actionInitializers, association],
);
const createSingleDetailsSchema = useCallback(
async ({ item }) => {
if (item.template) {
const template = await getTemplateSchemaByMode(item);
insert(templateWrap(template, { item }));
} else {
insert(
createDetailsBlockSchema({
actionInitializers,
association,
collection: item.collectionName || item.name,
dataSource: item.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
settings: 'blockSettings:singleDataDetails',
}),
);
}
},
[actionInitializers, association, getTemplateSchemaByMode, insert, templateWrap],
);
return { createSingleDetailsSchema, templateWrap };
}

View File

@ -8,10 +8,11 @@ test.describe('where single data details block can be added', () => {
await page.getByLabel('action-Action.Link-View-view-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.getByRole('menuitem', { name: 'Details' }).hover();
await page.getByRole('menuitem', { name: 'General' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-general-form')).toBeVisible();
await expect(page.getByLabel('block-item-CardItem-general-details')).toBeVisible();
});
});
@ -23,38 +24,39 @@ test.describe('configure actions', () => {
await page.getByLabel('action-Action.Link-View-view-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.getByRole('menuitem', { name: 'Details' }).hover();
await page.getByRole('menuitem', { name: 'General' }).click();
await page.mouse.move(300, 0);
// create edit ------------------------------------------------------------------------------------
await createAction(page, 'Edit');
await expect(page.getByLabel('action-Action-Edit-update-general-form-0')).toBeVisible();
await expect(page.getByLabel('action-Action-Edit-update-')).toBeVisible();
await expectSettingsMenu({
page,
showMenu: async () => {
await page.getByLabel('action-Action-Edit-update-general-form-0').hover();
await page.getByLabel('action-Action-Edit-update-').hover();
await page.getByRole('button', { name: 'designer-schema-settings-Action-actionSettings:edit-general' }).hover();
},
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'],
});
await deleteAction(page, 'action-Action-Edit-update-general-form-0');
await deleteAction(page, 'action-Action-Edit-update-');
// create delete ------------------------------------------------------------------------------------
await createAction(page, 'Delete');
await expect(page.getByLabel('action-Action-Delete-destroy-general-form-0')).toBeVisible();
await expect(page.getByLabel('action-Action-Delete-destroy-general-details-')).toBeVisible();
// create print
await createAction(page, 'Print');
await expect(page.getByLabel('action-Action-Print-print-general-form-0')).toBeVisible();
await expect(page.getByLabel('action-Action-Print-print-')).toBeVisible();
// create customize actions ----------------------------------------------------------------------------
// Popup
await createCustomAction(page, 'Popup');
await expect(page.getByLabel('action-Action-Popup-customize:popup-general-form-0')).toBeVisible();
await expect(page.getByLabel('action-Action-Popup-customize')).toBeVisible();
// Update record
await createCustomAction(page, 'Update record');
await expect(page.getByLabel('action-Action-Update record-customize:update-general-form-0')).toBeVisible();
await expect(page.getByLabel('action-Action-Update record-')).toBeVisible();
});
});

View File

@ -1,19 +1,69 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import { useSchemaInitializerItem } from '../../../../application';
import React, { useCallback } from 'react';
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';
export const FormBlockInitializer = () => {
export const FormBlockInitializer = ({
filterCollections,
onlyCurrentDataSource,
hideSearch,
createBlockSchema,
componentType = 'FormItem',
templateWrap,
showAssociationFields,
}: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
hideSearch?: boolean;
createBlockSchema?: (options: any) => any;
/**
* template
*/
componentType?: 'FormItem';
templateWrap?: (
templateSchema: any,
{
item,
}: {
item: any;
},
) => any;
showAssociationFields?: boolean;
}) => {
const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem();
const { isCusomeizeCreate } = itemConfig;
const onCreateFormBlockSchema = useCallback(
({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
insert(
createFormBlockSchema({
collection: item.collectionName || item.name,
dataSource: item.dataSource,
isCusomeizeCreate,
settings: 'blockSettings:createForm',
}),
);
},
[createBlockSchema, insert, isCusomeizeCreate],
);
return (
<DataBlockInitializer
{...itemConfig}
icon={<FormOutlined />}
componentType={'FormItem'}
componentType={componentType}
templateWrap={(templateSchema, { item }) => {
const s = createFormBlockSchema({
if (templateWrap) {
return templateWrap(templateSchema, { item });
}
const schema = createFormBlockSchema({
isCusomeizeCreate,
dataSource: item.dataSource,
template: templateSchema,
@ -21,11 +71,15 @@ export const FormBlockInitializer = () => {
settings: 'blockSettings:createForm',
});
if (item.template && item.mode === 'reference') {
s['x-template-key'] = item.template.key;
schema['x-template-key'] = item.template.key;
}
return s;
return schema;
}}
createBlockSchema={createFormBlockSchema}
onCreateBlockSchema={onCreateFormBlockSchema}
filter={filterCollections}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
showAssociationFields={showAssociationFields}
/>
);
};

View File

@ -1,61 +1,87 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager';
import { useSchemaTemplateManager } from '../../../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
/**
* @deprecated
*/
export const RecordFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, targetCollection, ...others } = itemConfig;
const { targetCollection, ...others } = itemConfig;
const { insert } = useSchemaInitializer();
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const currentCollection = useCollection_deprecated();
const collection = targetCollection || currentCollection;
const association = useBlockAssociationContext();
const { createEditFormBlock, templateWrap } = useCreateEditFormBlock();
return (
<SchemaInitializerItem
icon={<FormOutlined />}
{...others}
onClick={async ({ item }) => {
if (item.template) {
const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({
association,
collection: collection.name,
dataSource: collection.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers: 'editForm:configureActions',
template: s,
settings: 'blockSettings:editForm',
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
insert(blockSchema);
} else {
insert(s);
}
const template = await getTemplateSchemaByMode(item);
insert(templateWrap(template, { item }));
} else {
insert(
createFormBlockSchema({
association,
collection: collection.name,
dataSource: collection.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers: 'editForm:configureActions',
settings: 'blockSettings:editForm',
}),
);
createEditFormBlock({ item });
}
}}
items={useRecordCollectionDataSourceItems('FormItem', null, collection?.name)}
/>
);
};
export function useCreateEditFormBlock() {
const { insert } = useSchemaInitializer();
const association = useBlockAssociationContext();
const createEditFormBlock = useCallback(
({ item }) => {
insert(
createFormBlockSchema({
association,
collection: item.collectionName || item.name,
dataSource: item.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers: 'editForm:configureActions',
settings: 'blockSettings:editForm',
}),
);
},
[association, insert],
);
const templateWrap = useCallback(
(templateSchema, { item }) => {
if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({
association,
collection: item.collectionName || item.name,
dataSource: item.dataSource,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers: 'editForm:configureActions',
template: templateSchema,
settings: 'blockSettings:editForm',
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
return blockSchema;
} else {
return templateSchema;
}
},
[association],
);
return { createEditFormBlock, templateWrap };
}

View File

@ -1183,7 +1183,7 @@ test.describe('creation form block schema settings', () => {
//在编辑操作中使用引用模板
await page.getByLabel('action-Action.Link-Edit-update-users-table-0').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-users').click();
await page.getByRole('menuitem', { name: 'form Form' }).first().hover();
await page.getByRole('menuitem', { name: 'form Form (Edit)' }).first().hover();
await page.getByRole('menuitem', { name: 'Reference template' }).hover();
await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).first().click();
await page.mouse.move(300, 0);

View File

@ -5499,7 +5499,7 @@ export const T3469: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-initializer': 'page:addBlock',
'x-index': 1,
properties: {
isaxpp1w32b: {
@ -5551,7 +5551,7 @@ export const T3469: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-initializer': 'form:configureFields',
'x-index': 1,
properties: {
e4ke3wqxm2o: {
@ -5710,7 +5710,7 @@ export const T3529: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-initializer': 'page:addBlock',
properties: {
l62athlwjbe: {
_isJSONSchemaObject: true,
@ -5752,7 +5752,7 @@ export const T3529: PageConfig = {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'TableActionInitializers',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
@ -5767,7 +5767,7 @@ export const T3529: PageConfig = {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'TableColumnInitializers',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-component-props': {
rowKey: 'id',
@ -5786,7 +5786,7 @@ export const T3529: PageConfig = {
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'TableActionColumnInitializers',
'x-initializer': 'table:configureItemActions',
properties: {
'7glkmum3znh': {
_isJSONSchemaObject: true,
@ -5847,7 +5847,7 @@ export const T3529: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'RecordBlockInitializers',
'x-initializer': 'popup:common:addBlock',
properties: {
'5ubuvb82lbu': {
_isJSONSchemaObject: true,
@ -5898,7 +5898,7 @@ export const T3529: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-initializer': 'form:configureFields',
'x-uid': 'aigmsbzdyeb',
'x-async': false,
'x-index': 1,
@ -5907,7 +5907,7 @@ export const T3529: PageConfig = {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'CreateFormActionInitializers',
'x-initializer': 'createForm:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',

View File

@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createGridCardBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
export const GridCardBlockInitializer = () => {
export const GridCardBlockInitializer = ({
filterCollections,
onlyCurrentDataSource,
hideSearch,
createBlockSchema,
componentType = 'FormItem',
templateWrap,
showAssociationFields,
}: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
hideSearch?: boolean;
createBlockSchema?: (options: any) => any;
/**
* template
*/
componentType?: 'FormItem';
templateWrap?: (
templateSchema: any,
{
item,
}: {
item: any;
},
) => any;
showAssociationFields?: boolean;
}) => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem();
@ -15,6 +42,10 @@ export const GridCardBlockInitializer = () => {
icon={<OrderedListOutlined />}
componentType={'GridCard'}
onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const collection = getCollection(item.name, item.dataSource);
const schema = createGridCardBlockSchema({
collection: item.name,
@ -24,6 +55,10 @@ export const GridCardBlockInitializer = () => {
});
insert(schema);
}}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
showAssociationFields={showAssociationFields}
/>
);
};

View File

@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createListBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
export const ListBlockInitializer = () => {
export const ListBlockInitializer = ({
filterCollections,
onlyCurrentDataSource,
hideSearch,
createBlockSchema,
componentType = 'FormItem',
templateWrap,
showAssociationFields,
}: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
hideSearch?: boolean;
createBlockSchema?: (options: any) => any;
/**
* template
*/
componentType?: 'FormItem';
templateWrap?: (
templateSchema: any,
{
item,
}: {
item: any;
},
) => any;
showAssociationFields?: boolean;
}) => {
const { getCollection } = useCollectionManager_deprecated();
const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem();
@ -15,6 +42,10 @@ export const ListBlockInitializer = () => {
icon={<OrderedListOutlined />}
componentType={'List'}
onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const collection = getCollection(item.name, item.dataSource);
const schema = createListBlockSchema({
collection: item.name,
@ -24,6 +55,10 @@ export const ListBlockInitializer = () => {
});
insert(schema);
}}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
showAssociationFields={showAssociationFields}
/>
);
};

View File

@ -4,8 +4,30 @@ import { useCollectionManager_deprecated } from '../../../../collection-manager/
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createTableBlockSchema } from '../../../../schema-initializer/utils';
import React from 'react';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
export const TableBlockInitializer = () => {
export const TableBlockInitializer = ({
filterCollections,
onlyCurrentDataSource,
hideSearch,
createBlockSchema,
templateWrap,
showAssociationFields,
}: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
hideSearch?: boolean;
createBlockSchema?: (options: any) => any;
templateWrap?: (
templateSchema: any,
{
item,
}: {
item: any;
},
) => any;
showAssociationFields?: boolean;
}) => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem();
@ -15,6 +37,10 @@ export const TableBlockInitializer = () => {
icon={<TableOutlined />}
componentType={'Table'}
onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const collection = getCollection(item.name, item.dataSource);
const schema = createTableBlockSchema({
collection: item.name,
@ -23,6 +49,10 @@ export const TableBlockInitializer = () => {
});
insert(schema);
}}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
showAssociationFields={showAssociationFields}
/>
);
};

View File

@ -4,14 +4,16 @@ import React from 'react';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { createCollapseBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection } from '../../../../data-source';
import { Collection, CollectionFieldOptions } from '../../../../data-source';
export const FilterCollapseBlockInitializer = ({
filterMenuItemChildren,
filterCollections,
onlyCurrentDataSource,
showChildren,
}: {
filterMenuItemChildren: (collection: Collection) => boolean;
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
showChildren?: boolean;
}) => {
const itemConfig = useSchemaInitializerItem();
const { insert } = useSchemaInitializer();
@ -31,7 +33,8 @@ export const FilterCollapseBlockInitializer = ({
});
insert(schema);
}}
filter={filterMenuItemChildren}
filter={filterCollections}
showChildren={showChildren}
/>
);
};

View File

@ -1,18 +1,21 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import { useSchemaInitializerItem } from '../../../../application';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { createFilterFormBlockSchema } from '../../../../schema-initializer/utils';
import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer';
import { Collection } from '../../../../data-source';
import { Collection, CollectionFieldOptions } from '../../../../data-source';
export const FilterFormBlockInitializer = ({
filterMenuItemChildren,
filterCollections,
onlyCurrentDataSource,
showChildren,
}: {
filterMenuItemChildren: (collection: Collection) => boolean;
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
showChildren?: boolean;
}) => {
const itemConfig = useSchemaInitializerItem();
const { insert } = useSchemaInitializer();
return (
<FilterBlockInitializer
@ -32,11 +35,17 @@ export const FilterFormBlockInitializer = ({
}
return s;
}}
createBlockSchema={(options) => {
options = { ...options, settings: 'blockSettings:filterForm' };
return createFilterFormBlockSchema(options);
onCreateBlockSchema={({ item }) => {
return insert(
createFilterFormBlockSchema({
collection: item.collectionName || item.name,
dataSource: item.dataSource,
settings: 'blockSettings:filterForm',
}),
);
}}
filter={filterMenuItemChildren}
filter={filterCollections}
showChildren={showChildren}
/>
);
};

View File

@ -77,10 +77,9 @@ test.describe('where to open a popup and what can be added to it', () => {
await expect(page.getByText('test8')).toBeVisible();
// add blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.getByText('Form').first().click();
await page.getByRole('menuitem', { name: 'Markdown' }).click();
await addBlock(['table Details right', 'General']);
await addBlock(['form Form (Edit)']);
await addBlock(['Markdown']);
await expect(page.getByText('GeneralConfigure actionsConfigure fields')).toBeVisible();
await expect(page.getByText('GeneralConfigure fieldsConfigure actions')).toBeVisible();
@ -106,26 +105,18 @@ test.describe('where to open a popup and what can be added to it', () => {
await page.getByRole('button', { name: 'OK', exact: true }).click();
// add relationship blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Many to one' }).hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'One to many' }).hover();
// 下拉列表中,可选择以下区块进行创建
await expect(page.getByText('Table')).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible();
await expect(page.getByText('Form').nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Calendar' })).toBeVisible();
await page.getByText('Table').click();
await page.mouse.move(300, 0);
await addBlock(['table Details right', 'Many to one']);
await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
await addBlock(['table Table right', 'One to many']);
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
async function addBlock(names: string[]) {
await page.getByLabel('schema-initializer-Grid-popup').hover();
for (const name of names) {
await page.getByRole('menuitem', { name }).click();
}
await page.mouse.move(300, 0);
}
});
test('bulk edit', async ({ page, mockPage }) => {
@ -171,8 +162,11 @@ test.describe('where to open a popup and what can be added to it', () => {
// add blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.getByText('Form').first().click();
await page.getByRole('menuitem', { name: 'Details' }).hover();
await page.getByRole('menuitem', { name: 'General' }).click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'form Form (Edit)' }).first().click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Markdown' }).click();
await page.mouse.move(300, 0);
@ -181,27 +175,27 @@ test.describe('where to open a popup and what can be added to it', () => {
await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible();
// add relationship blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Many to one' }).hover();
await page.getByRole('menuitem', { name: 'Details' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-general-').nth(2)).toBeVisible();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'One to many' }).hover();
// 下拉列表中,可选择以下区块进行创建
await expect(page.getByText('Table')).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible();
await expect(page.getByText('Form').nth(1)).toBeVisible();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await expect(page.getByRole('menuitem', { name: 'table Details right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'form Form (Edit)' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'form Form (Add new) right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'form Form (Add new) right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'table Table right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'ordered-list List right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'ordered-list Grid Card right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Calendar' })).toBeVisible();
await page.getByText('Table').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'Details' }).hover();
await page.getByRole('menuitem', { name: 'Many to one' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
await page.getByRole('menuitem', { name: 'One to many' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible();
// 屏幕上没有显示错误提示
await expect(page.locator('.ant-notification-notice').first()).toBeHidden({ timeout: 1000 });

View File

@ -16,7 +16,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-initializer': 'page:addBlock',
properties: {
irc4mhtog83: {
_isJSONSchemaObject: true,
@ -58,7 +58,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'TableActionInitializers',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
@ -73,7 +73,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'TableColumnInitializers',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-component-props': {
rowKey: 'id',
@ -92,7 +92,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = {
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'TableActionColumnInitializers',
'x-initializer': 'table:configureItemActions',
properties: {
gk4b4e4wp8b: {
_isJSONSchemaObject: true,
@ -192,7 +192,7 @@ export const oneFormBlockWithDatetimeFields: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-initializer': 'page:addBlock',
properties: {
annx4g65mtu: {
_isJSONSchemaObject: true,
@ -239,7 +239,7 @@ export const oneFormBlockWithDatetimeFields: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-initializer': 'form:configureFields',
properties: {
'269bdhc35j2': {
_isJSONSchemaObject: true,

View File

@ -35,7 +35,7 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali
title: '{{t("Form")}}',
Component: 'FilterFormBlockInitializer',
componentProps: {
filterMenuItemChildren() {
filterCollections() {
return false;
},
onlyCurrentDataSource: true,
@ -48,7 +48,7 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali
title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer',
componentProps: {
filterMenuItemChildren() {
filterCollections() {
return false;
},
onlyCurrentDataSource: true,
@ -117,7 +117,7 @@ export const tableSelectorInitializers = new CompatibleSchemaInitializer(
title: '{{t("Form")}}',
Component: 'FilterFormBlockInitializer',
componentProps: {
filterMenuItemChildren() {
filterCollections() {
return false;
},
onlyCurrentDataSource: true,
@ -130,7 +130,7 @@ export const tableSelectorInitializers = new CompatibleSchemaInitializer(
title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer',
componentProps: {
filterMenuItemChildren() {
filterCollections() {
return false;
},
onlyCurrentDataSource: true,

View File

@ -0,0 +1,12 @@
import { test, expect } from '@nocobase/test/e2e';
test.describe('page:addBlock', () => {
test('当搜索不到数据时显示空状态', async ({ page, mockPage }) => {
await mockPage().goto();
await page.getByLabel('schema-initializer-Grid-').hover();
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
await page.getByRole('textbox', { name: 'Search and select collection' }).fill('no match');
await expect(page.getByRole('menuitem', { name: 'No data' })).toBeVisible();
});
});

View File

@ -1,3 +1,16 @@
import React from 'react';
import { FormV2 } from '../form-v2';
import { useDetailsBlockContext } from '../../../block-provider/DetailsBlockProvider';
import _ from 'lodash';
import { Empty } from 'antd';
import { useDataBlockRequest } from '../../../data-source';
export const Details = FormV2;
export const Details = (props) => {
const request = useDataBlockRequest();
if (!request?.loading && _.isEmpty(request?.data?.data)) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
}
return <FormV2 {...props} />;
};

View File

@ -6,7 +6,6 @@ describe('Details', () => {
it('should render correctly', () => {
render(<App1 />);
expect(screen.getByText('this is name')).toBeInTheDocument();
expect(screen.getByText('this is age')).toBeInTheDocument();
expect(screen.getByText('No data')).toBeInTheDocument();
});
});

View File

@ -1,9 +1,20 @@
import { Schema, useFieldSchema } from '@formily/react';
import { useMemo } from 'react';
import { useCollectionManager_deprecated, useCollection_deprecated } from '../..';
import { SchemaInitializerItemType, useSchemaInitializer } from '../../application';
import { Schema } from '@formily/react';
import { useCallback, useMemo } from 'react';
import {
useCollection,
useCollectionManager_deprecated,
useCollection_deprecated,
useCreateAssociationDetailsBlock,
useCreateAssociationDetailsWithoutPagination,
useCreateAssociationFormBlock,
useCreateAssociationGridCardBlock,
useCreateAssociationListBlock,
useCreateAssociationTableBlock,
useCreateEditFormBlock,
} from '../..';
import { CompatibleSchemaInitializer } from '../../application/schema-initializer/CompatibleSchemaInitializer';
import { gridRowColWrap } from '../utils';
import { useCreateSingleDetailsSchema } from '../../modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer';
const recursiveParent = (schema: Schema) => {
if (!schema) return null;
@ -15,223 +26,233 @@ const recursiveParent = (schema: Schema) => {
}
};
const useRelationFields = () => {
const fieldSchema = useFieldSchema();
const { getCollectionFields } = useCollectionManager_deprecated();
const collection = useCollection_deprecated();
let fields = [];
if (fieldSchema['x-initializer']) {
fields = collection.fields;
} else {
const collection = recursiveParent(fieldSchema.parent);
if (collection) {
fields = getCollectionFields(collection);
}
}
const relationFields = fields
.filter((field) => ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface))
.map((field) => {
if (['hasOne', 'belongsTo'].includes(field.type)) {
return {
name: field.name,
type: 'subMenu',
title: field?.uiSchema?.title || field.name,
children: [
{
name: `${field.name}_details`,
type: 'item',
title: '{{t("Details")}}',
field,
Component: 'RecordReadPrettyAssociationFormBlockInitializer',
},
// {
// name: `${field.name}_form`,
// type: 'item',
// title: '{{t("Form")}}',
// field,
// component: 'RecordAssociationFormBlockInitializer',
// },
],
};
}
if (['hasMany', 'belongsToMany'].includes(field.type)) {
return {
name: field.name,
type: 'subMenu',
title: field?.uiSchema?.title || field.name,
children: [
{
name: `${field.name}_table`,
type: 'item',
title: '{{t("Table")}}',
field,
Component: 'RecordAssociationBlockInitializer',
},
{
name: `${field.name}_details`,
type: 'item',
title: '{{t("Details")}}',
field,
Component: 'RecordAssociationDetailsBlockInitializer',
},
{
name: `${field.name}_list`,
type: 'item',
title: '{{t("List")}}',
field,
Component: 'RecordAssociationListBlockInitializer',
},
{
name: `${field.name}_grid_card`,
type: 'item',
title: '{{t("Grid Card")}}',
field,
Component: 'RecordAssociationGridCardBlockInitializer',
},
{
name: `${field.name}_form`,
type: 'item',
title: '{{t("Form")}}',
field,
Component: 'RecordAssociationFormBlockInitializer',
},
// TODO: This one should be append in the calendar plugin
{
name: `${field.name}_calendar`,
type: 'item',
title: '{{t("Calendar")}}',
field,
Component: 'RecordAssociationCalendarBlockInitializer',
},
],
};
}
return {
name: field.name,
type: 'item',
field,
title: field?.uiSchema?.title || field.name,
Component: 'RecordAssociationBlockInitializer',
};
}) as any;
return relationFields;
};
const useDetailCollections = (props) => {
const { actionInitializers, childrenCollections, collection } = props;
const detailCollections = [
{
name: collection.name,
type: 'item',
title: collection?.title || collection.name,
Component: 'RecordReadPrettyFormBlockInitializer',
icon: false,
targetCollection: collection,
actionInitializers,
},
].concat(
childrenCollections.map((c) => {
return {
name: c.name,
type: 'item',
title: c?.title || c.name,
Component: 'RecordReadPrettyFormBlockInitializer',
icon: false,
targetCollection: c,
actionInitializers,
};
}),
) as SchemaInitializerItemType[];
return detailCollections;
};
const useFormCollections = (props) => {
const { actionInitializers, childrenCollections, collection } = props;
const formCollections = [
{
name: collection.name,
type: 'item',
title: collection?.title || collection.name,
Component: 'RecordFormBlockInitializer',
icon: false,
targetCollection: collection,
actionInitializers,
},
].concat(
childrenCollections.map((c) => {
return {
name: c.name,
type: 'item',
title: c?.title || c.name,
Component: 'RecordFormBlockInitializer',
icon: false,
targetCollection: c,
actionInitializers,
};
}),
) as SchemaInitializerItemType[];
return formCollections;
export const canMakeAssociationBlock = (field) => {
return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface);
};
function useRecordBlocks() {
const { options } = useSchemaInitializer();
const { actionInitializers } = options;
const collection = useCollection_deprecated();
const { getChildrenCollections } = useCollectionManager_deprecated();
const collectionsWithView = getChildrenCollections(collection.name, true, collection.dataSource).filter(
(v) => v?.filterTargetKey,
);
const hasChildCollection = collectionsWithView?.length > 0;
const modifyFlag = (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql';
const detailChildren = useDetailCollections({
...options,
childrenCollections: collectionsWithView,
collection,
});
const formChildren = useFormCollections({
...options,
childrenCollections: collectionsWithView,
collection,
});
const res = [];
if (hasChildCollection) {
res.push({
name: 'details',
type: 'subMenu',
title: '{{t("Details")}}',
children: detailChildren,
});
} else {
res.push({
const res: any[] = [
{
name: 'details',
title: '{{t("Details")}}',
Component: 'RecordReadPrettyFormBlockInitializer',
actionInitializers,
});
}
Component: 'DetailsBlockInitializer',
collectionName: collection.name,
dataSource: collection.dataSource,
useComponentProps() {
const currentCollection = useCollection_deprecated();
const { createSingleDetailsSchema, templateWrap } = useCreateSingleDetailsSchema();
const { createAssociationDetailsBlock } = useCreateAssociationDetailsBlock();
const {
createAssociationDetailsWithoutPagination,
templateWrap: templateWrapOfAssociationDetailsWithoutPagination,
} = useCreateAssociationDetailsWithoutPagination();
const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView];
const createBlockSchema = useCallback(
({ item }) => {
if (item.associationField) {
if (['hasOne', 'belongsTo'].includes(item.associationField.type)) {
return createAssociationDetailsWithoutPagination({ item });
}
return createAssociationDetailsBlock({ item });
}
return createSingleDetailsSchema({ item });
},
[createAssociationDetailsBlock, createAssociationDetailsWithoutPagination, createSingleDetailsSchema],
);
return {
filterCollections({ collection, associationField }) {
if (collection) {
return collectionsNeedToDisplay.some((c) => c.name === collection.name);
}
if (associationField) {
return true;
}
return false;
},
onlyCurrentDataSource: true,
// hideSearch: true,
componentType: 'ReadPrettyFormItem',
createBlockSchema,
templateWrap: useCallback(
(templateSchema, { item }) => {
if (item.associationField) {
if (['hasOne', 'belongsTo'].includes(item.associationField.type)) {
return templateWrapOfAssociationDetailsWithoutPagination(templateSchema, { item });
}
return templateSchema;
}
return templateWrap(templateSchema, { item });
},
[templateWrap, templateWrapOfAssociationDetailsWithoutPagination],
),
showAssociationFields: true,
};
},
},
{
name: 'editForm',
title: '{{t("Form (Edit)")}}',
Component: 'FormBlockInitializer',
collectionName: collection.name,
dataSource: collection.dataSource,
useComponentProps() {
const currentCollection = useCollection_deprecated();
const { createEditFormBlock, templateWrap } = useCreateEditFormBlock();
const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView];
if (hasChildCollection) {
res.push({
name: 'form',
type: 'subMenu',
title: '{{t("Form")}}',
children: formChildren,
});
} else {
modifyFlag &&
res.push({
name: 'form',
title: '{{t("Form")}}',
Component: 'RecordFormBlockInitializer',
});
}
return {
filterCollections({ collection }) {
if (collection) {
return collectionsNeedToDisplay.some((c) => c.name === collection.name);
}
return false;
},
onlyCurrentDataSource: true,
// hideSearch: true,
componentType: 'FormItem',
createBlockSchema: createEditFormBlock,
templateWrap: templateWrap,
showAssociationFields: true,
};
},
useVisible() {
return (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql';
},
},
{
name: 'createForm',
title: '{{t("Form (Add new)")}}',
Component: 'FormBlockInitializer',
collectionName: collection.name,
dataSource: collection.dataSource,
useComponentProps() {
const { createAssociationFormBlock, templateWrap } = useCreateAssociationFormBlock();
return {
filterCollections({ collection, associationField }) {
if (associationField) {
return ['hasMany', 'belongsToMany'].includes(associationField.type);
}
return false;
},
onlyCurrentDataSource: true,
// hideSearch: true,
componentType: 'FormItem',
createBlockSchema: createAssociationFormBlock,
templateWrap: templateWrap,
showAssociationFields: true,
};
},
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
},
{
name: 'table',
title: '{{t("Table")}}',
Component: 'TableBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() {
const { createAssociationTableBlock } = useCreateAssociationTableBlock();
return {
// hideSearch: true,
onlyCurrentDataSource: true,
filterCollections({ associationField }) {
if (associationField) {
return ['hasMany', 'belongsToMany'].includes(associationField.type);
}
return false;
},
createBlockSchema: createAssociationTableBlock,
showAssociationFields: true,
};
},
},
{
name: 'list',
title: '{{t("List")}}',
Component: 'ListBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() {
const { createAssociationListBlock } = useCreateAssociationListBlock();
return {
// hideSearch: true,
onlyCurrentDataSource: true,
filterCollections({ associationField }) {
if (associationField) {
return ['hasMany', 'belongsToMany'].includes(associationField.type);
}
return false;
},
createBlockSchema: createAssociationListBlock,
showAssociationFields: true,
};
},
},
{
name: 'gridCard',
title: '{{t("Grid Card")}}',
Component: 'GridCardBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() {
const { createAssociationGridCardBlock } = useCreateAssociationGridCardBlock();
return {
// hideSearch: true,
onlyCurrentDataSource: true,
filterCollections({ associationField }) {
if (associationField) {
return ['hasMany', 'belongsToMany'].includes(associationField.type);
}
return false;
},
createBlockSchema: createAssociationGridCardBlock,
showAssociationFields: true,
};
},
},
];
return res;
}
@ -247,8 +268,8 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
items: [
{
type: 'itemGroup',
name: 'currentRecordBlocks',
title: '{{t("Current record blocks")}}',
name: 'dataBlocks',
title: '{{t("Data blocks")}}',
useChildren: useRecordBlocks,
},
{
@ -256,8 +277,14 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
title: '{{t("Filter blocks")}}',
type: 'itemGroup',
useVisible() {
const collection = useCollection_deprecated();
return collection.fields.some((field) => ['hasMany', 'belongsToMany'].includes(field.type));
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
children: [
{
@ -272,10 +299,13 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
);
return {
filterMenuItemChildren(collection) {
return toManyField.some((field) => field.target === collection.name);
filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name);
}
},
onlyCurrentDataSource: true,
showChildren: true,
};
},
},
@ -284,32 +314,35 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer',
useComponentProps() {
const collection = useCollection_deprecated();
const collection = useCollection();
const toManyField = useMemo(
() => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)),
[collection.fields],
);
return {
filterMenuItemChildren(collection) {
return toManyField.some((field) => field.target === collection.name);
filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name);
}
},
onlyCurrentDataSource: true,
showChildren: true,
};
},
},
],
},
{
type: 'itemGroup',
name: 'relationshipBlocks',
title: '{{t("Relationship blocks")}}',
useChildren: useRelationFields,
useVisible() {
const res = useRelationFields();
return res.length > 0;
},
},
// {
// type: 'itemGroup',
// name: 'relationshipBlocks',
// title: '{{t("Relationship blocks")}}',
// useChildren: useRelationFields,
// useVisible() {
// const res = useRelationFields();
// return res.length > 0;
// },
// },
{
type: 'itemGroup',
name: 'otherBlocks',
@ -334,8 +367,8 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
items: [
{
type: 'itemGroup',
name: 'currentRecordBlocks',
title: '{{t("Current record blocks")}}',
name: 'dataBlocks',
title: '{{t("Data blocks")}}',
useChildren: useRecordBlocks,
},
{
@ -343,8 +376,14 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
title: '{{t("Filter blocks")}}',
type: 'itemGroup',
useVisible() {
const collection = useCollection_deprecated();
return collection.fields.some((field) => ['hasMany', 'belongsToMany'].includes(field.type));
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
children: [
{
@ -359,10 +398,13 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
);
return {
filterMenuItemChildren(collection) {
return toManyField.some((field) => field.target === collection.name);
filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name);
}
},
onlyCurrentDataSource: true,
showChildren: true,
};
},
},
@ -371,32 +413,35 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer',
useComponentProps() {
const collection = useCollection_deprecated();
const collection = useCollection();
const toManyField = useMemo(
() => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)),
[collection.fields],
);
return {
filterMenuItemChildren(collection) {
return toManyField.some((field) => field.target === collection.name);
filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name);
}
},
onlyCurrentDataSource: true,
showChildren: true,
};
},
},
],
},
{
type: 'itemGroup',
name: 'relationshipBlocks',
title: '{{t("Relationship blocks")}}',
useChildren: useRelationFields,
useVisible() {
const res = useRelationFields();
return res.length > 0;
},
},
// {
// type: 'itemGroup',
// name: 'relationshipBlocks',
// title: '{{t("Relationship blocks")}}',
// useChildren: useRelationFields,
// useVisible() {
// const res = useRelationFields();
// return res.length > 0;
// },
// },
{
type: 'itemGroup',
name: 'otherBlocks',

View File

@ -124,6 +124,7 @@ export {
createFilterFormBlockSchema,
createFormBlockSchema,
createReadPrettyFormBlockSchema,
createDetailsBlockSchema,
createTableBlockSchema,
gridRowColWrap,
itemsMerge,

View File

@ -8,7 +8,7 @@ import {
useGetSchemaInitializerMenuItems,
useSchemaInitializer,
} from '../../application';
import { Collection } from '../../data-source/collection/Collection';
import { Collection, CollectionFieldOptions } from '../../data-source/collection/Collection';
import { useCompile } from '../../schema-component';
import { useSchemaTemplateManager } from '../../schema-templates';
import { useCollectionDataSourceItems } from '../utils';
@ -109,7 +109,17 @@ const LoadingItem = ({ loadMore, maxHeight }) => {
);
};
export function useMenuSearch(data: any[], openKeys: string[], showType?: boolean) {
export function useMenuSearch({
data,
openKeys,
showType,
hideSearch,
}: {
data: any[];
openKeys: string[];
showType?: boolean;
hideSearch?: boolean;
}) {
const [searchValue, setSearchValue] = useState('');
const [count, setCount] = useState(STEP);
@ -154,28 +164,31 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea
// 最终的返回结果
const resultItems = useMemo<MenuProps['items']>(() => {
// isMenuType 为了 `useSchemaInitializerMenuItems()` 里面处理判断标识的
const res: any[] = [
const res = [];
if (!hideSearch) {
// 开头:搜索框
Object.assign(
{
key: 'search',
label: (
<SearchCollections
value={searchValue}
onChange={(val: string) => {
setCount(STEP);
setSearchValue(val);
}}
/>
),
onClick({ domEvent }) {
domEvent.stopPropagation();
res.push(
Object.assign(
{
key: 'search',
label: (
<SearchCollections
value={searchValue}
onChange={(val: string) => {
setCount(STEP);
setSearchValue(val);
}}
/>
),
onClick({ domEvent }) {
domEvent.stopPropagation();
},
},
},
showType ? { isMenuType: true } : {},
),
];
// isMenuType 为了 `useSchemaInitializerMenuItems()` 里面处理判断标识的
showType ? { isMenuType: true } : {},
),
);
}
// 中间:搜索的数据
if (limitedSearchedItems.length > 0) {
@ -220,7 +233,7 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea
}
return res;
}, [limitedSearchedItems, searchValue, shouldLoadMore, showType]);
}, [hideSearch, limitedSearchedItems, searchValue, shouldLoadMore, showType]);
const res = useMemo(() => {
if (!isMuliSource) return resultItems;
@ -237,7 +250,7 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea
};
}
});
}, [data, openKey, resultItems]);
}, [data, isMuliSource, openKey, resultItems]);
return res;
}
@ -252,13 +265,16 @@ export interface DataBlockInitializerProps {
) => any;
onCreateBlockSchema?: (args: any) => void;
createBlockSchema?: (args: any) => any;
isCusomeizeCreate?: boolean;
icon?: string | React.ReactNode;
name: string;
title: string;
filter?: (collection: Collection) => boolean;
filter?: (options: { collection: Collection; associationField: CollectionFieldOptions }) => boolean;
componentType: string;
onlyCurrentDataSource?: boolean;
hideSearch?: boolean;
showAssociationFields?: boolean;
/** 即使 children 只有一个时,也显示出来 */
showChildren?: boolean;
}
export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
@ -266,13 +282,14 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
templateWrap,
onCreateBlockSchema,
componentType,
createBlockSchema,
isCusomeizeCreate,
icon = TableOutlined,
name,
title,
filter,
onlyCurrentDataSource,
hideSearch,
showAssociationFields,
showChildren,
} = props;
const { insert, setVisible } = useSchemaInitializer();
const compile = useCompile();
@ -285,30 +302,34 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
} else {
if (onCreateBlockSchema) {
onCreateBlockSchema({ item });
} else if (createBlockSchema) {
insert(
createBlockSchema({
collection: item.collectionName || item.name,
dataSource: item.dataSource,
isCusomeizeCreate,
settings: 'blockSettings:createForm',
}),
);
}
}
setVisible(false);
},
[createBlockSchema, getTemplateSchemaByMode, insert, isCusomeizeCreate, onCreateBlockSchema, templateWrap],
[getTemplateSchemaByMode, insert, onCreateBlockSchema, setVisible, templateWrap],
);
const items = useCollectionDataSourceItems(componentType, filter, onlyCurrentDataSource);
const items = useCollectionDataSourceItems({
componentName: componentType,
filter,
onlyCurrentDataSource,
showAssociationFields,
});
const getMenuItems = useGetSchemaInitializerMenuItems(onClick);
const childItems = useMemo(() => {
return getMenuItems(items, name);
}, [getMenuItems, items, name]);
const [openMenuKeys, setOpenMenuKeys] = useState([]);
const searchedChildren = useMenuSearch(childItems, openMenuKeys);
const compiledMenuItems = useMemo(
() => [
const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys, hideSearch });
const compiledMenuItems = useMemo(() => {
let children = searchedChildren.filter((item) => item.key !== 'search' && item.key !== 'empty');
const hasAssociationField = children.some((item) => item.associationField);
if (!showChildren && !hasAssociationField && children.length === 1) {
// 只有一项可选时,直接展开
children = children[0].children;
} else {
children = searchedChildren;
}
return [
{
key: name,
label: compile(title),
@ -317,11 +338,10 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
if (info.key !== name) return;
onClick({ ...info, item: props });
},
children: searchedChildren,
children,
},
],
[name, compile, title, icon, childItems, onClick, props],
);
];
}, [name, compile, title, icon, searchedChildren, onClick, props]);
if (childItems.length > 1 || (childItems.length === 1 && childItems[0].children?.length > 0)) {
return (

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback } from 'react';
import { TableOutlined } from '@ant-design/icons';
import { useCollectionManager_deprecated } from '../../collection-manager';
@ -6,6 +6,9 @@ import { useSchemaTemplateManager } from '../../schema-templates';
import { createTableBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
/**
* @deprecated
*/
export const RecordAssociationBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig;
@ -39,3 +42,27 @@ export const RecordAssociationBlockInitializer = () => {
/>
);
};
export function useCreateAssociationTableBlock() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createAssociationTableBlock = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
insert(
createTableBlockSchema({
rowKey: collection.filterTargetKey,
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
}),
);
},
[getCollection, insert],
);
return { createAssociationTableBlock };
}

View File

@ -1,5 +1,5 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useCollectionManager_deprecated } from '../../collection-manager';
@ -40,3 +40,28 @@ export const RecordAssociationDetailsBlockInitializer = () => {
/>
);
};
export function useCreateAssociationDetailsBlock() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createAssociationDetailsBlock = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
insert(
createDetailsBlockSchema({
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
rowKey: collection.filterTargetKey || 'id',
settings: 'blockSettings:multiDataDetails',
}),
);
},
[getCollection, insert],
);
return { createAssociationDetailsBlock };
}

View File

@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { FormOutlined } from '@ant-design/icons';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
@ -6,6 +6,9 @@ import { useSchemaTemplateManager } from '../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { useCollectionManager_deprecated } from '../../collection-manager';
/**
* @deprecated
*/
export const RecordAssociationFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig;
@ -27,7 +30,7 @@ export const RecordAssociationFormBlockInitializer = () => {
: 'createForm:configureActions';
if (item.template) {
const s = await getTemplateSchemaByMode(item);
const template = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({
collection: collectionName,
@ -38,7 +41,7 @@ export const RecordAssociationFormBlockInitializer = () => {
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
template: s,
template: template,
settings: 'blockSettings:createForm',
});
if (item.mode === 'reference') {
@ -46,7 +49,7 @@ export const RecordAssociationFormBlockInitializer = () => {
}
insert(blockSchema);
} else {
insert(s);
insert(template);
}
} else {
insert(
@ -68,3 +71,67 @@ export const RecordAssociationFormBlockInitializer = () => {
/>
);
};
export function useCreateAssociationFormBlock() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createAssociationFormBlock = useCallback(
({ item }) => {
const field = item.associationField;
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(
createFormBlockSchema({
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
action,
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
settings: 'blockSettings:createForm',
}),
);
},
[getCollection, insert],
);
const templateWrap = useCallback(
(templateSchema, { item }) => {
const field = item.associationField;
const action = ['hasOne', 'belongsTo'].includes(field.type) ? 'get' : null;
const collection = getCollection(field.target);
const actionInitializers = ['hasOne', 'belongsTo'].includes(field.type)
? 'editForm:configureActions'
: 'createForm:configureActions';
if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
action,
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
actionInitializers,
template: templateSchema,
settings: 'blockSettings:createForm',
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
return blockSchema;
} else {
return templateSchema;
}
},
[getCollection],
);
return { createAssociationFormBlock, templateWrap };
}

View File

@ -1,11 +1,14 @@
import { TableOutlined } from '@ant-design/icons';
import React from 'react';
import React, { useCallback } from 'react';
import { useCollectionManager_deprecated } from '../../collection-manager';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useSchemaTemplateManager } from '../../schema-templates';
import { createGridCardBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
/**
* @deprecated
*/
export const RecordAssociationGridCardBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig;
@ -41,3 +44,28 @@ export const RecordAssociationGridCardBlockInitializer = () => {
/>
);
};
export function useCreateAssociationGridCardBlock() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createAssociationGridCardBlock = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
insert(
createGridCardBlockSchema({
rowKey: collection.filterTargetKey,
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
settings: 'blockSettings:gridCard',
}),
);
},
[getCollection, insert],
);
return { createAssociationGridCardBlock };
}

View File

@ -1,5 +1,5 @@
import { TableOutlined } from '@ant-design/icons';
import React from 'react';
import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useCollectionManager_deprecated } from '../../collection-manager';
@ -41,3 +41,28 @@ export const RecordAssociationListBlockInitializer = () => {
/>
);
};
export function useCreateAssociationListBlock() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createAssociationListBlock = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
insert(
createListBlockSchema({
rowKey: collection.filterTargetKey,
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
settings: 'blockSettings:list',
}),
);
},
[getCollection, insert],
);
return { createAssociationListBlock };
}

View File

@ -1,12 +1,15 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useBlockRequestContext } from '../../block-provider';
import { useSchemaTemplateManager } from '../../schema-templates';
import { createReadPrettyFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { useCollectionManager_deprecated } from '../../collection-manager';
/**
* @deprecated
*/
export const RecordReadPrettyAssociationFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig;
@ -30,7 +33,7 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
if (item.template) {
const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createReadPrettyFormBlockSchema({
const blockSchema = createDetailsBlockSchema({
actionInitializers,
collection: collectionName,
dataSource: collection.dataSource,
@ -51,7 +54,7 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
}
} else {
insert(
createReadPrettyFormBlockSchema({
createDetailsBlockSchema({
actionInitializers,
collection: collectionName,
resource,
@ -69,3 +72,61 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
/>
);
};
export function useCreateAssociationDetailsWithoutPagination() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const { block } = useBlockRequestContext();
const actionInitializers = block !== 'TableField' ? 'details:configureActions' : null;
const createAssociationDetailsWithoutPagination = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
insert(
createDetailsBlockSchema({
actionInitializers,
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
settings: 'blockSettings:singleDataDetails',
}),
);
},
[actionInitializers, getCollection, insert],
);
const templateWrap = useCallback(
(templateSchema, { item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createDetailsBlockSchema({
actionInitializers,
collection: field.target,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}',
template: templateSchema,
settings: 'blockSettings:singleDataDetails',
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
return blockSchema;
} else {
return templateSchema;
}
},
[actionInitializers, getCollection],
);
return { createAssociationDetailsWithoutPagination, templateWrap };
}

View File

@ -3,14 +3,21 @@ import { ISchema, Schema, useFieldSchema, useForm } from '@formily/react';
import { uid } from '@formily/shared';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializerItemType, useDataSourceKey, useFormActiveFields, useFormBlockContext } from '../';
import {
SchemaInitializerItemType,
useCollection,
useCollectionManager,
useDataSourceKey,
useFormActiveFields,
useFormBlockContext,
} from '../';
import { FieldOptions, useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager';
import { isAssocField } from '../filter-provider/utils';
import { useActionContext, useDesignable } from '../schema-component';
import { useActionContext, useCompile, useDesignable } from '../schema-component';
import { useSchemaTemplateManager } from '../schema-templates';
import { Collection } from '../data-source/collection/Collection';
import { Collection, CollectionFieldOptions } from '../data-source/collection/Collection';
import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
import { DataSourceManager } from '../data-source/data-source/DataSourceManager';
import _ from 'lodash';
export const itemsMerge = (items1) => {
return items1;
@ -750,6 +757,10 @@ export const useCurrentSchema = (action: string, key: string, find = findSchema,
};
};
/**
* @deprecated
*
*/
export const useRecordCollectionDataSourceItems = (
componentName,
item = null,
@ -822,15 +833,29 @@ export const useRecordCollectionDataSourceItems = (
];
};
export const useCollectionDataSourceItems = (
export const useCollectionDataSourceItems = ({
componentName,
filter: (collection: Collection) => boolean = () => true,
filter = () => true,
onlyCurrentDataSource = false,
) => {
showAssociationFields,
}: {
componentName;
filter?: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource?: boolean;
showAssociationFields?: boolean;
}) => {
const { t } = useTranslation();
const dm = useDataSourceManager();
const dataSourceKey = useDataSourceKey();
let allCollections = dm.getAllCollections(filter);
const collection = useCollection();
const associationFields = useAssociationFields({ componentName, filterCollections: filter, showAssociationFields });
let allCollections = dm.getAllCollections((collection) => {
if (onlyCurrentDataSource && collection.dataSource !== dataSourceKey) {
return false;
}
return filter({ collection });
});
if (onlyCurrentDataSource) {
allCollections = allCollections.filter((collection) => collection.key === dataSourceKey);
}
@ -841,16 +866,23 @@ export const useCollectionDataSourceItems = (
name: key,
label: displayName,
type: 'subMenu',
children: getChildren({
collections,
componentName,
searchValue: '',
dataSource: key,
getTemplatesByCollection,
t,
}),
children: [
...getChildren({
collections,
componentName,
searchValue: '',
dataSource: key,
getTemplatesByCollection,
t,
}).sort((item) => {
// fix https://nocobase.height.app/T-3551
const inherits = _.toArray(collection?.inherits || []);
if (item.name === collection?.name || inherits.some((inheritName) => inheritName === item.name)) return -1;
}),
...associationFields,
],
}));
}, [allCollections, componentName, dm, getTemplatesByCollection, t]);
}, [allCollections, associationFields, componentName, getTemplatesByCollection, t]);
return res;
};
@ -862,27 +894,29 @@ export const createDetailsBlockSchema = (options) => {
collection,
dataSource,
association,
resource,
template,
settings,
action = 'list',
...others
} = options;
const resourceName = resource || association || collection;
const resourceName = association || collection;
const schema: ISchema = {
type: 'void',
'x-acl-action': `${resourceName}:view`,
'x-acl-action': action === 'get' ? `${resourceName}:get` : `${resourceName}:view`,
'x-decorator': 'DetailsBlockProvider',
'x-decorator-props': {
resource: resourceName,
dataSource,
collection,
association,
readPretty: true,
action: 'list',
params: {
pageSize: 1,
},
// useParams: '{{ useParamsFromRecord }}',
action,
...(action === 'list'
? {
params: {
pageSize: 1,
},
}
: {}),
...others,
},
'x-toolbar': 'BlockSchemaToolbar',
@ -914,13 +948,17 @@ export const createDetailsBlockSchema = (options) => {
'x-initializer': formItemInitializers,
properties: {},
},
pagination: {
type: 'void',
'x-component': 'Pagination',
'x-component-props': {
useProps: '{{ useDetailsPaginationProps }}',
},
},
...(action === 'list'
? {
pagination: {
type: 'void',
'x-component': 'Pagination',
'x-component-props': {
useProps: '{{ useDetailsPaginationProps }}',
},
},
}
: {}),
},
},
},
@ -936,18 +974,16 @@ export const createListBlockSchema = (options) => {
collection,
dataSource,
association,
resource,
template,
settings,
...others
} = options;
const resourceName = resource || association || collection;
const resourceName = association || collection;
const schema: ISchema = {
type: 'void',
'x-acl-action': `${resourceName}:view`,
'x-decorator': 'List.Decorator',
'x-decorator-props': {
resource: resourceName,
collection,
dataSource,
association,
@ -1025,19 +1061,17 @@ export const createGridCardBlockSchema = (options) => {
itemActionInitializers = 'gridCard:configureItemActions',
collection,
association,
resource,
template,
dataSource,
settings,
...others
} = options;
const resourceName = resource || association || collection;
const resourceName = association || collection;
const schema: ISchema = {
type: 'void',
'x-acl-action': `${resourceName}:view`,
'x-decorator': 'GridCard.Decorator',
'x-decorator-props': {
resource: resourceName,
collection,
association,
dataSource,
@ -1248,6 +1282,12 @@ export const createFilterFormBlockSchema = (options) => {
return schema;
};
/**
* @deprecated
* 使 createDetailsBlockSchema
* @param options
* @returns
*/
export const createReadPrettyFormBlockSchema = (options) => {
const {
formItemInitializers = 'details:configureFields',
@ -1315,7 +1355,6 @@ export const createReadPrettyFormBlockSchema = (options) => {
export const createTableBlockSchema = (options) => {
const {
collection,
resource,
rowKey,
tableActionInitializers,
tableColumnInitializers,
@ -1330,11 +1369,10 @@ export const createTableBlockSchema = (options) => {
const schema: ISchema = {
type: 'void',
'x-decorator': tableBlockProvider ?? 'TableBlockProvider',
'x-acl-action': `${resource || collection}:list`,
'x-acl-action': `${collection}:list`,
'x-decorator-props': {
collection,
dataSource,
resource: resource || collection,
action: 'list',
params: {
pageSize,
@ -1526,7 +1564,9 @@ const getChildren = ({
return (
componentName &&
template.componentName === componentName &&
(!template.resourceName || template.resourceName === item.name)
(['FormItem', 'ReadPrettyFormItem'].includes(componentName) ||
!template.resourceName ||
template.resourceName === item.name)
);
});
if (!templates.length) {
@ -1560,8 +1600,9 @@ const getChildren = ({
dataSource,
title: t('Duplicate template'),
children: templates.map((template) => {
const templateName =
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
? `${template?.name} ${t('(Fields only)')}`
: template?.name;
return {
type: 'item',
mode: 'copy',
@ -1579,8 +1620,9 @@ const getChildren = ({
dataSource,
title: t('Reference template'),
children: templates.map((template) => {
const templateName =
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name;
const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
? `${template?.name} ${t('(Fields only)')}`
: template?.name;
return {
type: 'item',
mode: 'reference',
@ -1595,3 +1637,142 @@ const getChildren = ({
};
});
};
function useAssociationFields({
componentName,
filterCollections,
showAssociationFields,
}: {
componentName: string;
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
showAssociationFields?: boolean;
}) {
const fieldSchema = useFieldSchema();
const { getCollectionFields } = useCollectionManager_deprecated();
const collection = useCollection_deprecated();
const cm = useCollectionManager();
const dataSource = useDataSourceKey();
const { getTemplatesByCollection } = useSchemaTemplateManager();
const { t } = useTranslation();
const compile = useCompile();
return useMemo(() => {
if (!showAssociationFields) {
return [];
}
let fields: CollectionFieldOptions[] = [];
if (fieldSchema['x-initializer']) {
fields = collection.fields;
} else {
const collection = recursiveParent(fieldSchema.parent);
if (collection) {
fields = getCollectionFields(collection);
}
}
return fields
.filter((field) => ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface))
.filter((field) => filterCollections({ associationField: field }))
.map((field, index) => {
const targetCollection = cm.getCollection(field.target);
const title = `${compile(field.uiSchema.title || field.name)} -> ${compile(targetCollection.title)}`;
const templates = getTemplatesByCollection(dataSource, field.target).filter((template) => {
return (
componentName &&
template.componentName === componentName &&
(['FormItem', 'ReadPrettyFormItem'].includes(componentName) ||
!template.resourceName ||
template.resourceName === `${field.collectionName}.${field.name}`)
);
});
if (!templates.length) {
return {
type: 'item',
name: `${field.collectionName}.${field.name}`,
collectionName: field.target,
title,
dataSource,
associationField: field,
};
}
return {
key: `${componentName}_table_subMenu_${index}`,
type: 'subMenu',
name: `${field.target}_${index}`,
title,
dataSource,
children: [
{
type: 'item',
name: `${field.collectionName}.${field.name}`,
collectionName: field.target,
dataSource,
title: t('Blank block'),
associationField: field,
},
{
type: 'divider',
},
{
key: `${componentName}_table_subMenu_${index}_copy`,
type: 'subMenu',
name: 'copy',
dataSource,
title: t('Duplicate template'),
children: templates.map((template) => {
const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
? `${template?.name} ${t('(Fields only)')}`
: template?.name;
return {
type: 'item',
mode: 'copy',
name: `${field.collectionName}.${field.name}`,
collectionName: field.target,
template,
dataSource,
title: templateName || t('Untitled'),
associationField: field,
};
}),
},
{
key: `${componentName}_table_subMenu_${index}_ref`,
type: 'subMenu',
name: 'ref',
dataSource,
title: t('Reference template'),
children: templates.map((template) => {
const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
? `${template?.name} ${t('(Fields only)')}`
: template?.name;
return {
type: 'item',
mode: 'reference',
name: `${field.collectionName}.${field.name}`,
collectionName: field.target,
template,
dataSource,
title: templateName || t('Untitled'),
associationField: field,
};
}),
},
],
};
});
}, [
cm,
collection.fields,
compile,
componentName,
dataSource,
fieldSchema,
filterCollections,
getCollectionFields,
getTemplatesByCollection,
showAssociationFields,
t,
]);
}

View File

@ -1,4 +1,4 @@
import { Plugin } from '@nocobase/client';
import { Plugin, canMakeAssociationBlock, useCollection } from '@nocobase/client';
import { generateNTemplate } from '../locale';
import { CalendarV2 } from './calendar';
import { calendarBlockSettings } from './calendar/Calender.Settings';
@ -10,7 +10,12 @@ import {
calendarActionInitializers,
deleteEventActionInitializer,
} from './schema-initializer/initializers';
import { CalendarBlockInitializer, RecordAssociationCalendarBlockInitializer } from './schema-initializer/items';
import {
CalendarBlockInitializer,
RecordAssociationCalendarBlockInitializer,
useCreateAssociationCalendarBlock,
} from './schema-initializer/items';
import { useMemo } from 'react';
export class PluginCalendarClient extends Plugin {
async load() {
@ -19,9 +24,39 @@ export class PluginCalendarClient extends Plugin {
title: generateNTemplate('Calendar'),
Component: 'CalendarBlockInitializer',
});
this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'dataBlocks.calendar', {
title: generateNTemplate('Calendar'),
Component: 'CalendarBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() {
const { createAssociationCalendarBlock } = useCreateAssociationCalendarBlock();
return {
onlyCurrentDataSource: true,
filterCollections({ associationField }) {
if (associationField) {
return ['hasMany', 'belongsToMany'].includes(associationField.type);
}
return false;
},
createBlockSchema: createAssociationCalendarBlock,
showAssociationFields: true,
};
},
});
this.app.addComponents({
CalendarBlockProvider,
CalendarBlockInitializer,
CalendarBlockInitializer: CalendarBlockInitializer as any,
RecordAssociationCalendarBlockInitializer,
CalendarV2,
});

View File

@ -2,6 +2,8 @@ import { FormOutlined } from '@ant-design/icons';
import { FormLayout } from '@formily/antd-v5';
import { SchemaOptionsContext } from '@formily/react';
import {
Collection,
CollectionFieldOptions,
DataBlockInitializer,
FormDialog,
SchemaComponent,
@ -15,7 +17,19 @@ import React, { useContext } from 'react';
import { createCalendarBlockSchema } from '../utils';
import { useTranslation } from '../../../locale';
export const CalendarBlockInitializer = () => {
export const CalendarBlockInitializer = ({
filterCollections,
onlyCurrentDataSource,
hideSearch,
createBlockSchema,
showAssociationFields,
}: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean;
hideSearch?: boolean;
createBlockSchema?: (options: any) => any;
showAssociationFields?: boolean;
}) => {
const { insert } = useSchemaInitializer();
const { t } = useTranslation();
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated();
@ -29,6 +43,10 @@ export const CalendarBlockInitializer = () => {
componentType={'Calendar'}
icon={<FormOutlined />}
onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource });
const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', {
association: ['o2o', 'obo', 'oho', 'm2o'],
@ -87,6 +105,10 @@ export const CalendarBlockInitializer = () => {
}),
);
}}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
showAssociationFields={showAssociationFields}
/>
);
};

View File

@ -113,3 +113,86 @@ export const RecordAssociationCalendarBlockInitializer = () => {
/>
);
};
export function useCreateAssociationCalendarBlock() {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const { t } = useTranslation();
const options = useContext(SchemaOptionsContext);
const { theme } = useGlobalTheme();
const createAssociationCalendarBlock = async ({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
const stringFields = collection?.fields
?.filter((field) => field.type === 'string')
?.map((field) => {
return {
label: field?.uiSchema?.title,
value: field.name,
};
});
const dateFields = collection?.fields
?.filter((field) => field.type === 'date')
?.map((field) => {
return {
label: field?.uiSchema?.title,
value: field.name,
};
});
const values = await FormDialog(
t('Create calendar block'),
() => {
return (
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
<FormLayout layout={'vertical'}>
<SchemaComponent
schema={{
properties: {
title: {
title: t('Title field'),
enum: stringFields,
required: true,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
start: {
title: t('Start date field'),
enum: dateFields,
required: true,
default: 'createdAt',
'x-component': 'Select',
'x-decorator': 'FormItem',
},
end: {
title: t('End date field'),
enum: dateFields,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
},
}}
/>
</FormLayout>
</SchemaComponentOptions>
);
},
theme,
).open({
initialValues: {},
});
insert(
createCalendarBlockSchema({
collection: field.target,
association: `${field.collectionName}.${field.name}`,
fieldNames: {
...values,
},
settings: 'blockSettings:calendar',
}),
);
};
return { createAssociationCalendarBlock };
}

View File

@ -13,7 +13,7 @@ export const T3377: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'BlockInitializers',
'x-initializer': 'page:addBlock',
properties: {
dtuo84w9hyg: {
_isJSONSchemaObject: true,
@ -60,7 +60,7 @@ export const T3377: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'FormItemInitializers',
'x-initializer': 'form:configureFields',
properties: {
'729znsrntep': {
_isJSONSchemaObject: true,
@ -98,7 +98,7 @@ export const T3377: PageConfig = {
version: '2.0',
type: 'void',
'x-component': 'AssociationField.SubTable',
'x-initializer': 'TableColumnInitializers',
'x-initializer': 'table:configureColumns',
'x-initializer-props': {
action: false,
},

View File

@ -76,25 +76,25 @@ export default class extends Plugin {
const FormActionInitializers = this.app.schemaInitializerManager.get('FormActionInitializers');
FormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer);
const CreateFormActionInitializers = this.app.schemaInitializerManager.get('CreateFormActionInitializers');
const CreateFormActionInitializers = this.app.schemaInitializerManager.get('createForm:configureActions');
CreateFormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer);
const UpdateFormActionInitializers = this.app.schemaInitializerManager.get('UpdateFormActionInitializers');
const UpdateFormActionInitializers = this.app.schemaInitializerManager.get('editForm:configureActions');
UpdateFormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer);
const DetailsActionInitializers = this.app.schemaInitializerManager.get('DetailsActionInitializers');
const DetailsActionInitializers = this.app.schemaInitializerManager.get('detailsWithPaging:configureActions');
DetailsActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionInitializer);
const ReadPrettyFormActionInitializers = this.app.schemaInitializerManager.get('ReadPrettyFormActionInitializers');
const ReadPrettyFormActionInitializers = this.app.schemaInitializerManager.get('details:configureActions');
ReadPrettyFormActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionInitializer);
const TableActionColumnInitializers = this.app.schemaInitializerManager.get('TableActionColumnInitializers');
const TableActionColumnInitializers = this.app.schemaInitializerManager.get('table:configureItemActions');
TableActionColumnInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
const GridCardItemActionInitializers = this.app.schemaInitializerManager.get('GridCardItemActionInitializers');
const GridCardItemActionInitializers = this.app.schemaInitializerManager.get('gridCard:configureItemActions');
GridCardItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
const ListItemActionInitializers = this.app.schemaInitializerManager.get('ListItemActionInitializers');
const ListItemActionInitializers = this.app.schemaInitializerManager.get('list:configureItemActions');
ListItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
}
}

View File

@ -65,7 +65,7 @@ export default {
[allCollections],
);
const [openMenuKeys, setOpenMenuKeys] = useState([]);
const searchedChildren = useMenuSearch(childItems, openMenuKeys);
const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys });
return {
name: 'createRecordForm',

View File

@ -101,7 +101,7 @@ export default {
[allCollections],
);
const [openMenuKeys, setOpenMenuKeys] = useState([]);
const searchedChildren = useMenuSearch(childItems, openMenuKeys);
const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys });
return {
name: 'updateRecordForm',
key: 'updateRecordForm',