fix(测试跟踪): 测试计划报告导出性能测试报告无法打开
--bug=1018685 --user=陈建星 【测试跟踪】测试计划-测试报告-导出-接口和性能的报告无法打开 https://www.tapd.cn/55049933/s/1273189
This commit is contained in:
parent
31ddfb94fc
commit
df34484cc6
|
@ -1,768 +0,0 @@
|
|||
<template>
|
||||
<!-- <div>-->
|
||||
<!-- <!– 基本配置 –>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-form :inline="true" :disabled="isReadOnly">-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <div>{{ $t('load_test.connect_timeout') }}</div>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input-number-->
|
||||
<!-- size="mini" v-model="timeout"-->
|
||||
<!-- controls-position="right"-->
|
||||
<!-- :min="0"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- ms-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-form :inline="true" :disabled="isReadOnly">-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <div>{{ $t('load_test.response_timeout') }}</div>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input-number-->
|
||||
<!-- size="mini" :min="0"-->
|
||||
<!-- controls-position="right"-->
|
||||
<!-- v-model="responseTimeout"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- ms-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-form :inline="true" :disabled="isReadOnly">-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <div>-->
|
||||
<!-- {{ $t('load_test.granularity') }}-->
|
||||
<!-- <el-popover-->
|
||||
<!-- placement="left"-->
|
||||
<!-- width="300"-->
|
||||
<!-- trigger="hover">-->
|
||||
<!-- <el-table :data="granularityData">-->
|
||||
<!-- <el-table-column property="start" :label="$t('load_test.duration')">-->
|
||||
<!-- <template v-slot:default="scope">-->
|
||||
<!-- <span>{{ scope.row.start }}S - {{ scope.row.end }}S</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column property="granularity" :label="$t('load_test.granularity')"/>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- <i slot="reference" class="el-icon-info pointer"/>-->
|
||||
<!-- </el-popover>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-select v-model="granularity" :placeholder="$t('commons.please_select')" size="mini"-->
|
||||
<!-- clearable>-->
|
||||
<!-- <el-option v-for="op in granularityData" :key="op.granularity" :label="op.granularity"-->
|
||||
<!-- :value="op.granularity"></el-option>-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-form :inline="true" :disabled="isReadOnly">-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <div>{{ $t('load_test.custom_http_code') }}</div>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini" v-model="statusCodeStr"-->
|
||||
<!-- :placeholder="$t('load_test.separated_by_commas')"-->
|
||||
<!-- @input="checkStatusCode"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-form>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <!– DNS –>-->
|
||||
<!-- <el-row type="flex" justify="start">-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <h3>{{ $t('load_test.domain_bind') }}</h3>-->
|
||||
<!-- <el-button icon="el-icon-circle-plus-outline"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- plain size="mini" @click="add('domains')">-->
|
||||
<!-- {{ $t('commons.add') }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-table :data="domains" size="mini" class="tb-edit" align="center" border highlight-current-row>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.domain')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- v-model="row.domain"-->
|
||||
<!-- :placeholder="$t('load_test.domain')"-->
|
||||
<!-- clearable>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- <span>{{ row.domain }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.ip')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- v-model="row.ip"-->
|
||||
<!-- :placeholder="$t('load_test.ip')"-->
|
||||
<!-- clearable></el-input>-->
|
||||
<!-- <span>{{ row.ip }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.enable')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-model="row.enable"-->
|
||||
<!-- inactive-color="#DCDFE6"-->
|
||||
<!-- >-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" :label="$t('load_test.operating')">-->
|
||||
<!-- <template v-slot:default="{row, $index}">-->
|
||||
<!-- <ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete"-->
|
||||
<!-- type="danger" :disabled="isReadOnly"-->
|
||||
<!-- @exec="del(row, 'domains', $index)"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <!– csv 配置 –>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <h3>CSVDataSet</h3>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-table :data="csvFiles" size="mini" class="tb-edit" align="center" border highlight-current-row>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- prop="name"-->
|
||||
<!-- :label="$t('commons.name')">-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" prop="csvSplit" :label="$t('load_test.csv_split')">-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-switch :disabled="isReadOnly" v-model="row.csvSplit"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" prop="csvHasHeader" :label="$t('load_test.csv_has_header')">-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-switch :disabled="isReadOnly || !row.csvSplit" v-model="row.csvHasHeader"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <!– 参数列表 –>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <h3>{{ $t('load_test.params') }}</h3>-->
|
||||
<!-- <el-button icon="el-icon-circle-plus-outline"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- plain size="mini" @click="add('params')">-->
|
||||
<!-- {{ $t('commons.add') }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-table :data="params" size="mini" class="tb-edit" align="center" border highlight-current-row>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.param_name')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- v-model="row.name"-->
|
||||
<!-- :placeholder="$t('load_test.param_name')"-->
|
||||
<!-- clearable>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- <span>{{ row.name }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- :label="$t('load_test.param_value')"-->
|
||||
<!-- show-overflow-tooltip align="center">-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- v-model="row.value"-->
|
||||
<!-- :placeholder="$t('load_test.param_value')"-->
|
||||
<!-- clearable></el-input>-->
|
||||
<!-- <span>{{ row.value }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.enable')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-model="row.enable"-->
|
||||
<!-- inactive-color="#DCDFE6">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" :label="$t('load_test.operating')">-->
|
||||
<!-- <template v-slot:default="{row, $index}">-->
|
||||
<!-- <ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete"-->
|
||||
<!-- type="danger"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- @exec="del(row, 'params', $index)"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <!– JMeter Properties –>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <h3>JMeter Properties</h3>-->
|
||||
<!-- <el-button icon="el-icon-circle-plus-outline"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- plain size="mini" @click="add('properties')">-->
|
||||
<!-- {{ $t('commons.add') }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-table :data="properties" size="mini" class="tb-edit" align="center" border highlight-current-row>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.param_name')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- v-model="row.name"-->
|
||||
<!-- :placeholder="$t('load_test.param_name')"-->
|
||||
<!-- clearable>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- <span>{{ row.name }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- :label="$t('load_test.param_value')"-->
|
||||
<!-- show-overflow-tooltip align="center">-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- v-model="row.value"-->
|
||||
<!-- :placeholder="$t('load_test.param_value')"-->
|
||||
<!-- clearable></el-input>-->
|
||||
<!-- <span>{{ row.value }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.enable')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-model="row.enable"-->
|
||||
<!-- inactive-color="#DCDFE6">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" :label="$t('load_test.operating')">-->
|
||||
<!-- <template v-slot:default="{row, $index}">-->
|
||||
<!-- <ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete"-->
|
||||
<!-- type="danger"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- @exec="del(row, 'properties', $index)"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <!– System Properties –>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <h3>System Properties</h3>-->
|
||||
<!-- <el-button icon="el-icon-circle-plus-outline"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- plain size="mini" @click="add('systemProperties')">-->
|
||||
<!-- {{ $t('commons.add') }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-table :data="systemProperties" size="mini" class="tb-edit" align="center" border highlight-current-row>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.param_name')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- v-model="row.name"-->
|
||||
<!-- :placeholder="$t('load_test.param_name')"-->
|
||||
<!-- clearable>-->
|
||||
<!-- </el-input>-->
|
||||
<!-- <span>{{ row.name }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- :label="$t('load_test.param_value')"-->
|
||||
<!-- show-overflow-tooltip align="center">-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-input-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-if="!isReadOnly"-->
|
||||
<!-- type="textarea"-->
|
||||
<!-- class="edit-input"-->
|
||||
<!-- :rows="1"-->
|
||||
<!-- v-model="row.value"-->
|
||||
<!-- :placeholder="$t('load_test.param_value')"-->
|
||||
<!-- clearable></el-input>-->
|
||||
<!-- <span>{{ row.value }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- :label="$t('load_test.enable')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- <template v-slot:default="{row}">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- v-model="row.enable"-->
|
||||
<!-- inactive-color="#DCDFE6">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" :label="$t('load_test.operating')">-->
|
||||
<!-- <template v-slot:default="{row, $index}">-->
|
||||
<!-- <ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete"-->
|
||||
<!-- type="danger"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- @exec="del(row, 'systemProperties', $index)"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <!– 监控配置 –>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="8">-->
|
||||
<!-- <h3>{{ $t('commons.monitor') }}</h3>-->
|
||||
<!-- <el-button icon="el-icon-circle-plus-outline" :disabled="isReadOnly"-->
|
||||
<!-- plain size="mini" @click="addMonitor">-->
|
||||
<!-- {{ $t('commons.add') }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button icon="el-icon-circle-plus-outline" plain size="mini"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- @click="batchAddMonitor">-->
|
||||
<!-- {{ $t('commons.batch_add') }}-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <el-table :data="monitorParams" size="mini" class="tb-edit" border highlight-current-row>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- prop="name"-->
|
||||
<!-- :label="$t('commons.name')">-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- prop="ip"-->
|
||||
<!-- label="IP">-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- prop="port"-->
|
||||
<!-- label="Port">-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column-->
|
||||
<!-- align="center"-->
|
||||
<!-- prop="description"-->
|
||||
<!-- :label="$t('commons.description')">-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column align="center" :label="$t('load_test.operating')">-->
|
||||
<!-- <template v-slot:default="{row, $index}">-->
|
||||
<!-- <ms-table-operator-button tip="编辑" icon="el-icon-edit"-->
|
||||
<!-- type="primary"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- @exec="modifyMonitor(row, $index)"/>-->
|
||||
<!-- <ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete"-->
|
||||
<!-- :disabled="isReadOnly"-->
|
||||
<!-- type="danger"-->
|
||||
<!-- @exec="delMonitor(row, $index)"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- </el-table>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<!-- <edit-monitor ref="monitorDialog" :testId="testId" :list.sync="monitorParams"/>-->
|
||||
<!-- <batch-add-monitor ref="batchMonitorDialog" @batchSave="batchSave"/>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton.vue";
|
||||
// import EditMonitor from "./EditMonitor";
|
||||
// import BatchAddMonitor from "./BatchAddMonitor";
|
||||
// import {getAdvancedConfig} from "@/api/performance";
|
||||
|
||||
export default {
|
||||
name: "PerformanceAdvancedConfig",
|
||||
components: {
|
||||
// BatchAddMonitor,
|
||||
// EditMonitor,
|
||||
MsTableOperatorButton},
|
||||
data() {
|
||||
return {
|
||||
timeout: undefined,
|
||||
responseTimeout: undefined,
|
||||
statusCode: [],
|
||||
domains: [],
|
||||
params: [],
|
||||
properties: [],
|
||||
systemProperties: [],
|
||||
monitorParams: [],
|
||||
csvFiles: [],
|
||||
csvConfig: [],
|
||||
statusCodeStr: '',
|
||||
granularity: undefined,
|
||||
granularityData: [
|
||||
{start: 0, end: 100, granularity: 1},
|
||||
{start: 101, end: 500, granularity: 5},
|
||||
{start: 501, end: 1000, granularity: 10},
|
||||
{start: 1001, end: 3000, granularity: 30},
|
||||
{start: 3001, end: 6000, granularity: 60},
|
||||
{start: 6001, end: 30000, granularity: 300},
|
||||
{start: 30001, end: 60000, granularity: 600},
|
||||
{start: 60001, end: 180000, granularity: 1800},
|
||||
{start: 180001, end: 360000, granularity: 3600},
|
||||
],
|
||||
};
|
||||
},
|
||||
props: {
|
||||
testId: String,
|
||||
reportId: {
|
||||
type: String
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isShare: Boolean,
|
||||
shareId: String,
|
||||
},
|
||||
mounted() {
|
||||
if (this.testId) {
|
||||
this.getAdvancedConfig();
|
||||
} else if (this.reportId) {
|
||||
this.getAdvancedConfig('report');
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
testId() {
|
||||
if (this.testId) {
|
||||
this.getAdvancedConfig();
|
||||
}
|
||||
},
|
||||
csvFiles() {
|
||||
this.refreshCsv();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAdvancedConfig(type) {
|
||||
getAdvancedConfig(type, this.testId, this.reportId, this.isShare, this.shareId)
|
||||
.then(response => {
|
||||
let data = JSON.parse(response.data);
|
||||
this.timeout = data.timeout;
|
||||
this.responseTimeout = data.responseTimeout;
|
||||
this.statusCode = data.statusCode || [];
|
||||
this.statusCodeStr = this.statusCode.join(',');
|
||||
this.domains = data.domains || [];
|
||||
this.params = data.params || [];
|
||||
this.granularity = data.granularity;
|
||||
this.monitorParams = data.monitorParams || [];
|
||||
this.properties = data.properties || [];
|
||||
this.systemProperties = data.systemProperties || [];
|
||||
this.csvConfig = data.csvConfig;
|
||||
this.refreshCsv();
|
||||
});
|
||||
},
|
||||
refreshCsv() {
|
||||
if (this.csvConfig && this.csvFiles) {
|
||||
this.csvFiles.forEach(f => {
|
||||
f.csvSplit = this.csvConfig[f.name]?.csvSplit;
|
||||
f.csvHasHeader = this.csvConfig[f.name]?.csvHasHeader;
|
||||
});
|
||||
}
|
||||
},
|
||||
add(dataName) {
|
||||
if (dataName === 'domains') {
|
||||
this[dataName].push({
|
||||
domain: 'fit2cloud.com',
|
||||
enable: true,
|
||||
ip: '127.0.0.1',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
if (dataName === 'params') {
|
||||
this[dataName].push({
|
||||
name: 'param1',
|
||||
enable: true,
|
||||
value: '0',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
if (dataName === 'properties') {
|
||||
this[dataName].push({
|
||||
name: 'prop1',
|
||||
enable: true,
|
||||
value: '0',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
if (dataName === 'systemProperties') {
|
||||
this[dataName].push({
|
||||
name: 'prop1',
|
||||
enable: true,
|
||||
value: '0',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
edit(row) {
|
||||
row.edit = !row.edit;
|
||||
},
|
||||
del(row, dataName, index) {
|
||||
this[dataName].splice(index, 1);
|
||||
},
|
||||
confirmEdit(row) {
|
||||
row.edit = false;
|
||||
row.enable = true;
|
||||
},
|
||||
groupBy(data, key) {
|
||||
// return data.reduce((p, c) => {
|
||||
// let name = c[key];
|
||||
// if (!Object.prototype.hasOwnProperty.call(p, name)) {
|
||||
// p[name] = 0;
|
||||
// }
|
||||
// p[name]++;
|
||||
// return p;
|
||||
// }, {});
|
||||
},
|
||||
validConfig() {
|
||||
let counts = this.groupBy(this.domains, 'domain');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.domain_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
counts = this.groupBy(this.params, 'name');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.param_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
counts = this.groupBy(this.properties, 'name');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.param_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.domains.filter(d => !d.domain || !d.ip).length > 0) {
|
||||
this.$error(this.$t('load_test.domain_ip_is_empty'));
|
||||
return false;
|
||||
}
|
||||
if (this.params.filter(d => !d.name || !d.value).length > 0) {
|
||||
this.$error(this.$t('load_test.param_name_value_is_empty'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
checkStatusCode() {
|
||||
let license_num = this.statusCodeStr;
|
||||
license_num = license_num.replace(/[^\d,]/g, ''); // 清除“数字”和“.”以外的字符
|
||||
this.statusCodeStr = license_num;
|
||||
},
|
||||
cancelAllEdit() {
|
||||
this.domains.forEach(d => d.edit = false);
|
||||
this.params.forEach(d => d.edit = false);
|
||||
},
|
||||
configurations() {
|
||||
let statusCode = [];
|
||||
if (this.statusCodeStr) {
|
||||
statusCode = this.statusCodeStr.split(',');
|
||||
}
|
||||
return {
|
||||
timeout: this.timeout,
|
||||
responseTimeout: this.responseTimeout,
|
||||
statusCode: statusCode,
|
||||
params: this.params,
|
||||
properties: this.properties,
|
||||
systemProperties: this.systemProperties,
|
||||
csvConfig: this.csvFiles.reduce((result, curr) => {
|
||||
result[curr.name] = {csvHasHeader: curr.csvHasHeader, csvSplit: curr.csvSplit};
|
||||
return result;
|
||||
}, {}),
|
||||
domains: this.domains,
|
||||
granularity: this.granularity,
|
||||
monitorParams: this.monitorParams
|
||||
};
|
||||
},
|
||||
addMonitor() {
|
||||
this.$refs.monitorDialog.open();
|
||||
},
|
||||
batchAddMonitor() {
|
||||
this.$refs.batchMonitorDialog.open();
|
||||
},
|
||||
batchSave(params) {
|
||||
let targets = this._handleBatchVars(params);
|
||||
targets.forEach(row => {
|
||||
this.monitorParams.push(row);
|
||||
});
|
||||
},
|
||||
_handleBatchVars(data) {
|
||||
let params = data.split("\n");
|
||||
let keyValues = [];
|
||||
params.forEach(item => {
|
||||
let line = item.split(/,|,/);
|
||||
if (line.length < 3) {
|
||||
return;
|
||||
}
|
||||
let ipRe = new RegExp("^[0-9a-zA-Z,\.]*$");
|
||||
if (!ipRe.test(line[1])) {
|
||||
this.$message.warning("ip" + this.$t("commons.formatErr"));
|
||||
return;
|
||||
}
|
||||
let numRe = new RegExp("^[0-9]*$");
|
||||
if (!numRe.test(line[2])) {
|
||||
this.$message.warning("Port" + this.$t("commons.type_of_num"));
|
||||
return;
|
||||
}
|
||||
keyValues.push({
|
||||
name: line[0],
|
||||
ip: line[1],
|
||||
port: line[2],
|
||||
description: line[3] || '',
|
||||
});
|
||||
});
|
||||
return keyValues;
|
||||
},
|
||||
modifyMonitor(row, index) {
|
||||
this.$refs.monitorDialog.open(row, index);
|
||||
},
|
||||
delMonitor(row, index) {
|
||||
this.monitorParams.splice(index, 1);
|
||||
},
|
||||
refreshStatus() {
|
||||
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-row {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.tb-edit .el-textarea {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tb-edit .current-row .el-textarea {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tb-edit .current-row .el-textarea + span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-col {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.el-col .el-table {
|
||||
align: center;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.duration-input .el-input-number--mini {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.el-select--mini {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,388 +0,0 @@
|
|||
<template>
|
||||
<div v-loading="result.loading">
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
<h4>{{ $t('load_test.scenario_list') }}</h4>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="start" align="middle">
|
||||
<ms-table-button icon="el-icon-circle-plus-outline"
|
||||
:disabled="isReadOnly"
|
||||
:content="$t('load_test.load_exist_jmx')" @click="loadJMX()"/>
|
||||
<ms-table-button icon="el-icon-share"
|
||||
:disabled="isReadOnly"
|
||||
@click="loadApiAutomation()"
|
||||
:content="$t('load_test.load_api_automation_jmx')"/>
|
||||
</el-row>
|
||||
<el-table class="basic-config" :data="threadGroups.filter(tg=>tg.deleted=='false')">
|
||||
<el-table-column
|
||||
:label="$t('load_test.scenario_name')">
|
||||
<template v-slot:default="{row}">
|
||||
{{ row.attributes.testname }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Enable/Disable">
|
||||
<template v-slot:default="{row}">
|
||||
<el-switch v-model="row.enabled"
|
||||
inactive-color="#DCDFE6"
|
||||
active-value="true"
|
||||
inactive-value="false"
|
||||
:disabled="isReadOnly || threadGroupDisable(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="CSVDataSet">
|
||||
<template v-slot:default="scope">
|
||||
<ms-tag v-for="(f, index) in scope.row.csvFiles"
|
||||
:key="index"
|
||||
effect="light"
|
||||
:content="f"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('load_test.thread_group')">
|
||||
<template v-slot:default="{row}">
|
||||
<span v-if="row.tgType === 'PostThreadGroup' || row.tgType === 'SetupThreadGroup'">
|
||||
{{ row.tgType }}
|
||||
</span>
|
||||
<el-select v-else v-model="row.tgType"
|
||||
:disabled="isReadOnly"
|
||||
:placeholder="$t('commons.please_select')" size="small"
|
||||
@change="tgTypeChange(row)">
|
||||
<el-option v-for="tg in threadGroupForSelect" :key="tg.tagName" :label="tg.name"
|
||||
:value="tg.testclass"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-button :disabled="isReadOnly || threadGroupDisable(row)"
|
||||
@click="handleDeleteThreadGroup(row)"
|
||||
type="danger"
|
||||
icon="el-icon-delete" size="mini"
|
||||
circle/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
<h4>{{ $t('load_test.other_resource') }}</h4>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="start" align="middle">
|
||||
|
||||
<ms-table-button icon="el-icon-circle-plus-outline"
|
||||
:disabled="isReadOnly"
|
||||
:content="$t('load_test.load_exist_file')" @click="loadFile()"/>
|
||||
</el-row>
|
||||
<el-table class="basic-config" :data="tableData">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('load_test.file_name')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="size"
|
||||
:label="$t('load_test.file_size')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="type"
|
||||
:label="$t('load_test.file_type')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('load_test.last_modify_time')">
|
||||
<template v-slot:default="scope">
|
||||
<i v-if="scope.row.updateTime > 0" class="el-icon-time"/>
|
||||
<span class="last-modified">{{ scope.row.updateTime | datetimeFormat }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id || isReadOnly" type="primary"
|
||||
icon="el-icon-download"
|
||||
size="mini" circle/>
|
||||
<el-button :disabled="isReadOnly" @click="handleDelete(scope.row, scope.$index)" type="danger"
|
||||
icon="el-icon-delete" size="mini"
|
||||
circle/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<exist-files ref="existFiles"
|
||||
@fileChange="fileChange"
|
||||
:file-list="fileList"
|
||||
:table-data="tableData"
|
||||
:upload-list="uploadList"
|
||||
:is-read-only="isReadOnly"
|
||||
:scenarios="threadGroups"/>
|
||||
|
||||
<exist-scenarios ref="existScenarios"
|
||||
@fileChange="fileChange"
|
||||
:file-list="fileList"
|
||||
:table-data="tableData"
|
||||
:upload-list="uploadList"
|
||||
:scenarios="threadGroups"/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Message} from "element-ui";
|
||||
import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
|
||||
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
|
||||
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
|
||||
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||
import ExistFiles from "./ExistFiles";
|
||||
import ExistScenarios from "./ExistScenarios";
|
||||
// import {findThreadGroup} from "@/business/api/model/ThreadGroup";
|
||||
import {hasPermission} from "metersphere-frontend/src/utils/permission";
|
||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||
import {downloadFile, getFiles, getMetadataById} from "@/api/performance";
|
||||
|
||||
export default {
|
||||
name: "PerformanceBasicConfig",
|
||||
components: {
|
||||
MsTag,
|
||||
ExistScenarios, ExistFiles, MsDialogFooter, MsTableOperatorButton, MsTablePagination, MsTableButton
|
||||
},
|
||||
props: {
|
||||
test: {
|
||||
type: Object
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
isReadOnly: false,
|
||||
projectLoadingResult: {},
|
||||
getFileMetadataPath: "/performance/file/metadata",
|
||||
getFileMetadataById: "/performance/file/getMetadataById",
|
||||
jmxDownloadPath: '/performance/file/download',
|
||||
jmxDeletePath: '/performance/file/delete',
|
||||
fileList: [],
|
||||
tableData: [],
|
||||
uploadList: [],
|
||||
metadataIdList: [],
|
||||
fileNumLimit: 10,
|
||||
threadGroups: [],
|
||||
loadFileVisible: false,
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
existFiles: [],
|
||||
apiScenarios: [],
|
||||
loadApiAutomationVisible: false,
|
||||
selectIds: new Set(),
|
||||
threadGroupForSelect: [
|
||||
{
|
||||
name: 'ThreadGroup',
|
||||
tagName: 'ThreadGroup',
|
||||
testclass: 'ThreadGroup',
|
||||
guiclass: 'ThreadGroupGui'
|
||||
},
|
||||
{
|
||||
name: 'ConcurrencyThreadGroup',
|
||||
tagName: 'com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup',
|
||||
testclass: 'com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup',
|
||||
guiclass: "com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroupGui"
|
||||
},
|
||||
]
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.test.id) {
|
||||
this.getFileMetadata(this.test);
|
||||
}
|
||||
this.isReadOnly = !hasPermission('PROJECT_PERFORMANCE_TEST:READ+EDIT');
|
||||
},
|
||||
watch: {
|
||||
test() {
|
||||
if (this.test.id) {
|
||||
this.getFileMetadata(this.test);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFileMetadata(test) {
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
this.uploadList = [];
|
||||
this.metadataIdList = [];
|
||||
getFiles(test.id)
|
||||
.then(response => {
|
||||
let files = response.data;
|
||||
if (!files) {
|
||||
Message.error({message: this.$t('load_test.related_file_not_found'), showClose: true});
|
||||
return;
|
||||
}
|
||||
// deep copy
|
||||
this.fileList = JSON.parse(JSON.stringify(files));
|
||||
this.tableData = JSON.parse(JSON.stringify(files));
|
||||
this.tableData.map(f => {
|
||||
f.size = (f.size / 1024).toFixed(2) + ' KB';
|
||||
});
|
||||
});
|
||||
},
|
||||
selectAttachFileById(metadataIdArr) {
|
||||
this.metadataIdList = metadataIdArr;
|
||||
for (let i = 0; i < metadataIdArr.length; i++) {
|
||||
let id = metadataIdArr[i];
|
||||
getMetadataById(id)
|
||||
.then(response => {
|
||||
let files = response.data;
|
||||
if (files) {
|
||||
this.fileList.push(JSON.parse(JSON.stringify(files)));
|
||||
this.tableData.push(JSON.parse(JSON.stringify(files)));
|
||||
this.tableData.map(f => {
|
||||
f.size = (f.size / 1024).toFixed(2) + ' KB';
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
handleDownload(file) {
|
||||
downloadFile(this.jmxDownloadPath, file)
|
||||
.then(response => {
|
||||
const content = response.data;
|
||||
const blob = new Blob([content]);
|
||||
if ("download" in document.createElement("a")) {
|
||||
// 非IE下载
|
||||
// chrome/firefox
|
||||
let aTag = document.createElement('a');
|
||||
aTag.download = file.name;
|
||||
aTag.href = URL.createObjectURL(blob);
|
||||
aTag.click();
|
||||
URL.revokeObjectURL(aTag.href);
|
||||
} else {
|
||||
// IE10+下载
|
||||
navigator.msSaveBlob(blob, this.filename);
|
||||
}
|
||||
}).catch(e => {
|
||||
Message.error({message: e.message, showClose: true});
|
||||
});
|
||||
},
|
||||
handleDelete(file) {
|
||||
this.$alert(this.$t('load_test.delete_file_confirm') + file.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this._handleDelete(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
_handleDelete(file) {
|
||||
let index = this.fileList.findIndex(f => f.name === file.name);
|
||||
if (index > -1) {
|
||||
this.fileList.splice(index, 1);
|
||||
}
|
||||
index = this.tableData.findIndex(f => f.name === file.name);
|
||||
if (index > -1) {
|
||||
this.tableData.splice(index, 1);
|
||||
}
|
||||
//
|
||||
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
|
||||
if (i > -1) {
|
||||
this.uploadList.splice(i, 1);
|
||||
}
|
||||
|
||||
let jmxIndex = this.threadGroups.findIndex(tg => tg.handler === file.name);
|
||||
while (jmxIndex !== -1) {
|
||||
this.threadGroups.splice(jmxIndex, 1);
|
||||
jmxIndex = this.threadGroups.findIndex(tg => tg.handler === file.name);
|
||||
}
|
||||
},
|
||||
handleDeleteThreadGroup(tg) {
|
||||
this.$alert(this.$t('load_test.delete_threadgroup_confirm') + tg.attributes.testname + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
tg.deleted = 'true';
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
threadGroupDisable(row) {
|
||||
return this.threadGroups.filter(tg => tg.enabled == 'true').length === 1 && row.enabled == 'true';
|
||||
},
|
||||
tgTypeChange(row) {
|
||||
this.$emit("tgTypeChange", row);
|
||||
},
|
||||
updatedFileList() {
|
||||
return this.fileList;// 表示修改了已经上传的文件列表
|
||||
},
|
||||
conversionMetadataIdList() {
|
||||
return this.metadataIdList;// 表示修改了已经上传的文件列表
|
||||
},
|
||||
fileSorts() {
|
||||
let fileSorts = {};
|
||||
this.tableData.forEach((f, index) => {
|
||||
fileSorts[f.name] = index;
|
||||
});
|
||||
return fileSorts;
|
||||
},
|
||||
loadJMX() {
|
||||
this.$refs.existFiles.open('jmx');
|
||||
},
|
||||
loadFile() {
|
||||
this.$refs.existFiles.open('resource');
|
||||
},
|
||||
loadApiAutomation() {
|
||||
this.$refs.existScenarios.open();
|
||||
},
|
||||
fileChange(threadGroups) {
|
||||
this.$emit('fileChange', threadGroups);
|
||||
},
|
||||
validConfig() {
|
||||
if (this.uploadList.length + this.fileList.length > this.fileNumLimit) {
|
||||
this.$refs.existFiles.handleExceed();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.threadGroups.filter(tg => tg.enabled == 'true').length === 0) {
|
||||
this.$error(this.$t('load_test.threadgroup_at_least_one'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
importScenario(scenarioId) {
|
||||
this.$refs.existScenarios.selectIds.add(scenarioId);
|
||||
},
|
||||
importCase(caseObj) {
|
||||
let suffixIndex = caseObj.name.lastIndexOf(".jmx");
|
||||
let jmxName = caseObj.name.substring(0, suffixIndex) + "_" + new Date().getTime() + ".jmx";
|
||||
let threadGroups = findThreadGroup(caseObj.xml, jmxName);
|
||||
threadGroups.forEach(tg => {
|
||||
tg.options = {};
|
||||
});
|
||||
this.fileChange(threadGroups);
|
||||
let file = new File([caseObj.xml], jmxName);
|
||||
this.uploadList.push(file);
|
||||
this.tableData.push({
|
||||
name: file.name,
|
||||
size: (file.size / 1024).toFixed(2) + ' KB',
|
||||
type: 'JMX',
|
||||
updateTime: file.lastModified,
|
||||
});
|
||||
},
|
||||
handleUpload() {
|
||||
// 从api创建的测试
|
||||
this.$refs.existScenarios.handleImport();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.basic-config {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.last-modified {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.el-dialog :deep( .el-dialog__body ) {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
</style>
|
|
@ -246,8 +246,8 @@
|
|||
|
||||
<script>
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
// import {findThreadGroup} from "@/business/api/model/ThreadGroup";
|
||||
import {getJmxContent, getLoadConfig, getResourcePools} from "@/api/performance";
|
||||
import {findThreadGroup} from "./ThreadGroup";
|
||||
// import {getJmxContent, getLoadConfig, getResourcePools} from "@/api/load-test";
|
||||
|
||||
const HANDLER = "handler";
|
||||
const THREAD_GROUP_TYPE = "tgType";
|
||||
|
@ -390,135 +390,135 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
getResourcePools() {
|
||||
getResourcePools(this.isShare)
|
||||
.then(response => {
|
||||
this.resourcePools = response.data;
|
||||
// 如果当前的资源池无效 设置 null
|
||||
if (response.data.filter(p => p.id === this.resourcePool).length === 0) {
|
||||
this.resourcePool = null;
|
||||
// 标记因资源池无效而将资源池ID置为null
|
||||
this.setPoolNull = true;
|
||||
}
|
||||
|
||||
this.resourcePoolChange();
|
||||
});
|
||||
// getResourcePools(this.isShare)
|
||||
// .then(response => {
|
||||
// this.resourcePools = response.data;
|
||||
// // 如果当前的资源池无效 设置 null
|
||||
// if (response.data.filter(p => p.id === this.resourcePool && p.performance).length === 0) {
|
||||
// this.resourcePool = null;
|
||||
// // 标记因资源池无效而将资源池ID置为null
|
||||
// this.setPoolNull = true;
|
||||
// }
|
||||
//
|
||||
// this.resourcePoolChange();
|
||||
// });
|
||||
},
|
||||
getLoadConfig() {
|
||||
getLoadConfig(this.testId, this.reportId, this.isShare)
|
||||
.then(response => {
|
||||
let data = JSON.parse(response.data);
|
||||
for (let i = 0; i < this.threadGroups.length; i++) {
|
||||
data[i].forEach(item => {
|
||||
switch (item.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadGroups[i].threadNumber = item.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.threadGroups[i].rampUpTime = item.value;
|
||||
break;
|
||||
case ITERATE_RAMP_UP:
|
||||
this.threadGroups[i].iterateRampUp = item.value;
|
||||
break;
|
||||
case DURATION:
|
||||
this.threadGroups[i].duration = item.value;
|
||||
break;
|
||||
case DURATION_HOURS:
|
||||
this.threadGroups[i].durationHours = item.value;
|
||||
break;
|
||||
case DURATION_MINUTES:
|
||||
this.threadGroups[i].durationMinutes = item.value;
|
||||
break;
|
||||
case DURATION_SECONDS:
|
||||
this.threadGroups[i].durationSeconds = item.value;
|
||||
break;
|
||||
case UNIT:
|
||||
this.threadGroups[i].unit = item.value;
|
||||
break;
|
||||
case STEPS:
|
||||
this.threadGroups[i].step = item.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.threadGroups[i].rpsLimit = item.value;
|
||||
break;
|
||||
case RPS_LIMIT_ENABLE:
|
||||
this.threadGroups[i].rpsLimitEnable = item.value;
|
||||
break;
|
||||
case THREAD_TYPE:
|
||||
this.threadGroups[i].threadType = item.value;
|
||||
break;
|
||||
case ITERATE_NUM:
|
||||
this.threadGroups[i].iterateNum = item.value;
|
||||
break;
|
||||
case ENABLED:
|
||||
this.threadGroups[i].enabled = item.value;
|
||||
break;
|
||||
case DELETED:
|
||||
this.threadGroups[i].deleted = item.value;
|
||||
break;
|
||||
case HANDLER:
|
||||
this.threadGroups[i].handler = item.value;
|
||||
break;
|
||||
case THREAD_GROUP_TYPE:
|
||||
this.threadGroups[i].tgType = item.value;
|
||||
break;
|
||||
case ON_SAMPLE_ERROR:
|
||||
this.threadGroups[i].onSampleError = item.value;
|
||||
break;
|
||||
case STRATEGY:
|
||||
this.threadGroups[i].strategy = item.value;
|
||||
break;
|
||||
case RESOURCE_NODE_INDEX:
|
||||
this.threadGroups[i].resourceNodeIndex = item.value;
|
||||
break;
|
||||
case RATIOS:
|
||||
this.threadGroups[i].ratios = item.value;
|
||||
break;
|
||||
case SERIALIZE_THREAD_GROUPS:
|
||||
this.serializeThreadGroups = item.value;// 所有的线程组值一样
|
||||
break;
|
||||
case AUTO_STOP:
|
||||
this.autoStop = item.value;// 所有的线程组值一样
|
||||
break;
|
||||
case AUTO_STOP_DELAY:
|
||||
this.autoStopDelay = item.value;// 所有的线程组值一样
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//
|
||||
this.$set(this.threadGroups[i], "unit", this.threadGroups[i].unit || 'S');
|
||||
this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION');
|
||||
this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1);
|
||||
this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10);
|
||||
this.$set(this.threadGroups[i], "enabled", this.threadGroups[i].enabled || 'true');
|
||||
this.$set(this.threadGroups[i], "deleted", this.threadGroups[i].deleted || 'false');
|
||||
this.$set(this.threadGroups[i], "onSampleError", this.threadGroups[i].onSampleError || 'continue');
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < this.threadGroups.length; i++) {
|
||||
let tg = this.threadGroups[i];
|
||||
tg.durationHours = Math.floor(tg.duration / 3600);
|
||||
tg.durationMinutes = Math.floor((tg.duration / 60 % 60));
|
||||
tg.durationSeconds = Math.floor((tg.duration % 60));
|
||||
}
|
||||
this.resourcePoolChange();
|
||||
this.calculateTotalChart();
|
||||
});
|
||||
// getLoadConfig(this.testId, this.reportId, this.isShare)
|
||||
// .then(response => {
|
||||
// let data = JSON.parse(response.data);
|
||||
// for (let i = 0; i < this.threadGroups.length; i++) {
|
||||
// data[i].forEach(item => {
|
||||
// switch (item.key) {
|
||||
// case TARGET_LEVEL:
|
||||
// this.threadGroups[i].threadNumber = item.value;
|
||||
// break;
|
||||
// case RAMP_UP:
|
||||
// this.threadGroups[i].rampUpTime = item.value;
|
||||
// break;
|
||||
// case ITERATE_RAMP_UP:
|
||||
// this.threadGroups[i].iterateRampUp = item.value;
|
||||
// break;
|
||||
// case DURATION:
|
||||
// this.threadGroups[i].duration = item.value;
|
||||
// break;
|
||||
// case DURATION_HOURS:
|
||||
// this.threadGroups[i].durationHours = item.value;
|
||||
// break;
|
||||
// case DURATION_MINUTES:
|
||||
// this.threadGroups[i].durationMinutes = item.value;
|
||||
// break;
|
||||
// case DURATION_SECONDS:
|
||||
// this.threadGroups[i].durationSeconds = item.value;
|
||||
// break;
|
||||
// case UNIT:
|
||||
// this.threadGroups[i].unit = item.value;
|
||||
// break;
|
||||
// case STEPS:
|
||||
// this.threadGroups[i].step = item.value;
|
||||
// break;
|
||||
// case RPS_LIMIT:
|
||||
// this.threadGroups[i].rpsLimit = item.value;
|
||||
// break;
|
||||
// case RPS_LIMIT_ENABLE:
|
||||
// this.threadGroups[i].rpsLimitEnable = item.value;
|
||||
// break;
|
||||
// case THREAD_TYPE:
|
||||
// this.threadGroups[i].threadType = item.value;
|
||||
// break;
|
||||
// case ITERATE_NUM:
|
||||
// this.threadGroups[i].iterateNum = item.value;
|
||||
// break;
|
||||
// case ENABLED:
|
||||
// this.threadGroups[i].enabled = item.value;
|
||||
// break;
|
||||
// case DELETED:
|
||||
// this.threadGroups[i].deleted = item.value;
|
||||
// break;
|
||||
// case HANDLER:
|
||||
// this.threadGroups[i].handler = item.value;
|
||||
// break;
|
||||
// case THREAD_GROUP_TYPE:
|
||||
// this.threadGroups[i].tgType = item.value;
|
||||
// break;
|
||||
// case ON_SAMPLE_ERROR:
|
||||
// this.threadGroups[i].onSampleError = item.value;
|
||||
// break;
|
||||
// case STRATEGY:
|
||||
// this.threadGroups[i].strategy = item.value;
|
||||
// break;
|
||||
// case RESOURCE_NODE_INDEX:
|
||||
// this.threadGroups[i].resourceNodeIndex = item.value;
|
||||
// break;
|
||||
// case RATIOS:
|
||||
// this.threadGroups[i].ratios = item.value;
|
||||
// break;
|
||||
// case SERIALIZE_THREAD_GROUPS:
|
||||
// this.serializeThreadGroups = item.value;// 所有的线程组值一样
|
||||
// break;
|
||||
// case AUTO_STOP:
|
||||
// this.autoStop = item.value;// 所有的线程组值一样
|
||||
// break;
|
||||
// case AUTO_STOP_DELAY:
|
||||
// this.autoStopDelay = item.value;// 所有的线程组值一样
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// //
|
||||
// this.$set(this.threadGroups[i], "unit", this.threadGroups[i].unit || 'S');
|
||||
// this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION');
|
||||
// this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1);
|
||||
// this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10);
|
||||
// this.$set(this.threadGroups[i], "enabled", this.threadGroups[i].enabled || 'true');
|
||||
// this.$set(this.threadGroups[i], "deleted", this.threadGroups[i].deleted || 'false');
|
||||
// this.$set(this.threadGroups[i], "onSampleError", this.threadGroups[i].onSampleError || 'continue');
|
||||
// });
|
||||
// }
|
||||
// for (let i = 0; i < this.threadGroups.length; i++) {
|
||||
// let tg = this.threadGroups[i];
|
||||
// tg.durationHours = Math.floor(tg.duration / 3600);
|
||||
// tg.durationMinutes = Math.floor((tg.duration / 60 % 60));
|
||||
// tg.durationSeconds = Math.floor((tg.duration % 60));
|
||||
// }
|
||||
// this.resourcePoolChange();
|
||||
// this.calculateTotalChart();
|
||||
// });
|
||||
},
|
||||
getJmxContent() {
|
||||
let threadGroups = [];
|
||||
getJmxContent(this.testId, this.reportId, this.isShare)
|
||||
.then(response => {
|
||||
response.data.forEach(d => {
|
||||
threadGroups = threadGroups.concat(findThreadGroup(d.jmx, d.name));
|
||||
threadGroups.forEach(tg => {
|
||||
tg.options = {};
|
||||
});
|
||||
});
|
||||
this.threadGroups = threadGroups;
|
||||
this.$emit('fileChange', threadGroups);
|
||||
this.getLoadConfig();
|
||||
});
|
||||
// getJmxContent(this.testId, this.reportId, this.isShare)
|
||||
// .then(response => {
|
||||
// response.data.forEach(d => {
|
||||
// threadGroups = threadGroups.concat(findThreadGroup(d.jmx, d.name));
|
||||
// threadGroups.forEach(tg => {
|
||||
// tg.options = {};
|
||||
// });
|
||||
// });
|
||||
// this.threadGroups = threadGroups;
|
||||
// this.$emit('fileChange', threadGroups);
|
||||
// this.getLoadConfig();
|
||||
// });
|
||||
},
|
||||
resourcePoolChange() {
|
||||
let result = this.resourcePools.filter(p => p.id === this.resourcePool);
|
|
@ -37,8 +37,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
// 测试计划报告导出的是时候需要引入
|
||||
// import LoadCaseReportView from "../../../../../../../../../../performance-test/frontend/src/template/report/performance/share/LoadCaseReportView";
|
||||
import LoadCaseReportView from "../load/LoadCaseReportView";
|
||||
|
||||
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
|
||||
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
|
||||
|
@ -56,7 +55,7 @@ export default {
|
|||
MsMainContainer,
|
||||
MsAsideContainer,
|
||||
LoadFailureResult, StatusTableItem, MethodTableItem, TypeTableItem,
|
||||
// LoadCaseReportView,
|
||||
LoadCaseReportView,
|
||||
MicroApp
|
||||
},
|
||||
props: {
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
<template>
|
||||
<div>
|
||||
<span class="table-title">Errors</span>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
:default-sort="{prop: 'elementLabel'}"
|
||||
>
|
||||
<el-table-column
|
||||
prop="errorType"
|
||||
label="Type of error"
|
||||
sortable>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="200"
|
||||
prop="errorNumber"
|
||||
label="Number of errors"
|
||||
sortable>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="200"
|
||||
prop="percentOfErrors"
|
||||
label="% in errors"
|
||||
sortable>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="200"
|
||||
prop="percentOfAllSamples"
|
||||
label="% in all samples"
|
||||
sortable>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<span class="table-title">Top 5 Errors</span>
|
||||
<el-table
|
||||
:data="errorSummary"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
show-summary
|
||||
>
|
||||
<el-table-column prop="sample" label="Sample"/>
|
||||
<el-table-column prop="samples" label="#Samples"/>
|
||||
<el-table-column prop="errorsAllSize" label="All Errors"/>
|
||||
</el-table>
|
||||
|
||||
<span class="table-title">#1 Error</span>
|
||||
<el-table
|
||||
:data="errorTop1"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="sample" label="Sample"/>
|
||||
<el-table-column prop="error1" label="#1 Error"/>
|
||||
<el-table-column prop="error1Size" label="#1 Errors Count" width="200"/>
|
||||
</el-table>
|
||||
|
||||
<span class="table-title">#2 Error</span>
|
||||
<el-table
|
||||
:data="errorTop2"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="sample" label="Sample"/>
|
||||
<el-table-column prop="error2" label="#2 Error"/>
|
||||
<el-table-column prop="error2Size" label="#2 Errors Count" width="200"/>
|
||||
</el-table>
|
||||
|
||||
<span class="table-title">#3 Error</span>
|
||||
<el-table
|
||||
:data="errorTop3"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="sample" label="Sample"/>
|
||||
<el-table-column prop="error3" label="#3 Error"/>
|
||||
<el-table-column prop="error3Size" label="#3 Errors Count" width="200"/>
|
||||
</el-table>
|
||||
|
||||
<span class="table-title">#4 Error</span>
|
||||
<el-table
|
||||
:data="errorTop4"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="sample" label="Sample"/>
|
||||
<el-table-column prop="error4" label="#4 Error"/>
|
||||
<el-table-column prop="error4Size" label="#4 Errors Count" width="200"/>
|
||||
</el-table>
|
||||
|
||||
<span class="table-title">#5 Error</span>
|
||||
<el-table
|
||||
:data="errorTop5"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="sample" label="Sample"/>
|
||||
<el-table-column prop="error5" label="#5 Error"/>
|
||||
<el-table-column prop="error5Size" label="#5 Errors Count" width="200"/>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "ErrorLog",
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
errorSummary: [],
|
||||
errorTop1: [],
|
||||
errorTop2: [],
|
||||
errorTop3: [],
|
||||
errorTop4: [],
|
||||
errorTop5: [],
|
||||
id: ''
|
||||
};
|
||||
},
|
||||
props: ['report', 'isShare', 'shareId', 'planReportTemplate'],
|
||||
methods: {
|
||||
initTableData() {
|
||||
if (this.planReportTemplate) {
|
||||
this.tableData = this.planReportTemplate.reportErrors;
|
||||
this.handleGetTop5(this.planReportTemplate.reportErrorsTop5);
|
||||
}
|
||||
},
|
||||
handleGetTop5(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.errorTop1 = data
|
||||
.map(e => {
|
||||
return {sample: e.sample, error1: e.error1, error1Size: e.error1Size};
|
||||
})
|
||||
.filter(e => e.error1Size > 0);
|
||||
|
||||
this.errorTop2 = data
|
||||
.map(e => {
|
||||
return {sample: e.sample, error2: e.error2, error2Size: e.error2Size};
|
||||
})
|
||||
.filter(e => e.error2Size > 0);
|
||||
|
||||
this.errorTop3 = data
|
||||
.map(e => {
|
||||
return {sample: e.sample, error3: e.error3, error3Size: e.error3Size};
|
||||
})
|
||||
.filter(e => e.error3Size > 0);
|
||||
|
||||
this.errorTop4 = data
|
||||
.map(e => {
|
||||
return {sample: e.sample, error4: e.error4, error4Size: e.error4Size};
|
||||
})
|
||||
.filter(e => e.error4Size > 0);
|
||||
|
||||
this.errorTop5 = data
|
||||
.map(e => {
|
||||
return {sample: e.sample, error5: e.error5, error5Size: e.error5Size};
|
||||
})
|
||||
.filter(e => e.error5Size > 0);
|
||||
|
||||
this.errorSummary = data.map(e => {
|
||||
return {sample: e.sample, samples: e.samples, errorsAllSize: e.errorsAllSize};
|
||||
});
|
||||
},
|
||||
initData() {
|
||||
this.tableData = [];
|
||||
this.errorTop1 = [];
|
||||
this.errorTop2 = [];
|
||||
this.errorTop3 = [];
|
||||
this.errorTop4 = [];
|
||||
this.errorTop5 = [];
|
||||
this.errorSummary = [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
report: {
|
||||
handler(val) {
|
||||
if (!val.status || !val.id) {
|
||||
return;
|
||||
}
|
||||
let status = val.status;
|
||||
this.id = val.id;
|
||||
if (status === "Completed" || status === "Running") {
|
||||
this.initTableData();
|
||||
} else {
|
||||
this.tableData = [];
|
||||
this.errorTop1 = [];
|
||||
this.errorTop2 = [];
|
||||
this.errorTop3 = [];
|
||||
this.errorTop4 = [];
|
||||
this.errorTop5 = [];
|
||||
this.errorSummary = [];
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
planReportTemplate: {
|
||||
handler() {
|
||||
if (this.planReportTemplate) {
|
||||
this.initTableData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-title {
|
||||
font-size: 20px;
|
||||
color: #8492a6;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,300 @@
|
|||
<template>
|
||||
<ms-container>
|
||||
<el-main>
|
||||
<el-card v-loading="loading" v-if="show">
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div v-if="isPlanReport" style="float: right;margin-right: 10px;">
|
||||
<div v-if="showProjectEnv" type="flex">
|
||||
<span> {{ $t('commons.environment') + ':' }} </span>
|
||||
<div v-for="(values,key) in projectEnvMap" :key="key" style="margin-right: 10px">
|
||||
{{ key + ":" }}
|
||||
<ms-tag v-for="(item,index) in values" :key="index" type="success" :content="item"
|
||||
style="margin-left: 2px"/>
|
||||
</div>
|
||||
<div v-show="showMoreProjectEnvMap">
|
||||
<el-link icon="el-icon-more" @click="showAllProjectInfo"></el-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="float: left">
|
||||
<span class="ms-report-time-desc">
|
||||
{{
|
||||
$t('report.test_duration', [templateMinutes ? templateMinutes : minutes,
|
||||
templateSeconds ? templateSeconds : seconds])
|
||||
}}
|
||||
</span>
|
||||
<span class="ms-report-time-desc" v-if="startTime !== '0'">
|
||||
{{ $t('report.test_start_time') }}:{{ startTime | datetimeFormat }}
|
||||
</span>
|
||||
<span class="ms-report-time-desc" v-else-if="planReportTemplate && planReportTemplate.startTime">
|
||||
{{ $t('report.test_start_time') }}:{{ planReportTemplate.startTime | datetimeFormat }}
|
||||
</span>
|
||||
<span class="ms-report-time-desc" v-else>
|
||||
{{ $t('report.test_start_time') }}:-
|
||||
</span>
|
||||
<span class="ms-report-time-desc" v-if="report.status === 'Completed' && endTime !== '0'">
|
||||
{{ $t('report.test_end_time') }}:{{ endTime | datetimeFormat }}
|
||||
</span>
|
||||
<span class="ms-report-time-desc" v-else-if="planReportTemplate && planReportTemplate.endTime">
|
||||
{{ $t('report.test_end_time') }}:{{ planReportTemplate.endTime | datetimeFormat }}
|
||||
</span>
|
||||
<span class="ms-report-time-desc" v-else>
|
||||
{{ $t('report.test_end_time') }}:-
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider/>
|
||||
<div ref="resume">
|
||||
<el-tabs v-model="active">
|
||||
<el-tab-pane :label="$t('report.test_overview')">
|
||||
<ms-report-test-overview :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
|
||||
:share-id="shareId" ref="testOverview"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_details')">
|
||||
<ms-report-test-details :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
|
||||
:share-id="shareId" ref="testDetails"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_request_statistics')">
|
||||
<ms-report-request-statistics :report="report" :is-share="isShare"
|
||||
:plan-report-template="planReportTemplate"
|
||||
:share-id="shareId" ref="requestStatistics"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_error_log')">
|
||||
<ms-report-error-log :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
|
||||
:share-id="shareId" ref="errorLog"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_log_details')">
|
||||
<ms-report-log-details :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
|
||||
:share-id="shareId"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_monitor_details')">
|
||||
<monitor-card :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
|
||||
:share-id="shareId"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_config')">
|
||||
<ms-test-configuration :report-id="reportId" :report="report"
|
||||
:plan-report-template="planReportTemplate"
|
||||
:is-share="isShare" :share-id="shareId"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
|
||||
<project-environment-dialog ref="projectEnvDialog"></project-environment-dialog>
|
||||
</el-main>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsReportErrorLog from "./ErrorLog";
|
||||
import MsReportLogDetails from "./LogDetails";
|
||||
import MsReportRequestStatistics from "./RequestStatistics";
|
||||
import MsReportTestOverview from "./TestOverview";
|
||||
import MsContainer from "metersphere-frontend/src/components/MsContainer";
|
||||
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
|
||||
import MonitorCard from "./MonitorCard";
|
||||
import MsReportTestDetails from './TestDetails';
|
||||
import ProjectEnvironmentDialog from "./ProjectEnvironmentDialog";
|
||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||
import MsTestConfiguration from "./TestConfiguration";
|
||||
|
||||
|
||||
export default {
|
||||
name: "LoadCaseReportView",
|
||||
components: {
|
||||
MsTestConfiguration,
|
||||
MonitorCard,
|
||||
MsReportErrorLog,
|
||||
MsReportLogDetails,
|
||||
MsReportRequestStatistics,
|
||||
MsReportTestOverview,
|
||||
MsReportTestDetails,
|
||||
MsContainer,
|
||||
MsMainContainer,
|
||||
ProjectEnvironmentDialog,
|
||||
MsTag,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
active: '0',
|
||||
status: '',
|
||||
reportName: '',
|
||||
testId: '',
|
||||
testName: '',
|
||||
projectId: '',
|
||||
projectName: '',
|
||||
startTime: '0',
|
||||
endTime: '0',
|
||||
minutes: '0',
|
||||
seconds: '0',
|
||||
title: 'Logging',
|
||||
projectEnvMap: null,
|
||||
showMoreProjectEnvMap: false,
|
||||
allProjectEnvMap: null,
|
||||
report: {},
|
||||
websocket: null,
|
||||
dialogFormVisible: false,
|
||||
testPlan: {testResourcePoolId: null},
|
||||
show: true,
|
||||
test: {testResourcePoolId: null},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
reportId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isPlanReport: Boolean,
|
||||
isShare: Boolean,
|
||||
shareId: String,
|
||||
planReportTemplate: Object
|
||||
},
|
||||
watch: {
|
||||
reportId() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showProjectEnv() {
|
||||
return this.projectEnvMap && JSON.stringify(this.projectEnvMap) !== '{}';
|
||||
},
|
||||
templateMinutes() {
|
||||
if (this.planReportTemplate && this.planReportTemplate.duration) {
|
||||
let duration = this.planReportTemplate.duration;
|
||||
return Math.floor(duration / 60);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
templateSeconds() {
|
||||
if (this.planReportTemplate && this.planReportTemplate.duration) {
|
||||
let duration = this.planReportTemplate.duration;
|
||||
return duration % 60;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showAllProjectInfo() {
|
||||
this.$refs.projectEnvDialog.open(this.allProjectEnvMap);
|
||||
},
|
||||
isProjectEnvShowMore(projectEnvMap) {
|
||||
this.showMoreProjectEnvMap = false;
|
||||
this.projectEnvMap = {};
|
||||
if (projectEnvMap) {
|
||||
let keySize = 0;
|
||||
for (let key in projectEnvMap) {
|
||||
keySize++;
|
||||
if (keySize > 1) {
|
||||
this.showMoreProjectEnvMap = true;
|
||||
return;
|
||||
} else {
|
||||
this.projectEnvMap = {};
|
||||
this.$set(this.projectEnvMap, key, projectEnvMap[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
initBreadcrumb(callback) {
|
||||
if (this.isPlanReport) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
initReportTimeInfo() {
|
||||
if (this.status === 'Starting') {
|
||||
this.clearData();
|
||||
return;
|
||||
}
|
||||
if (this.planReportTemplate) {
|
||||
this.handleInitReportTimeInfo(this.planReportTemplate);
|
||||
}
|
||||
},
|
||||
handleInitReportTimeInfo(data) {
|
||||
if (data) {
|
||||
this.startTime = data.startTime;
|
||||
this.endTime = data.endTime;
|
||||
let duration = data.duration;
|
||||
this.minutes = Math.floor(duration / 60);
|
||||
this.seconds = duration % 60;
|
||||
}
|
||||
},
|
||||
checkReportStatus(status) {
|
||||
switch (status) {
|
||||
case 'Error':
|
||||
// this.$warning(this.$t('report.generation_error'));
|
||||
this.active = '4';
|
||||
break;
|
||||
case 'Starting':
|
||||
this.$alert(this.$t('report.start_status'));
|
||||
break;
|
||||
case 'Reporting':
|
||||
case 'Running':
|
||||
case 'Completed':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
clearData() {
|
||||
this.show = false;
|
||||
this.startTime = '0';
|
||||
this.endTime = '0';
|
||||
this.minutes = '0';
|
||||
this.seconds = '0';
|
||||
this.$nextTick(() => {
|
||||
this.show = true;
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.clearData();
|
||||
if (this.planReportTemplate) {
|
||||
this.handleInit(this.planReportTemplate);
|
||||
}
|
||||
},
|
||||
handleInit(data) {
|
||||
if (data) {
|
||||
this.allProjectEnvMap = data.projectEnvMap;
|
||||
this.isProjectEnvShowMore(data.projectEnvMap);
|
||||
this.status = data.status;
|
||||
this.$set(this, "report", data);
|
||||
this.$set(this.test, "testResourcePoolId", data.testResourcePoolId);
|
||||
this.checkReportStatus(data.status);
|
||||
if (this.status === "Completed" || this.status === "Running") {
|
||||
this.initReportTimeInfo();
|
||||
}
|
||||
this.initBreadcrumb();
|
||||
} else {
|
||||
this.$error(this.$t('report.not_exist'));
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-report-view-btns {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.ms-report-time-desc {
|
||||
text-align: left;
|
||||
display: block;
|
||||
color: #5C7878;
|
||||
}
|
||||
|
||||
.ms-report-time-desc-share {
|
||||
text-align: left;
|
||||
color: #5C7878;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<span class="title">Load</span>
|
||||
</template>
|
||||
<div v-for="(option, index) in loadList" :key="index">
|
||||
<ms-chart ref="chart1" :options="option" :autoresize="true"></ms-chart>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
import {getPerformanceReportLoadChart} from "@/api/load-test";
|
||||
|
||||
export default {
|
||||
name: "LoadCompareCard",
|
||||
components: {MsChart},
|
||||
data() {
|
||||
return {
|
||||
loadList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initCard() {
|
||||
this.loadList = [];
|
||||
this.reportId = this.$route.path.split('/')[4];
|
||||
this.compareReports = JSON.parse(sessionStorage.getItem("compareReports"));
|
||||
|
||||
this.compareReports.forEach(report => {
|
||||
this.initOverview(report);
|
||||
})
|
||||
},
|
||||
initOverview(report) {
|
||||
getPerformanceReportLoadChart(report.id)
|
||||
.then(({data}) => {
|
||||
let yAxisList = data.filter(m => m.yAxis2 === -1).map(m => m.yAxis);
|
||||
let yAxis2List = data.filter(m => m.yAxis === -1).map(m => m.yAxis2);
|
||||
let yAxisListMax = this._getChartMax(yAxisList);
|
||||
let yAxis2ListMax = this._getChartMax(yAxis2List);
|
||||
|
||||
let yAxisIndex0List = data.filter(m => m.yAxis2 === -1).map(m => m.groupName);
|
||||
yAxisIndex0List = this._unique(yAxisIndex0List);
|
||||
let yAxisIndex1List = data.filter(m => m.yAxis === -1).map(m => m.groupName);
|
||||
yAxisIndex1List = this._unique(yAxisIndex1List);
|
||||
|
||||
let loadOption = {
|
||||
title: {
|
||||
text: report.name + " " + this.$options.filters['datetimeFormat'](report.createTime),
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#65A2FF'
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
// extraCssText: 'z-index: 999;',
|
||||
confine: true,
|
||||
},
|
||||
legend: {},
|
||||
xAxis: {},
|
||||
yAxis: [{
|
||||
name: 'User',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxisListMax,
|
||||
splitNumber: 5,
|
||||
interval: yAxisListMax / 5
|
||||
},
|
||||
{
|
||||
name: 'Transactions/s',
|
||||
type: 'value',
|
||||
splitNumber: 5,
|
||||
min: 0,
|
||||
max: yAxis2ListMax,
|
||||
interval: yAxis2ListMax / 5
|
||||
}
|
||||
],
|
||||
series: []
|
||||
};
|
||||
let setting = {
|
||||
series: [
|
||||
{
|
||||
name: 'users',
|
||||
color: '#0CA74A',
|
||||
},
|
||||
{
|
||||
name: 'hits',
|
||||
yAxisIndex: '1',
|
||||
color: '#65A2FF',
|
||||
},
|
||||
{
|
||||
name: 'errors',
|
||||
yAxisIndex: '1',
|
||||
color: '#E6113C',
|
||||
}
|
||||
]
|
||||
}
|
||||
yAxisIndex0List.forEach(item => {
|
||||
setting["series"].splice(0, 0, {name: item, yAxisIndex: '0'})
|
||||
})
|
||||
|
||||
yAxisIndex1List.forEach(item => {
|
||||
setting["series"].splice(0, 0, {name: item, yAxisIndex: '1'})
|
||||
})
|
||||
this.loadList.push(this.generateOption(loadOption, data, setting));
|
||||
})
|
||||
.catch(() => {
|
||||
this.loadList = [];
|
||||
})
|
||||
},
|
||||
generateOption(option, data, setting) {
|
||||
let chartData = data;
|
||||
let seriesArray = [];
|
||||
for (let set in setting) {
|
||||
if (set === "series") {
|
||||
seriesArray = setting[set];
|
||||
continue;
|
||||
}
|
||||
this.$set(option, set, setting[set]);
|
||||
}
|
||||
let legend = [], series = {}, xAxis = [], seriesData = [];
|
||||
chartData.forEach(item => {
|
||||
if (!xAxis.includes(item.xAxis)) {
|
||||
xAxis.push(item.xAxis);
|
||||
}
|
||||
xAxis.sort()
|
||||
let name = item.groupName
|
||||
if (!legend.includes(name)) {
|
||||
legend.push(name)
|
||||
series[name] = []
|
||||
}
|
||||
if (item.yAxis === -1) {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis2.toFixed(2)]);
|
||||
} else {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis.toFixed(2)]);
|
||||
}
|
||||
})
|
||||
this.$set(option.legend, "data", legend);
|
||||
this.$set(option.legend, "type", "scroll");
|
||||
this.$set(option.legend, "bottom", "10px");
|
||||
this.$set(option.xAxis, "data", xAxis);
|
||||
for (let name in series) {
|
||||
let d = series[name];
|
||||
d.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
let items = {
|
||||
name: name,
|
||||
type: 'line',
|
||||
data: d,
|
||||
smooth: true
|
||||
};
|
||||
let seriesArrayNames = seriesArray.map(m => m.name);
|
||||
if (seriesArrayNames.includes(name)) {
|
||||
for (let j = 0; j < seriesArray.length; j++) {
|
||||
let seriesObj = seriesArray[j];
|
||||
if (seriesObj['name'] === name) {
|
||||
Object.assign(items, seriesObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
seriesData.push(items);
|
||||
}
|
||||
this.$set(option, "series", seriesData);
|
||||
return option;
|
||||
},
|
||||
_getChartMax(arr) {
|
||||
const max = Math.max(...arr);
|
||||
return Math.ceil(max / 4.5) * 5;
|
||||
},
|
||||
_unique(arr) {
|
||||
return Array.from(new Set(arr));
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.echarts {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="4">
|
||||
<el-select v-model="currentInstance" placeholder="" size="small" style="width: 100%"
|
||||
@change="changeInstance(currentInstance)">
|
||||
<el-option
|
||||
v-for="item in resource"
|
||||
:key="item.resourceId"
|
||||
:label="item.resourceName"
|
||||
:value="item.resourceId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<div class="logging-content" v-loading="loading">
|
||||
<ul class="infinite-list">
|
||||
<li class="infinite-list-item" v-for="(log, index) in logContent"
|
||||
:key="currentInstance+index">
|
||||
{{ log.content }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "LogDetails",
|
||||
data() {
|
||||
return {
|
||||
resource: [],
|
||||
logContent: [],
|
||||
result: {},
|
||||
id: '',
|
||||
page: 1,
|
||||
pageCount: 5,
|
||||
loading: false,
|
||||
currentInstance: ''
|
||||
};
|
||||
},
|
||||
props: ['report', 'export', 'isShare', 'shareId', 'planReportTemplate'],
|
||||
methods: {
|
||||
getResource() {
|
||||
if (this.planReportTemplate) {
|
||||
this.handleGetLogResource(this.planReportTemplate.reportLogResource);
|
||||
}
|
||||
},
|
||||
handleGetLogResource(data) {
|
||||
this.resource = data;
|
||||
if (!this.currentInstance) {
|
||||
this.currentInstance = this.resource[0]?.resourceId;
|
||||
}
|
||||
|
||||
//
|
||||
if (this.currentInstance) {
|
||||
this.changeInstance(this.currentInstance);
|
||||
}
|
||||
},
|
||||
load(resourceId) {
|
||||
if (this.loading || this.page > this.pageCount) {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
if (this.planReportTemplate) {
|
||||
let {reportLogResource} = this.planReportTemplate;
|
||||
if (reportLogResource && reportLogResource.length > 0) {
|
||||
let {reportLogs} = reportLogResource[0];
|
||||
if (reportLogs) {
|
||||
this.handleGetPlanTemplateLog(reportLogs);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
handleGetPlanTemplateLog(data) {
|
||||
data.forEach(log => {
|
||||
if (this.logContent) {
|
||||
this.logContent.push(log);
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
changeInstance(instance) {
|
||||
this.currentInstance = instance;
|
||||
this.loading = false;
|
||||
this.page = 1;
|
||||
this.logContent = [];
|
||||
this.load(instance);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
if (to.name === "perReportView") {
|
||||
this.id = to.path.split('/')[4];
|
||||
this.getResource();
|
||||
}
|
||||
},
|
||||
report: {
|
||||
handler(val) {
|
||||
if (!val.status || !val.id) {
|
||||
return;
|
||||
}
|
||||
let status = val.status;
|
||||
this.id = val.id;
|
||||
if (status === "Completed" || status === "Running") {
|
||||
this.getResource();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
planReportTemplate: {
|
||||
handler() {
|
||||
if (this.planReportTemplate) {
|
||||
this.getResource();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.logging-content {
|
||||
white-space: pre-line;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.infinite-list {
|
||||
height: calc(100vh - 205px);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.infinite-list-item {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,348 @@
|
|||
<template>
|
||||
<div v-loading="result.loading">
|
||||
<el-row>
|
||||
<el-col :span="4">
|
||||
<div>
|
||||
<el-select v-model="currentInstance" placeholder="" size="small" style="width: 100%"
|
||||
@change="getResource(currentInstance)">
|
||||
<el-option
|
||||
v-for="item in instances"
|
||||
:key="item.ip+item.port"
|
||||
:value="item.ip+':'+item.port">
|
||||
{{ item.ip }} {{ item.name }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div style="padding-top: 10px">
|
||||
<el-checkbox-group v-model="checkList"
|
||||
@change="handleCheckListChange(currentInstance)">
|
||||
<div v-for="op in checkOptions"
|
||||
:key="op.key"
|
||||
:content="op.label">
|
||||
<el-checkbox :label="op.label"/>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<ms-chart v-if="showChart" ref="chart2" class="chart-config" @datazoom="changeDataZoom"
|
||||
:options="totalOption"
|
||||
:autoresize="true"></ms-chart>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :offset="2" :span="20">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
stripe
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column label="Label" align="center">
|
||||
<el-table-column
|
||||
prop="label"
|
||||
label="Label"
|
||||
sortable>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="Aggregate" align="center">
|
||||
<el-table-column
|
||||
prop="avg"
|
||||
label="Avg."
|
||||
width="100"
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
prop="min"
|
||||
label="Min."
|
||||
width="100"
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
prop="max"
|
||||
label="Max."
|
||||
width="100"
|
||||
sortable
|
||||
/>
|
||||
</el-table-column>
|
||||
<el-table-column label="Range" align="center">
|
||||
<el-table-column
|
||||
prop="startTime"
|
||||
label="Start"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="endTime"
|
||||
label="End"
|
||||
width="160"
|
||||
/>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
|
||||
const color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
|
||||
const checkList = ['CPU', 'Memory', 'Disk', 'Network In', 'Network Out'];
|
||||
const checkOptions = [
|
||||
{key: 'cpu', label: 'CPU'},
|
||||
{key: 'memory', label: 'Memory'},
|
||||
{key: 'disk', label: 'Disk'},
|
||||
{key: 'netIn', label: 'Network In'},
|
||||
{key: 'netOut', label: 'Network Out'}
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "MonitorCard",
|
||||
props: ['report', 'export', 'isShare', 'shareId', 'planReportTemplate'],
|
||||
components: {MsChart},
|
||||
data() {
|
||||
return {
|
||||
activeNames: '0',
|
||||
result: {},
|
||||
id: '',
|
||||
init: false,
|
||||
loading: false,
|
||||
currentInstance: '',
|
||||
instances: [],
|
||||
data: [],
|
||||
tableData: [],
|
||||
checkList: checkList,
|
||||
checkOptions: checkOptions,
|
||||
showChart: true,
|
||||
baseOption: {
|
||||
color: color,
|
||||
grid: {
|
||||
// right: '35%' // 动态改这个值
|
||||
},
|
||||
title: {},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
y: 'top'
|
||||
},
|
||||
xAxis: {type: 'category'},
|
||||
yAxis: [{
|
||||
name: 'Usage(%)',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 100,
|
||||
}, {
|
||||
type: 'value',
|
||||
name: 'kb/s',
|
||||
min: 0,
|
||||
}],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
}
|
||||
],
|
||||
series: []
|
||||
},
|
||||
totalOption: {},
|
||||
seriesData: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.data = [];
|
||||
this.instances = [];
|
||||
},
|
||||
methods: {
|
||||
getResource(currentInstance) {
|
||||
// this.init = true;
|
||||
if (this.planReportTemplate) {
|
||||
this.instances = this.planReportTemplate.reportResource;
|
||||
this.currentInstance = currentInstance || this.instances[0].ip + ":" + this.instances[0].port;
|
||||
this.data = this.planReportTemplate.metricData;
|
||||
this.totalOption = this.getOption(this.currentInstance);
|
||||
}
|
||||
},
|
||||
handleChecked(id) {
|
||||
let curr = this.instances.filter(instance => id === instance.ip + ":" + instance.port)[0];
|
||||
if (curr && curr.monitorConfig) {
|
||||
this.checkList = [];
|
||||
this.checkOptions = curr.monitorConfig.filter(mc => mc.value && mc.name)
|
||||
.map(mc => {
|
||||
this.checkList.push(mc.name);
|
||||
return {key: mc.name, label: mc.name,};
|
||||
});
|
||||
if (this.checkList.length === 0) {
|
||||
this.checkList = checkList;
|
||||
this.checkOptions = checkOptions;
|
||||
}
|
||||
} else {
|
||||
this.checkOptions = checkOptions;
|
||||
this.checkList = checkList;
|
||||
}
|
||||
this.totalOption = {};
|
||||
this.$nextTick(() => {
|
||||
this.totalOption = this.getOption(id);
|
||||
this.changeDataZoom({start: 0, end: 100});
|
||||
});
|
||||
},
|
||||
handleCheckListChange(id) {
|
||||
this.totalOption = {};
|
||||
this.showChart = false;
|
||||
this.$nextTick(() => {
|
||||
this.showChart = true;
|
||||
this.totalOption = this.getOption(id);
|
||||
this.changeDataZoom({start: 0, end: 100});
|
||||
});
|
||||
},
|
||||
getOption(id) {
|
||||
let legend = [];
|
||||
let series = [];
|
||||
|
||||
for (const name of this.checkList) {
|
||||
let check = this.checkOptions.filter(op => op.label === name)[0].key;
|
||||
let yAxisIndex = 1;
|
||||
if (check === 'cpu' || check === 'memory' || check === 'disk') {
|
||||
yAxisIndex = 0;
|
||||
}
|
||||
this.data.forEach(d => {
|
||||
if (d.instance === id && d.seriesName === check) {
|
||||
if (legend.indexOf(name) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.baseOption.xAxis.data = d.timestamps;
|
||||
|
||||
let yAxis = d.values.map(v => v.toFixed(2));
|
||||
let data = [];
|
||||
for (let i = 0; i < d.timestamps.length; i++) {
|
||||
data.push([d.timestamps[i], yAxis[i]]);
|
||||
}
|
||||
|
||||
legend.push(name);
|
||||
series.push({
|
||||
name: name,
|
||||
data: data,
|
||||
type: 'line',
|
||||
yAxisIndex: yAxisIndex,
|
||||
smooth: true,
|
||||
sampling: 'lttb',
|
||||
showSymbol: false,
|
||||
});
|
||||
|
||||
this.seriesData = series;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.baseOption.legend.data = legend;
|
||||
this.baseOption.series = series;
|
||||
return this.baseOption;
|
||||
},
|
||||
changeDataZoom(params) {
|
||||
let start = params.start / 100;
|
||||
let end = params.end / 100;
|
||||
if (params.batch) {
|
||||
start = params.batch[0].start / 100;
|
||||
end = params.batch[0].end / 100;
|
||||
}
|
||||
|
||||
let tableData = [];
|
||||
for (let i = 0; i < this.seriesData.length; i++) {
|
||||
let sub = this.seriesData[i].data, label = this.seriesData[i].name;
|
||||
let len = 0;
|
||||
let min, avg, max, sum = 0, startTime, endTime;
|
||||
for (let j = 0; j < sub.length; j++) {
|
||||
let time = sub[j][0];
|
||||
let value = Number.parseFloat(sub[j][1]);
|
||||
let index = (j / (sub.length - 1)).toFixed(2);
|
||||
if (index < start) {
|
||||
continue;
|
||||
}
|
||||
if (index >= end) {
|
||||
endTime = time;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!startTime) {
|
||||
startTime = time;
|
||||
}
|
||||
|
||||
if (!min && !max) {
|
||||
min = max = value;
|
||||
}
|
||||
|
||||
if (min > value) {
|
||||
min = value;
|
||||
}
|
||||
if (max < value) {
|
||||
max = value;
|
||||
}
|
||||
sum += value;
|
||||
|
||||
len++; // 实际 len
|
||||
}
|
||||
|
||||
avg = (sum / len).toFixed(2);
|
||||
tableData.push({label, min, max, avg, startTime, endTime});
|
||||
}
|
||||
this.tableData = tableData;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
report: {
|
||||
handler(val) {
|
||||
if (!val.status || !val.id) {
|
||||
return;
|
||||
}
|
||||
let status = val.status;
|
||||
this.id = val.id;
|
||||
if (this.init) {
|
||||
return;
|
||||
}
|
||||
if (status === "Completed" || status === "Running") {
|
||||
this.getResource();
|
||||
} else {
|
||||
this.instances = [];
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
planReportTemplate: {
|
||||
handler() {
|
||||
if (this.planReportTemplate) {
|
||||
this.getResource();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-config {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monitor-detail {
|
||||
height: calc(100vh - 375px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__label ) {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<el-card class="table-card">
|
||||
<template v-slot:header>
|
||||
<span class="title">Overview</span>
|
||||
</template>
|
||||
<el-table border :data="overviewList" class="adjust-table test-content">
|
||||
<el-table-column prop="name" :label="$t('commons.name')"/>
|
||||
<el-table-column prop="createTime" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | datetimeFormat }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="maxUsers" label="Max Users"/>
|
||||
<el-table-column prop="avgTransactions" label="Avg.Transactions"/>
|
||||
<el-table-column prop="errors" label="Errors"/>
|
||||
<el-table-column prop="avgResponseTime" label="Avg.Response Time"/>
|
||||
<el-table-column prop="responseTime90" label="90% Response Time"/>
|
||||
<el-table-column prop="avgBandwidth" label="Avg.Bandwidth"/>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getOverview} from "@/api/report";
|
||||
|
||||
export default {
|
||||
name: "OverviewCompareCard",
|
||||
data() {
|
||||
return {
|
||||
reportId: null,
|
||||
compareReports: [],
|
||||
overviewList: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
initTable() {
|
||||
this.overviewList = [];
|
||||
|
||||
this.reportId = this.$route.path.split('/')[4];
|
||||
this.compareReports = JSON.parse(sessionStorage.getItem("compareReports"));
|
||||
|
||||
this.compareReports.forEach(report => {
|
||||
this.initOverview(report);
|
||||
})
|
||||
},
|
||||
initOverview(report) {
|
||||
getOverview(report.id)
|
||||
.then(({data}) => {
|
||||
this.overviewList.push({
|
||||
name: report.name,
|
||||
createTime: report.createTime,
|
||||
maxUsers: data.maxUsers,
|
||||
avgThroughput: data.avgThroughput,
|
||||
avgTransactions: data.avgTransactions,
|
||||
errors: data.errors,
|
||||
avgResponseTime: data.avgResponseTime,
|
||||
responseTime90: data.responseTime90,
|
||||
avgBandwidth: data.avgBandwidth,
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,145 @@
|
|||
<template>
|
||||
<ms-chart :options="bar"></ms-chart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
|
||||
export default {
|
||||
name: "PerformanceChart",
|
||||
components: {MsChart},
|
||||
data() {
|
||||
return {
|
||||
bar: {
|
||||
|
||||
backgroundColor: '#394056',
|
||||
title: {
|
||||
top: 20,
|
||||
text: 'Requests',
|
||||
textStyle: {
|
||||
fontWeight: 'normal',
|
||||
fontSize: 16,
|
||||
color: '#F1F1F3'
|
||||
},
|
||||
left: '1%'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: 20,
|
||||
icon: 'rect',
|
||||
itemWidth: 14,
|
||||
itemHeight: 5,
|
||||
itemGap: 13,
|
||||
data: ['CMCC', 'CTCC', 'CUCC'],
|
||||
right: '4%',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#F1F1F3'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: 100,
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '2%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
},
|
||||
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
|
||||
}],
|
||||
yAxis: [{
|
||||
type: 'value',
|
||||
name: '(%)',
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
margin: 10,
|
||||
textStyle: {
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
}
|
||||
}],
|
||||
series: [{
|
||||
name: 'CMCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 1
|
||||
},
|
||||
itemStyle: {
|
||||
color: 'rgb(137,189,27)',
|
||||
borderColor: 'rgba(137,189,2,0.27)',
|
||||
borderWidth: 12
|
||||
},
|
||||
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
|
||||
}, {
|
||||
name: 'CTCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 1
|
||||
},
|
||||
itemStyle: {
|
||||
color: 'rgb(0,136,212)',
|
||||
borderColor: 'rgba(0,136,212,0.2)',
|
||||
borderWidth: 12
|
||||
},
|
||||
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
|
||||
}, {
|
||||
name: 'CUCC',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 1
|
||||
},
|
||||
itemStyle: {
|
||||
color: 'rgb(219,50,51)',
|
||||
borderColor: 'rgba(219,50,51,0.2)',
|
||||
borderWidth: 12
|
||||
},
|
||||
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
|
||||
}]
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('commons.environment')"
|
||||
:visible.sync="dialogVisible"
|
||||
width="30%"
|
||||
:append-to-body="true"
|
||||
:destroy-on-close="true"
|
||||
:before-close="handleClose">
|
||||
<div>
|
||||
<div v-if="projectEnvMap" type="flex">
|
||||
<div v-for="(values,key) in projectEnvMap" :key="key" style="margin-right: 10px">
|
||||
{{ key + ":" }}
|
||||
<ms-tag v-for="(item,index) in values" :key="index" type="success" :content="item"
|
||||
style="margin-left: 2px"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||
|
||||
export default {
|
||||
name: "ProjectEnvironmentDialog",
|
||||
components: {MsTag},
|
||||
data() {
|
||||
return {
|
||||
projectEnvMap: {},
|
||||
dialogVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
this.projectEnvMap = {};
|
||||
},
|
||||
open(projectEnvMap) {
|
||||
this.dialogVisible = true;
|
||||
this.projectEnvMap = projectEnvMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,200 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
stripe
|
||||
border
|
||||
height="calc(100vh - 190px)"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column label="Requests" min-width="150" align="center">
|
||||
<el-table-column
|
||||
prop="label"
|
||||
label="Label"
|
||||
sortable
|
||||
min-width="150">
|
||||
<template v-slot:header="{column}">
|
||||
<span>Label</span>
|
||||
<i class="el-icon-search" style="margin-left: 8px;cursor: pointer;font-weight: bold;"
|
||||
@click="click(column)"></i>
|
||||
<el-input v-model="searchLabel"
|
||||
placeholder="请输入 Label 搜索"
|
||||
size="mini"
|
||||
class="search_input"
|
||||
style="width: 100px; margin-left: 5px"
|
||||
v-if="column.showSearch"
|
||||
clearable
|
||||
@clear="filterLabel"
|
||||
@keyup.enter.native="filterLabel"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Executions" align="center">
|
||||
<el-table-column
|
||||
prop="samples"
|
||||
label="Samples"
|
||||
sortable
|
||||
width="110"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="fail"
|
||||
label="FAIL"
|
||||
sortable
|
||||
align="center"
|
||||
min-width="60"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
prop="error"
|
||||
label="Error%"
|
||||
sortable
|
||||
align="center"
|
||||
/>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Response Times(ms)" align="center">
|
||||
<el-table-column
|
||||
prop="average"
|
||||
label="Avg"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="min"
|
||||
label="Min"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="max"
|
||||
label="Max"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="median"
|
||||
label="Med"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="tp90"
|
||||
label="90%"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="tp95"
|
||||
label="95%"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="tp99"
|
||||
label="99%"
|
||||
sortable
|
||||
min-width="60"
|
||||
/>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Throughput">
|
||||
<el-table-column
|
||||
prop="transactions"
|
||||
label="Trans/s"
|
||||
sortable
|
||||
width="100"
|
||||
/>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="NetWork(KB/sec)" align="center">
|
||||
<el-table-column
|
||||
prop="received"
|
||||
label="Recd"
|
||||
sortable
|
||||
align="center"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="sent"
|
||||
label="Sent"
|
||||
sortable
|
||||
align="center"
|
||||
width="100"
|
||||
/>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "RequestStatistics",
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
originalData: [],
|
||||
id: '',
|
||||
searchLabel: '',
|
||||
showSearch: false,
|
||||
showBtn: true,
|
||||
}
|
||||
},
|
||||
props: ['report', 'isShare', 'shareId', 'planReportTemplate'],
|
||||
methods: {
|
||||
initTableData() {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.reportStatistics;
|
||||
this.tableData = data;
|
||||
this.originalData = data;
|
||||
}
|
||||
},
|
||||
click(column) {
|
||||
this.searchLabel = '';
|
||||
this.tableData = this.originalData;
|
||||
this.$set(column, 'showSearch', !column.showSearch);
|
||||
},
|
||||
filterLabel() {
|
||||
this.tableData = this.searchLabel ? this.originalData.filter(this.createFilter(this.searchLabel)) : this.originalData;
|
||||
},
|
||||
createFilter(queryString) {
|
||||
return item => {
|
||||
return (item.label.toLowerCase().indexOf(queryString.toLowerCase()) !== -1);
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
report: {
|
||||
handler(val) {
|
||||
if (!val.status || !val.id) {
|
||||
return;
|
||||
}
|
||||
let status = val.status;
|
||||
this.id = val.id;
|
||||
if (status === "Completed" || status === "Running") {
|
||||
this.initTableData();
|
||||
} else {
|
||||
this.tableData = [];
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
planReportTemplate: {
|
||||
handler() {
|
||||
if (this.planReportTemplate) {
|
||||
this.initTableData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search_input :deep( .el-input__inner ) {
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<span class="title">Response Time</span>
|
||||
</template>
|
||||
<div v-for="(option, index) in responseTimeList" :key="index">
|
||||
<ms-chart ref="chart1" :options="option" :autoresize="true"></ms-chart>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
import {getPerformanceReportResChart} from "@/api/load-test";
|
||||
|
||||
export default {
|
||||
name: "ResponseTimeCompareCard",
|
||||
components: {MsChart},
|
||||
data() {
|
||||
return {
|
||||
responseTimeList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initCard() {
|
||||
this.responseTimeList = [];
|
||||
this.reportId = this.$route.path.split('/')[4];
|
||||
this.compareReports = JSON.parse(sessionStorage.getItem("compareReports"));
|
||||
|
||||
this.compareReports.forEach(report => {
|
||||
this.initOverview(report);
|
||||
})
|
||||
},
|
||||
initOverview(report) {
|
||||
getPerformanceReportResChart(report.id)
|
||||
.then(({data}) => {
|
||||
let yAxisList = data.filter(m => m.yAxis2 === -1).map(m => m.yAxis);
|
||||
let yAxis2List = data.filter(m => m.yAxis === -1).map(m => m.yAxis2);
|
||||
let yAxisListMax = this._getChartMax(yAxisList);
|
||||
let yAxis2ListMax = this._getChartMax(yAxis2List);
|
||||
|
||||
let yAxisIndex0List = data.filter(m => m.yAxis2 === -1).map(m => m.groupName);
|
||||
yAxisIndex0List = this._unique(yAxisIndex0List);
|
||||
let yAxisIndex1List = data.filter(m => m.yAxis === -1).map(m => m.groupName);
|
||||
yAxisIndex1List = this._unique(yAxisIndex1List);
|
||||
|
||||
let resOption = {
|
||||
title: {
|
||||
text: report.name + " " + this.$options.filters['datetimeFormat'](report.createTime),
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#99743C'
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
// extraCssText: 'z-index: 999;',
|
||||
confine: true,
|
||||
formatter: function (params, ticket, callback) {
|
||||
let result = "";
|
||||
let name = params[0].name;
|
||||
result += name + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let seriesName = params[i].seriesName;
|
||||
if (seriesName.length > 100) {
|
||||
seriesName = seriesName.substring(0, 100);
|
||||
}
|
||||
let value = params[i].value;
|
||||
let marker = params[i].marker;
|
||||
result += marker + seriesName + ": " + value[1] + "<br/>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
xAxis: {},
|
||||
yAxis: [{
|
||||
name: 'User',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxisListMax,
|
||||
interval: yAxisListMax / 5
|
||||
},
|
||||
{
|
||||
name: 'Response Time',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxis2ListMax,
|
||||
interval: yAxis2ListMax / 5
|
||||
}
|
||||
],
|
||||
series: []
|
||||
}
|
||||
let setting = {
|
||||
series: [
|
||||
{
|
||||
name: 'users',
|
||||
color: '#0CA74A',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
yAxisIndex0List.forEach(item => {
|
||||
setting["series"].splice(0, 0, {name: item, yAxisIndex: '0'})
|
||||
})
|
||||
|
||||
yAxisIndex1List.forEach(item => {
|
||||
setting["series"].splice(0, 0, {name: item, yAxisIndex: '1'})
|
||||
})
|
||||
|
||||
this.responseTimeList.push(this.generateOption(resOption, data, setting));
|
||||
})
|
||||
.catch(() => {
|
||||
this.responseTimeList = [];
|
||||
})
|
||||
},
|
||||
generateOption(option, data, setting) {
|
||||
let chartData = data;
|
||||
let seriesArray = [];
|
||||
for (let set in setting) {
|
||||
if (set === "series") {
|
||||
seriesArray = setting[set];
|
||||
continue;
|
||||
}
|
||||
this.$set(option, set, setting[set]);
|
||||
}
|
||||
let legend = [], series = {}, xAxis = [], seriesData = [];
|
||||
chartData.forEach(item => {
|
||||
if (!xAxis.includes(item.xAxis)) {
|
||||
xAxis.push(item.xAxis);
|
||||
}
|
||||
xAxis.sort()
|
||||
let name = item.groupName
|
||||
if (!legend.includes(name)) {
|
||||
legend.push(name)
|
||||
series[name] = []
|
||||
}
|
||||
if (item.yAxis === -1) {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis2.toFixed(2)]);
|
||||
} else {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis.toFixed(2)]);
|
||||
}
|
||||
})
|
||||
this.$set(option.legend, "data", legend);
|
||||
this.$set(option.legend, "type", "scroll");
|
||||
this.$set(option.legend, "bottom", "10px");
|
||||
this.$set(option.xAxis, "data", xAxis);
|
||||
for (let name in series) {
|
||||
let d = series[name];
|
||||
d.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
let items = {
|
||||
name: name,
|
||||
type: 'line',
|
||||
data: d,
|
||||
smooth: true
|
||||
};
|
||||
let seriesArrayNames = seriesArray.map(m => m.name);
|
||||
if (seriesArrayNames.includes(name)) {
|
||||
for (let j = 0; j < seriesArray.length; j++) {
|
||||
let seriesObj = seriesArray[j];
|
||||
if (seriesObj['name'] === name) {
|
||||
Object.assign(items, seriesObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
seriesData.push(items);
|
||||
}
|
||||
this.$set(option, "series", seriesData);
|
||||
return option;
|
||||
},
|
||||
_getChartMax(arr) {
|
||||
const max = Math.max(...arr);
|
||||
return Math.ceil(max / 4.5) * 5;
|
||||
},
|
||||
_unique(arr) {
|
||||
return Array.from(new Set(arr));
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.echarts {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<el-dialog :close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
:title="$t('load_test.completed_test_report')" width="60%"
|
||||
v-loading="loading"
|
||||
:show-close="false"
|
||||
:visible.sync="loadReportVisible">
|
||||
|
||||
<el-header class="header-btn">
|
||||
<ms-dialog-header :enable-cancel="true" @cancel="close" @confirm="handleCompare" btn-size="mini">
|
||||
</ms-dialog-header>
|
||||
</el-header>
|
||||
|
||||
<ms-table
|
||||
:data="tableData"
|
||||
:show-select-all="false"
|
||||
:screen-height="screenHeight"
|
||||
ref="table"
|
||||
>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<i v-if="scope.row.id === report.id" class="el-icon-star-on"></i> {{ scope.row.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="userName"
|
||||
:label="$t('report.user_name')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column prop="triggerMode"
|
||||
:label="$t('test_track.report.list.trigger_mode')">
|
||||
<template v-slot:default="scope">
|
||||
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<i class="el-icon-time"/>
|
||||
<span class="last-modified">{{ scope.row.createTime | datetimeFormat }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ms-table>
|
||||
<ms-table-pagination :change="getCompareReports" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
|
||||
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||
import ReportTriggerModeItem from "metersphere-frontend/src/components/tableItem/ReportTriggerModeItem";
|
||||
import MsTable from "metersphere-frontend/src/components/table/MsTable";
|
||||
import MsDialogHeader from "metersphere-frontend/src/components/MsDialogHeader";
|
||||
import {searchReports} from "@/api/report";
|
||||
|
||||
export default {
|
||||
name: "SameTestReports",
|
||||
components: {MsDialogHeader, MsTable, ReportTriggerModeItem, MsDialogFooter, MsTablePagination},
|
||||
data() {
|
||||
return {
|
||||
loadReportVisible: false,
|
||||
loading: false,
|
||||
tableData: [],
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
report: {},
|
||||
compareReports: [],
|
||||
screenHeight: 'calc(100vh - 400px)',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(report) {
|
||||
this.report = report;
|
||||
this.compareReports = [];
|
||||
this.getCompareReports(report);
|
||||
|
||||
this.compareReports.push(report);
|
||||
|
||||
this.loadReportVisible = true;
|
||||
},
|
||||
close() {
|
||||
this.loadReportVisible = false;
|
||||
},
|
||||
getCompareReports() {
|
||||
let condition = {
|
||||
testId: this.report.testId,
|
||||
filters: {status: ["Completed"]}
|
||||
};
|
||||
this.loading = searchReports(this.currentPage, this.pageSize, condition)
|
||||
.then(res => {
|
||||
let data = res.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
})
|
||||
},
|
||||
handleCompare() {
|
||||
|
||||
let reportIds = [...this.$refs.table.selectIds];
|
||||
this.tableData
|
||||
.filter(r => reportIds.indexOf(r.id) > -1 && this.report.id !== r.id)
|
||||
.forEach(r => this.compareReports.push(r));
|
||||
|
||||
sessionStorage.setItem("compareReports", JSON.stringify(this.compareReports));
|
||||
this.close();
|
||||
this.$router.push({path: '/performance/report/compare/' + reportIds[0]});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
background: 0 0;
|
||||
border: none;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<el-tabs>
|
||||
<el-tab-pane :label="$t('load_test.pressure_config')">
|
||||
<performance-pressure-config :is-read-only="true" :test="test" :report="report" :report-id="reportId"
|
||||
:is-share="isShare" :share-id="shareId" @fileChange="fileChange"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.advanced_config')">
|
||||
<performance-advanced-config :is-read-only="true" :report-id="reportId" :report="report" :is-share="isShare"
|
||||
:share-id="shareId" ref="advancedConfig"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PerformancePressureConfig from "../../../load/PerformancePressureConfig";
|
||||
import PerformanceAdvancedConfig from "../../../load/PerformanceAdvancedConfig";
|
||||
|
||||
export default {
|
||||
name: "TestConfiguration",
|
||||
components: {PerformancePressureConfig, PerformanceAdvancedConfig},
|
||||
props: {
|
||||
test: Object,
|
||||
testId: String,
|
||||
reportId: String,
|
||||
report: Object,
|
||||
isShare: Boolean,
|
||||
shareId: String,
|
||||
},
|
||||
methods: {
|
||||
fileChange(threadGroups) {
|
||||
let csvSet = new Set;
|
||||
threadGroups.forEach(tg => {
|
||||
if (tg.csvFiles) {
|
||||
tg.csvFiles.map(item => csvSet.add(item));
|
||||
}
|
||||
});
|
||||
let csvFiles = [];
|
||||
for (const f of csvSet) {
|
||||
csvFiles.push({name: f, csvSplit: false, csvHasHeader: true});
|
||||
}
|
||||
|
||||
this.$refs.advancedConfig.csvFiles = csvFiles;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,728 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<div style="padding-bottom: 5px;">
|
||||
<el-link type="primary" @click="resetDefault()">{{ $t('load_test.report.set_default') }}</el-link>
|
||||
</div>
|
||||
<el-collapse v-model="activeNames" class="test-detail">
|
||||
<el-collapse-item name="users">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.ActiveThreadsChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'ActiveThreadsChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('ActiveThreadsChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['ActiveThreadsChart']"
|
||||
@change="handleChecked('ActiveThreadsChart')">
|
||||
<div v-for="name in checkOptions['ActiveThreadsChart']" :key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="transactions">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.TransactionsChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'TransactionsChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('TransactionsChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['TransactionsChart']" @change="handleChecked('TransactionsChart')">
|
||||
<div v-for="name in checkOptions['TransactionsChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="responseTime">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.ResponseTimeChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'ResponseTimeChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('ResponseTimeChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['ResponseTimeChart']" @change="handleChecked('ResponseTimeChart')">
|
||||
<div v-for="name in checkOptions['ResponseTimeChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="responseTimePercentiles">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.ResponseTimePercentilesChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'ResponseTimePercentilesChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('ResponseTimePercentilesChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['ResponseTimePercentilesChart']"
|
||||
@change="handleChecked('ResponseTimePercentilesChart')">
|
||||
<div v-for="name in checkOptions['ResponseTimePercentilesChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item :title="$t('load_test.report.ResponseCodeChart')" name="responseCode">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.ResponseCodeChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'ResponseCodeChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('ResponseCodeChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['ResponseCodeChart']" @change="handleChecked('ResponseCodeChart')">
|
||||
<div v-for="name in checkOptions['ResponseCodeChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item :title="$t('load_test.report.LatencyChart')" name="latency">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.LatencyChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'LatencyChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('LatencyChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['LatencyChart']" @change="handleChecked('LatencyChart')">
|
||||
<div v-for="name in checkOptions['LatencyChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item :title="$t('load_test.report.BytesThroughputChart')" name="bytes">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.BytesThroughputChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'BytesThroughputChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('BytesThroughputChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['BytesThroughputChart']"
|
||||
@change="handleChecked('BytesThroughputChart')">
|
||||
<div v-for="name in checkOptions['BytesThroughputChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item :title="$t('load_test.report.ErrorsChart')" name="errors">
|
||||
<template v-slot:title>
|
||||
<div style="width: 100%">
|
||||
<span>{{ $t('load_test.report.ErrorsChart') }}</span>
|
||||
<span style="float:right;">
|
||||
<el-link type="primary" @click="selectAll( 'ErrorsChart', $event)">
|
||||
{{ $t('load_test.report.select_all') }}
|
||||
</el-link>
|
||||
/
|
||||
<el-link type="default" @click="unselectAll('ErrorsChart', $event)">
|
||||
{{ $t('load_test.report.unselect_all') }}
|
||||
</el-link>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox-group v-model="checkList['ErrorsChart']" @change="handleChecked('ErrorsChart')">
|
||||
<div v-for="name in checkOptions['ErrorsChart']"
|
||||
:key="name">
|
||||
<el-tooltip class="item" effect="dark"
|
||||
:content="name"
|
||||
:disabled="name.length < minLength"
|
||||
placement="top">
|
||||
<el-checkbox :label="name"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-col>
|
||||
<el-col :span="18" v-loading="result.loading">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<ms-chart ref="chart2"
|
||||
v-if="refresh"
|
||||
class="chart-config"
|
||||
:options="totalOption"
|
||||
@datazoom="changeDataZoom"
|
||||
:autoresize="true"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :offset="2" :span="20">
|
||||
<el-table
|
||||
v-if="refresh"
|
||||
:data="tableData"
|
||||
stripe
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column label="Label" align="center">
|
||||
<el-table-column
|
||||
prop="label"
|
||||
label="Label"
|
||||
sortable>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="Aggregate" align="center">
|
||||
<el-table-column
|
||||
prop="avg"
|
||||
label="Avg."
|
||||
width="100"
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
prop="min"
|
||||
label="Min."
|
||||
width="100"
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
prop="max"
|
||||
label="Max."
|
||||
width="100"
|
||||
sortable
|
||||
/>
|
||||
</el-table-column>
|
||||
<el-table-column label="Range" align="center">
|
||||
<el-table-column
|
||||
prop="startTime"
|
||||
label="Start"
|
||||
width="160"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="endTime"
|
||||
label="End"
|
||||
width="160"
|
||||
/>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
|
||||
const color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
|
||||
|
||||
const groupBy = function (xs, key) {
|
||||
return xs.reduce(function (rv, x) {
|
||||
(rv[x[key]] = rv[x[key]] || []).push(x);
|
||||
return rv;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const CHART_MAP = [
|
||||
'ActiveThreadsChart',
|
||||
'TransactionsChart',
|
||||
'ResponseTimeChart',
|
||||
'ResponseTimePercentilesChart',
|
||||
'ResponseCodeChart',
|
||||
'ErrorsChart',
|
||||
'LatencyChart',
|
||||
'BytesThroughputChart',
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "TestDetails",
|
||||
components: {MsChart},
|
||||
props: ['report', 'export', 'isShare', 'shareId', 'planReportTemplate'],
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
activeNames: 'users',
|
||||
minLength: 35,
|
||||
loadOption: {},
|
||||
resOption: {},
|
||||
totalOption: {},
|
||||
responseCodes: [],
|
||||
checkList: CHART_MAP.reduce((result, curr) => {
|
||||
result[curr] = [];
|
||||
return result;
|
||||
}, {}),
|
||||
checkOptions: {},
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
},
|
||||
init: false,
|
||||
refresh: true,
|
||||
tableData: [],
|
||||
baseOption: {
|
||||
color: color,
|
||||
grid: {
|
||||
// right: '35%' // 动态改这个值
|
||||
},
|
||||
title: {},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
confine: true,
|
||||
formatter: function (params, ticket, callback) {
|
||||
let result = "";
|
||||
let name = params[0].name;
|
||||
result += name + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let seriesName = params[i].seriesName;
|
||||
if (seriesName.length > 100) {
|
||||
seriesName = seriesName.substring(0, 100);
|
||||
}
|
||||
let value = params[i].value;
|
||||
let marker = params[i].marker;
|
||||
result += marker + seriesName + ": " + value[1] + "<br/>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
y: 'top',
|
||||
},
|
||||
xAxis: {boundaryGap: false},
|
||||
yAxis: [],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
start: 0,
|
||||
end: 100
|
||||
},
|
||||
{
|
||||
start: 0,
|
||||
end: 20
|
||||
}
|
||||
],
|
||||
series: []
|
||||
},
|
||||
seriesData: [],
|
||||
legend: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
resetDefault() {
|
||||
|
||||
this.checkList['ActiveThreadsChart'] = ['ALL'];
|
||||
this.checkList['TransactionsChart'] = ['ALL'];
|
||||
this.checkList['ResponseTimeChart'] = ['ALL'];
|
||||
//
|
||||
this.checkList['ResponseTimePercentilesChart'] = [];
|
||||
this.checkList['ErrorsChart'] = [];
|
||||
this.checkList['LatencyChart'] = [];
|
||||
this.checkList['BytesThroughputChart'] = [];
|
||||
|
||||
this.getTotalChart();
|
||||
},
|
||||
selectAll(name, e) {
|
||||
if (e) {
|
||||
e.stopPropagation(); // 阻止冒泡
|
||||
}
|
||||
this.seriesData = [];
|
||||
this.totalOption = {};
|
||||
this.baseOption.yAxis = [];
|
||||
this.legend = [];
|
||||
this.checkList[name] = this.checkOptions[name];
|
||||
|
||||
this.getTotalChart();
|
||||
},
|
||||
unselectAll(name, e) {
|
||||
if (e) {
|
||||
e.stopPropagation(); // 阻止冒泡
|
||||
}
|
||||
this.seriesData = [];
|
||||
this.totalOption = {};
|
||||
this.baseOption.yAxis = [];
|
||||
this.legend = [];
|
||||
if (name) {
|
||||
this.checkList[name] = [];
|
||||
this.getTotalChart();
|
||||
return;
|
||||
}
|
||||
for (const name in this.checkList) {
|
||||
this.checkList[name] = [];
|
||||
}
|
||||
},
|
||||
handleChecked(name) {
|
||||
|
||||
this.getTotalChart();
|
||||
|
||||
this.refresh = false;
|
||||
this.$nextTick(() => {
|
||||
this.refresh = true;
|
||||
});
|
||||
},
|
||||
initTableData() {
|
||||
for (const name of CHART_MAP) {
|
||||
this.getCheckOptions(name);
|
||||
}
|
||||
|
||||
this.resetDefault();
|
||||
},
|
||||
getCheckOptions(reportKey) {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.checkOptions[reportKey];
|
||||
this.handleGetCheckOptions(data, reportKey);
|
||||
}
|
||||
},
|
||||
handleGetCheckOptions(data, reportKey) {
|
||||
if (!data || data.length === 0) {
|
||||
this.init = false;
|
||||
return;
|
||||
}
|
||||
let yAxisIndex0List = data.filter(m => m.yAxis2 === -1).map(m => m.groupName);
|
||||
yAxisIndex0List = this._unique(yAxisIndex0List);
|
||||
this.checkOptions[reportKey] = ['ALL'].concat(yAxisIndex0List);
|
||||
},
|
||||
getTotalChart() {
|
||||
this.result.loading = true;
|
||||
|
||||
this.totalOption = {};
|
||||
this.seriesData = [];
|
||||
this.baseOption.yAxis = [];
|
||||
this.legend = [];
|
||||
let promises = [];
|
||||
if (this.planReportTemplate) {
|
||||
let chars = [];
|
||||
for (let name in this.checkList) {
|
||||
let data = this.planReportTemplate.checkOptions[name];
|
||||
chars.push({data, 'reportKey': name});
|
||||
}
|
||||
this.handleGetTotalChart(chars);
|
||||
} else {
|
||||
for (let name in this.checkList) {
|
||||
promises.push(this.getChart(name, this.checkList[name]));
|
||||
}
|
||||
Promise.all(promises).then((res) => {
|
||||
this.handleGetTotalChart(res);
|
||||
}).catch(() => {
|
||||
this.result.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
handleGetTotalChart(res) {
|
||||
res = res.filter(v => !!v);
|
||||
if (res.length === 0) {
|
||||
this.refresh = false;
|
||||
this.result.loading = false;
|
||||
} else {
|
||||
this.refresh = true;
|
||||
}
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (i === 0) {
|
||||
this.baseOption.yAxis.push({
|
||||
name: this.$t('load_test.report.' + res[i].reportKey),
|
||||
type: 'value',
|
||||
min: 0,
|
||||
position: 'left',
|
||||
boundaryGap: [0, '100%']
|
||||
});
|
||||
} else {
|
||||
this.baseOption.yAxis.push({
|
||||
name: this.$t('load_test.report.' + res[i].reportKey),
|
||||
type: 'value',
|
||||
min: 0,
|
||||
position: 'right',
|
||||
nameRotate: 20,
|
||||
offset: (i - 1) * 50,
|
||||
boundaryGap: [0, '100%']
|
||||
});
|
||||
}
|
||||
this.totalOption = this.generateOption(this.baseOption, res[i].data, i);
|
||||
}
|
||||
this.totalOption.grid.right = (res.length - 1) * 5 + '%';
|
||||
this.changeDataZoom({start: 0, end: 100});
|
||||
this.result.loading = false;
|
||||
},
|
||||
getChart(reportKey, checkList) {
|
||||
if (!checkList || checkList.length === 0) {
|
||||
return;
|
||||
}
|
||||
this.totalOption = {};
|
||||
},
|
||||
handleGetChart(data, reportKey, checkList) {
|
||||
let allData = [];
|
||||
let checkAllOption = checkList.indexOf('ALL') > -1;
|
||||
if (checkAllOption) {
|
||||
let avgOpt = [
|
||||
'ResponseTimeChart',
|
||||
'ResponseTimePercentilesChart',
|
||||
'LatencyChart',
|
||||
];
|
||||
let result = groupBy(data, 'xAxis');
|
||||
for (const xAxis in result) {
|
||||
let yAxis = result[xAxis].map(a => a.yAxis).reduce((a, b) => a + b, 0);
|
||||
if (avgOpt.indexOf(reportKey) > -1) {
|
||||
yAxis = yAxis / result[xAxis].length;
|
||||
}
|
||||
allData.push({
|
||||
groupName: 'ALL',
|
||||
xAxis: xAxis,
|
||||
yAxis: yAxis
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
data = data.filter(item => {
|
||||
if (checkList.indexOf(item.groupName) > -1) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 选中了all
|
||||
data = data.concat(allData);
|
||||
|
||||
|
||||
// prefix
|
||||
data.forEach(item => {
|
||||
item.groupName = this.$t('load_test.report.' + reportKey) + ': ' + item.groupName;
|
||||
});
|
||||
return {data, reportKey};
|
||||
},
|
||||
generateOption(option, data, yAxisIndex) {
|
||||
let chartData = data;
|
||||
let series = {}, xAxis = [];
|
||||
chartData.forEach(item => {
|
||||
if (!xAxis.includes(item.xAxis)) {
|
||||
xAxis.push(item.xAxis);
|
||||
}
|
||||
xAxis.sort();
|
||||
let name = item.groupName;
|
||||
if (!this.legend.includes(name)) {
|
||||
this.legend.push(name);
|
||||
series[name] = [];
|
||||
}
|
||||
if (series[name]) {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis.toFixed(2)]);
|
||||
}
|
||||
|
||||
});
|
||||
this.$set(option.legend, "data", this.legend);
|
||||
this.$set(option.legend, "type", "scroll");
|
||||
this.$set(option.legend, "bottom", "10px");
|
||||
this.$set(option.xAxis, "data", xAxis);
|
||||
for (let name in series) {
|
||||
let d = series[name];
|
||||
d.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
let items = {
|
||||
name: name,
|
||||
type: 'line',
|
||||
data: d,
|
||||
yAxisIndex: yAxisIndex,
|
||||
smooth: true,
|
||||
sampling: 'lttb',
|
||||
showSymbol: false,
|
||||
animation: !this.export,
|
||||
};
|
||||
this.seriesData.push(items);
|
||||
}
|
||||
this.$set(option, "series", this.seriesData);
|
||||
return option;
|
||||
},
|
||||
changeDataZoom(params) {
|
||||
let start = params.start / 100;
|
||||
let end = params.end / 100;
|
||||
if (params.batch) {
|
||||
start = params.batch[0].start / 100;
|
||||
end = params.batch[0].end / 100;
|
||||
}
|
||||
|
||||
let tableData = [];
|
||||
for (let i = 0; i < this.seriesData.length; i++) {
|
||||
let sub = this.seriesData[i].data, label = this.seriesData[i].name;
|
||||
let len = 0;
|
||||
let min, avg, max, sum = 0, startTime, endTime;
|
||||
for (let j = 0; j < sub.length; j++) {
|
||||
let time = sub[j][0];
|
||||
let value = Number.parseFloat(sub[j][1]);
|
||||
let index = (j / (sub.length - 1)).toFixed(2);
|
||||
if (index < start) {
|
||||
continue;
|
||||
}
|
||||
if (index >= end) {
|
||||
endTime = time;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!startTime) {
|
||||
startTime = time;
|
||||
}
|
||||
|
||||
if (!min && !max) {
|
||||
min = max = value;
|
||||
}
|
||||
|
||||
if (min > value) {
|
||||
min = value;
|
||||
}
|
||||
if (max < value) {
|
||||
max = value;
|
||||
}
|
||||
sum += value;
|
||||
|
||||
len++; // 实际 len
|
||||
}
|
||||
|
||||
avg = (sum / len).toFixed(2);
|
||||
tableData.push({label, min, max, avg, startTime, endTime});
|
||||
}
|
||||
this.tableData = tableData;
|
||||
},
|
||||
_getChartMax(arr) {
|
||||
const max = Math.max(...arr);
|
||||
return Math.ceil(max / 4.5) * 5;
|
||||
},
|
||||
_unique(arr) {
|
||||
return Array.from(new Set(arr));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
if (to.name === "perReportView") {
|
||||
this.id = to.path.split('/')[4];
|
||||
this.init = false;
|
||||
this.initTableData();
|
||||
}
|
||||
},
|
||||
report: {
|
||||
handler(val) {
|
||||
if (!val.status || !val.id) {
|
||||
return;
|
||||
}
|
||||
let status = val.status;
|
||||
this.id = val.id;
|
||||
if (this.init) {
|
||||
return;
|
||||
}
|
||||
if (status === "Running") {
|
||||
this.getTotalChart();
|
||||
} else if (status === "Completed") {
|
||||
this.initTableData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
planReportTemplate: {
|
||||
handler() {
|
||||
if (this.planReportTemplate) {
|
||||
this.initTableData();
|
||||
// todo 勾选无效
|
||||
// this.getTotalChart();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-config {
|
||||
width: 100%;
|
||||
height: 445px;
|
||||
}
|
||||
|
||||
.test-detail {
|
||||
height: calc(100vh - 215px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox__label ) {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,584 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="4">
|
||||
<el-card shadow="always" class="ms-card-index-1">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ maxUsers }}</span>
|
||||
<span class="ms-card-data-unit"> VU</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">{{ $t('load_test.report.ActiveThreadsChart') }}</span>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card shadow="always" class="ms-card-index-2">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ avgTransactions }}</span>
|
||||
<span class="ms-card-data-unit"> TPS</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">{{ $t('load_test.report.TransactionsChart') }}</span>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card shadow="always" class="ms-card-index-3">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ errors }}</span>
|
||||
<span class="ms-card-data-unit"> %</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">{{ $t('load_test.report.ErrorsChart') }}</span>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card shadow="always" class="ms-card-index-4">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ avgResponseTime }}</span>
|
||||
<span class="ms-card-data-unit"> s</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">{{ $t('load_test.report.ResponseTimeChart') }}</span>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card shadow="always" class="ms-card-index-5">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ responseTime90 }}</span>
|
||||
<span class="ms-card-data-unit"> s</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">90% {{ $t('load_test.report.ResponseTimeChart') }}</span>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-card shadow="always" class="ms-card-index-6">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ avgBandwidth }}</span>
|
||||
<span class="ms-card-data-unit"> KiB/s</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">{{ $t('load_test.report.Network') }}</span>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<ms-chart ref="chart1" :options="loadOption" class="chart-config" :autoresize="true"></ms-chart>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<ms-chart ref="chart2" :options="resOption" class="chart-config" :autoresize="true"></ms-chart>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
|
||||
|
||||
const color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
|
||||
|
||||
const groupBy = function (xs, key) {
|
||||
return xs.reduce(function (rv, x) {
|
||||
(rv[x[key]] = rv[x[key]] || []).push(x);
|
||||
return rv;
|
||||
}, {});
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "TestOverview",
|
||||
components: {MsChart},
|
||||
data() {
|
||||
return {
|
||||
maxUsers: "0",
|
||||
avgThroughput: "0",
|
||||
avgTransactions: "0",
|
||||
errors: "0",
|
||||
avgResponseTime: "0",
|
||||
responseTime90: "0",
|
||||
avgBandwidth: "0",
|
||||
loadOption: {},
|
||||
resOption: {},
|
||||
errorOption: {},
|
||||
resCodeOption: {},
|
||||
id: ''
|
||||
};
|
||||
},
|
||||
props: ['report', 'export', 'isShare', 'shareId', 'planReportTemplate'],
|
||||
methods: {
|
||||
initTableData() {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.testOverview;
|
||||
this.buildInfo(data);
|
||||
}
|
||||
this.getLoadChart();
|
||||
this.getResChart();
|
||||
},
|
||||
buildInfo(data) {
|
||||
this.maxUsers = data ? data.maxUsers : '0';
|
||||
this.avgThroughput = data ? data.avgThroughput : '0';
|
||||
this.avgTransactions = data ? data.avgTransactions : '0';
|
||||
this.errors = data ? data.errors : '0';
|
||||
this.avgResponseTime = data ? data.avgResponseTime : '0';
|
||||
this.responseTime90 = data ? data.responseTime90 : '0';
|
||||
this.avgBandwidth = data ? data.avgBandwidth : '0';
|
||||
},
|
||||
getLoadChart() {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.loadChartData;
|
||||
this.handleGetLoadChart(data);
|
||||
}
|
||||
},
|
||||
handleGetLoadChart(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let loadOption = {
|
||||
color: color,
|
||||
title: {
|
||||
text: 'Load',
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#65A2FF'
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
// extraCssText: 'z-index: 999;',
|
||||
confine: true,
|
||||
},
|
||||
legend: {},
|
||||
xAxis: {},
|
||||
series: []
|
||||
};
|
||||
|
||||
let allData = [];
|
||||
let result = groupBy(data, 'xAxis');
|
||||
for (const xAxis in result) {
|
||||
let yAxis1 = result[xAxis].filter(a => a.yAxis2 === -1).map(a => a.yAxis).reduce((a, b) => a + b, 0);
|
||||
let yAxis2 = result[xAxis].filter(a => a.yAxis === -1).map(a => a.yAxis2).reduce((a, b) => a + b, 0);
|
||||
allData.push({
|
||||
groupName: 'users',
|
||||
xAxis: xAxis,
|
||||
yAxis: yAxis1,
|
||||
yAxis2: -1,
|
||||
yAxisIndex: 0,
|
||||
}, {
|
||||
groupName: 'transactions/s',
|
||||
xAxis: xAxis,
|
||||
yAxis: -1,
|
||||
yAxis2: yAxis2,
|
||||
yAxisIndex: 1,
|
||||
});
|
||||
}
|
||||
let yAxisList = allData.filter(m => m.yAxis2 === -1).map(m => m.yAxis);
|
||||
let yAxis2List = allData.filter(m => m.yAxis === -1).map(m => m.yAxis2);
|
||||
let yAxisListMax = this._getChartMax(yAxisList);
|
||||
let yAxis2ListMax = this._getChartMax(yAxis2List);
|
||||
loadOption.yAxis = [{
|
||||
name: 'User',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxisListMax,
|
||||
splitNumber: 5,
|
||||
interval: yAxisListMax / 5
|
||||
},
|
||||
{
|
||||
name: 'Transactions/s',
|
||||
type: 'value',
|
||||
splitNumber: 5,
|
||||
min: 0,
|
||||
max: yAxis2ListMax,
|
||||
interval: yAxis2ListMax / 5
|
||||
}
|
||||
];
|
||||
this.loadOption = this.generateOption(loadOption, allData);
|
||||
},
|
||||
getResChart() {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.responseTimeChartData;
|
||||
this.handleGetResChart(data);
|
||||
}
|
||||
},
|
||||
handleGetResChart(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let resOption = {
|
||||
color: color,
|
||||
title: {
|
||||
text: 'Response Time',
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#99743C'
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
// extraCssText: 'z-index: 999;',
|
||||
confine: true,
|
||||
formatter: function (params, ticket, callback) {
|
||||
let result = "";
|
||||
let name = params[0].name;
|
||||
result += name + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let seriesName = params[i].seriesName;
|
||||
if (seriesName.length > 100) {
|
||||
seriesName = seriesName.substring(0, 100);
|
||||
}
|
||||
let value = params[i].value;
|
||||
let marker = params[i].marker;
|
||||
result += marker + seriesName + ": " + value[1] + "<br/>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
xAxis: {},
|
||||
series: []
|
||||
};
|
||||
|
||||
let allData = [];
|
||||
let result = groupBy(data, 'xAxis');
|
||||
for (const xAxis in result) {
|
||||
let yAxis1 = result[xAxis].filter(a => a.yAxis2 === -1).map(a => a.yAxis).reduce((a, b) => a + b, 0);
|
||||
yAxis1 = yAxis1 / result[xAxis].length;
|
||||
|
||||
allData.push({
|
||||
groupName: 'response',
|
||||
xAxis: xAxis,
|
||||
yAxis: -1,
|
||||
yAxis2: yAxis1,
|
||||
yAxisIndex: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let yAxisList = allData.filter(m => m.yAxis === -1).map(m => m.yAxis2);
|
||||
let yAxisListMax = this._getChartMax(yAxisList);
|
||||
resOption.yAxis = [
|
||||
{
|
||||
name: 'Response Time',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxisListMax,
|
||||
interval: yAxisListMax / 5
|
||||
}
|
||||
];
|
||||
this.resOption = this.generateOption(resOption, allData);
|
||||
},
|
||||
getErrorChart() {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.loadOverviewErrorChart;
|
||||
this.handleGetErrorChart(data);
|
||||
}
|
||||
},
|
||||
handleGetErrorChart(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let errorOption = {
|
||||
color: color,
|
||||
title: {
|
||||
text: 'Errors',
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#99743C'
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
// extraCssText: 'z-index: 999;',
|
||||
confine: true,
|
||||
formatter: function (params, ticket, callback) {
|
||||
let result = "";
|
||||
let name = params[0].name;
|
||||
result += name + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let seriesName = params[i].seriesName;
|
||||
if (seriesName.length > 100) {
|
||||
seriesName = seriesName.substring(0, 100);
|
||||
}
|
||||
let value = params[i].value;
|
||||
let marker = params[i].marker;
|
||||
result += marker + seriesName + ": " + value[1] + "<br/>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
xAxis: {},
|
||||
series: []
|
||||
};
|
||||
|
||||
let allData = [];
|
||||
let result = groupBy(data, 'xAxis');
|
||||
for (const xAxis in result) {
|
||||
let yAxis1 = result[xAxis].filter(a => a.yAxis2 === -1).map(a => a.yAxis).reduce((a, b) => a + b, 0);
|
||||
|
||||
allData.push({
|
||||
groupName: 'errors',
|
||||
xAxis: xAxis,
|
||||
yAxis: -1,
|
||||
yAxis2: yAxis1,
|
||||
yAxisIndex: 0,
|
||||
});
|
||||
}
|
||||
let yAxisList = allData.filter(m => m.yAxis === -1).map(m => m.yAxis2);
|
||||
let yAxisListMax = this._getChartMax(yAxisList);
|
||||
errorOption.yAxis = [
|
||||
{
|
||||
name: 'No',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxisListMax,
|
||||
interval: yAxisListMax / 5
|
||||
}
|
||||
];
|
||||
|
||||
this.errorOption = this.generateOption(errorOption, allData);
|
||||
},
|
||||
getResponseCodeChart() {
|
||||
if (this.planReportTemplate) {
|
||||
let data = this.planReportTemplate.responseCodeChartData;
|
||||
this.handleGetResponseCodeChart(data);
|
||||
}
|
||||
},
|
||||
handleGetResponseCodeChart(data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let resCodeOption = {
|
||||
color: color,
|
||||
title: {
|
||||
text: 'Response code',
|
||||
left: 'center',
|
||||
top: 20,
|
||||
textStyle: {
|
||||
color: '#99743C'
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
// extraCssText: 'z-index: 999;',
|
||||
confine: true,
|
||||
formatter: function (params, ticket, callback) {
|
||||
let result = "";
|
||||
let name = params[0].name;
|
||||
result += name + "<br/>";
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
let seriesName = params[i].seriesName;
|
||||
if (seriesName.length > 100) {
|
||||
seriesName = seriesName.substring(0, 100);
|
||||
}
|
||||
let value = params[i].value;
|
||||
let marker = params[i].marker;
|
||||
result += marker + seriesName + ": " + value[1] + "<br/>";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
xAxis: {},
|
||||
series: []
|
||||
};
|
||||
|
||||
let allData = [];
|
||||
let result = groupBy(data, 'xAxis');
|
||||
for (const xAxis in result) {
|
||||
let yAxis1 = result[xAxis].filter(a => a.yAxis2 === -1).map(a => a.yAxis).reduce((a, b) => a + b, 0);
|
||||
|
||||
allData.push({
|
||||
groupName: 'codes',
|
||||
xAxis: xAxis,
|
||||
yAxis: -1,
|
||||
yAxis2: yAxis1,
|
||||
yAxisIndex: 0,
|
||||
});
|
||||
}
|
||||
let yAxisList = allData.filter(m => m.yAxis === -1).map(m => m.yAxis2);
|
||||
let yAxisListMax = this._getChartMax(yAxisList);
|
||||
resCodeOption.yAxis = [
|
||||
{
|
||||
name: 'No',
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: yAxisListMax,
|
||||
interval: yAxisListMax / 5
|
||||
}
|
||||
];
|
||||
this.resCodeOption = this.generateOption(resCodeOption, allData);
|
||||
},
|
||||
generateOption(option, data) {
|
||||
let chartData = data;
|
||||
let legend = [], series = {}, xAxis = [], seriesData = [], yAxisIndex = {};
|
||||
chartData.forEach(item => {
|
||||
if (!xAxis.includes(item.xAxis)) {
|
||||
xAxis.push(item.xAxis);
|
||||
}
|
||||
xAxis.sort();
|
||||
let name = item.groupName;
|
||||
yAxisIndex[name] = item.yAxisIndex;
|
||||
if (!legend.includes(name)) {
|
||||
legend.push(name);
|
||||
series[name] = [];
|
||||
}
|
||||
if (item.yAxis === -1) {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis2.toFixed(2)]);
|
||||
} else {
|
||||
series[name].splice(xAxis.indexOf(item.xAxis), 0, [item.xAxis, item.yAxis.toFixed(2)]);
|
||||
}
|
||||
});
|
||||
this.$set(option.legend, "data", legend);
|
||||
this.$set(option.legend, "type", "scroll");
|
||||
this.$set(option.legend, "bottom", "10px");
|
||||
this.$set(option.xAxis, "data", xAxis);
|
||||
for (let name in series) {
|
||||
let d = series[name];
|
||||
d.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
let items = {
|
||||
name: name,
|
||||
type: 'line',
|
||||
data: d,
|
||||
smooth: true,
|
||||
sampling: 'lttb',
|
||||
showSymbol: false,
|
||||
animation: !this.export,
|
||||
yAxisIndex: yAxisIndex[name]
|
||||
};
|
||||
seriesData.push(items);
|
||||
}
|
||||
this.$set(option, "series", seriesData);
|
||||
return option;
|
||||
},
|
||||
_getChartMax(arr) {
|
||||
const max = Math.max(...arr);
|
||||
return Math.ceil(max / 4.5) * 5;
|
||||
},
|
||||
_unique(arr) {
|
||||
return Array.from(new Set(arr));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
report: {
|
||||
handler(val) {
|
||||
if (!val.status || !val.id) {
|
||||
return;
|
||||
}
|
||||
let status = val.status;
|
||||
this.id = val.id;
|
||||
if (status === "Completed" || status === "Running") {
|
||||
this.initTableData();
|
||||
} else {
|
||||
this.maxUsers = '0';
|
||||
this.avgThroughput = '0';
|
||||
this.avgTransactions = '0';
|
||||
this.errors = '0';
|
||||
this.avgResponseTime = '0';
|
||||
this.responseTime90 = '0';
|
||||
this.avgBandwidth = '0';
|
||||
this.loadOption = {};
|
||||
this.resOption = {};
|
||||
this.errorOption = {};
|
||||
this.resCodeOption = {};
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
planReportTemplate: {
|
||||
handler() {
|
||||
if (this.planReportTemplate) {
|
||||
this.initTableData();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-card-data {
|
||||
text-align: left;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.ms-card-desc {
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ms-card-data-digital {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.ms-card-data-unit {
|
||||
color: #8492a6;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.ms-card-index-1 .ms-card-data-digital {
|
||||
color: #44b349;
|
||||
}
|
||||
|
||||
.ms-card-index-1 {
|
||||
border-left-color: #44b349;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
.ms-card-index-2 .ms-card-data-digital {
|
||||
color: #65A2FF;
|
||||
}
|
||||
|
||||
.ms-card-index-2 {
|
||||
border-left-color: #65A2FF;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
.ms-card-index-3 .ms-card-data-digital {
|
||||
color: #E6113C;
|
||||
}
|
||||
|
||||
.ms-card-index-3 {
|
||||
border-left-color: #E6113C;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
.ms-card-index-4 .ms-card-data-digital {
|
||||
color: #99743C;
|
||||
}
|
||||
|
||||
.ms-card-index-4 {
|
||||
border-left-color: #99743C;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
.ms-card-index-5 .ms-card-data-digital {
|
||||
color: #99743C;
|
||||
}
|
||||
|
||||
.ms-card-index-5 {
|
||||
border-left-color: #99743C;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
.ms-card-index-6 .ms-card-data-digital {
|
||||
color: #3C9899;
|
||||
}
|
||||
|
||||
.ms-card-index-6 {
|
||||
border-left-color: #3C9899;
|
||||
border-left-width: 3px;
|
||||
}
|
||||
|
||||
.chart-config {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,22 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<span v-if="!reportId">{{$t('commons.not_performed_yet')}}</span>
|
||||
<ms-api-report-view-detail v-if="reportId" :report-id="reportId"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
// import MsApiReportViewDetail from "@/business/api/relation/ApiReportViewDetail";
|
||||
|
||||
export default {
|
||||
name: "ApiTestResult",
|
||||
components: {MsApiReportViewDetail},
|
||||
props: ['reportId'],
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,135 +0,0 @@
|
|||
<template>
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card>
|
||||
<!--<el-card v-loading="result.loading">-->
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-input :disabled="true" :placeholder="$t('load_test.input_name')" v-model="test.name" class="input-with-select">
|
||||
<template v-slot:prepend>
|
||||
<el-select :disabled="true" v-model="project.name" :placeholder="$t('load_test.select_project')" slot="prepend">
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="2">
|
||||
<el-button :disabled="isReadOnly" type="primary" plain @click="runTest">执行</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-tabs class="test-config" v-model="active">
|
||||
<el-tab-pane :label="$t('load_test.basic_config')">
|
||||
<performance-basic-config :is-read-only="true" :test="test" ref="basicConfig"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.pressure_config')">
|
||||
<performance-pressure-config :is-read-only="true" :test="test" :test-id="id" ref="pressureConfig"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
|
||||
<performance-advanced-config :read-only="true" :test-id="id" ref="advancedConfig"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsContainer from "metersphere-frontend/src/components/MsContainer";
|
||||
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
|
||||
// import PerformanceBasicConfig from "../../../../../performance/test/components/PerformanceBasicConfig";
|
||||
// import PerformancePressureConfig from "../../../../../performance/test/components/PerformancePressureConfig";
|
||||
// import PerformanceAdvancedConfig from "../../../../../performance/test/components/PerformanceAdvancedConfig";
|
||||
|
||||
export default {
|
||||
name: "PerformanceTestDetail",
|
||||
components: {
|
||||
PerformanceAdvancedConfig,
|
||||
PerformancePressureConfig,
|
||||
PerformanceBasicConfig,
|
||||
MsMainContainer,
|
||||
MsContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
test: {},
|
||||
savePath: "/performance/save",
|
||||
editPath: "/performance/edit",
|
||||
runPath: "/performance/run",
|
||||
project: {},
|
||||
projectId: '',
|
||||
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'
|
||||
}]
|
||||
}
|
||||
},
|
||||
props: {
|
||||
id: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getTest();
|
||||
},
|
||||
getProject(projectId) {
|
||||
this.$get("/project/get/" + projectId, response => {
|
||||
this.project = response.data;
|
||||
});
|
||||
},
|
||||
getTest() {
|
||||
if (this.id) {
|
||||
this.result = this.$get('/performance/get/' + this.id, response => {
|
||||
if (response.data) {
|
||||
this.test = response.data;
|
||||
this.getProject(this.test.projectId);
|
||||
} else {
|
||||
this.test = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
runTest() {
|
||||
this.result = this.$post(this.runPath, {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
|
||||
this.$success(this.$t('load_test.is_running'));
|
||||
this.$emit('runTest', response.data);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.test-config {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.edit-test-container .input-with-select .el-input-group__prepend {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.advanced-config {
|
||||
height: calc(100vh - 280px);
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
|
@ -1,241 +0,0 @@
|
|||
<template>
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<span v-if="!reportId">{{$t('commons.not_performed_yet')}}</span>
|
||||
<el-card v-loading="result.loading" v-if="reportId">
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
<el-row>
|
||||
<el-breadcrumb separator-class="el-icon-arrow-right">
|
||||
<el-breadcrumb-item :to="{ path: '/performance/test/' + this.projectId }">{{projectName}}
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item :to="{ path: '/performance/test/edit/' + this.testId }">{{testName}}
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>{{reportName}}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</el-row>
|
||||
<!--<el-row class="ms-report-view-btns">-->
|
||||
<!--<el-button :disabled="isReadOnly" type="primary" plain size="mini">{{$t('report.test_stop_now')}}</el-button>-->
|
||||
<!--<el-button :disabled="isReadOnly" type="success" plain size="mini">{{$t('report.test_execute_again')}}</el-button>-->
|
||||
<!--<el-button :disabled="isReadOnly" type="info" plain size="mini">{{$t('report.export')}}</el-button>-->
|
||||
<!--<el-button :disabled="isReadOnly" type="warning" plain size="mini">{{$t('report.compare')}}</el-button>-->
|
||||
<!--</el-row>-->
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-report-time-desc">
|
||||
{{$t('report.test_duration', [this.minutes, this.seconds])}}
|
||||
</span>
|
||||
<span class="ms-report-time-desc">
|
||||
{{$t('report.test_start_time')}}:{{startTime}}
|
||||
</span>
|
||||
<span class="ms-report-time-desc">
|
||||
{{$t('report.test_end_time')}}:{{endTime}}
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider></el-divider>
|
||||
|
||||
<el-tabs v-model="active">
|
||||
<el-tab-pane :label="$t('report.test_overview')">
|
||||
<test-overview :report="report"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_details')">
|
||||
<ms-report-test-details :report="report" ref="testDetails"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_request_statistics')">
|
||||
<request-statistics :report="report"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_error_log')">
|
||||
<error-log :report="report"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('report.test_log_details')">
|
||||
<log-details :report="report"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
</el-card>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
|
||||
import MsContainer from "metersphere-frontend/src/components/MsContainer";
|
||||
// import LogDetails from "metersphere-frontend/src/components/report/LogDetails";
|
||||
// import ErrorLog from "metersphere-frontend/src/components/report/ErrorLog";
|
||||
// import RequestStatistics from "metersphere-frontend/src/components/report/RequestStatistics";
|
||||
// import TestOverview from "metersphere-frontend/src/components/report/TestOverview";
|
||||
// import MsReportTestDetails from "metersphere-frontend/src/components/report/TestDetails";
|
||||
|
||||
export default {
|
||||
name: "PerformanceTestResult",
|
||||
components: {
|
||||
TestOverview,
|
||||
MsReportTestDetails,
|
||||
RequestStatistics,
|
||||
ErrorLog,
|
||||
LogDetails,
|
||||
MsContainer,
|
||||
MsMainContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
active: '0',
|
||||
status: '',
|
||||
reportName: '',
|
||||
testId: '',
|
||||
testName: '',
|
||||
projectId: '',
|
||||
projectName: '',
|
||||
startTime: '0',
|
||||
endTime: '0',
|
||||
minutes: '0',
|
||||
seconds: '0',
|
||||
title: 'Logging',
|
||||
report: {}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
reportId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
reportId() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initBreadcrumb() {
|
||||
if (this.reportId) {
|
||||
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
|
||||
let data = res.data;
|
||||
if (data) {
|
||||
this.reportName = data.name;
|
||||
this.testId = data.testId;
|
||||
this.testName = data.testName;
|
||||
this.projectId = data.projectId;
|
||||
this.projectName = data.projectName;
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
initReportTimeInfo() {
|
||||
if (this.reportId) {
|
||||
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
|
||||
.then(res => {
|
||||
let data = res.data.data;
|
||||
if (data) {
|
||||
this.startTime = data.startTime;
|
||||
this.endTime = data.endTime;
|
||||
let duration = data.duration;
|
||||
this.minutes = Math.floor(duration / 60);
|
||||
this.seconds = duration % 60;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.clearData();
|
||||
})
|
||||
}
|
||||
},
|
||||
checkReportStatus() {
|
||||
if (!this.report) {
|
||||
return;
|
||||
}
|
||||
switch (this.report.status) {
|
||||
case 'Error':
|
||||
// this.$warning(this.$t('report.generation_error'));
|
||||
break;
|
||||
case 'Starting':
|
||||
this.$warning(this.$t('report.start_status'));
|
||||
break;
|
||||
case 'Reporting':
|
||||
case 'Running':
|
||||
case 'Completed':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
clearData() {
|
||||
this.startTime = '0';
|
||||
this.endTime = '0';
|
||||
this.minutes = '0';
|
||||
this.seconds = '0';
|
||||
},
|
||||
init() {
|
||||
this.getReport();
|
||||
this.getReportView();
|
||||
},
|
||||
getReportView() {
|
||||
if (this.reportId) {
|
||||
this.$get("/performance/report/test/pro/info/" + this.reportId, response => {
|
||||
let data = response.data;
|
||||
if (data) {
|
||||
this.status = data.status;
|
||||
this.reportName = data.name;
|
||||
this.testName = data.testName;
|
||||
this.projectName = data.projectName;
|
||||
|
||||
this.$set(this.report, "id", this.reportId);
|
||||
this.$set(this.report, "status", data.status);
|
||||
|
||||
if (this.status === "Completed") {
|
||||
this.result = this.$get("/performance/report/content/report_time/" + this.reportId).then(res => {
|
||||
let data = res.data.data;
|
||||
if (data) {
|
||||
this.startTime = data.startTime;
|
||||
this.endTime = data.endTime;
|
||||
let duration = data.duration;
|
||||
this.minutes = Math.floor(duration / 60);
|
||||
this.seconds = duration % 60;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.clearData();
|
||||
})
|
||||
} else {
|
||||
this.clearData();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
getReport() {
|
||||
if (this.reportId) {
|
||||
this.result = this.$get("/performance/report/" + this.reportId, res => {
|
||||
let data = res.data;
|
||||
if (data) {
|
||||
this.status = data.status;
|
||||
this.$set(this.report, "id", this.reportId);
|
||||
this.$set(this.report, "status", data.status);
|
||||
if (this.status === "Completed") {
|
||||
this.initReportTimeInfo();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.initBreadcrumb();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-report-view-btns {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.ms-report-time-desc {
|
||||
text-align: left;
|
||||
display: block;
|
||||
color: #5C7878;
|
||||
}
|
||||
|
||||
</style>
|
Loading…
Reference in New Issue