fix(全局): bug修复
This commit is contained in:
parent
40dded74b3
commit
43877c59a6
|
@ -218,7 +218,7 @@
|
||||||
};
|
};
|
||||||
fileSaveAsSourceId?: string | number; // 文件转存关联的资源id
|
fileSaveAsSourceId?: string | number; // 文件转存关联的资源id
|
||||||
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; // 文件转存接口
|
fileSaveAsApi?: (params: TransferFileParams) => Promise<string>; // 文件转存接口
|
||||||
fileModuleOptionsApi?: (...args) => Promise<any>; // 文件转存目录下拉框接口
|
fileModuleOptionsApi?: (...args: any[]) => Promise<any>; // 文件转存目录下拉框接口
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
mode: 'button',
|
mode: 'button',
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
</MsButton>
|
</MsButton>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="arco-table-filters-content">
|
<div class="arco-table-filters-content">
|
||||||
<div class="ml-[6px] flex items-center justify-start px-[6px] py-[2px]">
|
<div class="py-[2px]] flex w-full items-center justify-start overflow-hidden px-[12px]">
|
||||||
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
<a-checkbox-group v-model:model-value="statusFilters" direction="vertical" size="small">
|
||||||
<a-checkbox :key="CommonScriptStatusEnum.PASSED" :value="CommonScriptStatusEnum.PASSED">
|
<a-checkbox :key="CommonScriptStatusEnum.PASSED" :value="CommonScriptStatusEnum.PASSED">
|
||||||
<commonScriptStatus :status="CommonScriptStatusEnum.PASSED" />
|
<commonScriptStatus :status="CommonScriptStatusEnum.PASSED" />
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
import type { Description } from '@/components/pure/ms-description/index.vue';
|
import type { Description } from '@/components/pure/ms-description/index.vue';
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
|
||||||
import useFullScreen from '@/hooks/useFullScreen';
|
import useFullScreen, { UseFullScreen } from '@/hooks/useFullScreen';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
import { getMaxZIndexLayer } from '@/utils/dom';
|
import { getMaxZIndexLayer } from '@/utils/dom';
|
||||||
|
@ -171,6 +171,7 @@
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const visible = ref(props.visible);
|
const visible = ref(props.visible);
|
||||||
|
const fullScreen = ref<UseFullScreen>();
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
|
@ -188,12 +189,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
|
fullScreen.value?.exitFullscreen();
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
fullScreen.value?.exitFullscreen();
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
emit('close');
|
emit('close');
|
||||||
|
@ -246,7 +249,6 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fullScreen = ref();
|
|
||||||
watch(
|
watch(
|
||||||
() => visible.value,
|
() => visible.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
|
|
|
@ -796,6 +796,12 @@
|
||||||
@apply overflow-hidden;
|
@apply overflow-hidden;
|
||||||
|
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
.arco-table-filters-content-list {
|
||||||
|
@apply overflow-y-auto;
|
||||||
|
.ms-scroll-bar();
|
||||||
|
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
.arco-checkbox-group {
|
.arco-checkbox-group {
|
||||||
@apply flex w-full flex-col;
|
@apply flex w-full flex-col;
|
||||||
.arco-checkbox {
|
.arco-checkbox {
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import { mergeStyles } from '@/utils/dom';
|
import { mergeStyles } from '@/utils/dom';
|
||||||
|
|
||||||
|
export interface UseFullScreen {
|
||||||
|
isFullScreen: Ref<boolean>;
|
||||||
|
toggleFullScreen: () => void;
|
||||||
|
exitFullscreen: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全屏 hook
|
* 全屏 hook
|
||||||
* @param domRef dom ref
|
* @param domRef dom ref
|
||||||
*/
|
*/
|
||||||
export default function useFullScreen(
|
export default function useFullScreen(
|
||||||
domRef: Ref<HTMLElement | null | undefined> | HTMLElement | Element | null | undefined
|
domRef: Ref<HTMLElement | null | undefined> | HTMLElement | Element | null | undefined
|
||||||
) {
|
): UseFullScreen {
|
||||||
const isFullScreen = ref(false);
|
const isFullScreen = ref(false);
|
||||||
const originalStyle = ref('');
|
const originalStyle = ref('');
|
||||||
|
|
||||||
|
@ -41,5 +47,6 @@ export default function useFullScreen(
|
||||||
return {
|
return {
|
||||||
isFullScreen,
|
isFullScreen,
|
||||||
toggleFullScreen,
|
toggleFullScreen,
|
||||||
|
exitFullscreen,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,7 +322,7 @@ export interface ExecuteAssertionConfig {
|
||||||
}
|
}
|
||||||
// 执行请求-共用配置子项
|
// 执行请求-共用配置子项
|
||||||
export interface ExecuteCommonChild {
|
export interface ExecuteCommonChild {
|
||||||
polymorphicName: 'MsCommonElement'; // 协议多态名称,写死MsCommonElement
|
polymorphicName: string; // 协议多态名称,写死MsCommonElement
|
||||||
assertionConfig: ExecuteAssertionConfig;
|
assertionConfig: ExecuteAssertionConfig;
|
||||||
postProcessorConfig: ExecuteConditionConfig; // 后置处理器配置
|
postProcessorConfig: ExecuteConditionConfig; // 后置处理器配置
|
||||||
preProcessorConfig: ExecuteConditionConfig; // 前置处理器配置
|
preProcessorConfig: ExecuteConditionConfig; // 前置处理器配置
|
||||||
|
@ -358,7 +358,7 @@ export interface ExecuteApiRequestFullParams {
|
||||||
authConfig: ExecuteAuthConfig;
|
authConfig: ExecuteAuthConfig;
|
||||||
body: ExecuteBody;
|
body: ExecuteBody;
|
||||||
headers: EnableKeyValueParam[];
|
headers: EnableKeyValueParam[];
|
||||||
method: RequestMethods;
|
method: RequestMethods | string;
|
||||||
otherConfig: ExecuteOtherConfig;
|
otherConfig: ExecuteOtherConfig;
|
||||||
path: string;
|
path: string;
|
||||||
query: ExecuteRequestCommonParam[];
|
query: ExecuteRequestCommonParam[];
|
||||||
|
@ -409,7 +409,7 @@ export interface RequestResult {
|
||||||
body: string;
|
body: string;
|
||||||
headers: string;
|
headers: string;
|
||||||
url: string;
|
url: string;
|
||||||
method: string;
|
method: RequestMethods | string;
|
||||||
responseResult: ResponseResult;
|
responseResult: ResponseResult;
|
||||||
isSuccessful?: boolean;
|
isSuccessful?: boolean;
|
||||||
console?: string;
|
console?: string;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { ExecuteApiRequestFullParams, ExecutePluginRequestParams } from './commo
|
||||||
export interface SaveDebugParams {
|
export interface SaveDebugParams {
|
||||||
name: string;
|
name: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
method: RequestMethods;
|
method: RequestMethods | string;
|
||||||
path: string;
|
path: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
moduleId: string;
|
moduleId: string;
|
||||||
|
@ -47,7 +47,7 @@ export interface DebugDetail {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
method: string;
|
method: RequestMethods | string;
|
||||||
path: string;
|
path: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
moduleId: string;
|
moduleId: string;
|
||||||
|
|
|
@ -60,7 +60,7 @@ export interface ApiDefinitionDetail extends ApiDefinitionCreateParams {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
method: RequestMethods;
|
method: RequestMethods | string;
|
||||||
path: string;
|
path: string;
|
||||||
num: number;
|
num: number;
|
||||||
pos: number;
|
pos: number;
|
||||||
|
@ -314,7 +314,7 @@ export interface ApiCaseDetail extends ExecuteRequestParams {
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
environmentName: string;
|
environmentName: string;
|
||||||
follow: boolean;
|
follow: boolean;
|
||||||
method: RequestMethods;
|
method: RequestMethods | string;
|
||||||
path: string;
|
path: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
passRate: string;
|
passRate: string;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { RequestResult } from '@/models/apiTest/common';
|
import { RequestResult } from '@/models/apiTest/common';
|
||||||
|
import type { RequestMethods } from '@/enums/apiEnum';
|
||||||
|
|
||||||
export interface LegendData {
|
export interface LegendData {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -55,7 +56,7 @@ export interface StepContent {
|
||||||
cookies: string;
|
cookies: string;
|
||||||
body: string;
|
body: string;
|
||||||
status: string;
|
status: string;
|
||||||
method: string;
|
method: RequestMethods | string;
|
||||||
assertionTotal: number;
|
assertionTotal: number;
|
||||||
passAssertionsTotal: number;
|
passAssertionsTotal: number;
|
||||||
subRequestResults: ResponseResult[];
|
subRequestResults: ResponseResult[];
|
||||||
|
|
|
@ -62,7 +62,7 @@ export interface ApiScenarioScheduleConfig {
|
||||||
export interface ApiScenarioTableItem {
|
export interface ApiScenarioTableItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
method: string;
|
method: RequestMethods | string;
|
||||||
path: string;
|
path: string;
|
||||||
num: number;
|
num: number;
|
||||||
pos: number;
|
pos: number;
|
||||||
|
@ -328,7 +328,7 @@ export type ScenarioStepDetail = Partial<
|
||||||
LoopStepDetail &
|
LoopStepDetail &
|
||||||
ScenarioStepConfig & {
|
ScenarioStepConfig & {
|
||||||
protocol: string;
|
protocol: string;
|
||||||
method: RequestMethods;
|
method: RequestMethods | string;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
// 场景步骤项
|
// 场景步骤项
|
||||||
|
@ -367,13 +367,13 @@ export interface ScenarioStepItem {
|
||||||
}
|
}
|
||||||
// 场景步骤文件参数
|
// 场景步骤文件参数
|
||||||
export interface ScenarioStepFileParams {
|
export interface ScenarioStepFileParams {
|
||||||
uploadFileIds: string[];
|
uploadFileIds?: string[];
|
||||||
linkFileIds: string[];
|
linkFileIds?: string[];
|
||||||
deleteFileIds?: string[];
|
deleteFileIds?: string[];
|
||||||
unLinkFileIds?: string[];
|
unLinkFileIds?: string[];
|
||||||
}
|
}
|
||||||
// 场景步骤详情
|
// 场景步骤详情
|
||||||
export type ScenarioStepDetails = RequestParam | CaseRequestParam | ExecuteConditionProcessor;
|
export type ScenarioStepDetails = Partial<RequestParam | CaseRequestParam | ExecuteConditionProcessor>;
|
||||||
// 场景
|
// 场景
|
||||||
export interface Scenario {
|
export interface Scenario {
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
|
|
|
@ -137,22 +137,49 @@
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
<!-- 接口调试,支持快捷保存 -->
|
<!-- 接口调试,支持快捷保存 -->
|
||||||
<a-button
|
<template
|
||||||
v-else-if="
|
v-else-if="
|
||||||
requestVModel.isNew
|
requestVModel.isNew
|
||||||
? props.permissionMap && hasAnyPermission([props.permissionMap.create])
|
? props.permissionMap && hasAnyPermission([props.permissionMap.create])
|
||||||
: props.permissionMap && hasAnyPermission([props.permissionMap.update])
|
: props.permissionMap && hasAnyPermission([props.permissionMap.update])
|
||||||
"
|
"
|
||||||
type="secondary"
|
|
||||||
:disabled="isHttpProtocol && !requestVModel.url"
|
|
||||||
:loading="saveLoading"
|
|
||||||
@click="handleSaveShortcut"
|
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<!-- 接口调试-可保存或保存为新接口定义 -->
|
||||||
{{ t('common.save') }}
|
<a-dropdown-button
|
||||||
<div class="text-[var(--color-text-4)]">(<icon-command size="14" />+S)</div>
|
v-if="
|
||||||
</div>
|
props.permissionMap &&
|
||||||
</a-button>
|
props.permissionMap.saveASApi &&
|
||||||
|
hasAllPermission([props.permissionMap.create, props.permissionMap.saveASApi])
|
||||||
|
"
|
||||||
|
:disabled="(isHttpProtocol && !requestVModel.url) || saveLoading"
|
||||||
|
@click="handleSaveShortcut"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
<div class="text-[var(--color-text-4)]">(<icon-command size="14" />+S)</div>
|
||||||
|
</div>
|
||||||
|
<template #icon>
|
||||||
|
<icon-down />
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<a-doption value="saveAsApi" @click="() => handleSelect('saveAsApi')">
|
||||||
|
{{ t('apiTestDebug.saveAsApi') }}
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown-button>
|
||||||
|
<a-button
|
||||||
|
v-else
|
||||||
|
type="secondary"
|
||||||
|
:disabled="isHttpProtocol && !requestVModel.url"
|
||||||
|
:loading="saveLoading"
|
||||||
|
@click="handleSaveShortcut"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
{{ t('common.save') }}
|
||||||
|
<div class="text-[var(--color-text-4)]">(<icon-command size="14" />+S)</div>
|
||||||
|
</div>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -412,6 +439,11 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
<saveAsApiModal
|
||||||
|
v-if="tempApiDetail"
|
||||||
|
v-model:visible="saveNewApiModalVisible"
|
||||||
|
:detail="tempApiDetail"
|
||||||
|
></saveAsApiModal>
|
||||||
<addDependencyDrawer
|
<addDependencyDrawer
|
||||||
v-if="props.isDefinition"
|
v-if="props.isDefinition"
|
||||||
v-model:visible="showAddDependencyDrawer"
|
v-model:visible="showAddDependencyDrawer"
|
||||||
|
@ -439,6 +471,7 @@
|
||||||
import setting from './setting.vue';
|
import setting from './setting.vue';
|
||||||
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 saveAsApiModal from '@/views/api-test/components/saveAsApiModal.vue';
|
||||||
import apiBaseForm from '@/views/api-test/management/components/management/api/apiBaseForm.vue';
|
import apiBaseForm from '@/views/api-test/management/components/management/api/apiBaseForm.vue';
|
||||||
|
|
||||||
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
|
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
|
||||||
|
@ -451,7 +484,7 @@
|
||||||
import { filterTree, getGenerateId, parseQueryParams } from '@/utils';
|
import { filterTree, getGenerateId, parseQueryParams } from '@/utils';
|
||||||
import { scrollIntoView } from '@/utils/dom';
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
import { registerCatchSaveShortcut, removeCatchSaveShortcut } from '@/utils/event';
|
import { registerCatchSaveShortcut, removeCatchSaveShortcut } from '@/utils/event';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ExecuteApiRequestFullParams,
|
ExecuteApiRequestFullParams,
|
||||||
|
@ -462,7 +495,6 @@
|
||||||
} from '@/models/apiTest/common';
|
} from '@/models/apiTest/common';
|
||||||
import { AddApiCaseParams } from '@/models/apiTest/management';
|
import { AddApiCaseParams } from '@/models/apiTest/management';
|
||||||
import { ModuleTreeNode, TransferFileParams } from '@/models/common';
|
import { ModuleTreeNode, TransferFileParams } from '@/models/common';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
RequestBodyFormat,
|
RequestBodyFormat,
|
||||||
|
@ -542,6 +574,7 @@
|
||||||
execute: string;
|
execute: string;
|
||||||
create: string;
|
create: string;
|
||||||
update: string;
|
update: string;
|
||||||
|
saveASApi?: string;
|
||||||
};
|
};
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits(['addDone', 'execute']);
|
const emit = defineEmits(['addDone', 'execute']);
|
||||||
|
@ -846,7 +879,7 @@
|
||||||
initPluginScript();
|
initPluginScript();
|
||||||
} else {
|
} else {
|
||||||
requestVModel.value.activeTab = RequestComposition.HEADER;
|
requestVModel.value.activeTab = RequestComposition.HEADER;
|
||||||
if (!Object.values(RequestMethods).includes(requestVModel.value.method)) {
|
if (!Object.values(RequestMethods).includes(requestVModel.value.method as RequestMethods)) {
|
||||||
// 第三方插件协议切换到 HTTP 时,请求方法默认设置 GET
|
// 第三方插件协议切换到 HTTP 时,请求方法默认设置 GET
|
||||||
requestVModel.value.method = RequestMethods.GET;
|
requestVModel.value.method = RequestMethods.GET;
|
||||||
}
|
}
|
||||||
|
@ -1562,12 +1595,25 @@
|
||||||
|
|
||||||
const apiBaseFormRef = ref<InstanceType<typeof apiBaseForm>>();
|
const apiBaseFormRef = ref<InstanceType<typeof apiBaseForm>>();
|
||||||
const isUrlError = ref(false);
|
const isUrlError = ref(false);
|
||||||
|
const tempApiDetail = ref<RequestParam>();
|
||||||
|
const saveNewApiModalVisible = ref(false);
|
||||||
|
|
||||||
function handleSelect(value: string | number | Record<string, any> | undefined) {
|
function handleSelect(value: string | number | Record<string, any> | undefined) {
|
||||||
if (requestVModel.value.url === '' && requestVModel.value.protocol === 'HTTP') {
|
if (requestVModel.value.url === '' && requestVModel.value.protocol === 'HTTP') {
|
||||||
isUrlError.value = true;
|
isUrlError.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isUrlError.value = false;
|
isUrlError.value = false;
|
||||||
|
if (value === 'saveAsApi') {
|
||||||
|
const params = makeRequestParams();
|
||||||
|
tempApiDetail.value = {
|
||||||
|
...params,
|
||||||
|
...params.request,
|
||||||
|
polymorphicName: params.request.polymorphicName,
|
||||||
|
};
|
||||||
|
saveNewApiModalVisible.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
apiBaseFormRef.value?.formRef?.validate(async (errors) => {
|
apiBaseFormRef.value?.formRef?.validate(async (errors) => {
|
||||||
if (errors) {
|
if (errors) {
|
||||||
requestVModel.value.activeTab = RequestComposition.BASE_INFO;
|
requestVModel.value.activeTab = RequestComposition.BASE_INFO;
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:title="t('apiTestDebug.saveAsApi')"
|
||||||
|
class="ms-modal-form"
|
||||||
|
title-align="start"
|
||||||
|
body-class="!p-0"
|
||||||
|
>
|
||||||
|
<a-form ref="saveModalFormRef" :model="saveModalForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
field="name"
|
||||||
|
:label="t('apiTestDebug.requestName')"
|
||||||
|
:rules="[{ required: true, message: t('apiTestDebug.requestNameRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="saveModalForm.name"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('apiTestDebug.requestNamePlaceholder')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="props.detail.protocol === 'HTTP'"
|
||||||
|
field="path"
|
||||||
|
:label="t('apiTestDebug.requestUrl')"
|
||||||
|
:rules="[{ required: true, message: t('apiTestDebug.requestUrlRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="saveModalForm.path"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('apiTestDebug.requestModule')" class="mb-0">
|
||||||
|
<a-tree-select
|
||||||
|
v-model:modelValue="saveModalForm.moduleId"
|
||||||
|
:data="apiModuleTree"
|
||||||
|
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||||
|
:tree-props="{
|
||||||
|
virtualListProps: {
|
||||||
|
height: 200,
|
||||||
|
threshold: 200,
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
allow-search
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-[4px]">
|
||||||
|
<a-checkbox v-model:model-value="saveModalForm.saveApiAsCase"></a-checkbox>
|
||||||
|
{{ t('apiScenario.syncSaveAsCase') }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-[12px]">
|
||||||
|
<a-button type="secondary" :disabled="saveLoading" @click="handleSaveApiCancel">
|
||||||
|
{{ t('common.cancel') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" :loading="saveLoading" @click="handleSaveApi">{{ t('common.confirm') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
|
|
||||||
|
import { addCase, addDefinition, getModuleTreeOnlyModules } from '@/api/modules/api-test/management';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
|
import { AddApiCaseParams } from '@/models/apiTest/management';
|
||||||
|
import { RequestCaseStatus, RequestDefinitionStatus } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
import { defaultResponseItem } from '@/views/api-test/components/config';
|
||||||
|
import type { RequestParam as ApiDefinitionRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
import type { RequestParam } from '@/views/api-test/scenario/components/common/customApiDrawer.vue';
|
||||||
|
import type { FormInstance } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
detail: RequestParam | ApiDefinitionRequestParam;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const visible = defineModel<boolean>('visible', {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
const saveModalForm = ref({
|
||||||
|
name: '',
|
||||||
|
path: '',
|
||||||
|
moduleId: 'root',
|
||||||
|
saveApiAsCase: false,
|
||||||
|
});
|
||||||
|
const saveModalFormRef = ref<FormInstance>();
|
||||||
|
const saveLoading = ref(false);
|
||||||
|
|
||||||
|
const apiModuleTree = ref<MsTreeNodeData[]>([]);
|
||||||
|
async function initApiModuleTree(protocol: string) {
|
||||||
|
try {
|
||||||
|
apiModuleTree.value = await getModuleTreeOnlyModules({
|
||||||
|
keyword: '',
|
||||||
|
protocol,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: [],
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveApiAsCase(id: string) {
|
||||||
|
let url;
|
||||||
|
let path = '';
|
||||||
|
try {
|
||||||
|
url = new URL(saveModalForm.value.path);
|
||||||
|
path = url.pathname + url.search + url.hash;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
path = saveModalForm.value.path;
|
||||||
|
}
|
||||||
|
const params: AddApiCaseParams = {
|
||||||
|
name: saveModalForm.value.name,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
environmentId: appStore.currentEnvConfig?.id || '',
|
||||||
|
apiDefinitionId: id,
|
||||||
|
request: {
|
||||||
|
...props.detail,
|
||||||
|
url: path,
|
||||||
|
},
|
||||||
|
priority: 'P0',
|
||||||
|
status: RequestCaseStatus.PROCESSING,
|
||||||
|
tags: [],
|
||||||
|
uploadFileIds: props.detail.uploadFileIds || [],
|
||||||
|
linkFileIds: props.detail.linkFileIds || [],
|
||||||
|
};
|
||||||
|
await addCase(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存请求
|
||||||
|
* @param isSaveCase 是否需要保存用例
|
||||||
|
*/
|
||||||
|
async function realSaveAsApi() {
|
||||||
|
try {
|
||||||
|
saveLoading.value = true;
|
||||||
|
let url;
|
||||||
|
let path = '';
|
||||||
|
try {
|
||||||
|
url = new URL(saveModalForm.value.path);
|
||||||
|
path = url.pathname + url.search + url.hash;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
path = saveModalForm.value.path;
|
||||||
|
}
|
||||||
|
const res = await addDefinition({
|
||||||
|
...saveModalForm.value,
|
||||||
|
path,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
tags: [],
|
||||||
|
description: '',
|
||||||
|
status: RequestDefinitionStatus.PROCESSING,
|
||||||
|
customFields: [],
|
||||||
|
versionId: '',
|
||||||
|
environmentId: appStore.currentEnvConfig?.id || '',
|
||||||
|
request: {
|
||||||
|
...props.detail,
|
||||||
|
url: path,
|
||||||
|
path,
|
||||||
|
},
|
||||||
|
uploadFileIds: props.detail.uploadFileIds || [],
|
||||||
|
linkFileIds: props.detail.linkFileIds || [],
|
||||||
|
response: [defaultResponseItem],
|
||||||
|
method: props.detail.method,
|
||||||
|
protocol: props.detail.protocol,
|
||||||
|
});
|
||||||
|
if (saveModalForm.value.saveApiAsCase) {
|
||||||
|
await saveApiAsCase(res.id);
|
||||||
|
}
|
||||||
|
Message.success(t('common.saveSuccess'));
|
||||||
|
visible.value = false;
|
||||||
|
saveLoading.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
saveLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSaveApiCancel() {
|
||||||
|
saveModalFormRef.value?.resetFields();
|
||||||
|
saveModalForm.value.saveApiAsCase = false;
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSaveApi() {
|
||||||
|
saveModalFormRef.value?.validate(async (errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
await realSaveAsApi();
|
||||||
|
handleSaveApiCancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
saveModalForm.value.path = props.detail.url || props.detail.path;
|
||||||
|
initApiModuleTree(props.detail.protocol);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -51,6 +51,7 @@
|
||||||
execute: 'PROJECT_API_DEBUG:READ+EXECUTE',
|
execute: 'PROJECT_API_DEBUG:READ+EXECUTE',
|
||||||
update: 'PROJECT_API_DEBUG:READ+UPDATE',
|
update: 'PROJECT_API_DEBUG:READ+UPDATE',
|
||||||
create: 'PROJECT_API_DEBUG:READ+ADD',
|
create: 'PROJECT_API_DEBUG:READ+ADD',
|
||||||
|
saveASApi: 'PROJECT_API_DEFINITION:READ+ADD',
|
||||||
}"
|
}"
|
||||||
@add-done="handleDebugAddDone"
|
@add-done="handleDebugAddDone"
|
||||||
/>
|
/>
|
||||||
|
@ -91,6 +92,64 @@
|
||||||
</MsCodeEditor>
|
</MsCodeEditor>
|
||||||
</div>
|
</div>
|
||||||
</MsDrawer>
|
</MsDrawer>
|
||||||
|
<!-- <a-modal
|
||||||
|
v-model:visible="saveAsApiModalVisible"
|
||||||
|
:title="t('common.save')"
|
||||||
|
:ok-loading="saveLoading"
|
||||||
|
class="ms-modal-form"
|
||||||
|
title-align="start"
|
||||||
|
body-class="!p-0"
|
||||||
|
@before-ok="handleSave"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form ref="saveModalFormRef" :model="saveModalForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
field="name"
|
||||||
|
:label="t('apiTestDebug.requestName')"
|
||||||
|
:rules="[{ required: true, message: t('apiTestDebug.requestNameRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="saveModalForm.name"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('apiTestDebug.requestNamePlaceholder')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="isHttpProtocol"
|
||||||
|
field="path"
|
||||||
|
:label="t('apiTestDebug.requestUrl')"
|
||||||
|
:rules="[{ required: true, message: t('apiTestDebug.requestUrlRequired') }]"
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="saveModalForm.path"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('apiTestDebug.requestModule')" class="mb-0">
|
||||||
|
<a-tree-select
|
||||||
|
v-model:modelValue="saveModalForm.moduleId"
|
||||||
|
:data="apiModules as ModuleTreeNode[]"
|
||||||
|
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
||||||
|
:tree-props="{
|
||||||
|
virtualListProps: {
|
||||||
|
height: 200,
|
||||||
|
threshold: 200,
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
allow-search
|
||||||
|
>
|
||||||
|
<template #tree-slot-title="node">
|
||||||
|
<a-tooltip :content="`${node.name}`" position="tl">
|
||||||
|
<div class="one-line-text w-[300px] text-[var(--color-text-1)]">{{ node.name }}</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-tree-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -107,6 +166,7 @@
|
||||||
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
|
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
|
||||||
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
|
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
import moduleTree from './components/moduleTree.vue';
|
import moduleTree from './components/moduleTree.vue';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import debug, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
import debug, { RequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
@ -121,8 +181,10 @@
|
||||||
updateDebug,
|
updateDebug,
|
||||||
uploadTempFile,
|
uploadTempFile,
|
||||||
} from '@/api/modules/api-test/debug';
|
} from '@/api/modules/api-test/debug';
|
||||||
|
import { getModuleTreeOnlyModules } from '@/api/modules/api-test/management';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useLeaveTabUnSaveCheck from '@/hooks/useLeaveTabUnSaveCheck';
|
import useLeaveTabUnSaveCheck from '@/hooks/useLeaveTabUnSaveCheck';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
import { parseCurlScript } from '@/utils';
|
import { parseCurlScript } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
|
@ -138,7 +200,9 @@
|
||||||
|
|
||||||
import { defaultBodyParams, defaultResponse } from '../components/config';
|
import { defaultBodyParams, defaultResponse } from '../components/config';
|
||||||
import { parseRequestBodyFiles } from '../components/utils';
|
import { parseRequestBodyFiles } from '../components/utils';
|
||||||
|
import type { FormInstance } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -354,6 +418,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveAsApiModalVisible = ref(false);
|
||||||
|
const saveModalForm = ref({
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
path: '',
|
||||||
|
moduleId: 'root',
|
||||||
|
});
|
||||||
|
const saveModalFormRef = ref<FormInstance>();
|
||||||
|
const saveLoading = ref(false);
|
||||||
|
const apiModules = ref<ModuleTreeNode[]>([]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => saveAsApiModalVisible.value,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
saveModalFormRef.value?.resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
async function openSaveAsApiModal(node: MsTreeNodeData) {
|
||||||
|
try {
|
||||||
|
const [modules] = await getModuleTreeOnlyModules({
|
||||||
|
keyword: '',
|
||||||
|
protocol: '',
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
moduleIds: [],
|
||||||
|
});
|
||||||
|
apiModules.value = modules;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (route.query.id) {
|
if (route.query.id) {
|
||||||
openApiTab(route.query.id as string);
|
openApiTab(route.query.id as string);
|
||||||
|
|
|
@ -205,4 +205,5 @@ export default {
|
||||||
'apiTestDebug.extractValueTitleTip':
|
'apiTestDebug.extractValueTitleTip':
|
||||||
'Enter the column name and corresponding value in column storage. If you want to extract the first value of the name column, enter name_1',
|
'Enter the column name and corresponding value in column storage. If you want to extract the first value of the name column, enter name_1',
|
||||||
'apiTestDebug.responseRepeatMessage': 'The name is duplicated, please re-enter it.',
|
'apiTestDebug.responseRepeatMessage': 'The name is duplicated, please re-enter it.',
|
||||||
|
'apiTestDebug.saveAsApi': 'Save as Api',
|
||||||
};
|
};
|
||||||
|
|
|
@ -191,4 +191,5 @@ export default {
|
||||||
'apiTestDebug.regexMatchRules': '表达式匹配规则',
|
'apiTestDebug.regexMatchRules': '表达式匹配规则',
|
||||||
'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取name列的第一个值则输入name_1',
|
'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取name列的第一个值则输入name_1',
|
||||||
'apiTestDebug.responseRepeatMessage': '名称重复,请重新输入',
|
'apiTestDebug.responseRepeatMessage': '名称重复,请重新输入',
|
||||||
|
'apiTestDebug.saveAsApi': '另存为接口',
|
||||||
};
|
};
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
@change="handleUrlChange"
|
@change="handleUrlChange"
|
||||||
>
|
>
|
||||||
<template v-if="showEnvPrefix" #prefix>
|
<template v-if="showEnvPrefix" #prefix>
|
||||||
{{ appStore.currentEnvConfig?.httpConfig.find((e) => e.type === 'NONE')?.url }}
|
{{ (appStore.currentEnvConfig as EnvConfig)?.httpConfig.find((e) => e.type === 'NONE')?.url }}
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
|
@ -390,13 +390,19 @@
|
||||||
import { scrollIntoView } from '@/utils/dom';
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
EnableKeyValueParam,
|
||||||
ExecuteApiRequestFullParams,
|
ExecuteApiRequestFullParams,
|
||||||
|
ExecuteBody,
|
||||||
ExecuteConditionConfig,
|
ExecuteConditionConfig,
|
||||||
|
ExecutePluginRequestParams,
|
||||||
|
ExecuteRequestCommonParam,
|
||||||
|
ExecuteRequestFormBody,
|
||||||
PluginConfig,
|
PluginConfig,
|
||||||
RequestResult,
|
RequestResult,
|
||||||
RequestTaskResult,
|
RequestTaskResult,
|
||||||
} from '@/models/apiTest/common';
|
} from '@/models/apiTest/common';
|
||||||
import { ScenarioStepFileParams, ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepFileParams, ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
|
import type { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
RequestBodyFormat,
|
RequestBodyFormat,
|
||||||
|
@ -429,7 +435,8 @@
|
||||||
// );
|
// );
|
||||||
|
|
||||||
export interface RequestCustomAttr {
|
export interface RequestCustomAttr {
|
||||||
type: 'api';
|
type?: 'api';
|
||||||
|
label: string;
|
||||||
name: string;
|
name: string;
|
||||||
stepId: string | number; // 所属步骤 id
|
stepId: string | number; // 所属步骤 id
|
||||||
resourceId: string | number; // 引用、复制的资源 id
|
resourceId: string | number; // 引用、复制的资源 id
|
||||||
|
@ -450,7 +457,7 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RequestParam = ExecuteApiRequestFullParams & {
|
export type RequestParam = (ExecuteApiRequestFullParams | ExecutePluginRequestParams) & {
|
||||||
response?: RequestTaskResult;
|
response?: RequestTaskResult;
|
||||||
customizeRequest?: boolean;
|
customizeRequest?: boolean;
|
||||||
customizeRequestEnvEnable?: boolean;
|
customizeRequestEnvEnable?: boolean;
|
||||||
|
@ -489,6 +496,7 @@
|
||||||
const loading = defineModel<boolean>('detailLoading', { default: false });
|
const loading = defineModel<boolean>('detailLoading', { default: false });
|
||||||
|
|
||||||
const defaultApiParams: RequestParam = {
|
const defaultApiParams: RequestParam = {
|
||||||
|
label: '',
|
||||||
name: '',
|
name: '',
|
||||||
type: 'api',
|
type: 'api',
|
||||||
stepId: '',
|
stepId: '',
|
||||||
|
@ -824,7 +832,7 @@
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (fApi.value) {
|
if (fApi.value) {
|
||||||
fApi.value.nextRefresh(() => {
|
fApi.value.nextRefresh(() => {
|
||||||
const form = {};
|
const form: Record<string, any> = {};
|
||||||
controlPluginFormFields().forEach((key) => {
|
controlPluginFormFields().forEach((key) => {
|
||||||
form[key] = formData[key];
|
form[key] = formData[key];
|
||||||
});
|
});
|
||||||
|
@ -891,7 +899,7 @@
|
||||||
initPluginScript();
|
initPluginScript();
|
||||||
} else {
|
} else {
|
||||||
requestVModel.value.activeTab = RequestComposition.HEADER;
|
requestVModel.value.activeTab = RequestComposition.HEADER;
|
||||||
if (!Object.values(RequestMethods).includes(requestVModel.value.method)) {
|
if (!Object.values(RequestMethods).includes(requestVModel.value.method as RequestMethods)) {
|
||||||
// 第三方插件协议切换到 HTTP 时,请求方法默认设置 GET
|
// 第三方插件协议切换到 HTTP 时,请求方法默认设置 GET
|
||||||
requestVModel.value.method = RequestMethods.GET;
|
requestVModel.value.method = RequestMethods.GET;
|
||||||
}
|
}
|
||||||
|
@ -1082,12 +1090,12 @@
|
||||||
async function execute(executeType?: 'localExec' | 'serverExec') {
|
async function execute(executeType?: 'localExec' | 'serverExec') {
|
||||||
requestVModel.value.executeLoading = true;
|
requestVModel.value.executeLoading = true;
|
||||||
if (isHttpProtocol.value) {
|
if (isHttpProtocol.value) {
|
||||||
emit('execute', makeRequestParams(executeType), executeType);
|
emit('execute', makeRequestParams(executeType) as RequestParam, executeType);
|
||||||
} else {
|
} else {
|
||||||
// 插件需要校验动态表单
|
// 插件需要校验动态表单
|
||||||
fApi.value?.validate(async (valid) => {
|
fApi.value?.validate(async (valid) => {
|
||||||
if (valid === true) {
|
if (valid === true) {
|
||||||
emit('execute', makeRequestParams(executeType), executeType);
|
emit('execute', makeRequestParams(executeType) as RequestParam, executeType);
|
||||||
} else {
|
} else {
|
||||||
requestVModel.value.activeTab = RequestComposition.PLUGIN;
|
requestVModel.value.activeTab = RequestComposition.PLUGIN;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
@ -1103,7 +1111,7 @@
|
||||||
emit('stopDebug');
|
emit('stopDebug');
|
||||||
}
|
}
|
||||||
|
|
||||||
function initErrorMessageInfoItem(key) {
|
function initErrorMessageInfoItem(key: string) {
|
||||||
if (requestVModel.value.errorMessageInfo && !requestVModel.value.errorMessageInfo[key]) {
|
if (requestVModel.value.errorMessageInfo && !requestVModel.value.errorMessageInfo[key]) {
|
||||||
requestVModel.value.errorMessageInfo[key] = {};
|
requestVModel.value.errorMessageInfo[key] = {};
|
||||||
}
|
}
|
||||||
|
@ -1181,7 +1189,7 @@
|
||||||
showMessage();
|
showMessage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit('addStep', cloneDeep(makeRequestParams()));
|
emit('addStep', cloneDeep(makeRequestParams()) as RequestParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
|
@ -1202,7 +1210,7 @@
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
// 关闭时若不是创建行为则是编辑行为,需要触发 applyStep
|
// 关闭时若不是创建行为则是编辑行为,需要触发 applyStep
|
||||||
if (!requestVModel.value.isNew) {
|
if (!requestVModel.value.isNew) {
|
||||||
emit('applyStep', cloneDeep(makeRequestParams()));
|
emit('applyStep', cloneDeep(makeRequestParams()) as RequestParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,21 +1245,28 @@
|
||||||
if (_stepType.value.isQuoteApi && props.request && isHttpProtocol.value && !props.step?.isQuoteScenarioStep) {
|
if (_stepType.value.isQuoteApi && props.request && isHttpProtocol.value && !props.step?.isQuoteScenarioStep) {
|
||||||
// 是引用 api、并且传入了请求参数、且不是插件、且不是引用场景下的步骤
|
// 是引用 api、并且传入了请求参数、且不是插件、且不是引用场景下的步骤
|
||||||
// 初始化引用的详情后,需要要把外面传入的数据的请求头、请求体、query、rest里面的参数值写入
|
// 初始化引用的详情后,需要要把外面传入的数据的请求头、请求体、query、rest里面的参数值写入
|
||||||
['headers', 'query', 'rest'].forEach((type) => {
|
(['headers', 'query', 'rest'] as (keyof RequestParam)[]).forEach((type: keyof RequestParam) => {
|
||||||
props.request?.[type]?.forEach((item) => {
|
(props.request?.[type] as (EnableKeyValueParam | ExecuteRequestCommonParam)[])?.forEach(
|
||||||
if (!item.key.length) return;
|
(item: EnableKeyValueParam) => {
|
||||||
const index = requestVModel.value[type]?.findIndex((itemReq) => itemReq.key === item.key);
|
if (!item.key.length) return;
|
||||||
if (index > -1) {
|
const index = (
|
||||||
requestVModel.value[type][index].value = item.value;
|
requestVModel.value[type] as (EnableKeyValueParam | ExecuteRequestCommonParam)[]
|
||||||
|
)?.findIndex((itemReq) => itemReq.key === item.key);
|
||||||
|
if (index > -1) {
|
||||||
|
(requestVModel.value[type] as (EnableKeyValueParam | ExecuteRequestCommonParam)[])[index].value =
|
||||||
|
item.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
['formDataBody', 'wwwFormBody'].forEach((type) => {
|
(['formDataBody', 'wwwFormBody'] as (keyof ExecuteBody)[]).forEach((type: keyof ExecuteBody) => {
|
||||||
props.request?.body?.[type].formValues.forEach((item) => {
|
(props.request?.body?.[type] as ExecuteRequestFormBody).formValues.forEach((item) => {
|
||||||
if (!item.key.length) return;
|
if (!item.key.length) return;
|
||||||
const index = requestVModel.value.body[type].formValues.findIndex((itemReq) => itemReq.key === item.key);
|
const index = (requestVModel.value.body[type] as ExecuteRequestFormBody).formValues.findIndex(
|
||||||
|
(itemReq) => itemReq.key === item.key
|
||||||
|
);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
requestVModel.value.body[type].formValues[index].value = item.value;
|
(requestVModel.value.body[type] as ExecuteRequestFormBody).formValues[index].value = item.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<div v-show="!pluginError || isHttpProtocol" class="flex h-full flex-col">
|
<div v-show="!pluginError || isHttpProtocol" class="flex h-full flex-col">
|
||||||
<div class="flex items-center gap-[16px] p-[16px] pb-[8px]">
|
<div class="flex items-center gap-[16px] p-[16px] pb-[8px]">
|
||||||
<a-input
|
<a-input
|
||||||
v-if="activeStep?.stepType && _stepType.isQuoteCase"
|
v-if="_stepType.isQuoteCase || activeStep?.isQuoteScenarioStep"
|
||||||
v-model:model-value="requestVModel.name"
|
v-model:model-value="requestVModel.name"
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
:show-word-limit="isEditableApi"
|
:show-word-limit="isEditableApi"
|
||||||
|
@ -365,6 +365,7 @@
|
||||||
const hasLocalExec = inject<Ref<boolean>>('hasLocalExec');
|
const hasLocalExec = inject<Ref<boolean>>('hasLocalExec');
|
||||||
|
|
||||||
const defaultApiParams: RequestParam = {
|
const defaultApiParams: RequestParam = {
|
||||||
|
label: '',
|
||||||
name: '',
|
name: '',
|
||||||
type: 'api',
|
type: 'api',
|
||||||
stepId: '',
|
stepId: '',
|
||||||
|
|
|
@ -389,69 +389,11 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<a-modal
|
<saveAsApiModal
|
||||||
|
v-if="tempApiDetail"
|
||||||
v-model:visible="saveNewApiModalVisible"
|
v-model:visible="saveNewApiModalVisible"
|
||||||
:title="t('common.save')"
|
:detail="tempApiDetail"
|
||||||
class="ms-modal-form"
|
></saveAsApiModal>
|
||||||
title-align="start"
|
|
||||||
body-class="!p-0"
|
|
||||||
>
|
|
||||||
<a-form ref="saveModalFormRef" :model="saveModalForm" layout="vertical">
|
|
||||||
<a-form-item
|
|
||||||
field="name"
|
|
||||||
:label="t('apiTestDebug.requestName')"
|
|
||||||
:rules="[{ required: true, message: t('apiTestDebug.requestNameRequired') }]"
|
|
||||||
asterisk-position="end"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:model-value="saveModalForm.name"
|
|
||||||
:max-length="255"
|
|
||||||
:placeholder="t('apiTestDebug.requestNamePlaceholder')"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item
|
|
||||||
v-if="activeStep?.config.protocol === 'HTTP'"
|
|
||||||
field="path"
|
|
||||||
:label="t('apiTestDebug.requestUrl')"
|
|
||||||
:rules="[{ required: true, message: t('apiTestDebug.requestUrlRequired') }]"
|
|
||||||
asterisk-position="end"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:model-value="saveModalForm.path"
|
|
||||||
:max-length="255"
|
|
||||||
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item :label="t('apiTestDebug.requestModule')" class="mb-0">
|
|
||||||
<a-tree-select
|
|
||||||
v-model:modelValue="saveModalForm.moduleId"
|
|
||||||
:data="apiModuleTree"
|
|
||||||
:field-names="{ title: 'name', key: 'id', children: 'children' }"
|
|
||||||
:tree-props="{
|
|
||||||
virtualListProps: {
|
|
||||||
height: 200,
|
|
||||||
threshold: 200,
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
allow-search
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="flex items-center gap-[4px]">
|
|
||||||
<a-checkbox v-model:model-value="saveModalForm.saveApiAsCase"></a-checkbox>
|
|
||||||
{{ t('apiScenario.syncSaveAsCase') }}
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-[12px]">
|
|
||||||
<a-button type="secondary" :disabled="saveLoading" @click="handleSaveApiCancel">
|
|
||||||
{{ t('common.cancel') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="primary" :loading="saveLoading" @click="handleSaveApi">{{ t('common.confirm') }}</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-modal>
|
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="saveCaseModalVisible"
|
v-model:visible="saveCaseModalVisible"
|
||||||
:title="t('apiTestManagement.saveAsCase')"
|
:title="t('apiTestManagement.saveAsCase')"
|
||||||
|
@ -519,14 +461,10 @@
|
||||||
import waitTimeContent from './stepNodeComposition/waitTimeContent.vue';
|
import waitTimeContent from './stepNodeComposition/waitTimeContent.vue';
|
||||||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||||
import { RequestParam as CaseRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
import { RequestParam as CaseRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
import saveAsApiModal from '@/views/api-test/components/saveAsApiModal.vue';
|
||||||
|
|
||||||
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||||
import {
|
import { addCase, getDefinitionDetail } from '@/api/modules/api-test/management';
|
||||||
addCase,
|
|
||||||
addDefinition,
|
|
||||||
getDefinitionDetail,
|
|
||||||
getModuleTreeOnlyModules,
|
|
||||||
} from '@/api/modules/api-test/management';
|
|
||||||
import { debugScenario, getScenarioDetail, getScenarioStep } from '@/api/modules/api-test/scenario';
|
import { debugScenario, getScenarioDetail, getScenarioStep } from '@/api/modules/api-test/scenario';
|
||||||
import { getSocket } from '@/api/modules/project-management/commonScript';
|
import { getSocket } from '@/api/modules/project-management/commonScript';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -547,6 +485,7 @@
|
||||||
ExecuteApiRequestFullParams,
|
ExecuteApiRequestFullParams,
|
||||||
ExecuteConditionProcessor,
|
ExecuteConditionProcessor,
|
||||||
ExecutePluginRequestParams,
|
ExecutePluginRequestParams,
|
||||||
|
RequestResult,
|
||||||
} from '@/models/apiTest/common';
|
} from '@/models/apiTest/common';
|
||||||
import { AddApiCaseParams } from '@/models/apiTest/management';
|
import { AddApiCaseParams } from '@/models/apiTest/management';
|
||||||
import {
|
import {
|
||||||
|
@ -554,14 +493,13 @@
|
||||||
CreateStepAction,
|
CreateStepAction,
|
||||||
Scenario,
|
Scenario,
|
||||||
ScenarioStepConfig,
|
ScenarioStepConfig,
|
||||||
|
ScenarioStepDetail,
|
||||||
ScenarioStepDetails,
|
ScenarioStepDetails,
|
||||||
ScenarioStepFileParams,
|
ScenarioStepFileParams,
|
||||||
ScenarioStepItem,
|
ScenarioStepItem,
|
||||||
} from '@/models/apiTest/scenario';
|
} from '@/models/apiTest/scenario';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
|
||||||
import {
|
import {
|
||||||
RequestCaseStatus,
|
RequestCaseStatus,
|
||||||
RequestDefinitionStatus,
|
|
||||||
ScenarioAddStepActionType,
|
ScenarioAddStepActionType,
|
||||||
ScenarioExecuteStatus,
|
ScenarioExecuteStatus,
|
||||||
ScenarioStepRefType,
|
ScenarioStepRefType,
|
||||||
|
@ -571,7 +509,7 @@
|
||||||
import type { RequestParam } from '../common/customApiDrawer.vue';
|
import type { RequestParam } from '../common/customApiDrawer.vue';
|
||||||
import updateStepStatus from '../utils';
|
import updateStepStatus from '../utils';
|
||||||
import useCreateActions from './createAction/useCreateActions';
|
import useCreateActions from './createAction/useCreateActions';
|
||||||
import { casePriorityOptions, caseStatusOptions, defaultResponseItem } from '@/views/api-test/components/config';
|
import { casePriorityOptions, caseStatusOptions } from '@/views/api-test/components/config';
|
||||||
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
||||||
import getStepType from '@/views/api-test/scenario/components/common/stepType/utils';
|
import getStepType from '@/views/api-test/scenario/components/common/stepType/utils';
|
||||||
import { defaultStepItemCommon } from '@/views/api-test/scenario/components/config';
|
import { defaultStepItemCommon } from '@/views/api-test/scenario/components/config';
|
||||||
|
@ -899,11 +837,13 @@
|
||||||
stepDetails.value[step.id] = {
|
stepDetails.value[step.id] = {
|
||||||
...res,
|
...res,
|
||||||
stepId: step.id,
|
stepId: step.id,
|
||||||
protocol: step.config.protocol,
|
protocol: step.config.protocol || '',
|
||||||
method: step.config.method,
|
method: step.config.method || '',
|
||||||
|
...parseRequestBodyResult,
|
||||||
|
};
|
||||||
|
scenario.value.stepFileParam[step.id] = {
|
||||||
...parseRequestBodyResult,
|
...parseRequestBodyResult,
|
||||||
};
|
};
|
||||||
scenario.value.stepFileParam[step.id] = parseRequestBodyResult;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -912,124 +852,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiModuleTree = ref<MsTreeNodeData[]>([]);
|
|
||||||
async function initApiModuleTree(protocol: string) {
|
|
||||||
try {
|
|
||||||
apiModuleTree.value = await getModuleTreeOnlyModules({
|
|
||||||
keyword: '',
|
|
||||||
protocol,
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
moduleIds: [],
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveNewApiModalVisible = ref(false);
|
const saveNewApiModalVisible = ref(false);
|
||||||
const saveModalForm = ref({
|
const tempApiDetail = ref<RequestParam>();
|
||||||
name: '',
|
|
||||||
path: '',
|
|
||||||
moduleId: 'root',
|
|
||||||
saveApiAsCase: false,
|
|
||||||
});
|
|
||||||
const saveModalFormRef = ref<FormInstance>();
|
|
||||||
const saveLoading = ref(false);
|
|
||||||
|
|
||||||
async function saveApiAsCase(id: string) {
|
|
||||||
if (activeStep.value) {
|
|
||||||
const detail = stepDetails.value[activeStep.value.id] as RequestParam;
|
|
||||||
const fileParams = scenario.value.stepFileParam[activeStep.value.id];
|
|
||||||
const url = new URL(saveModalForm.value.path);
|
|
||||||
const path = url.pathname + url.search + url.hash;
|
|
||||||
const params: AddApiCaseParams = {
|
|
||||||
name: saveModalForm.value.name,
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
environmentId: appStore.currentEnvConfig?.id || '',
|
|
||||||
apiDefinitionId: id,
|
|
||||||
request: {
|
|
||||||
...detail,
|
|
||||||
url: path,
|
|
||||||
},
|
|
||||||
priority: 'P0',
|
|
||||||
status: RequestCaseStatus.PROCESSING,
|
|
||||||
tags: [],
|
|
||||||
uploadFileIds: fileParams?.uploadFileIds || [],
|
|
||||||
linkFileIds: fileParams?.linkFileIds || [],
|
|
||||||
};
|
|
||||||
await addCase(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存请求
|
|
||||||
* @param isSaveCase 是否需要保存用例
|
|
||||||
*/
|
|
||||||
async function realSaveAsApi() {
|
|
||||||
try {
|
|
||||||
saveLoading.value = true;
|
|
||||||
if (activeStep.value) {
|
|
||||||
let url;
|
|
||||||
let path = '';
|
|
||||||
try {
|
|
||||||
url = new URL(saveModalForm.value.path);
|
|
||||||
path = url.pathname + url.search + url.hash;
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
path = saveModalForm.value.path;
|
|
||||||
}
|
|
||||||
const detail = stepDetails.value[activeStep.value.id] as RequestParam;
|
|
||||||
const fileParams = scenario.value.stepFileParam[activeStep.value.id];
|
|
||||||
const res = await addDefinition({
|
|
||||||
...saveModalForm.value,
|
|
||||||
path,
|
|
||||||
projectId: appStore.currentProjectId,
|
|
||||||
tags: [],
|
|
||||||
description: '',
|
|
||||||
status: RequestDefinitionStatus.PROCESSING,
|
|
||||||
customFields: [],
|
|
||||||
versionId: '',
|
|
||||||
environmentId: appStore.currentEnvConfig?.id || '',
|
|
||||||
request: {
|
|
||||||
...detail,
|
|
||||||
url: path,
|
|
||||||
path,
|
|
||||||
},
|
|
||||||
uploadFileIds: fileParams?.uploadFileIds || [],
|
|
||||||
linkFileIds: fileParams?.linkFileIds || [],
|
|
||||||
response: [defaultResponseItem],
|
|
||||||
method: detail?.method,
|
|
||||||
protocol: detail?.protocol,
|
|
||||||
});
|
|
||||||
if (saveModalForm.value.saveApiAsCase) {
|
|
||||||
await saveApiAsCase(res.id);
|
|
||||||
}
|
|
||||||
Message.success(t('common.saveSuccess'));
|
|
||||||
saveNewApiModalVisible.value = false;
|
|
||||||
saveLoading.value = false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
saveLoading.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSaveApiCancel() {
|
|
||||||
saveModalFormRef.value?.resetFields();
|
|
||||||
saveNewApiModalVisible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSaveApi() {
|
|
||||||
saveModalFormRef.value?.validate(async (errors) => {
|
|
||||||
if (!errors) {
|
|
||||||
await realSaveAsApi();
|
|
||||||
handleSaveApiCancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveCaseModalVisible = ref(false);
|
const saveCaseModalVisible = ref(false);
|
||||||
const saveCaseLoading = ref(false);
|
const saveCaseLoading = ref(false);
|
||||||
|
@ -1209,8 +1033,13 @@
|
||||||
break;
|
break;
|
||||||
case 'saveAsApi':
|
case 'saveAsApi':
|
||||||
activeStep.value = node as ScenarioStepItem;
|
activeStep.value = node as ScenarioStepItem;
|
||||||
initApiModuleTree((stepDetails.value[node.id] as RequestParam)?.protocol);
|
const detail = stepDetails.value[activeStep.value.id] as RequestParam;
|
||||||
saveModalForm.value.path = (stepDetails.value[node.id] as RequestParam)?.url;
|
const fileParams = scenario.value.stepFileParam[activeStep.value.id];
|
||||||
|
tempApiDetail.value = {
|
||||||
|
...detail,
|
||||||
|
uploadFileIds: fileParams?.uploadFileIds || [],
|
||||||
|
linkFileIds: fileParams?.linkFileIds || [],
|
||||||
|
};
|
||||||
saveNewApiModalVisible.value = true;
|
saveNewApiModalVisible.value = true;
|
||||||
break;
|
break;
|
||||||
case 'saveAsCase':
|
case 'saveAsCase':
|
||||||
|
@ -1284,7 +1113,7 @@
|
||||||
scenario.value.unSaved = !!tempStepDesc.value;
|
scenario.value.unSaved = !!tempStepDesc.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleStepContentChange($event, step: ScenarioStepItem) {
|
function handleStepContentChange($event: Record<string, any>, step: ScenarioStepItem) {
|
||||||
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
|
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
|
||||||
if (realStep) {
|
if (realStep) {
|
||||||
Object.keys($event).forEach((key) => {
|
Object.keys($event).forEach((key) => {
|
||||||
|
@ -1399,7 +1228,7 @@
|
||||||
if (data.msgType === 'EXEC_RESULT') {
|
if (data.msgType === 'EXEC_RESULT') {
|
||||||
if (step.reportId === data.reportId) {
|
if (step.reportId === data.reportId) {
|
||||||
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
// 判断当前查看的tab是否是当前返回的报告的tab,是的话直接赋值
|
||||||
data.taskResult.requestResults.forEach((result) => {
|
data.taskResult.requestResults.forEach((result: RequestResult) => {
|
||||||
if (_scenario.stepResponses[result.stepId] === undefined) {
|
if (_scenario.stepResponses[result.stepId] === undefined) {
|
||||||
_scenario.stepResponses[result.stepId] = [];
|
_scenario.stepResponses[result.stepId] = [];
|
||||||
}
|
}
|
||||||
|
@ -1466,7 +1295,7 @@
|
||||||
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, node.uniqueId, 'uniqueId');
|
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, node.uniqueId, 'uniqueId');
|
||||||
if (realStep) {
|
if (realStep) {
|
||||||
realStep.reportId = getGenerateId();
|
realStep.reportId = getGenerateId();
|
||||||
const _stepDetails = {};
|
const _stepDetails: Record<string, any> = {};
|
||||||
const stepFileParam = scenario.value.stepFileParam[realStep.id];
|
const stepFileParam = scenario.value.stepFileParam[realStep.id];
|
||||||
traverseTree(
|
traverseTree(
|
||||||
realStep,
|
realStep,
|
||||||
|
@ -1579,7 +1408,9 @@
|
||||||
// 将旧步骤替换为新步骤
|
// 将旧步骤替换为新步骤
|
||||||
if (realStep.parent?.children) {
|
if (realStep.parent?.children) {
|
||||||
// 如果被替换的步骤是子孙步骤,则需要在父级的 children 中替换
|
// 如果被替换的步骤是子孙步骤,则需要在父级的 children 中替换
|
||||||
const index = realStep.parent.children.findIndex((item) => item.uniqueId === realStep.uniqueId);
|
const index = realStep.parent.children.findIndex(
|
||||||
|
(item: ScenarioStepItem) => item.uniqueId === realStep.uniqueId
|
||||||
|
);
|
||||||
realStep.parent.children.splice(index, 1, newStep);
|
realStep.parent.children.splice(index, 1, newStep);
|
||||||
} else {
|
} else {
|
||||||
// 如果被替换的步骤是第一层级步骤,则直接替换
|
// 如果被替换的步骤是第一层级步骤,则直接替换
|
||||||
|
@ -1930,10 +1761,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const showQuickInput = ref(false);
|
const showQuickInput = ref(false);
|
||||||
const quickInputParamValue = ref('');
|
const quickInputParamValue = ref<any>('');
|
||||||
const quickInputDataKey = ref('');
|
const quickInputDataKey = ref('');
|
||||||
|
|
||||||
function setQuickInput(step: ScenarioStepItem, dataKey: string) {
|
function setQuickInput(step: ScenarioStepItem, dataKey: keyof ScenarioStepDetail) {
|
||||||
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
|
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, step.uniqueId, 'uniqueId');
|
||||||
if (realStep) {
|
if (realStep) {
|
||||||
activeStep.value = realStep as ScenarioStepItem;
|
activeStep.value = realStep as ScenarioStepItem;
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
<assertion
|
<assertion
|
||||||
v-if="activeKey === ScenarioCreateComposition.ASSERTION"
|
v-if="activeKey === ScenarioCreateComposition.ASSERTION"
|
||||||
v-model:assertion-config="scenario.scenarioConfig.assertionConfig"
|
v-model:assertion-config="scenario.scenarioConfig.assertionConfig"
|
||||||
|
@change="scenario.unSaved = true"
|
||||||
/>
|
/>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
|
|
@ -8,35 +8,40 @@
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="arco-table-filters-content">
|
<div class="arco-table-filters-content">
|
||||||
<div class="ml-[6px] flex w-full items-center justify-start overflow-hidden px-[6px] py-[2px]">
|
<div class="arco-table-filters-content-list">
|
||||||
<a-checkbox-group
|
<div class="flex w-full items-center justify-start overflow-hidden px-[12px] py-[2px]">
|
||||||
v-if="props.mode === 'static' && props.list?.length"
|
<a-checkbox-group
|
||||||
v-model:model-value="innerStatusFilters"
|
v-if="props.mode === 'static' && props.list?.length"
|
||||||
direction="vertical"
|
v-model:model-value="innerStatusFilters"
|
||||||
size="small"
|
direction="vertical"
|
||||||
>
|
size="small"
|
||||||
<a-checkbox
|
|
||||||
v-for="(item, index) of props.list"
|
|
||||||
:key="item[props.valueKey || 'value']"
|
|
||||||
:value="item[props.valueKey || 'value']"
|
|
||||||
>
|
>
|
||||||
<a-tooltip :content="item[props.labelKey || 'text']" :mouse-enter-delay="300">
|
<a-checkbox
|
||||||
<div class="one-line-text">
|
v-for="(item, index) of props.list"
|
||||||
<slot name="item" :item="item" :index="index"></slot>
|
:key="item[props.valueKey || 'value']"
|
||||||
</div>
|
:value="item[props.valueKey || 'value']"
|
||||||
</a-tooltip>
|
>
|
||||||
</a-checkbox>
|
<a-tooltip :content="item[props.labelKey || 'text']" :mouse-enter-delay="300">
|
||||||
</a-checkbox-group>
|
<div class="one-line-text">
|
||||||
|
<slot name="item" :item="item" :index="index"></slot>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-checkbox>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</div>
|
||||||
|
<div v-if="props.mode === 'remote'" class="w-[200px] p-[12px] pb-0">
|
||||||
|
<MsUserSelector
|
||||||
|
v-model="innerStatusFilters"
|
||||||
|
:load-option-params="props.loadOptionParams"
|
||||||
|
:type="props.type"
|
||||||
|
:placeholder="props.placeholderText"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="props.mode === 'remote'" class="w-[200px] p-4 pb-0">
|
<div
|
||||||
<MsUserSelector
|
class="flex items-center border-t border-[var(--color-text-n8)] p-[12px]"
|
||||||
v-model="innerStatusFilters"
|
:class="[props.mode === 'static' ? 'justify-between' : 'justify-end']"
|
||||||
:load-option-params="props.loadOptionParams"
|
>
|
||||||
:type="props.type"
|
|
||||||
:placeholder="props.placeholderText"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center p-4" :class="[props.mode === 'static' ? 'justify-between' : 'justify-end']">
|
|
||||||
<a-button size="mini" class="mr-[8px]" @click="resetFilter">
|
<a-button size="mini" class="mr-[8px]" @click="resetFilter">
|
||||||
{{ t('common.reset') }}
|
{{ t('common.reset') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
|
@ -4,8 +4,8 @@ export default {
|
||||||
'login.form.password.errMsg': '密码不能为空',
|
'login.form.password.errMsg': '密码不能为空',
|
||||||
'login.form.login.errMsg': '登录出错,请刷新重试',
|
'login.form.login.errMsg': '登录出错,请刷新重试',
|
||||||
'login.form.login.success': '欢迎使用',
|
'login.form.login.success': '欢迎使用',
|
||||||
'login.form.userName.placeholder': '请输入邮箱登录',
|
'login.form.userName.placeholder': '请输入邮箱',
|
||||||
'login.form.userName.placeholderOther': '请输入账号登录',
|
'login.form.userName.placeholderOther': '请输入账号',
|
||||||
'login.form.password.placeholder': '请输入密码',
|
'login.form.password.placeholder': '请输入密码',
|
||||||
'login.form.rememberPassword': '记住密码',
|
'login.form.rememberPassword': '记住密码',
|
||||||
'login.form.forgetPassword': '忘记密码',
|
'login.form.forgetPassword': '忘记密码',
|
||||||
|
|
|
@ -131,7 +131,7 @@
|
||||||
mode="fileUpdateDesc"
|
mode="fileUpdateDesc"
|
||||||
:title="t('project.fileManagement.desc')"
|
:title="t('project.fileManagement.desc')"
|
||||||
:field-config="{
|
:field-config="{
|
||||||
field: detail.desc,
|
field: detail.description,
|
||||||
placeholder: t('project.fileManagement.descPlaceholder'),
|
placeholder: t('project.fileManagement.descPlaceholder'),
|
||||||
maxLength: 1000,
|
maxLength: 1000,
|
||||||
isTextArea: true,
|
isTextArea: true,
|
||||||
|
|
|
@ -71,39 +71,38 @@
|
||||||
:title="t('system.authorized.authorityChecking')"
|
:title="t('system.authorized.authorityChecking')"
|
||||||
:ok-text="t('system.authorized.authorization')"
|
:ok-text="t('system.authorized.authorization')"
|
||||||
:ok-loading="drawerLoading"
|
:ok-loading="drawerLoading"
|
||||||
:width="480"
|
:width="680"
|
||||||
@confirm="confirmHandler"
|
@confirm="confirmHandler"
|
||||||
@cancel="cancelHandler"
|
@cancel="cancelHandler"
|
||||||
>
|
>
|
||||||
<a-form ref="authFormRef" :model="authorizedForm" layout="vertical">
|
<a-form ref="authFormRef" :model="authorizedForm" layout="vertical">
|
||||||
<a-row class="grid-demo">
|
<a-form-item
|
||||||
<a-form-item
|
:label="t('system.authorized.license')"
|
||||||
:label="t('system.authorized.license')"
|
field="licenseCode"
|
||||||
field="licenseCode"
|
asterisk-position="end"
|
||||||
asterisk-position="end"
|
required
|
||||||
|
:validate-trigger="['input']"
|
||||||
|
:rules="[{ required: true, message: t('system.authorized.LicenseIsRequired') }]"
|
||||||
|
>
|
||||||
|
<MsUpload
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
accept="none"
|
||||||
|
:is-limit="false"
|
||||||
|
:show-sub-text="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
:auto-upload="false"
|
||||||
|
/>
|
||||||
|
<a-textarea
|
||||||
|
v-model="authorizedForm.licenseCode"
|
||||||
|
class="mt-4"
|
||||||
|
:placeholder="t('system.authorized.licenseCode')"
|
||||||
|
:auto-size="{
|
||||||
|
minRows: 3,
|
||||||
|
}"
|
||||||
:rules="[{ required: true, message: t('system.authorized.LicenseIsRequired') }]"
|
:rules="[{ required: true, message: t('system.authorized.LicenseIsRequired') }]"
|
||||||
:validate-trigger="['input']"
|
:max-length="1000"
|
||||||
>
|
></a-textarea>
|
||||||
<MsUpload
|
</a-form-item>
|
||||||
v-model:file-list="fileList"
|
|
||||||
accept="none"
|
|
||||||
:is-limit="false"
|
|
||||||
:show-sub-text="false"
|
|
||||||
:show-file-list="false"
|
|
||||||
:auto-upload="false"
|
|
||||||
/>
|
|
||||||
<a-textarea
|
|
||||||
v-model="authorizedForm.licenseCode"
|
|
||||||
class="mt-4"
|
|
||||||
:placeholder="t('system.authorized.licenseCode')"
|
|
||||||
:auto-size="{
|
|
||||||
minRows: 3,
|
|
||||||
}"
|
|
||||||
:rules="[{ required: true, message: t('system.authorized.LicenseIsRequired') }]"
|
|
||||||
:max-length="1000"
|
|
||||||
></a-textarea>
|
|
||||||
</a-form-item>
|
|
||||||
</a-row>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
</MsDrawer>
|
</MsDrawer>
|
||||||
</MsCard>
|
</MsCard>
|
||||||
|
@ -260,7 +259,4 @@
|
||||||
@apply flex flex-col justify-between;
|
@apply flex flex-col justify-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
:deep(.ms-upload-area) {
|
|
||||||
width: 446px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -373,7 +373,6 @@
|
||||||
/**
|
/**
|
||||||
* @description 系统设置-资源池详情
|
* @description 系统设置-资源池详情
|
||||||
*/
|
*/
|
||||||
import { computed, onBeforeMount, Ref, ref, watch, watchEffect } from 'vue';
|
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
import { FormInstance, Message, SelectOptionData } from '@arco-design/web-vue';
|
||||||
import { cloneDeep, isEmpty } from 'lodash-es';
|
import { cloneDeep, isEmpty } from 'lodash-es';
|
||||||
|
@ -467,6 +466,9 @@
|
||||||
orgOptions.value = await getSystemOrgOption();
|
orgOptions.value = await getSystemOrgOption();
|
||||||
if (!isXpack.value) {
|
if (!isXpack.value) {
|
||||||
useList.value = useList.value.filter((item) => item.value === 'API');
|
useList.value = useList.value.filter((item) => item.value === 'API');
|
||||||
|
nextTick(() => {
|
||||||
|
setIsSave(true); // 初始化时设置为已保存状态
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -489,6 +491,9 @@
|
||||||
orgIds: orgIdNameMap?.map((e) => e.id) || [],
|
orgIds: orgIdNameMap?.map((e) => e.id) || [],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
nextTick(() => {
|
||||||
|
setIsSave(true); // 初始化时设置为已保存状态
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
Loading…
Reference in New Issue