feat(k8s): 事件页简化、可视化编辑 P0、Deployment 五 Tab 修复
- 事件中心:去彩虹图表,改专业排名列表 + 主题变量 - 网络/配置/存储:ResourceVisualEditor 双向 YAML 同步、导入 YAML、新建 - vite manualChunks 减小 embed 主包体积 - run.sh:多副本强制 MODE=release + MySQL/Redis - Deployment 编辑:共享 manifest、统一 saveDeploymentManifest - 修复存储挂载 initContainers 混入 bug、补 Tolerations UI - 服务 Tab:applyYAML 创建 + ResourceVisualEditor 编辑 - 基本信息:Pod 模板标签 + Selector 同步
This commit is contained in:
1 parent
339b24a579
commit
d0cf71b05a
17 files changed
+814
-497
No files matched your search
@@ -13,9 +13,11 @@ import {
|
||||
import {
|
||||
RefreshCw, Search, Loader2, Boxes, Container,
|
||||
Network, FileText, Shield, Activity, Clock, ChevronRight,
|
||||
Download, FileCode, Pencil, Terminal, HardDrive,
|
||||
Download, FileCode, Pencil, Terminal, HardDrive, Plus, Upload,
|
||||
} from 'lucide-react';
|
||||
import { YamlViewer } from './YamlViewer';
|
||||
import { ResourceVisualEditor } from './ResourceVisualEditor';
|
||||
import { YamlImportDialog } from './YamlImportDialog';
|
||||
import { PodExecTerminal } from './PodExecTerminal';
|
||||
import { PodFileBrowser } from './PodFileBrowser';
|
||||
import { kubernetesApi } from '@/api';
|
||||
@@ -27,6 +29,18 @@ type ResourceType =
|
||||
| 'pvcs' | 'statefulsets' | 'daemonsets'
|
||||
| 'serviceaccounts' | 'roles' | 'rolebindings';
|
||||
|
||||
// 支持可视化编辑/新建的资源类型 // Logan.Li
|
||||
const VISUAL_RESOURCE_TYPES: ResourceType[] = ['services', 'ingresses', 'configmaps', 'secrets', 'pvcs'];
|
||||
|
||||
const VisualActions = ({ name, opts }: { name: string; opts: { onYaml?: (n: string) => void; onEdit?: (n: string) => void } }) => (
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-emerald-400 hover:text-emerald-300" title="可视化编辑"
|
||||
onClick={() => opts.onEdit?.(name)}><Pencil className="h-3.5 w-3.5 mr-1" />编辑</Button>
|
||||
<Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-slate-400 hover:text-slate-300" title="YAML"
|
||||
onClick={() => opts.onYaml?.(name)}><FileCode className="h-3.5 w-3.5 mr-1" />YAML</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
@@ -104,10 +118,7 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell><Badge className="bg-blue-500/20 text-blue-400 border-blue-500/30">{s.type || '-'}</Badge></TableCell>
|
||||
<TableCell className="text-sm text-slate-400">{s.cluster_ip || '-'}</TableCell>
|
||||
<TableCell className="text-sm text-slate-400">{s.ports || '-'}</TableCell>
|
||||
<TableCell>
|
||||
<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>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}><VisualActions name={s.name} opts={opts} /></TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
@@ -122,10 +133,7 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell><Badge variant="outline" className="border-slate-700 text-slate-400">{i.namespace}</Badge></TableCell>
|
||||
<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>
|
||||
<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>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}><VisualActions name={i.name} opts={opts} /></TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
@@ -138,10 +146,7 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell className="font-medium text-slate-200">{c.name}</TableCell>
|
||||
<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>
|
||||
<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>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}><VisualActions name={c.name} opts={opts} /></TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
@@ -153,10 +158,7 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell className="font-medium text-slate-200">{s.name}</TableCell>
|
||||
<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>
|
||||
<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>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}><VisualActions name={s.name} opts={opts} /></TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
@@ -197,10 +199,7 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
<TableCell><Badge className={p.phase === 'Bound' ? 'bg-emerald-500/20 text-emerald-400' : 'bg-yellow-500/20 text-yellow-400'}>{p.phase || '-'}</Badge></TableCell>
|
||||
<TableCell className="text-sm text-slate-400">{p.size || '-'}</TableCell>
|
||||
<TableCell className="text-sm text-slate-400">{p.storage_class || '-'}</TableCell>
|
||||
<TableCell>
|
||||
<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>
|
||||
<TableCell onClick={(e) => e.stopPropagation()}><VisualActions name={p.name} opts={opts} /></TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
@@ -338,6 +337,8 @@ 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 [visualItem, setVisualItem] = useState<{ resource: string; name: string; isCreate?: boolean } | null>(null);
|
||||
const [showYamlImport, setShowYamlImport] = useState(false);
|
||||
|
||||
const [terminalPod, setTerminalPod] = useState<any>(null);
|
||||
const [fileBrowserPod, setFileBrowserPod] = useState<any>(null);
|
||||
@@ -405,8 +406,15 @@ export default function NamespaceResourcePage() {
|
||||
};
|
||||
|
||||
const handleYaml = (name: string) => setYamlItem({ resource: type, name });
|
||||
// 网络/配置/存储统一走 YamlViewer 编辑(清理只读字段 + CodeMirror)// Logan.Li
|
||||
const handleEdit = (name: string) => setYamlItem({ resource: type, name, editMode: true });
|
||||
// 网络/配置/存储走可视化编辑器,YAML 作为高级入口 // Logan.Li
|
||||
const handleEdit = (name: string) => {
|
||||
if (VISUAL_RESOURCE_TYPES.includes(type)) {
|
||||
setVisualItem({ resource: type, name });
|
||||
} else {
|
||||
setYamlItem({ resource: type, name, editMode: true });
|
||||
}
|
||||
};
|
||||
const handleCreate = () => setVisualItem({ resource: type, name: '', isCreate: true });
|
||||
const handleExportCSV = () => kubernetesApi.exportAsCSV(`${type}-${clusterCode}-${namespace}.csv`, filtered);
|
||||
const handleExportJSON = () => kubernetesApi.exportAsJSON(`${type}-${clusterCode}-${namespace}.json`, filtered);
|
||||
|
||||
@@ -426,6 +434,24 @@ export default function NamespaceResourcePage() {
|
||||
autoEdit={yamlItem.editMode === true}
|
||||
/>
|
||||
)}
|
||||
{showYamlImport && (
|
||||
<YamlImportDialog
|
||||
clusterCode={clusterCode || ''}
|
||||
onClose={() => setShowYamlImport(false)}
|
||||
onApplied={loadData}
|
||||
/>
|
||||
)}
|
||||
{visualItem && (
|
||||
<ResourceVisualEditor
|
||||
clusterCode={clusterCode || ''}
|
||||
namespace={namespace || ''}
|
||||
resource={visualItem.resource}
|
||||
name={visualItem.name}
|
||||
isCreate={visualItem.isCreate}
|
||||
onClose={() => setVisualItem(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>
|
||||
@@ -456,6 +482,16 @@ export default function NamespaceResourcePage() {
|
||||
<Button variant="outline" size="sm" className="h-9 border-slate-700 text-slate-400 text-xs" onClick={handleExportJSON} title="导出JSON">
|
||||
<Download className="h-3 w-3 mr-1" />JSON
|
||||
</Button>
|
||||
{VISUAL_RESOURCE_TYPES.includes(type) && (
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="h-9 border-slate-700 text-slate-400 text-xs" onClick={() => setShowYamlImport(true)}>
|
||||
<Upload className="h-3 w-3 mr-1" />导入
|
||||
</Button>
|
||||
<Button size="sm" className="h-9 text-xs bg-blue-600 hover:bg-blue-700" onClick={handleCreate}>
|
||||
<Plus className="h-3 w-3 mr-1" />新建
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
|
||||
Reference in new issue
Block a user