mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-07 06:14:42 +08:00
修复了一些已知问题
This commit is contained in:
@@ -16,8 +16,8 @@ export function RingStat({ value, max = 100, label, subLabel, size = 120, stroke
|
||||
|
||||
const radius = (size - strokeWidth) / 2
|
||||
const circumference = radius * 2 * Math.PI
|
||||
const percentage = Math.min(Math.max(value / max * 100, 0), 100)
|
||||
const strokeDashoffset = circumference - (percentage / 100) * circumference
|
||||
const percentage = max === Infinity ? Math.max(value, 0) : Math.min(Math.max(value / max * 100, 0), 100)
|
||||
const strokeDashoffset = circumference - (Math.min(percentage, 100) / 100) * circumference
|
||||
|
||||
const bgStroke = isDark ? '#374151' : '#f3f4f6'
|
||||
const progressStroke = isDark ? '#f9fafb' : '#000000'
|
||||
@@ -51,7 +51,7 @@ export function RingStat({ value, max = 100, label, subLabel, size = 120, stroke
|
||||
</svg>
|
||||
{/* Center value */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
||||
<span className="text-2xl font-bold text-black dark:text-white">{value.toFixed(percentage < 1 ? 2 : 1)}%</span>
|
||||
<span className="text-2xl font-bold text-black dark:text-white">{percentage.toFixed(percentage < 1 ? 2 : 1)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 text-center">
|
||||
@@ -73,7 +73,6 @@ interface RingStatsProps {
|
||||
swapUsed?: number
|
||||
swapTotal?: number
|
||||
loadPercent: number
|
||||
loadStatus: string
|
||||
diskPercent: number
|
||||
diskUsed: number
|
||||
diskTotal: number
|
||||
@@ -90,7 +89,6 @@ export default function RingStats({
|
||||
swapUsed = 0,
|
||||
swapTotal = 0,
|
||||
loadPercent,
|
||||
loadStatus,
|
||||
diskPercent,
|
||||
diskUsed,
|
||||
diskTotal,
|
||||
@@ -125,8 +123,8 @@ export default function RingStats({
|
||||
)}
|
||||
<RingStat
|
||||
value={loadPercent}
|
||||
max={Infinity}
|
||||
label="负载"
|
||||
subLabel={loadStatus}
|
||||
/>
|
||||
<RingStat
|
||||
value={diskPercent}
|
||||
|
||||
@@ -654,6 +654,7 @@ export default function ContainerDetail() {
|
||||
const filtered = filterHistory(history, range)
|
||||
const cpuPct = clamp(usage?.cpu_usage_pct || 0)
|
||||
const ramPct = container.ram_mb > 0 ? clamp(((usage?.memory_usage_bytes || 0) / (container.ram_mb * 1024 * 1024)) * 100) : 0
|
||||
const loadPct = container.vcpu > 0 ? ((usage?.load1 || 0) / container.vcpu) * 100 : 0
|
||||
const diskPct = container.disk_gb > 0 ? clamp(((usage?.disk_usage_bytes || 0) / (container.disk_gb * 1024 * 1024 * 1024)) * 100) : 0
|
||||
const networkBps = (usage?.network_rx_bps || 0) + (usage?.network_tx_bps || 0)
|
||||
const rx = usage?.network_rx_bps || 0
|
||||
@@ -873,9 +874,9 @@ export default function ContainerDetail() {
|
||||
subLabel={`${formatMB(usage?.memory_usage_bytes || 0)} / ${formatMB(container.ram_mb * 1024 * 1024)}`}
|
||||
/>
|
||||
<RingStat
|
||||
value={Math.min(cpuPct, 100)}
|
||||
value={loadPct}
|
||||
max={Infinity}
|
||||
label="负载"
|
||||
subLabel={cpuPct < 70 ? '正常' : cpuPct < 90 ? '中等' : '高'}
|
||||
/>
|
||||
<RingStat
|
||||
value={diskPct}
|
||||
|
||||
@@ -119,8 +119,7 @@ export default function Dashboard() {
|
||||
ramPercent={host.ram.total_mb > 0 ? (host.ram.used_mb / host.ram.total_mb) * 100 : 0}
|
||||
ramUsed={host.ram.used_mb}
|
||||
ramTotal={host.ram.total_mb}
|
||||
loadPercent={Math.min((host.load.load1 / host.cpu.cores) * 100, 100)}
|
||||
loadStatus={host.load.load1 < host.cpu.cores * 0.7 ? '正常' : host.load.load1 < host.cpu.cores * 1.0 ? '中等' : '高'}
|
||||
loadPercent={(host.load.load1 / Math.max(host.cpu.cores, 1)) * 100}
|
||||
diskPercent={host.disk.total_gb > 0 ? (host.disk.used_gb / host.disk.total_gb) * 100 : 0}
|
||||
diskUsed={host.disk.used_gb * 1024}
|
||||
diskTotal={host.disk.total_gb * 1024}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Network, RefreshCw, Route, Search, Server, X } from 'lucide-react'
|
||||
import { RefreshCw, Search, Server, X } from 'lucide-react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { getRoutingInfo, RoutingInfo, NAT4Route, IPv6Route } from '../services/api'
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function Routing() {
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<CapacityCard
|
||||
title="NAT4 端口"
|
||||
icon={<Route className="h-5 w-5 text-gray-600" />}
|
||||
icon={<Nat4Icon />}
|
||||
remaining={routing?.nat4.remaining || '0'}
|
||||
total={routing?.nat4.total || '0'}
|
||||
used={routing?.nat4.used || 0}
|
||||
@@ -104,7 +104,7 @@ export default function Routing() {
|
||||
/>
|
||||
<CapacityCard
|
||||
title="IPv6 地址"
|
||||
icon={<Network className="h-5 w-5 text-gray-600" />}
|
||||
icon={<IPv6Icon />}
|
||||
remaining={formatCapacity(routing?.ipv6.remaining || '0')}
|
||||
total={formatCapacity(routing?.ipv6.total || '0')}
|
||||
used={routing?.ipv6.used || 0}
|
||||
@@ -137,7 +137,7 @@ export default function Routing() {
|
||||
</div>
|
||||
</div>
|
||||
{nat4Mappings.length === 0 ? (
|
||||
<EmptyState icon={<Route className="h-7 w-7 text-gray-400" />} text="暂无 NAT4 端口映射" />
|
||||
<EmptyState icon={<Nat4Icon className="h-7 w-7" />} text="暂无 NAT4 端口映射" />
|
||||
) : (
|
||||
<>
|
||||
<div className="overflow-x-auto">
|
||||
@@ -214,7 +214,7 @@ export default function Routing() {
|
||||
</div>
|
||||
</div>
|
||||
{ipv6Assignments.length === 0 ? (
|
||||
<EmptyState icon={<Network className="h-7 w-7 text-gray-400" />} text="暂无 IPv6 地址分配" />
|
||||
<EmptyState icon={<IPv6Icon className="h-7 w-7" />} text="暂无 IPv6 地址分配" />
|
||||
) : (
|
||||
<>
|
||||
<div className="overflow-x-auto">
|
||||
@@ -357,3 +357,21 @@ function formatCapacity(value: string): string {
|
||||
if (value === 'large') return '充足'
|
||||
return value
|
||||
}
|
||||
|
||||
function Nat4Icon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
||||
<path d="M797.866667 128c64 0 115.2 51.2 119.466666 110.933333v558.933334c0 64-51.2 115.2-110.933333 119.466666H243.2c-59.733333 0-110.933333-51.2-115.2-110.933333V247.466667C128 187.733333 174.933333 136.533333 234.666667 128h563.2z m38.4 473.6H204.8v196.266667c0 21.333333 17.066667 38.4 38.4 38.4h554.666667c21.333333 0 38.4-17.066667 38.4-38.4v-196.266667z m-315.733334 76.8c21.333333 0 38.4 17.066667 38.4 42.666667 0 17.066667-12.8 34.133333-34.133333 38.4H320c-21.333333 0-38.4-17.066667-38.4-42.666667 0-17.066667 12.8-34.133333 34.133333-38.4h204.8z m157.866667 0c21.333333 0 38.4 17.066667 38.4 42.666667 0 17.066667-12.8 34.133333-34.133333 38.4h-46.933334c-21.333333 0-38.4-17.066667-38.4-42.666667 0-17.066667 12.8-34.133333 34.133334-38.4h46.933333z m119.466667-473.6h-554.666667c-21.333333 0-38.4 17.066667-38.4 38.4v277.333333h631.466667V243.2c0-17.066667-17.066667-34.133333-38.4-38.4z" />
|
||||
<path d="M277.333333 426.666667V243.2h34.133334V426.666667h-34.133334zM426.666667 358.4h-34.133334V426.666667h-34.133333V243.2h72.533333c38.4 0 59.733333 25.6 59.733334 55.466667s-25.6 59.733333-64 59.733333z m-4.266667-81.066667h-34.133333v51.2h34.133333c17.066667 0 25.6-8.533333 25.6-25.6s-8.533333-25.6-25.6-25.6zM571.733333 426.666667h-25.6l-51.2-132.266667h34.133334l25.6 81.066667 25.6-81.066667h34.133333l-42.666667 132.266667zM733.866667 401.066667v25.6h-34.133334v-25.6h-72.533333v-29.866667l64-123.733333h38.4l-64 123.733333h38.4v-34.133333h34.133333v34.133333h17.066667v29.866667h-21.333333z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function IPv6Icon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
||||
<path d="M797.866667 128c64 0 115.2 51.2 119.466666 110.933333v558.933334c0 64-51.2 115.2-110.933333 119.466666H243.2c-59.733333 0-110.933333-51.2-115.2-110.933333V247.466667C128 187.733333 174.933333 136.533333 234.666667 128h563.2z m38.4 473.6H204.8v196.266667c0 21.333333 17.066667 38.4 38.4 38.4h554.666667c21.333333 0 38.4-17.066667 38.4-38.4v-196.266667z m-315.733334 76.8c21.333333 0 38.4 17.066667 38.4 42.666667 0 17.066667-12.8 34.133333-34.133333 38.4H320c-21.333333 0-38.4-17.066667-38.4-42.666667 0-17.066667 12.8-34.133333 34.133333-38.4h204.8z m157.866667 0c21.333333 0 38.4 17.066667 38.4 42.666667 0 17.066667-12.8 34.133333-34.133333 38.4h-46.933334c-21.333333 0-38.4-17.066667-38.4-42.666667 0-17.066667 12.8-34.133333 34.133334-38.4h46.933333z m119.466667-473.6h-554.666667c-21.333333 0-38.4 17.066667-38.4 38.4v277.333333h631.466667V243.2c0-17.066667-17.066667-34.133333-38.4-38.4z" />
|
||||
<path d="M277.333333 426.666667V243.2h34.133334V426.666667h-34.133334zM426.666667 358.4h-34.133334V426.666667h-34.133333V243.2h72.533333c38.4 0 59.733333 25.6 59.733334 55.466667s-25.6 59.733333-64 59.733333z m-4.266667-81.066667h-34.133333v51.2h34.133333c17.066667 0 25.6-8.533333 25.6-25.6s-8.533333-25.6-25.6-25.6zM571.733333 426.666667h-25.6l-51.2-132.266667h34.133334l25.6 81.066667 25.6-81.066667h34.133333l-42.666667 132.266667zM691.2 426.666667c-34.133333 0-55.466667-21.333333-55.466667-55.466667 0-17.066667 8.533333-34.133333 17.066667-46.933333l38.4-76.8h38.4l-38.4 76.8c4.266667 0 8.533333-4.266667 12.8-4.266667 25.6 0 46.933333 21.333333 46.933333 55.466667-4.266667 29.866667-29.866667 51.2-59.733333 51.2z m0-81.066667c-12.8 0-25.6 8.533333-25.6 25.6 0 17.066667 8.533333 25.6 25.6 25.6s25.6-8.533333 25.6-25.6c-4.266667-17.066667-12.8-25.6-25.6-25.6z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -170,6 +170,9 @@ export interface ContainerUsage {
|
||||
disk_write_bytes: number
|
||||
disk_read_bps: number
|
||||
disk_write_bps: number
|
||||
load1: number
|
||||
load5: number
|
||||
load15: number
|
||||
}
|
||||
|
||||
export interface APIResponse<T = unknown> {
|
||||
|
||||
Reference in New Issue
Block a user