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 componentVal = Component || component;
const isBuiltType = !componentVal && type && typeComponentMap[type]; 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(() => { const contextValue = useMemo(() => {
return { return {
...others, ...others,

View File

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

View File

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

View File

@ -84,7 +84,10 @@ export const SchemaSettingsChild: FC<SchemaSettingsItemType> = memo((props) => {
const useChildrenRes = useChildren(); const useChildrenRes = useChildren();
const useComponentPropsRes = useComponentProps(); const useComponentPropsRes = useComponentProps();
const findComponent = useFindComponent(); 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 visibleResult = useVisible();
const ComponentValue = useMemo(() => { const ComponentValue = useMemo(() => {
return !Component && type && typeComponentMap[type] ? typeComponentMap[type] : Component; return !Component && type && typeComponentMap[type] ? typeComponentMap[type] : Component;

View File

@ -346,7 +346,7 @@ export const useParamsFromRecord = () => {
const obj = { const obj = {
filterByTk: filterByTk, 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; obj['targetCollection'] = record.__collection;
} }
if (!filterByTk) { if (!filterByTk) {

View File

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

View File

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

View File

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

View File

@ -331,6 +331,10 @@
"Add tab": "Añadir pestaña", "Add tab": "Añadir pestaña",
"Disable tabs": "Desactivar pestañas", "Disable tabs": "Desactivar pestañas",
"Details": "Detalles", "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", "Edit tab": "Editar pestaña",
"Relationship blocks": "Bloques de relación", "Relationship blocks": "Bloques de relación",
"Select record": "Seleccionar registro", "Select record": "Seleccionar registro",

View File

@ -346,6 +346,10 @@
"Add tab": "Ajouter un onglet", "Add tab": "Ajouter un onglet",
"Disable tabs": "Désactiver les onglets", "Disable tabs": "Désactiver les onglets",
"Details": "Détails", "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", "Edit tab": "Modifier l'onglet",
"Relationship blocks": "Blocs de relations", "Relationship blocks": "Blocs de relations",
"Select record": "Sélectionner un enregistrement", "Select record": "Sélectionner un enregistrement",

View File

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

View File

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

View File

@ -295,6 +295,10 @@
"Add tab": "Adicionar guia", "Add tab": "Adicionar guia",
"Disable tabs": "Desativar guias", "Disable tabs": "Desativar guias",
"Details": "Detalhes", "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", "Edit tab": "Editar guia",
"Relationship blocks": "Blocos de relacionamento", "Relationship blocks": "Blocos de relacionamento",
"Select record": "Selecionar registro", "Select record": "Selecionar registro",

View File

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

View File

@ -233,6 +233,10 @@
"Add tab": "Sekme ekle", "Add tab": "Sekme ekle",
"Disable tabs": "Sekmeleri pasifleştir", "Disable tabs": "Sekmeleri pasifleştir",
"Details": "Detaylar", "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", "Edit tab": "Sekme düzenle",
"Relationship blocks": "İlişki blokları", "Relationship blocks": "İlişki blokları",
"Select record": "Kayıt seç", "Select record": "Kayıt seç",

View File

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

View File

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

View File

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

View File

@ -4,8 +4,36 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createDetailsBlockSchema } from '../../../../schema-initializer/utils'; import { createDetailsBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
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 { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated(); const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
@ -13,8 +41,13 @@ export const DetailsBlockInitializer = () => {
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
icon={<TableOutlined />} icon={<TableOutlined />}
componentType={'Details'} componentType={componentType}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async (options) => {
if (createBlockSchema) {
return createBlockSchema(options);
}
const { item } = options;
const collection = getCollection(item.name, item.dataSource); const collection = getCollection(item.name, item.dataSource);
const schema = createDetailsBlockSchema({ const schema = createDetailsBlockSchema({
collection: item.name, collection: item.name,
@ -28,6 +61,11 @@ export const DetailsBlockInitializer = () => {
}); });
insert(schema); 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.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 mockPage(oneEmptyDetailsBlock).goto();
await mockRecord('general');
const formItemInitializer = page.getByLabel('schema-initializer-Grid-details:configureFields-general'); const formItemInitializer = page.getByLabel('schema-initializer-Grid-details:configureFields-general');
@ -60,8 +61,9 @@ test.describe('configure fields', () => {
}); });
test.describe('configure actions', () => { test.describe('configure actions', () => {
test('edit & delete & duplicate', async ({ page, mockPage }) => { test('edit & delete & duplicate', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyDetailsBlock).goto(); await mockPage(oneEmptyDetailsBlock).goto();
await mockRecord('general');
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover(); await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();
await page.getByRole('menuitem', { name: 'Edit' }).click(); 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.describe('actions schema settings', () => {
test('edit & delete & duplicate', async ({ page, mockPage }) => { test('edit & delete & duplicate', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneEmptyDetailsBlock).goto(); await mockPage(oneEmptyDetailsBlock).goto();
await mockRecord('general');
// 创建 Edit & Delete 两个按钮 // 创建 Edit & Delete 两个按钮
await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover(); await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover();

View File

@ -1,66 +1,75 @@
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider'; import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { useSchemaTemplateManager } from '../../../../schema-templates'; import { useSchemaTemplateManager } from '../../../../schema-templates';
import { import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
createReadPrettyFormBlockSchema,
useRecordCollectionDataSourceItems,
} from '../../../../schema-initializer/utils';
export const RecordReadPrettyFormBlockInitializer = () => { export const RecordReadPrettyFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { const { icon = true, targetCollection, ...others } = itemConfig;
onCreateBlockSchema,
componentType,
createBlockSchema,
icon = true,
targetCollection,
...others
} = itemConfig;
const { insert } = useSchemaInitializer();
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const currentCollection = useCollection_deprecated(); const currentCollection = useCollection_deprecated();
const collection = targetCollection || currentCollection; const collection = targetCollection || currentCollection;
const association = useBlockAssociationContext(); const { createSingleDetailsSchema } = useCreateSingleDetailsSchema();
const { block } = useBlockRequestContext();
const actionInitializers =
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
return ( return (
<SchemaInitializerItem <SchemaInitializerItem
icon={icon && <FormOutlined />} icon={icon && <FormOutlined />}
{...others} {...others}
onClick={async ({ item }) => { onClick={(options) => createSingleDetailsSchema(options)}
if (item.template) { items={useRecordCollectionDataSourceItems('ReadPrettyFormItem', null, collection?.name)}
const s = await getTemplateSchemaByMode(item); />
);
};
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') { if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createReadPrettyFormBlockSchema({ const blockSchema = createDetailsBlockSchema({
actionInitializers, actionInitializers,
association, association,
collection: collection.name, collection: item.collectionName || item.name,
dataSource: collection.dataSource, dataSource: item.dataSource,
action: 'get', action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}', useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}', useParams: '{{ useParamsFromRecord }}',
template: s, template: templateSchema,
settings: 'blockSettings:singleDataDetails', settings: 'blockSettings:singleDataDetails',
}); });
if (item.mode === 'reference') { if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key; blockSchema['x-template-key'] = item.template.key;
} }
insert(blockSchema); return blockSchema;
} else { } else {
insert(s); return templateSchema;
} }
},
[actionInitializers, association],
);
const createSingleDetailsSchema = useCallback(
async ({ item }) => {
if (item.template) {
const template = await getTemplateSchemaByMode(item);
insert(templateWrap(template, { item }));
} else { } else {
insert( insert(
createReadPrettyFormBlockSchema({ createDetailsBlockSchema({
actionInitializers, actionInitializers,
association, association,
collection: collection.name, collection: item.collectionName || item.name,
dataSource: collection.dataSource, dataSource: item.dataSource,
action: 'get', action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}', useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}', useParams: '{{ useParamsFromRecord }}',
@ -68,8 +77,9 @@ export const RecordReadPrettyFormBlockInitializer = () => {
}), }),
); );
} }
}} },
items={useRecordCollectionDataSourceItems('ReadPrettyFormItem', null, collection?.name)} [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('action-Action.Link-View-view-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); 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 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('action-Action.Link-View-view-general-table-0').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); 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 page.mouse.move(300, 0);
// create edit ------------------------------------------------------------------------------------ // create edit ------------------------------------------------------------------------------------
await createAction(page, '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({ await expectSettingsMenu({
page, page,
showMenu: async () => { 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(); await page.getByRole('button', { name: 'designer-schema-settings-Action-actionSettings:edit-general' }).hover();
}, },
supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'], 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 ------------------------------------------------------------------------------------ // create delete ------------------------------------------------------------------------------------
await createAction(page, '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 // create print
await createAction(page, '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 ---------------------------------------------------------------------------- // create customize actions ----------------------------------------------------------------------------
// Popup // Popup
await createCustomAction(page, '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 // Update record
await createCustomAction(page, '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 { FormOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { createFormBlockSchema } from '../../../../schema-initializer/utils'; import { createFormBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
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 itemConfig = useSchemaInitializerItem();
const { isCusomeizeCreate } = itemConfig; 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 ( return (
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
icon={<FormOutlined />} icon={<FormOutlined />}
componentType={'FormItem'} componentType={componentType}
templateWrap={(templateSchema, { item }) => { templateWrap={(templateSchema, { item }) => {
const s = createFormBlockSchema({ if (templateWrap) {
return templateWrap(templateSchema, { item });
}
const schema = createFormBlockSchema({
isCusomeizeCreate, isCusomeizeCreate,
dataSource: item.dataSource, dataSource: item.dataSource,
template: templateSchema, template: templateSchema,
@ -21,11 +71,15 @@ export const FormBlockInitializer = () => {
settings: 'blockSettings:createForm', settings: 'blockSettings:createForm',
}); });
if (item.template && item.mode === 'reference') { 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,51 +1,52 @@
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useBlockAssociationContext } from '../../../../block-provider'; import { useBlockAssociationContext } from '../../../../block-provider';
import { useCollection_deprecated } from '../../../../collection-manager'; import { useCollection_deprecated } from '../../../../collection-manager';
import { useSchemaTemplateManager } from '../../../../schema-templates'; import { useSchemaTemplateManager } from '../../../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
/**
* @deprecated
*/
export const RecordFormBlockInitializer = () => { export const RecordFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, targetCollection, ...others } = itemConfig; const { targetCollection, ...others } = itemConfig;
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
const { getTemplateSchemaByMode } = useSchemaTemplateManager(); const { getTemplateSchemaByMode } = useSchemaTemplateManager();
const currentCollection = useCollection_deprecated(); const currentCollection = useCollection_deprecated();
const collection = targetCollection || currentCollection; const collection = targetCollection || currentCollection;
const association = useBlockAssociationContext();
const { createEditFormBlock, templateWrap } = useCreateEditFormBlock();
return ( return (
<SchemaInitializerItem <SchemaInitializerItem
icon={<FormOutlined />} icon={<FormOutlined />}
{...others} {...others}
onClick={async ({ item }) => { onClick={async ({ item }) => {
if (item.template) { if (item.template) {
const s = await getTemplateSchemaByMode(item); const template = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') { insert(templateWrap(template, { item }));
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 { } else {
insert(s); createEditFormBlock({ item });
} }
} else { }}
items={useRecordCollectionDataSourceItems('FormItem', null, collection?.name)}
/>
);
};
export function useCreateEditFormBlock() {
const { insert } = useSchemaInitializer();
const association = useBlockAssociationContext();
const createEditFormBlock = useCallback(
({ item }) => {
insert( insert(
createFormBlockSchema({ createFormBlockSchema({
association, association,
collection: collection.name, collection: item.collectionName || item.name,
dataSource: collection.dataSource, dataSource: item.dataSource,
action: 'get', action: 'get',
useSourceId: '{{ useSourceIdFromParentRecord }}', useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}', useParams: '{{ useParamsFromRecord }}',
@ -53,9 +54,34 @@ export const RecordFormBlockInitializer = () => {
settings: 'blockSettings:editForm', settings: 'blockSettings:editForm',
}), }),
); );
} },
}} [association, insert],
items={useRecordCollectionDataSourceItems('FormItem', null, collection?.name)}
/>
); );
};
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('action-Action.Link-Edit-update-users-table-0').click();
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-users').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: 'Reference template' }).hover();
await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).first().click(); await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).first().click();
await page.mouse.move(300, 0); await page.mouse.move(300, 0);

View File

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

View File

@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createGridCardBlockSchema } from '../../../../schema-initializer/utils'; import { createGridCardBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
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 { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated(); const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
@ -15,6 +42,10 @@ export const GridCardBlockInitializer = () => {
icon={<OrderedListOutlined />} icon={<OrderedListOutlined />}
componentType={'GridCard'} componentType={'GridCard'}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const collection = getCollection(item.name, item.dataSource); const collection = getCollection(item.name, item.dataSource);
const schema = createGridCardBlockSchema({ const schema = createGridCardBlockSchema({
collection: item.name, collection: item.name,
@ -24,6 +55,10 @@ export const GridCardBlockInitializer = () => {
}); });
insert(schema); 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 { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createListBlockSchema } from '../../../../schema-initializer/utils'; import { createListBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
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 { getCollection } = useCollectionManager_deprecated();
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
@ -15,6 +42,10 @@ export const ListBlockInitializer = () => {
icon={<OrderedListOutlined />} icon={<OrderedListOutlined />}
componentType={'List'} componentType={'List'}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const collection = getCollection(item.name, item.dataSource); const collection = getCollection(item.name, item.dataSource);
const schema = createListBlockSchema({ const schema = createListBlockSchema({
collection: item.name, collection: item.name,
@ -24,6 +55,10 @@ export const ListBlockInitializer = () => {
}); });
insert(schema); 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 { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createTableBlockSchema } from '../../../../schema-initializer/utils'; import { createTableBlockSchema } from '../../../../schema-initializer/utils';
import React from 'react'; 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 { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated(); const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
@ -15,6 +37,10 @@ export const TableBlockInitializer = () => {
icon={<TableOutlined />} icon={<TableOutlined />}
componentType={'Table'} componentType={'Table'}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const collection = getCollection(item.name, item.dataSource); const collection = getCollection(item.name, item.dataSource);
const schema = createTableBlockSchema({ const schema = createTableBlockSchema({
collection: item.name, collection: item.name,
@ -23,6 +49,10 @@ export const TableBlockInitializer = () => {
}); });
insert(schema); 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 { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { createCollapseBlockSchema } from '../../../../schema-initializer/utils'; import { createCollapseBlockSchema } from '../../../../schema-initializer/utils';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection } from '../../../../data-source'; import { Collection, CollectionFieldOptions } from '../../../../data-source';
export const FilterCollapseBlockInitializer = ({ export const FilterCollapseBlockInitializer = ({
filterMenuItemChildren, filterCollections,
onlyCurrentDataSource, onlyCurrentDataSource,
showChildren,
}: { }: {
filterMenuItemChildren: (collection: Collection) => boolean; filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean; onlyCurrentDataSource: boolean;
showChildren?: boolean;
}) => { }) => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { insert } = useSchemaInitializer(); const { insert } = useSchemaInitializer();
@ -31,7 +33,8 @@ export const FilterCollapseBlockInitializer = ({
}); });
insert(schema); insert(schema);
}} }}
filter={filterMenuItemChildren} filter={filterCollections}
showChildren={showChildren}
/> />
); );
}; };

View File

@ -1,18 +1,21 @@
import { FormOutlined } from '@ant-design/icons'; import { FormOutlined } from '@ant-design/icons';
import React from 'react'; import React from 'react';
import { useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { createFilterFormBlockSchema } from '../../../../schema-initializer/utils'; import { createFilterFormBlockSchema } from '../../../../schema-initializer/utils';
import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer'; import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer';
import { Collection } from '../../../../data-source'; import { Collection, CollectionFieldOptions } from '../../../../data-source';
export const FilterFormBlockInitializer = ({ export const FilterFormBlockInitializer = ({
filterMenuItemChildren, filterCollections,
onlyCurrentDataSource, onlyCurrentDataSource,
showChildren,
}: { }: {
filterMenuItemChildren: (collection: Collection) => boolean; filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean; onlyCurrentDataSource: boolean;
showChildren?: boolean;
}) => { }) => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { insert } = useSchemaInitializer();
return ( return (
<FilterBlockInitializer <FilterBlockInitializer
@ -32,11 +35,17 @@ export const FilterFormBlockInitializer = ({
} }
return s; return s;
}} }}
createBlockSchema={(options) => { onCreateBlockSchema={({ item }) => {
options = { ...options, settings: 'blockSettings:filterForm' }; return insert(
return createFilterFormBlockSchema(options); 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(); await expect(page.getByText('test8')).toBeVisible();
// add blocks // add blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); await addBlock(['table Details right', 'General']);
await page.getByRole('menuitem', { name: 'Details' }).click(); await addBlock(['form Form (Edit)']);
await page.getByText('Form').first().click(); await addBlock(['Markdown']);
await page.getByRole('menuitem', { name: 'Markdown' }).click();
await expect(page.getByText('GeneralConfigure actionsConfigure fields')).toBeVisible(); await expect(page.getByText('GeneralConfigure actionsConfigure fields')).toBeVisible();
await expect(page.getByText('GeneralConfigure fieldsConfigure actions')).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(); await page.getByRole('button', { name: 'OK', exact: true }).click();
// add relationship blocks // add relationship blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); await addBlock(['table Details right', 'Many to one']);
await page.getByRole('menuitem', { name: 'Many to one' }).hover(); await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible();
await page.getByRole('menuitem', { name: 'Details' }).click(); await addBlock(['table Table right', 'One to many']);
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 expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible(); 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 }) => { 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 // add blocks
await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); 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.getByText('Form').first().click(); 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.getByRole('menuitem', { name: 'Markdown' }).click();
await page.mouse.move(300, 0); 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(); await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible();
// add relationship blocks // 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 page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover();
await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible(); await expect(page.getByRole('menuitem', { name: 'table Details right' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible(); await expect(page.getByRole('menuitem', { name: 'form Form (Edit)' })).toBeVisible();
await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible(); await expect(page.getByRole('menuitem', { name: 'form Form (Add new) right' })).toBeVisible();
await expect(page.getByText('Form').nth(1)).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 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 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.getByLabel('block-item-CardItem-users-table')).toBeVisible();
// 屏幕上没有显示错误提示 // 屏幕上没有显示错误提示
await expect(page.locator('.ant-notification-notice').first()).toBeHidden({ timeout: 1000 }); await expect(page.locator('.ant-notification-notice').first()).toBeHidden({ timeout: 1000 });

View File

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

View File

@ -35,7 +35,7 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali
title: '{{t("Form")}}', title: '{{t("Form")}}',
Component: 'FilterFormBlockInitializer', Component: 'FilterFormBlockInitializer',
componentProps: { componentProps: {
filterMenuItemChildren() { filterCollections() {
return false; return false;
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
@ -48,7 +48,7 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali
title: '{{t("Collapse")}}', title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer', Component: 'FilterCollapseBlockInitializer',
componentProps: { componentProps: {
filterMenuItemChildren() { filterCollections() {
return false; return false;
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
@ -117,7 +117,7 @@ export const tableSelectorInitializers = new CompatibleSchemaInitializer(
title: '{{t("Form")}}', title: '{{t("Form")}}',
Component: 'FilterFormBlockInitializer', Component: 'FilterFormBlockInitializer',
componentProps: { componentProps: {
filterMenuItemChildren() { filterCollections() {
return false; return false;
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
@ -130,7 +130,7 @@ export const tableSelectorInitializers = new CompatibleSchemaInitializer(
title: '{{t("Collapse")}}', title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer', Component: 'FilterCollapseBlockInitializer',
componentProps: { componentProps: {
filterMenuItemChildren() { filterCollections() {
return false; return false;
}, },
onlyCurrentDataSource: true, 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 { 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', () => { it('should render correctly', () => {
render(<App1 />); render(<App1 />);
expect(screen.getByText('this is name')).toBeInTheDocument(); expect(screen.getByText('No data')).toBeInTheDocument();
expect(screen.getByText('this is age')).toBeInTheDocument();
}); });
}); });

View File

@ -1,9 +1,20 @@
import { Schema, useFieldSchema } from '@formily/react'; import { Schema } from '@formily/react';
import { useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useCollectionManager_deprecated, useCollection_deprecated } from '../..'; import {
import { SchemaInitializerItemType, useSchemaInitializer } from '../../application'; useCollection,
useCollectionManager_deprecated,
useCollection_deprecated,
useCreateAssociationDetailsBlock,
useCreateAssociationDetailsWithoutPagination,
useCreateAssociationFormBlock,
useCreateAssociationGridCardBlock,
useCreateAssociationListBlock,
useCreateAssociationTableBlock,
useCreateEditFormBlock,
} from '../..';
import { CompatibleSchemaInitializer } from '../../application/schema-initializer/CompatibleSchemaInitializer'; import { CompatibleSchemaInitializer } from '../../application/schema-initializer/CompatibleSchemaInitializer';
import { gridRowColWrap } from '../utils'; import { gridRowColWrap } from '../utils';
import { useCreateSingleDetailsSchema } from '../../modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer';
const recursiveParent = (schema: Schema) => { const recursiveParent = (schema: Schema) => {
if (!schema) return null; if (!schema) return null;
@ -15,223 +26,233 @@ const recursiveParent = (schema: Schema) => {
} }
}; };
const useRelationFields = () => { export const canMakeAssociationBlock = (field) => {
const fieldSchema = useFieldSchema(); return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface);
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;
}; };
function useRecordBlocks() { function useRecordBlocks() {
const { options } = useSchemaInitializer();
const { actionInitializers } = options;
const collection = useCollection_deprecated(); const collection = useCollection_deprecated();
const { getChildrenCollections } = useCollectionManager_deprecated(); const { getChildrenCollections } = useCollectionManager_deprecated();
const collectionsWithView = getChildrenCollections(collection.name, true, collection.dataSource).filter( const collectionsWithView = getChildrenCollections(collection.name, true, collection.dataSource).filter(
(v) => v?.filterTargetKey, (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 = []; const res: any[] = [
if (hasChildCollection) { {
res.push({
name: 'details',
type: 'subMenu',
title: '{{t("Details")}}',
children: detailChildren,
});
} else {
res.push({
name: 'details', name: 'details',
title: '{{t("Details")}}', title: '{{t("Details")}}',
Component: 'RecordReadPrettyFormBlockInitializer', Component: 'DetailsBlockInitializer',
actionInitializers, 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) { return {
res.push({ filterCollections({ collection }) {
name: 'form', if (collection) {
type: 'subMenu', return collectionsNeedToDisplay.some((c) => c.name === collection.name);
title: '{{t("Form")}}',
children: formChildren,
});
} else {
modifyFlag &&
res.push({
name: 'form',
title: '{{t("Form")}}',
Component: 'RecordFormBlockInitializer',
});
} }
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; return res;
} }
@ -247,8 +268,8 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
items: [ items: [
{ {
type: 'itemGroup', type: 'itemGroup',
name: 'currentRecordBlocks', name: 'dataBlocks',
title: '{{t("Current record blocks")}}', title: '{{t("Data blocks")}}',
useChildren: useRecordBlocks, useChildren: useRecordBlocks,
}, },
{ {
@ -256,8 +277,14 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
title: '{{t("Filter blocks")}}', title: '{{t("Filter blocks")}}',
type: 'itemGroup', type: 'itemGroup',
useVisible() { useVisible() {
const collection = useCollection_deprecated(); const collection = useCollection();
return collection.fields.some((field) => ['hasMany', 'belongsToMany'].includes(field.type)); return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
}, },
children: [ children: [
{ {
@ -272,10 +299,13 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
); );
return { return {
filterMenuItemChildren(collection) { filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name); return toManyField.some((field) => field.target === collection.name);
}
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
showChildren: true,
}; };
}, },
}, },
@ -284,32 +314,35 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize
title: '{{t("Collapse")}}', title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer', Component: 'FilterCollapseBlockInitializer',
useComponentProps() { useComponentProps() {
const collection = useCollection_deprecated(); const collection = useCollection();
const toManyField = useMemo( const toManyField = useMemo(
() => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)), () => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)),
[collection.fields], [collection.fields],
); );
return { return {
filterMenuItemChildren(collection) { filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name); return toManyField.some((field) => field.target === collection.name);
}
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
showChildren: true,
}; };
}, },
}, },
], ],
}, },
{ // {
type: 'itemGroup', // type: 'itemGroup',
name: 'relationshipBlocks', // name: 'relationshipBlocks',
title: '{{t("Relationship blocks")}}', // title: '{{t("Relationship blocks")}}',
useChildren: useRelationFields, // useChildren: useRelationFields,
useVisible() { // useVisible() {
const res = useRelationFields(); // const res = useRelationFields();
return res.length > 0; // return res.length > 0;
}, // },
}, // },
{ {
type: 'itemGroup', type: 'itemGroup',
name: 'otherBlocks', name: 'otherBlocks',
@ -334,8 +367,8 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
items: [ items: [
{ {
type: 'itemGroup', type: 'itemGroup',
name: 'currentRecordBlocks', name: 'dataBlocks',
title: '{{t("Current record blocks")}}', title: '{{t("Data blocks")}}',
useChildren: useRecordBlocks, useChildren: useRecordBlocks,
}, },
{ {
@ -343,8 +376,14 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
title: '{{t("Filter blocks")}}', title: '{{t("Filter blocks")}}',
type: 'itemGroup', type: 'itemGroup',
useVisible() { useVisible() {
const collection = useCollection_deprecated(); const collection = useCollection();
return collection.fields.some((field) => ['hasMany', 'belongsToMany'].includes(field.type)); return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
}, },
children: [ children: [
{ {
@ -359,10 +398,13 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
); );
return { return {
filterMenuItemChildren(collection) { filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name); return toManyField.some((field) => field.target === collection.name);
}
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
showChildren: true,
}; };
}, },
}, },
@ -371,32 +413,35 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer(
title: '{{t("Collapse")}}', title: '{{t("Collapse")}}',
Component: 'FilterCollapseBlockInitializer', Component: 'FilterCollapseBlockInitializer',
useComponentProps() { useComponentProps() {
const collection = useCollection_deprecated(); const collection = useCollection();
const toManyField = useMemo( const toManyField = useMemo(
() => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)), () => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)),
[collection.fields], [collection.fields],
); );
return { return {
filterMenuItemChildren(collection) { filterCollections({ collection }) {
if (collection) {
return toManyField.some((field) => field.target === collection.name); return toManyField.some((field) => field.target === collection.name);
}
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
showChildren: true,
}; };
}, },
}, },
], ],
}, },
{ // {
type: 'itemGroup', // type: 'itemGroup',
name: 'relationshipBlocks', // name: 'relationshipBlocks',
title: '{{t("Relationship blocks")}}', // title: '{{t("Relationship blocks")}}',
useChildren: useRelationFields, // useChildren: useRelationFields,
useVisible() { // useVisible() {
const res = useRelationFields(); // const res = useRelationFields();
return res.length > 0; // return res.length > 0;
}, // },
}, // },
{ {
type: 'itemGroup', type: 'itemGroup',
name: 'otherBlocks', name: 'otherBlocks',

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useCallback } from 'react';
import { TableOutlined } from '@ant-design/icons'; import { TableOutlined } from '@ant-design/icons';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
@ -6,6 +6,9 @@ import { useSchemaTemplateManager } from '../../schema-templates';
import { createTableBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { createTableBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
/**
* @deprecated
*/
export const RecordAssociationBlockInitializer = () => { export const RecordAssociationBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; 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 { FormOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
@ -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 { FormOutlined } from '@ant-design/icons';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
@ -6,6 +6,9 @@ import { useSchemaTemplateManager } from '../../schema-templates';
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
/**
* @deprecated
*/
export const RecordAssociationFormBlockInitializer = () => { export const RecordAssociationFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig;
@ -27,7 +30,7 @@ export const RecordAssociationFormBlockInitializer = () => {
: 'createForm:configureActions'; : 'createForm:configureActions';
if (item.template) { if (item.template) {
const s = await getTemplateSchemaByMode(item); const template = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'FormItem') { if (item.template.componentName === 'FormItem') {
const blockSchema = createFormBlockSchema({ const blockSchema = createFormBlockSchema({
collection: collectionName, collection: collectionName,
@ -38,7 +41,7 @@ export const RecordAssociationFormBlockInitializer = () => {
useSourceId: '{{ useSourceIdFromParentRecord }}', useSourceId: '{{ useSourceIdFromParentRecord }}',
useParams: '{{ useParamsFromRecord }}', useParams: '{{ useParamsFromRecord }}',
actionInitializers, actionInitializers,
template: s, template: template,
settings: 'blockSettings:createForm', settings: 'blockSettings:createForm',
}); });
if (item.mode === 'reference') { if (item.mode === 'reference') {
@ -46,7 +49,7 @@ export const RecordAssociationFormBlockInitializer = () => {
} }
insert(blockSchema); insert(blockSchema);
} else { } else {
insert(s); insert(template);
} }
} else { } else {
insert( 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 { TableOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useSchemaTemplateManager } from '../../schema-templates'; import { useSchemaTemplateManager } from '../../schema-templates';
import { createGridCardBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { createGridCardBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
/**
* @deprecated
*/
export const RecordAssociationGridCardBlockInitializer = () => { export const RecordAssociationGridCardBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; 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 { TableOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
@ -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 { FormOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useBlockRequestContext } from '../../block-provider'; import { useBlockRequestContext } from '../../block-provider';
import { useSchemaTemplateManager } from '../../schema-templates'; import { useSchemaTemplateManager } from '../../schema-templates';
import { createReadPrettyFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
/**
* @deprecated
*/
export const RecordReadPrettyAssociationFormBlockInitializer = () => { export const RecordReadPrettyAssociationFormBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig;
@ -30,7 +33,7 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
if (item.template) { if (item.template) {
const s = await getTemplateSchemaByMode(item); const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'ReadPrettyFormItem') { if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createReadPrettyFormBlockSchema({ const blockSchema = createDetailsBlockSchema({
actionInitializers, actionInitializers,
collection: collectionName, collection: collectionName,
dataSource: collection.dataSource, dataSource: collection.dataSource,
@ -51,7 +54,7 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => {
} }
} else { } else {
insert( insert(
createReadPrettyFormBlockSchema({ createDetailsBlockSchema({
actionInitializers, actionInitializers,
collection: collectionName, collection: collectionName,
resource, 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 { uid } from '@formily/shared';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; 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 { FieldOptions, useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager';
import { isAssocField } from '../filter-provider/utils'; 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 { 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 { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider';
import { DataSourceManager } from '../data-source/data-source/DataSourceManager'; import _ from 'lodash';
export const itemsMerge = (items1) => { export const itemsMerge = (items1) => {
return items1; return items1;
@ -750,6 +757,10 @@ export const useCurrentSchema = (action: string, key: string, find = findSchema,
}; };
}; };
/**
* @deprecated
*
*/
export const useRecordCollectionDataSourceItems = ( export const useRecordCollectionDataSourceItems = (
componentName, componentName,
item = null, item = null,
@ -822,15 +833,29 @@ export const useRecordCollectionDataSourceItems = (
]; ];
}; };
export const useCollectionDataSourceItems = ( export const useCollectionDataSourceItems = ({
componentName, componentName,
filter: (collection: Collection) => boolean = () => true, filter = () => true,
onlyCurrentDataSource = false, onlyCurrentDataSource = false,
) => { showAssociationFields,
}: {
componentName;
filter?: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource?: boolean;
showAssociationFields?: boolean;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dm = useDataSourceManager(); const dm = useDataSourceManager();
const dataSourceKey = useDataSourceKey(); 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) { if (onlyCurrentDataSource) {
allCollections = allCollections.filter((collection) => collection.key === dataSourceKey); allCollections = allCollections.filter((collection) => collection.key === dataSourceKey);
} }
@ -841,16 +866,23 @@ export const useCollectionDataSourceItems = (
name: key, name: key,
label: displayName, label: displayName,
type: 'subMenu', type: 'subMenu',
children: getChildren({ children: [
...getChildren({
collections, collections,
componentName, componentName,
searchValue: '', searchValue: '',
dataSource: key, dataSource: key,
getTemplatesByCollection, getTemplatesByCollection,
t, 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; return res;
}; };
@ -862,27 +894,29 @@ export const createDetailsBlockSchema = (options) => {
collection, collection,
dataSource, dataSource,
association, association,
resource,
template, template,
settings, settings,
action = 'list',
...others ...others
} = options; } = options;
const resourceName = resource || association || collection; const resourceName = association || collection;
const schema: ISchema = { const schema: ISchema = {
type: 'void', type: 'void',
'x-acl-action': `${resourceName}:view`, 'x-acl-action': action === 'get' ? `${resourceName}:get` : `${resourceName}:view`,
'x-decorator': 'DetailsBlockProvider', 'x-decorator': 'DetailsBlockProvider',
'x-decorator-props': { 'x-decorator-props': {
resource: resourceName,
dataSource, dataSource,
collection, collection,
association, association,
readPretty: true, readPretty: true,
action: 'list', action,
...(action === 'list'
? {
params: { params: {
pageSize: 1, pageSize: 1,
}, },
// useParams: '{{ useParamsFromRecord }}', }
: {}),
...others, ...others,
}, },
'x-toolbar': 'BlockSchemaToolbar', 'x-toolbar': 'BlockSchemaToolbar',
@ -914,6 +948,8 @@ export const createDetailsBlockSchema = (options) => {
'x-initializer': formItemInitializers, 'x-initializer': formItemInitializers,
properties: {}, properties: {},
}, },
...(action === 'list'
? {
pagination: { pagination: {
type: 'void', type: 'void',
'x-component': 'Pagination', 'x-component': 'Pagination',
@ -921,6 +957,8 @@ export const createDetailsBlockSchema = (options) => {
useProps: '{{ useDetailsPaginationProps }}', useProps: '{{ useDetailsPaginationProps }}',
}, },
}, },
}
: {}),
}, },
}, },
}, },
@ -936,18 +974,16 @@ export const createListBlockSchema = (options) => {
collection, collection,
dataSource, dataSource,
association, association,
resource,
template, template,
settings, settings,
...others ...others
} = options; } = options;
const resourceName = resource || association || collection; const resourceName = association || collection;
const schema: ISchema = { const schema: ISchema = {
type: 'void', type: 'void',
'x-acl-action': `${resourceName}:view`, 'x-acl-action': `${resourceName}:view`,
'x-decorator': 'List.Decorator', 'x-decorator': 'List.Decorator',
'x-decorator-props': { 'x-decorator-props': {
resource: resourceName,
collection, collection,
dataSource, dataSource,
association, association,
@ -1025,19 +1061,17 @@ export const createGridCardBlockSchema = (options) => {
itemActionInitializers = 'gridCard:configureItemActions', itemActionInitializers = 'gridCard:configureItemActions',
collection, collection,
association, association,
resource,
template, template,
dataSource, dataSource,
settings, settings,
...others ...others
} = options; } = options;
const resourceName = resource || association || collection; const resourceName = association || collection;
const schema: ISchema = { const schema: ISchema = {
type: 'void', type: 'void',
'x-acl-action': `${resourceName}:view`, 'x-acl-action': `${resourceName}:view`,
'x-decorator': 'GridCard.Decorator', 'x-decorator': 'GridCard.Decorator',
'x-decorator-props': { 'x-decorator-props': {
resource: resourceName,
collection, collection,
association, association,
dataSource, dataSource,
@ -1248,6 +1282,12 @@ export const createFilterFormBlockSchema = (options) => {
return schema; return schema;
}; };
/**
* @deprecated
* 使 createDetailsBlockSchema
* @param options
* @returns
*/
export const createReadPrettyFormBlockSchema = (options) => { export const createReadPrettyFormBlockSchema = (options) => {
const { const {
formItemInitializers = 'details:configureFields', formItemInitializers = 'details:configureFields',
@ -1315,7 +1355,6 @@ export const createReadPrettyFormBlockSchema = (options) => {
export const createTableBlockSchema = (options) => { export const createTableBlockSchema = (options) => {
const { const {
collection, collection,
resource,
rowKey, rowKey,
tableActionInitializers, tableActionInitializers,
tableColumnInitializers, tableColumnInitializers,
@ -1330,11 +1369,10 @@ export const createTableBlockSchema = (options) => {
const schema: ISchema = { const schema: ISchema = {
type: 'void', type: 'void',
'x-decorator': tableBlockProvider ?? 'TableBlockProvider', 'x-decorator': tableBlockProvider ?? 'TableBlockProvider',
'x-acl-action': `${resource || collection}:list`, 'x-acl-action': `${collection}:list`,
'x-decorator-props': { 'x-decorator-props': {
collection, collection,
dataSource, dataSource,
resource: resource || collection,
action: 'list', action: 'list',
params: { params: {
pageSize, pageSize,
@ -1526,7 +1564,9 @@ const getChildren = ({
return ( return (
componentName && componentName &&
template.componentName === componentName && template.componentName === componentName &&
(!template.resourceName || template.resourceName === item.name) (['FormItem', 'ReadPrettyFormItem'].includes(componentName) ||
!template.resourceName ||
template.resourceName === item.name)
); );
}); });
if (!templates.length) { if (!templates.length) {
@ -1560,8 +1600,9 @@ const getChildren = ({
dataSource, dataSource,
title: t('Duplicate template'), title: t('Duplicate template'),
children: templates.map((template) => { children: templates.map((template) => {
const templateName = const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name; ? `${template?.name} ${t('(Fields only)')}`
: template?.name;
return { return {
type: 'item', type: 'item',
mode: 'copy', mode: 'copy',
@ -1579,8 +1620,9 @@ const getChildren = ({
dataSource, dataSource,
title: t('Reference template'), title: t('Reference template'),
children: templates.map((template) => { children: templates.map((template) => {
const templateName = const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name; ? `${template?.name} ${t('(Fields only)')}`
: template?.name;
return { return {
type: 'item', type: 'item',
mode: 'reference', 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 { generateNTemplate } from '../locale';
import { CalendarV2 } from './calendar'; import { CalendarV2 } from './calendar';
import { calendarBlockSettings } from './calendar/Calender.Settings'; import { calendarBlockSettings } from './calendar/Calender.Settings';
@ -10,7 +10,12 @@ import {
calendarActionInitializers, calendarActionInitializers,
deleteEventActionInitializer, deleteEventActionInitializer,
} from './schema-initializer/initializers'; } 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 { export class PluginCalendarClient extends Plugin {
async load() { async load() {
@ -19,9 +24,39 @@ export class PluginCalendarClient extends Plugin {
title: generateNTemplate('Calendar'), title: generateNTemplate('Calendar'),
Component: 'CalendarBlockInitializer', 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({ this.app.addComponents({
CalendarBlockProvider, CalendarBlockProvider,
CalendarBlockInitializer, CalendarBlockInitializer: CalendarBlockInitializer as any,
RecordAssociationCalendarBlockInitializer, RecordAssociationCalendarBlockInitializer,
CalendarV2, CalendarV2,
}); });

View File

@ -2,6 +2,8 @@ import { FormOutlined } from '@ant-design/icons';
import { FormLayout } from '@formily/antd-v5'; import { FormLayout } from '@formily/antd-v5';
import { SchemaOptionsContext } from '@formily/react'; import { SchemaOptionsContext } from '@formily/react';
import { import {
Collection,
CollectionFieldOptions,
DataBlockInitializer, DataBlockInitializer,
FormDialog, FormDialog,
SchemaComponent, SchemaComponent,
@ -15,7 +17,19 @@ import React, { useContext } from 'react';
import { createCalendarBlockSchema } from '../utils'; import { createCalendarBlockSchema } from '../utils';
import { useTranslation } from '../../../locale'; 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 { insert } = useSchemaInitializer();
const { t } = useTranslation(); const { t } = useTranslation();
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated(); const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated();
@ -29,6 +43,10 @@ export const CalendarBlockInitializer = () => {
componentType={'Calendar'} componentType={'Calendar'}
icon={<FormOutlined />} icon={<FormOutlined />}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async ({ item }) => {
if (createBlockSchema) {
return createBlockSchema({ item });
}
const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource }); const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource });
const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', { const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', {
association: ['o2o', 'obo', 'oho', 'm2o'], 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', version: '2.0',
type: 'void', type: 'void',
'x-component': 'Grid', 'x-component': 'Grid',
'x-initializer': 'BlockInitializers', 'x-initializer': 'page:addBlock',
properties: { properties: {
dtuo84w9hyg: { dtuo84w9hyg: {
_isJSONSchemaObject: true, _isJSONSchemaObject: true,
@ -60,7 +60,7 @@ export const T3377: PageConfig = {
version: '2.0', version: '2.0',
type: 'void', type: 'void',
'x-component': 'Grid', 'x-component': 'Grid',
'x-initializer': 'FormItemInitializers', 'x-initializer': 'form:configureFields',
properties: { properties: {
'729znsrntep': { '729znsrntep': {
_isJSONSchemaObject: true, _isJSONSchemaObject: true,
@ -98,7 +98,7 @@ export const T3377: PageConfig = {
version: '2.0', version: '2.0',
type: 'void', type: 'void',
'x-component': 'AssociationField.SubTable', 'x-component': 'AssociationField.SubTable',
'x-initializer': 'TableColumnInitializers', 'x-initializer': 'table:configureColumns',
'x-initializer-props': { 'x-initializer-props': {
action: false, action: false,
}, },

View File

@ -76,25 +76,25 @@ export default class extends Plugin {
const FormActionInitializers = this.app.schemaInitializerManager.get('FormActionInitializers'); const FormActionInitializers = this.app.schemaInitializerManager.get('FormActionInitializers');
FormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer); 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); 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); 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); 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); 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); 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); 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); ListItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer);
} }
} }

View File

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

View File

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