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