fix(接口测试): 报告bug修复和补充部分细节
This commit is contained in:
parent
44ab9a2cd8
commit
1c64577431
|
@ -3,7 +3,7 @@
|
|||
<div class="relative mr-4">
|
||||
<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-[18px] font-medium">4</div>
|
||||
<div class="text-[18px] font-medium">{{ totalCount }}</div>
|
||||
</div>
|
||||
<MsChart width="110px" height="110px" :options="props.options" />
|
||||
</div>
|
||||
|
@ -38,6 +38,12 @@
|
|||
options: Record<string, any>;
|
||||
legendData: LegendData[];
|
||||
}>();
|
||||
|
||||
const totalCount = computed(() => {
|
||||
return props.legendData.reduce((prev, item) => {
|
||||
return prev + item.count;
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
<div class="flex items-center">
|
||||
<span class="count"> {{ getExcuteRate() }} %</span>
|
||||
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
|
||||
<span>{{ getRequestEacuteCount }}</span>
|
||||
<span class="mx-1 text-[var(--color-text-4)]">/ {{ getRequestTotalCount || 0 }}</span>
|
||||
<span>{{ getIndicators(getRequestEacuteCount) }}</span>
|
||||
<span class="mx-1 text-[var(--color-text-4)]">/ {{ getIndicators(getRequestTotalCount) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-card-item-rote">
|
||||
|
@ -70,8 +70,17 @@
|
|||
>{{ detail.assertionPassRate === 'Calculating' ? '-' : detail.assertionPassRate || '0.00' }}%</span
|
||||
>
|
||||
<a-divider direction="vertical" class="!h-[16px]" :margin="8"></a-divider>
|
||||
<span>{{ addCommasToNumber(detail.assertionSuccessCount || 0) }}</span>
|
||||
<span class="mx-1 text-[var(--color-text-4)]">/ {{ detail.assertionCount || 0 }}</span>
|
||||
<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>
|
||||
|
@ -80,20 +89,13 @@
|
|||
<!-- 报告步骤分析结束 -->
|
||||
<!-- 报告明细开始 -->
|
||||
<div class="report-info">
|
||||
<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="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="CASE" :active-type="activeTab" :report-detail="detail || []" />
|
||||
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" />
|
||||
<TiledList
|
||||
:key-words="cascaderKeywords"
|
||||
show-type="CASE"
|
||||
:active-type="activeTab"
|
||||
:report-detail="detail || []"
|
||||
/>
|
||||
</div>
|
||||
<!-- 报告明细结束 -->
|
||||
</div>
|
||||
|
@ -104,6 +106,7 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
import SetReportChart from './case/setReportChart.vue';
|
||||
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
||||
import TiledList from './tiledList.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -111,6 +114,8 @@
|
|||
|
||||
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
|
||||
|
||||
import { getIndicators } from '../utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
detailInfo?: ReportDetail;
|
||||
|
@ -156,6 +161,8 @@
|
|||
console: '',
|
||||
});
|
||||
|
||||
const cascaderKeywords = ref<string>('');
|
||||
|
||||
const getTotalTime = computed(() => {
|
||||
if (detail.value) {
|
||||
const { endTime, startTime } = detail.value;
|
||||
|
@ -166,16 +173,6 @@
|
|||
}
|
||||
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 charOptions = ref({
|
||||
|
@ -237,7 +234,6 @@
|
|||
},
|
||||
});
|
||||
const activeTab = ref<'tiled' | 'tab'>('tiled');
|
||||
const condition = ref('');
|
||||
|
||||
function getExcuteRate() {
|
||||
return 100 - Number(detail.value.requestPendingRate)
|
||||
|
@ -301,7 +297,7 @@
|
|||
return {
|
||||
...item,
|
||||
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],
|
||||
};
|
||||
});
|
||||
|
|
|
@ -28,31 +28,31 @@
|
|||
<!-- 总数 -->
|
||||
<div class="countItem">
|
||||
<span class="mr-2 text-[var(--color-text-4)]"> {{ t('report.detail.stepTotal') }}</span>
|
||||
{{ detail.stepTotal || 0 }}
|
||||
{{ getTotal() || 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('report.detail.successCount') }}</div>
|
||||
{{ detail.successCount || 0 }}
|
||||
{{ getIndicators(detail.successCount) }}
|
||||
</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('report.detail.fakeErrorCount') }}</div>
|
||||
{{ detail.fakeErrorCount || 0 }}
|
||||
{{ getIndicators(detail.fakeErrorCount) }}
|
||||
</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('report.detail.errorCount') }}</div>
|
||||
{{ detail.errorCount || 0 }}
|
||||
{{ getIndicators(detail.errorCount) }}
|
||||
</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('report.detail.pendingCount') }}</div>
|
||||
{{ detail.pendingCount || 0 }}
|
||||
{{ getIndicators(detail.pendingCount) }}
|
||||
</div>
|
||||
</div>
|
||||
<StepProgress :report-detail="detail" height="8px" radius="var(--border-radius-mini)" />
|
||||
|
@ -88,12 +88,19 @@
|
|||
</div>
|
||||
<div class="flex items-center">
|
||||
<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>
|
||||
<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="mx-1">/</span> {{ addCommasToNumber(detail.assertionCount) || 0 }}</span
|
||||
><span class="mx-1">/</span>
|
||||
{{
|
||||
getIndicators(detail.assertionCount) === '-' ? '-' : addCommasToNumber(detail.assertionCount) || 0
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -106,7 +113,7 @@
|
|||
<div class="relative mr-4">
|
||||
<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-[18px] font-medium">4</div>
|
||||
<div class="text-[18px] font-medium">{{ getTotal() }}</div>
|
||||
</div>
|
||||
<MsChart width="110px" height="110px" :options="charOptions" />
|
||||
</div>
|
||||
|
@ -127,20 +134,8 @@
|
|||
<!-- 报告步骤分析和请求分析结束 -->
|
||||
<!-- 报告明细开始 -->
|
||||
<div class="report-info">
|
||||
<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="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 || []" />
|
||||
<reportInfoHeader v-model:keyword="cascaderKeywords" v-model:active-tab="activeTab" />
|
||||
<TiledList :key-words="cascaderKeywords" show-type="API" :active-type="activeTab" :report-detail="detail || []" />
|
||||
</div>
|
||||
<!-- 报告明细结束 -->
|
||||
</div>
|
||||
|
@ -151,6 +146,7 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
import MsChart from '@/components/pure/chart/index.vue';
|
||||
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
||||
import StepProgress from './stepProgress.vue';
|
||||
import TiledList from './tiledList.vue';
|
||||
|
||||
|
@ -159,6 +155,8 @@
|
|||
|
||||
import type { LegendData, ReportDetail } from '@/models/apiTest/report';
|
||||
|
||||
import { getIndicators } from '../utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
detailInfo?: ReportDetail;
|
||||
|
@ -204,7 +202,7 @@
|
|||
console: '',
|
||||
});
|
||||
|
||||
const timeUnits = ['ms', 'sec', 'min', 'hr'];
|
||||
const cascaderKeywords = ref<string>('');
|
||||
|
||||
const getTotalTime = computed(() => {
|
||||
if (detail.value) {
|
||||
|
@ -216,16 +214,10 @@
|
|||
}
|
||||
return '-';
|
||||
});
|
||||
const methods = ref([
|
||||
{
|
||||
label: t('report.detail.api.tiledDisplay'),
|
||||
value: 'tiled',
|
||||
},
|
||||
{
|
||||
label: t('report.detail.api.tabDisplay'),
|
||||
value: 'tab',
|
||||
},
|
||||
]);
|
||||
function getTotal() {
|
||||
const { errorCount, successCount, fakeErrorCount, pendingCount } = detail.value;
|
||||
return errorCount + successCount + fakeErrorCount + pendingCount;
|
||||
}
|
||||
|
||||
const legendData = ref<LegendData[]>([]);
|
||||
const charOptions = ref({
|
||||
|
@ -287,7 +279,6 @@
|
|||
},
|
||||
});
|
||||
const activeTab = ref<'tiled' | 'tab'>('tiled');
|
||||
const condition = ref('');
|
||||
|
||||
function initOptionsData() {
|
||||
const tempArr = [
|
||||
|
@ -334,7 +325,7 @@
|
|||
return {
|
||||
...item,
|
||||
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],
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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
|
||||
ref="treeRef"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
v-model:expanded-keys="innerExpandedKeys"
|
||||
v-model:data="steps"
|
||||
:expand-all="props.expandAll"
|
||||
:field-names="{ title: 'name', key: 'stepId', children: 'children' }"
|
||||
|
@ -75,9 +76,27 @@
|
|||
</a-tooltip>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<MsTag class="cursor-pointer" :type="step.status === 'SUCCESS' ? 'success' : 'danger'" theme="light">
|
||||
{{ step.status === 'SUCCESS' ? t('report.detail.api.pass') : t('report.detail.api.resError') }}
|
||||
</MsTag>
|
||||
<stepStatus v-if="step.status" :status="step.status" />
|
||||
<!-- 脚本报错 -->
|
||||
<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>
|
||||
<template #content>
|
||||
<div>{{ step.scriptIdentifier }}</div>
|
||||
</template>
|
||||
</a-popover>
|
||||
<span class="statusCode mx-2">
|
||||
<div class="mr-2"> {{ t('report.detail.api.statusCode') }}</div>
|
||||
<a-popover position="left" content-class="response-popover-content">
|
||||
|
@ -97,14 +116,22 @@
|
|||
<span class="resTime">
|
||||
{{ t('report.detail.api.responseTime') }}
|
||||
<span class="resTimeCount ml-2"
|
||||
>{{ formatDuration(step.requestTime).split('-')[0]
|
||||
}}{{ formatDuration(step.requestTime).split('-')[1] }}</span
|
||||
>{{ step.requestTime ? formatDuration(step.requestTime).split('-')[0] : '-'
|
||||
}}{{ step.requestTime ? formatDuration(step.requestTime).split('-')[1] : 'ms' }}</span
|
||||
></span
|
||||
>
|
||||
<span class="resSize">
|
||||
{{ t('report.detail.api.responseSize') }}
|
||||
<span class="resTimeCount ml-2">{{ step.responseSize || 0 }} bytes</span></span
|
||||
>
|
||||
<a-popover position="left" content-class="response-popover-content">
|
||||
<span class="resSize">
|
||||
{{ t('report.detail.api.responseSize') }}
|
||||
<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 v-if="!step.fold" class="line"></div>
|
||||
|
@ -142,6 +169,7 @@
|
|||
import MsTag from '@/components/pure/ms-tag/ms-tag.vue';
|
||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||
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 ConditionStatus from '@/views/api-test/report/component/conditionStatus.vue';
|
||||
|
||||
|
@ -160,6 +188,7 @@
|
|||
console?: string;
|
||||
environmentName?: string;
|
||||
reportId?: string;
|
||||
expandedKeys: (string | number)[];
|
||||
}>();
|
||||
const loading = ref(false);
|
||||
|
||||
|
@ -170,6 +199,9 @@
|
|||
const steps = defineModel<ScenarioItemType[]>('steps', {
|
||||
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',
|
||||
}"
|
||||
>
|
||||
<!-- <a-scrollbar
|
||||
:style="{
|
||||
overflow: 'auto',
|
||||
height: 'calc(100vh - 424px)',
|
||||
width: '100%',
|
||||
}"
|
||||
> -->
|
||||
<!-- 步骤树 -->
|
||||
<stepTree
|
||||
ref="stepTreeRef"
|
||||
v-model:steps="tiledList"
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
:show-type="props.showType"
|
||||
:active-type="props.activeType"
|
||||
:expand-all="isExpandAll"
|
||||
|
@ -24,7 +18,6 @@
|
|||
:report-id="props.reportDetail.id"
|
||||
@detail="showDetail"
|
||||
/>
|
||||
<!-- </a-scrollbar> -->
|
||||
<!-- 步骤抽屉 -->
|
||||
<StepDrawer
|
||||
v-model:visible="showStepDrawer"
|
||||
|
@ -41,13 +34,11 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { cloneDeep, debounce } from 'lodash-es';
|
||||
|
||||
import StepDrawer from './step/stepDrawer.vue';
|
||||
import StepTree from './step/stepTree.vue';
|
||||
|
||||
import { addLevelToTree } from '@/utils';
|
||||
|
||||
import type { ReportDetail, ScenarioItemType } from '@/models/apiTest/report';
|
||||
|
||||
import { addFoldField } from '../utils';
|
||||
|
@ -56,6 +47,7 @@
|
|||
reportDetail: ReportDetail;
|
||||
activeType: 'tiled' | 'tab'; // 平铺模式|tab模式
|
||||
showType: 'API' | 'CASE'; // 接口场景|用例
|
||||
keyWords: string;
|
||||
}>();
|
||||
|
||||
const tiledList = ref<ScenarioItemType[]>([]);
|
||||
|
@ -77,19 +69,74 @@
|
|||
activeStepIndex.value = item.sort;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
tiledList.value = addLevelToTree<ScenarioItemType>(tiledList.value) as ScenarioItemType[];
|
||||
});
|
||||
const expandedKeys = ref<(string | number)[]>([]);
|
||||
const originTreeData = ref<ScenarioItemType[]>([]);
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.reportDetail && props.reportDetail.children) {
|
||||
tiledList.value = props.reportDetail.children || [];
|
||||
tiledList.value = addLevelToTree<ScenarioItemType>(tiledList.value) as ScenarioItemType[];
|
||||
tiledList.value.forEach((item) => {
|
||||
addFoldField(item);
|
||||
function initStepTree() {
|
||||
tiledList.value = cloneDeep(props.reportDetail.children) || [];
|
||||
tiledList.value.forEach((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>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
|
|
@ -66,4 +66,8 @@ export default {
|
|||
'report.detail.api.extract': 'extract',
|
||||
'report.detail.api.assert': 'assertion',
|
||||
'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.assert': '断言',
|
||||
'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 {};
|
||||
|
|
Loading…
Reference in New Issue