优化 K8s 页面布局与 local 模式部署收尾

P0-P1:
- Dockerfile.embed 启用 CGO 支持 SQLite+redka
- K8s scale>1 时警告 local 模式数据不共享

K8s UI:
- 移除侧边栏资源用量/容量分析及资源页横向 Tab 栏
- 修复事件路由(/workloads/events)与 Deployment 事件过滤
- 网络/配置/存储统一 YamlViewer 编辑,加大编辑弹窗
This commit is contained in:
Logan.Li committed 2026-06-26 13:36:27 +08:00
1 parent 859db427a4
commit 339b24a579
12 files changed
+87 -90

No files matched your search

@@ -16,7 +16,6 @@ import {
Download, FileCode, Pencil, Terminal, HardDrive,
} from 'lucide-react';
import { YamlViewer } from './YamlViewer';
import { ResourceVisualEditor } from './ResourceVisualEditor';
import { PodExecTerminal } from './PodExecTerminal';
import { PodFileBrowser } from './PodFileBrowser';
import { kubernetesApi } from '@/api';
@@ -106,10 +105,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
<TableCell className="text-sm text-slate-400">{s.cluster_ip || '-'}</TableCell>
<TableCell className="text-sm text-slate-400">{s.ports || '-'}</TableCell>
<TableCell>
<div className="flex items-center gap-0.5">
<Button variant="ghost" size="icon" className="h-7 w-7 text-slate-400 hover:text-blue-400" title="查看YAML" onClick={() => opts.onYaml?.(s.name)}><FileCode className="h-3.5 w-3.5" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-emerald-400 hover:text-emerald-300" title="编辑" onClick={() => opts.onEdit?.(s.name)}><Pencil className="h-3.5 w-3.5" /></Button>
</div>
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-blue-400 hover:text-blue-300" title="YAML 编辑"
onClick={() => opts.onEdit?.(s.name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
</TableCell>
</TableRow>
),
@@ -126,10 +123,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
<TableCell className="text-sm text-slate-400 max-w-[250px] truncate">{hosts.length > 0 ? hosts.join(', ') : '-'}</TableCell>
<TableCell><Badge className={i.tls ? 'bg-emerald-500/20 text-emerald-400' : 'bg-slate-500/20 text-slate-400'}>{i.tls ? 'HTTPS' : 'HTTP'}</Badge></TableCell>
<TableCell>
<div className="flex items-center gap-0.5">
<Button variant="ghost" size="icon" className="h-7 w-7 text-slate-400 hover:text-blue-400" title="查看YAML" onClick={() => opts.onYaml?.(i.name)}><FileCode className="h-3.5 w-3.5" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-emerald-400 hover:text-emerald-300" title="编辑" onClick={() => opts.onEdit?.(i.name)}><Pencil className="h-3.5 w-3.5" /></Button>
</div>
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-blue-400 hover:text-blue-300" title="YAML 编辑"
onClick={() => opts.onEdit?.(i.name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
</TableCell>
</TableRow>
);
@@ -144,12 +139,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
<TableCell><Badge variant="outline" className="border-slate-700 text-slate-400">{c.namespace}</Badge></TableCell>
<TableCell className="text-sm text-slate-400">{c.key_count || '-'}</TableCell>
<TableCell>
<div className="flex items-center gap-0.5">
<Button variant="ghost" size="icon" className="h-7 w-7 text-slate-400 hover:text-blue-400" title="查看YAML"
onClick={() => opts.onYaml?.(c.name)}><FileCode className="h-3.5 w-3.5" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-emerald-400 hover:text-emerald-300" title="编辑"
onClick={() => opts.onEdit?.(c.name)}><Pencil className="h-3.5 w-3.5" /></Button>
</div>
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-blue-400 hover:text-blue-300" title="YAML 编辑"
onClick={() => opts.onEdit?.(c.name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
</TableCell>
</TableRow>
),
@@ -163,12 +154,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
<TableCell><Badge variant="outline" className="border-slate-700 text-slate-400">{s.namespace}</Badge></TableCell>
<TableCell className="text-sm text-slate-400">{s.type || '-'}</TableCell>
<TableCell>
<div className="flex items-center gap-0.5">
<Button variant="ghost" size="icon" className="h-7 w-7 text-slate-400 hover:text-blue-400" title="查看YAML"
onClick={() => opts.onYaml?.(s.name)}><FileCode className="h-3.5 w-3.5" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-emerald-400 hover:text-emerald-300" title="编辑"
onClick={() => opts.onEdit?.(s.name)}><Pencil className="h-3.5 w-3.5" /></Button>
</div>
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-blue-400 hover:text-blue-300" title="YAML 编辑"
onClick={() => opts.onEdit?.(s.name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
</TableCell>
</TableRow>
),
@@ -211,10 +198,8 @@ const RESOURCE_CONFIG: Record<ResourceType, {
<TableCell className="text-sm text-slate-400">{p.size || '-'}</TableCell>
<TableCell className="text-sm text-slate-400">{p.storage_class || '-'}</TableCell>
<TableCell>
<div className="flex items-center gap-0.5">
<Button variant="ghost" size="icon" className="h-7 w-7 text-slate-400 hover:text-blue-400" title="查看YAML" onClick={() => opts.onYaml?.(p.name)}><FileCode className="h-3.5 w-3.5" /></Button>
<Button variant="ghost" size="icon" className="h-7 w-7 text-emerald-400 hover:text-emerald-300" title="编辑" onClick={() => opts.onEdit?.(p.name)}><Pencil className="h-3.5 w-3.5" /></Button>
</div>
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-blue-400 hover:text-blue-300" title="YAML 编辑"
onClick={() => opts.onEdit?.(p.name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
</TableCell>
</TableRow>
),
@@ -353,7 +338,7 @@ export default function NamespaceResourcePage() {
const [loading, setLoading] = useState(false);
const [search, setSearch] = useState('');
const [yamlItem, setYamlItem] = useState<{resource: string; name: string; editMode?: boolean} | null>(null);
const [visualEditItem, setVisualEditItem] = useState<{resource: string; name: string} | null>(null);
const [terminalPod, setTerminalPod] = useState<any>(null);
const [fileBrowserPod, setFileBrowserPod] = useState<any>(null);
@@ -420,34 +405,11 @@ export default function NamespaceResourcePage() {
};
const handleYaml = (name: string) => setYamlItem({ resource: type, name });
const handleEdit = (name: string) => {
// 使用可视化编辑器的资源类型
if (['configmaps', 'secrets', 'services', 'ingresses', 'pvcs'].includes(type)) {
setVisualEditItem({ resource: type, name });
} else {
setYamlItem({ resource: type, name, editMode: true });
}
};
// 网络/配置/存储统一走 YamlViewer 编辑(清理只读字段 + CodeMirror)// Logan.Li
const handleEdit = (name: string) => setYamlItem({ resource: type, name, editMode: true });
const handleExportCSV = () => kubernetesApi.exportAsCSV(`${type}-${clusterCode}-${namespace}.csv`, filtered);
const handleExportJSON = () => kubernetesApi.exportAsJSON(`${type}-${clusterCode}-${namespace}.json`, filtered);
const sidebarResources = useMemo(() => [
{ type: 'deployments' as ResourceType, label: 'Deployments', icon: Boxes },
{ type: 'statefulsets' as ResourceType, label: 'StatefulSets', icon: Boxes },
{ type: 'daemonsets' as ResourceType, label: 'DaemonSets', icon: Boxes },
{ type: 'pods' as ResourceType, label: 'Pods', icon: Container },
{ type: 'cronjobs' as ResourceType, label: 'CronJobs', icon: Clock },
{ type: 'services' as ResourceType, label: 'Services', icon: Network },
{ type: 'ingresses' as ResourceType, label: 'Ingresses', icon: Network },
{ type: 'configmaps' as ResourceType, label: 'ConfigMaps', icon: FileText },
{ type: 'secrets' as ResourceType, label: 'Secrets', icon: Shield },
{ type: 'hpas' as ResourceType, label: 'HPAs', icon: Activity },
{ type: 'pvcs' as ResourceType, label: '存储卷', icon: Boxes },
{ type: 'serviceaccounts' as ResourceType, label: 'ServiceAccounts', icon: Shield },
{ type: 'roles' as ResourceType, label: 'Roles', icon: Shield },
{ type: 'rolebindings' as ResourceType, label: 'RoleBindings', icon: Shield },
], []);
if (!config) return <div className="text-slate-400 p-8 text-center">未知资源类型: {type}</div>;
return (
@@ -464,16 +426,7 @@ export default function NamespaceResourcePage() {
autoEdit={yamlItem.editMode === true}
/>
)}
{visualEditItem && (
<ResourceVisualEditor
clusterCode={clusterCode || ''}
namespace={namespace || ''}
resource={visualEditItem.resource}
name={visualEditItem.name}
onClose={() => setVisualEditItem(null)}
onSaved={loadData}
/>
)}
<div className="flex items-center gap-2 text-sm text-slate-500">
<Link to="/kubernetes" className="hover:text-slate-300 transition-colors">集群</Link>
{clusterCode && <><ChevronRight className="h-3 w-3" /><Link to={`/kubernetes/${clusterCode}`} className="hover:text-slate-300 transition-colors">{clusterCode}</Link></>}
@@ -505,18 +458,12 @@ export default function NamespaceResourcePage() {
</Button>
</div>
</div>
<div className="flex items-center gap-1 bg-slate-900/50 border border-slate-800 rounded-lg p-1 overflow-x-auto">
{sidebarResources.map((r) => (
<Link key={r.type} to={`/kubernetes/${clusterCode}/namespace/${namespace}/${r.type}`}
className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md transition-colors whitespace-nowrap ${r.type === type ? 'bg-blue-600/20 text-blue-400' : 'text-slate-400 hover:text-slate-200 hover:bg-slate-800/50'}`}>
<r.icon className="h-3 w-3" />{r.label}
</Link>
))}
</div>
<div className="relative w-56">
<div className="flex items-center gap-3 flex-wrap">
<div className="relative w-56 flex-1 min-w-[200px] max-w-xs">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-500" />
<Input placeholder="搜索..." value={search} onChange={(e) => setSearch(e.target.value)} className="pl-9 h-9 bg-slate-800/50 border-slate-700 text-slate-200" />
</div>
</div>
<Card className="bg-slate-900 border-slate-800">
<CardContent className="p-0">
<Table>