From 2e7dbd705fc82e8db74b073e55af938d67d8c19f Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 17 三月 2026 09:05:49 +0800
Subject: [PATCH] #

---
 src/main/webapp/static/js/dashboard/dashboard.js |  173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 168 insertions(+), 5 deletions(-)

diff --git a/src/main/webapp/static/js/dashboard/dashboard.js b/src/main/webapp/static/js/dashboard/dashboard.js
index dc8cf57..db6676a 100644
--- a/src/main/webapp/static/js/dashboard/dashboard.js
+++ b/src/main/webapp/static/js/dashboard/dashboard.js
@@ -20,6 +20,7 @@
           taskDirection: null,
           taskStage: null,
           deviceType: null,
+          networkStatus: null,
           aiRoute: null
         },
         overview: {
@@ -55,6 +56,26 @@
             onlineRate: 0
           },
           typeStats: []
+        },
+        network: {
+          overview: {
+            totalDevices: 0,
+            okDevices: 0,
+            unstableDevices: 0,
+            offlineDevices: 0,
+            noDataDevices: 0,
+            attentionDevices: 0,
+            avgLatencyMs: null,
+            maxLatencyMs: null
+          },
+          samplingConfig: {
+            intervalMs: 1000,
+            timeoutMs: 800,
+            probeCount: 3,
+            packetSize: -1
+          },
+          statusStats: [],
+          focusDevices: []
         },
         ai: {
           overview: {
@@ -120,6 +141,7 @@
         this.overview = payload.overview || this.overview;
         this.tasks = payload.tasks || this.tasks;
         this.devices = payload.devices || this.devices;
+        this.network = payload.network || this.network;
         this.ai = payload.ai || this.ai;
         this.updateCharts();
       },
@@ -128,16 +150,18 @@
         this.charts.taskDirection = echarts.init(this.$refs.taskDirectionChart);
         this.charts.taskStage = echarts.init(this.$refs.taskStageChart);
         this.charts.deviceType = echarts.init(this.$refs.deviceTypeChart);
+        this.charts.networkStatus = echarts.init(this.$refs.networkStatusChart);
         this.charts.aiRoute = echarts.init(this.$refs.aiRouteChart);
       },
       updateCharts: function () {
-        if (!this.charts.taskDirection || !this.charts.taskStage || !this.charts.deviceType || !this.charts.aiRoute) {
+        if (!this.charts.taskDirection || !this.charts.taskStage || !this.charts.deviceType || !this.charts.networkStatus || !this.charts.aiRoute) {
           return;
         }
 
         this.charts.taskDirection.setOption(this.buildTaskDirectionOption());
         this.charts.taskStage.setOption(this.buildTaskStageOption());
         this.charts.deviceType.setOption(this.buildDeviceTypeOption());
+        this.charts.networkStatus.setOption(this.buildNetworkStatusOption());
         this.charts.aiRoute.setOption(this.buildAiRouteOption());
         this.resizeCharts();
       },
@@ -323,6 +347,51 @@
           }]
         };
       },
+      buildNetworkStatusOption: function () {
+        var data = cloneMetricList(this.network.statusStats);
+        return {
+          color: ["#2fa38e", "#f59a4a", "#de5c5c", "#c8d4e1"],
+          tooltip: {
+            trigger: "item",
+            formatter: "{b}<br/>{c} ({d}%)"
+          },
+          graphic: [{
+            type: "text",
+            left: "center",
+            top: "38%",
+            style: {
+              text: this.formatNumber(this.network.overview.attentionDevices || 0),
+              fill: "#1f3142",
+              fontSize: 26,
+              fontWeight: 700
+            }
+          }, {
+            type: "text",
+            left: "center",
+            top: "54%",
+            style: {
+              text: "闇�鍏虫敞璁惧",
+              fill: "#7c8fa4",
+              fontSize: 12
+            }
+          }],
+          legend: {
+            bottom: 0,
+            itemWidth: 10,
+            itemHeight: 10,
+            textStyle: { color: "#60778d", fontSize: 12 }
+          },
+          series: [{
+            type: "pie",
+            radius: ["55%", "75%"],
+            center: ["50%", "42%"],
+            avoidLabelOverlap: false,
+            label: { show: false },
+            labelLine: { show: false },
+            data: data
+          }]
+        };
+      },
       formatNumber: function (value) {
         var num = Number(value || 0);
         if (!isFinite(num)) {
@@ -330,8 +399,31 @@
         }
         return num.toLocaleString("zh-CN");
       },
+      formatLatency: function (value) {
+        var num;
+        if (value == null || value === "") {
+          return "--";
+        }
+        num = Number(value);
+        if (!isFinite(num)) {
+          return "--";
+        }
+        return num.toLocaleString("zh-CN", { maximumFractionDigits: 2 }) + " ms";
+      },
+      formatPacketSize: function (value) {
+        var num = Number(value);
+        if (!isFinite(num) || num < 0) {
+          return "绯荤粺榛樿";
+        }
+        return num + " B";
+      },
       displayText: function (value, fallback) {
         return value == null || value === "" ? (fallback || "") : value;
+      },
+      networkSamplingText: function () {
+        var config = this.network && this.network.samplingConfig ? this.network.samplingConfig : {};
+        return "閲囨牱 " + this.displayText(config.intervalMs, 0) + " ms / 瓒呮椂 " + this.displayText(config.timeoutMs, 0) +
+          " ms / 姣忔牱鏈� " + this.displayText(config.probeCount, 0) + " 娆� / 鍖呭ぇ灏� " + this.formatPacketSize(config.packetSize);
       },
       startAutoRefresh: function () {
         var self = this;
@@ -381,15 +473,86 @@
           }
         }
       },
-      openMonitor: function () {
+      resolveParentMenuApp: function () {
+        var parentDocument;
+        var parentRoot;
+        try {
+          if (!window.parent || window.parent === window || !window.parent.document) {
+            return null;
+          }
+          parentDocument = window.parent.document;
+          parentRoot = parentDocument.getElementById("app");
+          return parentRoot && parentRoot.__vue__ ? parentRoot.__vue__ : null;
+        } catch (e) {
+          return null;
+        }
+      },
+      resolveAbsoluteViewPath: function (path) {
+        if (!path) {
+          return "";
+        }
+        if (/^https?:\/\//.test(path) || path.indexOf(baseUrl) === 0) {
+          return path;
+        }
+        if (path.indexOf("/views/") === 0) {
+          return baseUrl + path;
+        }
+        if (path.indexOf("views/") === 0) {
+          return baseUrl + "/" + path;
+        }
+        return baseUrl + "/views/" + path.replace(/^\/+/, "");
+      },
+      findParentMenuByPath: function (targetPath, preferredName, preferredGroup) {
+        var parentApp = this.resolveParentMenuApp();
+        var targetBasePath = this.resolveAbsoluteViewPath(targetPath).split("#")[0].split("?")[0];
+        var fallback = null;
+        var i;
+        var j;
+        var group;
+        var item;
+        var itemBasePath;
+
+        if (!parentApp || !Array.isArray(parentApp.menus)) {
+          return null;
+        }
+
+        for (i = 0; i < parentApp.menus.length; i++) {
+          group = parentApp.menus[i];
+          for (j = 0; j < (group.subMenu || []).length; j++) {
+            item = group.subMenu[j];
+            if (!item || !item.url) {
+              continue;
+            }
+            itemBasePath = item.url.split("#")[0].split("?")[0];
+            if (!fallback && ((preferredName && item.name === preferredName) || itemBasePath === targetBasePath)) {
+              fallback = item;
+            }
+            if ((!preferredGroup || group.menu === preferredGroup) && itemBasePath === targetBasePath) {
+              return item;
+            }
+          }
+        }
+
+        return fallback;
+      },
+      openParentMenuView: function (targetPath, fallbackName, preferredName, preferredGroup) {
+        var targetMenu = this.findParentMenuByPath(targetPath, preferredName || fallbackName, preferredGroup);
+        var menuPath = targetMenu && targetMenu.url ? targetMenu.url : targetPath;
+        var menuName = targetMenu && targetMenu.name ? targetMenu.name : (fallbackName || preferredName || "");
         if (window.parent && window.parent.index && typeof window.parent.index.loadView === "function") {
           window.parent.index.loadView({
-            menuPath: "/views/watch/console.html",
-            menuName: "鐩戞帶宸ヤ綔鍙�"
+            menuPath: menuPath,
+            menuName: menuName
           });
           return;
         }
-        window.open(baseUrl + "/views/watch/console.html", "_blank");
+        window.open(targetMenu && targetMenu.url ? targetMenu.url : this.resolveAbsoluteViewPath(targetPath), "_blank");
+      },
+      openMonitor: function () {
+        this.openParentMenuView("/views/watch/console.html", "鐩戞帶鐢婚潰", "鐩戞帶鐢婚潰", "鐩戞帶绯荤粺");
+      },
+      openDevicePingAnalysis: function () {
+        this.openParentMenuView("/views/devicePingLog/devicePingLog.html", "璁惧缃戠粶鍒嗘瀽", "璁惧缃戠粶鍒嗘瀽");
       }
     }
   });

--
Gitblit v1.9.1