style: 接口调试/用例/定义/场景展示布局优化

This commit is contained in:
teukkk 2024-05-06 20:11:39 +08:00 committed by Craftsman
parent f4fb8a7a04
commit ecae5a4d99
21 changed files with 626 additions and 896 deletions

View File

@ -364,9 +364,10 @@
<style lang="less" scoped>
.ms-code-editor {
@apply z-10;
// TODO:
width: v-bind(width);
height: v-bind(height);
min-height: 200px;
// &.MS-text[data-mode-id='plaintext'] {
// :deep(.mtk1) {
// color: rgb(var(--primary-5));

View File

@ -342,7 +342,7 @@
.handle {
@apply absolute left-0 top-0 flex h-full items-center;
z-index: 10;
z-index: 200;
width: 8px;
background-color: var(--color-neutral-3);
cursor: col-resize;

View File

@ -231,7 +231,6 @@
:disabled-param-value="props.disabled"
:scroll="{ x: '100%' }"
:columns="scriptColumns"
:height-used="heightUsed"
:selectable="false"
@change="() => emit('change')"
/>
@ -375,7 +374,6 @@
:selectable="false"
:scroll="{ x: '700px' }"
:response="props.response"
:height-used="props.heightUsed"
@change="handleExtractParamTableChange"
@more-action-select="(e,r)=> handleExtractParamMoreActionSelect(e,r as ExpressionConfig)"
>
@ -529,7 +527,6 @@
defineProps<{
disabled?: boolean;
response?: string; //
heightUsed?: number;
isBuildIn?: boolean; //
showAssociatedScene?: boolean; //
requestRadioTextProps?: Record<string, any>; //

View File

@ -39,7 +39,6 @@
:disabled="props.disabled"
:total-list="list"
:response="props.response"
:height-used="props.heightUsed"
:show-associated-scene="props.showAssociatedScene"
:show-pre-post-request="props.showPrePostRequest"
:request-radio-text-props="props.requestRadioTextProps"
@ -70,7 +69,6 @@
conditionTypes: Array<ConditionType>;
addText: string;
requestRadioTextProps?: Record<string, any>;
heightUsed?: number;
response?: string; //
showAssociatedScene?: boolean;
showPrePostRequest?: boolean; //

View File

@ -25,7 +25,6 @@
:draggable="!props.disabledExceptParam"
:scroll="{ minWidth: 1160 }"
:columns="columns"
:height-used="heightUsed"
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_DATA"
:default-param-item="defaultBodyParamsItem"
@ -44,7 +43,6 @@
:draggable="!props.disabledExceptParam"
:scroll="{ minWidth: 1160 }"
:columns="columns"
:height-used="heightUsed"
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_URL_ENCODE"
:default-param-item="defaultBodyParamsItem"
@ -135,11 +133,8 @@
const props = defineProps<{
params: ExecuteBody;
layout: 'horizontal' | 'vertical';
disabledParamValue?: boolean; //
disabledExceptParam?: boolean; //
secondBoxHeight: number;
isDrawer?: boolean;
uploadTempFileApi?: (file: File) => Promise<any>; //
fileSaveAsSourceId?: string | number; // id
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; //
@ -260,31 +255,6 @@
];
});
const heightUsed = ref<number | undefined>(undefined);
watch(
() => props.layout,
(val) => {
const otherHeight = props.isDrawer ? 328 : 372;
heightUsed.value = val === 'horizontal' ? otherHeight : otherHeight + props.secondBoxHeight;
},
{
immediate: true,
}
);
watch(
() => props.secondBoxHeight,
(val) => {
if (props.layout === 'vertical') {
heightUsed.value = (props.isDrawer ? 328 : 372) + val;
}
},
{
immediate: true,
}
);
const showParamTable = computed(() => {
// FORM_DATAX_WWW_FORM_URLENCODED
return [RequestBodyFormat.FORM_DATA, RequestBodyFormat.WWW_FORM].includes(innerParams.value.bodyType);

View File

@ -7,7 +7,6 @@
:disabled-param-value="props.disabledParamValue"
:disabled-except-param="props.disabledExceptParam"
:columns="columns"
:height-used="heightUsed"
:scroll="scroll"
:default-param-item="defaultHeaderParamsItem"
:draggable="!props.disabledExceptParam"
@ -41,11 +40,9 @@
const props = defineProps<{
params: EnableKeyValueParam[];
layout: 'horizontal' | 'vertical';
secondBoxHeight: number;
disabledParamValue?: boolean; //
disabledExceptParam?: boolean; //
typeTitle?: string;
isDrawer?: boolean;
}>();
const emit = defineEmits<{
(e: 'update:selectedKeys', value: string[]): void;
@ -88,12 +85,6 @@
]);
const batchAddKeyValVisible = ref(false);
const heightUsed = computed(() => {
if (props.layout === 'horizontal') {
return props.isDrawer ? 328 : 372;
}
return (props.isDrawer ? 328 : 372) + props.secondBoxHeight;
});
const scroll = computed(() => (props.layout === 'horizontal' ? { x: '700px' } : { x: '100%' }));
/**

View File

@ -187,153 +187,122 @@
<div v-if="isUrlError" class="url-input-tip">
<span>{{ t('apiTestDebug.apiUrlRequired') }}</span>
</div>
<div class="px-[16px]">
<div :class="`${!props.isCase ? 'request-tab-and-response' : ''} mt-[8px] flex-1`">
<MsTab
v-model:active-key="requestVModel.activeTab"
:content-tab-list="contentTabList"
:get-text-func="getTabBadge"
class="no-content relative mt-[8px] border-b"
class="sticky-content no-content relative top-0 border-b px-[16px]"
/>
</div>
<div ref="splitContainerRef" class="request-and-response h-[calc(100%-92px)]">
<MsSplitBox
ref="verticalSplitBoxRef"
v-model:size="splitBoxSize"
:max="!showResponse ? 1 : 0.98"
min="10px"
:direction="activeLayout"
second-container-class="!overflow-y-hidden"
:class="!showResponse ? 'hidden-second' : 'show-second'"
@expand-change="handleVerticalExpandChange"
>
<template #first>
<a-spin class="block h-full w-full" :loading="requestVModel.executeLoading || loading">
<div
:class="`flex h-full min-w-[800px] flex-col p-[16px] ${
activeLayout === 'horizontal' ? ' pr-[16px]' : ''
}`"
<div :class="`request-content-and-response ${activeLayout}`">
<a-spin class="request" :loading="requestVModel.executeLoading || loading">
<div class="request-tab-pane flex flex-col p-[16px]">
<apiBaseForm
v-if="!props.isCase && props.isDefinition"
v-show="requestVModel.activeTab === RequestComposition.BASE_INFO"
ref="apiBaseFormRef"
v-model:requestVModel="requestVModel"
:select-tree="selectTree as ModuleTreeNode[]"
/>
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<div class="tab-pane-container">
<apiBaseForm
v-if="!props.isCase && props.isDefinition"
v-show="requestVModel.activeTab === RequestComposition.BASE_INFO"
ref="apiBaseFormRef"
v-model:requestVModel="requestVModel"
:select-tree="selectTree as ModuleTreeNode[]"
/>
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@change="
() => {
if (isInitPluginForm) {
handlePluginFormChange();
}
}
"
/>
</a-spin>
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
:upload-temp-file-api="props.uploadTempFileApi"
:file-save-as-source-id="props.fileSaveAsSourceId"
:file-save-as-api="props.fileSaveAsApi"
:file-module-options-api="props.fileModuleOptionsApi"
@change="handleActiveDebugChange"
/>
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:is-definition="props.isDefinition"
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
:assertion-config="requestVModel.children[0].assertionConfig"
:show-extraction="true"
/>
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
@change="handleActiveDebugChange"
/>
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
@change="handleActiveDebugChange"
/>
</div>
</div>
</a-spin>
</template>
<template #second>
<response
v-show="showResponse"
ref="responseRef"
v-model:active-layout="activeLayout"
v-model:active-tab="requestVModel.responseActiveTab"
v-model:response-definition="requestVModel.responseDefinition"
:show-response-result-button="requestVModel.mode === 'debug'"
:is-http-protocol="isHttpProtocol"
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:hide-layout-switch="props.hideResponseLayoutSwitch"
:request-result="requestVModel.response?.requestResults[0]"
:console="requestVModel.response?.console"
:is-edit="props.isDefinition && isHttpProtocol && !props.isCase"
:upload-temp-file-api="props.uploadTempFileApi"
:loading="requestVModel.executeLoading || loading"
:is-definition="props.isDefinition"
@change-expand="changeVerticalExpand"
@change-layout="handleActiveLayoutChange"
@change="handleActiveDebugChange"
@execute="(executeType) => (props.isCase ? emit('execute', executeType) : execute(executeType))"
/>
</template>
</MsSplitBox>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@change="
() => {
if (isInitPluginForm) {
handlePluginFormChange();
}
}
"
/>
</a-spin>
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:layout="activeLayout"
@change="handleActiveDebugChange"
/>
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:upload-temp-file-api="props.uploadTempFileApi"
:file-save-as-source-id="props.fileSaveAsSourceId"
:file-save-as-api="props.fileSaveAsApi"
:file-module-options-api="props.fileModuleOptionsApi"
@change="handleActiveDebugChange"
/>
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
@change="handleActiveDebugChange"
/>
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:is-definition="props.isDefinition"
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
:assertion-config="requestVModel.children[0].assertionConfig"
:show-extraction="true"
/>
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
@change="handleActiveDebugChange"
/>
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
@change="handleActiveDebugChange"
/>
</div>
</a-spin>
<response
v-show="showResponse"
ref="responseRef"
v-model:active-layout="activeLayout"
v-model:active-tab="requestVModel.responseActiveTab"
v-model:response-definition="requestVModel.responseDefinition"
class="response"
:show-response-result-button="requestVModel.mode === 'debug'"
:is-http-protocol="isHttpProtocol"
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:hide-layout-switch="props.hideResponseLayoutSwitch"
:request-result="requestVModel.response?.requestResults[0]"
:console="requestVModel.response?.console"
:is-edit="props.isDefinition && isHttpProtocol && !props.isCase"
:upload-temp-file-api="props.uploadTempFileApi"
:loading="requestVModel.executeLoading || loading"
:is-definition="props.isDefinition"
@change="handleActiveDebugChange"
@execute="(executeType) => (props.isCase ? emit('execute', executeType) : execute(executeType))"
/>
</div>
</div>
</div>
<a-modal
@ -461,7 +430,6 @@
import { TabItem } from '@/components/pure/ms-editable-tab/types';
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 MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import assertion from '@/components/business/ms-assertion/index.vue';
@ -913,58 +881,12 @@
requestVModel.value.response?.requestResults[0]?.responseResult.responseCode ||
props.isCase
);
const splitBoxSize = ref<string | number>(!showResponse.value ? 1 : 0.6);
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
const splitContainerRef = ref<HTMLElement>();
const secondBoxHeight = ref(0);
watch(
() => showResponse.value,
(val) => {
if (val) {
splitBoxSize.value = 0.6;
} else {
splitBoxSize.value = 1;
}
}
);
watch(
() => splitBoxSize.value,
debounce((val) => {
// 300ms
if (splitContainerRef.value) {
if (typeof val === 'string' && val.includes('px')) {
val = Number(val.split('px')[0]);
secondBoxHeight.value = splitContainerRef.value.clientHeight - val;
} else {
secondBoxHeight.value = splitContainerRef.value.clientHeight * (1 - val);
}
}
}, 300),
{
immediate: true,
}
);
const verticalSplitBoxRef = ref<InstanceType<typeof MsSplitBox>>();
const isVerticalExpanded = ref(true);
function handleVerticalExpandChange(val: boolean) {
isVerticalExpanded.value = val;
}
const responseRef = ref<InstanceType<typeof response>>();
const isVerticalExpanded = computed(() => activeLayout.value === 'vertical');
function changeVerticalExpand(val: boolean) {
isVerticalExpanded.value = val;
if (val) {
verticalSplitBoxRef.value?.expand(0.6);
} else {
verticalSplitBoxRef.value?.collapse(
splitContainerRef.value
? `${splitContainerRef.value.clientHeight - (props.hideResponseLayoutSwitch ? 37 : 42)}px`
: 0
);
}
responseRef.value?.changeExpand(val);
}
watch(
() => showResponse.value,
(val) => {
@ -976,12 +898,6 @@
}
);
function handleActiveLayoutChange() {
isVerticalExpanded.value = true;
splitBoxSize.value = 0.6;
verticalSplitBoxRef.value?.expand(0.6);
}
const saveModalVisible = ref(false);
const saveModalForm = ref({
name: '',
@ -1232,8 +1148,6 @@
requestVModel.value.executeLoading = false;
}
const responseRef = ref<InstanceType<typeof response>>();
watch(
() => requestVModel.value.id,
async () => {
@ -1696,10 +1610,6 @@
.btn-base-primary-disabled();
}
}
.tab-pane-container {
@apply flex-1 overflow-y-auto;
.ms-scroll-bar();
}
:deep(.no-content) {
.arco-tabs-content {
display: none;
@ -1711,16 +1621,6 @@
:deep(.arco-tabs-tab) {
@apply leading-none;
}
.hidden-second {
:deep(.arco-split-trigger) {
@apply hidden;
}
}
.show-second {
:deep(.arco-split-trigger) {
@apply block;
}
}
.url-input-tip {
margin-top: 2px 0 250px;
font-size: 12px;
@ -1728,4 +1628,45 @@
line-height: 16px;
@apply flex flex-col flex-nowrap items-center justify-start;
}
.request-tab-and-response {
overflow-x: hidden;
overflow-y: auto;
.ms-scroll-bar();
}
.sticky-content {
@apply sticky bg-white;
z-index: 101; // .arco-scrollbar-track100
}
.request-content-and-response {
display: flex;
&.vertical {
flex-direction: column;
.response :deep(.response-head) {
@apply sticky bg-white;
top: 48px; // tab(border-bottom)
z-index: 11;
}
.request-tab-pane {
min-height: 400px;
}
}
&.horizontal {
flex-direction: row;
min-height: calc(100% - 49px); // 49px:tab
.request {
flex: 1;
overflow-x: auto;
.ms-scroll-bar();
.request-tab-pane {
min-width: 800px;
}
}
.response {
width: 500px;
border-left: 1px solid var(--color-text-n8);
}
}
}
</style>

View File

@ -6,7 +6,6 @@
add-text="apiTestDebug.postCondition"
:response="props.response"
:disabled="props.disabled"
:height-used="heightUsed"
:sql-code-editor-height="props.sqlCodeEditorHeight"
@change="emit('change')"
>
@ -43,8 +42,6 @@
const props = defineProps<{
config: ExecuteConditionConfig;
secondBoxHeight?: number;
layout: 'horizontal' | 'vertical';
response?: string; //
isDefinition?: boolean; //
isScenario?: boolean; //
@ -59,12 +56,6 @@
const { t } = useI18n();
const innerConfig = useVModel(props, 'config', emit);
const heightUsed = computed(() => {
if (props.layout === 'horizontal') {
return 328;
}
return 328 + (props.secondBoxHeight || 0);
});
const conditionTypes = computed(() => {
if (props.isDefinition) {

View File

@ -15,7 +15,6 @@
:disabled-param-value="props.disabledParamValue"
:disabled-except-param="props.disabledExceptParam"
:columns="columns"
:height-used="heightUsed"
:scroll="{ minWidth: 1160 }"
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_QUERY"
@ -51,11 +50,8 @@
const props = defineProps<{
params: ExecuteRequestCommonParam[];
layout: 'horizontal' | 'vertical';
disabledParamValue?: boolean; //
disabledExceptParam?: boolean; //
secondBoxHeight: number;
isDrawer?: boolean;
}>();
const emit = defineEmits<{
(e: 'update:params', value: any[]): void;
@ -124,30 +120,6 @@
]);
const batchAddKeyValVisible = ref(false);
const heightUsed = ref<number | undefined>(undefined);
watch(
() => props.layout,
(val) => {
const otherHeight = props.isDrawer ? 328 : 372;
heightUsed.value = val === 'horizontal' ? otherHeight : otherHeight + props.secondBoxHeight;
},
{
immediate: true,
}
);
watch(
() => props.secondBoxHeight,
(val) => {
if (props.layout === 'vertical') {
heightUsed.value = (props.isDrawer ? 328 : 372) + val;
}
},
{
immediate: true,
}
);
/**
* 批量参数代码转换为参数表格数据

View File

@ -117,7 +117,6 @@
activeResponse.body.bodyType
)
"
class="h-[calc(100%-35px)]"
>
<!-- <MsJsonSchema
v-if="activeResponse.body.jsonBody.enableJsonSchema"
@ -129,7 +128,6 @@
v-model:model-value="currentBodyCode"
:language="currentCodeLanguage"
theme="vs"
height="100%"
:show-full-screen="false"
:show-theme-change="false"
:show-language-change="false"
@ -477,11 +475,7 @@
<style lang="less" scoped>
.response-container {
@apply overflow-y-auto;
.ms-scroll-bar();
margin-top: 8px;
height: calc(100% - 88px);
}
:deep(.arco-table-th) {
background-color: var(--color-text-n9);

View File

@ -1,25 +1,25 @@
<template>
<div class="response flex h-full min-w-[300px] flex-col">
<div :class="['response-head', props.isExpanded ? '' : 'border-t']">
<div class="response flex min-w-[300px] flex-col">
<div :class="['response-head', activeLayout === 'vertical' ? 'border-t' : '']">
<slot name="titleLeft">
<div class="flex items-center justify-between">
<template v-if="activeLayout === 'vertical'">
<MsButton
v-if="props.isExpanded"
v-show="innerIsExpanded"
type="icon"
class="!mr-0 !rounded-full bg-[rgb(var(--primary-1))]"
size="small"
@click="emit('changeExpand', false)"
@click="changeExpand(false)"
>
<icon-down :size="8" />
</MsButton>
<MsButton
v-else
v-show="!innerIsExpanded"
type="icon"
status="secondary"
class="!mr-0 !rounded-full bg-[rgb(var(--primary-1))]"
size="small"
@click="emit('changeExpand', true)"
@click="changeExpand(true)"
>
<icon-right :size="8" />
</MsButton>
@ -58,8 +58,9 @@
<responseCodeTimeSize :request-result="props.requestResult" />
</div>
<a-spin
v-show="innerIsExpanded"
:loading="props.loading"
:class="[isResponseModel ? 'h-[381px] w-full' : 'h-[calc(100%-35px)] w-full px-[16px] pb-[16px]']"
:class="[isResponseModel ? 'h-[381px] w-full' : 'w-full px-[16px] pb-[16px]']"
>
<edit
v-if="props.isEdit && activeResponseType === 'content' && responseDefinition"
@ -119,7 +120,6 @@
}
);
const emit = defineEmits<{
(e: 'changeExpand', value: boolean): void;
(e: 'changeLayout', value: Direction): void;
(e: 'change'): void;
(e: 'execute', executeType: 'localExec' | 'serverExec'): void;
@ -136,6 +136,23 @@
const responseDefinition = defineModel<ResponseItem[]>('responseDefinition', {
default: [],
});
const innerIsExpanded = defineModel<boolean>('isExpanded', {
default: true,
});
function changeExpand(isExpanded: boolean) {
innerIsExpanded.value = isExpanded;
}
watch(
() => activeLayout.value,
(val) => {
if (val === 'horizontal') {
changeExpand(true);
}
}
);
watchEffect(() => {
// null
let hasInvalid = false;
@ -210,6 +227,7 @@
defineExpose({
setActiveResponse,
changeExpand,
});
</script>

View File

@ -135,9 +135,7 @@
<style lang="less" scoped>
.response-container {
overflow: hidden;
margin-top: 8px;
height: calc(100% - 48px);
}
:deep(.arco-table-th) {
background-color: var(--color-text-n9);

View File

@ -16,7 +16,6 @@
:draggable="!props.disabledExceptParam"
:disabled-param-value="props.disabledParamValue"
:disabled-except-param="props.disabledExceptParam"
:height-used="heightUsed"
:scroll="{ minWidth: 1160 }"
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_REST"
@ -51,11 +50,8 @@
const props = defineProps<{
params: ExecuteRequestCommonParam[];
layout: 'horizontal' | 'vertical';
secondBoxHeight: number;
disabledParamValue?: boolean; //
disabledExceptParam?: boolean; //
isDrawer?: boolean;
}>();
const emit = defineEmits<{
(e: 'update:params', value: any[]): void;
@ -125,30 +121,6 @@
]);
const batchAddKeyValVisible = ref(false);
const heightUsed = ref<number | undefined>(undefined);
watch(
() => props.layout,
(val) => {
const otherHeight = props.isDrawer ? 328 : 372;
heightUsed.value = val === 'horizontal' ? otherHeight : otherHeight + props.secondBoxHeight;
},
{
immediate: true,
}
);
watch(
() => props.secondBoxHeight,
(val) => {
if (props.layout === 'vertical') {
heightUsed.value = (props.isDrawer ? 328 : 372) + val;
}
},
{
immediate: true,
}
);
/**
* 批量参数代码转换为参数表格数据

View File

@ -14,65 +14,61 @@
@cancel="handleSaveCaseCancel"
>
<div class="flex h-full flex-col overflow-hidden">
<div class="px-[16px] pt-[16px]">
<MsDetailCard
:title="`【${apiDetailInfo.num}】${apiDetailInfo.name}`"
:description="description"
class="!flex-row justify-between"
>
<template #type="{ value }">
<apiMethodName :method="value as RequestMethods" tag-size="small" is-tag />
</template>
</MsDetailCard>
<a-form ref="formRef" class="mt-[16px]" :model="detailForm" layout="vertical">
<a-form-item field="name" label="" :rules="[{ required: true, message: t('case.caseNameRequired') }]">
<div class="flex w-full items-center gap-[8px]">
<a-input
v-model:model-value="detailForm.name"
:placeholder="t('case.caseNamePlaceholder')"
allow-clear
:max-length="255"
show-word-limit
/>
<MsEnvironmentSelect ref="environmentSelectRef" :env="environmentId" />
<executeButton
ref="executeRef"
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
:execute-loading="detailForm.executeLoading"
@stop-debug="stopDebug"
@execute="handleExecute"
/>
</div>
<MsDetailCard
:title="`【${apiDetailInfo.num}】${apiDetailInfo.name}`"
:description="description"
class="m-[16px] !flex-row justify-between"
>
<template #type="{ value }">
<apiMethodName :method="value as RequestMethods" tag-size="small" is-tag />
</template>
</MsDetailCard>
<a-form ref="formNameRef" class="flex-row items-start gap-[8px] px-[16px]" :model="detailForm" layout="vertical">
<a-form-item field="name" label="" :rules="[{ required: true, message: t('case.caseNameRequired') }]">
<a-input
v-model:model-value="detailForm.name"
:placeholder="t('case.caseNamePlaceholder')"
allow-clear
:max-length="255"
show-word-limit
/>
</a-form-item>
<MsEnvironmentSelect ref="environmentSelectRef" :env="environmentId" />
<executeButton
ref="executeRef"
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
:execute-loading="detailForm.executeLoading"
@stop-debug="stopDebug"
@execute="handleExecute"
/>
</a-form>
<div class="request-tab-and-response flex-1">
<a-form ref="formRef" class="flex-row gap-[16px] px-[16px]" :model="detailForm" layout="vertical">
<a-form-item field="priority" :label="t('case.caseLevel')">
<a-select v-model:model-value="detailForm.priority" :placeholder="t('common.pleaseSelect')">
<template #label>
<span class="text-[var(--color-text-2)]"> <caseLevel :case-level="detailForm.priority" /></span>
</template>
<a-option v-for="item of casePriorityOptions" :key="item.value" :value="item.value">
<caseLevel :case-level="item.label as CaseLevel" />
</a-option>
</a-select>
</a-form-item>
<a-form-item field="status" :label="t('apiTestManagement.apiStatus')">
<a-select v-model:model-value="detailForm.status" :placeholder="t('common.pleaseSelect')">
<template #label>
<apiStatus :status="detailForm.status" size="small" />
</template>
<a-option v-for="item of Object.values(RequestCaseStatus)" :key="item" :value="item">
<apiStatus :status="item" size="small" />
</a-option>
</a-select>
</a-form-item>
<a-form-item field="tags" :label="t('common.tag')">
<MsTagsInput v-model:model-value="detailForm.tags" :max-tag-count="1" />
</a-form-item>
<div class="flex gap-[16px]">
<a-form-item field="priority" :label="t('case.caseLevel')">
<a-select v-model:model-value="detailForm.priority" :placeholder="t('common.pleaseSelect')">
<template #label>
<span class="text-[var(--color-text-2)]"> <caseLevel :case-level="detailForm.priority" /></span>
</template>
<a-option v-for="item of casePriorityOptions" :key="item.value" :value="item.value">
<caseLevel :case-level="item.label as CaseLevel" />
</a-option>
</a-select>
</a-form-item>
<a-form-item field="status" :label="t('apiTestManagement.apiStatus')">
<a-select v-model:model-value="detailForm.status" :placeholder="t('common.pleaseSelect')">
<template #label>
<apiStatus :status="detailForm.status" size="small" />
</template>
<a-option v-for="item of Object.values(RequestCaseStatus)" :key="item" :value="item">
<apiStatus :status="item" size="small" />
</a-option>
</a-select>
</a-form-item>
<a-form-item field="tags" :label="t('common.tag')">
<MsTagsInput v-model:model-value="detailForm.tags" :max-tag-count="1" />
</a-form-item>
</div>
</a-form>
</div>
<div class="px-[16px] font-medium">{{ t('apiTestManagement.requestParams') }}</div>
<div class="flex-1 overflow-hidden">
<div class="px-[16px] font-medium">{{ t('apiTestManagement.requestParams') }}</div>
<requestComposition
ref="requestCompositionRef"
v-model:request="detailForm"
@ -174,6 +170,7 @@
);
const formRef = ref<FormInstance>();
const formNameRef = ref<FormInstance>();
const requestCompositionRef = ref<InstanceType<typeof requestComposition>>();
const defaultCaseParams = inject<RequestParam>('defaultCaseParams');
const defaultDetail = computed<RequestParam>(() => {
@ -239,10 +236,11 @@
isEdit.value = false;
innerVisible.value = false;
formRef.value?.resetFields();
formNameRef.value?.resetFields();
}
function handleDrawerConfirm(isContinue: boolean) {
formRef.value?.validate(async (errors) => {
formNameRef.value?.validate(async (errors) => {
if (!errors) {
//
if (requestCompositionRef.value?.getFlattenedMessages()?.length) {
@ -393,10 +391,12 @@
max-width: 50%;
}
}
:deep(.arco-form > .arco-form-item):nth-child(1) .arco-form-item-label-col {
:deep(.arco-form):nth-of-type(1) > .arco-form-item .arco-form-item-label-col {
display: none;
}
:deep(.request-and-response) {
height: calc(100% - 56px);
.request-tab-and-response {
overflow-x: hidden;
overflow-y: auto;
.ms-scroll-bar();
}
</style>

View File

@ -100,260 +100,227 @@
</template>
</a-empty>
<div v-show="!pluginError || isHttpProtocol" class="flex h-full flex-col">
<div class="px-[18px] pt-[8px]">
<div class="flex flex-wrap items-center justify-between gap-[12px]">
<div class="flex flex-1 items-center gap-[16px]">
<a-select
v-if="requestVModel.isNew"
v-model:model-value="requestVModel.protocol"
:options="protocolOptions"
:loading="protocolLoading"
:disabled="_stepType.isQuoteApi || props.step?.isQuoteScenarioStep"
class="w-[90px]"
@change="(val) => handleActiveDebugProtocolChange(val as string)"
<div class="flex flex-wrap items-center justify-between gap-[12px] px-[18px] pt-[8px]">
<div class="flex flex-1 items-center gap-[16px]">
<a-select
v-if="requestVModel.isNew"
v-model:model-value="requestVModel.protocol"
:options="protocolOptions"
:loading="protocolLoading"
:disabled="_stepType.isQuoteApi || props.step?.isQuoteScenarioStep"
class="w-[90px]"
@change="(val) => handleActiveDebugProtocolChange(val as string)"
/>
<div v-else class="flex items-center gap-[4px]">
<apiMethodName
:method="(requestVModel.protocol as RequestMethods)"
tag-background-color="rgb(var(--link-7))"
tag-text-color="white"
is-tag
class="flex items-center"
/>
<div v-else class="flex items-center gap-[4px]">
<apiMethodName
:method="(requestVModel.protocol as RequestMethods)"
tag-background-color="rgb(var(--link-7))"
tag-text-color="white"
is-tag
class="flex items-center"
/>
<a-tooltip v-if="!isHttpProtocol" :content="requestVModel.name" :mouse-enter-delay="500">
<div class="one-line-text max-w-[350px]"> {{ requestVModel.name }}</div>
</a-tooltip>
</div>
<a-input-group v-if="isHttpProtocol" class="flex-1">
<apiMethodSelect
v-model:model-value="requestVModel.method"
class="w-[140px]"
:disabled="_stepType.isQuoteApi || props.step?.isQuoteScenarioStep"
@change="handleActiveDebugChange"
/>
<a-input
v-model:model-value="requestVModel.url"
:max-length="255"
:placeholder="showEnvPrefix ? t('apiScenario.pleaseInputUrl') : t('apiTestDebug.urlPlaceholder')"
allow-clear
class="hover:z-10"
:style="isUrlError ? 'border: 1px solid rgb(var(--danger-6);z-index: 10' : ''"
:disabled="_stepType.isQuoteApi || props.step?.isQuoteScenarioStep"
@input="() => (isUrlError = false)"
@change="handleUrlChange"
>
<template v-if="showEnvPrefix" #prefix>
{{ (appStore.currentEnvConfig as EnvConfig)?.httpConfig.find((e) => e.type === 'NONE')?.url }}
</template>
</a-input>
</a-input-group>
<a-tooltip v-if="!isHttpProtocol" :content="requestVModel.name" :mouse-enter-delay="500">
<div class="one-line-text max-w-[350px]"> {{ requestVModel.name }}</div>
</a-tooltip>
</div>
<div v-permission="[props.permissionMap?.execute]">
<template v-if="hasLocalExec">
<a-dropdown-button
v-if="!requestVModel.executeLoading"
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
class="exec-btn"
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
@select="execute"
>
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
<template #icon>
<icon-down />
</template>
<template #content>
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
</a-doption>
</template>
</a-dropdown-button>
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
{{ t('common.stop') }}
</a-button>
</template>
<a-button
v-else-if="!requestVModel.executeLoading"
class="mr-[12px]"
type="primary"
@click="() => execute('serverExec')"
<a-input-group v-if="isHttpProtocol" class="flex-1">
<apiMethodSelect
v-model:model-value="requestVModel.method"
class="w-[140px]"
:disabled="_stepType.isQuoteApi || props.step?.isQuoteScenarioStep"
@change="handleActiveDebugChange"
/>
<a-input
v-model:model-value="requestVModel.url"
:max-length="255"
:placeholder="showEnvPrefix ? t('apiScenario.pleaseInputUrl') : t('apiTestDebug.urlPlaceholder')"
allow-clear
class="hover:z-10"
:style="isUrlError ? 'border: 1px solid rgb(var(--danger-6);z-index: 10' : ''"
:disabled="_stepType.isQuoteApi || props.step?.isQuoteScenarioStep"
@input="() => (isUrlError = false)"
@change="handleUrlChange"
>
{{ t('apiTestDebug.serverExec') }}
</a-button>
<template v-if="showEnvPrefix" #prefix>
{{ (appStore.currentEnvConfig as EnvConfig)?.httpConfig.find((e) => e.type === 'NONE')?.url }}
</template>
</a-input>
</a-input-group>
</div>
<div v-permission="[props.permissionMap?.execute]">
<template v-if="hasLocalExec">
<a-dropdown-button
v-if="!requestVModel.executeLoading"
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
class="exec-btn"
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
@select="execute"
>
{{ isPriorityLocalExec ? t('apiTestDebug.localExec') : t('apiTestDebug.serverExec') }}
<template #icon>
<icon-down />
</template>
<template #content>
<a-doption :value="isPriorityLocalExec ? 'serverExec' : 'localExec'">
{{ isPriorityLocalExec ? t('apiTestDebug.serverExec') : t('apiTestDebug.localExec') }}
</a-doption>
</template>
</a-dropdown-button>
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
{{ t('common.stop') }}
</a-button>
</div>
</template>
<a-button
v-else-if="!requestVModel.executeLoading"
class="mr-[12px]"
type="primary"
@click="() => execute('serverExec')"
>
{{ t('apiTestDebug.serverExec') }}
</a-button>
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">
{{ t('common.stop') }}
</a-button>
</div>
<a-input
v-if="props.step?.stepType && _stepType.isQuoteApi && isHttpProtocol"
v-model:model-value="requestVModel.name"
:max-length="255"
:placeholder="t('apiTestManagement.apiNamePlaceholder')"
:disabled="!isEditableApi"
allow-clear
class="mt-[8px]"
/>
</div>
<div class="px-[16px]">
<div class="request-tab-and-response flex-1">
<div class="px-[18px]">
<a-input
v-if="props.step?.stepType && _stepType.isQuoteApi && isHttpProtocol"
v-model:model-value="requestVModel.name"
:max-length="255"
:placeholder="t('apiTestManagement.apiNamePlaceholder')"
:disabled="!isEditableApi"
allow-clear
class="my-[8px]"
/>
</div>
<MsTab
v-if="requestVModel.activeTab"
v-model:active-key="requestVModel.activeTab"
:content-tab-list="contentTabList"
:get-text-func="getTabBadge"
class="no-content relative mt-[8px] border-b"
class="sticky-content no-content relative top-0 mx-[16px] border-b"
/>
</div>
<div ref="splitContainerRef" class="h-[calc(100%-97px)]">
<MsSplitBox
ref="verticalSplitBoxRef"
v-model:size="splitBoxSize"
:max="!showResponse ? 1 : 0.98"
min="10px"
:direction="activeLayout"
second-container-class="!overflow-y-hidden"
:class="!showResponse ? 'hidden-second' : 'show-second'"
@expand-change="handleVerticalExpandChange"
>
<template #first>
<a-spin class="block h-full w-full" :loading="requestVModel.executeLoading || loading">
<div
:class="`flex h-full min-w-[800px] flex-col p-[16px] ${
activeLayout === 'horizontal' ? ' pr-[16px]' : ''
}`"
<div :class="`request-content-and-response ${activeLayout}`">
<a-spin class="request block h-full w-full" :loading="requestVModel.executeLoading || loading">
<div class="request-tab-pane flex flex-col p-[16px]">
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<div class="tab-pane-container">
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@change="
() => {
if (isInitPluginForm) {
handlePluginFormChange();
}
}
"
/>
</a-spin>
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
is-drawer
@change="handleActiveDebugChange"
/>
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:layout="activeLayout"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
:upload-temp-file-api="uploadTempFile"
:file-save-as-source-id="scenarioId"
:file-save-as-api="transferFile"
:file-module-options-api="getTransferOptions"
is-drawer
@change="handleActiveDebugChange"
/>
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:layout="activeLayout"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
is-drawer
@change="handleActiveDebugChange"
/>
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:layout="activeLayout"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
is-drawer
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
is-definition
:disabled="!isEditableApi"
:tip-content="t('apiScenario.openGlobalPreConditionTip')"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="responseResultBody"
:layout="activeLayout"
:disabled="!isEditableApi"
:second-box-height="secondBoxHeight"
:tip-content="t('apiScenario.openGlobalPostConditionTip')"
is-definition
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:response="responseResultBody"
is-definition
:disabled="!isEditableApi"
:assertion-config="requestVModel.children[0].assertionConfig"
:show-extraction="true"
/>
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
</div>
</div>
</a-spin>
</template>
<template #second>
<response
v-if="visible"
v-show="showResponse"
v-model:active-layout="activeLayout"
v-model:active-tab="requestVModel.responseActiveTab"
:is-http-protocol="isHttpProtocol"
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:request-result="currentResponse"
:console="currentResponse?.console"
:is-edit="false"
is-definition
hide-layout-switch
:loading="requestVModel.executeLoading || loading"
@change-expand="changeVerticalExpand"
@change-layout="handleActiveLayoutChange"
@execute="execute"
>
<template #titleRight>
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
</template>
</response>
</template>
</MsSplitBox>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@change="
() => {
if (isInitPluginForm) {
handlePluginFormChange();
}
}
"
/>
</a-spin>
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:layout="activeLayout"
@change="handleActiveDebugChange"
/>
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
:upload-temp-file-api="uploadTempFile"
:file-save-as-source-id="scenarioId"
:file-save-as-api="transferFile"
:file-module-options-api="getTransferOptions"
@change="handleActiveDebugChange"
/>
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
@change="handleActiveDebugChange"
/>
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:disabled-param-value="!isEditableApi && !isEditableParamValue"
:disabled-except-param="!isEditableApi"
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
is-definition
:disabled="!isEditableApi"
:tip-content="t('apiScenario.openGlobalPreConditionTip')"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="responseResultBody"
:disabled="!isEditableApi"
:tip-content="t('apiScenario.openGlobalPostConditionTip')"
is-definition
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:response="responseResultBody"
is-definition
:disabled="!isEditableApi"
:assertion-config="requestVModel.children[0].assertionConfig"
:show-extraction="true"
/>
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
</div>
</a-spin>
<response
v-if="visible"
v-show="showResponse"
ref="responseRef"
v-model:active-layout="activeLayout"
v-model:active-tab="requestVModel.responseActiveTab"
class="response"
:is-http-protocol="isHttpProtocol"
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:request-result="currentResponse"
:console="currentResponse?.console"
:is-edit="false"
is-definition
hide-layout-switch
:loading="requestVModel.executeLoading || loading"
@execute="execute"
>
<template #titleRight>
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" />
</template>
</response>
</div>
</div>
</div>
<!-- <addDependencyDrawer v-model:visible="showAddDependencyDrawer" :mode="addDependencyMode" /> -->
@ -368,7 +335,6 @@
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
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 assertion from '@/components/business/ms-assertion/index.vue';
import loopPagination from './loopPagination.vue';
@ -936,47 +902,13 @@
const showResponse = computed(
() => isHttpProtocol.value || 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);
watch(
() => splitBoxSize.value,
debounce((val) => {
// 300ms
if (splitContainerRef.value) {
if (typeof val === 'string' && val.includes('px')) {
val = Number(val.split('px')[0]);
secondBoxHeight.value = splitContainerRef.value.clientHeight - val;
} else {
secondBoxHeight.value = splitContainerRef.value.clientHeight * (1 - val);
}
}
}, 300),
{
immediate: true,
}
);
const verticalSplitBoxRef = ref<InstanceType<typeof MsSplitBox>>();
const isVerticalExpanded = ref(true);
function handleVerticalExpandChange(val: boolean) {
isVerticalExpanded.value = val;
}
const isVerticalExpanded = computed(() => activeLayout.value === 'vertical');
const responseRef = ref<InstanceType<typeof response>>();
function changeVerticalExpand(val: boolean) {
isVerticalExpanded.value = val;
if (val) {
verticalSplitBoxRef.value?.expand(0.6);
} else {
verticalSplitBoxRef.value?.collapse(
splitContainerRef.value ? `${splitContainerRef.value.clientHeight - 42}px` : 0
);
}
responseRef.value?.changeExpand(val);
}
watch(
() => showResponse.value,
(val) => {
@ -984,19 +916,12 @@
if (val) {
changeVerticalExpand(true);
} else {
isVerticalExpanded.value = false;
verticalSplitBoxRef.value?.collapse(1);
changeVerticalExpand(false);
}
});
}
);
function handleActiveLayoutChange() {
isVerticalExpanded.value = true;
splitBoxSize.value = 0.6;
verticalSplitBoxRef.value?.expand(0.6);
}
/**
* 生成请求参数
* @param executeType 执行类型执行时传入
@ -1373,10 +1298,6 @@
.btn-base-primary-disabled();
}
}
.tab-pane-container {
@apply flex-1 overflow-y-auto;
.ms-scroll-bar();
}
:deep(.no-content) {
.arco-tabs-content {
display: none;
@ -1388,4 +1309,29 @@
:deep(.arco-tabs-tab) {
@apply leading-none;
}
.request-tab-and-response {
overflow-x: hidden;
overflow-y: auto;
.ms-scroll-bar();
}
.sticky-content {
@apply sticky bg-white;
z-index: 101;
}
.request-content-and-response {
display: flex;
&.vertical {
flex-direction: column;
.response :deep(.response-head) {
@apply sticky bg-white;
top: 48px; // tab(border-bottom)
z-index: 11;
}
.request-tab-pane {
min-height: 400px;
}
}
}
</style>

View File

@ -114,161 +114,131 @@
</a-button>
</div>
</div>
<div class="px-[16px]">
<div class="request-tab-and-response flex-1">
<MsTab
v-model:active-key="requestVModel.activeTab"
:content-tab-list="contentTabList"
:get-text-func="getTabBadge"
no-content
class="relative mt-[8px] border-b"
class="sticky-content relative top-0 mx-[16px] border-b"
/>
</div>
<div ref="splitContainerRef" class="h-[calc(100%-97px)]">
<MsSplitBox
ref="verticalSplitBoxRef"
v-model:size="splitBoxSize"
:max="!showResponse ? 1 : 0.98"
min="10px"
:direction="activeLayout"
second-container-class="!overflow-y-hidden"
:class="!showResponse ? 'hidden-second' : 'show-second'"
@expand-change="handleVerticalExpandChange"
>
<template #first>
<a-spin class="block h-full w-full" :loading="requestVModel.executeLoading || loading">
<div
:class="`flex h-full min-w-[800px] flex-col p-[16px] ${
activeLayout === 'horizontal' ? ' pr-[16px]' : ''
}`"
<div :class="`request-content-and-response ${activeLayout}`">
<a-spin class="request block h-full w-full" :loading="requestVModel.executeLoading || loading">
<div class="request-tab-pane flex flex-col p-[16px]">
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<div class="tab-pane-container">
<a-spin
v-show="requestVModel.activeTab === RequestComposition.PLUGIN"
:loading="pluginLoading"
class="min-h-[100px] w-full"
>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@change="
() => {
if (isInitPluginForm) {
handlePluginFormChange();
}
}
"
/>
</a-spin>
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:layout="activeLayout"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
:upload-temp-file-api="uploadTempFileCase"
:file-save-as-source-id="scenarioId"
:file-save-as-api="transferFileCase"
:file-module-options-api="getTransferOptionsCase"
@change="handleActiveDebugChange"
/>
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:layout="activeLayout"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:layout="activeLayout"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
:second-box-height="secondBoxHeight"
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
is-definition
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="responseResultBody"
:layout="activeLayout"
:disabled="!isEditableApi"
:second-box-height="secondBoxHeight"
is-definition
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:response="responseResultBody"
is-definition
:disabled="!isEditableApi"
:assertion-config="requestVModel.children[0].assertionConfig"
:show-extraction="true"
script-code-editor-height="calc(100vh - 242px)"
/>
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
</div>
</div>
</a-spin>
</template>
<template #second>
<response
v-if="visible"
v-show="showResponse"
v-model:active-layout="activeLayout"
v-model:active-tab="requestVModel.responseActiveTab"
:is-http-protocol="isHttpProtocol"
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:request-result="currentResponse"
:console="currentResponse?.console"
:is-edit="false"
is-definition
hide-layout-switch
:loading="requestVModel.executeLoading || loading"
@change-expand="changeVerticalExpand"
@change-layout="handleActiveLayoutChange"
@execute="execute"
>
<template #titleRight>
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" class="!mb-0" />
</template>
</response>
</template>
</MsSplitBox>
<MsFormCreate
v-model:api="fApi"
:rule="currentPluginScript"
:option="currentPluginOptions"
@change="
() => {
if (isInitPluginForm) {
handlePluginFormChange();
}
}
"
/>
</a-spin>
<httpHeader
v-if="requestVModel.activeTab === RequestComposition.HEADER"
v-model:params="requestVModel.headers"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
:layout="activeLayout"
@change="handleActiveDebugChange"
/>
<httpBody
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
v-model:params="requestVModel.body"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
:upload-temp-file-api="uploadTempFileCase"
:file-save-as-source-id="scenarioId"
:file-save-as-api="transferFileCase"
:file-module-options-api="getTransferOptionsCase"
@change="handleActiveDebugChange"
/>
<httpQuery
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
v-model:params="requestVModel.query"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
@change="handleActiveDebugChange"
/>
<httpRest
v-else-if="requestVModel.activeTab === RequestComposition.REST"
v-model:params="requestVModel.rest"
:disabled-param-value="!isEditableApi"
:disabled-except-param="!isEditableApi"
@change="handleActiveDebugChange"
/>
<precondition
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
v-model:config="requestVModel.children[0].preProcessorConfig"
is-definition
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
<postcondition
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
v-model:config="requestVModel.children[0].postProcessorConfig"
:response="responseResultBody"
:disabled="!isEditableApi"
is-definition
@change="handleActiveDebugChange"
/>
<assertion
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
v-model:params="requestVModel.children[0].assertionConfig.assertions"
:response="responseResultBody"
is-definition
:disabled="!isEditableApi"
:assertion-config="requestVModel.children[0].assertionConfig"
:show-extraction="true"
script-code-editor-height="calc(100vh - 242px)"
/>
<auth
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
v-model:params="requestVModel.authConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
<setting
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
v-model:params="requestVModel.otherConfig"
:disabled="!isEditableApi"
@change="handleActiveDebugChange"
/>
</div>
</a-spin>
<response
v-if="visible"
v-show="showResponse"
ref="responseRef"
v-model:active-layout="activeLayout"
v-model:active-tab="requestVModel.responseActiveTab"
class="response"
:is-http-protocol="isHttpProtocol"
:is-priority-local-exec="isPriorityLocalExec"
:request-url="requestVModel.url"
:is-expanded="isVerticalExpanded"
:request-result="currentResponse"
:console="currentResponse?.console"
:is-edit="false"
is-definition
hide-layout-switch
:loading="requestVModel.executeLoading || loading"
@execute="execute"
>
<template #titleRight>
<loopPagination v-model:current-loop="currentLoop" :loop-total="loopTotal" class="!mb-0" />
</template>
</response>
</div>
</div>
</div>
</MsDrawer>
@ -282,7 +252,6 @@
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import assertion from '@/components/business/ms-assertion/index.vue';
import loopPagination from './loopPagination.vue';
@ -772,47 +741,13 @@
const showResponse = computed(
() => isHttpProtocol.value || 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);
watch(
() => splitBoxSize.value,
debounce((val) => {
// 300ms
if (splitContainerRef.value) {
if (typeof val === 'string' && val.includes('px')) {
val = Number(val.split('px')[0]);
secondBoxHeight.value = splitContainerRef.value.clientHeight - val;
} else {
secondBoxHeight.value = splitContainerRef.value.clientHeight * (1 - val);
}
}
}, 300),
{
immediate: true,
}
);
const verticalSplitBoxRef = ref<InstanceType<typeof MsSplitBox>>();
const isVerticalExpanded = ref(true);
function handleVerticalExpandChange(val: boolean) {
isVerticalExpanded.value = val;
}
const isVerticalExpanded = computed(() => activeLayout.value === 'vertical');
const responseRef = ref<InstanceType<typeof response>>();
function changeVerticalExpand(val: boolean) {
isVerticalExpanded.value = val;
if (val) {
verticalSplitBoxRef.value?.expand(0.6);
} else {
verticalSplitBoxRef.value?.collapse(
splitContainerRef.value ? `${splitContainerRef.value.clientHeight - 42}px` : 0
);
}
responseRef.value?.changeExpand(val);
}
watch(
() => showResponse.value,
(val) => {
@ -820,19 +755,12 @@
if (val) {
changeVerticalExpand(true);
} else {
isVerticalExpanded.value = false;
verticalSplitBoxRef.value?.collapse(1);
changeVerticalExpand(false);
}
});
}
);
function handleActiveLayoutChange() {
isVerticalExpanded.value = true;
splitBoxSize.value = 0.6;
verticalSplitBoxRef.value?.expand(0.6);
}
/**
* 生成请求参数
* @param executeType 执行类型执行时传入
@ -1132,14 +1060,35 @@
.btn-base-primary-disabled();
}
}
.tab-pane-container {
@apply flex-1 overflow-y-auto;
.ms-scroll-bar();
}
:deep(.arco-tabs-tab:first-child) {
margin-left: 0;
}
:deep(.arco-tabs-tab) {
@apply leading-none;
}
.request-tab-and-response {
overflow-x: hidden;
overflow-y: auto;
.ms-scroll-bar();
}
.sticky-content {
@apply sticky bg-white;
z-index: 101;
}
.request-content-and-response {
display: flex;
&.vertical {
flex-direction: column;
.response :deep(.response-head) {
@apply sticky bg-white;
top: 48px; // tab(border-bottom)
z-index: 11;
}
.request-tab-pane {
min-height: 400px;
}
}
}
</style>

View File

@ -24,7 +24,6 @@
<postcondition
v-model:config="postProcessorConfig"
:is-definition="false"
:layout="activeLayout"
sql-code-editor-height="300px"
:tip-content="t('apiScenario.openGlobalPostConditionTip')"
is-scenario
@ -57,7 +56,6 @@
const { t } = useI18n();
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
const preProcessorConfig = defineModel<ExecuteConditionConfig>('preProcessorConfig', {
required: true,
});

View File

@ -4,7 +4,6 @@
:condition-types="[RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL]"
add-text="apiTestDebug.postCondition"
response=""
:height-used="600"
:show-associated-scene="props.showAssociatedScene"
:show-pre-post-request="props.showPrePostRequest"
:request-radio-text-props="props.requestRadioTextProps"

View File

@ -4,7 +4,6 @@
:condition-types="[RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL]"
add-text="apiTestDebug.precondition"
response=""
:height-used="600"
:show-associated-scene="props.showAssociatedScene"
:show-pre-post-request="props.showPrePostRequest"
:request-radio-text-props="props.requestRadioTextProps"

View File

@ -175,7 +175,6 @@
:layout="activeLayout"
:disabled-param-value="false"
:disabled-except-param="false"
:second-box-height="secondBoxHeight"
:type-title="t('project.environmental.requestHeader')"
/>
<a-form-item class="mt-4" asterisk-position="end" field="path" :label="t('project.environmental.http.authType')">
@ -303,7 +302,6 @@
const form = ref<HttpForm>({ ...initForm });
const hostType = ref<string>('http://');
const secondBoxHeight = ref(0);
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
const httpRef = ref();

View File

@ -4,7 +4,6 @@
:layout="activeLayout"
:disabled-param-value="props.disabledParamValue"
:disabled-except-param="props.disabledExceptParam"
:second-box-height="secondBoxHeight"
@change="emit('change')"
/>
</template>
@ -32,7 +31,6 @@
}>();
const innerParams = useVModel(props, 'params', emit);
const secondBoxHeight = ref(0);
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
</script>