💄 improve: done #10 进度条

This commit is contained in:
naiba 2020-12-19 12:43:03 +08:00
parent 9dbbfac551
commit 13f90e7a1f
7 changed files with 141 additions and 182 deletions

View File

@ -27,7 +27,7 @@
- 默认主题更改进度条颜色示例 - 默认主题更改进度条颜色示例
``` ```
.ui.green.progress> .bar { .ui.fine.progress> .bar {
background-color: pink !important; background-color: pink !important;
} }
``` ```

View File

@ -25,7 +25,7 @@ var (
server string server string
clientSecret string clientSecret string
debug bool debug bool
version = "v9.9.9" version string
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
Use: "nezha-agent", Use: "nezha-agent",

View File

@ -28,6 +28,9 @@ func ServeWeb(port uint) {
"css": func(s string) template.CSS { "css": func(s string) template.CSS {
return template.CSS(s) return template.CSS(s)
}, },
"tag": func(s string) template.HTML {
return template.HTML(`<` + s + `>`)
},
"stf": func(s uint64) string { "stf": func(s uint64) string {
return time.Unix(int64(s), 0).Format("2006年1月2号 15:04") return time.Unix(int64(s), 0).Format("2006年1月2号 15:04")
}, },

View File

@ -1589,12 +1589,6 @@ th {
max-width: 100%; max-width: 100%;
} }
.table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td {
padding: 12px 10px;
line-height: 1.42857143;
vertical-align: middle;
}
.table > thead > tr > th { .table > thead > tr > th {
padding: 16px 10px; padding: 16px 10px;
vertical-align: bottom; vertical-align: bottom;
@ -1622,67 +1616,6 @@ th {
overflow: hidden; overflow: hidden;
} }
.progress {
height: 25px;
overflow: hidden;
background-color: #f5f5f5;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
}
.progress-bar {
float: left;
width: 0;
height: 100%;
font-size: 12px;
line-height: 20px;
color: #fff;
text-align: center;
background-color: #1e88e5;
-webkit-border-radius: 6px;
border-radius: 6px;
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}
.progress-striped .progress-bar, .progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
-webkit-background-size: 40px 40px;
background-size: 40px 40px;
}
.progress.active .progress-bar, .progress-bar.active {
-webkit-animation: progress-bar-stripes 2s linear infinite;
-o-animation: progress-bar-stripes 2s linear infinite;
animation: progress-bar-stripes 2s linear infinite;
}
.progress-bar-success {
background-color: #5cb85c;
}
.progress-striped .progress-bar-success {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.progress-striped .progress-bar-warning {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.progress-striped .progress-bar-danger {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.panel { .panel {
position: relative; position: relative;
display: block; display: block;

View File

@ -89,32 +89,6 @@ table {
font-size: 24px; font-size: 24px;
} }
.location-progress .progress {
margin: 0 0 15px 0;
}
.progress {
background-color: #d5dade;
-webkit-box-shadow: none;
box-shadow: none;
}
.progress-bar {
background: linear-gradient(to right, #44ce78 0%, #43ce9f 100%) !important;
}
.progress-bar-danger {
background: #d9534f !important;
}
.progress-bar-warning {
background: #f0ad4e !important;
}
.progress-sm {
height: 5px;
}
.table { .table {
/* font-size: 0.9rem; */ /* font-size: 0.9rem; */
font-weight: 800; font-weight: 800;
@ -184,3 +158,20 @@ h1 {
} }
} }
} }
.location-progress .bar {
height: 0.5em !important;
margin-bottom: 1em !important;
}
.ui.progress {
margin: unset !important;
}
.ui.progress .bar {
line-height: unset !important;
}
table tr {
height: 3em !important;
}

View File

@ -1,21 +1,21 @@
{{define "theme-default/home"}} {{define "theme-default/home"}}
{{template "common/header" .}} {{template "common/header" .}}
{{if ts .CustomCSS}} {{if ts .CustomCSS}}
<style> {{tag "style"}}
{{.CustomCSS|css}} {{.CustomCSS|css}}
</style> {{tag "/style"}}
{{end}} {{end}}
{{template "common/menu" .}} {{template "common/menu" .}}
<div class="nb-container"> <div class="nb-container">
<div class="ui container"> <div class="ui container">
<div class="ui four stackable status cards"> <div class="ui four stackable status cards">
<div v-for='server in servers' :id="server.ID" class="card"> <div v-for='server in servers' :id="server.ID" class="card">
<div class="content" v-if='isAlive(server)'> <div class="content" v-if='server.Host'>
<div class="header"><i :class="server.Host.CountryCode + ' flag'"></i><i <div class="header"><i :class="server.Host.CountryCode + ' flag'"></i><i
v-if='server.Host.Platform == "darwin"' class="apple icon"></i><i v-if='server.Host.Platform == "darwin"' class="apple icon"></i><i
v-if='server.Host.Platform == "linux"' class="linux icon"></i><i v-if='server.Host.Platform == "linux"' class="linux icon"></i><i
v-if='server.Host.Platform == "windows"' class="windows icon"></i><i v-if='server.Host.Platform == "windows"' class="windows icon"></i><i
v-if='server.Host.Platform == "freebsd"' class="freebsd icon"></i>@#server.Name#@ v-if='server.Host.Platform == "freebsd"' class="freebsd icon"></i>@#server.Name + (server.live?'':' [已离线]')#@
<i class="yellow info circle icon"></i> <i class="yellow info circle icon"></i>
<div class='ui content popup'> <div class='ui content popup'>
系统:@#server.Host.Platform#@-@#server.Host.PlatformVersion#@ [<span 系统:@#server.Host.Platform#@-@#server.Host.PlatformVersion#@ [<span
@ -35,26 +35,26 @@
<div class="ui grid"> <div class="ui grid">
<div class="three wide column">CPU</div> <div class="three wide column">CPU</div>
<div class="thirteen wide column"> <div class="thirteen wide column">
<div class="ui cpu green progress" :data-value="server.State.CPU" data-total="100"> <div :class="formatPercent(server.live,server.State.CPU, 100).class">
<div class="bar"> <div class="bar" :style="formatPercent(server.live,server.State.CPU, 100).style">
<div class="progress"></div> <div class="progress"></div>
</div> </div>
</div> </div>
</div> </div>
<div class="three wide column">内存</div> <div class="three wide column">内存</div>
<div class="thirteen wide column"> <div class="thirteen wide column">
<div class="ui mem green progress" :data-value="server.State.MemUsed" <div :class="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class">
:data-total="server.Host.MemTotal"> <div class="bar"
<div class="bar"> :style="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).style">
<div class="progress"></div> <div class="progress"></div>
</div> </div>
</div> </div>
</div> </div>
<div class="three wide column">交换</div> <div class="three wide column">交换</div>
<div class="thirteen wide column"> <div class="thirteen wide column">
<div class="ui swap green progress" :data-value="server.State.SwapUsed" <div :class="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).class">
:data-total="server.Host.SwapTotal"> <div class="bar"
<div class="bar"> :style="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).style">
<div class="progress"></div> <div class="progress"></div>
</div> </div>
</div> </div>
@ -68,9 +68,9 @@
</div> </div>
<div class="three wide column">硬盘</div> <div class="three wide column">硬盘</div>
<div class="thirteen wide column"> <div class="thirteen wide column">
<div class="ui disk green progress" :data-value="server.State.DiskUsed" <div :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
:data-total="server.Host.DiskTotal"> <div class="bar"
<div class="bar"> :style="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style">
<div class="progress"></div> <div class="progress"></div>
</div> </div>
</div> </div>
@ -99,21 +99,44 @@
data: { data: {
servers: initData, servers: initData,
pulled: false, pulled: false,
cache: [],
}, },
mounted() { mounted() {
$('.progress').progress();
$('.yellow.info.icon').popup({ $('.yellow.info.icon').popup({
popup: '.ui.content.popup' popup: '.ui.content.popup'
}); });
}, },
methods: { methods: {
isAlive(s) { formatPercent(live, used, total) {
if (!s.Host) return false const percent = live ? (parseInt(used / total * 100) || 0) : -1
const lastActive = new Date(s.LastActive).getTime() if (!this.cache[percent]) {
if (this.pulled > 10 && Date.now() - lastActive > 20 * 1000) { this.cache[percent] = {
return false class: {
ui: true,
progress: true,
},
style: {
'transition-duration': '300ms',
'min-width': 'unset',
width: percent + '% !important',
},
percent,
}
if (percent < 0) {
this.cache[percent].style['background-color'] = 'slategray'
this.cache[percent].class.offline = true
} else if (percent < 51) {
this.cache[percent].style['background-color'] = 'lime'
this.cache[percent].class.fine = true
} else if (percent < 81) {
this.cache[percent].style['background-color'] = 'orange'
this.cache[percent].class.warning = true
} else {
this.cache[percent].style['background-color'] = 'crimson'
this.cache[percent].class.error = true
}
} }
return true return this.cache[percent]
}, },
secondToDate(s) { secondToDate(s) {
var d = Math.floor(s / 3600 / 24); var d = Math.floor(s / 3600 / 24);
@ -154,13 +177,15 @@
const keys = Object.keys(statusCards.servers) const keys = Object.keys(statusCards.servers)
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const ns = statusCards.servers[keys[i]]; const ns = statusCards.servers[keys[i]];
// 进度条 if (!ns.Host) ns.live = false
const bars = [ else {
$('#' + ns.ID + ' .cpu.progress'), const lastActive = new Date(ns.LastActive).getTime()
$('#' + ns.ID + ' .mem.progress'), if (statusCards.pulled > 3 && Date.now() - lastActive > 5 * 1000) {
$('#' + ns.ID + ' .swap.progress'), ns.live = false
$('#' + ns.ID + ' .disk.progress') } else {
] ns.live = true
}
}
for (let j = 0; j < oldServers.length; j++) { for (let j = 0; j < oldServers.length; j++) {
const os = oldServers[j]; const os = oldServers[j];
if (ns.ID == os.ID) { if (ns.ID == os.ID) {
@ -171,13 +196,6 @@
popup: '.ui.content.popup' popup: '.ui.content.popup'
}); });
} }
// 刷新进度条
bars.forEach((b, i) => {
if (b[0] && b[0].dataset) {
b.progress('set total', i == 0 ? 100 : b[0].dataset.total);
b.progress('update progress', b[0].dataset.value);
}
})
} }
} }
ws.onclose = function () { ws.onclose = function () {

View File

@ -16,13 +16,13 @@
<!-- Styles --> <!-- Styles -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css"> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
<link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css"> <link rel="stylesheet" type="text/css" href="/static/semantic-ui-alerts.min.css">
<link rel="stylesheet" href="/static/theme-hotaru/css/core.css?v202012092025" type="text/css"> <link rel="stylesheet" href="/static/theme-hotaru/css/core.css?v202012121912" type="text/css">
<link rel="stylesheet" href="/static/theme-hotaru/css/main.css?v202012092025" type="text/css"> <link rel="stylesheet" href="/static/theme-hotaru/css/main.css?v202012121912" type="text/css">
<link rel="stylesheet" href="/static/theme-hotaru/css/darkmode.css?v202012092025" type="text/css"> <link rel="stylesheet" href="/static/theme-hotaru/css/darkmode.css?v202012121912" type="text/css">
{{if ts .CustomCSS}} {{if ts .CustomCSS}}
<style> {{tag "style"}}
{{.CustomCSS|css}} {{.CustomCSS|css}}
</style> {{tag "/style"}}
{{end}} {{end}}
</head> </head>
@ -62,8 +62,8 @@
<td> <td>
<div class="progress"> <div class="progress">
<div style="width: 100%;" <div style="width: 100%;"
:class="'progress-bar progress-bar-'+ (isAlive(server)?'success':pulled>3?'danger':'warning')"> :class="'progress-bar progress-bar-'+ (server.live?'success':pulled>3?'danger':'warning')">
<small>@#isAlive(server)?'运行中':pulled>3?'已离线':'……'#@</small> <small>@#server.live?'运行中':pulled>3?'已离线':'……'#@</small>
</div> </div>
</div> </div>
</td> </td>
@ -76,25 +76,26 @@
<td>@#server.State?formatByteSize(server.State.NetInTransfer)+'|'+formatByteSize(server.State.NetOutTransfer):'-'#@ <td>@#server.State?formatByteSize(server.State.NetInTransfer)+'|'+formatByteSize(server.State.NetOutTransfer):'-'#@
</td> </td>
<td> <td>
<div class="progress progress-striped active"> <div v-if="server.State" :class="formatPercent(server.live,server.State.CPU, 100).class">
<div :style="'width: '+parseInt(server.State?server.State.CPU:0)+'%;'" <div class="bar" :style="formatPercent(server.live,server.State.CPU, 100).style">
:class="'progress-bar progress-bar-'+ (isAlive(server)?'success':pulled>3?'danger':'warning')"> <small>@#formatPercent(server.State.CPU, 100).percent#@%</small>
<small>@#parseInt(server.State?server.State.CPU:0)#@%</small>
</div> </div>
</div> </div>
</td> </td>
<td> <td>
<div class="progress progress-striped active"> <div v-if="server.State"
<div :style="'width: '+parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)+'%;'" :class="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class">
:class="'progress-bar progress-bar-'+ (isAlive(server)?'success':pulled>3?'danger':'warning')"> <div class="bar"
:style="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).style">
<small>@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</small> <small>@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</small>
</div> </div>
</div> </div>
</td> </td>
<td> <td>
<div class="progress progress-striped active"> <div v-if="server.State"
<div :style="'width: '+(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)+'%;'" :class="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class">
:class="'progress-bar progress-bar-'+ (isAlive(server)?'success':pulled>3?'danger':'warning')"> <div
:style="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style">
<small>@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)#@%</small> <small>@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal*100:0)#@%</small>
</div> </div>
</div> </div>
@ -116,9 +117,9 @@
<i class="zmdi zmdi-check-circle text-success"></i> <i class="zmdi zmdi-check-circle text-success"></i>
</div> </div>
<div class="location-progress"> <div class="location-progress">
<div class="progress progress-sm"> <div :class="formatPercent(server.live,server.State?server.State.CPU:0, 100).class">
<div :class="'progress-bar progress-bar-'+ (isAlive(server)?'success':pulled>3?'danger':'warning')" <div class="bar"
:style="'width:'+parseInt(server.State?server.State.CPU:0)+'%;'"> :style="formatPercent(server.live,server.State?server.State.CPU:0, 100).style">
</div> </div>
</div> </div>
</div> </div>
@ -153,11 +154,43 @@
data: { data: {
servers: initData, servers: initData,
pulled: false, pulled: false,
cache: [],
}, },
mounted() { mounted() {
this.troggleDarkMode(); this.troggleDarkMode();
}, },
methods: { methods: {
formatPercent(live, used, total) {
const percent = live ? (parseInt(used / total * 100) || 0) : -1
if (!this.cache[percent]) {
this.cache[percent] = {
class: {
ui: true,
progress: true,
},
style: {
'transition-duration': '300ms',
'min-width': 'unset',
width: percent + '% !important',
},
percent,
}
if (percent < 0) {
this.cache[percent].style['background-color'] = 'slategray'
this.cache[percent].class.offline = true
} else if (percent < 51) {
this.cache[percent].style['background-color'] = 'lime'
this.cache[percent].class.fine = true
} else if (percent < 81) {
this.cache[percent].style['background-color'] = 'orange'
this.cache[percent].class.warning = true
} else {
this.cache[percent].style['background-color'] = 'crimson'
this.cache[percent].class.error = true
}
}
return this.cache[percent]
},
readableBytes(bytes) { readableBytes(bytes) {
var i = Math.floor(Math.log(bytes) / Math.log(1024)), var i = Math.floor(Math.log(bytes) / Math.log(1024)),
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
@ -165,6 +198,11 @@
return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i];
}, },
troggleDarkMode() { troggleDarkMode() {
const hour = new Date(Date.now()).getHours()
if (hour > 17 || hour < 4) {
document.body.classList.add('dark');
}
return
//darkmode //darkmode
if (document.getElementById("darkmodeButton")) { if (document.getElementById("darkmodeButton")) {
var night = parseInt(document.cookie.replace(/(?:(?:^|.*;\s*)dark\s*=\s*([^;]*).*$)|^.*$/, "$1") || '0'); var night = parseInt(document.cookie.replace(/(?:(?:^|.*;\s*)dark\s*=\s*([^;]*).*$)|^.*$/, "$1") || '0');
@ -189,14 +227,6 @@
console.log('Darkmode not Support'); console.log('Darkmode not Support');
} }
}, },
isAlive(s) {
if (!s.Host) return false
const lastActive = new Date(s.LastActive).getTime()
if (this.pulled > 10 && Date.now() - lastActive > 20 * 1000) {
return false
}
return true
},
secondToDate(s) { secondToDate(s) {
var d = Math.floor(s / 3600 / 24); var d = Math.floor(s / 3600 / 24);
var h = Math.floor(s / 3600 % 24); var h = Math.floor(s / 3600 % 24);
@ -230,36 +260,20 @@
}, 1000 * 60 * 6); }, 1000 * 60 * 6);
} }
ws.onmessage = function (evt) { ws.onmessage = function (evt) {
const oldServers = statusCards.servers
statusCards.servers = JSON.parse(evt.data) statusCards.servers = JSON.parse(evt.data)
statusCards.pulled++ statusCards.pulled++
const keys = Object.keys(statusCards.servers) const keys = Object.keys(statusCards.servers)
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const ns = statusCards.servers[keys[i]]; const ns = statusCards.servers[keys[i]];
// 进度条 if (!ns.Host) ns.live = false
const bars = [ else {
$('#' + ns.ID + ' .cpu.progress'), const lastActive = new Date(ns.LastActive).getTime()
$('#' + ns.ID + ' .mem.progress'), if (statusCards.pulled > 3 && Date.now() - lastActive > 5 * 1000) {
$('#' + ns.ID + ' .swap.progress'), ns.live = false
$('#' + ns.ID + ' .disk.progress') } else {
] ns.live = true
for (let j = 0; j < oldServers.length; j++) {
const os = oldServers[j];
if (ns.ID == os.ID) {
break
} }
// 新加入的仔
$('#' + ns.ID + ' .yellow.info.icon').popup({
popup: '.ui.content.popup'
});
} }
// 刷新进度条
bars.forEach((b, i) => {
if (b[0] && b[0].dataset) {
b.progress('set total', i == 0 ? 100 : b[0].dataset.total);
b.progress('update progress', b[0].dataset.value);
}
})
} }
} }
ws.onclose = function () { ws.onclose = function () {