是抄别人的。
修改后的效果如下:
后台自定义CSS:
<style>
/\* 屏幕适配 \*/
@media only screen and (min-width: 1200px) {
.ui.container {
width: 90% !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;
}
}
/\* 整体图标 \*/
i.icon {
color: #000;
width: 1.2em !important;
}
/\* 背景图片 \*/
body {
content: " " !important;
background: fixed !important;
z-index: -1 !important;
top: 0 !important;
right: 0 !important;
bottom: 0 !important;
left: 0 !important;
background-position: top !important;
background-repeat: no-repeat !important;
background-size: pic !important;
background-image: url(https://s1.locimg.com/2023/04/25/4d8a5ab26c352.jpg) !important;
font-family: Arial, Helvetica, sans-serif !important;
}
/\* 导航栏 \*/
.ui.large.menu {
border: 0 !important;
border-radius: 0px !important;
background-color: rgba(29, 29, 29, 0.55) !important;
}
/\* 首页按钮 \*/
.ui.menu .active.item {
background-color: transparent !important;
}
/\* 导航栏下拉框 \*/
.ui.dropdown .menu {
border: 0 !important;
border-radius: 0 !important;
background-color: rgba(29, 29, 29, 0.8) !important;
}
/\* 登陆按钮 \*/
.nezha-primary-btn {
background-color: transparent !important;
color: #fff !important;
}
/\* 大卡片 \*/
#app .ui.fluid.accordion {
background-color: #3030301f !important;
border-radius: 0.4rem !important;
}
/\* 小卡片 \*/
.ui.four.cards\>.card {
border-radius: 0.6rem !important;
background-color: #272526a3 !important;
}
/\* 小鸡名 \*/
.status.cards .flag {
margin-right: 0.5rem !important;
}
/\* 弹出卡片图标 \*/
.status.cards .header\>.info.icon {
margin-right: 0 !important;
}
.nezha-secondary-font {
color: #21ba45 !important;
}
/\* 进度条 \*/
.ui.progress {
border-radius: 50rem !important;
}
.ui.progress .bar {
min-width: 1.8em !important;
border-radius: 15px !important;
line-height: 1.65em !important;
color: black
}
.ui.fine.progress\>.bar {
background-color: #ba45ac !important;
}
.ui.progress\>.bar {
background-color: #000 !important;
}
.ui.progress.fine .bar {
background-image: linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%);
!important;
}
.ui.progress.warning .bar {
background-image: linear-gradient(to right, #fa709a 0%, #fee140 100%);
!important;
}
.ui.progress.error .bar {
background-image: linear-gradient(to top, #ff0844 0%, #ffb199 100%);
important;
}
.ui.progress.offline .bar {
background-image: linear-gradient(to top, #e6e9f0 0%, #eef1f5 100%);
!important;
}
/\* 上传下载 \*/
.status.cards .outline.icon {
margin-right: 1px !important;
}
i.arrow.alternate.circle.down.outline.icon {
color: #21ba45 !important;
}
i.arrow.alternate.circle.up.outline.icon {
color: red !important;
}
/\* 弹出卡片小箭头 \*/
.ui.right.center.popup {
margin: -3px 0 0 0.914286em !important;
-webkit-transform-origin: left 50% !important;
transform-origin: left 50% !important;
}
.ui.bottom.left.popup {
margin-left: 1px !important;
margin-top: 3px !important;
}
.ui.top.left.popup {
margin-left: 0 !important;
margin-bottom: 10px !important;
}
.ui.top.right.popup {
margin-right: 0 !important;
margin-bottom: 8px !important;
}
.ui.left.center.popup {
margin: -3px .91428571em 0 0 !important;
-webkit-transform-origin: right 50% !important;
transform-origin: right 50% !important;
}
.ui.right.center.popup:before,
.ui.left.center.popup:before {
border: 0px solid #272526eb !important;
background: #272526eb !important;
}
.ui.top.popup:before {
border-color: #272526eb transparent transparent !important;
}
.ui.popup:before {
border-color: #272526eb transparent transparent !important;
}
.ui.bottom.left.popup:before {
border-radius: 0 !important;
border: 1px solid transparent !important;
border-color: #272526eb transparent transparent !important;
background: #272526eb !important;
-webkit-box-shadow: 0px 0px 0 0 #272526eb !important;
box-shadow: 0px 0px 0 0 #272526eb !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
}
.ui.bottom.right.popup:before {
border-radius: 0 !important;
border: 1px solid transparent !important;
border-color: #272526eb transparent transparent !important;
background: #272526eb !important -webkit-box-shadow: 0px 0px 0 0 #272526eb !important;
box-shadow: 0px 0px 0 0 #272526eb !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
}
.ui.top.left.popup:before {
border-radius: 0 !important;
border: 1px solid transparent !important;
border-color: #272526eb transparent transparent !important;
background: #272526eb !important;
-webkit-box-shadow: 0px 0px 0 0 #272526eb !important;
box-shadow: 0px 0px 0 0 #272526eb !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
}
.ui.top.right.popup:before {
border-radius: 0 !important;
border: 1px solid transparent !important;
border-color: #272526eb transparent transparent !important;
background: #272526eb !important;
-webkit-box-shadow: 0px 0px 0 0 #272526eb !important;
box-shadow: 0px 0px 0 0 #272526eb !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
}
.ui.left.center.popup:before {
border-radius: 0 !important;
border: 1px solid transparent !important;
border-color: #272526eb transparent transparent !important;
background: #272526eb !important;
-webkit-box-shadow: 0px 0px 0 0 #272526eb !important;
box-shadow: 0px 0px 0 0 #272526eb !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
}
/\* 弹出卡片 \*/
.status.cards .ui.content.popup {
min-width: 20rem !important;
line-height: 2rem !important;
border-radius: 5px !important;
border: 1px solid transparent !important;
background-color: #272526eb !important;
font-family: Arial, Helvetica, sans-serif !important;
}
.ui.content {
margin: 0 !important;
padding: 1em !important;
}
/\* 服务页 \*/
.ui.table {
background: RGB(225, 225, 225, 0.6) !important;
}
.ui.table thead th {
background: transparent !important;
}
/\* 服务页进度条 \*/
/\* 版权 \*/
.ui.inverted.segment,
.ui.primary.inverted.segment {
color: #000 !important;
font-weight: bold !important;
background-color: #272526a3 !important;
}
</style>
关于黑色背景与显示服务器CPU信息显示等效果:
需更改Docker容器(比如我的路径为/var/lib/docker/overlay2/6fa7db6cd487ae00e3d516e0873141030526aa69a4d783100547590e94ce2f77/merged/dashboard/resource/template
)中/common/
文件夹中的header.html
与menu.html
;同时需要更改/theme-default/
文件夹中的home.html
。大致修改思路如下:
<i class="dropdown icon"></i>
修改为<i class="dropdown icon" style="color:#ffffff"></i>
以及
@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@
修改为<font color="#dddddd">@#server.Name + (server.live?'':'[{{tr "Offline"}}]')#@</font>
大致就是需要将文字显示部分更改为白色、将背景与顶部菜单更改为黑色。
示例HTML:
header.html
{{define "theme-default/home"}}
{{template "common/header" .}}
{{if ts .CustomCode}} {{.CustomCode|safe}} {{end}}
{{template "common/menu" .}}
<div class\="nb-container"\>
<div class\="ui container"\>
<div id\="app"\>
<div class\="ui styled fluid accordion" v-for\="group in groups"\>
<div class\="active title"\>
<i class\="dropdown icon"\></i\>
<font color\="#dddddd"\>@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@</font\>
</div\>
<div class\="active content"\>
<div class\="ui four stackable status cards"\>
<div v-for\="server in group.data" :id\="server.ID" class\="ui card"\>
<div class\="content" v-if\="server.Host" style\="margin-top: 10px; padding-bottom: 5px"\>
<div class\="header"\>
<i :class\="server.Host.CountryCode + ' flag'"\></i\> <i v-if\='server.Host.Platform == "darwin"'
class\="apple icon" style\="color:#ffffff"\></i\><i v-else-if\='isWindowsPlatform(server.Host.Platform)'
class\="windows icon" style\="color:#ffffff"\></i\><i v-else
:class\="'fl-' + getFontLogoClass(server.Host.Platform)" style\="color:#ffffff"\></i\>
<font color\="#dddddd"\>@#server.Name + (server.live?'':'\[{{tr "Offline"}}\]')#@</font\>
<i class\="nezha-secondary-font info circle icon" style\="height: 28px"\></i\>
<div class\="ui content popup" style\="margin-bottom: 0 ;color:#ffffff"\>
<font color\="#dddddd"\>{{tr "Platform"}}: @#server.Host.Platform#@-@#server.Host.PlatformVersion#@
</font\>
\[<span
v-if\="server.Host.Virtualization"\>@#server.Host.Virtualization#@:</span\>@#server.Host.Arch#@\]<br />
<font color\="#dddddd"\>CPU: @#server.Host.CPU#@</font\><br />
<font color\="#dddddd"\>{{tr "DiskUsed"}}:
@#formatByteSize(server.State.DiskUsed)#@/@#formatByteSize(server.Host.DiskTotal)#@</font\><br />
<font color\="#dddddd"\>{{tr "MemUsed"}}:
@#formatByteSize(server.State.MemUsed)#@/@#formatByteSize(server.Host.MemTotal)#@</font\><br />
<font color\="#dddddd"\>{{tr "SwapUsed"}}:
@#formatByteSize(server.State.SwapUsed)#@/@#formatByteSize(server.Host.SwapTotal)#@</font\><br />
<font color\="#dddddd"\>{{tr "NetTransfer"}}: <i class\="arrow alternate circle down outline icon"
style\="color:#ffffff"\></i\>@#formatByteSize(server.State.NetInTransfer)#@<i
class\="arrow alternate circle up outline icon"
style\="color:#ffffff"\></i\>@#formatByteSize(server.State.NetOutTransfer)#@</font\><br />
<font color\="#dddddd"\>{{tr "Load"}}: @# toFixed2(server.State.Load1) #@/@#
toFixed2(server.State.Load5) #@/@#
toFixed2(server.State.Load15) #@</font\><br />
<font color\="#dddddd"\>{{tr "ProcessCount"}}: @# server.State.ProcessCount #@</font\><br />
<font color\="#dddddd"\>{{tr "ConnCount"}}: TCP @# server.State.TcpConnCount #@ / UDP @#
server.State.UdpConnCount #@</font\><br />
<font color\="#dddddd"\>{{tr "BootTime"}}: @# formatTimestamp(server.Host.BootTime) #@</font\><br />
<font color\="#dddddd"\>{{tr "LastActive"}}: @# new Date(server.LastActive).toLocaleString() #@</font\>
<br />
<font color\="#dddddd"\>{{tr "Version"}}: @#server.Host.Version#@</font\><br />
</div\>
<div class\="ui divider" style\="margin-bottom: 5px"\></div\>
</div\>
<div class\="description"\>
<div class\="ui grid"\>
<div class\="three wide column" style\="color:#ffffff"\>CPU</div\>
<div class\="thirteen wide column"\>
<div :class\="formatPercent(server.live,server.State.CPU, 100).class"\>
<div class\="bar" :style\="formatPercent(server.live,server.State.CPU, 100).style"\>
<small\>
@#formatPercent(server.live,server.State.CPU,100).percent#@%
</small\>
</div\>
</div\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>{{tr "MemUsed"}}</div\>
<div class\="thirteen wide column"\>
<div :class\="formatPercent(server.live,server.State.MemUsed, server.Host.MemTotal).class"\>
<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\>
</div\>
</div\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>{{tr "SwapUsed"}}</div\>
<div class\="thirteen wide column"\>
<div :class\="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).class"\>
<div class\="bar"
:style\="formatPercent(server.live,server.State.SwapUsed, server.Host.SwapTotal).style"\>
<small\>
@#parseInt(server.State?server.State.SwapUsed/server.Host.SwapTotal\*100:0)#@%
</small\>
</div\>
</div\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>{{tr "DiskUsed"}}</div\>
<div class\="thirteen wide column"\>
<div :class\="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).class"\>
<div class\="bar"
:style\="formatPercent(server.live,server.State.DiskUsed, server.Host.DiskTotal).style"\>
<small\>
@#parseInt(server.State?server.State.DiskUsed/server.Host.DiskTotal\*100:0)#@%
</small\>
</div\>
</div\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>{{tr "NetSpeed"}}</div\>
<div class\="thirteen wide column"\>
<i class\="arrow alternate circle down outline icon"\></i\>
<font color\="#dddddd"\>@#formatByteSize(server.State.NetInSpeed)#@/s</font\>
<i class\="arrow alternate circle up outline icon"\></i\>
<font color\="#dddddd"\>@#formatByteSize(server.State.NetOutSpeed)#@/s</font\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>流量</div\>
<div class\="thirteen wide column"\>
<i class\="arrow circle down icon" style\="color:#ffffff"\></i\>
<font color\="#dddddd"\>@#formatByteSize(server.State.NetInTransfer)#@</font\>
<i class\="arrow circle up icon" style\="color:#ffffff"\></i\>
<font color\="#dddddd"\>@#formatByteSize(server.State.NetOutTransfer)#@</font\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>信息</div\>
<div class\="thirteen wide column"\>
<i class\="bi bi-cpu-fill" style\="font-size: 1.1rem; color: #4a86e8;"\></i\>
<font color\="#dddddd"\>@#getCoreAndGHz(server.Host.CPU)#@</font\>
<i class\="bi bi-memory" style\="font-size: 1.1rem; color: #00ac0d;"\></i\>
<font color\="#dddddd"\>@#getK2Gb(server.Host.MemTotal)#@</font\>
<i class\="bi bi-hdd-rack-fill" style\="font-size: 1.1rem; color: #980000"\></i\>
<font color\="#dddddd"\>@#getK2Gb(server.Host.DiskTotal)#@</font\>
</div\>
<div class\="three wide column" style\="color:#ffffff"\>{{tr "Uptime"}}</div\>
<div class\="thirteen wide column"\>
<i class\="clock icon" style\="color:#ffffff"\></i\>
<font color\="#dddddd"\>@#secondToDate(server.State.Uptime)#@</font\>
</div\>
</div\>
</div\>
</div\>
<div class\="content" v-else\>
<p\>
<font color\="#dddddd"\>@#server.Name#@</font\>
</p\>
<p\>
<font color\="#dddddd"\>{{tr "ServerIsOffline"}}</font\>
</p\>
</div\>
</div\>
</div\>
</div\>
</div\>
</div\>
</div\>
</div\>
{{template "common/footer" .}}
<script\>
const initData = JSON.parse('{{.Servers}}').servers;
var statusCards = new Vue({
el: '#app',
delimiters: \['@#', '#@'\],
data: {
data: initData,
groups: \[\],
cache: \[\],
},
created() {
this.group()
},
mounted() {
$('.nezha-secondary-font.info.icon').popup({
popup: '.ui.content.popup',
exclusive: true,
});
},
methods: {
toFixed2(f) {
return f.toFixed(2)
},
isWindowsPlatform(str) {
return str.includes('Windows')
},
getFontLogoClass(str) {
if (\["almalinux",
"alpine",
"aosc",
"apple",
"archlinux",
"archlabs",
"artix",
"budgie",
"centos",
"coreos",
"debian",
"deepin",
"devuan",
"docker",
"elementary",
"fedora",
"ferris",
"flathub",
"freebsd",
"gentoo",
"gnu-guix",
"illumos",
"kali-linux",
"linuxmint",
"mageia",
"mandriva",
"manjaro",
"nixos",
"openbsd",
"opensuse",
"pop-os",
"raspberry-pi",
"redhat",
"rocky-linux",
"sabayon",
"slackware",
"snappy",
"solus",
"tux",
"ubuntu",
"void",
"zorin"\].indexOf(str)
> -1) {
return str;
}
if (\['openwrt', 'linux'\].indexOf(str) > -1) {
return 'tux';
}
if (str == 'amazon') {
return 'redhat';
}
if (str == 'arch') {
return 'archlinux';
}
return '';
},
group() {
this.groups = groupingData(this.data, "Tag")
},
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'\] = '#0a94f2'
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\]
},
secondToDate(s) {
var d = Math.floor(s / 3600 / 24);
if (d > 0) {
return d + " {{tr "Day"}}"
}
var h = Math.floor(s / 3600 % 24);
var m = Math.floor(s / 60 % 60);
var s = Math.floor(s % 60);
return h + ":" + ("0" + m).slice(-2) + ":" + ("0" + s).slice(-2);
},
formatTimestamp(t) {
return new Date(t \* 1000).toLocaleString()
},
formatByteSize(bs) {
const x = readableBytes(bs)
return x != "NaN undefined" ? x : '0B'
},
getCoreAndGHz(str) {
if ((str || \[\]).hasOwnProperty(0) === false) {
return '';
}
str = str\[0\];
let GHz = str.match(/(\\d|\\.)+GHz/g);
let Core = str.match(/(\\d|\\.)+ Physical/g);
GHz = GHz !== null ? GHz.hasOwnProperty(0) === false ? '' : GHz\[0\] : ''
Core = Core !== null ? Core.hasOwnProperty(0) === false ? '?' : Core\[0\] : '?'
if (Core === '?') {
let Core = str.match(/(\\d|\\.)+ Virtual/g);
Core = Core !== null ? Core.hasOwnProperty(0) === false ? '?' : Core\[0\] : '?'
return Core.replace('Virtual', 'Core')
}
return Core.replace('Physical', 'Core');
},
getK2Gb(bs) {
bs = bs / 1024 / 1024 / 1024;
return Math.ceil(bs.toFixed(2)) + 'GB';
},
listTipsMouseenter(obj, strs, tipsNum = 1) {
this.layerIndex = layer.tips(strs, '#' + obj, { tips: \[tipsNum, 'rgb(0 0 0 / 85%)'\], time: 0 });
$('#' + obj).attr('layerIndex', this.layerIndex)
},
listTipsMouseleave(obj) {
layer.close(this.layerIndex)
}
}
})
function groupingData(data, field) {
let map = {};
let dest = \[\];
data.forEach(item => {
if (!map\[item\[field\]\]) {
dest.push({
\[field\]: item\[field\],
data: \[item\]
});
map\[item\[field\]\] = item;
} else {
dest.forEach(dItem => {
if (dItem\[field\] == item\[field\]) {
dItem.data.push(item);
}
});
}
})
return dest;
}
let canShowError = true;
function connect() {
const wsProtocol = window.location.protocol == "https:" ? "wss" : "ws"
const ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
ws.onopen = function (evt) {
canShowError = true;
$.suiAlert({
title: '{{tr "RealtimeChannelEstablished"}}',
description: '{{tr "GetTheLatestMonitoringDataInRealTime"}}',
type: 'success',
time: '2',
position: 'top-center',
});
}
ws.onmessage = function (evt) {
const oldServers = statusCards.servers
const data = JSON.parse(evt.data)
statusCards.servers = data.servers
for (let i = 0; i < statusCards.servers.length; i++) {
const ns = statusCards.servers\[i\];
if (!ns.Host) ns.live = false
else {
const lastActive = new Date(ns.LastActive).getTime()
if (data.now - lastActive > 10 \* 1000) {
ns.live = false
} else {
ns.live = true
}
}
}
statusCards.groups = groupingData(statusCards.servers, "Tag")
}
ws.onclose = function () {
if (canShowError) {
canShowError = false;
$.suiAlert({
title: '{{tr "RealtimeChannelDisconnect"}}',
description: '{{tr "CanNotGetTheLatestMonitoringDataInRealTime"}}',
type: 'warning',
time: '2',
position: 'top-center',
});
}
setTimeout(function () {
connect()
}, 3000);
}
ws.onerror = function () {
ws.close()
}
}
connect();
$('.ui.accordion').accordion({ "exclusive": false });
</script\>
{{end}}
header.html
{{define "common/header"}}
<!DOCTYPE html\>
<html lang\="{{.Conf.Language}}"\>
<head\>
<meta charset\="UTF-8"\>
<meta name\="viewport" content\="width=device-width, initial-scale=1.0"\>
<meta http-equiv\="X-UA-Compatible" content\="ie=edge"\>
<meta content\="telephone=no" name\="format-detection"\>
<title\>{{.Title}}</title\>
<link rel\="stylesheet" type\="text/css" href\="https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.css"\>
<link href\="https://cdn.staticfile.org/font-logos/0.17/font-logos.min.css" type\="text/css" rel\="stylesheet" />
<link href\="https://cdn.staticfile.org/bootstrap-icons/1.10.3/font/bootstrap-icons.css" type\="text/css"
rel\="stylesheet" />
<link rel\="stylesheet" type\="text/css" href\="/static/semantic-ui-alerts.min.css"\>
<link rel\="stylesheet" type\="text/css" href\="/static/main.css?v2022042314"\>
<link rel\="shortcut icon" type\="image/png" href\="/static/logo.svg?v20210804" />
</head\>
<body\>
{{end}}
评论