refactor(i18n): U35 Deploy + Task 模块残余 JSX 硬编码中文清理 — PipelineRunPanel 12处 + CronJobTable 12处 + ExecutionHistoryDialog 10处 + ScrollButtons 2处, 新增 54 翻译键(deploy.runStatus/task.cronJobCol/task.execHistory/deploy.credCol/common.*), zh/en 7565→7616 对齐, tsc -b=0, npm build✓ (17.02s), ESLint=0/43/0, go build=0, 17/17 测试通过

This commit is contained in:
attacker committed 2026-07-12 23:47:22 +08:00
1 parent 0dbb85f5b8
commit 8d64936caf
7 files changed
+163 -43

No files matched your search

+2 -1
View File
@@ -522,7 +522,8 @@ services/api.ts (1324L)
| U31 | AIOps Agent CLI 组件 i18n 修复(AgentCLI + AgentCLIToolbar + AgentCLIHistory + AgentCLIEmptyState + ChatInputArea + agent-cli-constants.ts 6 文件 — Q7/Q35 拆分后丢失的 i18n 集成全部修复,新增 `aiops.cli.*` 命名空间 100 翻译键(8 子分类: toolbar/safety/tips/error/info/viewMode/placeholder/history/empty/input),SAFETY_MODES+TIPS_HOST+TIPS_KUBERNETES 常量数组改为 `getSafetyModes(t)`/`getTipsHost(t)`/`getTipsKubernetes(t)` 接收 TFunction 参数的工厂函数,全部 UI 硬编码中文清零,zh/en 7381→7481 +100 键对齐,tsc -b=0,npm build✓ (18.21s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** | | U31 | AIOps Agent CLI 组件 i18n 修复(AgentCLI + AgentCLIToolbar + AgentCLIHistory + AgentCLIEmptyState + ChatInputArea + agent-cli-constants.ts 6 文件 — Q7/Q35 拆分后丢失的 i18n 集成全部修复,新增 `aiops.cli.*` 命名空间 100 翻译键(8 子分类: toolbar/safety/tips/error/info/viewMode/placeholder/history/empty/input),SAFETY_MODES+TIPS_HOST+TIPS_KUBERNETES 常量数组改为 `getSafetyModes(t)`/`getTipsHost(t)`/`getTipsKubernetes(t)` 接收 TFunction 参数的工厂函数,全部 UI 硬编码中文清零,zh/en 7381→7481 +100 键对齐,tsc -b=0,npm build✓ (18.21s),ESLint=0/53/0,go build=0,17/17 测试通过) | P3 | **done** |
| U32 | 全量 hash key 清理 + React Hooks 警告修复(151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key — monitor 58/kubernetes 42/aiops 17/agent 12/cloud 13/pipeline 6/system 3;58 个源文件 147 处 t()/i18n.t() 调用更新;3 个命名空间冲突修复(monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult 原 string → .title 子键);React Hooks 警告 53→43 (-10): exhaustive-deps 4→0 + preserve-manual-memoization 5→0 + 变量遮蔽修复(filter(t)→filter(tg));zh/en 7480=7480 完全对齐 0 hash key 残留;tsc -b=0,npm build✓ (17.59s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | | U32 | 全量 hash key 清理 + React Hooks 警告修复(151 个 hash-style flat key (s+hex 后缀) 全部重命名为语义化 nested key — monitor 58/kubernetes 42/aiops 17/agent 12/cloud 13/pipeline 6/system 3;58 个源文件 147 处 t()/i18n.t() 调用更新;3 个命名空间冲突修复(monitor.hostMonitor/agent.agentMonitor/aiops.aiAnalysisResult 原 string → .title 子键);React Hooks 警告 53→43 (-10): exhaustive-deps 4→0 + preserve-manual-memoization 5→0 + 变量遮蔽修复(filter(t)→filter(tg));zh/en 7480=7480 完全对齐 0 hash key 残留;tsc -b=0,npm build✓ (17.59s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** |
| U33 | K8s 资源编辑器 + Pod 日志/Metrics i18n 补全(resource-visual-editors/ 5 文件: ConfigMap/Secret/Service/Ingress/PvcEditor 按钮文案+帮助文本+访问模式+IngressClass 提示全部 i18n;pod-logs-viewer/ 3 文件: ControlBar 导出/刷新/实时/停止+LogContentArea 加载中+LogsHeader 标题;PodLogsViewer 6 处错误消息+DialogTitle/Description;PodMetricsPage subtitle+表头+所有命名空间;17 处 toast description 模板字面量中文→i18n.t() 插值 (DeploymentEditAdvanced/Containers/Storage/View/Detail/Service/ResourceVisualEditor/NamespaceResourcePage/ClusterOverviewPage);5 处 confirm description (DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection);5 处 subtitle 模板字面量 (PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage);新增 k8s.rve 14键+k8s.toast 18键+k8s.podMetrics 4键+k8s.podLogs 13键 共 49 翻译键;zh/en 7481→7524 +43 对齐;tsc -b=0,npm build✓ (17.76s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | | U33 | K8s 资源编辑器 + Pod 日志/Metrics i18n 补全(resource-visual-editors/ 5 文件: ConfigMap/Secret/Service/Ingress/PvcEditor 按钮文案+帮助文本+访问模式+IngressClass 提示全部 i18n;pod-logs-viewer/ 3 文件: ControlBar 导出/刷新/实时/停止+LogContentArea 加载中+LogsHeader 标题;PodLogsViewer 6 处错误消息+DialogTitle/Description;PodMetricsPage subtitle+表头+所有命名空间;17 处 toast description 模板字面量中文→i18n.t() 插值 (DeploymentEditAdvanced/Containers/Storage/View/Detail/Service/ResourceVisualEditor/NamespaceResourcePage/ClusterOverviewPage);5 处 confirm description (DeploymentView/EditService/Detail/WorkloadViewPage/MiddlewareMonitor/SiteMonitor/HealthProbeSection);5 处 subtitle 模板字面量 (PodMetricsPage/CapacityPage/EventsPage/KubernetesNodes/ClusterOverviewPage);新增 k8s.rve 14键+k8s.toast 18键+k8s.podMetrics 4键+k8s.podLogs 13键 共 49 翻译键;zh/en 7481→7524 +43 对齐;tsc -b=0,npm build✓ (17.76s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** |
| U34 | System & User 模块 i18n 清理(ProfilePage + ResetPasswordDialog + SystemRolesTab + LdapManagementSection 4 文件 — ProfilePage 从零接入 useTranslation + ResetPasswordDialog 从零接入 useTranslation + SystemRolesTab 权限分组标签(云资源/自动化/审批/审计)+toast+confirm description+角色计数+空状态+权限列表计数+编码标签+尾部文案全部 i18n + LdapManagementSection 验证按钮+根OU标签+创建按钮+导入按钮+编辑用户描述全部 i18n;新增 user.profile.* 14键+user.resetPwd.* 11键+system.* 16键 共 41 翻译键;UI 硬编码中文 ProfilePage 15→0 + ResetPasswordDialog 11→0 + SystemRolesTab 12→0 + LdapManagementSection 8→0;zh/en 7524→7565 +41 对齐;tsc -b=0,npm build✓ (16.05s),ESLint=0/43/any=0,go build=0,17/17 测试通过) | P3 | **done** | | U34 | System & User 模块 i18n 清理(ProfilePage + ResetPasswordDialog + SystemRolesTab + LdapManagementSection 4 文件 — ProfilePage 从零接入 useTranslation + ResetPasswordDialog 从零接入 useTranslation + SystemRolesTab 权限分组标签(云资源/自动化/审批/审计)+toast+confirm description+角色计数+空状态+权限列表计数+编码标签+尾部文案全部 i18n + LdapManagementSection 验证按钮+根OU标签+创建按钮+导入按钮+编辑用户描述全部 i18n;新增 user.profile.* 14键+user.resetPwd.* 11键+system.* 16键 共 41 翻译键;UI 硬编码中文 ProfilePage 15→0 + ResetPasswordDialog 11→0 + SystemRolesTab 12→0 + LdapManagementSection 8→0;zh/en 7524→7565 +41 对齐;tsc -b=0, npm build✓ (16.05s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
| U35 | Deploy + Task 模块残余 JSX 硬编码中文清理(PipelineRunPanel 12处 + CronJobTable 12处 + ExecutionHistoryDialog 10处 + ScrollButtons 2处 — 新增 deploy.runStatus.* 12键 + task.cronJobCol.* 14键 + task.execHistory.* 14键 + deploy.credCol.* 9键 + common.scrollToTop/scrollToBottom/success/failed/total 5键 共 54 翻译键;zh/en 7565→7616 +51 对齐;tsc -b=0, npm build✓ (17.02s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
--- ---
+3 -2
View File
@@ -1,6 +1,7 @@
// ScrollButtons.tsx — 浮动滚动按钮(回到顶部/底部),AgentCLI 对话区复用 // ScrollButtons.tsx — 浮动滚动按钮(回到顶部/底部),AgentCLI 对话区复用
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ChevronUp, ChevronDown } from 'lucide-react'; import { ChevronUp, ChevronDown } from 'lucide-react';
import i18n from '@/i18n';
export interface ScrollButtonsProps { export interface ScrollButtonsProps {
show: boolean; show: boolean;
@@ -21,7 +22,7 @@ export default function ScrollButtons({ show, atTop, atBottom, onScrollTop, onSc
size="icon" size="icon"
className="h-8 w-8 rounded-full shadow-md border border-[var(--dg-border)] bg-[var(--surface-raised)]/95 opacity-90 hover:opacity-100" className="h-8 w-8 rounded-full shadow-md border border-[var(--dg-border)] bg-[var(--surface-raised)]/95 opacity-90 hover:opacity-100"
onClick={onScrollTop} onClick={onScrollTop}
title="回到顶部" title={i18n.t('common.scrollToTop')}
> >
<ChevronUp className="h-4 w-4" /> <ChevronUp className="h-4 w-4" />
</Button> </Button>
@@ -33,7 +34,7 @@ export default function ScrollButtons({ show, atTop, atBottom, onScrollTop, onSc
size="icon" size="icon"
className="h-8 w-8 rounded-full shadow-md border border-[var(--dg-border)] bg-[var(--surface-raised)]/95 opacity-90 hover:opacity-100" className="h-8 w-8 rounded-full shadow-md border border-[var(--dg-border)] bg-[var(--surface-raised)]/95 opacity-90 hover:opacity-100"
onClick={onScrollBottom} onClick={onScrollBottom}
title="回到底部" title={i18n.t('common.scrollToBottom')}
> >
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</Button> </Button>
@@ -74,7 +74,7 @@ function PipelineQueuePanel({ pipeline }: { pipeline: Record<string, unknown> })
<span className="font-bold text-[var(--dg-text-primary)]">{running}/{max}</span> <span className="font-bold text-[var(--dg-text-primary)]">{running}/{max}</span>
{queued > 0 && ( {queued > 0 && (
<span className="text-[var(--dg-text-secondary)] ml-2"> <span className="text-[var(--dg-text-secondary)] ml-2">
构建队列 <span className="font-bold dg-text-yellow">({queued})</span> {i18n.t('deploy.runStatus.buildQueue')} <span className="font-bold dg-text-yellow">({queued})</span>
</span> </span>
)} )}
</div> </div>
@@ -90,7 +90,7 @@ function PipelineQueuePanel({ pipeline }: { pipeline: Record<string, unknown> })
<span className="text-[var(--dg-text-secondary)] opacity-50">{String(run.triggered_by || '-')}</span> <span className="text-[var(--dg-text-secondary)] opacity-50">{String(run.triggered_by || '-')}</span>
</div> </div>
<Button variant="ghost" size="sm" className="h-6 text-xs dg-text-red hover:dg-text-red" onClick={() => handleCancel(run.id as number)}> <Button variant="ghost" size="sm" className="h-6 text-xs dg-text-red hover:dg-text-red" onClick={() => handleCancel(run.id as number)}>
<XCircle className="h-3 w-3 mr-1" />取消 <XCircle className="h-3 w-3 mr-1" />{i18n.t('deploy.runStatus.cancel')}
</Button> </Button>
</div> </div>
))} ))}
@@ -106,7 +106,7 @@ function PipelineQueuePanel({ pipeline }: { pipeline: Record<string, unknown> })
<span className="text-[var(--dg-text-muted)] font-mono">Run #{id}</span> <span className="text-[var(--dg-text-muted)] font-mono">Run #{id}</span>
</div> </div>
<Button variant="ghost" size="sm" className="h-6 text-xs dg-text-red hover:dg-text-red" onClick={() => handleCancel(id)}> <Button variant="ghost" size="sm" className="h-6 text-xs dg-text-red hover:dg-text-red" onClick={() => handleCancel(id)}>
<XCircle className="h-3 w-3 mr-1" />取消 <XCircle className="h-3 w-3 mr-1" />{i18n.t('deploy.runStatus.cancel')}
</Button> </Button>
</div> </div>
))} ))}
@@ -163,19 +163,19 @@ export function PipelineRunPanel({ pipeline, pipelineRuns, isLoadingRuns, onStop
<td className="px-4 py-2"> <td className="px-4 py-2">
{r.status === 'success' ? ( {r.status === 'success' ? (
<Badge className="bg-emerald-500/20 dg-text-green border-emerald-500/30 text-[11px]"> <Badge className="bg-emerald-500/20 dg-text-green border-emerald-500/30 text-[11px]">
<CheckCircle className="h-3 w-3 mr-0.5" />成功 <CheckCircle className="h-3 w-3 mr-0.5" />{i18n.t('deploy.runStatus.success')}
</Badge> </Badge>
) : r.status === 'failed' ? ( ) : r.status === 'failed' ? (
<Badge className="bg-red-500/20 dg-text-red border-red-500/30 text-[11px]"> <Badge className="bg-red-500/20 dg-text-red border-red-500/30 text-[11px]">
<XCircle className="h-3 w-3 mr-0.5" />失败 <XCircle className="h-3 w-3 mr-0.5" />{i18n.t('deploy.runStatus.failed')}
</Badge> </Badge>
) : r.status === 'running' ? ( ) : r.status === 'running' ? (
<Badge className="bg-blue-500/20 dg-text-blue border-blue-500/30 text-[11px]"> <Badge className="bg-blue-500/20 dg-text-blue border-blue-500/30 text-[11px]">
<Loader2 className="h-3 w-3 animate-spin mr-0.5" />执行中 <Loader2 className="h-3 w-3 animate-spin mr-0.5" />{i18n.t('deploy.runStatus.running')}
</Badge> </Badge>
) : r.status === 'cancelled' ? ( ) : r.status === 'cancelled' ? (
<Badge className="bg-yellow-500/20 dg-text-yellow border-yellow-500/30 text-[11px]"> <Badge className="bg-yellow-500/20 dg-text-yellow border-yellow-500/30 text-[11px]">
<StopCircle className="h-3 w-3 mr-0.5" />已停止 <StopCircle className="h-3 w-3 mr-0.5" />{i18n.t('deploy.runStatus.cancelled')}
</Badge> </Badge>
) : ( ) : (
<Badge variant="secondary" className="text-[11px]">{String(r.status)}</Badge> <Badge variant="secondary" className="text-[11px]">{String(r.status)}</Badge>
@@ -189,11 +189,11 @@ export function PipelineRunPanel({ pipeline, pipelineRuns, isLoadingRuns, onStop
<td className="px-4 py-2 text-right"> <td className="px-4 py-2 text-right">
<div className="flex items-center justify-end gap-1"> <div className="flex items-center justify-end gap-1">
<Button variant="ghost" size="sm" className="h-7 text-xs dg-text-blue hover:text-blue-300" onClick={() => handleViewLog(r)}> <Button variant="ghost" size="sm" className="h-7 text-xs dg-text-blue hover:text-blue-300" onClick={() => handleViewLog(r)}>
<FileText className="h-3.5 w-3.5 mr-1" />日志 <FileText className="h-3.5 w-3.5 mr-1" />{i18n.t('deploy.runStatus.log')}
</Button> </Button>
{(r.status === 'running' || r.status === 'pending') && ( {(r.status === 'running' || r.status === 'pending') && (
<Button variant="ghost" size="sm" className="h-7 text-xs dg-text-red hover:dg-text-red" onClick={() => onStopRun(r.id as number | string)}> <Button variant="ghost" size="sm" className="h-7 text-xs dg-text-red hover:dg-text-red" onClick={() => onStopRun(r.id as number | string)}>
<StopCircle className="h-3.5 w-3.5 mr-1" />停止 <StopCircle className="h-3.5 w-3.5 mr-1" />{i18n.t('deploy.runStatus.stop')}
</Button> </Button>
)} )}
</div> </div>
@@ -208,14 +208,14 @@ export function PipelineRunPanel({ pipeline, pipelineRuns, isLoadingRuns, onStop
<DialogContent className="max-w-3xl max-h-[85vh] overflow-y-auto bg-[var(--dg-bg-primary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]"> <DialogContent className="max-w-3xl max-h-[85vh] overflow-y-auto bg-[var(--dg-bg-primary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center gap-2"> <DialogTitle className="flex items-center gap-2">
<FileText className="h-5 w-5" />执行日志 #{String(logRun?.build_number ?? logRun?.id ?? '')} <FileText className="h-5 w-5" />{i18n.t('deploy.runStatus.execLog', { number: String(logRun?.build_number ?? logRun?.id ?? '') })}
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-3"> <div className="space-y-3">
<div className="flex flex-wrap gap-3 text-sm text-[var(--dg-text-secondary)]"> <div className="flex flex-wrap gap-3 text-sm text-[var(--dg-text-secondary)]">
<span>触发方式: {String(logRun?.triggered_by || 'manual')}</span> <span>{i18n.t('deploy.runStatus.triggerMethod')} {String(logRun?.triggered_by || 'manual')}</span>
<span>分支: {String(logRun?.branch || '-')}</span> <span>{i18n.t('deploy.runStatus.branch')} {String(logRun?.branch || '-')}</span>
<span>耗时: {fmtDuration(logRun?.duration as number | undefined)}</span> <span>{i18n.t('deploy.runStatus.duration')} {fmtDuration(logRun?.duration as number | undefined)}</span>
</div> </div>
<div className="bg-[var(--dg-bg-primary)] rounded-md p-3 max-h-[50vh] overflow-y-auto font-mono text-xs"> <div className="bg-[var(--dg-bg-primary)] rounded-md p-3 max-h-[50vh] overflow-y-auto font-mono text-xs">
{logs.length === 0 ? ( {logs.length === 0 ? (
+13 -13
View File
@@ -48,14 +48,14 @@ export function CronJobTable({
className="border-[var(--dg-border)]" className="border-[var(--dg-border)]"
/> />
</TableHead> </TableHead>
<TableHead className="dg-text-secondary text-xs min-w-[140px]">任务</TableHead> <TableHead className="dg-text-secondary text-xs min-w-[140px]">{t('task.cronJobCol.task')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[120px] hidden lg:table-cell">调度</TableHead> <TableHead className="dg-text-secondary text-xs w-[120px] hidden lg:table-cell">{t('task.cronJobCol.schedule')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[88px]">类型</TableHead> <TableHead className="dg-text-secondary text-xs w-[88px]">{t('task.cronJobCol.type')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[72px] hidden sm:table-cell">开关</TableHead> <TableHead className="dg-text-secondary text-xs w-[72px] hidden sm:table-cell">{t('task.cronJobCol.switch')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[72px]">最近</TableHead> <TableHead className="dg-text-secondary text-xs w-[72px]">{t('task.cronJobCol.recent')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[100px] hidden md:table-cell">上次执行</TableHead> <TableHead className="dg-text-secondary text-xs w-[100px] hidden md:table-cell">{t('task.cronJobCol.lastExec')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[96px] hidden md:table-cell">成功率</TableHead> <TableHead className="dg-text-secondary text-xs w-[96px] hidden md:table-cell">{t('task.cronJobCol.successRate')}</TableHead>
<TableHead className="dg-text-secondary text-xs w-[148px] text-right">操作</TableHead> <TableHead className="dg-text-secondary text-xs w-[148px] text-right">{t('task.cronJobCol.actions')}</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@@ -122,7 +122,7 @@ export function CronJobTable({
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-7 w-7 dg-text-green hover:bg-emerald-500/10" className="h-7 w-7 dg-text-green hover:bg-emerald-500/10"
title="立即执行" title={t('task.cronJobCol.execNow')}
disabled={isExecuting} disabled={isExecuting}
onClick={() => onExecute(job.id)} onClick={() => onExecute(job.id)}
> >
@@ -136,7 +136,7 @@ export function CronJobTable({
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-7 w-7" className="h-7 w-7"
title="执行历史" title={t('task.cronJobCol.execHistory')}
onClick={() => onHistory(job)} onClick={() => onHistory(job)}
> >
<History className="h-3.5 w-3.5" /> <History className="h-3.5 w-3.5" />
@@ -145,7 +145,7 @@ export function CronJobTable({
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-7 w-7" className="h-7 w-7"
title={job.enabled ? '禁用' : '启用'} title={job.enabled ? t('task.cronJobCol.disable') : t('task.cronJobCol.enable')}
onClick={() => onToggle(job.id)} onClick={() => onToggle(job.id)}
> >
{job.enabled ? <Pause className="h-3.5 w-3.5" /> : <Play className="h-3.5 w-3.5" />} {job.enabled ? <Pause className="h-3.5 w-3.5" /> : <Play className="h-3.5 w-3.5" />}
@@ -155,7 +155,7 @@ export function CronJobTable({
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-7 w-7" className="h-7 w-7"
title="编辑" title={t('task.cronJobCol.edit')}
onClick={() => onEdit(job)} onClick={() => onEdit(job)}
> >
<Edit className="h-3.5 w-3.5" /> <Edit className="h-3.5 w-3.5" />
@@ -166,7 +166,7 @@ export function CronJobTable({
variant="ghost" variant="ghost"
size="icon" size="icon"
className="h-7 w-7" className="h-7 w-7"
title="删除" title={t('task.cronJobCol.delete')}
onClick={() => onDelete(job)} onClick={() => onDelete(job)}
> >
<Trash2 className="h-3.5 w-3.5 dg-text-red" /> <Trash2 className="h-3.5 w-3.5 dg-text-red" />
@@ -118,7 +118,7 @@ export function ExecutionHistoryDialog({
[executions, viewingExecId], [executions, viewingExecId],
); );
const title = taskName ? `执行历史 — ${taskName}` : '全部执行历史'; const title = taskName ? `${t('task.execHistory.title')} — ${taskName}` : t('task.execHistory.title');
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
@@ -126,7 +126,7 @@ export function ExecutionHistoryDialog({
<DialogHeader className="shrink-0"> <DialogHeader className="shrink-0">
<DialogTitle>{title}</DialogTitle> <DialogTitle>{title}</DialogTitle>
<DialogDescription className="text-[var(--dg-text-secondary)]"> <DialogDescription className="text-[var(--dg-text-secondary)]">
近 7 天趋势与执行日志(最近 100 条) {t('task.execHistory.recentTrend', { defaultValue: '近 7 天趋势与执行日志(最近 100 条)' })}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -137,7 +137,7 @@ export function ExecutionHistoryDialog({
) : executions.length === 0 ? ( ) : executions.length === 0 ? (
<div className="flex flex-col items-center justify-center py-16 text-[var(--dg-text-secondary)]"> <div className="flex flex-col items-center justify-center py-16 text-[var(--dg-text-secondary)]">
<History className="h-10 w-10 mb-3 opacity-40" /> <History className="h-10 w-10 mb-3 opacity-40" />
<p className="text-sm">暂无执行记录</p> <p className="text-sm">{t('task.execHistory.noRecords', { defaultValue: '暂无执行记录' })}</p>
</div> </div>
) : ( ) : (
<div className="flex flex-col gap-4 min-h-0 flex-1 overflow-y-auto mt-1"> <div className="flex flex-col gap-4 min-h-0 flex-1 overflow-y-auto mt-1">
@@ -146,27 +146,27 @@ export function ExecutionHistoryDialog({
<CheckCircle className="h-7 w-7 text-emerald-500 shrink-0" /> <CheckCircle className="h-7 w-7 text-emerald-500 shrink-0" />
<div> <div>
<div className="text-xl font-semibold text-emerald-500 tabular-nums">{stats.success}</div> <div className="text-xl font-semibold text-emerald-500 tabular-nums">{stats.success}</div>
<div className="text-[11px] dg-text-secondary">成功 · {stats.successRate}%</div> <div className="text-[11px] dg-text-secondary">{t('common.success', { defaultValue: '成功' })} · {stats.successRate}%</div>
</div> </div>
</div> </div>
<div className="rounded-lg border border-red-500/20 bg-red-500/5 px-3 py-2.5 flex items-center gap-2.5"> <div className="rounded-lg border border-red-500/20 bg-red-500/5 px-3 py-2.5 flex items-center gap-2.5">
<XCircle className="h-7 w-7 text-red-500 shrink-0" /> <XCircle className="h-7 w-7 text-red-500 shrink-0" />
<div> <div>
<div className="text-xl font-semibold text-red-500 tabular-nums">{stats.failed}</div> <div className="text-xl font-semibold text-red-500 tabular-nums">{stats.failed}</div>
<div className="text-[11px] dg-text-secondary">失败 · {stats.failedRate}%</div> <div className="text-[11px] dg-text-secondary">{t('common.failed', { defaultValue: '失败' })} · {stats.failedRate}%</div>
</div> </div>
</div> </div>
<div className="rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] px-3 py-2.5 flex items-center gap-2.5"> <div className="rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] px-3 py-2.5 flex items-center gap-2.5">
<BarChart3 className="h-7 w-7 dg-text-blue shrink-0" /> <BarChart3 className="h-7 w-7 dg-text-blue shrink-0" />
<div> <div>
<div className="text-xl font-semibold tabular-nums">{stats.total}</div> <div className="text-xl font-semibold tabular-nums">{stats.total}</div>
<div className="text-[11px] dg-text-secondary">总计</div> <div className="text-[11px] dg-text-secondary">{t('common.total', { defaultValue: '总计' })}</div>
</div> </div>
</div> </div>
</div> </div>
<div className="rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] px-3 py-3 shrink-0"> <div className="rounded-lg border border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] px-3 py-3 shrink-0">
<div className="text-xs font-medium mb-2 dg-text-secondary">最近 7 天</div> <div className="text-xs font-medium mb-2 dg-text-secondary">{t('task.execHistory.last7days', { defaultValue: '最近 7 天' })}</div>
<div className="flex items-end justify-between gap-1.5 h-24"> <div className="flex items-end justify-between gap-1.5 h-24">
{trend.map((item) => { {trend.map((item) => {
const heightPct = item.total > 0 ? Math.max((item.total / trendMax) * 100, 12) : 4; const heightPct = item.total > 0 ? Math.max((item.total / trendMax) * 100, 12) : 4;
@@ -197,10 +197,10 @@ export function ExecutionHistoryDialog({
<TableHeader> <TableHeader>
<TableRow className="border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] hover:bg-[var(--dg-bg-secondary)]"> <TableRow className="border-[var(--dg-border)] bg-[var(--dg-bg-secondary)] hover:bg-[var(--dg-bg-secondary)]">
<TableHead className="text-[var(--dg-text-secondary)] text-xs w-8" /> <TableHead className="text-[var(--dg-text-secondary)] text-xs w-8" />
{!taskId && <TableHead className="text-[var(--dg-text-secondary)] text-xs">任务</TableHead>} {!taskId && <TableHead className="text-[var(--dg-text-secondary)] text-xs">{t('task.cronJobCol.task')}</TableHead>}
<TableHead className="text-[var(--dg-text-secondary)] text-xs w-[72px]">状态</TableHead> <TableHead className="text-[var(--dg-text-secondary)] text-xs w-[72px]">{t('task.execHistory.status')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-xs w-[80px]">耗时</TableHead> <TableHead className="text-[var(--dg-text-secondary)] text-xs w-[80px]">{t('task.execHistory.duration')}</TableHead>
<TableHead className="text-[var(--dg-text-secondary)] text-xs">执行时间</TableHead> <TableHead className="text-[var(--dg-text-secondary)] text-xs">{t('task.execHistory.startTime')}</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@@ -256,7 +256,7 @@ export function ExecutionHistoryDialog({
</div> </div>
{viewingExec && !viewingExec.output && ( {viewingExec && !viewingExec.output && (
<p className="text-xs dg-text-muted text-center py-2">该记录无输出日志</p> <p className="text-xs dg-text-muted text-center py-2">{t('task.execHistory.noOutput')}</p>
)} )}
</div> </div>
)} )}
+60 -1
View File
@@ -514,7 +514,9 @@
"request.tokenExpired": "Session expired, please log in again", "request.tokenExpired": "Session expired, please log in again",
"days": "days", "days": "days",
"seconds": "sec", "seconds": "sec",
"copyManually": "Please manually select and copy" "copyManually": "Please manually select and copy",
"scrollToTop": "Scroll to top",
"scrollToBottom": "Scroll to bottom"
}, },
"menu": { "menu": {
"dashboard": "Dashboard", "dashboard": "Dashboard",
@@ -2381,6 +2383,31 @@
"namePlaceholder": "e.g., Backend Team", "namePlaceholder": "e.g., Backend Team",
"subtitle": "User groups - batch authorize pipeline access", "subtitle": "User groups - batch authorize pipeline access",
"memberCount": "Total Members" "memberCount": "Total Members"
},
"runStatus": {
"success": "Success",
"failed": "Failed",
"running": "Running",
"cancelled": "Stopped",
"cancel": "Cancel",
"log": "Log",
"stop": "Stop",
"execLog": "Execution Log #{{number}}",
"triggerMethod": "Trigger:",
"branch": "Branch:",
"duration": "Duration:",
"buildQueue": "Build Queue"
},
"credCol": {
"name": "Credential Name",
"type": "Type",
"tags": "Tags",
"actions": "Actions",
"used": "Used",
"unused": "Unused",
"copyRef": "Copy Reference ID",
"testConn": "Test Connection",
"noTags": "No tags"
} }
}, },
"k8s": { "k8s": {
@@ -5218,6 +5245,38 @@
"toastExecConfirm": "Confirm batch execution", "toastExecConfirm": "Confirm batch execution",
"toastExecConfirmDesc": "This will execute the command on {{count}} hosts. Please verify the command is safe:\n\n{{command}}", "toastExecConfirmDesc": "This will execute the command on {{count}} hosts. Please verify the command is safe:\n\n{{command}}",
"noOutput": "(no output)" "noOutput": "(no output)"
},
"cronJobCol": {
"task": "Task",
"schedule": "Schedule",
"type": "Type",
"switch": "Toggle",
"recent": "Recent",
"lastExec": "Last Run",
"successRate": "Success Rate",
"actions": "Actions",
"execNow": "Execute Now",
"execHistory": "Run History",
"disable": "Disable",
"enable": "Enable",
"edit": "Edit",
"delete": "Delete"
},
"execHistory": {
"title": "Execution History",
"exitCode": "Exit Code",
"output": "Output",
"duration": "Duration",
"startTime": "Start Time",
"endTime": "End Time",
"target": "Target",
"status": "Status",
"noOutput": "No output",
"copyOutput": "Copy Output",
"copied": "Copied to clipboard",
"recentTrend": "Last 7 days trends and execution logs (latest 100)",
"noRecords": "No execution records",
"last7days": "Last 7 days"
} }
}, },
"rbac": { "rbac": {
+60 -1
View File
@@ -514,7 +514,9 @@
"request.tokenExpired": "登录已过期,请重新登录", "request.tokenExpired": "登录已过期,请重新登录",
"days": "天", "days": "天",
"seconds": "秒", "seconds": "秒",
"copyManually": "请手动选中内容后复制" "copyManually": "请手动选中内容后复制",
"scrollToTop": "回到顶部",
"scrollToBottom": "回到底部"
}, },
"menu": { "menu": {
"dashboard": "仪表盘", "dashboard": "仪表盘",
@@ -2381,6 +2383,31 @@
"namePlaceholder": "例如: 后端开发组", "namePlaceholder": "例如: 后端开发组",
"subtitle": "用户组 — 批量授权流水线访问权限", "subtitle": "用户组 — 批量授权流水线访问权限",
"memberCount": "成员总数" "memberCount": "成员总数"
},
"runStatus": {
"success": "成功",
"failed": "失败",
"running": "执行中",
"cancelled": "已停止",
"cancel": "取消",
"log": "日志",
"stop": "停止",
"execLog": "执行日志 #{{number}}",
"triggerMethod": "触发方式:",
"branch": "分支:",
"duration": "耗时:",
"buildQueue": "构建队列"
},
"credCol": {
"name": "凭据名称",
"type": "类型",
"tags": "标签",
"actions": "操作",
"used": "已使用",
"unused": "未使用",
"copyRef": "复制引用ID",
"testConn": "测试连接",
"noTags": "无标签"
} }
}, },
"k8s": { "k8s": {
@@ -5218,6 +5245,38 @@
"toastExecConfirm": "确认批量执行命令", "toastExecConfirm": "确认批量执行命令",
"toastExecConfirmDesc": "将在 {{count}} 台主机上执行该命令,请确认命令安全:\n\n{{command}}", "toastExecConfirmDesc": "将在 {{count}} 台主机上执行该命令,请确认命令安全:\n\n{{command}}",
"noOutput": "(无输出)" "noOutput": "(无输出)"
},
"cronJobCol": {
"task": "任务",
"schedule": "调度",
"type": "类型",
"switch": "开关",
"recent": "最近",
"lastExec": "上次执行",
"successRate": "成功率",
"actions": "操作",
"execNow": "立即执行",
"execHistory": "执行历史",
"disable": "禁用",
"enable": "启用",
"edit": "编辑",
"delete": "删除"
},
"execHistory": {
"title": "执行历史",
"exitCode": "退出码",
"output": "输出",
"duration": "耗时",
"startTime": "开始时间",
"endTime": "结束时间",
"target": "目标",
"status": "状态",
"noOutput": "暂无输出",
"copyOutput": "复制输出",
"copied": "已复制到剪贴板",
"recentTrend": "近 7 天趋势与执行日志(最近 100 条)",
"noRecords": "暂无执行记录",
"last7days": "最近 7 天"
} }
}, },
"rbac": { "rbac": {