fix(接口测试): 报告bug修复和补充部分细节

This commit is contained in:
xinxin.wu 2024-03-25 16:11:38 +08:00 committed by Craftsman
parent 44ab9a2cd8
commit 1c64577431
10 changed files with 322 additions and 97 deletions

View File

@ -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">

View File

@ -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],
}; };
}); });

View File

@ -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],
}; };
}); });

View File

@ -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>

View File

@ -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>

View File

@ -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,
});
/** /**
* 处理步骤展开折叠 * 处理步骤展开折叠

View File

@ -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">

View File

@ -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',
}; };

View File

@ -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': '请求',
}; };

View File

@ -11,4 +11,8 @@ export function addFoldField(node: ScenarioItemType) {
} }
} }
// 是否为计算中
export function getIndicators(value: any) {
return value === 'Calculating' ? '-' : value || 0;
}
export default {}; export default {};