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

@@ -158,11 +158,11 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
const resCfg = getYamlResourceConfig(resource);
const resourceMeta: Record<string, { title: string; icon: ComponentType<{ className?: string }>; color: string }> = {
configmaps: { title: 'ConfigMap', icon: FileText, color: 'text-blue-400' },
secrets: { title: 'Secret', icon: Shield, color: 'text-amber-400' },
services: { title: 'Service', icon: Network, color: 'text-cyan-400' },
ingresses: { title: 'Ingress', icon: ChevronRight, color: 'text-purple-400' },
pvcs: { title: 'PersistentVolumeClaim', icon: Database, color: 'text-emerald-400' },
configmaps: { title: 'ConfigMap', icon: FileText, color: 'dg-text-blue' },
secrets: { title: 'Secret', icon: Shield, color: 'dg-text-yellow' },
services: { title: 'Service', icon: Network, color: 'dg-text-cyan' },
ingresses: { title: 'Ingress', icon: ChevronRight, color: 'dg-text-purple' },
pvcs: { title: 'PersistentVolumeClaim', icon: Database, color: 'dg-text-green' },
};
const meta = resourceMeta[resource] || resourceMeta.configmaps;
const MetaIcon = meta.icon;
@@ -338,7 +338,7 @@ function ConfigMapEditor({ manifest, onSave, saving, isCreate }: { manifest: any
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
/>
<Button variant="ghost" size="icon" className="h-8 w-8 text-red-400 hover:text-red-300 shrink-0" onClick={() => removeRow(i)}>
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-red hover:dg-text-red shrink-0" onClick={() => removeRow(i)}>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
@@ -439,7 +439,7 @@ function SecretEditor({ manifest, onSave, saving, isCreate }: { manifest: any; o
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
/>
<Button variant="ghost" size="icon" className="h-8 w-8 text-red-400 hover:text-red-300 shrink-0" onClick={() => removeRow(i)}>
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-red hover:dg-text-red shrink-0" onClick={() => removeRow(i)}>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
@@ -564,7 +564,7 @@ function ServiceEditor({ manifest, onSave, saving, isCreate }: { manifest: any;
{svcType === 'NodePort' ? (
<Input value={p.nodePort} onChange={e => updatePort(i, 'nodePort', e.target.value)} placeholder="NodePort" type="number" className="font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
) : <div />}
<Button variant="ghost" size="icon" className="h-7 w-7 text-red-400 hover:text-red-300" onClick={() => removePort(i)}><Trash2 className="h-3 w-3" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-red hover:dg-text-red" onClick={() => removePort(i)}><Trash2 className="h-3 w-3" /></Button>
</div>
</div>
))}
@@ -584,7 +584,7 @@ function ServiceEditor({ manifest, onSave, saving, isCreate }: { manifest: any;
<Input value={s.key} onChange={e => updateSelector(i, 'key', e.target.value)} placeholder="键 (如 app)" className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
<span className="text-[var(--dg-text-muted)] text-xs shrink-0">=</span>
<Input value={s.value} onChange={e => updateSelector(i, 'value', e.target.value)} placeholder="值 (如 nginx)" className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 text-red-400 hover:text-red-300" onClick={() => removeSelector(i)}><Trash2 className="h-3 w-3" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 dg-text-red hover:dg-text-red" onClick={() => removeSelector(i)}><Trash2 className="h-3 w-3" /></Button>
</div>
))}
{selector.length === 0 && <div className="text-center py-4 text-[var(--dg-text-muted)] text-xs">暂无选择器</div>}
@@ -769,7 +769,7 @@ function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate }: { ma
className="flex-1 min-w-0 font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
title={rule.host}
/>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 text-red-400 hover:text-red-300" onClick={() => removeRule(ri)}><Trash2 className="h-3 w-3" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 shrink-0 dg-text-red hover:dg-text-red" onClick={() => removeRule(ri)}><Trash2 className="h-3 w-3" /></Button>
</div>
{rule.paths.map((p, pi) => (
<div key={pi} className="overflow-x-auto pl-4">
@@ -781,7 +781,7 @@ function IngressEditor({ manifest, clusterCode, onSave, saving, isCreate }: { ma
</Select>
<Input value={p.serviceName} onChange={e => updatePath(ri, pi, 'serviceName', e.target.value)} placeholder="Service" className="font-mono text-xs h-7 min-w-0 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
<Input value={p.servicePort} onChange={e => updatePath(ri, pi, 'servicePort', e.target.value)} placeholder="端口" type="number" className="font-mono text-xs h-7 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" />
<Button variant="ghost" size="icon" className="h-7 w-7 text-red-400 hover:text-red-300" onClick={() => removePath(ri, pi)}><Trash2 className="h-3 w-3" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-red hover:dg-text-red" onClick={() => removePath(ri, pi)}><Trash2 className="h-3 w-3" /></Button>
</div>
</div>
))}