feat(接口场景): 执行响应优化&循环响应展示

This commit is contained in:
baiqi 2024-04-01 18:22:24 +08:00 committed by 刘瑞斌
parent 2441a882cc
commit e8f1ce98cc
17 changed files with 365 additions and 126 deletions

View File

@ -3,7 +3,7 @@ import { SelectProps } from '@arco-design/web-vue';
import { Size } from './types';
export const PAGE_ITEM_TYPES = ['page', 'more', 'previous', 'next'] as const;
export const PAGE_ITEM_TYPES = ['page', 'more', 'previous', 'next', 'jumper'] as const;
export type PageItemType = (typeof PAGE_ITEM_TYPES)[number];

View File

@ -15,10 +15,12 @@
@change="handleChange"
@enter="handleChange"
/>
<span v-if="$slots['jumper-append']" :class="`${prefixCls}-append`"><slot name="jumper-append" /></span>
<span :class="`${prefixCls}-total-page`" :style="{ 'min-width': totalPageWidth }">{{
t('msPagination.page', { page: pages })
}}</span>
<span v-if="$slots['jumper-append']" :class="`${prefixCls}-append`">
<slot name="jumper-append" />
</span>
<span :class="`${prefixCls}-total-page`" :style="{ 'min-width': totalPageWidth }">
{{ t('msPagination.page', { page: pages }) }}
</span>
</span>
</template>

View File

@ -389,7 +389,7 @@ export interface Scenario {
executeSuccessCount: number; // 执行成功数量
executeFailCount: number; // 执行失败数量
reportId?: string | number; // 场景报告 id
stepResponses: Record<string | number, RequestResult>; // 步骤响应集合key 为步骤 idvalue 为步骤响应内容
stepResponses: Record<string | number, Array<RequestResult>>; // 步骤响应集合key 为步骤 idvalue 为步骤响应内容
isExecute?: boolean; // 是否从列表执行进去场景详情
isDebug?: boolean; // 是否调试,区分执行场景和批量调试步骤
}

View File

@ -1,4 +1,4 @@
import { cloneDeep } from 'lodash-es';
import { cloneDeep, each } from 'lodash-es';
import JSEncrypt from 'jsencrypt';
import { BatchActionQueryParams, MsTableColumnData } from '@/components/pure/ms-table/type';
@ -194,6 +194,31 @@ export interface TreeNode<T> {
* @param tree
* @param customNodeFn
* @param customChildrenKey key
*/
export function traverseTree<T>(
tree: TreeNode<T> | TreeNode<T>[] | T | T[],
customNodeFn: (node: TreeNode<T>) => TreeNode<T> | null = (node) => node,
customChildrenKey = 'children'
) {
if (!Array.isArray(tree)) {
tree = [tree];
}
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (typeof customNodeFn === 'function') {
customNodeFn(node);
}
if (node[customChildrenKey] && Array.isArray(node[customChildrenKey]) && node[customChildrenKey].length > 0) {
traverseTree(node[customChildrenKey], customNodeFn, customChildrenKey);
}
}
}
/**
*
* @param tree
* @param customNodeFn
* @param customChildrenKey key
* @param parent
* @param parentPath
* @param level

View File

@ -61,7 +61,9 @@
value: item.id,
}));
currentEnv.value = currentEnv.value.length ? currentEnv.value : res[0]?.id;
await initEnvironment();
if (currentEnv.value) {
await initEnvironment();
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -20,7 +20,6 @@
<MsEditableTab
v-model:active-tab="activeDebug"
v-model:tabs="debugTabs"
:limit="10"
:readonly="!hasAnyPermission(['PROJECT_API_DEBUG:READ+ADD'])"
at-least-one
@add="addDebugTab"

View File

@ -12,7 +12,10 @@
>
<template #title>
<div class="flex max-w-[60%] items-center gap-[8px]">
<stepTypeVue v-if="props.step" :step="props.step" />
<stepTypeVue
v-if="props.step && [ScenarioStepType.API, ScenarioStepType.CUSTOM_REQUEST].includes(props.step?.stepType)"
:step="props.step"
/>
<a-tooltip :content="title" position="bottom">
<div class="one-line-text">
{{ title }}
@ -228,7 +231,7 @@
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="props.stepResponses?.[requestVModel.stepId]?.responseResult.body"
:response="responseResultBody"
:layout="activeLayout"
:disabled="!isEditableApi || isQuoteScenarioStep"
:second-box-height="secondBoxHeight"
@ -238,7 +241,7 @@
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:response="props.stepResponses?.[requestVModel.stepId]?.responseResult.body"
:response="responseResultBody"
is-definition
:disabled="!isEditableApi || isQuoteScenarioStep"
:assertion-config="requestVModel.children[0].assertionConfig"
@ -269,8 +272,8 @@
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:request-result="props.stepResponses?.[requestVModel.stepId]"
:console="props.stepResponses?.[requestVModel.stepId]?.console"
:request-result="requestResult"
:console="requestResult?.console"
:is-edit="false"
is-definition
:loading="requestVModel.executeLoading || loading"
@ -387,7 +390,7 @@
create: string;
update: string;
};
stepResponses?: Record<string | number, RequestResult>;
stepResponses?: Record<string | number, RequestResult[]>;
fileParams?: ScenarioStepFileParams;
}>();
@ -485,13 +488,27 @@
if (_stepType.value.isCopyApi || _stepType.value.isQuoteApi) {
return props.step?.name;
}
return props.step?.name || t('apiScenario.customApi');
return t('apiScenario.customApi');
});
const showEnvPrefix = computed(
() =>
requestVModel.value.customizeRequestEnvEnable &&
currentEnvConfig?.value.httpConfig.find((e) => e.type === 'NONE')?.url
);
const responseResultBody = computed(() => {
const length = props.stepResponses?.[requestVModel.value.stepId]
? props.stepResponses?.[requestVModel.value.stepId]?.length
: 0;
//
return props.stepResponses?.[requestVModel.value.stepId]?.[length].responseResult.body;
});
const requestResult = computed(() => {
const length = props.stepResponses?.[requestVModel.value.stepId]
? props.stepResponses?.[requestVModel.value.stepId]?.length
: 0;
//
return props.stepResponses?.[requestVModel.value.stepId]?.[length];
});
watch(
() => props.stepResponses,
@ -1086,6 +1103,7 @@
requestVModel.value = cloneDeep({
...defaultApiParams,
...props.request,
url: props.request.path, // path
activeTab: contentTabList.value[0].value,
responseActiveTab: ResponseComposition.BODY,
isNew: false,

View File

@ -178,7 +178,7 @@
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="props.stepResponses?.[requestVModel.stepId]?.responseResult.body"
:response="responseResultBody"
:layout="activeLayout"
:disabled="!isEditableApi"
:second-box-height="secondBoxHeight"
@ -188,7 +188,7 @@
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:response="props.stepResponses?.[requestVModel.stepId]?.responseResult.body"
:response="responseResultBody"
is-definition
:disabled="!isEditableApi"
:assertion-config="requestVModel.children[0].assertionConfig"
@ -219,8 +219,8 @@
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:request-result="props.stepResponses?.[requestVModel.stepId]"
:console="props.stepResponses?.[requestVModel.stepId]?.console"
:request-result="requestResult"
:console="requestResult?.console"
:is-edit="false"
is-definition
:loading="requestVModel.executeLoading || loading"
@ -297,7 +297,7 @@
const props = defineProps<{
request?: RequestParam; //
stepResponses?: Record<string | number, RequestResult>;
stepResponses?: Record<string | number, RequestResult[]>;
fileParams?: ScenarioStepFileParams;
}>();
const emit = defineEmits<{
@ -396,6 +396,20 @@
() =>
activeStep.value?.stepType === ScenarioStepType.API_CASE && activeStep.value?.refType === ScenarioStepRefType.REF
);
const responseResultBody = computed(() => {
const length = props.stepResponses?.[requestVModel.value.stepId]
? props.stepResponses?.[requestVModel.value.stepId]?.length
: 0;
//
return props.stepResponses?.[requestVModel.value.stepId]?.[length].responseResult.body;
});
const requestResult = computed(() => {
const length = props.stepResponses?.[requestVModel.value.stepId]
? props.stepResponses?.[requestVModel.value.stepId]?.length
: 0;
//
return props.stepResponses?.[requestVModel.value.stepId]?.[length];
});
watch(
() => props.stepResponses,

View File

@ -0,0 +1,95 @@
<template>
<MsPagination
v-if="props.loopTotal > 1"
v-model:current="currentLoop"
:page-size="1"
:total="props.loopTotal"
:show-jumper="props.loopTotal > 5"
show-total
size="mini"
class="loop-pagination"
>
<template #total="{ total }">
<div
class="rounded-[var(--border-radius-small)] bg-[var(--color-text-n8)] p-[2px_6px] leading-[20px] text-[var(--color-text-2)]"
>
{{ t('apiScenario.sumLoop', { count: total }) }}
</div>
</template>
<template #jumper-append>
{{ t('apiScenario.times') }}
</template>
</MsPagination>
</template>
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import MsPagination from '@/components/pure/ms-pagination';
const props = defineProps<{
loopTotal: number;
}>();
const { t } = useI18n();
const currentLoop = defineModel<number>('currentLoop', {
required: true,
});
</script>
<style lang="less">
.arco-popover {
.loop-pagination {
@apply justify-start;
gap: 2px;
margin-bottom: 8px;
.ms-pagination-list {
gap: 2px;
padding: 2px;
border-radius: var(--border-radius-small);
background-color: var(--color-text-n8);
.ms-pagination-item {
padding: 0 6px;
min-width: 20px;
height: 20px;
border: none;
border-radius: var(--border-radius-mini);
color: var(--color-text-1);
background-color: white;
line-height: 20px;
}
.ms-pagination-item-previous {
margin-left: 0;
}
.ms-pagination-item-disabled {
cursor: not-allowed;
color: var(--color-text-4);
}
.ms-pagination-item-active {
border: 1px solid rgb(var(--primary-5)) !important;
color: rgb(var(--primary-5)) !important;
background-color: white !important;
}
}
.ms-pagination-jumper {
gap: 4px;
padding: 2px 6px;
border-radius: var(--border-radius-mini);
color: var(--color-text-2);
background-color: var(--color-text-n8);
.ms-pagination-jumper-input {
width: 48px;
background-color: white;
input {
height: 18px;
}
}
.ms-pagination-jumper-total-page {
@apply hidden;
}
}
}
}
</style>

View File

@ -0,0 +1,93 @@
<template>
<a-popover
position="br"
content-class="scenario-step-response-popover"
@popup-visible-change="emit('visibleChange', $event, props.step)"
>
<executeStatus :status="lastExecuteStatus" size="small" class="ml-[4px]" />
<template #content>
<div class="flex h-full flex-col">
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
<div class="flex-1">
<responseResult
:active-tab="ResponseComposition.BODY"
:request-result="currentResponse"
:console="currentResponse?.console"
:show-empty="false"
:is-edit="false"
is-definition
>
<template #titleLeft>
<div class="flex items-center text-[14px]">
<div class="font-medium text-[var(--color-text-1)]">{{ t('apiScenario.response') }}</div>
<a-tooltip :content="props.step.name">
<div class="one-line-text">({{ props.step.name }})</div>
</a-tooltip>
</div>
</template>
</responseResult>
</div>
</div>
</template>
</a-popover>
</template>
<script lang="ts" setup>
import executeStatus from './executeStatus.vue';
import loopPagination from './loopPagination.vue';
import { useI18n } from '@/hooks/useI18n';
import { RequestResult } from '@/models/apiTest/common';
import { ScenarioStepItem } from '@/models/apiTest/scenario';
import { ResponseComposition, ScenarioExecuteStatus } from '@/enums/apiEnum';
const responseResult = defineAsyncComponent(
() => import('@/views/api-test/components/requestComposition/response/index.vue')
);
const props = defineProps<{
step: ScenarioStepItem;
stepResponses: Record<string | number, Array<RequestResult>>;
}>();
const emit = defineEmits(['visibleChange']);
const { t } = useI18n();
const currentLoop = ref(1);
const currentResponse = computed(() => props.stepResponses?.[props.step.id]?.[currentLoop.value - 1]);
const loopTotal = computed(() => props.stepResponses?.[props.step.id]?.length || 0);
const lastExecuteStatus = computed(() => {
if (props.stepResponses[props.step.id] && props.stepResponses[props.step.id].length > 0) {
//
return props.stepResponses[props.step.id].some((report) => !report.isSuccessful)
? ScenarioExecuteStatus.FAILED
: ScenarioExecuteStatus.SUCCESS;
}
return props.step.executeStatus;
});
</script>
<style lang="less">
.scenario-step-response-popover {
width: 540px;
height: 500px;
.arco-popover-content {
@apply h-full;
.response {
.response-head {
background-color: var(--color-text-n9);
}
border: 1px solid var(--color-text-n8);
border-radius: var(--border-radius-small);
.arco-spin {
padding: 0;
.response-container {
padding: 0 16px 14px;
}
}
}
}
}
</style>

View File

@ -82,8 +82,16 @@
() => visible.value,
(val) => {
if (val) {
scriptName.value = props.name || '';
activeItem.value = cloneDeep(props.detail || defaultScript);
scriptName.value = props.detail ? props.name || '' : '';
activeItem.value = cloneDeep(
props.detail
? {
...props.detail,
processorType: RequestConditionProcessor.SCRIPT,
polymorphicName: 'MsScriptElement',
}
: defaultScript
);
}
}
);

View File

@ -23,7 +23,7 @@ export default function useCreateActions() {
* @param steps
* @param parent
*/
function checkedIfNeed(
function selectedIfNeed(
selectedKeys: (string | number)[],
steps: (ScenarioStepItem | TreeNode<ScenarioStepItem>)[],
parent?: TreeNode<ScenarioStepItem>
@ -59,7 +59,7 @@ export default function useCreateActions() {
step.id,
newStep,
createStepAction,
(newNode, parent) => checkedIfNeed(selectedKeys, [newNode], parent),
(newNode, parent) => selectedIfNeed(selectedKeys, [newNode], parent),
'id'
);
}
@ -174,13 +174,13 @@ export default function useCreateActions() {
undefined,
'id'
);
checkedIfNeed(selectedKeys, readyInsertSteps, step);
selectedIfNeed(selectedKeys, readyInsertSteps, step);
}
return {
handleCreateStep,
buildInsertStepInfos,
handleCreateSteps,
checkedIfNeed,
selectedIfNeed,
};
}

View File

@ -1,13 +1,11 @@
<template>
<div class="flex items-center gap-[4px]">
<a-popover
v-if="
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.API_SCENARIO].includes(props.data.stepType)
"
position="bl"
content-class="detail-popover"
arrow-class="hidden"
>
<div
v-if="
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.API_SCENARIO].includes(props.data.stepType)
"
class="flex items-center gap-[4px]"
>
<a-popover position="bl" content-class="detail-popover" arrow-class="hidden">
<MsIcon type="icon-icon-draft" class="text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]" />
<template #content>
<div class="flex flex-col gap-[16px]">

View File

@ -178,7 +178,7 @@
/>
</template>
<template #extraEnd="step">
<a-popover
<responsePopover
v-if="
![
ScenarioStepType.LOOP_CONTROLLER,
@ -189,32 +189,16 @@
(getExecuteStatus(step) === ScenarioExecuteStatus.SUCCESS ||
getExecuteStatus(step) === ScenarioExecuteStatus.FAILED)
"
position="br"
content-class="scenario-step-response-popover"
@popup-visible-change="handleResponsePopoverVisibleChange($event, step)"
>
<executeStatus :status="getExecuteStatus(step)" size="small" />
<template #content>
<responseResult
:active-tab="ResponseComposition.BODY"
:request-result="scenario.stepResponses?.[step.id]"
:console="scenario.stepResponses?.[step.id]?.console"
:show-empty="false"
:is-edit="false"
is-definition
>
<template #titleLeft>
<div class="flex items-center text-[14px]">
<div class="font-medium text-[var(--color-text-1)]">{{ t('apiScenario.response') }}</div>
<a-tooltip :content="step.name">
<div class="one-line-text">({{ step.name }})</div>
</a-tooltip>
</div>
</template>
</responseResult>
</template>
</a-popover>
<executeStatus v-else-if="step.executeStatus" :status="getExecuteStatus(step)" size="small" />
:step="step"
:step-responses="scenario.stepResponses"
@visible-change="handleResponsePopoverVisibleChange"
/>
<executeStatus
v-else-if="step.executeStatus"
:status="getExecuteStatus(step)"
size="small"
class="ml-[4px]"
/>
</template>
<template v-if="steps.length === 0 && stepKeyword.trim() !== ''" #empty>
<div
@ -422,6 +406,7 @@
handleTreeDragDrop,
insertNodes,
mapTree,
traverseTree,
TreeNode,
} from '@/utils';
@ -437,7 +422,6 @@
} from '@/models/apiTest/scenario';
import { EnvConfig } from '@/models/projectManagement/environmental';
import {
ResponseComposition,
ScenarioAddStepActionType,
ScenarioExecuteStatus,
ScenarioStepRefType,
@ -445,6 +429,7 @@
} from '@/enums/apiEnum';
import type { RequestParam } from '../common/customApiDrawer.vue';
import updateStepStatus from '../utils';
import useCreateActions from './createAction/useCreateActions';
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
import getStepType from '@/views/api-test/scenario/components/common/stepType/utils';
@ -456,9 +441,7 @@
const customCaseDrawer = defineAsyncComponent(() => import('../common/customCaseDrawer.vue'));
const importApiDrawer = defineAsyncComponent(() => import('../common/importApiDrawer/index.vue'));
const scriptOperationDrawer = defineAsyncComponent(() => import('../common/scriptOperationDrawer.vue'));
const responseResult = defineAsyncComponent(
() => import('@/views/api-test/components/requestComposition/response/index.vue')
);
const responsePopover = defineAsyncComponent(() => import('../common/responsePopover.vue'));
const props = defineProps<{
stepKeyword: string;
@ -500,9 +483,10 @@
function getExecuteStatus(step: ScenarioStepItem) {
if (scenario.value.stepResponses && scenario.value.stepResponses[step.id]) {
return scenario.value.stepResponses[step.id].isSuccessful
? ScenarioExecuteStatus.SUCCESS
: ScenarioExecuteStatus.FAILED;
//
return scenario.value.stepResponses[step.id].some((report) => !report.isSuccessful)
? ScenarioExecuteStatus.FAILED
: ScenarioExecuteStatus.SUCCESS;
}
return step.executeStatus;
}
@ -552,7 +536,7 @@
/**
* 增加步骤时判断父节点是否选中如果选中则需要把新节点也选中
*/
function checkedIfNeed(step: TreeNode<ScenarioStepItem>, parent?: TreeNode<ScenarioStepItem>) {
function selectedIfNeed(step: TreeNode<ScenarioStepItem>, parent?: TreeNode<ScenarioStepItem>) {
if (parent && selectedKeys.value.includes(parent.id)) {
//
selectedKeys.value.push(step.id);
@ -803,7 +787,7 @@
id,
},
'after',
checkedIfNeed,
selectedIfNeed,
'id'
);
scenario.value.unSaved = true;
@ -971,6 +955,15 @@
customApiDrawerVisible.value = true;
} else if (step.stepType === ScenarioStepType.API_CASE) {
activeStep.value = step;
if (
_stepType.isCopyCase &&
((stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
(stepDetails.value[step.id] === undefined && !step.isNew))
) {
// case
//
await getStepDetail(step);
}
customCaseDrawerVisible.value = true;
} else if (step.stepType === ScenarioStepType.SCRIPT) {
activeStep.value = step;
@ -998,6 +991,7 @@
scenario.value.stepResponses[report.stepId] = temporaryStepReportMap[key];
});
temporaryStepReportMap = {};
updateStepStatus(steps.value, scenario.value.stepResponses);
}
}
);
@ -1017,22 +1011,25 @@
if (step.reportId === data.reportId) {
// tabtab
data.taskResult.requestResults.forEach((result) => {
scenario.value.stepResponses[result.stepId] = {
if (scenario.value.stepResponses[result.stepId] === undefined) {
scenario.value.stepResponses[result.stepId] = [];
}
scenario.value.stepResponses[result.stepId].push({
...result,
console: data.taskResult.console,
};
});
});
} else {
// tab
data.taskResult.requestResults.forEach((result) => {
if (step.reportId) {
if (temporaryStepReportMap[step.reportId] === undefined) {
temporaryStepReportMap[step.reportId] = {};
temporaryStepReportMap[step.reportId] = [];
}
temporaryStepReportMap[step.reportId] = {
temporaryStepReportMap[step.reportId].push({
...result,
console: data.taskResult.console,
};
});
}
});
}
@ -1041,6 +1038,7 @@
websocketMap[reportId].close();
if (step.reportId === data.reportId) {
step.isExecuting = false;
updateStepStatus([step], scenario.value.stepResponses);
}
}
});
@ -1056,6 +1054,7 @@
const [currentStep] = executeParams.steps;
try {
currentStep.isExecuting = true;
currentStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
debugSocket(currentStep, executeParams.reportId, executeType); // websocket
const res = await debugScenario({
id: scenario.value.id || '',
@ -1093,21 +1092,18 @@
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, node.id, 'id');
if (realStep) {
realStep.reportId = getGenerateId();
if (
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(realStep.stepType)
) {
//
realStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
}
const stepDetail = stepDetails.value[realStep.id];
delete scenario.value.stepResponses[realStep.id]; //
const _stepDetails = {};
const stepFileParam = scenario.value.stepFileParam[realStep.id];
traverseTree(realStep, (step) => {
_stepDetails[step.id] = stepDetails.value[step.id];
step.executeStatus = ScenarioExecuteStatus.EXECUTING;
delete scenario.value.stepResponses[step.id]; //
return step;
});
realExecute(
{
steps: [realStep as ScenarioStepItem],
stepDetails: {
[realStep.id]: stepDetail,
},
stepDetails: _stepDetails,
reportId: realStep.reportId,
uploadFileIds: stepFileParam?.uploadFileIds || [],
linkFileIds: stepFileParam?.linkFileIds || [],
@ -1286,7 +1282,10 @@
{
stepType: ScenarioStepType.CUSTOM_REQUEST,
name: t('apiScenario.customApi'),
method: request.method,
config: {
protocol: request.protocol,
method: request.method,
},
id: request.stepId,
projectId: appStore.currentProjectId,
},
@ -1355,6 +1354,8 @@
if (activeStep.value && activeCreateAction.value) {
handleCreateStep(
{
id,
refType: ScenarioStepRefType.DIRECT,
stepType: ScenarioStepType.SCRIPT,
name,
projectId: appStore.currentProjectId,
@ -1532,27 +1533,6 @@
color: rgb(var(--primary-5));
background-color: rgb(var(--primary-1));
}
.scenario-step-response-popover {
width: 500px;
height: 450px;
.arco-popover-content {
@apply h-full;
.response {
.response-head {
background-color: var(--color-text-n9);
}
border: 1px solid var(--color-text-n8);
border-radius: var(--border-radius-small);
.arco-spin {
padding: 0;
.response-container {
padding: 0 16px 14px;
}
}
}
}
}
</style>
<style lang="less" scoped>

View File

@ -8,10 +8,11 @@ import { ScenarioExecuteStatus, ScenarioStepType } from '@/enums/apiEnum';
*/
export default function updateStepStatus(
steps: ScenarioStepItem[],
stepResponses: Record<string | number, RequestResult>
stepResponses: Record<string | number, RequestResult[]>
) {
for (let i = 0; i < steps.length; i++) {
const node = steps[i];
console.log('node', node.stepType, node.executeStatus);
if (
[
ScenarioStepType.LOOP_CONTROLLER,
@ -54,10 +55,11 @@ export default function updateStepStatus(
node.executeStatus = ScenarioExecuteStatus.SUCCESS;
} else if (node.executeStatus === ScenarioExecuteStatus.EXECUTING) {
// 非逻辑控制器直接更改本身状态
if (stepResponses[node.id]) {
node.executeStatus = stepResponses[node.id].isSuccessful
? ScenarioExecuteStatus.SUCCESS
: ScenarioExecuteStatus.FAILED;
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)
? ScenarioExecuteStatus.FAILED
: ScenarioExecuteStatus.SUCCESS;
} else {
node.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
}

View File

@ -161,13 +161,6 @@
function setStepExecuteStatus() {
updateStepStatus(activeScenarioTab.value.steps, activeScenarioTab.value.stepResponses);
// activeScenarioTab.value.steps = mapTree<ScenarioStepItem>(activeScenarioTab.value.steps, (step) => {
// if (step.executeStatus === ScenarioExecuteStatus.EXECUTING) {
// //
// step.executeStatus = ScenarioExecuteStatus.UN_EXECUTE;
// }
// return step;
// });
}
/**
@ -185,10 +178,13 @@
if (activeScenarioTab.value.reportId === data.reportId) {
// tabtab
data.taskResult.requestResults.forEach((result) => {
activeScenarioTab.value.stepResponses[result.stepId] = {
if (activeScenarioTab.value.stepResponses[result.stepId] === undefined) {
activeScenarioTab.value.stepResponses[result.stepId] = [];
}
activeScenarioTab.value.stepResponses[result.stepId].push({
...result,
console: data.taskResult.console,
};
});
if (result.isSuccessful) {
activeScenarioTab.value.executeSuccessCount += 1;
} else {
@ -202,10 +198,13 @@
if (temporaryScenarioReportMap[activeScenarioTab.value.reportId] === undefined) {
temporaryScenarioReportMap[activeScenarioTab.value.reportId] = {};
}
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId] = {
if (temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId]) {
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId] = [];
}
temporaryScenarioReportMap[activeScenarioTab.value.reportId][result.stepId].push({
...result,
console: data.taskResult.console,
};
});
}
});
}

View File

@ -60,6 +60,10 @@ export default {
'api_scenario.table.tableNoDataAndPlease': '暂无数据,请',
'api_scenario.table.or': '或',
'apiScenario.execute': '执行',
'apiScenario.prev': '上一次',
'apiScenario.next': '下一次',
'apiScenario.sumLoop': '共{count}次循环',
'apiScenario.times': '次',
// 批量操作文案
'api_scenario.batch_operation.success': '成功{opt}至 {name}',
'api_scenario.table.batchMoveConfirm': '{opt}{count}个场景至已选模块',