mirror of
https://github.com/woodchen-ink/nezha-dash-v1.git
synced 2025-07-18 09:31:55 +08:00
更新 ServerCard 组件,优化 CPU 和内存信息的显示,增加 SWAP 使用率的提示功能,提升信息展示的清晰度和可读性。同时调整字节格式化函数,简化单位显示。
This commit is contained in:
parent
56812a52c3
commit
8eec93aff4
@ -9,13 +9,12 @@ 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"
|
||||||
|
|
||||||
interface ServerCardProps {
|
interface ServerCardProps {
|
||||||
now: number;
|
now: number;
|
||||||
serverInfo: NezhaServer;
|
serverInfo: NezhaServer;
|
||||||
cycleStats?: {
|
cycleStats?: {
|
||||||
[key: string]: CycleTransferData
|
[key: string]: CycleTransferData
|
||||||
@ -25,18 +24,18 @@ interface ServerCardProps {
|
|||||||
export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardProps) {
|
export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
country_code,
|
country_code,
|
||||||
online,
|
online,
|
||||||
cpu,
|
cpu,
|
||||||
up,
|
up,
|
||||||
down,
|
down,
|
||||||
mem,
|
mem,
|
||||||
stg,
|
stg,
|
||||||
net_in_transfer,
|
net_in_transfer,
|
||||||
net_out_transfer,
|
net_out_transfer,
|
||||||
public_note,
|
public_note,
|
||||||
platform,
|
platform,
|
||||||
cpu_info,
|
cpu_info,
|
||||||
mem_total,
|
mem_total,
|
||||||
@ -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,21 +62,19 @@ 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)
|
||||||
|
|
||||||
// 获取匹配当前服务器的流量计费周期
|
// 获取匹配当前服务器的流量计费周期
|
||||||
const getServerCycleData = () => {
|
const getServerCycleData = () => {
|
||||||
if (!cycleStats) {
|
if (!cycleStats) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保服务器ID的所有可能形式
|
// 确保服务器ID的所有可能形式
|
||||||
const serverId = String(serverInfo.id);
|
const serverId = String(serverInfo.id);
|
||||||
const serverIdNum = Number(serverInfo.id);
|
const serverIdNum = Number(serverInfo.id);
|
||||||
|
|
||||||
const matchedCycles: Array<{
|
const matchedCycles: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
from: string;
|
from: string;
|
||||||
@ -84,19 +84,19 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
nextUpdate: string;
|
nextUpdate: string;
|
||||||
progress: number;
|
progress: number;
|
||||||
}> = []
|
}> = []
|
||||||
|
|
||||||
// 遍历所有流量周期,查找匹配当前服务器ID的数据
|
// 遍历所有流量周期,查找匹配当前服务器ID的数据
|
||||||
Object.values(cycleStats).forEach((cycleData) => {
|
Object.values(cycleStats).forEach((cycleData) => {
|
||||||
|
|
||||||
if (!cycleData.server_name) {
|
if (!cycleData.server_name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverIdsInCycle = Object.keys(cycleData.server_name);
|
const serverIdsInCycle = Object.keys(cycleData.server_name);
|
||||||
|
|
||||||
// 检查各种可能的ID形式
|
// 检查各种可能的ID形式
|
||||||
let matchedId = null;
|
let matchedId = null;
|
||||||
|
|
||||||
// 1. 直接匹配字符串ID
|
// 1. 直接匹配字符串ID
|
||||||
if (serverIdsInCycle.includes(serverId)) {
|
if (serverIdsInCycle.includes(serverId)) {
|
||||||
matchedId = serverId;
|
matchedId = serverId;
|
||||||
@ -104,7 +104,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
// 2. 尝试匹配数字ID (如果API返回的是数字ID)
|
// 2. 尝试匹配数字ID (如果API返回的是数字ID)
|
||||||
else if (serverIdsInCycle.includes(String(serverIdNum))) {
|
else if (serverIdsInCycle.includes(String(serverIdNum))) {
|
||||||
matchedId = String(serverIdNum);
|
matchedId = String(serverIdNum);
|
||||||
}
|
}
|
||||||
// 3. 通过名称匹配
|
// 3. 通过名称匹配
|
||||||
else {
|
else {
|
||||||
// 检查名称是否匹配
|
// 检查名称是否匹配
|
||||||
@ -115,7 +115,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果还没匹配,尝试循环比较所有ID
|
// 如果还没匹配,尝试循环比较所有ID
|
||||||
if (!matchedId) {
|
if (!matchedId) {
|
||||||
for (const id of serverIdsInCycle) {
|
for (const id of serverIdsInCycle) {
|
||||||
@ -126,12 +126,12 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果找到匹配的ID,且有对应的传输数据
|
// 如果找到匹配的ID,且有对应的传输数据
|
||||||
if (matchedId && cycleData.transfer && cycleData.transfer[matchedId] !== undefined) {
|
if (matchedId && cycleData.transfer && cycleData.transfer[matchedId] !== undefined) {
|
||||||
const transfer = cycleData.transfer[matchedId];
|
const transfer = cycleData.transfer[matchedId];
|
||||||
const progress = (transfer / cycleData.max) * 100;
|
const progress = (transfer / cycleData.max) * 100;
|
||||||
|
|
||||||
matchedCycles.push({
|
matchedCycles.push({
|
||||||
name: cycleData.name,
|
name: cycleData.name,
|
||||||
from: cycleData.from,
|
from: cycleData.from,
|
||||||
@ -143,7 +143,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return matchedCycles.length > 0 ? matchedCycles : null;
|
return matchedCycles.length > 0 ? matchedCycles : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,10 +160,10 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
|
|
||||||
// 格式化网络速度
|
// 格式化网络速度
|
||||||
const formatSpeed = (speed: number) => {
|
const formatSpeed = (speed: number) => {
|
||||||
return speed >= 1024
|
return speed >= 1024
|
||||||
? `${(speed / 1024).toFixed(2)}G/s`
|
? `${(speed / 1024).toFixed(2)}G/s`
|
||||||
: speed >= 1
|
: speed >= 1
|
||||||
? `${speed.toFixed(2)}M/s`
|
? `${speed.toFixed(2)}M/s`
|
||||||
: `${(speed * 1024).toFixed(2)}K/s`
|
: `${(speed * 1024).toFixed(2)}K/s`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
if (value > 70) return "text-orange-400"
|
if (value > 70) return "text-orange-400"
|
||||||
return "text-green-500"
|
return "text-green-500"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据进度获取状态颜色
|
// 根据进度获取状态颜色
|
||||||
const getProgressColorClass = (value: number) => {
|
const getProgressColorClass = (value: number) => {
|
||||||
if (value > 90) return "bg-red-500"
|
if (value > 90) return "bg-red-500"
|
||||||
@ -214,14 +214,14 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
<BillingInfo parsedData={parsedData} />
|
<BillingInfo parsedData={parsedData} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{parsedData?.planDataMod && (
|
{parsedData?.planDataMod && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<PlanInfo parsedData={parsedData} />
|
<PlanInfo parsedData={parsedData} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 添加流量使用统计 */}
|
{/* 添加流量使用统计 */}
|
||||||
{serverCycleData && serverCycleData.length > 0 && (
|
{serverCycleData && serverCycleData.length > 0 && (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
@ -273,7 +273,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
onClick={cardClick}
|
onClick={cardClick}
|
||||||
>
|
>
|
||||||
<div className="absolute top-0 left-0 w-1 h-full bg-green-500 rounded-l-md"></div>
|
<div className="absolute top-0 left-0 w-1 h-full bg-green-500 rounded-l-md"></div>
|
||||||
|
|
||||||
<CardHeader className="p-4 pb-2">
|
<CardHeader className="p-4 pb-2">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@ -281,7 +281,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
{showFlag && <ServerFlag country_code={country_code} />}
|
{showFlag && <ServerFlag country_code={country_code} />}
|
||||||
<h3 className="font-bold text-sm truncate">{name}</h3>
|
<h3 className="font-bold text-sm truncate">{name}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center text-xs gap-2 text-muted-foreground">
|
<div className="flex items-center text-xs gap-2 text-muted-foreground">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{platform.includes("Windows") ? (
|
{platform.includes("Windows") ? (
|
||||||
@ -293,14 +293,14 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-start mt-2">
|
<div className="flex justify-between items-start mt-2">
|
||||||
{parsedData?.billingDataMod && (
|
{parsedData?.billingDataMod && (
|
||||||
<div>
|
<div>
|
||||||
<BillingInfo parsedData={parsedData} />
|
<BillingInfo parsedData={parsedData} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col gap-1 items-end">
|
<div className="flex flex-col gap-1 items-end">
|
||||||
{uptime > 0 && (
|
{uptime > 0 && (
|
||||||
<div className="flex items-center text-xs text-muted-foreground">
|
<div className="flex items-center text-xs text-muted-foreground">
|
||||||
@ -308,7 +308,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
<span>{formatUptime(uptime, t)}</span>
|
<span>{formatUptime(uptime, t)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{last_active_time_string && (
|
{last_active_time_string && (
|
||||||
<div className="flex items-center text-xs text-muted-foreground">
|
<div className="flex items-center text-xs text-muted-foreground">
|
||||||
<Calendar className="size-[12px] mr-1" />
|
<Calendar className="size-[12px] mr-1" />
|
||||||
@ -334,14 +334,14 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
{cycle.progress.toFixed(1)}%
|
{cycle.progress.toFixed(1)}%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between items-center text-xs mb-1">
|
<div className="flex justify-between items-center text-xs mb-1">
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<span className="font-medium">{formatBytes(cycle.transfer)}</span>
|
<span className="font-medium">{formatBytes(cycle.transfer)}</span>
|
||||||
<span className="text-[10px] text-muted-foreground">/ {formatBytes(cycle.max)}</span>
|
<span className="text-[10px] text-muted-foreground">/ {formatBytes(cycle.max)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative h-1">
|
<div className="relative h-1">
|
||||||
<div className="absolute inset-0 bg-muted rounded-full" />
|
<div className="absolute inset-0 bg-muted rounded-full" />
|
||||||
<div
|
<div
|
||||||
@ -349,7 +349,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
style={{ width: `${Math.min(cycle.progress, 100)}%` }}
|
style={{ width: `${Math.min(cycle.progress, 100)}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-1 text-[10px] text-muted-foreground flex justify-between">
|
<div className="mt-1 text-[10px] text-muted-foreground flex justify-between">
|
||||||
<span>
|
<span>
|
||||||
{new Date(cycle.from).toLocaleDateString()} - {new Date(cycle.to).toLocaleDateString()}
|
{new Date(cycle.from).toLocaleDateString()} - {new Date(cycle.to).toLocaleDateString()}
|
||||||
@ -364,7 +364,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 主要资源使用情况 - 全新设计 */}
|
{/* 主要资源使用情况 - 全新设计 */}
|
||||||
<div className="grid grid-cols-3 gap-4 mt-3">
|
<div className="grid grid-cols-3 gap-4 mt-3">
|
||||||
{/* CPU使用率 */}
|
{/* CPU使用率 */}
|
||||||
@ -379,8 +379,31 @@ 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>
|
||||||
|
|
||||||
{/* 内存使用率 */}
|
{/* 内存使用率 */}
|
||||||
<div className="bg-muted/40 rounded-lg p-2 flex flex-col">
|
<div className="bg-muted/40 rounded-lg p-2 flex flex-col">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
@ -401,8 +424,43 @@ 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>
|
||||||
|
|
||||||
{/* 存储使用率 */}
|
{/* 存储使用率 */}
|
||||||
<div className="bg-muted/40 rounded-lg p-2 flex flex-col">
|
<div className="bg-muted/40 rounded-lg p-2 flex flex-col">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
@ -415,9 +473,13 @@ 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>
|
||||||
|
|
||||||
{/* 网络使用情况 */}
|
{/* 网络使用情况 */}
|
||||||
<div className="grid grid-cols-2 gap-4 mt-3">
|
<div className="grid grid-cols-2 gap-4 mt-3">
|
||||||
{/* 网络速率 */}
|
{/* 网络速率 */}
|
||||||
@ -437,67 +499,23 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
<span className="text-xs font-medium">{formatSpeed(down)}</span>
|
<span className="text-xs font-medium">{formatSpeed(down)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 连接数与进程数 */}
|
{/* 连接数与进程数 */}
|
||||||
<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">
|
||||||
@ -507,7 +525,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
<PlanInfo parsedData={parsedData} />
|
<PlanInfo parsedData={parsedData} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 网络传输信息 */}
|
{/* 网络传输信息 */}
|
||||||
{showNetTransfer && (
|
{showNetTransfer && (
|
||||||
<div className="grid grid-cols-2 w-full gap-3 mt-1">
|
<div className="grid grid-cols-2 w-full gap-3 mt-1">
|
||||||
@ -528,7 +546,7 @@ export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardPr
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
|
|
||||||
{/* 视觉元素:左侧状态条 */}
|
{/* 视觉元素:左侧状态条 */}
|
||||||
<style>
|
<style>
|
||||||
{`
|
{`
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user