(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";
|
}
|
}
|
});
|
})();
|