feat(接口管理): 接口定义响应内容调整
This commit is contained in:
parent
05638aab20
commit
5e17fe21cb
|
@ -1,6 +1,6 @@
|
|||
export interface TabItem {
|
||||
id: string | number;
|
||||
label: string;
|
||||
label?: string;
|
||||
closable?: boolean;
|
||||
unSaved?: boolean; // 未保存
|
||||
draggable?: boolean;
|
||||
|
|
|
@ -414,18 +414,13 @@ export interface ResponseDefinitionBody {
|
|||
rawBody: ExecuteValueBody;
|
||||
binaryBody: ExecuteBinaryBody;
|
||||
}
|
||||
export interface ResponseDefinitionHeader extends EnableKeyValueParam {
|
||||
notBlankValue: boolean;
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
// 响应定义
|
||||
export interface ResponseDefinition {
|
||||
id: string | number;
|
||||
statusCode: string | number;
|
||||
defaultFlag: boolean; // 默认响应标志
|
||||
name: string; // 响应名称
|
||||
headers: ResponseDefinitionHeader[];
|
||||
headers: KeyValueParam[];
|
||||
body: ResponseDefinitionBody;
|
||||
[key: string]: any; // 用于前端渲染时填充的自定义信息,后台无此字段
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
EnableKeyValueParam,
|
||||
ExecuteRequestCommonParam,
|
||||
ExecuteRequestFormBodyFormValue,
|
||||
ResponseDefinition,
|
||||
} from '@/models/apiTest/common';
|
||||
import { RequestContentTypeEnum, RequestParamsType, ResponseBodyFormat, ResponseComposition } from '@/enums/apiEnum';
|
||||
|
||||
|
@ -42,10 +43,10 @@ export const defaultRequestParamsItem: ExecuteRequestCommonParam = {
|
|||
};
|
||||
|
||||
// 请求的响应 response 默认的响应信息项
|
||||
export const defaultResponseItem = {
|
||||
export const defaultResponseItem: ResponseDefinition = {
|
||||
id: new Date().getTime(),
|
||||
label: 'apiTestManagement.response',
|
||||
name: 'apiTestManagement.response',
|
||||
label: 'apiTestManagement.response',
|
||||
closable: false,
|
||||
statusCode: 200,
|
||||
defaultFlag: true,
|
||||
|
@ -72,3 +73,6 @@ export const defaultResponseItem = {
|
|||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 请求的响应 response 的响应状态码集合
|
||||
export const statusCodes = [200, 201, 202, 203, 204, 205, 400, 401, 402, 403, 404, 405, 500, 501, 502, 503, 504, 505];
|
||||
|
|
|
@ -102,15 +102,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 接口定义多出一个输入框占高度 40px -->
|
||||
<div ref="splitContainerRef" :class="`${props.isDefinition ? 'h-[calc(100%-80px)]' : 'h-[calc(100%-40px)]'}`">
|
||||
<div ref="splitContainerRef" :class="splitContainerClass">
|
||||
<MsSplitBox
|
||||
ref="splitBoxRef"
|
||||
v-model:size="splitBoxSize"
|
||||
:max="0.98"
|
||||
:max="!showResponse ? 1 : 0.98"
|
||||
min="10px"
|
||||
:direction="activeLayout"
|
||||
second-container-class="!overflow-y-hidden"
|
||||
:class="!showResponse ? 'hidden-second' : ''"
|
||||
@expand-change="handleExpandChange"
|
||||
>
|
||||
<template #first>
|
||||
|
@ -204,13 +204,14 @@
|
|||
</template>
|
||||
<template #second>
|
||||
<response
|
||||
v-show="showResponse"
|
||||
v-model:active-layout="activeLayout"
|
||||
v-model:active-tab="requestVModel.responseActiveTab"
|
||||
:is-expanded="isExpanded"
|
||||
:response-definition="requestVModel.responseDefinition"
|
||||
:hide-layout-switch="props.hideResponseLayoutSwitch"
|
||||
:request-task-result="requestVModel.response"
|
||||
:is-edit="props.isDefinition"
|
||||
:is-edit="props.isDefinition && isHttpProtocol"
|
||||
:upload-temp-file-api="props.uploadTempFileApi"
|
||||
:loading="requestVModel.executeLoading || loading"
|
||||
@change-expand="changeExpand"
|
||||
|
@ -547,9 +548,16 @@
|
|||
const formData = tempForm || requestVModel.value;
|
||||
if (fApi.value) {
|
||||
const form = {};
|
||||
pluginScriptMap.value[requestVModel.value.protocol].apiDebugFields?.forEach((key) => {
|
||||
form[key] = formData[key];
|
||||
});
|
||||
if (props.isDefinition) {
|
||||
// 接口定义使用接口定义的字段集
|
||||
pluginScriptMap.value[requestVModel.value.protocol].apiDefinitionFields?.forEach((key) => {
|
||||
form[key] = formData[key];
|
||||
});
|
||||
} else {
|
||||
pluginScriptMap.value[requestVModel.value.protocol].apiDebugFields?.forEach((key) => {
|
||||
form[key] = formData[key];
|
||||
});
|
||||
}
|
||||
fApi.value?.setValue(form);
|
||||
}
|
||||
});
|
||||
|
@ -654,10 +662,22 @@
|
|||
handleActiveDebugChange();
|
||||
}
|
||||
|
||||
const splitBoxSize = ref<string | number>(0.6);
|
||||
const showResponse = computed(
|
||||
() =>
|
||||
isHttpProtocol.value ||
|
||||
!props.isDefinition ||
|
||||
requestVModel.value.response?.requestResults[0]?.responseResult.responseCode
|
||||
);
|
||||
const splitBoxSize = ref<string | number>(!showResponse.value ? 1 : 0.6);
|
||||
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
|
||||
const splitContainerRef = ref<HTMLElement>();
|
||||
const secondBoxHeight = ref(0);
|
||||
const splitContainerClass = computed(() => {
|
||||
if (!showResponse.value) {
|
||||
return 'h-full';
|
||||
}
|
||||
return 'h-[calc(100%-40px)]';
|
||||
});
|
||||
|
||||
watch(
|
||||
() => splitBoxSize.value,
|
||||
|
@ -1056,4 +1076,9 @@
|
|||
:deep(.arco-tabs-tab) {
|
||||
@apply leading-none;
|
||||
}
|
||||
.hidden-second {
|
||||
:deep(.arco-split-trigger) {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<template #label="{ tab }">
|
||||
<div class="response-tab">
|
||||
<div v-if="tab.defaultFlag" class="response-tab-default-icon"></div>
|
||||
{{ t(tab.label) }}({{ tab.statusCode }})
|
||||
{{ t(tab.label || '') }}({{ tab.statusCode }})
|
||||
<MsMoreAction
|
||||
:list="
|
||||
tab.defaultFlag
|
||||
|
@ -23,8 +23,8 @@
|
|||
<popConfirm
|
||||
v-model:visible="tab.showRenamePopConfirm"
|
||||
mode="tabRename"
|
||||
:field-config="{ field: t(tab.label) }"
|
||||
:all-names="responseTabs.map((e) => t(tab.label))"
|
||||
:field-config="{ field: t(tab.label || '') }"
|
||||
:all-names="responseTabs.map((e) => t(tab.label || ''))"
|
||||
:popup-offset="20"
|
||||
@rename-finish="
|
||||
(val) => {
|
||||
|
@ -64,33 +64,33 @@
|
|||
<a-tab-pane v-for="item of responseCompositionTabList" :key="item.value" :title="item.label" />
|
||||
</a-tabs>
|
||||
<div class="response-container">
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<a-radio-group
|
||||
v-model:model-value="activeResponse.body.bodyType"
|
||||
type="button"
|
||||
size="small"
|
||||
@change="(val) => changeBodyFormat(val as ResponseBodyFormat)"
|
||||
>
|
||||
<a-radio v-for="item of ResponseBodyFormat" :key="item" :value="item">
|
||||
{{ ResponseBodyFormat[item].toLowerCase() }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
<div v-if="activeResponse.body.bodyType === ResponseBodyFormat.JSON" class="ml-auto flex items-center">
|
||||
<template v-if="activeResponse.responseActiveTab === ResponseComposition.BODY">
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<a-radio-group
|
||||
v-model:model-value="activeResponse.body.jsonBody.enableJsonSchema"
|
||||
size="mini"
|
||||
@change="emit('change')"
|
||||
v-model:model-value="activeResponse.body.bodyType"
|
||||
type="button"
|
||||
size="small"
|
||||
@change="(val) => changeBodyFormat(val as ResponseBodyFormat)"
|
||||
>
|
||||
<a-radio :value="false">Json</a-radio>
|
||||
<a-radio class="mr-0" :value="true"> Json Schema </a-radio>
|
||||
<a-radio v-for="item of ResponseBodyFormat" :key="item" :value="item">
|
||||
{{ ResponseBodyFormat[item].toLowerCase() }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="activeResponse.body.jsonBody.enableTransition" size="small" type="line" />
|
||||
{{ t('apiTestManagement.dynamicConversion') }}
|
||||
<div v-if="activeResponse.body.bodyType === ResponseBodyFormat.JSON" class="ml-auto flex items-center">
|
||||
<a-radio-group
|
||||
v-model:model-value="activeResponse.body.jsonBody.enableJsonSchema"
|
||||
size="mini"
|
||||
@change="emit('change')"
|
||||
>
|
||||
<a-radio :value="false">Json</a-radio>
|
||||
<a-radio class="mr-0" :value="true"> Json Schema </a-radio>
|
||||
</a-radio-group>
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="activeResponse.body.jsonBody.enableTransition" size="small" type="line" />
|
||||
{{ t('apiTestManagement.dynamicConversion') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="activeResponse.responseActiveTab === ResponseComposition.BODY">
|
||||
<div
|
||||
v-if="
|
||||
[ResponseBodyFormat.JSON, ResponseBodyFormat.XML, ResponseBodyFormat.RAW].includes(
|
||||
|
@ -133,6 +133,20 @@
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<paramTable
|
||||
v-else-if="activeResponse.responseActiveTab === ResponseComposition.HEADER"
|
||||
v-model:params="activeResponse.headers"
|
||||
:columns="columns"
|
||||
:default-param-item="[
|
||||
{
|
||||
key: '',
|
||||
value: '',
|
||||
},
|
||||
]"
|
||||
:selectable="false"
|
||||
@change="emit('change')"
|
||||
/>
|
||||
<a-select v-else v-model:model-value="activeResponse.statusCode" :options="statusCodeOptions" class="w-[200px]" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -143,12 +157,11 @@
|
|||
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
|
||||
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
import MsAddAttachment from '@/components/business/ms-add-attachment/index.vue';
|
||||
import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
import popConfirm from '@/views/api-test/components/popConfirm.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -157,7 +170,7 @@
|
|||
import { ResponseDefinition } from '@/models/apiTest/common';
|
||||
import { ResponseBodyFormat, ResponseComposition } from '@/enums/apiEnum';
|
||||
|
||||
import { defaultResponseItem } from '../../config';
|
||||
import { defaultResponseItem, statusCodes } from '../../config';
|
||||
|
||||
const props = defineProps<{
|
||||
responseDefinition: ResponseDefinition[];
|
||||
|
@ -338,42 +351,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
const columns: ParamTableColumn[] = [
|
||||
{
|
||||
title: 'apiTestDebug.content',
|
||||
dataIndex: 'content',
|
||||
showTooltip: true,
|
||||
title: 'apiTestManagement.paramName',
|
||||
dataIndex: 'key',
|
||||
slotName: 'key',
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.status',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'desc',
|
||||
showTooltip: true,
|
||||
title: 'apiTestManagement.paramVal',
|
||||
dataIndex: 'value',
|
||||
slotName: 'value',
|
||||
isNormal: true,
|
||||
},
|
||||
];
|
||||
const { propsRes, propsEvent } = useTable(() => Promise.resolve([]), {
|
||||
scroll: { x: '100%' },
|
||||
columns,
|
||||
});
|
||||
propsRes.value.data = [
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
content: 'Response Code equals: 200',
|
||||
status: 1,
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
content: '$.users[1].age REGEX: 31',
|
||||
status: 0,
|
||||
desc: `Value expected to match regexp '31', but it did not match: '30' match: '30'`,
|
||||
},
|
||||
] as any;
|
||||
|
||||
const statusCodeOptions = statusCodes.map((e) => ({
|
||||
label: e.toString(),
|
||||
value: e,
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -131,23 +131,6 @@
|
|||
<a-tab-pane v-if="!activeApiTab.isNew" key="case" :title="t('apiTestManagement.case')" class="ms-api-tab-pane">
|
||||
</a-tab-pane>
|
||||
<a-tab-pane v-if="!activeApiTab.isNew" key="mock" title="MOCK" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<template #extra>
|
||||
<div class="flex items-center gap-[8px] pr-[24px]">
|
||||
<a-button type="outline" class="arco-btn-outline--secondary !p-[8px]">
|
||||
<template #icon>
|
||||
<icon-location class="text-[var(--color-text-4)]" />
|
||||
</template>
|
||||
</a-button>
|
||||
<MsSelect
|
||||
v-model:model-value="checkedEnv"
|
||||
mode="static"
|
||||
:options="envOptions"
|
||||
class="!w-[150px]"
|
||||
:search-keys="['label']"
|
||||
allow-search
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -163,7 +146,6 @@
|
|||
// import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import MsSelect from '@/components/business/ms-select';
|
||||
import addDependencyDrawer from './addDependencyDrawer.vue';
|
||||
import apiTable from './apiTable.vue';
|
||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||
|
@ -402,26 +384,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const checkedEnv = ref('DEV');
|
||||
const envOptions = ref([
|
||||
{
|
||||
label: 'DEV',
|
||||
value: 'DEV',
|
||||
},
|
||||
{
|
||||
label: 'TEST',
|
||||
value: 'TEST',
|
||||
},
|
||||
{
|
||||
label: 'PRE',
|
||||
value: 'PRE',
|
||||
},
|
||||
{
|
||||
label: 'PROD',
|
||||
value: 'PROD',
|
||||
},
|
||||
]);
|
||||
|
||||
// const fApi = ref();
|
||||
// const options = {
|
||||
// form: {
|
||||
|
|
|
@ -13,10 +13,28 @@
|
|||
<a-tab-pane key="case" title="CASE" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<a-tab-pane key="mock" title="MOCK" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<a-tab-pane key="doc" :title="t('apiTestManagement.doc')" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<template #extra>
|
||||
<div class="flex items-center gap-[8px] pr-[24px]">
|
||||
<a-button type="outline" class="arco-btn-outline--secondary !p-[8px]">
|
||||
<template #icon>
|
||||
<icon-location class="text-[var(--color-text-4)]" />
|
||||
</template>
|
||||
</a-button>
|
||||
<MsSelect
|
||||
v-model:model-value="checkedEnv"
|
||||
mode="static"
|
||||
:options="envOptions"
|
||||
class="!w-[150px]"
|
||||
:search-keys="['label']"
|
||||
allow-search
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MsSelect from '@/components/business/ms-select';
|
||||
import api from './api/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -44,6 +62,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
const checkedEnv = ref('DEV');
|
||||
const envOptions = ref([
|
||||
{
|
||||
label: 'DEV',
|
||||
value: 'DEV',
|
||||
},
|
||||
{
|
||||
label: 'TEST',
|
||||
value: 'TEST',
|
||||
},
|
||||
{
|
||||
label: 'PRE',
|
||||
value: 'PRE',
|
||||
},
|
||||
{
|
||||
label: 'PROD',
|
||||
value: 'PROD',
|
||||
},
|
||||
]);
|
||||
|
||||
defineExpose({
|
||||
newTab,
|
||||
});
|
||||
|
|
|
@ -88,6 +88,8 @@ export default {
|
|||
'apiTestManagement.response': '响应{count}',
|
||||
'apiTestManagement.responseCode': '响应码',
|
||||
'apiTestManagement.dynamicConversion': '动态转换',
|
||||
'apiTestManagement.expandApi': '展开全部请求',
|
||||
'apiTestManagement.collapseApi': '收起全部请求',
|
||||
'apiTestManagement.expandApi': '显示全部请求',
|
||||
'apiTestManagement.collapseApi': '隐藏全部请求',
|
||||
'apiTestManagement.paramName': '参数名',
|
||||
'apiTestManagement.paramVal': '参数值',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue