update ServerDashboard

This commit is contained in:
Timi
2026-04-12 00:15:54 +08:00
parent 489cbb5d0f
commit 611830f393
30 changed files with 2078 additions and 892 deletions

View File

@@ -2,7 +2,7 @@
<div class="docker-dashboard">
<section class="card">
<p class="tag">Docker</p>
<h2 class="title">容器状态</h2>
<h2 class="header">容器状态</h2>
<p class="desc">这里用于承载容器列表运行状态镜像占用和资源使用情况</p>
</section>
</div>
@@ -26,7 +26,7 @@
}
.tag,
.title,
.header,
.desc {
margin: 0;
}
@@ -40,7 +40,7 @@
font-size: .875rem;
}
.title {
.header {
font-size: 1.1rem;
}
@@ -54,4 +54,4 @@
box-shadow: 0 .35rem 1rem rgba(0, 0, 0, .2);
}
}
</style>
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
<template>
<div class="server-detail">
<div v-if="isLoading && !snapshotView" class="loading-wrap">
<t-loading text="加载系统详情..." />
</div>
<template v-else-if="snapshotView">
<t-cell-group title="系统" theme="card">
<t-cell title="服务器时间" :note="Time.toDateTime(snapshotView.serverTime)" />
<t-cell title="采样周期" :note="`${snapshotView.sampleRateMs} ms`" />
<t-cell title="操作系统" :note="os?.name" />
<t-cell title="启动时间" :note="Time.toPassedDateTime(os?.bootAt)" />
</t-cell-group>
<t-cell-group title="硬件信息" theme="card">
<t-cell title="主板厂商" :note="hardware?.baseboard?.manufacturer" />
<t-cell title="主板型号" :note="hardware?.baseboard?.model" />
<t-cell title="主板版本" :note="hardware?.baseboard?.version" />
<t-cell title="主板序列号" :note="hardware?.baseboard?.serialNumber" />
<t-cell title="固件厂商" :note="hardware?.firmware?.manufacturer" />
<t-cell title="固件名称" :note="hardware?.firmware?.name" />
<t-cell title="固件描述" :note="hardware?.firmware?.description" />
<t-cell title="固件版本" :note="hardware?.firmware?.version" />
<t-cell title="固件发布日期" :note="hardware?.firmware?.releaseDate" />
</t-cell-group>
</template>
<t-empty v-else description="暂无系统详情数据" />
</div>
</template>
<script setup lang="ts">
import { Toast } from "tdesign-mobile-vue";
import { getSystemStatus, resolveSystemRequestErrorMessage } from "@/api/SystemAPI";
import type { SystemStatusSnapshotView } from "@/types/System";
import { Time } from "timi-web";
defineOptions({
name: "ServerDetail"
});
const isLoading = ref(false);
const snapshotView = ref<SystemStatusSnapshotView | null>(null);
const refreshIntervalMs = 3000;
let refreshTimer: ReturnType<typeof setInterval> | null = null;
const os = computed(() => snapshotView.value?.snapshot?.os);
const hardware = computed(() => snapshotView.value?.snapshot?.hardware);
onMounted(() => {
void refreshDetail();
refreshTimer = setInterval(() => {
void refreshDetail();
}, refreshIntervalMs);
});
onUnmounted(() => {
if (!refreshTimer) {
return;
}
clearInterval(refreshTimer);
refreshTimer = null;
});
async function refreshDetail(): Promise<void> {
if (isLoading.value) {
return;
}
isLoading.value = true;
try {
snapshotView.value = await getSystemStatus("os,hardware");
} catch (error) {
Toast({
theme: "error",
message: resolveSystemRequestErrorMessage(error)
});
} finally {
isLoading.value = false;
}
}
</script>
<style scoped lang="less">
.server-detail {
padding: var(--app-nav-offset) 0 1rem 0;
.loading-wrap {
display: flex;
min-height: 4rem;
align-items: center;
justify-content: center;
}
}
</style>

View File

@@ -0,0 +1,122 @@
<template>
<div class="server-performance-detail">
<div v-if="isLoading && !snapshotView" class="loading-wrap">
<t-loading text="加载资源详情..." />
</div>
<template v-else-if="snapshotView">
<t-cell-group v-if="cpu" title="CPU" theme="card">
<t-cell-info label="型号" :value="cpu.model" />
<t-cell title="物理核心" :note="String(cpu.physicalCores)" />
<t-cell title="逻辑核心" :note="String(cpu.logicalCores)" />
<t-cell title="使用率" :note="Text.unit(cpu.usageTotal * 100, '%')" />
<t-cell title="系统使用率" :note="Text.unit(cpu.usageSystem * 100, '%')" />
<t-cell title="温度" :note="Text.unit(cpu.temperatureCelsius || 0, '℃')"></t-cell>
</t-cell-group>
<t-cell-group v-if="memory" title="内存" theme="card">
<t-cell title="物理内存" :note="IOSize.format(memory.totalBytes)" />
<t-cell title="已使用" :note="IOSize.format(memory.usedBytes)" />
<t-cell title="使用率" :note="Text.unit(memory.usedBytes / memory.totalBytes * 100, '%')" />
<t-cell title="交换区内存" :note="IOSize.format(memory.swapTotalBytes)" />
<t-cell title="交换区已使用" :note="IOSize.format(memory.swapUsedBytes)" />
<t-cell title="交换区使用率" :note="Text.unit(memory.swapUsedBytes / memory.swapTotalBytes * 100, '%')" />
</t-cell-group>
<t-cell-group v-if="jvm" title="JVM" theme="card">
<t-cell title="名称" :note="jvm.name" />
<t-cell title="版本" :note="jvm.version" />
<t-cell title="启动于" :note="Time.toPassedDateTime(jvm.bootAt)" />
<t-cell title="堆内存最大大小" :note="IOSize.format(jvm.heapMaxBytes)" />
<t-cell title="堆内存初始大小" :note="IOSize.format(jvm.heapInitBytes)" />
<t-cell title="堆内存已提交" :note="IOSize.format(jvm.heapCommittedBytes)" />
<t-cell title="堆内存申请率" :note="Text.unit(jvm.heapCommittedBytes / jvm.heapMaxBytes * 100, '%')" />
<t-cell title="堆内存已使用" :note="IOSize.format(jvm.heapUsedBytes)" />
<t-cell title="堆内存使用率" :note="Text.unit(jvm.heapUsedBytes / jvm.heapCommittedBytes * 100, '%')" />
<t-cell-info label="GC 收集器" :value="jvm.gc.collector" />
<t-cell title="GC 周期次数" :note="String(jvm.gc.cycleCount)" />
<t-cell title="GC 暂停次数" :note="String(jvm.gc.pauseCount)" />
<t-cell title="最近暂停时间" :note="Time.toPassedDateTime(jvm.gc.lastPauseAt)" />
<t-cell title="最近回收内存" :note="IOSize.format(jvm.gc.lastRecoveredBytes)" />
</t-cell-group>
<t-cell-group v-if="network" title="网络" theme="card">
<t-cell-info label="网卡" :value="network.interfaceName" />
<t-cell title="MAC" :note="network.mac" />
<t-cell title="接收速率" :note="IOSize.speed(network.rxBytesPerSecond)" />
<t-cell title="发送速率" :note="IOSize.speed(network.txBytesPerSecond)" />
<t-cell title="累计接收" :note="IOSize.format(network.rxTotalBytes)" />
<t-cell title="累计发送" :note="IOSize.format(network.txTotalBytes)" />
<t-cell title="累计接收包" :note="network.rxPacketsTotal.toLocaleString()" />
<t-cell title="累计发送包" :note="network.txPacketsTotal.toLocaleString()" />
<t-cell title="输入错误包" :note="String(network.inErrors)" />
<t-cell title="输出错误包" :note="String(network.outErrors)" />
<t-cell title="输入丢包" :note="String(network.inDrops)" />
<t-cell title="累计冲突" :note="String(network.collisions)" />
</t-cell-group>
</template>
<t-empty v-else description="暂无资源详情数据" />
</div>
</template>
<script setup lang="ts">
import { Toast } from "tdesign-mobile-vue";
import { getSystemStatus, resolveSystemRequestErrorMessage } from "@/api/SystemAPI";
import type { SystemStatusSnapshotView } from "@/types/System";
import { IOSize, Text, Time } from "timi-web";
defineOptions({
name: "ServerPerformanceDetail"
});
const isLoading = ref(false);
const snapshotView = ref<SystemStatusSnapshotView | null>(null);
const refreshIntervalMs = 3000;
let refreshTimer: ReturnType<typeof setInterval> | null = null;
const cpu = computed(() => snapshotView.value?.snapshot?.cpu);
const memory = computed(() => snapshotView.value?.snapshot?.memory);
const jvm = computed(() => snapshotView.value?.snapshot?.jvm);
const network = computed(() => snapshotView.value?.snapshot?.network);
onMounted(() => {
void refreshDetail();
refreshTimer = setInterval(() => {
void refreshDetail();
}, refreshIntervalMs);
});
onUnmounted(() => {
if (!refreshTimer) {
return;
}
clearInterval(refreshTimer);
refreshTimer = null;
});
async function refreshDetail(): Promise<void> {
if (isLoading.value) {
return;
}
isLoading.value = true;
try {
snapshotView.value = await getSystemStatus("cpu,memory,jvm,network");
} catch (error) {
Toast({
theme: "error",
message: resolveSystemRequestErrorMessage(error)
});
} finally {
isLoading.value = false;
}
}
</script>
<style scoped lang="less">
.server-performance-detail {
padding: var(--app-nav-offset) 0 1rem 0;
.loading-wrap {
display: flex;
min-height: 4rem;
align-items: center;
justify-content: center;
}
}
</style>

View File

@@ -2,7 +2,7 @@
<div class="ups-dashboard">
<section class="card">
<p class="tag">UPS</p>
<h2 class="title">供电监控</h2>
<h2 class="header">供电监控</h2>
<p class="desc">这里用于展示 UPS 电池状态负载功率剩余续航和告警信息</p>
</section>
</div>
@@ -26,7 +26,7 @@
}
.tag,
.title,
.header,
.desc {
margin: 0;
}
@@ -40,7 +40,7 @@
font-size: .875rem;
}
.title {
.header {
font-size: 1.1rem;
}
@@ -54,4 +54,4 @@
box-shadow: 0 .35rem 1rem rgba(0, 0, 0, .2);
}
}
</style>
</style>