feat: ws-scrcpy/NeKo/Chromium hub links, light theme, ECharts, H5 nav, optional Neko Docker
Made-with: Cursor
This commit is contained in:
128
web-console/components/OverviewCharts.tsx
Normal file
128
web-console/components/OverviewCharts.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user