diff --git a/backend/pom.xml b/backend/pom.xml index 6f47ee07c7..1e6add67e4 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -19,7 +19,7 @@ 1.8 5.2.1 1.1.3 - 2.7.7 + 2.7.8 20.1.0 diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java new file mode 100644 index 0000000000..f37221cf82 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -0,0 +1,98 @@ +package io.metersphere.api.controller; + +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import io.metersphere.api.dto.APIReportResult; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.ApiDefinitionRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.RunDefinitionRequest; +import io.metersphere.api.dto.definition.SaveApiDefinitionRequest; +import io.metersphere.api.service.ApiDefinitionService; +import io.metersphere.base.domain.ApiDefinition; +import io.metersphere.commons.constants.RoleConstants; +import io.metersphere.commons.utils.PageUtils; +import io.metersphere.commons.utils.Pager; +import io.metersphere.commons.utils.SessionUtils; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.util.List; + + +@RestController +@RequestMapping(value = "/api/definition") +@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR) +public class ApiDefinitionController { + @Resource + private ApiDefinitionService apiDefinitionService; + + @PostMapping("/list/{goPage}/{pageSize}") + public Pager> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); + return PageUtils.setPageInfo(page, apiDefinitionService.list(request)); + } + + @PostMapping(value = "/create", consumes = {"multipart/form-data"}) + public void create(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List bodyFiles) { + apiDefinitionService.create(request, bodyFiles); + } + + @PostMapping(value = "/update", consumes = {"multipart/form-data"}) + public void update(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List bodyFiles) { + apiDefinitionService.update(request, bodyFiles); + } + + @GetMapping("/delete/{id}") + public void delete(@PathVariable String id) { + apiDefinitionService.delete(id); + } + + @PostMapping("/deleteBatch") + public void deleteBatch(@RequestBody List ids) { + apiDefinitionService.deleteBatch(ids); + } + + @PostMapping("/removeToGc") + public void removeToGc(@RequestBody List ids) { + apiDefinitionService.removeToGc(ids); + } + + + @GetMapping("/get/{id}") + public ApiDefinition get(@PathVariable String id) { + return apiDefinitionService.get(id); + } + + @PostMapping(value = "/run/debug", consumes = {"multipart/form-data"}) + public String runDebug(@RequestPart("request") RunDefinitionRequest request, @RequestPart(value = "files") List bodyFiles) { + return apiDefinitionService.run(request, bodyFiles); + } + + @PostMapping(value = "/run", consumes = {"multipart/form-data"}) + public String run(@RequestPart("request") RunDefinitionRequest request, @RequestPart(value = "files") List bodyFiles) { + return apiDefinitionService.run(request, bodyFiles); + } + + @GetMapping("/report/get/{testId}/{test}") + public APIReportResult get(@PathVariable String testId, @PathVariable String test) { + return apiDefinitionService.getResult(testId, test); + } + + @GetMapping("/report/getReport/{testId}") + public APIReportResult getReport(@PathVariable String testId) { + return apiDefinitionService.getDbResult(testId); + } + + @PostMapping(value = "/import", consumes = {"multipart/form-data"}) + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public String testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) { + return apiDefinitionService.apiTestImport(file, request); + } + + +} diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiModuleController.java b/backend/src/main/java/io/metersphere/api/controller/ApiModuleController.java new file mode 100644 index 0000000000..7d1066fb44 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/controller/ApiModuleController.java @@ -0,0 +1,56 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.dto.definition.ApiModuleDTO; +import io.metersphere.api.dto.definition.DragModuleRequest; +import io.metersphere.api.service.ApiModuleService; +import io.metersphere.base.domain.ApiModule; +import io.metersphere.commons.constants.RoleConstants; +import io.metersphere.service.CheckOwnerService; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@RequestMapping("/api/module") +@RestController +@RequiresRoles(value = {RoleConstants.ADMIN, RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER, RoleConstants.ORG_ADMIN}, logical = Logical.OR) +public class ApiModuleController { + + @Resource + ApiModuleService apiModuleService; + @Resource + private CheckOwnerService checkOwnerService; + + @GetMapping("/list/{projectId}/{protocol}") + public List getNodeByProjectId(@PathVariable String projectId,@PathVariable String protocol) { + checkOwnerService.checkProjectOwner(projectId); + return apiModuleService.getNodeTreeByProjectId(projectId,protocol); + } + + @PostMapping("/add") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public String addNode(@RequestBody ApiModule node) { + return apiModuleService.addNode(node); + } + + @PostMapping("/edit") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public int editNode(@RequestBody DragModuleRequest node) { + return apiModuleService.editNode(node); + } + + @PostMapping("/delete") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public int deleteNode(@RequestBody List nodeIds) { + //nodeIds 包含删除节点ID及其所有子节点ID + return apiModuleService.deleteNode(nodeIds); + } + + @PostMapping("/drag") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public void dragNode(@RequestBody DragModuleRequest node) { + apiModuleService.dragNode(node); + } +} diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java b/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java new file mode 100644 index 0000000000..7d4f44855b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/controller/ApiTestCaseController.java @@ -0,0 +1,46 @@ +package io.metersphere.api.controller; + +import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.ApiTestCaseResult; +import io.metersphere.api.dto.definition.SaveApiTestCaseRequest; +import io.metersphere.api.service.ApiTestCaseService; +import io.metersphere.commons.constants.RoleConstants; +import io.metersphere.commons.utils.SessionUtils; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.util.List; + +@RestController +@RequestMapping(value = "/api/testcase") +@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR) +public class ApiTestCaseController { + + @Resource + private ApiTestCaseService apiTestCaseService; + + @PostMapping("/list") + public List list(@RequestBody ApiTestCaseRequest request) { + request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); + return apiTestCaseService.list(request); + } + + @PostMapping(value = "/create", consumes = {"multipart/form-data"}) + public void create(@RequestPart("request") SaveApiTestCaseRequest request, @RequestPart(value = "files") List bodyFiles) { + apiTestCaseService.create(request, bodyFiles); + } + + @PostMapping(value = "/update", consumes = {"multipart/form-data"}) + public void update(@RequestPart("request") SaveApiTestCaseRequest request, @RequestPart(value = "files") List bodyFiles) { + apiTestCaseService.update(request, bodyFiles); + } + + @GetMapping("/delete/{id}") + public void delete(@PathVariable String id) { + apiTestCaseService.delete(id); + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java b/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java index 38e18ae261..704dadc295 100644 --- a/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/ApiTestImportRequest.java @@ -7,6 +7,8 @@ import lombok.Setter; @Getter public class ApiTestImportRequest { private String name; + private String moduleId; + private String modulePath; private String environmentId; private String projectId; private String platform; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiComputeResult.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiComputeResult.java new file mode 100644 index 0000000000..d00a14e682 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiComputeResult.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.definition; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ApiComputeResult { + private String apiDefinitionId; + private String caseTotal; + private String status; + private String passRate; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDTO.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDTO.java new file mode 100644 index 0000000000..acde76be3b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDTO.java @@ -0,0 +1,14 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.TestCaseWithBLOBs; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ApiDTO extends TestCaseWithBLOBs { + + private String maintainerName; + private String apiName; + private String performName; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java new file mode 100644 index 0000000000..2a3cb83eea --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java @@ -0,0 +1,28 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.controller.request.OrderRequest; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +public class ApiDefinitionRequest { + + private String id; + private String excludeId; + private String projectId; + private String moduleId; + private List moduleIds; + private String protocol; + private String name; + private String workspaceId; + private String userId; + private boolean recent = false; + private List orders; + private List filters; + private Map combine; + private List ids; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java new file mode 100644 index 0000000000..f129c5b161 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java @@ -0,0 +1,23 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.ApiDefinition; +import io.metersphere.base.domain.Schedule; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class ApiDefinitionResult extends ApiDefinition { + + private String projectName; + + private String userName; + + private String caseTotal; + + private String caseStatus; + + private String casePassingRate; + + private Schedule schedule; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiModuleDTO.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiModuleDTO.java new file mode 100644 index 0000000000..8641539e3c --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiModuleDTO.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.ApiModule; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class ApiModuleDTO extends ApiModule { + + private String label; + private List children; + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseRequest.java new file mode 100644 index 0000000000..3dfb1b9daa --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseRequest.java @@ -0,0 +1,21 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.controller.request.OrderRequest; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class ApiTestCaseRequest { + + private String id; + private String projectId; + private String priority; + private String name; + private String environmentId; + private String workspaceId; + private String apiDefinitionId; + private List orders; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseResult.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseResult.java new file mode 100644 index 0000000000..943126a6a3 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseResult.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.ApiTestCase; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class ApiTestCaseResult extends ApiTestCase { + private String projectName; + private String createUser; + private String updateUser; + private String execResult; + private boolean active = false; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/DragModuleRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/DragModuleRequest.java new file mode 100644 index 0000000000..39fe06f9dc --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/DragModuleRequest.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.base.domain.ApiModule; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class DragModuleRequest extends ApiModule { + + List nodeIds; + ApiModuleDTO nodeTree; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java new file mode 100644 index 0000000000..169d0450a4 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/RunDefinitionRequest.java @@ -0,0 +1,23 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.response.Response; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +public class RunDefinitionRequest { + + private String id; + + private String reportId; + + private MsTestElement testElement; + + private Response response; + + private List bodyUploadIds; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java new file mode 100644 index 0000000000..05adc44e30 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java @@ -0,0 +1,50 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.response.Response; +import io.metersphere.base.domain.Schedule; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +public class SaveApiDefinitionRequest { + + private String id; + + private String reportId; + + private String projectId; + + private String name; + + private String path; + + private String protocol; + + private String moduleId; + + private String status; + + private String description; + + private String modulePath; + + private String method; + + private MsTestElement request; + + private Response response; + + private String environmentId; + + private String userId; + + private Schedule schedule; + + private String triggerMode; + + private List bodyUploadIds; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java new file mode 100644 index 0000000000..50f2588399 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java @@ -0,0 +1,38 @@ +package io.metersphere.api.dto.definition; + +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +public class SaveApiTestCaseRequest { + + private String id; + + private String projectId; + + private String name; + + private String priority; + + private String apiDefinitionId; + + private String description; + + private MsTestElement request; + + private String response; + + private String crateUserId; + + private String updateUserId; + + private Long createTime; + + private Long updateTime; + + private List bodyUploadIds; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java new file mode 100644 index 0000000000..f4475d097f --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/ApiDefinitionImport.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.definition.parse; + +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import lombok.Data; + +import java.util.List; + +@Data +public class ApiDefinitionImport { + private String projectName; + private String protocol; + private List data; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanCollection.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanCollection.java new file mode 100644 index 0000000000..bf741ca795 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanCollection.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.definition.parse.postman; + +import lombok.Data; + +import java.util.List; + +@Data +public class PostmanCollection { + + private PostmanCollectionInfo info; + private List item; + private List variable; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanCollectionInfo.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanCollectionInfo.java new file mode 100644 index 0000000000..467056393c --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanCollectionInfo.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.definition.parse.postman; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; + +@Data +public class PostmanCollectionInfo { + + @JSONField(name = "_postman_id") + private String postmanId; + private String name; + private String schema; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanItem.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanItem.java new file mode 100644 index 0000000000..cadbc25cdb --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanItem.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition.parse.postman; + +import lombok.Data; + +import java.util.List; + +@Data +public class PostmanItem { + private String name; + private PostmanRequest request; + private List item; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanKeyValue.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanKeyValue.java new file mode 100644 index 0000000000..c54838905e --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanKeyValue.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.definition.parse.postman; + +import lombok.Data; + +@Data +public class PostmanKeyValue { + private String key; + private String value; + private String type; + + public PostmanKeyValue() { + } + + public PostmanKeyValue(String key, String value) { + this.key = key; + this.value = value; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanRequest.java new file mode 100644 index 0000000000..4c13b27b90 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanRequest.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.definition.parse.postman; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +import java.util.List; + +@Data +public class PostmanRequest { + + private String method; + private String schema; + private List header; + private JSONObject body; + private JSONObject auth; + private PostmanUrl url; + private String description; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanUrl.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanUrl.java new file mode 100644 index 0000000000..5c6715f658 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/postman/PostmanUrl.java @@ -0,0 +1,14 @@ +package io.metersphere.api.dto.definition.parse.postman; + +import lombok.Data; + +import java.util.List; + +@Data +public class PostmanUrl { + + private String raw; + private String protocol; + private String port; + private List query; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerApi.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerApi.java new file mode 100644 index 0000000000..f720eddea0 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerApi.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.definition.parse.swagger; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +import java.util.List; + +@Data +public class SwaggerApi { + private String swagger; + private SwaggerInfo info; + private String host; + private String basePath; + private List schemes; + private List tags; + private JSONObject paths; + private JSONObject definitions; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerInfo.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerInfo.java new file mode 100644 index 0000000000..a93726c697 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerInfo.java @@ -0,0 +1,11 @@ +package io.metersphere.api.dto.definition.parse.swagger; + +import lombok.Data; + +@Data +public class SwaggerInfo { + private String version; + private String title; + private String description; + private String termsOfService; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerParameter.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerParameter.java new file mode 100644 index 0000000000..8e9aa44df5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerParameter.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.definition.parse.swagger; + +import lombok.Data; + +@Data +public class SwaggerParameter { + private String name; + private String in; + private String description; + private Boolean required; + private String type; + private String format; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerRequest.java new file mode 100644 index 0000000000..08a5b4b99e --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerRequest.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.definition.parse.swagger; + +import lombok.Data; + +import java.util.List; + +@Data +public class SwaggerRequest { + private List tags; + private String summary; + private String description; + private String operationId; + private List consumes; + private List produces; + private List parameters; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerTag.java b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerTag.java new file mode 100644 index 0000000000..cf24aa2f39 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/parse/swagger/SwaggerTag.java @@ -0,0 +1,9 @@ +package io.metersphere.api.dto.definition.parse.swagger; + +import lombok.Data; + +@Data +public class SwaggerTag { + private String name; + private String description; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java new file mode 100644 index 0000000000..43cee3bad6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java @@ -0,0 +1,94 @@ +package io.metersphere.api.dto.definition.request; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.metersphere.api.dto.definition.request.assertions.MsAssertions; +import io.metersphere.api.dto.definition.request.auth.MsAuthManager; +import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager; +import io.metersphere.api.dto.definition.request.extract.MsExtract; +import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor; +import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor; +import io.metersphere.api.dto.definition.request.sampler.MsDubboSampler; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; +import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler; +import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; +import io.metersphere.commons.utils.LogUtil; +import lombok.Data; +import org.apache.jmeter.protocol.http.control.AuthManager; +import org.apache.jmeter.save.SaveService; +import org.apache.jorphan.collections.HashTree; +import org.apache.jorphan.collections.ListedHashTree; + +import java.io.ByteArrayOutputStream; +import java.util.LinkedList; +import java.util.List; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = MsHTTPSamplerProxy.class, name = "HTTPSamplerProxy"), + @JsonSubTypes.Type(value = MsHeaderManager.class, name = "HeaderManager"), + @JsonSubTypes.Type(value = MsJSR223PostProcessor.class, name = "JSR223PostProcessor"), + @JsonSubTypes.Type(value = MsJSR223PreProcessor.class, name = "JSR223PreProcessor"), + @JsonSubTypes.Type(value = MsTestPlan.class, name = "TestPlan"), + @JsonSubTypes.Type(value = MsThreadGroup.class, name = "ThreadGroup"), + @JsonSubTypes.Type(value = MsAuthManager.class, name = "AuthManager"), + @JsonSubTypes.Type(value = MsAssertions.class, name = "Assertions"), + @JsonSubTypes.Type(value = MsExtract.class, name = "Extract"), + @JsonSubTypes.Type(value = MsTCPSampler.class, name = "TCPSampler"), + @JsonSubTypes.Type(value = MsDubboSampler.class, name = "DubboSampler"), + @JsonSubTypes.Type(value = MsJDBCSampler.class, name = "JDBCSampler"), + +}) +@JSONType(seeAlso = {MsHTTPSamplerProxy.class, MsHeaderManager.class, MsJSR223PostProcessor.class, + MsJSR223PreProcessor.class, MsTestPlan.class, MsThreadGroup.class, AuthManager.class, MsAssertions.class, + MsExtract.class, MsTCPSampler.class, MsDubboSampler.class, MsJDBCSampler.class}, typeKey = "type") +@Data +public abstract class MsTestElement { + private String type; + @JSONField(ordinal = 1) + private String id; + @JSONField(ordinal = 2) + private String name; + @JSONField(ordinal = 3) + private String label; + @JSONField(ordinal = 4) + private LinkedList hashTree; + + public void toHashTree(HashTree tree, List hashTree) { + for (MsTestElement el : hashTree) { + el.toHashTree(tree, el.hashTree); + } + } + + /** + * 转换JMX + * + * @param hashTree + * @return + */ + public String getJmx(HashTree hashTree) { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + SaveService.saveTree(hashTree, baos); + System.out.print(baos.toString()); + return baos.toString(); + } catch (Exception e) { + e.printStackTrace(); + LogUtil.warn("HashTree error, can't log jmx content"); + } + return null; + } + + public HashTree generateHashTree() { + HashTree jmeterTestPlanHashTree = new ListedHashTree(); + this.toHashTree(jmeterTestPlanHashTree, this.hashTree); + return jmeterTestPlanHashTree; + } + +} + + + + + diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java new file mode 100644 index 0000000000..9e041cbc73 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestPlan.java @@ -0,0 +1,42 @@ +package io.metersphere.api.dto.definition.request; + +import com.alibaba.fastjson.annotation.JSONType; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.TestPlan; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "TestPlan") +public class MsTestPlan extends MsTestElement { + private String type = "TestPlan"; + + public void toHashTree(HashTree tree, List hashTree) { + final HashTree testPlanTree = tree.add(getPlan()); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(testPlanTree, el.getHashTree()); + }); + } + } + + public TestPlan getPlan() { + TestPlan testPlan = new TestPlan(this.getName() + "TestPlan"); + testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); + testPlan.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestPlanGui")); + testPlan.setEnabled(true); + testPlan.setFunctionalMode(false); + testPlan.setSerialized(true); + testPlan.setTearDownOnShutdown(true); + testPlan.setUserDefinedVariables(new Arguments()); + return testPlan; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java new file mode 100644 index 0000000000..7443d3a3c5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsThreadGroup.java @@ -0,0 +1,52 @@ +package io.metersphere.api.dto.definition.request; + +import com.alibaba.fastjson.annotation.JSONType; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.control.LoopController; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "ThreadGroup") +public class MsThreadGroup extends MsTestElement { + private String type = "ThreadGroup"; + + public void toHashTree(HashTree tree, List hashTree) { + final HashTree groupTree = tree.add(getThreadGroup()); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(groupTree, el.getHashTree()); + }); + } + } + + public ThreadGroup getThreadGroup() { + LoopController loopController = new LoopController(); + loopController.setName("LoopController"); + loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName()); + loopController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("LoopControlPanel")); + loopController.setEnabled(true); + loopController.setLoops(1); + + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setEnabled(true); + threadGroup.setName(this.getName() + "ThreadGroup"); + threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); + threadGroup.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ThreadGroupGui")); + threadGroup.setNumThreads(1); + threadGroup.setRampUp(1); + threadGroup.setDelay(0); + threadGroup.setDuration(0); + threadGroup.setProperty(ThreadGroup.ON_SAMPLE_ERROR, ThreadGroup.ON_SAMPLE_ERROR_CONTINUE); + threadGroup.setScheduler(false); + threadGroup.setSamplerController(loopController); + return threadGroup; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java new file mode 100644 index 0000000000..5a84a0cbea --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionDuration extends MsAssertionType { + private long value; + + public MsAssertionDuration() { + setType(MsAssertionType.DURATION); + } + + public boolean isValid() { + return value > 0; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java new file mode 100644 index 0000000000..110741291c --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java @@ -0,0 +1,25 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionJSR223 extends MsAssertionType { + private String variable; + private String operator; + private String value; + private String desc; + private String name; + private String script; + private String language; + + public MsAssertionJSR223() { + setType(MsAssertionType.JSR223); + } + + public boolean isValid() { + return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(language); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java new file mode 100644 index 0000000000..a86686a369 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java @@ -0,0 +1,21 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionJsonPath extends MsAssertionType { + private String expect; + private String expression; + private String description; + + public MsAssertionJsonPath() { + setType(MsAssertionType.JSON_PATH); + } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java new file mode 100644 index 0000000000..bbd1f465b3 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java @@ -0,0 +1,22 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionRegex extends MsAssertionType { + private String subject; + private String expression; + private String description; + private boolean assumeSuccess; + + public MsAssertionRegex() { + setType(MsAssertionType.REGEX); + } + + public boolean isValid() { + return StringUtils.isNotBlank(subject) && StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java new file mode 100644 index 0000000000..b77df8f7e8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; + +@Data +public class MsAssertionType { + public final static String REGEX = "Regex"; + public final static String DURATION = "Duration"; + public final static String JSON_PATH = "JSONPath"; + public final static String JSR223 = "JSR223"; + public final static String TEXT = "Text"; + public final static String XPATH2 = "XPath2"; + + private String type; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java new file mode 100644 index 0000000000..6ed27c3287 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java @@ -0,0 +1,19 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionXPath2 extends MsAssertionType { + private String expression; + + public MsAssertionXPath2() { + setType(MsAssertionType.XPATH2); + } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java new file mode 100644 index 0000000000..d266d7e0a5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java @@ -0,0 +1,130 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.assertions.*; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "Assertions") +public class MsAssertions extends MsTestElement { + private List regex; + private List jsonPath; + private List jsr223; + private List xpath2; + private MsAssertionDuration duration; + private String type = "Assertions"; + + public void toHashTree(HashTree tree, List hashTree) { + addAssertions(tree); + + } + private void addAssertions(HashTree hashTree) { + if (CollectionUtils.isNotEmpty(this.getRegex())) { + this.getRegex().stream().filter(MsAssertionRegex::isValid).forEach(assertion -> + hashTree.add(responseAssertion(assertion)) + ); + } + + if (CollectionUtils.isNotEmpty(this.getJsonPath())) { + this.getJsonPath().stream().filter(MsAssertionJsonPath::isValid).forEach(assertion -> + hashTree.add(jsonPathAssertion(assertion)) + ); + } + + if (CollectionUtils.isNotEmpty(this.getXpath2())) { + this.getXpath2().stream().filter(MsAssertionXPath2::isValid).forEach(assertion -> + hashTree.add(xPath2Assertion(assertion)) + ); + } + + if (CollectionUtils.isNotEmpty(this.getJsr223())) { + this.getJsr223().stream().filter(MsAssertionJSR223::isValid).forEach(assertion -> + hashTree.add(jsr223Assertion(assertion)) + ); + } + + if (this.getDuration().isValid()) { + hashTree.add(durationAssertion(this.getDuration())); + } + } + + private ResponseAssertion responseAssertion(MsAssertionRegex assertionRegex) { + ResponseAssertion assertion = new ResponseAssertion(); + assertion.setEnabled(true); + assertion.setName(assertionRegex.getDescription()); + assertion.setProperty(TestElement.TEST_CLASS, ResponseAssertion.class.getName()); + assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AssertionGui")); + assertion.setAssumeSuccess(assertionRegex.isAssumeSuccess()); + assertion.setToContainsType(); + switch (assertionRegex.getSubject()) { + case "Response Code": + assertion.setTestFieldResponseCode(); + break; + case "Response Headers": + assertion.setTestFieldResponseHeaders(); + break; + case "Response Data": + assertion.setTestFieldResponseData(); + break; + } + return assertion; + } + + private JSONPathAssertion jsonPathAssertion(MsAssertionJsonPath assertionJsonPath) { + JSONPathAssertion assertion = new JSONPathAssertion(); + assertion.setEnabled(true); + assertion.setName(assertionJsonPath.getDescription()); + assertion.setProperty(TestElement.TEST_CLASS, JSONPathAssertion.class.getName()); + assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("JSONPathAssertionGui")); + assertion.setJsonPath(assertionJsonPath.getExpression()); + assertion.setExpectedValue(assertionJsonPath.getExpect()); + assertion.setJsonValidationBool(true); + assertion.setExpectNull(false); + assertion.setInvert(false); + assertion.setIsRegex(true); + return assertion; + } + + private XPath2Assertion xPath2Assertion(MsAssertionXPath2 assertionXPath2) { + XPath2Assertion assertion = new XPath2Assertion(); + assertion.setEnabled(true); + assertion.setName(assertionXPath2.getExpression()); + assertion.setProperty(TestElement.TEST_CLASS, XPath2Assertion.class.getName()); + assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("XPath2AssertionGui")); + assertion.setXPathString(assertionXPath2.getExpression()); + assertion.setNegated(false); + return assertion; + } + + private DurationAssertion durationAssertion(MsAssertionDuration assertionDuration) { + DurationAssertion assertion = new DurationAssertion(); + assertion.setEnabled(true); + assertion.setName("Response In Time: " + assertionDuration.getValue()); + assertion.setProperty(TestElement.TEST_CLASS, DurationAssertion.class.getName()); + assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DurationAssertionGui")); + assertion.setAllowedDuration(assertionDuration.getValue()); + return assertion; + } + + private JSR223Assertion jsr223Assertion(MsAssertionJSR223 assertionJSR223) { + JSR223Assertion assertion = new JSR223Assertion(); + assertion.setEnabled(true); + assertion.setName(assertionJSR223.getDesc()); + assertion.setProperty(TestElement.TEST_CLASS, JSR223Assertion.class.getName()); + assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + assertion.setProperty("cacheKey", "true"); + assertion.setProperty("scriptLanguage", assertionJSR223.getLanguage()); + assertion.setProperty("script", assertionJSR223.getScript()); + return assertion; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java new file mode 100644 index 0000000000..d82d2519d6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/auth/MsAuthManager.java @@ -0,0 +1,77 @@ +package io.metersphere.api.dto.definition.request.auth; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; +import io.metersphere.api.service.ApiTestEnvironmentService; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; +import io.metersphere.commons.utils.CommonBeanFactory; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.jmeter.protocol.http.control.AuthManager; +import org.apache.jmeter.protocol.http.control.Authorization; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "AuthManager") +public class MsAuthManager extends MsTestElement { + private String type = "AuthManager"; + @JSONField(ordinal = 10) + private String username; + + @JSONField(ordinal = 11) + private String password; + + @JSONField(ordinal = 12) + private String url; + + @JSONField(ordinal = 13) + private String realm; + + @JSONField(ordinal = 14) + private String verification; + + @JSONField(ordinal = 15) + private String mechanism; + + @JSONField(ordinal = 16) + private String encrypt; + + @JSONField(ordinal = 17) + private String domain; + + @JSONField(ordinal = 18) + private String environment; + + public void toHashTree(HashTree tree, List hashTree) { + AuthManager authManager = new AuthManager(); + authManager.setEnabled(true); + authManager.setName(this.getUsername() + "AuthManager"); + authManager.setProperty(TestElement.TEST_CLASS, AuthManager.class.getName()); + authManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AuthPanel")); + Authorization auth = new Authorization(); + if (this.url != null) { + auth.setURL(this.url); + } else { + if (environment != null) { + ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + ApiTestEnvironmentWithBLOBs environmentWithBLOBs = environmentService.get(environment); + EnvironmentConfig config = JSONObject.parseObject(environmentWithBLOBs.getConfig(), EnvironmentConfig.class); + this.url = config.getHttpConfig().getProtocol() + "://" + config.getHttpConfig().getSocket(); + } + } + auth.setDomain(this.domain); + auth.setUser(this.username); + auth.setPass(this.password); + auth.setMechanism(AuthManager.Mechanism.DIGEST); + authManager.addAuth(auth); + tree.add(authManager); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java new file mode 100644 index 0000000000..3c56b89316 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/configurations/MsHeaderManager.java @@ -0,0 +1,43 @@ +package io.metersphere.api.dto.definition.request.configurations; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.KeyValue; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.protocol.http.control.Header; +import org.apache.jmeter.protocol.http.control.HeaderManager; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "HeaderManager") +public class MsHeaderManager extends MsTestElement { + + private String type = "HeaderManager"; + @JSONField(ordinal = 10) + private List headers; + + public void toHashTree(HashTree tree, List hashTree) { + HeaderManager headerManager = new HeaderManager(); + headerManager.setEnabled(true); + headerManager.setName(this.getName() + "Headers"); + headerManager.setProperty(TestElement.TEST_CLASS, HeaderManager.class.getName()); + headerManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HeaderPanel")); + headers.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> + headerManager.add(new Header(keyValue.getName(), keyValue.getValue())) + ); + final HashTree headersTree = tree.add(headerManager); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(headersTree, el.getHashTree()); + }); + } + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java new file mode 100644 index 0000000000..696217eeb6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/dns/MsDNSCacheManager.java @@ -0,0 +1,78 @@ +package io.metersphere.api.dto.definition.request.dns; + +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; +import io.metersphere.api.dto.scenario.environment.Host; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.protocol.http.control.DNSCacheManager; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.ArrayList; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "DNSCacheManager") +public class MsDNSCacheManager extends MsTestElement { + + public void toHashTree(HashTree tree, List hashTree) { + for (MsTestElement el : hashTree) { + el.toHashTree(tree, el.getHashTree()); + } + } + + public static void addEnvironmentVariables(HashTree samplerHashTree, String name, EnvironmentConfig config) { + name += "Environment Variables"; + samplerHashTree.add(arguments(name, config.getCommonConfig().getVariables())); + } + + public static void addEnvironmentDNS(HashTree samplerHashTree, String name, EnvironmentConfig config) { + if (config.getCommonConfig().isEnableHost() && CollectionUtils.isNotEmpty(config.getCommonConfig().getHosts())) { + String domain = config.getHttpConfig().getDomain().trim(); + List hosts = new ArrayList<>(); + config.getCommonConfig().getHosts().forEach(host -> { + if (StringUtils.isNotBlank(host.getDomain())) { + String hostDomain = host.getDomain().trim().replace("http://", "").replace("https://", ""); + if (StringUtils.equals(hostDomain, domain)) { + host.setDomain(hostDomain); // 域名去掉协议 + hosts.add(host); + } + } + }); + samplerHashTree.add(dnsCacheManager(name + " DNSCacheManager", hosts)); + } + } + + private static Arguments arguments(String name, List variables) { + Arguments arguments = new Arguments(); + arguments.setEnabled(true); + arguments.setName(name); + arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName()); + arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel")); + variables.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> + arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") + ); + return arguments; + } + + private static DNSCacheManager dnsCacheManager(String name, List hosts) { + DNSCacheManager dnsCacheManager = new DNSCacheManager(); + dnsCacheManager.setEnabled(true); + dnsCacheManager.setName(name); + dnsCacheManager.setProperty(TestElement.TEST_CLASS, DNSCacheManager.class.getName()); + dnsCacheManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DNSCachePanel")); + dnsCacheManager.setCustomResolver(true); + hosts.forEach(host -> dnsCacheManager.addHost(host.getDomain(), host.getIp())); + + return dnsCacheManager; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java new file mode 100644 index 0000000000..e44152b641 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtract.java @@ -0,0 +1,91 @@ +package io.metersphere.api.dto.definition.request.extract; + +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.extractor.RegexExtractor; +import org.apache.jmeter.extractor.XPath2Extractor; +import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "Extract") +public class MsExtract extends MsTestElement { + private List regex; + private List json; + private List xpath; + private String type = "Extract"; + + public void toHashTree(HashTree tree, List hashTree) { + addRequestExtractors(tree); + } + + private void addRequestExtractors(HashTree samplerHashTree) { + if (CollectionUtils.isNotEmpty(this.getRegex())) { + this.getRegex().stream().filter(MsExtractRegex::isValid).forEach(extractRegex -> + samplerHashTree.add(regexExtractor(extractRegex)) + ); + } + if (CollectionUtils.isNotEmpty(this.getXpath())) { + this.getXpath().stream().filter(MsExtractCommon::isValid).forEach(extractXPath -> + samplerHashTree.add(xPath2Extractor(extractXPath)) + ); + } + if (CollectionUtils.isNotEmpty(this.getJson())) { + this.getJson().stream().filter(MsExtractCommon::isValid).forEach(extractJSONPath -> + samplerHashTree.add(jsonPostProcessor(extractJSONPath)) + ); + } + } + + private RegexExtractor regexExtractor(MsExtractRegex extractRegex) { + RegexExtractor extractor = new RegexExtractor(); + extractor.setEnabled(true); + extractor.setName(extractRegex.getVariable() + " RegexExtractor"); + extractor.setProperty(TestElement.TEST_CLASS, RegexExtractor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("RegexExtractorGui")); + extractor.setRefName(extractRegex.getVariable()); + extractor.setRegex(extractRegex.getExpression()); + extractor.setUseField(extractRegex.getUseHeaders()); + if (extractRegex.isMultipleMatching()) { + extractor.setMatchNumber(-1); + } + extractor.setTemplate("$1$"); + return extractor; + } + + private XPath2Extractor xPath2Extractor(MsExtractXPath extractXPath) { + XPath2Extractor extractor = new XPath2Extractor(); + extractor.setEnabled(true); + extractor.setName(extractXPath.getVariable() + " XPath2Extractor"); + extractor.setProperty(TestElement.TEST_CLASS, XPath2Extractor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("XPath2ExtractorGui")); + extractor.setRefName(extractXPath.getVariable()); + extractor.setXPathQuery(extractXPath.getExpression()); + if (extractXPath.isMultipleMatching()) { + extractor.setMatchNumber(-1); + } + return extractor; + } + + private JSONPostProcessor jsonPostProcessor(MsExtractJSONPath extractJSONPath) { + JSONPostProcessor extractor = new JSONPostProcessor(); + extractor.setEnabled(true); + extractor.setName(extractJSONPath.getVariable() + " JSONExtractor"); + extractor.setProperty(TestElement.TEST_CLASS, JSONPostProcessor.class.getName()); + extractor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("JSONPostProcessorGui")); + extractor.setRefNames(extractJSONPath.getVariable()); + extractor.setJsonPathExpressions(extractJSONPath.getExpression()); + if (extractJSONPath.isMultipleMatching()) { + extractor.setMatchNumbers("-1"); + } + return extractor; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractCommon.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractCommon.java new file mode 100644 index 0000000000..3ac9f8a662 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractCommon.java @@ -0,0 +1,19 @@ +package io.metersphere.api.dto.definition.request.extract; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsExtractCommon extends MsExtractType{ + private String variable; + private String value; // value: ${variable} + private String expression; + private String description; + private boolean multipleMatching; + + public boolean isValid() { + return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractJSONPath.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractJSONPath.java new file mode 100644 index 0000000000..133159f5a0 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractJSONPath.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition.request.extract; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsExtractJSONPath extends MsExtractCommon { + public MsExtractJSONPath() { + setType(MsExtractType.JSON_PATH); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractRegex.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractRegex.java new file mode 100644 index 0000000000..bb5782d066 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractRegex.java @@ -0,0 +1,14 @@ +package io.metersphere.api.dto.definition.request.extract; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsExtractRegex extends MsExtractCommon { + private String useHeaders; + + public MsExtractRegex() { + setType(MsExtractType.REGEX); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractType.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractType.java new file mode 100644 index 0000000000..5a54e89de2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractType.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition.request.extract; + +import lombok.Data; + +@Data +public class MsExtractType { + public final static String REGEX = "Regex"; + public final static String JSON_PATH = "JSONPath"; + public final static String XPATH = "XPath"; + + private String type; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractXPath.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractXPath.java new file mode 100644 index 0000000000..0110037690 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/extract/MsExtractXPath.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.definition.request.extract; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsExtractXPath extends MsExtractCommon { + public MsExtractXPath() { + setType(MsExtractType.XPATH); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java new file mode 100644 index 0000000000..47321b3e6b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/post/MsJSR223PostProcessor.java @@ -0,0 +1,47 @@ +package io.metersphere.api.dto.definition.request.processors.post; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.extractor.JSR223PostProcessor; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "JSR223PostProcessor") +public class MsJSR223PostProcessor extends MsTestElement { + private String type = "JSR223PostProcessor"; + + @JSONField(ordinal = 10) + private String script; + + @JSONField(ordinal = 11) + private String scriptLanguage; + + + public void toHashTree(HashTree tree, List hashTree) { + JSR223PostProcessor processor = new JSR223PostProcessor(); + processor.setEnabled(true); + processor.setName(this.getName() + "JSR223PostProcessor"); + processor.setProperty(TestElement.TEST_CLASS, JSR223PostProcessor.class.getName()); + processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + processor.setProperty("cacheKey", "true"); + processor.setProperty("scriptLanguage", this.getScriptLanguage()); + processor.setProperty("script", this.getScript()); + + final HashTree jsr223PostTree = tree.add(processor); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(jsr223PostTree, el.getHashTree()); + }); + } + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java new file mode 100644 index 0000000000..e6102392b6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/processors/pre/MsJSR223PreProcessor.java @@ -0,0 +1,46 @@ +package io.metersphere.api.dto.definition.request.processors.pre; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.modifiers.JSR223PreProcessor; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "JSR223PreProcessor") +public class MsJSR223PreProcessor extends MsTestElement { + private String type = "JSR223PreProcessor"; + + @JSONField(ordinal = 10) + private String script; + + @JSONField(ordinal = 11) + private String scriptLanguage; + + public void toHashTree(HashTree tree, List hashTree) { + JSR223PreProcessor processor = new JSR223PreProcessor(); + processor.setEnabled(true); + processor.setName(this.getName() + "JSR223PreProcessor"); + processor.setProperty(TestElement.TEST_CLASS, JSR223PreProcessor.class.getName()); + processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + processor.setProperty("cacheKey", "true"); + processor.setProperty("scriptLanguage", this.getScriptLanguage()); + processor.setProperty("script", this.getScript()); + + final HashTree jsr223PreTree = tree.add(processor); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(jsr223PreTree, el.getHashTree()); + }); + } + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java new file mode 100644 index 0000000000..600833239a --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsDubboSampler.java @@ -0,0 +1,143 @@ +package io.metersphere.api.dto.definition.request.sampler; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample; +import io.github.ningyu.jmeter.plugin.dubbo.sample.MethodArgument; +import io.github.ningyu.jmeter.plugin.util.Constants; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.sampler.dubbo.MsConfigCenter; +import io.metersphere.api.dto.definition.request.sampler.dubbo.MsConsumerAndService; +import io.metersphere.api.dto.definition.request.sampler.dubbo.MsRegistryCenter; +import io.metersphere.api.dto.scenario.KeyValue; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; +import org.apache.jorphan.collections.ListedHashTree; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "DubboSampler") +public class MsDubboSampler extends MsTestElement { + // type 必须放最前面,以便能够转换正确的类 + private String type = "DubboSampler"; + + @JSONField(ordinal = 52) + private String protocol; + @JsonProperty(value = "interface") + @JSONField(ordinal = 53, name = "interface") + private String _interface; + @JSONField(ordinal = 54) + private String method; + + @JSONField(ordinal = 55) + private MsConfigCenter configCenter; + @JSONField(ordinal = 56) + private MsRegistryCenter registryCenter; + @JSONField(ordinal = 57) + private MsConsumerAndService consumerAndService; + + @JSONField(ordinal = 58) + private List args; + @JSONField(ordinal = 59) + private List attachmentArgs; + + public void toHashTree(HashTree tree, List hashTree) { + final HashTree testPlanTree = new ListedHashTree(); + testPlanTree.add(dubboConfig()); + tree.set(dubboSample(), testPlanTree); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(testPlanTree, el.getHashTree()); + }); + } + } + + private DubboSample dubboSample() { + DubboSample sampler = new DubboSample(); + sampler.setName(this.getName()); + sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName()); + sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboSampleGui")); + + sampler.addTestElement(configCenter(this.getConfigCenter())); + sampler.addTestElement(registryCenter(this.getRegistryCenter())); + sampler.addTestElement(consumerAndService(this.getConsumerAndService())); + + Constants.setRpcProtocol(this.getProtocol(), sampler); + Constants.setInterfaceName(this.get_interface(), sampler); + Constants.setMethod(this.getMethod(), sampler); + + List methodArgs = this.getArgs().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable) + .map(keyValue -> new MethodArgument(keyValue.getName(), keyValue.getValue())).collect(Collectors.toList()); + Constants.setMethodArgs(methodArgs, sampler); + + List attachmentArgs = this.getAttachmentArgs().stream().filter(KeyValue::isValid).filter(KeyValue::isEnable) + .map(keyValue -> new MethodArgument(keyValue.getName(), keyValue.getValue())).collect(Collectors.toList()); + Constants.setAttachmentArgs(attachmentArgs, sampler); + + return sampler; + } + + + private ConfigTestElement dubboConfig() { + ConfigTestElement configTestElement = new ConfigTestElement(); + configTestElement.setEnabled(true); + configTestElement.setName(this.getName()); + configTestElement.setProperty(TestElement.TEST_CLASS, ConfigTestElement.class.getName()); + configTestElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboDefaultConfigGui")); + configTestElement.addConfigElement(configCenter(this.getConfigCenter())); + configTestElement.addConfigElement(registryCenter(this.getRegistryCenter())); + configTestElement.addConfigElement(consumerAndService(this.getConsumerAndService())); + return configTestElement; + } + + private ConfigTestElement configCenter(MsConfigCenter configCenter) { + ConfigTestElement configTestElement = new ConfigTestElement(); + if (configCenter != null) { + Constants.setConfigCenterProtocol(configCenter.getProtocol(), configTestElement); + Constants.setConfigCenterGroup(configCenter.getGroup(), configTestElement); + Constants.setConfigCenterNamespace(configCenter.getNamespace(), configTestElement); + Constants.setConfigCenterUserName(configCenter.getUsername(), configTestElement); + Constants.setConfigCenterPassword(configCenter.getPassword(), configTestElement); + Constants.setConfigCenterAddress(configCenter.getAddress(), configTestElement); + Constants.setConfigCenterTimeout(configCenter.getTimeout(), configTestElement); + } + return configTestElement; + } + + private ConfigTestElement registryCenter(MsRegistryCenter registryCenter) { + ConfigTestElement configTestElement = new ConfigTestElement(); + if (registryCenter != null) { + Constants.setRegistryProtocol(registryCenter.getProtocol(), configTestElement); + Constants.setRegistryGroup(registryCenter.getGroup(), configTestElement); + Constants.setRegistryUserName(registryCenter.getUsername(), configTestElement); + Constants.setRegistryPassword(registryCenter.getPassword(), configTestElement); + Constants.setRegistryTimeout(registryCenter.getTimeout(), configTestElement); + Constants.setAddress(registryCenter.getAddress(), configTestElement); + } + return configTestElement; + } + + private ConfigTestElement consumerAndService(MsConsumerAndService consumerAndService) { + ConfigTestElement configTestElement = new ConfigTestElement(); + if (consumerAndService != null) { + Constants.setTimeout(consumerAndService.getTimeout(), configTestElement); + Constants.setVersion(consumerAndService.getVersion(), configTestElement); + Constants.setGroup(consumerAndService.getGroup(), configTestElement); + Constants.setConnections(consumerAndService.getConnections(), configTestElement); + Constants.setLoadbalance(consumerAndService.getLoadBalance(), configTestElement); + Constants.setAsync(consumerAndService.getAsync(), configTestElement); + Constants.setCluster(consumerAndService.getCluster(), configTestElement); + } + return configTestElement; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java new file mode 100644 index 0000000000..99e90af344 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -0,0 +1,250 @@ +package io.metersphere.api.dto.definition.request.sampler; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; +import io.metersphere.api.dto.scenario.request.BodyFile; +import io.metersphere.api.service.ApiTestEnvironmentService; +import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; +import org.apache.jmeter.protocol.http.util.HTTPArgument; +import org.apache.jmeter.protocol.http.util.HTTPFileArg; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "HTTPSamplerProxy") +public class MsHTTPSamplerProxy extends MsTestElement { + private String type = "HTTPSamplerProxy"; + + @JSONField(ordinal = 10) + private String protocol; + + @JSONField(ordinal = 11) + private String domain; + + @JSONField(ordinal = 12) + private String port; + + @JSONField(ordinal = 13) + private String method; + + @JSONField(ordinal = 14) + private String path; + + @JSONField(ordinal = 15) + private String connectTimeout; + @JSONField(ordinal = 16) + + private String responseTimeout; + @JSONField(ordinal = 17) + + private List arguments; + + @JSONField(ordinal = 18) + private Body body; + + @JSONField(ordinal = 19) + private List rest; + + @JSONField(ordinal = 20) + private String url; + + @JSONField(ordinal = 21) + private boolean followRedirects; + + @JSONField(ordinal = 22) + private boolean doMultipartPost; + + @JSONField(ordinal = 23) + private String useEnvironment; + + + public void toHashTree(HashTree tree, List hashTree) { + HTTPSamplerProxy sampler = new HTTPSamplerProxy(); + sampler.setEnabled(true); + sampler.setName(this.getName()); + sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName()); + sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui")); + sampler.setMethod(this.getMethod()); + sampler.setContentEncoding("UTF-8"); + sampler.setConnectTimeout(this.getConnectTimeout() == null ? "6000" : this.getConnectTimeout()); + sampler.setResponseTimeout(this.getResponseTimeout() == null ? "6000" : this.getResponseTimeout()); + sampler.setFollowRedirects(this.isFollowRedirects()); + sampler.setUseKeepAlive(true); + sampler.setDoMultipart(this.isDoMultipartPost()); + EnvironmentConfig config = null; + if (useEnvironment != null) { + ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); + ApiTestEnvironmentWithBLOBs environment = environmentService.get(useEnvironment); + config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); + } + try { + if (config != null) { + String url = ""; + sampler.setDomain(config.getHttpConfig().getDomain()); + sampler.setPort(config.getHttpConfig().getPort()); + sampler.setProtocol(config.getHttpConfig().getProtocol()); + url = config.getHttpConfig().getProtocol() + "://" + config.getHttpConfig().getSocket(); + URL urlObject = new URL(url); + String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath(); + if (StringUtils.isNotBlank(this.getPath())) { + envPath += this.getPath(); + } + if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) { + sampler.setPath(getRestParameters(URLDecoder.decode(envPath, "UTF-8"))); + } else { + sampler.setPath(getPostQueryParameters(URLDecoder.decode(envPath, "UTF-8"))); + } + } else { + String url = this.getUrl(); + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "http://" + url; + } + URL urlObject = new URL(url); + sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8")); + sampler.setPort(urlObject.getPort()); + sampler.setProtocol(urlObject.getProtocol()); + + sampler.setPath(getRestParameters(URLDecoder.decode(urlObject.getPath(), "UTF-8"))); + sampler.setPath(getPostQueryParameters(URLDecoder.decode(urlObject.getPath(), "UTF-8"))); + } + } catch (Exception e) { + LogUtil.error(e); + } + + // 请求参数 + if (CollectionUtils.isNotEmpty(this.getArguments())) { + sampler.setArguments(httpArguments(this.getArguments())); + } + // 请求体 + if (!StringUtils.equals(this.getMethod(), "GET")) { + List body = new ArrayList<>(); + if (this.getBody().isKV() || this.getBody().isBinary()) { + body = this.getBody().getKvs().stream().filter(KeyValue::isValid).collect(Collectors.toList()); + HTTPFileArg[] httpFileArgs = httpFileArgs(); + // 文件上传 + if (httpFileArgs.length > 0) { + sampler.setHTTPFiles(httpFileArgs()); + sampler.setDoMultipart(true); + } + } else if (this.getBody().isJson()) { + KeyValue keyValue = new KeyValue("", JSON.toJSONString(this.getBody().getJson())); + keyValue.setEnable(true); + keyValue.setEncode(false); + body.add(keyValue); + } else { + if (StringUtils.isNotBlank(this.getBody().getRaw())) { + sampler.setPostBodyRaw(true); + KeyValue keyValue = new KeyValue("", this.getBody().getRaw()); + keyValue.setEnable(true); + keyValue.setEncode(false); + body.add(keyValue); + } + if (StringUtils.isNotBlank(this.getBody().getXml())) { + sampler.setPostBodyRaw(true); + KeyValue keyValue = new KeyValue("", this.getBody().getXml()); + keyValue.setEnable(true); + keyValue.setEncode(false); + body.add(keyValue); + } + } + sampler.setArguments(httpArguments(body)); + } + + final HashTree httpSamplerTree = tree.add(sampler); + + //判断是否要开启DNS + if (config != null && config.getCommonConfig() != null && config.getCommonConfig().isEnableHost()) { + MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config); + MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config); + } + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(httpSamplerTree, el.getHashTree()); + }); + } + } + + private String getRestParameters(String path) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(path); + stringBuffer.append("/"); + this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).forEach(keyValue -> + stringBuffer.append(keyValue.getValue()).append("/") + ); + return stringBuffer.substring(0, stringBuffer.length() - 1); + } + + private String getPostQueryParameters(String path) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(path); + stringBuffer.append("?"); + this.getArguments().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).forEach(keyValue -> + stringBuffer.append(keyValue.getName()).append("=").append(keyValue.getValue()).append("&") + ); + return stringBuffer.substring(0, stringBuffer.length() - 1); + } + + private Arguments httpArguments(List list) { + Arguments arguments = new Arguments(); + list.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> { + HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), keyValue.getValue()); + httpArgument.setAlwaysEncoded(keyValue.isEncode()); + if (StringUtils.isNotBlank(keyValue.getContentType())) { + httpArgument.setContentType(keyValue.getContentType()); + } + arguments.addArgument(httpArgument); + } + ); + return arguments; + } + + private void setFileArg(List list, List files, KeyValue keyValue) { + final String BODY_FILE_DIR = "/opt/metersphere/data/body"; + if (files != null) { + files.forEach(file -> { + String paramName = keyValue.getName() == null ? this.getId() : keyValue.getName(); + String path = BODY_FILE_DIR + '/' + file.getId() + '_' + file.getName(); + String mimetype = keyValue.getContentType(); + list.add(new HTTPFileArg(path, paramName, mimetype)); + }); + } + } + + private HTTPFileArg[] httpFileArgs() { + List list = new ArrayList<>(); + this.getBody().getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { + setFileArg(list, keyValue.getFiles(), keyValue); + }); + this.getBody().getBinary().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> { + setFileArg(list, keyValue.getFiles(), keyValue); + }); + return list.toArray(new HTTPFileArg[0]); + } + + private boolean isRest() { + return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java new file mode 100644 index 0000000000..c37cc2a3c0 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsJDBCSampler.java @@ -0,0 +1,103 @@ +package io.metersphere.api.dto.definition.request.sampler; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.scenario.DatabaseConfig; +import io.metersphere.api.dto.scenario.KeyValue; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.protocol.jdbc.config.DataSourceElement; +import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "JDBCSampler") +public class MsJDBCSampler extends MsTestElement { + // type 必须放最前面,以便能够转换正确的类 + private String type = "JDBCSampler"; + @JSONField(ordinal = 10) + private DatabaseConfig dataSource; + @JSONField(ordinal = 11) + private String query; + @JSONField(ordinal = 12) + private long queryTimeout; + @JSONField(ordinal = 13) + private String resultVariable; + @JSONField(ordinal = 14) + private String variableNames; + @JSONField(ordinal = 15) + private List variables; + @JSONField(ordinal = 16) + private String environmentId; + + public void toHashTree(HashTree tree, List hashTree) { + final HashTree samplerHashTree = tree.add(jdbcSampler()); + tree.add(jdbcDataSource()); + tree.add(arguments(this.getName() + " Variables", this.getVariables())); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(samplerHashTree, el.getHashTree()); + }); + } + } + + private Arguments arguments(String name, List variables) { + Arguments arguments = new Arguments(); + if (!variables.isEmpty()) { + arguments.setEnabled(true); + arguments.setName(name); + arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName()); + arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel")); + variables.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> + arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") + ); + } + return arguments; + } + + private JDBCSampler jdbcSampler() { + JDBCSampler sampler = new JDBCSampler(); + sampler.setName(this.getName()); + sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName()); + sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + // request.getDataSource() 是ID,需要转换为Name + sampler.setProperty("dataSource", this.dataSource.getName()); + sampler.setProperty("query", this.getQuery()); + sampler.setProperty("queryTimeout", String.valueOf(this.getQueryTimeout())); + sampler.setProperty("resultVariable", this.getResultVariable()); + sampler.setProperty("variableNames", this.getVariableNames()); + sampler.setProperty("resultSetHandler", "Store as String"); + sampler.setProperty("queryType", "Callable Statement"); + return sampler; + } + + private DataSourceElement jdbcDataSource() { + DataSourceElement dataSourceElement = new DataSourceElement(); + dataSourceElement.setEnabled(true); + dataSourceElement.setName(this.getName() + " JDBCDataSource"); + dataSourceElement.setProperty(TestElement.TEST_CLASS, DataSourceElement.class.getName()); + dataSourceElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); + dataSourceElement.setProperty("autocommit", true); + dataSourceElement.setProperty("keepAlive", true); + dataSourceElement.setProperty("preinit", false); + dataSourceElement.setProperty("dataSource", dataSource.getName()); + dataSourceElement.setProperty("dbUrl", dataSource.getDbUrl()); + dataSourceElement.setProperty("driver", dataSource.getDriver()); + dataSourceElement.setProperty("username", dataSource.getUsername()); + dataSourceElement.setProperty("password", dataSource.getPassword()); + dataSourceElement.setProperty("poolMax", dataSource.getPoolMax()); + dataSourceElement.setProperty("timeout", String.valueOf(dataSource.getTimeout())); + dataSourceElement.setProperty("connectionAge", 5000); + dataSourceElement.setProperty("trimInterval", 6000); + dataSourceElement.setProperty("transactionIsolation", "DEFAULT"); + return dataSourceElement; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java new file mode 100644 index 0000000000..53bde7df7b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsTCPSampler.java @@ -0,0 +1,104 @@ +package io.metersphere.api.dto.definition.request.sampler; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.collections.CollectionUtils; +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.protocol.tcp.sampler.TCPSampler; +import org.apache.jmeter.save.SaveService; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jorphan.collections.HashTree; +import org.apache.jorphan.collections.ListedHashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "TCPSampler") +public class MsTCPSampler extends MsTestElement { + @JSONField(ordinal = 10) + private String type = "TCPSampler"; + @JSONField(ordinal = 11) + private String classname = ""; + @JSONField(ordinal = 12) + private String server = ""; + @JSONField(ordinal = 13) + private String port = ""; + @JSONField(ordinal = 14) + private String ctimeout = ""; + @JSONField(ordinal = 15) + private String timeout = ""; + @JSONField(ordinal = 16) + private boolean reUseConnection = true; + @JSONField(ordinal = 17) + private boolean nodelay; + @JSONField(ordinal = 18) + private boolean closeConnection; + @JSONField(ordinal = 19) + private String soLinger = ""; + @JSONField(ordinal = 20) + private String eolByte = ""; + @JSONField(ordinal = 21) + private String username = ""; + @JSONField(ordinal = 22) + private String password = ""; + @JSONField(ordinal = 23) + private String request; + + public void toHashTree(HashTree tree, List hashTree) { + final HashTree samplerHashTree = new ListedHashTree(); + samplerHashTree.add(tcpConfig()); + tree.set(tcpSampler(), samplerHashTree); + if (CollectionUtils.isNotEmpty(hashTree)) { + hashTree.forEach(el -> { + el.toHashTree(samplerHashTree, el.getHashTree()); + }); + } + } + + private TCPSampler tcpSampler() { + TCPSampler tcpSampler = new TCPSampler(); + tcpSampler.setName(this.getName()); + tcpSampler.setProperty(TestElement.TEST_CLASS, TCPSampler.class.getName()); + tcpSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TCPSamplerGui")); + tcpSampler.setClassname(this.getClassname()); + tcpSampler.setServer(this.getServer()); + tcpSampler.setPort(this.getPort()); + tcpSampler.setConnectTimeout(this.getCtimeout()); + tcpSampler.setProperty(TCPSampler.RE_USE_CONNECTION, this.isReUseConnection()); + tcpSampler.setProperty(TCPSampler.NODELAY, this.isNodelay()); + tcpSampler.setCloseConnection(String.valueOf(this.isCloseConnection())); + tcpSampler.setSoLinger(this.getSoLinger()); + tcpSampler.setEolByte(this.getEolByte()); + tcpSampler.setRequestData(this.getRequest()); + tcpSampler.setProperty(ConfigTestElement.USERNAME, this.getUsername()); + tcpSampler.setProperty(ConfigTestElement.PASSWORD, this.getPassword()); + + return tcpSampler; + } + + private ConfigTestElement tcpConfig() { + ConfigTestElement configTestElement = new ConfigTestElement(); + configTestElement.setEnabled(true); + configTestElement.setName(this.getName()); + configTestElement.setProperty(TestElement.TEST_CLASS, ConfigTestElement.class.getName()); + configTestElement.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TCPConfigGui")); + configTestElement.setProperty(TCPSampler.CLASSNAME, this.getClassname()); + configTestElement.setProperty(TCPSampler.SERVER, this.getServer()); + configTestElement.setProperty(TCPSampler.PORT, this.getPort()); + configTestElement.setProperty(TCPSampler.TIMEOUT_CONNECT, this.getCtimeout()); + configTestElement.setProperty(TCPSampler.RE_USE_CONNECTION, this.isReUseConnection()); + configTestElement.setProperty(TCPSampler.NODELAY, this.isNodelay()); + configTestElement.setProperty(TCPSampler.CLOSE_CONNECTION, this.isCloseConnection()); + configTestElement.setProperty(TCPSampler.SO_LINGER, this.getSoLinger()); + configTestElement.setProperty(TCPSampler.EOL_BYTE, this.getEolByte()); + configTestElement.setProperty(TCPSampler.SO_LINGER, this.getSoLinger()); + configTestElement.setProperty(ConfigTestElement.USERNAME, this.getUsername()); + configTestElement.setProperty(ConfigTestElement.PASSWORD, this.getPassword()); + return configTestElement; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsConfigCenter.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsConfigCenter.java new file mode 100644 index 0000000000..f1027b789b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsConfigCenter.java @@ -0,0 +1,14 @@ +package io.metersphere.api.dto.definition.request.sampler.dubbo; + +import lombok.Data; + +@Data +public class MsConfigCenter { + private String protocol; + private String group; + private String namespace; + private String username; + private String address; + private String password; + private String timeout; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsConsumerAndService.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsConsumerAndService.java new file mode 100644 index 0000000000..78897f843d --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsConsumerAndService.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.definition.request.sampler.dubbo; + +import lombok.Data; + +@Data +public class MsConsumerAndService { + private String timeout; + private String version; + private String retries; + private String cluster; + private String group; + private String connections; + private String async; + private String loadBalance; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsRegistryCenter.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsRegistryCenter.java new file mode 100644 index 0000000000..bfd98b49f6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/dubbo/MsRegistryCenter.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.definition.request.sampler.dubbo; + +import lombok.Data; + +@Data +public class MsRegistryCenter { + private String protocol; + private String group; + private String username; + private String address; + private String password; + private String timeout; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/response/HttpResponse.java b/backend/src/main/java/io/metersphere/api/dto/definition/response/HttpResponse.java new file mode 100644 index 0000000000..f623387b17 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/response/HttpResponse.java @@ -0,0 +1,26 @@ +package io.metersphere.api.dto.definition.response; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.KeyValue; +import io.metersphere.api.dto.scenario.request.RequestType; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = RequestType.HTTP) +public class HttpResponse extends Response { + // type 必须放最前面,以便能够转换正确的类 + private String type = RequestType.HTTP; + @JSONField(ordinal = 1) + private List headers; + @JSONField(ordinal = 2) + private List statusCode; + @JSONField(ordinal = 3) + private Body body; + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/response/Response.java b/backend/src/main/java/io/metersphere/api/dto/definition/response/Response.java new file mode 100644 index 0000000000..f0baa8cc25 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/response/Response.java @@ -0,0 +1,23 @@ +package io.metersphere.api.dto.definition.response; + +import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson.annotation.JSONType; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.metersphere.api.dto.scenario.request.RequestType; +import lombok.Data; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = HttpResponse.class, name = RequestType.HTTP), +}) +@JSONType(seeAlso = {HttpResponse.class}, typeKey = "type") +@Data +public abstract class Response { + @JSONField(ordinal = 1) + private String id; + @JSONField(ordinal = 2) + private String name; + @JSONField(ordinal = 3) + private Boolean enable; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/AuthConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/AuthConfig.java new file mode 100644 index 0000000000..a69c11b715 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/AuthConfig.java @@ -0,0 +1,11 @@ +package io.metersphere.api.dto.scenario; + +import lombok.Data; + +@Data +public class AuthConfig { + private String verification; + private String username; + private String password; + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java index 5073eeefbf..cbf9a73ea3 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/Body.java @@ -1,6 +1,7 @@ package io.metersphere.api.dto.scenario; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -9,5 +10,41 @@ public class Body { private String type; private String raw; private String format; + private List fromUrlencoded; private List kvs; + private List binary; + private Object json; + private String xml; + + private final static String KV = "KeyValue"; + private final static String FORM_DATA = "Form Data"; + private final static String RAW = "Raw"; + private final static String BINARY = "BINARY"; + private final static String JSON = "JSON"; + private final static String XML = "XML"; + + public boolean isValid() { + if (this.isKV()) { + return kvs.stream().anyMatch(KeyValue::isValid); + } else { + return StringUtils.isNotBlank(raw); + } + } + + public boolean isKV() { + return StringUtils.equals(type, KV); + } + + public boolean isBinary() { + return StringUtils.equals(type, BINARY); + } + + public boolean isJson() { + return StringUtils.equals(type, JSON); + } + + public boolean isXml() { + return StringUtils.equals(type, XML); + } + } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java new file mode 100644 index 0000000000..7a1aaf68f2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/HttpConfig.java @@ -0,0 +1,14 @@ +package io.metersphere.api.dto.scenario; + +import lombok.Data; + +import java.util.List; + +@Data +public class HttpConfig { + private String socket; + private String domain; + private String protocol = "https"; + private int port; + private List headers; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java index 45ab26da49..d141ddaa01 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/KeyValue.java @@ -2,6 +2,8 @@ package io.metersphere.api.dto.scenario; import io.metersphere.api.dto.scenario.request.BodyFile; import lombok.Data; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -14,15 +16,19 @@ public class KeyValue { private String description; private String contentType; private boolean enable; + private boolean encode = true; + private boolean required; public KeyValue() { this.enable = true; + this.required = true; } public KeyValue(String name, String value) { this.name = name; this.value = value; this.enable = true; + this.required = true; } public KeyValue(String name, String value, String description) { @@ -30,5 +36,14 @@ public class KeyValue { this.value = value; this.enable = true; this.description = description; + this.required = true; + } + + public boolean isValid() { + return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); + } + + public boolean isFile() { + return (CollectionUtils.isNotEmpty(files)) && StringUtils.equalsIgnoreCase(type, "file"); } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java b/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java index 20bc993991..8c5e2c2f4a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/Scenario.java @@ -12,7 +12,7 @@ public class Scenario { private String name; private String url; private String environmentId; - private Boolean enableCookieShare; + private boolean enableCookieShare; private List variables; private List headers; private List requests; @@ -22,4 +22,6 @@ public class Scenario { private List databaseConfigs; private Boolean enable; private Boolean referenceEnable; + private boolean enable = true; + private Boolean referenceEnable; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java index 62a34fd9a9..12e8702e9f 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/TCPConfig.java @@ -4,16 +4,16 @@ import lombok.Data; @Data public class TCPConfig { - private String classname; - private String server; - private Integer port; - private Integer ctimeout; - private Integer timeout; - private Boolean reUseConnection; - private Boolean nodelay; - private Boolean closeConnection; - private String soLinger; - private String eolByte; - private String username; - private String password; + private String classname = ""; + private String server = ""; + private String port = ""; + private String ctimeout = ""; + private String timeout = ""; + private boolean reUseConnection = true; + private boolean nodelay; + private boolean closeConnection; + private String soLinger = ""; + private String eolByte = ""; + private String username = ""; + private String password = ""; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java index af9f5ebbf1..1477f2e244 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionDuration.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -11,4 +12,8 @@ public class AssertionDuration extends AssertionType { public AssertionDuration() { setType(AssertionType.DURATION); } + + public boolean isValid() { + return value > 0; + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java index e492a46a59..46b6a39325 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJSR223.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -17,4 +18,8 @@ public class AssertionJSR223 extends AssertionType { public AssertionJSR223() { setType(AssertionType.JSR223); } + + public boolean isValid() { + return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(language); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java index c17a893559..b774725d78 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -13,4 +14,8 @@ public class AssertionJsonPath extends AssertionType { public AssertionJsonPath() { setType(AssertionType.JSON_PATH); } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java index 190a93e741..63dbc1b8a1 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionRegex.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -9,9 +10,13 @@ public class AssertionRegex extends AssertionType { private String subject; private String expression; private String description; - private Boolean assumeSuccess; + private boolean assumeSuccess; public AssertionRegex() { setType(AssertionType.REGEX); } + + public boolean isValid() { + return StringUtils.isNotBlank(subject) && StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionXPath2.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionXPath2.java index 4aa838bc41..39b29a4cf3 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionXPath2.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionXPath2.java @@ -2,12 +2,18 @@ package io.metersphere.api.dto.scenario.assertions; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data public class AssertionXPath2 extends AssertionType { private String expression; + public AssertionXPath2() { setType(AssertionType.XPATH2); } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java b/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java index e90289334b..0e7a89e69a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/controller/IfController.java @@ -1,13 +1,56 @@ package io.metersphere.api.dto.scenario.controller; import lombok.Data; +import org.apache.commons.lang3.StringUtils; @Data public class IfController { private String type; private String id; - private Boolean enable; + private boolean enable = true; private String variable; private String operator; private String value; + + public boolean isValid() { + if (StringUtils.contains(operator, "empty")) { + return StringUtils.isNotBlank(variable); + } + return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(operator) && StringUtils.isNotBlank(value); + } + + public String getLabel() { + if (isValid()) { + String label = variable + " " + operator; + if (StringUtils.isNotBlank(value)) { + label += " " + this.value; + } + return label; + } + return ""; + } + + public String getCondition() { + String variable = "\"" + this.variable + "\""; + String operator = this.operator; + String value = "\"" + this.value + "\""; + + if (StringUtils.contains(operator, "~")) { + value = "\".*" + this.value + ".*\""; + } + + if (StringUtils.equals(operator, "is empty")) { + variable = "empty(" + variable + ")"; + operator = ""; + value = ""; + } + + if (StringUtils.equals(operator, "is not empty")) { + variable = "!empty(" + variable + ")"; + operator = ""; + value = ""; + } + + return "${__jexl3(" + variable + operator + value + ")}"; + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java new file mode 100644 index 0000000000..6670bcfccc --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/CommonConfig.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.scenario.environment; + +import io.metersphere.api.dto.scenario.KeyValue; +import lombok.Data; + +import java.util.List; + +@Data +public class CommonConfig { + private List variables; + private boolean enableHost; + private List hosts; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java new file mode 100644 index 0000000000..8cf75f14d5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.scenario.environment; + +import io.metersphere.api.dto.scenario.DatabaseConfig; +import io.metersphere.api.dto.scenario.HttpConfig; +import io.metersphere.api.dto.scenario.TCPConfig; +import lombok.Data; + +import java.util.List; + +@Data +public class EnvironmentConfig { + private CommonConfig commonConfig; + private HttpConfig httpConfig; + private List databaseConfigs; + private TCPConfig tcpConfig; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java new file mode 100644 index 0000000000..d1b20e9e5a --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/environment/Host.java @@ -0,0 +1,12 @@ +package io.metersphere.api.dto.scenario.environment; + +import lombok.Data; + +@Data +public class Host { + private String ip; + private String domain; + private String status; + private String annotation; + private String uuid; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java b/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java index efd38b6a49..20d8f051a8 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/extract/ExtractCommon.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.extract; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @@ -10,5 +11,9 @@ public class ExtractCommon extends ExtractType { private String value; // value: ${variable} private String expression; private String description; - private Boolean multipleMatching; + private boolean multipleMatching; + + public boolean isValid() { + return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(expression); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java index a615b8dd98..1a7a6fa0bf 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/DubboRequest.java @@ -18,23 +18,23 @@ import java.util.List; public class DubboRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.DUBBO; - @JSONField(ordinal = 2) + @JSONField(ordinal = 52) private String protocol; @JsonProperty(value = "interface") - @JSONField(ordinal = 3, name = "interface") + @JSONField(ordinal = 53, name = "interface") private String _interface; - @JSONField(ordinal = 4) + @JSONField(ordinal = 54) private String method; - @JSONField(ordinal = 5) + @JSONField(ordinal = 55) private ConfigCenter configCenter; - @JSONField(ordinal = 6) + @JSONField(ordinal = 56) private RegistryCenter registryCenter; - @JSONField(ordinal = 7) + @JSONField(ordinal = 57) private ConsumerAndService consumerAndService; - @JSONField(ordinal = 8) + @JSONField(ordinal = 58) private List args; - @JSONField(ordinal = 9) + @JSONField(ordinal = 59) private List attachmentArgs; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java index 74b497d07c..7142ee4dc0 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java @@ -2,6 +2,7 @@ package io.metersphere.api.dto.scenario.request; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.scenario.AuthConfig; import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.KeyValue; import lombok.Data; @@ -15,26 +16,32 @@ import java.util.List; public class HttpRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.HTTP; - @JSONField(ordinal = 2) + @JSONField(ordinal = 50) private String url; - @JSONField(ordinal = 3) + @JSONField(ordinal = 51) private String method; - @JSONField(ordinal = 4) + @JSONField(ordinal = 52) private String path; - @JSONField(ordinal = 5) - private Boolean useEnvironment; - @JSONField(ordinal = 6) + @JSONField(ordinal = 53) private List parameters; - @JSONField(ordinal = 7) + @JSONField(ordinal = 54) private List headers; - @JSONField(ordinal = 8) + @JSONField(ordinal = 55) private Body body; - @JSONField(ordinal = 14) - private Long connectTimeout; - @JSONField(ordinal = 15) - private Long responseTimeout; - @JSONField(ordinal = 16) - private Boolean followRedirects; - @JSONField(ordinal = 17) - private Boolean doMultipartPost; + @JSONField(ordinal = 56) + private String connectTimeout; + @JSONField(ordinal = 57) + private String responseTimeout; + @JSONField(ordinal = 58) + private boolean followRedirects; + @JSONField(ordinal = 59) + private boolean doMultipartPost; + @JSONField(ordinal = 60) + private List rest; + @JSONField(ordinal = 61) + private AuthConfig authConfig; + // 和接口定义模块用途区分 + @JSONField(ordinal = 62) + private boolean isDefinition; + } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java index 0d8276b0fc..7d1d4eb070 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/Request.java @@ -22,22 +22,25 @@ import lombok.Data; @JSONType(seeAlso = {HttpRequest.class, DubboRequest.class, SqlRequest.class, TCPRequest.class}, typeKey = "type") @Data public abstract class Request { + private String type; @JSONField(ordinal = 1) private String id; @JSONField(ordinal = 2) private String name; @JSONField(ordinal = 3) - private Boolean enable; + private boolean enable = true; @JSONField(ordinal = 4) - private Assertions assertions; + private boolean useEnvironment; @JSONField(ordinal = 5) - private Extract extract; + private Assertions assertions; @JSONField(ordinal = 6) - private JSR223PreProcessor jsr223PreProcessor; + private Extract extract; @JSONField(ordinal = 7) - private JSR223PostProcessor jsr223PostProcessor; + private JSR223PreProcessor jsr223PreProcessor; @JSONField(ordinal = 8) - private IfController controller; + private JSR223PostProcessor jsr223PostProcessor; @JSONField(ordinal = 9) + private IfController controller; + @JSONField(ordinal = 10) private ConstantTimer timer; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java index b116326049..4fdf82c28c 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/SqlRequest.java @@ -14,20 +14,16 @@ import java.util.List; public class SqlRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.SQL; - @JSONField(ordinal = 3) + @JSONField(ordinal = 50) private String dataSource; - @JSONField(ordinal = 4) + @JSONField(ordinal = 51) private String query; - @JSONField(ordinal = 5) + @JSONField(ordinal = 52) private long queryTimeout; - @JSONField(ordinal = 6) - private Boolean useEnvironment; - @JSONField(ordinal = 7) - private Boolean followRedirects; - @JSONField(ordinal = 13) + @JSONField(ordinal = 53) private String resultVariable; - @JSONField(ordinal = 14) + @JSONField(ordinal = 54) private String variableNames; - @JSONField(ordinal = 15) + @JSONField(ordinal = 55) private List variables; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java index cce0363da4..9db65aa603 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/TCPRequest.java @@ -11,32 +11,30 @@ import lombok.EqualsAndHashCode; public class TCPRequest extends Request { // type 必须放最前面,以便能够转换正确的类 private String type = RequestType.TCP; - @JSONField(ordinal = 50) - private Boolean useEnvironment; @JSONField(ordinal = 51) - private String classname; + private String classname = ""; @JSONField(ordinal = 52) - private String server; + private String server = ""; @JSONField(ordinal = 53) - private Integer port; + private String port = ""; @JSONField(ordinal = 54) - private Integer ctimeout; + private String ctimeout = ""; @JSONField(ordinal = 55) - private Integer timeout; + private String timeout = ""; @JSONField(ordinal = 56) - private Boolean reUseConnection; + private boolean reUseConnection; @JSONField(ordinal = 57) - private Boolean nodelay; + private boolean nodelay; @JSONField(ordinal = 58) - private Boolean closeConnection; + private boolean closeConnection; @JSONField(ordinal = 59) - private String soLinger; + private String soLinger = ""; @JSONField(ordinal = 60) - private String eolByte; + private String eolByte = ""; @JSONField(ordinal = 61) - private String request; + private String request = ""; @JSONField(ordinal = 62) - private String username; + private String username = ""; @JSONField(ordinal = 63) - private String password; + private String password = ""; } diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java b/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java index 8823337b0c..fa233cf046 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/timer/ConstantTimer.java @@ -6,6 +6,6 @@ import lombok.Data; public class ConstantTimer { private String type; private String id; - private Boolean enable; + private boolean enable = true; private String delay; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java index 026b1fb1f6..440d97e4d7 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -2,6 +2,8 @@ package io.metersphere.api.jmeter; import io.metersphere.api.service.APIReportService; import io.metersphere.api.service.APITestService; +import io.metersphere.api.service.ApiDefinitionExecResultService; +import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.base.domain.ApiTestReport; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ApiRunMode; @@ -20,11 +22,14 @@ import io.metersphere.service.SystemParameterService; import io.metersphere.track.service.TestPlanTestCaseService; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.assertions.AssertionResult; +import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient; import org.apache.jmeter.visualizers.backend.BackendListenerContext; import org.springframework.http.HttpMethod; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; import java.io.Serializable; import java.util.*; @@ -51,6 +56,9 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl private MailService mailService; + private ApiDefinitionService apiDefinitionService; + private ApiDefinitionExecResultService apiDefinitionExecResultService; + public String runMode = ApiRunMode.RUN.name(); // 测试ID @@ -58,8 +66,22 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl private String debugReportId; + //获得控制台内容 + private PrintStream oldPrintStream = System.out; + private ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + private void setConsole() { + System.setOut(new PrintStream(bos)); //设置新的out + } + + private String getConsole() { + System.setOut(oldPrintStream); + return bos.toString(); + } + @Override public void setupTest(BackendListenerContext context) throws Exception { + setConsole(); setParam(context); apiTestService = CommonBeanFactory.getBean(APITestService.class); if (apiTestService == null) { @@ -82,6 +104,14 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl if (mailService == null) { LogUtil.error("mailService is required"); } + apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class); + if (apiDefinitionService == null) { + LogUtil.error("apiDefinitionService is required"); + } + apiDefinitionExecResultService = CommonBeanFactory.getBean(ApiDefinitionExecResultService.class); + if (apiDefinitionExecResultService == null) { + LogUtil.error("apiDefinitionExecResultService is required"); + } super.setupTest(context); } @@ -99,7 +129,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl // 一个脚本里可能包含多个场景(ThreadGroup),所以要区分开,key: 场景Id final Map scenarios = new LinkedHashMap<>(); - queue.forEach(result -> { + queue.forEach(result -> { // 线程名称: <场景名> <场景Index>-<请求Index>, 例如:Scenario 2-1 String scenarioName = StringUtils.substringBeforeLast(result.getThreadName(), THREAD_SPLIT); String index = StringUtils.substringAfterLast(result.getThreadName(), THREAD_SPLIT); @@ -137,17 +167,25 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl scenarioResult.addPassAssertions(requestResult.getPassAssertions()); scenarioResult.addTotalAssertions(requestResult.getTotalAssertions()); }); - testResult.getScenarios().addAll(scenarios.values()); testResult.getScenarios().sort(Comparator.comparing(ScenarioResult::getId)); - ApiTestReport report; + ApiTestReport report = null; if (StringUtils.equals(this.runMode, ApiRunMode.DEBUG.name())) { report = apiReportService.get(debugReportId); + apiReportService.complete(testResult, report); + } else if (StringUtils.equals(this.runMode, ApiRunMode.DELIMIT.name())) { + // 调试操作,不需要存储结果 + if (StringUtils.isBlank(debugReportId)) { + apiDefinitionService.addResult(testResult); + } else { + apiDefinitionService.addResult(testResult); + apiDefinitionExecResultService.saveApiResult(testResult); + } } else { apiTestService.changeStatus(testId, APITestStatus.Completed); report = apiReportService.getRunningReport(testResult.getTestId()); + apiReportService.complete(testResult, report); } - apiReportService.complete(testResult, report); queue.clear(); super.teardownTest(context); @@ -242,6 +280,14 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl requestResult.setTotalAssertions(result.getAssertionResults().length); requestResult.setSuccess(result.isSuccessful()); requestResult.setError(result.getErrorCount()); + if (result instanceof HTTPSampleResult) { + HTTPSampleResult res = (HTTPSampleResult) result; + requestResult.setCookies(res.getCookies()); + } + + for (SampleResult subResult : result.getSubResults()) { + requestResult.getSubRequestResults().add(getRequestResult(subResult)); + } for (SampleResult subResult : result.getSubResults()) { requestResult.getSubRequestResults().add(getRequestResult(subResult)); } @@ -277,6 +323,8 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl } responseResult.getAssertions().add(responseAssertionResult); } + responseResult.setConsole(getConsole()); + return requestResult; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index 220d4c7e25..6ef088f200 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -14,6 +14,7 @@ import org.apache.jorphan.collections.HashTree; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; +import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.File; import java.io.InputStream; @@ -25,19 +26,23 @@ public class JMeterService { @Resource private JmeterProperties jmeterProperties; - public void run(String testId, String debugReportId, InputStream is) { + @PostConstruct + public void init() { String JMETER_HOME = getJmeterHome(); String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties"; JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES); JMeterUtils.setJMeterHome(JMETER_HOME); JMeterUtils.setLocale(LocaleContextHolder.getLocale()); + } + public void run(String testId, String debugReportId, InputStream is) { + init(); try { Object scriptWrapper = SaveService.loadElement(is); HashTree testPlan = getHashTree(scriptWrapper); JMeterVars.addJSR223PostProcessor(testPlan); - addBackendListener(testId, debugReportId, testPlan); + addBackendListener(testId, debugReportId, ApiRunMode.DEBUG.name(), testPlan); LocalRunner runner = new LocalRunner(testPlan); runner.run(); } catch (Exception e) { @@ -66,17 +71,32 @@ public class JMeterService { return (HashTree) field.get(scriptWrapper); } - private void addBackendListener(String testId, String debugReportId, HashTree testPlan) { + private void addBackendListener(String testId, String debugReportId, String runMode, HashTree testPlan) { BackendListener backendListener = new BackendListener(); backendListener.setName(testId); Arguments arguments = new Arguments(); arguments.addArgument(APIBackendListenerClient.TEST_ID, testId); + if (StringUtils.isNotBlank(runMode)) { + arguments.addArgument("runMode", runMode); + } if (StringUtils.isNotBlank(debugReportId)) { - arguments.addArgument("runMode", ApiRunMode.DEBUG.name()); arguments.addArgument("debugReportId", debugReportId); } backendListener.setArguments(arguments); backendListener.setClassname(APIBackendListenerClient.class.getCanonicalName()); testPlan.add(testPlan.getArray()[0], backendListener); } + + public void runDefinition(String testId, HashTree testPlan, String debugReportId, String runMode) { + try { + init(); + JMeterVars.addJSR223PostProcessor(testPlan); + addBackendListener(testId, debugReportId, runMode, testPlan); + LocalRunner runner = new LocalRunner(testPlan); + runner.run(); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + MSException.throwException(Translator.get("api_load_script_error")); + } + } } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/ResponseResult.java b/backend/src/main/java/io/metersphere/api/jmeter/ResponseResult.java index 01b37b741a..96ff6991c1 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/ResponseResult.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/ResponseResult.java @@ -25,6 +25,8 @@ public class ResponseResult { private String vars; + private String console; + private final List assertions = new ArrayList<>(); } diff --git a/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java b/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java index f3086468eb..6173f71c77 100644 --- a/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/ApiImportParser.java @@ -1,10 +1,13 @@ package io.metersphere.api.parse; import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.dto.parse.ApiImport; import java.io.InputStream; public interface ApiImportParser { ApiImport parse(InputStream source, ApiTestImportRequest request); + ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request); + } diff --git a/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java index 49d70bcb60..4acf7f5690 100644 --- a/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java @@ -26,7 +26,7 @@ public class JmeterDocumentParser { private final static String STRING_PROP = "stringProp"; private final static String ARGUMENTS = "Arguments"; private final static String COLLECTION_PROP = "collectionProp"; - private final static String HTTP_SAMPLER_PROXY = "HTTPSamplerProxy"; + private final static String HTTP_SAMPLER_PROXY = "MsHTTPSamplerProxy"; private final static String ELEMENT_PROP = "elementProp"; public static byte[] parse(byte[] source) { diff --git a/backend/src/main/java/io/metersphere/api/parse/MsParser.java b/backend/src/main/java/io/metersphere/api/parse/MsParser.java index dd33f8709c..c9d165e247 100644 --- a/backend/src/main/java/io/metersphere/api/parse/MsParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/MsParser.java @@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.dto.parse.ApiImport; import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.commons.constants.MsRequestBodyType; @@ -22,6 +23,13 @@ public class MsParser extends ApiImportAbstractParser { return apiImport; } + @Override + public ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + ApiDefinitionImport apiImport = JSON.parseObject(testStr, ApiDefinitionImport.class); + return apiImport; + } + private String parsePluginFormat(String testStr) { JSONObject testObject = JSONObject.parseObject(testStr, Feature.OrderedField); if (testObject.get("scenarios") != null) { diff --git a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java index a4ffba9226..e8eed52279 100644 --- a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java @@ -3,6 +3,11 @@ package io.metersphere.api.parse; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.parse.ApiImport; import io.metersphere.api.dto.parse.postman.*; import io.metersphere.api.dto.scenario.Body; @@ -10,12 +15,17 @@ import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.Scenario; import io.metersphere.api.dto.scenario.request.HttpRequest; import io.metersphere.api.dto.scenario.request.Request; +import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.commons.constants.MsRequestBodyType; import io.metersphere.commons.constants.PostmanRequestBodyMode; import org.apache.commons.lang3.StringUtils; +import org.apache.jorphan.collections.HashTree; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; public class PostmanParser extends ApiImportAbstractParser { @@ -38,6 +48,67 @@ public class PostmanParser extends ApiImportAbstractParser { return apiImport; } + @Override + public ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request) { + String testStr = getApiTestStr(source); + PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class); + List variables = postmanCollection.getVariable(); + ApiDefinitionImport apiImport = new ApiDefinitionImport(); + List requests = new ArrayList<>(); + + parseItem(postmanCollection.getItem(), variables, requests); + apiImport.setData(requests); + return apiImport; + } + + private void parseItem(List items, List variables, List scenarios) { + for (PostmanItem item : items) { + List childItems = item.getItem(); + if (childItems != null) { + parseItem(childItems, variables, scenarios); + } else { + ApiDefinitionResult request = parsePostman(item); + if (request != null) { + scenarios.add(request); + } + } + } + } + + private ApiDefinitionResult parsePostman(PostmanItem requestItem) { + PostmanRequest requestDesc = requestItem.getRequest(); + if (requestDesc == null) { + return null; + } + PostmanUrl url = requestDesc.getUrl(); + ApiDefinitionResult request = new ApiDefinitionResult(); + request.setName(requestItem.getName()); + request.setPath(url.getRaw()); + request.setMethod(requestDesc.getMethod()); + request.setProtocol(RequestType.HTTP); + MsHTTPSamplerProxy requestElement = new MsHTTPSamplerProxy(); + requestElement.setName(requestItem.getName() + "Postman MHTTPSamplerProxy"); + requestElement.setBody(parseBody(requestDesc)); + requestElement.setArguments(parseKeyValue(url.getQuery())); + requestElement.setProtocol(RequestType.HTTP); + requestElement.setPath(url.getRaw()); + requestElement.setMethod(requestDesc.getMethod()); + requestElement.setId(UUID.randomUUID().toString()); + requestElement.setRest(new ArrayList()); + MsHeaderManager headerManager = new MsHeaderManager(); + headerManager.setId(UUID.randomUUID().toString()); + headerManager.setName(requestItem.getName() + "Postman MsHeaderManager"); + headerManager.setHeaders(parseKeyValue(requestDesc.getHeader())); + HashTree tree = new HashTree(); + tree.add(headerManager); + LinkedList list = new LinkedList<>(); + list.add(headerManager); + requestElement.setHashTree(list); + request.setRequest(JSON.toJSONString(requestElement)); + return request; + } + + private List parseKeyValue(List postmanKeyValues) { if (postmanKeyValues == null) { return null; diff --git a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java index b6471f413c..f16c220364 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -1,14 +1,21 @@ package io.metersphere.api.parse; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.definition.request.MsTestElement; +import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager; +import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.parse.ApiImport; import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.Scenario; import io.metersphere.api.dto.scenario.request.HttpRequest; import io.metersphere.api.dto.scenario.request.Request; +import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.commons.constants.MsRequestBodyType; import io.metersphere.commons.constants.SwaggerParameterType; import io.swagger.models.*; @@ -19,6 +26,7 @@ import io.swagger.models.properties.Property; import io.swagger.models.properties.RefProperty; import io.swagger.parser.SwaggerParser; import org.apache.commons.lang3.StringUtils; +import org.apache.jorphan.collections.HashTree; import java.io.InputStream; import java.util.*; @@ -39,6 +47,52 @@ public class Swagger2Parser extends ApiImportAbstractParser { return apiImport; } + @Override + public ApiDefinitionImport parseApi(InputStream source, ApiTestImportRequest request) { + ApiImport apiImport = this.parse(source, request); + ApiDefinitionImport definitionImport = new ApiDefinitionImport(); + definitionImport.setData(parseSwagger(apiImport)); + return definitionImport; + } + + private List parseSwagger(ApiImport apiImport) { + List results = new LinkedList<>(); + apiImport.getScenarios().forEach(item -> { + item.getRequests().forEach(childItem -> { + if (childItem instanceof HttpRequest) { + HttpRequest res = (HttpRequest) childItem; + ApiDefinitionResult request = new ApiDefinitionResult(); + request.setName(res.getName()); + request.setPath(res.getPath()); + request.setMethod(res.getMethod()); + request.setProtocol(RequestType.HTTP); + MsHTTPSamplerProxy requestElement = new MsHTTPSamplerProxy(); + requestElement.setName(res.getName() + "Postman MHTTPSamplerProxy"); + requestElement.setBody(res.getBody()); + requestElement.setArguments(res.getParameters()); + requestElement.setProtocol(RequestType.HTTP); + requestElement.setPath(res.getPath()); + requestElement.setMethod(res.getMethod()); + requestElement.setId(UUID.randomUUID().toString()); + requestElement.setRest(new ArrayList()); + MsHeaderManager headerManager = new MsHeaderManager(); + headerManager.setId(UUID.randomUUID().toString()); + headerManager.setName(res.getName() + "Postman MsHeaderManager"); + headerManager.setHeaders(res.getHeaders()); + HashTree tree = new HashTree(); + tree.add(headerManager); + LinkedList list = new LinkedList<>(); + list.add(headerManager); + requestElement.setHashTree(list); + request.setRequest(JSON.toJSONString(requestElement)); + results.add(request); + } + + }); + }); + return results; + } + private List parseRequests(Swagger swagger) { Map paths = swagger.getPaths(); Set pathNames = paths.keySet(); @@ -141,7 +195,7 @@ public class Swagger2Parser extends ApiImportAbstractParser { Model model = definitions.get(simpleRef); HashSet refSet = new HashSet<>(); refSet.add(simpleRef); - if (model != null ) { + if (model != null) { JSONObject bodyParameters = getBodyJSONObjectParameters(model.getProperties(), definitions, refSet); body.setRaw(bodyParameters.toJSONString()); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java new file mode 100644 index 0000000000..924306669b --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java @@ -0,0 +1,38 @@ +package io.metersphere.api.service; + +import com.alibaba.fastjson.JSON; +import io.metersphere.api.jmeter.TestResult; +import io.metersphere.base.domain.ApiDefinitionExecResult; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; +import io.metersphere.commons.utils.SessionUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.Objects; +import java.util.UUID; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiDefinitionExecResultService { + @Resource + private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; + + + public void saveApiResult(TestResult result) { + result.getScenarios().get(0).getRequestResults().forEach(item -> { + // 清理原始资源,每个执行 保留一条结果 + apiDefinitionExecResultMapper.deleteByResourceId(item.getName()); + ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult(); + saveResult.setId(UUID.randomUUID().toString()); + saveResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + saveResult.setName(item.getName()); + saveResult.setResourceId(item.getName()); + saveResult.setContent(JSON.toJSONString(item)); + saveResult.setStartTime(item.getStartTime()); + saveResult.setEndTime(item.getResponseResult().getResponseTime()); + saveResult.setStatus(item.getResponseResult().getResponseCode().equals("200") ? "success" : "error"); + apiDefinitionExecResultMapper.insert(saveResult); + }); + } +} diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java new file mode 100644 index 0000000000..14e7420015 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -0,0 +1,344 @@ +package io.metersphere.api.service; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.APIReportResult; +import io.metersphere.api.dto.ApiTestImportRequest; +import io.metersphere.api.dto.definition.*; +import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; +import io.metersphere.api.dto.scenario.request.RequestType; +import io.metersphere.api.jmeter.JMeterService; +import io.metersphere.api.jmeter.TestResult; +import io.metersphere.api.parse.ApiImportParser; +import io.metersphere.api.parse.ApiImportParserFactory; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; +import io.metersphere.base.mapper.ApiDefinitionMapper; +import io.metersphere.base.mapper.ApiTestFileMapper; +import io.metersphere.commons.constants.APITestStatus; +import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.ServiceUtils; +import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.i18n.Translator; +import io.metersphere.service.FileService; +import org.apache.jorphan.collections.HashTree; +import org.aspectj.util.FileUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; +import sun.security.util.Cache; + +import javax.annotation.Resource; +import java.io.*; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiDefinitionService { + @Resource + private ApiDefinitionMapper apiDefinitionMapper; + @Resource + private ApiTestFileMapper apiTestFileMapper; + @Resource + private FileService fileService; + @Resource + private ApiTestCaseService apiTestCaseService; + @Resource + private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; + @Resource + private JMeterService jMeterService; + + private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24); + + private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; + + public List list(ApiDefinitionRequest request) { + request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); + List resList = apiDefinitionMapper.list(request); + if (!resList.isEmpty()) { + List ids = resList.stream().map(ApiDefinitionResult::getId).collect(Collectors.toList()); + List results = apiDefinitionMapper.selectByIds(ids); + Map resultMap = results.stream().collect(Collectors.toMap(ApiComputeResult::getApiDefinitionId, Function.identity())); + for (ApiDefinitionResult res : resList) { + ApiComputeResult compRes = resultMap.get(res.getId()); + if (compRes != null) { + res.setCaseTotal(compRes.getCaseTotal()); + res.setCasePassingRate(compRes.getPassRate()); + res.setCaseStatus(compRes.getStatus()); + } else { + res.setCaseTotal("-"); + res.setCasePassingRate("-"); + res.setCaseStatus("-"); + } + } + } + return resList; + } + + public ApiDefinition get(String id) { + return apiDefinitionMapper.selectByPrimaryKey(id); + } + + public void create(SaveApiDefinitionRequest request, List bodyFiles) { + List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); + createTest(request); + createBodyFiles(bodyUploadIds, bodyFiles); + } + + public void update(SaveApiDefinitionRequest request, List bodyFiles) { + deleteFileByTestId(request.getRequest().getId()); + List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); + request.setBodyUploadIds(null); + updateTest(request); + createBodyFiles(bodyUploadIds, bodyFiles); + } + + private void createBodyFiles(List bodyUploadIds, List bodyFiles) { + if (bodyUploadIds.size() > 0) { + File testDir = new File(BODY_FILE_DIR); + if (!testDir.exists()) { + testDir.mkdirs(); + } + for (int i = 0; i < bodyUploadIds.size(); i++) { + MultipartFile item = bodyFiles.get(i); + File file = new File(BODY_FILE_DIR + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename()); + try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) { + file.createNewFile(); + FileUtil.copyStream(in, out); + } catch (IOException e) { + LogUtil.error(e); + MSException.throwException(Translator.get("upload_fail")); + } + } + } + } + + public void delete(String apiId) { + apiTestCaseService.deleteTestCase(apiId); + deleteFileByTestId(apiId); + apiDefinitionExecResultMapper.deleteByResourceId(apiId); + apiDefinitionMapper.deleteByPrimaryKey(apiId); + deleteBodyFiles(apiId); + } + + public void deleteBatch(List apiIds) { + // 简单处理后续优化 + apiIds.forEach(item -> { + delete(item); + }); + } + + public void removeToGc(List apiIds) { + apiDefinitionMapper.removeToGc(apiIds); + } + + public void deleteBodyFiles(String apiId) { + File file = new File(BODY_FILE_DIR + "/" + apiId); + FileUtil.deleteContents(file); + if (file.exists()) { + file.delete(); + } + } + + private void checkNameExist(SaveApiDefinitionRequest request) { + ApiDefinitionExample example = new ApiDefinitionExample(); + if (request.getProtocol().equals(RequestType.HTTP)) { + example.createCriteria().andProtocolEqualTo(request.getProtocol()).andPathEqualTo(request.getPath()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId()); + if (apiDefinitionMapper.countByExample(example) > 0) { + MSException.throwException(Translator.get("api_definition_url_not_repeating")); + } + } else { + example.createCriteria().andProtocolEqualTo(request.getProtocol()).andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId()); + if (apiDefinitionMapper.countByExample(example) > 0) { + MSException.throwException(Translator.get("load_test_already_exists")); + } + } + } + + + private ApiDefinition updateTest(SaveApiDefinitionRequest request) { + checkNameExist(request); + final ApiDefinition test = new ApiDefinition(); + test.setId(request.getId()); + test.setName(request.getName()); + test.setPath(request.getPath()); + test.setProjectId(request.getProjectId()); + test.setRequest(JSONObject.toJSONString(request.getRequest())); + test.setUpdateTime(System.currentTimeMillis()); + test.setStatus(request.getStatus()); + test.setModulePath(request.getModulePath()); + test.setModuleId(request.getModuleId()); + test.setMethod(request.getMethod()); + test.setProtocol(request.getProtocol()); + test.setDescription(request.getDescription()); + test.setResponse(JSONObject.toJSONString(request.getResponse())); + test.setEnvironmentId(request.getEnvironmentId()); + test.setUserId(request.getUserId()); + + apiDefinitionMapper.updateByPrimaryKeySelective(test); + return test; + } + + private ApiDefinition createTest(SaveApiDefinitionRequest request) { + checkNameExist(request); + final ApiDefinition test = new ApiDefinition(); + test.setId(request.getId()); + test.setName(request.getName()); + test.setProtocol(request.getProtocol()); + test.setMethod(request.getMethod()); + test.setPath(request.getPath()); + test.setModuleId(request.getModuleId()); + test.setProjectId(request.getProjectId()); + test.setRequest(JSONObject.toJSONString(request.getRequest())); + test.setCreateTime(System.currentTimeMillis()); + test.setUpdateTime(System.currentTimeMillis()); + test.setStatus(APITestStatus.Underway.name()); + test.setModulePath(request.getModulePath()); + test.setResponse(JSONObject.toJSONString(request.getResponse())); + test.setEnvironmentId(request.getEnvironmentId()); + if (request.getUserId() == null) { + test.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + } else { + test.setUserId(request.getUserId()); + } + test.setDescription(request.getDescription()); + apiDefinitionMapper.insert(test); + return test; + } + + private ApiDefinition createTest(ApiDefinitionResult request) { + SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest(); + saveReq.setId(UUID.randomUUID().toString()); + saveReq.setName(request.getName()); + saveReq.setProtocol(request.getProtocol()); + saveReq.setProjectId(request.getProjectId()); + saveReq.setPath(request.getPath()); + checkNameExist(saveReq); + final ApiDefinition test = new ApiDefinition(); + test.setId(request.getId()); + test.setName(request.getName()); + test.setProtocol(request.getProtocol()); + test.setMethod(request.getMethod()); + test.setPath(request.getPath()); + test.setModuleId(request.getModuleId()); + test.setProjectId(request.getProjectId()); + test.setRequest(request.getRequest()); + test.setCreateTime(System.currentTimeMillis()); + test.setUpdateTime(System.currentTimeMillis()); + test.setStatus(APITestStatus.Underway.name()); + test.setModulePath(request.getModulePath()); + test.setResponse(request.getResponse()); + test.setEnvironmentId(request.getEnvironmentId()); + if (request.getUserId() == null) { + test.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + } else { + test.setUserId(request.getUserId()); + } + test.setDescription(request.getDescription()); + apiDefinitionMapper.insert(test); + return test; + } + + private void deleteFileByTestId(String apiId) { + ApiTestFileExample apiTestFileExample = new ApiTestFileExample(); + apiTestFileExample.createCriteria().andTestIdEqualTo(apiId); + final List ApiTestFiles = apiTestFileMapper.selectByExample(apiTestFileExample); + apiTestFileMapper.deleteByExample(apiTestFileExample); + + if (!CollectionUtils.isEmpty(ApiTestFiles)) { + final List fileIds = ApiTestFiles.stream().map(ApiTestFile::getFileId).collect(Collectors.toList()); + fileService.deleteFileByIds(fileIds); + } + } + + /** + * 测试执行 + * + * @param request + * @param bodyFiles + * @return + */ + public String run(RunDefinitionRequest request, List bodyFiles) { + List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); + createBodyFiles(bodyUploadIds, bodyFiles); + + HashTree hashTree = request.getTestElement().generateHashTree(); + // 调用执行方法 + jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), ApiRunMode.DELIMIT.name()); + return request.getId(); + } + + public void addResult(TestResult res) { + if (!res.getScenarios().isEmpty() && !res.getScenarios().get(0).getRequestResults().isEmpty()) { + cache.put(res.getTestId(), res.getScenarios().get(0).getRequestResults().get(0)); + } else { + MSException.throwException(Translator.get("test_not_found")); + } + } + + /** + * 获取零时执行结果报告 + * + * @param testId + * @param test + * @return + */ + public APIReportResult getResult(String testId, String test) { + Object res = cache.get(testId); + if (res != null) { + cache.remove(testId); + APIReportResult reportResult = new APIReportResult(); + reportResult.setContent(JSON.toJSONString(res)); + return reportResult; + } + return null; + } + + /** + * 获取存储执行结果报告 + * + * @param testId + * @return + */ + public APIReportResult getDbResult(String testId) { + ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByResourceId(testId); + if (result == null) { + return null; + } + APIReportResult reportResult = new APIReportResult(); + reportResult.setContent(result.getContent()); + return reportResult; + } + + + public String apiTestImport(MultipartFile file, ApiTestImportRequest request) { + ApiImportParser apiImportParser = ApiImportParserFactory.getApiImportParser(request.getPlatform()); + ApiDefinitionImport apiImport = null; + try { + apiImport = Objects.requireNonNull(apiImportParser).parseApi(file == null ? null : file.getInputStream(), request); + } catch (Exception e) { + LogUtil.error(e.getMessage(), e); + MSException.throwException(Translator.get("parse_data_error")); + } + importApiTest(request, apiImport); + return "SUCCESS"; + } + + private void importApiTest(ApiTestImportRequest importRequest, ApiDefinitionImport apiImport) { + apiImport.getData().forEach(item -> { + item.setProjectId(importRequest.getProjectId()); + item.setModuleId(importRequest.getModuleId()); + item.setModulePath(importRequest.getModulePath()); + item.setEnvironmentId(importRequest.getEnvironmentId()); + item.setId(UUID.randomUUID().toString()); + item.setUserId(null); + createTest(item); + }); + } + +} diff --git a/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java b/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java new file mode 100644 index 0000000000..1e93aebfd9 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/service/ApiModuleService.java @@ -0,0 +1,242 @@ +package io.metersphere.api.service; + + +import io.metersphere.api.dto.definition.ApiDefinitionRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.api.dto.definition.ApiModuleDTO; +import io.metersphere.api.dto.definition.DragModuleRequest; +import io.metersphere.base.domain.ApiDefinitionExample; +import io.metersphere.base.domain.ApiModule; +import io.metersphere.base.domain.ApiModuleExample; +import io.metersphere.base.mapper.ApiDefinitionMapper; +import io.metersphere.base.mapper.ApiModuleMapper; +import io.metersphere.commons.constants.TestCaseConstants; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.i18n.Translator; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiModuleService { + + @Resource + ApiModuleMapper apiModuleMapper; + @Resource + private ApiDefinitionMapper apiDefinitionMapper; + @Resource + SqlSessionFactory sqlSessionFactory; + + public List getNodeTreeByProjectId(String projectId, String protocol) { + ApiModuleExample apiDefinitionNodeExample = new ApiModuleExample(); + apiDefinitionNodeExample.createCriteria().andProjectIdEqualTo(projectId).andProtocolEqualTo(protocol); + apiDefinitionNodeExample.setOrderByClause("create_time asc"); + List nodes = apiModuleMapper.selectByExample(apiDefinitionNodeExample); + return getNodeTrees(nodes); + } + + public List getNodeTrees(List nodes) { + + List nodeTreeList = new ArrayList<>(); + Map> nodeLevelMap = new HashMap<>(); + nodes.forEach(node -> { + Integer level = node.getLevel(); + if (nodeLevelMap.containsKey(level)) { + nodeLevelMap.get(level).add(node); + } else { + List apiModules = new ArrayList<>(); + apiModules.add(node); + nodeLevelMap.put(node.getLevel(), apiModules); + } + }); + List rootNodes = Optional.ofNullable(nodeLevelMap.get(1)).orElse(new ArrayList<>()); + rootNodes.forEach(rootNode -> nodeTreeList.add(buildNodeTree(nodeLevelMap, rootNode))); + return nodeTreeList; + } + + /** + * 递归构建节点树 + * + * @param nodeLevelMap + * @param rootNode + * @return + */ + private ApiModuleDTO buildNodeTree(Map> nodeLevelMap, ApiModule rootNode) { + + ApiModuleDTO nodeTree = new ApiModuleDTO(); + BeanUtils.copyBean(nodeTree, rootNode); + nodeTree.setLabel(rootNode.getName()); + + List lowerNodes = nodeLevelMap.get(rootNode.getLevel() + 1); + if (lowerNodes == null) { + return nodeTree; + } + List children = Optional.ofNullable(nodeTree.getChildren()).orElse(new ArrayList<>()); + lowerNodes.forEach(node -> { + if (node.getParentId() != null && node.getParentId().equals(rootNode.getId())) { + children.add(buildNodeTree(nodeLevelMap, node)); + nodeTree.setChildren(children); + } + }); + + return nodeTree; + } + + + public String addNode(ApiModule node) { + validateNode(node); + node.setCreateTime(System.currentTimeMillis()); + node.setUpdateTime(System.currentTimeMillis()); + node.setId(UUID.randomUUID().toString()); + apiModuleMapper.insertSelective(node); + return node.getId(); + } + + private void validateNode(ApiModule node) { + if (node.getLevel() > TestCaseConstants.MAX_NODE_DEPTH) { + throw new RuntimeException(Translator.get("test_case_node_level_tip") + + TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level")); + } + checkApiModuleExist(node); + } + + private void checkApiModuleExist(ApiModule node) { + if (node.getName() != null) { + ApiModuleExample example = new ApiModuleExample(); + ApiModuleExample.Criteria criteria = example.createCriteria(); + criteria.andNameEqualTo(node.getName()) + .andProjectIdEqualTo(node.getProjectId()); + if (StringUtils.isNotBlank(node.getParentId())) { + criteria.andParentIdEqualTo(node.getParentId()); + } else { + criteria.andParentIdIsNull(); + } + if (StringUtils.isNotBlank(node.getId())) { + criteria.andIdNotEqualTo(node.getId()); + } + if (apiModuleMapper.selectByExample(example).size() > 0) { + MSException.throwException(Translator.get("test_case_module_already_exists")); + } + } + } + + private List queryByModuleIds(List nodeIds) { + ApiDefinitionRequest apiDefinitionRequest = new ApiDefinitionRequest(); + apiDefinitionRequest.setModuleIds(nodeIds); + return apiDefinitionMapper.list(apiDefinitionRequest); + } + + public int editNode(DragModuleRequest request) { + request.setUpdateTime(System.currentTimeMillis()); + checkApiModuleExist(request); + List apiModule = queryByModuleIds(request.getNodeIds()); + + apiModule.forEach(apiDefinition -> { + StringBuilder path = new StringBuilder(apiDefinition.getModulePath()); + List pathLists = Arrays.asList(path.toString().split("/")); + pathLists.set(request.getLevel(), request.getName()); + path.delete(0, path.length()); + for (int i = 1; i < pathLists.size(); i++) { + path = path.append("/").append(pathLists.get(i)); + } + apiDefinition.setModulePath(path.toString()); + }); + + batchUpdateApiDefinition(apiModule); + + return apiModuleMapper.updateByPrimaryKeySelective(request); + } + + public int deleteNode(List nodeIds) { + ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample(); + apiDefinitionExample.createCriteria().andModuleIdIn(nodeIds); + apiDefinitionMapper.deleteByExample(apiDefinitionExample); + + ApiModuleExample apiDefinitionNodeExample = new ApiModuleExample(); + apiDefinitionNodeExample.createCriteria().andIdIn(nodeIds); + return apiModuleMapper.deleteByExample(apiDefinitionNodeExample); + } + + private void batchUpdateApiDefinition(List apiModule) { + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiDefinitionMapper apiDefinitionMapper = sqlSession.getMapper(ApiDefinitionMapper.class); + apiModule.forEach((value) -> { + apiDefinitionMapper.updateByPrimaryKey(value); + }); + sqlSession.flushStatements(); + } + + public void dragNode(DragModuleRequest request) { + + checkApiModuleExist(request); + + List nodeIds = request.getNodeIds(); + + List apiModule = queryByModuleIds(nodeIds); + + ApiModuleDTO nodeTree = request.getNodeTree(); + + List updateNodes = new ArrayList<>(); + + buildUpdateDefinition(nodeTree, apiModule, updateNodes, "/", "0", nodeTree.getLevel()); + + updateNodes = updateNodes.stream() + .filter(item -> nodeIds.contains(item.getId())) + .collect(Collectors.toList()); + + batchUpdateModule(updateNodes); + + batchUpdateApiDefinition(apiModule); + } + + private void buildUpdateDefinition(ApiModuleDTO rootNode, List apiDefinitions, + List updateNodes, String rootPath, String pId, int level) { + + rootPath = rootPath + rootNode.getName(); + + if (level > 8) { + MSException.throwException(Translator.get("node_deep_limit")); + } + if (rootNode.getId().equals("root")) { + rootPath = ""; + } + ApiModule apiDefinitionNode = new ApiModule(); + apiDefinitionNode.setId(rootNode.getId()); + apiDefinitionNode.setLevel(level); + apiDefinitionNode.setParentId(pId); + updateNodes.add(apiDefinitionNode); + + for (ApiDefinitionResult item : apiDefinitions) { + if (StringUtils.equals(item.getModuleId(), rootNode.getId())) { + item.setModulePath(rootPath); + } + } + + List children = rootNode.getChildren(); + if (children != null && children.size() > 0) { + for (int i = 0; i < children.size(); i++) { + buildUpdateDefinition(children.get(i), apiDefinitions, updateNodes, rootPath + '/', rootNode.getId(), level + 1); + } + } + } + + private void batchUpdateModule(List updateNodes) { + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiModuleMapper apiModuleMapper = sqlSession.getMapper(ApiModuleMapper.class); + updateNodes.forEach((value) -> { + apiModuleMapper.updateByPrimaryKeySelective(value); + }); + sqlSession.flushStatements(); + } + + +} diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java new file mode 100644 index 0000000000..672fdd9ac1 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -0,0 +1,208 @@ +package io.metersphere.api.service; + +import com.alibaba.fastjson.JSONObject; +import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.ApiTestCaseResult; +import io.metersphere.api.dto.definition.SaveApiTestCaseRequest; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; +import io.metersphere.base.mapper.ApiTestCaseMapper; +import io.metersphere.base.mapper.ApiTestFileMapper; +import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.ServiceUtils; +import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.i18n.Translator; +import io.metersphere.service.FileService; +import io.metersphere.service.QuotaService; +import org.aspectj.util.FileUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class ApiTestCaseService { + @Resource + private ApiTestCaseMapper apiTestCaseMapper; + @Resource + private ApiTestFileMapper apiTestFileMapper; + @Resource + private FileService fileService; + @Resource + private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; + + private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; + + public List list(ApiTestCaseRequest request) { + request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); + return apiTestCaseMapper.list(request); + } + + public ApiTestCase get(String id) { + return apiTestCaseMapper.selectByPrimaryKey(id); + } + + public void create(SaveApiTestCaseRequest request, List bodyFiles) { + List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); + ApiTestCase test = createTest(request); + createBodyFiles(test, bodyUploadIds, bodyFiles); + } + + private void checkQuota() { + QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class); + if (quotaService != null) { + quotaService.checkAPITestQuota(); + } + } + + public void update(SaveApiTestCaseRequest request, List bodyFiles) { + + deleteFileByTestId(request.getId()); + + List bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); + request.setBodyUploadIds(null); + ApiTestCase test = updateTest(request); + createBodyFiles(test, bodyUploadIds, bodyFiles); + } + + private void createBodyFiles(ApiTestCase test, List bodyUploadIds, List bodyFiles) { + if (bodyUploadIds.size() > 0) { + String dir = BODY_FILE_DIR + "/" + test.getId(); + File testDir = new File(dir); + if (!testDir.exists()) { + testDir.mkdirs(); + } + for (int i = 0; i < bodyUploadIds.size(); i++) { + MultipartFile item = bodyFiles.get(i); + File file = new File(testDir + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename()); + try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) { + file.createNewFile(); + FileUtil.copyStream(in, out); + } catch (IOException e) { + LogUtil.error(e); + MSException.throwException(Translator.get("upload_fail")); + } + } + } + } + + public void delete(String testId) { + deleteFileByTestId(testId); + apiDefinitionExecResultMapper.deleteByResourceId(testId); + apiTestCaseMapper.deleteByPrimaryKey(testId); + deleteBodyFiles(testId); + } + + public void deleteTestCase(String apiId) { + ApiTestCaseExample testCaseExample = new ApiTestCaseExample(); + testCaseExample.createCriteria().andApiDefinitionIdEqualTo(apiId); + List testCases = apiTestCaseMapper.selectByExample(testCaseExample); + if (testCases.size() > 0) { + for (ApiTestCase testCase : testCases) { + this.delete(testCase.getId()); + } + } + } + + /** + * 是否已经创建了测试用例 + */ + public void checkIsRelateTest(String apiId) { + ApiTestCaseExample testCaseExample = new ApiTestCaseExample(); + testCaseExample.createCriteria().andApiDefinitionIdEqualTo(apiId); + List testCases = apiTestCaseMapper.selectByExample(testCaseExample); + StringBuilder caseName = new StringBuilder(); + if (testCases.size() > 0) { + for (ApiTestCase testCase : testCases) { + caseName = caseName.append(testCase.getName()).append(","); + } + String str = caseName.toString().substring(0, caseName.length() - 1); + MSException.throwException(Translator.get("related_case_del_fail_prefix") + " " + str + " " + Translator.get("related_case_del_fail_suffix")); + } + } + + public void deleteBodyFiles(String testId) { + File file = new File(BODY_FILE_DIR + "/" + testId); + FileUtil.deleteContents(file); + if (file.exists()) { + file.delete(); + } + } + + private void checkNameExist(SaveApiTestCaseRequest request) { + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andNameEqualTo(request.getName()).andApiDefinitionIdEqualTo(request.getApiDefinitionId()).andIdNotEqualTo(request.getId()); + if (apiTestCaseMapper.countByExample(example) > 0) { + MSException.throwException(Translator.get("load_test_already_exists")); + } + } + + + private ApiTestCase updateTest(SaveApiTestCaseRequest request) { + checkNameExist(request); + final ApiTestCase test = new ApiTestCase(); + test.setId(request.getId()); + test.setName(request.getName()); + test.setApiDefinitionId(request.getApiDefinitionId()); + test.setUpdateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + test.setProjectId(request.getProjectId()); + test.setRequest(JSONObject.toJSONString(request.getRequest())); + test.setResponse(JSONObject.toJSONString(request.getResponse())); + test.setPriority(request.getPriority()); + test.setUpdateTime(System.currentTimeMillis()); + test.setDescription(request.getDescription()); + apiTestCaseMapper.updateByPrimaryKeySelective(test); + return test; + } + + private ApiTestCase createTest(SaveApiTestCaseRequest request) { + request.setId(UUID.randomUUID().toString()); + checkNameExist(request); + final ApiTestCase test = new ApiTestCase(); + test.setId(request.getId()); + test.setName(request.getName()); + test.setApiDefinitionId(request.getApiDefinitionId()); + test.setCreateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + test.setUpdateUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + test.setProjectId(request.getProjectId()); + test.setRequest(JSONObject.toJSONString(request.getRequest())); + test.setResponse(JSONObject.toJSONString(request.getResponse())); + test.setCreateTime(System.currentTimeMillis()); + test.setPriority(request.getPriority()); + test.setUpdateTime(System.currentTimeMillis()); + test.setDescription(request.getDescription()); + apiTestCaseMapper.insert(test); + return test; + } + + private void saveFile(String testId, MultipartFile file) { + final FileMetadata fileMetadata = fileService.saveFile(file); + ApiTestFile apiTestFile = new ApiTestFile(); + apiTestFile.setTestId(testId); + apiTestFile.setFileId(fileMetadata.getId()); + apiTestFileMapper.insert(apiTestFile); + } + + private void deleteFileByTestId(String testId) { + ApiTestFileExample ApiTestFileExample = new ApiTestFileExample(); + ApiTestFileExample.createCriteria().andTestIdEqualTo(testId); + final List ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample); + apiTestFileMapper.deleteByExample(ApiTestFileExample); + + if (!CollectionUtils.isEmpty(ApiTestFiles)) { + final List fileIds = ApiTestFiles.stream().map(ApiTestFile::getFileId).collect(Collectors.toList()); + fileService.deleteFileByIds(fileIds); + } + } +} diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDefinition.java b/backend/src/main/java/io/metersphere/base/domain/ApiDefinition.java new file mode 100644 index 0000000000..718db66998 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDefinition.java @@ -0,0 +1,42 @@ +package io.metersphere.base.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ApiDefinition implements Serializable { + private String id; + + private String projectId; + + private String name; + + private String method; + + private String path; + + private String protocol; + + private String environmentId; + + private String status; + + private String description; + + private String userId; + + private String moduleId; + + private String modulePath; + + private Long createTime; + + private Long updateTime; + + private String request; + + private String response; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExample.java b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExample.java new file mode 100644 index 0000000000..22b966e3a4 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExample.java @@ -0,0 +1,756 @@ +package io.metersphere.base.domain; + +import java.util.ArrayList; +import java.util.List; + +public class ApiDefinitionExample { + protected String orderByClause; + + protected boolean distinct; + + protected List oredCriteria; + + public ApiDefinitionExample() { + oredCriteria = new ArrayList(); + } + + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + + public String getOrderByClause() { + return orderByClause; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isDistinct() { + return distinct; + } + + public List getOredCriteria() { + return oredCriteria; + } + + public void or(Criteria criteria) { + oredCriteria.add(criteria); + } + + public Criteria or() { + Criteria criteria = createCriteriaInternal(); + oredCriteria.add(criteria); + return criteria; + } + + public Criteria createCriteria() { + Criteria criteria = createCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add(criteria); + } + return criteria; + } + + protected Criteria createCriteriaInternal() { + Criteria criteria = new Criteria(); + return criteria; + } + + public void clear() { + oredCriteria.clear(); + orderByClause = null; + distinct = false; + } + + protected abstract static class GeneratedCriteria { + protected List criteria; + + protected GeneratedCriteria() { + super(); + criteria = new ArrayList(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getAllCriteria() { + return criteria; + } + + public List getCriteria() { + return criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(String condition, Object value, String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value)); + } + + protected void addCriterion(String condition, Object value1, Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public Criteria andIdIsNull() { + addCriterion("id is null"); + return (Criteria) this; + } + + public Criteria andIdIsNotNull() { + addCriterion("id is not null"); + return (Criteria) this; + } + + public Criteria andIdEqualTo(String value) { + addCriterion("id =", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotEqualTo(String value) { + addCriterion("id <>", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThan(String value) { + addCriterion("id >", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThanOrEqualTo(String value) { + addCriterion("id >=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThan(String value) { + addCriterion("id <", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThanOrEqualTo(String value) { + addCriterion("id <=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLike(String value) { + addCriterion("id like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotLike(String value) { + addCriterion("id not like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdIn(List values) { + addCriterion("id in", values, "id"); + return (Criteria) this; + } + + public Criteria andModuleIdIn(List values) { + addCriterion("module_id in", values, "module_id"); + return (Criteria) this; + } + + public Criteria andIdNotIn(List values) { + addCriterion("id not in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdBetween(String value1, String value2) { + addCriterion("id between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andIdNotBetween(String value1, String value2) { + addCriterion("id not between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNull() { + addCriterion("project_id is null"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNotNull() { + addCriterion("project_id is not null"); + return (Criteria) this; + } + + public Criteria andProjectIdEqualTo(String value) { + addCriterion("project_id =", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotEqualTo(String value) { + addCriterion("project_id <>", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThan(String value) { + addCriterion("project_id >", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThanOrEqualTo(String value) { + addCriterion("project_id >=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThan(String value) { + addCriterion("project_id <", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThanOrEqualTo(String value) { + addCriterion("project_id <=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLike(String value) { + addCriterion("project_id like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotLike(String value) { + addCriterion("project_id not like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdIn(List values) { + addCriterion("project_id in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotIn(List values) { + addCriterion("project_id not in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdBetween(String value1, String value2) { + addCriterion("project_id between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotBetween(String value1, String value2) { + addCriterion("project_id not between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andNameIsNull() { + addCriterion("name is null"); + return (Criteria) this; + } + + public Criteria andNameIsNotNull() { + addCriterion("name is not null"); + return (Criteria) this; + } + + public Criteria andNameEqualTo(String value) { + addCriterion("name =", value, "name"); + return (Criteria) this; + } + + public Criteria andPathEqualTo(String value) { + addCriterion("path =", value, "path"); + return (Criteria) this; + } + + + public Criteria andProtocolEqualTo(String value) { + addCriterion("protocol =", value, "protocol"); + return (Criteria) this; + } + + public Criteria andNameNotEqualTo(String value) { + addCriterion("name <>", value, "name"); + return (Criteria) this; + } + + public Criteria andNameGreaterThan(String value) { + addCriterion("name >", value, "name"); + return (Criteria) this; + } + + public Criteria andNameGreaterThanOrEqualTo(String value) { + addCriterion("name >=", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLessThan(String value) { + addCriterion("name <", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLessThanOrEqualTo(String value) { + addCriterion("name <=", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLike(String value) { + addCriterion("name like", value, "name"); + return (Criteria) this; + } + + public Criteria andNameNotLike(String value) { + addCriterion("name not like", value, "name"); + return (Criteria) this; + } + + public Criteria andNameIn(List values) { + addCriterion("name in", values, "name"); + return (Criteria) this; + } + + public Criteria andNameNotIn(List values) { + addCriterion("name not in", values, "name"); + return (Criteria) this; + } + + public Criteria andNameBetween(String value1, String value2) { + addCriterion("name between", value1, value2, "name"); + return (Criteria) this; + } + + public Criteria andNameNotBetween(String value1, String value2) { + addCriterion("name not between", value1, value2, "name"); + return (Criteria) this; + } + + public Criteria andDescriptionIsNull() { + addCriterion("description is null"); + return (Criteria) this; + } + + public Criteria andDescriptionIsNotNull() { + addCriterion("description is not null"); + return (Criteria) this; + } + + public Criteria andDescriptionEqualTo(String value) { + addCriterion("description =", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotEqualTo(String value) { + addCriterion("description <>", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionGreaterThan(String value) { + addCriterion("description >", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionGreaterThanOrEqualTo(String value) { + addCriterion("description >=", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLessThan(String value) { + addCriterion("description <", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLessThanOrEqualTo(String value) { + addCriterion("description <=", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLike(String value) { + addCriterion("description like", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotLike(String value) { + addCriterion("description not like", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionIn(List values) { + addCriterion("description in", values, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotIn(List values) { + addCriterion("description not in", values, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionBetween(String value1, String value2) { + addCriterion("description between", value1, value2, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotBetween(String value1, String value2) { + addCriterion("description not between", value1, value2, "description"); + return (Criteria) this; + } + + public Criteria andStatusIsNull() { + addCriterion("status is null"); + return (Criteria) this; + } + + public Criteria andStatusIsNotNull() { + addCriterion("status is not null"); + return (Criteria) this; + } + + public Criteria andStatusEqualTo(String value) { + addCriterion("status =", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotEqualTo(String value) { + addCriterion("status <>", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThan(String value) { + addCriterion("status >", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThanOrEqualTo(String value) { + addCriterion("status >=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThan(String value) { + addCriterion("status <", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThanOrEqualTo(String value) { + addCriterion("status <=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLike(String value) { + addCriterion("status like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotLike(String value) { + addCriterion("status not like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusIn(List values) { + addCriterion("status in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotIn(List values) { + addCriterion("status not in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusBetween(String value1, String value2) { + addCriterion("status between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotBetween(String value1, String value2) { + addCriterion("status not between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andUserIdIsNull() { + addCriterion("user_id is null"); + return (Criteria) this; + } + + public Criteria andUserIdIsNotNull() { + addCriterion("user_id is not null"); + return (Criteria) this; + } + + public Criteria andUserIdEqualTo(String value) { + addCriterion("user_id =", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotEqualTo(String value) { + addCriterion("user_id <>", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdGreaterThan(String value) { + addCriterion("user_id >", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdGreaterThanOrEqualTo(String value) { + addCriterion("user_id >=", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdLessThan(String value) { + addCriterion("user_id <", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdLessThanOrEqualTo(String value) { + addCriterion("user_id <=", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdLike(String value) { + addCriterion("user_id like", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotLike(String value) { + addCriterion("user_id not like", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdIn(List values) { + addCriterion("user_id in", values, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotIn(List values) { + addCriterion("user_id not in", values, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdBetween(String value1, String value2) { + addCriterion("user_id between", value1, value2, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotBetween(String value1, String value2) { + addCriterion("user_id not between", value1, value2, "userId"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNull() { + addCriterion("create_time is null"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNotNull() { + addCriterion("create_time is not null"); + return (Criteria) this; + } + + public Criteria andCreateTimeEqualTo(Long value) { + addCriterion("create_time =", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotEqualTo(Long value) { + addCriterion("create_time <>", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThan(Long value) { + addCriterion("create_time >", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("create_time >=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThan(Long value) { + addCriterion("create_time <", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThanOrEqualTo(Long value) { + addCriterion("create_time <=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeIn(List values) { + addCriterion("create_time in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotIn(List values) { + addCriterion("create_time not in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeBetween(Long value1, Long value2) { + addCriterion("create_time between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotBetween(Long value1, Long value2) { + addCriterion("create_time not between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNull() { + addCriterion("update_time is null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNotNull() { + addCriterion("update_time is not null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeEqualTo(Long value) { + addCriterion("update_time =", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotEqualTo(Long value) { + addCriterion("update_time <>", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThan(Long value) { + addCriterion("update_time >", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("update_time >=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThan(Long value) { + addCriterion("update_time <", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThanOrEqualTo(Long value) { + addCriterion("update_time <=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIn(List values) { + addCriterion("update_time in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotIn(List values) { + addCriterion("update_time not in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeBetween(Long value1, Long value2) { + addCriterion("update_time between", value1, value2, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotBetween(Long value1, Long value2) { + addCriterion("update_time not between", value1, value2, "updateTime"); + return (Criteria) this; + } + } + + public static class Criteria extends GeneratedCriteria { + + protected Criteria() { + super(); + } + } + + public static class Criterion { + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (value instanceof List) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, null); + } + + protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java new file mode 100644 index 0000000000..b70459e976 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java @@ -0,0 +1,18 @@ +package io.metersphere.base.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ApiDefinitionExecResult implements Serializable { + private String id; + private String resourceId; + private String name; + private String content; + private String status; + private String userId; + private Long startTime; + private Long endTime; + +} diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiModule.java b/backend/src/main/java/io/metersphere/base/domain/ApiModule.java new file mode 100644 index 0000000000..851c52abe2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiModule.java @@ -0,0 +1,26 @@ +package io.metersphere.base.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ApiModule implements Serializable { + private String id; + + private String projectId; + + private String name; + + private String parentId; + + private Integer level; + + private String protocol; + + private Long createTime; + + private Long updateTime; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiModuleExample.java b/backend/src/main/java/io/metersphere/base/domain/ApiModuleExample.java new file mode 100644 index 0000000000..5a5d51e771 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiModuleExample.java @@ -0,0 +1,666 @@ +package io.metersphere.base.domain; + +import java.util.ArrayList; +import java.util.List; + +public class ApiModuleExample { + protected String orderByClause; + + protected boolean distinct; + + protected List oredCriteria; + + public ApiModuleExample() { + oredCriteria = new ArrayList(); + } + + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + + public String getOrderByClause() { + return orderByClause; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isDistinct() { + return distinct; + } + + public List getOredCriteria() { + return oredCriteria; + } + + public void or(Criteria criteria) { + oredCriteria.add(criteria); + } + + public Criteria or() { + Criteria criteria = createCriteriaInternal(); + oredCriteria.add(criteria); + return criteria; + } + + public Criteria createCriteria() { + Criteria criteria = createCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add(criteria); + } + return criteria; + } + + protected Criteria createCriteriaInternal() { + Criteria criteria = new Criteria(); + return criteria; + } + + public void clear() { + oredCriteria.clear(); + orderByClause = null; + distinct = false; + } + + protected abstract static class GeneratedCriteria { + protected List criteria; + + protected GeneratedCriteria() { + super(); + criteria = new ArrayList(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getAllCriteria() { + return criteria; + } + + public List getCriteria() { + return criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(String condition, Object value, String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value)); + } + + protected void addCriterion(String condition, Object value1, Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public Criteria andIdIsNull() { + addCriterion("id is null"); + return (Criteria) this; + } + + public Criteria andIdIsNotNull() { + addCriterion("id is not null"); + return (Criteria) this; + } + + public Criteria andIdEqualTo(String value) { + addCriterion("id =", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotEqualTo(String value) { + addCriterion("id <>", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThan(String value) { + addCriterion("id >", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThanOrEqualTo(String value) { + addCriterion("id >=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThan(String value) { + addCriterion("id <", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThanOrEqualTo(String value) { + addCriterion("id <=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLike(String value) { + addCriterion("id like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotLike(String value) { + addCriterion("id not like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdIn(List values) { + addCriterion("id in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdNotIn(List values) { + addCriterion("id not in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdBetween(String value1, String value2) { + addCriterion("id between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andIdNotBetween(String value1, String value2) { + addCriterion("id not between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNull() { + addCriterion("project_id is null"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNotNull() { + addCriterion("project_id is not null"); + return (Criteria) this; + } + + public Criteria andProjectIdEqualTo(String value) { + addCriterion("project_id =", value, "projectId"); + return (Criteria) this; + } + + + public Criteria andProtocolEqualTo(String value) { + addCriterion("protocol =", value, "protocol"); + return (Criteria) this; + } + + public Criteria andProjectIdNotEqualTo(String value) { + addCriterion("project_id <>", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThan(String value) { + addCriterion("project_id >", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThanOrEqualTo(String value) { + addCriterion("project_id >=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThan(String value) { + addCriterion("project_id <", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThanOrEqualTo(String value) { + addCriterion("project_id <=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLike(String value) { + addCriterion("project_id like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotLike(String value) { + addCriterion("project_id not like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdIn(List values) { + addCriterion("project_id in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotIn(List values) { + addCriterion("project_id not in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdBetween(String value1, String value2) { + addCriterion("project_id between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotBetween(String value1, String value2) { + addCriterion("project_id not between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andNameIsNull() { + addCriterion("name is null"); + return (Criteria) this; + } + + public Criteria andNameIsNotNull() { + addCriterion("name is not null"); + return (Criteria) this; + } + + public Criteria andNameEqualTo(String value) { + addCriterion("name =", value, "name"); + return (Criteria) this; + } + + public Criteria andNameNotEqualTo(String value) { + addCriterion("name <>", value, "name"); + return (Criteria) this; + } + + public Criteria andNameGreaterThan(String value) { + addCriterion("name >", value, "name"); + return (Criteria) this; + } + + public Criteria andNameGreaterThanOrEqualTo(String value) { + addCriterion("name >=", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLessThan(String value) { + addCriterion("name <", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLessThanOrEqualTo(String value) { + addCriterion("name <=", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLike(String value) { + addCriterion("name like", value, "name"); + return (Criteria) this; + } + + public Criteria andNameNotLike(String value) { + addCriterion("name not like", value, "name"); + return (Criteria) this; + } + + public Criteria andNameIn(List values) { + addCriterion("name in", values, "name"); + return (Criteria) this; + } + + public Criteria andNameNotIn(List values) { + addCriterion("name not in", values, "name"); + return (Criteria) this; + } + + public Criteria andNameBetween(String value1, String value2) { + addCriterion("name between", value1, value2, "name"); + return (Criteria) this; + } + + public Criteria andNameNotBetween(String value1, String value2) { + addCriterion("name not between", value1, value2, "name"); + return (Criteria) this; + } + + public Criteria andParentIdIsNull() { + addCriterion("parent_id is null"); + return (Criteria) this; + } + + public Criteria andParentIdIsNotNull() { + addCriterion("parent_id is not null"); + return (Criteria) this; + } + + public Criteria andParentIdEqualTo(String value) { + addCriterion("parent_id =", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdNotEqualTo(String value) { + addCriterion("parent_id <>", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdGreaterThan(String value) { + addCriterion("parent_id >", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdGreaterThanOrEqualTo(String value) { + addCriterion("parent_id >=", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdLessThan(String value) { + addCriterion("parent_id <", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdLessThanOrEqualTo(String value) { + addCriterion("parent_id <=", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdLike(String value) { + addCriterion("parent_id like", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdNotLike(String value) { + addCriterion("parent_id not like", value, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdIn(List values) { + addCriterion("parent_id in", values, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdNotIn(List values) { + addCriterion("parent_id not in", values, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdBetween(String value1, String value2) { + addCriterion("parent_id between", value1, value2, "parentId"); + return (Criteria) this; + } + + public Criteria andParentIdNotBetween(String value1, String value2) { + addCriterion("parent_id not between", value1, value2, "parentId"); + return (Criteria) this; + } + + public Criteria andLevelIsNull() { + addCriterion("level is null"); + return (Criteria) this; + } + + public Criteria andLevelIsNotNull() { + addCriterion("level is not null"); + return (Criteria) this; + } + + public Criteria andLevelEqualTo(Integer value) { + addCriterion("level =", value, "level"); + return (Criteria) this; + } + + public Criteria andLevelNotEqualTo(Integer value) { + addCriterion("level <>", value, "level"); + return (Criteria) this; + } + + public Criteria andLevelGreaterThan(Integer value) { + addCriterion("level >", value, "level"); + return (Criteria) this; + } + + public Criteria andLevelGreaterThanOrEqualTo(Integer value) { + addCriterion("level >=", value, "level"); + return (Criteria) this; + } + + public Criteria andLevelLessThan(Integer value) { + addCriterion("level <", value, "level"); + return (Criteria) this; + } + + public Criteria andLevelLessThanOrEqualTo(Integer value) { + addCriterion("level <=", value, "level"); + return (Criteria) this; + } + + public Criteria andLevelIn(List values) { + addCriterion("level in", values, "level"); + return (Criteria) this; + } + + public Criteria andLevelNotIn(List values) { + addCriterion("level not in", values, "level"); + return (Criteria) this; + } + + public Criteria andLevelBetween(Integer value1, Integer value2) { + addCriterion("level between", value1, value2, "level"); + return (Criteria) this; + } + + public Criteria andLevelNotBetween(Integer value1, Integer value2) { + addCriterion("level not between", value1, value2, "level"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNull() { + addCriterion("create_time is null"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNotNull() { + addCriterion("create_time is not null"); + return (Criteria) this; + } + + public Criteria andCreateTimeEqualTo(Long value) { + addCriterion("create_time =", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotEqualTo(Long value) { + addCriterion("create_time <>", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThan(Long value) { + addCriterion("create_time >", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("create_time >=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThan(Long value) { + addCriterion("create_time <", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThanOrEqualTo(Long value) { + addCriterion("create_time <=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeIn(List values) { + addCriterion("create_time in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotIn(List values) { + addCriterion("create_time not in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeBetween(Long value1, Long value2) { + addCriterion("create_time between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotBetween(Long value1, Long value2) { + addCriterion("create_time not between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNull() { + addCriterion("update_time is null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNotNull() { + addCriterion("update_time is not null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeEqualTo(Long value) { + addCriterion("update_time =", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotEqualTo(Long value) { + addCriterion("update_time <>", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThan(Long value) { + addCriterion("update_time >", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("update_time >=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThan(Long value) { + addCriterion("update_time <", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThanOrEqualTo(Long value) { + addCriterion("update_time <=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIn(List values) { + addCriterion("update_time in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotIn(List values) { + addCriterion("update_time not in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeBetween(Long value1, Long value2) { + addCriterion("update_time between", value1, value2, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotBetween(Long value1, Long value2) { + addCriterion("update_time not between", value1, value2, "updateTime"); + return (Criteria) this; + } + } + + public static class Criteria extends GeneratedCriteria { + + protected Criteria() { + super(); + } + } + + public static class Criterion { + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (value instanceof List) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, null); + } + + protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiTestCase.java b/backend/src/main/java/io/metersphere/base/domain/ApiTestCase.java new file mode 100644 index 0000000000..962634cc1a --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiTestCase.java @@ -0,0 +1,34 @@ +package io.metersphere.base.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ApiTestCase implements Serializable { + private String id; + + private String projectId; + + private String name; + + private String priority; + + private String apiDefinitionId; + + private String description; + + private String request; + + private String response; + + private String createUserId; + + private String updateUserId; + + private Long createTime; + + private Long updateTime; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiTestCaseExample.java b/backend/src/main/java/io/metersphere/base/domain/ApiTestCaseExample.java new file mode 100644 index 0000000000..a6d6fc1ef1 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiTestCaseExample.java @@ -0,0 +1,751 @@ +package io.metersphere.base.domain; + +import java.util.ArrayList; +import java.util.List; + +public class ApiTestCaseExample { + protected String orderByClause; + + protected boolean distinct; + + protected List oredCriteria; + + public ApiTestCaseExample() { + oredCriteria = new ArrayList(); + } + + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + + public String getOrderByClause() { + return orderByClause; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isDistinct() { + return distinct; + } + + public List getOredCriteria() { + return oredCriteria; + } + + public void or(Criteria criteria) { + oredCriteria.add(criteria); + } + + public Criteria or() { + Criteria criteria = createCriteriaInternal(); + oredCriteria.add(criteria); + return criteria; + } + + public Criteria createCriteria() { + Criteria criteria = createCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add(criteria); + } + return criteria; + } + + protected Criteria createCriteriaInternal() { + Criteria criteria = new Criteria(); + return criteria; + } + + public void clear() { + oredCriteria.clear(); + orderByClause = null; + distinct = false; + } + + protected abstract static class GeneratedCriteria { + protected List criteria; + + protected GeneratedCriteria() { + super(); + criteria = new ArrayList(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getAllCriteria() { + return criteria; + } + + public List getCriteria() { + return criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(String condition, Object value, String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value)); + } + + protected void addCriterion(String condition, Object value1, Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public Criteria andIdIsNull() { + addCriterion("id is null"); + return (Criteria) this; + } + + public Criteria andIdIsNotNull() { + addCriterion("id is not null"); + return (Criteria) this; + } + + public Criteria andIdEqualTo(String value) { + addCriterion("id =", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotEqualTo(String value) { + addCriterion("id <>", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThan(String value) { + addCriterion("id >", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThanOrEqualTo(String value) { + addCriterion("id >=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThan(String value) { + addCriterion("id <", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThanOrEqualTo(String value) { + addCriterion("id <=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLike(String value) { + addCriterion("id like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotLike(String value) { + addCriterion("id not like", value, "id"); + return (Criteria) this; + } + + public Criteria andIdIn(List values) { + addCriterion("id in", values, "id"); + return (Criteria) this; + } + + public Criteria andModuleIdIn(List values) { + addCriterion("module_id in", values, "module_id"); + return (Criteria) this; + } + + public Criteria andIdNotIn(List values) { + addCriterion("id not in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdBetween(String value1, String value2) { + addCriterion("id between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andIdNotBetween(String value1, String value2) { + addCriterion("id not between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNull() { + addCriterion("project_id is null"); + return (Criteria) this; + } + + public Criteria andProjectIdIsNotNull() { + addCriterion("project_id is not null"); + return (Criteria) this; + } + + public Criteria andProjectIdEqualTo(String value) { + addCriterion("project_id =", value, "projectId"); + return (Criteria) this; + } + + public Criteria andApiDefinitionIdEqualTo(String value) { + addCriterion("api_definition_id =", value, "api_definition_id"); + return (Criteria) this; + } + + + public Criteria andProjectIdNotEqualTo(String value) { + addCriterion("project_id <>", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThan(String value) { + addCriterion("project_id >", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdGreaterThanOrEqualTo(String value) { + addCriterion("project_id >=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThan(String value) { + addCriterion("project_id <", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLessThanOrEqualTo(String value) { + addCriterion("project_id <=", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdLike(String value) { + addCriterion("project_id like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotLike(String value) { + addCriterion("project_id not like", value, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdIn(List values) { + addCriterion("project_id in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotIn(List values) { + addCriterion("project_id not in", values, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdBetween(String value1, String value2) { + addCriterion("project_id between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andProjectIdNotBetween(String value1, String value2) { + addCriterion("project_id not between", value1, value2, "projectId"); + return (Criteria) this; + } + + public Criteria andNameIsNull() { + addCriterion("name is null"); + return (Criteria) this; + } + + public Criteria andNameIsNotNull() { + addCriterion("name is not null"); + return (Criteria) this; + } + + public Criteria andNameEqualTo(String value) { + addCriterion("name =", value, "name"); + return (Criteria) this; + } + + public Criteria andNameNotEqualTo(String value) { + addCriterion("name <>", value, "name"); + return (Criteria) this; + } + + public Criteria andNameGreaterThan(String value) { + addCriterion("name >", value, "name"); + return (Criteria) this; + } + + public Criteria andNameGreaterThanOrEqualTo(String value) { + addCriterion("name >=", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLessThan(String value) { + addCriterion("name <", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLessThanOrEqualTo(String value) { + addCriterion("name <=", value, "name"); + return (Criteria) this; + } + + public Criteria andNameLike(String value) { + addCriterion("name like", value, "name"); + return (Criteria) this; + } + + public Criteria andNameNotLike(String value) { + addCriterion("name not like", value, "name"); + return (Criteria) this; + } + + public Criteria andNameIn(List values) { + addCriterion("name in", values, "name"); + return (Criteria) this; + } + + public Criteria andNameNotIn(List values) { + addCriterion("name not in", values, "name"); + return (Criteria) this; + } + + public Criteria andNameBetween(String value1, String value2) { + addCriterion("name between", value1, value2, "name"); + return (Criteria) this; + } + + public Criteria andNameNotBetween(String value1, String value2) { + addCriterion("name not between", value1, value2, "name"); + return (Criteria) this; + } + + public Criteria andDescriptionIsNull() { + addCriterion("description is null"); + return (Criteria) this; + } + + public Criteria andDescriptionIsNotNull() { + addCriterion("description is not null"); + return (Criteria) this; + } + + public Criteria andDescriptionEqualTo(String value) { + addCriterion("description =", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotEqualTo(String value) { + addCriterion("description <>", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionGreaterThan(String value) { + addCriterion("description >", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionGreaterThanOrEqualTo(String value) { + addCriterion("description >=", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLessThan(String value) { + addCriterion("description <", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLessThanOrEqualTo(String value) { + addCriterion("description <=", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionLike(String value) { + addCriterion("description like", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotLike(String value) { + addCriterion("description not like", value, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionIn(List values) { + addCriterion("description in", values, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotIn(List values) { + addCriterion("description not in", values, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionBetween(String value1, String value2) { + addCriterion("description between", value1, value2, "description"); + return (Criteria) this; + } + + public Criteria andDescriptionNotBetween(String value1, String value2) { + addCriterion("description not between", value1, value2, "description"); + return (Criteria) this; + } + + public Criteria andStatusIsNull() { + addCriterion("status is null"); + return (Criteria) this; + } + + public Criteria andStatusIsNotNull() { + addCriterion("status is not null"); + return (Criteria) this; + } + + public Criteria andStatusEqualTo(String value) { + addCriterion("status =", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotEqualTo(String value) { + addCriterion("status <>", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThan(String value) { + addCriterion("status >", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThanOrEqualTo(String value) { + addCriterion("status >=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThan(String value) { + addCriterion("status <", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThanOrEqualTo(String value) { + addCriterion("status <=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLike(String value) { + addCriterion("status like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotLike(String value) { + addCriterion("status not like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusIn(List values) { + addCriterion("status in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotIn(List values) { + addCriterion("status not in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusBetween(String value1, String value2) { + addCriterion("status between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotBetween(String value1, String value2) { + addCriterion("status not between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andUserIdIsNull() { + addCriterion("user_id is null"); + return (Criteria) this; + } + + public Criteria andUserIdIsNotNull() { + addCriterion("user_id is not null"); + return (Criteria) this; + } + + public Criteria andUserIdEqualTo(String value) { + addCriterion("user_id =", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotEqualTo(String value) { + addCriterion("user_id <>", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdGreaterThan(String value) { + addCriterion("user_id >", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdGreaterThanOrEqualTo(String value) { + addCriterion("user_id >=", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdLessThan(String value) { + addCriterion("user_id <", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdLessThanOrEqualTo(String value) { + addCriterion("user_id <=", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdLike(String value) { + addCriterion("user_id like", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotLike(String value) { + addCriterion("user_id not like", value, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdIn(List values) { + addCriterion("user_id in", values, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotIn(List values) { + addCriterion("user_id not in", values, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdBetween(String value1, String value2) { + addCriterion("user_id between", value1, value2, "userId"); + return (Criteria) this; + } + + public Criteria andUserIdNotBetween(String value1, String value2) { + addCriterion("user_id not between", value1, value2, "userId"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNull() { + addCriterion("create_time is null"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNotNull() { + addCriterion("create_time is not null"); + return (Criteria) this; + } + + public Criteria andCreateTimeEqualTo(Long value) { + addCriterion("create_time =", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotEqualTo(Long value) { + addCriterion("create_time <>", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThan(Long value) { + addCriterion("create_time >", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("create_time >=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThan(Long value) { + addCriterion("create_time <", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThanOrEqualTo(Long value) { + addCriterion("create_time <=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeIn(List values) { + addCriterion("create_time in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotIn(List values) { + addCriterion("create_time not in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeBetween(Long value1, Long value2) { + addCriterion("create_time between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotBetween(Long value1, Long value2) { + addCriterion("create_time not between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNull() { + addCriterion("update_time is null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIsNotNull() { + addCriterion("update_time is not null"); + return (Criteria) this; + } + + public Criteria andUpdateTimeEqualTo(Long value) { + addCriterion("update_time =", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotEqualTo(Long value) { + addCriterion("update_time <>", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThan(Long value) { + addCriterion("update_time >", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("update_time >=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThan(Long value) { + addCriterion("update_time <", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeLessThanOrEqualTo(Long value) { + addCriterion("update_time <=", value, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeIn(List values) { + addCriterion("update_time in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotIn(List values) { + addCriterion("update_time not in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeBetween(Long value1, Long value2) { + addCriterion("update_time between", value1, value2, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotBetween(Long value1, Long value2) { + addCriterion("update_time not between", value1, value2, "updateTime"); + return (Criteria) this; + } + } + + public static class Criteria extends GeneratedCriteria { + + protected Criteria() { + super(); + } + } + + public static class Criterion { + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (value instanceof List) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, null); + } + + protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java new file mode 100644 index 0000000000..259c9cec55 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java @@ -0,0 +1,15 @@ +package io.metersphere.base.mapper; + +import io.metersphere.base.domain.ApiDefinitionExecResult; + +public interface ApiDefinitionExecResultMapper { + + int deleteByResourceId(String id); + + int insert(ApiDefinitionExecResult record); + + ApiDefinitionExecResult selectByResourceId(String resourceId); + + ApiDefinitionExecResult selectByPrimaryKey(String id); + +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml new file mode 100644 index 0000000000..92d1cc3075 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml @@ -0,0 +1,27 @@ + + + + + + delete from api_definition_exec_result where resource_id = #{id,jdbcType=VARCHAR} + + + + insert into api_definition_exec_result + (id, resource_id,name,content, status, user_id, start_time, end_time) + values + (#{id,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARCHAR}, #{status,jdbcType=VARCHAR}, #{userId,jdbcType=VARCHAR}, + #{startTime,jdbcType=BIGINT}, #{endTime,jdbcType=BIGINT}) + + + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.java new file mode 100644 index 0000000000..8fbc57deb3 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.java @@ -0,0 +1,42 @@ +package io.metersphere.base.mapper; + +import io.metersphere.api.dto.definition.ApiComputeResult; +import io.metersphere.api.dto.definition.ApiDefinitionRequest; +import io.metersphere.api.dto.definition.ApiDefinitionResult; +import io.metersphere.base.domain.ApiDefinition; +import io.metersphere.base.domain.ApiDefinitionExample; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ApiDefinitionMapper { + + List list(@Param("request") ApiDefinitionRequest request); + + List selectByIds(@Param("ids") List ids); + + long countByExample(ApiDefinitionExample example); + + int deleteByExample(ApiDefinitionExample example); + + int deleteByPrimaryKey(String id); + + int insert(ApiDefinition record); + + + List selectByExampleWithBLOBs(ApiDefinitionExample example); + + List selectByExample(ApiDefinitionExample example); + + ApiDefinition selectByPrimaryKey(String id); + + + int updateByPrimaryKeySelective(ApiDefinition record); + + int updateByPrimaryKeyWithBLOBs(ApiDefinition record); + + int updateByPrimaryKey(ApiDefinition record); + + int removeToGc(@Param("ids") List ids); + +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml new file mode 100644 index 0000000000..4ab05ab297 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionMapper.xml @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + id, project_id, name,module_id,module_path,protocol ,path,method ,description, status, user_id, create_time, update_time + + + request + + + + + + + + + and api_definition.name + + + + + + and api_definition.update_time + + + + + + and project.name + + + + + + and api_definition.create_time + + + + + + and api_definition.status + + + + + + and api_definition.user_id + + + + + + + + + + like CONCAT('%', #{${object}.value},'%') + + + not like CONCAT('%', #{${object}.value},'%') + + + in + + #{v} + + + + not in + + #{v} + + + + between #{${object}.value[0]} and #{${object}.value[1]} + + + > #{${object}.value} + + + < #{${object}.value} + + + >= #{${object}.value} + + + <= #{${object}.value} + + + = '${@io.metersphere.commons.utils.SessionUtils@getUserId()}' + + + = #{${object}.value} + + + + + + + + + + + delete from api_definition + where id = #{id,jdbcType=VARCHAR} + + + + delete from api_definition + + + + + + + insert into api_definition (id, project_id, name, protocol,path,module_id,module_path,method, + description, status, user_id,create_time, update_time, request,response,environment_id ) + values + (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{protocol,jdbcType=VARCHAR},#{path,jdbcType=VARCHAR}, #{moduleId,jdbcType=VARCHAR},#{modulePath,jdbcType=VARCHAR},#{method,jdbcType=VARCHAR}, + #{description,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{userId,jdbcType=VARCHAR}, + #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{request,jdbcType=LONGVARCHAR},#{response,jdbcType=LONGVARCHAR},#{environmentId,jdbcType=VARCHAR} + ) + + + + + + update api_definition + + + project_id = #{projectId,jdbcType=VARCHAR}, + + + name = #{name,jdbcType=VARCHAR}, + + + + module_id = #{moduleId,jdbcType=VARCHAR}, + + + protocol = #{protocol,jdbcType=VARCHAR}, + + + path = #{path,jdbcType=VARCHAR}, + + + method = #{method,jdbcType=VARCHAR}, + + + module_path = #{modulePath,jdbcType=VARCHAR}, + + + description = #{description,jdbcType=VARCHAR}, + + + status = #{status,jdbcType=VARCHAR}, + + + user_id = #{userId,jdbcType=VARCHAR}, + + + create_time = #{createTime,jdbcType=BIGINT}, + + + update_time = #{updateTime,jdbcType=BIGINT}, + + + request = #{request,jdbcType=LONGVARCHAR}, + + + response = #{response,jdbcType=LONGVARCHAR}, + + + environment_id = #{environmentId,jdbcType=VARCHAR}, + + + where id = #{id,jdbcType=VARCHAR} + + + update api_definition + set project_id = #{projectId,jdbcType=VARCHAR}, + name = #{name,jdbcType=VARCHAR}, + description = #{description,jdbcType=VARCHAR}, + status = #{status,jdbcType=VARCHAR}, + user_id = #{userId,jdbcType=VARCHAR}, + module_id = #{moduleId,jdbcType=VARCHAR}, + module_path = #{modulePath,jdbcType=VARCHAR}, + protocol = #{protocol,jdbcType=VARCHAR}, + path = #{path,jdbcType=VARCHAR}, + method = #{method,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT}, + update_time = #{updateTime,jdbcType=BIGINT}, + request = #{request,jdbcType=LONGVARCHAR}, + response = #{response,jdbcType=LONGVARCHAR}, + environment_id = #{environmentId,jdbcType=VARCHAR} + where id = #{id,jdbcType=VARCHAR} + + + update api_definition + set project_id = #{projectId,jdbcType=VARCHAR}, + name = #{name,jdbcType=VARCHAR}, + module_id = #{moduleId,jdbcType=VARCHAR}, + protocol = #{protocol,jdbcType=VARCHAR}, + path = #{path,jdbcType=VARCHAR}, + module_path = #{modulePath,jdbcType=VARCHAR}, + method = #{method,jdbcType=VARCHAR}, + description = #{description,jdbcType=VARCHAR}, + status = #{status,jdbcType=VARCHAR}, + user_id = #{userId,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT}, + update_time = #{updateTime,jdbcType=BIGINT} + where id = #{id,jdbcType=VARCHAR} + + + + update api_definition + set + status = 'Trash' + where id in + + #{v} + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiModuleMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiModuleMapper.java new file mode 100644 index 0000000000..65000c3129 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiModuleMapper.java @@ -0,0 +1,34 @@ +package io.metersphere.base.mapper; + +import io.metersphere.base.domain.ApiModule; +import io.metersphere.base.domain.ApiModuleExample; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ApiModuleMapper { + + long countByExample(ApiModuleExample example); + + int deleteByExample(ApiModuleExample example); + + int deleteByPrimaryKey(String id); + + int insert(ApiModule record); + + int insertBatch(@Param("records") List records); + + int insertSelective(ApiModule record); + + List selectByExample(ApiModuleExample example); + + ApiModule selectByPrimaryKey(String id); + + int updateByExampleSelective(@Param("record") ApiModule record, @Param("example") ApiModuleExample example); + + int updateByExample(@Param("record") ApiModule record, @Param("example") ApiModuleExample example); + + int updateByPrimaryKeySelective(ApiModule record); + + int updateByPrimaryKey(ApiModule record); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiModuleMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiModuleMapper.xml new file mode 100644 index 0000000000..8db13efb87 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiModuleMapper.xml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + id, project_id, name, parent_id, level, create_time, update_time + + + + + delete from api_module + where id = #{id,jdbcType=VARCHAR} + + + delete from api_module + + + + + + + insert into api_module (id, project_id, name,protocol + parent_id, level, create_time, + update_time) + values + + (#{emp.id,jdbcType=VARCHAR}, #{emp.projectId,jdbcType=VARCHAR}, + #{emp.name,jdbcType=VARCHAR},#{emp.protocol,jdbcType=VARCHAR}, + #{emp.parentId,jdbcType=VARCHAR}, #{emp.level,jdbcType=INTEGER}, #{emp.createTime,jdbcType=BIGINT}, + #{emp.updateTime,jdbcType=BIGINT}) + + + + + insert into api_module (id, project_id, name,protocol + parent_id, level, create_time, + update_time) + values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},#{protocol,jdbcType=VARCHAR}, + #{parentId,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER}, #{createTime,jdbcType=BIGINT}, + #{updateTime,jdbcType=BIGINT}) + + + + insert into api_module + + + id, + + + project_id, + + + name, + + + protocol, + + + parent_id, + + + level, + + + create_time, + + + update_time, + + + + + #{id,jdbcType=VARCHAR}, + + + #{projectId,jdbcType=VARCHAR}, + + + #{name,jdbcType=VARCHAR}, + + + #{protocol,jdbcType=VARCHAR}, + + + #{parentId,jdbcType=VARCHAR}, + + + #{level,jdbcType=INTEGER}, + + + #{createTime,jdbcType=BIGINT}, + + + #{updateTime,jdbcType=BIGINT}, + + + + + + update api_module + + + id = #{record.id,jdbcType=VARCHAR}, + + + project_id = #{record.projectId,jdbcType=VARCHAR}, + + + name = #{record.name,jdbcType=VARCHAR}, + + + protocol = #{record.protocol,jdbcType=VARCHAR}, + + + + parent_id = #{record.parentId,jdbcType=VARCHAR}, + + + level = #{record.level,jdbcType=INTEGER}, + + + create_time = #{record.createTime,jdbcType=BIGINT}, + + + update_time = #{record.updateTime,jdbcType=BIGINT}, + + + + + + + + update api_module + set id = #{record.id,jdbcType=VARCHAR}, + project_id = #{record.projectId,jdbcType=VARCHAR}, + name = #{record.name,jdbcType=VARCHAR}, + protocol = #{protocol,jdbcType=VARCHAR}, + parent_id = #{record.parentId,jdbcType=VARCHAR}, + level = #{record.level,jdbcType=INTEGER}, + create_time = #{record.createTime,jdbcType=BIGINT}, + update_time = #{record.updateTime,jdbcType=BIGINT} + + + + + + update api_module + + + project_id = #{projectId,jdbcType=VARCHAR}, + + + name = #{name,jdbcType=VARCHAR}, + + + protocol = #{protocol,jdbcType=VARCHAR}, + + + + parent_id = #{parentId,jdbcType=VARCHAR}, + + + level = #{level,jdbcType=INTEGER}, + + + create_time = #{createTime,jdbcType=BIGINT}, + + + update_time = #{updateTime,jdbcType=BIGINT}, + + + where id = #{id,jdbcType=VARCHAR} + + + update api_module + set project_id = #{projectId,jdbcType=VARCHAR}, + name = #{name,jdbcType=VARCHAR}, + protocol = #{protocol,jdbcType=VARCHAR}, + parent_id = #{parentId,jdbcType=VARCHAR}, + level = #{level,jdbcType=INTEGER}, + create_time = #{createTime,jdbcType=BIGINT}, + update_time = #{updateTime,jdbcType=BIGINT} + where id = #{id,jdbcType=VARCHAR} + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiTestCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiTestCaseMapper.java new file mode 100644 index 0000000000..1287fa06c8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiTestCaseMapper.java @@ -0,0 +1,32 @@ +package io.metersphere.base.mapper; + +import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.ApiTestCaseResult; +import io.metersphere.base.domain.ApiTestCase; +import io.metersphere.base.domain.ApiTestCaseExample; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ApiTestCaseMapper { + + List list(@Param("request") ApiTestCaseRequest request); + + long countByExample(ApiTestCaseExample example); + + int deleteByExample(ApiTestCaseExample example); + + int deleteByPrimaryKey(String id); + + int insert(ApiTestCase record); + + List selectByExampleWithBLOBs(ApiTestCaseExample example); + + List selectByExample(ApiTestCaseExample example); + + ApiTestCase selectByPrimaryKey(String id); + + int updateByPrimaryKeySelective(ApiTestCase record); + + int updateByPrimaryKey(ApiTestCase record); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiTestCaseMapper.xml new file mode 100644 index 0000000000..8e9002107e --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiTestCaseMapper.xml @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + id, project_id, name,api_definition_id,priority,description, create_user_id, update_user_id, create_time, update_time + + + request + + + + + + + like CONCAT('%', #{${object}.value},'%') + + + not like CONCAT('%', #{${object}.value},'%') + + + in + + #{v} + + + + not in + + #{v} + + + + between #{${object}.value[0]} and #{${object}.value[1]} + + + > #{${object}.value} + + + < #{${object}.value} + + + >= #{${object}.value} + + + <= #{${object}.value} + + + = '${@io.metersphere.commons.utils.SessionUtils@getUserId()}' + + + = #{${object}.value} + + + + + + + + + + + + delete from api_test_case where id = #{id,jdbcType=VARCHAR} + + + + delete from api_test_case + + + + + + + insert into api_test_case (id, project_id, name, priority,api_definition_id,description, create_user_id, update_user_id, + create_time, update_time, request,response + ) + values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{priority,jdbcType=VARCHAR}, #{apiDefinitionId,jdbcType=VARCHAR},#{description,jdbcType=VARCHAR}, + #{createUserId,jdbcType=VARCHAR}, #{updateUserId,jdbcType=VARCHAR}, + #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{request,jdbcType=LONGVARCHAR}, #{response,jdbcType=LONGVARCHAR}) + + + + + + update api_test_case + + + project_id = #{projectId,jdbcType=VARCHAR}, + + + name = #{name,jdbcType=VARCHAR}, + + + + api_definition_id = #{apiDefinitionId,jdbcType=BIGINT}, + + + priority = #{priority,jdbcType=LONGVARCHAR}, + + + description = #{description,jdbcType=VARCHAR}, + + + update_user_id = #{updateUserId,jdbcType=VARCHAR}, + + + update_time = #{updateTime,jdbcType=BIGINT}, + + + request = #{request,jdbcType=LONGVARCHAR}, + + + response = #{response,jdbcType=LONGVARCHAR}, + + + + where id = #{id,jdbcType=VARCHAR} + + + + update api_test_case + set project_id = #{projectId,jdbcType=VARCHAR}, + name = #{name,jdbcType=VARCHAR}, + api_definition_id = #{apiDefinitionId,jdbcType=VARCHAR}, + priority = #{priority,jdbcType=VARCHAR}, + description = #{description,jdbcType=VARCHAR}, + update_user_id = #{updateUserId,jdbcType=VARCHAR}, + update_time = #{updateTime,jdbcType=BIGINT} + where id = #{id,jdbcType=VARCHAR} + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java b/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java index abb0873aa3..69587b0f5e 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java +++ b/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum APITestStatus { - Saved, Starting, Running, Reporting, Completed, Debug, Error, Success + Saved, Starting, Running, Reporting, Completed, Debug, Error, Success,Underway } diff --git a/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java b/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java index 1d422af7fb..c751fea90d 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum ApiRunMode { - RUN, DEBUG + RUN, DEBUG,DELIMIT } diff --git a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java index 91b1a11f38..f1f7e91ee1 100644 --- a/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java +++ b/backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelDataUs.java @@ -8,6 +8,7 @@ import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; + @Data @ColumnWidth(15) public class TestCaseExcelDataUs extends TestCaseExcelData { diff --git a/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java b/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java index ae0c872574..825f0de177 100644 --- a/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java +++ b/backend/src/main/java/io/metersphere/job/sechedule/ApiTestJob.java @@ -2,7 +2,6 @@ package io.metersphere.job.sechedule; import io.metersphere.api.dto.SaveAPITestRequest; import io.metersphere.api.service.APITestService; -import io.metersphere.notice.service.MailService; import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.utils.CommonBeanFactory; diff --git a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java index f962b1f431..fdfbd929ce 100644 --- a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -491,9 +491,9 @@ public class JmeterDocumentParser implements DocumentParser { setupElement.setAttribute("testclass", "SetupThreadGroup"); setupElement.setAttribute("testname", "setUp Thread Group"); setupElement.setAttribute("enabled", "true"); - setupElement.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "stoptestnow")); + setupElement.appendChild(createStringProp(document, "MsThreadGroup.on_sample_error", "stoptestnow")); Element elementProp = document.createElement("elementProp"); - elementProp.setAttribute("name", "ThreadGroup.main_controller"); + elementProp.setAttribute("name", "MsThreadGroup.main_controller"); elementProp.setAttribute("elementType", "LoopController"); elementProp.setAttribute("guiclass", "LoopControlPanel"); elementProp.setAttribute("testclass", "LoopController"); @@ -502,12 +502,12 @@ public class JmeterDocumentParser implements DocumentParser { elementProp.appendChild(createBoolProp(document, "LoopController.continue_forever", false)); elementProp.appendChild(createIntProp(document, "LoopController.loops", 1)); setupElement.appendChild(elementProp); - setupElement.appendChild(createStringProp(document, "ThreadGroup.num_threads", "1")); - setupElement.appendChild(createStringProp(document, "ThreadGroup.ramp_time", "1")); - setupElement.appendChild(createStringProp(document, "ThreadGroup.duration", "")); - setupElement.appendChild(createStringProp(document, "ThreadGroup.delay", "")); - setupElement.appendChild(createBoolProp(document, "ThreadGroup.scheduler", false)); - setupElement.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true)); + setupElement.appendChild(createStringProp(document, "MsThreadGroup.num_threads", "1")); + setupElement.appendChild(createStringProp(document, "MsThreadGroup.ramp_time", "1")); + setupElement.appendChild(createStringProp(document, "MsThreadGroup.duration", "")); + setupElement.appendChild(createStringProp(document, "MsThreadGroup.delay", "")); + setupElement.appendChild(createBoolProp(document, "MsThreadGroup.scheduler", false)); + setupElement.appendChild(createBoolProp(document, "MsThreadGroup.same_user_on_next_iteration", true)); hashTree.appendChild(setupElement); Element setupHashTree = document.createElement(HASH_TREE_ELEMENT); @@ -568,17 +568,17 @@ public class JmeterDocumentParser implements DocumentParser { } /* - continue - + continue + false 1 - 1 - 1 - false - - - true + 1 + 1 + false + + + true */ Element tearDownElement = document.createElement("PostThreadGroup"); @@ -586,15 +586,15 @@ public class JmeterDocumentParser implements DocumentParser { tearDownElement.setAttribute("testclass", "PostThreadGroup"); tearDownElement.setAttribute("testname", "tearDown Thread Group"); tearDownElement.setAttribute("enabled", "true"); - tearDownElement.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue")); - tearDownElement.appendChild(createStringProp(document, "ThreadGroup.num_threads", "1")); - tearDownElement.appendChild(createStringProp(document, "ThreadGroup.ramp_time", "1")); - tearDownElement.appendChild(createStringProp(document, "ThreadGroup.duration", "")); - tearDownElement.appendChild(createStringProp(document, "ThreadGroup.delay", "")); - tearDownElement.appendChild(createBoolProp(document, "ThreadGroup.scheduler", false)); - tearDownElement.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true)); + tearDownElement.appendChild(createStringProp(document, "MsThreadGroup.on_sample_error", "continue")); + tearDownElement.appendChild(createStringProp(document, "MsThreadGroup.num_threads", "1")); + tearDownElement.appendChild(createStringProp(document, "MsThreadGroup.ramp_time", "1")); + tearDownElement.appendChild(createStringProp(document, "MsThreadGroup.duration", "")); + tearDownElement.appendChild(createStringProp(document, "MsThreadGroup.delay", "")); + tearDownElement.appendChild(createBoolProp(document, "MsThreadGroup.scheduler", false)); + tearDownElement.appendChild(createBoolProp(document, "MsThreadGroup.same_user_on_next_iteration", true)); Element elementProp = document.createElement("elementProp"); - elementProp.setAttribute("name", "ThreadGroup.main_controller"); + elementProp.setAttribute("name", "MsThreadGroup.main_controller"); elementProp.setAttribute("elementType", "LoopController"); elementProp.setAttribute("guiclass", "LoopControlPanel"); elementProp.setAttribute("testclass", "LoopController"); @@ -760,8 +760,8 @@ public class JmeterDocumentParser implements DocumentParser { threadGroup.setAttribute("guiclass", CONCURRENCY_THREAD_GROUP + "Gui"); threadGroup.setAttribute("testclass", CONCURRENCY_THREAD_GROUP); /* - - continue + + continue 2 12 2 @@ -773,11 +773,11 @@ public class JmeterDocumentParser implements DocumentParser { removeChildren(threadGroup); // elementProp Element elementProp = document.createElement("elementProp"); - elementProp.setAttribute("name", "ThreadGroup.main_controller"); + elementProp.setAttribute("name", "MsThreadGroup.main_controller"); elementProp.setAttribute("elementType", "com.blazemeter.jmeter.control.VirtualUserController"); threadGroup.appendChild(elementProp); - threadGroup.appendChild(createStringProp(document, "ThreadGroup.on_sample_error", "continue")); + threadGroup.appendChild(createStringProp(document, "MsThreadGroup.on_sample_error", "continue")); threadGroup.appendChild(createStringProp(document, "TargetLevel", "2")); threadGroup.appendChild(createStringProp(document, "RampUp", "12")); threadGroup.appendChild(createStringProp(document, "Steps", "2")); diff --git a/backend/src/main/resources/db/migration/V46__api_definition.sql b/backend/src/main/resources/db/migration/V46__api_definition.sql new file mode 100644 index 0000000000..cf07a28858 --- /dev/null +++ b/backend/src/main/resources/db/migration/V46__api_definition.sql @@ -0,0 +1,69 @@ +CREATE TABLE IF NOT EXISTS `api_module` ( + `id` varchar(50) NOT NULL COMMENT 'Test case node ID', + `project_id` varchar(50) NOT NULL COMMENT 'Project ID this node belongs to', + `name` varchar(64) NOT NULL COMMENT 'Node name', + `protocol` varchar(64) NOT NULL COMMENT 'Node protocol', + `parent_id` varchar(50) DEFAULT NULL COMMENT 'Parent node ID', + `level` int(10) DEFAULT 1 COMMENT 'Node level', + `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', + `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', + PRIMARY KEY (`id`) +) + ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4; + +CREATE TABLE IF NOT EXISTS `api_definition` ( + `id` varchar(50) NOT NULL COMMENT 'Test ID', + `project_id` varchar(50) NOT NULL COMMENT 'Project ID this test belongs to', + `name` varchar(64) NOT NULL COMMENT 'Test name', + `method` varchar(64) NOT NULL COMMENT 'method', + `protocol` varchar(255) NOT NULL COMMENT 'request protocol', + `path` varchar(255) DEFAULT NULL COMMENT 'request path', + `module_path` varchar(1000) COMMENT 'module path', + `description` varchar(255) DEFAULT NULL COMMENT 'Test description', + `environment_id` varchar(50) DEFAULT NULL COMMENT 'environment id', + `request` longtext COMMENT 'request (JSON format)', + `response` longtext COMMENT 'request (JSON format)', + `schedule` varchar(255) COMMENT 'Test schedule (cron list)', + `status` varchar(64) DEFAULT NULL COMMENT 'Status of this test', + `module_id` varchar(50) DEFAULT NULL COMMENT 'module_id of this module', + `user_id` varchar(64) DEFAULT NULL COMMENT 'User ID', + `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', + `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `api_test_case` ( + `id` varchar(50) NOT NULL COMMENT 'Test ID', + `project_id` varchar(50) NOT NULL COMMENT 'Project ID this test belongs to', + `name` varchar(64) NOT NULL COMMENT 'Test name', + `priority` varchar(64) NOT NULL COMMENT 'priority', + `api_definition_id` varchar(50) NOT NULL COMMENT 'api definition id', + `description` varchar(255) DEFAULT NULL COMMENT 'Test description', + `request` longtext COMMENT 'request (JSON format)', + `response` longtext COMMENT 'response (JSON format)', + `create_user_id` varchar(64) DEFAULT NULL COMMENT 'User ID', + `update_user_id` varchar(64) DEFAULT NULL COMMENT 'User ID', + `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', + `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `api_definition_exec_result` ( + `id` varchar(50) NOT NULL COMMENT 'Test ID', + `name` varchar(64) NOT NULL COMMENT 'Test name', + `resource_id` varchar(50) NOT NULL COMMENT 'api id or testcase id ', + `content` longtext COMMENT 'request (JSON format)', + `status` varchar(50) COMMENT 'execute status', + `user_id` varchar(64) DEFAULT NULL COMMENT 'User ID', + `start_time` bigint(13) NOT NULL COMMENT 'Create timestamp', + `end_time` bigint(13) NOT NULL COMMENT 'Update timestamp', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +ALTER TABLE `api_definition` ADD INDEX ( `user_id` ); +ALTER TABLE `api_definition` ADD INDEX ( `project_id` ); +ALTER TABLE `api_test_case` ADD INDEX ( `api_definition_id` ); +ALTER TABLE `api_test_case` ADD INDEX ( `create_user_id` ); +ALTER TABLE `api_test_case` ADD INDEX ( `update_user_id` ); +ALTER TABLE `api_definition_exec_result` ADD INDEX ( `resource_id` ); diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index bc98163881..8cf497635c 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -48,7 +48,6 @@ related_case_del_fail_prefix=Connected to related_case_del_fail_suffix=TestCase, please disassociate first jmx_content_valid=JMX content is invalid container_delete_fail=The container failed to stop, please try again -load_test_report_file_not_exist=There is no JTL file in the current report, please execute it again to get it #workspace workspace_name_is_null=Workspace name cannot be null workspace_name_already_exists=The workspace name already exists @@ -168,6 +167,8 @@ check_owner_comment=The current user does not have permission to manipulate this upload_content_is_null=Imported content is empty test_plan_notification=Test plan notification task_defect_notification=Task defect notification +task_notification_=Timing task result notification +api_definition_url_not_repeating=The interface request address already exists task_notification_jenkins=Jenkins Task notification task_notification=Result notification message_task_already_exists=Task recipient already exists diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 6a4133be16..c659fa7432 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -48,7 +48,6 @@ related_case_del_fail_prefix=已关联到 related_case_del_fail_suffix=测试用例,请先解除关联 jmx_content_valid=JMX 内容无效,请检查 container_delete_fail=容器由于网络原因停止失败,请重试 -load_test_report_file_not_exist=当前报告没有JTL文件,请重新执行以便获取 #workspace workspace_name_is_null=工作空间名不能为空 workspace_name_already_exists=工作空间名已存在 @@ -169,6 +168,8 @@ check_owner_comment=当前用户没有操作此评论的权限 upload_content_is_null=导入内容为空 test_plan_notification=测试计划通知 task_defect_notification=缺陷任务通知 +task_notification_=定时任务结果通知 +api_definition_url_not_repeating=接口请求地址已经存在 task_notification_jenkins=jenkins任务通知 task_notification=任务通知 message_task_already_exists=任务接收人已经存在 \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 9c2402b647..491a543523 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -48,7 +48,6 @@ related_case_del_fail_prefix=已關聯到 related_case_del_fail_suffix=測試用例,請先解除關聯 jmx_content_valid=JMX 內容無效,請檢查 container_delete_fail=容器由於網絡原因停止失敗,請重試 -load_test_report_file_not_exist=當前報告沒有JTL文件,請重新執行以便獲取 #workspace workspace_name_is_null=工作空間名不能為空 workspace_name_already_exists=工作空間名已存在 @@ -172,5 +171,7 @@ test_plan_notification=測試計畫通知 task_defect_notification=缺陷任務通知 task_notification_jenkins=jenkins任務通知 task_notification=任務通知 +task_notification_=定時任務通知 +api_definition_url_not_repeating=接口請求地址已經存在 message_task_already_exists=任務接收人已經存在 diff --git a/backend/src/main/resources/jmeter/bin/saveservice.properties b/backend/src/main/resources/jmeter/bin/saveservice.properties index 4f861dba48..9924378590 100644 --- a/backend/src/main/resources/jmeter/bin/saveservice.properties +++ b/backend/src/main/resources/jmeter/bin/saveservice.properties @@ -1,7 +1,6 @@ #--------------------------------------------------------- # SAVESERVICE PROPERTIES - JMETER INTERNAL USE ONLY #--------------------------------------------------------- - ## Licensed to the Apache Software Foundation (ASF) under one or more ## contributor license agreements. See the NOTICE file distributed with ## this work for additional information regarding copyright ownership. @@ -16,16 +15,11 @@ ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. - # This file is used to define how XStream (de-)serializes classnames # in JMX test plan files. - # FOR JMETER INTERNAL USE ONLY - #--------------------------------------------------------- - # N.B. To ensure backward compatibility, please do NOT change or delete any entries - # New entries can be added as necessary. # # Note that keys starting with an underscore are special, @@ -132,6 +126,9 @@ DebugSampler=org.apache.jmeter.sampler.DebugSampler DistributionGraphVisualizer=org.apache.jmeter.visualizers.DistributionGraphVisualizer DNSCacheManager=org.apache.jmeter.protocol.http.control.DNSCacheManager DNSCachePanel=org.apache.jmeter.protocol.http.gui.DNSCachePanel +DubboSample=io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample +DubboSampleGui=io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui +DubboDefaultConfigGui=io.github.ningyu.jmeter.plugin.dubbo.gui.DubboDefaultConfigGui DurationAssertion=org.apache.jmeter.assertions.DurationAssertion DurationAssertionGui=org.apache.jmeter.assertions.gui.DurationAssertionGui PreciseThroughputTimer=org.apache.jmeter.timers.poissonarrivals.PreciseThroughputTimer @@ -242,10 +239,8 @@ ModuleController=org.apache.jmeter.control.ModuleController ModuleControllerGui=org.apache.jmeter.control.gui.ModuleControllerGui MongoScriptSampler=org.apache.jmeter.protocol.mongodb.sampler.MongoScriptSampler MongoSourceElement=org.apache.jmeter.protocol.mongodb.config.MongoSourceElement - # removed in 3.2, class was deleted in r MonitorHealthVisualizer=org.apache.jmeter.visualizers.MonitorHealthVisualizer - NamePanel=org.apache.jmeter.gui.NamePanel BoltSampler=org.apache.jmeter.protocol.bolt.sampler.BoltSampler BoltConnectionElement=org.apache.jmeter.protocol.bolt.config.BoltConnectionElement @@ -301,12 +296,10 @@ SMIMEAssertion=org.apache.jmeter.assertions.SMIMEAssertionTestElement SMIMEAssertionGui=org.apache.jmeter.assertions.gui.SMIMEAssertionGui SmtpSampler=org.apache.jmeter.protocol.smtp.sampler.SmtpSampler SmtpSamplerGui=org.apache.jmeter.protocol.smtp.sampler.gui.SmtpSamplerGui - # removed in 3.2, class was deleted in r SoapSampler=org.apache.jmeter.protocol.http.sampler.SoapSampler # removed in 3.2, class was deleted in r SoapSamplerGui=org.apache.jmeter.protocol.http.control.gui.SoapSamplerGui - # removed in 3.1, class was deleted in r1763837 SplineVisualizer=org.apache.jmeter.visualizers.SplineVisualizer # Originally deleted in r397955 as class is obsolete; needed for compat. @@ -375,7 +368,6 @@ XPathExtractor=org.apache.jmeter.extractor.XPathExtractor XPathExtractorGui=org.apache.jmeter.extractor.gui.XPathExtractorGui XPath2Extractor=org.apache.jmeter.extractor.XPath2Extractor XPath2ExtractorGui=org.apache.jmeter.extractor.gui.XPath2ExtractorGui - # Properties - all start with lower case letter and end with Prop # boolProp=org.apache.jmeter.testelement.property.BooleanProperty @@ -400,7 +392,6 @@ httpSample=org.apache.jmeter.protocol.http.sampler.HTTPSampleResult statSample=org.apache.jmeter.samplers.StatisticalSampleResult testResults=org.apache.jmeter.save.TestResultWrapper assertionResult=org.apache.jmeter.assertions.AssertionResult - # removed in 3.2, class was deleted in r monitorStats=org.apache.jmeter.visualizers.MonitorStats sampleEvent=org.apache.jmeter.samplers.SampleEvent diff --git a/docker/jmeter-base/jmeter.properties b/docker/jmeter-base/jmeter.properties index f3fea289f3..3f5273c2cf 100644 --- a/docker/jmeter-base/jmeter.properties +++ b/docker/jmeter-base/jmeter.properties @@ -254,7 +254,7 @@ gui.quick_9=ViewResultsFullVisualizer # Remote hosts and RMI configuration #--------------------------------------------------------------------------- -# Remote Hosts - comma delimited +# Remote Hosts - comma definitioned remote_hosts=127.0.0.1 #remote_hosts=localhost:1099,localhost:2010 @@ -532,11 +532,11 @@ server.rmi.ssl.disable=true #jmeter.save.saveservice.timestamp_format=yyyy/MM/dd HH:mm:ss.SSS # For use with Comma-separated value (CSV) files or other formats -# where the fields' values are separated by specified delimiters. +# where the fields' values are separated by specified definitioners. # Default: -#jmeter.save.saveservice.default_delimiter=, +#jmeter.save.saveservice.default_definitioner=, # For TAB, one can use: -#jmeter.save.saveservice.default_delimiter=\t +#jmeter.save.saveservice.default_definitioner=\t # Only applies to CSV format files: # Print field names as first line in CSV @@ -969,15 +969,15 @@ beanshell.server.file=../extras/startup.bsh # CSVRead configuration #--------------------------------------------------------------------------- -# CSVRead delimiter setting (default ",") -# Make sure that there are no trailing spaces or tabs after the delimiter -# characters, or these will be included in the list of valid delimiters -#csvread.delimiter=, -#csvread.delimiter=; -#csvread.delimiter=! -#csvread.delimiter=~ +# CSVRead definitioner setting (default ",") +# Make sure that there are no trailing spaces or tabs after the definitioner +# characters, or these will be included in the list of valid definitioners +#csvread.definitioner=, +#csvread.definitioner=; +#csvread.definitioner=! +#csvread.definitioner=~ # The following line has a tab after the = -#csvread.delimiter= +#csvread.definitioner= #--------------------------------------------------------------------------- # __time() function configuration diff --git a/frontend/package.json b/frontend/package.json index 02e22efaba..cc05d4c0c6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,7 +38,8 @@ "el-table-infinite-scroll": "^1.0.10", "vue-pdf": "^4.2.0", "diffable-html": "^4.0.0", - "xml-js": "^1.6.11" + "xml-js": "^1.6.11", + "jsoneditor": "^9.1.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.1.0", diff --git a/frontend/src/assets/theme/fonts/element-icons.ttf b/frontend/src/assets/theme/fonts/element-icons.ttf new file mode 100644 index 0000000000..91b74de367 Binary files /dev/null and b/frontend/src/assets/theme/fonts/element-icons.ttf differ diff --git a/frontend/src/assets/theme/fonts/element-icons.woff b/frontend/src/assets/theme/fonts/element-icons.woff new file mode 100644 index 0000000000..02b9a2539e Binary files /dev/null and b/frontend/src/assets/theme/fonts/element-icons.woff differ diff --git a/frontend/src/assets/theme/index.css b/frontend/src/assets/theme/index.css new file mode 100644 index 0000000000..fbbd55f85c --- /dev/null +++ b/frontend/src/assets/theme/index.css @@ -0,0 +1 @@ +@charset "UTF-8";.el-pagination--small .arrow.disabled,.el-table .hidden-columns,.el-table td.is-hidden>*,.el-table th.is-hidden>*,.el-table--hidden{visibility:hidden}.el-input__suffix,.el-tree.is-dragging .el-tree-node__content *{pointer-events:none}.el-dropdown .el-dropdown-selfdefine:focus:active,.el-dropdown .el-dropdown-selfdefine:focus:not(.focusing),.el-message__closeBtn:focus,.el-message__content:focus,.el-popover:focus,.el-popover:focus:active,.el-popover__reference:focus:hover,.el-popover__reference:focus:not(.focusing),.el-rate:active,.el-rate:focus,.el-tooltip:focus:hover,.el-tooltip:focus:not(.focusing),.el-upload-list__item.is-success:active,.el-upload-list__item.is-success:not(.focusing):focus{outline-width:0}@font-face{font-family:element-icons;src:url(fonts/element-icons.woff) format("woff"),url(fonts/element-icons.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\e6a0"}.el-icon-ice-cream-square:before{content:"\e6a3"}.el-icon-lollipop:before{content:"\e6a4"}.el-icon-potato-strips:before{content:"\e6a5"}.el-icon-milk-tea:before{content:"\e6a6"}.el-icon-ice-drink:before{content:"\e6a7"}.el-icon-ice-tea:before{content:"\e6a9"}.el-icon-coffee:before{content:"\e6aa"}.el-icon-orange:before{content:"\e6ab"}.el-icon-pear:before{content:"\e6ac"}.el-icon-apple:before{content:"\e6ad"}.el-icon-cherry:before{content:"\e6ae"}.el-icon-watermelon:before{content:"\e6af"}.el-icon-grape:before{content:"\e6b0"}.el-icon-refrigerator:before{content:"\e6b1"}.el-icon-goblet-square-full:before{content:"\e6b2"}.el-icon-goblet-square:before{content:"\e6b3"}.el-icon-goblet-full:before{content:"\e6b4"}.el-icon-goblet:before{content:"\e6b5"}.el-icon-cold-drink:before{content:"\e6b6"}.el-icon-coffee-cup:before{content:"\e6b8"}.el-icon-water-cup:before{content:"\e6b9"}.el-icon-hot-water:before{content:"\e6ba"}.el-icon-ice-cream:before{content:"\e6bb"}.el-icon-dessert:before{content:"\e6bc"}.el-icon-sugar:before{content:"\e6bd"}.el-icon-tableware:before{content:"\e6be"}.el-icon-burger:before{content:"\e6bf"}.el-icon-knife-fork:before{content:"\e6c1"}.el-icon-fork-spoon:before{content:"\e6c2"}.el-icon-chicken:before{content:"\e6c3"}.el-icon-food:before{content:"\e6c4"}.el-icon-dish-1:before{content:"\e6c5"}.el-icon-dish:before{content:"\e6c6"}.el-icon-moon-night:before{content:"\e6ee"}.el-icon-moon:before{content:"\e6f0"}.el-icon-cloudy-and-sunny:before{content:"\e6f1"}.el-icon-partly-cloudy:before{content:"\e6f2"}.el-icon-cloudy:before{content:"\e6f3"}.el-icon-sunny:before{content:"\e6f6"}.el-icon-sunset:before{content:"\e6f7"}.el-icon-sunrise-1:before{content:"\e6f8"}.el-icon-sunrise:before{content:"\e6f9"}.el-icon-heavy-rain:before{content:"\e6fa"}.el-icon-lightning:before{content:"\e6fb"}.el-icon-light-rain:before{content:"\e6fc"}.el-icon-wind-power:before{content:"\e6fd"}.el-icon-baseball:before{content:"\e712"}.el-icon-soccer:before{content:"\e713"}.el-icon-football:before{content:"\e715"}.el-icon-basketball:before{content:"\e716"}.el-icon-ship:before{content:"\e73f"}.el-icon-truck:before{content:"\e740"}.el-icon-bicycle:before{content:"\e741"}.el-icon-mobile-phone:before{content:"\e6d3"}.el-icon-service:before{content:"\e6d4"}.el-icon-key:before{content:"\e6e2"}.el-icon-unlock:before{content:"\e6e4"}.el-icon-lock:before{content:"\e6e5"}.el-icon-watch:before{content:"\e6fe"}.el-icon-watch-1:before{content:"\e6ff"}.el-icon-timer:before{content:"\e702"}.el-icon-alarm-clock:before{content:"\e703"}.el-icon-map-location:before{content:"\e704"}.el-icon-delete-location:before{content:"\e705"}.el-icon-add-location:before{content:"\e706"}.el-icon-location-information:before{content:"\e707"}.el-icon-location-outline:before{content:"\e708"}.el-icon-location:before{content:"\e79e"}.el-icon-place:before{content:"\e709"}.el-icon-discover:before{content:"\e70a"}.el-icon-first-aid-kit:before{content:"\e70b"}.el-icon-trophy-1:before{content:"\e70c"}.el-icon-trophy:before{content:"\e70d"}.el-icon-medal:before{content:"\e70e"}.el-icon-medal-1:before{content:"\e70f"}.el-icon-stopwatch:before{content:"\e710"}.el-icon-mic:before{content:"\e711"}.el-icon-copy-document:before{content:"\e718"}.el-icon-full-screen:before{content:"\e719"}.el-icon-switch-button:before{content:"\e71b"}.el-icon-aim:before{content:"\e71c"}.el-icon-crop:before{content:"\e71d"}.el-icon-odometer:before{content:"\e71e"}.el-icon-time:before{content:"\e71f"}.el-icon-bangzhu:before{content:"\e724"}.el-icon-close-notification:before{content:"\e726"}.el-icon-microphone:before{content:"\e727"}.el-icon-turn-off-microphone:before{content:"\e728"}.el-icon-position:before{content:"\e729"}.el-icon-postcard:before{content:"\e72a"}.el-icon-message:before{content:"\e72b"}.el-icon-chat-line-square:before{content:"\e72d"}.el-icon-chat-dot-square:before{content:"\e72e"}.el-icon-chat-dot-round:before{content:"\e72f"}.el-icon-chat-square:before{content:"\e730"}.el-icon-chat-line-round:before{content:"\e731"}.el-icon-chat-round:before{content:"\e732"}.el-icon-set-up:before{content:"\e733"}.el-icon-turn-off:before{content:"\e734"}.el-icon-open:before{content:"\e735"}.el-icon-connection:before{content:"\e736"}.el-icon-link:before{content:"\e737"}.el-icon-cpu:before{content:"\e738"}.el-icon-thumb:before{content:"\e739"}.el-icon-female:before{content:"\e73a"}.el-icon-male:before{content:"\e73b"}.el-icon-guide:before{content:"\e73c"}.el-icon-news:before{content:"\e73e"}.el-icon-price-tag:before{content:"\e744"}.el-icon-discount:before{content:"\e745"}.el-icon-wallet:before{content:"\e747"}.el-icon-coin:before{content:"\e748"}.el-icon-money:before{content:"\e749"}.el-icon-bank-card:before{content:"\e74a"}.el-icon-box:before{content:"\e74b"}.el-icon-present:before{content:"\e74c"}.el-icon-sell:before{content:"\e6d5"}.el-icon-sold-out:before{content:"\e6d6"}.el-icon-shopping-bag-2:before{content:"\e74d"}.el-icon-shopping-bag-1:before{content:"\e74e"}.el-icon-shopping-cart-2:before{content:"\e74f"}.el-icon-shopping-cart-1:before{content:"\e750"}.el-icon-shopping-cart-full:before{content:"\e751"}.el-icon-smoking:before{content:"\e752"}.el-icon-no-smoking:before{content:"\e753"}.el-icon-house:before{content:"\e754"}.el-icon-table-lamp:before{content:"\e755"}.el-icon-school:before{content:"\e756"}.el-icon-office-building:before{content:"\e757"}.el-icon-toilet-paper:before{content:"\e758"}.el-icon-notebook-2:before{content:"\e759"}.el-icon-notebook-1:before{content:"\e75a"}.el-icon-files:before{content:"\e75b"}.el-icon-collection:before{content:"\e75c"}.el-icon-receiving:before{content:"\e75d"}.el-icon-suitcase-1:before{content:"\e760"}.el-icon-suitcase:before{content:"\e761"}.el-icon-film:before{content:"\e763"}.el-icon-collection-tag:before{content:"\e765"}.el-icon-data-analysis:before{content:"\e766"}.el-icon-pie-chart:before{content:"\e767"}.el-icon-data-board:before{content:"\e768"}.el-icon-data-line:before{content:"\e76d"}.el-icon-reading:before{content:"\e769"}.el-icon-magic-stick:before{content:"\e76a"}.el-icon-coordinate:before{content:"\e76b"}.el-icon-mouse:before{content:"\e76c"}.el-icon-brush:before{content:"\e76e"}.el-icon-headset:before{content:"\e76f"}.el-icon-umbrella:before{content:"\e770"}.el-icon-scissors:before{content:"\e771"}.el-icon-mobile:before{content:"\e773"}.el-icon-attract:before{content:"\e774"}.el-icon-monitor:before{content:"\e775"}.el-icon-search:before{content:"\e778"}.el-icon-takeaway-box:before{content:"\e77a"}.el-icon-paperclip:before{content:"\e77d"}.el-icon-printer:before{content:"\e77e"}.el-icon-document-add:before{content:"\e782"}.el-icon-document:before{content:"\e785"}.el-icon-document-checked:before{content:"\e786"}.el-icon-document-copy:before{content:"\e787"}.el-icon-document-delete:before{content:"\e788"}.el-icon-document-remove:before{content:"\e789"}.el-icon-tickets:before{content:"\e78b"}.el-icon-folder-checked:before{content:"\e77f"}.el-icon-folder-delete:before{content:"\e780"}.el-icon-folder-remove:before{content:"\e781"}.el-icon-folder-add:before{content:"\e783"}.el-icon-folder-opened:before{content:"\e784"}.el-icon-folder:before{content:"\e78a"}.el-icon-edit-outline:before{content:"\e764"}.el-icon-edit:before{content:"\e78c"}.el-icon-date:before{content:"\e78e"}.el-icon-c-scale-to-original:before{content:"\e7c6"}.el-icon-view:before{content:"\e6ce"}.el-icon-loading:before{content:"\e6cf"}.el-icon-rank:before{content:"\e6d1"}.el-icon-sort-down:before{content:"\e7c4"}.el-icon-sort-up:before{content:"\e7c5"}.el-icon-sort:before{content:"\e6d2"}.el-icon-finished:before{content:"\e6cd"}.el-icon-refresh-left:before{content:"\e6c7"}.el-icon-refresh-right:before{content:"\e6c8"}.el-icon-refresh:before{content:"\e6d0"}.el-icon-video-play:before{content:"\e7c0"}.el-icon-video-pause:before{content:"\e7c1"}.el-icon-d-arrow-right:before{content:"\e6dc"}.el-icon-d-arrow-left:before{content:"\e6dd"}.el-icon-arrow-up:before{content:"\e6e1"}.el-icon-arrow-down:before{content:"\e6df"}.el-icon-arrow-right:before{content:"\e6e0"}.el-icon-arrow-left:before{content:"\e6de"}.el-icon-top-right:before{content:"\e6e7"}.el-icon-top-left:before{content:"\e6e8"}.el-icon-top:before{content:"\e6e6"}.el-icon-bottom:before{content:"\e6eb"}.el-icon-right:before{content:"\e6e9"}.el-icon-back:before{content:"\e6ea"}.el-icon-bottom-right:before{content:"\e6ec"}.el-icon-bottom-left:before{content:"\e6ed"}.el-icon-caret-top:before{content:"\e78f"}.el-icon-caret-bottom:before{content:"\e790"}.el-icon-caret-right:before{content:"\e791"}.el-icon-caret-left:before{content:"\e792"}.el-icon-d-caret:before{content:"\e79a"}.el-icon-share:before{content:"\e793"}.el-icon-menu:before{content:"\e798"}.el-icon-s-grid:before{content:"\e7a6"}.el-icon-s-check:before{content:"\e7a7"}.el-icon-s-data:before{content:"\e7a8"}.el-icon-s-opportunity:before{content:"\e7aa"}.el-icon-s-custom:before{content:"\e7ab"}.el-icon-s-claim:before{content:"\e7ad"}.el-icon-s-finance:before{content:"\e7ae"}.el-icon-s-comment:before{content:"\e7af"}.el-icon-s-flag:before{content:"\e7b0"}.el-icon-s-marketing:before{content:"\e7b1"}.el-icon-s-shop:before{content:"\e7b4"}.el-icon-s-open:before{content:"\e7b5"}.el-icon-s-management:before{content:"\e7b6"}.el-icon-s-ticket:before{content:"\e7b7"}.el-icon-s-release:before{content:"\e7b8"}.el-icon-s-home:before{content:"\e7b9"}.el-icon-s-promotion:before{content:"\e7ba"}.el-icon-s-operation:before{content:"\e7bb"}.el-icon-s-unfold:before{content:"\e7bc"}.el-icon-s-fold:before{content:"\e7a9"}.el-icon-s-platform:before{content:"\e7bd"}.el-icon-s-order:before{content:"\e7be"}.el-icon-s-cooperation:before{content:"\e7bf"}.el-icon-bell:before{content:"\e725"}.el-icon-message-solid:before{content:"\e799"}.el-icon-video-camera:before{content:"\e772"}.el-icon-video-camera-solid:before{content:"\e796"}.el-icon-camera:before{content:"\e779"}.el-icon-camera-solid:before{content:"\e79b"}.el-icon-download:before{content:"\e77c"}.el-icon-upload2:before{content:"\e77b"}.el-icon-upload:before{content:"\e7c3"}.el-icon-picture-outline-round:before{content:"\e75f"}.el-icon-picture-outline:before{content:"\e75e"}.el-icon-picture:before{content:"\e79f"}.el-icon-close:before{content:"\e6db"}.el-icon-check:before{content:"\e6da"}.el-icon-plus:before{content:"\e6d9"}.el-icon-minus:before{content:"\e6d8"}.el-icon-help:before{content:"\e73d"}.el-icon-s-help:before{content:"\e7b3"}.el-icon-circle-close:before{content:"\e78d"}.el-icon-circle-check:before{content:"\e720"}.el-icon-circle-plus-outline:before{content:"\e723"}.el-icon-remove-outline:before{content:"\e722"}.el-icon-zoom-out:before{content:"\e776"}.el-icon-zoom-in:before{content:"\e777"}.el-icon-error:before{content:"\e79d"}.el-icon-success:before{content:"\e79c"}.el-icon-circle-plus:before{content:"\e7a0"}.el-icon-remove:before{content:"\e7a2"}.el-icon-info:before{content:"\e7a1"}.el-icon-question:before{content:"\e7a4"}.el-icon-warning-outline:before{content:"\e6c9"}.el-icon-warning:before{content:"\e7a3"}.el-icon-goods:before{content:"\e7c2"}.el-icon-s-goods:before{content:"\e7b2"}.el-icon-star-off:before{content:"\e717"}.el-icon-star-on:before{content:"\e797"}.el-icon-more-outline:before{content:"\e6cc"}.el-icon-more:before{content:"\e794"}.el-icon-phone-outline:before{content:"\e6cb"}.el-icon-phone:before{content:"\e795"}.el-icon-user:before{content:"\e6e3"}.el-icon-user-solid:before{content:"\e7a5"}.el-icon-setting:before{content:"\e6ca"}.el-icon-s-tools:before{content:"\e7ac"}.el-icon-delete:before{content:"\e6d7"}.el-icon-delete-solid:before{content:"\e7c9"}.el-icon-eleme:before{content:"\e7c7"}.el-icon-platform-eleme:before{content:"\e7ca"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}.el-pagination{white-space:nowrap;padding:2px 5px;color:#303133;font-weight:700}.el-pagination::after,.el-pagination::before{display:table;content:""}.el-pagination::after{clear:both}.el-pagination button,.el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;-webkit-box-sizing:border-box;box-sizing:border-box}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield;line-height:normal}.el-pagination .el-input__suffix{right:0;-webkit-transform:scale(.8);transform:scale(.8)}.el-pagination .el-select .el-input{width:100px;margin:0 5px}.el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px}.el-pagination button{border:none;padding:0 6px;background:0 0}.el-pagination button:focus{outline:0}.el-pagination button:hover{color:#783887}.el-pagination button:disabled{color:#C0C4CC;background-color:#FFF;cursor:not-allowed}.el-pagination .btn-next,.el-pagination .btn-prev{background:center center no-repeat #FFF;background-size:16px;cursor:pointer;margin:0;color:#303133}.el-pagination .btn-next .el-icon,.el-pagination .btn-prev .el-icon{display:block;font-size:12px;font-weight:700}.el-pagination .btn-prev{padding-right:12px}.el-pagination .btn-next{padding-left:12px}.el-pagination .el-pager li.disabled{color:#C0C4CC;cursor:not-allowed}.el-pager li,.el-pager li.btn-quicknext:hover,.el-pager li.btn-quickprev:hover{cursor:pointer}.el-pagination--small .btn-next,.el-pagination--small .btn-prev,.el-pagination--small .el-pager li,.el-pagination--small .el-pager li.btn-quicknext,.el-pagination--small .el-pager li.btn-quickprev,.el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.el-pagination--small .more::before,.el-pagination--small li.more::before{line-height:24px}.el-pagination--small button,.el-pagination--small span:not([class*=suffix]){height:22px;line-height:22px}.el-pagination--small .el-pagination__editor,.el-pagination--small .el-pagination__editor.el-input .el-input__inner{height:22px}.el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#606266}.el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.el-pagination__sizes .el-input .el-input__inner:hover{border-color:#783887}.el-pagination__total{margin-right:10px;font-weight:400;color:#606266}.el-pagination__jump{margin-left:24px;font-weight:400;color:#606266}.el-pagination__jump .el-input__inner{padding:0 3px}.el-pagination__rightwrapper{float:right}.el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:3px}.el-pager,.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev{padding:0}.el-pagination__editor.el-input{width:50px}.el-pagination__editor.el-input .el-input__inner{height:28px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev,.el-pagination.is-background .el-pager li{margin:0 5px;background-color:#f4f4f5;color:#606266;min-width:30px;border-radius:2px}.el-pagination.is-background .btn-next.disabled,.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-prev.disabled,.el-pagination.is-background .btn-prev:disabled,.el-pagination.is-background .el-pager li.disabled{color:#C0C4CC}.el-pagination.is-background .el-pager li:not(.disabled):hover{color:#783887}.el-pagination.is-background .el-pager li:not(.disabled).active{background-color:#783887;color:#FFF}.el-dialog,.el-pager li{background:#FFF;-webkit-box-sizing:border-box}.el-pagination.is-background.el-pagination--small .btn-next,.el-pagination.is-background.el-pagination--small .btn-prev,.el-pagination.is-background.el-pagination--small .el-pager li{margin:0 3px;min-width:22px}.el-pager,.el-pager li{vertical-align:top;margin:0;display:inline-block}.el-pager{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;list-style:none;font-size:0}.el-date-table,.el-table th{-webkit-user-select:none;-moz-user-select:none}.el-pager .more::before{line-height:30px}.el-pager li{padding:0 4px;font-size:13px;min-width:35.5px;height:28px;line-height:28px;box-sizing:border-box;text-align:center}.el-menu--collapse .el-menu .el-submenu,.el-menu--popup{min-width:200px}.el-pager li.btn-quicknext,.el-pager li.btn-quickprev{line-height:28px;color:#303133}.el-pager li.btn-quicknext.disabled,.el-pager li.btn-quickprev.disabled{color:#C0C4CC}.el-pager li.active+li{border-left:0}.el-pager li:hover{color:#783887}.el-pager li.active{color:#783887;cursor:default}@-webkit-keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{100%{opacity:0}}.el-dialog{position:relative;margin:0 auto 50px;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:20px 20px 10px}.el-dialog__headerbtn{position:absolute;top:20px;right:20px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#909399}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#783887}.el-dialog__title{line-height:24px;font-size:18px;color:#303133}.el-dialog__body{padding:30px 20px;color:#606266;font-size:14px;word-break:break-all}.el-dialog__footer{padding:10px 20px 20px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 25px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-autocomplete{position:relative;display:inline-block}.el-autocomplete-suggestion{margin:5px 0;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:4px;border:1px solid #E4E7ED;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#FFF}.el-dropdown-menu,.el-menu--collapse .el-submenu .el-menu{z-index:10;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-autocomplete-suggestion__wrap{max-height:280px;padding:10px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-autocomplete-suggestion__list{margin:0;padding:0}.el-autocomplete-suggestion li{padding:0 20px;margin:0;line-height:34px;cursor:pointer;color:#606266;font-size:14px;list-style:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-autocomplete-suggestion li.highlighted,.el-autocomplete-suggestion li:hover{background-color:#F5F7FA}.el-autocomplete-suggestion li.divider{margin-top:6px;border-top:1px solid #000}.el-autocomplete-suggestion li.divider:last-child{margin-bottom:-6px}.el-autocomplete-suggestion.is-loading li{text-align:center;height:100px;line-height:100px;font-size:20px;color:#999}.el-autocomplete-suggestion.is-loading li::after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-autocomplete-suggestion.is-loading li:hover{background-color:#FFF}.el-autocomplete-suggestion.is-loading .el-icon-loading{vertical-align:middle}.el-dropdown{display:inline-block;position:relative;color:#606266;font-size:14px}.el-dropdown .el-button-group{display:block}.el-dropdown .el-button-group .el-button{float:none}.el-dropdown .el-dropdown__caret-button{padding-left:5px;padding-right:5px;position:relative;border-left:none}.el-dropdown .el-dropdown__caret-button::before{content:'';position:absolute;display:block;width:1px;top:5px;bottom:5px;left:0;background:rgba(255,255,255,.5)}.el-dropdown .el-dropdown__caret-button.el-button--default::before{background:rgba(220,223,230,.5)}.el-dropdown .el-dropdown__caret-button:hover::before{top:0;bottom:0}.el-dropdown .el-dropdown__caret-button .el-dropdown__icon{padding-left:0}.el-dropdown__icon{font-size:12px;margin:0 3px}.el-dropdown-menu{position:absolute;top:0;left:0;padding:10px 0;margin:5px 0;background-color:#FFF;border:1px solid #EBEEF5;border-radius:4px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-dropdown-menu__item{list-style:none;line-height:36px;padding:0 20px;margin:0;font-size:14px;color:#606266;cursor:pointer;outline:0}.el-dropdown-menu__item:focus,.el-dropdown-menu__item:not(.is-disabled):hover{background-color:rgb(242, 235, 243);color:rgb(147, 96, 159)}.el-dropdown-menu__item i{margin-right:5px}.el-dropdown-menu__item--divided{position:relative;margin-top:6px;border-top:1px solid #EBEEF5}.el-dropdown-menu__item--divided:before{content:'';height:6px;display:block;margin:0 -20px;background-color:#FFF}.el-dropdown-menu__item.is-disabled{cursor:default;color:#bbb;pointer-events:none}.el-dropdown-menu--medium{padding:6px 0}.el-dropdown-menu--medium .el-dropdown-menu__item{line-height:30px;padding:0 17px;font-size:14px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:6px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:6px;margin:0 -17px}.el-dropdown-menu--small{padding:6px 0}.el-dropdown-menu--small .el-dropdown-menu__item{line-height:27px;padding:0 15px;font-size:13px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:4px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:4px;margin:0 -15px}.el-dropdown-menu--mini{padding:3px 0}.el-dropdown-menu--mini .el-dropdown-menu__item{line-height:24px;padding:0 10px;font-size:12px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:3px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:3px;margin:0 -10px}.el-menu{border-right:solid 1px #e6e6e6;list-style:none;position:relative;margin:0;padding-left:0;background-color:#FFF}.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,.el-menu--horizontal>.el-submenu .el-submenu__title:hover{background-color:#fff}.el-menu::after,.el-menu::before{display:table;content:""}.el-menu::after{clear:both}.el-menu.el-menu--horizontal{border-bottom:solid 1px #e6e6e6}.el-menu--horizontal{border-right:none}.el-menu--horizontal>.el-menu-item{float:left;height:60px;line-height:60px;margin:0;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-submenu{float:left}.el-menu--horizontal>.el-submenu:focus,.el-menu--horizontal>.el-submenu:hover{outline:0}.el-menu--horizontal>.el-submenu:focus .el-submenu__title,.el-menu--horizontal>.el-submenu:hover .el-submenu__title{color:#303133}.el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #783887;color:#303133}.el-menu--horizontal>.el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-submenu__title{background-color:#FFF;float:none;height:36px;line-height:36px;padding:0 10px;color:#909399}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-submenu.is-active>.el-submenu__title{color:#303133}.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,.el-menu--horizontal .el-menu-item:not(.is-disabled):hover{outline:0;color:#303133}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid #783887;color:#303133}.el-menu--collapse{width:64px}.el-menu--collapse>.el-menu-item [class^=el-icon-],.el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item span,.el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-submenu{position:relative}.el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;border:1px solid #E4E7ED;border-radius:2px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu-item,.el-submenu__title{height:56px;line-height:56px;position:relative;-webkit-box-sizing:border-box;white-space:nowrap;list-style:none}.el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:none;transform:none}.el-menu--popup{z-index:100;border:none;padding:5px 0;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu--popup-bottom-start{margin-top:5px}.el-menu--popup-right-start{margin-left:5px;margin-right:5px}.el-menu-item{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;box-sizing:border-box}.el-menu-item *{vertical-align:middle}.el-menu-item i{color:#909399}.el-menu-item:focus,.el-menu-item:hover{outline:0;background-color:rgb(242, 235, 243)}.el-menu-item.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px;vertical-align:middle}.el-menu-item.is-active{color:#783887}.el-menu-item.is-active i{color:inherit}.el-submenu{list-style:none;margin:0;padding-left:0}.el-submenu__title{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;box-sizing:border-box}.el-submenu__title *{vertical-align:middle}.el-submenu__title i{color:#909399}.el-submenu__title:focus,.el-submenu__title:hover{outline:0;background-color:rgb(242, 235, 243)}.el-submenu__title.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu__title:hover{background-color:rgb(242, 235, 243)}.el-submenu .el-menu{border:none}.el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:12px}.el-submenu.is-active .el-submenu__title{border-bottom-color:#783887}.el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.el-submenu.is-disabled .el-menu-item,.el-submenu.is-disabled .el-submenu__title{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#909399}.el-radio-button__inner,.el-radio-group{display:inline-block;line-height:1;vertical-align:middle}.horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{-webkit-transition:.2s;transition:.2s;opacity:0}.el-radio-group{font-size:0}.el-radio-button{position:relative;display:inline-block;outline:0}.el-radio-button__inner{white-space:nowrap;background:#FFF;border:1px solid #DCDFE6;font-weight:500;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-radio-button__inner.is-round{padding:12px 20px}.el-radio-button__inner:hover{color:#783887}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #DCDFE6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-radio-button__orig-radio{opacity:0;outline:0;position:absolute;z-index:-1}.el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#FFF;background-color:#783887;border-color:#783887;-webkit-box-shadow:-1px 0 0 0 #783887;box-shadow:-1px 0 0 0 #783887}.el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#C0C4CC;cursor:not-allowed;background-image:none;background-color:#FFF;border-color:#EBEEF5;-webkit-box-shadow:none;box-shadow:none}.el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#F2F6FC}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.el-popover,.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.el-radio-button:focus:not(.is-focus):not(:active):not(.is-disabled){-webkit-box-shadow:0 0 2px 2px #783887;box-shadow:0 0 2px 2px #783887}.el-switch{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.el-switch__core,.el-switch__label{display:inline-block;cursor:pointer}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__label{-webkit-transition:.2s;transition:.2s;height:20px;font-size:14px;font-weight:500;vertical-align:middle;color:#303133}.el-switch__label.is-active{color:#783887}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{line-height:1;font-size:14px;display:inline-block}.el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.el-switch__core{margin:0;position:relative;width:40px;height:20px;border:1px solid #DCDFE6;outline:0;border-radius:10px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#DCDFE6;-webkit-transition:border-color .3s,background-color .3s;transition:border-color .3s,background-color .3s;vertical-align:middle}.el-switch__core:after{content:"";position:absolute;top:1px;left:1px;border-radius:100%;-webkit-transition:all .3s;transition:all .3s;width:16px;height:16px;background-color:#FFF}.el-switch.is-checked .el-switch__core{border-color:#783887;background-color:#783887}.el-switch.is-checked .el-switch__core::after{left:100%;margin-left:-17px}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter,.el-switch .label-fade-leave-active{opacity:0}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #E4E7ED;border-radius:4px;background-color:#FFF;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#783887;background-color:#FFF}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#F5F7FA}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after{position:absolute;right:20px;font-family:element-icons;content:"\e6da";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.el-select-dropdown__item.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#FFF}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#F5F7FA}.el-select-dropdown__item.selected{color:#783887;font-weight:700}.el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type)::after{content:'';position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#E4E7ED}.el-select-group__title{padding-left:20px;font-size:12px;color:#909399;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-select{display:inline-block;position:relative}.el-select .el-select__tags>span{display:contents}.el-select:hover .el-input__inner{border-color:#C0C4CC}.el-select .el-input__inner{cursor:pointer;padding-right:35px}.el-select .el-input__inner:focus{border-color:#783887}.el-select .el-input .el-select__caret{color:#C0C4CC;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);cursor:pointer}.el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotateZ(0);transform:rotateZ(0)}.el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);border-radius:100%;color:#C0C4CC;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-select .el-input .el-select__caret.is-show-close:hover{color:#909399}.el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.el-select .el-input.is-disabled .el-input__inner:hover{border-color:#E4E7ED}.el-select .el-input.is-focus .el-input__inner{border-color:#783887}.el-select>.el-input{display:block}.el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.el-select__input.is-mini{height:14px}.el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#C0C4CC;line-height:18px;font-size:14px}.el-select__close:hover{color:#909399}.el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-select .el-tag__close{margin-top:-2px}.el-select .el-tag{-webkit-box-sizing:border-box;box-sizing:border-box;border-color:transparent;margin:2px 0 2px 6px;background-color:#f0f2f5}.el-select .el-tag__close.el-icon-close{background-color:#C0C4CC;right:-7px;top:0;color:#FFF}.el-select .el-tag__close.el-icon-close:hover{background-color:#909399}.el-table,.el-table__expanded-cell{background-color:#FFF}.el-select .el-tag__close.el-icon-close::before{display:block;-webkit-transform:translate(0,.5px);transform:translate(0,.5px)}.el-table{position:relative;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1;flex:1;width:100%;max-width:100%;font-size:14px;color:#606266}.el-table--mini,.el-table--small,.el-table__expand-icon{font-size:12px}.el-table__empty-block{min-height:60px;text-align:center;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-table__empty-text{line-height:60px;width:50%;color:#909399}.el-table__expand-column .cell{padding:0;text-align:center}.el-table__expand-icon{position:relative;cursor:pointer;color:#666;-webkit-transition:-webkit-transform .2s ease-in-out;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;height:20px}.el-table__expand-icon--expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-table__expand-icon>.el-icon{position:absolute;left:50%;top:50%;margin-left:-5px;margin-top:-5px}.el-table__expanded-cell[class*=cell]{padding:20px 50px}.el-table__expanded-cell:hover{background-color:transparent!important}.el-table__placeholder{display:inline-block;width:20px}.el-table__append-wrapper{overflow:hidden}.el-table--fit{border-right:0;border-bottom:0}.el-table--fit td.gutter,.el-table--fit th.gutter{border-right-width:1px}.el-table--scrollable-x .el-table__body-wrapper{overflow-x:auto}.el-table--scrollable-y .el-table__body-wrapper{overflow-y:auto}.el-table thead{color:#909399;font-weight:500}.el-table thead.is-group th{background:#F5F7FA}.el-table th,.el-table tr{background-color:#FFF}.el-table td,.el-table th{padding:12px 0;min-width:0;-webkit-box-sizing:border-box;box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;position:relative;text-align:left}.el-table td.is-center,.el-table th.is-center{text-align:center}.el-table td.is-right,.el-table th.is-right{text-align:right}.el-table td.gutter,.el-table th.gutter{width:15px;border-right-width:0;border-bottom-width:0;padding:0}.el-table--medium td,.el-table--medium th{padding:10px 0}.el-table--small td,.el-table--small th{padding:8px 0}.el-table--mini td,.el-table--mini th{padding:6px 0}.el-table .cell,.el-table--border td:first-child .cell,.el-table--border th:first-child .cell{padding-left:10px}.el-table tr input[type=checkbox]{margin:0}.el-table td,.el-table th.is-leaf{border-bottom:1px solid #EBEEF5}.el-table th.is-sortable{cursor:pointer}.el-table th{overflow:hidden;-ms-user-select:none;user-select:none}.el-table th>.cell{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;vertical-align:middle;padding-left:10px;padding-right:10px;width:100%}.el-table th>.cell.highlight{color:#783887}.el-table th.required>div::before{display:inline-block;content:"";width:8px;height:8px;border-radius:50%;background:#ff4d51;margin-right:5px;vertical-align:middle}.el-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.el-table td.gutter{width:0}.el-table .cell{-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:normal;word-break:break-all;line-height:23px;padding-right:10px}.el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.el-table--border,.el-table--group{border:1px solid #EBEEF5}.el-table--border::after,.el-table--group::after,.el-table::before{content:'';position:absolute;background-color:#EBEEF5;z-index:1}.el-table--border::after,.el-table--group::after{top:0;right:0;width:1px;height:100%}.el-table::before{left:0;bottom:0;width:100%;height:1px}.el-table--border{border-right:none;border-bottom:none}.el-table--border.el-loading-parent--relative{border-color:transparent}.el-table--border td,.el-table--border th,.el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:1px solid #EBEEF5}.el-table--border th.gutter:last-of-type{border-bottom:1px solid #EBEEF5;border-bottom-width:1px}.el-table--border th,.el-table__fixed-right-patch{border-bottom:1px solid #EBEEF5}.el-table__fixed,.el-table__fixed-right{position:absolute;top:0;left:0;overflow-x:hidden;overflow-y:hidden;-webkit-box-shadow:0 0 10px rgba(0,0,0,.12);box-shadow:0 0 10px rgba(0,0,0,.12)}.el-table__fixed-right::before,.el-table__fixed::before{content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;background-color:#EBEEF5;z-index:4}.el-table__fixed-right-patch{position:absolute;top:-1px;right:0;background-color:#FFF}.el-table__fixed-right{top:0;left:auto;right:0}.el-table__fixed-right .el-table__fixed-body-wrapper,.el-table__fixed-right .el-table__fixed-footer-wrapper,.el-table__fixed-right .el-table__fixed-header-wrapper{left:auto;right:0}.el-table__fixed-header-wrapper{position:absolute;left:0;top:0;z-index:3}.el-table__fixed-footer-wrapper{position:absolute;left:0;bottom:0;z-index:3}.el-table__fixed-footer-wrapper tbody td{border-top:1px solid #EBEEF5;background-color:#F5F7FA;color:#606266}.el-table__fixed-body-wrapper{position:absolute;left:0;top:37px;overflow:hidden;z-index:3}.el-table__body-wrapper,.el-table__footer-wrapper,.el-table__header-wrapper{width:100%}.el-table__footer-wrapper{margin-top:-1px}.el-table__footer-wrapper td{border-top:1px solid #EBEEF5}.el-table__body,.el-table__footer,.el-table__header{table-layout:fixed;border-collapse:separate}.el-table__footer-wrapper,.el-table__header-wrapper{overflow:hidden}.el-table__footer-wrapper tbody td,.el-table__header-wrapper tbody td{background-color:#F5F7FA;color:#606266}.el-table__body-wrapper{overflow:hidden;position:relative}.el-table__body-wrapper.is-scrolling-left~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed-right,.el-table__body-wrapper.is-scrolling-right~.el-table__fixed-right{-webkit-box-shadow:none;box-shadow:none}.el-picker-panel,.el-table-filter{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-table__body-wrapper .el-table--border.is-scrolling-right~.el-table__fixed-right{border-left:1px solid #EBEEF5}.el-table .caret-wrapper{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;width:24px;vertical-align:middle;cursor:pointer;overflow:initial;position:relative}.el-table .sort-caret{width:0;height:0;border:5px solid transparent;position:absolute;left:7px}.el-table .sort-caret.ascending{border-bottom-color:#C0C4CC;top:5px}.el-table .sort-caret.descending{border-top-color:#C0C4CC;bottom:7px}.el-table .ascending .sort-caret.ascending{border-bottom-color:#783887}.el-table .descending .sort-caret.descending{border-top-color:#783887}.el-table .hidden-columns{position:absolute;z-index:-1}.el-table--striped .el-table__body tr.el-table__row--striped td{background:#FAFAFA}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td{background-color:rgb(242, 235, 243)}.el-table__body tr.hover-row.current-row>td,.el-table__body tr.hover-row.el-table__row--striped.current-row>td,.el-table__body tr.hover-row.el-table__row--striped>td,.el-table__body tr.hover-row>td{background-color:#F5F7FA}.el-table__body tr.current-row>td{background-color:rgb(242, 235, 243)}.el-table__column-resize-proxy{position:absolute;left:200px;top:0;bottom:0;width:0;border-left:1px solid #EBEEF5;z-index:10}.el-table__column-filter-trigger{display:inline-block;line-height:34px;cursor:pointer}.el-table__column-filter-trigger i{color:#909399;font-size:12px;-webkit-transform:scale(.75);transform:scale(.75)}.el-table--enable-row-transition .el-table__body td{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}.el-table--enable-row-hover .el-table__body tr:hover>td{background-color:#F5F7FA}.el-table--fluid-height .el-table__fixed,.el-table--fluid-height .el-table__fixed-right{bottom:0;overflow:hidden}.el-table [class*=el-table__row--level] .el-table__expand-icon{display:inline-block;width:20px;line-height:20px;height:20px;text-align:center;margin-right:3px}.el-table-column--selection .cell{padding-left:14px;padding-right:14px}.el-table-filter{border:1px solid #EBEEF5;border-radius:2px;background-color:#FFF;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:2px 0}.el-date-table td,.el-date-table td div{height:30px;-webkit-box-sizing:border-box}.el-table-filter__list{padding:5px 0;margin:0;list-style:none;min-width:100px}.el-table-filter__list-item{line-height:36px;padding:0 10px;cursor:pointer;font-size:14px}.el-table-filter__list-item:hover{background-color:rgb(242, 235, 243);color:rgb(147, 96, 159)}.el-table-filter__list-item.is-active{background-color:#783887;color:#FFF}.el-table-filter__content{min-width:100px}.el-table-filter__bottom{border-top:1px solid #EBEEF5;padding:8px}.el-table-filter__bottom button{background:0 0;border:none;color:#606266;cursor:pointer;font-size:13px;padding:0 3px}.el-date-table td.in-range div,.el-date-table td.in-range div:hover,.el-date-table.is-week-mode .el-date-table__row.current div,.el-date-table.is-week-mode .el-date-table__row:hover div{background-color:#F2F6FC}.el-table-filter__bottom button:hover{color:#783887}.el-table-filter__bottom button:focus{outline:0}.el-table-filter__bottom button.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-table-filter__wrap{max-height:280px}.el-table-filter__checkbox-group{padding:10px}.el-table-filter__checkbox-group label.el-checkbox{display:block;margin-right:5px;margin-bottom:8px;margin-left:5px}.el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}.el-date-table{font-size:12px;-ms-user-select:none;user-select:none}.el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:#606266}.el-date-table.is-week-mode .el-date-table__row:hover td:first-child div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table.is-week-mode .el-date-table__row:hover td:last-child div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td{width:32px;padding:4px 0;box-sizing:border-box;text-align:center;cursor:pointer;position:relative}.el-date-table td div{padding:3px 0;box-sizing:border-box}.el-date-table td span{width:24px;height:24px;display:block;margin:0 auto;line-height:24px;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);border-radius:50%}.el-date-table td.next-month,.el-date-table td.prev-month{color:#C0C4CC}.el-date-table td.today{position:relative}.el-date-table td.today span{color:#783887;font-weight:700}.el-date-table td.today.end-date span,.el-date-table td.today.start-date span{color:#FFF}.el-date-table td.available:hover{color:#783887}.el-date-table td.current:not(.disabled) span{color:#FFF;background-color:#783887}.el-date-table td.end-date div,.el-date-table td.start-date div{color:#FFF}.el-date-table td.end-date span,.el-date-table td.start-date span{background-color:#783887}.el-date-table td.start-date div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table td.end-date div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td.disabled div{background-color:#F5F7FA;opacity:1;cursor:not-allowed;color:#C0C4CC}.el-date-table td.selected div{margin-left:5px;margin-right:5px;background-color:#F2F6FC;border-radius:15px}.el-date-table td.selected div:hover{background-color:#F2F6FC}.el-date-table td.selected span{background-color:#783887;color:#FFF;border-radius:15px}.el-date-table td.week{font-size:80%;color:#606266}.el-month-table,.el-year-table{font-size:12px;border-collapse:collapse}.el-date-table th{padding:5px;color:#606266;font-weight:400;border-bottom:solid 1px #EBEEF5}.el-month-table{margin:-1px}.el-month-table td{text-align:center;padding:8px 0;cursor:pointer}.el-month-table td div{height:48px;padding:6px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-month-table td.today .cell{color:#783887;font-weight:700}.el-month-table td.today.end-date .cell,.el-month-table td.today.start-date .cell{color:#FFF}.el-month-table td.disabled .cell{background-color:#F5F7FA;cursor:not-allowed;color:#C0C4CC}.el-month-table td.disabled .cell:hover{color:#C0C4CC}.el-month-table td .cell{width:60px;height:36px;display:block;line-height:36px;color:#606266;margin:0 auto;border-radius:18px}.el-month-table td .cell:hover{color:#783887}.el-month-table td.in-range div,.el-month-table td.in-range div:hover{background-color:#F2F6FC}.el-month-table td.end-date div,.el-month-table td.start-date div{color:#FFF}.el-month-table td.end-date .cell,.el-month-table td.start-date .cell{color:#FFF;background-color:#783887}.el-month-table td.start-date div{border-top-left-radius:24px;border-bottom-left-radius:24px}.el-month-table td.end-date div{border-top-right-radius:24px;border-bottom-right-radius:24px}.el-month-table td.current:not(.disabled) .cell{color:#783887}.el-year-table{margin:-1px}.el-year-table .el-icon{color:#303133}.el-year-table td{text-align:center;padding:20px 3px;cursor:pointer}.el-year-table td.today .cell{color:#783887;font-weight:700}.el-year-table td.disabled .cell{background-color:#F5F7FA;cursor:not-allowed;color:#C0C4CC}.el-year-table td.disabled .cell:hover{color:#C0C4CC}.el-year-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#606266;margin:0 auto}.el-year-table td .cell:hover,.el-year-table td.current:not(.disabled) .cell{color:#783887}.el-date-range-picker{width:646px}.el-date-range-picker.has-sidebar{width:756px}.el-date-range-picker table{table-layout:fixed;width:100%}.el-date-range-picker .el-picker-panel__body{min-width:513px}.el-date-range-picker .el-picker-panel__content{margin:0}.el-date-range-picker__header{position:relative;text-align:center;height:28px}.el-date-range-picker__header [class*=arrow-left]{float:left}.el-date-range-picker__header [class*=arrow-right]{float:right}.el-date-range-picker__header div{font-size:16px;font-weight:500;margin-right:50px}.el-date-range-picker__content{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:16px}.el-date-range-picker__content.is-left{border-right:1px solid #e4e4e4}.el-date-range-picker__content .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.el-date-range-picker__editors-wrap{-webkit-box-sizing:border-box;box-sizing:border-box;display:table-cell}.el-date-range-picker__editors-wrap.is-right{text-align:right}.el-date-range-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-range-picker__time-header>.el-icon-arrow-right{font-size:20px;vertical-align:middle;display:table-cell;color:#303133}.el-date-range-picker__time-picker-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-range-picker__time-picker-wrap .el-picker-panel{position:absolute;top:13px;right:0;z-index:1;background:#FFF}.el-date-picker{width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-picker__header{margin:12px;text-align:center}.el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:solid 1px #EBEEF5}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#606266}.el-date-picker__header-label.active,.el-date-picker__header-label:hover{color:#783887}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{padding:10px;text-align:center}.el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.time-select{margin:5px 0;min-width:0}.time-select .el-picker-panel__content{max-height:200px;margin:0}.time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.time-select-item.selected:not(.disabled){color:#783887;font-weight:700}.time-select-item.disabled{color:#E4E7ED;cursor:not-allowed}.time-select-item:hover{background-color:#F5F7FA;font-weight:700;cursor:pointer}.el-date-editor{position:relative;display:inline-block;text-align:left}.el-date-editor.el-input,.el-date-editor.el-input__inner{width:220px}.el-date-editor--monthrange.el-input,.el-date-editor--monthrange.el-input__inner{width:300px}.el-date-editor--daterange.el-input,.el-date-editor--daterange.el-input__inner,.el-date-editor--timerange.el-input,.el-date-editor--timerange.el-input__inner{width:350px}.el-date-editor--datetimerange.el-input,.el-date-editor--datetimerange.el-input__inner{width:400px}.el-date-editor--dates .el-input__inner{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .el-icon-circle-close{cursor:pointer}.el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#C0C4CC;float:left;line-height:32px}.el-date-editor .el-range-input,.el-date-editor .el-range-separator{height:100%;margin:0;text-align:center;display:inline-block;font-size:14px}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;padding:0;width:39%;color:#606266}.el-date-editor .el-range-input::-webkit-input-placeholder{color:#C0C4CC}.el-date-editor .el-range-input:-ms-input-placeholder{color:#C0C4CC}.el-date-editor .el-range-input::-ms-input-placeholder{color:#C0C4CC}.el-date-editor .el-range-input::placeholder{color:#C0C4CC}.el-date-editor .el-range-separator{padding:0 5px;line-height:32px;width:5%;color:#303133}.el-date-editor .el-range__close-icon{font-size:14px;color:#C0C4CC;width:25px;display:inline-block;float:right;line-height:32px}.el-range-editor.el-input__inner{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:3px 10px}.el-range-editor .el-range-input{line-height:1}.el-range-editor.is-active,.el-range-editor.is-active:hover{border-color:#783887}.el-range-editor--medium.el-input__inner{height:36px}.el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.el-range-editor--medium .el-range-input{font-size:14px}.el-range-editor--medium .el-range__close-icon,.el-range-editor--medium .el-range__icon{line-height:28px}.el-range-editor--small.el-input__inner{height:32px}.el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.el-range-editor--small .el-range-input{font-size:13px}.el-range-editor--small .el-range__close-icon,.el-range-editor--small .el-range__icon{line-height:24px}.el-range-editor--mini.el-input__inner{height:28px}.el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.el-range-editor--mini .el-range-input{font-size:12px}.el-range-editor--mini .el-range__close-icon,.el-range-editor--mini .el-range__icon{line-height:20px}.el-range-editor.is-disabled{background-color:#F5F7FA;border-color:#E4E7ED;color:#C0C4CC;cursor:not-allowed}.el-range-editor.is-disabled:focus,.el-range-editor.is-disabled:hover{border-color:#E4E7ED}.el-range-editor.is-disabled input{background-color:#F5F7FA;color:#C0C4CC;cursor:not-allowed}.el-range-editor.is-disabled input::-webkit-input-placeholder{color:#C0C4CC}.el-range-editor.is-disabled input:-ms-input-placeholder{color:#C0C4CC}.el-range-editor.is-disabled input::-ms-input-placeholder{color:#C0C4CC}.el-range-editor.is-disabled input::placeholder{color:#C0C4CC}.el-range-editor.is-disabled .el-range-separator{color:#C0C4CC}.el-picker-panel{color:#606266;border:1px solid #E4E7ED;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#FFF;border-radius:4px;line-height:30px;margin:5px 0}.el-popover,.el-time-panel{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-picker-panel__body-wrapper::after,.el-picker-panel__body::after{content:"";display:table;clear:both}.el-picker-panel__content{position:relative;margin:15px}.el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#FFF;position:relative;font-size:0}.el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#606266;padding-left:12px;text-align:left;outline:0;cursor:pointer}.el-picker-panel__shortcut:hover{color:#783887}.el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#783887}.el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.el-picker-panel__icon-btn{font-size:12px;color:#303133;border:0;background:0 0;cursor:pointer;outline:0;margin-top:8px}.el-picker-panel__icon-btn:hover{color:#783887}.el-picker-panel__icon-btn.is-disabled{color:#bbb}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;background-color:#FFF;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__input.el-input .el-input__inner,.el-time-spinner__list{padding:0;text-align:center}.el-time-spinner__wrapper.is-arrow{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{-webkit-transform:translateY(-32px);transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#FFF;cursor:default}.el-time-spinner__arrow{font-size:12px;color:#909399;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.el-time-spinner__arrow:hover{color:#783887}.el-time-spinner__arrow.el-icon-arrow-up{top:10px}.el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__list{margin:0;list-style:none}.el-time-spinner__list::after,.el-time-spinner__list::before{content:'';display:block;width:100%;height:80px}.el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#606266}.el-time-spinner__item:hover:not(.disabled):not(.active){background:#F5F7FA;cursor:pointer}.el-time-spinner__item.active:not(.disabled){color:#303133;font-weight:700}.el-time-spinner__item.disabled{color:#C0C4CC;cursor:not-allowed}.el-time-panel{margin:5px 0;border:1px solid #E4E7ED;background-color:#FFF;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:content-box;box-sizing:content-box}.el-slider__button,.el-slider__button-wrapper{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content::after,.el-time-panel__content::before{content:"";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #E4E7ED;border-bottom:1px solid #E4E7ED}.el-time-panel__content::after{left:50%;margin-left:12%;margin-right:12%}.el-time-panel__content::before{padding-left:50%;margin-right:12%;margin-left:12%}.el-time-panel__content.has-seconds::after{left:calc(100% / 3 * 2)}.el-time-panel__content.has-seconds::before{padding-left:calc(100% / 3)}.el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:0;font-size:12px;color:#303133}.el-time-panel__btn.confirm{font-weight:800;color:#783887}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{position:relative;text-align:center;padding:10px}.el-time-range-picker__cell{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:4px 7px 7px;width:50%;display:inline-block}.el-time-range-picker__header{margin-bottom:5px;text-align:center;font-size:14px}.el-time-range-picker__body{border-radius:2px;border:1px solid #E4E7ED}.el-popover{position:absolute;background:#FFF;min-width:150px;border:1px solid #EBEEF5;padding:12px;z-index:2000;color:#606266;line-height:1.4;text-align:justify;font-size:14px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);word-break:break-all}.el-popover--plain{padding:18px 20px}.el-popover__title{color:#303133;font-size:16px;line-height:1;margin-bottom:12px}.v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{100%{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-popup-parent--hidden{overflow:hidden}.el-message-box{display:inline-block;width:420px;padding-bottom:10px;vertical-align:middle;background-color:#FFF;border-radius:4px;border:1px solid #EBEEF5;font-size:18px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);text-align:left;overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden}.el-message-box__wrapper{position:fixed;top:0;bottom:0;left:0;right:0;text-align:center}.el-message-box__wrapper::after{content:"";display:inline-block;height:100%;width:0;vertical-align:middle}.el-message-box__header{position:relative;padding:15px 15px 10px}.el-message-box__title{padding-left:0;margin-bottom:0;font-size:18px;line-height:1;color:#303133}.el-message-box__headerbtn{position:absolute;top:15px;right:15px;padding:0;border:none;outline:0;background:0 0;font-size:16px;cursor:pointer}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus,.el-message-box__input input.invalid,.el-message-box__input input.invalid:focus{border-color:#F56C6C}.el-message-box__headerbtn .el-message-box__close{color:#909399}.el-message-box__headerbtn:focus .el-message-box__close,.el-message-box__headerbtn:hover .el-message-box__close{color:#783887}.el-message-box__content{padding:10px 15px;color:#606266;font-size:14px}.el-message-box__container{position:relative}.el-message-box__input{padding-top:15px}.el-message-box__status{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);font-size:24px!important}.el-message-box__status::before{padding-left:1px}.el-message-box__status+.el-message-box__message{padding-left:36px;padding-right:12px}.el-message-box__status.el-icon-success{color:#67C23A}.el-message-box__status.el-icon-info{color:#909399}.el-message-box__status.el-icon-warning{color:#E6A23C}.el-message-box__status.el-icon-error{color:#F56C6C}.el-message-box__message{margin:0}.el-message-box__message p{margin:0;line-height:24px}.el-message-box__errormsg{color:#F56C6C;font-size:12px;min-height:18px;margin-top:2px}.el-message-box__btns{padding:5px 15px 0;text-align:right}.el-message-box__btns button:nth-child(2){margin-left:10px}.el-message-box__btns-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.el-message-box--center{padding-bottom:30px}.el-message-box--center .el-message-box__header{padding-top:30px}.el-message-box--center .el-message-box__title{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message-box--center .el-message-box__status{position:relative;top:auto;padding-right:5px;text-align:center;-webkit-transform:translateY(-1px);transform:translateY(-1px)}.el-message-box--center .el-message-box__message{margin-left:0}.el-message-box--center .el-message-box__btns,.el-message-box--center .el-message-box__content{text-align:center}.el-message-box--center .el-message-box__content{padding-left:27px;padding-right:27px}.msgbox-fade-enter-active{-webkit-animation:msgbox-fade-in .3s;animation:msgbox-fade-in .3s}.msgbox-fade-leave-active{-webkit-animation:msgbox-fade-out .3s;animation:msgbox-fade-out .3s}@-webkit-keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes msgbox-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes msgbox-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb::after,.el-breadcrumb::before{display:table;content:""}.el-breadcrumb::after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#C0C4CC}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner{color:#606266}.el-breadcrumb__inner a,.el-breadcrumb__inner.is-link{font-weight:700;text-decoration:none;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#303133}.el-breadcrumb__inner a:hover,.el-breadcrumb__inner.is-link:hover{color:#783887;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover{font-weight:400;color:#606266;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{display:inline-block;margin-right:10px;vertical-align:top}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item::after,.el-form-item::before{display:table;content:""}.el-form-item::after{clear:both}.el-form-item .el-form-item{margin-bottom:0}.el-form-item--mini.el-form-item,.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label-wrap{float:left}.el-form-item__label-wrap .el-form-item__label{display:inline-block;float:none}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#606266;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content::after,.el-form-item__content::before{display:table;content:""}.el-form-item__content::after{clear:both}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:#F56C6C;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label:before{content:'*';color:#F56C6C;margin-right:4px}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#F56C6C}.el-form-item--feedback .el-input__validateIcon{display:inline-block}.el-tabs__header{padding:0;position:relative;margin:0 0 15px}.el-tabs__active-bar{position:absolute;bottom:0;left:0;height:2px;background-color:#783887;z-index:1;-webkit-transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1),-webkit-transform .3s cubic-bezier(.645,.045,.355,1);list-style:none}.el-tabs__new-tab{float:right;border:1px solid #d3dce6;height:18px;width:18px;line-height:18px;margin:12px 0 9px 10px;border-radius:3px;text-align:center;font-size:12px;color:#d3dce6;cursor:pointer;-webkit-transition:all .15s;transition:all .15s}.el-collapse-item__arrow,.el-tabs__nav{-webkit-transition:-webkit-transform .3s}.el-tabs__new-tab .el-icon-plus{-webkit-transform:scale(.8,.8);transform:scale(.8,.8)}.el-tabs__new-tab:hover{color:#783887}.el-tabs__nav-wrap{overflow:hidden;margin-bottom:-1px;position:relative}.el-tabs__nav-wrap::after{content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background-color:#E4E7ED;z-index:1}.el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap::after,.el-tabs--card>.el-tabs__header .el-tabs__nav-wrap::after{content:none}.el-tabs__nav-wrap.is-scrollable{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs__nav-scroll{overflow:hidden}.el-tabs__nav-next,.el-tabs__nav-prev{position:absolute;cursor:pointer;line-height:44px;font-size:12px;color:#909399}.el-tabs__nav-next{right:0}.el-tabs__nav-prev{left:0}.el-tabs__nav{white-space:nowrap;position:relative;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:left;z-index:2}.el-tabs__nav.is-stretch{min-width:100%;display:-webkit-box;display:-ms-flexbox;display:flex}.el-tabs__nav.is-stretch>*{-webkit-box-flex:1;-ms-flex:1;flex:1;text-align:center}.el-tabs__item{padding:0 20px;height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:40px;display:inline-block;list-style:none;font-size:14px;font-weight:500;color:#303133;position:relative}.el-tabs__item:focus,.el-tabs__item:focus:active{outline:0}.el-tabs__item:focus.is-active.is-focus:not(:active){-webkit-box-shadow:0 0 2px 2px #783887 inset;box-shadow:0 0 2px 2px #783887 inset;border-radius:3px}.el-tabs__item .el-icon-close{border-radius:50%;text-align:center;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);margin-left:5px}.el-tabs__item .el-icon-close:before{-webkit-transform:scale(.9);transform:scale(.9);display:inline-block}.el-tabs__item .el-icon-close:hover{background-color:#C0C4CC;color:#FFF}.el-tabs__item.is-active{color:#783887}.el-tabs__item:hover{color:#783887;cursor:pointer}.el-tabs__item.is-disabled{color:#C0C4CC;cursor:default}.el-tabs__content{overflow:hidden;position:relative}.el-tabs--card>.el-tabs__header{border-bottom:1px solid #E4E7ED}.el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid #E4E7ED;border-bottom:none;border-radius:4px 4px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.el-tabs--card>.el-tabs__header .el-tabs__item .el-icon-close{position:relative;font-size:12px;width:0;height:14px;vertical-align:middle;line-height:15px;overflow:hidden;top:-1px;right:-2px;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .el-icon-close,.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .el-icon-close{width:14px}.el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid transparent;border-left:1px solid #E4E7ED;-webkit-transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1);transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:#FFF}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.el-tabs--border-card{background:#FFF;border:1px solid #DCDFE6;-webkit-box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04);box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04)}.el-tabs--border-card>.el-tabs__content{padding:15px}.el-tabs--border-card>.el-tabs__header{background-color:#F5F7FA;border-bottom:1px solid #E4E7ED;margin:0}.el-tabs--border-card>.el-tabs__header .el-tabs__item{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);border:1px solid transparent;margin-top:-1px;color:#909399}.el-tabs--border-card>.el-tabs__header .el-tabs__item+.el-tabs__item,.el-tabs--border-card>.el-tabs__header .el-tabs__item:first-child{margin-left:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:#783887;background-color:#FFF;border-right-color:#DCDFE6;border-left-color:#DCDFE6}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:#783887}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-disabled{color:#C0C4CC}.el-tabs--border-card>.el-tabs__header .is-scrollable .el-tabs__item:first-child{margin-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2),.el-tabs--bottom .el-tabs__item.is-top:nth-child(2),.el-tabs--top .el-tabs__item.is-bottom:nth-child(2),.el-tabs--top .el-tabs__item.is-top:nth-child(2){padding-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:last-child,.el-tabs--bottom .el-tabs__item.is-top:last-child,.el-tabs--top .el-tabs__item.is-bottom:last-child,.el-tabs--top .el-tabs__item.is-top:last-child{padding-right:0}.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:20px}.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child{padding-right:20px}.el-tabs--bottom .el-tabs__header.is-bottom{margin-bottom:0;margin-top:10px}.el-tabs--bottom.el-tabs--border-card .el-tabs__header.is-bottom{border-bottom:0;border-top:1px solid #DCDFE6}.el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap.is-bottom{margin-top:-1px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom:not(.is-active){border:1px solid transparent}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom{margin:0 -1px -1px}.el-tabs--left,.el-tabs--right{overflow:hidden}.el-tabs--left .el-tabs__header.is-left,.el-tabs--left .el-tabs__header.is-right,.el-tabs--left .el-tabs__nav-scroll,.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__header.is-left,.el-tabs--right .el-tabs__header.is-right,.el-tabs--right .el-tabs__nav-scroll,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{height:100%}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__active-bar.is-right,.el-tabs--right .el-tabs__active-bar.is-left,.el-tabs--right .el-tabs__active-bar.is-right{top:0;bottom:auto;width:2px;height:auto}.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{margin-bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{height:30px;line-height:30px;width:100%;text-align:center;cursor:pointer}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i{-webkit-transform:rotateZ(90deg);transform:rotateZ(90deg)}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{left:auto;top:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{right:auto;bottom:0}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__nav-wrap.is-left::after{right:0;left:auto}.el-tabs--left .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--left .el-tabs__nav-wrap.is-right.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-right.is-scrollable{padding:30px 0}.el-tabs--left .el-tabs__nav-wrap.is-left::after,.el-tabs--left .el-tabs__nav-wrap.is-right::after,.el-tabs--right .el-tabs__nav-wrap.is-left::after,.el-tabs--right .el-tabs__nav-wrap.is-right::after{height:100%;width:2px;bottom:auto;top:0}.el-tabs--left .el-tabs__nav.is-left,.el-tabs--left .el-tabs__nav.is-right,.el-tabs--right .el-tabs__nav.is-left,.el-tabs--right .el-tabs__nav.is-right{float:none}.el-tabs--left .el-tabs__item.is-left,.el-tabs--left .el-tabs__item.is-right,.el-tabs--right .el-tabs__item.is-left,.el-tabs--right .el-tabs__item.is-right{display:block}.el-tabs--left.el-tabs--card .el-tabs__active-bar.is-left,.el-tabs--right.el-tabs--card .el-tabs__active-bar.is-right{display:none}.el-tabs--left .el-tabs__header.is-left{float:left;margin-bottom:0;margin-right:10px}.el-tabs--left .el-tabs__nav-wrap.is-left{margin-right:-1px}.el-tabs--left .el-tabs__item.is-left{text-align:right}.el-tabs--left.el-tabs--card .el-tabs__item.is-left{border-left:none;border-right:1px solid #E4E7ED;border-bottom:none;border-top:1px solid #E4E7ED;text-align:left}.el-tabs--left.el-tabs--card .el-tabs__item.is-left:first-child{border-right:1px solid #E4E7ED;border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{border:1px solid #E4E7ED;border-right-color:#fff;border-left:none;border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:first-child{border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:last-child{border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__nav{border-radius:4px 0 0 4px;border-bottom:1px solid #E4E7ED;border-right:none}.el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.el-tabs--left.el-tabs--border-card .el-tabs__header.is-left{border-right:1px solid #dfe4ed}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left{border:1px solid transparent;margin:-1px 0 -1px -1px}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left.is-active{border-color:#d1dbe5 transparent}.el-tabs--right .el-tabs__header.is-right{float:right;margin-bottom:0;margin-left:10px}.el-tabs--right .el-tabs__nav-wrap.is-right{margin-left:-1px}.el-tabs--right .el-tabs__nav-wrap.is-right::after{left:0;right:auto}.el-tabs--right .el-tabs__active-bar.is-right{left:0}.el-tabs--right.el-tabs--card .el-tabs__item.is-right{border-bottom:none;border-top:1px solid #E4E7ED}.el-tabs--right.el-tabs--card .el-tabs__item.is-right:first-child{border-left:1px solid #E4E7ED;border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active{border:1px solid #E4E7ED;border-left-color:#fff;border-right:none;border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:first-child{border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:last-child{border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__nav{border-radius:0 4px 4px 0;border-bottom:1px solid #E4E7ED;border-left:none}.el-tabs--right.el-tabs--border-card .el-tabs__header.is-right{border-left:1px solid #dfe4ed}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right{border:1px solid transparent;margin:-1px -1px -1px 0}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right.is-active{border-color:#d1dbe5 transparent}.slideInLeft-transition,.slideInRight-transition{display:inline-block}.slideInRight-enter{-webkit-animation:slideInRight-enter .3s;animation:slideInRight-enter .3s}.slideInRight-leave{position:absolute;left:0;right:0;-webkit-animation:slideInRight-leave .3s;animation:slideInRight-leave .3s}.slideInLeft-enter{-webkit-animation:slideInLeft-enter .3s;animation:slideInLeft-enter .3s}.slideInLeft-leave{position:absolute;left:0;right:0;-webkit-animation:slideInLeft-leave .3s;animation:slideInLeft-leave .3s}@-webkit-keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@-webkit-keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}@keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}.el-tree{position:relative;cursor:default;background:#FFF;color:#606266}.el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.el-tree__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#909399;font-size:14px}.el-tree__drop-indicator{position:absolute;left:0;right:0;height:1px;background-color:#783887}.el-tree-node{white-space:nowrap;outline:0}.el-tree-node:focus>.el-tree-node__content{background-color:#F5F7FA}.el-tree-node.is-drop-inner>.el-tree-node__content .el-tree-node__label{background-color:#783887;color:#fff}.el-tree-node__content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.el-tree-node__content>label.el-checkbox{margin-right:8px}.el-tree-node__content:hover{background-color:#F5F7FA}.el-tree.is-dragging .el-tree-node__content{cursor:move}.el-tree.is-dragging.is-drop-not-allow .el-tree-node__content{cursor:not-allowed}.el-tree-node__expand-icon{cursor:pointer;color:#C0C4CC;font-size:12px;-webkit-transform:rotate(0);transform:rotate(0);-webkit-transition:-webkit-transform .3s ease-in-out;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out}.el-tree-node__expand-icon.expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.el-tree-node__label{font-size:14px}.el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#C0C4CC}.el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.el-tree-node.is-expanded>.el-tree-node__children{display:block}.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#f0f7ff}.el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#FFF;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.el-alert.is-light .el-alert__closebtn{color:#C0C4CC}.el-alert.is-dark .el-alert__closebtn,.el-alert.is-dark .el-alert__description{color:#FFF}.el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-alert--success.is-light{background-color:#f0f9eb;color:#67C23A}.el-alert--success.is-light .el-alert__description{color:#67C23A}.el-alert--success.is-dark{background-color:#67C23A;color:#FFF}.el-alert--info.is-light{background-color:#f4f4f5;color:#909399}.el-alert--info.is-dark{background-color:#909399;color:#FFF}.el-alert--info .el-alert__description{color:#909399}.el-alert--warning.is-light{background-color:#fdf6ec;color:#E6A23C}.el-alert--warning.is-light .el-alert__description{color:#E6A23C}.el-alert--warning.is-dark{background-color:#E6A23C;color:#FFF}.el-alert--error.is-light{background-color:#fef0f0;color:#F56C6C}.el-alert--error.is-light .el-alert__description{color:#F56C6C}.el-alert--error.is-dark{background-color:#F56C6C;color:#FFF}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert-fade-enter,.el-alert-fade-leave-active,.el-loading-fade-enter,.el-loading-fade-leave-active,.el-notification-fade-leave-active{opacity:0}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #EBEEF5;position:fixed;background-color:#FFF;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{margin-left:13px;margin-right:8px}.el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}.el-notification__content p{margin:0}.el-notification__icon{height:24px;width:24px;font-size:24px}.el-notification__closeBtn{position:absolute;top:18px;right:15px;cursor:pointer;color:#909399;font-size:16px}.el-notification__closeBtn:hover{color:#606266}.el-notification .el-icon-success{color:#67C23A}.el-notification .el-icon-error{color:#F56C6C}.el-notification .el-icon-info{color:#909399}.el-notification .el-icon-warning{color:#E6A23C}.el-notification-fade-enter.right{right:0;-webkit-transform:translateX(100%);transform:translateX(100%)}.el-notification-fade-enter.left{left:0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}.el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.el-input-number .el-input{display:block}.el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.el-input-number__decrease,.el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#F5F7FA;color:#606266;cursor:pointer;font-size:13px}.el-input-number__decrease:hover,.el-input-number__increase:hover{color:#783887}.el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#783887}.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #DCDFE6}.el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #DCDFE6}.el-input-number.is-disabled .el-input-number__decrease,.el-input-number.is-disabled .el-input-number__increase{border-color:#E4E7ED;color:#E4E7ED}.el-input-number.is-disabled .el-input-number__decrease:hover,.el-input-number.is-disabled .el-input-number__increase:hover{color:#E4E7ED;cursor:not-allowed}.el-input-number--medium{width:200px;line-height:34px}.el-input-number--medium .el-input-number__decrease,.el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.el-input-number--small{width:130px;line-height:30px}.el-input-number--small .el-input-number__decrease,.el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.el-input-number--small .el-input-number__decrease [class*=el-icon],.el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.el-input-number--mini{width:130px;line-height:26px}.el-input-number--mini .el-input-number__decrease,.el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.el-input-number--mini .el-input-number__decrease [class*=el-icon],.el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.el-input-number.is-controls-right .el-input-number__decrease,.el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #DCDFE6}.el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #DCDFE6;border-radius:0 0 4px}.el-input-number.is-controls-right[class*=medium] [class*=decrease],.el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.el-input-number.is-controls-right[class*=small] [class*=decrease],.el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.el-input-number.is-controls-right[class*=mini] [class*=decrease],.el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2;min-width:10px;word-wrap:break-word}.el-tooltip__popper .popper__arrow,.el-tooltip__popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-tooltip__popper .popper__arrow{border-width:6px}.el-tooltip__popper .popper__arrow::after{content:" ";border-width:5px}.el-progress-bar__inner::after,.el-row::after,.el-row::before,.el-slider::after,.el-slider::before,.el-slider__button-wrapper::after,.el-upload-cover::after{content:""}.el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-5px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=right]{margin-left:12px}.el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=right] .popper__arrow::after{bottom:-5px;left:1px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=left]{margin-right:12px}.el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper.is-dark{background:#303133;color:#FFF}.el-tooltip__popper.is-light{background:#FFF;border:1px solid #303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow::after{border-top-color:#FFF}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#303133}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow::after{border-bottom-color:#FFF}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#303133}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow::after{border-left-color:#FFF}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#303133}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow::after{border-right-color:#FFF}.el-slider::after,.el-slider::before{display:table}.el-slider__button-wrapper .el-tooltip,.el-slider__button-wrapper::after{vertical-align:middle;display:inline-block}.el-slider::after{clear:both}.el-slider__runway{width:100%;height:6px;margin:16px 0;background-color:#E4E7ED;border-radius:3px;position:relative;cursor:pointer;vertical-align:middle}.el-slider__runway.show-input{margin-right:160px;width:auto}.el-slider__runway.disabled{cursor:default}.el-slider__runway.disabled .el-slider__bar{background-color:#C0C4CC}.el-slider__runway.disabled .el-slider__button{border-color:#C0C4CC}.el-slider__runway.disabled .el-slider__button-wrapper.dragging,.el-slider__runway.disabled .el-slider__button-wrapper.hover,.el-slider__runway.disabled .el-slider__button-wrapper:hover{cursor:not-allowed}.el-slider__runway.disabled .el-slider__button.dragging,.el-slider__runway.disabled .el-slider__button.hover,.el-slider__runway.disabled .el-slider__button:hover{-webkit-transform:scale(1);transform:scale(1);cursor:not-allowed}.el-slider__button-wrapper,.el-slider__stop{-webkit-transform:translateX(-50%);position:absolute}.el-slider__input{float:right;margin-top:3px;width:130px}.el-slider__input.el-input-number--mini{margin-top:5px}.el-slider__input.el-input-number--medium{margin-top:0}.el-slider__input.el-input-number--large{margin-top:-2px}.el-slider__bar{height:6px;background-color:#783887;border-top-left-radius:3px;border-bottom-left-radius:3px;position:absolute}.el-slider__button-wrapper{height:36px;width:36px;z-index:1001;top:-15px;transform:translateX(-50%);background-color:transparent;text-align:center;user-select:none;line-height:normal}.el-slider__button-wrapper::after{height:100%}.el-slider__button-wrapper.hover,.el-slider__button-wrapper:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button-wrapper.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__button{width:16px;height:16px;border:2px solid #783887;background-color:#FFF;border-radius:50%;-webkit-transition:.2s;transition:.2s;user-select:none}.el-image-viewer__btn,.el-step__icon-inner{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-slider__button.dragging,.el-slider__button.hover,.el-slider__button:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.el-slider__button.hover,.el-slider__button:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__stop{height:6px;width:6px;border-radius:100%;background-color:#FFF;transform:translateX(-50%)}.el-slider__marks{top:0;left:12px;width:18px;height:100%}.el-slider__marks-text{position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-size:14px;color:#909399;margin-top:15px}.el-slider.is-vertical{position:relative}.el-slider.is-vertical .el-slider__runway{width:6px;height:100%;margin:0 16px}.el-slider.is-vertical .el-slider__bar{width:6px;height:auto;border-radius:0 0 3px 3px}.el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:-15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.el-slider.is-vertical .el-slider__stop{-webkit-transform:translateY(50%);transform:translateY(50%)}.el-slider.is-vertical.el-slider--with-input{padding-bottom:58px}.el-slider.is-vertical.el-slider--with-input .el-slider__input{overflow:visible;float:none;position:absolute;bottom:22px;width:36px;margin-top:15px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input__inner{text-align:center;padding-left:5px;padding-right:5px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{top:32px;margin-top:-1px;border:1px solid #DCDFE6;line-height:20px;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease{width:18px;right:18px;border-bottom-left-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{width:19px;border-bottom-right-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase~.el-input .el-input__inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__increase{border-color:#C0C4CC}.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__increase{border-color:#783887}.el-slider.is-vertical .el-slider__marks-text{margin-top:0;left:15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:2000;background-color:rgba(255,255,255,.9);margin:0;top:0;right:0;bottom:0;left:0;-webkit-transition:opacity .3s;transition:opacity .3s}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.el-col-pull-0,.el-col-pull-1,.el-col-pull-10,.el-col-pull-11,.el-col-pull-13,.el-col-pull-14,.el-col-pull-15,.el-col-pull-16,.el-col-pull-17,.el-col-pull-18,.el-col-pull-19,.el-col-pull-2,.el-col-pull-20,.el-col-pull-21,.el-col-pull-22,.el-col-pull-23,.el-col-pull-24,.el-col-pull-3,.el-col-pull-4,.el-col-pull-5,.el-col-pull-6,.el-col-pull-7,.el-col-pull-8,.el-col-pull-9,.el-col-push-0,.el-col-push-1,.el-col-push-10,.el-col-push-11,.el-col-push-12,.el-col-push-13,.el-col-push-14,.el-col-push-15,.el-col-push-16,.el-col-push-17,.el-col-push-18,.el-col-push-19,.el-col-push-2,.el-col-push-20,.el-col-push-21,.el-col-push-22,.el-col-push-23,.el-col-push-24,.el-col-push-3,.el-col-push-4,.el-col-push-5,.el-col-push-6,.el-col-push-7,.el-col-push-8,.el-col-push-9,.el-row{position:relative}.el-loading-spinner .el-loading-text{color:#783887;margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:42px;width:42px;-webkit-animation:loading-rotate 2s linear infinite;animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{-webkit-animation:loading-dash 1.5s ease-in-out infinite;animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#783887;stroke-linecap:round}.el-loading-spinner i{color:#783887}@-webkit-keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.el-row{-webkit-box-sizing:border-box;box-sizing:border-box}.el-row::after,.el-row::before{display:table}.el-row::after{clear:both}.el-row--flex{display:-webkit-box;display:-ms-flexbox;display:flex}.el-col-0,.el-row--flex:after,.el-row--flex:before{display:none}.el-row--flex.is-justify-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-row--flex.is-justify-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.el-row--flex.is-justify-space-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.el-row--flex.is-align-middle{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-row--flex.is-align-bottom{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}[class*=el-col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.el-upload--picture-card,.el-upload-dragger{-webkit-box-sizing:border-box;cursor:pointer}.el-col-0{width:0%}.el-col-offset-0{margin-left:0}.el-col-pull-0{right:0}.el-col-push-0{left:0}.el-col-1{width:4.16667%}.el-col-offset-1{margin-left:4.16667%}.el-col-pull-1{right:4.16667%}.el-col-push-1{left:4.16667%}.el-col-2{width:8.33333%}.el-col-offset-2{margin-left:8.33333%}.el-col-pull-2{right:8.33333%}.el-col-push-2{left:8.33333%}.el-col-3{width:12.5%}.el-col-offset-3{margin-left:12.5%}.el-col-pull-3{right:12.5%}.el-col-push-3{left:12.5%}.el-col-4{width:16.66667%}.el-col-offset-4{margin-left:16.66667%}.el-col-pull-4{right:16.66667%}.el-col-push-4{left:16.66667%}.el-col-5{width:20.83333%}.el-col-offset-5{margin-left:20.83333%}.el-col-pull-5{right:20.83333%}.el-col-push-5{left:20.83333%}.el-col-6{width:25%}.el-col-offset-6{margin-left:25%}.el-col-pull-6{right:25%}.el-col-push-6{left:25%}.el-col-7{width:29.16667%}.el-col-offset-7{margin-left:29.16667%}.el-col-pull-7{right:29.16667%}.el-col-push-7{left:29.16667%}.el-col-8{width:33.33333%}.el-col-offset-8{margin-left:33.33333%}.el-col-pull-8{right:33.33333%}.el-col-push-8{left:33.33333%}.el-col-9{width:37.5%}.el-col-offset-9{margin-left:37.5%}.el-col-pull-9{right:37.5%}.el-col-push-9{left:37.5%}.el-col-10{width:41.66667%}.el-col-offset-10{margin-left:41.66667%}.el-col-pull-10{right:41.66667%}.el-col-push-10{left:41.66667%}.el-col-11{width:45.83333%}.el-col-offset-11{margin-left:45.83333%}.el-col-pull-11{right:45.83333%}.el-col-push-11{left:45.83333%}.el-col-12{width:50%}.el-col-offset-12{margin-left:50%}.el-col-pull-12{position:relative;right:50%}.el-col-push-12{left:50%}.el-col-13{width:54.16667%}.el-col-offset-13{margin-left:54.16667%}.el-col-pull-13{right:54.16667%}.el-col-push-13{left:54.16667%}.el-col-14{width:58.33333%}.el-col-offset-14{margin-left:58.33333%}.el-col-pull-14{right:58.33333%}.el-col-push-14{left:58.33333%}.el-col-15{width:62.5%}.el-col-offset-15{margin-left:62.5%}.el-col-pull-15{right:62.5%}.el-col-push-15{left:62.5%}.el-col-16{width:66.66667%}.el-col-offset-16{margin-left:66.66667%}.el-col-pull-16{right:66.66667%}.el-col-push-16{left:66.66667%}.el-col-17{width:70.83333%}.el-col-offset-17{margin-left:70.83333%}.el-col-pull-17{right:70.83333%}.el-col-push-17{left:70.83333%}.el-col-18{width:75%}.el-col-offset-18{margin-left:75%}.el-col-pull-18{right:75%}.el-col-push-18{left:75%}.el-col-19{width:79.16667%}.el-col-offset-19{margin-left:79.16667%}.el-col-pull-19{right:79.16667%}.el-col-push-19{left:79.16667%}.el-col-20{width:83.33333%}.el-col-offset-20{margin-left:83.33333%}.el-col-pull-20{right:83.33333%}.el-col-push-20{left:83.33333%}.el-col-21{width:87.5%}.el-col-offset-21{margin-left:87.5%}.el-col-pull-21{right:87.5%}.el-col-push-21{left:87.5%}.el-col-22{width:91.66667%}.el-col-offset-22{margin-left:91.66667%}.el-col-pull-22{right:91.66667%}.el-col-push-22{left:91.66667%}.el-col-23{width:95.83333%}.el-col-offset-23{margin-left:95.83333%}.el-col-pull-23{right:95.83333%}.el-col-push-23{left:95.83333%}.el-col-24{width:100%}.el-col-offset-24{margin-left:100%}.el-col-pull-24{right:100%}.el-col-push-24{left:100%}@media only screen and (max-width:767px){.el-col-xs-0{display:none;width:0%}.el-col-xs-offset-0{margin-left:0}.el-col-xs-pull-0{position:relative;right:0}.el-col-xs-push-0{position:relative;left:0}.el-col-xs-1{width:4.16667%}.el-col-xs-offset-1{margin-left:4.16667%}.el-col-xs-pull-1{position:relative;right:4.16667%}.el-col-xs-push-1{position:relative;left:4.16667%}.el-col-xs-2{width:8.33333%}.el-col-xs-offset-2{margin-left:8.33333%}.el-col-xs-pull-2{position:relative;right:8.33333%}.el-col-xs-push-2{position:relative;left:8.33333%}.el-col-xs-3{width:12.5%}.el-col-xs-offset-3{margin-left:12.5%}.el-col-xs-pull-3{position:relative;right:12.5%}.el-col-xs-push-3{position:relative;left:12.5%}.el-col-xs-4{width:16.66667%}.el-col-xs-offset-4{margin-left:16.66667%}.el-col-xs-pull-4{position:relative;right:16.66667%}.el-col-xs-push-4{position:relative;left:16.66667%}.el-col-xs-5{width:20.83333%}.el-col-xs-offset-5{margin-left:20.83333%}.el-col-xs-pull-5{position:relative;right:20.83333%}.el-col-xs-push-5{position:relative;left:20.83333%}.el-col-xs-6{width:25%}.el-col-xs-offset-6{margin-left:25%}.el-col-xs-pull-6{position:relative;right:25%}.el-col-xs-push-6{position:relative;left:25%}.el-col-xs-7{width:29.16667%}.el-col-xs-offset-7{margin-left:29.16667%}.el-col-xs-pull-7{position:relative;right:29.16667%}.el-col-xs-push-7{position:relative;left:29.16667%}.el-col-xs-8{width:33.33333%}.el-col-xs-offset-8{margin-left:33.33333%}.el-col-xs-pull-8{position:relative;right:33.33333%}.el-col-xs-push-8{position:relative;left:33.33333%}.el-col-xs-9{width:37.5%}.el-col-xs-offset-9{margin-left:37.5%}.el-col-xs-pull-9{position:relative;right:37.5%}.el-col-xs-push-9{position:relative;left:37.5%}.el-col-xs-10{width:41.66667%}.el-col-xs-offset-10{margin-left:41.66667%}.el-col-xs-pull-10{position:relative;right:41.66667%}.el-col-xs-push-10{position:relative;left:41.66667%}.el-col-xs-11{width:45.83333%}.el-col-xs-offset-11{margin-left:45.83333%}.el-col-xs-pull-11{position:relative;right:45.83333%}.el-col-xs-push-11{position:relative;left:45.83333%}.el-col-xs-12{width:50%}.el-col-xs-offset-12{margin-left:50%}.el-col-xs-pull-12{position:relative;right:50%}.el-col-xs-push-12{position:relative;left:50%}.el-col-xs-13{width:54.16667%}.el-col-xs-offset-13{margin-left:54.16667%}.el-col-xs-pull-13{position:relative;right:54.16667%}.el-col-xs-push-13{position:relative;left:54.16667%}.el-col-xs-14{width:58.33333%}.el-col-xs-offset-14{margin-left:58.33333%}.el-col-xs-pull-14{position:relative;right:58.33333%}.el-col-xs-push-14{position:relative;left:58.33333%}.el-col-xs-15{width:62.5%}.el-col-xs-offset-15{margin-left:62.5%}.el-col-xs-pull-15{position:relative;right:62.5%}.el-col-xs-push-15{position:relative;left:62.5%}.el-col-xs-16{width:66.66667%}.el-col-xs-offset-16{margin-left:66.66667%}.el-col-xs-pull-16{position:relative;right:66.66667%}.el-col-xs-push-16{position:relative;left:66.66667%}.el-col-xs-17{width:70.83333%}.el-col-xs-offset-17{margin-left:70.83333%}.el-col-xs-pull-17{position:relative;right:70.83333%}.el-col-xs-push-17{position:relative;left:70.83333%}.el-col-xs-18{width:75%}.el-col-xs-offset-18{margin-left:75%}.el-col-xs-pull-18{position:relative;right:75%}.el-col-xs-push-18{position:relative;left:75%}.el-col-xs-19{width:79.16667%}.el-col-xs-offset-19{margin-left:79.16667%}.el-col-xs-pull-19{position:relative;right:79.16667%}.el-col-xs-push-19{position:relative;left:79.16667%}.el-col-xs-20{width:83.33333%}.el-col-xs-offset-20{margin-left:83.33333%}.el-col-xs-pull-20{position:relative;right:83.33333%}.el-col-xs-push-20{position:relative;left:83.33333%}.el-col-xs-21{width:87.5%}.el-col-xs-offset-21{margin-left:87.5%}.el-col-xs-pull-21{position:relative;right:87.5%}.el-col-xs-push-21{position:relative;left:87.5%}.el-col-xs-22{width:91.66667%}.el-col-xs-offset-22{margin-left:91.66667%}.el-col-xs-pull-22{position:relative;right:91.66667%}.el-col-xs-push-22{position:relative;left:91.66667%}.el-col-xs-23{width:95.83333%}.el-col-xs-offset-23{margin-left:95.83333%}.el-col-xs-pull-23{position:relative;right:95.83333%}.el-col-xs-push-23{position:relative;left:95.83333%}.el-col-xs-24{width:100%}.el-col-xs-offset-24{margin-left:100%}.el-col-xs-pull-24{position:relative;right:100%}.el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.el-col-sm-0{display:none;width:0%}.el-col-sm-offset-0{margin-left:0}.el-col-sm-pull-0{position:relative;right:0}.el-col-sm-push-0{position:relative;left:0}.el-col-sm-1{width:4.16667%}.el-col-sm-offset-1{margin-left:4.16667%}.el-col-sm-pull-1{position:relative;right:4.16667%}.el-col-sm-push-1{position:relative;left:4.16667%}.el-col-sm-2{width:8.33333%}.el-col-sm-offset-2{margin-left:8.33333%}.el-col-sm-pull-2{position:relative;right:8.33333%}.el-col-sm-push-2{position:relative;left:8.33333%}.el-col-sm-3{width:12.5%}.el-col-sm-offset-3{margin-left:12.5%}.el-col-sm-pull-3{position:relative;right:12.5%}.el-col-sm-push-3{position:relative;left:12.5%}.el-col-sm-4{width:16.66667%}.el-col-sm-offset-4{margin-left:16.66667%}.el-col-sm-pull-4{position:relative;right:16.66667%}.el-col-sm-push-4{position:relative;left:16.66667%}.el-col-sm-5{width:20.83333%}.el-col-sm-offset-5{margin-left:20.83333%}.el-col-sm-pull-5{position:relative;right:20.83333%}.el-col-sm-push-5{position:relative;left:20.83333%}.el-col-sm-6{width:25%}.el-col-sm-offset-6{margin-left:25%}.el-col-sm-pull-6{position:relative;right:25%}.el-col-sm-push-6{position:relative;left:25%}.el-col-sm-7{width:29.16667%}.el-col-sm-offset-7{margin-left:29.16667%}.el-col-sm-pull-7{position:relative;right:29.16667%}.el-col-sm-push-7{position:relative;left:29.16667%}.el-col-sm-8{width:33.33333%}.el-col-sm-offset-8{margin-left:33.33333%}.el-col-sm-pull-8{position:relative;right:33.33333%}.el-col-sm-push-8{position:relative;left:33.33333%}.el-col-sm-9{width:37.5%}.el-col-sm-offset-9{margin-left:37.5%}.el-col-sm-pull-9{position:relative;right:37.5%}.el-col-sm-push-9{position:relative;left:37.5%}.el-col-sm-10{width:41.66667%}.el-col-sm-offset-10{margin-left:41.66667%}.el-col-sm-pull-10{position:relative;right:41.66667%}.el-col-sm-push-10{position:relative;left:41.66667%}.el-col-sm-11{width:45.83333%}.el-col-sm-offset-11{margin-left:45.83333%}.el-col-sm-pull-11{position:relative;right:45.83333%}.el-col-sm-push-11{position:relative;left:45.83333%}.el-col-sm-12{width:50%}.el-col-sm-offset-12{margin-left:50%}.el-col-sm-pull-12{position:relative;right:50%}.el-col-sm-push-12{position:relative;left:50%}.el-col-sm-13{width:54.16667%}.el-col-sm-offset-13{margin-left:54.16667%}.el-col-sm-pull-13{position:relative;right:54.16667%}.el-col-sm-push-13{position:relative;left:54.16667%}.el-col-sm-14{width:58.33333%}.el-col-sm-offset-14{margin-left:58.33333%}.el-col-sm-pull-14{position:relative;right:58.33333%}.el-col-sm-push-14{position:relative;left:58.33333%}.el-col-sm-15{width:62.5%}.el-col-sm-offset-15{margin-left:62.5%}.el-col-sm-pull-15{position:relative;right:62.5%}.el-col-sm-push-15{position:relative;left:62.5%}.el-col-sm-16{width:66.66667%}.el-col-sm-offset-16{margin-left:66.66667%}.el-col-sm-pull-16{position:relative;right:66.66667%}.el-col-sm-push-16{position:relative;left:66.66667%}.el-col-sm-17{width:70.83333%}.el-col-sm-offset-17{margin-left:70.83333%}.el-col-sm-pull-17{position:relative;right:70.83333%}.el-col-sm-push-17{position:relative;left:70.83333%}.el-col-sm-18{width:75%}.el-col-sm-offset-18{margin-left:75%}.el-col-sm-pull-18{position:relative;right:75%}.el-col-sm-push-18{position:relative;left:75%}.el-col-sm-19{width:79.16667%}.el-col-sm-offset-19{margin-left:79.16667%}.el-col-sm-pull-19{position:relative;right:79.16667%}.el-col-sm-push-19{position:relative;left:79.16667%}.el-col-sm-20{width:83.33333%}.el-col-sm-offset-20{margin-left:83.33333%}.el-col-sm-pull-20{position:relative;right:83.33333%}.el-col-sm-push-20{position:relative;left:83.33333%}.el-col-sm-21{width:87.5%}.el-col-sm-offset-21{margin-left:87.5%}.el-col-sm-pull-21{position:relative;right:87.5%}.el-col-sm-push-21{position:relative;left:87.5%}.el-col-sm-22{width:91.66667%}.el-col-sm-offset-22{margin-left:91.66667%}.el-col-sm-pull-22{position:relative;right:91.66667%}.el-col-sm-push-22{position:relative;left:91.66667%}.el-col-sm-23{width:95.83333%}.el-col-sm-offset-23{margin-left:95.83333%}.el-col-sm-pull-23{position:relative;right:95.83333%}.el-col-sm-push-23{position:relative;left:95.83333%}.el-col-sm-24{width:100%}.el-col-sm-offset-24{margin-left:100%}.el-col-sm-pull-24{position:relative;right:100%}.el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.el-col-md-0{display:none;width:0%}.el-col-md-offset-0{margin-left:0}.el-col-md-pull-0{position:relative;right:0}.el-col-md-push-0{position:relative;left:0}.el-col-md-1{width:4.16667%}.el-col-md-offset-1{margin-left:4.16667%}.el-col-md-pull-1{position:relative;right:4.16667%}.el-col-md-push-1{position:relative;left:4.16667%}.el-col-md-2{width:8.33333%}.el-col-md-offset-2{margin-left:8.33333%}.el-col-md-pull-2{position:relative;right:8.33333%}.el-col-md-push-2{position:relative;left:8.33333%}.el-col-md-3{width:12.5%}.el-col-md-offset-3{margin-left:12.5%}.el-col-md-pull-3{position:relative;right:12.5%}.el-col-md-push-3{position:relative;left:12.5%}.el-col-md-4{width:16.66667%}.el-col-md-offset-4{margin-left:16.66667%}.el-col-md-pull-4{position:relative;right:16.66667%}.el-col-md-push-4{position:relative;left:16.66667%}.el-col-md-5{width:20.83333%}.el-col-md-offset-5{margin-left:20.83333%}.el-col-md-pull-5{position:relative;right:20.83333%}.el-col-md-push-5{position:relative;left:20.83333%}.el-col-md-6{width:25%}.el-col-md-offset-6{margin-left:25%}.el-col-md-pull-6{position:relative;right:25%}.el-col-md-push-6{position:relative;left:25%}.el-col-md-7{width:29.16667%}.el-col-md-offset-7{margin-left:29.16667%}.el-col-md-pull-7{position:relative;right:29.16667%}.el-col-md-push-7{position:relative;left:29.16667%}.el-col-md-8{width:33.33333%}.el-col-md-offset-8{margin-left:33.33333%}.el-col-md-pull-8{position:relative;right:33.33333%}.el-col-md-push-8{position:relative;left:33.33333%}.el-col-md-9{width:37.5%}.el-col-md-offset-9{margin-left:37.5%}.el-col-md-pull-9{position:relative;right:37.5%}.el-col-md-push-9{position:relative;left:37.5%}.el-col-md-10{width:41.66667%}.el-col-md-offset-10{margin-left:41.66667%}.el-col-md-pull-10{position:relative;right:41.66667%}.el-col-md-push-10{position:relative;left:41.66667%}.el-col-md-11{width:45.83333%}.el-col-md-offset-11{margin-left:45.83333%}.el-col-md-pull-11{position:relative;right:45.83333%}.el-col-md-push-11{position:relative;left:45.83333%}.el-col-md-12{width:50%}.el-col-md-offset-12{margin-left:50%}.el-col-md-pull-12{position:relative;right:50%}.el-col-md-push-12{position:relative;left:50%}.el-col-md-13{width:54.16667%}.el-col-md-offset-13{margin-left:54.16667%}.el-col-md-pull-13{position:relative;right:54.16667%}.el-col-md-push-13{position:relative;left:54.16667%}.el-col-md-14{width:58.33333%}.el-col-md-offset-14{margin-left:58.33333%}.el-col-md-pull-14{position:relative;right:58.33333%}.el-col-md-push-14{position:relative;left:58.33333%}.el-col-md-15{width:62.5%}.el-col-md-offset-15{margin-left:62.5%}.el-col-md-pull-15{position:relative;right:62.5%}.el-col-md-push-15{position:relative;left:62.5%}.el-col-md-16{width:66.66667%}.el-col-md-offset-16{margin-left:66.66667%}.el-col-md-pull-16{position:relative;right:66.66667%}.el-col-md-push-16{position:relative;left:66.66667%}.el-col-md-17{width:70.83333%}.el-col-md-offset-17{margin-left:70.83333%}.el-col-md-pull-17{position:relative;right:70.83333%}.el-col-md-push-17{position:relative;left:70.83333%}.el-col-md-18{width:75%}.el-col-md-offset-18{margin-left:75%}.el-col-md-pull-18{position:relative;right:75%}.el-col-md-push-18{position:relative;left:75%}.el-col-md-19{width:79.16667%}.el-col-md-offset-19{margin-left:79.16667%}.el-col-md-pull-19{position:relative;right:79.16667%}.el-col-md-push-19{position:relative;left:79.16667%}.el-col-md-20{width:83.33333%}.el-col-md-offset-20{margin-left:83.33333%}.el-col-md-pull-20{position:relative;right:83.33333%}.el-col-md-push-20{position:relative;left:83.33333%}.el-col-md-21{width:87.5%}.el-col-md-offset-21{margin-left:87.5%}.el-col-md-pull-21{position:relative;right:87.5%}.el-col-md-push-21{position:relative;left:87.5%}.el-col-md-22{width:91.66667%}.el-col-md-offset-22{margin-left:91.66667%}.el-col-md-pull-22{position:relative;right:91.66667%}.el-col-md-push-22{position:relative;left:91.66667%}.el-col-md-23{width:95.83333%}.el-col-md-offset-23{margin-left:95.83333%}.el-col-md-pull-23{position:relative;right:95.83333%}.el-col-md-push-23{position:relative;left:95.83333%}.el-col-md-24{width:100%}.el-col-md-offset-24{margin-left:100%}.el-col-md-pull-24{position:relative;right:100%}.el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.el-col-lg-0{display:none;width:0%}.el-col-lg-offset-0{margin-left:0}.el-col-lg-pull-0{position:relative;right:0}.el-col-lg-push-0{position:relative;left:0}.el-col-lg-1{width:4.16667%}.el-col-lg-offset-1{margin-left:4.16667%}.el-col-lg-pull-1{position:relative;right:4.16667%}.el-col-lg-push-1{position:relative;left:4.16667%}.el-col-lg-2{width:8.33333%}.el-col-lg-offset-2{margin-left:8.33333%}.el-col-lg-pull-2{position:relative;right:8.33333%}.el-col-lg-push-2{position:relative;left:8.33333%}.el-col-lg-3{width:12.5%}.el-col-lg-offset-3{margin-left:12.5%}.el-col-lg-pull-3{position:relative;right:12.5%}.el-col-lg-push-3{position:relative;left:12.5%}.el-col-lg-4{width:16.66667%}.el-col-lg-offset-4{margin-left:16.66667%}.el-col-lg-pull-4{position:relative;right:16.66667%}.el-col-lg-push-4{position:relative;left:16.66667%}.el-col-lg-5{width:20.83333%}.el-col-lg-offset-5{margin-left:20.83333%}.el-col-lg-pull-5{position:relative;right:20.83333%}.el-col-lg-push-5{position:relative;left:20.83333%}.el-col-lg-6{width:25%}.el-col-lg-offset-6{margin-left:25%}.el-col-lg-pull-6{position:relative;right:25%}.el-col-lg-push-6{position:relative;left:25%}.el-col-lg-7{width:29.16667%}.el-col-lg-offset-7{margin-left:29.16667%}.el-col-lg-pull-7{position:relative;right:29.16667%}.el-col-lg-push-7{position:relative;left:29.16667%}.el-col-lg-8{width:33.33333%}.el-col-lg-offset-8{margin-left:33.33333%}.el-col-lg-pull-8{position:relative;right:33.33333%}.el-col-lg-push-8{position:relative;left:33.33333%}.el-col-lg-9{width:37.5%}.el-col-lg-offset-9{margin-left:37.5%}.el-col-lg-pull-9{position:relative;right:37.5%}.el-col-lg-push-9{position:relative;left:37.5%}.el-col-lg-10{width:41.66667%}.el-col-lg-offset-10{margin-left:41.66667%}.el-col-lg-pull-10{position:relative;right:41.66667%}.el-col-lg-push-10{position:relative;left:41.66667%}.el-col-lg-11{width:45.83333%}.el-col-lg-offset-11{margin-left:45.83333%}.el-col-lg-pull-11{position:relative;right:45.83333%}.el-col-lg-push-11{position:relative;left:45.83333%}.el-col-lg-12{width:50%}.el-col-lg-offset-12{margin-left:50%}.el-col-lg-pull-12{position:relative;right:50%}.el-col-lg-push-12{position:relative;left:50%}.el-col-lg-13{width:54.16667%}.el-col-lg-offset-13{margin-left:54.16667%}.el-col-lg-pull-13{position:relative;right:54.16667%}.el-col-lg-push-13{position:relative;left:54.16667%}.el-col-lg-14{width:58.33333%}.el-col-lg-offset-14{margin-left:58.33333%}.el-col-lg-pull-14{position:relative;right:58.33333%}.el-col-lg-push-14{position:relative;left:58.33333%}.el-col-lg-15{width:62.5%}.el-col-lg-offset-15{margin-left:62.5%}.el-col-lg-pull-15{position:relative;right:62.5%}.el-col-lg-push-15{position:relative;left:62.5%}.el-col-lg-16{width:66.66667%}.el-col-lg-offset-16{margin-left:66.66667%}.el-col-lg-pull-16{position:relative;right:66.66667%}.el-col-lg-push-16{position:relative;left:66.66667%}.el-col-lg-17{width:70.83333%}.el-col-lg-offset-17{margin-left:70.83333%}.el-col-lg-pull-17{position:relative;right:70.83333%}.el-col-lg-push-17{position:relative;left:70.83333%}.el-col-lg-18{width:75%}.el-col-lg-offset-18{margin-left:75%}.el-col-lg-pull-18{position:relative;right:75%}.el-col-lg-push-18{position:relative;left:75%}.el-col-lg-19{width:79.16667%}.el-col-lg-offset-19{margin-left:79.16667%}.el-col-lg-pull-19{position:relative;right:79.16667%}.el-col-lg-push-19{position:relative;left:79.16667%}.el-col-lg-20{width:83.33333%}.el-col-lg-offset-20{margin-left:83.33333%}.el-col-lg-pull-20{position:relative;right:83.33333%}.el-col-lg-push-20{position:relative;left:83.33333%}.el-col-lg-21{width:87.5%}.el-col-lg-offset-21{margin-left:87.5%}.el-col-lg-pull-21{position:relative;right:87.5%}.el-col-lg-push-21{position:relative;left:87.5%}.el-col-lg-22{width:91.66667%}.el-col-lg-offset-22{margin-left:91.66667%}.el-col-lg-pull-22{position:relative;right:91.66667%}.el-col-lg-push-22{position:relative;left:91.66667%}.el-col-lg-23{width:95.83333%}.el-col-lg-offset-23{margin-left:95.83333%}.el-col-lg-pull-23{position:relative;right:95.83333%}.el-col-lg-push-23{position:relative;left:95.83333%}.el-col-lg-24{width:100%}.el-col-lg-offset-24{margin-left:100%}.el-col-lg-pull-24{position:relative;right:100%}.el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.el-col-xl-0{display:none;width:0%}.el-col-xl-offset-0{margin-left:0}.el-col-xl-pull-0{position:relative;right:0}.el-col-xl-push-0{position:relative;left:0}.el-col-xl-1{width:4.16667%}.el-col-xl-offset-1{margin-left:4.16667%}.el-col-xl-pull-1{position:relative;right:4.16667%}.el-col-xl-push-1{position:relative;left:4.16667%}.el-col-xl-2{width:8.33333%}.el-col-xl-offset-2{margin-left:8.33333%}.el-col-xl-pull-2{position:relative;right:8.33333%}.el-col-xl-push-2{position:relative;left:8.33333%}.el-col-xl-3{width:12.5%}.el-col-xl-offset-3{margin-left:12.5%}.el-col-xl-pull-3{position:relative;right:12.5%}.el-col-xl-push-3{position:relative;left:12.5%}.el-col-xl-4{width:16.66667%}.el-col-xl-offset-4{margin-left:16.66667%}.el-col-xl-pull-4{position:relative;right:16.66667%}.el-col-xl-push-4{position:relative;left:16.66667%}.el-col-xl-5{width:20.83333%}.el-col-xl-offset-5{margin-left:20.83333%}.el-col-xl-pull-5{position:relative;right:20.83333%}.el-col-xl-push-5{position:relative;left:20.83333%}.el-col-xl-6{width:25%}.el-col-xl-offset-6{margin-left:25%}.el-col-xl-pull-6{position:relative;right:25%}.el-col-xl-push-6{position:relative;left:25%}.el-col-xl-7{width:29.16667%}.el-col-xl-offset-7{margin-left:29.16667%}.el-col-xl-pull-7{position:relative;right:29.16667%}.el-col-xl-push-7{position:relative;left:29.16667%}.el-col-xl-8{width:33.33333%}.el-col-xl-offset-8{margin-left:33.33333%}.el-col-xl-pull-8{position:relative;right:33.33333%}.el-col-xl-push-8{position:relative;left:33.33333%}.el-col-xl-9{width:37.5%}.el-col-xl-offset-9{margin-left:37.5%}.el-col-xl-pull-9{position:relative;right:37.5%}.el-col-xl-push-9{position:relative;left:37.5%}.el-col-xl-10{width:41.66667%}.el-col-xl-offset-10{margin-left:41.66667%}.el-col-xl-pull-10{position:relative;right:41.66667%}.el-col-xl-push-10{position:relative;left:41.66667%}.el-col-xl-11{width:45.83333%}.el-col-xl-offset-11{margin-left:45.83333%}.el-col-xl-pull-11{position:relative;right:45.83333%}.el-col-xl-push-11{position:relative;left:45.83333%}.el-col-xl-12{width:50%}.el-col-xl-offset-12{margin-left:50%}.el-col-xl-pull-12{position:relative;right:50%}.el-col-xl-push-12{position:relative;left:50%}.el-col-xl-13{width:54.16667%}.el-col-xl-offset-13{margin-left:54.16667%}.el-col-xl-pull-13{position:relative;right:54.16667%}.el-col-xl-push-13{position:relative;left:54.16667%}.el-col-xl-14{width:58.33333%}.el-col-xl-offset-14{margin-left:58.33333%}.el-col-xl-pull-14{position:relative;right:58.33333%}.el-col-xl-push-14{position:relative;left:58.33333%}.el-col-xl-15{width:62.5%}.el-col-xl-offset-15{margin-left:62.5%}.el-col-xl-pull-15{position:relative;right:62.5%}.el-col-xl-push-15{position:relative;left:62.5%}.el-col-xl-16{width:66.66667%}.el-col-xl-offset-16{margin-left:66.66667%}.el-col-xl-pull-16{position:relative;right:66.66667%}.el-col-xl-push-16{position:relative;left:66.66667%}.el-col-xl-17{width:70.83333%}.el-col-xl-offset-17{margin-left:70.83333%}.el-col-xl-pull-17{position:relative;right:70.83333%}.el-col-xl-push-17{position:relative;left:70.83333%}.el-col-xl-18{width:75%}.el-col-xl-offset-18{margin-left:75%}.el-col-xl-pull-18{position:relative;right:75%}.el-col-xl-push-18{position:relative;left:75%}.el-col-xl-19{width:79.16667%}.el-col-xl-offset-19{margin-left:79.16667%}.el-col-xl-pull-19{position:relative;right:79.16667%}.el-col-xl-push-19{position:relative;left:79.16667%}.el-col-xl-20{width:83.33333%}.el-col-xl-offset-20{margin-left:83.33333%}.el-col-xl-pull-20{position:relative;right:83.33333%}.el-col-xl-push-20{position:relative;left:83.33333%}.el-col-xl-21{width:87.5%}.el-col-xl-offset-21{margin-left:87.5%}.el-col-xl-pull-21{position:relative;right:87.5%}.el-col-xl-push-21{position:relative;left:87.5%}.el-col-xl-22{width:91.66667%}.el-col-xl-offset-22{margin-left:91.66667%}.el-col-xl-pull-22{position:relative;right:91.66667%}.el-col-xl-push-22{position:relative;left:91.66667%}.el-col-xl-23{width:95.83333%}.el-col-xl-offset-23{margin-left:95.83333%}.el-col-xl-pull-23{position:relative;right:95.83333%}.el-col-xl-push-23{position:relative;left:95.83333%}.el-col-xl-24{width:100%}.el-col-xl-offset-24{margin-left:100%}.el-col-xl-pull-24{position:relative;right:100%}.el-col-xl-push-24{position:relative;left:100%}}@-webkit-keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}.el-upload{display:inline-block;text-align:center;cursor:pointer;outline:0}.el-upload__input{display:none}.el-upload__tip{font-size:12px;color:#606266;margin-top:7px}.el-upload iframe{position:absolute;z-index:-1;top:0;left:0;opacity:0;filter:alpha(opacity=0)}.el-upload--picture-card{background-color:#fbfdff;border:1px dashed #c0ccda;border-radius:6px;box-sizing:border-box;width:148px;height:148px;line-height:146px;vertical-align:top}.el-upload--picture-card i{font-size:28px;color:#8c939d}.el-upload--picture-card:hover,.el-upload:focus{border-color:#783887;color:#783887}.el-upload:focus .el-upload-dragger{border-color:#783887}.el-upload-dragger{background-color:#fff;border:1px dashed #d9d9d9;border-radius:6px;box-sizing:border-box;width:360px;height:180px;text-align:center;position:relative;overflow:hidden}.el-upload-dragger .el-icon-upload{font-size:67px;color:#C0C4CC;margin:40px 0 16px;line-height:50px}.el-upload-dragger+.el-upload__tip{text-align:center}.el-upload-dragger~.el-upload__files{border-top:1px solid #DCDFE6;margin-top:7px;padding-top:5px}.el-upload-dragger .el-upload__text{color:#606266;font-size:14px;text-align:center}.el-upload-dragger .el-upload__text em{color:#783887;font-style:normal}.el-upload-dragger:hover{border-color:#783887}.el-upload-dragger.is-dragover{background-color:rgba(32,159,255,.06);border:2px dashed #783887}.el-upload-list{margin:0;padding:0;list-style:none}.el-upload-list__item{-webkit-transition:all .5s cubic-bezier(.55,0,.1,1);transition:all .5s cubic-bezier(.55,0,.1,1);font-size:14px;color:#606266;line-height:1.8;margin-top:5px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;width:100%}.el-upload-list__item .el-progress{position:absolute;top:20px;width:100%}.el-upload-list__item .el-progress__text{position:absolute;right:0;top:-13px}.el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.el-upload-list__item:first-child{margin-top:10px}.el-upload-list__item .el-icon-upload-success{color:#67C23A}.el-upload-list__item .el-icon-close{display:none;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.75;color:#606266}.el-upload-list__item .el-icon-close:hover{opacity:1}.el-upload-list__item .el-icon-close-tip{display:none;position:absolute;top:5px;right:5px;font-size:12px;cursor:pointer;opacity:1;color:#783887}.el-upload-list__item:hover{background-color:#F5F7FA}.el-upload-list__item:hover .el-icon-close{display:inline-block}.el-upload-list__item:hover .el-progress__text{display:none}.el-upload-list__item.is-success .el-upload-list__item-status-label{display:block}.el-upload-list__item.is-success .el-upload-list__item-name:focus,.el-upload-list__item.is-success .el-upload-list__item-name:hover{color:#783887;cursor:pointer}.el-upload-list__item.is-success:focus:not(:hover) .el-icon-close-tip{display:inline-block}.el-upload-list__item.is-success:active .el-icon-close-tip,.el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.el-upload-list__item.is-success:hover .el-upload-list__item-status-label,.el-upload-list__item.is-success:not(.focusing):focus .el-icon-close-tip{display:none}.el-upload-list.is-disabled .el-upload-list__item:hover .el-upload-list__item-status-label{display:block}.el-upload-list__item-name{color:#606266;display:block;margin-right:40px;overflow:hidden;padding-left:4px;text-overflow:ellipsis;-webkit-transition:color .3s;transition:color .3s;white-space:nowrap}.el-upload-list__item-name [class^=el-icon]{height:100%;margin-right:7px;color:#909399;line-height:inherit}.el-upload-list__item-status-label{position:absolute;right:5px;top:0;line-height:inherit;display:none}.el-upload-list__item-delete{position:absolute;right:10px;top:0;font-size:12px;color:#606266;display:none}.el-upload-list__item-delete:hover{color:#783887}.el-upload-list--picture-card{margin:0;display:inline;vertical-align:top}.el-upload-list--picture-card .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;margin:0 8px 8px 0;display:inline-block}.el-upload-list--picture-card .el-upload-list__item .el-icon-check,.el-upload-list--picture-card .el-upload-list__item .el-icon-circle-check{color:#FFF}.el-upload-list--picture-card .el-upload-list__item .el-icon-close,.el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{display:none}.el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture-card .el-upload-list__item-name{display:none}.el-upload-list--picture-card .el-upload-list__item-thumbnail{width:100%;height:100%}.el-upload-list--picture-card .el-upload-list__item-status-label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-list--picture-card .el-upload-list__item-status-label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture-card .el-upload-list__item-actions{position:absolute;width:100%;height:100%;left:0;top:0;cursor:default;text-align:center;color:#fff;opacity:0;font-size:20px;background-color:rgba(0,0,0,.5);-webkit-transition:opacity .3s;transition:opacity .3s}.el-upload-list--picture-card .el-upload-list__item-actions::after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-upload-list--picture-card .el-upload-list__item-actions span{display:none;cursor:pointer}.el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:15px}.el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{position:static;font-size:inherit;color:inherit}.el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-block}.el-upload-list--picture-card .el-progress{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);bottom:auto;width:126px}.el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.el-upload-list--picture .el-upload-list__item{overflow:hidden;z-index:0;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:10px;padding:10px 10px 10px 90px;height:92px}.el-upload-list--picture .el-upload-list__item .el-icon-check,.el-upload-list--picture .el-upload-list__item .el-icon-circle-check{color:#FFF}.el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{background:0 0;-webkit-box-shadow:none;box-shadow:none;top:-2px;right:-12px}.el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name{line-height:70px;margin-top:0}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.el-upload-list--picture .el-upload-list__item-thumbnail{vertical-align:middle;display:inline-block;width:70px;height:70px;float:left;position:relative;z-index:1;margin-left:-80px;background-color:#FFF}.el-upload-list--picture .el-upload-list__item-name{display:block;margin-top:20px}.el-upload-list--picture .el-upload-list__item-name i{font-size:70px;line-height:1;position:absolute;left:9px;top:10px}.el-upload-list--picture .el-upload-list__item-status-label{position:absolute;right:-17px;top:-7px;width:46px;height:26px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 1px 1px #ccc;box-shadow:0 1px 1px #ccc}.el-upload-list--picture .el-upload-list__item-status-label i{font-size:12px;margin-top:12px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture .el-progress{position:relative;top:-7px}.el-upload-cover{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;z-index:10;cursor:default}.el-upload-cover::after{display:inline-block;height:100%;vertical-align:middle}.el-upload-cover img{display:block;width:100%;height:100%}.el-upload-cover__label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-cover__label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);color:#fff}.el-upload-cover__progress{display:inline-block;vertical-align:middle;position:static;width:243px}.el-upload-cover__progress+.el-upload__inner{opacity:0}.el-upload-cover__content{position:absolute;top:0;left:0;width:100%;height:100%}.el-upload-cover__interact{position:absolute;bottom:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.72);text-align:center}.el-upload-cover__interact .btn{display:inline-block;color:#FFF;font-size:14px;cursor:pointer;vertical-align:middle;-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);margin-top:60px}.el-upload-cover__interact .btn span{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.el-upload-cover__interact .btn:hover{-webkit-transform:translateY(-13px);transform:translateY(-13px)}.el-upload-cover__interact .btn:hover span{opacity:1}.el-upload-cover__interact .btn i{color:#FFF;display:block;font-size:24px;line-height:inherit;margin:0 auto 5px}.el-upload-cover__title{position:absolute;bottom:0;left:0;background-color:#FFF;height:36px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;text-align:left;padding:0 10px;margin:0;line-height:36px;font-size:14px;color:#303133}.el-upload-cover+.el-upload__inner{opacity:0;position:relative;z-index:1}.el-progress{position:relative;line-height:1}.el-progress__text{font-size:14px;color:#606266;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress-bar,.el-progress-bar__inner::after,.el-progress-bar__innerText,.el-spinner{display:inline-block;vertical-align:middle}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:#67C23A}.el-progress.is-success .el-progress__text{color:#67C23A}.el-progress.is-warning .el-progress-bar__inner{background-color:#E6A23C}.el-progress.is-warning .el-progress__text{color:#E6A23C}.el-progress.is-exception .el-progress-bar__inner{background-color:#F56C6C}.el-progress.is-exception .el-progress__text{color:#F56C6C}.el-progress-bar{padding-right:50px;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:#EBEEF5;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#783887;text-align:right;border-radius:100px;line-height:1;white-space:nowrap;-webkit-transition:width .6s ease;transition:width .6s ease}.el-card,.el-message{border-radius:4px;overflow:hidden}.el-progress-bar__inner::after{height:100%}.el-progress-bar__innerText{color:#FFF;font-size:12px;margin:0 5px}@keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}.el-time-spinner{width:100%;white-space:nowrap}.el-spinner-inner{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;width:50px;height:50px}.el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite}@-webkit-keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}.el-message{min-width:380px;-webkit-box-sizing:border-box;box-sizing:border-box;border-width:1px;border-style:solid;border-color:#EBEEF5;position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:#edf2fc;-webkit-transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,transform .4s,top .4s;transition:opacity .3s,transform .4s,top .4s,-webkit-transform .4s;padding:15px 15px 15px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message.is-closable .el-message__content{padding-right:16px}.el-message p{margin:0}.el-message--info .el-message__content{color:#909399}.el-message--success{background-color:#f0f9eb;border-color:#e1f3d8}.el-message--success .el-message__content{color:#67C23A}.el-message--warning{background-color:#fdf6ec;border-color:#faecd8}.el-message--warning .el-message__content{color:#E6A23C}.el-message--error{background-color:#fef0f0;border-color:#fde2e2}.el-message--error .el-message__content{color:#F56C6C}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:#C0C4CC;font-size:16px}.el-message__closeBtn:hover{color:#909399}.el-message .el-icon-success{color:#67C23A}.el-message .el-icon-error{color:#F56C6C}.el-message .el-icon-info{color:#909399}.el-message .el-icon-warning{color:#E6A23C}.el-message-fade-enter,.el-message-fade-leave-active{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}.el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#F56C6C;border-radius:10px;color:#FFF;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #FFF}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.el-rate__icon,.el-rate__item{position:relative;display:inline-block}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.el-badge__content--primary{background-color:#783887}.el-badge__content--success{background-color:#67C23A}.el-badge__content--warning{background-color:#E6A23C}.el-badge__content--info{background-color:#909399}.el-badge__content--danger{background-color:#F56C6C}.el-card{border:1px solid #EBEEF5;background-color:#FFF;color:#303133;-webkit-transition:.3s;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #EBEEF5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px}.el-rate{height:20px;line-height:1}.el-rate__item{font-size:0;vertical-align:middle}.el-rate__icon{font-size:18px;margin-right:6px;color:#C0C4CC;-webkit-transition:.3s;transition:.3s}.el-rate__decimal,.el-rate__icon .path2{position:absolute;top:0;left:0}.el-rate__icon.hover{-webkit-transform:scale(1.15);transform:scale(1.15)}.el-rate__decimal{display:inline-block;overflow:hidden}.el-step.is-vertical,.el-steps{display:-webkit-box;display:-ms-flexbox}.el-rate__text{font-size:14px;vertical-align:middle}.el-steps{display:flex}.el-steps--simple{padding:13px 8%;border-radius:4px;background:#F5F7FA}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{height:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column;flex-flow:column}.el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.el-step:last-of-type .el-step__description,.el-step:last-of-type .el-step__main{padding-right:0}.el-step__head{position:relative;width:100%}.el-step__head.is-process{color:#303133;border-color:#303133}.el-step__head.is-wait{color:#C0C4CC;border-color:#C0C4CC}.el-step__head.is-success{color:#67C23A;border-color:#67C23A}.el-step__head.is-error{color:#F56C6C;border-color:#F56C6C}.el-step__head.is-finish{color:#783887;border-color:#783887}.el-step__icon{position:relative;z-index:1;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#FFF;-webkit-transition:.15s ease-out;transition:.15s ease-out}.el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{display:inline-block;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{-webkit-transform:translateY(1px);transform:translateY(1px)}.el-step__line{position:absolute;border-color:inherit;background-color:#C0C4CC}.el-step__line-inner{display:block;border-width:1px;border-style:solid;border-color:inherit;-webkit-transition:.15s ease-out;transition:.15s ease-out;-webkit-box-sizing:border-box;box-sizing:border-box;width:0;height:0}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{font-weight:700;color:#303133}.el-step__title.is-wait{color:#C0C4CC}.el-step__title.is-success{color:#67C23A}.el-step__title.is-error{color:#F56C6C}.el-step__title.is-finish{color:#783887}.el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.el-step__description.is-process{color:#303133}.el-step__description.is-wait{color:#C0C4CC}.el-step__description.is-success{color:#67C23A}.el-step__description.is-error{color:#F56C6C}.el-step__description.is-finish{color:#783887}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:flex}.el-step.is-vertical .el-step__head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{padding-left:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{-webkit-transform:scale(.8) translateY(1px);transform:scale(.8) translateY(1px)}.el-step.is-simple .el-step__main{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.el-step.is-simple .el-step__arrow{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-step.is-simple .el-step__arrow::after,.el-step.is-simple .el-step__arrow::before{content:'';display:inline-block;position:absolute;height:15px;width:1px;background:#C0C4CC}.el-step.is-simple .el-step__arrow::before{-webkit-transform:rotate(-45deg) translateY(-4px);transform:rotate(-45deg) translateY(-4px);-webkit-transform-origin:0 0;transform-origin:0 0}.el-step.is-simple .el-step__arrow::after{-webkit-transform:rotate(45deg) translateY(4px);transform:rotate(45deg) translateY(4px);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.el-step.is-simple:last-of-type .el-step__arrow{display:none}.el-carousel{position:relative}.el-carousel--horizontal{overflow-x:hidden}.el-carousel--vertical{overflow-y:hidden}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#FFF;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;margin:0;padding:0;z-index:2}.el-carousel__indicators--horizontal{bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-carousel__indicators--vertical{right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#C0C4CC;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{background-color:transparent;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator--horizontal{display:inline-block;padding:12px 4px}.el-carousel__indicator--vertical{padding:4px 12px}.el-carousel__indicator--vertical .el-carousel__button{width:2px;height:15px}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#FFF;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.el-carousel__item,.el-carousel__mask{height:100%;top:0;left:0;position:absolute}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0}.el-carousel__item{width:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card{width:50%;-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{width:100%;background-color:#FFF;opacity:.24;-webkit-transition:.2s;transition:.2s}.el-fade-in-enter,.el-fade-in-leave-active,.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-enter-active,.el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.el-collapse{border-top:1px solid #EBEEF5;border-bottom:1px solid #EBEEF5}.el-collapse-item.is-disabled .el-collapse-item__header{color:#bbb;cursor:not-allowed}.el-collapse-item__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:48px;line-height:48px;background-color:#FFF;color:#303133;cursor:pointer;border-bottom:1px solid #EBEEF5;font-size:13px;font-weight:500;-webkit-transition:border-bottom-color .3s;transition:border-bottom-color .3s;outline:0}.el-collapse-item__arrow{margin:0 8px 0 auto;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-weight:300}.el-collapse-item__arrow.is-active{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-collapse-item__header.focusing:focus:not(:hover){color:#783887}.el-collapse-item__header.is-active{border-bottom-color:transparent}.el-collapse-item__wrap{will-change:height;background-color:#FFF;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:1px solid #EBEEF5}.el-cascader__tags,.el-tag{-webkit-box-sizing:border-box}.el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#303133;line-height:1.769230769230769}.el-collapse-item:last-child{margin-bottom:-1px}.el-popper .popper__arrow,.el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.el-popper .popper__arrow::after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#EBEEF5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#FFF;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#EBEEF5}.el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#FFF}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#EBEEF5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#FFF;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#EBEEF5}.el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#FFF}.el-tag{background-color:rgb(242, 235, 243);border-color:rgb(228, 215, 231);display:inline-block;height:32px;padding:0 10px;line-height:30px;font-size:12px;color:#783887;border-width:1px;border-style:solid;border-radius:4px;box-sizing:border-box;white-space:nowrap}.el-tag.is-hit{border-color:#783887}.el-tag .el-tag__close{color:#783887}.el-tag .el-tag__close:hover{color:#FFF;background-color:#783887}.el-tag.el-tag--info{background-color:#f4f4f5;border-color:#e9e9eb;color:#909399}.el-tag.el-tag--info.is-hit{border-color:#909399}.el-tag.el-tag--info .el-tag__close{color:#909399}.el-tag.el-tag--info .el-tag__close:hover{color:#FFF;background-color:#909399}.el-tag.el-tag--success{background-color:#f0f9eb;border-color:#e1f3d8;color:#67c23a}.el-tag.el-tag--success.is-hit{border-color:#67C23A}.el-tag.el-tag--success .el-tag__close{color:#67c23a}.el-tag.el-tag--success .el-tag__close:hover{color:#FFF;background-color:#67c23a}.el-tag.el-tag--warning{background-color:#fdf6ec;border-color:#faecd8;color:#e6a23c}.el-tag.el-tag--warning.is-hit{border-color:#E6A23C}.el-tag.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag.el-tag--warning .el-tag__close:hover{color:#FFF;background-color:#e6a23c}.el-tag.el-tag--danger{background-color:#fef0f0;border-color:#fde2e2;color:#f56c6c}.el-tag.el-tag--danger.is-hit{border-color:#F56C6C}.el-tag.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag.el-tag--danger .el-tag__close:hover{color:#FFF;background-color:#f56c6c}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:16px;width:16px;line-height:16px;vertical-align:middle;top:-1px;right:-5px}.el-tag .el-icon-close::before{display:block}.el-tag--dark{background-color:#783887;border-color:#783887;color:#fff}.el-tag--dark.is-hit{border-color:#783887}.el-tag--dark .el-tag__close{color:#fff}.el-tag--dark .el-tag__close:hover{color:#FFF;background-color:rgb(147, 96, 159)}.el-tag--dark.el-tag--info{background-color:#909399;border-color:#909399;color:#fff}.el-tag--dark.el-tag--info.is-hit{border-color:#909399}.el-tag--dark.el-tag--info .el-tag__close{color:#fff}.el-tag--dark.el-tag--info .el-tag__close:hover{color:#FFF;background-color:#a6a9ad}.el-tag--dark.el-tag--success{background-color:#67c23a;border-color:#67c23a;color:#fff}.el-tag--dark.el-tag--success.is-hit{border-color:#67C23A}.el-tag--dark.el-tag--success .el-tag__close{color:#fff}.el-tag--dark.el-tag--success .el-tag__close:hover{color:#FFF;background-color:#85ce61}.el-tag--dark.el-tag--warning{background-color:#e6a23c;border-color:#e6a23c;color:#fff}.el-tag--dark.el-tag--warning.is-hit{border-color:#E6A23C}.el-tag--dark.el-tag--warning .el-tag__close{color:#fff}.el-tag--dark.el-tag--warning .el-tag__close:hover{color:#FFF;background-color:#ebb563}.el-tag--dark.el-tag--danger{background-color:#f56c6c;border-color:#f56c6c;color:#fff}.el-tag--dark.el-tag--danger.is-hit{border-color:#F56C6C}.el-tag--dark.el-tag--danger .el-tag__close{color:#fff}.el-tag--dark.el-tag--danger .el-tag__close:hover{color:#FFF;background-color:#f78989}.el-tag--plain{background-color:#fff;border-color:rgb(201, 175, 207);color:#783887}.el-tag--plain.is-hit{border-color:#783887}.el-tag--plain .el-tag__close{color:#783887}.el-tag--plain .el-tag__close:hover{color:#FFF;background-color:#783887}.el-tag--plain.el-tag--info{background-color:#fff;border-color:#d3d4d6;color:#909399}.el-tag--plain.el-tag--info.is-hit{border-color:#909399}.el-tag--plain.el-tag--info .el-tag__close{color:#909399}.el-tag--plain.el-tag--info .el-tag__close:hover{color:#FFF;background-color:#909399}.el-tag--plain.el-tag--success{background-color:#fff;border-color:#c2e7b0;color:#67c23a}.el-tag--plain.el-tag--success.is-hit{border-color:#67C23A}.el-tag--plain.el-tag--success .el-tag__close{color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close:hover{color:#FFF;background-color:#67c23a}.el-tag--plain.el-tag--warning{background-color:#fff;border-color:#f5dab1;color:#e6a23c}.el-tag--plain.el-tag--warning.is-hit{border-color:#E6A23C}.el-tag--plain.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close:hover{color:#FFF;background-color:#e6a23c}.el-tag--plain.el-tag--danger{background-color:#fff;border-color:#fbc4c4;color:#f56c6c}.el-tag--plain.el-tag--danger.is-hit{border-color:#F56C6C}.el-tag--plain.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close:hover{color:#FFF;background-color:#f56c6c}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.el-cascader{display:inline-block;position:relative;font-size:14px;line-height:40px}.el-cascader:not(.is-disabled):hover .el-input__inner{cursor:pointer;border-color:#C0C4CC}.el-cascader .el-input .el-input__inner:focus,.el-cascader .el-input.is-focus .el-input__inner{border-color:#783887}.el-cascader .el-input{cursor:pointer}.el-cascader .el-input .el-input__inner{text-overflow:ellipsis}.el-cascader .el-input .el-icon-arrow-down{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:14px}.el-cascader .el-input .el-icon-arrow-down.is-reverse{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.el-cascader .el-input .el-icon-circle-close:hover{color:#909399}.el-cascader--medium{font-size:14px;line-height:36px}.el-cascader--small{font-size:13px;line-height:32px}.el-cascader--mini{font-size:12px;line-height:28px}.el-cascader.is-disabled .el-cascader__label{z-index:2;color:#C0C4CC}.el-cascader__dropdown{margin:5px 0;font-size:14px;background:#FFF;border:1px solid #E4E7ED;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-cascader__tags{position:absolute;left:0;right:30px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;line-height:normal;text-align:left;box-sizing:border-box}.el-cascader__tags .el-tag{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;max-width:100%;margin:2px 0 2px 6px;text-overflow:ellipsis;background:#f0f2f5}.el-cascader__tags .el-tag:not(.is-hit){border-color:transparent}.el-cascader__tags .el-tag>span{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden;text-overflow:ellipsis}.el-cascader__tags .el-tag .el-icon-close{-webkit-box-flex:0;-ms-flex:none;flex:none;background-color:#C0C4CC;color:#FFF}.el-cascader__tags .el-tag .el-icon-close:hover{background-color:#909399}.el-cascader__suggestion-panel{border-radius:4px}.el-cascader__suggestion-list{max-height:204px;margin:0;padding:6px 0;font-size:14px;color:#606266;text-align:center}.el-cascader__suggestion-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;padding:0 15px;text-align:left;outline:0;cursor:pointer}.el-cascader__suggestion-item:focus,.el-cascader__suggestion-item:hover{background:#F5F7FA}.el-cascader__suggestion-item.is-checked{color:#783887;font-weight:700}.el-cascader__suggestion-item>span{margin-right:10px}.el-cascader__empty-text{margin:10px 0;color:#C0C4CC}.el-cascader__search-input{-webkit-box-flex:1;-ms-flex:1;flex:1;height:24px;min-width:60px;margin:2px 0 2px 15px;padding:0;color:#606266;border:none;outline:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-cascader__search-input::-webkit-input-placeholder{color:#C0C4CC}.el-cascader__search-input:-ms-input-placeholder{color:#C0C4CC}.el-cascader__search-input::-ms-input-placeholder{color:#C0C4CC}.el-cascader__search-input::placeholder{color:#C0C4CC}.el-color-predefine{display:-webkit-box;display:-ms-flexbox;display:flex;font-size:12px;margin-top:8px;width:280px}.el-color-predefine__colors{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-color-predefine__color-selector{margin:0 0 8px 8px;width:20px;height:20px;border-radius:4px;cursor:pointer}.el-color-predefine__color-selector:nth-child(10n+1){margin-left:0}.el-color-predefine__color-selector.selected{-webkit-box-shadow:0 0 3px 2px #783887;box-shadow:0 0 3px 2px #783887}.el-color-predefine__color-selector>div{display:-webkit-box;display:-ms-flexbox;display:flex;height:100%;border-radius:3px}.el-color-predefine__color-selector.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-hue-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.el-color-hue-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);height:100%}.el-color-hue-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to bottom,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-svpanel{position:relative;width:280px;height:180px}.el-color-svpanel__black,.el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.el-color-svpanel__white{background:-webkit-gradient(linear,left top,right top,from(#fff),to(rgba(255,255,255,0)));background:linear-gradient(to right,#fff,rgba(255,255,255,0))}.el-color-svpanel__black{background:-webkit-gradient(linear,left bottom,left top,from(#000),to(rgba(0,0,0,0)));background:linear-gradient(to top,#000,rgba(0,0,0,0))}.el-color-svpanel__cursor{position:absolute}.el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;-webkit-box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;-webkit-transform:translate(-2px,-2px);transform:translate(-2px,-2px)}.el-color-alpha-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-alpha-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to right,rgba(255,255,255,0) 0,#fff 100%);height:100%}.el-color-alpha-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fff 100%)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-dropdown{width:300px}.el-color-dropdown__main-wrapper{margin-bottom:6px}.el-color-dropdown__main-wrapper::after{content:"";display:table;clear:both}.el-color-dropdown__btns{margin-top:6px;text-align:right}.el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.el-color-dropdown__btn:hover{color:#783887;border-color:#783887}.el-color-dropdown__link-btn{cursor:pointer;color:#783887;text-decoration:none;padding:15px;font-size:12px}.el-color-dropdown__link-btn:hover{color:tint(primary,20%)}.el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.el-color-picker--medium{height:36px}.el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.el-color-picker--small{height:32px}.el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.el-color-picker--small .el-color-picker__empty,.el-color-picker--small .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker--mini{height:28px}.el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.el-color-picker--mini .el-color-picker__empty,.el-color-picker--mini .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:rgba(255,255,255,.7)}.el-color-picker__trigger{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;position:relative;cursor:pointer}.el-color-picker__color{position:relative;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.el-color-picker__empty,.el-color-picker__icon{top:50%;left:50%;font-size:12px;position:absolute}.el-color-picker__empty{color:#999;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.el-color-picker__icon{display:inline-block;width:100%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0);color:#FFF;text-align:center}.el-color-picker__panel{position:absolute;z-index:10;padding:6px;-webkit-box-sizing:content-box;box-sizing:content-box;background-color:#FFF;border:1px solid #EBEEF5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:14px}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:inherit;color:#606266;background-color:#FFF;background-image:none;border:1px solid #DCDFE6;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#C0C4CC}.el-textarea__inner:-ms-input-placeholder{color:#C0C4CC}.el-textarea__inner::-ms-input-placeholder{color:#C0C4CC}.el-textarea__inner::placeholder{color:#C0C4CC}.el-textarea__inner:hover{border-color:#C0C4CC}.el-textarea__inner:focus{outline:0;border-color:#783887}.el-textarea .el-input__count{color:#909399;background:#FFF;position:absolute;font-size:12px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:#F5F7FA;border-color:#E4E7ED;color:#C0C4CC;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#C0C4CC}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#C0C4CC}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#C0C4CC}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#C0C4CC}.el-textarea.is-exceed .el-textarea__inner{border-color:#F56C6C}.el-textarea.is-exceed .el-input__count{color:#F56C6C}.el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner{background:#fff}.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:#C0C4CC;font-size:14px;cursor:pointer;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-input .el-input__clear:hover{color:#909399}.el-input .el-input__count{height:100%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#909399;font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#FFF;line-height:initial;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:#FFF;background-image:none;border-radius:4px;border:1px solid #DCDFE6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;height:100%;color:#C0C4CC;text-align:center}.el-input__inner::-webkit-input-placeholder{color:#C0C4CC}.el-input__inner:-ms-input-placeholder{color:#C0C4CC}.el-input__inner::-ms-input-placeholder{color:#C0C4CC}.el-input__inner::placeholder{color:#C0C4CC}.el-input__inner:hover{border-color:#C0C4CC}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#783887;outline:0}.el-input__suffix{right:5px;transition:all .3s}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#F5F7FA;border-color:#E4E7ED;color:#C0C4CC;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__inner::placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-link,.el-transfer-panel__filter .el-icon-circle-close{cursor:pointer}.el-input.is-exceed .el-input__inner{border-color:#F56C6C}.el-input.is-exceed .el-input__suffix .el-input__count{color:#F56C6C}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#F5F7FA;color:#909399;vertical-align:middle;display:table-cell;position:relative;border:1px solid #DCDFE6;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner,.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-transfer{font-size:14px}.el-transfer__buttons{display:inline-block;vertical-align:middle;padding:0 30px}.el-transfer__button{display:block;margin:0 auto;padding:10px;border-radius:50%;color:#FFF;background-color:#783887;font-size:0}.el-transfer-panel__item+.el-transfer-panel__item,.el-transfer__button [class*=el-icon-]+span{margin-left:0}.el-transfer__button.is-with-texts{border-radius:4px}.el-transfer__button.is-disabled,.el-transfer__button.is-disabled:hover{border:1px solid #DCDFE6;background-color:#F5F7FA;color:#C0C4CC}.el-transfer__button:first-child{margin-bottom:10px}.el-transfer__button:nth-child(2){margin:0}.el-transfer__button i,.el-transfer__button span{font-size:14px}.el-transfer-panel{border:1px solid #EBEEF5;border-radius:4px;overflow:hidden;background:#FFF;display:inline-block;vertical-align:middle;width:200px;max-height:100%;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.el-transfer-panel__body{height:246px}.el-transfer-panel__body.is-with-footer{padding-bottom:40px}.el-transfer-panel__list{margin:0;padding:6px 0;list-style:none;height:246px;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box}.el-transfer-panel__list.is-filterable{height:194px;padding-top:0}.el-transfer-panel__item{height:30px;line-height:30px;padding-left:15px;display:block!important}.el-transfer-panel__item.el-checkbox{color:#606266}.el-transfer-panel__item:hover{color:#783887}.el-transfer-panel__item.el-checkbox .el-checkbox__label{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:24px;line-height:30px}.el-transfer-panel__item .el-checkbox__input{position:absolute;top:8px}.el-transfer-panel__filter{text-align:center;margin:15px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block;width:auto}.el-transfer-panel__filter .el-input__inner{height:32px;width:100%;font-size:12px;display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:16px;padding-right:10px;padding-left:30px}.el-transfer-panel__filter .el-input__icon{margin-left:5px}.el-transfer-panel .el-transfer-panel__header{height:40px;line-height:40px;background:#F5F7FA;margin:0;padding-left:15px;border-bottom:1px solid #EBEEF5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#000}.el-transfer-panel .el-transfer-panel__header .el-checkbox{display:block;line-height:40px}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{font-size:16px;color:#303133;font-weight:400}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label span{position:absolute;right:15px;color:#909399;font-size:12px;font-weight:400}.el-divider__text,.el-link{font-weight:500;font-size:14px}.el-transfer-panel .el-transfer-panel__footer{height:40px;background:#FFF;margin:0;padding:0;border-top:1px solid #EBEEF5;position:absolute;bottom:0;left:0;width:100%;z-index:1}.el-transfer-panel .el-transfer-panel__footer::after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-container,.el-timeline-item__node{display:-webkit-box;display:-ms-flexbox}.el-transfer-panel .el-transfer-panel__footer .el-checkbox{padding-left:20px;color:#606266}.el-transfer-panel .el-transfer-panel__empty{margin:0;height:30px;line-height:30px;padding:6px 15px 0;color:#909399;text-align:center}.el-transfer-panel .el-checkbox__label{padding-left:8px}.el-transfer-panel .el-checkbox__inner{height:14px;width:14px;border-radius:3px}.el-transfer-panel .el-checkbox__inner::after{height:6px;width:3px;left:4px}.el-container{display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-sizing:border-box;box-sizing:border-box;min-width:0}.el-container.is-vertical,.el-drawer{-webkit-box-orient:vertical;-webkit-box-direction:normal}.el-aside,.el-header{-webkit-box-sizing:border-box}.el-container.is-vertical{-ms-flex-direction:column;flex-direction:column}.el-header{padding:0 20px;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-aside{overflow:auto;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-footer,.el-main{-webkit-box-sizing:border-box}.el-main{display:block;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;overflow:auto;box-sizing:border-box;padding:20px}.el-footer{padding:0 20px;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-timeline{margin:0;font-size:14px;list-style:none}.el-timeline .el-timeline-item:last-child .el-timeline-item__tail{display:none}.el-timeline-item{position:relative;padding-bottom:20px}.el-timeline-item__wrapper{position:relative;padding-left:28px;top:-3px}.el-timeline-item__tail{position:absolute;left:4px;height:100%;border-left:2px solid #E4E7ED}.el-timeline-item__icon{color:#FFF;font-size:13px}.el-timeline-item__node{position:absolute;background-color:#E4E7ED;border-radius:50%;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image__error,.el-timeline-item__dot{display:-webkit-box;display:-ms-flexbox}.el-timeline-item__node--normal{left:-1px;width:12px;height:12px}.el-timeline-item__node--large{left:-2px;width:14px;height:14px}.el-timeline-item__node--primary{background-color:#783887}.el-timeline-item__node--success{background-color:#67C23A}.el-timeline-item__node--warning{background-color:#E6A23C}.el-timeline-item__node--danger{background-color:#F56C6C}.el-timeline-item__node--info{background-color:#909399}.el-timeline-item__dot{position:absolute;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-timeline-item__content{color:#303133}.el-timeline-item__timestamp{color:#909399;line-height:1;font-size:13px}.el-timeline-item__timestamp.is-top{margin-bottom:8px;padding-top:4px}.el-timeline-item__timestamp.is-bottom{margin-top:8px}.el-link{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;vertical-align:middle;position:relative;text-decoration:none;outline:0;padding:0}.el-link.is-underline:hover:after{content:"";position:absolute;left:0;right:0;height:0;bottom:0;border-bottom:1px solid #783887}.el-link.el-link--default:after,.el-link.el-link--primary.is-underline:hover:after,.el-link.el-link--primary:after{border-color:#783887}.el-link.is-disabled{cursor:not-allowed}.el-link [class*=el-icon-]+span{margin-left:5px}.el-link.el-link--default{color:#606266}.el-link.el-link--default:hover{color:#783887}.el-link.el-link--default.is-disabled{color:#C0C4CC}.el-link.el-link--primary{color:#783887}.el-link.el-link--primary:hover{color:rgb(147, 96, 159)}.el-link.el-link--primary.is-disabled{color:rgb(188, 156, 195)}.el-link.el-link--danger.is-underline:hover:after,.el-link.el-link--danger:after{border-color:#F56C6C}.el-link.el-link--danger{color:#F56C6C}.el-link.el-link--danger:hover{color:#f78989}.el-link.el-link--danger.is-disabled{color:#fab6b6}.el-link.el-link--success.is-underline:hover:after,.el-link.el-link--success:after{border-color:#67C23A}.el-link.el-link--success{color:#67C23A}.el-link.el-link--success:hover{color:#85ce61}.el-link.el-link--success.is-disabled{color:#b3e19d}.el-link.el-link--warning.is-underline:hover:after,.el-link.el-link--warning:after{border-color:#E6A23C}.el-link.el-link--warning{color:#E6A23C}.el-link.el-link--warning:hover{color:#ebb563}.el-link.el-link--warning.is-disabled{color:#f3d19e}.el-link.el-link--info.is-underline:hover:after,.el-link.el-link--info:after{border-color:#909399}.el-link.el-link--info{color:#909399}.el-link.el-link--info:hover{color:#a6a9ad}.el-link.el-link--info.is-disabled{color:#c8c9cc}.el-divider{background-color:#DCDFE6;position:relative}.el-divider--horizontal{display:block;height:1px;width:100%;margin:24px 0}.el-divider--vertical{display:inline-block;width:1px;height:1em;margin:0 8px;vertical-align:middle;position:relative}.el-divider__text{position:absolute;background-color:#FFF;padding:0 20px;color:#303133}.el-image__error,.el-image__placeholder{background:#F5F7FA}.el-divider__text.is-left{left:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-divider__text.is-center{left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.el-divider__text.is-right{right:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__inner--center{position:relative;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:block}.el-image__error{display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#C0C4CC;vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;user-select:none}.el-button,.el-checkbox{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:40px}.el-image-viewer__canvas{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image-viewer__actions{left:50%;bottom:30px;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:282px;height:44px;padding:0 23px;background-color:#606266;border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-pack:distribute;justify-content:space-around}.el-image-viewer__next,.el-image-viewer__prev{top:50%;width:44px;height:44px;font-size:24px;color:#fff;background-color:#606266;border-color:#fff}.el-image-viewer__prev{-webkit-transform:translateY(-50%);transform:translateY(-50%);left:40px}.el-image-viewer__next{-webkit-transform:translateY(-50%);transform:translateY(-50%);right:40px;text-indent:2px}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{-webkit-animation:viewer-fade-in .3s;animation:viewer-fade-in .3s}.viewer-fade-leave-active{-webkit-animation:viewer-fade-out .3s;animation:viewer-fade-out .3s}@-webkit-keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes viewer-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes viewer-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#FFF;border:1px solid #DCDFE6;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;padding:12px 20px;font-size:14px;border-radius:4px}.el-button+.el-button{margin-left:10px}.el-button:focus,.el-button:hover{color:#783887;border-color:rgb(215, 195, 219);background-color:rgb(242, 235, 243)}.el-button:active{color:rgb(108, 50, 122);border-color:rgb(108, 50, 122);outline:0}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon-]+span{margin-left:5px}.el-button.is-plain:focus,.el-button.is-plain:hover{background:#FFF;border-color:#783887;color:#783887}.el-button.is-active,.el-button.is-plain:active{color:rgb(108, 50, 122);border-color:rgb(108, 50, 122)}.el-button.is-plain:active{background:#FFF;outline:0}.el-button.is-disabled,.el-button.is-disabled:focus,.el-button.is-disabled:hover{color:#C0C4CC;cursor:not-allowed;background-image:none;background-color:#FFF;border-color:#EBEEF5}.el-button.is-disabled.el-button--text{background-color:transparent}.el-button.is-disabled.is-plain,.el-button.is-disabled.is-plain:focus,.el-button.is-disabled.is-plain:hover{background-color:#FFF;border-color:#EBEEF5;color:#C0C4CC}.el-button.is-loading{position:relative;pointer-events:none}.el-button.is-loading:before{pointer-events:none;content:'';position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:rgba(255,255,255,.35)}.el-button.is-round{border-radius:20px;padding:12px 23px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--primary{color:#FFF;background-color:#783887;border-color:#783887}.el-button--primary:focus,.el-button--primary:hover{background:rgb(147, 96, 159);border-color:rgb(147, 96, 159);color:#FFF}.el-button--primary.is-active,.el-button--primary:active{background:rgb(108, 50, 122);border-color:rgb(108, 50, 122);color:#FFF}.el-button--primary:active{outline:0}.el-button--primary.is-disabled,.el-button--primary.is-disabled:active,.el-button--primary.is-disabled:focus,.el-button--primary.is-disabled:hover{color:#FFF;background-color:rgb(188, 156, 195);border-color:rgb(188, 156, 195)}.el-button--primary.is-plain{color:#783887;background:rgb(242, 235, 243);border-color:rgb(201, 175, 207)}.el-button--primary.is-plain:focus,.el-button--primary.is-plain:hover{background:#783887;border-color:#783887;color:#FFF}.el-button--primary.is-plain:active{background:rgb(108, 50, 122);border-color:rgb(108, 50, 122);color:#FFF;outline:0}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:hover{color:rgb(174, 136, 183);background-color:rgb(242, 235, 243);border-color:rgb(228, 215, 231)}.el-button--success{color:#FFF;background-color:#67C23A;border-color:#67C23A}.el-button--success:focus,.el-button--success:hover{background:#85ce61;border-color:#85ce61;color:#FFF}.el-button--success.is-active,.el-button--success:active{background:#5daf34;border-color:#5daf34;color:#FFF}.el-button--success:active{outline:0}.el-button--success.is-disabled,.el-button--success.is-disabled:active,.el-button--success.is-disabled:focus,.el-button--success.is-disabled:hover{color:#FFF;background-color:#b3e19d;border-color:#b3e19d}.el-button--success.is-plain{color:#67C23A;background:#f0f9eb;border-color:#c2e7b0}.el-button--success.is-plain:focus,.el-button--success.is-plain:hover{background:#67C23A;border-color:#67C23A;color:#FFF}.el-button--success.is-plain:active{background:#5daf34;border-color:#5daf34;color:#FFF;outline:0}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:hover{color:#a4da89;background-color:#f0f9eb;border-color:#e1f3d8}.el-button--warning{color:#FFF;background-color:#E6A23C;border-color:#E6A23C}.el-button--warning:focus,.el-button--warning:hover{background:#ebb563;border-color:#ebb563;color:#FFF}.el-button--warning.is-active,.el-button--warning:active{background:#cf9236;border-color:#cf9236;color:#FFF}.el-button--warning:active{outline:0}.el-button--warning.is-disabled,.el-button--warning.is-disabled:active,.el-button--warning.is-disabled:focus,.el-button--warning.is-disabled:hover{color:#FFF;background-color:#f3d19e;border-color:#f3d19e}.el-button--warning.is-plain{color:#E6A23C;background:#fdf6ec;border-color:#f5dab1}.el-button--warning.is-plain:focus,.el-button--warning.is-plain:hover{background:#E6A23C;border-color:#E6A23C;color:#FFF}.el-button--warning.is-plain:active{background:#cf9236;border-color:#cf9236;color:#FFF;outline:0}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:hover{color:#f0c78a;background-color:#fdf6ec;border-color:#faecd8}.el-button--danger{color:#FFF;background-color:#F56C6C;border-color:#F56C6C}.el-button--danger:focus,.el-button--danger:hover{background:#f78989;border-color:#f78989;color:#FFF}.el-button--danger.is-active,.el-button--danger:active{background:#dd6161;border-color:#dd6161;color:#FFF}.el-button--danger:active{outline:0}.el-button--danger.is-disabled,.el-button--danger.is-disabled:active,.el-button--danger.is-disabled:focus,.el-button--danger.is-disabled:hover{color:#FFF;background-color:#fab6b6;border-color:#fab6b6}.el-button--danger.is-plain{color:#F56C6C;background:#fef0f0;border-color:#fbc4c4}.el-button--danger.is-plain:focus,.el-button--danger.is-plain:hover{background:#F56C6C;border-color:#F56C6C;color:#FFF}.el-button--danger.is-plain:active{background:#dd6161;border-color:#dd6161;color:#FFF;outline:0}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:hover{color:#f9a7a7;background-color:#fef0f0;border-color:#fde2e2}.el-button--info{color:#FFF;background-color:#909399;border-color:#909399}.el-button--info:focus,.el-button--info:hover{background:#a6a9ad;border-color:#a6a9ad;color:#FFF}.el-button--info.is-active,.el-button--info:active{background:#82848a;border-color:#82848a;color:#FFF}.el-button--info:active{outline:0}.el-button--info.is-disabled,.el-button--info.is-disabled:active,.el-button--info.is-disabled:focus,.el-button--info.is-disabled:hover{color:#FFF;background-color:#c8c9cc;border-color:#c8c9cc}.el-button--info.is-plain{color:#909399;background:#f4f4f5;border-color:#d3d4d6}.el-button--info.is-plain:focus,.el-button--info.is-plain:hover{background:#909399;border-color:#909399;color:#FFF}.el-button--info.is-plain:active{background:#82848a;border-color:#82848a;color:#FFF;outline:0}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:hover{color:#bcbec2;background-color:#f4f4f5;border-color:#e9e9eb}.el-button--text,.el-button--text.is-disabled,.el-button--text.is-disabled:focus,.el-button--text.is-disabled:hover,.el-button--text:active{border-color:transparent}.el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.el-button--mini,.el-button--small{font-size:12px;border-radius:3px}.el-button--medium.is-round{padding:10px 20px}.el-button--medium.is-circle{padding:10px}.el-button--small,.el-button--small.is-round{padding:9px 15px}.el-button--small.is-circle{padding:9px}.el-button--mini,.el-button--mini.is-round{padding:7px 15px}.el-button--mini.is-circle{padding:7px}.el-button--text{color:#783887;background:0 0;padding-left:0;padding-right:0}.el-button--text:focus,.el-button--text:hover{color:rgb(147, 96, 159);border-color:transparent;background-color:transparent}.el-button--text:active{color:rgb(108, 50, 122);background-color:transparent}.el-button-group{display:inline-block;vertical-align:middle}.el-button-group::after,.el-button-group::before{display:table;content:""}.el-button-group::after{clear:both}.el-button-group>.el-button{float:left;position:relative}.el-button-group>.el-button+.el-button{margin-left:0}.el-button-group>.el-button.is-disabled{z-index:1}.el-button-group>.el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.el-button-group>.el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group>.el-button:first-child:last-child{border-radius:4px}.el-button-group>.el-button:first-child:last-child.is-round{border-radius:20px}.el-button-group>.el-button:first-child:last-child.is-circle{border-radius:50%}.el-button-group>.el-button:not(:first-child):not(:last-child){border-radius:0}.el-button-group>.el-button:not(:last-child){margin-right:-1px}.el-button-group>.el-button.is-active,.el-button-group>.el-button:active,.el-button-group>.el-button:focus,.el-button-group>.el-button:hover{z-index:1}.el-button-group>.el-dropdown>.el-button{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--primary:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--primary:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--success:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--success:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--warning:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--warning:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--danger:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--danger:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--info:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--info:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-calendar{background-color:#fff}.el-calendar__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:12px 20px;border-bottom:1px solid #EBEEF5}.el-backtop,.el-page-header{display:-webkit-box;display:-ms-flexbox}.el-calendar__title{color:#000;-ms-flex-item-align:center;align-self:center}.el-calendar__body{padding:12px 20px 35px}.el-calendar-table{table-layout:fixed;width:100%}.el-calendar-table thead th{padding:12px 0;color:#606266;font-weight:400}.el-calendar-table:not(.is-range) td.next,.el-calendar-table:not(.is-range) td.prev{color:#C0C4CC}.el-backtop,.el-calendar-table td.is-today{color:#783887}.el-calendar-table td{border-bottom:1px solid #EBEEF5;border-right:1px solid #EBEEF5;vertical-align:top;-webkit-transition:background-color .2s ease;transition:background-color .2s ease}.el-calendar-table td.is-selected{background-color:#F2F8FE}.el-calendar-table tr:first-child td{border-top:1px solid #EBEEF5}.el-calendar-table tr td:first-child{border-left:1px solid #EBEEF5}.el-calendar-table tr.el-calendar-table__row--hide-border td{border-top:none}.el-calendar-table .el-calendar-day{-webkit-box-sizing:border-box;box-sizing:border-box;padding:8px;height:85px}.el-calendar-table .el-calendar-day:hover{cursor:pointer;background-color:#F2F8FE}.el-backtop{position:fixed;background-color:#FFF;width:40px;height:40px;border-radius:50%;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;-webkit-box-shadow:0 0 6px rgba(0,0,0,.12);box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#F2F6FC}.el-page-header{display:flex;line-height:24px}.el-page-header__left{display:-webkit-box;display:-ms-flexbox;display:flex;cursor:pointer;margin-right:40px;position:relative}.el-page-header__left::after{content:"";position:absolute;width:1px;height:16px;right:-20px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);background-color:#DCDFE6}.el-checkbox,.el-checkbox__input{display:inline-block;position:relative;white-space:nowrap}.el-page-header__left .el-icon-back{font-size:18px;margin-right:6px;-ms-flex-item-align:center;align-self:center}.el-page-header__title{font-size:14px;font-weight:500}.el-page-header__content{font-size:18px;color:#303133}.el-checkbox{color:#606266;font-weight:500;font-size:14px;cursor:pointer;user-select:none;margin-right:30px}.el-checkbox-button__inner,.el-radio{font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #DCDFE6;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:normal;height:40px}.el-checkbox.is-bordered.is-checked{border-color:#783887}.el-checkbox.is-bordered.is-disabled{border-color:#EBEEF5;cursor:not-allowed}.el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px;height:36px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.el-checkbox.is-bordered.el-checkbox--small{padding:5px 15px 5px 10px;border-radius:3px;height:32px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.el-checkbox.is-bordered.el-checkbox--mini{padding:3px 15px 3px 10px;border-radius:3px;height:28px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.el-checkbox__input{cursor:pointer;outline:0;line-height:1;vertical-align:middle}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#DCDFE6;cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#C0C4CC}.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#F2F6FC;border-color:#DCDFE6}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#C0C4CC}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#F2F6FC;border-color:#DCDFE6}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#C0C4CC;border-color:#C0C4CC}.el-checkbox__input.is-checked .el-checkbox__inner,.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#783887;border-color:#783887}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:#C0C4CC;cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:#783887}.el-checkbox__input.is-focus .el-checkbox__inner{border-color:#783887}.el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#FFF;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.el-checkbox__inner{display:inline-block;position:relative;border:1px solid #DCDFE6;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#FFF;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.el-checkbox__inner:hover{border-color:#783887}.el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:"";border:1px solid #FFF;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s ease-in .05s;transition:-webkit-transform .15s ease-in .05s;transition:transform .15s ease-in .05s;transition:transform .15s ease-in .05s,-webkit-transform .15s ease-in .05s;-webkit-transform-origin:center;transform-origin:center}.el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;z-index:-1}.el-checkbox-button,.el-checkbox-button__inner{display:inline-block;position:relative}.el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.el-checkbox:last-of-type{margin-right:0}.el-checkbox-button__inner{line-height:1;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#FFF;border:1px solid #DCDFE6;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-checkbox-button__inner.is-round{padding:12px 20px}.el-checkbox-button__inner:hover{color:#783887}.el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.el-radio,.el-radio__input{line-height:1;outline:0;white-space:nowrap}.el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;z-index:-1}.el-radio,.el-radio__inner,.el-radio__input{position:relative;display:inline-block}.el-checkbox-button.is-checked .el-checkbox-button__inner{color:#FFF;background-color:#783887;border-color:#783887;-webkit-box-shadow:-1px 0 0 0 rgb(174, 136, 183);box-shadow:-1px 0 0 0 rgb(174, 136, 183)}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner{border-left-color:#783887}.el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#C0C4CC;cursor:not-allowed;background-image:none;background-color:#FFF;border-color:#EBEEF5;-webkit-box-shadow:none;box-shadow:none}.el-checkbox-button.is-disabled:first-child .el-checkbox-button__inner{border-left-color:#EBEEF5}.el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #DCDFE6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#783887}.el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.el-checkbox-group{font-size:0}.el-radio,.el-radio--medium.is-bordered .el-radio__label{font-size:14px}.el-radio{color:#606266;cursor:pointer;margin-right:30px}.el-cascader-node>.el-radio,.el-radio:last-child{margin-right:0}.el-radio.is-bordered{padding:12px 20px 0 10px;border-radius:4px;border:1px solid #DCDFE6;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px}.el-radio.is-bordered.is-checked{border-color:#783887}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#EBEEF5}.el-radio__input.is-disabled .el-radio__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#F5F7FA;border-color:#E4E7ED}.el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.el-radio--medium.is-bordered{padding:10px 20px 0 10px;border-radius:4px;height:36px}.el-radio--mini.is-bordered .el-radio__label,.el-radio--small.is-bordered .el-radio__label{font-size:12px}.el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.el-radio--small.is-bordered{padding:8px 15px 0 10px;border-radius:3px;height:32px}.el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio--mini.is-bordered{padding:6px 15px 0 10px;border-radius:3px;height:28px}.el-radio--mini.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio__input{cursor:pointer;vertical-align:middle}.el-radio__input.is-disabled .el-radio__inner{cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner::after{cursor:not-allowed;background-color:#F5F7FA}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner::after{background-color:#C0C4CC}.el-radio__input.is-disabled+span.el-radio__label{color:#C0C4CC;cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:#783887;background:#783887}.el-radio__input.is-checked .el-radio__inner::after{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.el-radio__input.is-checked+.el-radio__label{color:#783887}.el-radio__input.is-focus .el-radio__inner{border-color:#783887}.el-radio__inner{border:1px solid #DCDFE6;border-radius:100%;width:14px;height:14px;background-color:#FFF;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box}.el-radio__inner:hover{border-color:#783887}.el-radio__inner::after{width:4px;height:4px;border-radius:100%;background-color:#FFF;content:"";position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transition:-webkit-transform .15s ease-in;transition:-webkit-transform .15s ease-in;transition:transform .15s ease-in;transition:transform .15s ease-in,-webkit-transform .15s ease-in}.el-radio__original{opacity:0;outline:0;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{-webkit-box-shadow:0 0 2px 2px #783887;box-shadow:0 0 2px 2px #783887}.el-radio__label{font-size:14px;padding-left:10px}.el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default{scrollbar-width:none}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(144,147,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.el-scrollbar__thumb:hover{background-color:rgba(144,147,153,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-cascader-panel{display:-webkit-box;display:-ms-flexbox;display:flex;border-radius:4px;font-size:14px}.el-cascader-panel.is-bordered{border:1px solid #E4E7ED;border-radius:4px}.el-cascader-menu{min-width:180px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;border-right:solid 1px #E4E7ED}.el-cascader-menu:last-child{border-right:none}.el-cascader-menu:last-child .el-cascader-node{padding-right:20px}.el-cascader-menu__wrap{height:204px}.el-cascader-menu__list{position:relative;min-height:100%;margin:0;padding:6px 0;list-style:none;-webkit-box-sizing:border-box;box-sizing:border-box}.el-avatar,.el-drawer{-webkit-box-sizing:border-box;overflow:hidden}.el-cascader-menu__hover-zone{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.el-cascader-menu__empty-text{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;color:#C0C4CC}.el-cascader-node{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 30px 0 20px;height:34px;line-height:34px;outline:0}.el-cascader-node.is-selectable.in-active-path{color:#606266}.el-cascader-node.in-active-path,.el-cascader-node.is-active,.el-cascader-node.is-selectable.in-checked-path{color:#783887;font-weight:700}.el-cascader-node:not(.is-disabled){cursor:pointer}.el-cascader-node:not(.is-disabled):focus,.el-cascader-node:not(.is-disabled):hover{background:#F5F7FA}.el-cascader-node.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-cascader-node__prefix{position:absolute;left:10px}.el-cascader-node__postfix{position:absolute;right:10px}.el-cascader-node__label{-webkit-box-flex:1;-ms-flex:1;flex:1;padding:0 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-cascader-node>.el-radio .el-radio__label{padding-left:0}.el-avatar{display:inline-block;box-sizing:border-box;text-align:center;color:#fff;background:#C0C4CC;width:40px;height:40px;line-height:40px;font-size:14px}.el-avatar>img{display:block;height:100%;vertical-align:middle}.el-drawer,.el-drawer__header{display:-webkit-box;display:-ms-flexbox}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:4px}.el-avatar--icon{font-size:18px}.el-avatar--large{width:40px;height:40px;line-height:40px}.el-avatar--medium{width:36px;height:36px;line-height:36px}.el-avatar--small{width:28px;height:28px;line-height:28px}.el-drawer.btt,.el-drawer.ttb,.el-drawer__container{left:0;right:0;width:100%}.el-drawer.ltr,.el-drawer.rtl,.el-drawer__container{top:0;bottom:0;height:100%}@-webkit-keyframes el-drawer-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes el-drawer-fade-in{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes rtl-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}}@keyframes rtl-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}}@-webkit-keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes ltr-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}}@keyframes ltr-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}}@-webkit-keyframes ttb-drawer-in{0%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes ttb-drawer-in{0%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes ttb-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}}@keyframes ttb-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}}@-webkit-keyframes btt-drawer-in{0%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes btt-drawer-in{0%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes btt-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}}@keyframes btt-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}}.el-drawer{position:absolute;box-sizing:border-box;background-color:#FFF;display:flex;-ms-flex-direction:column;flex-direction:column;-webkit-box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.el-drawer.rtl{-webkit-animation:rtl-drawer-out .3s;animation:rtl-drawer-out .3s;right:0}.el-drawer__open .el-drawer.rtl{-webkit-animation:rtl-drawer-in .3s 1ms;animation:rtl-drawer-in .3s 1ms}.el-drawer.ltr{-webkit-animation:ltr-drawer-out .3s;animation:ltr-drawer-out .3s;left:0}.el-drawer__open .el-drawer.ltr{-webkit-animation:ltr-drawer-in .3s 1ms;animation:ltr-drawer-in .3s 1ms}.el-drawer.ttb{-webkit-animation:ttb-drawer-out .3s;animation:ttb-drawer-out .3s;top:0}.el-drawer__open .el-drawer.ttb{-webkit-animation:ttb-drawer-in .3s 1ms;animation:ttb-drawer-in .3s 1ms}.el-drawer.btt{-webkit-animation:btt-drawer-out .3s;animation:btt-drawer-out .3s;bottom:0}.el-drawer__open .el-drawer.btt{-webkit-animation:btt-drawer-in .3s 1ms;animation:btt-drawer-in .3s 1ms}.el-drawer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:hidden;margin:0}.el-drawer__header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#72767b;display:flex;margin-bottom:32px;padding:20px 20px 0}.el-drawer__header>:first-child{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__title{margin:0;-webkit-box-flex:1;-ms-flex:1;flex:1;line-height:inherit;font-size:1rem}.el-drawer__close-btn{border:none;cursor:pointer;font-size:20px;color:inherit;background-color:transparent}.el-drawer__body{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__body>*{-webkit-box-sizing:border-box;box-sizing:border-box}.el-drawer__container{position:relative}.el-drawer-fade-enter-active{-webkit-animation:el-drawer-fade-in .3s;animation:el-drawer-fade-in .3s}.el-drawer-fade-leave-active{animation:el-drawer-fade-in .3s reverse}.el-popconfirm__main{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-popconfirm__icon{margin-right:5px}.el-popconfirm__action{text-align:right;margin:0} \ No newline at end of file diff --git a/frontend/src/business/components/api/definition/ApiDefinition.vue b/frontend/src/business/components/api/definition/ApiDefinition.vue new file mode 100644 index 0000000000..13240103dc --- /dev/null +++ b/frontend/src/business/components/api/definition/ApiDefinition.vue @@ -0,0 +1,266 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiAdvancedConfig.vue b/frontend/src/business/components/api/definition/components/ApiAdvancedConfig.vue new file mode 100644 index 0000000000..e832ff7704 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiAdvancedConfig.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiCaseList.vue b/frontend/src/business/components/api/definition/components/ApiCaseList.vue new file mode 100644 index 0000000000..49ca3414de --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiCaseList.vue @@ -0,0 +1,505 @@ + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiConfig.vue b/frontend/src/business/components/api/definition/components/ApiConfig.vue new file mode 100644 index 0000000000..f6f8dd2f67 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiConfig.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiKeyValue.vue b/frontend/src/business/components/api/definition/components/ApiKeyValue.vue new file mode 100644 index 0000000000..c528e245e5 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiKeyValue.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiList.vue b/frontend/src/business/components/api/definition/components/ApiList.vue new file mode 100644 index 0000000000..d9e2a69201 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiList.vue @@ -0,0 +1,397 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiModule.vue b/frontend/src/business/components/api/definition/components/ApiModule.vue new file mode 100644 index 0000000000..735b018bf9 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiModule.vue @@ -0,0 +1,504 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiScenarioVariables.vue b/frontend/src/business/components/api/definition/components/ApiScenarioVariables.vue new file mode 100644 index 0000000000..c4cb506c3a --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiScenarioVariables.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiVariable.vue b/frontend/src/business/components/api/definition/components/ApiVariable.vue new file mode 100644 index 0000000000..b2c9036b05 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiVariable.vue @@ -0,0 +1,257 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiVariableAdvance.vue b/frontend/src/business/components/api/definition/components/ApiVariableAdvance.vue new file mode 100644 index 0000000000..cdfbb8cf0d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiVariableAdvance.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/ApiVariableInput.vue b/frontend/src/business/components/api/definition/components/ApiVariableInput.vue new file mode 100644 index 0000000000..f9319f713b --- /dev/null +++ b/frontend/src/business/components/api/definition/components/ApiVariableInput.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/BottomContainer.vue b/frontend/src/business/components/api/definition/components/BottomContainer.vue new file mode 100644 index 0000000000..371fdc7cdc --- /dev/null +++ b/frontend/src/business/components/api/definition/components/BottomContainer.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/MsCodeEdit.vue b/frontend/src/business/components/api/definition/components/MsCodeEdit.vue new file mode 100644 index 0000000000..09655cb806 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/MsCodeEdit.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/Run.vue b/frontend/src/business/components/api/definition/components/Run.vue new file mode 100644 index 0000000000..69da69a55e --- /dev/null +++ b/frontend/src/business/components/api/definition/components/Run.vue @@ -0,0 +1,124 @@ + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionDuration.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionDuration.vue new file mode 100644 index 0000000000..a24aabc915 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionDuration.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsonPath.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsonPath.vue new file mode 100644 index 0000000000..62a37b75cf --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsonPath.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue new file mode 100644 index 0000000000..af8da65f10 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionRegex.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionRegex.vue new file mode 100644 index 0000000000..1feb9822d9 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionRegex.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionText.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionText.vue new file mode 100644 index 0000000000..fb13c58f50 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionText.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue new file mode 100644 index 0000000000..c134f9faea --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue new file mode 100644 index 0000000000..edc6c07058 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionsEdit.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionsEdit.vue new file mode 100644 index 0000000000..b2fdd70c8c --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionsEdit.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggestList.vue b/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggestList.vue new file mode 100644 index 0000000000..23b57dd83a --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggestList.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/auth/ApiAuthConfig.vue b/frontend/src/business/components/api/definition/components/auth/ApiAuthConfig.vue new file mode 100644 index 0000000000..36e4004250 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/auth/ApiAuthConfig.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue b/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue new file mode 100644 index 0000000000..087e0b066a --- /dev/null +++ b/frontend/src/business/components/api/definition/components/basis/AddBasisApi.vue @@ -0,0 +1,172 @@ + + + diff --git a/frontend/src/business/components/api/definition/components/body/ApiBinaryVariable.vue b/frontend/src/business/components/api/definition/components/body/ApiBinaryVariable.vue new file mode 100644 index 0000000000..f34b7348ca --- /dev/null +++ b/frontend/src/business/components/api/definition/components/body/ApiBinaryVariable.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/body/ApiBody.vue b/frontend/src/business/components/api/definition/components/body/ApiBody.vue new file mode 100644 index 0000000000..ce8d599ae8 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/body/ApiBody.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/body/ApiBodyFileUpload.vue b/frontend/src/business/components/api/definition/components/body/ApiBodyFileUpload.vue new file mode 100644 index 0000000000..e226346057 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/body/ApiBodyFileUpload.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/body/ApiFromUrlVariable.vue b/frontend/src/business/components/api/definition/components/body/ApiFromUrlVariable.vue new file mode 100644 index 0000000000..eb4fad64f4 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/body/ApiFromUrlVariable.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/body/ApiJsonVariable.vue b/frontend/src/business/components/api/definition/components/body/ApiJsonVariable.vue new file mode 100644 index 0000000000..ae349f2238 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/body/ApiJsonVariable.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/collapse/ApiCollapse.vue b/frontend/src/business/components/api/definition/components/collapse/ApiCollapse.vue new file mode 100644 index 0000000000..7e38dd82d0 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/collapse/ApiCollapse.vue @@ -0,0 +1,73 @@ + + diff --git a/frontend/src/business/components/api/definition/components/collapse/ApiCollapseItem.vue b/frontend/src/business/components/api/definition/components/collapse/ApiCollapseItem.vue new file mode 100644 index 0000000000..2dee4ef51f --- /dev/null +++ b/frontend/src/business/components/api/definition/components/collapse/ApiCollapseItem.vue @@ -0,0 +1,134 @@ + + + + diff --git a/frontend/src/business/components/api/definition/components/collapse/ApiRequestMethodSelect.vue b/frontend/src/business/components/api/definition/components/collapse/ApiRequestMethodSelect.vue new file mode 100644 index 0000000000..ffda071ffe --- /dev/null +++ b/frontend/src/business/components/api/definition/components/collapse/ApiRequestMethodSelect.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/BasisApi.vue b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue new file mode 100644 index 0000000000..00701cb96d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue new file mode 100644 index 0000000000..ee9ef4f851 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue new file mode 100644 index 0000000000..4e5abab9e6 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue new file mode 100644 index 0000000000..fae9602f72 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue new file mode 100644 index 0000000000..5dfd708339 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/debug/DebugDubboPage.vue b/frontend/src/business/components/api/definition/components/debug/DebugDubboPage.vue new file mode 100644 index 0000000000..015cdc803d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/debug/DebugDubboPage.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue b/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue new file mode 100644 index 0000000000..432765373d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/debug/DebugJdbcPage.vue b/frontend/src/business/components/api/definition/components/debug/DebugJdbcPage.vue new file mode 100644 index 0000000000..7d1a7a07e7 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/debug/DebugJdbcPage.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/debug/DebugTcpPage.vue b/frontend/src/business/components/api/definition/components/debug/DebugTcpPage.vue new file mode 100644 index 0000000000..550957044f --- /dev/null +++ b/frontend/src/business/components/api/definition/components/debug/DebugTcpPage.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/environment/ApiEnvironmentConfig.vue b/frontend/src/business/components/api/definition/components/environment/ApiEnvironmentConfig.vue new file mode 100644 index 0000000000..5af6936688 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/environment/ApiEnvironmentConfig.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/environment/ApiHostTable.vue b/frontend/src/business/components/api/definition/components/environment/ApiHostTable.vue new file mode 100644 index 0000000000..b7da802a17 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/environment/ApiHostTable.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/environment/EnvironmentCommonConfig.vue b/frontend/src/business/components/api/definition/components/environment/EnvironmentCommonConfig.vue new file mode 100644 index 0000000000..8c505131c8 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/environment/EnvironmentCommonConfig.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/environment/EnvironmentEdit.vue b/frontend/src/business/components/api/definition/components/environment/EnvironmentEdit.vue new file mode 100644 index 0000000000..840ff337f6 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/environment/EnvironmentEdit.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/environment/EnvironmentHttpConfig.vue b/frontend/src/business/components/api/definition/components/environment/EnvironmentHttpConfig.vue new file mode 100644 index 0000000000..00f39b75e2 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/environment/EnvironmentHttpConfig.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue b/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue new file mode 100644 index 0000000000..6c8dffcdeb --- /dev/null +++ b/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/extract/ApiExtractCommon.vue b/frontend/src/business/components/api/definition/components/extract/ApiExtractCommon.vue new file mode 100644 index 0000000000..6e45e64819 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/extract/ApiExtractCommon.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/extract/ApiExtractEdit.vue b/frontend/src/business/components/api/definition/components/extract/ApiExtractEdit.vue new file mode 100644 index 0000000000..cda3550b44 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/extract/ApiExtractEdit.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/import/ApiImport.vue b/frontend/src/business/components/api/definition/components/import/ApiImport.vue new file mode 100644 index 0000000000..1097907aa0 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/import/ApiImport.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/jmeter/components.js b/frontend/src/business/components/api/definition/components/jmeter/components.js new file mode 100644 index 0000000000..1675353bf3 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components.js @@ -0,0 +1,62 @@ +import UnsupportedComponent from "./components/unspported-component"; + +// JMX models +const models = require.context('./components/', true, /index\.js$/); +// Vue控件 +const components = require.context('./components/', true, /main\.vue$/); + +const MODELS = models.keys().map(key => { + return models(key).schema +}).reduce((c1, c2) => { + return {...c1, ...c2} +}) + +const COMPONENTS = [ + ...components.keys().map(key => { + return components(key).default.name; + }) +] + +export const createComponent = function (name) { + let component = MODELS[name]; + if (component) { + return new component(); + } else { + return new UnsupportedComponent() + } +} + +export const loadComponent = function (element, hashTree) { + if (element.name) { + let component = MODELS[element.name]; + if (component) { + return new component({options: element, hashTree: hashTree}); + } else { + return new UnsupportedComponent({options: element, hashTree: hashTree}) + } + } +} + +export const loadHashTree = function (options) { + if (options.elements) { + let list = []; + for (let i = 0; i < options.elements.length; i += 2) { + let element = loadComponent(options.elements[i], options.elements[i + 1]); + list.push(element); + } + return list; + } +} + +export const hasComponent = name => { + return COMPONENTS.includes(name) ? name : "UnsupportedComponent"; +} + +export class Request { + static TYPES = { + HTTP: "HTTP", + DUBBO: "DUBBO", + SQL: "SQL", + TCP: "TCP" + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/assertions/assertion.js b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/assertion.js new file mode 100644 index 0000000000..fe68add88e --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/assertion.js @@ -0,0 +1,10 @@ +import HashTreeElement from "../../hashtree"; + +export const TYPE = "Assertion"; + +export default class Assertion extends HashTreeElement { + constructor(options = {}) { + super(options); + this.$type = TYPE; + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/assertions/duration-assertion/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/duration-assertion/index.js new file mode 100644 index 0000000000..5dfda723b1 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/duration-assertion/index.js @@ -0,0 +1,25 @@ +import Assertion from "../assertion"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "DurationAssertionGui", + testclass: "DurationAssertion", + testname: "DurationAssertion", + enabled: "true" + }, + } +}; + +export default class DurationAssertion extends Assertion { + constructor(options = DEFAULT_OPTIONS) { + super(options); + + this.duration = this.initStringProp("DurationAssertion.duration") + this.scope = this.initStringProp("Assertion.scope") + } +} + +export const schema = { + DurationAssertion: DurationAssertion +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/assertions/json-path-assertion/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/json-path-assertion/index.js new file mode 100644 index 0000000000..957c8c8766 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/json-path-assertion/index.js @@ -0,0 +1,31 @@ +import Assertion from "../assertion"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "JSONPathAssertionGui", + testclass: "JSONPathAssertion", + testname: "JSONPath Assertion", + enabled: "true" + }, + } +}; + +export default class JSONPathAssertion extends Assertion { + constructor(options = DEFAULT_OPTIONS) { + super(options); + + this.jsonPath = this.initStringProp("JSON_PATH") + this.jsonValidation = this.initBoolProp("JSONVALIDATION", false) + this.isRegex = this.initBoolProp("ISREGEX", true) + this.expectedValue = this.initStringProp("EXPECTED_VALUE") + + this.expectNull = this.initBoolProp("EXPECT_NULL", false) + this.invert = this.initBoolProp("INVERT", false) + + } +} + +export const schema = { + JSONPathAssertion: JSONPathAssertion +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/assertions/response-assertion/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/response-assertion/index.js new file mode 100644 index 0000000000..cb5ed86f5a --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/assertions/response-assertion/index.js @@ -0,0 +1,51 @@ +import Assertion from "../assertion"; +import {stringProp} from "../../../props"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "AssertionGui", + testclass: "ResponseAssertion", + testname: "ResponseAssertion", + enabled: "true" + }, + } +}; + +export default class ResponseAssertion extends Assertion { + constructor(options = DEFAULT_OPTIONS) { + super(options); + + this.customMessage = this.initStringProp("Assertion.custom_message") + this.scope = this.initStringProp("Assertion.scope") + this.variable = this.initStringProp("Scope.variable") + this.testField = this.initStringProp("Assertion.test_field", 'Assertion.response_data') + this.assumeSuccess = this.initBoolProp("Assertion.assume_success", false) + this.testType = this.initIntProp("Assertion.test_type", 16) + + this.testStrings = []; + let collectionProp = this.initCollectionProp('Asserion.test_strings'); + collectionProp.forEach(stringProp => { + this.testStrings.push({key: stringProp.key, string: stringProp.value, enable: true}); + }) + } + + updateProps() { + let collectionProp = this.props['Asserion.test_strings']; + collectionProp.clear(); + this.testStrings.forEach((item, index) => { + if (item.enable !== false) { + collectionProp.add(stringProp(index, item.string)); + } + }) + } + + toJson() { + this.updateProps(); + return super.toJson(); + } +} + +export const schema = { + ResponseAssertion: ResponseAssertion +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/auth-manager/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/auth-manager/index.js new file mode 100644 index 0000000000..1541c07940 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/auth-manager/index.js @@ -0,0 +1,30 @@ +import HashTreeElement from "../../hashtree"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "AuthPanel", + testclass: "AuthManager", + testname: "AuthManager", + enabled: "true" + }, + } +}; + +export default class AuthManager extends HashTreeElement { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "AuthManager"; + this.username = undefined; + this.password = undefined; + this.url = undefined; + this.realm = undefined; + this.verification = "No Auth"; + this.mechanism = "BASIC_DIGEST"; + this.encrypt = false; + this.environment = undefined; + } +} +export const schema = { + AuthManager: AuthManager +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/configurations/arguments/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/configurations/arguments/index.js new file mode 100644 index 0000000000..47a4ea4379 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/configurations/arguments/index.js @@ -0,0 +1,24 @@ +import Configuration from "../configuration"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "ArgumentsPanel", + testclass: "Arguments", + testname: "Arguments", + enabled: "true" + }, + } +}; + +export default class Arguments extends Configuration { + constructor(options = DEFAULT_OPTIONS) { + super(options); + + this.arguments = []; + } +} + +export const schema = { + Arguments: Arguments +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/configurations/configuration.js b/frontend/src/business/components/api/definition/components/jmeter/components/configurations/configuration.js new file mode 100644 index 0000000000..b9ae152ac1 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/configurations/configuration.js @@ -0,0 +1,10 @@ +import HashTreeElement from "../../hashtree"; + +export const TYPE = "Configuration"; + +export default class Configuration extends HashTreeElement { + constructor(options = {}) { + super(options); + this.$type = TYPE; + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/configurations/header-manager/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/configurations/header-manager/index.js new file mode 100644 index 0000000000..428e14a674 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/configurations/header-manager/index.js @@ -0,0 +1,24 @@ +import Configuration from "../configuration"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "HeaderPanel", + testclass: "HeaderManager", + testname: "HeaderManager", + enabled: "true" + }, + } +}; + +export default class HeaderManager extends Configuration { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "HeaderManager"; + this.headers = []; + } +} + +export const schema = { + HeaderManager: HeaderManager +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/jmeter-test-plan/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/jmeter-test-plan/index.js new file mode 100644 index 0000000000..9929c3551d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/jmeter-test-plan/index.js @@ -0,0 +1,43 @@ +import Element from "../../element"; +import {loadHashTree} from "../../components"; + +const DEFAULT_OPTIONS = { + type: "element", + name: "jmeterTestPlan", + attributes: {version: "1.2", properties: "5.0", jmeter: "5.2.1"} +}; + +export default class JmeterTestPlan extends Element { + constructor({options = DEFAULT_OPTIONS} = {options}) { + super(options); + this.hashTree = []; + if (options.elements) { + this.hashTree = loadHashTree(options.elements[0]); + } + } + + toJson() { + let json = super.toJson(); + if (this.hashTree) { + json.elements = []; + let elements = []; + this.hashTree.forEach(e => { + let json = e.toJson(); + elements.push(json.options); + elements.push(json.hashTree); + }) + let hashTree = { + "type": "element", + "name": "hashTree", + "elements": elements + } + json.elements.push(hashTree); + } + return json; + } +} + +export const schema = { + jmeterTestPlan: JmeterTestPlan +} + diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/json-path-extractor/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/json-path-extractor/index.js new file mode 100644 index 0000000000..4bdc797b67 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/json-path-extractor/index.js @@ -0,0 +1,30 @@ +import PostProcessor from "../post-processor"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "JSONPostProcessorGui", + testclass: "JSONPostProcessor", + testname: "JSONPostProcessor", + enabled: "true" + }, + } +}; + +export default class JSONPostProcessor extends PostProcessor { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.scope = this.initStringProp("Sample.scope") + this.variable = this.initStringProp("Scope.variable") + this.referenceNames = this.initStringProp("JSONPostProcessor.referenceNames") + this.jsonPathExprs = this.initStringProp("JSONPostProcessor.jsonPathExprs") + this.matchNumber = this.initStringProp("JSONPostProcessor.match_number") + this.defaultValues = this.initStringProp("JSONPostProcessor.defaultValues") + this.computeConcat = this.initBoolProp("JSONPostProcessor.compute_concat") + } +} + +export const schema = { + JSONPostProcessor: JSONPostProcessor +} + diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/jsr223-post-processor/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/jsr223-post-processor/index.js new file mode 100644 index 0000000000..a3525aaff6 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/jsr223-post-processor/index.js @@ -0,0 +1,28 @@ +import PostProcessor from "../post-processor"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "TestBeanGUI", + testclass: "JSR223PostProcessor", + testname: "JSR223 PostProcessor", + enabled: "true" + }, + } +}; + +export default class JSR223PostProcessor extends PostProcessor { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "JSR223PostProcessor"; + this.scriptLanguage = "java"; + this.parameters = []; + this.filename = undefined; + this.cacheKey = true; + this.script = undefined; + } +} + +export const schema = { + JSR223PostProcessor: JSR223PostProcessor +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/post-processor.js b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/post-processor.js new file mode 100644 index 0000000000..ecd8d5827c --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/post-processor.js @@ -0,0 +1,10 @@ +import HashTreeElement from "../../hashtree"; + +export const TYPE = "PostProcessor"; + +export default class PostProcessor extends HashTreeElement { + constructor(options = {}) { + super(options); + this.$type = TYPE; + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/regex-extractor/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/regex-extractor/index.js new file mode 100644 index 0000000000..2ccfecdf28 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/regex-extractor/index.js @@ -0,0 +1,32 @@ +import PostProcessor from "../post-processor"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "RegexExtractorGui", + testclass: "RegexExtractor", + testname: "RegexExtractor", + enabled: "true" + }, + } +}; + +export default class RegexExtractor extends PostProcessor { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.scope = this.initStringProp("Sample.scope") + this.variable = this.initStringProp("Scope.variable") + this.useHeaders = this.initStringProp("RegexExtractor.useHeaders", "false") + this.refName = this.initStringProp("RegexExtractor.refname") + this.regex = this.initStringProp("RegexExtractor.regex") + this.template = this.initStringProp("RegexExtractor.template") + this.matchNumber = this.initStringProp("RegexExtractor.match_number") + this.default = this.initStringProp("RegexExtractor.default") + this.defaultEmpty = this.initBoolProp("RegexExtractor.default_empty_value") + } +} + +export const schema = { + RegexExtractor: RegexExtractor +} + diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/xpath2-extractor/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/xpath2-extractor/index.js new file mode 100644 index 0000000000..4dfc654f55 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/post-processors/xpath2-extractor/index.js @@ -0,0 +1,31 @@ +import PostProcessor from "../post-processor"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "XPath2ExtractorGui", + testclass: "XPath2Extractor", + testname: "XPath Extractor", + enabled: "true" + }, + } +}; + +export default class XPath2Extractor extends PostProcessor { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.scope = this.initStringProp("Sample.scope") + this.variable = this.initStringProp("Scope.variable") + this.refName = this.initStringProp("XPathExtractor2.refname") + this.xpathQuery = this.initStringProp("XPathExtractor2.xpathQuery") + this.matchNumber = this.initStringProp("XPathExtractor2.match_number", 0) + this.default = this.initStringProp("XPathExtractor2.default") + this.namespaces = this.initStringProp("XPathExtractor2.namespaces") + this.fragment = this.initBoolProp("XPathExtractor2.fragment") + } +} + +export const schema = { + XPath2Extractor: XPath2Extractor +} + diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/pre-processors/jsr223-pre-processor/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/pre-processors/jsr223-pre-processor/index.js new file mode 100644 index 0000000000..f73b7ee69b --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/pre-processors/jsr223-pre-processor/index.js @@ -0,0 +1,28 @@ +import PostProcessor from "../pre-processor"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "TestBeanGUI", + testclass: "JSR223PreProcessor", + testname: "JSR223 PreProcessor", + enabled: "true" + }, + } +}; + +export default class JSR223PreProcessor extends PostProcessor { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "JSR223PreProcessor"; + this.scriptLanguage = "java"; + this.parameters = []; + this.filename = undefined; + this.cacheKey = undefined; + this.script = undefined; + } +} + +export const schema = { + JSR223PreProcessor: JSR223PreProcessor +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/pre-processors/pre-processor.js b/frontend/src/business/components/api/definition/components/jmeter/components/pre-processors/pre-processor.js new file mode 100644 index 0000000000..4e96df515f --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/pre-processors/pre-processor.js @@ -0,0 +1,10 @@ +import HashTreeElement from "../../hashtree"; + +export const TYPE = "PreProcessor"; + +export default class PreProcessor extends HashTreeElement { + constructor(options = {}) { + super(options); + this.$type = TYPE; + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/result-collector/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/result-collector/index.js new file mode 100644 index 0000000000..7bdcee4186 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/result-collector/index.js @@ -0,0 +1,25 @@ +import HashTreeElement from "../../hashtree"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "ViewResultsFullVisualizer", + testclass: "ResultCollector", + testname: "View Results Tree", + enabled: "true" + }, + } +}; + +export const TYPE = "ResultCollector"; + +export default class ResultCollector extends HashTreeElement { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.$type = TYPE; + } +} + +export const schema = { + ResultCollector: ResultCollector +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/dubbo-sampler/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/dubbo-sampler/index.js new file mode 100644 index 0000000000..234a3af17e --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/dubbo-sampler/index.js @@ -0,0 +1,38 @@ +import Sampler from "../sampler"; +import {ConfigCenter, ConsumerAndService, RegistryCenter} from "../../../../../model/ApiTestModel"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "DubboSampleGui", + testclass: "DubboSampler", + testname: "DubboSampler", + enabled: "true" + }, + } +}; +export default class DubboSampler extends Sampler { + static PROTOCOLS = { + DUBBO: "dubbo://", + RMI: "rmi://", + } + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "DubboSampler"; + this.hashTree = []; + this.protocol = options.protocol || DubboSampler.PROTOCOLS.DUBBO; + this.interface = options.interface; + this.method = options.method; + this.configCenter = new ConfigCenter(options.configCenter); + this.registryCenter = new RegistryCenter(options.registryCenter); + this.consumerAndService = new ConsumerAndService(options.consumerAndService); + this.args = []; + this.attachmentArgs = []; + this.dubboConfig = undefined; + this.debugReport = undefined; + } +} + +export const schema = { + DubboSampler: DubboSampler +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js new file mode 100644 index 0000000000..63a357344b --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js @@ -0,0 +1,45 @@ +import Sampler from "../sampler"; +import {Body} from "../../../../../model/ApiTestModel"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "HttpTestSampleGui", + testclass: "HTTPSamplerProxy", + testname: "HTTPSamplerProxy", + enabled: "true" + }, + } +}; +export default class HTTPSamplerProxy extends Sampler { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "HTTPSamplerProxy"; + this.protocol = "HTTP"; + this.domain = undefined; + this.port = undefined; + this.method = undefined; + this.path = undefined; + this.contentEncoding = undefined; + + this.autoRedirects = false; + this.followRedirects = true; + this.useKeepalive = true; + this.postBodyRaw = undefined; + this.doMultipartPost = false; + this.browserCompatibleMultipart = undefined; + this.embeddedUrlRe = undefined; + this.connectTimeout = 6000; + this.responseTimeout = 6000; + // 初始化主体对象 + this.body = new Body(); + this.arguments = []; + this.rest = []; + this.files = []; + } +} + +export const schema = { + HTTPSamplerProxy: HTTPSamplerProxy +} + diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js new file mode 100644 index 0000000000..571925c643 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/jdbc-sampler/index.js @@ -0,0 +1,35 @@ +import Sampler from "../sampler"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "TestBeanGUI", + testclass: "JDBCSampler", + testname: "JDBC Request", + enabled: "true" + }, + } +}; + +export default class JDBCSampler extends Sampler { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "JDBCSampler"; + this.hashTree = []; + this.variables = []; + this.dataSource = undefined; + this.query = undefined; + this.queryType = undefined; + this.queryArguments = undefined; + this.queryArgumentsTypes = undefined; + this.queryTimeout = undefined; + this.resultSetHandler = undefined; + this.resultSetMaxRows = undefined; + this.resultVariable = undefined; + this.variableNames = undefined; + } +} + +export const schema = { + JDBCSampler: JDBCSampler +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/sampler.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/sampler.js new file mode 100644 index 0000000000..d07464d3be --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/sampler.js @@ -0,0 +1,10 @@ +import HashTreeElement from "../../hashtree"; + +export const TYPE = "Sampler"; + +export default class Sampler extends HashTreeElement { + constructor(options = {}) { + super(options); + this.$type = TYPE; + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/tcp-sampler/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/tcp-sampler/index.js new file mode 100644 index 0000000000..4d437674b5 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/tcp-sampler/index.js @@ -0,0 +1,40 @@ +import Sampler from "../sampler"; + +const DEFAULT_OPTIONS = { + options: { + attributes: { + guiclass: "TCPSamplerGui", + testclass: "TCPSampler", + testname: "TCPSampler", + enabled: "true" + }, + } +}; + +export default class TCPSampler extends Sampler { + static CLASSES = ["TCPClientImpl", "BinaryTCPClientImpl", "LengthPrefixedBinaryTCPClientImpl"] + + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.type = "TCPSampler"; + this.classname = options.classname || TCPSampler.CLASSES[0]; + this.server = options.server; + this.port = options.port; + this.ctimeout = options.ctimeout; // Connect + this.timeout = options.timeout; // Response + + this.reUseConnection = options.reUseConnection === undefined ? true : options.reUseConnection; + this.nodelay = options.nodelay === undefined ? false : options.nodelay; + this.closeConnection = options.closeConnection === undefined ? false : options.closeConnection; + this.soLinger = options.soLinger; + this.eolByte = options.eolByte; + + this.username = options.username; + this.password = options.password; + this.hashTree = []; + } +} + +export const schema = { + TCPSampler: TCPSampler +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js new file mode 100644 index 0000000000..6d0caf218b --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/test-plan/index.js @@ -0,0 +1,55 @@ +import HashTreeElement from "../../hashtree"; +import {elementProp, stringProp} from "../../props"; + +const DEFAULT_OPTIONS = { + options: { + attributes: {guiclass: "TestPlanGui", testclass: "TestPlan", testname: "TestPlan", enabled: "true"}, + } +}; + +export const TYPE = "TestPlan"; + +export default class TestPlan extends HashTreeElement { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.$type = TYPE; + this.type = TYPE; + this.functionalMode = this.initBoolProp('TestPlan.functional_mode', false); + this.serializeThreadGroups = this.initBoolProp('TestPlan.serialize_threadgroups', false); + this.tearDownOnShutdown = this.initBoolProp('TestPlan.tearDown_on_shutdown', true); + this.userDefineClasspath = this.initStringProp('TestPlan.user_define_classpath'); + + this.userDefinedVariables = []; + + let elementProp = this.initElementProp('TestPlan.user_defined_variables', 'Arguments'); + let collectionProp = elementProp.initCollectionProp('Arguments.arguments'); + collectionProp.forEach(ep => { + let name = ep.initStringProp('Argument.name').value; + let value = ep.initStringProp('Argument.value').value; + this.userDefinedVariables.push({name: name, value: value, enable: true}); + }) + } + + updateProps() { + let collectionProp = this.props['TestPlan.user_defined_variables'].elements['Arguments.arguments']; + collectionProp.clear(); + this.userDefinedVariables.forEach(variable => { + if (variable.enable !== false) { + let ep = elementProp(variable.name, "Argument"); + ep.add(stringProp("Argument.name", variable.name)); + ep.add(stringProp("Argument.value", variable.value)); + ep.add(stringProp("Argument.metadata", "=")); + collectionProp.add(ep) + } + }) + } + + toJson() { + this.updateProps(); + return super.toJson(); + } +} + +export const schema = { + TestPlan: TestPlan +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/thread-group/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/thread-group/index.js new file mode 100644 index 0000000000..ff84008fd7 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/thread-group/index.js @@ -0,0 +1,34 @@ +import HashTreeElement from "../../hashtree"; + +const DEFAULT_OPTIONS = { + options: { + attributes: {guiclass: "ThreadGroupGui", testclass: "ThreadGroup", testname: "ThreadGroup", enabled: "true"}, + } +}; + +export const TYPE = "ThreadGroup"; + +export default class ThreadGroup extends HashTreeElement { + constructor(options = DEFAULT_OPTIONS) { + super(options); + this.$type = TYPE; + this.type = TYPE; + this.onSampleError = this.initStringProp('ThreadGroup.on_sample_error', 'continue'); + this.numThreads = this.initStringProp('ThreadGroup.num_threads', 1); + this.rampTime = this.initStringProp('ThreadGroup.ramp_time', 1); + + let loopController = this.initElementProp('ThreadGroup.main_controller', 'LoopController'); + this.continueForever = loopController.initBoolProp('LoopController.continue_forever', false); + this.loops = loopController.initStringProp('LoopController.loops', 1); + + this.sameUserOnNextIteration = this.initBoolProp('ThreadGroup.same_user_on_next_iteration', true); + this.delayedStart = this.initBoolProp('ThreadGroup.delayedStart'); + this.scheduler = this.initBoolProp('ThreadGroup.scheduler', false); + this.delay = this.initStringProp('ThreadGroup.delay'); + this.duration = this.initStringProp('ThreadGroup.duration'); + } +} + +export const schema = { + ThreadGroup: ThreadGroup +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/unspported-component/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/unspported-component/index.js new file mode 100644 index 0000000000..7d90fdc233 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/components/unspported-component/index.js @@ -0,0 +1,14 @@ +import HashTreeElement from "../../hashtree"; + +export const TYPE = "UnsupportedComponent"; + +export default class UnsupportedComponent extends HashTreeElement { + constructor(options = {}) { + super(options) + this.$type = TYPE; + } +} + +export const schema = { + UnsupportedComponent: UnsupportedComponent +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/element.js b/frontend/src/business/components/api/definition/components/jmeter/element.js new file mode 100644 index 0000000000..bd1b80ce9f --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/element.js @@ -0,0 +1,50 @@ +import {uuid} from "@/common/js/utils"; + +/** + * options: JXM转换的JSON对象(xml2json) + */ +export default class Element { + constructor(options = {}) { + this.id = uuid(); + this.type = options.type || "element"; + this.name = options.name + + if (options.attributes) { + this.attributes = options.attributes; + if (options.attributes.testname) { + this.label = options.attributes.testname; + } + if (options.attributes.testclass && this.name === undefined) { + this.name = options.attributes.testclass; + } + if (options.attributes.enabled) { + this.enabled = options.attributes.enabled === "true"; + } + } + } + + toJson() { + let json = {}; + if (this.type) { + json.type = this.type; + } + if (this.name) { + json.name = this.name; + } + if (this.attributes) { + json.attributes = this.attributes; + if (this.label !== undefined) { + json.attributes.testname = this.label; + } + if (this.enabled !== undefined) { + json.attributes.enabled = this.enabled + ""; + } + } + return json; + } +} + + + + + diff --git a/frontend/src/business/components/api/definition/components/jmeter/hashtree.js b/frontend/src/business/components/api/definition/components/jmeter/hashtree.js new file mode 100644 index 0000000000..70205a2a02 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/hashtree.js @@ -0,0 +1,90 @@ +import Element from "./element"; +import {boolProp, intProp, longProp, stringProp, collectionProp, elementProp, getProps} from "./props"; +import {loadHashTree, loadComponent} from "./components"; + +export default class HashTreeElement extends Element { + constructor({options: options, hashTree: hashTree}) { + super(options); + this.props = getProps(options.elements); + this.comments = this.initStringProp('TestPlan.comments'); + + if (hashTree) { + this.hashTree = loadHashTree(hashTree); + } + } + + clone() { + let json = this.toJson(); + return loadComponent(json.options, json.hashTree); + } + + initIntProp(name, defaultValue) { + if (this.props[name] === undefined) { + this.props[name] = intProp(name, defaultValue); + } + return this.props[name]; + } + + initLongProp(name, defaultValue) { + if (this.props[name] === undefined) { + this.props[name] = longProp(name, defaultValue); + } + return this.props[name]; + } + + initBoolProp(name, defaultValue) { + if (this.props[name] === undefined) { + this.props[name] = boolProp(name, defaultValue); + } + return this.props[name]; + } + + initStringProp(name, defaultValue) { + if (this.props[name] === undefined) { + this.props[name] = stringProp(name, defaultValue); + } + return this.props[name]; + } + + initElementProp(name, elementType) { + if (this.props[name] === undefined) { + this.props[name] = elementProp(name, elementType); + } + return this.props[name]; + } + + initCollectionProp(name) { + if (this.props[name] === undefined) { + this.props[name] = collectionProp(name); + } + return this.props[name]; + } + + toJson() { + let self = super.toJson(); + if (this.props !== undefined) { + self.elements = []; + Object.keys(this.props).forEach(key => { + let json = this.props[key].toJson(); + if (json !== undefined) { + self.elements.push(json); + } + }); + } + + let hashTree = { + "type": "element", + "name": "hashTree", + } + if (this.hashTree) { + let elements = []; + this.hashTree.forEach(e => { + let json = e.toJson(); + elements.push(json.options); + elements.push(json.hashTree); + }) + hashTree.elements = elements; + } + return {options: self, hashTree: hashTree} + } +} diff --git a/frontend/src/business/components/api/definition/components/jmeter/props.js b/frontend/src/business/components/api/definition/components/jmeter/props.js new file mode 100644 index 0000000000..8bc3ee3d7b --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/props.js @@ -0,0 +1,374 @@ +import Element from "./element"; + +export class Prop extends Element { + constructor(options = {}) { + super(options); + if (options.attributes) { + this.key = options.attributes.name; + } + } +} + +/** + * int, long, bool, string + */ +export class BasicProp extends Prop { + constructor(options = {}) { + super(options); + if (options.elements) { + this.value = options.elements[0].text; + } + } + + toJson() { + if (this.value !== undefined && this.value !== "") { + let json = super.toJson(); + json.elements = [{ + "type": "text", + "text": "" + this.value + }] + return json; + } + } +} + +/** + * value:数值 + */ +export class IntProp extends BasicProp { + name = "intProp"; + + constructor(options = {}) { + super(options); + this.value = options.elements ? parseInt(options.elements[0].text) : undefined; + } +} + +/** + * value:数值 + */ +export class LongProp extends BasicProp { + name = "longProp"; + + constructor(options = {}) { + super(options); + this.value = options.elements ? parseInt(options.elements[0].text) : undefined; + } +} + +/** + * value:布尔 + */ +class BoolProp extends BasicProp { + name = "boolProp"; + + constructor(options = {}) { + super(options); + this.value = options.elements ? options.elements[0].text === 'true' : undefined; + } +} + +/** + * value:字符串 + */ +export class StringProp extends BasicProp { + name = "stringProp"; + + constructor(options = {}) { + super(options); + this.value = options.elements ? options.elements[0].text : undefined; + } +} + + +/** + * value: name/value 对象 + */ +export class ObjProp extends Prop { + constructor(options = {}) { + super(options); + if (options.elements) { + this.value = {}; + options.elements.forEach(e => { + if (e.name === "name") { + this.key = e.elements[0].text; + this.value.name = new BasicProp(e) + } else { + this.value.value = new ObjValue(e); + } + }) + } + } + + toJson() { + let json = super.toJson(); + if (this.value !== undefined) { + json.elements = []; + if (this.value.name) json.elements.push(this.value.name.toJson()); + if (this.value.value) json.elements.push(this.value.value.toJson()); + } + return json; + } +} + +export class ObjValue extends Element { + constructor(options = {}) { + super(options); + if (options.elements) { + this.value = {} + options.elements.forEach(e => { + this.value[e.name] = new BasicProp(e); + }) + } + } + + toJson() { + let json = super.toJson(); + if (this.value !== undefined) { + json.elements = []; + Object.keys(this.value).forEach(v => { + json.elements.push(this.value[v].toJson()); + }) + } + return json; + } +} + +/** + * elements:数组 + */ +export class CollectionProp extends Prop { + constructor(options = {}) { + super(options); + this.elements = []; + if (options.elements) { + options.elements.forEach(e => { + let prop; + switch (e.name) { + case "intProp": + prop = new IntProp(e); + break; + case "longProp": + prop = new LongProp(e); + break; + case "boolProp": + prop = new BoolProp(e); + break; + case "stringProp": + prop = new StringProp(e); + break; + case "elementProp": + prop = new ElementProp(e); + break; + } + this.add(prop); + }) + } + } + + add(prop) { + if (prop instanceof Prop) { + this.elements.push(prop); + } else { + console.error("prop is not Prop"); + } + } + + clear() { + this.elements = []; + } + + forEach(func) { + this.elements.forEach(func); + } + + toJson() { + let json = super.toJson(); + if (this.elements && this.elements.length > 0) { + json.elements = []; + this.elements.forEach(v => { + let element = v.toJson(); + if (element !== undefined) { + json.elements.push(element); + } + }) + } + return json; + } +} + +/** + * elements: Map + */ +export class ElementProp extends Prop { + constructor(options = {}) { + super(options); + this.elements = {}; + if (options.elements) { + this.elements = getProps(options.elements); + } + } + + initIntProp(name, defaultValue) { + if (this.elements[name] === undefined) { + this.elements[name] = intProp(name, defaultValue); + } + return this.elements[name]; + } + + initLongProp(name, defaultValue) { + if (this.elements[name] === undefined) { + this.elements[name] = longProp(name, defaultValue); + } + return this.elements[name]; + } + + initBoolProp(name, defaultValue) { + if (this.elements[name] === undefined) { + this.elements[name] = boolProp(name, defaultValue); + } + return this.elements[name]; + } + + initStringProp(name, defaultValue) { + if (this.elements[name] === undefined) { + this.elements[name] = stringProp(name, defaultValue); + } + return this.elements[name]; + } + + initCollectionProp(name) { + if (this.elements[name] === undefined) { + this.elements[name] = collectionProp(name); + } + return this.elements[name]; + } + + add(prop) { + if (prop instanceof Prop) { + this.elements[prop.key] = prop; + } else { + console.error("prop is not Prop"); + } + } + + remove(key) { + delete this.elements[key]; + } + + toJson() { + let json = super.toJson(); + let keys = Object.keys(this.elements); + if (keys.length > 0) { + json.elements = []; + keys.forEach(key => { + let element = this.elements[key].toJson(); + if (element !== undefined) { + json.elements.push(element); + } + }); + } + return json; + } +} + +export const getProps = function (elements) { + let props = {}; + if (elements) { + elements.forEach(e => { + let type = e.name; + let name; + if (e.attributes && e.attributes.name) { + name = e.attributes.name; + } + switch (type) { + case "intProp": + props[name] = new IntProp(e); + break; + case "longProp": + props[name] = new LongProp(e); + break; + case "boolProp": + props[name] = new BoolProp(e); + break; + case "stringProp": + props[name] = new StringProp(e); + break; + case "elementProp": + props[name] = new ElementProp(e); + break; + case "collectionProp": + props[name] = new CollectionProp(e); + break; + case "objProp": + // const obj = new ObjProp(e); + // props[obj.key] = obj; + props[name] = new ObjProp(e); + break; + } + }); + } + return props; +} + +export const basicProp = function (type, name, value) { + let options = { + type: "element", + attributes: { + name: name + } + } + if (value !== undefined) { + options.elements = [ + { + type: "text", + text: "" + value + } + ] + } + return new type(options); +} + +export const intProp = function (name, value) { + return basicProp(IntProp, name, value) +} + +export const longProp = function (name, value) { + return basicProp(LongProp, name, value) +} + +export const boolProp = function (name, value) { + return basicProp(BoolProp, name, value) +} + +export const stringProp = function (name, value) { + return basicProp(StringProp, name, value) +} + +export const collectionProp = function (name) { + let options = { + "type": "element", + "name": "collectionProp", + "attributes": { + "name": name + }, + elements: [] + }; + return new CollectionProp(options); +} + +export const elementProp = function (name, elementType, attributes) { + let options = { + "type": "element", + "name": "elementProp", + "attributes": { + "name": name, + "elementType": elementType, + ...attributes + }, + elements: [] + }; + return new ElementProp(options); +} + + diff --git a/frontend/src/business/components/api/definition/components/jmeter/setting.js b/frontend/src/business/components/api/definition/components/jmeter/setting.js new file mode 100644 index 0000000000..bf959a9ce9 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/jmeter/setting.js @@ -0,0 +1,21 @@ +export let Setting = { + // 示例 + // ThreadGroup: { + // tree: { + // drag: false, // 禁止拖拽 + // drop: false, // 禁止放置 + // }, + // menu: { + // disabled: true, //禁止显示菜单 + // }, + // edit: { + // disabled: true, // 禁止编辑页面 + // } + // } +}; + +export const use = function (s) { + Setting = s || Setting; +} + +export default {use} diff --git a/frontend/src/business/components/api/definition/components/processor/Jsr233Processor.vue b/frontend/src/business/components/api/definition/components/processor/Jsr233Processor.vue new file mode 100644 index 0000000000..e8816aba45 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/processor/Jsr233Processor.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue b/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue new file mode 100644 index 0000000000..a1476a8cb5 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/database/BasisParameters.vue @@ -0,0 +1,276 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/database/DatabaseConfig.vue b/frontend/src/business/components/api/definition/components/request/database/DatabaseConfig.vue new file mode 100644 index 0000000000..f5e96bad83 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/database/DatabaseConfig.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/database/DatabaseConfigList.vue b/frontend/src/business/components/api/definition/components/request/database/DatabaseConfigList.vue new file mode 100644 index 0000000000..ac84f433bb --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/database/DatabaseConfigList.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/database/DatabaseFrom.vue b/frontend/src/business/components/api/definition/components/request/database/DatabaseFrom.vue new file mode 100644 index 0000000000..d5c1f851d0 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/database/DatabaseFrom.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/dubbo/BasisParameters.vue b/frontend/src/business/components/api/definition/components/request/dubbo/BasisParameters.vue new file mode 100644 index 0000000000..7b7d91340c --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/dubbo/BasisParameters.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/dubbo/ConfigCenter.vue b/frontend/src/business/components/api/definition/components/request/dubbo/ConfigCenter.vue new file mode 100644 index 0000000000..a3082133f2 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/dubbo/ConfigCenter.vue @@ -0,0 +1,83 @@ + + + diff --git a/frontend/src/business/components/api/definition/components/request/dubbo/ConsumerAndService.vue b/frontend/src/business/components/api/definition/components/request/dubbo/ConsumerAndService.vue new file mode 100644 index 0000000000..1d26f45eed --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/dubbo/ConsumerAndService.vue @@ -0,0 +1,82 @@ + + + diff --git a/frontend/src/business/components/api/definition/components/request/dubbo/Interface.vue b/frontend/src/business/components/api/definition/components/request/dubbo/Interface.vue new file mode 100644 index 0000000000..6676c9b5f8 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/dubbo/Interface.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/dubbo/RegistryCenter.vue b/frontend/src/business/components/api/definition/components/request/dubbo/RegistryCenter.vue new file mode 100644 index 0000000000..5910097ce0 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/dubbo/RegistryCenter.vue @@ -0,0 +1,73 @@ + + + diff --git a/frontend/src/business/components/api/definition/components/request/dubbo/dubbo.css b/frontend/src/business/components/api/definition/components/request/dubbo/dubbo.css new file mode 100644 index 0000000000..d6a1180834 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/dubbo/dubbo.css @@ -0,0 +1,43 @@ +@media only screen and (max-width: 1200px) { + .dubbo-form-item { + width: 50%; + } + + .dubbo-form-item-long { + width: 100%; + } +} + +@media only screen and (min-width: 1201px) and (max-width: 1600px) { + .dubbo-form-item { + width: 33.33%; + } + + .dubbo-form-item-long { + width: 66.67%; + } +} + +@media only screen and (min-width: 1601px) { + .dubbo-form-item { + width: 25%; + } + + .dubbo-form-item-long { + width: 50%; + } +} + +.dubbo-form-item, .dubbo-form-item-long { + display: inline-block; +} + +.select-100 { + width: 100%; +} + +.dubbo-form-description { + width: 100%; + font-size: 13px; + margin-bottom: 12px; +} diff --git a/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue b/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue new file mode 100644 index 0000000000..f4aa06d0ac --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue b/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue new file mode 100644 index 0000000000..3a5af1910c --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/tcp/BasisParameters.vue b/frontend/src/business/components/api/definition/components/request/tcp/BasisParameters.vue new file mode 100644 index 0000000000..ee12c3e370 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/tcp/BasisParameters.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/tcp/TcpConfig.vue b/frontend/src/business/components/api/definition/components/request/tcp/TcpConfig.vue new file mode 100644 index 0000000000..24b97dc2a4 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/request/tcp/TcpConfig.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/response/AssertionResults.vue b/frontend/src/business/components/api/definition/components/response/AssertionResults.vue new file mode 100644 index 0000000000..f33a998781 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/response/AssertionResults.vue @@ -0,0 +1,36 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/response/RequestMetric.vue b/frontend/src/business/components/api/definition/components/response/RequestMetric.vue new file mode 100644 index 0000000000..0961c1f632 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/response/RequestMetric.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/response/RequestResultTail.vue b/frontend/src/business/components/api/definition/components/response/RequestResultTail.vue new file mode 100644 index 0000000000..4e411e5d34 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/response/RequestResultTail.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/response/ResponseResult.vue b/frontend/src/business/components/api/definition/components/response/ResponseResult.vue new file mode 100644 index 0000000000..8a0a36c8e9 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/response/ResponseResult.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/response/ResponseText.vue b/frontend/src/business/components/api/definition/components/response/ResponseText.vue new file mode 100644 index 0000000000..a0125cf9d6 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/response/ResponseText.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/response/SqlResultTable.vue b/frontend/src/business/components/api/definition/components/response/SqlResultTable.vue new file mode 100644 index 0000000000..5406710eef --- /dev/null +++ b/frontend/src/business/components/api/definition/components/response/SqlResultTable.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/runtest/RunTestDubboPage.vue b/frontend/src/business/components/api/definition/components/runtest/RunTestDubboPage.vue new file mode 100644 index 0000000000..8d19575df8 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/runtest/RunTestDubboPage.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/runtest/RunTestHTTPPage.vue b/frontend/src/business/components/api/definition/components/runtest/RunTestHTTPPage.vue new file mode 100644 index 0000000000..d06a7a409f --- /dev/null +++ b/frontend/src/business/components/api/definition/components/runtest/RunTestHTTPPage.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/runtest/RunTestSQLPage.vue b/frontend/src/business/components/api/definition/components/runtest/RunTestSQLPage.vue new file mode 100644 index 0000000000..9ab3d6ddac --- /dev/null +++ b/frontend/src/business/components/api/definition/components/runtest/RunTestSQLPage.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/runtest/RunTestTCPPage.vue b/frontend/src/business/components/api/definition/components/runtest/RunTestTCPPage.vue new file mode 100644 index 0000000000..191249affe --- /dev/null +++ b/frontend/src/business/components/api/definition/components/runtest/RunTestTCPPage.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/model/ApiTestModel.js b/frontend/src/business/components/api/definition/model/ApiTestModel.js new file mode 100644 index 0000000000..3e88ae1064 --- /dev/null +++ b/frontend/src/business/components/api/definition/model/ApiTestModel.js @@ -0,0 +1,1550 @@ +import { + Arguments, + CookieManager, + DNSCacheManager, + DubboSample, + DurationAssertion, + Element, + HashTree, + HeaderManager, + HTTPSamplerArguments, + HTTPsamplerFiles, + HTTPSamplerProxy, + JDBCDataSource, + JDBCSampler, + JSONPathAssertion, + JSONPostProcessor, + JSR223PostProcessor, + JSR223PreProcessor, + RegexExtractor, + ResponseCodeAssertion, + ResponseDataAssertion, + ResponseHeadersAssertion, + TestElement, + TestPlan, + ThreadGroup, + XPath2Extractor, + IfController as JMXIfController, + ConstantTimer as JMXConstantTimer, TCPSampler, +} from "./JMX"; +import Mock from "mockjs"; +import {funcFilters} from "@/common/js/func-filter"; + +export const uuid = function () { + let d = new Date().getTime() + let d2 = (performance && performance.now && (performance.now() * 1000)) || 0; + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + let r = Math.random() * 16; + if (d > 0) { + r = (d + r) % 16 | 0; + d = Math.floor(d / 16); + } else { + r = (d2 + r) % 16 | 0; + d2 = Math.floor(d2 / 16); + } + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); +} + +export const BODY_FILE_DIR = "/opt/metersphere/data/body"; //存放body文件上传目录 + +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; + } +} + +export const BODY_TYPE = { + KV: "KeyValue", + FORM_DATA: "Form Data", + RAW: "Raw", + WWW_FORM: "WWW_FORM", + XML: "XML", + BINARY: "BINARY", + JSON: "JSON" +} + +export const BODY_FORMAT = { + TEXT: "text", + JSON: "json", + XML: "xml", + HTML: "html", +} + +export const ASSERTION_TYPE = { + TEXT: "Text", + REGEX: "Regex", + JSON_PATH: "JSON", + DURATION: "Duration", + JSR223: "JSR223", + XPATH2: "XPath2", +} + +export const ASSERTION_REGEX_SUBJECT = { + RESPONSE_CODE: "Response Code", + RESPONSE_HEADERS: "Response Headers", + RESPONSE_DATA: "Response Data" +} + +export const EXTRACT_TYPE = { + REGEX: "Regex", + JSON_PATH: "JSONPath", + XPATH: "XPath" +} + +export class BaseConfig { + + set(options, notUndefined) { + options = this.initOptions(options) + for (let name in options) { + if (options.hasOwnProperty(name)) { + if (!(this[name] instanceof Array)) { + if (notUndefined === true) { + this[name] = options[name] === undefined ? this[name] : options[name]; + } + else { + this[name] = options[name]; + } + } + } + } + } + + sets(types, options) { + options = this.initOptions(options) + if (types) { + for (let name in types) { + if (types.hasOwnProperty(name) && options.hasOwnProperty(name)) { + options[name].forEach(o => { + this[name].push(new types[name](o)); + }) + } + } + } + } + + initOptions(options) { + return options || {}; + } + + isValid() { + return true; + } +} + +export class Test extends BaseConfig { + constructor(options) { + super(); + this.type = "MS API CONFIG"; + this.version = '1.4.0'; + this.id = uuid(); + this.name = undefined; + this.projectId = undefined; + this.request = {}; + this.schedule = {}; + this.set(options); + } + + initOptions(options) { + options = options || {}; + options.request = options.request || new RequestFactory(); + return options; + } + + isValid() { + if (!this.projectId) { + return { + isValid: false, + info: 'api_test.select_project' + } + } else if (!this.name) { + return { + isValid: false, + info: 'api_test.input_name' + } + } + return {isValid: true}; + } + + toJMX() { + return { + name: this.name + '.jmx', + xml: new JMXGenerator(this).toXML() + }; + } +} + +export class Scenario extends BaseConfig { + constructor(options = {}) { + super(); + this.id = undefined; + this.name = undefined; + this.url = undefined; + this.variables = []; + this.headers = []; + this.requests = []; + this.environmentId = undefined; + this.dubboConfig = undefined; + this.environment = undefined; + this.enableCookieShare = false; + this.enable = true; + this.databaseConfigs = []; + this.tcpConfig = undefined; + this.set(options); + this.sets({ + variables: KeyValue, + headers: KeyValue, + requests: RequestFactory, + databaseConfigs: DatabaseConfig + }, options); + } + + initOptions(options = {}) { + options.id = options.id || uuid(); + options.requests = options.requests || [new RequestFactory()]; + options.databaseConfigs = options.databaseConfigs || []; + options.dubboConfig = new DubboConfig(options.dubboConfig); + options.tcpConfig = new TCPConfig(options.tcpConfig); + return options; + } + + clone() { + let clone = new Scenario(this); + clone.id = uuid(); + return clone; + } + + isValid() { + if (this.enable) { + for (let i = 0; i < this.requests.length; i++) { + let validator = this.requests[i].isValid(this.environmentId, this.environment); + if (!validator.isValid) { + return validator; + } + } + } + return {isValid: true}; + } + + isReference() { + return this.id.indexOf("#") !== -1 + } +} + +class DubboConfig extends BaseConfig { + constructor(options = {}) { + super(); + this.configCenter = new ConfigCenter(options.configCenter) + this.registryCenter = new RegistryCenter(options.registryCenter) + if (options.consumerAndService === undefined) { + options.consumerAndService = { + timeout: undefined, + version: undefined, + retries: undefined, + cluster: undefined, + group: undefined, + connections: undefined, + async: undefined, + loadBalance: undefined + } + } + this.consumerAndService = new ConsumerAndService(options.consumerAndService) + } +} + +export class RequestFactory { + static TYPES = { + HTTP: "HTTP", + DUBBO: "DUBBO", + SQL: "SQL", + TCP: "TCP", + } + + constructor(options = {}) { + options.type = options.type || RequestFactory.TYPES.HTTP + switch (options.type) { + case RequestFactory.TYPES.DUBBO: + return new DubboRequest(options); + case RequestFactory.TYPES.SQL: + return new SqlRequest(options); + case RequestFactory.TYPES.TCP: + return new TCPRequest(options); + default: + return new HttpRequest(options); + } + } +} + +export class ResponseFactory { + static TYPES = { + HTTP: "HTTP", + DUBBO: "DUBBO", + SQL: "SQL", + TCP: "TCP", + } + + constructor(options = {}) { + options.type = options.type || ResponseFactory.TYPES.HTTP + switch (options.type) { + case RequestFactory.TYPES.DUBBO: + return new DubboRequest(options); + case RequestFactory.TYPES.SQL: + return new SqlRequest(options); + case RequestFactory.TYPES.TCP: + return new TCPRequest(options); + default: + return new HttpResponse(options); + } + } +} + +export class Request extends BaseConfig { + constructor(type, options = {}) { + super(); + this.type = type; + this.id = options.id || uuid(); + this.name = options.name; + this.enable = options.enable === undefined ? true : options.enable; + this.assertions = new Assertions(options.assertions); + this.extract = new Extract(options.extract); + this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor); + this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor); + this.timer = new ConstantTimer(options.timer); + this.controller = new IfController(options.controller); + } + + showType() { + return this.type; + } + + showMethod() { + return ""; + } +} + +export class HttpRequest extends Request { + constructor(options) { + super(RequestFactory.TYPES.HTTP, options); + this.url = options.url; + this.path = options.path; + this.method = options.method || "GET"; + this.parameters = []; + this.rest = []; + this.authConfig = {verification: "No Auth", isEncrypt: false}; + this.headers = []; + this.body = new Body(options.body); + this.environment = options.environment; + this.useEnvironment = options.useEnvironment; + this.debugReport = undefined; + this.doMultipartPost = options.doMultipartPost; + this.connectTimeout = options.connectTimeout || 10 * 1000; + this.responseTimeout = options.responseTimeout || 10 * 1000; + this.followRedirects = options.followRedirects === undefined ? true : options.followRedirects; + + this.sets({parameters: KeyValue, rest: KeyValue, headers: KeyValue}, options); + } + + isValid(environmentId, environment) { + if (this.enable) { + if (this.useEnvironment) { + if (!environmentId) { + return { + isValid: false, + info: 'api_test.request.please_configure_environment_in_scenario' + } + } + if (!environment.config.httpConfig.socket) { + return { + isValid: false, + info: 'api_test.request.please_configure_socket_in_environment' + } + } + } else { + if (!this.url) { + return { + isValid: false, + info: 'api_test.request.input_url' + } + } + try { + new URL(this.url) + } catch (e) { + return { + isValid: false, + info: 'api_test.request.url_invalid' + } + } + } + } + return { + isValid: true + } + } + + showType() { + return this.type; + } + + showMethod() { + return this.method.toUpperCase(); + } + +} + + +export class Response extends BaseConfig { + constructor(type, options = {}) { + super(); + this.type = type; + this.id = options.id || uuid(); + this.name = options.name; + this.enable = options.enable === undefined ? true : options.enable; + this.assertions = new Assertions(options.assertions); + this.extract = new Extract(options.extract); + this.jsr223PreProcessor = new JSR223Processor(options.jsr223PreProcessor); + this.jsr223PostProcessor = new JSR223Processor(options.jsr223PostProcessor); + } +} + + +export class HttpResponse extends Response { + constructor(options) { + super(ResponseFactory.TYPES.HTTP, options); + this.headers = []; + this.body = new Body(options.body); + this.statusCode = []; + this.sets({statusCode: KeyValue, headers: KeyValue}, options); + } +} + +export class DubboRequest extends Request { + static PROTOCOLS = { + DUBBO: "dubbo://", + RMI: "rmi://", + } + + constructor(options = {}) { + super(RequestFactory.TYPES.DUBBO, options); + this.protocol = options.protocol || DubboRequest.PROTOCOLS.DUBBO; + this.interface = options.interface; + this.method = options.method; + this.configCenter = new ConfigCenter(options.configCenter); + this.registryCenter = new RegistryCenter(options.registryCenter); + this.consumerAndService = new ConsumerAndService(options.consumerAndService); + this.args = []; + this.attachmentArgs = []; + // Scenario.dubboConfig + this.dubboConfig = undefined; + this.debugReport = undefined; + + this.sets({args: KeyValue, attachmentArgs: KeyValue}, options); + } + + isValid() { + if (this.enable) { + if (!this.interface) { + return { + isValid: false, + info: 'api_test.request.dubbo.input_interface' + } + } + if (!this.method) { + return { + isValid: false, + info: 'api_test.request.dubbo.input_method' + } + } + if (!this.registryCenter.isValid()) { + return { + isValid: false, + info: 'api_test.request.dubbo.input_registry_center' + } + } + if (!this.consumerAndService.isValid()) { + return { + isValid: false, + info: 'api_test.request.dubbo.input_consumer_service' + } + } + } + return { + isValid: true + } + } + + showType() { + return "RPC"; + } + + showMethod() { + // dubbo:// -> DUBBO + return this.protocol.substr(0, this.protocol.length - 3).toUpperCase(); + } + + clone() { + return new DubboRequest(this); + } +} + +export class SqlRequest extends Request { + + constructor(options = {}) { + super(RequestFactory.TYPES.SQL, options); + this.useEnvironment = options.useEnvironment; + this.resultVariable = options.resultVariable; + this.variableNames = options.variableNames; + this.variables = []; + this.debugReport = undefined; + this.dataSource = options.dataSource; + this.query = options.query; + // this.queryType = options.queryType; + this.queryTimeout = options.queryTimeout || 60000; + + this.sets({args: KeyValue, attachmentArgs: KeyValue, variables: KeyValue}, options); + } + + isValid() { + if (this.enable) { + if (!this.name) { + return { + isValid: false, + info: 'api_test.request.sql.name_cannot_be_empty' + } + } + if (!this.dataSource) { + return { + isValid: false, + info: 'api_test.request.sql.dataSource_cannot_be_empty' + } + } + } + return { + isValid: true + } + } + + showType() { + return "SQL"; + } + + showMethod() { + return "SQL"; + } + + clone() { + return new SqlRequest(this); + } +} + +export class TCPConfig extends BaseConfig { + static CLASSES = ["TCPClientImpl", "BinaryTCPClientImpl", "LengthPrefixedBinaryTCPClientImpl"] + + constructor(options = {}) { + super(); + this.classname = options.classname || TCPConfig.CLASSES[0]; + this.server = options.server; + this.port = options.port; + this.ctimeout = options.ctimeout; // Connect + this.timeout = options.timeout; // Response + + this.reUseConnection = options.reUseConnection === undefined ? true : options.reUseConnection; + this.nodelay = options.nodelay === undefined ? false : options.nodelay; + this.closeConnection = options.closeConnection === undefined ? false : options.closeConnection; + this.soLinger = options.soLinger; + this.eolByte = options.eolByte; + + this.username = options.username; + this.password = options.password; + } +} + +export class TCPRequest extends Request { + constructor(options = {}) { + super(RequestFactory.TYPES.TCP, options); + this.useEnvironment = options.useEnvironment; + this.debugReport = undefined; + + //设置TCPConfig的属性 + this.set(new TCPConfig(options)); + + this.request = options.request; + } + + isValid() { + return { + isValid: true + } + } + + showType() { + return "TCP"; + } + + showMethod() { + return "TCP"; + } + + clone() { + return new TCPRequest(this); + } +} + + +export class ConfigCenter extends BaseConfig { + static PROTOCOLS = ["zookeeper", "nacos", "apollo"]; + + constructor(options) { + super(); + this.protocol = undefined; + this.group = undefined; + this.namespace = undefined; + this.username = undefined; + this.address = undefined; + this.password = undefined; + this.timeout = undefined; + + this.set(options); + } + + isValid() { + return !!this.protocol || !!this.group || !!this.namespace || !!this.username || !!this.address || !!this.password || !!this.timeout; + } +} + +export class DatabaseConfig extends BaseConfig { + static DRIVER_CLASS = ["com.mysql.jdbc.Driver", "com.microsoft.sqlserver.jdbc.SQLServerDriver", "org.postgresql.Driver", "oracle.jdbc.OracleDriver"]; + + constructor(options) { + super(); + this.id = undefined; + this.name = undefined; + this.poolMax = undefined; + this.timeout = undefined; + this.driver = undefined; + this.dbUrl = undefined; + this.username = undefined; + this.password = undefined; + + this.set(options); + } + + initOptions(options = {}) { + // options.id = options.id || uuid(); + return options; + } + + isValid() { + return !!this.name || !!this.poolMax || !!this.timeout || !!this.driver || !!this.dbUrl || !!this.username || !!this.password; + } +} + +export class RegistryCenter extends BaseConfig { + static PROTOCOLS = ["none", "zookeeper", "nacos", "apollo", "multicast", "redis", "simple"]; + + constructor(options) { + super(); + this.protocol = undefined; + this.group = undefined; + this.username = undefined; + this.address = undefined; + this.password = undefined; + this.timeout = undefined; + + this.set(options); + } + + isValid() { + return !!this.protocol || !!this.group || !!this.username || !!this.address || !!this.password || !!this.timeout; + } +} + +export class ConsumerAndService extends BaseConfig { + static ASYNC_OPTIONS = ["sync", "async"]; + static LOAD_BALANCE_OPTIONS = ["random", "roundrobin", "leastactive", "consistenthash"]; + + constructor(options) { + super(); + this.timeout = "1000"; + this.version = "1.0"; + this.retries = "0"; + this.cluster = "failfast"; + this.group = undefined; + this.connections = "100"; + this.async = "sync"; + this.loadBalance = "random"; + + this.set(options); + } + + isValid() { + return !!this.timeout || !!this.version || !!this.retries || !!this.cluster || !!this.group || !!this.connections || !!this.async || !!this.loadBalance; + } +} + +export class Body extends BaseConfig { + constructor(options) { + super(); + this.type = "KeyValue"; + this.raw = undefined; + this.kvs = []; + this.fromUrlencoded = []; + this.binary = []; + this.xml = undefined; + this.json = undefined; + this.set(options); + this.sets({kvs: KeyValue}, {fromUrlencoded: KeyValue}, {binary: KeyValue}, options); + } + + isValid() { + if (this.isKV()) { + return this.kvs.some(kv => { + return kv.isValid(); + }) + } else { + return !!this.raw; + } + } + + isKV() { + return this.type === BODY_TYPE.KV; + } +} + +export class KeyValue extends BaseConfig { + constructor(options) { + options = options || {}; + options.enable = options.enable === undefined ? true : options.enable; + + super(); + this.name = undefined; + this.value = undefined; + this.type = undefined; + this.files = undefined; + this.enable = undefined; + this.uuid = undefined; + this.contentType = undefined; + this.set(options); + } + + isValid() { + return (!!this.name || !!this.value) && this.type !== 'file'; + } + + isFile() { + return (!!this.name || !!this.value) && this.type === 'file'; + } +} + +export class Assertions extends BaseConfig { + constructor(options) { + super(); + this.type = "Assertions"; + this.text = []; + this.regex = []; + this.jsonPath = []; + this.jsr223 = []; + this.xpath2 = []; + this.duration = undefined; + + this.set(options); + this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options); + } + + initOptions(options) { + options = options || {}; + options.duration = new Duration(options.duration); + return options; + } +} + +export class AssertionType extends BaseConfig { + constructor(type) { + super(); + this.type = type; + } +} + +export class AssertionJSR223 extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.JSR223); + this.variable = undefined; + this.operator = undefined; + this.value = undefined; + this.desc = undefined; + + this.name = undefined; + this.script = undefined; + this.language = "beanshell"; + this.set(options); + } + + isValid() { + return !!this.script && !!this.language; + } +} + +export class Text extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.TEXT); + this.subject = undefined; + this.condition = undefined; + this.value = undefined; + + this.set(options); + } +} + + +export class BeanShellProcessor extends BaseConfig { + constructor(options) { + super(); + this.script = undefined; + this.set(options); + } +} + + +export class JSR223Processor extends BaseConfig { + constructor(options) { + super(); + this.script = undefined; + this.language = "beanshell"; + this.set(options); + } +} + +export class Regex extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.REGEX); + this.subject = undefined; + this.expression = undefined; + this.description = undefined; + this.assumeSuccess = false; + + this.set(options); + } + + isValid() { + return !!this.subject && !!this.expression; + } +} + +export class JSONPath extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.JSON_PATH); + this.expression = undefined; + this.expect = undefined; + this.description = undefined; + + this.set(options); + } + + setJSONPathDescription() { + this.description = this.expression + " expect: " + (this.expect ? this.expect : ''); + } + + isValid() { + return !!this.expression; + } +} + +export class XPath2 extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.XPATH2); + this.expression = undefined; + this.description = undefined; + this.set(options); + } + + isValid() { + return !!this.expression; + } +} + + +export class Duration extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.DURATION); + this.value = undefined; + + this.set(options); + } + + isValid() { + return !!this.value; + } +} + +export class Extract extends BaseConfig { + constructor(options) { + super(); + this.type = "Extract"; + this.regex = []; + this.json = []; + this.xpath = []; + + this.set(options); + let types = { + json: ExtractJSONPath, + xpath: ExtractXPath, + regex: ExtractRegex + } + this.sets(types, options); + } +} + +export class ExtractType extends BaseConfig { + constructor(type) { + super(); + this.type = type; + } +} + +export class ExtractCommon extends ExtractType { + constructor(type, options) { + super(type); + this.variable = undefined; + this.useHeaders = undefined; + this.value = ""; // ${variable} + this.expression = undefined; + this.description = undefined; + this.multipleMatching = undefined; + + this.set(options); + } + + isValid() { + return !!this.variable && !!this.expression; + } +} + +export class ExtractRegex extends ExtractCommon { + constructor(options) { + super(EXTRACT_TYPE.REGEX, options); + } +} + +export class ExtractJSONPath extends ExtractCommon { + constructor(options) { + super(EXTRACT_TYPE.JSON_PATH, options); + } +} + +export class ExtractXPath extends ExtractCommon { + constructor(options) { + super(EXTRACT_TYPE.XPATH, options); + } +} + +export class Controller extends BaseConfig { + static TYPES = { + IF_CONTROLLER: "If Controller", + } + + constructor(type, options = {}) { + super(); + this.type = type + options.id = options.id || uuid(); + options.enable = options.enable === undefined ? true : options.enable; + } +} + +export class IfController extends Controller { + constructor(options = {}) { + super(Controller.TYPES.IF_CONTROLLER, options); + this.variable; + this.operator; + this.value; + + this.set(options); + } + + isValid() { + if (!!this.operator && this.operator.indexOf("empty") > 0) { + return !!this.variable && !!this.operator; + } + return !!this.variable && !!this.operator && !!this.value; + } + + label() { + if (this.isValid()) { + let label = this.variable; + if (this.operator) label += " " + this.operator; + if (this.value) label += " " + this.value; + return label; + } + return ""; + } +} + +export class Timer extends BaseConfig { + static TYPES = { + CONSTANT_TIMER: "Constant Timer", + } + + constructor(type, options = {}) { + super(); + this.type = type; + options.id = options.id || uuid(); + options.enable = options.enable === undefined ? true : options.enable; + } +} + +export class ConstantTimer extends Timer { + constructor(options = {}) { + super(Timer.TYPES.CONSTANT_TIMER, options); + this.delay; + + this.set(options); + } + + isValid() { + return this.delay > 0; + } + + label() { + if (this.isValid()) { + return this.delay + " ms"; + } + return ""; + } +} + +/** ------------------------------------------------------------------------ **/ +const JMX_ASSERTION_CONDITION = { + MATCH: 1, + CONTAINS: 1 << 1, + NOT: 1 << 2, + EQUALS: 1 << 3, + SUBSTRING: 1 << 4, + OR: 1 << 5 +} + +class JMXHttpRequest { + constructor(request, environment) { + if (request && request instanceof HttpRequest) { + this.useEnvironment = request.useEnvironment; + this.method = request.method; + if (!request.useEnvironment) { + if (!request.url.startsWith("http://") && !request.url.startsWith("https://")) { + request.url = 'http://' + request.url; + } + let url = new URL(request.url); + this.domain = decodeURIComponent(url.hostname); + this.port = url.port; + this.protocol = url.protocol.split(":")[0]; + this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname)); + } else { + this.domain = environment.config.httpConfig.domain; + this.port = environment.config.httpConfig.port; + this.protocol = environment.config.httpConfig.protocol; + let url = new URL(environment.config.httpConfig.protocol + "://" + environment.config.httpConfig.socket); + this.path = this.getPostQueryParameters(request, decodeURIComponent(url.pathname + (request.path ? request.path : ''))); + } + this.connectTimeout = request.connectTimeout; + this.responseTimeout = request.responseTimeout; + this.followRedirects = request.followRedirects; + this.doMultipartPost = request.doMultipartPost; + } + } + + getPostQueryParameters(request, path) { + if (this.method.toUpperCase() !== "GET") { + let parameters = []; + request.parameters.forEach(parameter => { + if (parameter.name && parameter.value && parameter.enable === true) { + parameters.push(parameter); + } + }); + if (parameters.length > 0) { + path += '?'; + } + for (let i = 0; i < parameters.length; i++) { + let parameter = parameters[i]; + path += (parameter.name + '=' + parameter.value); + if (i !== parameters.length - 1) { + path += '&'; + } + } + } + return path; + } +} + +class JMXDubboRequest { + constructor(request) { + // Request 复制 + let obj = request.clone(); + // 去掉无效的kv + obj.args = obj.args.filter(arg => { + return arg.isValid(); + }); + obj.attachmentArgs = obj.attachmentArgs.filter(arg => { + return arg.isValid(); + }); + return obj; + } + + copy(target, source) { + for (let key in source) { + if (source.hasOwnProperty(key)) { + if (source[key] !== undefined && !target[key]) { + target[key] = source[key]; + } + } + } + } +} + +class JMXTCPRequest { + constructor(request) { + let obj = request.clone(); + if (request.useEnvironment) { + return obj; + } + return obj; + } + + copy(target, source) { + for (let key in source) { + if (source.hasOwnProperty(key)) { + if (source[key] !== undefined && !target[key]) { + target[key] = source[key]; + } + } + } + } +} + +class JMeterTestPlan extends Element { + constructor() { + super('jmeterTestPlan', { + version: "1.2", properties: "5.0", jmeter: "5.2.1" + }); + + this.add(new HashTree()); + } + + put(te) { + if (te instanceof TestElement) { + this.elements[0].add(te); + } + } +} + +class JMXGenerator { + constructor(test) { + if (!test || !test.id || !(test instanceof Test)) return undefined; + + let testPlan = new TestPlan(test.name); + this.addScenarios(testPlan, test.id, test.request); + + this.jmeterTestPlan = new JMeterTestPlan(); + this.jmeterTestPlan.put(testPlan); + } + + addScenarios(testPlan, testId, request) { + + let threadGroup = new ThreadGroup(request.name || ""); + + if (!request.isValid()) return; + let sampler; + if (request instanceof DubboRequest) { + sampler = new DubboSample(request.name || "", new JMXDubboRequest(request)); + } else if (request instanceof HttpRequest) { + sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, false)); + this.addRequestHeader(sampler, request); + this.addRequestArguments(sampler, request); + this.addRequestBody(sampler, request, testId); + } else if (request instanceof SqlRequest) { + sampler = new JDBCSampler(request.name || "", request); + this.addRequestVariables(sampler, request); + } else if (request instanceof TCPRequest) { + sampler = new TCPSampler(request.name || "", new JMXTCPRequest(request)); + } + + this.addDNSCacheManager(sampler, false, request.useEnvironment); + + this.addRequestExtractor(sampler, request); + + this.addRequestAssertion(sampler, request); + + this.addJSR223PreProcessor(sampler, request); + + this.addConstantsTimer(sampler, request); + + if (request.controller && request.controller.isValid() && request.controller.enable) { + if (request.controller instanceof IfController) { + let controller = this.getController(sampler, request); + threadGroup.put(controller); + } + } else { + threadGroup.put(sampler); + } + testPlan.put(threadGroup); + } + + addEnvironments(environments, target) { + let keys = new Set(); + target.forEach(item => { + keys.add(item.name); + }); + let envArray = environments; + if (!(envArray instanceof Array)) { + envArray = JSON.parse(environments); + } + envArray.forEach(item => { + if (item.name && !keys.has(item.name)) { + target.push(new KeyValue({name: item.name, value: item.value})); + } + }) + } + + addScenarioVariables(threadGroup, scenario) { + if (scenario.environment) { + let config = scenario.environment.config; + if (!(scenario.environment.config instanceof Object)) { + config = JSON.parse(scenario.environment.config); + } + this.addEnvironments(config.commonConfig.variables, scenario.variables) + } + let args = this.filterKV(scenario.variables); + if (args.length > 0) { + let name = scenario.name + " Variables"; + threadGroup.put(new Arguments(name, args)); + } + } + + addRequestVariables(httpSamplerProxy, request) { + let name = request.name + " Variables"; + let variables = this.filterKV(request.variables); + if (variables && variables.length > 0) { + httpSamplerProxy.put(new Arguments(name, variables)); + } + } + + addScenarioCookieManager(threadGroup, scenario) { + if (scenario.enableCookieShare) { + threadGroup.put(new CookieManager(scenario.name)); + } + } + + addDNSCacheManager(httpSamplerProxy, environment, useEnv) { + if (environment && useEnv === true) { + let commonConfig = environment.config.commonConfig; + let hosts = commonConfig.hosts; + if (commonConfig.enableHost && hosts.length > 0) { + let name = " DNSCacheManager"; + // 强化判断,如果未匹配到合适的host则不开启DNSCache + let domain = environment.config.httpConfig.domain; + let validHosts = []; + hosts.forEach(item => { + if (item.domain !== undefined && domain !== undefined) { + let d = item.domain.trim().replace("http://", "").replace("https://", ""); + if (d === domain.trim()) { + item.domain = d; // 域名去掉协议 + validHosts.push(item); + } + } + }); + if (validHosts.length > 0) { + httpSamplerProxy.put(new DNSCacheManager(name, validHosts)); + } + } + } + } + + addJDBCDataSources(threadGroup, scenario) { + let names = new Set(); + let databaseConfigMap = new Map(); + scenario.databaseConfigs.forEach(config => { + let name = config.name + "JDBCDataSource"; + threadGroup.put(new JDBCDataSource(name, config)); + names.add(name); + databaseConfigMap.set(config.id, config.name); + }); + if (scenario.environment) { + let config = scenario.environment.config; + if (!(scenario.environment.config instanceof Object)) { + config = JSON.parse(scenario.environment.config); + } + config.databaseConfigs.forEach(config => { + if (!names.has(config.name)) { + let name = config.name + "JDBCDataSource"; + threadGroup.put(new JDBCDataSource(name, config)); + databaseConfigMap.set(config.id, config.name); + } + }); + } + scenario.databaseConfigMap = databaseConfigMap; + } + + addScenarioHeaders(threadGroup, scenario) { + if (scenario.environment) { + let config = scenario.environment.config; + if (!(scenario.environment.config instanceof Object)) { + config = JSON.parse(scenario.environment.config); + } + this.addEnvironments(config.httpConfig.headers, scenario.headers) + } + let headers = this.filterKV(scenario.headers); + if (headers.length > 0) { + let name = scenario.name + " Headers"; + threadGroup.put(new HeaderManager(name, headers)); + } + } + + addRequestHeader(httpSamplerProxy, request) { + let name = request.name + " Headers"; + this.addBodyFormat(request); + let headers = this.filterKV(request.headers); + if (headers.length > 0) { + httpSamplerProxy.put(new HeaderManager(name, headers)); + } + } + + addJSR223PreProcessor(sampler, request) { + let name = request.name; + if (request.jsr223PreProcessor && request.jsr223PreProcessor.script) { + sampler.put(new JSR223PreProcessor(name, request.jsr223PreProcessor)); + } + if (request.jsr223PostProcessor && request.jsr223PostProcessor.script) { + sampler.put(new JSR223PostProcessor(name, request.jsr223PostProcessor)); + } + } + + addConstantsTimer(sampler, request) { + if (request.timer && request.timer.isValid() && request.timer.enable) { + sampler.put(new JMXConstantTimer(request.timer.label(), request.timer)); + } + } + + getController(sampler, request) { + if (request.controller.isValid() && request.controller.enable) { + if (request.controller instanceof IfController) { + let name = request.controller.label(); + let variable = "\"" + request.controller.variable + "\""; + let operator = request.controller.operator; + let value = "\"" + request.controller.value + "\""; + + if (operator === "=~" || operator === "!~") { + value = "\".*" + request.controller.value + ".*\""; + } + + if (operator === "is empty") { + variable = "empty(" + variable + ")"; + operator = ""; + value = ""; + } + + if (operator === "is not empty") { + variable = "!empty(" + variable + ")"; + operator = ""; + value = ""; + } + + let condition = "${__jexl3(" + variable + operator + value + ")}"; + let controller = new JMXIfController(name, {condition: condition}); + controller.put(sampler); + return controller; + } + } + } + + addBodyFormat(request) { + let bodyFormat = request.body.format; + if (!request.body.isKV() && bodyFormat) { + switch (bodyFormat) { + case BODY_FORMAT.JSON: + this.addContentType(request, 'application/json'); + break; + case BODY_FORMAT.HTML: + this.addContentType(request, 'text/html'); + break; + case BODY_FORMAT.XML: + this.addContentType(request, 'text/xml'); + break; + default: + break; + } + } + } + + addContentType(request, type) { + for (let index in request.headers) { + if (request.headers.hasOwnProperty(index)) { + if (request.headers[index].name === 'Content-Type') { + request.headers.splice(index, 1); + break; + } + } + } + request.headers.push(new KeyValue({name: 'Content-Type', value: type})); + } + + addRequestArguments(httpSamplerProxy, request) { + let args = this.filterKV(request.parameters); + if (args.length > 0) { + httpSamplerProxy.add(new HTTPSamplerArguments(args)); + } + } + + addRequestBody(httpSamplerProxy, request, testId) { + let body = []; + if (request.body.isKV()) { + body = this.filterKV(request.body.kvs); + this.addRequestBodyFile(httpSamplerProxy, request, testId); + } else { + httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true); + body.push({name: '', value: request.body.raw, encode: false, enable: true}); + } + + if (request.method !== 'GET') { + httpSamplerProxy.add(new HTTPSamplerArguments(body)); + } + } + + addRequestBodyFile(httpSamplerProxy, request, testId) { + let files = []; + let kvs = this.filterKVFile(request.body.kvs); + kvs.forEach(kv => { + if ((kv.enable !== false) && kv.files) { + kv.files.forEach(file => { + let arg = {}; + arg.name = kv.name; + arg.value = BODY_FILE_DIR + '/' + testId + '/' + file.id + '_' + file.name; + files.push(arg); + }); + } + }); + httpSamplerProxy.add(new HTTPsamplerFiles(files)); + } + + addRequestAssertion(httpSamplerProxy, request) { + let assertions = request.assertions; + if (assertions.regex.length > 0) { + assertions.regex.filter(this.filter).forEach(regex => { + httpSamplerProxy.put(this.getResponseAssertion(regex)); + }) + } + + if (assertions.jsonPath.length > 0) { + assertions.jsonPath.filter(this.filter).forEach(item => { + httpSamplerProxy.put(this.getJSONPathAssertion(item)); + }) + } + + if (assertions.duration.isValid()) { + let name = "Response In Time: " + assertions.duration.value + httpSamplerProxy.put(new DurationAssertion(name, assertions.duration.value)); + } + } + + getJSONPathAssertion(jsonPath) { + let name = jsonPath.description; + return new JSONPathAssertion(name, jsonPath); + } + + getResponseAssertion(regex) { + let name = regex.description; + let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match,自己写正则 + let value = regex.expression; + let assumeSuccess = regex.assumeSuccess; + switch (regex.subject) { + case ASSERTION_REGEX_SUBJECT.RESPONSE_CODE: + return new ResponseCodeAssertion(name, type, value, assumeSuccess); + case ASSERTION_REGEX_SUBJECT.RESPONSE_DATA: + return new ResponseDataAssertion(name, type, value, assumeSuccess); + case ASSERTION_REGEX_SUBJECT.RESPONSE_HEADERS: + return new ResponseHeadersAssertion(name, type, value, assumeSuccess); + } + } + + addRequestExtractor(httpSamplerProxy, request) { + let extract = request.extract; + if (extract.regex.length > 0) { + extract.regex.filter(this.filter).forEach(regex => { + httpSamplerProxy.put(this.getExtractor(regex)); + }) + } + + if (extract.json.length > 0) { + extract.json.filter(this.filter).forEach(json => { + httpSamplerProxy.put(this.getExtractor(json)); + }) + } + + if (extract.xpath.length > 0) { + extract.xpath.filter(this.filter).forEach(xpath => { + httpSamplerProxy.put(this.getExtractor(xpath)); + }) + } + } + + getExtractor(extractCommon) { + let props = { + name: extractCommon.variable, + expression: extractCommon.expression, + match: extractCommon.multipleMatching ? -1 : undefined + } + let testName = props.name + switch (extractCommon.type) { + case EXTRACT_TYPE.REGEX: + testName += " RegexExtractor"; + props.headers = extractCommon.useHeaders; // 对应jMeter body + props.template = "$1$"; + return new RegexExtractor(testName, props); + case EXTRACT_TYPE.JSON_PATH: + testName += " JSONExtractor"; + return new JSONPostProcessor(testName, props); + case EXTRACT_TYPE.XPATH: + testName += " XPath2Evaluator"; + return new XPath2Extractor(testName, props); + } + } + + filter(config) { + return config.isValid(); + } + + filterKV(kvs) { + return kvs.filter(this.filter); + } + + filterKVFile(kvs) { + return kvs.filter(kv => { + return kv.isFile(); + }); + } + + toXML() { + let xml = '\n'; + xml += this.jmeterTestPlan.toXML(); + return xml; + } +} + + diff --git a/frontend/src/business/components/api/definition/model/EnvironmentModel.js b/frontend/src/business/components/api/definition/model/EnvironmentModel.js new file mode 100644 index 0000000000..2be5d57666 --- /dev/null +++ b/frontend/src/business/components/api/definition/model/EnvironmentModel.js @@ -0,0 +1,127 @@ +import {BaseConfig, DatabaseConfig, KeyValue} from "./ApiTestModel"; +import {TCPConfig} from "@/business/components/api/test/model/ScenarioModel"; + +export class Environment extends BaseConfig { + constructor(options = {}) { + + super(); + + this.projectId = undefined; + this.name = undefined; + this.id = undefined; + this.config = undefined; + + this.set(options); + this.sets({}, options); + } + + initOptions(options = {}) { + this.config = new Config(options.config); + return options; + } +} + +export class Config extends BaseConfig { + constructor(options = {}) { + super(); + this.commonConfig = undefined; + this.httpConfig = undefined; + this.databaseConfigs = []; + this.tcpConfig = undefined; + + this.set(options); + this.sets({databaseConfigs: DatabaseConfig}, options); + } + + initOptions(options = {}) { + this.commonConfig = new CommonConfig(options.commonConfig); + this.httpConfig = new HttpConfig(options.httpConfig); + options.databaseConfigs = options.databaseConfigs || []; + options.tcpConfig = new TCPConfig(options.tcpConfig); + return options; + } +} + +export class CommonConfig extends BaseConfig { + constructor(options = {}) { + super(); + this.variables = []; + this.enableHost = false; + this.hosts = []; + + this.set(options); + this.sets({variables: KeyValue, hosts: Host}, options); + } + + initOptions(options = {}) { + options.variables = options.variables || [new KeyValue()]; + options.hosts = options.hosts || []; + return options; + } +} + +export class HttpConfig extends BaseConfig { + constructor(options = {}) { + super(); + + this.socket = undefined; + this.domain = undefined; + this.headers = []; + this.protocol = 'https'; + this.port = undefined; + + this.set(options); + this.sets({headers: KeyValue}, options); + } + + initOptions(options = {}) { + options.headers = options.headers || [new KeyValue()]; + return options; + } +} + +export class Host extends BaseConfig { + constructor(options = {}) { + super(); + + this.ip = undefined; + this.domain = undefined; + this.status = undefined; + this.annotation = undefined; + this.uuid = undefined; + + this.set(options); + } +} + + +/* ---------- Functions ------- */ + +export function compatibleWithEnvironment(environment) { + //兼容旧版本 + if (!environment.config) { + let config = new Config(); + if (!(environment.variables instanceof Array)) { + config.commonConfig.variables = JSON.parse(environment.variables); + } + if (environment.hosts && !(environment.hosts instanceof Array)) { + config.commonConfig.hosts = JSON.parse(environment.hosts); + config.commonConfig.enableHost = true; + } + if (!(environment.headers instanceof Array)) { + config.httpConfig.headers = JSON.parse(environment.headers); + } + config.httpConfig.port = environment.port; + config.httpConfig.protocol = environment.protocol; + config.httpConfig.domain = environment.domain; + config.httpConfig.socket = environment.socket; + environment.config = JSON.stringify(config); + } +} + +export function parseEnvironment(environment) { + compatibleWithEnvironment(environment); + if (!(environment.config instanceof Config)) { + environment.config = new Config(JSON.parse(environment.config)); + } +} diff --git a/frontend/src/business/components/api/definition/model/JMX.js b/frontend/src/business/components/api/definition/model/JMX.js new file mode 100644 index 0000000000..3a10643de3 --- /dev/null +++ b/frontend/src/business/components/api/definition/model/JMX.js @@ -0,0 +1,679 @@ +const INDENT = ' '; // 缩进2空格 + +export class Element { + constructor(name, attributes, value) { + this.indent = ''; + this.name = name; // 标签名 + this.attributes = attributes || {}; // 属性 + this.value = undefined; // 基础类型的内容 + this.elements = []; // 子节点 + + if (value instanceof Element) { + this.elements.push(value); + } else { + this.value = value; + } + } + + set(value) { + this.elements = []; + this.value = value; + } + + add(element) { + if (element instanceof Element) { + this.value = undefined; + this.elements.push(element); + return element; + } + } + + getDefault(value, defaultValue) { + return value === undefined ? defaultValue : value; + } + + commonValue(tag, name, value, defaultValue) { + let v = this.getDefault(value, defaultValue); + return this.add(new Element(tag, {name: name}, v)); + } + + boolProp(name, value, defaultValue) { + return this.commonValue('boolProp', name, value, defaultValue); + } + + intProp(name, value, defaultValue) { + return this.commonValue('intProp', name, value, defaultValue); + } + + longProp(name, value, defaultValue) { + return this.commonValue('longProp', name, value, defaultValue); + } + + stringProp(name, value, defaultValue) { + return this.commonValue('stringProp', name, value, defaultValue); + } + + collectionProp(name) { + return this.commonValue('collectionProp', name); + } + + elementProp(name, elementType) { + return this.add(new Element('elementProp', {name: name, elementType: elementType})); + } + + isEmptyValue() { + return this.value === undefined || this.value === ''; + } + + isEmptyElement() { + return this.elements.length === 0; + } + + isEmpty() { + return this.isEmptyValue() && this.isEmptyElement(); + } + + replace(str) { + if (!str || !(typeof str === 'string')) return str; + return str.replace(/&/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """); + } + + toXML(indent) { + if (indent) { + this.indent = indent; + } + + let str = this.start(); + str += this.content(); + str += this.end(); + return str; + } + + start() { + let str = this.indent + '<' + this.replace(this.name); + for (let key in this.attributes) { + if (this.attributes.hasOwnProperty(key)) { + str += ' ' + this.replace(key) + '="' + this.replace(this.attributes[key]) + '"'; + } + } + if (this.isEmpty()) { + str += '/>'; + } else { + str += '>'; + } + return str; + } + + content() { + if (!this.isEmptyValue()) { + return this.replace(this.value); + } + + let str = ''; + let parent = this; + if (this.elements.length > 0) { + str += '\n'; + this.elements.forEach(e => { + e.indent += parent.indent + INDENT; + str += e.toXML(); + }); + } + return str; + } + + end() { + if (this.isEmpty()) { + return '\n'; + } + let str = '\n'; + if (!this.isEmptyValue()) { + return str; + } + if (!this.isEmptyElement()) { + return this.indent + str; + } + } +} + +// HashTree, 只能添加TestElement的子元素,没有基础类型内容 +export class HashTree extends Element { + constructor() { + super('hashTree'); + } + + add(te) { + if (te instanceof TestElement) { + super.add(te); + } + } +} + +// TestElement包含2部分,Element 和 HashTree +export class TestElement extends Element { + constructor(name, attributes, value) { + // Element, 只能添加Element + super(name, attributes, value); + // HashTree, 只能添加TestElement + this.hashTree = new HashTree(); + } + + put(te) { + this.hashTree.add(te); + } + + toXML() { + let str = super.toXML(); + str += this.hashTree.toXML(this.indent); + return str; + } +} + +export class DefaultTestElement extends TestElement { + constructor(tag, guiclass, testclass, testname, enabled) { + super(tag, { + guiclass: guiclass, + testclass: testclass, + testname: testname === undefined ? tag + ' Name' : testname, + enabled: enabled || true + }); + } +} + +export class TestPlan extends DefaultTestElement { + constructor(testName, props) { + super('TestPlan', 'TestPlanGui', 'TestPlan', testName); + + props = props || {}; + this.boolProp("TestPlan.functional_mode", props.mode, false); + this.boolProp("TestPlan.serialize_threadgroups", props.stg, true); + this.boolProp("TestPlan.tearDown_on_shutdown", props.tos, true); + this.stringProp("TestPlan.comments", props.comments); + this.stringProp("TestPlan.user_define_classpath", props.classpath); + this.add(new ElementArguments(props.args, "TestPlan.user_defined_variables", "User Defined Variables")); + } +} + +export class ThreadGroup extends DefaultTestElement { + constructor(testName, props) { + super('ThreadGroup', 'ThreadGroupGui', 'ThreadGroup', testName); + + props = props || {}; + this.intProp("ThreadGroup.num_threads", props.threads, 1); + this.intProp("ThreadGroup.ramp_time", props.ramp, 1); + this.longProp("ThreadGroup.delay", props.delay, 0); + this.longProp("ThreadGroup.duration", props.delay, 0); + this.stringProp("ThreadGroup.on_sample_error", props.error, "continue"); + this.boolProp("ThreadGroup.scheduler", props.scheduler, false); + + let loopAttrs = { + name: "ThreadGroup.main_controller", + elementType: "LoopController", + guiclass: "LoopControlPanel", + testclass: "LoopController", + testname: "Loop Controller", + enabled: "true" + }; + let loopProps = props.loopProps || {}; + let loopController = this.add(new Element('elementProp', loopAttrs)); + loopController.boolProp('LoopController.continue_forever', loopProps.continue, false); + loopController.stringProp('LoopController.loops', loopProps.loops, 1); + } +} + +export class DubboSample extends DefaultTestElement { + constructor(testName, request = {}) { + super('io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample', + 'io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui', + 'io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample', testName); + this.request = request; + + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_PROTOCOL", this.request.configCenter.protocol); + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_GROUP", this.request.configCenter.group); + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_NAMESPACE", this.request.configCenter.namespace); + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_USER_NAME", this.request.configCenter.username); + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_PASSWORD", this.request.configCenter.password); + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_ADDRESS", this.request.configCenter.address); + this.stringProp("FIELD_DUBBO_CONFIG_CENTER_TIMEOUT", this.request.configCenter.timeout); + + this.stringProp("FIELD_DUBBO_REGISTRY_PROTOCOL", this.request.registryCenter.protocol); + this.stringProp("FIELD_DUBBO_REGISTRY_GROUP", this.request.registryCenter.group); + this.stringProp("FIELD_DUBBO_REGISTRY_USER_NAME", this.request.registryCenter.username); + this.stringProp("FIELD_DUBBO_REGISTRY_PASSWORD", this.request.registryCenter.password); + this.stringProp("FIELD_DUBBO_ADDRESS", this.request.registryCenter.address); + this.stringProp("FIELD_DUBBO_REGISTRY_TIMEOUT", this.request.registryCenter.timeout); + + this.stringProp("FIELD_DUBBO_TIMEOUT", this.request.consumerAndService.timeout); + this.stringProp("FIELD_DUBBO_VERSION", this.request.consumerAndService.version); + this.stringProp("FIELD_DUBBO_RETRIES", this.request.consumerAndService.retries); + this.stringProp("FIELD_DUBBO_GROUP", this.request.consumerAndService.group); + this.stringProp("FIELD_DUBBO_CONNECTIONS", this.request.consumerAndService.connections); + this.stringProp("FIELD_DUBBO_LOADBALANCE", this.request.consumerAndService.loadBalance); + this.stringProp("FIELD_DUBBO_ASYNC", this.request.consumerAndService.async); + this.stringProp("FIELD_DUBBO_CLUSTER", this.request.consumerAndService.cluster); + + this.stringProp("FIELD_DUBBO_RPC_PROTOCOL", this.request.protocol); + this.stringProp("FIELD_DUBBO_INTERFACE", this.request.interface); + this.stringProp("FIELD_DUBBO_METHOD", this.request.method); + + this.intProp("FIELD_DUBBO_METHOD_ARGS_SIZE", this.request.args.length); + this.intProp("FIELD_DUBBO_ATTACHMENT_ARGS_SIZE", this.request.attachmentArgs.length); + this.request.args.forEach((arg, i) => { + if (!!arg.name || !!arg.value) { + let index = i + 1; + this.stringProp("FIELD_DUBBO_METHOD_ARGS_PARAM_TYPE" + index, arg.name); + this.stringProp("FIELD_DUBBO_METHOD_ARGS_PARAM_VALUE" + index, arg.value); + } + }) + this.request.attachmentArgs.forEach((arg, i) => { + if (!!arg.name || !!arg.value) { + let index = i + 1; + this.stringProp("FIELD_DUBBO_ATTACHMENT_ARGS_KEY" + index, arg.name); + this.stringProp("FIELD_DUBBO_ATTACHMENT_ARGS_VALUE" + index, arg.value); + } + }) + } +} + +export class JDBCSampler extends DefaultTestElement { + constructor(testName, request = {}) { + super('JDBCSampler', 'TestBeanGUI', 'JDBCSampler', testName); + + this.stringProp("dataSource", request.dataSource); + this.stringProp("query", request.query); + this.stringProp("queryTimeout", request.queryTimeout); + this.stringProp("resultVariable", request.resultVariable); + this.stringProp("variableNames", request.variableNames); + this.stringProp("queryArguments"); + this.stringProp("queryArgumentsTypes"); + this.stringProp("resultSetMaxRows"); + this.stringProp("resultSetHandler", 'Store as String'); + this.stringProp("queryType", 'Callable Statement'); + } +} + +export class TCPSampler extends DefaultTestElement { + constructor(testName, request = {}) { + super('TCPSampler', 'TCPSamplerGui', 'TCPSampler', testName); + + this.stringProp("TCPSampler.classname", request.classname); + this.stringProp("TCPSampler.server", request.server); + this.stringProp("TCPSampler.port", request.port); + this.stringProp("TCPSampler.ctimeout", request.ctimeout); + this.stringProp("TCPSampler.timeout", request.timeout); + this.boolProp("TCPSampler.reUseConnection", request.reUseConnection); + this.boolProp("TCPSampler.nodelay", request.nodelay); + this.boolProp("TCPSampler.closeConnection", request.closeConnection); + this.stringProp("TCPSampler.soLinger", request.soLinger); + this.stringProp("TCPSampler.EolByte", request.eolByte); + this.stringProp("TCPSampler.request", request.request); + this.stringProp("ConfigTestElement.username", request.username); + this.stringProp("ConfigTestElement.password", request.password); + } +} + +export class HTTPSamplerProxy extends DefaultTestElement { + constructor(testName, options = {}) { + super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName); + + this.stringProp("HTTPSampler.domain", options.domain); + this.stringProp("HTTPSampler.protocol", options.protocol); + this.stringProp("HTTPSampler.path", options.path); + + this.stringProp("HTTPSampler.method", options.method); + this.stringProp("HTTPSampler.contentEncoding", options.encoding, "UTF-8"); + if (!options.port) { + this.stringProp("HTTPSampler.port", ""); + } else { + this.stringProp("HTTPSampler.port", options.port); + } + if (options.connectTimeout) { + this.stringProp('HTTPSampler.connect_timeout', options.connectTimeout); + } + if (options.responseTimeout) { + this.stringProp('HTTPSampler.response_timeout', options.responseTimeout); + } + if (options.followRedirects) { + this.boolProp('HTTPSampler.follow_redirects', options.followRedirects, true); + } + + this.boolProp("HTTPSampler.use_keepalive", options.keepalive, true); + this.boolProp("HTTPSampler.DO_MULTIPART_POST", options.doMultipartPost, false); + } +} + +// 这是一个Element +export class HTTPSamplerArguments extends Element { + constructor(args) { + super('elementProp', { + name: "HTTPsampler.Arguments", // s必须小写 + elementType: "Arguments", + guiclass: "HTTPArgumentsPanel", + testclass: "Arguments", + enabled: "true" + }); + + this.args = args || []; + + let collectionProp = this.collectionProp('Arguments.arguments'); + this.args.forEach(arg => { + if (arg.enable === true || arg.enable === undefined) { // 非禁用的条件加入执行 + let elementProp = collectionProp.elementProp(arg.name, 'HTTPArgument'); + elementProp.boolProp('HTTPArgument.always_encode', arg.encode, true); + elementProp.boolProp('HTTPArgument.use_equals', arg.equals, true); + if (arg.name) { + elementProp.stringProp('Argument.name', arg.name); + } + elementProp.stringProp('Argument.value', arg.value); + elementProp.stringProp('Argument.metadata', arg.metadata || "="); + if (arg.contentType) { + elementProp.stringProp('HTTPArgument.content_type', arg.contentType, ""); + } + } + }); + } +} + +export class HTTPsamplerFiles extends Element { + constructor(args) { + super('elementProp', { + name: "HTTPsampler.Files", + elementType: "HTTPFileArgs", + }); + + this.args = args || {}; + + let collectionProp = this.collectionProp('HTTPFileArgs.files'); + this.args.forEach(arg => { + let elementProp = collectionProp.elementProp(arg.value, 'HTTPFileArg'); + elementProp.stringProp('File.path', arg.value); + elementProp.stringProp('File.paramname', arg.name); + elementProp.stringProp('File.mimetype', arg.contentType || "application/octet-stream"); + }); + } +} + +export class CookieManager extends DefaultTestElement { + constructor(testName) { + super('CookieManager', 'CookiePanel', 'CookieManager', testName); + this.collectionProp('CookieManager.cookies'); + this.boolProp('CookieManager.clearEachIteration', false, false); + this.boolProp('CookieManager.controlledByThreadGroup', false, false); + } +} + +export class DurationAssertion extends DefaultTestElement { + constructor(testName, duration) { + super('DurationAssertion', 'DurationAssertionGui', 'DurationAssertion', testName); + this.duration = duration || 0; + this.stringProp('DurationAssertion.duration', this.duration); + } +} + +export class ResponseAssertion extends DefaultTestElement { + constructor(testName, assertion) { + super('ResponseAssertion', 'AssertionGui', 'ResponseAssertion', testName); + this.assertion = assertion || {}; + + this.stringProp('Assertion.test_field', this.assertion.field); + this.boolProp('Assertion.assume_success', this.assertion.assumeSuccess); + this.intProp('Assertion.test_type', this.assertion.type); + this.stringProp('Assertion.custom_message', this.assertion.message); + + let collectionProp = this.collectionProp('Asserion.test_strings'); + let random = Math.floor(Math.random() * 10000); + collectionProp.stringProp(random, this.assertion.value); + } +} + +export class JSONPathAssertion extends DefaultTestElement { + constructor(testName, jsonPath) { + super('JSONPathAssertion', 'JSONPathAssertionGui', 'JSONPathAssertion', testName); + this.jsonPath = jsonPath || {}; + + this.stringProp('JSON_PATH', this.jsonPath.expression); + this.stringProp('EXPECTED_VALUE', this.jsonPath.expect); + this.boolProp('JSONVALIDATION', true); + this.boolProp('EXPECT_NULL', false); + this.boolProp('INVERT', false); + this.boolProp('ISREGEX', true); + } +} + +export class ResponseCodeAssertion extends ResponseAssertion { + constructor(testName, type, value, assumeSuccess, message) { + let assertion = { + field: 'Assertion.response_code', + type: type, + value: value, + assumeSuccess: assumeSuccess, + message: message, + } + super(testName, assertion) + } +} + +export class ResponseDataAssertion extends ResponseAssertion { + constructor(testName, type, value, assumeSuccess, message) { + let assertion = { + field: 'Assertion.response_data', + type: type, + value: value, + assumeSuccess: assumeSuccess, + message: message, + } + super(testName, assertion) + } +} + +export class ResponseHeadersAssertion extends ResponseAssertion { + constructor(testName, type, value, assumeSuccess, message) { + let assertion = { + field: 'Assertion.response_headers', + type: type, + value: value, + assumeSuccess: assumeSuccess, + message: message, + } + super(testName, assertion) + } +} + +export class BeanShellProcessor extends DefaultTestElement { + constructor(tag, guiclass, testclass, testname, processor) { + super(tag, guiclass, testclass, testname); + this.processor = processor || {}; + this.boolProp('resetInterpreter', false); + this.stringProp('parameters'); + this.stringProp('filename'); + this.stringProp('script', processor.script); + } +} + +export class JSR223Processor extends DefaultTestElement { + constructor(tag, guiclass, testclass, testname, processor) { + super(tag, guiclass, testclass, testname); + this.processor = processor || {}; + this.stringProp('cacheKey', 'true'); + this.stringProp('filename'); + this.stringProp('parameters'); + this.stringProp('script', this.processor.script); + this.stringProp('scriptLanguage', this.processor.language); + } +} + +export class JSR223PreProcessor extends JSR223Processor { + constructor(testName, processor) { + super('JSR223PreProcessor', 'TestBeanGUI', 'JSR223PreProcessor', testName, processor) + } +} + +export class JSR223PostProcessor extends JSR223Processor { + constructor(testName, processor) { + super('JSR223PostProcessor', 'TestBeanGUI', 'JSR223PostProcessor', testName, processor) + } +} + +export class BeanShellPreProcessor extends BeanShellProcessor { + constructor(testName, processor) { + super('BeanShellPreProcessor', 'TestBeanGUI', 'BeanShellPreProcessor', testName, processor) + } +} + +export class BeanShellPostProcessor extends BeanShellProcessor { + constructor(testName, processor) { + super('BeanShellPostProcessor', 'TestBeanGUI', 'BeanShellPostProcessor', testName, processor) + } +} + +export class IfController extends DefaultTestElement { + constructor(testName, controller = {}) { + super('IfController', 'IfControllerPanel', 'IfController', testName); + + this.stringProp('IfController.comments', controller.comments); + this.stringProp('IfController.condition', controller.condition); + this.boolProp('IfController.evaluateAll', controller.evaluateAll, false); + this.boolProp('IfController.useExpression', controller.useExpression, true); + } +} + +export class ConstantTimer extends DefaultTestElement { + constructor(testName, timer = {}) { + super('ConstantTimer', 'ConstantTimerGui', 'ConstantTimer', testName); + + this.stringProp('ConstantTimer.delay', timer.delay); + } +} + +export class HeaderManager extends DefaultTestElement { + constructor(testName, headers) { + super('HeaderManager', 'HeaderPanel', 'HeaderManager', testName); + this.headers = headers || []; + + let collectionProp = this.collectionProp('HeaderManager.headers'); + this.headers.forEach(header => { + if (header.enable === true || header.enable === undefined) { + let elementProp = collectionProp.elementProp('', 'Header'); + elementProp.stringProp('Header.name', header.name); + elementProp.stringProp('Header.value', header.value); + } + }); + } +} + +export class DNSCacheManager extends DefaultTestElement { + constructor(testName, hosts) { + super('DNSCacheManager', 'DNSCachePanel', 'DNSCacheManager', testName); + let collectionPropServers = this.collectionProp('DNSCacheManager.servers'); + let collectionPropHosts = this.collectionProp('DNSCacheManager.hosts'); + + hosts.forEach(host => { + let elementProp = collectionPropHosts.elementProp(host.domain, 'StaticHost'); + elementProp.stringProp('StaticHost.Name', host.domain); + elementProp.stringProp('StaticHost.Address', host.ip); + }); + + let boolProp = this.boolProp('DNSCacheManager.isCustomResolver', true); + } +} + +export class JDBCDataSource extends DefaultTestElement { + constructor(testName, datasource) { + super('JDBCDataSource', 'TestBeanGUI', 'JDBCDataSource', testName); + + this.boolProp('autocommit', true); + this.boolProp('keepAlive', true); + this.boolProp('preinit', false); + this.stringProp('dataSource', datasource.name); + this.stringProp('dbUrl', datasource.dbUrl); + this.stringProp('driver', datasource.driver); + this.stringProp('username', datasource.username); + this.stringProp('password', datasource.password); + this.stringProp('poolMax', datasource.poolMax); + this.stringProp('timeout', datasource.timeout); + this.stringProp('connectionAge', '5000'); + this.stringProp('trimInterval', '60000'); + this.stringProp('transactionIsolation', 'DEFAULT'); + this.stringProp('checkQuery'); + this.stringProp('initQuery'); + this.stringProp('connectionProperties'); + } +} + +export class Arguments extends DefaultTestElement { + constructor(testName, args) { + super('Arguments', 'ArgumentsPanel', 'Arguments', testName); + this.args = args || []; + + let collectionProp = this.collectionProp('Arguments.arguments'); + + this.args.forEach(arg => { + if (arg.enable === true || arg.enable === undefined) { // 非禁用的条件加入执行 + let elementProp = collectionProp.elementProp(arg.name, 'Argument'); + elementProp.stringProp('Argument.name', arg.name); + elementProp.stringProp('Argument.value', arg.value); + elementProp.stringProp('Argument.desc', arg.desc); + elementProp.stringProp('Argument.metadata', arg.metadata, "="); + } + }); + } +} + +export class ElementArguments extends Element { + constructor(args, name, testName) { + super('elementProp', { + name: name || "arguments", + elementType: "Arguments", + guiclass: "ArgumentsPanel", + testclass: "Arguments", + testname: testName || "", + enabled: "true" + }); + + let collectionProp = this.collectionProp('Arguments.arguments'); + if (args) { + args.forEach(arg => { + if (arg.enable === true || arg.enable === undefined) { // 非禁用的条件加入执行 + let elementProp = collectionProp.elementProp(arg.name, 'Argument'); + elementProp.stringProp('Argument.name', arg.name); + elementProp.stringProp('Argument.value', arg.value); + elementProp.stringProp('Argument.metadata', arg.metadata, "="); + } + }); + } + } +} + +export class RegexExtractor extends DefaultTestElement { + constructor(testName, props) { + super('RegexExtractor', 'RegexExtractorGui', 'RegexExtractor', testName); + this.props = props || {} + this.stringProp('RegexExtractor.useHeaders', props.headers); + this.stringProp('RegexExtractor.refname', props.name); + this.stringProp('RegexExtractor.regex', props.expression); + this.stringProp('RegexExtractor.template', props.template); + this.stringProp('RegexExtractor.default', props.default); + this.stringProp('RegexExtractor.match_number', props.match); + } +} + +export class JSONPostProcessor extends DefaultTestElement { + constructor(testName, props) { + super('JSONPostProcessor', 'JSONPostProcessorGui', 'JSONPostProcessor', testName); + this.props = props || {} + this.stringProp('JSONPostProcessor.referenceNames', props.name); + this.stringProp('JSONPostProcessor.jsonPathExprs', props.expression); + this.stringProp('JSONPostProcessor.match_numbers', props.match); + } +} + +export class XPath2Extractor extends DefaultTestElement { + constructor(testName, props) { + super('XPath2Extractor', 'XPath2ExtractorGui', 'XPath2Extractor', testName); + this.props = props || {} + this.stringProp('XPathExtractor2.default', props.default); + this.stringProp('XPathExtractor2.refname', props.name); + this.stringProp('XPathExtractor2.xpathQuery', props.expression); + this.stringProp('XPathExtractor2.namespaces', props.namespaces); + this.stringProp('XPathExtractor2.matchNumber', props.match); + } +} diff --git a/frontend/src/business/components/api/definition/model/JsonData.js b/frontend/src/business/components/api/definition/model/JsonData.js new file mode 100644 index 0000000000..60c3d8b8fb --- /dev/null +++ b/frontend/src/business/components/api/definition/model/JsonData.js @@ -0,0 +1,55 @@ +/* 用例等级 */ +export const PRIORITY = [ + {name: 'P0', id: 'P0'}, + {name: 'P1', id: 'P1'}, + {name: 'P2', id: 'P2'}, + {name: 'P3', id: 'P3'} +] + +export const OPTIONS = [ + {value: 'HTTP', name: 'HTTP'}, + {value: 'TCP', name: 'TCP'}, + {value: 'SQL', name: 'SQL'}, + {value: 'DUBBO', name: 'DUBBO'} +] + +export const DEFAULT_DATA = [{ + "id": "gc", + "name": "回收站", + "level": 1, + "children": [], +},{ + "id": "root", + "name": "默认模块", + "level": 0, + "children": [], +}] + +export const REQ_METHOD = [ + {id: 'GET', label: 'GET'}, + {id: 'POST', label: 'POST'} +] + +export const API_STATUS = [ + {id: 'Prepare', label: '未开始'}, + {id: 'Underway', label: '进行中'}, + {id: 'Completed', label: '已完成'} +] + +export const API_METHOD_COLOUR = [ + ['GET', "#61AFFE"], ['POST', '#49CC90'], ['PUT', '#fca130'], + ['PATCH', '#E2EE11'], ['DELETE', '#f93e3d'], ['OPTIONS', '#0EF5DA'], + ['HEAD', '#8E58E7'], ['CONNECT', '#90AFAE'], + ['DUBBO', '#C36EEF'], ['dubbo://', '#C36EEF'], ['SQL', '#0AEAD4'], ['TCP', '#0A52DF'], +] + +export const REQUIRED = [ + {name: '必填', id: true}, + {name: '非必填', id: false} +] + +export const RESULT_MAP = new Map([ + ['success', '执行结果:通过'], + ['error', '执行结果:未通过'], + ['default', '执行结果:未执行'] +]); diff --git a/frontend/src/business/components/api/head/ApiHeaderMenus.vue b/frontend/src/business/components/api/head/ApiHeaderMenus.vue index 5e1e6f1d1f..cd1817adef 100644 --- a/frontend/src/business/components/api/head/ApiHeaderMenus.vue +++ b/frontend/src/business/components/api/head/ApiHeaderMenus.vue @@ -7,6 +7,10 @@ {{ $t("i18n.home") }} + + {{ $t("i18n.definition") }} + + diff --git a/frontend/src/business/components/api/report/components/ResponseText.vue b/frontend/src/business/components/api/report/components/ResponseText.vue index f1f64f40fb..d960e179f8 100644 --- a/frontend/src/business/components/api/report/components/ResponseText.vue +++ b/frontend/src/business/components/api/report/components/ResponseText.vue @@ -7,8 +7,8 @@ - - + +
{{ response.headers }}
diff --git a/frontend/src/business/components/api/router.js b/frontend/src/business/components/api/router.js index 672d9faf7e..56021a803f 100644 --- a/frontend/src/business/components/api/router.js +++ b/frontend/src/business/components/api/router.js @@ -38,6 +38,11 @@ export default { path: "report/view/:reportId", name: "ApiReportView", component: () => import('@/business/components/api/report/ApiReportView'), + }, + { + path: "definition", + name: "ApiDefinition", + component: () => import('@/business/components/api/definition/ApiDefinition'), } ] } diff --git a/frontend/src/business/components/common/components/MsDialogFooter.vue b/frontend/src/business/components/common/components/MsDialogFooter.vue index 261a49422f..8f86fd508b 100644 --- a/frontend/src/business/components/common/components/MsDialogFooter.vue +++ b/frontend/src/business/components/common/components/MsDialogFooter.vue @@ -1,24 +1,35 @@ diff --git a/frontend/src/business/components/common/components/MsTag.vue b/frontend/src/business/components/common/components/MsTag.vue index e6fb0d871f..4daf579d5e 100644 --- a/frontend/src/business/components/common/components/MsTag.vue +++ b/frontend/src/business/components/common/components/MsTag.vue @@ -1,5 +1,5 @@