(function () { "use strict"; var REFRESH_SECONDS = 30; function cloneMetricList(source) { return Array.isArray(source) ? source : []; } new Vue({ el: "#app", data: function () { return { rawPayload: null, loading: true, refreshing: false, switchingSystem: 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.refreshDocumentTitle(); if (window.WCS_I18N && typeof window.WCS_I18N.onReady === "function") { window.WCS_I18N.onReady(function () { self.refreshLocalizedState(); }); } this.$nextTick(function () { self.initCharts(); self.loadDashboard(false); self.startAutoRefresh(); self.bindResize(); }); }, beforeDestroy: function () { this.clearTimers(); this.unbindResize(); this.disposeCharts(); }, methods: { formatMessage: function (text, params) { var list = Array.isArray(params) ? params : [params]; return String(text == null ? "" : text).replace(/\{(\d+)\}/g, function (match, index) { return list[index] == null ? "" : list[index]; }); }, i18n: function (key, fallback, params) { if (window.WCS_I18N && typeof window.WCS_I18N.t === "function") { var translated = window.WCS_I18N.t(key, params); if (translated && translated !== key) { return translated; } } return this.formatMessage(fallback || key, params); }, translateLegacyText: function (text) { if (text == null || text === "") { return text; } if (window.WCS_I18N && typeof window.WCS_I18N.tl === "function") { return window.WCS_I18N.tl(String(text)); } return text; }, getCurrentLocale: function () { if (window.WCS_I18N && typeof window.WCS_I18N.getLocale === "function") { return window.WCS_I18N.getLocale(); } return "zh-CN"; }, refreshDocumentTitle: function () { document.title = this.i18n("dashboard.title", "系统仪表盘"); }, refreshLocalizedState: function () { this.refreshDocumentTitle(); if (this.rawPayload) { this.applyData(this.rawPayload); return; } this.$forceUpdate(); this.updateCharts(); }, 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) || self.i18n("dashboard.loadFailed", "仪表盘数据加载失败")); }, error: function () { if (manual) { self.$message.error(self.i18n("dashboard.loadFailedDetail", "仪表盘数据加载失败,请检查接口状态")); } }, complete: function () { self.loading = false; self.refreshing = false; } }); }, applyData: function (payload) { var tasks = payload && payload.tasks ? payload.tasks : {}; var devices = payload && payload.devices ? payload.devices : {}; var network = payload && payload.network ? payload.network : {}; var ai = payload && payload.ai ? payload.ai : {}; this.rawPayload = payload || {}; this.refreshDocumentTitle(); this.overview = payload && payload.overview ? payload.overview : this.overview; this.tasks = { overview: tasks.overview || this.tasks.overview, directionStats: this.normalizeTaskDirectionMetrics(tasks.directionStats), stageStats: this.normalizeTaskStageMetrics(tasks.stageStats), statusStats: this.normalizeMetricList(tasks.statusStats), recentTasks: this.normalizeRecentTasks(tasks.recentTasks) }; this.devices = { overview: devices.overview || this.devices.overview, typeStats: this.normalizeDeviceTypeStats(devices.typeStats) }; this.network = { overview: network.overview || this.network.overview, samplingConfig: network.samplingConfig || this.network.samplingConfig, statusStats: this.normalizeNetworkStatusMetrics(network.statusStats), focusDevices: this.normalizeFocusDevices(network.focusDevices) }; this.ai = { overview: ai.overview || this.ai.overview, routeStats: this.normalizeAiRouteStats(ai.routeStats), routeList: this.normalizeAiRouteList(ai.routeList) }; this.updateCharts(); }, normalizeTaskDirectionMetrics: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); if (item && item.name === "入库任务") { result.name = self.i18n("dashboard.taskDirectionInbound", "入库任务"); } else if (item && item.name === "出库任务") { result.name = self.i18n("dashboard.taskDirectionOutbound", "出库任务"); } else if (item && item.name === "移库任务") { result.name = self.i18n("dashboard.taskDirectionMove", "移库任务"); } else { result.name = self.translateLegacyText(item && item.name); } return result; }); }, normalizeTaskStageMetrics: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); if (item && item.name === "执行中") { result.name = self.i18n("dashboard.taskRunningLabel", "执行中"); } else if (item && item.name === "待人工") { result.name = self.i18n("dashboard.taskManualLabel", "待人工"); } else if (item && item.name === "已完成") { result.name = self.i18n("dashboard.taskCompletedLabel", "已完成"); } else if (item && item.name === "新建") { result.name = self.i18n("dashboard.taskNewLabel", "新建"); } else { result.name = self.translateLegacyText(item && item.name); } return result; }); }, normalizeMetricList: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); result.name = self.translateLegacyText(item && item.name); return result; }); }, normalizeRecentTasks: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); result.taskType = self.translateLegacyText(item && item.taskType); result.status = self.translateLegacyText(item && item.status); result.source = self.translateLegacyText(item && item.source); result.target = self.translateLegacyText(item && item.target); result.device = self.translateLegacyText(item && item.device); return result; }); }, normalizeAiRouteStats: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); if (item && item.name === "可用") { result.name = self.i18n("dashboard.aiRouteStatusAvailable", "可用"); } else if (item && item.name === "冷却中") { result.name = self.i18n("dashboard.aiRouteStatusCooling", "冷却中"); } else if (item && item.name === "已禁用") { result.name = self.i18n("dashboard.aiRouteStatusDisabled", "已禁用"); } else { result.name = self.translateLegacyText(item && item.name); } return result; }); }, normalizeNetworkStatusMetrics: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); if (item && item.name === "正常") { result.name = self.i18n("dashboard.networkOkLabel", "正常"); } else if (item && item.name === "波动") { result.name = self.i18n("dashboard.networkUnstableLabel", "波动"); } else if (item && item.name === "超时/异常") { result.name = self.i18n("dashboard.networkOfflineLabel", "超时/异常"); } else if (item && item.name === "暂无数据") { result.name = self.i18n("dashboard.networkNoDataLabel", "暂无数据"); } else { result.name = self.translateLegacyText(item && item.name); } return result; }); }, normalizeDeviceTypeStats: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); result.name = self.translateLegacyText(item && item.name); return result; }); }, normalizeAiRouteList: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); if (result.statusType === "success") { result.statusText = self.i18n("dashboard.aiRouteStatusAvailable", "可用"); } else if (result.statusType === "warning") { result.statusText = self.i18n("dashboard.aiRouteStatusCooling", "冷却中"); } else { result.statusText = self.i18n("dashboard.aiRouteStatusDisabled", "已禁用"); } result.lastError = self.translateLegacyText(item && item.lastError); return result; }); }, normalizeFocusDevices: function (list) { var self = this; return cloneMetricList(list).map(function (item) { var result = Object.assign({}, item); if (result.statusType === "success") { result.statusText = self.i18n("dashboard.networkOkLabel", "正常"); } else if (result.statusType === "warning") { result.statusText = self.i18n("dashboard.networkUnstableLabel", "波动"); } else if (result.statusType === "danger") { result.statusText = self.i18n("dashboard.networkOfflineLabel", "超时/异常"); } else { result.statusText = self.i18n("dashboard.networkNoDataLabel", "暂无数据"); } result.message = self.translateLegacyText(item && item.message); return result; }); }, 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); var onlineText = this.i18n("dashboard.deviceOnlineLegend", "在线"); var offlineText = this.i18n("dashboard.deviceOfflineLegend", "离线"); return { color: ["#2fa38e", "#d8e2ec"], legend: { right: 0, top: 0, itemWidth: 10, itemHeight: 10, textStyle: { color: "#60778d", fontSize: 12 }, data: [onlineText, offlineText] }, 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: onlineText, type: "bar", stack: "device", barWidth: 18, data: data.map(function (item) { return item.online; }), itemStyle: { borderRadius: [9, 0, 0, 9] } }, { name: offlineText, 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: this.i18n("dashboard.chartCenter.availableRoutes", "可用路由"), 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: this.i18n("dashboard.chartCenter.attentionDevices", "需关注设备"), 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(this.getCurrentLocale()); }, formatLatency: function (value) { var num; if (value == null || value === "") { return "--"; } num = Number(value); if (!isFinite(num)) { return "--"; } return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + " ms"; }, formatPercentValue: function (value) { var num = Number(value); if (!isFinite(num)) { return "--"; } return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + "%"; }, formatPacketSize: function (value) { var num = Number(value); if (!isFinite(num) || num < 0) { return this.i18n("dashboard.systemDefaultPacketSize", "系统默认"); } return num.toLocaleString(this.getCurrentLocale()) + " B"; }, displayText: function (value, fallback) { return value == null || value === "" ? (fallback || "") : value; }, showMessage: function (message, type) { if (this.$message && typeof this.$message === "function") { this.$message({ message: message, type: type || "info" }); return; } console[type === "error" ? "error" : "log"](message); }, networkSamplingText: function () { var config = this.network && this.network.samplingConfig ? this.network.samplingConfig : {}; return this.i18n("dashboard.networkSampling", "采样 {0} ms / 超时 {1} ms / 每样本 {2} 次 / 包大小 {3}", [ this.displayText(config.intervalMs, 0), this.displayText(config.timeoutMs, 0), 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", this.i18n("dashboard.monitorView", "监控画面"), this.translateLegacyText("监控画面"), this.translateLegacyText("监控系统") ); }, syncParentSystemRunning: function (status) { try { if (window.parent && window.parent !== window) { window.parent.systemRunning = !!status; } } catch (e) { } }, requestSystemSwitch: function (operatorType, password) { var self = this; this.switchingSystem = true; $.ajax({ url: baseUrl + "/console/system/switch", headers: { token: localStorage.getItem("token") }, data: { operatorType: operatorType, password: password || "" }, method: "POST", success: function (res) { var status; if (res && res.code === 200) { status = !!(res.data && res.data.status); self.overview = Object.assign({}, self.overview, { systemRunning: status }); self.syncParentSystemRunning(status); self.showMessage( status ? self.i18n("dashboard.systemStarted", "系统已启动") : self.i18n("dashboard.systemStopped", "系统已停止"), "success" ); self.loadDashboard(false); return; } if (res && res.code === 403) { window.location.href = baseUrl + "/login"; return; } self.showMessage( (res && res.msg) || self.i18n("dashboard.systemSwitchFailed", "系统状态切换失败"), "error" ); }, error: function () { self.showMessage( self.i18n("dashboard.systemSwitchFailedDetail", "系统状态切换失败,请检查接口状态"), "error" ); }, complete: function () { self.switchingSystem = false; } }); }, startSystem: function () { if (this.overview.systemRunning || this.switchingSystem) { return; } this.requestSystemSwitch(1); }, maskStopSystemPromptInput: function () { setTimeout(function () { var input = document.querySelector(".el-message-box__wrapper .el-message-box__input input"); if (!input) { return; } input.setAttribute("type", "text"); input.setAttribute("name", "dashboard-stop-code"); input.setAttribute("autocomplete", "new-password"); input.setAttribute("autocapitalize", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("spellcheck", "false"); input.setAttribute("data-form-type", "other"); input.style.webkitTextSecurity = "disc"; }, 30); }, stopSystem: function () { var self = this; var proceed = function (password) { var cleanPassword = String(password == null ? "" : password).trim(); if (!cleanPassword) { self.showMessage(self.i18n("dashboard.stopSystemPasswordRequired", "请输入停止系统口令"), "warning"); return; } self.requestSystemSwitch(0, cleanPassword); }; if (!this.overview.systemRunning || this.switchingSystem) { return; } if (typeof this.$prompt === "function") { this.$prompt( this.i18n("dashboard.stopSystemPrompt", "请输入口令,并停止 WCS 系统"), this.i18n("dashboard.stopSystemTitle", "停止系统"), { confirmButtonText: this.i18n("common.confirm", "确定"), cancelButtonText: this.i18n("common.cancel", "取消"), inputType: "text", inputPattern: /\S+/, inputErrorMessage: this.i18n("dashboard.stopSystemPasswordRequired", "请输入停止系统口令") } ).then(function (result) { proceed(result && result.value); }).catch(function () { }); this.maskStopSystemPromptInput(); return; } proceed(window.prompt(this.i18n("dashboard.stopSystemPrompt", "请输入口令,并停止 WCS 系统"), "")); }, toggleSystem: function () { if (this.overview.systemRunning) { this.stopSystem(); return; } this.startSystem(); }, openDevicePingAnalysis: function () { this.openParentMenuView( "/views/devicePingLog/devicePingLog.html", this.i18n("devicePingLog.title", "设备网络分析"), this.translateLegacyText("设备网络分析") ); } } }); }());