feat: action linkage rule support current form variables

This commit is contained in:
katherinehhh 2025-04-29 22:12:59 +08:00
parent 0965749f8c
commit 5e3b58d369
2 changed files with 80 additions and 58 deletions

View File

@ -48,8 +48,6 @@ export const BlockLinkageRuleProvider = (props) => {
useEffect(() => { useEffect(() => {
if (shouldCalculateFormLinkage) { if (shouldCalculateFormLinkage) {
const id = uid(); const id = uid();
const disposes = [];
// 延迟执行,防止一开始获取到的 form.values 值是旧的 // 延迟执行,防止一开始获取到的 form.values 值是旧的
setTimeout(() => { setTimeout(() => {
form.addEffects(id, () => { form.addEffects(id, () => {
@ -77,9 +75,6 @@ export const BlockLinkageRuleProvider = (props) => {
// 清理副作用 // 清理副作用
return () => { return () => {
form.removeEffects(id); form.removeEffects(id);
disposes.forEach((dispose) => {
dispose();
});
}; };
} }
}, [linkageRules, shouldCalculateFormLinkage]); }, [linkageRules, shouldCalculateFormLinkage]);

View File

@ -12,10 +12,13 @@ import { observer, Schema, useField, useFieldSchema, useForm } from '@formily/re
import { isPortalInBody } from '@nocobase/utils/client'; import { isPortalInBody } from '@nocobase/utils/client';
import { App, Button, Tooltip } from 'antd'; import { App, Button, Tooltip } from 'antd';
import classnames from 'classnames'; import classnames from 'classnames';
import { isEqual } from 'lodash';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { reaction } from '@formily/reactive';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { uid } from '@formily/shared';
import { ErrorFallback, StablePopover, TabsContextProvider, useActionContext } from '../..'; import { ErrorFallback, StablePopover, TabsContextProvider, useActionContext } from '../..';
import { useDesignable } from '../../'; import { useDesignable } from '../../';
import { useACLActionParamsContext } from '../../../acl'; import { useACLActionParamsContext } from '../../../acl';
@ -29,7 +32,6 @@ import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps';
import { Icon } from '../../../icon'; import { Icon } from '../../../icon';
import { TreeRecordProvider } from '../../../modules/blocks/data-blocks/table/TreeRecordProvider'; import { TreeRecordProvider } from '../../../modules/blocks/data-blocks/table/TreeRecordProvider';
import { VariablePopupRecordProvider } from '../../../modules/variable/variablesProvider/VariablePopupRecordProvider'; import { VariablePopupRecordProvider } from '../../../modules/variable/variablesProvider/VariablePopupRecordProvider';
import { RecordProvider } from '../../../record-provider';
import { useLocalVariables, useVariables } from '../../../variables'; import { useLocalVariables, useVariables } from '../../../variables';
import { SortableItem } from '../../common'; import { SortableItem } from '../../common';
import { useCompile, useComponent, useDesigner } from '../../hooks'; import { useCompile, useComponent, useDesigner } from '../../hooks';
@ -49,7 +51,11 @@ import { useGetAriaLabelOfAction } from './hooks/useGetAriaLabelOfAction';
import { ActionContextProps, ActionProps, ComposedAction } from './types'; import { ActionContextProps, ActionProps, ComposedAction } from './types';
import { linkageAction, setInitialActionState } from './utils'; import { linkageAction, setInitialActionState } from './utils';
import { NAMESPACE_UI_SCHEMA } from '../../../i18n/constant'; import { NAMESPACE_UI_SCHEMA } from '../../../i18n/constant';
import { BlockContext } from '../../../block-provider/BlockProvider'; import { forEachLinkageRule } from '../../../schema-settings/LinkageRules/forEachLinkageRule';
import {
getVariableValuesInCondition,
getVariableValuesInExpression,
} from '../../../schema-settings/LinkageRules/bindLinkageRulesToFiled';
// 这个要放到最下面,否则会导致前端单测失败 // 这个要放到最下面,否则会导致前端单测失败
import { useApp } from '../../../application'; import { useApp } from '../../../application';
@ -97,39 +103,62 @@ export const Action: ComposedAction = withDynamicSchemaProps(
const { designable } = useDesignable(); const { designable } = useDesignable();
const tarComponent = useComponent(component) || component; const tarComponent = useComponent(component) || component;
const variables = useVariables(); const variables = useVariables();
const localVariables = useLocalVariables({ const localVariables = useLocalVariables();
currentForm: { values: recordData, readPretty: false } as any,
});
const { visibleWithURL, setVisibleWithURL } = usePopupUtils(); const { visibleWithURL, setVisibleWithURL } = usePopupUtils();
const { setSubmitted } = useActionContext(); const { setSubmitted } = useActionContext();
const { getAriaLabel } = useGetAriaLabelOfAction(title); const { getAriaLabel } = useGetAriaLabelOfAction(title);
const parentRecordData = useCollectionParentRecordData(); const parentRecordData = useCollectionParentRecordData();
const app = useApp(); const app = useApp();
const { getAllDataBlocks } = useAllDataBlocks(); const { getAllDataBlocks } = useAllDataBlocks();
const form = useForm();
useEffect(() => { useEffect(() => {
if (field.stateOfLinkageRules) { if (field.stateOfLinkageRules) {
setInitialActionState(field); setInitialActionState(field);
} }
field.stateOfLinkageRules = {}; const id = uid();
linkageRules const disposes = [];
.filter((k) => !k.disabled) // 如果不延迟执行,那么一开始获取到的 form.values 的值是旧的,会导致详情区块的联动规则出现一些问题
.forEach((v) => { setTimeout(() => {
v.actions?.forEach((h) => { form.addEffects(id, () => {
linkageAction( forEachLinkageRule(linkageRules, (action, rule) => {
{ disposes.push(
operator: h.operator, reaction(
field, () => {
condition: v.condition, // 获取条件中的变量值
variables, const variableValuesInCondition = getVariableValuesInCondition({ linkageRules, localVariables });
localVariables, const result = [variableValuesInCondition].map((item) => JSON.stringify(item)).join(',');
conditionType: v.conditionType, return result;
}, },
app.jsonLogic, () => {
console.log(action);
field.stateOfLinkageRules = {};
linkageAction(
{
operator: action.operator,
field,
condition: rule.condition,
variables,
localVariables,
conditionType: rule.conditionType,
},
app.jsonLogic,
);
},
{ fireImmediately: true, equals: isEqual },
),
); );
}); });
}); });
}, [field, linkageRules, localVariables, variables]); });
return () => {
form.removeEffects(id);
disposes.forEach((dispose) => {
dispose();
});
};
}, [linkageRules]);
const handleMouseEnter = useCallback( const handleMouseEnter = useCallback(
(e) => { (e) => {
@ -162,38 +191,36 @@ export const Action: ComposedAction = withDynamicSchemaProps(
}, [onClick, fieldSchema, getAllDataBlocks]); }, [onClick, fieldSchema, getAllDataBlocks]);
return ( return (
<BlockContext.Provider value={{ name: 'action' }}> <InternalAction
<InternalAction containerRefKey={containerRefKey}
containerRefKey={containerRefKey} fieldSchema={fieldSchema}
fieldSchema={fieldSchema} designable={designable}
designable={designable} field={field}
field={field} icon={icon}
icon={icon} loading={loading}
loading={loading} handleMouseEnter={handleMouseEnter}
handleMouseEnter={handleMouseEnter} tarComponent={tarComponent}
tarComponent={tarComponent} className={className}
className={className} type={props.type}
type={props.type} Designer={Designer}
Designer={Designer} onClick={handleClick}
onClick={handleClick} confirm={confirm}
confirm={confirm} confirmTitle={confirmTitle}
confirmTitle={confirmTitle} popover={popover}
popover={popover} addChild={addChild}
addChild={addChild} recordData={recordData}
recordData={recordData} title={title}
title={title} style={style}
style={style} propsDisabled={propsDisabled}
propsDisabled={propsDisabled} useAction={useAction}
useAction={useAction} visibleWithURL={visibleWithURL}
visibleWithURL={visibleWithURL} setVisibleWithURL={setVisibleWithURL}
setVisibleWithURL={setVisibleWithURL} setSubmitted={setSubmitted}
setSubmitted={setSubmitted} getAriaLabel={getAriaLabel}
getAriaLabel={getAriaLabel} parentRecordData={parentRecordData}
parentRecordData={parentRecordData} actionCallback={actionCallback}
actionCallback={actionCallback} {...others}
{...others} />
/>
</BlockContext.Provider>
); );
}), }),
{ displayName: 'Action' }, { displayName: 'Action' },