fix(ui报告): 修改ui报告的样式

--bug=1024353 --user=宋天阳 【测试跟踪】测试计划执行报告,UI报告展示没有对齐
https://www.tapd.cn/55049933/s/1349610
This commit is contained in:
song-tianyang 2023-03-14 15:42:28 +08:00 committed by 建国
parent 094ca0b791
commit 96d88ba2de
3 changed files with 624 additions and 332 deletions

View File

@ -3,78 +3,140 @@
<el-row>
<el-col>
<span v-if="!debug">
<el-input v-if="nameIsEdit" size="mini" @blur="handleSave(report.name)" @keyup.enter.native="handleSaveKeyUp"
style="width: 200px" v-model="report.name" maxlength="60" show-word-limit/>
<el-input
v-if="nameIsEdit"
size="mini"
@blur="handleSave(report.name)"
@keyup.enter.native="handleSaveKeyUp"
style="width: 200px"
v-model="report.name"
maxlength="60"
show-word-limit
/>
<span v-else>
<router-link v-if="isSingleScenario"
:to="{name: isUi ? 'uiAutomation' : 'ApiAutomation', params: { dataSelectRange: 'edit:' + scenarioId }}">
<router-link
v-if="isSingleScenario"
:to="{
name: isUi ? 'uiAutomation' : 'ApiAutomation',
params: { dataSelectRange: 'edit:' + scenarioId },
}"
>
{{ report.name }}
</router-link>
<span v-else>
{{ report.name }}
</span>
<i v-if="showCancelButton" class="el-icon-edit" style="cursor:pointer" @click="nameIsEdit = true"
@click.stop/>
<i
v-if="showCancelButton"
class="el-icon-edit"
style="cursor: pointer"
@click="nameIsEdit = true"
@click.stop
/>
</span>
</span>
<span v-if="report.endTime || report.createTime">
<span style="margin-left: 10px">{{ $t('report.test_start_time') }}</span>
<span style="margin-left: 10px"
>{{ $t("report.test_start_time") }}</span
>
<span class="time"> {{ report.createTime | datetimeFormat }}</span>
<span style="margin-left: 10px">{{ $t('report.test_end_time') }}</span>
<span style="margin-left: 10px"
>{{ $t("report.test_end_time") }}</span
>
<span class="time"> {{ report.endTime | datetimeFormat }}</span>
</span>
<div style="float: right">
<el-button v-if="!isPlan && (!debug || exportFlag) && !isTemplate && !isUi"
v-permission="['PROJECT_API_REPORT:READ+EXPORT']" :disabled="isReadOnly" class="export-button"
plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
{{ $t('test_track.plan_view.export_report') }}
<el-button
v-if="!isPlan && (!debug || exportFlag) && !isTemplate && !isUi"
v-permission="['PROJECT_API_REPORT:READ+EXPORT']"
:disabled="isReadOnly"
class="export-button"
plain
type="primary"
size="mini"
@click="handleExport(report.name)"
style="margin-right: 10px"
>
{{ $t("test_track.plan_view.export_report") }}
</el-button>
<el-popover
v-if="!isPlan && (!debug || exportFlag) && !isTemplate"
v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']"
style="margin-right: 10px;float: right;"
style="margin-right: 10px; float: right"
placement="bottom"
width="300">
width="300"
>
<p>{{ shareUrl }}</p>
<span style="color: red;float: left;margin-left: 10px;" v-if="application.typeValue">{{
$t('commons.validity_period') + application.typeValue
}}</span>
<span
style="color: red; float: left; margin-left: 10px"
v-if="application.typeValue"
>{{ $t("commons.validity_period") + application.typeValue }}</span
>
<div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}
<el-button
type="primary"
size="mini"
:disabled="!shareUrl"
v-clipboard:copy="shareUrl"
>{{ $t("commons.copy") }}
</el-button>
</div>
<el-button slot="reference" :disabled="isReadOnly" type="danger" plain size="mini"
@click="handleShare(report)">
{{ $t('test_track.plan_view.share_report') }}
<el-button
slot="reference"
:disabled="isReadOnly"
type="danger"
plain
size="mini"
@click="handleShare(report)"
>
{{ $t("test_track.plan_view.share_report") }}
</el-button>
</el-popover>
<el-button v-if="showRerunButton" class="rerun-button" plain size="mini" @click="rerun">
{{ $t('api_test.automation.rerun') }}
<el-button
v-if="showRerunButton"
class="rerun-button"
plain
size="mini"
@click="rerun"
>
{{ $t("api_test.automation.rerun") }}
</el-button>
<el-button v-if="showCancelButton" class="export-button" plain size="mini" @click="returnView">
{{ $t('commons.cancel') }}
<el-button
v-if="showCancelButton"
class="export-button"
plain
size="mini"
@click="returnView"
>
{{ $t("commons.cancel") }}
</el-button>
</div>
</el-col>
</el-row>
<el-row v-if="showProjectEnv" type="flex">
<span> {{ $t('commons.environment') + ':' }} </span>
<div v-for="(values,key) in projectEnvMap" :key="key" style="margin-right: 10px">
<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"/>
<ms-tag
v-for="(item, index) in values"
:key="index"
type="success"
:content="item"
style="margin-left: 2px"
/>
</div>
</el-row>
</header>
</template>
<script>
import { generateShareInfoWithExpired, getShareRedirectUrl } from "@/api/share";
import MsTag from "metersphere-frontend/src/components/MsTag";
import { getCurrentProjectID } from "@/business/utils/sdk-utils";
@ -99,17 +161,17 @@ export default {
type: Boolean,
default: false,
},
isPlan: Boolean
isPlan: Boolean,
},
computed: {
showProjectEnv() {
return this.projectEnvMap && JSON.stringify(this.projectEnvMap) !== '{}';
return this.projectEnvMap && JSON.stringify(this.projectEnvMap) !== "{}";
},
path() {
return "/api/test/edit?id=" + this.report.testId;
},
scenarioId() {
if (typeof this.report.scenarioId === 'string') {
if (typeof this.report.scenarioId === "string") {
return this.report.scenarioId;
} else {
return "";
@ -132,46 +194,45 @@ export default {
isReadOnly: false,
nameIsEdit: false,
shareUrl: "",
application: {}
}
},
created() {
application: {},
};
},
created() {},
methods: {
handleExport(name) {
this.$emit('reportExport', name);
this.$emit("reportExport", name);
},
handleSave(name) {
this.nameIsEdit = false;
this.$emit('reportSave', name);
this.$emit("reportSave", name);
},
handleSaveKeyUp($event) {
$event.target.blur();
},
rerun() {
let type = this.report.reportType;
let rerunObj = {type: type, reportId: this.report.id}
this.$post('/api/test/exec/rerun', rerunObj, res => {
if (res.data !== 'SUCCESS') {
let rerunObj = { type: type, reportId: this.report.id };
this.$post("/api/test/exec/rerun", rerunObj, (res) => {
if (res.data !== "SUCCESS") {
this.$error(res.data);
} else {
this.$success(this.$t('api_test.automation.rerun_success'));
this.$success(this.$t("api_test.automation.rerun_success"));
this.returnView();
}
});
},
returnView() {
if (this.isUi) {
this.$router.push('/ui/report');
this.$router.push("/ui/report");
} else {
this.$router.push('/api/automation/report');
this.$router.push("/api/automation/report");
}
},
handleShare(report) {
this.getProjectApplication();
let pram = {};
pram.customData = report.id;
pram.shareType = 'UI_REPORT';
pram.shareType = "UI_REPORT";
let thisHost = window.location.host;
generateShareInfoWithExpired(pram).then((res) => {
this.shareUrl = getShareRedirectUrl(res.data);
@ -182,29 +243,38 @@ export default {
if (this.isUi) {
path = "/UI_SHARE_REPORT_TIME";
}
this.$get('/project_application/get/' + getCurrentProjectID() + path).then(res => {
this.$get(
"/project_application/get/" + getCurrentProjectID() + path
).then((res) => {
if (res.data && res.data.typeValue) {
let quantity = res.data.typeValue.substring(0, res.data.typeValue.length - 1);
let unit = res.data.typeValue.substring(res.data.typeValue.length - 1);
if (unit === 'H') {
res.data.typeValue = quantity + this.$t('commons.date_unit.hour');
} else if (unit === 'D') {
res.data.typeValue = quantity + this.$t('commons.date_unit.day');
} else if (unit === 'M') {
res.data.typeValue = quantity + this.$t('commons.workspace_unit') + this.$t('commons.date_unit.month');
} else if (unit === 'Y') {
res.data.typeValue = quantity + this.$t('commons.date_unit.year');
let quantity = res.data.typeValue.substring(
0,
res.data.typeValue.length - 1
);
let unit = res.data.typeValue.substring(
res.data.typeValue.length - 1
);
if (unit === "H") {
res.data.typeValue = quantity + this.$t("commons.date_unit.hour");
} else if (unit === "D") {
res.data.typeValue = quantity + this.$t("commons.date_unit.day");
} else if (unit === "M") {
res.data.typeValue =
quantity +
this.$t("commons.workspace_unit") +
this.$t("commons.date_unit.month");
} else if (unit === "Y") {
res.data.typeValue = quantity + this.$t("commons.date_unit.year");
}
this.application = res.data;
}
});
},
}
}
},
};
</script>
<style scoped>
.export-button {
float: right;
margin-right: 10px;
@ -213,7 +283,10 @@ export default {
.rerun-button {
float: right;
margin-right: 10px;
background-color: #F2F9EF;
color: #87C45D;
background-color: #f2f9ef;
color: #87c45d;
}
.report-header {
min-width: 1200px;
}
</style>

View File

@ -10,129 +10,251 @@
</el-row>
<div v-if="isExport">
<span class="ms-req ms-req-error"
v-if="(content.error && content.error>0 )|| (content.errorCode && content.errorCode>0)|| (content.unExecute && content.unExecute>0)">
<span class="ms-req-span"> {{ content.success + content.error + content.errorCode + content.unExecute }} {{
isUi ? '指令' : $t('api_report.request')
}}</span>
<span
class="ms-req ms-req-error"
v-if="
(content.error && content.error > 0) ||
(content.errorCode && content.errorCode > 0) ||
(content.unExecute && content.unExecute > 0)
"
>
<span class="ms-req-span">
{{
content.success +
content.error +
content.errorCode +
content.unExecute
}}
{{ isUi ? "指令" : $t("api_report.request") }}</span
>
</span>
<span class="ms-req ms-req-success" v-else>
<span class="ms-req-span"> {{
content.success ? content.success + content.error : 0
}} {{ isUi ? '指令' : $t('api_report.request') }}</span>
<span class="ms-req-span">
{{ content.success ? content.success + content.error : 0 }}
{{ isUi ? "指令" : $t("api_report.request") }}</span
>
</span>
</div>
<ms-chart id="chart" ref="chart" :options="options" :height="220" style="margin-right: 10px"
:autoresize="true" v-else/>
<el-row type="flex" justify="center" align="middle" style="width: 150px">
<ms-chart
id="chart"
ref="chart"
:options="options"
:height="220"
style="margin-right: 10px"
:autoresize="true"
v-else
/>
<el-row
type="flex"
justify="center"
align="middle"
style="width: 150px"
>
<div>
<div class="metric-icon-box" style="height: 26px">
<span class="ms-point-success" style="margin: 7px;float: left;"/>
<span
class="ms-point-success"
style="margin: 7px; float: left"
/>
<div class="metric-box">
<div class="value" style="font-size: 12px">{{ content.success }} {{ $t('api_report.success') }}</div>
<div class="value" style="font-size: 12px">
{{ content.success }} {{ $t("api_report.success") }}
</div>
</div>
</div>
<el-divider></el-divider>
<div class="metric-icon-box" style="height: 26px">
<span class="ms-point-error" style="margin: 7px;float: left;"/>
<span class="ms-point-error" style="margin: 7px; float: left" />
<div class="metric-box">
<div class="value" style="font-size: 12px">{{ content.error }} {{ $t('api_report.fail') }}</div>
<div class="value" style="font-size: 12px">
{{ content.error }} {{ $t("api_report.fail") }}
</div>
</div>
</div>
<el-divider v-if="content.errorCode > 0"></el-divider>
<div class="metric-icon-box" v-if="content.errorCode > 0" style="height: 26px">
<span class="ms-point-error-code" style="margin: 7px;float: left;"/>
<div
class="metric-icon-box"
v-if="content.errorCode > 0"
style="height: 26px"
>
<span
class="ms-point-error-code"
style="margin: 7px; float: left"
/>
<div class="metric-box" v-if="content.errorCode > 0">
<div class="value" style="font-size: 12px">{{ content.errorCode }}
{{ $t('error_report_library.option.name') }}
<div class="value" style="font-size: 12px">
{{ content.errorCode }}
{{ $t("error_report_library.option.name") }}
</div>
</div>
</div>
<el-divider v-if="content.unExecute > 0"></el-divider>
<div class="metric-icon-box" style="height: 26px" v-if="content.unExecute > 0">
<span class="ms-point-unexecute" style="margin: 7px;float: left;"/>
<div
class="metric-icon-box"
style="height: 26px"
v-if="content.unExecute > 0"
>
<span
class="ms-point-unexecute"
style="margin: 7px; float: left"
/>
<div class="metric-box">
<div class="value" style="font-size: 12px">{{ content.unExecute }}
{{ $t('api_test.home_page.detail_card.unexecute') }}
<div class="value" style="font-size: 12px">
{{ content.unExecute }}
{{ $t("api_test.home_page.detail_card.unexecute") }}
</div>
</div>
</div>
</div>
</el-row>
</el-row>
</div>
<div class="split"></div>
<!-- 场景统计 -->
<div style="width: 50%">
<el-row type="flex" justify="center" align="middle" v-if="report.reportType !== 'API_INTEGRATED'">
<el-row
type="flex"
justify="center"
align="middle"
v-if="report.reportType !== 'API_INTEGRATED'"
>
<div class="metric-box">
<div class="value">{{ content.scenarioTotal ? content.scenarioTotal : 0 }}</div>
<div class="name">{{ $t('api_test.scenario.scenario') }}</div>
<div class="value">
{{ content.scenarioTotal ? content.scenarioTotal : 0 }}
</div>
<div class="name">{{ $t("api_test.scenario.scenario") }}</div>
</div>
<span class="ms-point-success" />
<div class="metric-box">
<div class="value">{{ content.scenarioSuccess ? content.scenarioSuccess : 0 }}</div>
<div class="name">{{ $t('api_report.success') }}</div>
<div class="value">
{{ content.scenarioSuccess ? content.scenarioSuccess : 0 }}
</div>
<div class="name">{{ $t("api_report.success") }}</div>
</div>
<span class="ms-point-error" />
<div class="metric-box">
<div class="value">{{ content.scenarioError ? content.scenarioError : 0 }}</div>
<div class="name">{{ $t('api_report.fail') }}</div>
<div class="value">
{{ content.scenarioError ? content.scenarioError : 0 }}
</div>
<span class="ms-point-error-code"
v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 "/>
<div class="metric-box" v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 ">
<div class="value">{{ content.scenarioErrorReport ? content.scenarioErrorReport : 0 }}</div>
<div class="name">{{ $t('error_report_library.option.name') }}</div>
<div class="name">{{ $t("api_report.fail") }}</div>
</div>
<span
class="ms-point-error-code"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
/>
<div
class="metric-box"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
>
<div class="value">
{{
content.scenarioErrorReport ? content.scenarioErrorReport : 0
}}
</div>
<div class="name">{{ $t("error_report_library.option.name") }}</div>
</div>
<span v-show="showUnExecuteReport" class="ms-point-unexecute" />
<div v-show="showUnExecuteReport" class="metric-box">
<div class="value">{{ content.scenarioUnExecute ? content.scenarioUnExecute : 0 }}</div>
<div class="name">{{ $t('api_test.home_page.detail_card.unexecute') }}</div>
<div class="value">
{{ content.scenarioUnExecute ? content.scenarioUnExecute : 0 }}
</div>
<div class="name">
{{ $t("api_test.home_page.detail_card.unexecute") }}
</div>
</div>
</el-row>
<el-divider v-if="report.reportType !== 'API_INTEGRATED'" />
<el-row type="flex" justify="center" align="middle">
<el-row type="flex" justify="center" align="middle">
<div class="metric-box">
<div class="value">{{ content.scenarioStepTotal ? content.scenarioStepTotal : 0 }}</div>
<div class="name" v-if="report.reportType === 'API_INTEGRATED'">
{{ $t('api_test.definition.request.case') }}
<div class="value">
{{ content.scenarioStepTotal ? content.scenarioStepTotal : 0 }}
</div>
<div class="name" v-if="report.reportType === 'API_INTEGRATED'">
{{ $t("api_test.definition.request.case") }}
</div>
<div class="name" v-else>
{{ $t("test_track.plan_view.step") }}
</div>
<div class="name" v-else>{{ $t('test_track.plan_view.step') }}</div>
</div>
<span class="ms-point-success" />
<div class="metric-box">
<div class="value">{{ content.scenarioStepSuccess ? content.scenarioStepSuccess : 0 }}</div>
<div class="name">{{ $t('api_report.success') }}</div>
<div class="value">
{{
content.scenarioStepSuccess ? content.scenarioStepSuccess : 0
}}
</div>
<div class="name">{{ $t("api_report.success") }}</div>
</div>
<span class="ms-point-error" />
<div class="metric-box">
<div class="value">{{ content.scenarioStepError ? content.scenarioStepError : 0 }}</div>
<div class="name">{{ $t('api_report.fail') }}</div>
<div class="value">
{{ content.scenarioStepError ? content.scenarioStepError : 0 }}
</div>
<span class="ms-point-error-code"
v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 "/>
<div class="metric-box" v-if="content.scenarioErrorReport > 0 || content.scenarioStepErrorReport > 0 ">
<div class="value">{{ content.scenarioStepErrorReport ? content.scenarioStepErrorReport : 0 }}</div>
<div class="name">{{ $t('error_report_library.option.name') }}</div>
<div class="name">{{ $t("api_report.fail") }}</div>
</div>
<span v-show="showUnExecuteReport && !isUi" class="ms-point-unexecute"/>
<span
class="ms-point-error-code"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
/>
<div
class="metric-box"
v-if="
content.scenarioErrorReport > 0 ||
content.scenarioStepErrorReport > 0
"
>
<div class="value">
{{
content.scenarioStepErrorReport
? content.scenarioStepErrorReport
: 0
}}
</div>
<div class="name">
{{ $t("error_report_library.option.name") }}
</div>
</div>
<span
v-show="showUnExecuteReport && !isUi"
class="ms-point-unexecute"
/>
<div v-show="showUnExecuteReport && !isUi" class="metric-box">
<div class="value">{{
content.scenarioStepUnExecuteReport ? content.scenarioStepUnExecuteReport : 0
<div class="value">
{{
content.scenarioStepUnExecuteReport
? content.scenarioStepUnExecuteReport
: 0
}}
</div>
<div class="name">{{ $t('api_test.home_page.detail_card.unexecute') }}</div>
<div class="name">
{{ $t("api_test.home_page.detail_card.unexecute") }}
</div>
<span v-show="showUnExecuteReport && isUi" class="ms-point-unexecute"/>
</div>
<span
v-show="showUnExecuteReport && isUi"
class="ms-point-unexecute"
/>
<div v-show="showUnExecuteReport && isUi" class="metric-box">
<div class="value">{{
content.scenarioStepUnExecuteReport ? content.scenarioStepUnExecuteReport : 0
<div class="value">
{{
content.scenarioStepUnExecuteReport
? content.scenarioStepUnExecuteReport
: 0
}}
</div>
<div class="name">{{ $t('api_test.home_page.detail_card.unexecute') }}</div>
<div class="name">
{{ $t("api_test.home_page.detail_card.unexecute") }}
</div>
</div>
</el-row>
</el-row>
@ -144,22 +266,24 @@
<div class="metric-icon-box">
<i class="el-icon-warning-outline fail"></i>
<div class="value">{{ fail }}</div>
<div class="name">{{ $t('api_report.fail') }}</div>
<div class="name">{{ $t("api_report.fail") }}</div>
</div>
<div class="metric-icon-box">
<i class="el-icon-document-checked assertions"></i>
<div class="value">{{ assertions }}</div>
<div class="name">{{ $t('api_report.assertions_pass') }}</div>
<div class="name">{{ $t("api_report.assertions_pass") }}</div>
</div>
<div class="metric-icon-box" v-if="content.errorCode > 0">
<i class="el-icon-document-checked assertions"></i>
<div class="value">{{ errorCodeAssertions }}</div>
<div class="name">{{ $t('error_report_library.assertion') }}</div>
<div class="name">{{ $t("error_report_library.assertion") }}</div>
</div>
<div class="metric-icon-box" v-if="!isUi">
<i class="el-icon-document-copy total"></i>
<div class="value">{{ this.content.total }}</div>
<div class="name">{{ isUi ? '指令' : $t('api_report.request') }}</div>
<div class="name">
{{ isUi ? "指令" : $t("api_report.request") }}
</div>
</div>
</el-row>
</div>
@ -180,7 +304,7 @@ export default {
isExport: {
type: Boolean,
default: false,
}
},
},
data() {
return {
@ -192,7 +316,7 @@ export default {
scenarioSuccess: 0,
scenarioError: 0,
reqTotal: 0,
}
};
},
created() {
this.initTime();
@ -200,25 +324,25 @@ export default {
methods: {
initTime() {
this.time = this.totalTime;
this.seconds = (this.time) / 1000;
this.seconds = this.time / 1000;
if (this.seconds >= 1) {
if (this.seconds < 60) {
this.seconds = Math.round(this.seconds * 100 / 1) / 100;
this.time = this.seconds + "s"
this.seconds = Math.round((this.seconds * 100) / 1) / 100;
this.time = this.seconds + "s";
}
if (this.seconds > 60) {
this.minutes = Math.round(this.seconds / 60)
this.seconds = Math.round(this.seconds * 100 % 60) / 100;
this.time = this.minutes + "min" + this.seconds + "s"
this.minutes = Math.round(this.seconds / 60);
this.seconds = Math.round((this.seconds * 100) % 60) / 100;
this.time = this.minutes + "min" + this.seconds + "s";
}
if (this.minutes > 60) {
this.hour = Math.round(this.minutes / 60)
this.minutes = Math.round(this.minutes % 60)
this.time = this.hour + "hour" + this.minutes + "min" + this.seconds + "s"
this.hour = Math.round(this.minutes / 60);
this.minutes = Math.round(this.minutes % 60);
this.time =
this.hour + "hour" + this.minutes + "min" + this.seconds + "s";
}
} else {
this.time = this.totalTime + "ms"
this.time = this.totalTime + "ms";
}
},
},
@ -241,36 +365,38 @@ export default {
},
options() {
return {
color: ['#67C23A', '#F56C6C', '#F6972A', '#9C9B9A'],
color: ["#67C23A", "#F56C6C", "#F6972A", "#9C9B9A"],
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
trigger: "item",
formatter: "{b}: {c} ({d}%)",
},
title: [{
title: [
{
text: this.totalCount,
subtext: this.isUi ? '指令' : this.$t('api_report.request'),
top: 'center',
left: 'center',
subtext: this.isUi ? "指令" : this.$t("api_report.request"),
top: "center",
left: "center",
textStyle: {
rich: {
align: 'center',
align: "center",
value: {
fontSize: 32,
fontWeight: 'bold',
padding: [10, 0]
fontWeight: "bold",
padding: [10, 0],
},
name: {
fontSize: 14,
fontWeight: 'normal',
color: '#7F7F7F',
}
}
}
}],
fontWeight: "normal",
color: "#7F7F7F",
},
},
},
},
],
series: [
{
type: 'pie',
radius: ['80%', '90%'],
type: "pie",
radius: ["80%", "90%"],
avoidLabelOverlap: false,
hoverAnimation: false,
label: {
@ -278,24 +404,33 @@ export default {
},
itemStyle: {
borderColor: "#FFF",
shadowColor: '#E1E1E1',
shadowBlur: 10
shadowColor: "#E1E1E1",
shadowBlur: 10,
},
labelLine: {
show: false
show: false,
},
data: [
{value: this.content.success, name: this.$t('api_report.success')},
{value: this.content.error, name: this.$t('api_report.fail')},
{value: this.content.errorCode, name: this.$t('error_report_library.option.name')},
{value: this.content.unExecute, name: this.$t('api_test.home_page.detail_card.unexecute')},
]
}
]
{
value: this.content.success,
name: this.$t("api_report.success"),
},
{ value: this.content.error, name: this.$t("api_report.fail") },
{
value: this.content.errorCode,
name: this.$t("error_report_library.option.name"),
},
{
value: this.content.unExecute,
name: this.$t("api_test.home_page.detail_card.unexecute"),
},
],
},
],
};
},
fail() {
return (this.content.error / this.content.total * 100).toFixed(0) + "%";
return ((this.content.error / this.content.total) * 100).toFixed(0) + "%";
},
assertions() {
return this.content.passAssertions + " / " + this.content.totalAssertions;
@ -307,16 +442,22 @@ export default {
return this.report.reportType && this.report.reportType.startsWith("UI");
},
showUnExecuteReport() {
return (this.content.scenarioStepUnExecuteReport && this.content.scenarioStepUnExecuteReport > 0)
|| (this.content.scenarioUnExecute && this.content.scenarioUnExecute > 0) || (this.content.unExecute && this.content.unExecute > 0);
return (
(this.content.scenarioStepUnExecuteReport &&
this.content.scenarioStepUnExecuteReport > 0) ||
(this.content.scenarioUnExecute &&
this.content.scenarioUnExecute > 0) ||
(this.content.unExecute && this.content.unExecute > 0)
);
},
},
}
};
</script>
<style scoped>
.metric-container {
padding: 5px 10px;
min-width: 1200px;
}
.metric-container #chart {
@ -328,25 +469,25 @@ export default {
.metric-container .split {
margin: 20px;
height: 100px;
border-left: 1px solid #D8DBE1;
border-left: 1px solid #d8dbe1;
}
.metric-container .circle {
width: 12px;
height: 12px;
border-radius: 50%;
box-shadow: 0 0 20px 1px rgba(200, 216, 226, .42);
box-shadow: 0 0 20px 1px rgba(200, 216, 226, 0.42);
display: inline-block;
margin-right: 10px;
vertical-align: middle;
}
.metric-container .circle.success {
background-color: #67C23A;
background-color: #67c23a;
}
.metric-container .circle.fail {
background-color: #F56C6C;
background-color: #f56c6c;
}
.metric-box {
@ -358,18 +499,18 @@ export default {
.metric-box .value {
font-size: 32px;
font-weight: 600;
letter-spacing: -.5px;
letter-spacing: -0.5px;
}
.metric-time .value {
font-size: 25px;
font-weight: 400;
letter-spacing: -.5px;
letter-spacing: -0.5px;
}
.metric-box .name {
font-size: 16px;
letter-spacing: -.2px;
letter-spacing: -0.2px;
color: #404040;
}
@ -381,7 +522,7 @@ export default {
.metric-icon-box .value {
font-size: 20px;
font-weight: 600;
letter-spacing: -.4px;
letter-spacing: -0.4px;
line-height: 28px;
vertical-align: middle;
}
@ -389,12 +530,12 @@ export default {
.metric-icon-box .name {
font-size: 13px;
letter-spacing: 1px;
color: #BFBFBF;
color: #bfbfbf;
line-height: 18px;
}
.metric-icon-box .fail {
color: #F56C6C;
color: #f56c6c;
font-size: 40px;
}
@ -416,11 +557,11 @@ export default {
}
.ms-req-error {
border: 5px #F56C6C solid;
border: 5px #f56c6c solid;
}
.ms-req-success {
border: 5px #67C23A solid;
border: 5px #67c23a solid;
}
.ms-req-span {
@ -440,7 +581,7 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #67C23A;
background-color: #67c23a;
}
.ms-point-error {
@ -452,7 +593,7 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #F56C6C;
background-color: #f56c6c;
}
.ms-point-error-code {
@ -464,7 +605,7 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #F6972A;
background-color: #f6972a;
}
.ms-point-unexecute {
@ -476,6 +617,6 @@ export default {
vertical-align: top;
margin-left: 20px;
margin-right: 20px;
background-color: #9C9B9A;
background-color: #9c9b9a;
}
</style>

View File

@ -13,14 +13,22 @@
:report="report"
:project-env-map="projectEnvMap"
@reportExport="handleExport"
@reportSave="handleSave"/>
@reportSave="handleSave"
/>
<!-- content -->
<main v-if="isNotRunning">
<!-- content header chart -->
<ms-metric-chart :content="content" :totalTime="totalTime" :report="report"/>
<el-tabs v-model="activeName" @tab-click="handleClick">
<ms-metric-chart
:content="content"
:totalTime="totalTime"
:report="report"
/>
<el-tabs
v-model="activeName"
@tab-click="handleClick"
style="min-width: 1200px"
>
<!-- all step-->
<el-tab-pane :label="$t('api_report.total')" name="total">
<ms-scenario-results
@ -31,12 +39,13 @@
:is-share="isShare"
:share-id="shareId"
v-on:requestResult="requestResult"
ref="resultsTree"/>
ref="resultsTree"
/>
</el-tab-pane>
<!-- fail step -->
<el-tab-pane name="fail">
<template slot="label">
<span class="fail">{{ $t('api_report.fail') }}</span>
<span class="fail">{{ $t("api_report.fail") }}</span>
</template>
<ms-scenario-results
v-on:requestResult="requestResult"
@ -45,14 +54,16 @@
:is-share="isShare"
:is-template="isTemplate"
:share-id="shareId"
:treeData="fullTreeNodes" ref="failsTree"
:errorReport="content.error"/>
:treeData="fullTreeNodes"
ref="failsTree"
:errorReport="content.error"
/>
</el-tab-pane>
<!--error step -->
<el-tab-pane name="errorReport" v-if="content.errorCode > 0">
<template slot="label">
<span class="fail" style="color: #F6972A">
{{ $t('error_report_library.option.name') }}
<span class="fail" style="color: #f6972a">
{{ $t("error_report_library.option.name") }}
</span>
</template>
<ms-scenario-results
@ -62,14 +73,15 @@
:is-template="isTemplate"
:share-id="shareId"
:console="content.console"
:treeData="fullTreeNodes" ref="errorReportTree"/>
:treeData="fullTreeNodes"
ref="errorReportTree"
/>
</el-tab-pane>
<!-- Not performed step -->
<el-tab-pane name="unExecute" v-if="content.unExecute > 0">
<template slot="label">
<span class="fail"
style="color: #9C9B9A">
{{ $t('api_test.home_page.detail_card.unexecute') }}
<span class="fail" style="color: #9c9b9a">
{{ $t("api_test.home_page.detail_card.unexecute") }}
</span>
</template>
<ms-scenario-results
@ -79,18 +91,23 @@
:is-template="isTemplate"
:share-id="shareId"
:console="content.console"
:treeData="fullTreeNodes" ref="unExecuteTree"/>
:treeData="fullTreeNodes"
ref="unExecuteTree"
/>
</el-tab-pane>
<!-- console -->
<el-tab-pane name="console">
<template slot="label">
<span class="console">{{ $t('api_test.definition.request.console') }}</span>
<span class="console">{{
$t("api_test.definition.request.console")
}}</span>
</template>
<ms-code-edit
:mode="'text'"
:read-only="true"
:data.sync="content.console"
height="calc(100vh - 500px)"/>
height="calc(100vh - 500px)"
/>
</el-tab-pane>
</el-tabs>
</main>
@ -101,7 +118,6 @@
</template>
<script>
import MsRequestResult from "./RequestResult";
import MsRequestResultTail from "./RequestResultTail";
import MsScenarioResult from "./ScenarioResult";
@ -112,8 +128,17 @@ import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer
import MsApiReportViewHeader from "./ApiReportViewHeader";
import { STEP } from "./Setting";
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import {getCurrentProjectID, getUUID, hasLicense, windowPrint} from "@/business/utils/sdk-utils";
import {getScenarioReport, getScenarioReportAll, getShareScenarioReport} from "@/api/ui-report";
import {
getCurrentProjectID,
getUUID,
hasLicense,
windowPrint,
} from "@/business/utils/sdk-utils";
import {
getScenarioReport,
getScenarioReportAll,
getShareScenarioReport,
} from "@/api/ui-report";
export default {
name: "UiShareReportDetail",
@ -121,7 +146,12 @@ export default {
MsApiReportViewHeader,
MsMainContainer,
MsCodeEdit,
MsContainer, MsScenarioResults, MsRequestResultTail, MsMetricChart, MsScenarioResult, MsRequestResult
MsContainer,
MsScenarioResults,
MsRequestResultTail,
MsMetricChart,
MsScenarioResult,
MsRequestResult,
},
data() {
return {
@ -139,11 +169,11 @@ export default {
reportExportVisible: false,
fullTreeNodes: [],
showRerunButton: false,
stepFilter: new STEP,
stepFilter: new STEP(),
exportReportIsOk: false,
tempResult: [],
projectEnvMap: {},
}
};
},
activated() {
this.isRequestResult = false;
@ -160,8 +190,8 @@ export default {
isPlan: Boolean,
showCancelButton: {
type: Boolean,
default: true
}
default: true,
},
},
watch: {
reportId() {
@ -173,7 +203,7 @@ export default {
if (this.isTemplate) {
this.getReport();
}
}
},
},
methods: {
filter(index) {
@ -198,20 +228,32 @@ export default {
this.showRerunButton = false;
},
rerunVerify() {
if (hasLicense() && this.fullTreeNodes && this.fullTreeNodes.length > 0 && !this.isShare) {
this.fullTreeNodes.forEach(item => {
if (
hasLicense() &&
this.fullTreeNodes &&
this.fullTreeNodes.length > 0 &&
!this.isShare
) {
this.fullTreeNodes.forEach((item) => {
item.redirect = true;
if (item.totalStatus === 'fail' || item.totalStatus === 'error' || item.unExecuteTotal > 0
|| (item.type === "API" && item.totalStatus === 'unexecute')) {
if (
item.totalStatus === "fail" ||
item.totalStatus === "error" ||
item.unExecuteTotal > 0 ||
(item.type === "API" && item.totalStatus === "unexecute")
) {
this.showRerunButton = true;
}
}
)
});
}
},
handleClick(tab, event) {
this.isRequestResult = false;
if (this.report && this.report.reportVersion && this.report.reportVersion > 1) {
if (
this.report &&
this.report.reportVersion &&
this.report.reportVersion > 1
) {
this.filter(tab.index);
}
},
@ -219,28 +261,28 @@ export default {
this.isActive = !this.isActive;
},
formatResult(res) {
let resMap = new Map;
let resMap = new Map();
let array = [];
if (res && res.scenarios) {
res.scenarios.forEach(item => {
res.scenarios.forEach((item) => {
if (item && item.requestResults) {
item.requestResults.forEach(req => {
item.requestResults.forEach((req) => {
req.responseResult.console = res.console;
resMap.set(req.id + req.name, req);
req.name = item.name + "^@~@^" + req.name + "UUID=" + getUUID();
array.push(req);
})
});
}
})
});
}
this.formatTree(array, this.fullTreeNodes);
this.sort(this.fullTreeNodes);
this.$emit('refresh', resMap);
this.$emit("refresh", resMap);
},
formatTree(array, tree) {
array.map((item) => {
let key = item.name;
let nodeArray = key.split('^@~@^');
let nodeArray = key.split("^@~@^");
let children = tree;
//1
//hashTreeID
@ -263,11 +305,13 @@ export default {
label: nodeArray[i],
value: item,
};
if (i !== (nodeArray.length - 1)) {
if (i !== nodeArray.length - 1) {
node.children = [];
} else {
if (item.subRequestResults && item.subRequestResults.length > 0) {
let itemChildren = this.deepFormatTreeNode(item.subRequestResults);
let itemChildren = this.deepFormatTreeNode(
item.subRequestResults
);
node.children = itemChildren;
if (node.label.indexOf("UUID=")) {
node.label = node.label.split("UUID=")[0];
@ -281,7 +325,6 @@ export default {
let isExist = false;
for (let j in children) {
if (children[j].label === node.label) {
let idIsPath = true;
//ID
//
@ -312,7 +355,8 @@ export default {
if (i !== nodeArray.length - 1 && !children[j].children) {
children[j].children = [];
}
children = (i === nodeArray.length - 1 ? children : children[j].children);
children =
i === nodeArray.length - 1 ? children : children[j].children;
isExist = true;
break;
}
@ -320,21 +364,27 @@ export default {
}
if (!isExist) {
children.push(node);
if (i !== nodeArray.length - 1 && !children[children.length - 1].children) {
if (
i !== nodeArray.length - 1 &&
!children[children.length - 1].children
) {
children[children.length - 1].children = [];
}
children = (i === nodeArray.length - 1 ? children : children[children.length - 1].children);
children =
i === nodeArray.length - 1
? children
: children[children.length - 1].children;
}
}
})
});
},
deepFormatTreeNode(array) {
let returnChildren = [];
array.map((item) => {
let children = [];
let key = item.name.split('^@~@^')[0];
let nodeArray = key.split('<->');
let key = item.name.split("^@~@^")[0];
let nodeArray = key.split("<->");
//1
//hashTreeID
let scenarioId = "";
@ -351,17 +401,16 @@ export default {
let node = {
label: nodeArray[0],
value: item,
children: []
children: [],
};
if (item.subRequestResults && item.subRequestResults.length > 0) {
let itemChildren = this.deepFormatTreeNode(item.subRequestResults);
node.children = itemChildren;
}
children.push(node);
children.forEach(itemNode => {
children.forEach((itemNode) => {
returnChildren.push(itemNode);
});
});
return returnChildren;
},
@ -380,7 +429,10 @@ export default {
//
if (scenarioDefinition[i]) {
scenarioDefinition[i].index = Number(i) + 1;
if (scenarioDefinition[i].children && scenarioDefinition[i].children.length > 0) {
if (
scenarioDefinition[i].children &&
scenarioDefinition[i].children.length > 0
) {
this.recursiveSorting(scenarioDefinition[i].children);
}
}
@ -390,7 +442,7 @@ export default {
if (this.exportReportIsOk) {
this.startExport();
} else {
getScenarioReportAll(this.reportId).then(data => {
getScenarioReportAll(this.reportId).then((data) => {
if (data && data.data.content) {
let report = JSON.parse(data.data.content);
if (report.projectEnvMap) {
@ -400,7 +452,8 @@ export default {
this.fullTreeNodes = report.steps;
this.content.console = report.console;
this.content.error = report.error;
let successCount = (report.total - report.error - report.errorCode - report.unExecute);
let successCount =
report.total - report.error - report.errorCode - report.unExecute;
this.content.success = successCount;
if (this.report.endTime > 0) {
this.totalTime = this.report.endTime - this.report.createTime;
@ -409,7 +462,7 @@ export default {
}
}
this.exportReportIsOk = true;
setTimeout(this.startExport, 500)
setTimeout(this.startExport, 500);
});
}
},
@ -426,13 +479,13 @@ export default {
}
} else if (this.isShare) {
if (this.reportId) {
getShareScenarioReport(this.shareId, this.reportId).then(data => {
getShareScenarioReport(this.shareId, this.reportId).then((data) => {
this.checkReport(data.data);
this.handleGetScenarioReport(data.data);
});
}
} else {
getScenarioReport(this.reportId).then(data => {
getScenarioReport(this.reportId).then((data) => {
this.checkReport(data.data);
this.handleGetScenarioReport(data.data);
});
@ -441,7 +494,7 @@ export default {
checkReport(data) {
if (!data) {
this.$emit('reportNotExist');
this.$emit("reportNotExist");
}
},
handleGetScenarioReport(data) {
@ -450,7 +503,7 @@ export default {
if (this.report.reportVersion && this.report.reportVersion > 1) {
this.report.status = data.status;
if (!this.isNotRunning) {
setTimeout(this.getReport, 2000)
setTimeout(this.getReport, 2000);
} else {
if (data.content) {
let report = JSON.parse(data.content);
@ -472,7 +525,11 @@ export default {
}
this.content.console = report.console;
this.content.error = report.error;
let successCount = (report.total - report.error - report.errorCode - report.unExecute);
let successCount =
report.total -
report.error -
report.errorCode -
report.unExecute;
this.content.success = successCount;
if (this.report.endTime > 0) {
this.totalTime = this.report.endTime - this.report.createTime;
@ -481,7 +538,11 @@ export default {
}
}
//
if (this.report && this.report.reportType === 'SCENARIO_INTEGRATED' || this.report.reportType === 'API_INTEGRATED') {
if (
(this.report &&
this.report.reportType === "SCENARIO_INTEGRATED") ||
this.report.reportType === "API_INTEGRATED"
) {
this.rerunVerify();
}
this.loading = false;
@ -490,8 +551,8 @@ export default {
this.buildReport();
}
} else {
this.$emit('invisible');
this.$warning(this.$t('commons.report_delete'));
this.$emit("invisible");
this.$warning(this.$t("commons.report_delete"));
}
},
checkOrder(origin) {
@ -500,36 +561,42 @@ export default {
}
if (Array.isArray(origin)) {
this.sortChildren(origin);
origin.forEach(v => {
origin.forEach((v) => {
if (v.children) {
this.checkOrder(v.children)
this.checkOrder(v.children);
}
})
});
}
},
sortChildren(source) {
if (!source) {
return;
}
source.forEach(item => {
source.forEach((item) => {
let children = item.children;
if (children && children.length > 0) {
let tempArr = new Array(children.length);
let tempMap = new Map();
for (let i = 0; i < children.length; i++) {
if (!children[i].value || !children[i].value.startTime || children[i].value.startTime === 0) {
if (
!children[i].value ||
!children[i].value.startTime ||
children[i].value.startTime === 0
) {
//valuestep
tempArr[i] = children[i];
//
tempMap.set(children[i].stepId, children[i])
tempMap.set(children[i].stepId, children[i]);
}
}
//step
let arr = children.filter(m => {
let arr = children
.filter((m) => {
return !tempMap.get(m.stepId);
}).sort((m, n) => {
})
.sort((m, n) => {
//
return m.value.startTime - n.value.startTime;
});
@ -548,12 +615,11 @@ export default {
//
item.children = tempArr;
}
})
});
},
buildReport() {
if (this.report) {
if (this.isNotRunning) {
this.content = JSON.parse(this.report.content);
if (!this.content) {
this.content = { scenarios: [] };
@ -563,21 +629,21 @@ export default {
this.computeTotalTime();
this.loading = false;
} else {
setTimeout(this.getReport, 2000)
setTimeout(this.getReport, 2000);
}
} else {
this.loading = false;
this.$error(this.$t('api_report.not_exist'));
this.$error(this.$t("api_report.not_exist"));
}
},
getFails() {
if (this.isNotRunning) {
this.fails = [];
let array = [];
this.totalTime = 0
this.totalTime = 0;
if (this.content.scenarios) {
this.content.scenarios.forEach((scenario) => {
this.totalTime = this.totalTime + Number(scenario.responseTime)
this.totalTime = this.totalTime + Number(scenario.responseTime);
let failScenario = Object.assign({}, scenario);
if (scenario.error > 0) {
this.fails.push(failScenario);
@ -588,9 +654,9 @@ export default {
failScenario.requestResults.push(failRequest);
array.push(request);
}
})
});
}
})
});
}
this.formatTree(array, this.failsTreeNodes);
this.sort(this.failsTreeNodes);
@ -611,14 +677,14 @@ export default {
}
let resTime;
if (startTime === 0 || endTime === 0) {
resTime = 0
resTime = 0;
} else {
resTime = endTime - startTime
resTime = endTime - startTime;
}
requestTime = requestTime + resTime;
})
})
this.totalTime = requestTime
});
});
this.totalTime = requestTime;
}
},
requestResult(requestResult) {
@ -631,8 +697,11 @@ export default {
});
},
formatExportApi(array, scenario) {
array.forEach(item => {
if (this.stepFilter && this.stepFilter.get("AllSamplerProxy").indexOf(item.type) !== -1) {
array.forEach((item) => {
if (
this.stepFilter &&
this.stepFilter.get("AllSamplerProxy").indexOf(item.type) !== -1
) {
if (item.errorCode) {
item.value.errorCode = item.errorCode;
}
@ -641,72 +710,82 @@ export default {
if (item.children && item.children.length > 0) {
this.formatExportApi(item.children, scenario);
}
})
});
},
handleExport() {
this.getReportByExport();
},
startExport() {
if (this.report.reportVersion && this.report.reportVersion > 1) {
if (this.report.reportType === 'API_INTEGRATED' || this.report.reportType === 'UI_INTEGRATED') {
if (
this.report.reportType === "API_INTEGRATED" ||
this.report.reportType === "UI_INTEGRATED"
) {
let scenario = { name: "", requestResults: [] };
this.content.scenarios = [scenario];
this.formatExportApi(this.fullTreeNodes, scenario);
} else {
if (this.fullTreeNodes) {
this.fullTreeNodes.forEach(item => {
this.fullTreeNodes.forEach((item) => {
if (item.type === "scenario" || item.type === "UiScenario") {
let scenario = { name: item.label, requestResults: [] };
if (this.content.scenarios && this.content.scenarios.length > 0) {
if (
this.content.scenarios &&
this.content.scenarios.length > 0
) {
this.content.scenarios.push(scenario);
} else {
this.content.scenarios = [scenario];
}
this.formatExportApi(item.children, scenario);
}
})
});
}
}
}
this.reportExportVisible = true;
let reset = this.exportReportReset;
this.$nextTick(() => {
windowPrint('apiTestReport', 0.57);
windowPrint("apiTestReport", 0.57);
reset();
});
},
handleSave() {
if (!this.report.name) {
this.$warning(this.$t('api_test.automation.report_name_info'));
this.$warning(this.$t("api_test.automation.report_name_info"));
return;
}
this.loading = true;
let url = "/ui/scenario/report/reName";
this.result = this.$post(url, {
this.result = this.$post(
url,
{
id: this.report.id,
name: this.report.name,
reportType: this.report.reportType
}, response => {
this.$success(this.$t('commons.save_success'));
reportType: this.report.reportType,
},
(response) => {
this.$success(this.$t("commons.save_success"));
this.loading = false;
this.$emit('refresh');
}, error => {
this.$emit("refresh");
},
(error) => {
this.loading = false;
});
}
);
},
exportReportReset() {
this.$router.go(0);
},
handleProjectChange() {
this.$router.push('/api/automation/report');
this.$router.push("/api/automation/report");
},
},
created() {
this.getReport();
},
destroyed() {
},
destroyed() {},
computed: {
path() {
return "/api/test/edit?id=" + this.report.testId;
@ -717,8 +796,8 @@ export default {
projectId() {
return getCurrentProjectID();
},
}
}
},
};
</script>
<style>
.report-container .el-tabs__header {
@ -727,7 +806,6 @@ export default {
</style>
<style scoped>
.report-container {
height: calc(100vh - 155px);
min-height: 600px;
@ -748,7 +826,7 @@ export default {
}
.report-container .fail {
color: #F56C6C;
color: #f56c6c;
}
.report-container .is-active .fail {