@nocobase/flow-engine 2.0.0-beta.21 → 2.0.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/FlowDefinition.d.ts +2 -0
- package/lib/JSRunner.js +23 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +66 -13
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
- package/lib/components/variables/VariableInput.js +1 -2
- package/lib/components/variables/VariableTag.js +46 -39
- package/lib/data-source/index.js +11 -5
- package/lib/flowContext.js +5 -1
- package/lib/flowI18n.js +6 -4
- package/lib/locale/en-US.json +2 -1
- package/lib/locale/index.d.ts +2 -0
- package/lib/locale/zh-CN.json +1 -0
- package/lib/resources/sqlResource.d.ts +3 -3
- package/lib/types.d.ts +12 -0
- package/lib/utils/associationObjectVariable.d.ts +2 -2
- package/lib/utils/index.d.ts +4 -2
- package/lib/utils/index.js +16 -0
- package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
- package/lib/utils/resolveRunJSObjectValues.js +61 -0
- package/lib/utils/runjsValue.d.ts +29 -0
- package/lib/utils/runjsValue.js +275 -0
- package/lib/utils/safeGlobals.d.ts +14 -0
- package/lib/utils/safeGlobals.js +37 -2
- package/lib/utils/schema-utils.d.ts +10 -0
- package/lib/utils/schema-utils.js +61 -0
- package/package.json +4 -4
- package/src/JSRunner.ts +29 -1
- package/src/__tests__/JSRunner.test.ts +64 -0
- package/src/__tests__/flowContext.test.ts +78 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +99 -14
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +94 -1
- package/src/components/variables/VariableInput.tsx +4 -2
- package/src/components/variables/VariableTag.tsx +54 -45
- package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
- package/src/data-source/index.ts +11 -5
- package/src/flowContext.ts +6 -2
- package/src/flowI18n.ts +7 -5
- package/src/locale/en-US.json +2 -1
- package/src/locale/zh-CN.json +1 -0
- package/src/resources/sqlResource.ts +3 -3
- package/src/types.ts +12 -0
- package/src/utils/__tests__/runjsValue.test.ts +44 -0
- package/src/utils/__tests__/safeGlobals.test.ts +8 -0
- package/src/utils/__tests__/utils.test.ts +95 -0
- package/src/utils/associationObjectVariable.ts +2 -2
- package/src/utils/index.ts +20 -2
- package/src/utils/resolveRunJSObjectValues.ts +46 -0
- package/src/utils/runjsValue.ts +287 -0
- package/src/utils/safeGlobals.ts +55 -1
- package/src/utils/schema-utils.ts +79 -0
package/lib/FlowDefinition.d.ts
CHANGED
|
@@ -419,6 +419,8 @@ export declare class FlowStep {
|
|
|
419
419
|
scene?: import("./types").ActionScene | import("./types").ActionScene[];
|
|
420
420
|
paramsRequired?: boolean;
|
|
421
421
|
hideInSettings?: boolean | ((ctx: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>) => boolean | Promise<boolean>);
|
|
422
|
+
disabledInSettings?: boolean | ((ctx: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>) => boolean | Promise<boolean>);
|
|
423
|
+
disabledReasonInSettings?: string | ((ctx: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>) => string | Promise<string>);
|
|
422
424
|
defineProperties?: Record<string, import("./flowContext").PropertyOptions> | ((ctx: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>) => Record<string, import("./flowContext").PropertyOptions> | Promise<Record<string, import("./flowContext").PropertyOptions>>);
|
|
423
425
|
defineMethods?: Record<string, (this: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>, ...args: any[]) => any> | ((ctx: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>) => Record<string, (this: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>, ...args: any[]) => any> | Promise<Record<string, (this: import("./flowContext").FlowRuntimeContext<import(".").FlowModel<import("./types").DefaultStructure>, any>, ...args: any[]) => any>>);
|
|
424
426
|
};
|
package/lib/JSRunner.js
CHANGED
|
@@ -35,6 +35,7 @@ const _JSRunner = class _JSRunner {
|
|
|
35
35
|
globals;
|
|
36
36
|
timeoutMs;
|
|
37
37
|
constructor(options = {}) {
|
|
38
|
+
var _a, _b;
|
|
38
39
|
const bindWindowFn = /* @__PURE__ */ __name((key) => {
|
|
39
40
|
if (typeof window !== "undefined" && typeof window[key] === "function") {
|
|
40
41
|
return window[key].bind(window);
|
|
@@ -42,13 +43,34 @@ const _JSRunner = class _JSRunner {
|
|
|
42
43
|
const fn = globalThis[key];
|
|
43
44
|
return typeof fn === "function" ? fn.bind(globalThis) : fn;
|
|
44
45
|
}, "bindWindowFn");
|
|
46
|
+
const providedGlobals = options.globals || {};
|
|
47
|
+
const liftedGlobals = {};
|
|
48
|
+
if (!Object.prototype.hasOwnProperty.call(providedGlobals, "Blob")) {
|
|
49
|
+
try {
|
|
50
|
+
const blobCtor = (_a = providedGlobals.window) == null ? void 0 : _a.Blob;
|
|
51
|
+
if (typeof blobCtor !== "undefined") {
|
|
52
|
+
liftedGlobals.Blob = blobCtor;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!Object.prototype.hasOwnProperty.call(providedGlobals, "URL")) {
|
|
58
|
+
try {
|
|
59
|
+
const urlCtor = (_b = providedGlobals.window) == null ? void 0 : _b.URL;
|
|
60
|
+
if (typeof urlCtor !== "undefined") {
|
|
61
|
+
liftedGlobals.URL = urlCtor;
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
45
66
|
this.globals = {
|
|
46
67
|
console,
|
|
47
68
|
setTimeout: bindWindowFn("setTimeout"),
|
|
48
69
|
clearTimeout: bindWindowFn("clearTimeout"),
|
|
49
70
|
setInterval: bindWindowFn("setInterval"),
|
|
50
71
|
clearInterval: bindWindowFn("clearInterval"),
|
|
51
|
-
...
|
|
72
|
+
...liftedGlobals,
|
|
73
|
+
...providedGlobals
|
|
52
74
|
};
|
|
53
75
|
this.timeoutMs = options.timeoutMs ?? 5e3;
|
|
54
76
|
}
|
|
@@ -135,10 +135,19 @@ const componentMap = {
|
|
|
135
135
|
const MenuLabelItem = /* @__PURE__ */ __name(({ title, uiMode, itemProps }) => {
|
|
136
136
|
const type = (uiMode == null ? void 0 : uiMode.type) || uiMode;
|
|
137
137
|
const Component = type ? componentMap[type] : null;
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
const disabled = !!(itemProps == null ? void 0 : itemProps.disabled);
|
|
139
|
+
const disabledReason = itemProps == null ? void 0 : itemProps.disabledReason;
|
|
140
|
+
const disabledIconColor = itemProps == null ? void 0 : itemProps.disabledIconColor;
|
|
141
|
+
const content = (() => {
|
|
142
|
+
if (!Component) {
|
|
143
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, title);
|
|
144
|
+
}
|
|
145
|
+
return /* @__PURE__ */ import_react.default.createElement(Component, { title, ...itemProps });
|
|
146
|
+
})();
|
|
147
|
+
if (!disabled) {
|
|
148
|
+
return content;
|
|
140
149
|
}
|
|
141
|
-
return /* @__PURE__ */ import_react.default.createElement(
|
|
150
|
+
return /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 } }, content, /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: disabledReason, placement: "right", destroyTooltipOnHide: true }, /* @__PURE__ */ import_react.default.createElement(import_icons.QuestionCircleOutlined, { style: { color: disabledIconColor } })));
|
|
142
151
|
}, "MenuLabelItem");
|
|
143
152
|
const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
144
153
|
model,
|
|
@@ -150,9 +159,13 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
150
159
|
}) => {
|
|
151
160
|
const { message } = import_antd.App.useApp();
|
|
152
161
|
const t = (0, import_react.useMemo)(() => (0, import_utils.getT)(model), [model]);
|
|
162
|
+
const { token } = import_antd.theme.useToken();
|
|
163
|
+
const disabledIconColor = (token == null ? void 0 : token.colorTextTertiary) || (token == null ? void 0 : token.colorTextDescription) || (token == null ? void 0 : token.colorTextSecondary);
|
|
153
164
|
const [visible, setVisible] = (0, import_react.useState)(false);
|
|
154
165
|
const [refreshTick, setRefreshTick] = (0, import_react.useState)(0);
|
|
155
166
|
const [extraMenuItems, setExtraMenuItems] = (0, import_react.useState)([]);
|
|
167
|
+
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
|
|
168
|
+
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
156
169
|
const closeDropdown = (0, import_react.useCallback)(() => {
|
|
157
170
|
setVisible(false);
|
|
158
171
|
}, []);
|
|
@@ -203,7 +216,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
203
216
|
return () => {
|
|
204
217
|
mounted = false;
|
|
205
218
|
};
|
|
206
|
-
}, [model, menuLevels, t, refreshTick, visible
|
|
219
|
+
}, [model, menuLevels, t, refreshTick, visible]);
|
|
207
220
|
const copyUidToClipboard = (0, import_react.useCallback)(
|
|
208
221
|
async (uid) => {
|
|
209
222
|
var _a;
|
|
@@ -309,6 +322,28 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
309
322
|
},
|
|
310
323
|
[closeDropdown, model, t]
|
|
311
324
|
);
|
|
325
|
+
const isStepMenuItemDisabled = (0, import_react.useCallback)(
|
|
326
|
+
(key) => {
|
|
327
|
+
const cleanKey = key.includes("-") && /^(.+)-\d+$/.test(key) ? key.replace(/-\d+$/, "") : key;
|
|
328
|
+
const keys = cleanKey.split(":");
|
|
329
|
+
let modelKey;
|
|
330
|
+
let flowKey;
|
|
331
|
+
let stepKey;
|
|
332
|
+
if (keys.length === 3) {
|
|
333
|
+
[modelKey, flowKey, stepKey] = keys;
|
|
334
|
+
} else if (keys.length === 2) {
|
|
335
|
+
[flowKey, stepKey] = keys;
|
|
336
|
+
} else {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
return configurableFlowsAndSteps.some(({ flow, steps, modelKey: flowModelKey }) => {
|
|
340
|
+
const sameModel = (flowModelKey || void 0) === modelKey;
|
|
341
|
+
if (!sameModel || flow.key !== flowKey) return false;
|
|
342
|
+
return steps.some((stepInfo) => stepInfo.stepKey === stepKey && !!stepInfo.disabled);
|
|
343
|
+
});
|
|
344
|
+
},
|
|
345
|
+
[configurableFlowsAndSteps]
|
|
346
|
+
);
|
|
312
347
|
const handleMenuClick = (0, import_react.useCallback)(
|
|
313
348
|
({ key }) => {
|
|
314
349
|
const originalKey = key;
|
|
@@ -324,6 +359,9 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
324
359
|
extra.onClick();
|
|
325
360
|
return;
|
|
326
361
|
}
|
|
362
|
+
if (isStepMenuItemDisabled(cleanKey)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
327
365
|
switch (cleanKey) {
|
|
328
366
|
case "copy-uid":
|
|
329
367
|
closeDropdown();
|
|
@@ -337,7 +375,15 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
337
375
|
break;
|
|
338
376
|
}
|
|
339
377
|
},
|
|
340
|
-
[
|
|
378
|
+
[
|
|
379
|
+
closeDropdown,
|
|
380
|
+
handleCopyUid,
|
|
381
|
+
handleDelete,
|
|
382
|
+
handleStepConfiguration,
|
|
383
|
+
handleCopyPopupUid,
|
|
384
|
+
extraMenuItems,
|
|
385
|
+
isStepMenuItemDisabled
|
|
386
|
+
]
|
|
341
387
|
);
|
|
342
388
|
const getModelConfigurableFlowsAndSteps = (0, import_react.useCallback)(
|
|
343
389
|
async (targetModel, modelKey) => {
|
|
@@ -355,6 +401,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
355
401
|
if (await (0, import_utils.shouldHideStepInSettings)(targetModel, flow, actionStep)) {
|
|
356
402
|
return null;
|
|
357
403
|
}
|
|
404
|
+
const disabledState = await (0, import_utils.resolveStepDisabledInSettings)(targetModel, flow, actionStep);
|
|
358
405
|
let uiMode = await (0, import_utils.resolveUiMode)(actionStep.uiMode, targetModel.context);
|
|
359
406
|
const hasStepUiSchema = actionStep.uiSchema != null;
|
|
360
407
|
let hasActionUiSchema = false;
|
|
@@ -392,7 +439,9 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
392
439
|
title: t(stepTitle) || stepKey,
|
|
393
440
|
modelKey,
|
|
394
441
|
// 添加模型标识
|
|
395
|
-
uiMode
|
|
442
|
+
uiMode,
|
|
443
|
+
disabled: disabledState.disabled,
|
|
444
|
+
disabledReason: disabledState.reason
|
|
396
445
|
};
|
|
397
446
|
})
|
|
398
447
|
).then((steps) => steps.filter(Boolean));
|
|
@@ -423,8 +472,6 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
423
472
|
}
|
|
424
473
|
return result;
|
|
425
474
|
}, [model, menuLevels, getModelConfigurableFlowsAndSteps]);
|
|
426
|
-
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
|
|
427
|
-
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
428
475
|
(0, import_react.useEffect)(() => {
|
|
429
476
|
const triggerRebuild = /* @__PURE__ */ __name(() => {
|
|
430
477
|
setRefreshTick((v) => v + 1);
|
|
@@ -517,11 +564,15 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
517
564
|
}
|
|
518
565
|
}, "onChange"),
|
|
519
566
|
...(uiMode == null ? void 0 : uiMode.props) || {},
|
|
520
|
-
itemKey: uiMode == null ? void 0 : uiMode.key
|
|
567
|
+
itemKey: uiMode == null ? void 0 : uiMode.key,
|
|
568
|
+
disabled: !!stepInfo.disabled,
|
|
569
|
+
disabledReason: stepInfo.disabledReason,
|
|
570
|
+
disabledIconColor
|
|
521
571
|
};
|
|
522
572
|
items.push({
|
|
523
573
|
key: uniqueKey,
|
|
524
|
-
label: /* @__PURE__ */ import_react.default.createElement(MenuLabelItem, { title:
|
|
574
|
+
label: /* @__PURE__ */ import_react.default.createElement(MenuLabelItem, { title: stepInfo.title, uiMode, itemProps }),
|
|
575
|
+
disabled: !!stepInfo.disabled
|
|
525
576
|
});
|
|
526
577
|
});
|
|
527
578
|
if (flow.options.divider === "bottom") {
|
|
@@ -555,7 +606,8 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
555
606
|
const uniqueKey = generateUniqueKey(`${flow.key}:${stepInfo.stepKey}`);
|
|
556
607
|
items.push({
|
|
557
608
|
key: uniqueKey,
|
|
558
|
-
label:
|
|
609
|
+
label: stepInfo.disabled ? /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 } }, stepInfo.title, /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: stepInfo.disabledReason, placement: "right", destroyTooltipOnHide: true }, /* @__PURE__ */ import_react.default.createElement(import_icons.QuestionCircleOutlined, { style: { color: disabledIconColor } }))) : stepInfo.title,
|
|
610
|
+
disabled: !!stepInfo.disabled
|
|
559
611
|
});
|
|
560
612
|
});
|
|
561
613
|
});
|
|
@@ -567,7 +619,8 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
567
619
|
const uniqueKey = generateUniqueKey(`${modelKey}:${flow.key}:${stepInfo.stepKey}`);
|
|
568
620
|
subMenuChildren.push({
|
|
569
621
|
key: uniqueKey,
|
|
570
|
-
label:
|
|
622
|
+
label: stepInfo.disabled ? /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 } }, stepInfo.title, /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: stepInfo.disabledReason, placement: "right", destroyTooltipOnHide: true }, /* @__PURE__ */ import_react.default.createElement(import_icons.QuestionCircleOutlined, { style: { color: disabledIconColor } }))) : stepInfo.title,
|
|
623
|
+
disabled: !!stepInfo.disabled
|
|
571
624
|
});
|
|
572
625
|
});
|
|
573
626
|
});
|
|
@@ -581,7 +634,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
581
634
|
}
|
|
582
635
|
}
|
|
583
636
|
return items;
|
|
584
|
-
}, [configurableFlowsAndSteps, flattenSubMenus, t]);
|
|
637
|
+
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, t]);
|
|
585
638
|
const finalMenuItems = (0, import_react.useMemo)(() => {
|
|
586
639
|
const items = [...menuItems];
|
|
587
640
|
const commonExtras = extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
|
|
@@ -61,6 +61,20 @@ const FlowsContextMenu = /* @__PURE__ */ __name((props) => {
|
|
|
61
61
|
const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
62
62
|
({ model, children, enabled = true, position = "right", showDeleteButton = true }) => {
|
|
63
63
|
const t = (0, import_utils.getT)(model);
|
|
64
|
+
const { token } = import_antd.theme.useToken();
|
|
65
|
+
const disabledIconColor = (token == null ? void 0 : token.colorTextTertiary) || (token == null ? void 0 : token.colorTextDescription) || (token == null ? void 0 : token.colorTextSecondary);
|
|
66
|
+
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
|
|
67
|
+
const isStepMenuItemDisabled = (0, import_react.useCallback)(
|
|
68
|
+
(key) => {
|
|
69
|
+
const [flowKey, stepKey] = key.split(":");
|
|
70
|
+
if (!flowKey || !stepKey) return false;
|
|
71
|
+
return configurableFlowsAndSteps.some(({ flow, steps }) => {
|
|
72
|
+
if (flow.key !== flowKey) return false;
|
|
73
|
+
return steps.some((stepInfo) => stepInfo.stepKey === stepKey && !!stepInfo.disabled);
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
[configurableFlowsAndSteps]
|
|
77
|
+
);
|
|
64
78
|
const handleMenuClick = (0, import_react.useCallback)(
|
|
65
79
|
({ key }) => {
|
|
66
80
|
var _a;
|
|
@@ -86,6 +100,9 @@ const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
|
86
100
|
}
|
|
87
101
|
});
|
|
88
102
|
} else {
|
|
103
|
+
if (isStepMenuItemDisabled(key)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
89
106
|
const [flowKey, stepKey] = key.split(":");
|
|
90
107
|
try {
|
|
91
108
|
const flow = model.getFlow(flowKey);
|
|
@@ -108,7 +125,7 @@ const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
|
108
125
|
}
|
|
109
126
|
}
|
|
110
127
|
},
|
|
111
|
-
[model]
|
|
128
|
+
[isStepMenuItemDisabled, model]
|
|
112
129
|
);
|
|
113
130
|
if (!model) {
|
|
114
131
|
return /* @__PURE__ */ import_react.default.createElement(import_antd.Alert, { message: "\u63D0\u4F9B\u7684\u6A21\u578B\u65E0\u6548", type: "error" });
|
|
@@ -129,6 +146,7 @@ const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
|
129
146
|
if (await (0, import_utils.shouldHideStepInSettings)(model, flow, actionStep)) {
|
|
130
147
|
return null;
|
|
131
148
|
}
|
|
149
|
+
const disabledState = await (0, import_utils.resolveStepDisabledInSettings)(model, flow, actionStep);
|
|
132
150
|
const stepUiSchema = actionStep.uiSchema || {};
|
|
133
151
|
let actionUiSchema = {};
|
|
134
152
|
if (actionStep.use) {
|
|
@@ -152,7 +170,9 @@ const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
|
152
170
|
stepKey,
|
|
153
171
|
step: actionStep,
|
|
154
172
|
uiSchema: mergedUiSchema,
|
|
155
|
-
title: actionStep.title || stepKey
|
|
173
|
+
title: actionStep.title || stepKey,
|
|
174
|
+
disabled: disabledState.disabled,
|
|
175
|
+
disabledReason: disabledState.reason
|
|
156
176
|
};
|
|
157
177
|
})
|
|
158
178
|
).then((steps) => steps.filter(Boolean));
|
|
@@ -165,7 +185,6 @@ const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
|
165
185
|
return [];
|
|
166
186
|
}
|
|
167
187
|
}, [model]);
|
|
168
|
-
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
|
|
169
188
|
(0, import_react.useEffect)(() => {
|
|
170
189
|
let mounted = true;
|
|
171
190
|
(async () => {
|
|
@@ -193,7 +212,8 @@ const FlowsContextMenuWithModel = (0, import_reactive.observer)(
|
|
|
193
212
|
menuItems.push({
|
|
194
213
|
key: `${flow.key}:${stepInfo.stepKey}`,
|
|
195
214
|
icon: /* @__PURE__ */ import_react.default.createElement(import_icons.SettingOutlined, null),
|
|
196
|
-
label: stepInfo.title
|
|
215
|
+
label: stepInfo.disabled ? /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 } }, stepInfo.title, /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: stepInfo.disabledReason, placement: "right", destroyTooltipOnHide: true }, /* @__PURE__ */ import_react.default.createElement(import_icons.QuestionCircleOutlined, { style: { color: disabledIconColor } }))) : stepInfo.title,
|
|
216
|
+
disabled: !!stepInfo.disabled
|
|
197
217
|
});
|
|
198
218
|
});
|
|
199
219
|
});
|
|
@@ -198,8 +198,7 @@ const VariableInputComponent = /* @__PURE__ */ __name(({
|
|
|
198
198
|
(0, import_react.useEffect)(() => {
|
|
199
199
|
if (!resolvedMetaTreeNode) return;
|
|
200
200
|
if (!Array.isArray(resolvedMetaTree) || innerValue == null) return;
|
|
201
|
-
|
|
202
|
-
emitChange(finalValue, resolvedMetaTreeNode);
|
|
201
|
+
emitChange(innerValue, resolvedMetaTreeNode);
|
|
203
202
|
setCurrentMetaTreeNode(resolvedMetaTreeNode);
|
|
204
203
|
}, [resolvedMetaTreeNode]);
|
|
205
204
|
const composingRef = (0, import_react.useRef)(false);
|
|
@@ -59,53 +59,60 @@ const VariableTagComponent = /* @__PURE__ */ __name(({
|
|
|
59
59
|
const ctx = (0, import_FlowContextProvider.useFlowContext)();
|
|
60
60
|
const { data: displayedValue } = (0, import_ahooks.useRequest)(
|
|
61
61
|
async () => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} catch {
|
|
62
|
+
const resolveLabelFromPath = /* @__PURE__ */ __name(async (rawPath2) => {
|
|
63
|
+
if (!rawPath2) return null;
|
|
64
|
+
if (!Array.isArray(rawPath2)) return null;
|
|
65
|
+
if (!Array.isArray(resolvedMetaTree)) return null;
|
|
66
|
+
const topNames = new Set((resolvedMetaTree || []).map((n) => String(n == null ? void 0 : n.name)));
|
|
67
|
+
const path = !topNames.has(String(rawPath2[0])) ? rawPath2.slice(1) : rawPath2;
|
|
68
|
+
if (!path.length) return "";
|
|
69
|
+
let nodes = resolvedMetaTree;
|
|
70
|
+
const titleChain = [];
|
|
71
|
+
let matchedCount = 0;
|
|
72
|
+
for (let i = 0; i < path.length; i++) {
|
|
73
|
+
if (!nodes) break;
|
|
74
|
+
const seg = String(path[i]);
|
|
75
|
+
const node = nodes.find((n) => String(n == null ? void 0 : n.name) === seg);
|
|
76
|
+
if (!node) break;
|
|
77
|
+
titleChain.push(String(node.title ?? node.name ?? seg));
|
|
78
|
+
matchedCount = i + 1;
|
|
79
|
+
if (i < path.length - 1) {
|
|
80
|
+
if (Array.isArray(node.children)) {
|
|
81
|
+
nodes = node.children;
|
|
82
|
+
} else if (typeof node.children === "function") {
|
|
83
|
+
try {
|
|
84
|
+
const childNodes = await node.children();
|
|
85
|
+
node.children = childNodes;
|
|
86
|
+
nodes = childNodes;
|
|
87
|
+
} catch {
|
|
88
|
+
nodes = void 0;
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
92
91
|
nodes = void 0;
|
|
93
92
|
}
|
|
94
|
-
} else {
|
|
95
|
-
nodes = void 0;
|
|
96
93
|
}
|
|
97
94
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const titles = deepest.parentTitles ? [...deepest.parentTitles, deepest.title] : [deepest.title];
|
|
101
|
-
let label = titles.map(ctx.t).join("/");
|
|
95
|
+
if (matchedCount === 0) return null;
|
|
96
|
+
let label2 = titleChain.map(ctx.t).join("/");
|
|
102
97
|
if (matchedCount < path.length) {
|
|
103
98
|
const tail = path.slice(matchedCount).join("/");
|
|
104
|
-
|
|
99
|
+
label2 = tail ? `${label2}/${tail}` : label2;
|
|
105
100
|
}
|
|
106
|
-
return
|
|
101
|
+
return label2;
|
|
102
|
+
}, "resolveLabelFromPath");
|
|
103
|
+
if (metaTreeNode == null ? void 0 : metaTreeNode.parentTitles) {
|
|
104
|
+
return [...metaTreeNode.parentTitles, metaTreeNode.title].map(ctx.t).join("/");
|
|
105
|
+
}
|
|
106
|
+
if (metaTreeNode) {
|
|
107
|
+
const rawPath2 = (0, import_utils.parseValueToPath)(value) || metaTreeNode.paths;
|
|
108
|
+
const label2 = await resolveLabelFromPath(rawPath2);
|
|
109
|
+
return label2 ?? ctx.t(metaTreeNode.title) ?? "";
|
|
107
110
|
}
|
|
108
|
-
return
|
|
111
|
+
if (!value) return String(value);
|
|
112
|
+
const rawPath = (0, import_utils.parseValueToPath)(value);
|
|
113
|
+
const label = await resolveLabelFromPath(rawPath);
|
|
114
|
+
if (label != null) return label;
|
|
115
|
+
return Array.isArray(rawPath) ? rawPath.join("/") : String(value);
|
|
109
116
|
},
|
|
110
117
|
{ refreshDeps: [resolvedMetaTree, value, metaTreeNode] }
|
|
111
118
|
);
|
package/lib/data-source/index.js
CHANGED
|
@@ -118,7 +118,7 @@ const _DataSource = class _DataSource {
|
|
|
118
118
|
return this.dataSourceManager.flowEngine;
|
|
119
119
|
}
|
|
120
120
|
get displayName() {
|
|
121
|
-
return this.
|
|
121
|
+
return this.flowEngine.translate(this.options.displayName, { ns: "lm-collections" }) || this.key;
|
|
122
122
|
}
|
|
123
123
|
get key() {
|
|
124
124
|
return this.options.key;
|
|
@@ -457,7 +457,7 @@ const _Collection = class _Collection {
|
|
|
457
457
|
return this.options.storage || "local";
|
|
458
458
|
}
|
|
459
459
|
get title() {
|
|
460
|
-
return this.
|
|
460
|
+
return this.flowEngine.translate(this.options.title, { ns: "lm-collections" }) || this.name;
|
|
461
461
|
}
|
|
462
462
|
get titleCollectionField() {
|
|
463
463
|
const titleFieldName = this.options.titleField || this.filterTargetKey;
|
|
@@ -692,8 +692,8 @@ const _CollectionField = class _CollectionField {
|
|
|
692
692
|
}
|
|
693
693
|
get title() {
|
|
694
694
|
var _a, _b, _c;
|
|
695
|
-
const titleValue = ((_b = (_a = this.options) == null ? void 0 : _a.uiSchema) == null ? void 0 : _b.title) || ((_c = this.options) == null ? void 0 : _c.title)
|
|
696
|
-
return this.flowEngine.translate(titleValue);
|
|
695
|
+
const titleValue = ((_b = (_a = this.options) == null ? void 0 : _a.uiSchema) == null ? void 0 : _b.title) || ((_c = this.options) == null ? void 0 : _c.title);
|
|
696
|
+
return this.flowEngine.translate(titleValue, { ns: "lm-collections" }) || this.options.name;
|
|
697
697
|
}
|
|
698
698
|
set title(value) {
|
|
699
699
|
this.options.title = value;
|
|
@@ -711,11 +711,17 @@ const _CollectionField = class _CollectionField {
|
|
|
711
711
|
}
|
|
712
712
|
return {
|
|
713
713
|
...v,
|
|
714
|
+
label: v.label ? this.flowEngine.translate(v.label, { ns: "lm-collections" }) : v.label,
|
|
714
715
|
value: Number(v.value)
|
|
715
716
|
};
|
|
716
717
|
});
|
|
717
718
|
}
|
|
718
|
-
return options
|
|
719
|
+
return options.map((v) => {
|
|
720
|
+
return {
|
|
721
|
+
...v,
|
|
722
|
+
label: this.flowEngine.translate(v.label, { ns: "lm-collections" })
|
|
723
|
+
};
|
|
724
|
+
});
|
|
719
725
|
}
|
|
720
726
|
get defaultValue() {
|
|
721
727
|
return this.options.defaultValue == null ? void 0 : this.options.defaultValue;
|
package/lib/flowContext.js
CHANGED
|
@@ -830,7 +830,8 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
830
830
|
value: this.engine
|
|
831
831
|
});
|
|
832
832
|
this.defineProperty("sql", {
|
|
833
|
-
get: /* @__PURE__ */ __name(() => new import_resources.FlowSQLRepository(
|
|
833
|
+
get: /* @__PURE__ */ __name((ctx) => new import_resources.FlowSQLRepository(ctx), "get"),
|
|
834
|
+
cache: false
|
|
834
835
|
});
|
|
835
836
|
this.defineProperty("dataSourceManager", {
|
|
836
837
|
value: dataSourceManager
|
|
@@ -1120,6 +1121,9 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
1120
1121
|
const modelClass = (0, import_registry.getModelClassName)(this);
|
|
1121
1122
|
const Ctor = import_registry.RunJSContextRegistry.resolve(version, modelClass) || FlowRunJSContext;
|
|
1122
1123
|
const runCtx = new Ctor(this);
|
|
1124
|
+
runCtx.defineMethod("t", (key, options2) => {
|
|
1125
|
+
return this.t(key, { ns: "runjs", ...options2 });
|
|
1126
|
+
});
|
|
1123
1127
|
const globals = { ctx: runCtx, ...(options == null ? void 0 : options.globals) || {} };
|
|
1124
1128
|
const { timeoutMs } = options || {};
|
|
1125
1129
|
return new import_JSRunner.JSRunner({ globals, timeoutMs });
|
package/lib/flowI18n.js
CHANGED
|
@@ -56,11 +56,13 @@ const _FlowI18n = class _FlowI18n {
|
|
|
56
56
|
if (!keyOrTemplate || typeof keyOrTemplate !== "string") {
|
|
57
57
|
return keyOrTemplate;
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
if ((options == null ? void 0 : options.compareWith) && keyOrTemplate === options.compareWith) {
|
|
60
|
+
return keyOrTemplate;
|
|
61
|
+
}
|
|
62
|
+
if (this.isTemplate(keyOrTemplate)) {
|
|
63
|
+
return this.compileTemplate(keyOrTemplate);
|
|
62
64
|
}
|
|
63
|
-
return
|
|
65
|
+
return this.translateKey(keyOrTemplate, options);
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
68
|
* 内部翻译方法
|
package/lib/locale/en-US.json
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"This is likely a NocoBase internals bug. Please open an issue at": "This is likely a NocoBase internals bug. Please open an issue at",
|
|
63
63
|
"This step has no configurable parameters": "This step has no configurable parameters",
|
|
64
64
|
"This variable is not available": "This variable is not available",
|
|
65
|
+
"Use return to output value": "Use return to output value",
|
|
65
66
|
"Try again": "Try again",
|
|
66
67
|
"Template created": "Template created",
|
|
67
68
|
"Template description": "Template description",
|
|
@@ -70,4 +71,4 @@
|
|
|
70
71
|
"UID copied to clipboard": "UID copied to clipboard",
|
|
71
72
|
"createModelOptions must specify use property": "createModelOptions must specify \"use\" property",
|
|
72
73
|
"here": "here"
|
|
73
|
-
}
|
|
74
|
+
}
|
package/lib/locale/index.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export declare const locales: {
|
|
|
71
71
|
"This is likely a NocoBase internals bug. Please open an issue at": string;
|
|
72
72
|
"This step has no configurable parameters": string;
|
|
73
73
|
"This variable is not available": string;
|
|
74
|
+
"Use return to output value": string;
|
|
74
75
|
"Try again": string;
|
|
75
76
|
"Template created": string;
|
|
76
77
|
"Template description": string;
|
|
@@ -148,6 +149,7 @@ export declare const locales: {
|
|
|
148
149
|
"This is likely a NocoBase internals bug. Please open an issue at": string;
|
|
149
150
|
"This step has no configurable parameters": string;
|
|
150
151
|
"This variable is not available": string;
|
|
152
|
+
"Use return to output value": string;
|
|
151
153
|
"Try again": string;
|
|
152
154
|
"UID copied to clipboard": string;
|
|
153
155
|
"createModelOptions must specify use property": string;
|
package/lib/locale/zh-CN.json
CHANGED
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"This is likely a NocoBase internals bug. Please open an issue at": "这可能是 NocoBase 内部错误。请在以下地址提交问题",
|
|
67
67
|
"This step has no configurable parameters": "此步骤没有可配置的参数",
|
|
68
68
|
"This variable is not available": "此变量不可用",
|
|
69
|
+
"Use return to output value": "使用 return 返回最终值",
|
|
69
70
|
"Try again": "重试",
|
|
70
71
|
"UID copied to clipboard": "UID 已复制到剪贴板",
|
|
71
72
|
"createModelOptions must specify use property": "createModelOptions 必须指定 \"use\" 属性",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import { FlowEngineContext } from '../flowContext';
|
|
9
|
+
import { FlowContext, FlowEngineContext } from '../flowContext';
|
|
10
10
|
import { BaseRecordResource } from './baseRecordResource';
|
|
11
11
|
type SQLRunOptions = {
|
|
12
12
|
bind?: Record<string, any>;
|
|
@@ -20,8 +20,8 @@ type SQLSaveOptions = {
|
|
|
20
20
|
dataSourceKey?: string;
|
|
21
21
|
};
|
|
22
22
|
export declare class FlowSQLRepository {
|
|
23
|
-
protected ctx:
|
|
24
|
-
constructor(ctx:
|
|
23
|
+
protected ctx: FlowContext;
|
|
24
|
+
constructor(ctx: FlowContext);
|
|
25
25
|
run(sql: string, options?: SQLRunOptions): Promise<any>;
|
|
26
26
|
save(data: SQLSaveOptions): Promise<void>;
|
|
27
27
|
runById(uid: string, options?: SQLRunOptions): Promise<any>;
|
package/lib/types.d.ts
CHANGED
|
@@ -147,6 +147,18 @@ export interface ActionDefinition<TModel extends FlowModel = FlowModel, TCtx ext
|
|
|
147
147
|
* - StepDefinition.hideInSettings can override the ActionDefinition value.
|
|
148
148
|
*/
|
|
149
149
|
hideInSettings?: boolean | ((ctx: TCtx) => boolean | Promise<boolean>);
|
|
150
|
+
/**
|
|
151
|
+
* Whether to disable this step/action in settings menus.
|
|
152
|
+
* - Supports static boolean and dynamic decision based on runtime context.
|
|
153
|
+
* - StepDefinition.disabledInSettings can override the ActionDefinition value.
|
|
154
|
+
*/
|
|
155
|
+
disabledInSettings?: boolean | ((ctx: TCtx) => boolean | Promise<boolean>);
|
|
156
|
+
/**
|
|
157
|
+
* Optional reason shown when this step/action is disabled in settings menus.
|
|
158
|
+
* - Supports static string and dynamic resolver based on runtime context.
|
|
159
|
+
* - StepDefinition.disabledReasonInSettings can override the ActionDefinition value.
|
|
160
|
+
*/
|
|
161
|
+
disabledReasonInSettings?: string | ((ctx: TCtx) => string | Promise<string>);
|
|
150
162
|
/**
|
|
151
163
|
* 在执行 Action 前为 ctx 定义临时属性。
|
|
152
164
|
* - 仅支持 PropertyOptions 形态(例如:{ foo: { value: 5 } });
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Collection } from '../data-source';
|
|
10
10
|
import type { FlowContext, PropertyMetaFactory } from '../flowContext';
|
|
11
11
|
/**
|
|
12
|
-
* 创建一个用于“对象类变量”(如 formValues /
|
|
12
|
+
* 创建一个用于“对象类变量”(如 formValues / item)的 `resolveOnServer` 判定函数。
|
|
13
13
|
* 仅当访问路径以“关联字段名”开头(且继续访问其子属性)时,返回 true 交由服务端解析;
|
|
14
14
|
* 否则在前端解析即可。
|
|
15
15
|
*
|
|
@@ -26,7 +26,7 @@ export declare function createAssociationSubpathResolver(collectionAccessor: ()
|
|
|
26
26
|
*
|
|
27
27
|
* @param collectionAccessor 获取集合对象,用于字段/元信息来源
|
|
28
28
|
* @param title 变量组标题(用于 UI 展示)
|
|
29
|
-
* @param valueAccessor 获取当前对象值(如 ctx.form.getFieldsValue() / ctx.
|
|
29
|
+
* @param valueAccessor 获取当前对象值(如 ctx.form.getFieldsValue() / ctx.item)
|
|
30
30
|
* @returns PropertyMetaFactory
|
|
31
31
|
*/
|
|
32
32
|
export declare function createAssociationAwareObjectMetaFactory(collectionAccessor: () => Collection | null, title: string, valueAccessor: (ctx: FlowContext) => any): PropertyMetaFactory;
|
package/lib/utils/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { FlowExitException } from './exceptions';
|
|
|
12
12
|
export { defineAction } from './flow-definitions';
|
|
13
13
|
export { isInheritedFrom } from './inheritance';
|
|
14
14
|
export { resolveCreateModelOptions, resolveDefaultParams, resolveExpressions } from './params-resolvers';
|
|
15
|
-
export { compileUiSchema, resolveStepUiSchema, resolveUiMode, shouldHideStepInSettings } from './schema-utils';
|
|
15
|
+
export { compileUiSchema, resolveStepUiSchema, resolveStepDisabledInSettings, resolveUiMode, shouldHideStepInSettings, } from './schema-utils';
|
|
16
16
|
export { setupRuntimeContextSteps } from './setupRuntimeContextSteps';
|
|
17
17
|
export { createCollectionContextMeta } from './createCollectionContextMeta';
|
|
18
18
|
export { createAssociationAwareObjectMetaFactory, createAssociationSubpathResolver } from './associationObjectVariable';
|
|
@@ -20,7 +20,9 @@ export { buildRecordMeta, collectContextParamsForTemplate, createCurrentRecordMe
|
|
|
20
20
|
export { extractPropertyPath, formatPathToVariable, isVariableExpression } from './context';
|
|
21
21
|
export { clearAutoFlowError, getAutoFlowError, setAutoFlowError, type AutoFlowError } from './autoFlowError';
|
|
22
22
|
export { parsePathnameToViewParams, type ViewParam } from './parsePathnameToViewParams';
|
|
23
|
-
export { createSafeDocument, createSafeWindow, createSafeNavigator } from './safeGlobals';
|
|
23
|
+
export { createSafeDocument, createSafeWindow, createSafeNavigator, createSafeRunJSGlobals, runjsWithSafeGlobals, } from './safeGlobals';
|
|
24
|
+
export { isRunJSValue, normalizeRunJSValue, extractUsedVariablePathsFromRunJS, type RunJSValue } from './runjsValue';
|
|
25
|
+
export { resolveRunJSObjectValues } from './resolveRunJSObjectValues';
|
|
24
26
|
export { prepareRunJsCode, preprocessRunJsTemplates } from './runjsTemplateCompat';
|
|
25
27
|
export { createEphemeralContext } from './createEphemeralContext';
|
|
26
28
|
export { pruneFilter } from './pruneFilter';
|