Merge remote-tracking branch 'origin/master'

# Conflicts:
#	frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue
#	frontend/src/business/components/api/definition/components/list/ApiList.vue
This commit is contained in:
song.tianyang 2021-01-05 18:05:46 +08:00
commit 252f5f3c10
97 changed files with 127291 additions and 1179 deletions

View File

@ -385,6 +385,12 @@
<artifactId>kubernetes-client</artifactId>
<version>4.13.0</version>
</dependency>
<dependency>
<groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId>
<version>2.2.6</version>
</dependency>
</dependencies>
<build>

View File

@ -11,6 +11,7 @@ import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.json.JSONSchemaGenerator;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
@ -158,4 +159,10 @@ public class ApiDefinitionController {
public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) {
apiDefinitionService.testPlanRelevance(request);
}
@PostMapping("/preview")
public String preview(@RequestBody String jsonSchema) {
return JSONSchemaGenerator.getJson(jsonSchema);
}
}

View File

@ -1,13 +1,10 @@
package io.metersphere.api.dto.definition;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.controller.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -32,6 +29,10 @@ public class ApiTestBatchRequest extends ApiTestCaseWithBLOBs {
private String name;
private String method;
private String path;
private List<String> moduleIds;
private List<String> unSelectIds;
@ -39,13 +40,4 @@ public class ApiTestBatchRequest extends ApiTestCaseWithBLOBs {
private String protocol;
private String status;
public void cleanSelectParam() {
filters = new HashMap<>();
name = null;
moduleIds = new ArrayList<>();
protocol = null;
status = null;
}
}

View File

@ -1,6 +1,7 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.scenario.request.BodyFile;
import io.metersphere.commons.json.JSONSchemaGenerator;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
@ -17,6 +18,7 @@ public class Body {
private String format;
private List<KeyValue> kvs;
private List<KeyValue> binary;
private Object jsonSchema;
public final static String KV = "KeyValue";
public final static String FORM_DATA = "Form Data";
@ -54,6 +56,10 @@ public class Body {
} else {
if (!this.isJson()) {
sampler.setPostBodyRaw(true);
} else {
if (StringUtils.isNotEmpty(this.format) && this.format.equals("JSON-SCHEMA") && this.getJsonSchema() != null) {
this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema()));
}
}
KeyValue keyValue = new KeyValue("", this.getRaw());
keyValue.setEnable(true);

View File

@ -1,5 +1,6 @@
package io.metersphere.api.jmeter;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.service.*;
import io.metersphere.base.domain.ApiScenarioReport;
import io.metersphere.base.domain.ApiTestReport;
@ -323,7 +324,10 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
if (responseAssertionResult.isPass()) {
requestResult.addPassAssertions();
}
responseResult.getAssertions().add(responseAssertionResult);
//xpath 提取错误会添加断言错误
if (!responseAssertionResult.getMessage().contains("The required item type of the first operand of")) {
responseResult.getAssertions().add(responseAssertionResult);
}
}
responseResult.setConsole(getConsole());
@ -336,7 +340,11 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
String start = "RPC Protocol: ";
String end = "://";
if (StringUtils.contains(body, start)) {
return StringUtils.substringBetween(body, start, end).toUpperCase();
String protocol = StringUtils.substringBetween(body, start, end);
if (StringUtils.isNotEmpty(protocol)) {
return protocol.toUpperCase();
}
return RequestType.DUBBO;
} else {
// Http Method
String method = StringUtils.substringBefore(body, " ");

View File

@ -1,5 +1,6 @@
package io.metersphere.api.jmeter;
import com.alibaba.fastjson.JSON;
import io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample;
import org.apache.jmeter.extractor.JSR223PostProcessor;
import org.apache.jmeter.extractor.RegexExtractor;
@ -30,12 +31,29 @@ public class JMeterVars {
* @param vars
* @param extract
*/
public static void addVars(Integer testId, JMeterVariables vars, String extract) {
public static void addVars(Integer testId, JMeterVariables vars, String extract) {
JMeterVariables vs = new JMeterVariables();
if (!StringUtils.isEmpty(extract) && vars != null) {
List<String> extracts = Arrays.asList(extract.split(";"));
Optional.ofNullable(extracts).orElse(new ArrayList<>()).forEach(item -> {
String nrKey = item + "_matchNr";
Object nr = vars.get(nrKey);
if (nr != null) {
int nrv = 0;
try {
nrv = Integer.valueOf(String.valueOf(nr));
} catch (Exception e) {
}
if (nrv > 0) {
List<Object> data = new ArrayList<>();
for (int i = 1; i < nrv + 1; i++) {
data.add(vars.get(item + "_" + i));
}
String array = JSON.toJSONString(data);
vars.put(item, array);
}
}
vs.put(item, vars.get(item) == null ? "" : vars.get(item));
});
vs.remove("TESTSTART.MS"); // 标示变量移除

View File

@ -97,8 +97,10 @@ public class Swagger2Parser extends ApiImportAbstractParser {
String name = "";
if (StringUtils.isNotBlank(operation.getSummary())) {
name = operation.getSummary();
} else {
} else if (StringUtils.isNotBlank(operation.getOperationId())) {
name = operation.getOperationId();
} else {
name = path;
}
return buildApiDefinition(id, name, path, method);
}
@ -275,6 +277,11 @@ public class Swagger2Parser extends ApiImportAbstractParser {
} else if (value instanceof RefProperty) {
RefProperty refProperty = (RefProperty) value;
String simpleRef = refProperty.getSimpleRef();
if (refSet.contains(simpleRef)) {
//避免嵌套死循环
jsonObject.put(key, new JSONArray());
return;
}
refSet.add(simpleRef);
Model model = definitions.get(simpleRef);
jsonObject.put(key, getBodyParameters(model.getProperties(), refSet));

View File

@ -129,8 +129,10 @@ public class Swagger3Parser extends ApiImportAbstractParser {
String name = "";
if (StringUtils.isNotBlank(operation.getSummary())) {
name = operation.getSummary();
} else {
} else if (StringUtils.isNotBlank(operation.getOperationId())) {
name = operation.getOperationId();
} else {
name = path;
}
return buildApiDefinition(id, name, path, method);
}

View File

@ -24,6 +24,7 @@ import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
@ -323,8 +324,12 @@ public class ApiAutomationService {
boolean isFirst = true;
for (ApiScenarioWithBLOBs item : apiScenarios) {
if (item.getStepTotal() == 0) {
MSException.throwException(item.getName() + "" + Translator.get("automation_exec_info"));
break;
// 只有一个场景且没有测试步骤则提示
if (apiScenarios.size() == 1) {
MSException.throwException((item.getName() + "" + Translator.get("automation_exec_info")));
}
LogUtil.warn(item.getName() + "" + Translator.get("automation_exec_info"));
continue;
}
MsThreadGroup group = new MsThreadGroup();
group.setLabel(item.getName());
@ -342,14 +347,12 @@ public class ApiAutomationService {
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取
if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) {
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"),
new TypeReference<LinkedList<MsTestElement>>() {
});
new TypeReference<LinkedList<MsTestElement>>() {});
scenario.setHashTree(elements);
}
if (StringUtils.isNotEmpty(element.getString("variables"))) {
LinkedList<KeyValue> variables = mapper.readValue(element.getString("variables"),
new TypeReference<LinkedList<KeyValue>>() {
});
new TypeReference<LinkedList<KeyValue>>() {});
scenario.setVariables(variables);
}
group.setEnableCookieShare(scenario.isEnableCookieShare());

View File

@ -1,5 +1,6 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
@ -11,6 +12,8 @@ import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.definition.request.MsThreadGroup;
import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionMapper;
@ -380,13 +383,33 @@ public class ApiTestCaseService {
if (request.isSelectAllDate()) {
ids = this.getAllApiCaseIdsByFontedSelect(request.getFilters(), request.getModuleIds(), request.getName(), request.getProjectId(), request.getProtocol(), request.getUnSelectIds(), request.getStatus());
}
request.cleanSelectParam();
ApiTestCaseExample apiDefinitionExample = new ApiTestCaseExample();
apiDefinitionExample.createCriteria().andIdIn(ids);
ApiTestCaseWithBLOBs apiDefinitionWithBLOBs = new ApiTestCaseWithBLOBs();
BeanUtils.copyBean(apiDefinitionWithBLOBs, request);
apiDefinitionWithBLOBs.setUpdateTime(System.currentTimeMillis());
apiTestCaseMapper.updateByExampleSelective(apiDefinitionWithBLOBs, apiDefinitionExample);
if (StringUtils.isNotEmpty(request.getPriority())) {
ApiTestCaseWithBLOBs apiDefinitionWithBLOBs = new ApiTestCaseWithBLOBs();
apiDefinitionWithBLOBs.setPriority(request.getPriority());
apiDefinitionWithBLOBs.setUpdateTime(System.currentTimeMillis());
apiTestCaseMapper.updateByExampleSelective(apiDefinitionWithBLOBs, apiDefinitionExample);
}
if ((StringUtils.isNotEmpty(request.getMethod()) || StringUtils.isNotEmpty(request.getPath())) && request.getProtocol().equals(RequestType.HTTP)) {
List<ApiTestCaseWithBLOBs> bloBs = apiTestCaseMapper.selectByExampleWithBLOBs(apiDefinitionExample);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiTestCaseMapper batchMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
bloBs.forEach(apiTestCase -> {
MsHTTPSamplerProxy req = JSON.parseObject(apiTestCase.getRequest(), MsHTTPSamplerProxy.class);
if (StringUtils.isNotEmpty(request.getMethod())) {
req.setMethod(request.getMethod());
}
if (StringUtils.isNotEmpty(request.getPath())) {
req.setPath(request.getPath());
}
String requestStr = JSON.toJSONString(req);
apiTestCase.setRequest(requestStr);
batchMapper.updateByPrimaryKeySelective(apiTestCase);
});
sqlSession.flushStatements();
}
}
private List<String> getAllApiCaseIdsByFontedSelect(Map<String, List<String>> filters, List<String> moduleIds, String name, String projectId, String protocol, List<String> unSelectIds, String status) {

View File

@ -310,20 +310,9 @@
</select>
<select id="listRelevance" resultType="io.metersphere.api.dto.definition.ApiDefinitionResult">
select api_definition.id, api_definition.project_id, api_definition.num,
api_definition.name,api_definition.protocol,api_definition.path,api_definition.module_id,api_definition.module_path,api_definition.method,
api_definition.description,api_definition.request,api_definition.response,api_definition.environment_id,
api_definition.status, api_definition.user_id, api_definition.create_time, api_definition.update_time
select
<include refid="io.metersphere.base.mapper.ApiDefinitionMapper.Base_Column_List"/>
from api_definition
inner join
api_test_case c
on c.api_definition_id = api_definition.id
and not exists (
select id
from test_plan_api_case t
where t.api_case_id = c.id
and t.test_plan_id = #{request.planId}
)
<where>
<if test="request.combine != null">
<include refid="combine">
@ -359,6 +348,17 @@
#{value}
</foreach>
</if>
and exists (
select id
from api_test_case c
where c.api_definition_id = api_definition.id
and not exists (
select id
from test_plan_api_case t
where t.api_case_id = c.id
and t.test_plan_id = #{request.planId}
)
)
</where>
<if test="request.orders != null and request.orders.size() > 0">
order by

View File

@ -15,4 +15,6 @@ public interface ExtTestPlanApiCaseMapper {
List<String> getExecResultByPlanId(String planId);
List<String> getIdsByPlanId(String planId);
List<String> getNotRelevanceCaseIds(@Param("planId")String planId, @Param("relevanceProjectIds")List<String> relevanceProjectIds);
}

View File

@ -18,7 +18,7 @@
<select id="list" resultType="io.metersphere.api.dto.definition.TestPlanApiCaseDTO">
select
t.id, t.environment_id, t.create_time, t.update_time,
c.id as case_id, c.project_id, c.name, c.api_definition_id, c.priority, c.description, c.create_user_id, c.update_user_id,
c.id as case_id, c.project_id, c.name, c.api_definition_id, c.priority, c.description, c.create_user_id, c.update_user_id, c.num,
a.module_id, a.path, a.protocol, t.status execResult
from
test_plan_api_case t
@ -90,17 +90,31 @@
</foreach>
</if>
</select>
<select id="getExecResultByPlanId" resultType="java.lang.String">
select status
from
test_plan_api_case
where test_plan_id = #{planId}
</select>
<select id="getExecResultByPlanId" resultType="java.lang.String">
select status
from
test_plan_api_case
where test_plan_id = #{planId}
</select>
<select id="getIdsByPlanId" resultType="java.lang.String">
select id from
from test_plan_api_case
where id = #{planId}
</select>
<select id="getIdsByPlanId" resultType="java.lang.String">
select id
from test_plan_api_case
where id = #{planId}
</select>
<select id="getNotRelevanceCaseIds" resultType="java.lang.String">
select t.id
from test_plan_api_case t
inner join api_test_case c
on c.id = t.api_case_id
<if test="relevanceProjectIds != null and relevanceProjectIds.size() > 0">
and c.project_id not in
<foreach collection="relevanceProjectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
</if>
where t.test_plan_id = #{planId}
</select>
</mapper>

View File

@ -21,4 +21,6 @@ public interface ExtTestPlanScenarioCaseMapper {
List<String> getExecResultByPlanId(String planId);
List<String> getIdsByPlanId(String planId);
List<String> getNotRelevanceCaseIds(String planId, List<String> relevanceProjectIds);
}

View File

@ -92,4 +92,18 @@
where test_plan_id = #{planId}
</select>
<select id="getNotRelevanceCaseIds" resultType="java.lang.String">
select t.id
from test_plan_api_scenario t
inner join api_scenario c
on c.id = t.api_scenario_id
<if test="relevanceProjectIds != null and relevanceProjectIds.size() > 0">
and c.project_id not in
<foreach collection="relevanceProjectIds" item="projectId" separator="," open="(" close=")">
#{projectId}
</foreach>
</if>
where t.test_plan_id = #{planId}
</select>
</mapper>

View File

@ -88,28 +88,22 @@ public interface ParamConstants {
}
}
enum MAIL {
SERVER("smtp.server", 1),
PORT("smtp.port", 2),
ACCOUNT("smtp.account", 3),
PASSWORD("smtp.password", 4),
SSL("smtp.ssl", 5),
TLS("smtp.tls", 6),
ANON("smtp.anon", 7);
enum MAIL implements ParamConstants{
SERVER("smtp.host"),
PORT("smtp.port"),
ACCOUNT("smtp.account"),
PASSWORD("smtp.password"),
SSL("smtp.ssl"),
TLS("smtp.tls"),
RECIPIENTS("smtp.recipient");
private String key;
private Integer value;
private String value;
private MAIL(String key, Integer value) {
this.key = key;
private MAIL(String value) {
this.value = value;
}
public String getKey() {
return this.key;
}
public Integer getValue() {
public String getValue() {
return this.value;
}
}

View File

@ -0,0 +1,310 @@
package io.metersphere.commons.json;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.cfg.ValidationConfiguration;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.processors.syntax.SyntaxValidator;
import com.google.gson.*;
import io.metersphere.commons.utils.ScriptEngineUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
public class JSONSchemaGenerator {
private static void generator(String json, JSONObject obj) {
analyzeSchema(json, obj);
}
private static void analyzeSchema(String json, JSONObject rootObj) {
// Let's start with the root element of the file
JsonObject rootElement = null;
try {
JsonParser jsonParser = new JsonParser();
JsonElement inputElement = jsonParser.parse(json);
rootElement = inputElement.getAsJsonObject();
} catch (Exception e) {
e.printStackTrace();
}
analyzeRootSchemaElement(rootElement, rootObj);
}
private static void analyzeRootSchemaElement(JsonObject rootElement, JSONObject rootObj) {
if (rootElement.has("type") || rootElement.has("allOf")) {
analyzeObject(rootElement, rootObj);
}
if (rootElement.has("definitions")) {
// Section 9 in json-validation
analyzeDefinitions(rootElement);
}
}
private static void analyzeObject(JsonObject object, JSONObject rootObj) {
// Creating the concept
if (object.has("title")) {
// 暂不处理后续使用时再加
String title = object.get("title").getAsString();
}
if (object.has("description")) {
// 暂不处理后续使用时再加
String description = object.get("description").getAsString();
}
if (object.has("allOf")) {
JsonArray allOfArray = object.get("allOf").getAsJsonArray();
for (JsonElement allOfElement : allOfArray) {
JsonObject allOfElementObj = allOfElement.getAsJsonObject();
if (allOfElementObj.has("$ref")) {
// 暂不处理后续使用时再加
String ref = allOfElementObj.get("$ref").getAsString();
} else if (allOfElementObj.has("properties")) {
// Properties elements will become the attributes/references of the element
JsonObject propertiesObj = allOfElementObj.get("properties").getAsJsonObject();
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
String propertyKey = entry.getKey();
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
analyzeProperty(rootObj, propertyKey, propertyObj);
}
}
}
} else if (object.has("oneOf")) {
// 暂不处理后续使用时再加
} else if (object.has("properties")) {
JsonObject propertiesObj = object.get("properties").getAsJsonObject();
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
String propertyKey = entry.getKey();
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
analyzeProperty(rootObj, propertyKey, propertyObj);
}
} else if (object.has("type") && !object.get("type").getAsString().equals("object")) {
analyzeProperty(rootObj, object.getAsString(), object);
}
if (object.has("required")) {
// 必选项暂不处理后续使用时再加
}
}
private static void analyzeProperty(JSONObject concept, String propertyName, JsonObject object) {
if (object.has("type")) {
String propertyObjType = null;
if (object.get("type") instanceof JsonPrimitive) {
propertyObjType = object.get("type").getAsString();
} else if (object.get("type") instanceof JsonArray) {
JsonArray typeArray = (JsonArray) object.get("type").getAsJsonArray();
propertyObjType = typeArray.get(0).getAsString();
if (typeArray.size() > 1) {
if (typeArray.get(1).getAsString().equals("null")) {
// 暂不处理后续使用时再加
}
}
}
if (object.has("enum")) {
concept.put(propertyName, analyzeEnumProperty(object));
} else if (propertyObjType.equals("string")) {
// 先设置空值
concept.put(propertyName, null);
if (object.has("format")) {
String propertyFormat = object.get("format").getAsString();
if (propertyFormat.equals("date-time")) {
}
}
if (object.has("default")) {
concept.put(propertyName, object.get("default"));
}
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString()) && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
String value = ScriptEngineUtils.calculate(object.get("mock").getAsJsonObject().get("mock").getAsString());
concept.put(propertyName, value);
}
if (object.has("maxLength")) {
}
// Section 6.3.1 in json-schema-validation. Resolved as OCL
if (object.has("minLength")) {
}
// Section 6.3.2 in json-schema-validation. Resolved as OCL
if (object.has("pattern")) {
// Section 6.3.3 in json-schema-validation. Resolved as OCL, possible?
// TODO 6.3.3 in json-schema-validation
}
} else if (propertyObjType.equals("integer") || propertyObjType.equals("number")) {
// 先设置空值
concept.put(propertyName, 0);
if (object.has("default")) {
concept.put(propertyName, object.get("default"));
}
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
String value = ScriptEngineUtils.calculate(object.get("mock").getAsJsonObject().get("mock").toString());
concept.put(propertyName, value);
}
if (object.has("multipleOf")) {
}
// Section 6.2.1 in json-schema-validation. Resolved as OCL
if (object.has("maximum")) {
}
// Section 6.2.2 in json-schema-validation. Resolved as OCL
if (object.has("exclusiveMaximum")) {
}
// Section 6.2.3 in json-schema-validation. Resolved as OCL
if (object.has("minimum")) {
}
// Section 6.2.4 in json-schema-validation. Resolved as OCL
if (object.has("exclusiveMinimum")) {
}
// Section 6.2.5 in json-schema-validation. Resolved as OCL
} else if (propertyObjType.equals("boolean")) {
// 先设置空值
concept.put(propertyName, false);
if (object.has("default")) {
concept.put(propertyName, object.get("default"));
}
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
String value = ScriptEngineUtils.calculate(object.get("mock").getAsJsonObject().get("mock").toString());
concept.put(propertyName, value);
}
} else if (propertyObjType.equals("array")) {
// 先设置空值
List<Object> array = new LinkedList<>();
JsonObject itemsObject = null;
if (object.has("items") && object.get("items").isJsonArray()) {
itemsObject = object.get("items").getAsJsonArray().get(0).getAsJsonObject();
} else {
itemsObject = object.get("items").getAsJsonObject();
}
if (object.has("items")) {
if (itemsObject.has("enum")) {
array.add(analyzeEnumProperty(object));
} else if (itemsObject.has("type") && itemsObject.get("type").getAsString().equals("string")) {
if (object.has("default")) {
array.add(object.get("default"));
} else if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
String value = ScriptEngineUtils.calculate(object.get("mock").getAsJsonObject().get("mock").toString());
array.add(object.get(value));
} else {
array.add(null);
}
} else if (itemsObject.has("type") && itemsObject.get("type").getAsString().equals("number")) {
if (object.has("default")) {
array.add(object.get("default"));
} else if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
String value = ScriptEngineUtils.calculate(object.get("mock").getAsJsonObject().get("mock").toString());
array.add(object.get(value));
} else {
array.add(0);
}
} else if (itemsObject.has("oneOf")) {
} else if (itemsObject.has("anyOf")) {
} else if (itemsObject.has("allOf")) {
// TODO
} else if (itemsObject.has("properties")) {
JSONObject propertyConcept = new JSONObject();
JsonObject propertiesObj = itemsObject.get("properties").getAsJsonObject();
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
String propertyKey = entry.getKey();
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
analyzeProperty(propertyConcept, propertyKey, propertyObj);
}
array.add(propertyConcept);
} else if (itemsObject.has("$ref")) {
analyzeRef(concept, propertyName, itemsObject);
}
} else if (object.has("items") && object.get("items").isJsonArray()) {
JsonArray itemsObjectArray = object.get("items").getAsJsonArray();
array.add(itemsObjectArray);
}
concept.put(propertyName, array);
} else if (propertyObjType.equals("object")) {
JSONObject obj = new JSONObject();
concept.put(propertyName, obj);
analyzeObject(object, obj);
}
} else if (object.has("$ref")) {
analyzeRef(concept, propertyName, object);
} else if (object.has("oneOf")) {
// Section 6.7.3 in json-schema-validation
} else if (object.has("anyOf")) {
// Section 6.7.2 in json-schema-validation
}
}
private static List<Object> analyzeEnumProperty(JsonObject object) {
JsonArray enumValues = object.get("enum").getAsJsonArray();
List<Object> list = new LinkedList<>();
for (JsonElement enumValueElem : enumValues) {
String enumValue = enumValueElem.getAsString();
list.add(enumValue);
}
return list;
}
private static void analyzeRef(JSONObject concept, String propertyName, JsonObject object) {
String ref = object.get("$ref").getAsString();
}
private static void analyzeDefinitions(JsonObject object) {
JsonObject definitionsObj = object.get("definitions").getAsJsonObject();
for (Entry<String, JsonElement> entry : definitionsObj.entrySet()) {
String definitionKey = entry.getKey();
JsonObject definitionObj = definitionsObj.get(definitionKey).getAsJsonObject();
JSONObject obj = new JSONObject();
analyzeRootSchemaElement(definitionObj, obj);
}
}
private static final SyntaxValidator VALIDATOR = new SyntaxValidator(ValidationConfiguration.byDefault());
public static String getJson(String jsonSchema) {
if (StringUtils.isEmpty(jsonSchema)) {
return null;
}
try {
JsonNode jsonNode = JsonLoader.fromString(jsonSchema);
ProcessingReport report = VALIDATOR.validateSchema(jsonNode);
if (report.isSuccess()) {
JSONObject root = new JSONObject();
generator(jsonSchema, root);
// 格式化返回
Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
return gson.toJson(root);
} else {
return report.getExceptionThreshold().toString();
}
} catch (Exception ex) {
ex.printStackTrace();
return ex.getMessage();
}
}
}

View File

@ -5,6 +5,7 @@ import io.metersphere.commons.constants.ParamConstants;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.ldap.domain.LdapInfo;
import io.metersphere.notice.domain.MailInfo;
import io.metersphere.service.SystemParameterService;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
@ -38,7 +39,7 @@ public class SystemParameterController {
@GetMapping("/mail/info")
@RequiresRoles(value = {RoleConstants.ADMIN})
public Object mailInfo() {
public MailInfo mailInfo() {
return SystemParameterService.mailInfo(ParamConstants.Classify.MAIL.getValue());
}

View File

@ -0,0 +1,15 @@
package io.metersphere.notice.domain;
import lombok.Data;
@Data
public class MailInfo {
private String host;
private String port;
private String account;
private String password;
private String ssl;
private String tls;
private String recipient;
}

View File

@ -11,15 +11,18 @@ import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.ldap.domain.LdapInfo;
import io.metersphere.notice.domain.MailInfo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.*;
@ -56,7 +59,7 @@ public class SystemParameterService {
parameters.forEach(parameter -> {
SystemParameterExample example = new SystemParameterExample();
if (parameter.getParamKey().equals(ParamConstants.MAIL.PASSWORD.getKey())) {
if (parameter.getParamKey().equals(ParamConstants.MAIL.PASSWORD.getValue())) {
if (!StringUtils.isBlank(parameter.getParamValue())) {
String string = EncryptUtils.aesEncrypt(parameter.getParamValue()).toString();
parameter.setParamValue(string);
@ -82,23 +85,16 @@ public class SystemParameterService {
public void testConnection(HashMap<String, String> hashMap) {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setDefaultEncoding("UTF-8");
javaMailSender.setHost(hashMap.get(ParamConstants.MAIL.SERVER.getKey()));
javaMailSender.setPort(Integer.valueOf(hashMap.get(ParamConstants.MAIL.PORT.getKey())));
javaMailSender.setUsername(hashMap.get(ParamConstants.MAIL.ACCOUNT.getKey()));
javaMailSender.setPassword(hashMap.get(ParamConstants.MAIL.PASSWORD.getKey()));
javaMailSender.setHost(hashMap.get(ParamConstants.MAIL.SERVER.getValue()));
javaMailSender.setPort(Integer.valueOf(hashMap.get(ParamConstants.MAIL.PORT.getValue())));
javaMailSender.setUsername(hashMap.get(ParamConstants.MAIL.ACCOUNT.getValue()));
javaMailSender.setPassword(hashMap.get(ParamConstants.MAIL.PASSWORD.getValue()));
Properties props = new Properties();
boolean isAnon = Boolean.parseBoolean(hashMap.get(ParamConstants.MAIL.ANON.getKey()));
if (isAnon) {
props.put("mail.smtp.auth", "false");
javaMailSender.setUsername(null);
javaMailSender.setPassword(null);
} else {
props.put("mail.smtp.auth", "true");
}
if (BooleanUtils.toBoolean(hashMap.get(ParamConstants.MAIL.SSL.getKey()))) {
String recipients = hashMap.get(ParamConstants.MAIL.RECIPIENTS.getValue());
if (BooleanUtils.toBoolean(hashMap.get(ParamConstants.MAIL.SSL.getValue()))) {
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
if (BooleanUtils.toBoolean(hashMap.get(ParamConstants.MAIL.TLS.getKey()))) {
if (BooleanUtils.toBoolean(hashMap.get(ParamConstants.MAIL.TLS.getValue()))) {
props.put("mail.smtp.starttls.enable", "true");
}
props.put("mail.smtp.timeout", "30000");
@ -110,39 +106,53 @@ public class SystemParameterService {
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("connection_failed"));
}
if(!StringUtils.isBlank(recipients)){
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = null;
try {
helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom(javaMailSender.getUsername());
helper.setSubject("MeterSphere测试邮件 " );
helper.setText("这是一封测试邮件,邮件发送成功", true);
helper.setTo(recipients);
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("connection_failed"));
}
}
}
public String getVersion() {
return System.getenv("MS_VERSION");
}
public Object mailInfo(String type) {
public MailInfo mailInfo(String type) {
List<SystemParameter> paramList = this.getParamList(type);
if (CollectionUtils.isEmpty(paramList)) {
paramList = new ArrayList<>();
ParamConstants.MAIL[] values = ParamConstants.MAIL.values();
for (ParamConstants.MAIL value : values) {
SystemParameter systemParameter = new SystemParameter();
if (value.equals(ParamConstants.MAIL.PASSWORD)) {
systemParameter.setType(ParamConstants.Type.PASSWORD.getValue());
} else {
systemParameter.setType(ParamConstants.Type.TEXT.getValue());
MailInfo mailInfo=new MailInfo ();
if (!CollectionUtils.isEmpty(paramList)) {
for (SystemParameter param : paramList) {
if (StringUtils.equals(param.getParamKey(),ParamConstants.MAIL.SERVER.getValue() )) {
mailInfo.setHost(param.getParamValue());
} else if (StringUtils.equals(param.getParamKey(), ParamConstants.MAIL.PORT.getValue())) {
mailInfo.setPort(param.getParamValue());
} else if (StringUtils.equals(param.getParamKey(), ParamConstants.MAIL.ACCOUNT.getValue())) {
mailInfo.setAccount(param.getParamValue());
} else if (StringUtils.equals(param.getParamKey(), ParamConstants.MAIL.PASSWORD.getValue())) {
String password = EncryptUtils.aesDecrypt(param.getParamValue()).toString();
mailInfo.setPassword(password);
} else if (StringUtils.equals(param.getParamKey(), ParamConstants.MAIL.SSL.getValue())) {
mailInfo.setSsl(param.getParamValue());
} else if (StringUtils.equals(param.getParamKey(), ParamConstants.MAIL.TLS.getValue())) {
mailInfo.setTls(param.getParamValue());
} else if (StringUtils.equals(param.getParamKey(), ParamConstants.MAIL.RECIPIENTS.getValue())) {
mailInfo.setRecipient(param.getParamValue());
}
systemParameter.setParamKey(value.getKey());
systemParameter.setSort(value.getValue());
paramList.add(systemParameter);
}
} else {
paramList.stream().filter(param -> param.getParamKey().equals(ParamConstants.MAIL.PASSWORD.getKey())).forEach(param -> {
if (!StringUtils.isBlank(param.getParamValue())) {
String string = EncryptUtils.aesDecrypt(param.getParamValue()).toString();
param.setParamValue(string);
}
});
}
paramList.sort(Comparator.comparingInt(SystemParameter::getSort));
return paramList;
return mailInfo;
}
public void saveLdap(List<SystemParameter> parameters) {

View File

@ -46,11 +46,11 @@ public class TestPlanController {
/*jenkins测试计划*/
@GetMapping("/list/all/{projectId}/{workspaceId}")
public List<TestPlanDTO> listByProjectId(@PathVariable String projectId, @PathVariable String workspaceId) {
public List<TestPlanDTOWithMetric> listByProjectId(@PathVariable String projectId, @PathVariable String workspaceId) {
QueryTestPlanRequest request = new QueryTestPlanRequest();
request.setWorkspaceId(workspaceId);
request.setProjectId(projectId);
return testPlanService.listTestPlanByProject(request);
return testPlanService.listTestPlan(request);
}
@PostMapping("/list/all")

View File

@ -77,6 +77,9 @@ public class TestPlanApiCaseService {
}
public void deleteApiCaseBath(TestPlanApiCaseBatchRequest request) {
if (CollectionUtils.isEmpty(request.getIds())) {
return;
}
apiDefinitionExecResultService.deleteByResourceIds(request.getIds());
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria()
@ -97,4 +100,11 @@ public class TestPlanApiCaseService {
apiCase.setStatus(status);
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
}
public void deleteByRelevanceProjectIds(String planId, List<String> relevanceProjectIds) {
TestPlanApiCaseBatchRequest request = new TestPlanApiCaseBatchRequest();
request.setPlanId(planId);
request.setIds(extTestPlanApiCaseMapper.getNotRelevanceCaseIds(planId, relevanceProjectIds));
deleteApiCaseBath(request);
}
}

View File

@ -73,6 +73,9 @@ public class TestPlanScenarioCaseService {
}
public void deleteApiCaseBath(TestPlanApiCaseBatchRequest request) {
if (CollectionUtils.isEmpty(request.getIds())) {
return;
}
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria()
.andIdIn(request.getIds());
@ -110,4 +113,11 @@ public class TestPlanScenarioCaseService {
request.setIds(ids);
deleteApiCaseBath(request);
}
public void deleteByRelevanceProjectIds(String planId, List<String> relevanceProjectIds) {
TestPlanApiCaseBatchRequest request = new TestPlanApiCaseBatchRequest();
request.setIds(extTestPlanScenarioCaseMapper.getNotRelevanceCaseIds(planId, relevanceProjectIds));
request.setPlanId(planId);
deleteApiCaseBath(request);
}
}

View File

@ -10,7 +10,6 @@ import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtProjectMapper;
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
@ -27,7 +26,6 @@ import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.track.Factory.ReportComponentFactory;
import io.metersphere.track.domain.ReportComponent;
import io.metersphere.track.dto.TestCaseReportMetricDTO;
import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.TestPlanDTO;
@ -70,8 +68,6 @@ public class TestPlanService {
@Resource
TestPlanTestCaseService testPlanTestCaseService;
@Resource
ExtProjectMapper extProjectMapper;
@Resource
TestCaseReportMapper testCaseReportMapper;
@Resource
TestPlanProjectMapper testPlanProjectMapper;
@ -230,6 +226,14 @@ public class TestPlanService {
criteria.andCaseIdNotIn(caseIds);
}
testPlanTestCaseMapper.deleteByExample(testPlanTestCaseExample);
List<String> relevanceProjectIds = new ArrayList<>();
relevanceProjectIds.add(testPlan.getProjectId());
if (!CollectionUtils.isEmpty(testPlan.getProjectIds())) {
relevanceProjectIds.addAll(testPlan.getProjectIds());
}
testPlanApiCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
testPlanScenarioCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
}
}

View File

@ -57,7 +57,6 @@ public class TestPlanTestCaseService {
}
public List<TestPlanCaseDTO> listByPlanId(QueryTestPlanCaseRequest request) {
List<TestPlanCaseDTO> list = extTestPlanTestCaseMapper.listByPlanId(request);
return list;
}

View File

@ -21,9 +21,12 @@
"echarts": "^4.6.0",
"el-table-infinite-scroll": "^1.0.10",
"element-ui": "^2.13.0",
"generate-schema": "^2.6.0",
"html2canvas": "^1.0.0-rc.7",
"js-base64": "^3.4.4",
"json-bigint": "^1.0.0",
"json-schema-faker": "^0.5.0-rcv.32",
"json5": "^2.1.3",
"jsoneditor": "^9.1.2",
"jspdf": "^2.1.1",
"md5": "^2.3.0",

View File

@ -108,7 +108,7 @@ export default {
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
loading: false,
currentProjectId: "",

View File

@ -62,7 +62,7 @@
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'"/>
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
<ms-request-result-tail :response="request.requestResult" ref="runResult"/>
<ms-request-result-tail :currentProtocol="request.protocol" :response="request.requestResult" ref="runResult"/>
<!-- 保存操作 -->
<el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)" v-if="!request.referenced">

View File

@ -623,12 +623,19 @@
this.$refs.tag.open();
},
remove(row, node) {
const parent = node.parent
const hashTree = parent.data.hashTree || parent.data;
const index = hashTree.findIndex(d => d.resourceId != undefined && row.resourceId != undefined && d.resourceId === row.resourceId)
hashTree.splice(index, 1);
this.sort();
this.reload();
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + row.name + " ", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
const parent = node.parent
const hashTree = parent.data.hashTree || parent.data;
const index = hashTree.findIndex(d => d.resourceId != undefined && row.resourceId != undefined && d.resourceId === row.resourceId)
hashTree.splice(index, 1);
this.sort();
this.reload();
}
}
});
}
,
copyRow(row, node) {

View File

@ -35,7 +35,7 @@
ref="codeEdit"/>
</el-col>
<el-col :span="4" class="script-index">
<ms-dropdown :default-command="jsr223ProcessorData.language" :commands="languages" @command="languageChange"/>
<ms-dropdown :default-command="jsr223ProcessorData.scriptLanguage" :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>
@ -135,7 +135,7 @@
this.jsr223ProcessorData.script = "";
}
this.jsr223ProcessorData.script += template.value;
if (this.jsr223ProcessorData.language === 'beanshell') {
if (this.jsr223ProcessorData.scriptLanguage === 'beanshell') {
this.jsr223ProcessorData.script += ';';
}
this.reload();
@ -151,7 +151,7 @@
this.$nextTick(() => (this.isCodeEditAlive = true));
},
languageChange(language) {
this.jsr223ProcessorData.language = language;
this.jsr223ProcessorData.scriptLanguage = language;
},
changeActive() {
this.jsr223ProcessorData.active = !this.jsr223ProcessorData.active;

View File

@ -3,9 +3,10 @@
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange">
<ms-environment-select :project-id="projectId" v-if="isTestPlan" :is-read-only="isReadOnly" @setEnvironment="setEnvironment"/>
<ms-environment-select :project-id="projectId" v-if="isTestPlan" :is-read-only="isReadOnly" @setEnvironment="setEnvironment"/>
<el-input placeholder="搜索" @blur="initTable" class="search-input" size="small" @keyup.enter.native="initTable" v-model="condition.name"/>
<el-input placeholder="搜索" @blur="initTable" class="search-input" size="small" @keyup.enter.native="initTable" v-model="condition.name"/>
<el-table v-loading="result.loading"
border
@ -253,8 +254,8 @@
.search-input {
float: right;
width: 300px;
/*margin-bottom: 20px;*/
width: 30%;
margin-bottom: 20px;
margin-right: 20px;
}

View File

@ -1,5 +1,5 @@
<template>
<div>
<div style="min-width: 1200px;margin-bottom: 20px">
<span class="kv-description" v-if="description">
{{ description }}
</span>

View File

@ -90,193 +90,194 @@
</template>
<script>
import {calculate, Scenario} from "../model/ApiTestModel";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
import {calculate, Scenario} from "../model/ApiTestModel";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
export default {
name: "MsApiVariableAdvance",
props: {
parameters: Array,
environment: Object,
scenario: Scenario,
currentItem: Object,
},
data() {
return {
itemValueVisible: false,
filterText: '',
environmentParams: [],
scenarioParams: [],
preRequests: [],
preRequestParams: [],
treeProps: {children: 'children', label: 'name'},
currentTab: 0,
itemValue: null,
itemValuePreview: null,
funcs: [
{name: "md5"},
{name: "base64"},
{name: "unbase64"},
{
name: "substr",
params: [{name: "start"}, {name: "length"}]
},
{
name: "concat",
params: [{name: "suffix"}]
},
{name: "lconcat", params: [{name: "prefix"}]},
{name: "sha1"},
{name: "sha224"},
{name: "sha256"},
{name: "sha384"},
{name: "sha512"},
{name: "lower"},
{name: "upper"},
{name: "length"},
{name: "number"}
],
mockFuncs: MOCKJS_FUNC.map(f => {
return {name: f.name, value: f.name}
}),
jmeterFuncs: JMETER_FUNC,
mockVariableFuncs: [],
jmeterVariableFuncs: [],
}
},
computed: {
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
mounted() {
this.prepareData();
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
export default {
name: "MsApiVariableAdvance",
props: {
parameters: Array,
environment: Object,
scenario: Scenario,
currentItem: Object,
},
},
methods: {
open() {
this.itemValueVisible = true;
},
prepareData() {
if (this.scenario) {
let variables = this.scenario.variables;
this.scenarioParams = [
data() {
return {
itemValueVisible: false,
filterText: '',
environmentParams: [],
scenarioParams: [],
preRequests: [],
preRequestParams: [],
treeProps: {children: 'children', label: 'name'},
currentTab: 0,
itemValue: null,
itemValuePreview: null,
funcs: [
{name: "md5"},
{name: "base64"},
{name: "unbase64"},
{
name: this.scenario.name,
children: variables.filter(v => v.name).map(v => {
return {name: v.name, value: '${' + v.name + '}'}
}),
}
];
if (this.environment) {
let variables = this.environment.config.commonConfig.variables;
this.environmentParams = [
name: "substr",
params: [{name: "start"}, {name: "length"}]
},
{
name: "concat",
params: [{name: "suffix"}]
},
{name: "lconcat", params: [{name: "prefix"}]},
{name: "sha1"},
{name: "sha224"},
{name: "sha256"},
{name: "sha384"},
{name: "sha512"},
{name: "lower"},
{name: "upper"},
{name: "length"},
{name: "number"}
],
mockFuncs: MOCKJS_FUNC.map(f => {
return {name: f.name, value: f.name}
}),
jmeterFuncs: JMETER_FUNC,
mockVariableFuncs: [],
jmeterVariableFuncs: [],
}
},
computed: {
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
mounted() {
this.prepareData();
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
methods: {
open() {
this.itemValueVisible = true;
},
prepareData() {
if (this.scenario) {
let variables = this.scenario.variables;
this.scenarioParams = [
{
name: this.environment.name,
name: this.scenario.name,
children: variables.filter(v => v.name).map(v => {
return {name: v.name, value: '${' + v.name + '}'}
}),
}
];
if (this.environment) {
let variables = this.environment.config.commonConfig.variables;
this.environmentParams = [
{
name: this.environment.name,
children: variables.filter(v => v.name).map(v => {
return {name: v.name, value: '${' + v.name + '}'}
}),
}
];
}
let i = this.scenario.requests.indexOf(this.request);
this.preRequests = this.scenario.requests.slice(0, i);
this.preRequests.forEach(r => {
let js = r.extract.json.map(v => {
return {name: v.variable, value: v.value}
});
let xs = r.extract.xpath.map(v => {
return {name: v.variable, value: v.value}
});
let rx = r.extract.regex.map(v => {
return {name: v.variable, value: v.value}
});
let vs = [...js, ...xs, ...rx];
if (vs.length > 0) {
this.preRequestParams.push({name: r.name, children: vs});
}
});
}
let i = this.scenario.requests.indexOf(this.request);
this.preRequests = this.scenario.requests.slice(0, i);
this.preRequests.forEach(r => {
let js = r.extract.json.map(v => {
return {name: v.variable, value: v.value}
});
let xs = r.extract.xpath.map(v => {
return {name: v.variable, value: v.value}
});
let rx = r.extract.regex.map(v => {
return {name: v.variable, value: v.value}
});
let vs = [...js, ...xs, ...rx];
if (vs.length > 0) {
this.preRequestParams.push({name: r.name, children: vs});
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
selectVariable(node) {
this.itemValue = node.value;
},
selectTab(tab) {
this.currentTab = +tab.index;
this.itemValue = null;
this.itemValuePreview = null;
},
showPreview() {
//
if (!this.itemValue) {
return;
}
let index = this.itemValue.indexOf("|");
if (index > -1) {
this.itemValue = this.itemValue.substring(0, index).trim();
}
this.mockVariableFuncs.forEach(f => {
if (!f.name) {
return;
}
this.itemValue += "|" + f.name;
if (f.params) {
this.itemValue += ":" + f.params.map(p => p.value).join(",");
}
});
}
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
selectVariable(node) {
this.itemValue = node.value;
},
selectTab(tab) {
this.currentTab = +tab.index;
this.itemValue = null;
this.itemValuePreview = null;
},
showPreview() {
//
if (!this.itemValue) {
return;
}
let index = this.itemValue.indexOf("|");
if (index > -1) {
this.itemValue = this.itemValue.substring(0, index).trim();
}
this.mockVariableFuncs.forEach(f => {
if (!f.name) {
this.itemValuePreview = calculate(this.itemValue);
},
methodChange(itemFunc, func) {
let index = this.mockVariableFuncs.indexOf(itemFunc);
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
// deep copy
this.mockVariableFuncs.push(JSON.parse(JSON.stringify(func)));
this.showPreview();
},
addFunc() {
if (this.mockVariableFuncs.length > 4) {
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
return;
}
this.itemValue += "|" + f.name;
if (f.params) {
this.itemValue += ":" + f.params.map(p => p.value).join(",");
}
});
this.itemValuePreview = calculate(this.itemValue);
},
methodChange(itemFunc, func) {
let index = this.mockVariableFuncs.indexOf(itemFunc);
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
// deep copy
this.mockVariableFuncs.push(JSON.parse(JSON.stringify(func)));
this.showPreview();
},
addFunc() {
if (this.mockVariableFuncs.length > 4) {
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
return;
}
if (this.mockVariableFuncs.length > 0) {
let func = this.mockVariableFuncs[this.mockVariableFuncs.length - 1];
if (!func.name) {
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
return;
}
if (func.params) {
for (let j = 0; j < func.params.length; j++) {
if (!func.params[j].value) {
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
return;
if (this.mockVariableFuncs.length > 0) {
let func = this.mockVariableFuncs[this.mockVariableFuncs.length - 1];
if (!func.name) {
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
return;
}
if (func.params) {
for (let j = 0; j < func.params.length; j++) {
if (!func.params[j].value) {
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
return;
}
}
}
}
this.mockVariableFuncs.push({name: '', params: []});
},
saveAdvanced() {
this.currentItem.value = this.itemValue;
this.itemValueVisible = false;
this.mockVariableFuncs = [];
this.$emit('advancedRefresh', this.itemValue);
}
this.mockVariableFuncs.push({name: '', params: []});
},
saveAdvanced() {
this.currentItem.value = this.itemValue;
this.itemValueVisible = false;
this.mockVariableFuncs = [];
}
}
}
</script>
<style scoped>
.col-height {
height: 40vh;
overflow: auto;
}
.col-height {
height: 40vh;
overflow: auto;
}
</style>

View File

@ -14,7 +14,10 @@
<el-option v-for="(type, index) in typeArr" :key="index" :value="type.id" :label="type.name"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-form-item v-if="form.type ==='path'" :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-input size="small" v-model="form.value"/>
</el-form-item>
<el-form-item v-else :label="$t('test_track.case.updated_attr_value')" prop="value">
<el-select v-model="form.value" style="width: 80%" :filterable="filterable">
<el-option v-for="(option, index) in options" :key="index" :value="option.id" :label="option.label">
<div v-if="option.email">

View File

@ -1,5 +1,5 @@
<template>
<div style="min-width: 1200px;margin-bottom: 20px">
<div>
<el-radio-group v-model="body.type" size="mini">
<el-radio :disabled="isReadOnly" :label="type.FORM_DATA" @change="modeChange">
{{ $t('api_test.definition.request.body_form_data') }}
@ -40,8 +40,12 @@
type="body"
v-if="body.type == 'WWW_FORM'"/>
<div class="ms-body" v-if="body.type == 'JSON'">
<ms-json-code-edit @json-change="jsonChange" @onError="jsonError" :value="body.raw" ref="jsonCodeEdit"/>
<div v-if="body.type == 'JSON'">
<div style="padding: 10px">
<el-switch active-text="JSON-SCHEMA" v-model="body.format" active-value="JSON-SCHEMA"/>
</div>
<ms-json-code-edit v-if="body.format==='JSON-SCHEMA'" :body="body" ref="jsonCodeEdit"/>
<ms-code-edit v-else :read-only="isReadOnly" height="400px" :data.sync="body.raw" :modes="modes" :mode="'json'" ref="codeEdit"/>
</div>
<div class="ms-body" v-if="body.type == 'XML'">
@ -67,7 +71,7 @@
import MsApiKeyValue from "../ApiKeyValue";
import {BODY_TYPE, KeyValue} from "../../model/ApiTestModel";
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
import MsJsonCodeEdit from "../../../../common/components/MsJsonCodeEdit";
import MsJsonCodeEdit from "../../../../common/json-schema/JsonSchemaEditor";
import MsDropdown from "../../../../common/components/MsDropdown";
import MsApiVariable from "../ApiVariable";
import MsApiBinaryVariable from "./ApiBinaryVariable";
@ -101,10 +105,10 @@
data() {
return {
type: BODY_TYPE,
modes: ['text', 'json', 'xml', 'html']
modes: ['text', 'json', 'xml', 'html'],
jsonSchema: "JSON",
};
},
methods: {
modeChange(mode) {
switch (this.body.type) {
@ -148,12 +152,6 @@
}
}
},
jsonChange(json) {
this.body.raw = JSON.stringify(json);
},
jsonError(e) {
this.$error(e);
},
batchAdd() {
this.$refs.batchAddParameter.open();
},

View File

@ -1,5 +1,5 @@
<template>
<div>
<div style="min-width: 1200px;margin-bottom: 20px">
<span class="kv-description" v-if="description">
{{ description }}
</span>

View File

@ -140,10 +140,18 @@
methods: {
deleteCase(index, row) {
this.$get('/api/testcase/delete/' + row.id, () => {
this.$success(this.$t('commons.delete_success'));
this.$emit('refresh');
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + row.name + " ", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.$get('/api/testcase/delete/' + row.id, () => {
this.$success(this.$t('commons.delete_success'));
this.$emit('refresh');
});
}
}
});
},
singleRun(data) {
this.$emit('singleRun', data);

View File

@ -43,7 +43,8 @@
<el-option :key="0" :value="''">
<div style="margin-left: 40px">
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{$t('api_test.definition.select_comp.no_data')}},
</span><el-link type="primary" @click="createModules">{{$t('api_test.definition.select_comp.add_data')}}</el-link>
</span>
<el-link type="primary" @click="createModules">{{$t('api_test.definition.select_comp.add_data')}}</el-link>
</div>
</el-option>
</div>
@ -106,7 +107,6 @@
import {REQ_METHOD, API_STATUS} from "../../model/JsonData";
import MsJsr233Processor from "../processor/Jsr233Processor";
import {KeyValue} from "../../model/ApiTestModel";
// import {append} from "./../../../../track/common/NodeTree";
export default {
name: "MsAddCompleteHttpApi",
@ -172,7 +172,7 @@
}
})
},
createModules(){
createModules() {
this.$emit("createRootModelInTree");
},
getPath(id) {
@ -185,7 +185,7 @@
return path[0].path;
},
urlChange() {
if (!this.httpForm.path) return;
if (!this.httpForm.path || this.httpForm.path.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.httpForm.path));
if (url) {
this.httpForm.path = decodeURIComponent("/" + url.hostname + url.pathname);
@ -202,7 +202,6 @@
getURL(urlStr) {
try {
let url = new URL(urlStr);
console.log(urlStr)
url.searchParams.forEach((value, key) => {
if (key && value) {
this.request.arguments.splice(0, 0, new KeyValue({name: key, required: false, value: value}));

View File

@ -15,7 +15,7 @@ export default class JSR223PostProcessor extends PostProcessor {
constructor(options = DEFAULT_OPTIONS) {
super(options);
this.type = "JSR223PostProcessor";
this.scriptLanguage = "java";
this.scriptLanguage = "beanshell";
this.parameters = [];
this.filename = undefined;
this.cacheKey = true;

View File

@ -15,7 +15,7 @@ export default class JSR223PreProcessor extends PostProcessor {
constructor(options = DEFAULT_OPTIONS) {
super(options);
this.type = "JSR223PreProcessor";
this.scriptLanguage = "java";
this.scriptLanguage = "beanshell";
this.parameters = [];
this.filename = undefined;
this.cacheKey = undefined;

View File

@ -4,8 +4,7 @@
:is-api-list-enable="isApiListEnable"
@isApiListEnableChange="isApiListEnableChange">
<el-input placeholder="搜索" @blur="search" @keyup.enter.native="search" class="search-input" size="small"
v-model="condition.name"/>
<el-input placeholder="搜索" @blur="search" @keyup.enter.native="search" class="search-input" size="small" v-model="condition.name"/>
<el-table v-loading="result.loading"
ref="caseTable"
@ -23,10 +22,10 @@
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native.stop="isSelectDataAll(true)">
{{ $t('api_test.batch_menus.select_all_data', [total]) }}
{{$t('api_test.batch_menus.select_all_data',[total])}}
</el-dropdown-item>
<el-dropdown-item @click.native.stop="isSelectDataAll(false)">
{{ $t('api_test.batch_menus.select_show_data', [tableData.length]) }}
{{$t('api_test.batch_menus.select_show_data',[tableData.length])}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -73,10 +72,8 @@
<el-table-column v-if="!isReadOnly" :label="$t('commons.operating')" min-width="130" align="center">
<template v-slot:default="scope">
<!--<el-button type="text" @click="reductionApi(scope.row)" v-if="trashEnable">{{$t('commons.reduction')}}</el-button>-->
<el-button type="text" @click="handleTestCase(scope.row)" v-if="!trashEnable">{{ $t('commons.edit') }}
</el-button>
<el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C">{{ $t('commons.delete') }}
</el-button>
<el-button type="text" @click="handleTestCase(scope.row)" v-if="!trashEnable">{{$t('commons.edit')}}</el-button>
<el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C">{{$t('commons.delete')}}</el-button>
</template>
</el-table-column>
@ -94,153 +91,154 @@
<script>
import MsTableOperator from "../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import MsTablePagination from "../../../../common/pagination/TablePagination";
import MsTag from "../../../../common/components/MsTag";
import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY} from "../../model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "./ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import ApiCaseList from "../case/ApiCaseList";
import {_filter, _sort} from "../../../../../../common/js/utils";
import TestPlanCaseListHeader from "../../../../track/plan/view/comonents/api/TestPlanCaseListHeader";
import MsEnvironmentSelect from "../case/MsEnvironmentSelect";
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
import MsTableOperator from "../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import MsTablePagination from "../../../../common/pagination/TablePagination";
import MsTag from "../../../../common/components/MsTag";
import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "./ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import ApiCaseList from "../case/ApiCaseList";
import {_filter, _sort} from "../../../../../../common/js/utils";
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
export default {
name: "ApiCaseSimpleList",
components: {
ApiCaseList,
PriorityTableItem,
ApiListContainer,
MsTableOperatorButton,
MsTableOperator,
MsTablePagination,
MsTag,
MsApiCaseList,
MsContainer,
MsBottomContainer,
ShowMoreBtn,
MsBatchEdit
},
data() {
return {
condition: {},
selectCase: {},
result: {},
moduleId: "",
selectDataRange: "all",
deletePath: "/test/case/delete",
selectRows: new Set(),
buttons: [
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
{name: this.$t('api_test.definition.request.batch_edit'), handleClick: this.handleEditBatch}
],
typeArr: [
{id: 'priority', name: this.$t('test_track.case.priority')},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
valueArr: {
priority: CASE_PRIORITY,
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
screenHeight: document.documentElement.clientHeight - 330,//
environmentId: undefined,
selectAll: false,
unSelection: [],
selectDataCounts: 0,
}
},
props: {
currentProtocol: String,
selectNodeIds: Array,
visible: {
type: Boolean,
default: false,
export default {
name: "ApiCaseSimpleList",
components: {
ApiCaseList,
PriorityTableItem,
ApiListContainer,
MsTableOperatorButton,
MsTableOperator,
MsTablePagination,
MsTag,
MsApiCaseList,
MsContainer,
MsBottomContainer,
ShowMoreBtn,
MsBatchEdit
},
trashEnable: {
type: Boolean,
default: false,
},
isApiListEnable: {
type: Boolean,
default: false,
},
isReadOnly: {
type: Boolean,
default: false
},
isCaseRelevance: {
type: Boolean,
default: false,
},
relevanceProjectId: String,
model: {
type: String,
default() {
'api'
data() {
return {
condition: {},
selectCase: {},
result: {},
moduleId: "",
selectDataRange: "all",
deletePath: "/test/case/delete",
selectRows: new Set(),
buttons: [
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
{name: this.$t('api_test.definition.request.batch_edit'), handleClick: this.handleEditBatch}
],
typeArr: [
{id: 'priority', name: this.$t('test_track.case.priority')},
{id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'path', name: this.$t('api_test.request.path')},
],
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
valueArr: {
priority: CASE_PRIORITY,
method: REQ_METHOD,
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
screenHeight: document.documentElement.clientHeight - 330,//
environmentId: undefined,
selectAll: false,
unSelection: [],
selectDataCounts: 0,
}
},
planId: String
},
created: function () {
this.initTable();
},
watch: {
selectNodeIds() {
props: {
currentProtocol: String,
selectNodeIds: Array,
visible: {
type: Boolean,
default: false,
},
trashEnable: {
type: Boolean,
default: false,
},
isApiListEnable: {
type: Boolean,
default: false,
},
isReadOnly: {
type: Boolean,
default: false
},
isCaseRelevance: {
type: Boolean,
default: false,
},
relevanceProjectId: String,
model: {
type: String,
default() {
'api'
}
},
planId: String
},
created: function () {
this.initTable();
},
currentProtocol() {
this.initTable();
},
trashEnable() {
if (this.trashEnable) {
watch: {
selectNodeIds() {
this.initTable();
},
currentProtocol() {
this.initTable();
},
trashEnable() {
if (this.trashEnable) {
this.initTable();
}
},
relevanceProjectId() {
this.initTable();
}
},
relevanceProjectId() {
this.initTable();
}
},
computed: {
computed: {
//
isApiModel() {
return this.model === 'api'
//
isApiModel() {
return this.model === 'api'
},
},
},
methods: {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.selectRows = new Set();
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.status = "Trash";
this.condition.moduleIds = [];
}
this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.condition.projectId = getCurrentProjectID();
methods: {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.selectRows = new Set();
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.status = "Trash";
this.condition.moduleIds = [];
}
this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.condition.projectId = getCurrentProjectID();
if (this.currentProtocol != null) {
this.condition.protocol = this.currentProtocol;
@ -306,170 +304,169 @@ export default {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleTestCase(testCase) {
this.$get('/api/definition/get/' + testCase.apiDefinitionId, (response) => {
let api = response.data;
let selectApi = api;
let request = {};
if (Object.prototype.toString.call(api.request).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
request = api.request;
} else {
request = JSON.parse(api.request);
}
if (!request.hashTree) {
request.hashTree = [];
}
selectApi.url = request.path;
this.$refs.caseList.open(selectApi, testCase.id);
});
},
reductionApi(row) {
let ids = [row.id];
this.$post('/api/testcase/reduction/', ids, () => {
this.$success(this.$t('commons.save_success'));
this.search();
});
},
handleDeleteBatch() {
// if (this.trashEnable) {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let obj = {};
obj.projectId = getCurrentProjectID();
obj.selectAllDate = this.isSelectAllDate;
obj.unSelectIds = this.unSelection;
obj.ids = Array.from(this.selectRows).map(row => row.id);
obj = Object.assign(obj, this.condition);
this.$post('/api/testcase/deleteBatchByParam/', obj, () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
handleTestCase(testCase) {
this.$get('/api/definition/get/' + testCase.apiDefinitionId, (response) => {
let api = response.data;
let selectApi = api;
let request = {};
if (Object.prototype.toString.call(api.request).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
request = api.request;
} else {
request = JSON.parse(api.request);
}
if (!request.hashTree) {
request.hashTree = [];
}
selectApi.url = request.path;
this.$refs.caseList.open(selectApi, testCase.id);
});
},
reductionApi(row) {
let ids = [row.id];
this.$post('/api/testcase/reduction/', ids, () => {
this.$success(this.$t('commons.save_success'));
this.search();
});
},
handleDeleteBatch() {
// if (this.trashEnable) {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let obj = {};
obj.projectId = getCurrentProjectID();
obj.selectAllDate = this.isSelectAllDate;
obj.unSelectIds = this.unSelection;
obj.ids = Array.from(this.selectRows).map(row => row.id);
obj = Object.assign(obj, this.condition);
this.$post('/api/testcase/deleteBatchByParam/', obj, () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
}
}
});
// } else {
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = Array.from(this.selectRows).map(row => row.id);
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.selectRows.clear();
// this.initTable();
// this.$success(this.$t('commons.delete_success'));
// });
// }
// }
// });
// }
},
handleEditBatch() {
this.$refs.batchEdit.open();
},
batchEdit(form) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
param.projectId = getCurrentProjectID();
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
this.$post('/api/testcase/batch/editByParam', param, () => {
this.$success(this.$t('commons.save_success'));
this.initTable();
});
},
handleDelete(apiCase) {
// if (this.trashEnable) {
this.$get('/api/testcase/delete/' + apiCase.id, () => {
this.$success(this.$t('commons.delete_success'));
this.initTable();
});
return;
// }
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + apiCase.name + " ", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = [apiCase.id];
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.$success(this.$t('commons.delete_success'));
// this.initTable();
// });
// }
// }
// });
},
setEnvironment(data) {
this.environmentId = data.id;
},
selectRowsCount(selection) {
let selectedIDs = this.getIds(selection);
let allIDs = this.tableData.map(s => s.id);
this.unSelection = allIDs.filter(function (val) {
return selectedIDs.indexOf(val) === -1
});
if (this.isSelectAllDate) {
this.selectDataCounts = this.total - this.unSelection.length;
} else {
this.selectDataCounts = selection.size;
}
});
// } else {
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = Array.from(this.selectRows).map(row => row.id);
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.selectRows.clear();
// this.initTable();
// this.$success(this.$t('commons.delete_success'));
// });
// }
// }
// });
// }
},
handleEditBatch() {
this.$refs.batchEdit.open();
},
batchEdit(form) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
param.projectId = getCurrentProjectID();
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
this.$post('/api/testcase/batch/editByParam', param, () => {
this.$success(this.$t('commons.save_success'));
this.initTable();
});
},
handleDelete(apiCase) {
// if (this.trashEnable) {
this.$get('/api/testcase/delete/' + apiCase.id, () => {
this.$success(this.$t('commons.delete_success'));
this.initTable();
});
return;
// }
// this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + apiCase.name + " ", '', {
// confirmButtonText: this.$t('commons.confirm'),
// callback: (action) => {
// if (action === 'confirm') {
// let ids = [apiCase.id];
// this.$post('/api/testcase/removeToGc/', ids, () => {
// this.$success(this.$t('commons.delete_success'));
// this.initTable();
// });
// }
// }
// });
},
setEnvironment(data) {
this.environmentId = data.id;
},
selectRowsCount(selection) {
let selectedIDs = this.getIds(selection);
let allIDs = this.tableData.map(s => s.id);
this.unSelection = allIDs.filter(function (val) {
return selectedIDs.indexOf(val) === -1
});
if (this.isSelectAllDate) {
this.selectDataCounts = this.total - this.unSelection.length;
} else {
this.selectDataCounts = selection.size;
},
isSelectDataAll(dataType) {
this.isSelectAllDate = dataType;
this.selectRowsCount(this.selectRows)
//
if (this.selectRows.size != this.tableData.length) {
this.$refs.caseTable.toggleAllSelection(true);
}
},
//
isSelectThissWeekData() {
this.selectDataRange = "all";
let routeParam = this.$route.params.dataSelectRange;
let dataType = this.$route.params.dataType;
if (dataType === 'apiTestCase') {
this.selectDataRange = routeParam;
}
},
changeSelectDataRangeAll() {
this.$emit("changeSelectDataRangeAll", "testCase");
},
getIds(rowSets) {
let rowArray = Array.from(rowSets)
let ids = rowArray.map(s => s.id);
return ids;
}
},
isSelectDataAll(dataType) {
this.isSelectAllDate = dataType;
this.selectRowsCount(this.selectRows)
//
if (this.selectRows.size != this.tableData.length) {
this.$refs.caseTable.toggleAllSelection(true);
}
},
//
isSelectThissWeekData() {
this.selectDataRange = "all";
let routeParam = this.$route.params.dataSelectRange;
let dataType = this.$route.params.dataType;
if (dataType === 'apiTestCase') {
this.selectDataRange = routeParam;
}
},
changeSelectDataRangeAll() {
this.$emit("changeSelectDataRangeAll", "testCase");
},
getIds(rowSets) {
let rowArray = Array.from(rowSets)
let ids = rowArray.map(s => s.id);
return ids;
}
},
}
}
</script>
<style scoped>
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
.request-method {
padding: 0 5px;
color: #1E90FF;
}
.request-method {
padding: 0 5px;
color: #1E90FF;
}
.api-el-tag {
color: white;
}
.api-el-tag {
color: white;
}
.search-input {
float: right;
width: 300px;
/*margin-bottom: 20px;*/
margin-right: 20px;
}
.search-input {
float: right;
width: 300px;
/*margin-bottom: 20px;*/
margin-right: 20px;
}
</style>

View File

@ -10,25 +10,18 @@
<el-table v-loading="result.loading"
ref="apiDefinitionTable"
border
:data="tableData" row-key="id" class="test-content adjust-table api-list"
:data="tableData" row-key="id" class="test-content adjust-table ms-select-all"
@select-all="handleSelectAll"
@select="handleSelect" :height="screenHeight">
<el-table-column width="20" type="selection"/>
<el-table-column width="50" type="selection"/>
<ms-table-select-all
:page-size="pageSize"
:total="total"
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"/>
<el-table-column width="30" :resizable="false" align="center">
<el-dropdown slot="header">
<span class="el-dropdown-link">
<i class="el-icon-arrow-down"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native.stop="isSelectDataAll(true)">
{{ $t('api_test.batch_menus.select_all_data', [total]) }}
</el-dropdown-item>
<el-dropdown-item @click.native.stop="isSelectDataAll(false)">
{{ $t('api_test.batch_menus.select_show_data', [tableData.length]) }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
</template>
@ -37,43 +30,38 @@
<el-table-column prop="num" label="ID" show-overflow-tooltip/>
<el-table-column prop="name" :label="$t('api_test.definition.api_name')" show-overflow-tooltip/>
<el-table-column
prop="status"
column-key="api_status"
:label="$t('api_test.definition.api_status')"
show-overflow-tooltip>
prop="status"
column-key="api_status"
:label="$t('api_test.definition.api_status')"
show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.status == 'Prepare'" type="info" effect="plain"
:content="$t('test_track.plan.plan_status_prepare')"/>
<ms-tag v-if="scope.row.status == 'Underway'" type="warning" effect="plain"
:content="$t('test_track.plan.plan_status_running')"/>
<ms-tag v-if="scope.row.status == 'Completed'" type="success" effect="plain"
:content="$t('test_track.plan.plan_status_completed')"/>
<ms-tag v-if="scope.row.status == 'Prepare'" type="info" effect="plain" :content="$t('test_track.plan.plan_status_prepare')"/>
<ms-tag v-if="scope.row.status == 'Underway'" type="warning" effect="plain" :content="$t('test_track.plan.plan_status_running')"/>
<ms-tag v-if="scope.row.status == 'Completed'" type="success" effect="plain" :content="$t('test_track.plan.plan_status_completed')"/>
<ms-tag v-if="scope.row.status == 'Trash'" type="danger" effect="plain" content="废弃"/>
</template>
</el-table-column>
<el-table-column
prop="method"
:label="$t('api_test.definition.api_type')"
show-overflow-tooltip>
prop="method"
:label="$t('api_test.definition.api_type')"
show-overflow-tooltip>
<template v-slot:default="scope" class="request-method">
<el-tag size="mini"
:style="{'background-color': getColor(true, scope.row.method), border: getColor(true, scope.row.method)}"
class="api-el-tag">
{{ scope.row.method }}
<el-tag size="mini" :style="{'background-color': getColor(true, scope.row.method), border: getColor(true, scope.row.method)}" class="api-el-tag">
{{ scope.row.method}}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="path"
:label="$t('api_test.definition.api_path')"
show-overflow-tooltip/>
prop="path"
:label="$t('api_test.definition.api_path')"
show-overflow-tooltip/>
<el-table-column
prop="userName"
:label="$t('api_test.definition.api_principal')"
show-overflow-tooltip/>
prop="userName"
:label="$t('api_test.definition.api_principal')"
show-overflow-tooltip/>
<el-table-column width="160" :label="$t('api_test.definition.api_last_time')" prop="updateTime">
<template v-slot:default="scope">
@ -82,31 +70,27 @@
</el-table-column>
<el-table-column
prop="caseTotal"
:label="$t('api_test.definition.api_case_number')"
show-overflow-tooltip/>
prop="caseTotal"
:label="$t('api_test.definition.api_case_number')"
show-overflow-tooltip/>
<el-table-column
prop="caseStatus"
:label="$t('api_test.definition.api_case_status')"
show-overflow-tooltip/>
prop="caseStatus"
:label="$t('api_test.definition.api_case_status')"
show-overflow-tooltip/>
<el-table-column
prop="casePassingRate"
:label="$t('api_test.definition.api_case_passing_rate')"
show-overflow-tooltip/>
prop="casePassingRate"
:width="100"
:label="$t('api_test.definition.api_case_passing_rate')"
show-overflow-tooltip/>
<el-table-column v-if="!isReadOnly" :label="$t('commons.operating')" min-width="130" align="center">
<template v-slot:default="scope">
<el-button type="text" @click="reductionApi(scope.row)" v-if="trashEnable" v-tester>
{{ $t('commons.reduction') }}
</el-button>
<el-button type="text" @click="editApi(scope.row)" v-else v-tester>{{ $t('commons.edit') }}</el-button>
<el-button type="text" @click="handleTestCase(scope.row)">{{ $t('api_test.definition.request.case') }}
</el-button>
<el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C" v-tester>
{{ $t('commons.delete') }}
</el-button>
<el-button type="text" @click="reductionApi(scope.row)" v-if="trashEnable" v-tester>{{$t('commons.reduction')}}</el-button>
<el-button type="text" @click="editApi(scope.row)" v-else v-tester>{{$t('commons.edit')}}</el-button>
<el-button type="text" @click="handleTestCase(scope.row)">{{$t('api_test.definition.request.case')}}</el-button>
<el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C" v-tester>{{$t('commons.delete')}}</el-button>
</template>
</el-table-column>
</el-table>
@ -122,134 +106,133 @@
<script>
import MsTableHeader from '../../../../common/components/MsTableHeader';
import MsTableOperator from "../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import MsTableButton from "../../../../common/components/MsTableButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import MsTablePagination from "../../../../common/pagination/TablePagination";
import MsTag from "../../../../common/components/MsTag";
import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, REQ_METHOD, API_STATUS} from "../../model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import ApiListContainer from "./ApiListContainer";
import MsTableHeader from '../../../../common/components/MsTableHeader';
import MsTableOperator from "../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import MsTableButton from "../../../../common/components/MsTableButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import MsTablePagination from "../../../../common/pagination/TablePagination";
import MsTag from "../../../../common/components/MsTag";
import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, REQ_METHOD, API_STATUS} from "../../model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import ApiListContainer from "./ApiListContainer";
import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll";
export default {
name: "ApiList",
components: {
ApiListContainer,
MsTableButton,
MsTableOperatorButton,
MsTableOperator,
MsTableHeader,
MsTablePagination,
MsTag,
MsApiCaseList,
MsContainer,
MsBottomContainer,
ShowMoreBtn,
MsBatchEdit
},
data() {
return {
condition: {},
selectApi: {},
result: {},
moduleId: "",
selectDataRange: "all",
deletePath: "/test/case/delete",
selectRows: new Set(),
buttons: [
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
{name: this.$t('api_test.definition.request.batch_edit'), handleClick: this.handleEditBatch}
],
typeArr: [
{id: 'status', name: this.$t('api_test.definition.api_status')},
{id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'userId', name: this.$t('api_test.definition.api_principal')},
],
valueArr: {
status: API_STATUS,
method: REQ_METHOD,
userId: [],
export default {
name: "ApiList",
components: {
MsTableSelectAll,
ApiListContainer,
MsTableButton,
MsTableOperatorButton,
MsTableOperator,
MsTableHeader,
MsTablePagination,
MsTag,
MsApiCaseList,
MsContainer,
MsBottomContainer,
ShowMoreBtn,
MsBatchEdit
},
data() {
return {
condition: {},
selectApi: {},
result: {},
moduleId: "",
selectDataRange: "all",
deletePath: "/test/case/delete",
selectRows: new Set(),
buttons: [
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
{name: this.$t('api_test.definition.request.batch_edit'), handleClick: this.handleEditBatch}
],
typeArr: [
{id: 'status', name: this.$t('api_test.definition.api_status')},
{id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'userId', name: this.$t('api_test.definition.api_principal')},
],
valueArr: {
status: API_STATUS,
method: REQ_METHOD,
userId: [],
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
screenHeight: document.documentElement.clientHeight - 330,//,
environmentId: undefined,
selectAll: false,
unSelection:[],
selectDataCounts:0,
}
},
props: {
currentProtocol: String,
selectNodeIds: Array,
isSelectThisWeek: String,
visible: {
type: Boolean,
default: false,
},
isCaseRelevance: {
type: Boolean,
default: false,
},
trashEnable: {
type: Boolean,
default: false,
},
isApiListEnable: Boolean,
isReadOnly: {
type: Boolean,
default: false
},
methodColorMap: new Map(API_METHOD_COLOUR),
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
screenHeight: document.documentElement.clientHeight - 330,//,
environmentId: undefined,
selectAll: false,
unSelection: [],
selectDataCounts: 0,
}
},
props: {
currentProtocol: String,
selectNodeIds: Array,
isSelectThisWeek: String,
visible: {
type: Boolean,
default: false,
},
isCaseRelevance: {
type: Boolean,
default: false,
},
trashEnable: {
type: Boolean,
default: false,
},
isApiListEnable: Boolean,
isReadOnly: {
type: Boolean,
default: false
},
},
mounted: function () {
this.initTable();
},
created: function () {
this.initTable();
this.getMaintainerOptions();
},
watch: {
selectNodeIds() {
created: function () {
this.initTable();
this.getMaintainerOptions();
},
currentProtocol() {
this.initTable();
},
trashEnable() {
if (this.trashEnable) {
watch: {
selectNodeIds() {
this.initTable();
},
currentProtocol() {
this.initTable();
},
trashEnable() {
if (this.trashEnable) {
this.initTable();
}
}
}
},
methods: {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.selectRows = new Set();
methods: {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.selectRows = new Set();
this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.condition.filters = ["Prepare", "Underway", "Completed"];
this.condition.filters = ["Prepare", "Underway", "Completed"];
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.filters = ["Trash"];
this.condition.moduleIds = [];
}
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.filters = ["Trash"];
this.condition.moduleIds = [];
}
this.condition.projectId = getCurrentProjectID();
if (this.currentProtocol != null) {
@ -342,76 +325,76 @@ export default {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
editApi(row) {
this.$emit('editApi', row);
},
reductionApi(row) {
row.request = null;
row.response = null;
let rows = [row];
this.$post('/api/definition/reduction/', rows, () => {
this.$success(this.$t('commons.save_success'));
this.search();
});
},
handleDeleteBatch() {
if (this.trashEnable) {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let deleteParam = {};
let ids = Array.from(this.selectRows).map(row => row.id);
deleteParam.dataIds = ids;
deleteParam.projectId = getCurrentProjectID();
deleteParam.selectAllDate = this.isSelectAllDate;
deleteParam.unSelectIds = this.unSelection;
deleteParam = Object.assign(deleteParam, this.condition);
this.$post('/api/definition/deleteBatchByParams/', deleteParam, () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
}
}
editApi(row) {
this.$emit('editApi', row);
},
reductionApi(row) {
row.request = null;
row.response = null;
let rows = [row];
this.$post('/api/definition/reduction/', rows, () => {
this.$success(this.$t('commons.save_success'));
this.search();
});
} else {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
let deleteParam = {};
deleteParam.dataIds = ids;
deleteParam.projectId = getCurrentProjectID();
deleteParam.selectAllDate = this.isSelectAllDate;
deleteParam.unSelectIds = this.unSelection;
deleteParam = Object.assign(deleteParam, this.condition);
this.$post('/api/definition/removeToGcByParams/', deleteParam, () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
this.$refs.caseList.apiCaseClose();
});
},
handleDeleteBatch() {
if (this.trashEnable) {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let deleteParam = {};
let ids = Array.from(this.selectRows).map(row => row.id);
deleteParam.dataIds = ids;
deleteParam.projectId = getCurrentProjectID();
deleteParam.selectAllDate = this.isSelectAllDate;
deleteParam.unSelectIds = this.unSelection;
deleteParam = Object.assign(deleteParam, this.condition);
this.$post('/api/definition/deleteBatchByParams/', deleteParam, () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
});
}
}
}
});
}
},
handleEditBatch() {
this.$refs.batchEdit.open();
},
batchEdit(form) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
});
} else {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
let deleteParam = {};
deleteParam.dataIds = ids;
deleteParam.projectId = getCurrentProjectID();
deleteParam.selectAllDate = this.isSelectAllDate;
deleteParam.unSelectIds = this.unSelection;
deleteParam = Object.assign(deleteParam, this.condition);
this.$post('/api/definition/removeToGcByParams/', deleteParam, () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
this.$refs.caseList.apiCaseClose();
});
}
}
});
}
},
handleEditBatch() {
this.$refs.batchEdit.open();
},
batchEdit(form) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
let param = {};
param[form.type] = form.value;
param.ids = ids;
param.projectId = getCurrentProjectID();
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
param.projectId = getCurrentProjectID();
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
this.$post('/api/definition/batch/editByParams', param, () => {
this.$success(this.$t('commons.save_success'));
@ -505,54 +488,52 @@ export default {
</script>
<style scoped>
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
.operate-button > div {
display: inline-block;
margin-left: 10px;
}
.request-method {
padding: 0 5px;
color: #1E90FF;
}
.request-method {
padding: 0 5px;
color: #1E90FF;
}
.api-el-tag {
color: white;
}
.api-el-tag {
color: white;
}
.search-input {
float: right;
width: 300px;
/*margin-bottom: 20px;*/
margin-right: 20px;
}
.search-input {
float: right;
width: 300px;
margin-right: 20px;
}
.api-list >>> th:first-child {
/*border: 1px solid #DCDFE6;*/
/*border-right: 0px;*/
/*border-top-left-radius:5px;*/
/*border-bottom-left-radius:5px;*/
/*width: 20px;*/
.api-list >>> th:first-child {
/*border: 1px solid #DCDFE6;*/
/*border-right: 0px;*/
/*border-top-left-radius:5px;*/
/*border-bottom-left-radius:5px;*/
/*width: 20px;*/
}
}
.api-list >>> th:nth-child(2) {
/*border: 1px solid #DCDFE6;*/
/*border-left: 0px;*/
/*border-top-right-radius:5px;*/
/*border-bottom-right-radius:5px;*/
}
.api-list >>> th:nth-child(2) {
/*border: 1px solid #DCDFE6;*/
/*border-left: 0px;*/
/*border-top-right-radius:5px;*/
/*border-bottom-right-radius:5px;*/
}
.api-list >>> th:first-child > .cell {
padding: 5px;
width: 30px;
}
.api-list >>> th:first-child>.cell {
padding: 5px;
width: 30px;
}
.api-list >>> th:nth-child(2)>.cell {
/*background-color: black;*/
}
.api-list >>> th:nth-child(2) > .cell {
/*background-color: black;*/
}
.api-list >>> .el-dropdown {
float: left;
}
.api-list >>> .el-dropdown {
float: left;
}
</style>

View File

@ -122,7 +122,7 @@ export default {
<style scoped>
.protocol-select {
width: 95px;
width: 92px;
height: 30px;
}

View File

@ -34,7 +34,7 @@
ref="codeEdit"/>
</el-col>
<el-col :span="4" class="script-index">
<ms-dropdown :default-command="jsr223ProcessorData.language" :commands="languages" @command="languageChange"/>
<ms-dropdown :default-command="jsr223ProcessorData.scriptLanguage" :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>
@ -140,7 +140,7 @@
this.jsr223ProcessorData.script = "";
}
this.jsr223ProcessorData.script += template.value;
if (this.jsr223ProcessorData.language === 'beanshell') {
if (this.jsr223ProcessorData.scriptLanguage === 'beanshell') {
this.jsr223ProcessorData.script += ';';
}
this.reload();
@ -156,7 +156,7 @@
this.$nextTick(() => (this.isCodeEditAlive = true));
},
languageChange(language) {
this.jsr223ProcessorData.language = language;
this.jsr223ProcessorData.scriptLanguage = language;
},
changeActive() {
this.active = !this.active;

View File

@ -842,8 +842,7 @@ export class JSR223Processor extends BaseConfig {
this.active = false;
this.type = "JSR223Processor";
this.script = undefined;
this.language = "beanshell";
this.scriptLanguage = "java";
this.scriptLanguage = "beanshell";
this.enable = true;
this.hashTree = [];
this.set(options);

View File

@ -97,7 +97,6 @@ export default {
methods: {
registerEvents() {
ApiEvent.$on(LIST_CHANGE, () => {
// // todo refs
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
@ -111,6 +110,9 @@ export default {
},
mounted() {
this.registerEvents();
},
beforeDestroy() {
ApiEvent.$off(LIST_CHANGE);
}
}

View File

@ -85,7 +85,7 @@ export default {
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
loading: false,
statusFilters: [

View File

@ -45,7 +45,7 @@ export default {
tableData: [],
loading: false,
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
}
},

View File

@ -90,7 +90,7 @@
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
loading: false,
selectIds: new Set(),

View File

@ -63,7 +63,7 @@ export default {
},
tableData: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
selection: false,
environmentMap: new Map()

View File

@ -118,7 +118,21 @@ export default {
},
copyRequest(index) {
let request = this.scenario.requests[index];
this.scenario.requests.push(new RequestFactory(request));
let item = new RequestFactory(request);
if (item.body && item.body.kvs) {
item.body.kvs.forEach(kv => {
let files = [];
if (kv.files) {
kv.files.forEach(file => {
let fileCopy = {};
Object.assign(fileCopy, file);
files.push(fileCopy);
})
}
kv.files = files;
});
}
this.scenario.requests.push(item);
},
disableRequest(index) {
this.scenario.requests[index].enable = false;

View File

@ -0,0 +1,28 @@
<template>
<el-table-column width="1" :resizable="false" align="center">
<el-dropdown slot="header">
<span class="el-dropdown-link">
<i class="el-icon-arrow-down"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native.stop="$emit('selectAll')">
{{$t('api_test.batch_menus.select_all_data',[total])}}
</el-dropdown-item>
<el-dropdown-item @click.native.stop="$emit('selectPageAll')">
{{$t('api_test.batch_menus.select_show_data',[pageSize])}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-table-column>
</template>
<script>
export default {
name: "MsTableSelectAll",
props: ['total', 'pageSize']
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,82 @@
<template>
<div id="app" v-loading="loading">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="$t('organization.message.template')" name="apiTemplate">
<el-button type="primary" size="mini" style="margin-left: 10px" @click="openOneClickOperation">导入</el-button>
<div style="min-height: 400px">
<json-schema-editor class="schema" :value="schema" lang="zh_CN" custom/>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('schema.preview')" name="preview">
<div style="min-height: 400px">
<pre>{{this.preview}}</pre>
</div>
</el-tab-pane>
</el-tabs>
<ms-import-json ref="importJson" @jsonData="jsonData"/>
</div>
</template>
<script>
import {schemaToJson} from './common';
import MsImportJson from './import/ImportJson';
const GenerateSchema = require('generate-schema/src/schemas/json.js');
export default {
name: 'App',
components: {MsImportJson},
props: {
body: {},
},
created() {
if (!this.body.jsonSchema && this.body.raw) {
let obj = {"root": GenerateSchema(JSON.parse(this.body.raw))}
this.schema = obj;
}
else if (this.body.jsonSchema) {
this.schema = {"root": this.body.jsonSchema};
}
this.body.jsonSchema = this.schema.root;
},
data() {
return {
schema:
{
"root": {
"type": "object",
"properties": {},
}
},
loading: false,
preview: null,
activeName: "apiTemplate",
}
},
methods: {
handleClick() {
if (this.activeName === 'preview') {
//
//this.preview = schemaToJson(this.schema.root);
this.loading = true;
//
this.$post('/api/definition/preview', this.schema.root, response => {
this.preview = response.data;
this.loading = false;
});
}
},
openOneClickOperation() {
this.$refs.importJson.openOneClickOperation();
},
jsonData(data) {
let obj = {"root": data}
this.schema = obj;
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,32 @@
const Mock = require('mockjs');
const jsf = require('json-schema-faker');
jsf.extend('mock', function () {
return {
mock: function (xx) {
if (xx && xx.startsWith("@")) {
return Mock.mock(xx);
}
return xx;
}
};
});
const defaultOptions = {
failOnInvalidTypes: false,
failOnInvalidFormat: false
};
export const schemaToJson = (schema, options = {}) => {
Object.assign(options, defaultOptions);
jsf.option(options);
let result;
try {
result = jsf.generate(schema);
} catch (err) {
result = err.message;
}
jsf.option(defaultOptions);
return result;
};

View File

@ -0,0 +1,117 @@
<template>
<el-dialog
title="导入"
:visible.sync="importVisible"
width="50%"
show-close
:close-on-click-modal="false"
@closed="handleClose">
<el-tabs v-model="activeName">
<el-tab-pane label="JSON" name="JSON">
<div style="height: 400px">
<ms-code-edit :mode="mode"
:data.sync="json" theme="eclipse" :modes="[]"
ref="codeEdit"/>
</div>
</el-tab-pane>
<el-tab-pane label="JSON-SCHEMA" name="JSON-SCHEMA">
<div style="height: 400px">
<ms-code-edit :mode="mode"
:data.sync="jsonSchema" theme="eclipse" :modes="[]"
ref="codeEdit"/>
</div>
</el-tab-pane>
</el-tabs>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="importVisible = false"
@confirm="saveConfirm"/>
</span>
</el-dialog>
</template>
<script>
import MsDialogFooter from '../../../common/components/MsDialogFooter'
import MsCodeEdit from "../../../common/components/MsCodeEdit";
import json5 from 'json5';
const GenerateSchema = require('generate-schema/src/schemas/json.js');
export default {
name: "MsImportJson",
components: {MsDialogFooter, MsCodeEdit},
data() {
return {
importVisible: false,
activeName: "JSON",
mode: "json",
json: "",
jsonSchema: "",
};
},
watch: {},
props: {},
methods: {
openOneClickOperation() {
this.importVisible = true;
},
checkIsJson(json) {
try {
json5.parse(json);
return true;
} catch (e) {
alert(1);
return false;
}
},
checkIsJsonSchema(json) {
try {
json = json5.parse(json);
if (json.properties && typeof json.properties === 'object' && !json.type) {
json.type = 'object';
}
if (json.items && typeof json.items === 'object' && !json.type) {
json.type = 'array';
}
if (!json.type) {
return false;
}
json.type = json.type.toLowerCase();
let types = ['object', 'string', 'number', 'array', 'boolean', 'integer'];
if (types.indexOf(json.type) === -1) {
return false;
}
return JSON.stringify(json);
} catch (e) {
return false;
}
},
saveConfirm() {
if (this.activeName === 'JSON') {
if (!this.checkIsJson(this.json)) {
this.$error("导入的数据非JSON格式");
return;
}
let jsonData = GenerateSchema(json5.parse(this.json));
this.$emit('jsonData', jsonData);
} else {
if (!this.checkIsJsonSchema(this.jsonSchema)) {
this.$error("导入的数据非JSON-SCHEMA 格式");
return;
}
let obj = json5.parse(this.jsonSchema);
this.$emit('jsonData', obj);
}
this.importVisible = false;
},
handleClose() {
this.importVisible = false;
},
}
}
</script>
<style scoped>
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
import JsonSchemaEditor from './main.vue'
JsonSchemaEditor.install = function (Vue) {
Vue.component(JsonSchemaEditor.name, JsonSchemaEditor)
}
export default JsonSchemaEditor

View File

@ -0,0 +1,398 @@
<template>
<div class="json-schema-editor">
<el-row class="row" :gutter="10">
<el-col :span="12" class="ant-col-name">
<div :style="{marginLeft:`${10*deep}px`}" class="ant-col-name-c">
<span v-if="pickValue.type==='object'" :class="hidden? 'el-tree-node__expand-icon el-icon-caret-right':
'expanded el-tree-node__expand-icon el-icon-caret-right'" @click="hidden = !hidden"/>
<span v-else style="width:10px;display:inline-block"></span>
<input class="el-input el-input__inner" style="height: 32px" :disabled="disabled || root" :value="pickKey" @blur="onInputName" size="small"/>
</div>
<el-tooltip v-if="root" :content="$t('schema.checked_all')" placement="top">
<input type="checkbox" :disabled="!isObject && !isArray" class="ant-col-name-required" @change="onRootCheck"/>
</el-tooltip>
<el-tooltip v-else :content="$t('schema.required')" placement="top">
<input type="checkbox" :disabled="isItem" :checked="checked" class="ant-col-name-required" @change="onCheck"/>
</el-tooltip>
</el-col>
<el-col :span="4">
<el-select v-model="pickValue.type" :disabled="disabledType" class="ant-col-type" @change="onChangeType" size="small">
<el-option :key="t" :value="t" :label="t" v-for="t in TYPE_NAME"/>
</el-select>
</el-col>
<el-col :span="8">
<ms-mock :disabled="pickValue.type==='object'" :schema="pickValue"/>
</el-col>
<el-col>
<el-input v-model="pickValue.description" class="ant-col-title" :placeholder="$t('schema.description')" size="small"/>
</el-col>
<el-col :span="5" class="col-item-setting">
<el-tooltip class="item" effect="dark" :content="$t('schema.adv_setting')" placement="top">
<i class="el-icon-setting" @click="onSetting"/>
</el-tooltip>
<el-tooltip v-if="isObject" :content="$t('schema.add_child_node')" placement="top">
<i class="el-icon-plus" @click="addChild" style="margin-left: 10px"/>
</el-tooltip>
<el-tooltip v-if="!root && !isItem" :content="$t('schema.remove_node')" placement="top">
<i class="el-icon-close" @click="removeNode" style="margin-left: 10px"/>
</el-tooltip>
</el-col>
</el-row>
<template v-if="!hidden&&pickValue.properties && !isArray">
<json-schema-editor v-for="(item,key,index) in pickValue.properties" :value="{[key]:item}" :parent="pickValue" :key="index" :deep="deep+1" :root="false" class="children" :lang="lang" :custom="custom"/>
</template>
<template v-if="isArray">
<json-schema-editor :value="{items:pickValue.items}" :deep="deep+1" disabled isItem :root="false" class="children" :lang="lang" :custom="custom"/>
</template>
<!-- 高级设置-->
<el-dialog :close-on-click-modal="false" :title="$t('schema.adv_setting')" :visible.sync="modalVisible" :destroy-on-close="true"
@close="handleClose">
<p class="tip">基础设置 </p>
<el-form :inline="true" v-model="advancedValue" class="ant-advanced-search-form">
<el-row :gutter="6">
<el-col :span="8" v-for="(item,key) in advancedValue" :key="key" style="float: right">
<el-form-item>
<div>{{ $t('schema.'+key)}}</div>
<el-input-number v-model="advancedValue[key]" v-if="advancedAttr[key].type === 'integer'" style="width:100%" :placeholder="key" size="small"/>
<el-input-number v-model="advancedValue[key]" v-else-if="advancedAttr[key].type === 'number'" style="width:100%" :placeholder="key" size="small"/>
<span v-else-if="advancedAttr[key].type === 'boolean'" style="display:inline-block;width:100%">
<el-switch v-model="advancedValue[key]"/>
</span>
<el-select v-else-if="advancedAttr[key].type === 'array'" v-model="advancedValue[key]" style="width:100%" size="small">
<el-option value="" :label="$t('schema.nothing')"></el-option>
<el-option :key="t" :value="t" :label="t" v-for="t in advancedAttr[key].enums"/>
</el-select>
<el-input v-model="advancedValue[key]" v-else style="width:100%;" :placeholder="key" size="small"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!--<h3 v-text="$t('schema.add_custom')" v-show="custom">添加自定义属性</h3>
<el-form class="ant-advanced-search-form" v-show="custom">
<el-row :gutter="6">
<el-col :span="8" v-for="item in customProps" :key="item.key">
<el-form-item :label="item.key">
<el-input v-model="item.value" style="width:calc(100% - 30px)" size="small"/>
<el-button icon="close" type="link" @click="customProps.splice(customProps.indexOf(item),1)" size="small"/>
</el-form-item>
</el-col>
<el-col :span="8" v-show="addProp.key != undefined">
<el-form-item>
<el-input slot="label" v-model="addProp.key" size="small"/>
<el-input v-model="addProp.value" size="small"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button icon="check" type="link" @click="confirmAddCustomNode" v-if="customing"/>
<el-tooltip content="$t('schema.add_custom')" v-else>
<el-button icon="el-icon-plus" type="link" @click="addCustomNode"/>
</el-tooltip>
</el-form-item>
</el-col>
</el-row>
</el-form>-->
<p class="tip">{{$t('schema.preview')}} </p>
<pre style="width:100%">{{completeNodeValue}}</pre>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="modalVisible = false"
@confirm="handleOk"/>
</span>
</el-dialog>
</div>
</template>
<script>
import {isNull} from './util'
import {TYPE_NAME, TYPE} from './type/type'
import MsMock from './mock/MockComplete'
import MsDialogFooter from '../../../components/MsDialogFooter'
import {getUUID} from "@/common/js/utils";
export default {
name: 'JsonSchemaEditor',
components: {MsMock, MsDialogFooter},
props: {
value: {
type: Object,
required: true
},
disabled: { //namename,name
type: Boolean,
default: false
},
disabledType: { //
type: Boolean,
default: false
},
isItem: { //
type: Boolean,
default: false
},
deep: { // deep=0
type: Number,
default: 0
},
root: { //root
type: Boolean,
default: true
},
parent: { //
type: Object,
default: null
},
custom: { //enable custom properties
type: Boolean,
default: false
},
lang: { // i18n language
type: String,
default: 'zh_CN'
}
},
computed: {
pickValue() {
return Object.values(this.value)[0]
},
pickKey() {
return Object.keys(this.value)[0]
},
isObject() {
return this.pickValue.type === 'object'
},
isArray() {
return this.pickValue.type === 'array'
},
checked() {
return this.parent && this.parent.required && this.parent.required.indexOf(this.pickKey) >= 0
},
advanced() {
return TYPE[this.pickValue.type]
},
advancedAttr() {
return TYPE[this.pickValue.type].attr
},
advancedNotEmptyValue() {
const jsonNode = Object.assign({}, this.advancedValue);
for (let key in jsonNode) {
isNull(jsonNode[key]) && delete jsonNode[key]
}
return jsonNode
},
completeNodeValue() {
const t = {}
for (const item of this.customProps) {
t[item.key] = item.value
}
return Object.assign({}, this.pickValue, this.advancedNotEmptyValue, t)
}
},
data() {
return {
TYPE_NAME,
hidden: false,
countAdd: 1,
modalVisible: false,
advancedValue: {},
addProp: {},//
customProps: [],
customing: false
}
},
methods: {
onInputName(e) {
const val = e.target.value
const p = {};
for (let key in this.parent.properties) {
if (key != this.pickKey) {
p[key] = this.parent.properties[key]
} else {
p[val] = this.parent.properties[key]
delete this.parent.properties[key]
}
}
this.$set(this.parent, 'properties', p)
},
onChangeType() {
this.$delete(this.pickValue, 'properties')
this.$delete(this.pickValue, 'items')
this.$delete(this.pickValue, 'required')
this.$delete(this.pickValue, 'mock')
if (this.isArray) {
this.$set(this.pickValue, 'items', {type: 'string', mock: {mock: ""}})
}
},
onCheck(e) {
this._checked(e.target.checked, this.parent)
},
onRootCheck(e) {
const checked = e.target.checked
this._deepCheck(checked, this.pickValue)
},
_deepCheck(checked, node) {
if (node.type === 'object' && node.properties) {
checked ? this.$set(node, 'required', Object.keys(node.properties)) : this.$delete(node, 'required')
Object.keys(node.properties).forEach(key => this._deepCheck(checked, node.properties[key]))
} else if (node.type === 'array' && node.items.type === 'object') {
checked ? this.$set(node.items, 'required', Object.keys(node.items.properties)) : this.$delete(node.items, 'required')
Object.keys(node.items.properties).forEach(key => this._deepCheck(checked, node.items.properties[key]))
}
},
_checked(checked, parent) {
let required = parent.required
if (checked) {
required || this.$set(this.parent, 'required', [])
required = this.parent.required
required.indexOf(this.pickKey) === -1 && required.push(this.pickKey)
} else {
const pos = required.indexOf(this.pickKey)
pos >= 0 && required.splice(pos, 1)
}
required.length === 0 && this.$delete(parent, 'required')
},
addChild() {
const name = this._joinName()
const type = 'string'
const node = this.pickValue
node.properties || this.$set(node, 'properties', {})
const props = node.properties
this.$set(props, name, {type: type, mock: {mock: ""}})
},
addCustomNode() {
this.$set(this.addProp, 'key', this._joinName())
this.$set(this.addProp, 'value', '')
this.customing = true
},
confirmAddCustomNode() {
this.customProps.push(this.addProp)
this.addProp = {}
this.customing = false
},
removeNode() {
const {properties, required} = this.parent
this.$delete(properties, this.pickKey)
if (required) {
const pos = required.indexOf(this.pickKey)
pos >= 0 && required.splice(pos, 1)
required.length === 0 && this.$delete(this.parent, 'required')
}
},
_joinName() {
return `feild_${this.deep}_${this.countAdd++}_${getUUID().substring(0, 5)}`
},
onSetting() {
this.modalVisible = true;
this.advancedValue = this.advanced.value
for (const k in this.advancedValue) {
if (this.pickValue[k]) this.advancedValue[k] = this.pickValue[k]
}
console.log(this.pickValue)
},
handleClose() {
this.modalVisible = false;
},
handleOk() {
this.modalVisible = false
for (const key in this.advancedValue) {
if (isNull(this.advancedValue[key])) {
this.$delete(this.pickValue, key)
} else {
this.$set(this.pickValue, key, this.advancedValue[key])
}
}
for (const item of this.customProps) {
this.$set(this.pickValue, item.key, item.value)
}
}
}
}
</script>
<style scoped>
.json-schema-editor .row {
display: flex;
margin: 12px;
}
.json-schema-editor .row .ant-col-name {
display: flex;
align-items: center;
}
.json-schema-editor .row .ant-col-name .ant-col-name-c {
display: flex;
align-items: center;
}
.json-schema-editor .row .ant-col-name .ant-col-name-required {
flex: 0 0 24px;
text-align: center;
}
.json-schema-editor .row .ant-col-type {
min-width: 100px;
width: 100%;
}
.json-schema-editor .row .ant-col-setting {
display: inline-block;
}
.json-schema-editor .row .setting-icon {
color: rgba(0, 0, 0, 0.45);
border: none;
}
.json-schema-editor .row .plus-icon {
border: none;
}
.json-schema-editor .row .close-icon {
color: #888;
border: none;
}
.json-schema-editor-advanced-modal {
color: rgba(0, 0, 0, 0.65);
min-width: 600px;
}
.json-schema-editor-advanced-modal pre {
font-family: monospace;
height: 100%;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: 12px;
width: 50%;
}
.json-schema-editor-advanced-modal h3 {
display: block;
border-left: 3px solid #1890ff;
padding: 0 8px;
}
.json-schema-editor-advanced-modal .ant-advanced-search-form {
display: flex;
}
.json-schema-editor-advanced-modal .ant-advanced-search-form .ant-form-item .ant-form-item-control-wrapper {
flex: 1;
}
.col-item-setting {
padding-top: 8px;
cursor: pointer;
}
.tip {
padding: 3px 5px;
font-size: 16px;
border-radius: 4px;
border-left: 4px solid #783887;
margin: 0px 0px 10px;
}
</style>

View File

@ -0,0 +1,236 @@
<template>
<el-dialog :title="$t('api_test.request.parameters_advance')"
:visible.sync="itemValueVisible"
class="advanced-item-value"
width="70%">
<el-tabs tab-position="top" style="height: 50vh;" @tab-click="selectTab">
<el-tab-pane :label="$t('api_test.request.parameters_advance_mock')">
<el-row type="flex" :gutter="20">
<el-col :span="6" class="col-height">
<div>
<el-input size="small" v-model="filterText"
:placeholder="$t('api_test.request.parameters_mock_filter_tips')"/>
<el-tree class="filter-tree" ref="tree" :data="mockFuncs" :props="treeProps"
default-expand-all @node-click="selectVariable"
:filter-node-method="filterNode"></el-tree>
</div>
</el-col>
<el-col :span="6" v-for="(itemFunc, itemIndex) in mockVariableFuncs" :key="itemIndex">
<div v-for="(func, funcIndex) in funcs"
:key="`${itemIndex}-${funcIndex}`">
<el-row>
<el-col :span="12">
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
@change="methodChange(itemFunc, func)"/>
</el-col>
<el-col :span="12" v-if="itemFunc.name === func.name">
<div v-for="(p, pIndex) in itemFunc.params" :key="`${itemIndex}-${funcIndex}-${pIndex}`">
<el-input :placeholder="p.name" size="mini" v-model="p.value" @change="showPreview"/>
</div>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.variable')">
<el-row>
<el-col :span="6" class="col-height">
<div v-if="preRequestParams">
<p>{{ $t('api_test.request.parameters_pre_request') }}</p>
<el-tree :data="preRequestParams" :props="treeProps" @node-click="selectVariable"></el-tree>
</div>
</el-col>
<el-col :span="18" class="col-height">
<div>
<h1>{{ $t('api_test.request.jmeter_func') }}</h1>
<el-table border :data="jmeterFuncs" class="adjust-table table-content" height="400">
<el-table-column prop="type" label="Type" width="150"/>
<el-table-column prop="name" label="Functions" width="250"/>
<el-table-column prop="description" label="Description"/>
</el-table>
</div>
</el-col>
</el-row>
</el-tab-pane>
</el-tabs>
<el-form>
<el-form-item>
<el-input :placeholder="valueText" size="small"
v-model="itemValue"/>
</el-form-item>
</el-form>
<div style="padding-top: 10px;">
<el-row type="flex" align="middle">
<el-col :span="12">
<el-button size="small" type="primary" plain @click="saveAdvanced()">
{{ $t('commons.save') }}
</el-button>
<el-button size="small" type="info" plain @click="addFunc()" v-if="currentTab === 0">
{{ $t('api_test.request.parameters_advance_add_func') }}
</el-button>
<el-button size="small" type="success" plain @click="showPreview()" v-if="currentTab === 0">
{{ $t('api_test.request.parameters_preview') }}
</el-button>
</el-col>
<el-col>
<div> {{ itemValuePreview }}</div>
</el-col>
</el-row>
</div>
</el-dialog>
</template>
<script>
import {calculate} from "./calculate";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
export default {
name: "MsApiVariableAdvance",
props: {
parameters: Array,
currentItem: Object,
},
data() {
return {
itemValueVisible: false,
filterText: '',
environmentParams: [],
scenarioParams: [],
Scenario: {},
preRequests: [],
preRequestParams: [],
treeProps: {children: 'children', label: 'name'},
currentTab: 0,
itemValue: null,
itemValuePreview: null,
funcs: [
{name: "md5"},
{name: "base64"},
{name: "unbase64"},
{
name: "substr",
params: [{name: "start"}, {name: "length"}]
},
{
name: "concat",
params: [{name: "suffix"}]
},
{name: "lconcat", params: [{name: "prefix"}]},
{name: "sha1"},
{name: "sha224"},
{name: "sha256"},
{name: "sha384"},
{name: "sha512"},
{name: "lower"},
{name: "upper"},
{name: "length"},
{name: "number"}
],
mockFuncs: MOCKJS_FUNC.map(f => {
return {name: f.name, value: f.name}
}),
jmeterFuncs: JMETER_FUNC,
mockVariableFuncs: [],
jmeterVariableFuncs: [],
}
},
computed: {
valueText() {
return this.valuePlaceholder || this.$t("api_test.value");
}
},
mounted() {
this.prepareData();
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
methods: {
open() {
this.itemValueVisible = true;
},
prepareData() {
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
selectVariable(node) {
this.itemValue = node.value;
},
selectTab(tab) {
this.currentTab = +tab.index;
this.itemValue = null;
this.itemValuePreview = null;
},
showPreview() {
//
if (!this.itemValue) {
return;
}
let index = this.itemValue.indexOf("|");
if (index > -1) {
this.itemValue = this.itemValue.substring(0, index).trim();
}
this.mockVariableFuncs.forEach(f => {
if (!f.name) {
return;
}
this.itemValue += "|" + f.name;
if (f.params) {
this.itemValue += ":" + f.params.map(p => p.value).join(",");
}
});
this.itemValuePreview = calculate(this.itemValue);
},
methodChange(itemFunc, func) {
let index = this.mockVariableFuncs.indexOf(itemFunc);
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
// deep copy
this.mockVariableFuncs.push(JSON.parse(JSON.stringify(func)));
this.showPreview();
},
addFunc() {
if (this.mockVariableFuncs.length > 4) {
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
return;
}
if (this.mockVariableFuncs.length > 0) {
let func = this.mockVariableFuncs[this.mockVariableFuncs.length - 1];
if (!func.name) {
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
return;
}
if (func.params) {
for (let j = 0; j < func.params.length; j++) {
if (!func.params[j].value) {
this.$warning(this.$t('api_test.request.parameters_advance_add_param_error'));
return;
}
}
}
}
this.mockVariableFuncs.push({name: '', params: []});
},
saveAdvanced() {
this.currentItem.mock = this.itemValue;
this.itemValueVisible = false;
this.mockVariableFuncs = [];
this.$emit('advancedRefresh', this.itemValue);
}
}
}
</script>
<style scoped>
.col-height {
height: 40vh;
overflow: auto;
}
</style>

View File

@ -0,0 +1,95 @@
<template>
<div>
<el-autocomplete
size="small"
class="input-with-autocomplete"
v-model="mock.mock"
:fetch-suggestions="funcSearch"
:disabled="disabled"
:placeholder="$t('api_test.value')"
value-key="name"
highlight-first-item
@select="change">
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced()"></i>
</el-autocomplete>
<ms-advance ref="variableAdvance" :current-item="mock"/>
</div>
</template>
<script>
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
import MsAdvance from "./Advance";
export default {
name: 'MsMock',
components: {MsAdvance},
props: {
schema: {
type: Object,
default: () => {
}
},
disabled: Boolean,
},
data() {
return {
mock: {mock: ""}
}
},
created() {
if (this.schema.mock && Object.prototype.toString.call(this.schema.mock).match(/\[object (\w+)\]/)[1].toLowerCase() === 'object') {
this.mock = this.schema.mock;
} else {
this.schema.mock = this.mock;
}
if (this.schema.type === 'object') {
this.$delete(this.schema, 'mock')
}
},
mounted() {
},
methods: {
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);
};
},
change: function () {
},
advanced() {
this.$refs.variableAdvance.open();
},
showEdit() {
this.$emit('showEdit')
},
handleChange(e) {
this.$emit('change', e)
},
querySearchAsync(queryString, cb) {
const arr = this.mock || []
const results = queryString
? arr.filter(this.createStateFilter(queryString))
: arr
cb(results)
},
createStateFilter(queryString) {
return state => {
return (
state.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,31 @@
import Mock from "mockjs";
import {funcFilters} from "@/common/js/func-filter";
export const calculate = function (itemValue) {
if (!itemValue) {
return;
}
try {
if (itemValue.trim().startsWith("${")) {
// jmeter 内置函数不做处理
return itemValue;
}
let funcs = itemValue.split("|");
let value = Mock.mock(funcs[0].trim());
if (funcs.length === 1) {
return value;
}
for (let i = 1; i < funcs.length; i++) {
let func = funcs[i].trim();
let args = func.split(":");
let strings = [];
if (args[1]) {
strings = args[1].split(",");
}
value = funcFilters[args[0].trim()](value, ...strings);
}
return value;
} catch (e) {
return itemValue;
}
}

View File

@ -0,0 +1,26 @@
const value = {
description: null,
minItems:null,
maxItems:null,
uniqueItems:false
}
const attr = {
description: {
name: '描述',
type: 'string'
},
maxItems:{
name: '最大元素个数',
type: 'integer'
},
minItems:{
name: '最小元素个数',
type: 'integer'
},
uniqueItems:{
name:'元素不可重复',
type: 'boolean'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -0,0 +1,11 @@
const value = {
description: null
}
const attr = {
description: {
name: '描述',
type: 'string'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -0,0 +1,31 @@
const value = {
description: null,
maximum: null,
minimum: null,
exclusiveMaximum:null,
exclusiveMinimum:null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maximum:{
name:'最大值',
type:'integer'
},
minimum:{
name:'最小值',
type:'integer'
},
exclusiveMaximum:{
name:'不包含最大值',
type:'boolean'
},
exclusiveMinimum:{
name:'不包含最小值',
type:'boolean'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -0,0 +1,31 @@
const value = {
description: null,
maximum: null,
minimum: null,
exclusiveMaximum:null,
exclusiveMinimum:null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maximum:{
name:'最大值',
type:'number'
},
minimum:{
name:'最小值',
type:'number'
},
exclusiveMaximum:{
name:'不包含最大值',
type:'boolean'
},
exclusiveMinimum:{
name:'不包含最小值',
type:'boolean'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -0,0 +1,21 @@
const value = {
description: null,
maxProperties: null,
minProperties: null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maxProperties:{
name:'最大元素个数',
type:'integer'
},
minProperties:{
name:'最小元素个数',
type:'integer'
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -0,0 +1,32 @@
const value = {
description: null,
maxLength: null,
minLength: null,
pattern: null,
format:null
}
const attr = {
description: {
name: '描述',
type: 'string',
},
maxLength:{
name:'最大字符数',
type:'integer'
},
minLength:{
name:'最小字符数',
type:'integer'
},
pattern: {
name: '正则表达式',
type:'string'
},
format: {
name:'格式',
type:'array',
enums:['date','date-time','email','hostname','ipv4','ipv6','uri']
}
}
const wrapper = {value, attr}
export default wrapper

View File

@ -0,0 +1,17 @@
import _object from './object'
import _string from './string'
import _array from './array'
import _boolean from './boolean'
import _integer from './integer'
import _number from './number'
const TYPE_NAME = ['string', 'number', 'integer','object', 'array', 'boolean']
const TYPE = {
'object': _object,
'string': _string,
'array': _array,
'boolean': _boolean,
'integer': _integer,
'number': _number
}
export {TYPE ,TYPE_NAME}

View File

@ -0,0 +1,24 @@
export function clearAttr(obj) {
for(let key in obj){
delete obj[key]
}
}
/**
* 快速拷贝两个对象的属性值
* @param {*} source
* @param {*} target
*/
export function copyAttr(source, target){
Object.keys(target).forEach(key=>{target[key]=source[key]})
}
export function isNull(ele){
if(typeof ele==='undefined'){
return true;
}else if(ele==null){
return true;
}else if(ele==''){
return true;
}
return false;
}

View File

@ -0,0 +1,25 @@
import JsonSchemaEditor from './editor/index'
const components = [
JsonSchemaEditor
]
// 定义 install 方法
const install = function (Vue) {
if (install.installed) return
install.installed = true
// 遍历并注册全局组件
components.map(component => {
Vue.component(component.name, component)
})
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export default {
// 导出的对象必须具备一个 install 方法
install,
// 组件列表
...components
}

View File

@ -1,211 +0,0 @@
<template>
<div v-loading="loading">
<el-table
:data="tableData"
style="width: 100%;margin-bottom: 20px;"
row-key="id"
default-expand-all
@cell-click="editor"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
<el-table-column
prop="name"
label="名称">
<template slot-scope="scope">
<el-input v-if="scope.row.id === tabClickIndex && tabClickProperty === 'name'" v-model="scope.row.name" size="mini" style="padding-left: 20px" @blur="inputBlur(scope.row)"></el-input>
<span v-else>{{scope.row.name}}</span>
</template>
</el-table-column>
<el-table-column
prop="required"
label="必输项"
align="center"
width="80">
<template slot-scope="scope">
<el-checkbox v-model="scope.row.required"/>
</template>
</el-table-column>
<el-table-column
prop="type"
width="120px"
label="类型">
<template slot-scope="scope">
<el-select v-model="scope.row.type" slot="prepend" size="small">
<el-option v-for="item in typeData" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</template>
</el-table-column>
<el-table-column
prop="value"
label="内容">
<template slot-scope="scope">
<el-input v-if="scope.row.id === tabClickIndex && tabClickProperty === 'value'" v-model="scope.row.value" size="mini"></el-input>
<span v-else>{{scope.row.value}}</span>
</template>
</el-table-column>
<el-table-column
prop="describe"
label="描述">
<template slot-scope="scope">
<el-input v-if="scope.row.id === tabClickIndex && tabClickProperty === 'describe'" v-model="scope.row.describe" size="mini"></el-input>
<span v-else>{{scope.row.describe}}</span>
</template>
</el-table-column>
<el-table-column
prop="opt"
label="操作"
width="100">
<template slot-scope="scope">
<div>
<i class="el-icon-setting" style="margin-left: 5px;cursor: pointer" @click="setting(scope.row)"/>
<i class="el-icon-plus" style="margin-left: 5px;cursor: pointer" @click="add(scope.row)"/>
<i class="el-icon-close" style="margin-left: 5px;cursor: pointer" @click="deleteRow(scope.row)"/>
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import {getUUID} from "@/common/js/utils";
export default {
name: "MsJsonTable",
components: {},
data() {
return {
loading: false,
tableData: [{
id: "root",
parent: null,
name: 'root',
required: true,
type: 'object',
value: 'object',
describe: 'describe',
editor: false,
children: [],
}]
,
tabClickProperty: "",
tabClickIndex: "",
typeData: [
{id: 'string', label: 'string'},
{id: 'number', label: 'number'},
{id: 'array', label: 'array'},
{id: 'object', label: 'object'},
{id: 'boolean', label: 'boolean'},
{id: 'integer', label: 'integer'}
]
}
},
methods: {
editor(row, column) {
switch (column.property) {
case 'name':
this.tabClickIndex = row.id
this.tabClickProperty = column.property
break
case 'value':
this.tabClickIndex = row.id
this.tabClickProperty = column.property
break;
case 'describe':
this.tabClickIndex = row.id
this.tabClickProperty = column.property
break;
default:
return
}
},
inputBlur() {
this.tabClickIndex = null;
this.tabClickProperty = '';
this.reload();
},
reload() {
this.loading = true;
this.$nextTick(() => {
this.loading = false
})
},
setting() {
},
add(row) {
let obj = {
id: getUUID(),
name: 'field',
required: true,
type: 'string',
value: 'test',
parent: null,
describe: 'describe',
children: [],
}
console.log("all", this.tableData)
console.log("row", row)
if (row.type === "object") {
obj.parent = row.id;
row.children.push(obj);
return;
}
let parentRow = {};
const index = this.tableData.findIndex(d => d.id != undefined && row.parent != undefined && d.id === row.parent);
if (index != -1) {
parentRow = this.tableData[index];
} else {
for (let i in this.tableData) {
if (this.tableData[i].children) {
parentRow = this.recursiveRemove(this.tableData[i].children, row);
}
}
}
console.log(parentRow)
if (parentRow) {
obj.parent = parentRow.id;
parentRow.children.push(obj);
return;
}
this.tableData.push(obj);
},
recursiveRemove(arr, row) {
for (let i in arr) {
const index = arr.findIndex(d => d.id != undefined && row.id != undefined && d.id === row.id)
if (index != -1) {
arr.splice(index, 1);
return arr[i];
}
if (arr[i].children != undefined && arr[i].children.length > 0) {
this.recursiveRemove(arr[i].children, row);
}
}
},
deleteRow(row) {
const index = this.tableData.findIndex(d => d.id != undefined && row.id != undefined && d.id === row.id)
if (index == -1) {
this.tableData.forEach(item => {
if (item.children) {
this.recursiveRemove(item.children, row);
}
})
} else {
this.tableData.splice(index, 1);
}
}
}
}
</script>
<style scoped>
.name-input >>> .el-input__inner {
height: 25px;
line-height: 25px;
}
</style>

View File

@ -84,7 +84,6 @@ export default {
methods: {
registerEvents() {
PerformanceEvent.$on(LIST_CHANGE, () => {
// // todo refs
this.$refs.testRecent.recent();
this.$refs.reportRecent.recent();
});
@ -92,6 +91,9 @@ export default {
},
mounted() {
this.registerEvents();
},
beforeDestroy() {
PerformanceEvent.$off(LIST_CHANGE);
}
}

View File

@ -128,7 +128,7 @@ export default {
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
loading: false,
testId: null,

View File

@ -108,7 +108,7 @@ export default {
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
loading: false,
testId: null,

View File

@ -143,7 +143,7 @@
},
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
options: [],
loading: false,

View File

@ -458,10 +458,10 @@
items: [],
userList: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
dialogCurrentPage: 1,
dialogPageSize: 5,
dialogPageSize: 10,
dialogTotal: 0,
memberLineData: [],
memberForm: {},

View File

@ -120,7 +120,7 @@ export default {
zentao: false,
form: {},
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
rules: {
name: [

View File

@ -36,16 +36,23 @@
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item :label="$t('system_parameter_setting.test_recipients')">
<el-input v-model="formInline.recipient" :placeholder="$t('system_parameter_setting.test_recipients')"
autocomplete="new-password" show-password type="text" ref="input">
</el-input>
<p style="color: #8a8b8d">({{ $t('system_parameter_setting.tip') }})</p>
</el-form-item>
</el-col>
</el-row>
<!---->
<div style="border: 0px;margin-bottom: 20px;margin-top: 20px">
<el-checkbox v-model="formInline.SSL" :label="$t('system_parameter_setting.SSL')"></el-checkbox>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.ssl" :label="$t('system_parameter_setting.SSL')"></el-checkbox>
</div>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.TLS" :label="$t('system_parameter_setting.TLS')"></el-checkbox>
</div>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.ANON" :label="$t('system_parameter_setting.SMTP')"></el-checkbox>
<el-checkbox v-model="formInline.tls" :label="$t('system_parameter_setting.TLS')"></el-checkbox>
</div>
<template v-slot:footer>
</template>
@ -114,13 +121,10 @@ export default {
},
query() {
this.result = this.$get("/system/mail/info", response => {
this.$set(this.formInline, "host", response.data[0].paramValue);
this.$set(this.formInline, "port", response.data[1].paramValue);
this.$set(this.formInline, "account", response.data[2].paramValue);
this.$set(this.formInline, "password", response.data[3].paramValue);
this.$set(this.formInline, "SSL", JSON.parse(response.data[4].paramValue));
this.$set(this.formInline, "TLS", JSON.parse(response.data[5].paramValue));
this.$set(this.formInline, "ANON", JSON.parse(response.data[6].paramValue));
this.formInline = response.data;
this.formInline.ssl = this.formInline.ssl === 'true';
this.formInline.tls = this.formInline.tls === 'true';
console.log(this.formInline)
this.$nextTick(() => {
this.$refs.formInline.clearValidate();
})
@ -137,13 +141,13 @@ export default {
},
testConnection(formInline) {
let param = {
"smtp.server": this.formInline.host,
"smtp.host": this.formInline.host,
"smtp.port": this.formInline.port,
"smtp.account": this.formInline.account,
"smtp.password": this.formInline.password,
"smtp.ssl": this.formInline.SSL,
"smtp.tls": this.formInline.TLS,
"smtp.anon": this.formInline.ANON,
"smtp.ssl": this.formInline.ssl,
"smtp.tls": this.formInline.tls,
"smtp.recipient": this.formInline.recipient,
};
this.$refs[formInline].validate((valid) => {
if (valid) {
@ -171,9 +175,10 @@ export default {
{paramKey: "smtp.port", paramValue: this.formInline.port, type: "text", sort: 2},
{paramKey: "smtp.account", paramValue: this.formInline.account, type: "text", sort: 3},
{paramKey: "smtp.password", paramValue: this.formInline.password, type: "password", sort: 4},
{paramKey: "smtp.ssl", paramValue: this.formInline.SSL, type: "text", sort: 5},
{paramKey: "smtp.tls", paramValue: this.formInline.TLS, type: "text", sort: 6},
{paramKey: "smtp.anon", paramValue: this.formInline.ANON, type: "text", sort: 7}
{paramKey: "smtp.ssl", paramValue: this.formInline.ssl, type: "text", sort: 5},
{paramKey: "smtp.tls", paramValue: this.formInline.tls, type: "text", sort: 6},
{paramKey: "smtp.recipient", paramValue: this.formInline.recipient, type: "text", sort: 8}
]
this.$refs[formInline].validate(valid => {

View File

@ -218,10 +218,10 @@ export default {
dialogOrgMemberUpdateVisible: false,
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
dialogCurrentPage: 1,
dialogPageSize: 5,
dialogPageSize: 10,
dialogTotal: 0,
currentRow: {},
condition: {},

View File

@ -487,10 +487,10 @@
dialogCondition: {},
items: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
dialogCurrentPage: 1,
dialogPageSize: 5,
dialogPageSize: 10,
dialogTotal: 0,
memberLineData: [],
memberForm: {},

View File

@ -177,7 +177,7 @@ export default {
condition: {},
items: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
form: {},
requiredRules: [{required: true, message: this.$t('test_resource_pool.fill_the_data'), trigger: 'blur'}],

View File

@ -367,7 +367,7 @@ export default {
multipleSelection: [],
userRole: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
condition: {},
tableData: [],

View File

@ -133,7 +133,7 @@
},
multipleSelection: [],
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
}
},

View File

@ -66,9 +66,6 @@ export default {
data() {
return {
result: {},
currentPage: 1,
pageSize: 5,
total: 0,
projects: [],
treeNodes: [],
selectNodeIds: [],

View File

@ -62,7 +62,7 @@ export default {
id: '',
condition: {},
currentPage: 1,
pageSize: 5,
pageSize: 10,
total: 0,
url: '',
type: ''

View File

@ -131,11 +131,13 @@ export default {
},
registerEvents() {
TrackEvent.$on(LIST_CHANGE, () => {
// // todo refs
this.$refs.planRecent.recent();
this.$refs.caseRecent.recent();
});
}
},
beforeDestroy() {
TrackEvent.$off(LIST_CHANGE);
}
}

View File

@ -26,6 +26,7 @@
</template>
</el-table-column>
<el-table-column prop="num" label="ID" show-overflow-tooltip/>
<el-table-column prop="name" :label="$t('api_test.definition.api_name')" show-overflow-tooltip/>
<el-table-column

View File

@ -59,7 +59,11 @@
<style scoped>
.el-dialog {
min-height: 700px;
min-height: 600px;
}
.tree-aside {
max-height: 600px;
}
.el-dialog >>> .el-dialog__body {

View File

@ -9,7 +9,7 @@
<template>
<ckeditor v-if="!isReportView" :editor="editor" v-model="preview.content" :config="editorConfig"></ckeditor>
<div v-if="isReportView" v-html="preview.content"></div>
<div class="rich-text-content" v-if="isReportView" v-html="preview.content"></div>
</template>
</common-component>
@ -53,4 +53,11 @@
<style scoped>
.rich-text-content >>> .table td {
border: solid 1px #e6e6e6;
min-width: 2em;
padding: .4em;
}
</style>

View File

@ -19,6 +19,8 @@ import '../common/css/main.css';
import CKEditor from '@ckeditor/ckeditor5-vue';
import VueFab from 'vue-float-action-button'
import {horizontalDrag} from "../common/js/directive";
import JsonSchemaEditor from './components/common/json-schema/schema/index';
Vue.use(JsonSchemaEditor);
Vue.config.productionTip = false;
Vue.use(icon);

View File

@ -120,7 +120,6 @@ html,body {
padding-right: 60px;
}
/* 滚动条样式 */
::-webkit-scrollbar{
width: 5px;
@ -137,3 +136,36 @@ html,body {
background-color: transparent;
position: fixed;
}
/* <-- 表格全选样式 */
.ms-select-all th:first-child {
border: 1px solid #DCDFE6;
border-radius:5px;
padding: 0px;
margin-top: 10px;
display: inline-block;
/*margin-top: 25px;*/
width: 50px;
}
.ms-select-all th:nth-child(2) {
overflow: visible;
}
.ms-select-all th:first-child>.cell {
padding: 5px;
width: 35px;
}
.ms-select-all th:nth-child(2)>.cell {
overflow: visible;
}
.ms-select-all .el-icon-arrow-down {
position: absolute;
display: inline-block;
top: -7px;
left: -38px;
width: 30px;
}
/* 表格全选样式 --> */

View File

@ -489,7 +489,7 @@ export default {
file_exist: "The name already exists in the project",
upload_limit_size: "Upload file size cannot exceed 30MB!",
},
batch_menus:{
batch_menus: {
select_all_data: "Select all datas({0})",
select_show_data: "Select show datas({0})",
},
@ -505,9 +505,9 @@ export default {
api_case_status: "Ise case status",
api_case_passing_rate: "Use case pass rate",
create_tip: "Note: Detailed interface information can be filled out on the edit page",
select_comp:{
no_data:"No Data",
add_data:"Add Data"
select_comp: {
no_data: "No Data",
add_data: "Add Data"
},
request: {
grade_info: "Filter by rank",
@ -1254,6 +1254,8 @@ export default {
host: 'Host number cannot be empty',
port: 'Port cannot be empty',
account: 'Account cannot be empty',
test_recipients:'Test recipients',
tip:'Tip: use as test mail recipient only',
},
i18n: {
@ -1365,5 +1367,47 @@ export default {
workspace_quota_list: "Workspace quota list of {0}",
unlimited: "Unlimited",
clean: "Clean"
},
schema: {
title: "Title",
import_json: "Import JSON",
base_setting: "Base Setting",
all_setting: "Source Code",
default: "Default",
description: "Description",
"adv_setting": "Advanced Settings",
"add_child_node": "Add child node",
add_sibling_node: "Add sibling nodes",
add_node: "Add sibling/child nodes",
remove_node: "Remove node",
child_node: "Child node",
sibling_node: "Sibling node",
ok: "OK",
cancel: "Cancel",
minLength: "Min length",
maxLength: "Max length",
pattern: "MUST be a valid regular expression.",
exclusiveMinimum: "Value strictly less than",
exclusiveMaximum: "Value strictly more than",
minimum: "Min",
maximum: "Max",
uniqueItems: "Unique Items",
minItems: "MinItems",
maxItems: "MaxItems",
minProperties: "MinProperties",
maxProperties: "MaxProperties",
checked_all: "Checked All",
valid_json: "Not valid json",
enum: "Enum",
enum_msg: "One value per line",
enum_desc: "desc",
enum_desc_msg: "enum description",
required: "Required",
mock: "mock",
mockLink: "Help",
format: "Format",
nothing: "Nothing",
preview: "Preview",
add_custom: "Add Custom Prop"
}
};

View File

@ -487,7 +487,7 @@ export default {
file_exist: "该项目下已存在该jar包",
upload_limit_size: "上传文件大小不能超过 30MB!",
},
batch_menus:{
batch_menus: {
select_all_data: "选择所有数据(共{0}条)",
select_show_data: "选择可见数据(共{0}条)",
},
@ -503,9 +503,9 @@ export default {
api_case_status: "用例状态",
api_case_passing_rate: "用例通过率",
create_tip: "注: 详细的接口信息可以在编辑页面填写",
select_comp:{
no_data:"无数据",
add_data:"去添加"
select_comp: {
no_data: "无数据",
add_data: "去添加"
},
request: {
grade_info: "按等级筛选",
@ -1255,6 +1255,8 @@ export default {
host: '主机号不能为空',
port: '端口号不能为空',
account: '账户不能为空',
test_recipients:'测试收件人',
tip:'提示:仅用来作为测试邮件收件人',
},
i18n: {
home: '首页',
@ -1365,5 +1367,47 @@ export default {
workspace_quota_list: "{0}的工作空间配额列表",
unlimited: "无限制",
clean: "清空"
},
schema: {
title: "标题",
import_json: "导入 json",
base_setting: "基础设置",
all_setting: "编辑源码",
default: "默认值",
description: "描述",
adv_setting: "高级设置",
add_child_node: "添加子节点",
add_sibling_node: "添加兄弟节点",
add_node: "添加兄弟/子节点",
remove_node: "删除节点",
child_node: "子节点",
sibling_node: "兄弟节点",
ok: "确定",
cancel: "取消",
minLength: "最小长度",
maxLength: "最大长度",
pattern: "用正则表达式约束字符串",
exclusiveMinimum: "开启后,数据必须大于最小值",
exclusiveMaximum: "开启后,数据必须小于最大值",
minimum: "最小值",
maximum: "最大值",
uniqueItems: "开启后,每个元素都不相同",
minItems: "最小元素个数",
maxItems: "最大元素个数",
minProperties: "最小元素个数",
maxProperties: "最大元素个数",
checked_all: "全选",
valid_json: "不是合法的json字符串",
enum: "枚举",
enum_msg: "每行只能写一个值",
enum_desc: "备注",
enum_desc_msg: "备注描述信息",
required: "是否必须",
mock: "mock",
mockLink: "查看文档",
format: "格式化",
nothing: "无",
preview: "预览",
add_custom: "添加自定义属性"
}
};

View File

@ -487,7 +487,7 @@ export default {
file_exist: "該項目下已存在該jar包",
upload_limit_size: "上傳文件大小不能超過 30MB!",
},
batch_menus:{
batch_menus: {
select_all_data: "選擇所有數據(共{0}條)",
select_show_data: "選擇可見數據(共{0}條)",
},
@ -503,9 +503,9 @@ export default {
api_case_status: "用例狀態",
api_case_passing_rate: "用例通過率",
create_tip: "註: 詳細的接口信息可以在編輯頁面填寫",
select_comp:{
no_data:"無數據",
add_data:"去添加"
select_comp: {
no_data: "無數據",
add_data: "去添加"
},
request: {
grade_info: "按等級筛选",
@ -1254,6 +1254,8 @@ export default {
host: '主機號不能為空',
port: '端口號不能為空',
account: '賬戶不能為空',
test_recipients:'測試收件人',
tip:'提示:僅用來作為測試郵件收件人',
},
i18n: {
home: '首頁',
@ -1364,5 +1366,47 @@ export default {
workspace_quota_list: "{0}的工作空間配額列表",
unlimited: "無限制",
clean: "清空"
},
schema: {
title: "标题",
import_json: "导入 json",
base_setting: "基础设置",
all_setting: "编辑源码",
default: "默认值",
description: "描述",
adv_setting: "高级设置",
add_child_node: "添加子节点",
add_sibling_node: "添加兄弟节点",
add_node: "添加兄弟/子节点",
remove_node: "删除节点",
child_node: "子节点",
sibling_node: "兄弟节点",
ok: "确定",
cancel: "取消",
minLength: "最小长度",
maxLength: "最大长度",
pattern: "用正则表达式约束字符串",
exclusiveMinimum: "开启后,数据必须大于最小值",
exclusiveMaximum: "开启后,数据必须小于最大值",
minimum: "最小值",
maximum: "最大值",
uniqueItems: "开启后,每个元素都不相同",
minItems: "最小元素个数",
maxItems: "最大元素个数",
minProperties: "最小元素个数",
maxProperties: "最大元素个数",
checked_all: "全选",
valid_json: "不是合法的json字符串",
enum: "枚举",
enum_msg: "每行只能写一个值",
enum_desc: "备注",
enum_desc_msg: "备注描述信息",
required: "是否必须",
mock: "mock",
mockLink: "查看文档",
format: "格式化",
nothing: "无",
preview: "预览",
add_custom: "添加自定义属性"
}
};