fix(接口管理): 执行结果抽屉&用例列表跳转执行&部分细节
This commit is contained in:
parent
3a5a33ad0d
commit
ad6bc5be35
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.66602 0.666504L8.72002 0.668504L8.76532 0.67385C8.77665 0.675589 8.78827 0.677639 8.79983 0.679997C8.81451 0.68293 8.82897 0.686393 8.84324 0.690318C8.85366 0.693236 8.86392 0.696323 8.87411 0.699663C8.88721 0.703919 8.9004 0.708714 8.91339 0.713907L8.94816 0.728953C8.96194 0.735422 8.97554 0.742367 8.98887 0.749754C8.99694 0.75417 9.00509 0.758909 9.01315 0.763834C9.0302 0.774316 9.04677 0.785497 9.06279 0.79738L9.07736 0.808456C9.07912 0.809885 9.08106 0.811419 9.08298 0.812963L9.13742 0.861766L12.4708 4.1951L12.516 4.2465L12.5351 4.26963C12.547 4.28575 12.5582 4.30232 12.5686 4.3194L12.5829 4.3438C12.5902 4.35697 12.5971 4.37058 12.6036 4.38444L12.6185 4.41895C12.6238 4.43212 12.6286 4.44531 12.633 4.45868C12.6362 4.4686 12.6393 4.47886 12.6421 4.48917C12.6461 4.50355 12.6496 4.51801 12.6526 4.53264C12.6549 4.54425 12.6569 4.55587 12.6587 4.56753C12.6597 4.57435 12.6607 4.58153 12.6615 4.58876L12.6635 4.60902C12.6646 4.62099 12.6653 4.63299 12.6657 4.645L12.666 4.6665V7.99984H11.3327V5.33317H8.66602C8.32412 5.33317 8.04234 5.07581 8.00383 4.74425L7.99935 4.6665V1.99984H1.99935V13.9998H7.99935V15.3332H1.99935C1.26297 15.3332 0.666016 14.7362 0.666016 13.9998V1.99984C0.666016 1.26346 1.26297 0.666504 1.99935 0.666504H8.66602ZM12.5381 8.8665C13.7153 8.8665 14.5574 9.64206 14.666 10.637H13.2444C13.1629 10.2923 12.9727 10.0259 12.5291 10.0259H11.5783C11.0531 10.0259 10.7181 10.4098 10.7181 10.872L10.7271 13.3277C10.7271 13.7899 11.0622 14.1737 11.5874 14.1737H12.5381C12.9727 14.1737 13.1538 13.9231 13.2444 13.5862H14.6751C14.5664 14.5654 13.7153 15.3332 12.5472 15.3332H11.5783C10.3468 15.3332 9.34174 14.4636 9.34174 13.3982L9.33268 10.8093C9.33268 9.73607 10.3378 8.8665 11.5692 8.8665H12.5381ZM9.33268 2.94317V3.99984H10.3893L9.33268 2.94317Z" fill="#3370FF"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -1,7 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-dropdown-button
|
<a-dropdown-button
|
||||||
v-if="!caseDetail?.executeLoading && !props.executeLoading"
|
v-if="hasLocalExec && !props.executeLoading"
|
||||||
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
|
|
||||||
class="exec-btn"
|
class="exec-btn"
|
||||||
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
||||||
@select="execute"
|
@select="execute"
|
||||||
|
@ -16,32 +15,21 @@
|
||||||
</a-doption>
|
</a-doption>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown-button>
|
</a-dropdown-button>
|
||||||
<a-button v-else type="primary" @click="stopDebug">{{ t('common.stop') }}</a-button>
|
<a-button v-else-if="!hasLocalExec && !props.executeLoading" type="primary" @click="() => execute('serverExec')">
|
||||||
|
{{ t('apiTestDebug.serverExec') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button v-else type="primary" @click="emit('stopDebug')">
|
||||||
|
{{ t('common.stop') }}
|
||||||
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
|
|
||||||
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
import { getLocalConfig } from '@/api/modules/user/index';
|
||||||
|
|
||||||
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
|
||||||
import { debugCase, runCase } from '@/api/modules/api-test/management';
|
|
||||||
import { getSocket } from '@/api/modules/project-management/commonScript';
|
|
||||||
import useAppStore from '@/store/modules/app';
|
|
||||||
import { getGenerateId } from '@/utils';
|
|
||||||
|
|
||||||
import { LocalConfig } from '@/models/user';
|
|
||||||
|
|
||||||
import { defaultResponse } from '@/views/api-test/components/config';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
environmentId?: string;
|
|
||||||
request?: (...args) => Record<string, any>;
|
|
||||||
isCaseDetail?: boolean;
|
|
||||||
executeCase?: boolean;
|
|
||||||
executeLoading?: boolean;
|
executeLoading?: boolean;
|
||||||
isEmit?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -50,117 +38,35 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStore();
|
|
||||||
|
|
||||||
const caseDetail = defineModel<RequestParam>('detail', {
|
const hasLocalExec = ref(false); // 是否配置了api本地执行
|
||||||
required: false,
|
const isPriorityLocalExec = ref(false); // 是否优先本地执行
|
||||||
});
|
const localExecuteUrl = ref('');
|
||||||
|
|
||||||
const apiLocalExec = inject<Ref<LocalConfig>>('apiLocalExec');
|
async function initLocalConfig() {
|
||||||
const isPriorityLocalExec = ref(apiLocalExec?.value?.enable || false); // 是否优先本地执行
|
if (hasLocalExec.value) {
|
||||||
const localExecuteUrl = ref(apiLocalExec?.value?.userUrl || '');
|
|
||||||
const reportId = ref('');
|
|
||||||
const websocket = ref<WebSocket>();
|
|
||||||
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 开启websocket监听,接收执行结果
|
|
||||||
*/
|
|
||||||
function debugSocket(executeType?: 'localExec' | 'serverExec') {
|
|
||||||
websocket.value = getSocket(
|
|
||||||
reportId.value,
|
|
||||||
executeType === 'localExec' ? '/ws/debug' : '',
|
|
||||||
executeType === 'localExec' ? localExecuteUrl.value : ''
|
|
||||||
);
|
|
||||||
websocket.value.addEventListener('message', (event) => {
|
|
||||||
if (!caseDetail.value || props.isEmit) return;
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
if (data.msgType === 'EXEC_RESULT') {
|
|
||||||
if (caseDetail.value.reportId === data.reportId) {
|
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
|
||||||
caseDetail.value.response = data.taskResult; // 渲染出用例详情和创建用例抽屉的响应数据
|
|
||||||
caseDetail.value.executeLoading = false;
|
|
||||||
} else {
|
|
||||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
|
||||||
temporaryResponseMap[data.reportId] = data.taskResult;
|
|
||||||
}
|
|
||||||
} else if (data.msgType === 'EXEC_END') {
|
|
||||||
// 执行结束,关闭websocket
|
|
||||||
websocket.value?.close();
|
|
||||||
caseDetail.value.executeLoading = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function execute(executeType?: 'localExec' | 'serverExec') {
|
|
||||||
if (!caseDetail.value || props.isEmit) {
|
|
||||||
emit('execute', executeType, localExecuteUrl.value);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
caseDetail.value.executeLoading = true;
|
const res = await getLocalConfig();
|
||||||
caseDetail.value.response = cloneDeep(defaultResponse);
|
const apiLocalExec = res.find((e) => e.type === 'API');
|
||||||
const makeRequestParams = props.request && props.request(executeType); // 写在reportId之前,防止覆盖reportId
|
if (apiLocalExec) {
|
||||||
reportId.value = getGenerateId();
|
hasLocalExec.value = true;
|
||||||
caseDetail.value.reportId = reportId.value; // 存储报告ID
|
isPriorityLocalExec.value = apiLocalExec.enable || false;
|
||||||
let res;
|
localExecuteUrl.value = apiLocalExec.userUrl || '';
|
||||||
const params = {
|
|
||||||
environmentId: props.environmentId as string,
|
|
||||||
frontendDebug: executeType === 'localExec',
|
|
||||||
reportId: reportId.value,
|
|
||||||
apiDefinitionId: caseDetail.value.apiDefinitionId,
|
|
||||||
};
|
|
||||||
debugSocket(executeType); // 开启websocket
|
|
||||||
if (!(caseDetail.value.id as string).startsWith('c') && executeType === 'serverExec') {
|
|
||||||
// 已创建的服务端
|
|
||||||
res = await runCase({
|
|
||||||
request: props.isCaseDetail ? caseDetail.value.request : makeRequestParams?.request,
|
|
||||||
id: caseDetail.value.id as string,
|
|
||||||
projectId: caseDetail.value.projectId,
|
|
||||||
linkFileIds: caseDetail.value.linkFileIds,
|
|
||||||
uploadFileIds: caseDetail.value.uploadFileIds,
|
|
||||||
...params,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res = await debugCase({
|
|
||||||
request: props.isCaseDetail ? caseDetail.value.request : makeRequestParams?.request,
|
|
||||||
linkFileIds: makeRequestParams?.linkFileIds,
|
|
||||||
uploadFileIds: makeRequestParams?.uploadFileIds,
|
|
||||||
id: `case-${Date.now()}`,
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
...params,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (executeType === 'localExec') {
|
|
||||||
await localExecuteApiDebug(localExecuteUrl.value, res);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
caseDetail.value.executeLoading = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onBeforeMount(() => {
|
||||||
|
initLocalConfig();
|
||||||
|
});
|
||||||
|
|
||||||
function stopDebug() {
|
async function execute(executeType?: 'localExec' | 'serverExec') {
|
||||||
if (!caseDetail.value || props.isEmit) {
|
emit('execute', executeType, localExecuteUrl.value);
|
||||||
emit('stopDebug');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
websocket.value?.close();
|
|
||||||
caseDetail.value.executeLoading = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.executeCase,
|
|
||||||
(val) => {
|
|
||||||
if (val === true) {
|
|
||||||
setTimeout(() => {
|
|
||||||
execute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
isPriorityLocalExec,
|
isPriorityLocalExec,
|
||||||
|
|
|
@ -705,7 +705,7 @@
|
||||||
.length
|
.length
|
||||||
);
|
);
|
||||||
const bodyTabBadgeApi = computed(() =>
|
const bodyTabBadgeApi = computed(() =>
|
||||||
props.apiDetail?.request.body?.bodyType !== RequestBodyFormat.NONE ? '1' : ''
|
props.apiDetail?.request?.body?.bodyType !== RequestBodyFormat.NONE ? '1' : ''
|
||||||
);
|
);
|
||||||
// 根据协议类型获取请求内容tab
|
// 根据协议类型获取请求内容tab
|
||||||
const contentTabList = computed(() => {
|
const contentTabList = computed(() => {
|
||||||
|
@ -1575,6 +1575,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
|
if (props.isCase) return;
|
||||||
initLocalConfig();
|
initLocalConfig();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,13 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
|
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
|
||||||
<a-tabs v-model:active-key="activeApiTab.definitionActiveKey" animation lazy-load class="ms-api-tab-nav">
|
<a-tabs
|
||||||
|
v-model:active-key="activeApiTab.definitionActiveKey"
|
||||||
|
animation
|
||||||
|
lazy-load
|
||||||
|
class="ms-api-tab-nav"
|
||||||
|
@change="changeDefinitionActiveKey"
|
||||||
|
>
|
||||||
<a-tab-pane
|
<a-tab-pane
|
||||||
v-if="!activeApiTab.isNew"
|
v-if="!activeApiTab.isNew"
|
||||||
key="preview"
|
key="preview"
|
||||||
|
@ -52,6 +58,7 @@
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane v-if="!activeApiTab.isNew" key="case" :title="t('apiTestManagement.case')" class="ms-api-tab-pane">
|
<a-tab-pane v-if="!activeApiTab.isNew" key="case" :title="t('apiTestManagement.case')" class="ms-api-tab-pane">
|
||||||
<caseTable
|
<caseTable
|
||||||
|
ref="caseTableRef"
|
||||||
:is-api="true"
|
:is-api="true"
|
||||||
:active-module="props.activeModule"
|
:active-module="props.activeModule"
|
||||||
:protocol="activeApiTab.protocol"
|
:protocol="activeApiTab.protocol"
|
||||||
|
@ -229,6 +236,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiTableRef = ref<InstanceType<typeof apiTable>>();
|
const apiTableRef = ref<InstanceType<typeof apiTable>>();
|
||||||
|
const caseTableRef = ref<InstanceType<typeof caseTable>>();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => activeApiTab.value.id,
|
() => activeApiTab.value.id,
|
||||||
|
@ -299,6 +307,13 @@
|
||||||
apiTableRef.value?.loadApiList();
|
apiTableRef.value?.loadApiList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeDefinitionActiveKey(val: string | number) {
|
||||||
|
// 在定义可以添加用例,故需要切换到case时刷新数据
|
||||||
|
if (val === 'case') {
|
||||||
|
caseTableRef.value?.loadCaseList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openApiTab,
|
openApiTab,
|
||||||
addApiTab,
|
addApiTab,
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full w-full overflow-hidden">
|
<div class="h-full w-full overflow-hidden">
|
||||||
<a-tabs v-model:active-key="activeKey" class="h-full px-[16px]" animation lazy-load>
|
<a-tabs v-model:active-key="activeKey" class="h-full px-[16px]" animation lazy-load @change="changeActiveKey">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<div class="flex gap-[12px]">
|
<div class="flex gap-[12px]">
|
||||||
<environmentSelect v-if="props.isDrawer" v-model:current-env="environmentIdByDrawer" />
|
<environmentSelect v-if="props.isDrawer" v-model:current-env="environmentIdByDrawer" />
|
||||||
<execute
|
<executeButton
|
||||||
ref="executeRef"
|
ref="executeRef"
|
||||||
v-model:detail="caseDetail"
|
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
|
||||||
:execute-case="props.executeCase"
|
:execute-loading="caseDetail.executeLoading"
|
||||||
:environment-id="environmentId as string"
|
@stop-debug="stopDebug"
|
||||||
is-case-detail
|
@execute="handleExecute"
|
||||||
/>
|
/>
|
||||||
<a-dropdown position="br" :hide-on-select="false" @select="handleSelect">
|
<a-dropdown position="br" :hide-on-select="false" @select="handleSelect">
|
||||||
<a-button v-if="!props.isDrawer" type="outline">{{ t('common.operation') }}</a-button>
|
<a-button v-if="!props.isDrawer" type="outline">{{ t('common.operation') }}</a-button>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-tab-pane key="detail" :title="t('apiTestManagement.detail')" class="px-[18px] py-[16px]">
|
<a-tab-pane key="detail" :title="t('case.detail')" class="px-[18px] py-[16px]">
|
||||||
<MsDetailCard :title="`【${caseDetail.num}】${caseDetail.name}`" :description="description" class="mb-[8px]">
|
<MsDetailCard :title="`【${caseDetail.num}】${caseDetail.name}`" :description="description" class="mb-[8px]">
|
||||||
<template #type="{ value }">
|
<template #type="{ value }">
|
||||||
<apiMethodName :method="value as RequestMethods" tag-size="small" is-tag />
|
<apiMethodName :method="value as RequestMethods" tag-size="small" is-tag />
|
||||||
|
@ -56,14 +56,19 @@
|
||||||
:protocols="protocols as ProtocolItem[]"
|
:protocols="protocols as ProtocolItem[]"
|
||||||
:is-priority-local-exec="isPriorityLocalExec"
|
:is-priority-local-exec="isPriorityLocalExec"
|
||||||
is-case
|
is-case
|
||||||
@execute="(val: 'localExec' | 'serverExec')=>executeRef?.execute(val)"
|
@execute="handleExecute"
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
|
<a-tab-pane key="reference" :title="t('apiTestManagement.reference')" class="px-[18px] py-[16px]">
|
||||||
<tab-case-dependency :source-id="caseDetail.id" />
|
<tab-case-dependency :source-id="caseDetail.id" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="executeHistory" :title="t('apiTestManagement.executeHistory')" class="px-[18px] py-[16px]">
|
<a-tab-pane key="executeHistory" :title="t('apiTestManagement.executeHistory')" class="px-[18px] py-[16px]">
|
||||||
<tab-case-execute-history :source-id="caseDetail.id" module-type="API_REPORT" :protocol="caseDetail.protocol" />
|
<tab-case-execute-history
|
||||||
|
ref="executeHistoryRef"
|
||||||
|
:source-id="caseDetail.id"
|
||||||
|
module-type="API_REPORT"
|
||||||
|
:protocol="caseDetail.protocol"
|
||||||
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<!-- <a-tab-pane key="dependencies" :title="t('apiTestManagement.dependencies')" class="px-[18px] py-[16px]">
|
<!-- <a-tab-pane key="dependencies" :title="t('apiTestManagement.dependencies')" class="px-[18px] py-[16px]">
|
||||||
</a-tab-pane> -->
|
</a-tab-pane> -->
|
||||||
|
@ -88,22 +93,26 @@
|
||||||
import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue';
|
import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
|
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
|
||||||
import execute from '@/views/api-test/components/executeButton.vue';
|
import executeButton from '@/views/api-test/components/executeButton.vue';
|
||||||
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
import TabCaseChangeHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseChangeHistory.vue';
|
import TabCaseChangeHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseChangeHistory.vue';
|
||||||
import TabCaseDependency from '@/views/api-test/management/components/management/case/tabContent/tabCaseDependency.vue';
|
import TabCaseDependency from '@/views/api-test/management/components/management/case/tabContent/tabCaseDependency.vue';
|
||||||
import TabCaseExecuteHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseExecuteHistory.vue';
|
import TabCaseExecuteHistory from '@/views/api-test/management/components/management/case/tabContent/tabCaseExecuteHistory.vue';
|
||||||
|
|
||||||
import { deleteCase, toggleFollowCase } from '@/api/modules/api-test/management';
|
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||||
|
import { debugCase, deleteCase, runCase, toggleFollowCase } from '@/api/modules/api-test/management';
|
||||||
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
|
import { getGenerateId } from '@/utils';
|
||||||
|
|
||||||
import { ProtocolItem } from '@/models/apiTest/common';
|
import { ProtocolItem } from '@/models/apiTest/common';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
import { RequestMethods } from '@/enums/apiEnum';
|
import { RequestMethods } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
import { defaultResponse } from '@/views/api-test/components/config';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isDrawer?: boolean; // 抽屉
|
isDrawer?: boolean; // 抽屉
|
||||||
executeCase?: boolean;
|
|
||||||
detail: RequestParam;
|
detail: RequestParam;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -115,12 +124,9 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openModal } = useModal();
|
const { openModal } = useModal();
|
||||||
|
|
||||||
|
const executeCase = defineModel<boolean>('executeCase', { default: false });
|
||||||
const caseDetail = ref<RequestParam>(cloneDeep(props.detail)); // props.detail是嵌套的引用类型,防止不必要的修改来源影响props.detail的数据
|
const caseDetail = ref<RequestParam>(cloneDeep(props.detail)); // props.detail是嵌套的引用类型,防止不必要的修改来源影响props.detail的数据
|
||||||
const environmentIdByDrawer = ref(props.detail.environmentId);
|
const environmentIdByDrawer = ref(props.detail.environmentId);
|
||||||
watchEffect(() => {
|
|
||||||
caseDetail.value = cloneDeep(props.detail); // props.detail是嵌套的引用类型,防止不必要的修改来源影响props.detail的数据
|
|
||||||
environmentIdByDrawer.value = props.detail.environmentId;
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeKey = ref('detail');
|
const activeKey = ref('detail');
|
||||||
|
|
||||||
|
@ -226,9 +232,105 @@
|
||||||
props.isDrawer ? environmentIdByDrawer.value : currentEnvConfigByInject?.value?.id
|
props.isDrawer ? environmentIdByDrawer.value : currentEnvConfigByInject?.value?.id
|
||||||
);
|
);
|
||||||
|
|
||||||
const executeRef = ref<InstanceType<typeof execute>>();
|
const executeHistoryRef = ref<InstanceType<typeof TabCaseExecuteHistory>>();
|
||||||
|
function changeActiveKey(val: string | number) {
|
||||||
|
if (val === 'executeHistory') {
|
||||||
|
executeHistoryRef.value?.loadExecuteList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const executeRef = ref<InstanceType<typeof executeButton>>();
|
||||||
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
||||||
|
|
||||||
|
const reportId = ref('');
|
||||||
|
const websocket = ref<WebSocket>();
|
||||||
|
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||||
|
// 开启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 (caseDetail.value.reportId === data.reportId) {
|
||||||
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
|
caseDetail.value.response = data.taskResult; // 渲染出用例详情和创建用例抽屉的响应数据
|
||||||
|
caseDetail.value.executeLoading = false;
|
||||||
|
executeCase.value = false;
|
||||||
|
} else {
|
||||||
|
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||||
|
temporaryResponseMap[data.reportId] = data.taskResult;
|
||||||
|
}
|
||||||
|
} else if (data.msgType === 'EXEC_END') {
|
||||||
|
// 执行结束,关闭websocket
|
||||||
|
websocket.value?.close();
|
||||||
|
caseDetail.value.executeLoading = false;
|
||||||
|
executeCase.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function handleExecute(executeType?: 'localExec' | 'serverExec') {
|
||||||
|
try {
|
||||||
|
caseDetail.value.executeLoading = true;
|
||||||
|
caseDetail.value.response = cloneDeep(defaultResponse);
|
||||||
|
reportId.value = getGenerateId();
|
||||||
|
caseDetail.value.reportId = reportId.value; // 存储报告ID
|
||||||
|
let res;
|
||||||
|
const params = {
|
||||||
|
id: caseDetail.value.id as string,
|
||||||
|
environmentId: environmentId.value,
|
||||||
|
frontendDebug: executeType === 'localExec',
|
||||||
|
reportId: reportId.value,
|
||||||
|
apiDefinitionId: caseDetail.value.apiDefinitionId,
|
||||||
|
request: caseDetail.value.request,
|
||||||
|
projectId: caseDetail.value.projectId,
|
||||||
|
linkFileIds: caseDetail.value.linkFileIds,
|
||||||
|
uploadFileIds: caseDetail.value.uploadFileIds,
|
||||||
|
};
|
||||||
|
debugSocket(executeType); // 开启websocket
|
||||||
|
if (executeType === 'serverExec') {
|
||||||
|
// 已创建的服务端
|
||||||
|
res = await runCase(params);
|
||||||
|
} else {
|
||||||
|
res = await debugCase(params);
|
||||||
|
}
|
||||||
|
if (executeType === 'localExec') {
|
||||||
|
await localExecuteApiDebug(executeRef.value?.localExecuteUrl as string, res);
|
||||||
|
}
|
||||||
|
// 执行完更新执行历史
|
||||||
|
executeHistoryRef.value?.loadExecuteList();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
caseDetail.value.executeLoading = false;
|
||||||
|
executeCase.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function stopDebug() {
|
||||||
|
websocket.value?.close();
|
||||||
|
caseDetail.value.executeLoading = false;
|
||||||
|
executeCase.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.detail,
|
||||||
|
() => {
|
||||||
|
caseDetail.value = cloneDeep(props.detail); // props.detail是嵌套的引用类型,防止不必要的修改来源影响props.detail的数据
|
||||||
|
environmentIdByDrawer.value = props.detail.environmentId;
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (executeCase.value && !caseDetail.value.executeLoading) {
|
||||||
|
// 如果是执行操作打开用例详情,且该用例不在执行状态中,则立即执行
|
||||||
|
handleExecute(isPriorityLocalExec.value ? 'localExec' : 'serverExec');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
editCase,
|
editCase,
|
||||||
share,
|
share,
|
||||||
|
|
|
@ -56,8 +56,8 @@
|
||||||
</template>
|
</template>
|
||||||
<caseDetail
|
<caseDetail
|
||||||
ref="caseDerailRef"
|
ref="caseDerailRef"
|
||||||
|
v-model:execute-case="executeCase"
|
||||||
is-drawer
|
is-drawer
|
||||||
:execute-case="props.executeCase"
|
|
||||||
:detail="props.detail"
|
:detail="props.detail"
|
||||||
:api-detail="props.apiDetail"
|
:api-detail="props.apiDetail"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
|
@ -78,7 +78,6 @@
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
detail: RequestParam;
|
detail: RequestParam;
|
||||||
apiDetail?: RequestParam;
|
apiDetail?: RequestParam;
|
||||||
executeCase?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -86,6 +85,7 @@
|
||||||
const innerVisible = defineModel<boolean>('visible', {
|
const innerVisible = defineModel<boolean>('visible', {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
const executeCase = defineModel<boolean>('executeCase', { default: false });
|
||||||
const caseDerailRef = ref<InstanceType<typeof caseDetail>>();
|
const caseDerailRef = ref<InstanceType<typeof caseDetail>>();
|
||||||
|
|
||||||
function handleSelect(val: string | number | Record<string, any> | undefined) {
|
function handleSelect(val: string | number | Record<string, any> | undefined) {
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
<template>
|
||||||
|
<MsDrawer
|
||||||
|
v-model:visible="innerVisible"
|
||||||
|
:title="reportStepDetail.name"
|
||||||
|
:width="1200"
|
||||||
|
:footer="false"
|
||||||
|
unmount-on-close
|
||||||
|
no-content-padding
|
||||||
|
show-full-screen
|
||||||
|
>
|
||||||
|
<template #tbutton>
|
||||||
|
<MsButton type="icon" status="secondary" class="mr-4 !rounded-[var(--border-radius-small)]" @click="shareHandler">
|
||||||
|
<MsIcon type="icon-icon_share1" class="mr-2 font-[16px]" />
|
||||||
|
{{ t('common.share') }}
|
||||||
|
</MsButton>
|
||||||
|
</template>
|
||||||
|
<CaseReportCom :detail-info="reportStepDetail" />
|
||||||
|
</MsDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
import CaseReportCom from '@/views/api-test/report/component/caseReportCom.vue';
|
||||||
|
|
||||||
|
import { getShareInfo, reportCaseDetail } from '@/api/modules/api-test/report';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
|
import type { ReportDetail } from '@/models/apiTest/report';
|
||||||
|
import { RouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
reportId: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const innerVisible = defineModel<boolean>('visible', {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const reportStepDetail = ref<ReportDetail>({
|
||||||
|
id: '',
|
||||||
|
name: '', // 报告名称
|
||||||
|
testPlanId: '',
|
||||||
|
createUser: '',
|
||||||
|
deleteTime: 0,
|
||||||
|
deleteUser: '',
|
||||||
|
deleted: false,
|
||||||
|
updateUser: '',
|
||||||
|
updateTime: 0,
|
||||||
|
startTime: 0, // 开始时间/同创建时间一致
|
||||||
|
endTime: 0, // 结束时间/报告执行完成
|
||||||
|
requestDuration: 0, // 请求总耗时
|
||||||
|
status: '', // 报告状态/SUCCESS/ERROR
|
||||||
|
triggerMode: '', // 触发方式
|
||||||
|
runMode: '', // 执行模式
|
||||||
|
poolId: '', // 资源池
|
||||||
|
poolName: '', // 资源池名称
|
||||||
|
versionId: '',
|
||||||
|
integrated: false, // 是否是集成报告
|
||||||
|
projectId: '',
|
||||||
|
environmentId: '', // 环境id
|
||||||
|
environmentName: '', // 环境名称
|
||||||
|
errorCount: 0, // 失败数
|
||||||
|
fakeErrorCount: 0, // 误报数
|
||||||
|
pendingCount: 0, // 未执行数
|
||||||
|
successCount: 0, // 成功数
|
||||||
|
assertionCount: 0, // 总断言数
|
||||||
|
assertionSuccessCount: 0, // 成功断言数
|
||||||
|
requestErrorRate: '', // 请求失败率
|
||||||
|
requestPendingRate: '', // 请求未执行率
|
||||||
|
requestFakeErrorRate: '', // 请求误报率
|
||||||
|
requestPassRate: '', // 请求通过率
|
||||||
|
assertionPassRate: '', // 断言通过率
|
||||||
|
scriptIdentifier: '', // 脚本标识
|
||||||
|
children: [], // 步骤列表
|
||||||
|
stepTotal: 0, // 步骤总数
|
||||||
|
console: '',
|
||||||
|
});
|
||||||
|
async function getReportCaseDetail() {
|
||||||
|
try {
|
||||||
|
reportStepDetail.value = await reportCaseDetail(props.reportId);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => innerVisible.value,
|
||||||
|
async (val) => {
|
||||||
|
if (val) {
|
||||||
|
await getReportCaseDetail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 分享share
|
||||||
|
const shareLink = ref<string>('');
|
||||||
|
async function shareHandler() {
|
||||||
|
try {
|
||||||
|
const res = await getShareInfo({
|
||||||
|
reportId: reportStepDetail.value.id,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
});
|
||||||
|
const shareId = res.shareUrl;
|
||||||
|
|
||||||
|
const { origin } = window.location;
|
||||||
|
shareLink.value = `${origin}/#/${RouteEnum.SHARE}/${RouteEnum.SHARE_REPORT_CASE}${shareId}`;
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(shareLink.value).then(
|
||||||
|
() => {
|
||||||
|
Message.info(t('bugManagement.detail.shareTip'));
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
Message.error(e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.value = shareLink.value;
|
||||||
|
document.body.appendChild(input);
|
||||||
|
input.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(input);
|
||||||
|
Message.info(t('bugManagement.detail.shareTip'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -148,7 +148,12 @@
|
||||||
</a-trigger>
|
</a-trigger>
|
||||||
</template>
|
</template>
|
||||||
<template #lastReportStatus="{ record }">
|
<template #lastReportStatus="{ record }">
|
||||||
<ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="record.lastReportStatus" />
|
<ExecutionStatus
|
||||||
|
:module-type="ReportEnum.API_REPORT"
|
||||||
|
:status="record.lastReportStatus"
|
||||||
|
:class="[!record.lastReportId ? '' : 'cursor-pointer']"
|
||||||
|
@click="showResult(record)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #passRateColumn>
|
<template #passRateColumn>
|
||||||
<div class="flex items-center text-[var(--color-text-3)]">
|
<div class="flex items-center text-[var(--color-text-3)]">
|
||||||
|
@ -285,9 +290,9 @@
|
||||||
/>
|
/>
|
||||||
<caseDetailDrawer
|
<caseDetailDrawer
|
||||||
v-model:visible="caseDetailDrawerVisible"
|
v-model:visible="caseDetailDrawerVisible"
|
||||||
|
v-model:execute-case="caseExecute"
|
||||||
:detail="caseDetail as RequestParam"
|
:detail="caseDetail as RequestParam"
|
||||||
:api-detail="apiDetail as RequestParam"
|
:api-detail="apiDetail as RequestParam"
|
||||||
:execute-case="caseExecute"
|
|
||||||
@update-follow="caseDetail.follow = !caseDetail.follow"
|
@update-follow="caseDetail.follow = !caseDetail.follow"
|
||||||
@load-case="(id: string) => loadCase(id)"
|
@load-case="(id: string) => loadCase(id)"
|
||||||
@delete-case="deleteCaseByDetail"
|
@delete-case="deleteCaseByDetail"
|
||||||
|
@ -300,6 +305,8 @@
|
||||||
:batch-run-func="batchExecuteCase"
|
:batch-run-func="batchExecuteCase"
|
||||||
@finished="loadCaseListAndResetSelector"
|
@finished="loadCaseListAndResetSelector"
|
||||||
/>
|
/>
|
||||||
|
<!-- 执行结果抽屉 -->
|
||||||
|
<caseReportDrawer v-model:visible="showExecuteResult" :report-id="activeReportId" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -316,6 +323,7 @@
|
||||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||||
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
import caseLevel from '@/components/business/ms-case-associate/caseLevel.vue';
|
||||||
import caseDetailDrawer from './caseDetailDrawer.vue';
|
import caseDetailDrawer from './caseDetailDrawer.vue';
|
||||||
|
import caseReportDrawer from './caseReportDrawer.vue';
|
||||||
import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue';
|
import createAndEditCaseDrawer from './createAndEditCaseDrawer.vue';
|
||||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||||
import BatchRunModal from '@/views/api-test/components/batchRunModal.vue';
|
import BatchRunModal from '@/views/api-test/components/batchRunModal.vue';
|
||||||
|
@ -367,7 +375,6 @@
|
||||||
const { openModal } = useModal();
|
const { openModal } = useModal();
|
||||||
|
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
const refreshModuleTree: (() => Promise<any>) | undefined = inject('refreshModuleTree');
|
|
||||||
|
|
||||||
const hasOperationPermission = computed(() =>
|
const hasOperationPermission = computed(() =>
|
||||||
hasAnyPermission([
|
hasAnyPermission([
|
||||||
|
@ -712,9 +719,6 @@
|
||||||
Message.success(t('common.deleteSuccess'));
|
Message.success(t('common.deleteSuccess'));
|
||||||
resetSelector();
|
resetSelector();
|
||||||
loadCaseListAndResetSelector();
|
loadCaseListAndResetSelector();
|
||||||
if (typeof refreshModuleTree === 'function') {
|
|
||||||
refreshModuleTree();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -932,6 +936,14 @@
|
||||||
loadCaseList();
|
loadCaseList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeReportId = ref('');
|
||||||
|
const showExecuteResult = ref(false);
|
||||||
|
async function showResult(record: ApiCaseDetail) {
|
||||||
|
if (!record.lastReportId) return;
|
||||||
|
activeReportId.value = record.lastReportId;
|
||||||
|
showExecuteResult.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
loadCaseList,
|
loadCaseList,
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,12 +34,13 @@
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
/>
|
/>
|
||||||
<environmentSelect v-model:current-env="environmentId" />
|
<environmentSelect ref="environmentSelectRef" v-model:current-env="environmentId" />
|
||||||
<execute
|
<executeButton
|
||||||
ref="executeRef"
|
ref="executeRef"
|
||||||
v-model:detail="detailForm"
|
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
|
||||||
:environment-id="currentEnvConfig?.id as string"
|
:execute-loading="detailForm.executeLoading"
|
||||||
:request="requestCompositionRef?.makeRequestParams"
|
@stop-debug="stopDebug"
|
||||||
|
@execute="handleExecute"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -84,7 +85,7 @@
|
||||||
:file-save-as-api="transferFileCase"
|
:file-save-as-api="transferFileCase"
|
||||||
:current-env-config="currentEnvConfig"
|
:current-env-config="currentEnvConfig"
|
||||||
is-definition
|
is-definition
|
||||||
@execute="(val: 'localExec' | 'serverExec')=>executeRef?.execute(val)"
|
@execute="handleExecute"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,25 +104,30 @@
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||||
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
|
import environmentSelect from '@/views/api-test/components/environmentSelect.vue';
|
||||||
import execute from '@/views/api-test/components/executeButton.vue';
|
import executeButton from '@/views/api-test/components/executeButton.vue';
|
||||||
import requestComposition, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
import requestComposition, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
|
||||||
|
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||||
import {
|
import {
|
||||||
addCase,
|
addCase,
|
||||||
|
debugCase,
|
||||||
getDefinitionDetail,
|
getDefinitionDetail,
|
||||||
getTransferOptionsCase,
|
getTransferOptionsCase,
|
||||||
|
runCase,
|
||||||
transferFileCase,
|
transferFileCase,
|
||||||
updateCase,
|
updateCase,
|
||||||
uploadTempFileCase,
|
uploadTempFileCase,
|
||||||
} from '@/api/modules/api-test/management';
|
} from '@/api/modules/api-test/management';
|
||||||
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
import { getGenerateId } from '@/utils';
|
||||||
|
|
||||||
import { AddApiCaseParams, ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management';
|
import { AddApiCaseParams, ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
import { RequestDefinitionStatus, RequestMethods } from '@/enums/apiEnum';
|
import { RequestDefinitionStatus, RequestMethods } from '@/enums/apiEnum';
|
||||||
|
|
||||||
import { casePriorityOptions } from '@/views/api-test/components/config';
|
import { casePriorityOptions, defaultResponse } from '@/views/api-test/components/config';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
apiDetail?: RequestParam | ApiDefinitionDetail;
|
apiDetail?: RequestParam | ApiDefinitionDetail;
|
||||||
|
@ -175,7 +181,6 @@
|
||||||
});
|
});
|
||||||
const detailForm = ref(cloneDeep(defaultDetail.value));
|
const detailForm = ref(cloneDeep(defaultDetail.value));
|
||||||
const isEdit = ref(false);
|
const isEdit = ref(false);
|
||||||
const executeRef = ref<InstanceType<typeof execute>>();
|
|
||||||
|
|
||||||
async function open(apiId: string, record?: ApiCaseDetail | RequestParam, isCopy?: boolean) {
|
async function open(apiId: string, record?: ApiCaseDetail | RequestParam, isCopy?: boolean) {
|
||||||
apiDefinitionId.value = apiId;
|
apiDefinitionId.value = apiId;
|
||||||
|
@ -276,6 +281,84 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const executeRef = ref<InstanceType<typeof executeButton>>();
|
||||||
|
const reportId = ref('');
|
||||||
|
const websocket = ref<WebSocket>();
|
||||||
|
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||||
|
// 开启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 (detailForm.value.reportId === data.reportId) {
|
||||||
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
|
detailForm.value.response = data.taskResult; // 渲染出用例详情和创建用例抽屉的响应数据
|
||||||
|
detailForm.value.executeLoading = false;
|
||||||
|
} else {
|
||||||
|
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||||
|
temporaryResponseMap[data.reportId] = data.taskResult;
|
||||||
|
}
|
||||||
|
} else if (data.msgType === 'EXEC_END') {
|
||||||
|
// 执行结束,关闭websocket
|
||||||
|
websocket.value?.close();
|
||||||
|
detailForm.value.executeLoading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function handleExecute(executeType?: 'localExec' | 'serverExec') {
|
||||||
|
try {
|
||||||
|
detailForm.value.executeLoading = true;
|
||||||
|
detailForm.value.response = cloneDeep(defaultResponse);
|
||||||
|
const makeRequestParams = requestCompositionRef.value?.makeRequestParams(executeType); // 写在reportId之前,防止覆盖reportId
|
||||||
|
reportId.value = getGenerateId();
|
||||||
|
detailForm.value.reportId = reportId.value; // 存储报告ID
|
||||||
|
let res;
|
||||||
|
const params = {
|
||||||
|
environmentId: environmentId.value as string,
|
||||||
|
frontendDebug: executeType === 'localExec',
|
||||||
|
reportId: reportId.value,
|
||||||
|
apiDefinitionId: detailForm.value.apiDefinitionId,
|
||||||
|
request: makeRequestParams?.request,
|
||||||
|
linkFileIds: makeRequestParams?.linkFileIds,
|
||||||
|
uploadFileIds: makeRequestParams?.uploadFileIds,
|
||||||
|
};
|
||||||
|
debugSocket(executeType); // 开启websocket
|
||||||
|
if (!(detailForm.value.id as string).startsWith('c') && executeType === 'serverExec') {
|
||||||
|
// 已创建的服务端
|
||||||
|
res = await runCase({
|
||||||
|
id: detailForm.value.id as string,
|
||||||
|
projectId: detailForm.value.projectId,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res = await debugCase({
|
||||||
|
id: `case-${Date.now()}`,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (executeType === 'localExec') {
|
||||||
|
await localExecuteApiDebug(executeRef.value?.localExecuteUrl as string, res);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
detailForm.value.executeLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function stopDebug() {
|
||||||
|
websocket.value?.close();
|
||||||
|
detailForm.value.executeLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const environmentSelectRef = ref<InstanceType<typeof environmentSelect>>();
|
||||||
|
const currentEnvConfigByDrawer = computed<EnvConfig | undefined>(() => environmentSelectRef.value?.currentEnvConfig);
|
||||||
|
provide('currentEnvConfig', readonly(currentEnvConfigByDrawer));
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open,
|
open,
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
|
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
|
||||||
<caseDetail
|
<caseDetail
|
||||||
|
v-model:execute-case="caseExecute"
|
||||||
:detail="activeApiTab"
|
:detail="activeApiTab"
|
||||||
:execute-case="caseExecute"
|
|
||||||
:module-tree="props.moduleTree"
|
:module-tree="props.moduleTree"
|
||||||
@delete-case="deleteCase"
|
@delete-case="deleteCase"
|
||||||
@update-follow="activeApiTab.follow = !activeApiTab.follow"
|
@update-follow="activeApiTab.follow = !activeApiTab.follow"
|
||||||
|
|
|
@ -82,59 +82,42 @@
|
||||||
<template #status="{ record }">
|
<template #status="{ record }">
|
||||||
<ExecutionStatus :status="record.status" :module-type="ReportEnum.API_REPORT" />
|
<ExecutionStatus :status="record.status" :module-type="ReportEnum.API_REPORT" />
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record, rowIndex }">
|
||||||
<a-tooltip :disabled="!record.deleted" :content="t('case.detail.report.delete')" position="top">
|
<a-tooltip :disabled="!record.deleted" :content="t('case.detail.report.delete')" position="top">
|
||||||
<MsButton :disabled="record.deleted" class="!mr-0" @click="showResult(record)"
|
<MsButton :disabled="record.deleted" class="!mr-0" @click="showResult(record, rowIndex)"
|
||||||
>{{ t('apiScenario.executeHistory.execution.operation') }}
|
>{{ t('apiScenario.executeHistory.execution.operation') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</ms-base-table>
|
</ms-base-table>
|
||||||
<a-modal
|
|
||||||
v-model:visible="showResponse"
|
|
||||||
class="ms-modal-response ms-modal-response-body"
|
|
||||||
title-align="start"
|
|
||||||
:footer="false"
|
|
||||||
>
|
|
||||||
<template #title> {{ t('caseManagement.featureCase.tableColumnExecutionResult') }} </template>
|
|
||||||
<response
|
|
||||||
v-show="showResponse"
|
|
||||||
:hide-layout-switch="true"
|
|
||||||
:is-expanded="true"
|
|
||||||
:is-http-protocol="props.protocol === 'HTTP'"
|
|
||||||
:is-priority-local-exec="false"
|
|
||||||
:active-tab="ResponseComposition.BODY"
|
|
||||||
:request-result="responseContent?.requestResults[0]"
|
|
||||||
:console="responseContent?.console"
|
|
||||||
:is-definition="true"
|
|
||||||
:is-response-model="true"
|
|
||||||
></response>
|
|
||||||
</a-modal>
|
|
||||||
</div>
|
</div>
|
||||||
|
<CaseReportDrawer
|
||||||
|
v-model:visible="showResponse"
|
||||||
|
:report-id="activeReportId"
|
||||||
|
:active-report-index="activeReportIndex"
|
||||||
|
:table-data="propsRes.data"
|
||||||
|
:page-change="propsEvent.pageChange"
|
||||||
|
:pagination="propsRes.msPagination!"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
import response from '@/views/api-test/components/requestComposition/response/index.vue';
|
import CaseReportDrawer from '@/views/api-test/report/component/caseReportDrawer.vue';
|
||||||
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
|
||||||
|
|
||||||
import { getApiCaseExecuteHistory, getCaseReportDetail, getReportById } from '@/api/modules/api-test/management';
|
import { getApiCaseExecuteHistory } from '@/api/modules/api-test/management';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { ApiCaseReportDetail, RequestTaskResult } from '@/models/apiTest/common';
|
|
||||||
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management';
|
import { ApiCaseExecuteHistoryItem } from '@/models/apiTest/management';
|
||||||
import { ResponseComposition } from '@/enums/apiEnum';
|
|
||||||
import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
|
import { ReportEnum, ReportStatus, TriggerModeLabel } from '@/enums/reportEnum';
|
||||||
|
|
||||||
import { defaultResponse } from '@/views/api-test/components/config';
|
|
||||||
|
|
||||||
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
const triggerModeListFilters = ref<string[]>(Object.keys(TriggerModeLabel));
|
||||||
const triggerModeFilterVisible = ref(false);
|
const triggerModeFilterVisible = ref(false);
|
||||||
const statusFilterVisible = ref(false);
|
const statusFilterVisible = ref(false);
|
||||||
|
@ -145,8 +128,6 @@
|
||||||
|
|
||||||
const showResponse = ref(false);
|
const showResponse = ref(false);
|
||||||
|
|
||||||
const responseContent = ref<RequestTaskResult>();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
sourceId: string | number;
|
sourceId: string | number;
|
||||||
moduleType: string;
|
moduleType: string;
|
||||||
|
@ -262,40 +243,21 @@
|
||||||
loadExecuteList();
|
loadExecuteList();
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadedReportDetail(detail: ApiCaseReportDetail[]) {
|
const activeReportIndex = ref<number>(0);
|
||||||
responseContent.value = cloneDeep(defaultResponse);
|
const activeReportId = ref('');
|
||||||
const apiCaseReportDetailElement = detail[0];
|
async function showResult(record: ApiCaseExecuteHistoryItem, rowIndex: number) {
|
||||||
if (apiCaseReportDetailElement.id) {
|
activeReportId.value = record.id;
|
||||||
responseContent.value.requestResults[0] = apiCaseReportDetailElement.content;
|
activeReportIndex.value = rowIndex;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadedReport(detail: Record<string, any>) {
|
|
||||||
if (detail.id) {
|
|
||||||
if (detail.children && detail.children.length > 0) {
|
|
||||||
try {
|
|
||||||
const caseReportDetail = await getCaseReportDetail(detail.id, detail.children[0].stepId);
|
|
||||||
loadedReportDetail(caseReportDetail);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showResult(record: ApiCaseExecuteHistoryItem) {
|
|
||||||
try {
|
|
||||||
showResponse.value = true;
|
showResponse.value = true;
|
||||||
const result = await getReportById(record.id);
|
|
||||||
await loadedReport(result);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
loadExecuteList();
|
loadExecuteList();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
loadExecuteList,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -10,10 +10,17 @@
|
||||||
>
|
>
|
||||||
<template #label="{ tab }">
|
<template #label="{ tab }">
|
||||||
<apiMethodName
|
<apiMethodName
|
||||||
v-if="tab.id !== 'all'"
|
v-if="tab.id !== 'all' && tab.type === 'api'"
|
||||||
:method="tab.protocol === 'HTTP' ? tab.method : tab.protocol"
|
:method="tab.protocol === 'HTTP' ? tab.method : tab.protocol"
|
||||||
class="mr-[4px]"
|
class="mr-[4px]"
|
||||||
/>
|
/>
|
||||||
|
<svg-icon
|
||||||
|
v-if="tab.id !== 'all' && tab.type === 'case'"
|
||||||
|
width="16px"
|
||||||
|
height="16px"
|
||||||
|
:name="'apiCase'"
|
||||||
|
class="mr-[4px]"
|
||||||
|
/>
|
||||||
<a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500">
|
<a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500">
|
||||||
<div class="one-line-text max-w-[144px]">
|
<div class="one-line-text max-w-[144px]">
|
||||||
{{ tab.name || tab.label }}
|
{{ tab.name || tab.label }}
|
||||||
|
@ -58,14 +65,12 @@
|
||||||
|
|
||||||
// import MockTable from '@/views/api-test/management/components/management/mock/mockTable.vue';
|
// import MockTable from '@/views/api-test/management/components/management/mock/mockTable.vue';
|
||||||
import { getProtocolList } from '@/api/modules/api-test/common';
|
import { getProtocolList } from '@/api/modules/api-test/common';
|
||||||
import { getLocalConfig } from '@/api/modules/user/index';
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { ProtocolItem } from '@/models/apiTest/common';
|
import { ProtocolItem } from '@/models/apiTest/common';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
import { LocalConfig } from '@/models/user';
|
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
RequestComposition,
|
RequestComposition,
|
||||||
|
@ -277,30 +282,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiLocalExec = ref<Record<string, any> | LocalConfig | undefined>({});
|
|
||||||
async function initLocalConfig() {
|
|
||||||
try {
|
|
||||||
const res = await getLocalConfig();
|
|
||||||
apiLocalExec.value = res.find((e) => e.type === 'API');
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const environmentSelectRef = ref<InstanceType<typeof environmentSelect>>();
|
const environmentSelectRef = ref<InstanceType<typeof environmentSelect>>();
|
||||||
const currentEnvConfig = computed<EnvConfig | undefined>(() => environmentSelectRef.value?.currentEnvConfig);
|
const currentEnvConfig = computed<EnvConfig | undefined>(() => environmentSelectRef.value?.currentEnvConfig);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
initProtocolList();
|
initProtocolList();
|
||||||
initLocalConfig();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 向孙组件提供属性 */
|
/** 向孙组件提供属性 */
|
||||||
provide('currentEnvConfig', readonly(currentEnvConfig));
|
provide('currentEnvConfig', readonly(currentEnvConfig));
|
||||||
provide('defaultCaseParams', readonly(defaultCaseParams));
|
provide('defaultCaseParams', readonly(defaultCaseParams));
|
||||||
provide('protocols', readonly(protocols));
|
provide('protocols', readonly(protocols));
|
||||||
provide('apiLocalExec', readonly(apiLocalExec));
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
newTab,
|
newTab,
|
||||||
|
|
|
@ -173,6 +173,7 @@ export default {
|
||||||
'case.execute.reportName': 'Report name',
|
'case.execute.reportName': 'Report name',
|
||||||
'case.execute.pool': 'Resource pool operation',
|
'case.execute.pool': 'Resource pool operation',
|
||||||
'case.allCase': 'All Case',
|
'case.allCase': 'All Case',
|
||||||
|
'case.detail': 'Case Detail',
|
||||||
'case.caseName': 'Case Name',
|
'case.caseName': 'Case Name',
|
||||||
'case.caseLevel': 'Case Level',
|
'case.caseLevel': 'Case Level',
|
||||||
'case.caseEnvironment': 'Case Environment',
|
'case.caseEnvironment': 'Case Environment',
|
||||||
|
|
|
@ -167,6 +167,7 @@ export default {
|
||||||
'case.execute.reportName': '报告名称',
|
'case.execute.reportName': '报告名称',
|
||||||
'case.execute.pool': '资源池运行',
|
'case.execute.pool': '资源池运行',
|
||||||
'case.allCase': '全部CASE',
|
'case.allCase': '全部CASE',
|
||||||
|
'case.detail': '用例详情',
|
||||||
'case.caseName': '用例名称',
|
'case.caseName': '用例名称',
|
||||||
'case.caseNameRequired': '用例名称不能为空',
|
'case.caseNameRequired': '用例名称不能为空',
|
||||||
'case.caseNamePlaceholder': '请输入用例名称',
|
'case.caseNamePlaceholder': '请输入用例名称',
|
||||||
|
|
|
@ -54,8 +54,7 @@
|
||||||
<executeButton
|
<executeButton
|
||||||
ref="executeRef"
|
ref="executeRef"
|
||||||
class="ml-[16px]"
|
class="ml-[16px]"
|
||||||
is-emit
|
:execute-loading="requestVModel.executeLoading"
|
||||||
:detail="requestVModel"
|
|
||||||
@execute="handleExecute"
|
@execute="handleExecute"
|
||||||
@stop-debug="stopDebug"
|
@stop-debug="stopDebug"
|
||||||
/>
|
/>
|
||||||
|
@ -99,12 +98,10 @@
|
||||||
uploadTempFileCase,
|
uploadTempFileCase,
|
||||||
} from '@/api/modules/api-test/management';
|
} from '@/api/modules/api-test/management';
|
||||||
import { getSocket } from '@/api/modules/project-management/commonScript';
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import { getLocalConfig } from '@/api/modules/user/index';
|
|
||||||
import { characterLimit, getGenerateId } 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';
|
||||||
import { LocalConfig } from '@/models/user';
|
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
RequestComposition,
|
RequestComposition,
|
||||||
|
@ -221,18 +218,6 @@
|
||||||
const requestAndResponseRef = ref<InstanceType<typeof requestAndResponse>>();
|
const requestAndResponseRef = ref<InstanceType<typeof requestAndResponse>>();
|
||||||
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
||||||
|
|
||||||
const apiLocalExec = ref<Record<string, any> | LocalConfig | undefined>({});
|
|
||||||
async function initLocalConfig() {
|
|
||||||
try {
|
|
||||||
const res = await getLocalConfig();
|
|
||||||
apiLocalExec.value = res.find((e) => e.type === 'API');
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
provide('apiLocalExec', readonly(apiLocalExec));
|
|
||||||
|
|
||||||
const isShowEditStepNameInput = ref(false);
|
const isShowEditStepNameInput = ref(false);
|
||||||
const stepNameInputRef = ref<InputInstance>();
|
const stepNameInputRef = ref<InputInstance>();
|
||||||
function showEditScriptNameInput() {
|
function showEditScriptNameInput() {
|
||||||
|
@ -369,7 +354,6 @@
|
||||||
// 引用时,需要初始化引用的详情;复制只在第一次初始化的时候需要加载后台数据(request.request是复制请求时列表参数字段request会为 null,以此判断释放第一次初始化)
|
// 引用时,需要初始化引用的详情;复制只在第一次初始化的时候需要加载后台数据(request.request是复制请求时列表参数字段request会为 null,以此判断释放第一次初始化)
|
||||||
initQuoteCaseDetail();
|
initQuoteCaseDetail();
|
||||||
}
|
}
|
||||||
await initLocalConfig();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
<executeButton
|
<executeButton
|
||||||
ref="executeButtonRef"
|
ref="executeButtonRef"
|
||||||
:execute-loading="activeScenarioTab.executeLoading"
|
:execute-loading="activeScenarioTab.executeLoading"
|
||||||
is-emit
|
|
||||||
@execute="handleExecute"
|
@execute="handleExecute"
|
||||||
@stop-debug="handleStopExecute"
|
@stop-debug="handleStopExecute"
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue