From 286af35ff8fa43efc807de86ac421d0a9ac02689 Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Thu, 14 Mar 2024 19:20:02 +0800 Subject: [PATCH] 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> --- .../components/SchemaInitializerChildren.tsx | 5 +- .../components/SchemaInitializerSubMenu.tsx | 3 + .../schema-initializer/hooks/index.tsx | 2 + .../components/SchemaSettingsChildren.tsx | 5 +- .../src/block-provider/BlockProvider.tsx | 2 +- .../block-provider/DetailsBlockProvider.tsx | 12 +- .../data-block/DataBlockRequestProvider.tsx | 4 - packages/core/client/src/locale/en_US.json | 4 + packages/core/client/src/locale/es_ES.json | 4 + packages/core/client/src/locale/fr_FR.json | 4 + packages/core/client/src/locale/ja_JP.json | 4 + packages/core/client/src/locale/ko_KR.json | 4 + packages/core/client/src/locale/pt_BR.json | 4 + packages/core/client/src/locale/ru_RU.json | 4 + packages/core/client/src/locale/tr_TR.json | 4 + packages/core/client/src/locale/uk_UA.json | 4 + packages/core/client/src/locale/zh-CN.json | 4 + packages/core/client/src/locale/zh-TW.json | 4 + .../details-multi/DetailsBlockInitializer.tsx | 44 +- .../__e2e__/schemaInitializer.test.ts | 6 +- .../__e2e__/schemaSettings.test.ts | 3 +- .../RecordReadPrettyFormBlockInitializer.tsx | 122 ++-- .../__e2e__/schemaInitializer.test.ts | 22 +- .../data-blocks/form/FormBlockInitializer.tsx | 70 ++- .../form/RecordFormBlockInitializer.tsx | 96 ++-- .../form-create/schemaSettings.test.ts | 2 +- .../__e2e__/form-create/templatesOfBug.ts | 18 +- .../grid-card/GridCardBlockInitializer.tsx | 37 +- .../data-blocks/list/ListBlockInitializer.tsx | 37 +- .../table/TableBlockInitializer.tsx | 32 +- .../FilterCollapseBlockInitializer.tsx | 11 +- .../form/FilterFormBlockInitializer.tsx | 25 +- .../dialog/__e2e__/schemaInitializer.test.ts | 76 ++- .../component/DatePicker/__e2e__/utils.ts | 12 +- .../Picker/TableSelectorInitializers.tsx | 8 +- .../page/__e2e__/blockInitializers.test.ts | 12 + .../schema-component/antd/details/Details.tsx | 15 +- .../antd/details/__tests__/details.test.tsx | 3 +- .../buttons/RecordBlockInitializers.tsx | 539 ++++++++++-------- .../client/src/schema-initializer/index.ts | 1 + .../items/DataBlockInitializer.tsx | 112 ++-- .../RecordAssociationBlockInitializer.tsx | 29 +- ...cordAssociationDetailsBlockInitializer.tsx | 27 +- .../RecordAssociationFormBlockInitializer.tsx | 75 ++- ...ordAssociationGridCardBlockInitializer.tsx | 30 +- .../RecordAssociationListBlockInitializer.tsx | 27 +- ...dPrettyAssociationFormBlockInitializer.tsx | 69 ++- .../client/src/schema-initializer/utils.ts | 275 +++++++-- .../plugin-calendar/src/client/index.tsx | 41 +- .../items/CalendarBlockInitializer.tsx | 24 +- ...ordAssociationCalendarBlockInitializer.tsx | 83 +++ .../fields/manyToOne/templatesOfBug.ts | 6 +- .../src/client/index.ts | 14 +- .../src/client/instruction/forms/create.tsx | 2 +- .../src/client/instruction/forms/update.tsx | 2 +- 55 files changed, 1511 insertions(+), 573 deletions(-) create mode 100644 packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts diff --git a/packages/core/client/src/application/schema-initializer/components/SchemaInitializerChildren.tsx b/packages/core/client/src/application/schema-initializer/components/SchemaInitializerChildren.tsx index fb4342981..8a0642af4 100644 --- a/packages/core/client/src/application/schema-initializer/components/SchemaInitializerChildren.tsx +++ b/packages/core/client/src/application/schema-initializer/components/SchemaInitializerChildren.tsx @@ -49,7 +49,10 @@ export const SchemaInitializerChild: FC = memo((props const componentVal = Component || component; const isBuiltType = !componentVal && type && typeComponentMap[type]; - const componentChildren = useChildrenRes || children; + const componentChildren = useMemo(() => { + const res = [...(useChildrenRes || []), ...(children || [])]; + return res.length === 0 ? undefined : res; + }, [useChildrenRes, children]); const contextValue = useMemo(() => { return { ...others, diff --git a/packages/core/client/src/application/schema-initializer/components/SchemaInitializerSubMenu.tsx b/packages/core/client/src/application/schema-initializer/components/SchemaInitializerSubMenu.tsx index 98daa7019..bbbad5035 100644 --- a/packages/core/client/src/application/schema-initializer/components/SchemaInitializerSubMenu.tsx +++ b/packages/core/client/src/application/schema-initializer/components/SchemaInitializerSubMenu.tsx @@ -50,6 +50,9 @@ export const SchemaInitializerMenu: FC = (props) => { .ant-menu-sub { max-height: 50vh !important; } + .ant-menu-item { + margin-block: 0; + } .ant-menu-root { margin: 0 -${token.margin}px; .ant-menu-submenu-title { diff --git a/packages/core/client/src/application/schema-initializer/hooks/index.tsx b/packages/core/client/src/application/schema-initializer/hooks/index.tsx index f534e9e3d..64cd3d30f 100644 --- a/packages/core/client/src/application/schema-initializer/hooks/index.tsx +++ b/packages/core/client/src/application/schema-initializer/hooks/index.tsx @@ -41,6 +41,7 @@ export function useGetSchemaInitializerMenuItems(onClick?: (args: any) => void) return { key: item.key, label: element, + associationField: item.associationField, }; } if (item.type === 'itemGroup') { @@ -73,6 +74,7 @@ export function useGetSchemaInitializerMenuItems(onClick?: (args: any) => void) return { key, label, + associationField: item.associationField, onClick: (info) => { if (info.key !== key) return; if (item.onClick) { diff --git a/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx b/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx index 1db8f2feb..bbf4a5859 100644 --- a/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx +++ b/packages/core/client/src/application/schema-settings/components/SchemaSettingsChildren.tsx @@ -84,7 +84,10 @@ export const SchemaSettingsChild: FC = memo((props) => { const useChildrenRes = useChildren(); const useComponentPropsRes = useComponentProps(); const findComponent = useFindComponent(); - const componentChildren = useChildrenRes || children; + const componentChildren = useMemo(() => { + const res = [...(useChildrenRes || []), ...(children || [])]; + return res.length === 0 ? undefined : res; + }, [useChildrenRes, children]); const visibleResult = useVisible(); const ComponentValue = useMemo(() => { return !Component && type && typeComponentMap[type] ? typeComponentMap[type] : Component; diff --git a/packages/core/client/src/block-provider/BlockProvider.tsx b/packages/core/client/src/block-provider/BlockProvider.tsx index 13f2229b6..483d48b20 100644 --- a/packages/core/client/src/block-provider/BlockProvider.tsx +++ b/packages/core/client/src/block-provider/BlockProvider.tsx @@ -346,7 +346,7 @@ export const useParamsFromRecord = () => { const obj = { filterByTk: filterByTk, }; - if (record.__collection && !['oho', 'm2o', 'obo'].includes(collectionField?.interface)) { + if (record.__collection && collectionField && !['oho', 'm2o', 'obo'].includes(collectionField.interface)) { obj['targetCollection'] = record.__collection; } if (!filterByTk) { diff --git a/packages/core/client/src/block-provider/DetailsBlockProvider.tsx b/packages/core/client/src/block-provider/DetailsBlockProvider.tsx index 36a2a95b0..97edf2075 100644 --- a/packages/core/client/src/block-provider/DetailsBlockProvider.tsx +++ b/packages/core/client/src/block-provider/DetailsBlockProvider.tsx @@ -19,11 +19,11 @@ const InternalDetailsBlockProvider = (props) => { createForm({ readPretty, }), - [], + [readPretty], ); const { resource, service } = useBlockRequestContext(); const parentRecord = useCollectionParentRecord(); - const currentRecord = service?.data?.data?.[0] || {}; + const currentRecord = (action === 'list' ? service?.data?.data?.[0] : service?.data?.data) || {}; const detailsBLockValue = useMemo(() => { return { action, @@ -65,6 +65,9 @@ export const DetailsBlockProvider = (props) => { ); }; +/** + * @deprecated + */ export const useDetailsBlockContext = () => { return useContext(DetailsBlockContext); }; @@ -73,14 +76,15 @@ export const useDetailsBlockProps = () => { const ctx = useDetailsBlockContext(); useEffect(() => { if (!ctx.service.loading) { + const data = ctx.action === 'list' ? ctx.service?.data?.data?.[0] : ctx.service?.data?.data; ctx.form .reset() .then(() => { - ctx.form.setValues(ctx.service?.data?.data?.[0] || {}); + ctx.form.setValues(data || {}); }) .catch(console.error); } - }, [ctx.form, ctx.service?.data?.data, ctx.service.loading]); + }, [ctx.action, ctx.form, ctx.service?.data?.data, ctx.service.loading]); return { form: ctx.form, }; diff --git a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx index 37c88569c..c8d361d61 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx @@ -133,9 +133,5 @@ export const BlockRequestProvider: FC = ({ children }) => { export const useDataBlockRequest = (): UseRequestResult<{ data: T }> => { const context = useContext(BlockRequestContext); - if (!context) { - throw new Error('useDataBlockRequest() must be used within a DataBlockRequestProvider'); - } - return context; }; diff --git a/packages/core/client/src/locale/en_US.json b/packages/core/client/src/locale/en_US.json index e3c4e97b4..cc614f0c9 100644 --- a/packages/core/client/src/locale/en_US.json +++ b/packages/core/client/src/locale/en_US.json @@ -358,6 +358,10 @@ "Add tab": "Add tab", "Disable tabs": "Disable tabs", "Details": "Details", + "Edit form": "Edit form", + "Create form": "Create form", + "Form (Edit)": "Form (Edit)", + "Form (Add new)": "Form (Add new)", "Edit tab": "Edit tab", "Relationship blocks": "Relationship blocks", "Select record": "Select record", diff --git a/packages/core/client/src/locale/es_ES.json b/packages/core/client/src/locale/es_ES.json index af55655a2..d60ad1632 100644 --- a/packages/core/client/src/locale/es_ES.json +++ b/packages/core/client/src/locale/es_ES.json @@ -331,6 +331,10 @@ "Add tab": "Añadir pestaña", "Disable tabs": "Desactivar pestañas", "Details": "Detalles", + "Edit form": "Editar formulario", + "Create form": "Crear formulario", + "Form (Edit)": "Formulario (Editar)", + "Form (Add new)": "Formulario (Añadir nuevo)", "Edit tab": "Editar pestaña", "Relationship blocks": "Bloques de relación", "Select record": "Seleccionar registro", diff --git a/packages/core/client/src/locale/fr_FR.json b/packages/core/client/src/locale/fr_FR.json index 852c37671..257ae5c2d 100644 --- a/packages/core/client/src/locale/fr_FR.json +++ b/packages/core/client/src/locale/fr_FR.json @@ -346,6 +346,10 @@ "Add tab": "Ajouter un onglet", "Disable tabs": "Désactiver les onglets", "Details": "Détails", + "Edit form": "Modifier le formulaire", + "Create form": "Créer un formulaire", + "Form (Edit)": "Formulaire (Modifier)", + "Form (Add new)": "Formulaire (Ajouter nouveau)", "Edit tab": "Modifier l'onglet", "Relationship blocks": "Blocs de relations", "Select record": "Sélectionner un enregistrement", diff --git a/packages/core/client/src/locale/ja_JP.json b/packages/core/client/src/locale/ja_JP.json index f70622c4c..b6bd69884 100644 --- a/packages/core/client/src/locale/ja_JP.json +++ b/packages/core/client/src/locale/ja_JP.json @@ -293,6 +293,10 @@ "Add tab": "タブを追加", "Disable tabs": "タブを無効にする", "Details": "詳細", + "Edit form": "フォームを編集", + "Create form": "フォームを作成", + "Form (Edit)": "フォーム (編集)", + "Form (Add new)": "フォーム (新規追加)", "Edit tab": "タブを編集", "Relationship blocks": "関連付けされたブロック", "Select record": "レコードを選択", diff --git a/packages/core/client/src/locale/ko_KR.json b/packages/core/client/src/locale/ko_KR.json index 5d0da90cd..f3c00c4e8 100644 --- a/packages/core/client/src/locale/ko_KR.json +++ b/packages/core/client/src/locale/ko_KR.json @@ -378,6 +378,10 @@ "Add tab": "탭 추가", "Disable tabs": "탭 비활성화", "Details": "세부 정보", + "Edit form": "폼 편집", + "Create form": "폼 생성", + "Form (Edit)": "폼 (편집)", + "Form (Add new)": "폼 (새로 추가)", "Edit tab": "탭 편집", "Relationship blocks": "관계 데이터 블록", "Select record": "레코드 선택", diff --git a/packages/core/client/src/locale/pt_BR.json b/packages/core/client/src/locale/pt_BR.json index 956dbd6ff..642e79221 100644 --- a/packages/core/client/src/locale/pt_BR.json +++ b/packages/core/client/src/locale/pt_BR.json @@ -295,6 +295,10 @@ "Add tab": "Adicionar guia", "Disable tabs": "Desativar guias", "Details": "Detalhes", + "Edit form": "Editar formulário", + "Create form": "Criar formulário", + "Form (Edit)": "Formulário (Editar)", + "Form (Add new)": "Formulário (Adicionar novo)", "Edit tab": "Editar guia", "Relationship blocks": "Blocos de relacionamento", "Select record": "Selecionar registro", diff --git a/packages/core/client/src/locale/ru_RU.json b/packages/core/client/src/locale/ru_RU.json index cc7aac53f..3411341e1 100644 --- a/packages/core/client/src/locale/ru_RU.json +++ b/packages/core/client/src/locale/ru_RU.json @@ -233,6 +233,10 @@ "Add tab": "Добавить вкладку", "Disable tabs": "Запретить вкладки", "Details": "Подробности", + "Edit form": "Изменить форму", + "Create form": "Создать форму", + "Form (Edit)": "Форма (Изменить)", + "Form (Add new)": "Форма (Добавить новый)", "Edit tab": "Изменить вкладку", "Relationship blocks": "Блоки отношений", "Select record": "Выбрать запись", diff --git a/packages/core/client/src/locale/tr_TR.json b/packages/core/client/src/locale/tr_TR.json index 6a16fc2bd..ec2c67ee5 100644 --- a/packages/core/client/src/locale/tr_TR.json +++ b/packages/core/client/src/locale/tr_TR.json @@ -233,6 +233,10 @@ "Add tab": "Sekme ekle", "Disable tabs": "Sekmeleri pasifleştir", "Details": "Detaylar", + "Edit form": "Formu düzenle", + "Create form": "Form oluştur", + "Form (Edit)": "Form (Düzenle)", + "Form (Add new)": "Form (Yeni ekle)", "Edit tab": "Sekme düzenle", "Relationship blocks": "İlişki blokları", "Select record": "Kayıt seç", diff --git a/packages/core/client/src/locale/uk_UA.json b/packages/core/client/src/locale/uk_UA.json index a9fcf430b..da860d5da 100644 --- a/packages/core/client/src/locale/uk_UA.json +++ b/packages/core/client/src/locale/uk_UA.json @@ -348,6 +348,10 @@ "Add tab": "Додати вкладку", "Disable tabs": "Вимкнути вкладки", "Details": "Деталі", + "Edit form": "Редагувати форму", + "Create form": "Створити форму", + "Form (Edit)": "Форма (Редагувати)", + "Form (Add new)": "Форма (Додати новий)", "Edit tab": "Редагувати вкладку", "Relationship blocks": "Блоки відношень", "Select record": "Вибрати запис", diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index d4ad16f75..2a5c4c32c 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -379,6 +379,10 @@ "Add tab": "添加标签页", "Disable tabs": "禁用标签页", "Details": "详情", + "Edit form": "编辑表单", + "Create form": "创建表单", + "Form (Edit)": "表单(编辑)", + "Form (Add new)": "表单(添加)", "Edit tab": "编辑标签页", "Relationship blocks": "关系数据区块", "Select record": "选择数据", diff --git a/packages/core/client/src/locale/zh-TW.json b/packages/core/client/src/locale/zh-TW.json index 5af1598dc..4f8c1727a 100644 --- a/packages/core/client/src/locale/zh-TW.json +++ b/packages/core/client/src/locale/zh-TW.json @@ -378,6 +378,10 @@ "Add tab": "新增標籤頁", "Disable tabs": "停用標籤頁", "Details": "詳情", + "Edit form": "編輯表單", + "Create form": "建立表單", + "Form (Edit)": "表單(編輯)", + "Form (Add new)": "表單(添加)", "Edit tab": "編輯標籤頁", "Relationship blocks": "關聯資料區塊", "Select record": "選擇資料", diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/DetailsBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/details-multi/DetailsBlockInitializer.tsx index ae075be78..361f73521 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-multi/DetailsBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/DetailsBlockInitializer.tsx @@ -4,8 +4,36 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { createDetailsBlockSchema } from '../../../../schema-initializer/utils'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; +import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; -export const DetailsBlockInitializer = () => { +export const DetailsBlockInitializer = ({ + filterCollections, + onlyCurrentDataSource, + hideSearch, + componentType = 'Details', + createBlockSchema, + templateWrap, + showAssociationFields, +}: { + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource: boolean; + hideSearch?: boolean; + /** + * 页面中的详情区块类型是 Details,弹窗中的详情区块类型是 ReadPrettyFormItem; + * 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。 + */ + componentType?: 'Details' | 'ReadPrettyFormItem'; + createBlockSchema?: (options: any) => any; + templateWrap?: ( + templateSchema: any, + { + item, + }: { + item: any; + }, + ) => any; + showAssociationFields?: boolean; +}) => { const { insert } = useSchemaInitializer(); const { getCollection } = useCollectionManager_deprecated(); const itemConfig = useSchemaInitializerItem(); @@ -13,8 +41,13 @@ export const DetailsBlockInitializer = () => { } - componentType={'Details'} - onCreateBlockSchema={async ({ item }) => { + componentType={componentType} + onCreateBlockSchema={async (options) => { + if (createBlockSchema) { + return createBlockSchema(options); + } + + const { item } = options; const collection = getCollection(item.name, item.dataSource); const schema = createDetailsBlockSchema({ collection: item.name, @@ -28,6 +61,11 @@ export const DetailsBlockInitializer = () => { }); insert(schema); }} + onlyCurrentDataSource={!!onlyCurrentDataSource} + hideSearch={hideSearch} + filter={filterCollections} + templateWrap={templateWrap} + showAssociationFields={showAssociationFields} /> ); }; diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaInitializer.test.ts index bfaa5c77d..3e1243559 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaInitializer.test.ts @@ -10,8 +10,9 @@ test.describe('where multi data details block can be added', () => { }); test.describe('configure fields', () => { - test('display collection fields & display association fields & add text', async ({ page, mockPage }) => { + test('display collection fields & display association fields & add text', async ({ page, mockPage, mockRecord }) => { await mockPage(oneEmptyDetailsBlock).goto(); + await mockRecord('general'); const formItemInitializer = page.getByLabel('schema-initializer-Grid-details:configureFields-general'); @@ -60,8 +61,9 @@ test.describe('configure fields', () => { }); test.describe('configure actions', () => { - test('edit & delete & duplicate', async ({ page, mockPage }) => { + test('edit & delete & duplicate', async ({ page, mockPage, mockRecord }) => { await mockPage(oneEmptyDetailsBlock).goto(); + await mockRecord('general'); await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover(); await page.getByRole('menuitem', { name: 'Edit' }).click(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts index 2ba21a02d..d2acc5228 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/__e2e__/schemaSettings.test.ts @@ -29,8 +29,9 @@ test.describe('multi data details block schema settings', () => { }); test.describe('actions schema settings', () => { - test('edit & delete & duplicate', async ({ page, mockPage }) => { + test('edit & delete & duplicate', async ({ page, mockPage, mockRecord }) => { await mockPage(oneEmptyDetailsBlock).goto(); + await mockRecord('general'); // 创建 Edit & Delete 两个按钮 await page.getByLabel('schema-initializer-ActionBar-detailsWithPaging:configureActions-general').hover(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx index 946574ee1..35782e788 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx @@ -1,75 +1,85 @@ import { FormOutlined } from '@ant-design/icons'; -import React from 'react'; +import React, { useCallback } from 'react'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider'; import { useCollection_deprecated } from '../../../../collection-manager'; import { useSchemaTemplateManager } from '../../../../schema-templates'; -import { - createReadPrettyFormBlockSchema, - useRecordCollectionDataSourceItems, -} from '../../../../schema-initializer/utils'; +import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; export const RecordReadPrettyFormBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); - const { - onCreateBlockSchema, - componentType, - createBlockSchema, - icon = true, - targetCollection, - ...others - } = itemConfig; - const { insert } = useSchemaInitializer(); - const { getTemplateSchemaByMode } = useSchemaTemplateManager(); + const { icon = true, targetCollection, ...others } = itemConfig; const currentCollection = useCollection_deprecated(); const collection = targetCollection || currentCollection; - const association = useBlockAssociationContext(); - const { block } = useBlockRequestContext(); - const actionInitializers = - block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null; + const { createSingleDetailsSchema } = useCreateSingleDetailsSchema(); return ( } {...others} - onClick={async ({ item }) => { - if (item.template) { - const s = await getTemplateSchemaByMode(item); - if (item.template.componentName === 'ReadPrettyFormItem') { - const blockSchema = createReadPrettyFormBlockSchema({ - actionInitializers, - association, - collection: collection.name, - dataSource: collection.dataSource, - action: 'get', - useSourceId: '{{ useSourceIdFromParentRecord }}', - useParams: '{{ useParamsFromRecord }}', - template: s, - settings: 'blockSettings:singleDataDetails', - }); - if (item.mode === 'reference') { - blockSchema['x-template-key'] = item.template.key; - } - insert(blockSchema); - } else { - insert(s); - } - } else { - insert( - createReadPrettyFormBlockSchema({ - actionInitializers, - association, - collection: collection.name, - dataSource: collection.dataSource, - action: 'get', - useSourceId: '{{ useSourceIdFromParentRecord }}', - useParams: '{{ useParamsFromRecord }}', - settings: 'blockSettings:singleDataDetails', - }), - ); - } - }} + onClick={(options) => createSingleDetailsSchema(options)} items={useRecordCollectionDataSourceItems('ReadPrettyFormItem', null, collection?.name)} /> ); }; + +export function useCreateSingleDetailsSchema() { + const itemConfig = useSchemaInitializerItem(); + const { insert } = useSchemaInitializer(); + const { getTemplateSchemaByMode } = useSchemaTemplateManager(); + const association = useBlockAssociationContext(); + const { block } = useBlockRequestContext(); + const actionInitializers = + block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null; + + const templateWrap = useCallback( + (templateSchema, options) => { + const { item } = options; + if (item.template.componentName === 'ReadPrettyFormItem') { + const blockSchema = createDetailsBlockSchema({ + actionInitializers, + association, + collection: item.collectionName || item.name, + dataSource: item.dataSource, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + template: templateSchema, + settings: 'blockSettings:singleDataDetails', + }); + if (item.mode === 'reference') { + blockSchema['x-template-key'] = item.template.key; + } + return blockSchema; + } else { + return templateSchema; + } + }, + [actionInitializers, association], + ); + + const createSingleDetailsSchema = useCallback( + async ({ item }) => { + if (item.template) { + const template = await getTemplateSchemaByMode(item); + insert(templateWrap(template, { item })); + } else { + insert( + createDetailsBlockSchema({ + actionInitializers, + association, + collection: item.collectionName || item.name, + dataSource: item.dataSource, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + settings: 'blockSettings:singleDataDetails', + }), + ); + } + }, + [actionInitializers, association, getTemplateSchemaByMode, insert, templateWrap], + ); + + return { createSingleDetailsSchema, templateWrap }; +} diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/data-blocks/details-single/__e2e__/schemaInitializer.test.ts index afcef7dfb..811248edc 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-single/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/__e2e__/schemaInitializer.test.ts @@ -8,10 +8,11 @@ test.describe('where single data details block can be added', () => { await page.getByLabel('action-Action.Link-View-view-general-table-0').click(); await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'Details' }).click(); + await page.getByRole('menuitem', { name: 'Details' }).hover(); + await page.getByRole('menuitem', { name: 'General' }).click(); await page.mouse.move(300, 0); - await expect(page.getByLabel('block-item-CardItem-general-form')).toBeVisible(); + await expect(page.getByLabel('block-item-CardItem-general-details')).toBeVisible(); }); }); @@ -23,38 +24,39 @@ test.describe('configure actions', () => { await page.getByLabel('action-Action.Link-View-view-general-table-0').click(); await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'Details' }).click(); + await page.getByRole('menuitem', { name: 'Details' }).hover(); + await page.getByRole('menuitem', { name: 'General' }).click(); await page.mouse.move(300, 0); // create edit ------------------------------------------------------------------------------------ await createAction(page, 'Edit'); - await expect(page.getByLabel('action-Action-Edit-update-general-form-0')).toBeVisible(); + await expect(page.getByLabel('action-Action-Edit-update-')).toBeVisible(); await expectSettingsMenu({ page, showMenu: async () => { - await page.getByLabel('action-Action-Edit-update-general-form-0').hover(); + await page.getByLabel('action-Action-Edit-update-').hover(); await page.getByRole('button', { name: 'designer-schema-settings-Action-actionSettings:edit-general' }).hover(); }, supportedOptions: ['Edit button', 'Linkage rules', 'Open mode', 'Popup size', 'Delete'], }); - await deleteAction(page, 'action-Action-Edit-update-general-form-0'); + await deleteAction(page, 'action-Action-Edit-update-'); // create delete ------------------------------------------------------------------------------------ await createAction(page, 'Delete'); - await expect(page.getByLabel('action-Action-Delete-destroy-general-form-0')).toBeVisible(); + await expect(page.getByLabel('action-Action-Delete-destroy-general-details-')).toBeVisible(); // create print await createAction(page, 'Print'); - await expect(page.getByLabel('action-Action-Print-print-general-form-0')).toBeVisible(); + await expect(page.getByLabel('action-Action-Print-print-')).toBeVisible(); // create customize actions ---------------------------------------------------------------------------- // Popup await createCustomAction(page, 'Popup'); - await expect(page.getByLabel('action-Action-Popup-customize:popup-general-form-0')).toBeVisible(); + await expect(page.getByLabel('action-Action-Popup-customize')).toBeVisible(); // Update record await createCustomAction(page, 'Update record'); - await expect(page.getByLabel('action-Action-Update record-customize:update-general-form-0')).toBeVisible(); + await expect(page.getByLabel('action-Action-Update record-')).toBeVisible(); }); }); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/FormBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/form/FormBlockInitializer.tsx index 96d53e497..1e195fe5c 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/FormBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/form/FormBlockInitializer.tsx @@ -1,19 +1,69 @@ import { FormOutlined } from '@ant-design/icons'; -import React from 'react'; -import { useSchemaInitializerItem } from '../../../../application'; +import React, { useCallback } from 'react'; +import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { createFormBlockSchema } from '../../../../schema-initializer/utils'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; +import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; -export const FormBlockInitializer = () => { +export const FormBlockInitializer = ({ + filterCollections, + onlyCurrentDataSource, + hideSearch, + createBlockSchema, + componentType = 'FormItem', + templateWrap, + showAssociationFields, +}: { + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource: boolean; + hideSearch?: boolean; + createBlockSchema?: (options: any) => any; + /** + * 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。 + */ + componentType?: 'FormItem'; + templateWrap?: ( + templateSchema: any, + { + item, + }: { + item: any; + }, + ) => any; + showAssociationFields?: boolean; +}) => { + const { insert } = useSchemaInitializer(); const itemConfig = useSchemaInitializerItem(); const { isCusomeizeCreate } = itemConfig; + const onCreateFormBlockSchema = useCallback( + ({ item }) => { + if (createBlockSchema) { + return createBlockSchema({ item }); + } + + insert( + createFormBlockSchema({ + collection: item.collectionName || item.name, + dataSource: item.dataSource, + isCusomeizeCreate, + settings: 'blockSettings:createForm', + }), + ); + }, + [createBlockSchema, insert, isCusomeizeCreate], + ); + return ( } - componentType={'FormItem'} + componentType={componentType} templateWrap={(templateSchema, { item }) => { - const s = createFormBlockSchema({ + if (templateWrap) { + return templateWrap(templateSchema, { item }); + } + + const schema = createFormBlockSchema({ isCusomeizeCreate, dataSource: item.dataSource, template: templateSchema, @@ -21,11 +71,15 @@ export const FormBlockInitializer = () => { settings: 'blockSettings:createForm', }); if (item.template && item.mode === 'reference') { - s['x-template-key'] = item.template.key; + schema['x-template-key'] = item.template.key; } - return s; + return schema; }} - createBlockSchema={createFormBlockSchema} + onCreateBlockSchema={onCreateFormBlockSchema} + filter={filterCollections} + onlyCurrentDataSource={onlyCurrentDataSource} + hideSearch={hideSearch} + showAssociationFields={showAssociationFields} /> ); }; diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx index 3fa04e006..a080b596d 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx @@ -1,61 +1,87 @@ import { FormOutlined } from '@ant-design/icons'; -import React from 'react'; +import React, { useCallback } from 'react'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useBlockAssociationContext } from '../../../../block-provider'; import { useCollection_deprecated } from '../../../../collection-manager'; import { useSchemaTemplateManager } from '../../../../schema-templates'; import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils'; +/** + * @deprecated + */ export const RecordFormBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); - const { onCreateBlockSchema, componentType, createBlockSchema, targetCollection, ...others } = itemConfig; + const { targetCollection, ...others } = itemConfig; const { insert } = useSchemaInitializer(); const { getTemplateSchemaByMode } = useSchemaTemplateManager(); const currentCollection = useCollection_deprecated(); const collection = targetCollection || currentCollection; - const association = useBlockAssociationContext(); + + const { createEditFormBlock, templateWrap } = useCreateEditFormBlock(); + return ( } {...others} onClick={async ({ item }) => { if (item.template) { - const s = await getTemplateSchemaByMode(item); - if (item.template.componentName === 'FormItem') { - const blockSchema = createFormBlockSchema({ - association, - collection: collection.name, - dataSource: collection.dataSource, - action: 'get', - useSourceId: '{{ useSourceIdFromParentRecord }}', - useParams: '{{ useParamsFromRecord }}', - actionInitializers: 'editForm:configureActions', - template: s, - settings: 'blockSettings:editForm', - }); - if (item.mode === 'reference') { - blockSchema['x-template-key'] = item.template.key; - } - insert(blockSchema); - } else { - insert(s); - } + const template = await getTemplateSchemaByMode(item); + insert(templateWrap(template, { item })); } else { - insert( - createFormBlockSchema({ - association, - collection: collection.name, - dataSource: collection.dataSource, - action: 'get', - useSourceId: '{{ useSourceIdFromParentRecord }}', - useParams: '{{ useParamsFromRecord }}', - actionInitializers: 'editForm:configureActions', - settings: 'blockSettings:editForm', - }), - ); + createEditFormBlock({ item }); } }} items={useRecordCollectionDataSourceItems('FormItem', null, collection?.name)} /> ); }; + +export function useCreateEditFormBlock() { + const { insert } = useSchemaInitializer(); + const association = useBlockAssociationContext(); + + const createEditFormBlock = useCallback( + ({ item }) => { + insert( + createFormBlockSchema({ + association, + collection: item.collectionName || item.name, + dataSource: item.dataSource, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + actionInitializers: 'editForm:configureActions', + settings: 'blockSettings:editForm', + }), + ); + }, + [association, insert], + ); + + const templateWrap = useCallback( + (templateSchema, { item }) => { + if (item.template.componentName === 'FormItem') { + const blockSchema = createFormBlockSchema({ + association, + collection: item.collectionName || item.name, + dataSource: item.dataSource, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + actionInitializers: 'editForm:configureActions', + template: templateSchema, + settings: 'blockSettings:editForm', + }); + if (item.mode === 'reference') { + blockSchema['x-template-key'] = item.template.key; + } + return blockSchema; + } else { + return templateSchema; + } + }, + [association], + ); + + return { createEditFormBlock, templateWrap }; +} diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts index cb311d787..738b7d6a1 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts @@ -1183,7 +1183,7 @@ test.describe('creation form block schema settings', () => { //在编辑操作中使用引用模板 await page.getByLabel('action-Action.Link-Edit-update-users-table-0').click(); await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-users').click(); - await page.getByRole('menuitem', { name: 'form Form' }).first().hover(); + await page.getByRole('menuitem', { name: 'form Form (Edit)' }).first().hover(); await page.getByRole('menuitem', { name: 'Reference template' }).hover(); await page.getByRole('menuitem', { name: 'Users_Form (Fields only)' }).first().click(); await page.mouse.move(300, 0); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/templatesOfBug.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/templatesOfBug.ts index 41afc0d6d..adf352e49 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/templatesOfBug.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/templatesOfBug.ts @@ -5499,7 +5499,7 @@ export const T3469: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'BlockInitializers', + 'x-initializer': 'page:addBlock', 'x-index': 1, properties: { isaxpp1w32b: { @@ -5551,7 +5551,7 @@ export const T3469: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'FormItemInitializers', + 'x-initializer': 'form:configureFields', 'x-index': 1, properties: { e4ke3wqxm2o: { @@ -5710,7 +5710,7 @@ export const T3529: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'BlockInitializers', + 'x-initializer': 'page:addBlock', properties: { l62athlwjbe: { _isJSONSchemaObject: true, @@ -5752,7 +5752,7 @@ export const T3529: PageConfig = { _isJSONSchemaObject: true, version: '2.0', type: 'void', - 'x-initializer': 'TableActionInitializers', + 'x-initializer': 'table:configureActions', 'x-component': 'ActionBar', 'x-component-props': { style: { @@ -5767,7 +5767,7 @@ export const T3529: PageConfig = { _isJSONSchemaObject: true, version: '2.0', type: 'array', - 'x-initializer': 'TableColumnInitializers', + 'x-initializer': 'table:configureColumns', 'x-component': 'TableV2', 'x-component-props': { rowKey: 'id', @@ -5786,7 +5786,7 @@ export const T3529: PageConfig = { 'x-decorator': 'TableV2.Column.ActionBar', 'x-component': 'TableV2.Column', 'x-designer': 'TableV2.ActionColumnDesigner', - 'x-initializer': 'TableActionColumnInitializers', + 'x-initializer': 'table:configureItemActions', properties: { '7glkmum3znh': { _isJSONSchemaObject: true, @@ -5847,7 +5847,7 @@ export const T3529: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'RecordBlockInitializers', + 'x-initializer': 'popup:common:addBlock', properties: { '5ubuvb82lbu': { _isJSONSchemaObject: true, @@ -5898,7 +5898,7 @@ export const T3529: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'FormItemInitializers', + 'x-initializer': 'form:configureFields', 'x-uid': 'aigmsbzdyeb', 'x-async': false, 'x-index': 1, @@ -5907,7 +5907,7 @@ export const T3529: PageConfig = { _isJSONSchemaObject: true, version: '2.0', type: 'void', - 'x-initializer': 'CreateFormActionInitializers', + 'x-initializer': 'createForm:configureActions', 'x-component': 'ActionBar', 'x-component-props': { layout: 'one-column', diff --git a/packages/core/client/src/modules/blocks/data-blocks/grid-card/GridCardBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/grid-card/GridCardBlockInitializer.tsx index d8c41e53f..c483b9710 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/grid-card/GridCardBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/grid-card/GridCardBlockInitializer.tsx @@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { createGridCardBlockSchema } from '../../../../schema-initializer/utils'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; +import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; -export const GridCardBlockInitializer = () => { +export const GridCardBlockInitializer = ({ + filterCollections, + onlyCurrentDataSource, + hideSearch, + createBlockSchema, + componentType = 'FormItem', + templateWrap, + showAssociationFields, +}: { + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource: boolean; + hideSearch?: boolean; + createBlockSchema?: (options: any) => any; + /** + * 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。 + */ + componentType?: 'FormItem'; + templateWrap?: ( + templateSchema: any, + { + item, + }: { + item: any; + }, + ) => any; + showAssociationFields?: boolean; +}) => { const { insert } = useSchemaInitializer(); const { getCollection } = useCollectionManager_deprecated(); const itemConfig = useSchemaInitializerItem(); @@ -15,6 +42,10 @@ export const GridCardBlockInitializer = () => { icon={} componentType={'GridCard'} onCreateBlockSchema={async ({ item }) => { + if (createBlockSchema) { + return createBlockSchema({ item }); + } + const collection = getCollection(item.name, item.dataSource); const schema = createGridCardBlockSchema({ collection: item.name, @@ -24,6 +55,10 @@ export const GridCardBlockInitializer = () => { }); insert(schema); }} + onlyCurrentDataSource={onlyCurrentDataSource} + hideSearch={hideSearch} + filter={filterCollections} + showAssociationFields={showAssociationFields} /> ); }; diff --git a/packages/core/client/src/modules/blocks/data-blocks/list/ListBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/list/ListBlockInitializer.tsx index 9271db04f..cd74176d2 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/list/ListBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/list/ListBlockInitializer.tsx @@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { createListBlockSchema } from '../../../../schema-initializer/utils'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; +import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; -export const ListBlockInitializer = () => { +export const ListBlockInitializer = ({ + filterCollections, + onlyCurrentDataSource, + hideSearch, + createBlockSchema, + componentType = 'FormItem', + templateWrap, + showAssociationFields, +}: { + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource: boolean; + hideSearch?: boolean; + createBlockSchema?: (options: any) => any; + /** + * 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。 + */ + componentType?: 'FormItem'; + templateWrap?: ( + templateSchema: any, + { + item, + }: { + item: any; + }, + ) => any; + showAssociationFields?: boolean; +}) => { const { getCollection } = useCollectionManager_deprecated(); const { insert } = useSchemaInitializer(); const itemConfig = useSchemaInitializerItem(); @@ -15,6 +42,10 @@ export const ListBlockInitializer = () => { icon={} componentType={'List'} onCreateBlockSchema={async ({ item }) => { + if (createBlockSchema) { + return createBlockSchema({ item }); + } + const collection = getCollection(item.name, item.dataSource); const schema = createListBlockSchema({ collection: item.name, @@ -24,6 +55,10 @@ export const ListBlockInitializer = () => { }); insert(schema); }} + onlyCurrentDataSource={onlyCurrentDataSource} + hideSearch={hideSearch} + filter={filterCollections} + showAssociationFields={showAssociationFields} /> ); }; diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx index 7bdd89c42..56d38dfc9 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/table/TableBlockInitializer.tsx @@ -4,8 +4,30 @@ import { useCollectionManager_deprecated } from '../../../../collection-manager/ import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; import { createTableBlockSchema } from '../../../../schema-initializer/utils'; import React from 'react'; +import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; -export const TableBlockInitializer = () => { +export const TableBlockInitializer = ({ + filterCollections, + onlyCurrentDataSource, + hideSearch, + createBlockSchema, + templateWrap, + showAssociationFields, +}: { + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource: boolean; + hideSearch?: boolean; + createBlockSchema?: (options: any) => any; + templateWrap?: ( + templateSchema: any, + { + item, + }: { + item: any; + }, + ) => any; + showAssociationFields?: boolean; +}) => { const { insert } = useSchemaInitializer(); const { getCollection } = useCollectionManager_deprecated(); const itemConfig = useSchemaInitializerItem(); @@ -15,6 +37,10 @@ export const TableBlockInitializer = () => { icon={} componentType={'Table'} onCreateBlockSchema={async ({ item }) => { + if (createBlockSchema) { + return createBlockSchema({ item }); + } + const collection = getCollection(item.name, item.dataSource); const schema = createTableBlockSchema({ collection: item.name, @@ -23,6 +49,10 @@ export const TableBlockInitializer = () => { }); insert(schema); }} + onlyCurrentDataSource={onlyCurrentDataSource} + hideSearch={hideSearch} + filter={filterCollections} + showAssociationFields={showAssociationFields} /> ); }; diff --git a/packages/core/client/src/modules/blocks/filter-blocks/collapse/FilterCollapseBlockInitializer.tsx b/packages/core/client/src/modules/blocks/filter-blocks/collapse/FilterCollapseBlockInitializer.tsx index a7e7ad999..d5879ddd5 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/collapse/FilterCollapseBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/filter-blocks/collapse/FilterCollapseBlockInitializer.tsx @@ -4,14 +4,16 @@ import React from 'react'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { createCollapseBlockSchema } from '../../../../schema-initializer/utils'; import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer'; -import { Collection } from '../../../../data-source'; +import { Collection, CollectionFieldOptions } from '../../../../data-source'; export const FilterCollapseBlockInitializer = ({ - filterMenuItemChildren, + filterCollections, onlyCurrentDataSource, + showChildren, }: { - filterMenuItemChildren: (collection: Collection) => boolean; + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; onlyCurrentDataSource: boolean; + showChildren?: boolean; }) => { const itemConfig = useSchemaInitializerItem(); const { insert } = useSchemaInitializer(); @@ -31,7 +33,8 @@ export const FilterCollapseBlockInitializer = ({ }); insert(schema); }} - filter={filterMenuItemChildren} + filter={filterCollections} + showChildren={showChildren} /> ); }; diff --git a/packages/core/client/src/modules/blocks/filter-blocks/form/FilterFormBlockInitializer.tsx b/packages/core/client/src/modules/blocks/filter-blocks/form/FilterFormBlockInitializer.tsx index a6aa6e28f..60a966017 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/form/FilterFormBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/filter-blocks/form/FilterFormBlockInitializer.tsx @@ -1,18 +1,21 @@ import { FormOutlined } from '@ant-design/icons'; import React from 'react'; -import { useSchemaInitializerItem } from '../../../../application'; +import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { createFilterFormBlockSchema } from '../../../../schema-initializer/utils'; import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer'; -import { Collection } from '../../../../data-source'; +import { Collection, CollectionFieldOptions } from '../../../../data-source'; export const FilterFormBlockInitializer = ({ - filterMenuItemChildren, + filterCollections, onlyCurrentDataSource, + showChildren, }: { - filterMenuItemChildren: (collection: Collection) => boolean; + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; onlyCurrentDataSource: boolean; + showChildren?: boolean; }) => { const itemConfig = useSchemaInitializerItem(); + const { insert } = useSchemaInitializer(); return ( { - options = { ...options, settings: 'blockSettings:filterForm' }; - return createFilterFormBlockSchema(options); + onCreateBlockSchema={({ item }) => { + return insert( + createFilterFormBlockSchema({ + collection: item.collectionName || item.name, + dataSource: item.dataSource, + settings: 'blockSettings:filterForm', + }), + ); }} - filter={filterMenuItemChildren} + filter={filterCollections} + showChildren={showChildren} /> ); }; diff --git a/packages/core/client/src/modules/dialog/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/dialog/__e2e__/schemaInitializer.test.ts index 47f4e2567..210eb77e0 100644 --- a/packages/core/client/src/modules/dialog/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/dialog/__e2e__/schemaInitializer.test.ts @@ -77,10 +77,9 @@ test.describe('where to open a popup and what can be added to it', () => { await expect(page.getByText('test8')).toBeVisible(); // add blocks - await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'Details' }).click(); - await page.getByText('Form').first().click(); - await page.getByRole('menuitem', { name: 'Markdown' }).click(); + await addBlock(['table Details right', 'General']); + await addBlock(['form Form (Edit)']); + await addBlock(['Markdown']); await expect(page.getByText('GeneralConfigure actionsConfigure fields')).toBeVisible(); await expect(page.getByText('GeneralConfigure fieldsConfigure actions')).toBeVisible(); @@ -106,26 +105,18 @@ test.describe('where to open a popup and what can be added to it', () => { await page.getByRole('button', { name: 'OK', exact: true }).click(); // add relationship blocks - await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'Many to one' }).hover(); - await page.getByRole('menuitem', { name: 'Details' }).click(); - - await page.mouse.move(300, 0); - - await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'One to many' }).hover(); - - // 下拉列表中,可选择以下区块进行创建 - await expect(page.getByText('Table')).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible(); - await expect(page.getByText('Form').nth(1)).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'Calendar' })).toBeVisible(); - - await page.getByText('Table').click(); - await page.mouse.move(300, 0); + await addBlock(['table Details right', 'Many to one']); + await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible(); + await addBlock(['table Table right', 'One to many']); await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible(); + + async function addBlock(names: string[]) { + await page.getByLabel('schema-initializer-Grid-popup').hover(); + for (const name of names) { + await page.getByRole('menuitem', { name }).click(); + } + await page.mouse.move(300, 0); + } }); test('bulk edit', async ({ page, mockPage }) => { @@ -171,8 +162,11 @@ test.describe('where to open a popup and what can be added to it', () => { // add blocks await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'Details' }).click(); - await page.getByText('Form').first().click(); + await page.getByRole('menuitem', { name: 'Details' }).hover(); + await page.getByRole('menuitem', { name: 'General' }).click(); + await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); + await page.getByRole('menuitem', { name: 'form Form (Edit)' }).first().click(); + await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); await page.getByRole('menuitem', { name: 'Markdown' }).click(); await page.mouse.move(300, 0); @@ -181,27 +175,27 @@ test.describe('where to open a popup and what can be added to it', () => { await expect(page.getByLabel('block-item-Markdown.Void-general-markdown')).toBeVisible(); // add relationship blocks - await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'Many to one' }).hover(); - await page.getByRole('menuitem', { name: 'Details' }).click(); - await page.mouse.move(300, 0); - - await expect(page.getByLabel('block-item-CardItem-general-').nth(2)).toBeVisible(); - - await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); - await page.getByRole('menuitem', { name: 'One to many' }).hover(); - // 下拉列表中,可选择以下区块进行创建 - await expect(page.getByText('Table')).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'Details' }).nth(1)).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'List' })).toBeVisible(); - await expect(page.getByRole('menuitem', { name: 'Grid Card' })).toBeVisible(); - await expect(page.getByText('Form').nth(1)).toBeVisible(); + await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); + await expect(page.getByRole('menuitem', { name: 'table Details right' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'form Form (Edit)' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'form Form (Add new) right' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'form Form (Add new) right' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'table Table right' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'ordered-list List right' })).toBeVisible(); + await expect(page.getByRole('menuitem', { name: 'ordered-list Grid Card right' })).toBeVisible(); await expect(page.getByRole('menuitem', { name: 'Calendar' })).toBeVisible(); - await page.getByText('Table').click(); + await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); + await page.getByRole('menuitem', { name: 'Details' }).hover(); + await page.getByRole('menuitem', { name: 'Many to one' }).click(); await page.mouse.move(300, 0); + await expect(page.getByLabel('block-item-CardItem-users-')).toBeVisible(); + await page.getByLabel('schema-initializer-Grid-popup:common:addBlock-general').hover(); + await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'One to many' }).click(); + await page.mouse.move(300, 0); await expect(page.getByLabel('block-item-CardItem-users-table')).toBeVisible(); // 屏幕上没有显示错误提示 await expect(page.locator('.ant-notification-notice').first()).toBeHidden({ timeout: 1000 }); diff --git a/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts b/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts index e583bac73..2b89970b4 100644 --- a/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts +++ b/packages/core/client/src/modules/fields/component/DatePicker/__e2e__/utils.ts @@ -16,7 +16,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'BlockInitializers', + 'x-initializer': 'page:addBlock', properties: { irc4mhtog83: { _isJSONSchemaObject: true, @@ -58,7 +58,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = { _isJSONSchemaObject: true, version: '2.0', type: 'void', - 'x-initializer': 'TableActionInitializers', + 'x-initializer': 'table:configureActions', 'x-component': 'ActionBar', 'x-component-props': { style: { @@ -73,7 +73,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = { _isJSONSchemaObject: true, version: '2.0', type: 'array', - 'x-initializer': 'TableColumnInitializers', + 'x-initializer': 'table:configureColumns', 'x-component': 'TableV2', 'x-component-props': { rowKey: 'id', @@ -92,7 +92,7 @@ export const oneTableBlockWithDatetimeFields: PageConfig = { 'x-decorator': 'TableV2.Column.ActionBar', 'x-component': 'TableV2.Column', 'x-designer': 'TableV2.ActionColumnDesigner', - 'x-initializer': 'TableActionColumnInitializers', + 'x-initializer': 'table:configureItemActions', properties: { gk4b4e4wp8b: { _isJSONSchemaObject: true, @@ -192,7 +192,7 @@ export const oneFormBlockWithDatetimeFields: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'BlockInitializers', + 'x-initializer': 'page:addBlock', properties: { annx4g65mtu: { _isJSONSchemaObject: true, @@ -239,7 +239,7 @@ export const oneFormBlockWithDatetimeFields: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'FormItemInitializers', + 'x-initializer': 'form:configureFields', properties: { '269bdhc35j2': { _isJSONSchemaObject: true, diff --git a/packages/core/client/src/modules/fields/component/Picker/TableSelectorInitializers.tsx b/packages/core/client/src/modules/fields/component/Picker/TableSelectorInitializers.tsx index defd4cf93..403ea1d41 100644 --- a/packages/core/client/src/modules/fields/component/Picker/TableSelectorInitializers.tsx +++ b/packages/core/client/src/modules/fields/component/Picker/TableSelectorInitializers.tsx @@ -35,7 +35,7 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali title: '{{t("Form")}}', Component: 'FilterFormBlockInitializer', componentProps: { - filterMenuItemChildren() { + filterCollections() { return false; }, onlyCurrentDataSource: true, @@ -48,7 +48,7 @@ export const tableSelectorInitializers_deprecated = new CompatibleSchemaInitiali title: '{{t("Collapse")}}', Component: 'FilterCollapseBlockInitializer', componentProps: { - filterMenuItemChildren() { + filterCollections() { return false; }, onlyCurrentDataSource: true, @@ -117,7 +117,7 @@ export const tableSelectorInitializers = new CompatibleSchemaInitializer( title: '{{t("Form")}}', Component: 'FilterFormBlockInitializer', componentProps: { - filterMenuItemChildren() { + filterCollections() { return false; }, onlyCurrentDataSource: true, @@ -130,7 +130,7 @@ export const tableSelectorInitializers = new CompatibleSchemaInitializer( title: '{{t("Collapse")}}', Component: 'FilterCollapseBlockInitializer', componentProps: { - filterMenuItemChildren() { + filterCollections() { return false; }, onlyCurrentDataSource: true, diff --git a/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts b/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts new file mode 100644 index 000000000..40e17d3cb --- /dev/null +++ b/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts @@ -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(); + }); +}); diff --git a/packages/core/client/src/schema-component/antd/details/Details.tsx b/packages/core/client/src/schema-component/antd/details/Details.tsx index ef97a6a1e..14025ff9b 100644 --- a/packages/core/client/src/schema-component/antd/details/Details.tsx +++ b/packages/core/client/src/schema-component/antd/details/Details.tsx @@ -1,3 +1,16 @@ +import React from 'react'; import { FormV2 } from '../form-v2'; +import { useDetailsBlockContext } from '../../../block-provider/DetailsBlockProvider'; +import _ from 'lodash'; +import { Empty } from 'antd'; +import { useDataBlockRequest } from '../../../data-source'; -export const Details = FormV2; +export const Details = (props) => { + const request = useDataBlockRequest(); + + if (!request?.loading && _.isEmpty(request?.data?.data)) { + return ; + } + + return ; +}; diff --git a/packages/core/client/src/schema-component/antd/details/__tests__/details.test.tsx b/packages/core/client/src/schema-component/antd/details/__tests__/details.test.tsx index f55293290..5c38bb711 100644 --- a/packages/core/client/src/schema-component/antd/details/__tests__/details.test.tsx +++ b/packages/core/client/src/schema-component/antd/details/__tests__/details.test.tsx @@ -6,7 +6,6 @@ describe('Details', () => { it('should render correctly', () => { render(); - expect(screen.getByText('this is name')).toBeInTheDocument(); - expect(screen.getByText('this is age')).toBeInTheDocument(); + expect(screen.getByText('No data')).toBeInTheDocument(); }); }); diff --git a/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx b/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx index 141b070fb..e9fce3aad 100644 --- a/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx +++ b/packages/core/client/src/schema-initializer/buttons/RecordBlockInitializers.tsx @@ -1,9 +1,20 @@ -import { Schema, useFieldSchema } from '@formily/react'; -import { useMemo } from 'react'; -import { useCollectionManager_deprecated, useCollection_deprecated } from '../..'; -import { SchemaInitializerItemType, useSchemaInitializer } from '../../application'; +import { Schema } from '@formily/react'; +import { useCallback, useMemo } from 'react'; +import { + useCollection, + useCollectionManager_deprecated, + useCollection_deprecated, + useCreateAssociationDetailsBlock, + useCreateAssociationDetailsWithoutPagination, + useCreateAssociationFormBlock, + useCreateAssociationGridCardBlock, + useCreateAssociationListBlock, + useCreateAssociationTableBlock, + useCreateEditFormBlock, +} from '../..'; import { CompatibleSchemaInitializer } from '../../application/schema-initializer/CompatibleSchemaInitializer'; import { gridRowColWrap } from '../utils'; +import { useCreateSingleDetailsSchema } from '../../modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer'; const recursiveParent = (schema: Schema) => { if (!schema) return null; @@ -15,223 +26,233 @@ const recursiveParent = (schema: Schema) => { } }; -const useRelationFields = () => { - const fieldSchema = useFieldSchema(); - const { getCollectionFields } = useCollectionManager_deprecated(); - const collection = useCollection_deprecated(); - let fields = []; - - if (fieldSchema['x-initializer']) { - fields = collection.fields; - } else { - const collection = recursiveParent(fieldSchema.parent); - if (collection) { - fields = getCollectionFields(collection); - } - } - - const relationFields = fields - .filter((field) => ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface)) - .map((field) => { - if (['hasOne', 'belongsTo'].includes(field.type)) { - return { - name: field.name, - type: 'subMenu', - title: field?.uiSchema?.title || field.name, - children: [ - { - name: `${field.name}_details`, - type: 'item', - title: '{{t("Details")}}', - field, - Component: 'RecordReadPrettyAssociationFormBlockInitializer', - }, - // { - // name: `${field.name}_form`, - // type: 'item', - // title: '{{t("Form")}}', - // field, - // component: 'RecordAssociationFormBlockInitializer', - // }, - ], - }; - } - - if (['hasMany', 'belongsToMany'].includes(field.type)) { - return { - name: field.name, - type: 'subMenu', - title: field?.uiSchema?.title || field.name, - children: [ - { - name: `${field.name}_table`, - type: 'item', - title: '{{t("Table")}}', - field, - Component: 'RecordAssociationBlockInitializer', - }, - { - name: `${field.name}_details`, - type: 'item', - title: '{{t("Details")}}', - field, - Component: 'RecordAssociationDetailsBlockInitializer', - }, - { - name: `${field.name}_list`, - type: 'item', - title: '{{t("List")}}', - field, - Component: 'RecordAssociationListBlockInitializer', - }, - { - name: `${field.name}_grid_card`, - type: 'item', - title: '{{t("Grid Card")}}', - field, - Component: 'RecordAssociationGridCardBlockInitializer', - }, - { - name: `${field.name}_form`, - type: 'item', - title: '{{t("Form")}}', - field, - Component: 'RecordAssociationFormBlockInitializer', - }, - // TODO: This one should be append in the calendar plugin - { - name: `${field.name}_calendar`, - type: 'item', - title: '{{t("Calendar")}}', - field, - Component: 'RecordAssociationCalendarBlockInitializer', - }, - ], - }; - } - - return { - name: field.name, - type: 'item', - field, - title: field?.uiSchema?.title || field.name, - Component: 'RecordAssociationBlockInitializer', - }; - }) as any; - - return relationFields; -}; - -const useDetailCollections = (props) => { - const { actionInitializers, childrenCollections, collection } = props; - const detailCollections = [ - { - name: collection.name, - type: 'item', - title: collection?.title || collection.name, - Component: 'RecordReadPrettyFormBlockInitializer', - icon: false, - targetCollection: collection, - actionInitializers, - }, - ].concat( - childrenCollections.map((c) => { - return { - name: c.name, - type: 'item', - title: c?.title || c.name, - Component: 'RecordReadPrettyFormBlockInitializer', - icon: false, - targetCollection: c, - actionInitializers, - }; - }), - ) as SchemaInitializerItemType[]; - return detailCollections; -}; - -const useFormCollections = (props) => { - const { actionInitializers, childrenCollections, collection } = props; - const formCollections = [ - { - name: collection.name, - type: 'item', - title: collection?.title || collection.name, - Component: 'RecordFormBlockInitializer', - icon: false, - targetCollection: collection, - actionInitializers, - }, - ].concat( - childrenCollections.map((c) => { - return { - name: c.name, - type: 'item', - title: c?.title || c.name, - Component: 'RecordFormBlockInitializer', - icon: false, - targetCollection: c, - actionInitializers, - }; - }), - ) as SchemaInitializerItemType[]; - - return formCollections; +export const canMakeAssociationBlock = (field) => { + return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface); }; function useRecordBlocks() { - const { options } = useSchemaInitializer(); - const { actionInitializers } = options; const collection = useCollection_deprecated(); const { getChildrenCollections } = useCollectionManager_deprecated(); const collectionsWithView = getChildrenCollections(collection.name, true, collection.dataSource).filter( (v) => v?.filterTargetKey, ); - const hasChildCollection = collectionsWithView?.length > 0; - const modifyFlag = (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql'; - const detailChildren = useDetailCollections({ - ...options, - childrenCollections: collectionsWithView, - collection, - }); - const formChildren = useFormCollections({ - ...options, - childrenCollections: collectionsWithView, - collection, - }); - const res = []; - if (hasChildCollection) { - res.push({ - name: 'details', - type: 'subMenu', - title: '{{t("Details")}}', - children: detailChildren, - }); - } else { - res.push({ + const res: any[] = [ + { name: 'details', title: '{{t("Details")}}', - Component: 'RecordReadPrettyFormBlockInitializer', - actionInitializers, - }); - } + Component: 'DetailsBlockInitializer', + collectionName: collection.name, + dataSource: collection.dataSource, + useComponentProps() { + const currentCollection = useCollection_deprecated(); + const { createSingleDetailsSchema, templateWrap } = useCreateSingleDetailsSchema(); + const { createAssociationDetailsBlock } = useCreateAssociationDetailsBlock(); + const { + createAssociationDetailsWithoutPagination, + templateWrap: templateWrapOfAssociationDetailsWithoutPagination, + } = useCreateAssociationDetailsWithoutPagination(); + const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView]; + const createBlockSchema = useCallback( + ({ item }) => { + if (item.associationField) { + if (['hasOne', 'belongsTo'].includes(item.associationField.type)) { + return createAssociationDetailsWithoutPagination({ item }); + } + return createAssociationDetailsBlock({ item }); + } + return createSingleDetailsSchema({ item }); + }, + [createAssociationDetailsBlock, createAssociationDetailsWithoutPagination, createSingleDetailsSchema], + ); + return { + filterCollections({ collection, associationField }) { + if (collection) { + return collectionsNeedToDisplay.some((c) => c.name === collection.name); + } + if (associationField) { + return true; + } + return false; + }, + onlyCurrentDataSource: true, + // hideSearch: true, + componentType: 'ReadPrettyFormItem', + createBlockSchema, + templateWrap: useCallback( + (templateSchema, { item }) => { + if (item.associationField) { + if (['hasOne', 'belongsTo'].includes(item.associationField.type)) { + return templateWrapOfAssociationDetailsWithoutPagination(templateSchema, { item }); + } + return templateSchema; + } + return templateWrap(templateSchema, { item }); + }, + [templateWrap, templateWrapOfAssociationDetailsWithoutPagination], + ), + showAssociationFields: true, + }; + }, + }, + { + name: 'editForm', + title: '{{t("Form (Edit)")}}', + Component: 'FormBlockInitializer', + collectionName: collection.name, + dataSource: collection.dataSource, + useComponentProps() { + const currentCollection = useCollection_deprecated(); + const { createEditFormBlock, templateWrap } = useCreateEditFormBlock(); + const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView]; - if (hasChildCollection) { - res.push({ - name: 'form', - type: 'subMenu', - title: '{{t("Form")}}', - children: formChildren, - }); - } else { - modifyFlag && - res.push({ - name: 'form', - title: '{{t("Form")}}', - Component: 'RecordFormBlockInitializer', - }); - } + return { + filterCollections({ collection }) { + if (collection) { + return collectionsNeedToDisplay.some((c) => c.name === collection.name); + } + return false; + }, + onlyCurrentDataSource: true, + // hideSearch: true, + componentType: 'FormItem', + createBlockSchema: createEditFormBlock, + templateWrap: templateWrap, + showAssociationFields: true, + }; + }, + useVisible() { + return (collection.template !== 'view' || collection?.writableView) && collection.template !== 'sql'; + }, + }, + { + name: 'createForm', + title: '{{t("Form (Add new)")}}', + Component: 'FormBlockInitializer', + collectionName: collection.name, + dataSource: collection.dataSource, + useComponentProps() { + const { createAssociationFormBlock, templateWrap } = useCreateAssociationFormBlock(); + return { + filterCollections({ collection, associationField }) { + if (associationField) { + return ['hasMany', 'belongsToMany'].includes(associationField.type); + } + return false; + }, + onlyCurrentDataSource: true, + // hideSearch: true, + componentType: 'FormItem', + createBlockSchema: createAssociationFormBlock, + templateWrap: templateWrap, + showAssociationFields: true, + }; + }, + useVisible() { + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); + }, + }, + { + name: 'table', + title: '{{t("Table")}}', + Component: 'TableBlockInitializer', + useVisible() { + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); + }, + useComponentProps() { + const { createAssociationTableBlock } = useCreateAssociationTableBlock(); + + return { + // hideSearch: true, + onlyCurrentDataSource: true, + filterCollections({ associationField }) { + if (associationField) { + return ['hasMany', 'belongsToMany'].includes(associationField.type); + } + return false; + }, + createBlockSchema: createAssociationTableBlock, + showAssociationFields: true, + }; + }, + }, + { + name: 'list', + title: '{{t("List")}}', + Component: 'ListBlockInitializer', + useVisible() { + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); + }, + useComponentProps() { + const { createAssociationListBlock } = useCreateAssociationListBlock(); + + return { + // hideSearch: true, + onlyCurrentDataSource: true, + filterCollections({ associationField }) { + if (associationField) { + return ['hasMany', 'belongsToMany'].includes(associationField.type); + } + return false; + }, + createBlockSchema: createAssociationListBlock, + showAssociationFields: true, + }; + }, + }, + { + name: 'gridCard', + title: '{{t("Grid Card")}}', + Component: 'GridCardBlockInitializer', + useVisible() { + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); + }, + useComponentProps() { + const { createAssociationGridCardBlock } = useCreateAssociationGridCardBlock(); + + return { + // hideSearch: true, + onlyCurrentDataSource: true, + filterCollections({ associationField }) { + if (associationField) { + return ['hasMany', 'belongsToMany'].includes(associationField.type); + } + return false; + }, + createBlockSchema: createAssociationGridCardBlock, + showAssociationFields: true, + }; + }, + }, + ]; return res; } @@ -247,8 +268,8 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize items: [ { type: 'itemGroup', - name: 'currentRecordBlocks', - title: '{{t("Current record blocks")}}', + name: 'dataBlocks', + title: '{{t("Data blocks")}}', useChildren: useRecordBlocks, }, { @@ -256,8 +277,14 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize title: '{{t("Filter blocks")}}', type: 'itemGroup', useVisible() { - const collection = useCollection_deprecated(); - return collection.fields.some((field) => ['hasMany', 'belongsToMany'].includes(field.type)); + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); }, children: [ { @@ -272,10 +299,13 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize ); return { - filterMenuItemChildren(collection) { - return toManyField.some((field) => field.target === collection.name); + filterCollections({ collection }) { + if (collection) { + return toManyField.some((field) => field.target === collection.name); + } }, onlyCurrentDataSource: true, + showChildren: true, }; }, }, @@ -284,32 +314,35 @@ export const recordBlockInitializers_deprecated = new CompatibleSchemaInitialize title: '{{t("Collapse")}}', Component: 'FilterCollapseBlockInitializer', useComponentProps() { - const collection = useCollection_deprecated(); + const collection = useCollection(); const toManyField = useMemo( () => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)), [collection.fields], ); return { - filterMenuItemChildren(collection) { - return toManyField.some((field) => field.target === collection.name); + filterCollections({ collection }) { + if (collection) { + return toManyField.some((field) => field.target === collection.name); + } }, onlyCurrentDataSource: true, + showChildren: true, }; }, }, ], }, - { - type: 'itemGroup', - name: 'relationshipBlocks', - title: '{{t("Relationship blocks")}}', - useChildren: useRelationFields, - useVisible() { - const res = useRelationFields(); - return res.length > 0; - }, - }, + // { + // type: 'itemGroup', + // name: 'relationshipBlocks', + // title: '{{t("Relationship blocks")}}', + // useChildren: useRelationFields, + // useVisible() { + // const res = useRelationFields(); + // return res.length > 0; + // }, + // }, { type: 'itemGroup', name: 'otherBlocks', @@ -334,8 +367,8 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer( items: [ { type: 'itemGroup', - name: 'currentRecordBlocks', - title: '{{t("Current record blocks")}}', + name: 'dataBlocks', + title: '{{t("Data blocks")}}', useChildren: useRecordBlocks, }, { @@ -343,8 +376,14 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer( title: '{{t("Filter blocks")}}', type: 'itemGroup', useVisible() { - const collection = useCollection_deprecated(); - return collection.fields.some((field) => ['hasMany', 'belongsToMany'].includes(field.type)); + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); }, children: [ { @@ -359,10 +398,13 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer( ); return { - filterMenuItemChildren(collection) { - return toManyField.some((field) => field.target === collection.name); + filterCollections({ collection }) { + if (collection) { + return toManyField.some((field) => field.target === collection.name); + } }, onlyCurrentDataSource: true, + showChildren: true, }; }, }, @@ -371,32 +413,35 @@ export const recordBlockInitializers = new CompatibleSchemaInitializer( title: '{{t("Collapse")}}', Component: 'FilterCollapseBlockInitializer', useComponentProps() { - const collection = useCollection_deprecated(); + const collection = useCollection(); const toManyField = useMemo( () => collection.fields.filter((field) => ['hasMany', 'belongsToMany'].includes(field.type)), [collection.fields], ); return { - filterMenuItemChildren(collection) { - return toManyField.some((field) => field.target === collection.name); + filterCollections({ collection }) { + if (collection) { + return toManyField.some((field) => field.target === collection.name); + } }, onlyCurrentDataSource: true, + showChildren: true, }; }, }, ], }, - { - type: 'itemGroup', - name: 'relationshipBlocks', - title: '{{t("Relationship blocks")}}', - useChildren: useRelationFields, - useVisible() { - const res = useRelationFields(); - return res.length > 0; - }, - }, + // { + // type: 'itemGroup', + // name: 'relationshipBlocks', + // title: '{{t("Relationship blocks")}}', + // useChildren: useRelationFields, + // useVisible() { + // const res = useRelationFields(); + // return res.length > 0; + // }, + // }, { type: 'itemGroup', name: 'otherBlocks', diff --git a/packages/core/client/src/schema-initializer/index.ts b/packages/core/client/src/schema-initializer/index.ts index cc68d9725..e8e9b2006 100644 --- a/packages/core/client/src/schema-initializer/index.ts +++ b/packages/core/client/src/schema-initializer/index.ts @@ -124,6 +124,7 @@ export { createFilterFormBlockSchema, createFormBlockSchema, createReadPrettyFormBlockSchema, + createDetailsBlockSchema, createTableBlockSchema, gridRowColWrap, itemsMerge, diff --git a/packages/core/client/src/schema-initializer/items/DataBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/DataBlockInitializer.tsx index 61263af92..98dc3c113 100644 --- a/packages/core/client/src/schema-initializer/items/DataBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/DataBlockInitializer.tsx @@ -8,7 +8,7 @@ import { useGetSchemaInitializerMenuItems, useSchemaInitializer, } from '../../application'; -import { Collection } from '../../data-source/collection/Collection'; +import { Collection, CollectionFieldOptions } from '../../data-source/collection/Collection'; import { useCompile } from '../../schema-component'; import { useSchemaTemplateManager } from '../../schema-templates'; import { useCollectionDataSourceItems } from '../utils'; @@ -109,7 +109,17 @@ const LoadingItem = ({ loadMore, maxHeight }) => { ); }; -export function useMenuSearch(data: any[], openKeys: string[], showType?: boolean) { +export function useMenuSearch({ + data, + openKeys, + showType, + hideSearch, +}: { + data: any[]; + openKeys: string[]; + showType?: boolean; + hideSearch?: boolean; +}) { const [searchValue, setSearchValue] = useState(''); const [count, setCount] = useState(STEP); @@ -154,28 +164,31 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea // 最终的返回结果 const resultItems = useMemo(() => { - // isMenuType 为了 `useSchemaInitializerMenuItems()` 里面处理判断标识的 - const res: any[] = [ + const res = []; + if (!hideSearch) { // 开头:搜索框 - Object.assign( - { - key: 'search', - label: ( - { - setCount(STEP); - setSearchValue(val); - }} - /> - ), - onClick({ domEvent }) { - domEvent.stopPropagation(); + res.push( + Object.assign( + { + key: 'search', + label: ( + { + setCount(STEP); + setSearchValue(val); + }} + /> + ), + onClick({ domEvent }) { + domEvent.stopPropagation(); + }, }, - }, - showType ? { isMenuType: true } : {}, - ), - ]; + // isMenuType 为了 `useSchemaInitializerMenuItems()` 里面处理判断标识的 + showType ? { isMenuType: true } : {}, + ), + ); + } // 中间:搜索的数据 if (limitedSearchedItems.length > 0) { @@ -220,7 +233,7 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea } return res; - }, [limitedSearchedItems, searchValue, shouldLoadMore, showType]); + }, [hideSearch, limitedSearchedItems, searchValue, shouldLoadMore, showType]); const res = useMemo(() => { if (!isMuliSource) return resultItems; @@ -237,7 +250,7 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea }; } }); - }, [data, openKey, resultItems]); + }, [data, isMuliSource, openKey, resultItems]); return res; } @@ -252,13 +265,16 @@ export interface DataBlockInitializerProps { ) => any; onCreateBlockSchema?: (args: any) => void; createBlockSchema?: (args: any) => any; - isCusomeizeCreate?: boolean; icon?: string | React.ReactNode; name: string; title: string; - filter?: (collection: Collection) => boolean; + filter?: (options: { collection: Collection; associationField: CollectionFieldOptions }) => boolean; componentType: string; onlyCurrentDataSource?: boolean; + hideSearch?: boolean; + showAssociationFields?: boolean; + /** 即使 children 只有一个时,也显示出来 */ + showChildren?: boolean; } export const DataBlockInitializer = (props: DataBlockInitializerProps) => { @@ -266,13 +282,14 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => { templateWrap, onCreateBlockSchema, componentType, - createBlockSchema, - isCusomeizeCreate, icon = TableOutlined, name, title, filter, onlyCurrentDataSource, + hideSearch, + showAssociationFields, + showChildren, } = props; const { insert, setVisible } = useSchemaInitializer(); const compile = useCompile(); @@ -285,30 +302,34 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => { } else { if (onCreateBlockSchema) { onCreateBlockSchema({ item }); - } else if (createBlockSchema) { - insert( - createBlockSchema({ - collection: item.collectionName || item.name, - dataSource: item.dataSource, - isCusomeizeCreate, - settings: 'blockSettings:createForm', - }), - ); } } setVisible(false); }, - [createBlockSchema, getTemplateSchemaByMode, insert, isCusomeizeCreate, onCreateBlockSchema, templateWrap], + [getTemplateSchemaByMode, insert, onCreateBlockSchema, setVisible, templateWrap], ); - const items = useCollectionDataSourceItems(componentType, filter, onlyCurrentDataSource); + const items = useCollectionDataSourceItems({ + componentName: componentType, + filter, + onlyCurrentDataSource, + showAssociationFields, + }); const getMenuItems = useGetSchemaInitializerMenuItems(onClick); const childItems = useMemo(() => { return getMenuItems(items, name); }, [getMenuItems, items, name]); const [openMenuKeys, setOpenMenuKeys] = useState([]); - const searchedChildren = useMenuSearch(childItems, openMenuKeys); - const compiledMenuItems = useMemo( - () => [ + const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys, hideSearch }); + const compiledMenuItems = useMemo(() => { + let children = searchedChildren.filter((item) => item.key !== 'search' && item.key !== 'empty'); + const hasAssociationField = children.some((item) => item.associationField); + if (!showChildren && !hasAssociationField && children.length === 1) { + // 只有一项可选时,直接展开 + children = children[0].children; + } else { + children = searchedChildren; + } + return [ { key: name, label: compile(title), @@ -317,11 +338,10 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => { if (info.key !== name) return; onClick({ ...info, item: props }); }, - children: searchedChildren, + children, }, - ], - [name, compile, title, icon, childItems, onClick, props], - ); + ]; + }, [name, compile, title, icon, searchedChildren, onClick, props]); if (childItems.length > 1 || (childItems.length === 1 && childItems[0].children?.length > 0)) { return ( diff --git a/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx index 2f6ef29ee..2d6e0d5ff 100644 --- a/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordAssociationBlockInitializer.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { TableOutlined } from '@ant-design/icons'; import { useCollectionManager_deprecated } from '../../collection-manager'; @@ -6,6 +6,9 @@ import { useSchemaTemplateManager } from '../../schema-templates'; import { createTableBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; +/** + * @deprecated + */ export const RecordAssociationBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; @@ -39,3 +42,27 @@ export const RecordAssociationBlockInitializer = () => { /> ); }; + +export function useCreateAssociationTableBlock() { + const { insert } = useSchemaInitializer(); + const { getCollection } = useCollectionManager_deprecated(); + + const createAssociationTableBlock = useCallback( + ({ item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + + insert( + createTableBlockSchema({ + rowKey: collection.filterTargetKey, + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + }), + ); + }, + [getCollection, insert], + ); + + return { createAssociationTableBlock }; +} diff --git a/packages/core/client/src/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx index d4e5ee200..ded2bf3cc 100644 --- a/packages/core/client/src/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx @@ -1,5 +1,5 @@ import { FormOutlined } from '@ant-design/icons'; -import React from 'react'; +import React, { useCallback } from 'react'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { useCollectionManager_deprecated } from '../../collection-manager'; @@ -40,3 +40,28 @@ export const RecordAssociationDetailsBlockInitializer = () => { /> ); }; + +export function useCreateAssociationDetailsBlock() { + const { insert } = useSchemaInitializer(); + const { getCollection } = useCollectionManager_deprecated(); + + const createAssociationDetailsBlock = useCallback( + ({ item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + + insert( + createDetailsBlockSchema({ + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + rowKey: collection.filterTargetKey || 'id', + settings: 'blockSettings:multiDataDetails', + }), + ); + }, + [getCollection, insert], + ); + + return { createAssociationDetailsBlock }; +} diff --git a/packages/core/client/src/schema-initializer/items/RecordAssociationFormBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordAssociationFormBlockInitializer.tsx index 274f94941..9b73c2ae4 100644 --- a/packages/core/client/src/schema-initializer/items/RecordAssociationFormBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordAssociationFormBlockInitializer.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { FormOutlined } from '@ant-design/icons'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; @@ -6,6 +6,9 @@ import { useSchemaTemplateManager } from '../../schema-templates'; import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { useCollectionManager_deprecated } from '../../collection-manager'; +/** + * @deprecated + */ export const RecordAssociationFormBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; @@ -27,7 +30,7 @@ export const RecordAssociationFormBlockInitializer = () => { : 'createForm:configureActions'; if (item.template) { - const s = await getTemplateSchemaByMode(item); + const template = await getTemplateSchemaByMode(item); if (item.template.componentName === 'FormItem') { const blockSchema = createFormBlockSchema({ collection: collectionName, @@ -38,7 +41,7 @@ export const RecordAssociationFormBlockInitializer = () => { useSourceId: '{{ useSourceIdFromParentRecord }}', useParams: '{{ useParamsFromRecord }}', actionInitializers, - template: s, + template: template, settings: 'blockSettings:createForm', }); if (item.mode === 'reference') { @@ -46,7 +49,7 @@ export const RecordAssociationFormBlockInitializer = () => { } insert(blockSchema); } else { - insert(s); + insert(template); } } else { insert( @@ -68,3 +71,67 @@ export const RecordAssociationFormBlockInitializer = () => { /> ); }; + +export function useCreateAssociationFormBlock() { + const { insert } = useSchemaInitializer(); + const { getCollection } = useCollectionManager_deprecated(); + + const createAssociationFormBlock = useCallback( + ({ item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + const action = ['hasOne', 'belongsTo'].includes(field.type) ? 'get' : null; + const actionInitializers = ['hasOne', 'belongsTo'].includes(field.type) + ? 'editForm:configureActions' + : 'createForm:configureActions'; + + insert( + createFormBlockSchema({ + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + action, + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + actionInitializers, + settings: 'blockSettings:createForm', + }), + ); + }, + [getCollection, insert], + ); + + const templateWrap = useCallback( + (templateSchema, { item }) => { + const field = item.associationField; + const action = ['hasOne', 'belongsTo'].includes(field.type) ? 'get' : null; + const collection = getCollection(field.target); + const actionInitializers = ['hasOne', 'belongsTo'].includes(field.type) + ? 'editForm:configureActions' + : 'createForm:configureActions'; + + if (item.template.componentName === 'FormItem') { + const blockSchema = createFormBlockSchema({ + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + action, + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + actionInitializers, + template: templateSchema, + settings: 'blockSettings:createForm', + }); + if (item.mode === 'reference') { + blockSchema['x-template-key'] = item.template.key; + } + return blockSchema; + } else { + return templateSchema; + } + }, + [getCollection], + ); + + return { createAssociationFormBlock, templateWrap }; +} diff --git a/packages/core/client/src/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx index 48942cc1b..eec7ef3d7 100644 --- a/packages/core/client/src/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx @@ -1,11 +1,14 @@ import { TableOutlined } from '@ant-design/icons'; -import React from 'react'; +import React, { useCallback } from 'react'; import { useCollectionManager_deprecated } from '../../collection-manager'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { useSchemaTemplateManager } from '../../schema-templates'; import { createGridCardBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; +/** + * @deprecated + */ export const RecordAssociationGridCardBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; @@ -41,3 +44,28 @@ export const RecordAssociationGridCardBlockInitializer = () => { /> ); }; + +export function useCreateAssociationGridCardBlock() { + const { insert } = useSchemaInitializer(); + const { getCollection } = useCollectionManager_deprecated(); + + const createAssociationGridCardBlock = useCallback( + ({ item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + + insert( + createGridCardBlockSchema({ + rowKey: collection.filterTargetKey, + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + settings: 'blockSettings:gridCard', + }), + ); + }, + [getCollection, insert], + ); + + return { createAssociationGridCardBlock }; +} diff --git a/packages/core/client/src/schema-initializer/items/RecordAssociationListBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordAssociationListBlockInitializer.tsx index 4dc50011e..444841954 100644 --- a/packages/core/client/src/schema-initializer/items/RecordAssociationListBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordAssociationListBlockInitializer.tsx @@ -1,5 +1,5 @@ import { TableOutlined } from '@ant-design/icons'; -import React from 'react'; +import React, { useCallback } from 'react'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { useCollectionManager_deprecated } from '../../collection-manager'; @@ -41,3 +41,28 @@ export const RecordAssociationListBlockInitializer = () => { /> ); }; + +export function useCreateAssociationListBlock() { + const { insert } = useSchemaInitializer(); + const { getCollection } = useCollectionManager_deprecated(); + + const createAssociationListBlock = useCallback( + ({ item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + + insert( + createListBlockSchema({ + rowKey: collection.filterTargetKey, + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + settings: 'blockSettings:list', + }), + ); + }, + [getCollection, insert], + ); + + return { createAssociationListBlock }; +} diff --git a/packages/core/client/src/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx b/packages/core/client/src/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx index 83b662f17..936fdc08f 100644 --- a/packages/core/client/src/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx +++ b/packages/core/client/src/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx @@ -1,12 +1,15 @@ import { FormOutlined } from '@ant-design/icons'; -import React from 'react'; +import React, { useCallback } from 'react'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { useBlockRequestContext } from '../../block-provider'; import { useSchemaTemplateManager } from '../../schema-templates'; -import { createReadPrettyFormBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; +import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../utils'; import { useCollectionManager_deprecated } from '../../collection-manager'; +/** + * @deprecated + */ export const RecordReadPrettyAssociationFormBlockInitializer = () => { const itemConfig = useSchemaInitializerItem(); const { onCreateBlockSchema, componentType, createBlockSchema, ...others } = itemConfig; @@ -30,7 +33,7 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => { if (item.template) { const s = await getTemplateSchemaByMode(item); if (item.template.componentName === 'ReadPrettyFormItem') { - const blockSchema = createReadPrettyFormBlockSchema({ + const blockSchema = createDetailsBlockSchema({ actionInitializers, collection: collectionName, dataSource: collection.dataSource, @@ -51,7 +54,7 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => { } } else { insert( - createReadPrettyFormBlockSchema({ + createDetailsBlockSchema({ actionInitializers, collection: collectionName, resource, @@ -69,3 +72,61 @@ export const RecordReadPrettyAssociationFormBlockInitializer = () => { /> ); }; + +export function useCreateAssociationDetailsWithoutPagination() { + const { insert } = useSchemaInitializer(); + const { getCollection } = useCollectionManager_deprecated(); + const { block } = useBlockRequestContext(); + const actionInitializers = block !== 'TableField' ? 'details:configureActions' : null; + + const createAssociationDetailsWithoutPagination = useCallback( + ({ item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + + insert( + createDetailsBlockSchema({ + actionInitializers, + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + settings: 'blockSettings:singleDataDetails', + }), + ); + }, + [actionInitializers, getCollection, insert], + ); + + const templateWrap = useCallback( + (templateSchema, { item }) => { + const field = item.associationField; + const collection = getCollection(field.target); + + if (item.template.componentName === 'ReadPrettyFormItem') { + const blockSchema = createDetailsBlockSchema({ + actionInitializers, + collection: field.target, + dataSource: collection.dataSource, + association: `${field.collectionName}.${field.name}`, + action: 'get', + useSourceId: '{{ useSourceIdFromParentRecord }}', + useParams: '{{ useParamsFromRecord }}', + template: templateSchema, + settings: 'blockSettings:singleDataDetails', + }); + if (item.mode === 'reference') { + blockSchema['x-template-key'] = item.template.key; + } + return blockSchema; + } else { + return templateSchema; + } + }, + [actionInitializers, getCollection], + ); + + return { createAssociationDetailsWithoutPagination, templateWrap }; +} diff --git a/packages/core/client/src/schema-initializer/utils.ts b/packages/core/client/src/schema-initializer/utils.ts index 34411a58e..913780650 100644 --- a/packages/core/client/src/schema-initializer/utils.ts +++ b/packages/core/client/src/schema-initializer/utils.ts @@ -3,14 +3,21 @@ import { ISchema, Schema, useFieldSchema, useForm } from '@formily/react'; import { uid } from '@formily/shared'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { SchemaInitializerItemType, useDataSourceKey, useFormActiveFields, useFormBlockContext } from '../'; +import { + SchemaInitializerItemType, + useCollection, + useCollectionManager, + useDataSourceKey, + useFormActiveFields, + useFormBlockContext, +} from '../'; import { FieldOptions, useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager'; import { isAssocField } from '../filter-provider/utils'; -import { useActionContext, useDesignable } from '../schema-component'; +import { useActionContext, useCompile, useDesignable } from '../schema-component'; import { useSchemaTemplateManager } from '../schema-templates'; -import { Collection } from '../data-source/collection/Collection'; +import { Collection, CollectionFieldOptions } from '../data-source/collection/Collection'; import { useDataSourceManager } from '../data-source/data-source/DataSourceManagerProvider'; -import { DataSourceManager } from '../data-source/data-source/DataSourceManager'; +import _ from 'lodash'; export const itemsMerge = (items1) => { return items1; @@ -750,6 +757,10 @@ export const useCurrentSchema = (action: string, key: string, find = findSchema, }; }; +/** + * @deprecated + * 待统一区块的创建之后,将废弃该方法 + */ export const useRecordCollectionDataSourceItems = ( componentName, item = null, @@ -822,15 +833,29 @@ export const useRecordCollectionDataSourceItems = ( ]; }; -export const useCollectionDataSourceItems = ( +export const useCollectionDataSourceItems = ({ componentName, - filter: (collection: Collection) => boolean = () => true, + filter = () => true, onlyCurrentDataSource = false, -) => { + showAssociationFields, +}: { + componentName; + filter?: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource?: boolean; + showAssociationFields?: boolean; +}) => { const { t } = useTranslation(); const dm = useDataSourceManager(); const dataSourceKey = useDataSourceKey(); - let allCollections = dm.getAllCollections(filter); + const collection = useCollection(); + const associationFields = useAssociationFields({ componentName, filterCollections: filter, showAssociationFields }); + + let allCollections = dm.getAllCollections((collection) => { + if (onlyCurrentDataSource && collection.dataSource !== dataSourceKey) { + return false; + } + return filter({ collection }); + }); if (onlyCurrentDataSource) { allCollections = allCollections.filter((collection) => collection.key === dataSourceKey); } @@ -841,16 +866,23 @@ export const useCollectionDataSourceItems = ( name: key, label: displayName, type: 'subMenu', - children: getChildren({ - collections, - componentName, - searchValue: '', - dataSource: key, - getTemplatesByCollection, - t, - }), + children: [ + ...getChildren({ + collections, + componentName, + searchValue: '', + dataSource: key, + getTemplatesByCollection, + t, + }).sort((item) => { + // fix https://nocobase.height.app/T-3551 + const inherits = _.toArray(collection?.inherits || []); + if (item.name === collection?.name || inherits.some((inheritName) => inheritName === item.name)) return -1; + }), + ...associationFields, + ], })); - }, [allCollections, componentName, dm, getTemplatesByCollection, t]); + }, [allCollections, associationFields, componentName, getTemplatesByCollection, t]); return res; }; @@ -862,27 +894,29 @@ export const createDetailsBlockSchema = (options) => { collection, dataSource, association, - resource, template, settings, + action = 'list', ...others } = options; - const resourceName = resource || association || collection; + const resourceName = association || collection; const schema: ISchema = { type: 'void', - 'x-acl-action': `${resourceName}:view`, + 'x-acl-action': action === 'get' ? `${resourceName}:get` : `${resourceName}:view`, 'x-decorator': 'DetailsBlockProvider', 'x-decorator-props': { - resource: resourceName, dataSource, collection, association, readPretty: true, - action: 'list', - params: { - pageSize: 1, - }, - // useParams: '{{ useParamsFromRecord }}', + action, + ...(action === 'list' + ? { + params: { + pageSize: 1, + }, + } + : {}), ...others, }, 'x-toolbar': 'BlockSchemaToolbar', @@ -914,13 +948,17 @@ export const createDetailsBlockSchema = (options) => { 'x-initializer': formItemInitializers, properties: {}, }, - pagination: { - type: 'void', - 'x-component': 'Pagination', - 'x-component-props': { - useProps: '{{ useDetailsPaginationProps }}', - }, - }, + ...(action === 'list' + ? { + pagination: { + type: 'void', + 'x-component': 'Pagination', + 'x-component-props': { + useProps: '{{ useDetailsPaginationProps }}', + }, + }, + } + : {}), }, }, }, @@ -936,18 +974,16 @@ export const createListBlockSchema = (options) => { collection, dataSource, association, - resource, template, settings, ...others } = options; - const resourceName = resource || association || collection; + const resourceName = association || collection; const schema: ISchema = { type: 'void', 'x-acl-action': `${resourceName}:view`, 'x-decorator': 'List.Decorator', 'x-decorator-props': { - resource: resourceName, collection, dataSource, association, @@ -1025,19 +1061,17 @@ export const createGridCardBlockSchema = (options) => { itemActionInitializers = 'gridCard:configureItemActions', collection, association, - resource, template, dataSource, settings, ...others } = options; - const resourceName = resource || association || collection; + const resourceName = association || collection; const schema: ISchema = { type: 'void', 'x-acl-action': `${resourceName}:view`, 'x-decorator': 'GridCard.Decorator', 'x-decorator-props': { - resource: resourceName, collection, association, dataSource, @@ -1248,6 +1282,12 @@ export const createFilterFormBlockSchema = (options) => { return schema; }; +/** + * @deprecated + * 已弃用,可以使用 createDetailsBlockSchema 替换 + * @param options + * @returns + */ export const createReadPrettyFormBlockSchema = (options) => { const { formItemInitializers = 'details:configureFields', @@ -1315,7 +1355,6 @@ export const createReadPrettyFormBlockSchema = (options) => { export const createTableBlockSchema = (options) => { const { collection, - resource, rowKey, tableActionInitializers, tableColumnInitializers, @@ -1330,11 +1369,10 @@ export const createTableBlockSchema = (options) => { const schema: ISchema = { type: 'void', 'x-decorator': tableBlockProvider ?? 'TableBlockProvider', - 'x-acl-action': `${resource || collection}:list`, + 'x-acl-action': `${collection}:list`, 'x-decorator-props': { collection, dataSource, - resource: resource || collection, action: 'list', params: { pageSize, @@ -1526,7 +1564,9 @@ const getChildren = ({ return ( componentName && template.componentName === componentName && - (!template.resourceName || template.resourceName === item.name) + (['FormItem', 'ReadPrettyFormItem'].includes(componentName) || + !template.resourceName || + template.resourceName === item.name) ); }); if (!templates.length) { @@ -1560,8 +1600,9 @@ const getChildren = ({ dataSource, title: t('Duplicate template'), children: templates.map((template) => { - const templateName = - template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name; + const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; return { type: 'item', mode: 'copy', @@ -1579,8 +1620,9 @@ const getChildren = ({ dataSource, title: t('Reference template'), children: templates.map((template) => { - const templateName = - template?.componentName === 'FormItem' ? `${template?.name} ${t('(Fields only)')}` : template?.name; + const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; return { type: 'item', mode: 'reference', @@ -1595,3 +1637,142 @@ const getChildren = ({ }; }); }; + +function useAssociationFields({ + componentName, + filterCollections, + showAssociationFields, +}: { + componentName: string; + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + showAssociationFields?: boolean; +}) { + const fieldSchema = useFieldSchema(); + const { getCollectionFields } = useCollectionManager_deprecated(); + const collection = useCollection_deprecated(); + const cm = useCollectionManager(); + const dataSource = useDataSourceKey(); + const { getTemplatesByCollection } = useSchemaTemplateManager(); + const { t } = useTranslation(); + const compile = useCompile(); + + return useMemo(() => { + if (!showAssociationFields) { + return []; + } + + let fields: CollectionFieldOptions[] = []; + + if (fieldSchema['x-initializer']) { + fields = collection.fields; + } else { + const collection = recursiveParent(fieldSchema.parent); + if (collection) { + fields = getCollectionFields(collection); + } + } + + return fields + .filter((field) => ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface)) + .filter((field) => filterCollections({ associationField: field })) + .map((field, index) => { + const targetCollection = cm.getCollection(field.target); + const title = `${compile(field.uiSchema.title || field.name)} -> ${compile(targetCollection.title)}`; + const templates = getTemplatesByCollection(dataSource, field.target).filter((template) => { + return ( + componentName && + template.componentName === componentName && + (['FormItem', 'ReadPrettyFormItem'].includes(componentName) || + !template.resourceName || + template.resourceName === `${field.collectionName}.${field.name}`) + ); + }); + if (!templates.length) { + return { + type: 'item', + name: `${field.collectionName}.${field.name}`, + collectionName: field.target, + title, + dataSource, + associationField: field, + }; + } + return { + key: `${componentName}_table_subMenu_${index}`, + type: 'subMenu', + name: `${field.target}_${index}`, + title, + dataSource, + children: [ + { + type: 'item', + name: `${field.collectionName}.${field.name}`, + collectionName: field.target, + dataSource, + title: t('Blank block'), + associationField: field, + }, + { + type: 'divider', + }, + { + key: `${componentName}_table_subMenu_${index}_copy`, + type: 'subMenu', + name: 'copy', + dataSource, + title: t('Duplicate template'), + children: templates.map((template) => { + const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'copy', + name: `${field.collectionName}.${field.name}`, + collectionName: field.target, + template, + dataSource, + title: templateName || t('Untitled'), + associationField: field, + }; + }), + }, + { + key: `${componentName}_table_subMenu_${index}_ref`, + type: 'subMenu', + name: 'ref', + dataSource, + title: t('Reference template'), + children: templates.map((template) => { + const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) + ? `${template?.name} ${t('(Fields only)')}` + : template?.name; + return { + type: 'item', + mode: 'reference', + name: `${field.collectionName}.${field.name}`, + collectionName: field.target, + template, + dataSource, + title: templateName || t('Untitled'), + associationField: field, + }; + }), + }, + ], + }; + }); + }, [ + cm, + collection.fields, + compile, + componentName, + dataSource, + fieldSchema, + filterCollections, + getCollectionFields, + getTemplatesByCollection, + showAssociationFields, + t, + ]); +} diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/index.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/index.tsx index de7d6fbd1..49e4680f0 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/index.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/index.tsx @@ -1,4 +1,4 @@ -import { Plugin } from '@nocobase/client'; +import { Plugin, canMakeAssociationBlock, useCollection } from '@nocobase/client'; import { generateNTemplate } from '../locale'; import { CalendarV2 } from './calendar'; import { calendarBlockSettings } from './calendar/Calender.Settings'; @@ -10,7 +10,12 @@ import { calendarActionInitializers, deleteEventActionInitializer, } from './schema-initializer/initializers'; -import { CalendarBlockInitializer, RecordAssociationCalendarBlockInitializer } from './schema-initializer/items'; +import { + CalendarBlockInitializer, + RecordAssociationCalendarBlockInitializer, + useCreateAssociationCalendarBlock, +} from './schema-initializer/items'; +import { useMemo } from 'react'; export class PluginCalendarClient extends Plugin { async load() { @@ -19,9 +24,39 @@ export class PluginCalendarClient extends Plugin { title: generateNTemplate('Calendar'), Component: 'CalendarBlockInitializer', }); + this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'dataBlocks.calendar', { + title: generateNTemplate('Calendar'), + Component: 'CalendarBlockInitializer', + useVisible() { + const collection = useCollection(); + return useMemo( + () => + collection.fields.some( + (field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type), + ), + [collection.fields], + ); + }, + useComponentProps() { + const { createAssociationCalendarBlock } = useCreateAssociationCalendarBlock(); + + return { + onlyCurrentDataSource: true, + filterCollections({ associationField }) { + if (associationField) { + return ['hasMany', 'belongsToMany'].includes(associationField.type); + } + return false; + }, + createBlockSchema: createAssociationCalendarBlock, + showAssociationFields: true, + }; + }, + }); + this.app.addComponents({ CalendarBlockProvider, - CalendarBlockInitializer, + CalendarBlockInitializer: CalendarBlockInitializer as any, RecordAssociationCalendarBlockInitializer, CalendarV2, }); diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/CalendarBlockInitializer.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/CalendarBlockInitializer.tsx index 9c4d2dfb3..8995f0e4a 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/CalendarBlockInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/CalendarBlockInitializer.tsx @@ -2,6 +2,8 @@ import { FormOutlined } from '@ant-design/icons'; import { FormLayout } from '@formily/antd-v5'; import { SchemaOptionsContext } from '@formily/react'; import { + Collection, + CollectionFieldOptions, DataBlockInitializer, FormDialog, SchemaComponent, @@ -15,7 +17,19 @@ import React, { useContext } from 'react'; import { createCalendarBlockSchema } from '../utils'; import { useTranslation } from '../../../locale'; -export const CalendarBlockInitializer = () => { +export const CalendarBlockInitializer = ({ + filterCollections, + onlyCurrentDataSource, + hideSearch, + createBlockSchema, + showAssociationFields, +}: { + filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; + onlyCurrentDataSource: boolean; + hideSearch?: boolean; + createBlockSchema?: (options: any) => any; + showAssociationFields?: boolean; +}) => { const { insert } = useSchemaInitializer(); const { t } = useTranslation(); const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated(); @@ -29,6 +43,10 @@ export const CalendarBlockInitializer = () => { componentType={'Calendar'} icon={} onCreateBlockSchema={async ({ item }) => { + if (createBlockSchema) { + return createBlockSchema({ item }); + } + const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource }); const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', { association: ['o2o', 'obo', 'oho', 'm2o'], @@ -87,6 +105,10 @@ export const CalendarBlockInitializer = () => { }), ); }} + onlyCurrentDataSource={onlyCurrentDataSource} + hideSearch={hideSearch} + filter={filterCollections} + showAssociationFields={showAssociationFields} /> ); }; diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/RecordAssociationCalendarBlockInitializer.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/RecordAssociationCalendarBlockInitializer.tsx index f7b357bf9..6a2b20ec7 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/RecordAssociationCalendarBlockInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/schema-initializer/items/RecordAssociationCalendarBlockInitializer.tsx @@ -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 ( + + + + + + ); + }, + theme, + ).open({ + initialValues: {}, + }); + insert( + createCalendarBlockSchema({ + collection: field.target, + association: `${field.collectionName}.${field.name}`, + fieldNames: { + ...values, + }, + settings: 'blockSettings:calendar', + }), + ); + }; + + return { createAssociationCalendarBlock }; +} diff --git a/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/manyToOne/templatesOfBug.ts b/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/manyToOne/templatesOfBug.ts index 1ddc7b732..5e9fa3af1 100644 --- a/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/manyToOne/templatesOfBug.ts +++ b/packages/plugins/@nocobase/plugin-collection-manager/src/client/__e2e__/fields/manyToOne/templatesOfBug.ts @@ -13,7 +13,7 @@ export const T3377: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'BlockInitializers', + 'x-initializer': 'page:addBlock', properties: { dtuo84w9hyg: { _isJSONSchemaObject: true, @@ -60,7 +60,7 @@ export const T3377: PageConfig = { version: '2.0', type: 'void', 'x-component': 'Grid', - 'x-initializer': 'FormItemInitializers', + 'x-initializer': 'form:configureFields', properties: { '729znsrntep': { _isJSONSchemaObject: true, @@ -98,7 +98,7 @@ export const T3377: PageConfig = { version: '2.0', type: 'void', 'x-component': 'AssociationField.SubTable', - 'x-initializer': 'TableColumnInitializers', + 'x-initializer': 'table:configureColumns', 'x-initializer-props': { action: false, }, diff --git a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/client/index.ts b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/client/index.ts index 53af3ba02..e629579b3 100644 --- a/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/client/index.ts +++ b/packages/plugins/@nocobase/plugin-workflow-action-trigger/src/client/index.ts @@ -76,25 +76,25 @@ export default class extends Plugin { const FormActionInitializers = this.app.schemaInitializerManager.get('FormActionInitializers'); FormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer); - const CreateFormActionInitializers = this.app.schemaInitializerManager.get('CreateFormActionInitializers'); + const CreateFormActionInitializers = this.app.schemaInitializerManager.get('createForm:configureActions'); CreateFormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer); - const UpdateFormActionInitializers = this.app.schemaInitializerManager.get('UpdateFormActionInitializers'); + const UpdateFormActionInitializers = this.app.schemaInitializerManager.get('editForm:configureActions'); UpdateFormActionInitializers.add('customize.submitToWorkflow', submitToWorkflowActionInitializer); - const DetailsActionInitializers = this.app.schemaInitializerManager.get('DetailsActionInitializers'); + const DetailsActionInitializers = this.app.schemaInitializerManager.get('detailsWithPaging:configureActions'); DetailsActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionInitializer); - const ReadPrettyFormActionInitializers = this.app.schemaInitializerManager.get('ReadPrettyFormActionInitializers'); + const ReadPrettyFormActionInitializers = this.app.schemaInitializerManager.get('details:configureActions'); ReadPrettyFormActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionInitializer); - const TableActionColumnInitializers = this.app.schemaInitializerManager.get('TableActionColumnInitializers'); + const TableActionColumnInitializers = this.app.schemaInitializerManager.get('table:configureItemActions'); TableActionColumnInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer); - const GridCardItemActionInitializers = this.app.schemaInitializerManager.get('GridCardItemActionInitializers'); + const GridCardItemActionInitializers = this.app.schemaInitializerManager.get('gridCard:configureItemActions'); GridCardItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer); - const ListItemActionInitializers = this.app.schemaInitializerManager.get('ListItemActionInitializers'); + const ListItemActionInitializers = this.app.schemaInitializerManager.get('list:configureItemActions'); ListItemActionInitializers.add('customize.submitToWorkflow', recordTriggerWorkflowActionLinkInitializer); } } diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/create.tsx b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/create.tsx index 86284b5ef..113193eeb 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/create.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/create.tsx @@ -65,7 +65,7 @@ export default { [allCollections], ); const [openMenuKeys, setOpenMenuKeys] = useState([]); - const searchedChildren = useMenuSearch(childItems, openMenuKeys); + const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys }); return { name: 'createRecordForm', diff --git a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/update.tsx b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/update.tsx index 0db045c25..20443e907 100644 --- a/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/update.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-manual/src/client/instruction/forms/update.tsx @@ -101,7 +101,7 @@ export default { [allCollections], ); const [openMenuKeys, setOpenMenuKeys] = useState([]); - const searchedChildren = useMenuSearch(childItems, openMenuKeys); + const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys }); return { name: 'updateRecordForm', key: 'updateRecordForm',