feat(接口测试): 接口调试-插件详情

This commit is contained in:
baiqi 2024-02-18 18:53:25 +08:00 committed by Craftsman
parent bc98bc302a
commit aeaa61c4f3
10 changed files with 230 additions and 155 deletions

View File

@ -1,7 +1,7 @@
import MSR from '@/api/http/index';
import { GetPluginOptionsUrl, GetPluginScriptUrl, GetProtocolListUrl } from '@/api/requrls/api-test/management';
import { GetPluginOptionsParams, PluginOption, ProtocolItem } from '@/models/apiTest/common';
import { GetPluginOptionsParams, PluginConfig, PluginOption, ProtocolItem } from '@/models/apiTest/common';
// 获取协议列表
export function getProtocolList(organizationId: string) {
@ -15,5 +15,5 @@ export function getPluginOptions(data: GetPluginOptionsParams) {
// 获取插件配置
export function getPluginScript(pluginId: string) {
return MSR.get({ url: GetPluginScriptUrl, params: pluginId });
return MSR.get<PluginConfig>({ url: GetPluginScriptUrl, params: pluginId });
}

View File

@ -390,12 +390,12 @@
//
const renderMenuItem = (element, icon) =>
element?.name === SettingRouteEnum.SETTING_ORGANIZATION ? (
orgTrigger(element, menuSwitchOrgVisible, () => (
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={() => goto(element)}>
<div class="inline-flex w-[calc(100%-34px)] items-center justify-between !bg-transparent">
{t(element?.meta?.locale || '')}
{orgTrigger(element, menuSwitchOrgVisible, () => (
<div
class="!bg-transparent"
class={collapsed.value ? 'hidden' : '!bg-transparent'} //
onMouseenter={() => {
if (xPack.value) {
// xpack
@ -410,9 +410,9 @@
>
<MsIcon type="icon-icon_switch_outlined1" class="text-[var(--color-text-4)]" />
</div>
))}
</div>
</a-menu-item>
))
) : (
<a-menu-item key={element?.name} v-slots={{ icon }} onClick={() => goto(element)}>
{t(element?.meta?.locale || '')}

View File

@ -1,5 +1,12 @@
<template>
<FormCreate v-model:api="formApi" :rule="formRules" :option="props.option"></FormCreate>
<FormCreate
v-model:api="formApi"
:rule="formRules"
:option="props.option"
@mounted="handleMounted"
@reload="handleReload"
@change="handleChange"
></FormCreate>
</template>
<script setup lang="ts">
@ -12,7 +19,7 @@
import PassWord from './formcreate-password.vue';
import SearchSelect from './searchSelect.vue';
import formCreate, { FormRule } from '@form-create/arco-design';
import formCreate, { Api, FormRule } from '@form-create/arco-design';
formCreate.component('PassWord', PassWord);
formCreate.component('SearchSelect', SearchSelect);
@ -20,14 +27,14 @@
const FormCreate = formCreate.$form();
const props = defineProps<{
rule: FormRule | undefined; //
rule?: FormRule; //
option: any; //
api: any; //
api?: Api; //
}>();
const emits = defineEmits(['update:api', 'update:rule']);
const emits = defineEmits(['update:api', 'update:rule', 'mounted', 'reload', 'change']);
const formApi = ref<any>({});
const formApi = ref<Api>();
watchEffect(() => {
formApi.value = props.api;
@ -57,6 +64,18 @@
emits('update:rule', val);
}
);
function handleMounted() {
emits('mounted');
}
function handleReload() {
emits('reload');
}
function handleChange() {
emits('change');
}
</script>
<style scoped></style>

View File

@ -108,13 +108,6 @@ export const pathMap: PathMapItem[] = [
permission: [],
level: MENU_LEVEL[2],
},
{
key: 'CASE_MANAGEMENT_CASE_CREATE_SUCCESS', // 功能测试-功能用例创建成功页面
locale: 'menu.caseManagement.featureCaseCreateSuccess',
route: RouteEnum.CASE_MANAGEMENT_CASE_CREATE_SUCCESS,
permission: [],
level: MENU_LEVEL[2],
},
{
key: 'CASE_MANAGEMENT_CASE_RECYCLE', // 功能测试-功能用例-回收站
locale: 'menu.caseManagement.featureCaseRecycle',

View File

@ -16,3 +16,11 @@ export interface ProtocolItem {
polymorphicName: string;
pluginId: string;
}
// 插件配置
export interface PluginConfig {
id: string;
name: string;
options: Record<string, any>;
script: Record<string, any>[];
scriptType: string;
}

View File

@ -1,6 +1,6 @@
<template>
<div class="mb-[8px] flex items-center justify-between">
<a-dropdown @select="addPrecondition">
<a-dropdown @select="(val) => addCondition(val as ConditionType)">
<a-button type="outline">
<template #icon>
<icon-plus :size="14" />
@ -108,10 +108,9 @@ org.apache.http.client.method . . . '' at line number 2
`);
/**
* 添加前置条件
* @param value script | sql | waitTime
* 添加条件
*/
function addPrecondition(value: string | number | Record<string, any> | undefined) {
function addCondition(value: ConditionType) {
const id = new Date().getTime();
switch (value) {
case RequestConditionProcessor.SCRIPT:

View File

@ -4,20 +4,21 @@
<div class="mb-[8px] flex items-center justify-between">
<div class="flex flex-1">
<a-select
v-model:model-value="requsetVModel.protocol"
v-model:model-value="requestVModel.protocol"
:options="protocolOptions"
:loading="protocolLoading"
:disabled="!requestVModel.isNew"
class="mr-[4px] w-[90px]"
@change="(val) => handleActiveDebugProtocolChange(val as string)"
/>
<a-input-group v-if="isHttpProtocol" class="flex-1">
<apiMethodSelect
v-model:model-value="requsetVModel.method"
v-model:model-value="requestVModel.method"
class="w-[140px]"
@change="handleActiveDebugChange"
/>
<a-input
v-model:model-value="requsetVModel.url"
v-model:model-value="requestVModel.url"
:max-length="255"
:placeholder="t('apiTestDebug.urlPlaceholder')"
@change="handleActiveDebugChange"
@ -26,8 +27,8 @@
</div>
<div class="ml-[16px]">
<a-dropdown-button
:button-props="{ loading: requsetVModel.executeLoading }"
:disabled="requsetVModel.executeLoading"
:button-props="{ loading: requestVModel.executeLoading }"
:disabled="requestVModel.executeLoading"
class="exec-btn"
@click="execute"
@select="execute"
@ -42,7 +43,7 @@
</a-doption>
</template>
</a-dropdown-button>
<a-dropdown v-if="props.isDefiniton" @select="handleSelect">
<a-dropdown v-if="props.isDefinition" @select="handleSelect">
<a-button type="secondary">{{ t('common.save') }}</a-button>
<template #content>
<a-doption value="save">{{ t('common.save') }}</a-doption>
@ -58,8 +59,8 @@
</div>
</div>
<a-input
v-if="props.isDefiniton"
v-model:model-value="requsetVModel.name"
v-if="props.isDefinition"
v-model:model-value="requestVModel.name"
:max-length="255"
:placeholder="t('apiTestManagement.apiNamePlaceholder')"
@change="handleActiveDebugChange"
@ -82,65 +83,75 @@
}`"
>
<div>
<a-tabs v-model:active-key="requsetVModel.activeTab" class="no-content mb-[16px]">
<a-tabs v-model:active-key="requestVModel.activeTab" class="no-content mb-[16px]">
<a-tab-pane v-for="item of contentTabList" :key="item.value" :title="item.label" />
</a-tabs>
</div>
<div class="tab-pane-container">
<template v-if="isInitPluginForm || requsetVModel.activeTab === RequestComposition.PLUGIN">
<a-spin v-show="requsetVModel.activeTab === RequestComposition.PLUGIN" :loading="pluginLoading">
<MsFormCreate v-model:api="fApi" :rule="currentPluginScript" :option="options" />
<template v-if="requestVModel.activeTab === RequestComposition.PLUGIN || isInitPluginForm">
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@mounted="() => (isInitPluginForm = true)"
@change="handlePluginFormChange"
/>
</a-spin>
</template>
<debugHeader
v-if="requsetVModel.activeTab === RequestComposition.HEADER"
v-model:params="requsetVModel.headers"
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<debugBody
v-else-if="requsetVModel.activeTab === RequestComposition.BODY"
v-model:params="requsetVModel.body"
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<debugQuery
v-else-if="requsetVModel.activeTab === RequestComposition.QUERY"
v-model:params="requsetVModel.query"
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<debugRest
v-else-if="requsetVModel.activeTab === RequestComposition.REST"
v-model:params="requsetVModel.rest"
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requsetVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requsetVModel.children[0].preProcessorConfig"
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requsetVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requsetVModel.children[0].postProcessorConfig"
:response="requsetVModel.response.requestResults[0]?.responseResult.body"
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="requestVModel.response.requestResults[0]?.responseResult.body"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<debugAuth
v-else-if="requsetVModel.activeTab === RequestComposition.AUTH"
v-model:params="requsetVModel.authConfig"
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
@change="handleActiveDebugChange"
/>
<debugSetting
v-else-if="requsetVModel.activeTab === RequestComposition.SETTING"
v-model:params="requsetVModel.otherConfig"
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
@change="handleActiveDebugChange"
/>
</div>
@ -149,10 +160,10 @@
<template #second>
<response
v-model:active-layout="activeLayout"
v-model:active-tab="requsetVModel.responseActiveTab"
v-model:active-tab="requestVModel.responseActiveTab"
:is-expanded="isExpanded"
:response="requsetVModel.response"
:hide-layout-swicth="props.hideResponseLayoutSwicth"
:response="requestVModel.response"
:hide-layout-swicth="props.hideResponseLayoutSwitch"
@change-expand="changeExpand"
@change-layout="handleActiveLayoutChange"
/>
@ -223,9 +234,12 @@
import { scrollIntoView } from '@/utils/dom';
import { registerCatchSaveShortcut, removeCatchSaveShortcut } from '@/utils/event';
import { PluginConfig } from '@/models/apiTest/common';
import { ExecuteHTTPRequestFullParams } from '@/models/apiTest/debug';
import { ModuleTreeNode } from '@/models/common';
import { RequestComposition } from '@/enums/apiEnum';
import { RequestComposition, RequestMethods } from '@/enums/apiEnum';
import { Api } from '@form-create/arco-design';
// Http
const debugHeader = defineAsyncComponent(() => import('./header.vue'));
@ -233,14 +247,18 @@
const debugQuery = defineAsyncComponent(() => import('./query.vue'));
const debugRest = defineAsyncComponent(() => import('./rest.vue'));
export type RequestParam = ExecuteHTTPRequestFullParams & TabItem & Record<string, any>;
export interface RequestCustomAttr {
isNew: boolean;
protocol: string;
}
export type RequestParam = ExecuteHTTPRequestFullParams & RequestCustomAttr & TabItem;
const props = defineProps<{
request: RequestParam; //
moduleTree: ModuleTreeNode[]; //
detailLoading: boolean; //
isDefiniton?: boolean; //
hideResponseLayoutSwicth?: boolean; //
isDefinition?: boolean; //
hideResponseLayoutSwitch?: boolean; //
executeApi: (...args) => Promise<any>; //
createApi: (...args) => Promise<any>; //
updateApi: (...args) => Promise<any>; //
@ -250,33 +268,20 @@
const appStore = useAppStore();
const { t } = useI18n();
const loading = defineModel('detailLoading', { default: false });
const requsetVModel = defineModel<RequestParam>('request', { required: true });
requsetVModel.value.executeLoading = false; // loading
const isHttpProtocol = computed(() => requsetVModel.value.protocol === 'HTTP');
const isInitPluginForm = ref(false); //
const temporyResponseMap = {}; // websockettab
watch(
() => requsetVModel.value.protocol,
(val) => {
if (val !== 'HTTP') {
isInitPluginForm.value = true;
}
},
{
immediate: true,
}
);
const loading = defineModel<boolean>('detailLoading', { default: false });
const requestVModel = defineModel<RequestParam>('request', { required: true });
requestVModel.value.executeLoading = false; // loading
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
const temporaryResponseMap = {}; // websockettab
watch(
() => props.request.id,
() => {
if (temporyResponseMap[props.request.reportId]) {
if (temporaryResponseMap[props.request.reportId]) {
//
requsetVModel.value.response = temporyResponseMap[props.request.reportId];
requsetVModel.value.executeLoading = false;
delete temporyResponseMap[props.request.reportId];
requestVModel.value.response = temporaryResponseMap[props.request.reportId];
requestVModel.value.executeLoading = false;
delete temporaryResponseMap[props.request.reportId];
}
}
);
@ -284,7 +289,7 @@
function handleActiveDebugChange() {
if (!loading.value) {
// change
requsetVModel.value.unSaved = true;
requestVModel.value.unSaved = true;
}
}
@ -382,22 +387,63 @@
}
}
const pluginScriptMap = ref<Record<string, any>>({}); //
const pluginScriptMap = ref<Record<string, PluginConfig>>({}); //
const temporaryPluginFormMap: Record<string, any> = {}; // tab
const pluginLoading = ref(false);
const currentPluginScript = computed<Record<string, any>[]>(
() => pluginScriptMap.value[requsetVModel.value.protocol] || []
const isInitPluginForm = ref(false);
const fApi = ref<Api>();
const currentPluginOptions = computed<Record<string, any>>(
() => pluginScriptMap.value[requestVModel.value.protocol]?.options || {}
);
const currentPluginScript = computed<Record<string, any>[]>(
() => pluginScriptMap.value[requestVModel.value.protocol]?.script || []
);
//
const handlePluginFormChange = debounce(() => {
temporaryPluginFormMap[requestVModel.value.id] = fApi.value?.formData();
handleActiveDebugChange();
}, 300);
/**
* 设置插件表单数据
*/
function setPluginFormData() {
const tempForm = temporaryPluginFormMap[requestVModel.value.id];
if (tempForm || !requestVModel.value.isNew) {
//
fApi.value?.nextRefresh(() => {
fApi.value?.reload(currentPluginScript.value);
const formData = tempForm || requestVModel.value;
if (fApi.value) {
const form = {};
fApi.value.fields().forEach((key) => {
form[key] = formData[key];
});
fApi.value?.setValue(form);
}
});
} else {
// form-create tab
nextTick(() => {
fApi.value?.resetFields();
});
}
}
async function initPluginScript() {
if (pluginScriptMap.value[requsetVModel.value.protocol] !== undefined) {
if (pluginScriptMap.value[requestVModel.value.protocol] !== undefined) {
setPluginFormData();
//
return;
}
try {
pluginLoading.value = true;
const res = await getPluginScript(
protocolOptions.value.find((e) => e.value === requsetVModel.value.protocol)?.pluginId || ''
protocolOptions.value.find((e) => e.value === requestVModel.value.protocol)?.pluginId || ''
);
pluginScriptMap.value[requsetVModel.value.protocol] = res.script;
pluginScriptMap.value[requestVModel.value.protocol] = res;
setPluginFormData();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -408,28 +454,30 @@
function handleActiveDebugProtocolChange(val: string) {
if (val !== 'HTTP') {
requsetVModel.value.activeTab = RequestComposition.PLUGIN;
requestVModel.value.activeTab = RequestComposition.PLUGIN;
initPluginScript();
} else {
requsetVModel.value.activeTab = RequestComposition.HEADER;
requestVModel.value.activeTab = RequestComposition.HEADER;
if (!Object.values(RequestMethods).includes(requestVModel.value.method)) {
// HTTP GET
requestVModel.value.method = RequestMethods.GET;
}
}
handleActiveDebugChange();
}
const fApi = ref();
const options = {
form: {
labelAlign: 'right',
autoLabelWidth: true,
size: 'small',
hideRequiredAsterisk: false,
showMessage: true,
inlineMessage: false,
scrollToFirstError: true,
watch(
() => requestVModel.value.id,
() => {
if (requestVModel.value.protocol !== 'HTTP') {
requestVModel.value.activeTab = RequestComposition.PLUGIN;
initPluginScript();
}
},
submitBtn: false,
resetBtn: false,
};
{
immediate: true,
}
);
const splitBoxSize = ref<string | number>(0.6);
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
@ -467,7 +515,7 @@
} else {
splitBoxRef.value?.collapse(
splitContainerRef.value
? `${splitContainerRef.value.clientHeight - (props.hideResponseLayoutSwicth ? 37 : 42)}px`
? `${splitContainerRef.value.clientHeight - (props.hideResponseLayoutSwitch ? 37 : 42)}px`
: 0
);
}
@ -486,13 +534,13 @@
websocket.value.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.msgType === 'EXEC_RESULT') {
if (requsetVModel.value.reportId === data.reportId) {
if (requestVModel.value.reportId === data.reportId) {
// tabtab
requsetVModel.value.response = data.taskResult;
requsetVModel.value.executeLoading = false;
requestVModel.value.response = data.taskResult;
requestVModel.value.executeLoading = false;
} else {
// tab
temporyResponseMap[data.reportId] = data.taskResult;
temporaryResponseMap[data.reportId] = data.taskResult;
}
}
});
@ -500,46 +548,46 @@
function makeRequestParams() {
const polymorphicName = protocolOptions.value.find(
(e) => e.value === requsetVModel.value.protocol
(e) => e.value === requestVModel.value.protocol
)?.polymorphicName; //
let requestParams;
if (isHttpProtocol.value) {
requestParams = {
authConfig: requsetVModel.value.authConfig,
authConfig: requestVModel.value.authConfig,
body: {
...requsetVModel.value.body,
...requestVModel.value.body,
binaryBody: undefined,
formDataBody: {
formValues: requsetVModel.value.body.formDataBody.formValues.filter(
(e, i) => i !== requsetVModel.value.body.formDataBody.formValues.length - 1
formValues: requestVModel.value.body.formDataBody.formValues.filter(
(e, i) => i !== requestVModel.value.body.formDataBody.formValues.length - 1
), //
},
wwwFormBody: {
formValues: requsetVModel.value.body.wwwFormBody.formValues.filter(
(e, i) => i !== requsetVModel.value.body.wwwFormBody.formValues.length - 1
formValues: requestVModel.value.body.wwwFormBody.formValues.filter(
(e, i) => i !== requestVModel.value.body.wwwFormBody.formValues.length - 1
), //
},
}, // TODO:binaryBody
headers: requsetVModel.value.headers.filter((e, i) => i !== requsetVModel.value.headers.length - 1), //
method: requsetVModel.value.method,
otherConfig: requsetVModel.value.otherConfig,
path: requsetVModel.value.url,
query: requsetVModel.value.query.filter((e, i) => i !== requsetVModel.value.query.length - 1), //
rest: requsetVModel.value.rest.filter((e, i) => i !== requsetVModel.value.rest.length - 1), //
url: requsetVModel.value.url,
headers: requestVModel.value.headers.filter((e, i) => i !== requestVModel.value.headers.length - 1), //
method: requestVModel.value.method,
otherConfig: requestVModel.value.otherConfig,
path: requestVModel.value.url,
query: requestVModel.value.query.filter((e, i) => i !== requestVModel.value.query.length - 1), //
rest: requestVModel.value.rest.filter((e, i) => i !== requestVModel.value.rest.length - 1), //
url: requestVModel.value.url,
polymorphicName,
};
} else {
requestParams = {
...fApi.value.form,
...fApi.value?.formData(),
polymorphicName,
};
}
reportId.value = getGenerateId();
requsetVModel.value.reportId = reportId.value; // ID
requestVModel.value.reportId = reportId.value; // ID
debugSocket(); // websocket
return {
id: requsetVModel.value.id.toString(),
id: requestVModel.value.id.toString(),
reportId: reportId.value,
environmentId: '',
tempFileIds: [],
@ -553,8 +601,8 @@
enableGlobal: false,
assertions: [],
},
postProcessorConfig: requsetVModel.value.children[0].postProcessorConfig,
preProcessorConfig: requsetVModel.value.children[0].preProcessorConfig,
postProcessorConfig: requestVModel.value.children[0].postProcessorConfig,
preProcessorConfig: requestVModel.value.children[0].preProcessorConfig,
},
],
},
@ -570,27 +618,27 @@
// TODO:&
if (isHttpProtocol.value) {
try {
requsetVModel.value.executeLoading = true;
requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requsetVModel.value.executeLoading = false;
requestVModel.value.executeLoading = false;
}
} else {
//
fApi.value?.validate(async (valid) => {
if (valid === true) {
try {
requsetVModel.value.executeLoading = true;
requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requsetVModel.value.executeLoading = false;
requestVModel.value.executeLoading = false;
}
} else {
requsetVModel.value.activeTab = RequestComposition.PLUGIN;
requestVModel.value.activeTab = RequestComposition.PLUGIN;
nextTick(() => {
scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
});
@ -602,7 +650,7 @@
const saveModalVisible = ref(false);
const saveModalForm = ref({
name: '',
path: requsetVModel.value.url || '',
path: requestVModel.value.url || '',
moduleId: 'root',
});
const saveModalFormRef = ref<FormInstance>();
@ -630,8 +678,8 @@
await fApi.value?.validate();
}
saveModalForm.value = {
name: requsetVModel.value.name || '',
path: requsetVModel.value.url || '',
name: requestVModel.value.name || '',
path: requestVModel.value.url || '',
moduleId: 'root',
};
saveModalVisible.value = true;
@ -639,7 +687,7 @@
// eslint-disable-next-line no-console
console.log(error);
//
requsetVModel.value.activeTab = RequestComposition.PLUGIN;
requestVModel.value.activeTab = RequestComposition.PLUGIN;
nextTick(() => {
scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
});
@ -668,13 +716,13 @@
if (!errors) {
try {
saveLoading.value = true;
if (requsetVModel.value.isNew) {
if (requestVModel.value.isNew) {
//
await props.createApi({
...makeRequestParams(),
...saveModalForm.value,
protocol: requsetVModel.value.protocol,
method: isHttpProtocol.value ? requsetVModel.value.method : requsetVModel.value.protocol,
protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
uploadFileIds: [],
linkFileIds: [],
});
@ -682,8 +730,8 @@
await props.updateApi({
...makeRequestParams(),
...saveModalForm.value,
protocol: requsetVModel.value.protocol,
method: isHttpProtocol.value ? requsetVModel.value.method : requsetVModel.value.protocol,
protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
uploadFileIds: [],
linkFileIds: [],
deleteFileIds: [], // TODO:
@ -693,11 +741,11 @@
saveLoading.value = false;
saveModalVisible.value = false;
done(true);
requsetVModel.value.unSaved = false;
requsetVModel.value.name = saveModalForm.value.name;
requsetVModel.value.label = saveModalForm.value.name;
requestVModel.value.unSaved = false;
requestVModel.value.name = saveModalForm.value.name;
requestVModel.value.label = saveModalForm.value.name;
emit('addDone');
Message.success(requsetVModel.value.isNew ? t('common.saveSuccess') : t('common.updateSuccess'));
Message.success(requestVModel.value.isNew ? t('common.saveSuccess') : t('common.updateSuccess'));
} catch (error) {
saveLoading.value = false;
}
@ -712,13 +760,13 @@
});
onMounted(() => {
if (!props.isDefiniton) {
if (!props.isDefinition) {
registerCatchSaveShortcut(handleSaveShortcut);
}
});
onBeforeUnmount(() => {
if (!props.isDefiniton) {
if (!props.isDefinition) {
removeCatchSaveShortcut(handleSaveShortcut);
}
});

View File

@ -16,9 +16,15 @@
<template #second>
<div class="flex h-full flex-col">
<div class="border-b border-[var(--color-text-n8)] p-[24px_24px_16px_24px]">
<MsEditableTab v-model:active-tab="activeDebug" v-model:tabs="debugTabs" at-least-one @add="addDebugTab">
<MsEditableTab
v-model:active-tab="activeDebug"
v-model:tabs="debugTabs"
:limit="10"
at-least-one
@add="addDebugTab"
>
<template #label="{ tab }">
<apiMethodName v-if="isHttpProtocol" :method="tab.method" class="mr-[4px]" />
<apiMethodName :method="tab.protocol === 'HTTP' ? tab.method : tab.protocol" class="mr-[4px]" />
{{ tab.label }}
</template>
</MsEditableTab>
@ -204,10 +210,10 @@
},
responseActiveTab: ResponseComposition.BODY,
response: cloneDeep(defaultResponse),
isNew: true,
};
const debugTabs = ref<RequestParam[]>([cloneDeep(defaultDebugParams)]);
const activeDebug = ref<RequestParam>(debugTabs.value[0]);
const isHttpProtocol = computed(() => activeDebug.value.protocol === 'HTTP');
function handleActiveDebugChange() {
if (!loading.value) {

View File

@ -221,6 +221,7 @@
},
responseActiveTab: ResponseComposition.BODY,
response: cloneDeep(defaultResponse),
isNew: true,
};
function addApiTab(defaultProps?: Partial<TabItem>) {
const id = `debug-${Date.now()}`;
@ -229,6 +230,7 @@
moduleId: props.module,
label: t('apiTestDebug.newApi'),
id,
isNew: !defaultProps?.id, // tabidid
...defaultProps,
});
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1] as RequestParam;