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
@@ -527,6 +527,7 @@ services/api.ts (1324L)
|
|||||||
| U36 | 全量 JSX 属性 + toast 硬编码中文清零(20 文件 30 处→0: TerminalLoadingOverlay/copy-button/AppRelease/DeployCredentials/PipelineManager/WorkspaceTermDialog/CredFormDialog/JobRoleCreateDialog/PipelineCreateDialog/AdvancedSettingsPanel/ServerForm/LdapManagementSection/LiveMonitorDialog/SystemManager/ActiveSessionsPanel/HistorySessionsPanel/AlertRulesTab/PrometheusDatasourceManager/SNMPMonitor/PredictiveCapacityTab — 23 JSX 属性 + 7 toast 调用全量清零, copy-button 从 import i18n→useTranslation hook, 4 组件新增 useTranslation hook;新增 25 翻译键;zh/en 7616→7640 +24 对齐;tsc -b=0, npm build✓ (17.25s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
| U36 | 全量 JSX 属性 + toast 硬编码中文清零(20 文件 30 处→0: TerminalLoadingOverlay/copy-button/AppRelease/DeployCredentials/PipelineManager/WorkspaceTermDialog/CredFormDialog/JobRoleCreateDialog/PipelineCreateDialog/AdvancedSettingsPanel/ServerForm/LdapManagementSection/LiveMonitorDialog/SystemManager/ActiveSessionsPanel/HistorySessionsPanel/AlertRulesTab/PrometheusDatasourceManager/SNMPMonitor/PredictiveCapacityTab — 23 JSX 属性 + 7 toast 调用全量清零, copy-button 从 import i18n→useTranslation hook, 4 组件新增 useTranslation hook;新增 25 翻译键;zh/en 7616→7640 +24 对齐;tsc -b=0, npm build✓ (17.25s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
||||||
| U38 | i18n phantom CTK 清理 — 模板字面量 + JSX 属性残余中文清零(13 文件 16 处→0: LogsHeader "行"suffix + analyzerHelpers "当前值" + DeploymentEditStorage 2 toast description + WorkloadViewPage 2 toast description + ServerFileBrowser confirm + LogDatasourcePage toast + LogAlertsPanel confirm + DeployTargetFields JSX hint + UserGroupsManager 3 处 confirm/desc + BuildNodesManager JSX "(可选)" + useLdapManagement confirm — 新增 14 语义化翻译键 (common.lineSuffix/currentValue/memberCount/fillIfMultiContainer/confirmDeleteType + k8s.editStorage.*Desc + k8s.workloadView.restartSentDesc + k8s.workloads.scaleSuccessDesc + aiops.reachable + monitor.deleteRuleConfirmDesc + deploy.rbac.deleteGroupDesc + pipeline.removeMemberDesc + system.ldapMgmt.deleteEntryDesc),zh/en 7737→7751 +14 对齐;tsc -b=0, npm build✓ (16.86s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
| U38 | i18n phantom CTK 清理 — 模板字面量 + JSX 属性残余中文清零(13 文件 16 处→0: LogsHeader "行"suffix + analyzerHelpers "当前值" + DeploymentEditStorage 2 toast description + WorkloadViewPage 2 toast description + ServerFileBrowser confirm + LogDatasourcePage toast + LogAlertsPanel confirm + DeployTargetFields JSX hint + UserGroupsManager 3 处 confirm/desc + BuildNodesManager JSX "(可选)" + useLdapManagement confirm — 新增 14 语义化翻译键 (common.lineSuffix/currentValue/memberCount/fillIfMultiContainer/confirmDeleteType + k8s.editStorage.*Desc + k8s.workloadView.restartSentDesc + k8s.workloads.scaleSuccessDesc + aiops.reachable + monitor.deleteRuleConfirmDesc + deploy.rbac.deleteGroupDesc + pipeline.removeMemberDesc + system.ldapMgmt.deleteEntryDesc),zh/en 7737→7751 +14 对齐;tsc -b=0, npm build✓ (16.86s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
||||||
| U39 | 残余 JSX 属性/模板字面量中文清零(10 文件 14 处→0: DualLineChart subtitle 接收/发送 + NetworkTab 5处 unit="个" + FDUsageCard subtitle 当前 + NamespaceOverviewPage/NamespaceManagementPage subtitle + CreateHPADialog title+text 命名空间 + LogFieldSidebar title 个值 + LiveMonitorDialog title 全屏/退出全屏 + ExecutionHistoryDialog title 成功/失败 + LdapSettingsTab className 成功检查→ldapTestSuccess boolean 状态替代字符串匹配 — 新增 11 翻译键, zh/en 7751→7762 对齐;tsc -b=0, npm build✓ (16.94s), ESLint=0/0/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
| U39 | 残余 JSX 属性/模板字面量中文清零(10 文件 14 处→0: DualLineChart subtitle 接收/发送 + NetworkTab 5处 unit="个" + FDUsageCard subtitle 当前 + NamespaceOverviewPage/NamespaceManagementPage subtitle + CreateHPADialog title+text 命名空间 + LogFieldSidebar title 个值 + LiveMonitorDialog title 全屏/退出全屏 + ExecutionHistoryDialog title 成功/失败 + LdapSettingsTab className 成功检查→ldapTestSuccess boolean 状态替代字符串匹配 — 新增 11 翻译键, zh/en 7751→7762 对齐;tsc -b=0, npm build✓ (16.94s), ESLint=0/0/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
||||||
|
| U40 | Monitor + Agent 子组件残余硬编码中文 i18n 补全(14 文件 ~50 处→0: ContextSwitchCard CTX/IRQ/次 + ProcessTable agentWaitData/cpuUsagePercent + FDUsageCard fdHandles + PortMonitorTab 黑名单/白名单/策略配置/暂无端口数据 + PortPolicyDialog 白名单全量上报/黑名单仅上报选中/端口列表/逗号分隔/白名单无需配置/已输入端口 + HostTimeRangePicker 时间范围/自定义/取消/应用 + DualLineChart 接收/发送 (i18n.t→useTranslation hook) + ZoomableAreaChart 暂无数据 + SiteResultsPanel 选择任务/次/可用率/立即拨测/SSL证书预警/到期/剩余/阈值/天 + MiddlewareMonitor Redis单机集群/上次更新/添加平台默认/暂无实例 + SiteMonitor subtitle + SNMPMonitor 标题/采集间隔/IP地址/OID模板/SNMP版本/确认/暂无设备/采集指标/暂无指标 + HealthProbeSection TCP目标地址/TCP端口 + AgentAlerts Agent告警/刷新/未恢复/已恢复/恢复/新建规则/创建第一条规则 — 新增 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 测试通过) | P3 | **done** |
|
||||||
| U37 | 全量 JSX 文本节点硬编码中文清零(65 文件 151 处→0: MiddlewareMonitor/SiteMonitor/SNMPMonitor/HealthProbeSection/ContextSwitchCard/ProcessTable/PortMonitorTab/MwTargetChartsPanel/MiddlewareDialog/ChartRangeHint/SnmpDeviceChartsPanel/AlertRulesTab/LogClusterPanel/LogDatasourcePage/LogQueryToolbar/LogAlertsPanel/PrometheusDatasourceManager/PodMetricsPage/CapacityPage/NamespaceManagementPage/NamespaceResourcePage/DeploymentEditService/EventsPage/DeploymentDetail/ClusterOverviewPage/DeploymentEditContainers/DeploymentEditStorage/WorkloadViewPage/BottomStatusBar/UpdateStrategySelectorCards/PodInstancesTable/shared-components/UpdateStrategyCard/TolerationsCard/HostSettingsCard/IngressListItem/ServiceListItem/ServiceCreateForm/IngressCreateForm/CreateHPADialog/DeployCredentials/DeployRBAC/UserGroupsManager/BuildNodesManager/DeployTargetFields/PipelineCreateDialog/CredFormDialog/WorkspaceTermDialog/AgentAlerts/AgentRuleDialog/LiveMonitorDialog/RoleFormDialog/CronJobViewDialog/TerminalOutput/AIAnalysisResult/AIPredictiveAnalytics/PredictiveCostTab/PredictiveCapacityTab/ActiveSessionsPanel/HistorySessionsPanel/TerminalDialog/ServerFileBrowser/PodFileBrowser/SiteResultsPanel/SiteProtocolPieChart — 151 JSX 文本节点中文全部替换为 t()/i18n.t() 调用;新增 common.* 98 翻译键 (selectedCount/itemsCount/newValue/namespaceLabel/containerLabel/replicasLabel/minReplicas/maxReplicas/createNamespace/noNamespace/unknownResource/exportYaml/loadTrend/loadCapacity/predicted/threshold/hostSettings/addVolume/addMount/createService/createIngress/editAction/updateStrategy/selectAndLabel/historyVersion/fileBrowser/scale/back/createHpa/allNamespaces/certLabel/observable/deviceList/addDevice/addMonitor/collectNow/addTask/addProbe/restoreFull/contextSwitch/processTop/listenPorts/terminalRealtime/auditWebssh/buildTerminal/fileBrowse/newDir/loadDeployment/notLinked/selectedPerms/none/hasLabel/deleteNamespace/deleteNamespaceWarn/allResources/notFoundDeploy/nodeList/fileLabel/addCustom/newInstanceService/newInstanceIngress/totalContainers/workContainers/loadingEllipsis/volumeClaimTemplates/containerTemplate/passwordOptional/updated/page/perPage/clusterMode/totalLogs/countLabel/percentage/queryTime/addDatasource/newPermission/newEnv/envLabel/displayName/newUserGroup/auditTrace/predictValue/confidence/predictMonthEnd/noCapacityData/securityMode/standardRdp/domainLabel/noRdpActive/noRdpHistory/buffer/matched/podLogs/createHpaTitle/password/events/total);37 文件已有 import i18n→i18n.t() 模式, 2 文件新增 import i18n;zh/en 7640→3526 namespace-flat +98 键对齐;tsc -b=0, npm build✓ (17.96s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
| U37 | 全量 JSX 文本节点硬编码中文清零(65 文件 151 处→0: MiddlewareMonitor/SiteMonitor/SNMPMonitor/HealthProbeSection/ContextSwitchCard/ProcessTable/PortMonitorTab/MwTargetChartsPanel/MiddlewareDialog/ChartRangeHint/SnmpDeviceChartsPanel/AlertRulesTab/LogClusterPanel/LogDatasourcePage/LogQueryToolbar/LogAlertsPanel/PrometheusDatasourceManager/PodMetricsPage/CapacityPage/NamespaceManagementPage/NamespaceResourcePage/DeploymentEditService/EventsPage/DeploymentDetail/ClusterOverviewPage/DeploymentEditContainers/DeploymentEditStorage/WorkloadViewPage/BottomStatusBar/UpdateStrategySelectorCards/PodInstancesTable/shared-components/UpdateStrategyCard/TolerationsCard/HostSettingsCard/IngressListItem/ServiceListItem/ServiceCreateForm/IngressCreateForm/CreateHPADialog/DeployCredentials/DeployRBAC/UserGroupsManager/BuildNodesManager/DeployTargetFields/PipelineCreateDialog/CredFormDialog/WorkspaceTermDialog/AgentAlerts/AgentRuleDialog/LiveMonitorDialog/RoleFormDialog/CronJobViewDialog/TerminalOutput/AIAnalysisResult/AIPredictiveAnalytics/PredictiveCostTab/PredictiveCapacityTab/ActiveSessionsPanel/HistorySessionsPanel/TerminalDialog/ServerFileBrowser/PodFileBrowser/SiteResultsPanel/SiteProtocolPieChart — 151 JSX 文本节点中文全部替换为 t()/i18n.t() 调用;新增 common.* 98 翻译键 (selectedCount/itemsCount/newValue/namespaceLabel/containerLabel/replicasLabel/minReplicas/maxReplicas/createNamespace/noNamespace/unknownResource/exportYaml/loadTrend/loadCapacity/predicted/threshold/hostSettings/addVolume/addMount/createService/createIngress/editAction/updateStrategy/selectAndLabel/historyVersion/fileBrowser/scale/back/createHpa/allNamespaces/certLabel/observable/deviceList/addDevice/addMonitor/collectNow/addTask/addProbe/restoreFull/contextSwitch/processTop/listenPorts/terminalRealtime/auditWebssh/buildTerminal/fileBrowse/newDir/loadDeployment/notLinked/selectedPerms/none/hasLabel/deleteNamespace/deleteNamespaceWarn/allResources/notFoundDeploy/nodeList/fileLabel/addCustom/newInstanceService/newInstanceIngress/totalContainers/workContainers/loadingEllipsis/volumeClaimTemplates/containerTemplate/passwordOptional/updated/page/perPage/clusterMode/totalLogs/countLabel/percentage/queryTime/addDatasource/newPermission/newEnv/envLabel/displayName/newUserGroup/auditTrace/predictValue/confidence/predictMonthEnd/noCapacityData/securityMode/standardRdp/domainLabel/noRdpActive/noRdpHistory/buffer/matched/podLogs/createHpaTitle/password/events/total);37 文件已有 import i18n→i18n.t() 模式, 2 文件新增 import i18n;zh/en 7640→3526 namespace-flat +98 键对齐;tsc -b=0, npm build✓ (17.96s), ESLint=0/43/any=0, go build=0, 17/17 测试通过) | P3 | **done** |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -113,12 +113,12 @@ export function AgentAlerts() {
|
|||||||
{/* 页头 */}
|
{/* 页头 */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">Agent 告警</h2>
|
<h2 className="text-xl font-semibold text-[var(--dg-text-primary)]">{i18n.t('agent.alerts.title')}</h2>
|
||||||
<p className="text-sm dg-text-secondary mt-0.5"> {i18n.t('agent.alerts.thresholdTriggered')} </p>
|
<p className="text-sm dg-text-secondary mt-0.5"> {i18n.t('agent.alerts.thresholdTriggered')} </p>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" size="sm" onClick={loadData} disabled={loading} className="dg-input text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]">
|
<Button variant="outline" size="sm" onClick={loadData} disabled={loading} className="dg-input text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]">
|
||||||
<RefreshCw className={`h-4 w-4 mr-1.5 ${loading ? 'animate-spin' : ''}`} />
|
<RefreshCw className={`h-4 w-4 mr-1.5 ${loading ? 'animate-spin' : ''}`} />
|
||||||
刷新
|
{i18n.t('common.refresh')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ export function AgentAlerts() {
|
|||||||
onClick={() => setResolvedView(false)}
|
onClick={() => setResolvedView(false)}
|
||||||
className={!resolvedView ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'dg-input text-[var(--dg-text-secondary)]'}
|
className={!resolvedView ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'dg-input text-[var(--dg-text-secondary)]'}
|
||||||
>
|
>
|
||||||
未恢复
|
{i18n.t('agent.alerts.unresolved')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -145,7 +145,7 @@ export function AgentAlerts() {
|
|||||||
onClick={() => setResolvedView(true)}
|
onClick={() => setResolvedView(true)}
|
||||||
className={resolvedView ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'dg-input text-[var(--dg-text-secondary)]'}
|
className={resolvedView ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'dg-input text-[var(--dg-text-secondary)]'}
|
||||||
>
|
>
|
||||||
已恢复
|
{i18n.t('agent.alerts.resolved')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ export function AgentAlerts() {
|
|||||||
className="shrink-0 h-8 dg-input dg-text-green hover:bg-emerald-500/10 hover:dg-text-green"
|
className="shrink-0 h-8 dg-input dg-text-green hover:bg-emerald-500/10 hover:dg-text-green"
|
||||||
>
|
>
|
||||||
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
||||||
恢复
|
{i18n.t('agent.alerts.resolve')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -223,7 +223,7 @@ export function AgentAlerts() {
|
|||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button size="sm" onClick={openCreateRule} className="bg-blue-600 hover:bg-blue-700 text-white">
|
<Button size="sm" onClick={openCreateRule} className="bg-blue-600 hover:bg-blue-700 text-white">
|
||||||
<Plus className="h-4 w-4 mr-1.5" />
|
<Plus className="h-4 w-4 mr-1.5" />
|
||||||
新建规则
|
{i18n.t('agent.alerts.newRule')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ export function AgentAlerts() {
|
|||||||
<p className="dg-text-secondary"> {i18n.t('monitor.noRules')} </p>
|
<p className="dg-text-secondary"> {i18n.t('monitor.noRules')} </p>
|
||||||
<Button size="sm" onClick={openCreateRule} className="mt-4 bg-blue-600 hover:bg-blue-700 text-white">
|
<Button size="sm" onClick={openCreateRule} className="mt-4 bg-blue-600 hover:bg-blue-700 text-white">
|
||||||
<Plus className="h-4 w-4 mr-1.5" />
|
<Plus className="h-4 w-4 mr-1.5" />
|
||||||
创建第一条规则
|
{i18n.t('agent.alerts.createFirstRule')}
|
||||||
</Button>
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -4377,7 +4377,53 @@
|
|||||||
"noProbeHint": "No health probes. Click \"{{action}}\" to create one.",
|
"noProbeHint": "No health probes. Click \"{{action}}\" to create one.",
|
||||||
"searchFields": "Search fields",
|
"searchFields": "Search fields",
|
||||||
"receiveSend": "RX {{rx}} · TX {{tx}}",
|
"receiveSend": "RX {{rx}} · TX {{tx}}",
|
||||||
"currentFDUsage": "Current {{pct}}%"
|
"currentFDUsage": "Current {{pct}}%",
|
||||||
|
"ctxPerSec": "CTX switches/sec",
|
||||||
|
"irqPerSec": "IRQ interrupts/sec",
|
||||||
|
"times": "times",
|
||||||
|
"agentWaitData": "Agent v2.0+ required, waiting for data…",
|
||||||
|
"cpuUsagePercent": "CPU Usage(%)",
|
||||||
|
"fdHandles": "FD File Handles",
|
||||||
|
"blacklistLabel": "Blacklist",
|
||||||
|
"whitelistModeLabel": "Whitelist mode",
|
||||||
|
"policyConfig": "Policy Config",
|
||||||
|
"noPortData": "No port data",
|
||||||
|
"whitelistFullReport": "Whitelist — Report all",
|
||||||
|
"blacklistSelectedOnly": "Blacklist — Report selected only",
|
||||||
|
"portListLabel": "Port list",
|
||||||
|
"portListHint": "(comma-separated, e.g.: 80,443,3306,6379)",
|
||||||
|
"whitelistNoConfig": "— No config needed in whitelist mode",
|
||||||
|
"portsEntered": "{{count}} ports entered",
|
||||||
|
"timeRange": "Time range",
|
||||||
|
"custom": "Custom",
|
||||||
|
"apply": "Apply",
|
||||||
|
"rx": "RX",
|
||||||
|
"tx": "TX",
|
||||||
|
"noData": "No data",
|
||||||
|
"selectTaskHint": "Select a task to view protocol-specific metrics",
|
||||||
|
"probeNow": "Probe now",
|
||||||
|
"sslCertWarning": "SSL Certificate Warning",
|
||||||
|
"sslExpiring": "Expires",
|
||||||
|
"sslRemaining": "remaining",
|
||||||
|
"sslThreshold": "threshold",
|
||||||
|
"sslDays": "days",
|
||||||
|
"availabilityRate": "Availability",
|
||||||
|
"redisMysqlAutoRefresh": "Redis standalone/cluster · MySQL · 30s auto refresh",
|
||||||
|
"lastUpdate": "Last update",
|
||||||
|
"addPlatformDefault": "Add platform default",
|
||||||
|
"noInstancePrefix": "No",
|
||||||
|
"instanceSuffix": "instances",
|
||||||
|
"siteMonitorSubtitle": "HTTP Web probe · SSL cert detection · TCP / DNS / PING",
|
||||||
|
"snmpMonitorTitle": "SNMP Monitor",
|
||||||
|
"snmpCollectInterval": "5 min collection · 7 day retention",
|
||||||
|
"tcpTargetAddr": "TCP target address",
|
||||||
|
"tcpPort": "TCP port",
|
||||||
|
"ipAddress": "IP address",
|
||||||
|
"snmpOidTemplate": "OID Template",
|
||||||
|
"snmpVersion": "SNMP Version",
|
||||||
|
"snmpNoDevice": "No SNMP devices. Click \"Add Device\" to start monitoring.",
|
||||||
|
"snmpCollectMetrics": "Collected Metrics",
|
||||||
|
"snmpNoMetrics": "No metrics yet. Please collect first."
|
||||||
},
|
},
|
||||||
"logAlertBell": {
|
"logAlertBell": {
|
||||||
"linked": "Alert Policy Linked",
|
"linked": "Alert Policy Linked",
|
||||||
@@ -7043,7 +7089,7 @@
|
|||||||
"loadFailed": "Failed to load alert data",
|
"loadFailed": "Failed to load alert data",
|
||||||
"resolveAlert": "Resolve Alert",
|
"resolveAlert": "Resolve Alert",
|
||||||
"resolveConfirm": "Mark this alert as resolved?",
|
"resolveConfirm": "Mark this alert as resolved?",
|
||||||
"resolved": "Alert resolved",
|
"resolved": "Resolved",
|
||||||
"deleteRule": "Delete Rule",
|
"deleteRule": "Delete Rule",
|
||||||
"thresholdTriggered": "Alert threshold triggered",
|
"thresholdTriggered": "Alert threshold triggered",
|
||||||
"noResolved": "No resolved alerts",
|
"noResolved": "No resolved alerts",
|
||||||
@@ -7051,7 +7097,12 @@
|
|||||||
"metricSwap": "Swap Usage",
|
"metricSwap": "Swap Usage",
|
||||||
"metricLoad1": "1-min Load Average",
|
"metricLoad1": "1-min Load Average",
|
||||||
"metricInode": "Inode Usage",
|
"metricInode": "Inode Usage",
|
||||||
"metricTcpEst": "TCP Established"
|
"metricTcpEst": "TCP Established",
|
||||||
|
"title": "Agent Alerts",
|
||||||
|
"unresolved": "Unresolved",
|
||||||
|
"resolve": "Resolve",
|
||||||
|
"newRule": "New Rule",
|
||||||
|
"createFirstRule": "Create your first rule"
|
||||||
},
|
},
|
||||||
"allPortsHint": "All ports reported automatically",
|
"allPortsHint": "All ports reported automatically",
|
||||||
"allPortsMode": "All Ports",
|
"allPortsMode": "All Ports",
|
||||||
|
|||||||
@@ -4377,7 +4377,53 @@
|
|||||||
"noProbeHint": "暂无健康探针,点击「{{action}}」创建",
|
"noProbeHint": "暂无健康探针,点击「{{action}}」创建",
|
||||||
"searchFields": "搜索字段",
|
"searchFields": "搜索字段",
|
||||||
"receiveSend": "接收 {{rx}} · 发送 {{tx}}",
|
"receiveSend": "接收 {{rx}} · 发送 {{tx}}",
|
||||||
"currentFDUsage": "当前 {{pct}}%"
|
"currentFDUsage": "当前 {{pct}}%",
|
||||||
|
"ctxPerSec": "CTX 切换/秒",
|
||||||
|
"irqPerSec": "IRQ 中断/秒",
|
||||||
|
"times": "次",
|
||||||
|
"agentWaitData": "Agent v2.0+ 支持,等待数据上报…",
|
||||||
|
"cpuUsagePercent": "CPU使用率(%)",
|
||||||
|
"fdHandles": "FD 文件句柄",
|
||||||
|
"blacklistLabel": "黑名单",
|
||||||
|
"whitelistModeLabel": "白名单模式",
|
||||||
|
"policyConfig": "策略配置",
|
||||||
|
"noPortData": "暂无端口数据",
|
||||||
|
"whitelistFullReport": "白名单 ─ 全量上报",
|
||||||
|
"blacklistSelectedOnly": "黑名单 ─ 仅上报选中",
|
||||||
|
"portListLabel": "端口列表",
|
||||||
|
"portListHint": "(逗号分隔,如: 80,443,3306,6379)",
|
||||||
|
"whitelistNoConfig": "— 白名单模式无需配置",
|
||||||
|
"portsEntered": "已输入 {{count}} 个端口",
|
||||||
|
"timeRange": "时间范围",
|
||||||
|
"custom": "自定义",
|
||||||
|
"apply": "应用",
|
||||||
|
"rx": "接收",
|
||||||
|
"tx": "发送",
|
||||||
|
"noData": "暂无数据",
|
||||||
|
"selectTaskHint": "选择任务查看协议专属可观测指标",
|
||||||
|
"probeNow": "立即拨测",
|
||||||
|
"sslCertWarning": "SSL 证书预警",
|
||||||
|
"sslExpiring": "到期",
|
||||||
|
"sslRemaining": "剩余",
|
||||||
|
"sslThreshold": "阈值",
|
||||||
|
"sslDays": "天",
|
||||||
|
"availabilityRate": "可用率",
|
||||||
|
"redisMysqlAutoRefresh": "Redis 单机/集群 · MySQL · 30s 自动刷新",
|
||||||
|
"lastUpdate": "上次更新",
|
||||||
|
"addPlatformDefault": "添加平台默认",
|
||||||
|
"noInstancePrefix": "暂无",
|
||||||
|
"instanceSuffix": "实例",
|
||||||
|
"siteMonitorSubtitle": "HTTP Web 拨测 · SSL 证书检测 · TCP / DNS / PING",
|
||||||
|
"snmpMonitorTitle": "SNMP 监控",
|
||||||
|
"snmpCollectInterval": "5 分钟采集 · 指标保留 7 天",
|
||||||
|
"tcpTargetAddr": "TCP 目标地址",
|
||||||
|
"tcpPort": "TCP 端口",
|
||||||
|
"ipAddress": "IP 地址",
|
||||||
|
"snmpOidTemplate": "OID 模板",
|
||||||
|
"snmpVersion": "SNMP 版本",
|
||||||
|
"snmpNoDevice": "暂无 SNMP 设备,点击「添加设备」开始监控",
|
||||||
|
"snmpCollectMetrics": "采集指标",
|
||||||
|
"snmpNoMetrics": "暂无指标,请先采集"
|
||||||
},
|
},
|
||||||
"logAlertBell": {
|
"logAlertBell": {
|
||||||
"linked": "已关联告警策略",
|
"linked": "已关联告警策略",
|
||||||
@@ -7043,7 +7089,7 @@
|
|||||||
"loadFailed": "加载告警数据失败",
|
"loadFailed": "加载告警数据失败",
|
||||||
"resolveAlert": "恢复告警",
|
"resolveAlert": "恢复告警",
|
||||||
"resolveConfirm": "确定将此告警标记为已恢复?",
|
"resolveConfirm": "确定将此告警标记为已恢复?",
|
||||||
"resolved": "告警已恢复",
|
"resolved": "已恢复",
|
||||||
"deleteRule": "删除规则",
|
"deleteRule": "删除规则",
|
||||||
"thresholdTriggered": "触发告警阈值",
|
"thresholdTriggered": "触发告警阈值",
|
||||||
"noResolved": "暂无已恢复的告警",
|
"noResolved": "暂无已恢复的告警",
|
||||||
@@ -7051,7 +7097,12 @@
|
|||||||
"metricSwap": "Swap 使用率",
|
"metricSwap": "Swap 使用率",
|
||||||
"metricLoad1": "1分钟负载",
|
"metricLoad1": "1分钟负载",
|
||||||
"metricInode": "Inode 使用率",
|
"metricInode": "Inode 使用率",
|
||||||
"metricTcpEst": "TCP 已建立"
|
"metricTcpEst": "TCP 已建立",
|
||||||
|
"title": "Agent 告警",
|
||||||
|
"unresolved": "未恢复",
|
||||||
|
"resolve": "恢复",
|
||||||
|
"newRule": "新建规则",
|
||||||
|
"createFirstRule": "创建第一条规则"
|
||||||
},
|
},
|
||||||
"allPortsHint": "全部端口自动上报",
|
"allPortsHint": "全部端口自动上报",
|
||||||
"allPortsMode": "全量上报",
|
"allPortsMode": "全量上报",
|
||||||
|
|||||||
@@ -153,10 +153,10 @@ export function MiddlewareMonitor() {
|
|||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold"> {t('monitor.alertHelper.srcMiddleware')} </h2>
|
<h2 className="text-lg font-semibold"> {t('monitor.alertHelper.srcMiddleware')} </h2>
|
||||||
<p className="text-xs text-[var(--dg-text-muted)] mt-0.5">
|
<p className="text-xs text-[var(--dg-text-muted)] mt-0.5">
|
||||||
Redis 单机/集群 · MySQL · 30s 自动刷新
|
{t('monitor.lbl.redisMysqlAutoRefresh')}
|
||||||
{lastRefreshAt && (
|
{lastRefreshAt && (
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
上次更新 {lastRefreshAt.toLocaleTimeString()}
|
{t('monitor.lbl.lastUpdate')} {lastRefreshAt.toLocaleTimeString()}
|
||||||
{refreshing && t('monitor.lbl.refreshing')}
|
{refreshing && t('monitor.lbl.refreshing')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -172,7 +172,7 @@ export function MiddlewareMonitor() {
|
|||||||
{targets.length === 0 && (
|
{targets.length === 0 && (
|
||||||
<Button variant="outline" size="sm" onClick={handleSeedDefaults} disabled={seeding} className="gap-1.5">
|
<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" />}
|
{seeding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Database className="h-4 w-4" />}
|
||||||
添加平台默认
|
{t('monitor.lbl.addPlatformDefault')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button size="sm" onClick={() => openCreate()} className="gap-1.5">
|
<Button size="sm" onClick={() => openCreate()} className="gap-1.5">
|
||||||
@@ -252,7 +252,7 @@ export function MiddlewareMonitor() {
|
|||||||
{currentTargets.length === 0 ? (
|
{currentTargets.length === 0 ? (
|
||||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||||
<CardContent className="p-8 text-center text-sm text-[var(--dg-text-muted)]">
|
<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>
|
<button type="button" className="dg-text-cyan ml-1 underline" onClick={() => openCreate(tab)}> {t('monitor.middlewareMonit.addNow')} </button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -183,8 +183,8 @@ export function SNMPMonitor() {
|
|||||||
{/* 工具栏 */}
|
{/* 工具栏 */}
|
||||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold">SNMP 监控</h2>
|
<h2 className="text-lg font-semibold">{t('monitor.lbl.snmpMonitorTitle')}</h2>
|
||||||
<p className="text-xs text-[var(--dg-text-muted)]">5 分钟采集 · 指标保留 7 天</p>
|
<p className="text-xs text-[var(--dg-text-muted)]">{t('monitor.lbl.snmpCollectInterval')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button size="sm" variant="outline" onClick={refresh} className="h-8">
|
<Button size="sm" variant="outline" onClick={refresh} className="h-8">
|
||||||
@@ -208,7 +208,7 @@ export function SNMPMonitor() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="grid gap-1.5">
|
<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" />
|
<Input value={form.ip} onChange={(e) => setForm({ ...form, ip: e.target.value })} placeholder="192.168.1.1" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-1.5">
|
<div className="grid gap-1.5">
|
||||||
@@ -217,7 +217,7 @@ export function SNMPMonitor() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-1.5">
|
<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 })}>
|
<Select value={form.template_id} onValueChange={(v) => setForm({ ...form, template_id: v })}>
|
||||||
<SelectTrigger><SelectValue placeholder={t('pipeline.selectTemplate')} /></SelectTrigger>
|
<SelectTrigger><SelectValue placeholder={t('pipeline.selectTemplate')} /></SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -229,7 +229,7 @@ export function SNMPMonitor() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="grid gap-1.5">
|
<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' })}>
|
<Select value={form.snmp_version} onValueChange={(v) => setForm({ ...form, snmp_version: v as 'v2c' | 'v3' })}>
|
||||||
<SelectTrigger><SelectValue /></SelectTrigger>
|
<SelectTrigger><SelectValue /></SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@@ -247,7 +247,7 @@ export function SNMPMonitor() {
|
|||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button variant="outline" onClick={() => setDialogOpen(false)}> {t('rbac.jobRole.cancel')} </Button>
|
<Button variant="outline" onClick={() => setDialogOpen(false)}> {t('rbac.jobRole.cancel')} </Button>
|
||||||
<Button onClick={handleAdd} disabled={submitting}>
|
<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>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
@@ -278,7 +278,7 @@ export function SNMPMonitor() {
|
|||||||
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
<Card className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)]">
|
||||||
<CardContent className="py-12 text-center text-sm text-[var(--dg-text-muted)]">
|
<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" />
|
<Router className="h-10 w-10 mx-auto mb-3 opacity-40" />
|
||||||
暂无 SNMP 设备,点击「添加设备」开始监控
|
{t('monitor.lbl.snmpNoDevice')}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
@@ -348,14 +348,14 @@ export function SNMPMonitor() {
|
|||||||
<Dialog open={metricsOpen} onOpenChange={setMetricsOpen}>
|
<Dialog open={metricsOpen} onOpenChange={setMetricsOpen}>
|
||||||
<DialogContent className="sm:max-w-2xl max-h-[80vh] overflow-y-auto bg-[var(--dg-bg-secondary)]">
|
<DialogContent className="sm:max-w-2xl max-h-[80vh] overflow-y-auto bg-[var(--dg-bg-secondary)]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{metricsDevice?.name} — 采集指标</DialogTitle>
|
<DialogTitle>{metricsDevice?.name} — {t('monitor.lbl.snmpCollectMetrics')}</DialogTitle>
|
||||||
<DialogDescription>{metricsDevice?.ip} · {templateName(metricsDevice?.template_id || 0)}</DialogDescription>
|
<DialogDescription>{metricsDevice?.ip} · {templateName(metricsDevice?.template_id || 0)}</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{metricsLoading ? (
|
{metricsLoading ? (
|
||||||
<div className="py-8 text-center text-sm text-muted-foreground"> {t('monitor.logPanel.logResults.loading')} </div>
|
<div className="py-8 text-center text-sm text-muted-foreground"> {t('monitor.logPanel.logResults.loading')} </div>
|
||||||
) : metrics.length === 0 ? (
|
) : metrics.length === 0 ? (
|
||||||
<div className="py-8 text-center text-sm text-muted-foreground">
|
<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>}
|
{metricsDevice?.last_error && <p className="mt-2 text-xs dg-text-red">{metricsDevice.last_error}</p>}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export function SiteMonitor() {
|
|||||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold"> {t('monitor.alertRulesTab.catSite')} </h2>
|
<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>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button size="sm" variant="outline" className="h-8" onClick={() => { loadStats(); loadTasks(); }} disabled={refreshing}>
|
<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 { ChartToolbar } from './ChartToolbar';
|
||||||
import { MetricIconBox } from './MetricIconBox';
|
import { MetricIconBox } from './MetricIconBox';
|
||||||
import type { AggType, BucketPoint } from './types';
|
import type { AggType, BucketPoint } from './types';
|
||||||
import i18n from '@/i18n';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
interface DualLineChartProps {
|
interface DualLineChartProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -21,6 +21,7 @@ interface DualLineChartProps {
|
|||||||
export function DualLineChart({
|
export function DualLineChart({
|
||||||
title, currentRx, currentTx, rxData, txData, aggType, unit,
|
title, currentRx, currentTx, rxData, txData, aggType, unit,
|
||||||
}: DualLineChartProps) {
|
}: DualLineChartProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const chartRef = useRef<ZoomableChartHandle>(null);
|
const chartRef = useRef<ZoomableChartHandle>(null);
|
||||||
const [zoomed, setZoomed] = useState(false);
|
const [zoomed, setZoomed] = useState(false);
|
||||||
|
|
||||||
@@ -43,11 +44,11 @@ export function DualLineChart({
|
|||||||
<div className="flex items-center gap-3 text-xs">
|
<div className="flex items-center gap-3 text-xs">
|
||||||
<span>
|
<span>
|
||||||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-blue-400 mr-1 align-middle" />
|
<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>
|
<span>
|
||||||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-emerald-400 mr-1 align-middle" />
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ChartToolbar
|
<ChartToolbar
|
||||||
@@ -61,14 +62,14 @@ export function DualLineChart({
|
|||||||
ref={chartRef}
|
ref={chartRef}
|
||||||
data={chartData}
|
data={chartData}
|
||||||
series={[
|
series={[
|
||||||
{ dataKey: 'rx', color: '#3b82f6', label: i18n.t('common.receive') },
|
{ dataKey: 'rx', color: '#3b82f6', label: t('common.receive') },
|
||||||
{ dataKey: 'tx', color: '#10b981', label: i18n.t('aiops.sendMessage') },
|
{ dataKey: 'tx', color: '#10b981', label: t('aiops.sendMessage') },
|
||||||
]}
|
]}
|
||||||
yDomain={[0, 'auto']}
|
yDomain={[0, 'auto']}
|
||||||
unit={unit}
|
unit={unit}
|
||||||
chartId={`dual-${title.replace(/\s/g, '')}`}
|
chartId={`dual-${title.replace(/\s/g, '')}`}
|
||||||
title={title}
|
title={title}
|
||||||
subtitle={i18n.t('monitor.lbl.receiveSend', { rx: currentRx, tx: currentTx })}
|
subtitle={t('monitor.lbl.receiveSend', { rx: currentRx, tx: currentTx })}
|
||||||
height={208}
|
height={208}
|
||||||
toolbarPlacement="header"
|
toolbarPlacement="header"
|
||||||
onZoomedChange={setZoomed}
|
onZoomedChange={setZoomed}
|
||||||
|
|||||||
@@ -200,9 +200,9 @@ export function HealthProbeSection({ selectedHost }: HealthProbeSectionProps) {
|
|||||||
|
|
||||||
{probeForm.probe_type === 'tcp_socket' && (
|
{probeForm.probe_type === 'tcp_socket' && (
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<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>
|
<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>
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export function HostTimeRangePicker({ value, onChange }: Props) {
|
|||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<span className="text-[11px] dg-text-muted hidden sm:inline-flex items-center gap-1">
|
<span className="text-[11px] dg-text-muted hidden sm:inline-flex items-center gap-1">
|
||||||
<Timer className="h-3 w-3" />
|
<Timer className="h-3 w-3" />
|
||||||
时间范围
|
{t('monitor.lbl.timeRange')}
|
||||||
</span>
|
</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">
|
<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) => (
|
{TIME_RANGE_PRESETS.map((p) => (
|
||||||
@@ -82,7 +82,7 @@ export function HostTimeRangePicker({ value, onChange }: Props) {
|
|||||||
onClick={() => setCustomOpen((v) => !v)}
|
onClick={() => setCustomOpen((v) => !v)}
|
||||||
>
|
>
|
||||||
<Calendar className="h-3 w-3" />
|
<Calendar className="h-3 w-3" />
|
||||||
自定义
|
{t('monitor.lbl.custom')}
|
||||||
<ChevronDown className={`h-3 w-3 transition-transform ${customOpen ? 'rotate-180' : ''}`} />
|
<ChevronDown className={`h-3 w-3 transition-transform ${customOpen ? 'rotate-180' : ''}`} />
|
||||||
</Button>
|
</Button>
|
||||||
{customOpen && (
|
{customOpen && (
|
||||||
@@ -110,10 +110,10 @@ export function HostTimeRangePicker({ value, onChange }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end gap-2 mt-3">
|
<div className="flex justify-end gap-2 mt-3">
|
||||||
<Button type="button" variant="ghost" size="sm" className="h-7 text-xs" onClick={() => setCustomOpen(false)}>
|
<Button type="button" variant="ghost" size="sm" className="h-7 text-xs" onClick={() => setCustomOpen(false)}>
|
||||||
取消
|
{t('common.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" size="sm" className="h-7 text-xs dg-btn-primary" onClick={applyCustom}>
|
<Button type="button" size="sm" className="h-7 text-xs dg-btn-primary" onClick={applyCustom}>
|
||||||
应用
|
{t('monitor.lbl.apply')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</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)]'}`}
|
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')}
|
onClick={() => onModeChange('whitelist')}
|
||||||
>
|
>
|
||||||
白名单 ─ 全量上报
|
{t('monitor.lbl.whitelistFullReport')}
|
||||||
</button>
|
</button>
|
||||||
<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)]'}`}
|
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')}
|
onClick={() => onModeChange('blacklist')}
|
||||||
>
|
>
|
||||||
黑名单 ─ 仅上报选中
|
{t('monitor.lbl.blacklistSelectedOnly')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1.5 leading-relaxed">
|
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1.5 leading-relaxed">
|
||||||
@@ -61,8 +61,8 @@ export function PortPolicyDialog({
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[11px] text-[var(--dg-text-muted)] mb-1.5 block">
|
<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>
|
{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">— 白名单模式无需配置</span>}
|
{portPolicyMode === 'whitelist' && <span className="dg-text-blue ml-1">{t('monitor.lbl.whitelistNoConfig')}</span>}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<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 ${
|
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'}
|
readOnly={portPolicyMode === 'whitelist'}
|
||||||
/>
|
/>
|
||||||
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1">
|
<p className="text-[10px] text-[var(--dg-text-muted)] mt-1">
|
||||||
已输入 {portWhitelist.length} 个端口
|
{t('monitor.lbl.portsEntered', { count: portWhitelist.length })}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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"
|
className="flex items-center justify-center text-xs dg-text-muted border border-dashed border-[var(--dg-border)] rounded-md w-full"
|
||||||
style={{ height }}
|
style={{ height }}
|
||||||
>
|
>
|
||||||
暂无数据
|
{t('monitor.lbl.noData')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ export function ContextSwitchCard({ csInfo }: { csInfo: JsonData }) {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-3 text-center">
|
<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">
|
<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>
|
{(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>
|
||||||
<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>
|
||||||
<div className="bg-[var(--dg-bg-primary)] rounded-lg p-3 text-center">
|
<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">
|
<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>
|
{(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>
|
||||||
<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>
|
</div>
|
||||||
<div className="space-y-1">
|
<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">
|
<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">
|
<h3 className="text-xs font-medium text-[var(--dg-text-primary)] flex items-center gap-2 leading-8">
|
||||||
<MetricIconBox icon={<HardDrive />} tone="purple" />
|
<MetricIconBox icon={<HardDrive />} tone="purple" />
|
||||||
FD 文件句柄
|
{t('monitor.lbl.fdHandles')}
|
||||||
</h3>
|
</h3>
|
||||||
{fdMetrics.length > 0 && (
|
{fdMetrics.length > 0 && (
|
||||||
<ChartToolbar
|
<ChartToolbar
|
||||||
|
|||||||
@@ -23,22 +23,22 @@ export function PortMonitorTab({ portInfo, portPolicyMode, portWhitelist, onOpen
|
|||||||
<span className="ml-auto flex items-center gap-2">
|
<span className="ml-auto flex items-center gap-2">
|
||||||
{portPolicyMode === 'blacklist' && portWhitelist.length > 0 && (
|
{portPolicyMode === 'blacklist' && portWhitelist.length > 0 && (
|
||||||
<Badge variant="outline" className="text-[10px] dg-text-yellow border-amber-500/30">
|
<Badge variant="outline" className="text-[10px] dg-text-yellow border-amber-500/30">
|
||||||
黑名单: {portWhitelist.join(',')}
|
{t('monitor.lbl.blacklistLabel')}: {portWhitelist.join(',')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{portPolicyMode === 'whitelist' && (
|
{portPolicyMode === 'whitelist' && (
|
||||||
<Badge variant="outline" className="text-[10px] dg-text-blue border-blue-500/30">
|
<Badge variant="outline" className="text-[10px] dg-text-blue border-blue-500/30">
|
||||||
白名单模式
|
{t('monitor.lbl.whitelistModeLabel')}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<Button variant="outline" size="sm" className="h-6 text-[10px] px-2" onClick={onOpenPortPolicy}>
|
<Button variant="outline" size="sm" className="h-6 text-[10px] px-2" onClick={onOpenPortPolicy}>
|
||||||
策略配置
|
{t('monitor.lbl.policyConfig')}
|
||||||
</Button>
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
{portInfo.length === 0 ? (
|
{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">
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function ProcessTable({ processTop }: { processTop: JsonData[] }) {
|
|||||||
</h3>
|
</h3>
|
||||||
{processTop.length === 0 ? (
|
{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">
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<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-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-[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%]"> {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>
|
<th className="py-1.5 text-right font-medium w-[20%]"> {t('monitor.lbl.processMonitor')} </th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export function SiteResultsPanel({ task, onProbed, fullPage = false }: Props) {
|
|||||||
if (!task) {
|
if (!task) {
|
||||||
const empty = (
|
const empty = (
|
||||||
<div className="p-8 text-center text-sm text-[var(--dg-text-muted)]">
|
<div className="p-8 text-center text-sm text-[var(--dg-text-muted)]">
|
||||||
选择任务查看协议专属可观测指标
|
{t('monitor.lbl.selectTaskHint')}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@@ -218,7 +218,7 @@ export function SiteResultsPanel({ task, onProbed, fullPage = false }: Props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<span className="text-[10px] text-[var(--dg-text-muted)] shrink-0">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5 shrink-0">
|
<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")}
|
<RefreshCw className={`h-3 w-3 mr-1 ${loading ? 'animate-spin' : ''}`} />{t("common.refresh")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" className="h-7 text-xs" onClick={handleProbeNow} disabled={probing}>
|
<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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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" />
|
<ShieldAlert className="h-4 w-4 shrink-0 mt-0.5 dg-text-yellow" />
|
||||||
<div>
|
<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)]">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user