提交 e5e7737f 编写于 作者: C chenjianxing

Merge branch 'dev' of https://github.com/fit2cloudrd/metersphere-server into dev

......@@ -42,7 +42,6 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
......@@ -53,7 +52,9 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
......@@ -66,7 +67,6 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
......@@ -119,10 +119,6 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
......@@ -188,6 +184,16 @@
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
......
package io.metersphere.controller;
package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.service.APIReportService;
import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.controller.request.ReportRequest;
import io.metersphere.dto.ApiReportDTO;
import io.metersphere.service.ApiReportService;
import io.metersphere.user.SessionUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import javax.annotation.Resource;
@RestController
@RequestMapping(value = "/api/report")
public class ApiReportController {
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public class APIReportController {
@Resource
private ApiReportService apiReportService;
private APIReportService apiReportService;
@GetMapping("/recent/{count}")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public List<ApiTestReport> recentProjects(@PathVariable int count) {
@GetMapping("recent/{count}")
public List<APIReportResult> recentTest(@PathVariable int count) {
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
ReportRequest request = new ReportRequest();
QueryAPIReportRequest request = new QueryAPIReportRequest();
request.setWorkspaceId(currentWorkspaceId);
PageHelper.startPage(1, count);
return apiReportService.getRecentReportList(request);
PageHelper.startPage(1, count, true);
return apiReportService.recentTest(request);
}
@PostMapping("/list/all/{goPage}/{pageSize}")
public Pager<List<ApiReportDTO>> getReportList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ReportRequest request) {
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<APIReportResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPIReportRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, apiReportService.getReportList(request));
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
return PageUtils.setPageInfo(page, apiReportService.list(request));
}
@PostMapping("/delete/{reportId}")
public void deleteReport(@PathVariable String reportId) {
apiReportService.deleteReport(reportId);
@GetMapping("/get/{testId}")
public ApiTestReport get(@PathVariable String testId) {
return apiReportService.get(testId);
}
@GetMapping("/test/pro/info/{reportId}")
public ApiReportDTO getReportTestAndProInfo(@PathVariable String reportId) {
return apiReportService.getReportTestAndProInfo(reportId);
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPIReportRequest request) {
apiReportService.delete(request);
}
}
......@@ -6,13 +6,11 @@ import io.metersphere.api.dto.APITestResult;
import io.metersphere.api.dto.DeleteAPITestRequest;
import io.metersphere.api.dto.QueryAPITestRequest;
import io.metersphere.api.dto.SaveAPITestRequest;
import io.metersphere.api.service.ApiTestService;
import io.metersphere.api.service.APITestService;
import io.metersphere.base.domain.ApiTestWithBLOBs;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.controller.request.testplan.SaveTestPlanRequest;
import io.metersphere.service.FileService;
import io.metersphere.user.SessionUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
......@@ -28,9 +26,7 @@ import javax.annotation.Resource;
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public class APITestController {
@Resource
private ApiTestService apiTestService;
@Resource
private FileService fileService;
private APITestService apiTestService;
@GetMapping("recent/{count}")
public List<APITestResult> recentTest(@PathVariable int count) {
......@@ -48,9 +44,14 @@ public class APITestController {
return PageUtils.setPageInfo(page, apiTestService.list(request));
}
@PostMapping(value = "/save", consumes = {"multipart/form-data"})
public String save(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> files) {
return apiTestService.save(request, files);
@PostMapping(value = "/create", consumes = {"multipart/form-data"})
public void create(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> files) {
apiTestService.create(request, files);
}
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
public void update(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> files) {
apiTestService.update(request, files);
}
@GetMapping("/get/{testId}")
......@@ -63,8 +64,8 @@ public class APITestController {
apiTestService.delete(request);
}
@PostMapping(value = "/run", consumes = {"multipart/form-data"})
public String run(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> files) {
return apiTestService.run(request, files);
@PostMapping(value = "/run")
public void run(@RequestBody SaveAPITestRequest request) {
apiTestService.run(request);
}
}
package io.metersphere.api.dto;
import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.base.domain.ApiTestWithBLOBs;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class APIReportResult extends ApiTestReport {
private String projectName;
}
package io.metersphere.api.dto;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class DeleteAPIReportRequest {
private String id;
}
package io.metersphere.api.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class QueryAPIReportRequest {
private String id;
private String projectId;
private String name;
private String workspaceId;
private boolean recent = false;
}
package io.metersphere.api.jmeter;
import io.metersphere.api.service.APIReportService;
import io.metersphere.api.service.APITestService;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.AssertionResult;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* JMeter BackendListener扩展, jmx脚本中使用
*/
public class APIBackendListenerClient extends AbstractBackendListenerClient implements Serializable {
private final AtomicInteger count = new AtomicInteger();
// 与前端JMXGenerator的SPLIT对应,用于获取 测试名称 和 测试ID
private final static String SPLIT = "@@:";
// 测试ID作为key
private final Map<String, List<SampleResult>> queue = new ConcurrentHashMap<>();
@Override
public void handleSampleResults(List<SampleResult> sampleResults, BackendListenerContext context) {
System.out.println(context.getParameter("id"));
sampleResults.forEach(result -> {
for (AssertionResult assertionResult : result.getAssertionResults()) {
System.out.println(assertionResult.getName() + ": " + assertionResult.isError());
System.out.println(assertionResult.getName() + ": " + assertionResult.isFailure());
System.out.println(assertionResult.getName() + ": " + assertionResult.getFailureMessage());
// 将不同的测试脚本按测试ID分开
String label = result.getSampleLabel();
if (!label.contains(SPLIT)) {
LogUtil.error("request name format is invalid, name: " + label);
return;
}
String name = label.split(SPLIT)[0];
String testId = label.split(SPLIT)[1];
if (!queue.containsKey(testId)) {
List<SampleResult> testResults = new ArrayList<>();
queue.put(testId, testResults);
}
result.setSampleLabel(name);
queue.get(testId).add(result);
});
}
@Override
public void teardownTest(BackendListenerContext context) throws Exception {
APITestService apiTestService = CommonBeanFactory.getBean(APITestService.class);
if (apiTestService == null) {
LogUtil.error("apiTestService is required");
return;
}
APIReportService apiReportService = CommonBeanFactory.getBean(APIReportService.class);
if (apiReportService == null) {
LogUtil.error("apiReportService is required");
return;
}
queue.forEach((id, sampleResults) -> {
TestResult testResult = new TestResult();
testResult.setId(id);
testResult.setTotal(sampleResults.size());
// key: 场景Id
final Map<String, ScenarioResult> scenarios = new LinkedHashMap<>();
sampleResults.forEach(result -> {
String thread = StringUtils.substringBeforeLast(result.getThreadName(), " ");
String scenarioName = StringUtils.substringBefore(thread, SPLIT);
String scenarioId = StringUtils.substringAfter(thread, SPLIT);
ScenarioResult scenarioResult;
if (!scenarios.containsKey(scenarioId)) {
scenarioResult = new ScenarioResult();
scenarioResult.setId(scenarioId);
scenarioResult.setName(scenarioName);
scenarios.put(scenarioId, scenarioResult);
} else {
scenarioResult = scenarios.get(scenarioId);
}
println("getSampleLabel", result.getSampleLabel());
println("getErrorCount", result.getErrorCount());
println("getRequestHeaders", result.getRequestHeaders());
println("getResponseHeaders", result.getResponseHeaders());
println("getSampleLabel", result.getSampleLabel());
println("getSampleLabel", result.getSampleLabel());
println("getResponseCode", result.getResponseCode());
println("getResponseCode size", result.getResponseData().length);
println("getLatency", result.getLatency());
println("end - start", result.getEndTime() - result.getStartTime());
println("getTimeStamp", result.getTimeStamp());
println("getTime", result.getTime());
if (result.isSuccessful()) {
scenarioResult.addSuccess();
testResult.addSuccess();
} else {
scenarioResult.addError();
testResult.addError();
}
RequestResult requestResult = getRequestResult(result);
scenarioResult.getRequestResult().add(requestResult);
testResult.addPassAssertions(requestResult.getPassAssertions());
testResult.addTotalAssertions(requestResult.getTotalAssertions());
scenarioResult.addPassAssertions(requestResult.getPassAssertions());
scenarioResult.addTotalAssertions(requestResult.getTotalAssertions());
});
testResult.getScenarios().addAll(scenarios.values());
apiTestService.changeStatus(id, APITestStatus.Completed);
apiReportService.save(testResult);
});
System.err.println(count.addAndGet(sampleResults.size()));
queue.clear();
super.teardownTest(context);
}
private void println(String name, Object value) {
System.out.println(name + ": " + value);
private RequestResult getRequestResult(SampleResult result) {
RequestResult requestResult = new RequestResult();
requestResult.setName(result.getSampleLabel());
requestResult.setUrl(result.getUrlAsString());
requestResult.setSuccess(result.isSuccessful());
requestResult.setBody(result.getSamplerData());
requestResult.setHeaders(result.getRequestHeaders());
requestResult.setRequestSize(result.getSentBytes());
requestResult.setTotalAssertions(result.getAssertionResults().length);
ResponseResult responseResult = requestResult.getResponseResult();
responseResult.setBody(result.getResponseDataAsString());
responseResult.setHeaders(result.getResponseHeaders());
responseResult.setLatency(result.getLatency());
responseResult.setResponseCode(result.getResponseCode());
responseResult.setResponseSize(result.getResponseData().length);
responseResult.setResponseTime(result.getTime());
responseResult.setResponseMessage(result.getResponseMessage());
for (AssertionResult assertionResult : result.getAssertionResults()) {
ResponseAssertionResult responseAssertionResult = getResponseAssertionResult(assertionResult);
if (responseAssertionResult.isPass()) {
requestResult.addPassAssertions();
}
responseResult.getAssertions().add(responseAssertionResult);
}
return requestResult;
}
private ResponseAssertionResult getResponseAssertionResult(AssertionResult assertionResult) {
ResponseAssertionResult responseAssertionResult = new ResponseAssertionResult();
responseAssertionResult.setMessage(assertionResult.getFailureMessage());
responseAssertionResult.setName(assertionResult.getName());
responseAssertionResult.setPass(!assertionResult.isFailure());
return responseAssertionResult;
}
}
package io.metersphere.api.jmeter;
import lombok.Data;
@Data
public class RequestResult {
private String name;
private String url;
private long requestSize;
private boolean success;
private String headers;
private String cookies;
private String body;
private int totalAssertions = 0;
private int passAssertions = 0;
private final ResponseResult responseResult = new ResponseResult();
public void addPassAssertions() {
this.passAssertions++;
}
}
package io.metersphere.api.jmeter;
import lombok.Data;
@Data
public class ResponseAssertionResult {
private String name;
private String message;
private boolean pass;
}
package io.metersphere.api.jmeter;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ResponseResult {
private String responseCode;
private String responseMessage;
private long responseTime;
private long latency;
private long responseSize;
private String headers;
private String body;
private final List<ResponseAssertionResult> assertions = new ArrayList<>();
}
package io.metersphere.api.jmeter;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ScenarioResult {
private String id;
private String name;
private long responseTime;
private int error = 0;
private int success = 0;
private int totalAssertions = 0;
private int passAssertions = 0;
private final List<RequestResult> requestResult = new ArrayList<>();
public void addError() {
this.error++;
}
public void addSuccess() {
this.success++;
}
public void addTotalAssertions(int count) {
this.totalAssertions += count;
}
public void addPassAssertions(int count) {
this.passAssertions += count;
}
}
package io.metersphere.api.jmeter;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class TestResult {
private String id;
private int success = 0;
private int error = 0;
private int total = 0;
private int totalAssertions = 0;
private int passAssertions = 0;
private final List<ScenarioResult> scenarios = new ArrayList<>();
public void addError() {
this.error++;
}
public void addSuccess() {
this.success++;
}
public void addTotalAssertions(int count) {
this.totalAssertions += count;
}
public void addPassAssertions(int count) {
this.passAssertions += count;
}
}
package io.metersphere.api.service;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.base.domain.ApiTestWithBLOBs;
import io.metersphere.base.mapper.ApiTestReportMapper;
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.UUID;
import javax.annotation.Resource;
@Service
@Transactional(rollbackFor = Exception.class)
public class APIReportService {
@Resource
private APITestService apiTestService;
@Resource
private ApiTestReportMapper apiTestReportMapper;
@Resource
private ExtApiTestReportMapper extApiTestReportMapper;
public List<APIReportResult> list(QueryAPIReportRequest request) {
return extApiTestReportMapper.list(request);
}
public List<APIReportResult> recentTest(QueryAPIReportRequest request) {
request.setRecent(true);
return extApiTestReportMapper.list(request);
}
public ApiTestReport get(String id) {
return apiTestReportMapper.selectByPrimaryKey(id);
}
public List<APIReportResult> listByTestId(String testId) {
return extApiTestReportMapper.listByTestId(testId);
}
public void delete(DeleteAPIReportRequest request) {
apiTestReportMapper.deleteByPrimaryKey(request.getId());
}
public void save(TestResult result) {
ApiTestWithBLOBs test = apiTestService.get(result.getId());
ApiTestReport report = new ApiTestReport();
report.setId(UUID.randomUUID().toString());
report.setTestId(result.getId());
report.setName(test.getName());
report.setDescription(test.getDescription());
report.setContent(JSONObject.toJSONString(result));
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Completed.name());
apiTestReportMapper.insert(report);
}
}
......@@ -13,22 +13,21 @@ import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.i18n.Translator;
import io.metersphere.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiTestService {
public class APITestService {
@Resource
private ApiTestMapper apiTestMapper;
......@@ -50,28 +49,21 @@ public class ApiTestService {
return extApiTestMapper.list(request);
}
public String save(SaveAPITestRequest request, List<MultipartFile> files) {
public void create(SaveAPITestRequest request, List<MultipartFile> files) {
if (files == null || files.isEmpty()) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
ApiTestWithBLOBs test = createTest(request);
saveFile(test.getId(), files);
}
final ApiTestWithBLOBs test;
if (StringUtils.isNotBlank(request.getId())) {
// 删除原来的文件
deleteFileByTestId(request.getId());
test = updateTest(request);
} else {
test = createTest(request);
public void update(SaveAPITestRequest request, List<MultipartFile> files) {
if (files == null || files.isEmpty()) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
// 保存新文件
files.forEach(file -> {
final FileMetadata fileMetadata = fileService.saveFile(file);
ApiTestFile apiTestFile = new ApiTestFile();
apiTestFile.setTestId(test.getId());
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
});
return test.getId();
deleteFileByTestId(request.getId());
ApiTestWithBLOBs test = updateTest(request);
saveFile(test.getId(), files);
}
public ApiTestWithBLOBs get(String id) {
......@@ -83,15 +75,15 @@ public class ApiTestService {
apiTestMapper.deleteByPrimaryKey(request.getId());
}
public String run(SaveAPITestRequest request, List<MultipartFile> files) {
String id = save(request, files);
try {
changeStatus(request.getId(), APITestStatus.Running);
jMeterService.run(files.get(0).getInputStream());
} catch (IOException e) {
MSException.throwException(Translator.get("api_load_script_error"));
public void run(SaveAPITestRequest request) {
ApiTestFile file = getFileByTestId(request.getId());
if (file == null) {
MSException.throwException(Translator.get("file_cannot_be_null"));
}
return id;
byte[] bytes = fileService.loadFileAsBytes(file.getFileId());
InputStream is = new ByteArrayInputStream(bytes);
changeStatus(request.getId(), APITestStatus.Running);
jMeterService.run(is);
}
public void changeStatus(String id, APITestStatus status) {
......@@ -121,7 +113,7 @@ public class ApiTestService {
}
final ApiTestWithBLOBs test = new ApiTestWithBLOBs();
test.setId(UUID.randomUUID().toString());
test.setId(request.getId());
test.setName(request.getName());
test.setProjectId(request.getProjectId());
test.setScenarioDefinition(request.getScenarioDefinition());
......@@ -132,6 +124,16 @@ public class ApiTestService {
return test;
}
private void saveFile(String testId, List<MultipartFile> files) {
files.forEach(file -> {
final FileMetadata fileMetadata = fileService.saveFile(file);
ApiTestFile apiTestFile = new ApiTestFile();
apiTestFile.setTestId(testId);
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
});
}
private void deleteFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
......
......@@ -6,7 +6,7 @@ import java.io.Serializable;
@Data
public class LoadTestReportLog implements Serializable {
private Long id;
private String id;
private String reportId;
......
......@@ -114,52 +114,62 @@ public class LoadTestReportLogExample {
return (Criteria) this;
}
public Criteria andIdEqualTo(Long value) {
public Criteria andIdEqualTo(String value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
public Criteria andIdNotEqualTo(Long value) {
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThan(Long value) {
public Criteria andIdGreaterThan(String value) {
addCriterion("id >", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThanOrEqualTo(Long value) {
public Criteria andIdGreaterThanOrEqualTo(String value) {
addCriterion("id >=", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThan(Long value) {
public Criteria andIdLessThan(String value) {
addCriterion("id <", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThanOrEqualTo(Long value) {
public Criteria andIdLessThanOrEqualTo(String value) {
addCriterion("id <=", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<Long> values) {
public Criteria andIdLike(String value) {
addCriterion("id like", value, "id");
return (Criteria) this;
}
public Criteria andIdNotLike(String value) {
addCriterion("id not like", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<String> values) {
addCriterion("id in", values, "id");
return (Criteria) this;
}
public Criteria andIdNotIn(List<Long> values) {
public Criteria andIdNotIn(List<String> values) {
addCriterion("id not in", values, "id");
return (Criteria) this;
}
public Criteria andIdBetween(Long value1, Long value2) {
public Criteria andIdBetween(String value1, String value2) {
addCriterion("id between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andIdNotBetween(Long value1, Long value2) {
public Criteria andIdNotBetween(String value1, String value2) {
addCriterion("id not between", value1, value2, "id");
return (Criteria) this;
}
......
......@@ -6,7 +6,7 @@ import java.io.Serializable;
@Data
public class LoadTestReportResult implements Serializable {
private Long id;
private String id;
private String reportId;
......
......@@ -114,52 +114,62 @@ public class LoadTestReportResultExample {
return (Criteria) this;
}
public Criteria andIdEqualTo(Long value) {
public Criteria andIdEqualTo(String value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
public Criteria andIdNotEqualTo(Long value) {
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThan(Long value) {
public Criteria andIdGreaterThan(String value) {
addCriterion("id >", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThanOrEqualTo(Long value) {
public Criteria andIdGreaterThanOrEqualTo(String value) {
addCriterion("id >=", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThan(Long value) {
public Criteria andIdLessThan(String value) {
addCriterion("id <", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThanOrEqualTo(Long value) {
public Criteria andIdLessThanOrEqualTo(String value) {
addCriterion("id <=", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<Long> values) {
public Criteria andIdLike(String value) {
addCriterion("id like", value, "id");
return (Criteria) this;
}
public Criteria andIdNotLike(String value) {
addCriterion("id not like", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<String> values) {
addCriterion("id in", values, "id");
return (Criteria) this;
}
public Criteria andIdNotIn(List<Long> values) {
public Criteria andIdNotIn(List<String> values) {
addCriterion("id not in", values, "id");
return (Criteria) this;
}
public Criteria andIdBetween(Long value1, Long value2) {
public Criteria andIdBetween(String value1, String value2) {
addCriterion("id between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andIdNotBetween(Long value1, Long value2) {
public Criteria andIdNotBetween(String value1, String value2) {
addCriterion("id not between", value1, value2, "id");
return (Criteria) this;
}
......
......@@ -11,7 +11,7 @@ public interface LoadTestReportLogMapper {
int deleteByExample(LoadTestReportLogExample example);
int deleteByPrimaryKey(Long id);
int deleteByPrimaryKey(String id);
int insert(LoadTestReportLog record);
......@@ -21,7 +21,7 @@ public interface LoadTestReportLogMapper {
List<LoadTestReportLog> selectByExample(LoadTestReportLogExample example);
LoadTestReportLog selectByPrimaryKey(Long id);
LoadTestReportLog selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") LoadTestReportLog record, @Param("example") LoadTestReportLogExample example);
......
......@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.LoadTestReportLogMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.LoadTestReportLog">
<id column="id" jdbcType="BIGINT" property="id" />
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="report_id" jdbcType="VARCHAR" property="reportId" />
<result column="resource_id" jdbcType="VARCHAR" property="resourceId" />
</resultMap>
......@@ -103,17 +103,17 @@
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="ResultMapWithBLOBs">
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from load_test_report_log
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from load_test_report_log
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.LoadTestReportLogExample">
delete from load_test_report_log
......@@ -124,7 +124,7 @@
<insert id="insert" parameterType="io.metersphere.base.domain.LoadTestReportLog">
insert into load_test_report_log (id, report_id, resource_id,
content)
values (#{id,jdbcType=BIGINT}, #{reportId,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
values (#{id,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
#{content,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.LoadTestReportLog">
......@@ -145,7 +145,7 @@
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=BIGINT},
#{id,jdbcType=VARCHAR},
</if>
<if test="reportId != null">
#{reportId,jdbcType=VARCHAR},
......@@ -168,7 +168,7 @@
update load_test_report_log
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=BIGINT},
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.reportId != null">
report_id = #{record.reportId,jdbcType=VARCHAR},
......@@ -186,7 +186,7 @@
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update load_test_report_log
set id = #{record.id,jdbcType=BIGINT},
set id = #{record.id,jdbcType=VARCHAR},
report_id = #{record.reportId,jdbcType=VARCHAR},
resource_id = #{record.resourceId,jdbcType=VARCHAR},
content = #{record.content,jdbcType=LONGVARCHAR}
......@@ -196,7 +196,7 @@
</update>
<update id="updateByExample" parameterType="map">
update load_test_report_log
set id = #{record.id,jdbcType=BIGINT},
set id = #{record.id,jdbcType=VARCHAR},
report_id = #{record.reportId,jdbcType=VARCHAR},
resource_id = #{record.resourceId,jdbcType=VARCHAR}
<if test="_parameter != null">
......@@ -216,19 +216,19 @@
content = #{content,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.LoadTestReportLog">
update load_test_report_log
set report_id = #{reportId,jdbcType=VARCHAR},
resource_id = #{resourceId,jdbcType=VARCHAR},
content = #{content,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.LoadTestReportLog">
update load_test_report_log
set report_id = #{reportId,jdbcType=VARCHAR},
resource_id = #{resourceId,jdbcType=VARCHAR}
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>
\ No newline at end of file
......@@ -11,7 +11,7 @@ public interface LoadTestReportResultMapper {
int deleteByExample(LoadTestReportResultExample example);
int deleteByPrimaryKey(Long id);
int deleteByPrimaryKey(String id);
int insert(LoadTestReportResult record);
......@@ -21,7 +21,7 @@ public interface LoadTestReportResultMapper {
List<LoadTestReportResult> selectByExample(LoadTestReportResultExample example);
LoadTestReportResult selectByPrimaryKey(Long id);
LoadTestReportResult selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") LoadTestReportResult record, @Param("example") LoadTestReportResultExample example);
......
......@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.LoadTestReportResultMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.LoadTestReportResult">
<id column="id" jdbcType="BIGINT" property="id" />
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="report_id" jdbcType="VARCHAR" property="reportId" />
<result column="report_key" jdbcType="VARCHAR" property="reportKey" />
</resultMap>
......@@ -103,17 +103,17 @@
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="ResultMapWithBLOBs">
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from load_test_report_result
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from load_test_report_result
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.LoadTestReportResultExample">
delete from load_test_report_result
......@@ -124,7 +124,7 @@
<insert id="insert" parameterType="io.metersphere.base.domain.LoadTestReportResult">
insert into load_test_report_result (id, report_id, report_key,
report_value)
values (#{id,jdbcType=BIGINT}, #{reportId,jdbcType=VARCHAR}, #{reportKey,jdbcType=VARCHAR},
values (#{id,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR}, #{reportKey,jdbcType=VARCHAR},
#{reportValue,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.LoadTestReportResult">
......@@ -145,7 +145,7 @@
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=BIGINT},
#{id,jdbcType=VARCHAR},
</if>
<if test="reportId != null">
#{reportId,jdbcType=VARCHAR},
......@@ -168,7 +168,7 @@
update load_test_report_result
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=BIGINT},
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.reportId != null">
report_id = #{record.reportId,jdbcType=VARCHAR},
......@@ -186,7 +186,7 @@
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update load_test_report_result
set id = #{record.id,jdbcType=BIGINT},
set id = #{record.id,jdbcType=VARCHAR},
report_id = #{record.reportId,jdbcType=VARCHAR},
report_key = #{record.reportKey,jdbcType=VARCHAR},
report_value = #{record.reportValue,jdbcType=LONGVARCHAR}
......@@ -196,7 +196,7 @@
</update>
<update id="updateByExample" parameterType="map">
update load_test_report_result
set id = #{record.id,jdbcType=BIGINT},
set id = #{record.id,jdbcType=VARCHAR},
report_id = #{record.reportId,jdbcType=VARCHAR},
report_key = #{record.reportKey,jdbcType=VARCHAR}
<if test="_parameter != null">
......@@ -216,19 +216,19 @@
report_value = #{reportValue,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.LoadTestReportResult">
update load_test_report_result
set report_id = #{reportId,jdbcType=VARCHAR},
report_key = #{reportKey,jdbcType=VARCHAR},
report_value = #{reportValue,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.LoadTestReportResult">
update load_test_report_result
set report_id = #{reportId,jdbcType=VARCHAR},
report_key = #{reportKey,jdbcType=VARCHAR}
where id = #{id,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>
\ No newline at end of file
package io.metersphere.base.mapper.ext;
import io.metersphere.controller.request.ReportRequest;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.dto.ApiReportDTO;
import org.apache.ibatis.annotations.Param;
......@@ -8,7 +9,8 @@ import java.util.List;
public interface ExtApiTestReportMapper {
List<ApiReportDTO> getReportList(@Param("reportRequest") ReportRequest request);
List<APIReportResult> list(@Param("request") QueryAPIReportRequest request);
List<APIReportResult> listByTestId(@Param("testId") String testId);
ApiReportDTO getReportTestAndProInfo(@Param("id") String id);
}
......@@ -2,24 +2,43 @@
<!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.ExtApiTestReportMapper">
<select id="getReportList" resultType="io.metersphere.dto.ApiReportDTO">
select ltr.id, ltr.name, ltr.test_id as testId, ltr.description,
ltr.create_time as createTime, ltr.update_time as updateTime, ltr.status as status, lt.name as testName
from api_test_report ltr left join api_test lt on ltr.test_id = lt.id
<resultMap id="BaseResultMap" type="io.metersphere.api.dto.APIReportResult"
extends="io.metersphere.base.mapper.ApiTestReportMapper.BaseResultMap">
<result column="project_name" property="projectName"/>
</resultMap>
<select id="list" resultMap="BaseResultMap">
SELECT t.name, t.description,
r.id, r.test_id, r.create_time, r.update_time, r.status,
project.name AS project_name
FROM api_test_report r JOIN api_test t ON r.test_id = t.id
JOIN project ON project.id = t.project_id
<where>
<if test="reportRequest.name != null">
AND ltr.name like CONCAT('%', #{reportRequest.name},'%')
<if test="request.name != null">
AND r.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.projectId != null">
AND project.id = #{request.projectId}
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId,jdbcType=VARCHAR}
</if>
</where>
<if test="request.recent">
ORDER BY r.update_time DESC
</if>
</select>
<select id="getReportTestAndProInfo" resultType="io.metersphere.dto.ApiReportDTO">
select ltr.id, ltr.name, ltr.test_id as testId, ltr.description,
ltr.create_time as createTime, ltr.update_time as updateTime, ltr.status as status, ltr.content as content,
lt.name as testName,
p.id as projectId, p.name as projectName
from api_test_report ltr left join api_test lt on ltr.test_id = lt.id left join project p on lt.project_id = p.id
where ltr.id = #{id}
<select id="listByTestId" resultMap="BaseResultMap">
SELECT t.name, t.description,
r.id, r.test_id, r.create_time, r.update_time, r.status,
project.name AS project_name
FROM api_test_report r JOIN api_test t ON r.test_id = t.id
JOIN project ON project.id = t.project_id
<where>
r.test_id = #{testId}
</where>
ORDER BY r.update_time DESC
</select>
</mapper>
\ No newline at end of file
......@@ -3,11 +3,14 @@ package io.metersphere.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.TestResourcePool;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest;
import io.metersphere.dto.TestResourcePoolDTO;
import io.metersphere.service.TestResourcePoolService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
......@@ -15,6 +18,7 @@ import java.util.List;
@RequestMapping("testresourcepool")
@RestController
@RequiresRoles(RoleConstants.ADMIN)
public class TestResourcePoolController {
@Resource
......@@ -35,6 +39,11 @@ public class TestResourcePoolController {
testResourcePoolService.updateTestResourcePool(testResourcePoolDTO);
}
@GetMapping("/update/{poolId}/{status}")
public void updateTestResourcePoolStatus(@PathVariable String poolId, @PathVariable String status) {
testResourcePoolService.updateTestResourcePoolStatus(poolId, status);
}
@PostMapping("list/{goPage}/{pageSize}")
public Pager<List<TestResourcePoolDTO>> listResourcePools(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryResourcePoolRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
......@@ -42,6 +51,7 @@ public class TestResourcePoolController {
}
@GetMapping("list/all/valid")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public List<TestResourcePool> listValidResourcePools() {
return testResourcePoolService.listValidResourcePools();
}
......
......@@ -27,7 +27,7 @@ public class UserRoleController {
@GetMapping("/list/ws/{workspaceId}/{userId}")
@RequiresRoles(value = {RoleConstants.ADMIN,RoleConstants.ORG_ADMIN}, logical = Logical.OR)
public List<Role> getWorkspaceMemberRole(@PathVariable String workspaceId, @PathVariable String userId) {
public List<Role> getWorkspaceMemberRoles(@PathVariable String workspaceId, @PathVariable String userId) {
return userRoleService.getWorkspaceMemberRoles(workspaceId, userId);
}
}
......@@ -63,7 +63,7 @@ public class KubernetesTestEngine extends AbstractEngine {
kubernetesProvider.confirmNamespace(context.getNamespace());
// create cm
try (KubernetesClient client = kubernetesProvider.getKubernetesClient()) {
String configMapName = context.getTestId() + "-files";
String configMapName = "jmeter-" + context.getTestId() + "-files";
ConfigMap configMap = client.configMaps().inNamespace(context.getNamespace()).withName(configMapName).get();
if (configMap == null) {
ConfigMap item = new ConfigMap();
......
package io.metersphere.service;
import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.base.domain.ApiTestReportExample;
import io.metersphere.base.mapper.ApiTestReportMapper;
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
import io.metersphere.controller.request.ReportRequest;
import io.metersphere.dto.ApiReportDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiReportService {
@Resource
private ApiTestReportMapper ApiTestReportMapper;
@Resource
private ExtApiTestReportMapper extApiTestReportMapper;
public List<ApiTestReport> getRecentReportList(ReportRequest request) {
ApiTestReportExample example = new ApiTestReportExample();
example.setOrderByClause("update_time desc");
return ApiTestReportMapper.selectByExample(example);
}
public List<ApiReportDTO> getReportList(ReportRequest request) {
return extApiTestReportMapper.getReportList(request);
}
public void deleteReport(String reportId) {
ApiTestReportMapper.deleteByPrimaryKey(reportId);
}
public ApiReportDTO getReportTestAndProInfo(String reportId) {
return extApiTestReportMapper.getReportTestAndProInfo(reportId);
}
}
......@@ -228,6 +228,7 @@ public class PerformanceTestService {
List<TestResource> testResourceList = testResourceService.getResourcesByPoolId(resourcePoolId);
testResourceList.forEach(r -> {
LoadTestReportLog record = new LoadTestReportLog();
record.setId(UUID.randomUUID().toString());
record.setReportId(testReport.getId());
record.setResourceId(r.getId());
record.setContent(StringUtils.EMPTY);
......
......@@ -72,6 +72,36 @@ public class TestResourcePoolService {
testResourcePoolMapper.updateByPrimaryKeySelective(testResourcePool);
}
public void updateTestResourcePoolStatus(String poolId, String status) {
TestResourcePool testResourcePool = testResourcePoolMapper.selectByPrimaryKey(poolId);
if (testResourcePool == null) {
MSException.throwException("Resource Pool not found.");
}
testResourcePool.setUpdateTime(System.currentTimeMillis());
testResourcePool.setStatus(status);
// 禁用资源池
if (INVALID.name().equals(status)) {
testResourcePoolMapper.updateByPrimaryKeySelective(testResourcePool);
return;
}
TestResourcePoolDTO testResourcePoolDTO = new TestResourcePoolDTO();
try {
BeanUtils.copyProperties(testResourcePoolDTO, testResourcePool);
TestResourceExample example2 = new TestResourceExample();
example2.createCriteria().andTestResourcePoolIdEqualTo(poolId);
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(example2);
testResourcePoolDTO.setResources(testResources);
if (validateTestResourcePool(testResourcePoolDTO)) {
testResourcePoolMapper.updateByPrimaryKeySelective(testResourcePool);
} else {
MSException.throwException("Resource Pool is invalid.");
}
} catch (IllegalAccessException | InvocationTargetException e) {
LogUtil.error(e);
}
}
public List<TestResourcePoolDTO> listResourcePools(QueryResourcePoolRequest request) {
TestResourcePoolExample example = new TestResourcePoolExample();
TestResourcePoolExample.Criteria criteria = example.createCriteria();
......@@ -96,15 +126,14 @@ public class TestResourcePoolService {
return testResourcePoolDTOS;
}
private void validateTestResourcePool(TestResourcePoolDTO testResourcePool) {
private boolean validateTestResourcePool(TestResourcePoolDTO testResourcePool) {
if (StringUtils.equalsIgnoreCase(testResourcePool.getType(), ResourcePoolTypeEnum.K8S.name())) {
validateK8s(testResourcePool);
return;
return validateK8s(testResourcePool);
}
validateNodes(testResourcePool);
return validateNodes(testResourcePool);
}
private void validateNodes(TestResourcePoolDTO testResourcePool) {
private boolean validateNodes(TestResourcePoolDTO testResourcePool) {
if (CollectionUtils.isEmpty(testResourcePool.getResources())) {
MSException.throwException(Translator.get("no_nodes_message"));
}
......@@ -121,19 +150,21 @@ public class TestResourcePoolService {
MSException.throwException(Translator.get("duplicate_node_ip"));
}
testResourcePool.setStatus(VALID.name());
boolean isValid = true;
for (TestResource resource : testResourcePool.getResources()) {
NodeDTO nodeDTO = JSON.parseObject(resource.getConfiguration(), NodeDTO.class);
boolean isValidate = validateNode(nodeDTO);
if (!isValidate) {
testResourcePool.setStatus(ResourceStatusEnum.INVALID.name());
resource.setStatus(ResourceStatusEnum.INVALID.name());
isValid = false;
} else {
resource.setStatus(VALID.name());
}
resource.setTestResourcePoolId(testResourcePool.getId());
updateTestResource(resource);
}
return isValid;
}
private boolean validateNode(NodeDTO node) {
......@@ -145,7 +176,7 @@ public class TestResourcePoolService {
}
}
private void validateK8s(TestResourcePoolDTO testResourcePool) {
private boolean validateK8s(TestResourcePoolDTO testResourcePool) {
if (CollectionUtils.isEmpty(testResourcePool.getResources()) || testResourcePool.getResources().size() != 1) {
throw new RuntimeException(Translator.get("only_one_k8s"));
......@@ -153,18 +184,21 @@ public class TestResourcePoolService {
TestResource testResource = testResourcePool.getResources().get(0);
testResource.setTestResourcePoolId(testResourcePool.getId());
boolean isValid;
try {
KubernetesProvider provider = new KubernetesProvider(testResource.getConfiguration());
provider.validateCredential();
testResource.setStatus(VALID.name());
testResourcePool.setStatus(VALID.name());
isValid = true;
} catch (Exception e) {
testResource.setStatus(ResourceStatusEnum.INVALID.name());
testResourcePool.setStatus(ResourceStatusEnum.INVALID.name());
isValid = false;
}
deleteTestResource(testResourcePool.getId());
updateTestResource(testResource);
return isValid;
}
private void updateTestResource(TestResource testResource) {
......@@ -189,6 +223,10 @@ public class TestResourcePoolService {
List<TestResourcePoolDTO> testResourcePools = listResourcePools(request);
// 重新校验 pool
for (TestResourcePoolDTO pool : testResourcePools) {
// 手动设置成无效的, 排除
if (INVALID.name().equals(pool.getStatus())) {
continue;
}
try {
updateTestResourcePool(pool);
} catch (MSException e) {
......@@ -201,4 +239,5 @@ public class TestResourcePoolService {
example.createCriteria().andStatusEqualTo(ResourceStatusEnum.VALID.name());
return testResourcePoolMapper.selectByExample(example);
}
}
CREATE TABLE IF NOT EXISTS `file_content` (
`file_id` varchar(64) COLLATE utf8mb4_bin NOT NULL COMMENT 'File ID',
`file_id` varchar(64) NOT NULL COMMENT 'File ID',
`file` longblob COMMENT 'File content',
PRIMARY KEY (`file_id`)
)
......@@ -70,29 +70,24 @@ CREATE TABLE IF NOT EXISTS `load_test_report_detail` (
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;
CREATE TABLE IF NOT EXISTS `load_test_report_log` (
`id` varchar(50) NOT NULL,
`report_id` varchar(50) NOT NULL,
`resource_id` varchar(50) DEFAULT NULL,
`content` longtext ,
PRIMARY KEY (`id`),
KEY `load_test_report_log_report_id_resource_name_index` (`report_id`,`resource_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE IF NOT EXISTS `load_test_report_result` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`report_id` varchar(50) NOT NULL,
`report_key` varchar(64) DEFAULT NULL,
`report_value` text,
`id` varchar(50) NOT NULL,
`report_id` varchar(50) NOT NULL,
`report_key` varchar(64) DEFAULT NULL,
`report_value` text ,
PRIMARY KEY (`id`),
KEY `load_test_report_result_report_id_report_key_index` (`report_id`,`report_key`)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE IF NOT EXISTS `load_test_report_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`report_id` varchar(50) NOT NULL,
`resource_id` varchar(50) DEFAULT NULL,
`content` longtext,
PRIMARY KEY (`id`),
KEY `load_test_report_log_report_id_resource_name_index` (`report_id`,`resource_id`)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_bin;
CREATE TABLE IF NOT EXISTS `organization` (
`id` varchar(50) NOT NULL COMMENT 'Organization ID',
......@@ -145,7 +140,7 @@ CREATE TABLE IF NOT EXISTS `system_parameter` (
CREATE TABLE IF NOT EXISTS `test_resource` (
`id` varchar(50) NOT NULL COMMENT 'Test resource ID',
`test_resource_pool_id` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 'Test resource pool ID this test resource belongs to',
`test_resource_pool_id` varchar(50) NOT NULL COMMENT 'Test resource pool ID this test resource belongs to',
`configuration` longtext COMMENT 'Test resource configuration',
`status` varchar(64) NOT NULL COMMENT 'Test resource status',
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
......@@ -217,13 +212,13 @@ CREATE TABLE IF NOT EXISTS `workspace` (
-- api start
CREATE TABLE IF NOT EXISTS `api_test` (
`id` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 'Test ID',
`project_id` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 'Project ID this test belongs to',
`name` varchar(64) COLLATE utf8mb4_bin NOT NULL COMMENT 'Test name',
`description` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Test description',
`scenario_definition` longtext COLLATE utf8mb4_bin COMMENT 'Scenario definition (JSON format)',
`schedule` longtext COLLATE utf8mb4_bin COMMENT 'Test schedule (cron list)',
`status` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
`id` varchar(50) NOT NULL COMMENT 'Test ID',
`project_id` varchar(50) NOT NULL COMMENT 'Project ID this test belongs to',
`name` varchar(64) NOT NULL COMMENT 'Test name',
`description` varchar(255) DEFAULT NULL COMMENT 'Test description',
`scenario_definition` longtext COMMENT 'Scenario definition (JSON format)',
`schedule` longtext COMMENT 'Test schedule (cron list)',
`status` varchar(64) DEFAULT NULL,
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
PRIMARY KEY (`id`)
......
......@@ -32,7 +32,7 @@
<template v-slot:title>{{$t('commons.report')}}</template>
<ms-recent-list :options="reportRecent"/>
<el-divider/>
<ms-show-all :index="'/api/report/all'"/>
<ms-show-all :index="'/api/report/list/all'"/>
<!-- <el-menu-item :index="reportViewPath" class="blank_item"></el-menu-item>-->
</el-submenu>
</el-menu>
......@@ -90,7 +90,7 @@
title: this.$t('report.recent'),
url: "/api/report/recent/5",
index: function (item) {
return '/api/report/view/' + item.id;
return '/api/report/view?id=' + item.id;
}
}
}
......
......@@ -3,18 +3,10 @@
<div class="main-content">
<el-card>
<template v-slot:header>
<el-row type="flex" justify="space-between" align="middle">
<span class="title">{{$t('commons.report')}}</span>
<span class="search">
<el-input type="text" size="small" :placeholder="$t('report.search_by_name')"
prefix-icon="el-icon-search"
maxlength="60"
v-model="condition" @change="search" clearable/>
</span>
</el-row>
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
:show-create="false"/>
</template>
<el-table :data="tableData" class="test-content">
<el-table :data="tableData" class="table-content">
<el-table-column
prop="name"
:label="$t('commons.name')"
......@@ -26,12 +18,6 @@
:label="$t('commons.description')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="testName"
:label="$t('report.test_name')"
width="150"
show-overflow-tooltip>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.create_time')">
......@@ -50,86 +36,68 @@
width="150"
:label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="handleEdit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="handleView(scope.row)" type="primary" icon="el-icon-s-data" size="mini" circle/>
<el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
</template>
</el-table-column>
</el-table>
<div>
<el-row>
<el-col :span="22" :offset="1">
<div class="table-page">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[5, 10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</el-col>
</el-row>
</div>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</div>
</div>
</template>
<script>
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
export default {
name: "ApiTestReport",
created: function () {
this.initTableData();
},
components: {MsTableHeader, MsTablePagination},
data() {
return {
result: {},
queryPath: "/api/report/list/all",
deletePath: "/api/report/delete/",
condition: "",
condition: {name: ""},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
loading: false
}
},
beforeRouteEnter(to, from, next) {
next(self => {
self.testId = to.params.testId;
self.search();
});
},
methods: {
initTableData() {
search() {
let param = {
name: this.condition,
name: this.condition.name,
};
this.result = this.$post(this.buildPagePath(this.queryPath), param, response => {
if (this.testId !== 'all') {
param.testId = this.testId;
}
let url = "/api/report/list/" + this.currentPage + "/" + this.pageSize
this.result = this.$post(url, param, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search() {
this.initTableData();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSizeChange(size) {
this.pageSize = size;
this.initTableData();
},
handleCurrentChange(current) {
this.currentPage = current;
this.initTableData();
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(report) {
handleView(report) {
this.$router.push({
path: '/api/report/view/' + report.id
path: '/api/report/view/' + report.id,
})
},
handleDelete(report) {
......@@ -137,33 +105,20 @@
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(report);
this.result = this.$post("/api/report/delete", {id: report.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
});
}
}
});
},
_handleDelete(report) {
this.result = this.$post(this.deletePath + report.id, {}, () => {
this.$message({
message: this.$t('commons.delete_success'),
type: 'success'
});
this.initTableData();
});
},
}
}
}
</script>
<style scoped>
.test-content {
.table-content {
width: 100%;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
</style>
......@@ -16,7 +16,7 @@
{{$t('commons.save')}}
</el-button>
<el-button type="primary" plain :disabled="isDisabled" @click="runTest">
<el-button type="primary" plain @click="runTest">
{{$t('load_test.save_and_run')}}
</el-button>
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
......@@ -42,6 +42,7 @@
data() {
return {
create: false,
result: {},
projects: [],
change: false,
......@@ -65,8 +66,10 @@
this.projects = response.data;
})
if (this.id) {
this.create = false;
this.getTest(this.id);
} else {
this.create = true;
this.test = new Test();
if (this.$refs.config) {
this.$refs.config.reset();
......@@ -89,20 +92,28 @@
});
},
saveTest: function () {
this.change = false;
this.result = this.$request(this.getOptions("/api/save"), response => {
this.test.id = response.data;
this.save(() => {
this.$success(this.$t('commons.save_success'));
})
},
save: function (callback) {
this.change = false;
let url = this.create ? "/api/create" : "/api/update";
this.result = this.$request(this.getOptions(url), response => {
this.create = false;
if (callback) callback();
});
},
runTest: function () {
this.change = false;
this.result = this.$request(this.getOptions("/api/run"), response => {
this.test.id = response.data;
this.save(() => {
this.$success(this.$t('commons.save_success'));
});
this.result = this.$post("/api/run", {id: this.test.id}, response => {
this.$success(this.$t('api_test.running'));
});
})
},
cancel: function () {
this.$router.push('/api/test/list/all');
......@@ -123,7 +134,6 @@
let jmx = this.test.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
formData.append("files", new File([blob], jmx.name));
console.log(jmx.xml)
return {
method: 'POST',
......
......@@ -6,7 +6,7 @@
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
@create="create" :createTip="$t('load_test.create')"/>
</template>
<el-table :data="tableData" class="test-content">
<el-table :data="tableData" class="table-content">
<el-table-column
prop="name"
:label="$t('commons.name')"
......@@ -70,8 +70,7 @@
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
loading: false
}
},
......@@ -128,7 +127,7 @@
</script>
<style scoped>
.test-content {
.table-content {
width: 100%;
}
</style>
......@@ -204,6 +204,39 @@ export class ThreadGroup extends DefaultTestElement {
}
}
export class PostThreadGroup extends DefaultTestElement {
constructor(testName) {
super('PostThreadGroup', 'PostThreadGroupGui', 'PostThreadGroup', testName || 'tearDown Thread Group');
this.intProp("ThreadGroup.num_threads", 1);
this.intProp("ThreadGroup.ramp_time", 1);
this.boolProp("ThreadGroup.scheduler", false);
this.stringProp("ThreadGroup.on_sample_error", "continue");
let loopAttrs = {
name: "ThreadGroup.main_controller",
elementType: "LoopController",
guiclass: "LoopControlPanel",
testclass: "LoopController",
testname: "Loop Controller",
enabled: "true"
};
let loopController = this.add(new Element('elementProp', loopAttrs));
loopController.boolProp('LoopController.continue_forever', false);
loopController.stringProp('LoopController.loops', 1);
}
}
export class DebugSampler extends DefaultTestElement {
constructor(testName) {
super('DebugSampler', 'TestBeanGUI', 'DebugSampler', testName || 'Debug Sampler');
this.boolProp("displayJMeterProperties", false);
this.boolProp("displayJMeterVariables", true);
this.boolProp("displaySystemProperties", false);
}
}
export class HTTPSamplerProxy extends DefaultTestElement {
constructor(testName, request) {
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName || 'HTTP Request');
......@@ -219,37 +252,6 @@ export class HTTPSamplerProxy extends DefaultTestElement {
this.stringProp("HTTPSampler.port", this.request.port);
}
}
addRequestArguments(arg) {
if (arg instanceof HTTPSamplerArguments) {
this.add(arg);
}
}
addRequestBody(body) {
if (body instanceof HTTPSamplerArguments) {
this.boolProp('HTTPSampler.postBodyRaw', true);
this.add(body);
}
}
putRequestHeader(header) {
if (header instanceof HeaderManager) {
this.put(header);
}
}
putResponseAssertion(assertion) {
if (assertion instanceof ResponseAssertion) {
this.put(assertion);
}
}
putDurationAssertion(assertion) {
if (assertion instanceof DurationAssertion) {
this.put(assertion);
}
}
}
// 这是一个Element
......@@ -373,7 +375,9 @@ export class BackendListener extends DefaultTestElement {
constructor(testName, className, args) {
super('BackendListener', 'BackendListenerGui', 'BackendListener', testName || 'Backend Listener');
this.stringProp('classname', className);
this.add(new ElementArguments(args));
if (args && args.length > 0) {
this.add(new ElementArguments(args));
}
}
}
......@@ -397,3 +401,6 @@ export class ElementArguments extends Element {
}
}
export class Class {
}
......@@ -5,6 +5,8 @@ import {
TestElement,
TestPlan,
ThreadGroup,
PostThreadGroup,
DebugSampler,
HeaderManager,
HTTPSamplerArguments,
ResponseCodeAssertion,
......@@ -13,9 +15,21 @@ import {
BackendListener
} from "./JMX";
export const generateId = function () {
return Math.floor(Math.random() * 10000);
};
export const uuid = function () {
let d = new Date().getTime()
let d2 = (performance && performance.now && (performance.now() * 1000)) || 0;
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
let r = Math.random() * 16;
if (d > 0) {
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else {
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}
export const BODY_TYPE = {
KV: "KeyValue",
......@@ -75,7 +89,7 @@ export class Test extends BaseConfig {
constructor(options) {
super();
this.version = '1.0.0';
this.id = null;
this.id = uuid();
this.name = null;
this.projectId = null;
this.scenarioDefinition = [];
......@@ -101,7 +115,7 @@ export class Test extends BaseConfig {
export class Scenario extends BaseConfig {
constructor(options) {
super();
this.id = generateId();
this.id = uuid();
this.name = null;
this.url = null;
this.parameters = [];
......@@ -122,7 +136,7 @@ export class Scenario extends BaseConfig {
export class Request extends BaseConfig {
constructor(options) {
super();
this.id = generateId();
this.id = uuid();
this.name = null;
this.url = null;
this.method = null;
......@@ -144,6 +158,10 @@ export class Request extends BaseConfig {
options.assertions = new Assertions(options.assertions);
return options;
}
isValid() {
return !!this.url && !!this.method
}
}
export class Body extends BaseConfig {
......@@ -304,14 +322,23 @@ class JMeterTestPlan extends Element {
class JMXGenerator {
constructor(test) {
if (!test || !test.id || !(test instanceof Test)) return;
if (!test || !(test instanceof Test)) return null;
if (!test.id) {
test.id = "#NULL_TEST_ID#";
}
const SPLIT = "@@:";
let testPlan = new TestPlan(test.name);
test.scenarioDefinition.forEach(scenario => {
let threadGroup = new ThreadGroup(scenario.name);
let threadGroup = new ThreadGroup(scenario.name + SPLIT + scenario.id);
scenario.requests.forEach(request => {
let httpSamplerProxy = new HTTPSamplerProxy(request.name, new JMXRequest(request));
if (!request.isValid()) return;
// test.id用于处理结果时区分属于哪个测试
let name = request.name + SPLIT + test.id;
let httpSamplerProxy = new HTTPSamplerProxy(name, new JMXRequest(request));
this.addRequestHeader(httpSamplerProxy, request);
......@@ -326,8 +353,15 @@ class JMXGenerator {
threadGroup.put(httpSamplerProxy);
})
this.addBackendListener(threadGroup, test.id);
this.addBackendListener(threadGroup);
testPlan.put(threadGroup);
// 暂时不加
// let tearDownThreadGroup = new PostThreadGroup();
// tearDownThreadGroup.put(new DebugSampler(test.id));
// this.addBackendListener(tearDownThreadGroup);
//
// testPlan.put(tearDownThreadGroup);
})
this.jmeterTestPlan = new JMeterTestPlan();
......@@ -338,14 +372,14 @@ class JMXGenerator {
let name = request.name + " Headers";
let headers = request.headers.filter(this.filter);
if (headers.length > 0) {
httpSamplerProxy.putRequestHeader(new HeaderManager(name, headers));
httpSamplerProxy.put(new HeaderManager(name, headers));
}
}
addRequestArguments(httpSamplerProxy, request) {
let args = request.parameters.filter(this.filter)
if (args.length > 0) {
httpSamplerProxy.addRequestArguments(new HTTPSamplerArguments(args));
httpSamplerProxy.add(new HTTPSamplerArguments(args));
}
}
......@@ -357,19 +391,20 @@ class JMXGenerator {
body.push({name: '', value: request.body.raw});
}
httpSamplerProxy.addRequestBody(new HTTPSamplerArguments(body));
httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true);
httpSamplerProxy.add(new HTTPSamplerArguments(body));
}
addRequestAssertion(httpSamplerProxy, request) {
let assertions = request.assertions;
if (assertions.regex.length > 0) {
assertions.regex.filter(this.filter).forEach(regex => {
httpSamplerProxy.putResponseAssertion(this.getAssertion(regex));
httpSamplerProxy.put(this.getAssertion(regex));
})
}
if (assertions.duration.isValid()) {
httpSamplerProxy.putDurationAssertion(assertions.duration.type, assertions.duration.value);
httpSamplerProxy.put(assertions.duration.type, assertions.duration.value);
}
}
......@@ -387,11 +422,10 @@ class JMXGenerator {
}
}
addBackendListener(threadGroup, testId) {
addBackendListener(threadGroup) {
let testName = 'API Backend Listener';
let className = 'io.metersphere.api.jmeter.APIBackendListenerClient';
let args = [{name: 'id', value: testId}];
threadGroup.put(new BackendListener(testName, className, args));
threadGroup.put(new BackendListener(testName, className));
}
filter(config) {
......
<template>
<div>
<ms-tag v-for="(role, index) in roles"
:key="index"
:effect="effect"
:type="type"
:content="role.name"/>
</div>
</template>
<script>
import MsTag from "./MsTag";
export default {
name: "MsRolesTag",
components: {MsTag},
props: {
type: {
type: String,
default: 'primary',
},
roles: {
type: Array
},
effect: {
type: String,
default: 'dark',
}
}
}
</script>
<style scoped>
</style>
......@@ -6,6 +6,7 @@
:effect="effect">
<el-button @click="exec()"
@click.stop="clickStop"
@keydown.enter.native.prevent
circle
:type="type"
:icon="icon"
......
......@@ -16,7 +16,6 @@ import PersonSetting from "../../settings/personal/PersonSetting";
import SystemWorkspace from "../../settings/system/SystemWorkspace";
import PerformanceChart from "../../performance/report/components/PerformanceChart";
import PerformanceTestReport from "../../performance/report/PerformanceTestReport";
import ApiTestReport from "../../api/report/ApiTestReport";
import ApiTest from "../../api/ApiTest";
import PerformanceTest from "../../performance/PerformanceTest";
import ApiTestConfig from "../../api/test/ApiTestConfig";
......@@ -30,6 +29,7 @@ import TestPlan from "../../track/plan/TestPlan";
import TestPlanView from "../../track/plan/view/TestPlanView";
import TestCase from "../../track/case/TestCase";
import TestTrack from "../../track/TestTrack";
import ApiReportList from "../../api/report/ApiReportList";
Vue.use(VueRouter);
......@@ -117,13 +117,13 @@ const router = new VueRouter({
component: MsProject
},
{
path: "report/:type",
name: "fucReport",
component: ApiTestReport
path: "report/list/:testId",
name: "ApiReportList",
component: ApiReportList
},
{
path: "report/view/:reportId",
name: "fucReportView",
name: "ApiReportView",
component: ApiReportView
}
]
......
......@@ -136,10 +136,10 @@
},
handleEdit(report) {
if (report.status === "Error") {
this.$warning("报告生成错误,无法查看!");
this.$warning(this.$t('report.generation_error'));
return false
} else if (report.status === "Starting") {
this.$info("报告生成中...")
this.$info(this.$t('being_generated'))
return false
}
this.$router.push({
......@@ -172,10 +172,4 @@
width: 100%;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
</style>
......@@ -31,7 +31,7 @@
</el-form>
<template v-slot:footer>
<div class="dialog-footer">
<el-button type="primary" onkeydown="return false;" @click="submit('form')" size="medium">
<el-button type="primary" @keydown.enter.native.prevent @click="submit('form')" size="medium">
{{$t('commons.save')}}
</el-button>
</div>
......
......@@ -3,7 +3,7 @@
<div class="menus">
<ms-current-user/>
<el-divider/>
<h1>设置</h1>
<h1>{{$t('commons.setting')}}</h1>
<ms-setting-menu/>
</div>
<div class="container">
......
......@@ -11,17 +11,12 @@
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column prop="roles" :label="$t('commons.role')" width="140">
<template v-slot:default="scope">
<el-tag v-for="(role, index) in scope.row.roles" :key="index" size="mini" effect="dark">
{{ role.name }}
</el-tag>
<ms-roles-tag :roles="scope.row.roles"/>
</template>
</el-table-column>
<el-table-column>
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit" size="mini"
circle/>
<el-button @click="del(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete" size="mini"
circle/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -58,8 +53,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="submitForm('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="submitForm('form')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -92,8 +88,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateOrgMember('updateUserForm')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateOrgMember('updateUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -105,10 +102,12 @@
import {TokenKey} from "../../../../common/js/constants";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsOrganizationMember",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator},
created() {
this.initTableData();
},
......
......@@ -16,10 +16,7 @@
</el-table-column>
<el-table-column>
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit" size="mini"
circle/>
<el-button @click="del(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete" size="mini"
circle/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -38,8 +35,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="submit('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="submit('form')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -55,26 +53,23 @@
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column :label="$t('commons.role')" width="120">
<template v-slot:default="scope">
<el-tag v-for="(role, index) in scope.row.roles" :key="index" size="mini" effect="dark" type="success">
{{ role.name }}
</el-tag>
<ms-roles-tag :roles="scope.row.roles" type="success"/>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="editMember(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit"
size="mini" circle/>
<el-button @click="delMember(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete"
size="mini" circle/>
<ms-table-operator @editClick="editMember(scope.row)" @deleteClick="delMember(scope.row)"/>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="dialogSearch" :current-page.sync="dialogCurrentPage" :page-size.sync="dialogPageSize"
<ms-table-pagination :change="dialogSearch" :current-page.sync="dialogCurrentPage"
:page-size.sync="dialogPageSize"
:total="dialogTotal"/>
</el-dialog>
<!-- add workspace member dialog -->
<el-dialog :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%" :destroy-on-close="true"
<el-dialog :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="memberForm" ref="form" :rules="wsMemberRule" label-position="right" label-width="100px"
size="small">
......@@ -105,14 +100,16 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="submitForm('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="submitForm('form')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
<!-- update workspace member dialog -->
<el-dialog :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%" :destroy-on-close="true"
<el-dialog :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="memberForm" label-position="right" label-width="100px" size="small" ref="updateUserForm">
<el-form-item label="ID" prop="id">
......@@ -141,8 +138,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateOrgMember('updateUserForm')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateOrgMember('updateUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -156,10 +154,12 @@
import {TokenKey} from "../../../../common/js/constants";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsOrganizationWorkspace",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator},
mounted() {
this.list();
},
......
......@@ -27,8 +27,10 @@
</el-table-column>
</el-table>
<el-dialog :title="$t('member.modify_personal_info')" :visible.sync="updateVisible" width="30%" :destroy-on-close="true" @close="closeFunc">
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule" ref="updateUserForm">
<el-dialog :title="$t('member.modify_personal_info')" :visible.sync="updateVisible" width="30%"
:destroy-on-close="true" @close="closeFunc">
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule"
ref="updateUserForm">
<el-form-item label="ID" prop="id">
<el-input v-model="form.id" autocomplete="off" :disabled="true"/>
</el-form-item>
......@@ -44,8 +46,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateUser('updateUserForm')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateUser('updateUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -68,7 +71,7 @@
rule: {
name: [
{required: true, message: this.$t('member.input_name'), trigger: 'blur'},
{ min: 2, max: 10, message: this.$t('commons.input_limit', [2, 10]), trigger: 'blur' },
{min: 2, max: 10, message: this.$t('commons.input_limit', [2, 10]), trigger: 'blur'},
{
required: true,
pattern: /^[\u4e00-\u9fa5_a-zA-Z0-9.·-]+$/,
......@@ -85,7 +88,7 @@
}
],
email: [
{ required: true, message: this.$t('member.input_email'), trigger: 'blur' },
{required: true, message: this.$t('member.input_email'), trigger: 'blur'},
{
required: true,
pattern: /^([A-Za-z0-9_\-.])+@([A-Za-z0-9]+\.)+[A-Za-z]{2,6}$/,
......@@ -112,7 +115,7 @@
updateUser(updateUserForm) {
this.$refs[updateUserForm].validate(valide => {
if (valide) {
this.result = this.$post(this.updatePath, this.form,response => {
this.result = this.$post(this.updatePath, this.form, response => {
this.$success(this.$t('commons.modify_success'));
localStorage.setItem(TokenKey, JSON.stringify(response.data));
this.updateVisible = false;
......
......@@ -19,10 +19,7 @@
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit" size="mini"
circle/>
<el-button @click="del(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete" size="mini"
circle/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -41,17 +38,12 @@
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column :label="$t('commons.role')" width="140">
<template v-slot:default="scope">
<el-tag v-for="(role, index) in scope.row.roles" :key="index" size="mini" effect="dark">
{{ role.name }}
</el-tag>
<ms-roles-tag :roles="scope.row.roles"/>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="editMember(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit"
size="mini" circle/>
<el-button @click="delMember(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete"
size="mini" circle/>
<ms-table-operator @editClick="editMember(scope.row)" @deleteClick="delMember(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -74,8 +66,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="createOrganization('createOrganization')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="createOrganization('createOrganization')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -95,9 +88,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateOrganization('updateOrganizationForm')"
size="medium">{{$t('organization.modify')}}</el-button>
<el-button @click="updateOrganization('updateOrganizationForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('organization.modify')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -135,8 +128,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="submitForm('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="submitForm('form')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -172,8 +166,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateOrgMember('updateUserForm')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateOrgMember('updateUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -185,10 +180,12 @@
import MsCreateBox from "../CreateBox";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsOrganization",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator},
data() {
return {
queryPath: '/organization/list',
......
......@@ -18,8 +18,7 @@
</el-table-column>
<el-table-column>
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="del(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -50,7 +49,7 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;" @click="submit('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button type="primary" @keydown.enter.native.prevent @click="submit('form')" size="medium">{{$t('commons.save')}}</el-button>
</span>
</template>
</el-dialog>
......@@ -78,7 +77,7 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" @click="updateWorkspace('updateForm')" onkeydown="return false;"
<el-button type="primary" @click="updateWorkspace('updateForm')" @keydown.enter.native.prevent
size="medium">{{$t('commons.save')}}</el-button>
</span>
</template>
......@@ -96,17 +95,12 @@
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column :label="$t('commons.role')" width="120">
<template v-slot:default="scope">
<el-tag v-for="(role, index) in scope.row.roles" :key="index" size="mini" effect="dark" type="success">
{{ role.name }}
</el-tag>
<ms-roles-tag :roles="scope.row.roles" type="success"/>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="editMember(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit"
size="mini" circle/>
<el-button @click="delMember(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete"
size="mini" circle/>
<ms-table-operator @editClick="editMember(scope.row)" @deleteClick="delMember(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -116,7 +110,8 @@
</el-dialog>
<!-- add workspace member dialog -->
<el-dialog :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%" :destroy-on-close="true"
<el-dialog :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="memberForm" ref="form" :rules="wsMemberRule" label-position="right" label-width="100px"
size="small">
......@@ -147,13 +142,16 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;" @click="submitForm('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button type="primary" @keydown.enter.native.prevent @click="submitForm('form')" size="medium">
{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
<!-- update workspace member dialog -->
<el-dialog :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%" :destroy-on-close="true"
<el-dialog :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="memberForm" label-position="right" label-width="100px" size="small" ref="updateUserForm">
<el-form-item label="ID" prop="id">
......@@ -182,8 +180,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" @click="updateOrgMember('updateUserForm')" onkeydown="return false;"
size="medium">{{$t('commons.save')}}</el-button>
<el-button type="primary" @click="updateOrgMember('updateUserForm')" @keydown.enter.native.prevent
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
......@@ -197,10 +196,12 @@
import {Message} from "element-ui";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsSystemWorkspace",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator},
mounted() {
this.list();
},
......
......@@ -38,8 +38,7 @@
</el-table-column>
<el-table-column>
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="del(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -122,9 +121,10 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="createTestResourcePool('createTestResourcePoolForm')"
size="medium">{{$t('commons.create')}}</el-button>
<el-button @click="createTestResourcePool('createTestResourcePoolForm')" @keydown.enter.native.prevent
type="primary"
size="medium">{{$t('commons.create')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -202,9 +202,10 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateTestResourcePool('updateTestResourcePoolForm')"
size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateTestResourcePool('updateTestResourcePoolForm')" @keydown.enter.native.prevent
type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -216,10 +217,11 @@
import MsCreateBox from "../CreateBox";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsTestResourcePool",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsTableOperator},
data() {
return {
result: {},
......@@ -413,9 +415,13 @@
this.form = {};
},
changeSwitch(row) {
this.result = this.$post('/testresourcepool/update', row).then(() => {
this.$success(this.$t('test_resource_pool.status_change_success'));
})
this.result = this.$get('/testresourcepool/update/' + row.id + '/' + row.status)
.then(() => {
this.$success(this.$t('test_resource_pool.status_change_success'));
}).catch(() => {
this.$error(this.$t('test_resource_pool.status_change_failed'));
row.status = 'INVALID';
})
}
}
}
......
......@@ -30,8 +30,7 @@
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="del(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>
</template>
</el-table-column>
</el-table>
......@@ -59,8 +58,8 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="createUser('createUserForm')" size="medium">创建</el-button>
<el-button @click="createUser('createUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}</el-button>
</span>
</template>
</el-dialog>
......@@ -83,8 +82,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateUser('updateUserForm')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateUser('updateUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -96,10 +96,11 @@
import MsCreateBox from "../CreateBox";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsUser",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsTableOperator},
data() {
return {
queryPath: '/user/special/list',
......
......@@ -9,19 +9,14 @@
<el-table-column prop="name" :label="$t('commons.username')"/>
<el-table-column prop="email" :label="$t('commons.email')"/>
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column prop="roles" label="角色" width="120">
<el-table-column prop="roles" :label="$t('commons.role')" width="120">
<template v-slot:default="scope">
<el-tag v-for="(role, index) in scope.row.roles" :key="index" size="mini" effect="dark" type="success">
{{ role.name }}
</el-tag>
<ms-roles-tag :roles="scope.row.roles" type="success"/>
</template>
</el-table-column>
<el-table-column>
<template v-slot:default="scope">
<el-button @click="edit(scope.row)" onkeydown="return false;" type="primary" icon="el-icon-edit" size="mini"
circle v-permission="['test_manager']"/>
<el-button @click="del(scope.row)" onkeydown="return false;" type="danger" icon="el-icon-delete" size="mini"
circle v-permission="['test_manager']"/>
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)" v-permission="['test_manager']"/>
</template>
</el-table-column>
</el-table>
......@@ -29,9 +24,10 @@
:total="total"/>
</el-card>
<el-dialog title="添加成员" :visible.sync="createVisible" width="30%" :destroy-on-close="true" @close="closeFunc">
<el-dialog :title="$t('member.create')" :visible.sync="createVisible" width="30%" :destroy-on-close="true"
@close="closeFunc">
<el-form :model="form" ref="form" :rules="rules" label-position="right" label-width="100px" size="small">
<el-form-item label="成员" prop="userIds">
<el-form-item :label="$t('commons.member')" prop="userIds">
<el-select v-model="form.userIds" multiple :placeholder="$t('member.please_choose_member')"
class="select-width">
<el-option
......@@ -44,7 +40,7 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="角色" prop="roleIds">
<el-form-item :label="$t('commons.role')" prop="roleIds">
<el-select v-model="form.roleIds" multiple :placeholder="$t('role.please_choose_role')" class="select-width">
<el-option
v-for="item in form.roles"
......@@ -57,13 +53,15 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="submitForm('form')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="submitForm('form')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
<el-dialog title="修改成员" :visible.sync="updateVisible" width="30%" :destroy-on-close="true" @close="closeFunc">
<el-dialog :title="$t('member.modify')" :visible.sync="updateVisible" width="30%" :destroy-on-close="true"
@close="closeFunc">
<el-form :model="form" label-position="right" label-width="100px" size="small" ref="updateUserForm">
<el-form-item label="ID" prop="id">
<el-input v-model="form.id" autocomplete="off" :disabled="true"/>
......@@ -77,7 +75,7 @@
<el-form-item :label="$t('commons.phone')" prop="phone">
<el-input v-model="form.phone" autocomplete="off"/>
</el-form-item>
<el-form-item label="角色" prop="roleIds">
<el-form-item :label="$t('commons.role')" prop="roleIds">
<el-select v-model="form.roleIds" multiple :placeholder="$t('role.please_choose_role')" class="select-width">
<el-option
v-for="item in form.allroles"
......@@ -90,8 +88,9 @@
</el-form>
<template v-slot:footer>
<span class="dialog-footer">
<el-button type="primary" onkeydown="return false;"
@click="updateWorkspaceMember('updateUserForm')" size="medium">{{$t('commons.save')}}</el-button>
<el-button @click="updateWorkspaceMember('updateUserForm')" @keydown.enter.native.prevent type="primary"
size="medium">{{$t('commons.save')}}
</el-button>
</span>
</template>
</el-dialog>
......@@ -104,10 +103,12 @@
import {TokenKey} from "../../../../common/js/constants";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsRolesTag from "../../common/components/MsRolesTag";
import MsTableOperator from "../../common/components/MsTableOperator";
export default {
name: "MsMember",
components: {MsCreateBox, MsTablePagination, MsTableHeader},
components: {MsCreateBox, MsTablePagination, MsTableHeader, MsRolesTag, MsTableOperator},
data() {
return {
result: {},
......
<template>
<el-dialog :title="$t('test_track.plan_view.change_executor')"
<el-dialog :title="$t('test_track.case.move')"
:visible.sync="dialogVisible"
:before-close="close"
width="20%">
<el-select v-model.trim="module"
:placeholder="$t('test_track.plan_view.select_executor')"
:placeholder="$t('test_track.case.move')"
filterable>
<el-option v-for="item in moduleOptions" :key="item.id"
:label="item.path" :value="item.id"></el-option>
......
......@@ -2,6 +2,7 @@ export default {
commons: {
'workspace': 'Workspace',
'organization': 'Organization',
'setting': 'Setting',
'project': 'Project',
'name': 'Name',
'description': 'Description',
......@@ -314,6 +315,7 @@ export default {
'fill_the_data': 'Please complete the data',
'delete_prompt': 'This operation will permanently delete the resource pool, continue?',
'status_change_success': 'Successfully changed the status!',
'status_change_failed': 'Failed to change the status!',
},
i18n: {
'home': 'Home'
......
......@@ -2,6 +2,7 @@ export default {
commons: {
'workspace': '工作空间',
'organization': '组织',
'setting': '设置',
'project': '项目',
'name': '名称',
'description': '描述',
......@@ -186,6 +187,7 @@ export default {
},
api_test: {
save_and_run: "保存并执行",
running: "正在执行",
reset: "重置",
input_name: "请输入测试名称",
select_project: "请选择项目",
......@@ -359,6 +361,7 @@ export default {
'fill_the_data': '请完善数据',
'delete_prompt': '此操作将永久删除该资源池, 是否继续?',
'status_change_success': '状态修改成功!',
'status_change_failed': '状态修改失败!',
},
i18n: {
'home': '首页'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册