feat(接口场景): 引用CASE
This commit is contained in:
parent
b28e266675
commit
a0a2e389df
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-dropdown v-model:popup-visible="dropdownVisible" position="tl" trigger="click">
|
||||
<a-button size="mini" type="outline">
|
||||
<a-dropdown v-model:popup-visible="dropdownVisible" :disabled="props.disabled" position="tl" trigger="click">
|
||||
<a-button :disabled="props.disabled" size="mini" type="outline">
|
||||
<template #icon> <icon-upload class="text-[14px] !text-[rgb(var(--primary-5))]" /> </template>
|
||||
</a-button>
|
||||
<template #content>
|
||||
|
@ -40,6 +40,10 @@
|
|||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'upload', file: File): void;
|
||||
(e: 'change', _fileList: MsFileItem[], fileItem: MsFileItem): void;
|
||||
|
|
|
@ -3,8 +3,13 @@
|
|||
<!-- TODO:跟下面统一样式 -->
|
||||
<div class="flex flex-col">
|
||||
<div class="mb-1">
|
||||
<a-dropdown v-model:popup-visible="buttonDropDownVisible" position="tr" trigger="click">
|
||||
<a-button type="outline">
|
||||
<a-dropdown
|
||||
v-model:popup-visible="buttonDropDownVisible"
|
||||
:disabled="props.disabled"
|
||||
position="tr"
|
||||
trigger="click"
|
||||
>
|
||||
<a-button :disabled="props.disabled" type="outline">
|
||||
<template #icon> <icon-plus class="text-[14px]" /> </template>
|
||||
{{ t('system.orgTemplate.addAttachment') }}
|
||||
</a-button>
|
||||
|
@ -65,6 +70,7 @@
|
|||
>
|
||||
<MsTagsInput
|
||||
v-model:model-value="inputFiles"
|
||||
:disabled="props.disabled"
|
||||
:input-class="props.inputClass"
|
||||
placeholder=" "
|
||||
:max-tag-count="1"
|
||||
|
@ -151,9 +157,10 @@
|
|||
</a-popover>
|
||||
</div>
|
||||
<div v-else class="flex w-full items-center gap-[4px]">
|
||||
<dropdownMenu @link-file="associatedFile" @change="handleChange" />
|
||||
<dropdownMenu :disabled="props.disabled" @link-file="associatedFile" @change="handleChange" />
|
||||
<a-input
|
||||
v-model:model-value="inputFileName"
|
||||
:disabled="props.disabled"
|
||||
:class="props.inputClass"
|
||||
:size="props.inputSize"
|
||||
allow-clear
|
||||
|
@ -198,6 +205,7 @@
|
|||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
mode?: 'button' | 'input';
|
||||
multiple?: boolean;
|
||||
inputClass?: string;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<paramsTable
|
||||
ref="extractParamsTableRef"
|
||||
v-model:params="condition.jsonPathAssertion.assertions"
|
||||
:disabled="props.disabled"
|
||||
:selectable="false"
|
||||
:columns="jsonPathColumns"
|
||||
:scroll="{ minWidth: '700px' }"
|
||||
|
@ -37,6 +38,7 @@
|
|||
v-model:model-value="record.expression"
|
||||
class="ms-params-input"
|
||||
:max-length="255"
|
||||
:disabled="props.disabled"
|
||||
@input="() => handleExpressionChange(rowIndex)"
|
||||
@change="() => handleExpressionChange(rowIndex)"
|
||||
>
|
||||
|
@ -48,10 +50,13 @@
|
|||
<div>{{ t('apiTestDebug.expressionTip3') }}</div>
|
||||
</template>
|
||||
<MsIcon
|
||||
:disabled="props.disabled"
|
||||
type="icon-icon_flashlamp"
|
||||
:size="15"
|
||||
:class="
|
||||
disabledExpressionSuffix ? 'ms-params-input-suffix-icon--disabled' : 'ms-params-input-suffix-icon'
|
||||
disabledExpressionSuffix || props.disabled
|
||||
? 'ms-params-input-suffix-icon--disabled'
|
||||
: 'ms-params-input-suffix-icon'
|
||||
"
|
||||
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.JSON_PATH)"
|
||||
/>
|
||||
|
@ -95,6 +100,7 @@
|
|||
<paramsTable
|
||||
ref="extractParamsTableRef"
|
||||
v-model:params="condition.xpathAssertion.assertions"
|
||||
:disabled="props.disabled"
|
||||
:selectable="false"
|
||||
:columns="xPathColumns"
|
||||
:scroll="{ minWidth: '700px' }"
|
||||
|
@ -118,6 +124,7 @@
|
|||
</template>
|
||||
<a-input
|
||||
v-model:model-value="record.expression"
|
||||
:disabled="props.disabled"
|
||||
class="ms-params-input"
|
||||
:max-length="255"
|
||||
@input="() => handleExpressionChange(rowIndex)"
|
||||
|
@ -132,9 +139,12 @@
|
|||
</template>
|
||||
<MsIcon
|
||||
type="icon-icon_flashlamp"
|
||||
:disabled="props.disabled"
|
||||
:size="15"
|
||||
:class="
|
||||
disabledExpressionSuffix ? 'ms-params-input-suffix-icon--disabled' : 'ms-params-input-suffix-icon'
|
||||
disabledExpressionSuffix || props.disabled
|
||||
? 'ms-params-input-suffix-icon--disabled'
|
||||
: 'ms-params-input-suffix-icon'
|
||||
"
|
||||
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.X_PATH)"
|
||||
/>
|
||||
|
@ -178,13 +188,14 @@
|
|||
<a-radio value="XML">XML</a-radio>
|
||||
</a-radio-group>
|
||||
<div class="mt-[16px]">
|
||||
<a-checkbox v-model:model-value="condition.document.followApi">
|
||||
<a-checkbox v-model:model-value="condition.document.followApi" :disabled="props.disabled">
|
||||
<span class="text-[var(--color-text-1)]">{{ t('ms.assertion.followApi') }}</span>
|
||||
</a-checkbox>
|
||||
</div>
|
||||
<div class="mt-[16px]">
|
||||
<paramsTable
|
||||
v-model:params="condition.document.jsonAssertion"
|
||||
:disabled="props.disabled"
|
||||
:selectable="false"
|
||||
:columns="documentColumns"
|
||||
:scroll="{
|
||||
|
@ -202,6 +213,7 @@
|
|||
v-if="showDeleteSingle && (record.rowSpan > 1 || record.groupId)"
|
||||
class="ml-[8px] cursor-pointer text-[var(--color-text-4)]"
|
||||
size="20"
|
||||
:disabled="props.disabled"
|
||||
@click="deleteSingleParam(record)"
|
||||
/>
|
||||
</template>
|
||||
|
@ -236,6 +248,7 @@
|
|||
ref="extractParamsTableRef"
|
||||
v-model:params="condition.regexAssertion.assertions"
|
||||
:selectable="false"
|
||||
:disabled="props.disabled"
|
||||
:columns="xPathColumns"
|
||||
:scroll="{ minWidth: '700px' }"
|
||||
:default-param-item="xPathDefaultParamItem"
|
||||
|
@ -258,6 +271,7 @@
|
|||
</template>
|
||||
<a-input
|
||||
v-model:model-value="record.expression"
|
||||
:disabled="props.disabled"
|
||||
class="ms-params-input"
|
||||
:max-length="255"
|
||||
@input="() => handleExpressionChange(rowIndex)"
|
||||
|
@ -273,8 +287,11 @@
|
|||
<MsIcon
|
||||
type="icon-icon_flashlamp"
|
||||
:size="15"
|
||||
:disabled="props.disabled"
|
||||
:class="
|
||||
disabledExpressionSuffix ? 'ms-params-input-suffix-icon--disabled' : 'ms-params-input-suffix-icon'
|
||||
disabledExpressionSuffix || props.disabled
|
||||
? 'ms-params-input-suffix-icon--disabled'
|
||||
: 'ms-params-input-suffix-icon'
|
||||
"
|
||||
@click.stop="() => showFastExtraction(record, RequestExtractExpressionEnum.REGEX)"
|
||||
/>
|
||||
|
@ -379,6 +396,7 @@
|
|||
const props = defineProps<{
|
||||
data: Param;
|
||||
response?: string;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const activeTab = ref(ResponseBodyAssertionType.JSON_PATH);
|
||||
const activeResponseFormat = ref('XML');
|
||||
|
@ -765,6 +783,7 @@
|
|||
function deleteListItem(id: string | number) {}
|
||||
|
||||
function showFastExtraction(record: ExpressionConfig, type: ExpressionType) {
|
||||
if (props.disabled) return;
|
||||
activeRecord.value = { ...record, extractType: type };
|
||||
fastExtractionVisible.value = true;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
:columns="columns"
|
||||
:scroll="{ minWidth: '700px' }"
|
||||
:default-param-item="defaultParamItem"
|
||||
:disabled="props.disabled"
|
||||
@change="handleParamTableChange"
|
||||
/>
|
||||
</div>
|
||||
|
@ -28,6 +29,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
data: Param;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
</div>
|
||||
<a-input-number
|
||||
v-model="condition.expectedValue"
|
||||
:disabled="props.disabled"
|
||||
:step="100"
|
||||
:min="0"
|
||||
mode="button"
|
||||
|
@ -29,6 +30,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
data: ExecuteAssertion;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
height: 'calc(100vh - 490px)',
|
||||
}"
|
||||
>
|
||||
<conditionContent v-model:data="condition" is-build-in @change="handleChange" />
|
||||
<conditionContent v-model:data="condition" :disabled="disabled" is-build-in @change="handleChange" />
|
||||
</a-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -26,6 +26,7 @@
|
|||
|
||||
interface ScriptTabProps {
|
||||
data: any;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<ScriptTabProps>();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<div class="mb-[8px]">{{ t('ms.assertion.statusCode') }}</div>
|
||||
<a-select
|
||||
v-model="condition.condition"
|
||||
:disabled="props.disabled"
|
||||
class="w-[157px]"
|
||||
@change="
|
||||
emit('change', {
|
||||
|
@ -19,6 +20,7 @@
|
|||
<a-input
|
||||
v-if="showInput"
|
||||
v-model="condition.expectedValue"
|
||||
:disabled="props.disabled"
|
||||
hide-button
|
||||
class="w-[157px]"
|
||||
@change="
|
||||
|
@ -49,6 +51,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
data: Param;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="ms-assertion">
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<a-dropdown trigger="hover" @select="handleSelect">
|
||||
<a-button class="w-[84px]" type="outline">
|
||||
<a-dropdown trigger="hover" :disabled="props.disabled" @select="handleSelect">
|
||||
<a-button class="w-[84px]" type="outline" :disabled="props.disabled">
|
||||
<div class="flex flex-row items-center gap-[8px]">
|
||||
<icon-plus />
|
||||
<span>{{ t('ms.assertion.button') }}</span>
|
||||
|
@ -15,7 +15,7 @@
|
|||
</template>
|
||||
</a-dropdown>
|
||||
<div v-if="props.isDefinition && innerConfig" class="flex items-center">
|
||||
<a-switch v-model:model-value="innerConfig.enableGlobal" size="small" type="line" />
|
||||
<a-switch v-model:model-value="innerConfig.enableGlobal" :disabled="props.disabled" size="small" type="line" />
|
||||
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('ms.assertion.openGlobal') }}</div>
|
||||
<a-tooltip :content="t('ms.assertion.openGlobalTip')" position="left">
|
||||
<icon-question-circle
|
||||
|
@ -71,7 +71,7 @@
|
|||
</MsTableMoreAction>
|
||||
</div>
|
||||
|
||||
<a-switch v-model:model-value="item.enable" type="line" size="small" />
|
||||
<a-switch v-model:model-value="item.enable" :disabled="props.disabled" type="line" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
|
@ -81,18 +81,21 @@
|
|||
<ResponseHeaderTab
|
||||
v-if="valueKey === ResponseAssertionType.RESPONSE_HEADER"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<!-- 状态码 -->
|
||||
<StatusCodeTab
|
||||
v-if="valueKey === ResponseAssertionType.RESPONSE_CODE"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<!-- 响应体 -->
|
||||
<ResponseBodyTab
|
||||
v-if="valueKey === ResponseAssertionType.RESPONSE_BODY"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
:response="props.response"
|
||||
@change="handleChange"
|
||||
/>
|
||||
|
@ -100,18 +103,21 @@
|
|||
<ResponseTimeTab
|
||||
v-if="valueKey === ResponseAssertionType.RESPONSE_TIME"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<!-- 变量 -->
|
||||
<VariableTab
|
||||
v-if="valueKey === ResponseAssertionType.VARIABLE"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<!-- 脚本 -->
|
||||
<ScriptTab
|
||||
v-if="valueKey === ResponseAssertionType.SCRIPT"
|
||||
v-model:data="getCurrentItemState"
|
||||
:disabled="props.disabled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
<!-- </a-scrollbar> -->
|
||||
|
@ -159,6 +165,7 @@
|
|||
isDefinition?: boolean; // 是否是定义页面
|
||||
assertionConfig?: ExecuteAssertionConfig; // 是否开启全局
|
||||
response?: string; // 响应内容
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
<a-select
|
||||
v-model="innerLanguageType"
|
||||
:disabled="props.disabled"
|
||||
class="max-w-[50%]"
|
||||
:placeholder="t('project.commonScript.pleaseSelected')"
|
||||
@change="changeHandler"
|
||||
|
@ -54,6 +55,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
expand: boolean;
|
||||
disabled?: boolean;
|
||||
languagesType: Language;
|
||||
}>();
|
||||
|
||||
|
@ -119,6 +121,7 @@
|
|||
}
|
||||
|
||||
function handleClick(obj: CommonScriptMenu) {
|
||||
if (props.disabled) return;
|
||||
let code = '';
|
||||
if (obj.command) {
|
||||
code = _handleCommand(obj.command);
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
height="460px"
|
||||
theme="vs"
|
||||
:language="innerLanguagesType"
|
||||
:read-only="false"
|
||||
:read-only="props.disabled"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
>
|
||||
|
@ -37,6 +37,7 @@
|
|||
<MsScriptMenu
|
||||
v-model:expand="expandMenu"
|
||||
v-model:languagesType="innerLanguagesType"
|
||||
:disabled="props.disabled"
|
||||
@insert="insertHandler"
|
||||
@form-api-import="formApiImport"
|
||||
@insert-common-script="insertCommonScript"
|
||||
|
@ -91,6 +92,7 @@
|
|||
const props = withDefaults(
|
||||
defineProps<{
|
||||
showType: 'commonScript' | 'executionResult'; // 执行类型
|
||||
disabled?: boolean;
|
||||
language: Language;
|
||||
code: string;
|
||||
enableRadioSelected?: boolean;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<slot name="tbutton"></slot>
|
||||
</div>
|
||||
</slot>
|
||||
<div>
|
||||
<div class="right-operation-button-icon">
|
||||
<MsButton
|
||||
v-if="props.showFullScreen"
|
||||
type="icon"
|
||||
|
@ -268,16 +268,16 @@
|
|||
@apply w-full;
|
||||
|
||||
line-height: 24px;
|
||||
.ms-drawer-fullscreen-btn {
|
||||
.right-operation-button-icon .ms-button-icon {
|
||||
border-radius: var(--border-radius-small);
|
||||
color: var(--color-text-1);
|
||||
.ms-drawer-fullscreen-btn-icon {
|
||||
.arco-icon {
|
||||
margin-right: 8px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
&:hover {
|
||||
color: rgb(var(--primary-5));
|
||||
.ms-drawer-fullscreen-btn-icon {
|
||||
.arco-icon {
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
<slot name="title" :item="item" :index="index"></slot>
|
||||
</div>
|
||||
<div class="flex items-center gap-[4px]">
|
||||
<icon-drag-dot-vertical v-if="props.draggable" class="ms-list-drag-icon" />
|
||||
<icon-drag-dot-vertical v-if="props.draggable && !props.disabled" class="ms-list-drag-icon" />
|
||||
<div
|
||||
v-if="$slots['itemAction'] || (props.itemMoreActions && props.itemMoreActions.length > 0)"
|
||||
v-if="
|
||||
$slots['itemAction'] || (props.itemMoreActions && props.itemMoreActions.length > 0 && !props.disabled)
|
||||
"
|
||||
class="ms-list-item-actions"
|
||||
>
|
||||
<slot name="itemAction" :item="item" :index="index"></slot>
|
||||
|
@ -98,6 +100,7 @@
|
|||
itemClass?: string;
|
||||
activeItemClass?: string;
|
||||
draggable?: boolean;
|
||||
disabled?: boolean;
|
||||
virtualListProps?: VirtualListProps;
|
||||
}>(),
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-tabs v-model:active-key="innerActiveKey" :class="props.class">
|
||||
<a-tabs v-model:active-key="innerActiveKey" :class="[props.class, props.noContent ? 'no-content' : '']">
|
||||
<a-tab-pane v-for="item of props.contentTabList" :key="item.value" :title="item.label">
|
||||
<template #title>
|
||||
<a-badge
|
||||
|
@ -27,6 +27,7 @@
|
|||
contentTabList: { label: string; value: string }[];
|
||||
class?: string;
|
||||
getTextFunc?: (value: any) => string;
|
||||
noContent?: boolean;
|
||||
}>(),
|
||||
{
|
||||
getTextFunc: (value: any) => value,
|
||||
|
@ -55,4 +56,9 @@
|
|||
background-color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
.no-content {
|
||||
:deep(.arco-tabs-content) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -142,5 +142,6 @@ export default {
|
|||
'common.batchDebug': 'Batch debug',
|
||||
'common.quote': 'Quote',
|
||||
'common.notQuote': 'Not quote',
|
||||
'common.execute': '执行',
|
||||
'common.execute': 'execute',
|
||||
'common.replace': 'replace',
|
||||
};
|
||||
|
|
|
@ -144,4 +144,5 @@ export default {
|
|||
'common.quote': '引用',
|
||||
'common.notQuote': '不引用',
|
||||
'common.execute': '执行',
|
||||
'common.replace': '替换',
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-button type="outline" size="mini" @click="showBatchAddParamDrawer = true">
|
||||
<a-button :disabled="props.disabled" type="outline" size="mini" @click="showBatchAddParamDrawer = true">
|
||||
{{ t('apiTestDebug.batchAdd') }}
|
||||
</a-button>
|
||||
<MsDrawer
|
||||
|
@ -54,6 +54,7 @@
|
|||
defaultParamItem?: Record<string, any>; // 默认参数项
|
||||
noParamType?: boolean; // 是否有参数类型
|
||||
addTypeText?: string; // 添加类型文案
|
||||
disabled?: boolean;
|
||||
}>(),
|
||||
{
|
||||
noParamType: false,
|
||||
|
|
|
@ -88,7 +88,12 @@
|
|||
<div class="one-line-text mr-[4px] max-w-[110px] font-medium text-[var(--color-text-1)]">
|
||||
{{ condition.name }}
|
||||
</div>
|
||||
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" />
|
||||
<MsIcon
|
||||
v-show="!props.disabled"
|
||||
type="icon-icon_edit_outlined"
|
||||
class="edit-script-name-icon"
|
||||
@click="showEditScriptNameInput"
|
||||
/>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<a-popover class="h-auto" position="right">
|
||||
|
@ -127,6 +132,7 @@
|
|||
<div class="flex items-center gap-[8px]">
|
||||
<a-button
|
||||
v-if="props.isFormat"
|
||||
:disabled="props.disabled"
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary p-[0_8px]"
|
||||
size="mini"
|
||||
|
@ -137,13 +143,25 @@
|
|||
</template>
|
||||
{{ t('project.commonScript.formatting') }}
|
||||
</a-button>
|
||||
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="undoScript">
|
||||
<a-button
|
||||
:disabled="props.disabled"
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary p-[0_8px]"
|
||||
size="mini"
|
||||
@click="undoScript"
|
||||
>
|
||||
<template #icon>
|
||||
<MsIcon type="icon-icon_undo_outlined" class="text-var(--color-text-4)" size="12" />
|
||||
</template>
|
||||
{{ t('common.revoke') }}
|
||||
</a-button>
|
||||
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="clearScript">
|
||||
<a-button
|
||||
:disabled="props.disabled"
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary p-[0_8px]"
|
||||
size="mini"
|
||||
@click="clearScript"
|
||||
>
|
||||
<template #icon>
|
||||
<MsIcon type="icon-icon_clear" class="text-var(--color-text-4)" size="12" />
|
||||
</template>
|
||||
|
@ -151,6 +169,7 @@
|
|||
</a-button>
|
||||
<a-button
|
||||
v-if="!props.isBuildIn && !props.showPrePostRequest"
|
||||
:disabled="props.disabled"
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary p-[0_8px]"
|
||||
size="mini"
|
||||
|
@ -160,6 +179,7 @@
|
|||
</a-button>
|
||||
<a-button
|
||||
v-if="!props.isBuildIn"
|
||||
:disabled="props.disabled"
|
||||
type="outline"
|
||||
class="arco-btn-outline--secondary p-[0_8px]"
|
||||
size="mini"
|
||||
|
@ -175,6 +195,7 @@
|
|||
ref="scriptDefinedRef"
|
||||
v-model:code="condition.script"
|
||||
v-model:language="condition.scriptLanguage"
|
||||
:disabled="props.disabled"
|
||||
show-type="commonScript"
|
||||
:show-header="false"
|
||||
/>
|
||||
|
@ -248,6 +269,7 @@
|
|||
<div class="mb-[16px] h-[300px]">
|
||||
<MsCodeEditor
|
||||
v-model:model-value="condition.script"
|
||||
:read-only="props.disabled"
|
||||
theme="vs"
|
||||
height="276px"
|
||||
:language="LanguageEnum.SQL"
|
||||
|
@ -310,6 +332,7 @@
|
|||
</div>
|
||||
<a-input-number
|
||||
v-model:model-value="condition.delay"
|
||||
:disabled="props.disabled"
|
||||
mode="button"
|
||||
:step="100"
|
||||
:min="0"
|
||||
|
@ -467,6 +490,7 @@
|
|||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data: ExecuteConditionProcessor;
|
||||
disabled?: boolean;
|
||||
response?: string; // 响应内容
|
||||
heightUsed?: number;
|
||||
isBuildIn?: boolean; // 是否是内置的条件
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<a-dropdown @select="(val) => addCondition(val as ConditionType)">
|
||||
<a-button type="outline">
|
||||
<a-button type="outline" :disabled="props.disabled">
|
||||
<template #icon>
|
||||
<icon-plus :size="14" />
|
||||
</template>
|
||||
|
@ -21,6 +21,7 @@
|
|||
<div class="h-full w-[20%] min-w-[220px]">
|
||||
<conditionList
|
||||
v-model:list="data"
|
||||
:disabled="props.disabled"
|
||||
:active-id="activeItem.id"
|
||||
:show-associated-scene="props.showAssociatedScene"
|
||||
:show-pre-post-request="props.showPrePostRequest"
|
||||
|
@ -30,6 +31,7 @@
|
|||
</div>
|
||||
<conditionContent
|
||||
v-model:data="activeItem"
|
||||
:disabled="props.disabled"
|
||||
:total-list="data"
|
||||
:response="props.response"
|
||||
:height-used="props.heightUsed"
|
||||
|
@ -56,6 +58,7 @@
|
|||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
list: ExecuteConditionProcessor[];
|
||||
conditionTypes: Array<ConditionType>;
|
||||
addText: string;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
v-model:data="data"
|
||||
mode="static"
|
||||
item-key-field="id"
|
||||
:disabled="props.disabled"
|
||||
:item-border="false"
|
||||
class="h-full rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[12px]"
|
||||
item-class="mb-[4px] bg-white !p-[4px_8px]"
|
||||
|
@ -47,7 +48,13 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #itemRight="{ item }">
|
||||
<a-switch v-model:model-value="item.enable" size="small" type="line" @change="() => emit('change')" />
|
||||
<a-switch
|
||||
v-model:model-value="item.enable"
|
||||
:disabled="props.disabled"
|
||||
size="small"
|
||||
type="line"
|
||||
@change="() => emit('change')"
|
||||
/>
|
||||
</template>
|
||||
</MsList>
|
||||
</template>
|
||||
|
@ -68,6 +75,7 @@
|
|||
list: ExecuteConditionProcessor[];
|
||||
activeId?: string | number;
|
||||
showAssociatedScene?: boolean;
|
||||
disabled?: boolean;
|
||||
showPrePostRequest?: boolean; // 是否展示前后置请求忽略选项
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-dropdown-button
|
||||
v-if="!caseDetail.executeLoading"
|
||||
v-if="!caseDetail?.executeLoading && !props.executeLoading"
|
||||
v-permission="['PROJECT_API_DEFINITION_CASE:READ+EXECUTE']"
|
||||
class="exec-btn"
|
||||
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
||||
|
@ -36,17 +36,24 @@
|
|||
import { defaultResponse } from '@/views/api-test/components/config';
|
||||
|
||||
const props = defineProps<{
|
||||
environmentId: string;
|
||||
environmentId?: string;
|
||||
request?: (...args) => Record<string, any>;
|
||||
isCaseDetail?: boolean;
|
||||
executeCase?: boolean;
|
||||
executeLoading?: boolean;
|
||||
isEmit?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'execute', executeType?: 'localExec' | 'serverExec'): void;
|
||||
(e: 'stopDebug'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const caseDetail = defineModel<RequestParam>('detail', {
|
||||
required: true,
|
||||
required: false,
|
||||
});
|
||||
|
||||
const apiLocalExec = inject<Ref<LocalConfig>>('apiLocalExec');
|
||||
|
@ -66,6 +73,7 @@
|
|||
executeType === 'localExec' ? localExecuteUrl.value : ''
|
||||
);
|
||||
websocket.value.addEventListener('message', (event) => {
|
||||
if (!caseDetail.value || props.isEmit) return;
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.msgType === 'EXEC_RESULT') {
|
||||
if (caseDetail.value.reportId === data.reportId) {
|
||||
|
@ -85,6 +93,10 @@
|
|||
}
|
||||
|
||||
async function execute(executeType?: 'localExec' | 'serverExec') {
|
||||
if (!caseDetail.value || props.isEmit) {
|
||||
emit('execute', executeType);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
caseDetail.value.executeLoading = true;
|
||||
caseDetail.value.response = cloneDeep(defaultResponse);
|
||||
|
@ -129,6 +141,10 @@
|
|||
}
|
||||
|
||||
function stopDebug() {
|
||||
if (!caseDetail.value || props.isEmit) {
|
||||
emit('stopDebug');
|
||||
return;
|
||||
}
|
||||
websocket.value?.close();
|
||||
caseDetail.value.executeLoading = false;
|
||||
}
|
||||
|
@ -146,6 +162,7 @@
|
|||
defineExpose({
|
||||
isPriorityLocalExec,
|
||||
execute,
|
||||
localExecuteUrl,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
ref="inputRef"
|
||||
v-model:model-value="innerValue"
|
||||
:max-length="255"
|
||||
:disabled="props.disabled"
|
||||
:size="props.size"
|
||||
:placeholder="t('ms.paramsInput.commonPlaceholder')"
|
||||
class="ms-form-table-input"
|
||||
|
@ -29,6 +30,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
desc: string;
|
||||
disabled?: boolean;
|
||||
size?: 'small' | 'large' | 'medium' | 'mini';
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
<template #documentMustIncludeTitle>
|
||||
<div class="flex flex-row items-center gap-[4px]">
|
||||
<a-checkbox
|
||||
:disabled="props.disabled"
|
||||
:model-value="mustIncludeAllChecked"
|
||||
:indeterminate="mustIncludeIndeterminate"
|
||||
@change="(v) => handleMustIncludeChange(v as boolean)"
|
||||
|
@ -47,6 +48,7 @@
|
|||
<template #documentTypeCheckingTitle>
|
||||
<div class="flex flex-row items-center gap-[4px]">
|
||||
<a-checkbox
|
||||
:disabled="props.disabled"
|
||||
:model-value="typeCheckingAllChecked"
|
||||
:indeterminate="typeCheckingIndeterminate"
|
||||
@change="(v) => handleTypeCheckingChange(v as boolean)"
|
||||
|
@ -73,6 +75,7 @@
|
|||
<a-auto-complete
|
||||
v-if="columnConfig.inputType === 'autoComplete'"
|
||||
v-model:model-value="record[columnConfig.dataIndex as string]"
|
||||
:disabled="props.disabled"
|
||||
:data="columnConfig.autoCompleteParams?.filter((e) => e.isShow === true)"
|
||||
class="ms-form-table-input"
|
||||
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
|
||||
|
@ -96,6 +99,7 @@
|
|||
<a-input
|
||||
v-else
|
||||
v-model:model-value="record[columnConfig.dataIndex as string]"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="t('apiTestDebug.paramNamePlaceholder')"
|
||||
class="ms-form-table-input"
|
||||
:max-length="255"
|
||||
|
@ -111,6 +115,7 @@
|
|||
:content="t(record.required ? 'apiTestDebug.paramRequired' : 'apiTestDebug.paramNotRequired')"
|
||||
>
|
||||
<MsButton
|
||||
:disabled="props.disabled"
|
||||
type="icon"
|
||||
:class="[
|
||||
record.required ? '!text-[rgb(var(--danger-5))]' : '!text-[var(--color-text-brand)]',
|
||||
|
@ -124,6 +129,7 @@
|
|||
</a-tooltip>
|
||||
<a-select
|
||||
v-model:model-value="record.paramType"
|
||||
:disabled="props.disabled"
|
||||
:options="columnConfig.typeOptions || []"
|
||||
class="ms-form-table-input w-full"
|
||||
size="mini"
|
||||
|
@ -134,6 +140,7 @@
|
|||
<template #extractType="{ record, columnConfig, rowIndex }">
|
||||
<a-select
|
||||
v-model:model-value="record.extractType"
|
||||
:disabled="props.disabled"
|
||||
:options="columnConfig.typeOptions || []"
|
||||
class="ms-form-table-input w-[110px]"
|
||||
size="mini"
|
||||
|
@ -144,6 +151,7 @@
|
|||
<template #variableType="{ record, columnConfig, rowIndex }">
|
||||
<a-select
|
||||
v-model:model-value="record.variableType"
|
||||
:disabled="props.disabled"
|
||||
:options="columnConfig.typeOptions || []"
|
||||
class="ms-form-table-input w-[110px]"
|
||||
size="mini"
|
||||
|
@ -154,6 +162,7 @@
|
|||
<template #extractScope="{ record, columnConfig, rowIndex }">
|
||||
<a-select
|
||||
v-model:model-value="record.extractScope"
|
||||
:disabled="props.disabled"
|
||||
:options="columnConfig.typeOptions || []"
|
||||
class="ms-form-table-input w-[180px]"
|
||||
size="mini"
|
||||
|
@ -192,6 +201,7 @@
|
|||
<MsAddAttachment
|
||||
v-else-if="record.paramType === RequestParamsType.FILE"
|
||||
v-model:file-list="record.files"
|
||||
:disabled="props.disabled"
|
||||
mode="input"
|
||||
:multiple="true"
|
||||
:fields="{
|
||||
|
@ -210,6 +220,7 @@
|
|||
<MsParamsInput
|
||||
v-else
|
||||
v-model:value="record.value"
|
||||
:disabled="props.disabled"
|
||||
size="mini"
|
||||
@change="() => addTableLine(rowIndex)"
|
||||
@dblclick="quickInputParams(record)"
|
||||
|
@ -221,6 +232,7 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<a-input-number
|
||||
v-model:model-value="record.minLength"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="t('apiTestDebug.paramMin')"
|
||||
:min="0"
|
||||
class="ms-form-table-input ms-form-table-input-number"
|
||||
|
@ -231,6 +243,7 @@
|
|||
<div class="mx-[4px]">{{ t('common.to') }}</div>
|
||||
<a-input-number
|
||||
v-model:model-value="record.maxLength"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="t('apiTestDebug.paramMax')"
|
||||
:min="0"
|
||||
class="ms-form-table-input"
|
||||
|
@ -257,6 +270,7 @@
|
|||
</template>
|
||||
<MsTagsInput
|
||||
v-model:model-value="record[columnConfig.dataIndex as string]"
|
||||
:disabled="props.disabled"
|
||||
:max-tag-count="1"
|
||||
input-class="ms-form-table-input"
|
||||
size="mini"
|
||||
|
@ -269,6 +283,7 @@
|
|||
<template #description="{ record, columnConfig, rowIndex }">
|
||||
<paramDescInput
|
||||
v-model:desc="record[columnConfig.dataIndex as string]"
|
||||
:disabled="props.disabled"
|
||||
size="mini"
|
||||
@input="() => addTableLine(rowIndex)"
|
||||
@dblclick="quickInputDesc(record)"
|
||||
|
@ -279,6 +294,7 @@
|
|||
<template #encode="{ record, rowIndex }">
|
||||
<a-switch
|
||||
v-model:model-value="record.encode"
|
||||
:disabled="props.disabled"
|
||||
size="small"
|
||||
class="ms-form-table-input-switch"
|
||||
type="line"
|
||||
|
@ -289,6 +305,7 @@
|
|||
<template #mustContain="{ record, columnConfig }">
|
||||
<a-checkbox
|
||||
v-model:model-value="record[columnConfig.dataIndex as string]"
|
||||
:disabled="props.disabled"
|
||||
@change="handleMustContainColChange(false)"
|
||||
/>
|
||||
</template>
|
||||
|
@ -296,18 +313,25 @@
|
|||
<template #typeChecking="{ record, columnConfig }">
|
||||
<a-checkbox
|
||||
v-model:model-value="record[columnConfig.dataIndex as string]"
|
||||
:disabled="props.disabled"
|
||||
@change="handleTypeCheckingColChange(false)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 响应头 -->
|
||||
<template #header="{ record, columnConfig, rowIndex }">
|
||||
<a-select v-model="record.header" class="ms-form-table-input" size="mini" @change="() => addTableLine(rowIndex)">
|
||||
<a-select
|
||||
v-model="record.header"
|
||||
:disabled="props.disabled"
|
||||
class="ms-form-table-input"
|
||||
size="mini"
|
||||
@change="() => addTableLine(rowIndex)"
|
||||
>
|
||||
<a-option v-for="item in columnConfig.options" :key="item.value">{{ t(item.label) }}</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<!-- 匹配条件 -->
|
||||
<template #condition="{ record, columnConfig }">
|
||||
<a-select v-model="record.condition" size="mini" class="ms-form-table-input">
|
||||
<a-select v-model="record.condition" :disabled="props.disabled" size="mini" class="ms-form-table-input">
|
||||
<a-option v-for="item in columnConfig.options" :key="item.value" :value="item.value">{{
|
||||
t(item.label)
|
||||
}}</a-option>
|
||||
|
@ -325,18 +349,20 @@
|
|||
record.required ? '!text-[rgb(var(--danger-5))]' : '!text-[var(--color-text-brand)]',
|
||||
'!mr-[4px] !p-[4px]',
|
||||
]"
|
||||
:disabled="props.disabled"
|
||||
size="mini"
|
||||
@click="toggleRequired(record, rowIndex)"
|
||||
>
|
||||
<div>*</div>
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
<a-input v-model="record.expectedValue" size="mini" class="ms-form-table-input" />
|
||||
<a-input v-model="record.expectedValue" :disabled="props.disabled" size="mini" class="ms-form-table-input" />
|
||||
</template>
|
||||
<!-- 项目选择 -->
|
||||
<template #project="{ record, rowIndex }">
|
||||
<a-select
|
||||
v-model:model-value="record.projectId"
|
||||
:disabled="props.disabled"
|
||||
class="ms-form-table-input w-max-[200px] focus-within:!bg-[var(--color-text-n8)] hover:!bg-[var(--color-text-n8)]"
|
||||
:bordered="false"
|
||||
allow-search
|
||||
|
@ -364,7 +390,7 @@
|
|||
v-if="record.projectId"
|
||||
v-model:model-value="record.environmentId"
|
||||
v-model:input-value="record.environmentInput"
|
||||
:disabled="!record.projectId"
|
||||
:disabled="props.disabled || !record.projectId"
|
||||
:options="[]"
|
||||
mode="remote"
|
||||
value-key="id"
|
||||
|
@ -395,6 +421,7 @@
|
|||
<a-switch
|
||||
v-if="columnConfig.hasDisable"
|
||||
v-model="record.enable"
|
||||
:disabled="props.disabled"
|
||||
size="small"
|
||||
type="line"
|
||||
class="mr-[8px]"
|
||||
|
@ -415,6 +442,7 @@
|
|||
<div class="mb-[8px] text-[var(--color-text-1)]">Content-Type</div>
|
||||
<a-select
|
||||
v-model:model-value="record.contentType"
|
||||
:disabled="props.disabled"
|
||||
:options="Object.values(RequestContentTypeEnum).map((e) => ({ label: e, value: e }))"
|
||||
allow-create
|
||||
size="mini"
|
||||
|
@ -607,6 +635,7 @@
|
|||
const paramsLength = computed(() => paramsData.value.length);
|
||||
|
||||
function deleteParam(record: Record<string, any>, rowIndex: number) {
|
||||
if (props.disabled) return;
|
||||
if (props.isTreeTable) {
|
||||
emit('treeDelete', record);
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
<template>
|
||||
<div class="request-and-response flex-1">
|
||||
<MsTab
|
||||
v-model:active-key="requestVModel.activeTab"
|
||||
:content-tab-list="contentTabList"
|
||||
:get-text-func="getTabBadge"
|
||||
no-content
|
||||
class="relative border-b"
|
||||
/>
|
||||
<a-spin class="block h-[300px] w-full p-[16px]" :loading="requestVModel.executeLoading || loading">
|
||||
<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"
|
||||
:disabled="props.disabledExceptParam"
|
||||
:second-box-height="secondBoxHeight"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<httpBody
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
|
||||
v-model:params="requestVModel.body"
|
||||
:layout="activeLayout"
|
||||
:disabled="props.disabledExceptParam"
|
||||
: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"
|
||||
:disabled="props.disabledExceptParam"
|
||||
:second-box-height="secondBoxHeight"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<httpRest
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.REST"
|
||||
v-model:params="requestVModel.rest"
|
||||
:layout="activeLayout"
|
||||
:disabled="props.disabledExceptParam"
|
||||
:second-box-height="secondBoxHeight"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<precondition
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
|
||||
v-model:config="requestVModel.children[0].preProcessorConfig"
|
||||
:disabled="props.disabledExceptParam"
|
||||
:is-definition="false"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<postcondition
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.POST_CONDITION"
|
||||
v-model:config="requestVModel.children[0].postProcessorConfig"
|
||||
:disabled="props.disabledExceptParam"
|
||||
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
|
||||
:layout="activeLayout"
|
||||
:second-box-height="secondBoxHeight"
|
||||
:is-definition="false"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<assertion
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.ASSERTION"
|
||||
v-model:params="requestVModel.children[0].assertionConfig.assertions"
|
||||
:disabled="props.disabledExceptParam"
|
||||
:is-definition="false"
|
||||
:assertion-config="requestVModel.children[0].assertionConfig"
|
||||
/>
|
||||
<auth
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
||||
v-model:params="requestVModel.authConfig"
|
||||
:disabled="props.disabledExceptParam"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<setting
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
|
||||
v-model:params="requestVModel.otherConfig"
|
||||
:disabled="props.disabledExceptParam"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
</a-spin>
|
||||
<response
|
||||
v-model:active-layout="activeLayout"
|
||||
v-model:active-tab="requestVModel.responseActiveTab"
|
||||
:is-http-protocol="isHttpProtocol"
|
||||
:is-priority-local-exec="props.isPriorityLocalExec"
|
||||
:request-url="requestVModel.url"
|
||||
:is-expanded="true"
|
||||
:request-task-result="requestVModel.response"
|
||||
:is-edit="false"
|
||||
hide-layout-switch
|
||||
:upload-temp-file-api="props.uploadTempFileApi"
|
||||
:loading="requestVModel.executeLoading || loading"
|
||||
:is-definition="true"
|
||||
@change="handleActiveDebugChange"
|
||||
@execute="(val) => emit('execute', val)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Message, SelectOptionData } from '@arco-design/web-vue';
|
||||
import { cloneDeep, debounce } from 'lodash-es';
|
||||
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||
import assertion from '@/components/business/ms-assertion/index.vue';
|
||||
import auth from '@/views/api-test/components/requestComposition/auth.vue';
|
||||
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
|
||||
import precondition from '@/views/api-test/components/requestComposition/precondition.vue';
|
||||
import response from '@/views/api-test/components/requestComposition/response/index.vue';
|
||||
import setting from '@/views/api-test/components/requestComposition/setting.vue';
|
||||
|
||||
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import { ExecuteConditionConfig, PluginConfig } from '@/models/apiTest/common';
|
||||
import { ModuleTreeNode, TransferFileParams } from '@/models/common';
|
||||
import { RequestAuthType, RequestBodyFormat, RequestComposition, RequestConditionProcessor } from '@/enums/apiEnum';
|
||||
|
||||
import {
|
||||
defaultBodyParamsItem,
|
||||
defaultHeaderParamsItem,
|
||||
defaultKeyValueParamItem,
|
||||
defaultRequestParamsItem,
|
||||
} from '@/views/api-test/components/config';
|
||||
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
||||
import type { Api } from '@form-create/arco-design';
|
||||
|
||||
// 懒加载Http协议组件
|
||||
const httpHeader = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/header.vue'));
|
||||
const httpBody = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/body.vue'));
|
||||
const httpQuery = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/query.vue'));
|
||||
const httpRest = defineAsyncComponent(() => import('@/views/api-test/components/requestComposition/rest.vue'));
|
||||
|
||||
const props = defineProps<{
|
||||
request?: RequestParam; // 请求参数集合
|
||||
defaultParams?: RequestParam;
|
||||
detailLoading?: boolean; // 详情加载状态
|
||||
isPriorityLocalExec?: boolean; // 是否优先本地执行
|
||||
disabledExceptParam?: boolean; // 除了可以修改参数值其他都只读
|
||||
isShowCommonContentTabKey?: boolean; // 是否展示请求内容公共tabKey
|
||||
uploadTempFileApi?: (...args) => Promise<any>; // 上传临时文件接口
|
||||
fileSaveAsSourceId?: string | number; // 文件转存关联的资源id
|
||||
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; // 文件转存接口
|
||||
fileModuleOptionsApi?: (projectId: string) => Promise<ModuleTreeNode[]>; // 文件转存目录下拉框接口
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'execute', executeType?: 'localExec' | 'serverExec'): void;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const loading = defineModel<boolean>('detailLoading', { default: false });
|
||||
const requestVModel = defineModel<RequestParam>('request', { required: true });
|
||||
const isHttpProtocol = computed(() => requestVModel.value.protocol === 'HTTP');
|
||||
|
||||
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');
|
||||
const secondBoxHeight = ref(0);
|
||||
|
||||
// 请求内容公共tabKey
|
||||
const commonContentTabKey = [
|
||||
RequestComposition.PRECONDITION,
|
||||
RequestComposition.POST_CONDITION,
|
||||
RequestComposition.ASSERTION,
|
||||
];
|
||||
// 请求内容插件tab
|
||||
const pluginContentTab = [
|
||||
{
|
||||
value: RequestComposition.PLUGIN,
|
||||
label: t('apiTestDebug.pluginData'),
|
||||
},
|
||||
];
|
||||
// Http 请求的tab
|
||||
const httpContentTabList = [
|
||||
{
|
||||
value: RequestComposition.HEADER,
|
||||
label: t('apiTestDebug.header'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.BODY,
|
||||
label: t('apiTestDebug.body'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.QUERY,
|
||||
label: RequestComposition.QUERY,
|
||||
},
|
||||
{
|
||||
value: RequestComposition.REST,
|
||||
label: RequestComposition.REST,
|
||||
},
|
||||
{
|
||||
value: RequestComposition.PRECONDITION,
|
||||
label: t('apiTestDebug.prefix'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.POST_CONDITION,
|
||||
label: t('apiTestDebug.post'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.ASSERTION,
|
||||
label: t('apiTestDebug.assertion'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.AUTH,
|
||||
label: t('apiTestDebug.auth'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.SETTING,
|
||||
label: t('apiTestDebug.setting'),
|
||||
},
|
||||
];
|
||||
// 根据协议类型获取请求内容tab
|
||||
const contentTabList = computed(() => {
|
||||
// HTTP 协议 tabs
|
||||
if (isHttpProtocol.value) {
|
||||
return props.isShowCommonContentTabKey
|
||||
? httpContentTabList
|
||||
: httpContentTabList.filter((e) => !commonContentTabKey.includes(e.value));
|
||||
}
|
||||
return [...pluginContentTab, ...httpContentTabList.filter((e) => commonContentTabKey.includes(e.value))];
|
||||
});
|
||||
// 获取 tab 的参数数量徽标
|
||||
function getTabBadge(tabKey: RequestComposition) {
|
||||
switch (tabKey) {
|
||||
case RequestComposition.HEADER:
|
||||
const headerNum = filterKeyValParams(requestVModel.value.headers, defaultHeaderParamsItem).validParams.length;
|
||||
return `${headerNum > 0 ? headerNum : ''}`;
|
||||
case RequestComposition.BODY:
|
||||
return requestVModel.value.body?.bodyType !== RequestBodyFormat.NONE ? '1' : '';
|
||||
case RequestComposition.QUERY:
|
||||
const queryNum = filterKeyValParams(requestVModel.value.query, defaultRequestParamsItem).validParams.length;
|
||||
return `${queryNum > 0 ? queryNum : ''}`;
|
||||
case RequestComposition.REST:
|
||||
const restNum = filterKeyValParams(requestVModel.value.rest, defaultRequestParamsItem).validParams.length;
|
||||
return `${restNum > 0 ? restNum : ''}`;
|
||||
case RequestComposition.PRECONDITION:
|
||||
return `${requestVModel.value.children[0].preProcessorConfig.processors.length || ''}`;
|
||||
case RequestComposition.POST_CONDITION:
|
||||
return `${requestVModel.value.children[0].postProcessorConfig.processors.length || ''}`;
|
||||
case RequestComposition.ASSERTION:
|
||||
return `${requestVModel.value.children[0].assertionConfig.assertions.length || ''}`;
|
||||
case RequestComposition.AUTH:
|
||||
return requestVModel.value.authConfig.authType !== RequestAuthType.NONE ? '1' : '';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
const protocolLoading = ref(false);
|
||||
const protocolOptions = ref<SelectOptionData[]>([]);
|
||||
async function initProtocolList() {
|
||||
try {
|
||||
protocolLoading.value = true;
|
||||
const res = await getProtocolList(appStore.currentOrgId);
|
||||
protocolOptions.value = res.map((e) => ({
|
||||
label: e.protocol,
|
||||
value: e.protocol,
|
||||
polymorphicName: e.polymorphicName,
|
||||
pluginId: e.pluginId,
|
||||
}));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
protocolLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const pluginError = ref(false);
|
||||
const pluginLoading = ref(false);
|
||||
const isInitPluginForm = ref(false);
|
||||
const pluginScriptMap = ref<Record<string, PluginConfig>>({}); // 存储初始化过后的插件配置
|
||||
const temporaryPluginFormMap: Record<string, any> = {}; // 缓存插件表单,避免切换tab导致动态表单数据丢失
|
||||
|
||||
const fApi = ref<Api>();
|
||||
const currentPluginOptions = computed<Record<string, any>>(
|
||||
() => pluginScriptMap.value[requestVModel.value.protocol]?.options || {}
|
||||
);
|
||||
const currentPluginScript = computed<Record<string, any>[]>(
|
||||
() => pluginScriptMap.value[requestVModel.value.protocol]?.script || []
|
||||
);
|
||||
|
||||
function handleActiveDebugChange() {
|
||||
if (!loading.value || (!isHttpProtocol.value && isInitPluginForm.value)) {
|
||||
// 如果是因为加载详情触发的change则不需要标记为未保存;或者是插件协议的话需要等待表单初始化完毕
|
||||
requestVModel.value.unSaved = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理插件表单输入框变化
|
||||
const handlePluginFormChange = debounce(() => {
|
||||
temporaryPluginFormMap[requestVModel.value.id] = fApi.value?.formData();
|
||||
handleActiveDebugChange();
|
||||
}, 300);
|
||||
|
||||
// 控制插件表单字段显示
|
||||
function controlPluginFormFields() {
|
||||
const allFields = fApi.value?.fields();
|
||||
let fields: string[] = [];
|
||||
if (requestVModel.value.useEnv === 'true') {
|
||||
fields = pluginScriptMap.value[requestVModel.value.protocol].apiDefinitionFields || [];
|
||||
} else {
|
||||
fields = pluginScriptMap.value[requestVModel.value.protocol].apiDebugFields || [];
|
||||
}
|
||||
fApi.value?.hidden(true, allFields?.filter((e) => !fields.includes(e)) || []);
|
||||
return fields;
|
||||
}
|
||||
|
||||
// 设置插件表单数据
|
||||
function setPluginFormData() {
|
||||
const tempForm = temporaryPluginFormMap[requestVModel.value.id];
|
||||
if (tempForm || !requestVModel.value.isNew || requestVModel.value.isCopy) {
|
||||
// 如果缓存的表单数据存在或者是编辑状态,则需要将之前的输入数据填充
|
||||
const formData = tempForm || requestVModel.value;
|
||||
if (fApi.value) {
|
||||
fApi.value.nextTick(() => {
|
||||
const form = {};
|
||||
controlPluginFormFields().forEach((key) => {
|
||||
form[key] = formData[key];
|
||||
});
|
||||
fApi.value?.setValue(form);
|
||||
setTimeout(() => {
|
||||
// 初始化时赋值会触发表单数据变更,300ms 是为了与 handlePluginFormChange的防抖时间保持一致
|
||||
isInitPluginForm.value = true;
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
fApi.value?.nextTick(() => {
|
||||
controlPluginFormFields();
|
||||
});
|
||||
nextTick(() => {
|
||||
// 如果是没有缓存也不是编辑,则需要重置表单,因为 form-create 只有一个实例,已经被其他有数据的 tab 污染了,需要重置
|
||||
fApi.value?.resetFields();
|
||||
});
|
||||
}
|
||||
}
|
||||
async function initPluginScript() {
|
||||
const pluginId = protocolOptions.value.find((e) => e.value === requestVModel.value.protocol)?.pluginId;
|
||||
if (!pluginId) {
|
||||
Message.warning(t('apiTestDebug.noPluginTip'));
|
||||
pluginError.value = true;
|
||||
return;
|
||||
}
|
||||
pluginError.value = false;
|
||||
isInitPluginForm.value = false;
|
||||
if (pluginScriptMap.value[requestVModel.value.protocol] !== undefined) {
|
||||
// 已经初始化过
|
||||
setPluginFormData();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
pluginLoading.value = true;
|
||||
const res = await getPluginScript(pluginId);
|
||||
pluginScriptMap.value[requestVModel.value.protocol] = res;
|
||||
setPluginFormData();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
pluginLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function filterConditionsSqlValidParams(condition: ExecuteConditionConfig) {
|
||||
const conditionCopy = cloneDeep(condition);
|
||||
conditionCopy.processors = conditionCopy.processors.map((processor) => {
|
||||
if (processor.processorType === RequestConditionProcessor.SQL) {
|
||||
processor.extractParams = filterKeyValParams(
|
||||
processor.extractParams || [],
|
||||
defaultKeyValueParamItem
|
||||
).validParams;
|
||||
}
|
||||
return processor;
|
||||
});
|
||||
return conditionCopy;
|
||||
}
|
||||
|
||||
// 生成请求参数
|
||||
function makeRequestParams(executeType?: 'localExec' | 'serverExec') {
|
||||
const isExecute = executeType === 'localExec' || executeType === 'serverExec';
|
||||
const { formDataBody, wwwFormBody } = requestVModel.value.body;
|
||||
const polymorphicName = protocolOptions.value.find(
|
||||
(e) => e.value === requestVModel.value.protocol
|
||||
)?.polymorphicName; // 协议多态名称
|
||||
let parseRequestBodyResult;
|
||||
let requestParams;
|
||||
if (isHttpProtocol.value) {
|
||||
const realFormDataBodyValues = filterKeyValParams(
|
||||
formDataBody.formValues,
|
||||
defaultBodyParamsItem,
|
||||
isExecute
|
||||
).validParams;
|
||||
const realWwwFormBodyValues = filterKeyValParams(
|
||||
wwwFormBody.formValues,
|
||||
defaultBodyParamsItem,
|
||||
isExecute
|
||||
).validParams;
|
||||
parseRequestBodyResult = parseRequestBodyFiles(
|
||||
requestVModel.value.body,
|
||||
requestVModel.value.uploadFileIds, // 外面解析详情的时候传入
|
||||
requestVModel.value.linkFileIds // 外面解析详情的时候传入
|
||||
);
|
||||
requestParams = {
|
||||
authConfig: requestVModel.value.authConfig,
|
||||
body: {
|
||||
...requestVModel.value.body,
|
||||
formDataBody: {
|
||||
formValues: realFormDataBodyValues,
|
||||
},
|
||||
wwwFormBody: {
|
||||
formValues: realWwwFormBodyValues,
|
||||
},
|
||||
},
|
||||
headers: filterKeyValParams(requestVModel.value.headers, defaultHeaderParamsItem, isExecute).validParams,
|
||||
method: requestVModel.value.method,
|
||||
otherConfig: requestVModel.value.otherConfig,
|
||||
path: requestVModel.value.url || requestVModel.value.path,
|
||||
query: filterKeyValParams(requestVModel.value.query, defaultRequestParamsItem, isExecute).validParams,
|
||||
rest: filterKeyValParams(requestVModel.value.rest, defaultRequestParamsItem, isExecute).validParams,
|
||||
url: requestVModel.value.url,
|
||||
polymorphicName,
|
||||
};
|
||||
} else {
|
||||
requestParams = {
|
||||
...fApi.value?.formData(),
|
||||
polymorphicName,
|
||||
};
|
||||
}
|
||||
|
||||
const requestName = requestVModel.value.name ?? '';
|
||||
const requestModuleId = requestVModel.value.moduleId ?? '';
|
||||
return {
|
||||
id: requestVModel.value.id.toString(),
|
||||
name: requestName,
|
||||
moduleId: requestModuleId,
|
||||
protocol: requestVModel.value.protocol,
|
||||
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
|
||||
path: isHttpProtocol.value ? requestVModel.value.url || requestVModel.value.path : undefined,
|
||||
request: {
|
||||
...requestParams,
|
||||
name: requestName,
|
||||
children: [
|
||||
{
|
||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||
assertionConfig: requestVModel.value.children[0].assertionConfig,
|
||||
postProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].postProcessorConfig),
|
||||
preProcessorConfig: filterConditionsSqlValidParams(requestVModel.value.children[0].preProcessorConfig),
|
||||
},
|
||||
],
|
||||
},
|
||||
...parseRequestBodyResult,
|
||||
projectId: appStore.currentProjectId,
|
||||
frontendDebug: executeType === 'localExec',
|
||||
isNew: requestVModel.value.isNew,
|
||||
};
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initProtocolList();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
initPluginScript,
|
||||
handleActiveDebugChange,
|
||||
makeRequestParams,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.request-and-response {
|
||||
:deep(.response) {
|
||||
height: 400px; // TODO: 暂时
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="h-full rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
|
||||
<div class="mb-[8px]">{{ t('apiTestDebug.authType') }}</div>
|
||||
<a-radio-group v-model:model-value="authForm.authType" class="mb-[16px]">
|
||||
<a-radio-group v-model:model-value="authForm.authType" :disabled="props.disabled" class="mb-[16px]">
|
||||
<a-radio :value="RequestAuthType.NONE">No Auth</a-radio>
|
||||
<a-radio :value="RequestAuthType.BASIC">Basic Auth</a-radio>
|
||||
<a-radio :value="RequestAuthType.DIGEST">Digest Auth</a-radio>
|
||||
|
@ -10,6 +10,7 @@
|
|||
<a-form-item :label="t('apiTestDebug.username')">
|
||||
<a-input
|
||||
v-model:model-value="authForm.basicAuth.userName"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||
class="w-[450px]"
|
||||
:max-length="255"
|
||||
|
@ -18,6 +19,7 @@
|
|||
<a-form-item :label="t('apiTestDebug.password')">
|
||||
<a-input-password
|
||||
v-model:model-value="authForm.basicAuth.password"
|
||||
:disabled="props.disabled"
|
||||
autocomplete="new-password"
|
||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||
class="w-[450px]"
|
||||
|
@ -28,6 +30,7 @@
|
|||
<a-form-item :label="t('apiTestDebug.username')">
|
||||
<a-input
|
||||
v-model:model-value="authForm.digestAuth.userName"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||
class="w-[450px]"
|
||||
:max-length="255"
|
||||
|
@ -36,6 +39,7 @@
|
|||
<a-form-item :label="t('apiTestDebug.password')">
|
||||
<a-input-password
|
||||
v-model:model-value="authForm.digestAuth.password"
|
||||
:disabled="props.disabled"
|
||||
autocomplete="new-password"
|
||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||
class="w-[450px]"
|
||||
|
@ -56,6 +60,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
params: ExecuteAuthConfig;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', val: ExecuteAuthConfig): void;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
</a-radio-group>
|
||||
<batchAddKeyVal
|
||||
v-if="showParamTable"
|
||||
:disabled="props.disabled"
|
||||
:params="currentTableParams"
|
||||
:default-param-item="defaultBodyParamsItem"
|
||||
@apply="handleBatchParamApply"
|
||||
|
@ -25,6 +26,7 @@
|
|||
</div>
|
||||
<paramTable
|
||||
v-else-if="innerParams.bodyType === RequestBodyFormat.FORM_DATA"
|
||||
:disabled="props.disabled"
|
||||
:params="currentTableParams"
|
||||
:scroll="{ minWidth: 1160 }"
|
||||
:columns="columns"
|
||||
|
@ -40,6 +42,7 @@
|
|||
/>
|
||||
<paramTable
|
||||
v-else-if="innerParams.bodyType === RequestBodyFormat.WWW_FORM"
|
||||
:disabled="props.disabled"
|
||||
:params="currentTableParams"
|
||||
:scroll="{ minWidth: 1160 }"
|
||||
:columns="columns"
|
||||
|
@ -53,11 +56,13 @@
|
|||
<div class="mb-[16px] flex justify-between gap-[8px] bg-[var(--color-text-n9)] p-[12px]">
|
||||
<a-input
|
||||
v-model:model-value="innerParams.binaryBody.description"
|
||||
:disabled="props.disabled"
|
||||
:placeholder="t('common.desc')"
|
||||
:max-length="255"
|
||||
/>
|
||||
<MsAddAttachment
|
||||
v-model:file-list="fileList"
|
||||
:disabled="props.disabled"
|
||||
mode="input"
|
||||
:multiple="false"
|
||||
:fields="{
|
||||
|
@ -85,6 +90,7 @@
|
|||
<div v-else class="flex h-[calc(100%-34px)]">
|
||||
<MsCodeEditor
|
||||
v-model:model-value="currentBodyCode"
|
||||
:read-only="props.disabled"
|
||||
class="flex-1"
|
||||
theme="vs"
|
||||
height="100%"
|
||||
|
@ -121,6 +127,7 @@
|
|||
const props = defineProps<{
|
||||
params: ExecuteBody;
|
||||
layout: 'horizontal' | 'vertical';
|
||||
disabled?: boolean;
|
||||
secondBoxHeight: number;
|
||||
uploadTempFileApi?: (file: File) => Promise<any>; // 上传临时文件接口
|
||||
fileSaveAsSourceId?: string | number; // 文件转存关联的资源id
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<batchAddKeyVal
|
||||
:disabled="props.disabled"
|
||||
:params="innerParams"
|
||||
:default-param-item="defaultHeaderParamsItem"
|
||||
no-param-type
|
||||
|
@ -9,6 +10,7 @@
|
|||
</div>
|
||||
<paramTable
|
||||
v-model:params="innerParams"
|
||||
:disabled="props.disabled"
|
||||
:columns="columns"
|
||||
:height-used="heightUsed"
|
||||
:scroll="scroll"
|
||||
|
@ -35,6 +37,7 @@
|
|||
params: EnableKeyValueParam[];
|
||||
layout: 'horizontal' | 'vertical';
|
||||
secondBoxHeight: number;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:selectedKeys', value: string[]): void;
|
||||
|
|
|
@ -4,11 +4,17 @@
|
|||
:condition-types="conditionTypes"
|
||||
add-text="apiTestDebug.postCondition"
|
||||
:response="props.response"
|
||||
:disabled="props.disabled"
|
||||
:height-used="heightUsed"
|
||||
@change="emit('change')"
|
||||
>
|
||||
<template v-if="props.isDefinition" #titleRight>
|
||||
<a-switch v-model:model-value="innerConfig.enableGlobal" size="small" type="line"></a-switch>
|
||||
<a-switch
|
||||
v-model:model-value="innerConfig.enableGlobal"
|
||||
:disabled="props.disabled"
|
||||
size="small"
|
||||
type="line"
|
||||
></a-switch>
|
||||
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPostCondition') }}</div>
|
||||
<a-tooltip :content="t('apiTestDebug.openGlobalPostConditionTip')" position="left">
|
||||
<icon-question-circle
|
||||
|
@ -36,6 +42,7 @@
|
|||
layout: 'horizontal' | 'vertical';
|
||||
response?: string; // 响应内容
|
||||
isDefinition?: boolean; // 是否是定义页面
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', params: ExecuteConditionProcessor[]): void;
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
<template>
|
||||
<condition
|
||||
v-model:list="innerConfig.processors"
|
||||
:disabled="props.disabled"
|
||||
:condition-types="conditionTypes"
|
||||
add-text="apiTestDebug.precondition"
|
||||
@change="emit('change')"
|
||||
>
|
||||
<template v-if="props.isDefinition" #titleRight>
|
||||
<a-switch v-model:model-value="innerConfig.enableGlobal" size="small" type="line"></a-switch>
|
||||
<a-switch
|
||||
v-model:model-value="innerConfig.enableGlobal"
|
||||
:disabled="props.disabled"
|
||||
size="small"
|
||||
type="line"
|
||||
></a-switch>
|
||||
<div class="ml-[8px] text-[var(--color-text-1)]">{{ t('apiTestDebug.openGlobalPrecondition') }}</div>
|
||||
<a-tooltip :content="t('apiTestDebug.openGlobalPreconditionTip')" position="left">
|
||||
<icon-question-circle
|
||||
|
@ -31,6 +37,7 @@
|
|||
const props = defineProps<{
|
||||
config: ExecuteConditionConfig;
|
||||
isDefinition?: boolean; // 是否是定义页面
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:config', params: ExecuteConditionConfig): void;
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
</div>
|
||||
<batchAddKeyVal
|
||||
:params="innerParams"
|
||||
:disabled="props.disabled"
|
||||
:default-param-item="defaultRequestParamsItem"
|
||||
@apply="handleBatchParamApply"
|
||||
/>
|
||||
</div>
|
||||
<paramTable
|
||||
:params="innerParams"
|
||||
:disabled="props.disabled"
|
||||
:columns="columns"
|
||||
:height-used="heightUsed"
|
||||
:scroll="{ minWidth: 1160 }"
|
||||
|
@ -42,6 +44,7 @@
|
|||
const props = defineProps<{
|
||||
params: ExecuteRequestCommonParam[];
|
||||
layout: 'horizontal' | 'vertical';
|
||||
disabled?: boolean;
|
||||
secondBoxHeight: number;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="flex h-full min-w-[300px] flex-col">
|
||||
<div class="response flex h-full min-w-[300px] flex-col">
|
||||
<div :class="['response-head', props.isExpanded ? '' : 'border-t']">
|
||||
<div class="flex items-center justify-between">
|
||||
<template v-if="props.activeLayout === 'vertical'">
|
||||
|
|
|
@ -246,4 +246,7 @@
|
|||
:deep(.arco-tabs-tab) {
|
||||
@apply leading-none;
|
||||
}
|
||||
.no-content :deep(.arco-tabs-content) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
</div>
|
||||
<batchAddKeyVal
|
||||
:params="innerParams"
|
||||
:disabled="props.disabled"
|
||||
:default-param-item="defaultRequestParamsItem"
|
||||
@apply="handleBatchParamApply"
|
||||
/>
|
||||
|
@ -18,6 +19,7 @@
|
|||
<paramTable
|
||||
:params="innerParams"
|
||||
:columns="columns"
|
||||
:disabled="props.disabled"
|
||||
:height-used="heightUsed"
|
||||
:scroll="{ minWidth: 1160 }"
|
||||
:default-param-item="defaultRequestParamsItem"
|
||||
|
@ -43,6 +45,7 @@
|
|||
params: ExecuteRequestCommonParam[];
|
||||
layout: 'horizontal' | 'vertical';
|
||||
secondBoxHeight: number;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', value: any[]): void;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
</template>
|
||||
<a-input-number
|
||||
v-model:model-value="settingForm.connectTimeout"
|
||||
:disabled="props.disabled"
|
||||
mode="button"
|
||||
:step="100"
|
||||
:min="0"
|
||||
|
@ -26,6 +27,7 @@
|
|||
</template>
|
||||
<a-input-number
|
||||
v-model:model-value="settingForm.responseTimeout"
|
||||
:disabled="props.disabled"
|
||||
mode="button"
|
||||
:step="100"
|
||||
:min="0"
|
||||
|
@ -44,6 +46,7 @@
|
|||
<a-form-item :label="t('apiTestDebug.redirect')">
|
||||
<a-checkbox
|
||||
v-model:model-value="settingForm.followRedirects"
|
||||
:disabled="props.disabled"
|
||||
@change="(val) => handleFollowRedirectsChange(val as boolean)"
|
||||
>
|
||||
{{ t('apiTestDebug.follow') }}
|
||||
|
@ -51,6 +54,7 @@
|
|||
<a-checkbox
|
||||
v-model:model-value="settingForm.autoRedirects"
|
||||
class="ml-[24px]"
|
||||
:disabled="props.disabled"
|
||||
@change="val => handleAutoRedirectsChange(val as boolean)"
|
||||
>
|
||||
{{ t('apiTestDebug.auto') }}
|
||||
|
@ -69,6 +73,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
params: ExecuteOtherConfig;
|
||||
disabled?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', val: ExecuteOtherConfig): void;
|
||||
|
|
|
@ -8,18 +8,18 @@
|
|||
no-content-padding
|
||||
>
|
||||
<template #tbutton>
|
||||
<div class="flex items-center gap-[4px]">
|
||||
<div class="right-operation-button-icon flex items-center gap-[4px]">
|
||||
<MsButton
|
||||
v-permission="['PROJECT_API_DEFINITION_CASE:READ+UPDATE']"
|
||||
type="icon"
|
||||
status="secondary"
|
||||
@click="caseDerailRef?.editCase()"
|
||||
>
|
||||
<MsIcon type="icon-icon_edit_outlined" class="mr-[8px]" />
|
||||
<MsIcon type="icon-icon_edit_outlined" />
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
<MsButton type="icon" status="secondary" @click="caseDerailRef?.share()">
|
||||
<MsIcon type="icon-icon_share1" class="mr-[8px]" />
|
||||
<MsIcon type="icon-icon_share1" />
|
||||
{{ t('common.share') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
|
@ -30,7 +30,6 @@
|
|||
>
|
||||
<MsIcon
|
||||
:type="props.detail.follow ? 'icon-icon_collect_filled' : 'icon-icon_collection_outlined'"
|
||||
class="mr-[8px]"
|
||||
:class="[props.detail.follow ? 'text-[rgb(var(--warning-6))]' : '']"
|
||||
/>
|
||||
{{ t('common.fork') }}
|
||||
|
@ -38,7 +37,7 @@
|
|||
<MsButton type="icon" status="secondary">
|
||||
<a-dropdown position="br" @select="handleSelect">
|
||||
<div>
|
||||
<icon-more class="mr-[8px]" />
|
||||
<icon-more />
|
||||
<span> {{ t('common.more') }}</span>
|
||||
</div>
|
||||
<template #content>
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
<template>
|
||||
<MsDrawer
|
||||
v-model:visible="visible"
|
||||
unmount-on-close
|
||||
:mask="false"
|
||||
:width="900"
|
||||
:footer="false"
|
||||
:show-full-screen="!isShowEditStepNameInput"
|
||||
no-content-padding
|
||||
@close="handleClose"
|
||||
>
|
||||
<template #title>
|
||||
<stepType v-if="props.activeStep?.type" :type="props.activeStep?.type" class="mr-[4px]" />
|
||||
<a-input
|
||||
v-show="isShowEditStepNameInput"
|
||||
ref="stepNameInputRef"
|
||||
v-model:model-value="stepName"
|
||||
class="flex-1"
|
||||
:placeholder="t('apiScenario.pleaseInputStepName')"
|
||||
:max-length="255"
|
||||
show-word-limit
|
||||
@press-enter="updateStepName"
|
||||
@blur="updateStepName"
|
||||
/>
|
||||
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-tooltip :content="stepName">
|
||||
<span> {{ characterLimit(stepName) }}</span>
|
||||
</a-tooltip>
|
||||
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" />
|
||||
</div>
|
||||
<div class="right-operation-button-icon flex items-center">
|
||||
<MsButton v-permission="['PROJECT_API_DEFINITION_CASE:READ+UPDATE']" type="icon" status="secondary">
|
||||
<MsIcon type="icon-icon_swich" />
|
||||
{{ t('common.replace') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-permission="['PROJECT_API_DEFINITION_CASE:READ+UPDATE']"
|
||||
class="mr-4"
|
||||
type="icon"
|
||||
status="secondary"
|
||||
>
|
||||
<MsIcon type="icon-icon_delete-trash_outlined" />
|
||||
{{ t('common.delete') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex items-center p-[16px]">
|
||||
<a-input
|
||||
v-model:model-value="requestVModel.name"
|
||||
:placeholder="t('apiTestManagement.apiNamePlaceholder')"
|
||||
allow-clear
|
||||
:max-length="255"
|
||||
:show-word-limit="!isQuote"
|
||||
:disabled="isQuote"
|
||||
/>
|
||||
<executeButton
|
||||
ref="executeRef"
|
||||
class="ml-[16px]"
|
||||
is-emit
|
||||
:detail="requestVModel"
|
||||
@execute="handleExecute"
|
||||
@stop-debug="stopDebug"
|
||||
/>
|
||||
</div>
|
||||
<requestAndResponse
|
||||
ref="requestAndResponseRef"
|
||||
:detail-loading="loading"
|
||||
:disabled-except-param="isQuote"
|
||||
:default-params="defaultCaseParams"
|
||||
:request="requestVModel"
|
||||
:is-priority-local-exec="isPriorityLocalExec"
|
||||
:file-save-as-source-id="requestVModel.id"
|
||||
:file-module-options-api="getTransferOptionsCase"
|
||||
:file-save-as-api="transferFileCase"
|
||||
:upload-temp-file="uploadTempFileCase"
|
||||
is-show-common-content-tab-key
|
||||
@execute="handleExecute"
|
||||
/>
|
||||
</MsDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { InputInstance, Message } from '@arco-design/web-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import { ScenarioStepItem } from '../step/stepTree.vue';
|
||||
import stepType from './stepType.vue';
|
||||
import executeButton from '@/views/api-test/components/executeButton.vue';
|
||||
import requestAndResponse from '@/views/api-test/components/requestAndResponse.vue';
|
||||
import { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||
|
||||
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||
import {
|
||||
debugCase,
|
||||
getCaseDetail,
|
||||
getTransferOptionsCase,
|
||||
runCase,
|
||||
transferFileCase,
|
||||
uploadTempFileCase,
|
||||
} from '@/api/modules/api-test/management';
|
||||
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||
import { getLocalConfig } from '@/api/modules/user/index';
|
||||
import { characterLimit, getGenerateId } from '@/utils';
|
||||
|
||||
import { LocalConfig } from '@/models/user';
|
||||
import {
|
||||
RequestAuthType,
|
||||
RequestComposition,
|
||||
RequestMethods,
|
||||
ResponseComposition,
|
||||
ScenarioStepType,
|
||||
} from '@/enums/apiEnum';
|
||||
|
||||
import { defaultBodyParams, defaultResponse, defaultResponseItem } from '@/views/api-test/components/config';
|
||||
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
activeStep?: ScenarioStepItem;
|
||||
request?: RequestParam; // 请求参数集合
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'applyStep', request: RequestParam): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const visible = defineModel<boolean>('visible', { required: true });
|
||||
|
||||
const defaultCaseParams: RequestParam = {
|
||||
id: `case-${Date.now()}`,
|
||||
type: 'case',
|
||||
moduleId: 'root',
|
||||
protocol: 'HTTP',
|
||||
tags: [],
|
||||
description: '',
|
||||
priority: 'P0',
|
||||
url: '',
|
||||
activeTab: RequestComposition.HEADER,
|
||||
closable: true,
|
||||
method: RequestMethods.GET,
|
||||
headers: [],
|
||||
body: cloneDeep(defaultBodyParams),
|
||||
query: [],
|
||||
rest: [],
|
||||
polymorphicName: '',
|
||||
name: '',
|
||||
path: '',
|
||||
projectId: '',
|
||||
uploadFileIds: [],
|
||||
linkFileIds: [],
|
||||
authConfig: {
|
||||
authType: RequestAuthType.NONE,
|
||||
basicAuth: {
|
||||
userName: '',
|
||||
password: '',
|
||||
},
|
||||
digestAuth: {
|
||||
userName: '',
|
||||
password: '',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||
assertionConfig: {
|
||||
enableGlobal: false,
|
||||
assertions: [],
|
||||
},
|
||||
postProcessorConfig: {
|
||||
enableGlobal: false,
|
||||
processors: [],
|
||||
},
|
||||
preProcessorConfig: {
|
||||
enableGlobal: false,
|
||||
processors: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
otherConfig: {
|
||||
connectTimeout: 60000,
|
||||
responseTimeout: 60000,
|
||||
certificateAlias: '',
|
||||
followRedirects: true,
|
||||
autoRedirects: false,
|
||||
},
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
response: cloneDeep(defaultResponse),
|
||||
responseDefinition: [cloneDeep(defaultResponseItem)],
|
||||
isNew: true,
|
||||
unSaved: false,
|
||||
executeLoading: false,
|
||||
preDependency: [], // 前置依赖
|
||||
postDependency: [], // 后置依赖
|
||||
};
|
||||
|
||||
const isCopyNeedInit = computed(
|
||||
() => props.activeStep?.type === ScenarioStepType.COPY_CASE && props.request?.request === null
|
||||
);
|
||||
const isQuote = computed(() => props.activeStep?.type === ScenarioStepType.QUOTE_CASE);
|
||||
|
||||
const stepName = ref(props.activeStep?.name);
|
||||
watchEffect(() => {
|
||||
stepName.value = props.activeStep?.name;
|
||||
});
|
||||
const requestVModel = ref<RequestParam>(cloneDeep(defaultCaseParams));
|
||||
|
||||
const executeRef = ref<InstanceType<typeof executeButton>>();
|
||||
const requestAndResponseRef = ref<InstanceType<typeof requestAndResponse>>();
|
||||
const isPriorityLocalExec = computed(() => executeRef.value?.isPriorityLocalExec ?? false);
|
||||
|
||||
const apiLocalExec = ref<Record<string, any> | LocalConfig | undefined>({});
|
||||
async function initLocalConfig() {
|
||||
try {
|
||||
const res = await getLocalConfig();
|
||||
apiLocalExec.value = res.find((e) => e.type === 'API');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
provide('apiLocalExec', readonly(apiLocalExec));
|
||||
|
||||
const isShowEditStepNameInput = ref(false);
|
||||
const stepNameInputRef = ref<InputInstance>();
|
||||
function showEditScriptNameInput() {
|
||||
isShowEditStepNameInput.value = true;
|
||||
nextTick(() => {
|
||||
stepNameInputRef.value?.focus();
|
||||
});
|
||||
}
|
||||
function updateStepName() {
|
||||
// TODO: 更新步骤名称接口
|
||||
Message.success(t('common.updateSuccess'));
|
||||
isShowEditStepNameInput.value = false;
|
||||
}
|
||||
|
||||
const reportId = ref('');
|
||||
const websocket = ref<WebSocket>();
|
||||
const temporaryResponseMap = {}; // 缓存websocket返回的报告内容,避免执行接口后切换tab导致报告丢失
|
||||
// 开启websocket监听,接收执行结果
|
||||
function debugSocket(executeType?: 'localExec' | 'serverExec') {
|
||||
websocket.value = getSocket(
|
||||
reportId.value,
|
||||
executeType === 'localExec' ? '/ws/debug' : '',
|
||||
executeType === 'localExec' ? executeRef.value?.localExecuteUrl : ''
|
||||
);
|
||||
websocket.value.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.msgType === 'EXEC_RESULT') {
|
||||
if (requestVModel.value.reportId === data.reportId) {
|
||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||
requestVModel.value.response = data.taskResult; // 渲染出用例详情和创建用例抽屉的响应数据
|
||||
requestVModel.value.executeLoading = false;
|
||||
} else {
|
||||
// 不是则需要把报告缓存起来,等切换到对应的tab再赋值
|
||||
temporaryResponseMap[data.reportId] = data.taskResult;
|
||||
}
|
||||
} else if (data.msgType === 'EXEC_END') {
|
||||
// 执行结束,关闭websocket
|
||||
websocket.value?.close();
|
||||
requestVModel.value.executeLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
async function handleExecute(executeType?: 'localExec' | 'serverExec') {
|
||||
try {
|
||||
requestVModel.value.executeLoading = true;
|
||||
requestVModel.value.response = cloneDeep(defaultResponse);
|
||||
const makeRequestParams = requestAndResponseRef.value?.makeRequestParams(executeType); // 写在reportId之前,防止覆盖reportId
|
||||
reportId.value = getGenerateId();
|
||||
requestVModel.value.reportId = reportId.value; // 存储报告ID
|
||||
debugSocket(executeType); // 开启websocket
|
||||
let res;
|
||||
const params = {
|
||||
...makeRequestParams,
|
||||
reportId: reportId.value,
|
||||
};
|
||||
if (!(requestVModel.value.id as string).startsWith('c') && executeType === 'serverExec') {
|
||||
// 已创建的服务端
|
||||
res = await runCase(params);
|
||||
} else {
|
||||
res = await debugCase(params);
|
||||
}
|
||||
if (executeType === 'localExec') {
|
||||
await localExecuteApiDebug(executeRef.value?.localExecuteUrl ?? '', res);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
requestVModel.value.executeLoading = false;
|
||||
}
|
||||
}
|
||||
function stopDebug() {
|
||||
websocket.value?.close();
|
||||
requestVModel.value.executeLoading = false;
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
emit('applyStep', requestVModel.value);
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
async function initQuoteCaseDetail() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getCaseDetail(requestVModel.value.id as string);
|
||||
let parseRequestBodyResult;
|
||||
if (res.protocol === 'HTTP') {
|
||||
parseRequestBodyResult = parseRequestBodyFiles(res.request.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||
}
|
||||
requestVModel.value = {
|
||||
responseActiveTab: ResponseComposition.BODY,
|
||||
executeLoading: false,
|
||||
activeTab: res.protocol === 'HTTP' ? RequestComposition.HEADER : RequestComposition.PLUGIN,
|
||||
unSaved: false,
|
||||
isNew: false,
|
||||
label: res.name,
|
||||
...res.request,
|
||||
...res,
|
||||
response: cloneDeep(defaultResponse),
|
||||
url: res.path,
|
||||
name: res.name, // request里面还有个name但是是null
|
||||
id: res.id,
|
||||
...parseRequestBodyResult,
|
||||
};
|
||||
nextTick(() => {
|
||||
// 等待内容渲染出来再隐藏loading
|
||||
loading.value = false;
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
if (props.request) {
|
||||
requestVModel.value = { ...cloneDeep(defaultCaseParams), ...props.request };
|
||||
if (isQuote.value || isCopyNeedInit.value) {
|
||||
// 引用时,需要初始化引用的详情;复制只在第一次初始化的时候需要加载后台数据(request.request是复制请求时列表参数字段request会为 null,以此判断释放第一次初始化)
|
||||
initQuoteCaseDetail();
|
||||
}
|
||||
}
|
||||
await initLocalConfig();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
|
@ -195,6 +195,11 @@
|
|||
@add-step="addCustomApiStep"
|
||||
@apply-step="applyApiStep"
|
||||
/>
|
||||
<customCaseDrawer
|
||||
v-model:visible="customCaseDrawerVisible"
|
||||
:active-step="activeStep"
|
||||
:request="currentStepDetail"
|
||||
/>
|
||||
<importApiDrawer
|
||||
v-if="importApiDrawerVisible"
|
||||
v-model:visible="importApiDrawerVisible"
|
||||
|
@ -283,6 +288,7 @@
|
|||
// 非首屏渲染必要组件,异步加载
|
||||
const MsCodeEditor = defineAsyncComponent(() => import('@/components/pure/ms-code-editor/index.vue'));
|
||||
const customApiDrawer = defineAsyncComponent(() => import('../common/customApiDrawer.vue'));
|
||||
const customCaseDrawer = defineAsyncComponent(() => import('../common/customCaseDrawer.vue'));
|
||||
const importApiDrawer = defineAsyncComponent(() => import('../common/importApiDrawer/index.vue'));
|
||||
const scriptOperationDrawer = defineAsyncComponent(() => import('../common/scriptOperationDrawer.vue'));
|
||||
|
||||
|
@ -577,6 +583,7 @@
|
|||
}
|
||||
|
||||
const importApiDrawerVisible = ref(false);
|
||||
const customCaseDrawerVisible = ref(false);
|
||||
const customApiDrawerVisible = ref(false);
|
||||
const scriptOperationDrawerVisible = ref(false);
|
||||
const activeStep = ref<ScenarioStepItem>(); // 用于抽屉操作创建步骤时记录当前操作的步骤节点
|
||||
|
@ -604,6 +611,9 @@
|
|||
if ([ScenarioStepType.CUSTOM_API, ScenarioStepType.QUOTE_API, ScenarioStepType.COPY_API].includes(step.type)) {
|
||||
activeStep.value = step;
|
||||
customApiDrawerVisible.value = true;
|
||||
} else if ([ScenarioStepType.QUOTE_CASE, ScenarioStepType.COPY_CASE].includes(step.type)) {
|
||||
activeStep.value = step;
|
||||
customCaseDrawerVisible.value = true;
|
||||
} else if (step.type === ScenarioStepType.SCRIPT_OPERATION) {
|
||||
activeStep.value = step;
|
||||
scriptOperationDrawerVisible.value = true;
|
||||
|
|
Loading…
Reference in New Issue