Reviewed-on: daoyoucloud/tachybase#1072
This commit is contained in:
parent
9e1bfbf8c4
commit
dc175f789f
@ -1,4 +1,5 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { APIClient } from './APIClient';
|
||||
|
||||
export const APIClientContext = createContext<APIClient>(new APIClient());
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { APIClientContext } from '../context';
|
||||
|
||||
export function useAPIClient() {
|
||||
|
@ -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>;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -15,8 +15,6 @@ import {
|
||||
* 是需要从 recordData 还是 parentRecordData 中获取;解决方法是通过更改 schema,在不同类型的关系区块中
|
||||
* (`通过点击关系字段按钮打开的弹窗中创建的非关系字段区块`和`关系字段区块`)使用不同的 hook。
|
||||
*
|
||||
* 更新:上面所说的“需要更改 schema 才能解决的问题”已在这个任务中更改:https://tachybase.height.app/T-3848/description
|
||||
*
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -21,5 +21,6 @@ export const useNiceDropdownMaxHeight = (deps: any[] = []) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
return useMemo(() => heightRef.current - 40, deps);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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")}}';
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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');
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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');
|
||||
|
@ -5696,7 +5696,7 @@ export const T3469: PageConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
// https://tachybase.height.app/T-3529
|
||||
//
|
||||
export const T3529: PageConfig = {
|
||||
collections: generalWithMultiLevelM2mFields,
|
||||
pageSchema: {
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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}>
|
||||
|
@ -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();
|
||||
|
@ -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}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import { GeneralSchemaDesigner } from '../../../schema-settings';
|
||||
|
||||
export const FilterFormDesigner = () => {
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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) {
|
||||
|
@ -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', () => {
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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({
|
||||
|
@ -1,13 +0,0 @@
|
||||
---
|
||||
group:
|
||||
title: Schema Components
|
||||
order: 3
|
||||
---
|
||||
|
||||
# FormItem
|
||||
|
||||
表单字段装饰器。除此之外,也继承了 BlockItem 的功能。
|
||||
|
||||
## Example
|
||||
|
||||
<code src="./demos/demo1.tsx"></code>
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 />
|
||||
|
@ -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 });
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { ISchemaComponentContext } from './types';
|
||||
|
||||
export const SchemaComponentContext = createContext<ISchemaComponentContext>({});
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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(() => {
|
||||
|
@ -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}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { SchemaComponentContext } from '../context';
|
||||
|
||||
export function useSchemaComponentContext() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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') || [];
|
||||
|
||||
|
@ -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') || [];
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './BlockTemplateDetails';
|
||||
export * from './BlockTemplatePage';
|
||||
export * from './SchemaTemplateManagerProvider';
|
||||
export * from './useSchemaTemplateManager';
|
||||
|
@ -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();
|
||||
|
@ -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") }}',
|
||||
|
@ -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 || [];
|
||||
},
|
||||
};
|
||||
};
|
@ -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;
|
||||
|
@ -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: {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -210,7 +210,7 @@ export function RuleConfigForm() {
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => {
|
||||
// fix https://tachybase.height.app/T-2868
|
||||
// fix
|
||||
FormDrawer(
|
||||
{
|
||||
title: compile(ruleType.title),
|
||||
|
Loading…
Reference in New Issue
Block a user