feat(测试用例): 测试用例支持导出Xmind #1001755

--story=1001755 --user=宋天阳 9.测试用例导出支持导出成.xmind
https://www.tapd.cn/55049933/s/1032481
This commit is contained in:
song-tianyang 2021-08-05 14:56:04 +08:00 committed by 刘瑞斌
parent 51fb710daa
commit 724d0d32f9
15 changed files with 583 additions and 37 deletions

View File

@ -434,7 +434,12 @@
<artifactId>Java-WebSocket</artifactId> <artifactId>Java-WebSocket</artifactId>
<version>1.3.5</version> <version>1.3.5</version>
</dependency> </dependency>
<!-- xmind export-->
<dependency>
<groupId>com.github.eljah</groupId>
<artifactId>xmindjbehaveplugin</artifactId>
<version>0.8</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -342,6 +342,10 @@
<property name="objectKey" value="request.combine.tags"/> <property name="objectKey" value="request.combine.tags"/>
</include> </include>
</if> </if>
<if test="request.statusIsNot != null">
and (test_case.status is null or test_case.status != #{request.statusIsNot})
</if>
<if test="request.name != null"> <if test="request.name != null">
and (test_case.name like CONCAT('%', #{request.name},'%') and (test_case.name like CONCAT('%', #{request.name},'%')
or test_case.num like CONCAT('%', #{request.name},'%') or test_case.num like CONCAT('%', #{request.name},'%')

View File

@ -196,6 +196,13 @@ public class TestCaseController {
testCaseService.testCaseExport(response, request); testCaseService.testCaseExport(response, request);
} }
@PostMapping("/export/testcase/xmind")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EXPORT)
@MsAuditLog(module = "track_test_case", type = OperLogConstants.EXPORT, sourceId = "#request.id", title = "#request.name", project = "#request.projectId")
public void testCaseXmindExport(HttpServletResponse response, @RequestBody TestCaseBatchRequest request) {
testCaseService.testCaseXmindExport(response, request);
}
@PostMapping("/batch/edit") @PostMapping("/batch/edit")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EDIT) @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EDIT)
@MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class)

View File

@ -29,6 +29,8 @@ public class QueryTestCaseRequest extends BaseQueryRequest {
private String nodeId; private String nodeId;
private String statusIsNot;
private long createTime = 0; private long createTime = 0;
private long relevanceCreateTime = 0; private long relevanceCreateTime = 0;
private List<String> testCaseContainIds; private List<String> testCaseContainIds;

View File

@ -749,4 +749,19 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
example.createCriteria().andIdEqualTo(nodeId); example.createCriteria().andIdEqualTo(nodeId);
return testCaseNodeMapper.countByExample(example); return testCaseNodeMapper.countByExample(example);
} }
public LinkedList<TestCaseNode> getPathNodeById(String moduleId) {
TestCaseNode testCaseNode = testCaseNodeMapper.selectByPrimaryKey(moduleId);
LinkedList<TestCaseNode> returnList = new LinkedList<>();
while (testCaseNode != null){
returnList.addFirst(testCaseNode);
if(testCaseNode.getParentId() == null){
testCaseNode = null;
}else {
testCaseNode = testCaseNodeMapper.selectByPrimaryKey(testCaseNode.getParentId());
}
}
return returnList;
}
} }

View File

@ -37,6 +37,8 @@ import io.metersphere.track.request.testcase.QueryTestCaseRequest;
import io.metersphere.track.request.testcase.TestCaseBatchRequest; import io.metersphere.track.request.testcase.TestCaseBatchRequest;
import io.metersphere.track.request.testcase.TestCaseMinderEditRequest; import io.metersphere.track.request.testcase.TestCaseMinderEditRequest;
import io.metersphere.xmind.XmindCaseParser; import io.metersphere.xmind.XmindCaseParser;
import io.metersphere.xmind.pojo.TestCaseXmindData;
import io.metersphere.xmind.utils.XmindExportUtil;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -47,7 +49,6 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -691,7 +692,7 @@ public class TestCaseService {
.andProjectIdEqualTo(projectId); .andProjectIdEqualTo(projectId);
List<TestCase> testCasesList = testCaseMapper.selectByExample(example); List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
Map<String, String> customIdMap = testCasesList.stream() Map<String, String> customIdMap = testCasesList.stream()
.collect(Collectors.toMap(TestCase::getCustomNum, TestCase::getId)); .collect(Collectors.toMap(TestCase::getCustomNum, TestCase::getId, (k1,k2) -> k1));
if (!testCases.isEmpty()) { if (!testCases.isEmpty()) {
@ -863,6 +864,56 @@ public class TestCaseService {
MSException.throwException(e); MSException.throwException(e);
} }
} }
public void testCaseXmindExport(HttpServletResponse response, TestCaseBatchRequest request) {
try {
request.getCondition().setStatusIsNot("Trash");
List<TestCaseDTO> testCaseDTOList= this.findByBatchRequest(request);
TestCaseXmindData rootXmindData = this.generateTestCaseXmind(testCaseDTOList);
boolean isUseCustomId = projectService.useCustomNum(request.getProjectId());
XmindExportUtil xmindExportUtil = new XmindExportUtil(isUseCustomId);
xmindExportUtil.exportXmind(response,rootXmindData);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(e);
}
}
private TestCaseXmindData generateTestCaseXmind(List<TestCaseDTO> testCaseDTOList) {
Map<String,List<TestCaseDTO>> moduleTestCaseMap = new HashMap<>();
for (TestCaseDTO dto : testCaseDTOList) {
String moduleId = dto.getNodeId();
if(StringUtils.isEmpty(moduleId)){
moduleId = "default";
}
if(moduleTestCaseMap.containsKey(moduleId)){
moduleTestCaseMap.get(moduleId).add(dto);
}else {
List<TestCaseDTO> list = new ArrayList<>();
list.add(dto);
moduleTestCaseMap.put(moduleId,list);
}
}
TestCaseXmindData rootMind = new TestCaseXmindData("ROOT","ROOT");
for (Map.Entry<String,List<TestCaseDTO>> entry:moduleTestCaseMap.entrySet()) {
String moduleId = entry.getKey();
List<TestCaseDTO> dataList = entry.getValue();
if(StringUtils.equals(moduleId,"ROOT")){
rootMind.setTestCaseList(dataList);
}else {
LinkedList<TestCaseNode> modulePathDataList = testCaseNodeService.getPathNodeById(moduleId);
rootMind.setItem(modulePathDataList,dataList);
}
}
return rootMind;
}
private List<List<Object>> generateTestCaseExcel(List<List<String>> headListParams,List<TestCaseExcelData> datas) { private List<List<Object>> generateTestCaseExcel(List<List<String>> headListParams,List<TestCaseExcelData> datas) {
List<List<Object>> returnDatas = new ArrayList<>(); List<List<Object>> returnDatas = new ArrayList<>();
//转化excel头 //转化excel头
@ -921,10 +972,10 @@ public class TestCaseService {
} }
return returnDatas; return returnDatas;
} }
private List<TestCaseExcelData> generateTestCaseExcel(TestCaseBatchRequest request) {
public List<TestCaseDTO> findByBatchRequest(TestCaseBatchRequest request){
ServiceUtils.getSelectAllIds(request, request.getCondition(), ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extTestCaseMapper.selectIds(query)); (query) -> extTestCaseMapper.selectIds(query));
boolean isUseCustomId = projectService.useCustomNum(request.getProjectId());
QueryTestCaseRequest condition = request.getCondition(); QueryTestCaseRequest condition = request.getCondition();
List<OrderRequest> orderList = new ArrayList<>(); List<OrderRequest> orderList = new ArrayList<>();
if (condition != null) { if (condition != null) {
@ -935,11 +986,18 @@ public class TestCaseService {
order.setType("desc"); order.setType("desc");
orderList.add(order); orderList.add(order);
request.setOrders(orderList); request.setOrders(orderList);
List<TestCaseDTO> TestCaseList = extTestCaseMapper.listByTestCaseIds(request); List<TestCaseDTO> testCaseList = extTestCaseMapper.listByTestCaseIds(request);
return testCaseList;
}
private List<TestCaseExcelData> generateTestCaseExcel(TestCaseBatchRequest request) {
request.getCondition().setStatusIsNot("Trash");
List<TestCaseDTO> testCaseList = this.findByBatchRequest(request);
boolean isUseCustomId = projectService.useCustomNum(request.getProjectId());
List<TestCaseExcelData> list = new ArrayList<>(); List<TestCaseExcelData> list = new ArrayList<>();
StringBuilder step = new StringBuilder(""); StringBuilder step = new StringBuilder("");
StringBuilder result = new StringBuilder(""); StringBuilder result = new StringBuilder("");
TestCaseList.forEach(t -> { testCaseList.forEach(t -> {
TestCaseExcelData data = new TestCaseExcelData(); TestCaseExcelData data = new TestCaseExcelData();
data.setNum(t.getNum()); data.setNum(t.getNum());
data.setName(t.getName()); data.setName(t.getName());

View File

@ -0,0 +1,73 @@
package io.metersphere.xmind.pojo;
import io.metersphere.base.domain.TestCaseNode;
import io.metersphere.track.dto.TestCaseDTO;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* @author song.tianyang
* @Date 2021/8/3 4:37 下午
*/
@Setter
@Getter
public class TestCaseXmindData {
private List<TestCaseDTO> testCaseList;
private String moduleName;
private String moduleId;
private List<TestCaseXmindData> children = new ArrayList<>();
public TestCaseXmindData(String moduleId, String moduleName) {
this.moduleId = moduleId;
this.moduleName = moduleName;
}
public void setItem(LinkedList<TestCaseNode> modulePathDataList, List<TestCaseDTO> dataList) {
if (CollectionUtils.isNotEmpty(modulePathDataList) && CollectionUtils.isNotEmpty(dataList)) {
if (modulePathDataList.size() == 1) {
this.setData(modulePathDataList.getFirst(), dataList);
} else {
TestCaseNode caseNode = modulePathDataList.getFirst();
if (caseNode != null) {
TestCaseXmindData matchedData = null;
for (TestCaseXmindData item : children) {
if (StringUtils.equals(item.getModuleId(), caseNode.getId())) {
matchedData = item;
break;
}
}
if(matchedData == null){
matchedData = new TestCaseXmindData(caseNode.getId(), caseNode.getName());
this.children.add(matchedData);
}
modulePathDataList.removeFirst();
matchedData.setItem(modulePathDataList,dataList);
}
}
}
}
public void setData(TestCaseNode caseNode, List<TestCaseDTO> dataList) {
if (caseNode != null && CollectionUtils.isNotEmpty(dataList)) {
boolean matching = false;
for (TestCaseXmindData item : children) {
if (StringUtils.equals(item.getModuleId(), caseNode.getId())) {
matching = true;
item.getTestCaseList().addAll(dataList);
}
}
if (!matching) {
TestCaseXmindData child = new TestCaseXmindData(caseNode.getId(), caseNode.getName());
child.setTestCaseList(dataList);
this.children.add(child);
}
}
}
}

View File

@ -0,0 +1,295 @@
package io.metersphere.xmind.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.exception.ExcelException;
import io.metersphere.i18n.Translator;
import io.metersphere.track.dto.TestCaseDTO;
import io.metersphere.xmind.pojo.TestCaseXmindData;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.xmind.core.*;
import org.xmind.core.style.IStyle;
import org.xmind.core.style.IStyleSheet;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* @author song.tianyang
* @Date 2021/7/30 11:05 上午
*/
public class XmindExportUtil {
boolean isUseCustomId = false;
public XmindExportUtil(boolean isUseCustomId) {
this.isUseCustomId = isUseCustomId;
}
public void exportXmind(HttpServletResponse response, TestCaseXmindData rootXmind) {
IWorkbook workBook = this.createXmindByTestCase(rootXmind);
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
try {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("TestCaseExport", "UTF-8") + ".xmind");
workBook.save(response.getOutputStream());
// EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("IO exception");
}
}
public void saveXmind(TestCaseXmindData rootXmind) throws IOException, CoreException {
this.createXmindByTestCase(rootXmind).save(File.separator + "User" + File.separator + "admin" + "/test.xmind");
}
public IWorkbook createXmindByTestCase(TestCaseXmindData rootXmind) {
// 创建思维导图的工作空间
IWorkbookBuilder workbookBuilder = Core.getWorkbookBuilder();
IWorkbook workbook = workbookBuilder.createWorkbook();
Map<String, IStyle> styleMap = this.initTheme(workbook);
// 获得默认sheet
ISheet primarySheet = workbook.getPrimarySheet();
if (styleMap.containsKey("mapStyle")) {
primarySheet.setStyleId(styleMap.get("mapStyle").getId());
}
// 获得根主题
ITopic rootTopic = primarySheet.getRootTopic();
if (styleMap.containsKey("centralTopicStyle")) {
rootTopic.setStyleId(styleMap.get("centralTopicStyle").getId());
}
// 设置根主题的标题
rootTopic.setTitleText(Translator.get("test_case"));
if (CollectionUtils.isNotEmpty(rootXmind.getChildren())) {
for (TestCaseXmindData data : rootXmind.getChildren()) {
addItemTopic(rootTopic, workbook, styleMap, data, true);
}
}
return workbook;
}
private Map<String, IStyle> initTheme(IWorkbook workbook) {
Map<String, IStyle> styleMap = new HashMap<>();
IStyleSheet styleSheet = workbook.getStyleSheet();
IStyle mapStyle = styleSheet.createStyle(IStyle.MAP);
mapStyle.setProperty("line-tapered","none");
mapStyle.setProperty("multi-line-colors","none");
mapStyle.setProperty("svg:fill","#FFFFFF");
mapStyle.setProperty("color-gradient","none");
styleSheet.addStyle(mapStyle, IStyleSheet.NORMAL_STYLES);
styleMap.put("mapStyle",mapStyle);
IStyle centralTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
centralTopicStyle.setProperty("line-width", "1pt");
centralTopicStyle.setProperty("svg:fill", "#DCE6F2");
centralTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
centralTopicStyle.setProperty("border-line-width", "5pt");
centralTopicStyle.setProperty("shape-class", "org.xmind.topicShape.roundedRect");
centralTopicStyle.setProperty("fo:color", "#376092");
centralTopicStyle.setProperty("line-class", "org.xmind.branchConnection.curve");
centralTopicStyle.setProperty("border-line-color", "#558ED5");
centralTopicStyle.setProperty("line-color", "#558ED5");
styleSheet.addStyle(centralTopicStyle, IStyleSheet.NORMAL_STYLES);
styleMap.put("centralTopicStyle", centralTopicStyle);
IStyle mainTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
mainTopicStyle.setProperty("line-width", "1pt");
mainTopicStyle.setProperty("svg:fill", "#DCE6F2");
mainTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
mainTopicStyle.setProperty("border-line-width", "2pt");
mainTopicStyle.setProperty("shape-class", "org.xmind.topicShape.roundedRect");
mainTopicStyle.setProperty("fo:color", "#17375E");
mainTopicStyle.setProperty("line-class", "org.xmind.branchConnection.curve");
mainTopicStyle.setProperty("border-line-color", "#558ED5");
mainTopicStyle.setProperty("line-color", "#558ED5");
styleSheet.addStyle(mainTopicStyle, IStyleSheet.NORMAL_STYLES);
styleMap.put("mainTopicStyle", mainTopicStyle);
IStyle subTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
subTopicStyle.setProperty("line-width", "1pt");
subTopicStyle.setProperty("fo:font-family", "Microsoft YaHei");
subTopicStyle.setProperty("border-line-width", "3pt");
subTopicStyle.setProperty("line-class", "org.xmind.branchConnection.curve");
subTopicStyle.setProperty("border-line-color", "#558ED5");
subTopicStyle.setProperty("line-color", "#558ED5");
styleSheet.addStyle(subTopicStyle, IStyleSheet.NORMAL_STYLES);
styleMap.put("subTopicStyle", subTopicStyle);
IStyle floatingTopicStyle = styleSheet.createStyle(IStyle.TOPIC);
floatingTopicStyle.setProperty("svg:fill","#558ED5");
floatingTopicStyle.setProperty("fo:font-family","Microsoft YaHei");
floatingTopicStyle.setProperty("border-line-width","0pt");
floatingTopicStyle.setProperty("fo:color","#FFFFFF");
floatingTopicStyle.setProperty("fo:font-weight","bold");
floatingTopicStyle.setProperty("line-color","#558ED5");
styleSheet.addStyle(floatingTopicStyle, IStyleSheet.NORMAL_STYLES);
styleMap.put("floatingTopicStyle", floatingTopicStyle);
IStyle summaryTopic = styleSheet.createStyle(IStyle.TOPIC);
summaryTopic.setProperty("fo:font-style","italic");
summaryTopic.setProperty("svg:fill","#77933C");
summaryTopic.setProperty("fo:font-family","Microsoft YaHei");
summaryTopic.setProperty("border-line-width","0pt");
summaryTopic.setProperty("fo:font-size","10pt");
summaryTopic.setProperty("shape-class","org.xmind.topicShape.roundedRect");
summaryTopic.setProperty("fo:color","#FFFFFF");
summaryTopic.setProperty("line-class","org.xmind.branchConnection.curve");
styleSheet.addStyle(summaryTopic, IStyleSheet.NORMAL_STYLES);
styleMap.put("summaryTopic", floatingTopicStyle);
IStyle itemTopic = styleSheet.createStyle(IStyle.TOPIC);
itemTopic.setProperty("fo:text-align","center");
itemTopic.setProperty("line-width","1pt");
itemTopic.setProperty("svg:fill","none");
itemTopic.setProperty("fo:font-family","Microsoft YaHei");
itemTopic.setProperty("border-line-width","2pt");
itemTopic.setProperty("shape-class","org.xmind.topicShape.underline");
itemTopic.setProperty("fo:font-size","14pt");
itemTopic.setProperty("fo:color","#17375E");
itemTopic.setProperty("line-class","org.xmind.branchConnection.curve");
itemTopic.setProperty("border-line-color","#558ED5");
itemTopic.setProperty("line-color","#558ED5");
styleSheet.addStyle(itemTopic, IStyleSheet.NORMAL_STYLES);
styleMap.put("itemTopic",itemTopic);
return styleMap;
}
private void addItemTopic(ITopic parentTpoic, IWorkbook workbook, Map<String, IStyle> styleMap, TestCaseXmindData xmindData, boolean isFirstLevel) {
ITopic topic = workbook.createTopic();
topic.setTitleText(xmindData.getModuleName());
if (isFirstLevel) {
if (styleMap.containsKey("mainTopicStyle")) {
topic.setStyleId(styleMap.get("mainTopicStyle").getId());
}
} else {
if (styleMap.containsKey("subTopicStyle")) {
topic.setStyleId(styleMap.get("subTopicStyle").getId());
}
}
parentTpoic.add(topic);
if (CollectionUtils.isNotEmpty(xmindData.getTestCaseList())) {
IStyle style = null;
if (styleMap.containsKey("subTopicStyle")) {
style = styleMap.get("subTopicStyle");
}
for (TestCaseDTO dto : xmindData.getTestCaseList()) {
// 创建小节节点
ITopic itemTopic = workbook.createTopic();
if (style != null) {
itemTopic.setStyleId(style.getId());
}
itemTopic.setTitleText("tc:" + dto.getName());
String id = dto.getNum().toString();
if (this.isUseCustomId) {
id = dto.getCustomNum();
}
ITopic idTopic = workbook.createTopic();
idTopic.setTitleText("id:" + id);
if (style != null) {
idTopic.setStyleId(style.getId());
}
itemTopic.add(idTopic, ITopic.ATTACHED);
if (dto.getPrerequisite() != null) {
ITopic pcTopic = workbook.createTopic();
pcTopic.setTitleText("pc:" + dto.getPrerequisite());
if (style != null) {
pcTopic.setStyleId(style.getId());
}
itemTopic.add(pcTopic, ITopic.ATTACHED);
}
if (dto.getRemark() != null) {
ITopic rcTopic = workbook.createTopic();
rcTopic.setTitleText("rc:" + dto.getRemark());
if (style != null) {
rcTopic.setStyleId(style.getId());
}
itemTopic.add(rcTopic, ITopic.ATTACHED);
}
if (dto.getTags() != null) {
try {
JSONArray arr = JSONArray.parseArray(dto.getTags());
String tagStr = "";
for (int i = 0; i < arr.size(); i++) {
tagStr = tagStr + arr.getString(i) + ",";
}
if (tagStr.endsWith(",")) {
tagStr = tagStr.substring(0, tagStr.length() - 1);
}
ITopic tagTopic = workbook.createTopic();
tagTopic.setTitleText("tag:" + tagStr);
if (style != null) {
tagTopic.setStyleId(style.getId());
}
itemTopic.add(tagTopic, ITopic.ATTACHED);
} catch (Exception e) {
}
}
if (dto.getSteps() != null) {
try {
JSONArray arr = JSONArray.parseArray(dto.getSteps());
for (int i = 0; i < arr.size(); i++) {
JSONObject obj = arr.getJSONObject(i);
if (obj.containsKey("desc")) {
ITopic stepTopic = workbook.createTopic();
String desc = obj.getString("desc");
stepTopic.setTitleText(desc);
if (style != null) {
stepTopic.setStyleId(style.getId());
}
boolean hasResult = false;
if (obj.containsKey("result")) {
String result = obj.getString("result");
if (StringUtils.isNotEmpty(result)) {
hasResult = true;
ITopic resultTopic = workbook.createTopic();
resultTopic.setTitleText(result);
if (style != null) {
resultTopic.setStyleId(style.getId());
}
stepTopic.add(resultTopic, ITopic.ATTACHED);
}
}
if (StringUtils.isNotEmpty(desc) || hasResult) {
itemTopic.add(stepTopic, ITopic.ATTACHED);
}
}
}
} catch (Exception e) {
}
}
topic.add(itemTopic);
}
}
if (CollectionUtils.isNotEmpty(xmindData.getChildren())) {
for (TestCaseXmindData data : xmindData.getChildren()) {
addItemTopic(topic, workbook, styleMap, data, false);
}
}
}
}

View File

@ -292,12 +292,12 @@ export default {
this.activeName = "default"; this.activeName = "default";
} }
}, },
exportTestCase() { exportTestCase(type) {
if (this.activeDom !== 'left') { if (this.activeDom !== 'left') {
this.$warning(this.$t('test_track.case.export.export_tip')); this.$warning(this.$t('test_track.case.export.export_tip'));
return; return;
} }
this.$refs.testCaseList.exportTestCase(); this.$refs.testCaseList.exportTestCase(type);
}, },
addListener() { addListener() {
let index = this.tabs.findIndex(item => item.name === this.activeName); // tabindex let index = this.tabs.findIndex(item => item.name === this.activeName); // tabindex

View File

@ -1,27 +1,85 @@
<template> <template>
<div> <el-dialog class="testcase-import" :title="$t('test_track.case.import.case_import')" :visible.sync="dialogVisible"
<el-row> @close="close">
<el-link type="primary" class="download-case"
@click="downloadCase"
>{{$t('test_track.case.import.download_case')}}
</el-link>
</el-row>
</div>
<el-row class="import-row" style="margin-left: 34px">
<el-radio v-model="exportType" label="excel">{{$t('commons.excelFile')}}</el-radio>
<el-radio v-model="exportType" label="xmind">{{$t('commons.xmindFile')}}</el-radio>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{$t('commons.cancel')}}</el-button>
<el-button type="primary" @click="exportTestCase">{{$t('commons.export')}}</el-button>
</span>
</el-dialog>
</template> </template>
<script> <script>
export default { import ElUploadList from "element-ui/packages/upload/src/upload-list";
name: "TestCaseExport", import MsTableButton from '../../../../components/common/components/MsTableButton';
methods: { import {getCurrentProjectID, listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
downloadCase() { import {TokenKey} from '../../../../../common/js/constants';
import axios from "axios";
} export default {
name: "TestCaseImport",
components: {ElUploadList, MsTableButton},
data() {
return {
exportType:"excel",
dialogVisible: false,
projectId:"",
}
},
created() {
},
activated() {
},
methods: {
handleError(err, file, fileList) {
this.isLoading = false;
this.$error(err.message);
},
open() {
listenGoBack(this.close);
this.projectId = getCurrentProjectID();
this.dialogVisible = true;
},
close() {
removeGoBackListener(this.close);
this.dialogVisible = false;
},
exportTestCase(){
this.$emit('exportTestCase',this.exportType);
this.close();
} }
} }
}
</script> </script>
<style>
</style>
<style scoped> <style scoped>
.download-template {
padding-top: 0px;
padding-bottom: 10px;
}
.import-row {
padding-top: 20px;
}
.testcase-import >>> .el-dialog {
width: 400px;
}
.testcase-import-img {
width: 614px;
height: 312px;
size: 200px;
}
</style> </style>

View File

@ -693,18 +693,31 @@ export default {
} }
this.$refs.testCaseImport.open(); this.$refs.testCaseImport.open();
}, },
exportTestCase() { exportTestCase(exportType) {
if (!this.projectId) { if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip')); this.$warning(this.$t('commons.check_project_tip'));
return; return;
} }
let config = { let config = {};
url: '/test/case/export/testcase', let fileNameSuffix = "";
method: 'post', if(exportType === 'xmind'){
responseType: 'blob', config = {
data: buildBatchParam(this, this.$refs.table.selectIds) url: '/test/case/export/testcase/xmind',
}; method: 'post',
responseType: 'blob',
data: buildBatchParam(this, this.$refs.table.selectIds)
};
fileNameSuffix = ".xmind";
}else {
config = {
url: '/test/case/export/testcase',
method: 'post',
responseType: 'blob',
data: buildBatchParam(this, this.$refs.table.selectIds)
};
fileNameSuffix = ".xlsx";
}
if (config.data.ids === undefined || config.data.ids.length < 1) { if (config.data.ids === undefined || config.data.ids.length < 1) {
this.$warning(this.$t("test_track.case.check_select")); this.$warning(this.$t("test_track.case.check_select"));
@ -712,7 +725,7 @@ export default {
} }
this.page.result = this.$request(config).then(response => { this.page.result = this.$request(config).then(response => {
const filename = "Metersphere_case_" + this.projectName+ ".xlsx"; const filename = "Metersphere_case_" + this.projectName+ fileNameSuffix;
const blob = new Blob([response.data]); const blob = new Blob([response.data]);
if ("download" in document.createElement("a")) { if ("download" in document.createElement("a")) {
let aTag = document.createElement('a'); let aTag = document.createElement('a');
@ -728,7 +741,7 @@ export default {
handleBatch(type) { handleBatch(type) {
if (this.$refs.selectRows.size < 1) { if (this.$refs.selectRows.size < 1) {
if (type === 'export') { if (type === 'export') {
this.exportTestCase(); this.handleExportTestCase();
return; return;
} else { } else {
this.$warning(this.$t('test_track.plan_view.select_manipulate')); this.$warning(this.$t('test_track.plan_view.select_manipulate'));
@ -741,7 +754,7 @@ export default {
} else if (type === 'delete') { } else if (type === 'delete') {
this.handleDeleteBatch(); this.handleDeleteBatch();
} else { } else {
this.exportTestCase(); this.handleExportTestCase();
} }
}, },
batchEdit(form) { batchEdit(form) {

View File

@ -25,6 +25,7 @@
</template> </template>
</ms-node-tree> </ms-node-tree>
<test-case-import @refreshAll="refreshAll" ref="testCaseImport"></test-case-import> <test-case-import @refreshAll="refreshAll" ref="testCaseImport"></test-case-import>
<test-case-export @refreshAll="refreshAll" @exportTestCase="exportTestCase" ref="testCaseExport"></test-case-export>
<test-case-create <test-case-create
:tree-nodes="treeNodes" :tree-nodes="treeNodes"
@saveAsEdit="saveAsEdit" @saveAsEdit="saveAsEdit"
@ -41,6 +42,7 @@ import NodeEdit from "./NodeEdit";
import MsNodeTree from "./NodeTree"; import MsNodeTree from "./NodeTree";
import TestCaseCreate from "@/business/components/track/case/components/TestCaseCreate"; import TestCaseCreate from "@/business/components/track/case/components/TestCaseCreate";
import TestCaseImport from "@/business/components/track/case/components/TestCaseImport"; import TestCaseImport from "@/business/components/track/case/components/TestCaseImport";
import TestCaseExport from "@/business/components/track/case/components/TestCaseExport";
import MsSearchBar from "@/business/components/common/components/search/MsSearchBar"; import MsSearchBar from "@/business/components/common/components/search/MsSearchBar";
import {buildTree} from "../../api/definition/model/NodeTree"; import {buildTree} from "../../api/definition/model/NodeTree";
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree"; import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
@ -49,7 +51,7 @@ import ModuleTrashButton from "@/business/components/api/definition/components/m
export default { export default {
name: "TestCaseNodeTree", name: "TestCaseNodeTree",
components: {MsSearchBar, TestCaseImport, TestCaseCreate, MsNodeTree, NodeEdit,ModuleTrashButton}, components: {MsSearchBar, TestCaseImport,TestCaseExport, TestCaseCreate, MsNodeTree, NodeEdit,ModuleTrashButton},
data() { data() {
return { return {
defaultProps: { defaultProps: {
@ -75,9 +77,7 @@ export default {
}, },
{ {
label: this.$t('api_test.export_config'), label: this.$t('api_test.export_config'),
callback: () => { callback: this.handleExport,
this.$emit('exportTestCase');
},
permissions: ['PROJECT_TRACK_CASE:READ+EXPORT'] permissions: ['PROJECT_TRACK_CASE:READ+EXPORT']
} }
] ]
@ -176,6 +176,16 @@ export default {
} }
this.$refs.testCaseImport.open(); this.$refs.testCaseImport.open();
}, },
handleExport() {
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.$refs.testCaseExport.open();
},
exportTestCase(type){
this.$emit('exportTestCase',type);
},
remove(nodeIds) { remove(nodeIds) {
this.$post("/case/node/delete", nodeIds, () => { this.$post("/case/node/delete", nodeIds, () => {
this.list(); this.list();

View File

@ -6,6 +6,8 @@ export default {
trash: "Trash", trash: "Trash",
yes: "yes", yes: "yes",
no: "no", no: "no",
excelFile: "Excel",
xmindFile: "Xmind",
default: "default", default: "default",
please_select_import_mode: 'Please select import mode', please_select_import_mode: 'Please select import mode',
please_select_import_module: 'Please select import module', please_select_import_module: 'Please select import module',

View File

@ -6,6 +6,8 @@ export default {
trash: "回收站", trash: "回收站",
yes: "是", yes: "是",
no: "否", no: "否",
excelFile: "表格文件.xls",
xmindFile: "思维导图.xmind",
default: "默认值", default: "默认值",
please_select_import_mode: '请选择导入模式', please_select_import_mode: '请选择导入模式',
please_select_import_module: '请选择导入模块', please_select_import_module: '请选择导入模块',

View File

@ -6,6 +6,8 @@ export default {
trash: "回收站", trash: "回收站",
yes: "是", yes: "是",
no: "否", no: "否",
excelFile: "表格文件.xls",
xmindFile: "思維導圖.xmind",
default: "默認值", default: "默認值",
please_select_import_mode: '請選擇導入模式', please_select_import_mode: '請選擇導入模式',
please_select_import_module: '請選擇導入模塊', please_select_import_module: '請選擇導入模塊',