refactor(i18n): U40 Monitor + Agent 子组件残余硬编码中文 i18n 补全 — 14 文件 ~50 处→0
- ContextSwitchCard: CTX/IRQ/次 → t() - ProcessTable: agentWaitData/cpuUsagePercent → t() - FDUsageCard: fdHandles → t() - PortMonitorTab: 黑名单/白名单/策略配置/暂无端口数据 → t() - PortPolicyDialog: 白名单全量上报/黑名单仅上报/端口列表/逗号分隔/白名单无需配置/已输入端口 → t() - HostTimeRangePicker: 时间范围/自定义/取消/应用 → t() - DualLineChart: 接收/发送 → t(), i18n.t→useTranslation hook - ZoomableAreaChart: 暂无数据 → t() - SiteResultsPanel: 选择任务/次/可用率/立即拨测/SSL证书预警/到期/剩余/阈值/天 → t() - MiddlewareMonitor: Redis单机集群/上次更新/添加平台默认/暂无实例 → t() - SiteMonitor: subtitle → t() - SNMPMonitor: 标题/采集间隔/IP地址/OID模板/SNMP版本/确认/暂无设备/采集指标/暂无指标 → t() - HealthProbeSection: TCP目标地址/TCP端口 → t() - AgentAlerts: Agent告警/刷新/未恢复/已恢复/恢复/新建规则/创建第一条规则 → i18n.t() 新增 monitor.lbl 46 语义化翻译键 + agent.alerts 6 键 zh/en 7762→7814 对齐 tsc -b=0, npm build✓ (16.88s), ESLint=0/43/any=0, go build=0, 17/17 测试通过
This commit is contained in:
1 parent
70a7c2907c
commit
4e2ba77aa8
17 files changed
+165
-61
No files matched your search
@@ -153,10 +153,10 @@ export function MiddlewareMonitor() {
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold"> {t('monitor.alertHelper.srcMiddleware')} </h2>
|
||||
<p className="text-xs text-[var(--dg-text-muted)] mt-0.5">
|
||||
Redis 单机/集群 · MySQL · 30s 自动刷新
|
||||
{t('monitor.lbl.redisMysqlAutoRefresh')}
|
||||
{lastRefreshAt && (
|
||||
<span className="ml-2">
|
||||
上次更新 {lastRefreshAt.toLocaleTimeString()}
|
||||
{t('monitor.lbl.lastUpdate')} {lastRefreshAt.toLocaleTimeString()}
|
||||
{refreshing && t('monitor.lbl.refreshing')}
|
||||
</span>
|
||||
)}
|
||||
@@ -172,7 +172,7 @@ export function MiddlewareMonitor() {
|
||||
{targets.length === 0 && (
|
||||
<Button variant="outline" size="sm" onClick={handleSeedDefaults} disabled={seeding} className="gap-1.5">
|
||||
{seeding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Database className="h-4 w-4" />}
|
||||
添加平台默认
|
||||
{t('monitor.lbl.addPlatformDefault')}
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" onClick={() => openCreate()} className="gap-1.5">
|
||||
@@ -252,7 +252,7 @@ export function MiddlewareMonitor() {
|
||||
{currentTargets.length === 0 ? (
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="p-8 text-center text-sm text-[var(--dg-text-muted)]">
|
||||
暂无 {tab === 'redis' ? 'Redis' : 'MySQL'} 实例,
|
||||
{t('monitor.lbl.noInstancePrefix')} {tab === 'redis' ? 'Redis' : 'MySQL'} {t('monitor.lbl.instanceSuffix')},
|
||||
<button type="button" className="dg-text-cyan ml-1 underline" onClick={() => openCreate(tab)}> {t('monitor.middlewareMonit.addNow')} </button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -183,8 +183,8 @@ export function SNMPMonitor() {
|
||||
{/* 工具栏 */}
|
||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">SNMP 监控</h2>
|
||||
<p className="text-xs text-[var(--dg-text-muted)]">5 分钟采集 · 指标保留 7 天</p>
|
||||
<h2 className="text-lg font-semibold">{t('monitor.lbl.snmpMonitorTitle')}</h2>
|
||||
<p className="text-xs text-[var(--dg-text-muted)]">{t('monitor.lbl.snmpCollectInterval')}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" variant="outline" onClick={refresh} className="h-8">
|
||||
@@ -208,7 +208,7 @@ export function SNMPMonitor() {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="grid gap-1.5">
|
||||
<Label>IP 地址</Label>
|
||||
<Label>{t('monitor.lbl.ipAddress')}</Label>
|
||||
<Input value={form.ip} onChange={(e) => setForm({ ...form, ip: e.target.value })} placeholder="192.168.1.1" />
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
@@ -217,7 +217,7 @@ export function SNMPMonitor() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label>OID 模板</Label>
|
||||
<Label>{t('monitor.lbl.snmpOidTemplate')}</Label>
|
||||
<Select value={form.template_id} onValueChange={(v) => setForm({ ...form, template_id: v })}>
|
||||
<SelectTrigger><SelectValue placeholder={t('pipeline.selectTemplate')} /></SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -229,7 +229,7 @@ export function SNMPMonitor() {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="grid gap-1.5">
|
||||
<Label>SNMP 版本</Label>
|
||||
<Label>{t('monitor.lbl.snmpVersion')}</Label>
|
||||
<Select value={form.snmp_version} onValueChange={(v) => setForm({ ...form, snmp_version: v as 'v2c' | 'v3' })}>
|
||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -247,7 +247,7 @@ export function SNMPMonitor() {
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setDialogOpen(false)}> {t('rbac.jobRole.cancel')} </Button>
|
||||
<Button onClick={handleAdd} disabled={submitting}>
|
||||
{submitting && <Loader2 className="h-4 w-4 animate-spin mr-1" />}确认
|
||||
{submitting && <Loader2 className="h-4 w-4 animate-spin mr-1" />}{t('common.confirm')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
@@ -278,7 +278,7 @@ export function SNMPMonitor() {
|
||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||
<CardContent className="py-12 text-center text-sm text-[var(--dg-text-muted)]">
|
||||
<Router className="h-10 w-10 mx-auto mb-3 opacity-40" />
|
||||
暂无 SNMP 设备,点击「添加设备」开始监控
|
||||
{t('monitor.lbl.snmpNoDevice')}
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
@@ -348,14 +348,14 @@ export function SNMPMonitor() {
|
||||
<Dialog open={metricsOpen} onOpenChange={setMetricsOpen}>
|
||||
<DialogContent className="sm:max-w-2xl max-h-[80vh] overflow-y-auto bg-[var(--dg-bg-secondary)]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{metricsDevice?.name} — 采集指标</DialogTitle>
|
||||
<DialogTitle>{metricsDevice?.name} — {t('monitor.lbl.snmpCollectMetrics')}</DialogTitle>
|
||||
<DialogDescription>{metricsDevice?.ip} · {templateName(metricsDevice?.template_id || 0)}</DialogDescription>
|
||||
</DialogHeader>
|
||||
{metricsLoading ? (
|
||||
<div className="py-8 text-center text-sm text-muted-foreground"> {t('monitor.logPanel.logResults.loading')} </div>
|
||||
) : metrics.length === 0 ? (
|
||||
<div className="py-8 text-center text-sm text-muted-foreground">
|
||||
暂无指标,请先采集
|
||||
{t('monitor.lbl.snmpNoMetrics')}
|
||||
{metricsDevice?.last_error && <p className="mt-2 text-xs dg-text-red">{metricsDevice.last_error}</p>}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -213,7 +213,7 @@ export function SiteMonitor() {
|
||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold"> {t('monitor.alertRulesTab.catSite')} </h2>
|
||||
<p className="text-xs text-[var(--dg-text-muted)]">HTTP Web 拨测 · SSL 证书检测 · TCP / DNS / PING</p>
|
||||
<p className="text-xs text-[var(--dg-text-muted)]">{t('monitor.lbl.siteMonitorSubtitle')}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" variant="outline" className="h-8" onClick={() => { loadStats(); loadTasks(); }} disabled={refreshing}>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ZoomableAreaChart, type ZoomableChartHandle } from './ZoomableAreaChart
|
||||
import { ChartToolbar } from './ChartToolbar';
|
||||
import { MetricIconBox } from './MetricIconBox';
|
||||
import type { AggType, BucketPoint } from './types';
|
||||
import i18n from '@/i18n';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface DualLineChartProps {
|
||||
title: string;
|
||||
@@ -21,6 +21,7 @@ interface DualLineChartProps {
|
||||
export function DualLineChart({
|
||||
title, currentRx, currentTx, rxData, txData, aggType, unit,
|
||||
}: DualLineChartProps) {
|
||||
const { t } = useTranslation();
|
||||
const chartRef = useRef<ZoomableChartHandle>(null);
|
||||
const [zoomed, setZoomed] = useState(false);
|
||||
|
||||
@@ -43,11 +44,11 @@ export function DualLineChart({
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
<span>
|
||||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-blue-400 mr-1 align-middle" />
|
||||
接收 <b className="text-blue-300">{currentRx}</b>
|
||||
{t('monitor.lbl.rx')} <b className="text-blue-300">{currentRx}</b>
|
||||
</span>
|
||||
<span>
|
||||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-emerald-400 mr-1 align-middle" />
|
||||
发送 <b className="dg-text-green">{currentTx}</b>
|
||||
{t('monitor.lbl.tx')} <b className="dg-text-green">{currentTx}</b>
|
||||
</span>
|
||||
</div>
|
||||
<ChartToolbar
|
||||
@@ -61,14 +62,14 @@ export function DualLineChart({
|
||||
ref={chartRef}
|
||||
data={chartData}
|
||||
series={[
|
||||
{ dataKey: 'rx', color: '#3b82f6', label: i18n.t('common.receive') },
|
||||
{ dataKey: 'tx', color: '#10b981', label: i18n.t('aiops.sendMessage') },
|
||||
{ dataKey: 'rx', color: '#3b82f6', label: t('common.receive') },
|
||||
{ dataKey: 'tx', color: '#10b981', label: t('aiops.sendMessage') },
|
||||
]}
|
||||
yDomain={[0, 'auto']}
|
||||
unit={unit}
|
||||
chartId={`dual-${title.replace(/\s/g, '')}`}
|
||||
title={title}
|
||||
subtitle={i18n.t('monitor.lbl.receiveSend', { rx: currentRx, tx: currentTx })}
|
||||
subtitle={t('monitor.lbl.receiveSend', { rx: currentRx, tx: currentTx })}
|
||||
height={208}
|
||||
toolbarPlacement="header"
|
||||
onZoomedChange={setZoomed}
|
||||
|
||||
@@ -200,9 +200,9 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) {
|
||||
|
||||
{probeForm.probe_type === 'tcp_socket' && (
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block">TCP 目标地址</label>
|
||||
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block">{t('monitor.lbl.tcpTargetAddr')}</label>
|
||||
<input className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.tcp_host} onChange={e => setProbeForm({ ...probeForm, tcp_host: e.target.value })} placeholder="localhost" /></div>
|
||||
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block">TCP 端口</label>
|
||||
<div><label className="text-[11px] text-[var(--dg-text-muted)] mb-1 block">{t('monitor.lbl.tcpPort')}</label>
|
||||
<input type="number" className="w-full h-8 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 text-xs text-[var(--dg-text-primary)]" value={probeForm.tcp_port} onChange={e => setProbeForm({ ...probeForm, tcp_port: Number(e.target.value) })} /></div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -58,7 +58,7 @@ export function HostTimeRangePicker({ value, onChange }: Props) {
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="text-[11px] dg-text-muted hidden sm:inline-flex items-center gap-1">
|
||||
<Timer className="h-3 w-3" />
|
||||
时间范围
|
||||
{t('monitor.lbl.timeRange')}
|
||||
</span>
|
||||
<div className="flex items-center gap-1 rounded-md border border-[var(--dg-border)] p-0.5 bg-[var(--dg-bg-secondary)] flex-wrap">
|
||||
{TIME_RANGE_PRESETS.map((p) => (
|
||||
@@ -82,7 +82,7 @@ export function HostTimeRangePicker({ value, onChange }: Props) {
|
||||
onClick={() => setCustomOpen((v) => !v)}
|
||||
>
|
||||
<Calendar className="h-3 w-3" />
|
||||
自定义
|
||||
{t('monitor.lbl.custom')}
|
||||
<ChevronDown className={`h-3 w-3 transition-transform ${customOpen ? 'rotate-180' : ''}`} />
|
||||
</Button>
|
||||
{customOpen && (
|
||||
@@ -110,10 +110,10 @@ export function HostTimeRangePicker({ value, onChange }: Props) {
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-3">
|
||||
<Button type="button" variant="ghost" size="sm" className="h-7 text-xs" onClick={() => setCustomOpen(false)}>
|
||||
取消
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button type="button" size="sm" className="h-7 text-xs dg-btn-primary" onClick={applyCustom}>
|
||||
应用
|
||||
{t('monitor.lbl.apply')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,13 +43,13 @@ export function PortPolicyDialog({
|
||||
className={`flex-1 h-9 rounded text-xs font-medium border transition-colors ${portPolicyMode === 'whitelist' ? 'bg-blue-500/20 border-blue-500 dg-text-blue' : 'bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] text-[var(--dg-text-muted)]'}`}
|
||||
onClick={() => onModeChange('whitelist')}
|
||||
>
|
||||
白名单 ─ 全量上报
|
||||
{t('monitor.lbl.whitelistFullReport')}
|
||||
</button>
|
||||
<button
|
||||
className={`flex-1 h-9 rounded text-xs font-medium border transition-colors ${portPolicyMode === 'blacklist' ? 'bg-amber-500/20 border-amber-500 dg-text-yellow' : 'bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] text-[var(--dg-text-muted)]'}`}
|
||||
onClick={() => onModeChange('blacklist')}
|
||||
>
|
||||
黑名单 ─ 仅上报选中
|
||||
{t('monitor.lbl.blacklistSelectedOnly')}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1.5 leading-relaxed">
|
||||
@@ -61,8 +61,8 @@ export function PortPolicyDialog({
|
||||
|
||||
<div>
|
||||
<label className="text-[11px] text-[var(--dg-text-muted)] mb-1.5 block">
|
||||
端口列表 <span className="text-[var(--dg-text-muted)]">(逗号分隔,如: 80,443,3306,6379)</span>
|
||||
{portPolicyMode === 'whitelist' && <span className="dg-text-blue ml-1">— 白名单模式无需配置</span>}
|
||||
{t('monitor.lbl.portListLabel')} <span className="text-[var(--dg-text-muted)]">{t('monitor.lbl.portListHint')}</span>
|
||||
{portPolicyMode === 'whitelist' && <span className="dg-text-blue ml-1">{t('monitor.lbl.whitelistNoConfig')}</span>}
|
||||
</label>
|
||||
<textarea
|
||||
className={`w-full h-20 bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded px-2 py-1.5 text-xs font-mono resize-none ${
|
||||
@@ -78,7 +78,7 @@ export function PortPolicyDialog({
|
||||
readOnly={portPolicyMode === 'whitelist'}
|
||||
/>
|
||||
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1">
|
||||
已输入 {portWhitelist.length} 个端口
|
||||
{t('monitor.lbl.portsEntered', { count: portWhitelist.length })}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ export const ZoomableAreaChart = forwardRef<ZoomableChartHandle, Props>(function
|
||||
className="flex items-center justify-center text-xs dg-text-muted border border-dashed border-[var(--dg-border)] rounded-md w-full"
|
||||
style={{ height }}
|
||||
>
|
||||
暂无数据
|
||||
{t('monitor.lbl.noData')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,18 +16,18 @@ export function ContextSwitchCard({ csInfo }: { csInfo: JsonData }) {
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-3 text-center">
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mb-1">CTX 切换/秒</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mb-1">{t('monitor.lbl.ctxPerSec')}</div>
|
||||
<div className="text-lg font-bold font-mono dg-text-green">
|
||||
{(csInfo.ctx_per_sec / 1e6).toFixed(1)}<span className="text-xs font-normal text-[var(--dg-text-muted)] ml-0.5">M</span>
|
||||
</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mt-1 font-mono">{csInfo.ctx_per_sec?.toLocaleString()} 次</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mt-1 font-mono">{csInfo.ctx_per_sec?.toLocaleString()} {t('monitor.lbl.times')}</div>
|
||||
</div>
|
||||
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-3 text-center">
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mb-1">IRQ 中断/秒</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mb-1">{t('monitor.lbl.irqPerSec')}</div>
|
||||
<div className="text-lg font-bold font-mono dg-text-yellow">
|
||||
{(csInfo.irq_per_sec / 1e6).toFixed(1)}<span className="text-xs font-normal text-[var(--dg-text-muted)] ml-0.5">M</span>
|
||||
</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mt-1 font-mono">{csInfo.irq_per_sec?.toLocaleString()} 次</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)] mt-1 font-mono">{csInfo.irq_per_sec?.toLocaleString()} {t('monitor.lbl.times')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
|
||||
@@ -26,7 +26,7 @@ export function FDUsageCard({ fdInfo, fdMetrics, aggType, fdChartRef, fdChartZoo
|
||||
<div className="flex items-start justify-between gap-2 mb-3">
|
||||
<h3 className="text-xs font-medium text-[var(--dg-text-primary)] flex items-center gap-2 leading-8">
|
||||
<MetricIconBox icon={<HardDrive />} tone="purple" />
|
||||
FD 文件句柄
|
||||
{t('monitor.lbl.fdHandles')}
|
||||
</h3>
|
||||
{fdMetrics.length > 0 && (
|
||||
<ChartToolbar
|
||||
|
||||
@@ -23,22 +23,22 @@ export function PortMonitorTab({ portInfo, portPolicyMode, portWhitelist, onOpen
|
||||
<span className="ml-auto flex items-center gap-2">
|
||||
{portPolicyMode === 'blacklist' && portWhitelist.length > 0 && (
|
||||
<Badge variant="outline" className="text-[10px] dg-text-yellow border-amber-500/30">
|
||||
黑名单: {portWhitelist.join(',')}
|
||||
{t('monitor.lbl.blacklistLabel')}: {portWhitelist.join(',')}
|
||||
</Badge>
|
||||
)}
|
||||
{portPolicyMode === 'whitelist' && (
|
||||
<Badge variant="outline" className="text-[10px] dg-text-blue border-blue-500/30">
|
||||
白名单模式
|
||||
{t('monitor.lbl.whitelistModeLabel')}
|
||||
</Badge>
|
||||
)}
|
||||
<Button variant="outline" size="sm" className="h-6 text-[10px] px-2" onClick={onOpenPortPolicy}>
|
||||
策略配置
|
||||
{t('monitor.lbl.policyConfig')}
|
||||
</Button>
|
||||
</span>
|
||||
</h3>
|
||||
{portInfo.length === 0 ? (
|
||||
<div className="text-center py-6 text-xs text-[var(--dg-text-muted)] border border-dashed border-[var(--dg-border)] rounded-lg">
|
||||
暂无端口数据
|
||||
{t('monitor.lbl.noPortData')}
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
|
||||
@@ -14,7 +14,7 @@ export function ProcessTable({ processTop }: { processTop: JsonData[] }) {
|
||||
</h3>
|
||||
{processTop.length === 0 ? (
|
||||
<div className="text-center py-6 text-xs text-[var(--dg-text-muted)] border border-dashed border-[var(--dg-border)] rounded-lg">
|
||||
Agent v2.0+ 支持,等待数据上报…
|
||||
{t('monitor.lbl.agentWaitData')}
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
@@ -24,7 +24,7 @@ export function ProcessTable({ processTop }: { processTop: JsonData[] }) {
|
||||
<th className="py-1.5 text-left font-medium w-[40%]"> {t('monitor.lbl.processMonitor')} </th>
|
||||
<th className="py-1.5 text-right font-medium w-[8%]">PID</th>
|
||||
<th className="py-1.5 text-right font-medium w-[16%]"> {t('monitor.lbl.processMonitor')} </th>
|
||||
<th className="py-1.5 text-right font-medium w-[16%]">CPU使用率(%)</th>
|
||||
<th className="py-1.5 text-right font-medium w-[16%]">{t('monitor.lbl.cpuUsagePercent')}</th>
|
||||
<th className="py-1.5 text-right font-medium w-[20%]"> {t('monitor.lbl.processMonitor')} </th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -98,7 +98,7 @@ export function SiteResultsPanel({ task, onProbed, fullPage = false }: Props) {
|
||||
if (!task) {
|
||||
const empty = (
|
||||
<div className="p-8 text-center text-sm text-[var(--dg-text-muted)]">
|
||||
选择任务查看协议专属可观测指标
|
||||
{t('monitor.lbl.selectTaskHint')}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
@@ -218,7 +218,7 @@ export function SiteResultsPanel({ task, onProbed, fullPage = false }: Props) {
|
||||
</>
|
||||
)}
|
||||
<span className="text-[10px] text-[var(--dg-text-muted)] shrink-0">
|
||||
{rangeLabel} · {results.length} 次 · 可用率 {availSummary}
|
||||
{rangeLabel} · {results.length} {t('monitor.lbl.times')} · {t('monitor.lbl.availabilityRate')} {availSummary}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
@@ -235,7 +235,7 @@ export function SiteResultsPanel({ task, onProbed, fullPage = false }: Props) {
|
||||
<RefreshCw className={`h-3 w-3 mr-1 ${loading ? 'animate-spin' : ''}`} />{t("common.refresh")}
|
||||
</Button>
|
||||
<Button size="sm" className="h-7 text-xs" onClick={handleProbeNow} disabled={probing}>
|
||||
{probing ? <Loader2 className="h-3 w-3 mr-1 animate-spin" /> : <Zap className="h-3 w-3 mr-1" />}立即拨测
|
||||
{probing ? <Loader2 className="h-3 w-3 mr-1 animate-spin" /> : <Zap className="h-3 w-3 mr-1" />}{t('monitor.lbl.probeNow')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -244,9 +244,9 @@ export function SiteResultsPanel({ task, onProbed, fullPage = false }: Props) {
|
||||
<div className="mb-4 flex items-start gap-2 rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 py-2.5 text-xs">
|
||||
<ShieldAlert className="h-4 w-4 shrink-0 mt-0.5 dg-text-yellow" />
|
||||
<div>
|
||||
<p className="font-medium text-[var(--dg-text-primary)]">SSL 证书预警</p>
|
||||
<p className="font-medium text-[var(--dg-text-primary)]">{t('monitor.lbl.sslCertWarning')}</p>
|
||||
<p className="mt-0.5 text-[var(--dg-text-muted)]">
|
||||
到期 <span className="font-mono">{task.ssl_cert_expiry?.slice(0, 10) || '--'}</span>,剩余 <span className={sslInfo.tone}>{sslInfo.text}</span>(阈值 {SSL_EXPIRY_WARN_DAYS} 天)
|
||||
{t('monitor.lbl.sslExpiring')} <span className="font-mono">{task.ssl_cert_expiry?.slice(0, 10) || '--'}</span>,{t('monitor.lbl.sslRemaining')} <span className={sslInfo.tone}>{sslInfo.text}</span>({t('monitor.lbl.sslThreshold')} {SSL_EXPIRY_WARN_DAYS} {t('monitor.lbl.sslDays')})
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user