feat(接口场景): 脚本操作&api、case 文件处理&交互优化
This commit is contained in:
parent
7048ec91e2
commit
56ad892491
|
@ -49,7 +49,7 @@
|
||||||
"@tiptap/vue-3": "^2.1.13",
|
"@tiptap/vue-3": "^2.1.13",
|
||||||
"@types/color": "^3.0.4",
|
"@types/color": "^3.0.4",
|
||||||
"@types/node": "^20.11.16",
|
"@types/node": "^20.11.16",
|
||||||
"@vueuse/core": "^10.7.2",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@xmldom/xmldom": "^0.8.10",
|
"@xmldom/xmldom": "^0.8.10",
|
||||||
"ace-builds": "^1.24.2",
|
"ace-builds": "^1.24.2",
|
||||||
"ahooks-vue": "^0.15.1",
|
"ahooks-vue": "^0.15.1",
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
try {
|
try {
|
||||||
await appStore.initSystemVersion(); // 初始化系统版本
|
appStore.initSystemVersion(); // 初始化系统版本
|
||||||
// 企业版才校验license
|
// 企业版才校验license
|
||||||
if (appStore.packageType === 'enterprise') {
|
if (appStore.packageType === 'enterprise') {
|
||||||
licenseStore.getValidateLicense();
|
licenseStore.getValidateLicense();
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
setLocalStorage('salt', publicKey);
|
setLocalStorage('salt', publicKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onBeforeMount(async () => {
|
||||||
await getPublicKey();
|
await getPublicKey();
|
||||||
if (WHITE_LIST.find((el) => el.path === window.location.hash.split('#')[1]) === undefined) {
|
if (WHITE_LIST.find((el) => el.path === window.location.hash.split('#')[1]) === undefined) {
|
||||||
await checkIsLogin();
|
await checkIsLogin();
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
UpdateScenarioUrl,
|
UpdateScenarioUrl,
|
||||||
} from '@/api/requrls/api-test/scenario';
|
} from '@/api/requrls/api-test/scenario';
|
||||||
|
|
||||||
|
import { ExecuteConditionProcessor } from '@/models/apiTest/common';
|
||||||
import {
|
import {
|
||||||
ApiScenarioBatchDeleteParams,
|
ApiScenarioBatchDeleteParams,
|
||||||
ApiScenarioBatchEditParams,
|
ApiScenarioBatchEditParams,
|
||||||
|
@ -55,6 +56,9 @@ import {
|
||||||
} from '@/models/apiTest/scenario';
|
} from '@/models/apiTest/scenario';
|
||||||
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules, TransferFileParams } from '@/models/common';
|
import { AddModuleParams, CommonList, ModuleTreeNode, MoveModules, TransferFileParams } from '@/models/common';
|
||||||
|
|
||||||
|
import type { RequestParam as CaseRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
import type { RequestParam } from '@/views/api-test/scenario/components/common/customApiDrawer.vue';
|
||||||
|
|
||||||
// 更新模块
|
// 更新模块
|
||||||
export function updateModule(data: ApiScenarioModuleUpdateParams) {
|
export function updateModule(data: ApiScenarioModuleUpdateParams) {
|
||||||
return MSR.post({ url: UpdateModuleUrl, data });
|
return MSR.post({ url: UpdateModuleUrl, data });
|
||||||
|
@ -207,7 +211,10 @@ export function getScenarioDetail(id: string) {
|
||||||
|
|
||||||
// 获取场景步骤详情
|
// 获取场景步骤详情
|
||||||
export function getScenarioStep(stepId: string | number) {
|
export function getScenarioStep(stepId: string | number) {
|
||||||
return MSR.get({ url: GetScenarioStepUrl, params: stepId });
|
return MSR.get<Partial<RequestParam & CaseRequestParam & ExecuteConditionProcessor>>({
|
||||||
|
url: GetScenarioStepUrl,
|
||||||
|
params: stepId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件转存
|
// 文件转存
|
||||||
|
|
|
@ -68,31 +68,31 @@
|
||||||
arrow-class="hidden"
|
arrow-class="hidden"
|
||||||
:popup-offset="0"
|
:popup-offset="0"
|
||||||
>
|
>
|
||||||
<MsTagsInput
|
<div class="!w-[calc(100%-28px)]">
|
||||||
v-model:model-value="inputFiles"
|
<MsTagsInput
|
||||||
:disabled="props.disabled"
|
v-model:model-value="inputFiles"
|
||||||
:input-class="props.inputClass"
|
:input-class="props.inputClass"
|
||||||
placeholder=" "
|
placeholder=" "
|
||||||
:max-tag-count="1"
|
:max-tag-count="1"
|
||||||
:size="props.inputSize"
|
:size="props.inputSize"
|
||||||
readonly
|
readonly
|
||||||
class="!w-[calc(100%-28px)]"
|
>
|
||||||
>
|
<template v-if="alreadyDeleteFiles.length > 0" #prefix>
|
||||||
<template v-if="alreadyDeleteFiles.length > 0" #prefix>
|
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />
|
||||||
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />
|
</template>
|
||||||
</template>
|
<template #tag="{ data }">
|
||||||
<template #tag="{ data }">
|
<MsTag
|
||||||
<MsTag
|
:size="props.tagSize"
|
||||||
:size="props.tagSize"
|
class="m-0 border-none p-0"
|
||||||
class="m-0 border-none p-0"
|
:self-style="{ backgroundColor: 'transparent !important' }"
|
||||||
:self-style="{ backgroundColor: 'transparent !important' }"
|
:closable="data.value !== '__arco__more'"
|
||||||
:closable="data.value !== '__arco__more'"
|
@close="handleClose(data)"
|
||||||
@close="handleClose(data)"
|
>
|
||||||
>
|
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
|
||||||
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
|
</MsTag>
|
||||||
</MsTag>
|
</template>
|
||||||
</template>
|
</MsTagsInput>
|
||||||
</MsTagsInput>
|
</div>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex w-[200px] flex-col gap-[8px]">
|
<div class="flex w-[200px] flex-col gap-[8px]">
|
||||||
<template v-if="alreadyDeleteFiles.length > 0">
|
<template v-if="alreadyDeleteFiles.length > 0">
|
||||||
|
@ -379,6 +379,14 @@
|
||||||
|
|
||||||
function handleSaveFileFinish(fileId: string) {
|
function handleSaveFileFinish(fileId: string) {
|
||||||
if (savingFile.value) {
|
if (savingFile.value) {
|
||||||
|
inputFiles.value = inputFiles.value.map((e) => {
|
||||||
|
if (e.value === savingFile.value?.fileId || e.value === savingFile.value?.uid) {
|
||||||
|
// 被存储过的文件没有 uid,只有fileId;刚上传还未保存的文件只有 uid,没有fileId
|
||||||
|
e.value = fileId;
|
||||||
|
e.local = false;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
});
|
||||||
savingFile.value.fileId = fileId;
|
savingFile.value.fileId = fileId;
|
||||||
savingFile.value.local = false;
|
savingFile.value.local = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@
|
||||||
<div class="relative w-full">
|
<div class="relative w-full">
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
ref="codeEditorRef"
|
ref="codeEditorRef"
|
||||||
v-model:model-value="innerCodeValue"
|
v-model:model-value="code"
|
||||||
title=""
|
title=""
|
||||||
:width="expandMenu ? '100%' : '68%'"
|
:width="expandMenu ? '100%' : '68%'"
|
||||||
height="460px"
|
height="460px"
|
||||||
theme="vs"
|
theme="vs"
|
||||||
:language="innerLanguagesType"
|
:language="language"
|
||||||
:read-only="props.disabled"
|
:read-only="props.disabled"
|
||||||
:show-full-screen="false"
|
:show-full-screen="false"
|
||||||
:show-theme-change="false"
|
:show-theme-change="false"
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
<template #rightBox>
|
<template #rightBox>
|
||||||
<MsScriptMenu
|
<MsScriptMenu
|
||||||
v-model:expand="expandMenu"
|
v-model:expand="expandMenu"
|
||||||
v-model:languagesType="innerLanguagesType"
|
v-model:languagesType="language"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
@insert="insertHandler"
|
@insert="insertHandler"
|
||||||
@form-api-import="formApiImport"
|
@form-api-import="formApiImport"
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</div>
|
</div>
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
v-else
|
v-else
|
||||||
v-model:model-value="executionResultValue"
|
v-model:model-value="executionResult"
|
||||||
title=""
|
title=""
|
||||||
width="100%"
|
width="100%"
|
||||||
height="calc(100vh - 155px)"
|
height="calc(100vh - 155px)"
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
/>
|
/>
|
||||||
<InsertCommonScript
|
<InsertCommonScript
|
||||||
v-model:visible="showInsertDrawer"
|
v-model:visible="showInsertDrawer"
|
||||||
:script-language="innerLanguagesType"
|
:script-language="language"
|
||||||
:enable-radio-selected="props.enableRadioSelected"
|
:enable-radio-selected="props.enableRadioSelected"
|
||||||
@save="saveHandler"
|
@save="saveHandler"
|
||||||
/>
|
/>
|
||||||
|
@ -73,7 +73,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useVModel } from '@vueuse/core';
|
|
||||||
|
|
||||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||||
import { Language, LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
import { Language, LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||||
|
@ -93,43 +92,27 @@
|
||||||
defineProps<{
|
defineProps<{
|
||||||
showType: 'commonScript' | 'executionResult'; // 执行类型
|
showType: 'commonScript' | 'executionResult'; // 执行类型
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
language: Language;
|
|
||||||
code: string;
|
|
||||||
enableRadioSelected?: boolean;
|
enableRadioSelected?: boolean;
|
||||||
executionResult?: string; // 执行结果
|
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'update:language', value: Language): void;
|
|
||||||
(e: 'update:code', value: string): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const projectId = ref<string>(appStore.currentProjectId);
|
const projectId = ref<string>(appStore.currentProjectId);
|
||||||
|
|
||||||
const innerLanguagesType = useVModel(props, 'language', emit);
|
const language = defineModel<Language>('language', {
|
||||||
const executionResultValue = useVModel(props, 'executionResult', emit);
|
required: true,
|
||||||
|
});
|
||||||
watch(
|
const executionResult = defineModel<string>('executionResult', {
|
||||||
() => innerLanguagesType.value,
|
default: '',
|
||||||
(val) => {
|
});
|
||||||
emit('update:language', val);
|
const code = defineModel<string>('code', {
|
||||||
}
|
required: true,
|
||||||
);
|
});
|
||||||
|
|
||||||
const innerCodeValue = useVModel(props, 'code', emit);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.code,
|
|
||||||
(val) => {
|
|
||||||
innerCodeValue.value = val;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const expandMenu = ref<boolean>(false);
|
const expandMenu = ref<boolean>(false);
|
||||||
|
|
||||||
|
@ -153,8 +136,8 @@
|
||||||
|
|
||||||
const confirmLoading = ref<boolean>(false);
|
const confirmLoading = ref<boolean>(false);
|
||||||
|
|
||||||
function insertHandler(code: string) {
|
function insertHandler(_code: string) {
|
||||||
codeEditorRef.value?.insertContent(code);
|
codeEditorRef.value?.insertContent(_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveHandler(data: CommonScriptItem[]) {
|
function saveHandler(data: CommonScriptItem[]) {
|
||||||
|
@ -185,7 +168,7 @@ ${item.script}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearCode() {
|
function clearCode() {
|
||||||
innerCodeValue.value = '';
|
code.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
@ -193,7 +176,7 @@ ${item.script}
|
||||||
insertHandler,
|
insertHandler,
|
||||||
undoHandler,
|
undoHandler,
|
||||||
clearCode,
|
clearCode,
|
||||||
executionResultValue,
|
executionResult,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@
|
||||||
v-permission="props.okPermission || []"
|
v-permission="props.okPermission || []"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
:loading="props.okLoading"
|
:loading="props.okLoading"
|
||||||
|
:disabled="okDisabled"
|
||||||
@click="handleContinue"
|
@click="handleContinue"
|
||||||
>
|
>
|
||||||
{{ t(props.saveContinueText || 'ms.drawer.saveContinue') }}
|
{{ t(props.saveContinueText || 'ms.drawer.saveContinue') }}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
@press-enter="tagInputEnter"
|
@press-enter="tagInputEnter"
|
||||||
@blur="tagInputBlur"
|
@blur="tagInputBlur"
|
||||||
@clear="emit('clear')"
|
@clear="emit('clear')"
|
||||||
|
@click="emit('click')"
|
||||||
>
|
>
|
||||||
<template v-if="$slots.prefix" #prefix>
|
<template v-if="$slots.prefix" #prefix>
|
||||||
<slot name="prefix"></slot>
|
<slot name="prefix"></slot>
|
||||||
|
@ -66,7 +67,7 @@
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change', 'clear', 'blur']);
|
const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change', 'clear', 'blur', 'click']);
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,17 @@ export default function useOpenNewPage() {
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
function openNewPage(name: RouteRecordName | undefined, query = {}) {
|
function openNewPage(name: RouteRecordName | undefined, query: Record<string, any> = {}) {
|
||||||
|
const pId = query.pId || appStore.currentProjectId;
|
||||||
|
if (pId) {
|
||||||
|
// 如果传入参数指定了项目 id,则使用传入的项目 id
|
||||||
|
delete query.pId;
|
||||||
|
}
|
||||||
const queryParams = new URLSearchParams(query).toString();
|
const queryParams = new URLSearchParams(query).toString();
|
||||||
window.open(
|
window.open(
|
||||||
`${window.location.origin}#${router.resolve({ name }).fullPath}?orgId=${appStore.currentOrgId}&projectId=${
|
`${window.location.origin}#${router.resolve({ name }).fullPath}?orgId=${
|
||||||
appStore.currentProjectId
|
appStore.currentOrgId
|
||||||
}&${queryParams}`,
|
}&pId=${pId}&${queryParams}`,
|
||||||
'_blank'
|
'_blank'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,12 @@ import {
|
||||||
ExecuteApiRequestFullParams,
|
ExecuteApiRequestFullParams,
|
||||||
ExecuteAssertionItem,
|
ExecuteAssertionItem,
|
||||||
ExecuteConditionConfig,
|
ExecuteConditionConfig,
|
||||||
|
ExecuteConditionProcessor,
|
||||||
RequestResult,
|
RequestResult,
|
||||||
ResponseDefinition,
|
ResponseDefinition,
|
||||||
} from './common';
|
} from './common';
|
||||||
|
import type { RequestParam as CaseRequestParam } from '@/views/api-test/components/requestComposition/index.vue';
|
||||||
|
import type { RequestParam } from '@/views/api-test/scenario/components/common/customApiDrawer.vue';
|
||||||
|
|
||||||
// 场景-更新模块参数
|
// 场景-更新模块参数
|
||||||
export interface ApiScenarioModuleUpdateParams {
|
export interface ApiScenarioModuleUpdateParams {
|
||||||
|
@ -190,7 +193,7 @@ export interface ScenarioHistoryItem {
|
||||||
createUserName: string;
|
createUserName: string;
|
||||||
versionName: string;
|
versionName: string;
|
||||||
}
|
}
|
||||||
|
// 场景步骤-自定义请求
|
||||||
export type CustomApiStep = ExecuteApiRequestFullParams & {
|
export type CustomApiStep = ExecuteApiRequestFullParams & {
|
||||||
protocol: string;
|
protocol: string;
|
||||||
activeTab: RequestComposition;
|
activeTab: RequestComposition;
|
||||||
|
@ -241,6 +244,7 @@ export interface Variable {
|
||||||
commonVariables: CommonVariable[];
|
commonVariables: CommonVariable[];
|
||||||
csvVariables: CsvVariable[];
|
csvVariables: CsvVariable[];
|
||||||
}
|
}
|
||||||
|
// 场景配置
|
||||||
export interface ScenarioConfig {
|
export interface ScenarioConfig {
|
||||||
variable: Variable;
|
variable: Variable;
|
||||||
preProcessorConfig: ExecuteConditionConfig;
|
preProcessorConfig: ExecuteConditionConfig;
|
||||||
|
@ -298,11 +302,13 @@ export interface LoopStepDetail extends StepDetailsCommon {
|
||||||
msCountController: CountController;
|
msCountController: CountController;
|
||||||
whileController: WhileController;
|
whileController: WhileController;
|
||||||
}
|
}
|
||||||
|
// 场景引用配置
|
||||||
export interface ScenarioStepConfig {
|
export interface ScenarioStepConfig {
|
||||||
useCurrentScenarioParam: boolean; // 是否优先使用当前场景参数
|
useCurrentScenarioParam: boolean; // 是否优先使用当前场景参数
|
||||||
useBothScenarioParam: boolean; // 是否当前场景参数和源场景参数都应用(勾选非空值时为 true)
|
useBothScenarioParam: boolean; // 是否当前场景参数和源场景参数都应用(勾选非空值时为 true)
|
||||||
enableScenarioEnv: boolean; // 是否应用源场景环境
|
enableScenarioEnv: boolean; // 是否应用源场景环境
|
||||||
}
|
}
|
||||||
|
// 场景步骤详情
|
||||||
export type ScenarioStepDetail = Partial<
|
export type ScenarioStepDetail = Partial<
|
||||||
CustomApiStepDetail &
|
CustomApiStepDetail &
|
||||||
ConditionStepDetail &
|
ConditionStepDetail &
|
||||||
|
@ -312,14 +318,16 @@ export type ScenarioStepDetail = Partial<
|
||||||
method: RequestMethods;
|
method: RequestMethods;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
// 场景步骤项
|
||||||
export interface ScenarioStepItem {
|
export interface ScenarioStepItem {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
sort: number;
|
sort: number;
|
||||||
name: string;
|
name: string;
|
||||||
enable: boolean; // 是否启用
|
enable: boolean; // 是否启用
|
||||||
copyFromStepId?: string; // 如果步骤是复制的,这个字段是复制的步骤id;如果复制的步骤也是复制的,并且没有加载过详情,则这个 id 是最原始的 被复制的步骤 id
|
copyFromStepId?: string; // 如果步骤是复制的,这个字段是复制的步骤id;如果复制的步骤也是复制的,并且没有加载过详情,则这个 id 是最原始的 被复制的步骤 id
|
||||||
resourceId?: string; // 详情或者引用的类型才有
|
resourceId?: string; // 详情或者引用、复制的类型才有
|
||||||
resourceNum?: string; // 详情或者引用的类型才有
|
resourceNum?: string; // 详情或者引用、复制的类型才有
|
||||||
|
originProjectId?: string; // 如果步骤是复制的,这个字段是复制的资源的所在的项目id
|
||||||
stepType: ScenarioStepType;
|
stepType: ScenarioStepType;
|
||||||
refType: ScenarioStepRefType;
|
refType: ScenarioStepRefType;
|
||||||
config: ScenarioStepDetail; // 存储步骤列表需要展示的信息
|
config: ScenarioStepDetail; // 存储步骤列表需要展示的信息
|
||||||
|
@ -342,6 +350,15 @@ export interface ScenarioStepItem {
|
||||||
isQuoteScenarioStep?: boolean; // 是否是引用场景下的步骤(不分是不是完全引用,只要是引用类型就是),不可修改引用 api 的参数值
|
isQuoteScenarioStep?: boolean; // 是否是引用场景下的步骤(不分是不是完全引用,只要是引用类型就是),不可修改引用 api 的参数值
|
||||||
isRefScenarioStep?: boolean; // 是否是完全引用的场景下的步骤,是的话不允许启用禁用
|
isRefScenarioStep?: boolean; // 是否是完全引用的场景下的步骤,是的话不允许启用禁用
|
||||||
}
|
}
|
||||||
|
// 场景步骤文件参数
|
||||||
|
export interface ScenarioStepFileParams {
|
||||||
|
uploadFileIds: string[];
|
||||||
|
linkFileIds: string[];
|
||||||
|
deleteFileIds?: string[];
|
||||||
|
unLinkFileIds?: string[];
|
||||||
|
}
|
||||||
|
// 场景步骤详情
|
||||||
|
export type ScenarioStepDetails = RequestParam | CaseRequestParam | ExecuteConditionProcessor;
|
||||||
// 场景
|
// 场景
|
||||||
export interface Scenario {
|
export interface Scenario {
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
|
@ -357,7 +374,8 @@ export interface Scenario {
|
||||||
environmentId?: string;
|
environmentId?: string;
|
||||||
scenarioConfig: ScenarioConfig;
|
scenarioConfig: ScenarioConfig;
|
||||||
steps: ScenarioStepItem[];
|
steps: ScenarioStepItem[];
|
||||||
stepDetails: Record<string, ScenarioStepDetail>;
|
stepDetails: Record<string, ScenarioStepDetails>; // case、api、脚本操作抽屉的详情结构
|
||||||
|
stepFileParam: Record<string, ScenarioStepFileParams>;
|
||||||
follow?: boolean;
|
follow?: boolean;
|
||||||
uploadFileIds: string[];
|
uploadFileIds: string[];
|
||||||
linkFileIds: string[];
|
linkFileIds: string[];
|
||||||
|
@ -375,6 +393,7 @@ export interface Scenario {
|
||||||
isExecute?: boolean; // 是否从列表执行进去场景详情
|
isExecute?: boolean; // 是否从列表执行进去场景详情
|
||||||
isDebug?: boolean; // 是否调试,区分执行场景和批量调试步骤
|
isDebug?: boolean; // 是否调试,区分执行场景和批量调试步骤
|
||||||
}
|
}
|
||||||
|
// 场景详情
|
||||||
export interface ScenarioDetail extends Scenario {
|
export interface ScenarioDetail extends Scenario {
|
||||||
stepTotal: number;
|
stepTotal: number;
|
||||||
requestPassRate: string;
|
requestPassRate: string;
|
||||||
|
@ -390,7 +409,7 @@ export interface ScenarioDetail extends Scenario {
|
||||||
updateTime: number;
|
updateTime: number;
|
||||||
updateUser: string;
|
updateUser: string;
|
||||||
}
|
}
|
||||||
|
// 场景-执行请求参数
|
||||||
export interface ApiScenarioDebugRequest {
|
export interface ApiScenarioDebugRequest {
|
||||||
id: string | number; // 场景 id
|
id: string | number; // 场景 id
|
||||||
grouped: boolean;
|
grouped: boolean;
|
||||||
|
@ -420,7 +439,7 @@ export interface ApiScenarioUpdateDTO extends Partial<Scenario> {
|
||||||
deleteFileIds?: string[];
|
deleteFileIds?: string[];
|
||||||
unLinkFileIds?: string[];
|
unLinkFileIds?: string[];
|
||||||
}
|
}
|
||||||
|
// 导入系统请求时获取步骤列表的分组入参
|
||||||
export interface GetSystemRequestTypeParams {
|
export interface GetSystemRequestTypeParams {
|
||||||
moduleIds?: (string | number)[];
|
moduleIds?: (string | number)[];
|
||||||
selectedIds: (string | number)[];
|
selectedIds: (string | number)[];
|
||||||
|
@ -429,7 +448,7 @@ export interface GetSystemRequestTypeParams {
|
||||||
protocol?: string;
|
protocol?: string;
|
||||||
versionId?: string;
|
versionId?: string;
|
||||||
}
|
}
|
||||||
|
// 导入系统请求时获取步骤列表的入参
|
||||||
export interface GetSystemRequestParams {
|
export interface GetSystemRequestParams {
|
||||||
apiRequest?: GetSystemRequestTypeParams;
|
apiRequest?: GetSystemRequestTypeParams;
|
||||||
caseRequest?: GetSystemRequestTypeParams;
|
caseRequest?: GetSystemRequestTypeParams;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { useAppStore } from '@/store';
|
|
||||||
import { clearToken, hasToken, isLoginExpires } from '@/utils/auth';
|
import { clearToken, hasToken, isLoginExpires } from '@/utils/auth';
|
||||||
|
|
||||||
import NProgress from 'nprogress'; // progress bar
|
import NProgress from 'nprogress'; // progress bar
|
||||||
|
|
|
@ -2,8 +2,6 @@ import { defineStore } from 'pinia';
|
||||||
|
|
||||||
import { getLicenseInfo } from '@/api/modules/setting/authorizedManagement';
|
import { getLicenseInfo } from '@/api/modules/setting/authorizedManagement';
|
||||||
|
|
||||||
import useAppStore from '../app';
|
|
||||||
|
|
||||||
const useLicenseStore = defineStore('license', {
|
const useLicenseStore = defineStore('license', {
|
||||||
persist: true,
|
persist: true,
|
||||||
state: (): { status: string | null } => ({
|
state: (): { status: string | null } => ({
|
||||||
|
@ -21,7 +19,6 @@ const useLicenseStore = defineStore('license', {
|
||||||
},
|
},
|
||||||
// license校验
|
// license校验
|
||||||
async getValidateLicense() {
|
async getValidateLicense() {
|
||||||
const appStore = useAppStore();
|
|
||||||
try {
|
try {
|
||||||
const result = await getLicenseInfo();
|
const result = await getLicenseInfo();
|
||||||
if (!result || !result.status || !result.license || !result.license.count) {
|
if (!result || !result.status || !result.license || !result.license.count) {
|
||||||
|
|
|
@ -59,8 +59,8 @@ const useUserStore = defineStore('user', {
|
||||||
} {
|
} {
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
state.userRoleRelations?.forEach(ug => {
|
state.userRoleRelations?.forEach((ug) => {
|
||||||
state.userRolePermissions?.forEach(gp => {
|
state.userRolePermissions?.forEach((gp) => {
|
||||||
if (gp.userRole.id === ug.roleId) {
|
if (gp.userRole.id === ug.roleId) {
|
||||||
ug.userRolePermissions = gp.userRolePermissions;
|
ug.userRolePermissions = gp.userRolePermissions;
|
||||||
ug.userRole = gp.userRole;
|
ug.userRole = gp.userRole;
|
||||||
|
@ -151,13 +151,8 @@ const useUserStore = defineStore('user', {
|
||||||
setToken(res.sessionId, res.csrfToken);
|
setToken(res.sessionId, res.csrfToken);
|
||||||
this.setInfo(res);
|
this.setInfo(res);
|
||||||
const { orgId, pId } = getHashParameters();
|
const { orgId, pId } = getHashParameters();
|
||||||
// 如果访问页面的时候携带了组织 ID和项目 ID,则不设置
|
appStore.setCurrentOrgId(forceSet ? res.lastOrganizationId || '' : orgId || '');
|
||||||
if (!orgId || forceSet) {
|
appStore.setCurrentProjectId(forceSet ? res.lastProjectId || '' : pId || '');
|
||||||
appStore.setCurrentOrgId(res.lastOrganizationId || '');
|
|
||||||
}
|
|
||||||
if (!pId || forceSet) {
|
|
||||||
appStore.setCurrentProjectId(res.lastProjectId || '');
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|
|
@ -86,7 +86,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between px-[12px] pt-[12px]">
|
<div class="flex items-center justify-between px-[12px] pt-[12px]">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a-tooltip :content="condition.name">
|
<a-tooltip v-if="condition.name" :content="condition.name">
|
||||||
<div class="script-name-container">
|
<div class="script-name-container">
|
||||||
<div class="one-line-text mr-[4px] max-w-[110px] font-medium text-[var(--color-text-1)]">
|
<div class="one-line-text mr-[4px] max-w-[110px] font-medium text-[var(--color-text-1)]">
|
||||||
{{ condition.name }}
|
{{ condition.name }}
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
isPriorityLocalExec,
|
isPriorityLocalExec,
|
||||||
execute,
|
execute,
|
||||||
localExecuteUrl,
|
localExecuteUrl,
|
||||||
|
hasLocalExec,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,9 @@
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
.code-container {
|
.code-container {
|
||||||
|
@apply overflow-y-auto;
|
||||||
|
.ms-scroll-bar();
|
||||||
|
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
border-radius: var(--border-radius-small);
|
border-radius: var(--border-radius-small);
|
||||||
|
|
|
@ -778,21 +778,24 @@
|
||||||
emitChange('addTableLine addLineDisabled', isInit);
|
emitChange('addTableLine addLineDisabled', isInit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (rowIndex === paramsData.value.length - 1) {
|
||||||
rowIndex === paramsData.value.length - 1 &&
|
// Don't change this!!!
|
||||||
(paramsData.value[rowIndex].key ||
|
|
||||||
paramsData.value[rowIndex].projectId ||
|
|
||||||
paramsData.value[rowIndex].header ||
|
|
||||||
paramsData.value[rowIndex].variableName ||
|
|
||||||
paramsData.value[rowIndex].expression)
|
|
||||||
) {
|
|
||||||
// 最后一行的更改才会触发添加新一行
|
// 最后一行的更改才会触发添加新一行
|
||||||
const id = new Date().getTime().toString();
|
const id = new Date().getTime().toString();
|
||||||
paramsData.value.push({
|
const lastLineData = paramsData.value[rowIndex]; // 上一行数据
|
||||||
|
const selectColumnKeys = props.columns.filter((e) => e.typeOptions).map((e) => e.dataIndex); // 找到下拉框选项的列
|
||||||
|
const nextLine = {
|
||||||
id,
|
id,
|
||||||
...cloneDeep(props.defaultParamItem), // 深拷贝,避免有嵌套引用类型,数据隔离
|
...cloneDeep(props.defaultParamItem), // 深拷贝,避免有嵌套引用类型,数据隔离
|
||||||
enable: true, // 是否勾选
|
enable: true, // 是否勾选
|
||||||
} as any);
|
} as any;
|
||||||
|
selectColumnKeys.forEach((key) => {
|
||||||
|
// 如果是更改了下拉框导致添加新的一列,需要将更改后的下拉框的值应用到下一行(产品为了方便统一输入参数类型)
|
||||||
|
if (key) {
|
||||||
|
nextLine[key] = lastLineData[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
paramsData.value.push(nextLine);
|
||||||
}
|
}
|
||||||
emitChange('addTableLine', isInit);
|
emitChange('addTableLine', isInit);
|
||||||
handleMustContainColChange(true);
|
handleMustContainColChange(true);
|
||||||
|
|
|
@ -108,7 +108,6 @@
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
import { TabItem } from '@/components/pure/ms-editable-tab/types';
|
||||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
|
||||||
import caseTable from '../case/caseTable.vue';
|
import caseTable from '../case/caseTable.vue';
|
||||||
// import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
// import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||||
import apiTable from './apiTable.vue';
|
import apiTable from './apiTable.vue';
|
||||||
|
@ -230,15 +229,15 @@
|
||||||
{
|
{
|
||||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||||
assertionConfig: {
|
assertionConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
assertions: [],
|
assertions: [],
|
||||||
},
|
},
|
||||||
postProcessorConfig: {
|
postProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
preProcessorConfig: {
|
preProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -169,15 +169,15 @@
|
||||||
{
|
{
|
||||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||||
assertionConfig: {
|
assertionConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
assertions: [],
|
assertions: [],
|
||||||
},
|
},
|
||||||
postProcessorConfig: {
|
postProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
preProcessorConfig: {
|
preProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -63,15 +63,14 @@ export default {
|
||||||
'apiTestManagement.importMode': 'Import mode',
|
'apiTestManagement.importMode': 'Import mode',
|
||||||
'apiTestManagement.importModeTip1': 'Cover:',
|
'apiTestManagement.importModeTip1': 'Cover:',
|
||||||
'apiTestManagement.importModeTip2':
|
'apiTestManagement.importModeTip2':
|
||||||
'1.If the request type + path is the same for the same interface, if the request parameter content is inconsistent, it will be overwritten.',
|
'1.The same interface already exists in the system (the request type + path are consistent). If the request parameter content is inconsistent, the original interface of the system will be overwritten.',
|
||||||
'apiTestManagement.importModeTip3':
|
'apiTestManagement.importModeTip3':
|
||||||
'2.The same interface request type + path are the same, and the request parameter content is the same and does not change.',
|
'2.The same interface already exists in the system (request type + path are consistent), if the content of the request parameters is consistent, no changes will be made.',
|
||||||
'apiTestManagement.importModeTip4':
|
'apiTestManagement.importModeTip4': '3.Interfaces that do not exist in the system are added.',
|
||||||
'3.If the interface is not the same and the request type + path is the same, add it',
|
|
||||||
'apiTestManagement.importModeTip5': 'Not covered:',
|
'apiTestManagement.importModeTip5': 'Not covered:',
|
||||||
'apiTestManagement.importModeTip6':
|
'apiTestManagement.importModeTip6':
|
||||||
'1.If the same interface request type + path are the same, no changes will be made.',
|
'1.If the same interface already exists in the system (the request type + path is the same), no changes will be made.',
|
||||||
'apiTestManagement.importModeTip7': '2.If the request type + path are not the same for the same interface, add a new',
|
'apiTestManagement.importModeTip7': '2.Interfaces that do not exist in the system are added.',
|
||||||
'apiTestManagement.cover': 'Cover',
|
'apiTestManagement.cover': 'Cover',
|
||||||
'apiTestManagement.uncover': 'Not covered',
|
'apiTestManagement.uncover': 'Not covered',
|
||||||
'apiTestManagement.moreSetting': 'More settings',
|
'apiTestManagement.moreSetting': 'More settings',
|
||||||
|
|
|
@ -60,12 +60,12 @@ export default {
|
||||||
'apiTestManagement.belongModule': '所属模块',
|
'apiTestManagement.belongModule': '所属模块',
|
||||||
'apiTestManagement.importMode': '导入模式',
|
'apiTestManagement.importMode': '导入模式',
|
||||||
'apiTestManagement.importModeTip1': '覆盖:',
|
'apiTestManagement.importModeTip1': '覆盖:',
|
||||||
'apiTestManagement.importModeTip2': '1.同一接口请求类型+路径一致,请求参数内容不一致则覆盖',
|
'apiTestManagement.importModeTip2': '1.系统已存在的同一接口(请求类型+路径一致),请求参数内容不一致则覆盖系统原接口',
|
||||||
'apiTestManagement.importModeTip3': '2.同一接口请求类型+路径一致,请求参数内容一致不做变更',
|
'apiTestManagement.importModeTip3': '2.系统已存在的同一接口(请求类型+路径一致),请求参数内容一致则不做变更',
|
||||||
'apiTestManagement.importModeTip4': '3.非同一接口,请求类型+路径一致,则新增',
|
'apiTestManagement.importModeTip4': '3.系统不存在的接口,则新增',
|
||||||
'apiTestManagement.importModeTip5': '不覆盖:',
|
'apiTestManagement.importModeTip5': '不覆盖:',
|
||||||
'apiTestManagement.importModeTip6': '1.同一接口请求类型+路径一致,则不做变更',
|
'apiTestManagement.importModeTip6': '1.系统已存在的同一接口(请求类型+路径一致),则不做变更',
|
||||||
'apiTestManagement.importModeTip7': '2.非同一接口请求类型+路径一致,则新增',
|
'apiTestManagement.importModeTip7': '2.系统不存在的接口,则新增',
|
||||||
'apiTestManagement.cover': '覆盖',
|
'apiTestManagement.cover': '覆盖',
|
||||||
'apiTestManagement.uncover': '不覆盖',
|
'apiTestManagement.uncover': '不覆盖',
|
||||||
'apiTestManagement.moreSetting': '更多设置',
|
'apiTestManagement.moreSetting': '更多设置',
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
no-content-padding
|
no-content-padding
|
||||||
:show-continue="true"
|
:show-continue="true"
|
||||||
:footer="requestVModel.isNew === true"
|
:footer="requestVModel.isNew === true"
|
||||||
|
:ok-disabled="requestVModel.executeLoading || (isHttpProtocol && !requestVModel.url)"
|
||||||
@confirm="handleSave"
|
@confirm="handleSave"
|
||||||
@continue="handleContinue"
|
@continue="handleContinue"
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
||||||
class="ml-auto flex items-center gap-[16px]"
|
class="customApiDrawer-title-right ml-auto flex items-center gap-[16px]"
|
||||||
>
|
>
|
||||||
<div class="text-[14px] font-normal text-[var(--color-text-4)]">
|
<div class="text-[14px] font-normal text-[var(--color-text-4)]">
|
||||||
{{ t('apiScenario.env', { name: currentEnvConfig?.name }) }}
|
{{ t('apiScenario.env', { name: currentEnvConfig?.name }) }}
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="requestVModel.customizeRequestEnvEnable"
|
v-model:model-value="requestVModel.customizeRequestEnvEnable"
|
||||||
class="w-[150px]"
|
class="w-[150px]"
|
||||||
|
popup-container=".customApiDrawer-title-right"
|
||||||
@change="handleUseEnvChange"
|
@change="handleUseEnvChange"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
|
@ -318,7 +320,7 @@
|
||||||
RequestResult,
|
RequestResult,
|
||||||
RequestTaskResult,
|
RequestTaskResult,
|
||||||
} from '@/models/apiTest/common';
|
} from '@/models/apiTest/common';
|
||||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepFileParams, ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
|
@ -366,6 +368,8 @@
|
||||||
unSaved: boolean;
|
unSaved: boolean;
|
||||||
uploadFileIds: string[];
|
uploadFileIds: string[];
|
||||||
linkFileIds: string[];
|
linkFileIds: string[];
|
||||||
|
deleteFileIds?: string[];
|
||||||
|
unLinkFileIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RequestParam = ExecuteApiRequestFullParams & {
|
export type RequestParam = ExecuteApiRequestFullParams & {
|
||||||
|
@ -384,6 +388,7 @@
|
||||||
update: string;
|
update: string;
|
||||||
};
|
};
|
||||||
stepResponses?: Record<string | number, RequestResult>;
|
stepResponses?: Record<string | number, RequestResult>;
|
||||||
|
fileParams?: ScenarioStepFileParams;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -399,6 +404,7 @@
|
||||||
// 注入祖先组件提供的属性
|
// 注入祖先组件提供的属性
|
||||||
const scenarioId = inject<string | number>('scenarioId');
|
const scenarioId = inject<string | number>('scenarioId');
|
||||||
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
||||||
|
const hasLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
|
||||||
const isPriorityLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
|
const isPriorityLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', { required: true });
|
const visible = defineModel<boolean>('visible', { required: true });
|
||||||
|
@ -439,15 +445,15 @@
|
||||||
{
|
{
|
||||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||||
assertionConfig: {
|
assertionConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
assertions: [],
|
assertions: [],
|
||||||
},
|
},
|
||||||
postProcessorConfig: {
|
postProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
preProcessorConfig: {
|
preProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -656,8 +662,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasLocalExec = ref(false); // 是否配置了api本地执行
|
|
||||||
|
|
||||||
const pluginScriptMap = ref<Record<string, PluginConfig>>({}); // 存储初始化过后的插件配置
|
const pluginScriptMap = ref<Record<string, PluginConfig>>({}); // 存储初始化过后的插件配置
|
||||||
const temporaryPluginFormMap: Record<string, any> = {}; // 缓存插件表单,避免切换传入的 API 数据导致动态表单数据丢失
|
const temporaryPluginFormMap: Record<string, any> = {}; // 缓存插件表单,避免切换传入的 API 数据导致动态表单数据丢失
|
||||||
const pluginLoading = ref(false);
|
const pluginLoading = ref(false);
|
||||||
|
@ -909,8 +913,8 @@
|
||||||
).validParams;
|
).validParams;
|
||||||
parseRequestBodyResult = parseRequestBodyFiles(
|
parseRequestBodyResult = parseRequestBodyFiles(
|
||||||
requestVModel.value.body,
|
requestVModel.value.body,
|
||||||
requestVModel.value.uploadFileIds, // 外面解析详情的时候传入
|
props.fileParams?.uploadFileIds || requestVModel.value.uploadFileIds, // 外面解析详情的时候传入,或引用 api 在requestVModel内存储
|
||||||
requestVModel.value.linkFileIds // 外面解析详情的时候传入
|
props.fileParams?.linkFileIds || requestVModel.value.linkFileIds // 外面解析详情的时候传入,或引用 api 在requestVModel内存储
|
||||||
);
|
);
|
||||||
requestParams = {
|
requestParams = {
|
||||||
authConfig: requestVModel.value.authConfig,
|
authConfig: requestVModel.value.authConfig,
|
||||||
|
|
|
@ -266,7 +266,7 @@
|
||||||
import { scrollIntoView } from '@/utils/dom';
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
import { ExecuteConditionConfig, PluginConfig, RequestResult } from '@/models/apiTest/common';
|
import { ExecuteConditionConfig, PluginConfig, RequestResult } from '@/models/apiTest/common';
|
||||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepFileParams, ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
RequestBodyFormat,
|
RequestBodyFormat,
|
||||||
|
@ -298,6 +298,7 @@
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
request?: RequestParam; // 请求参数集合
|
request?: RequestParam; // 请求参数集合
|
||||||
stepResponses?: Record<string | number, RequestResult>;
|
stepResponses?: Record<string | number, RequestResult>;
|
||||||
|
fileParams?: ScenarioStepFileParams;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'applyStep', request: RequestParam): void;
|
(e: 'applyStep', request: RequestParam): void;
|
||||||
|
@ -351,15 +352,15 @@
|
||||||
{
|
{
|
||||||
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
polymorphicName: 'MsCommonElement', // 协议多态名称,写死MsCommonElement
|
||||||
assertionConfig: {
|
assertionConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
assertions: [],
|
assertions: [],
|
||||||
},
|
},
|
||||||
postProcessorConfig: {
|
postProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
preProcessorConfig: {
|
preProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -797,11 +798,14 @@
|
||||||
defaultBodyParamsItem,
|
defaultBodyParamsItem,
|
||||||
isExecute
|
isExecute
|
||||||
).validParams;
|
).validParams;
|
||||||
parseRequestBodyResult = parseRequestBodyFiles(
|
if (activeStep.value?.refType === ScenarioStepRefType.COPY) {
|
||||||
requestVModel.value.body,
|
// 复制 case 才能编辑,才需要计算
|
||||||
requestVModel.value.uploadFileIds, // 外面解析详情的时候传入
|
parseRequestBodyResult = parseRequestBodyFiles(
|
||||||
requestVModel.value.linkFileIds // 外面解析详情的时候传入
|
requestVModel.value.body,
|
||||||
);
|
props.fileParams?.uploadFileIds || requestVModel.value.uploadFileIds, // 外面解析详情的时候传入,或引用 case 在requestVModel内存储
|
||||||
|
props.fileParams?.linkFileIds || requestVModel.value.linkFileIds // 外面解析详情的时候传入,或引用 case 在requestVModel内存储
|
||||||
|
);
|
||||||
|
}
|
||||||
requestParams = {
|
requestParams = {
|
||||||
authConfig: requestVModel.value.authConfig,
|
authConfig: requestVModel.value.authConfig,
|
||||||
body: {
|
body: {
|
||||||
|
@ -878,8 +882,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
// 关闭时若不是创建行为则是编辑行为,需要触发 applyStep
|
// 关闭时若不是创建行为则是编辑行为,需要触发 applyStep,引用 case 不能更改不需要触发
|
||||||
if (!requestVModel.value.isNew) {
|
if (!requestVModel.value.isNew && activeStep.value?.refType === ScenarioStepRefType.COPY) {
|
||||||
emit('applyStep', cloneDeep(makeRequestParams()));
|
emit('applyStep', cloneDeep(makeRequestParams()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,13 @@
|
||||||
* 获取复制或引用的步骤数据
|
* 获取复制或引用的步骤数据
|
||||||
* @param refType 复制或引用
|
* @param refType 复制或引用
|
||||||
*/
|
*/
|
||||||
async function getScenarioSteps(refType: ScenarioStepRefType.COPY | ScenarioStepRefType.REF) {
|
async function getScenarioSteps(
|
||||||
|
refType: ScenarioStepRefType.COPY | ScenarioStepRefType.REF,
|
||||||
|
other: {
|
||||||
|
api: MsTableDataItem<ApiDefinitionDetail>[];
|
||||||
|
case: MsTableDataItem<ApiCaseDetail>[];
|
||||||
|
}
|
||||||
|
) {
|
||||||
const scenarioMap: Record<string, MsTableDataItem<ApiScenarioTableItem>[]> = {};
|
const scenarioMap: Record<string, MsTableDataItem<ApiScenarioTableItem>[]> = {};
|
||||||
// 可以跨项目选择,但是接口的项目 id 是单个,所以需要按项目分组
|
// 可以跨项目选择,但是接口的项目 id 是单个,所以需要按项目分组
|
||||||
selectedScenarios.value.forEach((e) => {
|
selectedScenarios.value.forEach((e) => {
|
||||||
|
@ -236,18 +242,19 @@
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
copyFromStepId: node.id,
|
copyFromStepId: node.id,
|
||||||
|
originProjectId: node.projectId,
|
||||||
id: getGenerateId(),
|
id: getGenerateId(),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
name: `copy-${e.name}`,
|
|
||||||
copyFromStepId: e.resourceId,
|
copyFromStepId: e.resourceId,
|
||||||
|
originProjectId: e.projectId,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
emit(
|
emit(
|
||||||
'copy',
|
'copy',
|
||||||
cloneDeep({
|
cloneDeep({
|
||||||
api: selectedApis.value,
|
api: other.api,
|
||||||
case: selectedCases.value,
|
case: other.case,
|
||||||
scenario: fullScenarioArr,
|
scenario: fullScenarioArr,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -260,18 +267,20 @@
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
copyFromStepId: node.id,
|
copyFromStepId: node.id,
|
||||||
|
originProjectId: node.projectId,
|
||||||
id: getGenerateId(),
|
id: getGenerateId(),
|
||||||
isQuoteScenarioStep: true,
|
isQuoteScenarioStep: true,
|
||||||
isRefScenarioStep: true, // 默认是完全引用的
|
isRefScenarioStep: true, // 默认是完全引用的
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
originProjectId: e.projectId,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
emit(
|
emit(
|
||||||
'quote',
|
'quote',
|
||||||
cloneDeep({
|
cloneDeep({
|
||||||
api: selectedApis.value,
|
api: other.api,
|
||||||
case: selectedCases.value,
|
case: other.case,
|
||||||
scenario: fullScenarioArr,
|
scenario: fullScenarioArr,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -286,14 +295,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleCopy() {
|
async function handleCopy() {
|
||||||
|
const copyApis = selectedApis.value.map((e) => ({
|
||||||
|
...e,
|
||||||
|
originProjectId: e.projectId,
|
||||||
|
}));
|
||||||
|
const copyCases = selectedCases.value.map((e) => ({
|
||||||
|
...e,
|
||||||
|
originProjectId: e.projectId,
|
||||||
|
}));
|
||||||
if (selectedScenarios.value.length > 0) {
|
if (selectedScenarios.value.length > 0) {
|
||||||
await getScenarioSteps(ScenarioStepRefType.COPY);
|
await getScenarioSteps(ScenarioStepRefType.COPY, {
|
||||||
|
api: copyApis,
|
||||||
|
case: copyCases,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
emit(
|
emit(
|
||||||
'copy',
|
'copy',
|
||||||
cloneDeep({
|
cloneDeep({
|
||||||
api: selectedApis.value,
|
api: copyApis,
|
||||||
case: selectedCases.value,
|
case: copyCases,
|
||||||
scenario: selectedScenarios.value,
|
scenario: selectedScenarios.value,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -302,14 +322,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleQuote() {
|
async function handleQuote() {
|
||||||
|
const quoteApis = selectedApis.value.map((e) => ({
|
||||||
|
...e,
|
||||||
|
originProjectId: e.projectId,
|
||||||
|
}));
|
||||||
|
const quoteCases = selectedCases.value.map((e) => ({
|
||||||
|
...e,
|
||||||
|
originProjectId: e.projectId,
|
||||||
|
}));
|
||||||
if (selectedScenarios.value.length > 0) {
|
if (selectedScenarios.value.length > 0) {
|
||||||
await getScenarioSteps(ScenarioStepRefType.REF);
|
await getScenarioSteps(ScenarioStepRefType.REF, {
|
||||||
|
api: quoteApis,
|
||||||
|
case: quoteCases,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
emit(
|
emit(
|
||||||
'quote',
|
'quote',
|
||||||
cloneDeep({
|
cloneDeep({
|
||||||
api: selectedApis.value,
|
api: quoteApis,
|
||||||
case: selectedCases.value,
|
case: quoteCases,
|
||||||
scenario: selectedScenarios.value,
|
scenario: selectedScenarios.value,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
:width="960"
|
:width="960"
|
||||||
no-content-padding
|
no-content-padding
|
||||||
disabled-width-drag
|
disabled-width-drag
|
||||||
|
:footer="!props.detail"
|
||||||
|
@close="handleClose"
|
||||||
@cancel="handleDrawerCancel"
|
@cancel="handleDrawerCancel"
|
||||||
>
|
>
|
||||||
<div class="ml-[16px] mt-[10px]">
|
<div class="ml-[16px] mt-[10px]">
|
||||||
|
<!-- <stepTypeVue v-if="props.step" :step="props.step" /> -->
|
||||||
{{ t('apiScenario.scriptOperationName') }}
|
{{ t('apiScenario.scriptOperationName') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-[16px] mt-[3px] max-w-[70%]">
|
<div class="ml-[16px] mt-[3px] max-w-[70%]">
|
||||||
|
@ -19,9 +22,9 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[10px] flex h-[calc(100%-40px)] gap-[8px]">
|
<div class="mt-[10px] flex h-[calc(100%-40px)] gap-[8px]">
|
||||||
<conditionContent v-model:data="activeItem" :is-build-in="true" :is-format="true" />
|
<conditionContent v-if="visible" v-model:data="activeItem" :is-build-in="true" :is-format="true" />
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template v-if="!props.detail" #footer>
|
||||||
<a-button type="secondary" @click="handleDrawerCancel">
|
<a-button type="secondary" @click="handleDrawerCancel">
|
||||||
{{ t('common.cancel') }}
|
{{ t('common.cancel') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
@ -36,19 +39,29 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
import conditionContent from '@/views/api-test/components/condition/content.vue';
|
|
||||||
|
|
||||||
|
// import stepTypeVue from './stepType/stepType.vue';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
import { ExecuteConditionProcessor } from '@/models/apiTest/common';
|
import { ExecuteConditionProcessor } from '@/models/apiTest/common';
|
||||||
|
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
import { RequestConditionProcessor } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
const conditionContent = defineAsyncComponent(() => import('@/views/api-test/components/condition/content.vue'));
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
script?: ExecuteConditionProcessor;
|
detail?: ExecuteConditionProcessor;
|
||||||
|
step?: ScenarioStepItem;
|
||||||
name?: string;
|
name?: string;
|
||||||
}>();
|
}>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'add', name: string, scriptProcessor: ExecuteConditionProcessor): void;
|
||||||
|
(e: 'save', name: string, scriptProcessor: ExecuteConditionProcessor): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
const defaultScript = {
|
const defaultScript = {
|
||||||
processorType: RequestConditionProcessor.SCRIPT,
|
processorType: RequestConditionProcessor.SCRIPT,
|
||||||
|
@ -56,44 +69,45 @@
|
||||||
script: '',
|
script: '',
|
||||||
scriptLanguage: LanguageEnum.BEANSHELL_JSR233,
|
scriptLanguage: LanguageEnum.BEANSHELL_JSR233,
|
||||||
commonScriptInfo: {},
|
commonScriptInfo: {},
|
||||||
} as ExecuteConditionProcessor;
|
polymorphicName: 'MsScriptElement',
|
||||||
const scriptName = ref(props.name || '');
|
} as unknown as ExecuteConditionProcessor;
|
||||||
const activeItem = ref(props.script || defaultScript);
|
const scriptName = ref('');
|
||||||
|
const activeItem = ref<ExecuteConditionProcessor>(cloneDeep(defaultScript));
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', { required: true });
|
const visible = defineModel<boolean>('visible', { required: true });
|
||||||
|
|
||||||
const emit = defineEmits<{
|
watch(
|
||||||
(e: 'save', name: string, scriptProcessor: ExecuteConditionProcessor): void;
|
() => visible.value,
|
||||||
}>();
|
(val) => {
|
||||||
|
if (val) {
|
||||||
function resetField() {
|
scriptName.value = props.name || '';
|
||||||
scriptName.value = '';
|
activeItem.value = cloneDeep(props.detail || defaultScript);
|
||||||
activeItem.value = {
|
}
|
||||||
processorType: RequestConditionProcessor.SCRIPT,
|
}
|
||||||
enableCommonScript: false,
|
);
|
||||||
script: '',
|
|
||||||
scriptLanguage: LanguageEnum.BEANSHELL_JSR233,
|
|
||||||
commonScriptInfo: {},
|
|
||||||
} as ExecuteConditionProcessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDrawerCancel() {
|
function handleDrawerCancel() {
|
||||||
resetField();
|
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAndContinue() {
|
function saveAndContinue() {
|
||||||
emit('save', scriptName.value, activeItem.value);
|
emit('add', scriptName.value, activeItem.value);
|
||||||
resetField();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
emit('save', scriptName.value, activeItem.value);
|
emit('add', scriptName.value, activeItem.value);
|
||||||
resetField();
|
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
if (props.detail) {
|
||||||
|
emit('save', scriptName.value, activeItem.value);
|
||||||
|
}
|
||||||
|
scriptName.value = '';
|
||||||
|
activeItem.value = defaultScript as unknown as ExecuteConditionProcessor;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped></style>
|
||||||
|
|
|
@ -94,11 +94,11 @@ export const defaultScenario: Scenario = {
|
||||||
csvVariables: [],
|
csvVariables: [],
|
||||||
},
|
},
|
||||||
preProcessorConfig: {
|
preProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
postProcessorConfig: {
|
postProcessorConfig: {
|
||||||
enableGlobal: false,
|
enableGlobal: true,
|
||||||
processors: [],
|
processors: [],
|
||||||
},
|
},
|
||||||
assertionConfig: {
|
assertionConfig: {
|
||||||
|
@ -114,6 +114,7 @@ export const defaultScenario: Scenario = {
|
||||||
},
|
},
|
||||||
steps: [],
|
steps: [],
|
||||||
stepDetails: {},
|
stepDetails: {},
|
||||||
|
stepFileParam: {},
|
||||||
executeTime: 0,
|
executeTime: 0,
|
||||||
executeSuccessCount: 0,
|
executeSuccessCount: 0,
|
||||||
executeFailCount: 0,
|
executeFailCount: 0,
|
||||||
|
|
|
@ -116,9 +116,9 @@ export default function useCreateActions() {
|
||||||
if (item.id || item.resourceId) {
|
if (item.id || item.resourceId) {
|
||||||
// 引用复制接口、用例、场景时的源资源信息
|
// 引用复制接口、用例、场景时的源资源信息
|
||||||
resourceField = {
|
resourceField = {
|
||||||
resourceId: item.id || item.resourceId,
|
resourceId: item.id || item.resourceId, // 场景会调接口获取信息,所以有resourceId,接口、用例没有,下同
|
||||||
resourceNum: item.num,
|
resourceNum: item.num || item.resourceNum,
|
||||||
resourceName: item.name,
|
resourceName: item.name || item.resourceName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (item.protocol) {
|
if (item.protocol) {
|
||||||
|
@ -141,6 +141,7 @@ export default function useCreateActions() {
|
||||||
children: item.children || [],
|
children: item.children || [],
|
||||||
stepType,
|
stepType,
|
||||||
refType,
|
refType,
|
||||||
|
originProjectId: item.originProjectId,
|
||||||
copyFromStepId: item.copyFromStepId,
|
copyFromStepId: item.copyFromStepId,
|
||||||
...resourceField,
|
...resourceField,
|
||||||
name: name || item.name,
|
name: name || item.name,
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center gap-[4px]">
|
<div class="flex items-center gap-[4px]">
|
||||||
<!-- <a-popover position="bl" content-class="detail-popover" arrow-class="hidden">
|
<a-popover
|
||||||
|
v-if="
|
||||||
|
[ScenarioStepType.API, ScenarioStepType.API_CASE, ScenarioStepType.API_SCENARIO].includes(props.data.stepType)
|
||||||
|
"
|
||||||
|
position="bl"
|
||||||
|
content-class="detail-popover"
|
||||||
|
arrow-class="hidden"
|
||||||
|
>
|
||||||
<MsIcon type="icon-icon-draft" class="text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]" />
|
<MsIcon type="icon-icon-draft" class="text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]" />
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex flex-col gap-[16px]">
|
<div class="flex flex-col gap-[16px]">
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.belongProject') }}</div>
|
<div class="mb-[2px] text-[var(--color-text-4)]">{{ t('apiScenario.belongProject') }}</div>
|
||||||
<div class="text-[14px] text-[var(--color-text-1)]">
|
<div class="text-[14px] text-[var(--color-text-1)]">
|
||||||
{{ props.data.belongProjectName }}
|
<!-- {{ props.data.belongProjectName }} -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -18,9 +25,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-popover> -->
|
</a-popover>
|
||||||
<!-- <MsTag
|
<MsTag
|
||||||
v-if="props.data.projectId !== appStore.currentProjectId"
|
v-if="props.data.originProjectId !== appStore.currentProjectId"
|
||||||
theme="outline"
|
theme="outline"
|
||||||
size="small"
|
size="small"
|
||||||
:self-style="{
|
:self-style="{
|
||||||
|
@ -30,22 +37,23 @@
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ t('apiScenario.crossProject') }}
|
{{ t('apiScenario.crossProject') }}
|
||||||
</MsTag> -->
|
</MsTag>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
// import useOpenNewPage from '@/hooks/useOpenNewPage';
|
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
import { ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
// import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
import { ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
// import getStepType from '@/views/api-test/scenario/components/common/stepType/utils';
|
import getStepType from '@/views/api-test/scenario/components/common/stepType/utils';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: ScenarioStepItem;
|
data: ScenarioStepItem;
|
||||||
|
@ -53,27 +61,36 @@
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
// const { openNewPage } = useOpenNewPage();
|
const { openNewPage } = useOpenNewPage();
|
||||||
|
|
||||||
// function goDetail() {
|
function goDetail() {
|
||||||
// const _stepType = getStepType(props.data);
|
const _stepType = getStepType(props.data);
|
||||||
// switch (true) {
|
switch (true) {
|
||||||
// case _stepType.isCopyApi:
|
case _stepType.isCopyApi:
|
||||||
// case _stepType.isQuoteApi:
|
case _stepType.isQuoteApi:
|
||||||
// openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, { dId: props.data.id, pId: props.data.projectId });
|
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
|
||||||
// break;
|
pId: props.data.originProjectId,
|
||||||
// case _stepType.isCopyScenario:
|
dId: props.data.resourceId,
|
||||||
// case _stepType.isQuoteScenario:
|
});
|
||||||
// openNewPage(ApiTestRouteEnum.API_TEST_SCENARIO, { sId: props.data.id, pId: props.data.projectId });
|
break;
|
||||||
// break;
|
case _stepType.isCopyScenario:
|
||||||
// case _stepType.isQuoteCase:
|
case _stepType.isQuoteScenario:
|
||||||
// case _stepType.isCopyCase:
|
openNewPage(ApiTestRouteEnum.API_TEST_SCENARIO, {
|
||||||
// openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, { cId: props.data.id, pId: props.data.projectId });
|
pId: props.data.originProjectId,
|
||||||
// break;
|
sId: props.data.resourceId,
|
||||||
// default:
|
});
|
||||||
// break;
|
break;
|
||||||
// }
|
case _stepType.isQuoteCase:
|
||||||
// }
|
case _stepType.isCopyCase:
|
||||||
|
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
|
||||||
|
pId: props.data.originProjectId,
|
||||||
|
cId: props.data.resourceId,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
<!-- 步骤差异内容,按步骤类型展示不同组件 -->
|
<!-- 步骤差异内容,按步骤类型展示不同组件 -->
|
||||||
<component
|
<component
|
||||||
:is="getStepContent(step)"
|
:is="getStepContent(step)"
|
||||||
:data="step.config"
|
:data="checkStepIsApi(step) || step.stepType === ScenarioStepType.API_SCENARIO ? step : step.config"
|
||||||
:step-id="step.id"
|
:step-id="step.id"
|
||||||
@quick-input="setQuickInput(step, $event)"
|
@quick-input="setQuickInput(step, $event)"
|
||||||
@change="handleStepContentChange($event, step)"
|
@change="handleStepContentChange($event, step)"
|
||||||
|
@ -240,7 +240,8 @@
|
||||||
</createStepActions>
|
</createStepActions>
|
||||||
<customApiDrawer
|
<customApiDrawer
|
||||||
v-model:visible="customApiDrawerVisible"
|
v-model:visible="customApiDrawerVisible"
|
||||||
:request="currentStepDetail"
|
:request="currentStepDetail as unknown as RequestParam"
|
||||||
|
:file-params="currentStepFileParams"
|
||||||
:step="activeStep"
|
:step="activeStep"
|
||||||
:step-responses="scenario.stepResponses"
|
:step-responses="scenario.stepResponses"
|
||||||
@add-step="addCustomApiStep"
|
@add-step="addCustomApiStep"
|
||||||
|
@ -251,7 +252,8 @@
|
||||||
<customCaseDrawer
|
<customCaseDrawer
|
||||||
v-model:visible="customCaseDrawerVisible"
|
v-model:visible="customCaseDrawerVisible"
|
||||||
:active-step="activeStep"
|
:active-step="activeStep"
|
||||||
:request="currentStepDetail"
|
:request="currentStepDetail as unknown as RequestParam"
|
||||||
|
:file-params="currentStepFileParams"
|
||||||
:step-responses="scenario.stepResponses"
|
:step-responses="scenario.stepResponses"
|
||||||
@apply-step="applyApiStep"
|
@apply-step="applyApiStep"
|
||||||
@delete-step="deleteCaseStep(activeStep)"
|
@delete-step="deleteCaseStep(activeStep)"
|
||||||
|
@ -265,11 +267,12 @@
|
||||||
@quote="handleImportApiApply('quote', $event)"
|
@quote="handleImportApiApply('quote', $event)"
|
||||||
/>
|
/>
|
||||||
<scriptOperationDrawer
|
<scriptOperationDrawer
|
||||||
v-if="scriptOperationDrawerVisible"
|
|
||||||
v-model:visible="scriptOperationDrawerVisible"
|
v-model:visible="scriptOperationDrawerVisible"
|
||||||
:script="currentStepDetail"
|
:detail="currentStepDetail as unknown as ExecuteConditionProcessor"
|
||||||
|
:step="activeStep"
|
||||||
:name="activeStep?.name"
|
:name="activeStep?.name"
|
||||||
@save="addScriptStep"
|
@add="addScriptStep"
|
||||||
|
@save="saveScriptStep"
|
||||||
/>
|
/>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="showQuickInput"
|
v-model:visible="showQuickInput"
|
||||||
|
@ -406,7 +409,6 @@
|
||||||
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 responseResult from '@/views/api-test/components/requestComposition/response/index.vue';
|
|
||||||
|
|
||||||
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
import { localExecuteApiDebug } from '@/api/modules/api-test/common';
|
||||||
import { debugScenario, getScenarioStep } from '@/api/modules/api-test/scenario';
|
import { debugScenario, getScenarioStep } from '@/api/modules/api-test/scenario';
|
||||||
|
@ -429,6 +431,8 @@
|
||||||
CreateStepAction,
|
CreateStepAction,
|
||||||
Scenario,
|
Scenario,
|
||||||
ScenarioStepConfig,
|
ScenarioStepConfig,
|
||||||
|
ScenarioStepDetails,
|
||||||
|
ScenarioStepFileParams,
|
||||||
ScenarioStepItem,
|
ScenarioStepItem,
|
||||||
} from '@/models/apiTest/scenario';
|
} from '@/models/apiTest/scenario';
|
||||||
import { EnvConfig } from '@/models/projectManagement/environmental';
|
import { EnvConfig } from '@/models/projectManagement/environmental';
|
||||||
|
@ -442,6 +446,7 @@
|
||||||
|
|
||||||
import type { RequestParam } from '../common/customApiDrawer.vue';
|
import type { RequestParam } from '../common/customApiDrawer.vue';
|
||||||
import useCreateActions from './createAction/useCreateActions';
|
import useCreateActions from './createAction/useCreateActions';
|
||||||
|
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';
|
||||||
|
|
||||||
|
@ -451,6 +456,9 @@
|
||||||
const customCaseDrawer = defineAsyncComponent(() => import('../common/customCaseDrawer.vue'));
|
const customCaseDrawer = defineAsyncComponent(() => import('../common/customCaseDrawer.vue'));
|
||||||
const importApiDrawer = defineAsyncComponent(() => import('../common/importApiDrawer/index.vue'));
|
const importApiDrawer = defineAsyncComponent(() => import('../common/importApiDrawer/index.vue'));
|
||||||
const scriptOperationDrawer = defineAsyncComponent(() => import('../common/scriptOperationDrawer.vue'));
|
const scriptOperationDrawer = defineAsyncComponent(() => import('../common/scriptOperationDrawer.vue'));
|
||||||
|
const responseResult = defineAsyncComponent(
|
||||||
|
() => import('@/views/api-test/components/requestComposition/response/index.vue')
|
||||||
|
);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
stepKeyword: string;
|
stepKeyword: string;
|
||||||
|
@ -470,13 +478,14 @@
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
// 步骤详情映射,存储部分抽屉展示详情的数据
|
// 步骤详情映射,存储部分抽屉展示详情的数据
|
||||||
const stepDetails = defineModel<Record<string, any>>('stepDetails', {
|
const stepDetails = defineModel<Record<string, ScenarioStepDetails>>('stepDetails', {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
const scenario = defineModel<Scenario>('scenario', {
|
const scenario = defineModel<Scenario>('scenario', {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
const isPriorityLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
|
const isPriorityLocalExec = inject<Ref<boolean>>('isPriorityLocalExec');
|
||||||
|
const localExecuteUrl = inject<Ref<string>>('localExecuteUrl');
|
||||||
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
const currentEnvConfig = inject<Ref<EnvConfig>>('currentEnvConfig');
|
||||||
|
|
||||||
const selectedKeys = ref<(string | number)[]>([]); // 没啥用,目前用来展示选中样式
|
const selectedKeys = ref<(string | number)[]>([]); // 没啥用,目前用来展示选中样式
|
||||||
|
@ -510,11 +519,10 @@
|
||||||
* 根据步骤类型获取步骤内容组件
|
* 根据步骤类型获取步骤内容组件
|
||||||
*/
|
*/
|
||||||
function getStepContent(step: ScenarioStepItem) {
|
function getStepContent(step: ScenarioStepItem) {
|
||||||
const _stepType = getStepType(step);
|
|
||||||
if (_stepType.isQuoteApi || _stepType.isQuoteCase || _stepType.isQuoteScenario) {
|
|
||||||
return quoteContent;
|
|
||||||
}
|
|
||||||
switch (step.stepType) {
|
switch (step.stepType) {
|
||||||
|
case ScenarioStepType.API:
|
||||||
|
case ScenarioStepType.API_CASE:
|
||||||
|
case ScenarioStepType.API_SCENARIO:
|
||||||
case ScenarioStepType.CUSTOM_REQUEST:
|
case ScenarioStepType.CUSTOM_REQUEST:
|
||||||
return quoteContent;
|
return quoteContent;
|
||||||
case ScenarioStepType.LOOP_CONTROLLER:
|
case ScenarioStepType.LOOP_CONTROLLER:
|
||||||
|
@ -741,11 +749,16 @@
|
||||||
case 'copy':
|
case 'copy':
|
||||||
const id = getGenerateId();
|
const id = getGenerateId();
|
||||||
const stepDetail = stepDetails.value[node.id];
|
const stepDetail = stepDetails.value[node.id];
|
||||||
|
const stepFileParam = scenario.value.stepFileParam[node.id];
|
||||||
const { isQuoteScenario } = getStepType(node as ScenarioStepItem);
|
const { isQuoteScenario } = getStepType(node as ScenarioStepItem);
|
||||||
if (stepDetail) {
|
if (stepDetail) {
|
||||||
// 如果复制的步骤还有详情数据,则也复制详情数据
|
// 如果复制的步骤还有详情数据,则也复制详情数据
|
||||||
stepDetails.value[id] = cloneDeep(stepDetail);
|
stepDetails.value[id] = cloneDeep(stepDetail);
|
||||||
}
|
}
|
||||||
|
if (stepFileParam) {
|
||||||
|
// 如果复制的步骤还有详情数据,则也复制详情数据
|
||||||
|
scenario.value.stepFileParam[id] = cloneDeep(stepFileParam);
|
||||||
|
}
|
||||||
insertNodes<ScenarioStepItem>(
|
insertNodes<ScenarioStepItem>(
|
||||||
steps.value,
|
steps.value,
|
||||||
node.id,
|
node.id,
|
||||||
|
@ -754,11 +767,16 @@
|
||||||
mapTree<ScenarioStepItem>(node, (childNode) => {
|
mapTree<ScenarioStepItem>(node, (childNode) => {
|
||||||
const childId = getGenerateId();
|
const childId = getGenerateId();
|
||||||
const childStepDetail = stepDetails.value[node.id];
|
const childStepDetail = stepDetails.value[node.id];
|
||||||
|
const childStepFileParam = scenario.value.stepFileParam[node.id];
|
||||||
let childCopyFromStepId = childNode.id;
|
let childCopyFromStepId = childNode.id;
|
||||||
if (childStepDetail) {
|
if (childStepDetail) {
|
||||||
// 如果复制的步骤下子步骤还有详情数据,则也复制详情数据
|
// 如果复制的步骤下子步骤还有详情数据,则也复制详情数据
|
||||||
stepDetails.value[childId] = cloneDeep(childStepDetail);
|
stepDetails.value[childId] = cloneDeep(childStepDetail);
|
||||||
}
|
}
|
||||||
|
if (childStepFileParam) {
|
||||||
|
// 如果复制的步骤还有详情数据,则也复制详情数据
|
||||||
|
scenario.value.stepFileParam[id] = cloneDeep(childStepFileParam);
|
||||||
|
}
|
||||||
if (!isQuoteScenario) {
|
if (!isQuoteScenario) {
|
||||||
// 非引用场景才处理复制来源 id
|
// 非引用场景才处理复制来源 id
|
||||||
if (childStepDetail || (childNode.isNew && childNode.stepRefType === ScenarioStepRefType.REF)) {
|
if (childStepDetail || (childNode.isNew && childNode.stepRefType === ScenarioStepRefType.REF)) {
|
||||||
|
@ -892,24 +910,33 @@
|
||||||
const customApiDrawerVisible = ref(false);
|
const customApiDrawerVisible = ref(false);
|
||||||
const scriptOperationDrawerVisible = ref(false);
|
const scriptOperationDrawerVisible = ref(false);
|
||||||
const activeCreateAction = ref<CreateStepAction>(); // 用于抽屉操作创建步骤时记录当前插入类型
|
const activeCreateAction = ref<CreateStepAction>(); // 用于抽屉操作创建步骤时记录当前插入类型
|
||||||
const currentStepDetail = computed<any>(() => {
|
const currentStepDetail = computed<ScenarioStepDetails | undefined>(() => {
|
||||||
// TODO: 步骤详情类型
|
|
||||||
if (activeStep.value) {
|
if (activeStep.value) {
|
||||||
return stepDetails.value[activeStep.value.id];
|
return stepDetails.value[activeStep.value.id];
|
||||||
}
|
}
|
||||||
return undefined;
|
});
|
||||||
|
const currentStepFileParams = computed<ScenarioStepFileParams | undefined>(() => {
|
||||||
|
if (activeStep.value) {
|
||||||
|
return scenario.value.stepFileParam[activeStep.value.id];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getStepDetail(step: ScenarioStepItem) {
|
async function getStepDetail(step: ScenarioStepItem) {
|
||||||
try {
|
try {
|
||||||
appStore.showLoading();
|
appStore.showLoading();
|
||||||
const res = await getScenarioStep(step.copyFromStepId || step.id);
|
const res = await getScenarioStep(step.copyFromStepId || step.id);
|
||||||
|
let parseRequestBodyResult;
|
||||||
|
if (step.config.protocol === 'HTTP' && res.body) {
|
||||||
|
parseRequestBodyResult = parseRequestBodyFiles(res.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||||
|
}
|
||||||
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;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -947,6 +974,13 @@
|
||||||
customCaseDrawerVisible.value = true;
|
customCaseDrawerVisible.value = true;
|
||||||
} else if (step.stepType === ScenarioStepType.SCRIPT) {
|
} else if (step.stepType === ScenarioStepType.SCRIPT) {
|
||||||
activeStep.value = step;
|
activeStep.value = step;
|
||||||
|
if (
|
||||||
|
(stepDetails.value[step.id] === undefined && step.copyFromStepId) ||
|
||||||
|
(stepDetails.value[step.id] === undefined && !step.isNew)
|
||||||
|
) {
|
||||||
|
// 查看场景详情时,详情映射中没有对应数据,初始化步骤详情(复制的步骤没有加载详情前就被复制,打开复制后的步骤就初始化被复制步骤的详情)
|
||||||
|
await getStepDetail(step);
|
||||||
|
}
|
||||||
scriptOperationDrawerVisible.value = true;
|
scriptOperationDrawerVisible.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -971,16 +1005,11 @@
|
||||||
/**
|
/**
|
||||||
* 开启websocket监听,接收执行结果
|
* 开启websocket监听,接收执行结果
|
||||||
*/
|
*/
|
||||||
function debugSocket(
|
function debugSocket(step: ScenarioStepItem, reportId: string | number, executeType?: 'localExec' | 'serverExec') {
|
||||||
step: ScenarioStepItem,
|
|
||||||
reportId: string | number,
|
|
||||||
executeType?: 'localExec' | 'serverExec',
|
|
||||||
localExecuteUrl?: string
|
|
||||||
) {
|
|
||||||
websocketMap[reportId] = getSocket(
|
websocketMap[reportId] = getSocket(
|
||||||
reportId || '',
|
reportId || '',
|
||||||
executeType === 'localExec' ? '/ws/debug' : '',
|
executeType === 'localExec' ? '/ws/debug' : '',
|
||||||
executeType === 'localExec' ? localExecuteUrl : ''
|
executeType === 'localExec' ? localExecuteUrl?.value : ''
|
||||||
);
|
);
|
||||||
websocketMap[reportId].addEventListener('message', (event) => {
|
websocketMap[reportId].addEventListener('message', (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
|
@ -1022,13 +1051,12 @@
|
||||||
ApiScenarioDebugRequest,
|
ApiScenarioDebugRequest,
|
||||||
'steps' | 'stepDetails' | 'reportId' | 'uploadFileIds' | 'linkFileIds'
|
'steps' | 'stepDetails' | 'reportId' | 'uploadFileIds' | 'linkFileIds'
|
||||||
>,
|
>,
|
||||||
executeType?: 'localExec' | 'serverExec',
|
executeType?: 'localExec' | 'serverExec'
|
||||||
localExecuteUrl?: string
|
|
||||||
) {
|
) {
|
||||||
const [currentStep] = executeParams.steps;
|
const [currentStep] = executeParams.steps;
|
||||||
try {
|
try {
|
||||||
currentStep.isExecuting = true;
|
currentStep.isExecuting = true;
|
||||||
debugSocket(currentStep, executeParams.reportId, executeType, localExecuteUrl); // 开启websocket
|
debugSocket(currentStep, executeParams.reportId, executeType); // 开启websocket
|
||||||
const res = await debugScenario({
|
const res = await debugScenario({
|
||||||
id: scenario.value.id || '',
|
id: scenario.value.id || '',
|
||||||
grouped: false,
|
grouped: false,
|
||||||
|
@ -1044,8 +1072,8 @@
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (executeType === 'localExec' && localExecuteUrl) {
|
if (executeType === 'localExec' && localExecuteUrl?.value) {
|
||||||
await localExecuteApiDebug(localExecuteUrl, res);
|
await localExecuteApiDebug(localExecuteUrl.value, res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
@ -1073,15 +1101,16 @@
|
||||||
}
|
}
|
||||||
const stepDetail = stepDetails.value[realStep.id];
|
const stepDetail = stepDetails.value[realStep.id];
|
||||||
delete scenario.value.stepResponses[realStep.id]; // 先移除上一次的执行结果
|
delete scenario.value.stepResponses[realStep.id]; // 先移除上一次的执行结果
|
||||||
|
const stepFileParam = scenario.value.stepFileParam[realStep.id];
|
||||||
realExecute(
|
realExecute(
|
||||||
{
|
{
|
||||||
steps: [realStep as ScenarioStepItem],
|
steps: [realStep as ScenarioStepItem],
|
||||||
stepDetails: {
|
stepDetails: {
|
||||||
[realStep.id]: stepDetails.value[realStep.id],
|
[realStep.id]: stepDetail,
|
||||||
},
|
},
|
||||||
reportId: realStep.reportId,
|
reportId: realStep.reportId,
|
||||||
uploadFileIds: stepDetail?.uploadFileIds || [],
|
uploadFileIds: stepFileParam?.uploadFileIds || [],
|
||||||
linkFileIds: stepDetail?.linkFileIds || [],
|
linkFileIds: stepFileParam?.linkFileIds || [],
|
||||||
},
|
},
|
||||||
isPriorityLocalExec?.value ? 'localExec' : 'serverExec'
|
isPriorityLocalExec?.value ? 'localExec' : 'serverExec'
|
||||||
);
|
);
|
||||||
|
@ -1245,6 +1274,12 @@
|
||||||
customizeRequest: true,
|
customizeRequest: true,
|
||||||
customizeRequestEnvEnable: request.customizeRequestEnvEnable,
|
customizeRequestEnvEnable: request.customizeRequestEnvEnable,
|
||||||
};
|
};
|
||||||
|
scenario.value.stepFileParam[request.stepId] = {
|
||||||
|
linkFileIds: request.linkFileIds,
|
||||||
|
uploadFileIds: request.uploadFileIds,
|
||||||
|
deleteFileIds: request.deleteFileIds,
|
||||||
|
unLinkFileIds: request.unLinkFileIds,
|
||||||
|
};
|
||||||
emit('updateResource', request.uploadFileIds, request.linkFileIds);
|
emit('updateResource', request.uploadFileIds, request.linkFileIds);
|
||||||
if (activeStep.value && activeCreateAction.value) {
|
if (activeStep.value && activeCreateAction.value) {
|
||||||
handleCreateStep(
|
handleCreateStep(
|
||||||
|
@ -1287,7 +1322,13 @@
|
||||||
}
|
}
|
||||||
if (activeStep.value) {
|
if (activeStep.value) {
|
||||||
request.isNew = false;
|
request.isNew = false;
|
||||||
stepDetails.value[activeStep.value?.id] = request;
|
stepDetails.value[activeStep.value.id] = request;
|
||||||
|
scenario.value.stepFileParam[activeStep.value?.id] = {
|
||||||
|
linkFileIds: request.linkFileIds,
|
||||||
|
uploadFileIds: request.uploadFileIds,
|
||||||
|
deleteFileIds: request.deleteFileIds,
|
||||||
|
unLinkFileIds: request.unLinkFileIds,
|
||||||
|
};
|
||||||
emit('updateResource', request.uploadFileIds, request.linkFileIds);
|
emit('updateResource', request.uploadFileIds, request.linkFileIds);
|
||||||
activeStep.value = undefined;
|
activeStep.value = undefined;
|
||||||
}
|
}
|
||||||
|
@ -1337,6 +1378,14 @@
|
||||||
scenario.value.unSaved = true;
|
scenario.value.unSaved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveScriptStep(name: string, scriptProcessor: ExecuteConditionProcessor) {
|
||||||
|
if (activeStep.value) {
|
||||||
|
stepDetails.value[activeStep.value.id] = cloneDeep(scriptProcessor);
|
||||||
|
activeStep.value.name = name;
|
||||||
|
activeStep.value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 释放允许拖拽步骤到释放的节点内
|
* 释放允许拖拽步骤到释放的节点内
|
||||||
* @param dropNode 释放节点
|
* @param dropNode 释放节点
|
||||||
|
|
|
@ -529,6 +529,7 @@
|
||||||
try {
|
try {
|
||||||
appStore.showLoading();
|
appStore.showLoading();
|
||||||
const res = await getScenarioDetail(typeof record === 'string' ? record : record.id);
|
const res = await getScenarioDetail(typeof record === 'string' ? record : record.id);
|
||||||
|
res.stepFileParam = {};
|
||||||
res.stepDetails = {};
|
res.stepDetails = {};
|
||||||
if (!res.steps) {
|
if (!res.steps) {
|
||||||
res.steps = [];
|
res.steps = [];
|
||||||
|
@ -551,11 +552,15 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hasLocalExec = computed(() => executeButtonRef.value?.hasLocalExec);
|
||||||
|
const localExecuteUrl = computed(() => executeButtonRef.value?.localExecuteUrl);
|
||||||
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
|
const isPriorityLocalExec = computed(() => executeButtonRef.value?.isPriorityLocalExec);
|
||||||
const scenarioId = computed(() => activeScenarioTab.value.id);
|
const scenarioId = computed(() => activeScenarioTab.value.id);
|
||||||
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
|
const scenarioExecuteLoading = computed(() => activeScenarioTab.value.executeLoading);
|
||||||
// 为子孙组件提供属性
|
// 为子孙组件提供属性
|
||||||
provide('isPriorityLocalExec', readonly(isPriorityLocalExec));
|
provide('isPriorityLocalExec', readonly(isPriorityLocalExec));
|
||||||
|
provide('hasLocalExec', readonly(hasLocalExec));
|
||||||
|
provide('localExecuteUrl', readonly(localExecuteUrl));
|
||||||
provide('currentEnvConfig', readonly(currentEnvConfig));
|
provide('currentEnvConfig', readonly(currentEnvConfig));
|
||||||
provide('scenarioId', scenarioId);
|
provide('scenarioId', scenarioId);
|
||||||
provide('scenarioExecuteLoading', scenarioExecuteLoading);
|
provide('scenarioExecuteLoading', scenarioExecuteLoading);
|
||||||
|
|
|
@ -808,11 +808,11 @@
|
||||||
:deep(.apiFieldIdClass) {
|
:deep(.apiFieldIdClass) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.systemFieldWrapper) {
|
:deep(.systemFieldWrapper) {
|
||||||
.arco-form-item {
|
.arco-form-item {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
@ -821,11 +821,11 @@
|
||||||
background: var(--color-text-n9);
|
background: var(--color-text-n9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagWrapper {
|
.tagWrapper {
|
||||||
.arco-form-item {
|
.arco-form-item {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
Loading…
Reference in New Issue