fix(i18n): 回退到稳定的 32% 覆盖率状态 (tsc + Vite 构建通过)
批量转换脚本引入的语法错误已回退,保留稳定的 i18n 转换: - 翻译词典: 22 namespaces, 3600+ keys - 已接入: 145+ 文件 (Dashboard/Login/K8s 主页面/Monitor/System/Deploy) - 语言切换器在 Header 中可用 (🇨🇳/🇺🇸) - tsc 零错误, Vite 构建成功
This commit is contained in:
1 parent
ecec73989b
commit
72bfb0e12d
191 files changed
+5057
-7592
No files matched your search
@@ -19,10 +19,10 @@ import {
|
||||
Zap,
|
||||
Network,
|
||||
} from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { arrToText, textToArr } from './container-utils';
|
||||
import type {
|
||||
import { useTranslation } from 'react-i18next';
|
||||
ContainerEditorProps, ProbeEditorProps, LifecycleHandlerEditorProps,
|
||||
BoolToggleProps, ProbeDef, LifecycleHandler,
|
||||
} from './types';
|
||||
@@ -37,25 +37,25 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
{/* ---- 基本设置 ---- */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label className="dg-text-secondary text-xs">{t('kubernetes.containereditor.sa51cd0')}</Label>
|
||||
<Label className="dg-text-secondary text-xs">{t('k8s.container.name')}</Label>
|
||||
<Input value={c.name} onChange={e => onUpdate({ name: e.target.value })}
|
||||
placeholder="my-app"
|
||||
className="mt-1 dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="dg-text-secondary text-xs">{t('kubernetes.containereditor.s332ca5')}</Label>
|
||||
<Label className="dg-text-secondary text-xs">{t('k8s.container.image')}</Label>
|
||||
<Input value={c.image} onChange={e => onUpdate({ image: e.target.value })}
|
||||
placeholder="nginx:1.25"
|
||||
className="mt-1 dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="dg-text-secondary text-xs">{t('kubernetes.deployment.imagePullPolicy')}</Label>
|
||||
<Label className="dg-text-secondary text-xs">{t('k8s.container.imagePullPolicy')}</Label>
|
||||
<Select value={c.imagePullPolicy || '__none'} onValueChange={v => onUpdate({ imagePullPolicy: v === '__none' ? '' : v })}>
|
||||
<SelectTrigger className="mt-1 dg-input text-xs h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dg-input">
|
||||
<SelectItem value="__none" className="dg-text-secondary">{t('kubernetes.containereditor.sd2f3ee')}</SelectItem>
|
||||
<SelectItem value="__none" className="dg-text-secondary">{t('k8s.container.pullPolicyNone')}</SelectItem>
|
||||
<SelectItem value="Always" className="text-[var(--dg-text-primary)]">Always</SelectItem>
|
||||
<SelectItem value="IfNotPresent" className="text-[var(--dg-text-primary)]">IfNotPresent</SelectItem>
|
||||
<SelectItem value="Never" className="text-[var(--dg-text-primary)]">Never</SelectItem>
|
||||
@@ -63,7 +63,7 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="dg-text-secondary text-xs">{t('kubernetes.containereditor.s4867b9')}</Label>
|
||||
<Label className="dg-text-secondary text-xs">{t('k8s.container.workingDir')}</Label>
|
||||
<Input value={c.workingDir || ''} onChange={e => onUpdate({ workingDir: e.target.value })}
|
||||
placeholder="/app"
|
||||
className="mt-1 dg-input font-mono text-xs h-8" />
|
||||
@@ -73,13 +73,13 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
{/* ---- 命令与参数 ---- */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label className="dg-text-secondary text-xs">{t('kubernetes.containereditor.s34ab0f')}</Label>
|
||||
<Label className="dg-text-secondary text-xs">{t('k8s.container.command')}</Label>
|
||||
<Textarea value={arrToText(c.command)} placeholder={"/bin/sh\n-c"}
|
||||
onChange={e => onUpdate({ command: textToArr(e.target.value) })}
|
||||
className="mt-1 dg-input font-mono text-xs min-h-[60px]" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="dg-text-secondary text-xs">{t('kubernetes.containereditor.s6dd286')}</Label>
|
||||
<Label className="dg-text-secondary text-xs">{t('k8s.container.args')}</Label>
|
||||
<Textarea value={arrToText(c.args)} placeholder={"--config=/etc/app.yaml\n--debug"}
|
||||
onChange={e => onUpdate({ args: textToArr(e.target.value) })}
|
||||
className="mt-1 dg-input font-mono text-xs min-h-[60px]" />
|
||||
@@ -89,10 +89,10 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
{/* ---- 环境变量 ---- */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className={sectionBtn}><Terminal className="h-3.5 w-3.5 dg-text-cyan" /> {t('pipeline.envVar')}</div>
|
||||
<div className={sectionBtn}><Terminal className="h-3.5 w-3.5 dg-text-cyan" /> {t('k8s.container.envVars')}</div>
|
||||
<Button size="sm" variant="outline" className="h-6 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-[11px]"
|
||||
onClick={() => onUpdate({ env: [...c.env, { name: '', value: '' }] })}>
|
||||
<Plus className="h-3 w-3 mr-1" />新增
|
||||
<Plus className="h-3 w-3 mr-1" />{t('common.add')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
@@ -110,17 +110,17 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{c.env.length === 0 && <p className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.s9f809d')}</p>}
|
||||
{c.env.length === 0 && <p className="text-[11px] dg-text-muted">{t('k8s.container.noEnv')}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ---- 端口 ---- */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className={sectionBtn}><Network className="h-3.5 w-3.5 text-indigo-400" /> {t('server.csv.port')}</div>
|
||||
<div className={sectionBtn}><Network className="h-3.5 w-3.5 text-indigo-400" /> {t('k8s.container.ports')}</div>
|
||||
<Button size="sm" variant="outline" className="h-6 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-[11px]"
|
||||
onClick={() => onUpdate({ ports: [...c.ports, { name: '', containerPort: '', protocol: 'TCP' }] })}>
|
||||
<Plus className="h-3 w-3 mr-1" />新增
|
||||
<Plus className="h-3 w-3 mr-1" />{t('common.add')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
@@ -148,34 +148,34 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{c.ports.length === 0 && <p className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.sbc8b87')}</p>}
|
||||
{c.ports.length === 0 && <p className="text-[11px] dg-text-muted">{t('k8s.container.noPortsExposed')}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ---- 资源 requests/limits ---- */}
|
||||
<div>
|
||||
<div className={sectionBtn}><Cpu className="h-3.5 w-3.5 dg-text-yellow" /> {t('kubernetes.containereditor.s0b1499')}</div>
|
||||
<div className={sectionBtn}><Cpu className="h-3.5 w-3.5 dg-text-yellow" /> {t('k8s.container.resources')}</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
|
||||
<div>
|
||||
<Label className="text-[11px] dg-text-muted">{t('kubernetes.node.cpuRequests')}</Label>
|
||||
<Label className="text-[11px] dg-text-muted">{t('k8s.container.cpuRequest')}</Label>
|
||||
<Input value={c.resources.requests?.cpu || ''} placeholder="100m"
|
||||
onChange={e => onUpdate({ resources: { ...c.resources, requests: { ...c.resources.requests, cpu: e.target.value } } })}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.s96ac88')}</Label>
|
||||
<Label className="text-[11px] dg-text-muted">{t('k8s.container.cpuLimit')}</Label>
|
||||
<Input value={c.resources.limits?.cpu || ''} placeholder="500m"
|
||||
onChange={e => onUpdate({ resources: { ...c.resources, limits: { ...c.resources.limits, cpu: e.target.value } } })}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[11px] dg-text-muted">{t('kubernetes.node.memoryRequests')}</Label>
|
||||
<Label className="text-[11px] dg-text-muted">{t('k8s.container.memRequest')}</Label>
|
||||
<Input value={c.resources.requests?.memory || ''} placeholder="128Mi"
|
||||
onChange={e => onUpdate({ resources: { ...c.resources, requests: { ...c.resources.requests, memory: e.target.value } } })}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.sa0a98f')}</Label>
|
||||
<Label className="text-[11px] dg-text-muted">{t('k8s.container.memLimit')}</Label>
|
||||
<Input value={c.resources.limits?.memory || ''} placeholder="512Mi"
|
||||
onChange={e => onUpdate({ resources: { ...c.resources, limits: { ...c.resources.limits, memory: e.target.value } } })}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
@@ -185,11 +185,11 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
|
||||
{/* ---- 健康检查 ---- */}
|
||||
<div>
|
||||
<div className={sectionBtn}><Heart className="h-3.5 w-3.5 text-rose-400" /> {t('kubernetes.containereditor.s6779ab')}</div>
|
||||
<div className={sectionBtn}><Heart className="h-3.5 w-3.5 text-rose-400" /> {t('k8s.container.probes')}</div>
|
||||
<div className="space-y-2">
|
||||
{(['livenessProbe', 'readinessProbe', 'startupProbe'] as const).map(pt => (
|
||||
<ProbeEditor key={pt}
|
||||
label={{ livenessProbe: t('kubernetes.containereditor.sff49b1'), readinessProbe: t('kubernetes.containereditor.s176e68'), startupProbe: t('kubernetes.containereditor.sc5a531') }[pt]}
|
||||
label={t({ livenessProbe: 'k8s.container.liveness', readinessProbe: 'k8s.container.readiness', startupProbe: 'k8s.container.startup' }[pt])}
|
||||
probe={c[pt]}
|
||||
onChange={(p) => onUpdate({ [pt]: p } as Record<string, unknown>)}
|
||||
/>
|
||||
@@ -199,24 +199,24 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
|
||||
{/* ---- 生命周期 ---- */}
|
||||
<div>
|
||||
<div className={sectionBtn}><Zap className="h-3.5 w-3.5 dg-text-yellow" /> {t('kubernetes.containereditor.s12d037')}</div>
|
||||
<div className={sectionBtn}><Zap className="h-3.5 w-3.5 dg-text-yellow" /> {t('k8s.container.lifecycle')}</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<LifecycleHandlerEditor label=t('kubernetes.containereditor.s650280') handler={c.lifecycle?.postStart}
|
||||
<LifecycleHandlerEditor label={t('k8s.container.postStart')} handler={c.lifecycle?.postStart}
|
||||
onChange={h => onUpdate({ lifecycle: { ...c.lifecycle, postStart: h } })} />
|
||||
<LifecycleHandlerEditor label=t('kubernetes.containereditor.s5b7228') handler={c.lifecycle?.preStop}
|
||||
<LifecycleHandlerEditor label={t('k8s.container.preStop')} handler={c.lifecycle?.preStop}
|
||||
onChange={h => onUpdate({ lifecycle: { ...c.lifecycle, preStop: h } })} />
|
||||
</div>
|
||||
{!c.lifecycle && (
|
||||
<Button size="sm" variant="outline" className="h-6 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-[11px] mt-1"
|
||||
onClick={() => onUpdate({ lifecycle: { postStart: { type: 'none' }, preStop: { type: 'none' } } })}>
|
||||
<Plus className="h-3 w-3 mr-1" />启用生命周期回调
|
||||
<Plus className="h-3 w-3 mr-1" />{t('k8s.container.enableLifecycle')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ---- 安全上下文 ---- */}
|
||||
<div>
|
||||
<div className={sectionBtn}><Shield className="h-3.5 w-3.5 dg-text-green" /> {t('kubernetes.containereditor.s195d42')}</div>
|
||||
<div className={sectionBtn}><Shield className="h-3.5 w-3.5 dg-text-green" /> {t('k8s.container.securityContext')}</div>
|
||||
{c.securityContext ? (
|
||||
<div className="space-y-2 border border-[var(--dg-border)] rounded-md p-3 bg-[var(--dg-bg-primary)]/30">
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
|
||||
@@ -233,13 +233,13 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.s3142a7')}</Label>
|
||||
<Label className="text-[11px] dg-text-muted">{t('k8s.container.capsAdd')}</Label>
|
||||
<Input value={(c.securityContext.capabilitiesAdd || []).join(',')} placeholder="NET_ADMIN"
|
||||
onChange={e => onUpdate({ securityContext: { ...c.securityContext!, capabilitiesAdd: e.target.value.split(',').map(s => s.trim()).filter(Boolean) } })}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.sd705f3')}</Label>
|
||||
<Label className="text-[11px] dg-text-muted">{t('k8s.container.capsDrop')}</Label>
|
||||
<Input value={(c.securityContext.capabilitiesDrop || []).join(',')} placeholder="ALL"
|
||||
onChange={e => onUpdate({ securityContext: { ...c.securityContext!, capabilitiesDrop: e.target.value.split(',').map(s => s.trim()).filter(Boolean) } })}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
@@ -257,13 +257,13 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
</div>
|
||||
<Button size="sm" variant="ghost" className="h-6 dg-text-red text-[11px]"
|
||||
onClick={() => onUpdate({ securityContext: null })}>
|
||||
<Trash2 className="h-3 w-3 mr-1" />移除安全上下文
|
||||
<Trash2 className="h-3 w-3 mr-1" />{t('k8s.container.removeSecurity')}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button size="sm" variant="outline" className="h-6 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-[11px]"
|
||||
onClick={() => onUpdate({ securityContext: { runAsNonRoot: false, privileged: false } })}>
|
||||
<Plus className="h-3 w-3 mr-1" />启用安全上下文
|
||||
<Plus className="h-3 w-3 mr-1" />{t('k8s.container.enableSecurity')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -271,22 +271,22 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
{/* ---- 卷挂载 ---- */}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className={sectionBtn}><FolderOpen className="h-3.5 w-3.5 text-teal-400" /> {t('kubernetes.containereditor.s778eec')}</div>
|
||||
<div className={sectionBtn}><FolderOpen className="h-3.5 w-3.5 text-teal-400" /> {t('k8s.container.volumeMounts')}</div>
|
||||
<Button size="sm" variant="outline" className="h-6 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-[11px]"
|
||||
onClick={() => onUpdate({ volumeMounts: [...c.volumeMounts, { name: '', mountPath: '', subPath: '', readOnly: false }] })}>
|
||||
<Plus className="h-3 w-3 mr-1" />新增
|
||||
<Plus className="h-3 w-3 mr-1" />{t('common.add')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
{c.volumeMounts.map((m, mi) => (
|
||||
<div key={mi} className="grid grid-cols-[1fr_1fr_1fr_60px_auto] gap-2 items-center">
|
||||
<Input value={m.name} placeholder=t('kubernetes.containereditor.sb19f3d')
|
||||
<Input value={m.name} placeholder={t('k8s.container.volumeName')}
|
||||
onChange={e => { const n = [...c.volumeMounts]; n[mi] = { ...m, name: e.target.value }; onUpdate({ volumeMounts: n }); }}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
<Input value={m.mountPath} placeholder="/data"
|
||||
onChange={e => { const n = [...c.volumeMounts]; n[mi] = { ...m, mountPath: e.target.value }; onUpdate({ volumeMounts: n }); }}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
<Input value={m.subPath || ''} placeholder=t('kubernetes.containereditor.s4f4937')
|
||||
<Input value={m.subPath || ''} placeholder={t('k8s.container.subPathPlaceholder')}
|
||||
onChange={e => { const n = [...c.volumeMounts]; n[mi] = { ...m, subPath: e.target.value }; onUpdate({ volumeMounts: n }); }}
|
||||
className="dg-input font-mono text-xs h-8" />
|
||||
<label className="flex items-center gap-1 text-[10px] dg-text-muted cursor-pointer">
|
||||
@@ -300,7 +300,7 @@ export function ContainerEditor({ container, onUpdate }: ContainerEditorProps) {
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{c.volumeMounts.length === 0 && <p className="text-[11px] dg-text-muted">{t('kubernetes.containereditor.s9b6826')}</p>}
|
||||
{c.volumeMounts.length === 0 && <p className="text-[11px] dg-text-muted">{t('k8s.container.noMounts')}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -324,7 +324,7 @@ function ProbeEditor({ label, probe, onChange }: ProbeEditorProps) {
|
||||
{enabled && (
|
||||
<>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-[10px] dg-text-muted w-8">{t('common.type')}</span>
|
||||
<span className="text-[10px] dg-text-muted w-8">{t('k8s.container.type')}</span>
|
||||
<Select value={p.type || 'httpGet'} onValueChange={v => setField({ type: v as ProbeDef['type'] })}>
|
||||
<SelectTrigger className="dg-input text-xs h-7 flex-1">
|
||||
<SelectValue />
|
||||
@@ -332,7 +332,7 @@ function ProbeEditor({ label, probe, onChange }: ProbeEditorProps) {
|
||||
<SelectContent className="dg-input">
|
||||
<SelectItem value="httpGet" className="text-[var(--dg-text-primary)]">HTTP GET</SelectItem>
|
||||
<SelectItem value="tcpSocket" className="text-[var(--dg-text-primary)]">TCP Socket</SelectItem>
|
||||
<SelectItem value="exec" className="text-[var(--dg-text-primary)]">{t('kubernetes.containereditor.s16d93f')}</SelectItem>
|
||||
<SelectItem value="exec" className="text-[var(--dg-text-primary)]">{t('k8s.container.execCmd')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -344,52 +344,52 @@ function ProbeEditor({ label, probe, onChange }: ProbeEditorProps) {
|
||||
<Input type="number" value={p.httpPort ?? ''} placeholder="8080"
|
||||
onChange={e => setField({ httpPort: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
<Input value={p.httpHost || ''} placeholder=t('kubernetes.containereditor.s3cf87c')
|
||||
<Input value={p.httpHost || ''} placeholder={t('k8s.container.hostPlaceholder')}
|
||||
onChange={e => setField({ httpHost: e.target.value })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
</div>
|
||||
)}
|
||||
{p.type === 'tcpSocket' && (
|
||||
<div className="mb-2">
|
||||
<Input type="number" value={p.tcpPort ?? ''} placeholder=t('server.csv.port')
|
||||
<Input type="number" value={p.tcpPort ?? ''} placeholder={t('k8s.container.portPlaceholder')}
|
||||
onChange={e => setField({ tcpPort: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
</div>
|
||||
)}
|
||||
{p.type === 'exec' && (
|
||||
<div className="mb-2">
|
||||
<Textarea value={p.execCommand || ''} placeholder=t('kubernetes.containereditor.s743f00')
|
||||
<Textarea value={p.execCommand || ''} placeholder={t('k8s.container.execCmdPlaceholder')}
|
||||
onChange={e => setField({ execCommand: e.target.value })}
|
||||
className="dg-input font-mono text-xs min-h-[50px]" />
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-5 gap-2">
|
||||
<div>
|
||||
<Label className="text-[10px] dg-text-muted">{t('kubernetes.containereditor.s39b4e6')}</Label>
|
||||
<Label className="text-[10px] dg-text-muted">{t('k8s.container.delay')}</Label>
|
||||
<Input type="number" value={p.initialDelaySeconds ?? ''} placeholder="10"
|
||||
onChange={e => setField({ initialDelaySeconds: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[10px] dg-text-muted">{t('kubernetes.containereditor.sbbf419')}</Label>
|
||||
<Label className="text-[10px] dg-text-muted">{t('k8s.container.interval')}</Label>
|
||||
<Input type="number" value={p.periodSeconds ?? ''} placeholder="10"
|
||||
onChange={e => setField({ periodSeconds: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[10px] dg-text-muted">{t('kubernetes.containereditor.sde332f')}</Label>
|
||||
<Label className="text-[10px] dg-text-muted">{t('k8s.container.timeout')}</Label>
|
||||
<Input type="number" value={p.timeoutSeconds ?? ''} placeholder="1"
|
||||
onChange={e => setField({ timeoutSeconds: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[10px] dg-text-muted">{t('common.success')}</Label>
|
||||
<Label className="text-[10px] dg-text-muted">{t('k8s.container.success')}</Label>
|
||||
<Input type="number" value={p.successThreshold ?? ''} placeholder="1"
|
||||
onChange={e => setField({ successThreshold: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-[10px] dg-text-muted">{t('common.failed')}</Label>
|
||||
<Label className="text-[10px] dg-text-muted">{t('k8s.container.failure')}</Label>
|
||||
<Input type="number" value={p.failureThreshold ?? ''} placeholder="3"
|
||||
onChange={e => setField({ failureThreshold: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
@@ -410,21 +410,21 @@ function LifecycleHandlerEditor({ label, handler, onChange }: LifecycleHandlerEd
|
||||
<div className="border border-[var(--dg-border)] rounded-md p-2.5 bg-[var(--dg-bg-primary)]/30">
|
||||
<span className="text-xs text-[var(--dg-text-secondary)] font-medium block mb-2">{label}</span>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="text-[10px] dg-text-muted w-8">{t('common.type')}</span>
|
||||
<span className="text-[10px] dg-text-muted w-8">{t('k8s.container.type')}</span>
|
||||
<Select value={selVal} onValueChange={v => onChange({ ...h, type: (v === '__none' ? 'none' : v) as LifecycleHandler['type'] })}>
|
||||
<SelectTrigger className="dg-input text-xs h-7 flex-1">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dg-input">
|
||||
<SelectItem value="__none" className="dg-text-secondary">{t('kubernetes.containereditor.s2e3432')}</SelectItem>
|
||||
<SelectItem value="exec" className="text-[var(--dg-text-primary)]">{t('kubernetes.containereditor.s16d93f')}</SelectItem>
|
||||
<SelectItem value="__none" className="dg-text-secondary">{t('k8s.container.none')}</SelectItem>
|
||||
<SelectItem value="exec" className="text-[var(--dg-text-primary)]">{t('k8s.container.execCmd')}</SelectItem>
|
||||
<SelectItem value="httpGet" className="text-[var(--dg-text-primary)]">HTTP GET</SelectItem>
|
||||
<SelectItem value="tcpSocket" className="text-[var(--dg-text-primary)]">TCP Socket</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{h.type === 'exec' && (
|
||||
<Textarea value={h.execCommand || ''} placeholder=t('kubernetes.containereditor.s7970e5')
|
||||
<Textarea value={h.execCommand || ''} placeholder={t('k8s.container.execCmdLinePlaceholder')}
|
||||
onChange={e => onChange({ ...h, execCommand: e.target.value })}
|
||||
className="dg-input font-mono text-xs min-h-[50px]" />
|
||||
)}
|
||||
@@ -439,7 +439,7 @@ function LifecycleHandlerEditor({ label, handler, onChange }: LifecycleHandlerEd
|
||||
</div>
|
||||
)}
|
||||
{h.type === 'tcpSocket' && (
|
||||
<Input type="number" value={h.tcpPort ?? ''} placeholder=i18n.t('server.csv.port')
|
||||
<Input type="number" value={h.tcpPort ?? ''} placeholder={t('k8s.container.portPlaceholder')}
|
||||
onChange={e => onChange({ ...h, tcpPort: e.target.value === '' ? undefined : Number(e.target.value) })}
|
||||
className="dg-input font-mono text-xs h-7" />
|
||||
)}
|
||||
@@ -448,11 +448,10 @@ function LifecycleHandlerEditor({ label, handler, onChange }: LifecycleHandlerEd
|
||||
}
|
||||
|
||||
function BoolToggle({ label, value, onChange }: BoolToggleProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<label className="flex items-center gap-1.5 cursor-pointer">
|
||||
<Switch checked={value} onCheckedChange={onChange} />
|
||||
<span className="text-[11px] dg-text-secondary font-mono">{label}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -45,14 +45,14 @@ export function ContainerGroupSection({
|
||||
<Badge variant="outline" className={`border-[var(--dg-border)] dg-text-secondary text-[10px]`}>{containers.length}</Badge>
|
||||
</div>
|
||||
<Button size="sm" variant="outline" className="h-7 border-[var(--dg-border)] text-[var(--dg-text-secondary)] text-xs" onClick={onAdd}>
|
||||
<Plus className="h-3 w-3 mr-1" />新增容器
|
||||
<Plus className="h-3 w-3 mr-1" />{t('k8s.container.add')}
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-[11px] dg-text-muted">{description}</p>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0 space-y-3">
|
||||
{containers.length === 0 && (
|
||||
<div className="text-center py-6 dg-text-muted text-xs">{t('kubernetes.containergroupsection.s79ed70')}</div>
|
||||
<div className="text-center py-6 dg-text-muted text-xs">{t('k8s.container.empty')}</div>
|
||||
)}
|
||||
{containers.map((c, idx) => {
|
||||
const isOpen = openIndices.has(idx) || containers.length === 1;
|
||||
@@ -63,21 +63,21 @@ export function ContainerGroupSection({
|
||||
<div className="flex items-center gap-2">
|
||||
{isOpen ? <ChevronDown className="h-4 w-4 dg-text-muted" /> : <ChevronRight className="h-4 w-4 dg-text-muted" />}
|
||||
<Container className={`h-4 w-4 ${accentColor === 'blue' ? 'dg-text-blue' : 'dg-text-purple'}`} />
|
||||
<span className="text-sm font-mono text-[var(--dg-text-primary)] font-semibold">{c.name || t('kubernetes.containergroupsection.sac52a9')}</span>
|
||||
<span className="text-sm font-mono text-[var(--dg-text-primary)] font-semibold">{c.name || t('k8s.container.unnamed')}</span>
|
||||
{c.image && <code className="text-[10px] dg-text-muted font-mono truncate max-w-[300px]">{c.image}</code>}
|
||||
</div>
|
||||
<div className="flex items-center gap-1" onClick={e => e.stopPropagation()}>
|
||||
{onDuplicate && (
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-muted hover:text-[var(--dg-text-secondary)]" title=t('common.copy')
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-muted hover:text-[var(--dg-text-secondary)]" title={t('common.copy')}
|
||||
onClick={() => onDuplicate(idx)}>
|
||||
<RotateCw className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-red hover:dg-text-red" title=t('common.delete')
|
||||
<Button variant="ghost" size="icon" className="h-7 w-7 dg-text-red hover:dg-text-red" title={t('common.delete')}
|
||||
onClick={async () => {
|
||||
const ok = await confirm({
|
||||
title: t('kubernetes.containergroupsection.s860aaf'),
|
||||
description: t('kubernetes.containergroupsection.s04ce19', { c.name: c.name }),
|
||||
title: t('k8s.container.deleteTitle'),
|
||||
description: t('k8s.container.deleteConfirm', { name: c.name }),
|
||||
confirmText: t('common.delete'),
|
||||
variant: 'destructive',
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user