mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-23 05:08:13 -05:00
b701efd9b5
* feat & improve & fix 1. 增加WebAppManifest文件,实现把哪吒监控网页伪装成app放在移动端桌面 2. vps地图分布图增加数据异步加载loading效果 3. 修复echart图表纵坐标轴因数值过大显示不全bug * 刷新CDN缓存 * fix footer * 4.提升service页echarts图表相关体验 5.用设置基准1vh的方法解决footer页脚位置在移动端各种浏览器显示不一致的问题 6.修复section标签的使用位置 * 刷新CDN缓存
241 lines
10 KiB
HTML
Vendored
241 lines
10 KiB
HTML
Vendored
{{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}} |