mirror of
https://github.com/snowykami/server-status-web.git
synced 2025-06-04 14:15:23 +00:00
✨ 新增截图功能,优化UI样式
This commit is contained in:
parent
86ad7e06aa
commit
637559ff0a
@ -10,6 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"echarts": "^5.5.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"vue": "^3.4.37",
|
||||
"vue-router": "^4.4.5"
|
||||
},
|
||||
|
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@ -8,6 +8,9 @@ dependencies:
|
||||
echarts:
|
||||
specifier: ^5.5.1
|
||||
version: 5.5.1
|
||||
html2canvas:
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1
|
||||
vue:
|
||||
specifier: ^3.4.37
|
||||
version: 3.5.10(typescript@5.6.2)
|
||||
@ -545,6 +548,11 @@ packages:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: true
|
||||
|
||||
/base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
dev: false
|
||||
|
||||
/brace-expansion@2.0.1:
|
||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||
dependencies:
|
||||
@ -555,6 +563,12 @@ packages:
|
||||
resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
|
||||
dev: true
|
||||
|
||||
/css-line-break@2.1.0:
|
||||
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
dev: false
|
||||
|
||||
/csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
@ -620,6 +634,14 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/html2canvas@1.4.1:
|
||||
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
dependencies:
|
||||
css-line-break: 2.1.0
|
||||
text-segmentation: 1.0.3
|
||||
dev: false
|
||||
|
||||
/magic-string@0.30.11:
|
||||
resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
|
||||
dependencies:
|
||||
@ -696,6 +718,12 @@ packages:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
/text-segmentation@1.0.3:
|
||||
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
dev: false
|
||||
|
||||
/to-fast-properties@2.0.0:
|
||||
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
||||
engines: {node: '>=4'}
|
||||
@ -713,6 +741,12 @@ packages:
|
||||
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
|
||||
dev: true
|
||||
|
||||
/utrie@1.0.2:
|
||||
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
|
||||
dependencies:
|
||||
base64-arraybuffer: 1.0.2
|
||||
dev: false
|
||||
|
||||
/vite@5.4.8(@types/node@22.7.4):
|
||||
resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
1
public/svg/label.svg
Normal file
1
public/svg/label.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg t="1728071238053" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8448" width="500" height="500"><path d="M973.180137 514.739063L509.228934 50.787861c-11.980201-12.082596-28.260987-18.840658-45.258537-18.738263H96.065593c-35.326234 0-63.9968 28.670566-63.996801 63.9968v368.007199c0 16.99755 6.655667 33.278336 18.738263 45.258537l463.951203 463.951203c24.984351 24.984351 65.532723 24.984351 90.517074 0l368.0072-368.0072c24.881956-24.984351 24.881956-65.532723-0.102395-90.517074zM271.979997 335.957602c-44.132193 0-79.970401-35.838208-79.970402-79.970401s35.838208-79.970401 79.970402-79.970402c44.132193 0 79.970401 35.838208 79.970401 79.970402 0.102395 44.132193-35.735813 79.970401-79.970401 79.970401z" fill="#2c2c2c" p-id="8449"></path><path d="M901.298931 469.275736L469.294931 901.279736l80.072796 79.970401h31.947203l399.954402-399.954402v-31.947202z" fill="#2c2c2c" p-id="8450"></path><path d="M112.039194 335.957602H31.966398c-8.80596 0.20479-15.768812 7.577221-15.564022 16.383181 0.20479 8.498775 7.065247 15.359232 15.564022 15.564022h79.970401c8.80596-0.20479 15.768812-7.577221 15.564022-16.383181-0.102395-8.498775-6.962852-15.359232-15.461627-15.564022zM511.993596 480.027199H383.999996c-8.80596 0.20479-15.768812 7.577221-15.564022 16.38318 0.20479 8.498775 7.065247 15.359232 15.564022 15.564022h127.9936c8.80596 0.20479 16.178391-6.758062 16.383181-15.564022s-6.758062-16.178391-15.564022-16.38318c-0.307185-0.102395-0.511974-0.102395-0.819159 0z m63.9968-127.993601h-15.973601c-8.80596 0-15.973601 7.167642-15.973601 15.973602s7.167642 15.973601 15.973601 15.973601h15.973601c8.80596 0 15.973601-7.167642 15.973602-15.973601s-7.167642-15.973601-15.973602-15.973602z m15.973602-208.066396h-96.046398c-8.80596 0-15.973601 7.167642-15.973601 15.973601s7.167642 15.973601 15.973601 15.973601h96.046398c8.80596 0 15.973601-7.167642 15.973601-15.973601s-7.167642-15.973601-15.973601-15.973601z m-383.980801-48.023199h-32.049598c-8.80596 0.20479-15.768812 7.577221-15.564022 16.383181 0.20479 8.498775 7.065247 15.359232 15.564022 15.564021H207.880802c8.80596 0.20479 16.178391-6.758062 16.383181-15.564021 0.20479-8.80596-6.758062-16.178391-15.564022-16.383181h-0.716764zM383.999996 719.938403h-79.970402c-8.80596 0-15.973601 7.167642-15.973601 15.973601s7.167642 15.973601 15.973601 15.973602H383.999996c8.80596 0 15.973601-7.167642 15.973601-15.973602s-7.167642-15.973601-15.973601-15.973601z m352.033598 79.970402h-79.970401c-8.80596 0-15.973601 7.167642-15.973602 15.973601s7.167642 15.973601 15.973602 15.973601h79.970401c8.80596 0 15.973601-7.167642 15.973601-15.973601s-7.167642-15.973601-15.973601-15.973601z m-64.099195-143.967202h48.023199c8.80596 0.20479 16.178391-6.758062 16.383181-15.564022s-6.758062-16.178391-15.564022-16.383181H671.934399c-8.80596-0.20479-16.178391 6.758062-16.383181 15.564022s6.758062 16.178391 15.564022 16.383181h0.819159z" fill="#2c2c2c" p-id="8451"></path><path d="M995.809405 492.109795L531.858203 28.158592C513.939099 10.137093 489.466723 0 463.970397 0H96.065593C43.025045 0 0.019195 43.00585 0.019195 96.046398v368.007199c0 25.496325 10.137093 49.866307 28.158592 67.887806l463.951202 463.951202c37.476526 37.476526 98.299085 37.476526 135.775612 0l368.007199-368.007199c37.374131-37.476526 37.374131-98.299085-0.102395-135.775611zM470.011695 883.258237L73.436324 486.580471c-6.041298-5.938903-9.420329-14.130493-9.420329-22.629269V96.046398c0-17.714314 14.335283-31.947203 31.947203-31.947203h368.007199c8.498775 0 16.587971 3.379031 22.629269 9.420329L883.277432 469.9925 470.011695 883.258237zM950.550868 582.626869L582.646064 950.531673c-12.492175 12.492175-32.766362 12.492175-45.258538 0l-44.644167-44.644167 413.163342-413.265737 44.644167 44.644168c12.59457 12.59457 12.59457 32.868757 0 45.360932zM271.979997 159.940803c-53.040548 0-95.944003 43.00585-95.944003 96.046398s43.00585 95.944003 96.046398 95.944002 95.944003-43.00585 95.944003-96.046397c0-52.938153-43.00585-95.944003-96.046398-95.944003z m0 160.043198c-35.326234 0-63.9968-28.670566-63.9968-63.9968s28.670566-63.9968 63.9968-63.996801 63.9968 28.670566 63.9968 63.996801-28.670566 63.9968-63.9968 63.9968z" fill="#2c2c2c" p-id="8452"></path></svg>
|
After Width: | Height: | Size: 4.1 KiB |
1
public/svg/screenshots.svg
Normal file
1
public/svg/screenshots.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg t="1728073281593" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9684" width="500" height="500"><path d="M981.333333 789.333333h-106.666666V256c0-59.733333-46.933333-106.666667-106.666667-106.666667H234.666667V42.666667c0-25.6-17.066667-42.666667-42.666667-42.666667s-42.666667 17.066667-42.666667 42.666667v106.666666H42.666667c-25.6 0-42.666667 17.066667-42.666667 42.666667s17.066667 42.666667 42.666667 42.666667h106.666666V768c0 59.733333 46.933333 106.666667 106.666667 106.666667h533.333333V981.333333c0 25.6 17.066667 42.666667 42.666667 42.666667s42.666667-17.066667 42.666667-42.666667v-106.666666H981.333333c25.6 0 42.666667-17.066667 42.666667-42.666667s-17.066667-42.666667-42.666667-42.666667z m-213.333333-554.666666c12.8 0 21.333333 8.533333 21.333333 21.333333v341.333333l-145.066666-140.8c-17.066667-17.066667-42.666667-17.066667-59.733334 0l-140.8 145.066667L384 541.866667c-17.066667-17.066667-38.4-17.066667-55.466667-4.266667l-89.6 72.533333V234.666667H768z m-512 554.666666c-12.8 0-21.333333-8.533333-21.333333-21.333333v-42.666667l115.2-93.866666 64 64c8.533333 8.533333 17.066667 12.8 29.866666 12.8s21.333333-4.266667 29.866667-12.8l140.8-145.066667 174.933333 166.4v72.533333H256z" p-id="9685"></path><path d="M384 482.133333c51.2 0 93.866667-42.666667 93.866667-93.866666s-42.666667-93.866667-93.866667-93.866667-93.866667 38.4-93.866667 89.6 42.666667 98.133333 93.866667 98.133333z m0-128c17.066667 0 34.133333 12.8 34.133333 34.133334s-17.066667 29.866667-34.133333 29.866666-34.133333-17.066667-34.133333-34.133333 17.066667-29.866667 34.133333-29.866667z" p-id="9686"></path></svg>
|
After Width: | Height: | Size: 1.6 KiB |
1
public/svg/timezone.svg
Normal file
1
public/svg/timezone.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg t="1728070867266" class="icon" viewBox="0 0 1075 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2481" width="500" height="500"><path d="M486.58043 972.367932h-22.899578A489.640506 489.640506 0 0 1 0.720514 509.407595a482.619409 482.619409 0 0 1 134.048946-358.616034A482.511392 482.511392 0 0 1 486.58043 0.216034a486.940084 486.940084 0 0 1 486.075949 486.075949c0 13.286076-0.540084 26.896203-1.72827 40.398312a281.924051 281.924051 0 0 0-49.687763-53.144303A418.025316 418.025316 0 0 0 644.285071 102.075949v21.603376a108.016878 108.016878 0 0 1-110.933333 104.020253 108.016878 108.016878 0 0 0-110.933333 104.020253 54.008439 54.008439 0 0 1-55.412659 51.740085 108.016878 108.016878 0 0 0-110.933333 103.696202h291.64557a278.791561 278.791561 0 0 0-87.277637 203.719831 280.843882 280.843882 0 0 0 171.422784 259.240507 485.211814 485.211814 0 0 1-145.2827 22.251476zM93.93908 409.383966a42.234599 42.234599 0 0 0-42.450633 43.206751v0.972152A417.269198 417.269198 0 0 0 409.78043 870.616034v-64.810127a43.206751 43.206751 0 0 0-29.920675-41.370464 104.236287 104.236287 0 0 1-72.479325-100.779747v-34.349367a44.502954 44.502954 0 0 0-12.421941-30.892827L123.751738 422.562025a41.046414 41.046414 0 0 0-29.596624-12.854008z" p-id="2482"></path><path d="M768.072413 460.908017a256 256 0 1 0 256 256 256.216034 256.216034 0 0 0-256-256M742.472413 1024A332.691983 332.691983 0 1 1 1075.164396 691.308017a333.124051 333.124051 0 0 1-332.691983 332.691983" p-id="2483"></path><path d="M881.706168 819.308017a43.206751 43.206751 0 0 1-21.603375-5.832912l-142.798312-82.848945V548.293671a40.398312 40.398312 0 0 1 80.364557 0v143.122363l105.964557 61.56962a34.025316 34.025316 0 0 1 11.989873 49.903797 41.370464 41.370464 0 0 1-33.809283 16.526583" p-id="2484"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -8,7 +8,8 @@ const year = new Date().getFullYear()
|
||||
<h1 style="text-align: center">Server Status</h1>
|
||||
<router-view/>
|
||||
<footer>
|
||||
© Copyright 2024-{{year}} <a href="https://sfkm.me" target="_blank">snowykami</a> All Rights Reserved
|
||||
<a href="https://github.com/snowykami/server-status-server">Server Status Dashboard</a><br>
|
||||
© Copyright 2024-{{year}} <a href="https://sfkm.me" target="_blank">Snowykami</a> All Rights Reserved
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
@ -30,4 +31,7 @@ footer {
|
||||
padding: 1em;
|
||||
color: #666;
|
||||
}
|
||||
a {
|
||||
color: #36a7ec;
|
||||
}
|
||||
</style>
|
||||
|
@ -13,6 +13,7 @@ export interface Status {
|
||||
link: string | null; // 链接或是nil
|
||||
observed_at: number; // unix timestamp
|
||||
start_time: number; // unix timestamp
|
||||
timezone: string; // Asia/Shanghai
|
||||
};
|
||||
hardware: {
|
||||
mem: {
|
||||
@ -32,6 +33,9 @@ export interface Status {
|
||||
[key: string]: {
|
||||
used: number;
|
||||
total: number;
|
||||
mountpoint: string;
|
||||
fstype: string;
|
||||
device: string;
|
||||
};
|
||||
};
|
||||
net: {
|
||||
|
84
src/components/Disk.vue
Normal file
84
src/components/Disk.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {format2Size, getBaseColor, getBlankColor} from "../api/utils.ts";
|
||||
|
||||
const props = defineProps<{
|
||||
mountpoint: string
|
||||
device: string
|
||||
used: number
|
||||
total: number
|
||||
fstype: string
|
||||
}>()
|
||||
|
||||
const colorUsed = getBaseColor(props.used / props.total * 100)
|
||||
const colorBlank = getBlankColor(props.used / props.total * 100)
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="disk" :style="{backgroundColor: colorBlank}">
|
||||
<div class="disk-used" :style="{width: props.used / props.total * 100 + '%', backgroundColor: colorUsed}"></div>
|
||||
<div class="hover-text">
|
||||
<div class="left-text">
|
||||
<span class="disk-text">{{props.mountpoint}}</span>
|
||||
</div>
|
||||
<div class="right-text">
|
||||
<span class="disk-text">{{ format2Size(props.used, props.total) }} [{{ props.fstype }}]</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
:host {
|
||||
--text-size: 2px;
|
||||
}
|
||||
|
||||
.disk {
|
||||
margin-top: 0.5rem;
|
||||
height: 2rem;
|
||||
width: 100%;
|
||||
border-radius: 100px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.disk-used {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 100px 0 0 100px;
|
||||
}
|
||||
|
||||
.hover-text {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.left-text {
|
||||
margin-left: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.right-text {
|
||||
margin-right: 1rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.disk-text {
|
||||
font-size: var(--text-size);
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
</style>
|
@ -14,6 +14,8 @@ import {
|
||||
} from "../api/utils.ts";
|
||||
|
||||
import OutlineAnime from "./OutlineAnime.vue";
|
||||
import Disk from "./Disk.vue";
|
||||
import html2canvas from "html2canvas";
|
||||
|
||||
const props = defineProps<{
|
||||
status: Status
|
||||
@ -24,7 +26,6 @@ const status = computed(
|
||||
)
|
||||
|
||||
const uptime = ref(formatUptime(status.value.meta.uptime))
|
||||
console.log(uptime.value)
|
||||
const cpuChartRef = ref(null);
|
||||
const memoryChartRef = ref(null);
|
||||
const swapChartRef = ref(null);
|
||||
@ -33,10 +34,10 @@ const swapChartRef = ref(null);
|
||||
const netChartRef = ref(null);
|
||||
let netStats: [number, number, number][] = []
|
||||
const isOnline = ref(true)
|
||||
const dotColor = computed(
|
||||
const statusColor = computed(
|
||||
() => isOnline.value ? '#22c55e' : '#ff4d4f'
|
||||
)
|
||||
const spreadColor = computed(
|
||||
const statusColor2 = computed(
|
||||
() => isOnline.value ? '#80ffb0' : '#fd8182'
|
||||
)
|
||||
const deltaTime = ref('0')
|
||||
@ -52,18 +53,40 @@ const swapDetail = computed(() => {
|
||||
return status.value.hardware.swap.total > 0 ? format2Size(status.value.hardware.swap.used, status.value.hardware.swap.total) : 'N/A'
|
||||
})
|
||||
|
||||
const gradientStyle = computed(() => {
|
||||
return {
|
||||
borderColor: `linear-gradient(90deg, ${statusColor.value}, ${statusColor2.value})`
|
||||
}
|
||||
})
|
||||
|
||||
const hoverBorderColor = computed(() => {
|
||||
return statusColor.value
|
||||
})
|
||||
|
||||
|
||||
function onMountedFunc() {
|
||||
const cpuChart = echarts.init(cpuChartRef.value);
|
||||
const memoryChart = echarts.init(memoryChartRef.value);
|
||||
const swapChart = echarts.init(swapChartRef.value);
|
||||
const netChart = echarts.init(netChartRef.value);
|
||||
|
||||
// style
|
||||
const titleStyle = {
|
||||
color: 'rgba(0, 0, 0, 0.8)',
|
||||
fontSize: 13,
|
||||
fontSize: 15,
|
||||
}
|
||||
const radius = ['65%', '90%']
|
||||
const netColor = ['#a2d8f4', '#0194e3'] // Tx Rx
|
||||
const pieLabelPosition = 'center'
|
||||
const emphasis = {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 15,
|
||||
position: ['50%', '20%'] // 设置标签位置为圆环外部
|
||||
},
|
||||
}
|
||||
|
||||
const netColor = ['#a2d8f4', '#10a0ed']
|
||||
// 更新时间
|
||||
setInterval(() => {
|
||||
if (isOnline.value) {
|
||||
const deltaTime = (Date.now()) / 1000 - status.value.meta.observed_at
|
||||
@ -76,7 +99,7 @@ function onMountedFunc() {
|
||||
deltaTime.value = timeDiff.toFixed(1)
|
||||
// 判断该时间与上一个时间不同才push
|
||||
if (netStats.length === 0 || netStats[netStats.length - 1][0] !== status.value.meta.observed_at) {
|
||||
netStats.push([status.value.meta.observed_at, status.value.hardware.net.up, status.value.hardware.net.down])
|
||||
netStats.push([status.value.meta.observed_at, status.value.hardware.net.up, status.value.hardware.net.down]) // 时间 上行 下行
|
||||
}
|
||||
|
||||
if (netStats.length > 20) {
|
||||
@ -94,7 +117,7 @@ function onMountedFunc() {
|
||||
text: status.value.hardware.cpu.percent + '%',
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
textStyle: titleStyle
|
||||
textStyle: titleStyle,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
@ -103,22 +126,16 @@ function onMountedFunc() {
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '20',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
position: pieLabelPosition
|
||||
},
|
||||
emphasis: emphasis,
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: computed(
|
||||
() => [
|
||||
{value: status.value.hardware.cpu.percent},
|
||||
{value: 100 - status.value.hardware.cpu.percent}
|
||||
{value: status.value.hardware.cpu.percent, name: 'Used'},
|
||||
{value: 100 - status.value.hardware.cpu.percent, name: 'Free'}
|
||||
]
|
||||
).value
|
||||
}
|
||||
@ -144,21 +161,15 @@ function onMountedFunc() {
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '20',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
position: pieLabelPosition
|
||||
},
|
||||
emphasis: emphasis,
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{value: status.value.hardware.mem.used},
|
||||
{value: status.value.hardware.mem.total - status.value.hardware.mem.used}
|
||||
{value: status.value.hardware.mem.used, name: 'Used'},
|
||||
{value: status.value.hardware.mem.total - status.value.hardware.mem.used, name: 'Free'}
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -183,22 +194,17 @@ function onMountedFunc() {
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '20',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
position: pieLabelPosition
|
||||
},
|
||||
emphasis: emphasis,
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{value: status.value.hardware.swap.total > 0 ? status.value.hardware.swap.used : 0},
|
||||
{value: status.value.hardware.swap.total > 0 ? status.value.hardware.swap.used : 0, name: 'Used'},
|
||||
{
|
||||
value: status.value.hardware.swap.total > 0 ? status.value.hardware.swap.total - status.value.hardware.swap.used : 100
|
||||
value: status.value.hardware.swap.total > 0 ? status.value.hardware.swap.total - status.value.hardware.swap.used : 100,
|
||||
name: 'Free'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -209,16 +215,28 @@ function onMountedFunc() {
|
||||
netChart.setOption(
|
||||
{
|
||||
color: netColor,
|
||||
title: {
|
||||
text: 'Network',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
backgroundColor: '#0f7bc5',
|
||||
borderRadius: 50,
|
||||
formatter: function (params: any) {
|
||||
if (params.axisDimension === 'y') {
|
||||
return formatSizeByUnit(params.value * 8, null, 'bps');
|
||||
} else {
|
||||
return formatDate(params.value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
formatter: function (params: any) {
|
||||
let result = formatDate(params[0].name, true) + '<br/>';
|
||||
params.forEach(function (item: any) {
|
||||
result += item.marker + (item.seriesName == 'Tx' ? '↑' : '↓') + ': ' + formatSizeByUnit(item.value * 8, null, 'bps') + '<br/>';
|
||||
});
|
||||
return result;
|
||||
}
|
||||
},
|
||||
toolbox: {
|
||||
@ -257,24 +275,29 @@ function onMountedFunc() {
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'Up',
|
||||
name: 'Tx',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: netStats.map(item => item[1])
|
||||
data: netStats.map(item => item[1]),
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Down',
|
||||
name: 'Rx',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {},
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: netStats.map(item => item[2])
|
||||
data: netStats.map(item => item[2]),
|
||||
showSymbol: false
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -292,6 +315,21 @@ function onMountedFunc() {
|
||||
}
|
||||
|
||||
|
||||
// link.download = `screenshot-${status.value.meta.id}-${formatDate(Date.now(), false)}.svg`;
|
||||
|
||||
function downloadScreenshot() {
|
||||
const hostElement = document.querySelector(".host#"+status.value.meta.id);
|
||||
if (hostElement) {
|
||||
html2canvas(<HTMLElement>hostElement, { scale: 2 }).then((canvas) => {
|
||||
const dataURL = canvas.toDataURL("image/png");
|
||||
const link = document.createElement("a");
|
||||
link.href = dataURL;
|
||||
link.download = `screenshot-${status.value.meta.id}-${formatDate(Date.now(), false)}.png`;
|
||||
link.click();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(
|
||||
() => {
|
||||
onMountedFunc()
|
||||
@ -301,25 +339,36 @@ onMounted(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="host">
|
||||
<div class="host" :style="[gradientStyle, { '--hover-border-color': hoverBorderColor }]" :id="status.meta.id">
|
||||
<div class="meta-1" style="display: flex; justify-content: space-between">
|
||||
<div class="meta1-left" style="display: flex; justify-content: flex-start; align-items: center">
|
||||
<OutlineAnime class="outline-anime" :color="dotColor" :spreadColor="spreadColor" :is-online="isOnline"/>
|
||||
<span class="host-name" style="display: flex">{{ props.status.meta.name }}</span>
|
||||
<OutlineAnime class="outline-anime" :color="statusColor" :spreadColor="statusColor2" :is-online="isOnline"/>
|
||||
<span class="host-name" style="display: flex">{{ status.meta.name }}</span>
|
||||
</div>
|
||||
<div class="meta1-right" style="display: flex; justify-content: flex-end; align-items: center">
|
||||
<div style="margin-right: 5px">{{ uptime }}</div>
|
||||
<div class="uptime" style="margin-right: 5px"
|
||||
:style="{backgroundColor: statusColor2, borderColor: statusColor}">{{ uptime }}
|
||||
</div>
|
||||
<img @click="downloadScreenshot" class="icon" src="/svg/screenshots.svg" alt="download" style="width: 20px; height: 20px">
|
||||
</div>
|
||||
</div>
|
||||
<div class="meta-2" style="display: flex">
|
||||
<div class="meta-2">
|
||||
<div class="section">
|
||||
<img class="icon" :src="os.icon" alt="system">
|
||||
<span class="meta2-text">{{ os.name }} · {{ props.status.meta.location }}</span>
|
||||
<span class="meta2-text">{{ os.name }}</span>
|
||||
</div>
|
||||
<div class="section">
|
||||
<img class="icon" src="/svg/timezone.svg" alt="location">
|
||||
<span class="meta2-text">{{ status.meta.location }} · {{ status.meta.timezone }}</span>
|
||||
</div>
|
||||
<div class="labels section" style="display: flex; justify-content: flex-start">
|
||||
<img class="icon" src="/svg/label.svg" alt="labels">
|
||||
<span class="label" v-for="label in status.meta.labels" :key="label">{{ label }}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="labels" style="display: flex; justify-content: flex-start">
|
||||
<span class="label" v-for="label in props.status.meta.labels" :key="label">{{ label }}</span>
|
||||
<div class="section-name">
|
||||
Hardware
|
||||
</div>
|
||||
<div class="charts-container" style="display: flex; justify-content: space-between">
|
||||
<div class="cpu-info hw-info">
|
||||
@ -339,8 +388,18 @@ onMounted(
|
||||
</div>
|
||||
</div>
|
||||
<div class="net">
|
||||
<div class="section-name">
|
||||
Network
|
||||
</div>
|
||||
<div class="net-chart" ref="netChartRef"></div>
|
||||
</div>
|
||||
<div class="disks">
|
||||
<div class="section-name">
|
||||
Storage
|
||||
</div>
|
||||
<Disk v-for="disk in status.hardware.disks" :key="disk.mountpoint" :mountpoint="disk.mountpoint"
|
||||
:device="disk.device" :used="disk.used" :total="disk.total" :fstype="disk.fstype"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@ -348,12 +407,24 @@ onMounted(
|
||||
<style scoped>
|
||||
|
||||
:root {
|
||||
--text-color-1: #000;
|
||||
--text-color-2: #383838;
|
||||
|
||||
--liteyuki-color-1: #d0e9ff;
|
||||
--liteyuki-color-2: #a2d8f4;
|
||||
--hover-border-color: #ccc;
|
||||
}
|
||||
|
||||
.host {
|
||||
padding: 1em;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 20px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.host:hover {
|
||||
border-color: var(--hover-border-color); /* Change border color on hover */
|
||||
}
|
||||
|
||||
.meta-1 {
|
||||
.outline-anime {
|
||||
@ -376,6 +447,10 @@ onMounted(
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.labels {
|
||||
@ -383,7 +458,7 @@ onMounted(
|
||||
|
||||
.label {
|
||||
padding: 0.05rem 0.5rem;
|
||||
border: 2px dashed;
|
||||
border: 1px dashed;
|
||||
border-color: var(--text-color-1);
|
||||
border-radius: 50px;
|
||||
margin-right: 10px;
|
||||
@ -393,14 +468,6 @@ onMounted(
|
||||
}
|
||||
}
|
||||
|
||||
.host {
|
||||
padding: 1em;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 20px;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 0.3em;
|
||||
height: 16px;
|
||||
@ -412,6 +479,13 @@ onMounted(
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uptime {
|
||||
padding: 0.1em 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 50px;
|
||||
border: 1px dashed;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.charts-container {
|
||||
margin-top: 0.5rem;
|
||||
@ -437,8 +511,14 @@ onMounted(
|
||||
}
|
||||
}
|
||||
|
||||
.section-name {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.net {
|
||||
margin-top: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
|
||||
.net-chart {
|
||||
width: 100%;
|
||||
@ -446,4 +526,8 @@ onMounted(
|
||||
}
|
||||
}
|
||||
|
||||
.disks {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
</style>
|
@ -11,6 +11,8 @@
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
--text-color-1: #000;
|
||||
--text-color-2: #383838;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
@ -56,7 +56,7 @@ onUnmounted(() => {
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 10px;
|
||||
gap: 20px;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user