新增截图功能,优化UI样式

This commit is contained in:
远野千束 2024-10-05 04:42:03 +08:00
parent 86ad7e06aa
commit 637559ff0a
11 changed files with 283 additions and 67 deletions

View File

@ -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
View File

@ -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
View 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

View 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
View 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

View File

@ -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>

View File

@ -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
View 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>

View File

@ -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>&nbsp;
<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>

View File

@ -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 {

View File

@ -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>