diff --git a/packages/core/client/src/data-source/collection-field/CollectionField.tsx b/packages/core/client/src/data-source/collection-field/CollectionField.tsx index 4bb5f6df4f..850937fa90 100644 --- a/packages/core/client/src/data-source/collection-field/CollectionField.tsx +++ b/packages/core/client/src/data-source/collection-field/CollectionField.tsx @@ -83,6 +83,8 @@ const CollectionFieldInternalField_deprecated: React.FC = (props: Props) => { setRequired(field, fieldSchema, uiSchema); // @ts-ignore field.dataSource = uiSchema.enum; + field.data = field.data || {}; + field.data.dataSource = uiSchema.enum; const originalProps = compile(uiSchema['x-component-props']) || {}; field.componentProps = merge(originalProps, field.componentProps || {}, dynamicProps || {}); }, [uiSchemaOrigin]); @@ -109,6 +111,8 @@ const CollectionFieldInternalField = (props) => { field.readPretty = true; } }, [field, fieldSchema]); + field.data = field.data || {}; + field.data.dataSource = uiSchema.enum; if (!uiSchema) return null; diff --git a/packages/core/client/src/modules/fields/component/Select/__e2e__/selectOptionsInLinkageRule.test.ts b/packages/core/client/src/modules/fields/component/Select/__e2e__/selectOptionsInLinkageRule.test.ts new file mode 100644 index 0000000000..0684ebf21a --- /dev/null +++ b/packages/core/client/src/modules/fields/component/Select/__e2e__/selectOptionsInLinkageRule.test.ts @@ -0,0 +1,33 @@ +/** + * 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, test } from '@nocobase/test/e2e'; +import { oneFormWithSelectField } from './templatesOfBug'; + +test.describe('options of Select field in linkage rule', () => { + test('options change with linkage rule ', async ({ page, mockPage }) => { + await mockPage(oneFormWithSelectField).goto(); + // 联动规则控制选项 + await page.getByLabel('block-item-CardItem-general-').hover(); + await page.getByLabel('block-item-CollectionField-').click(); + await expect(page.getByRole('option', { name: 'option2' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'option3' })).not.toBeVisible(); + await page.getByRole('option', { name: 'option2' }).click(); + + // 去掉联动规则恢复选项 + await page.getByLabel('block-item-CardItem-general-').hover(); + await page.getByLabel('designer-schema-settings-CardItem-blockSettings:createForm-general').hover(); + await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('switch', { name: 'On Off' }).click(); + await page.getByRole('button', { name: 'OK' }).click(); + await page.reload(); + await expect(page.getByRole('option', { name: 'option2' })).toBeVisible(); + await expect(page.getByRole('option', { name: 'option3' })).toBeVisible(); + }); +}); diff --git a/packages/core/client/src/modules/fields/component/Select/__e2e__/templatesOfBug.ts b/packages/core/client/src/modules/fields/component/Select/__e2e__/templatesOfBug.ts index 1d90fe8ebf..dc2e7f36cf 100644 --- a/packages/core/client/src/modules/fields/component/Select/__e2e__/templatesOfBug.ts +++ b/packages/core/client/src/modules/fields/component/Select/__e2e__/templatesOfBug.ts @@ -7,6 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { generalWithM2oSingleSelect } from '@nocobase/test/e2e'; + export const T3867 = { pageSchema: { _isJSONSchemaObject: true, @@ -581,3 +583,176 @@ export const oneFormWithSubTableSelectField = { }, ], }; + +export const oneFormWithSelectField = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + iza2br2wzq4: { + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + dc4lkvej93w: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.7.0-beta.1', + properties: { + jrfai5z5a4e: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.7.0-beta.1', + properties: { + bph3a0yrmvp: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-acl-action': 'general:create', + 'x-decorator': 'FormBlockProvider', + 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'general', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:createForm', + 'x-component': 'CardItem', + 'x-app-version': '1.7.0-beta.1', + properties: { + '2ef5ea23d4b': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useCreateFormBlockProps', + 'x-app-version': '1.7.0-beta.1', + properties: { + grid: { + 'x-uid': 'oqk574y4mx2', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'form:configureFields', + 'x-app-version': '1.7.0-beta.1', + 'x-linkage-rules': [ + { + condition: { + $and: [], + }, + actions: [ + { + targetFields: ['singleSelect'], + operator: 'options', + value: { + value: ['option2'], + }, + }, + ], + }, + ], + properties: { + '1ijfrrxqs4c': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.7.0-beta.1', + properties: { + '8j09c65cp2x': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.7.0-beta.1', + properties: { + singleSelect: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'general.singleSelect', + 'x-component-props': { + style: { + width: '100%', + }, + }, + 'x-app-version': '1.7.0-beta.1', + 'x-uid': 'dxsbc80ewso', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'la6qedc9qwx', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ijsbxa4yys7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + kpp1azsxbzy: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'createForm:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + }, + 'x-app-version': '1.7.0-beta.1', + 'x-uid': '1n56lepnve8', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'lf74cpc3tub', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'mhxlfdk74jy', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'obyvsr0s0nx', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 's6jd8p9h28q', + 'x-async': false, + 'x-index': 1, + }, + }, + name: '8btun7jzkrk', + 'x-uid': 'eyjf89l83a1', + 'x-async': true, + 'x-index': 1, + }, + }, + 'x-uid': 'hizrr7jzogr', + 'x-async': true, + 'x-index': 1, + }, + collections: generalWithM2oSingleSelect, +}; diff --git a/packages/core/client/src/schema-component/antd/select/Select.tsx b/packages/core/client/src/schema-component/antd/select/Select.tsx index 269beecddd..e628256791 100644 --- a/packages/core/client/src/schema-component/antd/select/Select.tsx +++ b/packages/core/client/src/schema-component/antd/select/Select.tsx @@ -14,6 +14,7 @@ import { isPlainObject } from '@nocobase/utils/client'; import type { SelectProps as AntdSelectProps } from 'antd'; import { Select as AntdSelect, Empty, Spin, Tag } from 'antd'; import React from 'react'; +import { every } from 'lodash'; import { ReadPretty } from './ReadPretty'; import { FieldNames, defaultFieldNames, getCurrentOptions } from './utils'; import { BaseOptionType, DefaultOptionType } from 'antd/es/select'; @@ -188,8 +189,13 @@ const InternalSelect = connect( dataSource: 'options', }, (props, field) => { + const { value, options } = props; + const result = every(options, (k) => k.value !== value) + ? field?.data?.dataSource?.find?.((v) => v.value === value)?.label || value + : value; return { ...props, + value: result, fieldNames: { ...defaultFieldNames, ...props.fieldNames }, suffixIcon: field?.['loading'] || field?.['validating'] ? : props.suffixIcon, }; diff --git a/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleAction.tsx b/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleAction.tsx index f238c0a60e..06818fd0e7 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleAction.tsx +++ b/packages/core/client/src/schema-settings/LinkageRules/LinkageRuleAction.tsx @@ -18,6 +18,7 @@ import { useTranslation } from 'react-i18next'; import { useCompile } from '../..'; import { DynamicComponent } from './DynamicComponent'; import { ValueDynamicComponent } from './ValueDynamicComponent'; +import { OptionsComponent } from './OptionsComponent'; import { LinkageLogicContext, RemoveActionContext } from './context'; import { ActionType } from './type'; import { useValues } from './useValues'; @@ -107,6 +108,14 @@ export const FormFieldLinkageRuleAction = observer( collectionName={collectionName} /> )} + {[ActionType.Options].includes(operator) && ( + + )} {!props.disabled && ( remove()} style={{ color: '#bfbfbf' }} /> diff --git a/packages/core/client/src/schema-settings/LinkageRules/OptionsComponent.tsx b/packages/core/client/src/schema-settings/LinkageRules/OptionsComponent.tsx new file mode 100644 index 0000000000..758f2e92a3 --- /dev/null +++ b/packages/core/client/src/schema-settings/LinkageRules/OptionsComponent.tsx @@ -0,0 +1,45 @@ +/** + * 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 { css } from '@emotion/css'; +import React, { useCallback, useMemo } from 'react'; +import { Select } from 'antd'; + +import { DynamicComponent } from './DynamicComponent'; + +export type InputModeType = 'constant' | 'express' | 'empty'; +interface ValueDynamicComponentProps { + fieldValue: any; + schema: any; + setValue: (value: any) => void; + collectionName: string; + inputModes?: Array; +} + +export const OptionsComponent = (props: ValueDynamicComponentProps) => { + const { fieldValue, setValue, schema } = props; + const handleChangeOfConstant = useCallback( + (value) => { + setValue({ + value, + }); + }, + [setValue], + ); + + return ( +