feat: 全站 PageShell 设计系统与专业 UI 统一
- 新增 PageShell/PageTabs/ContentPanel/PageLoading/PageEmpty 页面骨架 - index.css 设计令牌文档化,布局令牌与纯色主按钮(去除渐变粉彩) - 全站路由页接入 PageShell,云账单三 Tab 为标杆 - 主题体系:跟随系统 + 主流/开发者分组,新增 Dracula/Tokyo Night,One Dark 暖灰差异化 - 登录页/侧栏/顶栏去渐变光球,企业克制风 - 修复 AIAlertAnalyzer JSX 与构建依赖导入问题
This commit is contained in:
1 parent
1acb0c49af
commit
e363ded445
213 files changed
+7148
-6818
No files matched your search
@@ -129,9 +129,9 @@ export function AgentMonitor() {
|
||||
const onlineCount = hosts.filter((h) => isOnline(h.last_report || h.last_reported_at || h.updated_at)).length;
|
||||
|
||||
const stats = [
|
||||
{ label: '主机总数', value: hosts.length, icon: Server, color: 'text-blue-400' },
|
||||
{ label: '在线', value: onlineCount, icon: Wifi, color: 'text-emerald-400' },
|
||||
{ label: '离线', value: hosts.length - onlineCount, icon: WifiOff, color: 'text-red-400' },
|
||||
{ label: '主机总数', value: hosts.length, icon: Server, color: 'dg-text-blue' },
|
||||
{ label: '在线', value: onlineCount, icon: Wifi, color: 'dg-text-green' },
|
||||
{ label: '离线', value: hosts.length - onlineCount, icon: WifiOff, color: 'dg-text-red' },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -139,8 +139,8 @@ export function AgentMonitor() {
|
||||
{/* 页头 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold text-slate-100">Agent 监控</h2>
|
||||
<p className="text-sm text-slate-400 mt-0.5">实时监控 Agent 上报的主机指标</p>
|
||||
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">Agent 监控</h2>
|
||||
<p className="text-sm dg-text-secondary mt-0.5">实时监控 Agent 上报的主机指标</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
@@ -148,7 +148,7 @@ export function AgentMonitor() {
|
||||
size="sm"
|
||||
onClick={() => loadLatest()}
|
||||
disabled={refreshing}
|
||||
className="bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700 hover:text-slate-100"
|
||||
className="dg-input text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)] hover:text-[var(--dg-text-primary)]"
|
||||
>
|
||||
<RefreshCw className={`h-4 w-4 mr-1.5 ${refreshing ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
@@ -167,14 +167,14 @@ export function AgentMonitor() {
|
||||
{/* 统计卡片 */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{stats.map((s) => (
|
||||
<Card key={s.label} className="bg-slate-900 border-slate-800 py-4">
|
||||
<Card key={s.label} className="dg-card py-4">
|
||||
<CardContent className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-slate-800 flex items-center justify-center">
|
||||
<div className="w-10 h-10 rounded-lg bg-[var(--dg-bg-tertiary)] flex items-center justify-center">
|
||||
<s.icon className={`h-5 w-5 ${s.color}`} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-2xl font-bold text-slate-100">{s.value}</div>
|
||||
<div className="text-xs text-slate-400">{s.label}</div>
|
||||
<div className="text-2xl font-bold text-[var(--dg-text-primary)]">{s.value}</div>
|
||||
<div className="text-xs dg-text-secondary">{s.label}</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -183,17 +183,17 @@ export function AgentMonitor() {
|
||||
|
||||
{/* 主机列表 */}
|
||||
{loading ? (
|
||||
<Card className="bg-slate-900 border-slate-800">
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex items-center justify-center py-16">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-slate-500" />
|
||||
<span className="ml-2 text-slate-400">加载中...</span>
|
||||
<Loader2 className="h-6 w-6 animate-spin dg-text-muted" />
|
||||
<span className="ml-2 dg-text-secondary">加载中...</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : hosts.length === 0 ? (
|
||||
<Card className="bg-slate-900 border-slate-800">
|
||||
<Card className="dg-card">
|
||||
<CardContent className="flex flex-col items-center justify-center py-16">
|
||||
<Server className="h-10 w-10 text-slate-600 mb-3" />
|
||||
<p className="text-slate-400">暂无 Agent 上报数据</p>
|
||||
<Server className="h-10 w-10 dg-text-muted mb-3" />
|
||||
<p className="dg-text-secondary">暂无 Agent 上报数据</p>
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={() => setWizardOpen(true)}
|
||||
@@ -215,22 +215,22 @@ export function AgentMonitor() {
|
||||
return (
|
||||
<Card
|
||||
key={host.hostname}
|
||||
className="bg-slate-900 border-slate-800 hover:border-slate-700 transition-colors cursor-pointer py-4"
|
||||
className="dg-card hover:border-[var(--dg-border)] transition-colors cursor-pointer py-4"
|
||||
onClick={() => setSelectedHost(host)}
|
||||
>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<Server className="h-4 w-4 text-slate-500 shrink-0" />
|
||||
<span className="font-medium text-slate-100 truncate">{host.hostname}</span>
|
||||
<Server className="h-4 w-4 dg-text-muted shrink-0" />
|
||||
<span className="font-medium text-[var(--dg-text-primary)] truncate">{host.hostname}</span>
|
||||
</div>
|
||||
{online ? (
|
||||
<Badge className="bg-emerald-500/15 text-emerald-400 border border-emerald-500/30">
|
||||
<Badge className="bg-emerald-500/15 dg-text-green border border-emerald-500/30">
|
||||
<Wifi className="h-3 w-3 mr-1" />
|
||||
在线
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge className="bg-red-500/15 text-red-400 border border-red-500/30">
|
||||
<Badge className="bg-red-500/15 dg-text-red border border-red-500/30">
|
||||
<WifiOff className="h-3 w-3 mr-1" />
|
||||
离线
|
||||
</Badge>
|
||||
@@ -245,7 +245,7 @@ export function AgentMonitor() {
|
||||
</div>
|
||||
|
||||
{/* 端口 & 时间 */}
|
||||
<div className="flex items-center justify-between text-xs text-slate-400">
|
||||
<div className="flex items-center justify-between text-xs dg-text-secondary">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<Network className="h-3.5 w-3.5" />
|
||||
{ports.length} 个端口
|
||||
@@ -261,7 +261,7 @@ export function AgentMonitor() {
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex-1 h-8 bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700"
|
||||
className="flex-1 h-8 dg-input text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]"
|
||||
onClick={() => openCommandDialog(host.hostname)}
|
||||
>
|
||||
<Terminal className="h-3.5 w-3.5 mr-1" />
|
||||
@@ -270,7 +270,7 @@ export function AgentMonitor() {
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="flex-1 h-8 bg-slate-800 border-slate-700 text-slate-300 hover:bg-slate-700"
|
||||
className="flex-1 h-8 dg-input text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]"
|
||||
onClick={() => setSelectedHost(host)}
|
||||
>
|
||||
<Activity className="h-3.5 w-3.5 mr-1" />
|
||||
@@ -293,13 +293,13 @@ export function AgentMonitor() {
|
||||
|
||||
{/* 远程命令弹窗 */}
|
||||
<Dialog open={cmdOpen} onOpenChange={(v) => { if (!v) closeCommandDialog(); }}>
|
||||
<DialogContent className="max-w-2xl bg-slate-900 border-slate-800 text-slate-100">
|
||||
<DialogContent className="max-w-2xl dg-card text-[var(--dg-text-primary)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-slate-100 flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5 text-blue-400" />
|
||||
<DialogTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5 dg-text-blue" />
|
||||
远程命令
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-slate-400">
|
||||
<DialogDescription className="dg-text-secondary">
|
||||
在 {cmdHost} 上执行命令
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -307,34 +307,34 @@ export function AgentMonitor() {
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-slate-300 text-xs">目标主机</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">目标主机</Label>
|
||||
<Input
|
||||
value={cmdHost}
|
||||
onChange={(e) => setCmdHost(e.target.value)}
|
||||
readOnly
|
||||
className="h-9 bg-slate-800 border-slate-700 text-slate-200"
|
||||
className="h-9 dg-input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-slate-300 text-xs">命令</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">命令</Label>
|
||||
<Textarea
|
||||
value={cmdCommand}
|
||||
onChange={(e) => setCmdCommand(e.target.value)}
|
||||
placeholder="例如:free -m && df -h"
|
||||
rows={3}
|
||||
className="bg-slate-950 border-slate-700 text-slate-200 font-mono text-sm resize-none"
|
||||
className="bg-[var(--dg-bg-primary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] font-mono text-sm resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-40 space-y-1.5">
|
||||
<Label className="text-slate-300 text-xs">超时(秒)</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">超时(秒)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={cmdTimeout}
|
||||
onChange={(e) => setCmdTimeout(Number(e.target.value) || 30)}
|
||||
className="h-9 bg-slate-800 border-slate-700 text-slate-200"
|
||||
className="h-9 dg-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -342,20 +342,20 @@ export function AgentMonitor() {
|
||||
{cmdResult && (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="text-slate-300 text-xs">执行结果</Label>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs">执行结果</Label>
|
||||
<Badge
|
||||
className={
|
||||
cmdResult.status === 'success' || cmdResult.status === 'done' || cmdResult.status === 'completed'
|
||||
? 'bg-emerald-500/15 text-emerald-400 border border-emerald-500/30'
|
||||
? 'bg-emerald-500/15 dg-text-green border border-emerald-500/30'
|
||||
: cmdResult.status === 'timeout'
|
||||
? 'bg-amber-500/15 text-amber-400 border border-amber-500/30'
|
||||
: 'bg-red-500/15 text-red-400 border border-red-500/30'
|
||||
? 'bg-amber-500/15 dg-text-yellow border border-amber-500/30'
|
||||
: 'bg-red-500/15 dg-text-red border border-red-500/30'
|
||||
}
|
||||
>
|
||||
{cmdResult.status}
|
||||
</Badge>
|
||||
</div>
|
||||
<pre className="text-xs text-slate-300 font-mono whitespace-pre-wrap break-all bg-slate-950 border border-slate-800 rounded-lg p-3 max-h-48 overflow-auto">
|
||||
<pre className="text-xs text-[var(--dg-text-secondary)] font-mono whitespace-pre-wrap break-all bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg p-3 max-h-48 overflow-auto">
|
||||
{cmdResult.result || cmdResult.output || cmdResult.error || '无输出'}
|
||||
</pre>
|
||||
</div>
|
||||
@@ -363,7 +363,7 @@ export function AgentMonitor() {
|
||||
</div>
|
||||
|
||||
<DialogFooter className="gap-2">
|
||||
<Button variant="outline" onClick={closeCommandDialog} className="border-slate-700 text-slate-300">
|
||||
<Button variant="outline" onClick={closeCommandDialog} className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">
|
||||
关闭
|
||||
</Button>
|
||||
<Button
|
||||
@@ -391,12 +391,12 @@ export function AgentMonitor() {
|
||||
function MetricBar({ icon: Icon, label, value }: { icon: ComponentType<{ className?: string }>; label: string; value: number }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon className="h-3.5 w-3.5 text-slate-500 shrink-0" />
|
||||
<span className="text-xs text-slate-400 w-8 shrink-0">{label}</span>
|
||||
<Icon className="h-3.5 w-3.5 dg-text-muted shrink-0" />
|
||||
<span className="text-xs dg-text-secondary w-8 shrink-0">{label}</span>
|
||||
<div className="flex-1">
|
||||
<Progress value={value} className={`h-1.5 ${value >= 90 ? '[&>[data-slot=progress-indicator]]:bg-red-500' : value >= 75 ? '[&>[data-slot=progress-indicator]]:bg-amber-500' : '[&>[data-slot=progress-indicator]]:bg-emerald-500'}`} />
|
||||
</div>
|
||||
<span className={`text-xs font-medium w-10 text-right ${value >= 90 ? 'text-red-400' : value >= 75 ? 'text-amber-400' : 'text-slate-300'}`}>
|
||||
<span className={`text-xs font-medium w-10 text-right ${value >= 90 ? 'dg-text-red' : value >= 75 ? 'dg-text-yellow' : 'text-[var(--dg-text-secondary)]'}`}>
|
||||
{value.toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user