feat(接口管理): 插件增删改查接口联调
This commit is contained in:
parent
33fda25e5f
commit
e0d1d6f3ed
|
@ -170,7 +170,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { Message, TagData } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
|
@ -281,9 +280,10 @@
|
|||
label: fileItem[props.fields.name] || fileItem.name || '',
|
||||
});
|
||||
} else {
|
||||
innerFileList.value = [fileItem];
|
||||
inputFileName.value = fileItem.name || '';
|
||||
}
|
||||
emit('change', _fileList, { ...fileItem, local: true });
|
||||
emit('change', innerFileList.value, { ...fileItem, local: true });
|
||||
nextTick(() => {
|
||||
// 在 emit 文件上去之后再关闭菜单
|
||||
buttonDropDownVisible.value = false;
|
||||
|
@ -302,7 +302,7 @@
|
|||
// 监视文件列表处理关联和本地文件
|
||||
watch(
|
||||
() => innerFileList.value,
|
||||
() => {
|
||||
(arr) => {
|
||||
getListFunParams.value.combine.hiddenIds = innerFileList.value
|
||||
.filter((item) => !item.local)
|
||||
.map((item) => item[props.fields.id] || item.uid);
|
||||
|
|
|
@ -269,6 +269,7 @@ export interface TimeWaitingProcessor extends ExecuteConditionProcessorCommon {
|
|||
export type ExpressionType = RequestExtractExpressionEnum;
|
||||
// 表达式配置
|
||||
export interface ExpressionCommonConfig {
|
||||
id?: number | string; // 前端渲染使用字段
|
||||
enable: boolean; // 是否启用
|
||||
expression: string;
|
||||
extractType: ExpressionType; // 表达式类型
|
||||
|
|
|
@ -41,10 +41,10 @@
|
|||
const config = statusMap[props.status];
|
||||
return {
|
||||
style: {
|
||||
backgroundColor: config.bgColor,
|
||||
color: config.color,
|
||||
backgroundColor: config?.bgColor,
|
||||
color: config?.color,
|
||||
},
|
||||
text: t(config.text),
|
||||
text: t(config?.text),
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -244,7 +244,7 @@
|
|||
<div v-else-if="condition.processorType === RequestConditionProcessor.EXTRACT">
|
||||
<paramTable
|
||||
ref="extractParamsTableRef"
|
||||
v-model:params="condition.extractParams"
|
||||
v-model:params="condition.extractors"
|
||||
:default-param-item="defaultExtractParamItem"
|
||||
:columns="extractParamsColumns"
|
||||
:selectable="false"
|
||||
|
@ -272,6 +272,7 @@
|
|||
v-model:model-value="record.expression"
|
||||
class="ms-params-input"
|
||||
:max-length="255"
|
||||
size="mini"
|
||||
@input="() => handleExpressionChange(rowIndex)"
|
||||
@change="() => handleExpressionChange(rowIndex)"
|
||||
>
|
||||
|
@ -649,7 +650,7 @@ if (!result){
|
|||
const disabledExpressionSuffix = ref(false);
|
||||
|
||||
function handleExtractParamTableChange(resultArr: any[], isInit?: boolean) {
|
||||
condition.value.extractParams = [...resultArr];
|
||||
condition.value.extractors = [...resultArr];
|
||||
if (!isInit) {
|
||||
emit('change');
|
||||
}
|
||||
|
@ -697,13 +698,13 @@ if (!result){
|
|||
* 提取参数表格-应用更多设置
|
||||
*/
|
||||
function applyMoreSetting(record: ExpressionConfig) {
|
||||
condition.value.extractParams = condition.value.extractParams?.map((e) => {
|
||||
condition.value.extractors = condition.value.extractors?.map((e) => {
|
||||
if (e.id === activeRecord.value.id) {
|
||||
record.moreSettingPopoverVisible = false;
|
||||
return {
|
||||
...activeRecord.value,
|
||||
moreSettingPopoverVisible: false,
|
||||
} as any; // TOOD: 这里的后台类型应该是不对的,需要修改
|
||||
};
|
||||
}
|
||||
return e;
|
||||
});
|
||||
|
@ -714,7 +715,7 @@ if (!result){
|
|||
* 提取参数表格-保存快速提取的配置
|
||||
*/
|
||||
function handleFastExtractionApply(config: RegexExtract | JSONPathExtract | XPathExtract) {
|
||||
condition.value.extractParams = condition.value.extractParams?.map((e) => {
|
||||
condition.value.extractors = condition.value.extractors?.map((e) => {
|
||||
if (e.id === activeRecord.value.id) {
|
||||
return {
|
||||
...e,
|
||||
|
@ -726,7 +727,7 @@ if (!result){
|
|||
fastExtractionVisible.value = false;
|
||||
nextTick(() => {
|
||||
extractParamsTableRef.value?.addTableLine(
|
||||
condition.value.extractParams?.findIndex((e) => e.id === activeRecord.value.id) || 0
|
||||
condition.value.extractors?.findIndex((e) => e.id === activeRecord.value.id) || 0
|
||||
);
|
||||
});
|
||||
emit('change');
|
||||
|
|
|
@ -120,20 +120,23 @@
|
|||
},
|
||||
});
|
||||
break;
|
||||
// case RequestConditionProcessor.SQL:
|
||||
// data.value.push({
|
||||
// id,
|
||||
// enableCommonScript: false,
|
||||
// desc: '',
|
||||
// enable: true,
|
||||
// sqlSource: {
|
||||
// scriptName: '',
|
||||
// script: '',
|
||||
// storageType: 'column',
|
||||
// params: [],
|
||||
// },
|
||||
// });
|
||||
// break;
|
||||
case RequestConditionProcessor.SQL:
|
||||
data.value.push({
|
||||
id,
|
||||
processorType: RequestConditionProcessor.SQL,
|
||||
enableCommonScript: false,
|
||||
description: '',
|
||||
enable: true,
|
||||
dataSourceId: '',
|
||||
environmentId: '',
|
||||
queryTimeout: 0,
|
||||
resultVariable: '',
|
||||
script: '',
|
||||
variableNames: '',
|
||||
variables: [],
|
||||
extractParams: [],
|
||||
});
|
||||
break;
|
||||
case RequestConditionProcessor.TIME_WAITING:
|
||||
data.value.push({
|
||||
id,
|
||||
|
|
|
@ -216,12 +216,13 @@
|
|||
break;
|
||||
case RequestExtractExpressionEnum.JSON_PATH:
|
||||
try {
|
||||
matchResult.value = JSONPath({
|
||||
json: props.response ? JSON.parse(props.response) : '',
|
||||
path: expressionForm.value.expression,
|
||||
});
|
||||
matchResult.value =
|
||||
JSONPath({
|
||||
json: props.response ? JSON.parse(props.response) : '',
|
||||
path: expressionForm.value.expression,
|
||||
}) || [];
|
||||
} catch (error) {
|
||||
matchResult.value = JSONPath({ json: props.response || '', path: expressionForm.value.expression });
|
||||
matchResult.value = JSONPath({ json: props.response || '', path: expressionForm.value.expression }) || [];
|
||||
}
|
||||
break;
|
||||
case RequestExtractExpressionEnum.REGEX:
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{{ t('apiTestDebug.expressionMatchRule') }}
|
||||
</div>
|
||||
<a-radio-group v-model:model-value="expressionForm.expressionMatchingRule" size="small">
|
||||
<a-radio :value="RequestExtractExpressionRuleType.EXPRESSION.toLowerCase()">
|
||||
<a-radio :value="RequestExtractExpressionRuleType.EXPRESSION">
|
||||
<div class="flex items-center">
|
||||
{{ t('apiTestDebug.matchExpression') }}
|
||||
<a-tooltip :content="t('apiTestDebug.matchExpressionTip')" :content-style="{ maxWidth: '500px' }">
|
||||
|
@ -16,7 +16,7 @@
|
|||
</a-tooltip>
|
||||
</div>
|
||||
</a-radio>
|
||||
<a-radio :value="RequestExtractExpressionRuleType.GROUP.toLowerCase()">
|
||||
<a-radio :value="RequestExtractExpressionRuleType.GROUP">
|
||||
<div class="flex items-center">
|
||||
{{ t('apiTestDebug.matchGroup') }}
|
||||
<a-tooltip :content="t('apiTestDebug.matchGroupTip')" :content-style="{ maxWidth: '500px' }">
|
||||
|
@ -34,7 +34,7 @@
|
|||
{{ t('apiTestDebug.resultMatchRule') }}
|
||||
</div>
|
||||
<a-radio-group v-model:model-value="expressionForm.resultMatchingRule" size="small">
|
||||
<a-radio :value="RequestExtractResultMatchingRule.RANDOM.toLowerCase()">
|
||||
<a-radio :value="RequestExtractResultMatchingRule.RANDOM">
|
||||
<div class="flex items-center">
|
||||
{{ t('apiTestDebug.randomMatch') }}
|
||||
<a-tooltip :content="t('apiTestDebug.randomMatchTip')" :content-style="{ maxWidth: '400px' }">
|
||||
|
@ -45,7 +45,7 @@
|
|||
</a-tooltip>
|
||||
</div>
|
||||
</a-radio>
|
||||
<a-radio :value="RequestExtractResultMatchingRule.SPECIFIC.toLowerCase()">
|
||||
<a-radio :value="RequestExtractResultMatchingRule.SPECIFIC">
|
||||
<div class="flex items-center">
|
||||
{{ t('apiTestDebug.specifyMatch') }}
|
||||
<a-tooltip :content="t('apiTestDebug.specifyMatchTip')" :content-style="{ maxWidth: '400px' }">
|
||||
|
@ -56,7 +56,7 @@
|
|||
</a-tooltip>
|
||||
</div>
|
||||
</a-radio>
|
||||
<a-radio :value="RequestExtractResultMatchingRule.ALL.toLowerCase()">
|
||||
<a-radio :value="RequestExtractResultMatchingRule.ALL">
|
||||
<div class="flex items-center">
|
||||
{{ t('apiTestDebug.allMatch') }}
|
||||
<a-tooltip :content="t('apiTestDebug.allMatchTip')" :content-style="{ maxWidth: '400px' }">
|
||||
|
|
|
@ -111,9 +111,9 @@
|
|||
@change="(val) => handleTypeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
|
||||
/>
|
||||
</template>
|
||||
<template #expressionType="{ record, columnConfig, rowIndex }">
|
||||
<template #extractType="{ record, columnConfig, rowIndex }">
|
||||
<a-select
|
||||
v-model:model-value="record.expressionType"
|
||||
v-model:model-value="record.extractType"
|
||||
:options="columnConfig.typeOptions || []"
|
||||
class="param-input w-[110px]"
|
||||
size="mini"
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
import { ref, watch } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import { addDebugModule, updateDebug, updateDebugModule } from '@/api/modules/api-test/debug';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
|
@ -74,6 +73,9 @@
|
|||
parentId?: string; // 父节点 id
|
||||
nodeId?: string; // 节点 id
|
||||
popupOffset?: number;
|
||||
addModuleApi?: (params: { projectId: string; parentId: string; name: string }) => Promise<any>;
|
||||
updateModuleApi?: (params: { id: string; name: string }) => Promise<any>;
|
||||
updateApiNodeApi?: (params: { id: string; name: string }) => Promise<any>;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'close', 'addFinish', 'renameFinish', 'updateDescFinish']);
|
||||
|
@ -121,26 +123,26 @@
|
|||
if (!errors) {
|
||||
try {
|
||||
loading.value = true;
|
||||
if (props.mode === 'add') {
|
||||
if (props.mode === 'add' && props.addModuleApi) {
|
||||
// 添加根级模块
|
||||
await addDebugModule({
|
||||
await props.addModuleApi({
|
||||
projectId: appStore.currentProjectId,
|
||||
parentId: props.parentId || '',
|
||||
name: form.value.field,
|
||||
});
|
||||
Message.success(t('common.addSuccess'));
|
||||
emit('addFinish', form.value.field);
|
||||
} else if (props.mode === 'rename' && props.nodeType === 'API') {
|
||||
} else if (props.mode === 'rename' && props.nodeType === 'API' && props.updateApiNodeApi) {
|
||||
// 接口节点重命名
|
||||
await updateDebug({
|
||||
await props.updateApiNodeApi({
|
||||
id: props.nodeId || '',
|
||||
name: form.value.field,
|
||||
});
|
||||
Message.success(t('common.updateSuccess'));
|
||||
emit('renameFinish', form.value.field, props.nodeId);
|
||||
} else if (props.mode === 'rename') {
|
||||
} else if (props.mode === 'rename' && props.updateModuleApi) {
|
||||
// 模块重命名
|
||||
await updateDebugModule({
|
||||
await props.updateModuleApi({
|
||||
id: props.nodeId || '',
|
||||
name: form.value.field,
|
||||
});
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
<MsCodeEditor
|
||||
v-model:model-value="currentBodyCode"
|
||||
class="flex-1"
|
||||
theme="MS-text"
|
||||
theme="vs"
|
||||
height="100%"
|
||||
:show-full-screen="false"
|
||||
:show-theme-change="false"
|
||||
|
@ -121,7 +121,7 @@
|
|||
params: ExecuteBody;
|
||||
layout: 'horizontal' | 'vertical';
|
||||
secondBoxHeight: number;
|
||||
uploadTempFileApi?: (file: MsFileItem) => Promise<any>; // 上传临时文件接口
|
||||
uploadTempFileApi?: (file: File) => Promise<any>; // 上传临时文件接口
|
||||
fileSaveAsSourceId?: string | number; // 文件转存关联的资源id
|
||||
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; // 文件转存接口
|
||||
fileModuleOptionsApi?: (projectId: string) => Promise<ModuleTreeNode[]>; // 文件转存目录下拉框接口
|
||||
|
@ -135,18 +135,18 @@
|
|||
const { t } = useI18n();
|
||||
|
||||
const innerParams = useVModel(props, 'params', emit);
|
||||
const fileList = ref<any[]>(
|
||||
innerParams.value.binaryBody && innerParams.value.binaryBody.file ? [innerParams.value.binaryBody.file] : []
|
||||
);
|
||||
const fileList = ref<MsFileItem[]>([]);
|
||||
|
||||
async function handleFileChange(files: MsFileItem[]) {
|
||||
if (files.length === 0) {
|
||||
innerParams.value.binaryBody.file = undefined;
|
||||
return;
|
||||
onBeforeMount(() => {
|
||||
if (innerParams.value.binaryBody && innerParams.value.binaryBody.file) {
|
||||
fileList.value = [innerParams.value.binaryBody.file as unknown as MsFileItem];
|
||||
}
|
||||
});
|
||||
|
||||
async function handleFileChange() {
|
||||
if (!props.uploadTempFileApi) return;
|
||||
try {
|
||||
if (fileList.value[0]?.local) {
|
||||
if (fileList.value[0]?.local && fileList.value[0].file) {
|
||||
appStore.showLoading();
|
||||
const res = await props.uploadTempFileApi(fileList.value[0].file);
|
||||
innerParams.value.binaryBody.file = {
|
||||
|
@ -160,7 +160,7 @@
|
|||
} else {
|
||||
innerParams.value.binaryBody.file = {
|
||||
...fileList.value[0],
|
||||
fileId: fileList.value[0].uid,
|
||||
fileId: fileList.value[0]?.uid,
|
||||
fileName: fileList.value[0]?.originalName || '',
|
||||
fileAlias: fileList.value[0]?.name || '',
|
||||
local: false,
|
||||
|
|
|
@ -43,7 +43,9 @@
|
|||
<a-input
|
||||
v-model:model-value="requestVModel.url"
|
||||
:max-length="255"
|
||||
:placeholder="t('apiTestDebug.urlPlaceholder')"
|
||||
:placeholder="
|
||||
props.isDefinition ? t('apiTestDebug.definitionUrlPlaceholder') : t('apiTestDebug.urlPlaceholder')
|
||||
"
|
||||
allow-clear
|
||||
@change="handleUrlChange"
|
||||
/>
|
||||
|
@ -51,8 +53,7 @@
|
|||
</div>
|
||||
<div class="ml-[16px]">
|
||||
<a-dropdown-button
|
||||
v-if="!requestVModel.executeLoading"
|
||||
v-permission="[props.permissionMap.execute]"
|
||||
v-if="!requestVModel.executeLoading && hasAnyPermission([props.permissionMap.execute])"
|
||||
:disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
|
||||
class="exec-btn"
|
||||
@click="() => execute(isPriorityLocalExec ? 'localExec' : 'serverExec')"
|
||||
|
@ -70,12 +71,14 @@
|
|||
</a-dropdown-button>
|
||||
<a-button v-else type="primary" class="mr-[12px]" @click="stopDebug">{{ t('common.stop') }}</a-button>
|
||||
<a-dropdown
|
||||
v-if="props.isDefinition"
|
||||
v-permission="[props.permissionMap.create, props.permissionMap.update]"
|
||||
v-if="props.isDefinition && hasAnyPermission([props.permissionMap.create, props.permissionMap.update])"
|
||||
:loading="saveLoading || (isHttpProtocol && !requestVModel.url)"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<a-button :disabled="requestVModel.url.trim() === '' || requestVModel.name.trim() === ''" type="secondary">
|
||||
<a-button
|
||||
:disabled="(isHttpProtocol && requestVModel.url.trim() === '') || requestVModel.name.trim() === ''"
|
||||
type="secondary"
|
||||
>
|
||||
{{ t('common.save') }}
|
||||
</a-button>
|
||||
<template #content>
|
||||
|
@ -147,14 +150,14 @@
|
|||
@change="handlePluginFormChange"
|
||||
/>
|
||||
</a-spin>
|
||||
<debugHeader
|
||||
<httpHeader
|
||||
v-if="requestVModel.activeTab === RequestComposition.HEADER"
|
||||
v-model:params="requestVModel.headers"
|
||||
:layout="activeLayout"
|
||||
:second-box-height="secondBoxHeight"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<debugBody
|
||||
<httpBody
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.BODY"
|
||||
v-model:params="requestVModel.body"
|
||||
:layout="activeLayout"
|
||||
|
@ -165,14 +168,14 @@
|
|||
:file-module-options-api="props.fileModuleOptionsApi"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<debugQuery
|
||||
<httpQuery
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.QUERY"
|
||||
v-model:params="requestVModel.query"
|
||||
:layout="activeLayout"
|
||||
:second-box-height="secondBoxHeight"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<debugRest
|
||||
<httpRest
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.REST"
|
||||
v-model:params="requestVModel.rest"
|
||||
:layout="activeLayout"
|
||||
|
@ -182,6 +185,7 @@
|
|||
<precondition
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.PRECONDITION"
|
||||
v-model:config="requestVModel.children[0].preProcessorConfig"
|
||||
:is-definition="props.isDefinition"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<postcondition
|
||||
|
@ -190,14 +194,15 @@
|
|||
:response="requestVModel.response?.requestResults[0]?.responseResult.body"
|
||||
:layout="activeLayout"
|
||||
:second-box-height="secondBoxHeight"
|
||||
:is-definition="props.isDefinition"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<debugAuth
|
||||
<auth
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.AUTH"
|
||||
v-model:params="requestVModel.authConfig"
|
||||
@change="handleActiveDebugChange"
|
||||
/>
|
||||
<debugSetting
|
||||
<setting
|
||||
v-else-if="requestVModel.activeTab === RequestComposition.SETTING"
|
||||
v-model:params="requestVModel.otherConfig"
|
||||
@change="handleActiveDebugChange"
|
||||
|
@ -280,11 +285,11 @@
|
|||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||
import debugAuth from './auth.vue';
|
||||
import auth from './auth.vue';
|
||||
import postcondition from './postcondition.vue';
|
||||
import precondition from './precondition.vue';
|
||||
import response from './response/index.vue';
|
||||
import debugSetting from './setting.vue';
|
||||
import setting from './setting.vue';
|
||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
||||
|
||||
|
@ -323,10 +328,10 @@
|
|||
import type { Api } from '@form-create/arco-design';
|
||||
|
||||
// 懒加载Http协议组件
|
||||
const debugHeader = defineAsyncComponent(() => import('./header.vue'));
|
||||
const debugBody = defineAsyncComponent(() => import('./body.vue'));
|
||||
const debugQuery = defineAsyncComponent(() => import('./query.vue'));
|
||||
const debugRest = defineAsyncComponent(() => import('./rest.vue'));
|
||||
const httpHeader = defineAsyncComponent(() => import('./header.vue'));
|
||||
const httpBody = defineAsyncComponent(() => import('./body.vue'));
|
||||
const httpQuery = defineAsyncComponent(() => import('./query.vue'));
|
||||
const httpRest = defineAsyncComponent(() => import('./rest.vue'));
|
||||
|
||||
export interface RequestCustomAttr {
|
||||
isNew: boolean;
|
||||
|
@ -615,9 +620,13 @@
|
|||
|
||||
watch(
|
||||
() => requestVModel.value.id,
|
||||
() => {
|
||||
async () => {
|
||||
if (requestVModel.value.protocol !== 'HTTP') {
|
||||
requestVModel.value.activeTab = RequestComposition.PLUGIN;
|
||||
if (protocolOptions.value.length === 0) {
|
||||
// 还没初始化过协议列表,则初始化;在这里初始化是为了阻塞脚本的初始化,避免脚本初始化时协议列表还没初始化
|
||||
await initProtocolList();
|
||||
}
|
||||
initPluginScript();
|
||||
}
|
||||
},
|
||||
|
@ -846,6 +855,7 @@
|
|||
...parseRequestBodyResult,
|
||||
projectId: appStore.currentProjectId,
|
||||
frontendDebug: executeType === 'localExec',
|
||||
isNew: requestVModel.value.isNew,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1014,6 @@
|
|||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initProtocolList();
|
||||
initLocalConfig();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<condition
|
||||
v-model:list="innerConfig.processors"
|
||||
:condition-types="[RequestConditionProcessor.SCRIPT]"
|
||||
:condition-types="conditionTypes"
|
||||
add-text="apiTestDebug.postCondition"
|
||||
:response="props.response"
|
||||
:height-used="heightUsed"
|
||||
@change="emit('change')"
|
||||
>
|
||||
<!-- <template #titleRight>
|
||||
<template v-if="props.isDefinition" #titleRight>
|
||||
<a-switch v-model:model-value="innerConfig.enableGlobal" 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">
|
||||
|
@ -16,7 +16,7 @@
|
|||
size="16"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template> -->
|
||||
</template>
|
||||
</condition>
|
||||
</template>
|
||||
|
||||
|
@ -25,23 +25,24 @@
|
|||
|
||||
import condition from '@/views/api-test/components/condition/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ExecuteConditionConfig, ExecuteConditionProcessor } from '@/models/apiTest/common';
|
||||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
||||
|
||||
// import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
config: ExecuteConditionConfig;
|
||||
secondBoxHeight?: number;
|
||||
layout: 'horizontal' | 'vertical';
|
||||
response?: string; // 响应内容
|
||||
isDefinition?: boolean; // 是否是定义页面
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', params: ExecuteConditionProcessor[]): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
// const { t } = useI18n();
|
||||
const { t } = useI18n();
|
||||
const innerConfig = useVModel(props, 'config', emit);
|
||||
const heightUsed = computed(() => {
|
||||
if (props.layout === 'horizontal') {
|
||||
|
@ -49,6 +50,13 @@
|
|||
}
|
||||
return 428 + (props.secondBoxHeight || 0);
|
||||
});
|
||||
|
||||
const conditionTypes = computed(() => {
|
||||
if (props.isDefinition) {
|
||||
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL, RequestConditionProcessor.EXTRACT];
|
||||
}
|
||||
return [RequestConditionProcessor.SCRIPT];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<template>
|
||||
<condition
|
||||
v-model:list="innerConfig.processors"
|
||||
:condition-types="[RequestConditionProcessor.SCRIPT, RequestConditionProcessor.TIME_WAITING]"
|
||||
:condition-types="conditionTypes"
|
||||
add-text="apiTestDebug.precondition"
|
||||
@change="emit('change')"
|
||||
/>
|
||||
|
||||
<!-- <template #titleRight>
|
||||
>
|
||||
<template v-if="props.isDefinition" #titleRight>
|
||||
<a-switch v-model:model-value="innerConfig.enableGlobal" 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">
|
||||
|
@ -15,7 +14,8 @@
|
|||
size="16"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template> -->
|
||||
</template>
|
||||
</condition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -23,21 +23,29 @@
|
|||
|
||||
import condition from '@/views/api-test/components/condition/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import { ExecuteConditionConfig } from '@/models/apiTest/common';
|
||||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
||||
|
||||
// import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
config: ExecuteConditionConfig;
|
||||
isDefinition?: boolean; // 是否是定义页面
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:config', params: ExecuteConditionConfig): void;
|
||||
(e: 'change'): void;
|
||||
}>();
|
||||
|
||||
// const { t } = useI18n();
|
||||
const { t } = useI18n();
|
||||
const innerConfig = useVModel(props, 'config', emit);
|
||||
|
||||
const conditionTypes = computed(() => {
|
||||
if (props.isDefinition) {
|
||||
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.SQL, RequestConditionProcessor.TIME_WAITING];
|
||||
}
|
||||
return [RequestConditionProcessor.SCRIPT, RequestConditionProcessor.TIME_WAITING];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -109,7 +109,6 @@
|
|||
:show-theme-change="false"
|
||||
:show-language-change="false"
|
||||
:show-charset-change="false"
|
||||
read-only
|
||||
>
|
||||
<template #rightTitle>
|
||||
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="copyScript">
|
||||
|
@ -171,7 +170,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
responseDefinition: ResponseDefinition[];
|
||||
uploadTempFileApi?: (...args) => Promise<any>; // 上传临时文件接口
|
||||
uploadTempFileApi?: (file: File) => Promise<any>; // 上传临时文件接口
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'change'): void;
|
||||
|
@ -322,20 +321,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
const fileList = ref<any[]>(
|
||||
activeResponse.value.body.binaryBody && activeResponse.value.body.binaryBody.file
|
||||
? [activeResponse.value.body.binaryBody.file]
|
||||
: []
|
||||
);
|
||||
|
||||
async function handleFileChange(files: MsFileItem[]) {
|
||||
if (files.length === 0) {
|
||||
activeResponse.value.body.binaryBody.file = undefined;
|
||||
return;
|
||||
const fileList = ref<MsFileItem[]>([]);
|
||||
onBeforeMount(() => {
|
||||
if (activeResponse.value.body.binaryBody && activeResponse.value.body.binaryBody.file) {
|
||||
fileList.value = [activeResponse.value.body.binaryBody.file as unknown as MsFileItem];
|
||||
}
|
||||
});
|
||||
|
||||
async function handleFileChange() {
|
||||
if (!props.uploadTempFileApi) return;
|
||||
try {
|
||||
if (fileList.value[0]?.local) {
|
||||
if (fileList.value[0]?.local && fileList.value[0].file) {
|
||||
appStore.showLoading();
|
||||
const res = await props.uploadTempFileApi(fileList.value[0].file);
|
||||
activeResponse.value.body.binaryBody.file = {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="h-full rounded-[var(--border-radius-small)] border border-[var(--color-text-n8)] p-[16px]">
|
||||
<div class="setting">
|
||||
<a-form :model="settingForm" layout="vertical">
|
||||
<div class="flex items-center gap-[32px]">
|
||||
<a-form-item class="flex-1">
|
||||
|
@ -99,4 +99,13 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<style lang="less" scoped>
|
||||
.setting {
|
||||
@apply h-full overflow-y-auto;
|
||||
.ms-scroll-bar();
|
||||
|
||||
padding: 16px;
|
||||
border: 1px solid var(--color-text-n8);
|
||||
border-radius: var(--border-radius-small);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
mode="add"
|
||||
:all-names="rootModulesName"
|
||||
parent-id="NONE"
|
||||
:add-module-api="addDebugModule"
|
||||
@add-finish="initModules"
|
||||
>
|
||||
<MsButton v-permission="['PROJECT_API_DEBUG:READ+ADD']" type="icon" class="!mr-0 p-[2px]">
|
||||
|
@ -100,6 +101,7 @@
|
|||
:field-config="{ field: renameFolderTitle }"
|
||||
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
|
||||
:node-type="nodeData.type"
|
||||
:add-module-api="addDebugModule"
|
||||
@close="resetFocusNodeKey"
|
||||
@rename-finish="handleRenameFinish"
|
||||
>
|
||||
|
@ -111,6 +113,8 @@
|
|||
mode="add"
|
||||
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
|
||||
:parent-id="nodeData.id"
|
||||
:update-module-api="updateDebugModule"
|
||||
:update-api-node-api="updateDebug"
|
||||
@close="resetFocusNodeKey"
|
||||
@add-finish="() => initModules()"
|
||||
>
|
||||
|
@ -137,12 +141,15 @@
|
|||
import popConfirm from '@/views/api-test/components/popConfirm.vue';
|
||||
|
||||
import {
|
||||
addDebugModule,
|
||||
deleteDebug,
|
||||
deleteDebugModule,
|
||||
dragDebug,
|
||||
getDebugModuleCount,
|
||||
getDebugModules,
|
||||
moveDebugModule,
|
||||
updateDebug,
|
||||
updateDebugModule,
|
||||
} from '@/api/modules/api-test/debug';
|
||||
import { dropPositionMap } from '@/config/common';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
|
|
@ -2,6 +2,7 @@ export default {
|
|||
'apiTestDebug.newApi': 'New request',
|
||||
'apiTestDebug.importApi': 'Import request',
|
||||
'apiTestDebug.urlPlaceholder': 'Please enter the full URL including http or https',
|
||||
'apiTestDebug.definitionUrlPlaceholder': 'Enter the interface URL, starting with "/"',
|
||||
'apiTestDebug.serverExec': 'Server execution',
|
||||
'apiTestDebug.localExec': 'Local execution',
|
||||
'apiTestDebug.noMatchModule': 'No matching module data yet',
|
||||
|
|
|
@ -2,6 +2,7 @@ export default {
|
|||
'apiTestDebug.newApi': '新建请求',
|
||||
'apiTestDebug.importApi': '导入请求',
|
||||
'apiTestDebug.urlPlaceholder': '请输入包含 http 或 https 的完整URL',
|
||||
'apiTestDebug.definitionUrlPlaceholder': '输入接口URL,以“/”开始',
|
||||
'apiTestDebug.serverExec': '服务端执行',
|
||||
'apiTestDebug.localExec': '本地执行',
|
||||
'apiTestDebug.noMatchModule': '暂无匹配的模块数据',
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
read-only
|
||||
@init="(val) => (folderTree = val)"
|
||||
@folder-node-select="handleNodeSelect"
|
||||
@change-protocol="handleProtocolChange"
|
||||
/>
|
||||
<a-divider direction="vertical" :margin="16"></a-divider>
|
||||
<apiTable
|
||||
:active-module="activeModule"
|
||||
:offspring-ids="offspringIds"
|
||||
class="flex-1 overflow-hidden !pl-0 !pr-[16px]"
|
||||
:protocol="protocol"
|
||||
read-only
|
||||
/>
|
||||
</div>
|
||||
|
@ -46,11 +48,16 @@
|
|||
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||
const activeModule = ref<string>('all');
|
||||
const offspringIds = ref<string[]>([]);
|
||||
const protocol = ref('HTTP');
|
||||
|
||||
function handleNodeSelect(keys: string[], _offspringIds: string[]) {
|
||||
[activeModule.value] = keys;
|
||||
offspringIds.value = _offspringIds;
|
||||
}
|
||||
|
||||
function handleProtocolChange(val: string) {
|
||||
protocol.value = val;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -52,10 +52,10 @@
|
|||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button type="text" class="arco-btn-text--secondary" @click="methodFilterVisible = true">
|
||||
<MsButton type="text" class="arco-btn-text--secondary" @click="methodFilterVisible = true">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
<icon-down :class="methodFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
</MsButton>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="flex items-center justify-center px-[6px] py-[2px]">
|
||||
|
@ -75,16 +75,16 @@
|
|||
trigger="click"
|
||||
@popup-visible-change="handleFilterHidden"
|
||||
>
|
||||
<a-button type="text" class="arco-btn-text--secondary" @click="statusFilterVisible = true">
|
||||
<MsButton type="text" class="arco-btn-text--secondary ml-[10px]" @click="statusFilterVisible = true">
|
||||
{{ t(columnConfig.title as string) }}
|
||||
<icon-down :class="statusFilterVisible ? 'text-[rgb(var(--primary-5))]' : ''" />
|
||||
</a-button>
|
||||
</MsButton>
|
||||
<template #content>
|
||||
<div class="arco-table-filters-content">
|
||||
<div class="flex items-center justify-center px-[6px] py-[2px]">
|
||||
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
||||
<a-checkbox v-for="key of RequestDefinitionStatus" :key="key" :value="key">
|
||||
<apiStatus :status="key" />
|
||||
<a-checkbox v-for="val of Object.values(RequestDefinitionStatus)" :key="val" :value="val">
|
||||
<apiStatus :status="val" />
|
||||
</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</div>
|
||||
|
@ -108,7 +108,7 @@
|
|||
<template #label>
|
||||
<apiStatus :status="record.status" />
|
||||
</template>
|
||||
<a-option v-for="item of RequestDefinitionStatus" :key="item" :value="item">
|
||||
<a-option v-for="item of Object.values(RequestDefinitionStatus)" :key="item" :value="item">
|
||||
<apiStatus :status="item" />
|
||||
</a-option>
|
||||
</a-select>
|
||||
|
@ -271,6 +271,7 @@
|
|||
class?: string;
|
||||
activeModule: string;
|
||||
offspringIds: string[];
|
||||
protocol: string; // 查看的协议类型
|
||||
readOnly?: boolean; // 是否是只读模式
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
|
@ -346,12 +347,6 @@
|
|||
titleSlotName: 'statusFilter',
|
||||
width: 130,
|
||||
},
|
||||
{
|
||||
title: 'apiTestManagement.responsiblePerson',
|
||||
dataIndex: 'createUserName',
|
||||
showTooltip: true,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: 'apiTestManagement.path',
|
||||
slotName: 'path',
|
||||
|
@ -474,6 +469,7 @@
|
|||
projectId: appStore.currentProjectId,
|
||||
moduleIds: moduleIds.value,
|
||||
env: checkedEnv.value,
|
||||
protocol: props.protocol,
|
||||
filter: { status: statusFilters.value, type: methodFilters.value },
|
||||
};
|
||||
setLoadListParams(params);
|
||||
|
@ -495,6 +491,13 @@
|
|||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.protocol,
|
||||
() => {
|
||||
loadApiList();
|
||||
}
|
||||
);
|
||||
|
||||
function handleFilterHidden(val: boolean) {
|
||||
if (!val) {
|
||||
loadApiList();
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
</MsEditableTab>
|
||||
</div>
|
||||
<div v-show="activeApiTab.id === 'all'" class="flex-1">
|
||||
<apiTable :active-module="props.activeModule" :offspring-ids="props.offspringIds" @open-api-tab="openApiTab" />
|
||||
<apiTable
|
||||
:active-module="props.activeModule"
|
||||
:offspring-ids="props.offspringIds"
|
||||
:protocol="props.protocol"
|
||||
@open-api-tab="openApiTab"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="activeApiTab.id !== 'all'" class="flex-1 overflow-hidden">
|
||||
<a-tabs default-active-key="definition" animation lazy-load class="ms-api-tab-nav">
|
||||
|
@ -68,7 +73,7 @@
|
|||
<template #label>
|
||||
<apiStatus :status="activeApiTab.status" />
|
||||
</template>
|
||||
<a-option v-for="item of RequestDefinitionStatus" :key="item" :value="item">
|
||||
<a-option v-for="item of Object.values(RequestDefinitionStatus)" :key="item" :value="item">
|
||||
<apiStatus :status="item" />
|
||||
</a-option>
|
||||
</a-select>
|
||||
|
@ -178,6 +183,7 @@
|
|||
activeModule: string;
|
||||
offspringIds: string[];
|
||||
moduleTree: ModuleTreeNode[]; // 模块树
|
||||
protocol: string;
|
||||
}>();
|
||||
const emit = defineEmits(['addDone']);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
:active-module="props.activeModule"
|
||||
:all-count="props.allCount"
|
||||
:offspring-ids="props.offspringIds"
|
||||
:protocol="protocol"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="case" title="CASE" class="ms-api-tab-pane"> </a-tab-pane>
|
||||
|
@ -26,6 +27,7 @@
|
|||
allCount: number;
|
||||
activeModule: string;
|
||||
offspringIds: string[];
|
||||
protocol: string;
|
||||
moduleTree: ModuleTreeNode[]; // 模块树
|
||||
}>();
|
||||
|
||||
|
|
|
@ -51,7 +51,13 @@
|
|||
<a-doption value="addModule">{{ t('apiTestManagement.addSubModule') }}</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<popConfirm mode="add" :all-names="rootModulesName" parent-id="NONE" @add-finish="initModules">
|
||||
<popConfirm
|
||||
mode="add"
|
||||
:all-names="rootModulesName"
|
||||
parent-id="NONE"
|
||||
:add-module-api="addModule"
|
||||
@add-finish="initModules"
|
||||
>
|
||||
<span id="addModulePopSpan"></span>
|
||||
</popConfirm>
|
||||
</template>
|
||||
|
@ -105,6 +111,7 @@
|
|||
mode="add"
|
||||
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
|
||||
:parent-id="nodeData.id"
|
||||
:add-module-api="addModule"
|
||||
@close="resetFocusNodeKey"
|
||||
@add-finish="() => initModules()"
|
||||
>
|
||||
|
@ -119,6 +126,8 @@
|
|||
:node-id="nodeData.id"
|
||||
:field-config="{ field: renameFolderTitle }"
|
||||
:all-names="(nodeData.children || []).map((e: ModuleTreeNode) => e.name || '')"
|
||||
:update-module-api="updateModule"
|
||||
:update-api-node-api="updateModule"
|
||||
@close="resetFocusNodeKey"
|
||||
@rename-finish="initModules"
|
||||
>
|
||||
|
@ -144,11 +153,13 @@
|
|||
|
||||
import { getProtocolList } from '@/api/modules/api-test/common';
|
||||
import {
|
||||
addModule,
|
||||
deleteModule,
|
||||
getModuleCount,
|
||||
getModuleTree,
|
||||
getModuleTreeOnlyModules,
|
||||
moveModule,
|
||||
updateModule,
|
||||
} from '@/api/modules/api-test/management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
|
@ -168,7 +179,7 @@
|
|||
activeModule: 'all',
|
||||
}
|
||||
);
|
||||
const emit = defineEmits(['init', 'newApi', 'import', 'folderNodeSelect', 'clickApiNode']);
|
||||
const emit = defineEmits(['init', 'newApi', 'import', 'folderNodeSelect', 'clickApiNode', 'changeProtocol']);
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
@ -345,7 +356,7 @@
|
|||
if (isSetDefaultKey) {
|
||||
selectedKeys.value = [folderTree.value[0].id];
|
||||
}
|
||||
emit('init', folderTree.value);
|
||||
emit('init', folderTree.value, moduleProtocol.value);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
@ -377,6 +388,7 @@
|
|||
}
|
||||
|
||||
async function handleProtocolChange() {
|
||||
emit('changeProtocol', moduleProtocol.value);
|
||||
await initModules();
|
||||
initModuleCount();
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
<div class="p-[24px]">
|
||||
<moduleTree
|
||||
:active-node-id="activeApi?.id"
|
||||
@init="(val) => (folderTree = val)"
|
||||
@init="handleModuleInit"
|
||||
@new-api="newApi"
|
||||
@import="importDrawerVisible = true"
|
||||
@folder-node-select="handleNodeSelect"
|
||||
@click-api-node="handleApiNodeClick"
|
||||
@change-protocol="handleProtocolChange"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="b-0 absolute w-[88%]">
|
||||
|
@ -42,6 +43,7 @@
|
|||
:all-count="allCount"
|
||||
:active-module="activeModule"
|
||||
:offspring-ids="offspringIds"
|
||||
:protocol="protocol"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -66,9 +68,15 @@
|
|||
const allCount = ref(0);
|
||||
const importDrawerVisible = ref(false);
|
||||
const offspringIds = ref<string[]>([]);
|
||||
const protocol = ref('HTTP');
|
||||
const activeApi = ref<RequestParam>();
|
||||
const managementRef = ref<InstanceType<typeof management>>();
|
||||
|
||||
function handleModuleInit(tree, _protocol: string) {
|
||||
folderTree.value = tree;
|
||||
protocol.value = _protocol;
|
||||
}
|
||||
|
||||
function newApi() {
|
||||
managementRef.value?.newTab();
|
||||
}
|
||||
|
@ -86,6 +94,10 @@
|
|||
activeApi.value = params;
|
||||
}
|
||||
|
||||
function handleProtocolChange(val: string) {
|
||||
protocol.value = val;
|
||||
}
|
||||
|
||||
provide('setActiveApi', setActiveApi);
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in New Issue