diff --git a/packages/core/app/client/.umirc.ts b/packages/core/app/client/.umirc.ts index dbea034576..f7fa98c609 100644 --- a/packages/core/app/client/.umirc.ts +++ b/packages/core/app/client/.umirc.ts @@ -24,6 +24,7 @@ export default defineConfig({ content: isDevCmd ? ` window['__nocobase_public_path__'] = "${process.env.APP_PUBLIC_PATH || '/'}"; + window['__nocobase_dev_public_path__'] = "/"; ` : ` window['__webpack_public_path__'] = '{{env.APP_PUBLIC_PATH}}'; diff --git a/packages/core/client/src/block-provider/DetailsBlockProvider.tsx b/packages/core/client/src/block-provider/DetailsBlockProvider.tsx index 81f1195f3b..69d9a50cf6 100644 --- a/packages/core/client/src/block-provider/DetailsBlockProvider.tsx +++ b/packages/core/client/src/block-provider/DetailsBlockProvider.tsx @@ -13,12 +13,13 @@ import { useUpdate } from 'ahooks'; import { Spin } from 'antd'; import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'; import { useCollectionManager_deprecated } from '../collection-manager'; -import { useCollectionRecordData } from '../data-source'; +import { useCollection, useCollectionRecordData } from '../data-source'; import { useCollectionParentRecord } from '../data-source/collection-record/CollectionRecordProvider'; import { withDynamicSchemaProps } from '../hoc/withDynamicSchemaProps'; import { useDetailsWithPaginationBlockParams } from '../modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationBlockParams'; import { RecordProvider } from '../record-provider'; import { useDesignable } from '../schema-component'; +import { CurrentRecordContextProvider } from '../schema-settings/VariableInput/hooks/useRecordVariable'; import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { TemplateBlockProvider } from './TemplateBlockProvider'; @@ -38,6 +39,7 @@ const InternalDetailsBlockProvider = (props) => { }), [readPretty], ); + const collection = useCollection(); const { resource, service } = useBlockRequestContext(); const parentRecord = useCollectionParentRecord(); const currentRecord = (action === 'list' ? service?.data?.data?.[0] : service?.data?.data) || {}; @@ -59,13 +61,15 @@ const InternalDetailsBlockProvider = (props) => { field.loaded = true; return ( - -
- - {props.children} - -
-
+ + +
+ + {props.children} + +
+
+
); }; diff --git a/packages/core/client/src/modules/variable/__e2e__/currentRecord.test.ts b/packages/core/client/src/modules/variable/__e2e__/currentRecord.test.ts new file mode 100644 index 0000000000..458997bd91 --- /dev/null +++ b/packages/core/client/src/modules/variable/__e2e__/currentRecord.test.ts @@ -0,0 +1,58 @@ +/** + * 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 { test } from '@nocobase/test/e2e'; +import { currentRecordShouldBeConsistentWithCurrentFormFieldValues } from './templates'; + +test.describe('variable: Current Record', () => { + test('Current record should be consistent with current form field values', async ({ mockPage, page }) => { + await mockPage(currentRecordShouldBeConsistentWithCurrentFormFieldValues).goto(); + + // 1. 子表单联动规则中的“当前记录”应该指的是整个表单的值 + await page.getByLabel('block-item-CollectionField-users-form-users.roles-Roles').hover(); + await page + .getByRole('button', { name: 'designer-schema-settings-CollectionField-fieldSettings:FormItem-users-users.' }) + .hover(); + await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').click(); + + // 当前表单中应该包含 “Nickname” 字段 + await page.getByRole('menuitemcheckbox', { name: 'Current form right' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click(); + + // 当前对象中应该包含 “Role UID” 字段 + await page.getByLabel('variable-button').click(); + await page.getByText('Current object').click(); + await page.getByRole('menuitemcheckbox', { name: 'Current object right' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Role UID' }).click(); + await page.getByRole('button', { name: 'Cancel' }).click(); + + // 2. 子详情联动规则中的“当前记录”应该指的是整个详情的值 + await page.getByLabel('block-item-CollectionField-users-details-users.roles-Roles').hover(); + await page + .getByRole('button', { name: 'designer-schema-settings-CollectionField-fieldSettings:FormItem-users-users.' }) + .hover(); + await page.getByRole('menuitem', { name: 'Linkage rules' }).click(); + await page.getByRole('button', { name: 'plus Add linkage rule' }).click(); + await page.getByText('Add condition', { exact: true }).click(); + await page.getByLabel('variable-button').click(); + + // 当前记录中应该包含 “Nickname” 字段 + await page.getByRole('menuitemcheckbox', { name: 'Current record right' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Nickname' }).click(); + await page.getByLabel('variable-button').click(); + + // 当前对象中应该包含 “Role UID” 字段 + await page.getByRole('menuitemcheckbox', { name: 'Current object right' }).click(); + await page.getByRole('menuitemcheckbox', { name: 'Role UID' }).click(); + await page.getByRole('button', { name: 'Cancel' }).click(); + }); +}); diff --git a/packages/core/client/src/modules/variable/__e2e__/templates.ts b/packages/core/client/src/modules/variable/__e2e__/templates.ts index f5c59fee6d..b615597c86 100644 --- a/packages/core/client/src/modules/variable/__e2e__/templates.ts +++ b/packages/core/client/src/modules/variable/__e2e__/templates.ts @@ -1700,3 +1700,445 @@ export const inDefaultValue = { 'x-index': 1, }, }; +export const currentRecordShouldBeConsistentWithCurrentFormFieldValues = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-app-version': '1.4.20', + properties: { + zo36w7ug1dz: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + 'x-app-version': '1.4.20', + properties: { + q5xiepxl3h4: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.4.20', + properties: { + '35ajnn2jwrw': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.4.20', + properties: { + '6r2awpsao1y': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action-props': { + skipScopeCheck: true, + }, + 'x-acl-action': 'users:create', + 'x-decorator': 'FormBlockProvider', + 'x-use-decorator-props': 'useCreateFormBlockDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'users', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:createForm', + 'x-component': 'CardItem', + 'x-app-version': '1.4.20', + properties: { + ck8awzqf151: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useCreateFormBlockProps', + 'x-app-version': '1.4.20', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'form:configureFields', + 'x-app-version': '1.4.20', + properties: { + '68rwhm0jce8': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.4.20', + properties: { + '76vob53gb7p': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.4.20', + properties: { + roles: { + 'x-uid': '0vcbo02pxee', + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'users.roles', + 'x-component-props': { + fieldNames: { + label: 'name', + value: 'name', + }, + mode: 'Nester', + }, + 'x-app-version': '1.4.20', + default: null, + properties: { + boypqry0nbt: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'AssociationField.Nester', + 'x-index': 1, + 'x-app-version': '1.4.20', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'form:configureFields', + 'x-app-version': '1.4.20', + properties: { + k9qjoqxrzr7: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.4.20', + properties: { + '5jv79jae2m8': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.4.20', + properties: { + title: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'roles.title', + 'x-component-props': {}, + 'x-app-version': '1.4.20', + 'x-uid': '2m8sp7gdotx', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vw3pbgutx5y', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '6u7fip4cj1v', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'ore97iux65x', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wzl5sc2c6yu', + 'x-async': false, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'r0vb4bt9tel', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '8qbpio2hkfd', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'lqfs7zk3s6y', + 'x-async': false, + 'x-index': 1, + }, + '2onf5kmpn4q': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'createForm:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + }, + 'x-app-version': '1.4.20', + 'x-uid': 'aoy46dmlkqo', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'jv590f4tadc', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7jwk00jtw3k', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1er15h3uvmo', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'x6n1myvjgk1', + 'x-async': false, + 'x-index': 1, + }, + gflub1edkyb: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.4.20', + properties: { + q5t38kueeo3: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.4.20', + properties: { + z5bfn8ukvfv: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-acl-action': 'users:view', + 'x-decorator': 'DetailsBlockProvider', + 'x-use-decorator-props': 'useDetailsWithPaginationDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'users', + readPretty: true, + action: 'list', + params: { + pageSize: 1, + }, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:detailsWithPagination', + 'x-component': 'CardItem', + 'x-app-version': '1.4.20', + properties: { + gm3s76nstug: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Details', + 'x-read-pretty': true, + 'x-use-component-props': 'useDetailsWithPaginationProps', + 'x-app-version': '1.4.20', + properties: { + qtz93axhj9i: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'details:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 24, + }, + }, + 'x-app-version': '1.4.20', + 'x-uid': '6bdkha9weab', + '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.4.20', + properties: { + vb6h5lz7q36: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.4.20', + properties: { + aku0q0eqewd: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.4.20', + properties: { + roles: { + 'x-uid': 'jic2hsrnf38', + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'users.roles', + 'x-component-props': { + fieldNames: { + label: 'name', + value: 'name', + }, + mode: 'Nester', + }, + 'x-app-version': '1.4.20', + properties: { + hue6lnwg207: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'AssociationField.Nester', + 'x-index': 1, + 'x-app-version': '1.4.20', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'form:configureFields', + 'x-app-version': '1.4.20', + properties: { + j8xe15cy2li: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.4.20', + properties: { + fa6a7554j0f: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.4.20', + properties: { + title: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'string', + 'x-toolbar': 'FormItemSchemaToolbar', + 'x-settings': 'fieldSettings:FormItem', + 'x-component': 'CollectionField', + 'x-decorator': 'FormItem', + 'x-collection-field': 'roles.title', + 'x-component-props': {}, + 'x-app-version': '1.4.20', + 'x-uid': 'q4jvlbcsfn3', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'obejokxbbho', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'nypmsk1jo08', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vsxldwluacr', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'v6m8c2f72w1', + 'x-async': false, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zbvlh2nycdj', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fht7awydz0x', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'zckbq3plc4g', + '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.4.20', + 'x-uid': '8flslu4xijz', + 'x-async': false, + 'x-index': 3, + }, + }, + 'x-uid': 'mepla8eyvus', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '2vjun6r21nv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '4904jmjuolh', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'coe5thhvl3s', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'lg3dusgeb2g', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'xbpy965iud8', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/schema-component/antd/upload/placeholder.ts b/packages/core/client/src/schema-component/antd/upload/placeholder.ts index b5a25bb4ac..f863f9e918 100644 --- a/packages/core/client/src/schema-component/antd/upload/placeholder.ts +++ b/packages/core/client/src/schema-component/antd/upload/placeholder.ts @@ -7,6 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +const publicPath = window['__nocobase_dev_public_path__'] || window['__nocobase_public_path__'] || '/'; + export const UPLOAD_PLACEHOLDER = [ { ext: /\.docx?$/i, @@ -64,6 +66,11 @@ export const UPLOAD_PLACEHOLDER = [ ext: /\.(zip|rar|arj|z|gz|iso|jar|ace|tar|uue|dmg|pkg|lzh|cab)$/i, icon: '/file-placeholder/zip-200-200.png', }, -]; +].map((item) => { + return { + ext: item.ext, + icon: publicPath + item.icon.slice(1), + }; +}); -export const UNKNOWN_FILE_ICON = '/file-placeholder/unknown-200-200.png'; +export const UNKNOWN_FILE_ICON = publicPath + 'file-placeholder/unknown-200-200.png'; diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx index 974134321f..9ae78a6154 100644 --- a/packages/core/client/src/schema-settings/SchemaSettings.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx @@ -112,6 +112,7 @@ import { ChildDynamicComponent } from './EnableChildCollections/DynamicComponent import { FormLinkageRules } from './LinkageRules'; import { useLinkageCollectionFieldOptions } from './LinkageRules/action-hooks'; import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './LinkageRules/type'; +import { CurrentRecordContextProvider, useCurrentRecord } from './VariableInput/hooks/useRecordVariable'; export interface SchemaSettingsProps { title?: any; dn?: Designable; @@ -821,6 +822,7 @@ export const SchemaSettingsModalItem: FC = (props) const dataSourceKey = useDataSourceKey(); const record = useCollectionRecord(); const { association } = useDataBlockProps() || {}; + const currentRecordCtx = useCurrentRecord(); const formCtx = useFormBlockContext(); const blockOptions = useBlockContext(); const { getOperators } = useOperators(); @@ -862,55 +864,57 @@ export const SchemaSettingsModalItem: FC = (props) }} > - - - - - - - - - 576px - @media (min-width: 576px) { - min-width: 520px; - } + + + + + + + + + + 576px + @media (min-width: 576px) { + min-width: 520px; + } - // screen <= 576px - @media (max-width: 576px) { - min-width: 320px; - } - `} - > - - - - - - - - - - - - - - - - + // screen <= 576px + @media (max-width: 576px) { + min-width: 320px; + } + `} + > + + + + + + + + + + + + + + + + + diff --git a/packages/core/client/src/schema-settings/VariableInput/hooks/useRecordVariable.ts b/packages/core/client/src/schema-settings/VariableInput/hooks/useRecordVariable.tsx similarity index 77% rename from packages/core/client/src/schema-settings/VariableInput/hooks/useRecordVariable.ts rename to packages/core/client/src/schema-settings/VariableInput/hooks/useRecordVariable.tsx index c368c0bc28..33d123fa51 100644 --- a/packages/core/client/src/schema-settings/VariableInput/hooks/useRecordVariable.ts +++ b/packages/core/client/src/schema-settings/VariableInput/hooks/useRecordVariable.tsx @@ -9,6 +9,7 @@ import { Schema } from '@formily/json-schema'; import _ from 'lodash'; +import React, { createContext, FC, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useBlockContext } from '../../../block-provider/BlockProvider'; import { useFormBlockContext } from '../../../block-provider/FormBlockProvider'; @@ -25,6 +26,28 @@ interface Props { targetFieldSchema?: Schema; } +interface CurrentRecordContextProps { + recordData: any; + collectionName: string; +} + +const CurrentRecordContext = createContext(null); + +export const CurrentRecordContextProvider: FC = (props) => { + const value = useMemo(() => { + return { + recordData: props.recordData, + collectionName: props.collectionName, + }; + }, [props.recordData, props.collectionName]); + + return {props.children} ; +}; + +export const useCurrentRecord = () => { + return useContext(CurrentRecordContext); +}; + /** * @deprecated * 该 hook 已废弃,请使用 `useCurrentRecordVariable` 代替 @@ -54,15 +77,18 @@ export const useRecordVariable = (props: Props) => { * @returns */ export const useCurrentRecordContext = () => { + const ctx = useCurrentRecord(); const { name: blockType } = useBlockContext() || {}; const collection = useCollection(); const recordData = useCollectionRecordData(); const { formRecord, collectionName } = useFormBlockContext(); - const realCollectionName = formRecord?.data ? collectionName : collection?.name; + let realCollectionName = formRecord?.data ? collectionName : collection?.name; + + realCollectionName = ctx?.collectionName || realCollectionName; return { /** 变量值 */ - currentRecordCtx: formRecord?.data || recordData, + currentRecordCtx: ctx?.recordData || formRecord?.data || recordData, /** 用于判断是否需要显示配置项 */ shouldDisplayCurrentRecord: !_.isEmpty(_.omit(recordData, ['__collectionName', '__parent'])) || !!formRecord?.data, /** 当前记录对应的 collection name */