fix(接口测试): 报告bug修复和补充部分细节
This commit is contained in:
parent
44ab9a2cd8
commit
1c64577431
|
@ -3,7 +3,7 @@
|
||||||
<div class="relative mr-4">
|
<div class="relative mr-4">
|
||||||
<div class="absolute bottom-0 left-[30%] top-[35%] text-center">
|
<div class="absolute bottom-0 left-[30%] top-[35%] text-center">
|
||||||
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
||||||
<div class="text-[18px] font-medium">4</div>
|
<div class="text-[18px] font-medium">{{ totalCount }}</div>
|
||||||
</div>
|
</div>
|
||||||
<MsChart width="110px" height="110px" :options="props.options" />
|
<MsChart width="110px" height="110px" :options="props.options" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,6 +38,12 @@
|
||||||
options: Record<string, any>;
|
options: Record<string, any>;
|
||||||
legendData: LegendData[];
|
legendData: LegendData[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const totalCount = computed(() => {
|
||||||
|
return props.legendData.reduce((prev, item) => {
|
||||||
|
return prev + item.count;
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
@ -56,8 +56,8 @@
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="count"> {{ getExcuteRate() }} %</span>
|
<span class="count"> {{ getExcuteRate() }} %</span>
|
||||||
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
|
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
|
||||||
<span>{{ getRequestEacuteCount }}</span>
|
<span>{{ getIndicators(getRequestEacuteCount) }}</span>
|
||||||
<span class="mx-1 text-[var(--color-text-4)]">/ {{ getRequestTotalCount || 0 }}</span>
|
<span class="mx-1 text-[var(--color-text-4)]">/ {{ getIndicators(getRequestTotalCount) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="time-card-item-rote">
|
<div class="time-card-item-rote">
|
||||||
|
@ -70,8 +70,17 @@
|
||||||
>{{ detail.assertionPassRate === 'Calculating' ? '-' : detail.assertionPassRate || '0.00' }}%</span
|
>{{ detail.assertionPassRate === 'Calculating' ? '-' : detail.assertionPassRate || '0.00' }}%</span
|
||||||
>
|
>
|
||||||
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
|
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
|
||||||
<span>{{ addCommasToNumber(detail.assertionSuccessCount || 0) }}</span>
|
<span>{{
|
||||||
<span class="mx-1 text-[var(--color-text-4)]">/ {{ detail.assertionCount || 0 }}</span>
|
getIndicators(detail.assertionSuccessCount) === '-'
|
||||||
|
? '-'
|
||||||
|
: addCommasToNumber(detail.assertionSuccessCount || 0)
|
||||||
|
}}</span>
|
||||||
|
<span class="mx-1 text-[var(--color-text-4)]"
|
||||||
|
>/
|
||||||
|
{{
|
||||||
|
getIndicators(detail.assertionCount) === '-' ? '-' : addCommasToNumber(detail.assertionCount) || 0
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,20 +89,13 @@
|
||||||
<!-- 报告步骤分析结束 -->
|
<!-- 报告步骤分析结束 -->
|
||||||
<!-- 报告明细开始 -->
|
<!-- 报告明细开始 -->
|
||||||
<div class="report-info">
|
<div class="report-info">
|
||||||
<div class="mb-4 flex h-[36px] items-center justify-between">
|
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" />
|
||||||
<div class="flex items-center">
|
<TiledList
|
||||||
<div class="mr-2 font-medium leading-[36px]">{{ t('report.detail.api.reportDetail') }}</div>
|
:key-words="cascaderKeywords"
|
||||||
<a-radio-group v-model:model-value="activeTab" type="button" size="small">
|
show-type="CASE"
|
||||||
<a-radio v-for="item of methods" :key="item.value" :value="item.value">
|
:active-type="activeTab"
|
||||||
{{ t(item.label) }}
|
:report-detail="detail || []"
|
||||||
</a-radio>
|
/>
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
<a-select v-model="condition" class="w-[240px]" :placeholder="t('report.detail.api.filterPlaceholder')">
|
|
||||||
<a-option :key="1" :value="1"> 1 </a-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
|
||||||
<TiledList show-type="CASE" :active-type="activeTab" :report-detail="detail || []" />
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 报告明细结束 -->
|
<!-- 报告明细结束 -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -104,6 +106,7 @@
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import SetReportChart from './case/setReportChart.vue';
|
import SetReportChart from './case/setReportChart.vue';
|
||||||
|
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
||||||
import TiledList from './tiledList.vue';
|
import TiledList from './tiledList.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
@ -111,6 +114,8 @@
|
||||||
|
|
||||||
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
|
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
|
||||||
|
|
||||||
|
import { getIndicators } from '../utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
detailInfo?: ReportDetail;
|
detailInfo?: ReportDetail;
|
||||||
|
@ -156,6 +161,8 @@
|
||||||
console: '',
|
console: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const cascaderKeywords = ref<string>('');
|
||||||
|
|
||||||
const getTotalTime = computed(() => {
|
const getTotalTime = computed(() => {
|
||||||
if (detail.value) {
|
if (detail.value) {
|
||||||
const { endTime, startTime } = detail.value;
|
const { endTime, startTime } = detail.value;
|
||||||
|
@ -166,16 +173,6 @@
|
||||||
}
|
}
|
||||||
return '-';
|
return '-';
|
||||||
});
|
});
|
||||||
const methods = ref([
|
|
||||||
{
|
|
||||||
label: t('report.detail.api.tiledDisplay'),
|
|
||||||
value: 'tiled',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('report.detail.api.tabDisplay'),
|
|
||||||
value: 'tab',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const legendData = ref<LegendData[]>([]);
|
const legendData = ref<LegendData[]>([]);
|
||||||
const charOptions = ref({
|
const charOptions = ref({
|
||||||
|
@ -237,7 +234,6 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const activeTab = ref<'tiled' | 'tab'>('tiled');
|
const activeTab = ref<'tiled' | 'tab'>('tiled');
|
||||||
const condition = ref('');
|
|
||||||
|
|
||||||
function getExcuteRate() {
|
function getExcuteRate() {
|
||||||
return 100 - Number(detail.value.requestPendingRate)
|
return 100 - Number(detail.value.requestPendingRate)
|
||||||
|
@ -301,7 +297,7 @@
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
label: t(item.label),
|
label: t(item.label),
|
||||||
count: detail.value[item.value] || 0,
|
count: detail.value[item.value] === 'Calculating' ? '-' : detail.value[item.value] || 0,
|
||||||
rote: detail.value[item.rateKey] === 'Calculating' ? '-' : detail.value[item.rateKey],
|
rote: detail.value[item.rateKey] === 'Calculating' ? '-' : detail.value[item.rateKey],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,31 +28,31 @@
|
||||||
<!-- 总数 -->
|
<!-- 总数 -->
|
||||||
<div class="countItem">
|
<div class="countItem">
|
||||||
<span class="mr-2 text-[var(--color-text-4)]"> {{ t('report.detail.stepTotal') }}</span>
|
<span class="mr-2 text-[var(--color-text-4)]"> {{ t('report.detail.stepTotal') }}</span>
|
||||||
{{ detail.stepTotal || 0 }}
|
{{ getTotal() || 0 }}
|
||||||
</div>
|
</div>
|
||||||
<!-- 通过 -->
|
<!-- 通过 -->
|
||||||
<div class="countItem">
|
<div class="countItem">
|
||||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--success-6))]"></div>
|
<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('report.detail.successCount') }}</div>
|
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.successCount') }}</div>
|
||||||
{{ detail.successCount || 0 }}
|
{{ getIndicators(detail.successCount) }}
|
||||||
</div>
|
</div>
|
||||||
<!-- 误报 -->
|
<!-- 误报 -->
|
||||||
<div class="countItem">
|
<div class="countItem">
|
||||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--warning-6))]"></div>
|
<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('report.detail.fakeErrorCount') }}</div>
|
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.fakeErrorCount') }}</div>
|
||||||
{{ detail.fakeErrorCount || 0 }}
|
{{ getIndicators(detail.fakeErrorCount) }}
|
||||||
</div>
|
</div>
|
||||||
<!-- 失败 -->
|
<!-- 失败 -->
|
||||||
<div class="countItem">
|
<div class="countItem">
|
||||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[rgb(var(--danger-6))]"></div>
|
<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('report.detail.errorCount') }}</div>
|
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.errorCount') }}</div>
|
||||||
{{ detail.errorCount || 0 }}
|
{{ getIndicators(detail.errorCount) }}
|
||||||
</div>
|
</div>
|
||||||
<!-- 未执行 -->
|
<!-- 未执行 -->
|
||||||
<div class="countItem">
|
<div class="countItem">
|
||||||
<div class="mb-[2px] mr-[4px] h-[6px] w-[6px] rounded-full bg-[var(--color-text-input-border)]"></div>
|
<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('report.detail.pendingCount') }}</div>
|
<div class="mr-2 text-[var(--color-text-4)]">{{ t('report.detail.pendingCount') }}</div>
|
||||||
{{ detail.pendingCount || 0 }}
|
{{ getIndicators(detail.pendingCount) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<StepProgress :report-detail="detail" height="8px" radius="var(--border-radius-mini)" />
|
<StepProgress :report-detail="detail" height="8px" radius="var(--border-radius-mini)" />
|
||||||
|
@ -88,12 +88,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-[18px] font-medium text-[var(--color-text-1)]"
|
<span class="text-[18px] font-medium text-[var(--color-text-1)]"
|
||||||
>{{ detail.assertionPassRate || 0 }} <span>%</span></span
|
>{{ getIndicators(detail.assertionPassRate) }} <span>%</span></span
|
||||||
>
|
>
|
||||||
<a-divider direction="vertical" :margin="0" class="!mx-2 h-[16px]"></a-divider>
|
<a-divider direction="vertical" :margin="0" class="!mx-2 h-[16px]"></a-divider>
|
||||||
<span class="text-[var(--color-text-1)]">{{ addCommasToNumber(detail.assertionSuccessCount || 0) }}</span>
|
<span class="text-[var(--color-text-1)]">{{
|
||||||
|
getIndicators(detail.assertionSuccessCount) === '-'
|
||||||
|
? '-'
|
||||||
|
: addCommasToNumber(detail.assertionSuccessCount || 0)
|
||||||
|
}}</span>
|
||||||
<span class="text-[var(--color-text-4)]"
|
<span class="text-[var(--color-text-4)]"
|
||||||
><span class="mx-1">/</span> {{ addCommasToNumber(detail.assertionCount) || 0 }}</span
|
><span class="mx-1">/</span>
|
||||||
|
{{
|
||||||
|
getIndicators(detail.assertionCount) === '-' ? '-' : addCommasToNumber(detail.assertionCount) || 0
|
||||||
|
}}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -106,7 +113,7 @@
|
||||||
<div class="relative mr-4">
|
<div class="relative mr-4">
|
||||||
<div class="absolute bottom-0 left-[30%] top-[35%] text-center">
|
<div class="absolute bottom-0 left-[30%] top-[35%] text-center">
|
||||||
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
<div class="text-[12px] text-[(var(--color-text-4))]">{{ t('report.detail.api.total') }}</div>
|
||||||
<div class="text-[18px] font-medium">4</div>
|
<div class="text-[18px] font-medium">{{ getTotal() }}</div>
|
||||||
</div>
|
</div>
|
||||||
<MsChart width="110px" height="110px" :options="charOptions" />
|
<MsChart width="110px" height="110px" :options="charOptions" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -127,20 +134,8 @@
|
||||||
<!-- 报告步骤分析和请求分析结束 -->
|
<!-- 报告步骤分析和请求分析结束 -->
|
||||||
<!-- 报告明细开始 -->
|
<!-- 报告明细开始 -->
|
||||||
<div class="report-info">
|
<div class="report-info">
|
||||||
<div class="mb-4 flex h-[36px] items-center justify-between">
|
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" />
|
||||||
<div class="flex items-center">
|
<TiledList :key-words="cascaderKeywords" show-type="API" :active-type="activeTab" :report-detail="detail || []" />
|
||||||
<div class="mr-2 font-medium leading-[36px]">{{ t('report.detail.api.reportDetail') }}</div>
|
|
||||||
<a-radio-group v-model:model-value="activeTab" type="button" size="small">
|
|
||||||
<a-radio v-for="item of methods" :key="item.value" :value="item.value">
|
|
||||||
{{ t(item.label) }}
|
|
||||||
</a-radio>
|
|
||||||
</a-radio-group>
|
|
||||||
</div>
|
|
||||||
<a-select v-model="condition" class="w-[240px]" :placeholder="t('report.detail.api.filterPlaceholder')">
|
|
||||||
<a-option :key="1" :value="1"> 1 </a-option>
|
|
||||||
</a-select>
|
|
||||||
</div>
|
|
||||||
<TiledList show-type="API" :active-type="activeTab" :report-detail="detail || []" />
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 报告明细结束 -->
|
<!-- 报告明细结束 -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -151,6 +146,7 @@
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import MsChart from '@/components/pure/chart/index.vue';
|
import MsChart from '@/components/pure/chart/index.vue';
|
||||||
|
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
||||||
import StepProgress from './stepProgress.vue';
|
import StepProgress from './stepProgress.vue';
|
||||||
import TiledList from './tiledList.vue';
|
import TiledList from './tiledList.vue';
|
||||||
|
|
||||||
|
@ -159,6 +155,8 @@
|
||||||
|
|
||||||
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
|
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
|
||||||
|
|
||||||
|
import { getIndicators } from '../utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
detailInfo?: ReportDetail;
|
detailInfo?: ReportDetail;
|
||||||
|
@ -204,7 +202,7 @@
|
||||||
console: '',
|
console: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeUnits = ['ms', 'sec', 'min', 'hr'];
|
const cascaderKeywords = ref<string>('');
|
||||||
|
|
||||||
const getTotalTime = computed(() => {
|
const getTotalTime = computed(() => {
|
||||||
if (detail.value) {
|
if (detail.value) {
|
||||||
|
@ -216,16 +214,10 @@
|
||||||
}
|
}
|
||||||
return '-';
|
return '-';
|
||||||
});
|
});
|
||||||
const methods = ref([
|
function getTotal() {
|
||||||
{
|
const { errorCount, successCount, fakeErrorCount, pendingCount } = detail.value;
|
||||||
label: t('report.detail.api.tiledDisplay'),
|
return errorCount + successCount + fakeErrorCount + pendingCount;
|
||||||
value: 'tiled',
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('report.detail.api.tabDisplay'),
|
|
||||||
value: 'tab',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const legendData = ref<LegendData[]>([]);
|
const legendData = ref<LegendData[]>([]);
|
||||||
const charOptions = ref({
|
const charOptions = ref({
|
||||||
|
@ -287,7 +279,6 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const activeTab = ref<'tiled' | 'tab'>('tiled');
|
const activeTab = ref<'tiled' | 'tab'>('tiled');
|
||||||
const condition = ref('');
|
|
||||||
|
|
||||||
function initOptionsData() {
|
function initOptionsData() {
|
||||||
const tempArr = [
|
const tempArr = [
|
||||||
|
@ -334,7 +325,7 @@
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
label: t(item.label),
|
label: t(item.label),
|
||||||
count: detail.value[item.value] || 0,
|
count: detail.value[item.value] === 'Calculating' ? '-' : detail.value[item.value] || 0,
|
||||||
rote: detail.value[item.rateKey] === 'Calculating' ? '-' : detail.value[item.rateKey],
|
rote: detail.value[item.rateKey] === 'Calculating' ? '-' : detail.value[item.rateKey],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<div class="mb-4 flex h-[36px] items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="mr-2 font-medium leading-[36px]">{{ t('report.detail.api.reportDetail') }}</div>
|
||||||
|
<a-radio-group v-model:model-value="innerActiveTab" type="button" size="small">
|
||||||
|
<a-radio v-for="item of methods" :key="item.value" :value="item.value">
|
||||||
|
{{ t(item.label) }}
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
<MsCascader
|
||||||
|
v-model:model-value="innerKeyword"
|
||||||
|
mode="native"
|
||||||
|
option-size="small"
|
||||||
|
:multiple="false"
|
||||||
|
class="w-[240px]"
|
||||||
|
:options="cascaderOptions || []"
|
||||||
|
:placeholder="t('report.detail.api.filterPlaceholder')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useVModel } from '@vueuse/core';
|
||||||
|
|
||||||
|
import MsCascader from '@/components/business/ms-cascader/index.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import { ScenarioStepType } from '@/enums/apiEnum';
|
||||||
|
|
||||||
|
import type { CascaderOption } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps<{
|
||||||
|
activeTab: 'tiled' | 'tab';
|
||||||
|
keyword: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:activeTab', 'update:keyword']);
|
||||||
|
|
||||||
|
const innerActiveTab = useVModel(props, 'activeTab', emit);
|
||||||
|
const innerKeyword = useVModel(props, 'keyword', emit);
|
||||||
|
|
||||||
|
const methods = ref([
|
||||||
|
{
|
||||||
|
label: t('report.detail.api.tiledDisplay'),
|
||||||
|
value: 'tiled',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('report.detail.api.tabDisplay'),
|
||||||
|
value: 'tab',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const createChildOption = (key: string) => [
|
||||||
|
{
|
||||||
|
value: `${key}-SUCCESS`,
|
||||||
|
label: t(`report.detail.successCount`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${key}-FAKE_ERROR`,
|
||||||
|
label: t(`report.detail.fakeErrorCount`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${key}-ERROR`,
|
||||||
|
label: t(`report.detail.errorCount`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${key}-PENDING`,
|
||||||
|
label: t(`report.detail.pendingCount`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${key}-scriptIdentifier`,
|
||||||
|
label: t(`report.detail.api.scriptError`),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const cascaderOptions = ref<CascaderOption[]>([
|
||||||
|
{
|
||||||
|
value: ScenarioStepType.API_SCENARIO,
|
||||||
|
label: t('report.detail.api.scenario'),
|
||||||
|
children: createChildOption(ScenarioStepType.API_SCENARIO),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'REQUEST',
|
||||||
|
label: t('report.detail.api.request'),
|
||||||
|
children: createChildOption(ScenarioStepType.CUSTOM_REQUEST),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<MsTag theme="light" :type="getStatus"> {{ t(statusMap[props.status || 'PENDING'].label) }}</MsTag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = defineProps<{
|
||||||
|
status: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const statusMap = {
|
||||||
|
PENDING: {
|
||||||
|
label: 'report.detail.pendingCount',
|
||||||
|
value: 'PENDING',
|
||||||
|
type: 'default',
|
||||||
|
},
|
||||||
|
FAKE_ERROR: {
|
||||||
|
label: 'report.detail.fakeErrorCount',
|
||||||
|
value: 'FAKE_ERROR',
|
||||||
|
type: 'warning',
|
||||||
|
},
|
||||||
|
ERROR: {
|
||||||
|
label: 'report.failure',
|
||||||
|
value: 'ERROR',
|
||||||
|
type: 'danger',
|
||||||
|
},
|
||||||
|
SUCCESS: {
|
||||||
|
label: 'report.detail.successCount',
|
||||||
|
value: 'SUCCESS',
|
||||||
|
type: 'success',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatus = computed(() => {
|
||||||
|
if (props.status) {
|
||||||
|
return statusMap[props.status].type;
|
||||||
|
}
|
||||||
|
return statusMap.PENDING.type;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -4,6 +4,7 @@
|
||||||
<MsTree
|
<MsTree
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
v-model:selected-keys="selectedKeys"
|
v-model:selected-keys="selectedKeys"
|
||||||
|
v-model:expanded-keys="innerExpandedKeys"
|
||||||
v-model:data="steps"
|
v-model:data="steps"
|
||||||
:expand-all="props.expandAll"
|
:expand-all="props.expandAll"
|
||||||
:field-names="{ title: 'name', key: 'stepId', children: 'children' }"
|
:field-names="{ title: 'name', key: 'stepId', children: 'children' }"
|
||||||
|
@ -75,9 +76,27 @@
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<MsTag class="cursor-pointer" :type="step.status === 'SUCCESS' ? 'success' : 'danger'" theme="light">
|
<stepStatus v-if="step.status" :status="step.status" />
|
||||||
{{ step.status === 'SUCCESS' ? t('report.detail.api.pass') : t('report.detail.api.resError') }}
|
<!-- 脚本报错 -->
|
||||||
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
|
<MsTag
|
||||||
|
v-if="step.scriptIdentifier"
|
||||||
|
type="primary"
|
||||||
|
theme="light"
|
||||||
|
:self-style="{
|
||||||
|
color: 'rgb(var(--primary-3))',
|
||||||
|
background: 'rgb(var(--primary-1))',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<MsIcon type="icon-icon_info_outlined" class="mx-1 !text-[rgb(var(--primary-3))]" size="16" />
|
||||||
|
<span class="!text-[rgb(var(--primary-3))]">{{ t('report.detail.api.scriptErrorTip') }}</span>
|
||||||
|
</template>
|
||||||
</MsTag>
|
</MsTag>
|
||||||
|
<template #content>
|
||||||
|
<div>{{ step.scriptIdentifier }}</div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
<span class="statusCode mx-2">
|
<span class="statusCode mx-2">
|
||||||
<div class="mr-2"> {{ t('report.detail.api.statusCode') }}</div>
|
<div class="mr-2"> {{ t('report.detail.api.statusCode') }}</div>
|
||||||
<a-popover position="left" content-class="response-popover-content">
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
|
@ -97,14 +116,22 @@
|
||||||
<span class="resTime">
|
<span class="resTime">
|
||||||
{{ t('report.detail.api.responseTime') }}
|
{{ t('report.detail.api.responseTime') }}
|
||||||
<span class="resTimeCount ml-2"
|
<span class="resTimeCount ml-2"
|
||||||
>{{ formatDuration(step.requestTime).split('-')[0]
|
>{{ step.requestTime ? formatDuration(step.requestTime).split('-')[0] : '-'
|
||||||
}}{{ formatDuration(step.requestTime).split('-')[1] }}</span
|
}}{{ step.requestTime ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}</span
|
||||||
></span
|
></span
|
||||||
>
|
>
|
||||||
|
<a-popover position="left" content-class="response-popover-content">
|
||||||
<span class="resSize">
|
<span class="resSize">
|
||||||
{{ t('report.detail.api.responseSize') }}
|
{{ t('report.detail.api.responseSize') }}
|
||||||
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
|
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
|
||||||
>
|
>
|
||||||
|
<template #content>
|
||||||
|
<span class="resSize">
|
||||||
|
{{ t('report.detail.api.responseSize') }}
|
||||||
|
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!step.fold" class="line"></div>
|
<div v-if="!step.fold" class="line"></div>
|
||||||
|
@ -142,6 +169,7 @@
|
||||||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||||
import { MsTreeExpandedData } from '@/components/business/ms-tree/types';
|
import { MsTreeExpandedData } from '@/components/business/ms-tree/types';
|
||||||
|
import stepStatus from './stepStatus.vue';
|
||||||
import StepDetailContent from '@/views/api-test/components/requestComposition/response/result/index.vue';
|
import StepDetailContent from '@/views/api-test/components/requestComposition/response/result/index.vue';
|
||||||
import ConditionStatus from '@/views/api-test/report/component/conditionStatus.vue';
|
import ConditionStatus from '@/views/api-test/report/component/conditionStatus.vue';
|
||||||
|
|
||||||
|
@ -160,6 +188,7 @@
|
||||||
console?: string;
|
console?: string;
|
||||||
environmentName?: string;
|
environmentName?: string;
|
||||||
reportId?: string;
|
reportId?: string;
|
||||||
|
expandedKeys: (string | number)[];
|
||||||
}>();
|
}>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
|
@ -170,6 +199,9 @@
|
||||||
const steps = defineModel<ScenarioItemType[]>('steps', {
|
const steps = defineModel<ScenarioItemType[]>('steps', {
|
||||||
required: true,
|
required: true,
|
||||||
});
|
});
|
||||||
|
const innerExpandedKeys = defineModel<(string | number)[]>('expandedKeys', {
|
||||||
|
required: false,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理步骤展开折叠
|
* 处理步骤展开折叠
|
||||||
|
|
|
@ -5,17 +5,11 @@
|
||||||
'border border-solid border-[var(--color-text-n8)]': props.showType === 'API',
|
'border border-solid border-[var(--color-text-n8)]': props.showType === 'API',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<!-- <a-scrollbar
|
|
||||||
:style="{
|
|
||||||
overflow: 'auto',
|
|
||||||
height: 'calc(100vh - 424px)',
|
|
||||||
width: '100%',
|
|
||||||
}"
|
|
||||||
> -->
|
|
||||||
<!-- 步骤树 -->
|
<!-- 步骤树 -->
|
||||||
<stepTree
|
<stepTree
|
||||||
ref="stepTreeRef"
|
ref="stepTreeRef"
|
||||||
v-model:steps="tiledList"
|
v-model:steps="tiledList"
|
||||||
|
v-model:expandedKeys="expandedKeys"
|
||||||
:show-type="props.showType"
|
:show-type="props.showType"
|
||||||
:active-type="props.activeType"
|
:active-type="props.activeType"
|
||||||
:expand-all="isExpandAll"
|
:expand-all="isExpandAll"
|
||||||
|
@ -24,7 +18,6 @@
|
||||||
:report-id="props.reportDetail.id"
|
:report-id="props.reportDetail.id"
|
||||||
@detail="showDetail"
|
@detail="showDetail"
|
||||||
/>
|
/>
|
||||||
<!-- </a-scrollbar> -->
|
|
||||||
<!-- 步骤抽屉 -->
|
<!-- 步骤抽屉 -->
|
||||||
<StepDrawer
|
<StepDrawer
|
||||||
v-model:visible="showStepDrawer"
|
v-model:visible="showStepDrawer"
|
||||||
|
@ -41,13 +34,11 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep, debounce } from 'lodash-es';
|
||||||
|
|
||||||
import StepDrawer from './step/stepDrawer.vue';
|
import StepDrawer from './step/stepDrawer.vue';
|
||||||
import StepTree from './step/stepTree.vue';
|
import StepTree from './step/stepTree.vue';
|
||||||
|
|
||||||
import { addLevelToTree } from '@/utils';
|
|
||||||
|
|
||||||
import type { ReportDetail, ScenarioItemType } from '@/models/apiTest/report';
|
import type { ReportDetail, ScenarioItemType } from '@/models/apiTest/report';
|
||||||
|
|
||||||
import { addFoldField } from '../utils';
|
import { addFoldField } from '../utils';
|
||||||
|
@ -56,6 +47,7 @@
|
||||||
reportDetail: ReportDetail;
|
reportDetail: ReportDetail;
|
||||||
activeType: 'tiled' | 'tab'; // 平铺模式|tab模式
|
activeType: 'tiled' | 'tab'; // 平铺模式|tab模式
|
||||||
showType: 'API' | 'CASE'; // 接口场景|用例
|
showType: 'API' | 'CASE'; // 接口场景|用例
|
||||||
|
keyWords: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const tiledList = ref<ScenarioItemType[]>([]);
|
const tiledList = ref<ScenarioItemType[]>([]);
|
||||||
|
@ -77,19 +69,74 @@
|
||||||
activeStepIndex.value = item.sort;
|
activeStepIndex.value = item.sort;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const expandedKeys = ref<(string | number)[]>([]);
|
||||||
tiledList.value = addLevelToTree<ScenarioItemType>(tiledList.value) as ScenarioItemType[];
|
const originTreeData = ref<ScenarioItemType[]>([]);
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
function initStepTree() {
|
||||||
if (props.reportDetail && props.reportDetail.children) {
|
tiledList.value = cloneDeep(props.reportDetail.children) || [];
|
||||||
tiledList.value = props.reportDetail.children || [];
|
|
||||||
tiledList.value = addLevelToTree<ScenarioItemType>(tiledList.value) as ScenarioItemType[];
|
|
||||||
tiledList.value.forEach((item) => {
|
tiledList.value.forEach((item) => {
|
||||||
addFoldField(item);
|
addFoldField(item);
|
||||||
});
|
});
|
||||||
|
originTreeData.value = cloneDeep(tiledList.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.reportDetail,
|
||||||
|
(val) => {
|
||||||
|
if (val && val.children) {
|
||||||
|
initStepTree();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
function searchStep() {
|
||||||
|
const splitLevel = props.keyWords.split('-');
|
||||||
|
const stepTypeStatus = splitLevel[1];
|
||||||
|
const stepType = splitLevel[0] !== 'REQUEST' ? ['API', 'API_CASE', 'CUSTOM_REQUEST'] : splitLevel[0];
|
||||||
|
const search = (_data: ScenarioItemType[]) => {
|
||||||
|
const result: ScenarioItemType[] = [];
|
||||||
|
_data.forEach((item) => {
|
||||||
|
if (
|
||||||
|
(stepType.includes(item.stepType) && item.status && item.status.includes(stepTypeStatus)) ||
|
||||||
|
(stepTypeStatus && stepTypeStatus.includes('scriptIdentifier') && item.scriptIdentifier)
|
||||||
|
) {
|
||||||
|
result.push({ ...item, expanded: true });
|
||||||
|
} else if (item.children) {
|
||||||
|
const filterData = search(item.children);
|
||||||
|
if (filterData.length) {
|
||||||
|
result.push({
|
||||||
|
...item,
|
||||||
|
expanded: true,
|
||||||
|
children: filterData,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
result.forEach((item: any) => expandedKeys.value.push(item.stepId)); // 搜索时,匹配的节点需要自动展开
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return search(originTreeData.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防抖搜索
|
||||||
|
const updateDebouncedSearch = debounce(() => {
|
||||||
|
if (props.keyWords) {
|
||||||
|
tiledList.value = searchStep();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.keyWords,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
initStepTree();
|
||||||
|
} else {
|
||||||
|
updateDebouncedSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
@ -66,4 +66,8 @@ export default {
|
||||||
'report.detail.api.extract': 'extract',
|
'report.detail.api.extract': 'extract',
|
||||||
'report.detail.api.assert': 'assertion',
|
'report.detail.api.assert': 'assertion',
|
||||||
'report.detail.api.subRequest': 'Sub-request',
|
'report.detail.api.subRequest': 'Sub-request',
|
||||||
|
'report.detail.api.scriptError': 'Script error',
|
||||||
|
'report.detail.api.scriptErrorTip': 'Script error',
|
||||||
|
'report.detail.api.scenario': 'scenario',
|
||||||
|
'report.detail.api.request': 'request',
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,4 +65,8 @@ export default {
|
||||||
'report.detail.api.extract': '提取',
|
'report.detail.api.extract': '提取',
|
||||||
'report.detail.api.assert': '断言',
|
'report.detail.api.assert': '断言',
|
||||||
'report.detail.api.subRequest': '子请求',
|
'report.detail.api.subRequest': '子请求',
|
||||||
|
'report.detail.api.scriptError': '脚本错误',
|
||||||
|
'report.detail.api.scriptErrorTip': '脚本报错',
|
||||||
|
'report.detail.api.scenario': '场景',
|
||||||
|
'report.detail.api.request': '请求',
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,4 +11,8 @@ export function addFoldField(node: ScenarioItemType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否为计算中
|
||||||
|
export function getIndicators(value: any) {
|
||||||
|
return value === 'Calculating' ? '-' : value || 0;
|
||||||
|
}
|
||||||
export default {};
|
export default {};
|
||||||
|
|
Loading…
Reference in New Issue