improve: status-server主题network页 (#422)

This commit is contained in:
nap0o 2024-09-11 09:50:40 -04:00 committed by GitHub
parent f78ba281fb
commit f32f127dfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 203 additions and 129 deletions

View File

@ -13,7 +13,18 @@ const mixinsVue = {
this.isMobile = this.checkIsMobile(); this.isMobile = this.checkIsMobile();
this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate; this.preferredTemplate = this.getCookie('preferred_theme') ? this.getCookie('preferred_theme') : this.$root.defaultTemplate;
}, },
mounted() {
this.initDropdown();
},
methods: { methods: {
initDropdown() {
if(this.isMobile) $('.ui.dropdown').dropdown({
action: 'hide',
on: 'click',
duration: 100,
direction: 'direction'
});
},
toggleTemplate(template) { toggleTemplate(template) {
if( template != this.preferredTemplate){ if( template != this.preferredTemplate){
this.preferredTemplate = template; this.preferredTemplate = template;

View File

@ -381,6 +381,7 @@ td.ping-network-quality {
} }
.network-box .chartTitle { .network-box .chartTitle {
cursor: pointer;
text-align: center; text-align: center;
font-size: 18px; font-size: 18px;
margin: 18px 0px 15px 0px; margin: 18px 0px 15px 0px;
@ -483,6 +484,10 @@ footer p{
min-height: calc(100vh - 90px); min-height: calc(100vh - 90px);
min-height: calc(var(--vh, 1vh) * 100 - 90px); min-height: calc(var(--vh, 1vh) * 100 - 90px);
} }
#chartbox {
min-height: calc(100vh - 170px);
min-height: calc(var(--vh, 1vh) * 100 - 170px);
}
.content { .content {
padding: 0; padding: 0;
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -30,7 +30,7 @@
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script> <script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
<script src="https://unpkg.com/echarts@5.5.0/dist/echarts.min.js"></script> <script src="https://unpkg.com/echarts@5.5.0/dist/echarts.min.js"></script>
<script src="/static/semantic-ui-alerts.min.js"></script> <script src="/static/semantic-ui-alerts.min.js"></script>
<script src="/static/theme-default/js/mixin.js?v20240302"></script> <script src="/static/theme-default/js/mixin.js?v20240911"></script>
<script> <script>
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
</script> </script>

View File

@ -7,7 +7,7 @@
</a> </a>
<a class='item' href="/"><i class="home icon"></i>{{tr "Home"}}</a> <a class='item' href="/"><i class="home icon"></i>{{tr "Home"}}</a>
<template v-if="isMobile"> <template v-if="isMobile">
<div class="item ui simple dropdown"> <div class="item ui dropdown" :class="{ simple: !isMobile }">
<div class="text"><i class="bi bi-gear-wide-connected icon" style="margin-right:3px;"></i>{{tr "Feature" }}<i class="dropdown icon" style="margin-right:0px;"></i></div> <div class="text"><i class="bi bi-gear-wide-connected icon" style="margin-right:3px;"></i>{{tr "Feature" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
<div class="menu"> <div class="menu">
<a href="/service" class="item"><i class="rss icon"></i>{{tr "Services" }}</a> <a href="/service" class="item"><i class="rss icon"></i>{{tr "Services" }}</a>
@ -20,7 +20,7 @@
<a href="/network" class="item"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a> <a href="/network" class="item"><i class="bi bi-hdd-network icon"></i>{{tr "NetworkSpiter"}}</a>
</template> </template>
{{ if not .Conf.DisableSwitchTemplateInFrontend }} {{ if not .Conf.DisableSwitchTemplateInFrontend }}
<div class="item ui simple dropdown"> <div class="item ui dropdown" :class="{ simple: !isMobile }">
<div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div> <div class="text"><i class="bi bi-incognito icon" style="margin-right:3px;"></i>{{tr "Template" }}<i class="dropdown icon" style="margin-right:0px;"></i></div>
<div class="menu"> <div class="menu">
<a v-for="(item, index) in adaptedTemplates" :key="index" @click="toggleTemplate(item.key)" class="item"> <a v-for="(item, index) in adaptedTemplates" :key="index" @click="toggleTemplate(item.key)" class="item">

View File

@ -18,7 +18,7 @@
</div> </div>
</div> </div>
<div class="ui container"> <div class="ui container">
<div ref="chartDom" style="margin-top: 15px;height: 520px;overflow: hidden"></div> <div ref="chartDom" style="margin-top: 15px;height: auto;overflow: hidden"></div>
</div> </div>
</div> </div>

View File

@ -26,7 +26,7 @@
<script src="https://unpkg.com/bootstrap@3.4.1/dist/js/bootstrap.min.js"></script> <script src="https://unpkg.com/bootstrap@3.4.1/dist/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script> <script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
<script src="https://unpkg.com/echarts@5.5.0/dist/echarts.min.js"></script> <script src="https://unpkg.com/echarts@5.5.0/dist/echarts.min.js"></script>
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240907"> <link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20240909">
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v202408011"> <link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v202408011">
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240811"> <link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240811">
<script src="/static/theme-server-status/js/mixin.js?v20240907"></script> <script src="/static/theme-server-status/js/mixin.js?v20240907"></script>

View File

@ -18,7 +18,7 @@
<tbody id="servers"> <tbody id="servers">
<template v-for="(node,index) in nodesNoTag"> <template v-for="(node,index) in nodesNoTag">
<tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle" :class="index % 2 === 0 ? 'odd': 'even'" <tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle" :class="index % 2 === 0 ? 'odd': 'even'"
aria-expanded="false" @click="showCharts($event, node.ID)"> aria-expanded="false" @click="showCharts(node.ID)">
<td class="node-cell status center"> <td class="node-cell status center">
<div class="status-container"> <div class="status-container">
<div v-if="node.online" class="status-icon online"></div> <div v-if="node.online" class="status-icon online"></div>
@ -143,7 +143,7 @@
@#node.host.Version#@ @#node.host.Version#@
</span> </span>
<span class="node-echarts-expand"> <span class="node-echarts-expand">
<div class="chartbox" chartbox-show="0" :key="node.ID" :ref="`chart${node.ID}`" style="width: 100%; height: auto;"></div> <div class="chartbox" :id="`chart-${node.ID}`" chartbox-show="0" :key="node.ID" style="width: 100%; height: auto;"></div>
</span> </span>
</div> </div>
</div> </div>

View File

@ -21,7 +21,7 @@
<tbody id="servers"> <tbody id="servers">
<template v-for="(node,index) in group.data"> <template v-for="(node,index) in group.data">
<tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle" <tr :id="'r'+node.ID" data-toggle="collapse" :data-target="'#rt'+node.ID" class="accordion-toggle"
:class="index % 2 === 0 ? 'odd': 'even'" aria-expanded="false" @click="showCharts($event, node.ID)"> :class="index % 2 === 0 ? 'odd': 'even'" aria-expanded="false" @click="showCharts(node.ID)">
<td class="node-cell status center"> <td class="node-cell status center">
<div class="status-container"> <div class="status-container">
<div v-if="node.online" class="status-icon online"></div> <div v-if="node.online" class="status-icon online"></div>
@ -146,7 +146,7 @@
@#node.host.Version#@ @#node.host.Version#@
</span> </span>
<span class="node-echarts-expand"> <span class="node-echarts-expand">
<div class="chartbox" chartbox-show="0" :key="node.ID" :ref="`chart${node.ID}`" style="width: 100%; height: auto;"></div> <div class="chartbox" :id="`chart-${node.ID}`" chartbox-show="0" :key="node.ID" style="width: 100%; height: auto;"></div>
</span> </span>
</div> </div>
</div> </div>

View File

@ -483,24 +483,27 @@
// 如果所有元素的 Temperature 都为 0则返回一个默认值 0 // 如果所有元素的 Temperature 都为 0则返回一个默认值 0
return 0; return 0;
}, },
showCharts(event, id) { showCharts(id,changeChartboxShow=true) {
const chartContainer = this.$refs[`chart${id}`][0]; const chartContainer = document.getElementById(`chart-${id}`);
const chartboxShow = chartContainer.getAttribute('chartbox-show'); if(changeChartboxShow){
chartContainer.setAttribute('chartbox-show', chartboxShow === '0' ? '1' : '0'); const chartboxShow = chartContainer.getAttribute('chartbox-show');
const isAriaExpandedFalse = event.currentTarget.getAttribute('aria-expanded') === 'false'; chartContainer.setAttribute('chartbox-show', chartboxShow === '0' ? '1' : '0');
if (!isAriaExpandedFalse) return; const collapseContainer = document.getElementById(`r${id}`);
const isAriaExpandedFalse = collapseContainer.getAttribute('aria-expanded') === 'false';
if (!isAriaExpandedFalse) return;
}
// 发起数据请求 // 发起数据请求
const url = `/api/v1/monitor/${id}`; const url = `/api/v1/monitor/${id}`;
fetch(url) fetch(url)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.result) { // 数据请求成功,更新数据并渲染图表 if (data.result) { // 数据请求成功,更新数据并渲染图表
this.chartDataList[id - 1] = data.result; this.chartDataList[id] = data.result;
this.$nextTick(() => { this.$nextTick(() => {
this.renderCharts(id); this.renderCharts(id);
}); });
} else { } else {
console.log('this agent (id:'+ id + ') has no monitor.'); console.log('this server (id:'+ id + ') has no monitor.');
} }
}) })
.catch(error => { .catch(error => {
@ -508,22 +511,18 @@
}); });
}, },
renderCharts(id, reload = false) { renderCharts(id, reload = false) {
if (!this.chartDataList[id - 1]) return; if (!this.chartDataList[id]) return;
const chartData = this.chartDataList[id - 1]; const chartData = this.chartDataList[id];
const chartContainer = this.$refs[`chart${id}`][0]; const chartContainer = document.getElementById(`chart-${id}`);
if (reload) { //点击切换亮色/暗色风格模式时,重新载入echarts图表的逻辑, if (reload) {
// 第一步,查找已经渲染出的图表容器,并销毁它
const existingChart = echarts.getInstanceByDom(chartContainer); const existingChart = echarts.getInstanceByDom(chartContainer);
if (existingChart) existingChart.dispose(); if (existingChart) existingChart.dispose();
// 第二步,如果图表容器处于不可见状态chartboxShow=0,不重新渲染出新的图表,
// 如果图表容器处于可见状态chartboxShow=1,重新渲染出新的图表
const chartboxShow = chartContainer.getAttribute('chartbox-show'); const chartboxShow = chartContainer.getAttribute('chartbox-show');
if ( chartboxShow === '0' ) return; if ( chartboxShow === '0' ) return;
} }
// 定义图表参数值
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300; const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
const fontSize = this.isMobile ? 10 : 14; const fontSize = this.isMobile ? 10 : 14;
const gridLeft = (MaxTCPPingValue > 500) ? (this.isMobile ? 30 : 40) : (this.isMobile ? 25 : 36); const gridLeft = (MaxTCPPingValue > 500) ? (this.isMobile ? 36 : 42) : (this.isMobile ? 25 : 36);
const gridRight = this.isMobile ? 5 : 20; const gridRight = this.isMobile ? 5 : 20;
const legendLeft = this.isMobile ? 'center' : 'center'; const legendLeft = this.isMobile ? 'center' : 'center';
const legendTop = this.isMobile ? 5 : 5; const legendTop = this.isMobile ? 5 : 5;
@ -537,47 +536,91 @@
const tooltipBorderColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.9)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,1)"); const tooltipBorderColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.9)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,1)");
const lineStyleWidth = this.isMobile ? 1 : 2; const lineStyleWidth = this.isMobile ? 1 : 2;
const splitLineWidth = this.isMobile ? 0.5 : 1; const splitLineWidth = this.isMobile ? 0.5 : 1;
// 渲染图表 const markLineItemStyleOpacity = this.semiTransparent ? 1 : 0.75;
const chart = echarts.init(chartContainer, chartTheme, { const markLineLineStyleWidth = this.isMobile ? 0.15 : 0.3;
const chart = echarts.init(chartContainer, chartTheme, { // init图表
renderer: 'canvas', renderer: 'canvas',
useDirtyRect: false, useDirtyRect: false,
width: 'auto', width: 'auto',
height: 300, height: 300
}); });
const xAxisData = chartData[0].created_at.map(time => new Date(time).toLocaleString()); let legendData = [];
const seriesData = chartData.map(item => { let seriesData = [];
chartData.forEach((item,key)=> {
let loss = 0; let loss = 0;
const data = item.avg_delay.map((avgDelay, index) => { let totalLossRate = 0;
if(avgDelay > 0 && avgDelay < MaxTCPPingValue){ let legendName = '';
loss += avgDelay > 0.9 * MaxTCPPingValue ? 1 : 0; let data = { main: [], markLine: []};
return [item.created_at[index], avgDelay.toFixed(2)]; item.avg_delay.forEach((avgDelay, index) => {
}else{ const threshold = 0.9 * MaxTCPPingValue; // 定义阀值,用于判断是否丢包
// 定义丢包 1. avgDelay==0 2. avgDelay>=MaxTCPPingValue 3. avgDelay>=threshold
if(avgDelay == 0 || avgDelay >= MaxTCPPingValue){ //绝对丢包
loss += 1; loss += 1;
const lossrate = 100 * loss / (index + 1);
if(lossrate != 100) {
data['markLine'].push({
xAxis: item.created_at[index],
label: { show: false },
emphasis: { disabled: true },
lineStyle: { type: "solid" }
});
}
} else if (avgDelay >= threshold && avgDelay < MaxTCPPingValue){ // 相对丢包
loss += 1;
const lossrate = 100 * loss / (index + 1);
if(lossrate != 100) {
data['main'].push(
[item.created_at[index], avgDelay, lossrate]
);
data['markLine'].push({
xAxis: item.created_at[index],
label: { show: false },
emphasis: { disabled: true },
lineStyle: { type: "solid" }
});
}
} else { // 未丢包
const lossrate = 100 * loss / (index + 1);
data['main'].push(
[item.created_at[index], avgDelay, lossrate]
);
} }
}); });
const lossRate = ((loss / item.created_at.length) * 100).toFixed(1); totalLossRate = ((loss / item.created_at.length) * 100).toFixed(1);
item.monitor_name = item.monitor_name.includes("%") ? item.monitor_name : `${item.monitor_name} ${lossRate}%`; legendName = `${item.monitor_name} ${totalLossRate}%`;
return { legendData.push(legendName);
name: item.monitor_name, seriesData.push(
type: 'line', {
smooth: true, name: legendName,
symbol: 'none', type: 'line',
data: data, smooth: true,
connectNulls: true, symbol: 'none',
legendHoverLink: false, connectNulls: true,
emphasis: { legendHoverLink: false,
disabled: true emphasis: {
}, disabled: true
lineStyle: { },
width: lineStyleWidth lineStyle: {
} width: lineStyleWidth
}; },
data: data['main'],
markLine: {
symbol: "none",
symbolSize :0,
data: data['markLine'],
itemStyle: {
opacity: markLineItemStyleOpacity
},
lineStyle:{
width: markLineLineStyleWidth
}
}
}
);
}); });
const legendData = chartData.map(item => item.monitor_name);
const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3; const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3;
const maxLegendsPerRowPc = localStorage.getItem("maxLegendsPerRowPc") ? localStorage.getItem("maxLegendsPerRowPc") : 6; const maxLegendsPerRowPc = localStorage.getItem("maxLegendsPerRowPc") ? localStorage.getItem("maxLegendsPerRowPc") : 6;
const autoIncrement = Math.floor((legendData.length - 1) / (this.isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (this.isMobile ? 20 : 28); const autoIncrement = Math.floor((legendData.length - 1) / (this.isMobile ? maxLegendsPerRowMobile : maxLegendsPerRowPc)) * (this.isMobile ? 20 : 28);
const height = 300 + autoIncrement; const height = 300 + autoIncrement;
const gridTop = 40 + autoIncrement; const gridTop = 40 + autoIncrement;
const legendIcon = this.isMobile ? 'rect' : ""; const legendIcon = this.isMobile ? 'rect' : "";
@ -587,39 +630,24 @@
width: 'auto', width: 'auto',
height: height height: height
}); });
const option = { const option = {
color: this.colors,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
textStyle: {
fontSize: fontSize,
color: fontColor
},
grid: {
top: gridTop,
left: gridLeft,
right: gridRight,
},
title: { title: {
show: false show: false,
},
tooltip: {
trigger: 'axis',
backgroundColor: tooltipBackgroundColor,
borderColor: tooltipBorderColor,
textStyle: {
fontSize: fontSize,
color: fontColor
}
},
legend: {
data: legendData,
show: true,
icon: legendIcon,
textStyle: {
fontSize: fontSize,
color: fontColor
},
top: legendTop,
bottom: 0,
left: legendLeft,
padding: legendPadding,
itemWidth: itemWidth,
itemHeight: itemHeight,
}, },
series: seriesData.flat(),
xAxis: { xAxis: {
type: 'time', type: 'time',
data: xAxisData,
axisLabel: { axisLabel: {
textStyle: { textStyle: {
fontSize: fontSize fontSize: fontSize
@ -639,30 +667,57 @@
} }
} }
}, },
legend: {
data: legendData,
show: true,
icon: legendIcon,
textStyle: {
fontSize: fontSize,
color: fontColor
},
top: legendTop,
bottom: 0,
left: legendLeft,
padding: legendPadding,
itemWidth: itemWidth,
itemHeight: itemHeight,
},
tooltip: {
trigger: 'axis',
backgroundColor: tooltipBackgroundColor,
borderColor: tooltipBorderColor,
textStyle: {
fontSize: fontSize,
color: fontColor
},
formatter: function (params) {
let tooltipContent = '';
const formattedTime = new Date(params[0].value[0]).toLocaleString();
tooltipContent += `<span style="line-height:2em">${formattedTime}</span><br>`;
params.forEach(param => {
const formattedTime = new Date(param.value[0]).toLocaleString();
if (!param.seriesName.includes('stack')) {
const name = param.seriesName.replace(/\s\d+(\.\d+)?%$/, '');
tooltipContent += `<span style="line-height:2em">${param.marker} ${name} ${param.value[2].toFixed(1)}% ${param.value[1].toFixed(2)}</span><br>`;
}
});
return tooltipContent;
}
},
dataZoom: [ dataZoom: [
{ {
type: 'slider', type: 'slider',
start: 0, start: 0,
end: 100 end: 100
} }
], ]
series: seriesData,
textStyle: {
fontSize: fontSize,
color: fontColor
},
grid: {
top: gridTop,
left: gridLeft,
right: gridRight
}
}; };
chart.setOption(option); chart.setOption(option);
}, },
reloadCharts() { // 重新加载所有图表 reloadCharts() {
this.servers.forEach(node => { this.servers.forEach(node => {
const id = node.ID; const id = node.ID;
const chartData = this.chartDataList[id - 1]; const chartData = this.chartDataList[id];
if (chartData) { if (chartData) {
this.renderCharts(id,true); this.renderCharts(id,true);
} }

View File

@ -15,7 +15,7 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="chartTitle"><i class="chartCountryCode" :class="'fi fi-' + chartCountryCode"></i> @#chartTitle#@</div> <div class="chartTitle" @click="showCharts(nextServerId)"><i class="chartCountryCode" :class="'fi fi-' + chartCountryCode"></i> @#chartTitle#@</div>
<div id="chartbox" style="width:100%;height:auto;"></div> <div id="chartbox" style="width:100%;height:auto;"></div>
</div> </div>
{{template "theme-server-status/footer" .}} {{template "theme-server-status/footer" .}}
@ -32,7 +32,8 @@
chartTitle: '', chartTitle: '',
chartCountryCode: '', chartCountryCode: '',
chart: null, chart: null,
currentServerId: '' currentServerId: '',
nextServerId: '',
}, },
mixins: [mixinsVue], mixins: [mixinsVue],
created() { created() {
@ -66,13 +67,13 @@
if(!this.chartDataList[id]) return; if(!this.chartDataList[id]) return;
if(this.chart) this.disposeCharts(this.chart); if(this.chart) this.disposeCharts(this.chart);
this.currentServerId = id; this.currentServerId = id;
this.nextServerId = this.getNextServerId(id);
this.chartCountryCode = this.getServerCountryCode(id); this.chartCountryCode = this.getServerCountryCode(id);
this.chartTitle = this.chartDataList[id][0].server_name; this.chartTitle = this.chartDataList[id][0].server_name;
const chartData = this.chartDataList[id]; const chartData = this.chartDataList[id];
const chartContainer = document.getElementById('chartbox'); const chartContainer = document.getElementById('chartbox');
// 定义图表参数值
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300; const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
const autoheight = this.isMobile ? (window.innerHeight - 200) : (window.innerHeight - 250); const autoheight = this.isMobile ? (window.innerHeight - 180) : (window.innerHeight - 250);
const fontSize = this.isMobile ? 10 : 14; const fontSize = this.isMobile ? 10 : 14;
const gridLeft = (MaxTCPPingValue > 500) ? (this.isMobile ? 36 : 42) : (this.isMobile ? 30 : 36); const gridLeft = (MaxTCPPingValue > 500) ? (this.isMobile ? 36 : 42) : (this.isMobile ? 30 : 36);
const gridRight = this.isMobile ? 12 : 20; const gridRight = this.isMobile ? 12 : 20;
@ -86,18 +87,27 @@
const tooltipBorderColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.9)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,1)"); const tooltipBorderColor = this.theme == "dark" ? (this.semiTransparent ? "rgba(28,29,38,0.9)" : "rgba(28,29,38,1)") : (this.semiTransparent ? "rgba(255,255,255,0.9)" : "rgba(255,255,255,1)");
const lineStyleWidth = this.isMobile ? 1 : 2; const lineStyleWidth = this.isMobile ? 1 : 2;
const splitLineWidth = this.isMobile ? 0.5 : 1; const splitLineWidth = this.isMobile ? 0.5 : 1;
const markPointSymbolSize = this.isMobile ? 30 : 42; const markPointSymbolSize = this.isMobile ? 36 : 42;
const markPointItemStyleOpacity = this.semiTransparent ? 1 : 0.9; const markPointItemStyleOpacity = this.semiTransparent ? 1 : 1;
const markPointFontSize = this.isMobile ? 8 : 10; const markPointFontSize = this.isMobile ? 8 : 10;
const markLineItemStyleOpacity = this.semiTransparent ? 1 : 0.75; const markLineItemStyleOpacity = this.semiTransparent ? 1 : 0.75;
const markLineLineStyleWidth = this.isMobile ? 0.15 : 0.3; const markLineLineStyleWidth = this.isMobile ? 0.15 : 0.3;
const showLoadingMaskColor = this.theme == "dark" ? 'rgba(0, 0, 0, 0)' : 'rgba(255, 255, 255, 0)';
const showLoadingTextColor = this.theme == "dark" ? 'rgba(241, 241, 241, 1)' : 'rgba(0, 0, 0, 1)';
const showLoadingColor = this.theme == "dark" ? '#D2B206' : '#FFDF32';
this.chart = echarts.init(chartContainer, chartTheme, { // init图表 this.chart = echarts.init(chartContainer, chartTheme, { // init图表
renderer: 'canvas', renderer: 'canvas',
useDirtyRect: false, useDirtyRect: false,
width: 'auto', width: 'auto',
height: autoheight, height: autoheight,
}); });
// 获取图表数据 this.chart.showLoading({
text: 'loading',
textColor: showLoadingTextColor,
color: showLoadingColor,
maskColor: showLoadingMaskColor,
zlevel: 2
});
let legendData = []; let legendData = [];
let seriesData = []; let seriesData = [];
chartData.forEach((item,key)=> { chartData.forEach((item,key)=> {
@ -112,9 +122,6 @@
loss += 1; loss += 1;
const lossrate = 100 * loss / (index + 1); const lossrate = 100 * loss / (index + 1);
if(lossrate != 100) { if(lossrate != 100) {
data['main'].push(
[item.created_at[index], MaxTCPPingValue, lossrate]
);
data['markLine'].push({ data['markLine'].push({
xAxis: item.created_at[index], xAxis: item.created_at[index],
label: { show: false }, label: { show: false },
@ -143,11 +150,9 @@
); );
} }
}); });
// 处理legendData
totalLossRate = ((loss / item.created_at.length) * 100).toFixed(1); totalLossRate = ((loss / item.created_at.length) * 100).toFixed(1);
legendName = `${item.monitor_name} ${totalLossRate}%`; legendName = `${item.monitor_name} ${totalLossRate}%`;
legendData.push(legendName); legendData.push(legendName);
// 处理seriesData
seriesData.push( seriesData.push(
{ {
name: legendName, name: legendName,
@ -228,29 +233,22 @@
}); });
// 设置图表配置项 // 设置图表配置项
const option = { const option = {
// 全局调色盘
color: this.colors, color: this.colors,
// 背景颜色
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
// 文字样式
textStyle: { textStyle: {
fontSize: fontSize, fontSize: fontSize,
color: fontColor color: fontColor
}, },
// 图表网格设置
grid: { grid: {
top: gridTop, top: gridTop,
left: gridLeft, left: gridLeft,
right: gridRight, right: gridRight,
bottom: gridBottom bottom: gridBottom
}, },
// 图表标题设置
title: { title: {
show: false, show: false,
}, },
// 图表系列数据设置
series: seriesData.flat(), series: seriesData.flat(),
// X轴设置
xAxis: { xAxis: {
type: 'time', type: 'time',
axisLabel: { axisLabel: {
@ -259,7 +257,6 @@
} }
} }
}, },
// Y轴设置
yAxis: { yAxis: {
type: 'value', type: 'value',
axisLabel: { axisLabel: {
@ -273,7 +270,6 @@
} }
} }
}, },
// 图例设置
legend: { legend: {
data: legendData, data: legendData,
show: true, show: true,
@ -289,7 +285,6 @@
itemWidth: itemWidth, itemWidth: itemWidth,
itemHeight: itemHeight, itemHeight: itemHeight,
}, },
// 提示框设置
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
backgroundColor: tooltipBackgroundColor, backgroundColor: tooltipBackgroundColor,
@ -312,7 +307,6 @@
return tooltipContent; return tooltipContent;
} }
}, },
// 数据缩放设置
dataZoom: [ dataZoom: [
{ {
type: 'slider', type: 'slider',
@ -321,17 +315,16 @@
} }
] ]
}; };
// 设置图表的配置选项 setTimeout(() => {
this.chart.setOption(option); this.chart.hideLoading();
this.chart.setOption(option);
}, 1000);
}, },
reloadCharts() { // 重新加载所有图表 reloadCharts() {
this.servers.forEach(node => { const chartData = this.chartDataList[this.currentServerId];
const id = node.ID; if (chartData) {
const chartData = this.chartDataList[id]; this.renderCharts(this.currentServerId,true);
if (chartData) { }
this.renderCharts(id,true);
}
});
}, },
disposeCharts(chart){ disposeCharts(chart){
chart.dispose(); chart.dispose();
@ -341,6 +334,16 @@
const result = this.servers.find(item => item.ID == id); const result = this.servers.find(item => item.ID == id);
return result.Host.CountryCode ? result.Host.CountryCode : 'rb'; return result.Host.CountryCode ? result.Host.CountryCode : 'rb';
}, },
getNextServerId(id) {
const currentIndex = this.servers.findIndex(item => item.ID === id);
if (currentIndex === -1) {
return this.servers[0].ID;
}
// 判断是否有下一个元素
const nextIndex = currentIndex + 1;
// 如果有下一个元素,返回下一个元素的 ID否则返回第一个元素的 ID
return nextIndex < this.servers.length ? this.servers[nextIndex].ID : this.servers[0].ID;
},
initSearch() { initSearch() {
$('#dropdown-search').on('keyup', function() { $('#dropdown-search').on('keyup', function() {
var searchTerm = $(this).val().toLowerCase(); var searchTerm = $(this).val().toLowerCase();