@nocobase/flow-engine
Version:
A standalone flow engine for NocoBase, managing workflows, models, and actions.
343 lines (341 loc) • 14.5 kB
JavaScript
/**
* 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.
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var StepRequiredSettingsDialog_exports = {};
__export(StepRequiredSettingsDialog_exports, {
openRequiredParamsStepFormDialog: () => openRequiredParamsStepFormDialog
});
module.exports = __toCommonJS(StepRequiredSettingsDialog_exports);
var import_react = require("@formily/react");
var import_reactive = require("@formily/reactive");
var import_antd = require("antd");
var import_react2 = __toESM(require("react"));
var import_flowContext = require("../../../../flowContext");
var import_useFlowSettingsContext = require("../../../../hooks/useFlowSettingsContext");
var import_utils = require("../../../../utils");
function hasRequiredParams(uiSchema, currentParams) {
for (const [fieldKey, fieldSchema] of Object.entries(uiSchema)) {
if (fieldSchema.required === true) {
const value = currentParams[fieldKey];
if (value === void 0 || value === null || value === "") {
return false;
}
}
}
return true;
}
__name(hasRequiredParams, "hasRequiredParams");
const SchemaField = (0, import_react.createSchemaField)();
const MultiStepContextProvider = /* @__PURE__ */ __name(({
model,
requiredSteps,
formStep,
children
}) => {
const currentStepIndex = (formStep == null ? void 0 : formStep.current) ?? 0;
const currentStepInfo = requiredSteps[currentStepIndex];
const flowRuntimeContext = import_react2.default.useMemo(() => {
const { flowKey, step } = currentStepInfo;
const flow = model.getFlow(flowKey);
const ctx = new import_flowContext.FlowRuntimeContext(model, flowKey, "settings");
(0, import_utils.setupRuntimeContextSteps)(ctx, (flow == null ? void 0 : flow.steps) || {}, model, flowKey);
ctx.defineProperty("currentStep", { value: step });
return ctx;
}, [model, currentStepInfo]);
return /* @__PURE__ */ import_react2.default.createElement(import_useFlowSettingsContext.FlowSettingsContextProvider, { value: flowRuntimeContext }, children);
}, "MultiStepContextProvider");
const openRequiredParamsStepFormDialog = /* @__PURE__ */ __name(async ({
model,
dialogWidth = 800,
dialogTitle
}) => {
const t = (0, import_utils.getT)(model);
const defaultTitle = dialogTitle || t("Step parameter configuration");
if (!model) {
import_antd.message.error(t("Invalid model provided"));
throw new Error(t("Invalid model provided"));
}
return new Promise((resolve, reject) => {
(async () => {
var _a, _b, _c;
try {
const allFlows = model.getFlows();
const requiredSteps = [];
for (const [flowKey, flow] of allFlows) {
for (const stepKey in flow.steps) {
const step = flow.steps[stepKey];
if (step.paramsRequired || step.preset) {
const mergedUiSchema = await (0, import_utils.resolveStepUiSchema)(model, flow, step);
if (mergedUiSchema) {
const currentStepParams = model.getStepParams(flowKey, stepKey) || {};
const hasAllRequiredParams = false;
console.log(`Flow: ${flowKey}, Step: ${stepKey}, hasAllRequiredParams:`, hasAllRequiredParams);
if (!hasAllRequiredParams) {
requiredSteps.push({
flowKey,
stepKey,
step,
uiSchema: mergedUiSchema,
title: step.title || stepKey,
flowTitle: flow.title || flowKey
});
}
}
}
}
}
console.log("Required steps:", requiredSteps);
if (requiredSteps.length === 0) {
resolve({});
return;
}
const initialValues = {};
for (const { flowKey, stepKey, step } of requiredSteps) {
const stepParams = model.getStepParams(flowKey, stepKey) || {};
let actionDefaultParams = {};
if (step.use) {
const action = (_b = (_a = model.flowEngine) == null ? void 0 : _a.getAction) == null ? void 0 : _b.call(_a, step.use);
actionDefaultParams = action.defaultParams || {};
}
const flowRuntimeContext = new import_flowContext.FlowRuntimeContext(model, flowKey, "settings");
const flow = model.getFlow(flowKey);
if (flow) {
(0, import_utils.setupRuntimeContextSteps)(flowRuntimeContext, flow.steps, model, flowKey);
}
flowRuntimeContext.defineProperty("currentStep", { value: step });
const resolvedActionDefaultParams = await (0, import_utils.resolveDefaultParams)(actionDefaultParams, flowRuntimeContext);
const resolvedDefaultParams = await (0, import_utils.resolveDefaultParams)(step.defaultParams, flowRuntimeContext);
const mergedParams = {
...(0, import_reactive.toJS)(resolvedActionDefaultParams),
...(0, import_reactive.toJS)(resolvedDefaultParams),
...(0, import_reactive.toJS)(stepParams)
};
if (Object.keys(mergedParams).length > 0) {
if (!initialValues[flowKey]) {
initialValues[flowKey] = {};
}
initialValues[flowKey][stepKey] = mergedParams;
}
}
const stepPanes = {};
requiredSteps.forEach(({ flowKey, stepKey, uiSchema, title, flowTitle }) => {
const stepId = `${flowKey}_${stepKey}`;
stepPanes[stepId] = {
type: "void",
"x-component": "FormStep.StepPane",
"x-component-props": {
title: `${title}`,
description: `Flow: ${flowTitle}`
},
properties: {
layout: {
type: "void",
"x-component": "FormLayout",
"x-component-props": {
layout: "vertical"
},
properties: {
[flowKey]: {
type: "object",
properties: {
[stepKey]: {
type: "object",
properties: uiSchema
}
}
}
}
}
}
};
});
const formSchema = requiredSteps.length === 1 ? {
type: "object",
properties: {
// 直接渲染单个步骤的内容,不显示步骤指示器
...Object.values(stepPanes)[0].properties
}
} : {
type: "object",
properties: {
step: {
type: "void",
"x-component": "FormStep",
"x-component-props": {
formStep: "{{formStep}}"
},
properties: stepPanes
}
}
};
const { FormDialog, FormStep } = await import("@formily/antd-v5");
const formStep = requiredSteps.length > 1 ? FormStep.createFormStep(0) : null;
const flowEngine = model.flowEngine;
const scopes = {
formStep,
totalSteps: requiredSteps.length,
requiredSteps,
useFlowSettingsContext: import_useFlowSettingsContext.useFlowSettingsContext,
...(_c = flowEngine.flowSettings) == null ? void 0 : _c.scopes
};
const formDialog = FormDialog(
{
title: dialogTitle || t("Step parameter configuration"),
width: dialogWidth,
footer: null,
// 移除默认的底部按钮,使用自定义的导航按钮
destroyOnClose: true
},
(form) => {
var _a2;
const handleSubmit = /* @__PURE__ */ __name(async () => {
try {
await form.submit();
const currentValues = form.values;
requiredSteps.forEach(({ flowKey, stepKey }) => {
var _a3;
const stepValues = (_a3 = currentValues[flowKey]) == null ? void 0 : _a3[stepKey];
if (stepValues) {
model.setStepParams(flowKey, stepKey, stepValues);
}
});
await model.saveStepParams();
resolve(currentValues);
formDialog.close();
} catch (error) {
console.error(t("Error submitting form"), ":", error);
}
}, "handleSubmit");
const handleClose = /* @__PURE__ */ __name(() => {
formDialog.close();
resolve({});
}, "handleClose");
const dialogScopes = {
...scopes,
closeDialog: handleClose,
handleNext: /* @__PURE__ */ __name(() => {
form.validate().then(() => {
if (formStep) {
formStep.next();
}
}).catch((errors) => {
console.log(t("Form validation failed"), ":", errors);
});
}, "handleNext")
};
const compiledFormSchema = (0, import_utils.compileUiSchema)(dialogScopes, formSchema);
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(MultiStepContextProvider, { model, requiredSteps, formStep }, /* @__PURE__ */ import_react2.default.createElement(
SchemaField,
{
schema: compiledFormSchema,
components: {
FormStep,
...(_a2 = flowEngine.flowSettings) == null ? void 0 : _a2.components
},
scope: dialogScopes
}
)), /* @__PURE__ */ import_react2.default.createElement(import_react.FormConsumer, null, () => /* @__PURE__ */ import_react2.default.createElement(
"div",
{
style: {
display: "flex",
justifyContent: "flex-end",
gap: 8,
marginTop: 24,
paddingTop: 16,
borderTop: "1px solid #f0f0f0"
}
},
requiredSteps.length === 1 ? /* @__PURE__ */ import_react2.default.createElement(import_antd.Button, { type: "primary", onClick: handleSubmit }, t("Complete configuration")) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(
import_antd.Button,
{
disabled: !(formStep == null ? void 0 : formStep.allowBack),
onClick: () => {
if (formStep) {
formStep.back();
}
}
},
t("Previous step")
), /* @__PURE__ */ import_react2.default.createElement(
import_antd.Button,
{
disabled: !(formStep == null ? void 0 : formStep.allowNext),
type: "primary",
onClick: () => {
form.validate().then(() => {
if (formStep) {
formStep.next();
}
}).catch((errors) => {
console.log(t("Form validation failed"), ":", errors);
});
},
style: {
display: ((formStep == null ? void 0 : formStep.current) ?? 0) < requiredSteps.length - 1 ? "inline-block" : "none"
}
},
t("Next step")
), /* @__PURE__ */ import_react2.default.createElement(
import_antd.Button,
{
disabled: formStep == null ? void 0 : formStep.allowNext,
type: "primary",
onClick: handleSubmit,
style: {
display: ((formStep == null ? void 0 : formStep.current) ?? 0) >= requiredSteps.length - 1 ? "inline-block" : "none"
}
},
t("Complete configuration")
))
)));
}
);
formDialog.open({
initialValues: (0, import_utils.compileUiSchema)(scopes, initialValues)
});
} catch (error) {
reject(new Error(`${t("Failed to import FormDialog or FormStep")}: ${error.message}`));
}
})();
}).catch((e) => {
console.error(e);
});
}, "openRequiredParamsStepFormDialog");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
openRequiredParamsStepFormDialog
});