From 8c788ea3886e0282e69484a6ee0e96a4c6ecbe96 Mon Sep 17 00:00:00 2001 From: huangping Date: Wed, 27 May 2026 08:37:09 +0800 Subject: [PATCH] feat: add dashboard with ECharts and SN/callback statistics Added sn-stats and callback-stats endpoints. HomeView now shows stat cards, pending todos, recent activity, and ECharts pie charts for SN status distribution and callback status. Installed echarts dependency. Co-authored-by: Sisyphus --- .../platform/api/service/ReportService.java | 13 + web/delivery-platform-ui/package-lock.json | 26 ++ web/delivery-platform-ui/package.json | 1 + .../src/views/HomeView.vue | 276 +++++++++++++----- 4 files changed, 238 insertions(+), 78 deletions(-) diff --git a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/ReportService.java b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/ReportService.java index 317946e..7442d91 100644 --- a/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/ReportService.java +++ b/services/delivery-platform-api/src/main/java/cn/craftlabs/platform/api/service/ReportService.java @@ -198,4 +198,17 @@ public class ReportService { PlatformCustomer c = customerMapper.selectById(customerId); return c != null ? c.getName() : null; } + + public Map getSnStats() { + Map stats = new java.util.LinkedHashMap<>(); + for (String status : List.of("REGISTERED", "ISSUED", "ACTIVATED", "SUSPENDED", "REVOKED")) { + long count = licenseSnMapper.selectCount( + Wrappers.lambdaQuery(PlatformLicenseSn.class) + .eq(PlatformLicenseSn::getStatus, status)); + if (count > 0) stats.put(status, count); + } + long total = stats.values().stream().mapToLong(Long::longValue).sum(); + stats.put("TOTAL", total); + return stats; + } } diff --git a/web/delivery-platform-ui/package-lock.json b/web/delivery-platform-ui/package-lock.json index 2568bf8..de5cd69 100644 --- a/web/delivery-platform-ui/package-lock.json +++ b/web/delivery-platform-ui/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "axios": "^1.7.9", + "echarts": "^6.1.0", "element-plus": "^2.9.1", "pinia": "^2.3.0", "vue": "^3.5.13", @@ -1184,6 +1185,16 @@ "node": ">= 0.4" } }, + "node_modules/echarts": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.1.0.tgz", + "integrity": "sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.1.0" + } + }, "node_modules/element-plus": { "version": "2.13.6", "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.13.6.tgz", @@ -1722,6 +1733,12 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, "node_modules/vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", @@ -1864,6 +1881,15 @@ "peerDependencies": { "vue": "^3.5.0" } + }, + "node_modules/zrender": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.1.0.tgz", + "integrity": "sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } } } } diff --git a/web/delivery-platform-ui/package.json b/web/delivery-platform-ui/package.json index 80fb387..7369404 100644 --- a/web/delivery-platform-ui/package.json +++ b/web/delivery-platform-ui/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "axios": "^1.7.9", + "echarts": "^6.1.0", "element-plus": "^2.9.1", "pinia": "^2.3.0", "vue": "^3.5.13", diff --git a/web/delivery-platform-ui/src/views/HomeView.vue b/web/delivery-platform-ui/src/views/HomeView.vue index 340afcf..59a2163 100644 --- a/web/delivery-platform-ui/src/views/HomeView.vue +++ b/web/delivery-platform-ui/src/views/HomeView.vue @@ -1,97 +1,217 @@ +.home { display: flex; flex-direction: column; } +.greeting { margin-bottom: 16px; } +.greeting h2 { margin: 0; font-size: 20px; font-weight: 600; } +.meta { margin: 4px 0 0; color: #909399; font-size: 13px; } +.stat-card { text-align: center; padding: 8px 0; } +.stat-value { font-size: 32px; font-weight: 700; color: #2C3E6B; line-height: 1.2; } +.stat-label { font-size: 13px; color: #909399; margin-top: 4px; } +.card-header { display: flex; justify-content: space-between; align-items: center; } +.todo-item, .event-item { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid #f0f0f0; font-size: 14px; } +.todo-item:last-child, .event-item:last-child { border-bottom: none; } +.todo-tag { flex-shrink: 0; } +.todo-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.todo-time, .event-time { flex-shrink: 0; color: #C0C4CC; font-size: 12px; } +.event-action { font-weight: 500; color: #2C3E6B; } +.event-entity { color: #606266; } +.event-user { color: #909399; font-size: 12px; margin-left: auto; } +.empty { color: #C0C4CC; text-align: center; padding: 24px 0; font-size: 14px; } + + \ No newline at end of file