feat(工作台): 工作台首页联调部分卡片接口覆盖率
This commit is contained in:
parent
e2ef7e8c93
commit
cbc8cd8a7a
|
@ -8,6 +8,7 @@ import type { CaseManagementTable } from '@/models/caseManagement/featureCase';
|
|||
import type { CommonList, TableQueryParams } from '@/models/common';
|
||||
import type { PassRateCountDetail, TestPlanItem } from '@/models/testPlan/testPlan';
|
||||
import type {
|
||||
ApiCoverageData,
|
||||
OverViewOfProject,
|
||||
PassRateDataType,
|
||||
SelectedCardItem,
|
||||
|
@ -19,6 +20,7 @@ import {
|
|||
GetDashboardLayoutUrl,
|
||||
WorkApiCaseCountDetailUrl,
|
||||
WorkApiChangeListUrl,
|
||||
WorkApiCountCoverRateUrl,
|
||||
WorkApiCountDetailUrl,
|
||||
WorkAssociateCaseDetailUrl,
|
||||
WorkbenchApiCaseListUrl,
|
||||
|
@ -37,6 +39,7 @@ import {
|
|||
WorkHandleUserOptionsUrl,
|
||||
WorkMemberViewDetailUrl,
|
||||
WorkMyCreatedDetailUrl,
|
||||
WorkPlanLegacyBugUrl,
|
||||
WorkProOverviewDetailUrl,
|
||||
WorkReviewListUrl,
|
||||
WorkScenarioCaseCountDetailUrl,
|
||||
|
@ -172,6 +175,16 @@ export function workHandleUserOptions(projectId: string) {
|
|||
return MSR.get({ url: WorkHandleUserOptionsUrl, params: projectId }, { ignoreCancelToken: true });
|
||||
}
|
||||
|
||||
// 工作台-首页-测试计划遗留缺陷
|
||||
export function workPlanLegacyBug(data: WorkHomePageDetail) {
|
||||
return MSR.post<PassRateDataType>({ url: WorkPlanLegacyBugUrl, data }, { ignoreCancelToken: true });
|
||||
}
|
||||
|
||||
// 工作台-首页-接口测试覆盖率
|
||||
export function workApiCountCoverRage(projectId: string) {
|
||||
return MSR.get<ApiCoverageData>({ url: WorkApiCountCoverRateUrl, params: projectId }, { ignoreCancelToken: true });
|
||||
}
|
||||
|
||||
// 待办-用例评审列表
|
||||
export function workbenchTodoReviewList(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<ReviewItem>>({ url: WorkTodoReviewListUrl, data });
|
||||
|
|
|
@ -26,3 +26,5 @@ export const WorkHandleUserOptionsUrl = '/dashboard/bug_handle_user/list'; //
|
|||
export const WorkBugCountDetailUrl = '/dashboard/bug_count'; // 工作台-首页-缺陷数量
|
||||
export const WorkBugByMeCreatedUrl = '/dashboard/create_bug_by_me'; // 工作台-首页-我创建的缺陷
|
||||
export const WorkBugHandleByMeUrl = '/dashboard/handle_bug_by_me'; // 工作台-首页-待我处理的缺陷
|
||||
export const WorkPlanLegacyBugUrl = '/dashboard/plan_legacy_bug'; // 工作台-首页-测试计划遗留缺陷
|
||||
export const WorkApiCountCoverRateUrl = '/api/definition/rage'; // 工作台-首页-覆盖率
|
||||
|
|
|
@ -62,12 +62,12 @@ export const defaultCover = [
|
|||
{
|
||||
label: 'workbench.homePage.covered',
|
||||
value: '-',
|
||||
name: '',
|
||||
name: 'workbench.homePage.covered',
|
||||
},
|
||||
{
|
||||
label: 'workbench.homePage.notCover',
|
||||
value: '-',
|
||||
name: '',
|
||||
name: 'workbench.homePage.notCover',
|
||||
},
|
||||
];
|
||||
// 评审率
|
||||
|
|
|
@ -83,3 +83,18 @@ export interface PassRateDataType {
|
|||
| null;
|
||||
errorCode: number;
|
||||
}
|
||||
|
||||
export interface ApiCoverageData {
|
||||
allApiCount: number; // 总的 API 数量
|
||||
unCoverWithApiDefinition: number; // 未覆盖 API 定义的数量
|
||||
coverWithApiDefinition: number; // 覆盖了 API 定义的数量
|
||||
apiCoverage: string; // API 覆盖率
|
||||
|
||||
unCoverWithApiCase: number; // 未覆盖 API 测试用例的数量
|
||||
coverWithApiCase: number; // 覆盖了 API 测试用例的数量
|
||||
apiCaseCoverage: string; // API 测试用例覆盖率(
|
||||
|
||||
unCoverWithApiScenario: number; // 未覆盖 API 场景的数量
|
||||
coverWithApiScenario: number; // 覆盖了 API 场景的数量
|
||||
scenarioCoverage: string; // API 场景覆盖率
|
||||
}
|
||||
|
|
|
@ -36,7 +36,12 @@
|
|||
</div>
|
||||
<div class="case-ratio-wrapper mt-[16px]">
|
||||
<div class="case-ratio-item">
|
||||
<RatioPie :has-permission="hasPermission" :data="coverData" :rate-config="coverTitleConfig" />
|
||||
<RatioPie
|
||||
:has-permission="hasPermission"
|
||||
:loading="loading"
|
||||
:data="coverData"
|
||||
:rate-config="coverTitleConfig"
|
||||
/>
|
||||
</div>
|
||||
<div class="case-ratio-item">
|
||||
<RatioPie :has-permission="hasPermission" :data="caseExecuteData" :rate-config="executeTitleConfig" />
|
||||
|
@ -54,16 +59,17 @@
|
|||
* @desc 接口用例数量/场景用例数量
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import MsSelect from '@/components/business/ms-select';
|
||||
import RatioPie from './ratioPie.vue';
|
||||
|
||||
import { workApiCaseCountDetail, workScenarioCaseCountDetail } from '@/api/modules/workbench';
|
||||
import { workApiCaseCountDetail, workApiCountCoverRage, workScenarioCaseCountDetail } from '@/api/modules/workbench';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { addCommasToNumber } from '@/utils';
|
||||
|
||||
import type { SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage';
|
||||
import type { ApiCoverageData, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage';
|
||||
import { WorkCardEnum } from '@/enums/workbenchEnum';
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
@ -72,6 +78,8 @@
|
|||
|
||||
const props = defineProps<{
|
||||
item: SelectedCardItem;
|
||||
status?: boolean;
|
||||
cover?: ApiCoverageData;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -86,7 +94,7 @@
|
|||
|
||||
const executionTimeValue = ref<{ name: string; count: number | string }[]>([
|
||||
{
|
||||
name: '执行次数',
|
||||
name: t('workbench.homePage.executionTimes'),
|
||||
count: '-',
|
||||
},
|
||||
]);
|
||||
|
@ -114,8 +122,11 @@
|
|||
})
|
||||
);
|
||||
|
||||
// 接口覆盖
|
||||
const coverData = ref<{ name: string; value: number }[]>([
|
||||
const initCoverRate = [
|
||||
{
|
||||
value: 0,
|
||||
name: t('workbench.homePage.coverRate'),
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
name: t('workbench.homePage.notCover'),
|
||||
|
@ -124,7 +135,10 @@
|
|||
value: 0,
|
||||
name: t('workbench.homePage.covered'),
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
// 接口覆盖
|
||||
const coverData = ref<{ name: string; value: number }[]>(cloneDeep(initCoverRate));
|
||||
const caseExecuteData = ref<{ name: string; value: number }[]>([
|
||||
{
|
||||
value: 0,
|
||||
|
@ -187,8 +201,49 @@
|
|||
});
|
||||
|
||||
const hasPermission = ref<boolean>(false);
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
function handleCoverData(detail: ApiCoverageData) {
|
||||
const { unCoverWithApiCase, coverWithApiCase, apiCaseCoverage } = detail;
|
||||
const { unCoverWithApiScenario, coverWithApiScenario, scenarioCoverage } = detail;
|
||||
|
||||
const coverage = props.item.key === WorkCardEnum.API_CASE_COUNT ? apiCaseCoverage : scenarioCoverage;
|
||||
const unCoverWithCase =
|
||||
props.item.key === WorkCardEnum.API_CASE_COUNT ? unCoverWithApiCase : unCoverWithApiScenario;
|
||||
|
||||
const coverWithCase = WorkCardEnum.API_CASE_COUNT ? coverWithApiCase : coverWithApiScenario;
|
||||
coverData.value = [
|
||||
{
|
||||
value: Number(coverage.split('%')[0]),
|
||||
name: t('workbench.homePage.coverRate'),
|
||||
},
|
||||
{
|
||||
value: unCoverWithCase,
|
||||
name: t('workbench.homePage.notCover'),
|
||||
},
|
||||
{
|
||||
value: coverWithCase,
|
||||
name: t('workbench.homePage.covered'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async function initApiCountRate() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const detail = await workApiCountCoverRage(projectId.value);
|
||||
handleCoverData(detail);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function initApiOrScenarioCount() {
|
||||
try {
|
||||
coverData.value = cloneDeep(initCoverRate);
|
||||
const { startTime, endTime, dayNumber } = timeForm.value;
|
||||
|
||||
const params = {
|
||||
|
@ -241,6 +296,7 @@
|
|||
nextTick(() => {
|
||||
initApiOrScenarioCount();
|
||||
emit('change');
|
||||
initApiCountRate();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -248,6 +304,26 @@
|
|||
initApiOrScenarioCount();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.cover,
|
||||
(val) => {
|
||||
if (val) {
|
||||
handleCoverData(val);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.status,
|
||||
(val) => {
|
||||
loading.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerProjectIds.value,
|
||||
(val) => {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
:tooltip-text="tabItem.tooltip"
|
||||
:options="tabItem.options"
|
||||
:size="60"
|
||||
:loading="tabItem.value === 'cover' ? loading : undefined"
|
||||
:has-permission="hasPermission"
|
||||
:value-list="tabItem.valueList"
|
||||
/>
|
||||
|
@ -49,11 +50,11 @@
|
|||
import PassRatePie from './passRatePie.vue';
|
||||
import TabCard from './tabCard.vue';
|
||||
|
||||
import { workApiCountDetail } from '@/api/modules/workbench';
|
||||
import { workApiCountCoverRage, workApiCountDetail } from '@/api/modules/workbench';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import type { SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage';
|
||||
import type { ApiCoverageData, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage';
|
||||
|
||||
import { handlePieData, handleUpdateTabPie } from '../utils';
|
||||
|
||||
|
@ -63,6 +64,8 @@
|
|||
const props = defineProps<{
|
||||
item: SelectedCardItem;
|
||||
projectIds: string[];
|
||||
status?: boolean;
|
||||
cover?: ApiCoverageData;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -73,6 +76,8 @@
|
|||
required: true,
|
||||
});
|
||||
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
const projectId = ref<string>(innerProjectIds.value[0]);
|
||||
|
||||
const timeForm = inject<Ref<TimeFormParams>>(
|
||||
|
@ -93,14 +98,14 @@
|
|||
return [
|
||||
{
|
||||
label: '',
|
||||
value: 'execution',
|
||||
value: 'cover',
|
||||
valueList: coverValueList.value,
|
||||
options: { ...coverOptions.value },
|
||||
tooltip: 'workbench.homePage.apiCountCoverRateTooltip',
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
value: 'pass',
|
||||
value: 'complete',
|
||||
valueList: completeValueList.value,
|
||||
options: { ...completeOptions.value },
|
||||
tooltip: 'workbench.homePage.apiCountCompleteRateTooltip',
|
||||
|
@ -109,8 +114,47 @@
|
|||
});
|
||||
|
||||
const apiCountOptions = ref({});
|
||||
|
||||
const hasPermission = ref<boolean>(false);
|
||||
function handleCoverData(detail: ApiCoverageData) {
|
||||
const { unCoverWithApiDefinition, coverWithApiDefinition, apiCoverage } = detail;
|
||||
const coverData: {
|
||||
name: string;
|
||||
count: number;
|
||||
}[] = [
|
||||
{
|
||||
count: Number(apiCoverage.split('%')[0]),
|
||||
name: t('workbench.homePage.coverRate'),
|
||||
},
|
||||
{
|
||||
count: coverWithApiDefinition,
|
||||
name: t('workbench.homePage.covered'),
|
||||
},
|
||||
{
|
||||
count: unCoverWithApiDefinition,
|
||||
name: t('workbench.homePage.notCover'),
|
||||
},
|
||||
];
|
||||
const { options: covOptions, valueList: coverList } = handleUpdateTabPie(
|
||||
coverData,
|
||||
hasPermission.value,
|
||||
`${props.item.key}-cover`
|
||||
);
|
||||
coverValueList.value = coverList;
|
||||
coverOptions.value = covOptions;
|
||||
}
|
||||
async function initApiCountRate() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const detail = await workApiCountCoverRage(projectId.value);
|
||||
handleCoverData(detail);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function initApiCount() {
|
||||
try {
|
||||
const { startTime, endTime, dayNumber } = timeForm.value;
|
||||
|
@ -127,18 +171,8 @@
|
|||
const { statusStatisticsMap, statusPercentList, errorCode } = detail;
|
||||
|
||||
hasPermission.value = errorCode !== 109001;
|
||||
|
||||
apiCountOptions.value = handlePieData(props.item.key, hasPermission.value, statusPercentList);
|
||||
|
||||
// 覆盖率 TODO 等接口
|
||||
// const { options: covOptions, valueList: coverList } = handleUpdateTabPie(
|
||||
// statusStatisticsMap?.cover || [],
|
||||
// hasPermission.value,
|
||||
// `${props.item.key}-cover`
|
||||
// );
|
||||
// coverValueList.value = coverList;
|
||||
// coverOptions.value = covOptions;
|
||||
|
||||
// 完成率
|
||||
const { options: comOptions, valueList: completedList } = handleUpdateTabPie(
|
||||
statusStatisticsMap?.completionRate || [],
|
||||
|
@ -157,6 +191,7 @@
|
|||
nextTick(() => {
|
||||
initApiCount();
|
||||
emit('change');
|
||||
initApiCountRate();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -165,6 +200,26 @@
|
|||
emit('change');
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.cover,
|
||||
(val) => {
|
||||
if (val) {
|
||||
handleCoverData(val);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.status,
|
||||
(val) => {
|
||||
loading.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => innerProjectIds.value,
|
||||
(val) => {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</template>
|
||||
<div class="setting-wrapper">
|
||||
<div class="setting-wrapper-config">
|
||||
<CardSettingList @add="addCard" />
|
||||
<CardSettingList v-model:selectedIds="selectedIds" @add="addCard" />
|
||||
</div>
|
||||
<div class="setting-wrapper-content">
|
||||
<!-- 布局开始 -->
|
||||
|
@ -131,8 +131,11 @@
|
|||
|
||||
const defaultAllProjectType = [WorkCardEnum.PROJECT_VIEW, WorkCardEnum.CREATE_BY_ME];
|
||||
|
||||
const selectedIds = ref<WorkCardEnum[]>([]);
|
||||
|
||||
// 添加卡片
|
||||
function addCard(item: childrenWorkConfigItem) {
|
||||
selectedIds.value.push(item.value);
|
||||
const newCard: SelectedCardItem = {
|
||||
id: getGenerateId(),
|
||||
fullScreen: !!isFullScreenType.includes(item.value),
|
||||
|
@ -153,6 +156,7 @@
|
|||
function deleteHandler(item: SelectedCardItem) {
|
||||
const index = selectedCardList.value.findIndex((e) => e.id === item.id);
|
||||
selectedCardList.value.splice(index, 1);
|
||||
selectedIds.value.splice(index, 1);
|
||||
}
|
||||
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
|
@ -183,6 +187,7 @@
|
|||
(val) => {
|
||||
if (val) {
|
||||
selectedCardList.value = cloneDeep(props.list);
|
||||
selectedIds.value = props.list.map((e) => e.key);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -59,6 +59,10 @@
|
|||
(e: 'add', item: childrenWorkConfigItem): void;
|
||||
}>();
|
||||
|
||||
const innerSelectedIds = defineModel<WorkCardEnum[]>('selectedIds', {
|
||||
required: true,
|
||||
});
|
||||
|
||||
const configList = ref<WorkConfigCard[]>([
|
||||
// 概览
|
||||
{
|
||||
|
@ -212,17 +216,14 @@
|
|||
const searchKeyword = ref(''); // 存储搜索关键字
|
||||
|
||||
const filteredConfigList = computed(() => {
|
||||
if (!searchKeyword.value) {
|
||||
return configList.value;
|
||||
}
|
||||
return configList.value
|
||||
.map((item) => ({
|
||||
return configList.value.map((item) => ({
|
||||
...item,
|
||||
children: item.children.filter((child) =>
|
||||
child.label.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
children: item.children.filter(
|
||||
(child) =>
|
||||
child.label.toLowerCase().includes(searchKeyword.value.toLowerCase()) &&
|
||||
!innerSelectedIds.value.includes(child.value)
|
||||
),
|
||||
}))
|
||||
.filter((item) => item.children.length > 0);
|
||||
}));
|
||||
});
|
||||
|
||||
// 搜索
|
||||
|
|
|
@ -46,7 +46,12 @@
|
|||
import MsSelect from '@/components/business/ms-select';
|
||||
import PassRatePie from './passRatePie.vue';
|
||||
|
||||
import { workBugByMeCreated, workBugCountDetail, workBugHandleByMe } from '@/api/modules/workbench';
|
||||
import {
|
||||
workBugByMeCreated,
|
||||
workBugCountDetail,
|
||||
workBugHandleByMe,
|
||||
workPlanLegacyBug,
|
||||
} from '@/api/modules/workbench';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
|
@ -93,12 +98,17 @@
|
|||
|
||||
const countOptions = ref<Record<string, any>>({});
|
||||
|
||||
type SelectedBugCountKeys = WorkCardEnum.BUG_COUNT | WorkCardEnum.HANDLE_BUG_BY_ME | WorkCardEnum.CREATE_BUG_BY_ME;
|
||||
type SelectedBugCountKeys =
|
||||
| WorkCardEnum.BUG_COUNT
|
||||
| WorkCardEnum.HANDLE_BUG_BY_ME
|
||||
| WorkCardEnum.CREATE_BUG_BY_ME
|
||||
| WorkCardEnum.PLAN_LEGACY_BUG;
|
||||
|
||||
const currentBugCount: (data: WorkHomePageDetail) => Promise<PassRateDataType> = {
|
||||
[WorkCardEnum.BUG_COUNT]: workBugCountDetail,
|
||||
[WorkCardEnum.HANDLE_BUG_BY_ME]: workBugHandleByMe,
|
||||
[WorkCardEnum.CREATE_BUG_BY_ME]: workBugByMeCreated,
|
||||
[WorkCardEnum.PLAN_LEGACY_BUG]: workPlanLegacyBug,
|
||||
}[props.item.key as SelectedBugCountKeys];
|
||||
|
||||
const hasPermission = ref<boolean>(false);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</div>
|
||||
<!-- 概览图 -->
|
||||
<div>
|
||||
<MsChart height="300px" :options="options" />
|
||||
<MsChart height="260px" :options="options" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -115,9 +115,11 @@
|
|||
// x轴
|
||||
options.value.xAxis.data = cardModuleList.value.map((e) => e.label);
|
||||
|
||||
let maxAxis = 5;
|
||||
|
||||
// 处理data数据
|
||||
options.value.series = detail.projectCountList.map((item) => {
|
||||
const countData = item.count.map((e) => {
|
||||
const countData: Record<string, any> = item.count.map((e) => {
|
||||
return {
|
||||
name: item.name,
|
||||
value: e !== 0 ? e : undefined,
|
||||
|
@ -142,6 +144,11 @@
|
|||
},
|
||||
};
|
||||
});
|
||||
|
||||
const itemMax = Math.max(...item.count);
|
||||
|
||||
maxAxis = Math.max(itemMax, maxAxis);
|
||||
|
||||
return {
|
||||
name: item.name,
|
||||
type: 'bar',
|
||||
|
@ -154,6 +161,10 @@
|
|||
data: countData,
|
||||
};
|
||||
});
|
||||
options.value.yAxis[0].max = maxAxis < 100 ? 50 : maxAxis + 50;
|
||||
|
||||
options.value.series[0].barGap = 4;
|
||||
options.value.series[0].barCategoryGap = 24;
|
||||
}
|
||||
|
||||
async function initOverViewDetail() {
|
||||
|
@ -178,6 +189,7 @@
|
|||
|
||||
handleData(detail);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,11 @@
|
|||
options.value.graphic.invisible = invisible;
|
||||
options.value.graphic.style.text = text;
|
||||
options.value.xAxis.data = detail.xaxis.map((e) => characterLimit(e, 10));
|
||||
let maxAxis = 5;
|
||||
options.value.series = detail.projectCountList.map((item, index) => {
|
||||
const itemMax = Math.max(...item.count);
|
||||
|
||||
maxAxis = Math.max(itemMax, maxAxis);
|
||||
return {
|
||||
name: t(contentTabList[index].label),
|
||||
type: 'bar',
|
||||
|
@ -110,6 +114,7 @@
|
|||
},
|
||||
};
|
||||
});
|
||||
options.value.yAxis[0].max = maxAxis < 100 ? 50 : maxAxis + 50;
|
||||
}
|
||||
|
||||
async function initOverViewMemberDetail() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="pass-rate-content">
|
||||
<a-spin class="pass-rate-content" :loading="props.loading">
|
||||
<div class="relative flex items-center justify-center">
|
||||
<a-tooltip
|
||||
v-if="props.tooltipText"
|
||||
|
@ -17,7 +17,7 @@
|
|||
<div class="pass-rate-count">{{ hasPermission ? addCommasToNumber(item.value as number) : '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -35,6 +35,7 @@
|
|||
size: number;
|
||||
tooltipText?: string;
|
||||
hasPermission: boolean;
|
||||
loading?: boolean;
|
||||
valueList: {
|
||||
label: string;
|
||||
value: number | string;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<template>
|
||||
<a-spin class="w-full" :loading="props.loading">
|
||||
<div class="rate-content relative">
|
||||
<div class="relative flex h-full w-full items-center justify-center">
|
||||
<a-tooltip
|
||||
|
@ -12,6 +13,7 @@
|
|||
<MsChart :options="options" width="146px" />
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -28,6 +30,7 @@
|
|||
const props = defineProps<{
|
||||
data: { name: string; value: number }[];
|
||||
hasPermission: boolean;
|
||||
loading?: boolean;
|
||||
rateConfig: {
|
||||
name: string;
|
||||
color: string[];
|
||||
|
|
|
@ -94,6 +94,8 @@
|
|||
v-model:projectIds="item.projectIds"
|
||||
:type="item.key"
|
||||
:item="item"
|
||||
:status="projectLoadingStatus[item.projectIds[0]]"
|
||||
:cover="requestResults.get(item.projectIds[0])"
|
||||
@change="changeHandler"
|
||||
/>
|
||||
<ApiChangeList
|
||||
|
@ -119,7 +121,9 @@
|
|||
<ApiCount
|
||||
v-else-if="item.key === WorkCardEnum.API_COUNT"
|
||||
v-model:projectIds="item.projectIds"
|
||||
:status="projectLoadingStatus[item.projectIds[0]]"
|
||||
:item="item"
|
||||
:cover="requestResults.get(item.projectIds[0])"
|
||||
@change="changeHandler"
|
||||
/>
|
||||
<TestPlanCount
|
||||
|
@ -132,8 +136,8 @@
|
|||
</div>
|
||||
<NoData
|
||||
v-if="showNoData || !appStore.projectList.length"
|
||||
:no-res-permission="!appStore.projectList.length"
|
||||
:all-screen="!!appStore.projectList.length"
|
||||
:no-res-permission="!defaultWorkList.length"
|
||||
:all-screen="!!defaultWorkList.length"
|
||||
height="h-[calc(100vh-110px)]"
|
||||
@config="cardSetting"
|
||||
/>
|
||||
|
@ -162,10 +166,11 @@
|
|||
import DefectMemberBar from '@/views/workbench/homePage/components/defectMemberBar.vue';
|
||||
import OverviewMember from '@/views/workbench/homePage/components/overviewMember.vue';
|
||||
|
||||
import { editDashboardLayout, getDashboardLayout } from '@/api/modules/workbench';
|
||||
import { editDashboardLayout, getDashboardLayout, workApiCountCoverRage } from '@/api/modules/workbench';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useUserStore } from '@/store';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { sleep } from '@/utils';
|
||||
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
|
||||
|
||||
import { SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage';
|
||||
|
@ -260,8 +265,43 @@
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initDefaultList();
|
||||
// 用来存储每个请求的结果,key 是项目ID
|
||||
const requestResults = ref(new Map());
|
||||
// 用来存储已请求过的项目
|
||||
const requestedIds = ref(new Set());
|
||||
|
||||
const projectLoadingStatus = ref<Record<string, boolean>>({});
|
||||
|
||||
const fetchProjectDetails = async (projectId: string) => {
|
||||
try {
|
||||
projectLoadingStatus.value[projectId] = true;
|
||||
const result = await workApiCountCoverRage(projectId);
|
||||
requestResults.value.set(projectId, result);
|
||||
projectLoadingStatus.value[projectId] = false;
|
||||
await sleep(300);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
projectLoadingStatus.value[projectId] = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 针对项目id不重复的依次请求
|
||||
async function requestQueue() {
|
||||
const awaitType = [WorkCardEnum.API_COUNT, WorkCardEnum.API_CASE_COUNT, WorkCardEnum.SCENARIO_COUNT];
|
||||
const queueList = defaultWorkList.value.filter((item) => awaitType.includes(item.key));
|
||||
for (let i = 0; i < queueList.length; i++) {
|
||||
const item = queueList[i];
|
||||
const [projectId] = item.projectIds;
|
||||
if (!requestedIds.value.has(projectId)) {
|
||||
requestedIds.value.add(projectId);
|
||||
fetchProjectDetails(projectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const defaultTime = getLocalStorage(`WORK_TIME_${userStore.id}`);
|
||||
if (!defaultTime) {
|
||||
setLocalStorage(`WORK_TIME_${userStore.id}`, JSON.stringify(timeForm.value));
|
||||
|
@ -272,6 +312,8 @@
|
|||
rangeTime.value = [startTime, endTime];
|
||||
}
|
||||
}
|
||||
await initDefaultList();
|
||||
requestQueue();
|
||||
});
|
||||
|
||||
const time = ref({ ...timeForm.value });
|
||||
|
|
|
@ -48,7 +48,7 @@ export const colorMapConfig: Record<string, string[]> = {
|
|||
[WorkCardEnum.REVIEW_CASE_COUNT]: ['#9441B1', '#00C261', '#D4D4D8', '#3370FF'],
|
||||
[WorkCardEnum.TEST_PLAN_COUNT]: ['#9441B1', '#3370FF', '#00C261', '#D4D4D8'],
|
||||
[WorkCardEnum.PLAN_LEGACY_BUG]: ['#9441B1', '#3370FF', '#00C261', '#D4D4D8'],
|
||||
[WorkCardEnum.BUG_COUNT]: ['#FFA200', '#00C261', '#D4D4D8'],
|
||||
[WorkCardEnum.BUG_COUNT]: ['#FFA200', '#D4D4D8', '#00C261'],
|
||||
[WorkCardEnum.HANDLE_BUG_BY_ME]: ['#9441B1', '#3370FF', '#00C261', '#D4D4D8'],
|
||||
[WorkCardEnum.CREATE_BY_ME]: ['#9441B1', '#3370FF', '#00C261', '#D4D4D8'],
|
||||
[WorkCardEnum.API_COUNT]: ['#811FA3', '#00C261', '#3370FF', '#FFA1FF', '#EE50A3', '#FF9964', '#F9F871', '#C3DD40'],
|
||||
|
@ -71,6 +71,7 @@ export function getCommonBarOptions(hasRoom: boolean, color: string[]): Record<s
|
|||
enterable: true,
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
axis: 'auto',
|
||||
},
|
||||
formatter(params: any) {
|
||||
const html = `
|
||||
|
@ -81,7 +82,7 @@ export function getCommonBarOptions(hasRoom: boolean, color: string[]): Record<s
|
|||
<div class="flex h-[18px] items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="mb-[2px] mr-[8px] h-[8px] w-[8px] rounded-sm" style="background:${item.color}"></div>
|
||||
<div class="one-line-text max-w-[120px]" style="color:#959598">${item.seriesName}</div>
|
||||
<div class="one-line-text max-w-[100px]" style="color:#959598">${item.seriesName}</div>
|
||||
</div>
|
||||
<div class="text-[#323233] font-medium">${addCommasToNumber(item.value)}</div>
|
||||
</div>
|
||||
|
@ -123,9 +124,14 @@ export function getCommonBarOptions(hasRoom: boolean, color: string[]): Record<s
|
|||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'log',
|
||||
type: 'value',
|
||||
name: t('workbench.homePage.unit'), // 设置单位
|
||||
position: 'left',
|
||||
axisLine: {
|
||||
show: false,
|
||||
onZero: false, // 禁止 Y 轴强制显示在 0 位置
|
||||
},
|
||||
offset: 0, // 第一个 Y 轴默认位置
|
||||
nameTextStyle: {
|
||||
fontSize: 12,
|
||||
color: '#AEAEB2', // 自定义字体大小和颜色
|
||||
|
@ -141,6 +147,7 @@ export function getCommonBarOptions(hasRoom: boolean, color: string[]): Record<s
|
|||
},
|
||||
},
|
||||
min: 1,
|
||||
max: 0,
|
||||
},
|
||||
],
|
||||
graphic: {
|
||||
|
|
Loading…
Reference in New Issue