feat(接口场景): 场景导入场景&部分问题修复

This commit is contained in:
baiqi 2024-03-27 18:14:37 +08:00 committed by Craftsman
parent 42d41df3f1
commit dee6bc7b32
13 changed files with 291 additions and 109 deletions

View File

@ -18,6 +18,7 @@ import {
GetModuleTreeUrl, GetModuleTreeUrl,
GetScenarioStepUrl, GetScenarioStepUrl,
GetScenarioUrl, GetScenarioUrl,
GetSystemRequestUrl,
GetTrashModuleCountUrl, GetTrashModuleCountUrl,
GetTrashModuleTreeUrl, GetTrashModuleTreeUrl,
MoveModuleUrl, MoveModuleUrl,
@ -45,6 +46,7 @@ import {
ApiScenarioUpdateDTO, ApiScenarioUpdateDTO,
ExecuteHistoryItem, ExecuteHistoryItem,
ExecutePageParams, ExecutePageParams,
GetSystemRequestParams,
Scenario, Scenario,
ScenarioDetail, ScenarioDetail,
ScenarioHistoryItem, ScenarioHistoryItem,
@ -231,3 +233,8 @@ export function debugScenario(data: ApiScenarioDebugRequest) {
export function executeScenario(data: ApiScenarioDebugRequest) { export function executeScenario(data: ApiScenarioDebugRequest) {
return MSR.post({ url: ExecuteScenarioUrl, data }); return MSR.post({ url: ExecuteScenarioUrl, data });
} }
// 获取导入的系统请求数据
export function getSystemRequest(data: GetSystemRequestParams) {
return MSR.post<ApiScenarioTableItem[]>({ url: GetSystemRequestUrl, data });
}

View File

@ -15,6 +15,7 @@ export const ScenarioTransferFileUrl = '/api/scenario/transfer'; // 接口场景
export const ScenarioTransferModuleOptionsUrl = '/api/scenario/transfer/options'; // 接口场景临时文件转存目录 export const ScenarioTransferModuleOptionsUrl = '/api/scenario/transfer/options'; // 接口场景临时文件转存目录
export const DebugScenarioUrl = '/api/scenario/debug'; // 接口场景调试(不保存报告) export const DebugScenarioUrl = '/api/scenario/debug'; // 接口场景调试(不保存报告)
export const ExecuteScenarioUrl = '/api/scenario/run'; // 接口场景执行(保存报告) export const ExecuteScenarioUrl = '/api/scenario/run'; // 接口场景执行(保存报告)
export const GetSystemRequestUrl = '/api/scenario/get/system-request'; // 获取导入的系统请求数据
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景 export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景 export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景 export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景

View File

@ -407,3 +407,19 @@ export interface ApiScenarioUpdateDTO extends Partial<Scenario> {
deleteFileIds?: string[]; deleteFileIds?: string[];
unLinkFileIds?: string[]; unLinkFileIds?: string[];
} }
export interface GetSystemRequestTypeParams {
moduleIds?: (string | number)[];
selectedIds: (string | number)[];
unselectedIds: (string | number)[];
projectId: string;
protocol?: string;
versionId?: string;
}
export interface GetSystemRequestParams {
apiRequest?: GetSystemRequestTypeParams;
caseRequest?: GetSystemRequestTypeParams;
scenarioRequest?: GetSystemRequestTypeParams;
refType: ScenarioStepRefType.COPY | ScenarioStepRefType.REF;
}

View File

@ -235,7 +235,7 @@
if (matchesIterator) { if (matchesIterator) {
const matches = Array.from(matchesIterator); const matches = Array.from(matchesIterator);
try { try {
if (expressionForm.value.expressionMatchingRule === 'EXPRESSION') { if (expressionForm.value.expressionMatchingRule === RequestExtractExpressionRuleType.EXPRESSION) {
// //
matchResult.value = matches.map((e) => e[0]) || []; matchResult.value = matches.map((e) => e[0]) || [];
} else { } else {

View File

@ -380,6 +380,8 @@
title: 'apiTestManagement.paramName', title: 'apiTestManagement.paramName',
dataIndex: 'key', dataIndex: 'key',
inputType: 'text', inputType: 'text',
width: 250,
showTooltip: true,
}, },
{ {
title: 'apiTestManagement.paramVal', title: 'apiTestManagement.paramVal',

View File

@ -362,7 +362,7 @@
export type RequestParam = ExecuteApiRequestFullParams & { export type RequestParam = ExecuteApiRequestFullParams & {
response?: RequestTaskResult; response?: RequestTaskResult;
customizeRequestEnvEnable: boolean; customizeRequestEnvEnable?: boolean;
} & RequestCustomAttr; } & RequestCustomAttr;
const props = defineProps<{ const props = defineProps<{

View File

@ -1,8 +1,6 @@
<template> <template>
<MsDrawer <MsDrawer
v-model:visible="visible" v-model:visible="visible"
unmount-on-close
:mask="false"
:width="900" :width="900"
:footer="false" :footer="false"
show-full-screen show-full-screen
@ -88,17 +86,13 @@
import requestAndResponse from '@/views/api-test/components/requestAndResponse.vue'; import requestAndResponse from '@/views/api-test/components/requestAndResponse.vue';
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue'; import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
import { import {
debugCase,
getCaseDetail, getCaseDetail,
getTransferOptionsCase, getTransferOptionsCase,
runCase,
transferFileCase, transferFileCase,
uploadTempFileCase, uploadTempFileCase,
} from '@/api/modules/api-test/management'; } from '@/api/modules/api-test/management';
import { getSocket } from '@/api/modules/project-management/commonScript'; import { characterLimit } from '@/utils';
import { characterLimit, getGenerateId } from '@/utils';
import { RequestResult } from '@/models/apiTest/common'; import { RequestResult } from '@/models/apiTest/common';
import { ScenarioStepItem } from '@/models/apiTest/scenario'; import { ScenarioStepItem } from '@/models/apiTest/scenario';
@ -121,6 +115,8 @@
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'applyStep', request: RequestParam): void; (e: 'applyStep', request: RequestParam): void;
(e: 'deleteStep'): void; (e: 'deleteStep'): void;
(e: 'execute', request: RequestParam, executeType?: 'localExec' | 'serverExec'): void;
(e: 'stopDebug'): void;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
@ -208,11 +204,23 @@
() => () =>
activeStep.value?.stepType === ScenarioStepType.API_CASE && activeStep.value?.refType === ScenarioStepRefType.REF activeStep.value?.stepType === ScenarioStepType.API_CASE && activeStep.value?.refType === ScenarioStepRefType.REF
); );
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
const stepName = ref(activeStep.value?.name); const stepName = ref(activeStep.value?.name);
watchEffect(() => { watchEffect(() => {
stepName.value = activeStep.value?.name; stepName.value = activeStep.value?.name;
}); });
watch(
() => props.stepResponses,
(val) => {
if (val && val[requestVModel.value.stepId]) {
requestVModel.value.executeLoading = false;
}
},
{
deep: true,
}
);
const executeRef = ref<InstanceType<typeof executeButton>>(); const executeRef = ref<InstanceType<typeof executeButton>>();
const requestAndResponseRef = ref<InstanceType<typeof requestAndResponse>>(); const requestAndResponseRef = ref<InstanceType<typeof requestAndResponse>>();
@ -230,66 +238,31 @@
isShowEditStepNameInput.value = false; isShowEditStepNameInput.value = false;
} }
const reportId = ref(''); /**
const websocket = ref<WebSocket>(); * 执行调试
const temporaryResponseMap = {}; // websockettab * @param val 执行类型
// websocket */
function debugSocket(executeType?: 'localExec' | 'serverExec') {
websocket.value = getSocket(
reportId.value,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? executeRef.value?.localExecuteUrl : ''
);
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (requestVModel.value.reportId === data.reportId) {
// tabtab
requestVModel.value.response = data.taskResult; //
requestVModel.value.executeLoading = false;
} else {
// tab
temporaryResponseMap[data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
requestVModel.value.executeLoading = false;
}
});
}
async function handleExecute(executeType?: 'localExec' | 'serverExec') { async function handleExecute(executeType?: 'localExec' | 'serverExec') {
try {
requestVModel.value.executeLoading = true; requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse); if (isHttpProtocol.value) {
const makeRequestParams = requestAndResponseRef.value?.makeRequestParams(executeType); // reportIdreportId emit('execute', requestAndResponseRef.value?.makeRequestParams(executeType), executeType);
reportId.value = getGenerateId();
requestVModel.value.reportId = reportId.value; // ID
debugSocket(executeType); // websocket
let res;
const params = {
apiDefinitionId: requestVModel.value.apiDefinitionId,
...makeRequestParams,
reportId: reportId.value,
};
if (!(requestVModel.value.resourceId as string).startsWith('c') && executeType === 'serverExec') {
//
res = await runCase(params);
} else { } else {
res = await debugCase(params); //
} // fApi.value?.validate(async (valid) => {
if (executeType === 'localExec') { // if (valid === true) {
await localExecuteApiDebug(executeRef.value?.localExecuteUrl ?? '', res); // emit('execute', requestAndResponseRef.value?.makeRequestParams(executeType), executeType);
} // } else {
} catch (error) { // requestVModel.value.activeTab = RequestComposition.PLUGIN;
// eslint-disable-next-line no-console // nextTick(() => {
console.log(error); // scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
requestVModel.value.executeLoading = false; // });
// }
// });
} }
} }
function stopDebug() { function stopDebug() {
websocket.value?.close(); emit('stopDebug');
requestVModel.value.executeLoading = false;
} }
function handleClose() { function handleClose() {

View File

@ -19,6 +19,7 @@
<div class="mb-[12px] flex items-center gap-[8px]"> <div class="mb-[12px] flex items-center gap-[8px]">
<MsProjectSelect v-model:project="currentProject" @change="resetModule" /> <MsProjectSelect v-model:project="currentProject" @change="resetModule" />
<a-select <a-select
v-if="activeKey !== 'scenario'"
v-model:model-value="protocol" v-model:model-value="protocol"
:options="protocolOptions" :options="protocolOptions"
class="w-[90px]" class="w-[90px]"
@ -70,11 +71,11 @@
</MsButton> </MsButton>
</div> </div>
<div class="flex items-center gap-[12px]"> <div class="flex items-center gap-[12px]">
<a-button type="secondary" @click="handleCancel">{{ t('common.cancel') }}</a-button> <a-button type="secondary" :disabled="loading" @click="handleCancel">{{ t('common.cancel') }}</a-button>
<a-button type="primary" :disabled="totalSelected === 0" @click="handleCopy"> <a-button type="primary" :loading="loading" :disabled="totalSelected === 0" @click="handleCopy">
{{ t('common.copy') }} {{ t('common.copy') }}
</a-button> </a-button>
<a-button type="primary" :disabled="totalSelected === 0" @click="handleQuote"> <a-button type="primary" :loading="loading" :disabled="totalSelected === 0" @click="handleQuote">
{{ t('common.quote') }} {{ t('common.quote') }}
</a-button> </a-button>
</div> </div>
@ -96,11 +97,13 @@
import apiTable from './table.vue'; import apiTable from './table.vue';
import { getProtocolList } from '@/api/modules/api-test/common'; import { getProtocolList } from '@/api/modules/api-test/common';
import { getSystemRequest } from '@/api/modules/api-test/scenario';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management'; import type { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management';
import { ApiScenarioTableItem } from '@/models/apiTest/scenario'; import type { ApiScenarioTableItem } from '@/models/apiTest/scenario';
import { ScenarioStepRefType } from '@/enums/apiEnum';
export interface ImportData { export interface ImportData {
api: MsTableDataItem<ApiDefinitionDetail>[]; api: MsTableDataItem<ApiDefinitionDetail>[];
@ -121,6 +124,7 @@
}); });
const activeKey = ref<'api' | 'case' | 'scenario'>('api'); const activeKey = ref<'api' | 'case' | 'scenario'>('api');
const loading = ref(false);
const selectedApis = ref<MsTableDataItem<ApiDefinitionDetail>[]>([]); const selectedApis = ref<MsTableDataItem<ApiDefinitionDetail>[]>([]);
const selectedCases = ref<MsTableDataItem<ApiCaseDetail>[]>([]); const selectedCases = ref<MsTableDataItem<ApiCaseDetail>[]>([]);
const selectedScenarios = ref<MsTableDataItem<ApiScenarioTableItem>[]>([]); const selectedScenarios = ref<MsTableDataItem<ApiScenarioTableItem>[]>([]);
@ -167,7 +171,9 @@
const moduleIds = ref<(string | number)[]>([]); const moduleIds = ref<(string | number)[]>([]);
function resetModule() { function resetModule() {
nextTick(() => {
moduleTreeRef.value?.init(activeKey.value); moduleTreeRef.value?.init(activeKey.value);
});
} }
function handleModuleSelect(ids: (string | number)[], node: MsTreeNodeData) { function handleModuleSelect(ids: (string | number)[], node: MsTreeNodeData) {
@ -187,7 +193,74 @@
visible.value = false; visible.value = false;
} }
function handleCopy() { async function getScenarioSteps(refType: ScenarioStepRefType.COPY | ScenarioStepRefType.REF) {
const scenarioMap: Record<string, MsTableDataItem<ApiScenarioTableItem>[]> = {};
selectedScenarios.value.forEach((e) => {
if (!scenarioMap[e.projectId]) {
scenarioMap[e.projectId] = [];
}
scenarioMap[e.projectId].push(e);
});
const scenarioRequestArr: any[] = [];
Object.keys(scenarioMap).forEach((projectId) => {
scenarioRequestArr.push(
getSystemRequest({
scenarioRequest: {
projectId,
unselectedIds: [],
selectedIds: scenarioMap[projectId].map((e) => e.id),
},
refType,
})
);
});
try {
loading.value = true;
const allRes = await Promise.all(scenarioRequestArr);
let fullScenarioArr: MsTableDataItem<ApiScenarioTableItem>[] = [];
allRes.forEach((res) => {
fullScenarioArr.push(...res);
});
if (refType === ScenarioStepRefType.COPY) {
fullScenarioArr = fullScenarioArr.map((e) => {
return {
...e,
name: `copy-${e.name}`,
copyFromStepId: e.id,
};
});
emit(
'copy',
cloneDeep({
api: selectedApis.value,
case: selectedCases.value,
scenario: fullScenarioArr,
})
);
handleCancel();
} else {
emit(
'quote',
cloneDeep({
api: selectedApis.value,
case: selectedCases.value,
scenario: fullScenarioArr,
})
);
handleCancel();
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
async function handleCopy() {
if (selectedScenarios.value.length > 0) {
await getScenarioSteps(ScenarioStepRefType.COPY);
} else {
emit( emit(
'copy', 'copy',
cloneDeep({ cloneDeep({
@ -198,8 +271,12 @@
); );
handleCancel(); handleCancel();
} }
}
function handleQuote() { async function handleQuote() {
if (selectedScenarios.value.length > 0) {
await getScenarioSteps(ScenarioStepRefType.REF);
} else {
emit( emit(
'quote', 'quote',
cloneDeep({ cloneDeep({
@ -210,6 +287,7 @@
); );
handleCancel(); handleCancel();
} }
}
onBeforeMount(() => { onBeforeMount(() => {
initProtocolList(); initProtocolList();

View File

@ -191,7 +191,72 @@
// //
const useCaseTable = useTable(getCasePage, tableConfig); const useCaseTable = useTable(getCasePage, tableConfig);
// //
const useScenarioTable = useTable(getScenarioPage, tableConfig); const useScenarioTable = useTable(getScenarioPage, {
...tableConfig,
columns: [
{
title: 'ID',
dataIndex: 'num',
slotName: 'num',
sortIndex: 1,
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
fixed: 'left',
width: 100,
showTooltip: true,
columnSelectorDisabled: true,
},
{
title: 'apiScenario.table.columns.name',
dataIndex: 'name',
sortable: {
sortDirections: ['ascend', 'descend'],
sorter: true,
},
width: 134,
showTooltip: true,
columnSelectorDisabled: true,
},
{
title: 'apiScenario.table.columns.level',
dataIndex: 'priority',
slotName: 'priority',
width: 100,
},
{
title: 'apiScenario.table.columns.status',
dataIndex: 'status',
slotName: 'status',
titleSlotName: 'statusFilter',
width: 140,
},
{
title: 'apiScenario.table.columns.tags',
dataIndex: 'tags',
isTag: true,
isStringTag: true,
width: 240,
},
{
title: 'apiScenario.table.columns.scenarioEnv',
dataIndex: 'environmentName',
width: 159,
},
{
title: 'apiScenario.table.columns.steps',
dataIndex: 'stepTotal',
width: 100,
},
{
title: 'apiScenario.table.columns.module',
dataIndex: 'modulePath',
width: 120,
showTooltip: true,
},
],
});
const methodFilterVisible = ref(false); const methodFilterVisible = ref(false);
const methodFilters = ref(Object.keys(RequestMethods)); const methodFilters = ref(Object.keys(RequestMethods));
@ -324,6 +389,7 @@
case 'scenario': case 'scenario':
default: default:
routeName = ApiTestRouteEnum.API_TEST_SCENARIO; routeName = ApiTestRouteEnum.API_TEST_SCENARIO;
query.sId = id;
break; break;
} }
openNewPage(routeName, query); openNewPage(routeName, query);

View File

@ -133,8 +133,10 @@ export default function useCreateActions() {
...defaultStepItemCommon.config, ...defaultStepItemCommon.config,
...config, ...config,
}, },
children: item.children || [],
stepType, stepType,
refType, refType,
copyFromStepId: item.copyFromStepId,
...resourceField, ...resourceField,
name: name || item.name, name: name || item.name,
sort: startOrder + index, sort: startOrder + index,

View File

@ -127,7 +127,7 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useOpenNewPage from '@/hooks/useOpenNewPage'; import useOpenNewPage from '@/hooks/useOpenNewPage';
import { deleteNodes, filterTree, getGenerateId } from '@/utils'; import { deleteNodes, filterTree, getGenerateId, mapTree } from '@/utils';
import { countNodes } from '@/utils/tree'; import { countNodes } from '@/utils/tree';
import { ApiScenarioDebugRequest, Scenario } from '@/models/apiTest/scenario'; import { ApiScenarioDebugRequest, Scenario } from '@/models/apiTest/scenario';
@ -207,16 +207,22 @@
async function handleBeforeBatchToggle(done: (closed: boolean) => void) { async function handleBeforeBatchToggle(done: (closed: boolean) => void) {
try { try {
let ids = checkedKeys.value; const ids = new Set(checkedKeys.value);
if (batchToggleRange.value === 'top') { if (batchToggleRange.value === 'top') {
ids = scenario.value.steps.map((item) => item.id); scenario.value.steps = scenario.value.steps.map((item) => {
if (ids.has(item.id)) {
item.enable = isBatchEnable.value;
} }
console.log('ids', ids); return item;
await new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 1000);
}); });
} else {
scenario.value.steps = mapTree(scenario.value.steps, (node) => {
if (ids.has(node.id)) {
node.enable = isBatchEnable.value;
}
return node;
});
}
done(true); done(true);
Message.success(isBatchEnable.value ? t('common.enableSuccess') : t('common.disableSuccess')); Message.success(isBatchEnable.value ? t('common.enableSuccess') : t('common.disableSuccess'));
} catch (error) { } catch (error) {
@ -228,6 +234,10 @@
function batchDelete() { function batchDelete() {
deleteNodes(scenario.value.steps, checkedKeys.value, 'id'); deleteNodes(scenario.value.steps, checkedKeys.value, 'id');
Message.success(t('common.deleteSuccess')); Message.success(t('common.deleteSuccess'));
if (scenario.value.steps.length === 0) {
checkedAll.value = false;
indeterminate.value = false;
}
} }
function checkReport() { function checkReport() {

View File

@ -60,7 +60,7 @@
<div class="mr-[8px] flex items-center gap-[8px]"> <div class="mr-[8px] flex items-center gap-[8px]">
<!-- 步骤启用/禁用 --> <!-- 步骤启用/禁用 -->
<a-switch <a-switch
:default-checked="step.enable" v-model:model-value="step.enable"
size="small" size="small"
@click.stop="handleStepToggleEnable(step)" @click.stop="handleStepToggleEnable(step)"
></a-switch> ></a-switch>
@ -242,6 +242,8 @@
:step-responses="scenario.stepResponses" :step-responses="scenario.stepResponses"
@apply-step="applyApiStep" @apply-step="applyApiStep"
@delete-step="deleteCaseStep" @delete-step="deleteCaseStep"
@stop-debug="handleStopExecute(activeStep)"
@execute="(request, executeType) => handleApiExecute((request as unknown as RequestParam), executeType)"
/> />
<importApiDrawer <importApiDrawer
v-if="importApiDrawerVisible" v-if="importApiDrawerVisible"
@ -519,6 +521,7 @@
case 'copy': case 'copy':
const id = getGenerateId(); const id = getGenerateId();
const stepDetail = stepDetails.value[node.id]; const stepDetail = stepDetails.value[node.id];
const { isQuoteScenario } = getStepType(node as ScenarioStepItem);
if (stepDetail) { if (stepDetail) {
// //
stepDetails.value[id] = cloneDeep(stepDetail); stepDetails.value[id] = cloneDeep(stepDetail);
@ -531,13 +534,25 @@
mapTree<ScenarioStepItem>(node, (childNode) => { mapTree<ScenarioStepItem>(node, (childNode) => {
const childId = getGenerateId(); const childId = getGenerateId();
const childStepDetail = stepDetails.value[node.id]; const childStepDetail = stepDetails.value[node.id];
let childCopyFromStepId = childNode.id;
if (childStepDetail) { if (childStepDetail) {
// //
stepDetails.value[childId] = cloneDeep(childStepDetail); stepDetails.value[childId] = cloneDeep(childStepDetail);
} }
if (!isQuoteScenario) {
// id
if (childStepDetail || (childNode.isNew && childNode.stepRefType === ScenarioStepRefType.REF)) {
// id
// id
childCopyFromStepId = childNode.id;
} else if (childNode.isNew && childNode.stepRefType === ScenarioStepRefType.COPY) {
// id
childCopyFromStepId = childNode.copyFromStepId;
}
}
return { return {
...cloneDeep(childNode), ...cloneDeep(childNode),
copyFromStepId: childNode.id, copyFromStepId: childCopyFromStepId,
id: childId, id: childId,
}; };
})[0] })[0]
@ -545,7 +560,7 @@
name: `copy-${node.name}`, name: `copy-${node.name}`,
copyFromStepId: node.id, copyFromStepId: node.id,
sort: node.sort + 1, sort: node.sort + 1,
isNew: false, isNew: true,
id, id,
}, },
'after', 'after',
@ -819,7 +834,12 @@
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, node.id, 'id'); const realStep = findNodeByKey<ScenarioStepItem>(steps.value, node.id, 'id');
if (realStep) { if (realStep) {
realStep.reportId = getGenerateId(); realStep.reportId = getGenerateId();
if (
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.CUSTOM_REQUEST].includes(realStep.stepType)
) {
//
realStep.executeStatus = ScenarioExecuteStatus.EXECUTING; realStep.executeStatus = ScenarioExecuteStatus.EXECUTING;
}
const stepDetail = stepDetails.value[realStep.id]; const stepDetail = stepDetails.value[realStep.id];
delete scenario.value.stepResponses[realStep.id]; // delete scenario.value.stepResponses[realStep.id]; //
realExecute( realExecute(

View File

@ -84,6 +84,7 @@
* @description 接口测试-接口场景主页 * @description 接口测试-接口场景主页
*/ */
import { useRoute } from 'vue-router';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -132,6 +133,7 @@
export type ScenarioParams = Scenario & TabItem; export type ScenarioParams = Scenario & TabItem;
const route = useRoute();
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
@ -211,8 +213,6 @@
}); });
} }
onBeforeMount(selectRecycleCount);
const createRef = ref<InstanceType<typeof create>>(); const createRef = ref<InstanceType<typeof create>>();
const saveLoading = ref(false); const saveLoading = ref(false);
@ -278,10 +278,10 @@
} }
} }
async function openScenarioTab(record: ApiScenarioTableItem, isCopy?: boolean) { async function openScenarioTab(record: ApiScenarioTableItem | string, isCopy?: boolean) {
try { try {
appStore.showLoading(); appStore.showLoading();
const res = await getScenarioDetail(record.id); const res = await getScenarioDetail(typeof record === 'string' ? record : record.id);
res.stepDetails = {}; res.stepDetails = {};
if (!res.steps) { if (!res.steps) {
res.steps = []; res.steps = [];
@ -297,6 +297,13 @@
} }
} }
onBeforeMount(() => {
selectRecycleCount();
if (route.query.sId) {
openScenarioTab(route.query.sId as string);
}
});
const websocket = ref<WebSocket>(); const websocket = ref<WebSocket>();
const temporaryScenarioReportMap = {}; // websockettab const temporaryScenarioReportMap = {}; // websockettab