Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
ec3f54de0f
|
@ -161,6 +161,17 @@
|
|||
<version>2.1.7</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
该依赖是私有仓库的依赖,现已经发布到 Github Packages,下载请在 settings 文件中配置自己的 GITHUB_TOKEN
|
||||
示例:
|
||||
<servers>
|
||||
<server>
|
||||
<id>github</id>
|
||||
<username>USERNAME</username>
|
||||
<password>TOKEN</password>
|
||||
</server>
|
||||
</servers>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.fit2cloud</groupId>
|
||||
<artifactId>quartz-spring-boot-starter</artifactId>
|
||||
|
@ -296,39 +307,9 @@
|
|||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jcenter-snapshots</id>
|
||||
<name>jcenter</name>
|
||||
<url>https://jcenter.bintray.com/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>fit2cloud-enterprise-release</id>
|
||||
<name>Fit2Cloud Enterprise Release</name>
|
||||
<url>http://repository.fit2cloud.com/content/repositories/fit2cloud-enterprise-release/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>fit2cloud</id>
|
||||
<id>github</id>
|
||||
<name>fit2cloud</name>
|
||||
<url>http://repository.fit2cloud.com/content/groups/public/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<url>https://maven.pkg.github.com/fit2cloud/quartz-spring-boot-starter</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
|
|
@ -96,4 +96,10 @@ public class APITestController {
|
|||
return apiTestService.run(request);
|
||||
}
|
||||
|
||||
@PostMapping("/import/{platform}/{projectId}")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public ApiTest testCaseImport(MultipartFile file, @PathVariable String platform, @PathVariable String projectId) {
|
||||
return apiTestService.apiTestImport(file, platform, projectId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto.parse;
|
||||
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ApiImport {
|
||||
private List<Scenario> scenarios;
|
||||
}
|
|
@ -11,6 +11,8 @@ public class Request {
|
|||
private String name;
|
||||
private String url;
|
||||
private String method;
|
||||
private Boolean useEnvironment;
|
||||
private String path;
|
||||
private List<KeyValue> parameters;
|
||||
private List<KeyValue> headers;
|
||||
private Body body;
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.List;
|
|||
public class Scenario {
|
||||
private String name;
|
||||
private String url;
|
||||
private String environmentId;
|
||||
private List<KeyValue> variables;
|
||||
private List<KeyValue> headers;
|
||||
private List<Request> requests;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package io.metersphere.api.parse;
|
||||
|
||||
import io.metersphere.api.dto.parse.ApiImport;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface ApiImportParser {
|
||||
ApiImport parse(InputStream source);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.api.parse;
|
||||
|
||||
import io.metersphere.commons.constants.ApiImportPlatform;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
import io.metersphere.performance.parse.EngineSourceParser;
|
||||
import io.metersphere.performance.parse.xml.XmlEngineSourceParse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class ApiImportParserFactory {
|
||||
public static ApiImportParser getApiImportParser(String platform) {
|
||||
if (StringUtils.equals(ApiImportPlatform.Metersphere.name(), platform)) {
|
||||
return new MsParser();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package io.metersphere.api.parse;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.api.dto.parse.ApiImport;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class MsParser implements ApiImportParser {
|
||||
|
||||
@Override
|
||||
public ApiImport parse(InputStream source) {BufferedReader bufferedReader = null;
|
||||
StringBuilder testStr = null;
|
||||
try {
|
||||
bufferedReader = new BufferedReader(new InputStreamReader(source, "UTF-8"));
|
||||
testStr = new StringBuilder();
|
||||
String inputStr = null;
|
||||
while ((inputStr = bufferedReader.readLine()) != null) {
|
||||
testStr.append(inputStr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
} finally {
|
||||
try {
|
||||
source.close();
|
||||
} catch (IOException e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return JSON.parseObject(testStr.toString(), ApiImport.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,14 +2,19 @@ package io.metersphere.api.service;
|
|||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.APITestResult;
|
||||
import io.metersphere.api.dto.parse.ApiImport;
|
||||
import io.metersphere.api.dto.QueryAPITestRequest;
|
||||
import io.metersphere.api.dto.SaveAPITestRequest;
|
||||
import io.metersphere.api.jmeter.JMeterService;
|
||||
import io.metersphere.api.parse.ApiImportParser;
|
||||
import io.metersphere.api.parse.ApiImportParserFactory;
|
||||
import io.metersphere.api.parse.MsParser;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.ApiTestFileMapper;
|
||||
import io.metersphere.base.mapper.ApiTestMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiTestMapper;
|
||||
import io.metersphere.commons.constants.APITestStatus;
|
||||
import io.metersphere.commons.constants.FileType;
|
||||
import io.metersphere.commons.constants.ScheduleGroup;
|
||||
import io.metersphere.commons.constants.ScheduleType;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
|
@ -26,8 +31,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
@ -163,6 +167,15 @@ public class APITestService {
|
|||
}
|
||||
}
|
||||
|
||||
private Boolean isNameExist(SaveAPITestRequest request) {
|
||||
ApiTestExample example = new ApiTestExample();
|
||||
example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId());
|
||||
if (apiTestMapper.countByExample(example) > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private ApiTest updateTest(SaveAPITestRequest request) {
|
||||
checkNameExist(request);
|
||||
final ApiTest test = new ApiTest();
|
||||
|
@ -246,4 +259,38 @@ public class APITestService {
|
|||
private void addOrUpdateApiTestCronJob(Schedule request) {
|
||||
scheduleService.addOrUpdateCronJob(request, ApiTestJob.getJobKey(request.getResourceId()), ApiTestJob.getTriggerKey(request.getResourceId()), ApiTestJob.class);
|
||||
}
|
||||
|
||||
public ApiTest apiTestImport(MultipartFile file, String platform, String projectId) {
|
||||
try {
|
||||
|
||||
ApiImportParser apiImportParser = ApiImportParserFactory.getApiImportParser(platform);
|
||||
ApiImport apiImport = apiImportParser.parse(file.getInputStream());
|
||||
SaveAPITestRequest request = getImportApiTest(file, projectId, apiImport);
|
||||
ApiTest test = createTest(request);
|
||||
return test;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SaveAPITestRequest getImportApiTest(MultipartFile file, String projectId, ApiImport apiImport) {
|
||||
SaveAPITestRequest request = new SaveAPITestRequest();
|
||||
request.setName(file.getOriginalFilename());
|
||||
request.setProjectId(projectId);
|
||||
request.setScenarioDefinition(apiImport.getScenarios());
|
||||
request.setUserId(SessionUtils.getUser().getId());
|
||||
request.setId(UUID.randomUUID().toString());
|
||||
for (FileType fileType : FileType.values()) {
|
||||
String suffix = fileType.suffix();
|
||||
String name = request.getName();
|
||||
if (name.endsWith(suffix)) {
|
||||
request.setName(name.substring(0, name.length() - suffix.length()));
|
||||
}
|
||||
};
|
||||
if (isNameExist(request)) {
|
||||
request.setName(request.getName() + "_" + request.getId().substring(0, 5));
|
||||
}
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package io.metersphere.api.service;
|
|||
import io.metersphere.base.domain.ApiTestEnvironmentExample;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -37,7 +39,20 @@ public class ApiTestEnvironmentService {
|
|||
|
||||
public String add(ApiTestEnvironmentWithBLOBs apiTestEnvironmentWithBLOBs) {
|
||||
apiTestEnvironmentWithBLOBs.setId(UUID.randomUUID().toString());
|
||||
checkEnvironmentExist(apiTestEnvironmentWithBLOBs);
|
||||
apiTestEnvironmentMapper.insert(apiTestEnvironmentWithBLOBs);
|
||||
return apiTestEnvironmentWithBLOBs.getId();
|
||||
}
|
||||
|
||||
private void checkEnvironmentExist (ApiTestEnvironmentWithBLOBs environment) {
|
||||
if (environment.getName() != null) {
|
||||
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
|
||||
example.createCriteria()
|
||||
.andNameEqualTo(environment.getName())
|
||||
.andProjectIdEqualTo(environment.getProjectId());
|
||||
if (apiTestEnvironmentMapper.selectByExample(example).size() > 0) {
|
||||
MSException.throwException(Translator.get("api_test_environment_already_exists"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum ApiImportPlatform {
|
||||
Metersphere, Postman
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum FileType {
|
||||
JMX(".jmx"), CSV(".csv");
|
||||
JMX(".jmx"), CSV(".csv"), JSON(".json");
|
||||
|
||||
// 保存后缀
|
||||
private String suffix;
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.metersphere.commons.exception.MSException;
|
|||
import io.metersphere.controller.ResultHolder;
|
||||
import io.metersphere.controller.request.LoginRequest;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.ldap.domain.Person;
|
||||
import io.metersphere.ldap.service.LdapService;
|
||||
import io.metersphere.ldap.domain.LdapInfo;
|
||||
import io.metersphere.service.SystemParameterService;
|
||||
|
@ -34,20 +35,25 @@ public class LdapController {
|
|||
MSException.throwException(Translator.get("ldap_authentication_not_enabled"));
|
||||
}
|
||||
|
||||
ldapService.authenticate(request);
|
||||
Person person = ldapService.authenticate(request);
|
||||
|
||||
SecurityUtils.getSubject().getSession().setAttribute("authenticate", "ldap");
|
||||
|
||||
String username = request.getUsername();
|
||||
String password = request.getPassword();
|
||||
|
||||
String email = person.getEmail();
|
||||
|
||||
if (StringUtils.isBlank(email)) {
|
||||
MSException.throwException(Translator.get("login_fail_email_null"));
|
||||
}
|
||||
|
||||
User u = userService.selectUser(request.getUsername());
|
||||
if (u == null) {
|
||||
User user = new User();
|
||||
user.setId(username);
|
||||
user.setName(username);
|
||||
// todo user email ?
|
||||
user.setEmail(username + "@fit2cloud.com");
|
||||
user.setEmail(email);
|
||||
user.setPassword(password);
|
||||
userService.createUser(user);
|
||||
} else {
|
||||
|
|
|
@ -32,6 +32,10 @@ public class PersonRepoImpl implements PersonRepo {
|
|||
|
||||
public boolean authenticate(String dn, String credentials) {
|
||||
LdapTemplate ldapTemplate = getConnection();
|
||||
return authenticate(dn, credentials, ldapTemplate);
|
||||
}
|
||||
|
||||
private boolean authenticate(String dn, String credentials, LdapTemplate ldapTemplate) {
|
||||
DirContext ctx = null;
|
||||
try {
|
||||
ctx = ldapTemplate.getContextSource().getContext(dn, credentials);
|
||||
|
@ -58,9 +62,8 @@ public class PersonRepoImpl implements PersonRepo {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List findByName(String name) {
|
||||
public List<Person> findByName(String name) {
|
||||
LdapTemplate ldapTemplate = getConnection();
|
||||
ldapTemplate.setIgnorePartialResultException(true);
|
||||
LdapQuery query = query().where("cn").is(name);
|
||||
return ldapTemplate.search(query, getContextMapper());
|
||||
}
|
||||
|
@ -68,7 +71,6 @@ public class PersonRepoImpl implements PersonRepo {
|
|||
@Override
|
||||
public String getDnForUser(String uid) {
|
||||
LdapTemplate ldapTemplate = getConnection();
|
||||
ldapTemplate.setIgnorePartialResultException(true);
|
||||
List<String> result = ldapTemplate.search(
|
||||
query().where("cn").is(uid),
|
||||
new AbstractContextMapper() {
|
||||
|
@ -112,7 +114,6 @@ public class PersonRepoImpl implements PersonRepo {
|
|||
|
||||
String credentials = EncryptUtils.aesDecrypt(password).toString();
|
||||
|
||||
|
||||
LdapContextSource sourceLdapCtx = new LdapContextSource();
|
||||
sourceLdapCtx.setUrl(url);
|
||||
sourceLdapCtx.setUserDn(dn);
|
||||
|
@ -120,8 +121,13 @@ public class PersonRepoImpl implements PersonRepo {
|
|||
sourceLdapCtx.setBase(ou);
|
||||
sourceLdapCtx.setDirObjectFactory(DefaultDirObjectFactory.class);
|
||||
sourceLdapCtx.afterPropertiesSet();
|
||||
LdapTemplate ldapTemplate = new LdapTemplate(sourceLdapCtx);
|
||||
ldapTemplate.setIgnorePartialResultException(true);
|
||||
|
||||
return new LdapTemplate(sourceLdapCtx);
|
||||
// ldapTemplate 是否可用
|
||||
authenticate(dn, credentials, ldapTemplate);
|
||||
|
||||
return ldapTemplate;
|
||||
}
|
||||
|
||||
private void preConnect(String url, String dn, String ou, String password) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.metersphere.controller.request.LoginRequest;
|
|||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.ldap.dao.PersonRepoImpl;
|
||||
import io.metersphere.ldap.domain.LdapInfo;
|
||||
import io.metersphere.ldap.domain.Person;
|
||||
import org.springframework.ldap.CommunicationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -18,18 +19,19 @@ public class LdapService {
|
|||
private PersonRepoImpl personRepo;
|
||||
|
||||
|
||||
public void authenticate(LoginRequest request) {
|
||||
public Person authenticate(LoginRequest request) {
|
||||
String dn = null;
|
||||
String username = request.getUsername();
|
||||
String credentials = request.getPassword();
|
||||
|
||||
List<Person> personList = null;
|
||||
try {
|
||||
// select user by sAMAccountName
|
||||
List user = personRepo.findByName(username);
|
||||
personList = personRepo.findByName(username);
|
||||
|
||||
if (user.size() == 1) {
|
||||
if (personList.size() == 1) {
|
||||
dn = personRepo.getDnForUser(username);
|
||||
} else if (user.size() == 0) {
|
||||
} else if (personList.size() == 0) {
|
||||
MSException.throwException(Translator.get("user_not_exist") + username);
|
||||
} else {
|
||||
MSException.throwException(Translator.get("find_more_user"));
|
||||
|
@ -38,6 +40,8 @@ public class LdapService {
|
|||
MSException.throwException(Translator.get("ldap_connect_fail"));
|
||||
}
|
||||
personRepo.authenticate(dn, credentials);
|
||||
|
||||
return personList.get(0);
|
||||
}
|
||||
|
||||
public void testConnect(LdapInfo ldap) {
|
||||
|
|
|
@ -102,16 +102,8 @@ public class PerformanceTestService {
|
|||
if (!loadTestReports.isEmpty()) {
|
||||
List<String> reportIdList = loadTestReports.stream().map(LoadTestReport::getId).collect(Collectors.toList());
|
||||
|
||||
// delete load_test_report_result
|
||||
LoadTestReportResultExample loadTestReportResultExample = new LoadTestReportResultExample();
|
||||
loadTestReportResultExample.createCriteria().andReportIdIn(reportIdList);
|
||||
loadTestReportResultMapper.deleteByExample(loadTestReportResultExample);
|
||||
|
||||
// delete load_test_report, delete load_test_report_detail
|
||||
// delete load_test_report
|
||||
reportIdList.forEach(reportId -> {
|
||||
LoadTestReportDetailExample example = new LoadTestReportDetailExample();
|
||||
example.createCriteria().andReportIdEqualTo(reportId);
|
||||
loadTestReportDetailMapper.deleteByExample(example);
|
||||
reportService.deleteReport(reportId);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,10 +3,7 @@ package io.metersphere.performance.service;
|
|||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.LoadTestMapper;
|
||||
import io.metersphere.base.mapper.LoadTestReportLogMapper;
|
||||
import io.metersphere.base.mapper.LoadTestReportMapper;
|
||||
import io.metersphere.base.mapper.LoadTestReportResultMapper;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
|
||||
import io.metersphere.commons.constants.PerformanceTestStatus;
|
||||
import io.metersphere.commons.constants.ReportKeys;
|
||||
|
@ -45,6 +42,8 @@ public class ReportService {
|
|||
private LoadTestReportLogMapper loadTestReportLogMapper;
|
||||
@Resource
|
||||
private TestResourceService testResourceService;
|
||||
@Resource
|
||||
private LoadTestReportDetailMapper loadTestReportDetailMapper;
|
||||
|
||||
public List<ReportDTO> getRecentReportList(ReportRequest request) {
|
||||
List<OrderRequest> orders = new ArrayList<>();
|
||||
|
@ -85,6 +84,16 @@ public class ReportService {
|
|||
stopEngine(loadTest, engine);
|
||||
}
|
||||
|
||||
// delete load_test_report_result
|
||||
LoadTestReportResultExample loadTestReportResultExample = new LoadTestReportResultExample();
|
||||
loadTestReportResultExample.createCriteria().andReportIdEqualTo(reportId);
|
||||
loadTestReportResultMapper.deleteByExample(loadTestReportResultExample);
|
||||
|
||||
// delete load_test_report_detail
|
||||
LoadTestReportDetailExample example = new LoadTestReportDetailExample();
|
||||
example.createCriteria().andReportIdEqualTo(reportId);
|
||||
loadTestReportDetailMapper.deleteByExample(example);
|
||||
|
||||
loadTestReportMapper.deleteByPrimaryKey(reportId);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ organization_id_is_null=Organization ID cannot be null
|
|||
#api
|
||||
api_load_script_error=Load script error
|
||||
api_report_is_null="Report is null, can't update"
|
||||
api_test_environment_already_exists="Api test environment already exists"
|
||||
#test case
|
||||
test_case_node_level=level
|
||||
test_case_node_level_tip=The node tree maximum depth is
|
||||
|
@ -120,7 +121,8 @@ ldap_dn_is_null=LDAP binding DN is empty
|
|||
ldap_ou_is_null=LDAP parameter OU is empty
|
||||
ldap_password_is_null=LDAP password is empty
|
||||
ldap_connect_fail=Connection failed
|
||||
authentication_failed=User authentication failed
|
||||
authentication_failed=User authentication failed,wrong user name or password
|
||||
user_not_found_or_not_unique=User does not exist or is not unique
|
||||
find_more_user=Multiple users found
|
||||
ldap_authentication_not_enabled=LDAP authentication is not enabled
|
||||
login_fail_email_null=Login failed, user mailbox is empty
|
|
@ -66,6 +66,7 @@ organization_id_is_null=组织 ID 不能为空
|
|||
#api
|
||||
api_load_script_error=读取脚本失败
|
||||
api_report_is_null="测试报告是未生成,无法更新"
|
||||
api_test_environment_already_exists="已存在该名称的环境配置"
|
||||
#test case
|
||||
test_case_node_level=层
|
||||
test_case_node_level_tip=模块树最大深度为
|
||||
|
@ -120,10 +121,10 @@ ldap_dn_is_null=LDAP绑定DN为空
|
|||
ldap_ou_is_null=LDAP参数OU为空
|
||||
ldap_password_is_null=LDAP密码为空
|
||||
ldap_connect_fail=连接失败
|
||||
authentication_failed=用户认证失败
|
||||
authentication_failed=用户认证失败,用户名或密码错误
|
||||
user_not_found_or_not_unique=用户不存在或者不唯一
|
||||
find_more_user=查找到多个用户
|
||||
ldap_authentication_not_enabled=LDAP认证未启用
|
||||
|
||||
login_fail_email_null=登录失败,用户邮箱为空
|
||||
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ organization_id_is_null=組織 ID 不能為空
|
|||
#api
|
||||
api_load_script_error=讀取腳本失敗
|
||||
api_report_is_null="測試報告是未生成,無法更新"
|
||||
api_test_environment_already_exists="已存在該名稱的環境配置"
|
||||
#test case
|
||||
test_case_node_level=層
|
||||
test_case_node_level_tip=模塊樹最大深度為
|
||||
|
@ -120,7 +121,8 @@ ldap_dn_is_null=LDAP綁定DN為空
|
|||
ldap_ou_is_null=LDAP參數OU為空
|
||||
ldap_password_is_null=LDAP密碼為空
|
||||
ldap_connect_fail=連接失敗
|
||||
authentication_failed=用戶認證失敗
|
||||
authentication_failed=用戶認證失敗,用戶名或密碼錯誤
|
||||
user_not_found_or_not_unique=用戶不存在或者不唯一
|
||||
find_more_user=查找到多個用戶
|
||||
ldap_authentication_not_enabled=LDAP認證未啟用
|
||||
login_fail_email_null=登錄失敗,用戶郵箱為空
|
||||
|
|
|
@ -39,18 +39,24 @@
|
|||
<el-dropdown-item command="performance" :disabled="create || isReadOnly">
|
||||
{{$t('api_test.create_performance_test')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="export" :disabled="isDisabled || isReadOnly">
|
||||
<el-dropdown-item command="export" :disabled="isReadOnly || create">
|
||||
{{$t('api_test.export_config')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="import" :disabled="isReadOnly">
|
||||
导入
|
||||
<!-- {{$t('api_test.export_config')}}-->
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<api-import :project-id="test.projectId" ref="apiImport"/>
|
||||
|
||||
<ms-api-report-dialog :test-id="id" ref="reportDialog"/>
|
||||
|
||||
<ms-schedule-config :schedule="test.schedule" :save="saveCronExpression" @scheduleChange="saveSchedule" :check-open="checkScheduleEdit"/>
|
||||
</el-row>
|
||||
</el-header>
|
||||
<ms-api-scenario-config :is-read-only="isReadOnly" :scenarios="test.scenarioDefinition" ref="config"/>
|
||||
<ms-api-scenario-config :is-read-only="isReadOnly" :scenarios="test.scenarioDefinition" :project-id="test.projectId" ref="config"/>
|
||||
</el-container>
|
||||
</el-card>
|
||||
</div>
|
||||
|
@ -64,11 +70,12 @@
|
|||
import MsApiReportDialog from "./ApiReportDialog";
|
||||
import {checkoutTestManagerOrTestUser, downloadFile} from "../../../../common/js/utils";
|
||||
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
||||
import ApiImport from "./components/import/ApiImport";
|
||||
|
||||
export default {
|
||||
name: "MsApiTestConfig",
|
||||
|
||||
components: {MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
||||
components: {ApiImport, MsScheduleConfig, MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
||||
|
||||
props: ["id"],
|
||||
|
||||
|
@ -211,6 +218,9 @@
|
|||
case "export":
|
||||
downloadFile(this.test.name + ".json", this.test.export());
|
||||
break;
|
||||
case "import":
|
||||
this.$refs.apiImport.open();
|
||||
break;
|
||||
}
|
||||
},
|
||||
saveCronExpression(cronExpression) {
|
||||
|
@ -228,13 +238,13 @@
|
|||
url = '/api/schedule/update';
|
||||
}
|
||||
this.$post(url, param, response => {
|
||||
this.$success('保存成功');
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.getTest(this.test.id);
|
||||
});
|
||||
},
|
||||
checkScheduleEdit() {
|
||||
if (this.create) {
|
||||
this.$message('请先保存测试');
|
||||
this.$message(this.$t('api_test.environment.please_save_test'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-dialog :title="'环境配置'" :visible.sync="visible" class="environment-dialog">
|
||||
<el-dialog :title="$t('api_test.environment.environment_config')" :visible.sync="visible" class="environment-dialog" @close="close">
|
||||
<el-container v-loading="result.loading">
|
||||
<ms-aside-item :title="'环境列表'" :data="environments" :item-operators="environmentOperators" :add-fuc="addEnvironment"
|
||||
<ms-aside-item :title="$t('api_test.environment.environment_list')" :data="environments" :item-operators="environmentOperators" :add-fuc="addEnvironment"
|
||||
:delete-fuc="deleteEnvironment" @itemSelected="environmentSelected" ref="environmentItems"/>
|
||||
<environment-edit :environment="currentEnvironment" ref="environmentEdit"/>
|
||||
</el-container>
|
||||
|
@ -51,7 +51,7 @@
|
|||
},
|
||||
deleteEnvironment(environment) {
|
||||
this.result = this.$get('/api/environment/delete/' + environment.id, response => {
|
||||
this.$success('删除成功');
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.getEnvironments();
|
||||
});
|
||||
},
|
||||
|
@ -93,6 +93,9 @@
|
|||
},
|
||||
getDefaultEnvironment() {
|
||||
return {variables: [{}], headers: [{}], protocol: 'https', projectId: this.projectId};
|
||||
},
|
||||
close() {
|
||||
this.$emit('close');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="request-container">
|
||||
<draggable :list="requests" group="Request" class="request-draggable" ghost-class="request-ghost">
|
||||
<div class="request-item" v-for="(request, index) in requests" :key="index" @click="select(request)"
|
||||
<draggable :list="this.scenario.requests" group="Request" class="request-draggable" ghost-class="request-ghost">
|
||||
<div class="request-item" v-for="(request, index) in this.scenario.requests" :key="index" @click="select(request)"
|
||||
:class="{'selected': isSelected(request)}">
|
||||
<el-row type="flex">
|
||||
<div class="request-method">
|
||||
|
@ -40,7 +40,7 @@
|
|||
components: {draggable},
|
||||
|
||||
props: {
|
||||
requests: Array,
|
||||
scenario: Object,
|
||||
open: Function,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
|
@ -65,15 +65,15 @@
|
|||
methods: {
|
||||
createRequest: function () {
|
||||
let request = new Request();
|
||||
this.requests.push(request);
|
||||
this.scenario.requests.push(request);
|
||||
},
|
||||
copyRequest: function (index) {
|
||||
let request = this.requests[index];
|
||||
this.requests.push(new Request(request));
|
||||
let request = this.scenario.requests[index];
|
||||
this.scenario.requests.push(new Request(request));
|
||||
},
|
||||
deleteRequest: function (index) {
|
||||
this.requests.splice(index, 1);
|
||||
if (this.requests.length === 0) {
|
||||
this.scenario.requests.splice(index, 1);
|
||||
if (this.scenario.requests.length === 0) {
|
||||
this.createRequest();
|
||||
}
|
||||
},
|
||||
|
@ -88,13 +88,17 @@
|
|||
}
|
||||
},
|
||||
select: function (request) {
|
||||
request.environment = this.scenario.environment;
|
||||
if (!request.useEnvironment) {
|
||||
request.useEnvironment = false;
|
||||
}
|
||||
this.selected = request;
|
||||
this.open(request);
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.select(this.requests[0]);
|
||||
this.select(this.scenario.requests[0]);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,30 +1,47 @@
|
|||
<template>
|
||||
<el-form :model="request" :rules="rules" ref="request" label-width="100px">
|
||||
|
||||
<el-form-item :label="$t('api_test.request.name')" prop="name">
|
||||
<el-input :disabled="isReadOnly" v-model="request.name" maxlength="100" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('api_test.request.url')" prop="url">
|
||||
<el-form-item v-if="!request.useEnvironment" :label="$t('api_test.request.url')" prop="url" class="adjust-margin-bottom">
|
||||
<el-input :disabled="isReadOnly" v-model="request.url" maxlength="500"
|
||||
:placeholder="$t('api_test.request.url_description')" @change="urlChange" clearable>
|
||||
<el-select :disabled="isReadOnly" v-model="request.method" slot="prepend" class="request-method-select"
|
||||
@change="methodChange">
|
||||
<el-option label="GET" value="GET"/>
|
||||
<el-option label="POST" value="POST"/>
|
||||
<el-option label="PUT" value="PUT"/>
|
||||
<el-option label="PATCH" value="PATCH"/>
|
||||
<el-option label="DELETE" value="DELETE"/>
|
||||
<el-option label="OPTIONS" value="OPTIONS"/>
|
||||
<el-option label="HEAD" value="HEAD"/>
|
||||
<el-option label="CONNECT" value="CONNECT"/>
|
||||
</el-select>
|
||||
<template v-slot:prepend>
|
||||
<ApiRequestMethodSelect :is-read-only="isReadOnly" :request="request" @change="methodChange"/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="request.useEnvironment" :label="$t('api_test.request.path')" prop="path">
|
||||
<el-input :disabled="isReadOnly" v-model="request.path" maxlength="500"
|
||||
:placeholder="$t('api_test.request.path_description')" @change="pathChange" clearable>
|
||||
<template v-slot:prepend>
|
||||
<ApiRequestMethodSelect :is-read-only="isReadOnly" :request="request" @change="methodChange"/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="request.useEnvironment" :label="$t('api_test.request.address')" class="adjust-margin-bottom">
|
||||
<el-tag class="environment-display">
|
||||
<span class="environment-name">{{request.environment ? request.environment.name + ': ' : ''}}</span>
|
||||
<span class="environment-url">{{displayUrl}}</span>
|
||||
<span v-if="!displayUrl" class="environment-url-tip">{{$t('api_test.request.please_configure_environment_in_scenario')}}</span>
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-switch
|
||||
v-model="request.useEnvironment"
|
||||
:active-text="$t('api_test.request.refer_to_environment')" @change="useEnvironmentChange">
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.parameters"
|
||||
:description="$t('api_test.request.parameters_desc')" @change="parametersChange"/>
|
||||
:description="$t('api_test.request.parameters_desc')"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.headers"/>
|
||||
|
@ -48,10 +65,11 @@
|
|||
import MsApiAssertions from "./assertion/ApiAssertions";
|
||||
import {KeyValue, Request} from "../model/ScenarioModel";
|
||||
import MsApiExtract from "./extract/ApiExtract";
|
||||
import ApiRequestMethodSelect from "./collapse/ApiRequestMethodSelect";
|
||||
|
||||
export default {
|
||||
name: "MsApiRequestForm",
|
||||
components: {MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
|
||||
components: {ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
|
||||
props: {
|
||||
request: Request,
|
||||
isReadOnly: {
|
||||
|
@ -77,6 +95,9 @@
|
|||
url: [
|
||||
{max: 500, required: true, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'},
|
||||
{validator: validateURL, trigger: 'blur'}
|
||||
],
|
||||
path: [
|
||||
{max: 500, required: true, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -85,39 +106,43 @@
|
|||
methods: {
|
||||
urlChange() {
|
||||
if (!this.request.url) return;
|
||||
|
||||
let parameters = [];
|
||||
let url = this.getURL(this.addProtocol(this.request.url));
|
||||
if (url) {
|
||||
this.request.url = decodeURIComponent(url.origin + url.pathname);
|
||||
}
|
||||
},
|
||||
pathChange() {
|
||||
if (!this.request.path) return;
|
||||
if (!this.request.path.startsWith('/')) {
|
||||
this.request.path = '/' + this.request.path;
|
||||
}
|
||||
let url = this.getURL(this.displayUrl);
|
||||
this.request.path = decodeURIComponent(url.pathname);
|
||||
this.request.urlWirhEnv = decodeURIComponent(url.origin + url.pathname);
|
||||
},
|
||||
getURL(urlStr) {
|
||||
try {
|
||||
let url = new URL(this.addProtocol(this.request.url));
|
||||
let url = new URL(urlStr);
|
||||
url.searchParams.forEach((value, key) => {
|
||||
if (key && value) {
|
||||
parameters.push(new KeyValue(key, value));
|
||||
this.request.parameters.splice(0, 0, new KeyValue(key, value));
|
||||
}
|
||||
});
|
||||
// 添加一个空的,用于填写
|
||||
parameters.push(new KeyValue());
|
||||
this.request.parameters = parameters;
|
||||
this.request.url = this.getURL(url);
|
||||
return url;
|
||||
} catch (e) {
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000)
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000);
|
||||
}
|
||||
|
||||
},
|
||||
methodChange(value) {
|
||||
if (value === 'GET' && this.activeName === 'body') {
|
||||
this.activeName = 'parameters';
|
||||
}
|
||||
},
|
||||
parametersChange(parameters) {
|
||||
if (!this.request.url) return;
|
||||
let url = new URL(this.addProtocol(this.request.url));
|
||||
url.search = "";
|
||||
parameters.forEach(function (parameter) {
|
||||
if (parameter.name && parameter.value) {
|
||||
url.searchParams.append(parameter.name, parameter.value);
|
||||
useEnvironmentChange(value) {
|
||||
if (value && !this.request.environment) {
|
||||
this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000);
|
||||
this.request.useEnvironment = false;
|
||||
}
|
||||
})
|
||||
this.request.url = this.getURL(url);
|
||||
},
|
||||
addProtocol(url) {
|
||||
if (url) {
|
||||
|
@ -126,15 +151,15 @@
|
|||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
getURL(url) {
|
||||
return decodeURIComponent(url.origin + url.pathname) + "?" + url.searchParams.toString();
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isNotGet() {
|
||||
return this.request.method !== "GET";
|
||||
},
|
||||
displayUrl() {
|
||||
return this.request.environment ? this.request.environment.protocol + '://' + this.request.environment.socket + (this.request.path ? this.request.path : '') : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,4 +169,28 @@
|
|||
.request-method-select {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.environment-display {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.environment-name {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.adjust-margin-bottom {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.environment-url-tip {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<ms-api-request-config :is-read-only="isReadOnly" :requests="scenario.requests" :open="select"/>
|
||||
<ms-api-request-config :is-read-only="isReadOnly" :scenario="scenario" :open="select"/>
|
||||
</ms-api-collapse-item>
|
||||
</draggable>
|
||||
</ms-api-collapse>
|
||||
|
@ -35,8 +35,8 @@
|
|||
|
||||
<el-main class="scenario-main">
|
||||
<div class="scenario-form">
|
||||
<ms-api-scenario-form :is-read-only="isReadOnly" :scenario="selected" v-if="isScenario"/>
|
||||
<ms-api-request-form :is-read-only="isReadOnly" :request="selected" v-if="isRequest"/>
|
||||
<ms-api-scenario-form :is-read-only="isReadOnly" :scenario="selected" :project-id="projectId" v-if="isScenario"/>
|
||||
<ms-api-request-form :is-read-only="isReadOnly" :request="selected" :project-id="projectId" v-if="isRequest"/>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
@ -66,6 +66,7 @@
|
|||
|
||||
props: {
|
||||
scenarios: Array,
|
||||
projectId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
<template>
|
||||
<el-form :model="scenario" :rules="rules" ref="scenario" label-width="100px">
|
||||
<el-form :model="scenario" :rules="rules" ref="scenario" label-width="100px" v-loading="result.loading">
|
||||
<el-form-item :label="$t('api_test.scenario.name')" prop="name">
|
||||
<el-input :disabled="isReadOnly" v-model="scenario.name" maxlength="100" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('api_test.environment.environment')">
|
||||
<el-select :disabled="isReadOnly" v-model="scenario.environmentId" class="environment-select" @change="environmentChange" clearable>
|
||||
<el-option v-for="(environment, index) in environments" :key="index" :label="environment.name + ': ' + environment.protocol + '://' + environment.socket" :value="environment.id"/>
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item :label="$t('api_test.scenario.base_url')" prop="url">-->
|
||||
<!-- <el-input :placeholder="$t('api_test.scenario.base_url_description')" v-model="scenario.url" maxlength="100"/>-->
|
||||
<!-- <el-input :placeholder="$t('api_test.scenario.base_url_description')" v-model="scenario.url" maxlength="200"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
|
@ -16,28 +23,38 @@
|
|||
<ms-api-key-value :is-read-only="isReadOnly" :items="scenario.headers" :description="$t('api_test.scenario.kv_description')"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
|
||||
|
||||
</el-form>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiKeyValue from "./ApiKeyValue";
|
||||
import {Scenario} from "../model/ScenarioModel";
|
||||
import MsApiScenarioVariables from "./ApiScenarioVariables";
|
||||
import ApiEnvironmentConfig from "./ApiEnvironmentConfig";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioForm",
|
||||
components: {MsApiScenarioVariables, MsApiKeyValue},
|
||||
components: {ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue},
|
||||
props: {
|
||||
scenario: Scenario,
|
||||
projectId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getEnvironments();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
activeName: "parameters",
|
||||
environments: [],
|
||||
rules: {
|
||||
name: [
|
||||
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
||||
|
@ -47,10 +64,57 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getEnvironments() {
|
||||
if (this.projectId) {
|
||||
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
this.environments = response.data;
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === this.scenario.environmentId) {
|
||||
this.scenario.environment = this.environments[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
environmentChange(value) {
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === value) {
|
||||
this.scenario.environment = this.environments[i];
|
||||
this.scenario.requests.forEach(request => {
|
||||
request.environment = this.environments[i];
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!value) {
|
||||
this.scenario.environment = undefined;
|
||||
this.scenario.requests.forEach(request => {
|
||||
request.environment = undefined;
|
||||
});
|
||||
}
|
||||
},
|
||||
openEnvironmentConfig() {
|
||||
this.$refs.environmentConfig.open(this.projectId);
|
||||
},
|
||||
environmentConfigClose() {
|
||||
this.getEnvironments();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.environment-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.environment-button {
|
||||
margin-left: 20px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</el-col>
|
||||
<el-col class="assertion-btn">
|
||||
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add" v-else>Add</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</el-col>
|
||||
<el-col class="assertion-btn">
|
||||
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add" v-else>Add</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
:placeholder="$t('api_test.request.assertions.value')"/>
|
||||
</el-col>
|
||||
<el-col class="assertion-btn">
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" icon="el-icon-plus" plain @click="add"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add">Add</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="assertion-add">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="4">
|
||||
<el-select :disabled="isReadOnly" class="assertion-item" v-model="type" :placeholder="$t('api_test.request.assertions.select_type')"
|
||||
|
@ -14,8 +15,10 @@
|
|||
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX" :callback="after"/>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
|
||||
v-if="type === options.DURATION" :callback="after"/>
|
||||
<el-button v-if="!type" :disabled="true" type="primary" size="small">Add</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<ms-api-assertions-edit :is-read-only="isReadOnly" :assertions="assertions"/>
|
||||
</div>
|
||||
|
@ -62,4 +65,11 @@
|
|||
.assertion-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.assertion-add {
|
||||
padding: 10px;
|
||||
border: #DCDFE6 solid 1px;
|
||||
margin: 5px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<el-select :disabled="isReadOnly" v-model="request.method" class="request-method-select" @change="change">
|
||||
<el-option label="GET" value="GET"/>
|
||||
<el-option label="POST" value="POST"/>
|
||||
<el-option label="PUT" value="PUT"/>
|
||||
<el-option label="PATCH" value="PATCH"/>
|
||||
<el-option label="DELETE" value="DELETE"/>
|
||||
<el-option label="OPTIONS" value="OPTIONS"/>
|
||||
<el-option label="HEAD" value="HEAD"/>
|
||||
<el-option label="CONNECT" value="CONNECT"/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ApiRequestMethodSelect",
|
||||
props: ['isReadOnly', 'request'],
|
||||
methods: {
|
||||
change(value) {
|
||||
this.$emit('change', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -2,13 +2,13 @@
|
|||
<el-main v-loading="result.loading">
|
||||
<el-form :model="environment" :rules="rules" ref="from">
|
||||
|
||||
<span>环境名称</span>
|
||||
<span>{{$t('api_test.environment.name')}}</span>
|
||||
<el-form-item
|
||||
prop="name">
|
||||
<el-input v-model="environment.name" :placeholder="'请填写名称'" clearable></el-input>
|
||||
<el-input v-model="environment.name" :placeholder="this.$t('commons.input_name')" clearable></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<span>环境域名</span>
|
||||
<span>{{$t('api_test.environment.socket')}}</span>
|
||||
<el-form-item
|
||||
prop="socket">
|
||||
<el-input v-model="environment.socket" :placeholder="$t('api_test.request.url_description')" clearable>
|
||||
|
@ -21,14 +21,14 @@
|
|||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<span>全局变量</span>
|
||||
<span>{{$t('api_test.environment.globalVariable')}}</span>
|
||||
<ms-api-scenario-variables :items="environment.variables"/>
|
||||
|
||||
<span>请求头</span>
|
||||
<span>{{$t('api_test.request.headers')}}</span>
|
||||
<ms-api-key-value :items="environment.headers"/>
|
||||
|
||||
<div class="environment-footer">
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
<el-button type="primary" @click="save">{{this.$t('commons.save')}}</el-button>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
|
@ -53,7 +53,7 @@
|
|||
data() {
|
||||
let socketValidator = (rule, value, callback) => {
|
||||
if (!this.validateSocket(value)) {
|
||||
callback(new Error('格式错误'));
|
||||
callback(new Error(this.$t('commons.formatErr')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
@ -61,7 +61,7 @@
|
|||
return {
|
||||
result: {},
|
||||
rules: {
|
||||
name :[{required: true, message: '请填写名称', trigger: 'blur'}],
|
||||
name :[{required: true, message: this.$t('commons.input_name'), trigger: 'blur'}],
|
||||
socket :[{required: true, validator: socketValidator, trigger: 'blur'}],
|
||||
},
|
||||
}
|
||||
|
@ -84,7 +84,7 @@
|
|||
}
|
||||
this.result = this.$post(url, param, response => {
|
||||
this.environment.id = response.data;
|
||||
this.$success("保存成功");
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
});
|
||||
},
|
||||
buildParam() {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<div class="extract-description">
|
||||
{{$t('api_test.request.extract.description')}}
|
||||
</div>
|
||||
<div class="extract-add">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="2">
|
||||
<el-select :disabled="isReadOnly" class="extract-item" v-model="type" :placeholder="$t('api_test.request.extract.select_type')"
|
||||
|
@ -15,7 +16,10 @@
|
|||
<el-col :span="22">
|
||||
<ms-api-extract-common :is-read-only="isReadOnly" :extract-type="type" :list="list" v-if="type" :callback="after"/>
|
||||
</el-col>
|
||||
|
||||
<el-button v-if="!type" :disabled="true" type="primary" size="small">Add</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<ms-api-extract-edit :is-read-only="isReadOnly" :extract="extract"/>
|
||||
</div>
|
||||
|
@ -81,4 +85,11 @@
|
|||
.extract-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.extract-add {
|
||||
padding: 10px;
|
||||
border: #DCDFE6 solid 1px;
|
||||
margin: 5px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<el-col class="extract-btn">
|
||||
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove"
|
||||
v-if="edit"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add" v-else>Add</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
<template>
|
||||
<el-dialog :title="'接口测试导入'" :visible.sync="visible" class="api-import" v-loading="result.loading">
|
||||
|
||||
<div class="data-format">
|
||||
<div>数据格式</div>
|
||||
<el-radio-group v-model="selectedPlatformValue">
|
||||
<el-radio v-for="(item, index) in platforms" :key="index" :label="item.value">{{item.name}}</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="api-upload">
|
||||
<el-upload
|
||||
drag
|
||||
action=""
|
||||
:http-request="upload"
|
||||
:limit="1"
|
||||
:beforeUpload="uploadValidate"
|
||||
:show-file-list="false"
|
||||
:file-list="fileList"
|
||||
multiple>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">文件大小不超过 20 M</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<div class="format-tip">
|
||||
<div>
|
||||
<span>说明:{{selectedPlatform.tip}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>导出方法:{{selectedPlatform.exportTip}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
export default {
|
||||
name: "ApiImport",
|
||||
components: {MsDialogFooter},
|
||||
props: ['projectId'],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
platforms: [
|
||||
{
|
||||
name: 'Metersphere',
|
||||
value: 'Metersphere',
|
||||
tip: '支持 Metersphere json 格式',
|
||||
exportTip: '通过 Metersphere Api 测试页面或者浏览器插件导出 json 格式文件',
|
||||
suffixes: new Set(['json'])
|
||||
}
|
||||
],
|
||||
selectedPlatform: {},
|
||||
selectedPlatformValue: 'Metersphere',
|
||||
fileList: [],
|
||||
result: {},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.selectedPlatform = this.platforms[0];
|
||||
},
|
||||
watch: {
|
||||
selectedPlatformId() {
|
||||
for (let i in this.platforms) {
|
||||
if (this.platforms[i].id === this.selectedPlatformValue) {
|
||||
this.selectedPlatform = this.platforms[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.visible = true;
|
||||
},
|
||||
upload(file) {
|
||||
this.fileList.push(file.file);
|
||||
this.result = this.$fileUpload('/api/import/' + this.selectedPlatformValue + '/' + this.projectId, this.fileList, response => {
|
||||
let res = response.data;
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.visible = false;
|
||||
this.$router.push({path: '/api/test/edit', query: {id: res.id}});
|
||||
});
|
||||
this.fileList = [];
|
||||
},
|
||||
uploadValidate(file, fileList) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (!this.selectedPlatform.suffixes.has(suffix)) {
|
||||
this.$warning("格式错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.format-tip {
|
||||
background: #EDEDED;
|
||||
}
|
||||
|
||||
.api-upload {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-radio-group {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.data-format,.format-tip,.api-upload {
|
||||
border: solid #E1E1E1 1px;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.api-import >>> .el-dialog__body {
|
||||
padding: 15px 25px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -225,9 +225,15 @@ export class HTTPSamplerProxy extends DefaultTestElement {
|
|||
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName);
|
||||
this.request = request || {};
|
||||
|
||||
if (request.useEnvironment) {
|
||||
this.stringProp("HTTPSampler.domain", this.request.environment.domain);
|
||||
this.stringProp("HTTPSampler.protocol", this.request.environment.protocol);
|
||||
this.stringProp("HTTPSampler.path", this.request.path);
|
||||
} else {
|
||||
this.stringProp("HTTPSampler.domain", this.request.hostname);
|
||||
this.stringProp("HTTPSampler.protocol", this.request.protocol.split(":")[0]);
|
||||
this.stringProp("HTTPSampler.path", this.request.pathname);
|
||||
}
|
||||
this.stringProp("HTTPSampler.method", this.request.method);
|
||||
this.stringProp("HTTPSampler.contentEncoding", this.request.encoding, "UTF-8");
|
||||
if (!this.request.port) {
|
||||
|
|
|
@ -147,6 +147,7 @@ export class Scenario extends BaseConfig {
|
|||
this.variables = [];
|
||||
this.headers = [];
|
||||
this.requests = [];
|
||||
this.environmentId = undefined;
|
||||
|
||||
this.set(options);
|
||||
this.sets({variables: KeyValue, headers: KeyValue, requests: Request}, options);
|
||||
|
@ -164,25 +165,31 @@ export class Scenario extends BaseConfig {
|
|||
|
||||
isValid() {
|
||||
for (let i = 0; i < this.requests.length; i++) {
|
||||
if (this.requests[i].isValid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!this.requests[i].isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!this.name) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class Request extends BaseConfig {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
this.path = undefined;
|
||||
this.method = undefined;
|
||||
this.parameters = [];
|
||||
this.headers = [];
|
||||
this.body = undefined;
|
||||
this.assertions = undefined;
|
||||
this.extract = undefined;
|
||||
this.environment = undefined;
|
||||
this.useEnvironment = undefined;
|
||||
|
||||
this.set(options);
|
||||
this.sets({parameters: KeyValue, headers: KeyValue}, options);
|
||||
|
@ -198,7 +205,7 @@ export class Request extends BaseConfig {
|
|||
}
|
||||
|
||||
isValid() {
|
||||
return !!this.url && !!this.method
|
||||
return ((!this.useEnvironment && !!this.url) || (this.useEnvironment && !!this.path && this.environment)) && !!this.method
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,6 +399,9 @@ class JMXRequest {
|
|||
this.method = request.method;
|
||||
this.hostname = decodeURIComponent(url.hostname);
|
||||
this.pathname = decodeURIComponent(url.pathname);
|
||||
this.path = decodeURIComponent(request.path);
|
||||
this.useEnvironment = request.useEnvironment;
|
||||
this.environment = request.environment;
|
||||
this.port = url.port;
|
||||
this.protocol = url.protocol.split(":")[0];
|
||||
if (this.method.toUpperCase() !== "GET") {
|
||||
|
@ -463,6 +473,22 @@ class JMXGenerator {
|
|||
}
|
||||
|
||||
addScenarioVariables(threadGroup, scenario) {
|
||||
let scenarioVariableKeys = new Set();
|
||||
scenario.variables.forEach(item => {
|
||||
scenarioVariableKeys.add(item.name);
|
||||
});
|
||||
let environment = scenario.environment;
|
||||
if (environment) {
|
||||
let envVariables = environment.variables;
|
||||
if (!(envVariables instanceof Array)) {
|
||||
envVariables = JSON.parse(environment.variables);
|
||||
envVariables.forEach(item => {
|
||||
if (item.name && !scenarioVariableKeys.has(item.name)) {
|
||||
scenario.variables.push(new KeyValue(item.name, item.value));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
let args = this.filterKV(scenario.variables);
|
||||
if (args.length > 0) {
|
||||
let name = scenario.name + " Variables"
|
||||
|
@ -471,6 +497,22 @@ class JMXGenerator {
|
|||
}
|
||||
|
||||
addScenarioHeaders(threadGroup, scenario) {
|
||||
let scenarioHeaderKeys = new Set();
|
||||
scenario.headers.forEach(item => {
|
||||
scenarioHeaderKeys.add(item.name);
|
||||
});
|
||||
let environment = scenario.environment;
|
||||
if (environment) {
|
||||
let envHeaders = environment.headers;
|
||||
if (!(envHeaders instanceof Array)) {
|
||||
envHeaders = JSON.parse(environment.headers);
|
||||
envHeaders.forEach(item => {
|
||||
if (item.name && !scenarioHeaderKeys.has(item.name)) {
|
||||
scenario.headers.push(new KeyValue(item.name, item.value));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
let headers = this.filterKV(scenario.headers);
|
||||
if (headers.length > 0) {
|
||||
let name = scenario.name + " Headers"
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<crontab-result v-show="false" :ex="schedule.value" ref="crontabResult" @resultListChange="resultListChange"/>
|
||||
</div>
|
||||
<div>
|
||||
<span :class="{'disable-character': !schedule.enable}"> 下次执行时间:{{this.recentList.length > 0 ? this.recentList[0] : '未设置'}} </span>
|
||||
<span :class="{'disable-character': !schedule.enable}"> {{$t('schedule.next_execution_time')}}:{{this.recentList.length > 0 ? this.recentList[0] : $t('schedule.not_set')}} </span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<el-dialog width="30%" class="schedule-edit" :title="'编辑定时任务'" :visible.sync="dialogVisible" @close="close">
|
||||
<el-dialog width="30%" class="schedule-edit" :title="$t('schedule.edit_timer_task')" :visible.sync="dialogVisible" @close="close">
|
||||
<div id="app">
|
||||
<el-form :model="form" :rules="rules" ref="from">
|
||||
<el-form-item
|
||||
:placeholder="'请输入 Cron 表达式'"
|
||||
:placeholder="$t('schedule.please_input_cron_expression')"
|
||||
prop="cronValue">
|
||||
<el-input v-model="form.cronValue" placeholder class="inp"/>
|
||||
<el-button type="primary" @click="showCronDialog">生成 Cron</el-button>
|
||||
<el-button type="primary" @click="saveCron">保存</el-button>
|
||||
<el-button type="primary" @click="showCronDialog">{{$t('schedule.generate_expression')}}</el-button>
|
||||
<el-button type="primary" @click="saveCron">{{$t('commons.save')}}</el-button>
|
||||
</el-form-item>
|
||||
<crontab-result :ex="form.cronValue" ref="crontabResult" />
|
||||
</el-form>
|
||||
<el-dialog title="生成 cron" :visible.sync="showCron" :modal="false">
|
||||
<el-dialog :title="$t('schedule.generate_expression')" :visible.sync="showCron" :modal="false">
|
||||
<crontab @hide="showCron=false" @fill="crontabFill" :expression="schedule.value" ref="crontab"/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
@ -46,9 +46,9 @@
|
|||
const validateCron = (rule, cronValue, callback) => {
|
||||
let customValidate = this.customValidate(this.getIntervalTime());
|
||||
if (!cronValidate(cronValue)) {
|
||||
callback(new Error('Cron 表达式格式错误'));
|
||||
callback(new Error(this.$t('schedule.cron_expression_format_error')));
|
||||
} else if(!this.intervalShortValidate()) {
|
||||
callback(new Error('间隔时间请大于 5 分钟'));
|
||||
callback(new Error(this.$t('schedule.cron_expression_interval_short_error')));
|
||||
} else if (!customValidate.pass){
|
||||
callback(new Error(customValidate.info));
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="秒" v-if="shouldHide('second')">
|
||||
<el-tab-pane :label="$t('schedule.cron.seconds')" v-if="shouldHide('second')">
|
||||
<crontab-second
|
||||
@update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
|
@ -9,7 +9,7 @@
|
|||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="分钟" v-if="shouldHide('min')">
|
||||
<el-tab-pane :label="$t('schedule.cron.minutes')" v-if="shouldHide('min')">
|
||||
<crontab-min
|
||||
@update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
|
@ -18,7 +18,7 @@
|
|||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="小时" v-if="shouldHide('hour')">
|
||||
<el-tab-pane :label="$t('schedule.cron.hours')" v-if="shouldHide('hour')">
|
||||
<crontab-hour
|
||||
@update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
|
@ -27,7 +27,7 @@
|
|||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="日" v-if="shouldHide('day')">
|
||||
<el-tab-pane :label="$t('schedule.cron.day')" v-if="shouldHide('day')">
|
||||
<crontab-day
|
||||
@update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
|
@ -36,7 +36,7 @@
|
|||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="月" v-if="shouldHide('mouth')">
|
||||
<el-tab-pane :label="$t('schedule.cron.month')" v-if="shouldHide('mouth')">
|
||||
<crontab-mouth
|
||||
@update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
|
@ -45,7 +45,7 @@
|
|||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="周" v-if="shouldHide('week')">
|
||||
<el-tab-pane :label="$t('schedule.cron.weeks')" v-if="shouldHide('week')">
|
||||
<crontab-week
|
||||
@update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
|
@ -54,7 +54,7 @@
|
|||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="年" v-if="shouldHide('year')">
|
||||
<el-tab-pane :label="$t('schedule.cron.years')" v-if="shouldHide('year')">
|
||||
<crontab-year @update="updateContabValue"
|
||||
:check="checkNumber"
|
||||
:cron="contabValueObj"
|
||||
|
@ -64,11 +64,11 @@
|
|||
|
||||
<div class="popup-main">
|
||||
<div class="popup-result-container">
|
||||
<p class="title">时间表达式</p>
|
||||
<p class="title">{{$t('schedule.cron.time_expression')}}</p>
|
||||
<table>
|
||||
<thead>
|
||||
<th v-for="item of tabTitles" width="40" :key="item">{{item}}</th>
|
||||
<th>crontab完整表达式</th>
|
||||
<th>{{$t('schedule.cron.complete_expression')}}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<td>
|
||||
|
@ -101,9 +101,9 @@
|
|||
<crontab-result :ex="contabValueString" ref="crontabResult"/>
|
||||
|
||||
<div class="pop_btn">
|
||||
<el-button size="small" type="primary" @click="submitFill">确定</el-button>
|
||||
<el-button size="small" type="warning" @click="clearCron">重置</el-button>
|
||||
<el-button size="small" @click="hidePopup">取消</el-button>
|
||||
<el-button size="small" type="primary" @click="submitFill">{{$t('commons.confirm')}}</el-button>
|
||||
<el-button size="small" type="warning" @click="clearCron">{{$t('api_test.reset')}}</el-button>
|
||||
<el-button size="small" @click="hidePopup">{{$t('commons.cancel')}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -123,7 +123,14 @@
|
|||
name: "Crontab",
|
||||
data() {
|
||||
return {
|
||||
tabTitles: ["秒", "分钟", "小时", "日", "月", "周", "年"],
|
||||
tabTitles: [
|
||||
this.$t('schedule.cron.seconds'),
|
||||
this.$t('schedule.cron.minutes'),
|
||||
this.$t('schedule.cron.hours'),
|
||||
this.$t('schedule.cron.day'),
|
||||
this.$t('schedule.cron.month'),
|
||||
this.$t('schedule.cron.weeks'),
|
||||
this.$t('schedule.cron.years')],
|
||||
tabActive: 0,
|
||||
myindex: 0,
|
||||
contabValueObj: {
|
||||
|
@ -179,7 +186,7 @@
|
|||
"updateContabValue", name, value, from;
|
||||
this.contabValueObj[name] = value;
|
||||
if (from && from !== name) {
|
||||
console.log(`来自组件 ${from} 改变了 ${name} ${value}`);
|
||||
// console.log(`来自组件 ${from} 改变了 ${name} ${value}`);
|
||||
this.changeRadio(name, value);
|
||||
}
|
||||
},
|
||||
|
@ -310,7 +317,7 @@
|
|||
},
|
||||
clearCron() {
|
||||
// 还原选择项
|
||||
("准备还原");
|
||||
// ("准备还原");
|
||||
this.contabValueObj = {
|
||||
second: "*",
|
||||
min: "*",
|
||||
|
|
|
@ -2,49 +2,49 @@
|
|||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
日,允许的通配符[, - * / L M]
|
||||
{{$t('schedule.cron.day')}},{{$t('schedule.cron.day_allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
不指定
|
||||
{{$t('schedule.cron.not_specify')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
周期从
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='cycle01' :min="0" :max="31" /> -
|
||||
<el-input-number v-model='cycle02' :min="0" :max="31" /> 日
|
||||
<el-input-number v-model='cycle02' :min="0" :max="31" /> {{$t('schedule.cron.day')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="31" /> 号开始,每
|
||||
<el-input-number v-model='average02' :min="0" :max="31" /> 日执行一次
|
||||
{{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='average01' :min="0" :max="31" /> {{$t('schedule.cron.day_unit')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}}
|
||||
<el-input-number v-model='average02' :min="0" :max="31" /> {{$t('schedule.cron.day')}}{{$t('schedule.cron.execute_once')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="5">
|
||||
每月
|
||||
<el-input-number v-model='workday' :min="0" :max="31" /> 号最近的那个工作日
|
||||
{{$t('schedule.cron.every')}}{{$t('schedule.cron.month')}}
|
||||
<el-input-number v-model='workday' :min="0" :max="31" /> {{$t('schedule.cron.day_unit')}}{{$t('schedule.cron.last_working_day')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="6">
|
||||
本月最后一天
|
||||
{{$t('schedule.cron.last_working_day')}}{{$t('schedule.cron.last_day_of_the_month')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="7">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||
{{$t('schedule.cron.specify')}}
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
|
||||
<el-option v-for="item in 31" :key="item" :value="item">{{item}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
小时,允许的通配符[, - * /]
|
||||
{{$t('schedule.cron.hours')}},{{$t('schedule.cron.allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='cycle01' :min="0" :max="60" /> -
|
||||
<el-input-number v-model='cycle02' :min="0" :max="60" /> 小时
|
||||
</el-radio>
|
||||
|
@ -16,16 +16,16 @@
|
|||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="60" /> 小时开始,每
|
||||
<el-input-number v-model='average02' :min="0" :max="60" /> 小时执行一次
|
||||
{{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='average01' :min="0" :max="60" /> {{$t('schedule.cron.hours')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}}
|
||||
<el-input-number v-model='average02' :min="0" :max="60" /> {{$t('schedule.cron.hours')}}{{$t('schedule.cron.execute_once')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||
{{$t('schedule.cron.specify')}}
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
|
||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
|
|
@ -2,30 +2,30 @@
|
|||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
分钟,允许的通配符[, - * /]
|
||||
{{$t('schedule.cron.minutes')}},{{$t('schedule.cron.allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='cycle01' :min="0" :max="60" /> -
|
||||
<el-input-number v-model='cycle02' :min="0" :max="60" /> 分钟
|
||||
<el-input-number v-model='cycle02' :min="0" :max="60" /> {{$t('schedule.cron.minutes')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="60" /> 分钟开始,每
|
||||
<el-input-number v-model='average02' :min="0" :max="60" /> 分钟执行一次
|
||||
{{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='average01' :min="0" :max="60" /> {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}}
|
||||
<el-input-number v-model='average02' :min="0" :max="60" /> {{$t('schedule.cron.minutes')}}{{$t('schedule.cron.execute_once')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||
{{$t('schedule.cron.specify')}}
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
|
||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
|
|
@ -2,30 +2,30 @@
|
|||
<el-form size='small'>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
月,允许的通配符[, - * /]
|
||||
{{$t('schedule.cron.month')}},{{$t('schedule.cron.allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='cycle01' :min="1" :max="12" /> -
|
||||
<el-input-number v-model='cycle02' :min="1" :max="12" /> 月
|
||||
<el-input-number v-model='cycle02' :min="1" :max="12" /> {{$t('schedule.cron.month')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="1" :max="12" /> 月开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="12" /> 月月执行一次
|
||||
{{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='average01' :min="1" :max="12" /> {{$t('schedule.cron.month')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}}
|
||||
<el-input-number v-model='average02' :min="1" :max="12" /> {{$t('schedule.cron.month')}}{{$t('schedule.cron.execute_once')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||
{{$t('schedule.cron.specify')}}
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
|
||||
<el-option v-for="item in 12" :key="item" :value="item">{{item}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="popup-result">
|
||||
<p class="title">最近5次运行时间</p>
|
||||
<p class="title">{{$t('schedule.cron.recent_run_time')}}</p>
|
||||
<ul class="popup-result-scroll">
|
||||
<template>
|
||||
<li v-for='item in resultList' :key="item">{{item}}</li>
|
||||
|
@ -340,12 +340,12 @@ export default {
|
|||
}
|
||||
// 判断100年内的结果条数
|
||||
if (resultArr.length == 0) {
|
||||
this.resultList = ['没有达到条件的结果!'];
|
||||
this.resultList = [this.$t('schedule.cron.no_qualifying_results')];
|
||||
} else {
|
||||
this.resultList = resultArr;
|
||||
if (resultArr.length !== 5) {
|
||||
this.resultList.push('最近100年内只有上面' + resultArr.length + '条结果!')
|
||||
}
|
||||
// if (resultArr.length !== 5) {
|
||||
// this.resultList.push('最近100年内只有上面' + resultArr.length + '条结果!')
|
||||
// }
|
||||
}
|
||||
|
||||
this.$emit("resultListChange", this.resultList);
|
||||
|
|
|
@ -2,30 +2,30 @@
|
|||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
秒,允许的通配符[, - * /]
|
||||
{{$t('schedule.cron.seconds')}},{{$t('schedule.cron.allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='cycle01' :min="0" :max="60" /> -
|
||||
<el-input-number v-model='cycle02' :min="0" :max="60" /> 秒
|
||||
<el-input-number v-model='cycle02' :min="0" :max="60" /> {{$t('schedule.cron.seconds')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="60" /> 秒开始,每
|
||||
<el-input-number v-model='average02' :min="0" :max="60" /> 秒执行一次
|
||||
{{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='average01' :min="0" :max="60" /> {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}}
|
||||
<el-input-number v-model='average02' :min="0" :max="60" /> {{$t('schedule.cron.seconds')}}{{$t('schedule.cron.execute_once')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||
{{$t('schedule.cron.specify')}}
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
|
||||
<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
<el-form size='small'>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
周,允许的通配符[, - * / L #]
|
||||
{{$t('schedule.cron.weeks')}},{{$t('schedule.cron.weeks_allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
不指定
|
||||
{{$t('schedule.cron.not_specify')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
周期从星期
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}{{$t('schedule.cron.week')}}
|
||||
<el-input-number v-model='cycle01' :min="1" :max="7" /> -
|
||||
<el-input-number v-model='cycle02' :min="1" :max="7" />
|
||||
</el-radio>
|
||||
|
@ -22,23 +22,23 @@
|
|||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
第
|
||||
<el-input-number v-model='average01' :min="1" :max="4" /> 周的星期
|
||||
{{$t('schedule.cron.num')}}
|
||||
<el-input-number v-model='average01' :min="1" :max="4" /> {{$t('schedule.cron.week_of_weeks')}}
|
||||
<el-input-number v-model='average02' :min="1" :max="7" />
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="5">
|
||||
本月最后一个星期
|
||||
{{$t('schedule.cron.last_week_of_the_month')}}
|
||||
<el-input-number v-model='weekday' :min="1" :max="7" />
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="6">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
|
||||
{{$t('schedule.cron.specify')}}
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple style="width:100%">
|
||||
<el-option v-for="(item,index) of weekList" :key="index" :value="index+1">{{item}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
@ -58,7 +58,15 @@ export default {
|
|||
average01: 1,
|
||||
average02: 1,
|
||||
checkboxList: [],
|
||||
weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
weekList: [
|
||||
this.$t('commons.weeks_1'),
|
||||
this.$t('commons.weeks_2'),
|
||||
this.$t('commons.weeks_3'),
|
||||
this.$t('commons.weeks_4'),
|
||||
this.$t('commons.weeks_5'),
|
||||
this.$t('commons.weeks_6'),
|
||||
this.$t('commons.weeks_0'),
|
||||
],
|
||||
checkNum: this.$options.propsData.check
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio :label="1" v-model='radioValue'>
|
||||
不填,允许的通配符[, - * /]
|
||||
{{$t('schedule.cron.not_fill')}},{{$t('schedule.cron.allowed_wildcards')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="2" v-model='radioValue'>
|
||||
每年
|
||||
{{$t('schedule.cron.every')}}{{$t('schedule.cron.years')}}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="3" v-model='radioValue'>
|
||||
周期从
|
||||
{{$t('schedule.cron.period')}} {{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='cycle01' :min='fullYear' /> -
|
||||
<el-input-number v-model='cycle02' :min='fullYear' />
|
||||
</el-radio>
|
||||
|
@ -22,9 +22,9 @@
|
|||
|
||||
<el-form-item>
|
||||
<el-radio :label="4" v-model='radioValue'>
|
||||
从
|
||||
<el-input-number v-model='average01' :min='fullYear' /> 年开始,每
|
||||
<el-input-number v-model='average02' :min='fullYear' /> 年执行一次
|
||||
{{$t('schedule.cron.from')}}
|
||||
<el-input-number v-model='average01' :min='fullYear' /> {{$t('schedule.cron.years')}}{{$t('schedule.cron.start')}},{{$t('schedule.cron.every')}}
|
||||
<el-input-number v-model='average02' :min='fullYear' /> {{$t('schedule.cron.years')}}{{$t('schedule.cron.execute_once')}}
|
||||
</el-radio>
|
||||
|
||||
</el-form-item>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<el-form-item>
|
||||
<el-radio :label="5" v-model='radioValue'>
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple>
|
||||
<el-select clearable v-model="checkboxList" :placeholder="$t('schedule.cron.multi_select')" multiple>
|
||||
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
|
|
|
@ -139,7 +139,11 @@
|
|||
}
|
||||
},
|
||||
initWebSocket() {
|
||||
const uri = "ws://" + window.location.host + "/performance/report/" + this.reportId;
|
||||
let protocol = "ws://";
|
||||
if (window.location.protocol === 'https:') {
|
||||
protocol = "wss://";
|
||||
}
|
||||
const uri = protocol + window.location.host + "/performance/report/" + this.reportId;
|
||||
this.websocket = new WebSocket(uri);
|
||||
this.websocket.onmessage = this.onMessage;
|
||||
this.websocket.onopen = this.onOpen;
|
||||
|
|
|
@ -268,7 +268,7 @@
|
|||
url = '/performance/schedule/update';
|
||||
}
|
||||
this.$post(url, param, response => {
|
||||
this.$success('保存成功');
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.getTest(this.testPlan.id);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
<el-form-item :label="$t('ldap.filter')" prop="filter">
|
||||
<el-input v-model="form.filter" :placeholder="$t('ldap.input_filter_placeholder')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ldap.mapping')" prop="mapping">
|
||||
<el-input v-model="form.mapping" :placeholder="$t('ldap.input_mapping')"></el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item :label="$t('ldap.mapping')" prop="mapping">-->
|
||||
<!-- <el-input v-model="form.mapping" :placeholder="$t('ldap.input_mapping')"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item :label="$t('ldap.open')" prop="open">
|
||||
<el-checkbox v-model="form.open"></el-checkbox>
|
||||
</el-form-item>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<div>
|
||||
<el-button type="primary" size="small" :disabled="!show" @click="testConnection">{{$t('ldap.test_connect')}}
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" :disabled="!show" @click="testLogin">{{$t('ldap.test_login')}}
|
||||
<el-button type="primary" size="small" :disabled="!showLogin || !show" @click="testLogin">{{$t('ldap.test_login')}}
|
||||
</el-button>
|
||||
<el-button v-if="showEdit" size="small" @click="edit">{{$t('ldap.edit')}}</el-button>
|
||||
<el-button type="success" v-if="showSave" size="small" @click="save('form')">{{$t('commons.save')}}</el-button>
|
||||
|
@ -75,6 +75,7 @@
|
|||
showEdit: true,
|
||||
showSave: false,
|
||||
showCancel: false,
|
||||
showLogin: false,
|
||||
loginVisible: false,
|
||||
rules: {
|
||||
url: {required: true, message: this.$t('ldap.input_url'), trigger: ['change', 'blur']},
|
||||
|
@ -120,6 +121,9 @@
|
|||
}
|
||||
this.result = this.$post("/ldap/test/connect", this.form, response => {
|
||||
this.$success(this.$t('commons.connection_successful'));
|
||||
this.showLogin = true;
|
||||
}, () => {
|
||||
this.showLogin = false;
|
||||
})
|
||||
},
|
||||
testLogin() {
|
||||
|
@ -172,6 +176,7 @@
|
|||
this.showEdit = true;
|
||||
this.showSave = false;
|
||||
this.showCancel = false;
|
||||
this.showLogin = false;
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.init();
|
||||
});
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
:limit="1"
|
||||
action=""
|
||||
:on-exceed="handleExceed"
|
||||
:beforeUpload="UploadValidate"
|
||||
:beforeUpload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:http-request="upload"
|
||||
|
@ -66,8 +66,8 @@
|
|||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
UploadValidate(file) {
|
||||
var suffix =file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
uploadValidate(file) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (suffix != 'xls' && suffix != 'xlsx') {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_format'));
|
||||
return false;
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
param.testCaseIds = [...this.selectIds];
|
||||
this.$post('/test/plan/relevance' , param, () => {
|
||||
this.selectIds.clear();
|
||||
this.$success("保存成功");
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.dialogFormVisible = false;
|
||||
this.$emit('refresh');
|
||||
});
|
||||
|
|
|
@ -101,6 +101,8 @@ export default {
|
|||
'delete_confirm': 'Please enter the following to confirm deletion:',
|
||||
'login_username': 'ID or email',
|
||||
'input_login_username': 'Please input the user ID or email',
|
||||
'input_name': 'Please enter name',
|
||||
'formatErr': 'Format Error'
|
||||
},
|
||||
workspace: {
|
||||
'create': 'Create Workspace',
|
||||
|
@ -294,6 +296,15 @@ export default {
|
|||
value: "Value",
|
||||
create_performance_test: "Create Performance Test",
|
||||
export_config: "Export Configuration",
|
||||
environment: {
|
||||
name: "Environment Name",
|
||||
socket: "Socket",
|
||||
globalVariable: "Global Variable",
|
||||
environment_list: "Environment List",
|
||||
environment_config: "Environment Config",
|
||||
environment: "Environment",
|
||||
please_save_test: "Please Save Test First",
|
||||
},
|
||||
scenario: {
|
||||
config: "Scenario Config",
|
||||
input_name: "Please enter the scenario name",
|
||||
|
@ -314,7 +325,13 @@ export default {
|
|||
name: "Name",
|
||||
method: "Method",
|
||||
url: "URL",
|
||||
path: "Path",
|
||||
address: "Address",
|
||||
refer_to_environment: "Use Environment",
|
||||
please_configure_environment_in_scenario: "Please Configure Environment In The Scenario",
|
||||
please_add_environment_to_scenario: "Please Add The Environment Configuration To The Scenario First",
|
||||
url_description: "etc: https://fit2cloud.com",
|
||||
path_description: "etc:/login",
|
||||
parameters: "Query parameters",
|
||||
parameters_desc: "Parameters will be appended to the URL e.g. https://fit2cloud.com?Name=Value&Name2=Value2",
|
||||
headers: "Headers",
|
||||
|
@ -591,5 +608,46 @@ export default {
|
|||
'dn_cannot_be_empty': 'LDAP DN cannot be empty',
|
||||
'ou_cannot_be_empty': 'LDAP OU cannot be empty',
|
||||
'password_cannot_be_empty': 'LDAP password cannot be empty',
|
||||
},
|
||||
schedule: {
|
||||
not_set: "Not Set",
|
||||
next_execution_time: "Next Execution Time",
|
||||
edit_timer_task: "Edit Timer Task",
|
||||
please_input_cron_expression: "Please Input Cron Expression",
|
||||
generate_expression: "Generate Expression",
|
||||
cron_expression_format_error: "Cron Expression Format Error",
|
||||
cron_expression_interval_short_error: "Interval Time Should Longer than 5 Minutes",
|
||||
cron: {
|
||||
seconds: "Seconds",
|
||||
minutes: "Minutes",
|
||||
hours: "Hours",
|
||||
day: "Day",
|
||||
month: "Month",
|
||||
weeks: "Weeks",
|
||||
years: "Years",
|
||||
week: "Week",
|
||||
time_expression: "Time Expression",
|
||||
complete_expression: "Complete Expression",
|
||||
allowed_wildcards: "Allowed Wildcards[, - * /]",
|
||||
day_allowed_wildcards: "Allowed Wildcards[, - * / L M]",
|
||||
weeks_allowed_wildcards: "Allowed Wildcards[, - * / L M]",
|
||||
not_specify: "Not Specify",
|
||||
specify: "Specify",
|
||||
period: "Period",
|
||||
from: "From",
|
||||
every: "Every",
|
||||
day_unit: "Day Unit",
|
||||
start: "Start",
|
||||
execute_once: "Execute Once",
|
||||
last_working_day: "The Last Working Day",
|
||||
last_day_of_the_month: "The Last Day Of The Month",
|
||||
multi_select: "Multi Select",
|
||||
num: "Number",
|
||||
week_of_weeks: "Week Of Weeks",
|
||||
last_week_of_the_month: "The Last Week Of The Month",
|
||||
not_fill: "Not Fill",
|
||||
recent_run_time: "Recent 5th Runing Time",
|
||||
no_qualifying_results: "No Qualifying Results",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -101,6 +101,8 @@ export default {
|
|||
'delete_confirm': '请输入以下内容,确认删除:',
|
||||
'login_username': 'ID 或 邮箱',
|
||||
'input_login_username': '请输入用户 ID 或 邮箱',
|
||||
'input_name': '请输入名称',
|
||||
'formatErr': '格式错误'
|
||||
},
|
||||
workspace: {
|
||||
'create': '创建工作空间',
|
||||
|
@ -292,6 +294,15 @@ export default {
|
|||
value: "值",
|
||||
create_performance_test: "创建性能测试",
|
||||
export_config: "导出配置",
|
||||
environment: {
|
||||
name: "环境名称",
|
||||
socket: "环境域名",
|
||||
globalVariable: "全局变量",
|
||||
environment_list: "环境列表",
|
||||
environment_config: "环境配置",
|
||||
environment: "环境",
|
||||
please_save_test: "请先保存测试",
|
||||
},
|
||||
scenario: {
|
||||
config: "场景配置",
|
||||
input_name: "请输入场景名称",
|
||||
|
@ -311,7 +322,13 @@ export default {
|
|||
name: "请求名称",
|
||||
method: "请求方法",
|
||||
url: "请求URL",
|
||||
url_description: "例如: https://fit2cloud.com",
|
||||
path: "请求路径",
|
||||
address: "请求地址",
|
||||
refer_to_environment: "引用环境",
|
||||
please_configure_environment_in_scenario: "请在场景中配置环境",
|
||||
please_add_environment_to_scenario: "请先在场景中添加环境配置",
|
||||
url_description: "例如:https://fit2cloud.com",
|
||||
path_description: "例如:/login",
|
||||
url_invalid: "URL无效",
|
||||
parameters: "请求参数",
|
||||
parameters_desc: "参数追加到URL,例如https://fit2cloud.com/entries?key1=Value1&Key2=Value2",
|
||||
|
@ -588,5 +605,46 @@ export default {
|
|||
'dn_cannot_be_empty': 'LDAP DN不能为空',
|
||||
'ou_cannot_be_empty': 'LDAP OU不能为空',
|
||||
'password_cannot_be_empty': 'LDAP 密码不能为空',
|
||||
},
|
||||
schedule: {
|
||||
not_set: "未设置",
|
||||
next_execution_time: "下次执行时间",
|
||||
edit_timer_task: "编辑定时任务",
|
||||
please_input_cron_expression: "请输入 Cron 表达式",
|
||||
generate_expression: "生成表达式",
|
||||
cron_expression_format_error: "Cron 表达式格式错误",
|
||||
cron_expression_interval_short_error: "间隔时间请大于 5 分钟",
|
||||
cron: {
|
||||
seconds: "秒",
|
||||
minutes: "分钟",
|
||||
hours: "小时",
|
||||
day: "日",
|
||||
month: "月",
|
||||
weeks: "周",
|
||||
years: "年",
|
||||
week: "星期",
|
||||
time_expression: "时间表达式",
|
||||
complete_expression: "完整表达式",
|
||||
allowed_wildcards: "允许的通配符[, - * /]",
|
||||
day_allowed_wildcards: "允许的通配符[, - * / L M]",
|
||||
weeks_allowed_wildcards: "允许的通配符[, - * / L M]",
|
||||
not_specify: "不指定",
|
||||
specify: "指定",
|
||||
period: "周期",
|
||||
from: "从",
|
||||
every: "每",
|
||||
day_unit: "号",
|
||||
start: "开始",
|
||||
execute_once: "执行一次",
|
||||
last_working_day: "最近的那个工作日",
|
||||
last_day_of_the_month: "本月最后一天",
|
||||
multi_select: "可多选",
|
||||
num: "第",
|
||||
week_of_weeks: "周的星期",
|
||||
last_week_of_the_month: "本月最后一个星期",
|
||||
not_fill: "不填",
|
||||
recent_run_time: "最近5次运行时间",
|
||||
no_qualifying_results: "没有达到条件的结果",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -99,6 +99,8 @@ export default {
|
|||
'not_performed_yet': '尚未執行',
|
||||
'incorrect_input': '輸入內容不正確',
|
||||
'delete_confirm': '請輸入以下內容,確認刪除:',
|
||||
'input_name': '請輸入名稱',
|
||||
'formatErr': '格式錯誤'
|
||||
},
|
||||
workspace: {
|
||||
'create': '創建工作空間',
|
||||
|
@ -291,6 +293,15 @@ export default {
|
|||
value: "值",
|
||||
create_performance_test: "創建性能測試",
|
||||
export_config: "匯出配寘",
|
||||
environment: {
|
||||
name: "環境名稱",
|
||||
socket: "環境域名",
|
||||
globalVariable: "全局變量",
|
||||
environment_list: "環境列表",
|
||||
environment_config: "環境配置",
|
||||
environment: "環境",
|
||||
please_save_test: "請先保存測試",
|
||||
},
|
||||
scenario: {
|
||||
creator: "創建人",
|
||||
config: "場景配寘",
|
||||
|
@ -311,7 +322,13 @@ export default {
|
|||
name: "請求名稱",
|
||||
method: "請求方法",
|
||||
url: "請求URL",
|
||||
path: "請求路徑",
|
||||
address: "請求地址",
|
||||
refer_to_environment: "引用環境",
|
||||
please_configure_environment_in_scenario: "請在場景中配置環境",
|
||||
please_add_environment_to_scenario: "請先在場景中添加環境配置",
|
||||
url_description: "例如:https://fit2cloud.com",
|
||||
path_description: "例如:/login",
|
||||
url_invalid: "URL無效",
|
||||
parameters: "請求參數",
|
||||
parameters_desc: "參數追加到URL,例如https://fit2cloud.com/entries?key1=Value1&Key2=Value2",
|
||||
|
@ -588,5 +605,46 @@ export default {
|
|||
'dn_cannot_be_empty': 'LDAP DN不能為空',
|
||||
'ou_cannot_be_empty': 'LDAP OU不能為空',
|
||||
'password_cannot_be_empty': 'LDAP 密碼不能為空',
|
||||
},
|
||||
schedule: {
|
||||
not_set: "未設置",
|
||||
next_execution_time: "下次執行時間",
|
||||
edit_timer_task: "編輯定時任務",
|
||||
please_input_cron_expression: "請輸入 Cron 表達式",
|
||||
generate_expression: "生成表達式",
|
||||
cron_expression_format_error: "Cron 表達式格式錯誤",
|
||||
cron_expression_interval_short_error: "間隔時間請大於 5 分鐘",
|
||||
cron: {
|
||||
seconds: "秒",
|
||||
minutes: "分鐘",
|
||||
hours: "小時",
|
||||
day: "日",
|
||||
month: "月",
|
||||
weeks: "周",
|
||||
years: "年",
|
||||
week: "星期",
|
||||
time_expression: "時間表達式",
|
||||
complete_expression: "完整表達式",
|
||||
allowed_wildcards: "允許的通配符[, - * /]",
|
||||
day_allowed_wildcards: "允許的通配符[, - * / L M]",
|
||||
weeks_allowed_wildcards: "允許的通配符[, - * / L M]",
|
||||
not_specify: "不指定",
|
||||
specify: "指定",
|
||||
period: "周期",
|
||||
from: "從",
|
||||
every: "每",
|
||||
day_unit: "號",
|
||||
start: "開始",
|
||||
execute_once: "執行壹次",
|
||||
last_working_day: "最近的那個工作日",
|
||||
last_day_of_the_month: "本月最後壹天",
|
||||
multi_select: "可多選",
|
||||
num: "第",
|
||||
week_of_weeks: "周的星期",
|
||||
last_week_of_the_month: "本月最後壹個星期",
|
||||
not_fill: "不填",
|
||||
recent_run_time: "最近5次運行時間",
|
||||
no_qualifying_results: "沒有達到條件的結果",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue