feat(client): support linkage style in table and form (#4467)

* feat: support linkage style in table and form

* fix: removed linkage rule in table column, fixed formitem style not refreshing, use colorpicker

* chore: remove unused functions and improve effect execution performance

* feat: add some e2e test for linkage style in table, extract linkage
  style logic to hooks

* feat: add some e2e test

* fix: fix some e2e test error

* test: fix e2e tests

* fix: replace deprecated api

* chore: give color picker a empty string as default value

* chore: improve some code

* refactor: remove useless code

* fix:  fix some incompatibility problem of old schema

* fix: fix some e2e test errors

---------

Co-authored-by: Zeke Zhang <958414905@qq.com>
This commit is contained in:
Sheldon Guo 2024-07-01 09:13:37 +08:00 committed by GitHub
parent 05cf9986b0
commit 95749f3d88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 1632 additions and 52 deletions

View File

@ -21,6 +21,7 @@ import { useCreateFormBlockDecoratorProps } from '../modules/blocks/data-blocks/
import { useCreateFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useCreateFormBlockProps';
import { useEditFormBlockDecoratorProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps';
import { useEditFormBlockProps } from '../modules/blocks/data-blocks/form/hooks/useEditFormBlockProps';
import { useDataFormItemProps } from '../modules/blocks/data-blocks/form/hooks/useDataFormItemProps';
import { useGridCardBlockDecoratorProps } from '../modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps';
import { useListBlockDecoratorProps } from '../modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps';
import { useTableSelectorDecoratorProps } from '../modules/blocks/data-blocks/table-selector/hooks/useTableSelectorDecoratorProps';
@ -85,6 +86,7 @@ export const BlockSchemaComponentProvider: React.FC = (props) => {
useFilterFormBlockDecoratorProps,
useGridCardBlockDecoratorProps,
useFormItemProps,
useDataFormItemProps,
}}
>
{props.children}
@ -145,6 +147,7 @@ export class BlockSchemaComponentPlugin extends Plugin {
useFilterFormBlockDecoratorProps,
useGridCardBlockDecoratorProps,
useFormItemProps,
useDataFormItemProps,
});
}
}

View File

@ -426,6 +426,7 @@
"Option value": "Option value",
"Option label": "Option label",
"Color": "Color",
"Background Color": "Background Color",
"Add option": "Add option",
"Related collection": "Related collection",
"Allow linking to multiple records": "Allow linking to multiple records",
@ -552,6 +553,7 @@
"Add condition group": "Add condition group",
"exists": "exists",
"not exists": "not exists",
"Style": "Style",
"=": "=",
"≠": "≠",
">": ">",

View File

@ -447,6 +447,7 @@
"Option value": "选项值",
"Option label": "选项标签",
"Color": "颜色",
"Background Color": "背景颜色",
"Add option": "添加选项",
"Related collection": "关系表",
"Allow linking to multiple records": "允许关联多条记录",

View File

@ -0,0 +1,63 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { expect, commonFormViewPage, test } from '@nocobase/test/e2e';
test.describe('field schema settings', () => {
test('linkage style color', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(commonFormViewPage).waitForInit();
await mockRecord('general', { singleLineText: 'asdfcedg' });
await nocoPage.goto();
await page.getByText('asdfcedg', { exact: true }).hover();
await page.getByLabel('block-item-CollectionField-general-details-general.singleLineText-singleLineText').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-fieldSettings:FormItem-general-general.singleLineText', {
exact: true,
})
.click();
await page.getByRole('menuitem', { name: 'Style' }).click();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-properties').click();
await page.getByText('Color', { exact: true }).click();
await page.getByLabel('color-picker-normal').click();
await page.locator('input[type="text"]').fill('A34FCC');
await page.getByRole('button', { name: 'OK' }).click();
const cell = await page
.getByLabel('block-item-CollectionField-general-details-general.singleLineText-singleLineText')
.locator('div.ant-formily-item-control-content-component');
await expect(cell).toHaveCSS('color', 'rgb(163, 79, 204)');
});
test('linkage style background color', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(commonFormViewPage).waitForInit();
await mockRecord('general', { singleLineText: 'asdfcedg' });
await nocoPage.goto();
await page.getByText('asdfcedg', { exact: true }).hover();
await page.getByLabel('block-item-CollectionField-general-details-general.singleLineText-singleLineText').hover();
await page
.getByLabel('designer-schema-settings-CollectionField-fieldSettings:FormItem-general-general.singleLineText', {
exact: true,
})
.click();
await page.getByRole('menuitem', { name: 'Style' }).click();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-properties').click();
await page.getByText('Background Color', { exact: true }).click();
await page.getByLabel('color-picker-normal').click();
await page.locator('input[type="text"]').fill('A34FCC');
await page.getByRole('button', { name: 'OK' }).click();
const cell = await page
.getByLabel('block-item-CollectionField-general-details-general.singleLineText-singleLineText')
.locator('div.ant-formily-item-control-content-component');
await expect(cell).toHaveCSS('background-color', 'rgb(163, 79, 204)');
});
});

View File

@ -242,6 +242,10 @@ describe('FieldSettingsFormItem', () => {
title: 'Pattern',
type: 'select',
},
{
title: 'Style',
type: 'modal',
},
{
title: 'Set validation rules',
type: 'modal',
@ -284,6 +288,10 @@ describe('FieldSettingsFormItem', () => {
title: 'Pattern',
type: 'select',
},
{
title: 'Style',
type: 'modal',
},
{
title: 'Field component',
type: 'select',

View File

@ -6,7 +6,7 @@
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React from 'react';
import { ArrayCollapse, FormLayout } from '@formily/antd-v5';
import { Field } from '@formily/core';
import { ISchema, useField, useFieldSchema } from '@formily/react';
@ -16,6 +16,7 @@ import { useApp, useSchemaToolbar } from '../../../../application';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useFormBlockContext } from '../../../../block-provider/FormBlockProvider';
import { useCollectionManager_deprecated, useCollection_deprecated } from '../../../../collection-manager';
import { useCollection } from '../../../../data-source';
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
import { useDesignable, useValidateSchema } from '../../../../schema-component';
import { useIsFormReadPretty } from '../../../../schema-component/antd/form-item/FormItem.Settings';
@ -24,7 +25,8 @@ import { isPatternDisabled } from '../../../../schema-settings';
import { ActionType } from '../../../../schema-settings/LinkageRules/type';
import { SchemaSettingsDefaultValue } from '../../../../schema-settings/SchemaSettingsDefaultValue';
import { useIsAllowToSetDefaultValue } from '../../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
import { useIsFieldReadPretty } from '../../../../schema-component/antd/form-item/FormItem.Settings';
export const fieldSettingsFormItem = new SchemaSettings({
name: 'fieldSettings:FormItem',
items: [
@ -442,6 +444,25 @@ export const fieldSettingsFormItem = new SchemaSettings({
return form && !isFormReadPretty && validateSchema;
},
},
{
name: 'style',
Component: (props) => {
const localProps = { ...props, category: 'style' };
return <SchemaSettingsLinkageRules {...localProps} />;
},
useVisible() {
const isFieldReadPretty = useIsFieldReadPretty();
return isFieldReadPretty;
},
useComponentProps() {
const { name } = useCollection();
const { linkageRulesProps } = useSchemaToolbar();
return {
...linkageRulesProps,
collectionName: name,
};
},
},
];
},
},

View File

@ -0,0 +1,16 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useForm } from '@formily/react';
import { useSatisfiedActionValues } from '../../../../../schema-settings/LinkageRules/useActionValues';
export function useDataFormItemProps() {
const form = useForm();
const { valueMap: style } = useSatisfiedActionValues({ category: 'style', formValues: form?.values });
return { wrapperStyle: style };
}

View File

@ -0,0 +1,60 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { expect, oneTableBlockWithIntegerAndIDColumn, test } from '@nocobase/test/e2e';
test.describe('view', () => {
test('linkage style color', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithIntegerAndIDColumn).waitForInit();
await mockRecord('general', { integer: '423' });
await nocoPage.goto();
await page.getByText('integer', { exact: true }).hover();
await page
.getByRole('button', {
name: 'designer-schema-settings-TableV2.Column-fieldSettings:TableColumn-general',
})
.click();
await page.getByRole('menuitem', { name: 'Style' }).click();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-properties').click();
await page.getByText('Color', { exact: true }).click();
await page.getByLabel('color-picker-normal').click();
await page.locator('input[type="text"]').fill('A34FCC');
await page.getByRole('button', { name: 'OK' }).click();
const cell = page.getByRole('button', { name: '423' });
const color = await cell.evaluate((el) => getComputedStyle(el).color);
expect(color).toContain('163, 79, 204');
});
test('linkage style background color', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(oneTableBlockWithIntegerAndIDColumn).waitForInit();
await mockRecord('general', { integer: '423' });
await nocoPage.goto();
await page.getByText('integer', { exact: true }).hover();
await page
.getByRole('button', {
name: 'designer-schema-settings-TableV2.Column-fieldSettings:TableColumn-general',
})
.click();
await page.getByRole('menuitem', { name: 'Style' }).click();
await page.getByRole('button', { name: 'plus Add linkage rule' }).click();
await page.getByText('Add property').click();
await page.getByTestId('select-linkage-properties').click();
await page.getByText('Background Color', { exact: true }).click();
await page.getByLabel('color-picker-normal').click();
await page.locator('input[type="text"]').fill('A34FCC');
await page.getByRole('button', { name: 'OK' }).click();
const cell = page.getByRole('button', { name: '423' });
const bgColor = await cell.evaluate((el) => getComputedStyle(el.parentElement).backgroundColor);
expect(bgColor).toContain('163, 79, 204');
});
});

View File

@ -7,10 +7,11 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React from 'react';
import { ISchema } from '@formily/json-schema';
import { useField, useFieldSchema } from '@formily/react';
import { useTranslation } from 'react-i18next';
import { useApp } from '../../../../application';
import { useApp, useSchemaToolbar } from '../../../../application';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
@ -20,6 +21,7 @@ import { useAssociationFieldContext } from '../../../../schema-component/antd/as
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { SchemaSettingsDefaultValue } from '../../../../schema-settings/SchemaSettingsDefaultValue';
import { isPatternDisabled } from '../../../../schema-settings/isPatternDisabled';
import { SchemaSettingsLinkageRules } from '../../../../schema-settings';
export const tableColumnSettings = new SchemaSettings({
name: 'fieldSettings:TableColumn',
@ -44,7 +46,6 @@ export const tableColumnSettings = new SchemaSettings({
const { t } = useTranslation();
const columnSchema = useFieldSchema();
const { dn } = useDesignable();
return {
title: t('Custom column title'),
schema: {
@ -79,6 +80,30 @@ export const tableColumnSettings = new SchemaSettings({
};
},
},
{
name: 'style',
Component: (props) => {
const localProps = { ...props, category: 'style' };
return <SchemaSettingsLinkageRules {...localProps} />;
},
useVisible() {
const { fieldSchema } = useColumnSchema();
const field: any = useField();
const path = field.path?.splice(field.path?.length - 1, 1);
if (fieldSchema) {
const isReadPretty = field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).get('readPretty');
return isReadPretty;
} else return false;
},
useComponentProps() {
const { name } = useCollection();
const { linkageRulesProps } = useSchemaToolbar();
return {
...linkageRulesProps,
collectionName: name,
};
},
},
{
name: 'columnWidth',
type: 'modal',

View File

@ -10,7 +10,7 @@
import { css, cx } from '@emotion/css';
import { IFormItemProps, FormItem as Item } from '@formily/antd-v5';
import { Field } from '@formily/core';
import { observer, useField, useFieldSchema } from '@formily/react';
import { observer, useField, useFieldSchema, useForm } from '@formily/react';
import React, { useEffect, useMemo } from 'react';
import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider';
import { useApp } from '../../../application';
@ -19,14 +19,14 @@ import { Collection_deprecated } from '../../../collection-manager';
import { CollectionFieldProvider } from '../../../data-source/collection-field/CollectionFieldProvider';
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
import { GeneralSchemaDesigner } from '../../../schema-settings';
import { useVariables } from '../../../variables';
import useContextVariable from '../../../variables/hooks/useContextVariable';
import { BlockItem } from '../block-item';
import { HTMLEncode } from '../input/shared';
import { FilterFormDesigner } from './FormItem.FilterFormDesigner';
import { useEnsureOperatorsValid } from './SchemaSettingOptions';
import useLazyLoadDisplayAssociationFieldsOfForm from './hooks/useLazyLoadDisplayAssociationFieldsOfForm';
import useParseDefaultValue from './hooks/useParseDefaultValue';
import { useVariables, useContextVariable } from '../../../variables';
import { useDataFormItemProps } from '../../../modules/blocks/data-blocks/form/hooks/useDataFormItemProps';
Item.displayName = 'FormilyFormItem';
@ -47,14 +47,14 @@ export const FormItem: any = withDynamicSchemaProps(
useEnsureOperatorsValid();
const field = useField<Field>();
const schema = useFieldSchema();
const contextVariable = useContextVariable();
const variables = useVariables();
const { addActiveFieldName } = useFormActiveFields() || {};
const form = useForm();
const { wrapperStyle } = useDataFormItemProps();
const variables = useVariables();
const contextVariable = useContextVariable();
useEffect(() => {
variables?.registerVariable(contextVariable);
}, [contextVariable]);
}, [contextVariable, variables]);
// 需要放在注冊完变量之后
useParseDefaultValue();
useLazyLoadDisplayAssociationFieldsOfForm();
@ -87,7 +87,7 @@ export const FormItem: any = withDynamicSchemaProps(
<CollectionFieldProvider allowNull={true}>
<BlockItem className={'nb-form-item'}>
<ACLCollectionFieldProvider>
<Item className={className} {...props} extra={extra} />
<Item className={className} {...props} extra={extra} wrapperStyle={wrapperStyle} />
</ACLCollectionFieldProvider>
</BlockItem>
</CollectionFieldProvider>

View File

@ -30,6 +30,9 @@ import { isPatternDisabled } from '../../../schema-settings/isPatternDisabled';
import { useCompile, useDesignable, useFieldModeOptions } from '../../hooks';
import { useAssociationFieldContext } from '../association-field/hooks';
import { removeNullCondition } from '../filter';
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
import { useCollection } from '../../../data-source';
import { useSchemaToolbar } from '../../../application';
export const useLabelFields = (collectionName?: any) => {
// 需要在组件顶层调用
@ -98,6 +101,12 @@ export const TableColumnDesigner = (props) => {
readOnlyMode = 'read-pretty';
}
const isSelectFieldMode = isAssociationField && fieldMode === 'Select';
const StyleSetting = () => {
const { name } = useCollection();
const { linkageRulesProps } = useSchemaToolbar();
return <SchemaSettingsLinkageRules category={'style'} {...{ ...linkageRulesProps, collectionName: name }} />;
};
return (
<GeneralSchemaDesigner disableInitializer>
<SchemaSettingsModalItem
@ -186,6 +195,7 @@ export const TableColumnDesigner = (props) => {
dn.refresh();
}}
/>
<StyleSetting />
{interfaceCfg && interfaceCfg.sortable === true && !currentMode && (
<SchemaSettingsSwitchItem
title={t('Sortable')}

View File

@ -37,11 +37,11 @@ import {
import { useACLFieldWhitelist } from '../../../acl/ACLProvider';
import { isNewRecord } from '../../../data-source/collection-record/isNewRecord';
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
import { useSatisfiedActionValues } from '../../../schema-settings/LinkageRules/useActionValues';
import { useToken } from '../__builtins__';
import { SubFormProvider } from '../association-field/hooks';
import { ColumnFieldProvider } from './components/ColumnFieldProvider';
import { extractIndex, isCollectionFieldComponent, isColumnComponent } from './utils';
const MemoizedAntdTable = React.memo(AntdTable);
const useArrayField = (props) => {
@ -145,6 +145,9 @@ const useTableColumns = (props: { showDel?: boolean; isSubTable?: boolean }) =>
</SubFormProvider>
);
},
onCell: (record) => {
return { record, schema: s };
},
} as TableColumnProps<any>;
// 这里不能把 columnsSchema 作为依赖,因为其每次都会变化,这里使用 hasChangedColumns 作为依赖
@ -529,16 +532,17 @@ export const Table: any = withDynamicSchemaProps(
const BodyCellComponent = useCallback(
(props) => {
const isIndex = props.className?.includes('selection-column');
const { record, schema } = props;
const { ref, inView } = useInView({
threshold: 0,
triggerOnce: true,
initialInView: isIndex || !!process.env.__E2E__ || dataSource.length <= 10,
skip: isIndex || !!process.env.__E2E__,
});
const { valueMap: style } = useSatisfiedActionValues({ formValues: record, category: 'style', schema });
return (
<td {...props} ref={ref} className={classNames(props.className, cellClass)}>
<td {...props} ref={ref} className={classNames(props.className, cellClass)} style={style}>
{inView || isIndex ? props.children : <Skeleton.Button style={{ height: '100%' }} />}
</td>
);

View File

@ -6,7 +6,7 @@
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import React from 'react';
import { Field } from '@formily/core';
import { ISchema, useField, useFieldSchema } from '@formily/react';
import { useTranslation } from 'react-i18next';
@ -14,6 +14,9 @@ import { useDesignable } from '../schema-component';
import { SchemaSettingOptions } from '../application';
import { useSchemaToolbar } from '../application/schema-toolbar';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager';
import { SchemaSettingsLinkageRules } from '../schema-settings';
import { useIsFieldReadPretty } from '../schema-component';
import { useCollection } from '../data-source';
export const generalSettingsItems: SchemaSettingOptions['items'] = [
{
@ -216,4 +219,23 @@ export const generalSettingsItems: SchemaSettingOptions['items'] = [
return !field.readPretty && fieldSchema['x-component'] !== 'FormField' && required;
},
},
{
name: 'style',
Component: (props) => {
const localProps = { ...props, category: 'style' };
return <SchemaSettingsLinkageRules {...localProps} />;
},
useVisible() {
const isFieldReadPretty = useIsFieldReadPretty();
return isFieldReadPretty;
},
useComponentProps() {
const { name } = useCollection();
const { linkageRulesProps } = useSchemaToolbar();
return {
...linkageRulesProps,
collectionName: name,
};
},
},
];

View File

@ -184,3 +184,67 @@ export const FormButtonLinkageRuleAction = observer(
},
{ displayName: 'FormButtonLinkageRuleAction' },
);
export const FormStyleLinkageRuleAction = observer(
(props: any) => {
const { value, options, collectionName } = props;
const { t } = useTranslation();
const compile = useCompile();
const remove = useContext(RemoveActionContext);
const { operator, setOperator, value: fieldValue, setValue } = useValues(options);
const operators = useMemo(
() =>
compile([
{ label: t('Color'), value: ActionType.Color, schema: {} },
{ label: t('Background Color'), value: ActionType.BackgroundColor, schema: {} },
]),
[compile, t],
);
const schema = {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'ColorPicker',
'x-component-props': {
defaultValue: '',
},
};
const onChange = useCallback(
(value) => {
setOperator(value);
},
[setOperator],
);
const closeStyle = useMemo(() => ({ color: '#bfbfbf' }), []);
return (
<div style={{ marginBottom: 8 }}>
<Space>
<Select
data-testid="select-linkage-properties"
popupMatchSelectWidth={false}
value={operator}
options={operators}
onChange={onChange}
placeholder={t('action')}
/>
{[ActionType.Color, ActionType.BackgroundColor].includes(operator) && (
<ValueDynamicComponent
fieldValue={fieldValue}
schema={schema}
setValue={setValue}
collectionName={collectionName}
inputModes={['constant']}
/>
)}
{!props.disabled && (
<a role="button" aria-label="icon-close">
<CloseCircleOutlined onClick={remove} style={closeStyle} />
</a>
)}
</Space>
</div>
);
},
{ displayName: 'FormStyleLinkageRuleAction' },
);

View File

@ -14,22 +14,28 @@ import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { withDynamicSchemaProps } from '../../hoc/withDynamicSchemaProps';
import { useProps } from '../../schema-component/hooks/useProps';
import { FormButtonLinkageRuleAction, FormFieldLinkageRuleAction } from './LinkageRuleAction';
import {
FormButtonLinkageRuleAction,
FormFieldLinkageRuleAction,
FormStyleLinkageRuleAction,
} from './LinkageRuleAction';
import { RemoveActionContext } from './context';
export const LinkageRuleActions = observer(
(props: any): any => {
const { type, linkageOptions } = props;
const { linkageOptions, category, elementType } = props;
const field = useField<ArrayFieldModel>();
const type = category === 'default' ? elementType : category;
const componentMap: {
[key in LinkageRuleActionGroupProps['type']]: any;
} = {
button: FormButtonLinkageRuleAction,
field: FormFieldLinkageRuleAction,
style: FormStyleLinkageRuleAction,
};
return field?.value?.map((item, index) => {
return (
<RemoveActionContext.Provider key={index} value={() => field.remove(index)}>
<ObjectField
name={index}
component={[
type === 'button' ? FormButtonLinkageRuleAction : FormFieldLinkageRuleAction,
{ ...props, options: linkageOptions },
]}
/>
<ObjectField name={index} component={[componentMap[type], { ...props, options: linkageOptions }]} />
</RemoveActionContext.Provider>
);
});
@ -37,8 +43,8 @@ export const LinkageRuleActions = observer(
{ displayName: 'LinkageRuleActions' },
);
interface LinkageRuleActionGroupProps {
type: 'button' | 'field';
export interface LinkageRuleActionGroupProps {
type: 'button' | 'field' | 'style';
linkageOptions: any;
collectionName: string;
}
@ -50,12 +56,11 @@ export const LinkageRuleActionGroup = withDynamicSchemaProps(
const logic = 'actions';
// 新版 UISchema1.0 之后)中已经废弃了 useProps这里之所以继续保留是为了兼容旧版的 UISchema
const { type, linkageOptions, collectionName } = useProps(props);
const { category, elementType, linkageOptions, collectionName } = useProps(props);
const style = useMemo(() => ({ marginLeft: 10 }), []);
const components = useMemo(
() => [LinkageRuleActions, { type, linkageOptions, collectionName }],
[collectionName, linkageOptions, type],
() => [LinkageRuleActions, { category, elementType, linkageOptions, collectionName }],
[collectionName, linkageOptions, category, elementType],
);
const spaceStyle = useMemo(() => ({ marginTop: 8, marginBottom: 8 }), []);
const onClick = useCallback(() => {

View File

@ -19,15 +19,17 @@ import { DynamicComponent } from './DynamicComponent';
const { Option } = Select;
export type InputModeType = 'constant' | 'express' | 'empty';
interface ValueDynamicComponentProps {
fieldValue: any;
schema: any;
setValue: (value: any) => void;
collectionName: string;
inputModes?: Array<InputModeType>;
}
export const ValueDynamicComponent = (props: ValueDynamicComponentProps) => {
const { fieldValue, schema, setValue, collectionName } = props;
const { fieldValue, schema, setValue, collectionName, inputModes } = props;
const [mode, setMode] = useState(fieldValue?.mode || 'constant');
const { t } = useTranslation();
const { form } = useFormBlockContext();
@ -111,6 +113,22 @@ export const ValueDynamicComponent = (props: ValueDynamicComponentProps) => {
),
};
const isModeContained = (mode: InputModeType) => {
if (!inputModes) return true;
else {
return inputModes.indexOf(mode) > -1;
}
};
type Options = Array<{ value: InputModeType; label: string }>;
const options: Options = (
[
{ value: 'constant', label: t('Constant value') },
{ value: 'express', label: t('Expression') },
{ value: 'empty', label: t('Empty') },
] as const
).filter((option) => isModeContained(option.value));
return (
<Input.Group compact>
<Select
@ -126,9 +144,11 @@ export const ValueDynamicComponent = (props: ValueDynamicComponentProps) => {
});
}}
>
<Option value="constant">{t('Constant value')}</Option>
<Option value="express">{t('Expression')}</Option>
<Option value="empty">{t('Empty')}</Option>
{options.map((option) => (
<Option value={option.value} key={option.value}>
{option.label}
</Option>
))}
</Select>
{modeMap[mode]}
</Input.Group>

View File

@ -0,0 +1,55 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { ActionType } from './type';
import { InputModeType } from './ValueDynamicComponent';
import { conditionAnalyses } from '../../schema-component/common/utils/uitls';
const getActionValue = (operator, value) => {
const getValueByMode = (value) => {
const mode = value?.mode as InputModeType;
if (mode === 'constant') {
return value.value;
} else return null;
};
switch (true) {
case [ActionType.Color, ActionType.BackgroundColor].includes(operator):
return getValueByMode(value);
default:
return null;
break;
}
};
const getSatisfiedActions = async ({ rules, variables, localVariables }) => {
const satisfiedRules = (
await Promise.all(
rules
.filter((k) => !k.disabled)
.map(async (rule) => {
if (await conditionAnalyses({ ruleGroup: rule.condition, variables, localVariables })) {
return rule;
} else return null;
}),
)
).filter(Boolean);
return satisfiedRules.map((rule) => rule.actions).flat();
};
const getSatisfiedValues = async ({ rules, variables, localVariables }) => {
return (await getSatisfiedActions({ rules, variables, localVariables })).map((action) => ({
...action,
value: getActionValue(action.operator, action.value),
}));
};
export const getSatisfiedValueMap = async ({ rules, variables, localVariables }) => {
const values = await getSatisfiedValues({ rules, variables, localVariables });
const valueMap = values.reduce((a, v) => ({ ...a, [v.operator]: v.value }), {});
return valueMap;
};

View File

@ -19,4 +19,16 @@ export enum ActionType {
Disabled = 'disabled',
Value = 'value',
Active = 'enabled',
Color = 'color',
BackgroundColor = 'backgroundColor',
}
export enum LinkageRuleCategory {
default = 'default',
style = 'style',
}
export const LinkageRuleDataKeyMap: Record<`${LinkageRuleCategory}`, string> = {
[LinkageRuleCategory.style]: 'x-linkage-style-rules',
[LinkageRuleCategory.default]: 'x-linkage-rules',
};

View File

@ -0,0 +1,48 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { useState, useEffect } from 'react';
import { useVariables, useLocalVariables } from '../../variables';
import { useFieldSchema } from '@formily/react';
import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type';
import { getSatisfiedValueMap } from './compute-rules';
import { isEmpty } from 'lodash';
export function useSatisfiedActionValues({
formValues,
category = 'default',
rules,
schema,
}: {
category: `${LinkageRuleCategory}`;
formValues: Record<string, any>;
rules?: any;
schema?: any;
}) {
const [valueMap, setValueMap] = useState({});
const fieldSchema = useFieldSchema();
const variables = useVariables();
const localVariables = useLocalVariables({ currentForm: { values: formValues } as any });
const localSchema = schema ?? fieldSchema;
const linkageRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]];
useEffect(() => {
if (linkageRules && formValues) {
getSatisfiedValueMap({ rules: linkageRules, variables, localVariables })
.then((valueMap) => {
if (!isEmpty(valueMap)) {
setValueMap(valueMap);
} else setValueMap({});
})
.catch((err) => {
throw new Error(err.message);
});
}
}, [variables, localVariables, formValues, linkageRules]);
return { valueMap };
}

View File

@ -101,6 +101,7 @@ import { EnableChildCollections } from './EnableChildCollections';
import { ChildDynamicComponent } from './EnableChildCollections/DynamicComponent';
import { FormLinkageRules } from './LinkageRules';
import { useLinkageCollectionFieldOptions } from './LinkageRules/action-hooks';
import { LinkageRuleDataKeyMap, LinkageRuleCategory } from './LinkageRules/type';
export interface SchemaSettingsProps {
title?: any;
@ -983,23 +984,35 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
const localVariables = useLocalVariables();
const record = useRecord();
const { type: formBlockType } = useFormBlockType();
const type = props?.type || fieldSchema?.['x-action'] ? 'button' : 'field';
const category = props?.category ?? LinkageRuleCategory.default;
const elementType = ['Action', 'Action.Link'].includes(fieldSchema['x-component']) ? 'button' : 'field';
const gridSchema = findGridSchema(fieldSchema) || fieldSchema;
const options = useLinkageCollectionFilterOptions(collectionName);
const linkageOptions = useLinkageCollectionFieldOptions(collectionName, readPretty);
const titleMap = {
[LinkageRuleCategory.default]: t('Linkage rules'),
[LinkageRuleCategory.style]: t('Style'),
};
const dataKey = LinkageRuleDataKeyMap[category];
const getRules = useCallback(() => {
return gridSchema?.[dataKey] || fieldSchema?.[dataKey] || [];
}, [gridSchema, fieldSchema, dataKey]);
const title = titleMap[category];
const schema = useMemo<ISchema>(
() => ({
type: 'object',
title: t('Linkage rules'),
title,
properties: {
fieldReaction: {
'x-component': FormLinkageRules,
'x-use-component-props': () => {
return {
options,
defaultValues: gridSchema?.['x-linkage-rules'] || fieldSchema?.['x-linkage-rules'],
type,
defaultValues: getRules(),
linkageOptions,
category,
elementType,
collectionName,
form,
variables,
@ -1011,7 +1024,7 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
},
},
}),
[collectionName, fieldSchema, form, gridSchema, localVariables, record, t, type, variables],
[collectionName, fieldSchema, form, gridSchema, localVariables, record, t, variables, getRules],
);
const components = useMemo(() => ({ ArrayCollapse, FormLayout }), []);
const onSubmit = useCallback(
@ -1025,25 +1038,18 @@ export const SchemaSettingsLinkageRules = function LinkageRules(props) {
const schema = {
['x-uid']: uid,
};
gridSchema['x-linkage-rules'] = rules;
schema['x-linkage-rules'] = rules;
gridSchema[dataKey] = rules;
schema[dataKey] = rules;
dn.emit('patch', {
schema,
});
dn.refresh();
},
[dn, getTemplateById, gridSchema],
[dn, getTemplateById, gridSchema, dataKey],
);
return (
<SchemaSettingsModalItem
title={t('Linkage rules')}
components={components}
width={770}
schema={schema}
onSubmit={onSubmit}
/>
<SchemaSettingsModalItem title={title} components={components} width={770} schema={schema} onSubmit={onSubmit} />
);
};

View File

@ -10,3 +10,4 @@
export * from './e2eUtils';
export * from './templatesOfCollection';
export * from './templatesOfPage';
export * from './templates';

View File

@ -0,0 +1,551 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
/**
*
* email, number, integer, singleLineText, longText
* password, percent, phone
*/
import { generalWithBasic } from '../../templatesOfCollection';
export const commonFormViewPage = {
collections: generalWithBasic,
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
'x-index': 1,
properties: {
nyayqbdi6fw: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
udrskvvihwz: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
sic837d78ru: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
j3tcb77zrjs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action': 'general:view',
'x-decorator': 'DetailsBlockProvider',
'x-use-decorator-props': 'useDetailsWithPaginationDecoratorProps',
'x-decorator-props': {
dataSource: 'main',
collection: 'general',
readPretty: true,
action: 'list',
params: {
pageSize: 1,
},
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:detailsWithPagination',
'x-component': 'CardItem',
'x-app-version': '1.0.0-alpha.17',
properties: {
hgde2qiovq4: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Details',
'x-read-pretty': true,
'x-use-component-props': 'useDetailsWithPaginationProps',
'x-app-version': '1.0.0-alpha.17',
properties: {
xaaixt3ikr2: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'details:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 24,
},
},
'x-app-version': '1.0.0-alpha.17',
properties: {
zc467d4k8pe: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Edit") }}',
'x-action': 'update',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:edit',
'x-component': 'Action',
'x-component-props': {
openMode: 'drawer',
icon: 'EditOutlined',
type: 'primary',
},
'x-decorator': 'ACLActionProvider',
'x-app-version': '1.0.0-alpha.17',
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Edit record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
'x-app-version': '1.0.0-alpha.17',
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
'x-app-version': '1.0.0-alpha.17',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Edit")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'popup:common:addBlock',
'x-app-version': '1.0.0-alpha.17',
'x-uid': 'tbxcoz6blxo',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'x4gthikn85e',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'dlcr60v0lae',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'goi3ac29a92',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xhu94zjv18h',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'dotl8t82ufw',
'x-async': false,
'x-index': 1,
},
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'details:configureFields',
'x-app-version': '1.0.0-alpha.17',
properties: {
qbd9sojqba9: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
'6fi8dxh1515': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
email: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.email',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-uid': '1ht8ymnfc18',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '2y8bzf9fa92',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'mszcq97x37n',
'x-async': false,
'x-index': 1,
},
'5qwtnq93h4m': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
woxnz7b32ms: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
number: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.number',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-uid': 'lg2wluayh2c',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'awljxm8hh55',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'g8raovgeqjc',
'x-async': false,
'x-index': 2,
},
v7ht9slmzwn: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
ijjcbuis4tv: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
integer: {
'x-uid': 'dhdtfo7nrxn',
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.integer',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-linkage-style-rules': [],
'x-async': false,
'x-index': 1,
},
},
'x-uid': '9rd5u4d3lhj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ii6w1eqf21k',
'x-async': false,
'x-index': 3,
},
fekkqosx4q6: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
nmpljskg642: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
singleLineText: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.singleLineText',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-uid': '88cqngc9wzf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '44pljtolwui',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zq7dfy9uvrj',
'x-async': false,
'x-index': 4,
},
p318qpfu85n: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
'1fayljjx90o': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
longText: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.longText',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-uid': 'y6rf9v4iz8o',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '1vhh859rxwx',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '19lk0gejxo3',
'x-async': false,
'x-index': 5,
},
'6t21tyr1aun': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
'3h1hp3t902r': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
password: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.password',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-uid': '2zgn6ew5mwy',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'i3twoaoewde',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'uvsong5fn8l',
'x-async': false,
'x-index': 6,
},
inytkbn0nae: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
h8e5m0gy2ei: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
percent: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.percent',
'x-component-props': {
style: {
width: '100%',
},
},
'x-app-version': '1.0.0-alpha.17',
'x-uid': '2maem18o31o',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jtbdipyy5sc',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'as7c9lme7mm',
'x-async': false,
'x-index': 7,
},
'81q1yw3hkkn': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
properties: {
ykobkj6pe8n: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
properties: {
phone: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props': 'useDataFormItemProps',
'x-collection-field': 'general.phone',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-uid': 'e66n6ewz0hi',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'ai34z8y2eb4',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'eltj0z2txug',
'x-async': false,
'x-index': 8,
},
},
'x-uid': '7crv1u0i3co',
'x-async': false,
'x-index': 2,
},
pagination: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Pagination',
'x-use-component-props': 'useDetailsPaginationProps',
'x-app-version': '1.0.0-alpha.17',
'x-uid': 'jfs3ntlghyo',
'x-async': false,
'x-index': 3,
},
},
'x-uid': 'buvsjjof9pw',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '9ruom7jpm1h',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '3pl1k9mqhy8',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '0eax0yvji6y',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'cs71v511x7o',
'x-async': false,
},
},
'x-uid': 'welnfb52obj',
'x-async': true,
},
};

View File

@ -0,0 +1,10 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export * from './commonView';

View File

@ -0,0 +1,10 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export * from './form-view';

View File

@ -5839,6 +5839,569 @@ export const oneTableBlockWithAddNewAndViewAndEditAndBasicFields: PageConfig = {
},
};
/**
* 1. Table
* 2. ID和一个整数列
* 3. 查看详情: email integer number phone
*/
export const oneTableBlockWithIntegerAndIDColumn: PageConfig = {
collections: generalWithBasic,
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
'x-index': 1,
properties: {
nyayqbdi6fw: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
a7zqokjj6gt: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
'3lbci797d7d': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
wvjxlk9dpcp: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'general:list',
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: 'general',
dataSource: 'main',
action: 'list',
params: {
pageSize: 20,
},
rowKey: 'id',
showIndex: true,
dragSort: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 'var(--nb-spacing)',
},
},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'uafyrboeeeh',
'x-async': false,
},
xpv8gfkv0w4: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'array',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-use-component-props': 'useTableBlockProps',
'x-component-props': {
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
},
'x-app-version': '1.0.0-alpha.17',
'x-index': 2,
properties: {
actions: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Actions") }}',
'x-action-column': 'actions',
'x-decorator': 'TableV2.Column.ActionBar',
'x-component': 'TableV2.Column',
'x-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'table:configureItemActions',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
c3br6p1m4ay: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
ycbfg04aq1u: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View") }}',
'x-action': 'view',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:view',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
'x-index': 1,
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
'x-index': 1,
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
'x-index': 1,
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Details")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-index': 1,
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'popup:common:addBlock',
'x-index': 1,
properties: {
fecjkwzjrlc: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
u5047nn630n: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
g6nol2a8f0v: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-acl-action': 'general:get',
'x-decorator': 'DetailsBlockProvider',
'x-use-decorator-props': 'useDetailsDecoratorProps',
'x-decorator-props': {
dataSource: 'main',
collection: 'general',
readPretty: true,
action: 'get',
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:details',
'x-component': 'CardItem',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
qe6uvivhlcs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Details',
'x-read-pretty': true,
'x-use-component-props': 'useDetailsProps',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
'1ul3ug1mjn1': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-initializer': 'details:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 24,
},
},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'tx7ej82dtav',
'x-async': false,
},
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'details:configureFields',
'x-app-version': '1.0.0-alpha.17',
'x-index': 2,
properties: {
i4gnqtwjsmo: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
kpg3nd9z03j: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
email: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar':
'FormItemSchemaToolbar',
'x-settings':
'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props':
'useDataFormItemProps',
'x-collection-field':
'general.email',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'igz2roi2lgf',
'x-async': false,
},
},
'x-uid': 'twtz1r704we',
'x-async': false,
},
},
'x-uid': '4yltx4dtlwa',
'x-async': false,
},
'0j7ky7h63km': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
'x-index': 2,
properties: {
'1yq1bw53408': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
integer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar':
'FormItemSchemaToolbar',
'x-settings':
'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props':
'useDataFormItemProps',
'x-collection-field':
'general.integer',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'kz1wgswubq6',
'x-async': false,
},
},
'x-uid': 'hxdhhdrttal',
'x-async': false,
},
},
'x-uid': 'h3df5v19yer',
'x-async': false,
},
tqgzzposgug: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
'x-index': 3,
properties: {
j88upzin86t: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
number: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar':
'FormItemSchemaToolbar',
'x-settings':
'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props':
'useDataFormItemProps',
'x-collection-field':
'general.number',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'lb582vapwrl',
'x-async': false,
},
},
'x-uid': 'b9hxznmlw4y',
'x-async': false,
},
},
'x-uid': 'ccemewjagrc',
'x-async': false,
},
zsifuulhfj8: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.0.0-alpha.17',
'x-index': 4,
properties: {
'94mix5vzskc': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
properties: {
phone: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'string',
'x-toolbar':
'FormItemSchemaToolbar',
'x-settings':
'fieldSettings:FormItem',
'x-component': 'CollectionField',
'x-decorator': 'FormItem',
'x-use-decorator-props':
'useDataFormItemProps',
'x-collection-field':
'general.phone',
'x-component-props': {},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'b9sth7gi9co',
'x-async': false,
},
},
'x-uid': '24nb1vhc3c8',
'x-async': false,
},
},
'x-uid': 'q3fdeysf8i0',
'x-async': false,
},
},
'x-uid': 'w2d78a9y0s2',
'x-async': false,
},
},
'x-uid': 'cjtl8872i5s',
'x-async': false,
},
},
'x-uid': 'x79v0wahp73',
'x-async': false,
},
},
'x-uid': 'z5k8x76thiv',
'x-async': false,
},
},
'x-uid': 'dz7694elunl',
'x-async': false,
},
},
'x-uid': 'dpyxl72qav7',
'x-async': false,
},
},
'x-uid': 'ejoaawmib5s',
'x-async': false,
},
},
'x-uid': 'vqu7oj3nsba',
'x-async': false,
},
},
'x-uid': '80o560bno10',
'x-async': false,
},
},
'x-uid': 'as0562wfiri',
'x-async': false,
},
},
'x-uid': 'ipotr34hb0r',
'x-async': false,
},
},
'x-uid': 'hy58uesiqfe',
'x-async': false,
},
'2tygi2ob18k': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-toolbar': 'TableColumnSchemaToolbar',
'x-settings': 'fieldSettings:TableColumn',
'x-component': 'TableV2.Column',
'x-app-version': '1.0.0-alpha.17',
'x-index': 2,
properties: {
id: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 'general.id',
'x-component': 'CollectionField',
'x-component-props': {},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'uxtounspwze',
'x-async': false,
},
},
'x-uid': 'yvfmjf6tzk7',
'x-async': false,
},
t8rlom0c3ju: {
'x-uid': 'jt87by9dks1',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableV2.Column.Decorator',
'x-toolbar': 'TableColumnSchemaToolbar',
'x-settings': 'fieldSettings:TableColumn',
'x-component': 'TableV2.Column',
'x-app-version': '1.0.0-alpha.17',
'x-index': 3,
'x-linkage-style-rules': [],
properties: {
integer: {
_isJSONSchemaObject: true,
version: '2.0',
'x-collection-field': 'general.integer',
'x-component': 'CollectionField',
'x-component-props': {},
'x-read-pretty': true,
'x-decorator': null,
'x-decorator-props': {
labelStyle: {
display: 'none',
},
},
'x-app-version': '1.0.0-alpha.17',
'x-index': 1,
'x-uid': 'd3fvg9v46yu',
'x-async': false,
},
},
'x-async': false,
},
},
'x-uid': 'nwqvi9i433x',
'x-async': false,
},
},
'x-uid': 'qf53grjobl2',
'x-async': false,
},
},
'x-uid': 'c95omaoid1m',
'x-async': false,
},
},
'x-uid': 'h70lk7jikxa',
'x-async': false,
},
},
'x-uid': 'iylotyrenoo',
'x-async': false,
},
},
'x-uid': '7z81p33jsb1',
'x-async': true,
},
};
/**
* 1. Table
* 2. Add new Form sub-table