Merge branch 'dev' of github.com:fit2cloudrd/metersphere-server into dev

This commit is contained in:
Captain.B 2020-03-12 16:35:37 +08:00
commit 3725a267f0
32 changed files with 1205 additions and 230 deletions

View File

@ -40,7 +40,9 @@
"plugin:vue/essential", "plugin:vue/essential",
"eslint:recommended" "eslint:recommended"
], ],
"rules": {}, "rules": {
"vue/no-unused-components": "off"
},
"parserOptions": { "parserOptions": {
"parser": "babel-eslint" "parser": "babel-eslint"
} }

View File

@ -117,6 +117,7 @@ export default {
'basic_config': 'Scene Configuration', 'basic_config': 'Scene Configuration',
'pressure_config': 'Pressure configuration', 'pressure_config': 'Pressure configuration',
'advanced_config': 'Advanced Configuration', 'advanced_config': 'Advanced Configuration',
'runtime_config': 'Runtime Configuration',
'is_running': 'Test is running! ', 'is_running': 'Test is running! ',
'test_name_is_null': 'Test name cannot be empty! ', 'test_name_is_null': 'Test name cannot be empty! ',
'project_is_null': 'Project cannot be empty! ', 'project_is_null': 'Project cannot be empty! ',

View File

@ -117,6 +117,7 @@ export default {
'basic_config': '场景配置', 'basic_config': '场景配置',
'pressure_config': '压力配置', 'pressure_config': '压力配置',
'advanced_config': '高级配置', 'advanced_config': '高级配置',
'runtime_config': '运行配置',
'is_running': '正在运行!', 'is_running': '正在运行!',
'test_name_is_null': '测试名称不能为空!', 'test_name_is_null': '测试名称不能为空!',
'project_is_null': '项目不能为空!', 'project_is_null': '项目不能为空!',

View File

@ -11,39 +11,23 @@
<ms-user/> <ms-user/>
</el-col> </el-col>
</el-row> </el-row>
<el-row id="header-bottom" type="flex" justify="space-between" align="middle">
<el-col :span="10">
<ms-menus/>
</el-col>
<el-col :span="4">
<el-row type="flex" justify="center" align="middle">
<router-link to="/createTest" v-permission="['test_user','test_manager']" v-if="isCurrentWorkspaceUser">
<el-button type="primary" size="small">{{$t('load_test.create')}}</el-button>
</router-link>
</el-row>
</el-col>
<el-col :span="10">
</el-col>
</el-row>
<ms-view/> <ms-view/>
<ms-web-socket/> <ms-web-socket/>
</el-col> </el-col>
</template> </template>
<script> <script>
import MsMenus from "./components/HeaderMenus";
import MsTopMenus from "./components/HeaderTopMenus"; import MsTopMenus from "./components/HeaderTopMenus";
import MsView from "./components/router/View"; import MsView from "./components/router/View";
import MsUser from "./components/HeaderUser"; import MsUser from "./components/HeaderUser";
import MsWebSocket from "./components/websocket/WebSocket"; import MsWebSocket from "./components/websocket/WebSocket";
import {checkoutCurrentWorkspace} from "../common/utils";
export default { export default {
name: 'app', name: 'app',
data() { data() {
return { return {
auth: false, auth: false
isCurrentWorkspaceUser: false,
} }
}, },
beforeCreate() { beforeCreate() {
@ -59,11 +43,8 @@
window.location.href = "/login" window.location.href = "/login"
}); });
}, },
components: {MsWebSocket, MsUser, MsMenus, MsView, MsTopMenus}, components: {MsWebSocket, MsUser, MsView, MsTopMenus},
methods: {}, methods: {}
mounted() {
this.isCurrentWorkspaceUser = checkoutCurrentWorkspace();
}
} }
</script> </script>
@ -96,14 +77,6 @@
background-image: url("../assets/MeterSphere-反白.png"); background-image: url("../assets/MeterSphere-反白.png");
} }
#header-bottom {
height: 40px;
padding: 0 15px;
border-bottom: 1px solid #E6E6E6;
cursor: default;
color: #404040;
}
.menus > * { .menus > * {
color: inherit; color: inherit;
padding: 0; padding: 0;

View File

@ -1,87 +1,120 @@
<template> <template>
<div id="menu-bar">
<el-menu class="header-menu" :unique-opened="true" mode="horizontal" router <el-menu class="header-menu" :unique-opened="true" mode="horizontal" router
:default-active='$route.path'> :default-active='$route.path'>
<el-menu-item index="/setting/personsetting"> <el-menu-item :index="'/' + beaseUrl + '/home'">
{{ $t("i18n.home") }} {{ $t("i18n.home") }}
</el-menu-item> </el-menu-item>
<el-submenu index="3" popper-class="submenu" v-permission="['test_manager']" v-if="isCurrentWorkspaceUser"> <el-submenu index="3" popper-class="submenu" v-permission="['test_manager']" v-if="isCurrentWorkspaceUser">
<template slot="title">{{$t('commons.project')}}</template> <template slot="title">{{$t('commons.project')}}</template>
<ms-recent-project/> <performance-recent-project v-if="beaseUrl == 'performance'"/>
<functional-recent-project v-if="beaseUrl == 'functional'"/>
<el-divider/> <el-divider/>
<el-menu-item index="/project/all"> <el-menu-item :index="'/' + beaseUrl + '/project/all'">
<font-awesome-icon :icon="['fa', 'list-ul']"/> <font-awesome-icon :icon="['fa', 'list-ul']"/>
<span style="padding-left: 5px;">{{$t('commons.show_all')}}</span> <span style="padding-left: 5px;">{{$t('commons.show_all')}}</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/project/create"> <el-menu-item :index="'/' + beaseUrl + '/project/create'">
<el-button type="text">{{$t('project.create')}}</el-button> <el-button type="text">{{$t('project.create')}}</el-button>
</el-menu-item> </el-menu-item>
</el-submenu> </el-submenu>
<el-submenu index="4" popper-class="submenu" v-permission="['test_manager', 'test_user']" <el-submenu index="4" popper-class="submenu" v-permission="['test_manager', 'test_user']"
v-if="isCurrentWorkspaceUser"> v-if="isCurrentWorkspaceUser">
<template slot="title">{{$t('commons.test')}}</template> <template slot="title">{{$t('commons.test')}}</template>
<ms-recent-test-plan/> <performance-recent-test-plan v-if="beaseUrl == 'performance'"/>
<functional-recent-test-plan v-if="beaseUrl == 'functional'"/>
<el-divider/> <el-divider/>
<el-menu-item index="/loadtest/all"> <el-menu-item :index="'/' + beaseUrl + '/plan/all'">
<font-awesome-icon :icon="['fa', 'list-ul']"/> <font-awesome-icon :icon="['fa', 'list-ul']"/>
<span style="padding-left: 5px;">{{$t('commons.show_all')}}</span> <span style="padding-left: 5px;">{{$t('commons.show_all')}}</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/createTest"> <el-menu-item :index="'/' + beaseUrl + '/plan/create'">
<el-button type="text">{{$t('load_test.create')}}</el-button> <el-button type="text">{{$t('load_test.create')}}</el-button>
</el-menu-item> </el-menu-item>
</el-submenu> </el-submenu>
<el-submenu index="5" popper-class="submenu" v-permission="['test_manager', 'test_user', 'test_viewer']" <el-submenu index="5" popper-class="submenu" v-permission="['test_manager', 'test_user', 'test_viewer']"
v-if="isCurrentWorkspaceUser"> v-if="isCurrentWorkspaceUser">
<template slot="title">{{$t('commons.report')}}</template> <template slot="title">{{$t('commons.report')}}</template>
<ms-recent-report/> <performance-recent-report v-if="beaseUrl == 'performance'"/>
<functional-recent-report v-if="beaseUrl == 'functional'"/>
<el-divider/> <el-divider/>
<el-menu-item index="/report/all"> <el-menu-item :index="'/' + beaseUrl + '/report/all'">
<font-awesome-icon :icon="['fa', 'list-ul']"/> <font-awesome-icon :icon="['fa', 'list-ul']"/>
<span style="padding-left: 5px;">{{$t('commons.show_all')}}</span> <span style="padding-left: 5px;">{{$t('commons.show_all')}}</span>
</el-menu-item> </el-menu-item>
</el-submenu> </el-submenu>
<router-link class="header-bottom" :to="'/' + beaseUrl + '/plan/create'" v-permission="['test_user','test_manager']"
v-if="isCurrentWorkspaceUser">
<el-button type="primary" size="small">{{$t('load_test.create')}}</el-button>
</router-link>
</el-menu> </el-menu>
</div>
</template> </template>
<script> <script>
import MsRecentTestPlan from "./testPlan/RecentTestPlan";
import MsRecentProject from "./project/RecentProject"; import PerformanceRecentTestPlan from "./testPlan/PerformanceRecentTestPlan";
import MsRecentReport from "./report/RecentReport"; import FunctionalRecentTestPlan from "./testPlan/FunctionalRecentTestPlan";
import PerformanceRecentProject from "./project/PerformanceRecentProject";
import FunctionalRecentProject from "./project/FunctionalRecentProject";
import PerformanceRecentReport from "./report/PerformanceRecentReport";
import FunctionalRecentReport from "./report/FunctionalRecentReport";
import {checkoutCurrentWorkspace} from "../../common/utils"; import {checkoutCurrentWorkspace} from "../../common/utils";
export default { export default {
name: "MsMenus", name: "MsMenus",
components: {MsRecentReport, MsRecentTestPlan, MsRecentProject}, components: {PerformanceRecentReport, PerformanceRecentTestPlan, FunctionalRecentTestPlan, FunctionalRecentReport,
PerformanceRecentProject,FunctionalRecentProject},
data() { data() {
return { return {
isCurrentWorkspaceUser: false, isCurrentWorkspaceUser: false,
} }
}, },
props: {
beaseUrl: {
type: String
}
},
mounted() { mounted() {
this.isCurrentWorkspaceUser = checkoutCurrentWorkspace(); this.isCurrentWorkspaceUser = checkoutCurrentWorkspace();
} }
} }
</script> </script>
<style> <style>
.header-menu.el-menu--horizontal > li.el-menu-item {
padding-left: 0;
}
.header-menu.el-menu--horizontal > li { .header-menu.el-menu--horizontal > li {
height: 39px; height: 39px;
line-height: 40px; line-height: 40px;
color: inherit; color: dimgray;
} }
.header-menu.el-menu--horizontal > li.el-submenu > * { .header-menu.el-menu--horizontal > li.el-submenu > * {
height: 39px; height: 39px;
line-height: 40px; line-height: 40px;
color: inherit; color: dimgray;
} }
.header-bottom {
line-height: 40px;
margin-left: 20%;
}
</style> </style>
<style scoped> <style scoped>
.el-divider--horizontal { .el-divider--horizontal {
margin: 0; margin: 0;
} }
#menu-bar {
border-bottom: 1px solid #E6E6E6;
}
</style> </style>

View File

@ -7,10 +7,11 @@
:default-active="activeIndex" :default-active="activeIndex"
@select="handleSelect" @select="handleSelect"
router> router>
<el-menu-item index="1" v-permission="['test_manager','test_user','test_viewer']">
<el-menu-item index="/functional" v-permission="['test_manager','test_user','test_viewer']">
{{$t('commons.functional')}} {{$t('commons.functional')}}
</el-menu-item> </el-menu-item>
<el-menu-item index="/loadtest" onselectstart="return false" <el-menu-item index="/performance" onselectstart="return false"
v-permission="['test_manager','test_user','test_viewer']"> v-permission="['test_manager','test_user','test_viewer']">
{{$t('commons.performance')}} {{$t('commons.performance')}}
</el-menu-item> </el-menu-item>

View File

@ -6,7 +6,7 @@
</div> </div>
<el-menu-item :key="p.id" v-for="p in recentProjects" <el-menu-item :key="p.id" v-for="p in recentProjects"
:index="'/loadtest/' + p.id" :route="{name:'loadtest', params:{projectId:p.id, projectName:p.name}}"> :index="'/functional/' + p.id" :route="{name:'fucPlan', params:{projectId:p.id, projectName:p.name}}">
{{ p.name }} {{ p.name }}
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
@ -18,7 +18,7 @@
import {hasRoles} from "../../../common/utils"; import {hasRoles} from "../../../common/utils";
export default { export default {
name: "MsRecentProject", name: "FunctionalRecentProject",
mounted() { mounted() {
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) { if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {

View File

@ -90,18 +90,23 @@
}, },
} }
}, },
props: {
beaseUrl: {
type: String
}
},
mounted() { mounted() {
if (this.$route.path.split('/')[2] === 'create') { if (this.$route.path.split('/')[3] === 'create') {
this.create(); this.create();
this.$router.push('/project/all'); this.$router.push( '/' + this.beaseUrl + '/project/all');
} }
this.list(); this.list();
}, },
watch: { watch: {
'$route'(to) { '$route'(to) {
if (to.path.split('/')[2] === 'create') { if (to.path.split('/')[3] === 'create') {
this.create(); this.create();
this.$router.push('/project/all'); this.$router.push('/' + this.beaseUrl + '/project/all');
} }
} }
}, },

View File

@ -6,7 +6,7 @@
import echarts from 'echarts' import echarts from 'echarts'
export default { export default {
name: "MsChart", name: "PerformanceChart",
data() { data() {
return { return {
bar: { bar: {

View File

@ -0,0 +1,44 @@
<template>
<el-menu router menu-trigger="click" :default-active="$route.path">
<div class="recent-text">
<i class="el-icon-time"/>
{{$t('project.recent')}}
</div>
<el-menu-item :key="p.id" v-for="p in recentProjects"
:index="'/performance/plan/' + p.id" :route="{name:'perPlan', params:{projectId:p.id, projectName:p.name}}">
{{ p.name }}
</el-menu-item>
</el-menu>
</template>
<script>
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER} from "../../../common/constants";
import {hasRoles} from "../../../common/utils";
export default {
name: "PerformanceRecentProject",
mounted() {
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.$get('/project/recent/5', (response) => {
this.recentProjects = response.data;
});
}
},
methods: {},
data() {
return {
recentProjects: [],
}
}
}
</script>
<style scoped>
.recent-text {
padding-left: 10%;
color: #777777;
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<el-menu router menu-trigger="click" :default-active="$route.path">
<div class="recent-text">
<i class="el-icon-time"/>
{{$t('load_test.recent')}}
</div>
<el-menu-item :key="p.id" v-for="p in recentReports"
:index="'/functional/report/view/' + p.id" :route="{path: '/functional/report/view/' + p.id}">
{{ p.name }}
</el-menu-item>
</el-menu>
</template>
<script>
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER} from "../../../common/constants";
export default {
name: "PerformanceRecentReport",
mounted() {
const rolesString = localStorage.getItem("roles");
const roles = rolesString.split(',');
if (roles.indexOf(ROLE_TEST_MANAGER) > -1 || roles.indexOf(ROLE_TEST_USER) > -1 || roles.indexOf(ROLE_TEST_VIEWER) > -1) {
this.$get('/report/recent/5', (response) => {
this.recentReports = response.data;
});
}
},
methods: {},
data() {
return {
recentReports: [],
}
}
}
</script>
<style scoped>
.recent-text {
padding-left: 10%;
color: #777777;
}
</style>

View File

@ -0,0 +1,185 @@
<template>
<div class="testreport-container" v-loading="result.loading">
<div class="main-content">
<el-card>
<div slot="header">
<el-row type="flex" justify="space-between" align="middle">
<span class="title">{{$t('commons.report')}}</span>
<span class="search">
<el-input type="text" size="small" :placeholder="$t('report.search_by_name')"
prefix-icon="el-icon-search"
maxlength="60"
v-model="condition" @change="search" clearable/>
</span>
</el-row>
</div>
<el-table :data="tableData" class="test-content">
<el-table-column
prop="name"
:label="$t('commons.name')"
width="150"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="description"
:label="$t('commons.description')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="testName"
:label="$t('report.test_name')"
width="150"
show-overflow-tooltip>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.create_time')">
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.update_time')">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="150"
:label="$t('commons.operating')">
<template slot-scope="scope">
<el-button @click="handleEdit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
</template>
</el-table-column>
</el-table>
<div>
<el-row>
<el-col :span="22" :offset="1">
<div class="table-page">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[5, 10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</el-col>
</el-row>
</div>
</el-card>
</div>
</div>
</template>
<script>
export default {
name: "FunctionalTestReport",
created: function () {
this.initTableData();
},
data() {
return {
result: {},
queryPath: "/report/list/all",
deletePath: "/report/delete/",
condition: "",
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
}
},
methods: {
initTableData() {
let param = {
name: this.condition,
};
this.result = this.$post(this.buildPagePath(this.queryPath), param, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search() {
this.initTableData();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSizeChange(size) {
this.pageSize = size;
this.initTableData();
},
handleCurrentChange(current) {
this.currentPage = current;
this.initTableData();
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(report) {
this.$router.push({
path: '/functional/reportView/' + report.id
})
},
handleDelete(report) {
this.$alert(this.$t('load_test.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(report);
}
}
});
},
_handleDelete(report) {
this.result = this.$post(this.deletePath + report.id, {},() => {
this.$message({
message: this.$t('commons.delete_success'),
type: 'success'
});
this.initTableData();
});
},
}
}
</script>
<style scoped>
.testreport-container {
padding: 15px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.main-content {
margin: 0 auto;
width: 100%;
max-width: 1200px;
}
.test-content {
width: 100%;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
</style>

View File

@ -5,7 +5,7 @@
{{$t('load_test.recent')}} {{$t('load_test.recent')}}
</div> </div>
<el-menu-item :key="p.id" v-for="p in recentReports" <el-menu-item :key="p.id" v-for="p in recentReports"
:index="'/reportView/' + p.id" :route="{path: '/reportView/' + p.id}"> :index="'/performance/report/view/' + p.id" :route="{path: '/performance/report/view/' + p.id}">
{{ p.name }} {{ p.name }}
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
@ -16,7 +16,7 @@
import {hasRoles} from "../../../common/utils"; import {hasRoles} from "../../../common/utils";
export default { export default {
name: "MsRecentReport", name: "PerformanceRecentReport",
mounted() { mounted() {
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) { if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {

View File

@ -60,7 +60,7 @@
import MsReportTestOverview from './components/TestOverview'; import MsReportTestOverview from './components/TestOverview';
export default { export default {
name: "ReportView", name: "PerformanceReportView",
components: { components: {
MsReportErrorLog, MsReportErrorLog,
MsReportLogDetails, MsReportLogDetails,
@ -79,30 +79,38 @@
}, },
methods: { methods: {
initBreadcrumb() { initBreadcrumb() {
if(this.reportId){
this.result = this.$get("report/test/pro/info/" + this.reportId, res => { this.result = this.$get("report/test/pro/info/" + this.reportId, res => {
let data = res.data; let data = res.data;
if(data){
this.reportName = data.name; this.reportName = data.name;
this.testName = data.testName; this.testName = data.testName;
this.projectName = data.projectName; this.projectName = data.projectName;
}
}) })
} }
}
}, },
created() { created() {
this.reportId = this.$route.path.split('/')[2]; this.reportId = this.$route.path.split('/')[4];
this.initBreadcrumb(); this.initBreadcrumb();
}, },
watch: { watch: {
'$route'(to) { '$route'(to) {
let reportId = to.path.split('/')[2]; // find testId let reportId = to.path.split('/')[4];
if(reportId){
this.$get("report/test/pro/info/" + reportId, response => { this.$get("report/test/pro/info/" + reportId, response => {
let data = response.data; let data = response.data;
if(data){
this.reportName = data.name; this.reportName = data.name;
this.testName = data.testName; this.testName = data.testName;
this.projectName = data.projectName; this.projectName = data.projectName;
}
}); });
} }
} }
} }
}
</script> </script>
<style scoped> <style scoped>

View File

@ -48,11 +48,11 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
width="200" width="150"
:label="$t('commons.operating')"> :label="$t('commons.operating')">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button @click="handleEdit(scope.row)" type="primary" size="mini">查看</el-button> <el-button @click="handleEdit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="handleDelete(scope.row)" type="danger" size="mini">删除</el-button> <el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -81,7 +81,7 @@
<script> <script>
export default { export default {
name: "MsAllTestReport", name: "PerformanceTestReport",
created: function () { created: function () {
this.initTableData(); this.initTableData();
}, },
@ -131,7 +131,7 @@
}, },
handleEdit(report) { handleEdit(report) {
this.$router.push({ this.$router.push({
path: '/reportView/' + report.id path: '/performance/report/view/' + report.id
}) })
}, },
handleDelete(report) { handleDelete(report) {

View File

@ -14,7 +14,7 @@
<style scoped> <style scoped>
#body { #body {
width: 100%; width: 100%;
height: calc(100vh - 80px); height: calc(100vh - 40px);
background-color: #F5F5F5; background-color: #F5F5F5;
} }

View File

@ -3,8 +3,8 @@ import VueRouter from 'vue-router'
import RouterSidebar from "./RouterSidebar"; import RouterSidebar from "./RouterSidebar";
import Setting from "../settings/Setting"; import Setting from "../settings/Setting";
import User from "../settings/system/User"; import User from "../settings/system/User";
import EditTestPlan from "../testPlan/EditTestPlan"; import EditPerformanceTestPlan from "../testPlan/EditPerformanceTestPlan";
import AllTestPlan from "../testPlan/AllTestPlan"; import PerformanceTestPlan from "../testPlan/PerformanceTestPlan";
import Organization from "../settings/system/Organization"; import Organization from "../settings/system/Organization";
import OrganizationMember from "../settings/organization/OrganizationMember"; import OrganizationMember from "../settings/organization/OrganizationMember";
import Member from "../settings/workspace/WorkspaceMember"; import Member from "../settings/workspace/WorkspaceMember";
@ -13,9 +13,16 @@ import MsProject from "../project/MsProject";
import OrganizationWorkspace from "../settings/organization/OrganizationWorkspace"; import OrganizationWorkspace from "../settings/organization/OrganizationWorkspace";
import PersonSetting from "../settings/personal/PersonSetting"; import PersonSetting from "../settings/personal/PersonSetting";
import SystemWorkspace from "../settings/system/SystemWorkspace"; import SystemWorkspace from "../settings/system/SystemWorkspace";
import MsChart from "../project/MsChart"; import PerformanceChart from "../project/PerformanceChart";
import AllTestReport from "../report/AllTestReport"; import PerformanceTestReport from "../report/PerformanceTestReport";
import ReportView from "../report/ReportView"; import FunctionalTestReport from "../report/FunctionalTestReport";
import FunctionalTest from "../testPlan/FunctionalTest";
import PerformanceTest from "../testPlan/PerformanceTest";
import EditFunctionalTestPlan from "../testPlan/EditFunctionalTestPlan";
import PerformanceTestHome from "../testPlan/PerformanceTestHome";
import FunctionalTestPlan from "../testPlan/FunctionalTestPlan";
import FunctionalTestHome from "../testPlan/FunctionalTestHome";
import PerformanceReportView from "../report/PerformanceReportView";
Vue.use(VueRouter); Vue.use(VueRouter);
@ -69,18 +76,27 @@ const router = new VueRouter({
] ]
}, },
{ {
path: "/createTest", path: "/functional",
name: "createTest", name: "functional",
redirect: "/functional/home",
components: { components: {
content: EditTestPlan content: FunctionalTest
} },
children: [
{
path: 'home',
name: 'fucHome',
component: FunctionalTestHome,
}, },
{ {
path: "/editTest/:testId", path: 'plan/create',
name: "editTest", name: "createFucTest",
components: { component: EditFunctionalTestPlan,
content: EditTestPlan
}, },
{
path: "plan/edit/:testId",
name: "editFucTest",
component: EditFunctionalTestPlan,
props: { props: {
content: (route) => { content: (route) => {
return { return {
@ -90,40 +106,79 @@ const router = new VueRouter({
} }
}, },
{ {
path: "/loadtest/:projectId", path: "plan/:projectId",
name: "loadtest", name: "fucPlan",
components: { component: FunctionalTestPlan
content: AllTestPlan
},
}, },
{ {
path: "/project/:type", path: "project/:type",
name: 'project', name: "fucProject",
component: MsProject
},
{
path: "report/:type",
name: "fucReport",
component: FunctionalTestReport
}
]
},
{
path: "/performance",
name: "performance",
redirect: "/performance/home",
components: { components: {
content: MsProject content: PerformanceTest
},
children: [
{
path: 'home',
name: 'perHome',
component: PerformanceTestHome,
},
{
path: 'plan/create',
name: "createPerTest",
component: EditPerformanceTestPlan,
},
{
path: "plan/edit/:testId",
name: "editPerTest",
component: EditPerformanceTestPlan,
props: {
content: (route) => {
return {
...route.params
}
}
} }
}, },
{ {
path: "/report/:type", path: "plan/:projectId",
name: 'report', name: "perPlan",
components: { component: PerformanceTestPlan
content: AllTestReport
}
}, },
{ {
path: "/chart", path: "project/:type",
name: 'chart', name: "perProject",
components: { component: MsProject
content: MsChart
}
}, },
{ {
path: "/reportView/:reportId", path: "report/:type",
name: "reportView", name: "perReport",
components: { component: PerformanceTestReport
content: ReportView
}
}, },
{
path: "chart",
name: "perChart",
component: PerformanceChart
},
{
path: "report/view/:reportId",
name: "perReportView",
component: PerformanceReportView
}
]
}
] ]
}); });

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="edit-testplan-container" v-loading="result.loading"> <div class="edit-testplan-container" >
<div class="main-content"> <div class="main-content">
<el-card> <el-card>
<el-row> <el-row>
@ -20,16 +20,12 @@
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button> <el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
</el-row> </el-row>
<el-tabs class="testplan-config" v-model="active" type="border-card" :stretch="true"> <el-tabs class="testplan-config" v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('load_test.basic_config')"> <el-tab-pane :label="$t('load_test.basic_config')">
<ms-test-plan-basic-config :test-plan="testPlan" ref="basicConfig"/> <functional-test-scene-config :test-plan="testPlan" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('load_test.pressure_config')"> <el-tab-pane :label="$t('load_test.runtime_config')">
<ms-test-plan-pressure-config :test-plan="testPlan" ref="pressureConfig"/> <functional-test-runtime-config :test-plan="testPlan" />
</el-tab-pane>
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
<ms-test-plan-advanced-config ref="advancedConfig"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
@ -38,16 +34,14 @@
</template> </template>
<script> <script>
import MsTestPlanBasicConfig from './components/BasicConfig'; import FunctionalTestSceneConfig from './components/FunctionalTestSceneConfig';
import MsTestPlanPressureConfig from './components/PressureConfig'; import FunctionalTestRuntimeConfig from './components/FunctionalTestRuntimeConfig';
import MsTestPlanAdvancedConfig from './components/AdvancedConfig';
export default { export default {
name: "MsEditTestPlan", name: "EditFunctionalTestPlan",
components: { components: {
MsTestPlanBasicConfig, FunctionalTestSceneConfig,
MsTestPlanPressureConfig, FunctionalTestRuntimeConfig,
MsTestPlanAdvancedConfig,
}, },
data() { data() {
return { return {
@ -77,19 +71,21 @@
watch: { watch: {
'$route'(to) { '$route'(to) {
// //
if (to.name === 'createTest') { if (to.name === 'createFucTest') {
window.location.reload(); window.location.reload();
return; return;
} }
let testId = to.path.split('/')[2]; // find testId let testId = to.path.split('/')[4]; // find testId
if (testId) {
this.$get('/testplan/get/' + testId, response => { this.$get('/testplan/get/' + testId, response => {
this.testPlan = response.data; this.testPlan = response.data;
}); });
} }
}
}, },
created() { created() {
let testId = this.$route.path.split('/')[2]; let testId = this.$route.path.split('/')[4];
if (testId) { if (testId) {
this.$get('/testplan/get/' + testId, response => { this.$get('/testplan/get/' + testId, response => {
this.testPlan = response.data; this.testPlan = response.data;

View File

@ -0,0 +1,255 @@
<template>
<div class="edit-testplan-container" v-loading="result.loading">
<div class="main-content">
<el-card>
<el-row>
<el-col :span="10">
<el-input :placeholder="$t('load_test.input_name')" v-model="testPlan.name" class="input-with-select">
<el-select v-model="testPlan.projectId" slot="prepend" :placeholder="$t('load_test.select_project')">
<el-option
v-for="item in projects"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-input>
</el-col>
<el-button type="primary" plain @click="save">{{$t('commons.save')}}</el-button>
<el-button type="primary" plain @click="saveAndRun">{{$t('load_test.save_and_run')}}</el-button>
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
</el-row>
<el-tabs class="testplan-config" v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('load_test.basic_config')">
<performance-basic-config :test-plan="testPlan" ref="basicConfig"/>
</el-tab-pane>
<el-tab-pane :label="$t('load_test.pressure_config')">
<performance-pressure-config :test-plan="testPlan" ref="pressureConfig"/>
</el-tab-pane>
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
<performance-advanced-config ref="advancedConfig"/>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</template>
<script>
import PerformanceBasicConfig from "./components/PerformanceBasicConfig";
import PerformancePressureConfig from "./components/PerformancePressureConfig";
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
export default {
name: "EditPerformanceTestPlan",
components: {
PerformancePressureConfig,
PerformanceBasicConfig,
PerformanceAdvancedConfig
},
data() {
return {
result: {},
testPlan: {},
listProjectPath: "/project/listAll",
savePath: "/testplan/save",
editPath: "/testplan/edit",
runPath: "/testplan/run",
projects: [],
active: '0',
tabs: [{
title: this.$t('load_test.basic_config'),
id: '0',
component: 'PerformanceBasicConfig'
}, {
title: this.$t('load_test.pressure_config'),
id: '1',
component: 'PerformancePressureConfig'
}, {
title: this.$t('load_test.advanced_config'),
id: '2',
component: 'PerformanceAdvancedConfig'
}]
}
},
watch: {
'$route'(to) {
//
if (to.name === 'createPerTest') {
window.location.reload();
return;
}
let testId = to.path.split('/')[4]; // find testId
if (testId) {
this.$get('/testplan/get/' + testId, response => {
if(response.data){
this.testPlan = response.data;
}
});
}
}
},
created() {
let testId = this.$route.path.split('/')[4];
if (testId) {
this.$get('/testplan/get/' + testId, response => {
this.testPlan = response.data;
});
}
this.listProjects();
},
methods: {
listProjects() {
this.result = this.$get(this.listProjectPath, response => {
this.projects = response.data;
})
},
save() {
if (!this.validTestPlan()) {
return;
}
let options = this.getSaveOption();
this.result = this.$request(options, () => {
this.$message({
message: this.$t('commons.save_success'),
type: 'success'
});
this.$refs.advancedConfig.cancelAllEdit();
this.$router.push({path: '/performance/plan/all'})
});
},
saveAndRun() {
if (!this.validTestPlan()) {
return;
}
let options = this.getSaveOption();
this.result = this.$request(options, (response) => {
this.testPlan.id = response.data;
this.$message({
message: this.$t('commons.save_success'),
type: 'success'
});
this.result = this.$post(this.runPath, {id: this.testPlan.id}, () => {
this.$message({
message: this.$t('load_test.is_running'),
type: 'success'
});
})
});
},
getSaveOption() {
let formData = new FormData();
let url = this.testPlan.id ? this.editPath : this.savePath;
if (!this.testPlan.file.id) {
formData.append("file", this.testPlan.file);
}
//
this.testPlan.loadConfiguration = JSON.stringify(this.$refs.pressureConfig.convertProperty());
//
this.testPlan.advancedConfiguration = JSON.stringify(this.$refs.advancedConfig.configurations());
// filejson
let requestJson = JSON.stringify(this.testPlan, function (key, value) {
return key === "file" ? undefined : value
});
formData.append('request', new Blob([requestJson], {
type: "application/json"
}));
return {
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined
}
};
},
cancel() {
this.$router.push({path: '/performance/plan/all'})
},
validTestPlan() {
if (!this.testPlan.name) {
this.$message({
message: this.$t('load_test.test_name_is_null'),
type: 'error'
});
return false;
}
if (!this.testPlan.projectId) {
this.$message({
message: this.$t('load_test.project_is_null'),
type: 'error'
});
return false;
}
if (!this.testPlan.file) {
this.$message({
message: this.$t('load_test.jmx_is_null'),
type: 'error'
});
return false;
}
if (!this.$refs.advancedConfig.validConfig()) {
return false;
}
/// todo:
return true;
}
}
}
</script>
<style scoped>
.edit-testplan-container {
float: none;
text-align: center;
padding: 15px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.edit-testplan-container .main-content {
margin: 0 auto;
width: 100%;
max-width: 1200px;
}
.edit-testplan-container .testplan-config {
margin-top: 15px;
}
.el-select {
min-width: 130px;
}
.edit-testplan-container .input-with-select .el-input-group__prepend {
background-color: #fff;
}
.advanced-config {
height: calc(100vh - 280px);
overflow: auto;
}
</style>

View File

@ -4,7 +4,7 @@
<i class="el-icon-time"/> <i class="el-icon-time"/>
{{$t('load_test.recent')}} {{$t('load_test.recent')}}
</div> </div>
<el-menu-item :key="t.id" v-for="t in recentTestPlans" :index="'/editTest/' + t.id"> <el-menu-item :key="t.id" v-for="t in recentTestPlans" :index="'/functional/plan/edit/' + t.id">
{{ t.name }} {{ t.name }}
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
@ -15,7 +15,7 @@
import {hasRoles} from "../../../common/utils"; import {hasRoles} from "../../../common/utils";
export default { export default {
name: "MsRecentTestPlan", name: "PerformanceRecentTestPlan",
mounted() { mounted() {
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) { if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {

View File

@ -0,0 +1,31 @@
<template>
<el-col>
<header-menus :beaseUrl="beaseUrl"/>
<div>
<transition>
<keep-alive>
<router-view :beaseUrl="beaseUrl"/>
</keep-alive>
</transition>
</div>
</el-col>
</template>
<script>
import HeaderMenus from "../HeaderMenus";
export default {
name: "FunctionalTest",
components: {HeaderMenus},
data() {
return {
beaseUrl: "functional"
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
<h1>功能测试首页</h1>
</div>
</template>
<script>
export default {
name: "PerformanceTestHome"
}
</script>
<style scoped>
</style>

View File

@ -139,7 +139,7 @@
}, },
handleEdit(testPlan) { handleEdit(testPlan) {
this.$router.push({ this.$router.push({
path: '/editTest/' + testPlan.id, path: '/performance/plan/edit/' + testPlan.id,
}) })
}, },
handleDelete(testPlan) { handleDelete(testPlan) {

View File

@ -0,0 +1,42 @@
<template>
<el-menu router menu-trigger="click" :default-active="$route.path">
<div class="recent-text">
<i class="el-icon-time"/>
{{$t('load_test.recent')}}
</div>
<el-menu-item :key="t.id" v-for="t in recentTestPlans" :index="'/performance/plan/edit/' + t.id">
{{ t.name }}
</el-menu-item>
</el-menu>
</template>
<script>
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER} from "../../../common/constants";
export default {
name: "PerformanceRecentTestPlan",
mounted() {
const rolesString = localStorage.getItem("roles");
const roles = rolesString.split(',');
if (roles.indexOf(ROLE_TEST_MANAGER) > -1 || roles.indexOf(ROLE_TEST_USER) > -1 || roles.indexOf(ROLE_TEST_VIEWER) > -1) {
this.$get('/testplan/recent/5', (response) => {
this.recentTestPlans = response.data;
});
}
},
data() {
return {
recentTestPlans: []
}
}
}
</script>
<style scoped>
.recent-text {
padding-left: 10%;
color: #777777;
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<el-col>
<header-menus :beaseUrl="beaseUrl"/>
<div>
<transition>
<keep-alive>
<router-view :beaseUrl="beaseUrl"/>
</keep-alive>
</transition>
</div>
</el-col>
</template>
<script>
import HeaderMenus from "../HeaderMenus";
export default {
name: "PerformanceTest",
components: {HeaderMenus},
data() {
return {
beaseUrl: "performance"
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
<h1>性能测试首页</h1>
</div>
</template>
<script>
export default {
name: "PerformanceTestHome"
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,195 @@
<template>
<div class="testplan-container" v-loading="result.loading">
<div class="main-content">
<el-card>
<div slot="header">
<el-row type="flex" justify="space-between" align="middle">
<span class="title">{{$t('commons.test')}}</span>
<span class="search">
<el-input type="text" size="small" :placeholder="$t('load_test.search_by_name')"
prefix-icon="el-icon-search"
maxlength="60"
v-model="condition" @change="search" clearable/>
</span>
</el-row>
</div>
<el-table :data="tableData" class="test-content">
<el-table-column
prop="name"
:label="$t('commons.name')"
width="150"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="description"
:label="$t('commons.description')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="projectName"
:label="$t('load_test.project_name')"
width="150"
show-overflow-tooltip>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.create_time')">
<template slot-scope="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.update_time')">
<template slot-scope="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="150"
:label="$t('commons.operating')">
<template slot-scope="scope">
<el-button @click="handleEdit(scope.row)" type="primary" icon="el-icon-edit" size="mini" circle/>
<el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
</template>
</el-table-column>
</el-table>
<div>
<el-row>
<el-col :span="22" :offset="1">
<div class="table-page">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[5, 10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</el-col>
</el-row>
</div>
</el-card>
</div>
</div>
</template>
<script>
export default {
data() {
return {
result: {},
queryPath: "/testplan/list",
deletePath: "/testplan/delete",
condition: "",
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
testId: null,
}
},
watch: {
'$route'(to) {
this.projectId = to.params.projectId;
this.initTableData();
}
},
created: function () {
this.projectId = this.$route.params.projectId;
this.initTableData();
},
methods: {
initTableData() {
let param = {
name: this.condition,
};
if (this.projectId !== 'all') {
param.projectId = this.projectId;
}
this.result = this.$post(this.buildPagePath(this.queryPath), param, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
search() {
this.initTableData();
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleSizeChange(size) {
this.pageSize = size;
this.initTableData();
},
handleCurrentChange(current) {
this.currentPage = current;
this.initTableData();
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(testPlan) {
this.$router.push({
path: '/performance/plan/edit/' + testPlan.id,
})
},
handleDelete(testPlan) {
this.$alert(this.$t('load_test.delete_confirm') + testPlan.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this._handleDelete(testPlan);
}
}
});
},
_handleDelete(testPlan) {
let data = {
id: testPlan.id
};
this.result = this.$post(this.deletePath, data, () => {
this.$message({
message: this.$t('commons.delete_success'),
type: 'success'
});
this.initTableData();
});
},
}
}
</script>
<style scoped>
.testplan-container {
padding: 15px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
.main-content {
margin: 0 auto;
width: 100%;
max-width: 1200px;
}
.test-content {
width: 100%;
}
.table-page {
padding-top: 20px;
margin-right: -9px;
float: right;
}
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
</div>
</template>
<script>
export default {
name: "FunctionalTestRuntimeConfig"
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
</div>
</template>
<script>
export default {
name: "FunctionalTestSceneConfig"
}
</script>
<style scoped>
</style>

View File

@ -200,7 +200,7 @@
<script> <script>
export default { export default {
name: "MsTestPlanAdvancedConfig", name: "PerformanceAdvancedConfig",
data() { data() {
return { return {
timeout: 10, timeout: 10,
@ -211,14 +211,17 @@
} }
}, },
mounted() { mounted() {
let testId = this.$route.path.split('/')[2]; let testId = this.$route.path.split('/')[4];
if (testId) { if (testId) {
this.getAdvancedConfig(testId); this.getAdvancedConfig(testId);
} }
}, },
watch: { watch: {
'$route'(to) { '$route'(to, from) {
let testId = to.path.split('/')[2]; if(from.name != 'createPerTest' || from.name != 'editPerTest'){
return;
}
let testId = to.path.split('/')[4];
if (testId) { if (testId) {
this.getAdvancedConfig(testId); this.getAdvancedConfig(testId);
} }

View File

@ -56,7 +56,7 @@
import {Message} from "element-ui"; import {Message} from "element-ui";
export default { export default {
name: "MsTestPlanBasicConfig", name: "PerformanceBasicConfig",
props: ["testPlan"], props: ["testPlan"],
data() { data() {
return { return {

View File

@ -91,7 +91,7 @@
const RPS_LIMIT = "rpsLimit"; const RPS_LIMIT = "rpsLimit";
export default { export default {
name: "MsTestPlanPressureConfig", name: "PerformancePressureConfig",
data() { data() {
return { return {
testPlan: {}, testPlan: {},
@ -104,7 +104,7 @@
} }
}, },
mounted() { mounted() {
let testId = this.$route.path.split('/')[2]; let testId = this.$route.path.split('/')[4];
if (testId) { if (testId) {
this.getLoadConfig(testId); this.getLoadConfig(testId);
} else { } else {
@ -112,8 +112,11 @@
} }
}, },
watch: { watch: {
'$route'(to) { '$route'(to, from) {
let testId = to.path.split('/')[2]; if(from.name != 'createPerTest' || from.name != 'editPerTest'){
return;
}
let testId = to.path.split('/')[4];
if (testId) { if (testId) {
this.getLoadConfig(testId); this.getLoadConfig(testId);
} else { } else {
@ -123,8 +126,10 @@
}, },
methods: { methods: {
getLoadConfig(testId) { getLoadConfig(testId) {
if(testId) {
this.$get('/testplan/get-load-config/' + testId, (response) => { this.$get('/testplan/get-load-config/' + testId, (response) => {
if (response.data) { if (response.data && response.data != "") {
let data = JSON.parse(response.data); let data = JSON.parse(response.data);
data.forEach(d => { data.forEach(d => {
@ -158,6 +163,7 @@
this.calculateChart(); this.calculateChart();
} }
}); });
}
}, },
calculateChart() { calculateChart() {
if (this.duration < this.rampUpTime) { if (this.duration < this.rampUpTime) {