diff --git a/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties index d4ef5ada72..349d117e92 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties @@ -274,6 +274,7 @@ xmind_stepDescription=Step description case.export.system.columns.name=Name case.export.system.columns.id=ID case.export.system.columns.prerequisite=Prerequisite +case.export.system.columns.module=Module case.export.system.columns.text_description=Text description case.export.system.columns.expected_result=Expected result case.export.system.other.columns.last_execute_result=Last execution result diff --git a/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties index 75a634c892..99372e6443 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties @@ -271,6 +271,7 @@ xmind_stepDescription=步骤描述 case.export.system.columns.name=用例名称 case.export.system.columns.id=ID case.export.system.columns.prerequisite=前置条件 +case.export.system.columns.module=模块 case.export.system.columns.text_description=步骤描述 case.export.system.columns.expected_result=预期结果 case.export.system.other.columns.last_execute_result=执行结果 diff --git a/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties index fb94306e2c..2653ebcc2d 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties @@ -275,6 +275,7 @@ case.find_file_error=找不到該文件 case.export.system.columns.name=用例名稱 case.export.system.columns.id=ID case.export.system.columns.prerequisite=前置條件 +case.export.system.columns.module=模塊 case.export.system.columns.text_description=步驟描述 case.export.system.columns.expected_result=預期結果 case.export.system.other.columns.last_execute_result=執行結果 diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/excel/constants/FunctionalCaseExportOtherField.java b/backend/services/case-management/src/main/java/io/metersphere/functional/excel/constants/FunctionalCaseExportOtherField.java index b4cca2f9e5..057476d387 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/excel/constants/FunctionalCaseExportOtherField.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/excel/constants/FunctionalCaseExportOtherField.java @@ -2,15 +2,15 @@ package io.metersphere.functional.excel.constants; public enum FunctionalCaseExportOtherField { - CREATE_USER("createUser"), - CREATE_TIME("createTime"), - UPDATE_USER("updateUser"), - UPDATE_TIME("updateTime"), - REVIEW_STATUS("reviewStatus"), - LAST_EXECUTE_RESULT("lastExecuteResult"), - CASE_COMMENT("caseComment"), - EXECUTE_COMMENT("executeComment"), - REVIEW_COMMENT("reviewComment"); + CREATE_USER("create_user"), + CREATE_TIME("create_time"), + UPDATE_USER("update_user"), + UPDATE_TIME("update_time"), + REVIEW_STATUS("review_status"), + LAST_EXECUTE_RESULT("last_execute_result"), + CASE_COMMENT("case_comment"), + EXECUTE_COMMENT("execute_comment"), + REVIEW_COMMENT("review_comment"); private String value; diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/excel/validate/CustomFieldMultipleTextValidator.java b/backend/services/case-management/src/main/java/io/metersphere/functional/excel/validate/CustomFieldMultipleTextValidator.java index 4f78e74e69..25335bf49b 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/excel/validate/CustomFieldMultipleTextValidator.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/excel/validate/CustomFieldMultipleTextValidator.java @@ -54,7 +54,7 @@ public class CustomFieldMultipleTextValidator extends AbstractCustomFieldValidat @Override public Object parse2Value(String keyOrValuesStr, TemplateCustomFieldDTO customField) { - if (StringUtils.isBlank(keyOrValuesStr)) { + if (StringUtils.isBlank(keyOrValuesStr) || StringUtils.equals(keyOrValuesStr, "[]")) { return JSON.toJSONString(new ArrayList<>()); } List keyOrValues = parse2Array(keyOrValuesStr); diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseFileService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseFileService.java index 2b3326cbd5..cc6527b881 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseFileService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseFileService.java @@ -165,7 +165,7 @@ public class FunctionalCaseFileService { for (String head : headList) { boolean isSystemField = false; for (FunctionalCaseImportFiled importFiled : importFields) { - if (StringUtils.equals("name", importFiled.getValue()) && model.getHyperLinkName() != null) { + if (importFiled.getFiledLangMap().containsValue(head) && StringUtils.equals("name", importFiled.getValue()) && model.getHyperLinkName() != null) { fields.add(model.getHyperLinkName()); isSystemField = true; break; @@ -759,8 +759,12 @@ public class FunctionalCaseFileService { if (caseFieldvalueMap.containsKey(k)) { AbstractCustomFieldValidator customFieldValidator = customFieldValidatorMap.get(v.getType()); if (customFieldValidator.isKVOption) { - // 这里如果填的是选项值,替换成选项ID,保存 - map.put(v.getFieldName(), customFieldValidator.parse2Value(caseFieldvalueMap.get(k), v)); + if (!v.getInternal()) { + // 这里如果填的是选项值,替换成选项ID,保存 + map.put(v.getFieldName(), customFieldValidator.parse2Value(caseFieldvalueMap.get(k), v)); + } else { + map.put(v.getFieldName(), caseFieldvalueMap.get(k)); + } } } }); diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseXmindService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseXmindService.java index 3f7d007d52..f378ec9c25 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseXmindService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseXmindService.java @@ -10,7 +10,6 @@ import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO; import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData; import io.metersphere.functional.xmind.utils.XmindExportUtil; import io.metersphere.sdk.constants.LocalRepositoryDir; -import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.sdk.constants.MsgType; import io.metersphere.sdk.dto.ExportMsgDTO; import io.metersphere.sdk.exception.MSException; @@ -126,8 +125,7 @@ public class FunctionalCaseXmindService { tmpFile = new File(LocalRepositoryDir.getSystemTempDir() + File.separatorChar + EXPORT_CASE_TMP_DIR + "_" + IDGenerator.nextStr() + ".xmind"); List templateCustomFields = functionalCaseFileService.getCustomFields(request.getProjectId()); - TemplateCustomFieldDTO templateCustomFieldDTO = templateCustomFields.stream().filter(item -> StringUtils.equalsIgnoreCase(item.getFieldName(), Translator.get("custom_field.functional_priority"))).findFirst().get(); - XmindExportUtil.export(xmindData, request, tmpFile, templateCustomFieldDTO); + XmindExportUtil.export(xmindData, request, tmpFile, templateCustomFields); functionalCaseFileService.uploadFileToMinio(XMIND, tmpFile, request.getFileId()); functionalCaseLogService.exportExcelLog(request); @@ -180,14 +178,9 @@ public class FunctionalCaseXmindService { String moduleId = entry.getKey(); List dataList = entry.getValue(); List dtos = buildXmindDTO(dataList, functionalCaseBlobMap, customFieldMap); - if (StringUtils.equals(moduleId, ModuleConstants.DEFAULT_NODE_ID)) { - xmindData.setFunctionalCaseList(dtos); - } else { - LinkedList returnList = new LinkedList<>(); - LinkedList modulePathDataList = getModuleById(moduleId, tree, returnList); - xmindData.setItem(modulePathDataList, dtos); - System.out.println("modulePathDataList: " + modulePathDataList); - } + LinkedList returnList = new LinkedList<>(); + LinkedList modulePathDataList = getModuleById(moduleId, tree, returnList); + xmindData.setItem(modulePathDataList, dtos); } return xmindData; diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/xmind/utils/XmindExportUtil.java b/backend/services/case-management/src/main/java/io/metersphere/functional/xmind/utils/XmindExportUtil.java index 0999620322..33caadfad9 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/xmind/utils/XmindExportUtil.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/xmind/utils/XmindExportUtil.java @@ -4,6 +4,8 @@ import io.metersphere.functional.constants.FunctionalCaseTypeConstants; import io.metersphere.functional.domain.FunctionalCaseCustomField; import io.metersphere.functional.excel.domain.FunctionalCaseExportColumns; import io.metersphere.functional.excel.domain.FunctionalCaseHeader; +import io.metersphere.functional.excel.validate.AbstractCustomFieldValidator; +import io.metersphere.functional.excel.validate.CustomFieldValidatorFactory; import io.metersphere.functional.request.FunctionalCaseExportRequest; import io.metersphere.functional.xmind.domain.FunctionalCaseXmindDTO; import io.metersphere.functional.xmind.domain.FunctionalCaseXmindData; @@ -59,7 +61,7 @@ public class XmindExportUtil { } } - private static IWorkbook createXmindByCaseData(FunctionalCaseXmindData caseData, boolean template, Map> customFieldOptionsMap, FunctionalCaseExportRequest request, TemplateCustomFieldDTO priority) { + private static IWorkbook createXmindByCaseData(FunctionalCaseXmindData caseData, boolean template, Map> customFieldOptionsMap, FunctionalCaseExportRequest request, List templateCustomFields) { // 创建思维导图的工作空间 IWorkbookBuilder workbookBuilder = Core.getWorkbookBuilder(); IWorkbook workbook = workbookBuilder.createWorkbook(); @@ -84,7 +86,7 @@ public class XmindExportUtil { if (template) { addTemplateTopic(rootTopic, workbook, styleMap, data, true, customFieldOptionsMap); } else { - addTopic(rootTopic, workbook, styleMap, data, true, request, priority); + addTopic(rootTopic, workbook, styleMap, data, true, request, templateCustomFields); } } } @@ -365,8 +367,8 @@ public class XmindExportUtil { } - public static void export(FunctionalCaseXmindData xmindData, FunctionalCaseExportRequest request, File tmpFile, TemplateCustomFieldDTO priority) { - IWorkbook workBook = createXmindByCaseData(xmindData, false, null, request, priority); + public static void export(FunctionalCaseXmindData xmindData, FunctionalCaseExportRequest request, File tmpFile, List templateCustomFields) { + IWorkbook workBook = createXmindByCaseData(xmindData, false, null, request, templateCustomFields); try { workBook.save(tmpFile.getPath()); } catch (UnsupportedEncodingException e) { @@ -379,7 +381,7 @@ public class XmindExportUtil { } - private static void addTopic(ITopic parentTopic, IWorkbook workbook, Map styleMap, FunctionalCaseXmindData xmindData, boolean isFirstLevel, FunctionalCaseExportRequest request, TemplateCustomFieldDTO priority) { + private static void addTopic(ITopic parentTopic, IWorkbook workbook, Map styleMap, FunctionalCaseXmindData xmindData, boolean isFirstLevel, FunctionalCaseExportRequest request, List templateCustomFields) { ITopic topic = workbook.createTopic(); topic.setTitleText(xmindData.getModuleName()); if (isFirstLevel) { @@ -403,28 +405,30 @@ public class XmindExportUtil { if (style != null) { itemTopic.setStyleId(style.getId()); } - buildTopic(topic, style, dto, itemTopic, workbook, request, xmindData.getModuleName(), priority); + buildTopic(topic, style, dto, itemTopic, workbook, request, xmindData.getModuleName(), templateCustomFields); } } if (CollectionUtils.isNotEmpty(xmindData.getChildren())) { for (FunctionalCaseXmindData data : xmindData.getChildren()) { - addTopic(topic, workbook, styleMap, data, false, request, priority); + addTopic(topic, workbook, styleMap, data, false, request, templateCustomFields); } } } - private static void buildTopic(ITopic topic, IStyle style, FunctionalCaseXmindDTO dto, ITopic itemTopic, IWorkbook workbook, FunctionalCaseExportRequest request, String moduleName, TemplateCustomFieldDTO priority) { + private static void buildTopic(ITopic topic, IStyle style, FunctionalCaseXmindDTO dto, ITopic itemTopic, IWorkbook workbook, FunctionalCaseExportRequest request, String moduleName, List templateCustomFields) { List systemColumns = request.getSystemFields().stream().map(FunctionalCaseHeader::getId).toList(); FunctionalCaseExportColumns columns = new FunctionalCaseExportColumns(); Map customFieldMap = dto.getCustomFieldDTOList().stream().collect(Collectors.toMap(FunctionalCaseCustomField::getFieldId, FunctionalCaseCustomField::getValue)); //用例名称 + TemplateCustomFieldDTO priority = templateCustomFields.stream().filter(item -> StringUtils.equalsIgnoreCase(item.getFieldName(), Translator.get("custom_field.functional_priority"))).findFirst().get(); String casePriority = customFieldMap.get(priority.getFieldId()); - itemTopic.setTitleText("case-".concat(StringUtils.defaultIfBlank(casePriority,StringUtils.EMPTY)).concat(": ").concat(dto.getName())); + itemTopic.setTitleText("case-".concat(StringUtils.defaultIfBlank(casePriority, StringUtils.EMPTY)).concat(": ").concat(dto.getName())); //系统字段 systemColumns.forEach(item -> { - if (columns.getSystemColumns().containsKey(item) && !StringUtils.equalsIgnoreCase(item, "name")) { + if (columns.getSystemColumns().containsKey(item) && !StringUtils.equalsIgnoreCase(item, "name") + && !StringUtils.equalsIgnoreCase(item, "text_description") && !StringUtils.equalsIgnoreCase(item, "expected_result")) { ITopic preTopic = workbook.createTopic(); switch (item) { case "num": @@ -436,12 +440,6 @@ public class XmindExportUtil { case "module": preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(moduleName)); break; - case "text_description": - preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(dto.getTextDescription())); - break; - case "expected_result": - preTopic.setTitleText(columns.getSystemColumns().get(item).concat(": ").concat(dto.getExpectedResult())); - break; default: break; } @@ -452,14 +450,91 @@ public class XmindExportUtil { } }); + if (StringUtils.equalsIgnoreCase(dto.getCaseEditType(), FunctionalCaseTypeConstants.CaseEditType.TEXT.name())) { + //文本描述 + ITopic textDesTopic = workbook.createTopic(); + String desc = dto.getTextDescription(); + textDesTopic.setTitleText(Translator.get("xmind_textDescription").concat(": ").concat(dto.getTextDescription())); + if (style != null) { + textDesTopic.setStyleId(style.getId()); + } + + String result = dto.getExpectedResult(); + ITopic resultTopic = workbook.createTopic(); + resultTopic.setTitleText(Translator.get("xmind_expectedResult").concat(": ").concat(dto.getExpectedResult())); + if (style != null) { + resultTopic.setStyleId(style.getId()); + } + textDesTopic.add(resultTopic, ITopic.ATTACHED); + + if (StringUtils.isNotEmpty(desc) || StringUtils.isNotEmpty(result)) { + itemTopic.add(textDesTopic, ITopic.ATTACHED); + } + } else { + //步骤描述 + try { + ITopic stepDesTopic = workbook.createTopic(); + stepDesTopic.setTitleText(Translator.get("xmind_stepDescription")); + if (style != null) { + stepDesTopic.setStyleId(style.getId()); + } + List arr = JSON.parseArray(dto.getSteps()); + for (int i = 0; i < arr.size(); i++) { + Map obj = arr.get(i); + if (obj.containsKey("desc")) { + ITopic stepTopic = workbook.createTopic(); + String desc = obj.get("desc"); + stepTopic.setTitleText(Translator.get("xmind_step").concat(": ").concat(desc)); + if (style != null) { + stepTopic.setStyleId(style.getId()); + } + + boolean hasResult = false; + if (obj.containsKey("result")) { + String result = obj.get("result"); + if (StringUtils.isNotEmpty(result)) { + hasResult = true; + ITopic resultTopic = workbook.createTopic(); + resultTopic.setTitleText(Translator.get("xmind_expectedResult").concat(": ").concat(result)); + if (style != null) { + resultTopic.setStyleId(style.getId()); + } + stepTopic.add(resultTopic, ITopic.ATTACHED); + } + } + + if (StringUtils.isNotEmpty(desc) || hasResult) { + stepDesTopic.add(stepTopic, ITopic.ATTACHED); + } + + } + } + itemTopic.add(stepDesTopic, ITopic.ATTACHED); + } catch (Exception e) { + } + } + + //自定义字段 Map customColumnsMap = request.getCustomFields().stream().collect(Collectors.toMap(FunctionalCaseHeader::getId, FunctionalCaseHeader::getName)); - + Map temCustomFieldsMap = templateCustomFields.stream().collect(Collectors.toMap(TemplateCustomFieldDTO::getFieldId, i -> i)); + HashMap customFieldValidatorMap = CustomFieldValidatorFactory.getValidatorMap(); customColumnsMap.forEach((k, v) -> { - if (customFieldMap.containsKey(k)) { + if (customFieldMap.containsKey(k) && temCustomFieldsMap.containsKey(k)) { + AbstractCustomFieldValidator customFieldValidator = customFieldValidatorMap.get(temCustomFieldsMap.get(k).getType()); + String value = ""; + if (customFieldValidator.isKVOption) { + if (!temCustomFieldsMap.get(k).getInternal()) { + // 这里如果填的是选项值,替换成选项ID,保存 + value = customFieldValidator.parse2Value(customFieldMap.get(k), temCustomFieldsMap.get(k)).toString(); + } else { + value = customFieldMap.get(k); + } + } + ITopic preTopic = workbook.createTopic(); - preTopic.setTitleText(v.concat(": ").concat(customFieldMap.get(k))); + preTopic.setTitleText(v.concat(": ").concat(value)); if (style != null) { preTopic.setStyleId(style.getId()); } diff --git a/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue b/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue index 967c1eb40e..20febbefc2 100644 --- a/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue +++ b/frontend/src/views/case-management/caseManagementFeature/components/caseTable.vue @@ -1114,7 +1114,7 @@ try { const response = await getCaseDownloadFile(currentProjectId.value, reportId.value); const fileName = response?.headers.get('content-disposition').split('filename=')[1]; - downloadByteFile(response.data, fileName); + downloadByteFile(response.data, decodeURIComponent(fileName)); } catch (error) { // eslint-disable-next-line no-console console.log(error);