feat(接口调试): 99.5%

This commit is contained in:
baiqi 2024-02-26 14:28:13 +08:00 committed by Craftsman
parent 446346b6ee
commit 42eb1a827c
17 changed files with 270 additions and 85 deletions

View File

@ -3,7 +3,7 @@
v-model:visible="insertScriptDrawer"
:title="
props.enableRadioSelected
? t('project.commonScript.insertCommonScript')
? t('project.commonScript.quoteCommonScript')
: t('project.commonScript.insertCommonScript')
"
:width="960"
@ -16,8 +16,7 @@
@cancel="handleDrawerCancel"
>
<div class="mb-4 flex items-center justify-between">
<div v-if="propsRes.data.length" class="font-medium">{{ t('project.commonScript.commonScriptList') }}</div>
<a-button v-else type="outline" @click="addCommonScript">
<a-button type="outline" @click="addCommonScript">
{{ t('project.commonScript.addPublicScript') }}
</a-button>
<a-input-search

View File

@ -166,7 +166,7 @@
</template>
</a-form>
<div
v-if="paramSettingType === 'mock'"
v-if="paramSettingType === 'mock' && paramForm.type !== ''"
class="mb-[16px] flex items-center gap-[16px] bg-[var(--color-text-n9)] p-[5px_8px]"
>
<div class="text-[var(--color-text-3)]">{{ t('ms.paramsInput.preview') }}</div>

View File

@ -29,7 +29,7 @@
v-if="_props.hideMoreAction !== true"
:class="[
'ms-tree-node-extra',
innerFocusNodeKey === _props[props.fieldNames.key] ? 'ms-tree-node-extra--focus' : '',
innerFocusNodeKey === _props[props.fieldNames.key] ? 'ms-tree-node-extra--focus' : '', // TODO:
]"
>
<div
@ -467,6 +467,7 @@
}
}
.arco-tree-node-selected {
background-color: rgb(var(--primary-1));
.arco-tree-node-minus-icon,
.arco-tree-node-plus-icon {
border: 1px solid rgb(var(--primary-5));

View File

@ -0,0 +1,58 @@
<template>
<a-tabs v-model:active-key="innerActiveKey" :class="props.class">
<a-tab-pane v-for="item of props.contentTabList" :key="item.value" :title="item.label">
<template #title>
<a-badge
v-if="props.getTextFunc(item.value) !== ''"
:class="item.value === innerActiveKey ? 'active-badge' : ''"
:max-count="99"
:text="props.getTextFunc(item.value)"
>
<div class="mr-[4px]">
{{ item.label }}
</div>
</a-badge>
<div v-else>
{{ item.label }}
</div>
</template>
</a-tab-pane>
</a-tabs>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{
activeKey: string;
contentTabList: { label: string; value: string }[];
class?: string;
getTextFunc?: (value: any) => string;
}>(),
{
getTextFunc: (value: any) => value,
class: '',
}
);
const innerActiveKey = defineModel<string>('activeKey', {
default: '',
});
</script>
<style lang="less" scoped>
:deep(.arco-badge) {
@apply flex items-center;
line-height: 22px;
.arco-badge-text,
.arco-badge-number {
@apply relative right-0 top-0 transform-none shadow-none;
}
}
:deep(.active-badge) {
.arco-badge-text,
.arco-badge-number {
background-color: rgb(var(--primary-5));
}
}
</style>

View File

@ -118,4 +118,5 @@ export default {
'common.to': 'To',
'common.tip': 'Tips',
'common.stay': 'Stay',
'common.apply': 'Apply',
};

View File

@ -121,4 +121,5 @@ export default {
'common.to': '至',
'common.tip': '温馨提示',
'common.stay': '留下',
'common.apply': '应用',
};

View File

@ -99,7 +99,7 @@
<div v-else class="flex h-[calc(100%-47px)] flex-col">
<div class="mb-[16px] flex w-full items-center bg-[var(--color-text-n9)] p-[12px]">
<div class="text-[var(--color-text-2)]">
{{ condition.scriptName || '-' }}
{{ condition.commonScriptInfo?.name || '-' }}
</div>
<a-divider margin="8px" direction="vertical" />
<MsButton type="text" class="font-medium" @click="showQuoteDrawer = true">
@ -320,6 +320,14 @@
:checked-id="condition.scriptId"
enable-radio-selected
@save="saveQuoteScriptHandler"
@add-script="showAddScriptDrawer = true"
/>
<AddScriptDrawer
v-model:visible="showAddScriptDrawer"
v-model:params="paramsList"
:confirm-loading="confirmLoading"
ok-text="common.apply"
:enable-radio-selected="true"
/>
</template>
@ -336,6 +344,7 @@
import useTable from '@/components/pure/ms-table/useTable';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import InsertCommonScript from '@/components/business/ms-common-script/insertCommonScript.vue';
import AddScriptDrawer from '@/components/business/ms-common-script/ms-addScriptDrawer.vue';
import MsScriptDefined from '@/components/business/ms-common-script/scriptDefined.vue';
import fastExtraction from '../fastExtraction/index.vue';
import moreSetting from '../fastExtraction/moreSetting.vue';
@ -345,6 +354,7 @@
import { useI18n } from '@/hooks/useI18n';
import { ExecuteConditionProcessor, JSONPathExtract, RegexExtract, XPathExtract } from '@/models/apiTest/debug';
import { ParamsRequestType } from '@/models/projectManagement/commonScript';
import {
RequestConditionProcessor,
RequestExtractEnvType,
@ -478,6 +488,10 @@ if (!result){
showQuoteDrawer.value = false;
}
const showAddScriptDrawer = ref(false);
const paramsList = ref<ParamsRequestType[]>([]);
const confirmLoading = ref(false);
const sqlSourceColumns: ParamTableColumn[] = [
{
title: 'apiTestDebug.paramName',

View File

@ -0,0 +1,42 @@
import {
EnableKeyValueParam,
ExecuteRequestCommonParam,
ExecuteRequestFormBodyFormValue,
} from '@/models/apiTest/debug';
import { RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
// 请求 body 参数表格默认行的值
export const defaultBodyParamsItem: ExecuteRequestFormBodyFormValue = {
key: '',
value: '',
paramType: RequestParamsType.STRING,
description: '',
required: false,
maxLength: undefined,
minLength: undefined,
encode: false,
enable: true,
contentType: RequestContentTypeEnum.TEXT,
files: [],
};
// 请求 header 参数表格默认行的值
export const defaultHeaderParamsItem: EnableKeyValueParam = {
key: '',
value: '',
description: '',
enable: true,
};
// 请求 query、rest 参数表格默认行的值
export const defaultRequestParamsItem: ExecuteRequestCommonParam = {
key: '',
value: '',
paramType: RequestParamsType.STRING,
description: '',
required: false,
maxLength: undefined,
minLength: undefined,
encode: false,
enable: true,
};

View File

@ -404,6 +404,7 @@
import { RequestBodyFormat, RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
import { SelectAllEnum, TableKeyEnum } from '@/enums/tableEnum';
import { filterKeyValParams } from './utils';
import { TableOperationColumn } from '@arco-design/web-vue/es/table/interface';
//
const MsAddAttachment = defineAsyncComponent(() => import('@/components/business/ms-add-attachment/index.vue'));
@ -627,9 +628,12 @@
watch(
() => props.params,
(val) => {
if (val.length > 0) {
propsRes.value.data = val;
(arr) => {
if (arr.length > 0) {
propsRes.value.data = arr;
if (!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault) {
addTableLine(arr.length - 1);
}
} else {
const id = new Date().getTime().toString();
propsRes.value.data = [

View File

@ -13,7 +13,7 @@
<batchAddKeyVal
v-if="showParamTable"
:params="currentTableParams"
:default-param-item="defaultParamItem"
:default-param-item="defaultBodyParamsItem"
@apply="handleBatchParamApply"
/>
</div>
@ -31,7 +31,7 @@
:height-used="heightUsed"
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_DATA"
:default-param-item="defaultParamItem"
:default-param-item="defaultBodyParamsItem"
:upload-temp-file-api="props.uploadTempFileApi"
@change="handleParamTableChange"
/>
@ -43,7 +43,7 @@
:height-used="heightUsed"
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_URL_ENCODE"
:default-param-item="defaultParamItem"
:default-param-item="defaultBodyParamsItem"
@change="handleParamTableChange"
/>
<div v-else-if="innerParams.bodyType === RequestBodyFormat.BINARY">
@ -116,10 +116,12 @@
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { ExecuteBody, ExecuteRequestFormBodyFormValue } from '@/models/apiTest/debug';
import { RequestBodyFormat, RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
import { ExecuteBody } from '@/models/apiTest/debug';
import { RequestBodyFormat, RequestParamsType } from '@/enums/apiEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { defaultBodyParamsItem } from '@/views/api-test/components/config';
const props = defineProps<{
params: ExecuteBody;
layout: 'horizontal' | 'vertical';
@ -135,19 +137,6 @@
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const defaultParamItem: ExecuteRequestFormBodyFormValue = {
key: '',
value: '',
paramType: RequestParamsType.STRING,
description: '',
required: false,
maxLength: undefined,
minLength: undefined,
encode: false,
enable: true,
contentType: RequestContentTypeEnum.TEXT,
files: [],
};
const fileList = ref<any[]>(
innerParams.value.binaryBody && innerParams.value.binaryBody.file ? [innerParams.value.binaryBody.file] : []
);

View File

@ -1,14 +1,18 @@
<template>
<div class="mb-[8px] flex items-center justify-between">
<div class="font-medium">{{ t('apiTestDebug.header') }}</div>
<batchAddKeyVal :params="innerParams" :default-param-item="defaultParamItem" @apply="handleBatchParamApply" />
<batchAddKeyVal
:params="innerParams"
:default-param-item="defaultHeaderParamsItem"
@apply="handleBatchParamApply"
/>
</div>
<paramTable
v-model:params="innerParams"
:columns="columns"
:height-used="heightUsed"
:scroll="scroll"
:default-param-item="defaultParamItem"
:default-param-item="defaultHeaderParamsItem"
draggable
@change="handleParamTableChange"
/>
@ -24,6 +28,8 @@
import { EnableKeyValueParam } from '@/models/apiTest/debug';
import { defaultHeaderParamsItem } from '@/views/api-test/components/config';
const props = defineProps<{
params: EnableKeyValueParam[];
layout: 'horizontal' | 'vertical';
@ -38,12 +44,6 @@
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const defaultParamItem = {
key: '',
value: '',
description: '',
enable: true,
};
const columns: ParamTableColumn[] = [
{

View File

@ -118,9 +118,12 @@
}`"
>
<div>
<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>
<MsTab
v-model:active-key="requestVModel.activeTab"
:content-tab-list="contentTabList"
:get-text-func="getTabBadge"
class="no-content relative mb-[16px]"
></MsTab>
</div>
<div class="tab-pane-container">
<template v-if="requestVModel.activeTab === RequestComposition.PLUGIN || isInitPluginForm">
@ -199,7 +202,8 @@
v-model:active-tab="requestVModel.responseActiveTab"
:is-expanded="isExpanded"
:response="requestVModel.response"
:hide-layout-swicth="props.hideResponseLayoutSwitch"
:hide-layout-switch="props.hideResponseLayoutSwitch"
:request="requestVModel"
@change-expand="changeExpand"
@change-layout="handleActiveLayoutChange"
/>
@ -255,6 +259,7 @@
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import debugAuth from './auth.vue';
import postcondition from './postcondition.vue';
import precondition from './precondition.vue';
@ -275,10 +280,21 @@
import { PluginConfig } from '@/models/apiTest/common';
import { ExecuteHTTPRequestFullParams } from '@/models/apiTest/debug';
import { ModuleTreeNode } from '@/models/common';
import { RequestComposition, RequestMethods, RequestParamsType } from '@/enums/apiEnum';
import {
RequestAuthType,
RequestBodyFormat,
RequestComposition,
RequestMethods,
RequestParamsType,
} from '@/enums/apiEnum';
import { parseRequestBodyFiles } from '../utils';
import { Api } from '@form-create/arco-design';
import {
defaultBodyParamsItem,
defaultHeaderParamsItem,
defaultRequestParamsItem,
} from '@/views/api-test/components/config';
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils';
import type { Api } from '@form-create/arco-design';
// Http
const debugHeader = defineAsyncComponent(() => import('./header.vue'));
@ -387,11 +403,40 @@
},
];
// tab
const contentTabList = computed(() =>
isHttpProtocol.value
? httpContentTabList
: [...pluginContentTab, ...httpContentTabList.filter((e) => commonContentTabKey.includes(e.value))]
);
const contentTabList = computed(() => {
if (isHttpProtocol.value) {
//
return props.isDefinition
? httpContentTabList
: httpContentTabList.filter((e) => e.value !== RequestComposition.ASSERTION);
}
return [...pluginContentTab, ...httpContentTabList.filter((e) => commonContentTabKey.includes(e.value))];
});
function getTabBadge(tabKey: RequestComposition) {
switch (tabKey) {
case RequestComposition.HEADER:
const headerNum = filterKeyValParams(requestVModel.value.headers, defaultHeaderParamsItem).validParams.length;
return `${headerNum > 0 ? headerNum : ''}`;
case RequestComposition.BODY:
return requestVModel.value.body.bodyType !== RequestBodyFormat.NONE ? '1' : '';
case RequestComposition.QUERY:
const queryNum = filterKeyValParams(requestVModel.value.query, defaultRequestParamsItem).validParams.length;
return `${queryNum > 0 ? queryNum : ''}`;
case RequestComposition.REST:
const restNum = filterKeyValParams(requestVModel.value.rest, defaultRequestParamsItem).validParams.length;
return `${restNum > 0 ? restNum : ''}`;
case RequestComposition.PRECONDITION:
return `${requestVModel.value.children[0].preProcessorConfig.processors.length || ''}`;
case RequestComposition.POST_CONDITION:
return `${requestVModel.value.children[0].postProcessorConfig.processors.length || ''}`;
case RequestComposition.AUTH:
return requestVModel.value.authConfig.authType !== RequestAuthType.NONE ? '1' : '';
default:
return '';
}
}
const protocolLoading = ref(false);
const protocolOptions = ref<SelectOptionData[]>([]);
async function initProtocolList() {
@ -655,8 +700,8 @@
let parseRequestBodyResult;
let requestParams;
if (isHttpProtocol.value) {
const realFormDataBodyValues = formDataBody.formValues.filter((e, i) => i !== formDataBody.formValues.length - 1); //
const realWwwFormBodyValues = wwwFormBody.formValues.filter((e, i) => i !== wwwFormBody.formValues.length - 1); //
const realFormDataBodyValues = filterKeyValParams(formDataBody.formValues, defaultBodyParamsItem).validParams;
const realWwwFormBodyValues = filterKeyValParams(wwwFormBody.formValues, defaultBodyParamsItem).validParams;
parseRequestBodyResult = parseRequestBodyFiles(
requestVModel.value.body,
requestVModel.value.uploadFileIds, //
@ -673,12 +718,12 @@
formValues: realWwwFormBodyValues,
},
},
headers: requestVModel.value.headers.filter((e, i) => i !== requestVModel.value.headers.length - 1), //
headers: filterKeyValParams(requestVModel.value.headers, defaultHeaderParamsItem).validParams,
method: requestVModel.value.method,
otherConfig: requestVModel.value.otherConfig,
path: requestVModel.value.url || requestVModel.value.path,
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), //
query: filterKeyValParams(requestVModel.value.query, defaultRequestParamsItem).validParams,
rest: filterKeyValParams(requestVModel.value.rest, defaultRequestParamsItem).validParams,
url: requestVModel.value.url,
polymorphicName,
};

View File

@ -9,14 +9,18 @@
/>
</a-tooltip>
</div>
<batchAddKeyVal :params="innerParams" :default-param-item="defaultParamItem" @apply="handleBatchParamApply" />
<batchAddKeyVal
:params="innerParams"
:default-param-item="defaultRequestParamsItem"
@apply="handleBatchParamApply"
/>
</div>
<paramTable
v-model:params="innerParams"
:columns="columns"
:height-used="heightUsed"
:scroll="{ minWidth: 1160 }"
:default-param-item="defaultParamItem"
:default-param-item="defaultRequestParamsItem"
@change="handleParamTableChange"
/>
</template>
@ -32,6 +36,8 @@
import { ExecuteRequestCommonParam } from '@/models/apiTest/debug';
import { RequestParamsType } from '@/enums/apiEnum';
import { defaultRequestParamsItem } from '@/views/api-test/components/config';
const props = defineProps<{
params: ExecuteRequestCommonParam[];
layout: 'horizontal' | 'vertical';
@ -45,18 +51,6 @@
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const defaultParamItem = {
key: '',
value: '',
paramType: RequestParamsType.STRING,
description: '',
required: false,
maxLength: undefined,
minLength: undefined,
encode: false,
enable: true,
};
const columns: ParamTableColumn[] = [
{
title: 'apiTestDebug.paramName',

View File

@ -23,7 +23,7 @@
</template>
<div class="ml-[4px] mr-[24px] font-medium">{{ t('apiTestDebug.responseContent') }}</div>
<a-radio-group
v-if="!props.hideLayoutSwicth"
v-if="!props.hideLayoutSwitch"
v-model:model-value="innerLayout"
type="button"
size="small"
@ -163,9 +163,12 @@
import { ResponseComposition } from '@/enums/apiEnum';
import type { RequestParam } from './index.vue';
export interface Response {
requestResults: {
body: string;
headers: string;
responseResult: {
body: string;
contentType: string;
@ -191,11 +194,12 @@
activeLayout?: Direction;
isExpanded: boolean;
response: Response;
hideLayoutSwicth?: boolean; //
request?: RequestParam;
hideLayoutSwitch?: boolean; //
}>(),
{
activeLayout: 'vertical',
hideLayoutSwicth: false,
hideLayoutSwitch: false,
}
);
const emit = defineEmits<{
@ -313,7 +317,9 @@
case ResponseComposition.HEADER:
return props.response.requestResults[0].responseResult.headers.trim();
case ResponseComposition.REAL_REQUEST:
return props.response.requestResults[0].body.trim();
return `${t('apiTestDebug.requestUrl')}:\n${props.request?.url}\n${t('apiTestDebug.header')}:\n${
props.response.requestResults[0].headers
}\nBody:\n${props.response.requestResults[0].body.trim()}`;
case ResponseComposition.CONSOLE:
return props.response.console.trim();
// case ResponseComposition.EXTRACT:

View File

@ -9,14 +9,18 @@
/>
</a-tooltip>
</div>
<batchAddKeyVal :params="innerParams" :default-param-item="defaultParamItem" @apply="handleBatchParamApply" />
<batchAddKeyVal
:params="innerParams"
:default-param-item="defaultRequestParamsItem"
@apply="handleBatchParamApply"
/>
</div>
<paramTable
v-model:params="innerParams"
:columns="columns"
:height-used="heightUsed"
:scroll="{ minWidth: 1160 }"
:default-param-item="defaultParamItem"
:default-param-item="defaultRequestParamsItem"
@change="handleParamTableChange"
/>
</template>
@ -32,6 +36,8 @@
import { ExecuteRequestCommonParam } from '@/models/apiTest/debug';
import { RequestParamsType } from '@/enums/apiEnum';
import { defaultRequestParamsItem } from '@/views/api-test/components/config';
const props = defineProps<{
params: ExecuteRequestCommonParam[];
layout: 'horizontal' | 'vertical';
@ -45,17 +51,6 @@
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const defaultParamItem = {
key: '',
value: '',
paramType: RequestParamsType.STRING,
description: '',
required: false,
maxLength: undefined,
minLength: undefined,
encode: false,
enable: true,
};
const columns: ParamTableColumn[] = [
{

View File

@ -1,3 +1,5 @@
import { cloneDeep } from 'lodash-es';
import { ExecuteBody } from '@/models/apiTest/debug';
import { RequestParamsType } from '@/enums/apiEnum';
@ -100,3 +102,36 @@ export function parseRequestBodyFiles(
unLinkFileIds: saveLinkFileIds?.filter((id) => !tempSaveLinkFileIds.has(id)) || [], // 存储对比已保存的文件后,需要取消关联的文件 id 集合
};
}
/**
*
* @param params
* @param defaultParamItem
*/
export function filterKeyValParams(params: Record<string, any>[], defaultParamItem: Record<string, any>) {
const lastData = cloneDeep(params[params.length - 1]);
const defaultParam = cloneDeep(defaultParamItem);
if (!lastData || !defaultParam) {
return {
lastDataIsDefault: false,
validParams: params,
};
}
// id和enable属性不参与比较
delete lastData.id;
delete lastData.enable;
delete defaultParam.id;
delete defaultParam.enable;
const lastDataIsDefault = JSON.stringify(lastData) === JSON.stringify(defaultParam);
let validParams: Record<string, any>[] = [];
if (lastDataIsDefault) {
// 如果最后一条数据是默认数据,非用户添加更改的,说明是无效参数,删除最后一个
validParams = params.slice(0, params.length - 1);
} else {
validParams = params;
}
return {
lastDataIsDefault,
validParams,
};
}

View File

@ -412,9 +412,10 @@
}
}
function handleRenameFinish(newName: string, id: string) {
initModules();
async function handleRenameFinish(newName: string, id: string) {
emit('renameFinish', newName, id);
await initModules();
initModuleCount();
}
onBeforeMount(async () => {