feat(接口场景): 新建/详情的名称唯一校验

This commit is contained in:
teukkk 2024-04-17 18:32:13 +08:00 committed by Craftsman
parent 7bab0159ef
commit 6668378d9c
7 changed files with 194 additions and 4 deletions

View File

@ -409,6 +409,9 @@ export interface Scenario {
isExecute?: boolean; // 是否从列表执行进去场景详情
isDebug?: boolean; // 是否调试,区分执行场景和批量调试步骤
executeType?: 'localExec' | 'serverExec'; // 执行类型
errorMessageInfo?: {
[key: string]: Record<string, any>;
};
}
// 场景详情
export interface ScenarioDetail extends Scenario {

View File

@ -130,6 +130,7 @@ export const defaultScenario: Scenario = {
executeLoading: false, // 执行loading
isDebug: false,
stepResponses: {},
errorMessageInfo: {},
};
export const conditionOptions = [

View File

@ -75,6 +75,7 @@
title: 'apiScenario.params.name',
dataIndex: 'key',
slotName: 'key',
needValidRepeat: true,
},
{
title: 'apiScenario.params.type',

View File

@ -6,7 +6,7 @@
:is-definition="false"
sql-code-editor-height="300px"
is-scenario
@change="emit('change')"
@change="emit('change', true)"
/>
</div>
<a-divider class="my-[8px]" type="dashed" />
@ -17,7 +17,7 @@
:layout="activeLayout"
sql-code-editor-height="300px"
is-scenario
@change="emit('change')"
@change="emit('change', false)"
/>
</div>
</div>
@ -30,7 +30,7 @@
import { ExecuteConditionConfig } from '@/models/apiTest/common';
const emit = defineEmits<{
(e: 'change');
(e: 'change', isChangePre: boolean): void;
}>();
const activeLayout = ref<'horizontal' | 'vertical'>('vertical');

View File

@ -32,6 +32,7 @@
v-if="activeKey === ScenarioCreateComposition.PRE_POST"
v-model:post-processor-config="scenario.scenarioConfig.postProcessorConfig"
v-model:pre-processor-config="scenario.scenarioConfig.preProcessorConfig"
@change="changePrePost"
/>
</a-tab-pane>
<a-tab-pane :key="ScenarioCreateComposition.ASSERTION" class="scenario-create-tab-pane">
@ -124,8 +125,12 @@
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue';
import { cloneDeep, debounce } from 'lodash-es';
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
import baseInfo from '../components/baseInfo.vue';
import { TabErrorMessage } from '@/views/api-test/components/requestComposition/index.vue';
import { useI18n } from '@/hooks/useI18n';
@ -157,7 +162,95 @@
const splitBoxRef = ref<InstanceType<typeof MsSplitBox>>();
const baseInfoRef = ref<InstanceType<typeof baseInfo>>();
// tabisChangePreform
const isChangePre = ref(true);
function changePrePost(changePre: boolean) {
isChangePre.value = changePre;
}
// TODO:
function initErrorMessageInfoItem(key) {
if (scenario.value.errorMessageInfo && !scenario.value.errorMessageInfo[key]) {
scenario.value.errorMessageInfo[key] = {};
}
}
function setChildErrorMessage(key: number | string, listItem: TabErrorMessage) {
if (!scenario.value.errorMessageInfo) return;
scenario.value.errorMessageInfo[activeKey.value][key] = cloneDeep(listItem);
}
function changeTabErrorMessageList(tabKey: string, formErrorMessageList: string[]) {
if (!scenario.value.errorMessageInfo) return;
initErrorMessageInfoItem(tabKey);
if (tabKey === ScenarioCreateComposition.PRE_POST) {
setChildErrorMessage(
scenario.value.scenarioConfig[isChangePre.value ? 'preProcessorConfig' : 'postProcessorConfig']
.activeItemId as number,
{
value: tabKey,
label: t('apiScenario.prePost'),
messageList: formErrorMessageList,
}
);
} else if (tabKey === ScenarioCreateComposition.PARAMS) {
scenario.value.errorMessageInfo[tabKey] = {
value: tabKey,
label: t('apiScenario.params'),
messageList: formErrorMessageList,
};
}
}
const setErrorMessageList = debounce((list: string[]) => {
changeTabErrorMessageList(activeKey.value, list);
}, 300);
provide('setErrorMessageList', setErrorMessageList);
//
function getFlattenedMessages() {
if (!scenario.value.errorMessageInfo) return;
const flattenedMessages: { label: string; messageList: string[] }[] = [];
const { errorMessageInfo } = scenario.value;
Object.entries(errorMessageInfo).forEach(([key, item]) => {
const label = item.label || Object.values(item)[0]?.label;
//
if (key === ScenarioCreateComposition.PRE_POST) {
// id
const processorIds = [
...scenario.value.scenarioConfig.preProcessorConfig.processors,
...scenario.value.scenarioConfig.postProcessorConfig.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 validScenarioForm(cb: () => Promise<void>) {
//
if (getFlattenedMessages()?.length) {
showMessage();
return;
}
baseInfoRef.value?.createFormRef?.validate(async (errors) => {
if (errors) {
splitBoxRef.value?.expand();

View File

@ -75,7 +75,7 @@
v-if="activeKey === ScenarioDetailComposition.PRE_POST"
v-model:post-processor-config="scenario.scenarioConfig.postProcessorConfig"
v-model:pre-processor-config="scenario.scenarioConfig.preProcessorConfig"
@change="scenario.unSaved = true"
@change="changePrePost"
/>
</a-tab-pane>
<a-tab-pane
@ -133,6 +133,7 @@
import { useI18n } from 'vue-i18n';
import { useClipboard } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
import { cloneDeep, debounce } from 'lodash-es';
import MsDetailCard from '@/components/pure/ms-detail-card/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
@ -141,6 +142,7 @@
import baseInfo from '../components/baseInfo.vue';
import step from '../components/step/index.vue';
import apiStatus from '@/views/api-test/components/apiStatus.vue';
import { TabErrorMessage } from '@/views/api-test/components/requestComposition/index.vue';
import { followScenario } from '@/api/modules/api-test/scenario';
@ -221,8 +223,97 @@
const activeKey = ref<ScenarioDetailComposition>(ScenarioDetailComposition.STEP);
// tabisChangePreform
const isChangePre = ref(true);
function changePrePost(changePre: boolean) {
isChangePre.value = changePre;
scenario.value.unSaved = true;
}
// TODO:
function initErrorMessageInfoItem(key) {
if (scenario.value.errorMessageInfo && !scenario.value.errorMessageInfo[key]) {
scenario.value.errorMessageInfo[key] = {};
}
}
function setChildErrorMessage(key: number | string, listItem: TabErrorMessage) {
if (!scenario.value.errorMessageInfo) return;
scenario.value.errorMessageInfo[activeKey.value][key] = cloneDeep(listItem);
}
function changeTabErrorMessageList(tabKey: string, formErrorMessageList: string[]) {
if (!scenario.value.errorMessageInfo) return;
initErrorMessageInfoItem(tabKey);
if (tabKey === ScenarioDetailComposition.PRE_POST) {
setChildErrorMessage(
scenario.value.scenarioConfig[isChangePre.value ? 'preProcessorConfig' : 'postProcessorConfig']
.activeItemId as number,
{
value: tabKey,
label: t('apiScenario.prePost'),
messageList: formErrorMessageList,
}
);
} else if (tabKey === ScenarioDetailComposition.PARAMS) {
scenario.value.errorMessageInfo[tabKey] = {
value: tabKey,
label: t('apiScenario.params'),
messageList: formErrorMessageList,
};
}
}
const setErrorMessageList = debounce((list: string[]) => {
changeTabErrorMessageList(activeKey.value, list);
}, 300);
provide('setErrorMessageList', setErrorMessageList);
//
function getFlattenedMessages() {
if (!scenario.value.errorMessageInfo) return;
const flattenedMessages: { label: string; messageList: string[] }[] = [];
const { errorMessageInfo } = scenario.value;
Object.entries(errorMessageInfo).forEach(([key, item]) => {
const label = item.label || Object.values(item)[0]?.label;
//
if (key === ScenarioDetailComposition.PRE_POST) {
// id
const processorIds = [
...scenario.value.scenarioConfig.preProcessorConfig.processors,
...scenario.value.scenarioConfig.postProcessorConfig.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}`);
});
});
}
const baseInfoRef = ref<InstanceType<typeof baseInfo>>();
function validScenarioForm(cb: () => Promise<void>) {
//
if (getFlattenedMessages()?.length) {
showMessage();
return;
}
if (!baseInfoRef.value) {
cb();
return;

View File

@ -427,6 +427,7 @@
name: isCopy ? copyName : defaultScenarioInfo.name,
isNew: isCopy,
stepResponses: {},
errorMessageInfo: {},
});
if (action === 'execute') {
nextTick(() => {