feat: K8s→Kubernetes命名统一 + 工具安装bug修复 + 资源可视化编辑器
1. 工具安装bug修复 (P0 critical):
- 根因: ToolConfigHandler.InstallTool的goroutine只打了日志,
从未调用实际安装逻辑
- 修复: 注入ToolInstaller到handler, 新增ForceInstall方法绕过sync.Once
- 影响: 流水线工具配置页「安装」按钮现在能真正触发下载安装
2. K8s→Kubernetes命名统一:
- Dashboard/Prometheus/AIOpsAssistant等8个文件12处用户可见文案
- 内部变量名/文件名/API group保持不变
3. 资源可视化编辑器 (新增ResourceVisualEditor.tsx):
- ConfigMap: 键值对表格编辑器(增删改)
- Secret: 键值对编辑器(自动Base64编解码)
- Service: 端口/类型/选择器可视化配置
- Ingress: Host/Path路由规则可视化编辑
- PVC: 存储大小/访问模式/StorageClass配置
- 双模式: 可视化编辑 ↔ YAML编辑一键切换
- NamespaceResourcePage: 所有资源类型添加编辑按钮入口
This commit is contained in:
1 parent
e29dfb5868
commit
aaab923f4e
14 files changed
+748
-20
No files matched your search
@@ -16,6 +16,7 @@ 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 '@/services/api';
|
||||
@@ -104,7 +105,12 @@ 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="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></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>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
@@ -119,7 +125,12 @@ 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="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></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>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
},
|
||||
@@ -191,7 +202,7 @@ const RESOURCE_CONFIG: Record<ResourceType, {
|
||||
},
|
||||
pvcs: {
|
||||
title: '存储卷声明', subtitle: 'PersistentVolumeClaim', icon: Boxes,
|
||||
columns: ['名称', '命名空间', '状态', '大小', 'StorageClass'],
|
||||
columns: ['名称', '命名空间', '状态', '大小', 'StorageClass', '操作'],
|
||||
renderRow: (p, opts) => (
|
||||
<TableRow key={p.name} className="border-slate-800 hover:bg-slate-800/30">
|
||||
<TableCell className="font-medium text-slate-200">{p.name}</TableCell>
|
||||
@@ -199,6 +210,12 @@ 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>
|
||||
<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>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
),
|
||||
},
|
||||
@@ -336,6 +353,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);
|
||||
|
||||
@@ -402,7 +420,14 @@ export default function NamespaceResourcePage() {
|
||||
};
|
||||
|
||||
const handleYaml = (name: string) => setYamlItem({ resource: type, name });
|
||||
const handleEdit = (name: string) => setYamlItem({ resource: type, name, editMode: true });
|
||||
const handleEdit = (name: string) => {
|
||||
// 使用可视化编辑器的资源类型
|
||||
if (['configmaps', 'secrets', 'services', 'ingresses', 'pvcs'].includes(type)) {
|
||||
setVisualEditItem({ resource: type, name });
|
||||
} else {
|
||||
setYamlItem({ resource: type, name, editMode: true });
|
||||
}
|
||||
};
|
||||
const handleExportCSV = () => kubernetesApi.exportAsCSV(`${type}-${clusterCode}-${namespace}.csv`, filtered);
|
||||
const handleExportJSON = () => kubernetesApi.exportAsJSON(`${type}-${clusterCode}-${namespace}.json`, filtered);
|
||||
|
||||
@@ -439,6 +464,16 @@ 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></>}
|
||||
|
||||
Reference in new issue
Block a user