(function () { "use strict"; function nowDate() { return new Date(); } function minutesAgo(minutes) { return new Date(Date.now() - minutes * 60 * 1000); } function createEmptyOverviewSummary() { return { totalDevices: 0, okDevices: 0, unstableDevices: 0, offlineDevices: 0, noDataDevices: 0, avgLatencyMs: null, maxLatencyMs: null }; } function createEmptyDetailSummary() { return { totalSamples: 0, successSamples: 0, failSamples: 0, successRate: 0, avgLatencyMs: null, minLatencyMs: null, maxLatencyMs: null, packetSize: -1, latestStatus: "", latestTimeLabel: "" }; } function createEmptySamplingConfig() { return { intervalMs: 1000, timeoutMs: 800, probeCount: 3, packetSize: -1 }; } new Vue({ el: "#app", data: function () { return { lastOptionsData: null, lastOverviewData: null, lastTrendData: null, overviewLoading: false, detailLoading: false, devices: [], availableDays: [], samplingConfig: createEmptySamplingConfig(), overviewSummary: createEmptyOverviewSummary(), overviewRows: [], overviewFilters: { deviceType: "", keyword: "" }, detailFilters: { deviceKey: "", range: [minutesAgo(30), nowDate()] }, detailSummary: createEmptyDetailSummary(), series: [], alerts: [], charts: { latency: null, availability: null }, resizeHandler: null }; }, computed: { currentDevice: function () { var key = this.detailFilters.deviceKey; if (!key) { return null; } for (var i = 0; i < this.devices.length; i++) { var item = this.devices[i]; if ((item.deviceType + "#" + item.deviceNo) === key) { return item; } } for (var j = 0; j < this.overviewRows.length; j++) { var row = this.overviewRows[j]; if ((row.deviceType + "#" + row.deviceNo) === key) { return row; } } return null; }, filteredOverviewRows: function () { var type = this.overviewFilters.deviceType; var keyword = (this.overviewFilters.keyword || "").toLowerCase(); return this.overviewRows.filter(function (item) { if (type && item.deviceType !== type) { return false; } if (!keyword) { return true; } var deviceText = (item.deviceType + "-" + item.deviceNo).toLowerCase(); var ipText = (item.ip || "").toLowerCase(); return deviceText.indexOf(keyword) >= 0 || ipText.indexOf(keyword) >= 0; }); }, samplingConfigText: function () { var config = this.samplingConfig || createEmptySamplingConfig(); return this.i18n("devicePingLog.samplingConfigText", "采样 {0} ms / 超时 {1} ms / 每样本 {2} 次", [ this.formatNumber(config.intervalMs), this.formatNumber(config.timeoutMs), this.formatNumber(config.probeCount) ]); } }, 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.loadOptions(); self.loadOverview(); self.resizeHandler = function () { self.resizeCharts(); }; window.addEventListener("resize", self.resizeHandler); }); }, beforeDestroy: function () { if (this.resizeHandler) { window.removeEventListener("resize", this.resizeHandler); } 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("devicePingLog.title", "设备网络分析"); }, refreshLocalizedState: function () { this.refreshDocumentTitle(); if (this.lastOptionsData) { this.applyOptionsData(this.lastOptionsData); } if (this.lastOverviewData) { this.applyOverviewData(this.lastOverviewData); } if (this.lastTrendData) { this.applyTrendData(this.lastTrendData); } else { this.$forceUpdate(); this.updateCharts(); } }, resolveStatusText: function (status) { var code = status == null ? "" : String(status).toUpperCase(); if (code === "OK") { return this.i18n("devicePingLog.status.ok", "正常"); } if (code === "UNSTABLE") { return this.i18n("devicePingLog.status.unstable", "波动"); } if (code === "TIMEOUT") { return this.i18n("devicePingLog.status.timeout", "超时"); } if (code === "ERROR") { return this.i18n("devicePingLog.status.error", "异常"); } if (code === "NO_DATA") { return this.i18n("devicePingLog.status.noData", "暂无数据"); } if (!code) { return "--"; } return this.translateLegacyText(status); }, applyOptionsData: function (data) { this.lastOptionsData = data || {}; this.refreshDocumentTitle(); this.devices = (data && data.devices) || []; this.availableDays = (data && data.days) || []; this.samplingConfig = Object.assign(createEmptySamplingConfig(), data && data.samplingConfig ? data.samplingConfig : {}); }, applyOverviewData: function (data) { this.lastOverviewData = data || {}; this.overviewSummary = Object.assign(createEmptyOverviewSummary(), data && data.summary ? data.summary : {}); this.overviewRows = this.normalizeOverviewRows(data && data.devices ? data.devices : []); }, applyTrendData: function (data) { this.lastTrendData = data || {}; this.detailSummary = this.normalizeDetailSummary(data && data.summary ? data.summary : {}); this.series = (data && data.series) || []; this.alerts = this.normalizeAlerts(data && data.alerts ? data.alerts : []); this.updateCharts(); }, normalizeOverviewRows: function (rows) { var self = this; return (rows || []).map(function (item) { var result = Object.assign({}, item); result.statusText = self.resolveStatusText(item && item.status); result.message = self.translateLegacyText(item && item.message); result.label = self.translateLegacyText(item && item.label); return result; }); }, normalizeDetailSummary: function (summary) { var result = Object.assign(createEmptyDetailSummary(), summary || {}); result.latestStatus = this.resolveStatusText(result.latestStatus); return result; }, normalizeAlerts: function (alerts) { var self = this; return (alerts || []).map(function (item) { var result = Object.assign({}, item); result.status = self.resolveStatusText(item && item.status); result.message = self.translateLegacyText(item && item.message); return result; }); }, formatNumber: function (value) { var num = Number(value || 0); if (!isFinite(num)) { return "0"; } return num.toLocaleString(this.getCurrentLocale()); }, loadOptions: function () { var self = this; $.ajax({ url: baseUrl + "/devicePingLog/options/auth", headers: { token: localStorage.getItem("token") }, method: "GET", success: function (res) { if (res && res.code === 200) { var data = res.data || {}; self.applyOptionsData(data); return; } self.$message.error((res && res.msg) || self.i18n("devicePingLog.optionsLoadFailed", "设备配置加载失败")); }, error: function () { self.$message.error(self.i18n("devicePingLog.optionsLoadFailed", "设备配置加载失败")); } }); }, loadOverview: function () { var self = this; this.overviewLoading = true; $.ajax({ url: baseUrl + "/devicePingLog/overview/auth", headers: { token: localStorage.getItem("token") }, method: "GET", success: function (res) { if (res && res.code === 200) { self.applyOverviewData(res.data || {}); return; } self.overviewSummary = createEmptyOverviewSummary(); self.overviewRows = []; self.$message.error((res && res.msg) || self.i18n("devicePingLog.overviewLoadFailed", "总览数据加载失败")); }, error: function () { self.overviewSummary = createEmptyOverviewSummary(); self.overviewRows = []; self.$message.error(self.i18n("devicePingLog.overviewLoadFailed", "总览数据加载失败")); }, complete: function () { self.overviewLoading = false; } }); }, openDetail: function (row) { var self = this; if (!row) { return; } this.detailFilters.deviceKey = row.deviceType + "#" + row.deviceNo; this.setQuickRange(30, false); this.$nextTick(function () { self.ensureCharts(); self.queryTrend(); }); }, setQuickRange: function (minutes, autoQuery) { this.detailFilters.range = [minutesAgo(minutes), nowDate()]; if (autoQuery !== false && this.currentDevice) { this.queryTrend(); } }, queryTrend: function () { if (!this.currentDevice) { return; } if (!this.detailFilters.range || this.detailFilters.range.length !== 2) { this.$message.warning(this.i18n("devicePingLog.selectTimeRange", "请选择时间范围")); return; } var parts = this.detailFilters.deviceKey.split("#"); if (parts.length !== 2) { this.$message.warning(this.i18n("devicePingLog.invalidDevice", "设备信息无效")); return; } var self = this; this.detailLoading = true; $.ajax({ url: baseUrl + "/devicePingLog/trend/auth", headers: { token: localStorage.getItem("token") }, method: "GET", data: { deviceType: parts[0], deviceNo: parts[1], startTime: this.detailFilters.range[0].getTime(), endTime: this.detailFilters.range[1].getTime() }, success: function (res) { if (res && res.code === 200) { self.applyTrendData(res.data || {}); return; } self.detailSummary = createEmptyDetailSummary(); self.series = []; self.alerts = []; self.updateCharts(); self.$message.error((res && res.msg) || self.i18n("devicePingLog.detailLoadFailed", "设备详情加载失败")); }, error: function () { self.detailSummary = createEmptyDetailSummary(); self.series = []; self.alerts = []; self.updateCharts(); self.$message.error(self.i18n("devicePingLog.detailLoadFailed", "设备详情加载失败")); }, complete: function () { self.detailLoading = false; } }); }, ensureCharts: function () { if (this.$refs.latencyChart && !this.charts.latency) { this.charts.latency = echarts.init(this.$refs.latencyChart); } if (this.$refs.availabilityChart && !this.charts.availability) { this.charts.availability = echarts.init(this.$refs.availabilityChart); } }, disposeCharts: function () { if (this.charts.latency) { this.charts.latency.dispose(); this.charts.latency = null; } if (this.charts.availability) { this.charts.availability.dispose(); this.charts.availability = null; } }, resizeCharts: function () { if (this.charts.latency) { this.charts.latency.resize(); } if (this.charts.availability) { this.charts.availability.resize(); } }, updateCharts: function () { this.ensureCharts(); if (!this.charts.latency || !this.charts.availability) { return; } if (!this.series.length) { this.charts.latency.clear(); this.charts.availability.clear(); return; } this.charts.latency.setOption(this.buildLatencyOption(), true); this.charts.availability.setOption(this.buildAvailabilityOption(), true); this.resizeCharts(); }, buildLatencyOption: function () { var xAxisData = this.series.map(function (item) { return item.timeLabel; }); return { color: ["#1f6fb2", "#2fa38e", "#f59a4a"], tooltip: { trigger: "axis" }, legend: { top: 0, textStyle: { color: "#5e738a" } }, grid: { left: 54, right: 18, top: 40, bottom: 54 }, xAxis: { type: "category", data: xAxisData, boundaryGap: false, axisLine: { lineStyle: { color: "#d5dfeb" } }, axisLabel: { color: "#74879a", formatter: function (value) { return value.slice(11); } } }, yAxis: { type: "value", name: "ms", splitLine: { lineStyle: { color: "#edf2f7" } }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#7f92a5" } }, dataZoom: [{ type: "inside" }, { type: "slider", height: 18, bottom: 10 }], series: [{ name: this.i18n("devicePingLog.chart.latency.avg", "平均"), type: "line", showSymbol: false, sampling: "lttb", data: this.series.map(function (item) { return item.avgLatencyMs; }), lineStyle: { width: 2 } }, { name: this.i18n("devicePingLog.chart.latency.min", "最小"), type: "line", showSymbol: false, sampling: "lttb", data: this.series.map(function (item) { return item.minLatencyMs; }), lineStyle: { width: 1.5 } }, { name: this.i18n("devicePingLog.chart.latency.max", "最大"), type: "line", showSymbol: false, sampling: "lttb", data: this.series.map(function (item) { return item.maxLatencyMs; }), lineStyle: { width: 1.5, type: "dashed" } }] }; }, buildAvailabilityOption: function () { var xAxisData = this.series.map(function (item) { return item.timeLabel; }); return { color: ["#de5c5c", "#2fa38e"], tooltip: { trigger: "axis" }, legend: { top: 0, textStyle: { color: "#5e738a" } }, grid: { left: 48, right: 50, top: 40, bottom: 54 }, xAxis: { type: "category", data: xAxisData, axisLine: { lineStyle: { color: "#d5dfeb" } }, axisLabel: { color: "#74879a", formatter: function (value) { return value.slice(11); } } }, yAxis: [{ type: "value", name: this.i18n("devicePingLog.chart.availability.failAxis", "失败"), splitLine: { lineStyle: { color: "#edf2f7" } }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#7f92a5" } }, { type: "value", name: this.i18n("devicePingLog.chart.availability.successRateAxis", "成功率%"), min: 0, max: 100, splitLine: { show: false }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#7f92a5" } }], dataZoom: [{ type: "inside" }, { type: "slider", height: 18, bottom: 10 }], series: [{ name: this.i18n("devicePingLog.chart.availability.failCount", "失败次数"), type: "bar", yAxisIndex: 0, barMaxWidth: 18, data: this.series.map(function (item) { return item.failProbeCount; }), itemStyle: { borderRadius: [6, 6, 0, 0] } }, { name: this.i18n("devicePingLog.chart.availability.successRate", "成功率"), type: "line", yAxisIndex: 1, showSymbol: false, sampling: "lttb", data: this.series.map(function (item) { return item.successRate; }), lineStyle: { width: 2 } }] }; }, statusClass: function (status) { if (status === "OK") { return "ok"; } if (status === "UNSTABLE") { return "unstable"; } if (status === "NO_DATA") { return "nodata"; } return "offline"; }, formatLatency: function (value) { var num; if (value === null || value === undefined || value === "") { return "--"; } num = Number(value); if (!isFinite(num)) { return "--"; } return num.toLocaleString(this.getCurrentLocale(), { maximumFractionDigits: 2 }) + " ms"; }, formatPercent: function (value) { var num; if (value === null || value === undefined || value === "") { return "--"; } 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("devicePingLog.systemDefaultPacketSize", "系统默认"); } return num.toLocaleString(this.getCurrentLocale()) + " B"; } } }); })();