From 87091f454a86d77e292bb05f245fa66a664ea608 Mon Sep 17 00:00:00 2001 From: snowykami Date: Fri, 4 Oct 2024 10:18:38 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=96=B0=E5=BD=A9=E8=89=B2?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/svg/{location.svg => location-new.svg} | 0 src/api/index.ts | 1 + src/api/utils.ts | 55 +++++++- src/components/Host.vue | 131 ++++++++++++------ src/components/OutlineAnime.vue | 53 +++++++ src/views/Home.vue | 47 +++++-- 6 files changed, 231 insertions(+), 56 deletions(-) rename public/svg/{location.svg => location-new.svg} (100%) create mode 100644 src/components/OutlineAnime.vue diff --git a/public/svg/location.svg b/public/svg/location-new.svg similarity index 100% rename from public/svg/location.svg rename to public/svg/location-new.svg diff --git a/src/api/index.ts b/src/api/index.ts index 0686ae0..bb06c1f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -12,6 +12,7 @@ export interface Status { uptime: number; // uptime in seconds link: string | null; // 链接或是nil observed_at: number; // unix timestamp + start_time: number; // unix timestamp }; hardware: { mem: { diff --git a/src/api/utils.ts b/src/api/utils.ts index 0294bf1..9e061f7 100644 --- a/src/api/utils.ts +++ b/src/api/utils.ts @@ -1,3 +1,5 @@ +export const onlineTimeout = 30 + export function getLinuxReleaseIcon(name: string, release: string): { name: string, icon: string } { if (name.toLowerCase() == 'windows') { return {name: 'Windows', icon: '/svg/system-windows.svg'} @@ -19,10 +21,10 @@ export function getLinuxReleaseIcon(name: string, release: string): { name: stri } } -export function formatSizeByUnit(bytes: number, unit: string | null = null, suffix: string|null = null): string { +export function formatSizeByUnit(bytes: number, unit: string | null = null, suffix: string | null = null): string { // 若指定单位,则格式化为指定单位对应的大小字符串加上单位 // 若未指定单位,则选择1-1024之间的最大单位,格式化为该单位对应的大小字符串加上单位 - if( bytes == 0 ){ + if (bytes == 0) { return '0' } if (bytes < 1024) { @@ -51,9 +53,56 @@ export function format2Size(num1: number, num2: number, suffix: string | null = return `${n1}/${r2.num.toFixed(1)}${r2.unit}${suffix}` } -export function formatDate(timestamp: number, timeOnly: boolean = false){ +export function formatDate(timestamp: number, timeOnly: boolean = false) { const d = new Date(timestamp * 1000) const date = d.toLocaleDateString() const time = d.toLocaleTimeString() return timeOnly ? time : `${date} ${time}` +} + +export function getBaseColor(percent: number, disable: boolean = false) { + // 获取基础颜色 + // 0~60: green, 60~80: yellow, 80~90: orange, 90~100: red + if (disable) { + return '#9ca3af' + } + if (percent < 60) { + return '#22c55e' + } else if (percent < 80) { + return '#eab308' + } else if (percent < 90) { + return '#f97316' + } else { + return '#ef4444' + } +} + + +export function getBlankColor(percent: number, disable: boolean = false) { + if (disable) { + return '#e5e7eb' + } + + //相比base更浅的颜色 + if (percent < 60) { + return '#bbf7d0' + } else if (percent < 80) { + return '#fef08a' + } else if (percent < 90) { + return '#fed7aa' + } else { + return '#fecaca' + } +} + +// 1727998501 + +export function formatUptime(uptime: number ): string { + + const seconds = uptime + const d = Math.floor(seconds / 86400) + const h = Math.floor((seconds % 86400) / 3600) + const m = Math.floor((seconds % 3600) / 60) + const s = Math.floor(seconds % 60) + return `${d}:${h}:${m}:${s}` } \ No newline at end of file diff --git a/src/components/Host.vue b/src/components/Host.vue index af5e1a9..81eb2d2 100644 --- a/src/components/Host.vue +++ b/src/components/Host.vue @@ -2,7 +2,18 @@ import {Status} from "../api"; import {computed, onMounted, ref, watch} from "vue"; import * as echarts from "echarts"; -import {format2Size, formatDate, formatSizeByUnit, getLinuxReleaseIcon} from "../api/utils.ts"; +import { + format2Size, + formatDate, + formatSizeByUnit, + formatUptime, + getBaseColor, + getBlankColor, + getLinuxReleaseIcon, + onlineTimeout +} from "../api/utils.ts"; + +import OutlineAnime from "./OutlineAnime.vue"; const props = defineProps<{ status: Status @@ -12,6 +23,8 @@ const status = computed( () => props.status ) +const uptime = ref(formatUptime(status.value.meta.uptime)) +console.log(uptime.value) const cpuChartRef = ref(null); const memoryChartRef = ref(null); const swapChartRef = ref(null); @@ -19,11 +32,14 @@ const swapChartRef = ref(null); // 网络 const netChartRef = ref(null); let netStats: [number, number, number][] = [] - -const dotColor = ref('#22c55e') +const isOnline = ref(true) +const dotColor = computed( + () => isOnline.value ? '#22c55e' : '#ff4d4f' +) +const spreadColor = computed( + () => isOnline.value ? '#80ffb0' : '#fd8182' +) const deltaTime = ref('0') -// const isDiskCollapsed = ref(status.value.hardware.disks) -// const isDiskOpen = ref(false) const os = computed(() => { return getLinuxReleaseIcon(status.value.meta.os.name, status.value.meta.os.version) }) @@ -36,7 +52,6 @@ const swapDetail = computed(() => { return status.value.hardware.swap.total > 0 ? format2Size(status.value.hardware.swap.used, status.value.hardware.swap.total) : 'N/A' }) - function onMountedFunc() { const cpuChart = echarts.init(cpuChartRef.value); const memoryChart = echarts.init(memoryChartRef.value); @@ -48,14 +63,19 @@ function onMountedFunc() { } const radius = ['65%', '90%'] - const hwColor = ['#4c4c4c', '#e3e3e3'] - const netColor = ['#4c4c4c', '#bababa'] + const netColor = ['#a2d8f4', '#10a0ed'] + setInterval(() => { + if (isOnline.value) { + const deltaTime = (Date.now()) / 1000 - status.value.meta.observed_at + uptime.value = formatUptime(status.value.meta.uptime + deltaTime) + } + }, 1000) function update() { const timeDiff = (Date.now()) / 1000 - status.value.meta.observed_at deltaTime.value = timeDiff.toFixed(1) // 判断该时间与上一个时间不同才push - if (netStats.length === 0 || netStats[netStats.length - 1][0] !== status.value.meta.observed_at){ + 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]) } @@ -63,12 +83,13 @@ function onMountedFunc() { netStats.shift() } - if (timeDiff > 30) { - dotColor.value = '#ff4d4f' - } + isOnline.value = timeDiff <= onlineTimeout; cpuChart.setOption( { - color: hwColor, + color: [ + getBaseColor(status.value.hardware.cpu.percent), + getBlankColor(status.value.hardware.cpu.percent) + ], title: { text: status.value.hardware.cpu.percent + '%', left: 'center', @@ -106,7 +127,10 @@ function onMountedFunc() { ) memoryChart.setOption( { - color: hwColor, + color: [ + getBaseColor(status.value.hardware.mem.used / status.value.hardware.mem.total * 100), + getBlankColor(status.value.hardware.mem.used / status.value.hardware.mem.total * 100) + ], title: { text: `${(status.value.hardware.mem.used / status.value.hardware.mem.total * 100).toFixed(1)}%`, left: 'center', @@ -142,7 +166,10 @@ function onMountedFunc() { ) swapChart.setOption( { - color: hwColor, + color: [ + getBaseColor(status.value.hardware.swap.used / status.value.hardware.swap.total * 100, status.value.hardware.swap.total <= 0), + getBlankColor(status.value.hardware.swap.used / status.value.hardware.swap.total * 100, status.value.hardware.swap.total <= 0) + ], title: { text: status.value.hardware.swap.total > 0 ? `${(status.value.hardware.swap.used / status.value.hardware.swap.total * 100).toFixed(1)}%` : 'N/A', left: 'center', @@ -223,7 +250,7 @@ function onMountedFunc() { type: 'value', axisLabel: { formatter: function (value: number) { - return formatSizeByUnit(value*8, null, 'b') + return formatSizeByUnit(value * 8, null, 'b') } } } @@ -276,29 +303,24 @@ onMounted( \ No newline at end of file diff --git a/src/views/Home.vue b/src/views/Home.vue index ec4dcb1..c50665f 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -1,33 +1,58 @@