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

@@ -50,47 +50,47 @@ export function DnsHostSection({
return (
<>
<Card className="bg-slate-900 border-slate-800">
<Card className="dg-card">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-slate-200 flex items-center gap-2">
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
<Globe className="h-4 w-4 text-indigo-400" />DNS 配置 (dnsConfig)
</CardTitle>
</CardHeader>
<CardContent className="pt-0 space-y-3">
<p className="text-[11px] text-slate-500">
提示:dnsConfig 通常与 <code className="text-slate-400">dnsPolicy: None</code> 搭配使用。每行一个值。
<p className="text-[11px] dg-text-muted">
提示:dnsConfig 通常与 <code className="dg-text-secondary">dnsPolicy: None</code> 搭配使用。每行一个值。
</p>
<div className="grid grid-cols-2 gap-3">
<div>
<Label className="text-slate-500 text-xs">nameservers(每行一个 IP)</Label>
<Label className="dg-text-muted text-xs">nameservers(每行一个 IP)</Label>
<textarea
value={dnsNameservers}
onChange={e => onDnsNameserversChange(e.target.value)}
placeholder={'8.8.8.8\n114.114.114.114'}
rows={3}
className="w-full mt-1 bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 font-mono p-2 resize-y"
className="w-full mt-1 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] font-mono p-2 resize-y"
/>
</div>
<div>
<Label className="text-slate-500 text-xs">searches(搜索域,每行一个)</Label>
<Label className="dg-text-muted text-xs">searches(搜索域,每行一个)</Label>
<textarea
value={dnsSearches}
onChange={e => onDnsSearchesChange(e.target.value)}
placeholder={'svc.cluster.local\ncluster.local'}
rows={3}
className="w-full mt-1 bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 font-mono p-2 resize-y"
className="w-full mt-1 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] font-mono p-2 resize-y"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between mb-1">
<Label className="text-slate-400 text-xs">options(DNS 选项)</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs text-slate-400" onClick={addDnsOption}>
<Label className="dg-text-secondary text-xs">options(DNS 选项)</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs dg-text-secondary" onClick={addDnsOption}>
<Plus className="h-3 w-3 mr-1" />添加
</Button>
</div>
{dnsOptions.length === 0 ? (
<p className="text-xs text-slate-600 italic">未配置</p>
<p className="text-xs dg-text-muted italic">未配置</p>
) : (
dnsOptions.map((o, i) => (
<div key={i} className="flex items-center gap-2 mb-1.5">
@@ -98,16 +98,16 @@ export function DnsHostSection({
value={o.name}
onChange={e => updateDnsOption(i, { name: e.target.value })}
placeholder="ndots"
className="flex-1 h-7 bg-slate-800 border-slate-700 text-slate-300 text-xs font-mono"
className="flex-1 h-7 dg-input text-[var(--dg-text-secondary)] text-xs font-mono"
/>
<span className="text-slate-600">:</span>
<span className="dg-text-muted">:</span>
<Input
value={o.value}
onChange={e => updateDnsOption(i, { value: e.target.value })}
placeholder="5(可留空)"
className="flex-1 h-7 bg-slate-800 border-slate-700 text-slate-300 text-xs font-mono"
className="flex-1 h-7 dg-input text-[var(--dg-text-secondary)] text-xs font-mono"
/>
<Button size="icon" variant="ghost" className="h-6 w-6 text-red-400" onClick={() => removeDnsOption(i)}>
<Button size="icon" variant="ghost" className="h-6 w-6 dg-text-red" onClick={() => removeDnsOption(i)}>
<Trash2 className="h-3 w-3" />
</Button>
</div>
@@ -117,21 +117,21 @@ export function DnsHostSection({
</CardContent>
</Card>
<Card className="bg-slate-900 border-slate-800">
<Card className="dg-card">
<CardHeader className="pb-3">
<CardTitle className="text-sm text-slate-200 flex items-center gap-2">
<MapPin className="h-4 w-4 text-amber-400" />主机别名 (hostAliases)
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
<MapPin className="h-4 w-4 dg-text-yellow" />主机别名 (hostAliases)
</CardTitle>
</CardHeader>
<CardContent className="pt-0 space-y-2">
<div className="flex items-center justify-between mb-1">
<Label className="text-slate-400 text-xs">IP → 主机名映射(注入 Pod 的 /etc/hosts)</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs text-slate-400" onClick={addHostAlias}>
<Label className="dg-text-secondary text-xs">IP → 主机名映射(注入 Pod 的 /etc/hosts)</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs dg-text-secondary" onClick={addHostAlias}>
<Plus className="h-3 w-3 mr-1" />添加
</Button>
</div>
{hostAliases.length === 0 ? (
<p className="text-xs text-slate-600 italic">未配置</p>
<p className="text-xs dg-text-muted italic">未配置</p>
) : (
hostAliases.map((a, i) => (
<div key={i} className="flex items-center gap-2">
@@ -139,16 +139,16 @@ export function DnsHostSection({
value={a.ip}
onChange={e => updateHostAlias(i, { ip: e.target.value })}
placeholder="10.10.10.72"
className="w-40 h-7 bg-slate-800 border-slate-700 text-slate-300 text-xs font-mono"
className="w-40 h-7 dg-input text-[var(--dg-text-secondary)] text-xs font-mono"
/>
<span className="text-slate-600">→</span>
<span className="dg-text-muted">→</span>
<Input
value={a.hostnames}
onChange={e => updateHostAlias(i, { hostnames: e.target.value })}
placeholder="gitlab.local, registry.local"
className="flex-1 h-7 bg-slate-800 border-slate-700 text-slate-300 text-xs font-mono"
className="flex-1 h-7 dg-input text-[var(--dg-text-secondary)] text-xs font-mono"
/>
<Button size="icon" variant="ghost" className="h-6 w-6 text-red-400" onClick={() => removeHostAlias(i)}>
<Button size="icon" variant="ghost" className="h-6 w-6 dg-text-red" onClick={() => removeHostAlias(i)}>
<Trash2 className="h-3 w-3" />
</Button>
</div>
@@ -51,21 +51,21 @@ export function SchedulingSection({
nodeLabels,
}: SchedulingSectionProps) {
return (
<Card className="bg-slate-900 border-slate-800">
<Card className="dg-card">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-sm text-slate-200 flex items-center gap-2">
<Server className="h-4 w-4 text-emerald-400" />调度 (Scheduling)
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
<Server className="h-4 w-4 dg-text-green" />调度 (Scheduling)
</CardTitle>
<div className="flex items-center gap-1 bg-slate-800 rounded-lg p-0.5 border border-slate-700">
<div className="flex items-center gap-1 bg-[var(--dg-bg-tertiary)] rounded-lg p-0.5 border border-[var(--dg-border)]">
{SCHEDULE_MODES.map(m => (
<button
key={m.key}
onClick={() => onScheduleModeChange(m.key)}
className={`px-2.5 py-1 rounded text-[10px] font-medium transition-all ${
scheduleMode === m.key
? 'bg-emerald-500/20 text-emerald-300 shadow-sm'
: 'text-slate-400 hover:text-slate-300'
? 'bg-emerald-500/20 dg-text-green shadow-sm'
: 'dg-text-secondary hover:text-[var(--dg-text-secondary)]'
}`}
>
{m.label}
@@ -73,7 +73,7 @@ export function SchedulingSection({
))}
</div>
</div>
<p className="text-xs text-slate-500 mt-1">
<p className="text-xs dg-text-muted mt-1">
{scheduleMode === 'nodeName' && 'Pod 将直接调度到指定节点'}
{scheduleMode === 'nodeSelector' && 'Pod 将调度到匹配标签的节点'}
{scheduleMode === 'nodeAffinity' && '通过亲和性规则控制 Pod 调度到指定节点'}
@@ -82,16 +82,16 @@ export function SchedulingSection({
<CardContent className="pt-0">
{scheduleMode === 'nodeName' && (
<div className="space-y-2">
<Label className="text-slate-400 text-xs">请选择节点</Label>
<Label className="dg-text-secondary text-xs">请选择节点</Label>
{nodesLoading ? (
<div className="flex items-center gap-1 text-slate-500 text-xs">
<div className="flex items-center gap-1 dg-text-muted text-xs">
<Loader2 className="h-3 w-3 animate-spin" />加载节点列表...
</div>
) : (
<select
value={nodeName}
onChange={e => onNodeNameChange(e.target.value)}
className="h-8 w-full bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 px-2"
className="h-8 w-full bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] px-2"
>
<option value="">(不指定)</option>
{availableNodes.map(n => (
@@ -102,8 +102,8 @@ export function SchedulingSection({
</select>
)}
{nodeName && (
<div className="text-[10px] text-slate-500">
已选择: <code className="bg-slate-800 px-1 py-0.5 rounded text-emerald-400">{nodeName}</code>
<div className="text-[10px] dg-text-muted">
已选择: <code className="bg-[var(--dg-bg-tertiary)] px-1 py-0.5 rounded dg-text-green">{nodeName}</code>
</div>
)}
</div>
@@ -112,20 +112,20 @@ export function SchedulingSection({
{scheduleMode === 'nodeSelector' && (
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label className="text-slate-400 text-xs">请选择标签</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs text-slate-400" onClick={onAddLabelSelector}>
<Label className="dg-text-secondary text-xs">请选择标签</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs dg-text-secondary" onClick={onAddLabelSelector}>
<Plus className="h-3 w-3 mr-1" />添加
</Button>
</div>
{labelSelectorKv.length === 0 ? (
<p className="text-xs text-slate-600 italic">未配置标签选择器</p>
<p className="text-xs dg-text-muted italic">未配置标签选择器</p>
) : (
labelSelectorKv.map((n, i) => (
<div key={i} className="flex items-center gap-2">
<select
value={n.key}
onChange={e => onUpdateLabelSelector(i, { key: e.target.value, value: '' })}
className="flex-1 h-7 bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 px-1.5"
className="flex-1 h-7 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] px-1.5"
>
<option value="">选择标签</option>
{Object.entries(nodeLabels).map(([k, vals]) => (
@@ -135,7 +135,7 @@ export function SchedulingSection({
<select
value={n.value}
onChange={e => onUpdateLabelSelector(i, { value: e.target.value })}
className="flex-1 h-7 bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 px-1.5"
className="flex-1 h-7 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] px-1.5"
disabled={!n.key}
>
<option value="">选择值</option>
@@ -146,7 +146,7 @@ export function SchedulingSection({
<Button
size="icon"
variant="ghost"
className="h-6 w-6 text-red-400"
className="h-6 w-6 dg-text-red"
onClick={() => onRemoveLabelSelector(i)}
>
<Trash2 className="h-3 w-3" />
@@ -160,22 +160,22 @@ export function SchedulingSection({
{scheduleMode === 'nodeAffinity' && (
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label className="text-slate-400 text-xs">
请选择标签 <span className="text-slate-600">(生成 requiredDuringScheduling 规则)</span>
<Label className="dg-text-secondary text-xs">
请选择标签 <span className="dg-text-muted">(生成 requiredDuringScheduling 规则)</span>
</Label>
<Button size="sm" variant="ghost" className="h-6 text-xs text-slate-400" onClick={onAddLabelSelector}>
<Button size="sm" variant="ghost" className="h-6 text-xs dg-text-secondary" onClick={onAddLabelSelector}>
<Plus className="h-3 w-3 mr-1" />添加
</Button>
</div>
{labelSelectorKv.length === 0 ? (
<p className="text-xs text-slate-600 italic">未配置亲和性规则</p>
<p className="text-xs dg-text-muted italic">未配置亲和性规则</p>
) : (
labelSelectorKv.map((n, i) => (
<div key={i} className="flex items-center gap-2">
<select
value={n.key}
onChange={e => onUpdateLabelSelector(i, { key: e.target.value, value: '' })}
className="flex-1 h-7 bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 px-1.5"
className="flex-1 h-7 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] px-1.5"
>
<option value="">选择标签</option>
{Object.entries(nodeLabels).map(([k, vals]) => (
@@ -185,7 +185,7 @@ export function SchedulingSection({
<select
value={n.value}
onChange={e => onUpdateLabelSelector(i, { value: e.target.value })}
className="flex-1 h-7 bg-slate-800 border border-slate-700 rounded text-xs text-slate-300 px-1.5"
className="flex-1 h-7 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded text-xs text-[var(--dg-text-secondary)] px-1.5"
disabled={!n.key}
>
<option value="">选择值</option>
@@ -196,7 +196,7 @@ export function SchedulingSection({
<Button
size="icon"
variant="ghost"
className="h-6 w-6 text-red-400"
className="h-6 w-6 dg-text-red"
onClick={() => onRemoveLabelSelector(i)}
>
<Trash2 className="h-3 w-3" />