feat(接口测试): 场景复制步骤&复制场景文件
This commit is contained in:
parent
e83985a2f5
commit
0608840cb0
|
@ -36,6 +36,7 @@ import {
|
||||||
ScenarioAssociateExportUrl,
|
ScenarioAssociateExportUrl,
|
||||||
ScenarioBatchEditScheduleUrl,
|
ScenarioBatchEditScheduleUrl,
|
||||||
ScenarioBatchExportLogUrl,
|
ScenarioBatchExportLogUrl,
|
||||||
|
ScenarioCopyStepFilesUrl,
|
||||||
ScenarioExportLogUrl,
|
ScenarioExportLogUrl,
|
||||||
ScenarioHistoryUrl,
|
ScenarioHistoryUrl,
|
||||||
ScenarioPageUrl,
|
ScenarioPageUrl,
|
||||||
|
@ -71,6 +72,7 @@ import {
|
||||||
ExecuteHistoryItem,
|
ExecuteHistoryItem,
|
||||||
ExecutePageParams,
|
ExecutePageParams,
|
||||||
type ExportScenarioParams,
|
type ExportScenarioParams,
|
||||||
|
type GetScenarioUnSaveStepParams,
|
||||||
GetSystemRequestParams,
|
GetSystemRequestParams,
|
||||||
type ImportScenarioParams,
|
type ImportScenarioParams,
|
||||||
ImportSystemData,
|
ImportSystemData,
|
||||||
|
@ -376,3 +378,11 @@ export function scenarioBatchEditSchedule(data: ApiScenarioBatchScheduleConfig)
|
||||||
export function getScenarioStatistics(data: string[]) {
|
export function getScenarioStatistics(data: string[]) {
|
||||||
return MSR.post<ScenarioStatisticsItem[]>({ url: ScenarioStatisticsUrl, data });
|
return MSR.post<ScenarioStatisticsItem[]>({ url: ScenarioStatisticsUrl, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 复制步骤时复制文件
|
||||||
|
export function scenarioCopyStepFiles(data: GetScenarioUnSaveStepParams) {
|
||||||
|
return MSR.post<Record<string, any>>({
|
||||||
|
url: ScenarioCopyStepFilesUrl,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ export const BatchRunScenarioUrl = '/api/scenario/batch-operation/run'; // 批
|
||||||
export const UpdateScenarioPriorityUrl = '/api/scenario/update-priority'; // 场景更新等级
|
export const UpdateScenarioPriorityUrl = '/api/scenario/update-priority'; // 场景更新等级
|
||||||
export const UpdateScenarioStatusUrl = '/api/scenario/update-status'; // 场景更新状态
|
export const UpdateScenarioStatusUrl = '/api/scenario/update-status'; // 场景更新状态
|
||||||
export const ScenarioStatisticsUrl = '/api/scenario/statistics'; // 场景执行率统计
|
export const ScenarioStatisticsUrl = '/api/scenario/statistics'; // 场景执行率统计
|
||||||
|
export const ScenarioCopyStepFilesUrl = '/api/scenario/step/file/copy'; // 复制步骤时复制文件
|
||||||
|
|
||||||
// 场景导入导出相关
|
// 场景导入导出相关
|
||||||
export const ImportScenarioUrl = '/api/scenario/import'; // 导入场景
|
export const ImportScenarioUrl = '/api/scenario/import'; // 导入场景
|
||||||
|
|
|
@ -571,3 +571,13 @@ export interface ScenarioStatisticsItem {
|
||||||
id: string;
|
id: string;
|
||||||
execPassRate: string;
|
execPassRate: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 场景未保存步骤请求参数
|
||||||
|
export interface GetScenarioUnSaveStepParams {
|
||||||
|
copyFromStepId?: string;
|
||||||
|
resourceId?: string;
|
||||||
|
stepType?: string;
|
||||||
|
refType: string;
|
||||||
|
isTempFile: boolean; // 复制未保存的步骤时 true
|
||||||
|
fileIds?: string[]; // 未保存的步骤文件 id,复制未加载/修改过详情的步骤时无需传
|
||||||
|
}
|
||||||
|
|
|
@ -31,12 +31,17 @@ export interface ParseResult {
|
||||||
/**
|
/**
|
||||||
* 解析接口请求/Mock body 内的文件列表
|
* 解析接口请求/Mock body 内的文件列表
|
||||||
* @param body body 参数对象
|
* @param body body 参数对象
|
||||||
|
* @param response 响应列表
|
||||||
|
* @param saveUploadFileIds 已保存的上传文件 id 集合
|
||||||
|
* @param saveLinkFileIds 已保存的关联文件 id 集合
|
||||||
|
* @param newFileMap 新文件 id 映射
|
||||||
*/
|
*/
|
||||||
export function parseRequestBodyFiles(
|
export function parseRequestBodyFiles(
|
||||||
body: ExecuteBody | MockBody,
|
body: ExecuteBody | MockBody,
|
||||||
response?: ResponseDefinition[],
|
response?: ResponseDefinition[],
|
||||||
saveUploadFileIds?: string[],
|
saveUploadFileIds?: string[],
|
||||||
saveLinkFileIds?: string[]
|
saveLinkFileIds?: string[],
|
||||||
|
newFileMap?: Record<string, string>
|
||||||
): ParseResult {
|
): ParseResult {
|
||||||
const { binaryBody } = body;
|
const { binaryBody } = body;
|
||||||
const uploadFileIds = new Set<string>(); // 存储本地上传的文件 id 集合
|
const uploadFileIds = new Set<string>(); // 存储本地上传的文件 id 集合
|
||||||
|
@ -45,49 +50,58 @@ export function parseRequestBodyFiles(
|
||||||
const tempSaveLinkFileIds = new Set<string>(); // 临时存储 body 内已保存的关联文件 id 集合,用于对比 saveLinkFileIds 以判断有哪些文件被取消关联
|
const tempSaveLinkFileIds = new Set<string>(); // 临时存储 body 内已保存的关联文件 id 集合,用于对比 saveLinkFileIds 以判断有哪些文件被取消关联
|
||||||
// 获取上传文件和关联文件
|
// 获取上传文件和关联文件
|
||||||
const formValues =
|
const formValues =
|
||||||
((body as ExecuteBody).formDataBody?.formValues || (body as MockBody).formDataBody?.matchRules || []).filter(
|
(body as ExecuteBody).formDataBody?.formValues || (body as MockBody).formDataBody?.matchRules || [] || [];
|
||||||
(e) => e
|
|
||||||
) || [];
|
|
||||||
for (let i = 0; i < formValues.length; i++) {
|
for (let i = 0; i < formValues.length; i++) {
|
||||||
const item = formValues[i];
|
const item = formValues[i];
|
||||||
|
if (item) {
|
||||||
if (item.paramType === RequestParamsType.FILE) {
|
if (item.paramType === RequestParamsType.FILE) {
|
||||||
if (item.files) {
|
if (item.files) {
|
||||||
for (let j = 0; j < item.files.length; j++) {
|
for (let j = 0; j < item.files.length; j++) {
|
||||||
const file = item.files[j];
|
const file = item.files[j];
|
||||||
|
let { fileId } = file;
|
||||||
|
if (newFileMap && newFileMap[fileId]) {
|
||||||
|
fileId = newFileMap[fileId];
|
||||||
|
file.fileId = fileId;
|
||||||
|
}
|
||||||
if (file.local) {
|
if (file.local) {
|
||||||
// 本地上传的文件
|
// 本地上传的文件
|
||||||
if (saveUploadFileIds) {
|
if (saveUploadFileIds) {
|
||||||
// 如果有已保存的上传文件id集合
|
// 如果有已保存的上传文件id集合
|
||||||
if (saveUploadFileIds.includes(file.fileId)) {
|
if (saveUploadFileIds.includes(fileId)) {
|
||||||
// 当前文件是已保存的文件,存入 tempSaveUploadFileIds
|
// 当前文件是已保存的文件,存入 tempSaveUploadFileIds
|
||||||
tempSaveUploadFileIds.add(file.fileId);
|
tempSaveUploadFileIds.add(fileId);
|
||||||
} else {
|
} else {
|
||||||
// 当前文件不是已保存的文件,存入 uploadFileIds
|
// 当前文件不是已保存的文件,存入 uploadFileIds
|
||||||
uploadFileIds.add(file.fileId);
|
uploadFileIds.add(fileId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 没有已保存的文件id集合,直接存入 uploadFileIds
|
// 没有已保存的文件id集合,直接存入 uploadFileIds
|
||||||
uploadFileIds.add(file.fileId);
|
uploadFileIds.add(fileId);
|
||||||
}
|
}
|
||||||
} else if (saveLinkFileIds) {
|
} else if (saveLinkFileIds) {
|
||||||
// 如果有已保存的关联文件id集合
|
// 如果有已保存的关联文件id集合
|
||||||
if (saveLinkFileIds.includes(file.fileId)) {
|
if (saveLinkFileIds.includes(fileId)) {
|
||||||
// 当前文件是已保存的文件,存入
|
// 当前文件是已保存的文件,存入
|
||||||
tempSaveLinkFileIds.add(file.fileId);
|
tempSaveLinkFileIds.add(fileId);
|
||||||
} else {
|
} else {
|
||||||
// 当前文件不是已保存的文件,存入 uploadFileIds
|
// 当前文件不是已保存的文件,存入 uploadFileIds
|
||||||
linkFileIds.add(file.fileId);
|
linkFileIds.add(fileId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 关联的文件
|
// 关联的文件
|
||||||
linkFileIds.add(file.fileId);
|
linkFileIds.add(fileId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (binaryBody && binaryBody.file) {
|
if (binaryBody && binaryBody.file) {
|
||||||
const { fileId } = binaryBody.file;
|
let { fileId } = binaryBody.file;
|
||||||
|
if (newFileMap && newFileMap[fileId]) {
|
||||||
|
fileId = newFileMap[fileId];
|
||||||
|
binaryBody.file.fileId = fileId;
|
||||||
|
}
|
||||||
if (binaryBody.file?.local) {
|
if (binaryBody.file?.local) {
|
||||||
if (saveUploadFileIds) {
|
if (saveUploadFileIds) {
|
||||||
// 如果有已保存的上传文件id集合
|
// 如果有已保存的上传文件id集合
|
||||||
|
@ -119,7 +133,11 @@ export function parseRequestBodyFiles(
|
||||||
if (response) {
|
if (response) {
|
||||||
response.forEach((res) => {
|
response.forEach((res) => {
|
||||||
if (res.body.binaryBody && res.body.binaryBody.file) {
|
if (res.body.binaryBody && res.body.binaryBody.file) {
|
||||||
const { fileId } = res.body.binaryBody.file;
|
let { fileId } = res.body.binaryBody.file;
|
||||||
|
if (newFileMap && newFileMap[fileId]) {
|
||||||
|
fileId = newFileMap[fileId];
|
||||||
|
res.body.binaryBody.file.fileId = fileId;
|
||||||
|
}
|
||||||
if (res.body.binaryBody.file?.local) {
|
if (res.body.binaryBody.file?.local) {
|
||||||
if (saveUploadFileIds) {
|
if (saveUploadFileIds) {
|
||||||
// 如果有已保存的上传文件id集合
|
// 如果有已保存的上传文件id集合
|
||||||
|
|
|
@ -375,7 +375,12 @@
|
||||||
|
|
||||||
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
|
import { getPluginScript, getProtocolList } from '@/api/modules/api-test/common';
|
||||||
import { getDefinitionDetail } from '@/api/modules/api-test/management';
|
import { getDefinitionDetail } from '@/api/modules/api-test/management';
|
||||||
import { getTransferOptions, stepTransferFile, uploadTempFile } from '@/api/modules/api-test/scenario';
|
import {
|
||||||
|
getTransferOptions,
|
||||||
|
scenarioCopyStepFiles,
|
||||||
|
stepTransferFile,
|
||||||
|
uploadTempFile,
|
||||||
|
} from '@/api/modules/api-test/scenario';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { getGenerateId, parseQueryParams } from '@/utils';
|
import { getGenerateId, parseQueryParams } from '@/utils';
|
||||||
|
@ -403,6 +408,7 @@
|
||||||
RequestComposition,
|
RequestComposition,
|
||||||
RequestMethods,
|
RequestMethods,
|
||||||
ResponseComposition,
|
ResponseComposition,
|
||||||
|
ScenarioStepRefType,
|
||||||
ScenarioStepType,
|
ScenarioStepType,
|
||||||
} from '@/enums/apiEnum';
|
} from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
@ -564,6 +570,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestVModel = ref<RequestParam>(defaultApiParams);
|
const requestVModel = ref<RequestParam>(defaultApiParams);
|
||||||
|
const copyStepFileIdsMap = ref<Record<string, any>>({});
|
||||||
// 步骤类型判断
|
// 步骤类型判断
|
||||||
const _stepType = computed(() => {
|
const _stepType = computed(() => {
|
||||||
if (props.step) {
|
if (props.step) {
|
||||||
|
@ -994,7 +1001,8 @@
|
||||||
requestVModel.value.body,
|
requestVModel.value.body,
|
||||||
undefined,
|
undefined,
|
||||||
props.fileParams?.uploadFileIds || requestVModel.value.uploadFileIds, // 外面解析详情的时候传入,或引用 api 在requestVModel内存储
|
props.fileParams?.uploadFileIds || requestVModel.value.uploadFileIds, // 外面解析详情的时候传入,或引用 api 在requestVModel内存储
|
||||||
props.fileParams?.linkFileIds || requestVModel.value.linkFileIds // 外面解析详情的时候传入,或引用 api 在requestVModel内存储
|
props.fileParams?.linkFileIds || requestVModel.value.linkFileIds, // 外面解析详情的时候传入,或引用 api 在requestVModel内存储
|
||||||
|
copyStepFileIdsMap.value
|
||||||
);
|
);
|
||||||
requestParams = {
|
requestParams = {
|
||||||
authConfig: requestVModel.value.authConfig,
|
authConfig: requestVModel.value.authConfig,
|
||||||
|
@ -1208,7 +1216,20 @@
|
||||||
const res = await getDefinitionDetail(props.step?.resourceId || '');
|
const res = await getDefinitionDetail(props.step?.resourceId || '');
|
||||||
let parseRequestBodyResult;
|
let parseRequestBodyResult;
|
||||||
if (res.protocol === 'HTTP') {
|
if (res.protocol === 'HTTP') {
|
||||||
parseRequestBodyResult = parseRequestBodyFiles(res.request.body, res.response); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
if ((props.step?.copyFromStepId || props.step?.refType === ScenarioStepRefType.COPY) && props.step?.isNew) {
|
||||||
|
// 复制的步骤需要复制文件
|
||||||
|
copyStepFileIdsMap.value = await scenarioCopyStepFiles({
|
||||||
|
copyFromStepId: props.step?.copyFromStepId,
|
||||||
|
resourceId: props.step?.resourceId,
|
||||||
|
stepType: props.step?.stepType,
|
||||||
|
refType: props.step?.refType,
|
||||||
|
isTempFile: false, // 复制未保存的步骤时 true
|
||||||
|
fileIds: Object.values(parseRequestBodyFiles(res.request.body, [], [], [])).flat(),
|
||||||
|
});
|
||||||
|
parseRequestBodyFiles(res.body, [], [], [], copyStepFileIdsMap.value);
|
||||||
|
} else {
|
||||||
|
parseRequestBodyResult = parseRequestBodyFiles(res.request.body, [], [], [], copyStepFileIdsMap.value); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||||
|
}
|
||||||
}
|
}
|
||||||
requestVModel.value = {
|
requestVModel.value = {
|
||||||
executeLoading: false,
|
executeLoading: false,
|
||||||
|
|
|
@ -482,6 +482,7 @@
|
||||||
import saveAsApiModal from '@/views/api-test/components/saveAsApiModal.vue';
|
import saveAsApiModal from '@/views/api-test/components/saveAsApiModal.vue';
|
||||||
|
|
||||||
import { addCase, getDefinitionDetail } from '@/api/modules/api-test/management';
|
import { addCase, getDefinitionDetail } from '@/api/modules/api-test/management';
|
||||||
|
import { scenarioCopyStepFiles } from '@/api/modules/api-test/scenario';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
@ -514,6 +515,7 @@
|
||||||
import useStepNodeEdit from './useStepNodeEdit';
|
import useStepNodeEdit from './useStepNodeEdit';
|
||||||
import useStepOperation from './useStepOperation';
|
import useStepOperation from './useStepOperation';
|
||||||
import { casePriorityOptions, caseStatusOptions } from '@/views/api-test/components/config';
|
import { casePriorityOptions, caseStatusOptions } from '@/views/api-test/components/config';
|
||||||
|
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
||||||
import getStepType from '@/views/api-test/scenario/components/common/stepType/utils';
|
import 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';
|
||||||
|
|
||||||
|
@ -919,23 +921,50 @@
|
||||||
* 复制步骤
|
* 复制步骤
|
||||||
* @param node 复制的节点
|
* @param node 复制的节点
|
||||||
*/
|
*/
|
||||||
function copyStep(node: MsTreeNodeData) {
|
async function copyStep(node: MsTreeNodeData) {
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
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);
|
||||||
|
let { copyFromStepId } = node;
|
||||||
|
if (stepDetail || node.isNew !== true || !node.copyFromStepId) {
|
||||||
|
// 如果复制的步骤查看过详情,则复制来源直接取它的 id
|
||||||
|
// 如果复制的步骤没有查看过详情,且是新建的步骤,则取它本身的 id
|
||||||
|
copyFromStepId = node.id;
|
||||||
|
}
|
||||||
|
let parseRequestBodyResult: Record<string, any> = {
|
||||||
|
uploadFileIds: [],
|
||||||
|
linkFileIds: [],
|
||||||
|
deleteFileIds: [], // 存储对比已保存的文件后,需要删除的文件 id 集合
|
||||||
|
unLinkFileIds: [], // 存储对比已保存的文件后,需要取消关联的文件 id 集合
|
||||||
|
};
|
||||||
|
let newFileRes;
|
||||||
|
if (node.config.protocol === 'HTTP' && (stepDetail as RequestParam)?.body) {
|
||||||
|
if (node.copyFromStepId || node.refType === ScenarioStepRefType.COPY) {
|
||||||
|
// 复制的步骤需要复制文件
|
||||||
|
newFileRes = await scenarioCopyStepFiles({
|
||||||
|
copyFromStepId,
|
||||||
|
resourceId: node.resourceId,
|
||||||
|
stepType: node.stepType,
|
||||||
|
refType: node.refType,
|
||||||
|
isTempFile: !!stepDetail, // 复制未保存的步骤时 true
|
||||||
|
fileIds: Object.values(parseRequestBodyFiles((stepDetail as RequestParam).body, [], [], [])).flat(),
|
||||||
|
});
|
||||||
|
parseRequestBodyFiles((stepDetail as RequestParam).body, [], [], [], newFileRes);
|
||||||
|
} else {
|
||||||
|
parseRequestBodyResult = parseRequestBodyFiles((stepDetail as RequestParam).body, [], [], [], newFileRes); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||||
|
}
|
||||||
|
}
|
||||||
if (stepDetail) {
|
if (stepDetail) {
|
||||||
// 如果复制的步骤还有详情数据,则也复制详情数据
|
// 如果复制的步骤还有详情数据,则也复制详情数据
|
||||||
stepDetails.value[id] = cloneDeep({
|
stepDetails.value[id] = cloneDeep({
|
||||||
...stepDetail,
|
...stepDetail,
|
||||||
stepId: id,
|
stepId: id,
|
||||||
uniqueId: id,
|
uniqueId: id,
|
||||||
|
...parseRequestBodyResult,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (stepFileParam) {
|
|
||||||
// 如果复制的步骤还有详情数据,则也复制详情数据
|
|
||||||
scenario.value.stepFileParam[id] = cloneDeep(stepFileParam);
|
|
||||||
}
|
|
||||||
insertNodes<ScenarioStepItem>(
|
insertNodes<ScenarioStepItem>(
|
||||||
steps.value,
|
steps.value,
|
||||||
node.uniqueId,
|
node.uniqueId,
|
||||||
|
@ -944,16 +973,11 @@
|
||||||
mapTree<ScenarioStepItem>(node, (childNode) => {
|
mapTree<ScenarioStepItem>(node, (childNode) => {
|
||||||
const childId = getGenerateId();
|
const childId = getGenerateId();
|
||||||
const childStepDetail = stepDetails.value[childNode.id];
|
const childStepDetail = stepDetails.value[childNode.id];
|
||||||
const childStepFileParam = scenario.value.stepFileParam[childNode.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[childNode.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)) {
|
||||||
|
@ -975,7 +999,7 @@
|
||||||
})[0]
|
})[0]
|
||||||
),
|
),
|
||||||
name: `copy_${node.name}`.substring(0, 255),
|
name: `copy_${node.name}`.substring(0, 255),
|
||||||
copyFromStepId: stepDetail || node.isNew !== true ? node.id : node.copyFromStepId,
|
copyFromStepId,
|
||||||
sort: node.sort + 1,
|
sort: node.sort + 1,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
id,
|
id,
|
||||||
|
@ -986,6 +1010,12 @@
|
||||||
'uniqueId'
|
'uniqueId'
|
||||||
);
|
);
|
||||||
scenario.value.unSaved = true;
|
scenario.value.unSaved = true;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleStepMoreActionSelect(item: ActionsItem, node: MsTreeNodeData) {
|
async function handleStepMoreActionSelect(item: ActionsItem, node: MsTreeNodeData) {
|
||||||
|
@ -1189,6 +1219,14 @@
|
||||||
appStore.currentProjectId
|
appStore.currentProjectId
|
||||||
);
|
);
|
||||||
const insertSteps = insertApiSteps.concat(insertCaseSteps).concat(insertScenarioSteps);
|
const insertSteps = insertApiSteps.concat(insertCaseSteps).concat(insertScenarioSteps);
|
||||||
|
insertSteps.forEach((step) => {
|
||||||
|
scenario.value.stepFileParam[step.id] = {
|
||||||
|
linkFileIds: [],
|
||||||
|
uploadFileIds: [],
|
||||||
|
deleteFileIds: [],
|
||||||
|
unLinkFileIds: [],
|
||||||
|
};
|
||||||
|
});
|
||||||
if (activeStepByCreate.value && activeCreateAction.value) {
|
if (activeStepByCreate.value && activeCreateAction.value) {
|
||||||
handleCreateSteps(
|
handleCreateSteps(
|
||||||
activeStepByCreate.value,
|
activeStepByCreate.value,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import type { MsTreeExpandedData, MsTreeNodeData } from '@/components/business/ms-tree/types';
|
import type { MsTreeExpandedData, MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
|
|
||||||
import { getScenarioStep } from '@/api/modules/api-test/scenario';
|
import { getScenarioStep, scenarioCopyStepFiles } from '@/api/modules/api-test/scenario';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
@ -12,6 +12,7 @@ import { deleteNode, findNodeByKey, handleTreeDragDrop, mapTree } from '@/utils'
|
||||||
import type { Scenario, ScenarioStepItem } from '@/models/apiTest/scenario';
|
import type { Scenario, ScenarioStepItem } from '@/models/apiTest/scenario';
|
||||||
import { ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
import { ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
import type { RequestParam } from '../common/customApiDrawer.vue';
|
||||||
import getStepType from '../common/stepType/utils';
|
import getStepType from '../common/stepType/utils';
|
||||||
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
import { parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
||||||
|
|
||||||
|
@ -57,9 +58,28 @@ export default function useStepOperation({
|
||||||
try {
|
try {
|
||||||
appStore.showLoading();
|
appStore.showLoading();
|
||||||
const res = await getScenarioStep(step.copyFromStepId || step.id);
|
const res = await getScenarioStep(step.copyFromStepId || step.id);
|
||||||
let parseRequestBodyResult;
|
let parseRequestBodyResult: Record<string, any> = {
|
||||||
|
uploadFileIds: [],
|
||||||
|
linkFileIds: [],
|
||||||
|
deleteFileIds: [], // 存储对比已保存的文件后,需要删除的文件 id 集合
|
||||||
|
unLinkFileIds: [], // 存储对比已保存的文件后,需要取消关联的文件 id 集合
|
||||||
|
};
|
||||||
|
let newFileRes;
|
||||||
if (step.config.protocol === 'HTTP' && res.body) {
|
if (step.config.protocol === 'HTTP' && res.body) {
|
||||||
parseRequestBodyResult = parseRequestBodyFiles(res.body); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
if ((step.copyFromStepId || step.refType === ScenarioStepRefType.COPY) && step.isNew) {
|
||||||
|
// 复制的步骤需要复制文件
|
||||||
|
newFileRes = await scenarioCopyStepFiles({
|
||||||
|
copyFromStepId: step.copyFromStepId,
|
||||||
|
resourceId: step.resourceId,
|
||||||
|
stepType: step.stepType,
|
||||||
|
refType: step.refType,
|
||||||
|
isTempFile: false, // 复制未保存的步骤时 true
|
||||||
|
fileIds: Object.values(parseRequestBodyFiles((res as RequestParam).body, [], [], [])).flat(),
|
||||||
|
});
|
||||||
|
parseRequestBodyFiles(res.body, [], [], [], newFileRes);
|
||||||
|
} else {
|
||||||
|
parseRequestBodyResult = parseRequestBodyFiles(res.body, [], [], [], newFileRes); // 解析请求体中的文件,将详情中的文件 id 集合收集,更新时以判断文件是否删除以及是否新上传的文件
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stepDetails.value[step.id] = {
|
stepDetails.value[step.id] = {
|
||||||
...res,
|
...res,
|
||||||
|
@ -68,9 +88,12 @@ export default function useStepOperation({
|
||||||
method: step.config.method || '',
|
method: step.config.method || '',
|
||||||
...parseRequestBodyResult,
|
...parseRequestBodyResult,
|
||||||
};
|
};
|
||||||
|
if (!step.copyFromStepId && step.refType !== ScenarioStepRefType.COPY) {
|
||||||
|
// 复制的步骤文件都是新的,不需要记录,等详情抽屉关闭时会处理
|
||||||
scenario.value.stepFileParam[step.id] = {
|
scenario.value.stepFileParam[step.id] = {
|
||||||
...parseRequestBodyResult,
|
...parseRequestBodyResult,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
|
@ -153,7 +153,7 @@
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useCacheStore from '@/store/modules/cache/cache';
|
import useCacheStore from '@/store/modules/cache/cache';
|
||||||
import { filterTree, getGenerateId, mapTree } from '@/utils';
|
import { filterTree, getGenerateId, mapTree, traverseTree } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { RequestResult } from '@/models/apiTest/common';
|
import { RequestResult } from '@/models/apiTest/common';
|
||||||
|
@ -425,6 +425,7 @@
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
// 场景被复制,递归处理节点,增加copyFromStepId
|
// 场景被复制,递归处理节点,增加copyFromStepId
|
||||||
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
|
copySteps = mapTree(defaultScenarioInfo.steps, (node) => {
|
||||||
|
node.isNew = true;
|
||||||
node.copyFromStepId = node.id;
|
node.copyFromStepId = node.id;
|
||||||
if (
|
if (
|
||||||
node.parent &&
|
node.parent &&
|
||||||
|
@ -677,6 +678,10 @@
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
traverseTree(activeScenarioTab.value.steps, (node) => {
|
||||||
|
node.isNew = false;
|
||||||
|
});
|
||||||
|
activeScenarioTab.value.stepFileParam = {};
|
||||||
refreshTree(tempTableQueryParams.value);
|
refreshTree(tempTableQueryParams.value);
|
||||||
Message.success(activeScenarioTab.value.isNew ? t('common.createSuccess') : t('common.saveSuccess'));
|
Message.success(activeScenarioTab.value.isNew ? t('common.createSuccess') : t('common.saveSuccess'));
|
||||||
activeScenarioTab.value.unSaved = false;
|
activeScenarioTab.value.unSaved = false;
|
||||||
|
|
Loading…
Reference in New Issue