@steedos-labs/plugin-workflow 3.0.83 → 3.0.84

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.
@@ -5,7 +5,7 @@
5
5
  <link rel="shortcut icon" type="image/svg+xml" href="/images/logo.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>designer</title>
8
- <script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-WAa42ctb.js"></script>
8
+ <script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-DpFVuLlI.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/api/workflow/designer-v2/assets/index-B56yvQDB.css">
10
10
  </head>
11
11
  <body>
@@ -1,5 +1,5 @@
1
1
  waitForThing(window, 'antd').then(function(){
2
- var v = '3.0.83';
2
+ var v = '3.0.84';
3
3
  loadJs('/amis-renderer/amis-renderer.js?v=' + v);
4
4
  loadCss('/amis-renderer/amis-renderer.css?v=' + v)
5
5
  })
@@ -127,7 +127,7 @@ amis_schema: |-
127
127
  ]
128
128
  }
129
129
  },
130
- "hiddenOn": "${listName!==\"draft\" || display===\"split\"}"
130
+ "hiddenOn": "${listName!==\"draft\" || display===\"split\" || window:innerWidth <= 768}"
131
131
  }
132
132
  ],
133
133
  "regions": [
@@ -48,6 +48,6 @@ extra_columns:
48
48
  - extras
49
49
  - values
50
50
  disableSwitch: true
51
- # filter_required: true
51
+ filter_required: true
52
52
  # searchable_default:
53
53
  # submit_date: "${[STARTOF(DATEMODIFY(NOW(), -180, 'day'), 'day'),ENDOF(NOW(), 'day')]}"
@@ -25,19 +25,17 @@
25
25
  "type": "service",
26
26
  "id": "u:d6db0c84f150",
27
27
  "body": {},
28
- "messages": {
29
- },
28
+ "messages": {},
30
29
  "schemaApi": {
31
30
  "url": "${context.rootUrl}/graphql?recordId=${recordId}",
32
31
  "method": "post",
33
- "messages": {
34
- },
32
+ "messages": {},
35
33
  "dataType": "json",
36
34
  "headers": {
37
35
  "Authorization": "Bearer ${context.tenantId},${context.authToken}"
38
36
  },
39
37
  "requestAdaptor": "\nconst { recordId } = api.body;\napi.data = {\n query: `{instance_task:instance_tasks__findOne(id: \"${recordId}\"){_id,instance,is_read}}`\n};\n\nreturn api;",
40
- "adaptor": "if(!payload.data.instance_task){return payload;}\nlet boxName = api.body.side_listview_id === 'inbox' ? 'inbox' : 'outbox';\npayload.data = {\n instanceId: payload.data.instance_task.instance,\n boxName: boxName,\n isRead: payload.data.instance_task.is_read\n}\n; console.log('=======>payload===>', payload); \n const result = {data: {'type':'wrapper','className':'p-0 h-full','body':[{'type':'steedos-instance-detail','id':'u:40052b3812c1','label':'Instance Detail','instanceId':payload.data.instanceId,'boxName':context.side_listview_id}],'id':'u:829a40757f0a'}};console.log('result===>', result); return result;"
38
+ "adaptor": "if(!payload.data.instance_task){\n // 同步清掉 PageRecordDetail.tsx 加上的 loading 遮罩和滑动动画\n try{\n document.body.classList.remove('steedos-detail-loading');\n if(window.$){window.$('.page-object-detail-wrapper').removeClass('slide-out-bottom').addClass('slide-in-top');}\n }catch(e){}\n // 简易 i18n:plugins 端 adaptor 无法 import i18next,按浏览器语言切换中英文\n var isZh=(navigator.language||'').toLowerCase().indexOf('zh')===0;\n var notFoundText=isZh?'\\u65e0\\u6cd5\\u627e\\u5230\\u8bb0\\u5f55':'No records found.';\n var backText=isZh?'\\u8fd4\\u56de\\u5217\\u8868':'Back to list';\n // 警示三角图标(Heroicons ExclamationTriangle,amber-500 描边)\n var svgHtml=\"<span class='empty-record-icon' style='display:block;margin-bottom:16px'><svg xmlns='http://www.w3.org/2000/svg' width='72' height='72' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='#f59e0b'><path stroke-linecap='round' stroke-linejoin='round' d='M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.732 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z'/></svg></span>\";\n // 返回按钮统一调用全局 window.goBack(),参考 header.js 内置详情返回按钮\n var nfBody=[\n {type:'html',html:svgHtml},\n {type:'tpl',tpl:notFoundText,className:'empty-record-title text-xl text-gray-800 font-medium mb-4'},\n {type:'button',label:backText,level:'primary',className:'empty-record-back-btn',onEvent:{click:{actions:[{actionType:'custom',script:'window.goBack && window.goBack()'}]}}}\n ];\n // isRead:true 防止下游 set_have_read ajax 触发\n return {data:{type:'wrapper',className:'flex flex-col items-center justify-center bg-white p-8 text-center',style:{minHeight:'100vh'},body:nfBody,id:'u:notfound-instance-task',isRead:true}};\n}\nlet boxName = api.body.side_listview_id === 'inbox' ? 'inbox' : 'outbox';\npayload.data = {\n instanceId: payload.data.instance_task.instance,\n boxName: boxName,\n isRead: payload.data.instance_task.is_read\n};\nreturn {data: {'type':'wrapper','className':'p-0 h-full','body':[{'type':'steedos-instance-detail','id':'u:40052b3812c1','label':'Instance Detail','instanceId':payload.data.instanceId,'boxName':context.side_listview_id}],'id':'u:829a40757f0a'}};"
41
39
  },
42
40
  "onEvent": {
43
41
  "fetchSchemaInited": {
@@ -64,8 +62,7 @@
64
62
  "regions": [
65
63
  "body"
66
64
  ],
67
- "data": {
68
- },
65
+ "data": {},
69
66
  "className": "steedos-instance-wrapper",
70
67
  "id": "u:d37465183f56",
71
68
  "bodyClassName": "p-0 flex flex-1 overflow-hidden h-full",
@@ -201,7 +201,7 @@
201
201
  {
202
202
  "id": "u:259e5e73a3e2",
203
203
  "type": "tpl",
204
- "tpl": "无权限",
204
+ "tpl": "${recordNotFound ? '无法找到记录' : '无权限'}",
205
205
  "visibleOn": "${permission === false}"
206
206
  }
207
207
  ],
@@ -222,7 +222,7 @@
222
222
  "method": "get",
223
223
  "data": {},
224
224
  "requestAdaptor": "",
225
- "adaptor": "",
225
+ "adaptor": "if (payload && payload.errors && payload.errors.length) { return {data: {permission: false, recordNotFound: true}}; } return payload;",
226
226
  "messages": {},
227
227
  "sendOn": "${recordId}"
228
228
  },
@@ -7,6 +7,7 @@ const _ = require('lodash');
7
7
  const { getCollection } = require("../utils/collection");
8
8
  const WorkflowManager = require('../manager/workflow_manager');
9
9
  const getHandlersManager = require('../manager/handlers_manager');
10
+ const UUFlowManager = require('../manager/uuflow_manager');
10
11
  const { t } = require('@steedos/i18n')
11
12
 
12
13
  const FlowversionAPI = {
@@ -136,37 +137,118 @@ const FlowversionAPI = {
136
137
  return stepName;
137
138
  },
138
139
 
140
+ // 查找从fromStep到targetStepId的边路径(可能经过中间条件节点)
141
+ // 返回边集合,如 ["stepA->conditionB", "conditionB->stepC"]
142
+ findEdgePath: function (fromStep, targetStepId, stepsMap) {
143
+ let edges = [];
144
+ // 直接连线
145
+ if (fromStep.lines) {
146
+ for (let line of fromStep.lines) {
147
+ if (line.to_step === targetStepId) {
148
+ edges.push(`${fromStep._id}->${targetStepId}`);
149
+ return edges;
150
+ }
151
+ }
152
+ // 通过条件节点间接连线(BFS,最多2层中间节点)
153
+ for (let line of fromStep.lines) {
154
+ let midStep = stepsMap[line.to_step];
155
+ if (midStep && midStep.step_type === 'condition') {
156
+ if (midStep.lines) {
157
+ for (let midLine of midStep.lines) {
158
+ if (midLine.to_step === targetStepId) {
159
+ edges.push(`${fromStep._id}->${midStep._id}`);
160
+ edges.push(`${midStep._id}->${targetStepId}`);
161
+ return edges;
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ // 找不到直接路径,简单标记起止边
169
+ edges.push(`${fromStep._id}->${targetStepId}`);
170
+ return edges;
171
+ },
172
+
139
173
  generateStepsGraphSyntax: async function (steps, currentStepId, isConvertToString, direction, instance_id, instance) {
140
174
  let nodes = [`graph ${direction}`];
141
175
  let cachedStepNames = {};
142
176
  let skipSteps = new Set(instance?.skip_steps || []);
177
+ let completedStepIds = instance?._completedStepIds || new Set();
178
+ let traversedEdges = instance?._traversedEdges || new Set();
179
+ let predictedEdges = instance?._predictedEdges || new Set();
180
+ let isInstanceFinished = instance?._isFinished === true;
181
+ let linkIndex = 0;
182
+ let traversedLinkIndices = [];
183
+ let predictedLinkIndices = [];
184
+ // 先输出所有边,类指令统一在所有边之后输出,避免 mermaid 因
185
+ // “class 指令出现在节点首次声明之前”而静默忽略 class(V1/V2b 根因)
143
186
  for (let step of steps) {
144
187
  let lines = step.lines;
145
188
  if (lines?.length) {
146
189
  for (let line of lines) {
147
190
  let stepName = "";
148
- // 标记条件节点
149
191
  if (step.name) {
150
- if (step.step_type === "condition") {
151
- nodes.push(` class ${step._id} condition;`);
152
- }
153
192
  stepName = await FlowversionAPI.getStepName(step, cachedStepNames, instance_id, instance);
154
193
  } else {
155
194
  stepName = "";
156
195
  }
157
- // Mark skipped steps
158
- if (skipSteps.has(step._id)) {
159
- nodes.push(` class ${step._id} skip-step;`);
160
- }
161
- // 原findPropertyByPK("_id",line.to_step),转为find
162
196
  let toStep = steps.find(s => s._id === line.to_step);
163
197
  let toStepName = await FlowversionAPI.getStepName(toStep, cachedStepNames, instance_id, instance);
164
- nodes.push(` ${step._id}("${stepName}")-->${line.to_step}("${toStepName}")`);
198
+ if (step.step_type === "condition" && line.name) {
199
+ let lineName = FlowversionAPI.replaceErrorSymbol(line.name);
200
+ nodes.push(` ${step._id}("${stepName}")-->|${lineName}|${line.to_step}("${toStepName}")`);
201
+ } else {
202
+ nodes.push(` ${step._id}("${stepName}")-->${line.to_step}("${toStepName}")`);
203
+ }
204
+ if (traversedEdges.has(`${step._id}->${line.to_step}`)) {
205
+ traversedLinkIndices.push(linkIndex);
206
+ } else if (predictedEdges.has(`${step._id}->${line.to_step}`)) {
207
+ predictedLinkIndices.push(linkIndex);
208
+ }
209
+ linkIndex++;
165
210
  }
166
211
  }
167
212
  }
168
- if (currentStepId) {
169
- nodes.push(` class ${currentStepId} current-step-node;`);
213
+ // 覆盖 mermaid 默认主题对未流转(default)节点的紫色 inline style,使其匹配图例的灰色
214
+ nodes.push(` classDef default fill:#f1f5f9,stroke:#cbd5e1,stroke-width:1.5px,color:#475569;`);
215
+ // 统一输出 class 指令,优先级:skip-step > current-step-node > completed-step > condition
216
+ // 已结束流程(isInstanceFinished)不再标记 current-step-node(V3),最后一步按已完成展示
217
+ let classOf = new Map();
218
+ let assign = (id, cls) => { if (!classOf.has(id)) classOf.set(id, cls); };
219
+ if (!isInstanceFinished && currentStepId) {
220
+ assign(currentStepId, 'current-step-node');
221
+ }
222
+ for (let step of steps) {
223
+ if (skipSteps.has(step._id)) {
224
+ assign(step._id, 'skip-step');
225
+ }
226
+ }
227
+ for (let stepId of completedStepIds) {
228
+ if (skipSteps.has(stepId)) continue;
229
+ if (!isInstanceFinished && stepId === currentStepId) continue;
230
+ assign(stepId, 'completed-step');
231
+ }
232
+ for (let step of steps) {
233
+ if (step.step_type === 'condition') {
234
+ assign(step._id, 'condition');
235
+ }
236
+ }
237
+ for (let [id, cls] of classOf) {
238
+ nodes.push(` class ${id} ${cls};`);
239
+ }
240
+ // 先将所有连线设为灰色(不会流转的路径)
241
+ if (linkIndex > 0) {
242
+ let allIndices = Array.from({length: linkIndex}, (_, i) => i).join(',');
243
+ nodes.push(` linkStyle ${allIndices} stroke:#cbd5e1,stroke-width:1.5px;`);
244
+ }
245
+ // 高亮已流转的连线(绿色实线)
246
+ if (traversedLinkIndices.length > 0) {
247
+ nodes.push(` linkStyle ${traversedLinkIndices.join(',')} stroke:#10b981,stroke-width:3px;`);
248
+ }
249
+ // 高亮预测的下一步路径(蓝色虚线)
250
+ if (predictedLinkIndices.length > 0) {
251
+ nodes.push(` linkStyle ${predictedLinkIndices.join(',')} stroke:#3b82f6,stroke-width:2px,stroke-dasharray:5 3;`);
170
252
  }
171
253
  if (isConvertToString) {
172
254
  let graphSyntax = nodes.join("\n");
@@ -505,12 +587,124 @@ const FlowversionAPI = {
505
587
  break;
506
588
  }
507
589
  default: {
508
- let instance = await db.instances.findOne({ _id: instance_id }, { projection: { flow_version: 1, flow: 1, step_approve: 1, skip_steps: 1, traces: { $slice: -1 } } });
590
+ let instance = await db.instances.findOne({ _id: instance_id });
509
591
  if (instance) {
510
- let currentStepId = instance.traces?.[0]?.step;
592
+ let traces = instance.traces || [];
593
+ let currentStepId = traces.length > 0 ? traces[traces.length - 1]?.step : null;
594
+ // 收集已完成的步骤ID和已流转的路径(from_step -> to_step)
595
+ let completedStepIds = new Set();
596
+ let traversedEdges = new Set();
597
+ for (let i = 0; i < traces.length; i++) {
598
+ let trace = traces[i];
599
+ if (trace.step) {
600
+ completedStepIds.add(trace.step);
601
+ }
602
+ // 从前一个trace到当前trace的连线视为已流转
603
+ if (i > 0 && traces[i - 1].step && trace.step) {
604
+ traversedEdges.add(`${traces[i - 1].step}->${trace.step}`);
605
+ }
606
+ }
607
+ instance._completedStepIds = completedStepIds;
608
+ instance._traversedEdges = traversedEdges;
609
+ let isInstanceFinished = instance.state === 'completed' || instance.state === 'terminated';
610
+ instance._isFinished = isInstanceFinished;
611
+
612
+ // 只有进行中的单子才预测后续路径,已结束的单子不需要预测
613
+ let predictedEdges = new Set();
614
+ try {
615
+ if (currentStepId && !isInstanceFinished) {
616
+ let flow = await UUFlowManager.getFlow(instance.flow);
617
+ let flowversion = UUFlowManager.getFlowVersion(flow, instance.flow_version);
618
+ // 取最新trace的最新approve的_id作为参数
619
+ let latestTrace = traces[traces.length - 1];
620
+ let latestApproveId = null;
621
+ if (latestTrace?.approves?.length) {
622
+ latestApproveId = latestTrace.approves[latestTrace.approves.length - 1]._id;
623
+ }
624
+ let values = UUFlowManager.getUpdatedValues(instance, latestApproveId);
625
+ if (process.env.STEEDOS_DEBUG) {
626
+ console.log('values===>', values, latestApproveId);
627
+ }
628
+ let stepsMap = {};
629
+ for (let s of flowversion.steps) {
630
+ stepsMap[s._id] = s;
631
+ }
632
+ // 递归预测,最多追踪20步防止死循环
633
+ let visited = new Set();
634
+ let predictFrom = [currentStepId];
635
+ let maxDepth = 20;
636
+ let depth = 0;
637
+ while (predictFrom.length > 0 && depth < maxDepth) {
638
+ depth++;
639
+ let nextBatch = [];
640
+ for (let fromStepId of predictFrom) {
641
+ if (visited.has(fromStepId)) continue;
642
+ visited.add(fromStepId);
643
+ let fromStep = stepsMap[fromStepId];
644
+ if (!fromStep || fromStep.step_type === 'end') continue;
645
+
646
+ if (fromStep.step_type === 'condition') {
647
+ // 条件节点:优先按条件表达式评估;若返回空(如表单字段未填,
648
+ // 无法判断走哪个分支),fallback 展开所有出线,保证预测路径继续延伸(V4)
649
+ let nextStepIds = await UUFlowManager.getNextSteps(instance, flow, fromStep, '', values);
650
+ if (!nextStepIds || nextStepIds.length === 0) {
651
+ nextStepIds = (fromStep.lines || []).map(l => l.to_step);
652
+ }
653
+ for (let nextId of nextStepIds || []) {
654
+ let nextStep = stepsMap[nextId];
655
+ if (!nextStep) continue;
656
+ predictedEdges.add(`${fromStepId}->${nextId}`);
657
+ if (!visited.has(nextId)) {
658
+ nextBatch.push(nextId);
659
+ }
660
+ }
661
+ } else {
662
+ // 非条件步骤:直接根据lines找下一步,不通过getNextSteps(它会把条件节点的所有分支都展开)
663
+ let judge = fromStep.step_type === 'sign' ? 'approved' : 'submitted';
664
+ let lines = (fromStep.lines || []).filter(l => l.state === judge);
665
+ for (let line of lines) {
666
+ let toStep = stepsMap[line.to_step];
667
+ if (!toStep) continue;
668
+ predictedEdges.add(`${fromStepId}->${line.to_step}`);
669
+ if (!visited.has(line.to_step)) {
670
+ nextBatch.push(line.to_step);
671
+ }
672
+ }
673
+ }
674
+ }
675
+ predictFrom = nextBatch;
676
+ }
677
+ }
678
+ } catch (e) {
679
+ // 预测失败不影响主流程,但输出详细错误信息便于排查
680
+ console.warn('Chart prediction failed:', e.message, e.stack);
681
+ }
682
+ instance._predictedEdges = predictedEdges;
683
+
511
684
  let flowversion = await WorkflowManager.getInstanceFlowVersion(instance);
512
685
  let steps = flowversion?.steps;
513
686
  if (steps?.length) {
687
+ // 解析经过条件节点的已流转边,将 stepA->stepC 展开为 stepA->conditionB, conditionB->stepC
688
+ if (traversedEdges.size > 0) {
689
+ let stepsMap = {};
690
+ for (let s of steps) {
691
+ stepsMap[s._id] = s;
692
+ }
693
+ let resolvedTraversedEdges = new Set();
694
+ for (let edge of traversedEdges) {
695
+ let [fromId, toId] = edge.split('->');
696
+ let fromStep = stepsMap[fromId];
697
+ if (fromStep) {
698
+ let edgePaths = FlowversionAPI.findEdgePath(fromStep, toId, stepsMap);
699
+ for (let e of edgePaths) {
700
+ resolvedTraversedEdges.add(e);
701
+ }
702
+ } else {
703
+ resolvedTraversedEdges.add(edge);
704
+ }
705
+ }
706
+ instance._traversedEdges = resolvedTraversedEdges;
707
+ }
514
708
  graphSyntax = await FlowversionAPI.generateStepsGraphSyntax(steps, currentStepId, false, direction, instance_id, instance);
515
709
  } else {
516
710
  error_msg = "没有找到当前申请单的流程步骤数据";
@@ -535,9 +729,11 @@ const FlowversionAPI = {
535
729
  <script type="text/javascript" src="/unpkg.com/mermaid@9.1.2/dist/mermaid.min.js"></script>
536
730
  <style>
537
731
  body {
538
- font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
732
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
539
733
  text-align: center;
540
- background-color: #fff;
734
+ background-color: #f8fafc;
735
+ margin: 0;
736
+ padding: 20px;
541
737
  }
542
738
  .loading{
543
739
  position: absolute;
@@ -547,8 +743,9 @@ const FlowversionAPI = {
547
743
  z-index: 1100;
548
744
  text-align: center;
549
745
  margin-top: -30px;
550
- font-size: 36px;
551
- color: #dfdfdf;
746
+ font-size: 20px;
747
+ color: #94a3b8;
748
+ font-weight: 500;
552
749
  }
553
750
  .error-msg{
554
751
  position: absolute;
@@ -557,65 +754,137 @@ const FlowversionAPI = {
557
754
  bottom: 20px;
558
755
  z-index: 1100;
559
756
  text-align: center;
560
- font-size: 20px;
561
- color: #a94442;
757
+ font-size: 16px;
758
+ color: #dc2626;
562
759
  }
760
+ /* 未流转的步骤 - 浅灰色 */
563
761
  #flow-steps-svg .node rect{
564
- fill: #ccccff;
565
- stroke: rgb(144, 144, 255);
762
+ fill: #f1f5f9;
763
+ stroke: #cbd5e1;
764
+ stroke-width: 1.5px;
765
+ rx: 8px;
766
+ ry: 8px;
767
+ }
768
+ /* 已完成的步骤 - 蓝色调 */
769
+ #flow-steps-svg .node.completed-step rect{
770
+ fill: #dbeafe;
771
+ stroke: #3b82f6;
566
772
  stroke-width: 2px;
567
773
  }
774
+ #flow-steps-svg .node.completed-step .step-name{
775
+ color: #1e40af;
776
+ }
777
+ #flow-steps-svg .node.completed-step .step-handler-name{
778
+ color: #3b82f6;
779
+ }
780
+ /* 当前步骤 - 绿色高亮 + 呼吸动画 */
568
781
  #flow-steps-svg .node.current-step-node rect{
569
- fill: #cde498;
570
- stroke: #13540c;
571
- stroke-width: 2px;
782
+ fill: #d1fae5;
783
+ stroke: #10b981;
784
+ stroke-width: 2.5px;
785
+ animation: currentStepPulse 2s ease-in-out infinite;
786
+ }
787
+ @keyframes currentStepPulse {
788
+ 0%, 100% { stroke-width: 2.5px; filter: drop-shadow(0 0 0px rgba(16,185,129,0)); }
789
+ 50% { stroke-width: 3px; filter: drop-shadow(0 0 6px rgba(16,185,129,0.4)); }
790
+ }
791
+ #flow-steps-svg .node.current-step-node .step-name{
792
+ color: #065f46;
793
+ font-weight: 600;
572
794
  }
795
+ #flow-steps-svg .node.current-step-node .step-handler-name{
796
+ color: #10b981;
797
+ }
798
+ /* 条件节点 */
573
799
  #flow-steps-svg .node.condition rect{
574
- fill: #ececff;
575
- stroke: rgb(204, 204, 255);
576
- stroke-width: 1px;
800
+ fill: #fef3c7;
801
+ stroke: #f59e0b;
802
+ stroke-width: 1.5px;
803
+ }
804
+ #flow-steps-svg .node.condition .step-name{
805
+ color: #92400e;
577
806
  }
807
+ /* 跳过的步骤 */
578
808
  #flow-steps-svg .node.skip-step rect{
579
- fill: #e8e8e8;
580
- stroke: #999999;
581
- stroke-width: 2px;
809
+ fill: #f9fafb;
810
+ stroke: #d1d5db;
811
+ stroke-width: 1.5px;
582
812
  stroke-dasharray: 5, 5;
583
- opacity: 0.7;
813
+ opacity: 0.6;
584
814
  }
585
815
  #flow-steps-svg .node.skip-step .step-name{
586
- color: #666666;
816
+ color: #9ca3af;
587
817
  text-decoration: line-through;
588
818
  }
589
819
  #flow-steps-svg .node.skip-step .step-handler-name{
590
- color: #999999;
820
+ color: #d1d5db;
591
821
  }
592
- #flow-steps-svg .node .trace-handler-name{
593
- color: #777;
822
+ /* 节点文字样式 */
823
+ #flow-steps-svg .node .step-name{
824
+ color: #64748b;
825
+ font-size: 14px;
594
826
  }
595
827
  #flow-steps-svg .node .step-handler-name{
596
- color: #777;
828
+ color: #94a3b8;
829
+ font-size: 12px;
830
+ }
831
+ #flow-steps-svg .node .trace-handler-name{
832
+ color: #94a3b8;
833
+ font-size: 12px;
834
+ }
835
+ /* 默认连线样式 */
836
+ #flow-steps-svg .edgePath path{
837
+ stroke: #cbd5e1;
838
+ stroke-width: 1.5px;
839
+ }
840
+ #flow-steps-svg marker path{
841
+ fill: #cbd5e1;
842
+ }
843
+ /* 连线上的文字标签 */
844
+ #flow-steps-svg .edgeLabel{
845
+ background-color: transparent;
846
+ padding: 0;
847
+ font-size: 12px;
848
+ color: #64748b;
849
+ }
850
+ #flow-steps-svg .edgeLabel span{
851
+ color: #64748b !important;
852
+ background: transparent !important;
853
+ }
854
+ #flow-steps-svg .edgeLabel span:not(:empty){
855
+ background: #f8fafc !important;
856
+ padding: 2px 6px;
857
+ border-radius: 4px;
597
858
  }
598
859
  div.mermaidTooltip{
599
860
  position: fixed!important;
600
861
  text-align: left!important;
601
- padding: 4px!important;
602
- font-size: 14px!important;
862
+ padding: 8px 12px!important;
863
+ font-size: 13px!important;
603
864
  max-width: 500px!important;
604
865
  left: auto!important;
605
866
  top: 15px!important;
606
867
  right: 15px;
868
+ border-radius: 6px;
869
+ box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
870
+ }
871
+ .graph-node {
872
+ padding: 4px 8px;
873
+ }
874
+ .graph-node .step-name {
875
+ font-weight: 500;
876
+ margin-bottom: 2px;
607
877
  }
608
878
  .btn-zoom{
609
- background: rgba(0, 0, 0, 0.1);
610
- border-color: transparent;
879
+ border: none;
611
880
  display: inline-block;
612
- padding: 2px 10px;
613
- font-size: 26px;
614
- border-radius: 20px;
615
- background: #eee;
616
- color: #777;
881
+ padding: 6px 14px;
882
+ font-size: 20px;
883
+ border-radius: 8px;
884
+ background: #fff;
885
+ color: #475569;
617
886
  position: fixed;
618
- bottom: 15px;
887
+ bottom: 20px;
619
888
  outline: none;
620
889
  cursor: pointer;
621
890
  z-index: 99999;
@@ -624,6 +893,8 @@ const FlowversionAPI = {
624
893
  -ms-user-select: none;
625
894
  user-select: none;
626
895
  line-height: 1.2;
896
+ box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.06);
897
+ transition: all 0.15s ease;
627
898
  }
628
899
  @media (max-width: 768px) {
629
900
  .btn-zoom{
@@ -631,20 +902,72 @@ const FlowversionAPI = {
631
902
  }
632
903
  }
633
904
  .btn-zoom:hover{
634
- background: rgba(0, 0, 0, 0.2);
905
+ background: #f1f5f9;
906
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
635
907
  }
636
908
  .btn-zoom-up{
637
- left: 15px;
909
+ left: 20px;
638
910
  }
639
911
  .btn-zoom-down{
640
- left: 60px;
641
- padding: 1px 13px 3px 13px;
912
+ left: 70px;
913
+ }
914
+ /* 图例 */
915
+ .chart-legend {
916
+ position: fixed;
917
+ top: 15px;
918
+ right: 15px;
919
+ background: #fff;
920
+ border-radius: 8px;
921
+ padding: 12px 16px;
922
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
923
+ font-size: 12px;
924
+ color: #475569;
925
+ text-align: left;
926
+ z-index: 99999;
927
+ }
928
+ .chart-legend .legend-item {
929
+ display: flex;
930
+ align-items: center;
931
+ margin-bottom: 6px;
932
+ }
933
+ .chart-legend .legend-item:last-child {
934
+ margin-bottom: 0;
935
+ }
936
+ .chart-legend .legend-dot {
937
+ width: 12px;
938
+ height: 12px;
939
+ border-radius: 3px;
940
+ margin-right: 8px;
941
+ flex-shrink: 0;
942
+ }
943
+ .legend-dot.completed { background: #dbeafe; border: 1.5px solid #3b82f6; }
944
+ .legend-dot.current { background: #d1fae5; border: 1.5px solid #10b981; }
945
+ .legend-dot.pending { background: #f1f5f9; border: 1.5px solid #cbd5e1; }
946
+ .legend-dot.skipped { background: #f9fafb; border: 1.5px dashed #94a3b8; }
947
+ .legend-dot.condition { background: #fef3c7; border: 1.5px solid #f59e0b; }
948
+ .chart-legend .legend-line {
949
+ width: 24px;
950
+ height: 3px;
951
+ margin-right: 8px;
952
+ flex-shrink: 0;
953
+ border-radius: 2px;
642
954
  }
955
+ .legend-line.traversed { background: #10b981; }
956
+ .legend-line.predicted { background: repeating-linear-gradient(90deg, #3b82f6 0px, #3b82f6 5px, transparent 5px, transparent 8px); height: 2px; }
643
957
  </style>
644
958
  </head>
645
959
  <body>
646
960
  <div class = "loading">Loading...</div>
647
961
  <div class = "error-msg">${error_msg}</div>
962
+ <div class="chart-legend">
963
+ <div class="legend-item"><span class="legend-dot completed"></span>已完成</div>
964
+ <div class="legend-item"><span class="legend-dot current"></span>当前步骤</div>
965
+ <div class="legend-item"><span class="legend-dot pending"></span>未流转</div>
966
+ <div class="legend-item"><span class="legend-dot skipped"></span>已跳过</div>
967
+ <div class="legend-item"><span class="legend-dot condition"></span>条件判断</div>
968
+ <div class="legend-item"><span class="legend-line traversed"></span>已流转路径</div>
969
+ <div class="legend-item"><span class="legend-line predicted"></span>预测路径</div>
970
+ </div>
648
971
  <div class="mermaid"></div>
649
972
  <script type="text/javascript">
650
973
  mermaid.initialize({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steedos-labs/plugin-workflow",
3
- "version": "3.0.83",
3
+ "version": "3.0.84",
4
4
  "main": "package.service.js",
5
5
  "license": "MIT",
6
6
  "files": [