feat(接口调试): 接口调试文件参数联调&问题修复

This commit is contained in:
baiqi 2024-02-21 18:43:10 +08:00 committed by Craftsman
parent d199f398ba
commit 716e5ead03
29 changed files with 780 additions and 278 deletions

View File

@ -4,6 +4,7 @@ import {
AddDebugModuleUrl,
DeleteDebugModuleUrl,
DeleteDebugUrl,
DragDebugUrl,
ExecuteApiDebugUrl,
GetApiDebugDetailUrl,
GetDebugModuleCountUrl,
@ -11,6 +12,7 @@ import {
MoveDebugModuleUrl,
UpdateApiDebugUrl,
UpdateDebugModuleUrl,
UploadTempFileUrl,
} from '@/api/requrls/api-test/debug';
import {
@ -21,7 +23,7 @@ import {
UpdateDebugModule,
UpdateDebugParams,
} from '@/models/apiTest/debug';
import { ModuleTreeNode, MoveModules } from '@/models/common';
import { DragSortParams, ModuleTreeNode, MoveModules } from '@/models/common';
// 获取模块树
export function getDebugModules() {
@ -53,6 +55,11 @@ export function getDebugModuleCount(data: { keyword: string }) {
return MSR.post({ url: GetDebugModuleCountUrl, data });
}
// 拖拽调试节点
export function dragDebug(data: DragSortParams) {
return MSR.post({ url: DragDebugUrl, data });
}
// 执行调试
export function executeDebug(data: ExecuteRequestParams) {
return MSR.post<ExecuteRequestParams>({ url: ExecuteApiDebugUrl, data });
@ -77,3 +84,8 @@ export function getDebugDetail(id: string) {
export function deleteDebug(id: string) {
return MSR.get({ url: DeleteDebugUrl, params: id });
}
// 上传文件
export function uploadTempFile(file: File) {
return MSR.uploadFile({ url: UploadTempFileUrl }, { fileList: [file] }, 'file');
}

View File

@ -9,3 +9,5 @@ export const GetDebugModuleCountUrl = '/api/debug/module/count'; // 模块统计
export const AddDebugModuleUrl = '/api/debug/module/add'; // 添加模块
export const GetDebugModulesUrl = '/api/debug/module/tree'; // 查询模块树
export const DeleteDebugModuleUrl = '/api/debug/module/delete'; // 删除模块
export const DragDebugUrl = '/api/debug/edit/pos'; // 拖拽调试节点
export const UploadTempFileUrl = '/api/debug/upload/temp/file'; // 上传文件

View File

@ -0,0 +1,64 @@
<template>
<a-dropdown position="tl" trigger="hover">
<a-button size="mini" type="outline">
<template #icon> <icon-upload class="text-[14px] !text-[rgb(var(--primary-5))]" /> </template>
</a-button>
<template #content>
<a-upload
ref="uploadRef"
v-model:file-list="innerFileList"
:auto-upload="false"
:show-file-list="false"
:before-upload="beforeUpload"
@change="handleChange"
>
<template #upload-button>
<a-button size="small" type="text" class="ms-add-attachment-dropdown-btn">
<icon-upload class="mr-[8px]" />{{ t('ms.add.attachment.localUpload') }}
</a-button>
</template>
</a-upload>
<a-button size="small" type="text" class="ms-add-attachment-dropdown-btn" @click="emit('linkFile')">
<MsIcon type="icon-icon_link-copy_outlined" class="mr-[8px]" size="16" />
{{ t('ms.add.attachment.associateFile') }}
</a-button>
</template>
</a-dropdown>
</template>
<script setup lang="ts">
import { MsFileItem } from '@/components/pure/ms-upload/types';
import { useI18n } from '@/hooks/useI18n';
const emit = defineEmits<{
(e: 'upload', file: File): void;
(e: 'change', _fileList: MsFileItem[], fileItem: MsFileItem): void;
(e: 'linkFile'): void;
}>();
const { t } = useI18n();
const innerFileList = defineModel<MsFileItem[]>('fileList', {
required: true,
});
function beforeUpload(file: File) {
emit('upload', file);
}
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
emit('change', _fileList, fileItem);
}
</script>
<style lang="less" scoped>
.ms-add-attachment-dropdown-btn {
padding-right: 8px;
padding-left: 8px;
color: var(--color-text-1) !important;
&:hover {
background: rgb(var(--primary-1)) !important;
}
}
</style>

View File

@ -1,12 +1,12 @@
<template>
<a-form-item field="attachment" :label="t('caseManagement.featureCase.addAttachment')">
<a-form-item v-if="props.mode === 'button'" field="attachment" :label="t('caseManagement.featureCase.addAttachment')">
<div class="flex flex-col">
<div class="mb-1">
<a-dropdown position="tr" trigger="hover">
<a-button type="outline">
<template #icon> <icon-plus class="text-[14px]" /> </template
>{{ t('system.orgTemplate.addAttachment') }}</a-button
>
<template #icon> <icon-plus class="text-[14px]" /> </template>
{{ t('system.orgTemplate.addAttachment') }}
</a-button>
<template #content>
<a-upload
ref="uploadRef"
@ -17,61 +17,212 @@
@change="handleChange"
>
<template #upload-button>
<a-button type="text" class="!text-[var(--color-text-1)]">
<icon-upload />{{ t('caseManagement.featureCase.uploadFile') }}</a-button
>
<a-button type="text" class="arco-dropdown-option !text-[var(--color-text-1)]">
<icon-upload />{{ t('caseManagement.featureCase.uploadFile') }}
</a-button>
</template>
</a-upload>
<a-button type="text" class="!text-[var(--color-text-1)]" @click="associatedFile">
<MsIcon type="icon-icon_link-copy_outlined" size="16" />{{
t('caseManagement.featureCase.associatedFile')
}}</a-button
>
<a-button type="text" class="arco-dropdown-option !text-[var(--color-text-1)]" @click="associatedFile">
<MsIcon type="icon-icon_link-copy_outlined" size="16" />
{{ t('caseManagement.featureCase.associatedFile') }}
</a-button>
</template>
</a-dropdown>
</div>
<div class="!hover:bg-[rgb(var(--primary-1))] !text-[var(--color-text-4)]">{{
t('system.orgTemplate.addAttachmentTip')
}}</div>
<div class="!hover:bg-[rgb(var(--primary-1))] !text-[var(--color-text-4)]">
{{ t('system.orgTemplate.addAttachmentTip') }}
</div>
</div>
</a-form-item>
<template v-else>
<div v-if="props.multiple" class="flex w-full items-center gap-[4px]">
<dropdownMenu v-model:file-list="innerFileList" @link-file="associatedFile" @change="handleChange" />
<MsTagsInput
v-model:model-value="inputFiles"
:class="props.inputClass"
placeholder=" "
:max-tag-count="1"
readonly
>
<template #tag="{ data }">
<MsTag :closable="!data.label.includes('+')" class="m-0 p-0" @close="() => handleClose(data)">
{{ data.label }}
</MsTag>
</template>
</MsTagsInput>
</div>
<div v-else class="flex w-full items-center gap-[4px]">
<dropdownMenu v-model:file-list="innerFileList" @link-file="associatedFile" @change="handleChange" />
<a-input
v-model:model-value="inputFileName"
:class="props.inputClass"
allow-clear
readonly
@clear="handleFileClear"
>
</a-input>
</div>
</template>
<LinkFileDrawer
v-model:visible="showDrawer"
:get-tree-request="getModules"
:get-count-request="getModulesCount"
:get-list-request="getAssociatedFileListUrl"
:get-list-fun-params="getListFunParams"
:selector-type="props.multiple ? 'checkbox' : 'radio'"
@save="saveSelectAssociatedFile"
/>
</template>
<script setup lang="ts">
import { useVModel } from '@vueuse/core';
import { TagData } from '@arco-design/web-vue';
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import type { MsFileItem } from '@/components/pure/ms-upload/types';
import LinkFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
import dropdownMenu from './dropdownMenu.vue';
import { getAssociatedFileListUrl } from '@/api/modules/case-management/featureCase';
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n';
import { AssociatedList } from '@/models/caseManagement/featureCase';
import { TableQueryParams } from '@/models/common';
import { convertToFile } from '@/views/case-management/caseManagementFeature/components/utils';
const props = withDefaults(
defineProps<{
mode: 'button' | 'input';
fileList: MsFileItem[];
multiple?: boolean;
inputClass?: string;
fields: {
id: string;
name: string;
};
}>(),
{
mode: 'button',
multiple: true,
fields: () => ({
id: 'uid',
name: 'name',
}),
}
);
const emit = defineEmits<{
(e: 'update:fileList', fileList: MsFileItem[]): void;
(e: 'upload', file: File): void;
(e: 'change', _fileList: MsFileItem[], fileItem?: MsFileItem): void;
(e: 'linkFile'): void;
(e: 'deleteFile', fileId?: string | number): void;
}>();
const { t } = useI18n();
const props = defineProps<{
fileList: MsFileItem[];
}>();
const emit = defineEmits<{
(e: 'upload', file: File): void;
(e: 'change', _fileList: MsFileItem[], fileItem: MsFileItem): void;
(e: 'linkFile'): void;
(e: 'update:fileList', fileList: MsFileItem[]): void;
}>();
// const innerFileList = ref<MsFileItem[]>([]);
const innerFileList = useVModel(props, 'fileList', emit);
const inputFileName = ref('');
const inputFiles = ref<TagData[]>([]);
const showDrawer = ref(false);
const getListFunParams = ref<TableQueryParams>({
combine: {
hiddenIds: [],
},
});
onBeforeMount(() => {
//
const defaultFiles = props.fileList.filter((item) => item) || [];
if (defaultFiles.length > 0) {
if (props.multiple) {
inputFiles.value = defaultFiles.map((item) => ({
//
value: item?.[props.fields.id] || '',
label: item?.[props.fields.name] || '',
}));
} else {
inputFileName.value = defaultFiles[0]?.[props.fields.name] || '';
}
getListFunParams.value.combine.hiddenIds = defaultFiles
.filter((item) => !item?.local)
.map((item) => item?.[props.fields.id] || '')
.filter((item) => item);
}
});
function beforeUpload(file: File) {
emit('upload', file);
}
function handleChange(_fileList: MsFileItem[], fileItem: MsFileItem) {
innerFileList.value = _fileList;
innerFileList.value = _fileList.map((item) => ({ ...item, local: true }));
if (props.multiple) {
inputFiles.value = _fileList.map((item) => ({
value: item?.uid || '',
label: item?.name || '',
}));
} else {
inputFileName.value = fileItem.name || '';
}
emit('change', _fileList, fileItem);
}
function associatedFile() {
emit('linkFile');
// TODO:
if (props.mode === 'button') {
emit('linkFile');
} else {
showDrawer.value = true;
}
}
//
watch(
() => innerFileList.value,
() => {
getListFunParams.value.combine.hiddenIds = innerFileList.value
.filter((item) => !item.local)
.map((item) => item[props.fields.id] || item.uid);
},
{ deep: true, immediate: true }
);
//
function saveSelectAssociatedFile(fileData: AssociatedList[]) {
const fileResultList = fileData.map((fileInfo) => convertToFile(fileInfo));
if (props.mode === 'button') {
innerFileList.value.push(...fileResultList);
} else if (props.multiple) {
innerFileList.value.push(...fileResultList);
inputFiles.value.push(
...fileResultList.map((item) => ({
value: item?.uid || '',
label: item?.name || '',
}))
);
} else {
//
innerFileList.value = fileResultList;
inputFileName.value = fileResultList[0].name || '';
}
emit('change', innerFileList.value);
}
function handleClose(data: TagData) {
inputFiles.value = inputFiles.value.filter((item) => item.value !== data.value);
innerFileList.value = innerFileList.value.filter((item) => item[props.fields.id] !== data.value);
emit('deleteFile', data.value);
}
function handleFileClear() {
inputFileName.value = '';
inputFiles.value = [];
innerFileList.value = [];
emit('change', []);
}
</script>
<style scoped></style>
<style lang="less" scoped></style>

View File

@ -0,0 +1,4 @@
export default {
'ms.add.attachment.localUpload': 'Local upload',
'ms.add.attachment.associateFile': 'Associate file',
};

View File

@ -0,0 +1,4 @@
export default {
'ms.add.attachment.localUpload': '本地上传',
'ms.add.attachment.associateFile': '关联文件',
};

View File

@ -52,7 +52,7 @@
</template>
<a-input
v-if="model.type === 'input'"
v-model="element[model.filed]"
v-model:model-value="element[model.filed]"
class="flex-1"
:placeholder="t(model.placeholder || '')"
:max-length="model.maxLength || 255"
@ -61,11 +61,12 @@
/>
<a-input-number
v-if="model.type === 'inputNumber'"
v-model="element[model.filed]"
v-model:model-value="element[model.filed]"
class="flex-1"
:placeholder="t(model.placeholder || '')"
:min="model.min"
:max="model.max || 9999999"
model-event="input"
allow-clear
@change="emit('change')"
/>

View File

@ -17,7 +17,7 @@
:placeholder="t('project.commonScript.pleaseSelected')"
@change="changeHandler"
>
<a-option v-for="item of languages" :key="item.value">
<a-option v-for="item of languages" :key="item.value" :value="item.value">
<a-tooltip :content="item.text">
{{ item.text }}
</a-tooltip>
@ -64,8 +64,8 @@
(e: 'update:languagesType', value: Language): void;
(e: 'insert', code: string): void;
(e: 'formApiImport'): void; // api
(e: 'insertCommonScript'): void; // api
(e: 'updateLanguages', value: Language): void; // api
(e: 'insertCommonScript'): void; //
(e: 'updateLanguages', value: Language): void; //
}>();
const innerExpand = useVModel(props, 'expand', emit);
@ -73,11 +73,11 @@
const innerLanguageType = useVModel(props, 'languagesType', emit);
const languages = [
{ text: 'beanshellJSR223', value: 'beanshell-jsr233' },
{ text: 'beanshell', value: 'beanshell' },
{ text: 'python', value: 'python' },
{ text: 'groovy', value: 'groovy' },
{ text: 'javascript', value: 'javascript' },
{ text: 'beanshellJSR223', value: RequestConditionScriptLanguage.BEANSHELL_JSR233 },
{ text: 'beanshell', value: RequestConditionScriptLanguage.BEANSHELL },
{ text: 'python', value: RequestConditionScriptLanguage.PYTHON },
{ text: 'groovy', value: RequestConditionScriptLanguage.GROOVY },
{ text: 'javascript', value: RequestConditionScriptLanguage.JAVASCRIPT },
];
function expandedHandler() {

View File

@ -14,9 +14,9 @@
{{ t('project.commonScript.clear') }}</MsTag
>
</div>
<MsTag class="cursor-pointer" theme="outline" @click="formatCoding">{{
t('project.commonScript.formatting')
}}</MsTag>
<MsTag class="cursor-pointer" theme="outline" @click="formatCoding">
{{ t('project.commonScript.formatting') }}
</MsTag>
</div>
</div>
<div v-if="props.showType === 'commonScript'" class="flex bg-[var(--color-bg-3)]">
@ -179,6 +179,13 @@ ${item.script}
function clearCode() {
innerCodeValue.value = '';
}
defineExpose({
formatCoding,
insertHandler,
undoHandler,
clearCode,
});
</script>
<style scoped lang="less"></style>

View File

@ -4,11 +4,12 @@
:width="props.width"
:footer="false"
class="ms-drawer"
:show-full-screen="props.showFullScreen"
no-content-padding
unmount-on-close
>
<template #title>
<div class="flex w-full items-center">
<div class="flex flex-1 items-center">
<a-tooltip :content="props.title" position="bottom">
<div class="one-line-text max-w-[300px]">
{{ props.title }}
@ -51,6 +52,7 @@
detailIndex: number; //
tableData: any[]; //
pagination: MsPaginationI; //
showFullScreen?: boolean; //
pageChange: (page: number) => Promise<void>; //
getDetailFunc: (id: string) => Promise<any>; //
}>();

View File

@ -1,7 +1,6 @@
<template>
<MsDrawer
v-model:visible="showDrawer"
:mask="false"
:title="t('caseManagement.featureCase.associatedFile')"
:ok-text="t('caseManagement.featureCase.associated')"
:ok-loading="drawerLoading"
@ -72,6 +71,7 @@
:show-type="showType"
:get-list-request="props.getListRequest"
:get-list-fun-params="props.getListFunParams"
:selector-type="props.selectorType"
@init="handleModuleTableInit"
/>
</template>
@ -103,6 +103,7 @@
getCountRequest: (params: any) => Promise<Record<string, any>>; //
getListRequest: (params: TableQueryParams) => Promise<CommonList<AssociatedList>>; //
getListFunParams: TableQueryParams; // id
selectorType?: 'none' | 'checkbox' | 'radio';
}>();
const emit = defineEmits<{

View File

@ -21,7 +21,7 @@
@press-enter="searchList"
/></div>
</div>
<ms-base-table v-bind="propsRes" ref="tableRef" no-disable v-on="propsEvent">
<ms-base-table v-bind="propsRes" ref="tableRef" v-model:selected-key="selectedKey" no-disable v-on="propsEvent">
<template #name="{ record }">
<MsTag
v-if="record.fileType.toLowerCase() === 'jar'"
@ -83,6 +83,7 @@
showType: 'Module' | 'Storage'; //
storageList: Repository[]; //
getListFunParams: TableQueryParams; //
selectorType?: 'none' | 'checkbox' | 'radio';
}>();
const emit = defineEmits<{
(e: 'init', params: FileListQueryParams): void;
@ -144,6 +145,7 @@
selectable: true,
showSelectAll: true,
heightUsed: 300,
selectorType: props.selectorType || 'checkbox',
},
(item) => {
return {
@ -278,8 +280,6 @@
};
});
const tableSelected = ref<AssociatedList[]>([]);
const selectedIds = computed(() => {
return [...propsRes.value.selectedKeys];
});
@ -287,8 +287,21 @@
watch(
() => selectedIds.value,
() => {
tableSelected.value = propsRes.value.data.filter((item: any) => selectedIds.value.indexOf(item.id) > -1);
emit('update:selectFile', tableSelected.value);
emit(
'update:selectFile',
propsRes.value.data.filter((item: any) => selectedIds.value.indexOf(item.id) > -1)
);
}
);
const selectedKey = ref('');
watch(
() => selectedKey.value,
(key) => {
emit(
'update:selectFile',
propsRes.value.data.filter((item: any) => key === item.id)
);
}
);

View File

@ -8,6 +8,9 @@
v-model:selected-keys="innerSelectedKeys"
:data="treeData"
class="ms-tree"
:allow-drop="handleAllowDrop"
@drag-start="onDragStart"
@drag-end="onDragEnd"
@drop="onDrop"
@select="select"
@check="checked"
@ -117,6 +120,7 @@
| 'right'
| 'rt'
| 'rb'; // tooltip
allowDrop?: (dropNode: MsTreeNodeData, dropPosition: -1 | 0 | 1, dragNode?: MsTreeNodeData | null) => boolean; //
filterMoreActionFunc?: (items: ActionsItem[], node: MsTreeNodeData) => ActionsItem[]; //
}>(),
{
@ -265,6 +269,23 @@
});
}
const tempDragNode = ref<MsTreeNodeData | null>(null);
function handleAllowDrop({ dropNode, dropPosition }: { dropNode: MsTreeNodeData; dropPosition: -1 | 0 | 1 }) {
if (props.allowDrop) {
return props.allowDrop(dropNode, dropPosition, tempDragNode.value);
}
return true;
}
function onDragStart(e, node: MsTreeNodeData) {
tempDragNode.value = node;
}
function onDragEnd() {
tempDragNode.value = null;
}
/**
* 处理拖拽结束
*/

View File

@ -9,6 +9,7 @@ export interface MsTreeFieldNames extends TreeFieldNames {
export type MsTreeNodeData = {
hideMoreAction?: boolean; // 隐藏更多操作
parentId?: string;
[key: string]: any;
} & TreeNodeData;

View File

@ -94,6 +94,9 @@
top: 12,
bottom: 12,
},
minimap: {
enabled: false, //
},
...props,
});

View File

@ -10,8 +10,10 @@
:unique-value="props.uniqueValue"
:max-tag-count="props.maxTagCount"
:readonly="props.readonly"
:class="props.class"
@press-enter="tagInputEnter"
@blur="tagInputBlur"
@clear="emit('clear')"
>
<template v-if="$slots.prefix" #prefix>
<slot name="prefix"></slot>
@ -47,15 +49,17 @@
maxTagCount?: number;
maxLength?: number;
readonly?: boolean;
class?: string;
}>(),
{
retainInputValue: true,
uniqueValue: true,
allowClear: true,
maxLength: 64,
class: '',
}
);
const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change']);
const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change', 'clear']);
const { t } = useI18n();

View File

@ -0,0 +1,7 @@
export default {};
export const dropPositionMap: Record<string, any> = {
'-1': 'BEFORE',
'0': 'APPEND',
'1': 'AFTER',
};

View File

@ -38,7 +38,7 @@ export interface ResponseTiming {
}
// key-value参数信息
export interface KeyValueParam {
id: string; // id用于前端渲染后台无此字段
id?: string; // id用于前端渲染后台无此字段
key: string;
value: string;
[key: string]: any; // 用于前端渲染时填充的自定义信息,后台无此字段
@ -63,6 +63,8 @@ export type ExecuteRequestFormBodyFormValue = ExecuteRequestCommonParam & {
files?: {
fileId: string;
fileName: string;
local: boolean; // 是否是本地上传的文件
[key: string]: any; // 用于前端渲染时填充的自定义信息,后台无此字段
}[];
contentType?: RequestContentTypeEnum & string;
};
@ -75,6 +77,8 @@ export interface ExecuteBinaryBody {
file?: {
fileId: string;
fileName: string;
local: boolean; // 是否是本地上传的文件
[key: string]: any; // 用于前端渲染时填充的自定义信息,后台无此字段
};
}
// 接口请求json-body参数集合信息
@ -296,7 +300,8 @@ export interface ExecuteRequestParams {
id?: string;
reportId: string;
environmentId: string;
tempFileIds: string[];
uploadFileIds: string[];
linkFileIds: string[];
request: ExecuteHTTPRequestFullParams | ExecutePluginRequestParams;
projectId: string;
}

View File

@ -65,3 +65,12 @@ export interface ModuleTreeNode {
parentId: string;
path: string;
}
// 拖拽排序
export type MoveMode = 'BEFORE' | 'AFTER' | 'APPEND';
export interface DragSortParams {
projectId: string;
targetId: string;
moveMode: MoveMode; // 拖拽类型
moveId: string;
moduleId?: string;
}

View File

@ -31,7 +31,7 @@
<MsIcon type="icon-icon_edit_outlined" class="edit-script-name-icon" @click="showEditScriptNameInput" />
</div>
</a-tooltip>
<a-popover class="h-auto" position="top">
<a-popover class="h-auto" position="right">
<div class="text-[rgb(var(--primary-5))]">{{ t('apiTestDebug.scriptEx') }}</div>
<template #content>
<div class="mb-[8px] flex items-center justify-between">
@ -64,7 +64,7 @@
</a-popover>
</div>
<div class="flex items-center gap-[8px]">
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini">
<a-button 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>
@ -87,6 +87,7 @@
<div class="h-[calc(100%-24px)] min-h-[300px]">
<MsScriptDefined
v-if="condition.script !== undefined && condition.scriptLanguage !== undefined"
ref="scriptDefinedRef"
v-model:code="condition.script"
v-model:language="condition.scriptLanguage"
show-type="commonScript"
@ -402,8 +403,14 @@ org.apache.http.client.method . . . '' at line number 2
}
}
const scriptDefinedRef = ref<InstanceType<typeof MsScriptDefined>>();
function undoScript() {
scriptDefinedRef.value?.undoHandler();
}
function clearScript() {
condition.value.enable = false;
condition.value.script = '';
}
/**

View File

@ -124,6 +124,18 @@
@input="(val) => addTableLine(val, 'value')"
/>
</a-popover>
<MsAddAttachment
v-else-if="record.paramType === RequestParamsType.FILE"
v-model:file-list="record.files"
mode="input"
:multiple="true"
:fields="{
id: 'fileId',
name: 'fileName',
}"
input-class="param-input"
@change="(files) => handleFileChange(files, record)"
/>
<MsParamsInput
v-else
v-model:value="record.value"
@ -141,7 +153,7 @@
class="param-input param-input-number"
@change="(val) => addTableLine(val, 'minLength')"
/>
<div class="mx-[4px]"></div>
<div class="mx-[4px]">{{ t('common.to') }}</div>
<a-input-number
v-model:model-value="record.maxLength"
:placeholder="t('apiTestDebug.paramMax')"
@ -317,7 +329,7 @@
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import MsTagsGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import MsParamsInput from '@/components/business/ms-params-input/index.vue';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import paramDescInput from './paramDescInput.vue';
import { useI18n } from '@/hooks/useI18n';
@ -325,6 +337,9 @@
import { RequestBodyFormat, RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
import { SelectAllEnum, TableKeyEnum } from '@/enums/tableEnum';
//
const MsAddAttachment = defineAsyncComponent(() => import('@/components/business/ms-add-attachment/index.vue'));
const MsParamsInput = defineAsyncComponent(() => import('@/components/business/ms-params-input/index.vue'));
export type ParamTableColumn = MsTableColumnData & {
isNormal?: boolean; // value MsParamsInput
@ -356,6 +371,7 @@
showSelectorAll?: boolean; //
isSimpleSetting?: boolean; // Column
response?: string; //
uploadTempFileApi?: (...args) => Promise<any>; //
}>(),
{
params: () => [],
@ -513,6 +529,34 @@
emit('change', propsRes.value.data);
}
async function handleFileChange(files: MsFileItem[], record: Record<string, any>) {
try {
if (props.uploadTempFileApi && files.length === 1) {
//
const fileItem = files[0];
const res = await props.uploadTempFileApi(fileItem.file);
record.files = [
{
...fileItem,
fileId: res.data,
fileName: fileItem.name || '',
local: true,
},
];
} else {
record.files = files.map((e) => ({
...e,
fileId: e.uid || e.fileId || '',
fileName: e.name || e.fileName || '',
}));
}
emit('change', propsRes.value.data);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
const showQuickInputParam = ref(false);
const activeQuickInputRecord = ref<any>({});
const quickInputParamValue = ref('');

View File

@ -32,6 +32,7 @@
:show-setting="true"
:table-key="TableKeyEnum.API_TEST_DEBUG_FORM_DATA"
:default-param-item="defaultParamItem"
:upload-temp-file-api="props.uploadTempFileApi"
@change="handleParamTableChange"
/>
<paramTable
@ -47,15 +48,21 @@
/>
<div v-else-if="innerParams.bodyType === RequestBodyFormat.BINARY">
<div class="mb-[16px] flex justify-between gap-[8px] bg-[var(--color-text-n9)] p-[12px]">
<!--TODO:文件上传&关联组件-->
<a-input
v-model:model-value="innerParams.binaryBody.description"
:placeholder="t('common.desc')"
:max-length="255"
/>
<MsAddAttachment
v-model:file-list="fileList"
mode="input"
:multiple="false"
:default-file-list="[innerParams.binaryBody.file]"
@change="handleFileChange"
/>
</div>
<div class="flex items-center">
<!-- <a-switch v-model:model-value="innerParams.binarySend" class="mr-[8px]" size="small" type="line"></a-switch> -->
<!-- <div class="flex items-center">
<a-switch v-model:model-value="innerParams.binarySend" class="mr-[8px]" size="small" type="line"></a-switch>
<span>{{ t('apiTestDebug.sendAsMainText') }}</span>
<a-tooltip position="right">
<template #content>
@ -67,7 +74,7 @@
size="16"
/>
</a-tooltip>
</div>
</div> -->
</div>
<div v-else class="flex h-[calc(100%-100px)]">
<MsCodeEditor
@ -97,13 +104,15 @@
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import MsAddAttachment from '@/components/business/ms-add-attachment/index.vue';
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
import paramTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import { requestBodyTypeMap } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n';
import { ExecuteBody } from '@/models/apiTest/debug';
import { ExecuteBody, ExecuteRequestFormBodyFormValue } from '@/models/apiTest/debug';
import { RequestBodyFormat, RequestContentTypeEnum, RequestParamsType } from '@/enums/apiEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -111,6 +120,7 @@
params: ExecuteBody;
layout: 'horizontal' | 'vertical';
secondBoxHeight: number;
uploadTempFileApi?: (...args) => Promise<any>; //
}>();
const emit = defineEmits<{
(e: 'update:params', value: any[]): void;
@ -120,7 +130,7 @@
const { t } = useI18n();
const innerParams = useVModel(props, 'params', emit);
const defaultParamItem = {
const defaultParamItem: ExecuteRequestFormBodyFormValue = {
key: '',
value: '',
paramType: RequestParamsType.STRING,
@ -131,58 +141,102 @@
encode: false,
enable: true,
contentType: RequestContentTypeEnum.TEXT,
files: [],
};
const fileList = ref<any[]>(
innerParams.value.binaryBody && innerParams.value.binaryBody.file ? [innerParams.value.binaryBody.file] : []
);
const columns = computed<ParamTableColumn[]>(() => [
{
title: 'apiTestDebug.paramName',
dataIndex: 'key',
slotName: 'key',
},
{
title: 'apiTestDebug.paramType',
dataIndex: 'paramType',
slotName: 'paramType',
hasRequired: true,
typeOptions: Object.values(RequestParamsType).map((val) => ({
label: val,
value: val,
})),
width: 120,
},
{
title: 'apiTestDebug.paramValue',
dataIndex: 'value',
slotName: 'value',
width: 240,
},
{
title: 'apiTestDebug.paramLengthRange',
dataIndex: 'lengthRange',
slotName: 'lengthRange',
align: 'center',
width: 200,
},
{
title: 'apiTestDebug.encode',
dataIndex: 'encode',
slotName: 'encode',
titleSlotName: 'encodeTitle',
width: 80,
},
{
title: 'apiTestDebug.desc',
dataIndex: 'description',
slotName: 'description',
},
{
title: '',
slotName: 'operation',
fixed: 'right',
format: innerParams.value.bodyType,
width: innerParams.value.bodyType === RequestBodyFormat.FORM_DATA ? 90 : 50,
},
]);
async function handleFileChange(files: MsFileItem[]) {
if (files.length === 0) {
innerParams.value.binaryBody.file = undefined;
return;
}
if (!props.uploadTempFileApi) return;
try {
if (fileList.value[0]?.local) {
const res = await props.uploadTempFileApi(fileList.value[0].file);
innerParams.value.binaryBody.file = {
...fileList.value[0],
fileId: res.data,
fileName: fileList.value[0]?.name || '',
local: true,
};
} else {
innerParams.value.binaryBody.file = {
...fileList.value[0],
fileId: fileList.value[0].uid,
fileName: fileList.value[0]?.name || '',
local: false,
};
}
emit('change');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
const typeOptions = computed(() => {
const fullOptions = Object.values(RequestParamsType).map((val) => ({
label: val,
value: val,
}));
if (innerParams.value.bodyType === RequestBodyFormat.FORM_DATA) {
return fullOptions;
}
return fullOptions.filter((item) => item.value !== RequestParamsType.FILE && item.value !== RequestParamsType.JSON);
});
const columns = computed<ParamTableColumn[]>(() => {
return [
{
title: 'apiTestDebug.paramName',
dataIndex: 'key',
slotName: 'key',
},
{
title: 'apiTestDebug.paramType',
dataIndex: 'paramType',
slotName: 'paramType',
hasRequired: true,
typeOptions: typeOptions.value,
width: 120,
},
{
title: 'apiTestDebug.paramValue',
dataIndex: 'value',
slotName: 'value',
multiple: true,
width: 240,
},
{
title: 'apiTestDebug.paramLengthRange',
dataIndex: 'lengthRange',
slotName: 'lengthRange',
align: 'center',
width: 200,
},
{
title: 'apiTestDebug.encode',
dataIndex: 'encode',
slotName: 'encode',
titleSlotName: 'encodeTitle',
width: 80,
},
{
title: 'apiTestDebug.desc',
dataIndex: 'description',
slotName: 'description',
},
{
title: '',
slotName: 'operation',
fixed: 'right',
format: innerParams.value.bodyType,
width: innerParams.value.bodyType === RequestBodyFormat.FORM_DATA ? 90 : 50,
},
];
});
const heightUsed = ref<number | undefined>(undefined);

View File

@ -4,13 +4,18 @@
<div class="mb-[8px] flex items-center justify-between">
<div class="flex flex-1">
<a-select
v-if="requestVModel.isNew"
v-model:model-value="requestVModel.protocol"
:options="protocolOptions"
:loading="protocolLoading"
:disabled="!requestVModel.isNew"
class="mr-[4px] w-[90px]"
@change="(val) => handleActiveDebugProtocolChange(val as string)"
/>
<apiMethodName
v-else
:method="(requestVModel.protocol as RequestMethods)"
class="mr-[16px] flex h-[30px] items-center"
/>
<a-input-group v-if="isHttpProtocol" class="flex-1">
<apiMethodSelect
v-model:model-value="requestVModel.method"
@ -50,7 +55,7 @@
<a-doption value="saveAsCase">{{ t('apiTestManagement.saveAsCase') }}</a-doption>
</template>
</a-dropdown>
<a-button v-else type="secondary" @click="handleSaveShortcut">
<a-button v-else type="secondary" :loading="saveLoading" @click="handleSaveShortcut">
<div class="flex items-center">
{{ t('common.save') }}
<div class="text-[var(--color-text-4)]">(<icon-command size="14" />+S)</div>
@ -115,6 +120,7 @@
v-model:params="requestVModel.body"
:layout="activeLayout"
:second-box-height="secondBoxHeight"
:upload-temp-file-api="props.uploadTempFileApi"
@change="handleActiveDebugChange"
/>
<debugQuery
@ -223,6 +229,7 @@
import precondition from './precondition.vue';
import response from './response.vue';
import debugSetting from './setting.vue';
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/management';
@ -263,6 +270,7 @@
executeApi: (...args) => Promise<any>; //
createApi: (...args) => Promise<any>; //
updateApi: (...args) => Promise<any>; //
uploadTempFileApi?: (...args) => Promise<any>; //
}>();
const emit = defineEmits(['addDone']);
@ -573,107 +581,6 @@
});
}
function makeRequestParams() {
const polymorphicName = protocolOptions.value.find(
(e) => e.value === requestVModel.value.protocol
)?.polymorphicName; //
let requestParams;
if (isHttpProtocol.value) {
requestParams = {
authConfig: requestVModel.value.authConfig,
body: {
...requestVModel.value.body,
binaryBody: undefined,
formDataBody: {
formValues: requestVModel.value.body.formDataBody.formValues.filter(
(e, i) => i !== requestVModel.value.body.formDataBody.formValues.length - 1
), //
},
wwwFormBody: {
formValues: requestVModel.value.body.wwwFormBody.formValues.filter(
(e, i) => i !== requestVModel.value.body.wwwFormBody.formValues.length - 1
), //
},
}, // TODO:binaryBody
headers: requestVModel.value.headers.filter((e, i) => i !== requestVModel.value.headers.length - 1), //
method: requestVModel.value.method,
otherConfig: requestVModel.value.otherConfig,
path: requestVModel.value.url,
query: requestVModel.value.query.filter((e, i) => i !== requestVModel.value.query.length - 1), //
rest: requestVModel.value.rest.filter((e, i) => i !== requestVModel.value.rest.length - 1), //
url: requestVModel.value.url,
polymorphicName,
};
} else {
requestParams = {
...fApi.value?.formData(),
polymorphicName,
};
}
reportId.value = getGenerateId();
requestVModel.value.reportId = reportId.value; // ID
debugSocket(); // websocket
return {
id: requestVModel.value.id.toString(),
reportId: reportId.value,
environmentId: '',
tempFileIds: [],
request: {
...requestParams,
children: [
{
polymorphicName: 'MsCommonElement', // MsCommonElement
assertionConfig: {
// TODO:
enableGlobal: false,
assertions: [],
},
postProcessorConfig: requestVModel.value.children[0].postProcessorConfig,
preProcessorConfig: requestVModel.value.children[0].preProcessorConfig,
},
],
},
projectId: appStore.currentProjectId,
};
}
/**
* 执行调试
* @param val 执行类型
*/
async function execute(execuetType?: 'localExec' | 'serverExec') {
// TODO:&
if (isHttpProtocol.value) {
try {
requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requestVModel.value.executeLoading = false;
}
} else {
//
fApi.value?.validate(async (valid) => {
if (valid === true) {
try {
requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requestVModel.value.executeLoading = false;
}
} else {
requestVModel.value.activeTab = RequestComposition.PLUGIN;
nextTick(() => {
scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
});
}
});
}
}
const saveModalVisible = ref(false);
const saveModalForm = ref({
name: '',
@ -698,7 +605,200 @@
}
);
function makeRequestParams() {
const { formDataBody, wwwFormBody, binaryBody } = requestVModel.value.body;
const polymorphicName = protocolOptions.value.find(
(e) => e.value === requestVModel.value.protocol
)?.polymorphicName; //
const realFormDataBodyValues = formDataBody.formValues.filter((e, i) => i !== formDataBody.formValues.length - 1); //
const realWwwFormBodyValues = wwwFormBody.formValues.filter((e, i) => i !== wwwFormBody.formValues.length - 1); //
const uploadFileIds: string[] = [];
const linkFileIds: string[] = [];
//
for (let i = 0; i < formDataBody.formValues.length; i++) {
const item = formDataBody.formValues[i];
if (item.paramType === RequestParamsType.FILE) {
if (item.files) {
for (let j = 0; j < item.files.length; j++) {
const file = item.files[j];
if (file.isLocal) {
uploadFileIds.push(file.fileId);
} else {
linkFileIds.push(file.fileId);
}
}
}
}
}
if (binaryBody) {
if (binaryBody.file?.isLocal) {
uploadFileIds.push(binaryBody.file.fileId);
} else if (binaryBody.file?.fileId) {
linkFileIds.push(binaryBody.file.fileId);
}
}
let requestParams;
if (isHttpProtocol.value) {
requestParams = {
authConfig: requestVModel.value.authConfig,
body: {
...requestVModel.value.body,
formDataBody: {
formValues: realFormDataBodyValues,
},
wwwFormBody: {
formValues: realWwwFormBodyValues,
},
}, // TODO:binaryBody
headers: requestVModel.value.headers.filter((e, i) => i !== requestVModel.value.headers.length - 1), //
method: requestVModel.value.method,
otherConfig: requestVModel.value.otherConfig,
path: requestVModel.value.url,
query: requestVModel.value.query.filter((e, i) => i !== requestVModel.value.query.length - 1), //
rest: requestVModel.value.rest.filter((e, i) => i !== requestVModel.value.rest.length - 1), //
url: requestVModel.value.url,
polymorphicName,
};
} else {
requestParams = {
...fApi.value?.formData(),
polymorphicName,
};
}
reportId.value = getGenerateId();
requestVModel.value.reportId = reportId.value; // ID
debugSocket(); // websocket
return {
id: requestVModel.value.id.toString(),
reportId: reportId.value,
environmentId: '',
name: saveModalForm.value.name || requestVModel.value.name,
request: {
...requestParams,
name: saveModalForm.value.name || requestVModel.value.name,
children: [
{
polymorphicName: 'MsCommonElement', // MsCommonElement
assertionConfig: {
// TODO:
enableGlobal: false,
assertions: [],
},
postProcessorConfig: requestVModel.value.children[0].postProcessorConfig,
preProcessorConfig: requestVModel.value.children[0].preProcessorConfig,
},
],
},
uploadFileIds,
linkFileIds,
projectId: appStore.currentProjectId,
};
}
/**
* 执行调试
* @param val 执行类型
*/
async function execute(execuetType?: 'localExec' | 'serverExec') {
// TODO:&
if (isHttpProtocol.value) {
try {
requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requestVModel.value.executeLoading = false;
} finally {
websocket.value?.close();
}
} else {
//
fApi.value?.validate(async (valid) => {
if (valid === true) {
try {
requestVModel.value.executeLoading = true;
await props.executeApi(makeRequestParams());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
requestVModel.value.executeLoading = false;
} finally {
websocket.value?.close();
}
} else {
requestVModel.value.activeTab = RequestComposition.PLUGIN;
nextTick(() => {
scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
});
}
});
}
}
async function updateDebug() {
try {
saveLoading.value = true;
await props.updateApi({
...makeRequestParams(),
...saveModalForm.value,
protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
deleteFileIds: [], // TODO:
unLinkRefIds: [], // TODO:
});
Message.success(t('common.updateSuccess'));
requestVModel.value.unSaved = false;
requestVModel.value.name = saveModalForm.value.name;
requestVModel.value.label = saveModalForm.value.name;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
saveLoading.value = false;
}
}
async function handleSave(done: (closed: boolean) => void) {
saveModalFormRef.value?.validate(async (errors) => {
if (!errors) {
try {
saveLoading.value = true;
if (requestVModel.value.isNew) {
//
const res = await props.createApi({
...makeRequestParams(),
...saveModalForm.value,
protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
});
requestVModel.value.id = res.id;
requestVModel.value.isNew = false;
Message.success(t('common.saveSuccess'));
requestVModel.value.unSaved = false;
requestVModel.value.name = saveModalForm.value.name;
requestVModel.value.label = saveModalForm.value.name;
} else {
updateDebug();
}
saveLoading.value = false;
saveModalVisible.value = false;
done(true);
emit('addDone');
} catch (error) {
saveLoading.value = false;
}
}
});
done(false);
}
async function handleSaveShortcut() {
if (!requestVModel.value.isNew) {
//
updateDebug();
return;
}
try {
if (!isHttpProtocol.value) {
//
@ -738,51 +838,6 @@
saveModalFormRef.value?.resetFields();
}
async function handleSave(done: (closed: boolean) => void) {
saveModalFormRef.value?.validate(async (errors) => {
if (!errors) {
try {
saveLoading.value = true;
if (requestVModel.value.isNew) {
//
const res = await props.createApi({
...makeRequestParams(),
...saveModalForm.value,
protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
uploadFileIds: [],
linkFileIds: [],
});
requestVModel.value.id = res.id;
requestVModel.value.isNew = false;
} else {
await props.updateApi({
...makeRequestParams(),
...saveModalForm.value,
protocol: requestVModel.value.protocol,
method: isHttpProtocol.value ? requestVModel.value.method : requestVModel.value.protocol,
uploadFileIds: [],
linkFileIds: [],
deleteFileIds: [], // TODO:
unLinkRefIds: [], // TODO:
});
}
saveLoading.value = false;
saveModalVisible.value = false;
done(true);
requestVModel.value.unSaved = false;
requestVModel.value.name = saveModalForm.value.name;
requestVModel.value.label = saveModalForm.value.name;
emit('addDone');
Message.success(requestVModel.value.isNew ? t('common.saveSuccess') : t('common.updateSuccess'));
} catch (error) {
saveLoading.value = false;
}
}
});
done(false);
}
onBeforeMount(() => {
initProtocolList();
initLocalConfig();

View File

@ -33,14 +33,14 @@
/>
</a-form-item>
</div>
<a-form-item :label="t('apiTestDebug.certificateAlias')">
<!-- <a-form-item :label="t('apiTestDebug.certificateAlias')">
<a-input
v-model:model-value="settingForm.certificateAlias"
:max-length="255"
:placeholder="t('apiTestDebug.commonPlaceholder')"
class="w-[450px]"
/>
</a-form-item>
</a-form-item> -->
<a-form-item :label="t('apiTestDebug.redirect')">
<a-radio
v-model:model-value="settingForm.followRedirects"

View File

@ -55,9 +55,11 @@
children: 'children',
count: 'count',
}"
:draggable="true"
:selectable="false"
block-node
title-tooltip-position="left"
:allow-drop="allowDrop"
@more-action-select="handleFolderMoreSelect"
@more-actions-close="moreActionsClose"
@drop="handleDrop"
@ -124,12 +126,15 @@
import {
deleteDebug,
deleteDebugModule,
dragDebug,
getDebugModuleCount,
getDebugModules,
moveDebugModule,
} from '@/api/modules/api-test/debug';
import { dropPositionMap } from '@/config/common';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useAppStore from '@/store/modules/app';
import { mapTree } from '@/utils';
import { ModuleTreeNode } from '@/models/common';
@ -139,6 +144,7 @@
}>();
const emit = defineEmits(['init', 'clickApiNode', 'newApi', 'import', 'renameFinish']);
const appStore = useAppStore();
const { t } = useI18n();
const { openModal } = useModal();
@ -204,6 +210,7 @@
return {
...e,
hideMoreAction: e.id === 'root',
draggable: e.id !== 'root',
};
});
rootModulesName.value = folderTree.value.map((e) => e.name || '');
@ -324,6 +331,18 @@
}
}
function allowDrop(dropNode: MsTreeNodeData, dropPosition: number, dragNode?: MsTreeNodeData | null) {
if (dropNode.type === 'API' && dropPosition === 0) {
// API
return false;
}
if (dropNode.type === 'MODULE' && dragNode?.type === 'API' && dropPosition !== 0) {
// API
return false;
}
return true;
}
/**
* 处理文件夹树节点拖拽事件
* @param tree 树数据
@ -339,11 +358,21 @@
) {
try {
loading.value = true;
await moveDebugModule({
dragNodeId: dragNode.id as string,
dropNodeId: dropNode.id || '',
dropPosition,
});
if (dragNode.type === 'MODULE') {
await moveDebugModule({
dragNodeId: dragNode.id as string,
dropNodeId: dropNode.id || '',
dropPosition,
});
} else {
await dragDebug({
projectId: appStore.currentProjectId,
moveMode: dropPositionMap[dropPosition],
moveId: dropNode.id,
targetId: dragNode.id,
moduleId: dropNode.type === 'API' ? dropNode.parentId : dropNode.id, // APIidid
});
}
Message.success(t('apiTestDebug.moduleMoveSuccess'));
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -36,6 +36,7 @@
:create-api="addDebug"
:update-api="updateDebug"
:execute-api="executeDebug"
:upload-temp-file-api="uploadTempFile"
@add-done="handleDebugAddDone"
/>
</div>
@ -88,7 +89,7 @@
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import debug, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
import { addDebug, executeDebug, getDebugDetail, updateDebug } from '@/api/modules/api-test/debug';
import { addDebug, executeDebug, getDebugDetail, updateDebug, uploadTempFile } from '@/api/modules/api-test/debug';
import { useI18n } from '@/hooks/useI18n';
import { parseCurlScript } from '@/utils';

View File

@ -100,7 +100,7 @@ export default {
'apiTestDebug.deleteDebugTipContent': 'Deletion cannot be restored, please proceed with caution!',
'apiTestDebug.deleteConfirm': 'Confirm delete',
'apiTestDebug.deleteSuccess': 'Successfully deleted',
'apiTestDebug.moduleMoveSuccess': 'Module moved successfully',
'apiTestDebug.moduleMoveSuccess': 'Moved successfully',
'apiTestDebug.sqlSourceName': 'Data source name',
'apiTestDebug.driver': 'Drive',
'apiTestDebug.username': 'Username',

View File

@ -94,7 +94,7 @@ export default {
'apiTestDebug.deleteDebugTipContent': '删除后无法恢复,请谨慎操作!',
'apiTestDebug.deleteConfirm': '确认删除',
'apiTestDebug.deleteSuccess': '删除成功',
'apiTestDebug.moduleMoveSuccess': '模块移动成功',
'apiTestDebug.moduleMoveSuccess': '移动成功',
'apiTestDebug.sqlSourceName': '数据源名称',
'apiTestDebug.driver': '驱动',
'apiTestDebug.username': '用户名',

View File

@ -79,7 +79,8 @@
:show-fill-icon="false"
/>
</a-form-item>
<template v-if="isCheckedPerformance">
<!--TODO:暂无性能测试-->
<!-- <template v-if="isCheckedPerformance">
<a-form-item :label="t('system.resourcePool.mirror')" field="testResourceDTO.loadTestImage" class="form-item">
<a-input
v-model:model-value="form.testResourceDTO.loadTestImage"
@ -98,9 +99,9 @@
@fill="fillHeapByDefault"
/>
</a-form-item>
</template>
<template v-if="isCheckedUI">
</template> -->
<!--TODO:暂无UI测试-->
<!-- <template v-if="isCheckedUI">
<a-form-item
:label="t('system.resourcePool.uiGrid')"
field="testResourceDTO.uiGrid"
@ -131,7 +132,7 @@
class="w-[160px]"
></a-input-number>
</a-form-item>
</template>
</template> -->
<a-form-item v-if="isShowTypeItem" :label="t('system.resourcePool.type')" field="type" class="form-item">
<a-radio-group v-model:model-value="form.type" type="button" @change="changeResourceType">