Files
gitlab-instance-0a899031_do…/web-console/components/OverviewCharts.tsx

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>
);
}