Merge branch 'next' into develop

This commit is contained in:
nocobase[bot] 2024-12-21 16:28:43 +00:00
commit 1af5dc070b
7 changed files with 1170 additions and 30 deletions

View File

@ -13,7 +13,7 @@ import {
oneTableBlockWithAddNewAndViewAndEditAndBasicFields, oneTableBlockWithAddNewAndViewAndEditAndBasicFields,
test, test,
} from '@nocobase/test/e2e'; } from '@nocobase/test/e2e';
import { T2165, T3251, T3806, T3815, expressionTemplateInLinkageRules, T4891 } from './templatesOfBug'; import { expressionTemplateInLinkageRules, T2165, T3251, T3806, T3815, T4891 } from './templatesOfBug';
test.describe('linkage rules', () => { test.describe('linkage rules', () => {
test('basic usage', async ({ page, mockPage }) => { test('basic usage', async ({ page, mockPage }) => {
@ -276,7 +276,6 @@ test.describe('linkage rules', () => {
} }
}); });
// https://nocobase.height.app/T-T3815 &&T-3802
test('fireImmediately in create form & edit form', async ({ page, mockPage, mockRecord }) => { test('fireImmediately in create form & edit form', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(T3815).waitForInit(); const nocoPage = await mockPage(T3815).waitForInit();
await mockRecord('general', { await mockRecord('general', {

View File

@ -18,6 +18,7 @@ import { useActionContext } from '..';
import { useAttach, useComponent } from '../..'; import { useAttach, useComponent } from '../..';
import { getCardItemSchema } from '../../../block-provider'; import { getCardItemSchema } from '../../../block-provider';
import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider'; import { useTemplateBlockContext } from '../../../block-provider/TemplateBlockProvider';
import { useDataBlockRequest } from '../../../data-source/data-block/DataBlockRequestProvider';
import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField';
import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
import { bindLinkageRulesToFiled } from '../../../schema-settings/LinkageRules/bindLinkageRulesToFiled'; import { bindLinkageRulesToFiled } from '../../../schema-settings/LinkageRules/bindLinkageRulesToFiled';
@ -134,6 +135,7 @@ const WithForm = (props: WithFormProps) => {
const variables = useVariables(); const variables = useVariables();
const localVariables = useLocalVariables({ currentForm: form }); const localVariables = useLocalVariables({ currentForm: form });
const { templateFinished } = useTemplateBlockContext(); const { templateFinished } = useTemplateBlockContext();
const { loading } = useDataBlockRequest() || {};
const linkageRules: any[] = const linkageRules: any[] =
(getLinkageRules(fieldSchema) || fieldSchema.parent?.['x-linkage-rules'])?.filter((k) => !k.disabled) || []; (getLinkageRules(fieldSchema) || fieldSchema.parent?.['x-linkage-rules'])?.filter((k) => !k.disabled) || [];
@ -156,29 +158,36 @@ const WithForm = (props: WithFormProps) => {
}, [form, props.disabled, setFormValueChanged]); }, [form, props.disabled, setFormValueChanged]);
useEffect(() => { useEffect(() => {
if (loading) {
return;
}
const id = uid(); const id = uid();
const disposes = []; const disposes = [];
form.addEffects(id, () => { // 如果不延迟执行,那么一开始获取到的 form.values 的值是旧的,会导致详情区块的联动规则出现一些问题
forEachLinkageRule(linkageRules, (action, rule) => { setTimeout(() => {
if (action.targetFields?.length) { form.addEffects(id, () => {
const fields = action.targetFields.join(','); forEachLinkageRule(linkageRules, (action, rule) => {
if (action.targetFields?.length) {
const fields = action.targetFields.join(',');
// 之前使用的 `onFieldReact` 有问题,没有办法被取消监听,所以这里用 `onFieldInit` 和 `reaction` 代替 // 之前使用的 `onFieldReact` 有问题,没有办法被取消监听,所以这里用 `onFieldInit` 和 `reaction` 代替
onFieldInit(`*(${fields})`, (field: any, form) => { onFieldInit(`*(${fields})`, (field: any, form) => {
disposes.push( disposes.push(
bindLinkageRulesToFiled({ bindLinkageRulesToFiled({
field, field,
linkageRules, linkageRules,
formValues: form.values, formValues: form.values,
localVariables, localVariables,
action, action,
rule, rule,
variables, variables,
}), }),
); );
}); });
} }
});
}); });
}); });
@ -188,7 +197,7 @@ const WithForm = (props: WithFormProps) => {
dispose(); dispose();
}); });
}; };
}, [linkageRules, templateFinished]); }, [linkageRules, templateFinished, loading]);
return fieldSchema['x-decorator'] === 'FormV2' ? <FormDecorator {...props} /> : <FormComponent {...props} />; return fieldSchema['x-decorator'] === 'FormV2' ? <FormDecorator {...props} /> : <FormComponent {...props} />;
}; };

View File

@ -95,7 +95,6 @@ export const conditionAnalyses = async ({
const conditions = ruleGroup[type]; const conditions = ruleGroup[type];
let results = conditions.map(async (condition) => { let results = conditions.map(async (condition) => {
// fix https://nocobase.height.app/T-3152
if ('$and' in condition || '$or' in condition) { if ('$and' in condition || '$or' in condition) {
return await conditionAnalyses({ ruleGroup: condition, variables, localVariables }); return await conditionAnalyses({ ruleGroup: condition, variables, localVariables });
} }
@ -155,7 +154,7 @@ export const conditionAnalyses = async ({
* @param targetField * @param targetField
* @returns * @returns
*/ */
export function targetFieldToVariableString(targetField: string[], variableName = '$nForm') { function targetFieldToVariableString(targetField: string[], variableName = '$nForm') {
// Action 中的联动规则虽然没有 form 上下文但是在这里也使用的是 `$nForm` 变量,这样实现更简单 // Action 中的联动规则虽然没有 form 上下文但是在这里也使用的是 `$nForm` 变量,这样实现更简单
return `{{ ${variableName}.${targetField.join('.')} }}`; return `{{ ${variableName}.${targetField.join('.')} }}`;
} }

View File

@ -102,7 +102,6 @@ function getFieldValuesInCondition({ linkageRules, formValues }) {
return conditions return conditions
.map((condition) => { .map((condition) => {
// fix https://nocobase.height.app/T-3251
if ('$and' in condition || '$or' in condition) { if ('$and' in condition || '$or' in condition) {
return run(condition); return run(condition);
} }

View File

@ -77,16 +77,13 @@ const useCurrentFormData = () => {
*/ */
export const useCurrentFormContext = ({ form: _form }: Pick<Props, 'form'> = {}) => { export const useCurrentFormContext = ({ form: _form }: Pick<Props, 'form'> = {}) => {
const { form } = useFormBlockContext(); const { form } = useFormBlockContext();
const formData = useCurrentFormData();
const { isVariableParsedInOtherContext } = useFlag(); const { isVariableParsedInOtherContext } = useFlag();
const formInstance = _form || form; const formInstance = _form || form;
return { return {
/** 变量值 */ /** 变量值 */
currentFormCtx: currentFormCtx: formInstance?.values,
formInstance?.readPretty === false && formInstance?.values && Object.keys(formInstance?.values)?.length
? formInstance?.values
: formData || formInstance?.values,
/** 用来判断是否可以显示`当前表单`变量 */ /** 用来判断是否可以显示`当前表单`变量 */
shouldDisplayCurrentForm: formInstance && !formInstance.readPretty && !isVariableParsedInOtherContext, shouldDisplayCurrentForm: formInstance && !formInstance.readPretty && !isVariableParsedInOtherContext,
}; };

View File

@ -0,0 +1,61 @@
/**
* 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 { formFieldDependsOnSubtableFieldsWithLinkageRules } from './template';
test.describe('linkage rules', () => {
test('form field depends on subtable fields with linkage rules', async ({ page, mockPage, mockRecord }) => {
const nocoPage = await mockPage(formFieldDependsOnSubtableFieldsWithLinkageRules).waitForInit();
const record = await mockRecord('test');
await nocoPage.goto();
// 1. 点击 Add new打开新增表单弹窗
await page.getByLabel('action-Action-Add new-create-').click();
// 2. 新增表单中填上子表单的值result 会自动计算结果
await page.getByLabel('action-AssociationField.').click();
await page.getByLabel('block-item-CollectionField-A-form-A.count-count').getByRole('spinbutton').fill('10');
await page.getByLabel('block-item-CollectionField-A-form-A.price-price').getByRole('spinbutton').fill('10');
// 10 * 10 = 100
await expect(
page.getByLabel('block-item-CollectionField-test-form-test.result-result').getByRole('spinbutton'),
).toHaveValue('100');
// 3. 关闭弹窗,点击 Edit 按钮打开编辑表单弹窗
await page.getByLabel('drawer-Action.Container-test-Add record-mask').click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await page.getByLabel('action-Action.Link-Edit-').click();
// 4. 编辑表单中的 result 字段值,应该是由子表格中填入的值计算得出的
await expect(
page.getByLabel('block-item-CollectionField-test-form-test.result-result').getByRole('spinbutton'),
).toHaveValue(String(calcResult(record)));
// 5. 在子表格中新增并输入新的值result 字段值会自动计算
await page.getByLabel('action-AssociationField.').click();
await page
.getByRole('row', { name: `table-index-${record.subtable.length + 1} block-item-` })
.getByRole('spinbutton')
.first()
.fill('10');
await page
.getByRole('row', { name: `table-index-${record.subtable.length + 1} block-item-` })
.getByRole('spinbutton')
.nth(1)
.fill('10');
await expect(
page.getByLabel('block-item-CollectionField-test-form-test.result-result').getByRole('spinbutton'),
).toHaveValue(String(calcResult(record) + 10 * 10));
});
});
function calcResult(record) {
return record.subtable.reduce((acc, item) => acc + item.count * item.price, 0);
}

File diff suppressed because it is too large Load Diff