feat(接口场景): 增加api/case请求参数的参数名称唯一的校验

This commit is contained in:
teukkk 2024-04-16 20:37:33 +08:00 committed by Craftsman
parent 43d696670f
commit 3a2adf5889
4 changed files with 200 additions and 3 deletions

View File

@ -75,8 +75,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { FormInstance } from '@arco-design/web-vue'; import { FormInstance, Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es'; import { cloneDeep, debounce } from 'lodash-es';
import { LanguageEnum } from '@/components/pure/ms-code-editor/types'; import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
import MsDrawer from '@/components/pure/ms-drawer/index.vue'; import MsDrawer from '@/components/pure/ms-drawer/index.vue';
@ -142,6 +142,7 @@
title: 'project.commonScript.ParameterNames', title: 'project.commonScript.ParameterNames',
slotName: 'key', slotName: 'key',
dataIndex: 'key', dataIndex: 'key',
needValidRepeat: true,
}, },
{ {
title: 'project.commonScript.ParameterValue', title: 'project.commonScript.ParameterValue',
@ -179,9 +180,21 @@
} }
} }
const paramMessageList = ref<string[]>([]);
const setErrorMessageList = debounce((list: string[]) => {
paramMessageList.value = [...list];
}, 300);
provide('setErrorMessageList', setErrorMessageList);
function handleDrawerConfirm() { function handleDrawerConfirm() {
formRef.value?.validate(async (errors) => { formRef.value?.validate(async (errors) => {
if (!errors) { if (!errors) {
if (paramMessageList.value.length) {
paramMessageList.value?.forEach((message) => {
Message.error(message);
});
return;
}
emit('save', form.value); emit('save', form.value);
} }
}); });

View File

@ -12,6 +12,7 @@
props.noContentPadding ? 'ms-drawer-no-content-padding' : '', props.noContentPadding ? 'ms-drawer-no-content-padding' : '',
props.noTitle ? 'ms-drawer-no-title' : '', props.noTitle ? 'ms-drawer-no-title' : '',
]" ]"
:on-before-cancel="handleBeforeCancel"
@cancel="handleCancel" @cancel="handleCancel"
@close="handleClose" @close="handleClose"
> >
@ -150,6 +151,7 @@
drawerStyle?: Record<string, string>; // drawerStyle?: Record<string, string>; //
showFullScreen?: boolean; // showFullScreen?: boolean; //
maskClosable?: boolean; // maskClosable?: boolean; //
handleBeforeCancel?: () => boolean;
} }
const props = withDefaults(defineProps<DrawerProps>(), { const props = withDefaults(defineProps<DrawerProps>(), {
@ -197,6 +199,11 @@
emit('close'); emit('close');
}; };
//
const handleBeforeCancel = () => {
return props.handleBeforeCancel ? props.handleBeforeCancel() : true;
};
const resizing = ref(false); // const resizing = ref(false); //
const drawerWidth = ref(props.width); // const drawerWidth = ref(props.width); //

View File

@ -6,6 +6,7 @@
:show-continue="true" :show-continue="true"
:footer="requestVModel.isNew === true" :footer="requestVModel.isNew === true"
:ok-disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)" :ok-disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
:handle-before-cancel="handleBeforeCancel"
show-full-screen show-full-screen
@confirm="handleSave" @confirm="handleSave"
@continue="handleContinue" @continue="handleContinue"
@ -25,7 +26,7 @@
/> />
<a-tooltip v-if="!isShowEditStepNameInput" :content="title" position="bottom"> <a-tooltip v-if="!isShowEditStepNameInput" :content="title" position="bottom">
<div class="flex items-center gap-[4px]"> <div class="flex items-center gap-[4px]">
<div class="one-line-text"> <div class="one-line-text max-w-[300px]">
{{ title }} {{ title }}
</div> </div>
<MsIcon <MsIcon
@ -374,6 +375,7 @@
import apiMethodName from '@/views/api-test/components/apiMethodName.vue'; import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue'; import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
import auth from '@/views/api-test/components/requestComposition/auth.vue'; import auth from '@/views/api-test/components/requestComposition/auth.vue';
import { TabErrorMessage } from '@/views/api-test/components/requestComposition/index.vue';
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue'; import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
import precondition from '@/views/api-test/components/requestComposition/precondition.vue'; import precondition from '@/views/api-test/components/requestComposition/precondition.vue';
import response from '@/views/api-test/components/requestComposition/response/index.vue'; import response from '@/views/api-test/components/requestComposition/response/index.vue';
@ -444,6 +446,9 @@
linkFileIds: string[]; linkFileIds: string[];
deleteFileIds?: string[]; deleteFileIds?: string[];
unLinkFileIds?: string[]; unLinkFileIds?: string[];
errorMessageInfo?: {
[key: string]: Record<string, any>;
};
} }
export type RequestParam = ExecuteApiRequestFullParams & { export type RequestParam = ExecuteApiRequestFullParams & {
@ -544,6 +549,7 @@
responseActiveTab: ResponseComposition.BODY, responseActiveTab: ResponseComposition.BODY,
isNew: true, isNew: true,
executeLoading: false, executeLoading: false,
errorMessageInfo: {},
}; };
const requestVModel = ref<RequestParam>(defaultApiParams); const requestVModel = ref<RequestParam>(defaultApiParams);
@ -1099,15 +1105,102 @@
emit('stopDebug'); emit('stopDebug');
} }
function initErrorMessageInfoItem(key) {
if (requestVModel.value.errorMessageInfo && !requestVModel.value.errorMessageInfo[key]) {
requestVModel.value.errorMessageInfo[key] = {};
}
}
function setChildErrorMessage(key: number | string, listItem: TabErrorMessage) {
if (requestVModel.value.errorMessageInfo) {
requestVModel.value.errorMessageInfo[requestVModel.value.activeTab][key] = cloneDeep(listItem);
}
}
function changeTabErrorMessageList(tabKey: string, formErrorMessageList: string[]) {
if (!requestVModel.value.errorMessageInfo) return;
const label = contentTabList.value.find((item) => item.value === tabKey)?.label ?? '';
const listItem: TabErrorMessage = {
value: tabKey,
label,
messageList: formErrorMessageList,
};
initErrorMessageInfoItem(requestVModel.value.activeTab);
if (requestVModel.value.activeTab === RequestComposition.BODY) {
setChildErrorMessage(requestVModel.value.body.bodyType, listItem);
} else if (requestVModel.value.activeTab === RequestComposition.POST_CONDITION) {
setChildErrorMessage(requestVModel.value.children[0].postProcessorConfig.activeItemId as number, listItem);
} else if (requestVModel.value.activeTab === RequestComposition.PRECONDITION) {
setChildErrorMessage(requestVModel.value.children[0].preProcessorConfig.activeItemId as number, listItem);
} else {
requestVModel.value.errorMessageInfo[requestVModel.value.activeTab] = cloneDeep(listItem);
}
}
const setErrorMessageList = debounce((list: string[]) => {
changeTabErrorMessageList(requestVModel.value.activeTab, list);
}, 300);
provide('setErrorMessageList', setErrorMessageList);
//
function getFlattenedMessages() {
if (!requestVModel.value.errorMessageInfo) return;
const flattenedMessages: { label: string; messageList: string[] }[] = [];
const { errorMessageInfo } = requestVModel.value;
Object.entries(errorMessageInfo).forEach(([key, item]) => {
const label = item.label || Object.values(item)[0]?.label;
//
if ([RequestComposition.POST_CONDITION as string, RequestComposition.PRECONDITION as string].includes(key)) {
const processorIds = requestVModel.value.children[0][
key === RequestComposition.POST_CONDITION ? 'postProcessorConfig' : 'preProcessorConfig'
].processors.map((processorItem) => String(processorItem.id));
Object.entries(item).forEach(([childKey, childItem]) => {
if (!processorIds.includes(childKey)) {
childItem.messageList = [];
}
});
}
const messageList: string[] =
item.messageList || [...new Set(Object.values(item).flatMap((child) => child.messageList))] || [];
if (messageList.length) {
flattenedMessages.push({ label, messageList: [...new Set(messageList)] });
}
});
return flattenedMessages;
}
function showMessage() {
getFlattenedMessages()?.forEach(({ label, messageList }) => {
messageList?.forEach((message) => {
Message.error(`${label}${message}`);
});
});
}
function handleContinue() { function handleContinue() {
//
if (getFlattenedMessages()?.length) {
showMessage();
return;
}
emit('addStep', cloneDeep(makeRequestParams())); emit('addStep', cloneDeep(makeRequestParams()));
} }
function handleSave() { function handleSave() {
handleContinue(); handleContinue();
if (getFlattenedMessages()?.length) return;
visible.value = false; visible.value = false;
} }
function handleBeforeCancel() {
//
if (getFlattenedMessages()?.length) {
showMessage();
return false;
}
return true;
}
function handleClose() { function handleClose() {
// applyStep // applyStep
if (!requestVModel.value.isNew) { if (!requestVModel.value.isNew) {

View File

@ -5,6 +5,7 @@
:footer="false" :footer="false"
show-full-screen show-full-screen
no-content-padding no-content-padding
:handle-before-cancel="handleBeforeCancel"
@close="handleClose" @close="handleClose"
> >
<template #title> <template #title>
@ -286,6 +287,7 @@
import replaceButton from './replaceButton.vue'; import replaceButton from './replaceButton.vue';
import stepType from './stepType/stepType.vue'; import stepType from './stepType/stepType.vue';
import auth from '@/views/api-test/components/requestComposition/auth.vue'; import auth from '@/views/api-test/components/requestComposition/auth.vue';
import { TabErrorMessage } from '@/views/api-test/components/requestComposition/index.vue';
import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue'; import postcondition from '@/views/api-test/components/requestComposition/postcondition.vue';
import precondition from '@/views/api-test/components/requestComposition/precondition.vue'; import precondition from '@/views/api-test/components/requestComposition/precondition.vue';
import response from '@/views/api-test/components/requestComposition/response/index.vue'; import response from '@/views/api-test/components/requestComposition/response/index.vue';
@ -420,6 +422,7 @@
responseActiveTab: ResponseComposition.BODY, responseActiveTab: ResponseComposition.BODY,
isNew: true, isNew: true,
executeLoading: false, executeLoading: false,
errorMessageInfo: {},
}; };
const requestVModel = ref<RequestParam>(defaultApiParams); const requestVModel = ref<RequestParam>(defaultApiParams);
const _stepType = computed(() => { const _stepType = computed(() => {
@ -938,6 +941,87 @@
emit('stopDebug'); emit('stopDebug');
} }
function initErrorMessageInfoItem(key) {
if (requestVModel.value.errorMessageInfo && !requestVModel.value.errorMessageInfo[key]) {
requestVModel.value.errorMessageInfo[key] = {};
}
}
function setChildErrorMessage(key: number | string, listItem: TabErrorMessage) {
if (requestVModel.value.errorMessageInfo) {
requestVModel.value.errorMessageInfo[requestVModel.value.activeTab][key] = cloneDeep(listItem);
}
}
function changeTabErrorMessageList(tabKey: string, formErrorMessageList: string[]) {
if (!requestVModel.value.errorMessageInfo) return;
const label = contentTabList.value.find((item) => item.value === tabKey)?.label ?? '';
const listItem: TabErrorMessage = {
value: tabKey,
label,
messageList: formErrorMessageList,
};
initErrorMessageInfoItem(requestVModel.value.activeTab);
if (requestVModel.value.activeTab === RequestComposition.BODY) {
setChildErrorMessage(requestVModel.value.body.bodyType, listItem);
} else if (requestVModel.value.activeTab === RequestComposition.POST_CONDITION) {
setChildErrorMessage(requestVModel.value.children[0].postProcessorConfig.activeItemId as number, listItem);
} else if (requestVModel.value.activeTab === RequestComposition.PRECONDITION) {
setChildErrorMessage(requestVModel.value.children[0].preProcessorConfig.activeItemId as number, listItem);
} else {
requestVModel.value.errorMessageInfo[requestVModel.value.activeTab] = cloneDeep(listItem);
}
}
const setErrorMessageList = debounce((list: string[]) => {
changeTabErrorMessageList(requestVModel.value.activeTab, list);
}, 300);
provide('setErrorMessageList', setErrorMessageList);
//
function getFlattenedMessages() {
if (!requestVModel.value.errorMessageInfo) return;
const flattenedMessages: { label: string; messageList: string[] }[] = [];
const { errorMessageInfo } = requestVModel.value;
Object.entries(errorMessageInfo).forEach(([key, item]) => {
const label = item.label || Object.values(item)[0]?.label;
//
if ([RequestComposition.POST_CONDITION as string, RequestComposition.PRECONDITION as string].includes(key)) {
const processorIds = requestVModel.value.children[0][
key === RequestComposition.POST_CONDITION ? 'postProcessorConfig' : 'preProcessorConfig'
].processors.map((processorItem) => String(processorItem.id));
Object.entries(item).forEach(([childKey, childItem]) => {
if (!processorIds.includes(childKey)) {
childItem.messageList = [];
}
});
}
const messageList: string[] =
item.messageList || [...new Set(Object.values(item).flatMap((child) => child.messageList))] || [];
if (messageList.length) {
flattenedMessages.push({ label, messageList: [...new Set(messageList)] });
}
});
return flattenedMessages;
}
function showMessage() {
getFlattenedMessages()?.forEach(({ label, messageList }) => {
messageList?.forEach((message) => {
Message.error(`${label}${message}`);
});
});
}
function handleBeforeCancel() {
//
if (getFlattenedMessages()?.length) {
showMessage();
return false;
}
return true;
}
function handleClose() { function handleClose() {
// applyStep case // applyStep case
if (!requestVModel.value.isNew && activeStep.value?.refType === ScenarioStepRefType.COPY) { if (!requestVModel.value.isNew && activeStep.value?.refType === ScenarioStepRefType.COPY) {