@lambo-design/workflow-approve 1.0.0-beta.140 → 1.0.0-beta.141

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.
@@ -0,0 +1,810 @@
1
+ <template>
2
+ <div class="reject-node-prototype">
3
+ <Table
4
+ border
5
+ size="small"
6
+ :data="tableRows"
7
+ :columns="tableColumns"
8
+ :span-method="spanMethod"
9
+ :row-class-name="rowClassName"
10
+ @on-row-click="handleRowClick"
11
+ />
12
+
13
+ <Form :label-width="90" class="reject-node-prototype__takeover-form">
14
+ <FormItem label="驳回给:">
15
+ <RadioGroup v-model="rejectToTakeOver" @on-change="handleRejectToTakeOverChange">
16
+ <Radio label="original">原办理人</Radio>
17
+ <Radio label="actual">实际办理人</Radio>
18
+ </RadioGroup>
19
+ </FormItem>
20
+ </Form>
21
+
22
+ <div class="reject-node-prototype__summary">
23
+ 当前选择:<strong>{{ selectedSummary || '未选择' }}</strong>
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script>
29
+ export default {
30
+ name: 'RejectNodeTable',
31
+ props: {
32
+ nodes: {
33
+ type: Array,
34
+ default: () => []
35
+ },
36
+ currentTaskNode: {
37
+ type: [String, Number],
38
+ default: ''
39
+ },
40
+ defaultRejectToTakeOver: {
41
+ type: Boolean,
42
+ default: false
43
+ },
44
+ visible: {
45
+ type: Boolean,
46
+ default: false
47
+ }
48
+ },
49
+ data() {
50
+ return {
51
+ rejectNodeCollapsedGroupKeys: [],
52
+ selectedRejectNodeTaskNodes: [],
53
+ selectedRejectNodePathKeys: [],
54
+ selectedRejectNodePathEntries: [],
55
+ selectedRejectNodeNames: [],
56
+ rejectToTakeOver: this.defaultRejectToTakeOver ? 'actual' : 'original'
57
+ }
58
+ },
59
+ computed: {
60
+ tableRows() {
61
+ if (!Array.isArray(this.nodes) || this.nodes.length === 0) {
62
+ return []
63
+ }
64
+ return this.buildRejectNodeTableRows()
65
+ },
66
+ tableColumns() {
67
+ const self = this
68
+ return [
69
+ {
70
+ title: '',
71
+ key: 'selected',
72
+ width: 56,
73
+ align: 'center',
74
+ render: (h, params) => {
75
+ if (params.row.type === 'group') {
76
+ return h('div', {
77
+ class: 'reject-node-prototype__group-cell'
78
+ }, [
79
+ h('Button', {
80
+ class: 'reject-node-prototype__group-toggle',
81
+ props: { type: 'text' },
82
+ style: {
83
+ paddingLeft: `${Math.max((params.row.groupLevel || 1) - 1, 0) * 16}px`
84
+ },
85
+ on: {
86
+ click: event => {
87
+ event.stopPropagation()
88
+ self.toggleRejectNodeGroup(params.row)
89
+ }
90
+ }
91
+ }, [
92
+ h('span', {
93
+ class: 'reject-node-prototype__group-arrow'
94
+ }, self.isRejectNodeGroupCollapsed(params.row.groupId) ? '▸' : '▾'),
95
+ h('span', {
96
+ class: 'reject-node-prototype__group-label'
97
+ }, params.row.groupName || params.row.gatewayLabel || '无网关')
98
+ ])
99
+ ].filter(Boolean))
100
+ }
101
+ const checked = self.isRejectNodeSelected(params.row)
102
+ const disabled = self.isRejectNodeDisabled(params.row) && !checked
103
+ return h('Checkbox', {
104
+ props: {
105
+ value: checked,
106
+ disabled
107
+ },
108
+ nativeOn: {
109
+ click: event => event.stopPropagation()
110
+ },
111
+ on: {
112
+ 'on-change': value => {
113
+ self.toggleRejectNodeSelection(params.row, value)
114
+ }
115
+ }
116
+ })
117
+ }
118
+ },
119
+ {
120
+ title: '节点名称',
121
+ key: 'taskName',
122
+ minWidth: 180,
123
+ render: (h, params) => {
124
+ if (params.row.type === 'group') {
125
+ return h('div')
126
+ }
127
+ return h('div', {
128
+ class: 'reject-node-prototype__name-cell',
129
+ style: {
130
+ paddingLeft: `${Math.min(params.row.depth || 0, 4) * 16}px`
131
+ }
132
+ }, [
133
+ h('span', {
134
+ class: {
135
+ 'reject-node-prototype__node-name': true,
136
+ 'is-current': String(params.row.taskNode) === String(this.currentTaskNode)
137
+ }
138
+ }, params.row.taskName || params.row.name || params.row.title || '')
139
+ ])
140
+ }
141
+ },
142
+ {
143
+ title: '节点状态',
144
+ key: 'auditResult',
145
+ width: 160,
146
+ align: 'center',
147
+ render: (h, params) => {
148
+ if (params.row.type === 'group') {
149
+ return h('div')
150
+ }
151
+ const status = self.getRejectNodeStatus(params.row)
152
+ return h('Tag', { props: { color: status.color } }, status.label)
153
+ }
154
+ }
155
+ ]
156
+ },
157
+ selectedSummary() {
158
+ return this.selectedRejectNodeNames.join('、')
159
+ }
160
+ },
161
+ watch: {
162
+ visible: {
163
+ immediate: true,
164
+ handler(value) {
165
+ if (value) {
166
+ this.resetSelection()
167
+ return
168
+ }
169
+ this.resetSelection(false)
170
+ }
171
+ },
172
+ nodes: {
173
+ deep: true,
174
+ handler() {
175
+ if (this.visible) {
176
+ this.resetSelection()
177
+ }
178
+ }
179
+ }
180
+ },
181
+ methods: {
182
+ resetSelection(emitChange = true) {
183
+ this.rejectNodeCollapsedGroupKeys = []
184
+ this.selectedRejectNodeTaskNodes = []
185
+ this.selectedRejectNodePathKeys = []
186
+ this.selectedRejectNodePathEntries = []
187
+ this.selectedRejectNodeNames = []
188
+ this.rejectToTakeOver = this.defaultRejectToTakeOver ? 'actual' : 'original'
189
+ if (emitChange) {
190
+ this.emitSelectionChange()
191
+ }
192
+ },
193
+ emitSelectionChange() {
194
+ this.$emit('selection-change', {
195
+ taskNodes: this.selectedRejectNodeTaskNodes.slice(),
196
+ taskNames: this.selectedRejectNodeNames.slice(),
197
+ pathKeys: this.selectedRejectNodePathKeys.slice(),
198
+ pathEntries: this.selectedRejectNodePathEntries.map(item => ({
199
+ pathKey: item.pathKey,
200
+ pathIds: Array.isArray(item.pathIds) ? item.pathIds.slice() : []
201
+ })),
202
+ rejectToTakeOver: this.rejectToTakeOver
203
+ })
204
+ },
205
+ handleRejectToTakeOverChange() {
206
+ this.emitSelectionChange()
207
+ },
208
+ handleRowClick(row) {
209
+ if (!row) {
210
+ return
211
+ }
212
+ if (row.type === 'group') {
213
+ this.toggleRejectNodeGroup(row)
214
+ return
215
+ }
216
+ this.toggleRejectNodeSelection(row)
217
+ },
218
+ normalizeRejectNode(node, nodeIndex, nodeNameMap) {
219
+ const rejectPaths = Array.isArray(node?.rejectPaths) ? node.rejectPaths : []
220
+ const startGatewayPaths = Array.isArray(node?.startGatewayPaths) ? node.startGatewayPaths : []
221
+ const pathEntries = rejectPaths
222
+ .map((path, index) => {
223
+ const pathIds = Array.isArray(path) ? path.filter(Boolean).map(item => String(item)) : []
224
+ const pathKey = pathIds.join('>')
225
+ const gatewayPath = Array.isArray(startGatewayPaths[index]) ? startGatewayPaths[index] : []
226
+ const gatewayLabel = this.formatGatewayPathLabel(gatewayPath)
227
+ const pathLabel = this.formatRejectPathLabel(pathIds, nodeNameMap)
228
+ const pathOrder = pathIds.indexOf(String(node?.taskNode))
229
+ return {
230
+ pathKey,
231
+ pathLabel,
232
+ gatewayLabel,
233
+ gatewayPath,
234
+ pathOrder: pathOrder === -1 ? pathIds.length : pathOrder,
235
+ pathIds
236
+ }
237
+ })
238
+ .filter(item => item.pathKey)
239
+ .sort((left, right) => this.compareRejectNodePathEntries(left, right))
240
+
241
+ return {
242
+ ...node,
243
+ _nodeIndex: nodeIndex,
244
+ pathEntries,
245
+ pathKeys: pathEntries.map(item => item.pathKey)
246
+ }
247
+ },
248
+ formatRejectPathLabel(pathIds, nodeNameMap) {
249
+ return pathIds.map(id => nodeNameMap.get(id) || id).join(' > ')
250
+ },
251
+ formatGatewayPathLabel(gatewayPath) {
252
+ if (!Array.isArray(gatewayPath) || gatewayPath.length === 0) {
253
+ return ''
254
+ }
255
+ return gatewayPath
256
+ .map(item => item.groupName || item.groupId)
257
+ .filter(Boolean)
258
+ .join(' > ')
259
+ },
260
+ getRejectNodeGatewaySortKey(gatewayPath) {
261
+ if (!Array.isArray(gatewayPath) || gatewayPath.length === 0) {
262
+ return ''
263
+ }
264
+ return gatewayPath
265
+ .map(item => String(item?.groupId || item?.groupName || ''))
266
+ .filter(Boolean)
267
+ .join('>')
268
+ },
269
+ compareRejectNodePathEntries(left, right) {
270
+ const leftDepth = Array.isArray(left?.gatewayPath) ? left.gatewayPath.length : 0
271
+ const rightDepth = Array.isArray(right?.gatewayPath) ? right.gatewayPath.length : 0
272
+ if (leftDepth !== rightDepth) {
273
+ return leftDepth - rightDepth
274
+ }
275
+ const leftGatewayKey = this.getRejectNodeGatewaySortKey(left?.gatewayPath)
276
+ const rightGatewayKey = this.getRejectNodeGatewaySortKey(right?.gatewayPath)
277
+ if (leftGatewayKey !== rightGatewayKey) {
278
+ return leftGatewayKey.localeCompare(rightGatewayKey)
279
+ }
280
+ const leftPathOrder = typeof left?.pathOrder === 'number' ? left.pathOrder : Number.MAX_SAFE_INTEGER
281
+ const rightPathOrder = typeof right?.pathOrder === 'number' ? right.pathOrder : Number.MAX_SAFE_INTEGER
282
+ if (leftPathOrder !== rightPathOrder) {
283
+ return leftPathOrder - rightPathOrder
284
+ }
285
+ return String(left?.pathKey || '').localeCompare(String(right?.pathKey || ''))
286
+ },
287
+ getRejectNodeSortKey(node) {
288
+ const pathEntries = Array.isArray(node?.pathEntries) ? node.pathEntries : []
289
+ if (pathEntries.length === 0) {
290
+ const taskName = String(node?.taskName || node?.name || node?.title || node?.taskNode || '')
291
+ return `z|z|${taskName}`
292
+ }
293
+ const firstEntry = pathEntries[0]
294
+ const depth = Array.isArray(firstEntry?.gatewayPath) ? firstEntry.gatewayPath.length : 0
295
+ const gatewayKey = this.getRejectNodeGatewaySortKey(firstEntry?.gatewayPath)
296
+ const pathOrder = typeof firstEntry?.pathOrder === 'number' ? firstEntry.pathOrder : Number.MAX_SAFE_INTEGER
297
+ const taskName = String(node?.taskName || node?.name || node?.title || node?.taskNode || '')
298
+ return `${String(depth).padStart(4, '0')}|${gatewayKey}|${String(pathOrder).padStart(4, '0')}|${taskName}`
299
+ },
300
+ getRejectNodePathKeys(node) {
301
+ if (Array.isArray(node?.pathKeys) && node.pathKeys.length > 0) {
302
+ return node.pathKeys
303
+ }
304
+ if (node?.pathKey) {
305
+ return [node.pathKey]
306
+ }
307
+ return this.normalizeRejectNode(node, 0, new Map()).pathKeys
308
+ },
309
+ isRejectNodeSelected(row) {
310
+ if (!row || row.type !== 'node') {
311
+ return false
312
+ }
313
+ return this.selectedRejectNodeTaskNodes.includes(String(row.taskNode))
314
+ },
315
+ getRejectNodePathKeysByTaskNode(taskNode) {
316
+ if (!taskNode) {
317
+ return []
318
+ }
319
+ const nodes = Array.isArray(this.nodes)
320
+ ? this.nodes.filter(item => item && String(item.taskNode) === String(taskNode))
321
+ : []
322
+ if (!nodes.length) {
323
+ return []
324
+ }
325
+ const pathKeys = []
326
+ nodes.forEach(node => {
327
+ this.getRejectNodePathKeys(node).forEach(pathKey => {
328
+ if (pathKey && !pathKeys.includes(pathKey)) {
329
+ pathKeys.push(pathKey)
330
+ }
331
+ })
332
+ })
333
+ return pathKeys
334
+ },
335
+ getRejectNodePathEntriesByTaskNode(taskNode) {
336
+ if (!taskNode) {
337
+ return []
338
+ }
339
+ const nodes = Array.isArray(this.nodes)
340
+ ? this.nodes.filter(item => item && String(item.taskNode) === String(taskNode))
341
+ : []
342
+ if (!nodes.length) {
343
+ return []
344
+ }
345
+ const pathEntries = []
346
+ nodes.forEach(node => {
347
+ this.normalizeRejectNode(node, 0, new Map()).pathEntries.forEach(pathEntry => {
348
+ if (!pathEntry || !pathEntry.pathKey) {
349
+ return
350
+ }
351
+ if (!pathEntries.some(item => item.pathKey === pathEntry.pathKey)) {
352
+ pathEntries.push({
353
+ pathKey: pathEntry.pathKey,
354
+ pathIds: Array.isArray(pathEntry.pathIds) ? pathEntry.pathIds.slice() : []
355
+ })
356
+ }
357
+ })
358
+ })
359
+ return pathEntries
360
+ },
361
+ isRejectPathRelated(pathIdsA, pathIdsB) {
362
+ if (!Array.isArray(pathIdsA) || !Array.isArray(pathIdsB) || pathIdsA.length === 0 || pathIdsB.length === 0) {
363
+ return false
364
+ }
365
+ const minLength = Math.min(pathIdsA.length, pathIdsB.length)
366
+ for (let i = 0; i < minLength; i += 1) {
367
+ if (String(pathIdsA[i]) !== String(pathIdsB[i])) {
368
+ return false
369
+ }
370
+ }
371
+ return true
372
+ },
373
+ syncSelection(taskNodeList) {
374
+ const uniqueTaskNodes = Array.from(new Set((taskNodeList || []).map(item => String(item)).filter(Boolean)))
375
+ this.selectedRejectNodeTaskNodes = uniqueTaskNodes
376
+
377
+ const selectedNames = []
378
+ const selectedPathKeys = []
379
+ const selectedPathEntries = []
380
+ uniqueTaskNodes.forEach(taskNode => {
381
+ const node = Array.isArray(this.nodes) ? this.nodes.find(item => item && String(item.taskNode) === taskNode) : null
382
+ const nodeName = node ? (node.taskName || node.name || node.title || '') : ''
383
+ if (nodeName) {
384
+ selectedNames.push(nodeName)
385
+ }
386
+ this.getRejectNodePathEntriesByTaskNode(taskNode).forEach(pathEntry => {
387
+ if (!pathEntry || !pathEntry.pathKey) {
388
+ return
389
+ }
390
+ if (!selectedPathKeys.includes(pathEntry.pathKey)) {
391
+ selectedPathKeys.push(pathEntry.pathKey)
392
+ }
393
+ const exists = selectedPathEntries.some(item => item.pathKey === pathEntry.pathKey)
394
+ if (!exists) {
395
+ selectedPathEntries.push({
396
+ pathKey: pathEntry.pathKey,
397
+ pathIds: Array.isArray(pathEntry.pathIds) ? pathEntry.pathIds.slice() : []
398
+ })
399
+ }
400
+ })
401
+ })
402
+ this.selectedRejectNodeNames = selectedNames
403
+ this.selectedRejectNodePathKeys = selectedPathKeys
404
+ this.selectedRejectNodePathEntries = selectedPathEntries
405
+ this.emitSelectionChange()
406
+ },
407
+ toggleRejectNodeSelection(row, checked) {
408
+ if (!row || row.type !== 'node') {
409
+ return
410
+ }
411
+ const taskNode = String(row.taskNode)
412
+ const selectedTaskNodes = this.selectedRejectNodeTaskNodes.slice()
413
+ const existsIndex = selectedTaskNodes.indexOf(taskNode)
414
+ const shouldSelect = typeof checked === 'boolean' ? checked : existsIndex === -1
415
+ if (shouldSelect) {
416
+ if (existsIndex === -1) {
417
+ if (this.isRejectNodeDisabled(row)) {
418
+ return
419
+ }
420
+ selectedTaskNodes.push(taskNode)
421
+ }
422
+ } else if (existsIndex > -1) {
423
+ selectedTaskNodes.splice(existsIndex, 1)
424
+ }
425
+ this.syncSelection(selectedTaskNodes)
426
+ },
427
+ isRejectNodeGroupCollapsed(groupId) {
428
+ return this.rejectNodeCollapsedGroupKeys.includes(groupId)
429
+ },
430
+ toggleRejectNodeGroup(row) {
431
+ if (!row || !row.groupId) {
432
+ return
433
+ }
434
+ const nextKeys = this.rejectNodeCollapsedGroupKeys.slice()
435
+ const collapsedIndex = nextKeys.indexOf(row.groupId)
436
+ if (collapsedIndex === -1) {
437
+ nextKeys.push(row.groupId)
438
+ } else {
439
+ nextKeys.splice(collapsedIndex, 1)
440
+ }
441
+ this.rejectNodeCollapsedGroupKeys = nextKeys
442
+ },
443
+ buildRejectNodeTableRows() {
444
+ const nodeNameMap = new Map()
445
+ const groupedNodes = this.sortRejectNodesForTable(this.mergeRejectNodesByTaskNode(this.nodes))
446
+ groupedNodes.forEach(node => {
447
+ if (node && node.taskNode) {
448
+ nodeNameMap.set(String(node.taskNode), node.taskName || node.name || node.taskNode)
449
+ }
450
+ })
451
+
452
+ const rows = []
453
+ const groupSet = new Set()
454
+ const nodeRowMap = new Map()
455
+ groupedNodes.forEach((node, nodeIndex) => {
456
+ const normalizedNode = this.normalizeRejectNode(node, nodeIndex, nodeNameMap)
457
+ const nodeKey = String(normalizedNode.taskNode || `node-${nodeIndex}`)
458
+ let nodeRow = nodeRowMap.get(nodeKey) || null
459
+ normalizedNode.pathEntries.forEach(pathEntry => {
460
+ const gatewayPath = Array.isArray(pathEntry.gatewayPath) ? pathEntry.gatewayPath : []
461
+ const ancestorGroupIds = []
462
+ let parentGroupId = ''
463
+ gatewayPath.forEach((gateway, gatewayIndex) => {
464
+ const groupId = String(gateway.groupId || `${nodeIndex}-${pathEntry.pathKey}-${gatewayIndex}`)
465
+ if (!groupSet.has(groupId)) {
466
+ groupSet.add(groupId)
467
+ rows.push({
468
+ type: 'group',
469
+ rowKey: `group:${groupId}`,
470
+ groupId,
471
+ groupName: gateway.groupName || gateway.groupId || groupId,
472
+ groupLevel: gatewayIndex + 1,
473
+ gatewayLabel: this.formatGatewayPathLabel(gatewayPath),
474
+ pathLabel: pathEntry.pathLabel,
475
+ parentGroupId,
476
+ ancestorGroupIds: ancestorGroupIds.slice()
477
+ })
478
+ }
479
+ ancestorGroupIds.push(groupId)
480
+ parentGroupId = groupId
481
+ })
482
+
483
+ if (!nodeRow) {
484
+ nodeRow = {
485
+ ...normalizedNode,
486
+ type: 'node',
487
+ rowKey: nodeKey,
488
+ pathKey: pathEntry.pathKey,
489
+ pathLabel: pathEntry.pathLabel,
490
+ gatewayLabel: pathEntry.gatewayLabel,
491
+ ancestorGroupIds: ancestorGroupIds.slice(),
492
+ depth: gatewayPath.length,
493
+ displayIndex: 0,
494
+ pathOrder: pathEntry.pathOrder,
495
+ pathKeys: [pathEntry.pathKey],
496
+ pathLabels: pathEntry.pathLabel ? [pathEntry.pathLabel] : [],
497
+ gatewayLabels: pathEntry.gatewayLabel ? [pathEntry.gatewayLabel] : []
498
+ }
499
+ } else {
500
+ if (!Array.isArray(nodeRow.pathKeys)) {
501
+ nodeRow.pathKeys = []
502
+ }
503
+ if (pathEntry.pathKey && !nodeRow.pathKeys.includes(pathEntry.pathKey)) {
504
+ nodeRow.pathKeys.push(pathEntry.pathKey)
505
+ }
506
+ if (!Array.isArray(nodeRow.pathLabels)) {
507
+ nodeRow.pathLabels = []
508
+ }
509
+ if (pathEntry.pathLabel && !nodeRow.pathLabels.includes(pathEntry.pathLabel)) {
510
+ nodeRow.pathLabels.push(pathEntry.pathLabel)
511
+ }
512
+ if (!Array.isArray(nodeRow.gatewayLabels)) {
513
+ nodeRow.gatewayLabels = []
514
+ }
515
+ if (pathEntry.gatewayLabel && !nodeRow.gatewayLabels.includes(pathEntry.gatewayLabel)) {
516
+ nodeRow.gatewayLabels.push(pathEntry.gatewayLabel)
517
+ }
518
+ nodeRow.ancestorGroupIds = Array.from(new Set([...(nodeRow.ancestorGroupIds || []), ...ancestorGroupIds]))
519
+ nodeRow.depth = Math.max(nodeRow.depth || 0, gatewayPath.length)
520
+ nodeRow.pathOrder = Math.min(nodeRow.pathOrder ?? pathEntry.pathOrder, pathEntry.pathOrder)
521
+ }
522
+ })
523
+ if (nodeRow && !nodeRowMap.has(nodeKey)) {
524
+ nodeRowMap.set(nodeKey, nodeRow)
525
+ rows.push(nodeRow)
526
+ }
527
+ })
528
+
529
+ const visibleRows = []
530
+ rows.forEach(row => {
531
+ const hidden = Array.isArray(row.ancestorGroupIds) && row.ancestorGroupIds.some(groupId => this.isRejectNodeGroupCollapsed(groupId))
532
+ if (hidden) {
533
+ return
534
+ }
535
+ visibleRows.push(row)
536
+ })
537
+
538
+ return visibleRows
539
+ },
540
+ sortRejectNodesForTable(nodes) {
541
+ const list = Array.isArray(nodes) ? nodes.slice() : []
542
+ return list.sort((left, right) => {
543
+ const leftSortKey = this.getRejectNodeSortKey(this.normalizeRejectNode(left, 0, new Map()))
544
+ const rightSortKey = this.getRejectNodeSortKey(this.normalizeRejectNode(right, 0, new Map()))
545
+ return leftSortKey.localeCompare(rightSortKey)
546
+ })
547
+ },
548
+ mergeRejectNodesByTaskNode(nodes) {
549
+ const groupedMap = new Map()
550
+ const sourceNodes = Array.isArray(nodes) ? nodes : []
551
+ sourceNodes.forEach((node, index) => {
552
+ if (!node) {
553
+ return
554
+ }
555
+ const taskNodeKey = node.taskNode ? String(node.taskNode) : `__index__${index}`
556
+ const existing = groupedMap.get(taskNodeKey)
557
+ if (!existing) {
558
+ groupedMap.set(taskNodeKey, {
559
+ ...node,
560
+ rejectPaths: Array.isArray(node.rejectPaths) ? node.rejectPaths.slice() : [],
561
+ startGatewayPaths: Array.isArray(node.startGatewayPaths) ? node.startGatewayPaths.slice() : []
562
+ })
563
+ return
564
+ }
565
+ if (!Array.isArray(existing.rejectPaths)) {
566
+ existing.rejectPaths = []
567
+ }
568
+ if (!Array.isArray(existing.startGatewayPaths)) {
569
+ existing.startGatewayPaths = []
570
+ }
571
+ ;(Array.isArray(node.rejectPaths) ? node.rejectPaths : []).forEach((path, pathIndex) => {
572
+ const pathKey = JSON.stringify(path || [])
573
+ const exists = existing.rejectPaths.some(item => JSON.stringify(item || []) === pathKey)
574
+ if (!exists) {
575
+ existing.rejectPaths.push(Array.isArray(path) ? path.slice() : path)
576
+ const gatewayPath = Array.isArray(node.startGatewayPaths) ? node.startGatewayPaths[pathIndex] : null
577
+ if (gatewayPath) {
578
+ const gatewayKey = JSON.stringify(gatewayPath || [])
579
+ const gatewayExists = existing.startGatewayPaths.some(item => JSON.stringify(item || []) === gatewayKey)
580
+ if (!gatewayExists) {
581
+ existing.startGatewayPaths.push(Array.isArray(gatewayPath) ? gatewayPath.slice() : gatewayPath)
582
+ }
583
+ }
584
+ }
585
+ })
586
+ Object.keys(node).forEach(key => {
587
+ if (key === 'rejectPaths' || key === 'startGatewayPaths') {
588
+ return
589
+ }
590
+ if (existing[key] === undefined || existing[key] === null || existing[key] === '') {
591
+ existing[key] = node[key]
592
+ }
593
+ })
594
+ })
595
+ return Array.from(groupedMap.values())
596
+ },
597
+ isRejectNodeDisabled(row) {
598
+ if (!row || row.type !== 'node') {
599
+ return false
600
+ }
601
+ if (this.isRejectNodeSelected(row)) {
602
+ return false
603
+ }
604
+ const rowPathEntries = Array.isArray(row.pathEntries) && row.pathEntries.length > 0
605
+ ? row.pathEntries
606
+ : this.normalizeRejectNode(row, 0, new Map()).pathEntries
607
+ if (rowPathEntries.length === 0 || this.selectedRejectNodePathEntries.length === 0) {
608
+ return false
609
+ }
610
+ return rowPathEntries.some(rowPathEntry => {
611
+ return this.selectedRejectNodePathEntries.some(selectedPathEntry => {
612
+ return this.isRejectPathRelated(rowPathEntry.pathIds, selectedPathEntry.pathIds)
613
+ })
614
+ })
615
+ },
616
+ spanMethod({ row, columnIndex }) {
617
+ if (row && row.type === 'group') {
618
+ return columnIndex === 0 ? [1, this.tableColumns.length] : [0, 0]
619
+ }
620
+ return [1, 1]
621
+ },
622
+ rowClassName(row) {
623
+ if (!row) {
624
+ return ''
625
+ }
626
+ if (row.type === 'group') {
627
+ return 'reject-node-prototype__group-row'
628
+ }
629
+ const selected = this.isRejectNodeSelected(row)
630
+ const disabled = !selected && this.isRejectNodeDisabled(row)
631
+ return [
632
+ 'reject-node-prototype__node-row',
633
+ selected && 'is-selected',
634
+ disabled && 'is-disabled'
635
+ ].filter(Boolean).join(' ')
636
+ },
637
+ getRejectNodeStatus(row) {
638
+ if (String(row.taskNode) === String(this.currentTaskNode)) {
639
+ return { label: '当前节点', color: 'orange' }
640
+ }
641
+ if (!row.auditResult) {
642
+ return { label: `未${row.handleName ? row.handleName : '审批'}`, color: 'orange' }
643
+ }
644
+ const statusMap = {
645
+ '30': { label: `${row.handleName ? row.handleName : '审批'}通过`, color: 'green' },
646
+ '10': { label: '首节点自动跳过', color: 'green' },
647
+ '11': { label: '与上一环节办理人相同自动跳过', color: 'green' },
648
+ '12': { label: '与发起人相同自动跳过', color: 'green' },
649
+ '13': { label: '办理人为空自动跳过', color: 'green' },
650
+ '14': { label: '符合流程变量条件自动跳过', color: 'green' },
651
+ '40': { label: `${row.rejectName ? row.rejectName : '驳回'}到上一节点`, color: 'volcano' },
652
+ '50': { label: `${row.rejectName ? row.rejectName : '驳回'}到原点`, color: 'red' },
653
+ '51': { label: '流程终止', color: 'purple' },
654
+ '60': { label: '撤回', color: 'blue' },
655
+ '61': { label: '交回委派任务', color: 'green' },
656
+ '62': { label: '委派任务已撤回', color: 'blue' },
657
+ '63': { label: '委派任务', color: 'orange' },
658
+ '80': { label: '跳转到指定节点', color: 'cyan' },
659
+ '82': { label: '指定他人处理', color: 'cyan' },
660
+ '83': { label: '会签减签', color: 'blue' },
661
+ '90': { label: `${row.rejectName ? row.rejectName : '驳回'}到指定节点`, color: 'magenta' },
662
+ '92': { label: `${row.rejectName ? row.rejectName : '驳回'}到指定节点`, color: 'magenta' },
663
+ '31': { label: '会签表决通过', color: 'green' }
664
+ }
665
+ return statusMap[row.auditResult] || { label: row.auditResult, color: 'default' }
666
+ }
667
+ }
668
+ }
669
+ </script>
670
+
671
+ <style lang="less" scoped>
672
+ .reject-node-prototype {
673
+ display: flex;
674
+ flex-direction: column;
675
+ gap: 12px;
676
+ }
677
+
678
+ .reject-node-prototype /deep/ .ivu-table {
679
+ font-size: 14px;
680
+ color: #333333;
681
+ }
682
+
683
+ .reject-node-prototype /deep/ .ivu-table th,
684
+ .reject-node-prototype /deep/ .ivu-table td {
685
+ box-sizing: border-box;
686
+ text-align: center;
687
+ vertical-align: middle;
688
+ }
689
+
690
+ .reject-node-prototype /deep/ .ivu-table th {
691
+ height: 40px;
692
+ background: #f5f7fa;
693
+ font-weight: 700;
694
+ color: #666666;
695
+ }
696
+
697
+ .reject-node-prototype /deep/ .ivu-table td {
698
+ height: 40px;
699
+ padding: 6px 10px;
700
+ background: #ffffff;
701
+ }
702
+
703
+ .reject-node-prototype /deep/ .ivu-table-cell {
704
+ width: 100%;
705
+ }
706
+
707
+ .reject-node-prototype /deep/ .reject-node-prototype__group-row td {
708
+ height: 32px;
709
+ padding: 0 12px;
710
+ text-align: left;
711
+ }
712
+
713
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__group-toggle {
714
+ display: inline-flex;
715
+ align-items: center;
716
+ gap: 6px;
717
+ padding: 0;
718
+ border: 0;
719
+ background: transparent;
720
+ color: #333333;
721
+ font-size: 14px;
722
+ line-height: 1;
723
+ height: auto;
724
+ cursor: pointer;
725
+ }
726
+
727
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__group-arrow {
728
+ width: 12px;
729
+ color: #808695;
730
+ text-align: center;
731
+ }
732
+
733
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__group-label {
734
+ font-weight: 600;
735
+ }
736
+
737
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__group-desc {
738
+ margin-left: 16px;
739
+ color: #808695;
740
+ font-size: 12px;
741
+ white-space: normal;
742
+ }
743
+
744
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__node-name {
745
+ display: inline-block;
746
+ max-width: 100%;
747
+ overflow: hidden;
748
+ text-overflow: ellipsis;
749
+ white-space: nowrap;
750
+ }
751
+
752
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__name-cell {
753
+ display: flex;
754
+ align-items: center;
755
+ width: 100%;
756
+ text-align: left;
757
+ }
758
+
759
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__node-name.is-current {
760
+ color: #2d8cf0;
761
+ font-weight: 600;
762
+ }
763
+
764
+ .reject-node-prototype /deep/ .ivu-table-cell .reject-node-prototype__group-cell {
765
+ display: flex;
766
+ align-items: center;
767
+ flex-wrap: wrap;
768
+ gap: 6px;
769
+ min-height: 28px;
770
+ text-align: left;
771
+ }
772
+
773
+ .reject-node-prototype /deep/ .reject-node-prototype__node-row {
774
+ cursor: pointer;
775
+ }
776
+
777
+ .reject-node-prototype /deep/ .reject-node-prototype__node-row.is-disabled {
778
+ cursor: not-allowed;
779
+ }
780
+
781
+ .reject-node-prototype /deep/ .reject-node-prototype__group-row {
782
+ cursor: default;
783
+ }
784
+
785
+ .reject-node-prototype__takeover-form {
786
+ margin-top: 4px;
787
+ }
788
+
789
+ .reject-node-prototype__summary {
790
+ min-height: 20px;
791
+ color: #515a6e;
792
+ font-size: 12px;
793
+ }
794
+
795
+ .reject-node-prototype /deep/ .reject-node-prototype__node-row.is-selected td {
796
+ background: #ecf5ff;
797
+ }
798
+
799
+ .reject-node-prototype /deep/ .reject-node-prototype__node-row.is-disabled td {
800
+ color: #c5c8ce;
801
+ }
802
+
803
+ .reject-node-prototype /deep/ .reject-node-prototype__node-row.is-disabled:hover td {
804
+ background: transparent;
805
+ }
806
+
807
+ .reject-node-prototype /deep/ .reject-node-prototype__node-row.is-disabled .reject-node-prototype__node-name {
808
+ color: #c5c8ce;
809
+ }
810
+ </style>