feat(接口调试): 联调98%

This commit is contained in:
baiqi 2024-02-22 21:17:37 +08:00 committed by 刘瑞斌
parent f777cb034e
commit d3442f9de4
15 changed files with 171 additions and 47 deletions

View File

@ -10,6 +10,7 @@ import {
GetDebugModuleCountUrl,
GetDebugModulesUrl,
MoveDebugModuleUrl,
TestMockUrl,
UpdateApiDebugUrl,
UpdateDebugModuleUrl,
UploadTempFileUrl,
@ -85,6 +86,11 @@ export function deleteDebug(id: string) {
return MSR.get({ url: DeleteDebugUrl, params: id });
}
// 测试mock
export function testMock(key: string) {
return MSR.get({ url: TestMockUrl, params: key });
}
// 上传文件
export function uploadTempFile(file: File) {
return MSR.uploadFile({ url: UploadTempFileUrl }, { fileList: [file] }, 'file');

View File

@ -3,6 +3,7 @@ export const AddApiDebugUrl = '/api/debug/add'; // 新增调试
export const UpdateApiDebugUrl = '/api/debug/update'; // 更新调试
export const GetApiDebugDetailUrl = '/api/debug/get'; // 获取接口调试详情
export const DeleteDebugUrl = '/api/debug/delete'; // 删除调试
export const TestMockUrl = '/api/test/mock'; // 测试mock
export const UpdateDebugModuleUrl = '/api/debug/module/update'; // 更新模块
export const MoveDebugModuleUrl = '/api/debug/module/move'; // 移动模块
export const GetDebugModuleCountUrl = '/api/debug/module/count'; // 模块统计数量

View File

@ -37,25 +37,67 @@
<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"
:input-class="props.inputClass"
placeholder=" "
:max-tag-count="1"
:size="props.inputSize"
readonly
class="!w-[calc(100%-28px)]"
<a-popover
v-model:popup-visible="inputFilesPopoverVisible"
trigger="click"
position="bottom"
:disabled="inputFiles.length === 0"
>
<template #tag="{ data }">
<MsTag
:size="props.tagSize"
class="m-0 border-none p-0"
:self-style="{ backgroundColor: 'transparent !important' }"
>
{{ data.label }}
</MsTag>
<MsTagsInput
v-model:model-value="inputFiles"
:input-class="props.inputClass"
placeholder=" "
:max-tag-count="1"
:size="props.inputSize"
readonly
class="!w-[calc(100%-28px)]"
>
<template v-if="alreadyDeleteFiles.length > 0" #prefix>
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />
</template>
<template #tag="{ data }">
<MsTag
:size="props.tagSize"
class="m-0 border-none p-0"
:self-style="{ backgroundColor: 'transparent !important' }"
:closable="data.value !== '__arco__more'"
@close="handleClose(data)"
>
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
</MsTag>
</template>
</MsTagsInput>
<template #content>
<div class="flex w-[200px] flex-col gap-[8px]">
<template v-if="alreadyDeleteFiles.length > 0">
<div class="flex items-center gap-[4px]">
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />
<div class="text-[var(--color-text-4)]">{{ t('ms.add.attachment.alreadyDelete') }}</div>
<MsButton type="text" @click="clearDeletedFiles">{{ t('ms.add.attachment.quickClear') }}</MsButton>
</div>
<div class="file-list">
<div v-for="file of alreadyDeleteFiles" :key="file.value">
<MsTag size="small" max-width="100%" closable @close="handleClose(file)">
{{ file.label }}
</MsTag>
</div>
</div>
</template>
<template v-if="otherFiles.length > 0">
<div v-if="alreadyDeleteFiles.length > 0" class="mt-[4px] text-[var(--color-text-4)]">
{{ t('ms.add.attachment.other') }}
</div>
<div class="file-list">
<div v-for="file of otherFiles" :key="file.value">
<MsTag size="small" max-width="100%" closable @close="handleClose(file)">
{{ file.label }}
</MsTag>
</div>
</div>
</template>
</div>
</template>
</MsTagsInput>
</a-popover>
</div>
<div v-else class="flex w-full items-center gap-[4px]">
<dropdownMenu v-model:file-list="innerFileList" @link-file="associatedFile" @change="handleChange" />
@ -85,6 +127,7 @@
import { useVModel } from '@vueuse/core';
import { TagData } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsTag, { Size } 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';
@ -148,6 +191,7 @@
if (defaultFiles.length > 0) {
if (props.multiple) {
inputFiles.value = defaultFiles.map((item) => ({
...item,
//
value: item?.[props.fields.id] || '',
label: item?.[props.fields.name] || '',
@ -220,9 +264,24 @@
emit('change', innerFileList.value);
}
const inputFilesPopoverVisible = ref(false);
const alreadyDeleteFiles = computed(() => {
return inputFiles.value.filter((item) => item.delete);
});
const otherFiles = computed(() => {
return inputFiles.value.filter((item) => !item.delete);
});
function clearDeletedFiles() {
inputFiles.value = inputFiles.value.filter((item) => !item.delete);
}
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);
if (innerFileList.value.length === 0) {
inputFilesPopoverVisible.value = false;
}
emit('deleteFile', data.value);
}
@ -235,6 +294,19 @@
</script>
<style lang="less" scoped>
.file-list {
@apply flex flex-col overflow-y-auto overflow-x-hidden;
.ms-scroll-bar();
gap: 8px;
max-height: 100px;
}
:deep(.arco-input-tag-has-prefix) {
padding-left: 4px;
}
:deep(.arco-input-tag-prefix) {
padding-right: 4px;
}
:deep(.arco-input-tag-inner) {
@apply flex w-full items-center;
.arco-input-tag-tag {

View File

@ -1,4 +1,7 @@
export default {
'ms.add.attachment.localUpload': 'Local upload',
'ms.add.attachment.associateFile': 'Associate file',
'ms.add.attachment.alreadyDelete': 'Deleted files',
'ms.add.attachment.other': 'Other files',
'ms.add.attachment.quickClear': 'Clear',
};

View File

@ -1,4 +1,7 @@
export default {
'ms.add.attachment.localUpload': '本地上传',
'ms.add.attachment.associateFile': '关联文件',
'ms.add.attachment.alreadyDelete': '已被删除文件',
'ms.add.attachment.other': '其他文件',
'ms.add.attachment.quickClear': '一键移除',
};

View File

@ -75,6 +75,7 @@
import { FormInstance } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
@ -84,7 +85,7 @@
import { getCommonScriptDetail, getSocket, testCommonScript } from '@/api/modules/project-management/commonScript';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { getGenerateId, sleep } from '@/utils';
import { getGenerateId } from '@/utils';
import type { AddOrUpdateCommonScript, ParamsRequestType } from '@/models/projectManagement/commonScript';
@ -127,7 +128,7 @@
projectId: '',
params: '',
script: '',
type: 'beanshell-jsr233',
type: LanguageEnum.BEANSHELL_JSR233,
result: '',
};

View File

@ -148,9 +148,10 @@
<div
:class="`flex ${data.isLeaf ? 'mr-[-26px] w-[270px]' : 'w-[120px]'} items-center justify-between`"
>
<a-tooltip :content="t(data.label)">
<!-- {symbol: '|'}是为了解决vue-i18n中是特殊功能符号会被转换掉 -->
<a-tooltip :content="t(data.label, { symbol: '|' })">
<div :class="`one-line-text ${data.isLeaf ? 'max-w-[50%]' : ''}`" title="">
{{ t(data.label) }}
{{ t(data.label, { symbol: '|' }) }}
</div>
</a-tooltip>
<a-tooltip v-if="data.isLeaf" :content="data.value">
@ -164,9 +165,15 @@
</a-form-item>
</template>
</a-form>
<div class="mb-[16px] flex items-center gap-[16px] bg-[var(--color-text-n9)] p-[5px_8px]">
<div
v-if="paramForm.type === 'mock'"
class="mb-[16px] flex items-center gap-[16px] bg-[var(--color-text-n9)] p-[5px_8px]"
>
<div class="text-[var(--color-text-3)]">{{ t('ms.paramsInput.preview') }}</div>
<div class="text-[var(--color-text-1)]">{{ paramPreview }}</div>
<a-spin :loading="previewLoading" class="flex gap-[8px]">
<div class="text-[var(--color-text-1)]">{{ paramPreview }}</div>
<MsButton type="text" @click="getMockValue">{{ t('ms.paramsInput.previewClick') }}</MsButton>
</a-spin>
</div>
</div>
<div class="flex items-center justify-end gap-[8px]">
@ -192,12 +199,14 @@
<div class="ms-params-popover-value mb-[8px]">
{{ innerValue }}
</div>
<div class="ms-params-popover-subtitle">
{{ t('ms.paramsInput.preview') }}
</div>
<div class="ms-params-popover-value">
{{ innerValue }}
</div>
<template v-if="/^@/.test(innerValue)">
<div class="ms-params-popover-subtitle">
{{ t('ms.paramsInput.preview') }}
</div>
<div class="ms-params-popover-value">
{{ paramPreview }}
</div>
</template>
</template>
<a-auto-complete
ref="autoCompleteRef"
@ -230,12 +239,14 @@
<script setup lang="ts">
import { useEventListener, useStorage, useVModel } from '@vueuse/core';
import { cloneDeep, includes } from 'lodash-es';
import { cloneDeep } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsCascader from '@/components/business/ms-cascader/index.vue';
import paramsInputGroup from './paramsInputGroup.vue';
import { testMock } from '@/api/modules/api-test/debug';
import { useI18n } from '@/hooks/useI18n';
import {
@ -322,11 +333,6 @@
}
}
function selectAutoComplete(val: string) {
innerValue.value = val;
setLastTenParams(val);
}
const autoCompleteRef = ref<InstanceType<typeof AutoComplete>>();
onMounted(() => {
@ -365,7 +371,6 @@
const paramFormRef = ref<FormInstance>();
const paramTypeOptions: CascaderOption[] = cloneDeep(mockAllGroup);
const paramFuncOptions: MockParamItem[] = cloneDeep(mockFunctions);
const paramPreview = ref('xsxsxsxs');
const currentParamsInputGroup = ref<MockParamInputGroupItem[]>([]);
/**
@ -541,6 +546,35 @@
return resultStr;
}
const paramPreview = ref('');
const previewLoading = ref(false);
async function getMockValue(val?: string) {
try {
previewLoading.value = true;
paramPreview.value = await testMock(val || applyMock());
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
paramPreview.value = '';
} finally {
previewLoading.value = false;
}
}
watch(
() => popoverVisible.value,
(val) => {
if (val && /^@/.test(innerValue.value)) {
getMockValue(innerValue.value);
}
}
);
function selectAutoComplete(val: string) {
innerValue.value = val;
setLastTenParams(val);
}
function apply() {
paramFormRef.value?.validate((errors) => {
if (!errors) {

View File

@ -2,6 +2,7 @@ export default {
'ms.paramsInput.value': 'Parameter value',
'ms.paramsInput.placeholder': 'Starting with {at}, double-click to quickly enter',
'ms.paramsInput.preview': 'Parameter preview',
'ms.paramsInput.previewClick': 'Click to get',
'ms.paramsInput.natural': 'Natural number',
'ms.paramsInput.naturalDesc': 'Returns a random natural number',
'ms.paramsInput.naturalRange': 'Natural numbers from 1-100',
@ -162,7 +163,7 @@ export default {
'ms.paramsInput.base': 'Basic variables',
'ms.paramsInput.apply': 'Apply',
'ms.paramsInput.variable': 'Variable',
'ms.paramsInput.randomFromMultipleVars': 'Extract elements from a set of values of variables separated by |',
'ms.paramsInput.randomFromMultipleVars': 'Extract elements from a set of values of variables separated by {symbol}',
'ms.paramsInput.split': 'Split string into variables',
'ms.paramsInput.eval': 'Compute variable expression',
'ms.paramsInput.evalVar': 'Evaluate an expression stored in a variable',

View File

@ -2,6 +2,7 @@ export default {
'ms.paramsInput.value': '参数值',
'ms.paramsInput.placeholder': '以{at}开始,双击可快速输入',
'ms.paramsInput.preview': '参数预览',
'ms.paramsInput.previewClick': '点击获取',
'ms.paramsInput.natural': '自然数',
'ms.paramsInput.naturalDesc': '返回一个随机的自然数',
'ms.paramsInput.naturalRange': '1-100自然数',
@ -154,7 +155,7 @@ export default {
'ms.paramsInput.base': '基础变量',
'ms.paramsInput.apply': '应用',
'ms.paramsInput.variable': '变量',
'ms.paramsInput.randomFromMultipleVars': '从由|分隔的一组变量的值中提取元素',
'ms.paramsInput.randomFromMultipleVars': '从由{symbol}分隔的一组变量的值中提取元素',
'ms.paramsInput.split': '将字符串拆分为变量',
'ms.paramsInput.eval': '计算变量表达式',
'ms.paramsInput.evalVar': '计算存储在变量中的表达式',

View File

@ -20,7 +20,7 @@ export const LanguageEnum = {
YAML: 'yaml' as const,
SHELL: 'shell' as const,
BEANSHELL: 'beanshell' as const,
BEANSHELLJSR233: 'beanshell-jsr233' as const,
BEANSHELL_JSR233: 'beanshell-jsr233' as const,
GROOVY: 'groovy' as const,
NASHORNSCRIPT: 'nashornScript' as const,
RHINOSCRIPT: 'rhinoScript' as const,

View File

@ -1,5 +1,5 @@
<template>
<div :class="`w-full ${props.class}`">
<div :class="`flex w-full items-center ${props.class}`">
<a-input-tag
v-model:model-value="innerModelValue"
v-model:input-value="innerInputValue"

View File

@ -99,6 +99,7 @@ export interface AssociatedList {
tags: any;
description: string;
moduleName: string; // 模块名称
originalName: string; // 文件原始名称
moduleId: string;
createUser: string;
createTime: number | string;

View File

@ -166,6 +166,7 @@
...fileList.value[0],
fileId: res.data,
fileName: fileList.value[0]?.name || '',
fileAlias: fileList.value[0]?.name || '',
local: true,
};
appStore.hideLoading();
@ -173,7 +174,8 @@
innerParams.value.binaryBody.file = {
...fileList.value[0],
fileId: fileList.value[0].uid,
fileName: fileList.value[0]?.name || '',
fileName: fileList.value[0]?.originalName || '',
fileAlias: fileList.value[0]?.name || '',
local: false,
};
}

View File

@ -645,8 +645,8 @@
const realWwwFormBodyValues = wwwFormBody.formValues.filter((e, i) => i !== wwwFormBody.formValues.length - 1); //
parseRequestBodyResult = parseRequestBodyFiles(
requestVModel.value.body,
requestVModel.value.uploadFileIds,
requestVModel.value.linkFileIds
requestVModel.value.uploadFileIds, //
requestVModel.value.linkFileIds //
);
requestParams = {
authConfig: requestVModel.value.authConfig,
@ -658,7 +658,7 @@
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,
@ -748,8 +748,6 @@
...makeRequestParams(),
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;

View File

@ -101,11 +101,12 @@ export function convertToFile(fileInfo: AssociatedList): MsFileItem {
enable: fileInfo.enable || false,
file,
name: fileName,
originalName: fileInfo.originalName,
percent: 0,
status: 'done',
uid: id,
url: `${gatewayAddress}/${fileInfo.filePath || ''}`,
local,
local: !!local,
deleteContent: local ? '' : 'caseManagement.featureCase.cancelLink',
isUpdateFlag,
associateId,