diff --git a/dashboard/backend/src/routes/dashboard.ts b/dashboard/backend/src/routes/dashboard.ts index 8fe4123..9803519 100644 --- a/dashboard/backend/src/routes/dashboard.ts +++ b/dashboard/backend/src/routes/dashboard.ts @@ -268,4 +268,46 @@ router.get('/api/dashboard/active-daily', async (req: Request, res: Response) => } }); +/** + * GET /api/dashboard/device-versions + * 按型号统计全量设备的版本分布,空版本计入“未知版本” + */ +router.get('/api/dashboard/device-versions', async (_req: Request, res: Response) => { + try { + const version = sql`COALESCE(NULLIF(TRIM(${userDevices.ver}), ''), '未知版本')`; + const rows = await db + .select({ + model: userDevices.model, + ver: version.as('version'), + count: sql`COUNT(*)`.mapWith(Number).as('count'), + }) + .from(userDevices) + .groupBy(userDevices.model, version) + .orderBy(userDevices.model, desc(sql`COUNT(*)`), version); + + const modelMap = new Map; + }>(); + + for (const row of rows) { + let stats = modelMap.get(row.model); + if (!stats) { + stats = { model: row.model, total: 0, versions: [] }; + modelMap.set(row.model, stats); + } + const count = Number(row.count); + stats.total += count; + stats.versions.push({ ver: row.ver, count }); + } + + res.json(ApiResponse.success({ items: [...modelMap.values()] })); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e); + logger.error(`获取设备版本分布失败:${msg}`); + res.json(ApiResponse.error(`获取设备版本分布失败:${msg}`)); + } +}); + export default router; diff --git a/dashboard/frontend/src/service/api/dashboard.ts b/dashboard/frontend/src/service/api/dashboard.ts index 2f58ac9..9215577 100644 --- a/dashboard/frontend/src/service/api/dashboard.ts +++ b/dashboard/frontend/src/service/api/dashboard.ts @@ -15,6 +15,13 @@ export function fetchGetDeviceDaily(days: number = 30) { }); } +export function fetchGetDeviceVersions() { + return request({ + url: '/dashboard/device-versions', + method: 'get' + }); +} + export function fetchGetActiveDaily(days: number = 30) { return request({ url: '/dashboard/active-daily', diff --git a/dashboard/frontend/src/typings/api/dashboard.d.ts b/dashboard/frontend/src/typings/api/dashboard.d.ts index 14d9ab0..22a084e 100644 --- a/dashboard/frontend/src/typings/api/dashboard.d.ts +++ b/dashboard/frontend/src/typings/api/dashboard.d.ts @@ -49,6 +49,19 @@ declare namespace Api { total: number; } + interface DeviceVersionModel { + model: string; + total: number; + versions: Array<{ + ver: string; + count: number; + }>; + } + + interface DeviceVersionStats { + items: DeviceVersionModel[]; + } + interface ActiveDailyStats { dates: string[]; series: DeviceDailySeries[]; diff --git a/dashboard/frontend/src/views/customer-service/feedbacks/index.vue b/dashboard/frontend/src/views/customer-service/feedbacks/index.vue index 1f05529..fd90a69 100644 --- a/dashboard/frontend/src/views/customer-service/feedbacks/index.vue +++ b/dashboard/frontend/src/views/customer-service/feedbacks/index.vue @@ -1,6 +1,6 @@ @@ -375,6 +399,25 @@ onMounted(() => { + + + + 按各型号全部设备统计,未上报版本号的设备计入“未知版本”。 + + + + + + + + + + + + + diff --git a/dashboard/frontend/src/views/home/modules/pie-chart.vue b/dashboard/frontend/src/views/home/modules/pie-chart.vue index 9ff7978..3f5ba9e 100644 --- a/dashboard/frontend/src/views/home/modules/pie-chart.vue +++ b/dashboard/frontend/src/views/home/modules/pie-chart.vue @@ -1,108 +1,137 @@