增加公共页面对比度
This commit is contained in:
1 parent
e10da77a9c
commit
83cd391eab
32 files changed
+112
-119
No files matched your search
@@ -29,14 +29,14 @@ const NetworkStatCard: FC<NetworkStatCardProps> = ({
|
||||
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||
<ArrowUp className="w-3 h-3 text-blue-400 flex-shrink-0"/>
|
||||
<span className="text-cyan-300 truncate">{formatSpeed(uploadRate)}</span>
|
||||
<span className="text-cyan-600 hidden sm:inline">
|
||||
<span className="text-cyan-400 hidden sm:inline">
|
||||
({formatBytes(uploadTotal)})
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 sm:gap-2">
|
||||
<ArrowDown className="w-3 h-3 text-emerald-400 flex-shrink-0"/>
|
||||
<span className="text-cyan-300 truncate">{formatSpeed(downloadRate)}</span>
|
||||
<span className="text-cyan-600 hidden sm:inline">
|
||||
<span className="text-cyan-400 hidden sm:inline">
|
||||
({formatBytes(downloadTotal)})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ const PublicFooter = () => {
|
||||
<footer className="border-t border-cyan-900/50 bg-[#05050a]">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="py-6">
|
||||
<div className="flex flex-col items-center justify-between gap-4 text-xs text-cyan-600 sm:flex-row font-mono">
|
||||
<div className="flex flex-col items-center justify-between gap-4 text-xs text-cyan-400 sm:flex-row font-mono">
|
||||
<div className="flex flex-wrap items-center justify-center gap-2">
|
||||
<span className="text-cyan-500">© {currentYear}</span>
|
||||
<span className="text-cyan-900">|</span>
|
||||
@@ -24,7 +24,7 @@ const PublicFooter = () => {
|
||||
<span className="underline decoration-cyan-700 underline-offset-2">Pika Monitor</span>
|
||||
</a>
|
||||
<span className="text-cyan-900">|</span>
|
||||
<span className="text-cyan-600/80 tracking-wider">保持洞察 · 稳定运行</span>
|
||||
<span className="text-cyan-400/80 tracking-wider">保持洞察 · 稳定运行</span>
|
||||
{/* ICP 备案号 */}
|
||||
{icpCode && (
|
||||
<>
|
||||
@@ -33,7 +33,7 @@ const PublicFooter = () => {
|
||||
href="https://beian.miit.gov.cn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-cyan-600/80 hover:text-cyan-400 transition-colors"
|
||||
className="text-cyan-400/80 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
{icpCode}
|
||||
</a>
|
||||
|
||||
@@ -79,7 +79,7 @@ const PublicHeader = () => {
|
||||
<h1 className="text-2xl font-black tracking-widest text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 via-blue-400 to-purple-400 uppercase italic">
|
||||
{leftName}<span className="text-white">{rightName}</span>
|
||||
</h1>
|
||||
<p className="text-xs text-cyan-500/60 font-mono tracking-[0.3em] uppercase">
|
||||
<p className="text-xstext-cyan-400 font-mono tracking-[0.3em] uppercase">
|
||||
{window.SystemConfig?.SystemNameZh}
|
||||
</p>
|
||||
</div>
|
||||
@@ -96,7 +96,7 @@ const PublicHeader = () => {
|
||||
<button
|
||||
className={`
|
||||
relative group flex items-center gap-2 py-2 text-xs font-bold tracking-widest transition-colors cursor-pointer
|
||||
${activeTab === tab.id ? 'text-cyan-400' : 'text-slate-500 hover:text-cyan-200'}
|
||||
${activeTab === tab.id ? 'text-cyan-400' : 'text-slate-400 hover:text-cyan-200'}
|
||||
`}
|
||||
>
|
||||
<tab.icon
|
||||
@@ -117,7 +117,7 @@ const PublicHeader = () => {
|
||||
<div className="hidden lg:flex flex-col items-end">
|
||||
<span className="text-xs font-mono text-cyan-400">{currentTime.toLocaleTimeString()}</span>
|
||||
<span
|
||||
className="text-xs text-cyan-600 font-mono tracking-widest">{currentTime.toLocaleDateString()}</span>
|
||||
className="text-xs text-cyan-400 font-mono tracking-widest">{currentTime.toLocaleDateString()}</span>
|
||||
</div>
|
||||
<div className="h-8 w-[1px] bg-cyan-900/50 hidden lg:block"></div>
|
||||
|
||||
@@ -176,8 +176,8 @@ const PublicHeader = () => {
|
||||
className={`
|
||||
flex items-center gap-3 p-4 rounded-lg border transition-all
|
||||
${activeTab === tab.id
|
||||
? 'bg-cyan-500/20 border-cyan-500/50 text-cyan-400'
|
||||
: 'bg-cyan-500/5 border-cyan-500/20 text-slate-400 hover:bg-cyan-500/10 hover:border-cyan-500/30'
|
||||
? 'bg-cyan-500/20 border-cyan-500/80 text-cyan-400'
|
||||
: 'bg-cyan-500/5 border-cyan-500/30 text-slate-400 hover:bg-cyan-500/10 hover:border-cyan-500/30'
|
||||
}
|
||||
`}
|
||||
>
|
||||
|
||||
@@ -82,7 +82,7 @@ const ServerCard: FC<ServerCardProps> = ({server, onClick}) => {
|
||||
<div className="font-bold text-cyan-100 font-mono text-base truncate">
|
||||
{server.name || server.hostname}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-cyan-600 mt-1 font-mono uppercase">
|
||||
<div className="flex items-center gap-2 text-xs text-cyan-400 mt-1 font-mono uppercase">
|
||||
<span>{server.os}</span>
|
||||
<span className="w-px h-2 bg-cyan-800"></span>
|
||||
<span>{server.arch}</span>
|
||||
@@ -110,12 +110,12 @@ const ServerCard: FC<ServerCardProps> = ({server, onClick}) => {
|
||||
|
||||
{isOnline && server.metrics?.host && (
|
||||
<div className="flex items-center gap-2 text-xs font-mono mt-1.5">
|
||||
<div className="flex items-center gap-1 text-cyan-700">
|
||||
<div className="flex items-center gap-1 text-cyan-400">
|
||||
<Clock className="w-3 h-3"/>
|
||||
<span>{formatUptime(server.metrics.host.uptime)}</span>
|
||||
</div>
|
||||
<span className="w-px h-2 bg-cyan-800"></span>
|
||||
<div className="flex items-center gap-1 text-cyan-700">
|
||||
<div className="flex items-center gap-1 text-cyan-400">
|
||||
<Activity className="w-3 h-3"/>
|
||||
<span>{server.metrics.host.procs} 进程</span>
|
||||
</div>
|
||||
@@ -152,7 +152,7 @@ const ServerCard: FC<ServerCardProps> = ({server, onClick}) => {
|
||||
{temperatures.map((temp, index) => (
|
||||
<span key={index} className="flex items-center gap-1">
|
||||
<span className="text-orange-400">{temp.temperature?.toFixed(1)}°C</span>
|
||||
<span className="text-cyan-700">{temp.type}</span>
|
||||
<span className="text-cyan-400">{temp.type}</span>
|
||||
{index < temperatures.length - 1 && <span className="text-cyan-900">|</span>}
|
||||
</span>
|
||||
))}
|
||||
@@ -180,7 +180,7 @@ const ServerCard: FC<ServerCardProps> = ({server, onClick}) => {
|
||||
</span>
|
||||
</div>
|
||||
{server.expireTime > 0 && (
|
||||
<div className="text-xs font-mono text-cyan-600 flex items-center gap-1">
|
||||
<div className="text-xs font-mono text-cyan-400 flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3"/>
|
||||
{new Date(server.expireTime).toLocaleDateString('zh-CN')}
|
||||
</div>
|
||||
@@ -191,17 +191,17 @@ const ServerCard: FC<ServerCardProps> = ({server, onClick}) => {
|
||||
<span className="flex items-center gap-1">
|
||||
<Network className="w-3 h-3 text-emerald-400"/>
|
||||
<span className="text-emerald-400">{netConn.established || 0}</span>
|
||||
<span className="text-cyan-700">已建立</span>
|
||||
<span className="text-cyan-400">已建立</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Network className="w-3 h-3 text-blue-400"/>
|
||||
<span className="text-blue-400">{netConn.listen || 0}</span>
|
||||
<span className="text-cyan-700">监听</span>
|
||||
<span className="text-cyan-400">监听</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Network className="w-3 h-3 text-amber-400"/>
|
||||
<span className="text-amber-400">{netConn.closeWait || 0}</span>
|
||||
<span className="text-cyan-700">等待关闭</span>
|
||||
<span className="text-cyan-400">等待关闭</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -210,7 +210,7 @@ const ServerCard: FC<ServerCardProps> = ({server, onClick}) => {
|
||||
{/* 流量限制 */}
|
||||
{server.trafficLimit > 0 && (
|
||||
<div className="pt-2 border-t border-cyan-900/30">
|
||||
<div className="flex justify-between text-xs text-cyan-700 mb-1">
|
||||
<div className="flex justify-between text-xs text-cyan-400 mb-1">
|
||||
<span>流量使用</span>
|
||||
<span>{Math.round((server.trafficUsed || 0) / server.trafficLimit * 100)}%</span>
|
||||
</div>
|
||||
|
||||
@@ -6,10 +6,10 @@ const StatCard = ({title, value, icon: Icon, color}: {
|
||||
color: string;
|
||||
}) => {
|
||||
const colorMap: any = {
|
||||
gray: 'text-slate-400 border-slate-500/30 bg-slate-500/5',
|
||||
emerald: 'text-emerald-400 border-emerald-500/30 bg-emerald-500/5',
|
||||
gray: 'text-slate-400 border-slate-500/40 bg-slate-500/5',
|
||||
emerald: 'text-emerald-500 border-emerald-500/30 bg-emerald-500/5',
|
||||
rose: 'text-rose-400 border-rose-500/30 bg-rose-500/5',
|
||||
blue: 'text-blue-400 border-blue-500/30 bg-blue-500/5'
|
||||
blue: 'text-blue-400 border-blue-500/40 bg-blue-500/5'
|
||||
};
|
||||
const style = colorMap[color] || colorMap.gray;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export const Card = ({
|
||||
{description && (
|
||||
<p className={cn(
|
||||
"mt-1 text-xs",
|
||||
isDark ? "text-cyan-600" : "text-sm text-slate-500 dark:text-slate-400"
|
||||
isDark ? "text-cyan-400" : "text-sm text-slate-500 dark:text-slate-400"
|
||||
)}>
|
||||
{description}
|
||||
</p>
|
||||
|
||||
@@ -22,14 +22,14 @@ export const ChartPlaceholder = ({
|
||||
<div className={cn(
|
||||
"flex items-center justify-center rounded-lg border border-dashed text-sm",
|
||||
isDark
|
||||
? "border-cyan-500/30 bg-[#0a0b10]/50 text-cyan-600"
|
||||
? "border-cyan-500/30 bg-[#0a0b10]/50 text-cyan-400"
|
||||
: "border-slate-200 dark:border-slate-700 text-slate-500 dark:text-slate-400",
|
||||
heightClass
|
||||
)}>
|
||||
<div className="text-center">
|
||||
<Icon className={cn(
|
||||
"mx-auto mb-3 h-10 w-10",
|
||||
isDark ? "text-cyan-900/50" : "text-slate-300 dark:text-slate-600"
|
||||
isDark ? "text-cyan-400/50" : "text-slate-300 dark:text-slate-600"
|
||||
)}/>
|
||||
<p className={cn(
|
||||
isDark ? "uppercase tracking-wider font-mono" : "font-medium"
|
||||
@@ -37,7 +37,7 @@ export const ChartPlaceholder = ({
|
||||
{subtitle && (
|
||||
<p className={cn(
|
||||
"mt-1 text-xs",
|
||||
isDark ? "text-cyan-800" : "text-slate-400 dark:text-slate-500"
|
||||
isDark ? "text-cyan-400" : "text-slate-400 dark:text-slate-500"
|
||||
)}>
|
||||
{subtitle}
|
||||
</p>
|
||||
|
||||
@@ -28,7 +28,7 @@ export const EmptyState = ({variant = 'light', message, showBackButton = false}:
|
||||
</div>
|
||||
<p className={cn(
|
||||
"text-sm font-mono",
|
||||
isDark ? "text-cyan-600" : "text-slate-600 dark:text-slate-400"
|
||||
isDark ? "text-cyan-400" : "text-slate-600 dark:text-slate-400"
|
||||
)}>
|
||||
{message || defaultMessage}
|
||||
</p>
|
||||
|
||||
@@ -11,7 +11,7 @@ const LittleStatCard = ({
|
||||
key={label}
|
||||
className="rounded-xl bg-black/40 border border-cyan-900/50 p-4 text-left hover:border-cyan-700/50 transition"
|
||||
>
|
||||
<p className="text-sm uppercase tracking-[0.3em] text-cyan-600 font-mono font-bold">{label}</p>
|
||||
<p className="text-sm uppercase tracking-[0.3em] text-cyan-400 font-mono font-bold">{label}</p>
|
||||
<p className="mt-2 text-base font-semibold text-cyan-100">{value}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
+8
-5
@@ -1,4 +1,4 @@
|
||||
import {cn} from '@/lib/utils';
|
||||
import {cn} from '@/lib/utils.ts';
|
||||
|
||||
interface StatusBadgeProps {
|
||||
status: string;
|
||||
@@ -6,9 +6,9 @@ interface StatusBadgeProps {
|
||||
|
||||
export const StatusBadge = ({status}: StatusBadgeProps) => {
|
||||
const styles = {
|
||||
up: "bg-emerald-500/10 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border-emerald-500/20",
|
||||
down: "bg-rose-500/10 dark:bg-rose-500/10 text-rose-700 dark:text-rose-400 border-rose-500/20",
|
||||
unknown: "bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 border-slate-200 dark:border-slate-700",
|
||||
up: "bg-emerald-500/10 dark:bg-emerald-500/10 text-emerald-400 dark:text-emerald-400 border-emerald-500/80",
|
||||
down: "bg-rose-500/10 dark:bg-rose-500/10 text-rose-400 dark:text-rose-400 border-rose-500/80",
|
||||
unknown: "bg-slate-100 dark:bg-slate-800 text-slate-400 dark:text-slate-300 border-slate-200 dark:border-slate-700",
|
||||
};
|
||||
|
||||
const labels = {
|
||||
@@ -20,9 +20,10 @@ export const StatusBadge = ({status}: StatusBadgeProps) => {
|
||||
const style = styles[status as keyof typeof styles] || styles.unknown;
|
||||
const label = labels[status as keyof typeof labels] || labels.unknown;
|
||||
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn("px-2.5 py-0.5 rounded-full text-xs font-medium border flex items-center gap-1.5 w-fit", style)}>
|
||||
className={cn("px-2.5 py-0.5 rounded-xl text-xs font-medium border flex items-center gap-1.5 w-fit", style)}>
|
||||
<span className={cn(
|
||||
"w-1.5 h-1.5 rounded-full animate-pulse",
|
||||
status === 'up' ? 'bg-emerald-500 dark:bg-emerald-400' :
|
||||
@@ -31,5 +32,7 @@ export const StatusBadge = ({status}: StatusBadgeProps) => {
|
||||
)}/>
|
||||
{label}
|
||||
</span>
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
@@ -33,7 +33,7 @@ export const TimeRangeSelector = ({
|
||||
? 'border-cyan-500/50 bg-cyan-500/20 text-cyan-300 shadow-[0_0_10px_rgba(34,211,238,0.3)]'
|
||||
: 'border-blue-500 dark:border-blue-500 bg-blue-500 text-white shadow-sm'
|
||||
: isDark
|
||||
? 'border-cyan-900/30 bg-black/30 text-cyan-700 hover:text-cyan-400 hover:border-cyan-700'
|
||||
? 'border-cyan-900/30 bg-black/30 text-cyan-400 hover:text-cyan-400 hover:border-cyan-700'
|
||||
: 'border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:border-blue-300 dark:hover:border-blue-600 hover:text-blue-600 dark:hover:text-blue-400'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {AlertCircle, Clock, MapPin} from 'lucide-react';
|
||||
import {StatusBadge} from './StatusBadge';
|
||||
import {StatusBadge} from '../common/StatusBadge.tsx';
|
||||
import {CertBadge} from './CertBadge';
|
||||
import {AGENT_COLORS} from '@/constants/colors';
|
||||
import {formatDateTime, formatTime} from '@/utils/util';
|
||||
@@ -18,7 +18,7 @@ interface AgentStatsTableProps {
|
||||
export const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProps) => {
|
||||
if (monitorStats.length === 0) {
|
||||
return (
|
||||
<div className="text-center py-12 text-cyan-600">
|
||||
<div className="text-center py-12 text-cyan-400">
|
||||
<p className="text-sm font-mono">暂无探针数据</p>
|
||||
</div>
|
||||
);
|
||||
@@ -28,31 +28,31 @@ export const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProp
|
||||
<CyberCard className="p-6">
|
||||
<div className="mb-6">
|
||||
<h3 className="text-lg font-bold tracking-wide text-cyan-100 uppercase">探针监控详情</h3>
|
||||
<p className="text-xs text-cyan-600 mt-1 font-mono">各探针的当前状态和统计数据</p>
|
||||
<p className="text-xs text-cyan-400 mt-1 font-mono">各探针的当前状态和统计数据</p>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto -mx-6 px-6">
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-cyan-900/50">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widest text-cyan-500/60 font-mono">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widesttext-cyan-400 font-mono">
|
||||
探针名称
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widest text-cyan-500/60 font-mono">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widesttext-cyan-400 font-mono">
|
||||
状态
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widest text-cyan-500/60 font-mono">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widesttext-cyan-400 font-mono">
|
||||
响应时间
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widest text-cyan-500/60 font-mono hidden lg:table-cell">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widesttext-cyan-400 font-mono hidden lg:table-cell">
|
||||
最后检测
|
||||
</th>
|
||||
{monitorType === 'https' && (
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widest text-cyan-500/60 font-mono hidden xl:table-cell">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widesttext-cyan-400 font-mono hidden xl:table-cell">
|
||||
证书信息
|
||||
</th>
|
||||
)}
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widest text-cyan-500/60 font-mono hidden xl:table-cell">
|
||||
<th className="px-4 py-3 text-left text-xs font-semibold uppercase tracking-widesttext-cyan-400 font-mono hidden xl:table-cell">
|
||||
错误信息
|
||||
</th>
|
||||
</tr>
|
||||
@@ -70,7 +70,7 @@ export const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProp
|
||||
style={{backgroundColor: color}}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<MapPin className="h-3.5 w-3.5 text-cyan-600"/>
|
||||
<MapPin className="h-3.5 w-3.5 text-cyan-400"/>
|
||||
<span className="font-mono text-sm text-cyan-200">
|
||||
{stat.agentName || stat.agentId.substring(0, 8)}
|
||||
</span>
|
||||
@@ -82,7 +82,7 @@ export const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProp
|
||||
</td>
|
||||
<td className="px-4 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="h-4 w-4 text-cyan-600"/>
|
||||
<Clock className="h-4 w-4 text-cyan-400"/>
|
||||
<span className="text-sm font-semibold text-cyan-100 font-mono">
|
||||
{formatTime(stat.responseTime)}
|
||||
</span>
|
||||
@@ -99,7 +99,7 @@ export const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProp
|
||||
daysLeft={stat.certDaysLeft}
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xs text-cyan-600">-</span>
|
||||
<span className="text-xs text-cyan-400">-</span>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
@@ -114,7 +114,7 @@ export const AgentStatsTable = ({monitorStats, monitorType}: AgentStatsTableProp
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs text-cyan-600">-</span>
|
||||
<span className="text-xs text-cyan-400">-</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -4,7 +4,7 @@ import {useQuery} from "@tanstack/react-query";
|
||||
import {type GetMetricsResponse, getMonitorHistory} from "@/api/monitor.ts";
|
||||
import {useMemo} from "react";
|
||||
import CyberCard from "@/components/CyberCard.tsx";
|
||||
import {StatusBadge} from "@/components/monitor/StatusBadge.tsx";
|
||||
import {StatusBadge} from "@/components/common/StatusBadge.tsx";
|
||||
import {CertBadge} from "@/components/monitor/CertBadge.tsx";
|
||||
import {formatDateTime} from "@/utils/util.ts";
|
||||
import {MiniChart} from "@/components/monitor/MiniChart.tsx";
|
||||
@@ -85,7 +85,7 @@ const MonitorCard = ({monitor, displayMode}: {
|
||||
<h3 className="font-bold text-sm text-cyan-100 tracking-wide truncate group-hover:text-cyan-400 transition-colors">
|
||||
{monitor.name}
|
||||
</h3>
|
||||
<div className="text-xs font-mono text-cyan-500/60 mb-0.5 tracking-wider truncate">
|
||||
<div className="text-xs font-mono text-cyan-400 mb-0.5 tracking-wider truncate">
|
||||
{monitor.target}
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,7 +98,7 @@ const MonitorCard = ({monitor, displayMode}: {
|
||||
{/* 指标信息 */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<p className="text-xs text-cyan-500/60 mb-1 flex items-center gap-1">
|
||||
<p className="text-xs text-cyan-400 mb-1 flex items-center gap-1">
|
||||
{displayLabel}
|
||||
{monitor.agentCount > 0 && (
|
||||
<span
|
||||
@@ -109,13 +109,13 @@ const MonitorCard = ({monitor, displayMode}: {
|
||||
</p>
|
||||
<div
|
||||
className={`text-xl font-bold flex items-baseline gap-1 ${displayValue > 200 ? 'text-amber-400 drop-shadow-[0_0_8px_rgba(251,191,36,0.5)]' : 'text-white drop-shadow-[0_0_8px_rgba(34,211,238,0.5)]'}`}>
|
||||
{displayValue}<span className="text-xs text-cyan-600 font-normal">ms</span>
|
||||
{displayValue}<span className="text-xs text-cyan-400 font-normal">ms</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{monitor.type === 'https' && monitor.certExpiryTime ? (
|
||||
<>
|
||||
<p className="text-xs text-cyan-500/60 mb-1">SSL 证书</p>
|
||||
<p className="text-xs text-cyan-400 mb-1">SSL 证书</p>
|
||||
<CertBadge
|
||||
expiryTime={monitor.certExpiryTime}
|
||||
daysLeft={monitor.certDaysLeft}
|
||||
@@ -123,7 +123,7 @@ const MonitorCard = ({monitor, displayMode}: {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-xs text-cyan-500/60 mb-1">上次检测</p>
|
||||
<p className="text-xs text-cyan-400 mb-1">上次检测</p>
|
||||
<p className="text-sm font-medium text-cyan-300 font-mono">
|
||||
{formatDateTime(monitor.lastCheckTime)}
|
||||
</p>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {ArrowLeft} from 'lucide-react';
|
||||
import {TypeIcon} from './TypeIcon';
|
||||
import {StatusBadge} from './StatusBadge';
|
||||
import {StatusBadge} from '../common/StatusBadge.tsx';
|
||||
import {CertBadge} from './CertBadge';
|
||||
import LittleStatCard from '@/components/common/LittleStatCard';
|
||||
import type {PublicMonitor} from '@/types';
|
||||
@@ -22,7 +22,7 @@ export const MonitorHero = ({monitor, onBack}: MonitorHeroProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
className="group inline-flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-cyan-600 hover:text-cyan-400 transition font-mono"
|
||||
className="group inline-flex items-center gap-2 text-xs font-medium uppercase tracking-wider text-cyan-400 hover:text-cyan-400 transition font-mono"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4 transition-transform group-hover:-translate-x-1"/>
|
||||
返回监控列表
|
||||
@@ -69,7 +69,7 @@ export const MonitorHero = ({monitor, onBack}: MonitorHeroProps) => {
|
||||
{/* 证书信息(如果是 HTTPS)*/}
|
||||
{monitor.type === 'https' && monitor.certExpiryTime && (
|
||||
<div className="flex items-center gap-2 pt-4 border-t border-cyan-900/50">
|
||||
<span className="text-xs text-cyan-500/60 font-mono">SSL 证书:</span>
|
||||
<span className="text-xstext-cyan-400 font-mono">SSL 证书:</span>
|
||||
<CertBadge
|
||||
expiryTime={monitor.certExpiryTime}
|
||||
daysLeft={monitor.certDaysLeft}
|
||||
|
||||
@@ -112,7 +112,7 @@ export const ResponseTimeChart = ({monitorId, monitorStats}: ResponseTimeChartPr
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-bold tracking-wide text-cyan-100 uppercase">响应时间趋势</h3>
|
||||
<p className="text-xs text-cyan-600 mt-1 font-mono">监控各探针的响应时间变化</p>
|
||||
<p className="text-xs text-cyan-400 mt-1 font-mono">监控各探针的响应时间变化</p>
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row flex-wrap items-start sm:items-center gap-3">
|
||||
<TimeRangeSelector
|
||||
@@ -167,7 +167,7 @@ export const ResponseTimeChart = ({monitorId, monitorStats}: ResponseTimeChartPr
|
||||
scale="time"
|
||||
domain={['dataMin', 'dataMax']}
|
||||
tickFormatter={(value) => formatChartTime(Number(value), timeRange)}
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
stroke="#164e63"
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
@@ -175,7 +175,7 @@ export const ResponseTimeChart = ({monitorId, monitorStats}: ResponseTimeChartPr
|
||||
textAnchor="end"
|
||||
/>
|
||||
<YAxis
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
stroke="#164e63"
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export {CertBadge} from './CertBadge';
|
||||
export {MiniChart} from './MiniChart';
|
||||
export {StatusBadge} from './StatusBadge';
|
||||
export {StatusBadge} from '../common/StatusBadge.tsx';
|
||||
export {TypeIcon} from './TypeIcon';
|
||||
export {MonitorHero} from './MonitorHero';
|
||||
export {ResponseTimeChart} from './ResponseTimeChart';
|
||||
|
||||
@@ -33,7 +33,7 @@ export const GpuMonitorSection = ({latestMetrics}: GpuMonitorSectionProps) => {
|
||||
</span>
|
||||
<div>
|
||||
<p className="text-sm font-bold font-mono text-cyan-100">GPU {gpu.index}</p>
|
||||
<p className="text-xs text-cyan-600">{gpu.name}</p>
|
||||
<p className="text-xs text-cyan-400">{gpu.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-2xl font-bold text-purple-400">
|
||||
@@ -42,21 +42,21 @@ export const GpuMonitorSection = ({latestMetrics}: GpuMonitorSectionProps) => {
|
||||
</div>
|
||||
<div className="space-y-2 text-xs">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-cyan-600 font-mono text-xs uppercase tracking-wider">温度</span>
|
||||
<span className="text-cyan-400 font-mono text-xs uppercase tracking-wider">温度</span>
|
||||
<span className="font-medium text-cyan-200">{gpu.temperature?.toFixed(1)}°C</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-cyan-600 font-mono text-xs uppercase tracking-wider">显存</span>
|
||||
<span className="text-cyan-400 font-mono text-xs uppercase tracking-wider">显存</span>
|
||||
<span className="font-medium text-cyan-200">
|
||||
{formatBytes(gpu.memoryUsed)} / {formatBytes(gpu.memoryTotal)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-cyan-600 font-mono text-xs uppercase tracking-wider">功耗</span>
|
||||
<span className="text-cyan-400 font-mono text-xs uppercase tracking-wider">功耗</span>
|
||||
<span className="font-medium text-cyan-200">{gpu.powerDraw?.toFixed(1)}W</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-cyan-600 font-mono text-xs uppercase tracking-wider">风扇转速</span>
|
||||
<span className="text-cyan-400 font-mono text-xs uppercase tracking-wider">风扇转速</span>
|
||||
<span className="font-medium text-cyan-200">{gpu.fanSpeed?.toFixed(0)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ export const InfoGrid = ({items}: InfoGridProps) => (
|
||||
<dl className="grid grid-cols-1 gap-4 text-sm sm:grid-cols-2">
|
||||
{items.map((item) => (
|
||||
<div key={item.label}>
|
||||
<dt className="text-xs font-mono uppercase tracking-widest text-cyan-600">{item.label}</dt>
|
||||
<dt className="text-xs font-mono uppercase tracking-widest text-cyan-400">{item.label}</dt>
|
||||
<dd className="mt-1 font-medium text-cyan-100">{item.value}</dd>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -20,31 +20,31 @@ export const NetworkConnectionSection = ({latestMetrics}: NetworkConnectionSecti
|
||||
<Card title="网络连接统计" description="TCP 连接各状态的实时统计数据" variant="dark">
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6">
|
||||
<div className="text-center">
|
||||
<div className="text-xs text-cyan-600 font-mono uppercase tracking-wider">Total</div>
|
||||
<div className="text-xs text-cyan-400 font-mono uppercase tracking-wider">Total</div>
|
||||
<div className="mt-1 text-lg font-semibold text-cyan-100">{latestMetrics.networkConnection.total}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xs text-cyan-600 font-mono uppercase tracking-wider">ESTABLISHED</div>
|
||||
<div className="text-xs text-cyan-400 font-mono uppercase tracking-wider">ESTABLISHED</div>
|
||||
<div
|
||||
className="mt-1 text-lg font-semibold text-emerald-400">{latestMetrics.networkConnection.established}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xs text-cyan-600 font-mono uppercase tracking-wider">TIME_WAIT</div>
|
||||
<div className="text-xs text-cyan-400 font-mono uppercase tracking-wider">TIME_WAIT</div>
|
||||
<div
|
||||
className="mt-1 text-lg font-semibold text-amber-400">{latestMetrics.networkConnection.timeWait}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xs text-cyan-600 font-mono uppercase tracking-wider">LISTEN</div>
|
||||
<div className="text-xs text-cyan-400 font-mono uppercase tracking-wider">LISTEN</div>
|
||||
<div
|
||||
className="mt-1 text-lg font-semibold text-blue-400">{latestMetrics.networkConnection.listen}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xs text-cyan-600 font-mono uppercase tracking-wider">CLOSE_WAIT</div>
|
||||
<div className="text-xs text-cyan-400 font-mono uppercase tracking-wider">CLOSE_WAIT</div>
|
||||
<div
|
||||
className="mt-1 text-lg font-semibold text-rose-400">{latestMetrics.networkConnection.closeWait}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-xs text-cyan-600 font-mono uppercase tracking-wider">OTHER</div>
|
||||
<div className="text-xs text-cyan-400 font-mono uppercase tracking-wider">OTHER</div>
|
||||
<div className="mt-1 text-lg font-semibold text-cyan-500">
|
||||
{latestMetrics.networkConnection.synSent +
|
||||
latestMetrics.networkConnection.synRecv +
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {ArrowLeft} from 'lucide-react';
|
||||
import {cn} from '@/lib/utils';
|
||||
import {formatBytes, formatDateTime, formatUptime} from '@/utils/util';
|
||||
import type {Agent, LatestMetrics} from '@/types';
|
||||
import LittleStatCard from '@/components/common/LittleStatCard';
|
||||
import CyberCard from "@/components/CyberCard.tsx";
|
||||
import {StatusBadge} from "@/components/common/StatusBadge.tsx";
|
||||
|
||||
interface ServerHeroProps {
|
||||
agent: Agent;
|
||||
@@ -49,7 +49,7 @@ export const ServerHero = ({agent, latestMetrics, onBack}: ServerHeroProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
className="group inline-flex items-center gap-2 text-xs font-bold font-mono uppercase tracking-[0.3em] text-cyan-600 transition hover:text-cyan-400"
|
||||
className="group inline-flex items-center gap-2 text-xs font-bold font-mono uppercase tracking-[0.3em] text-cyan-400 transition hover:text-cyan-400"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4 transition group-hover:-translate-x-0.5"/>
|
||||
返回概览
|
||||
@@ -58,19 +58,9 @@ export const ServerHero = ({agent, latestMetrics, onBack}: ServerHeroProps) => {
|
||||
<div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<h1 className="text-3xl font-bold text-cyan-100">{displayName}</h1>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center gap-1 rounded-full px-3 py-0.5 text-xs font-bold font-mono uppercase tracking-wider",
|
||||
isOnline
|
||||
? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/50'
|
||||
: 'bg-rose-500/20 text-rose-400 border border-rose-500/50'
|
||||
)}
|
||||
>
|
||||
<span className={cn("h-1.5 w-1.5 rounded-full", statusDotStyles)}/>
|
||||
{statusText}
|
||||
</span>
|
||||
<StatusBadge status={agent.status === 1 ? 'up' : 'down'}/>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-cyan-600 font-mono">
|
||||
<p className="mt-2 text-sm text-cyan-400 font-mono">
|
||||
{[agent.hostname, agent.ip].filter(Boolean).join(' · ') || '-'}
|
||||
</p>
|
||||
</div>
|
||||
@@ -84,7 +74,7 @@ export const ServerHero = ({agent, latestMetrics, onBack}: ServerHeroProps) => {
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-wrap items-center gap-3 text-xs text-cyan-600 font-mono pt-4 border-t border-cyan-900/30">
|
||||
className="flex flex-wrap items-center gap-3 text-xs text-cyan-400 font-mono pt-4 border-t border-cyan-900/30">
|
||||
<span>探针 ID:{agent.id}</span>
|
||||
<span className="hidden h-1 w-1 rounded-full bg-cyan-900 sm:inline-block"/>
|
||||
<span>版本:{agent.version || '-'}</span>
|
||||
|
||||
@@ -61,7 +61,7 @@ export const SnapshotGrid = ({cards}: SnapshotGridProps) => (
|
||||
<div className="space-y-2">
|
||||
{card.metrics.map((metric) => (
|
||||
<div key={metric.label} className="flex items-center justify-between text-xs">
|
||||
<span className="text-cyan-600 font-mono text-xs uppercase tracking-wider">{metric.label}</span>
|
||||
<span className="text-cyan-400 font-mono text-xs uppercase tracking-wider">{metric.label}</span>
|
||||
<span
|
||||
className="ml-2 text-right font-medium text-cyan-200">{metric.value}</span>
|
||||
</div>
|
||||
|
||||
@@ -158,14 +158,14 @@ export const SystemInfoSection = ({agent, latestMetrics}: SystemInfoSectionProps
|
||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<CyberCard className={'p-6'}>
|
||||
<h3 className="text-sm font-bold font-mono uppercase tracking-widest text-cyan-400">运行环境</h3>
|
||||
<p className="mt-1 text-xs text-cyan-700">来自最近一次探针上报的硬件与系统信息</p>
|
||||
<p className="mt-1 text-xs text-cyan-400">来自最近一次探针上报的硬件与系统信息</p>
|
||||
<div className="mt-4">
|
||||
<InfoGrid items={environmentInfo}/>
|
||||
</div>
|
||||
</CyberCard>
|
||||
<CyberCard className={'p-6'}>
|
||||
<h3 className="text-sm font-bold font-mono uppercase tracking-widest text-cyan-400">运行状态</h3>
|
||||
<p className="mt-1 text-xs text-cyan-700">关键时间与网络指标,帮助快速判断主机健康状况</p>
|
||||
<p className="mt-1 text-xs text-cyan-400">关键时间与网络指标,帮助快速判断主机健康状况</p>
|
||||
<div className="mt-4">
|
||||
<InfoGrid items={statusInfo}/>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ export const TemperatureMonitorSection = ({latestMetrics}: TemperatureMonitorSec
|
||||
className="rounded-xl border border-cyan-900/50 bg-black/30 p-4 backdrop-blur-sm hover:border-cyan-700/50 transition"
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Thermometer className="h-4 w-4 text-cyan-600"/>
|
||||
<Thermometer className="h-4 w-4 text-cyan-400"/>
|
||||
<p className="text-xs font-bold font-mono uppercase tracking-wider text-cyan-400 truncate">{temp.type}</p>
|
||||
</div>
|
||||
<p className="text-2xl font-bold text-orange-400">{temp.temperature.toFixed(1)}°C</p>
|
||||
|
||||
@@ -63,7 +63,7 @@ export const CpuChart = ({agentId, timeRange}: CpuChartProps) => {
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
/>
|
||||
<YAxis
|
||||
domain={[0, 100]}
|
||||
|
||||
@@ -85,7 +85,7 @@ export const DiskIOChart = ({agentId, timeRange}: DiskIOChartProps) => {
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
height={45}
|
||||
/>
|
||||
<YAxis
|
||||
|
||||
@@ -63,7 +63,7 @@ export const MemoryChart = ({agentId, timeRange}: MemoryChartProps) => {
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
/>
|
||||
<YAxis
|
||||
domain={[0, 100]}
|
||||
|
||||
@@ -109,7 +109,7 @@ export const MonitorChart = ({agentId, timeRange}: MonitorChartProps) => {
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
height={45}
|
||||
/>
|
||||
<YAxis
|
||||
|
||||
@@ -117,7 +117,7 @@ export const NetworkChart = ({agentId, timeRange}: NetworkChartProps) => {
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
height={45}
|
||||
/>
|
||||
<YAxis
|
||||
|
||||
@@ -75,7 +75,7 @@ export const NetworkConnectionChart = ({agentId, timeRange}: NetworkConnectionCh
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
height={45}
|
||||
/>
|
||||
<YAxis
|
||||
|
||||
@@ -114,7 +114,7 @@ export const TemperatureChart = ({agentId, timeRange}: TemperatureChartProps) =>
|
||||
stroke="currentColor"
|
||||
angle={-15}
|
||||
textAnchor="end"
|
||||
className="text-xs text-cyan-600 font-mono"
|
||||
className="text-xs text-cyan-400 font-mono"
|
||||
height={45}
|
||||
/>
|
||||
<YAxis
|
||||
|
||||
@@ -11,7 +11,7 @@ import MonitorCard from "@/components/monitor/MonitorCard.tsx";
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<div className="flex min-h-[400px] w-full items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-3 text-cyan-600">
|
||||
<div className="flex flex-col items-center gap-3 text-cyan-400">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-cyan-400"/>
|
||||
<span className="text-sm font-mono">加载监控数据中...</span>
|
||||
</div>
|
||||
@@ -22,7 +22,7 @@ const EmptyState = () => (
|
||||
<div className="flex min-h-[400px] flex-col items-center justify-center text-cyan-500">
|
||||
<Shield className="mb-4 h-16 w-16 opacity-20"/>
|
||||
<p className="text-lg font-medium font-mono">暂无监控数据</p>
|
||||
<p className="mt-2 text-sm text-cyan-600">请先在管理后台添加监控任务</p>
|
||||
<p className="mt-2 text-sm text-cyan-400">请先在管理后台添加监控任务</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -142,7 +142,7 @@ const MonitorList = () => {
|
||||
"px-4 py-1.5 rounded-md text-xs font-medium transition-all font-mono cursor-pointer",
|
||||
filter === f
|
||||
? 'bg-cyan-500/20 text-cyan-300 border border-cyan-500/30'
|
||||
: 'text-cyan-600 hover:text-cyan-400'
|
||||
: 'text-cyan-400 hover:text-cyan-400'
|
||||
)}
|
||||
>
|
||||
{labels[f]}
|
||||
@@ -153,14 +153,14 @@ const MonitorList = () => {
|
||||
|
||||
{/* 显示模式切换 */}
|
||||
<div className="flex gap-1 bg-black/40 p-1 rounded-lg border border-cyan-900/50 items-center">
|
||||
<span className="text-xs text-cyan-600 px-2 font-mono">卡片指标:</span>
|
||||
<span className="text-xs text-cyan-400 px-2 font-mono">卡片指标:</span>
|
||||
<button
|
||||
onClick={() => setDisplayMode('avg')}
|
||||
className={cn(
|
||||
"px-3 py-1.5 text-xs font-medium rounded transition-all flex items-center gap-1 font-mono cursor-pointer",
|
||||
displayMode === 'avg'
|
||||
? 'bg-cyan-500/20 text-cyan-300 border border-cyan-500/30'
|
||||
: 'text-cyan-600 hover:text-cyan-400'
|
||||
: 'text-cyan-400 hover:text-cyan-400'
|
||||
)}
|
||||
>
|
||||
<BarChart3 className="w-3 h-3"/> 平均
|
||||
@@ -171,7 +171,7 @@ const MonitorList = () => {
|
||||
"px-3 py-1.5 text-xs font-medium rounded transition-all flex items-center gap-1 font-mono cursor-pointer",
|
||||
displayMode === 'max'
|
||||
? 'bg-cyan-500/20 text-cyan-300 border border-cyan-500/30'
|
||||
: 'text-cyan-600 hover:text-cyan-400'
|
||||
: 'text-cyan-400 hover:text-cyan-400'
|
||||
)}
|
||||
>
|
||||
<Maximize2 className="w-3 h-3"/> 最差(Max)
|
||||
@@ -184,7 +184,7 @@ const MonitorList = () => {
|
||||
<div
|
||||
className="absolute -inset-0.5 bg-gradient-to-r from-cyan-500 to-blue-600 rounded-lg blur opacity-20 group-hover:opacity-40 transition duration-500"></div>
|
||||
<div className="relative flex items-center bg-[#0a0b10] rounded-lg border border-cyan-900">
|
||||
<Search className="w-4 h-4 ml-3 text-cyan-600"/>
|
||||
<Search className="w-4 h-4 ml-3 text-cyan-400"/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索服务名称或地址..."
|
||||
|
||||
@@ -53,7 +53,7 @@ const EmptyState = ({title, description, extra}: EmptyStateProps) => (
|
||||
<HardDrive className="h-7 w-7"/>
|
||||
</div>
|
||||
<h3 className="mt-4 text-base font-semibold text-cyan-100 font-mono">{title}</h3>
|
||||
<p className="mt-2 max-w-sm text-sm text-cyan-600">{description}</p>
|
||||
<p className="mt-2 max-w-sm text-sm text-cyan-400">{description}</p>
|
||||
{extra ? <div className="mt-4">{extra}</div> : null}
|
||||
</div>
|
||||
);
|
||||
@@ -200,7 +200,7 @@ const ServerList = () => {
|
||||
{allTags.length > 1 && (
|
||||
<div className="flex flex-wrap gap-1.5 sm:gap-2 items-center">
|
||||
<div
|
||||
className="text-sm sm:text-xs font-mono text-cyan-600 flex items-center gap-1.5 sm:gap-2 mr-1 sm:mr-2">
|
||||
className="text-sm sm:text-xs font-mono text-cyan-400 flex items-center gap-1.5 sm:gap-2 mr-1 sm:mr-2">
|
||||
<Filter className="w-3 h-3"/>
|
||||
<span className="hidden sm:inline">FILTERS:</span>
|
||||
</div>
|
||||
@@ -222,7 +222,7 @@ const ServerList = () => {
|
||||
"px-3 py-1 rounded-full text-xs font-bold font-mono tracking-wider transition-all border cursor-pointer",
|
||||
selectedTag === tagKey
|
||||
? 'bg-cyan-500/20 text-cyan-300 border-cyan-500/50 shadow-[0_0_10px_rgba(34,211,238,0.3)]'
|
||||
: 'bg-black/30 text-cyan-700 border-cyan-900/30 hover:text-cyan-400 hover:border-cyan-700'
|
||||
: 'bg-black/30 text-cyan-400 border-cyan-900/30 hover:text-cyan-400 hover:border-cyan-700'
|
||||
)}
|
||||
>
|
||||
{tag} ({count})
|
||||
@@ -245,7 +245,7 @@ const ServerList = () => {
|
||||
className="hidden md:block bg-[#0a0b10]/90 border border-cyan-900/50 rounded-md overflow-hidden shadow-2xl backdrop-blur-sm">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-black/40 text-sm font-mono uppercase tracking-widest text-cyan-600 border-b border-cyan-900/50">
|
||||
<tr className="bg-black/40 text-sm font-mono uppercase tracking-widest text-cyan-400 border-b border-cyan-900/50">
|
||||
<th className="p-4 font-normal w-[300px]">System Identity</th>
|
||||
<th className="p-4 font-normal">Resource Telemetry</th>
|
||||
<th className="p-4 font-normal w-[200px]">Network I/O</th>
|
||||
@@ -289,18 +289,18 @@ const ServerList = () => {
|
||||
{server.name || server.hostname}
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-2 text-xs text-cyan-600 mt-1 font-mono uppercase">
|
||||
className="flex items-center gap-2 text-xs text-cyan-400 mt-1 font-mono uppercase">
|
||||
<span>{server.os}</span>
|
||||
<span className="w-px h-2 bg-cyan-800"></span>
|
||||
<span>{server.arch}</span>
|
||||
</div>
|
||||
{isOnline && server.metrics?.host && (
|
||||
<div className="flex items-center gap-3 text-xs font-mono mt-1">
|
||||
<div className="flex items-center gap-1 text-cyan-700">
|
||||
<div className="flex items-center gap-1 text-cyan-400">
|
||||
<Clock className="w-3 h-3"/>
|
||||
<span>{formatUptime(server.metrics.host.uptime)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-cyan-700">
|
||||
<div className="flex items-center gap-1 text-cyan-400">
|
||||
<Activity className="w-3 h-3"/>
|
||||
<span>{server.metrics.host.procs} 进程</span>
|
||||
</div>
|
||||
@@ -341,7 +341,7 @@ const ServerList = () => {
|
||||
{temperatures.map((temp, index) => (
|
||||
<span key={index} className="flex items-center gap-1">
|
||||
<span className="text-orange-400">{temp.temperature?.toFixed(1)}°C</span>
|
||||
<span className="text-cyan-700">{temp.type}</span>
|
||||
<span className="text-cyan-400">{temp.type}</span>
|
||||
{index < temperatures.length - 1 && <span className="text-cyan-900">|</span>}
|
||||
</span>
|
||||
))}
|
||||
@@ -372,7 +372,7 @@ const ServerList = () => {
|
||||
{server.trafficLimit > 0 && (
|
||||
<div className="w-32 text-xs">
|
||||
<div
|
||||
className="flex justify-between text-cyan-700 mb-0.5">
|
||||
className="flex justify-between text-cyan-400 mb-0.5">
|
||||
<span className={''}>流量使用</span>
|
||||
<span>{Math.round((server.trafficUsed || 0) / server.trafficLimit * 100)}%</span>
|
||||
</div>
|
||||
@@ -391,21 +391,21 @@ const ServerList = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
<Network className="w-3 h-3 text-emerald-400"/>
|
||||
<span className="text-emerald-400">{netConn.established || 0}</span>
|
||||
<span className="text-cyan-700">已建立</span>
|
||||
<span className="text-cyan-400">已建立</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Network className="w-3 h-3 text-blue-400"/>
|
||||
<span className="text-blue-400">{netConn.listen || 0}</span>
|
||||
<span className="text-cyan-700">监听中</span>
|
||||
<span className="text-cyan-400">监听中</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Network className="w-3 h-3 text-rose-400"/>
|
||||
<span className="text-rose-400">{netConn.closeWait || 0}</span>
|
||||
<span className="text-cyan-700">等待关闭</span>
|
||||
<span className="text-cyan-400">等待关闭</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-cyan-700/50">-</div>
|
||||
<div className="text-cyan-500">-</div>
|
||||
)}
|
||||
</td>
|
||||
|
||||
@@ -421,7 +421,7 @@ const ServerList = () => {
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className={`text-xs font-mono flex items-center gap-1 ${server.expireTime && server.expireTime > 0 ? 'text-cyan-600' : 'text-emerald-500/60'}`}>
|
||||
className={`text-xs font-mono flex items-center gap-1 ${server.expireTime && server.expireTime > 0 ? 'text-cyan-400' : 'text-emerald-400/80'}`}>
|
||||
|
||||
{server.expireTime > 0 &&
|
||||
<div className={'flex items-center gap-1'}>
|
||||
|
||||
Reference in new issue
Block a user