Compare commits

..
2 Commits
Author SHA1 Message Date
nap0o e9428d5757 improve & fix : 主题优化及bug修复 (#409)
* 主题优化及bug修复

default主题
1.修复主box过宽导致的系列问题(pc和移动端)
2.network页面适配深色模式和浅色模式

status-server主题
1. network页面折线图取数逻辑优化,丢包率一直是100%时,不显示丢包markline线
2. 隐藏所有table横向滚动条
3. 关闭折线图hover效果,大幅提升echarts图表渲染速度
4. 移动端页底显示位置优化

* 刷新cdn缓存

* 修复default深色模式在ios设备不生效bug

* 1. 恢复主baox 1680px宽度,用其他方式修复上一版存在bug(移动端左右留空不一致等)
2. 首页echarts自动适配深色浅色模式

* fix
2024-08-17 11:14:28 +08:00
naiba daab64d232 feat: 管理后台自定义代码 2024-08-14 00:24:17 +08:00
24 changed files with 250 additions and 242 deletions
+1 -4
View File
@@ -99,7 +99,6 @@ func (p *commonPage) service(c *gin.Context) {
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
"Services": res.([]interface{})[0],
"CycleTransferStats": res.([]interface{})[1],
"CustomCode": singleton.Conf.Site.CustomCode,
}))
}
@@ -204,7 +203,6 @@ func (cp *commonPage) network(c *gin.Context) {
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/network"), mygin.CommonEnvironment(c, gin.H{
"Servers": string(serversBytes),
"MonitorInfos": string(monitorInfos),
"CustomCode": singleton.Conf.Site.CustomCode,
"MaxTCPPingValue": singleton.Conf.MaxTCPPingValue,
}))
}
@@ -254,8 +252,7 @@ func (cp *commonPage) home(c *gin.Context) {
return
}
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/home"), mygin.CommonEnvironment(c, gin.H{
"Servers": string(stat),
"CustomCode": singleton.Conf.Site.CustomCode,
"Servers": string(stat),
}))
}
+2
View File
@@ -908,6 +908,7 @@ type settingForm struct {
Theme string
DashboardTheme string
CustomCode string
CustomCodeDashboard string
ViewPassword string
IgnoredIPNotification string
IPChangeNotificationTag string // IP变更提醒的通知组
@@ -973,6 +974,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
singleton.Conf.Site.Theme = sf.Theme
singleton.Conf.Site.DashboardTheme = sf.DashboardTheme
singleton.Conf.Site.CustomCode = sf.CustomCode
singleton.Conf.Site.CustomCodeDashboard = sf.CustomCodeDashboard
singleton.Conf.Site.ViewPassword = sf.ViewPassword
singleton.Conf.Oauth2.Admin = sf.Admin
// 保证NotificationTag不为空
+7 -6
View File
@@ -80,12 +80,13 @@ type Config struct {
Debug bool // debug模式开关
Language string // 系统语言,默认 zh-CN
Site struct {
Brand string // 站点名称
CookieName string // 浏览器 Cookie 名称
Theme string
DashboardTheme string
CustomCode string
ViewPassword string // 前台查看密码
Brand string // 站点名称
CookieName string // 浏览器 Cookie 名称
Theme string
DashboardTheme string
CustomCode string
CustomCodeDashboard string
ViewPassword string // 前台查看密码
}
Oauth2 struct {
Type string
+2
View File
@@ -26,6 +26,8 @@ func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {
data["Version"] = singleton.Version
data["Conf"] = singleton.Conf
data["Themes"] = model.Themes
data["CustomCode"] = singleton.Conf.Site.CustomCode
data["CustomCodeDashboard"] = singleton.Conf.Site.CustomCodeDashboard
// 是否是管理页面
data["IsAdminPage"] = adminPage[data["MatchedPath"].(string)]
// 站点标题
+1 -2
View File
@@ -37,8 +37,7 @@ func ValidateViewPassword(opt ValidateViewPasswordOption) gin.HandlerFunc {
}
if opt.IsPage {
c.HTML(http.StatusOK, GetPreferredTheme(c, "/viewpassword"), CommonEnvironment(c, gin.H{
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
"CustomCode": singleton.Conf.Site.CustomCode,
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
}))
} else {
+3
View File
@@ -331,6 +331,9 @@ other = "Frontend Theme"
[CustomCodes]
other = "Custom Codes (Style and Script)"
[CustomCodesDashboard]
other = "Custom Codes for Dashboard"
[AccessPassword]
other = "Frontend Access Password"
+3
View File
@@ -331,6 +331,9 @@ other = "Tema de Frontend"
[CustomCodes]
other = "Códigos Personalizados (Incluye style y script)"
[CustomCodesDashboard]
other = "Custom Codes for Dashboard"
[AccessPassword]
other = "Contraseña de Acceso al Frontend"
+3
View File
@@ -331,6 +331,9 @@ other = "前台界面主题"
[CustomCodes]
other = "自定义代码(包括 style 和 script"
[CustomCodesDashboard]
other = "Custom Codes for Dashboard"
[AccessPassword]
other = "前台访问密码"
+3
View File
@@ -331,6 +331,9 @@ other = "前台界面主題"
[CustomCodes]
other = "自定義代碼(包括 style 和 script"
[CustomCodesDashboard]
other = "Custom Codes for Dashboard"
[AccessPassword]
other = "前台訪問密碼"
+110 -117
View File
@@ -1,148 +1,141 @@
.ui.container {
width: 95vw !important;
max-width: 1680px !important;
html[nz-theme='dark'] body {
background-color: #121212 !important;
color: #fff !important;
}
html[nz-theme='dark'] {
body {
background-color: #121212 !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.menu {
background-color: #282828 !important;
}
.ui.menu {
background-color: #282828 !important;
}
html[nz-theme='dark'] .ui.menu * {
color: #fff !important;
}
.ui.menu * {
color: #fff !important;
}
html[nz-theme='dark'] .accordion {
background-color: #282828 !important;
}
.accordion {
background-color: #282828 !important;
}
html[nz-theme='dark'] .accordion .title {
color: #fff !important;
}
.accordion .title {
color: #fff !important;
}
html[nz-theme='dark'] .ui.card {
background-color: #3f3f3f !important;
border: none !important;
box-shadow: none !important;
}
.ui.card {
background-color: #3f3f3f !important;
border: none !important;
box-shadow: none !important;
}
html[nz-theme='dark'] .header {
color: #fff !important;
}
.header {
color: #fff !important;
}
html[nz-theme='dark'] .description {
color: #fff !important;
}
.description {
color: #fff !important;
}
html[nz-theme='dark'] .icon {
color: #fff !important;
}
.icon {
color: #fff !important;
}
html[nz-theme='dark'] .ui.popup {
background-color: #575757 !important;
color: #fff !important;
}
.ui.popup {
background-color: #575757 !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.table {
background-color: #282828 !important;
color: #fff !important;
}
.ui.table {
background-color: #282828 !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui thead th {
background-color: #3f3f3f !important;
color: #fff !important;
}
.ui thead th {
background-color: #3f3f3f !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.buttons .button {
background-color: #3f3f3f !important;
color: #fff !important;
}
.ui.buttons .button {
background-color: #3f3f3f !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.modal {
background-color: #282828 !important;
}
.ui.modal {
background-color: #282828 !important;
}
html[nz-theme='dark'] .ui.modal * {
color: #fff !important;
}
.ui.modal * {
color: #fff !important;
}
html[nz-theme='dark'] textarea,
html[nz-theme='dark'] input,
html[nz-theme='dark'] select,
html[nz-theme='dark'] .dropdown {
background-color: #3f3f3f !important;
color: #fff !important;
}
textarea,
input,
select,
.dropdown {
background-color: #3f3f3f !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.message {
background-color: unset !important;
}
.ui.message {
background-color: unset !important;
}
html[nz-theme='dark'] .ui.dropdown .menu {
background-color: #575757 !important;
color: #fff !important;
}
.ui.dropdown .menu {
background-color: #575757 !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.modal>.header {
background-color: #3f3f3f !important;
}
.ui.modal>.header {
background-color: #3f3f3f !important;
}
html[nz-theme='dark'] .ui.modal>.content {
background-color: #282828 !important;
}
.ui.modal>.content {
background-color: #282828 !important;
}
html[nz-theme='dark'] .ui.modal>.actions {
background-color: #3f3f3f !important;
}
.ui.modal>.actions {
background-color: #3f3f3f !important;
}
html[nz-theme='dark'] #alert {
background-color: #3f3f3f !important;
color: #fff !important;
}
#alert {
background-color: #3f3f3f !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.form .field>label {
color: unset !important;
}
.ui.form .field>label {
color: unset !important;
}
html[nz-theme='dark'] .ui.segment {
background-color: #3f3f3f !important;
}
.ui.segment {
background-color: #3f3f3f !important;
}
html[nz-theme='dark'] .ui.segment textarea,
html[nz-theme='dark'] input,
html[nz-theme='dark'] select,
html[nz-theme='dark'] .dropdown {
background-color: #575757 !important;
color: #fff !important;
}
.ui.segment textarea,
input,
select,
.dropdown {
background-color: #575757 !important;
color: #fff !important;
}
html[nz-theme='dark'] form label {
color: #fff !important;
}
form label {
color: #fff !important;
}
html[nz-theme='dark'] .ui.inverted.segment {
background-color: #121212 !important;
}
.ui.inverted.segment {
background-color: #121212 !important;
}
html[nz-theme='dark'] .ui.inverted.segment * {
color: #8b8b8b !important;
}
.ui.inverted.segment * {
color: #8b8b8b !important;
}
html[nz-theme='dark'] .menu .dropdown {
background-color: #282828 !important;
color: #fff !important;
}
.menu .dropdown {
background-color: #282828 !important;
color: #fff !important;
}
html[nz-theme='dark'] .ui.menu .ui.dropdown .menu>.item {
color: #fff !important;
}
.ui.menu .ui.dropdown .menu>.item {
color: #fff !important;
}
.login .ui.message{
color: #8b8b8b !important;
}
}
html[nz-theme='dark'] .login .ui.message{
color: #8b8b8b !important;
}
+5
View File
@@ -9,6 +9,11 @@ td {
word-break: break-all;
}
.ui.container{
width: 95vw !important;
max-width: 1680px !important;
}
.nb-container {
padding-top: 75px;
min-height: 100vh;
+5 -19
View File
@@ -1,14 +1,5 @@
/* 屏幕适配 */
@media only screen and (min-width:1200px) {
.ui.container {
width: 95% !important;
font-size: 90% !important;
max-width: 1300px !important;
}
}
@media only screen and (max-width:767px) {
.ui.card>.content>.header:not(.ui),
.ui.cards>.card>.content>.header:not(.ui) {
margin-top: 0.4em !important;
@@ -41,16 +32,6 @@ i.fi {
margin: 0px 6px 0px 2px;
}
body {
content: " " !important;
background: fixed !important;
z-index: -1 !important;
top: 0 !important;
right: 0 !important;
bottom: 0 !important;
left: 0 !important;
}
td {
word-wrap: break-word;
word-break: break-all;
@@ -63,6 +44,11 @@ td {
margin-bottom: -47px;
}
.ui.container {
width: 95%;
max-width: 1680px !important;
}
#app .ui.fluid.accordion {
margin-bottom: 1rem;
}
+5 -2
View File
@@ -9,6 +9,10 @@ body {
margin: 0 auto;
}
.container-fluid::-webkit-scrollbar {
display: none;
}
.nezha {
min-height: calc(100vh - 100px);
}
@@ -470,14 +474,13 @@ footer p{
background-image: url(/static/theme-server-status/img/rb.png);
}
@media only screen and (max-width: 767px) {
body {
font-size: 10px !important;
padding-top:60px !important;
}
.nezha {
min-height: calc(100vh - 80px);
min-height: calc(74.5vh);
}
.content {
padding: 0;
+1 -2
View File
@@ -14,8 +14,7 @@
<script>
(function () {
updateLang({{.LANG }});
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
}) ();
})();
</script>
</body>
+4
View File
@@ -13,6 +13,10 @@
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
<link rel="stylesheet" type="text/css" href="/static/main.css?v20240813">
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240813">
<script>
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
</script>
</head>
{{if ts .CustomCodeDashboard}} {{.CustomCodeDashboard|safe}} {{end}}
<body>
{{end}}
+7 -3
View File
@@ -41,6 +41,10 @@
<label>{{tr "CustomCodes"}}</label>
<textarea name="CustomCode">{{.Conf.Site.CustomCode}}</textarea>
</div>
<div class="field">
<label>{{tr "CustomCodesDashboard"}}</label>
<textarea name="CustomCodeDashboard">{{.Conf.Site.CustomCodeDashboard}}</textarea>
</div>
<div class="field">
<label>{{tr "AccessPassword"}}</label>
<input type="text" name="ViewPassword" placeholder="" value="{{.Conf.Site.ViewPassword}}">
@@ -128,13 +132,13 @@
})
$('.checkbox').checkbox()
$('#settingForm').find("select[name=Cover]").val({{.Conf.Cover }});
{{if .Conf.EnableIPChangeNotification}}
{{ if .Conf.EnableIPChangeNotification}}
$('.checkbox.ip-change').checkbox('set checked')
{{ end }}
{{if .Conf.EnablePlainIPInNotification}}
{{ if .Conf.EnablePlainIPInNotification}}
$('.checkbox.plain-ip').checkbox('set checked')
{{ end }}
{{if .Conf.DisableSwitchTemplateInFrontend }}
{{ if .Conf.DisableSwitchTemplateInFrontend }}
$('.checkbox.disable-switch-template').checkbox('set checked')
{{ end }}
</script>
-3
View File
@@ -14,11 +14,8 @@
// console.log("currentBackendTheme:",currentBackendTheme);
}
showSwitchTemplate({{ .Themes }}, {{ .Conf.Site.Theme }})
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
</script>
{{ end }}
<script>
</script>
</body>
</html>
{{end}}
+8 -4
View File
@@ -1,6 +1,7 @@
{{define "theme-default/header"}}
<!DOCTYPE html>
<html lang="{{.Conf.Language}}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -13,15 +14,18 @@
<link rel="stylesheet" href="https://unpkg.com/[email protected]/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/css/flag-icons.min.css">
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240616">
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240813">
<link rel="stylesheet" type="text/css" href="/static/theme-default/css/main.css?v20240816">
<link rel="stylesheet" type="text/css" href="/static/darkmode.css?v20240816">
<script src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/semantic.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
<script src="/static/semantic-ui-alerts.min.js"></script>
<script src="/static/theme-default/js/mixin.js?v20240302"></script>
<script>
document.documentElement.setAttribute('nz-theme', window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
</script>
</head>
<body>
{{end}}
{{end}}
+2 -5
View File
@@ -240,12 +240,12 @@
const itemHeight = isMobile ? 10 : 10;
const gridLeft = 25;
const gridRight = 12;
const fontColor = "rgba(0, 0, 0, 0.68)";
const backgroundColor = '';
const borderColor = "#ffffff";
const chartData = this.chartDataList[id - 1];
const chartContainer = this.$refs[`chart${id}`][0];
const chart = echarts.init(chartContainer, null, {
const chartTheme = $('html').attr('nz-theme') == "dark" ? "dark" : "";
const chart = echarts.init(chartContainer, chartTheme, {
renderer: 'canvas',
useDirtyRect: false,
width: 'auto',
@@ -283,7 +283,6 @@
trigger: 'axis',
textStyle: {
fontSize: fontSize,
color: fontColor
}
},
legend: {
@@ -292,7 +291,6 @@
show: true,
textStyle: {
fontSize: fontSize,
color: fontColor
},
lineStyle: {
cap: 'butt'
@@ -332,7 +330,6 @@
series: seriesData,
textStyle: {
fontSize: fontSize,
color: fontColor
},
grid: {
top: '30',
+6 -5
View File
@@ -18,7 +18,7 @@
</div>
</div>
<div class="ui container">
<div ref="chartDom" style="border-radius: 28px; margin-top: 15px;height: 520px;overflow: hidden"></div>
<div ref="chartDom" style="margin-top: 15px;height: 520px;overflow: hidden"></div>
</div>
</div>
@@ -65,10 +65,10 @@
}
},
grid: {
left: '8%',
right: '8%',
left: this.isMobile ? '8%' : '3.8%',
right: this.isMobile ? '8%' : '3.8%',
},
backgroundColor: 'rgba(255, 255, 255, 0.8)',
backgroundColor: '',
toolbox: {
feature: {
dataZoom: {
@@ -243,7 +243,8 @@
return str.includes('Windows')
},
renderChart() {
this.myChart = echarts.init(this.$refs.chartDom);
const chartTheme = $('html').attr('nz-theme') == "dark" ? "dark" : "";
this.myChart = echarts.init(this.$refs.chartDom,chartTheme);
this.myChart.setOption(this.option);
},
resizeHandle () {
+32 -32
View File
@@ -1,36 +1,36 @@
{{define "theme-server-status/footer"}}
</div>
<footer class="container-fluid">
<p>{{ .Conf.Site.Brand }} | Theme ServerStatus | Powered by <a target="_blank" href="https://github.com/naiba/nezha">{{tr "NezhaMonitoring"}}</a> {{.Version}}</p>
</footer>
<aside class="toolbox">
<template v-if="showTools">
<span v-if="page=='index' && countryMapChartData.length!=0" class="showMapChart">
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
</span>
<span class="toggleView">
<i v-if="showGroup" @click="toggleShowGroup" class="show-nogroup bi bi-justify"></i>
<i v-else @click="toggleShowGroup" class="show-group bi bi-view-stacked"></i>
</span>
<span class="toggleSemiTransparent" @click="toggleSemiTransparent">
<i class="bi" :class="semiTransparent ? 'bi-droplet' : 'bi-droplet-half'"></i>
</span>
</template>
<span v-if="!showTools">
<i @click="toggleShowTools" class="bi bi-three-dots"></i>
</span>
<span class="setTheme">
<i v-if="theme === 'light'" @click="setTheme('dark')" class="setTheme-dark bi bi-moon-fill"></i>
<i v-else @click="setTheme('light')" class="setTheme-light bi bi-brightness-high-fill"></i>
</span>
<span v-if="showGoTop" class="showGoTop">
<i @click="goTop" class="goTop bi bi-arrow-up"></i>
</span>
</aside>
<template v-if="semiTransparent">
<link rel="stylesheet" href="/static/theme-server-status/css/light.plus.css?v20240807">
<link rel="stylesheet" href="/static/theme-server-status/css/dark.plus.css?v20240807">
</template>
</div>
<footer class="container-fluid">
<p>{{ .Conf.Site.Brand }} | Theme ServerStatus | Powered by <a target="_blank" href="https://github.com/naiba/nezha">{{tr "NezhaMonitoring"}}</a> {{.Version}}</p>
</footer>
<aside class="toolbox">
<template v-if="showTools">
<span v-if="page=='index' && countryMapChartData.length!=0" class="showMapChart">
<i @click="showMapChart" data-toggle="modal" data-target="#mapChartBox" class="bi bi-geo-alt"></i>
</span>
<span class="toggleView">
<i v-if="showGroup" @click="toggleShowGroup" class="show-nogroup bi bi-justify"></i>
<i v-else @click="toggleShowGroup" class="show-group bi bi-view-stacked"></i>
</span>
<span class="toggleSemiTransparent" @click="toggleSemiTransparent">
<i class="bi" :class="semiTransparent ? 'bi-droplet' : 'bi-droplet-half'"></i>
</span>
</template>
<span v-if="!showTools">
<i @click="toggleShowTools" class="bi bi-three-dots"></i>
</span>
<span class="setTheme">
<i v-if="theme === 'light'" @click="setTheme('dark')" class="setTheme-dark bi bi-moon-fill"></i>
<i v-else @click="setTheme('light')" class="setTheme-light bi bi-brightness-high-fill"></i>
</span>
<span v-if="showGoTop" class="showGoTop">
<i @click="goTop" class="goTop bi bi-arrow-up"></i>
</span>
</aside>
<template v-if="semiTransparent">
<link rel="stylesheet" href="/static/theme-server-status/css/light.plus.css?v20240807">
<link rel="stylesheet" href="/static/theme-server-status/css/dark.plus.css?v20240807">
</template>
</div>
{{if ts .CustomCode}}{{.CustomCode|safe}}{{end}}
</body>
+1 -1
View File
@@ -16,7 +16,7 @@
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/echarts.min.js"></script>
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v202408011">
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v202408016">
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v202408011">
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20240811">
<script src="/static/theme-server-status/js/mixin.js?v20240811"></script>
+5 -1
View File
@@ -551,6 +551,10 @@
symbol: 'none',
data: data,
connectNulls: true,
legendHoverLink: false,
emphasis: {
disabled: true
},
lineStyle: {
width: lineStyleWidth
}
@@ -560,7 +564,7 @@
const legendData = chartData.map(item => item.monitor_name);
const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3;
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 gridTop = 40 + autoIncrement;
const legendIcon = this.isMobile ? 'rect' : "";
+34 -36
View File
@@ -12,7 +12,7 @@
</li>
<li class="dropdown-item" v-for="server in servers" @click="showCharts(server.ID)">
<a><i :class="'fi fi-' + (server.Host.CountryCode || 'rb')"></i> @#server.Name#@ <i v-if="server.ID == currentServerId" class="check icon"></i></a>
</li>
</li>
</ul>
</div>
<div class="chartTitle"><i class="chartCountryCode" :class="'fi fi-' + chartCountryCode"></i> @#chartTitle#@</div>
@@ -44,7 +44,6 @@
},
methods: {
showCharts(id) {
const chartContainer = document.getElementById('chartbox');
// 发起数据请求
const url = `/api/v1/monitor/${id}`;
fetch(url)
@@ -64,17 +63,13 @@
});
},
renderCharts(id, reload = false) {
if (!this.chartDataList[id]) return;
this.disposeCharts();
if(!this.chartDataList[id]) return;
if(this.chart) this.disposeCharts(this.chart);
this.currentServerId = id;
this.chartCountryCode = this.getServerCountryCode(id);
this.chartTitle = this.chartDataList[id][0].server_name;
const chartData = this.chartDataList[id];
const chartContainer = document.getElementById('chartbox');
this.chartTitle = chartData[0].server_name;
if (reload) {
const existingChart = echarts.getInstanceByDom(chartContainer);
if (existingChart) existingChart.dispose();
}
// 定义图表参数值
const MaxTCPPingValue = {{.Conf.MaxTCPPingValue}} ? {{.Conf.MaxTCPPingValue}} : 300;
const autoheight = this.isMobile ? (window.innerHeight - 200) : (window.innerHeight - 250);
@@ -111,34 +106,35 @@
let data = { main: [], markLine: []};
item.avg_delay.forEach((avgDelay, index) => {
const threshold = 0.9 * MaxTCPPingValue; // 定义阀值,用于判断是否丢包
const filterAvgDelay = item.avg_delay.filter(value => value !== 0 && value !== MaxTCPPingValue);
const max = Math.max(...filterAvgDelay).toFixed(1);
const autoAvgDelay = 1.05 * max > 0.91 * MaxTCPPingValue ? 1.05 * max : 0.91 * MaxTCPPingValue;
// 定义丢包 1. avgDelay==0 2. avgDelay>=MaxTCPPingValue 3. avgDelay>=threshold
if(avgDelay == 0 || avgDelay >= MaxTCPPingValue){ //绝对丢包
loss += 1;
const lossrate = 100 * loss / (index + 1);
data['main'].push(
[item.created_at[index], autoAvgDelay, lossrate]
);
data['markLine'].push({
xAxis: item.created_at[index],
label: { show: false },
emphasis: { disabled: true },
lineStyle: { type: "solid" }
});
if(lossrate != 100) {
data['main'].push(
[item.created_at[index], MaxTCPPingValue, lossrate]
);
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);
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" }
});
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(
@@ -158,6 +154,10 @@
smooth: true,
symbol: 'none',
connectNulls: true,
legendHoverLink: false,
emphasis: {
disabled: true
},
lineStyle: {
width: lineStyleWidth
},
@@ -216,7 +216,7 @@
});
const maxLegendsPerRowMobile = localStorage.getItem("maxLegendsPerRowMobile") ? localStorage.getItem("maxLegendsPerRowMobile") : 3;
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 = autoheight + autoIncrement;
const gridTop = 40 + autoIncrement;
const legendIcon = this.isMobile ? 'rect' : "";
@@ -333,11 +333,9 @@
}
});
},
disposeCharts(){
if(this.chart) {
this.chart.dispose();
this.chart = null;
}
disposeCharts(chart){
chart.dispose();
chart = null;
},
getServerCountryCode(id){
const result = this.servers.find(item => item.ID == id);