yyd-flow-designer 0.1.0 → 0.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yyd-flow-designer",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "YYD Flow designer (process orchestration UI)",
6
6
  "keywords": [
@@ -0,0 +1,43 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ /** 画布图内容,与草稿 content / 实例 FlowDefineJson 同形 */
4
+ export type FlowGraphContent = {
5
+ nodes?: any[];
6
+ edges?: any[];
7
+ timestamp?: string;
8
+ [key: string]: any;
9
+ };
10
+
11
+ export type FlowDesignerFeatures = {
12
+ /** 发布版本,默认:定义模式 true,受控模式 false */
13
+ publish?: boolean;
14
+ /** 版本管理,默认:定义模式 true,受控模式 false */
15
+ versionManager?: boolean;
16
+ /** 调用测试,默认 true */
17
+ executeTest?: boolean;
18
+ /** 导入/导出,默认 true */
19
+ importExport?: boolean;
20
+ };
21
+
22
+ export type DesignerConfig = {
23
+ /** 受控:传入则不再拉草稿;用于编辑实例 FlowDefineJson */
24
+ value?: FlowGraphContent | null;
25
+ /** 受控保存:传入则保存时调此回调,不写 FlowDraft */
26
+ onSave?: (content: FlowGraphContent) => void | Promise<void>;
27
+ title?: string;
28
+ features?: FlowDesignerFeatures;
29
+ };
30
+
31
+ const DesignerConfigCtx = createContext<DesignerConfig>({});
32
+
33
+ export const DesignerConfigProvider = DesignerConfigCtx.Provider;
34
+
35
+ export function useDesignerConfig(): DesignerConfig {
36
+ return useContext(DesignerConfigCtx);
37
+ }
38
+
39
+ /** 是否受控模式(编辑实例图 / 宿主自管存取) */
40
+ export function useIsControlledDesigner(): boolean {
41
+ const { value, onSave } = useDesignerConfig();
42
+ return value !== undefined || typeof onSave === 'function';
43
+ }
@@ -1,9 +1,25 @@
1
- /** HTTP — 替代 @/share_lib/utils/HttpRequest;由宿主注入 baseURL / token */
1
+ /** HTTP — 由宿主注入 baseURL / 管理入口 / headers;鉴权头与宿主一致用 token */
2
+
2
3
  let baseURL = '';
4
+ /** 设计器管理单一入口,默认联调路径;宿主可改成自己的 Controller */
5
+ let adminPath = '/yydflowweb';
3
6
  let getHeaders: () => Record<string, string> = () => ({});
4
7
 
5
- export function configureHttp(options: { baseURL?: string; getHeaders?: () => Record<string, string> }) {
8
+ export function configureHttp(options: {
9
+ baseURL?: string;
10
+ /** 管理 action 入口,默认 /yydflowweb */
11
+ adminPath?: string;
12
+ /**
13
+ * 请求头。宿主应对齐登录体系:
14
+ * `() => ({ token: getUserToken() })`
15
+ */
16
+ getHeaders?: () => Record<string, string>;
17
+ }) {
6
18
  if (options.baseURL != null) baseURL = options.baseURL.replace(/\/$/, '');
19
+ if (options.adminPath != null) {
20
+ const p = options.adminPath.trim() || '/yydflowweb';
21
+ adminPath = p.startsWith('/') || p.startsWith('http') ? p.replace(/\/$/, '') : `/${p.replace(/\/$/, '')}`;
22
+ }
7
23
  if (options.getHeaders) getHeaders = options.getHeaders;
8
24
  }
9
25
 
@@ -47,3 +63,8 @@ export const httpGet = <T = any>(url: string, params?: Record<string, any>) => {
47
63
  };
48
64
 
49
65
  export const httpPost = <T = any>(url: string, data?: any) => request<T>('POST', url, data);
66
+
67
+ /** POST { action, ...data } 到管理入口 */
68
+ export function flowAction<T = any>(action: string, data?: Record<string, any>) {
69
+ return httpPost<T>(adminPath, { action, ...(data || {}) });
70
+ }
@@ -2,10 +2,9 @@
2
2
  import { createContext, useContext } from 'react';
3
3
 
4
4
  export type RouteParams = {
5
+ /** 定义模式必填;受控编辑实例图时可空 */
5
6
  processId?: string;
6
7
  designMode?: string;
7
- appId?: string;
8
- projectId?: string;
9
8
  };
10
9
 
11
10
  const RouteCtx = createContext<RouteParams>({});
@@ -0,0 +1,36 @@
1
+ import { flowAction } from '../adapters/http';
2
+
3
+ /** 设计器管理 action(与后端 FlowAdminActions 对齐) */
4
+ export const Actions = {
5
+ projectAdd: 'project.add',
6
+ projectUpdate: 'project.update',
7
+ projectDelete: 'project.delete',
8
+ projectGet: 'project.get',
9
+ projectList: 'project.list',
10
+
11
+ tagAdd: 'tag.add',
12
+ tagUpdate: 'tag.update',
13
+ tagDelete: 'tag.delete',
14
+ tagGet: 'tag.get',
15
+ tagList: 'tag.list',
16
+ defineSetTags: 'define.setTags',
17
+
18
+ defineAdd: 'define.add',
19
+ defineUpdate: 'define.update',
20
+ defineDelete: 'define.delete',
21
+ defineGet: 'define.get',
22
+ defineList: 'define.list',
23
+ defineCheckCode: 'define.checkCode',
24
+
25
+ draftSave: 'draft.save',
26
+ draftGet: 'draft.get',
27
+ draftGetByProcessId: 'draft.getByProcessId',
28
+ draftList: 'draft.list',
29
+
30
+ versionAdd: 'version.add',
31
+ versionRestore: 'version.restore',
32
+ versionGet: 'version.get',
33
+ versionList: 'version.list',
34
+ } as const;
35
+
36
+ export { flowAction };
@@ -1,30 +1,27 @@
1
- import { httpGet, httpPost } from '../adapters/http';
2
-
3
- /** 与 lowcode 前端一致的路径前缀;boot 可配 context 或反代 */
4
- const P = '/web';
1
+ import { Actions, flowAction } from './flowAction';
5
2
 
6
3
  async function getEntity(params: any) {
7
- return httpGet(`${P}/flowDefine/get/` + params.processId);
4
+ return flowAction(Actions.defineGet, { processId: params.processId });
8
5
  }
9
6
 
10
7
  async function getList(params: any) {
11
- return httpPost(`${P}/flowDefine/getList`, params);
8
+ return flowAction(Actions.defineList, params);
12
9
  }
13
10
 
14
11
  async function add(params: any) {
15
- return httpPost(`${P}/flowDefine/add`, params);
12
+ return flowAction(Actions.defineAdd, params);
16
13
  }
17
14
 
18
15
  async function update(params: any) {
19
- return httpPost(`${P}/flowDefine/update`, params);
16
+ return flowAction(Actions.defineUpdate, params);
20
17
  }
21
18
 
22
19
  async function del(params: any) {
23
- return httpPost(`${P}/flowDefine/deleteByIds`, params);
20
+ return flowAction(Actions.defineDelete, params);
24
21
  }
25
22
 
26
23
  async function checkProcessCodeExists(params: any) {
27
- return httpGet(`${P}/flowDefine/checkProcessCodeExists`, params);
24
+ return flowAction(Actions.defineCheckCode, params);
28
25
  }
29
26
 
30
27
  export default { getEntity, getList, add, update, del, checkProcessCodeExists };
@@ -1,13 +1,8 @@
1
- import { httpGet, httpPost } from '../adapters/http';
1
+ import { Actions, flowAction } from './flowAction';
2
2
 
3
- const P = '/web';
4
-
5
- export const FlowDraftSaveReq = (params: any) => httpPost(`${P}/flowDraft/save`, params);
3
+ export const FlowDraftSaveReq = (params: any) => flowAction(Actions.draftSave, params);
6
4
  export const FlowDraftGetReq = (params: any) =>
7
- httpGet(`${P}/flowDraft/getByProcessId/` + params?.processId);
8
- export const FlowVersionHistoryAddReq = (params: any) =>
9
- httpPost(`${P}/flowVersionHistory/add`, params);
10
- export const FlowVersionHistoryGetListReq = (params: any) =>
11
- httpPost(`${P}/flowVersionHistory/getList`, params);
12
- export const FlowVersionHistoryRestoreReq = (params: any) =>
13
- httpPost(`${P}/flowVersionHistory/restore`, params);
5
+ flowAction(Actions.draftGetByProcessId, { processId: params?.processId });
6
+ export const FlowVersionHistoryAddReq = (params: any) => flowAction(Actions.versionAdd, params);
7
+ export const FlowVersionHistoryGetListReq = (params: any) => flowAction(Actions.versionList, params);
8
+ export const FlowVersionHistoryRestoreReq = (params: any) => flowAction(Actions.versionRestore, params);
@@ -26,6 +26,10 @@ ServiceTask.nodeSchema = {
26
26
  code: '',
27
27
  name: '服务任务',
28
28
  description: '',
29
+ handler: '',
30
+ assignee: {
31
+ rules: [{ type: 'initiator' }],
32
+ },
29
33
  inputs: [],
30
34
  outputs: [],
31
35
  outputNotifyWebhook: { enabled: false, url: '', body: [] },
@@ -4,8 +4,8 @@ import AssigneeRulesEditor from './components/AssigneeRulesEditor';
4
4
  import TimeoutSettingEditor from './components/TimeoutSettingEditor';
5
5
  import GatewayBranchesEditor from './components/GatewayBranchesEditor';
6
6
  import {
7
- InputParamsEditor,
8
- OutputParamsEditor,
7
+ InputsEditor,
8
+ OutputsEditor,
9
9
  ProcessParamsEditor,
10
10
  } from './components/ParamIOEditors';
11
11
 
@@ -81,8 +81,8 @@ const TaskIoProperty = memo(({ nodeInfo, updateNodes }: PropertyProps) => {
81
81
  const content = nodeInfo?.data?.nodeConfig?.content ?? {};
82
82
  return (
83
83
  <Form layout="vertical">
84
- <InputParamsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
85
- <OutputParamsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
84
+ <InputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
85
+ <OutputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
86
86
  <DescriptionField content={content} nodeInfo={nodeInfo} updateNodes={updateNodes} />
87
87
  </Form>
88
88
  );
@@ -92,22 +92,47 @@ export const UserTaskProperty = memo(({ nodeInfo, updateNodes }: PropertyProps)
92
92
  const content = nodeInfo?.data?.nodeConfig?.content ?? {};
93
93
  return (
94
94
  <Form layout="vertical">
95
- <InputParamsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
95
+ <InputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
96
96
  <AssigneeRulesEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
97
97
  <TimeoutSettingEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
98
- <OutputParamsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
98
+ <OutputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
99
99
  <DescriptionField content={content} nodeInfo={nodeInfo} updateNodes={updateNodes} />
100
100
  </Form>
101
101
  );
102
102
  });
103
103
 
104
- export const ServiceTaskProperty = TaskIoProperty;
104
+ export const ServiceTaskProperty = memo(({ nodeInfo, updateNodes }: PropertyProps) => {
105
+ const content = nodeInfo?.data?.nodeConfig?.content ?? {};
106
+ return (
107
+ <Form layout="vertical">
108
+ <InputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
109
+ <Form.Item
110
+ label="Handler"
111
+ extra="对应宿主 @Component(&quot;名称&quot;) 的 Bean 名;留空则不调 Java SPI"
112
+ >
113
+ <Input
114
+ placeholder="如 notifySms"
115
+ value={content.handler ?? ''}
116
+ onChange={(e) => updateNodeContent(nodeInfo, updateNodes, { handler: e.target.value })}
117
+ />
118
+ </Form.Item>
119
+ <AssigneeRulesEditor
120
+ nodeInfo={nodeInfo}
121
+ updateNodes={updateNodes}
122
+ label="接收人"
123
+ extra="给 handler 用(通知/抄送等),不产生待办。规则与用户任务相同:按 scope 对宿主用户体系。"
124
+ />
125
+ <OutputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} />
126
+ <DescriptionField content={content} nodeInfo={nodeInfo} updateNodes={updateNodes} />
127
+ </Form>
128
+ );
129
+ });
105
130
 
106
131
  export const ExclusiveGatewayProperty = memo(({ nodeInfo, updateNodes }: PropertyProps) => {
107
132
  const content = nodeInfo?.data?.nodeConfig?.content ?? {};
108
133
  return (
109
134
  <Form layout="vertical">
110
- <InputParamsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} title="判断参数" />
135
+ <InputsEditor nodeInfo={nodeInfo} updateNodes={updateNodes} title="判断参数" />
111
136
  <GatewayBranchesEditor
112
137
  nodeInfo={nodeInfo}
113
138
  updateNodes={updateNodes}
@@ -454,7 +454,7 @@ export const ProcessParamsEditor = memo(({ nodeInfo, updateNodes }: {
454
454
  );
455
455
  });
456
456
 
457
- export const InputParamsEditor = memo(({ nodeInfo, updateNodes, title = '输入参数' }: {
457
+ export const InputsEditor = memo(({ nodeInfo, updateNodes, title = '输入参数' }: {
458
458
  nodeInfo: any;
459
459
  updateNodes: (nodes: any[]) => void;
460
460
  title?: string;
@@ -472,7 +472,7 @@ export const InputParamsEditor = memo(({ nodeInfo, updateNodes, title = '输入
472
472
  );
473
473
  });
474
474
 
475
- export const OutputParamsEditor = memo(({ nodeInfo, updateNodes, showNotifyWebhook = true }: {
475
+ export const OutputsEditor = memo(({ nodeInfo, updateNodes, showNotifyWebhook = true }: {
476
476
  nodeInfo: any;
477
477
  updateNodes: (nodes: any[]) => void;
478
478
  showNotifyWebhook?: boolean;
@@ -1,115 +1,3 @@
1
- import {
2
- Background,
3
- Controls,
4
- ReactFlow,
5
- ReactFlowProvider,
6
- } from '@xyflow/react';
7
- import { memo, useMemo } from 'react';
8
- import { PopoverProvider } from '../../../shared/flow/PopoverContext';
9
- import edgeTypes from '../Edges';
10
- import defaultEdgeOptions from '../Edges/defaultEdgeOptions';
11
- import nodeTypes from '../Nodes';
12
- import { normalizeFlowNodes } from '../utils/nodeIdentity';
13
- import {
14
- applyTrailToEdges,
15
- applyTrailToNodes,
16
- collectCodes,
17
- type TrailOverlay,
18
- } from '../utils/trailOverlay';
19
-
20
- export interface ExecuteTestFlowPreviewProps {
21
- nodes: any[];
22
- edges: any[];
23
- passedNodes?: any[];
24
- parallelTokens?: any[];
25
- currentNodeCode?: string;
26
- }
27
-
28
- /**
29
- * 测试面板内的流程图:原样抄设计器节点/连线/布局,只叠加轨迹高亮。
30
- */
31
- const ExecuteTestFlowInner = ({
32
- nodes,
33
- edges,
34
- passedNodes = [],
35
- parallelTokens = [],
36
- currentNodeCode = '',
37
- }: ExecuteTestFlowPreviewProps) => {
38
- const trailOverlay: TrailOverlay = useMemo(() => {
39
- const passedCodes = collectCodes(passedNodes, ['nodeCode', 'NodeCode']);
40
- const waitingCodes = collectCodes(parallelTokens, ['nodeCode', 'NodeCode']);
41
- const currentCode = currentNodeCode?.trim() || '';
42
- if (!passedCodes.length && !waitingCodes.length && !currentCode) {
43
- return null;
44
- }
45
- return { passedCodes, waitingCodes, currentCode };
46
- }, [passedNodes, parallelTokens, currentNodeCode]);
47
-
48
- const { previewNodes, previewEdges } = useMemo(() => {
49
- const baseNodes = normalizeFlowNodes(nodes as any[]);
50
- const baseEdges = edges ?? [];
51
- return {
52
- previewNodes: applyTrailToNodes(baseNodes, trailOverlay).map((n) => ({
53
- ...n,
54
- selected: false,
55
- draggable: false,
56
- selectable: false,
57
- connectable: false,
58
- })),
59
- previewEdges: applyTrailToEdges(baseEdges as any[], baseNodes, trailOverlay).map((e) => ({
60
- ...e,
61
- selected: false,
62
- })),
63
- };
64
- }, [nodes, edges, trailOverlay]);
65
-
66
- if (!previewNodes.length) {
67
- return <div className="execute-test-flow-preview__empty">画布暂无节点</div>;
68
- }
69
-
70
- return (
71
- <ReactFlow
72
- nodes={previewNodes}
73
- edges={previewEdges}
74
- nodeTypes={nodeTypes}
75
- edgeTypes={edgeTypes}
76
- defaultEdgeOptions={defaultEdgeOptions}
77
- fitView
78
- fitViewOptions={{ padding: 0.2, maxZoom: 1 }}
79
- nodesDraggable={false}
80
- nodesConnectable={false}
81
- elementsSelectable={false}
82
- panOnDrag
83
- zoomOnScroll
84
- preventScrolling={false}
85
- proOptions={{ hideAttribution: true }}
86
- minZoom={0.15}
87
- maxZoom={1.5}
88
- className="bpmn-design-flow"
89
- >
90
- <Background gap={14} size={1.2} color="rgba(0,0,0,0.05)" />
91
- <Controls showInteractive={false} position="bottom-right" />
92
- </ReactFlow>
93
- );
94
- };
95
-
96
- const ExecuteTestFlowPreview = (props: ExecuteTestFlowPreviewProps) => (
97
- <div className="execute-test-flow-preview">
98
- <div className="execute-test-flow-preview__legend">
99
- <span><i className="dot passed" />已走过</span>
100
- <span><i className="dot waiting" />挂起待办</span>
101
- <span><i className="dot current" />当前续跑</span>
102
- <span><i className="dot idle" />未到达</span>
103
- </div>
104
- {/* bpmn-design-page:复用设计器节点皮肤(Modal 挂到 body 时否则丢样式) */}
105
- <div className="execute-test-flow-preview__canvas bpmn-design-page">
106
- <ReactFlowProvider>
107
- <PopoverProvider>
108
- <ExecuteTestFlowInner {...props} />
109
- </PopoverProvider>
110
- </ReactFlowProvider>
111
- </div>
112
- </div>
113
- );
114
-
115
- export default memo(ExecuteTestFlowPreview);
1
+ /** @deprecated 请用包导出的 FlowViewer;测试弹窗内部仍走此别名 */
2
+ export { default } from '../../../viewer/FlowViewer';
3
+ export type { FlowViewerProps as ExecuteTestFlowPreviewProps } from '../../../viewer/FlowViewer';
@@ -2,7 +2,7 @@ import { PlayCircleOutlined } from '@ant-design/icons';
2
2
  import { Button, Collapse, Form, Input, Modal, Space, Typography, message } from 'antd';
3
3
  import { memo, useCallback, useEffect, useMemo, useState } from 'react';
4
4
  import { httpPost } from '../../../adapters/http';
5
- import ExecuteTestFlowPreview from './ExecuteTestFlowPreview';
5
+ import FlowViewer from '../../../viewer/FlowViewer';
6
6
  import { showFlowIssues, validateFlowGraph } from '../utils/validateFlowGraph';
7
7
 
8
8
  export interface ExecuteTestModalProps {
@@ -41,9 +41,9 @@ const safeParseArray = (text: string | undefined): any[] => {
41
41
  }
42
42
  };
43
43
 
44
- /** 从 execute 结果回填表单(整包 state;续跑目标优先用本步 nextNodes,否则用仍挂起的 parallelTokens) */
44
+ /** 从 execute 结果回填表单(整包 instanceState;续跑目标优先用本步 nextNodes,否则用仍挂起的 parallelTokens) */
45
45
  const applyResultToForm = (form: ReturnType<typeof Form.useForm>[0], r: any) => {
46
- const st = r?.state ?? r?.State ?? {};
46
+ const st = r?.instanceState ?? r?.InstanceState ?? {};
47
47
  const nextNodes = r?.nextNodes ?? r?.NextNodes ?? [];
48
48
  const tokens = st.parallelTokens ?? st.ParallelTokens ?? [];
49
49
  const stillWaiting = (Array.isArray(tokens) ? tokens : []).filter(
@@ -111,7 +111,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
111
111
  currentNodeCode: '',
112
112
  resumeTokenId: '',
113
113
  operatorId: 'u1001',
114
- inputParams: '{\n "orderId": "A001"\n}',
114
+ inputs: '{\n "orderId": "A001"\n}',
115
115
  instanceId: '',
116
116
  startUserId: 'u1001',
117
117
  startOrgId: '',
@@ -147,18 +147,18 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
147
147
 
148
148
  if (instanceId && (!Array.isArray(passedNodes) || passedNodes.length === 0)) {
149
149
  setAdvancedKeys(['advanced']);
150
- message.error('续跑时 passedNodes 不能为空:请先执行一次后点「用 state 回填」,或清空 instanceId 作为新实例');
150
+ message.error('续跑时 passedNodes 不能为空:请先执行一次后点「用 instanceState 回填」,或清空 instanceId 作为新实例');
151
151
  return;
152
152
  }
153
153
  if (instanceId && values.resumeTokenId?.trim()
154
154
  && (!Array.isArray(parallelTokens) || parallelTokens.length === 0)) {
155
155
  setAdvancedKeys(['advanced']);
156
- message.error('续跑时 parallelTokens 不能为空:请点「用 state 回填」整包带回');
156
+ message.error('续跑时 parallelTokens 不能为空:请点「用 instanceState 回填」整包带回');
157
157
  return;
158
158
  }
159
159
 
160
160
  const payload = {
161
- flowData: {
161
+ flowDefineJson: {
162
162
  nodes: flowData.nodes,
163
163
  edges: flowData.edges ?? [],
164
164
  },
@@ -166,9 +166,9 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
166
166
  currentNodeCode: values.currentNodeCode?.trim() || '',
167
167
  resumeTokenId: values.resumeTokenId?.trim() || undefined,
168
168
  operatorId: values.operatorId?.trim() || undefined,
169
- inputParams: tryParseJson(values.inputParams, 'inputParams') ?? {},
169
+ inputs: tryParseJson(values.inputs, 'inputs') ?? {},
170
170
  },
171
- state: {
171
+ instanceState: {
172
172
  instanceId: instanceId || null,
173
173
  startUserId: values.startUserId?.trim() || undefined,
174
174
  startOrgId: values.startOrgId?.trim() || undefined,
@@ -179,7 +179,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
179
179
  };
180
180
 
181
181
  setLoading(true);
182
- const res = await httpPost<any>('/lcflow/engine/execute', payload);
182
+ const res = await httpPost<any>('/yydflow/execute', payload);
183
183
  setLastResult(res);
184
184
  setResultText(pretty(res));
185
185
 
@@ -188,11 +188,11 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
188
188
  } else {
189
189
  const info = applyResultToForm(form, res);
190
190
  if (info.newTodos > 0) {
191
- message.success(`执行完成;本步新待办 ${info.newTodos} 条,已回填 state`);
191
+ message.success(`执行完成;本步新待办 ${info.newTodos} 条,已回填 instanceState`);
192
192
  } else if (info.stillWaiting > 0) {
193
193
  message.success(`执行完成;仍有 ${info.stillWaiting} 条挂起,已回填`);
194
194
  } else {
195
- message.success('执行完成;已自动回填 state');
195
+ message.success('执行完成;已自动回填 instanceState');
196
196
  }
197
197
  }
198
198
  } catch (e: any) {
@@ -210,7 +210,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
210
210
  return;
211
211
  }
212
212
  if (lastResult.status === 'error' || lastResult.Status === 'error') {
213
- message.warning('错误结果没有可续跑的 state');
213
+ message.warning('错误结果没有可续跑的 instanceState');
214
214
  return;
215
215
  }
216
216
  const info = applyResultToForm(form, lastResult);
@@ -219,7 +219,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
219
219
  ? `已回填;本步新待办 ${info.newTodos} 条`
220
220
  : info.stillWaiting > 0
221
221
  ? `已回填;无新待办,仍有 ${info.stillWaiting} 条挂起可续跑`
222
- : '已回填完整 state'
222
+ : '已回填完整 instanceState'
223
223
  );
224
224
  }, [form, lastResult]);
225
225
 
@@ -242,7 +242,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
242
242
  关闭
243
243
  </Button>,
244
244
  <Button key="fill" disabled={!lastResult} onClick={fillBackFromResult}>
245
- 用 state 回填
245
+ 用 instanceState 回填
246
246
  </Button>,
247
247
  <Button key="run" type="primary" loading={loading} icon={<PlayCircleOutlined />} onClick={handleExecute}>
248
248
  执行
@@ -257,7 +257,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
257
257
  请求参数
258
258
  </Typography.Text>
259
259
  <Typography.Paragraph type="secondary" className="!mb-3 !mt-1 text-xs">
260
- 首次执行请清空 instanceId;成功后自动回填 state。
260
+ 首次执行请清空 instanceId;成功后自动回填 instanceState。
261
261
  </Typography.Paragraph>
262
262
 
263
263
  <Typography.Text type="secondary">command</Typography.Text>
@@ -274,9 +274,9 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
274
274
  <Input placeholder="留空则引擎生成" allowClear />
275
275
  </Form.Item>
276
276
  <Form.Item
277
- label="inputParams(仅业务 JSON)"
278
- name="inputParams"
279
- rules={[{ required: true, message: '请填写 inputParams JSON' }]}
277
+ label="inputs(仅业务 JSON)"
278
+ name="inputs"
279
+ rules={[{ required: true, message: '请填写 inputs JSON' }]}
280
280
  >
281
281
  <Input.TextArea rows={5} className="font-mono text-xs" />
282
282
  </Form.Item>
@@ -297,7 +297,7 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
297
297
  items={[
298
298
  {
299
299
  key: 'advanced',
300
- label: '高级 · state 原始 JSON',
300
+ label: '高级 · instanceState 原始 JSON',
301
301
  forceRender: true,
302
302
  children: (
303
303
  <>
@@ -326,11 +326,12 @@ const ExecuteTestModal = ({ open, onClose, getFlowData }: ExecuteTestModalProps)
326
326
  流程图与设计器一致;执行后按轨迹高亮已过 / 挂起 / 当前节点。
327
327
  </Typography.Paragraph>
328
328
 
329
- <ExecuteTestFlowPreview
330
- nodes={flowSnapshot.nodes}
331
- edges={flowSnapshot.edges}
332
- passedNodes={trailPassed}
333
- parallelTokens={trailTokens}
329
+ <FlowViewer
330
+ flowDefineJson={flowSnapshot}
331
+ instanceState={{
332
+ passedNodes: trailPassed,
333
+ parallelTokens: trailTokens,
334
+ }}
334
335
  currentNodeCode={watchedCurrent}
335
336
  />
336
337
 
@@ -136,6 +136,16 @@ export const validateFlowGraph = (nodes: Node[] = [], edges: Edge[] = []): FlowI
136
136
  issues.push({ level: 'warning', message: `${label}:未配置审批人` });
137
137
  }
138
138
  }
139
+
140
+ if (type === 'ServiceTask') {
141
+ const handler = String(contentOf(node).handler ?? '').trim();
142
+ const rules = contentOf(node).assignee?.rules;
143
+ if (!handler) {
144
+ issues.push({ level: 'warning', message: `${label}:未指定 handler,不会调用 Java SPI` });
145
+ } else if (!Array.isArray(rules) || rules.length === 0) {
146
+ issues.push({ level: 'warning', message: `${label}:已指定 handler 但未配置接收人` });
147
+ }
148
+ }
139
149
  });
140
150
 
141
151
  if (starts.length === 1) {