This commit is contained in:
chenjianxing 2020-05-18 17:33:29 +08:00
commit 81922206ca
43 changed files with 930 additions and 225 deletions

View File

@ -10,11 +10,13 @@ import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.DashboardTestDTO;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@ -34,6 +36,11 @@ public class APIReportController {
return apiReportService.recentTest(request);
}
@GetMapping("/list/{testId}")
public List<APIReportResult> listByTestId(@PathVariable String testId) {
return apiReportService.listByTestId(testId);
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<APIReportResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPIReportRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
@ -51,4 +58,10 @@ public class APIReportController {
apiReportService.delete(request);
}
@GetMapping("dashboard/tests")
public List<DashboardTestDTO> dashboardTests() {
return apiReportService.dashboardTests(SessionUtils.getCurrentWorkspaceId());
}
}

View File

@ -10,9 +10,12 @@ import io.metersphere.base.domain.ApiTestWithBLOBs;
import io.metersphere.base.mapper.ApiTestReportMapper;
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.dto.DashboardTestDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.UUID;
@ -62,4 +65,11 @@ public class APIReportService {
report.setStatus(APITestStatus.Completed.name());
apiTestReportMapper.insert(report);
}
public List<DashboardTestDTO> dashboardTests(String workspaceId) {
Instant oneYearAgo = Instant.now().plus(-365, ChronoUnit.DAYS);
long startTimestamp = oneYearAgo.toEpochMilli();
return extApiTestReportMapper.selectDashboardTests(workspaceId, startTimestamp);
}
}

View File

@ -3,6 +3,7 @@ package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.dto.ApiReportDTO;
import io.metersphere.dto.DashboardTestDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -15,4 +16,6 @@ public interface ExtApiTestReportMapper {
APIReportResult get(@Param("id") String id);
List<DashboardTestDTO> selectDashboardTests(@Param("workspaceId") String workspaceId, @Param("startTimestamp") long startTimestamp);
}

View File

@ -29,7 +29,7 @@
</select>
<select id="listByTestId" resultMap="BaseResultMap">
SELECT c,
SELECT t.name AS test_name,
r.name, r.description, r.id, r.test_id, r.create_time, r.update_time, r.status,
project.name AS project_name
FROM api_test_report r JOIN api_test t ON r.test_id = t.id
@ -50,4 +50,16 @@
ORDER BY r.update_time DESC
</select>
<select id="selectDashboardTests" resultType="io.metersphere.dto.DashboardTestDTO">
SELECT create_time AS date, count(api_test_report.id) AS count,
date_format(from_unixtime(create_time / 1000), '%Y-%m-%d') AS x
FROM api_test_report
WHERE test_id IN (SELECT api_test.id
FROM api_test
JOIN project ON api_test.project_id = project.id
WHERE workspace_id = #{workspaceId,jdbcType=VARCHAR})
AND create_time > #{startTimestamp}
GROUP BY x
</select>
</mapper>

View File

@ -1,6 +1,7 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.LoadTestReport;
import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.dto.ReportDTO;
import io.metersphere.performance.controller.request.ReportRequest;
import org.apache.ibatis.annotations.Param;
@ -16,4 +17,6 @@ public interface ExtLoadTestReportMapper {
int appendLine(@Param("testId") String id, @Param("line") String line);
LoadTestReport selectByPrimaryKey(String id);
List<DashboardTestDTO> selectDashboardTests(@Param("workspaceId") String workspaceId, @Param("startTimestamp") long startTimestamp);
}

View File

@ -48,10 +48,22 @@
</update>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from load_test_report
where id = #{id,jdbcType=VARCHAR}
SELECT
<include refid="Base_Column_List"/>
FROM load_test_report
WHERE id = #{id,jdbcType=VARCHAR}
</select>
<select id="selectDashboardTests" resultType="io.metersphere.dto.DashboardTestDTO">
SELECT create_time AS date, count(load_test_report.id) AS count,
date_format(from_unixtime(create_time / 1000), '%Y-%m-%d') AS x
FROM load_test_report
WHERE test_id IN (SELECT load_test.id
FROM load_test
JOIN project ON load_test.project_id = project.id
WHERE workspace_id = #{workspaceId,jdbcType=VARCHAR})
AND create_time > #{startTimestamp}
GROUP BY x
</select>
</mapper>

View File

@ -0,0 +1,11 @@
package io.metersphere.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class DashboardTestDTO {
private Long date;
private Integer count;
}

View File

@ -7,6 +7,7 @@ import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.dto.LoadTestDTO;
import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.service.FileService;
@ -101,4 +102,9 @@ public class PerformanceTestController {
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"")
.body(bytes);
}
@GetMapping("dashboard/tests")
public List<DashboardTestDTO> dashboardTests() {
return performanceTestService.dashboardTests(SessionUtils.getCurrentWorkspaceId());
}
}

View File

@ -8,6 +8,7 @@ import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.dto.LoadTestDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.performance.engine.Engine;
@ -23,6 +24,8 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ -276,4 +279,10 @@ public class PerformanceTestService {
return loadTestMapper.selectByExampleWithBLOBs(example);
}
public List<DashboardTestDTO> dashboardTests(String workspaceId) {
Instant oneYearAgo = Instant.now().plus(-365, ChronoUnit.DAYS);
long startTimestamp = oneYearAgo.toEpochMilli();
return extLoadTestReportMapper.selectDashboardTests(workspaceId, startTimestamp);
}
}

View File

@ -23,7 +23,8 @@
"vue-i18n": "^8.15.3",
"vue-router": "^3.1.3",
"vuedraggable": "^2.23.2",
"vuex": "^3.1.2"
"vuex": "^3.1.2",
"vue-calendar-heatmap": "^0.8.4"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",

View File

@ -73,7 +73,7 @@
return '/api/' + item.id;
},
router: function (item) {
return {name: 'fucPlan', params: {projectId: item.id, projectName: item.name}}
return {name: 'ApiTestList', params: {projectId: item.id, projectName: item.name}}
}
},
testRecent: {

View File

@ -0,0 +1,68 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">{{$t('api_report.title')}}</span>
</template>
<el-table :data="tableData" class="table-content">
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
<template v-slot:default="scope">
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-api-report-status :row="row"/>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import MsApiReportStatus from "../report/ApiReportStatus";
export default {
name: "MsApiReportRecentList",
components: {MsApiReportStatus},
data() {
return {
result: {},
tableData: [],
loading: false
}
},
methods: {
search() {
this.result = this.$get("/api/report/recent/5", response => {
this.tableData = response.data;
});
},
link(row) {
this.$router.push({
path: '/api/report/view/' + row.id,
})
}
},
created() {
this.search();
}
}
</script>
<style scoped>
</style>

View File

@ -1,16 +1,57 @@
<template>
<div>
<h1>API测试首页</h1>
</div>
<ms-container>
<ms-main-container v-loading="result.loading">
<el-row :gutter="20">
<el-col :span="12">
<ms-api-test-recent-list/>
</el-col>
<el-col :span="12">
<ms-api-report-recent-list/>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<ms-test-heatmap :values="values"/>
</el-col>
</el-row>
</ms-main-container>
</ms-container>
</template>
<script>
export default {
name: "ApiTestHome"
}
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestRecentList from "./ApiTestRecentList";
import MsApiReportRecentList from "./ApiReportRecentList";
import MsTestHeatmap from "../../common/components/MsTestHeatmap";
export default {
name: "ApiTestHome",
components: {MsTestHeatmap, MsApiReportRecentList, MsApiTestRecentList, MsMainContainer, MsContainer},
data() {
return {
values: [],
result: {},
}
},
mounted() {
this.result = this.$get('/api/report/dashboard/tests', response => {
this.values = response.data;
});
},
}
</script>
<style scoped>
.el-row {
margin-bottom: 20px;
}
.el-row:last-child {
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,69 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">{{$t('api_test.title')}}</span>
</template>
<el-table :data="tableData" class="table-content">
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
<template v-slot:default="scope">
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
</template>
</el-table-column>
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip/>
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-api-test-status :row="row"/>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import MsApiTestStatus from "../test/ApiTestStatus";
export default {
name: "MsApiTestRecentList",
components: {MsApiTestStatus},
data() {
return {
result: {},
tableData: [],
loading: false
}
},
methods: {
search() {
this.result = this.$get("/api/recent/5", response => {
this.tableData = response.data;
});
},
link(row) {
this.$router.push({
path: '/api/test/edit?id=' + row.id,
})
}
},
created() {
this.search();
}
}
</script>
<style scoped>
</style>

View File

@ -3,70 +3,27 @@
<ms-main-container>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
<ms-table-header :condition.sync="condition" @search="search" :title="$t('api_report.title')"
:show-create="false"/>
</template>
<el-table :data="tableData" class="table-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
width="250"
:label="$t('commons.create_time')">
<el-table-column prop="name" :label="$t('commons.name')" width="150" show-overflow-tooltip/>
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.update_time')">
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
prop="status"
:label="$t('commons.status')">
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<span v-else>
{{ row.status }}
</span>
<ms-api-report-status :row="row"/>
</template>
</el-table-column>
<el-table-column
width="150"
:label="$t('commons.operating')">
<el-table-column width="150" :label="$t('commons.operating')">
<template v-slot:default="scope">
<el-button @click="handleView(scope.row)" type="primary" icon="el-icon-s-data" size="mini" circle/>
<el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
@ -85,14 +42,14 @@
import MsTableHeader from "../../common/components/MsTableHeader";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiReportStatus from "./ApiReportStatus";
export default {
components: {MsMainContainer, MsContainer, MsTableHeader, MsTablePagination},
components: {MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination},
data() {
return {
result: {},
condition: {name: ""},
projectId: null,
tableData: [],
multipleSelection: [],
currentPage: 1,
@ -102,11 +59,8 @@
}
},
beforeRouteEnter(to, from, next) {
next(self => {
self.testId = to.params.testId;
self.search();
});
watch: {
'$route': 'init',
},
methods: {
@ -146,7 +100,15 @@
}
}
});
},
init() {
this.testId = this.$route.params.testId;
this.search();
}
},
created() {
this.init();
}
}
</script>

View File

@ -0,0 +1,38 @@
<template>
<div>
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<el-tag v-else size="mini" type="info">
{{ row.status }}
</el-tag>
</div>
</template>
<script>
export default {
name: "MsApiReportStatus",
props: {
row: Object
}
}
</script>
<style scoped>
</style>

View File

@ -75,7 +75,7 @@
}
.scenario-result + .scenario-result {
border-top: 1px solid #EBEEF5;
border-top: 1px solid #DCDFE6;
}
.scenario-result .info {

View File

@ -0,0 +1,75 @@
<template>
<div class="relate_report">
<el-button type="success" plain @click="search">{{$t('api_report.title')}}</el-button>
<el-dialog :title="$t('api_report.title')" :visible.sync="reportVisible">
<el-table :data="tableData" v-loading="result.loading">
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
<template v-slot:default="scope">
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-api-report-status :row="row"/>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
import MsApiReportStatus from "../report/ApiReportStatus";
export default {
name: "MsApiReportDialog",
components: {MsApiReportStatus},
props: ["testId"],
data() {
return {
reportVisible: false,
result: {},
tableData: [],
loading: false
}
},
methods: {
search() {
this.reportVisible = true;
let url = "/api/report/list/" + this.testId;
this.result = this.$get(url, response => {
this.tableData = response.data;
});
},
link(row) {
this.reportVisible = false;
this.$router.push({
path: '/api/report/view/' + row.id,
})
}
},
}
</script>
<style scoped>
.relate_report {
margin-left: 10px;
}
</style>

View File

@ -23,7 +23,10 @@
<el-button type="primary" plain v-if="isShowRun" @click="runTest">
{{$t('api_test.run')}}
</el-button>
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
<ms-api-report-dialog :test-id="id" v-if="test.status === 'Completed'"/>
</el-row>
</el-header>
<ms-api-scenario-config :scenarios="test.scenarioDefinition" ref="config"/>
@ -36,16 +39,19 @@
<script>
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import {Test} from "./model/ScenarioModel"
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
export default {
name: "MsApiTestConfig",
components: {MsApiScenarioConfig},
components: {MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
props: ["id"],
data() {
return {
reportVisible: false,
create: false,
result: {},
projects: [],
@ -89,6 +95,7 @@
id: item.id,
projectId: item.projectId,
name: item.name,
status: item.status,
scenarioDefinition: JSON.parse(item.scenarioDefinition),
});
this.$refs.config.reset();

View File

@ -3,76 +3,30 @@
<ms-main-container>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
<ms-table-header :condition.sync="condition" @search="search" :title="$t('api_test.title')"
@create="create" :createTip="$t('load_test.create')"/>
</template>
<el-table :data="tableData" class="table-content">
<el-table-column
prop="name"
:label="$t('commons.name')"
width="150"
show-overflow-tooltip>
<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 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')">
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
width="250"
:label="$t('commons.update_time')">
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
prop="status"
:label="$t('commons.status')">
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<span v-else>
{{ row.status }}
</span>
<ms-api-test-status :row="row"/>
</template>
</el-table-column>
<el-table-column
width="150"
:label="$t('commons.operating')">
<el-table-column width="150" :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator @editClick="handleEdit(scope.row)" @deleteClick="handleDelete(scope.row)"/>
</template>
@ -91,9 +45,10 @@
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestStatus from "./ApiTestStatus";
export default {
components: {MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator},
components: {MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator},
data() {
return {
result: {},
@ -108,11 +63,8 @@
}
},
beforeRouteEnter(to, from, next) {
next(self => {
self.projectId = to.params.projectId;
self.search();
});
watch: {
'$route': 'init'
},
methods: {
@ -155,7 +107,14 @@
}
}
});
},
init() {
this.projectId = this.$route.params.projectId;
this.search();
}
},
created() {
this.init();
}
}
</script>

View File

@ -0,0 +1,38 @@
<template>
<div>
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<el-tag v-else size="mini" type="info">
{{ row.status }}
</el-tag>
</div>
</template>
<script>
export default {
name: "MsApiTestStatus",
props: {
row: Object
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,64 @@
<template>
<el-card>
<template v-slot:header>
<span class="title">{{$t('commons.calendar_heatmap')}}</span>
</template>
<calendar-heatmap :end-date="endDate" :values="values" :locale="locale"
:tooltip-unit="unit"
:range-color="colorRange"/>
</el-card>
</template>
<script>
export default {
name: "MsTestHeatmap",
props: ['values'],
data() {
return {
endDate: new Date(),
unit: 'tests',
colorRange: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'],
locale: {
//
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
// Sun. Mon. Tues. Wed. Thur. Fri. Sat.
days: ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'],
No: 'No',
on: ',',
less: 'Less',
more: 'More'
},
}
},
mounted() {
this.locale.months = [
this.$t('commons.months_1'),
this.$t('commons.months_2'),
this.$t('commons.months_3'),
this.$t('commons.months_4'),
this.$t('commons.months_5'),
this.$t('commons.months_6'),
this.$t('commons.months_7'),
this.$t('commons.months_8'),
this.$t('commons.months_9'),
this.$t('commons.months_10'),
this.$t('commons.months_11'),
this.$t('commons.months_12')
];
this.locale.days = [
this.$t('commons.weeks_0'),
this.$t('commons.weeks_1'),
this.$t('commons.weeks_2'),
this.$t('commons.weeks_3'),
this.$t('commons.weeks_4'),
this.$t('commons.weeks_5'),
this.$t('commons.weeks_6')
];
this.unit = this.$t('commons.test_unit')
}
}
</script>
<style scoped>
</style>

View File

@ -34,7 +34,9 @@
},
watch: {
'$route'(to) {
this.activeIndex = to.matched[0].path;
if (to.matched.length > 0) {
this.activeIndex = to.matched[0].path;
}
this.handleSelect(this.activeIndex);
}
},

View File

@ -13,7 +13,6 @@
</template>
<script>
import {TokenKey} from '../../../../common/js/constants';
import {getCurrentUser} from "../../../../common/js/utils";
export default {
@ -32,7 +31,7 @@
break;
case "logout":
this.$get("/signout", function () {
localStorage.removeItem(TokenKey);
localStorage.clear();
window.location.href = "/login";
});
break;

View File

@ -0,0 +1,66 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">{{$t('api_report.title')}}</span>
</template>
<el-table :data="tableData" class="table-content">
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
<template v-slot:default="scope">
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-performance-report-status :row="row"/>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import MsPerformanceReportStatus from "../report/PerformanceReportStatus";
export default {
name: "MsPerformanceReportRecentList",
components: {MsPerformanceReportStatus},
data() {
return {
result: {},
tableData: []
}
},
methods: {
search() {
this.result = this.$get("/performance/report/recent/5", response => {
this.tableData = response.data;
});
},
link(row) {
this.$router.push({
path: '/performance/report/view/' + row.id,
})
}
},
created() {
this.search();
}
}
</script>
<style scoped>
</style>

View File

@ -1,16 +1,56 @@
<template>
<div>
<h1>性能测试首页</h1>
</div>
<ms-container>
<ms-main-container v-loading="result.loading">
<el-row :gutter="20">
<el-col :span="12">
<ms-performance-report-recent-list/>
</el-col>
<el-col :span="12">
<ms-performance-test-recent-list/>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<ms-test-heatmap :values="values"/>
</el-col>
</el-row>
</ms-main-container>
</ms-container>
</template>
<script>
export default {
name: "PerformanceTestHome"
}
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceTestRecentList from "./PerformanceTestRecentList"
import MsPerformanceReportRecentList from "./PerformanceReportRecentList"
import MsTestHeatmap from "../../common/components/MsTestHeatmap";
export default {
name: "PerformanceTestHome",
components: {
MsTestHeatmap,
MsMainContainer,
MsContainer,
MsPerformanceTestRecentList,
MsPerformanceReportRecentList
},
data() {
return {
values: [],
result: {},
}
},
mounted() {
this.result = this.$get('/performance/dashboard/tests', response => {
this.values = response.data;
});
},
}
</script>
<style scoped>
.el-row {
padding-bottom: 20px;
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<span class="title">{{$t('api_test.title')}}</span>
</template>
<el-table :data="tableData" class="table-content">
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
<template v-slot:default="scope">
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
</template>
</el-table-column>
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip/>
<el-table-column width="250" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column width="250" :label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-performance-test-status :row="row"/>
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import MsPerformanceTestStatus from "../test/PerformanceTestStatus";
export default {
name: "MsPerformanceTestRecentList",
components: {MsPerformanceTestStatus},
data() {
return {
result: {},
tableData: []
}
},
methods: {
search() {
this.result = this.$get("/performance/recent/5", response => {
this.tableData = response.data;
});
},
link(row) {
this.$router.push({
path: '/performance/test/edit/' + row.id,
})
}
},
created() {
this.search();
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,41 @@
<template>
<div>
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<span v-else>
{{ row.status }}
</span>
</div>
</template>
<script>
export default {
name: "MsPerformanceReportStatus",
props: {
row: Object
}
}
</script>
<style scoped>
</style>

View File

@ -1,7 +1,7 @@
<template>
<div v-loading="result.loading" class="container">
<div class="main-content">
<el-card>
<ms-container>
<ms-main-container>
<el-card v-loading="result.loading">
<el-row>
<el-col :span="16">
<el-row>
@ -51,8 +51,8 @@
</el-tabs>
</el-card>
</div>
</div>
</ms-main-container>
</ms-container>
</template>
<script>
@ -60,6 +60,8 @@
import MsReportLogDetails from './components/LogDetails';
import MsReportRequestStatistics from './components/RequestStatistics';
import MsReportTestOverview from './components/TestOverview';
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
export default {
name: "PerformanceReportView",
@ -67,7 +69,9 @@
MsReportErrorLog,
MsReportLogDetails,
MsReportRequestStatistics,
MsReportTestOverview
MsReportTestOverview,
MsContainer,
MsMainContainer
},
data() {
return {

View File

@ -1,8 +1,7 @@
<template>
<div class="container" v-loading="result.loading">
<div class="main-content">
<el-card class="table-card">
<ms-container>
<ms-main-container>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<div>
<el-row type="flex" justify="space-between" align="middle">
@ -47,29 +46,7 @@
prop="status"
:label="$t('commons.status')">
<template v-slot:default="{row}">
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<span v-else>
{{ row.status }}
</span>
<ms-performance-report-status :row="row"/>
</template>
</el-table-column>
<el-table-column
@ -84,17 +61,19 @@
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</div>
</div>
</ms-main-container>
</ms-container>
</template>
<script>
import MsTablePagination from "../../common/pagination/TablePagination";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceReportStatus from "./PerformanceReportStatus";
export default {
name: "PerformanceTestReport",
components: {MsTablePagination},
components: {MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer},
created: function () {
this.initTableData();
},

View File

@ -1,7 +1,7 @@
<template>
<div class="container" v-loading="result.loading">
<div class="main-content">
<el-card>
<ms-container>
<ms-main-container>
<el-card v-loading="result.loading">
<el-row>
<el-col :span="10">
<el-input :placeholder="$t('load_test.input_name')" v-model="testPlan.name" class="input-with-select">
@ -37,21 +37,25 @@
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</ms-main-container>
</ms-container>
</template>
<script>
import PerformanceBasicConfig from "./components/PerformanceBasicConfig";
import PerformancePressureConfig from "./components/PerformancePressureConfig";
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
export default {
name: "EditPerformanceTestPlan",
components: {
PerformancePressureConfig,
PerformanceBasicConfig,
PerformanceAdvancedConfig
PerformanceAdvancedConfig,
MsContainer,
MsMainContainer
},
data() {
return {

View File

@ -1,7 +1,7 @@
<template>
<div class="container" v-loading="result.loading">
<div class="main-content">
<el-card class="table-card">
<ms-container>
<ms-main-container>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<div>
<el-row type="flex" justify="space-between" align="middle">
@ -46,32 +46,7 @@
prop="status"
:label="$t('commons.status')">
<template v-slot:default="{row}">
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<span v-else>
{{ row.status }}
</span>
<ms-performance-test-status :row="row"/>
</template>
</el-table-column>
<el-table-column
@ -85,16 +60,25 @@
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</div>
</div>
</ms-main-container>
</ms-container>
</template>
<script>
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceTestStatus from "./PerformanceTestStatus";
export default {
components: {MsTablePagination, MsTableOperator},
components: {
MsPerformanceTestStatus,
MsTablePagination,
MsTableOperator,
MsContainer,
MsMainContainer
},
data() {
return {
result: {},

View File

@ -0,0 +1,44 @@
<template>
<div>
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
{{ row.status }}
</el-tag>
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
{{ row.status }}
</el-tag>
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
<template v-slot:content>
<div>{{row.description}}</div>
</template>
<el-tag size="mini" type="danger">
{{ row.status }}
</el-tag>
</el-tooltip>
<span v-else>
{{ row.status }}
</span>
</div>
</template>
<script>
export default {
name: "MsPerformanceTestStatus",
props: {
row: Object
}
}
</script>
<style scoped>
</style>

View File

@ -9,7 +9,7 @@
<el-table :data="items" style="width: 100%">
<el-table-column prop="name" :label="$t('commons.name')"/>
<el-table-column prop="description" :label="$t('commons.description')"/>
<el-table-column prop="workspaceName" label="所属工作空间"/>
<el-table-column prop="workspaceName" :label="$t('project.owning_workspace')"/>
<el-table-column>
<template v-slot:default="scope">
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)"/>

View File

@ -35,7 +35,7 @@
padding: 20px;
}
.ms-main-container {
ms-main-container {
height: calc(100vh - 40px);
}

View File

@ -12,10 +12,10 @@
<!--Personal information menu-->
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="ID"/>
<el-table-column prop="name" :label="$t('commons.username')" width="120"/>
<el-table-column prop="name" :label="$t('commons.username')"/>
<el-table-column prop="email" :label="$t('commons.email')"/>
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column prop="createTime" :label="$t('commons.create_time')" width="180">
<el-table-column prop="createTime" :label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>

View File

@ -8,7 +8,6 @@
</template>
<!-- system menu organization table-->
<el-table :data="tableData" style="width: 100%">
<el-table-column type="selection" width="55"/>
<el-table-column prop="name" :label="$t('commons.name')"/>
<el-table-column prop="description" :label="$t('commons.description')"/>
<el-table-column :label="$t('commons.member')">

View File

@ -8,12 +8,11 @@
</template>
<el-table :data="tableData" style="width: 100%">
<el-table-column type="selection" width="55"/>
<el-table-column prop="id" label="ID"/>
<el-table-column prop="name" :label="$t('commons.username')" width="120"/>
<el-table-column prop="name" :label="$t('commons.username')"/>
<el-table-column prop="email" :label="$t('commons.email')"/>
<el-table-column prop="phone" :label="$t('commons.phone')"/>
<el-table-column prop="status" :label="$t('commons.status')" width="100">
<el-table-column prop="status" :label="$t('commons.status')">
<template v-slot:default="scope">
<el-switch v-model="scope.row.status"
active-color="#13ce66"

View File

@ -12,6 +12,7 @@ import i18n from "../i18n/i18n";
import store from "./store";
import {permission} from './permission'
import chart from "../common/js/chart";
import CalendarHeatmap from "../common/js/calendar-heatmap";
import '../common/css/menu-header.css';
import '../common/css/main.css';
import CKEditor from '@ckeditor/ckeditor5-vue';
@ -24,6 +25,7 @@ Vue.use(ElementUI, {
Vue.use(filters);
Vue.use(ajax);
Vue.use(chart);
Vue.use(CalendarHeatmap);
Vue.use(message);
Vue.use(CKEditor);

View File

@ -0,0 +1,7 @@
import {CalendarHeatmap} from 'vue-calendar-heatmap'
export default {
install(Vue) {
Vue.component('calendarHeatmap', CalendarHeatmap);
}
}

View File

@ -60,6 +60,27 @@ export default {
'title': 'Title',
'custom': 'Custom',
'select_date': 'Select date',
'calendar_heatmap': 'Calendar Heatmap',
'months_1': 'Jan',
'months_2': 'Feb',
'months_3': 'Mar',
'months_4': 'Apr',
'months_5': 'May',
'months_6': 'Jun',
'months_7': 'Jul',
'months_8': 'Aug',
'months_9': 'Sep',
'months_10': 'Oct',
'months_11': 'Nov',
'months_12': 'Dec',
'weeks_0': 'Sun',
'weeks_1': 'Mon',
'weeks_2': 'Tues',
'weeks_3': 'Wed',
'weeks_4': 'Thur',
'weeks_5': 'Fri',
'weeks_6': 'Sat',
'test_unit': 'tests',
},
workspace: {
'create': 'Create Workspace',
@ -212,6 +233,7 @@ export default {
'download_log_file': 'Download',
},
api_test: {
title: "Test",
save_and_run: "Save and Run",
run: "Run",
running: "Running",
@ -273,6 +295,7 @@ export default {
}
},
api_report: {
title: "Report",
request: "Request",
request_body: "Body",
request_headers: "Headers",

View File

@ -60,6 +60,27 @@ export default {
'title': '标题',
'custom': '自定义',
'select_date': '选择日期',
'calendar_heatmap': '测试日历',
'months_1': '一月',
'months_2': '二月',
'months_3': '三月',
'months_4': '四月',
'months_5': '五月',
'months_6': '六月',
'months_7': '七月',
'months_8': '八月',
'months_9': '九月',
'months_10': '十月',
'months_11': '十一月',
'months_12': '十二月',
'weeks_0': '周日',
'weeks_1': '周一',
'weeks_2': '周二',
'weeks_3': '周三',
'weeks_4': '周四',
'weeks_5': '周五',
'weeks_6': '周六',
'test_unit': '测试',
},
workspace: {
'create': '创建工作空间',
@ -212,6 +233,7 @@ export default {
'pressure_prediction_chart': '压力预估图',
},
api_test: {
title: "测试",
save_and_run: "保存并执行",
run: "执行",
running: "正在执行",
@ -273,6 +295,7 @@ export default {
}
},
api_report: {
title: "报告",
request: "请求",
request_body: "请求内容",
request_headers: "请求头",

View File

@ -60,6 +60,27 @@ export default {
'title': '標題',
'custom': '自定義',
'select_date': '選擇日期',
'calendar_heatmap': '測試日曆',
'months_1': '一月',
'months_2': '二月',
'months_3': '三月',
'months_4': '四月',
'months_5': '五月',
'months_6': '六月',
'months_7': '七月',
'months_8': '八月',
'months_9': '九月',
'months_10': '十月',
'months_11': '十一月',
'months_12': '十二月',
'weeks_0': '周日',
'weeks_1': '周一',
'weeks_2': '周二',
'weeks_3': '周三',
'weeks_4': '周四',
'weeks_5': '周五',
'weeks_6': '周六',
'test_unit': '測試',
},
workspace: {
'create': '創建工作空間',