android-midscene-automation 0.1.19 → 0.1.21
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/CHANGELOG.md +25 -0
- package/README.md +25 -0
- package/package.json +9 -1
- package/server/appium-recorder/appium-runner.ts +41 -20
- package/server/appium-recorder/repository.ts +27 -6
- package/src/appium-recorder/AppiumPage.vue +77 -48
- package/src/appium-recorder/components/FlowStepEditor.vue +0 -8
- package/src/appium-recorder/components/NestedConditionBranches.vue +373 -0
- package/src/appium-recorder/components/RecordedSteps.vue +607 -23
- package/src/appium-recorder/flow-copy.ts +193 -0
- package/src/appium-recorder/flow-layout.ts +88 -0
- package/src/style.css +163 -34
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, reactive, shallowRef, watch } from 'vue';
|
|
3
|
-
import {
|
|
3
|
+
import { ElMessage } from 'element-plus';
|
|
4
|
+
import { CopyDocument, Delete, Edit, FullScreen, Link, Timer, VideoPlay } from '@element-plus/icons-vue';
|
|
4
5
|
import type { AppiumRecordedStep } from '../types';
|
|
6
|
+
import { createFlowClipboard } from '../flow-copy';
|
|
7
|
+
import {
|
|
8
|
+
buildConditionLayouts,
|
|
9
|
+
FLOW_BRANCH_GAP,
|
|
10
|
+
FLOW_NODE_WIDTH,
|
|
11
|
+
placeConditionLayout,
|
|
12
|
+
} from '../flow-layout';
|
|
5
13
|
import FlowStepEditor from './FlowStepEditor.vue';
|
|
14
|
+
import NestedConditionBranches from './NestedConditionBranches.vue';
|
|
6
15
|
|
|
7
16
|
type FlowKind = 'action' | 'condition' | 'assertion';
|
|
8
17
|
type BranchName = 'yes' | 'no';
|
|
9
18
|
type FlowViewport = 'main' | 'overview';
|
|
10
19
|
type StepItem = { step: AppiumRecordedStep; index: number };
|
|
20
|
+
const PASTE_COMMAND = '__paste_flow_nodes__';
|
|
11
21
|
type InsertAction =
|
|
12
22
|
| 'delay'
|
|
13
23
|
| 'tap'
|
|
@@ -39,10 +49,13 @@ const props = defineProps<{
|
|
|
39
49
|
disabled?: boolean;
|
|
40
50
|
allowedLockedActions?: InsertAction[];
|
|
41
51
|
launchingStepId?: string;
|
|
52
|
+
clipboardCount?: number;
|
|
42
53
|
}>();
|
|
43
54
|
|
|
44
55
|
const emit = defineEmits<{
|
|
45
56
|
remove: [index: number];
|
|
57
|
+
copy: [indexes: number[]];
|
|
58
|
+
paste: [index: number, branch?: BranchName];
|
|
46
59
|
addDelay: [index?: number];
|
|
47
60
|
insertAction: [index: number, action: InsertAction];
|
|
48
61
|
insertBranchAction: [index: number, branch: BranchName, action: InsertAction];
|
|
@@ -54,6 +67,8 @@ const emit = defineEmits<{
|
|
|
54
67
|
}>();
|
|
55
68
|
|
|
56
69
|
const expandedStepIndex = shallowRef<number | null>(null);
|
|
70
|
+
const copyMode = shallowRef(false);
|
|
71
|
+
const selectedCopyIndexes = shallowRef<number[]>([]);
|
|
57
72
|
const flowDialogVisible = shallowRef(false);
|
|
58
73
|
const flowPan = reactive({ x: 0, y: 0 });
|
|
59
74
|
const flowScale = shallowRef(1);
|
|
@@ -79,6 +94,11 @@ const visibleStepItems = computed(() => stepItems.value
|
|
|
79
94
|
.filter(({ step }) => !branchTargetIds.value.has(step.id)));
|
|
80
95
|
const hasFlowSteps = computed(() => visibleStepItems.value.length > 0);
|
|
81
96
|
const hasLaunchAppStep = computed(() => props.steps.some((step) => step.type === 'launchApp'));
|
|
97
|
+
const conditionLayouts = computed(() => buildConditionLayouts(props.steps));
|
|
98
|
+
const flowContentWidth = computed(() => Math.max(
|
|
99
|
+
980,
|
|
100
|
+
...visibleStepItems.value.map(({ step }) => conditionLayouts.value.get(step.id)?.totalWidth || 0),
|
|
101
|
+
));
|
|
82
102
|
const flowAxisByStepId = computed(() => {
|
|
83
103
|
const axes = new Map<string, number>();
|
|
84
104
|
let inheritedAxis = 50;
|
|
@@ -86,8 +106,15 @@ const flowAxisByStepId = computed(() => {
|
|
|
86
106
|
const incomingAxes: number[] = [];
|
|
87
107
|
visibleStepItems.value.forEach(({ step: condition }) => {
|
|
88
108
|
if (defaultKind(condition) !== 'condition') return;
|
|
89
|
-
|
|
90
|
-
if (
|
|
109
|
+
const layout = conditionLayouts.value.get(condition.id);
|
|
110
|
+
if (!layout) return;
|
|
111
|
+
const placement = placeConditionLayout(
|
|
112
|
+
layout,
|
|
113
|
+
flowContentWidth.value,
|
|
114
|
+
axes.get(condition.id) ?? inheritedAxis,
|
|
115
|
+
);
|
|
116
|
+
if (branchConnectionTargetId(condition, 'yes') === step.id) incomingAxes.push(placement.yesAxis);
|
|
117
|
+
if (branchConnectionTargetId(condition, 'no') === step.id) incomingAxes.push(placement.noAxis);
|
|
91
118
|
});
|
|
92
119
|
if (incomingAxes.length) {
|
|
93
120
|
inheritedAxis = incomingAxes.reduce((total, axis) => total + axis, 0) / incomingAxes.length;
|
|
@@ -221,7 +248,7 @@ function targetLabel(id?: string) {
|
|
|
221
248
|
if (!id) return '结束';
|
|
222
249
|
const index = props.steps.findIndex((step) => step.id === id);
|
|
223
250
|
if (index < 0) return '未找到目标';
|
|
224
|
-
return
|
|
251
|
+
return props.steps[index].label;
|
|
225
252
|
}
|
|
226
253
|
|
|
227
254
|
function branchStepItems(step: AppiumRecordedStep, branch: BranchName) {
|
|
@@ -269,21 +296,51 @@ function flowAxisStyle(step: AppiumRecordedStep) {
|
|
|
269
296
|
return { '--flow-axis': `${flowAxis(step)}%` };
|
|
270
297
|
}
|
|
271
298
|
|
|
299
|
+
function conditionLayout(step: AppiumRecordedStep) {
|
|
300
|
+
return conditionLayouts.value.get(step.id) || {
|
|
301
|
+
yesWidth: FLOW_NODE_WIDTH,
|
|
302
|
+
noWidth: FLOW_NODE_WIDTH,
|
|
303
|
+
totalWidth: FLOW_NODE_WIDTH * 2 + FLOW_BRANCH_GAP,
|
|
304
|
+
yesAxis: 25,
|
|
305
|
+
noAxis: 75,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function branchAxis(step: AppiumRecordedStep, branch: BranchName) {
|
|
310
|
+
const layout = conditionLayout(step);
|
|
311
|
+
const placement = placeConditionLayout(layout, flowContentWidth.value, flowAxis(step));
|
|
312
|
+
return branch === 'yes' ? placement.yesAxis : placement.noAxis;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function branchLayoutStyle(step: AppiumRecordedStep) {
|
|
316
|
+
const layout = conditionLayout(step);
|
|
317
|
+
const placement = placeConditionLayout(layout, flowContentWidth.value, flowAxis(step));
|
|
318
|
+
return {
|
|
319
|
+
width: `${layout.totalWidth}px`,
|
|
320
|
+
minWidth: `${layout.totalWidth}px`,
|
|
321
|
+
gridTemplateColumns: `${layout.yesWidth}px ${layout.noWidth}px`,
|
|
322
|
+
justifySelf: 'start',
|
|
323
|
+
marginLeft: `${placement.left}px`,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
272
327
|
function branchPath(step: AppiumRecordedStep, branch: BranchName) {
|
|
273
|
-
const
|
|
274
|
-
|
|
328
|
+
const layout = conditionLayout(step);
|
|
329
|
+
const placement = placeConditionLayout(layout, flowContentWidth.value, flowAxis(step));
|
|
330
|
+
const targetAxis = branch === 'yes' ? layout.yesAxis : layout.noAxis;
|
|
331
|
+
return `M${placement.startAxis} 0 C${placement.startAxis} 16 ${targetAxis} 16 ${targetAxis} 31 L${targetAxis} 48`;
|
|
275
332
|
}
|
|
276
333
|
|
|
277
334
|
function mergeTargetAxis(step: AppiumRecordedStep) {
|
|
278
335
|
const axes = [
|
|
279
|
-
branchConnectionTargetId(step, 'yes') ?
|
|
280
|
-
branchConnectionTargetId(step, 'no') ?
|
|
336
|
+
branchConnectionTargetId(step, 'yes') ? branchAxis(step, 'yes') : 0,
|
|
337
|
+
branchConnectionTargetId(step, 'no') ? branchAxis(step, 'no') : 0,
|
|
281
338
|
].filter(Boolean);
|
|
282
339
|
return axes.reduce((total, axis) => total + axis, 0) / axes.length;
|
|
283
340
|
}
|
|
284
341
|
|
|
285
342
|
function mergePath(step: AppiumRecordedStep, branch: BranchName) {
|
|
286
|
-
const sourceAxis = branch
|
|
343
|
+
const sourceAxis = branchAxis(step, branch);
|
|
287
344
|
const targetAxis = mergeTargetAxis(step);
|
|
288
345
|
return `M${sourceAxis} 0 C${sourceAxis} 20 ${targetAxis} 20 ${targetAxis} 44`;
|
|
289
346
|
}
|
|
@@ -300,15 +357,90 @@ function toggleExpanded(index: number) {
|
|
|
300
357
|
expandedStepIndex.value = expandedStepIndex.value === index ? null : index;
|
|
301
358
|
}
|
|
302
359
|
|
|
360
|
+
function isDescendantStep(descendantIndex: number, ancestorIndex: number) {
|
|
361
|
+
const ancestorId = props.steps[ancestorIndex]?.id;
|
|
362
|
+
let parentId = props.steps[descendantIndex]?.flow?.parentConditionId;
|
|
363
|
+
while (ancestorId && parentId) {
|
|
364
|
+
if (parentId === ancestorId) return true;
|
|
365
|
+
parentId = props.steps.find((item) => item.id === parentId)?.flow?.parentConditionId;
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function isCopySelected(index: number) {
|
|
371
|
+
return selectedCopyIndexes.value.includes(index)
|
|
372
|
+
|| selectedCopyIndexes.value.some((selectedIndex) => isDescendantStep(index, selectedIndex));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function toggleCopySelection(index: number) {
|
|
376
|
+
const step = props.steps[index];
|
|
377
|
+
if (!step) return;
|
|
378
|
+
if (step.type === 'launchApp') {
|
|
379
|
+
ElMessage.warning('启动 APP 节点不能复制');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const current = selectedCopyIndexes.value;
|
|
383
|
+
if (current.includes(index)) {
|
|
384
|
+
selectedCopyIndexes.value = current.filter((item) => item !== index);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (current.some((item) => isDescendantStep(index, item))) {
|
|
388
|
+
ElMessage.info('该子节点已随判断节点选中');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const effectiveCurrent = current.filter((item) => !isDescendantStep(item, index));
|
|
392
|
+
const candidate = [...effectiveCurrent, index].sort((left, right) => left - right);
|
|
393
|
+
try {
|
|
394
|
+
createFlowClipboard(props.steps, candidate);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
ElMessage.warning(error instanceof Error ? error.message : '当前节点不能一起复制');
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
selectedCopyIndexes.value = candidate;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function startCopyMode() {
|
|
403
|
+
copyMode.value = true;
|
|
404
|
+
selectedCopyIndexes.value = [];
|
|
405
|
+
expandedStepIndex.value = null;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function cancelCopyMode() {
|
|
409
|
+
copyMode.value = false;
|
|
410
|
+
selectedCopyIndexes.value = [];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function copySelectedNodes() {
|
|
414
|
+
if (!selectedCopyIndexes.value.length) return;
|
|
415
|
+
emit('copy', selectedCopyIndexes.value);
|
|
416
|
+
cancelCopyMode();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function copySingleNode(index: number) {
|
|
420
|
+
emit('copy', [index]);
|
|
421
|
+
}
|
|
422
|
+
|
|
303
423
|
function insertAction(index: number, command: string | number | object) {
|
|
424
|
+
if (command === PASTE_COMMAND) {
|
|
425
|
+
emit('paste', index);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
304
428
|
emit('insertAction', index, command as InsertAction);
|
|
305
429
|
}
|
|
306
430
|
|
|
307
431
|
function insertStartAction(command: string | number | object) {
|
|
432
|
+
if (command === PASTE_COMMAND) {
|
|
433
|
+
emit('paste', -1);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
308
436
|
emit('insertAction', -1, command as InsertAction);
|
|
309
437
|
}
|
|
310
438
|
|
|
311
439
|
function insertBranchAction(index: number, branch: BranchName, command: string | number | object) {
|
|
440
|
+
if (command === PASTE_COMMAND) {
|
|
441
|
+
emit('paste', index, branch);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
312
444
|
emit('insertBranchAction', index, branch, command as InsertAction);
|
|
313
445
|
}
|
|
314
446
|
|
|
@@ -374,6 +506,10 @@ function handleFlowWheel(event: WheelEvent, viewport: FlowViewport) {
|
|
|
374
506
|
|
|
375
507
|
function handleNodeClick(index: number) {
|
|
376
508
|
if (flowDragMoved.value) return;
|
|
509
|
+
if (copyMode.value) {
|
|
510
|
+
toggleCopySelection(index);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
377
513
|
toggleExpanded(index);
|
|
378
514
|
}
|
|
379
515
|
|
|
@@ -421,6 +557,28 @@ watch(() => props.steps.length, () => {
|
|
|
421
557
|
<template>
|
|
422
558
|
<div class="appium-recorded-steps-panel">
|
|
423
559
|
<div class="appium-flow-toolbar">
|
|
560
|
+
<template v-if="copyMode">
|
|
561
|
+
<span class="appium-flow-toolbar__status">已选择 {{ selectedCopyIndexes.length }} 个节点</span>
|
|
562
|
+
<el-button
|
|
563
|
+
size="small"
|
|
564
|
+
type="primary"
|
|
565
|
+
:disabled="!selectedCopyIndexes.length"
|
|
566
|
+
:icon="CopyDocument"
|
|
567
|
+
@click="copySelectedNodes"
|
|
568
|
+
>
|
|
569
|
+
复制选中
|
|
570
|
+
</el-button>
|
|
571
|
+
<el-button size="small" @click="cancelCopyMode">取消</el-button>
|
|
572
|
+
</template>
|
|
573
|
+
<el-button
|
|
574
|
+
v-else
|
|
575
|
+
size="small"
|
|
576
|
+
:icon="CopyDocument"
|
|
577
|
+
:disabled="!steps.length"
|
|
578
|
+
@click="startCopyMode"
|
|
579
|
+
>
|
|
580
|
+
批量复制
|
|
581
|
+
</el-button>
|
|
424
582
|
<el-button
|
|
425
583
|
size="small"
|
|
426
584
|
:icon="FullScreen"
|
|
@@ -444,6 +602,8 @@ watch(() => props.steps.length, () => {
|
|
|
444
602
|
class="appium-flow-content"
|
|
445
603
|
:style="{
|
|
446
604
|
transform: `translate(${flowPan.x}px, ${flowPan.y}px) scale(${flowScale})`,
|
|
605
|
+
width: `${flowContentWidth}px`,
|
|
606
|
+
minWidth: `${flowContentWidth}px`,
|
|
447
607
|
'--appium-flow-line-width': '3px',
|
|
448
608
|
}"
|
|
449
609
|
>
|
|
@@ -475,6 +635,13 @@ watch(() => props.steps.length, () => {
|
|
|
475
635
|
</button>
|
|
476
636
|
<template #dropdown>
|
|
477
637
|
<el-dropdown-menu>
|
|
638
|
+
<el-dropdown-item
|
|
639
|
+
v-if="clipboardCount"
|
|
640
|
+
:command="PASTE_COMMAND"
|
|
641
|
+
divided
|
|
642
|
+
>
|
|
643
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
644
|
+
</el-dropdown-item>
|
|
478
645
|
<template v-for="group in startActionGroups" :key="group.title">
|
|
479
646
|
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
480
647
|
<el-dropdown-item
|
|
@@ -510,7 +677,11 @@ watch(() => props.steps.length, () => {
|
|
|
510
677
|
class="appium-flow-node"
|
|
511
678
|
:class="[
|
|
512
679
|
`appium-flow-node--${defaultKind(step)}`,
|
|
513
|
-
{
|
|
680
|
+
{
|
|
681
|
+
'appium-flow-node--expanded': expandedStepIndex === index,
|
|
682
|
+
'appium-flow-node--selected': isCopySelected(index),
|
|
683
|
+
'appium-flow-node--copying': copyMode,
|
|
684
|
+
},
|
|
514
685
|
]"
|
|
515
686
|
>
|
|
516
687
|
<button
|
|
@@ -519,7 +690,6 @@ watch(() => props.steps.length, () => {
|
|
|
519
690
|
:aria-expanded="expandedStepIndex === index"
|
|
520
691
|
@click.stop="handleNodeClick(index)"
|
|
521
692
|
>
|
|
522
|
-
<span class="appium-flow-node__index">{{ index + 1 }}</span>
|
|
523
693
|
<span class="appium-flow-node__body">
|
|
524
694
|
<span class="appium-flow-node__title" :title="step.label">{{ step.label }}</span>
|
|
525
695
|
<span class="appium-flow-node__meta" :title="stepMeta(step)">
|
|
@@ -531,6 +701,14 @@ watch(() => props.steps.length, () => {
|
|
|
531
701
|
</span>
|
|
532
702
|
</button>
|
|
533
703
|
<span class="appium-flow-node__actions" @click.stop>
|
|
704
|
+
<el-button
|
|
705
|
+
v-if="!copyMode && step.type !== 'launchApp'"
|
|
706
|
+
text
|
|
707
|
+
size="small"
|
|
708
|
+
:icon="CopyDocument"
|
|
709
|
+
title="复制节点"
|
|
710
|
+
@click="copySingleNode(index)"
|
|
711
|
+
/>
|
|
534
712
|
<el-button
|
|
535
713
|
v-if="step.type === 'launchApp'"
|
|
536
714
|
text
|
|
@@ -561,7 +739,11 @@ watch(() => props.steps.length, () => {
|
|
|
561
739
|
</span>
|
|
562
740
|
</div>
|
|
563
741
|
|
|
564
|
-
<div
|
|
742
|
+
<div
|
|
743
|
+
v-if="defaultKind(step) === 'condition'"
|
|
744
|
+
class="appium-flow-branches"
|
|
745
|
+
:style="branchLayoutStyle(step)"
|
|
746
|
+
>
|
|
565
747
|
<svg class="appium-flow-branch-lines" viewBox="0 0 100 48" preserveAspectRatio="none" aria-hidden="true">
|
|
566
748
|
<path :d="branchPath(step, 'yes')" />
|
|
567
749
|
<path :d="branchPath(step, 'no')" />
|
|
@@ -589,7 +771,11 @@ watch(() => props.steps.length, () => {
|
|
|
589
771
|
class="appium-flow-node appium-flow-branch-step"
|
|
590
772
|
:class="[
|
|
591
773
|
`appium-flow-node--${defaultKind(item.step)}`,
|
|
592
|
-
{
|
|
774
|
+
{
|
|
775
|
+
'appium-flow-node--expanded': expandedStepIndex === item.index,
|
|
776
|
+
'appium-flow-node--selected': isCopySelected(item.index),
|
|
777
|
+
'appium-flow-node--copying': copyMode,
|
|
778
|
+
},
|
|
593
779
|
]"
|
|
594
780
|
>
|
|
595
781
|
<button
|
|
@@ -598,7 +784,6 @@ watch(() => props.steps.length, () => {
|
|
|
598
784
|
:aria-expanded="expandedStepIndex === item.index"
|
|
599
785
|
@click.stop="handleNodeClick(item.index)"
|
|
600
786
|
>
|
|
601
|
-
<span class="appium-flow-node__index">{{ item.index + 1 }}</span>
|
|
602
787
|
<span class="appium-flow-node__body">
|
|
603
788
|
<span class="appium-flow-node__title">{{ item.step.label }}</span>
|
|
604
789
|
<span class="appium-flow-node__meta">
|
|
@@ -609,6 +794,14 @@ watch(() => props.steps.length, () => {
|
|
|
609
794
|
</span>
|
|
610
795
|
</button>
|
|
611
796
|
<span class="appium-flow-node__actions" @click.stop>
|
|
797
|
+
<el-button
|
|
798
|
+
v-if="!copyMode"
|
|
799
|
+
text
|
|
800
|
+
size="small"
|
|
801
|
+
:icon="CopyDocument"
|
|
802
|
+
title="复制节点"
|
|
803
|
+
@click="copySingleNode(item.index)"
|
|
804
|
+
/>
|
|
612
805
|
<el-button
|
|
613
806
|
text
|
|
614
807
|
size="small"
|
|
@@ -626,6 +819,27 @@ watch(() => props.steps.length, () => {
|
|
|
626
819
|
:disabled="disabled"
|
|
627
820
|
@update="updateStep"
|
|
628
821
|
/>
|
|
822
|
+
<NestedConditionBranches
|
|
823
|
+
v-if="defaultKind(item.step) === 'condition'"
|
|
824
|
+
:steps="steps"
|
|
825
|
+
:condition="item.step"
|
|
826
|
+
:condition-index="item.index"
|
|
827
|
+
:expanded-step-index="expandedStepIndex"
|
|
828
|
+
:copy-mode="copyMode"
|
|
829
|
+
:selected-copy-indexes="selectedCopyIndexes"
|
|
830
|
+
:disabled="disabled"
|
|
831
|
+
:allowed-locked-actions="allowedLockedActions"
|
|
832
|
+
:clipboard-count="clipboardCount"
|
|
833
|
+
@node-click="handleNodeClick"
|
|
834
|
+
@copy="$emit('copy', $event)"
|
|
835
|
+
@remove="$emit('remove', $event)"
|
|
836
|
+
@paste="(childIndex, childBranch) => $emit('paste', childIndex, childBranch)"
|
|
837
|
+
@insert-branch-action="(childIndex, childBranch, action) => $emit('insertBranchAction', childIndex, childBranch, action)"
|
|
838
|
+
@connect-next="(childIndex, childBranch) => $emit('connectNext', childIndex, childBranch)"
|
|
839
|
+
@disconnect-next="(childIndex, childBranch) => $emit('disconnectNext', childIndex, childBranch)"
|
|
840
|
+
@edit-input="$emit('editInput', $event)"
|
|
841
|
+
@update-step="(childIndex, childStep) => $emit('updateStep', childIndex, childStep)"
|
|
842
|
+
/>
|
|
629
843
|
</div>
|
|
630
844
|
</div>
|
|
631
845
|
<span
|
|
@@ -653,6 +867,13 @@ watch(() => props.steps.length, () => {
|
|
|
653
867
|
</el-button>
|
|
654
868
|
<template #dropdown>
|
|
655
869
|
<el-dropdown-menu>
|
|
870
|
+
<el-dropdown-item
|
|
871
|
+
v-if="clipboardCount"
|
|
872
|
+
:command="PASTE_COMMAND"
|
|
873
|
+
divided
|
|
874
|
+
>
|
|
875
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
876
|
+
</el-dropdown-item>
|
|
656
877
|
<template v-for="group in mainActionGroups" :key="group.title">
|
|
657
878
|
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
658
879
|
<el-dropdown-item
|
|
@@ -703,7 +924,11 @@ watch(() => props.steps.length, () => {
|
|
|
703
924
|
class="appium-flow-node appium-flow-branch-step"
|
|
704
925
|
:class="[
|
|
705
926
|
`appium-flow-node--${defaultKind(item.step)}`,
|
|
706
|
-
{
|
|
927
|
+
{
|
|
928
|
+
'appium-flow-node--expanded': expandedStepIndex === item.index,
|
|
929
|
+
'appium-flow-node--selected': isCopySelected(item.index),
|
|
930
|
+
'appium-flow-node--copying': copyMode,
|
|
931
|
+
},
|
|
707
932
|
]"
|
|
708
933
|
>
|
|
709
934
|
<button
|
|
@@ -712,7 +937,6 @@ watch(() => props.steps.length, () => {
|
|
|
712
937
|
:aria-expanded="expandedStepIndex === item.index"
|
|
713
938
|
@click.stop="handleNodeClick(item.index)"
|
|
714
939
|
>
|
|
715
|
-
<span class="appium-flow-node__index">{{ item.index + 1 }}</span>
|
|
716
940
|
<span class="appium-flow-node__body">
|
|
717
941
|
<span class="appium-flow-node__title">{{ item.step.label }}</span>
|
|
718
942
|
<span class="appium-flow-node__meta">
|
|
@@ -723,6 +947,14 @@ watch(() => props.steps.length, () => {
|
|
|
723
947
|
</span>
|
|
724
948
|
</button>
|
|
725
949
|
<span class="appium-flow-node__actions" @click.stop>
|
|
950
|
+
<el-button
|
|
951
|
+
v-if="!copyMode"
|
|
952
|
+
text
|
|
953
|
+
size="small"
|
|
954
|
+
:icon="CopyDocument"
|
|
955
|
+
title="复制节点"
|
|
956
|
+
@click="copySingleNode(item.index)"
|
|
957
|
+
/>
|
|
726
958
|
<el-button
|
|
727
959
|
text
|
|
728
960
|
size="small"
|
|
@@ -740,6 +972,27 @@ watch(() => props.steps.length, () => {
|
|
|
740
972
|
:disabled="disabled"
|
|
741
973
|
@update="updateStep"
|
|
742
974
|
/>
|
|
975
|
+
<NestedConditionBranches
|
|
976
|
+
v-if="defaultKind(item.step) === 'condition'"
|
|
977
|
+
:steps="steps"
|
|
978
|
+
:condition="item.step"
|
|
979
|
+
:condition-index="item.index"
|
|
980
|
+
:expanded-step-index="expandedStepIndex"
|
|
981
|
+
:copy-mode="copyMode"
|
|
982
|
+
:selected-copy-indexes="selectedCopyIndexes"
|
|
983
|
+
:disabled="disabled"
|
|
984
|
+
:allowed-locked-actions="allowedLockedActions"
|
|
985
|
+
:clipboard-count="clipboardCount"
|
|
986
|
+
@node-click="handleNodeClick"
|
|
987
|
+
@copy="$emit('copy', $event)"
|
|
988
|
+
@remove="$emit('remove', $event)"
|
|
989
|
+
@paste="(childIndex, childBranch) => $emit('paste', childIndex, childBranch)"
|
|
990
|
+
@insert-branch-action="(childIndex, childBranch, action) => $emit('insertBranchAction', childIndex, childBranch, action)"
|
|
991
|
+
@connect-next="(childIndex, childBranch) => $emit('connectNext', childIndex, childBranch)"
|
|
992
|
+
@disconnect-next="(childIndex, childBranch) => $emit('disconnectNext', childIndex, childBranch)"
|
|
993
|
+
@edit-input="$emit('editInput', $event)"
|
|
994
|
+
@update-step="(childIndex, childStep) => $emit('updateStep', childIndex, childStep)"
|
|
995
|
+
/>
|
|
743
996
|
</div>
|
|
744
997
|
</div>
|
|
745
998
|
<span
|
|
@@ -767,6 +1020,13 @@ watch(() => props.steps.length, () => {
|
|
|
767
1020
|
</el-button>
|
|
768
1021
|
<template #dropdown>
|
|
769
1022
|
<el-dropdown-menu>
|
|
1023
|
+
<el-dropdown-item
|
|
1024
|
+
v-if="clipboardCount"
|
|
1025
|
+
:command="PASTE_COMMAND"
|
|
1026
|
+
divided
|
|
1027
|
+
>
|
|
1028
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
1029
|
+
</el-dropdown-item>
|
|
770
1030
|
<template v-for="group in mainActionGroups" :key="group.title">
|
|
771
1031
|
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
772
1032
|
<el-dropdown-item
|
|
@@ -837,6 +1097,13 @@ watch(() => props.steps.length, () => {
|
|
|
837
1097
|
</button>
|
|
838
1098
|
<template #dropdown>
|
|
839
1099
|
<el-dropdown-menu>
|
|
1100
|
+
<el-dropdown-item
|
|
1101
|
+
v-if="clipboardCount"
|
|
1102
|
+
:command="PASTE_COMMAND"
|
|
1103
|
+
divided
|
|
1104
|
+
>
|
|
1105
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
1106
|
+
</el-dropdown-item>
|
|
840
1107
|
<template v-for="group in mainActionGroups" :key="group.title">
|
|
841
1108
|
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
842
1109
|
<el-dropdown-item
|
|
@@ -861,6 +1128,30 @@ watch(() => props.steps.length, () => {
|
|
|
861
1128
|
class="appium-flow-dialog"
|
|
862
1129
|
append-to-body
|
|
863
1130
|
>
|
|
1131
|
+
<div class="appium-flow-toolbar appium-flow-dialog__toolbar">
|
|
1132
|
+
<template v-if="copyMode">
|
|
1133
|
+
<span class="appium-flow-toolbar__status">已选择 {{ selectedCopyIndexes.length }} 个节点</span>
|
|
1134
|
+
<el-button
|
|
1135
|
+
size="small"
|
|
1136
|
+
type="primary"
|
|
1137
|
+
:disabled="!selectedCopyIndexes.length"
|
|
1138
|
+
:icon="CopyDocument"
|
|
1139
|
+
@click="copySelectedNodes"
|
|
1140
|
+
>
|
|
1141
|
+
复制选中
|
|
1142
|
+
</el-button>
|
|
1143
|
+
<el-button size="small" @click="cancelCopyMode">取消</el-button>
|
|
1144
|
+
</template>
|
|
1145
|
+
<el-button
|
|
1146
|
+
v-else
|
|
1147
|
+
size="small"
|
|
1148
|
+
:icon="CopyDocument"
|
|
1149
|
+
:disabled="!steps.length"
|
|
1150
|
+
@click="startCopyMode"
|
|
1151
|
+
>
|
|
1152
|
+
批量复制
|
|
1153
|
+
</el-button>
|
|
1154
|
+
</div>
|
|
864
1155
|
<div
|
|
865
1156
|
class="appium-flow-dialog-canvas"
|
|
866
1157
|
:class="{ 'appium-flow-dialog-canvas--dragging': draggingViewport === 'overview' }"
|
|
@@ -875,6 +1166,8 @@ watch(() => props.steps.length, () => {
|
|
|
875
1166
|
class="appium-flow-content appium-flow-content--overview"
|
|
876
1167
|
:style="{
|
|
877
1168
|
transform: `translate(${overviewPan.x}px, ${overviewPan.y}px) scale(${overviewScale})`,
|
|
1169
|
+
width: `${flowContentWidth}px`,
|
|
1170
|
+
minWidth: `${flowContentWidth}px`,
|
|
878
1171
|
'--appium-flow-line-width': '3px',
|
|
879
1172
|
}"
|
|
880
1173
|
>
|
|
@@ -889,6 +1182,40 @@ watch(() => props.steps.length, () => {
|
|
|
889
1182
|
<path d="M5 0 V100" />
|
|
890
1183
|
</svg>
|
|
891
1184
|
<div class="appium-flow-start__node">开始</div>
|
|
1185
|
+
<el-dropdown
|
|
1186
|
+
trigger="click"
|
|
1187
|
+
:disabled="!canOpenInsertMenu()"
|
|
1188
|
+
max-height="320px"
|
|
1189
|
+
popper-class="appium-action-dropdown"
|
|
1190
|
+
@command="insertStartAction($event)"
|
|
1191
|
+
>
|
|
1192
|
+
<button
|
|
1193
|
+
type="button"
|
|
1194
|
+
class="appium-flow-insert"
|
|
1195
|
+
:disabled="!canOpenInsertMenu()"
|
|
1196
|
+
>
|
|
1197
|
+
<el-icon><Timer /></el-icon>
|
|
1198
|
+
<span>插入操作</span>
|
|
1199
|
+
</button>
|
|
1200
|
+
<template #dropdown>
|
|
1201
|
+
<el-dropdown-menu>
|
|
1202
|
+
<el-dropdown-item v-if="clipboardCount" :command="PASTE_COMMAND" divided>
|
|
1203
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
1204
|
+
</el-dropdown-item>
|
|
1205
|
+
<template v-for="group in startActionGroups" :key="group.title">
|
|
1206
|
+
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
1207
|
+
<el-dropdown-item
|
|
1208
|
+
v-for="action in group.actions"
|
|
1209
|
+
:key="action.type"
|
|
1210
|
+
:command="action.type"
|
|
1211
|
+
:disabled="isStartActionDisabled(action.type)"
|
|
1212
|
+
>
|
|
1213
|
+
{{ action.label }}
|
|
1214
|
+
</el-dropdown-item>
|
|
1215
|
+
</template>
|
|
1216
|
+
</el-dropdown-menu>
|
|
1217
|
+
</template>
|
|
1218
|
+
</el-dropdown>
|
|
892
1219
|
</div>
|
|
893
1220
|
<div
|
|
894
1221
|
v-for="{ step, index } in visibleStepItems"
|
|
@@ -910,7 +1237,11 @@ watch(() => props.steps.length, () => {
|
|
|
910
1237
|
class="appium-flow-node"
|
|
911
1238
|
:class="[
|
|
912
1239
|
`appium-flow-node--${defaultKind(step)}`,
|
|
913
|
-
{
|
|
1240
|
+
{
|
|
1241
|
+
'appium-flow-node--expanded': expandedStepIndex === index,
|
|
1242
|
+
'appium-flow-node--selected': isCopySelected(index),
|
|
1243
|
+
'appium-flow-node--copying': copyMode,
|
|
1244
|
+
},
|
|
914
1245
|
]"
|
|
915
1246
|
>
|
|
916
1247
|
<button
|
|
@@ -919,7 +1250,6 @@ watch(() => props.steps.length, () => {
|
|
|
919
1250
|
:aria-expanded="expandedStepIndex === index"
|
|
920
1251
|
@click="handleNodeClick(index)"
|
|
921
1252
|
>
|
|
922
|
-
<span class="appium-flow-node__index">{{ index + 1 }}</span>
|
|
923
1253
|
<span class="appium-flow-node__body">
|
|
924
1254
|
<span class="appium-flow-node__title" :title="step.label">{{ step.label }}</span>
|
|
925
1255
|
<span class="appium-flow-node__meta" :title="stepMeta(step)">
|
|
@@ -930,6 +1260,43 @@ watch(() => props.steps.length, () => {
|
|
|
930
1260
|
<span v-if="step.note" class="appium-flow-node__note" :title="step.note">{{ step.note }}</span>
|
|
931
1261
|
</span>
|
|
932
1262
|
</button>
|
|
1263
|
+
<span class="appium-flow-node__actions" @click.stop>
|
|
1264
|
+
<el-button
|
|
1265
|
+
v-if="!copyMode && step.type !== 'launchApp'"
|
|
1266
|
+
text
|
|
1267
|
+
size="small"
|
|
1268
|
+
:icon="CopyDocument"
|
|
1269
|
+
title="复制节点"
|
|
1270
|
+
@click="copySingleNode(index)"
|
|
1271
|
+
/>
|
|
1272
|
+
<el-button
|
|
1273
|
+
v-if="step.type === 'launchApp'"
|
|
1274
|
+
text
|
|
1275
|
+
size="small"
|
|
1276
|
+
:icon="VideoPlay"
|
|
1277
|
+
:loading="launchingStepId === step.id"
|
|
1278
|
+
:disabled="isLaunchExecutionDisabled()"
|
|
1279
|
+
title="立即执行启动 App"
|
|
1280
|
+
@click="$emit('executeStep', index)"
|
|
1281
|
+
/>
|
|
1282
|
+
<el-button
|
|
1283
|
+
v-if="step.type === 'input' || step.type === 'inputIfExists'"
|
|
1284
|
+
text
|
|
1285
|
+
size="small"
|
|
1286
|
+
:icon="Edit"
|
|
1287
|
+
:disabled="disabled"
|
|
1288
|
+
title="修改输入内容"
|
|
1289
|
+
@click="$emit('editInput', index)"
|
|
1290
|
+
/>
|
|
1291
|
+
<el-button
|
|
1292
|
+
text
|
|
1293
|
+
size="small"
|
|
1294
|
+
:icon="Delete"
|
|
1295
|
+
:disabled="disabled"
|
|
1296
|
+
title="删除节点"
|
|
1297
|
+
@click="$emit('remove', index)"
|
|
1298
|
+
/>
|
|
1299
|
+
</span>
|
|
933
1300
|
</div>
|
|
934
1301
|
|
|
935
1302
|
<FlowStepEditor
|
|
@@ -940,7 +1307,11 @@ watch(() => props.steps.length, () => {
|
|
|
940
1307
|
@update="updateStep"
|
|
941
1308
|
/>
|
|
942
1309
|
|
|
943
|
-
<div
|
|
1310
|
+
<div
|
|
1311
|
+
v-if="defaultKind(step) === 'condition'"
|
|
1312
|
+
class="appium-flow-branches"
|
|
1313
|
+
:style="branchLayoutStyle(step)"
|
|
1314
|
+
>
|
|
944
1315
|
<svg class="appium-flow-branch-lines" viewBox="0 0 100 48" preserveAspectRatio="none" aria-hidden="true">
|
|
945
1316
|
<path :d="branchPath(step, 'yes')" />
|
|
946
1317
|
<path :d="branchPath(step, 'no')" />
|
|
@@ -968,7 +1339,11 @@ watch(() => props.steps.length, () => {
|
|
|
968
1339
|
class="appium-flow-node appium-flow-branch-step"
|
|
969
1340
|
:class="[
|
|
970
1341
|
`appium-flow-node--${defaultKind(item.step)}`,
|
|
971
|
-
{
|
|
1342
|
+
{
|
|
1343
|
+
'appium-flow-node--expanded': expandedStepIndex === item.index,
|
|
1344
|
+
'appium-flow-node--selected': isCopySelected(item.index),
|
|
1345
|
+
'appium-flow-node--copying': copyMode,
|
|
1346
|
+
},
|
|
972
1347
|
]"
|
|
973
1348
|
>
|
|
974
1349
|
<button
|
|
@@ -977,7 +1352,6 @@ watch(() => props.steps.length, () => {
|
|
|
977
1352
|
:aria-expanded="expandedStepIndex === item.index"
|
|
978
1353
|
@click="handleNodeClick(item.index)"
|
|
979
1354
|
>
|
|
980
|
-
<span class="appium-flow-node__index">{{ item.index + 1 }}</span>
|
|
981
1355
|
<span class="appium-flow-node__body">
|
|
982
1356
|
<span class="appium-flow-node__title">{{ item.step.label }}</span>
|
|
983
1357
|
<span class="appium-flow-node__meta">
|
|
@@ -987,6 +1361,24 @@ watch(() => props.steps.length, () => {
|
|
|
987
1361
|
<span v-if="item.step.note" class="appium-flow-node__note" :title="item.step.note">{{ item.step.note }}</span>
|
|
988
1362
|
</span>
|
|
989
1363
|
</button>
|
|
1364
|
+
<span class="appium-flow-node__actions" @click.stop>
|
|
1365
|
+
<el-button
|
|
1366
|
+
v-if="!copyMode"
|
|
1367
|
+
text
|
|
1368
|
+
size="small"
|
|
1369
|
+
:icon="CopyDocument"
|
|
1370
|
+
title="复制节点"
|
|
1371
|
+
@click="copySingleNode(item.index)"
|
|
1372
|
+
/>
|
|
1373
|
+
<el-button
|
|
1374
|
+
text
|
|
1375
|
+
size="small"
|
|
1376
|
+
:icon="Delete"
|
|
1377
|
+
:disabled="disabled"
|
|
1378
|
+
title="删除节点"
|
|
1379
|
+
@click="$emit('remove', item.index)"
|
|
1380
|
+
/>
|
|
1381
|
+
</span>
|
|
990
1382
|
</div>
|
|
991
1383
|
<FlowStepEditor
|
|
992
1384
|
v-if="expandedStepIndex === item.index"
|
|
@@ -995,6 +1387,27 @@ watch(() => props.steps.length, () => {
|
|
|
995
1387
|
:disabled="disabled"
|
|
996
1388
|
@update="updateStep"
|
|
997
1389
|
/>
|
|
1390
|
+
<NestedConditionBranches
|
|
1391
|
+
v-if="defaultKind(item.step) === 'condition'"
|
|
1392
|
+
:steps="steps"
|
|
1393
|
+
:condition="item.step"
|
|
1394
|
+
:condition-index="item.index"
|
|
1395
|
+
:expanded-step-index="expandedStepIndex"
|
|
1396
|
+
:copy-mode="copyMode"
|
|
1397
|
+
:selected-copy-indexes="selectedCopyIndexes"
|
|
1398
|
+
:disabled="disabled"
|
|
1399
|
+
:allowed-locked-actions="allowedLockedActions"
|
|
1400
|
+
:clipboard-count="clipboardCount"
|
|
1401
|
+
@node-click="handleNodeClick"
|
|
1402
|
+
@copy="$emit('copy', $event)"
|
|
1403
|
+
@remove="$emit('remove', $event)"
|
|
1404
|
+
@paste="(childIndex, childBranch) => $emit('paste', childIndex, childBranch)"
|
|
1405
|
+
@insert-branch-action="(childIndex, childBranch, action) => $emit('insertBranchAction', childIndex, childBranch, action)"
|
|
1406
|
+
@connect-next="(childIndex, childBranch) => $emit('connectNext', childIndex, childBranch)"
|
|
1407
|
+
@disconnect-next="(childIndex, childBranch) => $emit('disconnectNext', childIndex, childBranch)"
|
|
1408
|
+
@edit-input="$emit('editInput', $event)"
|
|
1409
|
+
@update-step="(childIndex, childStep) => $emit('updateStep', childIndex, childStep)"
|
|
1410
|
+
/>
|
|
998
1411
|
</div>
|
|
999
1412
|
</div>
|
|
1000
1413
|
<span
|
|
@@ -1003,6 +1416,53 @@ watch(() => props.steps.length, () => {
|
|
|
1003
1416
|
>
|
|
1004
1417
|
{{ targetLabel(step.flow?.yesTargetId) }}
|
|
1005
1418
|
</span>
|
|
1419
|
+
<div class="appium-flow-branch__actions">
|
|
1420
|
+
<el-dropdown
|
|
1421
|
+
trigger="click"
|
|
1422
|
+
:disabled="!canOpenInsertMenu()"
|
|
1423
|
+
max-height="320px"
|
|
1424
|
+
popper-class="appium-action-dropdown"
|
|
1425
|
+
@command="insertBranchAction(index, 'yes', $event)"
|
|
1426
|
+
>
|
|
1427
|
+
<el-button
|
|
1428
|
+
size="small"
|
|
1429
|
+
class="appium-flow-branch__insert"
|
|
1430
|
+
:disabled="!canOpenInsertMenu()"
|
|
1431
|
+
:icon="Timer"
|
|
1432
|
+
@click.stop
|
|
1433
|
+
>
|
|
1434
|
+
插入操作
|
|
1435
|
+
</el-button>
|
|
1436
|
+
<template #dropdown>
|
|
1437
|
+
<el-dropdown-menu>
|
|
1438
|
+
<el-dropdown-item v-if="clipboardCount" :command="PASTE_COMMAND" divided>
|
|
1439
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
1440
|
+
</el-dropdown-item>
|
|
1441
|
+
<template v-for="group in mainActionGroups" :key="group.title">
|
|
1442
|
+
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
1443
|
+
<el-dropdown-item
|
|
1444
|
+
v-for="action in group.actions"
|
|
1445
|
+
:key="action.type"
|
|
1446
|
+
:command="action.type"
|
|
1447
|
+
:disabled="isInsertActionDisabled(action.type)"
|
|
1448
|
+
>
|
|
1449
|
+
{{ action.label }}
|
|
1450
|
+
</el-dropdown-item>
|
|
1451
|
+
</template>
|
|
1452
|
+
</el-dropdown-menu>
|
|
1453
|
+
</template>
|
|
1454
|
+
</el-dropdown>
|
|
1455
|
+
<el-button
|
|
1456
|
+
v-if="shouldShowBranchConnect(index, step, 'yes')"
|
|
1457
|
+
size="small"
|
|
1458
|
+
class="appium-flow-branch__connect"
|
|
1459
|
+
:icon="Link"
|
|
1460
|
+
:disabled="disabled"
|
|
1461
|
+
@click.stop="toggleBranchConnection(index, step, 'yes')"
|
|
1462
|
+
>
|
|
1463
|
+
{{ branchConnectionTargetId(step, 'yes') ? '取消连接' : '连接到下一节点' }}
|
|
1464
|
+
</el-button>
|
|
1465
|
+
</div>
|
|
1006
1466
|
</div>
|
|
1007
1467
|
<div
|
|
1008
1468
|
class="appium-flow-branch appium-flow-branch--no"
|
|
@@ -1027,7 +1487,11 @@ watch(() => props.steps.length, () => {
|
|
|
1027
1487
|
class="appium-flow-node appium-flow-branch-step"
|
|
1028
1488
|
:class="[
|
|
1029
1489
|
`appium-flow-node--${defaultKind(item.step)}`,
|
|
1030
|
-
{
|
|
1490
|
+
{
|
|
1491
|
+
'appium-flow-node--expanded': expandedStepIndex === item.index,
|
|
1492
|
+
'appium-flow-node--selected': isCopySelected(item.index),
|
|
1493
|
+
'appium-flow-node--copying': copyMode,
|
|
1494
|
+
},
|
|
1031
1495
|
]"
|
|
1032
1496
|
>
|
|
1033
1497
|
<button
|
|
@@ -1036,7 +1500,6 @@ watch(() => props.steps.length, () => {
|
|
|
1036
1500
|
:aria-expanded="expandedStepIndex === item.index"
|
|
1037
1501
|
@click="handleNodeClick(item.index)"
|
|
1038
1502
|
>
|
|
1039
|
-
<span class="appium-flow-node__index">{{ item.index + 1 }}</span>
|
|
1040
1503
|
<span class="appium-flow-node__body">
|
|
1041
1504
|
<span class="appium-flow-node__title">{{ item.step.label }}</span>
|
|
1042
1505
|
<span class="appium-flow-node__meta">
|
|
@@ -1046,6 +1509,24 @@ watch(() => props.steps.length, () => {
|
|
|
1046
1509
|
<span v-if="item.step.note" class="appium-flow-node__note" :title="item.step.note">{{ item.step.note }}</span>
|
|
1047
1510
|
</span>
|
|
1048
1511
|
</button>
|
|
1512
|
+
<span class="appium-flow-node__actions" @click.stop>
|
|
1513
|
+
<el-button
|
|
1514
|
+
v-if="!copyMode"
|
|
1515
|
+
text
|
|
1516
|
+
size="small"
|
|
1517
|
+
:icon="CopyDocument"
|
|
1518
|
+
title="复制节点"
|
|
1519
|
+
@click="copySingleNode(item.index)"
|
|
1520
|
+
/>
|
|
1521
|
+
<el-button
|
|
1522
|
+
text
|
|
1523
|
+
size="small"
|
|
1524
|
+
:icon="Delete"
|
|
1525
|
+
:disabled="disabled"
|
|
1526
|
+
title="删除节点"
|
|
1527
|
+
@click="$emit('remove', item.index)"
|
|
1528
|
+
/>
|
|
1529
|
+
</span>
|
|
1049
1530
|
</div>
|
|
1050
1531
|
<FlowStepEditor
|
|
1051
1532
|
v-if="expandedStepIndex === item.index"
|
|
@@ -1054,6 +1535,27 @@ watch(() => props.steps.length, () => {
|
|
|
1054
1535
|
:disabled="disabled"
|
|
1055
1536
|
@update="updateStep"
|
|
1056
1537
|
/>
|
|
1538
|
+
<NestedConditionBranches
|
|
1539
|
+
v-if="defaultKind(item.step) === 'condition'"
|
|
1540
|
+
:steps="steps"
|
|
1541
|
+
:condition="item.step"
|
|
1542
|
+
:condition-index="item.index"
|
|
1543
|
+
:expanded-step-index="expandedStepIndex"
|
|
1544
|
+
:copy-mode="copyMode"
|
|
1545
|
+
:selected-copy-indexes="selectedCopyIndexes"
|
|
1546
|
+
:disabled="disabled"
|
|
1547
|
+
:allowed-locked-actions="allowedLockedActions"
|
|
1548
|
+
:clipboard-count="clipboardCount"
|
|
1549
|
+
@node-click="handleNodeClick"
|
|
1550
|
+
@copy="$emit('copy', $event)"
|
|
1551
|
+
@remove="$emit('remove', $event)"
|
|
1552
|
+
@paste="(childIndex, childBranch) => $emit('paste', childIndex, childBranch)"
|
|
1553
|
+
@insert-branch-action="(childIndex, childBranch, action) => $emit('insertBranchAction', childIndex, childBranch, action)"
|
|
1554
|
+
@connect-next="(childIndex, childBranch) => $emit('connectNext', childIndex, childBranch)"
|
|
1555
|
+
@disconnect-next="(childIndex, childBranch) => $emit('disconnectNext', childIndex, childBranch)"
|
|
1556
|
+
@edit-input="$emit('editInput', $event)"
|
|
1557
|
+
@update-step="(childIndex, childStep) => $emit('updateStep', childIndex, childStep)"
|
|
1558
|
+
/>
|
|
1057
1559
|
</div>
|
|
1058
1560
|
</div>
|
|
1059
1561
|
<span
|
|
@@ -1062,6 +1564,53 @@ watch(() => props.steps.length, () => {
|
|
|
1062
1564
|
>
|
|
1063
1565
|
{{ targetLabel(step.flow?.noTargetId) }}
|
|
1064
1566
|
</span>
|
|
1567
|
+
<div class="appium-flow-branch__actions">
|
|
1568
|
+
<el-dropdown
|
|
1569
|
+
trigger="click"
|
|
1570
|
+
:disabled="!canOpenInsertMenu()"
|
|
1571
|
+
max-height="320px"
|
|
1572
|
+
popper-class="appium-action-dropdown"
|
|
1573
|
+
@command="insertBranchAction(index, 'no', $event)"
|
|
1574
|
+
>
|
|
1575
|
+
<el-button
|
|
1576
|
+
size="small"
|
|
1577
|
+
class="appium-flow-branch__insert"
|
|
1578
|
+
:disabled="!canOpenInsertMenu()"
|
|
1579
|
+
:icon="Timer"
|
|
1580
|
+
@click.stop
|
|
1581
|
+
>
|
|
1582
|
+
插入操作
|
|
1583
|
+
</el-button>
|
|
1584
|
+
<template #dropdown>
|
|
1585
|
+
<el-dropdown-menu>
|
|
1586
|
+
<el-dropdown-item v-if="clipboardCount" :command="PASTE_COMMAND" divided>
|
|
1587
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
1588
|
+
</el-dropdown-item>
|
|
1589
|
+
<template v-for="group in mainActionGroups" :key="group.title">
|
|
1590
|
+
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
1591
|
+
<el-dropdown-item
|
|
1592
|
+
v-for="action in group.actions"
|
|
1593
|
+
:key="action.type"
|
|
1594
|
+
:command="action.type"
|
|
1595
|
+
:disabled="isInsertActionDisabled(action.type)"
|
|
1596
|
+
>
|
|
1597
|
+
{{ action.label }}
|
|
1598
|
+
</el-dropdown-item>
|
|
1599
|
+
</template>
|
|
1600
|
+
</el-dropdown-menu>
|
|
1601
|
+
</template>
|
|
1602
|
+
</el-dropdown>
|
|
1603
|
+
<el-button
|
|
1604
|
+
v-if="shouldShowBranchConnect(index, step, 'no')"
|
|
1605
|
+
size="small"
|
|
1606
|
+
class="appium-flow-branch__connect"
|
|
1607
|
+
:icon="Link"
|
|
1608
|
+
:disabled="disabled"
|
|
1609
|
+
@click.stop="toggleBranchConnection(index, step, 'no')"
|
|
1610
|
+
>
|
|
1611
|
+
{{ branchConnectionTargetId(step, 'no') ? '取消连接' : '连接到下一节点' }}
|
|
1612
|
+
</el-button>
|
|
1613
|
+
</div>
|
|
1065
1614
|
</div>
|
|
1066
1615
|
</div>
|
|
1067
1616
|
<div v-if="hasBranchConnection(step)" class="appium-flow-merge">
|
|
@@ -1078,6 +1627,41 @@ watch(() => props.steps.length, () => {
|
|
|
1078
1627
|
/>
|
|
1079
1628
|
</svg>
|
|
1080
1629
|
</div>
|
|
1630
|
+
<el-dropdown
|
|
1631
|
+
v-if="defaultKind(step) !== 'condition'"
|
|
1632
|
+
trigger="click"
|
|
1633
|
+
:disabled="!canOpenInsertMenu()"
|
|
1634
|
+
max-height="320px"
|
|
1635
|
+
popper-class="appium-action-dropdown"
|
|
1636
|
+
@command="insertAction(index, $event)"
|
|
1637
|
+
>
|
|
1638
|
+
<button
|
|
1639
|
+
type="button"
|
|
1640
|
+
class="appium-flow-insert"
|
|
1641
|
+
:disabled="!canOpenInsertMenu()"
|
|
1642
|
+
>
|
|
1643
|
+
<el-icon><Timer /></el-icon>
|
|
1644
|
+
<span>插入操作</span>
|
|
1645
|
+
</button>
|
|
1646
|
+
<template #dropdown>
|
|
1647
|
+
<el-dropdown-menu>
|
|
1648
|
+
<el-dropdown-item v-if="clipboardCount" :command="PASTE_COMMAND" divided>
|
|
1649
|
+
粘贴 {{ clipboardCount }} 个节点
|
|
1650
|
+
</el-dropdown-item>
|
|
1651
|
+
<template v-for="group in mainActionGroups" :key="group.title">
|
|
1652
|
+
<div class="appium-action-dropdown__group">{{ group.title }}</div>
|
|
1653
|
+
<el-dropdown-item
|
|
1654
|
+
v-for="action in group.actions"
|
|
1655
|
+
:key="action.type"
|
|
1656
|
+
:command="action.type"
|
|
1657
|
+
:disabled="isInsertActionDisabled(action.type)"
|
|
1658
|
+
>
|
|
1659
|
+
{{ action.label }}
|
|
1660
|
+
</el-dropdown-item>
|
|
1661
|
+
</template>
|
|
1662
|
+
</el-dropdown-menu>
|
|
1663
|
+
</template>
|
|
1664
|
+
</el-dropdown>
|
|
1081
1665
|
</div>
|
|
1082
1666
|
</div>
|
|
1083
1667
|
</div>
|