(function () { "use strict"; var REFRESH_SECONDS = 30; function cloneMetricList(source) { return Array.isArray(source) ? source : []; } new Vue({ el: "#app", data: function () { return { loading: true, refreshing: false, countdown: REFRESH_SECONDS, countdownTimer: null, resizeHandler: null, charts: { taskDirection: null, taskStage: null, deviceType: null, networkStatus: null, aiRoute: null }, overview: { systemRunning: false, generatedAt: "", taskTotal: 0, taskRunning: 0, deviceTotal: 0, deviceOnline: 0, deviceAlarm: 0, aiTokenTotal: 0, aiCallTotal: 0 }, tasks: { overview: { running: 0, manual: 0, completed: 0, newCreated: 0 }, directionStats: [], stageStats: [], statusStats: [], recentTasks: [] }, devices: { overview: { total: 0, online: 0, offline: 0, busy: 0, alarm: 0, 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: { tokenTotal: 0, askCount: 0, llmCallTotal: 0, successCallTotal: 0, failCallTotal: 0, sessionCount: 0, availableRouteCount: 0, lastCallTime: "" }, routeStats: [], routeList: [] } }; }, mounted: function () { var self = this; this.$nextTick(function () { self.initCharts(); self.loadDashboard(false); self.startAutoRefresh(); self.bindResize(); }); }, beforeDestroy: function () { this.clearTimers(); this.unbindResize(); this.disposeCharts(); }, methods: { loadDashboard: function (manual) { var self = this; if (this.refreshing) { return; } this.refreshing = true; $.ajax({ url: baseUrl + "/dashboard/summary/auth", method: "GET", headers: { token: localStorage.getItem("token") }, success: function (res) { if (res && res.code === 200) { self.applyData(res.data || {}); self.countdown = REFRESH_SECONDS; return; } self.$message.error((res && res.msg) || "仪表盘数据加载失败"); }, error: function () { if (manual) { self.$message.error("仪表盘数据加载失败,请检查接口状态"); } }, complete: function () { self.loading = false; self.refreshing = false; } }); }, applyData: function (payload) { 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(); }, initCharts: function () { this.disposeCharts(); 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.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(); }, buildTaskDirectionOption: function () { var data = cloneMetricList(this.tasks.directionStats); return { color: ["#1f6fb2", "#f59a4a", "#2fa38e"], tooltip: { trigger: "item", formatter: "{b}
{c} ({d}%)" }, legend: { bottom: 0, itemWidth: 10, itemHeight: 10, textStyle: { color: "#60778d", fontSize: 12 } }, series: [{ type: "pie", radius: ["48%", "72%"], center: ["50%", "46%"], data: data, label: { color: "#40596f", formatter: "{b}\n{c}" }, labelLine: { lineStyle: { color: "#c6d5e3" } } }] }; }, buildTaskStageOption: function () { var data = cloneMetricList(this.tasks.stageStats); return { color: ["#1f6fb2"], grid: { left: 48, right: 18, top: 24, bottom: 28 }, tooltip: { trigger: "axis", axisPointer: { type: "shadow" } }, xAxis: { type: "category", data: data.map(function (item) { return item.name; }), axisLine: { lineStyle: { color: "#d4deea" } }, axisTick: { show: false }, axisLabel: { color: "#63798f", fontSize: 12 } }, yAxis: { type: "value", splitLine: { lineStyle: { color: "#edf2f7" } }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#8699ad", fontSize: 12 } }, series: [{ type: "bar", barWidth: 32, data: data.map(function (item) { return item.value; }), itemStyle: { borderRadius: [8, 8, 0, 0], color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: "#3388d2" }, { offset: 1, color: "#1f6fb2" }]) }, label: { show: true, position: "top", color: "#36506d", fontWeight: 600 } }] }; }, buildDeviceTypeOption: function () { var data = cloneMetricList(this.devices.typeStats); return { color: ["#2fa38e", "#d8e2ec"], legend: { right: 0, top: 0, itemWidth: 10, itemHeight: 10, textStyle: { color: "#60778d", fontSize: 12 }, data: ["在线", "离线"] }, grid: { left: 76, right: 12, top: 42, bottom: 18 }, tooltip: { trigger: "axis", axisPointer: { type: "shadow" } }, xAxis: { type: "value", splitLine: { lineStyle: { color: "#edf2f7" } }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#8398ab", fontSize: 12 } }, yAxis: { type: "category", data: data.map(function (item) { return item.name; }), axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#60778d", fontSize: 12 } }, series: [{ name: "在线", type: "bar", stack: "device", barWidth: 18, data: data.map(function (item) { return item.online; }), itemStyle: { borderRadius: [9, 0, 0, 9] } }, { name: "离线", type: "bar", stack: "device", barWidth: 18, data: data.map(function (item) { return item.offline; }), itemStyle: { borderRadius: [0, 9, 9, 0] } }] }; }, buildAiRouteOption: function () { var data = cloneMetricList(this.ai.routeStats); return { color: ["#2fa38e", "#f59a4a", "#c8d4e1"], tooltip: { trigger: "item", formatter: "{b}
{c} ({d}%)" }, graphic: [{ type: "text", left: "center", top: "39%", style: { text: this.formatNumber(this.ai.overview.availableRouteCount || 0), fill: "#1f3142", fontSize: 28, fontWeight: 700 } }, { type: "text", left: "center", top: "55%", style: { text: "可用路由", fill: "#7c8fa4", fontSize: 12 } }], legend: { bottom: 0, itemWidth: 10, itemHeight: 10, textStyle: { color: "#60778d", fontSize: 12 } }, series: [{ type: "pie", radius: ["56%", "76%"], center: ["50%", "43%"], avoidLabelOverlap: false, label: { show: false }, labelLine: { show: false }, data: data }] }; }, buildNetworkStatusOption: function () { var data = cloneMetricList(this.network.statusStats); return { color: ["#2fa38e", "#f59a4a", "#de5c5c", "#c8d4e1"], tooltip: { trigger: "item", formatter: "{b}
{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)) { return "0"; } 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; this.clearTimers(); this.countdownTimer = setInterval(function () { if (self.countdown <= 1) { self.countdown = REFRESH_SECONDS; self.loadDashboard(false); return; } self.countdown -= 1; }, 1000); }, clearTimers: function () { if (this.countdownTimer) { clearInterval(this.countdownTimer); this.countdownTimer = null; } }, bindResize: function () { var self = this; this.resizeHandler = function () { self.resizeCharts(); }; window.addEventListener("resize", this.resizeHandler); }, unbindResize: function () { if (this.resizeHandler) { window.removeEventListener("resize", this.resizeHandler); this.resizeHandler = null; } }, resizeCharts: function () { var key; for (key in this.charts) { if (this.charts.hasOwnProperty(key) && this.charts[key]) { this.charts[key].resize(); } } }, disposeCharts: function () { var key; for (key in this.charts) { if (this.charts.hasOwnProperty(key) && this.charts[key]) { this.charts[key].dispose(); this.charts[key] = null; } } }, 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: menuPath, menuName: menuName }); return; } 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", "设备网络分析", "设备网络分析"); } } }); }());