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'; import { APIClient } from './APIClient';
export const APIClientContext = createContext<APIClient>(new APIClient()); export const APIClientContext = createContext<APIClient>(new APIClient());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import { Field } from '@tachybase/schema'; import { Field, useField, useFieldSchema } from '@tachybase/schema';
import { useField, useFieldSchema } from '@tachybase/schema';
import { useCollectionField, useCollectionManager } from '../data-source';
import { useIsFileField } from '../schema-component/antd/form-item/FormItem.Settings'; import { useIsFileField } from '../schema-component/antd/form-item/FormItem.Settings';
import { useColumnSchema } from '../schema-component/antd/table-v2/Table.Column.Decorator'; import { useColumnSchema } from '../schema-component/antd/table-v2/Table.Column.Decorator';
import { useCollectionField, useCollectionManager } from '../data-source';
export function useFieldComponentName(): string { export function useFieldComponentName(): string {
const { fieldSchema: tableColumnSchema, collectionField: tableColumnField } = useColumnSchema(); 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); 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 { 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 { useAPIClient } from '../../api-client';
import { useCollectionManager } from '../collection';
import { CollectionRecord } from '../collection-record'; import { CollectionRecord } from '../collection-record';
import { useDataSourceHeaders } from '../utils'; import { useDataSourceHeaders } from '../utils';
import { useDataBlockProps } from './DataBlockProvider';
export const DataBlockResourceContext = createContext<IResource>(null); export const DataBlockResourceContext = createContext<IResource>(null);
DataBlockResourceContext.displayName = 'DataBlockResourceContext'; DataBlockResourceContext.displayName = 'DataBlockResourceContext';
export const DataBlockResourceProvider: FC<{ children?: ReactNode }> = ({ children }) => { export const DataBlockResourceProvider = ({ children }: { children?: ReactNode }) => {
const dataBlockProps = useDataBlockProps(); const dataBlockProps = useDataBlockProps();
const cm = useCollectionManager(); const cm = useCollectionManager();
const { association, collection, dataSource, sourceId, parentRecord } = dataBlockProps; 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 type { DataSourceManager } from './DataSourceManager';
import { CollectionManager } from '../collection';
type LoadCallback = (collections: CollectionOptions[]) => void; type LoadCallback = (collections: CollectionOptions[]) => void;
export interface DataSourceOptions { 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 _ 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_KEY = 'main';
export const DEFAULT_DATA_SOURCE_TITLE = '{{t("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'; import type { DataSourceManager } from './DataSourceManager';
export const DataSourceManagerContext = createContext<DataSourceManager>(null); export const DataSourceManagerContext = createContext<DataSourceManager>(null);
@ -9,7 +10,7 @@ export interface DataSourceManagerProviderProps {
children?: ReactNode; children?: ReactNode;
} }
export const DataSourceManagerProvider: FC<DataSourceManagerProviderProps> = ({ children, dataSourceManager }) => { export const DataSourceManagerProvider = ({ children, dataSourceManager }: DataSourceManagerProviderProps) => {
return <DataSourceManagerContext.Provider value={dataSourceManager}>{children}</DataSourceManagerContext.Provider>; 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 type { DataSource } from './DataSource';
import { useDataSourceManager } from './DataSourceManagerProvider'; 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); export const DataSourceContext = createContext<DataSource>(null);
DataSourceContext.displayName = 'DataSourceContext'; DataSourceContext.displayName = 'DataSourceContext';
@ -15,7 +17,7 @@ export interface DataSourceProviderProps {
children?: ReactNode; children?: ReactNode;
} }
export const DataSourceProvider: FC<DataSourceProviderProps> = ({ children, dataSource }) => { export const DataSourceProvider = ({ children, dataSource }: DataSourceProviderProps) => {
const dataSourceManager = useDataSourceManager(); const dataSourceManager = useDataSourceManager();
const { t } = useTranslation(); const { t } = useTranslation();
const { refresh } = useSchemaComponentContext(); const { refresh } = useSchemaComponentContext();

View File

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

View File

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

View File

@ -3,7 +3,7 @@ import { expect, test } from '@tachybase/test/e2e';
import { T3529 } from './templatesOfBug'; import { T3529 } from './templatesOfBug';
test.describe('association form block', () => { test.describe('association form block', () => {
// https://tachybase.height.app/T-3529 //
test('should be created instead of updated', async ({ page, mockPage, mockRecord }) => { test('should be created instead of updated', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T3529).waitForInit(); const nocoPage = await mockPage(T3529).waitForInit();
await mockRecord('general'); 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 }) => { test('should load association data', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2615).waitForInit(); const nocoPage = await mockPage(T2615).waitForInit();
await mockRecord('T2615'); 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 }) => { test('should load association data of subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2845).waitForInit(); const nocoPage = await mockPage(T2845).waitForInit();
// 和 T2615 使用一样的数据表结构 // 和 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 }) => { test('should load association data in subform', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2614).waitForInit(); const nocoPage = await mockPage(T2614).waitForInit();
await mockRecord('T2614'); await mockRecord('T2614');
@ -165,7 +165,7 @@ test.describe('display association fields', () => {
).toHaveText(`ID:1`); ).toHaveText(`ID:1`);
}); });
// https://tachybase.height.app/T-2993 //
test('should load association data of sub details', async ({ page, mockPage, mockRecord }) => { test('should load association data of sub details', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T2993).waitForInit(); const nocoPage = await mockPage(T2993).waitForInit();
const record = await mockRecord('T2993'); 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 }) => { test('should be possible to change the value of the association field normally', async ({ page, mockPage }) => {
await mockPage(T2200).goto(); await mockPage(T2200).goto();

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import { expect, test } from '@tachybase/test/e2e';
import { T2187 } from '../templatesOfBug'; 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 ({ test('in the Duplicate mode, the Roles field should not have a value after clicking it because it is not selected', async ({
page, page,
mockPage, mockPage,

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ describe('Filter', () => {
expect(inputs[0]).toHaveValue('aa'); expect(inputs[0]).toHaveValue('aa');
expect(inputs[1]).toHaveValue('aaa'); 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(/any/i));
await userEvent.click(screen.getByText(/all/i)); await userEvent.click(screen.getByText(/all/i));
expect(tooltip).toBeInTheDocument(); expect(tooltip).toBeInTheDocument();
@ -97,7 +97,7 @@ describe('Filter', () => {
expect(inputs[0]).toHaveValue(''); expect(inputs[0]).toHaveValue('');
expect(inputs[1]).toHaveValue('aaa'); 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(/any/i));
await userEvent.click(screen.getByText(/all/i)); await userEvent.click(screen.getByText(/all/i));
expect(tooltip).toBeInTheDocument(); expect(tooltip).toBeInTheDocument();

View File

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

View File

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

View File

@ -1,12 +1,15 @@
import { useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import React from 'react'; import React from 'react';
import { useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../application/schema-settings'; import { SchemaSettings } from '../../../application/schema-settings';
import { useFormBlockContext } from '../../../block-provider'; import { useFormBlockContext } from '../../../block-provider';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager'; import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
import { useRecord } from '../../../record-provider'; 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 { useLocalVariables, useVariables } from '../../../variables';
import { useDesignable } from '../../hooks'; import { useDesignable } from '../../hooks';
import { removeNullCondition } from '../filter'; import { removeNullCondition } from '../filter';
@ -21,7 +24,6 @@ import {
EditTooltip, EditTooltip,
EditValidationRules, EditValidationRules,
} from './SchemaSettingOptions'; } from './SchemaSettingOptions';
import { SchemaSettingsDataScope } from '../../../schema-settings/SchemaSettingsDataScope';
/** /**
* @deprecated * @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 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 { useTranslation } from 'react-i18next';
import { SchemaSettings } from '../../../application/schema-settings'; import { SchemaSettings } from '../../../application/schema-settings';
import { useFormBlockContext } from '../../../block-provider/FormBlockProvider'; import { useFormBlockContext } from '../../../block-provider/FormBlockProvider';
import { import {
@ -13,16 +14,17 @@ import {
useCollectionManager_deprecated, useCollectionManager_deprecated,
} from '../../../collection-manager'; } from '../../../collection-manager';
import { useRecord } from '../../../record-provider'; import { useRecord } from '../../../record-provider';
import { useColumnSchema } from '../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { generalSettingsItems } from '../../../schema-items/GeneralSettings'; 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 { useIsAllowToSetDefaultValue } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { useIsShowMultipleSwitch } from '../../../schema-settings/hooks/useIsShowMultipleSwitch'; 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 { useLocalVariables, useVariables } from '../../../variables';
import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks'; import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks';
import { isSubMode } from '../association-field/util'; import { isSubMode } from '../association-field/util';
@ -30,7 +32,6 @@ import { removeNullCondition } from '../filter';
import { DynamicComponentProps } from '../filter/DynamicComponent'; import { DynamicComponentProps } from '../filter/DynamicComponent';
import { getTempFieldState } from '../form-v2/utils'; import { getTempFieldState } from '../form-v2/utils';
import { useColorFields } from '../table-v2/Table.Column.Designer'; import { useColorFields } from '../table-v2/Table.Column.Designer';
import { useColumnSchema } from '../../../schema-component/antd/table-v2/Table.Column.Decorator';
/** /**
* @deprecated * @deprecated
@ -486,7 +487,6 @@ export const formItemSettings = new SchemaSettings({
'x-action': 'create', 'x-action': 'create',
'x-acl-action': 'create', 'x-acl-action': 'create',
title: "{{t('Add new')}}", title: "{{t('Add new')}}",
// 'x-designer': 'Action.Designer',
'x-toolbar': 'ActionSchemaToolbar', 'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actonSettings:addNew', 'x-settings': 'actonSettings:addNew',
'x-component': 'Action', 'x-component': 'Action',
@ -549,7 +549,6 @@ export const formItemSettings = new SchemaSettings({
'x-action': 'create', 'x-action': 'create',
'x-acl-action': 'create', 'x-acl-action': 'create',
title: "{{t('Add new')}}", title: "{{t('Add new')}}",
// 'x-designer': 'Action.Designer',
'x-toolbar': 'ActionSchemaToolbar', 'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actonSettings:addNew', 'x-settings': 'actonSettings:addNew',
'x-component': 'Action', '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 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 { useTranslation } from 'react-i18next';
import { useFormBlockContext } from '../../../block-provider'; import { useFormBlockContext } from '../../../block-provider';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager'; import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
import { SchemaSettingsModalItem, SchemaSettingsSelectItem, SchemaSettingsSwitchItem } from '../../../schema-settings'; import { SchemaSettingsModalItem, SchemaSettingsSelectItem, SchemaSettingsSwitchItem } from '../../../schema-settings';
@ -12,6 +12,7 @@ import { isPatternDisabled } from '../../../schema-settings/isPatternDisabled';
import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks'; import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks';
import { useOperatorList } from '../filter/useOperators'; import { useOperatorList } from '../filter/useOperators';
import { isFileCollection } from './FormItem'; import { isFileCollection } from './FormItem';
export const findFilterOperators = (schema: Schema) => { export const findFilterOperators = (schema: Schema) => {
while (schema) { while (schema) {
if (schema['x-filter-operators']) { if (schema['x-filter-operators']) {
@ -25,16 +26,6 @@ export const findFilterOperators = (schema: Schema) => {
return {}; return {};
}; };
const divWrap = (schema: ISchema) => {
return {
type: 'void',
'x-component': 'div',
properties: {
[schema.name || uid()]: schema,
},
};
};
export const EditTitle = () => { export const EditTitle = () => {
const { getCollectionJoinField } = useCollectionManager_deprecated(); const { getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated(); const { getField } = useCollection_deprecated();
@ -297,10 +288,6 @@ export const EditValidationRules = () => {
const schema = { const schema = {
['x-uid']: fieldSchema['x-uid'], ['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)) { if (['percent'].includes(collectionField?.interface)) {
for (const rule of rules) { for (const rule of rules) {
if (!!rule.maxValue || !!rule.minValue) { if (!!rule.maxValue || !!rule.minValue) {

View File

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

View File

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

View File

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

View File

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

View File

@ -47,7 +47,6 @@ export const useSpecialCase = () => {
return { return {
/** /**
* `对一` `对多` `子表格` `子表单` * `对一` `对多` `子表格` `子表单`
* https://tachybase.feishu.cn/docx/EmNEdEBOnoQohUx2UmBcqIQ5nyh#CUdLdy6OpoPKjyx9DLPc3lqknVc
*/ */
isSpecialCase, isSpecialCase,
setDefaultValue, setDefaultValue,
@ -72,7 +71,6 @@ export function getParentFieldSchema(fieldSchema: Schema) {
/** /**
* `对一` `对多` `子表格` `子表单` * `对一` `对多` `子表格` `子表单`
* https://tachybase.feishu.cn/docx/EmNEdEBOnoQohUx2UmBcqIQ5nyh#CUdLdy6OpoPKjyx9DLPc3lqknVc
* @returns * @returns
*/ */
export function isSpecialCaseField({ 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 return conditions
.map((condition) => { .map((condition) => {
// fix https://tachybase.height.app/T-3251 // fix
if ('$and' in condition || '$or' in condition) { if ('$and' in condition || '$or' in condition) {
return run(condition); return run(condition);
} }

View File

@ -136,8 +136,6 @@ const SideMenu = ({
const { Component, getMenuItems } = useMenuItem(); const { Component, getMenuItems } = useMenuItem();
const { styles } = useStyles(); const { styles } = useStyles();
// fix https://tachybase.height.app/T-3331/description
// 使用 ref 用来防止闭包问题
const sideMenuSchemaRef = useRef(sideMenuSchema); const sideMenuSchemaRef = useRef(sideMenuSchema);
sideMenuSchemaRef.current = 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 * https://github.com/ant-design/ant-design/issues/44119
* fix https://tachybase.height.app/T-1508
* @param props * @param props
* @returns * @returns
*/ */

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import { createContext } from 'react'; import { createContext } from 'react';
import { ISchemaComponentContext } from './types'; import { ISchemaComponentContext } from './types';
export const SchemaComponentContext = createContext<ISchemaComponentContext>({}); 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 { createForm, Schema } from '@tachybase/schema';
import { useRequest } from '../../api-client'; import { useRequest } from '../../api-client';
@ -16,11 +16,12 @@ export interface RemoteSchemaComponentProps {
hidden?: any; hidden?: any;
onlyRenderProperties?: boolean; onlyRenderProperties?: boolean;
noForm?: boolean; noForm?: boolean;
children?: React.ReactNode;
} }
const defaultTransform = (s: Schema) => s; const defaultTransform = (s: Schema) => s;
const RequestSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) => { const RequestSchemaComponent = (props: RemoteSchemaComponentProps) => {
const { const {
noForm, noForm,
onlyRenderProperties, onlyRenderProperties,
@ -45,18 +46,18 @@ const RequestSchemaComponent: React.FC<RemoteSchemaComponentProps> = (props) =>
reset && reset(); reset && reset();
}, },
}); });
if (loading || hidden) { if (loading || hidden || !data) {
return; return;
} }
return noForm ? ( return noForm ? (
<SchemaComponent memoized components={components} scope={scope} schema={schemaTransform(data?.data || {})} /> <SchemaComponent components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
) : ( ) : (
<FormProvider form={form}> <FormProvider form={form}>
<SchemaComponent memoized components={components} scope={scope} schema={schemaTransform(data?.data || {})} /> <SchemaComponent components={components} scope={scope} schema={schemaTransform(data?.data || {})} />
</FormProvider> </FormProvider>
); );
}; };
export const RemoteSchemaComponent: React.FC<PropsWithChildren<RemoteSchemaComponentProps>> = (props) => { export const RemoteSchemaComponent = (props: RemoteSchemaComponentProps) => {
return props.uid ? <RequestSchemaComponent {...props} /> : null; 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 { ExpressionScope, SchemaComponentsContext, SchemaOptionsContext } from '@tachybase/schema';
import { ISchemaComponentOptionsProps } from '../types'; import { ISchemaComponentOptionsProps } from '../types';
@ -8,7 +8,7 @@ export const useSchemaOptionsContext = () => {
return options || {}; return options || {};
}; };
export const SchemaComponentOptions: React.FC<PropsWithChildren<ISchemaComponentOptionsProps>> = memo((props) => { export const SchemaComponentOptions = memo((props: ISchemaComponentOptionsProps) => {
const { children } = props; const { children } = props;
const options = useSchemaOptionsContext(); const options = useSchemaOptionsContext();
const components = useMemo(() => { 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 { createForm, FormProvider, Schema, uid } from '@tachybase/schema';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -42,10 +42,11 @@ const Registry = {
Schema.registerCompiler(Registry.compile); Schema.registerCompiler(Registry.compile);
export const SchemaComponentProvider: React.FC<PropsWithChildren<ISchemaComponentProvider>> = (props) => { export const SchemaComponentProvider = (props: ISchemaComponentProvider) => {
const { designable, onDesignableChange, components, children } = props; const { designable, onDesignableChange, components, children } = props;
const [uidValue, setUid] = useState(uid()); const [uidValue, setUid] = useState(uid());
const [formId, setFormId] = useState(uid()); const [formId, setFormId] = useState(uid());
// eslint-disable-next-line react-hooks/exhaustive-deps
const form = useMemo(() => props.form || createForm(), [formId]); const form = useMemo(() => props.form || createForm(), [formId]);
const { t } = useTranslation(); const { t } = useTranslation();
const scope = useMemo(() => { const scope = useMemo(() => {
@ -69,12 +70,12 @@ export const SchemaComponentProvider: React.FC<PropsWithChildren<ISchemaComponen
onDesignableChange?.(value); onDesignableChange?.(value);
}, },
}), }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[uidValue, scope, components, designable, active], [uidValue, scope, components, designable, active],
); );
return ( return (
// TODO: 临时修复,处理模板类型的关联数据第一次进入无法加载 <SchemaComponentContext.Provider value={schemaComponentContextValue}>
<SchemaComponentContext.Provider value={{ ...schemaComponentContextValue }}>
<FormProvider form={form}> <FormProvider form={form}>
<SchemaComponentOptions inherit scope={scope} components={components}> <SchemaComponentOptions inherit scope={scope} components={components}>
{children} {children}

View File

@ -1,4 +1,5 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { SchemaComponentContext } from '../context'; import { SchemaComponentContext } from '../context';
export function useSchemaComponentContext() { 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 React from 'react';
import { Form, IRecursionFieldProps, ISchemaFieldProps, SchemaReactComponents } from '@tachybase/schema';
export interface ISchemaComponentContext { export interface ISchemaComponentContext {
scope?: any; scope?: any;
@ -9,7 +8,7 @@ export interface ISchemaComponentContext {
reset?: () => void; reset?: () => void;
designable?: boolean; designable?: boolean;
setDesignable?: (value: boolean) => void; setDesignable?: (value: boolean) => void;
SchemaField?: React.FC<ISchemaFieldProps>; SchemaField?: (props: ISchemaFieldProps) => React.ReactNode;
} }
export interface ISchemaComponentProvider { export interface ISchemaComponentProvider {
@ -18,6 +17,7 @@ export interface ISchemaComponentProvider {
form?: Form; form?: Form;
scope?: any; scope?: any;
components?: SchemaReactComponents; components?: SchemaReactComponents;
children?: React.ReactNode;
} }
export interface IRecursionComponentProps extends IRecursionFieldProps { export interface IRecursionComponentProps extends IRecursionFieldProps {
@ -29,4 +29,5 @@ export interface ISchemaComponentOptionsProps {
scope?: any; scope?: any;
components?: SchemaReactComponents; components?: SchemaReactComponents;
inherit?: boolean; 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 currentForm = scope.find((item) => item.value === '$nForm');
const fields = getCollectionFields(name); const fields = getCollectionFields(name);
// fix https://tachybase.height.app/T-1355 // fix
// 工作流人工节点的 `自定义表单` 区块,与其它表单区块不同,根据它的数据表名称,获取到的字段列表为空,所以需要在这里特殊处理一下 // 工作流人工节点的 `自定义表单` 区块,与其它表单区块不同,根据它的数据表名称,获取到的字段列表为空,所以需要在这里特殊处理一下
if (!fields?.length && currentForm) { if (!fields?.length && currentForm) {
currentForm.children = formatVariableScop(currentFormFields); currentForm.children = formatVariableScop(currentFormFields);

View File

@ -890,7 +890,7 @@ export const useCollectionDataSourceItems = ({
getTemplatesByCollection, getTemplatesByCollection,
t, t,
}).sort((item) => { }).sort((item) => {
// fix https://tachybase.height.app/T-3551 // fix
const inherits = _.toArray(collection?.inherits || []); const inherits = _.toArray(collection?.inherits || []);
if (item.name === collection?.name || inherits.some((inheritName) => inheritName === item.name)) return -1; 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]); }, [allCollections, collection?.inherits, collection?.name, componentName, getTemplatesByCollection, t]);
// https://tachybase.height.app/T-3821 //
// showAssociationFields 的值是固定不变的,所以在 if 语句里使用 hooks 是安全的 // showAssociationFields 的值是固定不变的,所以在 if 语句里使用 hooks 是安全的
if (showAssociationFields) { if (showAssociationFields) {
// eslint-disable-next-line react-hooks/rules-of-hooks // 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 currentForm = scope.find((item) => item.value === '$nForm');
const fields = getCollectionFields(name); const fields = getCollectionFields(name);
// fix https://tachybase.height.app/T-1355
// 工作流人工节点的 `自定义表单` 区块,与其它表单区块不同,根据它的数据表名称,获取到的字段列表为空,所以需要在这里特殊处理一下 // 工作流人工节点的 `自定义表单` 区块,与其它表单区块不同,根据它的数据表名称,获取到的字段列表为空,所以需要在这里特殊处理一下
if (!fields?.length && currentForm) { if (!fields?.length && currentForm) {
currentForm.children = formatVariableScop(getFields()); currentForm.children = formatVariableScop(getFields());

View File

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

View File

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

View File

@ -1,8 +1,9 @@
import { observer, useField, useFieldSchema } from '@tachybase/schema'; import React, { createContext, useCallback, useContext, useMemo } from 'react';
import React, { createContext, useContext, useMemo } from 'react'; import { useField, useFieldSchema } from '@tachybase/schema';
import { RemoteSchemaComponent, useDesignable } from '..';
import { useSchemaTemplateManager } from './SchemaTemplateManagerProvider'; import { RemoteSchemaComponent, useDesignable, useSchemaComponentContext } from '..';
import { useTemplateBlockContext } from '../block-provider/TemplateBlockProvider'; import { useTemplateBlockContext } from '../block-provider/TemplateBlockProvider';
import { useSchemaTemplateManager } from './useSchemaTemplateManager';
const BlockTemplateContext = createContext<any>({}); const BlockTemplateContext = createContext<any>({});
BlockTemplateContext.displayName = 'BlockTemplateContext'; BlockTemplateContext.displayName = 'BlockTemplateContext';
@ -11,26 +12,31 @@ export const useBlockTemplateContext = () => {
return useContext(BlockTemplateContext); return useContext(BlockTemplateContext);
}; };
export const BlockTemplate = observer( export const BlockTemplate = (props: any) => {
(props: any) => { const { templateId } = props;
const { templateId } = props; const { getTemplateById } = useSchemaTemplateManager();
const { getTemplateById } = useSchemaTemplateManager(); const field = useField();
const field = useField(); const fieldSchema = useFieldSchema();
const fieldSchema = useFieldSchema(); const { dn } = useDesignable();
const { dn } = useDesignable(); // eslint-disable-next-line react-hooks/exhaustive-deps
const template = useMemo(() => getTemplateById(templateId), [templateId]); const template = useMemo(() => getTemplateById(templateId), [templateId]);
const { onTemplateSuccess } = useTemplateBlockContext(); const { onTemplateSuccess } = useTemplateBlockContext();
const { refresh } = useSchemaComponentContext();
const onSuccess = (data) => { const onSuccess = useCallback(
(data) => {
fieldSchema['x-linkage-rules'] = data?.data?.['x-linkage-rules'] || []; fieldSchema['x-linkage-rules'] = data?.data?.['x-linkage-rules'] || [];
fieldSchema.setProperties(data?.data?.properties); fieldSchema.setProperties(data?.data?.properties);
onTemplateSuccess?.(); onTemplateSuccess?.();
}; refresh();
return template ? ( },
<BlockTemplateContext.Provider value={{ dn, field, fieldSchema, template }}> // eslint-disable-next-line react-hooks/exhaustive-deps
<RemoteSchemaComponent noForm uid={template?.uid} onSuccess={onSuccess} /> [template],
</BlockTemplateContext.Provider> );
) : null;
}, return template ? (
{ displayName: 'BlockTemplate' }, <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 { Input, Spin } from 'antd';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { useAPIClient, useRequest, useSchemaTemplateManager } from '..'; import { useRequest, useSchemaTemplateManager } from '..';
import { RemoteSchemaComponent, SchemaComponentContext } from '../schema-component'; import { RemoteSchemaComponent, SchemaComponentContext } from '../schema-component';
const EditableTitle = (props) => { const EditableTitle = (props) => {
const [title, setTitle] = useState(props.title); const [title, setTitle] = useState(props.title);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { refresh } = useSchemaTemplateManager(); const { refresh } = useSchemaTemplateManager();
const api = useAPIClient();
const { run } = useRequest( const { run } = useRequest(
{ {
resource: 'uiSchemaTemplates', resource: 'uiSchemaTemplates',

View File

@ -1,14 +1,10 @@
import { ISchema, useFieldSchema } from '@tachybase/schema'; import React, { createContext, useMemo } from 'react';
import { uid } from '@tachybase/schema'; import { ISchema, uid, useFieldSchema } from '@tachybase/schema';
import { cloneDeep } from 'lodash';
import React, { ReactNode, createContext, useContext, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { useAPIClient, useRequest } from '../api-client'; import { useAPIClient, useRequest } from '../api-client';
import { Plugin } from '../application/Plugin'; import { Plugin } from '../application/Plugin';
import { useAppSpin } from '../application/hooks/useAppSpin';
import { useCollectionManager_deprecated } from '../collection-manager';
import { BlockTemplate } from './BlockTemplate'; import { BlockTemplate } from './BlockTemplate';
import { DEFAULT_DATA_SOURCE_KEY } from '../data-source'; import { useSchemaTemplateManager } from './useSchemaTemplateManager';
export const SchemaTemplateManagerContext = createContext<any>({}); export const SchemaTemplateManagerContext = createContext<any>({});
SchemaTemplateManagerContext.displayName = 'SchemaTemplateManagerContext'; 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(); s['name'] = s['x-uid'] = uid();
Object.keys(s.properties || {}).forEach((key) => { Object.keys(s.properties || {}).forEach((key) => {
regenerateUid(s.properties[key]); regenerateUid(s.properties[key]);
@ -30,106 +26,21 @@ const regenerateUid = (s: ISchema) => {
}; };
export const useSchemaTemplate = () => { export const useSchemaTemplate = () => {
const { getTemplateBySchema, templates } = useSchemaTemplateManager(); const { getTemplateBySchema } = useSchemaTemplateManager();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const schemaId = fieldSchema['x-uid']; const schemaId = fieldSchema['x-uid'];
const templateKey = fieldSchema['x-template-key']; const templateKey = fieldSchema['x-template-key'];
// console.log('templateKey', { schemaId, templateKey }) // eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => getTemplateBySchema(fieldSchema), [schemaId, templateKey]); const result = useMemo(() => getTemplateBySchema(fieldSchema), [schemaId, templateKey]);
return result;
}; };
export const useSchemaTemplateManager = () => { const InternalRemoteSchemaTemplateManagerProvider = (props) => {
const { getInheritCollections } = useCollectionManager_deprecated();
const { refresh, templates = [] } = useContext(SchemaTemplateManagerContext);
const api = useAPIClient(); 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 = { const options = {
resource: 'uiSchemaTemplates', resource: 'uiSchemaTemplates',
action: 'list', action: 'list',
params: { params: {
// appends: ['collection'],
paginate: false, paginate: false,
}, },
}; };
@ -137,9 +48,11 @@ const Internal = (props) => {
const service = useRequest<{ const service = useRequest<{
data: any[]; data: any[];
}>(options); }>(options);
if (service.loading) {
return render(); if (service.loading && !service.data) {
return;
} }
return ( return (
<SchemaTemplateManagerProvider <SchemaTemplateManagerProvider
refresh={async () => { refresh={async () => {
@ -153,19 +66,14 @@ const Internal = (props) => {
); );
}; };
export const RemoteSchemaTemplateManagerProvider: React.FC<{ children?: ReactNode }> = (props) => { export const RemoteSchemaTemplateManagerProvider = (props: { children?: React.ReactNode }) => {
const location = useLocation(); return <InternalRemoteSchemaTemplateManagerProvider>{props.children}</InternalRemoteSchemaTemplateManagerProvider>;
if (location.pathname.startsWith('/admin')) {
return <Internal {...props} />;
}
return <>{props.children}</>;
}; };
export class RemoteSchemaTemplateManagerPlugin extends Plugin { export class RemoteSchemaTemplateManagerPlugin extends Plugin {
async load() { async load() {
this.addRoutes();
this.addComponents(); this.addComponents();
this.app.use(RemoteSchemaTemplateManagerProvider); this.app.addProviders([RemoteSchemaTemplateManagerProvider]);
} }
addComponents() { addComponents() {
@ -173,11 +81,4 @@ export class RemoteSchemaTemplateManagerPlugin extends Plugin {
BlockTemplate, 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 './BlockTemplateDetails';
export * from './BlockTemplatePage'; export * from './BlockTemplatePage';
export * from './SchemaTemplateManagerProvider'; export * from './SchemaTemplateManagerProvider';
export * from './useSchemaTemplateManager';

View File

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

View File

@ -1,8 +1,8 @@
import { ISchema } from '@tachybase/schema'; import { ISchema, uid } from '@tachybase/schema';
import { uid } from '@tachybase/schema';
import { useBulkDestroyActionProps, useDestroyActionProps, useUpdateActionProps } from '../../block-provider/hooks'; import { useBulkDestroyActionProps, useDestroyActionProps, useUpdateActionProps } from '../../block-provider/hooks';
import { useSchemaTemplateManager } from '../SchemaTemplateManagerProvider';
import { uiSchemaTemplatesCollection } from '../collections/uiSchemaTemplates'; import { uiSchemaTemplatesCollection } from '../collections/uiSchemaTemplates';
import { useSchemaTemplateManager } from '../useSchemaTemplateManager';
import { CollectionTitle } from './CollectionTitle'; import { CollectionTitle } from './CollectionTitle';
const useUpdateSchemaTemplateActionProps = () => { const useUpdateSchemaTemplateActionProps = () => {
@ -50,7 +50,6 @@ export const uiSchemaTemplatesSchema: ISchema = {
action: 'list', action: 'list',
params: { params: {
pageSize: 20, pageSize: 20,
// appends: ['collection'],
sort: ['-createdAt'], sort: ['-createdAt'],
}, },
rowKey: 'key', rowKey: 'key',
@ -106,14 +105,6 @@ export const uiSchemaTemplatesSchema: ISchema = {
split: '|', split: '|',
}, },
properties: { properties: {
// view: {
// title: '{{ t("View") }}',
// 'x-action': 'view',
// 'x-component': 'RecordLink',
// 'x-component-props': {
// to: '/admin/plugins/block-templates/${record.key}',
// },
// },
edit: { edit: {
type: 'void', type: 'void',
title: '{{ t("Edit") }}', 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); clearRequested(url);
} }
// fix https://tachybase.height.app/T-3144使用 `raw` 方法是为了避免触发 autorun,以修复 T-3144 的错误 // 使用 `raw` 方法是为了避免触发 autorun
if (!raw(current)[key]) { if (!raw(current)[key]) {
// 把接口返回的数据保存起来,避免重复请求 // 把接口返回的数据保存起来,避免重复请求
raw(current)[key] = data.data.data; raw(current)[key] = data.data.data;
@ -296,7 +296,7 @@ function shouldToRequest(value) {
// value 有可能是一个响应式对象,使用 untracked 可以避免意外触发 autorun // value 有可能是一个响应式对象,使用 untracked 可以避免意外触发 autorun
untracked(() => { untracked(() => {
// fix https://tachybase.height.app/T-2502 // fix
// 兼容 `对多` 和 `对一` 子表单子表格字段的情况 // 兼容 `对多` 和 `对一` 子表单子表格字段的情况
if (JSON.stringify(value) === '[{}]' || JSON.stringify(value) === '{}') { if (JSON.stringify(value) === '[{}]' || JSON.stringify(value) === '{}') {
result = true; result = true;

View File

@ -195,7 +195,7 @@ describe('array field operator', function () {
expect(filter3[0].get('name')).toEqual(t2.get('name')); expect(filter3[0].get('name')).toEqual(t2.get('name'));
}); });
// fix https://tachybase.height.app/T-2803 // fix
test('$anyOf with string', async () => { test('$anyOf with string', async () => {
const filter3 = await Test.repository.find({ const filter3 = await Test.repository.find({
filter: { filter: {
@ -228,7 +228,7 @@ describe('array field operator', function () {
expect(filter[0].get('name')).toEqual(t1.get('name')); expect(filter[0].get('name')).toEqual(t1.get('name'));
}); });
// fix https://tachybase.height.app/T-2803 // fix
test('$noneOf with string', async () => { test('$noneOf with string', async () => {
const filter = await Test.repository.find({ const filter = await Test.repository.find({
filter: { 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 }) => { test('sequence rules: z-index of configure drawer', async ({ page, mockCollections }) => {
const collectionManagerPage = new CollectionManagerPage(page); const collectionManagerPage = new CollectionManagerPage(page);
await collectionManagerPage.goto(); await collectionManagerPage.goto();

View File

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

View File

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