feat(接口定义): 请求/响应头输入联想&部分高优先级 bug 修复
This commit is contained in:
parent
a017474064
commit
263317e3c4
|
@ -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', // 响应头
|
||||||
|
|
|
@ -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', // 变量名
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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; // 展示值格式化,仅在inputType为text时生效
|
valueFormat?: (record: Record<string, any>) => string; // 展示值格式化,仅在inputType为text时生效
|
||||||
[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 {
|
||||||
|
|
|
@ -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' },
|
||||||
|
];
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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: '',
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue