From 8ae75e7f6ecc21fe7e45df143ff0fbe602d95be6 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:00:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=89=8D=E7=AB=AFUI):=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E8=BF=90=E7=BB=B4=E7=9B=91=E6=8E=A7=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增帮助提示组件(HelpTooltip.vue) - 更新侧边栏添加 ops 监控菜单项 - 扩展设置视图集成 ops 配置面板 - 新增 ops 监控视图目录(dashboard, alerts, realtime, settings 等) --- .../src/components/common/HelpTooltip.vue | 44 + frontend/src/components/layout/AppSidebar.vue | 25 +- frontend/src/views/admin/SettingsView.vue | 104 ++- frontend/src/views/admin/ops/OpsDashboard.vue | 854 ++++++++++++++++++ .../ops/components/OpsAlertEventsCard.vue | 165 ++++ .../ops/components/OpsAlertRulesCard.vue | 357 ++++++++ .../ops/components/OpsConcurrencyCard.vue | 525 +++++++++++ .../ops/components/OpsDashboardHeader.vue | 374 ++++++++ .../ops/components/OpsDashboardSkeleton.vue | 53 ++ .../components/OpsEmailNotificationCard.vue | 441 +++++++++ .../ops/components/OpsErrorDetailModal.vue | 360 ++++++++ .../ops/components/OpsErrorDetailsModal.vue | 293 ++++++ .../components/OpsErrorDistributionChart.vue | 157 ++++ .../admin/ops/components/OpsErrorLogTable.vue | 238 +++++ .../ops/components/OpsErrorTrendChart.vue | 185 ++++ .../admin/ops/components/OpsLatencyChart.vue | 101 +++ .../ops/components/OpsRequestDetailsModal.vue | 309 +++++++ .../ops/components/OpsRuntimeSettingsCard.vue | 439 +++++++++ .../components/OpsThroughputTrendChart.vue | 252 ++++++ frontend/src/views/admin/ops/types.ts | 17 + .../views/admin/ops/utils/opsFormatters.ts | 75 ++ 21 files changed, 5362 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/common/HelpTooltip.vue create mode 100644 frontend/src/views/admin/ops/OpsDashboard.vue create mode 100644 frontend/src/views/admin/ops/components/OpsAlertEventsCard.vue create mode 100644 frontend/src/views/admin/ops/components/OpsAlertRulesCard.vue create mode 100644 frontend/src/views/admin/ops/components/OpsConcurrencyCard.vue create mode 100644 frontend/src/views/admin/ops/components/OpsDashboardHeader.vue create mode 100644 frontend/src/views/admin/ops/components/OpsDashboardSkeleton.vue create mode 100644 frontend/src/views/admin/ops/components/OpsEmailNotificationCard.vue create mode 100644 frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue create mode 100644 frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue create mode 100644 frontend/src/views/admin/ops/components/OpsErrorDistributionChart.vue create mode 100644 frontend/src/views/admin/ops/components/OpsErrorLogTable.vue create mode 100644 frontend/src/views/admin/ops/components/OpsErrorTrendChart.vue create mode 100644 frontend/src/views/admin/ops/components/OpsLatencyChart.vue create mode 100644 frontend/src/views/admin/ops/components/OpsRequestDetailsModal.vue create mode 100644 frontend/src/views/admin/ops/components/OpsRuntimeSettingsCard.vue create mode 100644 frontend/src/views/admin/ops/components/OpsThroughputTrendChart.vue create mode 100644 frontend/src/views/admin/ops/types.ts create mode 100644 frontend/src/views/admin/ops/utils/opsFormatters.ts diff --git a/frontend/src/components/common/HelpTooltip.vue b/frontend/src/components/common/HelpTooltip.vue new file mode 100644 index 00000000..7679ced4 --- /dev/null +++ b/frontend/src/components/common/HelpTooltip.vue @@ -0,0 +1,44 @@ + + + + diff --git a/frontend/src/components/layout/AppSidebar.vue b/frontend/src/components/layout/AppSidebar.vue index 791327a1..78217ec8 100644 --- a/frontend/src/components/layout/AppSidebar.vue +++ b/frontend/src/components/layout/AppSidebar.vue @@ -144,10 +144,10 @@ diff --git a/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue new file mode 100644 index 00000000..c2c6adb6 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsDashboardHeader.vue @@ -0,0 +1,374 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsDashboardSkeleton.vue b/frontend/src/views/admin/ops/components/OpsDashboardSkeleton.vue new file mode 100644 index 00000000..5bbadd03 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsDashboardSkeleton.vue @@ -0,0 +1,53 @@ + + diff --git a/frontend/src/views/admin/ops/components/OpsEmailNotificationCard.vue b/frontend/src/views/admin/ops/components/OpsEmailNotificationCard.vue new file mode 100644 index 00000000..0204cbeb --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsEmailNotificationCard.vue @@ -0,0 +1,441 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue new file mode 100644 index 00000000..118a1f3a --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue @@ -0,0 +1,360 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue b/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue new file mode 100644 index 00000000..f4a522de --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue @@ -0,0 +1,293 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsErrorDistributionChart.vue b/frontend/src/views/admin/ops/components/OpsErrorDistributionChart.vue new file mode 100644 index 00000000..a52b5442 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsErrorDistributionChart.vue @@ -0,0 +1,157 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue b/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue new file mode 100644 index 00000000..6a4be1a7 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue @@ -0,0 +1,238 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsErrorTrendChart.vue b/frontend/src/views/admin/ops/components/OpsErrorTrendChart.vue new file mode 100644 index 00000000..032e1205 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsErrorTrendChart.vue @@ -0,0 +1,185 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsLatencyChart.vue b/frontend/src/views/admin/ops/components/OpsLatencyChart.vue new file mode 100644 index 00000000..c62b3aa9 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsLatencyChart.vue @@ -0,0 +1,101 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsRequestDetailsModal.vue b/frontend/src/views/admin/ops/components/OpsRequestDetailsModal.vue new file mode 100644 index 00000000..541aa3ed --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsRequestDetailsModal.vue @@ -0,0 +1,309 @@ + + + diff --git a/frontend/src/views/admin/ops/components/OpsRuntimeSettingsCard.vue b/frontend/src/views/admin/ops/components/OpsRuntimeSettingsCard.vue new file mode 100644 index 00000000..e9df347d --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsRuntimeSettingsCard.vue @@ -0,0 +1,439 @@ + + + + diff --git a/frontend/src/views/admin/ops/components/OpsThroughputTrendChart.vue b/frontend/src/views/admin/ops/components/OpsThroughputTrendChart.vue new file mode 100644 index 00000000..e3bd26c2 --- /dev/null +++ b/frontend/src/views/admin/ops/components/OpsThroughputTrendChart.vue @@ -0,0 +1,252 @@ + + + diff --git a/frontend/src/views/admin/ops/types.ts b/frontend/src/views/admin/ops/types.ts new file mode 100644 index 00000000..08830542 --- /dev/null +++ b/frontend/src/views/admin/ops/types.ts @@ -0,0 +1,17 @@ +// Ops 前端视图层的共享类型(与后端 DTO 解耦)。 + +export type ChartState = 'loading' | 'empty' | 'ready' + +// Re-export ops alert/settings types so view components can import from a single place +// while keeping the API contract centralized in `@/api/admin/ops`. +export type { + AlertRule, + AlertEvent, + AlertSeverity, + ThresholdMode, + MetricType, + Operator, + EmailNotificationConfig, + OpsDistributedLockSettings, + OpsAlertRuntimeSettings +} from '@/api/admin/ops' diff --git a/frontend/src/views/admin/ops/utils/opsFormatters.ts b/frontend/src/views/admin/ops/utils/opsFormatters.ts new file mode 100644 index 00000000..d503b5a5 --- /dev/null +++ b/frontend/src/views/admin/ops/utils/opsFormatters.ts @@ -0,0 +1,75 @@ +/** + * Ops 页面共享的格式化/样式工具。 + * + * 目标:尽量对齐 `docs/sub2api` 备份版本的视觉表现(需求一致部分保持一致), + * 同时避免引入额外 UI 依赖。 + */ + +import type { OpsSeverity } from '@/api/admin/ops' +import { formatBytes } from '@/utils/format' + +export function getSeverityClass(severity: OpsSeverity): string { + const classes: Record = { + P0: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400', + P1: 'bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400', + P2: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400', + P3: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400' + } + return classes[String(severity || '')] || classes.P3 +} + +export function truncateMessage(msg: string, maxLength = 80): string { + if (!msg) return '' + return msg.length > maxLength ? msg.substring(0, maxLength) + '...' : msg +} + +/** + * 格式化日期时间(短格式,和旧 Ops 页面一致)。 + * 输出: `MM-DD HH:mm:ss` + */ +export function formatDateTime(dateStr: string): string { + const d = new Date(dateStr) + if (Number.isNaN(d.getTime())) return '' + return `${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}` +} + +export function sumNumbers(values: Array): number { + return values.reduce((acc, v) => { + const n = typeof v === 'number' && Number.isFinite(v) ? v : 0 + return acc + n + }, 0) +} + +/** + * 解析 time_range 为分钟数。 + * 支持:`5m/30m/1h/6h/24h` + */ +export function parseTimeRangeMinutes(range: string): number { + const trimmed = (range || '').trim() + if (!trimmed) return 60 + if (trimmed.endsWith('m')) { + const v = Number.parseInt(trimmed.slice(0, -1), 10) + return Number.isFinite(v) && v > 0 ? v : 60 + } + if (trimmed.endsWith('h')) { + const v = Number.parseInt(trimmed.slice(0, -1), 10) + return Number.isFinite(v) && v > 0 ? v * 60 : 60 + } + return 60 +} + +export function formatHistoryLabel(date: string | undefined, timeRange: string): string { + if (!date) return '' + const d = new Date(date) + if (Number.isNaN(d.getTime())) return '' + const minutes = parseTimeRangeMinutes(timeRange) + if (minutes >= 24 * 60) { + return `${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}` + } + return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}` +} + +export function formatByteRate(bytes: number, windowMinutes: number): string { + const seconds = Math.max(1, (windowMinutes || 1) * 60) + return `${formatBytes(bytes / seconds, 1)}/s` +}