This commit is contained in:
chenjianxing 2020-08-07 10:03:33 +08:00
commit a9d6c98c09
7 changed files with 232 additions and 90 deletions

View File

@ -1,5 +1,6 @@
# MeterSphere 开源持续测试平台 # MeterSphere 开源持续测试平台
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/176186d132df448b955f8bdd5e6ef9c0)](https://app.codacy.com/gh/metersphere/metersphere?utm_source=github.com&utm_medium=referral&utm_content=metersphere/metersphere&utm_campaign=Badge_Grade_Dashboard)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/metersphere/metersphere)](https://github.com/metersphere/metersphere/releases/latest) [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/metersphere/metersphere)](https://github.com/metersphere/metersphere/releases/latest)
[![GitHub All Releases](https://img.shields.io/github/downloads/metersphere/metersphere/total)](https://github.com/metersphere/metersphere/releases) [![GitHub All Releases](https://img.shields.io/github/downloads/metersphere/metersphere/total)](https://github.com/metersphere/metersphere/releases)
[![TesterHome](https://img.shields.io/badge/TTF-TesterHome-2955C5.svg)](https://testerhome.com/github_statistics) [![TesterHome](https://img.shields.io/badge/TTF-TesterHome-2955C5.svg)](https://testerhome.com/github_statistics)

View File

@ -365,6 +365,54 @@
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-and-filter-allatori-config</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target</outputDirectory>
<resources>
<resource>
<directory>${basedir}/allatori</directory>
<includes>
<include>allatori.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>run-allatori</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<arguments>
<argument>-Xms128m</argument>
<argument>-Xmx512m</argument>
<argument>-jar</argument>
<argument>${basedir}/target/classes/allatori/allatori.jar</argument>
<argument>${basedir}/target/classes/allatori/allatori.xml</argument>
</arguments>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -3,8 +3,13 @@
<jar in="../../*.jar" out="../../*.jar"/> <jar in="../../*.jar" out="../../*.jar"/>
</input> </input>
<ignore-classes>
<class template="class regex:io.metersphere.(?!xpack).*"/>
<class template="class org.*"/>
</ignore-classes>
<keep-names> <keep-names>
<class access="private+"> <class template="class io.metersphere.xpack.*" access="private+">
<field access="protected+"/> <field access="protected+"/>
<method access="protected+"/> <method access="protected+"/>
</class> </class>

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-dialog <el-dialog
title="批量编辑用例" :title="dialogTitle"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="25%" width="25%"
class="batch-edit-dialog" class="batch-edit-dialog"
@ -11,15 +11,15 @@
<el-form :model="form" label-position="right" label-width="150px" size="medium" ref="form" :rules="rules"> <el-form :model="form" label-position="right" label-width="150px" size="medium" ref="form" :rules="rules">
<el-form-item :label="$t('test_track.case.batch_update', [size])" prop="type"> <el-form-item :label="$t('test_track.case.batch_update', [size])" prop="type">
<el-select v-model="form.type" style="width: 80%" @change="changeType"> <el-select v-model="form.type" style="width: 80%" @change="changeType">
<el-option label="用例等级" value="priority"/> <el-option v-for="(type, index) in typeArr" :key="index" :value="type.id" :label="type.name"/>
<el-option label="类型" value="type"/>
<el-option label="测试方式" value="method"/>
<el-option label="维护人" value="maintainer"/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="更新后属性值为" prop="value"> <el-form-item label="更新后属性值为" prop="value">
<el-select v-model="form.value" style="width: 80%" :filterable="filterable"> <el-select v-model="form.value" style="width: 80%" :filterable="filterable">
<el-option v-for="(option, index) in options" :key="index" :value="option.id" :label="option.name"> <el-option v-for="(option, index) in options" :key="index" :value="option.id" :label="option.name">
<div v-if="option.email">
<span>{{option.id}}({{option.name}})</span>
</div>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -35,7 +35,6 @@
<script> <script>
import MsDialogFooter from "../../../common/components/MsDialogFooter"; import MsDialogFooter from "../../../common/components/MsDialogFooter";
import {WORKSPACE_ID} from "../../../../../common/js/constants";
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils"; import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
export default { export default {
@ -43,8 +42,13 @@
components: { components: {
MsDialogFooter MsDialogFooter
}, },
created() { props: {
this.getMaintainerOptions(); typeArr: Array,
valueArr: Object,
dialogTitle: {
type: String,
default: "批量操作"
}
}, },
data() { data() {
return { return {
@ -56,22 +60,6 @@
value: {required: true, message: "请选择属性对应的值", trigger: ['blur','change']} value: {required: true, message: "请选择属性对应的值", trigger: ['blur','change']}
}, },
options: [], options: [],
priorities: [
{name: 'P0', id: 'P0'},
{name: 'P1', id: 'P1'},
{name: 'P2', id: 'P2'},
{name: 'P3', id: 'P3'}
],
types: [
{name: this.$t('commons.functional'), id: 'functional'},
{name: this.$t('commons.performance'), id: 'performance'},
{name: this.$t('commons.api'), id: 'api'}
],
methods: [
{name: this.$t('test_track.case.manual'), id: 'manual'},
{name: this.$t('test_track.case.auto'), id: 'auto'}
],
maintainers: [],
filterable: false, filterable: false,
} }
}, },
@ -93,33 +81,13 @@
}, },
handleClose() { handleClose() {
this.form = {}; this.form = {};
this.options = [];
removeGoBackListener(this.handleClose); removeGoBackListener(this.handleClose);
}, },
changeType(val) { changeType(val) {
this.$set(this.form, "value", ""); this.$set(this.form, "value", "");
this.filterable = val === "maintainer"; this.filterable = val === "maintainer" || val === "executor";
switch (val) { this.options = this.valueArr[val];
case "priority":
this.options = this.priorities;
break;
case "type":
this.options = this.types;
break;
case "method":
this.options = this.methods;
break;
case "maintainer":
this.options = this.maintainers;
break;
default:
this.options = [];
}
},
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.maintainers = response.data;
});
} }
} }
} }

View File

@ -1,12 +1,12 @@
<template> <template>
<div v-if="isShow"> <div v-if="isShow">
<el-dropdown placement="bottom" trigger="click" size="medium"> <el-dropdown placement="bottom" trigger="click" size="medium">
<div @click.stop="click" class="show-more-btn"> <div @click.stop class="show-more-btn">
<i class="el-icon-more ms-icon-more"/> <i class="el-icon-more ms-icon-more"/>
</div> </div>
<el-dropdown-menu slot="dropdown" class="dropdown-menu-class"> <el-dropdown-menu slot="dropdown" class="dropdown-menu-class">
<div class="show-more-btn-title">{{$t('test_track.case.batch_handle', [size])}}</div> <div class="show-more-btn-title">{{$t('test_track.case.batch_handle', [size])}}</div>
<el-dropdown-item v-for="(btn,index) in buttons" :key="index" @click.native.stop="clickStop(btn)"> <el-dropdown-item v-for="(btn,index) in buttons" :key="index" @click.native.stop="click(btn)">
{{btn.name}} {{btn.name}}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
@ -17,19 +17,6 @@
<script> <script>
export default { export default {
name: "ShowMoreBtn", name: "ShowMoreBtn",
data() {
return {}
},
methods: {
click() {
// console.log("click");
},
clickStop(btn) {
if (btn.stop instanceof Function) {
btn.stop();
}
}
},
props: { props: {
isShow: { isShow: {
type: Boolean, type: Boolean,
@ -38,6 +25,13 @@
buttons: Array, buttons: Array,
row: Object, row: Object,
size: Number size: Number
},
methods: {
click(btn) {
if (btn.handleClick instanceof Function) {
btn.handleClick();
}
}
} }
} }
</script> </script>

View File

@ -120,7 +120,8 @@
</el-card> </el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"/> <batch-edit ref="batchEdit" @batchEdit="batchEdit"
:typeArr="typeArr" :value-arr="valueArr" dialog-title="批量编辑用例"/>
</div> </div>
</template> </template>
@ -142,6 +143,7 @@
import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components"; import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components";
import ShowMoreBtn from "./ShowMoreBtn"; import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit"; import BatchEdit from "./BatchEdit";
import {WORKSPACE_ID} from "../../../../../common/js/constants";
export default { export default {
name: "TestCaseList", name: "TestCaseList",
@ -191,13 +193,37 @@
showMore: false, showMore: false,
buttons: [ buttons: [
{ {
name: '批量编辑用例', stop: this.handleClickStop name: '批量编辑用例', handleClick: this.handleBatchEdit
}, { }, {
name: '批量移动用例', stop: this.handleMove name: '批量移动用例', handleClick: this.handleBatchMove
}, { }, {
name: '批量删除用例', stop: this.handleDeleteBatch name: '批量删除用例', handleClick: this.handleDeleteBatch
} }
] ],
typeArr: [
{id: 'priority', name: '用例等级'},
{id: 'type', name: '类型'},
{id: 'method', name: '测试方式'},
{id: 'maintainer', name: '维护人'},
],
valueArr: {
priority: [
{name: 'P0', id: 'P0'},
{name: 'P1', id: 'P1'},
{name: 'P2', id: 'P2'},
{name: 'P3', id: 'P3'}
],
type: [
{name: this.$t('commons.functional'), id: 'functional'},
{name: this.$t('commons.performance'), id: 'performance'},
{name: this.$t('commons.api'), id: 'api'}
],
method: [
{name: this.$t('test_track.case.manual'), id: 'manual'},
{name: this.$t('test_track.case.auto'), id: 'auto'}
],
maintainer: [],
}
} }
}, },
props: { props: {
@ -428,11 +454,18 @@
_sort(column, this.condition); _sort(column, this.condition);
this.initTableData(); this.initTableData();
}, },
handleClickStop() { handleBatchEdit() {
this.getMaintainerOptions();
this.$refs.batchEdit.open(); this.$refs.batchEdit.open();
}, },
handleMove() { handleBatchMove() {
this.$emit("batchMove", Array.from(this.selectRows).map(row => row.id)); this.$emit("batchMove", Array.from(this.selectRows).map(row => row.id));
},
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.valueArr.maintainer = response.data;
});
} }
} }
} }

View File

@ -30,8 +30,10 @@
</ms-table-header> </ms-table-header>
</template> </template>
<executor-edit ref="executorEdit" :select-ids="selectIds" @refresh="initTableData"/> <executor-edit ref="executorEdit" :select-ids="new Set(Array.from(this.selectRows).map(row => row.id))"
<status-edit ref="statusEdit" :plan-id="planId" :select-ids="selectIds" @refresh="initTableData"/> @refresh="initTableData"/>
<status-edit ref="statusEdit" :plan-id="planId"
:select-ids="new Set(Array.from(this.selectRows).map(row => row.id))" @refresh="initTableData"/>
<el-table <el-table
class="adjust-table" class="adjust-table"
@ -45,7 +47,12 @@
:data="tableData"> :data="tableData">
<el-table-column <el-table-column
type="selection"></el-table-column> type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectRows.size"/>
</template>
</el-table-column>
<el-table-column <el-table-column
prop="num" prop="num"
:label="$t('commons.id')" :label="$t('commons.id')"
@ -163,8 +170,9 @@
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/> <test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
<test-case-report-view @refresh="initTableData" ref="testCaseReportView"/> <test-case-report-view @refresh="initTableData" ref="testCaseReportView"/>
</el-card> </el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"
:type-arr="typeArr" :value-arr="valueArr" dialog-title="批量更改测试计划"/>
</div> </div>
</template> </template>
@ -178,7 +186,7 @@
import MsTableButton from '../../../../common/components/MsTableButton'; import MsTableButton from '../../../../common/components/MsTableButton';
import NodeBreadcrumb from '../../../common/NodeBreadcrumb'; import NodeBreadcrumb from '../../../common/NodeBreadcrumb';
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, TokenKey} from '../../../../../../common/js/constants'; import {ROLE_TEST_MANAGER, ROLE_TEST_USER, TokenKey, WORKSPACE_ID} from '../../../../../../common/js/constants';
import {_filter, _sort, checkoutTestManagerOrTestUser, hasRoles} from '../../../../../../common/js/utils'; import {_filter, _sort, checkoutTestManagerOrTestUser, hasRoles} from '../../../../../../common/js/utils';
import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem"; import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem";
import StatusTableItem from "../../../common/tableItems/planview/StatusTableItem"; import StatusTableItem from "../../../common/tableItems/planview/StatusTableItem";
@ -189,6 +197,8 @@
import TestReportTemplateList from "./TestReportTemplateList"; import TestReportTemplateList from "./TestReportTemplateList";
import TestCaseReportView from "./report/TestCaseReportView"; import TestCaseReportView from "./report/TestCaseReportView";
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components"; import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
import ShowMoreBtn from "../../../case/components/ShowMoreBtn";
import BatchEdit from "../../../case/components/BatchEdit";
export default { export default {
name: "TestPlanTestCaseList", name: "TestPlanTestCaseList",
@ -201,7 +211,8 @@
TypeTableItem, TypeTableItem,
StatusTableItem, StatusTableItem,
PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination, PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination,
TestPlanTestCaseEdit, MsTableHeader, NodeBreadcrumb, MsTableButton TestPlanTestCaseEdit, MsTableHeader, NodeBreadcrumb, MsTableButton, ShowMoreBtn,
BatchEdit
}, },
data() { data() {
return { return {
@ -215,7 +226,8 @@
currentPage: 1, currentPage: 1,
pageSize: 10, pageSize: 10,
total: 0, total: 0,
selectIds: new Set(), // selectIds: new Set(),
selectRows: new Set(),
testPlan: {}, testPlan: {},
isReadOnly: false, isReadOnly: false,
isTestManagerOrTestUser: false, isTestManagerOrTestUser: false,
@ -241,7 +253,29 @@
{text: this.$t('test_track.plan_view.blocking'), value: 'Blocking'}, {text: this.$t('test_track.plan_view.blocking'), value: 'Blocking'},
{text: this.$t('test_track.plan_view.skip'), value: 'Skip'}, {text: this.$t('test_track.plan_view.skip'), value: 'Skip'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'}, {text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
] ],
showMore: false,
buttons: [
{
name: '批量更改测试计划', handleClick: this.handleBatchEdit
},
{
name: '批量取消用例关联', handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'status', name: '执行结果'},
{id: 'executor', name: '执行人'},
],
valueArr: {
executor: [],
status: [
{name: this.$t('test_track.plan_view.pass'), id: 'Pass'},
{name: this.$t('test_track.plan_view.failure'), id: 'Failure'},
{name: this.$t('test_track.plan_view.blocking'), id: 'Blocking'},
{name: this.$t('test_track.plan_view.skip'), id: 'Skip'}
]
}
} }
}, },
props: { props: {
@ -282,7 +316,8 @@
let data = response.data; let data = response.data;
this.total = data.itemCount; this.total = data.itemCount;
this.tableData = data.listObject; this.tableData = data.listObject;
this.selectIds.clear(); // this.selectIds.clear();
this.selectRows.clear();
}); });
} }
}, },
@ -292,7 +327,8 @@
}, },
refresh() { refresh() {
this.condition = {components: TEST_CASE_CONFIGS}; this.condition = {components: TEST_CASE_CONFIGS};
this.selectIds.clear(); // this.selectIds.clear();
this.selectRows.clear();
this.$emit('refresh'); this.$emit('refresh');
}, },
refreshTableAndPlan() { refreshTableAndPlan() {
@ -335,8 +371,10 @@
confirmButtonText: this.$t('commons.confirm'), confirmButtonText: this.$t('commons.confirm'),
callback: (action) => { callback: (action) => {
if (action === 'confirm') { if (action === 'confirm') {
this.$post('/test/plan/case/batch/delete', {ids: [...this.selectIds]}, () => { let ids = Array.from(this.selectRows).map(row => row.id);
this.selectIds.clear(); this.$post('/test/plan/case/batch/delete', {ids: ids}, () => {
// this.selectIds.clear();
this.selectRows.clear();
this.$emit("refresh"); this.$emit("refresh");
this.$success(this.$t('commons.delete_success')); this.$success(this.$t('commons.delete_success'));
}); });
@ -352,23 +390,56 @@
}); });
}, },
handleSelectAll(selection) { handleSelectAll(selection) {
// if (selection.length > 0) {
// this.tableData.forEach(item => {
// this.selectIds.add(item.id);
// });
// } else {
// this.selectIds.clear();
// }
if (selection.length > 0) { if (selection.length > 0) {
this.tableData.forEach(item => { if (selection.length === 1) {
this.selectIds.add(item.id); this.selectRows.add(selection[0]);
}); } else {
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
}
} else { } else {
this.selectIds.clear(); this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
} }
}, },
handleSelectionChange(selection, row) { handleSelectionChange(selection, row) {
if (this.selectIds.has(row.id)) { // if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id); // this.selectIds.delete(row.id);
// } else {
// this.selectIds.add(row.id);
// }
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
} else { } else {
this.selectIds.add(row.id); 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);
})
} }
}, },
handleBatch(type) { handleBatch(type) {
if (this.selectIds.size < 1) { if (this.selectRows.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate')); this.$warning(this.$t('test_track.plan_view.select_manipulate'));
return; return;
} }
@ -428,6 +499,28 @@
sort(column) { sort(column) {
_sort(column, this.condition); _sort(column, this.condition);
this.initTableData(); this.initTableData();
},
batchEdit(form) {
let param = {};
param[form.type] = form.value;
param.ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/test/plan/case/batch/edit', param, () => {
this.selectRows.clear();
this.status = '';
this.$post('/test/plan/edit/status/' + this.planId);
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
});
},
handleBatchEdit() {
this.getMaintainerOptions();
this.$refs.batchEdit.open();
},
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.valueArr.executor = response.data;
});
} }
} }
} }