更新 ServerCard 组件,优化 CPU 和内存信息的显示,增加 SWAP 使用率的提示功能,提升信息展示的清晰度和可读性。同时调整字节格式化函数,简化单位显示。

This commit is contained in:
wood chen 2025-04-26 16:27:01 +08:00
parent 56812a52c3
commit 8eec93aff4
2 changed files with 128 additions and 110 deletions

View File

@ -9,7 +9,6 @@ import { useNavigate } from "react-router-dom"
import PlanInfo from "./PlanInfo" import PlanInfo from "./PlanInfo"
import BillingInfo from "./billingInfo" import BillingInfo from "./billingInfo"
import { Badge } from "./ui/badge"
import { Card, CardContent, CardHeader, CardFooter } from "./ui/card" import { Card, CardContent, CardHeader, CardFooter } from "./ui/card"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip" import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip"
import { ArrowDown, ArrowUp, Clock, Cpu, HardDrive, Server, Activity, BarChart3, Calendar } from "lucide-react" import { ArrowDown, ArrowUp, Clock, Cpu, HardDrive, Server, Activity, BarChart3, Calendar } from "lucide-react"
@ -45,7 +44,10 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
udp, udp,
process, process,
uptime, uptime,
last_active_time_string last_active_time_string,
arch,
swap,
swap_total
} = formatNezhaInfo( } = formatNezhaInfo(
now, now,
serverInfo, serverInfo,
@ -60,8 +62,6 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined
// @ts-expect-error ShowNetTransfer is a global variable // @ts-expect-error ShowNetTransfer is a global variable
const showNetTransfer = window.ShowNetTransfer as boolean const showNetTransfer = window.ShowNetTransfer as boolean
// @ts-expect-error ShowServerDetails is a global variable
const showServerDetails = window.ShowServerDetails !== undefined ? window.ShowServerDetails as boolean : true
const parsedData = parsePublicNote(public_note) const parsedData = parsePublicNote(public_note)
@ -379,6 +379,29 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
</span> </span>
</div> </div>
<ServerUsageBar value={cpu} /> <ServerUsageBar value={cpu} />
{/* CPU信息 */}
{cpu_info && cpu_info.length > 0 && (
<div className="mt-1.5 flex flex-col gap-1 text-[10px]">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="bg-blue-500/10 text-blue-600 dark:text-blue-400 rounded px-1.5 py-0.5 text-center">
{cpu_info[0].includes("Physical") ? "pCPU: " : "vCPU: "}
{cpu_info[0].match(/(\d+)\s+(?:Physical|Virtual)\s+Core/)?.[1] || "-"}
</div>
</TooltipTrigger>
<TooltipContent className="max-w-[250px] text-xs whitespace-pre-wrap p-2">
{cpu_info.join("\n")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
{arch && (
<div className="bg-green-500/10 text-green-600 dark:text-green-400 rounded px-1.5 py-0.5 text-center">
{arch}
</div>
)}
</div>
)}
</div> </div>
{/* 内存使用率 */} {/* 内存使用率 */}
@ -401,6 +424,41 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
</span> </span>
</div> </div>
<ServerUsageBar value={mem} /> <ServerUsageBar value={mem} />
{/* 内存信息 */}
<div className="mt-1.5 flex flex-col gap-1 text-[10px]">
<div className="bg-purple-500/10 text-purple-600 dark:text-purple-400 rounded px-1.5 py-0.5 text-center">
{mem_total > 0 ? formatBytes(mem_total) : "-"}
</div>
{swap_total > 0 ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className={cn("bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 rounded px-1.5 py-0.5 text-center",
Number(swap) > 90 ? "bg-red-500/10 text-red-600 dark:text-red-400" :
Number(swap) > 70 ? "bg-orange-500/10 text-orange-600 dark:text-orange-400" : "")}>
SWAP:{swap.toFixed(0)}%
</div>
</TooltipTrigger>
<TooltipContent className="text-xs">
<div className="flex flex-col gap-1 p-2">
<div className="flex justify-between items-center gap-3">
<span>:</span>
<span>{formatBytes(swap_total)}</span>
</div>
<div className="flex justify-between items-center gap-3">
<span>使:</span>
<span className={getColorClass(Number(swap))}>{swap.toFixed(1)}%</span>
</div>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
):(
<div className="bg-amber-500/10 text-amber-600 dark:text-amber-400 rounded px-1.5 py-0.5 text-center">
-
</div>
)}
</div>
</div> </div>
{/* 存储使用率 */} {/* 存储使用率 */}
@ -415,6 +473,10 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
</span> </span>
</div> </div>
<ServerUsageBar value={stg} /> <ServerUsageBar value={stg} />
{/* 存储信息 */}
<div className="mt-1.5 bg-amber-500/10 text-amber-600 dark:text-amber-400 rounded px-1.5 py-0.5 text-center text-[10px]">
{disk_total > 0 ? formatBytes(disk_total) : "-"}
</div>
</div> </div>
</div> </div>
@ -440,64 +502,20 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
{/* 连接数与进程数 */} {/* 连接数与进程数 */}
<div className="bg-muted/40 rounded-lg p-2 grid grid-cols-2 gap-2"> <div className="bg-muted/40 rounded-lg p-2 grid grid-cols-2 gap-2">
<div className="flex items-center min-w-0"> <div className="flex items-center min-w-0">
<Server className="size-[14px] text-indigo-500 mr-1 flex-shrink-0" /> <Server className="size-[14px] text-indigo-500 mr-1 flex-shrink-0" />
<span className="text-xs truncate" title={`TCP连接: ${tcp}`}>T: {formatLargeNumber(tcp)}</span> <span className="text-xs truncate" title={`TCP连接: ${tcp}`}>T: {formatLargeNumber(tcp)}</span>
</div> </div>
<div className="flex items-center min-w-0"> <div className="flex items-center min-w-0">
<Server className="size-[14px] text-pink-500 mr-1 flex-shrink-0" /> <Server className="size-[14px] text-pink-500 mr-1 flex-shrink-0" />
<span className="text-xs truncate" title={`UDP连接: ${udp}`}>U: {formatLargeNumber(udp)}</span> <span className="text-xs truncate" title={`UDP连接: ${udp}`}>U: {formatLargeNumber(udp)}</span>
</div> </div>
<div className="flex items-center min-w-0 col-span-2"> <div className="flex items-center min-w-0 col-span-2">
<Activity className="size-[14px] text-orange-500 mr-1 flex-shrink-0" /> <Activity className="size-[14px] text-orange-500 mr-1 flex-shrink-0" />
<span className="text-xs truncate" title={`进程数: ${process}`}>P: {formatLargeNumber(process)}</span> <span className="text-xs truncate" title={`进程数: ${process}`}>P: {formatLargeNumber(process)}</span>
</div> </div>
</div> </div>
</div> </div>
{/* 服务器详细信息区域 */}
{showServerDetails && (
<div className="mt-3 flex items-center flex-wrap gap-1.5">
{/* CPU信息 */}
{cpu_info && cpu_info.length > 0 && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Badge variant="outline" className="text-[10px] py-0 h-5 bg-blue-500/10 hover:bg-blue-500/20">
{cpu_info[0].includes("Physical") ? "pCPU: " : "vCPU: "}
{cpu_info[0].match(/(\d+)\s+(?:Physical|Virtual)\s+Core/)?.[1] || "-"}
</Badge>
</TooltipTrigger>
<TooltipContent className="text-xs">
{cpu_info.join(", ")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{/* 内存大小 */}
{mem_total > 0 ? (
<Badge variant="outline" className="text-[10px] py-0 h-5 bg-purple-500/10 hover:bg-purple-500/20">
RAM: {formatBytes(mem_total)}
</Badge>
) : (
<Badge variant="outline" className="text-[10px] py-0 h-5 bg-purple-500/10 hover:bg-purple-500/20">
RAM: -
</Badge>
)}
{/* 存储大小 */}
{disk_total > 0 ? (
<Badge variant="outline" className="text-[10px] py-0 h-5 bg-amber-500/10 hover:bg-amber-500/20">
DISK: {formatBytes(disk_total)}
</Badge>
) : (
<Badge variant="outline" className="text-[10px] py-0 h-5 bg-amber-500/10 hover:bg-amber-500/20">
DISK: -
</Badge>
)}
</div>
)}
</CardContent> </CardContent>
<CardFooter className="p-4 pt-0 flex flex-col gap-2 pb-3"> <CardFooter className="p-4 pt-0 flex flex-col gap-2 pb-3">

View File

@ -1,9 +1,9 @@
export function formatBytes(bytes: number, decimals: number = 2) { export function formatBytes(bytes: number, decimals: number = 2) {
if (!+bytes) return "0 Bytes" if (!+bytes) return "0 B"
const k = 1024 const k = 1024
const dm = decimals < 0 ? 0 : decimals const dm = decimals < 0 ? 0 : decimals
const sizes = ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
const i = Math.floor(Math.log(bytes) / Math.log(k)) const i = Math.floor(Math.log(bytes) / Math.log(k))