@nocobase/plugin-workflow-loop 1.3.44-beta → 1.4.0-alpha.0
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/dist/client/LoopInstruction.d.ts +64 -3
- package/dist/client/index.js +3 -3
- package/dist/constants.d.ts +21 -0
- package/dist/constants.js +52 -0
- package/dist/externalVersion.js +7 -3
- package/dist/locale/zh-CN.json +16 -2
- package/dist/server/LoopInstruction.d.ts +18 -3
- package/dist/server/LoopInstruction.js +69 -13
- package/dist/server/migrations/20240929212031-change-loop-result.d.ts +20 -0
- package/dist/server/migrations/20240929212031-change-loop-result.js +55 -0
- package/package.json +3 -2
- /package/dist/locale/{ja_JP.json → ja-JP.json} +0 -0
- /package/dist/locale/{ko_KR.json → ko-KR.json} +0 -0
|
@@ -7,7 +7,16 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import React from 'react';
|
|
10
|
-
import { VariableOption, WorkflowVariableInput, Instruction } from '@nocobase/plugin-workflow/client';
|
|
10
|
+
import { VariableOption, WorkflowVariableInput, WorkflowVariableTextArea, Instruction, RadioWithTooltip, CalculationConfig } from '@nocobase/plugin-workflow/client';
|
|
11
|
+
declare function LoopCondition({ value, onChange }: {
|
|
12
|
+
value: any;
|
|
13
|
+
onChange: any;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
declare function useScopeVariables(node: any, options: any): VariableOption[];
|
|
16
|
+
declare function LoopVariableTextArea({ variableOptions, ...props }: {
|
|
17
|
+
[x: string]: any;
|
|
18
|
+
variableOptions: any;
|
|
19
|
+
}): JSX.Element;
|
|
11
20
|
export default class extends Instruction {
|
|
12
21
|
title: string;
|
|
13
22
|
type: string;
|
|
@@ -22,17 +31,69 @@ export default class extends Instruction {
|
|
|
22
31
|
'x-component': string;
|
|
23
32
|
'x-component-props': {
|
|
24
33
|
changeOnSelect: boolean;
|
|
25
|
-
|
|
34
|
+
nullable: boolean;
|
|
35
|
+
useTypedConstant: (string | (string | {
|
|
36
|
+
step: number;
|
|
37
|
+
min: number;
|
|
38
|
+
precision: number;
|
|
39
|
+
})[])[];
|
|
26
40
|
className: string;
|
|
27
41
|
};
|
|
28
42
|
required: boolean;
|
|
43
|
+
default: number;
|
|
44
|
+
'x-reactions': {
|
|
45
|
+
target: string;
|
|
46
|
+
effects: string[];
|
|
47
|
+
fulfill: {
|
|
48
|
+
state: {
|
|
49
|
+
value: string;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}[];
|
|
29
53
|
};
|
|
54
|
+
condition: {
|
|
55
|
+
type: string;
|
|
56
|
+
'x-decorator': string;
|
|
57
|
+
'x-component': string;
|
|
58
|
+
'x-reactions': {
|
|
59
|
+
dependencies: string[];
|
|
60
|
+
fulfill: {
|
|
61
|
+
state: {
|
|
62
|
+
visible: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}[];
|
|
66
|
+
default: boolean;
|
|
67
|
+
};
|
|
68
|
+
exit: {
|
|
69
|
+
type: string;
|
|
70
|
+
title: string;
|
|
71
|
+
'x-decorator': string;
|
|
72
|
+
'x-component': string;
|
|
73
|
+
'x-component-props': {
|
|
74
|
+
direction: string;
|
|
75
|
+
options: {
|
|
76
|
+
label: string;
|
|
77
|
+
value: number;
|
|
78
|
+
}[];
|
|
79
|
+
};
|
|
80
|
+
default: number;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
scope: {
|
|
84
|
+
renderEngineReference: (key: string) => React.JSX.Element;
|
|
30
85
|
};
|
|
31
86
|
components: {
|
|
87
|
+
LoopCondition: typeof LoopCondition;
|
|
32
88
|
WorkflowVariableInput: typeof WorkflowVariableInput;
|
|
89
|
+
WorkflowVariableTextArea: typeof WorkflowVariableTextArea;
|
|
90
|
+
LoopVariableTextArea: typeof LoopVariableTextArea;
|
|
91
|
+
RadioWithTooltip: typeof RadioWithTooltip;
|
|
92
|
+
CalculationConfig: typeof CalculationConfig;
|
|
33
93
|
};
|
|
34
94
|
Component({ data }: {
|
|
35
95
|
data: any;
|
|
36
96
|
}): React.JSX.Element;
|
|
37
|
-
useScopeVariables
|
|
97
|
+
useScopeVariables: typeof useScopeVariables;
|
|
38
98
|
}
|
|
99
|
+
export {};
|
package/dist/client/index.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
(function(n
|
|
10
|
+
(function(o,n){typeof exports=="object"&&typeof module!="undefined"?n(exports,require("@nocobase/client"),require("react/jsx-runtime"),require("react"),require("@ant-design/icons"),require("antd"),require("@formily/antd-v5"),require("@formily/react"),require("@nocobase/plugin-workflow/client"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","react/jsx-runtime","react","@ant-design/icons","antd","@formily/antd-v5","@formily/react","@nocobase/plugin-workflow/client","react-i18next"],n):(o=typeof globalThis!="undefined"?globalThis:o||self,n(o["@nocobase/plugin-workflow-loop"]={},o["@nocobase/client"],o.jsxRuntime,o.react,o["@ant-design/icons"],o.antd,o["@formily/antd-v5"],o["@formily/react"],o["@nocobase/plugin-workflow"],o["react-i18next"]))})(this,function(o,n,e,d,v,L,y,C,a,w){"use strict";var U=Object.defineProperty,Y=Object.defineProperties;var z=Object.getOwnPropertyDescriptors;var F=Object.getOwnPropertySymbols;var A=Object.prototype.hasOwnProperty,B=Object.prototype.propertyIsEnumerable;var q=(o,n,e)=>n in o?U(o,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[n]=e,g=(o,n)=>{for(var e in n||(n={}))A.call(n,e)&&q(o,e,n[e]);if(F)for(var e of F(n))B.call(n,e)&&q(o,e,n[e]);return o},m=(o,n)=>Y(o,z(n));var S=(o,n)=>{var e={};for(var d in o)A.call(o,d)&&n.indexOf(d)<0&&(e[d]=o[d]);if(o!=null&&F)for(var d of F(o))n.indexOf(d)<0&&B.call(o,d)&&(e[d]=o[d]);return e};var b=(o,n,e)=>(q(o,typeof n!="symbol"?n+"":n,e),e);var O=(o,n,e)=>new Promise((d,v)=>{var L=a=>{try{C(e.next(a))}catch(w){v(w)}},y=a=>{try{C(e.throw(a))}catch(w){v(w)}},C=a=>a.done?d(a.value):Promise.resolve(a.value).then(L,y);C((e=e.apply(o,n)).next())});const c="workflow-loop";function T(s,l={}){const{t:r}=E(l);return r(s)}function E(s){return w.useTranslation(c,s)}function $(s,l){let r=s,f=null;for(let i=0;i<l.length;i++){const h=l[i],t=r.find(u=>u.value===h);if(!t)return null;f=t,!t.isLeaf&&t.loadChildren&&t.loadChildren(t),t.children&&(r=t.children)}return f}function j({value:s,onChange:l}){const{t:r}=w.useTranslation(),f=d.useCallback(t=>{l(m(g({},s),{checkpoint:t.target.value}))},[s,l]),i=d.useCallback(t=>{l(m(g({},s),{continueOnFalse:t.target.value}))},[s,l]),h=d.useCallback(t=>{l(m(g({},s),{calculation:t}))},[s,l]);return e.jsxs(e.Fragment,{children:[e.jsx(L.Checkbox,{checked:!!s,onChange:t=>{l(t.target.checked?{checkpoint:0,continueOnFalse:!1,calculation:{group:{type:"and",calculations:[]}}}:!1)},children:r("Enable loop condition",{ns:c})}),s?e.jsx(L.Card,{children:e.jsxs(y.FormLayout,{layout:"vertical",children:[e.jsx(y.FormItem,{label:r("Condition",{ns:c}),children:e.jsx(a.CalculationConfig,{value:s.calculation,onChange:h,useVariableHook:V})}),e.jsx(y.FormItem,{label:r("When to check",{ns:c}),children:e.jsx(a.RadioWithTooltip,{value:s.checkpoint,onChange:f,options:[{label:r("Before each starts",{ns:c}),value:0},{label:r("After each ends",{ns:c}),value:1}]})}),e.jsx(y.FormItem,{label:r("When condition is not met on item",{ns:c}),children:e.jsx(a.RadioWithTooltip,{value:s.continueOnFalse,onChange:i,options:[{label:r("Exit loop",{ns:c}),value:!1},{label:r("Continue on next item",{ns:c}),value:!0}]})})]})}):null]})}function N(s,l){const r=n.useCompile(),f=T("Loop target"),i=T("Loop index (starts from 0)"),h=T("Loop sequence (starts from 1)"),t=T("Loop length"),{target:u}=s.config;if(u==null)return null;const{fieldNames:p=a.defaultFieldNames,scope:x}=l;let I={key:"item",[p.value]:"item",[p.label]:f};if(typeof u=="string"&&u.startsWith("{{")&&u.endsWith("}}")){const _=u.slice(2,-2).split(".").map(k=>k.trim()),D=x!=null?x:[a.scopeOptions,a.nodesOptions,a.triggerOptions].map(k=>{const W=k.useOptions(m(g({},l),{current:s})).filter(Boolean);return{[p.label]:r(k.label),[p.value]:k.value,key:k.value,[p.children]:W,disabled:W&&!W.length}}),K=$(D,_);I=Object.assign({},K,I)}return[I,{key:"index",[p.value]:"index",[p.label]:i},{key:"sequence",[p.value]:"sequence",[p.label]:h},{key:"length",[p.value]:"length",[p.label]:t}]}function V(s={}){var p;const{values:l}=C.useForm(),r=a.useNodeContext(),f=m(g({},r),{config:m(g({},r.config),{target:l.target})}),i=a.useWorkflowVariableOptions(s),h=N(f,m(g({},s),{scope:i.filter(x=>["$scopes","$jobsMapByNodeKey","$context"].includes(x.key))})),{fieldNames:t=a.defaultFieldNames}=s;if(!h)return i;const u=i.find(x=>x[t.value]==="$scopes");return u&&(u[t.children]||(u[t.children]=[]),u[t.children].unshift({key:r.key,[t.value]:r.key,[t.label]:(p=r.title)!=null?p:`#${r.id}`,[t.children]:h}),u.disabled=!1),[...i]}function M(r){var f=r,{variableOptions:s}=f,l=S(f,["variableOptions"]);const i=V(s);return e.jsx(n.Variable.TextArea,g({scope:i},l))}class P extends a.Instruction{constructor(){super(...arguments);b(this,"title",`{{t("Loop", { ns: "${c}" })}}`);b(this,"type","loop");b(this,"group","control");b(this,"description",`{{t("By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.", { ns: "${c}" })}}`);b(this,"fieldset",{target:{type:"string",title:`{{t("Loop target", { ns: "${c}" })}}`,description:`{{t("A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.", { ns: "${c}" })}}`,"x-decorator":"FormItem","x-component":"WorkflowVariableInput","x-component-props":{changeOnSelect:!0,nullable:!1,useTypedConstant:["string",["number",{step:1,min:0,precision:0}]],className:n.css`
|
|
11
11
|
width: 100%;
|
|
12
12
|
|
|
13
13
|
.variable {
|
|
@@ -17,6 +17,6 @@
|
|
|
17
17
|
.ant-input.null-value {
|
|
18
18
|
width: 100%;
|
|
19
19
|
}
|
|
20
|
-
`},required:!0}});
|
|
20
|
+
`},required:!0,default:1,"x-reactions":[{target:"calculation",effects:["onFieldValueChange"],fulfill:{state:{value:null}}},{target:"expression",effects:["onFieldValueChange"],fulfill:{state:{value:""}}}]},condition:{type:"boolean","x-decorator":"FormItem","x-component":"LoopCondition","x-reactions":[{dependencies:["target"],fulfill:{state:{visible:"{{Boolean($deps[0])}}"}}}],default:!1},exit:{type:"number",title:`{{t("When node inside loop failed", { ns: "${c}" })}}`,"x-decorator":"FormItem","x-component":"RadioWithTooltip","x-component-props":{direction:"vertical",options:[{label:`{{t("Exit workflow", { ns: "${c}" })}}`,value:0},{label:`{{t("Exit loop and continue workflow", { ns: "${c}" })}}`,value:1},{label:`{{t("Continue loop on next item", { ns: "${c}" })}}`,value:2}]},default:0}});b(this,"scope",{renderEngineReference:a.renderEngineReference});b(this,"components",{LoopCondition:j,WorkflowVariableInput:a.WorkflowVariableInput,WorkflowVariableTextArea:a.WorkflowVariableTextArea,LoopVariableTextArea:M,RadioWithTooltip:a.RadioWithTooltip,CalculationConfig:a.CalculationConfig});b(this,"useScopeVariables",N)}Component({data:r}){var t;const{nodes:f}=a.useFlowContext(),{styles:i}=a.useStyles(),h=f.find(u=>u.upstreamId===r.id&&u.branchIndex!=null);return e.jsx(a.NodeDefaultView,{data:r,children:e.jsx("div",{className:i.nodeSubtreeClass,children:e.jsxs("div",{className:n.cx(i.branchBlockClass,n.css`
|
|
21
21
|
padding-left: 20em;
|
|
22
|
-
`),children:[
|
|
22
|
+
`),children:[e.jsx(a.Branch,{from:r,entry:h,branchIndex:(t=h==null?void 0:h.branchIndex)!=null?t:0}),e.jsxs("div",{className:i.branchClass,children:[e.jsx("div",{className:"workflow-branch-lines"}),e.jsx("div",{className:n.cx(i.addButtonClass,i.loopLineClass),children:e.jsx(v.ArrowUpOutlined,{})})]})]})})})}}class H extends n.Plugin{afterAdd(){return O(this,null,function*(){})}beforeLoad(){return O(this,null,function*(){})}load(){return O(this,null,function*(){this.app.pm.get("workflow").registerInstruction("loop",P)})}}o.default=H,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CHECKPOINT: {
|
|
10
|
+
BEFORE: number;
|
|
11
|
+
AFTER: number;
|
|
12
|
+
};
|
|
13
|
+
export declare const CONTINUE_ON_FALSE: {
|
|
14
|
+
BREAK: boolean;
|
|
15
|
+
CONTINUE: boolean;
|
|
16
|
+
};
|
|
17
|
+
export declare const EXIT: {
|
|
18
|
+
CONTINUE: number;
|
|
19
|
+
BREAK: number;
|
|
20
|
+
RETURN: number;
|
|
21
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var constants_exports = {};
|
|
28
|
+
__export(constants_exports, {
|
|
29
|
+
CHECKPOINT: () => CHECKPOINT,
|
|
30
|
+
CONTINUE_ON_FALSE: () => CONTINUE_ON_FALSE,
|
|
31
|
+
EXIT: () => EXIT
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(constants_exports);
|
|
34
|
+
const CHECKPOINT = {
|
|
35
|
+
BEFORE: 0,
|
|
36
|
+
AFTER: 1
|
|
37
|
+
};
|
|
38
|
+
const CONTINUE_ON_FALSE = {
|
|
39
|
+
BREAK: false,
|
|
40
|
+
CONTINUE: true
|
|
41
|
+
};
|
|
42
|
+
const EXIT = {
|
|
43
|
+
CONTINUE: 2,
|
|
44
|
+
BREAK: 1,
|
|
45
|
+
RETURN: 0
|
|
46
|
+
};
|
|
47
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
48
|
+
0 && (module.exports = {
|
|
49
|
+
CHECKPOINT,
|
|
50
|
+
CONTINUE_ON_FALSE,
|
|
51
|
+
EXIT
|
|
52
|
+
});
|
package/dist/externalVersion.js
CHANGED
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
module.exports = {
|
|
11
11
|
"react": "18.2.0",
|
|
12
12
|
"@ant-design/icons": "5.2.6",
|
|
13
|
-
"
|
|
14
|
-
"@
|
|
13
|
+
"antd": "5.12.8",
|
|
14
|
+
"@formily/antd-v5": "1.1.9",
|
|
15
|
+
"@formily/react": "2.3.0",
|
|
16
|
+
"@nocobase/client": "1.4.0-alpha.0",
|
|
17
|
+
"@nocobase/plugin-workflow": "1.4.0-alpha.0",
|
|
15
18
|
"react-i18next": "11.18.6",
|
|
16
|
-
"@nocobase/
|
|
19
|
+
"@nocobase/evaluators": "1.4.0-alpha.0",
|
|
20
|
+
"@nocobase/server": "1.4.0-alpha.0"
|
|
17
21
|
};
|
package/dist/locale/zh-CN.json
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"Loop": "循环",
|
|
3
3
|
"Loop target": "循环对象",
|
|
4
|
-
"Loop index": "
|
|
4
|
+
"Loop index (starts from 0)": "当前索引(从 0 开始)",
|
|
5
|
+
"Loop sequence (starts from 1)": "当前序号(从 1 开始)",
|
|
5
6
|
"Loop length": "循环长度",
|
|
6
7
|
"By using a loop node, you can perform the same operation on multiple sets of data. The source of these sets can be either multiple records from a query node or multiple associated records of a single record. Loop node can also be used for iterating a certain number of times or for looping through each character in a string. However, excessive looping may cause performance issues, so use with caution.": "使用循环节点可以对多条数据进行同样的操作,多条数据的来源可以是查询节点的多条结果,或者一条数据的多条关系数据。也可以用于一定次数的循环,或者对字符串中每一个字符的循环处理。循环次数过高可能引起性能问题,请谨慎使用。",
|
|
7
|
-
"A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "单一数字值将被视为循环次数,单一字符串值将被视为字符数组,其他非数组值将被转换为数组。达到循环次数,或者将数组循环完成后,循环节点结束。你也可以在循环中添加条件节点,以终止循环。"
|
|
8
|
+
"A single number will be treated as a loop count, a single string will be treated as an array of characters, and other non-array values will be converted to arrays. The loop node ends when the loop count is reached, or when the array loop is completed. You can also add condition nodes to the loop to terminate it.": "单一数字值将被视为循环次数,单一字符串值将被视为字符数组,其他非数组值将被转换为数组。达到循环次数,或者将数组循环完成后,循环节点结束。你也可以在循环中添加条件节点,以终止循环。",
|
|
9
|
+
"Enable loop condition": "启用循环条件",
|
|
10
|
+
"Loop condition on each item": "循环每一项的条件",
|
|
11
|
+
"When to check": "检查时机",
|
|
12
|
+
"Before each starts": "每一轮开始之前",
|
|
13
|
+
"After each ends": "每一轮完成之后",
|
|
14
|
+
"When condition is not met on item": "当项不满足条件时",
|
|
15
|
+
"Exit loop": "退出循环",
|
|
16
|
+
"Continue on next item": "继续下一项",
|
|
17
|
+
"Condition": "条件",
|
|
18
|
+
"When node inside loop failed": "当循环内节点执行失败时",
|
|
19
|
+
"Continue loop on next item": "继续循环下一项",
|
|
20
|
+
"Exit loop and continue workflow": "退出循环并继续工作流",
|
|
21
|
+
"Exit workflow": "退出工作流"
|
|
8
22
|
}
|
|
@@ -7,15 +7,30 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Processor, Instruction, FlowNodeModel, JobModel } from '@nocobase/plugin-workflow';
|
|
10
|
+
export type LoopInstructionConfig = {
|
|
11
|
+
target: any;
|
|
12
|
+
condition?: {
|
|
13
|
+
checkpoint?: number;
|
|
14
|
+
continueOnFalse?: boolean;
|
|
15
|
+
calculation?: any;
|
|
16
|
+
expression?: string;
|
|
17
|
+
} | false;
|
|
18
|
+
exit?: number;
|
|
19
|
+
};
|
|
10
20
|
export default class extends Instruction {
|
|
11
|
-
run(node: FlowNodeModel, prevJob: JobModel, processor: Processor): Promise<{
|
|
21
|
+
run(node: FlowNodeModel, prevJob: JobModel, processor: Processor): Promise<JobModel | {
|
|
12
22
|
status: 1;
|
|
13
|
-
result:
|
|
23
|
+
result: {
|
|
24
|
+
looped: number;
|
|
25
|
+
};
|
|
14
26
|
}>;
|
|
15
27
|
resume(node: FlowNodeModel, branchJob: any, processor: Processor): Promise<any>;
|
|
16
|
-
getScope(node: any,
|
|
28
|
+
getScope(node: any, { looped }: {
|
|
29
|
+
looped: any;
|
|
30
|
+
}, processor: any): {
|
|
17
31
|
item: any;
|
|
18
32
|
index: any;
|
|
33
|
+
sequence: any;
|
|
19
34
|
length: number;
|
|
20
35
|
};
|
|
21
36
|
}
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
var __create = Object.create;
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
11
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
16
|
var __export = (target, all) => {
|
|
15
17
|
for (var name in all)
|
|
@@ -23,13 +25,23 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
23
25
|
}
|
|
24
26
|
return to;
|
|
25
27
|
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
29
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
30
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
31
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
32
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
33
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
34
|
+
mod
|
|
35
|
+
));
|
|
26
36
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
37
|
var LoopInstruction_exports = {};
|
|
28
38
|
__export(LoopInstruction_exports, {
|
|
29
39
|
default: () => LoopInstruction_default
|
|
30
40
|
});
|
|
31
41
|
module.exports = __toCommonJS(LoopInstruction_exports);
|
|
42
|
+
var import_evaluators = __toESM(require("@nocobase/evaluators"));
|
|
32
43
|
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
44
|
+
var import_constants = require("../constants");
|
|
33
45
|
function getTargetLength(target) {
|
|
34
46
|
let length = 0;
|
|
35
47
|
if (typeof target === "number") {
|
|
@@ -38,30 +50,48 @@ function getTargetLength(target) {
|
|
|
38
50
|
}
|
|
39
51
|
length = Math.floor(target);
|
|
40
52
|
} else {
|
|
41
|
-
const targets =
|
|
53
|
+
const targets = Array.isArray(target) ? target : [target].filter((t) => t != null);
|
|
42
54
|
length = targets.length;
|
|
43
55
|
}
|
|
44
56
|
return length;
|
|
45
57
|
}
|
|
58
|
+
function calculateCondition(node, processor) {
|
|
59
|
+
const { engine, calculation, expression } = node.config.condition ?? {};
|
|
60
|
+
const evaluator = import_evaluators.default.get(engine);
|
|
61
|
+
return evaluator ? evaluator(expression, processor.getScope(node.id, true)) : (0, import_plugin_workflow.logicCalculate)(processor.getParsedValue(calculation, node.id, { includeSelfScope: true }));
|
|
62
|
+
}
|
|
46
63
|
class LoopInstruction_default extends import_plugin_workflow.Instruction {
|
|
47
64
|
async run(node, prevJob, processor) {
|
|
48
65
|
const [branch] = processor.getBranches(node);
|
|
49
66
|
const target = processor.getParsedValue(node.config.target, node.id);
|
|
50
67
|
const length = getTargetLength(target);
|
|
68
|
+
const looped = 0;
|
|
51
69
|
if (!branch || !length) {
|
|
52
70
|
return {
|
|
53
71
|
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
|
54
|
-
result:
|
|
72
|
+
result: { looped }
|
|
55
73
|
};
|
|
56
74
|
}
|
|
57
75
|
const job = await processor.saveJob({
|
|
58
76
|
status: import_plugin_workflow.JOB_STATUS.PENDING,
|
|
59
|
-
|
|
60
|
-
result: 0,
|
|
77
|
+
result: { looped },
|
|
61
78
|
nodeId: node.id,
|
|
62
79
|
nodeKey: node.key,
|
|
63
80
|
upstreamId: (prevJob == null ? void 0 : prevJob.id) ?? null
|
|
64
81
|
});
|
|
82
|
+
if (node.config.condition) {
|
|
83
|
+
const { checkpoint, calculation, expression, continueOnFalse } = node.config.condition ?? {};
|
|
84
|
+
if ((calculation || expression) && !checkpoint) {
|
|
85
|
+
const condition = calculateCondition(node, processor);
|
|
86
|
+
if (!condition && !continueOnFalse) {
|
|
87
|
+
job.set({
|
|
88
|
+
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
|
89
|
+
result: { looped, broken: true }
|
|
90
|
+
});
|
|
91
|
+
return job;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
65
95
|
await processor.run(branch, job);
|
|
66
96
|
return null;
|
|
67
97
|
}
|
|
@@ -73,30 +103,56 @@ class LoopInstruction_default extends import_plugin_workflow.Instruction {
|
|
|
73
103
|
if (status !== import_plugin_workflow.JOB_STATUS.PENDING) {
|
|
74
104
|
return processor.exit();
|
|
75
105
|
}
|
|
76
|
-
const nextIndex = result + 1;
|
|
106
|
+
const nextIndex = result.looped + 1;
|
|
77
107
|
const target = processor.getParsedValue(loop.config.target, node.id);
|
|
78
|
-
if (branchJob.status
|
|
79
|
-
job.set({ result: nextIndex });
|
|
108
|
+
if (branchJob.status === import_plugin_workflow.JOB_STATUS.RESOLVED || branchJob.status < import_plugin_workflow.JOB_STATUS.PENDING && loop.config.exit === import_constants.EXIT.CONTINUE) {
|
|
109
|
+
job.set({ result: { looped: nextIndex } });
|
|
110
|
+
await processor.saveJob(job);
|
|
111
|
+
if (loop.config.condition) {
|
|
112
|
+
const { calculation, expression, continueOnFalse } = loop.config.condition ?? {};
|
|
113
|
+
if (calculation || expression) {
|
|
114
|
+
const condition = calculateCondition(loop, processor);
|
|
115
|
+
if (!condition && !continueOnFalse) {
|
|
116
|
+
job.set({
|
|
117
|
+
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
|
118
|
+
result: { looped: nextIndex, broken: true }
|
|
119
|
+
});
|
|
120
|
+
return job;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
80
124
|
const length = getTargetLength(target);
|
|
81
125
|
if (nextIndex < length) {
|
|
82
126
|
await processor.saveJob(job);
|
|
83
127
|
await processor.run(branch, job);
|
|
84
|
-
return
|
|
128
|
+
return processor.exit();
|
|
129
|
+
} else {
|
|
130
|
+
job.set({
|
|
131
|
+
status: import_plugin_workflow.JOB_STATUS.RESOLVED
|
|
132
|
+
});
|
|
85
133
|
}
|
|
134
|
+
} else {
|
|
135
|
+
job.set(
|
|
136
|
+
loop.config.exit ? {
|
|
137
|
+
result: { looped: result.looped, broken: true },
|
|
138
|
+
status: import_plugin_workflow.JOB_STATUS.RESOLVED
|
|
139
|
+
} : {
|
|
140
|
+
status: branchJob.status
|
|
141
|
+
}
|
|
142
|
+
);
|
|
86
143
|
}
|
|
87
|
-
job.set({
|
|
88
|
-
status: branchJob.status
|
|
89
|
-
});
|
|
90
144
|
return job;
|
|
91
145
|
}
|
|
92
|
-
getScope(node,
|
|
146
|
+
getScope(node, { looped }, processor) {
|
|
93
147
|
const target = processor.getParsedValue(node.config.target, node.id);
|
|
94
148
|
const targets = (Array.isArray(target) ? target : [target]).filter((t) => t != null);
|
|
95
149
|
const length = getTargetLength(target);
|
|
96
|
-
const
|
|
150
|
+
const index = looped;
|
|
151
|
+
const item = typeof target === "number" ? index : targets[looped];
|
|
97
152
|
const result = {
|
|
98
153
|
item,
|
|
99
154
|
index,
|
|
155
|
+
sequence: index + 1,
|
|
100
156
|
length
|
|
101
157
|
};
|
|
102
158
|
return result;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* This file is part of the NocoBase (R) project.
|
|
11
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
12
|
+
* Authors: NocoBase Team.
|
|
13
|
+
*
|
|
14
|
+
* This program is offered under a commercial license.
|
|
15
|
+
* For more information, see <https://www.nocobase.com/agreement>
|
|
16
|
+
*/
|
|
17
|
+
import { Migration } from '@nocobase/server';
|
|
18
|
+
export default class extends Migration {
|
|
19
|
+
up(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var change_loop_result_exports = {};
|
|
28
|
+
__export(change_loop_result_exports, {
|
|
29
|
+
default: () => change_loop_result_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(change_loop_result_exports);
|
|
32
|
+
var import_server = require("@nocobase/server");
|
|
33
|
+
class change_loop_result_default extends import_server.Migration {
|
|
34
|
+
async up() {
|
|
35
|
+
const { db, app } = this.context;
|
|
36
|
+
const JobRepo = db.getRepository("jobs");
|
|
37
|
+
await db.sequelize.transaction(async (transaction) => {
|
|
38
|
+
const records = await JobRepo.find({
|
|
39
|
+
filter: {
|
|
40
|
+
node: {
|
|
41
|
+
type: "loop"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
transaction
|
|
45
|
+
});
|
|
46
|
+
app.logger.debug(`${records.length} records need to be migrated.`);
|
|
47
|
+
for (const record of records) {
|
|
48
|
+
const { result } = record;
|
|
49
|
+
if (typeof result === "number") {
|
|
50
|
+
await record.update({ result: { looped: result } }, { transaction });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"displayName.zh-CN": "工作流:循环节点",
|
|
5
5
|
"description": "Used to repeat the sub-process processing of each value in an array, and can also be used for fixed times of sub-process processing.",
|
|
6
6
|
"description.zh-CN": "用于对一个数组中的每个值进行重复的子流程处理,也可用于固定次数的重复子流程处理。",
|
|
7
|
-
"version": "1.
|
|
7
|
+
"version": "1.4.0-alpha.0",
|
|
8
8
|
"license": "AGPL-3.0",
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
10
|
"homepage": "https://docs.nocobase.com/handbook/workflow-loop",
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"@nocobase/client": "1.x",
|
|
19
19
|
"@nocobase/database": "1.x",
|
|
20
|
+
"@nocobase/evaluators": "1.x",
|
|
20
21
|
"@nocobase/plugin-workflow": ">=0.17.0-alpha.3",
|
|
21
22
|
"@nocobase/server": "1.x",
|
|
22
23
|
"@nocobase/test": "1.x"
|
|
23
24
|
},
|
|
24
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "8ffa7b54bbaf720c0c9857da4b19a99110dffc4b",
|
|
25
26
|
"keywords": [
|
|
26
27
|
"Workflow"
|
|
27
28
|
]
|
|
File without changes
|
|
File without changes
|