/** * 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 { createForm } from '@formily/core'; import { createSchemaField, FormProvider, ISchema } from '@formily/react'; import { autorun, toJS } from '@formily/reactive'; import { Button, Space } from 'antd'; import React, { useEffect } from 'react'; import { FlowSettingsContextProvider, useFlowSettingsContext } from '../../../../hooks/useFlowSettingsContext'; import { StepSettingsDialogProps } from '../../../../types'; import { compileUiSchema, FlowExitException, getT, resolveDefaultParams, resolveStepUiSchema, FlowCancelSaveException, } from '../../../../utils'; import { FlowExitAllException } from '../../../../utils/exceptions'; import { observer } from '../../../../reactive'; const SchemaField = createSchemaField(); /** * StepSettingsDialog组件 - 使用 FormDialog 显示单个步骤的配置界面 * @param props.model 模型实例 * @param props.flowKey 流程Key * @param props.stepKey 步骤Key * @param props.dialogWidth 对话框宽度,默认为600 * @param props.dialogTitle 自定义对话框标题,默认使用step的title * @returns Promise 返回表单提交的值 */ const openStepSettingsDialog = async ({ model, flowKey, stepKey, dialogWidth = 600, dialogTitle, mode = 'dialog', ctx, uiModeProps, cleanup, }: StepSettingsDialogProps): Promise => { const t = getT(model); const message = model.context.message; if (!model) { message.error(t('Invalid model provided')); throw new Error(t('Invalid model provided')); } // 获取流程和步骤信息 const flow = model.getFlow(flowKey); const step = flow?.steps?.[stepKey]; if (!flow) { message.error(t('Flow with key {{flowKey}} not found', { flowKey })); throw new Error(t('Flow with key {{flowKey}} not found', { flowKey })); } if (!step) { message.error(t('Step with key {{stepKey}} not found', { stepKey })); throw new Error(t('Step with key {{stepKey}} not found', { stepKey })); } let title = step.title; let beforeParamsSave = step.beforeParamsSave; let afterParamsSave = step.afterParamsSave; let actionDefaultParams = {}; if (step.use) { const action = model.flowEngine?.getAction?.(step.use); if (action) { actionDefaultParams = action.defaultParams || {}; title = title || action.title; beforeParamsSave = beforeParamsSave || action.beforeParamsSave; afterParamsSave = afterParamsSave || action.afterParamsSave; } } // 获取流程定义 const flowDefinition = model.getFlow(flowKey); const mergedUiSchema = await resolveStepUiSchema(model, flowDefinition, step); // 如果没有可配置的UI Schema,显示提示 if (!mergedUiSchema) { message.info(t('This step has no configurable parameters')); return {}; } const flowRuntimeContext = ctx; // 确保上下文中设置了当前步骤 if (!flowRuntimeContext.currentStep || flowRuntimeContext.currentStep !== step) { flowRuntimeContext.defineProperty('currentStep', { value: step }); } const flowEngine = model.flowEngine; const scopes = { useFlowSettingsContext, ...flowEngine.flowSettings?.scopes, }; // 获取初始值 const stepParams = model.getStepParams(flowKey, stepKey) || {}; // 解析 defaultParams const resolvedDefaultParams = await resolveDefaultParams(step.defaultParams, flowRuntimeContext); const resolveActionDefaultParams = await resolveDefaultParams(actionDefaultParams, flowRuntimeContext); const initialValues = { ...toJS(resolveActionDefaultParams), ...toJS(resolvedDefaultParams), ...toJS(stepParams) }; // 保存旧参数用于 onParamsChange 回调 const previousParams = { ...toJS(stepParams) }; // 构建表单Schema const formSchema: ISchema = { type: 'object', properties: { layout: { type: 'void', 'x-component': 'FormLayout', 'x-component-props': { layout: 'vertical', // 垂直布局 }, properties: mergedUiSchema, }, }, }; const openView = model.context.viewer[mode].bind(model.context.viewer); const resolvedUiModeProps = toJS(uiModeProps) || {}; const { zIndex: uiModeZIndex, ...restUiModeProps } = resolvedUiModeProps; const resolveDialogZIndex = (rawZIndex?: number) => { const nextZIndex = typeof model.context.viewer?.getNextZIndex === 'function' ? model.context.viewer.getNextZIndex() : (model.context.themeToken?.zIndexPopupBase || 1000) + 1; const inputZIndex = Number(rawZIndex) || 0; return Math.max(nextZIndex, inputZIndex); }; const mergedZIndex = resolveDialogZIndex(uiModeZIndex); const form = createForm({ initialValues: compileUiSchema(scopes, initialValues), }); const viewInputArgs = model.context.view?.inputArgs || {}; const navigation = ctx?.view?.navigation ?? model.context.view?.navigation; const inputArgs = { ...viewInputArgs, ...(navigation ? { navigation } : {}), ...(toJS(uiModeProps)?.inputArgs || {}), __isSettingsPopup: true, }; openView({ title: dialogTitle || t(title), width: dialogWidth, destroyOnClose: true, ...restUiModeProps, zIndex: mergedZIndex, // 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗 inputArgs, onClose: () => { if (cleanup) { cleanup(); } }, content: (currentDialog) => { const FlowStepSettings = observer(() => { useEffect(() => { return autorun(() => { const dynamicProps = toJS(uiModeProps); const { zIndex, ...restDynamicProps } = dynamicProps || {}; currentDialog.update({ ...restDynamicProps, zIndex: resolveDialogZIndex(zIndex), }); }); }, []); const compiledFormSchema = compileUiSchema(scopes, formSchema); return ( ); }); return ; }, }); }; export { openStepSettingsDialog };