refactor: add blocks in a unified way (#3668)
* refactor: rename filterMenuItemChildren to filterCollections * refactor: revert parameters to destructured object * refactor(Details): unify details block between page and popup * refactor(Form): refactor(Details): unify form block between page and popup * refactor: table & list & grid card * refactor: form * refactor: association details * chore: useVisible * refactor: calendar * chore: remove relationshipBlocks * refactor: unify Details schema * fix: avoid error * chore: add translate * chore: add tag to association fields * fix: add block useChildren and children merge * test: fix e2e * test: fix e2e * fix: fix Calendar * fix: fix search * test: add e2e for search * fix: fix T-3554 * fix: fix T-3551 * feat: add no data for details block * chore: fix unit test * chore: fix unit test * chore: change text * chore: optimize text * chore: add translation * style: fix memu item style * refactor: rename * test: fix e2e * test: fix e2e --------- Co-authored-by: dream2023 <1098626505@qq.com>
This commit is contained in:
parent
ba1e44c527
commit
286af35ff8
@ -49,7 +49,10 @@ export const SchemaInitializerChild: FC<SchemaInitializerItemType> = memo((props
|
||||
const componentVal = Component || component;
|
||||
const isBuiltType = !componentVal && type && typeComponentMap[type];
|
||||
|
||||
const componentChildren = useChildrenRes || children;
|
||||
const componentChildren = useMemo(() => {
|
||||
const res = [...(useChildrenRes || []), ...(children || [])];
|
||||
return res.length === 0 ? undefined : res;
|
||||
}, [useChildrenRes, children]);
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
...others,
|
||||
|
@ -50,6 +50,9 @@ export const SchemaInitializerMenu: FC<MenuProps> = (props) => {
|
||||
.ant-menu-sub {
|
||||
max-height: 50vh !important;
|
||||
}
|
||||
.ant-menu-item {
|
||||
margin-block: 0;
|
||||
}
|
||||
.ant-menu-root {
|
||||
margin: 0 -${token.margin}px;
|
||||
.ant-menu-submenu-title {
|
||||
|
@ -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) {
|
||||
|
@ -84,7 +84,10 @@ export const SchemaSettingsChild: FC<SchemaSettingsItemType> = memo((props) => {
|
||||
const useChildrenRes = useChildren();
|
||||
const useComponentPropsRes = useComponentProps();
|
||||
const findComponent = useFindComponent();
|
||||
const componentChildren = useChildrenRes || children;
|
||||
const componentChildren = useMemo(() => {
|
||||
const res = [...(useChildrenRes || []), ...(children || [])];
|
||||
return res.length === 0 ? undefined : res;
|
||||
}, [useChildrenRes, children]);
|
||||
const visibleResult = useVisible();
|
||||
const ComponentValue = useMemo(() => {
|
||||
return !Component && type && typeComponentMap[type] ? typeComponentMap[type] : Component;
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -133,9 +133,5 @@ export const BlockRequestProvider: FC = ({ children }) => {
|
||||
|
||||
export const useDataBlockRequest = <T extends {}>(): UseRequestResult<{ data: T }> => {
|
||||
const context = useContext(BlockRequestContext);
|
||||
if (!context) {
|
||||
throw new Error('useDataBlockRequest() must be used within a DataBlockRequestProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -293,6 +293,10 @@
|
||||
"Add tab": "タブを追加",
|
||||
"Disable tabs": "タブを無効にする",
|
||||
"Details": "詳細",
|
||||
"Edit form": "フォームを編集",
|
||||
"Create form": "フォームを作成",
|
||||
"Form (Edit)": "フォーム (編集)",
|
||||
"Form (Add new)": "フォーム (新規追加)",
|
||||
"Edit tab": "タブを編集",
|
||||
"Relationship blocks": "関連付けされたブロック",
|
||||
"Select record": "レコードを選択",
|
||||
|
@ -378,6 +378,10 @@
|
||||
"Add tab": "탭 추가",
|
||||
"Disable tabs": "탭 비활성화",
|
||||
"Details": "세부 정보",
|
||||
"Edit form": "폼 편집",
|
||||
"Create form": "폼 생성",
|
||||
"Form (Edit)": "폼 (편집)",
|
||||
"Form (Add new)": "폼 (새로 추가)",
|
||||
"Edit tab": "탭 편집",
|
||||
"Relationship blocks": "관계 데이터 블록",
|
||||
"Select record": "레코드 선택",
|
||||
|
@ -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",
|
||||
|
@ -233,6 +233,10 @@
|
||||
"Add tab": "Добавить вкладку",
|
||||
"Disable tabs": "Запретить вкладки",
|
||||
"Details": "Подробности",
|
||||
"Edit form": "Изменить форму",
|
||||
"Create form": "Создать форму",
|
||||
"Form (Edit)": "Форма (Изменить)",
|
||||
"Form (Add new)": "Форма (Добавить новый)",
|
||||
"Edit tab": "Изменить вкладку",
|
||||
"Relationship blocks": "Блоки отношений",
|
||||
"Select record": "Выбрать запись",
|
||||
|
@ -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ç",
|
||||
|
@ -348,6 +348,10 @@
|
||||
"Add tab": "Додати вкладку",
|
||||
"Disable tabs": "Вимкнути вкладки",
|
||||
"Details": "Деталі",
|
||||
"Edit form": "Редагувати форму",
|
||||
"Create form": "Створити форму",
|
||||
"Form (Edit)": "Форма (Редагувати)",
|
||||
"Form (Add new)": "Форма (Додати новий)",
|
||||
"Edit tab": "Редагувати вкладку",
|
||||
"Relationship blocks": "Блоки відношень",
|
||||
"Select record": "Вибрати запис",
|
||||
|
@ -379,6 +379,10 @@
|
||||
"Add tab": "添加标签页",
|
||||
"Disable tabs": "禁用标签页",
|
||||
"Details": "详情",
|
||||
"Edit form": "编辑表单",
|
||||
"Create form": "创建表单",
|
||||
"Form (Edit)": "表单(编辑)",
|
||||
"Form (Add new)": "表单(添加)",
|
||||
"Edit tab": "编辑标签页",
|
||||
"Relationship blocks": "关系数据区块",
|
||||
"Select record": "选择数据",
|
||||
|
@ -378,6 +378,10 @@
|
||||
"Add tab": "新增標籤頁",
|
||||
"Disable tabs": "停用標籤頁",
|
||||
"Details": "詳情",
|
||||
"Edit form": "編輯表單",
|
||||
"Create form": "建立表單",
|
||||
"Form (Edit)": "表單(編輯)",
|
||||
"Form (Add new)": "表單(添加)",
|
||||
"Edit tab": "編輯標籤頁",
|
||||
"Relationship blocks": "關聯資料區塊",
|
||||
"Select record": "選擇資料",
|
||||
|
@ -4,8 +4,36 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
|
||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||
import { createDetailsBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||
|
||||
export const DetailsBlockInitializer = () => {
|
||||
export const DetailsBlockInitializer = ({
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
componentType = 'Details',
|
||||
createBlockSchema,
|
||||
templateWrap,
|
||||
showAssociationFields,
|
||||
}: {
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
hideSearch?: boolean;
|
||||
/**
|
||||
* 页面中的详情区块类型是 Details,弹窗中的详情区块类型是 ReadPrettyFormItem;
|
||||
* 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。
|
||||
*/
|
||||
componentType?: 'Details' | 'ReadPrettyFormItem';
|
||||
createBlockSchema?: (options: any) => any;
|
||||
templateWrap?: (
|
||||
templateSchema: any,
|
||||
{
|
||||
item,
|
||||
}: {
|
||||
item: any;
|
||||
},
|
||||
) => any;
|
||||
showAssociationFields?: boolean;
|
||||
}) => {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getCollection } = useCollectionManager_deprecated();
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
@ -13,8 +41,13 @@ export const DetailsBlockInitializer = () => {
|
||||
<DataBlockInitializer
|
||||
{...itemConfig}
|
||||
icon={<TableOutlined />}
|
||||
componentType={'Details'}
|
||||
onCreateBlockSchema={async ({ item }) => {
|
||||
componentType={componentType}
|
||||
onCreateBlockSchema={async (options) => {
|
||||
if (createBlockSchema) {
|
||||
return createBlockSchema(options);
|
||||
}
|
||||
|
||||
const { item } = options;
|
||||
const collection = getCollection(item.name, item.dataSource);
|
||||
const schema = createDetailsBlockSchema({
|
||||
collection: item.name,
|
||||
@ -28,6 +61,11 @@ export const DetailsBlockInitializer = () => {
|
||||
});
|
||||
insert(schema);
|
||||
}}
|
||||
onlyCurrentDataSource={!!onlyCurrentDataSource}
|
||||
hideSearch={hideSearch}
|
||||
filter={filterCollections}
|
||||
templateWrap={templateWrap}
|
||||
showAssociationFields={showAssociationFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -1,75 +1,85 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||
import { useBlockAssociationContext, useBlockRequestContext } from '../../../../block-provider';
|
||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||
import { useSchemaTemplateManager } from '../../../../schema-templates';
|
||||
import {
|
||||
createReadPrettyFormBlockSchema,
|
||||
useRecordCollectionDataSourceItems,
|
||||
} from '../../../../schema-initializer/utils';
|
||||
import { createDetailsBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
|
||||
|
||||
export const RecordReadPrettyFormBlockInitializer = () => {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const {
|
||||
onCreateBlockSchema,
|
||||
componentType,
|
||||
createBlockSchema,
|
||||
icon = true,
|
||||
targetCollection,
|
||||
...others
|
||||
} = itemConfig;
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||
const { icon = true, targetCollection, ...others } = itemConfig;
|
||||
const currentCollection = useCollection_deprecated();
|
||||
const collection = targetCollection || currentCollection;
|
||||
const association = useBlockAssociationContext();
|
||||
const { block } = useBlockRequestContext();
|
||||
const actionInitializers =
|
||||
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
|
||||
const { createSingleDetailsSchema } = useCreateSingleDetailsSchema();
|
||||
|
||||
return (
|
||||
<SchemaInitializerItem
|
||||
icon={icon && <FormOutlined />}
|
||||
{...others}
|
||||
onClick={async ({ item }) => {
|
||||
if (item.template) {
|
||||
const s = await getTemplateSchemaByMode(item);
|
||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||
const blockSchema = createReadPrettyFormBlockSchema({
|
||||
actionInitializers,
|
||||
association,
|
||||
collection: collection.name,
|
||||
dataSource: collection.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
template: s,
|
||||
settings: 'blockSettings:singleDataDetails',
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
insert(blockSchema);
|
||||
} else {
|
||||
insert(s);
|
||||
}
|
||||
} else {
|
||||
insert(
|
||||
createReadPrettyFormBlockSchema({
|
||||
actionInitializers,
|
||||
association,
|
||||
collection: collection.name,
|
||||
dataSource: collection.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
settings: 'blockSettings:singleDataDetails',
|
||||
}),
|
||||
);
|
||||
}
|
||||
}}
|
||||
onClick={(options) => createSingleDetailsSchema(options)}
|
||||
items={useRecordCollectionDataSourceItems('ReadPrettyFormItem', null, collection?.name)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export function useCreateSingleDetailsSchema() {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||
const association = useBlockAssociationContext();
|
||||
const { block } = useBlockRequestContext();
|
||||
const actionInitializers =
|
||||
block !== 'TableField' ? itemConfig.actionInitializers || 'details:configureActions' : null;
|
||||
|
||||
const templateWrap = useCallback(
|
||||
(templateSchema, options) => {
|
||||
const { item } = options;
|
||||
if (item.template.componentName === 'ReadPrettyFormItem') {
|
||||
const blockSchema = createDetailsBlockSchema({
|
||||
actionInitializers,
|
||||
association,
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
template: templateSchema,
|
||||
settings: 'blockSettings:singleDataDetails',
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
return blockSchema;
|
||||
} else {
|
||||
return templateSchema;
|
||||
}
|
||||
},
|
||||
[actionInitializers, association],
|
||||
);
|
||||
|
||||
const createSingleDetailsSchema = useCallback(
|
||||
async ({ item }) => {
|
||||
if (item.template) {
|
||||
const template = await getTemplateSchemaByMode(item);
|
||||
insert(templateWrap(template, { item }));
|
||||
} else {
|
||||
insert(
|
||||
createDetailsBlockSchema({
|
||||
actionInitializers,
|
||||
association,
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
settings: 'blockSettings:singleDataDetails',
|
||||
}),
|
||||
);
|
||||
}
|
||||
},
|
||||
[actionInitializers, association, getTemplateSchemaByMode, insert, templateWrap],
|
||||
);
|
||||
|
||||
return { createSingleDetailsSchema, templateWrap };
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,19 +1,69 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { useSchemaInitializerItem } from '../../../../application';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||
import { createFormBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||
|
||||
export const FormBlockInitializer = () => {
|
||||
export const FormBlockInitializer = ({
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
createBlockSchema,
|
||||
componentType = 'FormItem',
|
||||
templateWrap,
|
||||
showAssociationFields,
|
||||
}: {
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
hideSearch?: boolean;
|
||||
createBlockSchema?: (options: any) => any;
|
||||
/**
|
||||
* 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。
|
||||
*/
|
||||
componentType?: 'FormItem';
|
||||
templateWrap?: (
|
||||
templateSchema: any,
|
||||
{
|
||||
item,
|
||||
}: {
|
||||
item: any;
|
||||
},
|
||||
) => any;
|
||||
showAssociationFields?: boolean;
|
||||
}) => {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const { isCusomeizeCreate } = itemConfig;
|
||||
const onCreateFormBlockSchema = useCallback(
|
||||
({ item }) => {
|
||||
if (createBlockSchema) {
|
||||
return createBlockSchema({ item });
|
||||
}
|
||||
|
||||
insert(
|
||||
createFormBlockSchema({
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
isCusomeizeCreate,
|
||||
settings: 'blockSettings:createForm',
|
||||
}),
|
||||
);
|
||||
},
|
||||
[createBlockSchema, insert, isCusomeizeCreate],
|
||||
);
|
||||
|
||||
return (
|
||||
<DataBlockInitializer
|
||||
{...itemConfig}
|
||||
icon={<FormOutlined />}
|
||||
componentType={'FormItem'}
|
||||
componentType={componentType}
|
||||
templateWrap={(templateSchema, { item }) => {
|
||||
const s = createFormBlockSchema({
|
||||
if (templateWrap) {
|
||||
return templateWrap(templateSchema, { item });
|
||||
}
|
||||
|
||||
const schema = createFormBlockSchema({
|
||||
isCusomeizeCreate,
|
||||
dataSource: item.dataSource,
|
||||
template: templateSchema,
|
||||
@ -21,11 +71,15 @@ export const FormBlockInitializer = () => {
|
||||
settings: 'blockSettings:createForm',
|
||||
});
|
||||
if (item.template && item.mode === 'reference') {
|
||||
s['x-template-key'] = item.template.key;
|
||||
schema['x-template-key'] = item.template.key;
|
||||
}
|
||||
return s;
|
||||
return schema;
|
||||
}}
|
||||
createBlockSchema={createFormBlockSchema}
|
||||
onCreateBlockSchema={onCreateFormBlockSchema}
|
||||
filter={filterCollections}
|
||||
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||
hideSearch={hideSearch}
|
||||
showAssociationFields={showAssociationFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,61 +1,87 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||
import { useBlockAssociationContext } from '../../../../block-provider';
|
||||
import { useCollection_deprecated } from '../../../../collection-manager';
|
||||
import { useSchemaTemplateManager } from '../../../../schema-templates';
|
||||
import { createFormBlockSchema, useRecordCollectionDataSourceItems } from '../../../../schema-initializer/utils';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const RecordFormBlockInitializer = () => {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const { onCreateBlockSchema, componentType, createBlockSchema, targetCollection, ...others } = itemConfig;
|
||||
const { targetCollection, ...others } = itemConfig;
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||
const currentCollection = useCollection_deprecated();
|
||||
const collection = targetCollection || currentCollection;
|
||||
const association = useBlockAssociationContext();
|
||||
|
||||
const { createEditFormBlock, templateWrap } = useCreateEditFormBlock();
|
||||
|
||||
return (
|
||||
<SchemaInitializerItem
|
||||
icon={<FormOutlined />}
|
||||
{...others}
|
||||
onClick={async ({ item }) => {
|
||||
if (item.template) {
|
||||
const s = await getTemplateSchemaByMode(item);
|
||||
if (item.template.componentName === 'FormItem') {
|
||||
const blockSchema = createFormBlockSchema({
|
||||
association,
|
||||
collection: collection.name,
|
||||
dataSource: collection.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
actionInitializers: 'editForm:configureActions',
|
||||
template: s,
|
||||
settings: 'blockSettings:editForm',
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
insert(blockSchema);
|
||||
} else {
|
||||
insert(s);
|
||||
}
|
||||
const template = await getTemplateSchemaByMode(item);
|
||||
insert(templateWrap(template, { item }));
|
||||
} else {
|
||||
insert(
|
||||
createFormBlockSchema({
|
||||
association,
|
||||
collection: collection.name,
|
||||
dataSource: collection.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
actionInitializers: 'editForm:configureActions',
|
||||
settings: 'blockSettings:editForm',
|
||||
}),
|
||||
);
|
||||
createEditFormBlock({ item });
|
||||
}
|
||||
}}
|
||||
items={useRecordCollectionDataSourceItems('FormItem', null, collection?.name)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export function useCreateEditFormBlock() {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const association = useBlockAssociationContext();
|
||||
|
||||
const createEditFormBlock = useCallback(
|
||||
({ item }) => {
|
||||
insert(
|
||||
createFormBlockSchema({
|
||||
association,
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
actionInitializers: 'editForm:configureActions',
|
||||
settings: 'blockSettings:editForm',
|
||||
}),
|
||||
);
|
||||
},
|
||||
[association, insert],
|
||||
);
|
||||
|
||||
const templateWrap = useCallback(
|
||||
(templateSchema, { item }) => {
|
||||
if (item.template.componentName === 'FormItem') {
|
||||
const blockSchema = createFormBlockSchema({
|
||||
association,
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
action: 'get',
|
||||
useSourceId: '{{ useSourceIdFromParentRecord }}',
|
||||
useParams: '{{ useParamsFromRecord }}',
|
||||
actionInitializers: 'editForm:configureActions',
|
||||
template: templateSchema,
|
||||
settings: 'blockSettings:editForm',
|
||||
});
|
||||
if (item.mode === 'reference') {
|
||||
blockSchema['x-template-key'] = item.template.key;
|
||||
}
|
||||
return blockSchema;
|
||||
} else {
|
||||
return templateSchema;
|
||||
}
|
||||
},
|
||||
[association],
|
||||
);
|
||||
|
||||
return { createEditFormBlock, templateWrap };
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
|
||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||
import { createGridCardBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||
|
||||
export const GridCardBlockInitializer = () => {
|
||||
export const GridCardBlockInitializer = ({
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
createBlockSchema,
|
||||
componentType = 'FormItem',
|
||||
templateWrap,
|
||||
showAssociationFields,
|
||||
}: {
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
hideSearch?: boolean;
|
||||
createBlockSchema?: (options: any) => any;
|
||||
/**
|
||||
* 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。
|
||||
*/
|
||||
componentType?: 'FormItem';
|
||||
templateWrap?: (
|
||||
templateSchema: any,
|
||||
{
|
||||
item,
|
||||
}: {
|
||||
item: any;
|
||||
},
|
||||
) => any;
|
||||
showAssociationFields?: boolean;
|
||||
}) => {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getCollection } = useCollectionManager_deprecated();
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
@ -15,6 +42,10 @@ export const GridCardBlockInitializer = () => {
|
||||
icon={<OrderedListOutlined />}
|
||||
componentType={'GridCard'}
|
||||
onCreateBlockSchema={async ({ item }) => {
|
||||
if (createBlockSchema) {
|
||||
return createBlockSchema({ item });
|
||||
}
|
||||
|
||||
const collection = getCollection(item.name, item.dataSource);
|
||||
const schema = createGridCardBlockSchema({
|
||||
collection: item.name,
|
||||
@ -24,6 +55,10 @@ export const GridCardBlockInitializer = () => {
|
||||
});
|
||||
insert(schema);
|
||||
}}
|
||||
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||
hideSearch={hideSearch}
|
||||
filter={filterCollections}
|
||||
showAssociationFields={showAssociationFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -4,8 +4,35 @@ import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../appl
|
||||
import { useCollectionManager_deprecated } from '../../../../collection-manager';
|
||||
import { createListBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||
|
||||
export const ListBlockInitializer = () => {
|
||||
export const ListBlockInitializer = ({
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
createBlockSchema,
|
||||
componentType = 'FormItem',
|
||||
templateWrap,
|
||||
showAssociationFields,
|
||||
}: {
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
hideSearch?: boolean;
|
||||
createBlockSchema?: (options: any) => any;
|
||||
/**
|
||||
* 虽然这里的命名现在看起来比较奇怪,但为了兼容旧版本的 template,暂时保留这个命名。
|
||||
*/
|
||||
componentType?: 'FormItem';
|
||||
templateWrap?: (
|
||||
templateSchema: any,
|
||||
{
|
||||
item,
|
||||
}: {
|
||||
item: any;
|
||||
},
|
||||
) => any;
|
||||
showAssociationFields?: boolean;
|
||||
}) => {
|
||||
const { getCollection } = useCollectionManager_deprecated();
|
||||
const { insert } = useSchemaInitializer();
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
@ -15,6 +42,10 @@ export const ListBlockInitializer = () => {
|
||||
icon={<OrderedListOutlined />}
|
||||
componentType={'List'}
|
||||
onCreateBlockSchema={async ({ item }) => {
|
||||
if (createBlockSchema) {
|
||||
return createBlockSchema({ item });
|
||||
}
|
||||
|
||||
const collection = getCollection(item.name, item.dataSource);
|
||||
const schema = createListBlockSchema({
|
||||
collection: item.name,
|
||||
@ -24,6 +55,10 @@ export const ListBlockInitializer = () => {
|
||||
});
|
||||
insert(schema);
|
||||
}}
|
||||
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||
hideSearch={hideSearch}
|
||||
filter={filterCollections}
|
||||
showAssociationFields={showAssociationFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -4,8 +4,30 @@ import { useCollectionManager_deprecated } from '../../../../collection-manager/
|
||||
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
|
||||
import { createTableBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import React from 'react';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
|
||||
|
||||
export const TableBlockInitializer = () => {
|
||||
export const TableBlockInitializer = ({
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
createBlockSchema,
|
||||
templateWrap,
|
||||
showAssociationFields,
|
||||
}: {
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
hideSearch?: boolean;
|
||||
createBlockSchema?: (options: any) => any;
|
||||
templateWrap?: (
|
||||
templateSchema: any,
|
||||
{
|
||||
item,
|
||||
}: {
|
||||
item: any;
|
||||
},
|
||||
) => any;
|
||||
showAssociationFields?: boolean;
|
||||
}) => {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getCollection } = useCollectionManager_deprecated();
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
@ -15,6 +37,10 @@ export const TableBlockInitializer = () => {
|
||||
icon={<TableOutlined />}
|
||||
componentType={'Table'}
|
||||
onCreateBlockSchema={async ({ item }) => {
|
||||
if (createBlockSchema) {
|
||||
return createBlockSchema({ item });
|
||||
}
|
||||
|
||||
const collection = getCollection(item.name, item.dataSource);
|
||||
const schema = createTableBlockSchema({
|
||||
collection: item.name,
|
||||
@ -23,6 +49,10 @@ export const TableBlockInitializer = () => {
|
||||
});
|
||||
insert(schema);
|
||||
}}
|
||||
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||
hideSearch={hideSearch}
|
||||
filter={filterCollections}
|
||||
showAssociationFields={showAssociationFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { useSchemaInitializerItem } from '../../../../application';
|
||||
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
|
||||
import { createFilterFormBlockSchema } from '../../../../schema-initializer/utils';
|
||||
import { FilterBlockInitializer } from '../../../../schema-initializer/items/FilterBlockInitializer';
|
||||
import { Collection } from '../../../../data-source';
|
||||
import { Collection, CollectionFieldOptions } from '../../../../data-source';
|
||||
|
||||
export const FilterFormBlockInitializer = ({
|
||||
filterMenuItemChildren,
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
showChildren,
|
||||
}: {
|
||||
filterMenuItemChildren: (collection: Collection) => boolean;
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
showChildren?: boolean;
|
||||
}) => {
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const { insert } = useSchemaInitializer();
|
||||
|
||||
return (
|
||||
<FilterBlockInitializer
|
||||
@ -32,11 +35,17 @@ export const FilterFormBlockInitializer = ({
|
||||
}
|
||||
return s;
|
||||
}}
|
||||
createBlockSchema={(options) => {
|
||||
options = { ...options, settings: 'blockSettings:filterForm' };
|
||||
return createFilterFormBlockSchema(options);
|
||||
onCreateBlockSchema={({ item }) => {
|
||||
return insert(
|
||||
createFilterFormBlockSchema({
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
settings: 'blockSettings:filterForm',
|
||||
}),
|
||||
);
|
||||
}}
|
||||
filter={filterMenuItemChildren}
|
||||
filter={filterCollections}
|
||||
showChildren={showChildren}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -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 });
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
@ -1,3 +1,16 @@
|
||||
import React from 'react';
|
||||
import { FormV2 } from '../form-v2';
|
||||
import { useDetailsBlockContext } from '../../../block-provider/DetailsBlockProvider';
|
||||
import _ from 'lodash';
|
||||
import { Empty } from 'antd';
|
||||
import { useDataBlockRequest } from '../../../data-source';
|
||||
|
||||
export const Details = FormV2;
|
||||
export const Details = (props) => {
|
||||
const request = useDataBlockRequest();
|
||||
|
||||
if (!request?.loading && _.isEmpty(request?.data?.data)) {
|
||||
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
|
||||
}
|
||||
|
||||
return <FormV2 {...props} />;
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ describe('Details', () => {
|
||||
it('should render correctly', () => {
|
||||
render(<App1 />);
|
||||
|
||||
expect(screen.getByText('this is name')).toBeInTheDocument();
|
||||
expect(screen.getByText('this is age')).toBeInTheDocument();
|
||||
expect(screen.getByText('No data')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -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',
|
||||
|
@ -124,6 +124,7 @@ export {
|
||||
createFilterFormBlockSchema,
|
||||
createFormBlockSchema,
|
||||
createReadPrettyFormBlockSchema,
|
||||
createDetailsBlockSchema,
|
||||
createTableBlockSchema,
|
||||
gridRowColWrap,
|
||||
itemsMerge,
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
useGetSchemaInitializerMenuItems,
|
||||
useSchemaInitializer,
|
||||
} from '../../application';
|
||||
import { Collection } from '../../data-source/collection/Collection';
|
||||
import { Collection, CollectionFieldOptions } from '../../data-source/collection/Collection';
|
||||
import { useCompile } from '../../schema-component';
|
||||
import { useSchemaTemplateManager } from '../../schema-templates';
|
||||
import { useCollectionDataSourceItems } from '../utils';
|
||||
@ -109,7 +109,17 @@ const LoadingItem = ({ loadMore, maxHeight }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export function useMenuSearch(data: any[], openKeys: string[], showType?: boolean) {
|
||||
export function useMenuSearch({
|
||||
data,
|
||||
openKeys,
|
||||
showType,
|
||||
hideSearch,
|
||||
}: {
|
||||
data: any[];
|
||||
openKeys: string[];
|
||||
showType?: boolean;
|
||||
hideSearch?: boolean;
|
||||
}) {
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [count, setCount] = useState(STEP);
|
||||
|
||||
@ -154,28 +164,31 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea
|
||||
|
||||
// 最终的返回结果
|
||||
const resultItems = useMemo<MenuProps['items']>(() => {
|
||||
// isMenuType 为了 `useSchemaInitializerMenuItems()` 里面处理判断标识的
|
||||
const res: any[] = [
|
||||
const res = [];
|
||||
if (!hideSearch) {
|
||||
// 开头:搜索框
|
||||
Object.assign(
|
||||
{
|
||||
key: 'search',
|
||||
label: (
|
||||
<SearchCollections
|
||||
value={searchValue}
|
||||
onChange={(val: string) => {
|
||||
setCount(STEP);
|
||||
setSearchValue(val);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onClick({ domEvent }) {
|
||||
domEvent.stopPropagation();
|
||||
res.push(
|
||||
Object.assign(
|
||||
{
|
||||
key: 'search',
|
||||
label: (
|
||||
<SearchCollections
|
||||
value={searchValue}
|
||||
onChange={(val: string) => {
|
||||
setCount(STEP);
|
||||
setSearchValue(val);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onClick({ domEvent }) {
|
||||
domEvent.stopPropagation();
|
||||
},
|
||||
},
|
||||
},
|
||||
showType ? { isMenuType: true } : {},
|
||||
),
|
||||
];
|
||||
// isMenuType 为了 `useSchemaInitializerMenuItems()` 里面处理判断标识的
|
||||
showType ? { isMenuType: true } : {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 中间:搜索的数据
|
||||
if (limitedSearchedItems.length > 0) {
|
||||
@ -220,7 +233,7 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea
|
||||
}
|
||||
|
||||
return res;
|
||||
}, [limitedSearchedItems, searchValue, shouldLoadMore, showType]);
|
||||
}, [hideSearch, limitedSearchedItems, searchValue, shouldLoadMore, showType]);
|
||||
|
||||
const res = useMemo(() => {
|
||||
if (!isMuliSource) return resultItems;
|
||||
@ -237,7 +250,7 @@ export function useMenuSearch(data: any[], openKeys: string[], showType?: boolea
|
||||
};
|
||||
}
|
||||
});
|
||||
}, [data, openKey, resultItems]);
|
||||
}, [data, isMuliSource, openKey, resultItems]);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -252,13 +265,16 @@ export interface DataBlockInitializerProps {
|
||||
) => any;
|
||||
onCreateBlockSchema?: (args: any) => void;
|
||||
createBlockSchema?: (args: any) => any;
|
||||
isCusomeizeCreate?: boolean;
|
||||
icon?: string | React.ReactNode;
|
||||
name: string;
|
||||
title: string;
|
||||
filter?: (collection: Collection) => boolean;
|
||||
filter?: (options: { collection: Collection; associationField: CollectionFieldOptions }) => boolean;
|
||||
componentType: string;
|
||||
onlyCurrentDataSource?: boolean;
|
||||
hideSearch?: boolean;
|
||||
showAssociationFields?: boolean;
|
||||
/** 即使 children 只有一个时,也显示出来 */
|
||||
showChildren?: boolean;
|
||||
}
|
||||
|
||||
export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
|
||||
@ -266,13 +282,14 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
|
||||
templateWrap,
|
||||
onCreateBlockSchema,
|
||||
componentType,
|
||||
createBlockSchema,
|
||||
isCusomeizeCreate,
|
||||
icon = TableOutlined,
|
||||
name,
|
||||
title,
|
||||
filter,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
showAssociationFields,
|
||||
showChildren,
|
||||
} = props;
|
||||
const { insert, setVisible } = useSchemaInitializer();
|
||||
const compile = useCompile();
|
||||
@ -285,30 +302,34 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
|
||||
} else {
|
||||
if (onCreateBlockSchema) {
|
||||
onCreateBlockSchema({ item });
|
||||
} else if (createBlockSchema) {
|
||||
insert(
|
||||
createBlockSchema({
|
||||
collection: item.collectionName || item.name,
|
||||
dataSource: item.dataSource,
|
||||
isCusomeizeCreate,
|
||||
settings: 'blockSettings:createForm',
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
setVisible(false);
|
||||
},
|
||||
[createBlockSchema, getTemplateSchemaByMode, insert, isCusomeizeCreate, onCreateBlockSchema, templateWrap],
|
||||
[getTemplateSchemaByMode, insert, onCreateBlockSchema, setVisible, templateWrap],
|
||||
);
|
||||
const items = useCollectionDataSourceItems(componentType, filter, onlyCurrentDataSource);
|
||||
const items = useCollectionDataSourceItems({
|
||||
componentName: componentType,
|
||||
filter,
|
||||
onlyCurrentDataSource,
|
||||
showAssociationFields,
|
||||
});
|
||||
const getMenuItems = useGetSchemaInitializerMenuItems(onClick);
|
||||
const childItems = useMemo(() => {
|
||||
return getMenuItems(items, name);
|
||||
}, [getMenuItems, items, name]);
|
||||
const [openMenuKeys, setOpenMenuKeys] = useState([]);
|
||||
const searchedChildren = useMenuSearch(childItems, openMenuKeys);
|
||||
const compiledMenuItems = useMemo(
|
||||
() => [
|
||||
const searchedChildren = useMenuSearch({ data: childItems, openKeys: openMenuKeys, hideSearch });
|
||||
const compiledMenuItems = useMemo(() => {
|
||||
let children = searchedChildren.filter((item) => item.key !== 'search' && item.key !== 'empty');
|
||||
const hasAssociationField = children.some((item) => item.associationField);
|
||||
if (!showChildren && !hasAssociationField && children.length === 1) {
|
||||
// 只有一项可选时,直接展开
|
||||
children = children[0].children;
|
||||
} else {
|
||||
children = searchedChildren;
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: name,
|
||||
label: compile(title),
|
||||
@ -317,11 +338,10 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
|
||||
if (info.key !== name) return;
|
||||
onClick({ ...info, item: props });
|
||||
},
|
||||
children: searchedChildren,
|
||||
children,
|
||||
},
|
||||
],
|
||||
[name, compile, title, icon, childItems, onClick, props],
|
||||
);
|
||||
];
|
||||
}, [name, compile, title, icon, searchedChildren, onClick, props]);
|
||||
|
||||
if (childItems.length > 1 || (childItems.length === 1 && childItems[0].children?.length > 0)) {
|
||||
return (
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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,
|
||||
]);
|
||||
}
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -2,6 +2,8 @@ import { FormOutlined } from '@ant-design/icons';
|
||||
import { FormLayout } from '@formily/antd-v5';
|
||||
import { SchemaOptionsContext } from '@formily/react';
|
||||
import {
|
||||
Collection,
|
||||
CollectionFieldOptions,
|
||||
DataBlockInitializer,
|
||||
FormDialog,
|
||||
SchemaComponent,
|
||||
@ -15,7 +17,19 @@ import React, { useContext } from 'react';
|
||||
import { createCalendarBlockSchema } from '../utils';
|
||||
import { useTranslation } from '../../../locale';
|
||||
|
||||
export const CalendarBlockInitializer = () => {
|
||||
export const CalendarBlockInitializer = ({
|
||||
filterCollections,
|
||||
onlyCurrentDataSource,
|
||||
hideSearch,
|
||||
createBlockSchema,
|
||||
showAssociationFields,
|
||||
}: {
|
||||
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
|
||||
onlyCurrentDataSource: boolean;
|
||||
hideSearch?: boolean;
|
||||
createBlockSchema?: (options: any) => any;
|
||||
showAssociationFields?: boolean;
|
||||
}) => {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { t } = useTranslation();
|
||||
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated();
|
||||
@ -29,6 +43,10 @@ export const CalendarBlockInitializer = () => {
|
||||
componentType={'Calendar'}
|
||||
icon={<FormOutlined />}
|
||||
onCreateBlockSchema={async ({ item }) => {
|
||||
if (createBlockSchema) {
|
||||
return createBlockSchema({ item });
|
||||
}
|
||||
|
||||
const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource });
|
||||
const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', {
|
||||
association: ['o2o', 'obo', 'oho', 'm2o'],
|
||||
@ -87,6 +105,10 @@ export const CalendarBlockInitializer = () => {
|
||||
}),
|
||||
);
|
||||
}}
|
||||
onlyCurrentDataSource={onlyCurrentDataSource}
|
||||
hideSearch={hideSearch}
|
||||
filter={filterCollections}
|
||||
showAssociationFields={showAssociationFields}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -113,3 +113,86 @@ export const RecordAssociationCalendarBlockInitializer = () => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export function useCreateAssociationCalendarBlock() {
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getCollection } = useCollectionManager_deprecated();
|
||||
const { t } = useTranslation();
|
||||
const options = useContext(SchemaOptionsContext);
|
||||
const { theme } = useGlobalTheme();
|
||||
|
||||
const createAssociationCalendarBlock = async ({ item }) => {
|
||||
const field = item.associationField;
|
||||
const collection = getCollection(field.target);
|
||||
|
||||
const stringFields = collection?.fields
|
||||
?.filter((field) => field.type === 'string')
|
||||
?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title,
|
||||
value: field.name,
|
||||
};
|
||||
});
|
||||
const dateFields = collection?.fields
|
||||
?.filter((field) => field.type === 'date')
|
||||
?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema?.title,
|
||||
value: field.name,
|
||||
};
|
||||
});
|
||||
const values = await FormDialog(
|
||||
t('Create calendar block'),
|
||||
() => {
|
||||
return (
|
||||
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
||||
<FormLayout layout={'vertical'}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
properties: {
|
||||
title: {
|
||||
title: t('Title field'),
|
||||
enum: stringFields,
|
||||
required: true,
|
||||
'x-component': 'Select',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
start: {
|
||||
title: t('Start date field'),
|
||||
enum: dateFields,
|
||||
required: true,
|
||||
default: 'createdAt',
|
||||
'x-component': 'Select',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
end: {
|
||||
title: t('End date field'),
|
||||
enum: dateFields,
|
||||
'x-component': 'Select',
|
||||
'x-decorator': 'FormItem',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormLayout>
|
||||
</SchemaComponentOptions>
|
||||
);
|
||||
},
|
||||
theme,
|
||||
).open({
|
||||
initialValues: {},
|
||||
});
|
||||
insert(
|
||||
createCalendarBlockSchema({
|
||||
collection: field.target,
|
||||
association: `${field.collectionName}.${field.name}`,
|
||||
fieldNames: {
|
||||
...values,
|
||||
},
|
||||
settings: 'blockSettings:calendar',
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return { createAssociationCalendarBlock };
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user