feat(接口定义): 请求/响应头输入联想&部分高优先级 bug 修复

This commit is contained in:
baiqi 2024-03-14 15:19:23 +08:00 committed by Craftsman
parent a017474064
commit 263317e3c4
17 changed files with 222 additions and 154 deletions

View File

@ -17,6 +17,8 @@
import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index'; import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index';
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue'; import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import { responseHeaderOption } from '@/config/apiTest';
import type { ExecuteAssertionItem } from '@/models/apiTest/common'; import type { ExecuteAssertionItem } from '@/models/apiTest/common';
interface Param { interface Param {
@ -41,21 +43,6 @@
enable: true, enable: true,
}; };
const responseHeaderOption = [
{ label: 'Accept', value: 'accept' },
{ label: 'Accept-Encoding', value: 'acceptEncoding' },
{ label: 'Accept-Language', value: 'acceptLanguage' },
{ label: 'Cache-Control', value: 'cacheControl' },
{ label: 'Content-Type', value: 'contentType' },
{ label: 'Content-Length', value: 'contentLength' },
{ label: 'User-Agent', value: 'userAgent' },
{ label: 'Referer', value: 'referer' },
{ label: 'Cookie', value: 'cookie' },
{ label: 'Authorization', value: 'authorization' },
{ label: 'If-None-Match', value: 'ifNoneMatch' },
{ label: 'If-Modified-Since', value: 'ifModifiedSince' },
];
const columns: ParamTableColumn[] = [ const columns: ParamTableColumn[] = [
{ {
title: 'ms.assertion.responseHeader', // title: 'ms.assertion.responseHeader', //

View File

@ -15,6 +15,8 @@
import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index'; import { statusCodeOptions } from '@/components/pure/ms-advance-filter/index';
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue'; import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import { responseHeaderOption } from '@/config/apiTest';
interface Param { interface Param {
[key: string]: any; [key: string]: any;
variableAssertionItems: any[]; variableAssertionItems: any[];
@ -37,21 +39,6 @@
enable: true, enable: true,
}; };
const responseHeaderOption = [
{ label: 'Accept', value: 'accept' },
{ label: 'Accept-Encoding', value: 'acceptEncoding' },
{ label: 'Accept-Language', value: 'acceptLanguage' },
{ label: 'Cache-Control', value: 'cacheControl' },
{ label: 'Content-Type', value: 'contentType' },
{ label: 'Content-Length', value: 'contentLength' },
{ label: 'User-Agent', value: 'userAgent' },
{ label: 'Referer', value: 'referer' },
{ label: 'Cookie', value: 'cookie' },
{ label: 'Authorization', value: 'authorization' },
{ label: 'If-None-Match', value: 'ifNoneMatch' },
{ label: 'If-Modified-Since', value: 'ifModifiedSince' },
];
const columns: ParamTableColumn[] = [ const columns: ParamTableColumn[] = [
{ {
title: 'ms.assertion.variableName', // title: 'ms.assertion.variableName', //

View File

@ -1,5 +1,5 @@
<template> <template>
<a-breadcrumb v-if="appStore.breadcrumbList.length > 0" class="z-10 mb-[-8px] mt-[8px]"> <a-breadcrumb v-if="appStore.breadcrumbList.length > 0" class="z-10 mb-[-8px]">
<a-breadcrumb-item v-for="crumb of appStore.breadcrumbList" :key="crumb.name" @click="jumpTo(crumb)"> <a-breadcrumb-item v-for="crumb of appStore.breadcrumbList" :key="crumb.name" @click="jumpTo(crumb)">
{{ isEdit ? t(crumb.editLocale || crumb.locale) : t(crumb.locale) }} {{ isEdit ? t(crumb.editLocale || crumb.locale) : t(crumb.locale) }}
</a-breadcrumb-item> </a-breadcrumb-item>

View File

@ -10,6 +10,7 @@
props.autoHeight ? '' : 'min-h-[500px]', props.autoHeight ? '' : 'min-h-[500px]',
props.noContentPadding ? 'ms-card--noContentPadding' : 'p-[24px]', props.noContentPadding ? 'ms-card--noContentPadding' : 'p-[24px]',
props.noBottomRadius ? 'ms-card--noBottomRadius' : '', props.noBottomRadius ? 'ms-card--noBottomRadius' : '',
!props.hideFooter && !props.simple ? 'pb-[80px]' : '',
]" ]"
> >
<a-scrollbar v-if="!props.simple" :style="{ overflow: 'auto' }"> <a-scrollbar v-if="!props.simple" :style="{ overflow: 'auto' }">
@ -149,9 +150,9 @@
} }
if (props.simple) { if (props.simple) {
// //
return props.noContentPadding ? 76 + _specialHeight : 124 + _specialHeight; return props.noContentPadding ? 66 + _specialHeight : 114 + _specialHeight;
} }
return 190 + _specialHeight; return 250 + _specialHeight;
}); });
const getComputedContentStyle = computed(() => { const getComputedContentStyle = computed(() => {

View File

@ -88,6 +88,30 @@
v-model:model-value="record[item.dataIndex as string]" v-model:model-value="record[item.dataIndex as string]"
@change="() => handleFormChange(record, rowIndex, item)" @change="() => handleFormChange(record, rowIndex, item)"
/> />
<template v-else-if="item.inputType === 'autoComplete'">
<a-auto-complete
v-model:model-value="record[item.dataIndex as string]"
:data="item.autoCompleteParams?.filter((e) => e.isShow === true)"
class="ms-form-table-input"
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
:filter-option="false"
size="small"
@search="(val) => handleSearchParams(val, item)"
@change="() => handleFormChange(record, rowIndex, item)"
@select="(val) => selectAutoComplete(val, record, item)"
>
<template #option="{ data: opt }">
<div class="w-[350px]">
{{ opt.raw.value }}
<a-tooltip :content="t(opt.raw.desc)" position="bl" :mouse-enter-delay="300">
<div class="one-line-text max-w-full text-[12px] leading-[16px] text-[var(--color-text-4)]">
{{ t(opt.raw.desc) }}
</div>
</a-tooltip>
</div>
</template>
</a-auto-complete>
</template>
<template v-else-if="item.inputType === 'text'"> <template v-else-if="item.inputType === 'text'">
{{ {{
typeof item.valueFormat === 'function' ? item.valueFormat(record) : record[item.dataIndex as string] || '-' typeof item.valueFormat === 'function' ? item.valueFormat(record) : record[item.dataIndex as string] || '-'
@ -122,7 +146,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { TableColumnData, TableData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
@ -140,12 +163,14 @@
import { SelectAllEnum, TableKeyEnum } from '@/enums/tableEnum'; import { SelectAllEnum, TableKeyEnum } from '@/enums/tableEnum';
import { ActionsItem } from '../ms-table-more-action/types'; import { ActionsItem } from '../ms-table-more-action/types';
import type { SelectOptionData, TableColumnData, TableData } from '@arco-design/web-vue';
import { TableOperationColumn } from '@arco-design/web-vue/es/table/interface'; import { TableOperationColumn } from '@arco-design/web-vue/es/table/interface';
export interface FormTableColumn extends MsTableColumnData { export interface FormTableColumn extends MsTableColumnData {
enable?: boolean; // enable?: boolean; //
required?: boolean; // required?: boolean; //
inputType?: 'input' | 'select' | 'tags' | 'switch' | 'text' | 'checkbox'; // inputType?: 'input' | 'select' | 'tags' | 'switch' | 'text' | 'checkbox' | 'autoComplete'; //
autoCompleteParams?: SelectOptionData[]; //
valueFormat?: (record: Record<string, any>) => string; // inputTypetext valueFormat?: (record: Record<string, any>) => string; // inputTypetext
[key: string]: any; // [key: string]: any; //
} }
@ -330,6 +355,21 @@
emitChange('deleteParam'); emitChange('deleteParam');
} }
/**
* 搜索变量
* @param val 变量名
*/
function handleSearchParams(val: string, item: FormTableColumn) {
item.autoCompleteParams = item.autoCompleteParams?.map((e) => {
e.isShow = e.label?.includes(val);
return e;
});
}
function selectAutoComplete(val: string, record: Record<string, any>, item: FormTableColumn) {
record[item.dataIndex as string] = val;
}
await initColumns(); await initColumns();
</script> </script>
@ -381,6 +421,16 @@
} }
} }
} }
:deep(.ms-form-table-input-trigger) {
width: 350px;
.arco-select-dropdown-list {
.arco-select-option {
@apply !h-auto;
padding: 2px 8px !important;
}
}
}
:deep(.ms-form-table-input-number) { :deep(.ms-form-table-input-number) {
@apply bg-transparent pr-0; @apply bg-transparent pr-0;
.arco-input { .arco-input {

View File

@ -23,3 +23,18 @@ export const requestBodyTypeMap = {
[RequestBodyFormat.XML]: 'xml', [RequestBodyFormat.XML]: 'xml',
[RequestBodyFormat.NONE]: 'none', [RequestBodyFormat.NONE]: 'none',
}; };
// 请求/响应头选项
export const responseHeaderOption = [
{ label: 'Accept', value: 'accept' },
{ label: 'Accept-Encoding', value: 'acceptEncoding' },
{ label: 'Accept-Language', value: 'acceptLanguage' },
{ label: 'Cache-Control', value: 'cacheControl' },
{ label: 'Content-Type', value: 'contentType' },
{ label: 'Content-Length', value: 'contentLength' },
{ label: 'User-Agent', value: 'userAgent' },
{ label: 'Referer', value: 'referer' },
{ label: 'Cookie', value: 'cookie' },
{ label: 'Authorization', value: 'authorization' },
{ label: 'If-None-Match', value: 'ifNoneMatch' },
{ label: 'If-Modified-Since', value: 'ifModifiedSince' },
];

View File

@ -41,12 +41,7 @@
</a-drawer> </a-drawer>
<a-layout class="layout-content" :style="paddingStyle"> <a-layout class="layout-content" :style="paddingStyle">
<a-spin :loading="appStore.loading" :tip="appStore.loadingTip"> <a-spin :loading="appStore.loading" :tip="appStore.loadingTip">
<a-scrollbar <a-scrollbar class="flex h-[calc(100vh-56px)] flex-col gap-[8px] overflow-auto">
:style="{
overflow: 'auto',
height: 'calc(100vh - 64px)',
}"
>
<MsBreadCrumb /> <MsBreadCrumb />
<a-layout-content> <a-layout-content>
<slot name="page"> <slot name="page">
@ -211,7 +206,6 @@
transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 1); transition: padding 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
.arco-layout-content { .arco-layout-content {
padding: 8px 16px 0 0; padding: 8px 16px 0 0;
min-height: 500px;
} }
} }
.arco-layout-sider-light { .arco-layout-sider-light {

View File

@ -17,4 +17,10 @@
<script lang="ts" setup></script> <script lang="ts" setup></script>
<style lang="less" scoped></style> <style lang="less" scoped>
.page-content {
@apply h-full overflow-y-auto;
min-height: 500px;
}
</style>

View File

@ -39,7 +39,7 @@ const ApiTest: AppRouteRecordRaw = {
component: () => import('@/views/api-test/management/index.vue'), component: () => import('@/views/api-test/management/index.vue'),
meta: { meta: {
locale: 'menu.apiTest.management', locale: 'menu.apiTest.management',
roles: ['*'], roles: ['PROJECT_API_DEFINITION:READ'],
isTopMenu: true, isTopMenu: true,
}, },
}, },

View File

@ -1,11 +1,14 @@
import { import {
EnableKeyValueParam, EnableKeyValueParam,
ExecuteBody,
ExecuteRequestCommonParam, ExecuteRequestCommonParam,
ExecuteRequestFormBodyFormValue, ExecuteRequestFormBodyFormValue,
KeyValueParam, KeyValueParam,
RequestTaskResult,
ResponseDefinition, ResponseDefinition,
} from '@/models/apiTest/common'; } from '@/models/apiTest/common';
import { import {
RequestBodyFormat,
RequestCaseStatus, RequestCaseStatus,
RequestContentTypeEnum, RequestContentTypeEnum,
RequestParamsType, RequestParamsType,
@ -81,6 +84,54 @@ export const defaultResponseItem: ResponseDefinition = {
}, },
}; };
// 请求的默认 body 参数
export const defaultBodyParams: ExecuteBody = {
bodyType: RequestBodyFormat.NONE,
formDataBody: {
formValues: [],
},
wwwFormBody: {
formValues: [],
},
jsonBody: {
jsonValue: '',
},
xmlBody: { value: '' },
binaryBody: {
description: '',
file: undefined,
},
rawBody: { value: '' },
};
// 默认的响应内容结构
export const defaultResponse: RequestTaskResult = {
requestResults: [
{
body: '',
headers: '',
method: '',
url: '',
responseResult: {
body: '',
contentType: '',
headers: '',
dnsLookupTime: 0,
downloadTime: 0,
latency: 0,
responseCode: 0,
responseTime: 0,
responseSize: 0,
socketInitTime: 0,
tcpHandshakeTime: 0,
transferStartTime: 0,
sslHandshakeTime: 0,
},
},
],
console: '',
};
// 默认提取参数的 key-value 表格行的值 // 默认提取参数的 key-value 表格行的值
export const defaultKeyValueParamItem: KeyValueParam = { export const defaultKeyValueParamItem: KeyValueParam = {
key: '', key: '',

View File

@ -55,6 +55,7 @@
</div> </div>
</template> </template>
<!-- 表格列 slot --> <!-- 表格列 slot -->
<!-- 参数名 or 请求/响应头联想输入 -->
<template #key="{ record, columnConfig, rowIndex }"> <template #key="{ record, columnConfig, rowIndex }">
<a-popover <a-popover
position="tl" position="tl"
@ -69,7 +70,31 @@
{{ record[columnConfig.dataIndex as string] }} {{ record[columnConfig.dataIndex as string] }}
</div> </div>
</template> </template>
<a-auto-complete
v-if="columnConfig.inputType === 'autoComplete'"
v-model:model-value="record[columnConfig.dataIndex as string]"
:data="columnConfig.autoCompleteParams?.filter((e) => e.isShow === true)"
class="ms-form-table-input"
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
:filter-option="false"
size="mini"
@search="(val) => handleSearchParams(val, columnConfig)"
@change="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
@select="(val) => selectAutoComplete(val, record, columnConfig)"
>
<template #option="{ data: opt }">
<div class="w-[350px]">
{{ t(opt.raw.label) }}
<!-- <a-tooltip :content="t(opt.raw.label)" position="bl" :mouse-enter-delay="300">
<div class="one-line-text max-w-full">
{{ t(opt.raw.label) }}
</div>
</a-tooltip> -->
</div>
</template>
</a-auto-complete>
<a-input <a-input
v-else
v-model:model-value="record[columnConfig.dataIndex as string]" v-model:model-value="record[columnConfig.dataIndex as string]"
:placeholder="t('apiTestDebug.paramNamePlaceholder')" :placeholder="t('apiTestDebug.paramNamePlaceholder')"
class="ms-form-table-input" class="ms-form-table-input"
@ -79,6 +104,7 @@
/> />
</a-popover> </a-popover>
</template> </template>
<!-- 参数类型 -->
<template #paramType="{ record, columnConfig, rowIndex }"> <template #paramType="{ record, columnConfig, rowIndex }">
<a-tooltip <a-tooltip
v-if="columnConfig.hasRequired" v-if="columnConfig.hasRequired"
@ -104,6 +130,7 @@
@change="(val) => handleTypeChange(val, record, rowIndex, columnConfig.addLineDisabled)" @change="(val) => handleTypeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
/> />
</template> </template>
<!-- 提取类型 -->
<template #extractType="{ record, columnConfig, rowIndex }"> <template #extractType="{ record, columnConfig, rowIndex }">
<a-select <a-select
v-model:model-value="record.extractType" v-model:model-value="record.extractType"
@ -113,6 +140,7 @@
@change="() => addTableLine(rowIndex)" @change="() => addTableLine(rowIndex)"
/> />
</template> </template>
<!-- 变量类型 -->
<template #variableType="{ record, columnConfig, rowIndex }"> <template #variableType="{ record, columnConfig, rowIndex }">
<a-select <a-select
v-model:model-value="record.variableType" v-model:model-value="record.variableType"
@ -122,6 +150,7 @@
@change="() => addTableLine(rowIndex)" @change="() => addTableLine(rowIndex)"
/> />
</template> </template>
<!-- 提取范围 -->
<template #extractScope="{ record, columnConfig, rowIndex }"> <template #extractScope="{ record, columnConfig, rowIndex }">
<a-select <a-select
v-model:model-value="record.extractScope" v-model:model-value="record.extractScope"
@ -131,9 +160,11 @@
@change="() => addTableLine(rowIndex)" @change="() => addTableLine(rowIndex)"
/> />
</template> </template>
<!-- 表达式 -->
<template #expression="{ record, rowIndex, columnConfig }"> <template #expression="{ record, rowIndex, columnConfig }">
<slot name="expression" :record="record" :row-index="rowIndex" :column-config="columnConfig"></slot> <slot name="expression" :record="record" :row-index="rowIndex" :column-config="columnConfig"></slot>
</template> </template>
<!-- 参数值 -->
<template #value="{ record, columnConfig, rowIndex }"> <template #value="{ record, columnConfig, rowIndex }">
<a-popover <a-popover
v-if="columnConfig.isNormal" v-if="columnConfig.isNormal"
@ -185,6 +216,7 @@
@apply="() => addTableLine(rowIndex)" @apply="() => addTableLine(rowIndex)"
/> />
</template> </template>
<!-- 长度范围 -->
<template #lengthRange="{ record, rowIndex }"> <template #lengthRange="{ record, rowIndex }">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<a-input-number <a-input-number
@ -208,6 +240,7 @@
/> />
</div> </div>
</template> </template>
<!-- 标签 -->
<template #tag="{ record, columnConfig, rowIndex }"> <template #tag="{ record, columnConfig, rowIndex }">
<a-popover <a-popover
position="tl" position="tl"
@ -232,6 +265,7 @@
/> />
</a-popover> </a-popover>
</template> </template>
<!-- 描述 -->
<template #description="{ record, columnConfig, rowIndex }"> <template #description="{ record, columnConfig, rowIndex }">
<paramDescInput <paramDescInput
v-model:desc="record[columnConfig.dataIndex as string]" v-model:desc="record[columnConfig.dataIndex as string]"
@ -241,6 +275,7 @@
@change="handleDescChange" @change="handleDescChange"
/> />
</template> </template>
<!-- 编码 -->
<template #encode="{ record, rowIndex }"> <template #encode="{ record, rowIndex }">
<a-switch <a-switch
v-model:model-value="record.encode" v-model:model-value="record.encode"
@ -250,12 +285,14 @@
@change="() => addTableLine(rowIndex)" @change="() => addTableLine(rowIndex)"
/> />
</template> </template>
<!-- 必须包含 -->
<template #mustContain="{ record, columnConfig }"> <template #mustContain="{ record, columnConfig }">
<a-checkbox <a-checkbox
v-model:model-value="record[columnConfig.dataIndex as string]" v-model:model-value="record[columnConfig.dataIndex as string]"
@change="handleMustContainColChange(false)" @change="handleMustContainColChange(false)"
/> />
</template> </template>
<!-- 类型检查 -->
<template #typeChecking="{ record, columnConfig }"> <template #typeChecking="{ record, columnConfig }">
<a-checkbox <a-checkbox
v-model:model-value="record[columnConfig.dataIndex as string]" v-model:model-value="record[columnConfig.dataIndex as string]"
@ -296,6 +333,7 @@
</a-tooltip> </a-tooltip>
<a-input v-model="record.expectedValue" size="mini" class="ms-form-table-input" /> <a-input v-model="record.expectedValue" size="mini" class="ms-form-table-input" />
</template> </template>
<!-- 项目选择 -->
<template #project="{ record, rowIndex }"> <template #project="{ record, rowIndex }">
<a-select <a-select
v-model:model-value="record.projectId" v-model:model-value="record.projectId"
@ -320,6 +358,7 @@
</a-tooltip> </a-tooltip>
</a-select> </a-select>
</template> </template>
<!-- 环境选择 -->
<template #environment="{ record }"> <template #environment="{ record }">
<MsSelect <MsSelect
v-if="record.projectId" v-if="record.projectId"
@ -340,6 +379,7 @@
/> />
<span v-else></span> <span v-else></span>
</template> </template>
<!-- 域名 -->
<template #host="{ record }"> <template #host="{ record }">
<MsTagsGroup <MsTagsGroup
v-if="Array.isArray(record.domain)" v-if="Array.isArray(record.domain)"
@ -349,6 +389,7 @@
/> />
<div v-else class="text-[var(--color-text-1)]">{{ '-' }}</div> <div v-else class="text-[var(--color-text-1)]">{{ '-' }}</div>
</template> </template>
<!-- 操作 -->
<template #operation="{ record, rowIndex, columnConfig }"> <template #operation="{ record, rowIndex, columnConfig }">
<div class="flex flex-row items-center" :class="{ 'justify-end': columnConfig.align === 'right' }"> <div class="flex flex-row items-center" :class="{ 'justify-end': columnConfig.align === 'right' }">
<a-switch <a-switch
@ -482,6 +523,7 @@
const MsParamsInput = defineAsyncComponent(() => import('@/components/business/ms-params-input/index.vue')); const MsParamsInput = defineAsyncComponent(() => import('@/components/business/ms-params-input/index.vue'));
export interface ParamTableColumn extends FormTableColumn { export interface ParamTableColumn extends FormTableColumn {
isAutoComplete?: boolean; // key /
isNormal?: boolean; // value MsParamsInput isNormal?: boolean; // value MsParamsInput
hasRequired?: boolean; // type required hasRequired?: boolean; // type required
typeOptions?: { label: string; value: string }[]; // type typeOptions?: { label: string; value: string }[]; // type
@ -923,6 +965,21 @@
emitChange('handleFormTableChange'); emitChange('handleFormTableChange');
} }
/**
* 搜索变量
* @param val 变量名
*/
function handleSearchParams(val: string, item: FormTableColumn) {
item.autoCompleteParams = item.autoCompleteParams?.map((e) => {
e.isShow = (e.label || '').includes(val);
return e;
});
}
function selectAutoComplete(val: string, record: Record<string, any>, item: FormTableColumn) {
record[item.dataIndex as string] = val;
}
defineExpose({ defineExpose({
addTableLine, addTableLine,
}); });

View File

@ -24,6 +24,8 @@
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue'; import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue'; import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import { responseHeaderOption } from '@/config/apiTest';
import { EnableKeyValueParam } from '@/models/apiTest/common'; import { EnableKeyValueParam } from '@/models/apiTest/common';
import { filterKeyValParams } from '../utils'; import { filterKeyValParams } from '../utils';
@ -47,6 +49,8 @@
title: 'apiTestDebug.paramName', title: 'apiTestDebug.paramName',
dataIndex: 'key', dataIndex: 'key',
slotName: 'key', slotName: 'key',
inputType: 'autoComplete',
autoCompleteParams: responseHeaderOption,
}, },
{ {
title: 'apiTestDebug.paramValue', title: 'apiTestDebug.paramValue',

View File

@ -143,10 +143,10 @@
v-model:active-key="requestVModel.activeTab" v-model:active-key="requestVModel.activeTab"
:content-tab-list="contentTabList" :content-tab-list="contentTabList"
:get-text-func="getTabBadge" :get-text-func="getTabBadge"
class="no-content relative mt-[8px]" class="no-content relative mt-[8px] border-b"
/> />
</div> </div>
<div ref="splitContainerRef" class="h-[calc(100%-97px)]"> <div ref="splitContainerRef" class="h-[calc(100%-87px)]">
<MsSplitBox <MsSplitBox
ref="horizontalSplitBoxRef" ref="horizontalSplitBoxRef"
:size="props.isDefinition ? 0.7 : 1" :size="props.isDefinition ? 0.7 : 1"
@ -166,7 +166,7 @@
min="10px" min="10px"
:direction="activeLayout" :direction="activeLayout"
second-container-class="!overflow-y-hidden" second-container-class="!overflow-y-hidden"
:class="!showResponse ? 'hidden-second' : ''" :class="!showResponse ? 'hidden-second' : 'show-second'"
@expand-change="handleVerticalExpandChange" @expand-change="handleVerticalExpandChange"
> >
<template #first> <template #first>
@ -545,6 +545,7 @@
defaultHeaderParamsItem, defaultHeaderParamsItem,
defaultKeyValueParamItem, defaultKeyValueParamItem,
defaultRequestParamsItem, defaultRequestParamsItem,
defaultResponse,
} from '@/views/api-test/components/config'; } from '@/views/api-test/components/config';
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils'; import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils';
import type { Api } from '@form-create/arco-design'; import type { Api } from '@form-create/arco-design';
@ -876,22 +877,16 @@
function handleUrlChange(val: string) { function handleUrlChange(val: string) {
const params = parseQueryParams(val.trim()); const params = parseQueryParams(val.trim());
if (params.length > 0) { if (params.length > 0) {
requestVModel.value.query.splice( requestVModel.value.query = [
0,
requestVModel.value.query.length - 2,
...params.map((e, i) => ({ ...params.map((e, i) => ({
id: (new Date().getTime() + i).toString(), id: (new Date().getTime() + i).toString(),
paramType: RequestParamsType.STRING, ...defaultRequestParamsItem,
description: '',
required: false,
maxLength: undefined,
minLength: undefined,
encode: false,
enable: true,
...e, ...e,
})) })),
); cloneDeep(defaultRequestParamsItem),
];
requestVModel.value.activeTab = RequestComposition.QUERY; requestVModel.value.activeTab = RequestComposition.QUERY;
[requestVModel.value.url] = val.split('?');
} }
handleActiveDebugChange(); handleActiveDebugChange();
} }
@ -1151,6 +1146,7 @@
if (isHttpProtocol.value) { if (isHttpProtocol.value) {
try { try {
requestVModel.value.executeLoading = true; requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse);
const res = await props.executeApi(makeRequestParams(executeType)); const res = await props.executeApi(makeRequestParams(executeType));
if (executeType === 'localExec') { if (executeType === 'localExec') {
await props.localExecuteApi(localExecuteUrl.value, res); await props.localExecuteApi(localExecuteUrl.value, res);
@ -1166,6 +1162,7 @@
if (valid === true) { if (valid === true) {
try { try {
requestVModel.value.executeLoading = true; requestVModel.value.executeLoading = true;
requestVModel.value.response = cloneDeep(defaultResponse);
const res = await props.executeApi(makeRequestParams(executeType)); const res = await props.executeApi(makeRequestParams(executeType));
if (executeType === 'localExec') { if (executeType === 'localExec') {
await props.localExecuteApi(localExecuteUrl.value, res); await props.localExecuteApi(localExecuteUrl.value, res);
@ -1518,4 +1515,9 @@
@apply hidden; @apply hidden;
} }
} }
.show-second {
:deep(.arco-split-trigger) {
@apply block;
}
}
</style> </style>

View File

@ -178,6 +178,7 @@
import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue'; import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import popConfirm from '@/views/api-test/components/popConfirm.vue'; import popConfirm from '@/views/api-test/components/popConfirm.vue';
import { responseHeaderOption } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
@ -397,7 +398,8 @@
title: 'apiTestManagement.paramName', title: 'apiTestManagement.paramName',
dataIndex: 'key', dataIndex: 'key',
slotName: 'key', slotName: 'key',
inputType: 'input', inputType: 'autoComplete',
autoCompleteParams: responseHeaderOption,
}, },
{ {
title: 'apiTestManagement.paramVal', title: 'apiTestManagement.paramVal',

View File

@ -133,6 +133,7 @@
ResponseComposition, ResponseComposition,
} from '@/enums/apiEnum'; } from '@/enums/apiEnum';
import { defaultBodyParams, defaultResponse } from '../components/config';
import { parseRequestBodyFiles } from '../components/utils'; import { parseRequestBodyFiles } from '../components/utils';
const { t } = useI18n(); const { t } = useI18n();
@ -150,50 +151,6 @@
} }
const initDefaultId = `debug-${Date.now()}`; const initDefaultId = `debug-${Date.now()}`;
const defaultBodyParams: ExecuteBody = {
bodyType: RequestBodyFormat.NONE,
formDataBody: {
formValues: [],
},
wwwFormBody: {
formValues: [],
},
jsonBody: {
jsonValue: '',
},
xmlBody: { value: '' },
binaryBody: {
description: '',
file: undefined,
},
rawBody: { value: '' },
};
const defaultResponse: RequestTaskResult = {
requestResults: [
{
body: '',
headers: '',
url: '',
method: '',
responseResult: {
body: '',
contentType: '',
headers: '',
dnsLookupTime: 0,
downloadTime: 0,
latency: 0,
responseCode: 0,
responseTime: 0,
responseSize: 0,
socketInitTime: 0,
sslHandshakeTime: 0,
tcpHandshakeTime: 0,
transferStartTime: 0,
},
},
],
console: '',
}; //
const defaultDebugParams: RequestParam = { const defaultDebugParams: RequestParam = {
id: initDefaultId, id: initDefaultId,
moduleId: 'root', moduleId: 'root',

View File

@ -92,7 +92,7 @@
ResponseComposition, ResponseComposition,
} from '@/enums/apiEnum'; } from '@/enums/apiEnum';
import { defaultResponseItem } from '@/views/api-test/components/config'; import { defaultBodyParams, defaultResponse, defaultResponseItem } from '@/views/api-test/components/config';
import type { RequestParam } from '@/views/api-test/components/requestComposition/index.vue'; import type { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import { parseRequestBodyFiles } from '@/views/api-test/components/utils'; import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
// requestComposition // requestComposition
@ -138,51 +138,6 @@
}); });
const initDefaultId = `definition-${Date.now()}`; const initDefaultId = `definition-${Date.now()}`;
const defaultBodyParams: ExecuteBody = {
bodyType: RequestBodyFormat.NONE,
formDataBody: {
formValues: [],
},
wwwFormBody: {
formValues: [],
},
jsonBody: {
jsonValue: '',
},
xmlBody: { value: '' },
binaryBody: {
description: '',
file: undefined,
},
rawBody: { value: '' },
};
//
const defaultResponse: RequestTaskResult = {
requestResults: [
{
body: '',
headers: '',
method: '',
url: '',
responseResult: {
body: '',
contentType: '',
headers: '',
dnsLookupTime: 0,
downloadTime: 0,
latency: 0,
responseCode: 0,
responseTime: 0,
responseSize: 0,
socketInitTime: 0,
tcpHandshakeTime: 0,
transferStartTime: 0,
sslHandshakeTime: 0,
},
},
],
console: '',
};
const defaultDefinitionParams: RequestParam = { const defaultDefinitionParams: RequestParam = {
id: initDefaultId, id: initDefaultId,
moduleId: props.activeModule === 'all' ? 'root' : props.activeModule, moduleId: props.activeModule === 'all' ? 'root' : props.activeModule,

View File

@ -313,10 +313,10 @@
label: 'apiTestManagement.share', label: 'apiTestManagement.share',
eventTag: 'share', eventTag: 'share',
}, },
{ // {
label: 'apiTestManagement.shareModule', // label: 'apiTestManagement.shareModule',
eventTag: 'shareModule', // eventTag: 'shareModule',
}, // }, // TODO:
{ {
isDivider: true, isDivider: true,
}, },