fix: template-loading (#1072) close #626

Reviewed-on: daoyoucloud/tachybase#1072
This commit is contained in:
sealday 2024-05-30 15:15:45 +08:00
parent 9e1bfbf8c4
commit dc175f789f
67 changed files with 291 additions and 331 deletions

View File

@ -1,4 +1,5 @@
import React, { createContext } from 'react';
import { createContext } from 'react';
import { APIClient } from './APIClient';
export const APIClientContext = createContext<APIClient>(new APIClient());

View File

@ -1,4 +1,5 @@
import { useContext } from 'react';
import { APIClientContext } from '../context';
export function useAPIClient() {

View File

@ -1,11 +1,13 @@
import { merge } from '@tachybase/schema';
import { useRequest as useReq, useSetState } from 'ahooks';
import { Options, Result } from 'ahooks/es/useRequest/src/types';
import { SetState } from 'ahooks/lib/useSetState';
import { AxiosRequestConfig } from 'axios';
import cloneDeep from 'lodash/cloneDeep';
import { cloneDeep } from 'lodash';
import { assign } from './assign';
import { useAPIClient } from './useAPIClient';
import { SetState } from 'ahooks/lib/useSetState';
type FunctionService = (...args: any[]) => Promise<any>;

View File

@ -1,13 +1,5 @@
import { ChangeEvent, useCallback, useContext, useEffect, useMemo } from 'react';
import {
Field,
Form,
SchemaExpressionScopeContext,
untracked,
useField,
useFieldSchema,
useForm,
} from '@tachybase/schema';
import { ChangeEvent, useCallback, useEffect } from 'react';
import { Field, Form, untracked, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { isURL, parse } from '@tachybase/utils/client';
import { App, message } from 'antd';
@ -25,7 +17,6 @@ import {
useDataSourceHeaders,
useFormActiveFields,
useFormBlockContext,
useTableBlockContext,
} from '../..';
import { useAPIClient, useRequest } from '../../api-client';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../collection-manager';
@ -1313,7 +1304,7 @@ function getTargetField(obj) {
/**
* 使 form.reset()
* https://tachybase.height.app/T-3106
*
* @param form
*/
async function resetFormCorrectly(form: Form) {

View File

@ -15,8 +15,6 @@ import {
* recordData parentRecordData ; schema
* `通过点击关系字段按钮打开的弹窗中创建的非关系字段区块``关系字段区块`使 hook
*
* schema https://tachybase.height.app/T-3848/description
*
* @param param0
* @returns
*/

View File

@ -15,7 +15,6 @@ import {
PinnedPluginList,
RemoteCollectionManagerProvider,
RemoteSchemaTemplateManagerPlugin,
RemoteSchemaTemplateManagerProvider,
SchemaComponent,
useACLRoleContext,
useAdminSchemaUid,
@ -445,13 +444,11 @@ export const AdminProvider = (props) => {
return (
<CurrentAppInfoProvider>
<NavigateIfNotSignIn>
<RemoteSchemaTemplateManagerProvider>
<RemoteCollectionManagerProvider>
<VariablesProvider>
<ACLRolesCheckProvider>{props.children}</ACLRolesCheckProvider>
</VariablesProvider>
</RemoteCollectionManagerProvider>
</RemoteSchemaTemplateManagerProvider>
<RemoteCollectionManagerProvider>
<VariablesProvider>
<ACLRolesCheckProvider>{props.children}</ACLRolesCheckProvider>
</VariablesProvider>
</RemoteCollectionManagerProvider>
</NavigateIfNotSignIn>
</CurrentAppInfoProvider>
);

View File

@ -587,7 +587,6 @@ export const useFilterActionProps = () => {
const options = useFilterFieldOptions(collection.fields);
const service = useResourceActionContext();
return useFilterFieldProps({
// 目前仅需要支持筛选 title 和 name其它字段可能会报错。详见https://tachybase.height.app/T-2745
options: options.filter((option) => ['title', 'name'].includes(option.name)),
params: service.state?.params?.[0] || service.params,
service,

View File

@ -1,6 +1,7 @@
import { Select } from 'antd';
import React, { useRef, useState } from 'react';
import { Select } from 'antd';
export interface SelectWithTitleProps {
title?: any;
defaultValue?: any;

View File

@ -1,8 +1,8 @@
import { Field } from '@tachybase/schema';
import { useField, useFieldSchema } from '@tachybase/schema';
import { Field, useField, useFieldSchema } from '@tachybase/schema';
import { useCollectionField, useCollectionManager } from '../data-source';
import { useIsFileField } from '../schema-component/antd/form-item/FormItem.Settings';
import { useColumnSchema } from '../schema-component/antd/table-v2/Table.Column.Decorator';
import { useCollectionField, useCollectionManager } from '../data-source';
export function useFieldComponentName(): string {
const { fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema();

View File

@ -21,5 +21,6 @@ export const useNiceDropdownMaxHeight = (deps: any[] = []) => {
};
}, []);
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => heightRef.current - 40, deps);
};

View File

@ -1,16 +1,16 @@
import React, { createContext, ReactNode, useContext, useMemo } from 'react';
import { IResource } from '@tachybase/sdk';
import React, { FC, ReactNode, createContext, useContext, useMemo } from 'react';
import { useCollectionManager } from '../collection';
import { useDataBlockProps } from './DataBlockProvider';
import { useAPIClient } from '../../api-client';
import { useCollectionManager } from '../collection';
import { CollectionRecord } from '../collection-record';
import { useDataSourceHeaders } from '../utils';
import { useDataBlockProps } from './DataBlockProvider';
export const DataBlockResourceContext = createContext<IResource>(null);
DataBlockResourceContext.displayName = 'DataBlockResourceContext';
export const DataBlockResourceProvider: FC<{ children?: ReactNode }> = ({ children }) => {
export const DataBlockResourceProvider = ({ children }: { children?: ReactNode }) => {
const dataBlockProps = useDataBlockProps();
const cm = useCollectionManager();
const { association, collection, dataSource, sourceId, parentRecord } = dataBlockProps;

View File

@ -1,8 +1,6 @@
import type { CollectionOptions } from '../collection';
import { CollectionManager, type CollectionOptions } from '../collection';
import type { DataSourceManager } from './DataSourceManager';
import { CollectionManager } from '../collection';
type LoadCallback = (collections: CollectionOptions[]) => void;
export interface DataSourceOptions {

View File

@ -1,11 +1,11 @@
import type { CollectionOptions, Collection } from '../collection';
import type { Application } from '../../application/Application';
import { type DataSourceOptions, DataSource, LocalDataSource, DataSourceFactory } from './DataSource';
import { type CollectionTemplateFactory, CollectionTemplateManager } from '../collection-template';
import { type CollectionFieldInterfaceFactory, CollectionFieldInterfaceManager } from '../collection-field-interface';
import _ from 'lodash';
import type { Application } from '../../application/Application';
import type { Collection, CollectionOptions } from '../collection';
import { CollectionFieldInterfaceManager, type CollectionFieldInterfaceFactory } from '../collection-field-interface';
import { CollectionTemplateManager, type CollectionTemplateFactory } from '../collection-template';
import { DataSource, DataSourceFactory, LocalDataSource, type DataSourceOptions } from './DataSource';
export const DEFAULT_DATA_SOURCE_KEY = 'main';
export const DEFAULT_DATA_SOURCE_TITLE = '{{t("Main")}}';

View File

@ -1,4 +1,5 @@
import React, { FC, ReactNode, createContext, useContext } from 'react';
import React, { createContext, ReactNode, useContext } from 'react';
import type { DataSourceManager } from './DataSourceManager';
export const DataSourceManagerContext = createContext<DataSourceManager>(null);
@ -9,7 +10,7 @@ export interface DataSourceManagerProviderProps {
children?: ReactNode;
}
export const DataSourceManagerProvider: FC<DataSourceManagerProviderProps> = ({ children, dataSourceManager }) => {
export const DataSourceManagerProvider = ({ children, dataSourceManager }: DataSourceManagerProviderProps) => {
return <DataSourceManagerContext.Provider value={dataSourceManager}>{children}</DataSourceManagerContext.Provider>;
};

View File

@ -1,11 +1,13 @@
import React, { FC, ReactNode, createContext, useContext } from 'react';
import React, { createContext, ReactNode, useContext } from 'react';
import { LoadingOutlined } from '@ant-design/icons';
import { Button, Result } from 'antd';
import { useTranslation } from 'react-i18next';
import { CardItem, useSchemaComponentContext } from '../../schema-component';
import { CollectionDeletedPlaceholder } from '../components/CollectionDeletedPlaceholder';
import type { DataSource } from './DataSource';
import { useDataSourceManager } from './DataSourceManagerProvider';
import { CollectionDeletedPlaceholder } from '../components/CollectionDeletedPlaceholder';
import { CardItem, useSchemaComponentContext } from '../../schema-component';
import { Button, Result } from 'antd';
import { LoadingOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
export const DataSourceContext = createContext<DataSource>(null);
DataSourceContext.displayName = 'DataSourceContext';
@ -15,7 +17,7 @@ export interface DataSourceProviderProps {
children?: ReactNode;
}
export const DataSourceProvider: FC<DataSourceProviderProps> = ({ children, dataSource }) => {
export const DataSourceProvider = ({ children, dataSource }: DataSourceProviderProps) => {
const dataSourceManager = useDataSourceManager();
const { t } = useTranslation();
const { refresh } = useSchemaComponentContext();

View File

@ -1,11 +1,9 @@
// import { CascaderProps } from 'antd';
import _ from 'lodash';
import { useMemo } from 'react';
import { CollectionFieldOptions } from './collection';
import { DEFAULT_DATA_SOURCE_KEY, DataSourceManager } from './data-source/DataSourceManager';
// 等把老的去掉后,再把这个函数的实现从那边移动过来
// export function getCollectionFieldsOptions(){}
import _ from 'lodash';
import { CollectionFieldOptions } from './collection';
import { DataSourceManager, DEFAULT_DATA_SOURCE_KEY } from './data-source/DataSourceManager';
export const isTitleField = (dm: DataSourceManager, field: CollectionFieldOptions) => {
return !field.isForeignKey && dm.collectionFieldInterfaceManager.getFieldInterface(field.interface)?.titleUsable;
@ -15,7 +13,6 @@ export const useDataSourceHeaders = (dataSource?: string): any => {
const headers = useMemo(() => {
if (dataSource && dataSource !== DEFAULT_DATA_SOURCE_KEY) {
return { 'x-data-source': dataSource };
// return { 'x-connection': dataSource };
}
}, [dataSource]);

View File

@ -1,5 +1,7 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useCollectionManager, useDataBlockProps } from '../../data-source';
import { useCollection } from '../../data-source/collection/CollectionProvider';
import { useCompile } from '../../schema-component';

View File

@ -3,7 +3,7 @@ import { expect, test } from '@tachybase/test/e2e';
import { T3529 } from './templatesOfBug';
test.describe('association form block', () => {
// https://tachybase.height.app/T-3529
//
test('should be created instead of updated', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T3529).waitForInit();
await mockRecord('general');

View File

@ -50,7 +50,7 @@ test.describe('display association fields', () => {
);
});
// https://tachybase.height.app/T-2615
//
test('should load association data', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2615).waitForInit();
await mockRecord('T2615');
@ -88,7 +88,7 @@ test.describe('display association fields', () => {
);
});
// https://tachybase.height.app/T-2845
//
test('should load association data of subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2845).waitForInit();
// 和 T2615 使用一样的数据表结构
@ -137,7 +137,7 @@ test.describe('display association fields', () => {
);
});
// https://tachybase.height.app/T-2614
//
test('should load association data in subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2614).waitForInit();
await mockRecord('T2614');
@ -165,7 +165,7 @@ test.describe('display association fields', () => {
).toHaveText(`ID:1`);
});
// https://tachybase.height.app/T-2993
//
test('should load association data of sub details', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2993).waitForInit();
const record = await mockRecord('T2993');
@ -265,7 +265,7 @@ test.describe('association fields', () => {
}
});
// fix https://tachybase.height.app/T-2200
// fix
test('should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
await mockPage(T2200).goto();

View File

@ -19,7 +19,7 @@ test.describe('variables with default value', () => {
.getByRole('textbox'),
).toHaveValue('1');
// https://tachybase.height.app/T-2805 ----------------------------------------------------------------------
// ----------------------------------------------------------------------
await page
.getByLabel('block-item-CollectionField-general-form-general.m2oField0-m2oField0')
.getByTestId('select-object-single')

View File

@ -81,7 +81,7 @@ test.describe('configure actions', () => {
await expect(page.getByRole('button', { name: 'Submit' })).not.toBeVisible();
});
// https://tachybase.height.app/T-3106
//
test('subTable: should clear form value after submit', async ({ page, mockPage }) => {
await mockPage(T3106).goto();
@ -103,7 +103,7 @@ test.describe('configure actions', () => {
).toHaveValue('test name');
});
// https://tachybase.height.app/T-3469
//
test('default values for fields should not be cleared after submission', async ({ page, mockPage }) => {
await mockPage(T3469).goto();

View File

@ -212,7 +212,7 @@ test.describe('creation form block schema settings', () => {
).toHaveValue('123');
});
// https://tachybase.height.app/T-2165
//
test('variable labels should be displayed normally', async ({ page, mockPage }) => {
await mockPage(T2165).goto();
@ -224,7 +224,7 @@ test.describe('creation form block schema settings', () => {
await expect(page.getByText('Current form / Phone')).toBeVisible();
});
// https://tachybase.height.app/T-3251
//
test('nested conditions', async ({ page, mockPage }) => {
await mockPage(T3251).goto();
@ -256,7 +256,7 @@ test.describe('creation form block schema settings', () => {
).toBeEditable();
});
// https://tachybase.height.app/T-3806
//
test('after save as block template', async ({ page, mockPage }) => {
await mockPage(T3806).goto();
@ -732,7 +732,7 @@ test.describe('creation form block schema settings', () => {
).toHaveValue('new value');
});
// fix https://tachybase.height.app/T-2174
// fix
test('should show default value option', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2174).waitForInit();
await mockRecord('test2174');

View File

@ -5696,7 +5696,7 @@ export const T3469: PageConfig = {
},
};
// https://tachybase.height.app/T-3529
//
export const T3529: PageConfig = {
collections: generalWithMultiLevelM2mFields,
pageSchema: {

View File

@ -2,7 +2,7 @@ import { expect, test } from '@tachybase/test/e2e';
import { T2187 } from '../templatesOfBug';
// fix https://tachybase.height.app/T-2187
// fix
test('in the Duplicate mode, the Roles field should not have a value after clicking it because it is not selected', async ({
page,
mockPage,

View File

@ -2,7 +2,7 @@ import { expect, test } from '@tachybase/test/e2e';
import { T2183, T2186 } from '../templatesOfBug';
// fix https://tachybase.height.app/T-2183
// fix
test('should save conditions', async ({ page, mockPage }) => {
await mockPage(T2183).goto();
await page.getByLabel('action-Filter.Action-Filter-filter-users-table').click();
@ -19,7 +19,7 @@ test('should save conditions', async ({ page, mockPage }) => {
await expect(page.getByTestId('select-filter-operator').getByText('is')).toBeVisible();
});
// fix https://tachybase.height.app/T-2186
// fix
test('the input box displayed should correspond to the field type', async ({ page, mockPage }) => {
await mockPage(T2186).goto();

View File

@ -3,7 +3,7 @@ import { expect, test } from '@tachybase/test/e2e';
import { T2797, T2838 } from './templatesOfBug';
test.describe('z-index of dialog', () => {
// https://tachybase.height.app/T-2797
//
test('edit block title', async ({ page, mockPage }) => {
await mockPage(T2797).goto();
@ -18,7 +18,7 @@ test.describe('z-index of dialog', () => {
await expect(page.getByLabel('block-item-Input-users-Block')).not.toBeVisible();
});
// https://tachybase.height.app/T-2838
//
test('multilevel modal', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2838).waitForInit();
await mockRecord('general');

View File

@ -174,7 +174,7 @@ export const Action: ComposedAction = withDynamicSchemaProps(
</ActionContextProvider>
);
// fix https://tachybase.height.app/T-3235/description
// fix /description
if (addChild) {
return wrapSSR(
<RecordProvider record={null} parent={record}>

View File

@ -31,7 +31,7 @@ describe('Filter', () => {
expect(inputs[0]).toHaveValue('aa');
expect(inputs[1]).toHaveValue('aaa');
// 点击下拉框中的选项Popover 不应该关闭。详见:https://tachybase.height.app/T-1508
// 点击下拉框中的选项Popover 不应该关闭。详见:
await userEvent.click(screen.getByText(/any/i));
await userEvent.click(screen.getByText(/all/i));
expect(tooltip).toBeInTheDocument();
@ -97,7 +97,7 @@ describe('Filter', () => {
expect(inputs[0]).toHaveValue('');
expect(inputs[1]).toHaveValue('aaa');
// 点击下拉框中的选项Popover 不应该关闭。详见:https://tachybase.height.app/T-1508
// 点击下拉框中的选项Popover 不应该关闭。详见:
await userEvent.click(screen.getByText(/any/i));
await userEvent.click(screen.getByText(/all/i));
expect(tooltip).toBeInTheDocument();

View File

@ -110,8 +110,8 @@ export function FormDialog(title: any, id: any, renderer?: any, theme?: any): IF
return (
<Modal
// fix https://tachybase.height.app/T-2797
// fix https://tachybase.height.app/T-2838
// fix
// fix
zIndex={token.zIndexPopupBase + 1000}
{...modal}
open={open}

View File

@ -1,4 +1,5 @@
import React from 'react';
import { GeneralSchemaDesigner } from '../../../schema-settings';
export const FilterFormDesigner = () => {

View File

@ -1,12 +1,15 @@
import { useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import React from 'react';
import { useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../application/schema-settings';
import { useFormBlockContext } from '../../../block-provider';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
import { useRecord } from '../../../record-provider';
import { VariableInput, getShouldChange } from '../../../schema-settings';
import { getShouldChange, VariableInput } from '../../../schema-settings';
import { SchemaSettingsDataScope } from '../../../schema-settings/SchemaSettingsDataScope';
import { useLocalVariables, useVariables } from '../../../variables';
import { useDesignable } from '../../hooks';
import { removeNullCondition } from '../filter';
@ -21,7 +24,6 @@ import {
EditTooltip,
EditValidationRules,
} from './SchemaSettingOptions';
import { SchemaSettingsDataScope } from '../../../schema-settings/SchemaSettingsDataScope';
/**
* @deprecated

View File

@ -1,9 +1,10 @@
import { ArrayCollapse, FormLayout } from '@tachybase/components';
import { Field } from '@tachybase/schema';
import { ISchema, useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import React from 'react';
import { ArrayCollapse, FormLayout } from '@tachybase/components';
import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../application/schema-settings';
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
import {
@ -13,16 +14,17 @@ import {
useCollectionManager_deprecated,
} from '../../../collection-manager';
import { useRecord } from '../../../record-provider';
import { useColumnSchema } from '../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { generalSettingsItems } from '../../../schema-items/GeneralSettings';
import { isPatternDisabled } from '../../../schema-settings/isPatternDisabled';
import { SchemaSettingsSortingRule } from '../../../schema-settings/SchemaSettingsSortingRule';
import { SchemaSettingsDateFormat } from '../../../schema-settings/SchemaSettingsDateFormat';
import { SchemaSettingsDataScope } from '../../../schema-settings/SchemaSettingsDataScope';
import { SchemaSettingsDefaultValue } from '../../../schema-settings/SchemaSettingsDefaultValue';
import { ActionType } from '../../../schema-settings/LinkageRules/type';
import { VariableInput, getShouldChange } from '../../../schema-settings/VariableInput/VariableInput';
import { useIsAllowToSetDefaultValue } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { useIsShowMultipleSwitch } from '../../../schema-settings/hooks/useIsShowMultipleSwitch';
import { isPatternDisabled } from '../../../schema-settings/isPatternDisabled';
import { ActionType } from '../../../schema-settings/LinkageRules/type';
import { SchemaSettingsDataScope } from '../../../schema-settings/SchemaSettingsDataScope';
import { SchemaSettingsDateFormat } from '../../../schema-settings/SchemaSettingsDateFormat';
import { SchemaSettingsDefaultValue } from '../../../schema-settings/SchemaSettingsDefaultValue';
import { SchemaSettingsSortingRule } from '../../../schema-settings/SchemaSettingsSortingRule';
import { getShouldChange, VariableInput } from '../../../schema-settings/VariableInput/VariableInput';
import { useLocalVariables, useVariables } from '../../../variables';
import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks';
import { isSubMode } from '../association-field/util';
@ -30,7 +32,6 @@ import { removeNullCondition } from '../filter';
import { DynamicComponentProps } from '../filter/DynamicComponent';
import { getTempFieldState } from '../form-v2/utils';
import { useColorFields } from '../table-v2/Table.Column.Designer';
import { useColumnSchema } from '../../../schema-component/antd/table-v2/Table.Column.Decorator';
/**
* @deprecated
@ -486,7 +487,6 @@ export const formItemSettings = new SchemaSettings({
'x-action': 'create',
'x-acl-action': 'create',
title: "{{t('Add new')}}",
// 'x-designer': 'Action.Designer',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actonSettings:addNew',
'x-component': 'Action',
@ -549,7 +549,6 @@ export const formItemSettings = new SchemaSettings({
'x-action': 'create',
'x-acl-action': 'create',
title: "{{t('Add new')}}",
// 'x-designer': 'Action.Designer',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actonSettings:addNew',
'x-component': 'Action',

View File

@ -1,10 +1,10 @@
import { ArrayCollapse, FormLayout } from '@tachybase/components';
import { Field } from '@tachybase/schema';
import { ISchema, Schema, useField, useFieldSchema } from '@tachybase/schema';
import { uid } from '@tachybase/schema';
import _ from 'lodash';
import React from 'react';
import { ArrayCollapse, FormLayout } from '@tachybase/components';
import { Field, ISchema, Schema, useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { useFormBlockContext } from '../../../block-provider';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
import { SchemaSettingsModalItem, SchemaSettingsSelectItem, SchemaSettingsSwitchItem } from '../../../schema-settings';
@ -12,6 +12,7 @@ import { isPatternDisabled } from '../../../schema-settings/isPatternDisabled';
import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks';
import { useOperatorList } from '../filter/useOperators';
import { isFileCollection } from './FormItem';
export const findFilterOperators = (schema: Schema) => {
while (schema) {
if (schema['x-filter-operators']) {
@ -25,16 +26,6 @@ export const findFilterOperators = (schema: Schema) => {
return {};
};
const divWrap = (schema: ISchema) => {
return {
type: 'void',
'x-component': 'div',
properties: {
[schema.name || uid()]: schema,
},
};
};
export const EditTitle = () => {
const { getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
@ -297,10 +288,6 @@ export const EditValidationRules = () => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
// return;
// if (['number'].includes(collectionField?.interface) && collectionField?.uiSchema?.['x-component-props']?.['stringMode'] === true) {
// rules['numberStringMode'] = true;
// }
if (['percent'].includes(collectionField?.interface)) {
for (const rule of rules) {
if (!!rule.maxValue || !!rule.minValue) {

View File

@ -1,5 +1,6 @@
import { render, screen, waitFor } from '@tachybase/test/client';
import React from 'react';
import { render, screen, waitFor } from '@tachybase/test/client';
import App1 from '../demos/demo1';
describe('FormItem', () => {

View File

@ -1,3 +1,4 @@
import React from 'react';
import {
APIClientProvider,
CurrentUserProvider,
@ -6,7 +7,7 @@ import {
Input,
SchemaComponent,
} from '@tachybase/client';
import React from 'react';
import { mockAPIClient } from '../../../../testUtils';
const { apiClient, mockRequest } = mockAPIClient();

View File

@ -87,7 +87,6 @@ const useLazyLoadDisplayAssociationFieldsOfForm = () => {
.then((value) => {
nextTick(() => {
const result = transformVariableValue(value, { targetCollectionField: collectionFieldRef.current });
// fix https://tachybase.height.app/T-2608
if (_.isEmpty(result) && !_.isNumber(result)) {
field.value = null;
} else {

View File

@ -82,7 +82,7 @@ const useParseDefaultValue = () => {
});
if (value == null || value === '') {
// fix https://tachybase.height.app/T-2805
// fix
field.setInitialValue(null);
await field.reset({ forceClear: true });
} else if (isSpecialCase()) {
@ -122,7 +122,6 @@ const useParseDefaultValue = () => {
const obj = { [variableName]: variable?.ctx || {} };
const path = getPath(fieldSchema.default);
const value = getValuesByPath(obj, path);
// fix https://tachybase.height.app/T-2212
if (value === undefined) {
// 返回一个随机值,确保能触发 run 函数
return Math.random();

View File

@ -47,7 +47,6 @@ export const useSpecialCase = () => {
return {
/**
* `对一` `对多` `子表格` `子表单`
* https://tachybase.feishu.cn/docx/EmNEdEBOnoQohUx2UmBcqIQ5nyh#CUdLdy6OpoPKjyx9DLPc3lqknVc
*/
isSpecialCase,
setDefaultValue,
@ -72,7 +71,6 @@ export function getParentFieldSchema(fieldSchema: Schema) {
/**
* `对一` `对多` `子表格` `子表单`
* https://tachybase.feishu.cn/docx/EmNEdEBOnoQohUx2UmBcqIQ5nyh#CUdLdy6OpoPKjyx9DLPc3lqknVc
* @returns
*/
export function isSpecialCaseField({

View File

@ -1,13 +0,0 @@
---
group:
title: Schema Components
order: 3
---
# FormItem
表单字段装饰器。除此之外,也继承了 BlockItem 的功能。
## Example
<code src="./demos/demo1.tsx"></code>

View File

@ -332,7 +332,7 @@ function getFieldValuesInCondition({ linkageRules, formValues }) {
return conditions
.map((condition) => {
// fix https://tachybase.height.app/T-3251
// fix
if ('$and' in condition || '$or' in condition) {
return run(condition);
}

View File

@ -136,8 +136,6 @@ const SideMenu = ({
const { Component, getMenuItems } = useMenuItem();
const { styles } = useStyles();
// fix https://tachybase.height.app/T-3331/description
// 使用 ref 用来防止闭包问题
const sideMenuSchemaRef = useRef(sideMenuSchema);
sideMenuSchemaRef.current = sideMenuSchema;

View File

@ -4,7 +4,6 @@ import { Popover as AntdPopover, PopoverProps } from 'antd';
/**
* https://github.com/ant-design/ant-design/issues/44119
* fix https://tachybase.height.app/T-1508
* @param props
* @returns
*/

View File

@ -119,7 +119,6 @@ const useTableColumns = () => {
const index = field.value?.indexOf(record);
return (
<RecordIndexProvider index={index}>
{/* fix https://tachybase.height.app/T-3232/description */}
{/* 如果作为关系表格区块,则 parentRecordData 应该有值;如果作为普通表格使用(如数据源管理页面的表格)则应该使用 recordData且 parentRecordData 为空 */}
<RecordProvider record={record} parent={parentRecordData || recordData}>
<RecursionField schema={s} name={record.__index || index} onlyRenderProperties />

View File

@ -79,7 +79,7 @@ export const conditionAnalyses = async ({
const conditions = ruleGroup[type];
let results = conditions.map(async (condition) => {
// fix https://tachybase.height.app/T-3152
// fix
if ('$and' in condition || '$or' in condition) {
return await conditionAnalyses({ ruleGroup: condition, variables, localVariables });
}

View File

@ -1,4 +1,5 @@
import { createContext } from 'react';
import { ISchemaComponentContext } from './types';
export const SchemaComponentContext = createContext<ISchemaComponentContext>({});

View File

@ -1,4 +1,4 @@
import React, { PropsWithChildren, useMemo } from 'react';
import React, { useMemo } from 'react';
import { createForm, Schema } from '@tachybase/schema';
import { useRequest } from '../../api-client';
@ -16,11 +16,12 @@ export interface RemoteSchemaComponentProps {
hidden?: any;
onlyRenderProperties?: boolean;
noForm?: boolean;
children?: React.ReactNode;
}
const defaultTransform = (s: Schema) => s;
const RequestSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) => {
const RequestSchemaComponent = (props: RemoteSchemaComponentProps) => {
const {
noForm,
onlyRenderProperties,
@ -45,18 +46,18 @@ const RequestSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) =>
reset && reset();
},
});
if (loading || hidden) {
if (loading || hidden || !data) {
return;
}
return noForm ? (
<SchemaComponent memoized components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
<SchemaComponent components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
) : (
<FormProvider form={form}>
<SchemaComponent memoized components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
<SchemaComponent components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
</FormProvider>
);
};
export const RemoteSchemaComponent: React.FC<PropsWithChildren<RemoteSchemaComponentProps>> = (props) => {
export const RemoteSchemaComponent = (props: RemoteSchemaComponentProps) => {
return props.uid ? <RequestSchemaComponent {...props} /> : null;
};

View File

@ -1,4 +1,4 @@
import React, { memo, PropsWithChildren, useContext, useMemo } from 'react';
import React, { memo, useContext, useMemo } from 'react';
import { ExpressionScope, SchemaComponentsContext, SchemaOptionsContext } from '@tachybase/schema';
import { ISchemaComponentOptionsProps } from '../types';
@ -8,7 +8,7 @@ export const useSchemaOptionsContext = () => {
return options || {};
};
export const SchemaComponentOptions: React.FC<PropsWithChildren<ISchemaComponentOptionsProps>> = memo((props) => {
export const SchemaComponentOptions = memo((props: ISchemaComponentOptionsProps) => {
const { children } = props;
const options = useSchemaOptionsContext();
const components = useMemo(() => {

View File

@ -1,4 +1,4 @@
import React, { PropsWithChildren, useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import { createForm, FormProvider, Schema, uid } from '@tachybase/schema';
import { useTranslation } from 'react-i18next';
@ -42,10 +42,11 @@ const Registry = {
Schema.registerCompiler(Registry.compile);
export const SchemaComponentProvider: React.FC<PropsWithChildren<ISchemaComponentProvider>> = (props) => {
export const SchemaComponentProvider = (props: ISchemaComponentProvider) => {
const { designable, onDesignableChange, components, children } = props;
const [uidValue, setUid] = useState(uid());
const [formId, setFormId] = useState(uid());
// eslint-disable-next-line react-hooks/exhaustive-deps
const form = useMemo(() => props.form || createForm(), [formId]);
const { t } = useTranslation();
const scope = useMemo(() => {
@ -69,12 +70,12 @@ export const SchemaComponentProvider: React.FC<PropsWithChildren<ISchemaComponen
onDesignableChange?.(value);
},
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[uidValue, scope, components, designable, active],
);
return (
// TODO: 临时修复,处理模板类型的关联数据第一次进入无法加载
<SchemaComponentContext.Provider value={{ ...schemaComponentContextValue }}>
<SchemaComponentContext.Provider value={schemaComponentContextValue}>
<FormProvider form={form}>
<SchemaComponentOptions inherit scope={scope} components={components}>
{children}

View File

@ -1,4 +1,5 @@
import { useContext } from 'react';
import { SchemaComponentContext } from '../context';
export function useSchemaComponentContext() {

View File

@ -1,6 +1,5 @@
import { Form } from '@tachybase/schema';
import { IRecursionFieldProps, ISchemaFieldProps, SchemaReactComponents } from '@tachybase/schema';
import React from 'react';
import { Form, IRecursionFieldProps, ISchemaFieldProps, SchemaReactComponents } from '@tachybase/schema';
export interface ISchemaComponentContext {
scope?: any;
@ -9,7 +8,7 @@ export interface ISchemaComponentContext {
reset?: () => void;
designable?: boolean;
setDesignable?: (value: boolean) => void;
SchemaField?: React.FC<ISchemaFieldProps>;
SchemaField?: (props: ISchemaFieldProps) => React.ReactNode;
}
export interface ISchemaComponentProvider {
@ -18,6 +17,7 @@ export interface ISchemaComponentProvider {
form?: Form;
scope?: any;
components?: SchemaReactComponents;
children?: React.ReactNode;
}
export interface IRecursionComponentProps extends IRecursionFieldProps {
@ -29,4 +29,5 @@ export interface ISchemaComponentOptionsProps {
scope?: any;
components?: SchemaReactComponents;
inherit?: boolean;
children?: React.ReactNode;
}

View File

@ -116,7 +116,7 @@ export const AssignedField = (props: AssignedFieldProps) => {
const currentForm = scope.find((item) => item.value === '$nForm');
const fields = getCollectionFields(name);
// fix https://tachybase.height.app/T-1355
// fix
// 工作流人工节点的 `自定义表单` 区块,与其它表单区块不同,根据它的数据表名称,获取到的字段列表为空,所以需要在这里特殊处理一下
if (!fields?.length && currentForm) {
currentForm.children = formatVariableScop(currentFormFields);

View File

@ -890,7 +890,7 @@ export const useCollectionDataSourceItems = ({
getTemplatesByCollection,
t,
}).sort((item) => {
// fix https://tachybase.height.app/T-3551
// fix
const inherits = _.toArray(collection?.inherits || []);
if (item.name === collection?.name || inherits.some((inheritName) => inheritName === item.name)) return -1;
}),
@ -898,7 +898,7 @@ export const useCollectionDataSourceItems = ({
}));
}, [allCollections, collection?.inherits, collection?.name, componentName, getTemplatesByCollection, t]);
// https://tachybase.height.app/T-3821
//
// showAssociationFields 的值是固定不变的,所以在 if 语句里使用 hooks 是安全的
if (showAssociationFields) {
// eslint-disable-next-line react-hooks/rules-of-hooks

View File

@ -81,7 +81,6 @@ export const SchemaSettingsDefaultValue = function DefaultValueConfigure(props:
const currentForm = scope.find((item) => item.value === '$nForm');
const fields = getCollectionFields(name);
// fix https://tachybase.height.app/T-1355
// 工作流人工节点的 `自定义表单` 区块,与其它表单区块不同,根据它的数据表名称,获取到的字段列表为空,所以需要在这里特殊处理一下
if (!fields?.length && currentForm) {
currentForm.children = formatVariableScop(getFields());

View File

@ -37,7 +37,7 @@ export const useFormVariable = ({ collectionName, collectionField, schema, noDis
collectionName: collectionName,
noDisabled,
returnFields: (fields, option) => {
// fix https://tachybase.height.app/T-2277
// fix
return fields;
// const activeFieldsName = getActiveFieldsName?.('form') || [];
@ -77,7 +77,7 @@ export const useCurrentFormVariable = ({
collectionName: collectionName,
noDisabled,
returnFields: (fields, option) => {
// fix https://tachybase.height.app/T-2277
// fix
return fields;
// const activeFieldsName = getActiveFieldsName?.('form') || [];

View File

@ -43,7 +43,7 @@ export const useIterationVariable = ({
collectionName: currentCollection,
noDisabled,
returnFields: (fields, option) => {
// fix https://tachybase.height.app/T-2277
// fix
return fields;
// const activeFieldsName = getActiveFieldsName?.('nester') || [];
@ -90,7 +90,7 @@ export const useCurrentObjectVariable = ({
collectionName: collectionOfCurrentObject?.name || collection?.name,
noDisabled,
returnFields: (fields, option) => {
// fix https://tachybase.height.app/T-2277
// fix
return fields;
// const activeFieldsName = getActiveFieldsName?.('nester') || [];

View File

@ -1,8 +1,9 @@
import { observer, useField, useFieldSchema } from '@tachybase/schema';
import React, { createContext, useContext, useMemo } from 'react';
import { RemoteSchemaComponent, useDesignable } from '..';
import { useSchemaTemplateManager } from './SchemaTemplateManagerProvider';
import React, { createContext, useCallback, useContext, useMemo } from 'react';
import { useField, useFieldSchema } from '@tachybase/schema';
import { RemoteSchemaComponent, useDesignable, useSchemaComponentContext } from '..';
import { useTemplateBlockContext } from '../block-provider/TemplateBlockProvider';
import { useSchemaTemplateManager } from './useSchemaTemplateManager';
const BlockTemplateContext = createContext<any>({});
BlockTemplateContext.displayName = 'BlockTemplateContext';
@ -11,26 +12,31 @@ export const useBlockTemplateContext = () => {
return useContext(BlockTemplateContext);
};
export const BlockTemplate = observer(
(props: any) => {
const { templateId } = props;
const { getTemplateById } = useSchemaTemplateManager();
const field = useField();
const fieldSchema = useFieldSchema();
const { dn } = useDesignable();
const template = useMemo(() => getTemplateById(templateId), [templateId]);
const { onTemplateSuccess } = useTemplateBlockContext();
export const BlockTemplate = (props: any) => {
const { templateId } = props;
const { getTemplateById } = useSchemaTemplateManager();
const field = useField();
const fieldSchema = useFieldSchema();
const { dn } = useDesignable();
// eslint-disable-next-line react-hooks/exhaustive-deps
const template = useMemo(() => getTemplateById(templateId), [templateId]);
const { onTemplateSuccess } = useTemplateBlockContext();
const { refresh } = useSchemaComponentContext();
const onSuccess = (data) => {
const onSuccess = useCallback(
(data) => {
fieldSchema['x-linkage-rules'] = data?.data?.['x-linkage-rules'] || [];
fieldSchema.setProperties(data?.data?.properties);
onTemplateSuccess?.();
};
return template ? (
<BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}>
<RemoteSchemaComponent noForm uid={template?.uid} onSuccess={onSuccess} />
</BlockTemplateContext.Provider>
) : null;
},
{ displayName: 'BlockTemplate' },
);
refresh();
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[template],
);
return template ? (
<BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}>
<RemoteSchemaComponent noForm uid={template?.uid} onSuccess={onSuccess} />
</BlockTemplateContext.Provider>
) : null;
};

View File

@ -4,14 +4,13 @@ import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout';
import { Input, Spin } from 'antd';
import { useNavigate, useParams } from 'react-router-dom';
import { useAPIClient, useRequest, useSchemaTemplateManager } from '..';
import { useRequest, useSchemaTemplateManager } from '..';
import { RemoteSchemaComponent, SchemaComponentContext } from '../schema-component';
const EditableTitle = (props) => {
const [title, setTitle] = useState(props.title);
const [visible, setVisible] = useState(false);
const { refresh } = useSchemaTemplateManager();
const api = useAPIClient();
const { run } = useRequest(
{
resource: 'uiSchemaTemplates',

View File

@ -1,14 +1,10 @@
import { ISchema, useFieldSchema } from '@tachybase/schema';
import { uid } from '@tachybase/schema';
import { cloneDeep } from 'lodash';
import React, { ReactNode, createContext, useContext, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import React, { createContext, useMemo } from 'react';
import { ISchema, uid, useFieldSchema } from '@tachybase/schema';
import { useAPIClient, useRequest } from '../api-client';
import { Plugin } from '../application/Plugin';
import { useAppSpin } from '../application/hooks/useAppSpin';
import { useCollectionManager_deprecated } from '../collection-manager';
import { BlockTemplate } from './BlockTemplate';
import { DEFAULT_DATA_SOURCE_KEY } from '../data-source';
import { useSchemaTemplateManager } from './useSchemaTemplateManager';
export const SchemaTemplateManagerContext = createContext<any>({});
SchemaTemplateManagerContext.displayName = 'SchemaTemplateManagerContext';
@ -22,7 +18,7 @@ export const SchemaTemplateManagerProvider: React.FC<any> = (props) => {
);
};
const regenerateUid = (s: ISchema) => {
export const regenerateUid = (s: ISchema) => {
s['name'] = s['x-uid'] = uid();
Object.keys(s.properties || {}).forEach((key) => {
regenerateUid(s.properties[key]);
@ -30,106 +26,21 @@ const regenerateUid = (s: ISchema) => {
};
export const useSchemaTemplate = () => {
const { getTemplateBySchema, templates } = useSchemaTemplateManager();
const { getTemplateBySchema } = useSchemaTemplateManager();
const fieldSchema = useFieldSchema();
const schemaId = fieldSchema['x-uid'];
const templateKey = fieldSchema['x-template-key'];
// console.log('templateKey', { schemaId, templateKey })
return useMemo(() => getTemplateBySchema(fieldSchema), [schemaId, templateKey]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const result = useMemo(() => getTemplateBySchema(fieldSchema), [schemaId, templateKey]);
return result;
};
export const useSchemaTemplateManager = () => {
const { getInheritCollections } = useCollectionManager_deprecated();
const { refresh, templates = [] } = useContext(SchemaTemplateManagerContext);
const InternalRemoteSchemaTemplateManagerProvider = (props) => {
const api = useAPIClient();
return {
templates,
refresh,
async getTemplateSchemaByMode(options) {
const { mode, template } = options;
if (mode === 'copy') {
const { data } = await api.request({
url: `/uiSchemas:getJsonSchema/${template.uid}?includeAsyncNode=true`,
});
const s = data?.data || {};
regenerateUid(s);
return cloneDeep(s);
} else if (mode === 'reference') {
return {
type: 'void',
'x-component': 'BlockTemplate',
'x-component-props': {
templateId: template.key,
},
};
}
},
async copyTemplateSchema(template) {
const { data } = await api.request({
url: `/uiSchemas:getJsonSchema/${template.uid}?includeAsyncNode=true`,
});
const s = data?.data || {};
regenerateUid(s);
return cloneDeep(s);
},
async saveAsTemplate(values) {
const { uid: schemaId } = values;
const key = uid();
await api.resource('uiSchemas').saveAsTemplate({
filterByTk: schemaId,
values: {
key,
...values,
},
});
await refresh();
return { key };
},
getTemplateBySchema(schema) {
if (!schema) return;
const templateKey = schema['x-template-key'];
if (templateKey) {
return templates?.find((template) => template.key === templateKey);
}
const schemaId = schema['x-uid'];
if (schemaId) {
return templates?.find((template) => template.uid === schemaId);
}
},
getTemplateBySchemaId(schemaId) {
if (!schemaId) {
return null;
}
return templates?.find((template) => template.uid === schemaId);
},
getTemplateById(key) {
return templates?.find((template) => template.key === key);
},
getTemplatesByCollection(dataSource: string, collectionName: string) {
const parentCollections = getInheritCollections(collectionName, dataSource);
const totalCollections = parentCollections.concat([collectionName]);
const items = templates?.filter?.(
(template) =>
(template.dataSourceKey || DEFAULT_DATA_SOURCE_KEY) === dataSource &&
totalCollections.includes(template.collectionName),
);
return items || [];
},
getTemplatesByComponentName(componentName: string): Array<any> {
const items = templates?.filter?.((template) => template.componentName === componentName);
return items || [];
},
};
};
const Internal = (props) => {
const api = useAPIClient();
const { render } = useAppSpin();
const options = {
resource: 'uiSchemaTemplates',
action: 'list',
params: {
// appends: ['collection'],
paginate: false,
},
};
@ -137,9 +48,11 @@ const Internal = (props) => {
const service = useRequest<{
data: any[];
}>(options);
if (service.loading) {
return render();
if (service.loading && !service.data) {
return;
}
return (
<SchemaTemplateManagerProvider
refresh={async () => {
@ -153,19 +66,14 @@ const Internal = (props) => {
);
};
export const RemoteSchemaTemplateManagerProvider: React.FC<{ children?: ReactNode }> = (props) => {
const location = useLocation();
if (location.pathname.startsWith('/admin')) {
return <Internal {...props} />;
}
return <>{props.children}</>;
export const RemoteSchemaTemplateManagerProvider = (props: { children?: React.ReactNode }) => {
return <InternalRemoteSchemaTemplateManagerProvider>{props.children}</InternalRemoteSchemaTemplateManagerProvider>;
};
export class RemoteSchemaTemplateManagerPlugin extends Plugin {
async load() {
this.addRoutes();
this.addComponents();
this.app.use(RemoteSchemaTemplateManagerProvider);
this.app.addProviders([RemoteSchemaTemplateManagerProvider]);
}
addComponents() {
@ -173,11 +81,4 @@ export class RemoteSchemaTemplateManagerPlugin extends Plugin {
BlockTemplate,
});
}
addRoutes() {
this.app.router.add('admin.plugins.block-templates-key', {
path: '/admin/plugins/block-templates/:key',
Component: 'BlockTemplateDetails',
});
}
}

View File

@ -1,3 +1,4 @@
export * from './BlockTemplateDetails';
export * from './BlockTemplatePage';
export * from './SchemaTemplateManagerProvider';
export * from './useSchemaTemplateManager';

View File

@ -1,8 +1,9 @@
import React, { useMemo } from 'react';
import { useDataSourceManager, useCollectionRecordData } from '../../data-source';
import { useCollectionRecordData, useDataSourceManager } from '../../data-source';
import { useCompile } from '../../schema-component';
export const CollectionTitle = (props) => {
export const CollectionTitle = () => {
const { dataSourceKey = 'main', collectionName } = useCollectionRecordData();
const dataSourceManager = useDataSourceManager();
const compile = useCompile();

View File

@ -1,8 +1,8 @@
import { ISchema } from '@tachybase/schema';
import { uid } from '@tachybase/schema';
import { ISchema, uid } from '@tachybase/schema';
import { useBulkDestroyActionProps, useDestroyActionProps, useUpdateActionProps } from '../../block-provider/hooks';
import { useSchemaTemplateManager } from '../SchemaTemplateManagerProvider';
import { uiSchemaTemplatesCollection } from '../collections/uiSchemaTemplates';
import { useSchemaTemplateManager } from '../useSchemaTemplateManager';
import { CollectionTitle } from './CollectionTitle';
const useUpdateSchemaTemplateActionProps = () => {
@ -50,7 +50,6 @@ export const uiSchemaTemplatesSchema: ISchema = {
action: 'list',
params: {
pageSize: 20,
// appends: ['collection'],
sort: ['-createdAt'],
},
rowKey: 'key',
@ -106,14 +105,6 @@ export const uiSchemaTemplatesSchema: ISchema = {
split: '|',
},
properties: {
// view: {
// title: '{{ t("View") }}',
// 'x-action': 'view',
// 'x-component': 'RecordLink',
// 'x-component-props': {
// to: '/admin/plugins/block-templates/${record.key}',
// },
// },
edit: {
type: 'void',
title: '{{ t("Edit") }}',

View File

@ -0,0 +1,96 @@
import { useCallback, useContext } from 'react';
import { uid } from '@tachybase/schema';
import { cloneDeep } from 'lodash';
import { useAPIClient } from '../api-client';
import { useCollectionManager_deprecated } from '../collection-manager';
import { DEFAULT_DATA_SOURCE_KEY } from '../data-source';
import { regenerateUid, SchemaTemplateManagerContext } from './SchemaTemplateManagerProvider';
export const useSchemaTemplateManager = () => {
const { getInheritCollections } = useCollectionManager_deprecated();
const { refresh, templates = [] } = useContext(SchemaTemplateManagerContext);
const api = useAPIClient();
return {
templates,
refresh,
async getTemplateSchemaByMode(options) {
const { mode, template } = options;
if (mode === 'copy') {
const { data } = await api.request({
url: `/uiSchemas:getJsonSchema/${template.uid}?includeAsyncNode=true`,
});
const s = data?.data || {};
regenerateUid(s);
return cloneDeep(s);
} else if (mode === 'reference') {
return {
type: 'void',
'x-component': 'BlockTemplate',
'x-component-props': {
templateId: template.key,
},
};
}
},
async copyTemplateSchema(template) {
const { data } = await api.request({
url: `/uiSchemas:getJsonSchema/${template.uid}?includeAsyncNode=true`,
});
const s = data?.data || {};
regenerateUid(s);
return cloneDeep(s);
},
async saveAsTemplate(values) {
const { uid: schemaId } = values;
const key = uid();
await api.resource('uiSchemas').saveAsTemplate({
filterByTk: schemaId,
values: {
key,
...values,
},
});
await refresh();
return { key };
},
getTemplateBySchema: useCallback(
(schema) => {
if (!schema) return;
const templateKey = schema['x-template-key'];
if (templateKey) {
return templates?.find((template) => template.key === templateKey);
}
const schemaId = schema['x-uid'];
if (schemaId) {
return templates?.find((template) => template.uid === schemaId);
}
},
[templates],
),
getTemplateBySchemaId(schemaId) {
if (!schemaId) {
return null;
}
return templates?.find((template) => template.uid === schemaId);
},
getTemplateById(key) {
return templates?.find((template) => template.key === key);
},
getTemplatesByCollection(dataSource: string, collectionName: string) {
const parentCollections = getInheritCollections(collectionName, dataSource);
const totalCollections = parentCollections.concat([collectionName]);
const items = templates?.filter?.(
(template) =>
(template.dataSourceKey || DEFAULT_DATA_SOURCE_KEY) === dataSource &&
totalCollections.includes(template.collectionName),
);
return items || [];
},
getTemplatesByComponentName(componentName: string): Array<any> {
const items = templates?.filter?.((template) => template.componentName === componentName);
return items || [];
},
};
};

View File

@ -136,7 +136,7 @@ const VariablesProvider = ({ children }) => {
clearRequested(url);
}
// fix https://tachybase.height.app/T-3144使用 `raw` 方法是为了避免触发 autorun,以修复 T-3144 的错误
// 使用 `raw` 方法是为了避免触发 autorun
if (!raw(current)[key]) {
// 把接口返回的数据保存起来,避免重复请求
raw(current)[key] = data.data.data;
@ -296,7 +296,7 @@ function shouldToRequest(value) {
// value 有可能是一个响应式对象,使用 untracked 可以避免意外触发 autorun
untracked(() => {
// fix https://tachybase.height.app/T-2502
// fix
// 兼容 `对多` 和 `对一` 子表单子表格字段的情况
if (JSON.stringify(value) === '[{}]' || JSON.stringify(value) === '{}') {
result = true;

View File

@ -195,7 +195,7 @@ describe('array field operator', function () {
expect(filter3[0].get('name')).toEqual(t2.get('name'));
});
// fix https://tachybase.height.app/T-2803
// fix
test('$anyOf with string', async () => {
const filter3 = await Test.repository.find({
filter: {
@ -228,7 +228,7 @@ describe('array field operator', function () {
expect(filter[0].get('name')).toEqual(t1.get('name'));
});
// fix https://tachybase.height.app/T-2803
// fix
test('$noneOf with string', async () => {
const filter = await Test.repository.find({
filter: {

View File

@ -477,7 +477,7 @@ test.describe('configure fields', () => {
}
});
// https://tachybase.height.app/T-2868
//
test('sequence rules: z-index of configure drawer', async ({ page, mockCollections }) => {
const collectionManagerPage = new CollectionManagerPage(page);
await collectionManagerPage.goto();

View File

@ -665,7 +665,7 @@ test.describe('table column & table', () => {
});
test.describe('table column & sub-table', () => {
// https://tachybase.height.app/T-3377
//
test('title field', async ({ page, mockPage }) => {
await mockPage(T3377).goto();

View File

@ -210,7 +210,7 @@ export function RuleConfigForm() {
<Button
type="link"
onClick={() => {
// fix https://tachybase.height.app/T-2868
// fix
FormDrawer(
{
title: compile(ruleType.title),