refactor(接口测试): 接口报告优化

--bug=1015170 --user=吕梦园
https://www.tapd.cn/55049933/prong/stories/view/1155049933001015170
This commit is contained in:
teukkk 2024-06-04 19:35:17 +08:00 committed by 刘瑞斌
parent 4530788d66
commit 7ee89429c7
12 changed files with 197 additions and 382 deletions

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" rx="4" fill="#3370FF"/>
<path d="M11.3334 9.00008C11.3334 8.81599 11.4826 8.66675 11.6667 8.66675H12.3334C12.5174 8.66675 12.6667 8.81599 12.6667 9.00008V11.3334H15C15.1841 11.3334 15.3334 11.4827 15.3334 11.6667V12.3334C15.3334 12.5175 15.1841 12.6667 15 12.6667H11.6667C11.4826 12.6667 11.3334 12.5175 11.3334 12.3334V9.00008Z" fill="white"/>
<path d="M12 19.3334C16.0501 19.3334 19.3334 16.0502 19.3334 12.0001C19.3334 7.94999 16.0501 4.66675 12 4.66675C7.94993 4.66675 4.66669 7.94999 4.66669 12.0001C4.66669 16.0502 7.94993 19.3334 12 19.3334ZM12 18.0001C8.68631 18.0001 6.00002 15.3138 6.00002 12.0001C6.00002 8.68637 8.68631 6.00008 12 6.00008C15.3137 6.00008 18 8.68637 18 12.0001C18 15.3138 15.3137 18.0001 12 18.0001Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 876 B

View File

@ -24,6 +24,15 @@ export interface PlanReportDetail {
// apiScenarioCount: countDetail; // 接口场景用例分析-用例数
}
export interface ReportMetricsItemModel {
unit: string;
value: number | string;
name: string;
icon: string;
tip?: string;
runMode?: string;
}
export interface StatusListType {
label: string;
value: keyof countDetail;

View File

@ -160,6 +160,14 @@
const { t } = useI18n();
const responseCompositionTabList = [
{
label: t('apiTestDebug.extract'),
value: ResponseComposition.EXTRACT,
},
{
label: t('apiTestDebug.assertion'),
value: ResponseComposition.ASSERTION,
},
{
label: t('apiTestDebug.responseBody'),
value: ResponseComposition.BODY,
@ -176,14 +184,6 @@
label: t('apiTestDebug.console'),
value: ResponseComposition.CONSOLE,
},
{
label: t('apiTestDebug.extract'),
value: ResponseComposition.EXTRACT,
},
{
label: t('apiTestDebug.assertion'),
value: ResponseComposition.ASSERTION,
},
];
const activeTab = ref(ResponseComposition.BODY);

View File

@ -45,7 +45,7 @@
<ExtractTable :request-result="props.requestResult" :scroll="{ x: '100%' }" />
</div>
<div v-if="!expandIds.includes(item.value) && item.value === ResponseComposition.ASSERTION">
<ResAssertion :request-result="props.requestResult" />
<ResAssertion :request-result="props.requestResult" :scroll="{ x: '100%' }" />
</div>
</div>
</transition>

View File

@ -166,6 +166,7 @@
{
title: 'apiTestManagement.taskOperator',
dataIndex: 'operationUser',
showTooltip: true,
width: 100,
},
{
@ -236,7 +237,6 @@
.ms-scroll-bar();
}
.history-table-before {
display: flex;
justify-content: space-between;

View File

@ -57,21 +57,6 @@
</div>
</template>
</a-popover>
<a-popover position="left" content-class="response-popover-content">
<span v-if="props.detail.integrated">
{{ props.detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</span
>
<a-divider v-if="props.detail.integrated" direction="vertical" :margin="4" class="!mx-2"></a-divider>
<template #content>
<div class="items-center gap-[8px] text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.runMode') }}</div>
<div class="mt-1">
{{ props.detail.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel') }}</div
>
</div>
</template>
</a-popover>
<a-popover position="bottom" content-class="response-popover-content">
<div class="one-line-text max-w-[150px]"> {{ props.detail.creatUserName || '-' }}</div>
<template #content>

View File

@ -3,156 +3,27 @@
<!-- 报告参数开始 -->
<ReportDetailHeader :detail="detail" show-type="API" />
<!-- 报告参数结束 -->
<!-- 报告步骤分析和请求分析开始 -->
<!-- 报告分析报告步骤分析和请求分析开始 -->
<div class="analyze mb-1">
<div class="step-analyze min-w-[522px]">
<div class="analyze-item">
<div class="block-title">{{ t('report.detail.api.stepAnalysis') }}</div>
<div class="mb-2 flex items-center">
<!-- 总数 -->
<div class="countItem">
<span class="mr-2 text-[var(--color-text-4)]"> {{ t('report.detail.stepTotal') }}</span>
{{ detail.stepTotal || 0 }}
</div>
<!-- 通过 -->
<div class="countItem">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--success-6))]"></div>
<div class="mr-2 text-[var(--color-text-4)]">{{ t('common.pass') }}</div>
{{ detail.stepSuccessCount || 0 }}
</div>
<!-- 误报 -->
<div class="countItem">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--warning-6))]"></div>
<div class="mr-2 text-[var(--color-text-4)]">{{ t('common.fakeError') }}</div>
{{ detail.stepFakeErrorCount || 0 }}
</div>
<!-- 失败 -->
<div class="countItem">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--danger-6))]"></div>
<div class="mr-2 text-[var(--color-text-4)]">{{ t('common.fail') }}</div>
{{ detail.stepErrorCount || 0 }}
</div>
<!-- 未执行 -->
<div class="countItem">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[var(--color-text-input-border)]"></div>
<div class="mr-2 text-[var(--color-text-4)]">{{ t('common.unExecute') }}</div>
{{ detail.stepPendingCount || 0 }}
</div>
</div>
<StepProgress :report-detail="detail" height="8px" radius="var(--border-radius-mini)" />
<div class="card">
<div class="timer-card mr-2">
<div class="text-[var(--color-text-4)]">
<MsIcon type="icon-icon_time_outlined" class="text-[var(--color-text-4)]x mr-[4px]" size="16" />
{{ t('report.detail.api.totalTime') }}
</div>
<div>
<a-popover position="bottom" content-class="response-popover-content">
<div class="flex items-center">
<div class="one-line-text ml-4 max-w-[80px] text-[18px] font-medium">{{
getTotalTime.split('-')[0]
}}</div>
<div class="ml-1 text-[var(--color-text-4)]">{{ getTotalTime.split('-')[1] || 'ms' }}</div>
</div>
<template #content>
<div class="min-w-[140px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.totalTime') }}</div>
<div class="mt-2 text-[var(--color-text-1)]">
<span class="text-[18px] font-medium">{{ getTotalTime.split('-')[0] }}</span
>{{ getTotalTime.split('-')[1] || 'ms' }}</div
>
</div>
</template>
</a-popover>
</div>
</div>
<div class="timer-card mr-2">
<div class="text-[var(--color-text-4)]">
<MsIcon type="icon-icon_time_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
{{ t('report.detail.api.requestTotalTime') }}
</div>
<div>
<a-popover position="bottom" content-class="response-popover-content">
<span class="one-line-text ml-4 inline-block max-w-[80px] align-middle text-[18px] font-medium">{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '0'
}}</span>
<span class="ml-1 text-[var(--color-text-4)]">{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms'
}}</span>
<template #content>
<div class="min-w-[140px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.requestTotalTime') }}</div>
<div class="mt-2 text-[var(--color-text-1)]">
<span class="text-[18px] font-medium">{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[0] : '0'
}}</span
>{{
detail.requestDuration !== null ? formatDuration(detail.requestDuration).split('-')[1] : 'ms'
}}</div
>
</div>
</template>
</a-popover>
</div>
</div>
<div class="timer-card min-w-[40%]">
<div class="text-[var(--color-text-4)]">
<MsIcon type="icon-icon_yes_outlined" class="mr-[4px] text-[var(--color-text-4)]" size="16" />
{{ t('report.detail.api.assertPass') }}
</div>
<a-popover position="bottom" content-class="response-popover-content">
<div class="flex flex-nowrap items-center">
<div class="one-line-text max-w-[80px] text-[18px] font-medium text-[var(--color-text-1)]"
>{{ detail.assertionPassRate || 0 }}
</div>
<span v-show="detail.assertionPassRate !== 'Calculating'" class="ml-1">%</span>
<a-divider direction="vertical" :margin="0" class="!mx-2 h-[16px]"></a-divider>
<div class="one-line-text max-w-[80px] text-[var(--color-text-1)]">{{
getIndicators(detail.assertionSuccessCount) !== 'Calculating'
? addCommasToNumber(detail.assertionSuccessCount)
: getIndicators(detail.assertionSuccessCount)
}}</div>
<span class="mx-1">/</span>
<div class="one-line-text max-w-[80px] text-[var(--color-text-4)]">
{{
getIndicators(detail.assertionCount) !== 'Calculating'
? addCommasToNumber(detail.assertionCount)
: getIndicators(detail.assertionCount)
}}</div
>
</div>
<template #content>
<div class="min-w-[190px] max-w-[400px] p-4 text-[14px]">
<div class="text-[var(--color-text-4)]">{{ t('report.detail.api.assertPass') }}</div>
<div class="mt-2 flex items-center justify-between">
<div class="text-[18px] font-medium text-[var(--color-text-1)]"
>{{ getIndicators(detail.assertionPassRate) }}
<span v-show="detail.assertionPassRate !== 'Calculating'">%</span></div
>
<div>
<span class="text-[var(--color-text-1)]">{{
getIndicators(detail.assertionSuccessCount) !== 'Calculating'
? addCommasToNumber(detail.assertionSuccessCount || 0)
: getIndicators(detail.assertionSuccessCount)
}}</span>
<span class="text-[var(--color-text-4)]"
><span class="mx-1">/</span>
{{
getIndicators(detail.assertionCount) !== 'Calculating'
? addCommasToNumber(detail.assertionCount)
: getIndicators(detail.assertionCount)
}}</span
>
</div>
</div>
</div>
</template>
</a-popover>
</div>
</div>
<ReportMetricsItem
v-for="analysisItem in reportAnalysisList"
:key="analysisItem.name"
:item-info="analysisItem"
/>
</div>
<div class="request-analyze">
<!-- 步骤分析 -->
<div class="analyze-item request-analyze">
<div class="block-title">{{ t('report.detail.api.stepAnalysis') }}</div>
<SetReportChart
:legend-data="stepAnalysisLegendData"
:options="charOptions"
:request-total="getIndicators(detail.requestTotal) || 0"
/>
</div>
<!-- 请求分析 -->
<div class="analyze-item request-analyze">
<div class="block-title">{{ t('report.detail.api.requestAnalysis') }}</div>
<SetReportChart
:legend-data="legendData"
@ -161,7 +32,7 @@
/>
</div>
</div>
<!-- 报告步骤分析和请求分析结束 -->
<!-- 报告分析报告步骤分析和请求分析结束 -->
<!-- 报告明细开始 -->
<div class="report-info">
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" show-type="API" />
@ -173,23 +44,21 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import SetReportChart from './case/setReportChart.vue';
import ReportDetailHeader from './reportDetailHeader.vue';
import reportInfoHeader from './step/reportInfoHeaders.vue';
import StepProgress from './stepProgress.vue';
import TiledList from './tiledList.vue';
import ReportMetricsItem from '@/views/test-plan/report/detail/component/ReportMetricsItem.vue';
import { useI18n } from '@/hooks/useI18n';
import { addCommasToNumber, formatDuration } from '@/utils';
import { formatDuration } from '@/utils';
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
import type { ReportMetricsItemModel } from '@/models/testPlan/testPlanReport';
import { getIndicators } from '../utils';
const route = useRoute();
const { t } = useI18n();
const props = defineProps<{
detailInfo?: ReportDetail;
@ -252,7 +121,39 @@
return '0';
});
const getRunMode = computed(() => {
if (detail.value.integrated) {
return detail.value.runMode === 'SERIAL' ? t('case.execute.serial') : t('case.execute.parallel');
}
return '';
});
const reportAnalysisList = computed<ReportMetricsItemModel[]>(() => [
{
name: t('report.detail.api.totalTime'),
value: getTotalTime.value.split('-')[0],
unit: getTotalTime.value.split('-')[1] || 'ms',
icon: 'totalTime',
tip: t('report.detail.api.totalTimeTip'),
runMode: getRunMode.value,
},
{
name: t('report.detail.api.requestTotalTime'),
value: detail.value.requestDuration !== null ? formatDuration(detail.value.requestDuration).split('-')[0] : '0',
unit: detail.value.requestDuration !== null ? formatDuration(detail.value.requestDuration).split('-')[1] : 'ms',
icon: 'totalTime',
tip: t('report.detail.api.requestTotalTimeTip'),
},
{
name: t('report.detail.api.assertPass'),
value: getIndicators(detail.value.assertionPassRate),
unit: '%',
icon: 'passRate',
},
]);
const legendData = ref<LegendData[]>([]);
const stepAnalysisLegendData = ref<LegendData[]>([]);
const charOptions = ref({
tooltip: {
show: false,
@ -314,6 +215,9 @@
});
const activeTab = ref<'tiled' | 'tab'>('tiled');
function getRote(count: number, countTotal: number) {
return (((count || 0) / countTotal) * 100).toFixed(2);
}
function initOptionsData() {
const tempArr = [
{
@ -365,6 +269,15 @@
rote: `${detail.value[item.rateKey] || 0}%`,
};
});
stepAnalysisLegendData.value = tempArr.map((item: any) => {
const valueName = `step${item.value.charAt(0).toUpperCase() + item.value.slice(1)}`;
return {
...item,
label: t(item.label),
count: detail.value[valueName] || 0,
rote: `${getRote(detail.value[valueName], detail.value.stepTotal)}%`,
};
});
}
watchEffect(() => {
@ -390,30 +303,14 @@
height: 196px;
border-radius: 4px;
@apply mb-4 flex justify-between;
.step-analyze {
.analyze-item {
padding: 16px;
width: 60%;
height: 196px;
width: 33%;
border-radius: 4px;
@apply h-full bg-white;
.countItem {
@apply mr-6 flex items-center;
}
.card {
@apply mt-4 flex items-center justify-between;
.timer-card {
border-radius: 6px;
background-color: var(--color-text-n9);
@apply flex flex-1 flex-col p-4;
}
}
}
.request-analyze {
padding: 16px;
width: 40%;
height: 100%;
border-radius: 4px;
@apply ml-4 h-full flex-grow bg-white;
@apply ml-4 flex-grow;
.chart-legend {
.chart-legend-item {
@apply grid grid-cols-3;

View File

@ -1,120 +0,0 @@
<template>
<MsColorLine :color-data="colorData" :height="props.height" :radius="props.radius">
<template #popoverContent>
<table class="min-w-[144px]">
<tr>
<td class="popover-label-td">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--success-6))]"></div>
<div>{{ t('common.pass') }}</div>
</td>
<td class="popover-value-td">
{{ props.reportDetail.stepSuccessCount }}
</td>
</tr>
<tr>
<td class="popover-label-td">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--warning-6))]"></div>
<div>{{ t('common.fakeError') }}</div>
</td>
<td class="popover-value-td">
{{ props.reportDetail.stepFakeErrorCount }}
</td>
</tr>
<tr>
<td class="popover-label-td">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--danger-6))]"></div>
<div>{{ t('common.fail') }}</div>
</td>
<td class="popover-value-td">
{{ props.reportDetail.stepErrorCount }}
</td>
</tr>
<tr>
<td class="popover-label-td">
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[var(--color-text-input-border)]"></div>
<div>{{ t('common.unExecute') }}</div>
</td>
<td class="popover-value-td">
{{ props.reportDetail.stepPendingCount }}
</td>
</tr>
</table>
</template>
</MsColorLine>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import MsColorLine from '@/components/pure/ms-color-line/index.vue';
import { useI18n } from '@/hooks/useI18n';
const props = defineProps<{
reportDetail: {
stepTotal: number;
errorCount: number;
fakeErrorCount: number;
pendingCount: number;
successCount: number;
[key: string]: any;
};
height: string;
radius?: string;
}>();
const { t } = useI18n();
const getCountTotal = computed(() => {
const { stepSuccessCount, stepFakeErrorCount, stepErrorCount, stepPendingCount } = props.reportDetail;
return stepSuccessCount + stepFakeErrorCount + stepErrorCount + stepPendingCount;
});
const colorData = computed(() => {
if (
props.reportDetail.stepSuccessCount === 0 &&
props.reportDetail.stepErrorCount === 0 &&
props.reportDetail.stepFakeErrorCount === 0 &&
props.reportDetail.stepPendingCount === 0
) {
return [
{
percentage: 100,
color: 'var(--color-text-n8)',
},
];
}
return [
{
percentage: (props.reportDetail.stepSuccessCount / getCountTotal.value) * 100,
color: 'rgb(var(--success-6))',
},
{
percentage: (props.reportDetail.stepErrorCount / getCountTotal.value) * 100,
color: 'rgb(var(--danger-6))',
},
{
percentage: (props.reportDetail.stepFakeErrorCount / getCountTotal.value) * 100,
color: 'rgb(var(--warning-6))',
},
{
percentage: (props.reportDetail.stepPendingCount / getCountTotal.value) * 100,
color: 'var(--color-text-input-border)',
},
];
});
</script>
<style lang="less" scoped>
.popover-label-td {
@apply flex items-center;
padding: 8px 8px 0 0;
color: var(--color-text-4);
}
.popover-value-td {
@apply font-medium;
padding-top: 8px;
color: var(--color-text-1);
}
</style>

View File

@ -28,8 +28,11 @@ export default {
'report.detail.api.stepAnalysis': 'step Analysis',
'report.detail.api.content': 'content',
'report.detail.api.assertStatus': 'status',
'report.detail.api.totalTime': 'total time',
'report.detail.api.totalTime': 'Total report time',
'report.detail.api.requestTotalTime': 'req total time',
'report.detail.api.totalTimeTip':
'The total time spent executing testing tasks, including waiting and processing times during the task execution process',
'report.detail.api.requestTotalTimeTip': 'The total response time of all requests',
'report.detail.api.assertPass': 'Assert pass',
'report.detail.api.executionRate': 'Req execution Rate',
'report.detail.api.requestAnalysis': 'Request analysis',

View File

@ -27,8 +27,10 @@ export default {
'report.detail.api.stepAnalysis': '步骤分析',
'report.detail.api.content': '内容',
'report.detail.api.assertStatus': '状态',
'report.detail.api.totalTime': '总耗时',
'report.detail.api.totalTime': '报告总耗时',
'report.detail.api.totalTimeTip': '执行测试任务的总耗时,包含任务执行过程的等待时间和处理时间',
'report.detail.api.requestTotalTime': '请求总耗时',
'report.detail.api.requestTotalTimeTip': '全部请求的响应时间总和',
'report.detail.api.assertPass': '断言通过率',
'report.detail.api.executionRate': '请求执行率',
'report.detail.api.requestAnalysis': '请求分析',

View File

@ -0,0 +1,63 @@
<template>
<div class="report-analysis-item">
<div class="report-analysis-item-icon">
<svg-icon class="mr-2" width="24px" height="24px" :name="props.itemInfo.icon" />
<span>{{ props.itemInfo.name }}</span>
<a-tooltip :content="props.itemInfo.tip" position="top">
<MsIcon
v-if="props.itemInfo.tip?.length"
type="icon-icon-maybe_outlined"
class="ml-[8px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
/>
</a-tooltip>
<MsTag
v-if="props.itemInfo.runMode?.length"
class="ml-[8px]"
size="small"
theme="outline"
:self-style="{
border: `1px solid rgb(var(--link-6))`,
color: `rgb(var(--link-6))`,
padding: '0 4px',
}"
>
{{ props.itemInfo.runMode }}
</MsTag>
</div>
<div>
<span class="report-analysis-item-number">{{ props.itemInfo.value }}</span>
<span class="report-analysis-item-unit">({{ props.itemInfo.unit }})</span>
</div>
</div>
</template>
<script setup lang="ts">
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
import type { ReportMetricsItemModel } from '@/models/testPlan/testPlanReport';
const props = defineProps<{
itemInfo: ReportMetricsItemModel;
}>();
</script>
<style scoped lang="less">
.report-analysis-item {
padding: 4px 8px;
border-radius: 4px;
background-color: var(--color-text-n9);
@apply mb-3 flex items-center justify-between;
.report-analysis-item-icon {
@apply flex items-center;
}
.report-analysis-item-number {
font-size: 16px;
@apply font-medium;
}
.report-analysis-item-unit {
font-size: 12px;
color: var(--color-text-4);
@apply ml-1 font-medium;
}
}
</style>

View File

@ -14,48 +14,11 @@
<div class="analysis-wrapper">
<div class="analysis min-w-[238px]">
<div class="block-title">{{ t('report.detail.api.requestAnalysis') }}</div>
<ul class="report-analysis">
<li class="report-analysis-item">
<div class="report-analysis-item-icon">
<svg-icon class="mr-2" width="24px" height="24px" name="threshold" />
<span>{{ t('report.detail.threshold') }}</span>
</div>
<div>
<span class="report-analysis-item-number">{{ detail.passThreshold }}</span>
<span class="report-analysis-item-unit">(%)</span>
</div>
</li>
<li class="report-analysis-item">
<div class="report-analysis-item-icon">
<svg-icon class="mr-2" width="24px" height="24px" name="passRate" />
<span>{{ t('report.detail.reportPassRate') }}</span>
</div>
<div>
<span class="report-analysis-item-number">{{ detail.passRate }}</span>
<span class="report-analysis-item-unit">(%)</span>
</div>
</li>
<li class="report-analysis-item">
<div class="report-analysis-item-icon">
<svg-icon class="mr-2" width="24px" height="24px" name="passRate" />
<span>{{ t('report.detail.performCompletion') }}</span>
</div>
<div>
<span class="report-analysis-item-number">{{ detail.executeRate }}</span>
<span class="report-analysis-item-unit">(%)</span>
</div>
</li>
<li class="report-analysis-item">
<div class="report-analysis-item-icon">
<svg-icon class="mr-2" width="24px" height="24px" name="bugTotal" />
<span>{{ t('report.detail.totalDefects') }}</span>
</div>
<div>
<span class="report-analysis-item-number">{{ addCommasToNumber(detail.bugCount) }}</span>
<span class="report-analysis-item-unit">({{ t('report.detail.number') }})</span>
</div>
</li>
</ul>
<ReportMetricsItem
v-for="analysisItem in reportAnalysisList"
:key="analysisItem.name"
:item-info="analysisItem"
/>
</div>
<div class="analysis min-w-[410px]">
<div class="block-title">{{ t('report.detail.executionAnalysis') }}</div>
@ -144,6 +107,7 @@
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
import MsTab from '@/components/pure/ms-tab/index.vue';
import PlanDetailHeaderRight from './planDetailHeaderRight.vue';
import ReportMetricsItem from './ReportMetricsItem.vue';
import SetReportChart from '@/views/api-test/report/component/case/setReportChart.vue';
import SingleStatusProgress from '@/views/test-plan/report/component/singleStatusProgress.vue';
import BugTable from '@/views/test-plan/report/detail/component/bugTable.vue';
@ -157,7 +121,7 @@
import { hasAnyPermission } from '@/utils/permission';
import type { LegendData } from '@/models/apiTest/report';
import type { PlanReportDetail, StatusListType } from '@/models/testPlan/testPlanReport';
import type { PlanReportDetail, ReportMetricsItemModel, StatusListType } from '@/models/testPlan/testPlanReport';
import { getIndicators } from '@/views/api-test/report/utils';
@ -361,6 +325,33 @@
showButton.value = false;
}
const reportAnalysisList = computed<ReportMetricsItemModel[]>(() => [
{
name: t('report.detail.threshold'),
value: detail.value.passThreshold,
unit: '%',
icon: 'threshold',
},
{
name: t('report.detail.reportPassRate'),
value: detail.value.passRate,
unit: '%',
icon: 'passRate',
},
{
name: t('report.detail.performCompletion'),
value: detail.value.executeRate,
unit: '%',
icon: 'passRate',
},
{
name: t('report.detail.totalDefects'),
value: addCommasToNumber(detail.value.bugCount),
unit: t('report.detail.number'),
icon: 'bugTotal',
},
]);
const functionCasePassRate = computed(() => {
const { functionalCount } = detail.value;
const { success, error, pending, block } = functionalCount;
@ -409,26 +400,6 @@
height: 250px;
box-shadow: 0 0 10px rgba(120 56 135/ 5%);
@apply flex-1 rounded-xl bg-white;
.report-analysis {
.report-analysis-item {
padding: 4px 8px;
border-radius: 4px;
background-color: var(--color-text-n9);
@apply mb-3 flex items-center justify-between;
.report-analysis-item-icon {
@apply flex items-center;
}
.report-analysis-item-number {
font-size: 16px;
@apply font-medium;
}
.report-analysis-item-unit {
font-size: 12px;
color: var(--color-text-4);
@apply ml-1 font-medium;
}
}
}
.charts {
top: 36%;
right: 0;