(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 { 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 "采样 " + config.intervalMs + " ms / 超时 " + config.timeoutMs + " ms / 每样本 " + config.probeCount + " 次"; } }, mounted: function () { var self = this; 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: { 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.devices = data.devices || []; self.availableDays = data.days || []; self.samplingConfig = Object.assign(createEmptySamplingConfig(), data.samplingConfig || {}); return; } self.$message.error((res && res.msg) || "设备配置加载失败"); }, error: function () { self.$message.error("设备配置加载失败"); } }); }, 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) { var data = res.data || {}; self.overviewSummary = Object.assign(createEmptyOverviewSummary(), data.summary || {}); self.overviewRows = data.devices || []; return; } self.overviewSummary = createEmptyOverviewSummary(); self.overviewRows = []; self.$message.error((res && res.msg) || "总览数据加载失败"); }, error: function () { self.overviewSummary = createEmptyOverviewSummary(); self.overviewRows = []; self.$message.error("总览数据加载失败"); }, 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("请选择时间范围"); return; } var parts = this.detailFilters.deviceKey.split("#"); if (parts.length !== 2) { this.$message.warning("设备信息无效"); 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) { var data = res.data || {}; self.detailSummary = Object.assign(createEmptyDetailSummary(), data.summary || {}); self.series = data.series || []; self.alerts = data.alerts || []; self.updateCharts(); return; } self.detailSummary = createEmptyDetailSummary(); self.series = []; self.alerts = []; self.updateCharts(); self.$message.error((res && res.msg) || "设备详情加载失败"); }, error: function () { self.detailSummary = createEmptyDetailSummary(); self.series = []; self.alerts = []; self.updateCharts(); self.$message.error("设备详情加载失败"); }, 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: "平均", type: "line", showSymbol: false, sampling: "lttb", data: this.series.map(function (item) { return item.avgLatencyMs; }), lineStyle: { width: 2 } }, { name: "最小", type: "line", showSymbol: false, sampling: "lttb", data: this.series.map(function (item) { return item.minLatencyMs; }), lineStyle: { width: 1.5 } }, { name: "最大", 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: "失败", splitLine: { lineStyle: { color: "#edf2f7" } }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: "#7f92a5" } }, { type: "value", name: "成功率%", 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: "失败次数", type: "bar", yAxisIndex: 0, barMaxWidth: 18, data: this.series.map(function (item) { return item.failProbeCount; }), itemStyle: { borderRadius: [6, 6, 0, 0] } }, { name: "成功率", 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) { if (value === null || value === undefined || value === "") { return "--"; } return value + " ms"; }, formatPercent: function (value) { if (value === null || value === undefined || value === "") { return "--"; } return value + "%"; }, formatPacketSize: function (value) { if (value === null || value === undefined || value === "" || value < 0) { return "系统默认"; } return value + " B"; } } }); })();