feat: support model shared context

This commit is contained in:
gchust 2025-06-18 15:05:42 +08:00
parent 5db5960c9a
commit a2328d95f4
2 changed files with 95 additions and 32 deletions

View File

@ -5,21 +5,30 @@
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*
*/
/**
* 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.
*
* @example
* // 基本使用
* <FlowModelRenderer model={myModel} />
*
*
* // 显示设置但隐藏删除按钮
* <FlowModelRenderer
* model={myModel}
* <FlowModelRenderer
* model={myModel}
* showFlowSettings={true}
* hideRemoveInSettings={true}
* />
*
*
* // 使用右键菜单模式并隐藏删除按钮
* <FlowModelRenderer
* model={myModel}
* <FlowModelRenderer
* model={myModel}
* showFlowSettings={true}
* flowSettingsVariant="contextMenu"
* hideRemoveInSettings={true}
@ -27,7 +36,7 @@
*/
import { observer } from '@formily/reactive-react';
import React, { Suspense } from 'react';
import React, { Suspense, useEffect } from 'react';
import { useApplyAutoFlows, useFlowExtraContext } from '../hooks';
import { FlowModel } from '../models';
import { FlowsContextMenu } from './settings/wrappers/contextual/FlowsContextMenu';
@ -51,7 +60,10 @@ interface FlowModelRendererProps {
skipApplyAutoFlows?: boolean; // 默认 false
/** 当 skipApplyAutoFlows !== false 时,传递给 useApplyAutoFlows 的额外上下文 */
extraContext?: Record<string, any>
extraContext?: Record<string, any>;
/** Model 共享运行上下文,会沿着 model 树向下传递 */
sharedContext?: Record<string, any>;
/** 是否为每个组件独立执行 auto flow默认 false */
independentAutoFlowExecution?: boolean; // 默认 false
@ -66,20 +78,34 @@ const FlowModelRendererWithAutoFlows: React.FC<{
flowSettingsVariant: string;
hideRemoveInSettings: boolean;
extraContext?: Record<string, any>;
sharedContext?: Record<string, any>;
independentAutoFlowExecution?: boolean;
}> = observer(({ model, showFlowSettings, flowSettingsVariant, hideRemoveInSettings, extraContext, independentAutoFlowExecution }) => {
const defaultExtraContext = useFlowExtraContext();
useApplyAutoFlows(model, extraContext || defaultExtraContext, !independentAutoFlowExecution);
}> = observer(
({
model,
showFlowSettings,
flowSettingsVariant,
hideRemoveInSettings,
extraContext,
sharedContext,
independentAutoFlowExecution,
}) => {
const defaultExtraContext = useFlowExtraContext();
useApplyAutoFlows(model, extraContext || defaultExtraContext, !independentAutoFlowExecution);
useEffect(() => {
model.setSharedContext(sharedContext);
}, [sharedContext]);
return (
<FlowModelRendererCore
model={model}
showFlowSettings={showFlowSettings}
flowSettingsVariant={flowSettingsVariant}
hideRemoveInSettings={hideRemoveInSettings}
/>
);
});
return (
<FlowModelRendererCore
model={model}
showFlowSettings={showFlowSettings}
flowSettingsVariant={flowSettingsVariant}
hideRemoveInSettings={hideRemoveInSettings}
/>
);
},
);
/**
* useApplyAutoFlows
@ -89,7 +115,12 @@ const FlowModelRendererWithoutAutoFlows: React.FC<{
showFlowSettings: boolean;
flowSettingsVariant: string;
hideRemoveInSettings: boolean;
}> = observer(({ model, showFlowSettings, flowSettingsVariant, hideRemoveInSettings }) => {
sharedContext?: Record<string, any>;
}> = observer(({ model, showFlowSettings, flowSettingsVariant, hideRemoveInSettings, sharedContext }) => {
useEffect(() => {
model.setSharedContext(sharedContext);
}, [sharedContext]);
return (
<FlowModelRendererCore
model={model}
@ -120,10 +151,18 @@ const FlowModelRendererCore: React.FC<{
// 根据 flowSettingsVariant 包装相应的设置组件
switch (flowSettingsVariant) {
case 'dropdown':
return <FlowsFloatContextMenu model={model} showDeleteButton={!hideRemoveInSettings}>{modelContent}</FlowsFloatContextMenu>;
return (
<FlowsFloatContextMenu model={model} showDeleteButton={!hideRemoveInSettings}>
{modelContent}
</FlowsFloatContextMenu>
);
case 'contextMenu':
return <FlowsContextMenu model={model} showDeleteButton={!hideRemoveInSettings}>{modelContent}</FlowsContextMenu>;
return (
<FlowsContextMenu model={model} showDeleteButton={!hideRemoveInSettings}>
{modelContent}
</FlowsContextMenu>
);
case 'modal':
// TODO: 实现 modal 模式的流程设置
@ -136,10 +175,12 @@ const FlowModelRendererCore: React.FC<{
return modelContent;
default:
console.warn(
`FlowModelRenderer: Unknown flowSettingsVariant '${flowSettingsVariant}', falling back to dropdown`,
console.warn(`FlowModelRenderer: Unknown flowSettingsVariant '${flowSettingsVariant}', falling back to dropdown`);
return (
<FlowsFloatContextMenu model={model} showDeleteButton={!hideRemoveInSettings}>
{modelContent}
</FlowsFloatContextMenu>
);
return <FlowsFloatContextMenu model={model} showDeleteButton={!hideRemoveInSettings}>{modelContent}</FlowsFloatContextMenu>;
}
});
@ -155,17 +196,19 @@ const FlowModelRendererCore: React.FC<{
* @param {boolean} props.hideRemoveInSettings - Whether to hide remove button in settings.
* @param {boolean} props.skipApplyAutoFlows - Whether to skip applying auto flows.
* @param {any} props.extraContext - Extra context to pass to useApplyAutoFlows when skipApplyAutoFlows is false.
* @param {any} props.sharedContext - Shared context to pass to the model.
* @param {boolean} props.independentAutoFlowExecution - Whether each component has independent auto flow execution.
* @returns {React.ReactNode | null} The rendered output of the model, or null if the model or its render method is invalid.
*/
export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
({
model,
({
model,
showFlowSettings = false,
flowSettingsVariant = 'dropdown',
hideRemoveInSettings = false,
skipApplyAutoFlows = false,
extraContext,
sharedContext,
independentAutoFlowExecution = false,
}) => {
if (!model || typeof model.render !== 'function') {
@ -183,6 +226,7 @@ export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
showFlowSettings={showFlowSettings}
flowSettingsVariant={flowSettingsVariant}
hideRemoveInSettings={hideRemoveInSettings}
sharedContext={sharedContext}
/>
</Suspense>
);
@ -195,6 +239,7 @@ export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
flowSettingsVariant={flowSettingsVariant}
hideRemoveInSettings={hideRemoveInSettings}
extraContext={extraContext}
sharedContext={sharedContext}
independentAutoFlowExecution={independentAutoFlowExecution}
/>
</Suspense>

View File

@ -57,7 +57,26 @@ export class FlowModel<Structure extends { parent?: any; subModels?: any } = Def
* key fork fork
*/
private forkCache: Map<string, ForkFlowModel<any>> = new Map();
// public static meta: FlowModelMeta;
// model 树的共享运行上下文
private _sharedContext: Record<string, any> = {};
public setSharedContext(ctx: Record<string, any>) {
this._sharedContext = ctx;
}
public getSharedContext() {
const parentProto = Object.getPrototypeOf(Object.getPrototypeOf(this));
let parentContext = {};
if (parentProto && typeof parentProto.getSharedContext === 'function') {
parentContext = parentProto.getSharedContext();
}
return {
...parentContext,
...this._sharedContext, // 当前实例的 context 优先级最高
};
}
constructor(protected options: FlowModelOptions<Structure>) {
if (options?.flowEngine?.getModel(options.uid)) {
@ -345,7 +364,6 @@ export class FlowModel<Structure extends { parent?: any; subModels?: any } = Def
let lastResult: any;
let exited = false;
const stepResults: Record<string, any> = {};
const shared: Record<string, any> = {};
// Create a new FlowContext instance for this flow execution
const createLogger = (level: string) => (message: string, meta?: any) => {
@ -367,7 +385,7 @@ export class FlowModel<Structure extends { parent?: any; subModels?: any } = Def
debug: createLogger('DEBUG'),
},
stepResults,
shared,
shared: this.getSharedContext(),
globals: globalContexts,
extra: extra || {},
model: this,