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 index cae25e201f..abbce6821b 100644 --- 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 @@ -84,8 +84,7 @@ public class MsAuthManager extends MsTestElement { authManager.setProperty(TestElement.TEST_CLASS, AuthManager.class.getName()); authManager.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("AuthPanel")); Authorization auth = new Authorization(); - auth.setURL(samplerProxy.getUrl().toString()); - auth.setDomain(samplerProxy.getDomain()); + auth.setURL(samplerProxy.getProtocol() + "://" + samplerProxy.getDomain()); auth.setUser(msAuthManager.getUsername()); auth.setPass(msAuthManager.getPassword()); auth.setMechanism(AuthManager.Mechanism.DIGEST); diff --git a/backend/src/main/java/io/metersphere/commons/utils/DateUtils.java b/backend/src/main/java/io/metersphere/commons/utils/DateUtils.java index 1728a2b0e5..62c0af9290 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/DateUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/DateUtils.java @@ -45,6 +45,10 @@ public class DateUtils { SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_PATTERN); return dateFormat.format(timeStamp); } + public static String getDataStr(long timeStamp) { + SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_PATTERM); + return dateFormat.format(timeStamp); + } public static Date dateSum (Date date,int countDays){ diff --git a/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java index 9039e564be..f3fcecf316 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestPlanApiCaseController.java @@ -50,4 +50,10 @@ public class TestPlanApiCaseController { testPlanApiCaseService.deleteApiCaseBath(request); } + @PostMapping("/batch/update/env") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public void batchUpdateEnv(@RequestBody TestPlanApiCaseBatchRequest request) { + testPlanApiCaseService.batchUpdateEnv(request); + } + } diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/TestPlanApiCaseBatchRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/TestPlanApiCaseBatchRequest.java index 215e9cecfd..79ac65bda8 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/TestPlanApiCaseBatchRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/TestPlanApiCaseBatchRequest.java @@ -5,9 +5,20 @@ import lombok.Getter; import lombok.Setter; import java.util.List; +import java.util.Map; @Getter @Setter public class TestPlanApiCaseBatchRequest extends TestPlanTestCase { private List ids; + + /** + * 批量修改选中的数据 + */ + private Map selectRows; + + /** + * 项目ID,环境ID对应关系 + */ + private Map projectEnvMap; } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java index 17ae51df69..4f5ae37971 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanApiCaseService.java @@ -12,6 +12,7 @@ import io.metersphere.api.dto.definition.request.MsThreadGroup; import io.metersphere.api.service.ApiDefinitionExecResultService; import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.api.service.ApiTestCaseService; +import io.metersphere.base.domain.ApiTestCaseExample; import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import io.metersphere.base.domain.TestPlanApiCase; import io.metersphere.base.domain.TestPlanApiCaseExample; @@ -27,9 +28,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; +import java.util.*; @Service @Transactional(rollbackFor = Exception.class) @@ -129,4 +128,19 @@ public class TestPlanApiCaseService { request.setIds(extTestPlanApiCaseMapper.getNotRelevanceCaseIds(planId, relevanceProjectIds)); deleteApiCaseBath(request); } + + public void batchUpdateEnv(TestPlanApiCaseBatchRequest request) { + // 批量修改用例环境 + Map rows = request.getSelectRows(); + Set ids = rows.keySet(); + Map env = request.getProjectEnvMap(); + if (env != null && !env.isEmpty()) { + ids.forEach(id -> { + TestPlanApiCase apiCase = new TestPlanApiCase(); + apiCase.setId(id); + apiCase.setEnvironmentId(env.get(rows.get(id))); + testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase); + }); + } + } } diff --git a/frontend/package.json b/frontend/package.json index 9b89c5a842..a63cffc926 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -48,7 +48,8 @@ "vuedraggable": "^2.24.3", "vuex": "^3.1.2", "xml-js": "^1.6.11", - "yan-progress": "^1.0.3" + "yan-progress": "^1.0.3", + "jsonpath": "^1.1.0" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.1.0", diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index ac270ddc2a..57358447bf 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -133,7 +133,7 @@ + @remove="remove" @copyRow="copyRow" @suggestClick="suggestClick" @refReload="refReload"/> @@ -215,8 +215,8 @@ import ScenarioRelevance from "./api/ScenarioRelevance"; import MsComponentConfig from "./component/ComponentConfig"; import {handleCtrlSEvent} from "../../../../../common/js/utils"; - import {getProject} from "@/business/components/api/automation/scenario/event"; import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover"; + let jsonPath = require('jsonpath'); export default { name: "EditApiScenario", props: { @@ -295,12 +295,6 @@ this.getApiScenario(); this.addListener(); // 添加 ctrl s 监听 }, - mounted() { - getProject.$on('addProjectEnv', (projectId, projectEnv) => { - this.projectIds.add(projectId); - // this.projectEnvMap.set(projectId, projectEnv); - }) - }, directives: {OutsideClick}, computed: { buttons() { @@ -397,7 +391,7 @@ }, { title: this.$t('api_test.automation.scenario_import'), - show: this.operatingElements && this.operatingElements.indexOf('scenario') === 0, + show:this.showButton("scenario"), titleColor: "#606266", titleBgColor: "#F4F4F5", icon: "movie", @@ -583,6 +577,7 @@ } this.sort(); this.reload(); + this.initProjectIds(); this.scenarioVisible = false; }, setApiParameter(item, refType, referenced) { @@ -624,6 +619,7 @@ }); this.sort(); this.reload(); + this.initProjectIds(); }, getMaintainerOptions() { let workspaceId = localStorage.getItem(WORKSPACE_ID); @@ -647,15 +643,10 @@ const parent = node.parent const hashTree = parent.data.hashTree || parent.data; const index = hashTree.findIndex(d => d.resourceId != undefined && row.resourceId != undefined && d.resourceId === row.resourceId) - if (hashTree[index] && hashTree[index].projectId) { - this.projectIds.delete(hashTree[index].projectId); - if (this.projectEnvMap.has(hashTree[index].projectId)) { - this.projectEnvMap.delete(hashTree[index].projectId); - } - } hashTree.splice(index, 1); this.sort(); this.reload(); + this.initProjectIds(); } } }); @@ -928,6 +919,7 @@ } this.enableCookieShare = obj.enableCookieShare; this.scenarioDefinition = obj.hashTree; + this.initProjectIds(); } } if (this.currentScenario.copy) { @@ -992,9 +984,6 @@ } return size; }, - beforeDestroy() { - getProject.$off('addProjectEnv'); - }, handleEnv() { this.$refs.apiScenarioEnv.open(); }, @@ -1006,6 +995,18 @@ this.projectList = res.data; }) }, + refReload() { + this.initProjectIds(); + this.reload(); + }, + initProjectIds() { + // 加载环境配置 + this.projectIds.clear(); + this.scenarioDefinition.forEach(data=>{ + let arr = jsonPath.query(data, "$..projectId"); + arr.forEach(a => this.projectIds.add(a)); + }) + } } } diff --git a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue index 93d437382e..281c466aea 100644 --- a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue +++ b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue @@ -62,7 +62,9 @@ export default { // 固定环境列表渲染顺序 let temp = this.data.find(dt => dt.id === id); temp.envs = envs; - temp.selectEnv = this.envMap.get(id); + let envId = this.envMap.get(id); + // 选中环境是否存在 + temp.selectEnv = envs.filter(e =>e.id === envId).length === 0 ? null : envId; }) }) }, diff --git a/frontend/src/business/components/api/automation/scenario/Setting.js b/frontend/src/business/components/api/automation/scenario/Setting.js index b147a83658..9636cf8e03 100644 --- a/frontend/src/business/components/api/automation/scenario/Setting.js +++ b/frontend/src/business/components/api/automation/scenario/Setting.js @@ -6,7 +6,7 @@ export const ELEMENTS = new Map([ ['JDBCSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], ['TCPSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], ['OT_IMPORT', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], - ['IfController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]], + ['IfController', ["IfController","scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]], ['LoopController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]], ['ConstantTimer', []], ['JSR223Processor', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue index 40189f0feb..1f48fe7912 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiComponent.vue @@ -71,7 +71,6 @@ import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiResponseComponent from "./ApiResponseComponent"; import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo"; - import {getProject} from "@/business/components/api/automation/scenario/event"; export default { name: "MsApiComponent", @@ -123,7 +122,6 @@ } } } - getProject.$emit('addProjectEnv', this.request.projectId, this.currentEnvironmentId); }, computed: { displayColor() { diff --git a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue index 9658c5a521..a0b2873f78 100644 --- a/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue +++ b/frontend/src/business/components/api/automation/scenario/component/ApiScenarioComponent.vue @@ -29,7 +29,6 @@ import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters"; import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm"; import ApiBaseComponent from "../common/ApiBaseComponent"; - import {getProject} from "@/business/components/api/automation/scenario/event"; import {getCurrentProjectID} from "@/common/js/utils"; export default { @@ -49,7 +48,6 @@ if (!this.scenario.projectId) { this.scenario.projectId = getCurrentProjectID(); } - getProject.$emit('addProjectEnv', this.scenario.projectId, this.currentEnvironmentId); if (this.scenario.id && this.scenario.referenced === 'REF' && !this.scenario.loaded) { this.result = this.$get("/api/automation/getApiScenario/" + this.scenario.id, response => { if (response.data) { diff --git a/frontend/src/business/components/api/automation/scenario/event.js b/frontend/src/business/components/api/automation/scenario/event.js deleted file mode 100644 index 858344afcd..0000000000 --- a/frontend/src/business/components/api/automation/scenario/event.js +++ /dev/null @@ -1,2 +0,0 @@ -import Vue from 'vue'; -export const getProject = new Vue(); diff --git a/frontend/src/business/components/track/case/components/BatchEdit.vue b/frontend/src/business/components/track/case/components/BatchEdit.vue index 0fd6678255..421330b3ad 100644 --- a/frontend/src/business/components/track/case/components/BatchEdit.vue +++ b/frontend/src/business/components/track/case/components/BatchEdit.vue @@ -14,7 +14,11 @@ - + + + +
@@ -35,11 +39,13 @@ diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue index 98ffc7eb4c..73073a8485 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue @@ -129,6 +129,10 @@ + + +
@@ -145,9 +149,9 @@ import ApiCaseList from "../../../../../api/definition/components/case/ApiCaseLi import MsContainer from "../../../../../common/components/MsContainer"; import MsBottomContainer from "../../../../../api/definition/components/BottomContainer"; import ShowMoreBtn from "../../../../case/components/ShowMoreBtn"; -import MsBatchEdit from "../../../../../api/definition/components/basis/BatchEdit"; +import BatchEdit from "@/business/components/track/case/components/BatchEdit"; import {API_METHOD_COLOUR, CASE_PRIORITY, RESULT_MAP} from "../../../../../api/definition/model/JsonData"; -import {getCurrentProjectID, getCurrentUser} from "@/common/js/utils"; +import {getCurrentProjectID, strMapToObj} from "@/common/js/utils"; import ApiListContainer from "../../../../../api/definition/components/list/ApiListContainer"; import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem"; import {getBodyUploadFiles, getUUID} from "../../../../../../../common/js/utils"; @@ -156,16 +160,17 @@ import MsRun from "../../../../../api/definition/components/Run"; import TestPlanApiCaseResult from "./TestPlanApiCaseResult"; import TestPlan from "../../../../../api/definition/components/jmeter/components/test-plan"; import ThreadGroup from "../../../../../api/definition/components/jmeter/components/thread-group"; -import {TEST_CASE_LIST, TEST_PLAN_API_CASE, WORKSPACE_ID} from "@/common/js/constants"; +import {TEST_PLAN_API_CASE, WORKSPACE_ID} from "@/common/js/constants"; import {_filter, _sort, getLabel} from "@/common/js/tableUtils"; import HeaderCustom from "@/business/components/common/head/HeaderCustom"; -import {Test_Plan_Api_Case, Track_Test_Case} from "@/business/components/common/model/JsonData"; +import {Test_Plan_Api_Case} from "@/business/components/common/model/JsonData"; import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate"; export default { name: "TestPlanApiCaseList", components: { + BatchEdit, HeaderLabelOperate, HeaderCustom, TestPlanApiCaseResult, @@ -182,7 +187,6 @@ export default { MsContainer, MsBottomContainer, ShowMoreBtn, - MsBatchEdit }, data() { return { @@ -198,10 +202,11 @@ export default { selectRows: new Set(), buttons: [ {name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch}, - {name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute} + {name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute}, + {name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit} ], typeArr: [ - {id: 'priority', name: this.$t('test_track.case.priority')}, + {id: 'projectEnv', name: this.$t('api_test.definition.request.run_env')}, ], priorityFilters: [ {text: 'P0', value: 'P0'}, @@ -212,6 +217,7 @@ export default { valueArr: { priority: CASE_PRIORITY, userId: [], + projectEnv: [] }, methodColorMap: new Map(API_METHOD_COLOUR), tableData: [], @@ -225,7 +231,9 @@ export default { reportId: "", response: {}, rowLoading: "", - userFilters: [] + userFilters: [], + projectIds: [], + projectList: [] } }, props: { @@ -342,15 +350,6 @@ export default { this.$set(row, "showMore", true); this.selectRows.add(row); } - let arr = Array.from(this.selectRows); - // 选中1个以上的用例时显示更多操作 - if (this.selectRows.size === 1) { - this.$set(arr[0], "showMore", false); - } else if (this.selectRows.size === 2) { - arr.forEach(row => { - this.$set(row, "showMore", true); - }) - } }, showExecResult(row) { this.$emit('showExecResult', row); @@ -369,16 +368,10 @@ export default { }, handleSelectAll(selection) { if (selection.length > 0) { - if (selection.length === 1) { - selection.hashTree = []; - this.selectRows.add(selection[0]); - } else { - this.tableData.forEach(item => { - item.hashTree = []; - this.$set(item, "showMore", true); - this.selectRows.add(item); - }); - } + this.tableData.forEach(item => { + this.$set(item, "showMore", true); + this.selectRows.add(item); + }); } else { this.selectRows.clear(); this.tableData.forEach(row => { @@ -449,16 +442,27 @@ export default { this.reportId = getUUID().substring(0, 8); }); }, + handleBatchEdit() { + this.$refs.batchEdit.open(this.selectRows.size); + this.$refs.batchEdit.setSelectRows(this.selectRows); + }, batchEdit(form) { - let arr = Array.from(this.selectRows); - let ids = arr.map(row => row.id); let param = {}; - param[form.type] = form.value; - param.ids = ids; - this.$post('/api/testcase/batch/edit', param, () => { - this.$success(this.$t('commons.save_success')); - this.initTable(); - }); + // 批量修改环境 + if (form.type === 'projectEnv') { + let map = new Map(); + param.projectEnvMap = strMapToObj(form.projectEnvMap); + this.selectRows.forEach(row => { + map[row.id] = row.projectId; + }) + param.selectRows = map; + this.$post('/test/plan/api/case/batch/update/env', param, () => { + this.$success(this.$t('commons.save_success')); + this.initTable(); + }); + } else { + // 批量修改其它 + } }, handleBatchExecute() { this.selectRows.forEach(row => { diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index ba60fb987f..8e0019a6dd 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -194,6 +194,9 @@ export default { module: { select_module: "Select module", default_module: "Default module", + }, + report_statistics: { + title: "Report statistics" } }, license: { diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 3830658161..9e9a7f538e 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -195,6 +195,9 @@ export default { module: { select_module: "選擇模塊", default_module: "默認模塊", + }, + report_statistics: { + title: "報表統計" } }, license: {