feat: 全站 PageShell 设计系统与专业 UI 统一

- 新增 PageShell/PageTabs/ContentPanel/PageLoading/PageEmpty 页面骨架
- index.css 设计令牌文档化,布局令牌与纯色主按钮(去除渐变粉彩)
- 全站路由页接入 PageShell,云账单三 Tab 为标杆
- 主题体系:跟随系统 + 主流/开发者分组,新增 Dracula/Tokyo Night,One Dark 暖灰差异化
- 登录页/侧栏/顶栏去渐变光球,企业克制风
- 修复 AIAlertAnalyzer JSX 与构建依赖导入问题
This commit is contained in:
Logan committed 2026-07-01 12:45:38 +08:00
1 parent 1acb0c49af
commit e363ded445
213 files changed
+7148 -6818

No files matched your search

@@ -57,24 +57,24 @@ export function ActionModals({
{modal?.kind === 'scale' && (
<ModalShell title="伸缩 (Scale)" onClose={onClose}>
<div className="space-y-3">
<p className="text-xs text-slate-500">当前副本: {deployment.replicas || 0}</p>
<p className="text-xs dg-text-muted">当前副本: {deployment.replicas || 0}</p>
<div className="flex items-center justify-center gap-3 py-3">
<Button variant="outline" size="icon" className="border-slate-700 text-slate-300"
<Button variant="outline" size="icon" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
disabled={actionLoading === 'scale' || scaleValue <= 0}
onClick={() => setScaleValue(v => Math.max(0, v - 1))}>
<Minus className="h-4 w-4" />
</Button>
<Input type="number" min={0} value={scaleValue}
onChange={e => setScaleValue(Math.max(0, Number(e.target.value)))}
className="w-24 text-center text-lg font-bold bg-slate-800 border-slate-700 text-slate-200" />
<Button variant="outline" size="icon" className="border-slate-700 text-slate-300"
className="w-24 text-center text-lg font-bold dg-input" />
<Button variant="outline" size="icon" className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]"
disabled={actionLoading === 'scale'}
onClick={() => setScaleValue(v => v + 1)}>
<Plus className="h-4 w-4" />
</Button>
</div>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" className="border-slate-700 text-slate-400" onClick={onClose}>取消</Button>
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={onClose}>取消</Button>
<Button className="bg-blue-600 hover:bg-blue-700" disabled={actionLoading === 'scale'} onClick={onScaleTo}>
{actionLoading === 'scale' ? <Loader2 className="h-4 w-4 animate-spin" /> : '确认'}
</Button>
@@ -87,16 +87,16 @@ export function ActionModals({
{modal?.kind === 'delete' && (
<ModalShell title="删除 Deployment" onClose={onClose}>
<div className="space-y-3">
<div className="flex items-center gap-2 text-red-400">
<div className="flex items-center gap-2 dg-text-red">
<AlertTriangle className="h-5 w-5" />
<span className="font-semibold">此操作不可撤销</span>
</div>
<p className="text-sm text-slate-400">
确认删除 Deployment <code className="text-slate-200 font-mono">{namespace}/{deploymentName}</code>?
<p className="text-sm dg-text-secondary">
确认删除 Deployment <code className="text-[var(--dg-text-primary)] font-mono">{namespace}/{deploymentName}</code>?
其管理的所有 Pod 将被一并回收。
</p>
<div className="flex justify-end gap-2 pt-2">
<Button variant="outline" className="border-slate-700 text-slate-400" onClick={onClose}>取消</Button>
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={onClose}>取消</Button>
<Button variant="destructive" disabled={actionLoading === 'delete'} onClick={onDelete}>
{actionLoading === 'delete' ? <Loader2 className="h-4 w-4 animate-spin mr-1" /> : <Trash2 className="h-4 w-4 mr-1" />}
确认删除
@@ -53,7 +53,7 @@ export function EditModeContainer({ clusterCode, namespace, deploymentName, mani
return (
<div className="space-y-4">
<div className="flex items-center gap-1 border-b border-slate-800 pb-px overflow-x-auto">
<div className="flex items-center gap-1 border-b border-[var(--dg-border)] pb-px overflow-x-auto">
{tabs.map((tab) => {
const active = activeTab === tab.key;
const Icon = tab.icon;
@@ -62,7 +62,7 @@ export function EditModeContainer({ clusterCode, namespace, deploymentName, mani
key={tab.key}
onClick={() => setActiveTab(tab.key)}
className={`flex items-center gap-1.5 px-3 py-2 text-xs font-medium border-b-2 transition-colors whitespace-nowrap ${
active ? 'border-blue-500 text-blue-400' : 'border-transparent text-slate-400 hover:text-slate-200'
active ? 'border-blue-500 dg-text-blue' : 'border-transparent dg-text-secondary hover:text-[var(--dg-text-primary)]'
}`}
>
<Icon className="h-3.5 w-3.5" />
@@ -17,12 +17,12 @@ export function ModalShell({ title, children, onClose, size = 'default' }: Modal
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" onClick={onClose}>
<div
className={`bg-slate-900 border border-slate-700 rounded-xl ${widthClass} max-w-[92vw] max-h-[85vh] flex flex-col shadow-2xl`}
className={`bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl ${widthClass} max-w-[92vw] max-h-[85vh] flex flex-col shadow-2xl`}
onClick={e => e.stopPropagation()}
>
<div className="flex items-center justify-between gap-3 px-6 pt-6 pb-4 shrink-0">
<h2 className="text-base font-bold text-slate-100 truncate min-w-0" title={title}>{title}</h2>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 text-slate-400" onClick={onClose}><X className="h-4 w-4" /></Button>
<h2 className="text-base font-bold text-[var(--dg-text-primary)] truncate min-w-0" title={title}>{title}</h2>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 dg-text-secondary" onClick={onClose}><X className="h-4 w-4" /></Button>
</div>
<div className="flex-1 min-h-0 overflow-y-auto px-6 pb-6">{children}</div>
</div>
@@ -63,35 +63,35 @@ export function PodLogsModal({ clusterCode, namespace, podName, containers, onCl
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
<div className="bg-slate-900 border border-slate-700 rounded-xl w-[90vw] max-w-4xl max-h-[80vh] flex flex-col" onClick={e => e.stopPropagation()}>
<div className="flex items-center justify-between p-3 border-b border-slate-700 gap-3 shrink-0">
<div className="bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl w-[90vw] max-w-4xl max-h-[80vh] flex flex-col" onClick={e => e.stopPropagation()}>
<div className="flex items-center justify-between p-3 border-b border-[var(--dg-border)] gap-3 shrink-0">
<div className="flex items-center gap-2 min-w-0 flex-1">
<FileText className="h-4 w-4 shrink-0 text-blue-400" />
<span className="text-sm font-semibold text-slate-200 truncate" title={podName}>{podName}</span>
{container && <Badge variant="outline" className="border-slate-700 text-slate-400 text-[10px] shrink-0">{container}</Badge>}
<FileText className="h-4 w-4 shrink-0 dg-text-blue" />
<span className="text-sm font-semibold text-[var(--dg-text-primary)] truncate" title={podName}>{podName}</span>
{container && <Badge variant="outline" className="border-[var(--dg-border)] dg-text-secondary text-[10px] shrink-0">{container}</Badge>}
</div>
<div className="flex items-center gap-2 shrink-0 flex-wrap justify-end">
{containers.length > 0 && (
<select value={container} onChange={e => setContainer(e.target.value)}
className="bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 px-2 py-1">
className="bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] px-2 py-1">
{containers.map(c => <option key={c} value={c}>{c}</option>)}
</select>
)}
<span className="text-[11px] text-slate-500">最近</span>
<span className="text-[11px] dg-text-muted">最近</span>
<Input type="number" value={tail} onChange={e => setTail(Number(e.target.value) || 200)}
className="w-16 h-7 bg-slate-800 border-slate-700 text-slate-200 text-xs" />
<span className="text-[11px] text-slate-500">行</span>
<Button size="sm" variant="outline" className="h-7 border-slate-700 text-slate-300 text-xs" onClick={loadLogs} disabled={loading}>
className="w-16 h-7 dg-input text-xs" />
<span className="text-[11px] dg-text-muted">行</span>
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-xs" onClick={loadLogs} disabled={loading}>
{loading ? <Loader2 className="h-3 w-3 animate-spin" /> : <RefreshCw className="h-3 w-3" />}
</Button>
<Button size="sm" variant="outline" className="h-7 border-slate-700 text-emerald-400 text-xs" onClick={startStream} disabled={streaming}>
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] dg-text-green text-xs" onClick={startStream} disabled={streaming}>
{streaming ? '流中...' : '实时流'}
</Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-slate-400" onClick={onClose}><X className="h-4 w-4" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-secondary" onClick={onClose}><X className="h-4 w-4" /></Button>
</div>
</div>
<div className="flex-1 overflow-auto p-3">
<pre className="bg-slate-950 rounded p-3 text-xs text-slate-300 font-mono whitespace-pre-wrap max-h-[60vh] overflow-auto">
<pre className="bg-[var(--dg-bg-primary)] rounded p-3 text-xs text-[var(--dg-text-secondary)] font-mono whitespace-pre-wrap max-h-[60vh] overflow-auto">
{logs || '无日志内容'}
</pre>
</div>
@@ -49,34 +49,34 @@ export function UpdateImageModal({
<ModalShell title="调整镜像版本" onClose={onClose} size="xl">
<div className="space-y-5">
{/* 当前镜像:只读展示完整地址与 Tag // Logan.Li */}
<div className="rounded-lg border border-slate-700/80 bg-slate-800/40 p-4 space-y-3">
<p className="text-xs font-medium text-slate-400">当前镜像</p>
<div className="rounded-lg border border-[var(--dg-border)]/80 bg-[var(--dg-bg-tertiary)]/40 p-4 space-y-3">
<p className="text-xs font-medium dg-text-secondary">当前镜像</p>
<div className="grid grid-cols-[1fr_180px] gap-3">
<div>
<Label className="text-slate-500 text-xs">镜像地址</Label>
<Label className="dg-text-muted text-xs">镜像地址</Label>
<p
className="mt-1.5 text-sm text-slate-200 font-mono break-all leading-relaxed bg-slate-900/60 rounded-md px-3 py-2.5 min-h-[42px]"
className="mt-1.5 text-sm text-[var(--dg-text-primary)] font-mono break-all leading-relaxed bg-[var(--dg-bg-secondary)]/60 rounded-md px-3 py-2.5 min-h-[42px]"
title={current.repository || '-'}
>
{current.repository || '-'}
</p>
</div>
<div>
<Label className="text-slate-500 text-xs">当前 Tag</Label>
<Label className="dg-text-muted text-xs">当前 Tag</Label>
<p
className="mt-1.5 text-sm text-cyan-300 font-mono break-all leading-relaxed bg-slate-900/60 rounded-md px-3 py-2.5 min-h-[42px]"
className="mt-1.5 text-sm text-cyan-300 font-mono break-all leading-relaxed bg-[var(--dg-bg-secondary)]/60 rounded-md px-3 py-2.5 min-h-[42px]"
title={current.tag || '-'}
>
{current.tag || '-'}
</p>
</div>
</div>
<p className="text-[11px] text-slate-500 font-mono break-all" title={currentImage}>
完整引用:<span className="text-slate-400">{currentImage || '-'}</span>
<p className="text-[11px] dg-text-muted font-mono break-all" title={currentImage}>
完整引用:<span className="dg-text-secondary">{currentImage || '-'}</span>
</p>
</div>
<div className="flex items-center justify-center text-slate-600">
<div className="flex items-center justify-center dg-text-muted">
<ArrowRight className="h-4 w-4" />
</div>
@@ -85,7 +85,7 @@ export function UpdateImageModal({
<p className="text-xs font-medium text-blue-300/90">新镜像</p>
<div className="grid grid-cols-[1fr_180px] gap-3">
<div>
<Label className="text-slate-400 text-xs">镜像地址</Label>
<Label className="dg-text-secondary text-xs">镜像地址</Label>
<Input
value={newRepo}
onChange={e => {
@@ -94,11 +94,11 @@ export function UpdateImageModal({
syncImage(v, newTag);
}}
placeholder="harbor.example.com/project/app"
className="mt-1.5 h-10 bg-slate-800 border-slate-700 text-slate-200 text-sm font-mono"
className="mt-1.5 h-10 dg-input text-sm font-mono"
/>
</div>
<div>
<Label className="text-slate-400 text-xs">新版本 Tag</Label>
<Label className="dg-text-secondary text-xs">新版本 Tag</Label>
<Input
value={newTag}
onChange={e => {
@@ -107,37 +107,37 @@ export function UpdateImageModal({
syncImage(newRepo, v);
}}
placeholder="v2.0.0"
className="mt-1.5 h-10 bg-slate-800 border-slate-700 text-cyan-200 text-sm font-mono"
className="mt-1.5 h-10 dg-input text-cyan-200 text-sm font-mono"
/>
</div>
</div>
<div className="flex flex-wrap items-center gap-2 text-xs">
<span className="text-slate-500">Tag 变更:</span>
<code className="px-2 py-0.5 rounded bg-slate-800 text-slate-400 font-mono">{current.tag}</code>
<ArrowRight className="h-3 w-3 text-slate-600" />
<span className="dg-text-muted">Tag 变更:</span>
<code className="px-2 py-0.5 rounded bg-[var(--dg-bg-tertiary)] dg-text-secondary font-mono">{current.tag}</code>
<ArrowRight className="h-3 w-3 dg-text-muted" />
<code
className={`px-2 py-0.5 rounded font-mono ${
tagChanged ? 'bg-cyan-500/15 text-cyan-300' : 'bg-slate-800 text-slate-400'
tagChanged ? 'bg-cyan-500/15 text-cyan-300' : 'bg-[var(--dg-bg-tertiary)] dg-text-secondary'
}`}
>
{newTag.trim() || '-'}
</code>
{!tagChanged && !repoChanged && (
<span className="text-slate-600">(未修改)</span>
<span className="dg-text-muted">(未修改)</span>
)}
{repoChanged && !tagChanged && (
<span className="text-amber-400/80">地址已变更</span>
<span className="dg-text-yellow/80">地址已变更</span>
)}
</div>
<p className="text-[11px] text-slate-500 font-mono break-all">
更新预览:<span className="text-slate-300">{previewImage || '-'}</span>
<p className="text-[11px] dg-text-muted font-mono break-all">
更新预览:<span className="text-[var(--dg-text-secondary)]">{previewImage || '-'}</span>
</p>
</div>
<div className="flex justify-end gap-2 pt-1">
<Button variant="outline" className="border-slate-700 text-slate-400" onClick={onClose}>
<Button variant="outline" className="border-[var(--dg-border)] dg-text-secondary" onClick={onClose}>
取消
</Button>
<Button