nezha/resource/template/theme-server-status/service.html
nap0o b701efd9b5
server-status和default主题:feat & improve & fix (#420)
* feat & improve & fix
1. 增加WebAppManifest文件,实现把哪吒监控网页伪装成app放在移动端桌面
2. vps地图分布图增加数据异步加载loading效果
3. 修复echart图表纵坐标轴因数值过大显示不全bug

* 刷新CDN缓存

* fix footer

* 4.提升service页echarts图表相关体验
5.用设置基准1vh的方法解决footer页脚位置在移动端各种浏览器显示不一致的问题
6.修复section标签的使用位置

* 刷新CDN缓存
2024-09-06 23:31:38 +08:00

241 lines
10 KiB
HTML
Vendored
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{define "theme-server-status/service"}}
{{template "theme-server-status/header" .}}
{{template "theme-server-status/menu" .}}
<!-- showGroup true -->
<template v-if="showGroup">
<div v-if="servicesTag.length === 0" class="container-fluid content" style="min-height: .01%;overflow-x: auto;">
<p>No Valid Service Monitor Configuration Entries Found. Please Verify in the <a href="/monitor">Admin Panel</a>.</p>
</div>
<div v-else class="container-fluid content" style="min-height: .01%;overflow-x: auto;" v-for="group in servicesTag">
{{template "theme-server-status/service-group-true" .}}
</div>
</template>
<!-- showGroup false -->
<template v-else>
<div v-if="servicesNoTag.length === 0" class="container-fluid content" style="min-height: .01%;overflow-x: auto;">
<p>No Valid Service Monitor Configuration Entries Found. Please Verify in the <a href="/monitor">Admin Panel</a>.</p>
</div>
<div v-else class="container-fluid content" style="min-height: .01%;overflow-x: auto;">
{{template "theme-server-status/service-group-false" .}}
</div>
</template>
{{if .CycleTransferStats}}
<div class="container-fluid content table-responsive">
<table class="table table-striped table-condensed table-hover">
<thead>
<tr class="node-group-tag">
<th colspan="16" style="border:none;">
{{tr "CycleTransferStats"}}
</th>
</tr>
<tr class="node-group-cell">
<th class="node-cell center">ID</th>
<th class="node-cell center">{{tr "Rules"}}</th>
<th class="node-cell center">{{tr "Server"}}</th>
<th class="node-cell center">{{tr "From"}}</th>
<th class="node-cell center">{{tr "To"}}</th>
<th class="node-cell center">MAX</th>
<th class="node-cell center">MIN</th>
<th class="node-cell center">{{tr "NextCheck"}}</th>
<th class="node-cell center">{{tr "CurrentUsage"}}</th>
<th class="node-cell center">{{tr "Transleft"}}</th>
</tr>
</thead>
<tbody>
{{range $id, $stats := .CycleTransferStats}}
{{range $innerId, $transfer := $stats.Transfer}}
{{$TransLeftPercent := TransLeftPercent (UintToFloat $transfer) (UintToFloat $stats.Max)}}
<tr>
<td class="node-cell center">{{$id}}</td>
<td class="node-cell center">{{$stats.Name}}</td>
<td class="node-cell center">{{index $stats.ServerName $innerId}}</td>
<td class="node-cell center">{{$stats.From|tf}}</td>
<td class="node-cell center">{{$stats.To|tf}}</td>
<td class="node-cell center">{{$stats.Max|bf}}</td>
<td class="node-cell center">{{$stats.Min|bf}}</td>
<td class="node-cell center">{{(index $stats.NextUpdate $innerId)|sft}}</td>
<td class="node-cell center">{{$transfer|bf}}</td>
<td class="node-cell center">
<div class="progress">
<div style="width: {{$TransLeftPercent}}%" :class="'progress-bar progress-bar-' + toSSBar('{{TransClassName $TransLeftPercent}}')">
<small style="display: inline-block;width: max-content;">{{TransLeft $stats.Max $transfer}} / {{$TransLeftPercent}} %</small>
</div>
</div>
</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
{{end}}
{{template "theme-server-status/footer" .}}
<script>
new Vue({
el: '#app',
delimiters: ['@#', '#@'],
data: {
page: 'service',
defaultTemplate: {{.Conf.Site.Theme}},
templates: {{.Themes}},
services: {{.Services}},
servicesTag: [],
servicesNoTag: []
},
created() {
this.servicesTag = this.groupingData(this.initData(this.services),"type");
this.servicesNoTag = this.initData(this.services);
},
mounted() {
this.initTooltip();
},
mixins: [mixinsVue],
methods: {
toSSBar(n) {
switch (n) {
case "fine":
return "success"
case "offline":
return "danger"
case "error":
return "danger"
case "warning":
return "warning"
}
return n
},
initData(services) {
let nodes = [];
for (let key in services) {
const service = services[key];
let node = {
type: service.Monitor.Type,
name: service.Monitor.Name,
currentUp: parseInt(service.CurrentUp),
currentDown: parseInt(service.CurrentDown),
totalUp: parseInt(service.TotalUp),
totalDown: parseInt(service.TotalDown),
up: service.Up,
down: service.Down,
delay: service.Delay,
avgDelay: parseInt(this.getAvgDelay(service.Delay)) + "ms",
health: this.getStateInfo(this.getPercent(service.CurrentUp, service.CurrentDown)),
totalUpTime: this.getProgressInfo(this.getPercent(service.TotalUp, service.TotalDown))
};
nodes.push(node);
};
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
node.dayDetail = this.getDayTails(node)
}
return nodes
},
initTooltip() {
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
},
getPercent(up, down) {
if (!up) {
up = 0;
}
if (!down) {
down = 0
}
const currentUp = parseInt(up)
const currentDown = parseInt(down)
const total = currentUp + currentDown
if (total === 0) {
if (currentUp > 0) {
return 100
}
return 0
} else if (currentUp === 0) {
return 0.00001 / total * 100
}
return this.toFixed2(currentUp / total * 100)
},
getDayTails(service) {
const result = [];
for (let i = 0; i < service.up.length; i++) {
const up = service.up[i];
const down = service.down[i];
let delay = Number(service.delay[i]);
let percent = this.getPercent(up, down);
if (percent <= 0) {
percent = 0;
}
let className = this.getStateInfo(percent).className;
let available = '{{tr "Availability"}}';
let averageLatency = '{{tr "AverageLatency"}}';
const text = `${this.beforeDay(service.up.length - i - 1)}${available}${Number(percent).toFixed(3)}%${averageLatency}${Number(delay).toFixed(3)}ms`;
result.push({
text, className
});
}
return result;
},
beforeDay(days) {
const today = new Date();
today.setDate(today.getDate() - days);
// 获取月份和日期并格式化
const month = (today.getMonth() + 1).toString().padStart(2, '0');
const day = today.getDate().toString().padStart(2, '0');
return `${month}-${day}`;
},
getStateInfo(percent) {
if (percent < 0) {
percent = 0;
}
const result = {
className: "good",
text: "",
percent
}
if (percent === 0) {
result.className = ""
result.text = '{{tr "StatusNoData"}}'
} else if (percent > 95) {
result.className = "good"
result.text = '{{tr "StatusGood"}}'
} else if (percent > 80) {
result.className = "warning"
result.text = '{{tr "StatusLowAvailability"}}'
} else {
result.className = "danger"
result.text = '{{tr "StatusDown"}}'
}
return result;
},
getProgressInfo(percent) {
const result = this.getStateInfo(percent);
result.style = `width: ${parseInt(percent)}%`;
result.percent = Number(percent).toFixed(2);
const className = result.className;
if (className === "good") {
result.className = 'progress-bar progress-bar-success'
} else if (className === "warning") {
result.className = 'progress-bar progress-bar-warning'
} else if (className === "danger") {
result.className = 'progress-bar progress-bar-danger'
} else {
result.className = ""
result.style = "width: 100%"
}
return result
},
getAvgDelay(array) {
const { sum, count } = array.reduce((acc, value) => {
if (value !== 0) {
acc.sum += value;
acc.count += 1;
}
return acc;
}, { sum: 0, count: 0 });
return count > 0 ? sum / count : 0;
}
}
})
</script>
{{end}}