Merge remote-tracking branch 'origin/master'

# Conflicts:
#	frontend/src/business/components/api/test/components/ApiScenarioConfig.vue
#	frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue
This commit is contained in:
q4speed 2020-09-07 13:29:37 +08:00
commit 7f296f453c
83 changed files with 3125 additions and 1241 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ target
.settings
.project
.classpath
.jython_cache

View File

@ -20,7 +20,7 @@ MeterSphere 是一站式的开源企业级持续测试平台,涵盖测试跟
## UI 展示
![UI](https://metersphere.io/images/screenshot/ss07.png)
![UI](https://metersphere.io/images/screenshot/ss01.png)
## 在线体验
- 环境地址https://demo.metersphere.com/
@ -49,6 +49,10 @@ curl -sSL https://github.com/metersphere/metersphere/releases/latest/download/qu
- [完整文档](https://metersphere.io/docs/)
- [演示视频](http://video.fit2cloud.com/%E3%80%90%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91%E3%80%91202006%20MeterSphere%20v1.0%20%E5%8A%9F%E8%83%BD%E6%BC%94%E7%A4%BA.mp4)
## MeterSphere 企业版
[申请企业版使用](https://jinshuju.net/f/CzzAOe)
> 注: 企业版支持离线安装,申请通过后会提供高速下载链接
## 相关工具
- [Jenkins 插件](https://github.com/metersphere/jenkins-plugin)

View File

@ -153,6 +153,12 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_functions</artifactId>
@ -207,13 +213,7 @@
<dependency>
<groupId>com.fit2cloud</groupId>
<artifactId>quartz-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
</exclusions>
<version>0.0.4</version>
<version>0.0.7</version>
</dependency>
<dependency>

View File

@ -12,6 +12,7 @@ public class KeyValue {
private String type;
private List<BodyFile> files;
private String description;
private boolean enable;
public KeyValue() {
}

View File

@ -0,0 +1,10 @@
package io.metersphere.api.dto.scenario.processor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class JSR223PostProcessor extends JSR223Processor {
}

View File

@ -0,0 +1,10 @@
package io.metersphere.api.dto.scenario.processor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class JSR223PreProcessor extends JSR223Processor {
}

View File

@ -0,0 +1,9 @@
package io.metersphere.api.dto.scenario.processor;
import lombok.Data;
@Data
public class JSR223Processor {
private String script;
private String language;
}

View File

@ -8,6 +8,8 @@ import io.metersphere.api.dto.scenario.assertions.Assertions;
import io.metersphere.api.dto.scenario.extract.Extract;
import io.metersphere.api.dto.scenario.processor.BeanShellPostProcessor;
import io.metersphere.api.dto.scenario.processor.BeanShellPreProcessor;
import io.metersphere.api.dto.scenario.processor.JSR223PostProcessor;
import io.metersphere.api.dto.scenario.processor.JSR223PreProcessor;
import io.metersphere.api.dto.scenario.request.dubbo.ConfigCenter;
import io.metersphere.api.dto.scenario.request.dubbo.ConsumerAndService;
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
@ -53,4 +55,8 @@ public class DubboRequest implements Request {
private BeanShellPostProcessor beanShellPostProcessor;
@JSONField(ordinal = 14)
private Boolean enable;
@JSONField(ordinal = 15)
private JSR223PreProcessor jsr223PreProcessor;
@JSONField(ordinal = 16)
private JSR223PostProcessor jsr223PostProcessor;
}

View File

@ -8,6 +8,8 @@ import io.metersphere.api.dto.scenario.assertions.Assertions;
import io.metersphere.api.dto.scenario.extract.Extract;
import io.metersphere.api.dto.scenario.processor.BeanShellPostProcessor;
import io.metersphere.api.dto.scenario.processor.BeanShellPreProcessor;
import io.metersphere.api.dto.scenario.processor.JSR223PostProcessor;
import io.metersphere.api.dto.scenario.processor.JSR223PreProcessor;
import lombok.Data;
import java.util.List;
@ -49,4 +51,10 @@ public class HttpRequest implements Request {
private Long connectTimeout;
@JSONField(ordinal = 15)
private Long responseTimeout;
@JSONField(ordinal = 16)
private Boolean followRedirects;
@JSONField(ordinal = 17)
private JSR223PreProcessor jsr223PreProcessor;
@JSONField(ordinal = 18)
private JSR223PostProcessor jsr223PostProcessor;
}

View File

@ -11,6 +11,8 @@ import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.backend.BackendListener;
import org.apache.jorphan.collections.HashTree;
import org.python.core.Options;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -30,6 +32,12 @@ public class JMeterService {
String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties";
JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES);
JMeterUtils.setJMeterHome(JMETER_HOME);
JMeterUtils.setLocale(LocaleContextHolder.getLocale());
//解决无法加载 PyScriptEngineFactory
Options.importSite = false;
try {
Object scriptWrapper = SaveService.loadElement(is);
HashTree testPlan = getHashTree(scriptWrapper);

View File

@ -157,7 +157,7 @@ public class JmeterDocumentParser {
u += k + "=" + ScriptEngineUtils.calculate(v);
return u;
});
ele.setTextContent(url + params);
ele.setTextContent(url + ((params != null && !params.equals("?")) ? params : ""));
break;
case "Argument.value":
String textContent = ele.getTextContent();

View File

@ -1,10 +1,11 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@ -15,5 +16,6 @@ public class ApiTestEnvironmentWithBLOBs extends ApiTestEnvironment implements S
private String customData;
private String hosts;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
public class Notice implements Serializable {
private String event;
private String testId;
private String name;
private String email;
private String enable;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,550 @@
package io.metersphere.base.domain;
import java.util.ArrayList;
import java.util.List;
public class NoticeExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public NoticeExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andEventIsNull() {
addCriterion("EVENT is null");
return (Criteria) this;
}
public Criteria andEventIsNotNull() {
addCriterion("EVENT is not null");
return (Criteria) this;
}
public Criteria andEventEqualTo(String value) {
addCriterion("EVENT =", value, "event");
return (Criteria) this;
}
public Criteria andEventNotEqualTo(String value) {
addCriterion("EVENT <>", value, "event");
return (Criteria) this;
}
public Criteria andEventGreaterThan(String value) {
addCriterion("EVENT >", value, "event");
return (Criteria) this;
}
public Criteria andEventGreaterThanOrEqualTo(String value) {
addCriterion("EVENT >=", value, "event");
return (Criteria) this;
}
public Criteria andEventLessThan(String value) {
addCriterion("EVENT <", value, "event");
return (Criteria) this;
}
public Criteria andEventLessThanOrEqualTo(String value) {
addCriterion("EVENT <=", value, "event");
return (Criteria) this;
}
public Criteria andEventLike(String value) {
addCriterion("EVENT like", value, "event");
return (Criteria) this;
}
public Criteria andEventNotLike(String value) {
addCriterion("EVENT not like", value, "event");
return (Criteria) this;
}
public Criteria andEventIn(List<String> values) {
addCriterion("EVENT in", values, "event");
return (Criteria) this;
}
public Criteria andEventNotIn(List<String> values) {
addCriterion("EVENT not in", values, "event");
return (Criteria) this;
}
public Criteria andEventBetween(String value1, String value2) {
addCriterion("EVENT between", value1, value2, "event");
return (Criteria) this;
}
public Criteria andEventNotBetween(String value1, String value2) {
addCriterion("EVENT not between", value1, value2, "event");
return (Criteria) this;
}
public Criteria andTestIdIsNull() {
addCriterion("TEST_ID is null");
return (Criteria) this;
}
public Criteria andTestIdIsNotNull() {
addCriterion("TEST_ID is not null");
return (Criteria) this;
}
public Criteria andTestIdEqualTo(String value) {
addCriterion("TEST_ID =", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdNotEqualTo(String value) {
addCriterion("TEST_ID <>", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdGreaterThan(String value) {
addCriterion("TEST_ID >", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdGreaterThanOrEqualTo(String value) {
addCriterion("TEST_ID >=", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdLessThan(String value) {
addCriterion("TEST_ID <", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdLessThanOrEqualTo(String value) {
addCriterion("TEST_ID <=", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdLike(String value) {
addCriterion("TEST_ID like", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdNotLike(String value) {
addCriterion("TEST_ID not like", value, "testId");
return (Criteria) this;
}
public Criteria andTestIdIn(List<String> values) {
addCriterion("TEST_ID in", values, "testId");
return (Criteria) this;
}
public Criteria andTestIdNotIn(List<String> values) {
addCriterion("TEST_ID not in", values, "testId");
return (Criteria) this;
}
public Criteria andTestIdBetween(String value1, String value2) {
addCriterion("TEST_ID between", value1, value2, "testId");
return (Criteria) this;
}
public Criteria andTestIdNotBetween(String value1, String value2) {
addCriterion("TEST_ID not between", value1, value2, "testId");
return (Criteria) this;
}
public Criteria andNameIsNull() {
addCriterion("`NAME` is null");
return (Criteria) this;
}
public Criteria andNameIsNotNull() {
addCriterion("`NAME` is not null");
return (Criteria) this;
}
public Criteria andNameEqualTo(String value) {
addCriterion("`NAME` =", value, "name");
return (Criteria) this;
}
public Criteria andNameNotEqualTo(String value) {
addCriterion("`NAME` <>", value, "name");
return (Criteria) this;
}
public Criteria andNameGreaterThan(String value) {
addCriterion("`NAME` >", value, "name");
return (Criteria) this;
}
public Criteria andNameGreaterThanOrEqualTo(String value) {
addCriterion("`NAME` >=", value, "name");
return (Criteria) this;
}
public Criteria andNameLessThan(String value) {
addCriterion("`NAME` <", value, "name");
return (Criteria) this;
}
public Criteria andNameLessThanOrEqualTo(String value) {
addCriterion("`NAME` <=", value, "name");
return (Criteria) this;
}
public Criteria andNameLike(String value) {
addCriterion("`NAME` like", value, "name");
return (Criteria) this;
}
public Criteria andNameNotLike(String value) {
addCriterion("`NAME` not like", value, "name");
return (Criteria) this;
}
public Criteria andNameIn(List<String> values) {
addCriterion("`NAME` in", values, "name");
return (Criteria) this;
}
public Criteria andNameNotIn(List<String> values) {
addCriterion("`NAME` not in", values, "name");
return (Criteria) this;
}
public Criteria andNameBetween(String value1, String value2) {
addCriterion("`NAME` between", value1, value2, "name");
return (Criteria) this;
}
public Criteria andNameNotBetween(String value1, String value2) {
addCriterion("`NAME` not between", value1, value2, "name");
return (Criteria) this;
}
public Criteria andEmailIsNull() {
addCriterion("EMAIL is null");
return (Criteria) this;
}
public Criteria andEmailIsNotNull() {
addCriterion("EMAIL is not null");
return (Criteria) this;
}
public Criteria andEmailEqualTo(String value) {
addCriterion("EMAIL =", value, "email");
return (Criteria) this;
}
public Criteria andEmailNotEqualTo(String value) {
addCriterion("EMAIL <>", value, "email");
return (Criteria) this;
}
public Criteria andEmailGreaterThan(String value) {
addCriterion("EMAIL >", value, "email");
return (Criteria) this;
}
public Criteria andEmailGreaterThanOrEqualTo(String value) {
addCriterion("EMAIL >=", value, "email");
return (Criteria) this;
}
public Criteria andEmailLessThan(String value) {
addCriterion("EMAIL <", value, "email");
return (Criteria) this;
}
public Criteria andEmailLessThanOrEqualTo(String value) {
addCriterion("EMAIL <=", value, "email");
return (Criteria) this;
}
public Criteria andEmailLike(String value) {
addCriterion("EMAIL like", value, "email");
return (Criteria) this;
}
public Criteria andEmailNotLike(String value) {
addCriterion("EMAIL not like", value, "email");
return (Criteria) this;
}
public Criteria andEmailIn(List<String> values) {
addCriterion("EMAIL in", values, "email");
return (Criteria) this;
}
public Criteria andEmailNotIn(List<String> values) {
addCriterion("EMAIL not in", values, "email");
return (Criteria) this;
}
public Criteria andEmailBetween(String value1, String value2) {
addCriterion("EMAIL between", value1, value2, "email");
return (Criteria) this;
}
public Criteria andEmailNotBetween(String value1, String value2) {
addCriterion("EMAIL not between", value1, value2, "email");
return (Criteria) this;
}
public Criteria andEnableIsNull() {
addCriterion("`ENABLE` is null");
return (Criteria) this;
}
public Criteria andEnableIsNotNull() {
addCriterion("`ENABLE` is not null");
return (Criteria) this;
}
public Criteria andEnableEqualTo(String value) {
addCriterion("`ENABLE` =", value, "enable");
return (Criteria) this;
}
public Criteria andEnableNotEqualTo(String value) {
addCriterion("`ENABLE` <>", value, "enable");
return (Criteria) this;
}
public Criteria andEnableGreaterThan(String value) {
addCriterion("`ENABLE` >", value, "enable");
return (Criteria) this;
}
public Criteria andEnableGreaterThanOrEqualTo(String value) {
addCriterion("`ENABLE` >=", value, "enable");
return (Criteria) this;
}
public Criteria andEnableLessThan(String value) {
addCriterion("`ENABLE` <", value, "enable");
return (Criteria) this;
}
public Criteria andEnableLessThanOrEqualTo(String value) {
addCriterion("`ENABLE` <=", value, "enable");
return (Criteria) this;
}
public Criteria andEnableLike(String value) {
addCriterion("`ENABLE` like", value, "enable");
return (Criteria) this;
}
public Criteria andEnableNotLike(String value) {
addCriterion("`ENABLE` not like", value, "enable");
return (Criteria) this;
}
public Criteria andEnableIn(List<String> values) {
addCriterion("`ENABLE` in", values, "enable");
return (Criteria) this;
}
public Criteria andEnableNotIn(List<String> values) {
addCriterion("`ENABLE` not in", values, "enable");
return (Criteria) this;
}
public Criteria andEnableBetween(String value1, String value2) {
addCriterion("`ENABLE` between", value1, value2, "enable");
return (Criteria) this;
}
public Criteria andEnableNotBetween(String value1, String value2) {
addCriterion("`ENABLE` not between", value1, value2, "enable");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -0,0 +1,13 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
public class TestPlanProject implements Serializable {
private String testPlanId;
private String projectId;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,340 @@
package io.metersphere.base.domain;
import java.util.ArrayList;
import java.util.List;
public class TestPlanProjectExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public TestPlanProjectExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andTestPlanIdIsNull() {
addCriterion("test_plan_id is null");
return (Criteria) this;
}
public Criteria andTestPlanIdIsNotNull() {
addCriterion("test_plan_id is not null");
return (Criteria) this;
}
public Criteria andTestPlanIdEqualTo(String value) {
addCriterion("test_plan_id =", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotEqualTo(String value) {
addCriterion("test_plan_id <>", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdGreaterThan(String value) {
addCriterion("test_plan_id >", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdGreaterThanOrEqualTo(String value) {
addCriterion("test_plan_id >=", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdLessThan(String value) {
addCriterion("test_plan_id <", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdLessThanOrEqualTo(String value) {
addCriterion("test_plan_id <=", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdLike(String value) {
addCriterion("test_plan_id like", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotLike(String value) {
addCriterion("test_plan_id not like", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdIn(List<String> values) {
addCriterion("test_plan_id in", values, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotIn(List<String> values) {
addCriterion("test_plan_id not in", values, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdBetween(String value1, String value2) {
addCriterion("test_plan_id between", value1, value2, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotBetween(String value1, String value2) {
addCriterion("test_plan_id not between", value1, value2, "testPlanId");
return (Criteria) this;
}
public Criteria andProjectIdIsNull() {
addCriterion("project_id is null");
return (Criteria) this;
}
public Criteria andProjectIdIsNotNull() {
addCriterion("project_id is not null");
return (Criteria) this;
}
public Criteria andProjectIdEqualTo(String value) {
addCriterion("project_id =", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotEqualTo(String value) {
addCriterion("project_id <>", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdGreaterThan(String value) {
addCriterion("project_id >", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdGreaterThanOrEqualTo(String value) {
addCriterion("project_id >=", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLessThan(String value) {
addCriterion("project_id <", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLessThanOrEqualTo(String value) {
addCriterion("project_id <=", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdLike(String value) {
addCriterion("project_id like", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotLike(String value) {
addCriterion("project_id not like", value, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdIn(List<String> values) {
addCriterion("project_id in", values, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotIn(List<String> values) {
addCriterion("project_id not in", values, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdBetween(String value1, String value2) {
addCriterion("project_id between", value1, value2, "projectId");
return (Criteria) this;
}
public Criteria andProjectIdNotBetween(String value1, String value2) {
addCriterion("project_id not between", value1, value2, "projectId");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -14,6 +14,7 @@
<result column="variables" jdbcType="LONGVARCHAR" property="variables" />
<result column="headers" jdbcType="LONGVARCHAR" property="headers" />
<result column="custom_data" jdbcType="LONGVARCHAR" property="customData" />
<result column="hosts" jdbcType="LONGVARCHAR" property="hosts" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -77,7 +78,7 @@
id, `name`, project_id, protocol, socket, `domain`, port
</sql>
<sql id="Blob_Column_List">
`variables`, headers, custom_data
`variables`, headers, custom_data,hosts
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.ApiTestEnvironmentExample" resultMap="ResultMapWithBLOBs">
select
@ -131,11 +132,11 @@
insert into api_test_environment (id, `name`, project_id,
protocol, socket, `domain`,
port, `variables`, headers,
custom_data)
custom_data,hosts)
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR},
#{protocol,jdbcType=VARCHAR}, #{socket,jdbcType=VARCHAR}, #{domain,jdbcType=VARCHAR},
#{port,jdbcType=INTEGER}, #{variables,jdbcType=LONGVARCHAR}, #{headers,jdbcType=LONGVARCHAR},
#{customData,jdbcType=LONGVARCHAR})
#{customData,jdbcType=LONGVARCHAR},#{hosts,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs">
insert into api_test_environment
@ -170,6 +171,10 @@
<if test="customData != null">
custom_data,
</if>
<if test="hosts != null">
hosts,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -202,6 +207,9 @@
<if test="customData != null">
#{customData,jdbcType=LONGVARCHAR},
</if>
<if test="hosts != null">
#{hosts,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.ApiTestEnvironmentExample" resultType="java.lang.Long">
@ -243,6 +251,9 @@
<if test="record.customData != null">
custom_data = #{record.customData,jdbcType=LONGVARCHAR},
</if>
<if test="record.hosts != null">
hosts = #{hosts,jdbcType=LONGVARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -259,7 +270,8 @@
port = #{record.port,jdbcType=INTEGER},
`variables` = #{record.variables,jdbcType=LONGVARCHAR},
headers = #{record.headers,jdbcType=LONGVARCHAR},
custom_data = #{record.customData,jdbcType=LONGVARCHAR}
custom_data = #{record.customData,jdbcType=LONGVARCHAR},
hosts = #{hosts,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -307,6 +319,9 @@
<if test="customData != null">
custom_data = #{customData,jdbcType=LONGVARCHAR},
</if>
<if test="hosts != null">
hosts = #{hosts,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -320,7 +335,8 @@
port = #{port,jdbcType=INTEGER},
`variables` = #{variables,jdbcType=LONGVARCHAR},
headers = #{headers,jdbcType=LONGVARCHAR},
custom_data = #{customData,jdbcType=LONGVARCHAR}
custom_data = #{customData,jdbcType=LONGVARCHAR},
hosts = #{hosts,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.ApiTestEnvironment">

View File

@ -0,0 +1,22 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.Notice;
import io.metersphere.base.domain.NoticeExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface NoticeMapper {
long countByExample(NoticeExample example);
int deleteByExample(NoticeExample example);
int insert(Notice record);
int insertSelective(Notice record);
List<Notice> selectByExample(NoticeExample example);
int updateByExampleSelective(@Param("record") Notice record, @Param("example") NoticeExample example);
int updateByExample(@Param("record") Notice record, @Param("example") NoticeExample example);
}

View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.NoticeMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.Notice">
<result column="EVENT" jdbcType="VARCHAR" property="event" />
<result column="TEST_ID" jdbcType="VARCHAR" property="testId" />
<result column="NAME" jdbcType="VARCHAR" property="name" />
<result column="EMAIL" jdbcType="VARCHAR" property="email" />
<result column="ENABLE" jdbcType="VARCHAR" property="enable" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
EVENT, TEST_ID, `NAME`, EMAIL, `ENABLE`
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.NoticeExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from notice
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.NoticeExample">
delete from notice
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.Notice">
insert into notice (EVENT, TEST_ID, `NAME`,
EMAIL, `ENABLE`)
values (#{event,jdbcType=VARCHAR}, #{testId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR}, #{enable,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Notice">
insert into notice
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="event != null">
EVENT,
</if>
<if test="testId != null">
TEST_ID,
</if>
<if test="name != null">
`NAME`,
</if>
<if test="email != null">
EMAIL,
</if>
<if test="enable != null">
`ENABLE`,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="event != null">
#{event,jdbcType=VARCHAR},
</if>
<if test="testId != null">
#{testId,jdbcType=VARCHAR},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="enable != null">
#{enable,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.NoticeExample" resultType="java.lang.Long">
select count(*) from notice
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update notice
<set>
<if test="record.event != null">
EVENT = #{record.event,jdbcType=VARCHAR},
</if>
<if test="record.testId != null">
TEST_ID = #{record.testId,jdbcType=VARCHAR},
</if>
<if test="record.name != null">
`NAME` = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.email != null">
EMAIL = #{record.email,jdbcType=VARCHAR},
</if>
<if test="record.enable != null">
`ENABLE` = #{record.enable,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update notice
set EVENT = #{record.event,jdbcType=VARCHAR},
TEST_ID = #{record.testId,jdbcType=VARCHAR},
`NAME` = #{record.name,jdbcType=VARCHAR},
EMAIL = #{record.email,jdbcType=VARCHAR},
`ENABLE` = #{record.enable,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
</mapper>

View File

@ -0,0 +1,22 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.TestPlanProject;
import io.metersphere.base.domain.TestPlanProjectExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface TestPlanProjectMapper {
long countByExample(TestPlanProjectExample example);
int deleteByExample(TestPlanProjectExample example);
int insert(TestPlanProject record);
int insertSelective(TestPlanProject record);
List<TestPlanProject> selectByExample(TestPlanProjectExample example);
int updateByExampleSelective(@Param("record") TestPlanProject record, @Param("example") TestPlanProjectExample example);
int updateByExample(@Param("record") TestPlanProject record, @Param("example") TestPlanProjectExample example);
}

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.TestPlanProjectMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.TestPlanProject">
<result column="test_plan_id" jdbcType="VARCHAR" property="testPlanId" />
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
test_plan_id, project_id
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.TestPlanProjectExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from test_plan_project
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.TestPlanProjectExample">
delete from test_plan_project
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlanProject">
insert into test_plan_project (test_plan_id, project_id)
values (#{testPlanId,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlanProject">
insert into test_plan_project
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="testPlanId != null">
test_plan_id,
</if>
<if test="projectId != null">
project_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="testPlanId != null">
#{testPlanId,jdbcType=VARCHAR},
</if>
<if test="projectId != null">
#{projectId,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.TestPlanProjectExample" resultType="java.lang.Long">
select count(*) from test_plan_project
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update test_plan_project
<set>
<if test="record.testPlanId != null">
test_plan_id = #{record.testPlanId,jdbcType=VARCHAR},
</if>
<if test="record.projectId != null">
project_id = #{record.projectId,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update test_plan_project
set test_plan_id = #{record.testPlanId,jdbcType=VARCHAR},
project_id = #{record.projectId,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
</mapper>

View File

@ -1,92 +0,0 @@
package io.metersphere.config;
import com.fit2cloud.autoconfigure.QuartzProperties;
import com.fit2cloud.quartz.QuartzInstanceIdGenerator;
import com.fit2cloud.quartz.SchedulerStarter;
import com.fit2cloud.quartz.service.QuartzManageService;
import com.fit2cloud.quartz.util.QuartzBeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.Properties;
import java.util.TimeZone;
@Configuration
@EnableConfigurationProperties(QuartzProperties.class)
@ConditionalOnClass(DataSource.class)
@AutoConfigureAfter(DataSource.class)
public class QuartzAutoConfiguration {
private DataSource dataSource;
private QuartzProperties properties;
public QuartzAutoConfiguration(ObjectProvider<DataSource> dataSourceProvider, QuartzProperties properties) {
this.dataSource = dataSourceProvider.getIfAvailable();
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true")
public SchedulerStarter schedulerStarter() {
return new SchedulerStarter();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true")
public QuartzBeanFactory quartzBeanFactory() {
return new QuartzBeanFactory();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true")
public QuartzManageService quartzManageService() {
return new QuartzManageService();
}
@Bean
@ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true")
public TimeZone quartzTimeZone() {
return TimeZone.getTimeZone(properties.getTimeZone());
}
@Bean
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "quartz", value = "enabled", havingValue = "true")
public SchedulerFactoryBean clusterSchedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setDataSource(this.dataSource);
schedulerFactoryBean.setApplicationContextSchedulerContextKey("applicationContextKey");
schedulerFactoryBean.setOverwriteExistingJobs(true);
schedulerFactoryBean.setStartupDelay(60);// 60 秒之后开始执行定时任务
Properties props = new Properties();
props.put("org.quartz.scheduler.instanceName", "clusterScheduler");
props.put("org.quartz.scheduler.instanceId", "AUTO"); // 集群下的instanceId 必须唯一
props.put("org.quartz.scheduler.instanceIdGenerator.class", QuartzInstanceIdGenerator.class.getName());// instanceId 生成的方式
props.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
props.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
props.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
props.put("org.quartz.jobStore.isClustered", "true");
props.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
props.put("org.quartz.threadPool.threadCount", "10");
props.put("org.quartz.threadPool.threadPriority", "5");
props.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
schedulerFactoryBean.setQuartzProperties(props);
if (!StringUtils.isEmpty(this.properties.getSchedulerName())) {
schedulerFactoryBean.setBeanName(this.properties.getSchedulerName());
}
return schedulerFactoryBean;
}
}

View File

@ -102,7 +102,7 @@ public class ShiroConfig implements EnvironmentAware {
@Bean
public SessionManager sessionManager(MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
Long sessionTimeout = env.getProperty("session.timeout", Long.class, 1800L); // 默认1800s, 个小时
Long sessionTimeout = env.getProperty("session.timeout", Long.class, 43200L); // 默认43200s, 12个小时
return ShiroUtils.getSessionManager(sessionTimeout, memoryConstrainedCacheManager);
}

View File

@ -1,8 +1,11 @@
package io.metersphere.excel.utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.exception.ExcelException;
import org.apache.poi.ss.usermodel.IndexedColors;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@ -29,9 +32,12 @@ public class EasyExcelExporter {
public void export(HttpServletResponse response, List data, String fileName, String sheetName) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setWrapped(true);
try {
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream(), this.clazz).sheet(sheetName).doWrite(data);
EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported");

View File

@ -34,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
@ -42,8 +43,6 @@ import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@Service
@Transactional(rollbackFor = Exception.class)
public class PerformanceTestService {
@ -381,6 +380,10 @@ public class PerformanceTestService {
if (!CollectionUtils.isEmpty(loadTestFiles)) {
loadTestFiles.forEach(loadTestFile -> {
FileMetadata fileMetadata = fileService.copyFile(loadTestFile.getFileId());
if (fileMetadata == null) {
// 如果性能测试出现文件变更这里会有 null
return;
}
loadTestFile.setTestId(copy.getId());
loadTestFile.setFileId(fileMetadata.getId());
loadTestFileMapper.insert(loadTestFile);

View File

@ -60,6 +60,10 @@ public class FileService {
FileContentExample example2 = new FileContentExample();
example2.createCriteria().andFileIdIn(ids);
fileContentMapper.deleteByExample(example2);
LoadTestFileExample example3 = new LoadTestFileExample();
example3.createCriteria().andFileIdIn(ids);
loadTestFileMapper.deleteByExample(example3);
}
public FileMetadata saveFile(MultipartFile file) {

View File

@ -224,6 +224,10 @@ public class IssuesService {
String account = object.getString("account");
String password = object.getString("password");
String url = object.getString("url");
String issuetype = object.getString("issuetype");
if (StringUtils.isBlank(issuetype)) {
MSException.throwException("Jira 问题类型为空");
}
String auth = EncryptUtils.base64Encoding(account + ":" + password);
String testCaseId = issuesRequest.getTestCaseId();
@ -252,8 +256,7 @@ public class IssuesService {
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\": " + JSON.toJSONString(desc) + ",\n" +
" \"issuetype\":{\n" +
" \"id\":\"10009\",\n" +
" \"name\":\"Defect\"\n" +
" \"name\":\"" + issuetype + "\"\n" +
" }\n" +
" }\n" +
"}";

View File

@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jmeter.util;
import org.apache.jmeter.testbeans.TestBean;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import java.util.*;
import java.util.Map.Entry;
/**
* 解决JSR233加载 ScriptEngineFactory 空指针问题
*/
public abstract class JSR223BeanInfoSupport extends ScriptingBeanInfoSupport {
private static final String[] LANGUAGE_TAGS;
/**
* Will be removed in next version following 3.2
* @deprecated use {@link JSR223BeanInfoSupport#getLanguageNames()}
*/
@Deprecated
public static final String[][] LANGUAGE_NAMES; // NOSONAR Kept for backward compatibility
private static final String[][] CONSTANT_LANGUAGE_NAMES;
static {
Map<String, ScriptEngineFactory> nameMap = new HashMap<>();
ScriptEngineManager sem = new ScriptEngineManager();
final List<ScriptEngineFactory> engineFactories = sem.getEngineFactories();
for(ScriptEngineFactory fact : engineFactories){
List<String> names = fact.getNames();
for(String shortName : names) {
if (shortName != null) {
nameMap.put(shortName.toLowerCase(Locale.ENGLISH), fact);
}
}
}
LANGUAGE_TAGS = nameMap.keySet().toArray(new String[nameMap.size()]);
Arrays.sort(LANGUAGE_TAGS);
CONSTANT_LANGUAGE_NAMES = new String[nameMap.size()][2];
int i = 0;
for(Entry<String, ScriptEngineFactory> me : nameMap.entrySet()) {
final String key = me.getKey();
CONSTANT_LANGUAGE_NAMES[i][0] = key;
final ScriptEngineFactory fact = me.getValue();
CONSTANT_LANGUAGE_NAMES[i++][1] = key +
" (" // $NON-NLS-1$
+ fact.getLanguageName() + " " + fact.getLanguageVersion() // $NON-NLS-1$
+ " / " // $NON-NLS-1$
+ fact.getEngineName() + " " + fact.getEngineVersion() // $NON-NLS-1$
+ ")"; // $NON-NLS-1$
}
LANGUAGE_NAMES = getLanguageNames(); // NOSONAR Kept for backward compatibility
}
private static final ResourceBundle NAME_BUNDLE = new ListResourceBundle() {
@Override
protected Object[][] getContents() {
return CONSTANT_LANGUAGE_NAMES;
}
};
protected JSR223BeanInfoSupport(Class<? extends TestBean> beanClass) {
super(beanClass, LANGUAGE_TAGS, NAME_BUNDLE);
}
/**
* @return String array of 2 columns array containing Script engine short name / Script Language details
*/
public static final String[][] getLanguageNames() {
return CONSTANT_LANGUAGE_NAMES.clone();
}
}

View File

@ -78,3 +78,4 @@ spring.servlet.multipart.max-request-size=500MB
management.server.port=8083
management.endpoints.web.exposure.include=*
spring.freemarker.checkTemplateLocation=false

View File

@ -0,0 +1,9 @@
create table if not exists test_plan_project
(
test_plan_id varchar(50) null,
project_id varchar(50) null,
constraint test_plan_project_pk
unique (test_plan_id, project_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into test_plan_project(test_plan_id, project_id) select id test_plan_id, project_id project_id from test_plan;

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS `notice`(
`EVENT` VARCHAR(100) NOT NULL,
`TEST_ID` VARCHAR(100) NOT NULL,
`NAME` VARCHAR(100) NOT NULL,
`EMAIL` VARCHAR(100) NOT NULL,
`ENABLE` VARCHAR(40) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@ -0,0 +1 @@
ALTER TABLE `api_test_environment` ADD `hosts` longtext COMMENT 'hosts ';

View File

@ -65,6 +65,7 @@
<!--要生成的数据库表 -->
<table tableName="schedule"/>
<table tableName="notice"/>
</context>

View File

@ -1,4 +1,3 @@
export HEAP=$(cat /proc/meminfo | grep MemTotal | awk '{ mem=int($2/1024/1024 * 3/4 + 0.5); metasize=int(mem/4+0.5)"g"; if(mem<1) mem=1; if (metasize == "0g") metasize="256m"; HEAP="-Xms"mem"g -Xmx"mem"g -XX:MaxMetaspaceSize="metasize; print HEAP }')
for file in ${TESTS_DIR}/*.jmx; do
jmeter -n -t ${file} -Jserver.rmi.ssl.disable=${SSL_DISABLED}
done

View File

@ -1,85 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="947.2 183.1 174 35" style="enable-background:new 947.2 183.1 174 35;" xml:space="preserve">
<style type="text/css">
.st0{fill:#783887;}
.st1{fill:#622870;}
.st2{fill:#8B489B;}
.st3{fill:#FFFFFF;}
</style>
<g>
<g id="XMLID_803_">
<path id="XMLID_829_" class="st0" d="M998.3,209.6l-1.1-11.2l-3.1,11.2h-3.6l-3-11.2l-1.1,11.2l-5,0l2.4-16.5h6.3l2.2,7.8l2.2-7.8
h6.2l2.5,16.5H998.3z"/>
<path id="XMLID_826_" class="st0" d="M1015.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1015.1,200.7,1015.2,201.6,1015.2,202.7z M1011.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_823_" class="st0" d="M1037.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1037.2,200.7,1037.3,201.6,1037.3,202.7z M1033.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1L1033.9,202.1z"/>
<path id="XMLID_820_" class="st0" d="M1098.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1098.1,200.7,1098.2,201.6,1098.2,202.7z M1094.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_817_" class="st0" d="M1120.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1120.2,200.7,1120.3,201.6,1120.3,202.7z M1116.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_815_" class="st0" d="M1025.7,209.6h-2.1c-0.4,0-0.7,0-0.9,0c-0.7-0.1-1.3-0.2-1.7-0.4c-0.5-0.3-1-0.7-1.2-1.2
c-0.3-0.6-0.5-1.4-0.5-2.5l0-4.6h-1.7v-3h1.7v-2.3l1.7,0l1.8-0.2v2.6h2.7l-0.4,3h-2.3v4.4c0,0.1,0,0.2,0,0.3c0,0.2,0,0.3,0.1,0.5
c0.1,0.3,0.3,0.5,0.7,0.6c0.3,0.1,1.9,0.1,1.9,0.1L1025.7,209.6z"/>
<path id="XMLID_813_" class="st0" d="M1047.7,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1046.7,197.8,1047.3,197.9,1047.7,197.9z"/>
<path id="XMLID_811_" class="st0" d="M1108.9,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1107.9,197.8,1108.5,197.9,1108.9,197.9z"/>
<path id="XMLID_809_" class="st0" d="M1054.6,210.1c-0.8,0-1.8-0.1-2.7-0.2c-0.4,0-0.8-0.1-1.2-0.2l-1.6-0.2l0-4.1l2.4,0.4
c0,0,1,0.1,1.4,0.2c0.5,0.1,1,0.1,1.4,0.1c1.5,0,2.4-0.4,2.7-1.1c0.1-0.3,0.1-0.6,0-0.9c-0.2-0.3-0.5-0.6-0.9-0.8
c-0.2-0.1-0.5-0.2-0.8-0.3l-0.9-0.3c-0.4-0.1-0.8-0.2-1.1-0.3c-0.4-0.1-0.8-0.3-1.1-0.4c-0.8-0.4-1.5-0.8-2-1.3
c-0.4-0.5-0.7-1.1-0.9-1.8c-0.1-0.5-0.1-1.1,0-1.7c0.2-1.8,1-3.1,2.3-3.8c0.5-0.3,1.1-0.5,1.8-0.6c0.3,0,0.6-0.1,0.9-0.1l0.9-0.1
h0.6c0.7,0,1.5,0,2.3,0.1l1,0.1l1.2,0.2l0,3.6c-0.5-0.1-1-0.2-1.7-0.3c-0.9-0.1-1.7-0.2-2.4-0.2c-1.6,0-2.2,0.3-2.5,1.1
c-0.1,0.3-0.1,0.6-0.1,0.9c0,0.4,0,0.5,0.7,0.8c0.2,0.1,0.5,0.2,0.8,0.3l2.1,0.6c0.4,0.1,0.8,0.3,1.1,0.4c0.8,0.3,1.4,0.7,1.8,1.1
c0.5,0.5,0.8,1.1,0.9,1.8c0.1,0.5,0.1,1.1,0,1.7c-0.2,1.8-1.1,3.4-2.4,4.1c-0.5,0.3-1.1,0.5-1.8,0.6c-0.3,0-0.6,0.1-0.9,0.1
l-0.8,0.1L1054.6,210.1L1054.6,210.1z"/>
<path id="XMLID_806_" class="st0" d="M1063.6,197.8h3.2l-0.1,0.8c0.3-0.2,0.6-0.3,1-0.4c0.4-0.2,0.8-0.3,1.1-0.3
c0.9-0.2,1.8-0.2,2.7,0.1c0.5,0.2,1,0.5,1.4,1c0.2,0.2,0.3,0.4,0.4,0.6l0.1,0.2c0,0.1,0.1,0.1,0.1,0.2c0.2,0.4,0.3,0.9,0.4,1.4
c0.1,0.6,0.2,1.3,0.2,2.2v0.7c0,1.8-0.1,2.7-0.4,3.4c-0.5,1.1-1.6,1.6-3.7,1.7c-0.3,0-0.7,0-1.2,0l-0.8,0c-0.3,0-0.5,0-0.7,0v5.1
l-3.6,0.2V197.8L1063.6,197.8z M1067.2,206.1c0.7,0.1,1.1,0.1,1.7,0.1l0.6-0.1c0.7-0.2,1-0.6,1-1.3v-2.3c0-0.3,0-0.6-0.1-0.7
c-0.2-0.7-0.8-1-1.8-0.9l-0.4,0.1l-0.7,0.2l-0.3,0.1L1067.2,206.1L1067.2,206.1z"/>
<path id="XMLID_804_" class="st0" d="M1086.5,203.2v6.4h-3.6v-7c0-0.4-0.3-1.1-0.8-1.4c-0.4-0.2-1-0.2-1.2-0.1
c-0.5,0.1-1,0.4-1.3,0.6v7.9h-3.6v-17l3.6,0v6c0.4-0.2,0.9-0.3,1.4-0.4c0.4-0.1,0.8-0.1,1.2-0.1c2.1,0,3.2,0.6,3.7,1.7
C1086.2,200.2,1086.5,201.4,1086.5,203.2z"/>
</g>
<g id="XMLID_799_">
<path id="XMLID_800_" class="st0" d="M961.7,184.4l13.7,8v16.1l-13.7,8l-13.7-8v-16.1L961.7,184.4 M961.7,183.9l-14.1,8.3v16.5
l14.1,8.3l14.1-8.3v-16.5L961.7,183.9L961.7,183.9z"/>
</g>
<g id="XMLID_796_">
<polygon id="XMLID_64_" class="st1" points="961.9,200.6 973.8,193.6 973.8,207.5 961.9,214.5 "/>
</g>
<polygon id="XMLID_795_" class="st2" points="949.8,193.2 961.7,186.3 973.6,193.2 961.7,200.2 "/>
<g id="XMLID_792_">
<polygon id="XMLID_65_" class="st0" points="949.6,207.5 949.6,193.6 961.5,200.6 961.5,214.5 "/>
</g>
<g id="XMLID_789_">
<path id="XMLID_790_" class="st3" d="M957.8,209.4v-4.9l-1.8,3.9l-1.5-0.9l-0.8-5.4l-0.9,4.3l-2.1-1.3l1.7-6.2l2.6,1.6l0.6,3.8
l1.2-2.7l2.6,1.5l0.4,7.5L957.8,209.4z"/>
</g>
<g id="XMLID_786_">
<path id="XMLID_787_" class="st3" d="M968,209.1c-0.4,0.2-0.9,0.3-1.4,0.5c-0.2,0.1-0.4,0.1-0.6,0.2l-0.8,0.2l0-2l1.2-0.3
c0,0,0.5-0.1,0.7-0.2c0.3-0.1,0.5-0.2,0.7-0.3c0.8-0.3,1.2-0.7,1.3-1.1c0.1-0.2,0.1-0.3,0-0.4c-0.1-0.1-0.2-0.2-0.5-0.2
c-0.1,0-0.2,0-0.4,0l-0.5,0c-0.2,0-0.4,0.1-0.5,0.1c-0.2,0-0.4,0-0.6,0c-0.4,0-0.8-0.1-1-0.2c-0.2-0.1-0.4-0.4-0.5-0.7
c-0.1-0.2-0.1-0.5,0-0.8c0.1-0.9,0.4-1.7,1.1-2.3c0.3-0.2,0.5-0.5,0.9-0.7c0.1-0.1,0.3-0.2,0.5-0.2l0.5-0.2l0.3-0.1
c0.4-0.1,0.7-0.3,1.2-0.4l0.5-0.2l0.6-0.2l0.1,1.8c-0.2,0-0.5,0.1-0.9,0.2c-0.4,0.1-0.9,0.3-1.2,0.4c-0.8,0.3-1.1,0.6-1.2,1
c0,0.2,0,0.3,0,0.5c0,0.2,0,0.2,0.4,0.3c0.1,0,0.3,0,0.4,0l1.1-0.1c0.2,0,0.4,0,0.5,0c0.4,0,0.7,0.1,0.9,0.2
c0.2,0.1,0.4,0.4,0.5,0.7c0.1,0.2,0.1,0.5,0,0.8c-0.1,0.9-0.5,1.9-1.1,2.5c-0.2,0.2-0.5,0.5-0.9,0.7c-0.1,0.1-0.3,0.2-0.5,0.2
l-0.4,0.2L968,209.1z"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="947.2 183.1 174 35" style="enable-background:new 947.2 183.1 174 35;" xml:space="preserve">
<style type="text/css">
.st0{fill:#783887;}
.st1{fill:#622870;}
.st2{fill:#8B489B;}
.st3{fill:#FFFFFF;}
</style>
<g>
<g id="XMLID_803_">
<path id="XMLID_829_" class="st0" d="M998.3,209.6l-1.1-11.2l-3.1,11.2h-3.6l-3-11.2l-1.1,11.2l-5,0l2.4-16.5h6.3l2.2,7.8l2.2-7.8
h6.2l2.5,16.5H998.3z"/>
<path id="XMLID_826_" class="st0" d="M1015.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1015.1,200.7,1015.2,201.6,1015.2,202.7z M1011.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_823_" class="st0" d="M1037.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1037.2,200.7,1037.3,201.6,1037.3,202.7z M1033.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1L1033.9,202.1z"/>
<path id="XMLID_820_" class="st0" d="M1098.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1098.1,200.7,1098.2,201.6,1098.2,202.7z M1094.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_817_" class="st0" d="M1120.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1120.2,200.7,1120.3,201.6,1120.3,202.7z M1116.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_815_" class="st0" d="M1025.7,209.6h-2.1c-0.4,0-0.7,0-0.9,0c-0.7-0.1-1.3-0.2-1.7-0.4c-0.5-0.3-1-0.7-1.2-1.2
c-0.3-0.6-0.5-1.4-0.5-2.5l0-4.6h-1.7v-3h1.7v-2.3l1.7,0l1.8-0.2v2.6h2.7l-0.4,3h-2.3v4.4c0,0.1,0,0.2,0,0.3c0,0.2,0,0.3,0.1,0.5
c0.1,0.3,0.3,0.5,0.7,0.6c0.3,0.1,1.9,0.1,1.9,0.1L1025.7,209.6z"/>
<path id="XMLID_813_" class="st0" d="M1047.7,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1046.7,197.8,1047.3,197.9,1047.7,197.9z"/>
<path id="XMLID_811_" class="st0" d="M1108.9,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1107.9,197.8,1108.5,197.9,1108.9,197.9z"/>
<path id="XMLID_809_" class="st0" d="M1054.6,210.1c-0.8,0-1.8-0.1-2.7-0.2c-0.4,0-0.8-0.1-1.2-0.2l-1.6-0.2l0-4.1l2.4,0.4
c0,0,1,0.1,1.4,0.2c0.5,0.1,1,0.1,1.4,0.1c1.5,0,2.4-0.4,2.7-1.1c0.1-0.3,0.1-0.6,0-0.9c-0.2-0.3-0.5-0.6-0.9-0.8
c-0.2-0.1-0.5-0.2-0.8-0.3l-0.9-0.3c-0.4-0.1-0.8-0.2-1.1-0.3c-0.4-0.1-0.8-0.3-1.1-0.4c-0.8-0.4-1.5-0.8-2-1.3
c-0.4-0.5-0.7-1.1-0.9-1.8c-0.1-0.5-0.1-1.1,0-1.7c0.2-1.8,1-3.1,2.3-3.8c0.5-0.3,1.1-0.5,1.8-0.6c0.3,0,0.6-0.1,0.9-0.1l0.9-0.1
h0.6c0.7,0,1.5,0,2.3,0.1l1,0.1l1.2,0.2l0,3.6c-0.5-0.1-1-0.2-1.7-0.3c-0.9-0.1-1.7-0.2-2.4-0.2c-1.6,0-2.2,0.3-2.5,1.1
c-0.1,0.3-0.1,0.6-0.1,0.9c0,0.4,0,0.5,0.7,0.8c0.2,0.1,0.5,0.2,0.8,0.3l2.1,0.6c0.4,0.1,0.8,0.3,1.1,0.4c0.8,0.3,1.4,0.7,1.8,1.1
c0.5,0.5,0.8,1.1,0.9,1.8c0.1,0.5,0.1,1.1,0,1.7c-0.2,1.8-1.1,3.4-2.4,4.1c-0.5,0.3-1.1,0.5-1.8,0.6c-0.3,0-0.6,0.1-0.9,0.1
l-0.8,0.1L1054.6,210.1L1054.6,210.1z"/>
<path id="XMLID_806_" class="st0" d="M1063.6,197.8h3.2l-0.1,0.8c0.3-0.2,0.6-0.3,1-0.4c0.4-0.2,0.8-0.3,1.1-0.3
c0.9-0.2,1.8-0.2,2.7,0.1c0.5,0.2,1,0.5,1.4,1c0.2,0.2,0.3,0.4,0.4,0.6l0.1,0.2c0,0.1,0.1,0.1,0.1,0.2c0.2,0.4,0.3,0.9,0.4,1.4
c0.1,0.6,0.2,1.3,0.2,2.2v0.7c0,1.8-0.1,2.7-0.4,3.4c-0.5,1.1-1.6,1.6-3.7,1.7c-0.3,0-0.7,0-1.2,0l-0.8,0c-0.3,0-0.5,0-0.7,0v5.1
l-3.6,0.2V197.8L1063.6,197.8z M1067.2,206.1c0.7,0.1,1.1,0.1,1.7,0.1l0.6-0.1c0.7-0.2,1-0.6,1-1.3v-2.3c0-0.3,0-0.6-0.1-0.7
c-0.2-0.7-0.8-1-1.8-0.9l-0.4,0.1l-0.7,0.2l-0.3,0.1L1067.2,206.1L1067.2,206.1z"/>
<path id="XMLID_804_" class="st0" d="M1086.5,203.2v6.4h-3.6v-7c0-0.4-0.3-1.1-0.8-1.4c-0.4-0.2-1-0.2-1.2-0.1
c-0.5,0.1-1,0.4-1.3,0.6v7.9h-3.6v-17l3.6,0v6c0.4-0.2,0.9-0.3,1.4-0.4c0.4-0.1,0.8-0.1,1.2-0.1c2.1,0,3.2,0.6,3.7,1.7
C1086.2,200.2,1086.5,201.4,1086.5,203.2z"/>
</g>
<g id="XMLID_799_">
<path id="XMLID_800_" class="st0" d="M961.7,184.4l13.7,8v16.1l-13.7,8l-13.7-8v-16.1L961.7,184.4 M961.7,183.9l-14.1,8.3v16.5
l14.1,8.3l14.1-8.3v-16.5L961.7,183.9L961.7,183.9z"/>
</g>
<g id="XMLID_796_">
<polygon id="XMLID_64_" class="st1" points="961.9,200.6 973.8,193.6 973.8,207.5 961.9,214.5 "/>
</g>
<polygon id="XMLID_795_" class="st2" points="949.8,193.2 961.7,186.3 973.6,193.2 961.7,200.2 "/>
<g id="XMLID_792_">
<polygon id="XMLID_65_" class="st0" points="949.6,207.5 949.6,193.6 961.5,200.6 961.5,214.5 "/>
</g>
<g id="XMLID_789_">
<path id="XMLID_790_" class="st3" d="M957.8,209.4v-4.9l-1.8,3.9l-1.5-0.9l-0.8-5.4l-0.9,4.3l-2.1-1.3l1.7-6.2l2.6,1.6l0.6,3.8
l1.2-2.7l2.6,1.5l0.4,7.5L957.8,209.4z"/>
</g>
<g id="XMLID_786_">
<path id="XMLID_787_" class="st3" d="M968,209.1c-0.4,0.2-0.9,0.3-1.4,0.5c-0.2,0.1-0.4,0.1-0.6,0.2l-0.8,0.2l0-2l1.2-0.3
c0,0,0.5-0.1,0.7-0.2c0.3-0.1,0.5-0.2,0.7-0.3c0.8-0.3,1.2-0.7,1.3-1.1c0.1-0.2,0.1-0.3,0-0.4c-0.1-0.1-0.2-0.2-0.5-0.2
c-0.1,0-0.2,0-0.4,0l-0.5,0c-0.2,0-0.4,0.1-0.5,0.1c-0.2,0-0.4,0-0.6,0c-0.4,0-0.8-0.1-1-0.2c-0.2-0.1-0.4-0.4-0.5-0.7
c-0.1-0.2-0.1-0.5,0-0.8c0.1-0.9,0.4-1.7,1.1-2.3c0.3-0.2,0.5-0.5,0.9-0.7c0.1-0.1,0.3-0.2,0.5-0.2l0.5-0.2l0.3-0.1
c0.4-0.1,0.7-0.3,1.2-0.4l0.5-0.2l0.6-0.2l0.1,1.8c-0.2,0-0.5,0.1-0.9,0.2c-0.4,0.1-0.9,0.3-1.2,0.4c-0.8,0.3-1.1,0.6-1.2,1
c0,0.2,0,0.3,0,0.5c0,0.2,0,0.2,0.4,0.3c0.1,0,0.3,0,0.4,0l1.1-0.1c0.2,0,0.4,0,0.5,0c0.4,0,0.7,0.1,0.9,0.2
c0.2,0.1,0.4,0.4,0.5,0.7c0.1,0.2,0.1,0.5,0,0.8c-0.1,0.9-0.5,1.9-1.1,2.5c-0.2,0.2-0.5,0.5-0.9,0.7c-0.1,0.1-0.3,0.2-0.5,0.2
l-0.4,0.2L968,209.1z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,80 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="947.2 183.1 174 35" style="enable-background:new 947.2 183.1 174 35;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<g id="XMLID_140_">
<path id="XMLID_166_" class="st0" d="M998.3,209.6l-1.1-11.2l-3.1,11.2h-3.6l-3-11.2l-1.1,11.2l-5,0l2.4-16.5h6.3l2.2,7.8l2.2-7.8
h6.2l2.5,16.5H998.3z"/>
<path id="XMLID_163_" class="st0" d="M1015.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1015.1,200.7,1015.2,201.6,1015.2,202.7z M1011.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_160_" class="st0" d="M1037.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1037.2,200.7,1037.3,201.6,1037.3,202.7z M1033.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1L1033.9,202.1z"/>
<path id="XMLID_157_" class="st0" d="M1098.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1098.1,200.7,1098.2,201.6,1098.2,202.7z M1094.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_154_" class="st0" d="M1120.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1120.2,200.7,1120.3,201.6,1120.3,202.7z M1116.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_152_" class="st0" d="M1025.7,209.6h-2.1c-0.4,0-0.7,0-0.9,0c-0.7-0.1-1.3-0.2-1.7-0.4c-0.5-0.3-1-0.7-1.2-1.2
c-0.3-0.6-0.5-1.4-0.5-2.5l0-4.6h-1.7v-3h1.7v-2.3l1.7,0l1.8-0.2v2.6h2.7l-0.4,3h-2.3v4.4c0,0.1,0,0.2,0,0.3c0,0.2,0,0.3,0.1,0.5
c0.1,0.3,0.3,0.5,0.7,0.6c0.3,0.1,1.9,0.1,1.9,0.1L1025.7,209.6z"/>
<path id="XMLID_150_" class="st0" d="M1047.7,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1046.7,197.8,1047.3,197.9,1047.7,197.9z"/>
<path id="XMLID_148_" class="st0" d="M1108.9,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1107.9,197.8,1108.5,197.9,1108.9,197.9z"/>
<path id="XMLID_146_" class="st0" d="M1054.6,210.1c-0.8,0-1.8-0.1-2.7-0.2c-0.4,0-0.8-0.1-1.2-0.2l-1.6-0.2l0-4.1l2.4,0.4
c0,0,1,0.1,1.4,0.2c0.5,0.1,1,0.1,1.4,0.1c1.5,0,2.4-0.4,2.7-1.1c0.1-0.3,0.1-0.6,0-0.9c-0.2-0.3-0.5-0.6-0.9-0.8
c-0.2-0.1-0.5-0.2-0.8-0.3l-0.9-0.3c-0.4-0.1-0.8-0.2-1.1-0.3c-0.4-0.1-0.8-0.3-1.1-0.4c-0.8-0.4-1.5-0.8-2-1.3
c-0.4-0.5-0.7-1.1-0.9-1.8c-0.1-0.5-0.1-1.1,0-1.7c0.2-1.8,1-3.1,2.3-3.8c0.5-0.3,1.1-0.5,1.8-0.6c0.3,0,0.6-0.1,0.9-0.1l0.9-0.1
h0.6c0.7,0,1.5,0,2.3,0.1l1,0.1l1.2,0.2l0,3.6c-0.5-0.1-1-0.2-1.7-0.3c-0.9-0.1-1.7-0.2-2.4-0.2c-1.6,0-2.2,0.3-2.5,1.1
c-0.1,0.3-0.1,0.6-0.1,0.9c0,0.4,0,0.5,0.7,0.8c0.2,0.1,0.5,0.2,0.8,0.3l2.1,0.6c0.4,0.1,0.8,0.3,1.1,0.4c0.8,0.3,1.4,0.7,1.8,1.1
c0.5,0.5,0.8,1.1,0.9,1.8c0.1,0.5,0.1,1.1,0,1.7c-0.2,1.8-1.1,3.4-2.4,4.1c-0.5,0.3-1.1,0.5-1.8,0.6c-0.3,0-0.6,0.1-0.9,0.1
l-0.8,0.1L1054.6,210.1L1054.6,210.1z"/>
<path id="XMLID_143_" class="st0" d="M1063.6,197.8h3.2l-0.1,0.8c0.3-0.2,0.6-0.3,1-0.4c0.4-0.2,0.8-0.3,1.1-0.3
c0.9-0.2,1.8-0.2,2.7,0.1c0.5,0.2,1,0.5,1.4,1c0.2,0.2,0.3,0.4,0.4,0.6l0.1,0.2c0,0.1,0.1,0.1,0.1,0.2c0.2,0.4,0.3,0.9,0.4,1.4
c0.1,0.6,0.2,1.3,0.2,2.2v0.7c0,1.8-0.1,2.7-0.4,3.4c-0.5,1.1-1.6,1.6-3.7,1.7c-0.3,0-0.7,0-1.2,0l-0.8,0c-0.3,0-0.5,0-0.7,0v5.1
l-3.6,0.2V197.8L1063.6,197.8z M1067.2,206.1c0.7,0.1,1.1,0.1,1.7,0.1l0.6-0.1c0.7-0.2,1-0.6,1-1.3v-2.3c0-0.3,0-0.6-0.1-0.7
c-0.2-0.7-0.8-1-1.8-0.9l-0.4,0.1l-0.7,0.2l-0.3,0.1L1067.2,206.1L1067.2,206.1z"/>
<path id="XMLID_141_" class="st0" d="M1086.5,203.2v6.4h-3.6v-7c0-0.4-0.3-1.1-0.8-1.4c-0.4-0.2-1-0.2-1.2-0.1
c-0.5,0.1-1,0.4-1.3,0.6v7.9h-3.6v-17l3.6,0v6c0.4-0.2,0.9-0.3,1.4-0.4c0.4-0.1,0.8-0.1,1.2-0.1c2.1,0,3.2,0.6,3.7,1.7
C1086.2,200.2,1086.5,201.4,1086.5,203.2z"/>
</g>
<g id="XMLID_136_">
<path id="XMLID_137_" class="st0" d="M961.7,184.4l13.7,8v16.1l-13.7,8l-13.7-8v-16.1L961.7,184.4 M961.7,183.9l-14.1,8.3v16.5
l14.1,8.3l14.1-8.3v-16.5L961.7,183.9L961.7,183.9z"/>
</g>
<polygon id="XMLID_132_" class="st0" points="949.8,193.2 961.7,186.3 973.6,193.2 961.7,200.2 "/>
<g id="XMLID_278_">
<g id="XMLID_42_">
<path id="XMLID_43_" class="st0" d="M949.6,193.6v13.9l11.9,6.9v-13.9L949.6,193.6z M957.8,209.4v-4.9l-1.8,3.9l-1.5-0.9
l-0.8-5.4l-0.9,4.3l-2.1-1.3l1.7-6.2l2.6,1.6l0.6,3.8l1.2-2.7l2.6,1.5l0.4,7.5L957.8,209.4z"/>
</g>
</g>
<g id="XMLID_129_">
<g id="XMLID_37_">
<path id="XMLID_38_" class="st0" d="M961.9,200.6v13.9l11.9-6.9v-13.9L961.9,200.6z M971.3,205.3c-0.1,0.9-0.5,1.9-1.1,2.5
c-0.2,0.2-0.5,0.5-0.9,0.7c-0.1,0.1-0.3,0.2-0.5,0.2l-0.4,0.2l-0.3,0.1c-0.4,0.2-0.9,0.3-1.4,0.5c-0.2,0.1-0.4,0.1-0.6,0.2
l-0.8,0.2l0-2l1.2-0.3c0,0,0.5-0.1,0.7-0.2c0.3-0.1,0.5-0.2,0.7-0.3c0.8-0.3,1.2-0.7,1.3-1.1c0.1-0.2,0.1-0.3,0-0.4
c-0.1-0.1-0.2-0.2-0.5-0.2c-0.1,0-0.2,0-0.4,0l-0.5,0c-0.2,0-0.4,0.1-0.5,0.1c-0.2,0-0.4,0-0.6,0c-0.4,0-0.8-0.1-1-0.2
c-0.2-0.1-0.4-0.4-0.5-0.7c-0.1-0.2-0.1-0.5,0-0.8c0.1-0.9,0.4-1.7,1.1-2.3c0.3-0.2,0.5-0.5,0.9-0.7c0.1-0.1,0.3-0.2,0.5-0.2
l0.5-0.2l0.3-0.1c0.4-0.1,0.7-0.3,1.2-0.4l0.5-0.2l0.6-0.2l0.1,1.8c-0.2,0-0.5,0.1-0.9,0.2c-0.4,0.1-0.9,0.3-1.2,0.4
c-0.8,0.3-1.1,0.6-1.2,1c0,0.2,0,0.3,0,0.5c0,0.2,0,0.2,0.4,0.3c0.1,0,0.3,0,0.4,0l1.1-0.1c0.2,0,0.4,0,0.5,0
c0.4,0,0.7,0.1,0.9,0.2c0.2,0.1,0.4,0.4,0.5,0.7C971.3,204.7,971.3,205,971.3,205.3z"/>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="947.2 183.1 174 35" style="enable-background:new 947.2 183.1 174 35;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<g id="XMLID_140_">
<path id="XMLID_166_" class="st0" d="M998.3,209.6l-1.1-11.2l-3.1,11.2h-3.6l-3-11.2l-1.1,11.2l-5,0l2.4-16.5h6.3l2.2,7.8l2.2-7.8
h6.2l2.5,16.5H998.3z"/>
<path id="XMLID_163_" class="st0" d="M1015.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1015.1,200.7,1015.2,201.6,1015.2,202.7z M1011.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_160_" class="st0" d="M1037.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1037.2,200.7,1037.3,201.6,1037.3,202.7z M1033.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1L1033.9,202.1z"/>
<path id="XMLID_157_" class="st0" d="M1098.2,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1098.1,200.7,1098.2,201.6,1098.2,202.7z M1094.8,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_154_" class="st0" d="M1120.3,202.7v2.1h-6.5l0,0.4c0,0.8,0.3,1.3,0.8,1.4c0.4,0.1,0.8,0.2,1.3,0.2h0.3
c1.3,0,3.1-0.3,4.5-0.6h0l-0.5,2.9c0,0-0.6,0.3-1.8,0.5c-0.8,0.1-1.6,0.1-2.6,0.1c-2.6,0-4.2-0.9-4.9-2.6
c-0.3-0.7-0.5-1.4-0.5-2.2v-2.8c0-2.1,1-3.4,2.7-4.1c0.8-0.3,1.6-0.4,2.4-0.4c2.4,0,4,0.8,4.7,2.4
C1120.2,200.7,1120.3,201.6,1120.3,202.7z M1116.9,202.1c0-0.8-0.2-1.4-0.6-1.5c-0.3-0.2-0.7-0.2-1-0.2c-1,0-1.4,0.5-1.4,1.4
l-0.1,0.6h3.1V202.1z"/>
<path id="XMLID_152_" class="st0" d="M1025.7,209.6h-2.1c-0.4,0-0.7,0-0.9,0c-0.7-0.1-1.3-0.2-1.7-0.4c-0.5-0.3-1-0.7-1.2-1.2
c-0.3-0.6-0.5-1.4-0.5-2.5l0-4.6h-1.7v-3h1.7v-2.3l1.7,0l1.8-0.2v2.6h2.7l-0.4,3h-2.3v4.4c0,0.1,0,0.2,0,0.3c0,0.2,0,0.3,0.1,0.5
c0.1,0.3,0.3,0.5,0.7,0.6c0.3,0.1,1.9,0.1,1.9,0.1L1025.7,209.6z"/>
<path id="XMLID_150_" class="st0" d="M1047.7,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1046.7,197.8,1047.3,197.9,1047.7,197.9z"/>
<path id="XMLID_148_" class="st0" d="M1108.9,197.9l-0.1,2.8c-0.3,0-1.2,0-2.1,0.2c-0.8,0.2-1.7,0.6-2.1,0.8v8h-3.5v-11.8h3.3
c0,0-0.1,0.6,0,0.9l0.8-0.3c0.5-0.2,1-0.3,1.7-0.4C1107.9,197.8,1108.5,197.9,1108.9,197.9z"/>
<path id="XMLID_146_" class="st0" d="M1054.6,210.1c-0.8,0-1.8-0.1-2.7-0.2c-0.4,0-0.8-0.1-1.2-0.2l-1.6-0.2l0-4.1l2.4,0.4
c0,0,1,0.1,1.4,0.2c0.5,0.1,1,0.1,1.4,0.1c1.5,0,2.4-0.4,2.7-1.1c0.1-0.3,0.1-0.6,0-0.9c-0.2-0.3-0.5-0.6-0.9-0.8
c-0.2-0.1-0.5-0.2-0.8-0.3l-0.9-0.3c-0.4-0.1-0.8-0.2-1.1-0.3c-0.4-0.1-0.8-0.3-1.1-0.4c-0.8-0.4-1.5-0.8-2-1.3
c-0.4-0.5-0.7-1.1-0.9-1.8c-0.1-0.5-0.1-1.1,0-1.7c0.2-1.8,1-3.1,2.3-3.8c0.5-0.3,1.1-0.5,1.8-0.6c0.3,0,0.6-0.1,0.9-0.1l0.9-0.1
h0.6c0.7,0,1.5,0,2.3,0.1l1,0.1l1.2,0.2l0,3.6c-0.5-0.1-1-0.2-1.7-0.3c-0.9-0.1-1.7-0.2-2.4-0.2c-1.6,0-2.2,0.3-2.5,1.1
c-0.1,0.3-0.1,0.6-0.1,0.9c0,0.4,0,0.5,0.7,0.8c0.2,0.1,0.5,0.2,0.8,0.3l2.1,0.6c0.4,0.1,0.8,0.3,1.1,0.4c0.8,0.3,1.4,0.7,1.8,1.1
c0.5,0.5,0.8,1.1,0.9,1.8c0.1,0.5,0.1,1.1,0,1.7c-0.2,1.8-1.1,3.4-2.4,4.1c-0.5,0.3-1.1,0.5-1.8,0.6c-0.3,0-0.6,0.1-0.9,0.1
l-0.8,0.1L1054.6,210.1L1054.6,210.1z"/>
<path id="XMLID_143_" class="st0" d="M1063.6,197.8h3.2l-0.1,0.8c0.3-0.2,0.6-0.3,1-0.4c0.4-0.2,0.8-0.3,1.1-0.3
c0.9-0.2,1.8-0.2,2.7,0.1c0.5,0.2,1,0.5,1.4,1c0.2,0.2,0.3,0.4,0.4,0.6l0.1,0.2c0,0.1,0.1,0.1,0.1,0.2c0.2,0.4,0.3,0.9,0.4,1.4
c0.1,0.6,0.2,1.3,0.2,2.2v0.7c0,1.8-0.1,2.7-0.4,3.4c-0.5,1.1-1.6,1.6-3.7,1.7c-0.3,0-0.7,0-1.2,0l-0.8,0c-0.3,0-0.5,0-0.7,0v5.1
l-3.6,0.2V197.8L1063.6,197.8z M1067.2,206.1c0.7,0.1,1.1,0.1,1.7,0.1l0.6-0.1c0.7-0.2,1-0.6,1-1.3v-2.3c0-0.3,0-0.6-0.1-0.7
c-0.2-0.7-0.8-1-1.8-0.9l-0.4,0.1l-0.7,0.2l-0.3,0.1L1067.2,206.1L1067.2,206.1z"/>
<path id="XMLID_141_" class="st0" d="M1086.5,203.2v6.4h-3.6v-7c0-0.4-0.3-1.1-0.8-1.4c-0.4-0.2-1-0.2-1.2-0.1
c-0.5,0.1-1,0.4-1.3,0.6v7.9h-3.6v-17l3.6,0v6c0.4-0.2,0.9-0.3,1.4-0.4c0.4-0.1,0.8-0.1,1.2-0.1c2.1,0,3.2,0.6,3.7,1.7
C1086.2,200.2,1086.5,201.4,1086.5,203.2z"/>
</g>
<g id="XMLID_136_">
<path id="XMLID_137_" class="st0" d="M961.7,184.4l13.7,8v16.1l-13.7,8l-13.7-8v-16.1L961.7,184.4 M961.7,183.9l-14.1,8.3v16.5
l14.1,8.3l14.1-8.3v-16.5L961.7,183.9L961.7,183.9z"/>
</g>
<polygon id="XMLID_132_" class="st0" points="949.8,193.2 961.7,186.3 973.6,193.2 961.7,200.2 "/>
<g id="XMLID_278_">
<g id="XMLID_42_">
<path id="XMLID_43_" class="st0" d="M949.6,193.6v13.9l11.9,6.9v-13.9L949.6,193.6z M957.8,209.4v-4.9l-1.8,3.9l-1.5-0.9
l-0.8-5.4l-0.9,4.3l-2.1-1.3l1.7-6.2l2.6,1.6l0.6,3.8l1.2-2.7l2.6,1.5l0.4,7.5L957.8,209.4z"/>
</g>
</g>
<g id="XMLID_129_">
<g id="XMLID_37_">
<path id="XMLID_38_" class="st0" d="M961.9,200.6v13.9l11.9-6.9v-13.9L961.9,200.6z M971.3,205.3c-0.1,0.9-0.5,1.9-1.1,2.5
c-0.2,0.2-0.5,0.5-0.9,0.7c-0.1,0.1-0.3,0.2-0.5,0.2l-0.4,0.2l-0.3,0.1c-0.4,0.2-0.9,0.3-1.4,0.5c-0.2,0.1-0.4,0.1-0.6,0.2
l-0.8,0.2l0-2l1.2-0.3c0,0,0.5-0.1,0.7-0.2c0.3-0.1,0.5-0.2,0.7-0.3c0.8-0.3,1.2-0.7,1.3-1.1c0.1-0.2,0.1-0.3,0-0.4
c-0.1-0.1-0.2-0.2-0.5-0.2c-0.1,0-0.2,0-0.4,0l-0.5,0c-0.2,0-0.4,0.1-0.5,0.1c-0.2,0-0.4,0-0.6,0c-0.4,0-0.8-0.1-1-0.2
c-0.2-0.1-0.4-0.4-0.5-0.7c-0.1-0.2-0.1-0.5,0-0.8c0.1-0.9,0.4-1.7,1.1-2.3c0.3-0.2,0.5-0.5,0.9-0.7c0.1-0.1,0.3-0.2,0.5-0.2
l0.5-0.2l0.3-0.1c0.4-0.1,0.7-0.3,1.2-0.4l0.5-0.2l0.6-0.2l0.1,1.8c-0.2,0-0.5,0.1-0.9,0.2c-0.4,0.1-0.9,0.3-1.2,0.4
c-0.8,0.3-1.1,0.6-1.2,1c0,0.2,0,0.3,0,0.5c0,0.2,0,0.2,0.4,0.3c0.1,0,0.3,0,0.4,0l1.1-0.1c0.2,0,0.4,0,0.5,0
c0.4,0,0.7,0.1,0.9,0.2c0.2,0.1,0.4,0.4,0.5,0.7C971.3,204.7,971.3,205,971.3,205.3z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -87,13 +87,17 @@ export default {
}
}
},
methods: {
registerEvents() {
ApiEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
}
},
mounted() {
let self = this;
ApiEvent.$on(LIST_CHANGE, () => {
self.$refs.projectRecent.recent();
self.$refs.testRecent.recent();
self.$refs.reportRecent.recent();
});
this.registerEvents();
}
}

View File

@ -171,7 +171,7 @@ export default {
<style scoped>
.report-container {
height: calc(100vh - 150px);
height: calc(100vh - 155px);
min-height: 600px;
overflow-y: auto;
}

View File

@ -379,7 +379,7 @@ export default {
<style scoped>
.test-container {
height: calc(100vh - 150px);
height: calc(100vh - 155px);
min-height: 600px;
}

View File

@ -17,7 +17,7 @@
<el-table border :data="tableData" class="adjust-table table-content" @sort-change="sort"
@row-click="handleView"
@filter-change="filter" @select-all="handleSelectAll" @select="selectionChange">
@filter-change="filter" @select-all="select" @select="select">
<el-table-column
type="selection"></el-table-column>
<el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip>
@ -127,18 +127,7 @@
create() {
this.$router.push('/api/test/create');
},
handleSelectAll(selection) {
this.selectIds.clear()
this.selectProjectNames.clear()
this.selectProjectId.clear()
selection.forEach(s => {
this.selectIds.add(s.id)
this.selectProjectNames.add(s.projectName)
this.selectProjectId.add(s.projectId)
})
},
selectionChange(selection) {
select(selection) {
this.selectIds.clear()
this.selectProjectNames.clear()
this.selectProjectId.clear()

View File

@ -26,7 +26,7 @@
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {getUUID} from "../../../../common/js/utils";
import {getUUID} from "@/common/js/utils";
export default {
@ -95,13 +95,12 @@
},
checkName(callback) {
for (let i of this.selectProjectId) {
this.result = this.$post('/api/checkName', {name: this.ruleForm.testName, projectId: i}, response => {
this.result = this.$post('/api/checkName', {name: this.ruleForm.testName, projectId: i}, () => {
if (callback) callback();
})
}
},
_getEnvironmentAndRunTest: function (item) {
let count = 0;
this.result = this.$get('/api/environment/list/' + item.projectId, response => {
let environments = response.data;
let environmentMap = new Map();
@ -131,7 +130,6 @@
scenarioDefinition: JSON.parse(item.scenarioDefinition),
schedule: {},
});
console.log(test)
this.test = this.test || test;
if (this.tests.length > 1) {
this.test.scenarioDefinition = this.test.scenarioDefinition.concat(test.scenarioDefinition);

View File

@ -1,8 +1,10 @@
<template>
<el-dialog :title="$t('api_test.environment.environment_config')" :visible.sync="visible" class="environment-dialog"
<el-dialog :close-on-click-modal="false" :title="$t('api_test.environment.environment_config')"
:visible.sync="visible" class="environment-dialog"
@close="close" append-to-body ref="environmentConfig">
<el-container v-loading="result.loading">
<ms-aside-item :enable-aside-hidden="false" :title="$t('api_test.environment.environment_list')" :data="environments" :item-operators="environmentOperators" :add-fuc="addEnvironment"
<ms-aside-item :enable-aside-hidden="false" :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" @close="close"/>
</el-container>
@ -10,136 +12,145 @@
</template>
<script>
import MsApiCollapse from "./collapse/ApiCollapse";
import MsApiCollapseItem from "./collapse/ApiCollapseItem";
import draggable from 'vuedraggable';
import MsContainer from "../../../common/components/MsContainer";
import MsAsideContainer from "../../../common/components/MsAsideContainer";
import MsMainContainer from "../../../common/components/MsMainContainer";
import MsAsideItem from "../../../common/components/MsAsideItem";
import EnvironmentEdit from "./environment/EnvironmentEdit";
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
import MsApiCollapse from "./collapse/ApiCollapse";
import MsApiCollapseItem from "./collapse/ApiCollapseItem";
import draggable from 'vuedraggable';
import MsContainer from "../../../common/components/MsContainer";
import MsAsideContainer from "../../../common/components/MsAsideContainer";
import MsMainContainer from "../../../common/components/MsMainContainer";
import MsAsideItem from "../../../common/components/MsAsideItem";
import EnvironmentEdit from "./environment/EnvironmentEdit";
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
export default {
name: "ApiEnvironmentConfig",
components: {
EnvironmentEdit,
MsAsideItem,
MsMainContainer, MsAsideContainer, MsContainer, MsApiCollapseItem, MsApiCollapse, draggable},
data() {
return {
result: {},
visible:false,
projectId: '',
environments: [],
currentEnvironment: {variables: [{}], headers: [{}], protocol: 'https', projectId: this.projectId},
environmentOperators: [
{
icon: 'el-icon-document-copy',
func: this.copyEnvironment
},
{
icon: 'el-icon-delete',
func: this.deleteEnvironment
}
]
export default {
name: "ApiEnvironmentConfig",
components: {
EnvironmentEdit,
MsAsideItem,
MsMainContainer, MsAsideContainer, MsContainer, MsApiCollapseItem, MsApiCollapse, draggable
},
data() {
return {
result: {},
visible: false,
projectId: '',
environments: [],
currentEnvironment: {variables: [{}], headers: [{}], protocol: 'https', projectId: this.projectId, hosts: [{}]},
environmentOperators: [
{
icon: 'el-icon-document-copy',
func: this.copyEnvironment
},
{
icon: 'el-icon-delete',
func: this.deleteEnvironment
}
]
}
},
methods: {
open: function (projectId) {
this.visible = true;
this.projectId = projectId;
this.getEnvironments();
listenGoBack(this.close);
},
deleteEnvironment(environment, index) {
if (environment.id) {
this.result = this.$get('/api/environment/delete/' + environment.id, () => {
this.$success(this.$t('commons.delete_success'));
this.getEnvironments();
});
}
else {
this.environments.splice(index, 1);
}
},
methods: {
open: function (projectId) {
this.visible = true;
this.projectId = projectId;
this.getEnvironments();
listenGoBack(this.close);
},
deleteEnvironment(environment, index) {
if (environment.id) {
this.result = this.$get('/api/environment/delete/' + environment.id, () => {
this.$success(this.$t('commons.delete_success'));
this.getEnvironments();
});
}
else {
this.environments.splice(index, 1);
}
},
copyEnvironment(environment) {
if (!environment.id) {
this.$warning(this.$t('commons.please_save'))
return;
}
let newEnvironment = {};
Object.assign(newEnvironment, environment);
newEnvironment.id = null;
newEnvironment.name = this.getNoRepeatName(newEnvironment.name);
if (!this.validateEnvironment(newEnvironment)) {
return;
}
this.$refs.environmentEdit._save(newEnvironment);
this.environments.push(newEnvironment);
this.$refs.environmentItems.itemSelected(this.environments.length -1 , newEnvironment);
},
validateEnvironment(environment) {
if (!environment.name || !!environment.name && environment.name.length > 64) {
this.$error(this.$t('commons.input_limit', [1, 64]));
return false;
}
if (!this.$refs.environmentEdit.validateSocket(environment.socket)) {
this.$error(this.$t('commons.formatErr'));
return false;
}
return true;
},
getNoRepeatName(name) {
for (let i in this.environments) {
if (this.environments[i].name === name) {
return this.getNoRepeatName(name + ' copy');
}
}
return name;
},
addEnvironment() {
let newEnvironment = this.getDefaultEnvironment();
this.environments.push(newEnvironment);
this.$refs.environmentItems.itemSelected(this.environments.length -1 , newEnvironment);
},
environmentSelected(environment) {
this.getEnvironment(environment);
},
getEnvironments() {
if (this.projectId) {
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data;
if (this.environments.length > 0) {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
} else {
let item = this.getDefaultEnvironment();
this.environments.push(item);
this.$refs.environmentItems.itemSelected(0, item);
}
});
}
},
getEnvironment(environment) {
if (!(environment.variables instanceof Array)) {
environment.variables = JSON.parse(environment.variables);
}
if (!(environment.headers instanceof Array)) {
environment.headers = JSON.parse(environment.headers);
}
this.currentEnvironment = environment;
},
getDefaultEnvironment() {
return {variables: [{}], headers: [{}], protocol: 'https', projectId: this.projectId};
},
close() {
this.$emit('close');
this.visible = false;
this.$refs.environmentEdit.clearValidate();
removeGoBackListener(this.close);
copyEnvironment(environment) {
if (!environment.id) {
this.$warning(this.$t('commons.please_save'))
return;
}
let newEnvironment = {};
Object.assign(newEnvironment, environment);
newEnvironment.id = null;
newEnvironment.name = this.getNoRepeatName(newEnvironment.name);
if (!this.validateEnvironment(newEnvironment)) {
return;
}
this.$refs.environmentEdit._save(newEnvironment);
this.environments.push(newEnvironment);
this.$refs.environmentItems.itemSelected(this.environments.length - 1, newEnvironment);
},
validateEnvironment(environment) {
if (!environment.name || !!environment.name && environment.name.length > 64) {
this.$error(this.$t('commons.input_limit', [1, 64]));
return false;
}
if (!this.$refs.environmentEdit.validateSocket(environment.socket)) {
this.$error(this.$t('commons.formatErr'));
return false;
}
return true;
},
getNoRepeatName(name) {
for (let i in this.environments) {
if (this.environments[i].name === name) {
return this.getNoRepeatName(name + ' copy');
}
}
return name;
},
addEnvironment() {
let newEnvironment = this.getDefaultEnvironment();
this.environments.push(newEnvironment);
this.$refs.environmentItems.itemSelected(this.environments.length - 1, newEnvironment);
},
environmentSelected(environment) {
this.getEnvironment(environment);
},
getEnvironments() {
if (this.projectId) {
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data;
if (this.environments.length > 0) {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
} else {
let item = this.getDefaultEnvironment();
this.environments.push(item);
this.$refs.environmentItems.itemSelected(0, item);
}
});
}
},
getEnvironment(environment) {
if (!(environment.variables instanceof Array)) {
environment.variables = JSON.parse(environment.variables);
}
if (!(environment.headers instanceof Array)) {
environment.headers = JSON.parse(environment.headers);
}
if(environment.hosts === undefined || environment.hosts ===null || environment.hosts ===''){
environment.hosts = [];
environment.enable =false;
}
else if (!(environment.hosts instanceof Array)) {
environment.hosts = JSON.parse(environment.hosts);
environment.enable =true;
}
this.currentEnvironment = environment;
},
getDefaultEnvironment() {
return {variables: [{}], headers: [{}], protocol: 'https', projectId: this.projectId, hosts: [{}]};
},
close() {
this.$emit('close');
this.visible = false;
this.$refs.environmentEdit.clearValidate();
removeGoBackListener(this.close);
}
}
}
</script>
<style scoped>

View File

@ -0,0 +1,166 @@
<template>
<div>
<el-card class="table-card">
<el-table :data="hostTable" style="width: 100%" @cell-dblclick="dblHostTable" class="ht-tb">
<el-table-column prop="ip" label="IP">
<template slot-scope="scope">
<el-input v-if="scope.row.status" v-model="scope.row.ip"></el-input>
<span v-else>{{scope.row.ip}}</span>
</template>
</el-table-column>
<el-table-column prop="domain" :label="$t('load_test.domain')">
<template slot-scope="scope">
<el-input v-if="scope.row.status" v-model="scope.row.domain"></el-input>
<span v-else>{{scope.row.domain}}</span>
</template>
</el-table-column>
<el-table-column prop="annotation" :label="$t('commons.annotation')">
<template slot-scope="scope">
<el-input v-if="scope.row.status" v-model="scope.row.annotation"></el-input>
<span v-else>{{scope.row.annotation}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" width="100">
<template v-slot:default="scope">
<span>
<el-button size="mini" p="$t('commons.remove')" icon="el-icon-close" circle @click="remove(scope.row)"
class="ht-btn-remove"/>
<el-button size="mini" p="$t('commons.save')" icon="el-icon-check" circle @click="confirm(scope.row)"
class="ht-btn-confirm"/>
</span>
</template>
</el-table-column>
</el-table>
<el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-circle-plus-outline" @click="add"
>添加
</el-button>
</el-card>
</div>
</template>
<script>
import MsApiVariableInput from "./ApiVariableInput";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
export default {
name: "MsApiHostTable",
components: {MsApiVariableInput, MsTableOperatorButton},
props: {
hostTable: Array,
},
data() {
return {
currentPage: 1,
pageSize: 5,
total: 0,
}
},
created() {
this.init();
},
methods: {
remove: function (row) {
this.hostTable.splice(this.hostTable.indexOf(row), 1);
},
change: function () {
this.$emit('change', this.hostTable);
},
add: function (r) {
let row = {
ip: '',
domain: '',
status: 'edit',
annotation: '',
uuid: this.uuid(),
}
//
for (let i = this.hostTable.length - 1; i >= 0; i--) {
if (this.hostTable[i].status === '') {
row.ip = this.hostTable[i].ip;
row.domain = this.hostTable[i].domain;
row.annotation = this.hostTable[i].annotation;
break;
}
}
this.hostTable.push(row);
},
confirm: function (row) {
this.validateIp(row.ip) && this.validateDomain(row.domain) ? row.status = '' : row.status;
this.$emit('change', this.hostTable);
if (row.status === "") {
return true;
}
return false;
},
init: function () {
if (this.hostTable === undefined || this.hostTable.length === 0) {
let row = {
ip: '',
domain: '',
status: 'edit',
annotation: '',
uuid: this.uuid()
}
this.hostTable.push(row);
}
},
validateIp(ip) {
let regexp = /^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$/;
if (regexp.test(ip) == false) {
this.$warning(this.$t('load_test.input_ip'));
return false;
}
return true;
},
validateDomain(domain) {
let url = {};
try {
url = new URL(domain);
} catch (e) {
this.$warning(this.$t('load_test.input_domain'));
return false
}
return true;
},
dblHostTable: function (row) {
row.status = 'edit';
},
uuid: function () {
return (((1 + Math.random()) * 0x100000) | 0).toString(16).substring(1);
},
}
}
</script>
<style scoped>
.ht-btn-remove {
color: white;
background-color: #DCDFE6;
}
.ht-btn-confirm {
color: white;
background-color: #1483F6;
}
.ht-btn-add {
border: 0px;
margin-top: 10px;
color: #1483F6;
background-color: white;
}
.ht-tb {
background-color: white;
border: 0px;
}
</style>

View File

@ -5,6 +5,11 @@
</span>
<div class="kv-row" v-for="(item, index) in items" :key="index">
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
<el-col v-if="isShowEnable" class="kv-checkbox">
<input type="checkbox" v-if="!isDisable(index)" @change="change" :value="item.uuid" v-model="checkedValues"
:disabled="isDisable(index) || isReadOnly"/>
</el-col>
<el-col>
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
@change="change"
@ -28,92 +33,129 @@
</template>
<script>
import {KeyValue} from "../model/ScenarioModel";
import {KeyValue} from "../model/ScenarioModel";
export default {
name: "MsApiKeyValue",
export default {
name: "MsApiKeyValue",
props: {
keyPlaceholder: String,
valuePlaceholder: String,
description: String,
items: Array,
isReadOnly: {
type: Boolean,
default: false
props: {
keyPlaceholder: String,
valuePlaceholder: String,
description: String,
isShowEnable: Boolean,
items: Array,
isReadOnly: {
type: Boolean,
default: false
},
suggestions: Array
},
suggestions: Array
},
computed: {
keyText() {
return this.keyPlaceholder || this.$t("api_test.key");
},
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
methods: {
remove: function (index) {
this.items.splice(index, 1);
this.$emit('change', this.items);
},
change: function () {
let isNeedCreate = true;
let removeIndex = -1;
this.items.forEach((item, index) => {
if (!item.name && !item.value) {
//
if (index !== this.items.length - 1) {
removeIndex = index;
}
//
isNeedCreate = false;
}
});
if (isNeedCreate) {
this.items.push(new KeyValue());
data() {
return {
checkedValues: []
}
this.$emit('change', this.items);
// TODO key
},
isDisable: function (index) {
return this.items.length - 1 === index;
computed: {
keyText() {
return this.keyPlaceholder || this.$t("api_test.key");
},
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
querySearch(queryString, cb) {
let suggestions = this.suggestions;
let results = queryString ? suggestions.filter(this.createFilter(queryString)) : suggestions;
cb(results);
methods: {
remove: function (index) {
if (this.isShowEnable) {
//
let checkIndex = this.checkedValues.indexOf(this.items[index].uuid);
checkIndex != -1 ? this.checkedValues.splice(checkIndex, 1) : this.checkedValues;
}
//
this.items.splice(index, 1);
this.$emit('change', this.items);
},
change: function () {
let isNeedCreate = true;
let removeIndex = -1;
this.items.forEach((item, index) => {
//
if (this.isShowEnable) {
item.enable = this.checkedValues.indexOf(item.uuid) != -1 ? true : false;
}
if (!item.name && !item.value) {
//
if (index !== this.items.length - 1) {
removeIndex = index;
}
//
isNeedCreate = false;
}
});
if (isNeedCreate) {
//
if (this.isShowEnable) {
this.items[this.items.length - 1].enable = true;
// v-model
this.checkedValues.push(this.items[this.items.length - 1].uuid);
}
this.items.push(new KeyValue());
}
this.$emit('change', this.items);
// TODO key
},
isDisable: function (index) {
return this.items.length - 1 === index;
},
querySearch(queryString, cb) {
let suggestions = this.suggestions;
let results = queryString ? suggestions.filter(this.createFilter(queryString)) : suggestions;
cb(results);
},
uuid: function () {
return (((1 + Math.random()) * 0x100000) | 0).toString(16).substring(1);
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
};
},
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
};
},
},
created() {
if (this.items.length === 0) {
this.items.push(new KeyValue());
created() {
if (this.items.length === 0) {
this.items.push(new KeyValue());
} else if (this.isShowEnable) {
this.items.forEach((item, index) => {
let uuid = this.uuid();
item.uuid = uuid;
if (item.enable) {
this.checkedValues.push(uuid);
}
})
}
}
}
}
</script>
<style scoped>
.kv-description {
font-size: 13px;
}
.kv-description {
font-size: 13px;
}
.kv-row {
margin-top: 10px;
}
.kv-row {
margin-top: 10px;
}
.kv-delete {
width: 60px;
}
.kv-checkbox {
width: 20px;
margin-right: 10px;
}
.el-autocomplete {
width: 100%;
}
.kv-delete {
width: 60px;
}
.el-autocomplete {
width: 100%;
}
</style>

View File

@ -207,7 +207,12 @@ export default {
});
this.scenarios.forEach(scenario => {
if (scenario.environmentId) {
scenario.environment = environmentMap.get(scenario.environmentId);
let env = environmentMap.get(scenario.environmentId);
if (!env) {
scenario.environmentId = undefined;
} else {
scenario.environment = env;
}
}
});
});

View File

@ -5,12 +5,18 @@
</span>
<div class="kv-row" v-for="(item, index) in parameters" :key="index">
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
<el-col class="kv-checkbox">
<input type="checkbox" v-if="!isDisable(index)" @change="change" :value="item.uuid" v-model="checkedValues"
:disabled="isDisable(index) || isReadOnly"/>
</el-col>
<el-col>
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200" @change="change" :placeholder="keyText" show-word-limit>
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
@change="change" :placeholder="keyText" show-word-limit>
<template v-slot:prepend>
<el-select v-if="type === 'body'" :disabled="isReadOnly" class="kv-type" v-model="item.type">
<el-option value="text" />
<el-select v-if="type === 'body'" :disabled="isReadOnly" class="kv-type" v-model="item.type">
<el-option value="text"/>
<el-option value="file"/>
</el-select>
</template>
@ -31,12 +37,12 @@
value-key="name"
highlight-first-item
@select="change">
<i slot="suffix" class="el-input__icon el-icon-edit" style="cursor: pointer;" @click="advanced(item)"></i>
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced(item)"></i>
</el-autocomplete>
</el-col>
<el-col v-if="item.type === 'file'">
<ms-api-body-file-upload :parameter="item"/>
<ms-api-body-file-upload :parameter="item"/>
</el-col>
<el-col class="kv-delete">
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
@ -51,107 +57,129 @@
</template>
<script>
import {KeyValue, Scenario} from "../model/ScenarioModel";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
import MsApiVariableAdvance from "@/business/components/api/test/components/ApiVariableAdvance";
import MsApiBodyFileUpload from "./body/ApiBodyFileUpload";
import {KeyValue, Scenario} from "../model/ScenarioModel";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
import MsApiVariableAdvance from "@/business/components/api/test/components/ApiVariableAdvance";
import MsApiBodyFileUpload from "./body/ApiBodyFileUpload";
export default {
name: "MsApiVariable",
components: {MsApiBodyFileUpload, MsApiVariableAdvance},
props: {
keyPlaceholder: String,
valuePlaceholder: String,
description: String,
parameters: Array,
environment: Object,
scenario: Scenario,
type: {
type: String,
default: ''
export default {
name: "MsApiVariable",
components: {MsApiBodyFileUpload, MsApiVariableAdvance},
props: {
keyPlaceholder: String,
valuePlaceholder: String,
description: String,
parameters: Array,
environment: Object,
scenario: Scenario,
type: {
type: String,
default: ''
},
isReadOnly: {
type: Boolean,
default: false
},
suggestions: Array
},
isReadOnly: {
type: Boolean,
default: false
},
suggestions: Array
},
data() {
return {
currentItem: null,
}
},
computed: {
keyText() {
return this.keyPlaceholder || this.$t("api_test.key");
},
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
methods: {
remove: function (index) {
this.parameters.splice(index, 1);
this.$emit('change', this.parameters);
},
change: function () {
let isNeedCreate = true;
let removeIndex = -1;
this.parameters.forEach((item, index) => {
if (!item.name && !item.value) {
//
if (index !== this.parameters.length - 1) {
removeIndex = index;
}
//
isNeedCreate = false;
}
});
if (isNeedCreate) {
this.parameters.push(new KeyValue(null, null, 'text'));
data() {
return {
currentItem: null,
checkedValues: []
}
this.$emit('change', this.parameters);
// TODO key
},
isDisable: function (index) {
return this.parameters.length - 1 === index;
},
querySearch(queryString, cb) {
let suggestions = this.suggestions;
let results = queryString ? suggestions.filter(this.createFilter(queryString)) : suggestions;
cb(results);
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
};
},
funcSearch(queryString, cb) {
let funcs = MOCKJS_FUNC.concat(JMETER_FUNC);
let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs;
// callback
cb(results);
},
funcFilter(queryString) {
return (func) => {
return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1);
};
computed: {
keyText() {
return this.keyPlaceholder || this.$t("api_test.key");
},
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
methods: {
remove: function (index) {
//
let checkIndex = this.checkedValues.indexOf(this.parameters[index].uuid);
checkIndex != -1 ? this.checkedValues.splice(checkIndex, 1) : this.checkedValues;
//
this.parameters.splice(index, 1);
this.$emit('change', this.parameters);
},
change: function () {
let isNeedCreate = true;
let removeIndex = -1;
this.parameters.forEach((item, index) => {
//
item.enable = this.checkedValues.indexOf(item.uuid) != -1 ? true : false;
advanced(item) {
this.$refs.variableAdvance.open();
this.currentItem = item;
this.itemValue = '';
this.mockVariableFuncs = [];
},
if (!item.name && !item.value) {
//
if (index !== this.parameters.length - 1) {
removeIndex = index;
}
//
isNeedCreate = false;
}
});
if (isNeedCreate) {
//
this.parameters[this.parameters.length - 1].enable = true;
// v-model
this.checkedValues.push(this.parameters[this.parameters.length - 1].uuid);
this.parameters.push(new KeyValue(null, null, 'text', false, this.uuid()));
}
this.$emit('change', this.parameters);
// TODO key
},
isDisable: function (index) {
return this.parameters.length - 1 === index;
},
querySearch(queryString, cb) {
let suggestions = this.suggestions;
let results = queryString ? suggestions.filter(this.createFilter(queryString)) : suggestions;
cb(results);
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
};
},
funcSearch(queryString, cb) {
let funcs = MOCKJS_FUNC.concat(JMETER_FUNC);
let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs;
// callback
cb(results);
},
funcFilter(queryString) {
return (func) => {
return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1);
};
},
uuid: function () {
return (((1 + Math.random()) * 0x100000) | 0).toString(16).substring(1);
},
advanced(item) {
this.$refs.variableAdvance.open();
this.currentItem = item;
this.itemValue = '';
this.mockVariableFuncs = [];
},
},
created() {
if (this.parameters.length === 0) {
this.parameters.push(new KeyValue(null, null, 'text'));
},
created() {
if (this.parameters.length === 0) {
this.parameters.push(new KeyValue(null, null, 'text', false, this.uuid()));
} else {
this.parameters.forEach((item, index) => {
let uuid = this.uuid();
item.uuid = uuid;
if (item.enable) {
this.checkedValues.push(uuid);
}
})
}
}
}
}
</script>
<style scoped>
@ -171,6 +199,11 @@ export default {
width: 100%;
}
.kv-checkbox {
width: 20px;
margin-right: 10px;
}
.advanced-item-value >>> .el-dialog__body {
padding: 15px 25px;
}
@ -182,4 +215,9 @@ export default {
.kv-type {
width: 70px;
}
.pointer {
cursor: pointer;
color: #1E90FF;
}
</style>

View File

@ -1,13 +1,12 @@
<template>
<el-main v-loading="result.loading">
<el-form :model="environment" :rules="rules" ref="from">
<el-form :model="environment" :rules="rules" ref="environment">
<span>{{$t('api_test.environment.name')}}</span>
<el-form-item
prop="name">
<el-input v-model="environment.name" :placeholder="this.$t('commons.input_name')" clearable></el-input>
</el-form-item>
<span>{{$t('api_test.environment.socket')}}</span>
<el-form-item
prop="socket">
@ -21,6 +20,15 @@
</el-input>
</el-form-item>
<el-form-item>
<el-switch
v-model="envEnable"
inactive-text="hosts">
</el-switch>
</el-form-item>
<ms-api-host-table v-if="envEnable" :hostTable="environment.hosts" ref="refHostTable"/>
<span>{{$t('api_test.environment.globalVariable')}}</span>
<ms-api-scenario-variables :show-variable="false" :items="environment.variables"/>
@ -32,111 +40,134 @@
@cancel="cancel"
@confirm="save()"/>
</div>
</el-form>
</el-main>
</template>
<script>
import MsApiScenarioVariables from "../ApiScenarioVariables";
import MsApiKeyValue from "../ApiKeyValue";
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {REQUEST_HEADERS} from "../../../../../../common/js/constants";
import {KeyValue} from "../../model/ScenarioModel";
import MsApiScenarioVariables from "../ApiScenarioVariables";
import MsApiKeyValue from "../ApiKeyValue";
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {REQUEST_HEADERS} from "../../../../../../common/js/constants";
import {KeyValue} from "../../model/ScenarioModel";
import MsApiHostTable from "../ApiHostTable";
export default {
name: "EnvironmentEdit",
components: {MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables},
props: {
environment: {
type: Object,
default() {
return {variables: [{}], headers: [{}], protocol: 'https'};
}
export default {
name: "EnvironmentEdit",
components: {MsApiHostTable, MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables},
props: {
environment: Object,
},
data() {
let socketValidator = (rule, value, callback) => {
if (!this.validateSocket(value)) {
callback(new Error(this.$t('commons.formatErr')));
} else {
callback();
}
},
data() {
let socketValidator = (rule, value, callback) => {
if (!this.validateSocket(value)) {
callback(new Error(this.$t('commons.formatErr')));
}
return {
result: {},
envEnable: false,
rules: {
name: [
{required: true, message: this.$t('commons.input_name'), trigger: 'blur'},
{max: 64, message: this.$t('commons.input_limit', [1, 64]), trigger: 'blur'}
],
socket: [{required: true, validator: socketValidator, trigger: 'blur'}],
},
headerSuggestions: REQUEST_HEADERS
}
},
watch: {
environment: function (o) {
this.envEnable = o.enable;
}
},
methods: {
save() {
this.$refs['environment'].validate((valid) => {
// host
let valHost = true;
if (this.envEnable) {
for (let i = 0; i < this.environment.hosts.length; i++) {
valHost = this.$refs['refHostTable'].confirm(this.environment.hosts[i]);
}
}
if (valid && valHost) {
this._save(this.environment);
} else {
callback();
return false;
}
};
return {
result: {},
rules: {
name :[
{required: true, message: this.$t('commons.input_name'), trigger: 'blur'},
{max: 64, message: this.$t('commons.input_limit', [1, 64]), trigger: 'blur'}
],
socket :[{required: true, validator: socketValidator, trigger: 'blur'}],
},
headerSuggestions: REQUEST_HEADERS
}
});
},
methods: {
save() {
this.$refs['from'].validate((valid) => {
if (valid) {
this._save(this.environment);
} else {
return false;
_save(environment) {
let param = this.buildParam(environment);
let url = '/api/environment/add';
if (param.id) {
url = '/api/environment/update';
}
this.result = this.$post(url, param, response => {
if (!param.id) {
environment.id = response.data;
}
this.$success(this.$t('commons.save_success'));
});
},
buildParam(environment) {
let param = {};
Object.assign(param, environment);
if (!(environment.variables instanceof String)) {
param.variables = JSON.stringify(environment.variables);
}
if (!(environment.headers instanceof String)) {
param.headers = JSON.stringify(environment.headers);
}
if (environment.hosts != undefined && !(environment.hosts instanceof String)) {
let hosts = JSON.parse(JSON.stringify(environment.hosts));
let validHosts = [];
// host
hosts.forEach(host => {
if (host.status === '') {
validHosts.push(host);
}
});
},
_save(environment) {
let param = this.buildParam(environment);
let url = '/api/environment/add';
if (param.id) {
url = '/api/environment/update';
}
this.result = this.$post(url, param, response => {
if (!param.id) {
environment.id = response.data;
}
this.$success(this.$t('commons.save_success'));
});
},
buildParam(environment) {
let param = {};
Object.assign(param, environment);
if (!(environment.variables instanceof String)) {
param.variables = JSON.stringify(environment.variables);
}
if (!(environment.headers instanceof String)) {
param.headers = JSON.stringify(environment.headers);
}
return param;
},
validateSocket(socket) {
if (!socket) return;
let urlStr = this.environment.protocol + '://' + socket;
let url = {};
try {
url = new URL(urlStr);
} catch (e) {
return false
}
this.environment.port = url.port;
this.environment.domain = decodeURIComponent(url.hostname);
if (url.port) {
this.environment.socket = this.environment.domain + ':' + url.port + url.pathname;
} else {
this.environment.socket = this.environment.domain + url.pathname;
}
return true;
},
cancel() {
this.$emit('close');
},
clearValidate() {
this.$refs["from"].clearValidate();
},
environment.hosts = validHosts;
param.hosts = JSON.stringify(validHosts);
}
if (!this.envEnable) {
param.hosts = null;
}
return param;
},
}
validateSocket(socket) {
if (!socket) return;
let urlStr = this.environment.protocol + '://' + socket;
let url = {};
try {
url = new URL(urlStr);
} catch (e) {
return false
}
this.environment.port = url.port;
this.environment.domain = decodeURIComponent(url.hostname);
if (url.port) {
this.environment.socket = this.environment.domain + ':' + url.port + url.pathname;
} else {
this.environment.socket = this.environment.domain + url.pathname;
}
return true;
},
cancel() {
this.$emit('close');
},
clearValidate() {
this.$refs["environment"].clearValidate();
},
},
}
</script>
<style scoped>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog :title="$t('api_test.api_import.title')" :visible.sync="visible" class="api-import" v-loading="result.loading" @close="close">
<el-dialog :close-on-click-modal="false" :title="$t('api_test.api_import.title')" :visible.sync="visible" class="api-import" v-loading="result.loading" @close="close">
<div class="header-bar">
<div>{{$t('api_test.api_import.data_format')}}</div>

View File

@ -2,9 +2,10 @@
<div >
<el-row>
<el-col :span="20" class="script-content">
<ms-code-edit v-if="isCodeEditAlive" mode="java" :read-only="isReadOnly" :data.sync="beanShellProcessor.script" theme="eclipse" :modes="['java']" ref="codeEdit"/>
<ms-code-edit v-if="isCodeEditAlive" :mode="codeEditModeMap[jsr223Processor.language]" :read-only="isReadOnly" :data.sync="jsr223Processor.script" theme="eclipse" :modes="['java','python']" ref="codeEdit"/>
</el-col>
<el-col :span="4" class="script-index">
<ms-dropdown :default-command="jsr223Processor.language" :commands="languages" @command="languageChange"/>
<div class="template-title">{{$t('api_test.request.processor.code_template')}}</div>
<div v-for="(template, index) in codeTemplates" :key="index" class="code-template">
<el-link :disabled="template.disabled" @click="addTemplate(template)">{{template.title}}</el-link>
@ -21,9 +22,13 @@
<script>
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
import MsInstructionsIcon from "../../../../common/components/MsInstructionsIcon";
import MsDropdown from "../../../../common/components/MsDropdown";
export default {
name: "MsBeanShellProcessor",
components: {MsInstructionsIcon, MsCodeEdit},
name: "MsJsr233Processor",
components: {MsDropdown, MsInstructionsIcon, MsCodeEdit},
data() {
return {
codeTemplates: [
@ -51,7 +56,14 @@
disabled: this.isPreProcessor
}
],
isCodeEditAlive: true
isCodeEditAlive: true,
languages: [
'beanshell',"python"
],
codeEditModeMap: {
beanshell: 'java',
python: 'python'
}
}
},
props: {
@ -62,7 +74,7 @@
type: Boolean,
default: false
},
beanShellProcessor: {
jsr223Processor: {
type: Object,
},
isPreProcessor: {
@ -70,17 +82,25 @@
default: false
}
},
watch: {
jsr223Processor() {
this.reload();
}
},
methods: {
addTemplate(template) {
if (!this.beanShellProcessor.script) {
this.beanShellProcessor.script = "";
if (!this.jsr223Processor.script) {
this.jsr223Processor.script = "";
}
this.beanShellProcessor.script += template.value;
this.jsr223Processor.script += template.value;
this.reload();
},
reload() {
this.isCodeEditAlive = false;
this.$nextTick(() => (this.isCodeEditAlive = true));
},
languageChange(language) {
this.jsr223Processor.language = language;
}
}
}
@ -100,13 +120,10 @@
padding: 0 20px;
}
.script-index div:first-child {
font-weight: bold;
font-size: 15px;
}
.template-title {
margin-bottom: 5px;
font-weight: bold;
font-size: 15px;
}
.document-url {
@ -117,4 +134,8 @@
margin-left: 5px;
}
.ms-dropdown {
margin-bottom: 20px;
}
</style>

View File

@ -45,10 +45,10 @@
<ms-api-extract :is-read-only="isReadOnly" :extract="request.extract"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.processor.pre_exec_script')" name="beanShellPreProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPreProcessor"/>
<ms-jsr233-processor :is-read-only="isReadOnly" :jsr223-processor="request.jsr223PreProcessor"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.processor.post_exec_script')" name="beanShellPostProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPostProcessor"/>
<ms-jsr233-processor :is-read-only="isReadOnly" :jsr223-processor="request.jsr223PostProcessor"/>
</el-tab-pane>
</el-tabs>
</el-form>
@ -65,12 +65,12 @@
import MsDubboRegistryCenter from "@/business/components/api/test/components/request/dubbo/RegistryCenter";
import MsDubboConfigCenter from "@/business/components/api/test/components/request/dubbo/ConfigCenter";
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
import MsBeanShellProcessor from "../processor/BeanShellProcessor";
import MsJsr233Processor from "../processor/Jsr233Processor";
export default {
name: "MsApiDubboRequestForm",
components: {
MsBeanShellProcessor,
MsJsr233Processor,
MsDubboConsumerService,
MsDubboConfigCenter,
MsDubboRegistryCenter,

View File

@ -38,6 +38,7 @@
v-model="request.useEnvironment"
:active-text="$t('api_test.request.refer_to_environment')" @change="useEnvironmentChange">
</el-switch>
<el-checkbox class="follow-redirects-item" v-model="request.followRedirects">{{$t('api_test.request.follow_redirects')}}</el-checkbox>
</el-form-item>
<el-button :disabled="!request.enable || !scenario.enable || isReadOnly" class="debug-button" size="small"
@ -54,7 +55,7 @@
: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" :suggestions="headerSuggestions" :items="request.headers"/>
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="true" :suggestions="headerSuggestions" :items="request.headers"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.body')" name="body" v-if="isNotGet">
<ms-api-body :is-read-only="isReadOnly"
@ -69,12 +70,11 @@
<el-tab-pane :label="$t('api_test.request.extract.label')" name="extract">
<ms-api-extract :is-read-only="isReadOnly" :extract="request.extract"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.processor.pre_exec_script')" name="beanShellPreProcessor">
<ms-bean-shell-processor :is-pre-processor="true" :is-read-only="isReadOnly"
:bean-shell-processor="request.beanShellPreProcessor"/>
<el-tab-pane :label="$t('api_test.request.processor.pre_exec_script')" name="jsr223PreProcessor">
<ms-jsr233-processor :is-read-only="isReadOnly" :jsr223-processor="request.jsr223PreProcessor"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.processor.post_exec_script')" name="beanShellPostProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPostProcessor"/>
<el-tab-pane :label="$t('api_test.request.processor.post_exec_script')" name="jsr223PostProcessor">
<ms-jsr233-processor :is-read-only="isReadOnly" :jsr223-processor="request.jsr223PostProcessor"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.timeout_config')" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
@ -92,16 +92,15 @@ import MsApiExtract from "../extract/ApiExtract";
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
import {REQUEST_HEADERS} from "@/common/js/constants";
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
import MsBeanShellProcessor from "../processor/BeanShellProcessor";
import MsJsr233Processor from "../processor/Jsr233Processor";
import MsApiAdvancedConfig from "../ApiAdvancedConfig";
export default {
name: "MsApiHttpRequestForm",
components: {
MsJsr233Processor,
MsApiAdvancedConfig,
MsBeanShellProcessor,
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue
},
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
props: {
request: HttpRequest,
scenario: Scenario,
@ -225,4 +224,8 @@ export default {
color: #F56C6C;
}
.follow-redirects-item {
margin-left: 30px;
}
</style>

View File

@ -8,7 +8,7 @@
</template>
<script>
import {Request, RequestFactory, Scenario} from "../../model/ScenarioModel";
import {JSR223Processor, Request, RequestFactory, Scenario} from "../../model/ScenarioModel";
import MsApiHttpRequestForm from "./ApiHttpRequestForm";
import MsApiDubboRequestForm from "./ApiDubboRequestForm";
import MsScenarioResults from "../../../report/components/ScenarioResults";
@ -55,6 +55,15 @@ export default {
this.getReport();
}
},
mounted() {
// beanshell
if (!this.request.jsr223PreProcessor.script && this.request.beanShellPreProcessor) {
this.request.jsr223PreProcessor = new JSR223Processor(this.request.beanShellPreProcessor);
}
if (!this.request.jsr223PostProcessor.script && this.request.beanShellPostProcessor) {
this.request.jsr223PostProcessor = new JSR223Processor(this.request.beanShellPostProcessor);
}
},
methods: {
getReport() {
if (this.debugReportId) {

View File

@ -295,8 +295,10 @@ export class HTTPSamplerProxy extends DefaultTestElement {
if (options.responseTimeout) {
this.stringProp('HTTPSampler.response_timeout', options.responseTimeout);
}
if (options.followRedirects) {
this.boolProp('HTTPSampler.follow_redirects', options.followRedirects, true);
}
this.boolProp("HTTPSampler.follow_redirects", options.follow, true);
this.boolProp("HTTPSampler.use_keepalive", options.keepalive, true);
}
}
@ -316,14 +318,16 @@ export class HTTPSamplerArguments extends Element {
let collectionProp = this.collectionProp('Arguments.arguments');
this.args.forEach(arg => {
let elementProp = collectionProp.elementProp(arg.name, 'HTTPArgument');
elementProp.boolProp('HTTPArgument.always_encode', arg.encode, true);
elementProp.boolProp('HTTPArgument.use_equals', arg.equals, true);
if (arg.name) {
elementProp.stringProp('Argument.name', arg.name);
if (arg.enable === true) { // 非禁用的条件加入执行
let elementProp = collectionProp.elementProp(arg.name, 'HTTPArgument');
elementProp.boolProp('HTTPArgument.always_encode', arg.encode, true);
elementProp.boolProp('HTTPArgument.use_equals', arg.equals, true);
if (arg.name) {
elementProp.stringProp('Argument.name', arg.name);
}
elementProp.stringProp('Argument.value', arg.value);
elementProp.stringProp('Argument.metadata', arg.metadata || "=");
}
elementProp.stringProp('Argument.value', arg.value);
elementProp.stringProp('Argument.metadata', arg.metadata || "=");
});
}
}
@ -441,6 +445,30 @@ export class BeanShellProcessor extends DefaultTestElement {
}
}
export class JSR223Processor extends DefaultTestElement {
constructor(tag, guiclass, testclass, testname, processor) {
super(tag, guiclass, testclass, testname);
this.processor = processor || {};
this.stringProp('cacheKey', 'true');
this.stringProp('filename');
this.stringProp('parameters');
this.stringProp('script', this.processor.script);
this.stringProp('scriptLanguage', this.processor.language);
}
}
export class JSR223PreProcessor extends JSR223Processor {
constructor(testName, processor) {
super('JSR223PreProcessor', 'TestBeanGUI', 'JSR223PreProcessor', testName, processor)
}
}
export class JSR223PostProcessor extends JSR223Processor {
constructor(testName, processor) {
super('JSR223PostProcessor', 'TestBeanGUI', 'JSR223PostProcessor', testName, processor)
}
}
export class BeanShellPreProcessor extends BeanShellProcessor {
constructor(testName, processor) {
super('BeanShellPreProcessor', 'TestBeanGUI', 'BeanShellPreProcessor', testName, processor)
@ -459,28 +487,49 @@ export class HeaderManager extends DefaultTestElement {
this.headers = headers || [];
let collectionProp = this.collectionProp('HeaderManager.headers');
this.headers.forEach(header => {
let elementProp = collectionProp.elementProp('', 'Header');
elementProp.stringProp('Header.name', header.name);
elementProp.stringProp('Header.value', header.value);
if (header.enable === true) {
let elementProp = collectionProp.elementProp('', 'Header');
elementProp.stringProp('Header.name', header.name);
elementProp.stringProp('Header.value', header.value);
}
});
}
}
export class DNSCacheManager extends DefaultTestElement {
constructor(testName, domain, hosts) {
super('DNSCacheManager', 'DNSCachePanel', 'DNSCacheManager', testName);
let collectionPropServers = this.collectionProp('DNSCacheManager.servers');
let collectionPropHosts = this.collectionProp('DNSCacheManager.hosts');
hosts.forEach(host => {
let elementProp = collectionPropHosts.elementProp('', 'StaticHost');
if (host && host.domain.trim() === domain.trim()) {
elementProp.stringProp('StaticHost.Name', host.domain);
elementProp.stringProp('StaticHost.Address', host.ip);
}
});
let boolProp = this.boolProp('DNSCacheManager.isCustomResolver', true);
}
}
export class Arguments extends DefaultTestElement {
constructor(testName, args) {
super('Arguments', 'ArgumentsPanel', 'Arguments', testName);
this.args = args || [];
let collectionProp = this.collectionProp('Arguments.arguments');
this.args.forEach(arg => {
let elementProp = collectionProp.elementProp(arg.name, 'Argument');
elementProp.stringProp('Argument.name', arg.name);
elementProp.stringProp('Argument.value', arg.value);
elementProp.stringProp('Argument.desc', arg.desc);
elementProp.stringProp('Argument.metadata', arg.metadata, "=");
if (arg.enable === true) { // 非禁用的条件加入执行
let elementProp = collectionProp.elementProp(arg.name, 'Argument');
elementProp.stringProp('Argument.name', arg.name);
elementProp.stringProp('Argument.value', arg.value);
elementProp.stringProp('Argument.desc', arg.desc);
elementProp.stringProp('Argument.metadata', arg.metadata, "=");
}
});
}
}
@ -499,10 +548,12 @@ export class ElementArguments extends Element {
let collectionProp = this.collectionProp('Arguments.arguments');
if (args) {
args.forEach(arg => {
let elementProp = collectionProp.elementProp(arg.name, 'Argument');
elementProp.stringProp('Argument.name', arg.name);
elementProp.stringProp('Argument.value', arg.value);
elementProp.stringProp('Argument.metadata', arg.metadata, "=");
if (arg.enable === true) { // 非禁用的条件加入执行
let elementProp = collectionProp.elementProp(arg.name, 'Argument');
elementProp.stringProp('Argument.name', arg.name);
elementProp.stringProp('Argument.value', arg.value);
elementProp.stringProp('Argument.metadata', arg.metadata, "=");
}
});
}
}

View File

@ -1,6 +1,7 @@
import {
Arguments, BeanShellPostProcessor, BeanShellPreProcessor,
CookieManager,
DNSCacheManager,
DubboSample,
DurationAssertion,
Element,
@ -9,7 +10,7 @@ import {
HTTPSamplerArguments, HTTPsamplerFiles,
HTTPSamplerProxy,
JSONPathAssertion,
JSONPostProcessor,
JSONPostProcessor, JSR223PostProcessor, JSR223PreProcessor,
RegexExtractor,
ResponseCodeAssertion,
ResponseDataAssertion,
@ -316,9 +317,12 @@ export class HttpRequest extends Request {
this.debugReport = undefined;
this.beanShellPreProcessor = undefined;
this.beanShellPostProcessor = undefined;
this.jsr223PreProcessor = undefined;
this.jsr223PostProcessor = undefined;
this.enable = true;
this.connectTimeout = 60 * 1000;
this.responseTimeout = undefined;
this.followRedirects = true;
this.set(options);
this.sets({parameters: KeyValue, headers: KeyValue}, options);
@ -330,8 +334,8 @@ export class HttpRequest extends Request {
options.body = new Body(options.body);
options.assertions = new Assertions(options.assertions);
options.extract = new Extract(options.extract);
options.beanShellPreProcessor = new BeanShellProcessor(options.beanShellPreProcessor);
options.beanShellPostProcessor = new BeanShellProcessor(options.beanShellPostProcessor);
options.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor);
options.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor);
return options;
}
@ -401,7 +405,9 @@ export class DubboRequest extends Request {
this.debugReport = undefined;
this.beanShellPreProcessor = new BeanShellProcessor(options.beanShellPreProcessor);
this.beanShellPostProcessor = new BeanShellProcessor(options.beanShellPostProcessor);
this.enable = true;
this.enable = options.enable == undefined ? true : options.enable;
this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor);
this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor);
this.sets({args: KeyValue, attachmentArgs: KeyValue}, options);
}
@ -544,7 +550,7 @@ export class Body extends BaseConfig {
export class KeyValue extends BaseConfig {
constructor() {
let options, key, value, type;
let options, key, value, type, enable, uuid;
if (arguments.length === 1) {
options = arguments[0];
}
@ -558,13 +564,20 @@ export class KeyValue extends BaseConfig {
value = arguments[1];
type = arguments[2];
}
if (arguments.length === 5) {
key = arguments[0];
value = arguments[1];
type = arguments[2];
enable = arguments[3];
uuid = arguments[4];
}
super();
this.name = key;
this.value = value;
this.type = type;
this.files = undefined;
this.enable = enable;
this.uuid = uuid;
this.set(options);
}
@ -611,6 +624,16 @@ export class BeanShellProcessor extends BaseConfig {
}
}
export class JSR223Processor extends BaseConfig {
constructor(options) {
super();
this.script = undefined;
this.language = "beanshell";
this.set(options);
}
}
export class Text extends AssertionType {
constructor(options) {
super(ASSERTION_TYPE.TEXT);
@ -757,6 +780,7 @@ class JMXHttpRequest {
}
this.connectTimeout = request.connectTimeout;
this.responseTimeout = request.responseTimeout;
this.followRedirects = request.followRedirects;
}
}
@ -765,7 +789,7 @@ class JMXHttpRequest {
if (this.method.toUpperCase() !== "GET") {
let parameters = [];
request.parameters.forEach(parameter => {
if (parameter.name && parameter.value) {
if (parameter.name && parameter.value && parameter.enable === true) {
parameters.push(parameter);
}
});
@ -855,6 +879,8 @@ class JMXGenerator {
this.addScenarioHeaders(threadGroup, scenario);
this.addScenarioCookieManager(threadGroup, scenario);
// 放在计划或线程组中,不建议放具体某个请求中
this.addDNSCacheManager(threadGroup, scenario.requests[0]);
scenario.requests.forEach(request => {
if (request.enable) {
@ -875,11 +901,11 @@ class JMXGenerator {
}
}
this.addBeanShellProcessor(sampler, request);
this.addRequestExtractor(sampler, request);
this.addRequestAssertion(sampler, request);
this.addRequestExtractor(sampler, request);
this.addJSR223PreProcessor(sampler, request);
threadGroup.put(sampler);
}
@ -925,6 +951,17 @@ class JMXGenerator {
}
}
addDNSCacheManager(threadGroup, request) {
if (request.environment && request.environment.hosts) {
let name = request.name + " DNSCacheManager";
let hosts = JSON.parse(request.environment.hosts);
if (hosts.length > 0) {
let domain = request.environment.protocol + "://" + request.environment.domain;
threadGroup.put(new DNSCacheManager(name, domain, hosts));
}
}
}
addScenarioHeaders(threadGroup, scenario) {
let environment = scenario.environment;
if (environment) {
@ -946,13 +983,13 @@ class JMXGenerator {
}
}
addBeanShellProcessor(sampler, request) {
addJSR223PreProcessor(sampler, request) {
let name = request.name;
if (request.beanShellPreProcessor && request.beanShellPreProcessor.script) {
sampler.put(new BeanShellPreProcessor(name, request.beanShellPreProcessor));
if (request.jsr223PreProcessor && request.jsr223PreProcessor.script) {
sampler.put(new JSR223PreProcessor(name, request.jsr223PreProcessor));
}
if (request.beanShellPostProcessor && request.beanShellPostProcessor.script) {
sampler.put(new BeanShellPostProcessor(name, request.beanShellPostProcessor));
if (request.jsr223PostProcessor && request.jsr223PostProcessor.script) {
sampler.put(new JSR223PostProcessor(name, request.jsr223PostProcessor));
}
}

View File

@ -1,5 +1,6 @@
<template>
<el-dialog :title="title"
<el-dialog :close-on-click-modal="false"
:title="title"
:visible.sync="dialogVisible"
class="delete-confirm" >

View File

@ -1,5 +1,5 @@
<template>
<el-dropdown @command="handleCommand">
<el-dropdown @command="handleCommand" class="ms-dropdown">
<slot>
<span class="el-dropdown-link">
{{currentCommand}}

View File

@ -1,104 +1,110 @@
<template>
<div class="schedule-config">
<div>
<div class="schedule-config">
<div>
<span class="cron-ico" @click="scheduleEdit">
<i class="el-icon-date" size="small"></i>
<span class="character">SCHEDULER</span>
</span>
<el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>
<ms-schedule-edit :is-read-only="isReadOnly" :schedule="schedule" :save="save" :custom-validate="customValidate" ref="scheduleEdit"/>
<el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>
<ms-schedule-edit :is-read-only="isReadOnly" :schedule="schedule" :save="save" :custom-validate="customValidate"
ref="scheduleEdit"/>
</div>
<div>
</div>
<div>
<span>
{{$t('schedule.next_execution_time')}}
<span :class="{'disable-character': !schedule.enable}" v-if="!schedule.enable">{{$t('schedule.not_set')}}</span>
{{ $t('schedule.next_execution_time') }}
<span :class="{'disable-character': !schedule.enable}"
v-if="!schedule.enable">{{ $t('schedule.not_set') }}</span>
<crontab-result v-if="schedule.enable" :enable-simple-mode="true" :ex="schedule.value" ref="crontabResult"/>
</span>
</div>
</div>
</div>
</template>
<script>
import MsScheduleEdit from "./MsScheduleEdit";
import CrontabResult from "../cron/CrontabResult";
import MsScheduleEdit from "./MsScheduleEdit";
import CrontabResult from "../cron/CrontabResult";
function defaultCustomValidate() {return {pass: true};}
function defaultCustomValidate() {
return {pass: true};
}
export default {
name: "MsScheduleConfig",
components: {CrontabResult, MsScheduleEdit},
data() {
export default {
name: "MsScheduleConfig",
components: {CrontabResult, MsScheduleEdit},
data() {
return {
recentList: [],
}
},
props: {
save: Function,
schedule: {},
checkOpen: {
type: Function,
default() {
return {
recentList: [],
}
},
props: {
save: Function,
schedule: {},
checkOpen: {
type: Function,
default() {
return {
checkOpen() {return true;}
}
checkOpen() {
return true;
}
},
customValidate: {
type: Function,
default: defaultCustomValidate
},
isReadOnly: {
type: Boolean,
default: false
}
},
methods: {
scheduleEdit() {
if (!this.checkOpen()) {
return;
}
this.$refs.scheduleEdit.open();
},
scheduleChange() {
this.$emit('scheduleChange');
},
flashResultList() {
this.$refs.crontabResult.expressionChange();
}
}
},
customValidate: {
type: Function,
default: defaultCustomValidate
},
isReadOnly: {
type: Boolean,
default: false
}
},
methods: {
scheduleEdit() {
if (!this.checkOpen()) {
return;
}
this.$refs.scheduleEdit.open();
},
scheduleChange() {
this.$emit('scheduleChange');
},
flashResultList() {
this.$refs.crontabResult.expressionChange();
}
}
}
</script>
<style scoped>
.schedule-config {
float: right;
width: 250px;
height: 15px;
line-height: 25px;
}
.schedule-config {
float: right;
width: 250px;
height: 15px;
line-height: 25px;
}
.el-icon-date {
font-size: 20px;
margin-left: 5px;
}
.el-icon-date {
font-size: 20px;
margin-left: 5px;
}
.character {
font-weight: bold;
margin: 0 5px;
}
.character {
font-weight: bold;
margin: 0 5px;
}
.disable-character {
color: #cccccc;
}
.disable-character {
color: #cccccc;
}
.el-switch {
margin: 0 5px;
}
.el-switch {
margin: 0 5px;
}
.cron-ico {
cursor: pointer;
}
.cron-ico {
cursor: pointer;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog :close-on-click-modal="false" width="35%" class="schedule-edit" :visible.sync="dialogVisible"
<el-dialog :close-on-click-modal="false" width="50%" class="schedule-edit" :visible.sync="dialogVisible"
@close="close">
<template>
<div>
@ -26,18 +26,20 @@
</el-tab-pane>
<el-tab-pane :label="$t('schedule.task_notification')" name="second">
<template>
<el-select v-model="value" :placeholder="$t('commons.please_select')">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="event"
:label="$t('schedule.event')"
>
</el-table-column>
<el-table-column
prop="receiver"
:label="$t('schedule.receiver')"
width="200"
>
<template v-slot:default="{row}">
<el-input
@ -54,7 +56,8 @@
<el-table-column
prop="email"
:label="$t('schedule.receiving_mode')"
width="300">
width="200"
>
<template v-slot:default="{row}">
<el-input
size="mini"
@ -69,19 +72,35 @@
</el-table-column>
<el-table-column
align="center"
:label="$t('schedule.operation')"
show-overflow-tooltip>
prop="enable"
:label="$t('test_resource_pool.enable_disable')"
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="2"
v-model="scope.row.enable"
:active-value="true"
:inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949"
/>
</template>
</el-table-column>
<el-table-column :label="$t('schedule.operation')">
<template v-slot:default="scope">
<el-button
type="primary"
icon="el-icon-plus"
circle size="mini"
@click="handleAddStep(scope.$index)"></el-button>
<el-button
type="danger"
icon="el-icon-delete"
circle size="mini"
@click="handleDeleteStep(scope.$index)"></el-button>
</template>
</el-table-column>
</el-table>
<el-button type="primary" @click="saveNotice">{{$t('commons.save')}}</el-button>
</template>
</el-tab-pane>
</el-tabs>
@ -92,14 +111,14 @@
<script>
import Crontab from "../cron/Crontab";
import CrontabResult from "../cron/CrontabResult";
import {cronValidate} from "../../../../common/js/cron";
import {listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
import Crontab from "../cron/Crontab";
import CrontabResult from "../cron/CrontabResult";
import {cronValidate} from "@/common/js/cron";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
function defaultCustomValidate() {
return {pass: true};
}
function defaultCustomValidate() {
return {pass: true};
}
export default {
name: "MsScheduleEdit",
@ -145,21 +164,26 @@
form: {
cronValue: ""
},
options: [{
value: 'success',
label: '执行成功通知'
}, {
value: 'fail',
label: '执行失败通知'
}, {
value: 'all',
label: '全部通知'
}],
value: '',
tableData: [
{
event: '执行成功',
receiver: '',
email: '',
operation: 1
}, {
event: '执行成功',
receiver: '',
email: '',
operation: 2
receiver: "",
email: "",
enable: false
}
],
enable: false,
email: "",
enable: true,
activeName: 'first',
rules: {
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
@ -169,6 +193,30 @@
methods: {
handleClick() {
},
saveNotice(){
let param = this.buildParam();
/* this.result=this.$post("notice/save",param,()=>{
})*/
},
buildParam() {
let param = {};
param.form = this.tableData
param.testId = ""
param.event = this.value
return param;
},
handleAddStep(index) {
let form = {}
form.receiver = null;
form.email = null;
form.enable = null
this.tableData.splice(index + 1, 0, form);
},
handleDeleteStep(index) {
this.tableData.splice(index, 1);
},
open() {
this.dialogVisible = true;
@ -226,13 +274,13 @@
<style scoped>
.inp {
width: 50%;
margin-right: 20px;
}
.inp {
width: 50%;
margin-right: 20px;
}
.el-form-item {
margin-bottom: 10px;
}
.el-form-item {
margin-bottom: 10px;
}
</style>

View File

@ -95,13 +95,17 @@ export default {
}
}
},
methods: {
registerEvents() {
PerformanceEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
}
},
mounted() {
let self = this;
PerformanceEvent.$on(LIST_CHANGE, () => {
self.$refs.projectRecent.recent();
self.$refs.testRecent.recent();
self.$refs.reportRecent.recent();
});
this.registerEvents();
}
}

View File

@ -22,9 +22,9 @@
@click="rerun(testId)">
{{ $t('report.test_execute_again') }}
</el-button>
<!-- <el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
{{$t('report.export')}}
</el-button>-->
<!-- <el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
{{$t('report.export')}}
</el-button>-->
<!--
<el-button :disabled="isReadOnly" type="warning" plain size="mini">
{{$t('report.compare')}}
@ -82,252 +82,237 @@
</template>
<script>
import MsReportErrorLog from './components/ErrorLog';
import MsReportLogDetails from './components/LogDetails';
import MsReportRequestStatistics from './components/RequestStatistics';
import MsReportTestOverview from './components/TestOverview';
import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsReportErrorLog from './components/ErrorLog';
import MsReportLogDetails from './components/LogDetails';
import MsReportRequestStatistics from './components/RequestStatistics';
import MsReportTestOverview from './components/TestOverview';
import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import writer from "file-writer";
import ResumeCss from "../../../../common/css/main.css";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
export default {
name: "PerformanceReportView",
components: {
MsReportErrorLog,
MsReportLogDetails,
MsReportRequestStatistics,
MsReportTestOverview,
MsContainer,
MsMainContainer,
MsPerformancePressureConfig
},
data() {
return {
result: {},
active: '1',
reportId: '',
status: '',
reportName: '',
testId: '',
testName: '',
projectId: '',
projectName: '',
startTime: '0',
endTime: '0',
minutes: '0',
seconds: '0',
title: 'Logging',
report: {},
isReadOnly: false,
websocket: null,
dialogFormVisible: false,
testPlan: {testResourcePoolId: null}
}
},
methods: {
initBreadcrumb() {
if (this.reportId) {
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
let data = res.data;
if (data) {
this.reportName = data.name;
this.testId = data.testId;
this.testName = data.testName;
this.projectId = data.projectId;
this.projectName = data.projectName;
}
})
}
},
initReportTimeInfo() {
if (this.reportId) {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
.then(res => {
let data = res.data.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
})
}
},
initWebSocket() {
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;
this.websocket.onerror = this.onError;
this.websocket.onclose = this.onClose;
},
checkReportStatus(status) {
switch (status) {
case 'Error':
this.$warning(this.$t('report.generation_error'));
break;
case 'Starting':
this.$alert(this.$t('report.start_status'));
break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
},
stopTest(forceStop) {
this.result = this.$get('/performance/stop/' + this.reportId + '/' + forceStop, () => {
this.$success(this.$t('report.test_stop_success'));
if (forceStop) {
this.$router.push('/performance/report/all');
export default {
name: "PerformanceReportView",
components: {
MsReportErrorLog,
MsReportLogDetails,
MsReportRequestStatistics,
MsReportTestOverview,
MsContainer,
MsMainContainer,
MsPerformancePressureConfig
},
data() {
return {
result: {},
active: '1',
reportId: '',
status: '',
reportName: '',
testId: '',
testName: '',
projectId: '',
projectName: '',
startTime: '0',
endTime: '0',
minutes: '0',
seconds: '0',
title: 'Logging',
report: {},
isReadOnly: false,
websocket: null,
dialogFormVisible: false,
testPlan: {testResourcePoolId: null}
}
},
methods: {
initBreadcrumb(callback) {
if (this.reportId) {
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
let data = res.data;
if (data) {
this.reportName = data.name;
this.testId = data.testId;
this.testName = data.testName;
this.projectId = data.projectId;
this.projectName = data.projectName;
//
if (callback) callback(res);
} else {
this.report.status = 'Completed';
this.$error(this.$t('report.not_exist'));
}
});
this.dialogFormVisible = false;
},
rerun(testId) {
this.$confirm(this.$t('report.test_rerun_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket
this.initWebSocket();
})
}).catch(() => {
});
},
onOpen() {
window.console.log("socket opening.");
},
onError(e) {
window.console.error(e)
},
onMessage(e) {
this.$set(this.report, "refresh", e.data); //
this.$set(this.report, "status", 'Running');
this.initReportTimeInfo();
window.console.log('receive a message:', e.data);
},
onClose(e) {
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
window.console.log("socket closed.");
})
}
},
created() {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
initReportTimeInfo() {
if (this.status === 'Starting') {
this.clearData();
return;
}
this.reportId = this.$route.path.split('/')[4];
this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data;
if (data) {
this.status = data.status;
this.$set(this.report, "id", this.reportId);
this.$set(this.report, "status", data.status);
this.$set(this.report, "testId", data.testId);
this.$set(this.report, "loadConfiguration", data.loadConfiguration);
this.checkReportStatus(data.status);
if (this.status === "Completed" || this.status === "Running") {
this.initReportTimeInfo();
}
this.initBreadcrumb();
this.initWebSocket();
if (this.reportId) {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
.then(res => {
let data = res.data.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
});
}
},
initWebSocket() {
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;
this.websocket.onerror = this.onError;
this.websocket.onclose = this.onClose;
},
checkReportStatus(status) {
switch (status) {
case 'Error':
this.$warning(this.$t('report.generation_error'));
break;
case 'Starting':
this.$alert(this.$t('report.start_status'));
break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
},
stopTest(forceStop) {
this.result = this.$get('/performance/stop/' + this.reportId + '/' + forceStop, () => {
this.$success(this.$t('report.test_stop_success'));
if (forceStop) {
this.$router.push('/performance/report/all');
} else {
this.$error(this.$t('report.not_exist'))
this.report.status = 'Completed';
}
});
this.dialogFormVisible = false;
},
beforeDestroy() {
this.websocket.close() //websocket
rerun(testId) {
this.$confirm(this.$t('report.test_rerun_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket
this.initWebSocket();
})
}).catch(() => {
});
},
watch: {
'$route'(to) {
if (to.name === "perReportView") {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let reportId = to.path.split('/')[4];
this.reportId = reportId;
if (reportId) {
this.$get("/performance/report/test/pro/info/" + reportId, response => {
let data = response.data;
if (data) {
this.status = data.status;
this.reportName = data.name;
this.testName = data.testName;
this.testId = data.testId;
this.projectName = data.projectName;
this.$set(this.report, "id", reportId);
this.$set(this.report, "status", data.status);
this.checkReportStatus(data.status);
if (this.status === "Completed") {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId).then(res => {
let data = res.data.data;
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
}).catch(() => {
this.clearData();
})
} else {
this.clearData();
}
} else {
this.$error(this.$t('report.not_exist'));
}
});
}
onOpen() {
window.console.log("socket opening.");
},
onError(e) {
window.console.error(e)
},
onMessage(e) {
this.$set(this.report, "refresh", e.data); //
this.$set(this.report, "status", 'Running');
this.status = 'Running';
this.initReportTimeInfo();
window.console.log('receive a message:', e.data);
},
onClose(e) {
if (e.code === 1005) {
// socketreport
return;
}
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
window.console.log("socket closed.");
}
},
created() {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
this.reportId = this.$route.path.split('/')[4];
this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data;
if (data) {
this.status = data.status;
this.$set(this.report, "id", this.reportId);
this.$set(this.report, "status", data.status);
this.$set(this.report, "testId", data.testId);
this.$set(this.report, "loadConfiguration", data.loadConfiguration);
this.checkReportStatus(data.status);
if (this.status === "Completed" || this.status === "Running") {
this.initReportTimeInfo();
}
this.initBreadcrumb();
this.initWebSocket();
} else {
this.$error(this.$t('report.not_exist'))
}
});
},
watch: {
'$route'(to) {
if (to.name === "perReportView") {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let reportId = to.path.split('/')[4];
this.reportId = reportId;
this.initBreadcrumb((response) => {
let data = response.data;
this.$set(this.report, "id", reportId);
this.$set(this.report, "status", data.status);
this.checkReportStatus(data.status);
this.initReportTimeInfo();
});
this.initWebSocket();
} else {
console.log("close socket.");
this.websocket.close() //websocket
}
}
}
}
</script>
<style scoped>
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
</style>

View File

@ -58,7 +58,7 @@ import PerformancePressureConfig from "./components/PerformancePressureConfig";
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";

View File

@ -4,7 +4,7 @@
accept=".jmx,.csv"
drag
action=""
:limit="2"
:limit="5"
multiple
:show-file-list="false"
:before-upload="beforeUpload"

View File

@ -164,6 +164,7 @@ export default {
} else {
this.calculateChart();
}
this.getResourcePools();
},
},
methods: {

View File

@ -49,7 +49,7 @@
</el-card>
</ms-main-container>
<el-dialog :title="title" :visible.sync="createVisible" destroy-on-close @close="handleClose">
<el-dialog :close-on-click-modal="false" :title="title" :visible.sync="createVisible" destroy-on-close @close="handleClose">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"></el-input>

View File

@ -25,6 +25,9 @@
<el-form-item :label="$t('organization.integration.jira_url')" prop="url" v-if="platform === 'Jira'">
<el-input v-model="form.url" :placeholder="$t('organization.integration.input_jira_url')"/>
</el-form-item>
<el-form-item :label="$t('organization.integration.jira_issuetype')" prop="issuetype" v-if="platform === 'Jira'">
<el-input v-model="form.issuetype" :placeholder="$t('organization.integration.input_jira_issuetype')"/>
</el-form-item>
</el-form>
</div>
@ -43,10 +46,13 @@
<div class="defect-tip">
<div>{{$t('organization.integration.use_tip')}}</div>
<div>
1. {{$t('organization.integration.use_tip_one')}}
1. {{$t('organization.integration.use_tip_tapd')}}
</div>
<div>
2. {{$t('organization.integration.use_tip_two')}}
2. {{$t('organization.integration.use_tip_jira')}}
</div>
<div>
3. {{$t('organization.integration.use_tip_two')}}
<router-link to="/track/project/all" style="margin-left: 5px">
{{$t('organization.integration.link_the_project_now')}}
</router-link>
@ -85,6 +91,11 @@
required: true,
message: this.$t('organization.integration.input_jira_url'),
trigger: ['change', 'blur']
},
issuetype: {
required: true,
message: this.$t('organization.integration.input_jira_issuetype'),
trigger: ['change', 'blur']
}
},
}
@ -105,6 +116,7 @@
this.$set(this.form, 'account', config.account);
this.$set(this.form, 'password', config.password);
this.$set(this.form, 'url', config.url);
this.$set(this.form, 'issuetype', config.issuetype);
} else {
this.clear();
}
@ -149,17 +161,26 @@
this.$warning(this.$t('organization.integration.choose_platform'));
return;
}
let param = {};
let auth = {
account: this.form.account,
password: this.form.password,
url: this.form.url
};
param.organizationId = getCurrentUser().lastOrganizationId;
param.platform = this.platform;
param.configuration = JSON.stringify(auth);
this.$refs[form].validate(valid => {
if (valid) {
let formatUrl = this.form.url;
if (!formatUrl.endsWith('/')) {
formatUrl = formatUrl + '/';
}
let param = {};
let auth = {
account: this.form.account,
password: this.form.password,
url: formatUrl,
issuetype: this.form.issuetype
};
param.organizationId = getCurrentUser().lastOrganizationId;
param.platform = this.platform;
param.configuration = JSON.stringify(auth);
this.result = this.$post("service/integration/save", param, () => {
this.show = true;
this.showEdit = true;
@ -188,6 +209,7 @@
this.$set(this.form, 'account', config.account);
this.$set(this.form, 'password', config.password);
this.$set(this.form, 'url', config.url);
this.$set(this.form, 'issuetype', config.issuetype);
} else {
this.clear();
}
@ -197,6 +219,7 @@
this.$set(this.form, 'account', '');
this.$set(this.form, 'password', '');
this.$set(this.form, 'url', '');
this.$set(this.form, 'issuetype', '');
this.$nextTick(() => {
this.$refs.form.clearValidate();
});

View File

@ -25,7 +25,7 @@
:total="total"/>
</el-card>
<el-dialog :title="$t('member.create')" :visible.sync="createVisible" width="30%" :destroy-on-close="true"
<el-dialog :close-on-click-modal="false" :title="$t('member.create')" :visible.sync="createVisible" width="30%" :destroy-on-close="true"
@close="handleClose">
<el-form :model="form" ref="form" :rules="rules" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.member')" prop="ids"
@ -73,7 +73,7 @@
</template>
</el-dialog>
<el-dialog :title="$t('member.modify')" :visible.sync="updateVisible" width="30%" :destroy-on-close="true"
<el-dialog :close-on-click-modal="false" :title="$t('member.modify')" :visible.sync="updateVisible" width="30%" :destroy-on-close="true"
@close="handleClose">
<el-form :model="form" label-position="right" label-width="100px" size="small" ref="updateUserForm">
<el-form-item label="ID" prop="id">

View File

@ -24,7 +24,7 @@
:total="total"/>
</el-card>
<el-dialog :title="$t('workspace.create')" :visible.sync="dialogWsAddVisible" width="30%" @close="close">
<el-dialog :close-on-click-modal="false" :title="$t('workspace.create')" :visible.sync="dialogWsAddVisible" width="30%" @close="close">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"/>
@ -39,7 +39,7 @@
@confirm="submit('form')"/>
</template>
</el-dialog>
<el-dialog :title="$t('workspace.update')" :visible.sync="dialogWsUpdateVisible" width="30%">
<el-dialog :close-on-click-modal="false" :title="$t('workspace.update')" :visible.sync="dialogWsUpdateVisible" width="30%">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"/>
@ -56,7 +56,7 @@
</el-dialog>
<!-- dialog of workspace member -->
<el-dialog :visible.sync="dialogWsMemberVisible" width="70%" :destroy-on-close="true" @close="close"
<el-dialog :close-on-click-modal="false" :visible.sync="dialogWsMemberVisible" width="70%" :destroy-on-close="true" @close="close"
class="dialog-css">
<ms-table-header :condition.sync="dialogCondition" @create="addMember" @search="dialogSearch"
:create-tip="$t('member.create')" :title="$t('commons.member')"/>
@ -82,7 +82,7 @@
</el-dialog>
<!-- add workspace member dialog -->
<el-dialog :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%"
<el-dialog :close-on-click-modal="false" :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"
@ -125,7 +125,7 @@
</el-dialog>
<!-- update workspace member dialog -->
<el-dialog :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%"
<el-dialog :close-on-click-modal="false" :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">

View File

@ -32,7 +32,7 @@
</el-card>
<!--Modify personal details-->
<el-dialog :title="$t('member.modify_personal_info')" :visible.sync="updateVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('member.modify_personal_info')" :visible.sync="updateVisible" width="30%"
:destroy-on-close="true" @close="handleClose">
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule"
ref="updateUserForm">
@ -57,7 +57,7 @@
</el-dialog>
<!--Change personal password-->
<el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="35%"
<el-dialog :close-on-click-modal="false" :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="35%"
:destroy-on-close="true" @close="handleClose" left>
<el-form :model="ruleForm" :rules="rules" ref="editPasswordForm" label-width="120px" class="demo-ruleForm">
<el-form-item :label="$t('member.old_password')" prop="password" style="margin-bottom: 29px">

View File

@ -28,7 +28,7 @@
</el-card>
<!-- dialog of organization member -->
<el-dialog :visible.sync="dialogOrgMemberVisible" width="70%" :destroy-on-close="true" @close="closeFunc"
<el-dialog :close-on-click-modal="false" :visible.sync="dialogOrgMemberVisible" width="70%" :destroy-on-close="true" @close="closeFunc"
class="dialog-css">
<ms-table-header :condition.sync="dialogCondition" @create="addMember" @search="dialogSearch"
:create-tip="$t('member.create')" :title="$t('commons.member')"/>
@ -56,7 +56,7 @@
</el-dialog>
<!-- add organization form -->
<el-dialog :title="$t('organization.create')" :visible.sync="dialogOrgAddVisible" width="30%" @closed="closeFunc"
<el-dialog :close-on-click-modal="false" :title="$t('organization.create')" :visible.sync="dialogOrgAddVisible" width="30%" @closed="closeFunc"
:destroy-on-close="true">
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule"
ref="createOrganization">
@ -75,7 +75,7 @@
</el-dialog>
<!-- update organization form -->
<el-dialog :title="$t('organization.modify')" :visible.sync="dialogOrgUpdateVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('organization.modify')" :visible.sync="dialogOrgUpdateVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="form" label-position="right" label-width="100px" size="small" :rules="rule"
@ -95,7 +95,7 @@
</el-dialog>
<!-- add organization member form -->
<el-dialog :title="$t('member.create')" :visible.sync="dialogOrgMemberAddVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('member.create')" :visible.sync="dialogOrgMemberAddVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="memberForm" ref="form" :rules="orgMemberRule" label-position="right" label-width="100px"
@ -133,7 +133,7 @@
</el-dialog>
<!-- update organization member form -->
<el-dialog :title="$t('member.modify')" :visible.sync="dialogOrgMemberUpdateVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('member.modify')" :visible.sync="dialogOrgMemberUpdateVisible" width="30%"
:destroy-on-close="true"
@close="closeFunc">
<el-form :model="memberForm" label-position="right" label-width="100px" size="small" ref="updateUserForm">

View File

@ -28,7 +28,7 @@
</el-card>
<!-- add workspace dialog -->
<el-dialog :title="$t('workspace.create')" :visible.sync="dialogWsAddVisible" width="30%" @close="close">
<el-dialog :close-on-click-modal="false" :title="$t('workspace.create')" :visible.sync="dialogWsAddVisible" width="30%" @close="close">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"/>
@ -56,7 +56,7 @@
</el-dialog>
<!-- update workspace dialog -->
<el-dialog :title="$t('workspace.update')" :visible.sync="dialogWsUpdateVisible" width="30%" @close="close">
<el-dialog :close-on-click-modal="false" :title="$t('workspace.update')" :visible.sync="dialogWsUpdateVisible" width="30%" @close="close">
<el-form :model="form" :rules="rules" ref="updateForm" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"/>
@ -85,7 +85,7 @@
</el-dialog>
<!-- dialog of workspace member -->
<el-dialog :visible.sync="dialogWsMemberVisible" width="70%" :destroy-on-close="true" @close="closeWsMemberDialog" class="dialog-css">
<el-dialog :close-on-click-modal="false" :visible.sync="dialogWsMemberVisible" width="70%" :destroy-on-close="true" @close="closeWsMemberDialog" class="dialog-css">
<ms-table-header :condition.sync="dialogCondition" @create="addMember" @search="dialogSearch"
:create-tip="$t('member.create')" :title="$t('commons.member')"/>
<!-- organization member table -->
@ -111,7 +111,7 @@
</el-dialog>
<!-- add workspace member dialog -->
<el-dialog :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('member.create')" :visible.sync="dialogWsMemberAddVisible" width="30%"
:destroy-on-close="true"
@close="handleClose">
<el-form :model="memberForm" ref="form" :rules="wsMemberRule" label-position="right" label-width="100px"
@ -149,7 +149,7 @@
</el-dialog>
<!-- update workspace member dialog -->
<el-dialog :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('member.modify')" :visible.sync="dialogWsMemberUpdateVisible" width="30%"
:destroy-on-close="true"
@close="handleClose">
<el-form :model="memberForm" label-position="right" label-width="100px" size="small" ref="updateUserForm">

View File

@ -45,6 +45,7 @@
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="$t('test_resource_pool.create_resource_pool')"
:visible.sync="createVisible" width="70%"
@closed="closeFunc"
@ -109,6 +110,7 @@
</el-dialog>
<el-dialog
:close-on-click-modal="false"
v-loading="result.loading"
:title="$t('test_resource_pool.update_resource_pool')" :visible.sync="updateVisible" width="70%"
:destroy-on-close="true"

View File

@ -50,7 +50,7 @@
</el-card>
<!--Create user-->
<el-dialog :title="$t('user.create')" :visible.sync="createVisible" width="35%" @closed="handleClose"
<el-dialog :close-on-click-modal="false" :title="$t('user.create')" :visible.sync="createVisible" width="35%" @closed="handleClose"
:destroy-on-close="true">
<el-form :model="form" label-position="right" label-width="120px" size="small" :rules="rule" ref="createUserForm">
<el-form-item label="ID" prop="id">
@ -163,7 +163,7 @@
</el-dialog>
<!--Modify user information in system settings-->
<el-dialog :title="$t('user.modify')" :visible.sync="updateVisible" width="35%" :destroy-on-close="true"
<el-dialog :close-on-click-modal="false" :title="$t('user.modify')" :visible.sync="updateVisible" width="35%" :destroy-on-close="true"
@close="handleClose" v-loading="result.loading">
<el-form :model="form" label-position="right" label-width="120px" size="small" :rules="rule" ref="updateUserForm">
<el-form-item label="ID" prop="id">
@ -268,7 +268,7 @@
</template>
</el-dialog>
<!--Changing user password in system settings-->
<el-dialog :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%"
<el-dialog :close-on-click-modal="false" :title="$t('member.edit_password')" :visible.sync="editPasswordVisible" width="30%"
:destroy-on-close="true" @close="handleClose" left>
<el-form :model="ruleForm" label-position="right" label-width="120px" size="small" :rules="rule"
ref="editPasswordForm" class="demo-ruleForm">

View File

@ -26,7 +26,7 @@
:total="total"/>
</el-card>
<el-dialog :title="$t('member.create')" :visible.sync="createVisible" width="30%" :destroy-on-close="true"
<el-dialog :close-on-click-modal="false" :title="$t('member.create')" :visible.sync="createVisible" width="30%" :destroy-on-close="true"
@close="handleClose">
<el-form :model="form" ref="form" :rules="rules" label-position="right" label-width="100px" size="small">
<el-form-item :label="$t('commons.member')" prop="memberSign" :rules="{required: true, message: $t('member.input_id_or_email'), trigger: 'change'}">
@ -65,7 +65,7 @@
</template>
</el-dialog>
<el-dialog :title="$t('member.modify')" :visible.sync="updateVisible" width="30%" :destroy-on-close="true"
<el-dialog :close-on-click-modal="false" :title="$t('member.modify')" :visible.sync="updateVisible" width="30%" :destroy-on-close="true"
@close="handleClose">
<el-form :model="form" label-position="right" label-width="100px" size="small" ref="updateUserForm">
<el-form-item label="ID" prop="id">

View File

@ -2,9 +2,10 @@
<div>
<el-dialog @close="close"
<el-dialog :close-on-click-modal="false"
@close="close"
:title="operationType == 'edit' ? ( readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')"
:visible.sync="dialogFormVisible" width="65%" :close-on-click-modal="false">
:visible.sync="dialogFormVisible" width="65%">
<el-form :model="form" :rules="rules" ref="caseFrom" v-loading="result.loading">
@ -268,8 +269,8 @@ export default {
type: [{required: true, message: this.$t('test_track.case.input_type'), trigger: 'change'}],
testId: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
method: [{required: true, message: this.$t('test_track.case.input_method'), trigger: 'change'}],
prerequisite: [{max: 300, message: this.$t('test_track.length_less_than') + '300', trigger: 'blur'}],
remark: [{max: 300, message: this.$t('test_track.length_less_than') + '300', trigger: 'blur'}]
prerequisite: [{max: 500, message: this.$t('test_track.length_less_than') + '500', trigger: 'blur'}],
remark: [{max: 500, message: this.$t('test_track.length_less_than') + '500', trigger: 'blur'}]
},
formLabelWidth: "120px",
operationType: '',
@ -351,8 +352,8 @@ export default {
handleAddStep(index, data) {
let step = {};
step.num = data.num + 1;
step.desc = null;
step.result = null;
step.desc = "";
step.result = "";
this.form.steps.forEach(step => {
if (step.num > data.num) {
step.num++;

View File

@ -100,12 +100,7 @@ export default {
},
mounted() {
this.init();
let self = this;
TrackEvent.$on(LIST_CHANGE, () => {
self.$refs.projectRecent.recent();
self.$refs.planRecent.recent();
self.$refs.caseRecent.recent();
});
this.registerEvents();
},
methods: {
reload() {
@ -132,6 +127,13 @@ export default {
this.testCaseEditPath = path;
this.reload();
}
},
registerEvents() {
TrackEvent.$on(LIST_CHANGE, () => {
this.$refs.projectRecent.recent();
this.$refs.planRecent.recent();
this.$refs.caseRecent.recent();
});
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<ms-container>
<ms-main-container>
<el-row>
<el-row :gutter="20">
<el-col :span="15">
<related-test-plan-list ref="relatedTestPlanList"/>
</el-col>
@ -14,33 +14,34 @@
</template>
<script>
import RelatedTestPlanList from "./components/RelatedTestPlanList";
import TestCaseSideList from "./components/TestCaseSideList";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
export default {
name: "TrackHome",
components: {MsMainContainer, MsContainer, TestCaseSideList, RelatedTestPlanList},
watch: {
'$route'(to,from) {
if (to.path.indexOf('/track/home') > -1) {
this.innitData();
}
}
},
methods: {
innitData() {
this.$refs.relatedTestPlanList.initTableData();
this.$refs.testCaseRecentList.initTableData();
}
import RelatedTestPlanList from "./components/RelatedTestPlanList";
import TestCaseSideList from "./components/TestCaseSideList";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
export default {
name: "TrackHome",
components: {MsMainContainer, MsContainer, TestCaseSideList, RelatedTestPlanList},
watch: {
'$route'(to, from) {
if (to.path.indexOf('/track/home') > -1) {
this.innitData();
}
}
},
methods: {
innitData() {
this.$refs.relatedTestPlanList.initTableData();
this.$refs.testCaseRecentList.initTableData();
}
}
}
</script>
<style scoped>
.ms-main-container >>> .el-table {
cursor:pointer;
}
.ms-main-container >>> .el-table {
cursor: pointer;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<div class="track-home-component">
<div>
<el-card>
<template v-slot:header>
<span class="title">{{title}}</span>
<span class="title">{{ title }}</span>
</template>
<slot></slot>
</el-card>
@ -10,27 +10,19 @@
</template>
<script>
export default {
name: "HomeBaseComponent",
props: {
title: {
type: String,
default() {
this.$t('commons.title')
}
}
export default {
name: "HomeBaseComponent",
props: {
title: {
type: String,
default() {
this.$t('commons.title')
}
}
}
}
</script>
<style scoped>
.el-card {
padding: 15px;
}
.track-home-component {
padding: 0 15px 15px;
}
</style>

View File

@ -2,7 +2,8 @@
<div>
<el-dialog :title="operationType == 'edit' ? $t('test_track.plan.edit_plan') : $t('test_track.plan.create_plan')"
<el-dialog :close-on-click-modal="false"
:title="operationType == 'edit' ? $t('test_track.plan.edit_plan') : $t('test_track.plan.create_plan')"
:visible.sync="dialogFormVisible"
@close="close"
width="65%">

View File

@ -23,22 +23,22 @@
</template>
<script>
import {Test} from "../../../../../api/test/model/ScenarioModel"
import MsApiScenarioConfig from "../../../../../api/test/components/ApiScenarioConfig";
import MsContainer from "../../../../../common/components/MsContainer";
import MsMainContainer from "../../../../../common/components/MsMainContainer";
import {Test} from "../../../../../api/test/model/ScenarioModel"
import MsApiScenarioConfig from "../../../../../api/test/components/ApiScenarioConfig";
import MsContainer from "../../../../../common/components/MsContainer";
import MsMainContainer from "../../../../../common/components/MsMainContainer";
export default {
name: "ApiTestDetail",
components: {MsMainContainer, MsContainer, MsApiScenarioConfig},
props: {
id: String,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
export default {
name: "ApiTestDetail",
components: {MsMainContainer, MsContainer, MsApiScenarioConfig},
props: {
id: String,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
result: {},
test: new Test(),
@ -92,7 +92,7 @@
<style scoped>
.test-container {
height: calc(100vh - 150px);
height: calc(100vh - 155px);
min-height: 600px;
padding: 15px;
}

View File

@ -30,24 +30,24 @@
<script>
import MsScenarioResult from "../../../../../api/report/components/ScenarioResult";
import MsMetricChart from "../../../../../api/report/components/MetricChart";
import MsScenarioResults from "../../../../../api/report/components/ScenarioResults";
import MsRequestResult from "../../../../../api/report/components/RequestResult";
import MsContainer from "../../../../../common/components/MsContainer";
import MsMainContainer from "../../../../../common/components/MsMainContainer";
import MsScenarioResult from "../../../../../api/report/components/ScenarioResult";
import MsMetricChart from "../../../../../api/report/components/MetricChart";
import MsScenarioResults from "../../../../../api/report/components/ScenarioResults";
import MsRequestResult from "../../../../../api/report/components/RequestResult";
import MsContainer from "../../../../../common/components/MsContainer";
import MsMainContainer from "../../../../../common/components/MsMainContainer";
export default {
name: "ApiTestResult",
components: {MsMainContainer, MsContainer, MsRequestResult, MsScenarioResults, MsMetricChart, MsScenarioResult},
data() {
return {
activeName: "total",
content: {},
report: {},
loading: true,
fails: [],
}
export default {
name: "ApiTestResult",
components: {MsMainContainer, MsContainer, MsRequestResult, MsScenarioResults, MsMetricChart, MsScenarioResult},
data() {
return {
activeName: "total",
content: {},
report: {},
loading: true,
fails: [],
}
},
props:['reportId'],
watch: {
@ -108,7 +108,7 @@
<style scoped>
.report-container {
height: calc(100vh - 150px);
height: calc(100vh - 155px);
min-height: 600px;
overflow-y: auto;
}

View File

@ -10,6 +10,7 @@ export default {
current_project: 'Current Project',
name: 'Name',
description: 'Description',
annotation: 'Annotation',
clear: 'Clear',
save: 'Save',
save_success: 'Saved successfully',
@ -187,11 +188,14 @@ export default {
api_account: 'API account',
api_password: 'API password',
jira_url: 'JIRA url',
jira_issuetype: 'JIRA issuetype',
input_api_account: 'please enter account',
input_api_password: 'Please enter password',
input_jira_url: 'Please enter Jira address, for example: https://metersphere.atlassian.net/',
input_jira_issuetype: 'Please enter the question type',
use_tip: 'Usage guidelines:',
use_tip_one: 'Basic Auth account information is queried in "Company Management-Security and Integration-Open Platform"',
use_tip_tapd: 'Basic Auth account information is queried in "Company Management-Security and Integration-Open Platform"',
use_tip_jira: 'Jira software server authentication information is account password, Jira software cloud authentication information is account + token (account settings-security-create API token)',
use_tip_two: 'After saving the Basic Auth account information, you need to manually associate the ID/key in the Metersphere project',
link_the_project_now: 'Link the project now',
cancel_edit: 'Cancel edit',
@ -346,6 +350,8 @@ export default {
domain: 'Domain',
enable: 'Enable',
ip: 'IP',
input_ip: 'Please enter the correct IP address',
input_domain: 'Please enter the correct domain',
params: 'Parameters',
param_name: 'Name',
param_value: 'Value',
@ -456,6 +462,7 @@ export default {
timeout_config: "Timeout Config",
connect_timeout: "Connect Timeout",
response_timeout: "Response Timeout",
follow_redirects: "Follow Redirects",
body_upload_limit_size: "The file size does not exceed 500 MB",
assertions: {
label: "Assertion",
@ -820,7 +827,7 @@ export default {
schedule: {
input_email: "Please input email account",
event: "event",
receiving_mode: "Receiving mode",
receiving_mode: "mailbox",
receiver: "Receiver",
operation: "operation",
task_notification: "Task notification",

View File

@ -10,6 +10,7 @@ export default {
current_project: '当前项目',
name: '名称',
description: '描述',
annotation: '注释',
clear: '清空',
save: '保存',
save_success: '保存成功',
@ -188,11 +189,14 @@ export default {
api_account: 'API 账号',
api_password: 'API 口令',
jira_url: 'JIRA 地址',
jira_issuetype: '问题类型',
input_api_account: '请输入账号',
input_api_password: '请输入口令',
input_jira_url: '请输入Jira地址https://metersphere.atlassian.net/',
input_jira_issuetype: '请输入问题类型',
use_tip: '使用指引:',
use_tip_one: 'Basic Auth 账号信息在"公司管理-安全与集成-开放平台"中查询',
use_tip_tapd: 'Tapd Basic Auth 账号信息在"公司管理-安全与集成-开放平台"中查询',
use_tip_jira: 'Jira software server 认证信息为 账号密码Jira software cloud 认证信息为 账号+令牌(账户设置-安全-创建API令牌)',
use_tip_two: '保存 Basic Auth 账号信息后,需要在 Metersphere 项目中手动关联 ID/key',
link_the_project_now: '马上关联项目',
cancel_edit: '取消编辑',
@ -345,6 +349,8 @@ export default {
domain: '域名',
enable: '是否启用',
ip: 'IP地址',
input_ip: '请输入正确的IP地址',
input_domain: '请输入正确的域名!',
params: '自定义属性',
param_name: '属性名',
param_value: '属性值',
@ -457,6 +463,7 @@ export default {
timeout_config: "超时设置",
connect_timeout: "连接超时",
response_timeout: "响应超时",
follow_redirects: "跟随重定向",
body_upload_limit_size: "上传文件大小不能超过 500 MB!",
assertions: {
label: "断言",
@ -822,7 +829,7 @@ export default {
schedule: {
input_email: "请输入邮箱账号",
event: "事件",
receiving_mode: "接收方式",
receiving_mode: "邮箱",
receiver: "接收人",
operation: "操作",
task_notification: "任务通知",

View File

@ -10,6 +10,7 @@ export default {
current_project: '當前項目',
name: '名稱',
description: '描述',
annotation: '註釋',
clear: '清空',
save: '保存',
save_success: '保存成功',
@ -186,11 +187,14 @@ export default {
api_account: 'API 賬號',
api_password: 'API 口令',
jira_url: 'JIRA 地址',
jira_issuetype: '問題類型',
input_api_account: '請輸入賬號',
input_api_password: '請輸入口令',
input_jira_url: '請輸入Jira地址https://metersphere.atlassian.net/',
input_jira_issuetype: '請輸入問題類型',
use_tip: '使用指引:',
use_tip_one: 'Basic Auth 賬號信息在"公司管理-安全與集成-開放平台"中查詢',
use_tip_tapd: 'Basic Auth 賬號信息在"公司管理-安全與集成-開放平台"中查詢',
use_tip_jira: 'Jira software server 認證信息為 賬號密碼Jira software cloud 認證信息為 賬號+令牌(賬戶設置-安全-創建API令牌)',
use_tip_two: '保存 Basic Auth 賬號信息後,需要在 Metersphere 項目中手動關聯 ID/key',
link_the_project_now: '馬上關聯項目',
cancel_edit: '取消編輯',
@ -344,6 +348,8 @@ export default {
domain: '域名',
enable: '是否啟用',
ip: 'IP地址',
input_ip: '請輸入正確的IP地址',
input_domain: '請輸入正確的域名!',
params: '自定義屬性',
param_name: '屬性名',
param_value: '屬性值',
@ -819,7 +825,7 @@ export default {
schedule: {
input_email: "請輸入郵箱帳號",
event: "事件",
receiving_mode: "接收管道",
receiving_mode: "郵箱",
receiver: "接收人",
operation: "操作",
task_notification: "任務通知",