feat(接口场景): 场景执行初步&插件问题修复

This commit is contained in:
baiqi 2024-03-25 18:02:26 +08:00 committed by Craftsman
parent a9396da1b6
commit 88756c8685
15 changed files with 260 additions and 61 deletions

View File

@ -9,6 +9,7 @@ import {
BatchRecoverScenarioUrl,
BatchRecycleScenarioUrl,
BatchRunScenarioUrl,
DebugScenarioUrl,
DeleteModuleUrl,
DeleteScenarioUrl,
ExecuteHistoryUrl,
@ -23,7 +24,10 @@ import {
RecycleScenarioUrl,
ScenarioHistoryUrl,
ScenarioPageUrl,
ScenarioTransferFileUrl,
ScenarioTransferModuleOptionsUrl,
ScenarioTrashPageUrl,
ScenarioUploadTempFileUrl,
UpdateModuleUrl,
UpdateScenarioUrl,
} from '@/api/requrls/api-test/scenario';
@ -32,6 +36,7 @@ import {
ApiScenarioBatchDeleteParams,
ApiScenarioBatchEditParams,
ApiScenarioBatchRunParams,
ApiScenarioDebugRequest,
ApiScenarioGetModuleParams,
ApiScenarioModuleUpdateParams,
ApiScenarioPageParams,
@ -44,7 +49,7 @@ import {
ScenarioHistoryItem,
ScenarioHistoryPageParams,
} from '@/models/apiTest/scenario';
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules } from '@/models/common';
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules, TransferFileParams } from '@/models/common';
// 更新模块
export function updateModule(data: ApiScenarioModuleUpdateParams) {
@ -200,3 +205,23 @@ export function getScenarioDetail(id: string) {
export function getScenarioStep(stepId: string | number) {
return MSR.get({ url: GetScenarioStepUrl, params: stepId });
}
// 文件转存
export function transferFile(data: TransferFileParams) {
return MSR.post({ url: ScenarioTransferFileUrl, data });
}
// 文件转存目录
export function getTransferOptions(projectId: string) {
return MSR.get<ModuleTreeNode[]>({ url: ScenarioTransferModuleOptionsUrl, params: projectId });
}
// 上传文件
export function uploadTempFile(file: File) {
return MSR.uploadFile({ url: ScenarioUploadTempFileUrl }, { fileList: [file] }, 'file');
}
// 场景调试
export function debugScenario(data: ApiScenarioDebugRequest) {
return MSR.post({ url: DebugScenarioUrl, data });
}

View File

@ -10,6 +10,10 @@ export const GetScenarioUrl = '/api/scenario/get'; // 获取接口场景详情
export const GetScenarioStepUrl = '/api/scenario/step/get'; // 获取接口场景步骤详情
export const UpdateScenarioUrl = '/api/scenario/update'; // 更新接口场景
export const RecycleScenarioUrl = '/api/scenario/delete-to-gc'; // 删除接口场景
export const ScenarioUploadTempFileUrl = '/api/scenario/upload/temp/file'; // 接口场景上传临时文件
export const ScenarioTransferFileUrl = '/api/scenario/transfer'; // 接口场景临时文件转存
export const ScenarioTransferModuleOptionsUrl = '/api/scenario/transfer/options'; // 接口场景临时文件转存目录
export const DebugScenarioUrl = '/api/scenario/debug'; // 接口场景调试
export const BatchRecycleScenarioUrl = '/api/scenario/batch-operation/delete-gc'; // 批量删除接口场景
export const BatchMoveScenarioUrl = '/api/scenario/batch-operation/move'; // 批量移动接口场景
export const BatchCopyScenarioUrl = '/api/scenario/batch-operation/copy'; // 批量复制接口场景

View File

@ -368,6 +368,7 @@ export interface Scenario {
executeTime?: string | number; // 执行时间
executeSuccessCount?: number; // 执行成功数量
executeFailCount?: number; // 执行失败数量
reportId?: string;
}
export interface ScenarioDetail extends Scenario {
stepTotal: number;
@ -384,3 +385,17 @@ export interface ScenarioDetail extends Scenario {
updateTime: number;
updateUser: string;
}
export interface ApiScenarioDebugRequest {
id: string | number; // 场景 id
grouped: boolean;
environmentId: string;
scenarioConfig: ScenarioConfig;
stepDetails: Record<string, ScenarioStepDetail>;
reportId?: string | number;
steps: ScenarioStepItem[];
projectId: string;
uploadFileIds: string[];
linkFileIds: string[];
frontendDebug?: boolean;
}

View File

@ -31,14 +31,19 @@
const { openNewPage } = useOpenNewPage();
const currentEnv = defineModel<string>('currentEnv', { default: '' });
const currentEnvConfig = ref<EnvConfig>();
const currentEnvConfig = defineModel<EnvConfig>('currentEnvConfig', {
default: {},
});
const envLoading = ref(false);
const envOptions = ref<SelectOptionData[]>([]);
async function initEnvironment() {
try {
currentEnvConfig.value = await getEnvironment(currentEnv.value);
currentEnvConfig.value.id = currentEnv.value;
const res = await getEnvironment(currentEnv.value);
currentEnvConfig.value = {
...res,
id: currentEnv.value,
};
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);

View File

@ -45,7 +45,7 @@
}>();
const emit = defineEmits<{
(e: 'execute', executeType?: 'localExec' | 'serverExec'): void;
(e: 'execute', executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string): void;
(e: 'stopDebug'): void;
}>();
@ -94,7 +94,7 @@
async function execute(executeType?: 'localExec' | 'serverExec') {
if (!caseDetail.value || props.isEmit) {
emit('execute', executeType);
emit('execute', executeType, localExecuteUrl.value);
return;
}
try {

View File

@ -188,10 +188,10 @@
:layout="activeLayout"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
:upload-temp-file-api="props.uploadTempFileApi"
:file-save-as-source-id="props.fileSaveAsSourceId"
:file-save-as-api="props.fileSaveAsApi"
:file-module-options-api="props.fileModuleOptionsApi"
:upload-temp-file-api="uploadTempFile"
:file-save-as-source-id="scenarioId"
:file-save-as-api="transferFile"
:file-module-options-api="getTransferOptions"
@change="handleActiveDebugChange"
/>
<httpQuery
@ -213,7 +213,7 @@
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
:is-definition="false"
is-definition
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
@ -224,13 +224,13 @@
:layout="activeLayout"
:disabled="!isEditableApi"
:second-box-height="secondBoxHeight"
:is-definition="false"
is-definition
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:is-definition="false"
is-definition
:disabled="!isEditableApi"
:assertion-config="requestVModel.children[0].assertionConfig"
/>
@ -262,7 +262,7 @@
:is-expanded="isVerticalExpanded"
:request-task-result="requestVModel.response"
:is-edit="false"
:upload-temp-file-api="props.uploadTempFileApi"
:upload-temp-file-api="uploadTempFile"
:loading="requestVModel.executeLoading || loading"
:is-definition="false"
@change-expand="changeVerticalExpand"
@ -299,6 +299,7 @@
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
import { getDefinitionDetail } from '@/api/modules/api-test/management';
import { getTransferOptions, transferFile, uploadTempFile } from '@/api/modules/api-test/scenario';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
@ -313,7 +314,6 @@
RequestTaskResult,
} from '@/models/apiTest/common';
import { ScenarioStepItem } from '@/models/apiTest/scenario';
import { ModuleTreeNode, TransferFileParams } from '@/models/common';
import {
RequestAuthType,
RequestBodyFormat,
@ -378,10 +378,6 @@
};
executeApi?: (params: ExecuteRequestParams) => Promise<any>; //
localExecuteApi?: (url: string, params: ExecuteRequestParams) => Promise<any>; //
uploadTempFileApi?: (...args) => Promise<any>; //
fileSaveAsSourceId?: string | number; // id
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; //
fileModuleOptionsApi?: (projectId: string) => Promise<ModuleTreeNode[]>; //
permissionMap?: {
execute: string;
create: string;
@ -397,6 +393,9 @@
const appStore = useAppStore();
const { t } = useI18n();
//
const scenarioId = inject<string | number>('scenarioId');
const visible = defineModel<boolean>('visible', { required: true });
const loading = defineModel<boolean>('detailLoading', { default: false });
@ -666,7 +665,6 @@
*/
function setPluginFormData() {
const tempForm = temporaryPluginFormMap[requestVModel.value.stepId];
console.log('setPluginFormData', temporaryPluginFormMap, requestVModel.value.stepId);
if (tempForm || !requestVModel.value.isNew) {
//
const formData = isEditableApi.value ? tempForm || requestVModel.value : requestVModel.value;
@ -696,17 +694,9 @@
}
// 使
async function handleUseEnvChange() {
function handleUseEnvChange() {
if (!isHttpProtocol.value) {
const pluginId = protocolOptions.value.find((e) => e.value === requestVModel.value.protocol)?.pluginId;
const res = await getPluginScript(pluginId);
pluginScriptMap.value[requestVModel.value.protocol] = res;
fApi.value?.nextTick(() => {
controlPluginFormFields();
});
nextTick(() => {
fApi.value?.resetFields();
});
controlPluginFormFields();
}
}
@ -1055,7 +1045,6 @@
resourceId: res.id,
...parseRequestBodyResult,
};
console.log('initQuoteApiDetail', requestVModel.value);
nextTick(() => {
// loading
loading.value = false;

View File

@ -45,6 +45,11 @@ export const defaultConditionController = {
condition: RequestAssertionCondition.EQUALS, // 条件操作符
};
// 条件控制器
export const defaultTimeController = {
delay: 0, // 等待时间
};
export const defaultStepItemCommon = {
checked: false,
expanded: false,
@ -56,7 +61,6 @@ export const defaultStepItemCommon = {
id: '',
name: '',
enable: true,
waitTime: 0, // 等待时间
},
createActionsVisible: false,
};

View File

@ -48,7 +48,7 @@
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { findNodeByKey, getGenerateId } from '@/utils';
import { findNodeByKey } from '@/utils';
import { CreateStepAction, ScenarioStepItem } from '@/models/apiTest/scenario';
import { ScenarioAddStepActionType, ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';

View File

@ -6,7 +6,12 @@ import { getGenerateId, insertNodes, TreeNode } from '@/utils';
import { CreateStepAction, ScenarioStepItem } from '@/models/apiTest/scenario';
import { ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
import { defaultConditionController, defaultLoopController, defaultStepItemCommon } from '../../config';
import {
defaultConditionController,
defaultLoopController,
defaultStepItemCommon,
defaultTimeController,
} from '../../config';
export default function useCreateActions() {
const { t } = useI18n();
@ -102,6 +107,8 @@ export default function useCreateActions() {
config = cloneDeep(defaultLoopController);
} else if (stepType === ScenarioStepType.IF_CONTROLLER) {
config = cloneDeep(defaultConditionController);
} else if (stepType === ScenarioStepType.CONSTANT_TIMER) {
config = cloneDeep(defaultTimeController);
}
if (item.id) {
// 引用复制接口、用例、场景时的源资源信息

View File

@ -1,5 +1,5 @@
<template>
<div class="flex h-full flex-col gap-[16px]">
<div class="flex h-full flex-col gap-[8px]">
<div class="action-line">
<div class="action-group">
<a-checkbox
@ -79,7 +79,7 @@
</a-button>
</div>
</div>
<div class="h-[calc(100%-48px)]">
<div class="h-[calc(100%-30px)]">
<stepTree
ref="stepTreeRef"
v-model:steps="scenario.steps"
@ -224,6 +224,16 @@
function refreshStepInfo() {
console.log('刷新步骤信息');
}
async function executeScenario() {
try {
scenario.value.executeLoading = true;
} catch (error) {
console.log(error);
} finally {
scenario.value.executeLoading = false;
}
}
</script>
<style lang="less">

View File

@ -1,8 +1,8 @@
<template>
<div class="flex items-center gap-[4px]" draggable="false">
<a-tooltip :content="innerData.waitTime.toString()" :disabled="!innerData.waitTime">
<a-tooltip :content="innerData.delay.toString()" :disabled="!innerData.delay">
<a-input-number
v-model:model-value="innerData.waitTime"
v-model:model-value="innerData.delay"
class="max-w-[500px] px-[8px]"
size="mini"
:step="1"
@ -25,7 +25,7 @@
export interface WaitTimeContentProps {
id: string | number;
waitTime: number;
delay: number;
}
const props = defineProps<{

View File

@ -1,5 +1,5 @@
<template>
<div class="flex h-full flex-col gap-[16px]">
<div class="flex h-full flex-col gap-[8px]">
<a-spin class="max-h-[calc(100%-46px)] w-full" :loading="loading">
<MsTree
ref="treeRef"

View File

@ -2,29 +2,49 @@
<MsSplitBox ref="splitBoxRef" :size="0.7" :max="0.9" :min="0.7" direction="horizontal" expand-direction="right">
<template #first>
<a-tabs v-model:active-key="activeKey" class="h-full" animation lazy-load>
<a-tab-pane :key="ScenarioCreateComposition.STEP" :title="t('apiScenario.step')" class="p-[16px]">
<a-tab-pane
:key="ScenarioCreateComposition.STEP"
:title="t('apiScenario.step')"
class="scenario-create-tab-pane"
>
<step v-if="activeKey === ScenarioCreateComposition.STEP" v-model:scenario="scenario" is-new />
</a-tab-pane>
<a-tab-pane :key="ScenarioCreateComposition.PARAMS" :title="t('apiScenario.params')" class="p-[16px]">
<a-tab-pane
:key="ScenarioCreateComposition.PARAMS"
:title="t('apiScenario.params')"
class="scenario-create-tab-pane"
>
<params
v-if="activeKey === ScenarioCreateComposition.PARAMS"
v-model:params="scenario.scenarioConfig.variable.commonVariables"
/>
</a-tab-pane>
<a-tab-pane :key="ScenarioCreateComposition.PRE_POST" :title="t('apiScenario.prePost')" class="p-[16px]">
<a-tab-pane
:key="ScenarioCreateComposition.PRE_POST"
:title="t('apiScenario.prePost')"
class="scenario-create-tab-pane"
>
<prePost
v-if="activeKey === ScenarioCreateComposition.PRE_POST"
v-model:post-processor-config="scenario.scenarioConfig.postProcessorConfig"
v-model:pre-processor-config="scenario.scenarioConfig.preProcessorConfig"
/>
</a-tab-pane>
<a-tab-pane :key="ScenarioCreateComposition.ASSERTION" :title="t('apiScenario.assertion')" class="p-[16px]">
<a-tab-pane
:key="ScenarioCreateComposition.ASSERTION"
:title="t('apiScenario.assertion')"
class="scenario-create-tab-pane"
>
<assertion
v-if="activeKey === ScenarioCreateComposition.ASSERTION"
v-model:assertion-config="scenario.scenarioConfig.assertionConfig"
/>
</a-tab-pane>
<a-tab-pane :key="ScenarioCreateComposition.SETTING" :title="t('common.setting')" class="p-[16px]">
<a-tab-pane
:key="ScenarioCreateComposition.SETTING"
:title="t('common.setting')"
class="scenario-create-tab-pane"
>
<setting
v-if="activeKey === ScenarioCreateComposition.SETTING"
v-model:other-config="scenario.scenarioConfig.otherConfig"
@ -207,5 +227,8 @@
@apply h-full;
}
}
.scenario-create-tab-pane {
padding: 8px 16px;
}
}
</style>

View File

@ -34,22 +34,26 @@
</template>
</MsDetailCard>
</div>
<div class="h-[calc(100%-124px)]">
<div class="h-[calc(100%-104px)]">
<a-tabs v-model:active-key="activeKey" class="h-full" animation lazy-load>
<a-tab-pane
:key="ScenarioDetailComposition.BASE_INFO"
:title="t('apiScenario.baseInfo')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<baseInfo :scenario="scenario as ScenarioDetail" />
</a-tab-pane>
<a-tab-pane :key="ScenarioDetailComposition.STEP" :title="t('apiScenario.step')" class="px-[24px] py-[16px]">
<a-tab-pane
:key="ScenarioDetailComposition.STEP"
:title="t('apiScenario.step')"
class="scenario-detail-tab-pane"
>
<step v-if="activeKey === ScenarioDetailComposition.STEP" v-model:scenario="scenario" />
</a-tab-pane>
<a-tab-pane
:key="ScenarioDetailComposition.PARAMS"
:title="t('apiScenario.params')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<params
v-if="activeKey === ScenarioDetailComposition.PARAMS"
@ -59,7 +63,7 @@
<a-tab-pane
:key="ScenarioDetailComposition.PRE_POST"
:title="t('apiScenario.prePost')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<prePost
v-if="activeKey === ScenarioDetailComposition.PRE_POST"
@ -70,7 +74,7 @@
<a-tab-pane
:key="ScenarioDetailComposition.ASSERTION"
:title="t('apiScenario.assertion')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<assertion
v-if="activeKey === ScenarioDetailComposition.ASSERTION"
@ -80,28 +84,32 @@
<a-tab-pane
:key="ScenarioDetailComposition.EXECUTE_HISTORY"
:title="t('apiScenario.executeHistory')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<executeHistory v-if="activeKey === ScenarioDetailComposition.EXECUTE_HISTORY" :scenario-id="scenario.id" />
</a-tab-pane>
<a-tab-pane
:key="ScenarioDetailComposition.CHANGE_HISTORY"
:title="t('apiScenario.changeHistory')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<changeHistory v-if="activeKey === ScenarioDetailComposition.CHANGE_HISTORY" :source-id="scenario.id" />
</a-tab-pane>
<!-- <a-tab-pane
:key="ScenarioDetailComposition.DEPENDENCY"
:title="t('apiScenario.dependency')"
class="px-[24px] py-[16px]"
class="scenario-detail-tab-pane"
>
<dependency v-if="activeKey === ScenarioDetailComposition.DEPENDENCY" />
</a-tab-pane>
<a-tab-pane :key="ScenarioDetailComposition.QUOTE" :title="t('apiScenario.quote')" class="px-[24px] py-[16px]">
<a-tab-pane :key="ScenarioDetailComposition.QUOTE" :title="t('apiScenario.quote')" class="scenario-detail-tab-pane">
<quote v-if="activeKey === ScenarioDetailComposition.QUOTE" />
</a-tab-pane> -->
<a-tab-pane :key="ScenarioDetailComposition.SETTING" :title="t('common.setting')" class="px-[24px] py-[16px]">
<a-tab-pane
:key="ScenarioDetailComposition.SETTING"
:title="t('common.setting')"
class="scenario-detail-tab-pane"
>
<setting
v-if="activeKey === ScenarioDetailComposition.SETTING"
v-model:other-config="scenario.scenarioConfig.otherConfig"
@ -197,5 +205,16 @@
}
:deep(.arco-tabs-content) {
@apply pt-0;
height: calc(100% - 49px);
.arco-tabs-content-list {
@apply h-full;
.arco-tabs-pane {
@apply h-full;
}
}
.scenario-detail-tab-pane {
padding: 8px 16px;
}
}
</style>

View File

@ -16,11 +16,16 @@
</template>
</MsEditableTab>
<div v-if="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
<environmentSelect />
<environmentSelect v-model:current-env-config="currentEnvConfig" />
<a-button type="primary" :loading="saveLoading" @click="saveScenario">
{{ t('common.save') }}
</a-button>
<!-- <executeButton /> -->
<executeButton
:execute-loading="activeScenarioTab.executeLoading"
is-emit
@execute="handleExecute"
@stop-debug="handleStopExecute"
/>
</div>
</div>
<a-divider class="!my-0" />
@ -84,17 +89,31 @@
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import scenarioModuleTree from './components/scenarioModuleTree.vue';
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
// import executeButton from '@/views/api-test/components/executeButton.vue';
import executeButton from '@/views/api-test/components/executeButton.vue';
import ScenarioTable from '@/views/api-test/scenario/components/scenarioTable.vue';
import { addScenario, getScenarioDetail, getTrashModuleCount, updateScenario } from '@/api/modules/api-test/scenario';
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
import {
addScenario,
debugScenario,
getScenarioDetail,
getTrashModuleCount,
updateScenario,
} from '@/api/modules/api-test/scenario';
import { getSocket } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import router from '@/router';
import useAppStore from '@/store/modules/app';
import { getGenerateId } from '@/utils';
import { ApiScenarioGetModuleParams, ApiScenarioTableItem, Scenario } from '@/models/apiTest/scenario';
import {
ApiScenarioDebugRequest,
ApiScenarioGetModuleParams,
ApiScenarioTableItem,
Scenario,
} from '@/models/apiTest/scenario';
import { ModuleTreeNode } from '@/models/common';
import { EnvConfig } from '@/models/projectManagement/environmental';
import { ApiTestRouteEnum } from '@/enums/routeEnum';
import { defaultScenario } from './components/config';
@ -251,6 +270,85 @@
});
}
}
const currentEnvConfig = ref<EnvConfig>();
const reportId = ref('');
const websocket = ref<WebSocket>();
const temporaryResponseMap = {}; // websockettab
/**
* 开启websocket监听接收执行结果
*/
function debugSocket(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
websocket.value = getSocket(
reportId.value,
executeType === 'localExec' ? '/ws/debug' : '',
executeType === 'localExec' ? localExecuteUrl : ''
);
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (activeScenarioTab.value.reportId === data.reportId) {
// tabtab
activeScenarioTab.value.executeLoading = false;
} else {
// tab
temporaryResponseMap[activeScenarioTab.value.id][data.reportId] = data.taskResult;
}
} else if (data.msgType === 'EXEC_END') {
// websocket
websocket.value?.close();
activeScenarioTab.value.executeLoading = false;
}
});
}
async function realExecute(
executeParams: ApiScenarioDebugRequest,
executeType?: 'localExec' | 'serverExec',
localExecuteUrl?: string
) {
try {
activeScenarioTab.value.executeLoading = true;
reportId.value = getGenerateId();
activeScenarioTab.value.reportId = reportId.value; // ID
debugSocket(executeType, localExecuteUrl); // websocket
executeParams.environmentId = currentEnvConfig.value?.id || '';
const res = await debugScenario(executeParams);
if (executeType === 'localExec' && localExecuteUrl) {
await localExecuteApiDebug(localExecuteUrl, res);
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
activeScenarioTab.value.executeLoading = false;
}
}
function handleExecute(executeType?: 'localExec' | 'serverExec', localExecuteUrl?: string) {
const environmentId = currentEnvConfig.value?.id || '';
realExecute(
{
grouped: false,
environmentId,
...activeScenarioTab.value,
},
executeType,
localExecuteUrl
);
}
function handleStopExecute() {
websocket.value?.close();
activeScenarioTab.value.executeLoading = false;
}
const scenarioId = computed(() => activeScenarioTab.value.id);
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
//
provide('scenarioId', scenarioId);
provide('scenarioExecuteLoading', scenarioExecuteLoading);
provide('temporaryResponseMap', temporaryResponseMap);
</script>
<style scoped lang="less">