feat(接口测试): 请求参数的参数名称唯一的校验(请求头,请求体,Query,REST)
This commit is contained in:
parent
ce975229c9
commit
087bfb417f
|
@ -1,148 +1,168 @@
|
||||||
<template>
|
<template>
|
||||||
<MsBaseTable
|
<a-form ref="formRef" :model="propsRes">
|
||||||
v-bind="propsRes"
|
<MsBaseTable
|
||||||
:hoverable="false"
|
v-bind="propsRes"
|
||||||
is-simple-setting
|
:hoverable="false"
|
||||||
:span-method="props.spanMethod"
|
is-simple-setting
|
||||||
:class="!props.selectable && !props.draggable ? 'ms-form-table-no-left-action' : ''"
|
:span-method="props.spanMethod"
|
||||||
v-on="propsEvent"
|
:class="!props.selectable && !props.draggable ? 'ms-form-table-no-left-action' : ''"
|
||||||
@drag-change="tableChange"
|
v-on="propsEvent"
|
||||||
>
|
@drag-change="tableChange"
|
||||||
<!-- 展开行-->
|
@init-end="validateAndUpdateErrorMessageList"
|
||||||
<template #expand-icon="{ expanded, record }">
|
|
||||||
<div class="flex items-center gap-[2px] text-[var(--color-text-4)]">
|
|
||||||
<MsIcon :type="expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'" />
|
|
||||||
<div v-if="record.children">{{ record.children.length }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
v-for="item of props.columns.filter((e) => e.slotName !== undefined)"
|
|
||||||
#[item.slotName!]="{ record, rowIndex, column }"
|
|
||||||
>
|
>
|
||||||
<slot :name="item.slotName" v-bind="{ record, rowIndex, column, dataIndex: item.dataIndex, columnConfig: item }">
|
<!-- 展开行-->
|
||||||
<a-tooltip
|
<template #expand-icon="{ expanded, record }">
|
||||||
v-if="item.hasRequired"
|
<div class="flex items-center gap-[2px] text-[var(--color-text-4)]">
|
||||||
:content="t(record.required ? 'msFormTable.paramRequired' : 'msFormTable.paramNotRequired')"
|
<MsIcon :type="expanded ? 'icon-icon_split_turn-down_arrow' : 'icon-icon_split-turn-down-left'" />
|
||||||
|
<div v-if="record.children">{{ record.children.length }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-for="item of props.columns.filter((e) => e.slotName !== undefined)"
|
||||||
|
:key="item.toString()"
|
||||||
|
#[item.slotName!]="{ record, rowIndex, column }"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:field="`data[${rowIndex}].${item.dataIndex}`"
|
||||||
|
label=""
|
||||||
|
:rules="{
|
||||||
|
validator: (value, callback) => {
|
||||||
|
validRepeat(rowIndex, item.dataIndex as string, value, callback);
|
||||||
|
},
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<MsButton
|
<slot
|
||||||
type="icon"
|
:name="item.slotName"
|
||||||
:class="[
|
v-bind="{ record, rowIndex, column, dataIndex: item.dataIndex, columnConfig: item }"
|
||||||
record.required ? '!text-[rgb(var(--danger-5))]' : '!text-[var(--color-text-brand)]',
|
|
||||||
'!mr-[4px] !p-[4px]',
|
|
||||||
]"
|
|
||||||
size="mini"
|
|
||||||
@click="toggleRequired(record, rowIndex, item)"
|
|
||||||
>
|
>
|
||||||
<div>*</div>
|
<a-tooltip
|
||||||
</MsButton>
|
v-if="item.hasRequired"
|
||||||
</a-tooltip>
|
:content="t(record.required ? 'msFormTable.paramRequired' : 'msFormTable.paramNotRequired')"
|
||||||
<a-input
|
>
|
||||||
v-if="item.inputType === 'input'"
|
<MsButton
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
type="icon"
|
||||||
:placeholder="t(item.locale)"
|
:class="[
|
||||||
class="ms-form-table-input"
|
record.required ? '!text-[rgb(var(--danger-5))]' : '!text-[var(--color-text-brand)]',
|
||||||
:max-length="255"
|
'!mr-[4px] !p-[4px]',
|
||||||
size="mini"
|
]"
|
||||||
@input="() => handleFormChange(record, rowIndex, item)"
|
size="mini"
|
||||||
/>
|
@click="toggleRequired(record, rowIndex, item)"
|
||||||
<a-select
|
>
|
||||||
v-else-if="item.inputType === 'select'"
|
<div>*</div>
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
</MsButton>
|
||||||
:options="item.typeOptions || []"
|
</a-tooltip>
|
||||||
class="ms-form-table-input w-full"
|
<a-input
|
||||||
size="mini"
|
v-if="item.inputType === 'input'"
|
||||||
@change="() => handleFormChange(record, rowIndex, item)"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
/>
|
:placeholder="t(item.locale)"
|
||||||
<MsTagsInput
|
class="ms-form-table-input"
|
||||||
v-else-if="item.inputType === 'tags'"
|
:max-length="255"
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
size="mini"
|
||||||
:max-tag-count="1"
|
@input="() => handleFormChange(record, rowIndex, item)"
|
||||||
input-class="ms-form-table-input"
|
/>
|
||||||
size="mini"
|
<a-select
|
||||||
@change="() => handleFormChange(record, rowIndex, item)"
|
v-else-if="item.inputType === 'select'"
|
||||||
@clear="() => handleFormChange(record, rowIndex, item)"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
/>
|
:options="item.typeOptions || []"
|
||||||
<a-switch
|
class="ms-form-table-input w-full"
|
||||||
v-else-if="item.inputType === 'switch'"
|
size="mini"
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
@change="() => handleFormChange(record, rowIndex, item)"
|
||||||
size="small"
|
/>
|
||||||
class="ms-form-table-input-switch"
|
<MsTagsInput
|
||||||
type="line"
|
v-else-if="item.inputType === 'tags'"
|
||||||
@change="() => handleFormChange(record, rowIndex, item)"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
/>
|
:max-tag-count="1"
|
||||||
<a-checkbox
|
input-class="ms-form-table-input"
|
||||||
v-else-if="item.inputType === 'checkbox'"
|
size="mini"
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
@change="() => handleFormChange(record, rowIndex, item)"
|
||||||
@change="() => handleFormChange(record, rowIndex, item)"
|
@clear="() => handleFormChange(record, rowIndex, item)"
|
||||||
/>
|
/>
|
||||||
<template v-else-if="item.inputType === 'autoComplete'">
|
<a-switch
|
||||||
<a-auto-complete
|
v-else-if="item.inputType === 'switch'"
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
:data="item.autoCompleteParams?.filter((e) => e.isShow === true)"
|
size="small"
|
||||||
class="ms-form-table-input"
|
class="ms-form-table-input-switch"
|
||||||
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
|
type="line"
|
||||||
:filter-option="false"
|
@change="() => handleFormChange(record, rowIndex, item)"
|
||||||
size="small"
|
/>
|
||||||
@search="(val) => handleSearchParams(val, item)"
|
<a-checkbox
|
||||||
@change="() => handleFormChange(record, rowIndex, item)"
|
v-else-if="item.inputType === 'checkbox'"
|
||||||
@select="(val) => selectAutoComplete(val, record, item)"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
>
|
@change="() => handleFormChange(record, rowIndex, item)"
|
||||||
<template #option="{ data: opt }">
|
/>
|
||||||
<div class="w-[350px]">
|
<template v-else-if="item.inputType === 'autoComplete'">
|
||||||
{{ opt.raw.value }}
|
<a-auto-complete
|
||||||
<a-tooltip :content="t(opt.raw.desc)" position="bl" :mouse-enter-delay="300">
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
<div class="one-line-text max-w-full text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
:data="item.autoCompleteParams?.filter((e) => e.isShow === true)"
|
||||||
{{ t(opt.raw.desc) }}
|
class="ms-form-table-input"
|
||||||
|
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
|
||||||
|
:filter-option="false"
|
||||||
|
size="small"
|
||||||
|
@search="(val) => handleSearchParams(val, item)"
|
||||||
|
@change="() => handleFormChange(record, rowIndex, item)"
|
||||||
|
@select="(val) => selectAutoComplete(val, record, item)"
|
||||||
|
>
|
||||||
|
<template #option="{ data: opt }">
|
||||||
|
<div class="w-[350px]">
|
||||||
|
{{ opt.raw.value }}
|
||||||
|
<a-tooltip :content="t(opt.raw.desc)" position="bl" :mouse-enter-delay="300">
|
||||||
|
<div class="one-line-text max-w-full text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
||||||
|
{{ t(opt.raw.desc) }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</template>
|
||||||
|
</a-auto-complete>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.inputType === 'text'">
|
||||||
|
{{
|
||||||
|
typeof item.valueFormat === 'function'
|
||||||
|
? item.valueFormat(record)
|
||||||
|
: record[item.dataIndex as string] || '-'
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template v-else-if="item.dataIndex === 'action'">
|
||||||
|
<div
|
||||||
|
:key="item.dataIndex"
|
||||||
|
class="flex flex-row items-center"
|
||||||
|
:class="{ 'justify-end': item.align === 'right' }"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
name="operationPre"
|
||||||
|
v-bind="{ record, rowIndex, column, dataIndex: item.dataIndex, columnConfig: item }"
|
||||||
|
></slot>
|
||||||
|
<MsTableMoreAction
|
||||||
|
v-if="item.moreAction"
|
||||||
|
:list="getMoreActionList(item.moreAction, record)"
|
||||||
|
@select="(e) => handleMoreActionSelect(e, record, item, rowIndex)"
|
||||||
|
/>
|
||||||
|
<icon-minus-circle
|
||||||
|
v-if="dataLength > 1 && rowIndex !== dataLength - 1"
|
||||||
|
class="ml-[8px] cursor-pointer text-[var(--color-text-4)]"
|
||||||
|
size="20"
|
||||||
|
@click="deleteParam(record, rowIndex)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-auto-complete>
|
</slot>
|
||||||
</template>
|
</a-form-item>
|
||||||
<template v-else-if="item.inputType === 'text'">
|
</template>
|
||||||
{{
|
<template
|
||||||
typeof item.valueFormat === 'function' ? item.valueFormat(record) : record[item.dataIndex as string] || '-'
|
v-for="item of props.columns.filter((e) => e.titleSlotName !== undefined)"
|
||||||
}}
|
#[item.titleSlotName!]="{ record, rowIndex, column }"
|
||||||
</template>
|
|
||||||
<template v-else-if="item.dataIndex === 'action'">
|
|
||||||
<div
|
|
||||||
:key="item.dataIndex"
|
|
||||||
class="flex flex-row items-center"
|
|
||||||
:class="{ 'justify-end': item.align === 'right' }"
|
|
||||||
>
|
|
||||||
<slot
|
|
||||||
name="operationPre"
|
|
||||||
v-bind="{ record, rowIndex, column, dataIndex: item.dataIndex, columnConfig: item }"
|
|
||||||
></slot>
|
|
||||||
<MsTableMoreAction
|
|
||||||
v-if="item.moreAction"
|
|
||||||
:list="getMoreActionList(item.moreAction, record)"
|
|
||||||
@select="(e) => handleMoreActionSelect(e, record, item, rowIndex)"
|
|
||||||
/>
|
|
||||||
<icon-minus-circle
|
|
||||||
v-if="dataLength > 1 && rowIndex !== dataLength - 1"
|
|
||||||
class="ml-[8px] cursor-pointer text-[var(--color-text-4)]"
|
|
||||||
size="20"
|
|
||||||
@click="deleteParam(record, rowIndex)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
v-for="item of props.columns.filter((e) => e.titleSlotName !== undefined)"
|
|
||||||
#[item.titleSlotName!]="{ record, rowIndex, column }"
|
|
||||||
>
|
|
||||||
<slot
|
|
||||||
:name="item.titleSlotName"
|
|
||||||
v-bind="{ record, rowIndex, column, dataIndex: item.dataIndex, columnConfig: item }"
|
|
||||||
>
|
>
|
||||||
</slot>
|
<slot
|
||||||
</template>
|
:name="item.titleSlotName"
|
||||||
</MsBaseTable>
|
v-bind="{ record, rowIndex, column, dataIndex: item.dataIndex, columnConfig: item }"
|
||||||
|
>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</MsBaseTable>
|
||||||
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { FormInstance } from '@arco-design/web-vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
@ -167,6 +187,7 @@
|
||||||
required?: boolean; // 是否必填
|
required?: boolean; // 是否必填
|
||||||
inputType?: 'input' | 'select' | 'tags' | 'switch' | 'text' | 'checkbox' | 'autoComplete'; // 输入组件类型
|
inputType?: 'input' | 'select' | 'tags' | 'switch' | 'text' | 'checkbox' | 'autoComplete'; // 输入组件类型
|
||||||
autoCompleteParams?: SelectOptionData[]; // 自动补全参数
|
autoCompleteParams?: SelectOptionData[]; // 自动补全参数
|
||||||
|
needValidRepeat?: boolean; // 是否需要判断重复
|
||||||
valueFormat?: (record: Record<string, any>) => string; // 展示值格式化,仅在inputType为text时生效
|
valueFormat?: (record: Record<string, any>) => string; // 展示值格式化,仅在inputType为text时生效
|
||||||
[key: string]: any; // 扩展属性
|
[key: string]: any; // 扩展属性
|
||||||
}
|
}
|
||||||
|
@ -258,6 +279,35 @@
|
||||||
|
|
||||||
const dataLength = computed(() => propsRes.value.data.length);
|
const dataLength = computed(() => propsRes.value.data.length);
|
||||||
|
|
||||||
|
// 校验重复
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
async function validRepeat(rowIndex: number, dataIndex: string, value: any, callback: (error?: string) => void) {
|
||||||
|
const currentColumn = props.columns.find((item) => item.dataIndex === dataIndex);
|
||||||
|
if (!currentColumn?.needValidRepeat) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
propsRes.value.data?.forEach((row, index) => {
|
||||||
|
if (row[dataIndex].length && index !== rowIndex && row[dataIndex] === value) {
|
||||||
|
callback(`${t(currentColumn?.title as string)}${t('msFormTable.paramRepeatMessage')}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验并更新错误信息列表
|
||||||
|
const setErrorMessageList: ((params: string[]) => void) | undefined = inject('setErrorMessageList', undefined);
|
||||||
|
const errorMessageList = ref<string[]>([]); // 错误信息列表
|
||||||
|
async function validateAndUpdateErrorMessageList() {
|
||||||
|
if (typeof setErrorMessageList === 'function' && props.columns.some((item) => item.needValidRepeat)) {
|
||||||
|
await nextTick();
|
||||||
|
formRef.value?.validate((errors) => {
|
||||||
|
errorMessageList.value = !errors ? [] : [...new Set(Object.values(errors).map(({ message }) => message))];
|
||||||
|
setErrorMessageList(errorMessageList.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => selectedKeys.value,
|
() => selectedKeys.value,
|
||||||
(arr) => {
|
(arr) => {
|
||||||
|
@ -276,6 +326,7 @@
|
||||||
() => props.data,
|
() => props.data,
|
||||||
(arr) => {
|
(arr) => {
|
||||||
propsRes.value.data = arr as any[];
|
propsRes.value.data = arr as any[];
|
||||||
|
validateAndUpdateErrorMessageList();
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
@ -468,4 +519,20 @@
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
color: var(--color-text-1);
|
color: var(--color-text-1);
|
||||||
}
|
}
|
||||||
|
:deep(.arco-form-item) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
.arco-form-item-label-col {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.arco-form-item-content,
|
||||||
|
.arco-form-item-wrapper-col {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
.arco-form-item-message {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.arco-form-item-content-flex {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
'msFormTable.paramRequired': '必填',
|
'msFormTable.paramRequired': '必填',
|
||||||
'msFormTable.paramNotRequired': '非必填',
|
'msFormTable.paramNotRequired': '非必填',
|
||||||
|
'msFormTable.paramRepeatMessage': '唯一,不能重复',
|
||||||
};
|
};
|
||||||
|
|
|
@ -331,6 +331,7 @@
|
||||||
(e: 'clearSelector'): void;
|
(e: 'clearSelector'): void;
|
||||||
(e: 'filterChange', dataIndex: string, value: (string | number)[], multiple: boolean, isCustomParam: boolean): void;
|
(e: 'filterChange', dataIndex: string, value: (string | number)[], multiple: boolean, isCustomParam: boolean): void;
|
||||||
(e: 'moduleChange'): void;
|
(e: 'moduleChange'): void;
|
||||||
|
(e: 'initEnd'): void;
|
||||||
}>();
|
}>();
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
|
|
||||||
|
@ -408,6 +409,7 @@
|
||||||
scrollObj.value = {};
|
scrollObj.value = {};
|
||||||
currentColumns.value = arr || tmpArr;
|
currentColumns.value = arr || tmpArr;
|
||||||
}
|
}
|
||||||
|
emit('initEnd');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('InitColumn failed', error);
|
console.error('InitColumn failed', error);
|
||||||
|
|
|
@ -208,6 +208,7 @@
|
||||||
{
|
{
|
||||||
title: 'apiTestDebug.paramName',
|
title: 'apiTestDebug.paramName',
|
||||||
dataIndex: 'key',
|
dataIndex: 'key',
|
||||||
|
needValidRepeat: true,
|
||||||
slotName: 'key',
|
slotName: 'key',
|
||||||
width: 240,
|
width: 240,
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
dataIndex: 'key',
|
dataIndex: 'key',
|
||||||
slotName: 'key',
|
slotName: 'key',
|
||||||
inputType: 'autoComplete',
|
inputType: 'autoComplete',
|
||||||
|
needValidRepeat: true,
|
||||||
autoCompleteParams: responseHeaderOption,
|
autoCompleteParams: responseHeaderOption,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -496,6 +496,12 @@
|
||||||
() => import('@/views/api-test/management/components/addDependencyDrawer.vue')
|
() => import('@/views/api-test/management/components/addDependencyDrawer.vue')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export interface TabErrorMessage {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
messageList: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface RequestCustomAttr {
|
export interface RequestCustomAttr {
|
||||||
type: 'api' | 'case' | 'mock' | 'doc'; // 展示的请求 tab 类型;api包含了接口调试和接口定义
|
type: 'api' | 'case' | 'mock' | 'doc'; // 展示的请求 tab 类型;api包含了接口调试和接口定义
|
||||||
isNew: boolean;
|
isNew: boolean;
|
||||||
|
@ -505,6 +511,9 @@
|
||||||
executeLoading: boolean; // 执行中loading
|
executeLoading: boolean; // 执行中loading
|
||||||
isCopy?: boolean; // 是否是复制
|
isCopy?: boolean; // 是否是复制
|
||||||
isExecute?: boolean; // 是否是执行
|
isExecute?: boolean; // 是否是执行
|
||||||
|
errorMessageInfo?: {
|
||||||
|
[key: string]: Record<string, any>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export type RequestParam = ExecuteApiRequestFullParams & {
|
export type RequestParam = ExecuteApiRequestFullParams & {
|
||||||
responseDefinition?: ResponseItem[];
|
responseDefinition?: ResponseItem[];
|
||||||
|
@ -1346,6 +1355,59 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initErrorMessageInfoItem(key) {
|
||||||
|
if (requestVModel.value.errorMessageInfo && !requestVModel.value.errorMessageInfo[key]) {
|
||||||
|
requestVModel.value.errorMessageInfo[key] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
// TODO: 处理前置的sql 后置的sql和提取 断言的响应头和变量
|
||||||
|
if (requestVModel.value.activeTab === RequestComposition.BODY) {
|
||||||
|
initErrorMessageInfoItem(RequestComposition.BODY);
|
||||||
|
requestVModel.value.errorMessageInfo[RequestComposition.BODY][requestVModel.value.body.bodyType] =
|
||||||
|
cloneDeep(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.values(errorMessageInfo).forEach((item) => {
|
||||||
|
const label = item.label || (Object.values(item)[0] && Object.values(item)[0].label);
|
||||||
|
const messageList: string[] =
|
||||||
|
item.messageList || [...new Set(Object.values(item).flatMap((bodyType) => bodyType.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 handleSave(done: (closed: boolean) => void) {
|
function handleSave(done: (closed: boolean) => void) {
|
||||||
saveModalFormRef.value?.validate(async (errors) => {
|
saveModalFormRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
|
@ -1368,6 +1430,11 @@
|
||||||
// 插件需要校验动态表单
|
// 插件需要校验动态表单
|
||||||
await fApi.value?.validate();
|
await fApi.value?.validate();
|
||||||
}
|
}
|
||||||
|
// 检查全部的校验信息
|
||||||
|
if (getFlattenedMessages()?.length) {
|
||||||
|
showMessage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!requestVModel.value.isNew) {
|
if (!requestVModel.value.isNew) {
|
||||||
// 更新接口不需要弹窗,直接更新保存
|
// 更新接口不需要弹窗,直接更新保存
|
||||||
updateRequest();
|
updateRequest();
|
||||||
|
@ -1487,6 +1554,11 @@
|
||||||
if (errors) {
|
if (errors) {
|
||||||
requestVModel.value.activeTab = RequestComposition.BASE_INFO;
|
requestVModel.value.activeTab = RequestComposition.BASE_INFO;
|
||||||
} else {
|
} else {
|
||||||
|
// 检查全部的校验信息
|
||||||
|
if (getFlattenedMessages()?.length) {
|
||||||
|
showMessage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'save':
|
case 'save':
|
||||||
handleSaveShortcut();
|
handleSaveShortcut();
|
||||||
|
@ -1565,6 +1637,8 @@
|
||||||
execute,
|
execute,
|
||||||
makeRequestParams,
|
makeRequestParams,
|
||||||
changeVerticalExpand,
|
changeVerticalExpand,
|
||||||
|
getFlattenedMessages,
|
||||||
|
showMessage,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
title: 'apiTestDebug.paramName',
|
title: 'apiTestDebug.paramName',
|
||||||
dataIndex: 'key',
|
dataIndex: 'key',
|
||||||
slotName: 'key',
|
slotName: 'key',
|
||||||
|
needValidRepeat: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'apiTestDebug.paramType',
|
title: 'apiTestDebug.paramType',
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
title: 'apiTestDebug.paramName',
|
title: 'apiTestDebug.paramName',
|
||||||
dataIndex: 'key',
|
dataIndex: 'key',
|
||||||
slotName: 'key',
|
slotName: 'key',
|
||||||
|
needValidRepeat: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'apiTestDebug.paramType',
|
title: 'apiTestDebug.paramType',
|
||||||
|
|
|
@ -212,6 +212,7 @@
|
||||||
response: cloneDeep(defaultResponse),
|
response: cloneDeep(defaultResponse),
|
||||||
isNew: true,
|
isNew: true,
|
||||||
executeLoading: false,
|
executeLoading: false,
|
||||||
|
errorMessageInfo: {},
|
||||||
};
|
};
|
||||||
const debugTabs = ref<RequestParam[]>([cloneDeep(defaultDebugParams)]);
|
const debugTabs = ref<RequestParam[]>([cloneDeep(defaultDebugParams)]);
|
||||||
const activeDebug = ref<RequestParam>(debugTabs.value[0]);
|
const activeDebug = ref<RequestParam>(debugTabs.value[0]);
|
||||||
|
|
|
@ -275,6 +275,7 @@
|
||||||
executeLoading: false,
|
executeLoading: false,
|
||||||
preDependency: [], // 前置依赖
|
preDependency: [], // 前置依赖
|
||||||
postDependency: [], // 后置依赖
|
postDependency: [], // 后置依赖
|
||||||
|
errorMessageInfo: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function addApiTab(defaultProps?: Partial<TabItem>) {
|
function addApiTab(defaultProps?: Partial<TabItem>) {
|
||||||
|
|
|
@ -237,6 +237,11 @@
|
||||||
function handleDrawerConfirm(isContinue: boolean) {
|
function handleDrawerConfirm(isContinue: boolean) {
|
||||||
formRef.value?.validate(async (errors) => {
|
formRef.value?.validate(async (errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
|
// 检查全部的校验信息
|
||||||
|
if (requestCompositionRef.value?.getFlattenedMessages()?.length) {
|
||||||
|
requestCompositionRef.value?.showMessage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
drawerLoading.value = true;
|
drawerLoading.value = true;
|
||||||
// 给后端传的参数
|
// 给后端传的参数
|
||||||
if (!requestCompositionRef.value?.makeRequestParams()) return;
|
if (!requestCompositionRef.value?.makeRequestParams()) return;
|
||||||
|
|
|
@ -205,6 +205,7 @@
|
||||||
executeLoading: false,
|
executeLoading: false,
|
||||||
preDependency: [], // 前置依赖
|
preDependency: [], // 前置依赖
|
||||||
postDependency: [], // 后置依赖
|
postDependency: [], // 后置依赖
|
||||||
|
errorMessageInfo: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听模块树的激活节点变化,记录表格数据的模块 id
|
// 监听模块树的激活节点变化,记录表格数据的模块 id
|
||||||
|
|
Loading…
Reference in New Issue