refactor(i18n): U43 深度残余硬编码中文 UI 文本清零 — 17 文件 ~40 处→0
趋势标签(上升/下降/平稳) + K8s 卡片标题(Pod级别/安全上下文/实例/详情) + data-table 默认值(搜索/暂无数据) + Deployment 编辑器标签(数据卷/挂载/只读写/manifest提示/镜像拉取凭证) + HostSettings 4 个命名空间描述 + 7 个 K8s 页面刷新按钮 + 资源编辑器新建/编辑 + Agent 执行命令描述 + 账单保存成功 toast + 容量预警标题 新增 common 30 语义化翻译键, zh/en 7898→7928 对齐 tsc -b=0, npm build✓ (16.55s), ESLint=0/43/any=0, go build=0, 17/17 测试通过
This commit is contained in:
1 parent
c6e0d6ca57
commit
39a4432f79
26 files changed
+117
-62
No files matched your search
@@ -303,7 +303,7 @@ export function AgentMonitor() {
|
||||
{t("agent.agentmonitortsx.s8h9i0j1k")}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="dg-text-secondary">
|
||||
在 {cmdHost} 上执行命令
|
||||
{t('common.executeOn', { host: cmdHost })}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ export function HostDetailDialog({
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)] flex items-center gap-1.5">
|
||||
<Activity className="h-4 w-4 dg-text-blue" />
|
||||
指标趋势
|
||||
{i18n.t('common.metricTrend')}
|
||||
</span>
|
||||
<Select value={String(hours)} onValueChange={(v) => setHours(Number(v))}>
|
||||
<SelectTrigger size="sm" className="h-7 w-28 dg-input text-[var(--dg-text-secondary)] text-xs">
|
||||
@@ -158,7 +158,7 @@ export function HostDetailDialog({
|
||||
<div>
|
||||
<span className="text-sm font-medium text-[var(--dg-text-primary)] flex items-center gap-1.5 mb-2">
|
||||
<Network className="h-4 w-4 dg-text-blue" />
|
||||
端口发现 ({ports.length})
|
||||
{i18n.t('common.portDiscovery', { count: ports.length })}
|
||||
</span>
|
||||
{ports.length === 0 ? (
|
||||
<p className="text-xs dg-text-muted"> {i18n.t('agent.hostDetailDialog.noPortData')} </p>
|
||||
@@ -181,10 +181,10 @@ export function HostDetailDialog({
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white"
|
||||
>
|
||||
<Terminal className="h-4 w-4 mr-1.5" />
|
||||
远程命令
|
||||
{i18n.t('common.remoteCommand')}
|
||||
</Button>
|
||||
<Button variant="outline" onClick={onClose} className="border-[var(--dg-border)] text-[var(--dg-text-secondary)]">
|
||||
关闭
|
||||
{i18n.t('common.close')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -207,7 +207,7 @@ export function AIPredictiveAnalytics() {
|
||||
<div className="flex items-center gap-1">
|
||||
{getTrendIcon(prediction.trend)}
|
||||
<span className={`text-sm ${trendColorClass(prediction.trend)}`}>
|
||||
{prediction.trend === 'up' ? '上升' : prediction.trend === 'down' ? '下降' : '平稳'}
|
||||
{prediction.trend === 'up' ? t('common.trendUp') : prediction.trend === 'down' ? t('common.trendDown') : t('common.trendStable')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,7 @@ export function PredictiveCapacityTab({
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<HardDrive className="h-5 w-5 dg-text-yellow" />
|
||||
容量规划预警
|
||||
{t('aiops.capacity.warningTitle')}
|
||||
</CardTitle>
|
||||
{!loading && forecasts.length > 0 && (
|
||||
<Button
|
||||
@@ -57,7 +57,7 @@ export function PredictiveCapacityTab({
|
||||
) : (
|
||||
<Sparkles className="h-3.5 w-3.5 mr-1" />
|
||||
)}
|
||||
AI 深度解读
|
||||
{t('aiops.capacity.aiInsight')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -156,7 +156,7 @@ export function PredictiveCostTab({
|
||||
<div className="flex items-center gap-1 mt-1 text-xs">
|
||||
{getTrendIcon(summary.trend)}
|
||||
<span className={trendColorClass(summary.trend)}>
|
||||
{summary.trend === 'up' ? '上升' : summary.trend === 'down' ? '下降' : '平稳'}
|
||||
{summary.trend === 'up' ? t('common.trendUp') : summary.trend === 'down' ? t('common.trendDown') : t('common.trendStable')}
|
||||
</span>
|
||||
<span className="dg-text-muted ml-1">{t('aiops.predictivecost.s5o6p7q8r', { pct: summary.confidence })}</span>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@ export function PredictiveCostTab({
|
||||
<div className="flex items-center gap-1 text-sm">
|
||||
{getTrendIcon(dim.trend)}
|
||||
<span className={trendColorClass(dim.trend)}>
|
||||
{dim.trend === 'up' ? '上升' : dim.trend === 'down' ? '下降' : '平稳'}
|
||||
{dim.trend === 'up' ? t('common.trendUp') : dim.trend === 'down' ? t('common.trendDown') : t('common.trendStable')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -278,8 +278,8 @@ export function PredictiveCostTab({
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm dg-text-secondary mt-2">
|
||||
当前 {formatCurrency(risk.current)} / 阈值 {formatCurrency(risk.threshold)}
|
||||
,预测 {formatCurrency(risk.predicted)}
|
||||
{t('aiops.capacity.currentThreshold', { current: formatCurrency(risk.current), threshold: formatCurrency(risk.threshold) })}
|
||||
{t('aiops.capacity.predictedLabel', { predicted: formatCurrency(risk.predicted) })}
|
||||
</p>
|
||||
<div className="flex items-center gap-4 mt-2 text-xs dg-text-muted">
|
||||
<span>{risk.timeframe}</span>
|
||||
|
||||
@@ -80,7 +80,7 @@ export function BillingRecordDialog({ open, onOpenChange, editingRecord, onSucce
|
||||
}
|
||||
onOpenChange(false);
|
||||
onSuccess();
|
||||
toast('保存成功');
|
||||
toast(i18n.t("common.saveSuccess"));
|
||||
} catch (e) {
|
||||
toast.error(i18n.t('kubernetes.msg.saveFailed'), { description: (e as JsonData).message });
|
||||
} finally {
|
||||
|
||||
@@ -71,7 +71,7 @@ export function BillingRuleDialog({ open, onOpenChange, editingRule, onSuccess }
|
||||
}
|
||||
onOpenChange(false);
|
||||
onSuccess();
|
||||
toast('保存成功');
|
||||
toast(i18n.t("common.saveSuccess"));
|
||||
} catch (e) {
|
||||
toast.error(i18n.t('kubernetes.msg.saveFailed'), { description: (e as JsonData).message });
|
||||
} finally {
|
||||
|
||||
@@ -97,9 +97,7 @@ export default function CapacityPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadCapacity} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={exportCSV}>
|
||||
<Download className="h-4 w-4 mr-1" />
|
||||
CSV
|
||||
|
||||
@@ -99,9 +99,7 @@ export default function ClusterOverviewPage() {
|
||||
) : undefined}
|
||||
actions={(
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadData} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
)}
|
||||
>
|
||||
{/* 节点列表 // Logan.Li */}
|
||||
@@ -188,7 +186,7 @@ export default function ClusterOverviewPage() {
|
||||
<Boxes className="h-4 w-4 text-[var(--dg-green)]" />{i18n.t("common.namespaceLabel")}
|
||||
</h2>
|
||||
<Link to={`/kubernetes/${clusterCode}/namespaces`} className="text-sm text-[var(--dg-blue)] hover:text-[var(--dg-blue)] transition-colors flex items-center gap-1">
|
||||
管理全部 <ChevronRight className="h-3 w-3" />
|
||||
{i18n.t('common.manageAll')} <ChevronRight className="h-3 w-3" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
|
||||
@@ -123,7 +123,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
|
||||
return (
|
||||
<Card className="dg-card">
|
||||
<CardContent className="p-8 text-center dg-text-muted">
|
||||
未获取到 manifest 数据,请使用 YAML 编辑模式直接修改。
|
||||
{i18n.t('common.noManifestHint')}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
@@ -136,7 +136,7 @@ export function DeploymentEditContainers({ clusterCode, namespace, deploymentNam
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<KeyRound className="h-4 w-4 dg-text-yellow" />
|
||||
镜像拉取凭证 (imagePullSecrets)
|
||||
{i18n.t('common.imagePullSecrets')}
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] dg-text-muted text-[10px]">{i18n.t('k8s.podLevel')}</Badge>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
@@ -196,7 +196,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
||||
<div>
|
||||
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<Database className="h-4 w-4 dg-text-green" />
|
||||
数据卷
|
||||
{i18n.t('common.volumeDefinition')}
|
||||
<Badge variant="outline" className="border-[var(--dg-border)] dg-text-secondary">{entries.length}</Badge>
|
||||
</CardTitle>
|
||||
<p className="text-[11px] dg-text-muted mt-1"> {i18n.t('kubernetes.storage.volumeDesc')} </p>
|
||||
@@ -210,7 +210,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
||||
{entries.length === 0 ? (
|
||||
<div className="text-center py-10 dg-text-muted text-sm border border-dashed border-[var(--dg-border)] rounded-lg">
|
||||
<Database className="h-8 w-8 mx-auto mb-2 dg-text-muted" />
|
||||
未配置数据卷,点击「添加数据卷」开始
|
||||
{i18n.t('common.noVolumeHint')}
|
||||
</div>
|
||||
) : entries.map((entry, i) => {
|
||||
const vol = entry.volume;
|
||||
@@ -282,7 +282,7 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-[11px] dg-text-muted flex items-center gap-1.5">
|
||||
<Container className="h-3.5 w-3.5 dg-text-blue" />
|
||||
将上述数据卷挂载到容器
|
||||
{i18n.t('common.mountToContainers')}
|
||||
</p>
|
||||
<Button size="sm" variant="ghost" className="h-6 px-2 text-[11px] dg-text-secondary hover:text-[var(--dg-text-primary)]" onClick={() => addMount(i)}>
|
||||
<Plus className="h-3 w-3 mr-0.5" />{i18n.t("common.addMount")}
|
||||
@@ -302,11 +302,11 @@ export function DeploymentEditStorage({ clusterCode, namespace, deploymentName,
|
||||
<div className="flex items-center gap-1 bg-[var(--dg-bg-tertiary)] rounded-lg p-0.5 border border-[var(--dg-border)]">
|
||||
<button type="button" onClick={() => updateMount(i, mi, { readOnly: true })}
|
||||
className={`px-2.5 py-1 rounded text-xs font-medium transition-all ${mount.readOnly ? 'bg-amber-500/20 text-amber-300' : 'dg-text-muted hover:text-[var(--dg-text-secondary)]'}`}>
|
||||
只读
|
||||
{i18n.t('common.readOnly')}
|
||||
</button>
|
||||
<button type="button" onClick={() => updateMount(i, mi, { readOnly: false })}
|
||||
className={`px-2.5 py-1 rounded text-xs font-medium transition-all ${!mount.readOnly ? 'bg-blue-500/20 text-blue-300' : 'dg-text-muted hover:text-[var(--dg-text-secondary)]'}`}>
|
||||
读写
|
||||
{i18n.t('common.readWrite')}
|
||||
</button>
|
||||
</div>
|
||||
<Input
|
||||
|
||||
@@ -221,9 +221,7 @@ export default function EventsPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={() => { loadEvents(); loadSummary(); }} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -101,9 +101,7 @@ export function KubernetesNodes() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadNodes} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -137,9 +137,7 @@ export default function NamespaceManagementPage() {
|
||||
actions={(
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadData} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
<Dialog open={createOpen} onOpenChange={setCreateOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button size="sm" className="dg-btn-primary">
|
||||
|
||||
@@ -199,9 +199,7 @@ export default function NamespaceResourcePage() {
|
||||
<SelectContent className="max-h-[300px]">{namespaces.map((ns) => <SelectItem key={ns} value={ns}>{ns}</SelectItem>)}</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadData} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
{BULK_EXPORT_TYPES.includes(type) && (
|
||||
<>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary text-xs" onClick={handleExportCSV} title={i18n.t('k8s.nsResourcePage.exportCsv')}>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function PodDetailDialog({ pod, onClose }: PodDetailDialogProps) {
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-[var(--dg-border)]">
|
||||
<h3 className="text-base font-semibold text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<Container className="h-4 w-4 text-[var(--dg-blue)]" />
|
||||
Pod 详情
|
||||
{i18n.t('common.podDetail')}
|
||||
</h3>
|
||||
<button className="text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]" onClick={onClose}>✕</button>
|
||||
</div>
|
||||
|
||||
@@ -115,9 +115,7 @@ export default function PodMetricsPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button variant="outline" size="sm" className="dg-btn-secondary" onClick={loadMetrics} disabled={loading}>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />
|
||||
刷新
|
||||
</Button>
|
||||
<RefreshCw className={`h-4 w-4 mr-1 ${loading ? 'animate-spin' : ''}`} />{i18n.t('common.refresh')}</Button>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -182,7 +182,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
||||
<MetaIcon className={`h-5 w-5 shrink-0 ${meta.color}`} />
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-lg font-bold text-[var(--dg-text-primary)] truncate">
|
||||
{isCreate ? `新建 ${meta.title}` : `${meta.title} 编辑`}
|
||||
{isCreate ? i18n.t('common.createNew') + ' ' + meta.title : meta.title + ' ' + i18n.t('common.edit')}
|
||||
</h2>
|
||||
<p
|
||||
className="text-xs text-[var(--dg-text-muted)] truncate"
|
||||
|
||||
@@ -32,22 +32,22 @@ export function HostSettingsCard({
|
||||
<label className="flex items-center gap-2 text-xs text-[var(--dg-text-secondary)] cursor-pointer">
|
||||
<input type="checkbox" checked={hostNetwork} onChange={e => onHostNetworkChange(e.target.checked)}
|
||||
className="accent-blue-500" />
|
||||
hostNetwork(共享主机网络命名空间)
|
||||
{i18n.t('common.hostNetworkDesc')}
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-xs text-[var(--dg-text-secondary)] cursor-pointer">
|
||||
<input type="checkbox" checked={hostIPC} onChange={e => onHostIPCChange(e.target.checked)}
|
||||
className="accent-blue-500" />
|
||||
hostIPC(共享主机 IPC 命名空间)
|
||||
{i18n.t('common.hostIPCDesk')}
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-xs text-[var(--dg-text-secondary)] cursor-pointer">
|
||||
<input type="checkbox" checked={hostPID} onChange={e => onHostPIDChange(e.target.checked)}
|
||||
className="accent-blue-500" />
|
||||
hostPID(共享主机 PID 命名空间)
|
||||
{i18n.t('common.hostPIDDesk')}
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-xs text-[var(--dg-text-secondary)] cursor-pointer">
|
||||
<input type="checkbox" checked={shareProcessNamespace} onChange={e => onShareProcessNamespaceChange(e.target.checked)}
|
||||
className="accent-blue-500" />
|
||||
shareProcessNamespace(共享进程命名空间)
|
||||
{i18n.t('common.shareProcessNamespaceDesc')}
|
||||
</label>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// PodMiscCard.tsx — Pod 级别杂项设置卡片 (Q21 提取)
|
||||
// dnsPolicy / restartPolicy / schedulerName / terminationGracePeriodSeconds / serviceAccountName
|
||||
import { Settings } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
@@ -25,11 +26,12 @@ export function PodMiscCard({
|
||||
terminationGracePeriodSeconds, onTerminationGracePeriodSecondsChange,
|
||||
serviceAccountName, onServiceAccountNameChange,
|
||||
}: PodMiscCardProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card className="dg-card">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<Settings className="h-4 w-4 dg-text-secondary" />Pod 级别
|
||||
<Settings className="h-4 w-4 dg-text-secondary" />{t('common.podLevel')}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0 grid grid-cols-2 gap-3">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// PodSecurityCard.tsx — Pod 安全上下文 (securityContext) 卡片 (Q21 提取)
|
||||
import { Shield } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
@@ -21,11 +22,12 @@ export function PodSecurityCard({
|
||||
podRunAsGroup, onPodRunAsGroupChange,
|
||||
podRunAsNonRoot, onPodRunAsNonRootChange,
|
||||
}: PodSecurityCardProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card className="dg-card">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<Shield className="h-4 w-4 text-rose-400" />Pod 安全上下文 (securityContext)
|
||||
<Shield className="h-4 w-4 text-rose-400" />{t('common.podSecurityContext')}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0 grid grid-cols-2 gap-3">
|
||||
|
||||
@@ -23,7 +23,7 @@ export function PodInstancesTable({
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm text-[var(--dg-text-primary)] flex items-center gap-2">
|
||||
<Activity className="h-4 w-4 text-[var(--dg-green)]" />Pod 实例 ({pods.length})
|
||||
<Activity className="h-4 w-4 text-[var(--dg-green)]" />{i18n.t('common.podInstances', { count: pods.length })}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* ```
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -224,7 +225,7 @@ export function DataTable<T = Record<string, unknown>>(
|
||||
data,
|
||||
rowKey,
|
||||
searchable = false,
|
||||
searchPlaceholder = '搜索...',
|
||||
searchPlaceholder = undefined,
|
||||
searchKeys,
|
||||
sortable: globalSortable = false,
|
||||
pagination: enablePagination = false,
|
||||
@@ -237,7 +238,7 @@ export function DataTable<T = Record<string, unknown>>(
|
||||
exportFilename = 'export.csv',
|
||||
columnToggle = false,
|
||||
loading = false,
|
||||
emptyText = '暂无数据',
|
||||
emptyText = undefined,
|
||||
emptyRender,
|
||||
onRowClick,
|
||||
rowClassName,
|
||||
@@ -247,6 +248,9 @@ export function DataTable<T = Record<string, unknown>>(
|
||||
toolbarExtra,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const effectiveSearchPlaceholder = searchPlaceholder ?? t('common.searchPlaceholder');
|
||||
const effectiveEmptyText = emptyText ?? t('common.noData');
|
||||
// ── Search state ──
|
||||
const [searchTerm, setSearchTerm] = React.useState('');
|
||||
|
||||
@@ -405,7 +409,7 @@ export function DataTable<T = Record<string, unknown>>(
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
placeholder={searchPlaceholder}
|
||||
placeholder={effectiveSearchPlaceholder}
|
||||
className="pl-9 h-9 w-56 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] placeholder:text-[var(--dg-text-muted)]"
|
||||
/>
|
||||
</div>
|
||||
@@ -532,7 +536,7 @@ export function DataTable<T = Record<string, unknown>>(
|
||||
colSpan={visibleColumns.length + (selectable ? 1 : 0)}
|
||||
className="text-center text-[var(--dg-text-muted)] py-16"
|
||||
>
|
||||
{emptyRender ? emptyRender() : emptyText}
|
||||
{emptyRender ? emptyRender() : effectiveEmptyText}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
|
||||
Reference in new issue
Block a user