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 62fcf926be..22879f8491 100644 --- a/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java +++ b/backend/src/main/java/io/metersphere/api/parse/Swagger2Parser.java @@ -290,7 +290,9 @@ public class Swagger2Parser extends SwaggerAbstractParser { } refSet.add(simpleRef); Model model = definitions.get(simpleRef); - jsonObject.put(key, getBodyParameters(model.getProperties(), refSet)); + if (model != null) { + jsonObject.put(key, getBodyParameters(model.getProperties(), refSet)); + } } else { jsonObject.put(key, getDefaultValueByPropertyType(value)); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtBaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtBaseMapper.java new file mode 100644 index 0000000000..d2e2d1e39e --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtBaseMapper.java @@ -0,0 +1,4 @@ +package io.metersphere.base.mapper.ext; + +public interface ExtBaseMapper { +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtBaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtBaseMapper.xml new file mode 100644 index 0000000000..a3ac8dc6e9 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtBaseMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + order by + + ${order.name} ${order.type} + + + + + + + + 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} + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java index 8c15824c62..bfe5e65b55 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java @@ -1,6 +1,7 @@ package io.metersphere.base.mapper.ext; import io.metersphere.base.domain.TestCase; +import io.metersphere.controller.request.BaseQueryRequest; import io.metersphere.track.dto.TestCaseDTO; import io.metersphere.track.request.testcase.QueryTestCaseRequest; import io.metersphere.track.request.testcase.TestCaseBatchRequest; @@ -46,4 +47,5 @@ public interface ExtTestCaseMapper { */ int checkIsHave(@Param("caseId") String caseId, @Param("workspaceIds") Set workspaceIds); + List selectIds(@Param("request") BaseQueryRequest condition); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml index 730baeb9d8..a933f2d1ce 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml @@ -2,102 +2,58 @@ - - - - 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} - - - - and test_case.name - + and test_case.node_path - + and test_case.priority - + and test_case.create_time - + and test_case.type - + and test_case.update_time - + and test_case.method - + and test_case.maintainer - + and test_case.tags - + @@ -129,32 +85,7 @@ #{nodeId} - - - - - - and test_case.priority in - - #{value} - - - - and test_case.review_status in - - #{value} - - - - and test_case.type in - - #{value} - - - - - - + ORDER BY test_case.update_time DESC @@ -184,32 +115,7 @@ #{nodeId} - - - - - - and test_case.priority in - - #{value} - - - - and test_case.review_status in - - #{value} - - - - and test_case.type in - - #{value} - - - - - - + ORDER BY test_case.update_time DESC @@ -236,32 +142,7 @@ #{nodeId} - - - - - - and test_case.priority in - - #{value} - - - - and test_case.review_status in - - #{value} - - - - and test_case.type in - - #{value} - - - - - - + ORDER BY test_case.update_time DESC @@ -277,66 +158,8 @@ from test_case - - - - - - - - - and (test_case.name like CONCAT('%', #{request.name},'%') - or test_case.num like CONCAT('%', #{request.name},'%') - or test_case.tags like CONCAT('%', #{request.name},'%')) - - - and test_case.node_id in - - #{nodeId} - - - - and test_case.project_id = #{request.projectId} - - - - - - - and test_case.priority in - - #{value} - - - - and test_case.type in - - #{value} - - - - and test_case.review_status in - - #{value} - - - - and test_case.method in - - #{value} - - - - - - - - - order by - - ${order.name} ${order.type} - - + + + + + + + + + + + + and test_case.priority in + + #{value} + + + + and test_case.type in + + #{value} + + + + and test_case.review_status in + + #{value} + + + + and test_case.method in + + #{value} + + + + + + + + + + + + + + + + + + and (test_case.name like CONCAT('%', #{request.name},'%') + or test_case.num like CONCAT('%', #{request.name},'%') + or test_case.tags like CONCAT('%', #{request.name},'%')) + + + and test_case.node_id in + + #{nodeId} + + + + and test_case.project_id = #{request.projectId} + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java index 566956a5d8..9bf213370e 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java @@ -1,9 +1,12 @@ package io.metersphere.commons.utils; +import io.metersphere.controller.request.BaseQueryRequest; import io.metersphere.controller.request.OrderRequest; import java.util.ArrayList; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; public class ServiceUtils { @@ -18,4 +21,22 @@ public class ServiceUtils { } return orders; } + + /** + * 获取前端全选的id列表 + * @param queryRequest 查询条件 + * @param func 查询id列表的数据库查询 + * @return + */ + public static void getSelectAllIds(BaseQueryRequest queryRequest, Function> func) { + if (queryRequest.isSelectAll()) { + List ids = func.apply(queryRequest); + if (!ids.isEmpty()) { + ids = ids.stream() + .filter(id -> !queryRequest.getUnSelectIds().contains(id)) + .collect(Collectors.toList()); + } + queryRequest.setIds(ids); + } + } } diff --git a/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java b/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java new file mode 100644 index 0000000000..b9a2297a59 --- /dev/null +++ b/backend/src/main/java/io/metersphere/controller/request/BaseQueryRequest.java @@ -0,0 +1,55 @@ +package io.metersphere.controller.request; + +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +public class BaseQueryRequest { + + private String projectId; + + private String name; + + private String workspaceId; + + private List ids; + + private List moduleIds; + + private List nodeIds; + + /** + * 是否选中所有数据 + */ + private boolean selectAll; + + /** + * 全选之后取消选中的id + */ + private List unSelectIds; + + /** + * 排序条件 + */ + private List orders; + + /** + * 过滤条件 + */ + private Map> filters; + + /** + * 高级搜索 + + */ + private Map combine; + + /** + * 要查询的字段 + */ + private List selectFields; +} diff --git a/backend/src/main/java/io/metersphere/service/NodeResourcePoolService.java b/backend/src/main/java/io/metersphere/service/NodeResourcePoolService.java index b22c00f9c2..fc16e3c70c 100644 --- a/backend/src/main/java/io/metersphere/service/NodeResourcePoolService.java +++ b/backend/src/main/java/io/metersphere/service/NodeResourcePoolService.java @@ -46,9 +46,22 @@ public class NodeResourcePoolService { }) .distinct() .collect(Collectors.toList()); - if (nodeIps.size() < testResourcePool.getResources().size()) { + List nodePorts = testResourcePool.getResources().stream() + .map(resource -> { + NodeDTO nodeDTO = JSON.parseObject(resource.getConfiguration(), NodeDTO.class); + return nodeDTO.getPort(); + }) + .distinct() + .collect(Collectors.toList()); + if (nodeIps.size() < testResourcePool.getResources().size() && nodePorts.size() < testResourcePool.getResources().size()) { + MSException.throwException(Translator.get("duplicate_node_ip_port")); + } + else if (nodeIps.size() < testResourcePool.getResources().size()) { MSException.throwException(Translator.get("duplicate_node_ip")); } + else if (nodePorts.size() < testResourcePool.getResources().size()) { + MSException.throwException(Translator.get("duplicate_node_port")); + } testResourcePool.setStatus(VALID.name()); boolean isValid = true; for (TestResource resource : testResourcePool.getResources()) { diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java index 2328e5de13..e0b265d662 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/QueryTestCaseRequest.java @@ -1,6 +1,7 @@ package io.metersphere.track.request.testcase; import io.metersphere.base.domain.TestCase; +import io.metersphere.controller.request.BaseQueryRequest; import io.metersphere.controller.request.OrderRequest; import lombok.Getter; import lombok.Setter; @@ -10,27 +11,15 @@ import java.util.Map; @Getter @Setter -public class QueryTestCaseRequest extends TestCase { +public class QueryTestCaseRequest extends BaseQueryRequest { private String name; - private List nodeIds; - private List testCaseIds; - private List orders; - - private Map> filters; - - private List selectFields; - private String planId; - private String workspaceId; - private String userId; - private Map combine; - private String reviewId; } diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/TestCaseBatchRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/TestCaseBatchRequest.java index aa9b8e9871..86c38dcd3e 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/TestCaseBatchRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/TestCaseBatchRequest.java @@ -1,6 +1,7 @@ package io.metersphere.track.request.testcase; import io.metersphere.base.domain.TestCaseWithBLOBs; +import io.metersphere.controller.request.BaseQueryRequest; import io.metersphere.controller.request.OrderRequest; import lombok.Getter; import lombok.Setter; @@ -13,4 +14,6 @@ public class TestCaseBatchRequest extends TestCaseWithBLOBs { private List ids; private List orders; private String projectId; + + private BaseQueryRequest condition; } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 7634e20c4d..58f8bb5b9f 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -528,6 +528,7 @@ public class TestCaseService { public void editTestCaseBath(TestCaseBatchRequest request) { + getSelectAllIds(request); TestCaseExample testCaseExample = new TestCaseExample(); testCaseExample.createCriteria().andIdIn(request.getIds()); @@ -541,12 +542,21 @@ public class TestCaseService { } public void deleteTestCaseBath(TestCaseBatchRequest request) { + getSelectAllIds(request); deleteTestPlanTestCaseBath(request.getIds()); TestCaseExample example = new TestCaseExample(); example.createCriteria().andIdIn(request.getIds()); testCaseMapper.deleteByExample(example); } + public void getSelectAllIds(TestCaseBatchRequest request) { + if (request.getCondition().isSelectAll()) { + ServiceUtils.getSelectAllIds(request.getCondition(), + (query) -> extTestCaseMapper.selectIds(query)); + request.setIds(request.getCondition().getIds()); + } + } + public void deleteTestPlanTestCaseBath(List caseIds) { TestPlanTestCaseExample example = new TestPlanTestCaseExample(); example.createCriteria().andCaseIdIn(caseIds); diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index e4521190f0..647e945828 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit e4521190f0f1be113c8b84fbdcdf8ff273bf274e +Subproject commit 647e945828e9f15de61cf19e726424bec06051f8 diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 63a9f26f1e..3ac43e7a11 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -43,6 +43,8 @@ test_not_running=Test is not running load_test_already_exists=Duplicate load test name no_nodes_message=No node message duplicate_node_ip=Duplicate IPs +duplicate_node_port=Duplicate Ports +duplicate_node_ip_port=Duplicate IPs & Ports max_thread_insufficient=The number of concurrent users exceeds related_case_del_fail_prefix=Connected to related_case_del_fail_suffix=TestCase, please disassociate first diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index d42e0b0162..87c7dd1353 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -43,6 +43,8 @@ test_not_running=测试未运行 load_test_already_exists=测试名称不能重复 no_nodes_message=没有节点信息 duplicate_node_ip=节点 IP 重复 +duplicate_node_port=节点 Port 重复 +duplicate_node_ip_port=节点 IP、Port 重复 max_thread_insufficient=并发用户数超额 related_case_del_fail_prefix=已关联到 related_case_del_fail_suffix=测试用例,请先解除关联 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 5c2666354b..4e9f9ffff4 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -43,6 +43,8 @@ test_not_running=測試未運行 load_test_already_exists=測試名稱不能重復 no_nodes_message=沒有節點信息 duplicate_node_ip=節點 IP 重復 +duplicate_node_port=節點 Port 重復 +duplicate_node_ip_port=節點 IP、Port 重復 max_thread_insufficient=並發用戶數超額 related_case_del_fail_prefix=已關聯到 related_case_del_fail_suffix=測試用例,請先解除關聯 diff --git a/frontend/src/business/components/api/automation/ApiAutomation.vue b/frontend/src/business/components/api/automation/ApiAutomation.vue index f4e322c62a..097ceb5346 100644 --- a/frontend/src/business/components/api/automation/ApiAutomation.vue +++ b/frontend/src/business/components/api/automation/ApiAutomation.vue @@ -35,7 +35,7 @@ closable>
+ :moduleOptions="moduleOptions" ref="autoScenarioConfig"/>
@@ -111,6 +111,16 @@ // 在 DOM 中添加 my-component 组件 this.renderComponent = true; }); + }, + '$route'(to, from) { // 路由改变时,把接口定义界面中的 ctrl s 保存快捷键监听移除 + if (to.path.indexOf('/api/automation') == -1) { + if (this.$refs && this.$refs.autoScenarioConfig) { + console.log(this.$refs.autoScenarioConfig); + this.$refs.autoScenarioConfig.forEach(item => { + item.removeListener(); + }); + } + } } }, methods: { @@ -158,6 +168,20 @@ label = tab.currentScenario.name; this.tabs.push({label: label, name: name, currentScenario: tab.currentScenario}); } + if (this.$refs && this.$refs.autoScenarioConfig) { + this.$refs.autoScenarioConfig.forEach(item => { + item.removeListener(); + }); // 删除所有tab的 ctrl + s 监听 + this.addListener(); + } + }, + addListener() { + let index = this.tabs.findIndex(item => item.name === this.activeName); // 找到当前选中tab的index + if(index != -1) { // 为当前选中的tab添加监听 + this.$nextTick(()=>{ + this.$refs.autoScenarioConfig[index].addListener(); + }); + } }, handleTabClose() { this.tabs = []; @@ -181,6 +205,7 @@ this.tabs = this.tabs.filter(tab => tab.name !== targetName); if (this.tabs.length > 0) { this.activeName = this.tabs[this.tabs.length - 1].name; + this.addListener(); // 自动切换当前标签时,也添加监听 } else { this.activeName = "default" } diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index e80461184f..257b9335ef 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -5,7 +5,7 @@
- {{$t('commons.save')}} + {{$t('commons.save')}}
{{$t('test_track.plan_view.base_info')}}
@@ -222,6 +222,7 @@ import ScenarioApiRelevance from "./api/ApiRelevance"; import ScenarioRelevance from "./api/ScenarioRelevance"; import MsComponentConfig from "./component/ComponentConfig"; + import {handleCtrlSEvent} from "../../../../../common/js/utils"; export default { name: "EditApiScenario", @@ -295,6 +296,7 @@ this.operatingElements = ELEMENTS.get("ALL"); this.getMaintainerOptions(); this.getApiScenario(); + this.addListener(); // 添加 ctrl s 监听 }, directives: {OutsideClick}, computed: { @@ -413,6 +415,17 @@ } }, methods: { + addListener() { + document.addEventListener("keydown", this.createCtrlSHandle); + // document.addEventListener("keydown", (even => handleCtrlSEvent(even, this.$refs.httpApi.saveApi))); + }, + removeListener() { + document.removeEventListener("keydown", this.createCtrlSHandle); + }, + createCtrlSHandle(event) { + console.log("create ctrl + s"); + handleCtrlSEvent(event, this.editScenario); + }, getIdx(index) { return index - 0.33 }, @@ -822,15 +835,13 @@ } return bodyUploadFiles; }, - editScenario(showMessage) { + editScenario() { this.$refs['currentScenario'].validate((valid) => { if (valid) { this.setParameter(); let bodyFiles = this.getBodyUploadFiles(this.currentScenario); this.$fileUpload(this.path, null, bodyFiles, this.currentScenario, response => { - if (showMessage) { - this.$success(this.$t('commons.save_success')); - } + this.$success(this.$t('commons.save_success')); this.path = "/api/automation/update"; if (response.data) { this.currentScenario.id = response.data.id; diff --git a/frontend/src/business/components/api/definition/ApiDefinition.vue b/frontend/src/business/components/api/definition/ApiDefinition.vue index 0421b518eb..9b47c97278 100644 --- a/frontend/src/business/components/api/definition/ApiDefinition.vue +++ b/frontend/src/business/components/api/definition/ApiDefinition.vue @@ -198,6 +198,15 @@ // 在 DOM 中添加 my-component 组件 this.renderComponent = true; }); + }, + '$route'(to, from) { // 路由改变时,把接口定义界面中的 ctrl s 保存快捷键监听移除 + if (to.path.indexOf('/api/definition') == -1) { + if (this.$refs && this.$refs.apiConfig) { + this.$refs.apiConfig.forEach(item => { + item.removeListener(); + }); + } + } } }, methods: { @@ -211,6 +220,17 @@ if (tab.name === 'add') { this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug"); } + if(this.$refs.apiConfig) { + this.$refs.apiConfig.forEach(item => { + console.log(item); + item.removeListener(); + }); // 删除所有tab的 ctrl + s 监听 + let tabs = this.apiTabs; + let index = tabs.findIndex(item => item.name === tab.name); // 找到当前选中tab的index + if(index != -1) { + this.$refs.apiConfig[index - 1].addListener(); // 为选中tab添加 ctrl + s 监听(index-1的原因是要除去第一个固有tab) + } + } }, handleCommand(e) { switch (e) { diff --git a/frontend/src/business/components/api/definition/components/ApiConfig.vue b/frontend/src/business/components/api/definition/components/ApiConfig.vue index 1cf299f8a2..bbd76219c8 100644 --- a/frontend/src/business/components/api/definition/components/ApiConfig.vue +++ b/frontend/src/business/components/api/definition/components/ApiConfig.vue @@ -3,16 +3,16 @@
+ :basisData="currentApi" :moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'HTTP'" ref="httpApi"/> + :moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'TCP'" ref="tcpApi"/> + :moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'DUBBO'" ref="dubboApi"/> + :moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'SQL'" ref="sqlApi"/>
@@ -27,6 +27,7 @@ import {createComponent, Request} from "./jmeter/components"; import Sampler from "./jmeter/components/sampler/sampler"; import {WORKSPACE_ID} from '@/common/js/constants'; + import {handleCtrlSEvent} from "../../../../../common/js/utils"; export default { name: "ApiConfig", @@ -65,8 +66,30 @@ break; } this.formatApi(); + this.addListener(); }, methods: { + addListener() { + document.addEventListener("keydown", this.createCtrlSHandle); + // document.addEventListener("keydown", (even => handleCtrlSEvent(even, this.$refs.httpApi.saveApi))); + }, + removeListener() { + document.removeEventListener("keydown", this.createCtrlSHandle); + }, + createCtrlSHandle(event) { + if(this.$refs.httpApi) { + handleCtrlSEvent(event, this.$refs.httpApi.saveApi); + } + else if(this.$refs.tcpApi) { + handleCtrlSEvent(event, this.$refs.tcpApi.saveApi); + } + else if(this.$refs.dubboApi) { + handleCtrlSEvent(event, this.$refs.dubboApi.saveApi); + } + else if(this.$refs.sqlApi) { + handleCtrlSEvent(event, this.$refs.sqlApi.saveApi); + } + }, runTest(data) { this.setParameters(data); let bodyFiles = this.getBodyUploadFiles(data); diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue index ffecc7cd01..90769131cb 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue @@ -6,7 +6,7 @@
- {{ $t('commons.save') }} + {{ $t('commons.save') }} {{ $t('commons.test') }}
diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue index 6f4415ee65..7cb015595a 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue @@ -6,7 +6,7 @@
- {{ $t('commons.save') }} + {{ $t('commons.save') }} {{ $t('commons.test') }}

diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue index 0b0bb5699e..14df6b2c0e 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue @@ -5,7 +5,7 @@
- {{ $t('commons.save') }} + {{ $t('commons.save') }} {{ $t('commons.test') }}
diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue index 3253522333..682ea8817c 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue @@ -5,7 +5,7 @@
- {{ $t('commons.save') }} + {{ $t('commons.save') }} {{ $t('commons.test') }}
diff --git a/frontend/src/business/components/common/head/HeaderUser.vue b/frontend/src/business/components/common/head/HeaderUser.vue index ce11e27682..c096706f44 100644 --- a/frontend/src/business/components/common/head/HeaderUser.vue +++ b/frontend/src/business/components/common/head/HeaderUser.vue @@ -8,6 +8,7 @@ {{ $t('commons.personal_information') }} {{ $t('commons.about_us') }} {{ $t('commons.help_documentation') }} + {{ $t('commons.api_help_documentation') }} {{ $t('commons.cut_back_old_version') }} @@ -64,6 +65,9 @@ export default { case "help": window.location.href = "https://metersphere.io/docs/index.html"; break; + case "ApiHelp": + window.open('/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config', "_blank"); + break; default: break; } diff --git a/frontend/src/business/components/track/case/TestCase.vue b/frontend/src/business/components/track/case/TestCase.vue index 09cb2a382a..b618625b53 100644 --- a/frontend/src/business/components/track/case/TestCase.vue +++ b/frontend/src/business/components/track/case/TestCase.vue @@ -12,15 +12,15 @@ @@ -28,6 +28,7 @@ - - - @@ -49,12 +47,10 @@ import NodeTree from '../common/NodeTree'; import TestCaseEdit from './components/TestCaseEdit'; import TestCaseList from "./components/TestCaseList"; import SelectMenu from "../common/SelectMenu"; -import TestCaseMove from "./components/TestCaseMove"; import MsContainer from "../../common/components/MsContainer"; import MsAsideContainer from "../../common/components/MsAsideContainer"; import MsMainContainer from "../../common/components/MsMainContainer"; import {checkoutTestManagerOrTestUser, getCurrentProjectID, hasRoles} from "../../../../common/js/utils"; -import BatchMove from "./components/BatchMove"; import TestCaseNodeTree from "../common/TestCaseNodeTree"; import {TrackEvent,LIST_CHANGE} from "@/business/components/common/head/ListEvent"; @@ -63,7 +59,7 @@ export default { components: { TestCaseNodeTree, MsMainContainer, - MsAsideContainer, MsContainer, TestCaseMove, TestCaseList, NodeTree, TestCaseEdit, SelectMenu, BatchMove + MsAsideContainer, MsContainer, TestCaseList, NodeTree, TestCaseEdit, SelectMenu }, comments: {}, data() { @@ -75,7 +71,8 @@ export default { selectParentNodes: [], testCaseReadOnly: true, selectNode: {}, - condition: {} + condition: {}, + moduleOptions: [] } }, mounted() { @@ -153,32 +150,14 @@ export default { this.$refs.testCaseEditDialog.open(); } }, - - moveToNode(selectIds) { - if (selectIds.size < 1) { - this.$warning(this.$t('test_track.plan_view.select_manipulate')); - return; - } - this.$refs.testCaseEditDialog.getModuleOptions(); - this.$refs.testCaseMove.open(this.$refs.testCaseEditDialog.moduleOptions, selectIds); - }, - batchMove(selectIds) { - this.$refs.testBatchMove.open(this.treeNodes, selectIds, this.$refs.testCaseEditDialog.moduleOptions); - }, setTreeNodes(data) { this.treeNodes = data; }, setCondition(data) { this.condition = data; }, - moveSave(param) { - this.result = this.$post('/test/case/batch/edit', param, () => { - this.$success(this.$t('commons.save_success')); - this.$refs.testBatchMove.close(); - // 发送广播,刷新 head 上的最新列表 - TrackEvent.$emit(LIST_CHANGE); - this.refresh(); - }); + setModuleOptions(data) { + this.moduleOptions = data; } } } diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index b517936329..aaa1679c6e 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -379,6 +379,9 @@ export default { treeNodes() { this.getModuleOptions(); }, + moduleOptions() { + this.$emit('setModuleOptions', this.moduleOptions); + } }, methods: { reload() { diff --git a/frontend/src/business/components/track/case/components/TestCaseList.vue b/frontend/src/business/components/track/case/components/TestCaseList.vue index 7d62024dec..d076a1f38b 100644 --- a/frontend/src/business/components/track/case/components/TestCaseList.vue +++ b/frontend/src/business/components/track/case/components/TestCaseList.vue @@ -15,11 +15,6 @@ :content="$t('test_track.case.import.import')" @click="importTestCase"/> - - - - - @@ -33,12 +28,22 @@ @sort-change="sort" @filter-change="filter" @select-all="handleSelectAll" - @select="handleSelectionChange" + @select="handleSelect" @cell-mouse-enter="showPopover" row-key="id" - class="test-content adjust-table"> + class="test-content adjust-table ms-select-all" + ref="table"> + + + + @@ -184,10 +191,15 @@ import TestCaseDetail from "./TestCaseDetail"; import ReviewStatus from "@/business/components/track/case/components/ReviewStatus"; import {getCurrentProjectID} from "../../../../../common/js/utils"; import MsTag from "@/business/components/common/components/MsTag"; +import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll"; +import {_handleSelect, _handleSelectAll} from "../../../../../common/js/tableUtils"; +import BatchMove from "./BatchMove"; export default { name: "TestCaseList", components: { + BatchMove, + MsTableSelectAll, MsTableButton, MsTableOperatorButton, MsTableOperator, @@ -274,7 +286,7 @@ export default { maintainer: [], }, currentCaseId: null, - projectId: "" + projectId: "", } }, props: { @@ -283,6 +295,12 @@ export default { }, selectParentNodes: { type: Array + }, + treeNodes: { + type: Array + }, + moduleOptions: { + type: Array } }, created: function () { @@ -303,6 +321,8 @@ export default { this.projectId = getCurrentProjectID(); this.condition.planId = ""; this.condition.nodeIds = []; + this.condition.selectAll = false; + this.condition.unSelectIds = []; if (this.planId) { // param.planId = this.planId; this.condition.planId = this.planId; @@ -366,8 +386,10 @@ export default { confirmButtonText: this.$t('commons.confirm'), callback: (action) => { if (action === 'confirm') { - let ids = Array.from(this.selectRows).map(row => row.id); - this.$post('/test/case/batch/delete', {ids: ids}, () => { + let param = {}; + param.ids = Array.from(this.selectRows).map(row => row.id); + param.condition = this.condition; + this.$post('/test/case/batch/delete', param, () => { this.selectRows.clear(); this.$emit("refresh"); this.$success(this.$t('commons.delete_success')); @@ -401,26 +423,12 @@ export default { this.$emit('testCaseDetail', row); }, handleSelectAll(selection) { - if (selection.length > 0) { - this.tableData.forEach(item => { - this.$set(item, "showMore", true); - this.selectRows.add(item); - }); - } else { - this.selectRows.clear(); - this.tableData.forEach(row => { - this.$set(row, "showMore", false); - }) - } + _handleSelectAll(this, selection, this.tableData, this.selectRows); + this.setUnSelectIds(); }, - handleSelectionChange(selection, row) { - if (this.selectRows.has(row)) { - this.$set(row, "showMore", false); - this.selectRows.delete(row); - } else { - this.$set(row, "showMore", true); - this.selectRows.add(row); - } + handleSelect(selection, row) { + _handleSelect(this, selection, row, this.selectRows); + this.setUnSelectIds(); }, importTestCase() { if (!getCurrentProjectID()) { @@ -457,7 +465,6 @@ export default { }); }, handleBatch(type) { - if (this.selectRows.size < 1) { if (type === 'export') { this.$alert(this.$t('test_track.case.export_all_cases'), '', { @@ -489,6 +496,7 @@ export default { let param = {}; param[form.type] = form.value; param.ids = ids; + param.condition = this.condition; this.$post('/test/case/batch/edit', param, () => { this.$success(this.$t('commons.save_success')); this.refresh(); @@ -513,7 +521,7 @@ export default { this.$refs.batchEdit.open(); }, handleBatchMove() { - this.$emit("batchMove", Array.from(this.selectRows).map(row => row.id)); + this.$refs.testBatchMove.open(this.treeNodes, Array.from(this.selectRows).map(row => row.id), this.moduleOptions); }, getMaintainerOptions() { let workspaceId = localStorage.getItem(WORKSPACE_ID); @@ -525,6 +533,31 @@ export default { if (column.property === 'name') { this.currentCaseId = row.id; } + }, + isSelectDataAll(data) { + this.condition.selectAll = data; + this.setUnSelectIds(); + //如果已经全选,不需要再操作了 + if (this.selectRows.size != this.tableData.length) { + this.$refs.table.toggleAllSelection(true); + } + }, + setUnSelectIds() { + let ids = Array.from(this.selectRows).map(o => o.id); + let allIDs = this.tableData.map(o => o.id); + this.condition.unSelectIds = allIDs.filter(function (val) { + return ids.indexOf(val) === -1 + }); + }, + moveSave(param) { + param.condition = this.condition; + this.result = this.$post('/test/case/batch/edit', param, () => { + this.$success(this.$t('commons.save_success')); + this.$refs.testBatchMove.close(); + // 发送广播,刷新 head 上的最新列表 + TrackEvent.$emit(LIST_CHANGE); + this.refresh(); + }); } } } diff --git a/frontend/src/common/js/utils.js b/frontend/src/common/js/utils.js index 38de23f333..30602936cc 100644 --- a/frontend/src/common/js/utils.js +++ b/frontend/src/common/js/utils.js @@ -356,3 +356,12 @@ export function _getBodyUploadFiles(request, bodyUploadFiles, obj) { } } } +export function handleCtrlSEvent(event, func) { + if (event.keyCode === 83 && event.ctrlKey) { + // console.log('拦截到 ctrl + s');//ctrl+s + func(); + event.preventDefault(); + event.returnValue = false; + return false; + } +} diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 85d73e703d..cf7154389a 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -8,6 +8,7 @@ export default { comment: '评论', examples: '示例', help_documentation: '帮助文档', + api_help_documentation: 'API文档', delete_cancelled: '已取消删除', workspace: '工作空间', organization: '组织', diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 47ede5346e..9c07b711b2 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -8,6 +8,7 @@ export default { comment: '評論', examples: '示例', help_documentation: '幫助文檔', + api_help_documentation: 'API文檔', delete_cancelled: '已取消刪除', workspace: '工作空間', organization: '組織',