mirror of
https://github.com/woodchen-ink/nezha-dash-v1.git
synced 2025-07-19 09:51:56 +08:00
更新 ServerCard 组件,添加流量使用统计功能,支持周期数据展示,优化进度条颜色显示,提升用户对流量使用情况的理解和可视化效果。
This commit is contained in:
parent
6b4ba04995
commit
dfb648a32b
@ -3,7 +3,7 @@ import ServerUsageBar from "@/components/ServerUsageBar"
|
|||||||
import { formatBytes } from "@/lib/format"
|
import { formatBytes } from "@/lib/format"
|
||||||
import { GetFontLogoClass, GetOsName, MageMicrosoftWindows } from "@/lib/logo-class"
|
import { GetFontLogoClass, GetOsName, MageMicrosoftWindows } from "@/lib/logo-class"
|
||||||
import { cn, formatNezhaInfo, parsePublicNote } from "@/lib/utils"
|
import { cn, formatNezhaInfo, parsePublicNote } from "@/lib/utils"
|
||||||
import { NezhaServer } from "@/types/nezha-api"
|
import { CycleTransferData, NezhaServer } from "@/types/nezha-api"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { useNavigate } from "react-router-dom"
|
import { useNavigate } from "react-router-dom"
|
||||||
|
|
||||||
@ -12,9 +12,17 @@ import BillingInfo from "./billingInfo"
|
|||||||
import { Badge } from "./ui/badge"
|
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 } from "lucide-react"
|
import { ArrowDown, ArrowUp, Clock, Cpu, HardDrive, Server, Activity, BarChart3 } from "lucide-react"
|
||||||
|
|
||||||
export default function ServerCard({ now, serverInfo }: { now: number; serverInfo: NezhaServer }) {
|
interface ServerCardProps {
|
||||||
|
now: number;
|
||||||
|
serverInfo: NezhaServer;
|
||||||
|
cycleStats?: {
|
||||||
|
[key: string]: CycleTransferData
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ServerCard({ now, serverInfo, cycleStats }: ServerCardProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const {
|
const {
|
||||||
@ -55,6 +63,49 @@ export default function ServerCard({ now, serverInfo }: { now: number; serverInf
|
|||||||
const showServerDetails = window.ShowServerDetails !== undefined ? window.ShowServerDetails as boolean : true
|
const showServerDetails = window.ShowServerDetails !== undefined ? window.ShowServerDetails as boolean : true
|
||||||
|
|
||||||
const parsedData = parsePublicNote(public_note)
|
const parsedData = parsePublicNote(public_note)
|
||||||
|
|
||||||
|
// 获取匹配当前服务器的流量计费周期
|
||||||
|
const getServerCycleData = () => {
|
||||||
|
if (!cycleStats) return null
|
||||||
|
|
||||||
|
const serverId = serverInfo.id.toString()
|
||||||
|
const matchedCycles: Array<{
|
||||||
|
name: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
max: number;
|
||||||
|
transfer: number;
|
||||||
|
nextUpdate: string;
|
||||||
|
progress: number;
|
||||||
|
}> = []
|
||||||
|
|
||||||
|
// 遍历所有流量周期,查找匹配当前服务器ID的数据
|
||||||
|
Object.values(cycleStats).forEach(cycleData => {
|
||||||
|
if (
|
||||||
|
cycleData.server_name &&
|
||||||
|
cycleData.server_name[serverId] &&
|
||||||
|
cycleData.transfer &&
|
||||||
|
cycleData.transfer[serverId] !== undefined
|
||||||
|
) {
|
||||||
|
const transfer = cycleData.transfer[serverId]
|
||||||
|
const progress = (transfer / cycleData.max) * 100
|
||||||
|
|
||||||
|
matchedCycles.push({
|
||||||
|
name: cycleData.name,
|
||||||
|
from: cycleData.from,
|
||||||
|
to: cycleData.to,
|
||||||
|
max: cycleData.max,
|
||||||
|
transfer: transfer,
|
||||||
|
nextUpdate: cycleData.next_update?.[serverId] || "",
|
||||||
|
progress: progress
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return matchedCycles.length > 0 ? matchedCycles : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverCycleData = getServerCycleData()
|
||||||
|
|
||||||
// 格式化运行时间
|
// 格式化运行时间
|
||||||
const formatUptime = (seconds: number, t: any) => {
|
const formatUptime = (seconds: number, t: any) => {
|
||||||
@ -80,6 +131,13 @@ export default function ServerCard({ now, serverInfo }: { now: number; serverInf
|
|||||||
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) => {
|
||||||
|
if (value > 90) return "bg-red-500"
|
||||||
|
if (value > 70) return "bg-orange-500"
|
||||||
|
return "bg-emerald-500"
|
||||||
|
}
|
||||||
|
|
||||||
if (!online) {
|
if (!online) {
|
||||||
return (
|
return (
|
||||||
@ -111,6 +169,39 @@ export default function ServerCard({ now, serverInfo }: { now: number; serverInf
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 添加流量使用统计 */}
|
||||||
|
{serverCycleData && (
|
||||||
|
<div className="mt-3">
|
||||||
|
{serverCycleData.map((cycle, index) => (
|
||||||
|
<div key={index} className="mt-3 bg-muted/30 rounded-md p-2">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<BarChart3 className="size-[12px] mr-1 text-emerald-500" />
|
||||||
|
<span className="text-xs font-medium">{cycle.name}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 px-2 py-0.5 rounded">
|
||||||
|
{new Date(cycle.from).toLocaleDateString()} - {new Date(cycle.to).toLocaleDateString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center text-xs mt-1">
|
||||||
|
<div className="flex items-baseline gap-1">
|
||||||
|
<span className="font-medium text-xs">{formatBytes(cycle.transfer)}</span>
|
||||||
|
<span className="text-[10px] text-muted-foreground">/ {formatBytes(cycle.max)}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-[10px] font-medium">{cycle.progress.toFixed(1)}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="relative h-1 mt-1">
|
||||||
|
<div className="absolute inset-0 bg-muted rounded-full" />
|
||||||
|
<div
|
||||||
|
className={cn("absolute inset-0 rounded-full transition-all duration-300", getProgressColorClass(cycle.progress))}
|
||||||
|
style={{ width: `${Math.min(cycle.progress, 100)}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
@ -163,6 +254,51 @@ export default function ServerCard({ now, serverInfo }: { now: number; serverInf
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent className="p-4 pt-0 pb-2">
|
<CardContent className="p-4 pt-0 pb-2">
|
||||||
|
{/* 流量使用统计 */}
|
||||||
|
{serverCycleData && serverCycleData.length > 0 && (
|
||||||
|
<div className="mb-3 mt-2">
|
||||||
|
{serverCycleData.map((cycle, index) => (
|
||||||
|
<div key={index} className="bg-muted/40 rounded-lg p-2 mb-2 last:mb-0">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<BarChart3 className="size-[14px] mr-1 text-emerald-500" />
|
||||||
|
<span className="text-xs font-medium">{cycle.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 px-2 py-0.5 rounded text-[10px] font-medium">
|
||||||
|
{cycle.progress.toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center text-xs mb-1">
|
||||||
|
<div className="flex items-baseline gap-1">
|
||||||
|
<span className="font-medium">{formatBytes(cycle.transfer)}</span>
|
||||||
|
<span className="text-[10px] text-muted-foreground">/ {formatBytes(cycle.max)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative h-1">
|
||||||
|
<div className="absolute inset-0 bg-muted rounded-full" />
|
||||||
|
<div
|
||||||
|
className={cn("absolute inset-0 rounded-full transition-all duration-300", getProgressColorClass(cycle.progress))}
|
||||||
|
style={{ width: `${Math.min(cycle.progress, 100)}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-1 text-[10px] text-muted-foreground flex justify-between">
|
||||||
|
<span>
|
||||||
|
{new Date(cycle.from).toLocaleDateString()} - {new Date(cycle.to).toLocaleDateString()}
|
||||||
|
</span>
|
||||||
|
{cycle.nextUpdate && (
|
||||||
|
<span>
|
||||||
|
{t("cycleTransfer.nextUpdate")}: {new Date(cycle.nextUpdate).toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 主要资源使用情况 - 全新设计 */}
|
{/* 主要资源使用情况 - 全新设计 */}
|
||||||
<div className="grid grid-cols-3 gap-4 mt-3">
|
<div className="grid grid-cols-3 gap-4 mt-3">
|
||||||
{/* CPU使用率 */}
|
{/* CPU使用率 */}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user