129 lines
4.1 KiB
TypeScript
129 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
|
|
export type SnapshotLite = {
|
|
cpu_load?: { "1m"?: number; "5m"?: number; "15m"?: number };
|
|
disk?: { total?: number; free?: number; used?: number };
|
|
memory?: { total?: number; available?: number; free?: number };
|
|
};
|
|
|
|
export default function OverviewCharts({
|
|
snapshot,
|
|
theme,
|
|
tr,
|
|
}: {
|
|
snapshot: SnapshotLite | null;
|
|
theme: "dark" | "light";
|
|
tr: (zh: string, en: string) => string;
|
|
}) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
const el = ref.current;
|
|
if (!el || !snapshot) return;
|
|
let disposed = false;
|
|
let chart: import("echarts").ECharts | null = null;
|
|
const fg = theme === "light" ? "#0f172a" : "#e2e8f0";
|
|
const muted = theme === "light" ? "#64748b" : "#94a3b8";
|
|
const gridLine = theme === "light" ? "rgba(15,23,42,0.1)" : "rgba(148,163,184,0.12)";
|
|
|
|
import("echarts").then((echarts) => {
|
|
if (disposed || !ref.current) return;
|
|
chart = echarts.init(ref.current, undefined, { renderer: "canvas" });
|
|
const l1 = snapshot.cpu_load?.["1m"] ?? 0;
|
|
const l5 = snapshot.cpu_load?.["5m"] ?? 0;
|
|
const l15 = snapshot.cpu_load?.["15m"] ?? 0;
|
|
const memTotal = snapshot.memory?.total || 1;
|
|
const memAvail = snapshot.memory?.available ?? snapshot.memory?.free ?? 0;
|
|
const memUsed = Math.max(0, memTotal - memAvail);
|
|
const diskTotal = snapshot.disk?.total || 1;
|
|
const diskFree = snapshot.disk?.free || 0;
|
|
const diskUsed = Math.max(0, diskTotal - diskFree);
|
|
|
|
chart.setOption({
|
|
animationDurationUpdate: 450,
|
|
textStyle: { color: fg },
|
|
title: {
|
|
text: tr("无人直播节点 · 资源", "Unmanned live · resources"),
|
|
left: 12,
|
|
top: 8,
|
|
textStyle: { color: fg, fontSize: 12, fontWeight: 600 },
|
|
},
|
|
tooltip: { trigger: "axis" },
|
|
grid: { left: 52, right: "48%", top: 52, bottom: 36 },
|
|
xAxis: {
|
|
type: "category",
|
|
data: ["1m", "5m", "15m"],
|
|
axisLabel: { color: muted, fontSize: 10 },
|
|
axisLine: { lineStyle: { color: gridLine } },
|
|
},
|
|
yAxis: {
|
|
type: "value",
|
|
name: tr("负载", "Load"),
|
|
nameTextStyle: { color: muted, fontSize: 10 },
|
|
axisLabel: { color: muted, fontSize: 10 },
|
|
splitLine: { lineStyle: { color: gridLine } },
|
|
},
|
|
series: [
|
|
{
|
|
name: tr("CPU", "CPU"),
|
|
type: "bar",
|
|
data: [l1, l5, l15],
|
|
barMaxWidth: 28,
|
|
itemStyle: {
|
|
borderRadius: [6, 6, 0, 0],
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
{ offset: 0, color: "#22d3ee" },
|
|
{ offset: 1, color: "#6366f1" },
|
|
]),
|
|
},
|
|
},
|
|
{
|
|
name: tr("内存", "RAM"),
|
|
type: "pie",
|
|
radius: ["26%", "40%"],
|
|
center: ["78%", "38%"],
|
|
label: { color: fg, fontSize: 9 },
|
|
data: [
|
|
{ value: memUsed, name: tr("已用", "Used") },
|
|
{ value: Math.max(0, memAvail), name: tr("可用", "Free") },
|
|
],
|
|
},
|
|
{
|
|
name: tr("磁盘", "Disk"),
|
|
type: "pie",
|
|
radius: ["20%", "32%"],
|
|
center: ["78%", "72%"],
|
|
label: { color: fg, fontSize: 9 },
|
|
data: [
|
|
{ value: diskUsed, name: tr("已用", "Used") },
|
|
{ value: Math.max(0, diskFree), name: tr("空闲", "Free") },
|
|
],
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
const ro = new ResizeObserver(() => chart?.resize());
|
|
ro.observe(el);
|
|
return () => {
|
|
disposed = true;
|
|
ro.disconnect();
|
|
chart?.dispose();
|
|
};
|
|
}, [snapshot, theme, tr]);
|
|
|
|
if (!snapshot) return null;
|
|
|
|
return (
|
|
<div className="mt-4">
|
|
<p className="mb-2 text-xs opacity-70">{tr("ECharts 动态图表", "ECharts live charts")}</p>
|
|
<div
|
|
ref={ref}
|
|
className="chart-surface h-64 w-full rounded-2xl border border-white/[0.08] bg-black/15 md:h-72"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|