@coding-flow/flow-design 0.1.1 → 0.1.3

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.
Files changed (35) hide show
  1. package/dist/components/design-editor/node-components/strategy/advice.js +19 -0
  2. package/dist/components/design-editor/node-components/strategy/error-trigger.d.ts +5 -3
  3. package/dist/components/design-editor/node-components/strategy/error-trigger.js +18 -26
  4. package/dist/components/design-editor/node-components/strategy/error_trigger.module.js +7 -0
  5. package/dist/components/design-editor/node-components/strategy/error_trigger_module.css +8 -0
  6. package/dist/components/design-editor/node-components/strategy/sub-process.d.ts +1 -0
  7. package/dist/components/design-editor/node-components/strategy/sub-process.js +67 -10
  8. package/dist/components/design-editor/node-components/strategy/sub_process.module.js +7 -0
  9. package/dist/components/design-editor/node-components/strategy/sub_process_module.css +8 -0
  10. package/dist/components/design-editor/nodes/start/index.js +1 -2
  11. package/dist/components/design-editor/nodes/sub-process/form-meta.js +8 -2
  12. package/dist/components/design-panel/tabs/setting.js +41 -4
  13. package/dist/components/design-panel/types.d.ts +1 -0
  14. package/dist/plugins/error-trigger-view-type.d.ts +2 -0
  15. package/dist/plugins/view/error-trigger-view.js +6 -9
  16. package/dist/plugins/view/error_trigger_view.module.js +5 -0
  17. package/dist/plugins/view/error_trigger_view_module.css +4 -0
  18. package/dist/script-components/components/form-data/index.js +7 -3
  19. package/dist/script-components/components/form-data/store.d.ts +8 -2
  20. package/dist/script-components/components/form-data/store.js +6 -6
  21. package/dist/script-components/components/sub-process/index.js +132 -45
  22. package/dist/script-components/components/sub-process/presenters/sub-process-presenter.d.ts +18 -3
  23. package/dist/script-components/components/sub-process/presenters/sub-process-presenter.js +43 -10
  24. package/dist/script-components/components/sub-process/sub_process.module.js +7 -0
  25. package/dist/script-components/components/sub-process/sub_process_module.css +4 -0
  26. package/dist/script-components/components/sub-process/typings/index.d.ts +9 -0
  27. package/dist/script-components/default-script.d.ts +1 -0
  28. package/dist/script-components/default-script.js +7 -1
  29. package/dist/script-components/modal/error-trigger-config-modal.d.ts +2 -0
  30. package/dist/script-components/modal/error-trigger-config-modal.js +6 -2
  31. package/dist/script-components/modal/sub_process_result_config_modal.d.ts +11 -0
  32. package/dist/script-components/modal/sub_process_result_config_modal.js +24 -0
  33. package/dist/script-components/typings/script.d.ts +2 -0
  34. package/dist/script-components/typings/script.js +1 -0
  35. package/package.json +7 -7
@@ -49,6 +49,25 @@ const AdviceStrategy = ()=>{
49
49
  })
50
50
  })
51
51
  })
52
+ }),
53
+ /*#__PURE__*/ jsx(Form.Item, {
54
+ label: /*#__PURE__*/ jsx(FieldTip, {
55
+ label: "隐藏审批意见",
56
+ description: "开启后,审批时不展示审批意见输入框,仅弹框确认。"
57
+ }),
58
+ name: [
59
+ "AdviceStrategy",
60
+ "adviceHidden"
61
+ ],
62
+ children: /*#__PURE__*/ jsx(Field, {
63
+ name: "AdviceStrategy.adviceHidden",
64
+ render: ({ field: { value, onChange } })=>/*#__PURE__*/ jsx(Fragment, {
65
+ children: /*#__PURE__*/ jsx(Switch, {
66
+ value: value,
67
+ onChange: onChange
68
+ })
69
+ })
70
+ })
52
71
  })
53
72
  ]
54
73
  });
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
+ export interface ErrorTriggerStrategyProps {
3
+ nodeOnly?: boolean;
4
+ }
2
5
  /**
3
- * 错误触发策略配置(没有匹配到人时)
4
- * @constructor
6
+ * 错误触发策略配置。
5
7
  */
6
- export declare const ErrorTriggerStrategy: React.FC;
8
+ export declare const ErrorTriggerStrategy: React.FC<ErrorTriggerStrategyProps>;
@@ -1,55 +1,44 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import react from "react";
3
- import { Button, Form, Space } from "antd";
3
+ import { EditOutlined } from "@ant-design/icons";
4
4
  import { Field } from "@flowgram.ai/fixed-layout-editor";
5
+ import { Button, Form, Space } from "antd";
6
+ import { FieldTip } from "../../../field-tip/index.js";
7
+ import { GroovyScriptLoader } from "../../../../script-components/components/groovy-script-loader.js";
5
8
  import { GroovyScriptPreview } from "../../../../script-components/components/groovy-script-preview.js";
6
- import { EditOutlined } from "@ant-design/icons";
7
9
  import { ErrorTriggerConfigModal } from "../../../../script-components/modal/error-trigger-config-modal.js";
8
- import { GroovyScriptLoader } from "../../../../script-components/components/groovy-script-loader.js";
9
- import { FieldTip } from "../../../field-tip/index.js";
10
+ import error_trigger_module from "./error_trigger.module.js";
10
11
  const ErrorTriggerConfigContent = (props)=>{
11
12
  const [visible, setVisible] = react.useState(false);
12
13
  const value = props.value || '';
13
14
  return /*#__PURE__*/ jsxs(Space.Compact, {
14
- style: {
15
- width: '100%'
16
- },
15
+ className: error_trigger_module.scriptInput,
17
16
  children: [
18
17
  /*#__PURE__*/ jsx(GroovyScriptPreview, {
19
18
  script: value
20
19
  }),
21
20
  /*#__PURE__*/ jsx(Button, {
22
21
  icon: /*#__PURE__*/ jsx(EditOutlined, {}),
23
- onClick: ()=>{
24
- setVisible(true);
25
- },
26
- style: {
27
- borderRadius: '0 6px 6px 0'
28
- },
22
+ className: error_trigger_module.editButton,
23
+ onClick: ()=>setVisible(true),
29
24
  children: "编辑"
30
25
  }),
31
26
  /*#__PURE__*/ jsx(ErrorTriggerConfigModal, {
32
27
  open: visible,
33
- onCancel: ()=>{
34
- setVisible(false);
35
- },
36
- onConfirm: (value)=>{
37
- props.onChange?.(value);
38
- },
28
+ onCancel: ()=>setVisible(false),
29
+ onConfirm: (script)=>props.onChange?.(script),
39
30
  script: value,
40
- scriptKey: props.scriptKey
31
+ scriptKey: props.scriptKey,
32
+ nodeOnly: props.nodeOnly
41
33
  })
42
34
  ]
43
35
  });
44
36
  };
45
- const ErrorTriggerStrategy = ()=>{
37
+ const ErrorTriggerStrategy = (props)=>{
46
38
  const [form] = Form.useForm();
47
- const [visible, setVisible] = react.useState(false);
48
39
  return /*#__PURE__*/ jsx(Form, {
49
40
  form: form,
50
- style: {
51
- width: '100%'
52
- },
41
+ className: error_trigger_module.form,
53
42
  layout: "vertical",
54
43
  children: /*#__PURE__*/ jsx(Form.Item, {
55
44
  label: /*#__PURE__*/ jsx(FieldTip, {
@@ -63,7 +52,10 @@ const ErrorTriggerStrategy = ()=>{
63
52
  children: /*#__PURE__*/ jsx(Field, {
64
53
  name: "ErrorTriggerStrategy.script",
65
54
  render: ({ field: { value, onChange } })=>/*#__PURE__*/ jsx(GroovyScriptLoader, {
66
- content: ErrorTriggerConfigContent,
55
+ content: (contentProps)=>/*#__PURE__*/ jsx(ErrorTriggerConfigContent, {
56
+ ...contentProps,
57
+ nodeOnly: props.nodeOnly
58
+ }),
67
59
  value: value,
68
60
  onChange: onChange
69
61
  })
@@ -0,0 +1,7 @@
1
+ import "./error_trigger_module.css";
2
+ const error_trigger_module = {
3
+ form: "form-y_des8",
4
+ scriptInput: "scriptInput-LTvfQq",
5
+ editButton: "editButton-XvwDWn"
6
+ };
7
+ export { error_trigger_module as default };
@@ -0,0 +1,8 @@
1
+ .form-y_des8, .scriptInput-LTvfQq {
2
+ width: 100%;
3
+ }
4
+
5
+ .editButton-XvwDWn {
6
+ border-radius: 0 6px 6px 0;
7
+ }
8
+
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ export declare const normalizeShowParentProcessRecords: (value?: boolean) => boolean;
2
3
  /**
3
4
  * 子流程任务策略
4
5
  * @constructor
@@ -7,13 +7,14 @@ import { EditOutlined } from "@ant-design/icons";
7
7
  import { SubProcessConfigModal } from "../../../../script-components/modal/sub-process-config-modal.js";
8
8
  import { GroovyScriptLoader } from "../../../../script-components/components/groovy-script-loader.js";
9
9
  import { FieldTip } from "../../../field-tip/index.js";
10
+ import { SubProcessResultConfigModal } from "../../../../script-components/modal/sub_process_result_config_modal.js";
11
+ import sub_process_module from "./sub_process.module.js";
12
+ const normalizeShowParentProcessRecords = (value)=>true === value;
10
13
  const SubProcessScriptContent = (props)=>{
11
14
  const [visible, setVisible] = react.useState(false);
12
15
  const value = props.value || '';
13
16
  return /*#__PURE__*/ jsxs(Space.Compact, {
14
- style: {
15
- width: '100%'
16
- },
17
+ className: sub_process_module.scriptInput,
17
18
  children: [
18
19
  /*#__PURE__*/ jsx(GroovyScriptPreview, {
19
20
  script: value
@@ -23,9 +24,7 @@ const SubProcessScriptContent = (props)=>{
23
24
  onClick: ()=>{
24
25
  setVisible(true);
25
26
  },
26
- style: {
27
- borderRadius: '0 6px 6px 0'
28
- },
27
+ className: sub_process_module.editButton,
29
28
  children: "编辑"
30
29
  }),
31
30
  /*#__PURE__*/ jsx(SubProcessConfigModal, {
@@ -42,13 +41,36 @@ const SubProcessScriptContent = (props)=>{
42
41
  ]
43
42
  });
44
43
  };
44
+ const SubProcessResultScriptContent = (props)=>{
45
+ const [visible, setVisible] = react.useState(false);
46
+ const value = props.value || '';
47
+ return /*#__PURE__*/ jsxs(Space.Compact, {
48
+ className: sub_process_module.scriptInput,
49
+ children: [
50
+ /*#__PURE__*/ jsx(GroovyScriptPreview, {
51
+ script: value
52
+ }),
53
+ /*#__PURE__*/ jsx(Button, {
54
+ icon: /*#__PURE__*/ jsx(EditOutlined, {}),
55
+ className: sub_process_module.editButton,
56
+ onClick: ()=>setVisible(true),
57
+ children: "编辑"
58
+ }),
59
+ /*#__PURE__*/ jsx(SubProcessResultConfigModal, {
60
+ open: visible,
61
+ onCancel: ()=>setVisible(false),
62
+ onConfirm: (script)=>props.onChange?.(script),
63
+ script: value,
64
+ scriptKey: props.scriptKey
65
+ })
66
+ ]
67
+ });
68
+ };
45
69
  const SubProcessStrategy = ()=>{
46
70
  const [form] = Form.useForm();
47
71
  return /*#__PURE__*/ jsxs(Form, {
48
72
  form: form,
49
- style: {
50
- width: '100%'
51
- },
73
+ className: sub_process_module.form,
52
74
  layout: "vertical",
53
75
  children: [
54
76
  /*#__PURE__*/ jsx(Form.Item, {
@@ -69,6 +91,24 @@ const SubProcessStrategy = ()=>{
69
91
  })
70
92
  })
71
93
  }),
94
+ /*#__PURE__*/ jsx(Form.Item, {
95
+ label: /*#__PURE__*/ jsx(FieldTip, {
96
+ label: "结果判定脚本",
97
+ description: "每个子流程结束时执行;返回 true 则主流程继续,全部结束仍为 false 则执行异常跳转。"
98
+ }),
99
+ name: [
100
+ "SubProcessStrategy",
101
+ "resultScript"
102
+ ],
103
+ children: /*#__PURE__*/ jsx(Field, {
104
+ name: "SubProcessStrategy.resultScript",
105
+ render: ({ field: { value, onChange } })=>/*#__PURE__*/ jsx(GroovyScriptLoader, {
106
+ content: SubProcessResultScriptContent,
107
+ value: value,
108
+ onChange: onChange
109
+ })
110
+ })
111
+ }),
72
112
  /*#__PURE__*/ jsx(Form.Item, {
73
113
  label: /*#__PURE__*/ jsx(FieldTip, {
74
114
  label: "创建后并提交",
@@ -87,8 +127,25 @@ const SubProcessStrategy = ()=>{
87
127
  })
88
128
  })
89
129
  })
130
+ }),
131
+ /*#__PURE__*/ jsx(Form.Item, {
132
+ label: /*#__PURE__*/ jsx(FieldTip, {
133
+ label: "展示主流程记录",
134
+ description: "开启后,子流程参与人可在节点记录中查看进入当前子流程前的主流程实际历史,包括审批人和审批意见;默认关闭。"
135
+ }),
136
+ name: [
137
+ "SubProcessStrategy",
138
+ "showParentProcessRecords"
139
+ ],
140
+ children: /*#__PURE__*/ jsx(Field, {
141
+ name: "SubProcessStrategy.showParentProcessRecords",
142
+ render: ({ field: { value, onChange } })=>/*#__PURE__*/ jsx(Switch, {
143
+ value: normalizeShowParentProcessRecords(value),
144
+ onChange: onChange
145
+ })
146
+ })
90
147
  })
91
148
  ]
92
149
  });
93
150
  };
94
- export { SubProcessStrategy };
151
+ export { SubProcessStrategy, normalizeShowParentProcessRecords };
@@ -0,0 +1,7 @@
1
+ import "./sub_process_module.css";
2
+ const sub_process_module = {
3
+ form: "form-XvmZT3",
4
+ scriptInput: "scriptInput-Iyb2Gu",
5
+ editButton: "editButton-MPsvjc"
6
+ };
7
+ export { sub_process_module as default };
@@ -0,0 +1,8 @@
1
+ .form-XvmZT3, .scriptInput-Iyb2Gu {
2
+ width: 100%;
3
+ }
4
+
5
+ .editButton-MPsvjc {
6
+ border-radius: 0 6px 6px 0;
7
+ }
8
+
@@ -7,8 +7,7 @@ const StartNodeRegistry = {
7
7
  selectable: false,
8
8
  copyDisable: true,
9
9
  expandable: false,
10
- addDisable: true,
11
- editTitleDisable: true
10
+ addDisable: true
12
11
  },
13
12
  info: {
14
13
  icon: 'START',
@@ -8,14 +8,20 @@ import { PanelLayout } from "../../node-components/layout/index.js";
8
8
  import { SubProcessStrategy } from "../../node-components/strategy/sub-process.js";
9
9
  import "react";
10
10
  import { NodeHint } from "../../node-components/node-hint/index.js";
11
+ import { ErrorTriggerStrategy } from "../../node-components/strategy/error-trigger.js";
11
12
  const renderForm = (data)=>{
12
13
  const isSidebar = useIsSidebar();
13
14
  if (isSidebar) return /*#__PURE__*/ jsxs(NodePanel, {
14
15
  data: data,
15
16
  children: [
16
17
  /*#__PURE__*/ jsx(NodeHeader, {}),
17
- /*#__PURE__*/ jsx(PanelLayout, {
18
- children: /*#__PURE__*/ jsx(SubProcessStrategy, {})
18
+ /*#__PURE__*/ jsxs(PanelLayout, {
19
+ children: [
20
+ /*#__PURE__*/ jsx(SubProcessStrategy, {}),
21
+ /*#__PURE__*/ jsx(ErrorTriggerStrategy, {
22
+ nodeOnly: true
23
+ })
24
+ ]
19
25
  })
20
26
  ]
21
27
  });
@@ -1,6 +1,8 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import react from "react";
3
+ import { InputNumber } from "antd";
3
4
  import { CardForm, Panel } from "@coding-flow/flow-pc-ui";
5
+ import { FieldTip } from "../../field-tip/index.js";
4
6
  import { InterferePanel } from "../panels/workflow/interfere.js";
5
7
  import { UrgePanel } from "../panels/workflow/urge.js";
6
8
  import { useDesignContext } from "../hooks/use-design-context.js";
@@ -12,22 +14,37 @@ const TabSetting = ()=>{
12
14
  const workflowStrategyManager = new WorkflowStrategyManager();
13
15
  const resetFieldsValue = ()=>{
14
16
  const formData = workflowStrategyManager.toRender(state.workflow.strategies);
15
- form.setFieldsValue(formData);
17
+ form.setFieldsValue({
18
+ ...formData,
19
+ maxNestDepth: state.workflow.maxNestDepth ?? 10
20
+ });
16
21
  };
17
22
  react.useEffect(()=>{
18
23
  resetFieldsValue();
19
24
  }, [
20
- state.workflow.strategies
25
+ state.workflow.strategies,
26
+ state.workflow.maxNestDepth
21
27
  ]);
22
28
  react.useEffect(()=>{
23
29
  form.resetFields();
24
30
  resetFieldsValue();
25
31
  formActionContext.addAction({
26
- save: ()=>workflowStrategyManager.toData(form.getFieldsValue()),
32
+ save: ()=>{
33
+ const formValues = form.getFieldsValue();
34
+ const strategyData = workflowStrategyManager.toData(formValues);
35
+ return {
36
+ ...strategyData || {},
37
+ maxNestDepth: formValues.maxNestDepth
38
+ };
39
+ },
27
40
  key: ()=>'setting',
28
41
  validate: ()=>new Promise((resolve, reject)=>{
29
42
  form.validateFields().then((values)=>{
30
- resolve(workflowStrategyManager.toData(values));
43
+ const strategyData = workflowStrategyManager.toData(values);
44
+ resolve({
45
+ ...strategyData || {},
46
+ maxNestDepth: values.maxNestDepth
47
+ });
31
48
  }).catch(reject);
32
49
  })
33
50
  });
@@ -42,6 +59,26 @@ const TabSetting = ()=>{
42
59
  }),
43
60
  /*#__PURE__*/ jsx(UrgePanel, {
44
61
  form: form
62
+ }),
63
+ /*#__PURE__*/ jsx(CardForm, {
64
+ form: form,
65
+ title: "循环防护配置",
66
+ children: /*#__PURE__*/ jsx(CardForm.Item, {
67
+ name: [
68
+ "maxNestDepth"
69
+ ],
70
+ label: /*#__PURE__*/ jsx(FieldTip, {
71
+ label: "最大嵌套深度",
72
+ description: "子流程嵌套层数上限(默认 10)。运行期创建子流程时沿父链统计嵌套层数,超过该值将拒绝创建,用于兜底防护子流程创建自身/祖先流程等循环配置导致的无限递归。"
73
+ }),
74
+ children: /*#__PURE__*/ jsx(InputNumber, {
75
+ min: 1,
76
+ max: 100,
77
+ style: {
78
+ width: '100%'
79
+ }
80
+ })
81
+ })
45
82
  })
46
83
  ]
47
84
  });
@@ -16,6 +16,7 @@ export interface Workflow {
16
16
  operatorCreateScript: string;
17
17
  strategies?: any[];
18
18
  nodes?: FlowNode[];
19
+ maxNestDepth?: number;
19
20
  }
20
21
  export interface FlowNode {
21
22
  id: string;
@@ -13,4 +13,6 @@ export interface ErrorTriggerViewPlugin {
13
13
  onChange: (script: string) => void;
14
14
  /** 动作控制 **/
15
15
  action?: React.Ref<DesignViewPluginAction>;
16
+ /** 是否仅允许选择跳转节点 */
17
+ nodeOnly?: boolean;
16
18
  }
@@ -9,6 +9,7 @@ import { useNodeRouterManager } from "../../components/design-panel/hooks/use-no
9
9
  import { useNodeRenderContext } from "../../components/design-editor/hooks/use-node-render-context.js";
10
10
  import { ErrorTriggerScriptUtils } from "../../script-components/services/node-error-trigger.js";
11
11
  import { useScriptMetaData } from "../../script-components/hooks/use-script-meta-data.js";
12
+ import error_trigger_view_module from "./error_trigger_view.module.js";
12
13
  const ErrorTriggerPluginView = (props)=>{
13
14
  const ErrorTriggerPluginViewComponent = ViewBindPlugin.getInstance().get(VIEW_KEY);
14
15
  const [type, setType] = react.useState('node');
@@ -26,7 +27,7 @@ const ErrorTriggerPluginView = (props)=>{
26
27
  ...mappingData
27
28
  },
28
29
  children: [
29
- /*#__PURE__*/ jsx(Form.Item, {
30
+ !props.nodeOnly && /*#__PURE__*/ jsx(Form.Item, {
30
31
  name: "type",
31
32
  label: "触发类型",
32
33
  children: /*#__PURE__*/ jsx(Select, {
@@ -40,12 +41,10 @@ const ErrorTriggerPluginView = (props)=>{
40
41
  value: 'user'
41
42
  }
42
43
  ],
43
- onChange: (value)=>{
44
- setType(value);
45
- }
44
+ onChange: setType
46
45
  })
47
46
  }),
48
- "node" === type && /*#__PURE__*/ jsx(Form.Item, {
47
+ (props.nodeOnly || "node" === type) && /*#__PURE__*/ jsx(Form.Item, {
49
48
  name: "node",
50
49
  label: "指定节点",
51
50
  children: /*#__PURE__*/ jsx(Select, {
@@ -56,15 +55,13 @@ const ErrorTriggerPluginView = (props)=>{
56
55
  }
57
56
  })
58
57
  }),
59
- "user" === type && /*#__PURE__*/ jsx("div", {
58
+ !props.nodeOnly && "user" === type && /*#__PURE__*/ jsx("div", {
60
59
  children: "选择人员:暂不支持"
61
60
  })
62
61
  ]
63
62
  }),
64
63
  /*#__PURE__*/ jsxs(Space, {
65
- style: {
66
- marginTop: 8
67
- },
64
+ className: error_trigger_view_module.actions,
68
65
  children: [
69
66
  /*#__PURE__*/ jsx(Button, {
70
67
  icon: /*#__PURE__*/ jsx(CodeOutlined, {}),
@@ -0,0 +1,5 @@
1
+ import "./error_trigger_view_module.css";
2
+ const error_trigger_view_module = {
3
+ actions: "actions-EN8Hw_"
4
+ };
5
+ export { error_trigger_view_module as default };
@@ -0,0 +1,4 @@
1
+ .actions-EN8Hw_ {
2
+ margin-top: 8px;
3
+ }
4
+
@@ -2,7 +2,7 @@ import { Fragment, jsx } from "react/jsx-runtime";
2
2
  import react from "react";
3
3
  import { FormDataList } from "./components/list.js";
4
4
  import { Provider } from "react-redux";
5
- import { formDataStore } from "./store.js";
5
+ import { createFormDataStore } from "./store.js";
6
6
  import { FormDataContext } from "./context/index.js";
7
7
  import { createFormDataContext, useFormDataContext } from "./hooks/use-form-data-context.js";
8
8
  const FormDataContent = (props)=>{
@@ -28,12 +28,16 @@ const FormDataContextContent = (props)=>{
28
28
  })
29
29
  });
30
30
  };
31
- const FormDataReduxContent = (props)=>/*#__PURE__*/ jsx(Provider, {
32
- store: formDataStore,
31
+ const FormDataReduxContent = (props)=>{
32
+ const storeRef = react.useRef(void 0);
33
+ if (!storeRef.current) storeRef.current = createFormDataStore();
34
+ return /*#__PURE__*/ jsx(Provider, {
35
+ store: storeRef.current,
33
36
  children: /*#__PURE__*/ jsx(FormDataContextContent, {
34
37
  ...props
35
38
  })
36
39
  });
40
+ };
37
41
  const FormDataView = (props)=>{
38
42
  const form = props.form;
39
43
  if (form) return /*#__PURE__*/ jsx(FormDataReduxContent, {
@@ -5,11 +5,17 @@ export type FormDataStoreAction = {
5
5
  };
6
6
  export declare const formDataSlice: import("@reduxjs/toolkit").Slice<FormDataState, FormDataStoreAction, "formData", "formData", {}>;
7
7
  export declare const updateState: import("@reduxjs/toolkit").ActionCreatorWithPayload<Partial<FormDataState> | ((prev: FormDataState) => Partial<FormDataState>), "formData/updateState">;
8
- export declare const formDataStore: import("@reduxjs/toolkit").EnhancedStore<{
8
+ /**
9
+ * 按实例创建表单数据 store。
10
+ * 每个 FormDataView 使用独立的 store,避免多个子流程配置项共享模块级单例 store
11
+ * 导致表单数据互相覆盖、输入时被刷新。
12
+ */
13
+ export declare const createFormDataStore: () => import("@reduxjs/toolkit").EnhancedStore<{
9
14
  formData: FormDataState;
10
15
  }, import("redux").UnknownAction, import("@reduxjs/toolkit").Tuple<[import("redux").StoreEnhancer<{
11
16
  dispatch: import("redux-thunk").ThunkDispatch<{
12
17
  formData: FormDataState;
13
18
  }, undefined, import("redux").UnknownAction>;
14
19
  }>, import("redux").StoreEnhancer]>>;
15
- export type FormDataReduxState = ReturnType<typeof formDataStore.getState>;
20
+ export type FormDataStore = ReturnType<typeof createFormDataStore>;
21
+ export type FormDataReduxState = ReturnType<FormDataStore['getState']>;
@@ -13,9 +13,9 @@ const formDataSlice = createSlice({
13
13
  }
14
14
  });
15
15
  const { updateState } = formDataSlice.actions;
16
- const formDataStore = configureStore({
17
- reducer: {
18
- formData: formDataSlice.reducer
19
- }
20
- });
21
- export { formDataSlice, formDataStore, updateState };
16
+ const createFormDataStore = ()=>configureStore({
17
+ reducer: {
18
+ formData: formDataSlice.reducer
19
+ }
20
+ });
21
+ export { createFormDataStore, formDataSlice, updateState };
@@ -1,70 +1,157 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import react from "react";
3
- import { Form, Select } from "antd";
3
+ import { Button, Card, Form, Select, Space } from "antd";
4
4
  import { useTargetWorkflowPresenter } from "./hooks/use-target-workflow-presenter.js";
5
5
  import { SubProcessOperatorPluginView } from "../../../plugins/view/sub-process-opreator-view.js";
6
6
  import { FormDataView } from "../form-data/index.js";
7
7
  import { useSubProcessPresenter } from "./hooks/use-sub-process-presenter.js";
8
+ import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
9
+ import sub_process_module from "./sub_process.module.js";
10
+ const SubProcessConfigItem = ({ form, index, removable, remove })=>{
11
+ const { state, presenter } = useTargetWorkflowPresenter();
12
+ const workId = Form.useWatch([
13
+ 'processes',
14
+ index,
15
+ 'workId'
16
+ ], form);
17
+ react.useEffect(()=>{
18
+ if (workId) presenter.setCurrentWorkId(workId);
19
+ }, [
20
+ presenter,
21
+ workId
22
+ ]);
23
+ return /*#__PURE__*/ jsxs(Card, {
24
+ className: sub_process_module.configCard,
25
+ size: "small",
26
+ title: `子流程 ${index + 1}`,
27
+ extra: /*#__PURE__*/ jsx(Button, {
28
+ type: "text",
29
+ danger: true,
30
+ disabled: !removable,
31
+ icon: /*#__PURE__*/ jsx(DeleteOutlined, {}),
32
+ "aria-label": `删除子流程 ${index + 1}`,
33
+ onClick: ()=>remove(index)
34
+ }),
35
+ children: [
36
+ /*#__PURE__*/ jsx(Form.Item, {
37
+ name: [
38
+ index,
39
+ "workId"
40
+ ],
41
+ label: "发起流程",
42
+ rules: [
43
+ {
44
+ required: true,
45
+ message: '请选择发起流程'
46
+ }
47
+ ],
48
+ children: /*#__PURE__*/ jsx(Select, {
49
+ placeholder: "请选择发起流程",
50
+ options: state.workflows
51
+ })
52
+ }),
53
+ /*#__PURE__*/ jsx(Form.Item, {
54
+ name: [
55
+ index,
56
+ "actionId"
57
+ ],
58
+ label: "触发动作",
59
+ rules: [
60
+ {
61
+ required: true,
62
+ message: '请选择触发动作'
63
+ }
64
+ ],
65
+ children: /*#__PURE__*/ jsx(Select, {
66
+ placeholder: "请选择触发动作",
67
+ options: (state.actions ?? []).map((item)=>({
68
+ key: item.actionId,
69
+ value: item.actionId,
70
+ label: item.title
71
+ }))
72
+ })
73
+ }),
74
+ /*#__PURE__*/ jsx(Form.Item, {
75
+ name: [
76
+ index,
77
+ "operatorId"
78
+ ],
79
+ label: "流程发起人",
80
+ rules: [
81
+ {
82
+ required: true,
83
+ message: '请选择流程发起人'
84
+ }
85
+ ],
86
+ children: /*#__PURE__*/ jsx(SubProcessOperatorPluginView, {})
87
+ }),
88
+ /*#__PURE__*/ jsx(Form.Item, {
89
+ name: [
90
+ index,
91
+ "formData"
92
+ ],
93
+ label: "流程数据",
94
+ children: /*#__PURE__*/ jsx(FormDataView, {
95
+ form: state.form
96
+ })
97
+ })
98
+ ]
99
+ });
100
+ };
8
101
  const SubProcessView = (props)=>{
9
102
  const [form] = Form.useForm();
10
- const { state, presenter } = useTargetWorkflowPresenter();
11
103
  const subProcessPresenter = useSubProcessPresenter(props);
12
104
  react.useEffect(()=>{
13
- if (props.value) {
105
+ if (subProcessPresenter.isExternalChange(props.value)) {
14
106
  const data = subProcessPresenter.parserScript(props.value);
15
- const workId = data.workId;
16
107
  form.resetFields();
17
- form.setFieldsValue(data);
18
- if (workId) presenter.setCurrentWorkId(workId);
108
+ form.setFieldsValue({
109
+ processes: data.processes.length > 0 ? data.processes : [
110
+ {}
111
+ ]
112
+ });
19
113
  }
20
114
  }, [
21
- props.value
115
+ form,
116
+ props.value,
117
+ subProcessPresenter
22
118
  ]);
23
119
  return /*#__PURE__*/ jsx(Fragment, {
24
- children: /*#__PURE__*/ jsxs(Form, {
120
+ children: /*#__PURE__*/ jsx(Form, {
25
121
  form: form,
26
122
  layout: "vertical",
123
+ initialValues: {
124
+ processes: [
125
+ {}
126
+ ]
127
+ },
27
128
  onValuesChange: ()=>{
28
- const values = form.getFieldsValue();
129
+ const values = form.getFieldsValue(true);
29
130
  subProcessPresenter.updateScript(values);
30
131
  },
31
- children: [
32
- /*#__PURE__*/ jsx(Form.Item, {
33
- name: "workId",
34
- label: "发起流程",
35
- children: /*#__PURE__*/ jsx(Select, {
36
- placeholder: "请选择发起流程",
37
- options: state.workflows,
38
- onChange: (value)=>{
39
- presenter.setCurrentWorkId(value);
40
- }
132
+ children: /*#__PURE__*/ jsx(Form.List, {
133
+ name: "processes",
134
+ children: (fields, { add, remove })=>/*#__PURE__*/ jsxs(Space, {
135
+ className: sub_process_module.configList,
136
+ direction: "vertical",
137
+ size: "middle",
138
+ children: [
139
+ fields.map((field)=>/*#__PURE__*/ jsx(SubProcessConfigItem, {
140
+ form: form,
141
+ index: field.name,
142
+ removable: fields.length > 1,
143
+ remove: remove
144
+ }, field.key)),
145
+ /*#__PURE__*/ jsx(Button, {
146
+ className: sub_process_module.addButton,
147
+ type: "dashed",
148
+ icon: /*#__PURE__*/ jsx(PlusOutlined, {}),
149
+ onClick: ()=>add({}),
150
+ children: "添加子流程"
151
+ })
152
+ ]
41
153
  })
42
- }),
43
- /*#__PURE__*/ jsx(Form.Item, {
44
- name: "actionId",
45
- label: "触发动作",
46
- children: /*#__PURE__*/ jsx(Select, {
47
- placeholder: "请选择触发动作",
48
- options: (state.actions ?? []).map((item)=>({
49
- key: item.actionId,
50
- value: item.actionId,
51
- label: item.title
52
- }))
53
- })
54
- }),
55
- /*#__PURE__*/ jsx(Form.Item, {
56
- name: "operatorId",
57
- label: "流程发起人",
58
- children: /*#__PURE__*/ jsx(SubProcessOperatorPluginView, {})
59
- }),
60
- /*#__PURE__*/ jsx(Form.Item, {
61
- name: "formData",
62
- label: "流程数据",
63
- children: /*#__PURE__*/ jsx(FormDataView, {
64
- form: state.form
65
- })
66
- })
67
- ]
154
+ })
68
155
  })
69
156
  });
70
157
  };
@@ -1,9 +1,24 @@
1
- import { SubProcessViewProps } from "../typings";
1
+ import { SubProcessFormValues, SubProcessViewProps } from "../typings";
2
2
  export declare class SubProcessPresenter {
3
3
  private readonly props;
4
+ /**
5
+ * 自身最后一次生成的脚本。
6
+ * 用于区分"自身编辑触发的脚本变更"与"外部脚本变更",
7
+ * 避免编辑回填表单后再次重置表单,导致输入时表单自动刷新。
8
+ */
9
+ private lastEmittedScript?;
4
10
  constructor(props: SubProcessViewProps);
5
- parserScript(value: string): any;
6
- updateScript(values: any): void;
11
+ /**
12
+ * 判断脚本变更是否来自外部(非自身编辑产生)。
13
+ * 仅外部变更才需要将脚本解析结果回填到表单。
14
+ *
15
+ * @param value 当前脚本内容
16
+ */
17
+ isExternalChange(value: string | undefined): boolean;
18
+ parserScript(value: string): SubProcessFormValues;
19
+ updateScript(values: SubProcessFormValues): void;
7
20
  private toFormData;
8
21
  private toScript;
22
+ private isRecord;
23
+ private isSubProcessConfig;
9
24
  }
@@ -1,34 +1,67 @@
1
1
  import { GroovyScriptConvertorUtil } from "@coding-flow/flow-core";
2
2
  class SubProcessPresenter {
3
3
  props;
4
+ lastEmittedScript;
4
5
  constructor(props){
5
6
  this.props = props;
6
7
  }
8
+ isExternalChange(value) {
9
+ if (!value) return false;
10
+ return value !== this.lastEmittedScript;
11
+ }
7
12
  parserScript(value) {
8
13
  const meta = GroovyScriptConvertorUtil.getScriptMeta(value);
9
- if (meta) return JSON.parse(meta);
10
- return {};
14
+ if (!meta) return {
15
+ processes: []
16
+ };
17
+ const parsed = JSON.parse(meta);
18
+ if (!this.isRecord(parsed)) return {
19
+ processes: []
20
+ };
21
+ if (Array.isArray(parsed.processes)) return {
22
+ processes: parsed.processes.filter(this.isSubProcessConfig)
23
+ };
24
+ return this.isSubProcessConfig(parsed) ? {
25
+ processes: [
26
+ parsed
27
+ ]
28
+ } : {
29
+ processes: []
30
+ };
11
31
  }
12
32
  updateScript(values) {
13
- this.props.onChange(this.toScript(values));
33
+ const script = this.toScript(values);
34
+ this.lastEmittedScript = script;
35
+ this.props.onChange(script);
14
36
  }
15
- toFormData(values) {
16
- if (values && values?.formData) {
17
- const formData = JSON.parse(values.formData);
18
- const data = formData.dataBody?.data;
19
- if (data) return JSON.stringify(data);
37
+ toFormData(config) {
38
+ if (config.formData) {
39
+ const formData = JSON.parse(config.formData);
40
+ if (this.isRecord(formData) && this.isRecord(formData.dataBody)) {
41
+ const data = formData.dataBody.data;
42
+ if (data) return JSON.stringify(data);
43
+ }
20
44
  }
21
45
  return '';
22
46
  }
23
47
  toScript(values) {
24
48
  const meta = JSON.stringify(values);
25
- const formData = this.toFormData(values);
49
+ const requests = values.processes.map((config)=>{
50
+ const formData = this.toFormData(config);
51
+ return `request.toCreateRequest('${config.workId}', ${config.operatorId}, '${config.actionId}', '${formData}')`;
52
+ }).join(',\n ');
26
53
  return `
27
54
  // @SCRIPT_TITLE 子流程配置
28
55
  // @SCRIPT_META ${meta}
29
56
  def run(request){
30
- return request.toCreateRequest('${values.workId}', ${values.operatorId},'${values.actionId}', '${formData}');
57
+ return [
58
+ ${requests}
59
+ ];
31
60
  }`;
32
61
  }
62
+ isRecord(value) {
63
+ return 'object' == typeof value && null !== value;
64
+ }
65
+ isSubProcessConfig = (value)=>this.isRecord(value) && (void 0 === value.workId || 'string' == typeof value.workId) && (void 0 === value.actionId || 'string' == typeof value.actionId) && (void 0 === value.operatorId || 'string' == typeof value.operatorId || 'number' == typeof value.operatorId) && (void 0 === value.formData || 'string' == typeof value.formData);
33
66
  }
34
67
  export { SubProcessPresenter };
@@ -0,0 +1,7 @@
1
+ import "./sub_process_module.css";
2
+ const sub_process_module = {
3
+ configList: "configList-uCyYRt",
4
+ configCard: "configCard-avhsI0",
5
+ addButton: "addButton-yvy4ng"
6
+ };
7
+ export { sub_process_module as default };
@@ -0,0 +1,4 @@
1
+ .configList-uCyYRt, .configCard-avhsI0, .addButton-yvy4ng {
2
+ width: 100%;
3
+ }
4
+
@@ -6,6 +6,15 @@ export interface SubProcessViewProps {
6
6
  value: string;
7
7
  onChange: (value: string) => void;
8
8
  }
9
+ export interface SubProcessConfig {
10
+ workId?: string;
11
+ actionId?: string;
12
+ operatorId?: string | number;
13
+ formData?: string;
14
+ }
15
+ export interface SubProcessFormValues {
16
+ processes: SubProcessConfig[];
17
+ }
9
18
  export interface WorkflowMeta {
10
19
  workId: string;
11
20
  actions: ActionOption[];
@@ -9,3 +9,4 @@ export declare const SCRIPT_DEFAULT_ROUTER = "// @SCRIPT_TITLE \u53D1\u8D77\u828
9
9
  export declare const SCRIPT_DEFAULT_TRIGGER = "// @SCRIPT_TITLE \u793A\u4F8B\u89E6\u53D1\u8282\u70B9\uFF08\u6253\u5370\u89E6\u53D1\u65E5\u5FD7\uFF09 \ndef run(request){ \n print('hello trigger node.\\n'); \n}\n";
10
10
  export declare const SCRIPT_DEFAULT_CUSTOM = "// @SCRIPT_TITLE \u89E6\u53D1\u901A\u8FC7\n// @SCRIPT_META {\"trigger\":\"PASS\"}\ndef run(request){ \n return 'PASS'; \n}\n";
11
11
  export declare const SCRIPT_DEFAULT_SUB_PROCESS = "// @SCRIPT_TITLE \u521B\u5EFA\u5F53\u524D\u6D41\u7A0B \ndef run(request){ \n return request.toCreateRequest() \n}\n";
12
+ export declare const SCRIPT_DEFAULT_SUB_PROCESS_RESULT = "// @SCRIPT_TITLE \u5168\u90E8\u5B50\u6D41\u7A0B\u7ED3\u675F\u540E\u7EE7\u7EED\ndef run(request){\n return request.findSubProcessRecords(request.getCurrentNode().getId()).size()\n == request.getSubProcessTotal();\n}\n";
@@ -60,4 +60,10 @@ def run(request){
60
60
  return request.toCreateRequest()
61
61
  }
62
62
  `;
63
- export { SCRIPT_APPROVER_SELECT, SCRIPT_DEFAULT_CONDITION, SCRIPT_DEFAULT_CUSTOM, SCRIPT_DEFAULT_ERROR_TRIGGER, SCRIPT_DEFAULT_NODE_TITLE, SCRIPT_DEFAULT_OPERATOR_CREATE, SCRIPT_DEFAULT_OPERATOR_LOAD, SCRIPT_DEFAULT_ROUTER, SCRIPT_DEFAULT_SUB_PROCESS, SCRIPT_DEFAULT_TRIGGER, SCRIPT_INITIATOR_SELECT };
63
+ const SCRIPT_DEFAULT_SUB_PROCESS_RESULT = `// @SCRIPT_TITLE 全部子流程结束后继续
64
+ def run(request){
65
+ return request.findSubProcessRecords(request.getCurrentNode().getId()).size()
66
+ == request.getSubProcessTotal();
67
+ }
68
+ `;
69
+ export { SCRIPT_APPROVER_SELECT, SCRIPT_DEFAULT_CONDITION, SCRIPT_DEFAULT_CUSTOM, SCRIPT_DEFAULT_ERROR_TRIGGER, SCRIPT_DEFAULT_NODE_TITLE, SCRIPT_DEFAULT_OPERATOR_CREATE, SCRIPT_DEFAULT_OPERATOR_LOAD, SCRIPT_DEFAULT_ROUTER, SCRIPT_DEFAULT_SUB_PROCESS, SCRIPT_DEFAULT_SUB_PROCESS_RESULT, SCRIPT_DEFAULT_TRIGGER, SCRIPT_INITIATOR_SELECT };
@@ -13,5 +13,7 @@ export interface ErrorTriggerConfigModalProps {
13
13
  onCancel: () => void;
14
14
  /** 确认回调 */
15
15
  onConfirm: (script: string) => void;
16
+ /** 是否仅允许配置节点跳转 */
17
+ nodeOnly?: boolean;
16
18
  }
17
19
  export declare const ErrorTriggerConfigModal: React.FC<ErrorTriggerConfigModalProps>;
@@ -15,7 +15,8 @@ const ErrorTriggerConfigContent = (props)=>{
15
15
  resetScript: ()=>SCRIPT_DEFAULT_ERROR_TRIGGER
16
16
  }),
17
17
  !isAdvance && /*#__PURE__*/ jsx(ErrorTriggerPluginView, {
18
- ...props
18
+ ...props,
19
+ nodeOnly: props.nodeOnly
19
20
  })
20
21
  ]
21
22
  });
@@ -29,7 +30,10 @@ const ErrorTriggerConfigModal = (props)=>/*#__PURE__*/ jsx(GroovyScriptModal, {
29
30
  onConfirm: props.onConfirm,
30
31
  onCancel: props.onCancel,
31
32
  title: "异常配置",
32
- content: ErrorTriggerConfigContent,
33
+ content: (contentProps)=>/*#__PURE__*/ jsx(ErrorTriggerConfigContent, {
34
+ ...contentProps,
35
+ nodeOnly: props.nodeOnly
36
+ }),
33
37
  scriptKey: props.scriptKey
34
38
  });
35
39
  export { ErrorTriggerConfigModal };
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { GroovyVariableMapping } from "../typings";
3
+ export interface SubProcessResultConfigModalProps {
4
+ open: boolean;
5
+ script: string;
6
+ scriptKey: string;
7
+ variables?: GroovyVariableMapping[];
8
+ onCancel: () => void;
9
+ onConfirm: (script: string) => void;
10
+ }
11
+ export declare const SubProcessResultConfigModal: React.FC<SubProcessResultConfigModalProps>;
@@ -0,0 +1,24 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import "react";
3
+ import { AdvancedScriptEditor } from "../components/advanced-script-editor.js";
4
+ import { GroovyScriptModal } from "../components/groovy-script-modal.js";
5
+ import { SCRIPT_DEFAULT_SUB_PROCESS_RESULT } from "../default-script.js";
6
+ import { ScriptType } from "../typings/index.js";
7
+ const SubProcessResultConfigContent = (props)=>/*#__PURE__*/ jsx(AdvancedScriptEditor, {
8
+ ...props,
9
+ title: "子流程结果判定脚本",
10
+ resetScript: ()=>SCRIPT_DEFAULT_SUB_PROCESS_RESULT
11
+ });
12
+ const SubProcessResultConfigModal = (props)=>/*#__PURE__*/ jsx(GroovyScriptModal, {
13
+ type: ScriptType.SUB_PROCESS_RESULT,
14
+ open: props.open,
15
+ width: "70%",
16
+ script: props.script,
17
+ variables: props.variables ?? [],
18
+ onConfirm: props.onConfirm,
19
+ onCancel: props.onCancel,
20
+ title: "结果判定脚本",
21
+ content: SubProcessResultConfigContent,
22
+ scriptKey: props.scriptKey
23
+ });
24
+ export { SubProcessResultConfigModal };
@@ -19,6 +19,8 @@ export declare enum ScriptType {
19
19
  ROUTER = "ROUTER",
20
20
  /** 子流程节点脚本 */
21
21
  SUB_PROCESS = "SUB_PROCESS",
22
+ /** 子流程结果判定脚本 */
23
+ SUB_PROCESS_RESULT = "SUB_PROCESS_RESULT",
22
24
  /** 自定义操作 */
23
25
  CUSTOM_ACTION = "CUSTOM_ACTION",
24
26
  /** 按钮展示条件 */
@@ -7,6 +7,7 @@ var script_ScriptType = /*#__PURE__*/ function(ScriptType) {
7
7
  ScriptType["TRIGGER"] = "TRIGGER";
8
8
  ScriptType["ROUTER"] = "ROUTER";
9
9
  ScriptType["SUB_PROCESS"] = "SUB_PROCESS";
10
+ ScriptType["SUB_PROCESS_RESULT"] = "SUB_PROCESS_RESULT";
10
11
  ScriptType["CUSTOM_ACTION"] = "CUSTOM_ACTION";
11
12
  ScriptType["ACTION_DISPLAY"] = "ACTION_DISPLAY";
12
13
  return ScriptType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coding-flow/flow-design",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "flow-engine design components ",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -54,16 +54,16 @@
54
54
  "nanoid": "^5.1.6",
55
55
  "react-redux": "^9.2.0",
56
56
  "styled-components": "^5.3.11",
57
- "@coding-flow/flow-core": "0.1.1",
58
- "@coding-flow/flow-types": "0.1.1",
59
- "@coding-flow/flow-icons": "0.1.1",
60
- "@coding-flow/flow-pc-ui": "0.1.1"
57
+ "@coding-flow/flow-core": "0.1.3",
58
+ "@coding-flow/flow-icons": "0.1.3",
59
+ "@coding-flow/flow-pc-ui": "0.1.3",
60
+ "@coding-flow/flow-types": "0.1.3"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/lodash-es": "^4.17.12",
64
64
  "@types/styled-components": "^5.1.36",
65
- "@coding-flow/flow-core": "0.1.1",
66
- "@coding-flow/flow-types": "0.1.1"
65
+ "@coding-flow/flow-core": "0.1.3",
66
+ "@coding-flow/flow-types": "0.1.3"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "rslib build",