diff --git a/frontend/src/config/workbench.ts b/frontend/src/config/workbench.ts index 07e6b92f87..5a6b6f77e2 100644 --- a/frontend/src/config/workbench.ts +++ b/frontend/src/config/workbench.ts @@ -281,6 +281,8 @@ export const commonRatePieOptions = { color: [], radius: ['65%', '80%'], center: [44, '50%'], + minAngle: 5, + minShowLabelAngle: 10, avoidLabelOverlap: false, label: { show: false, diff --git a/frontend/src/views/api-test/components/caseAndScenarioReportDrawer.vue b/frontend/src/views/api-test/components/caseAndScenarioReportDrawer.vue index bdfb554c7d..bea591d7db 100644 --- a/frontend/src/views/api-test/components/caseAndScenarioReportDrawer.vue +++ b/frontend/src/views/api-test/components/caseAndScenarioReportDrawer.vue @@ -44,9 +44,19 @@ + - @@ -76,6 +86,9 @@ doNotShowShare?: boolean; // 不展示分享按钮 reportDetail?: (...args: any) => Promise; // 获取报告接口 getReportStepDetail?: (...args: any) => Promise; // 获取步骤的详情内容接口 + caseName?: string; // 用例名称 + caseId?: string; // 用例id + isFilterStep?: boolean; }>(); const appStore = useAppStore(); diff --git a/frontend/src/views/api-test/management/components/management/case/caseTable.vue b/frontend/src/views/api-test/management/components/management/case/caseTable.vue index 81b54226f0..d1b392535c 100644 --- a/frontend/src/views/api-test/management/components/management/case/caseTable.vue +++ b/frontend/src/views/api-test/management/components/management/case/caseTable.vue @@ -288,7 +288,13 @@ @finished="loadCaseListAndResetSelector" /> - + (''); + const currentCaseName = ref(''); const showExecuteResult = ref(false); async function showResult(record: ApiCaseDetail) { if (!record.lastReportId) return; activeReportId.value = record.lastReportId; + currentId.value = record.id; + currentCaseName.value = record.name; showExecuteResult.value = true; } diff --git a/frontend/src/views/api-test/report/component/caseReportCom.vue b/frontend/src/views/api-test/report/component/caseReportCom.vue index 5ce37e1e2a..850288e12c 100644 --- a/frontend/src/views/api-test/report/component/caseReportCom.vue +++ b/frontend/src/views/api-test/report/component/caseReportCom.vue @@ -175,9 +175,12 @@ v-model:keyword-name="keywordName" :key-words="cascaderKeywords" show-type="CASE" + :case-id="props.caseId" + :case-name="props.caseName" :active-type="activeTab" :report-detail="detail || []" :get-report-step-detail="props.getReportStepDetail" + :is-filter-step="props.isFilterStep" :is-export="props.isExport" /> @@ -207,6 +210,9 @@ detailInfo?: ReportDetail; getReportStepDetail?: (...args: any) => Promise; // 获取步骤的详情内容接口 isExport?: boolean; + isFilterStep?: boolean; // 是否打开抽屉之前过滤用例步骤 + caseName?: string; // 用例名称关键字 + caseId?: string; // 用例id }>(); const detail = ref({ @@ -250,7 +256,7 @@ }); const cascaderKeywords = ref(''); - const keywordName = ref(''); + const keywordName = ref(props.caseName || ''); const getTotalTime = computed(() => { if (detail.value) { diff --git a/frontend/src/views/api-test/report/component/scenarioCom.vue b/frontend/src/views/api-test/report/component/scenarioCom.vue index 81d39ec5bb..02100dc158 100644 --- a/frontend/src/views/api-test/report/component/scenarioCom.vue +++ b/frontend/src/views/api-test/report/component/scenarioCom.vue @@ -47,12 +47,15 @@ @@ -84,6 +87,9 @@ detailInfo?: ReportDetail; getReportStepDetail?: (...args: any) => Promise; // 获取步骤的详情内容接口 isExport?: boolean; // 是否是导出pdf预览 + isFilterStep?: boolean; // 是否打开抽屉之前过滤用例步骤 + caseName?: string; // 用例名称关键字 + caseId?: string; // 用例id }>(); const detail = ref({ @@ -149,7 +155,7 @@ } return ''; }); - const keywordName = ref(''); + const keywordName = ref(props.caseName || ''); const reportAnalysisList = computed(() => [ { diff --git a/frontend/src/views/api-test/report/component/tiledList.vue b/frontend/src/views/api-test/report/component/tiledList.vue index 1e0c618073..a5683adbe7 100644 --- a/frontend/src/views/api-test/report/component/tiledList.vue +++ b/frontend/src/views/api-test/report/component/tiledList.vue @@ -76,6 +76,9 @@ keyWords: string; getReportStepDetail?: (...args: any) => Promise; // 获取步骤的详情内容接口 isExport?: boolean; // 是否是导出pdf预览 + isFilterStep?: boolean; // 是否打开抽屉之前过滤用例步骤 + caseId?: string; // 用例id + caseName?: string; // 用例名称 }>(); const { t } = useI18n(); @@ -106,14 +109,6 @@ const expandedKeys = ref<(string | number)[]>([]); const originTreeData = ref([]); - function initStepTree() { - tiledList.value = cloneDeep(props.reportDetail.children) || []; - tiledList.value.forEach((item) => { - addFoldField(item); - }); - originTreeData.value = cloneDeep(tiledList.value); - } - const controlCurrent = ref(0); const isFailedRetry = computed(() => { // 所有步骤 id 相同且带有重试前缀,说明是单个用例的重试结果 @@ -145,15 +140,6 @@ }); }); - watch( - () => props.reportDetail, - (val) => { - if (val && val.children) { - initStepTree(); - } - }, - { deep: true, immediate: true } - ); const showApiType = ref([ ScenarioStepType.API, ScenarioStepType.API_CASE, @@ -167,12 +153,14 @@ const stepType = splitLevel[0] === 'CUSTOM_REQUEST' ? ['API', 'API_CASE', 'CUSTOM_REQUEST'] : Object.values(ScenarioStepType); const nameSearch = innerKeyword.value?.toLowerCase(); // 传入的 name 检索关键字 - const search = (_data: ScenarioItemType[]) => { const result: ScenarioItemType[] = []; _data.forEach((item) => { const isStepChildren = item.children && item?.children.length && showApiType.value.includes(item.stepType); + const isFilterCaseStep = + props.isFilterStep && props.reportDetail.integrated && innerKeyword.value === props.caseName; + // 匹配步骤类型 const matchStepType = stepType.includes(item.stepType); @@ -183,9 +171,16 @@ // 条件匹配逻辑 let matchesStepCondition; + // 如果开启用例过滤且为集合报告过滤用例步骤 + if (isFilterCaseStep) { + const caseStepCondition = item.name?.toLowerCase().includes(nameSearch) && item.stepId === props.caseId; + matchesStepCondition = stepTypeStatus + ? caseStepCondition && matchStepType && matchStepStatus + : caseStepCondition; + } // 如果传入了 name 且有状态 - if (nameSearch && stepTypeStatus) { + else if (nameSearch && stepTypeStatus) { matchesStepCondition = matchStepType && matchStepStatus && item.name?.toLowerCase().includes(nameSearch); } // 仅传入了 name 没有状态或类型 @@ -235,6 +230,19 @@ } }, 300); + function initStepTree() { + tiledList.value = cloneDeep(props.reportDetail.children) || []; + tiledList.value.forEach((item) => { + addFoldField(item); + }); + originTreeData.value = cloneDeep(tiledList.value); + + // 过滤当前用例步骤 + if (props.isFilterStep && props.reportDetail.integrated) { + updateDebouncedSearch(); + } + } + watch( () => props.keyWords, (val) => { @@ -246,6 +254,16 @@ } ); + watch( + () => props.reportDetail, + (val) => { + if (val && val.children) { + initStepTree(); + } + }, + { deep: true, immediate: true } + ); + defineExpose({ updateDebouncedSearch, initStepTree, diff --git a/frontend/src/views/api-test/scenario/components/scenarioTable.vue b/frontend/src/views/api-test/scenario/components/scenarioTable.vue index 04b00d5b86..cb7416ebd2 100644 --- a/frontend/src/views/api-test/scenario/components/scenarioTable.vue +++ b/frontend/src/views/api-test/scenario/components/scenarioTable.vue @@ -468,6 +468,9 @@ diff --git a/frontend/src/views/workbench/homePage/utils.ts b/frontend/src/views/workbench/homePage/utils.ts index 04161c074a..e8ffd22870 100644 --- a/frontend/src/views/workbench/homePage/utils.ts +++ b/frontend/src/views/workbench/homePage/utils.ts @@ -597,19 +597,20 @@ export function handleUpdateTabPie( if (hasPermission) { const pieBorderWidth = countList.slice(1).filter((e) => Number(e.count) > 0).length === 1 ? 0 : 1; - lastCountList = countList.slice(1).map((item) => { + lastCountList = countList.slice(1).map((item, i) => { return { value: item.count, label: item.name, name: item.name, itemStyle: { + color: defaultValueMap[typeKey][valueKey].color[i], borderWidth: pieBorderWidth, borderColor: '#ffffff', }, }; }); - options.series.data = lastCountList.every((e) => e.value === 0) ? [] : lastCountList; + options.series.data = lastCountList.every((e) => e.value === 0) ? [] : lastCountList.filter((e) => e.value !== 0); options.title.text = countList[0].name ?? ''; options.title.subtext = `${countList[0].count ?? 0}%`; @@ -625,8 +626,6 @@ export function handleUpdateTabPie( options.title.subtext = '-%'; } - options.series.color = defaultValueMap[typeKey][valueKey].color; - const lastValueList = lastCountList.map((item, index) => { return { ...item,