/**
* 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 { Alert, Form, Input, InputNumber, Select, Switch } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import { FlowRuntimeContext } from '../../../../flowContext';
import { useFlowModelById } from '../../../../hooks';
import { FlowModel } from '../../../../models';
import { resolveDefaultParams, setupRuntimeContextSteps, shouldHideStepInSettings } from '../../../../utils';
import { observer } from '../../../../reactive';
const { Item: FormItem } = Form;
// 创建两个组件版本,一个使用props传递的model,一个使用hook获取model
interface ModelProvidedProps {
model: FlowModel;
flowKey: string;
}
interface ModelByIdProps {
uid: string;
flowKey: string;
modelClassName: string;
}
type FlowSettingsProps = ModelProvidedProps | ModelByIdProps;
// 判断是否是通过ID获取模型的props
const isModelByIdProps = (props: FlowSettingsProps): props is ModelByIdProps => {
return 'uid' in props && 'modelClassName' in props && Boolean(props.uid) && Boolean(props.modelClassName);
};
/**
* FlowSettings组件 - 单个流程的详细配置表单(embedded版本)
*
* 特点:
* - 实时保存到model
* - 适用于嵌入式配置界面
*
* 支持两种使用方式:
* 1. 直接提供model:
* 2. 通过uid和modelClassName获取model:
*/
const FlowSettings: React.FC = (props) => {
if (isModelByIdProps(props)) {
return ;
} else {
return ;
}
};
// 使用传入的model
const FlowSettingsWithModel: React.FC = observer(({ model, flowKey }) => {
if (!model) {
return ;
}
return ;
});
// 通过useModelById hook获取model
const FlowSettingsWithModelById: React.FC = observer(({ uid, flowKey, modelClassName }) => {
const model = useFlowModelById(uid, modelClassName);
if (!model) {
return ;
}
return ;
});
// 提取核心渲染逻辑到一个共享组件
interface FlowSettingsContentProps {
model: FlowModel;
flowKey: string;
}
const FlowSettingsContent: React.FC = observer(({ model, flowKey }) => {
const [form] = Form.useForm();
// 获取流程定义
const flows = model.getFlows();
const flow = flows.get(flowKey);
const [configurableSteps, setConfigurableSteps] = useState<{ stepKey: string; step: any; uiSchema: any }[]>([]);
// 获取可配置的步骤(支持动态 hideInSettings)
useEffect(() => {
let mounted = true;
const buildConfigurableSteps = async () => {
const steps: { stepKey: string; step: any; uiSchema: any }[] = [];
for (const [stepKey, actionStep] of Object.entries(flow?.steps || {})) {
if (await shouldHideStepInSettings(model, flow, actionStep as any)) {
continue;
}
// 从step获取uiSchema(如果存在)
const stepUiSchema = actionStep.uiSchema || {};
// 如果step使用了action,也获取action的uiSchema
let actionUiSchema = {};
if (actionStep.use) {
const action = model.flowEngine?.getAction?.(actionStep.use);
if (action && action.uiSchema) {
actionUiSchema = action.uiSchema;
}
}
// 合并uiSchema,确保step的uiSchema优先级更高
const mergedUiSchema = { ...actionUiSchema };
// 将stepUiSchema中的字段合并到mergedUiSchema
Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
if (mergedUiSchema[fieldKey]) {
mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
} else {
mergedUiSchema[fieldKey] = schema;
}
});
// 如果没有可配置的UI Schema,跳过
if (Object.keys(mergedUiSchema).length === 0) {
continue;
}
steps.push({ stepKey, step: actionStep, uiSchema: mergedUiSchema });
}
if (mounted) {
setConfigurableSteps(steps);
}
};
buildConfigurableSteps();
return () => {
mounted = false;
};
}, [model, flow]);
// 获取当前流程的参数 - 从model中获取实际参数
const getCurrentParams = useCallback(async () => {
const params = {};
// 从model中获取每个步骤的参数,如果为空则使用默认参数
for (const { stepKey, step } of configurableSteps) {
const stepParams = model.getStepParams(flowKey, stepKey) || {};
const flowRuntimeContext = new FlowRuntimeContext(model, flowKey, 'settings');
const flow = model.getFlow(flowKey);
setupRuntimeContextSteps(flowRuntimeContext, flow?.steps || {}, model, flowKey);
// 解析 defaultParams
const resolvedDefaultParams = await resolveDefaultParams(step.defaultParams, flowRuntimeContext);
// 合并默认参数和当前参数,当前参数优先
const mergedParams = { ...resolvedDefaultParams, ...stepParams };
if (Object.keys(mergedParams).length > 0) {
params[stepKey] = mergedParams;
}
}
return params;
}, [model, flowKey, configurableSteps]);
// 初始化表单值
useEffect(() => {
const loadParams = async () => {
try {
const currentParams = await getCurrentParams();
form.setFieldsValue(currentParams);
} catch (error) {
console.error('Error loading default params:', error);
}
};
loadParams();
}, [flowKey, form, getCurrentParams]);
// 处理表单值变化 - 实时保存
const handleValuesChange = useCallback(
(changedValues: any, allValues: any) => {
// 实时保存到model
Object.entries(allValues).forEach(([stepKey, stepValues]: [string, any]) => {
if (stepValues && typeof stepValues === 'object') {
// 使用setStepParams保存步骤参数
model.setStepParams(flowKey, stepKey, stepValues);
}
});
},
[model, flowKey],
);
if (!flow) {
return ;
}
if (configurableSteps.length === 0) {
return ;
}
// 渲染表单字段
const renderFormFields = () => {
return configurableSteps.map(({ stepKey, uiSchema }) => {
return Object.entries(uiSchema).map(([fieldKey, schema]: [string, any]) => {
const fieldName = `${stepKey}.${fieldKey}`;
// 根据schema类型渲染不同的组件
const renderField = () => {
switch (schema['x-component']) {
case 'Select':
return (
);
case 'InputNumber':
return (
);
case 'Switch':
return ;
case 'Input.TextArea':
return (
);
default:
return (
);
}
};
return (
{renderField()}
);
});
});
};
return (
);
});
export { FlowSettings };