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
@@ -39,6 +39,7 @@ import { ActionModals } from './deployment-workload/ActionModals';
|
||||
import { createEmptyManifest } from './resource-create-templates';
|
||||
import { CREATE_DEPLOYMENT_SLUG, isCreateDeployment } from './deployment-constants';
|
||||
import { extractContainers } from './deployment-detail/helpers';
|
||||
import { PageShell } from '@/components/layout/PageShell';
|
||||
import type { ActionModal, DeploymentWorkloadProps } from './deployment-workload/types';
|
||||
|
||||
export type { DeploymentWorkloadProps, WorkloadMode } from './deployment-workload/types';
|
||||
@@ -283,161 +284,74 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
||||
}
|
||||
};
|
||||
|
||||
// ============ 渲染 ============
|
||||
// ============ 渲染 // Logan.Li ============
|
||||
if (loading) {
|
||||
return (
|
||||
<Card className="bg-slate-900 border-slate-800">
|
||||
<CardContent className="p-12 text-center">
|
||||
<Loader2 className="h-6 w-6 animate-spin mx-auto text-slate-500" />
|
||||
<p className="text-slate-500 mt-3">加载中...</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<PageShell variant="workspace" title={deploymentName} subtitle={`${clusterCode} / ${namespace}`} loading loadingLabel="加载 Deployment..." actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" />返回</Button>}>
|
||||
{null}
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
|
||||
if (!deployment && !isCreate) {
|
||||
return (
|
||||
<Card className="bg-slate-900 border-slate-800">
|
||||
<CardContent className="p-12 text-center">
|
||||
<XCircle className="h-8 w-8 mx-auto text-red-400" />
|
||||
<p className="text-slate-400 mt-3">未找到 Deployment: {deploymentName}</p>
|
||||
<Button variant="outline" className="mt-4" onClick={onBack}>返回</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<PageShell variant="default" title={deploymentName} subtitle={`${clusterCode} / ${namespace}`} actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" />返回</Button>}>
|
||||
<Card className="dg-card"><CardContent className="p-12 text-center"><XCircle className="h-8 w-8 mx-auto dg-text-red" /><p className="dg-text-secondary mt-3">未找到 Deployment: {deploymentName}</p></CardContent></Card>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
|
||||
if (isCreate) {
|
||||
const handleCreated = (updated?: any) => {
|
||||
const name = updated?.metadata?.name;
|
||||
if (name) {
|
||||
navigate(`/kubernetes/${clusterCode}/namespace/${namespace}/workload/view/Deployment/${name}`);
|
||||
}
|
||||
if (name) navigate(`/kubernetes/${clusterCode}/namespace/${namespace}/workload/view/Deployment/${name}`);
|
||||
};
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Button variant="ghost" size="sm" className="text-slate-400" onClick={onBack}>
|
||||
<ArrowLeft className="h-4 w-4 mr-1" />返回
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-slate-100">新建 Deployment</h1>
|
||||
<p className="text-xs text-slate-500 mt-0.5">{namespace} · {clusterCode}</p>
|
||||
</div>
|
||||
</div>
|
||||
<EditModeContainer
|
||||
clusterCode={clusterCode}
|
||||
namespace={namespace}
|
||||
deploymentName={CREATE_DEPLOYMENT_SLUG}
|
||||
manifest={manifest}
|
||||
isCreate
|
||||
onSaved={handleCreated}
|
||||
onCancel={onBack}
|
||||
/>
|
||||
</div>
|
||||
<PageShell variant="workspace" title="新建 Deployment" subtitle={`${clusterCode} / ${namespace}`} actions={<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" />返回</Button>}>
|
||||
<EditModeContainer clusterCode={clusterCode} namespace={namespace} deploymentName={CREATE_DEPLOYMENT_SLUG} manifest={manifest} isCreate onSaved={handleCreated} onCancel={onBack} />
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
|
||||
const ready = deployment.ready_replicas || deployment.readyReplicas || 0;
|
||||
const total = deployment.replicas || 0;
|
||||
const isHealthy = ready === total && total > 0;
|
||||
const _viewBase = `/kubernetes/${clusterCode}/namespace/${namespace}/workload`;
|
||||
|
||||
const modeTabs = (
|
||||
<div className="inline-flex rounded-md border border-[var(--dg-border)] overflow-hidden">
|
||||
<button className={`px-3 py-1.5 text-xs font-medium transition-colors ${mode === 'view' ? 'bg-[var(--dg-bg-active)] text-[var(--dg-text-primary)]' : 'bg-transparent dg-text-secondary hover:text-[var(--dg-text-primary)]'}`} onClick={() => onModeChange('view')}>查看</button>
|
||||
<button className={`px-3 py-1.5 text-xs font-medium transition-colors ${mode === 'edit' ? 'bg-blue-600 text-white' : 'bg-transparent dg-text-secondary hover:text-[var(--dg-text-primary)]'}`} onClick={() => onModeChange('edit')}>编辑</button>
|
||||
<button className={`px-3 py-1.5 text-xs font-medium transition-colors ${mode === 'yaml' ? 'bg-emerald-600 text-white' : 'bg-transparent dg-text-secondary hover:text-[var(--dg-text-primary)]'}`} onClick={() => onModeChange('yaml')}>YAML</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* ============ 顶部:标题 + 模式标识 + 操作菜单 ============ */}
|
||||
<div className="flex items-center justify-between gap-2 flex-wrap">
|
||||
<div className="flex items-center gap-3 min-w-0">
|
||||
<Button variant="ghost" size="sm" className="text-slate-400" onClick={onBack}>
|
||||
<ArrowLeft className="h-4 w-4 mr-1" />返回
|
||||
<PageShell
|
||||
variant="workspace"
|
||||
title={deploymentName}
|
||||
subtitle={`${clusterCode} / ${namespace} · Deployment · 镜像: ${deployment?.image || '-'}`}
|
||||
actions={(
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={onBack}><ArrowLeft className="h-4 w-4 mr-1" />返回</Button>
|
||||
{modeTabs}
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadData}><RefreshCw className="h-4 w-4" /></Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => { setNewImage(deployment?.image || ''); setModal({ kind: 'image' }); }}><ImageIcon className="h-3.5 w-3.5 mr-1" />调整镜像</Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={handleRestart}><RotateCw className="h-3.5 w-3.5 mr-1" />重启</Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" disabled={!!actionLoading} onClick={handleTogglePause}>
|
||||
{deployment?.paused ? <><Play className="h-3.5 w-3.5 mr-1 dg-text-green" />恢复</> : <><Pause className="h-3.5 w-3.5 mr-1 dg-text-orange" />暂停</>}
|
||||
</Button>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<h1 className="text-xl font-bold text-slate-100 truncate">{deploymentName}</h1>
|
||||
<Badge variant="outline" className="border-slate-700 text-slate-400">{namespace}</Badge>
|
||||
<Badge variant="outline" className="border-slate-700 text-slate-400">Deployment</Badge>
|
||||
<Badge
|
||||
className={isHealthy
|
||||
? 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30'
|
||||
: 'bg-amber-500/20 text-amber-400 border-amber-500/30'}
|
||||
>
|
||||
{isHealthy ? <CheckCircle className="h-3 w-3 mr-1" /> : <AlertTriangle className="h-3 w-3 mr-1" />}
|
||||
{ready}/{total}
|
||||
</Badge>
|
||||
{deployment?.paused && (
|
||||
<Badge className="bg-orange-500/20 text-orange-400 border-orange-500/30">
|
||||
<Pause className="h-3 w-3 mr-1" />已暂停更新
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-0.5 truncate">
|
||||
镜像: <code className="text-slate-400">{deployment.image || '-'}</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 模式切换 — 查看 / 编辑 / YAML */}
|
||||
<div className="inline-flex rounded-md border border-slate-700 overflow-hidden">
|
||||
<button
|
||||
className={`px-3 py-1.5 text-xs font-medium transition-colors ${mode === 'view' ? 'bg-slate-700 text-slate-100' : 'bg-transparent text-slate-400 hover:text-slate-200'}`}
|
||||
onClick={() => onModeChange('view')}
|
||||
>
|
||||
查看
|
||||
</button>
|
||||
<button
|
||||
className={`px-3 py-1.5 text-xs font-medium transition-colors ${mode === 'edit' ? 'bg-blue-600 text-white' : 'bg-transparent text-slate-400 hover:text-slate-200'}`}
|
||||
onClick={() => onModeChange('edit')}
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
className={`px-3 py-1.5 text-xs font-medium transition-colors ${mode === 'yaml' ? 'bg-emerald-600 text-white' : 'bg-transparent text-slate-400 hover:text-slate-200'}`}
|
||||
onClick={() => onModeChange('yaml')}
|
||||
>
|
||||
YAML
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" size="sm" className="border-slate-700 text-slate-400" onClick={loadData} title="刷新">
|
||||
<RefreshCw className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
{/* 操作按钮行 */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-slate-700 text-slate-300"
|
||||
onClick={() => {
|
||||
setNewImage(deployment?.image || '');
|
||||
setModal({ kind: 'image' });
|
||||
}}
|
||||
>
|
||||
<ImageIcon className="h-3.5 w-3.5 mr-1" />调整镜像
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="border-slate-700 text-slate-300" onClick={handleRestart}>
|
||||
<RotateCw className="h-3.5 w-3.5 mr-1" />重启
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline" size="sm"
|
||||
className="border-slate-700 text-slate-300"
|
||||
disabled={!!actionLoading}
|
||||
onClick={handleTogglePause}
|
||||
>
|
||||
{deployment?.paused
|
||||
? <><Play className="h-3.5 w-3.5 mr-1 text-green-400" />恢复</>
|
||||
: <><Pause className="h-3.5 w-3.5 mr-1 text-orange-400" />暂停</>
|
||||
}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="border-slate-700 text-slate-300" onClick={handleCICD}>
|
||||
<Rocket className="h-3.5 w-3.5 mr-1 text-purple-400" />CI/CD
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="border-slate-700 text-red-400 hover:bg-red-500/10" onClick={() => setModal({ kind: 'delete' })}>
|
||||
<Trash2 className="h-3.5 w-3.5 mr-1" />删除
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={handleCICD}><Rocket className="h-3.5 w-3.5 mr-1 dg-text-purple" />CI/CD</Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-danger" onClick={() => setModal({ kind: 'delete' })}><Trash2 className="h-3.5 w-3.5 mr-1" />删除</Button>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 flex-wrap mb-4">
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] dg-text-secondary">{namespace}</Badge>
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] dg-text-secondary">Deployment</Badge>
|
||||
<Badge className={isHealthy ? 'bg-emerald-500/20 dg-text-green border-emerald-500/30' : 'bg-amber-500/20 dg-text-yellow border-amber-500/30'}>
|
||||
{isHealthy ? <CheckCircle className="h-3 w-3 mr-1" /> : <AlertTriangle className="h-3 w-3 mr-1" />}{ready}/{total}
|
||||
</Badge>
|
||||
{deployment?.paused && <Badge className="bg-orange-500/20 dg-text-orange border-orange-500/30"><Pause className="h-3 w-3 mr-1" />已暂停更新</Badge>}
|
||||
</div>
|
||||
|
||||
{/* ============ 主体:view / edit / yaml ============ */}
|
||||
@@ -567,6 +481,6 @@ export function DeploymentWorkload({ clusterCode, namespace, deploymentName, mod
|
||||
podName={fileBrowserPod || ''}
|
||||
containers={(pods.find(( p: any) => p.name === fileBrowserPod)?.containers || []).map(( c: any) => c.name).filter(Boolean)}
|
||||
/>
|
||||
</div>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user