Merge remote-tracking branch 'origin/v1.3' into v1.3
This commit is contained in:
commit
7a420e26e9
|
@ -12,6 +12,7 @@ import io.metersphere.commons.utils.PageUtils;
|
|||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.controller.request.QueryScheduleRequest;
|
||||
import io.metersphere.dto.LicenseDTO;
|
||||
import io.metersphere.dto.ScheduleDao;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
|
@ -19,7 +20,6 @@ import org.springframework.web.bind.annotation.*;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
|
@ -127,4 +127,10 @@ public class APITestController {
|
|||
public List<ScheduleDao> listSchedule(@RequestBody QueryScheduleRequest request) {
|
||||
return apiTestService.listSchedule(request);
|
||||
}
|
||||
|
||||
@GetMapping("/license/valid")
|
||||
public LicenseDTO valid() {
|
||||
return apiTestService.validateLicense();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.metersphere.api.parse.JmeterDocumentParser;
|
|||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.ApiTestFileMapper;
|
||||
import io.metersphere.base.mapper.ApiTestMapper;
|
||||
import io.metersphere.base.mapper.UserMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiTestMapper;
|
||||
import io.metersphere.commons.constants.APITestStatus;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
|
@ -21,15 +20,13 @@ import io.metersphere.commons.constants.ScheduleType;
|
|||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.controller.request.QueryScheduleRequest;
|
||||
import io.metersphere.dto.LicenseDTO;
|
||||
import io.metersphere.dto.ScheduleDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.job.sechedule.ApiTestJob;
|
||||
import io.metersphere.notice.service.MailService;
|
||||
import io.metersphere.notice.service.NoticeService;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.service.QuotaService;
|
||||
import io.metersphere.service.ScheduleService;
|
||||
import io.metersphere.service.UserService;
|
||||
import io.metersphere.service.*;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
|
@ -40,7 +37,6 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -441,4 +437,13 @@ public class APITestService {
|
|||
quotaService.checkAPITestQuota();
|
||||
}
|
||||
}
|
||||
|
||||
public LicenseDTO validateLicense() {
|
||||
LicenseService licenseService = CommonBeanFactory.getBean(LicenseService.class);
|
||||
if (licenseService != null) {
|
||||
return licenseService.valid();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,5 +28,7 @@ public class Issues implements Serializable {
|
|||
|
||||
private String projectName;
|
||||
|
||||
private String currentOwner;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,16 @@
|
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestCaseReviewMapper">
|
||||
|
||||
<select id="list" resultType="io.metersphere.track.dto.TestCaseReviewDTO" parameterType="io.metersphere.track.request.testreview.QueryCaseReviewRequest">
|
||||
select distinct test_case_review.*
|
||||
from test_case_review, project, test_case_review_project
|
||||
<select id="list" resultType="io.metersphere.track.dto.TestCaseReviewDTO"
|
||||
parameterType="io.metersphere.track.request.testreview.QueryCaseReviewRequest">
|
||||
select distinct test_case_review.id, test_case_review.name, user.name as creator, test_case_review.status,
|
||||
test_case_review.create_time, test_case_review.update_time, test_case_review.end_time,
|
||||
test_case_review.description
|
||||
from test_case_review, project, test_case_review_project, user
|
||||
<where>
|
||||
test_case_review.id = test_case_review_project.review_id
|
||||
and test_case_review_project.project_id = project.id
|
||||
and user.id = test_case_review.creator
|
||||
<if test="request.name != null">
|
||||
and test_case_review.name like CONCAT('%', #{request.name},'%')
|
||||
</if>
|
||||
|
@ -21,7 +25,8 @@
|
|||
</if>
|
||||
</select>
|
||||
|
||||
<select id="listByWorkspaceId" resultType="io.metersphere.track.dto.TestCaseReviewDTO" parameterType="io.metersphere.track.request.testreview.QueryCaseReviewRequest">
|
||||
<select id="listByWorkspaceId" resultType="io.metersphere.track.dto.TestCaseReviewDTO"
|
||||
parameterType="io.metersphere.track.request.testreview.QueryCaseReviewRequest">
|
||||
select distinct test_case_review.*
|
||||
from test_case_review, project, test_case_review_project
|
||||
where test_case_review.id = test_case_review_project.review_id
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class LicenseDTO implements Serializable {
|
||||
|
||||
private String status;
|
||||
|
||||
private LicenseInfoDTO license;
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.metersphere.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class LicenseInfoDTO implements Serializable {
|
||||
// 客户名称
|
||||
private String corporation;
|
||||
// 授权截止时间
|
||||
private String expired;
|
||||
//产品名称
|
||||
private String product;
|
||||
//产品版本
|
||||
private String edition;
|
||||
//icense版本
|
||||
private String licenseVersion;
|
||||
//授权数量
|
||||
private int licenseCount;
|
||||
}
|
|
@ -76,7 +76,7 @@ public class MailService {
|
|||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <h3>" + type + "定时任务结果通知</h3>\n" +
|
||||
" <p> 尊敬的用户:您好,您所执行的" + testName + "运行失败,请点击报告链接查看</p>\n" +
|
||||
" <p> 尊敬的用户:您好,您所执行的" + testName + "运行失败</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
|
@ -89,7 +89,7 @@ public class MailService {
|
|||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <h3>" + type + "定时任务结果通知</h3>\n" +
|
||||
" <p> 尊敬的用户:您好," + testName + "运行成功,请点击报告链接查看</p>\n" +
|
||||
" <p> 尊敬的用户:您好," + testName + "运行成功</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
|
|
|
@ -238,8 +238,17 @@ public class PerformanceTestService {
|
|||
|
||||
startEngine(loadTest, engine, request.getTriggerMode());
|
||||
if (request.getTriggerMode().equals("SCHEDULE")) {
|
||||
List<Notice> notice = noticeService.queryNotice(request.getId());
|
||||
mailService.sendHtml(engine.getReportId(), notice, "status", "performance");
|
||||
List<Notice> notice = null;
|
||||
try {
|
||||
notice = noticeService.queryNotice(request.getId());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
mailService.sendHtml(engine.getReportId(), notice, "status", "performance");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return engine.getReportId();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import io.metersphere.dto.LicenseDTO;
|
||||
|
||||
public interface LicenseService {
|
||||
|
||||
public LicenseDTO valid();
|
||||
|
||||
public LicenseDTO addValidLicense(String reqLicenseCode);
|
||||
}
|
|
@ -67,7 +67,7 @@ public class TestCaseReviewController {
|
|||
|
||||
@PostMapping("/edit")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void editCaseReview(@RequestBody TestCaseReview testCaseReview) {
|
||||
public void editCaseReview(@RequestBody SaveTestCaseReviewRequest testCaseReview) {
|
||||
testCaseReviewService.editCaseReview(testCaseReview);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.apache.ibatis.session.SqlSession;
|
|||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -54,6 +56,10 @@ public class TestCaseReviewService {
|
|||
ExtProjectMapper extProjectMapper;
|
||||
@Resource
|
||||
UserService userService;
|
||||
@Resource
|
||||
TestCaseMapper testCaseMapper;
|
||||
@Resource
|
||||
TestCaseReviewTestCaseMapper testCaseReviewTestCaseMapper;
|
||||
|
||||
public void saveTestCaseReview(SaveTestCaseReviewRequest reviewRequest) {
|
||||
checkCaseReviewExist(reviewRequest);
|
||||
|
@ -119,20 +125,87 @@ public class TestCaseReviewService {
|
|||
.collect(Collectors.toList());
|
||||
|
||||
UserExample userExample = new UserExample();
|
||||
userExample.createCriteria().andIdIn(userIds);
|
||||
return userMapper.selectByExample(userExample);
|
||||
UserExample.Criteria criteria = userExample.createCriteria();
|
||||
if (!CollectionUtils.isEmpty(userIds)) {
|
||||
criteria.andIdIn(userIds);
|
||||
return userMapper.selectByExample(userExample);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<TestCaseReviewDTO> recent(String currentWorkspaceId) {
|
||||
return extTestCaseReviewMapper.listByWorkspaceId(currentWorkspaceId);
|
||||
}
|
||||
|
||||
public void editCaseReview(TestCaseReview testCaseReview) {
|
||||
public void editCaseReview(SaveTestCaseReviewRequest testCaseReview) {
|
||||
editCaseReviewer(testCaseReview);
|
||||
editCaseReviewProject(testCaseReview);
|
||||
testCaseReview.setUpdateTime(System.currentTimeMillis());
|
||||
checkCaseReviewExist(testCaseReview);
|
||||
testCaseReviewMapper.updateByPrimaryKeySelective(testCaseReview);
|
||||
}
|
||||
|
||||
private void editCaseReviewer(SaveTestCaseReviewRequest testCaseReview) {
|
||||
// 要更新的reviewerIds
|
||||
List<String> reviewerIds = testCaseReview.getUserIds();
|
||||
|
||||
String id = testCaseReview.getId();
|
||||
TestCaseReviewUsersExample testCaseReviewUsersExample = new TestCaseReviewUsersExample();
|
||||
testCaseReviewUsersExample.createCriteria().andReviewIdEqualTo(id);
|
||||
List<TestCaseReviewUsers> testCaseReviewUsers = testCaseReviewUsersMapper.selectByExample(testCaseReviewUsersExample);
|
||||
List<String> dbReviewIds = testCaseReviewUsers.stream().map(TestCaseReviewUsers::getUserId).collect(Collectors.toList());
|
||||
|
||||
reviewerIds.forEach(reviewerId -> {
|
||||
if (!dbReviewIds.contains(reviewerId)) {
|
||||
TestCaseReviewUsers caseReviewUser = new TestCaseReviewUsers();
|
||||
caseReviewUser.setUserId(reviewerId);
|
||||
caseReviewUser.setReviewId(id);
|
||||
testCaseReviewUsersMapper.insertSelective(caseReviewUser);
|
||||
}
|
||||
});
|
||||
|
||||
TestCaseReviewUsersExample example = new TestCaseReviewUsersExample();
|
||||
example.createCriteria().andReviewIdEqualTo(id).andUserIdNotIn(reviewerIds);
|
||||
testCaseReviewUsersMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
private void editCaseReviewProject(SaveTestCaseReviewRequest testCaseReview) {
|
||||
List<String> projectIds = testCaseReview.getProjectIds();
|
||||
String id = testCaseReview.getId();
|
||||
if (!CollectionUtils.isEmpty(projectIds)) {
|
||||
TestCaseReviewProjectExample testCaseReviewProjectExample = new TestCaseReviewProjectExample();
|
||||
testCaseReviewProjectExample.createCriteria().andReviewIdEqualTo(id);
|
||||
List<TestCaseReviewProject> testCaseReviewProjects = testCaseReviewProjectMapper.selectByExample(testCaseReviewProjectExample);
|
||||
List<String> dbProjectIds = testCaseReviewProjects.stream().map(TestCaseReviewProject::getProjectId).collect(Collectors.toList());
|
||||
projectIds.forEach(projectId -> {
|
||||
if (!dbProjectIds.contains(projectId)) {
|
||||
TestCaseReviewProject testCaseReviewProject = new TestCaseReviewProject();
|
||||
testCaseReviewProject.setReviewId(id);
|
||||
testCaseReviewProject.setProjectId(projectId);
|
||||
testCaseReviewProjectMapper.insert(testCaseReviewProject);
|
||||
}
|
||||
});
|
||||
|
||||
TestCaseReviewProjectExample example = new TestCaseReviewProjectExample();
|
||||
example.createCriteria().andReviewIdEqualTo(id).andProjectIdNotIn(projectIds);
|
||||
testCaseReviewProjectMapper.deleteByExample(example);
|
||||
|
||||
|
||||
// 关联的项目下的用例idList
|
||||
TestCaseExample testCaseExample = new TestCaseExample();
|
||||
testCaseExample.createCriteria().andProjectIdIn(projectIds);
|
||||
List<TestCase> caseList = testCaseMapper.selectByExample(testCaseExample);
|
||||
List<String> caseIds = caseList.stream().map(TestCase::getId).collect(Collectors.toList());
|
||||
|
||||
TestCaseReviewTestCaseExample testCaseReviewTestCaseExample = new TestCaseReviewTestCaseExample();
|
||||
TestCaseReviewTestCaseExample.Criteria criteria = testCaseReviewTestCaseExample.createCriteria().andReviewIdEqualTo(id);
|
||||
if (!CollectionUtils.isEmpty(caseIds)) {
|
||||
criteria.andCaseIdNotIn(caseIds);
|
||||
}
|
||||
testCaseReviewTestCaseMapper.deleteByExample(testCaseReviewTestCaseExample);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCaseReviewExist(TestCaseReview testCaseReview) {
|
||||
if (testCaseReview.getName() != null) {
|
||||
TestCaseReviewExample example = new TestCaseReviewExample();
|
||||
|
@ -153,6 +226,7 @@ public class TestCaseReviewService {
|
|||
public void deleteCaseReview(String reviewId) {
|
||||
deleteCaseReviewProject(reviewId);
|
||||
deleteCaseReviewUsers(reviewId);
|
||||
deleteCaseReviewTestCase(reviewId);
|
||||
testCaseReviewMapper.deleteByPrimaryKey(reviewId);
|
||||
}
|
||||
|
||||
|
@ -168,6 +242,12 @@ public class TestCaseReviewService {
|
|||
testCaseReviewUsersMapper.deleteByExample(testCaseReviewUsersExample);
|
||||
}
|
||||
|
||||
private void deleteCaseReviewTestCase(String reviewId) {
|
||||
TestCaseReviewTestCaseExample testCaseReviewTestCaseExample = new TestCaseReviewTestCaseExample();
|
||||
testCaseReviewTestCaseExample.createCriteria().andReviewIdEqualTo(reviewId);
|
||||
testCaseReviewTestCaseMapper.deleteByExample(testCaseReviewTestCaseExample);
|
||||
}
|
||||
|
||||
public List<TestCaseReview> listCaseReviewAll(String currentWorkspaceId) {
|
||||
ProjectExample projectExample = new ProjectExample();
|
||||
projectExample.createCriteria().andWorkspaceIdEqualTo(currentWorkspaceId);
|
||||
|
@ -311,7 +391,7 @@ public class TestCaseReviewService {
|
|||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
public List<TestReviewCaseDTO> listTestCaseByProjectIds(List<String> projectIds) {
|
||||
QueryCaseReviewRequest request = new QueryCaseReviewRequest();
|
||||
request.setProjectIds(projectIds);
|
||||
|
|
|
@ -27,7 +27,7 @@ import io.metersphere.i18n.Translator;
|
|||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
|
||||
import io.metersphere.xmind.XmindToTestCaseParser;
|
||||
import io.metersphere.xmind.XmindCaseParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
@ -273,13 +273,18 @@ public class TestCaseService {
|
|||
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
|
||||
try {
|
||||
errList = new ArrayList<>();
|
||||
String processLog = new XmindToTestCaseParser(this, userId, projectId, testCaseNames).importXmind(multipartFile);
|
||||
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames);
|
||||
String processLog = xmindParser.parse(multipartFile);
|
||||
if (!StringUtils.isEmpty(processLog)) {
|
||||
excelResponse.setSuccess(false);
|
||||
ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail")+":"+ processLog);
|
||||
ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail") + ":" + processLog);
|
||||
errList.add(excelErrData);
|
||||
excelResponse.setErrList(errList);
|
||||
} else {
|
||||
if (!xmindParser.getTestCase().isEmpty()) {
|
||||
this.saveImportData(xmindParser.getTestCase(), projectId);
|
||||
xmindParser.clear();
|
||||
}
|
||||
excelResponse.setSuccess(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -345,7 +350,7 @@ public class TestCaseService {
|
|||
// 发送给客户端的数据
|
||||
byte[] buff = new byte[1024];
|
||||
try (OutputStream outputStream = res.getOutputStream();
|
||||
BufferedInputStream bis = new BufferedInputStream(TestCaseService.class.getResourceAsStream("/io/metersphere/xmind/template/testcase.xml"));) {
|
||||
BufferedInputStream bis = new BufferedInputStream(TestCaseService.class.getResourceAsStream("/io/metersphere/xmind/template/xmind.xml"));) {
|
||||
int i = bis.read(buff);
|
||||
while (i != -1) {
|
||||
outputStream.write(buff, 0, buff.length);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.metersphere.xmind;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
|
@ -11,51 +10,60 @@ import io.metersphere.excel.domain.TestCaseExcelData;
|
|||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import io.metersphere.xmind.parser.XmindParser;
|
||||
import io.metersphere.xmind.parser.domain.Attached;
|
||||
import io.metersphere.xmind.parser.domain.JsonRootBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
import io.metersphere.xmind.parser.pojo.Attached;
|
||||
import io.metersphere.xmind.parser.pojo.JsonRootBean;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 数据转换
|
||||
* 1 解析Xmind文件 XmindParser.parseJson
|
||||
* 2 解析后的JSON 转成测试用例
|
||||
* 1 解析Xmind文件 XmindParser.parseObject
|
||||
* 2 解析后的JSON this.parse 转成测试用例
|
||||
*/
|
||||
public class XmindToTestCaseParser {
|
||||
public class XmindCaseParser {
|
||||
|
||||
private TestCaseService testCaseService;
|
||||
private String maintainer;
|
||||
private String projectId;
|
||||
private StringBuffer process; // 过程校验记录
|
||||
// 已存在用例名称
|
||||
private Set<String> testCaseNames;
|
||||
|
||||
public XmindToTestCaseParser(TestCaseService testCaseService, String userId, String projectId, Set<String> testCaseNames) {
|
||||
// 转换后的案例信息
|
||||
private List<TestCaseWithBLOBs> testCases;
|
||||
|
||||
// 案例详情重写了hashCode方法去重用
|
||||
private List<TestCaseExcelData> compartDatas;
|
||||
|
||||
public XmindCaseParser(TestCaseService testCaseService, String userId, String projectId, Set<String> testCaseNames) {
|
||||
this.testCaseService = testCaseService;
|
||||
this.maintainer = userId;
|
||||
this.projectId = projectId;
|
||||
this.testCaseNames = testCaseNames;
|
||||
testCaseWithBLOBs = new LinkedList<>();
|
||||
xmindDataList = new ArrayList<>();
|
||||
testCases = new LinkedList<>();
|
||||
compartDatas = new ArrayList<>();
|
||||
process = new StringBuffer();
|
||||
}
|
||||
|
||||
// 案例详情
|
||||
private List<TestCaseWithBLOBs> testCaseWithBLOBs;
|
||||
// 用于重复对比
|
||||
protected List<TestCaseExcelData> xmindDataList;
|
||||
// 这里清理是为了 加快jvm 回收
|
||||
public void clear() {
|
||||
compartDatas.clear();
|
||||
testCases.clear();
|
||||
testCaseNames.clear();
|
||||
}
|
||||
|
||||
public List<TestCaseWithBLOBs> getTestCase() {
|
||||
return this.testCases;
|
||||
}
|
||||
|
||||
// 递归处理案例数据
|
||||
private void makeXmind(StringBuffer processBuffer, Attached parent, int level, String nodePath, List<Attached> attacheds) {
|
||||
private void recursion(StringBuffer processBuffer, Attached parent, int level, String nodePath, List<Attached> attacheds) {
|
||||
for (Attached item : attacheds) {
|
||||
if (isBlack(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例
|
||||
if (isAvailable(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例
|
||||
item.setParent(parent);
|
||||
this.newTestCase(item.getTitle(), parent.getPath(), item.getChildren() != null ? item.getChildren().getAttached() : null);
|
||||
} else {
|
||||
|
@ -63,14 +71,13 @@ public class XmindToTestCaseParser {
|
|||
item.setPath(nodePath);
|
||||
if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty()) {
|
||||
item.setParent(parent);
|
||||
makeXmind(processBuffer, item, level + 1, nodePath, item.getChildren().getAttached());
|
||||
recursion(processBuffer, item, level + 1, nodePath, item.getChildren().getAttached());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBlack(String str, String regex) {
|
||||
// regex = "(?:tc:|tc:)"
|
||||
private boolean isAvailable(String str, String regex) {
|
||||
if (StringUtils.isEmpty(str) || StringUtils.isEmpty(regex))
|
||||
return false;
|
||||
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
|
||||
|
@ -88,7 +95,7 @@ public class XmindToTestCaseParser {
|
|||
}
|
||||
|
||||
// 获取步骤数据
|
||||
public String getSteps(List<Attached> attacheds) {
|
||||
private String getSteps(List<Attached> attacheds) {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (int i = 0; i < attacheds.size(); i++) {
|
||||
// 保持插入顺序,判断用例是否有相同的steps
|
||||
|
@ -148,9 +155,9 @@ public class XmindToTestCaseParser {
|
|||
List<Attached> steps = new LinkedList<>();
|
||||
if (attacheds != null && !attacheds.isEmpty()) {
|
||||
attacheds.forEach(item -> {
|
||||
if (isBlack(item.getTitle(), "(?:pc:|pc:)")) {
|
||||
if (isAvailable(item.getTitle(), "(?:pc:|pc:)")) {
|
||||
testCase.setPrerequisite(replace(item.getTitle(), "(?:pc:|pc:)"));
|
||||
} else if (isBlack(item.getTitle(), "(?:rc:|rc:)")) {
|
||||
} else if (isAvailable(item.getTitle(), "(?:rc:|rc:)")) {
|
||||
testCase.setRemark(replace(item.getTitle(), "(?:rc:|rc:)"));
|
||||
} else {
|
||||
steps.add(item);
|
||||
|
@ -171,74 +178,42 @@ public class XmindToTestCaseParser {
|
|||
}
|
||||
TestCaseExcelData compartData = new TestCaseExcelData();
|
||||
BeanUtils.copyBean(compartData, testCase);
|
||||
if (xmindDataList.contains(compartData)) {
|
||||
if (compartDatas.contains(compartData)) {
|
||||
process.append(Translator.get("test_case_already_exists_excel") + ":" + testCase.getName() + "; ");
|
||||
} else if (validate(testCase)) {
|
||||
testCase.setId(UUID.randomUUID().toString());
|
||||
testCase.setCreateTime(System.currentTimeMillis());
|
||||
testCase.setUpdateTime(System.currentTimeMillis());
|
||||
testCaseWithBLOBs.add(testCase);
|
||||
testCases.add(testCase);
|
||||
}
|
||||
xmindDataList.add(compartData);
|
||||
compartDatas.add(compartData);
|
||||
}
|
||||
|
||||
//获取流文件
|
||||
private static void inputStreamToFile(InputStream ins, File file) {
|
||||
try (OutputStream os = new FileOutputStream(file);) {
|
||||
int bytesRead = 0;
|
||||
byte[] buffer = new byte[8192];
|
||||
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MultipartFile 转 File
|
||||
*
|
||||
* @param file
|
||||
* @throws Exception
|
||||
*/
|
||||
private File multipartFileToFile(MultipartFile file) throws Exception {
|
||||
if (file != null && file.getSize() > 0) {
|
||||
try (InputStream ins = file.getInputStream();) {
|
||||
File toFile = new File(file.getOriginalFilename());
|
||||
inputStreamToFile(ins, toFile);
|
||||
return toFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public boolean validate(TestCaseWithBLOBs data) {
|
||||
// 验证合法性
|
||||
private boolean validate(TestCaseWithBLOBs data) {
|
||||
String nodePath = data.getNodePath();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if (nodePath != null) {
|
||||
if (!StringUtils.isEmpty(nodePath)) {
|
||||
String[] nodes = nodePath.split("/");
|
||||
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||
stringBuilder.append(Translator.get("test_case_node_level_tip") +
|
||||
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
|
||||
}
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
if (i != 0 && org.apache.commons.lang3.StringUtils.equals(nodes[i].trim(), "")) {
|
||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
||||
stringBuilder.append(Translator.get("module_not_null") + "; ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (org.apache.commons.lang3.StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && org.apache.commons.lang3.StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
||||
if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
||||
stringBuilder.append(Translator.get("functional_method_tip") + "; ");
|
||||
}
|
||||
|
||||
if (testCaseNames.contains(data.getName())) {
|
||||
boolean dbExist = testCaseService.exist(data);
|
||||
boolean excelExist = false;
|
||||
|
||||
if (dbExist) {
|
||||
// db exist
|
||||
stringBuilder.append(Translator.get("test_case_already_exists_excel") + ":" + data.getName() + "; ");
|
||||
|
@ -255,49 +230,29 @@ public class XmindToTestCaseParser {
|
|||
}
|
||||
|
||||
// 导入思维导图处理
|
||||
public String importXmind(MultipartFile multipartFile) {
|
||||
public String parse(MultipartFile multipartFile) {
|
||||
StringBuffer processBuffer = new StringBuffer();
|
||||
File file = null;
|
||||
try {
|
||||
file = multipartFileToFile(multipartFile);
|
||||
if (file == null || !file.exists())
|
||||
return Translator.get("incorrect_format");
|
||||
|
||||
// 获取思维导图内容
|
||||
String content = XmindParser.parseJson(file);
|
||||
if (StringUtils.isEmpty(content) || content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length == 1) {
|
||||
return Translator.get("import_xmind_not_found");
|
||||
}
|
||||
if (!StringUtils.isEmpty(content) && content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length > 500) {
|
||||
return Translator.get("import_xmind_count_error");
|
||||
}
|
||||
JsonRootBean root = JSON.parseObject(content, JsonRootBean.class);
|
||||
|
||||
JsonRootBean root = XmindParser.parseObject(multipartFile);
|
||||
if (root != null && root.getRootTopic() != null && root.getRootTopic().getChildren() != null) {
|
||||
// 判断是模块还是用例
|
||||
for (Attached item : root.getRootTopic().getChildren().getAttached()) {
|
||||
if (isBlack(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例
|
||||
if (isAvailable(item.getTitle(), "(?:tc:|tc:|tc)")) { // 用例
|
||||
return replace(item.getTitle(), "(?:tc:|tc:|tc)") + ":" + Translator.get("test_case_create_module_fail");
|
||||
} else {
|
||||
item.setPath(item.getTitle());
|
||||
if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty()) {
|
||||
item.setPath(item.getTitle());
|
||||
makeXmind(processBuffer, item, 1, item.getPath(), item.getChildren().getAttached());
|
||||
recursion(processBuffer, item, 1, item.getPath(), item.getChildren().getAttached());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(process.toString()) && !testCaseWithBLOBs.isEmpty()) {
|
||||
testCaseService.saveImportData(testCaseWithBLOBs, projectId);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
processBuffer.append(Translator.get("incorrect_format"));
|
||||
LogUtil.error(ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
if (file != null)
|
||||
file.delete();
|
||||
testCaseWithBLOBs.clear();
|
||||
return ex.getMessage();
|
||||
}
|
||||
return process.toString();
|
||||
}
|
|
@ -1,9 +1,14 @@
|
|||
package io.metersphere.xmind.parser;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.xmind.parser.domain.JsonRootBean;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.xmind.parser.pojo.JsonRootBean;
|
||||
import io.metersphere.xmind.utils.FileUtil;
|
||||
import org.apache.commons.compress.archivers.ArchiveException;
|
||||
import org.dom4j.DocumentException;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -16,101 +21,98 @@ import java.util.Objects;
|
|||
* @Description 解析主体
|
||||
*/
|
||||
public class XmindParser {
|
||||
public static final String xmindZenJson = "content.json";
|
||||
public static final String xmindLegacyContent = "content.xml";
|
||||
public static final String xmindLegacyComments = "comments.xml";
|
||||
public static final String xmindZenJson = "content.json";
|
||||
public static final String xmindLegacyContent = "content.xml";
|
||||
public static final String xmindLegacyComments = "comments.xml";
|
||||
|
||||
/**
|
||||
* 解析脑图文件,返回content整合后的内容
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ArchiveException
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static String parseJson(File file) throws IOException, ArchiveException, DocumentException {
|
||||
String res = ZipUtils.extract(file);
|
||||
/**
|
||||
* 解析脑图文件,返回content整合后的内容
|
||||
*
|
||||
* @param multipartFile
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ArchiveException
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static String parseJson(MultipartFile multipartFile) throws IOException, ArchiveException, DocumentException {
|
||||
|
||||
String content = null;
|
||||
if (isXmindZen(res, file)) {
|
||||
content = getXmindZenContent(file, res);
|
||||
} else {
|
||||
content = getXmindLegacyContent(file, res);
|
||||
}
|
||||
File file = FileUtil.multipartFileToFile(multipartFile);
|
||||
if (file == null || !file.exists())
|
||||
MSException.throwException(Translator.get("incorrect_format"));
|
||||
|
||||
// 删除生成的文件夹
|
||||
File dir = new File(res);
|
||||
boolean flag = deleteDir(dir);
|
||||
if (flag) {
|
||||
// do something
|
||||
}
|
||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||
return (JSON.toJSONString(jsonRootBean, false));
|
||||
}
|
||||
String res = ZipUtils.extract(file);
|
||||
String content = null;
|
||||
if (isXmindZen(res, file)) {
|
||||
content = getXmindZenContent(file, res);
|
||||
} else {
|
||||
content = getXmindLegacyContent(file, res);
|
||||
}
|
||||
|
||||
public static JsonRootBean parseObject(File file) throws DocumentException, ArchiveException, IOException {
|
||||
String content = parseJson(file);
|
||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||
return jsonRootBean;
|
||||
}
|
||||
// 删除生成的文件夹
|
||||
File dir = new File(res);
|
||||
FileUtil.deleteDir(dir);
|
||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||
// 删除零时文件
|
||||
if (file != null)
|
||||
file.delete();
|
||||
String json = (JSON.toJSONString(jsonRootBean, false));
|
||||
|
||||
public static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
// 递归删除目录中的子目录下
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
boolean success = deleteDir(new File(dir, children[i]));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 目录此时为空,可以删除
|
||||
return dir.delete();
|
||||
}
|
||||
if (StringUtils.isEmpty(content) || content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length == 1) {
|
||||
MSException.throwException(Translator.get("import_xmind_not_found"));
|
||||
}
|
||||
if (!StringUtils.isEmpty(content) && content.split("(?:tc:|tc:|TC:|TC:|tc|TC)").length > 500) {
|
||||
MSException.throwException(Translator.get("import_xmind_count_error"));
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getXmindZenContent(File file, String extractFileDir)
|
||||
throws IOException, ArchiveException {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.add(xmindZenJson);
|
||||
Map<String, String> map = ZipUtils.getContents(keys, file, extractFileDir);
|
||||
String content = map.get(xmindZenJson);
|
||||
content = XmindZen.getContent(content);
|
||||
return content;
|
||||
}
|
||||
public static JsonRootBean parseObject(MultipartFile multipartFile) throws DocumentException, ArchiveException, IOException {
|
||||
String content = parseJson(multipartFile);
|
||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||
return jsonRootBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getXmindLegacyContent(File file, String extractFileDir)
|
||||
throws IOException, ArchiveException, DocumentException {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.add(xmindLegacyContent);
|
||||
keys.add(xmindLegacyComments);
|
||||
Map<String, String> map = ZipUtils.getContents(keys, file, extractFileDir);
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getXmindZenContent(File file, String extractFileDir)
|
||||
throws IOException, ArchiveException {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.add(xmindZenJson);
|
||||
Map<String, String> map = ZipUtils.getContents(keys, file, extractFileDir);
|
||||
String content = map.get(xmindZenJson);
|
||||
content = XmindZen.getContent(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
String contentXml = map.get(xmindLegacyContent);
|
||||
String commentsXml = map.get(xmindLegacyComments);
|
||||
String xmlContent = XmindLegacy.getContent(contentXml, commentsXml);
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getXmindLegacyContent(File file, String extractFileDir)
|
||||
throws IOException, ArchiveException, DocumentException {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.add(xmindLegacyContent);
|
||||
keys.add(xmindLegacyComments);
|
||||
Map<String, String> map = ZipUtils.getContents(keys, file, extractFileDir);
|
||||
|
||||
return xmlContent;
|
||||
}
|
||||
String contentXml = map.get(xmindLegacyContent);
|
||||
String commentsXml = map.get(xmindLegacyComments);
|
||||
String xmlContent = XmindLegacy.getContent(contentXml, commentsXml);
|
||||
|
||||
private static boolean isXmindZen(String res, File file) throws IOException, ArchiveException {
|
||||
// 解压
|
||||
File parent = new File(res);
|
||||
if (parent.isDirectory()) {
|
||||
String[] files = parent.list(new ZipUtils.FileFilter());
|
||||
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
|
||||
if (files[i].equals(xmindZenJson)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
private static boolean isXmindZen(String res, File file) throws IOException, ArchiveException {
|
||||
// 解压
|
||||
File parent = new File(res);
|
||||
if (parent.isDirectory()) {
|
||||
String[] files = parent.list(new ZipUtils.FileFilter());
|
||||
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
|
||||
if (files[i].equals(xmindZenJson)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
package io.metersphere.xmind.parser.domain;
|
||||
package io.metersphere.xmind.parser.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
package io.metersphere.xmind.parser.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
package io.metersphere.xmind.parser.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
package io.metersphere.xmind.parser.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
package io.metersphere.xmind.parser.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
package io.metersphere.xmind.parser.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package io.metersphere.xmind.utils;
|
||||
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class FileUtil {
|
||||
|
||||
//获取流文件
|
||||
private static void inputStreamToFile(InputStream ins, File file) {
|
||||
try (OutputStream os = new FileOutputStream(file);) {
|
||||
int bytesRead = 0;
|
||||
byte[] buffer = new byte[8192];
|
||||
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MultipartFile 转 File
|
||||
*
|
||||
* @param file
|
||||
* @throws Exception
|
||||
*/
|
||||
public static File multipartFileToFile(MultipartFile file) {
|
||||
if (file != null && file.getSize() > 0) {
|
||||
try (InputStream ins = file.getInputStream();) {
|
||||
File toFile = new File(file.getOriginalFilename());
|
||||
inputStreamToFile(ins, toFile);
|
||||
return toFile;
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
// 递归删除目录中的子目录下
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
boolean success = deleteDir(new File(dir, children[i]));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 目录此时为空,可以删除
|
||||
return dir.delete();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -30,7 +30,9 @@
|
|||
"md5": "^2.3.0",
|
||||
"sha.js": "^2.4.11",
|
||||
"js-base64": "^3.4.4",
|
||||
"json-bigint": "^1.0.0"
|
||||
"json-bigint": "^1.0.0",
|
||||
"html2canvas": "^1.0.0-rc.7",
|
||||
"jspdf": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.1.0",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<el-col v-if="auth">
|
||||
<el-row id="header-top1" type="flex" justify="space-between" align="middle">
|
||||
<el-col>
|
||||
<div class="license-head" v-if="valid === true && validData.status == 'expired'">License has expired since
|
||||
<div class="license-head" v-if="validData.status == 'expired'">License has expired since
|
||||
{{(validData!= undefined && validData.license!= undefined) ? validData.license.expired:''}},please
|
||||
update license.
|
||||
</div>
|
||||
|
@ -40,15 +40,7 @@
|
|||
export default {
|
||||
name: 'app',
|
||||
data() {
|
||||
let xpack = false;
|
||||
Setting.children.forEach(child => {
|
||||
if (child.path === "license") {
|
||||
xpack = true;
|
||||
return;
|
||||
}
|
||||
})
|
||||
return {
|
||||
valid: xpack,
|
||||
validData: {},
|
||||
auth: false
|
||||
}
|
||||
|
@ -67,13 +59,14 @@
|
|||
});
|
||||
},
|
||||
beforeMount() {
|
||||
if (this.valid === true) {
|
||||
// 验证license
|
||||
this.result = this.$get("/license/valid", response => {
|
||||
// 验证license
|
||||
this.result = this.$get("/api/license/valid", response => {
|
||||
let data = response.data;
|
||||
if (data != undefined && data != null) {
|
||||
this.validData = response.data;
|
||||
saveLicense(response.data);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
components: {MsLanguageSwitch, MsUser, MsView, MsTopMenus, MsHeaderOrgWs},
|
||||
methods: {}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import MsMainContainer from "../../../common/components/MsMainContainer";
|
||||
import MsAsideItem from "../../../common/components/MsAsideItem";
|
||||
import EnvironmentEdit from "./environment/EnvironmentEdit";
|
||||
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
||||
import {deepClone, listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
||||
import {Environment, parseEnvironment} from "../model/EnvironmentModel";
|
||||
|
||||
export default {
|
||||
|
@ -68,12 +68,13 @@
|
|||
}
|
||||
},
|
||||
copyEnvironment(environment) {
|
||||
this.currentEnvironment = environment;
|
||||
if (!environment.id) {
|
||||
this.$warning(this.$t('commons.please_save'));
|
||||
return;
|
||||
}
|
||||
let newEnvironment = {};
|
||||
Object.assign(newEnvironment, environment);
|
||||
newEnvironment = new Environment(environment);
|
||||
newEnvironment.id = null;
|
||||
newEnvironment.name = this.getNoRepeatName(newEnvironment.name);
|
||||
if (!this.validateEnvironment(newEnvironment)) {
|
||||
|
@ -84,11 +85,7 @@
|
|||
this.$refs.environmentItems.itemSelected(this.environments.length - 1, newEnvironment);
|
||||
},
|
||||
validateEnvironment(environment) {
|
||||
if (!environment.name || !!environment.name && environment.name.length > 64) {
|
||||
this.$error(this.$t('commons.input_limit', [1, 64]));
|
||||
return false;
|
||||
}
|
||||
if (!this.$refs.environmentEdit.validateSocket(environment.socket)) {
|
||||
if (!this.$refs.environmentEdit.validate()) {
|
||||
this.$error(this.$t('commons.formatErr'));
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,17 @@
|
|||
}
|
||||
});
|
||||
},
|
||||
validate() {
|
||||
let isValidate = false;
|
||||
this.$refs['environment'].validate((valid) => {
|
||||
if (valid && this.$refs.commonConfig.validate() && this.$refs.httpConfig.validate()) {
|
||||
isValidate = true;
|
||||
} else {
|
||||
isValidate = false;
|
||||
}
|
||||
});
|
||||
return isValidate;
|
||||
},
|
||||
_save(environment) {
|
||||
let param = this.buildParam(environment);
|
||||
let url = '/api/environment/add';
|
||||
|
|
|
@ -8,13 +8,14 @@ export class Environment extends BaseConfig {
|
|||
this.projectId = undefined;
|
||||
this.name = undefined;
|
||||
this.id = undefined;
|
||||
this.config = options.config || new Config();
|
||||
this.config = undefined;
|
||||
|
||||
this.set(options);
|
||||
this.sets({}, options);
|
||||
}
|
||||
|
||||
initOptions(options = {}) {
|
||||
this.config = new Config(options.config);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
@ -22,14 +23,16 @@ export class Environment extends BaseConfig {
|
|||
export class Config extends BaseConfig {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
this.commonConfig = options.commonConfig || new CommonConfig();
|
||||
this.httpConfig = options.httpConfig || new HttpConfig();
|
||||
this.commonConfig = undefined;
|
||||
this.httpConfig = undefined;
|
||||
this.databaseConfigs = [];
|
||||
|
||||
this.set(options);
|
||||
this.sets({databaseConfigs: DatabaseConfig}, options);
|
||||
}
|
||||
initOptions(options = {}) {
|
||||
this.commonConfig = new CommonConfig(options.commonConfig);
|
||||
this.httpConfig = new HttpConfig(options.httpConfig);
|
||||
options.databaseConfigs = options.databaseConfigs || [];
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -187,6 +187,7 @@
|
|||
this.form.cronValue = this.schedule.value;
|
||||
listenGoBack(this.close);
|
||||
this.handleClick()
|
||||
this.activeName = 'first'
|
||||
},
|
||||
crontabFill(value, resultList) {
|
||||
//确定后回传的值
|
||||
|
|
|
@ -32,12 +32,12 @@
|
|||
|
||||
<el-submenu v-permission="['test_manager','test_user','test_viewer']"
|
||||
index="8" popper-class="submenu">
|
||||
<template v-slot:title>用例评审</template>
|
||||
<template v-slot:title>{{$t('test_track.review.test_review')}}</template>
|
||||
<ms-recent-list ref="reviewRecent" :options="reviewRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/track/review/all'"/>
|
||||
<el-menu-item :index="testCaseReviewEditPath" class="blank_item"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/review/create'" title="创建用例评审"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/review/create'" :title="$t('test_track.review.create_review')"/>
|
||||
</el-submenu>
|
||||
|
||||
<el-submenu v-permission="['test_manager','test_user','test_viewer']" index="7" popper-class="submenu">
|
||||
|
@ -94,7 +94,7 @@ export default {
|
|||
}
|
||||
},
|
||||
reviewRecent: {
|
||||
title: "最近的评审",
|
||||
title: this.$t('test_track.recent_review'),
|
||||
url: "/test/case/review/recent/5",
|
||||
index: function (item) {
|
||||
return '/track/review/view/' + item.id;
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="lastmodify"
|
||||
prop="currentOwner"
|
||||
:label="$t('test_track.module.current_owner')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
|
||||
<!--报告-->
|
||||
<div v-if="metric">
|
||||
<base-info-component :report-info="metric" v-if="preview.id == 1"/>
|
||||
<test-result-component :test-results="metric.moduleExecuteResult" v-if="preview.id == 2"/>
|
||||
<test-result-chart-component :execute-result="metric.executeResult" v-if="preview.id == 3"/>
|
||||
<failure-result-component :failure-test-cases="metric.failureTestCases" v-if="preview.id == 4"/>
|
||||
<defect-list-component :defect-list="metric.issues" v-if="preview.id == 5"/>
|
||||
<rich-text-component :is-report-view="isReportView" :preview="preview" v-if="preview.type != 'system'"/>
|
||||
<base-info-component id="baseInfoComponent" :report-info="metric" v-if="preview.id == 1"/>
|
||||
<test-result-component id="testResultComponent" :test-results="metric.moduleExecuteResult" v-if="preview.id == 2"/>
|
||||
<test-result-chart-component id="resultChartComponent" :execute-result="metric.executeResult" v-if="preview.id == 3"/>
|
||||
<failure-result-component id="failureResultComponent" :failure-test-cases="metric.failureTestCases" v-if="preview.id == 4"/>
|
||||
<defect-list-component id="defectListComponent" :defect-list="metric.issues" v-if="preview.id == 5"/>
|
||||
<rich-text-component id="richTextComponent" :is-report-view="isReportView" :preview="preview" v-if="preview.type != 'system'"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -31,6 +31,8 @@
|
|||
import RichTextComponent from "./RichTextComponent";
|
||||
import FailureResultComponent from "./FailureResultComponent";
|
||||
import DefectListComponent from "./DefectListComponent";
|
||||
import html2canvas from 'html2canvas';
|
||||
|
||||
export default {
|
||||
name: "TemplateComponent",
|
||||
components: {
|
||||
|
@ -51,6 +53,41 @@
|
|||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getCanvas(canvasList) {
|
||||
let index = this.index;
|
||||
let componentId = this.getComponentId();
|
||||
return new Promise(function(resolve, reject) {
|
||||
html2canvas(document.getElementById(componentId), {
|
||||
scale: 2
|
||||
}).then(function(canvas) {
|
||||
//排序
|
||||
canvasList.splice(index, 0, canvas);
|
||||
resolve('success');
|
||||
});
|
||||
});
|
||||
},
|
||||
getComponentId() {
|
||||
switch (this.preview.id) {
|
||||
case 1:
|
||||
return "baseInfoComponent";
|
||||
case 2:
|
||||
return "testResultComponent";
|
||||
case 3:
|
||||
return "resultChartComponent";
|
||||
case 4:
|
||||
return "failureResultComponent";
|
||||
case 5:
|
||||
return "defectListComponent";
|
||||
default:
|
||||
return "richTextComponent";
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -25,38 +25,33 @@
|
|||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleEdit">
|
||||
{{$t('test_track.plan_view.edit_component')}}
|
||||
</el-button>
|
||||
<!--<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExport(report.name)">
|
||||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExport(report.name)">
|
||||
{{$t('test_track.plan_view.export_report')}}
|
||||
</el-button>-->
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="container" ref="resume" id="app">
|
||||
<el-main>
|
||||
<div class="preview" v-for="item in previews" :key="item.id">
|
||||
<template-component :isReportView="true" :metric="metric" :preview="item"/>
|
||||
<div v-for="(item, index) in previews" :key="item.id" id="reportViewpp">
|
||||
<template-component :isReportView="true" :metric="metric" :preview="item" :index="index" ref="templateComponent"/>
|
||||
</div>
|
||||
</el-main>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
<test-case-report-template-edit :metric="metric" ref="templateEdit" @refresh="getReport"/>
|
||||
<!-- <script>
|
||||
|
||||
</script>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {checkoutTestManagerOrTestUser, jsonToMap, mapToJson} from "../../../../../../../common/js/utils";
|
||||
import {checkoutTestManagerOrTestUser, exportPdf, jsonToMap, mapToJson} from "../../../../../../../common/js/utils";
|
||||
import BaseInfoComponent from "./TemplateComponent/BaseInfoComponent";
|
||||
import TestResultChartComponent from "./TemplateComponent/TestResultChartComponent";
|
||||
import TestResultComponent from "./TemplateComponent/TestResultComponent";
|
||||
import RichTextComponent from "./TemplateComponent/RichTextComponent";
|
||||
import TestCaseReportTemplateEdit from "./TestCaseReportTemplateEdit";
|
||||
import TemplateComponent from "./TemplateComponent/TemplateComponent";
|
||||
import writer from 'file-writer'
|
||||
import ReportStyle from "../../../../../../../common/css/report.css.js";
|
||||
|
||||
export default {
|
||||
name: "TestCaseReportView",
|
||||
|
@ -178,7 +173,7 @@
|
|||
},
|
||||
getMetric() {
|
||||
this.result = this.$get('/test/plan/get/metric/' + this.planId, response => {
|
||||
this.metric = response.data
|
||||
this.metric = response.data;
|
||||
|
||||
if (!this.metric.failureTestCases) {
|
||||
this.metric.failureTestCases = [];
|
||||
|
@ -205,31 +200,22 @@
|
|||
},
|
||||
/*导出报告*/
|
||||
handleExport(name) {
|
||||
let html = this.getHtml();
|
||||
writer(`${name}.html`, html, 'utf-8');
|
||||
console.log(html)
|
||||
},
|
||||
getHtml() {
|
||||
let template = this.$refs.resume.innerHTML;
|
||||
let html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>html</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||||
<style>${ReportStyle}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="margin:0 auto;width:1200px">
|
||||
${template}
|
||||
</div>
|
||||
<script src="https://cdn.bootcss.com/element-ui/2.4.11/index.js"/>
|
||||
</body>
|
||||
</html>`
|
||||
return html
|
||||
},
|
||||
|
||||
let result = this.result;
|
||||
result.loading = true;
|
||||
|
||||
let promises = [];
|
||||
let canvasList = new Array(this.previews.length);
|
||||
|
||||
for (let item of this.$refs.templateComponent) {
|
||||
promises.push(item.getCanvas(canvasList));
|
||||
}
|
||||
|
||||
Promise.all(promises).then(function (info) {
|
||||
exportPdf(canvasList);
|
||||
result.loading = false;
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div>
|
||||
|
||||
<el-dialog :close-on-click-modal="false"
|
||||
:title="operationType === 'edit' ? '编辑用例评审' : '创建用例评审'"
|
||||
:title="operationType === 'edit' ? $t('test_track.review.edit_review') : $t('test_track.review.create_review')"
|
||||
:visible.sync="dialogFormVisible"
|
||||
@close="close"
|
||||
v-loading="result.loading"
|
||||
|
@ -14,8 +14,8 @@
|
|||
<el-row>
|
||||
<el-col :span="8" :offset="1">
|
||||
<el-form-item
|
||||
placeholder="请输入评审标题"
|
||||
label="评审标题"
|
||||
:placeholder="$t('test_track.review.input_review_name')"
|
||||
:label="$t('test_track.review.review_name')"
|
||||
:label-width="formLabelWidth"
|
||||
prop="name">
|
||||
<el-input v-model="form.name"/>
|
||||
|
@ -23,11 +23,10 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :span="11" :offset="2">
|
||||
<el-form-item :label="$t('test_track.plan.plan_project')" :label-width="formLabelWidth" prop="projectIds">
|
||||
<el-form-item :label="$t('test_track.review.review_project')" :label-width="formLabelWidth" prop="projectIds">
|
||||
<el-select
|
||||
:disabled="(form.status == null) ? false : true"
|
||||
v-model="form.projectIds"
|
||||
:placeholder="$t('test_track.plan.input_plan_project')"
|
||||
:placeholder="$t('test_track.review.input_review_project')"
|
||||
multiple
|
||||
style="width: 100%"
|
||||
collapse-tags
|
||||
|
@ -45,13 +44,12 @@
|
|||
|
||||
<el-row>
|
||||
<el-col :span="10" :offset="1">
|
||||
<el-form-item label="评审人" :label-width="formLabelWidth" prop="principal">
|
||||
<el-form-item :label="$t('test_track.review.reviewer')" :label-width="formLabelWidth" prop="userIds">
|
||||
<el-select
|
||||
v-model="form.userIds"
|
||||
placeholder="请选择评审人"
|
||||
:placeholder="$t('test_track.review.input_reviewer')"
|
||||
filterable multiple
|
||||
collapse-tags
|
||||
:disabled="(form.status == null) ? false : true"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in reviewerOptions"
|
||||
|
@ -64,7 +62,7 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :span="10">
|
||||
<el-form-item label="截止时间" :label-width="formLabelWidth" prop="endTime">
|
||||
<el-form-item :label="$t('test_track.review.end_time')" :label-width="formLabelWidth" prop="endTime">
|
||||
<el-date-picker @change="endTimeChange" type="datetime" :placeholder="$t('commons.select_date')"
|
||||
v-model="form.endTime"/>
|
||||
</el-form-item>
|
||||
|
@ -86,7 +84,7 @@
|
|||
|
||||
<el-row v-if="operationType == 'edit'" type="flex" justify="left" style="margin-top: 10px;">
|
||||
<el-col :span="19" :offset="1">
|
||||
<el-form-item label="当前状态" :label-width="formLabelWidth" prop="status">
|
||||
<el-form-item :label="$t('test_track.review.review_status')" :label-width="formLabelWidth" prop="status">
|
||||
<test-plan-status-button :status="form.status" @statusChange="statusChange"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -132,6 +130,7 @@ export default {
|
|||
description: '',
|
||||
endTime: ''
|
||||
},
|
||||
dbProjectIds: [],
|
||||
rules: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.plan.input_plan_name'), trigger: 'blur'},
|
||||
|
@ -161,6 +160,7 @@ export default {
|
|||
let tmp = {};
|
||||
Object.assign(tmp, caseReview);
|
||||
Object.assign(this.form, tmp);
|
||||
this.dbProjectIds = JSON.parse(JSON.stringify(this.form.projectIds));
|
||||
}
|
||||
listenGoBack(this.close);
|
||||
this.dialogFormVisible = true;
|
||||
|
@ -178,16 +178,44 @@ export default {
|
|||
if (this.operationType === 'save') {
|
||||
this.compareTime(new Date().getTime(), this.form.endTime);
|
||||
}
|
||||
this.$post('/test/case/review/' + this.operationType, param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.dialogFormVisible = false;
|
||||
this.$emit("refresh");
|
||||
});
|
||||
|
||||
if (this.operationType === 'edit') {
|
||||
const nowIds = param.projectIds;
|
||||
let sign = true;
|
||||
this.dbProjectIds.forEach(dbId => {
|
||||
if (nowIds.indexOf(dbId) === -1 && sign) {
|
||||
sign = false;
|
||||
this.$confirm('取消项目关联会同时取消该项目下已关联的测试用例', '提示', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
cancelButtonText: this.$t('commons.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.editTestReview(param);
|
||||
}).catch(() => {
|
||||
this.$info(this.$t('commons.cancel'))
|
||||
});
|
||||
}
|
||||
});
|
||||
if (sign) {
|
||||
this.editTestReview(param);
|
||||
}
|
||||
} else {
|
||||
this.editTestReview(param);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
editTestReview(param) {
|
||||
this.$post('/test/case/review/' + this.operationType, param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.dialogFormVisible = false;
|
||||
this.$emit("refresh");
|
||||
});
|
||||
},
|
||||
getProjects() {
|
||||
this.result = this.$get("/project/listAll", (response) => {
|
||||
if (response.success) {
|
||||
|
@ -204,7 +232,8 @@ export default {
|
|||
});
|
||||
},
|
||||
statusChange(status) {
|
||||
|
||||
this.form.status = status;
|
||||
this.$forceUpdate();
|
||||
},
|
||||
close() {
|
||||
removeGoBackListener(this.close);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<template v-slot:header>
|
||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition"
|
||||
@search="initTableData" @create="testCaseReviewCreate"
|
||||
create-tip="创建用例评审"
|
||||
title="用例评审"/>
|
||||
:create-tip="$t('test_track.review.create_review')"
|
||||
:title="$t('test_track.review.test_review')"/>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
|
@ -16,63 +16,35 @@
|
|||
@row-click="intoReview">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="评审名称"
|
||||
:label="$t('test_track.review.review_name')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reviewer"
|
||||
label="评审人"
|
||||
:label="$t('test_track.review.reviewer')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="projectName"
|
||||
label="所属项目"
|
||||
:label="$t('test_track.review.review_project')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="creator"
|
||||
label="发起人"
|
||||
:label="$t('test_track.review.review_creator')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
column-key="status"
|
||||
:filters="statusFilters"
|
||||
:label="$t('test_track.plan.plan_status')"
|
||||
:label="$t('test_track.review.review_status')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span @click.stop="clickt = 'stop'">
|
||||
<el-dropdown class="test-case-status" @command="statusChange">
|
||||
<span class="el-dropdown-link">
|
||||
<plan-status-table-item :value="scope.row.status"/>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown" chang>
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Prepare'}">
|
||||
{{ $t('test_track.plan.plan_status_prepare') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
|
||||
:command="{id: scope.row.id, status: 'Underway'}">
|
||||
{{ $t('test_track.plan.plan_status_running') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
|
||||
:command="{id: scope.row.id, status: 'Completed'}">
|
||||
{{ $t('test_track.plan.plan_status_completed') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<plan-status-table-item :value="scope.row.status"/>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column-->
|
||||
<!-- prop="reviewerSize"-->
|
||||
<!-- label="已评"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- prop="resultMap"-->
|
||||
<!-- label="结果分布"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column
|
||||
prop="createTime"
|
||||
:label="$t('commons.create_time')"
|
||||
|
@ -83,7 +55,7 @@
|
|||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="endTime"
|
||||
label="截止时间"
|
||||
:label="$t('test_track.review.end_time')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.endTime | timestampFormatDate }}</span>
|
||||
|
@ -94,10 +66,10 @@
|
|||
<template v-slot:default="scope">
|
||||
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
|
||||
@deleteClick="handleDelete(scope.row)">
|
||||
<template v-slot:middle>
|
||||
<ms-table-operator-button :isTesterPermission="true" type="success" tip="重新发起" icon="el-icon-document"
|
||||
@exec="reCreate(scope.row)"/>
|
||||
</template>
|
||||
<!-- <template v-slot:middle>-->
|
||||
<!-- <ms-table-operator-button :isTesterPermission="true" type="success" tip="重新发起" icon="el-icon-document"-->
|
||||
<!-- @exec="reCreate(scope.row)"/>-->
|
||||
<!-- </template>-->
|
||||
</ms-table-operator>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -105,7 +77,7 @@
|
|||
|
||||
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
<ms-delete-confirm title="删除用例评审" @delete="_handleDelete" ref="deleteConfirm"/>
|
||||
<ms-delete-confirm :title="$t('test_track.review.delete')" @delete="_handleDelete" ref="deleteConfirm"/>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
|
@ -217,9 +189,6 @@ export default {
|
|||
_sort(column, this.condition);
|
||||
this.initTableData();
|
||||
},
|
||||
reCreate() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<select-menu
|
||||
:data="testReviews"
|
||||
:current-data="currentReview"
|
||||
title="评审"
|
||||
:title="$t('test_track.review_view.review')"
|
||||
@dataChange="changeReview"/>
|
||||
<node-tree class="node-tree"
|
||||
v-loading="result.loading"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<div>
|
||||
|
||||
<el-dialog title="关联测试评审"
|
||||
<el-dialog :title="$t('test_track.review_view.relevance_case')"
|
||||
:visible.sync="dialogFormVisible"
|
||||
@close="close"
|
||||
width="60%" v-loading="result.loading"
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="initTableData"
|
||||
:show-create="false" :tip="$t('commons.search_by_name_or_id')">
|
||||
<template v-slot:title>
|
||||
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh" title="全部评审"/>
|
||||
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh" :title="$t('test_track.review_view.all_review')"/>
|
||||
</template>
|
||||
<template v-slot:button>
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-video-play"
|
||||
content="开始用例评审" @click="startReview"/>
|
||||
:content="$t('test_track.review_view.start_review')" @click="startReview"/>
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-connection"
|
||||
content="关联用例评审"
|
||||
:content="$t('test_track.review_view.relevance_case')"
|
||||
@click="$emit('openTestReviewRelevanceDialog')"/>
|
||||
</template>
|
||||
</ms-table-header>
|
||||
|
@ -91,44 +91,13 @@
|
|||
|
||||
<el-table-column
|
||||
prop="projectName"
|
||||
:label="$t('test_track.plan.plan_project')"
|
||||
:label="$t('test_track.review.review_project')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
:label="$t('test_track.issue.issue')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-popover
|
||||
placement="right"
|
||||
width="400"
|
||||
trigger="hover">
|
||||
<el-table border class="adjust-table" :data="scope.row.issuesContent" style="width: 100%">
|
||||
<el-table-column prop="title" :label="$t('test_track.issue.title')" show-overflow-tooltip/>
|
||||
<el-table-column prop="description" :label="$t('test_track.issue.description')">
|
||||
<template v-slot:default="scope">
|
||||
<el-popover
|
||||
placement="left"
|
||||
width="400"
|
||||
trigger="hover"
|
||||
>
|
||||
<ckeditor :editor="editor" disabled :config="editorConfig"
|
||||
v-model="scope.row.description"/>
|
||||
<el-button slot="reference" type="text">{{$t('test_track.issue.preview')}}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="platform" :label="$t('test_track.issue.platform')"/>
|
||||
</el-table>
|
||||
<el-button slot="reference" type="text">{{scope.row.issuesSize}}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column
|
||||
prop="reviewerName"
|
||||
label="评审人"
|
||||
:label="$t('test_track.review.review_creator')"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
</el-table-column>
|
||||
|
@ -137,22 +106,10 @@
|
|||
prop="status"
|
||||
:filters="statusFilters"
|
||||
column-key="status"
|
||||
:label="$t('test_track.plan_view.execute_result')">
|
||||
:label="$t('test_track.review_view.execute_result')">
|
||||
<template v-slot:default="scope">
|
||||
<span @click.stop="clickt = 'stop'">
|
||||
<el-dropdown class="test-case-status" @command="statusChange">
|
||||
<span class="el-dropdown-link">
|
||||
<status-table-item :value="scope.row.status"/>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown" chang>
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Pass'}">
|
||||
{{$t('test_track.plan_view.pass')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'UnPass'}">
|
||||
未通过
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<status-table-item :value="scope.row.status"/>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -320,18 +277,6 @@ export default {
|
|||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
for (let i = 0; i < this.tableData.length; i++) {
|
||||
if (this.tableData[i]) {
|
||||
this.$set(this.tableData[i], "issuesSize", 0);
|
||||
this.$get("/issues/get/" + this.tableData[i].caseId, response => {
|
||||
let issues = response.data;
|
||||
if (this.tableData[i]) {
|
||||
this.$set(this.tableData[i], "issuesSize", issues.length);
|
||||
this.$set(this.tableData[i], "issuesContent", issues);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
this.selectRows.clear();
|
||||
});
|
||||
}
|
||||
|
@ -403,41 +348,10 @@ export default {
|
|||
});
|
||||
},
|
||||
handleSelectAll(selection) {
|
||||
if (selection.length > 0) {
|
||||
if (selection.length === 1) {
|
||||
this.selectRows.add(selection[0]);
|
||||
} else {
|
||||
this.tableData.forEach(item => {
|
||||
this.$set(item, "showMore", true);
|
||||
this.selectRows.add(item);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.selectRows.clear();
|
||||
this.tableData.forEach(row => {
|
||||
this.$set(row, "showMore", false);
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
handleSelectionChange(selection, row) {
|
||||
if (this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", false);
|
||||
this.selectRows.delete(row);
|
||||
} else {
|
||||
this.$set(row, "showMore", true);
|
||||
this.selectRows.add(row);
|
||||
}
|
||||
|
||||
let arr = Array.from(this.selectRows);
|
||||
|
||||
// 选中1个以上的用例时显示更多操作
|
||||
if (this.selectRows.size === 1) {
|
||||
this.$set(arr[0], "showMore", false);
|
||||
} else if (this.selectRows.size === 2) {
|
||||
arr.forEach(row => {
|
||||
this.$set(row, "showMore", true);
|
||||
})
|
||||
}
|
||||
},
|
||||
handleBatch(type) {
|
||||
if (this.selectRows.size < 1) {
|
||||
|
@ -502,6 +416,7 @@ export default {
|
|||
},
|
||||
startReview() {
|
||||
if (this.tableData.length !== 0) {
|
||||
this.isReadOnly = false;
|
||||
this.$refs.testReviewTestCaseEdit.openTestCaseEdit(this.tableData[0]);
|
||||
} else {
|
||||
this.$warning("没有关联的评审!");
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
LicenseKey
|
||||
} from "./constants";
|
||||
import axios from "axios";
|
||||
import {jsPDF} from "jspdf";
|
||||
|
||||
export function hasRole(role) {
|
||||
let user = getCurrentUser();
|
||||
|
@ -203,3 +204,57 @@ export function getUUID() {
|
|||
}
|
||||
|
||||
|
||||
export function exportPdf(canvasList) {
|
||||
|
||||
let pdf = new jsPDF('', 'pt', 'a4');
|
||||
|
||||
// 当前页面的当前高度
|
||||
let currentHeight = 0;
|
||||
for (let canvas of canvasList) {
|
||||
if (canvas) {
|
||||
let contentWidth = canvas.width;
|
||||
let contentHeight = canvas.height;
|
||||
|
||||
//a4纸的尺寸[595.28,841.89]
|
||||
let a4Width = 592.28;
|
||||
let a4Height = 841.89;
|
||||
|
||||
// html页面生成的canvas在pdf中图片的宽高
|
||||
let imgWidth = a4Width;
|
||||
let imgHeight = a4Width/contentWidth * contentHeight;
|
||||
|
||||
let pageData = canvas.toDataURL('image/jpeg', 1.0);
|
||||
|
||||
// 当前图片的剩余高度
|
||||
let leftHeight = imgHeight;
|
||||
|
||||
// 当前页面的剩余高度
|
||||
let blankHeight = a4Height - currentHeight;
|
||||
|
||||
if (leftHeight > blankHeight) {
|
||||
//页面偏移
|
||||
let position = 0;
|
||||
while(leftHeight > 0) {
|
||||
// 本次添加占用的高度
|
||||
let occupation = a4Height - currentHeight;
|
||||
pdf.addImage(pageData, 'JPEG', 0, position + currentHeight, imgWidth, imgHeight);
|
||||
currentHeight = leftHeight;
|
||||
leftHeight -= occupation;
|
||||
position -= occupation;
|
||||
//避免添加空白页
|
||||
if(leftHeight > 0) {
|
||||
pdf.addPage();
|
||||
currentHeight = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pdf.addImage(pageData, 'JPEG', 0, currentHeight, imgWidth, imgHeight);
|
||||
currentHeight += imgHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pdf.save('stone.pdf');
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -617,6 +617,7 @@ export default {
|
|||
length_less_than: "The length less than",
|
||||
recent_plan: "Recent plan",
|
||||
recent_case: "Recent case",
|
||||
recent_review: "Recent review",
|
||||
pass_rate: "Pass rate",
|
||||
execution_result: ": Please select the execution result",
|
||||
actual_result: ": The actual result is empty",
|
||||
|
@ -722,6 +723,28 @@ export default {
|
|||
plan_delete_confirm: "All use cases under this plan will be deleted,confirm delete test plan: ",
|
||||
plan_delete: "Delete test plan",
|
||||
},
|
||||
review: {
|
||||
test_review: "Test Review",
|
||||
create_review: "Create Review",
|
||||
edit_review: "Edit Review",
|
||||
review_name: "Name",
|
||||
reviewer: "Reviewer",
|
||||
review_project: "Project",
|
||||
review_creator: "Creator",
|
||||
review_status: "Status",
|
||||
end_time: "EndTime",
|
||||
delete: "Delete",
|
||||
input_review_name: "Please enter the name of the review",
|
||||
input_review_project: "Please select the project",
|
||||
input_reviewer: "Please select reviewer",
|
||||
},
|
||||
review_view: {
|
||||
review: "Review",
|
||||
all_review: "All Review",
|
||||
start_review: "Start Review",
|
||||
relevance_case: "Relevance Case",
|
||||
execute_result: "Result",
|
||||
},
|
||||
module: {
|
||||
search: "Search module",
|
||||
rename: "Rename",
|
||||
|
@ -936,7 +959,7 @@ export default {
|
|||
performance: "Number of performance tests",
|
||||
resource_pool: "Available test resource pool",
|
||||
max_threads: "Maximum Concurrency",
|
||||
duration: "Stress test duration",
|
||||
duration: "Stress test duration(minutes)",
|
||||
use_default: "Use default quota",
|
||||
yes: "Yes",
|
||||
no: "No",
|
||||
|
|
|
@ -619,6 +619,7 @@ export default {
|
|||
length_less_than: "长度必须小于",
|
||||
recent_plan: "最近的计划",
|
||||
recent_case: "最近的用例",
|
||||
recent_review: "最近的评审",
|
||||
pass_rate: "通过率",
|
||||
execution_result: ": 请选择执行结果",
|
||||
actual_result: ": 实际结果为空",
|
||||
|
@ -725,6 +726,28 @@ export default {
|
|||
plan_delete_confirm: "将删除该测试计划下所有用例,确认删除测试计划: ",
|
||||
plan_delete: "删除计划",
|
||||
},
|
||||
review: {
|
||||
test_review: "用例评审",
|
||||
create_review: "创建用例评审",
|
||||
edit_review: "编辑用例评审",
|
||||
review_name: "评审名称",
|
||||
reviewer: "评审人",
|
||||
review_project: "所属项目",
|
||||
review_creator: "发起人",
|
||||
review_status: "当前状态",
|
||||
end_time: "截止时间",
|
||||
delete: "删除评审",
|
||||
input_review_name: "请输入评审名称",
|
||||
input_review_project: "请选择所属项目",
|
||||
input_reviewer: "请选择评审人",
|
||||
},
|
||||
review_view: {
|
||||
review: "评审",
|
||||
all_review: "全部评审",
|
||||
start_review: "开始评审",
|
||||
relevance_case: "关联用例",
|
||||
execute_result: "执行结果",
|
||||
},
|
||||
module: {
|
||||
search: "搜索模块",
|
||||
rename: "重命名",
|
||||
|
@ -938,7 +961,7 @@ export default {
|
|||
performance: "性能测试数量",
|
||||
resource_pool: "可用测试资源池",
|
||||
max_threads: "最大并发数",
|
||||
duration: "压测时长",
|
||||
duration: "压测时长(分钟)",
|
||||
use_default: "使用默认配额",
|
||||
yes: "是",
|
||||
no: "否",
|
||||
|
|
|
@ -619,6 +619,7 @@ export default {
|
|||
length_less_than: "長度必須小於",
|
||||
recent_plan: "最近的計劃",
|
||||
recent_case: "最近的用例",
|
||||
recent_review: "最近的評審",
|
||||
pass_rate: "通過率",
|
||||
execution_result: ": 請選擇執行結果",
|
||||
actual_result: ": 實際結果為空",
|
||||
|
@ -725,6 +726,28 @@ export default {
|
|||
plan_delete_confirm: "將刪除該測試計劃下所有用例,確認刪除測試計劃: ",
|
||||
plan_delete: "刪除計劃",
|
||||
},
|
||||
review: {
|
||||
test_review: "用例評審",
|
||||
create_review: "創建用例評審",
|
||||
edit_review: "編輯用例評審",
|
||||
review_name: "評審名稱",
|
||||
reviewer: "評審人",
|
||||
review_project: "所屬項目",
|
||||
review_creator: "發起人",
|
||||
review_status: "當前狀態",
|
||||
end_time: "截止時間",
|
||||
delete: "刪除評審",
|
||||
input_review_name: "請輸入評審名稱",
|
||||
input_review_project: "請選擇所屬項目",
|
||||
input_reviewer: "請選擇評審人",
|
||||
},
|
||||
review_view: {
|
||||
review: "評審",
|
||||
all_review: "全部評審",
|
||||
start_review: "開始評審",
|
||||
relevance_case: "關聯用例",
|
||||
execute_result: "執行結果",
|
||||
},
|
||||
module: {
|
||||
search: "搜索模塊",
|
||||
rename: "重命名",
|
||||
|
@ -938,7 +961,7 @@ export default {
|
|||
performance: "性能測試數量",
|
||||
resource_pool: "可用測試資源池",
|
||||
max_threads: "最大並發數",
|
||||
duration: "壓測時長",
|
||||
duration: "壓測時長(分鐘)",
|
||||
use_default: "使用默認配額",
|
||||
yes: "是",
|
||||
no: "否",
|
||||
|
|
Loading…
Reference in New Issue