feat(接口场景): api、case 响应循环分页&脚本响应&步骤列表循环控制器响应结果优化
This commit is contained in:
parent
bb04b9cdc5
commit
987df3fa75
|
@ -194,9 +194,11 @@ export interface TreeNode<T> {
|
||||||
* @param tree 树形数组或树
|
* @param tree 树形数组或树
|
||||||
* @param customNodeFn 自定义节点函数
|
* @param customNodeFn 自定义节点函数
|
||||||
* @param customChildrenKey 自定义子节点的key
|
* @param customChildrenKey 自定义子节点的key
|
||||||
|
* @param continueCondition 继续递归的条件,某些情况下需要无需递归某些节点的子孙节点,可传入该条件
|
||||||
*/
|
*/
|
||||||
export function traverseTree<T>(
|
export function traverseTree<T>(
|
||||||
tree: TreeNode<T> | TreeNode<T>[] | T | T[],
|
tree: TreeNode<T> | TreeNode<T>[] | T | T[],
|
||||||
|
continueCondition?: (node: TreeNode<T>) => boolean,
|
||||||
customNodeFn: (node: TreeNode<T>) => TreeNode<T> | null = (node) => node,
|
customNodeFn: (node: TreeNode<T>) => TreeNode<T> | null = (node) => node,
|
||||||
customChildrenKey = 'children'
|
customChildrenKey = 'children'
|
||||||
) {
|
) {
|
||||||
|
@ -209,7 +211,11 @@ export function traverseTree<T>(
|
||||||
customNodeFn(node);
|
customNodeFn(node);
|
||||||
}
|
}
|
||||||
if (node[customChildrenKey] && Array.isArray(node[customChildrenKey]) && node[customChildrenKey].length > 0) {
|
if (node[customChildrenKey] && Array.isArray(node[customChildrenKey]) && node[customChildrenKey].length > 0) {
|
||||||
traverseTree(node[customChildrenKey], customNodeFn, customChildrenKey);
|
if (typeof continueCondition === 'function' && !continueCondition(node)) {
|
||||||
|
// 如果有继续递归的条件,则判断是否继续递归
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
traverseTree(node[customChildrenKey], continueCondition, customNodeFn, customChildrenKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
|
<slot name="titleRight"></slot>
|
||||||
<div
|
<div
|
||||||
v-if="props.requestResult?.responseResult?.responseCode"
|
v-if="props.requestResult?.responseResult?.responseCode"
|
||||||
class="flex items-center justify-between gap-[24px] text-[14px]"
|
class="flex items-center justify-between gap-[24px] text-[14px]"
|
||||||
|
|
|
@ -272,15 +272,20 @@
|
||||||
:is-priority-local-exec="isPriorityLocalExec"
|
:is-priority-local-exec="isPriorityLocalExec"
|
||||||
:request-url="requestVModel.url"
|
:request-url="requestVModel.url"
|
||||||
:is-expanded="isVerticalExpanded"
|
:is-expanded="isVerticalExpanded"
|
||||||
:request-result="requestResult"
|
:request-result="currentResponse"
|
||||||
:console="requestResult?.console"
|
:console="currentResponse?.console"
|
||||||
:is-edit="false"
|
:is-edit="false"
|
||||||
is-definition
|
is-definition
|
||||||
|
hide-layout-switch
|
||||||
:loading="requestVModel.executeLoading || loading"
|
:loading="requestVModel.executeLoading || loading"
|
||||||
@change-expand="changeVerticalExpand"
|
@change-expand="changeVerticalExpand"
|
||||||
@change-layout="handleActiveLayoutChange"
|
@change-layout="handleActiveLayoutChange"
|
||||||
@execute="execute"
|
@execute="execute"
|
||||||
/>
|
>
|
||||||
|
<template #titleRight>
|
||||||
|
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
|
||||||
|
</template>
|
||||||
|
</response>
|
||||||
</template>
|
</template>
|
||||||
</MsSplitBox>
|
</MsSplitBox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -299,6 +304,7 @@
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||||
import assertion from '@/components/business/ms-assertion/index.vue';
|
import assertion from '@/components/business/ms-assertion/index.vue';
|
||||||
|
import loopPagination from './loopPagination.vue';
|
||||||
import stepTypeVue from './stepType/stepType.vue';
|
import stepTypeVue from './stepType/stepType.vue';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
||||||
|
@ -475,6 +481,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestVModel = ref<RequestParam>(defaultApiParams);
|
const requestVModel = ref<RequestParam>(defaultApiParams);
|
||||||
|
// 步骤类型判断
|
||||||
const _stepType = computed(() => {
|
const _stepType = computed(() => {
|
||||||
if (props.step) {
|
if (props.step) {
|
||||||
return getStepType(props.step);
|
return getStepType(props.step);
|
||||||
|
@ -484,30 +491,29 @@
|
||||||
isQuoteApi: false,
|
isQuoteApi: false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
// 抽屉标题
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
|
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
|
||||||
return props.step?.name;
|
return props.step?.name;
|
||||||
}
|
}
|
||||||
return t('apiScenario.customApi');
|
return t('apiScenario.customApi');
|
||||||
});
|
});
|
||||||
|
// 是否显示环境域名前缀
|
||||||
const showEnvPrefix = computed(
|
const showEnvPrefix = computed(
|
||||||
() =>
|
() =>
|
||||||
requestVModel.value.customizeRequestEnvEnable &&
|
requestVModel.value.customizeRequestEnvEnable &&
|
||||||
currentEnvConfig?.value.httpConfig.find((e) => e.type === 'NONE')?.url
|
currentEnvConfig?.value.httpConfig.find((e) => e.type === 'NONE')?.url
|
||||||
);
|
);
|
||||||
const responseResultBody = computed(() => {
|
const currentLoop = ref(1);
|
||||||
const length = props.stepResponses?.[requestVModel.value.stepId]
|
const currentResponse = computed(() => {
|
||||||
? props.stepResponses?.[requestVModel.value.stepId]?.length
|
if (props.step?.id) {
|
||||||
: 0;
|
return props.stepResponses?.[props.step?.id]?.[currentLoop.value - 1];
|
||||||
// 取最后一次执行的结果
|
}
|
||||||
return props.stepResponses?.[requestVModel.value.stepId]?.[length].responseResult.body;
|
|
||||||
});
|
});
|
||||||
const requestResult = computed(() => {
|
const loopTotal = computed(() => (props.step?.id && props.stepResponses?.[props.step?.id]?.length) || 0);
|
||||||
const length = props.stepResponses?.[requestVModel.value.stepId]
|
// 执行响应结果 body 部分
|
||||||
? props.stepResponses?.[requestVModel.value.stepId]?.length
|
const responseResultBody = computed(() => {
|
||||||
: 0;
|
return currentResponse.value?.responseResult.body;
|
||||||
// 取最后一次执行的结果
|
|
||||||
return props.stepResponses?.[requestVModel.value.stepId]?.[length];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -219,15 +219,20 @@
|
||||||
:is-priority-local-exec="isPriorityLocalExec"
|
:is-priority-local-exec="isPriorityLocalExec"
|
||||||
:request-url="requestVModel.url"
|
:request-url="requestVModel.url"
|
||||||
:is-expanded="isVerticalExpanded"
|
:is-expanded="isVerticalExpanded"
|
||||||
:request-result="requestResult"
|
:request-result="currentResponse"
|
||||||
:console="requestResult?.console"
|
:console="currentResponse?.console"
|
||||||
:is-edit="false"
|
:is-edit="false"
|
||||||
is-definition
|
is-definition
|
||||||
|
hide-layout-switch
|
||||||
:loading="requestVModel.executeLoading || loading"
|
:loading="requestVModel.executeLoading || loading"
|
||||||
@change-expand="changeVerticalExpand"
|
@change-expand="changeVerticalExpand"
|
||||||
@change-layout="handleActiveLayoutChange"
|
@change-layout="handleActiveLayoutChange"
|
||||||
@execute="execute"
|
@execute="execute"
|
||||||
/>
|
>
|
||||||
|
<template #titleRight>
|
||||||
|
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" class="!mb-0" />
|
||||||
|
</template>
|
||||||
|
</response>
|
||||||
</template>
|
</template>
|
||||||
</MsSplitBox>
|
</MsSplitBox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -246,6 +251,7 @@
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||||
import assertion from '@/components/business/ms-assertion/index.vue';
|
import assertion from '@/components/business/ms-assertion/index.vue';
|
||||||
|
import loopPagination from './loopPagination.vue';
|
||||||
import stepType from './stepType/stepType.vue';
|
import stepType from './stepType/stepType.vue';
|
||||||
import auth from '@/views/api-test/components/requestComposition/auth.vue';
|
import auth from '@/views/api-test/components/requestComposition/auth.vue';
|
||||||
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
|
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
|
||||||
|
@ -396,19 +402,16 @@
|
||||||
() =>
|
() =>
|
||||||
activeStep.value?.stepType === ScenarioStepType.API_CASE && activeStep.value?.refType === ScenarioStepRefType.REF
|
activeStep.value?.stepType === ScenarioStepType.API_CASE && activeStep.value?.refType === ScenarioStepRefType.REF
|
||||||
);
|
);
|
||||||
const responseResultBody = computed(() => {
|
const currentLoop = ref(1);
|
||||||
const length = props.stepResponses?.[requestVModel.value.stepId]
|
const currentResponse = computed(() => {
|
||||||
? props.stepResponses?.[requestVModel.value.stepId]?.length
|
if (activeStep.value?.id) {
|
||||||
: 0;
|
return props.stepResponses?.[activeStep.value?.id]?.[currentLoop.value - 1];
|
||||||
// 取最后一次执行的结果
|
}
|
||||||
return props.stepResponses?.[requestVModel.value.stepId]?.[length].responseResult.body;
|
|
||||||
});
|
});
|
||||||
const requestResult = computed(() => {
|
const loopTotal = computed(() => (activeStep.value?.id && props.stepResponses?.[activeStep.value?.id]?.length) || 0);
|
||||||
const length = props.stepResponses?.[requestVModel.value.stepId]
|
// 执行响应结果 body 部分
|
||||||
? props.stepResponses?.[requestVModel.value.stepId]?.length
|
const responseResultBody = computed(() => {
|
||||||
: 0;
|
return currentResponse.value?.responseResult.body;
|
||||||
// 取最后一次执行的结果
|
|
||||||
return props.stepResponses?.[requestVModel.value.stepId]?.[length];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<MsTag v-if="status" :self-style="status.style" :size="props.size"> {{ status.text }}</MsTag>
|
<MsTag v-if="status" :self-style="status.style" :size="props.size">
|
||||||
|
<div class="flex items-center justify-between gap-[4px]">
|
||||||
|
<span>{{ status.text }}</span>
|
||||||
|
<span v-if="props.extraText">{{ props.extraText }}</span>
|
||||||
|
</div>
|
||||||
|
</MsTag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -12,6 +17,7 @@
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
status?: ScenarioExecuteStatus;
|
status?: ScenarioExecuteStatus;
|
||||||
size?: Size;
|
size?: Size;
|
||||||
|
extraText?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -39,7 +39,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.arco-popover {
|
.arco-popover,
|
||||||
|
.arco-drawer {
|
||||||
.loop-pagination {
|
.loop-pagination {
|
||||||
@apply justify-start;
|
@apply justify-start;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
content-class="scenario-step-response-popover"
|
content-class="scenario-step-response-popover"
|
||||||
@popup-visible-change="emit('visibleChange', $event, props.step)"
|
@popup-visible-change="emit('visibleChange', $event, props.step)"
|
||||||
>
|
>
|
||||||
<executeStatus :status="lastExecuteStatus" size="small" class="ml-[4px]" />
|
<executeStatus :status="finalExecuteStatus" size="small" class="ml-[4px]" />
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex h-full flex-col">
|
<div class="flex h-full flex-col">
|
||||||
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
|
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
const currentLoop = ref(1);
|
const currentLoop = ref(1);
|
||||||
const currentResponse = computed(() => props.stepResponses?.[props.step.id]?.[currentLoop.value - 1]);
|
const currentResponse = computed(() => props.stepResponses?.[props.step.id]?.[currentLoop.value - 1]);
|
||||||
const loopTotal = computed(() => props.stepResponses?.[props.step.id]?.length || 0);
|
const loopTotal = computed(() => props.stepResponses?.[props.step.id]?.length || 0);
|
||||||
const lastExecuteStatus = computed(() => {
|
const finalExecuteStatus = computed(() => {
|
||||||
if (props.stepResponses[props.step.id] && props.stepResponses[props.step.id].length > 0) {
|
if (props.stepResponses[props.step.id] && props.stepResponses[props.step.id].length > 0) {
|
||||||
// 有一次失败就是失败
|
// 有一次失败就是失败
|
||||||
return props.stepResponses[props.step.id].some((report) => !report.isSuccessful)
|
return props.stepResponses[props.step.id].some((report) => !report.isSuccessful)
|
||||||
|
|
|
@ -21,9 +21,16 @@
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[10px] flex h-[calc(100%-40px)] gap-[8px]">
|
<div class="mt-[10px] flex flex-1 gap-[8px]">
|
||||||
<conditionContent v-if="visible" v-model:data="activeItem" :is-build-in="true" :is-format="true" />
|
<conditionContent v-if="visible" v-model:data="activeItem" :is-build-in="true" :is-format="true" />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="currentResponse?.console" class="p-[8px]">
|
||||||
|
<div class="mb-[8px] font-medium text-[var(--color-text-1)]">{{ t('apiScenario.executionResult') }}</div>
|
||||||
|
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
|
||||||
|
<div class="h-[300px] bg-[var(--color-text-n9)] p-[12px]">
|
||||||
|
<pre class="response-header-pre">{{ currentResponse?.console }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template v-if="!props.detail" #footer>
|
<template v-if="!props.detail" #footer>
|
||||||
<a-button type="secondary" @click="handleDrawerCancel">
|
<a-button type="secondary" @click="handleDrawerCancel">
|
||||||
{{ t('common.cancel') }}
|
{{ t('common.cancel') }}
|
||||||
|
@ -43,11 +50,12 @@
|
||||||
|
|
||||||
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
import loopPagination from './loopPagination.vue';
|
||||||
|
|
||||||
// import stepTypeVue from './stepType/stepType.vue';
|
// import stepTypeVue from './stepType/stepType.vue';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import { ExecuteConditionProcessor } from '@/models/apiTest/common';
|
import { ExecuteConditionProcessor, RequestResult } from '@/models/apiTest/common';
|
||||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
@ -57,6 +65,7 @@
|
||||||
detail?: ExecuteConditionProcessor;
|
detail?: ExecuteConditionProcessor;
|
||||||
step?: ScenarioStepItem;
|
step?: ScenarioStepItem;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
stepResponses?: Record<string | number, RequestResult[]>;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'add', name: string, scriptProcessor: ExecuteConditionProcessor): void;
|
(e: 'add', name: string, scriptProcessor: ExecuteConditionProcessor): void;
|
||||||
|
@ -77,6 +86,13 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', { required: true });
|
const visible = defineModel<boolean>('visible', { required: true });
|
||||||
|
const currentLoop = ref(1);
|
||||||
|
const currentResponse = computed(() => {
|
||||||
|
if (props.step?.id) {
|
||||||
|
return props.stepResponses?.[props.step?.id]?.[currentLoop.value - 1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const loopTotal = computed(() => (props.step?.id && props.stepResponses?.[props.step?.id]?.length) || 0);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => visible.value,
|
() => visible.value,
|
||||||
|
@ -118,4 +134,12 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped>
|
||||||
|
.response-header-pre {
|
||||||
|
@apply h-full overflow-auto bg-white;
|
||||||
|
.ms-scroll-bar();
|
||||||
|
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -100,106 +100,62 @@
|
||||||
function handleCreateActionSelect(val: ScenarioAddStepActionType) {
|
function handleCreateActionSelect(val: ScenarioAddStepActionType) {
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case ScenarioAddStepActionType.LOOP_CONTROL:
|
case ScenarioAddStepActionType.LOOP_CONTROL:
|
||||||
if (step.value && props.createStepAction) {
|
const defaultLoopStep = buildInsertStepInfos(
|
||||||
handleCreateStep(
|
|
||||||
{
|
|
||||||
stepType: ScenarioStepType.LOOP_CONTROLLER,
|
|
||||||
name: t('apiScenario.loopControl'),
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
},
|
|
||||||
step.value,
|
|
||||||
steps.value,
|
|
||||||
props.createStepAction,
|
|
||||||
selectedKeys.value
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
steps.value.push(
|
|
||||||
buildInsertStepInfos(
|
|
||||||
[cloneDeep(defaultStepItemCommon)],
|
[cloneDeep(defaultStepItemCommon)],
|
||||||
ScenarioStepType.LOOP_CONTROLLER,
|
ScenarioStepType.LOOP_CONTROLLER,
|
||||||
ScenarioStepRefType.DIRECT,
|
ScenarioStepRefType.DIRECT,
|
||||||
steps.value.length + 1,
|
steps.value.length + 1,
|
||||||
appStore.currentProjectId
|
appStore.currentProjectId
|
||||||
)[0]
|
)[0];
|
||||||
);
|
if (step.value && props.createStepAction) {
|
||||||
|
handleCreateStep(defaultLoopStep, step.value, steps.value, props.createStepAction, selectedKeys.value);
|
||||||
|
} else {
|
||||||
|
steps.value.push(defaultLoopStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone');
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.CONDITION_CONTROL:
|
case ScenarioAddStepActionType.CONDITION_CONTROL:
|
||||||
if (step.value && props.createStepAction) {
|
const defaultConditionStep = buildInsertStepInfos(
|
||||||
handleCreateStep(
|
|
||||||
{
|
|
||||||
stepType: ScenarioStepType.IF_CONTROLLER,
|
|
||||||
name: t('apiScenario.conditionControl'),
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
},
|
|
||||||
step.value,
|
|
||||||
steps.value,
|
|
||||||
props.createStepAction,
|
|
||||||
selectedKeys.value
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
steps.value.push(
|
|
||||||
buildInsertStepInfos(
|
|
||||||
[cloneDeep(defaultStepItemCommon)],
|
[cloneDeep(defaultStepItemCommon)],
|
||||||
ScenarioStepType.IF_CONTROLLER,
|
ScenarioStepType.IF_CONTROLLER,
|
||||||
ScenarioStepRefType.DIRECT,
|
ScenarioStepRefType.DIRECT,
|
||||||
steps.value.length + 1,
|
steps.value.length + 1,
|
||||||
appStore.currentProjectId
|
appStore.currentProjectId
|
||||||
)[0]
|
)[0];
|
||||||
);
|
if (step.value && props.createStepAction) {
|
||||||
|
handleCreateStep(defaultConditionStep, step.value, steps.value, props.createStepAction, selectedKeys.value);
|
||||||
|
} else {
|
||||||
|
steps.value.push(defaultConditionStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone');
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.ONLY_ONCE_CONTROL:
|
case ScenarioAddStepActionType.ONLY_ONCE_CONTROL:
|
||||||
if (step.value && props.createStepAction) {
|
const defaultOnlyOnceStep = buildInsertStepInfos(
|
||||||
handleCreateStep(
|
|
||||||
{
|
|
||||||
stepType: ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
|
||||||
name: t('apiScenario.onlyOnceControl'),
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
},
|
|
||||||
step.value,
|
|
||||||
steps.value,
|
|
||||||
props.createStepAction,
|
|
||||||
selectedKeys.value
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
steps.value.push(
|
|
||||||
buildInsertStepInfos(
|
|
||||||
[cloneDeep(defaultStepItemCommon)],
|
[cloneDeep(defaultStepItemCommon)],
|
||||||
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
ScenarioStepType.ONCE_ONLY_CONTROLLER,
|
||||||
ScenarioStepRefType.DIRECT,
|
ScenarioStepRefType.DIRECT,
|
||||||
steps.value.length + 1,
|
steps.value.length + 1,
|
||||||
appStore.currentProjectId
|
appStore.currentProjectId
|
||||||
)[0]
|
)[0];
|
||||||
);
|
if (step.value && props.createStepAction) {
|
||||||
|
handleCreateStep(defaultOnlyOnceStep, step.value, steps.value, props.createStepAction, selectedKeys.value);
|
||||||
|
} else {
|
||||||
|
steps.value.push(defaultOnlyOnceStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone');
|
||||||
break;
|
break;
|
||||||
case ScenarioAddStepActionType.WAIT_TIME:
|
case ScenarioAddStepActionType.WAIT_TIME:
|
||||||
if (step.value && props.createStepAction) {
|
const defaultWaitTimeStep = buildInsertStepInfos(
|
||||||
handleCreateStep(
|
|
||||||
{
|
|
||||||
stepType: ScenarioStepType.CONSTANT_TIMER,
|
|
||||||
name: t('apiScenario.waitTime'),
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
},
|
|
||||||
step.value,
|
|
||||||
steps.value,
|
|
||||||
props.createStepAction,
|
|
||||||
selectedKeys.value
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
steps.value.push(
|
|
||||||
buildInsertStepInfos(
|
|
||||||
[cloneDeep(defaultStepItemCommon)],
|
[cloneDeep(defaultStepItemCommon)],
|
||||||
ScenarioStepType.CONSTANT_TIMER,
|
ScenarioStepType.CONSTANT_TIMER,
|
||||||
ScenarioStepRefType.DIRECT,
|
ScenarioStepRefType.DIRECT,
|
||||||
steps.value.length + 1,
|
steps.value.length + 1,
|
||||||
appStore.currentProjectId
|
appStore.currentProjectId
|
||||||
)[0]
|
)[0];
|
||||||
);
|
if (step.value && props.createStepAction) {
|
||||||
|
handleCreateStep(defaultWaitTimeStep, step.value, steps.value, props.createStepAction, selectedKeys.value);
|
||||||
|
} else {
|
||||||
|
steps.value.push(defaultWaitTimeStep);
|
||||||
}
|
}
|
||||||
emit('addDone');
|
emit('addDone');
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -227,6 +227,7 @@
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
innerData.value = props.data;
|
innerData.value = props.data;
|
||||||
|
console.log('watchEffect', props.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 接收全局双击时间戳
|
// 接收全局双击时间戳
|
||||||
|
|
|
@ -196,6 +196,7 @@
|
||||||
<executeStatus
|
<executeStatus
|
||||||
v-else-if="step.executeStatus"
|
v-else-if="step.executeStatus"
|
||||||
:status="getExecuteStatus(step)"
|
:status="getExecuteStatus(step)"
|
||||||
|
:extra-text="getExecuteStatusExtraText(step)"
|
||||||
size="small"
|
size="small"
|
||||||
class="ml-[4px]"
|
class="ml-[4px]"
|
||||||
/>
|
/>
|
||||||
|
@ -255,6 +256,7 @@
|
||||||
:detail="currentStepDetail as unknown as ExecuteConditionProcessor"
|
:detail="currentStepDetail as unknown as ExecuteConditionProcessor"
|
||||||
:step="activeStep"
|
:step="activeStep"
|
||||||
:name="activeStep?.name"
|
:name="activeStep?.name"
|
||||||
|
:step-responses="scenario.stepResponses"
|
||||||
@add="addScriptStep"
|
@add="addScriptStep"
|
||||||
@save="saveScriptStep"
|
@save="saveScriptStep"
|
||||||
/>
|
/>
|
||||||
|
@ -424,6 +426,7 @@
|
||||||
import {
|
import {
|
||||||
ScenarioAddStepActionType,
|
ScenarioAddStepActionType,
|
||||||
ScenarioExecuteStatus,
|
ScenarioExecuteStatus,
|
||||||
|
ScenarioStepLoopTypeEnum,
|
||||||
ScenarioStepRefType,
|
ScenarioStepRefType,
|
||||||
ScenarioStepType,
|
ScenarioStepType,
|
||||||
} from '@/enums/apiEnum';
|
} from '@/enums/apiEnum';
|
||||||
|
@ -481,6 +484,10 @@
|
||||||
focusStepKey.value = id || '';
|
focusStepKey.value = id || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkStepIsApi(step: ScenarioStepItem) {
|
||||||
|
return [ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(step.stepType);
|
||||||
|
}
|
||||||
|
|
||||||
function getExecuteStatus(step: ScenarioStepItem) {
|
function getExecuteStatus(step: ScenarioStepItem) {
|
||||||
if (scenario.value.stepResponses && scenario.value.stepResponses[step.id]) {
|
if (scenario.value.stepResponses && scenario.value.stepResponses[step.id]) {
|
||||||
// 有一次失败就是失败
|
// 有一次失败就是失败
|
||||||
|
@ -491,6 +498,24 @@
|
||||||
return step.executeStatus;
|
return step.executeStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExecuteStatusExtraText(step: ScenarioStepItem) {
|
||||||
|
if (
|
||||||
|
step.stepType === ScenarioStepType.LOOP_CONTROLLER &&
|
||||||
|
step.config.loopType === ScenarioStepLoopTypeEnum.LOOP_COUNT &&
|
||||||
|
step.config.msCountController &&
|
||||||
|
step.config.msCountController.loops > 0
|
||||||
|
) {
|
||||||
|
// 循环控制器展示当前执行次数/总次数
|
||||||
|
const firstHasResultChild = step.children?.find((child) => {
|
||||||
|
return checkStepIsApi(child) || child.stepType === ScenarioStepType.SCRIPT;
|
||||||
|
});
|
||||||
|
return firstHasResultChild && scenario.value.stepResponses[firstHasResultChild.id]
|
||||||
|
? `${scenario.value.stepResponses[firstHasResultChild.id].length}/${step.config.msCountController.loops}`
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function handleResponsePopoverVisibleChange(visible: boolean, step: ScenarioStepItem) {
|
function handleResponsePopoverVisibleChange(visible: boolean, step: ScenarioStepItem) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
setFocusNodeKey(step.id);
|
setFocusNodeKey(step.id);
|
||||||
|
@ -520,10 +545,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkStepIsApi(step: ScenarioStepItem) {
|
|
||||||
return [ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(step.stepType);
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkStepShowMethod(step: ScenarioStepItem) {
|
function checkStepShowMethod(step: ScenarioStepItem) {
|
||||||
return [
|
return [
|
||||||
ScenarioStepType.API,
|
ScenarioStepType.API,
|
||||||
|
@ -1094,12 +1115,22 @@
|
||||||
realStep.reportId = getGenerateId();
|
realStep.reportId = getGenerateId();
|
||||||
const _stepDetails = {};
|
const _stepDetails = {};
|
||||||
const stepFileParam = scenario.value.stepFileParam[realStep.id];
|
const stepFileParam = scenario.value.stepFileParam[realStep.id];
|
||||||
traverseTree(realStep, (step) => {
|
traverseTree(
|
||||||
|
realStep,
|
||||||
|
(step) => {
|
||||||
|
// 当前步骤是启用的情况,才需要继续递归子孙步骤;否则无需向下递归
|
||||||
|
return step.enable;
|
||||||
|
},
|
||||||
|
(step) => {
|
||||||
|
if (step.enable) {
|
||||||
|
// 启用的步骤才执行
|
||||||
_stepDetails[step.id] = stepDetails.value[step.id];
|
_stepDetails[step.id] = stepDetails.value[step.id];
|
||||||
step.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
step.executeStatus = ScenarioExecuteStatus.EXECUTING;
|
||||||
|
}
|
||||||
delete scenario.value.stepResponses[step.id]; // 先移除上一次的执行结果
|
delete scenario.value.stepResponses[step.id]; // 先移除上一次的执行结果
|
||||||
return step;
|
return step;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
realExecute(
|
realExecute(
|
||||||
{
|
{
|
||||||
steps: [realStep as ScenarioStepItem],
|
steps: [realStep as ScenarioStepItem],
|
||||||
|
|
|
@ -12,7 +12,6 @@ export default function updateStepStatus(
|
||||||
) {
|
) {
|
||||||
for (let i = 0; i < steps.length; i++) {
|
for (let i = 0; i < steps.length; i++) {
|
||||||
const node = steps[i];
|
const node = steps[i];
|
||||||
console.log('node', node.stepType, node.executeStatus);
|
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
ScenarioStepType.LOOP_CONTROLLER,
|
ScenarioStepType.LOOP_CONTROLLER,
|
||||||
|
@ -56,7 +55,6 @@ export default function updateStepStatus(
|
||||||
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
|
||||||
// 非逻辑控制器直接更改本身状态
|
// 非逻辑控制器直接更改本身状态
|
||||||
if (stepResponses[node.id] && stepResponses[node.id].length > 0) {
|
if (stepResponses[node.id] && stepResponses[node.id].length > 0) {
|
||||||
console.log('stepResponses[node.id]', stepResponses[node.id]);
|
|
||||||
node.executeStatus = stepResponses[node.id].some((report) => !report.isSuccessful)
|
node.executeStatus = stepResponses[node.id].some((report) => !report.isSuccessful)
|
||||||
? ScenarioExecuteStatus.FAILED
|
? ScenarioExecuteStatus.FAILED
|
||||||
: ScenarioExecuteStatus.SUCCESS;
|
: ScenarioExecuteStatus.SUCCESS;
|
||||||
|
|
|
@ -64,6 +64,7 @@ export default {
|
||||||
'apiScenario.next': '下一次',
|
'apiScenario.next': '下一次',
|
||||||
'apiScenario.sumLoop': '共{count}次循环',
|
'apiScenario.sumLoop': '共{count}次循环',
|
||||||
'apiScenario.times': '次',
|
'apiScenario.times': '次',
|
||||||
|
'apiScenario.executionResult': '执行结果',
|
||||||
// 批量操作文案
|
// 批量操作文案
|
||||||
'api_scenario.batch_operation.success': '成功{opt}至 {name}',
|
'api_scenario.batch_operation.success': '成功{opt}至 {name}',
|
||||||
'api_scenario.table.batchMoveConfirm': '{opt}{count}个场景至已选模块',
|
'api_scenario.table.batchMoveConfirm': '{opt}{count}个场景至已选模块',
|
||||||
|
|
Loading…
Reference in New Issue