效果图:加了之后
改了之后的 html(没格式化是因为我的格式化插件有 bug 格式化会导致哪吒插入的关键字段被优化掉):

{{define "theme-mdui/home"}}
<!doctype html>
<html lang="{{.Conf.Language}}">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>{{.Title}}</title>
  <link rel="shortcut icon" type="image/png" href="/static/logo.svg?v20210804" />

  <!-- MDUI CSS -->
  <link rel="stylesheet" href="https://cdn.staticfile.org/mdui/1.0.2/css/mdui.min.css"/>
  <link rel="stylesheet" href="/static/theme-mdui/mdui.css" type="text/css">
  <style>
    .mdui-table td, .mdui-table th{padding: 6px;}
    .progress{width: 10%;min-width: 75px;}
    .progress-text{font-size: 16px;font-weight: 800;position: relative;top: 4px;left: 6px;}
    .offline st,.offline at,.offline gt,.offline .progress-text{color: grey;}
    a{text-decoration:none;color:#333;}.mdui-theme-layout-dark a{color:#fff;}
  </style>
  {{if ts .CustomCode}}
  {{.CustomCode|safe}}
  {{end}}
</head>

<body>
{{template "theme-mdui/menu" .}}

  <div id="app">
  <div id="container" class="mdui-container">
    <button @click="toggleView" class="mdui-fab mdui-fab-wrapper mdui-fab-fixed mdui-ripple mdui-color-pink-accent">
      <i v-if="showCard" class="mdui-icon material-icons">list</i>
      <i v-else class="mdui-icon material-icons">apps</i>
    </button>
    <div v-if="showCard" class="mdui-row-xs-1 mdui-row-sm-2 mdui-row-md-3 mdui-row-lg-4">
    <div class="mdui-panel" mdui-panel="{accordion: false}">
      <div class="mdui-panel-item mdui-panel-item-open " v-for="group in groups">
        <div class="mdui-panel-item-header">@#(group.Tag!==''?group.Tag:'{{tr "Default"}}')#@</div>
        <div class="mdui-panel-item-body">
      <div id="servers">
        <div class="mdui-col" v-for='server in group.data' :id="server.ID">
          <div :class="'mdui-card mt' + (server.live?'':' offline')">
            <div class="mdui-card-header">
          <img class="mdui-card-header-avatar" :src="'https://cdn.staticfile.org/flag-icon-css/4.1.5/flags/1x1/' + (server.Host.CountryCode?server.Host.CountryCode:'cn') + '.svg'"/>
          <div class="mdui-card-header-title">@#server.Name#@</div>
          <div class="mdui-card-header-subtitle">@#server.Host.CountryCode.toUpperCase()#@ | @#server.Host.Platform#@ @#server.Host.PlatformVersion#@</div>
            </div>
            <div v-if="server.live" class="mdui-card-menu">
              <i :id="'info-' + server.ID" class="mdui-icon material-icons">info_outline</i>
            </div>
            <div v-else class="mdui-card-menu mdui-typo-title mdui-text-color-grey">Offline</div>
            <div class="mdui-card-content">
              <ul class="mdui-list">
                <li class="mdui-list-item">
                  <i class="mdui-list-item-icon mdui-icon material-icons">memory</i>
                  <div class="mdui-list-item-content">
                    <st class="mdui-list-item-title mdui-list-item-one-line">CPU <span>@#server.live?parseInt(server.State.CPU):'NaN'#@%</span></st>
                    <div class="mdui-list-item-text" style="opacity:1;">
                      <div class="mdui-progress">
                        <div class="mdui-progress-determinate mdui-color-indigo-400" :style="'width: ' + (server.live?server.State.CPU:'0') + '%;'"></div>
                      </div>
                    </div>
                  </div>
                </li>
                <li class="mdui-list-item" :id="'mem-' + server.ID">
                  <i class="mdui-list-item-icon mdui-icon material-icons">straighten</i>
                  <div class="mdui-list-item-content">
                    <at class="mdui-list-item-title mdui-list-item-one-line">MEM <span>@#server.live?parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0):'NaN'#@%</span></at>
                    <div class="mdui-progress">
                      <div class="mdui-progress-determinate mdui-color-pink-400" :style="'width: ' + (server.live?parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0):'0') + '%;'"></div>
                    </div>
                  </div>
                </li>
                <li class="mdui-list-item">
                  <i class="mdui-list-item-icon mdui-icon material-icons">swap_vert</i>
                  <div class="mdui-list-item-content">
                    <div class="mdui-list-item-title">{{tr "UpNetTransfer"}}</div>
                    <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;">
                      <at><span>@#formatNetByteSize(server.State.NetOutSpeed)#@</span></at>
                    </div>
                  </div>
                  <div class="mdui-list-item-content">
                    <div class="mdui-list-item-title">{{tr "DownNetTransfer"}}</div>
                    <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;">
                      <st><span>@#formatNetByteSize(server.State.NetInSpeed)#@</span></st>
                    </div>
                  </div>
                </li>
                <li class="mdui-list-item">
                  <i class="mdui-list-item-icon mdui-icon material-icons">swap_horiz</i>
                  <div class="mdui-list-item-content">
                    <div class="mdui-list-item-title">{{tr "TotalUpNetTransfer"}}</div>
                    <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;">
                      <at><span>@#formatByteSize(server.State.NetOutTransfer)#@</span></at>
                    </div>
                  </div>
                  <div class="mdui-list-item-content">
                    <div class="mdui-list-item-title">{{tr "TotalDownNetTransfer"}}</div>
                    <div class="mdui-list-item-text mdui-list-item-one-line" style="opacity:1;">
                      <st><span>@#formatByteSize(server.State.NetInTransfer)#@</span></st>
                    </div>
                  </div>
                </li>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </div>
    </div>
    </div>
    </div>

    <div v-else class="mdui-table-fluid mdui-m-t-1">
      <table class="mdui-table mdui-table-hoverable">
        <thead>
          <tr>
            <th class="mdui-text-center">ID</th>
            <th class="mdui-text-center">{{tr "Name"}}</th>
            <th class="mdui-text-center">{{tr "UpNetTransfer"}}</th>
            <th class="mdui-text-center">{{tr "DownNetTransfer"}}</th>
               <th class="mdui-text-center">{{tr "TotalUpNetTransfer"}}</th>
            <th class="mdui-text-center">{{tr "TotalDownNetTransfer"}}</th>
            <th class="mdui-text-center">CPU</th>
            <th class="mdui-text-center">RAM</th>
            <th class="mdui-text-center">{{tr "Uptime"}}</th>
          </tr>
        </thead>
        <tbody>
          <tr :class="(server.live?'':'offline')" v-for="server in servers">
            <td class="mdui-text-center">@#server.ID#@</td>
            <td class="mdui-text-center">@#server.Name#@</td>
            <td class="mdui-text-center"><at>@#formatNetByteSize(server.State.NetOutSpeed)#@</at></td>
            <td class="mdui-text-center"><st>@#formatNetByteSize(server.State.NetInSpeed)#@</st></td>
            <td class="mdui-text-center"><at>@#formatByteSize(server.State.NetOutTransfer)#@</at></td>
            <td class="mdui-text-center"><st>@#formatByteSize(server.State.NetInTransfer)#@</st></td>
            <td class="progress">
              <div class="mdui-progress" style="height: 30px; background-color: #edbbd2;">
                <div class="mdui-progress-determinate mdui-color-pink-a400" :style="'width: ' + (server.live?server.State.CPU:'0') + '%;'">
                    <span class="mdui-text-truncate progress-text">@#server.live?parseInt(server.State.CPU):'NaN'#@%</span>
                </div>
              </div>
            </td>
            <td class="progress">
              <div class="mdui-progress" style="height: 30px;">
                <div class="mdui-progress-determinate mdui-color-indigo-400" :style="'width: ' + parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0) + '%;'">
                    <span class="mdui-text-truncate progress-text">@#parseInt(server.State?server.State.MemUsed/server.Host.MemTotal*100:0)#@%</span>
                </div>
              </div>
            </td>
            <td class="mdui-text-center">@#secondToDate(server.State.Uptime)#@</td>
          </tr>
        </tbody>
      </table>
    </div>

  </div>
  </div>

{{template "theme-mdui/footer" .}}

  <script src="/static/theme-mdui/mdui.js"></script>
  <script src="https://cdn.staticfile.org/mdui/1.0.2/js/mdui.min.js"></script>
  <script src="https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://cdn.staticfile.org/vue/2.6.14/vue.min.js"></script>

  <script>
    var container = document.querySelector("#container");
    container.style.minHeight = window.innerHeight-document.body.clientHeight+container.clientHeight+'px';
    mdui.mutation();
    const initData = JSON.parse('{{.Servers}}').servers;
    var statusCards = new Vue({
        el: '#app',
        delimiters: ['@#', '#@'],
        data: {
            servers: initData,
            cache: [],
            groups: [],
            showCard: true
        },
        created() {
            this.group()
        },
        methods: {
            toggleView() {
              this.showCard = !this.showCard
            },
            toFixed2(f) {
              return f.toFixed(2)
            },
            group() {
                this.groups = groupingData(this.servers, "Tag")
            },
            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);
            },
            readableBytes(bytes) {
              if (!bytes) {
                return '0B'
              }
              var i = Math.floor(Math.log(bytes) / Math.log(1024)),
                sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
              return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + sizes[i];
            },
            readableNetBytes(bytes) {
              if (!bytes) {
                return '0B'
              }
              var Kbps=125, Mbps=Kbps*1000, Gbps=Mbps*1000, Tbps=Gbps*1000;
              if (bytes < Kbps) return (bytes * 8).toFixed(2) + 'bps';
              if (bytes < Mbps) return (bytes / Kbps).toFixed(2) + 'Kbps';
              if (bytes < Gbps) return (bytes / Mbps).toFixed(2) + 'Mbps';
              if (bytes < Tbps) return (bytes / Gbps).toFixed(2) + 'Gbps';
              else return (bytes / Tbps).toFixed(2) + 'Tbps';
            },
            formatTimestamp(t) {
                return new Date(t * 1000).toLocaleString()
            },
            formatByteSize(bs) {
                const x = this.readableBytes(bs)
                return x != "NaN undefined" ? x : 'NaN'
            },
            formatNetByteSize(bs) {
                const x = this.readableNetBytes(bs)
                return x != "NaN undefined" ? x : 'NaN'
            },
            formatTooltip(server) {
              var disk = this.formatByteSize(server.State.DiskUsed) + '/' + this.formatByteSize(server.Host.DiskTotal);
              var upTime = this.secondToDate(server.State.Uptime);
              var tooltip = `{content: 'System: ${server.Host.Platform}-${server.Host.PlatformVersion}[${server.Host.Arch}]<br>CPU: ${server.Host.CPU}<br>Disk: ${disk}<br>Online: ${upTime}<br>Version: ${server.Host.Version}'}`;
              return tooltip
            }
        }
    })

  function groupingData(data, field) {
    if (!data) {
      return
    }
    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;
  }
    const wsProtocol = window.location.protocol == "https:" ? "wss" : "ws"
    let canShowError = true;
    function connect() {
      const ws = new WebSocket(wsProtocol + '://' + window.location.host + '/ws');
      ws.onopen = function (evt) {
        canShowError = true;
        mdui.snackbar({
          message: '{{tr "RealtimeChannelEstablished"}}',
          timeout: 2000,
          position: 'top',
          onClosed: function () {
            mdui.mutation();
          }
        });
      }
      var infoTooltip = {}, memTooltip = {};
      ws.onmessage = function (evt) {
        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
              if (statusCards.showCard) {
                if (infoTooltip[ns.ID]) {
                  var disk = statusCards.formatByteSize(ns.State.DiskUsed) + '/' + statusCards.formatByteSize(ns.Host.DiskTotal);
                  var upTime = statusCards.secondToDate(ns.State.Uptime);
                  var content =
                    `System: ${ns.Host.Platform}-${ns.Host.PlatformVersion}[${ns.Host.Arch}]
  CPU: ${ns.Host.CPU}
  Disk: ${disk}
  Online: ${upTime}
  Version: ${ns.Host.Version}`;
                  infoTooltip[ns.ID].$element[0].innerText = content;
                }
                else {
                  if (document.getElementById(`info-${ns.ID}`)) infoTooltip[ns.ID] = new mdui.Tooltip(`#info-${ns.ID}`, {});
                }

                if (memTooltip[ns.ID]) {
                  var content = `${statusCards.formatByteSize(ns.State.MemUsed)}/${statusCards.formatByteSize(ns.Host.MemTotal)}`;
                  memTooltip[ns.ID].$element[0].innerText = content;
                }
                else {
                  if (document.getElementById(`mem-${ns.ID}`)) memTooltip[ns.ID] = new mdui.Tooltip(`#mem-${ns.ID}`, {});
                }
              } else { mdui.$('div').remove('.mdui-tooltip'); infoTooltip = {}; memTooltip = {}; }
            }
          }
        }
        statusCards.groups = groupingData(statusCards.servers, "Tag")
        mdui.mutation();
      }
      ws.onclose = function () {
        if (canShowError) {
          canShowError = false;
          mdui.snackbar({
            message: '{{tr "RealtimeChannelDisconnect"}}',
            timeout: 2000,
            position: 'top',
          });
        }
        setTimeout(function () {
          connect()
        }, 3000);
      }
      ws.onerror = function () {
        ws.close()
      }
    }
    connect();
  </script>
</body>
</html>
{{end}}
最后修改:2024 年 01 月 08 日
如果觉得我的文章对你有用,请随意赞赏