From fced7b284f1023efbd5e3bf5883de5f1cf72d81c Mon Sep 17 00:00:00 2001 From: "xinxin.wu" Date: Wed, 13 Nov 2024 19:17:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=B7=A5=E4=BD=9C=E5=8F=B0):=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E5=8F=B0=E9=A6=96=E9=A1=B5=E8=81=94=E8=B0=83=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/modules/workbench.ts | 32 +- frontend/src/api/requrls/workbench.ts | 4 + .../src/components/pure/ms-table/useTable.ts | 3 +- frontend/src/config/workbench.ts | 301 ++++++++++++++++++ frontend/src/models/workbench/homePage.ts | 17 +- .../homePage/components/apiChangeList.vue | 66 ++-- .../homePage/components/apiCount.vue | 125 +++----- .../homePage/components/caseCount.vue | 89 ++---- .../homePage/components/caseReviewedCount.vue | 84 ++--- .../homePage/components/defectCount.vue | 78 ++--- .../homePage/components/defectMemberBar.vue | 213 ++++++------- .../homePage/components/overview.vue | 6 +- .../homePage/components/overviewMember.vue | 20 +- .../homePage/components/passRatePie.vue | 5 +- .../homePage/components/ratioPie.vue | 2 +- .../homePage/components/relatedCaseCount.vue | 56 ++-- .../workbench/homePage/components/tabCard.vue | 2 +- .../homePage/components/testPlanCount.vue | 139 +++----- .../homePage/components/waitReviewList.vue | 49 ++- .../src/views/workbench/homePage/index.vue | 1 + .../views/workbench/homePage/locale/en-US.ts | 6 + .../views/workbench/homePage/locale/zh-CN.ts | 6 + .../src/views/workbench/homePage/utils.ts | 259 ++++++--------- 23 files changed, 881 insertions(+), 682 deletions(-) create mode 100644 frontend/src/config/workbench.ts diff --git a/frontend/src/api/modules/workbench.ts b/frontend/src/api/modules/workbench.ts index 773584d6b9..3bc4a2504c 100644 --- a/frontend/src/api/modules/workbench.ts +++ b/frontend/src/api/modules/workbench.ts @@ -1,6 +1,6 @@ import MSR from '@/api/http/index'; -import type { ApiCaseDetail } from '@/models/apiTest/management'; +import type { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management'; import type { ApiScenarioTableItem } from '@/models/apiTest/scenario'; import type { BugListItem } from '@/models/bug-management'; import type { ReviewItem } from '@/models/caseManagement/caseReview'; @@ -17,6 +17,7 @@ import type { import { EditDashboardLayoutUrl, GetDashboardLayoutUrl, + WorkApiChangeListUrl, WorkAssociateCaseDetailUrl, WorkbenchApiCaseListUrl, WorkbenchBugListUrl, @@ -25,10 +26,13 @@ import { WorkbenchScenarioListUrl, WorkbenchTestPlanListUrl, WorkbenchTestPlanStatisticUrl, + WorkBugHandlerDetailUrl, WorkCaseCountDetailUrl, + WorkCaseReviewDetailUrl, WorkMemberViewDetailUrl, WorkMyCreatedDetailUrl, WorkProOverviewDetailUrl, + WorkReviewListUrl, WorkTodoBugListUrl, WorkTodoPlanListUrl, WorkTodoReviewListUrl, @@ -100,6 +104,32 @@ export function workAssociateCaseDetail(data: WorkHomePageDetail) { return MSR.post({ url: WorkAssociateCaseDetailUrl, data }); } +// 工作台-首页-用例评审数 +export function workCaseReviewDetail(data: WorkHomePageDetail) { + return MSR.post({ url: WorkCaseReviewDetailUrl, data }); +} + +// 工作台-首页-缺陷处理人 +export function workBugHandlerDetail(data: WorkHomePageDetail) { + return MSR.post({ url: WorkBugHandlerDetailUrl, data }); +} + +// 工作台-首页-接口变更 +export function workApiChangeList(data: WorkHomePageDetail) { + return MSR.post>( + { url: WorkApiChangeListUrl, data }, + { ignoreCancelToken: true, errorMessageMode: 'none' } + ); +} + +// 工作台-首页-接口变更 +export function workReviewList(data: WorkHomePageDetail) { + return MSR.post>( + { url: WorkReviewListUrl, data }, + { ignoreCancelToken: true, errorMessageMode: 'none' } + ); +} + // 待办-用例评审列表 export function workbenchTodoReviewList(data: TableQueryParams) { return MSR.post>({ url: WorkTodoReviewListUrl, data }); diff --git a/frontend/src/api/requrls/workbench.ts b/frontend/src/api/requrls/workbench.ts index 37c95a392f..7f2d104b25 100644 --- a/frontend/src/api/requrls/workbench.ts +++ b/frontend/src/api/requrls/workbench.ts @@ -15,3 +15,7 @@ export const WorkTodoBugListUrl = '/dashboard/todo/bug/page'; // 工作台-待 export const WorkMemberViewDetailUrl = '/dashboard/project_member_view'; // 工作台-首页-人员概览 export const WorkCaseCountDetailUrl = '/dashboard/case_count'; // 工作台-首页-用例数量 export const WorkAssociateCaseDetailUrl = '/dashboard/associate_case_count'; // 工作台-首页-关联用例数量 +export const WorkBugHandlerDetailUrl = '/dashboard/bug_handle_user'; // 工作台-首页-缺陷处理人 +export const WorkApiChangeListUrl = '/dashboard/api_change'; // 工作台-首页-接口变更 +export const WorkCaseReviewDetailUrl = '/dashboard/review_case_count'; // 工作台-首页-用例评审数 +export const WorkReviewListUrl = '/dashboard/reviewing_by_me'; // 工作台-首页-待我评审 diff --git a/frontend/src/components/pure/ms-table/useTable.ts b/frontend/src/components/pure/ms-table/useTable.ts index e0cefe28ed..b3b5a44733 100644 --- a/frontend/src/components/pure/ms-table/useTable.ts +++ b/frontend/src/components/pure/ms-table/useTable.ts @@ -259,11 +259,12 @@ export default function useTableProps( return data; } } catch (err) { - // TODO 在这里处理拦截设置表格无资源权限 setTableErrorStatus('error'); propsRes.value.data = []; // eslint-disable-next-line no-console console.log(err); + + throw err; // 将错误抛出 } finally { setLoading(false); // debug 模式下打印属性 diff --git a/frontend/src/config/workbench.ts b/frontend/src/config/workbench.ts new file mode 100644 index 0000000000..843efc07fe --- /dev/null +++ b/frontend/src/config/workbench.ts @@ -0,0 +1,301 @@ +import { cloneDeep } from 'lodash-es'; + +import { commonConfig, toolTipConfig } from '@/config/testPlan'; + +import type { ModuleCardItem } from '@/models/workbench/homePage'; +import { WorkCardEnum, WorkOverviewEnum, WorkOverviewIconEnum } from '@/enums/workbenchEnum'; + +export const contentTabList: ModuleCardItem[] = [ + { + label: 'workbench.homePage.functionalUseCase', + value: WorkOverviewEnum.FUNCTIONAL, + icon: WorkOverviewIconEnum.FUNCTIONAL, + color: 'rgb(var(--primary-5))', + count: 0, + }, + { + label: 'workbench.homePage.useCaseReview', + value: WorkOverviewEnum.CASE_REVIEW, + icon: WorkOverviewIconEnum.CASE_REVIEW, + color: 'rgb(var(--success-6))', + count: 0, + }, + { + label: 'workbench.homePage.interfaceAPI', + value: WorkOverviewEnum.API, + icon: WorkOverviewIconEnum.API, + color: 'rgb(var(--link-6))', + count: 0, + }, + { + label: 'workbench.homePage.interfaceCASE', + value: WorkOverviewEnum.API_CASE, + icon: WorkOverviewIconEnum.API_CASE, + color: 'rgb(var(--link-6))', + count: 0, + }, + { + label: 'workbench.homePage.interfaceScenario', + value: WorkOverviewEnum.API_SCENARIO, + icon: WorkOverviewIconEnum.API_SCENARIO, + color: 'rgb(var(--link-6))', + count: 0, + }, + { + label: 'workbench.homePage.apiPlan', + value: WorkOverviewEnum.TEST_PLAN, + icon: WorkOverviewIconEnum.TEST_PLAN, + color: 'rgb(var(--link-6))', + count: 0, + }, + { + label: 'workbench.homePage.bugCount', + value: WorkOverviewEnum.BUG_COUNT, + icon: WorkOverviewIconEnum.BUG_COUNT, + color: 'rgb(var(--danger-6))', + count: 0, + }, +]; + +// 覆盖率 +export const defaultCover = [ + { + label: 'workbench.homePage.covered', + value: '-', + name: '', + }, + { + label: 'workbench.homePage.notCover', + value: '-', + name: '', + }, +]; +// 评审率 +export const defaultReview = [ + { + label: 'workbench.homePage.reviewed', + value: '-', + name: '', + }, + { + label: 'workbench.homePage.unReviewed', + value: '-', + name: '', + }, +]; +// 通过率 +export const defaultPass = [ + { + label: 'workbench.homePage.havePassed', + value: '-', + name: '', + }, + { + label: 'workbench.homePage.notPass', + value: '-', + name: '', + }, +]; +// 完成率 +export const defaultComplete = [ + { + label: 'common.completed', + value: 10000, + name: '', + }, + { + label: 'common.inProgress', + value: 2000, + name: '', + }, + { + label: 'workbench.homePage.unFinish', + value: 2000, + name: '', + }, +]; +// 执行率 +export const defaultExecution = [ + { + label: 'common.unExecute', + value: 10000, + name: '', + }, + { + label: 'common.executed', + value: 2000, + name: '', + }, +]; +// 遗留率 +export const defaultLegacy = [ + { + label: 'workbench.homePage.defectTotal', + value: 10000, + name: '', + }, + { + label: 'workbench.homePage.legacyDefectsNumber', + value: 2000, + name: '', + }, +]; + +export const defaultValueMap: Record = { + // 用例数量 + [WorkCardEnum.CASE_COUNT]: { + review: { + defaultList: cloneDeep(defaultCover), + color: ['#00C261', '#D4D4D8'], + defaultName: 'workbench.homePage.reviewRate', + }, + pass: { + defaultList: cloneDeep(defaultPass), + color: ['#00C261', '#ED0303'], + defaultName: 'workbench.homePage.passRate', + }, + }, + // 关联用例数量 + [WorkCardEnum.ASSOCIATE_CASE_COUNT]: { + cover: { + defaultList: cloneDeep(defaultCover), + color: ['#00C261', '#D4D4D8'], + defaultName: 'workbench.homePage.coverRate', + }, + }, + // 用例评审数 + [WorkCardEnum.REVIEW_CASE_COUNT]: { + cover: { + defaultList: cloneDeep(defaultCover), + color: ['#00C261', '#D4D4D8'], + defaultName: 'workbench.homePage.coverRate', + }, + }, + // 测试计划数 + [WorkCardEnum.TEST_PLAN_COUNT]: { + execute: { + defaultList: cloneDeep(defaultExecution), + color: ['#D4D4D8', '#00C261'], + defaultName: 'workbench.homePage.executeRate', + }, + pass: { + defaultList: cloneDeep(defaultPass), + color: ['#D4D4D8', '#00C261'], + defaultName: 'workbench.homePage.passRate', + }, + complete: { + defaultList: cloneDeep(defaultComplete), + color: ['#00C261', '#3370FF', '#D4D4D8'], + defaultName: 'workbench.homePage.completeRate', + }, + }, + // 测试计划遗留缺陷 + [WorkCardEnum.PLAN_LEGACY_BUG]: { + legacy: { + defaultList: cloneDeep(defaultLegacy), + color: ['#D4D4D8', '#00C261'], + defaultName: 'workbench.homePage.legacyRate', + }, + }, + // 缺陷数 + [WorkCardEnum.BUG_COUNT]: { + legacy: { + defaultList: cloneDeep(defaultLegacy), + color: ['#D4D4D8', '#00C261'], + defaultName: 'workbench.homePage.legacyRate', + }, + }, + // 待我处理的缺陷 + [WorkCardEnum.HANDLE_BUG_BY_ME]: { + legacy: { + defaultList: cloneDeep(defaultLegacy), + color: ['#D4D4D8', '#00C261'], + defaultName: 'workbench.homePage.legacyRate', + }, + }, + // 接口数量 + [WorkCardEnum.API_COUNT]: { + cover: { + defaultList: cloneDeep(defaultCover), + color: ['#00C261', '#D4D4D8'], + defaultName: 'workbench.homePage.coverRate', + }, + complete: { + defaultList: cloneDeep(defaultComplete), + color: ['#00C261', '#3370FF', '#D4D4D8'], + defaultName: 'workbench.homePage.completeRate', + }, + }, + // 我创建的缺陷 + [WorkCardEnum.CREATE_BUG_BY_ME]: { + legacy: { + defaultList: cloneDeep(defaultLegacy), + color: ['#D4D4D8', '#00C261'], + defaultName: 'workbench.homePage.legacyRate', + }, + }, +}; + +// XX率饼图配置 +export const commonRatePieOptions = { + ...commonConfig, + title: { + show: true, + text: '', + left: 26, + top: '20%', + textStyle: { + fontSize: 12, + fontWeight: 'normal', + color: '#959598', + }, + triggerEvent: true, // 开启鼠标事件 + subtext: '0', + subtextStyle: { + fontSize: 12, + color: '#323233', + fontWeight: 'bold', + align: 'center', + lineHeight: 3, + }, + textAlign: 'center', + tooltip: { + ...toolTipConfig, + position: 'right', + }, + }, + tooltip: { + ...toolTipConfig, + position: 'right', + }, + legend: { + show: false, + }, + series: { + name: '', + type: 'pie', + color: [], + padAngle: 2, + radius: ['85%', '100%'], + center: [30, '50%'], + avoidLabelOverlap: false, + label: { + show: false, + position: 'center', + }, + emphasis: { + scale: false, // 禁用放大效果 + label: { + show: false, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: [], + }, +}; + +export default {}; diff --git a/frontend/src/models/workbench/homePage.ts b/frontend/src/models/workbench/homePage.ts index a6b0145d7c..06361bd22d 100644 --- a/frontend/src/models/workbench/homePage.ts +++ b/frontend/src/models/workbench/homePage.ts @@ -56,7 +56,7 @@ export interface OverViewOfProject { } export interface ModuleCardItem { - label: string | number; + label: string; value: string | number; count?: number; icon?: string; @@ -73,10 +73,13 @@ export type StatusStatisticsMapType = Record< >; export interface PassRateDataType { - statusStatisticsMap: StatusStatisticsMapType; - statusPercentList: { - status: string; // 状态 - count: number; - percentValue: string; // 百分比 - }[]; + statusStatisticsMap: StatusStatisticsMapType | null; + statusPercentList: + | { + status: string; // 状态 + count: number; + percentValue: string; // 百分比 + }[] + | null; + errorCode: number; } diff --git a/frontend/src/views/workbench/homePage/components/apiChangeList.vue b/frontend/src/views/workbench/homePage/components/apiChangeList.vue index 987409377d..f7d69e7e95 100644 --- a/frontend/src/views/workbench/homePage/components/apiChangeList.vue +++ b/frontend/src/views/workbench/homePage/components/apiChangeList.vue @@ -14,6 +14,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > @@ -29,7 +30,16 @@ v-on="propsEvent" > + @@ -49,6 +59,7 @@ import useTable from '@/components/pure/ms-table/useTable'; import MsSelect from '@/components/business/ms-select'; + import { workApiChangeList } from '@/api/modules/workbench'; import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; @@ -85,8 +96,8 @@ }, { title: 'project.commonScript.apiName', - slotName: 'apiName', - dataIndex: 'apiName', + slotName: 'name', + dataIndex: 'name', width: 200, }, { @@ -98,14 +109,14 @@ }, { title: 'workbench.homePage.associationCASE', - slotName: 'case', - dataIndex: 'case', + slotName: 'caseTotal', + dataIndex: 'caseTotal', width: 200, }, { title: 'workbench.homePage.associatedScene', - slotName: 'associatedScene', - dataIndex: 'associatedScene', + slotName: 'scenarioTotal', + dataIndex: 'scenarioTotal', showDrag: true, width: 100, }, @@ -122,32 +133,43 @@ }, ]; const { propsRes, propsEvent, loadList, setLoadListParams } = useTable( - undefined, + workApiChangeList, { columns, scroll: { x: '100%' }, selectable: false, heightUsed: 272, showSelectAll: false, + validatePermission: true, }, (item) => ({ ...item, updateTime: dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss'), }) ); - function initData() { - const { startTime, endTime, dayNumber } = timeForm.value; - setLoadListParams({ - current: 1, - pageSize: 5, - startTime: dayNumber ? null : startTime, - endTime: dayNumber ? null : endTime, - dayNumber: dayNumber ?? null, - projectIds: innerProjectIds.value, - organizationId: appStore.currentOrgId, - handleUsers: [], - }); - loadList(); + const isNoPermission = ref(false); + async function initData() { + try { + const { startTime, endTime, dayNumber } = timeForm.value; + setLoadListParams({ + startTime: dayNumber ? null : startTime, + endTime: dayNumber ? null : endTime, + dayNumber: dayNumber ?? null, + projectIds: innerProjectIds.value, + organizationId: appStore.currentOrgId, + handleUsers: [], + }); + await loadList(); + isNoPermission.value = false; + } catch (error) { + isNoPermission.value = error === 'no_project_permission'; + // eslint-disable-next-line no-console + console.log(error); + } + } + + function changeProject() { + initData(); } onMounted(() => { @@ -160,7 +182,6 @@ if (val) { const [newProjectId] = val; projectId.value = newProjectId; - initData(); } } ); @@ -170,7 +191,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initData(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/apiCount.vue b/frontend/src/views/workbench/homePage/components/apiCount.vue index 2b49912dc7..b7e07497b8 100644 --- a/frontend/src/views/workbench/homePage/components/apiCount.vue +++ b/frontend/src/views/workbench/homePage/components/apiCount.vue @@ -12,6 +12,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > @@ -24,6 +25,7 @@ :tooltip-text="tabItem.tooltip" :options="tabItem.options" :size="60" + :has-permission="hasPermission" :value-list="tabItem.valueList" /> @@ -51,14 +53,9 @@ import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; - import type { - PassRateDataType, - SelectedCardItem, - StatusStatisticsMapType, - TimeFormParams, - } from '@/models/workbench/homePage'; + import type { PassRateDataType, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; - import { commonRatePieOptions, handlePieData } from '../utils'; + import { handlePieData, handleUpdateTabPie } from '../utils'; const props = defineProps<{ item: SelectedCardItem; @@ -82,55 +79,20 @@ }) ); - const options = ref(cloneDeep(commonRatePieOptions)); + const options = ref({}); // TODO 假数据 const detail = ref({ - statusStatisticsMap: { - cover: [ - { name: '覆盖率', count: 10 }, - { name: '已覆盖', count: 2 }, - { name: '未覆盖', count: 1 }, - ], - success: [ - { name: '覆盖率', count: 10 }, - { name: '已覆盖', count: 2 }, - { name: '未覆盖', count: 1 }, - ], - }, - statusPercentList: [ - { status: 'HTTP', count: 1, percentValue: '10%' }, - { status: 'TCP', count: 3, percentValue: '0%' }, - { status: 'BBB', count: 6, percentValue: '0%' }, - ], + statusStatisticsMap: null, + statusPercentList: null, + errorCode: 109001, }); - const coverValueList = ref([ - { - label: t('workbench.homePage.covered'), - value: 10000, - }, - { - label: t('workbench.homePage.notCover'), - value: 2000, - }, - ]); - const passValueList = ref([ - { - label: t('common.completed'), - value: 10000, - }, - { - label: t('common.inProgress'), - value: 2000, - }, - { - label: t('workbench.homePage.unFinish'), - value: 2000, - }, - ]); - const coverOptions = ref>(cloneDeep(options.value)); - const completeOptions = ref>(cloneDeep(options.value)); + const coverValueList = ref<{ value: string | number; label: string; name: string }[]>([]); + + const passValueList = ref<{ value: string | number; label: string; name: string }[]>([]); + const coverOptions = ref>({}); + const completeOptions = ref>({}); const apiCountTabList = computed(() => { return [ { @@ -152,34 +114,7 @@ const apiCountOptions = ref({}); - function handlePassRatePercent(data: { name: string; count: number }[]) { - return data.slice(1).map((item) => { - return { - value: item.count, - label: item.name, - name: item.name, - }; - }); - } - - function handleRatePieData(statusStatisticsMap: StatusStatisticsMapType) { - const { cover, success } = statusStatisticsMap; - coverValueList.value = handlePassRatePercent(cover); - passValueList.value = handlePassRatePercent(success); - - coverOptions.value.series.data = handlePassRatePercent(cover); - completeOptions.value.series.data = handlePassRatePercent(success); - - coverOptions.value.title.text = cover[0].name ?? ''; - coverOptions.value.title.subtext = `${cover[0].count ?? 0}%`; - - completeOptions.value.title.text = success[0].name ?? ''; - completeOptions.value.title.subtext = `${success[0].count ?? 0}%`; - - coverOptions.value.series.color = ['#00C261', '#D4D4D8']; - completeOptions.value.series.color = ['#00C261', '#ED0303']; - } - + const hasPermission = ref(false); function initApiCount() { try { const { startTime, endTime, dayNumber } = timeForm.value; @@ -193,14 +128,38 @@ organizationId: appStore.currentOrgId, handleUsers: [], }; - const { statusStatisticsMap, statusPercentList } = detail.value; - apiCountOptions.value = handlePieData(props.item.key, statusPercentList); - handleRatePieData(statusStatisticsMap); + const { statusStatisticsMap, statusPercentList, errorCode } = detail.value; + + hasPermission.value = errorCode !== 109001; + + apiCountOptions.value = handlePieData(props.item.key, hasPermission.value, statusPercentList); + + // 覆盖率 + 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?.cover || [], + hasPermission.value, + `${props.item.key}-complete` + ); + passValueList.value = completedList; + completeOptions.value = comOptions; } catch (error) { + // eslint-disable-next-line no-console console.log(error); } } + function changeProject() { + initApiCount(); + } + onMounted(() => { initApiCount(); }); @@ -211,7 +170,6 @@ if (val) { const [newProjectId] = val; projectId.value = newProjectId; - initApiCount(); } } ); @@ -221,7 +179,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initApiCount(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/caseCount.vue b/frontend/src/views/workbench/homePage/components/caseCount.vue index 59c73a5b40..4d878962ce 100644 --- a/frontend/src/views/workbench/homePage/components/caseCount.vue +++ b/frontend/src/views/workbench/homePage/components/caseCount.vue @@ -12,6 +12,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > @@ -25,6 +26,7 @@ :tooltip-text="tabItem.tooltip" :size="60" :value-list="tabItem.valueList" + :has-permission="hasPermission" /> @@ -41,7 +43,6 @@ * @desc 用例数量 */ import { ref } from 'vue'; - import { cloneDeep } from 'lodash-es'; import MsChart from '@/components/pure/chart/index.vue'; import MsSelect from '@/components/business/ms-select'; @@ -53,9 +54,8 @@ import useAppStore from '@/store/modules/app'; import type { SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; - import { StatusStatisticsMapType } from '@/models/workbench/homePage'; - import { commonRatePieOptions, handlePieData } from '../utils'; + import { handlePieData, handleUpdateTabPie } from '../utils'; const appStore = useAppStore(); const { t } = useI18n(); @@ -79,42 +79,12 @@ }) ); - const options = ref(cloneDeep(commonRatePieOptions)); + const reviewValueList = ref<{ value: number | string; label: string; name: string }[]>([]); - function handlePassRatePercent(data: { name: string; count: number }[]) { - return data.slice(1).map((item) => { - return { - value: item.count, - label: item.name, - name: item.name, - }; - }); - } + const passValueList = ref<{ value: number | string; label: string; name: string }[]>([]); - const reviewValueList = ref([ - { - label: t('workbench.homePage.reviewed'), - value: 10000, - }, - { - label: t('workbench.homePage.unReviewed'), - value: 2000, - }, - ]); - - const passValueList = ref([ - { - label: t('workbench.homePage.havePassed'), - value: 10000, - }, - { - label: t('workbench.homePage.notPass'), - value: 2000, - }, - ]); - - const reviewOptions = ref>(cloneDeep(options.value)); - const passOptions = ref>(cloneDeep(options.value)); + const reviewOptions = ref>({}); + const passOptions = ref>({}); const caseCountTabList = computed(() => { return [ { @@ -134,24 +104,10 @@ ]; }); - // 处理X率饼图数据 - function handleRatePieData(statusStatisticsMap: StatusStatisticsMapType) { - const { review, pass } = statusStatisticsMap; - reviewValueList.value = handlePassRatePercent(review); - passValueList.value = handlePassRatePercent(pass); - - reviewOptions.value.series.data = handlePassRatePercent(review); - passOptions.value.series.data = handlePassRatePercent(pass); - - reviewOptions.value.title.text = review[0].name ?? ''; - reviewOptions.value.title.subtext = `${review[0].count ?? 0}%`; - passOptions.value.title.text = pass[0].name ?? ''; - passOptions.value.title.subtext = `${pass[0].count ?? 0}%`; - reviewOptions.value.series.color = ['#00C261', '#D4D4D8']; - passOptions.value.series.color = ['#00C261', '#ED0303']; - } + const hasPermission = ref(false); const caseCountOptions = ref>({}); + async function initCaseCount() { try { const { startTime, endTime, dayNumber } = timeForm.value; @@ -167,14 +123,35 @@ }; const detail = await workCaseCountDetail(params); const { statusStatisticsMap, statusPercentList } = detail; - caseCountOptions.value = handlePieData(props.item.key, statusPercentList); - handleRatePieData(statusStatisticsMap); + hasPermission.value = detail.errorCode !== 109001; + caseCountOptions.value = handlePieData(props.item.key, hasPermission.value, statusPercentList); + + const { valueList: reviewValue, options: reviewedOptions } = handleUpdateTabPie( + statusStatisticsMap?.review || [], + hasPermission.value, + `${props.item.key}-review` + ); + + reviewOptions.value = reviewedOptions; + reviewValueList.value = reviewValue; + + const { valueList: passList, options: passOpt } = handleUpdateTabPie( + statusStatisticsMap?.pass || [], + hasPermission.value, + `${props.item.key}-pass` + ); + passOptions.value = passOpt; + passValueList.value = passList; } catch (error) { // eslint-disable-next-line no-console console.log(error); } } + function changeProject() { + initCaseCount(); + } + onMounted(() => { initCaseCount(); }); @@ -185,7 +162,6 @@ if (val) { const [newProjectId] = val; projectId.value = newProjectId; - initCaseCount(); } } ); @@ -195,7 +171,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initCaseCount(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/caseReviewedCount.vue b/frontend/src/views/workbench/homePage/components/caseReviewedCount.vue index 640cbafa99..ee48d62acd 100644 --- a/frontend/src/views/workbench/homePage/components/caseReviewedCount.vue +++ b/frontend/src/views/workbench/homePage/components/caseReviewedCount.vue @@ -12,6 +12,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > @@ -24,6 +25,7 @@ tooltip-text="workbench.homePage.caseReviewCoverRateTooltip" :size="60" :value-list="coverValueList" + :has-permission="hasPermission" /> @@ -39,18 +41,18 @@ * @desc 用例评审数量 */ import { ref } from 'vue'; - import { cloneDeep } from 'lodash-es'; import MsChart from '@/components/pure/chart/index.vue'; import MsSelect from '@/components/business/ms-select'; import PassRatePie from './passRatePie.vue'; + import { workCaseReviewDetail } from '@/api/modules/workbench'; import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; import type { PassRateDataType, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; - import { commonRatePieOptions, handlePieData } from '../utils'; + import { handlePieData, handleUpdateTabPie } from '../utils'; const { t } = useI18n(); const appStore = useAppStore(); @@ -74,54 +76,58 @@ }) ); - const options = ref>(cloneDeep(commonRatePieOptions)); + const options = ref>({}); - const coverValueList = ref([ + const coverValueList = ref<{ value: number | string; label: string; name: string }[]>([ { label: t('workbench.homePage.covered'), - value: 10000, + value: '-', + name: '', }, { label: t('workbench.homePage.notCover'), - value: 2000, + value: '-', + name: '', }, ]); - // TODO 假数据 - const detail = ref({ - statusStatisticsMap: { - cover: [ - { name: '覆盖率', count: 10 }, - { name: '已覆盖', count: 2 }, - { name: '未覆盖', count: 1 }, - ], - }, - statusPercentList: [ - { status: '未开始', count: 1, percentValue: '10%' }, - { status: '进行中', count: 3, percentValue: '0%' }, - { status: '已完成', count: 6, percentValue: '0%' }, - { status: '已归档', count: 7, percentValue: '0%' }, - ], - }); - const caseReviewCountOptions = ref>({}); - function initApiCount() { - const { statusStatisticsMap, statusPercentList } = detail.value; - caseReviewCountOptions.value = handlePieData(props.item.key, statusPercentList); - const { cover } = statusStatisticsMap; - coverValueList.value = cover.slice(1).map((item) => { - return { - value: item.count, - label: item.name, - name: item.name, - }; - }); + const hasPermission = ref(false); + async function initApiCount() { + const { startTime, endTime, dayNumber } = timeForm.value; + const params = { + current: 1, + pageSize: 5, + startTime: dayNumber ? null : startTime, + endTime: dayNumber ? null : endTime, + dayNumber: dayNumber ?? null, + projectIds: innerProjectIds.value, + organizationId: appStore.currentOrgId, + handleUsers: [], + }; + try { + const detail: PassRateDataType = await workCaseReviewDetail(params); - options.value.series.data = coverValueList.value; - options.value.title.text = cover[0].name ?? ''; - options.value.title.subtext = `${cover[0].count ?? 0}%`; - options.value.series.color = ['#00C261', '#D4D4D8']; + hasPermission.value = detail.errorCode !== 109001; + + const { statusStatisticsMap, statusPercentList } = detail; + caseReviewCountOptions.value = handlePieData(props.item.key, hasPermission.value, statusPercentList); + const { options: coverOptions, valueList } = handleUpdateTabPie( + statusStatisticsMap?.cover || [], + hasPermission.value, + `${props.item.key}-cover` + ); + coverValueList.value = valueList; + options.value = coverOptions; + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + } + + function changeProject() { + initApiCount(); } onMounted(() => { @@ -134,7 +140,6 @@ if (val) { const [newProjectId] = val; projectId.value = newProjectId; - initApiCount(); } } ); @@ -144,7 +149,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initApiCount(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/defectCount.vue b/frontend/src/views/workbench/homePage/components/defectCount.vue index ec27e2b0b2..b5ebddb8b4 100644 --- a/frontend/src/views/workbench/homePage/components/defectCount.vue +++ b/frontend/src/views/workbench/homePage/components/defectCount.vue @@ -12,6 +12,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > @@ -19,7 +20,13 @@
- +
@@ -34,7 +41,6 @@ * @desc 用于缺陷数量,待我处理的缺陷数量组件 */ import { ref } from 'vue'; - import { cloneDeep } from 'lodash-es'; import MsChart from '@/components/pure/chart/index.vue'; import MsSelect from '@/components/business/ms-select'; @@ -43,15 +49,10 @@ import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; - import type { - PassRateDataType, - SelectedCardItem, - StatusStatisticsMapType, - TimeFormParams, - } from '@/models/workbench/homePage'; + import type { PassRateDataType, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; import { WorkCardEnum } from '@/enums/workbenchEnum'; - import { commonRatePieOptions, handlePieData } from '../utils'; + import { handlePieData, handleUpdateTabPie } from '../utils'; const appStore = useAppStore(); @@ -67,21 +68,7 @@ const projectId = ref(innerProjectIds.value[0]); - const valueList = ref< - { - label: string; - value: number; - }[] - >([ - { - label: t('workbench.homePage.defectTotal'), - value: 10000, - }, - { - label: t('workbench.homePage.legacyDefectsNumber'), - value: 2000, - }, - ]); + const legacyValueList = ref<{ value: number | string; label: string; name: string }[]>([]); const timeForm = inject>( 'timeForm', @@ -92,7 +79,7 @@ }) ); - const legacyOptions = ref>(cloneDeep(commonRatePieOptions)); + const legacyOptions = ref>({}); // TODO 假数据 const detail = ref({ @@ -108,26 +95,12 @@ { status: 'BBB', count: 3, percentValue: '0%' }, { status: 'CCC', count: 6, percentValue: '0%' }, ], + errorCode: 0, }); - const countOptions = ref({}); - - function handleRatePieData(statusStatisticsMap: StatusStatisticsMapType) { - const { legacy } = statusStatisticsMap; - valueList.value = legacy.slice(1).map((item) => { - return { - value: item.count, - label: item.name, - name: item.name, - }; - }); - legacyOptions.value.series.data = valueList.value; - - legacyOptions.value.title.text = legacy[0].name ?? ''; - legacyOptions.value.title.subtext = `${legacy[0].count ?? 0}%`; - legacyOptions.value.series.color = ['#D4D4D8', '#00C261']; - } + const countOptions = ref>({}); + const hasPermission = ref(false); async function initCount() { try { const { startTime, endTime, dayNumber } = timeForm.value; @@ -141,9 +114,18 @@ organizationId: appStore.currentOrgId, handleUsers: [], }; - const { statusStatisticsMap, statusPercentList } = detail.value; - countOptions.value = handlePieData(props.item.key, statusPercentList); - handleRatePieData(statusStatisticsMap); + const { statusStatisticsMap, statusPercentList, errorCode } = detail.value; + hasPermission.value = errorCode !== 109001; + + countOptions.value = handlePieData(props.item.key, hasPermission.value, statusPercentList); + + const { options, valueList } = handleUpdateTabPie( + statusStatisticsMap?.legacy || [], + hasPermission.value, + `${props.item.key}-legacy` + ); + legacyValueList.value = valueList; + legacyOptions.value = options; } catch (error) { // eslint-disable-next-line no-console console.log(error); @@ -154,6 +136,10 @@ return props.item.key === WorkCardEnum.PLAN_LEGACY_BUG ? 'workbench.homePage.planCaseCountLegacyRateTooltip' : ''; }); + function changeProject() { + initCount(); + } + onMounted(() => { initCount(); }); @@ -164,7 +150,6 @@ if (val) { const [newProjectId] = val; projectId.value = newProjectId; - initCount(); } } ); @@ -174,7 +159,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initCount(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/defectMemberBar.vue b/frontend/src/views/workbench/homePage/components/defectMemberBar.vue index aa97ba5b0c..f7173aa0f8 100644 --- a/frontend/src/views/workbench/homePage/components/defectMemberBar.vue +++ b/frontend/src/views/workbench/homePage/components/defectMemberBar.vue @@ -6,13 +6,13 @@
@@ -44,12 +45,15 @@ import MsChart from '@/components/pure/chart/index.vue'; import MsSelect from '@/components/business/ms-select'; + import { getProjectOptions } from '@/api/modules/project-management/projectMember'; + import { workBugHandlerDetail } from '@/api/modules/workbench'; import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; + import { characterLimit } from '@/utils'; - import type { SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; + import type { OverViewOfProject, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; - import { commonColorConfig, getCommonBarOptions } from '../utils'; + import { commonColorConfig, getCommonBarOptions, handleNoDataDisplay } from '../utils'; import type { SelectOptionData } from '@arco-design/web-vue'; const { t } = useI18n(); @@ -58,19 +62,18 @@ item: SelectedCardItem; }>(); - const memberIds = ref(''); const innerProjectIds = defineModel('projectIds', { required: true, }); - const projectId = computed({ - get: () => { - const [newProject] = innerProjectIds.value; - return newProject; - }, - set: (val) => val, + const projectId = ref(innerProjectIds.value[0]); + + const innerHandleUsers = defineModel('handleUsers', { + required: true, }); + const memberIds = ref(innerHandleUsers.value); + const timeForm = inject>( 'timeForm', ref({ @@ -83,128 +86,91 @@ const memberOptions = ref([]); const options = ref>({}); - const members = computed(() => ['张三', '李四', '王五', '小王']); - const hasRoom = computed(() => members.value.length >= 7); - const seriesData = ref[]>([ - { - name: '新创建', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [400, 200, 400, 200, 400, 200], - }, - { - name: '激活', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '处理中', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '已关闭', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '新创建1', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [400, 200, 400, 200, 400, 200], - }, - { - name: '激活1', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '处理中1', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '已关闭1', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '已关闭2', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - // borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - { - name: '已关闭3', - type: 'bar', - barWidth: 12, - stack: 'bug', - itemStyle: { - borderRadius: [2, 2, 0, 0], - }, - data: [90, 160, 90, 160, 90, 160], - }, - ]); + const defectStatusColor = ['#811FA3', '#FFA200', '#3370FF', '#F24F4F']; - function getDefectMemberDetail() { - options.value = getCommonBarOptions(hasRoom.value, [...defectStatusColor, ...commonColorConfig]); - options.value.xAxis.data = members.value; - options.value.series = seriesData.value; + function handleData(detail: OverViewOfProject) { + options.value = getCommonBarOptions(detail.xaxis.length >= 7, [...defectStatusColor, ...commonColorConfig]); + const { invisible, text } = handleNoDataDisplay(detail.xaxis, detail.projectCountList); + options.value.graphic.invisible = invisible; + options.value.graphic.style.text = text; + options.value.xAxis.data = detail.xaxis.map((e) => characterLimit(e, 10)); + options.value.series = detail.projectCountList.map((item) => { + return { + name: item.name, + type: 'bar', + stack: 'bugMember', + barWidth: 12, + data: item.count, + itemStyle: { + borderRadius: [2, 2, 0, 0], + }, + }; + }); } - onMounted(() => { + async function getDefectMemberDetail() { + try { + const { startTime, endTime, dayNumber } = timeForm.value; + const detail = await workBugHandlerDetail({ + current: 1, + pageSize: 5, + startTime: dayNumber ? null : startTime, + endTime: dayNumber ? null : endTime, + dayNumber: dayNumber ?? null, + projectIds: innerProjectIds.value, + organizationId: appStore.currentOrgId, + handleUsers: innerHandleUsers.value, + }); + handleData(detail); + } catch (error) { + console.log(error); + } + } + + async function getMemberOptions() { + const [newProjectId] = innerProjectIds.value; + const res = await getProjectOptions(newProjectId); + memberOptions.value = res.map((e: any) => ({ + label: e.name, + value: e.id, + })); + } + + function changeProject() { + memberIds.value = []; + getMemberOptions(); getDefectMemberDetail(); - }); + } + + function changeMember() { + getDefectMemberDetail(); + } + + watch( + () => innerProjectIds.value, + (val) => { + if (val) { + const [newProjectId] = val; + projectId.value = newProjectId; + } + } + ); watch( () => projectId.value, (val) => { if (val) { innerProjectIds.value = [val]; - getDefectMemberDetail(); + } + } + ); + + watch( + () => memberIds.value, + (val) => { + if (val) { + innerHandleUsers.value = val; } } ); @@ -220,6 +186,11 @@ deep: true, } ); + + onMounted(() => { + getMemberOptions(); + getDefectMemberDetail(); + }); diff --git a/frontend/src/views/workbench/homePage/components/overview.vue b/frontend/src/views/workbench/homePage/components/overview.vue index 0b4dc27630..dfb582902e 100644 --- a/frontend/src/views/workbench/homePage/components/overview.vue +++ b/frontend/src/views/workbench/homePage/components/overview.vue @@ -42,6 +42,7 @@ import TabCard from './tabCard.vue'; import { workMyCreatedDetail, workProOverviewDetail } from '@/api/modules/workbench'; + import { contentTabList } from '@/config/workbench'; import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; @@ -53,7 +54,7 @@ } from '@/models/workbench/homePage'; import { WorkCardEnum, WorkOverviewEnum } from '@/enums/workbenchEnum'; - import { commonColorConfig, contentTabList, getCommonBarOptions, handleNoDataDisplay } from '../utils'; + import { commonColorConfig, getCommonBarOptions, handleNoDataDisplay } from '../utils'; const { t } = useI18n(); @@ -92,9 +93,10 @@ function handleData(detail: OverViewOfProject) { // 处理模块顺序 const tempAxisData = detail.xaxis.map((xAxisKey) => { - const data = contentTabList.value.find((e) => e.value === xAxisKey); + const data = contentTabList.find((e) => e.value === xAxisKey); return { ...data, + label: t(data?.label || ''), count: detail.caseCountMap[xAxisKey as WorkOverviewEnum], }; }); diff --git a/frontend/src/views/workbench/homePage/components/overviewMember.vue b/frontend/src/views/workbench/homePage/components/overviewMember.vue index 60cb3784de..db05bfda72 100644 --- a/frontend/src/views/workbench/homePage/components/overviewMember.vue +++ b/frontend/src/views/workbench/homePage/components/overviewMember.vue @@ -12,6 +12,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > memberIds.value.length >= 7); - const memberOptions = ref<{ label: string; value: string }[]>([]); + const memberOptions = ref<{ label: string; value: string }[]>([]); const options = ref>({}); function handleData(detail: OverViewOfProject) { - options.value = getCommonBarOptions(hasRoom.value, commonColorConfig); + options.value = getCommonBarOptions(detail.xaxis.length >= 7, commonColorConfig); const { invisible, text } = handleNoDataDisplay(detail.xaxis, detail.projectCountList); options.value.graphic.invisible = invisible; options.value.graphic.style.text = text; options.value.xAxis.data = detail.xaxis.map((e) => characterLimit(e, 10)); options.value.series = detail.projectCountList.map((item, index) => { return { - name: contentTabList.value[index].label, + name: t(contentTabList[index].label), type: 'bar', stack: 'member', barWidth: 12, @@ -122,6 +123,7 @@ const detail = await workMemberViewDetail(params); handleData(detail); } catch (error) { + // eslint-disable-next-line no-console console.log(error); } } @@ -135,6 +137,11 @@ })); } + function changeProject() { + getMemberOptions(); + initOverViewMemberDetail(); + } + watch( () => innerProjectIds.value, (val) => { @@ -142,8 +149,6 @@ const [newProjectId] = val; projectId.value = newProjectId; memberIds.value = []; - getMemberOptions(); - initOverViewMemberDetail(); } } ); @@ -181,6 +186,7 @@ onMounted(() => { getMemberOptions(); + initOverViewMemberDetail(); }); diff --git a/frontend/src/views/workbench/homePage/components/passRatePie.vue b/frontend/src/views/workbench/homePage/components/passRatePie.vue index 892705c86e..f602133007 100644 --- a/frontend/src/views/workbench/homePage/components/passRatePie.vue +++ b/frontend/src/views/workbench/homePage/components/passRatePie.vue @@ -14,7 +14,7 @@
{{ item.label }}
-
{{ addCommasToNumber(item.value) }}
+
{{ hasPermission ? addCommasToNumber(item.value as number) : '-' }}
@@ -34,9 +34,10 @@ options: Record; size: number; tooltipText?: string; + hasPermission: boolean; valueList: { label: string; - value: number; + value: number | string; }[]; }>(); diff --git a/frontend/src/views/workbench/homePage/components/ratioPie.vue b/frontend/src/views/workbench/homePage/components/ratioPie.vue index b6a5089d3a..f03ba183e8 100644 --- a/frontend/src/views/workbench/homePage/components/ratioPie.vue +++ b/frontend/src/views/workbench/homePage/components/ratioPie.vue @@ -83,7 +83,7 @@ name: '', type: 'pie', padAngle: 1, - radius: ['46%', '56%'], + radius: ['50%', '58%'], center: ['50%', '32%'], color: [], avoidLabelOverlap: false, diff --git a/frontend/src/views/workbench/homePage/components/relatedCaseCount.vue b/frontend/src/views/workbench/homePage/components/relatedCaseCount.vue index 9e511177f2..23bf08386e 100644 --- a/frontend/src/views/workbench/homePage/components/relatedCaseCount.vue +++ b/frontend/src/views/workbench/homePage/components/relatedCaseCount.vue @@ -11,6 +11,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" > @@ -19,7 +20,8 @@
>(cloneDeep(commonRatePieOptions)); + const relatedOptions = ref>({}); + const hasPermission = ref(false); - const coverRateValueList = ref<{ value: number; label: string; name: string }[]>([]); + const coverRateValueList = ref<{ value: number | string; label: string; name: string }[]>([ + { + label: t('workbench.homePage.covered'), + value: '-', + name: '', + }, + { + label: t('workbench.homePage.notCover'), + value: '-', + name: '', + }, + ]); async function getRelatedCaseCount() { try { const { startTime, endTime, dayNumber } = timeForm.value; - const detail = await workAssociateCaseDetail({ + const detail: PassRateDataType = await workAssociateCaseDetail({ current: 1, pageSize: 5, startTime: dayNumber ? null : startTime, @@ -88,24 +101,28 @@ organizationId: appStore.currentOrgId, handleUsers: [], }); - const { cover } = detail.statusStatisticsMap; - coverRateValueList.value = cover.slice(1).map((item) => { - return { - value: item.count, - label: item.name, - name: item.name, - }; - }); - options.value.series.data = coverRateValueList.value; - options.value.title.text = cover[0].name ?? ''; - options.value.title.subtext = `${cover[0].count ?? 0}%`; - options.value.series.color = ['#00C261', '#D4D4D8']; + + hasPermission.value = detail.errorCode !== 109001; + + const { statusStatisticsMap } = detail; + + const { options, valueList } = handleUpdateTabPie( + statusStatisticsMap?.cover || [], + hasPermission.value, + `${props.item.key}-cover` + ); + relatedOptions.value = options; + coverRateValueList.value = valueList; } catch (error) { // eslint-disable-next-line no-console console.log(error); } } + function changeProject() { + getRelatedCaseCount(); + } + onMounted(() => { getRelatedCaseCount(); }); @@ -125,7 +142,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - getRelatedCaseCount(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/tabCard.vue b/frontend/src/views/workbench/homePage/components/tabCard.vue index 5114510a71..3a014097ca 100644 --- a/frontend/src/views/workbench/homePage/components/tabCard.vue +++ b/frontend/src/views/workbench/homePage/components/tabCard.vue @@ -31,7 +31,7 @@ const props = defineProps<{ contentTabList: { - label: string | number; + label: string; value: string | number; count?: number; icon?: string; diff --git a/frontend/src/views/workbench/homePage/components/testPlanCount.vue b/frontend/src/views/workbench/homePage/components/testPlanCount.vue index 2491f33af7..a61f870f40 100644 --- a/frontend/src/views/workbench/homePage/components/testPlanCount.vue +++ b/frontend/src/views/workbench/homePage/components/testPlanCount.vue @@ -14,6 +14,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" >
@@ -22,7 +23,12 @@ @@ -38,7 +44,6 @@ * @desc 测试计划数量 */ import { ref } from 'vue'; - import { cloneDeep } from 'lodash-es'; import MsChart from '@/components/pure/chart/index.vue'; import MsSelect from '@/components/business/ms-select'; @@ -48,14 +53,9 @@ import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; - import type { - PassRateDataType, - SelectedCardItem, - StatusStatisticsMapType, - TimeFormParams, - } from '@/models/workbench/homePage'; + import type { PassRateDataType, SelectedCardItem, TimeFormParams } from '@/models/workbench/homePage'; - import { commonRatePieOptions, handlePieData } from '../utils'; + import { handlePieData, handleUpdateTabPie } from '../utils'; const props = defineProps<{ item: SelectedCardItem; @@ -103,51 +103,20 @@ { status: 'TCP', count: 3, percentValue: '0%' }, { status: 'BBB', count: 6, percentValue: '0%' }, ], + errorCode: 109001, }); - const options = ref(cloneDeep(commonRatePieOptions)); - const executionOptions = ref>(cloneDeep(options.value)); - const passOptions = ref>(cloneDeep(options.value)); - const completeOptions = ref>(cloneDeep(options.value)); + const executionOptions = ref>({}); + const passOptions = ref>({}); + const completeOptions = ref>({}); // 执行率 - const executionValueList = ref([ - { - label: t('common.unExecute'), - value: 10000, - }, - { - label: t('common.executed'), - value: 2000, - }, - ]); + const executionValueList = ref<{ value: number | string; label: string; name: string }[]>([]); // 通过率 - const passValueList = ref([ - { - label: t('workbench.homePage.havePassed'), - value: 10000, - }, - { - label: t('workbench.homePage.notPass'), - value: 2000, - }, - ]); + const passValueList = ref<{ value: number | string; label: string; name: string }[]>([]); // 完成率 - const completeValueList = ref([ - { - label: t('common.completed'), - value: 10000, - }, - { - label: t('common.inProgress'), - value: 2000, - }, - { - label: t('workbench.homePage.unFinish'), - value: 2000, - }, - ]); + const completeValueList = ref<{ value: number | string; label: string; name: string }[]>([]); const testPlanTabList = computed(() => { return [ @@ -172,41 +141,8 @@ ]; }); - function handlePassRatePercent(data: { name: string; count: number }[]) { - return data.slice(1).map((item) => { - return { - value: item.count, - label: item.name, - name: item.name, - }; - }); - } - - function handleRatePieData(statusStatisticsMap: StatusStatisticsMapType) { - const { execute, pass, complete } = statusStatisticsMap; - executionValueList.value = handlePassRatePercent(execute); - passValueList.value = handlePassRatePercent(pass); - completeValueList.value = handlePassRatePercent(complete); - - executionOptions.value.series.data = handlePassRatePercent(execute); - passOptions.value.series.data = handlePassRatePercent(pass); - completeOptions.value.series.data = handlePassRatePercent(complete); - - executionOptions.value.title.text = execute[0].name ?? ''; - executionOptions.value.title.subtext = `${execute[0].count ?? 0}%`; - - passOptions.value.title.text = pass[0].name ?? ''; - passOptions.value.title.subtext = `${pass[0].count ?? 0}%`; - - completeOptions.value.title.text = complete[0].name ?? ''; - completeOptions.value.title.subtext = `${complete[0].count ?? 0}%`; - - executionOptions.value.series.color = ['#D4D4D8', '#00C261']; - passOptions.value.series.color = ['#D4D4D8', '#00C261']; - completeOptions.value.series.color = ['#00C261', '#3370FF', '#D4D4D8']; - } - const testPlanCountOptions = ref({}); + const hasPermission = ref(false); async function initTestPlanCount() { try { const { startTime, endTime, dayNumber } = timeForm.value; @@ -220,18 +156,47 @@ organizationId: appStore.currentOrgId, handleUsers: [], }; - const { statusStatisticsMap, statusPercentList } = detail.value; + const { statusStatisticsMap, statusPercentList, errorCode } = detail.value; - testPlanCountOptions.value = handlePieData(props.item.key, statusPercentList); - handleRatePieData(statusStatisticsMap); + hasPermission.value = errorCode !== 109001; + testPlanCountOptions.value = handlePieData(props.item.key, hasPermission.value, statusPercentList); + + // 执行率 + const { options: executedOptions, valueList: executedList } = handleUpdateTabPie( + statusStatisticsMap?.execute || [], + hasPermission.value, + `${props.item.key}-execute` + ); + + // 通过率 + const { options: passedOptions, valueList: passList } = handleUpdateTabPie( + statusStatisticsMap?.pass || [], + hasPermission.value, + `${props.item.key}-pass` + ); + + // 完成率 + const { options: comOptions, valueList: completeList } = handleUpdateTabPie( + statusStatisticsMap?.complete || [], + hasPermission.value, + `${props.item.key}-complete` + ); + + executionValueList.value = executedList; + passValueList.value = passList; + completeValueList.value = completeList; + + executionOptions.value = executedOptions; + passOptions.value = passedOptions; + completeOptions.value = comOptions; } catch (error) { console.log(error); } } - onMounted(() => { + function changeProject() { initTestPlanCount(); - }); + } onMounted(() => { initTestPlanCount(); @@ -242,7 +207,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initTestPlanCount(); } } ); @@ -253,7 +217,6 @@ if (val) { const [newProjectId] = val; projectId.value = newProjectId; - initTestPlanCount(); } } ); diff --git a/frontend/src/views/workbench/homePage/components/waitReviewList.vue b/frontend/src/views/workbench/homePage/components/waitReviewList.vue index d31306d6ed..17c88e3117 100644 --- a/frontend/src/views/workbench/homePage/components/waitReviewList.vue +++ b/frontend/src/views/workbench/homePage/components/waitReviewList.vue @@ -12,6 +12,7 @@ :search-keys="['name']" class="!w-[240px]" :prefix="t('workbench.homePage.project')" + @change="changeProject" >
@@ -57,6 +58,15 @@ }} + @@ -74,6 +84,7 @@ import MsSelect from '@/components/business/ms-select'; import passRateLine from '@/views/case-management/caseReview/components/passRateLine.vue'; + import { workReviewList } from '@/api/modules/workbench'; import { useI18n } from '@/hooks/useI18n'; import useAppStore from '@/store/modules/app'; @@ -130,7 +141,7 @@ }, ]; - const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(undefined, { + const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(workReviewList, { columns, scroll: { x: '100%' }, selectable: false, @@ -138,19 +149,28 @@ showSelectAll: false, }); - function initData() { - const { startTime, endTime, dayNumber } = timeForm.value; - setLoadListParams({ - current: 1, - pageSize: 5, - startTime: dayNumber ? null : startTime, - endTime: dayNumber ? null : endTime, - dayNumber: dayNumber ?? null, - projectIds: innerProjectIds.value, - organizationId: appStore.currentOrgId, - handleUsers: [], - }); - loadList(); + const isNoPermission = ref(false); + + async function initData() { + try { + const { startTime, endTime, dayNumber } = timeForm.value; + setLoadListParams({ + startTime: dayNumber ? null : startTime, + endTime: dayNumber ? null : endTime, + dayNumber: dayNumber ?? null, + projectIds: innerProjectIds.value, + organizationId: appStore.currentOrgId, + handleUsers: [], + }); + await loadList(); + } catch (error) { + isNoPermission.value = error === 'no_project_permission'; + // eslint-disable-next-line no-console + } + } + + function changeProject() { + initData(); } onMounted(() => { @@ -162,7 +182,6 @@ (val) => { if (val) { innerProjectIds.value = [val]; - initData(); } } ); diff --git a/frontend/src/views/workbench/homePage/index.vue b/frontend/src/views/workbench/homePage/index.vue index ce0923f673..abf874ba13 100644 --- a/frontend/src/views/workbench/homePage/index.vue +++ b/frontend/src/views/workbench/homePage/index.vue @@ -93,6 +93,7 @@ = { [WorkCardEnum.CASE_COUNT]: ['#ED0303', '#FFA200', '#3370FF', '#D4D4D8'], [WorkCardEnum.ASSOCIATE_CASE_COUNT]: ['#00C261', '#3370FF'], - [WorkCardEnum.REVIEW_CASE_COUNT]: ['#9441B1', '#3370FF', '#00C261', '#D4D4D8'], + [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'], @@ -100,13 +102,14 @@ export function getCommonBarOptions(hasRoom: boolean, color: string[]): Record([ - { - label: t('workbench.homePage.functionalUseCase'), - value: WorkOverviewEnum.FUNCTIONAL, - icon: WorkOverviewIconEnum.FUNCTIONAL, - color: 'rgb(var(--primary-5))', - count: 0, - }, - { - label: t('workbench.homePage.useCaseReview'), - value: WorkOverviewEnum.CASE_REVIEW, - icon: WorkOverviewIconEnum.CASE_REVIEW, - color: 'rgb(var(--success-6))', - count: 0, - }, - { - label: t('workbench.homePage.interfaceAPI'), - value: WorkOverviewEnum.API, - icon: WorkOverviewIconEnum.API, - color: 'rgb(var(--link-6))', - count: 0, - }, - { - label: t('workbench.homePage.interfaceCASE'), - value: WorkOverviewEnum.API_CASE, - icon: WorkOverviewIconEnum.API_CASE, - color: 'rgb(var(--link-6))', - count: 0, - }, - { - label: t('workbench.homePage.interfaceScenario'), - value: WorkOverviewEnum.API_SCENARIO, - icon: WorkOverviewIconEnum.API_SCENARIO, - color: 'rgb(var(--link-6))', - count: 0, - }, - { - label: t('workbench.homePage.apiPlan'), - value: WorkOverviewEnum.TEST_PLAN, - icon: WorkOverviewIconEnum.TEST_PLAN, - color: 'rgb(var(--link-6))', - count: 0, - }, - { - label: t('workbench.homePage.bugCount'), - value: WorkOverviewEnum.BUG_COUNT, - icon: WorkOverviewIconEnum.BUG_COUNT, - color: 'rgb(var(--danger-6))', - count: 0, - }, -]); - // 下方饼图配置 -export function getPieCharOptions(key: WorkCardEnum) { +export function getPieCharOptions(key: WorkCardEnum, hasPermission: boolean) { return { title: { show: true, @@ -272,6 +224,7 @@ export function getPieCharOptions(key: WorkCardEnum) { tooltip: { ...toolTipConfig, position: 'right', + show: !!hasPermission, }, legend: { width: '100%', @@ -387,6 +340,20 @@ export function getPieCharOptions(key: WorkCardEnum) { }, data: [], }, + graphic: { + type: 'text', + left: 'center', + top: 'middle', + style: { + text: t('workbench.homePage.notHasResPermission'), + fontSize: 14, + fill: '#959598', + backgroundColor: '#F9F9FE', + padding: [6, 16, 6, 16], + borderRadius: 4, + }, + invisible: !!hasPermission, + }, }; } @@ -418,108 +385,21 @@ export function handleNoDataDisplay( }; } -// XX率饼图配置 -export const commonRatePieOptions = { - ...commonConfig, - title: { - show: true, - text: '', - left: 26, - top: '20%', - textStyle: { - fontSize: 12, - fontWeight: 'normal', - color: '#959598', - }, - triggerEvent: true, // 开启鼠标事件 - subtext: '0', - subtextStyle: { - fontSize: 12, - color: '#323233', - fontWeight: 'bold', - align: 'center', - lineHeight: 3, - }, - textAlign: 'center', - tooltip: { - ...toolTipConfig, - position: 'right', - }, - }, - tooltip: { - ...toolTipConfig, - position: 'right', - }, - legend: { - show: false, - }, - series: { - name: '', - type: 'pie', - color: [], - padAngle: 2, - radius: ['85%', '100%'], - center: [30, '50%'], - avoidLabelOverlap: false, - label: { - show: false, - position: 'center', - }, - emphasis: { - scale: false, // 禁用放大效果 - label: { - show: false, - fontSize: 40, - fontWeight: 'bold', - }, - }, - labelLine: { - show: false, - }, - data: [], - }, - // graphic: [ - // { - // type: 'text', - // left: 'center', - // top: '5%', - // style: { - // text: '饼图标题', - // fontSize: 18, - // fontWeight: 'bold', - // fill: '#333', - // cursor: 'pointer' - // }, - // onmouseover (params) { - // // 悬浮到标题上时显示提示信息 - // // chart.dispatchAction({ - // // type: 'showTip', - // // position: [params.event.offsetX, params.event.offsetY], - // // // 配置提示内容 - // // formatter: '这是饼图标题的提示内容' - // // }); - // }, - // onmouseout () { - // // 离开标题时隐藏提示信息 - // // chart.dispatchAction({ - // // type: 'hideTip' - // // }); - // } - // } - // ] -}; - // 统一处理下方饼图数据结构 export function handlePieData( key: WorkCardEnum, - statusPercentList: { - status: string; // 状态 - count: number; - percentValue: string; // 百分比 - }[] + hasPermission: boolean, + statusPercentList: + | { + status: string; // 状态 + count: number; + percentValue: string; // 百分比 + }[] + | null = [] ) { - const options: Record = getPieCharOptions(key); - options.series.data = statusPercentList.map((item) => ({ + const options: Record = getPieCharOptions(key, hasPermission); + const lastStatusPercentList = statusPercentList ?? []; + options.series.data = lastStatusPercentList.map((item) => ({ name: item.status, value: item.count, })); @@ -527,11 +407,17 @@ export function handlePieData( // 计算总数和图例格式 const tempObject: Record = {}; let totalCount = 0; - statusPercentList.forEach((item) => { + lastStatusPercentList.forEach((item) => { tempObject[item.status] = item; totalCount += item.count; }); + // 设置副标题为总数 + options.title.subtext = addCommasToNumber(totalCount); + if (!hasPermission) { + options.title.subtext = '-'; + } + // 设置图例的格式化函数,显示百分比 options.legend.formatter = (name: string) => { return `{a|${tempObject[name].status}} {b|${addCommasToNumber(tempObject[name].count)}} {c|${ @@ -539,8 +425,51 @@ export function handlePieData( }}`; }; - // 设置副标题为总数 - options.title.subtext = addCommasToNumber(totalCount); - return options; } + +// 更新options +export function handleUpdateTabPie( + list: { + name: string; + count: number; + }[], + hasPermission: boolean, // 是否有权限 + key: string +) { + const options: Record = cloneDeep(commonRatePieOptions); + const typeKey = key.split('-')[0]; + const valueKey = key.split('-')[1]; + const countList = list || []; + let lastCountList: { value: number | string; label: string; name: string }[] = []; + if (hasPermission) { + lastCountList = countList.slice(1).map((item) => { + return { + value: item.count, + label: item.name, + name: item.name, + }; + }); + options.series.data = lastCountList; + + options.title.text = countList[0].name ?? ''; + options.title.subtext = `${countList[0].count ?? 0}%`; + } else { + options.series.data = []; + lastCountList = defaultValueMap[typeKey][valueKey].defaultList.map((e: any) => { + return { + ...e, + label: t(e.label), + }; + }); + options.title.text = t(defaultValueMap[typeKey][valueKey].defaultName); + options.title.subtext = '-%'; + } + + options.series.color = defaultValueMap[typeKey][valueKey].color; + + return { + valueList: lastCountList, + options, + }; +}