feat(接口管理): 接口定义页面-响应
This commit is contained in:
parent
f7fcd8b690
commit
f02b147f2e
|
@ -0,0 +1,10 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="7.5" fill="url(#paint0_linear_9153_382902)" stroke="white"/>
|
||||
<path d="M8.12 9.272V9.728C7.32 9.808 6.12 9.88 4.52 9.936L4.456 9.448C5.016 9.44 5.544 9.424 6.032 9.408V8.72H4.592V8.256H6.032V7.6H4.552V4.76H8.064V7.6H6.544V8.256H8V8.72H6.544V9.384C7.136 9.352 7.664 9.32 8.12 9.272ZM4.704 10.232L5.184 10.288C5.136 10.872 5.032 11.392 4.856 11.84L4.376 11.688C4.552 11.264 4.656 10.776 4.704 10.232ZM6.008 10.288C6.088 10.624 6.144 11.096 6.176 11.688L5.688 11.744C5.672 11.136 5.624 10.672 5.56 10.336L6.008 10.288ZM7.784 10.128C7.968 10.56 8.112 10.96 8.216 11.328C8.928 10.128 9.336 8.744 9.448 7.184H8.44V6.632H9.472C9.488 5.696 9.496 4.984 9.496 4.488H10.048C10.048 5.08 10.04 5.8 10.032 6.632H11.44V7.184H10.064C10.312 9 10.856 10.4 11.696 11.384L11.304 11.816C10.592 10.912 10.104 9.776 9.848 8.408C9.592 9.752 9.152 10.896 8.528 11.824L8.16 11.416C8.168 11.4 8.176 11.384 8.184 11.376L7.8 11.52C7.688 11.08 7.544 10.656 7.376 10.24L7.784 10.128ZM6.896 10.2C7.04 10.584 7.16 11.016 7.272 11.512L6.808 11.632C6.72 11.184 6.608 10.752 6.48 10.336L6.896 10.2ZM10.72 4.752C11.072 5.2 11.336 5.6 11.52 5.936L11.12 6.224C10.928 5.856 10.664 5.44 10.328 4.992L10.72 4.752ZM7.6 7.152V5.208H6.504V7.152H7.6ZM6.088 7.152V5.208H5.016V7.152H6.088ZM7.008 5.624L7.312 5.696C7.264 6.08 7.176 6.456 7.048 6.824L6.744 6.72C6.872 6.376 6.96 6.008 7.008 5.624ZM5.544 5.656C5.664 5.976 5.768 6.336 5.864 6.744L5.568 6.824C5.48 6.456 5.368 6.096 5.24 5.752L5.544 5.656Z" fill="#783887"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_9153_382902" x1="0" y1="8" x2="16" y2="8" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.0336452" stop-color="#F2E9F6"/>
|
||||
<stop offset="1" stop-color="white"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -146,7 +146,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
const defualtMoreActionList = [
|
||||
const defaultMoreActionList = [
|
||||
{
|
||||
eventTag: 'closeAll',
|
||||
label: t('ms.editableTab.closeAll'),
|
||||
|
@ -158,8 +158,8 @@
|
|||
];
|
||||
const mergedMoreActionList = computed(() => {
|
||||
const dl = props.atLeastOne
|
||||
? defualtMoreActionList.filter((e) => e.eventTag !== 'closeAll')
|
||||
: defualtMoreActionList;
|
||||
? defaultMoreActionList.filter((e) => e.eventTag !== 'closeAll')
|
||||
: defaultMoreActionList;
|
||||
return props.moreActionList ? [...dl, ...props.moreActionList] : dl;
|
||||
});
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
<template>
|
||||
<span>
|
||||
<a-dropdown :trigger="props.trigger || 'hover'" @select="selectHandler" @popup-visible-change="visibleChange">
|
||||
<slot>
|
||||
<MsButton type="text" size="mini" class="more-icon">
|
||||
<MsIcon type="icon-icon_more_outlined" size="16" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</slot>
|
||||
<a-dropdown
|
||||
v-model:popup-visible="visible"
|
||||
:trigger="props.trigger || 'hover'"
|
||||
@select="selectHandler"
|
||||
@popup-visible-change="visibleChange"
|
||||
>
|
||||
<div :class="['ms-more-action-trigger-content', visible ? 'ms-more-action-trigger-content--focus' : '']">
|
||||
<slot>
|
||||
<MsButton type="text" size="mini" class="more-icon-btn">
|
||||
<MsIcon type="icon-icon_more_outlined" size="16" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</slot>
|
||||
</div>
|
||||
<template #content>
|
||||
<template v-for="item of props.list">
|
||||
<a-divider
|
||||
|
@ -48,6 +55,8 @@
|
|||
|
||||
const emit = defineEmits(['select', 'close']);
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
// 检测在横线之前是否有action
|
||||
const beforeDividerHasAction = computed(() => {
|
||||
let result = false;
|
||||
|
@ -99,10 +108,23 @@
|
|||
color: rgb(var(--danger-6));
|
||||
}
|
||||
}
|
||||
.more-icon {
|
||||
padding: 4px;
|
||||
border-radius: var(--border-radius-mini);
|
||||
&:hover {
|
||||
.ms-more-action-trigger-content {
|
||||
@apply flex items-center;
|
||||
.more-icon-btn {
|
||||
padding: 2px;
|
||||
border-radius: var(--border-radius-mini);
|
||||
&:hover {
|
||||
background-color: rgb(var(--primary-9)) !important;
|
||||
.arco-icon {
|
||||
color: rgb(var(--primary-5)) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ms-more-action-trigger-content--focus {
|
||||
.more-icon-btn {
|
||||
@apply !visible;
|
||||
|
||||
background-color: rgb(var(--primary-9));
|
||||
.arco-icon {
|
||||
color: rgb(var(--primary-5));
|
||||
|
|
|
@ -23,4 +23,28 @@ export interface PluginConfig {
|
|||
options: Record<string, any>;
|
||||
script: Record<string, any>[];
|
||||
scriptType: string;
|
||||
fields?: string[]; // 插件脚本内配置的全部字段集合
|
||||
}
|
||||
// 响应结果
|
||||
export interface ResponseResult {
|
||||
requestResults: {
|
||||
body: string;
|
||||
headers: string;
|
||||
responseResult: {
|
||||
body: string;
|
||||
contentType: string;
|
||||
headers: string;
|
||||
dnsLookupTime: number;
|
||||
downloadTime: number;
|
||||
latency: number;
|
||||
responseCode: number;
|
||||
responseTime: number;
|
||||
responseSize: number;
|
||||
socketInitTime: number;
|
||||
sslHandshakeTime: number;
|
||||
tcpHandshakeTime: number;
|
||||
transferStartTime: number;
|
||||
};
|
||||
}[]; // 请求结果
|
||||
console: string;
|
||||
}
|
||||
|
|
|
@ -558,7 +558,6 @@
|
|||
await tableStore.initColumn(props.tableKey, props.columns);
|
||||
}
|
||||
}
|
||||
initColumns();
|
||||
|
||||
const { propsRes, propsEvent } = useTable(() => Promise.resolve([]), {
|
||||
firstColumnWidth: 32,
|
||||
|
@ -768,12 +767,20 @@
|
|||
handleMustContainColChange(true);
|
||||
handleTypeCheckingColChange(true);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.params,
|
||||
(arr) => {
|
||||
if (arr.length > 0) {
|
||||
propsRes.value.data = arr;
|
||||
// 后台存储无id,渲染时需要手动添加一次
|
||||
propsRes.value.data = arr.map((item, i) => {
|
||||
if (!item.id) {
|
||||
return {
|
||||
...item,
|
||||
id: new Date().getTime() + i,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
if (!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault) {
|
||||
addTableLine(arr.length - 1);
|
||||
}
|
||||
|
@ -934,6 +941,8 @@
|
|||
defineExpose({
|
||||
addTableLine,
|
||||
});
|
||||
|
||||
await initColumns();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
}
|
||||
|
||||
const props = defineProps<{
|
||||
mode: 'add' | 'rename';
|
||||
mode: 'add' | 'rename' | 'tabRename';
|
||||
nodeType?: 'MODULE' | 'API';
|
||||
visible?: boolean;
|
||||
title?: string;
|
||||
|
@ -144,6 +144,10 @@
|
|||
});
|
||||
Message.success(t('common.updateSuccess'));
|
||||
emit('renameFinish', form.value.field, props.nodeId);
|
||||
} else if (props.mode === 'tabRename') {
|
||||
// 响应 tab 重命名
|
||||
Message.success(t('common.updateSuccess'));
|
||||
emit('renameFinish', form.value.field);
|
||||
}
|
||||
if (done) {
|
||||
done(true);
|
||||
|
|
|
@ -98,13 +98,15 @@
|
|||
<a-input
|
||||
v-if="props.isDefinition"
|
||||
v-model:model-value="requestVModel.name"
|
||||
class="mt-[8px]"
|
||||
:max-length="255"
|
||||
:placeholder="t('apiTestManagement.apiNamePlaceholder')"
|
||||
allow-clear
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
</div>
|
||||
<div ref="splitContainerRef" class="h-[calc(100%-40px)]">
|
||||
<!-- 接口定义多出一个输入框占高度 40px -->
|
||||
<div ref="splitContainerRef" :class="`${props.isDefinition ? 'h-[calc(100%-80px)]' : 'h-[calc(100%-40px)]'}`">
|
||||
<MsSplitBox
|
||||
ref="splitBoxRef"
|
||||
v-model:size="splitBoxSize"
|
||||
|
@ -207,8 +209,10 @@
|
|||
:hide-layout-switch="props.hideResponseLayoutSwitch"
|
||||
:request="requestVModel"
|
||||
:loading="requestVModel.executeLoading"
|
||||
:is-edit="props.isDefinition"
|
||||
@change-expand="changeExpand"
|
||||
@change-layout="handleActiveLayoutChange"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
|
@ -272,7 +276,7 @@
|
|||
import debugAuth from './auth.vue';
|
||||
import postcondition from './postcondition.vue';
|
||||
import precondition from './precondition.vue';
|
||||
import response from './response.vue';
|
||||
import response from './response/index.vue';
|
||||
import debugSetting from './setting.vue';
|
||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
||||
|
@ -321,7 +325,7 @@
|
|||
const props = defineProps<{
|
||||
request: RequestParam; // 请求参数集合
|
||||
moduleTree: ModuleTreeNode[]; // 模块树
|
||||
detailLoading: boolean; // 详情加载状态
|
||||
detailLoading?: boolean; // 详情加载状态
|
||||
isDefinition?: boolean; // 是否是接口定义模式
|
||||
hideResponseLayoutSwitch?: boolean; // 是否隐藏响应体的布局切换
|
||||
executeApi: (...args) => Promise<any>; // 执行接口
|
||||
|
@ -340,6 +344,7 @@
|
|||
requestVModel.value.executeLoading = false; // 注册loading
|
||||
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
|
||||
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||
const isInitPluginForm = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.request.id,
|
||||
|
@ -354,8 +359,8 @@
|
|||
);
|
||||
|
||||
function handleActiveDebugChange() {
|
||||
if (!loading.value) {
|
||||
// 如果是因为加载详情触发的change则不需要标记为未保存
|
||||
if (!loading.value && !isHttpProtocol.value && isInitPluginForm.value) {
|
||||
// 如果是因为加载详情触发的change则不需要标记为未保存;或者是插件协议的话需要等待表单初始化完毕
|
||||
requestVModel.value.unSaved = true;
|
||||
}
|
||||
}
|
||||
|
@ -364,7 +369,7 @@
|
|||
const commonContentTabKey = [
|
||||
RequestComposition.PRECONDITION,
|
||||
RequestComposition.POST_CONDITION,
|
||||
RequestComposition.ASSERTION,
|
||||
// RequestComposition.ASSERTION, TODO:断言暂时无
|
||||
];
|
||||
// 请求内容插件tab
|
||||
const pluginContentTab = [
|
||||
|
@ -514,12 +519,15 @@
|
|||
const formData = tempForm || requestVModel.value;
|
||||
if (fApi.value) {
|
||||
const form = {};
|
||||
fApi.value.fields().forEach((key) => {
|
||||
pluginScriptMap.value[requestVModel.value.protocol].fields?.forEach((key) => {
|
||||
form[key] = formData[key];
|
||||
});
|
||||
fApi.value?.setValue(form);
|
||||
}
|
||||
});
|
||||
nextTick(() => {
|
||||
isInitPluginForm.value = true;
|
||||
});
|
||||
} else {
|
||||
// 如果是没有缓存也不是编辑,则需要重置表单,因为 form-create 只有一个实例,已经被其他有数据的 tab 污染了,需要重置
|
||||
nextTick(() => {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<div> </div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -1,8 +1,6 @@
|
|||
<template>
|
||||
<div class="flex h-full min-w-[300px] flex-col">
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between gap-[8px] border-b border-[var(--color-text-n8)] p-[8px_16px]"
|
||||
>
|
||||
<div :class="['response-head', props.isExpanded ? '' : 'border-t']">
|
||||
<div class="flex items-center justify-between">
|
||||
<template v-if="props.activeLayout === 'vertical'">
|
||||
<MsButton
|
||||
|
@ -25,7 +23,27 @@
|
|||
<icon-right :size="8" />
|
||||
</MsButton>
|
||||
</template>
|
||||
<div class="ml-[4px] mr-[24px] font-medium">{{ t('apiTestDebug.responseContent') }}</div>
|
||||
<div
|
||||
v-if="props.isEdit && props.response.requestResults[0]?.responseResult?.responseCode"
|
||||
class="ml-[4px] flex items-center"
|
||||
>
|
||||
<MsButton
|
||||
type="text"
|
||||
:class="['font-medium', activeResponseType === 'content' ? '' : '!text-[var(--color-text-n4)]']"
|
||||
@click="() => setActiveResponse('content')"
|
||||
>
|
||||
{{ t('apiTestDebug.responseContent') }}
|
||||
</MsButton>
|
||||
<a-divider direction="vertical" :margin="4"></a-divider>
|
||||
<MsButton
|
||||
type="text"
|
||||
:class="['font-medium', activeResponseType === 'result' ? '' : '!text-[var(--color-text-n4)]']"
|
||||
@click="() => setActiveResponse('result')"
|
||||
>
|
||||
{{ t('apiTestManagement.executeResult') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<div v-else class="ml-[4px] mr-[24px] font-medium">{{ t('apiTestDebug.responseContent') }}</div>
|
||||
<a-radio-group
|
||||
v-if="!props.hideLayoutSwitch"
|
||||
v-model:model-value="innerLayout"
|
||||
|
@ -102,120 +120,99 @@
|
|||
</div>
|
||||
</div>
|
||||
<a-spin :loading="props.loading" class="h-[calc(100%-42px)] w-full px-[18px] pb-[18px]">
|
||||
<a-tabs v-model:active-key="activeTab" class="no-content border-b border-[var(--color-text-n8)]">
|
||||
<a-tab-pane v-for="item of responseTabList" :key="item.value" :title="item.label" />
|
||||
</a-tabs>
|
||||
<div class="response-container">
|
||||
<MsCodeEditor
|
||||
v-if="activeTab === ResponseComposition.BODY"
|
||||
ref="responseEditorRef"
|
||||
:model-value="props.response.requestResults[0]?.responseResult?.body"
|
||||
:language="responseLanguage"
|
||||
theme="vs"
|
||||
height="100%"
|
||||
:languages="[LanguageEnum.JSON, LanguageEnum.HTML, LanguageEnum.XML, LanguageEnum.PLAINTEXT]"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
show-language-change
|
||||
show-charset-change
|
||||
read-only
|
||||
<div v-if="props.isEdit" class="my-[8px] w-full">
|
||||
<MsEditableTab
|
||||
v-model:active-tab="activeResponse"
|
||||
v-model:tabs="responseTabs"
|
||||
at-least-one
|
||||
hide-more-action
|
||||
@add="addResponseTab"
|
||||
>
|
||||
<template #rightTitle>
|
||||
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="copyScript">
|
||||
<template #icon>
|
||||
<MsIcon type="icon-icon_copy_outlined" class="text-var(--color-text-4)" size="12" />
|
||||
</template>
|
||||
</a-button>
|
||||
<template #label="{ tab }">
|
||||
<div class="response-tab">
|
||||
<div v-if="tab.isDefault" class="response-tab-default-icon"></div>
|
||||
{{ tab.label }}({{ tab.code }})
|
||||
<MsMoreAction
|
||||
:list="
|
||||
tab.isDefault
|
||||
? tabMoreActionList.filter((e) => e.eventTag !== 'setDefault' && e.eventTag !== 'delete')
|
||||
: tabMoreActionList
|
||||
"
|
||||
class="response-more-action"
|
||||
icon-mode="hide"
|
||||
@select="(e) => handleMoreActionSelect(e, tab as ResponseItem)"
|
||||
/>
|
||||
<popConfirm
|
||||
v-model:visible="tab.showRenamePopConfirm"
|
||||
mode="tabRename"
|
||||
:field-config="{ field: tab.label }"
|
||||
:all-names="responseTabs.map((e) => e.label)"
|
||||
@rename-finish="(val) => (tab.label = val)"
|
||||
>
|
||||
<span :id="`renameSpan${tab.id}`" class="relative"></span>
|
||||
</popConfirm>
|
||||
<a-popconfirm
|
||||
v-model:popup-visible="tab.showPopConfirm"
|
||||
position="bottom"
|
||||
content-class="w-[300px]"
|
||||
:ok-text="t('common.confirm')"
|
||||
:popup-offset="20"
|
||||
@ok="() => handleDeleteResponseTab(tab.id)"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-exclamation-circle-fill class="!text-[rgb(var(--danger-6))]" />
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="font-semibold text-[var(--color-text-1)]">
|
||||
{{ t('apiTestManagement.confirmDelete', { name: tab.label }) }}
|
||||
</div>
|
||||
</template>
|
||||
<div class="relative"></div>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</MsCodeEditor>
|
||||
<MsCodeEditor
|
||||
v-else-if="activeTab === ResponseComposition.CONSOLE"
|
||||
:model-value="response.console.trim()"
|
||||
:language="LanguageEnum.PLAINTEXT"
|
||||
theme="MS-text"
|
||||
height="100%"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
:show-language-change="false"
|
||||
:show-charset-change="false"
|
||||
read-only
|
||||
>
|
||||
</MsCodeEditor>
|
||||
<div
|
||||
v-else-if="
|
||||
activeTab === ResponseComposition.HEADER ||
|
||||
activeTab === ResponseComposition.REAL_REQUEST ||
|
||||
activeTab === ResponseComposition.EXTRACT
|
||||
"
|
||||
class="h-full rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[12px]"
|
||||
>
|
||||
<pre class="response-header-pre">{{ getResponsePreContent(activeTab) }}</pre>
|
||||
</div>
|
||||
<MsBaseTable v-else-if="activeTab === 'ASSERTION'" v-bind="propsRes" v-on="propsEvent">
|
||||
<template #status="{ record }">
|
||||
<MsTag :type="record.status === 1 ? 'success' : 'danger'" theme="light">
|
||||
{{ record.status === 1 ? t('common.success') : t('common.fail') }}
|
||||
</MsTag>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
</MsEditableTab>
|
||||
</div>
|
||||
<result
|
||||
v-if="!props.isEdit || (props.isEdit && activeResponseType === 'result')"
|
||||
v-model:activeTab="activeTab"
|
||||
:response="props.response"
|
||||
:request="props.request"
|
||||
/>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useClipboard, useVModel } from '@vueuse/core';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
|
||||
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
||||
import type { Direction } from '@/components/pure/ms-split-box/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import MsMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import result from './result.vue';
|
||||
import popConfirm from '@/views/api-test/components/popConfirm.vue';
|
||||
import responseTimeLine from '@/views/api-test/components/responseTimeLine.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ResponseResult } from '@/models/apiTest/common';
|
||||
import { ResponseComposition } from '@/enums/apiEnum';
|
||||
|
||||
import type { RequestParam } from './index.vue';
|
||||
|
||||
export interface Response {
|
||||
requestResults: {
|
||||
body: string;
|
||||
headers: string;
|
||||
responseResult: {
|
||||
body: string;
|
||||
contentType: string;
|
||||
headers: string;
|
||||
dnsLookupTime: number;
|
||||
downloadTime: number;
|
||||
latency: number;
|
||||
responseCode: number;
|
||||
responseTime: number;
|
||||
responseSize: number;
|
||||
socketInitTime: number;
|
||||
sslHandshakeTime: number;
|
||||
tcpHandshakeTime: number;
|
||||
transferStartTime: number;
|
||||
};
|
||||
}[]; // 请求结果
|
||||
console: string;
|
||||
}
|
||||
import type { RequestParam } from '../index.vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
activeTab: keyof typeof ResponseComposition;
|
||||
activeLayout?: Direction;
|
||||
isExpanded: boolean;
|
||||
response: Response;
|
||||
response: ResponseResult;
|
||||
request?: RequestParam;
|
||||
hideLayoutSwitch?: boolean; // 隐藏布局切换
|
||||
loading?: boolean;
|
||||
isEdit?: boolean; // 是否可编辑
|
||||
}>(),
|
||||
{
|
||||
activeLayout: 'vertical',
|
||||
|
@ -227,6 +224,7 @@
|
|||
(e: 'update:activeTab', value: keyof typeof ResponseComposition): void;
|
||||
(e: 'changeExpand', value: boolean): void;
|
||||
(e: 'changeLayout', value: Direction): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
@ -267,115 +265,102 @@
|
|||
}
|
||||
return 'rgb(var(--danger-7)';
|
||||
});
|
||||
// 响应体语言类型
|
||||
const responseLanguage = computed(() => {
|
||||
const { contentType } = props.response.requestResults[0].responseResult;
|
||||
if (contentType.includes('json')) {
|
||||
return LanguageEnum.JSON;
|
||||
}
|
||||
if (contentType.includes('html')) {
|
||||
return LanguageEnum.HTML;
|
||||
}
|
||||
if (contentType.includes('xml')) {
|
||||
return LanguageEnum.XML;
|
||||
}
|
||||
return LanguageEnum.PLAINTEXT;
|
||||
});
|
||||
const responseEditorRef = ref<InstanceType<typeof MsCodeEditor>>();
|
||||
|
||||
const responseTabList = [
|
||||
{
|
||||
label: t('apiTestDebug.responseBody'),
|
||||
value: ResponseComposition.BODY,
|
||||
},
|
||||
{
|
||||
label: t('apiTestDebug.responseHeader'),
|
||||
value: ResponseComposition.HEADER,
|
||||
},
|
||||
{
|
||||
label: t('apiTestDebug.realRequest'),
|
||||
value: ResponseComposition.REAL_REQUEST,
|
||||
},
|
||||
{
|
||||
label: t('apiTestDebug.console'),
|
||||
value: ResponseComposition.CONSOLE,
|
||||
},
|
||||
// {
|
||||
// label: t('apiTestDebug.extract'),
|
||||
// value: ResponseComposition.EXTRACT,
|
||||
// },
|
||||
// {
|
||||
// label: t('apiTestDebug.assertion'),
|
||||
// value: ResponseComposition.ASSERTION,
|
||||
// }, // TODO:断言暂时没加
|
||||
];
|
||||
/** 响应内容编辑状态逻辑 */
|
||||
|
||||
const { copy, isSupported } = useClipboard();
|
||||
|
||||
function copyScript() {
|
||||
if (isSupported) {
|
||||
copy(props.response.requestResults[0].responseResult.body);
|
||||
Message.success(t('common.copySuccess'));
|
||||
} else {
|
||||
Message.warning(t('apiTestDebug.copyNotSupport'));
|
||||
}
|
||||
export interface ResponseItem extends TabItem {
|
||||
isDefault?: boolean; // 是否是默认tab
|
||||
code: number; // 状态码
|
||||
showPopConfirm?: boolean; // 是否显示确认弹窗
|
||||
showRenamePopConfirm?: boolean; // 是否显示重命名确认弹窗
|
||||
}
|
||||
|
||||
function getResponsePreContent(type: keyof typeof ResponseComposition) {
|
||||
switch (type) {
|
||||
case ResponseComposition.HEADER:
|
||||
return props.response.requestResults[0].responseResult.headers.trim();
|
||||
case ResponseComposition.REAL_REQUEST:
|
||||
return props.response.requestResults[0].body
|
||||
? `${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.EXTRACT:
|
||||
// return Object.keys(props.response.extract)
|
||||
// .map((e) => `${e}: ${props.response.extract[e]}`)
|
||||
// .join('\n'); // TODO:断言暂时没加
|
||||
const activeResponseType = ref<'content' | 'result'>('content');
|
||||
|
||||
function setActiveResponse(val: 'content' | 'result') {
|
||||
activeResponseType.value = val;
|
||||
}
|
||||
|
||||
const responseTabs = ref<ResponseItem[]>([
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
label: t('apiTestManagement.response'),
|
||||
closable: false,
|
||||
code: 200,
|
||||
isDefault: true,
|
||||
showPopConfirm: false,
|
||||
showRenamePopConfirm: false,
|
||||
},
|
||||
]);
|
||||
const activeResponse = ref<ResponseItem>(responseTabs.value[0]);
|
||||
|
||||
function addResponseTab(defaultProps?: Partial<ResponseItem>) {
|
||||
responseTabs.value.push({
|
||||
label: t('apiTestManagement.response', { count: responseTabs.value.length + 1 }),
|
||||
code: 200,
|
||||
...defaultProps,
|
||||
id: new Date().getTime(),
|
||||
isDefault: false,
|
||||
showPopConfirm: false,
|
||||
showRenamePopConfirm: false,
|
||||
});
|
||||
activeResponse.value = responseTabs.value[responseTabs.value.length - 1];
|
||||
emit('change');
|
||||
}
|
||||
|
||||
const tabMoreActionList: ActionsItem[] = [
|
||||
{
|
||||
label: t('apiTestManagement.setDefault'),
|
||||
eventTag: 'setDefault',
|
||||
},
|
||||
{
|
||||
label: t('common.rename'),
|
||||
eventTag: 'rename',
|
||||
},
|
||||
{
|
||||
label: t('common.copy'),
|
||||
eventTag: 'copy',
|
||||
},
|
||||
{
|
||||
isDivider: true,
|
||||
},
|
||||
{
|
||||
label: t('common.delete'),
|
||||
eventTag: 'delete',
|
||||
danger: true,
|
||||
},
|
||||
];
|
||||
const renameValue = ref('');
|
||||
|
||||
function handleMoreActionSelect(e: ActionsItem, _tab: ResponseItem) {
|
||||
switch (e.eventTag) {
|
||||
case 'setDefault':
|
||||
responseTabs.value = responseTabs.value.map((tab) => {
|
||||
tab.isDefault = _tab.id === tab.id;
|
||||
return tab;
|
||||
});
|
||||
break;
|
||||
case 'rename':
|
||||
renameValue.value = _tab.label || '';
|
||||
document.querySelector(`#renameSpan${_tab.id}`)?.dispatchEvent(new Event('click'));
|
||||
break;
|
||||
case 'copy':
|
||||
addResponseTab({ ..._tab, label: `${_tab.label}-Copy` });
|
||||
break;
|
||||
case 'delete':
|
||||
_tab.showPopConfirm = true;
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'apiTestDebug.content',
|
||||
dataIndex: 'content',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.status',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'desc',
|
||||
showTooltip: true,
|
||||
},
|
||||
];
|
||||
const { propsRes, propsEvent } = useTable(() => Promise.resolve([]), {
|
||||
scroll: { x: '100%' },
|
||||
columns,
|
||||
});
|
||||
propsRes.value.data = [
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
content: 'Response Code equals: 200',
|
||||
status: 1,
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
content: '$.users[1].age REGEX: 31',
|
||||
status: 0,
|
||||
desc: `Value expected to match regexp '31', but it did not match: '30' match: '30'`,
|
||||
},
|
||||
] as any;
|
||||
function handleDeleteResponseTab(id: number | string) {
|
||||
responseTabs.value = responseTabs.value.filter((tab) => tab.id !== id);
|
||||
if (id === activeResponse.value.id) {
|
||||
[activeResponse.value] = responseTabs.value;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
@ -391,21 +376,37 @@
|
|||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.response-container {
|
||||
margin-top: 8px;
|
||||
height: calc(100% - 48px);
|
||||
.response-header-pre {
|
||||
@apply h-full overflow-auto bg-white;
|
||||
.ms-scroll-bar();
|
||||
.response-head {
|
||||
@apply flex flex-wrap items-center justify-between border-b;
|
||||
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--border-radius-small);
|
||||
padding: 8px 16px;
|
||||
border-color: var(--color-text-n8);
|
||||
gap: 8px;
|
||||
}
|
||||
.response-tab {
|
||||
@apply flex items-center;
|
||||
.response-tab-default-icon {
|
||||
@apply rounded-full;
|
||||
|
||||
margin-right: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url('@/assets/svg/icons/default.svg') no-repeat;
|
||||
background-size: contain;
|
||||
box-shadow: 0 0 7px 0 rgb(15 0 78 / 9%);
|
||||
}
|
||||
:deep(.response-more-action) {
|
||||
margin-left: 4px;
|
||||
.more-icon-btn {
|
||||
@apply invisible;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
:deep(.response-more-action) {
|
||||
.more-icon-btn {
|
||||
@apply visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-text-n9);
|
||||
}
|
||||
:deep(.arco-tabs-tab) {
|
||||
@apply leading-none;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,217 @@
|
|||
<template>
|
||||
<a-tabs v-model:active-key="activeTab" class="no-content border-b border-[var(--color-text-n8)]">
|
||||
<a-tab-pane v-for="item of responseCompositionTabList" :key="item.value" :title="item.label" />
|
||||
</a-tabs>
|
||||
<div class="response-container">
|
||||
<MsCodeEditor
|
||||
v-if="activeTab === ResponseComposition.BODY"
|
||||
ref="responseEditorRef"
|
||||
:model-value="props.response.requestResults[0].responseResult?.body"
|
||||
:language="responseLanguage"
|
||||
theme="vs"
|
||||
height="100%"
|
||||
:languages="[LanguageEnum.JSON, LanguageEnum.HTML, LanguageEnum.XML, LanguageEnum.PLAINTEXT]"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
show-language-change
|
||||
show-charset-change
|
||||
read-only
|
||||
>
|
||||
<template #rightTitle>
|
||||
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="copyScript">
|
||||
<template #icon>
|
||||
<MsIcon type="icon-icon_copy_outlined" class="text-var(--color-text-4)" size="12" />
|
||||
</template>
|
||||
</a-button>
|
||||
</template>
|
||||
</MsCodeEditor>
|
||||
<MsCodeEditor
|
||||
v-else-if="activeTab === ResponseComposition.CONSOLE"
|
||||
:model-value="props.response.console.trim()"
|
||||
:language="LanguageEnum.PLAINTEXT"
|
||||
theme="MS-text"
|
||||
height="100%"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
:show-language-change="false"
|
||||
:show-charset-change="false"
|
||||
read-only
|
||||
>
|
||||
</MsCodeEditor>
|
||||
<div
|
||||
v-else-if="
|
||||
activeTab === ResponseComposition.HEADER ||
|
||||
activeTab === ResponseComposition.REAL_REQUEST ||
|
||||
activeTab === ResponseComposition.EXTRACT
|
||||
"
|
||||
class="h-full rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[12px]"
|
||||
>
|
||||
<pre class="response-header-pre">{{ getResponsePreContent(activeTab) }}</pre>
|
||||
</div>
|
||||
<MsBaseTable v-else-if="activeTab === 'ASSERTION'" v-bind="propsRes" v-on="propsEvent">
|
||||
<template #status="{ record }">
|
||||
<MsTag :type="record.status === 1 ? 'success' : 'danger'" theme="light">
|
||||
{{ record.status === 1 ? t('common.success') : t('common.fail') }}
|
||||
</MsTag>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ResponseResult } from '@/models/apiTest/common';
|
||||
import { ResponseComposition } from '@/enums/apiEnum';
|
||||
|
||||
import type { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
response: ResponseResult;
|
||||
request?: RequestParam;
|
||||
}>();
|
||||
const { t } = useI18n();
|
||||
|
||||
const responseCompositionTabList = [
|
||||
{
|
||||
label: t('apiTestDebug.responseBody'),
|
||||
value: ResponseComposition.BODY,
|
||||
},
|
||||
{
|
||||
label: t('apiTestDebug.responseHeader'),
|
||||
value: ResponseComposition.HEADER,
|
||||
},
|
||||
{
|
||||
label: t('apiTestDebug.realRequest'),
|
||||
value: ResponseComposition.REAL_REQUEST,
|
||||
},
|
||||
{
|
||||
label: t('apiTestDebug.console'),
|
||||
value: ResponseComposition.CONSOLE,
|
||||
},
|
||||
// {
|
||||
// label: t('apiTestDebug.extract'),
|
||||
// value: ResponseComposition.EXTRACT,
|
||||
// },
|
||||
// {
|
||||
// label: t('apiTestDebug.assertion'),
|
||||
// value: ResponseComposition.ASSERTION,
|
||||
// }, // TODO:断言暂时没加
|
||||
];
|
||||
const activeTab = defineModel<keyof typeof ResponseComposition>('activeTab', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
// 响应体语言类型
|
||||
const responseLanguage = computed(() => {
|
||||
const { contentType } = props.response.requestResults[0].responseResult;
|
||||
if (contentType.includes('json')) {
|
||||
return LanguageEnum.JSON;
|
||||
}
|
||||
if (contentType.includes('html')) {
|
||||
return LanguageEnum.HTML;
|
||||
}
|
||||
if (contentType.includes('xml')) {
|
||||
return LanguageEnum.XML;
|
||||
}
|
||||
return LanguageEnum.PLAINTEXT;
|
||||
});
|
||||
|
||||
const { copy, isSupported } = useClipboard();
|
||||
|
||||
function copyScript() {
|
||||
if (isSupported) {
|
||||
copy(props.response.requestResults[0].responseResult.body);
|
||||
Message.success(t('common.copySuccess'));
|
||||
} else {
|
||||
Message.warning(t('apiTestDebug.copyNotSupport'));
|
||||
}
|
||||
}
|
||||
|
||||
function getResponsePreContent(type: keyof typeof ResponseComposition) {
|
||||
switch (type) {
|
||||
case ResponseComposition.HEADER:
|
||||
return props.response.requestResults[0].responseResult?.headers.trim();
|
||||
case ResponseComposition.REAL_REQUEST:
|
||||
return props.response.requestResults[0].body
|
||||
? `${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.EXTRACT:
|
||||
// return Object.keys(props.response.extract)
|
||||
// .map((e) => `${e}: ${props.response.extract[e]}`)
|
||||
// .join('\n'); // TODO:断言暂时没加
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'apiTestDebug.content',
|
||||
dataIndex: 'content',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'apiTestDebug.status',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
dataIndex: 'desc',
|
||||
showTooltip: true,
|
||||
},
|
||||
];
|
||||
const { propsRes, propsEvent } = useTable(() => Promise.resolve([]), {
|
||||
scroll: { x: '100%' },
|
||||
columns,
|
||||
});
|
||||
propsRes.value.data = [
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
content: 'Response Code equals: 200',
|
||||
status: 1,
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
id: new Date().getTime(),
|
||||
content: '$.users[1].age REGEX: 31',
|
||||
status: 0,
|
||||
desc: `Value expected to match regexp '31', but it did not match: '30' match: '30'`,
|
||||
},
|
||||
] as any;
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.response-container {
|
||||
margin-top: 8px;
|
||||
height: calc(100% - 48px);
|
||||
.response-header-pre {
|
||||
@apply h-full overflow-auto bg-white;
|
||||
.ms-scroll-bar();
|
||||
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--border-radius-small);
|
||||
}
|
||||
}
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-text-n9);
|
||||
}
|
||||
:deep(.arco-tabs-tab) {
|
||||
@apply leading-none;
|
||||
}
|
||||
</style>
|
|
@ -8,7 +8,7 @@
|
|||
</template>
|
||||
</MsEditableTab>
|
||||
</div>
|
||||
<div v-show="activeApiTab?.id === 'all'" class="flex-1">
|
||||
<div v-show="activeApiTab.id === 'all'" class="flex-1">
|
||||
<apiTable :active-module="props.activeModule" :offspring-ids="props.offspringIds" />
|
||||
</div>
|
||||
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
|
||||
|
@ -20,12 +20,12 @@
|
|||
v-model:detail-loading="loading"
|
||||
v-model:request="activeApiTab"
|
||||
:module-tree="props.moduleTree"
|
||||
hide-response-layout-swicth
|
||||
hide-response-layout-switch
|
||||
:create-api="addDebug"
|
||||
:update-api="updateDebug"
|
||||
:execute-api="executeDebug"
|
||||
:local-execute-api="localExecuteApiDebug"
|
||||
is-definiton
|
||||
is-definition
|
||||
@add-done="emit('addDone')"
|
||||
/>
|
||||
</template>
|
||||
|
@ -48,8 +48,9 @@
|
|||
</template>
|
||||
</MsSplitBox>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="case" :title="t('apiTestManagement.case')" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<a-tab-pane key="mock" title="MOCK" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<a-tab-pane v-if="!activeApiTab.isNew" key="case" :title="t('apiTestManagement.case')" class="ms-api-tab-pane">
|
||||
</a-tab-pane>
|
||||
<a-tab-pane v-if="!activeApiTab.isNew" key="mock" title="MOCK" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
<template #extra>
|
||||
<div class="flex items-center gap-[8px] pr-[24px]">
|
||||
<a-button type="outline" class="arco-btn-outline--secondary !p-[8px]">
|
||||
|
@ -136,6 +137,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
const setActiveApi: ((params: RequestParam) => void) | undefined = inject('setActiveApi');
|
||||
watch(
|
||||
() => activeApiTab.value.id,
|
||||
() => {
|
||||
if (typeof setActiveApi === 'function') {
|
||||
setActiveApi(activeApiTab.value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const initDefaultId = `debug-${Date.now()}`;
|
||||
const defaultBodyParams: ExecuteBody = {
|
||||
bodyType: RequestBodyFormat.NONE,
|
||||
|
@ -236,6 +247,7 @@
|
|||
response: cloneDeep(defaultResponse),
|
||||
isNew: true,
|
||||
};
|
||||
|
||||
function addApiTab(defaultProps?: Partial<TabItem>) {
|
||||
const id = `debug-${Date.now()}`;
|
||||
apiTabs.value.push({
|
||||
|
@ -247,11 +259,6 @@
|
|||
...defaultProps,
|
||||
});
|
||||
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1] as RequestParam;
|
||||
nextTick(() => {
|
||||
if (defaultProps) {
|
||||
handleActiveDebugChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
isExpandAll?: boolean; // 是否展开所有节点
|
||||
activeModule?: string | number; // 选中的节点 key
|
||||
readOnly?: boolean; // 是否是只读模式
|
||||
activeNodeId?: string | number; // 当前选中节点 id
|
||||
}>(),
|
||||
{
|
||||
activeModule: 'all',
|
||||
|
@ -248,6 +249,16 @@
|
|||
function setActiveFolder(id: string) {
|
||||
selectedKeys.value = [id];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.activeNodeId,
|
||||
(val) => {
|
||||
if (val) {
|
||||
selectedKeys.value = [val];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function setFocusNodeKey(node: MsTreeNodeData) {
|
||||
focusNodeKey.value = node.id || '';
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<template #first>
|
||||
<div class="p-[24px]">
|
||||
<moduleTree
|
||||
:active-node-id="activeApi?.id"
|
||||
@init="(val) => (folderTree = val)"
|
||||
@new-api="newApi"
|
||||
@import="importDrawerVisible = true"
|
||||
|
@ -50,8 +51,11 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { provide } from 'vue';
|
||||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import { RequestParam } from '../components/requestComposition/index.vue';
|
||||
import importApi from './components/import.vue';
|
||||
import management from './components/management/index.vue';
|
||||
import moduleTree from './components/moduleTree.vue';
|
||||
|
@ -63,6 +67,7 @@
|
|||
const allCount = ref(0);
|
||||
const importDrawerVisible = ref(false);
|
||||
const offspringIds = ref<string[]>([]);
|
||||
const activeApi = ref<RequestParam>();
|
||||
const managementRef = ref<InstanceType<typeof management>>();
|
||||
|
||||
function newApi() {
|
||||
|
@ -77,6 +82,12 @@
|
|||
function handleApiNodeClick(node: ModuleTreeNode) {
|
||||
managementRef.value?.newTab(node);
|
||||
}
|
||||
|
||||
function setActiveApi(params: RequestParam) {
|
||||
activeApi.value = params;
|
||||
}
|
||||
|
||||
provide('setActiveApi', setActiveApi);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -82,4 +82,8 @@ export default {
|
|||
'apiTestManagement.addPostDependency': '添加后置依赖',
|
||||
'apiTestManagement.saveAsCase': '保存为新用例',
|
||||
'apiTestManagement.apiNamePlaceholder': '请输入接口名称',
|
||||
'apiTestManagement.executeResult': '执行结果',
|
||||
'apiTestManagement.setDefault': '设为默认',
|
||||
'apiTestManagement.confirmDelete': '确认删除 {name} 吗?',
|
||||
'apiTestManagement.response': '响应{count}',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue