refactor(quality): Q6 前端巨型组件拆分 — ResourceVisualEditor 882L→282L (-68%), 提取 5 个独立资源编辑器文件
将 ResourceVisualEditor.tsx (882L, 项目最大前端组件) 中的 5 个内联资源编辑器 提取为 resource-visual-editors/ 子目录下的独立文件: - ConfigMapEditor.tsx (95L) — ConfigMap 键值对表格 - SecretEditor.tsx (114L) — Secret Base64 编解码 - ServiceEditor.tsx (155L) — Service 端口/类型/选择器 - IngressEditor.tsx (215L) — Ingress Host/Path 路由规则 - PvcEditor.tsx (90L) — PVC 存储配置 主文件仅保留加载/保存逻辑 + YAML/可视化切换 + 资源类型分发。 清理 11 个 unused import。零逻辑变更,纯机械提取。 验证: tsc -b=0, npm build✓ (16.11s), ESLint errors=0, 17/17 测试通过
This commit is contained in:
1 parent
5d4a99b8a9
commit
892cb85676
8 files changed
+711
-607
No files matched your search
@@ -0,0 +1,114 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// SecretEditor — Secret 键值对表格可视化编辑器 (Base64 编码/解码, 从 ResourceVisualEditor.tsx 拆分)
|
||||
import { useState } from 'react';
|
||||
import { Plus, Trash2, Loader2, Save } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
export interface SecretEditorProps {
|
||||
manifest: any;
|
||||
onSave: (m: any) => void;
|
||||
saving: boolean;
|
||||
isCreate?: boolean;
|
||||
}
|
||||
|
||||
export function SecretEditor({ manifest, onSave, saving, isCreate }: SecretEditorProps) {
|
||||
const rawData = manifest?.data || {};
|
||||
const [resName, setResName] = useState(manifest?.metadata?.name || '');
|
||||
const [expandAll, setExpandAll] = useState(false);
|
||||
// Decode base64 values for editing
|
||||
const [entries, setEntries] = useState<{ key: string; value: string; encoded: boolean }[]>(
|
||||
Object.entries(rawData).map(([key, value]) => {
|
||||
const v = String(value);
|
||||
try {
|
||||
// Try to decode — if it's valid base64
|
||||
const decoded = atob(v);
|
||||
return { key, value: decoded, encoded: true };
|
||||
} catch {
|
||||
return { key, value: v, encoded: false };
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const update = (i: number, field: 'key' | 'value', val: string) => {
|
||||
const next = [...entries];
|
||||
next[i] = { ...next[i], [field]: val };
|
||||
setEntries(next);
|
||||
};
|
||||
|
||||
const addRow = () => setEntries([...entries, { key: '', value: '', encoded: true }]);
|
||||
const removeRow = (i: number) => setEntries(entries.filter((_, idx) => idx !== i));
|
||||
|
||||
const handleSave = () => {
|
||||
const newData: Record<string, string> = {};
|
||||
entries.forEach(e => {
|
||||
if (e.key.trim()) {
|
||||
// Re-encode if originally base64
|
||||
newData[e.key.trim()] = e.encoded ? btoa(e.value) : e.value;
|
||||
}
|
||||
});
|
||||
onSave({ ...manifest, metadata: { ...manifest.metadata, name: resName.trim() }, data: newData });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="shrink-0 grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs"> {i18n.t('kubernetes.secret.secretName')} </Label>
|
||||
<Input value={resName} onChange={(e) => setResName(e.target.value)} readOnly={!isCreate} className="mt-1 font-mono text-xs bg-[var(--dg-bg-tertiary)]" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[var(--dg-text-secondary)] text-xs"> {i18n.t('system.credentialType')} </Label>
|
||||
<Input value={manifest?.type || 'Opaque'} readOnly className="mt-1 font-mono text-xs bg-[var(--dg-bg-tertiary)]" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-h-0 flex flex-col mt-3">
|
||||
<div className="flex items-center justify-between mb-2 shrink-0">
|
||||
<Label className="text-[var(--dg-text-secondary)] text-sm"> {i18n.t('kubernetes.resourcevisuale.se1937033')} </Label>
|
||||
<div className="flex gap-1">
|
||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setExpandAll(!expandAll)}>
|
||||
{expandAll ? i18n.t('kubernetes.resourcevisuale.s880e849b') : i18n.t('system.expandAll')}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-7 text-xs" onClick={addRow}><Plus className="h-3 w-3 mr-1" /> {i18n.t('monitor.dashboard.custom.addBtn')} </Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 min-h-0 overflow-y-auto pr-1 space-y-2 border border-[var(--dg-border)] rounded-lg bg-[var(--dg-bg-secondary)] p-2">
|
||||
{entries.map((entry, i) => (
|
||||
<div key={i} className="flex items-start gap-2 p-2 rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-tertiary)]">
|
||||
<Input
|
||||
value={entry.key}
|
||||
onChange={e => update(i, 'key', e.target.value)}
|
||||
placeholder={i18n.t('kubernetes.resourcevisuale.scdecc460')}
|
||||
className="flex-1 font-mono text-xs h-8 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"
|
||||
/>
|
||||
<Textarea
|
||||
value={entry.value}
|
||||
onChange={e => update(i, 'value', e.target.value)}
|
||||
placeholder={i18n.t('kubernetes.resourcevisuale.s7f123dd4')}
|
||||
rows={expandAll || entry.value.length > 300 ? 10 : (entry.value.length > 100 ? 6 : 3)}
|
||||
className="flex-[2] font-mono text-xs min-h-[70px] max-h-[420px] resize-y bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] leading-relaxed"
|
||||
/>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 dg-text-red hover:dg-text-red shrink-0" onClick={() => removeRow(i)}>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{entries.length === 0 && (
|
||||
<div className="text-center py-8 text-[var(--dg-text-muted)] text-xs"> {i18n.t('kubernetes.resourcevisuale.s47ff6900')} </div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 flex justify-end gap-2 pt-3 mt-2 border-t border-[var(--dg-border)]">
|
||||
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={handleSave} disabled={saving}>
|
||||
{saving ? <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> : <Save className="h-3.5 w-3.5 mr-1" />}
|
||||
保存密钥
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user