// YamlViewer — YAML 查看/编辑(默认精简视图,工具栏:复制 / 编辑 / 保存)// Logan.Li import { useState, useEffect, useCallback } from 'react'; import { X, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { ModalOverlay } from '@/components/ui/modal-overlay'; import { useToast } from '@/hooks/use-toast'; import { kubernetesApi } from '@/api'; import { parseEditableManifest, parseEditableManifests, prepareYamlForSave } from './k8s-manifest-utils'; import { getYamlResourceConfig, yamlDownloadFilename } from './k8s-yaml-resource-config'; import { YamlEditorToolbar } from './YamlEditorToolbar'; import { YamlCodeMirror } from './YamlCodeMirror'; import { downloadYamlFile } from '@/utils/export'; import type { JsonData } from '@/types/common'; interface YamlViewerProps { clusterCode: string; namespace: string; resource: string; name: string; onClose: () => void; onSaved?: () => void; autoEdit?: boolean; inline?: boolean; initialYaml?: string; /** 覆盖资源配置的只读行为(Pod 默认只读)// Logan.Li */ readOnly?: boolean; /** 自定义加载(如 Deployment + Service 多文档)// Logan.Li */ loadYamlFn?: () => Promise; /** 自定义保存(如 Deployment + Service 多文档)// Logan.Li */ saveYamlFn?: (yaml: string) => Promise; /** 资源描述补充(如 "含关联 Service")// Logan.Li */ resourceHint?: string; } export function YamlViewer({ clusterCode, namespace, resource, name, onClose, onSaved, autoEdit = false, inline = false, initialYaml, readOnly: readOnlyProp, loadYamlFn, saveYamlFn, resourceHint, }: YamlViewerProps) { const { toast } = useToast(); const resCfg = getYamlResourceConfig(resource); const readOnly = readOnlyProp ?? resCfg.readOnly; const [rawYaml, setRawYaml] = useState(initialYaml || ''); const [displayYaml, setDisplayYaml] = useState(''); const [editYaml, setEditYaml] = useState(''); const [loading, setLoading] = useState(!initialYaml); const [saving, setSaving] = useState(false); const [editing, setEditing] = useState(autoEdit && !readOnly); const [copied, setCopied] = useState(false); const [showRaw, setShowRaw] = useState(false); const applyStripped = useCallback(async (text: string) => { const { yaml: stripped } = text.includes('\n---') ? await parseEditableManifests(text) : await parseEditableManifest(text); setDisplayYaml(stripped); setEditYaml(stripped); return stripped; }, []); const loadYaml = useCallback(async () => { if (initialYaml) { setRawYaml(initialYaml); await applyStripped(initialYaml); setLoading(false); return; } setLoading(true); try { const text = loadYamlFn ? await loadYamlFn() : await kubernetesApi.getResourceYaml(clusterCode, namespace, resource, name); setRawYaml(text); await applyStripped(text); } catch (e) { toast({ title: '获取YAML失败', description: (e as JsonData).message, variant: 'destructive' }); const err = `# Error: ${(e as Error).message}`; setRawYaml(err); setDisplayYaml(err); setEditYaml(err); } finally { setLoading(false); } }, [clusterCode, namespace, resource, name, initialYaml, loadYamlFn, applyStripped, toast]); useEffect(() => { loadYaml(); }, [loadYaml]); const enterEdit = () => { setEditYaml(displayYaml); setEditing(true); setShowRaw(false); }; const cancelEdit = async () => { setEditing(false); await loadYaml(); }; const saveYaml = async () => { setSaving(true); try { const yamlStr = await prepareYamlForSave(editYaml); if (saveYamlFn) { await saveYamlFn(yamlStr); } else { await kubernetesApi.updateResourceYaml(clusterCode, namespace, resource, name, yamlStr); } toast({ title: '保存成功', description: saveYamlFn ? '多文档 YAML 已应用' : `${resource}/${name} 已更新` }); setEditing(false); setRawYaml(yamlStr); setDisplayYaml(yamlStr); setEditYaml(yamlStr); onSaved?.(); } catch (e) { toast({ title: '保存失败', description: (e as Error)?.message || String(e), variant: 'destructive' }); } finally { setSaving(false); } }; const currentYaml = () => (editing ? editYaml : (showRaw ? rawYaml : displayYaml)); const copyYaml = async () => { await navigator.clipboard.writeText(currentYaml()); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const downloadYaml = () => { downloadYamlFile(yamlDownloadFilename(resource, namespace, name), currentYaml()); toast({ title: '已下载', description: yamlDownloadFilename(resource, namespace, name) }); }; const display = editing ? editYaml : (showRaw ? rawYaml : displayYaml); const toolbar = ( setShowRaw((v) => !v)} /> ); const editor = loading ? (
) : ( editing && setEditYaml(val)} /> ); if (inline) { return (

{resource}/{name} {resourceHint ? · {resourceHint} : null} {' · '}{namespace}

{toolbar}
{editor}
); } return (
e.stopPropagation()} >

{resCfg.label} YAML — {name} {readOnly && 只读}

{namespace} · {clusterCode}

{toolbar}
{editor}
); }