fix(接口测试): csv文件类型限制&场景步骤调整

This commit is contained in:
baiqi 2024-05-17 14:43:30 +08:00 committed by Craftsman
parent 586417e1bf
commit b84a874aa9
12 changed files with 234 additions and 214 deletions

View File

@ -8,7 +8,7 @@
<template #content>
<MsUpload
v-model:file-list="innerFileList"
accept="none"
:accept="props.accept || 'none'"
:auto-upload="false"
:show-file-list="false"
:limit="50"
@ -38,12 +38,13 @@
<script setup lang="ts">
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import MsUpload from '@/components/pure/ms-upload/index.vue';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import { MsFileItem, UploadType } from '@/components/pure/ms-upload/types';
import { useI18n } from '@/hooks/useI18n';
const props = defineProps<{
disabled?: boolean;
accept?: UploadType;
}>();
const emit = defineEmits<{

View File

@ -51,6 +51,7 @@
<dropdownMenu
:file-list="fileList"
:disabled="props.disabled"
:accept="props.accept"
@link-file="associatedFile"
@change="handleChange"
/>
@ -186,7 +187,12 @@
</a-popover>
</div>
<div v-else class="flex w-full items-center gap-[4px]">
<dropdownMenu :disabled="props.disabled" @link-file="associatedFile" @change="handleChange" />
<dropdownMenu
:accept="props.accept"
:disabled="props.disabled"
@link-file="associatedFile"
@change="handleChange"
/>
<a-input
v-model:model-value="inputFileName"
:disabled="props.disabled"
@ -206,6 +212,7 @@
:get-list-request="getAssociatedFileListUrl"
:get-list-fun-params="getListFunParams"
:selector-type="props.multiple ? 'checkbox' : 'radio'"
:filetype="props.accept"
@save="saveSelectAssociatedFile"
/>
</template>
@ -218,7 +225,7 @@
import MsTag, { Size } from '@/components/pure/ms-tag/ms-tag.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import MsUpload from '@/components/pure/ms-upload/index.vue';
import type { MsFileItem } from '@/components/pure/ms-upload/types';
import type { MsFileItem, UploadType } from '@/components/pure/ms-upload/types';
import LinkFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
import dropdownMenu from './dropdownMenu.vue';
import saveAsFilePopover from './saveAsFilePopover.vue';
@ -237,6 +244,7 @@
defineProps<{
disabled?: boolean;
mode?: 'button' | 'input';
accept?: UploadType;
multiple?: boolean;
inputClass?: string;
inputSize?: 'small' | 'medium' | 'large' | 'mini';

View File

@ -73,6 +73,7 @@
:get-list-fun-params="props.getListFunParams"
:selector-type="props.selectorType"
:file-all-count-by-storage="fileAllCountByStorage"
:filetype="props.filetype"
@init="handleModuleTableInit"
/>
</template>
@ -105,6 +106,7 @@
getListRequest: (params: TableQueryParams) => Promise<CommonList<AssociatedList>>; //
getListFunParams: TableQueryParams; // id
selectorType?: 'none' | 'checkbox' | 'radio';
filetype?: string;
}>();
const emit = defineEmits<{

View File

@ -6,7 +6,13 @@
><span class="ml-[4px] text-[var(--color-text-4)]">({{ moduleInfo.count }})</span></div
>
<div class="header-right">
<a-select v-model="tableFileType" class="w-[240px]" :loading="fileTypeLoading" @change="searchList">
<a-select
v-model="tableFileType"
class="w-[240px]"
:loading="fileTypeLoading"
:disabled="!!props.filetype"
@change="searchList"
>
<a-option key="" value="">{{ t('common.all') }}</a-option>
<a-option v-for="item of tableFileTypeOptions" :key="item" :value="item">
{{ item }}
@ -91,6 +97,7 @@
getListFunParams: TableQueryParams; //
selectorType?: 'none' | 'checkbox' | 'radio';
fileAllCountByStorage: number;
filetype?: string;
}>();
const emit = defineEmits<{
(e: 'init', params: FileListQueryParams): void;
@ -98,7 +105,7 @@
}>();
const tableFileTypeOptions = ref<string[]>([]);
const tableFileType = ref(''); //
const tableFileType = ref(props.filetype || ''); //
const keyword = ref('');
const fileTypeLoading = ref(false);
const fileType = ref('module'); // /
@ -249,7 +256,7 @@
} else {
res = await getFileTypes(appStore.currentProjectId);
}
tableFileType.value = '';
tableFileType.value = props.filetype || '';
tableFileTypeOptions.value = res;
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -260,6 +260,7 @@
id: 'fileId',
name: 'fileAlias',
}"
:accept="columnConfig.accept"
:file-save-as-source-id="props.fileSaveAsSourceId"
:file-save-as-api="props.fileSaveAsApi"
:file-module-options-api="props.fileModuleOptionsApi"
@ -280,7 +281,7 @@
/>
</template>
<!-- 文件 -->
<template #file="{ record, rowIndex }">
<template #file="{ record, rowIndex, columnConfig }">
<MsAddAttachment
:file-list="[record.file]"
:disabled="props.disabledParamValue"
@ -290,6 +291,7 @@
id: 'fileId',
name: 'fileAlias',
}"
:accept="columnConfig.accept"
:file-save-as-source-id="props.fileSaveAsSourceId"
:file-save-as-api="props.fileSaveAsApi"
:file-module-options-api="props.fileModuleOptionsApi"
@ -619,7 +621,7 @@
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import MsTagsGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import { MsFileItem, UploadType } from '@/components/pure/ms-upload/types';
import MsSelect from '@/components/business/ms-select/index';
import paramDescInput from './paramDescInput.vue';
import DomainModal from '@/views/project-management/environmental/components/envParams/popUp/domain.vue';
@ -656,6 +658,7 @@
format?: RequestBodyFormat; // operation
addLineDisabled?: boolean; //
disabledColumn?: boolean; //
accept?: UploadType; // accept
}
const props = withDefaults(

View File

@ -169,6 +169,7 @@
title: 'apiScenario.params.file',
dataIndex: 'file',
slotName: 'file',
accept: 'csv',
},
{
title: 'apiScenario.table.columns.status',

View File

@ -30,6 +30,7 @@
{{ title }}
</div>
<MsIcon
v-if="!props.step || !props.step.isQuoteScenarioStep"
type="icon-icon_edit_outlined"
class="cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="isShowEditStepNameInput = true"

View File

@ -35,6 +35,7 @@
>
</a-tooltip>
<MsIcon
v-if="!activeStep || !activeStep.isQuoteScenarioStep"
type="icon-icon_edit_outlined"
class="cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="showEditScriptNameInput"

View File

@ -480,7 +480,6 @@
import saveAsApiModal from '@/views/api-test/components/saveAsApiModal.vue';
import { addCase, getDefinitionDetail } from '@/api/modules/api-test/management';
import { getScenarioDetail, getScenarioStep } from '@/api/modules/api-test/scenario';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useAppStore from '@/store/modules/app';
@ -495,7 +494,6 @@
import {
CreateStepAction,
Scenario,
ScenarioStepConfig,
ScenarioStepDetails,
ScenarioStepFileParams,
ScenarioStepItem,
@ -514,7 +512,6 @@
import useStepNodeEdit from './useStepNodeEdit';
import useStepOperation from './useStepOperation';
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';
@ -728,151 +725,68 @@
return stepMoreActions;
}
const scenarioConfigForm = ref<
ScenarioStepConfig & {
refType: ScenarioStepRefType;
}
>({
refType: ScenarioStepRefType.REF,
enableScenarioEnv: false,
useOriginScenarioParamPreferential: true,
useOriginScenarioParam: false,
});
const showScenarioConfig = ref(false);
// const scenarioConfigParamTip = computed(() => {
// if (!scenarioConfigForm.value.useOriginScenarioParam && !scenarioConfigForm.value.enableScenarioEnv) {
// // 使-
// return t('apiScenario.notSource');
// }
// if (!scenarioConfigForm.value.useOriginScenarioParam && scenarioConfigForm.value.enableScenarioEnv) {
// // 使-
// return t('apiScenario.notSourceParamAndSourceEnv');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// !scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使-使
// return t('apiScenario.sourceParamAndSource');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使-使-
// return t('apiScenario.sourceParamAndSourceEnv');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// !scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使-使
// return t('apiScenario.currentParamAndSource');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使-使-
// return t('apiScenario.currentParamAndSourceEnv');
// }
// });
//
function cancelScenarioConfig() {
showScenarioConfig.value = false;
scenarioConfigForm.value = {
refType: ScenarioStepRefType.REF,
enableScenarioEnv: false,
useOriginScenarioParamPreferential: true,
useOriginScenarioParam: false,
};
}
/**
* 刷新引用场景的步骤数据
* 处理apicase场景步骤名称编辑
*/
async function refreshScenarioStepInfo(step: ScenarioStepItem, id: string | number) {
try {
loading.value = true;
const res = await getScenarioDetail(id);
if (step.children) {
step.children = mapTree(res.steps || [], (child) => {
child.uniqueId = getGenerateId();
child.isQuoteScenarioStep = true; //
child.isRefScenarioStep = true; //
child.draggable = false; //
if (selectedKeys.value.includes(step.uniqueId) && !selectedKeys.value.includes(child.uniqueId)) {
//
selectedKeys.value.push(child.uniqueId);
}
return child;
}) as ScenarioStepItem[];
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
const showStepNameEditInputStepId = ref<string | number>('');
const tempStepName = ref('');
/**
* 处理非 apicase场景步骤名称编辑
*/
const showStepDescEditInputStepId = ref<string | number>('');
const tempStepDesc = ref('');
const importApiDrawerVisible = ref(false);
const customCaseDrawerVisible = ref(false);
const customApiDrawerVisible = ref(false);
const scriptOperationDrawerVisible = ref(false);
//
async function saveScenarioConfig() {
if (activeStep.value) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, activeStep.value.uniqueId, 'uniqueId');
if (realStep) {
realStep.refType = scenarioConfigForm.value.refType; //
realStep.config = {
...realStep.config,
...scenarioConfigForm.value,
};
if (scenarioConfigForm.value.refType === ScenarioStepRefType.REF) {
//
await refreshScenarioStepInfo(realStep as ScenarioStepItem, realStep.resourceId);
} else {
realStep.children = mapTree<ScenarioStepItem>(realStep.children || [], (child) => {
// -
child.isRefScenarioStep = false;
return child;
});
}
Message.success(t('apiScenario.setSuccess'));
scenario.value.unSaved = true;
cancelScenarioConfig();
}
}
}
const { handleStepExpand, handleStepSelect, deleteStep, handleDrop, getStepDetail } = useStepOperation({
scenario,
steps,
stepDetails,
activeStep,
selectedKeys,
customApiDrawerVisible,
customCaseDrawerVisible,
scriptOperationDrawerVisible,
loading,
});
async function getStepDetail(step: ScenarioStepItem) {
try {
appStore.showLoading();
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] = {
...res,
stepId: step.id,
protocol: step.config.protocol || '',
method: step.config.method || '',
...parseRequestBodyResult,
};
scenario.value.stepFileParam[step.id] = {
...parseRequestBodyResult,
};
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
appStore.hideLoading();
}
}
const showQuickInput = ref(false);
const quickInputParamValue = ref<any>('');
const quickInputDataKey = ref('');
const {
setQuickInput,
clearQuickInput,
applyQuickInput,
handleStepDescClick,
applyStepDescChange,
handleStepContentChange,
handleStepToggleEnable,
handleStepNameClick,
applyStepNameChange,
saveScenarioConfig,
cancelScenarioConfig,
scenarioConfigForm,
} = useStepNodeEdit({
steps,
scenario,
activeStep,
quickInputDataKey,
quickInputParamValue,
showQuickInput,
treeRef,
tempStepDesc,
showStepDescEditInputStepId,
tempStepName,
showStepNameEditInputStepId,
loading,
selectedKeys,
showScenarioConfig,
});
const saveNewApiModalVisible = ref(false);
const tempApiDetail = ref<RequestParam>();
@ -1109,20 +1023,6 @@
treeRef.value?.checkAll(val);
}
/**
* 处理apicase场景步骤名称编辑
*/
const showStepNameEditInputStepId = ref<string | number>('');
const tempStepName = ref('');
/**
* 处理非 apicase场景步骤名称编辑
*/
const showStepDescEditInputStepId = ref<string | number>('');
const tempStepDesc = ref('');
const importApiDrawerVisible = ref(false);
const customCaseDrawerVisible = ref(false);
const customApiDrawerVisible = ref(false);
const scriptOperationDrawerVisible = ref(false);
const activeCreateAction = ref<CreateStepAction>(); //
const currentStepDetail = computed<ScenarioStepDetails | undefined>(() => {
if (activeStep.value) {
@ -1143,18 +1043,6 @@
scenario.value.unSaved = true;
}
const { handleStepExpand, handleStepSelect, deleteStep, handleDrop } = useStepOperation({
scenario,
steps,
stepDetails,
activeStep,
selectedKeys,
customApiDrawerVisible,
customCaseDrawerVisible,
scriptOperationDrawerVisible,
loading,
});
function handleReplaceStep(newStep: ScenarioStepItem) {
if (activeStep.value) {
//
@ -1343,14 +1231,13 @@
}
if (activeStep.value) {
const _stepType = getStepType(activeStep.value);
if (_stepType.isQuoteCase || activeStep.value.isQuoteScenarioStep) {
// case
if (_stepType.isQuoteCase && !activeStep.value.isQuoteScenarioStep) {
activeStep.value.name = request.stepName || request.name;
stepDetails.value[activeStep.value.id] = request; // polymorphicName
return;
}
}
if (activeStep.value) {
if (activeStep.value && !activeStep.value.isQuoteScenarioStep) {
request.isNew = false;
stepDetails.value[activeStep.value.id] = request;
scenario.value.stepFileParam[activeStep.value?.id] = {
@ -1363,10 +1250,9 @@
...activeStep.value.config,
method: request.method,
};
activeStep.value.name = request.stepName || request.name;
emit('updateResource', request.uploadFileIds, request.linkFileIds);
activeStep.value = undefined;
}
activeStep.value = undefined;
}
/**
@ -1408,7 +1294,8 @@
}
function saveScriptStep(name: string, scriptProcessor: ExecuteConditionProcessor, unSaved = false) {
if (activeStep.value) {
if (activeStep.value && !activeStep.value.isQuoteScenarioStep) {
//
stepDetails.value[activeStep.value.id] = cloneDeep(scriptProcessor);
activeStep.value.name = name;
activeStep.value = undefined;
@ -1418,34 +1305,6 @@
}
}
const showQuickInput = ref(false);
const quickInputParamValue = ref<any>('');
const quickInputDataKey = ref('');
const {
setQuickInput,
clearQuickInput,
applyQuickInput,
handleStepDescClick,
applyStepDescChange,
handleStepContentChange,
handleStepToggleEnable,
handleStepNameClick,
applyStepNameChange,
} = useStepNodeEdit({
steps,
scenario,
activeStep,
quickInputDataKey,
quickInputParamValue,
showQuickInput,
treeRef,
tempStepDesc,
showStepDescEditInputStepId,
tempStepName,
showStepNameEditInputStepId,
});
const dbClick = ref({
e: null as MouseEvent | null,
timeStamp: 0,

View File

@ -1,8 +1,13 @@
import { Message } from '@arco-design/web-vue';
import MsTree from '@/components/business/ms-tree/index.vue';
import { findNodeByKey } from '@/utils';
import { getScenarioDetail } from '@/api/modules/api-test/scenario';
import { t } from '@/hooks/useI18n';
import { findNodeByKey, getGenerateId, mapTree } from '@/utils';
import type { Scenario, ScenarioStepDetail, ScenarioStepItem } from '@/models/apiTest/scenario';
import type { Scenario, ScenarioStepConfig, ScenarioStepDetail, ScenarioStepItem } from '@/models/apiTest/scenario';
import { ScenarioStepRefType } from '@/enums/apiEnum';
/**
*
@ -19,6 +24,9 @@ export default function useStepNodeEdit({
showStepDescEditInputStepId,
tempStepName,
showStepNameEditInputStepId,
loading,
selectedKeys,
showScenarioConfig,
}: {
steps: Ref<ScenarioStepItem[]>;
scenario: Ref<Scenario>;
@ -31,6 +39,9 @@ export default function useStepNodeEdit({
showStepDescEditInputStepId: Ref<string | number>;
tempStepName: Ref<string>;
showStepNameEditInputStepId: Ref<string | number>;
loading: Ref<boolean>;
selectedKeys: Ref<Array<string | number>>;
showScenarioConfig: Ref<boolean>;
}) {
/**
*
@ -162,6 +173,125 @@ export default function useStepNodeEdit({
scenario.value.unSaved = !!tempStepName.value;
}
const scenarioConfigForm = ref<
ScenarioStepConfig & {
refType: ScenarioStepRefType;
}
>({
refType: ScenarioStepRefType.REF,
enableScenarioEnv: false,
useOriginScenarioParamPreferential: true,
useOriginScenarioParam: false,
});
// const scenarioConfigParamTip = computed(() => {
// if (!scenarioConfigForm.value.useOriginScenarioParam && !scenarioConfigForm.value.enableScenarioEnv) {
// // 非使用原场景参数-非选择源场景环境
// return t('apiScenario.notSource');
// }
// if (!scenarioConfigForm.value.useOriginScenarioParam && scenarioConfigForm.value.enableScenarioEnv) {
// // 非使用原场景参数-选择源场景环境
// return t('apiScenario.notSourceParamAndSourceEnv');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// !scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使用原场景参数-优先使用原场景参数
// return t('apiScenario.sourceParamAndSource');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使用原场景参数-优先使用原场景参数-选择源场景环境
// return t('apiScenario.sourceParamAndSourceEnv');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// !scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使用原场景参数-优先使用当前场景参数
// return t('apiScenario.currentParamAndSource');
// }
// if (
// scenarioConfigForm.value.useOriginScenarioParam &&
// !scenarioConfigForm.value.useOriginScenarioParamPreferential &&
// scenarioConfigForm.value.enableScenarioEnv
// ) {
// // 使用原场景参数-优先使用当前场景参数-选择源场景环境
// return t('apiScenario.currentParamAndSourceEnv');
// }
// });
// 关闭场景配置弹窗
function cancelScenarioConfig() {
showScenarioConfig.value = false;
scenarioConfigForm.value = {
refType: ScenarioStepRefType.REF,
enableScenarioEnv: false,
useOriginScenarioParamPreferential: true,
useOriginScenarioParam: false,
};
}
/**
*
*/
async function refreshScenarioStepInfo(step: ScenarioStepItem, id: string | number) {
try {
loading.value = true;
const res = await getScenarioDetail(id);
if (step.children) {
step.children = mapTree(res.steps || [], (child) => {
child.uniqueId = getGenerateId();
child.isQuoteScenarioStep = true; // 标记为引用场景下的子步骤
child.isRefScenarioStep = true; // 标记为完全引用场景
child.draggable = false; // 引用场景下的任何步骤不可拖拽
if (selectedKeys.value.includes(step.uniqueId) && !selectedKeys.value.includes(child.uniqueId)) {
// 如果有新增的子步骤,且当前步骤被选中,则这个新增的子步骤也要选中
selectedKeys.value.push(child.uniqueId);
}
return child;
}) as ScenarioStepItem[];
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
loading.value = false;
}
}
// 应用场景配置
async function saveScenarioConfig() {
if (activeStep.value) {
const realStep = findNodeByKey<ScenarioStepItem>(steps.value, activeStep.value.uniqueId, 'uniqueId');
if (realStep) {
realStep.refType = scenarioConfigForm.value.refType; // 更新场景引用类型
realStep.config = {
...realStep.config,
...scenarioConfigForm.value,
};
if (scenarioConfigForm.value.refType === ScenarioStepRefType.REF) {
// 更新子孙步骤完全引用
await refreshScenarioStepInfo(realStep as ScenarioStepItem, realStep.resourceId);
} else {
realStep.children = mapTree<ScenarioStepItem>(realStep.children || [], (child) => {
// 更新子孙步骤-步骤引用
child.isRefScenarioStep = false;
return child;
});
}
Message.success(t('apiScenario.setSuccess'));
scenario.value.unSaved = true;
cancelScenarioConfig();
}
}
}
return {
setQuickInput,
clearQuickInput,
@ -172,5 +302,8 @@ export default function useStepNodeEdit({
handleStepToggleEnable,
handleStepNameClick,
applyStepNameChange,
saveScenarioConfig,
cancelScenarioConfig,
scenarioConfigForm,
};
}

View File

@ -241,6 +241,7 @@ export default function useStepOperation({
}
return {
getStepDetail,
handleStepExpand,
handleStepSelect,
deleteStep,

View File

@ -317,7 +317,10 @@
const waitTingDebugSteps = filterTree(activeScenarioTab.value.steps, (node) => {
if (node.enable) {
node.executeStatus = ScenarioExecuteStatus.EXECUTING;
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
if (!node.isQuoteScenarioStep) {
//
waitingDebugStepDetails[node.id] = activeScenarioTab.value.stepDetails[node.id];
}
} else {
node.executeStatus = undefined;
}