From 657a4361eecd23326dde0c41ea9fa56d4cf3012a Mon Sep 17 00:00:00 2001 From: snowykami Date: Thu, 3 Oct 2024 18:38:10 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=96=B0=E9=85=8D=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.dev | 2 +- package.json | 4 +- public/svg/location.svg | 2 +- public/svg/system-arch.svg | 1 + public/svg/system-debian.svg | 1 + public/svg/system-linux.svg | 1 + public/svg/system-opensuse.svg | 1 + public/svg/system-ubuntu.svg | 1 + public/svg/system-windows.svg | 1 + public/svg/system.svg | 2 +- src/App.vue | 3 +- src/api/index.ts | 3 +- src/api/utils.ts | 52 ++++++ src/components/Host.vue | 294 +++++++++++++++++++++++++-------- src/components/HostDisks.vue | 11 ++ src/views/Home.vue | 10 +- tsconfig.app.tsbuildinfo | 2 +- 17 files changed, 304 insertions(+), 87 deletions(-) create mode 100644 public/svg/system-arch.svg create mode 100644 public/svg/system-debian.svg create mode 100644 public/svg/system-linux.svg create mode 100644 public/svg/system-opensuse.svg create mode 100644 public/svg/system-ubuntu.svg create mode 100644 public/svg/system-windows.svg create mode 100644 src/api/utils.ts create mode 100644 src/components/HostDisks.vue diff --git a/.env.dev b/.env.dev index 8fc605c..5a6afce 100644 --- a/.env.dev +++ b/.env.dev @@ -1 +1 @@ -VITE_API_ROOT=http://127.0.0.1:8088 \ No newline at end of file +VITE_API_ROOT=https://status.liteyuki.icu \ No newline at end of file diff --git a/package.json b/package.json index ddfea2e..dae5c94 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", - "build": "vue-tsc -b && vite build", + "dev": "vite --mode dev", + "build": "vue-tsc -b && vite build --mode prod", "preview": "vite preview" }, "dependencies": { diff --git a/public/svg/location.svg b/public/svg/location.svg index 8e4e4cf..6a22095 100644 --- a/public/svg/location.svg +++ b/public/svg/location.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/svg/system-arch.svg b/public/svg/system-arch.svg new file mode 100644 index 0000000..835466d --- /dev/null +++ b/public/svg/system-arch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/system-debian.svg b/public/svg/system-debian.svg new file mode 100644 index 0000000..2405bcd --- /dev/null +++ b/public/svg/system-debian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/system-linux.svg b/public/svg/system-linux.svg new file mode 100644 index 0000000..d6adcc4 --- /dev/null +++ b/public/svg/system-linux.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/system-opensuse.svg b/public/svg/system-opensuse.svg new file mode 100644 index 0000000..d79d20f --- /dev/null +++ b/public/svg/system-opensuse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/system-ubuntu.svg b/public/svg/system-ubuntu.svg new file mode 100644 index 0000000..9a974a1 --- /dev/null +++ b/public/svg/system-ubuntu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/system-windows.svg b/public/svg/system-windows.svg new file mode 100644 index 0000000..58263d9 --- /dev/null +++ b/public/svg/system-windows.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/system.svg b/public/svg/system.svg index 9800386..372577e 100644 --- a/public/svg/system.svg +++ b/public/svg/system.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 4723e02..c58b264 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,10 +1,9 @@ diff --git a/src/api/index.ts b/src/api/index.ts index 7778028..0686ae0 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -29,7 +29,8 @@ export interface Status { }; disks: { [key: string]: { - [key: string]: number; + used: number; + total: number; }; }; net: { diff --git a/src/api/utils.ts b/src/api/utils.ts new file mode 100644 index 0000000..272fc26 --- /dev/null +++ b/src/api/utils.ts @@ -0,0 +1,52 @@ +export function getLinuxReleaseIcon(name: string, release: string): { name: string, icon: string } { + if (name.toLowerCase() == 'windows') { + return {name: 'Windows', icon: '/svg/system-windows.svg'} + } else { + const map: Record = { + 'arch': {name: 'Arch Linux', icon: '/svg/system-archlinux.svg'}, + 'opensuse': {name: 'openSUSE', icon: '/svg/system-opensuse.svg'}, + 'ubuntu': {name: 'Ubuntu', icon: '/svg/system-ubuntu.svg'}, + 'centos': {name: 'CentOS', icon: '/svg/system-centos.svg'}, + 'debian': {name: 'Debian', icon: '/svg/system-debian.svg'}, + } + release = release.toLowerCase() + for (const key in map) { + if (release.includes(key)) { + return map[key] + } + } + return {name: name, icon: '/svg/system-linux.svg'} + } +} + +export function formatSizeByUnit(bytes: number, unit: string | null = null, suffix: string|null = null): string { + // 若指定单位,则格式化为指定单位对应的大小字符串加上单位 + // 若未指定单位,则选择1-1024之间的最大单位,格式化为该单位对应的大小字符串加上单位 + if( bytes == 0 ){ + return '0' + } + if (unit && bytes < 1024) { + return bytes.toFixed(0) + unit + } + const units = ['', 'K', 'M', 'G', 'T', 'P', 'E'] + let i = unit ? units.indexOf(unit) : Math.floor(Math.log2(bytes) / 10) + return (bytes / Math.pow(1024, i)).toFixed(1) + (suffix ? (units[i] + suffix) : '') +} + +export function formatSizeToNumAndUnit(bytes: number): { num: number, unit: string } { + const units = ['', 'K', 'M', 'G', 'T', 'P', 'E'] + let i = Math.floor(Math.log2(bytes) / 10) + return {num: (bytes / Math.pow(1024, i)), unit: units[i]} +} + +export function format2Size(num1: number, num2: number, suffix: string | null = 'iB'): string { + // const n1, unit = formatSizeToNumAndUnit(num1) + // const n2 = formatSizeWithUnit(num2, unit) + // return `${n1}/${n2}` + // const r1 = formatSizeToNumAndUnit(num1) + // let n2 = formatSizeWithUnit(num2, r1.unit) + // return `${r1.num.toFixed(2)}/${n2}` + const r2 = formatSizeToNumAndUnit(num2) + const n1 = formatSizeByUnit(num1, r2.unit) + return `${n1}/${r2.num.toFixed(1)}${r2.unit}${suffix}` +} \ No newline at end of file diff --git a/src/components/Host.vue b/src/components/Host.vue index bec2f8f..3f16479 100644 --- a/src/components/Host.vue +++ b/src/components/Host.vue @@ -2,6 +2,7 @@ import {Status} from "../api"; import {computed, onMounted, ref, watch} from "vue"; import * as echarts from "echarts"; +import {format2Size, formatSizeByUnit, getLinuxReleaseIcon} from "../api/utils.ts"; const props = defineProps<{ status: Status @@ -14,36 +15,66 @@ const status = computed( const cpuChartRef = ref(null); const memoryChartRef = ref(null); const swapChartRef = ref(null); -const diskChartRef = ref(null); -onMounted( - () => { - setOptions() - } -) +// 网络 +const netChartRef = ref(null); +let netStats: [number, number, number][] = [] -function setOptions() { +const dotColor = ref('#22c55e') +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) +}) + +const memDetail = computed(() => { + return format2Size(status.value.hardware.mem.used, status.value.hardware.mem.total) +}) + +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); const swapChart = echarts.init(swapChartRef.value); - const diskChart = echarts.init(diskChartRef.value); + const netChart = echarts.init(netChartRef.value); + const titleStyle = { + color: 'rgba(0, 0, 0, 0.8)', + fontSize: 13, + } + const radius = ['65%', '90%'] - function setOption() { + const hwColor = ['#4c4c4c', '#e3e3e3'] + const netColor = ['#4c4c4c', '#bababa'] + + function update() { + const timeDiff = (Date.now()) / 1000 - status.value.meta.observed_at + deltaTime.value = timeDiff.toFixed(1) + netStats.push([status.value.meta.observed_at, status.value.hardware.net.up, status.value.hardware.net.down]) + if (netStats.length > 20) { + netStats.shift() + } + + if (timeDiff > 30) { + dotColor.value = '#ff4d4f' + } cpuChart.setOption( { + color: hwColor, title: { - text: 'CPU', + text: status.value.hardware.cpu.percent + '%', left: 'center', top: 'center', - textStyle: { - color: 'rgba(255, 255, 255, 0.8)', - fontSize: 14, - } + textStyle: titleStyle }, series: [ { type: 'pie', - radius: ['50%', '70%'], + radius: radius, avoidLabelOverlap: false, label: { show: false, @@ -59,14 +90,10 @@ function setOptions() { labelLine: { show: false }, - // data: [ - // {value: status.value.hardware.cpu.percent, name: 'CPU'}, - // {value: 100 - status.value.hardware.cpu.percent, name: '空闲'} - // ] data: computed( () => [ - {value: status.value.hardware.cpu.percent, name: 'CPU'}, - {value: 100 - status.value.hardware.cpu.percent, name: '空闲'} + {value: status.value.hardware.cpu.percent}, + {value: 100 - status.value.hardware.cpu.percent} ] ).value } @@ -75,19 +102,17 @@ function setOptions() { ) memoryChart.setOption( { + color: hwColor, title: { - text: 'Memory', + text: `${(status.value.hardware.mem.used / status.value.hardware.mem.total * 100).toFixed(1)}%`, left: 'center', top: 'center', - textStyle: { - color: 'rgba(255, 255, 255, 0.8)', - fontSize: 14, - } + textStyle: titleStyle }, series: [ { type: 'pie', - radius: ['50%', '70%'], + radius: radius, avoidLabelOverlap: false, label: { show: false, @@ -104,8 +129,8 @@ function setOptions() { show: false }, data: [ - {value: props.status.hardware.mem.used, name: 'Memory'}, - {value: props.status.hardware.mem.total - props.status.hardware.mem.used, name: '空闲'} + {value: status.value.hardware.mem.used}, + {value: status.value.hardware.mem.total - status.value.hardware.mem.used} ] } ] @@ -113,19 +138,17 @@ function setOptions() { ) swapChart.setOption( { + color: hwColor, title: { - text: 'Swap', + text: status.value.hardware.swap.total > 0 ? `${(status.value.hardware.swap.used / status.value.hardware.swap.total * 100).toFixed(1)}%` : 'N/A', left: 'center', top: 'center', - textStyle: { - color: 'rgba(255, 255, 255, 0.8)', - fontSize: 14, - } + textStyle: titleStyle, }, series: [ { type: 'pie', - radius: ['50%', '70%'], + radius: radius, avoidLabelOverlap: false, label: { show: false, @@ -142,69 +165,173 @@ function setOptions() { show: false }, data: [ - {value: props.status.hardware.swap.used, name: 'Swap'}, - {value: props.status.hardware.swap.total - props.status.hardware.swap.used, name: '空闲'} + {value: status.value.hardware.swap.total > 0 ? status.value.hardware.swap.used : 0}, + { + value: status.value.hardware.swap.total > 0 ? status.value.hardware.swap.total - status.value.hardware.swap.used : 100 + } ] } ] } ) - diskChart.setOption( + + netChart.setOption( { + color: netColor, title: { - text: 'Disk', - left: 'center', - top: 'center', - textStyle: { - color: 'rgba(255, 255, 255, 0.8)', - fontSize: 14, + text: 'Network', + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + label: { + backgroundColor: '#6a7985' + } } }, + toolbox: { + feature: { + saveAsImage: {} + } + }, + grid: { + top: '25%', + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true + }, + xAxis: [ + { + type: 'category', + boundaryGap: false, + data: netStats.map(item => item[0]) + } + ], + yAxis: [ + { + type: 'value', + axisLabel: { + formatter: function (value: number) { + console.log(value) + return formatSizeByUnit(value, null, 'b') + } + } + } + ], + series: [ + { + name: 'Up', + type: 'line', + stack: 'Total', + areaStyle: {}, + emphasis: { + focus: 'series' + }, + data: netStats.map(item => item[1]) + }, + { + name: 'Down', + type: 'line', + stack: 'Total', + areaStyle: {}, + emphasis: { + focus: 'series' + }, + data: netStats.map(item => item[2]) + } + ] } ) + } - setOption() + update() watch( () => status.value, () => { - setOption() + update() } ) } +onMounted( + () => { + onMountedFunc() + } +) + \ No newline at end of file diff --git a/src/components/HostDisks.vue b/src/components/HostDisks.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/src/components/HostDisks.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/src/views/Home.vue b/src/views/Home.vue index 1990170..ec4dcb1 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -30,16 +30,8 @@ onMounted(async () => { \ No newline at end of file diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo index 4ff3549..6f86b91 100644 --- a/tsconfig.app.tsbuildinfo +++ b/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/index.ts","./src/api/node.ts","./src/router/index.ts","./src/app.vue","./src/components/helloworld.vue","./src/components/host.vue","./src/components/nav.vue","./src/views/home.vue","./src/views/test.vue"],"version":"5.6.2"} \ No newline at end of file +{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/index.ts","./src/api/node.ts","./src/api/utils.ts","./src/router/index.ts","./src/app.vue","./src/components/helloworld.vue","./src/components/host.vue","./src/components/hostdisks.vue","./src/components/nav.vue","./src/views/home.vue","./src/views/test.vue"],"version":"5.6.2"} \ No newline at end of file