From 5013290486efd7eab9f0e79da1af7c7c6fd91167 Mon Sep 17 00:00:00 2001 From: IanShaw027 <131567472+IanShaw027@users.noreply.github.com> Date: Wed, 14 Jan 2026 12:41:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E4=BC=98=E5=8C=96ops?= =?UTF-8?q?=E7=9B=91=E6=8E=A7UI=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ops/components/OpsErrorDetailModal.vue | 100 ++++--- .../ops/components/OpsErrorDetailsModal.vue | 77 ++--- .../admin/ops/components/OpsErrorLogTable.vue | 262 +++++++----------- .../ops/components/OpsSettingsDialog.vue | 22 +- 4 files changed, 212 insertions(+), 249 deletions(-) diff --git a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue index b60b45c3..9a535d32 100644 --- a/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue +++ b/frontend/src/views/admin/ops/components/OpsErrorDetailModal.vue @@ -101,7 +101,7 @@ -
+

{{ t('admin.ops.errorDetail.suggestion') || 'Suggestion' }}

{{ handlingSuggestion }} @@ -150,29 +150,6 @@
- -
-

{{ t('admin.ops.errorDetail.retrySummary') || 'Retry Summary' }}

-
-
-
total
-
{{ retryHistory.length }}
-
-
-
succeeded
-
{{ retryHistory.filter(r => r.success === true).length }}
-
-
-
failed
-
{{ retryHistory.filter(r => r.success === false).length }}
-
-
-
last
-
{{ retryHistory[0]?.created_at || '—' }}
-
-
-
-

{{ t('admin.ops.errorDetail.basicInfo') }}

@@ -186,9 +163,21 @@
{{ detail.model || '—' }}
-
{{ t('admin.ops.errorDetail.latency') }}
-
- {{ detail.latency_ms != null ? `${detail.latency_ms}ms` : '—' }} +
{{ t('admin.ops.errorDetail.group') }}
+
+ + {{ detail.group_name || detail.group_id }} + + +
+
+
+
{{ t('admin.ops.errorDetail.account') }}
+
+ + {{ detail.account_name || detail.account_id }} + +
@@ -203,7 +192,7 @@ {{ detail.is_business_limited ? 'true' : 'false' }}
-
+
{{ t('admin.ops.errorDetail.requestPath') }}
{{ detail.request_path || '—' }} @@ -343,11 +332,17 @@
-
-
account_id: {{ ev.account_id ?? '—' }}
-
status: {{ ev.upstream_status_code ?? '—' }}
-
- request_id: {{ ev.upstream_request_id || '—' }} +
+
+ account: + + {{ ev.account_name || ev.account_id }} + + +
+
status: {{ ev.upstream_status_code ?? '—' }}
+
+ request_id: {{ ev.upstream_request_id || '—' }}
@@ -426,13 +421,29 @@
{{ selectedA ? `#${selectedA.id} · ${selectedA.mode} · ${selectedA.status}` : '—' }}
-
http: {{ selectedA?.http_status_code ?? '—' }} · used: {{ selectedA?.used_account_id ?? '—' }}
+
+ http: {{ selectedA?.http_status_code ?? '—' }} · + used: + + {{ selectedA.used_account_name || selectedA.used_account_id }} + + + +
{{ selectedA?.response_preview || '' }}
{{ selectedA.error_message }}
{{ selectedB ? `#${selectedB.id} · ${selectedB.mode} · ${selectedB.status}` : '—' }}
-
http: {{ selectedB?.http_status_code ?? '—' }} · used: {{ selectedB?.used_account_id ?? '—' }}
+
+ http: {{ selectedB?.http_status_code ?? '—' }} · + used: + + {{ selectedB.used_account_name || selectedB.used_account_id }} + + + +
{{ selectedB?.response_preview || '' }}
{{ selectedB.error_message }}
@@ -447,8 +458,20 @@
success: {{ a.success ?? '—' }}
http: {{ a.http_status_code ?? '—' }}
-
pinned: {{ a.pinned_account_id ?? '—' }}
-
used: {{ a.used_account_id ?? '—' }}
+
+ pinned: + + {{ a.pinned_account_name || a.pinned_account_id }} + + +
+
+ used: + + {{ a.used_account_name || a.used_account_id }} + + +
{{ a.response_preview }}
{{ a.error_message }}
@@ -558,6 +581,7 @@ type UpstreamErrorEvent = { at_unix_ms?: number platform?: string account_id?: number + account_name?: string upstream_status_code?: number upstream_request_id?: string kind?: string @@ -777,4 +801,4 @@ async function runConfirmedRetry() { function cancelRetry() { showRetryConfirm.value = false } - + \ No newline at end of file diff --git a/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue b/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue index 1d9859d4..0abe183a 100644 --- a/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue +++ b/frontend/src/views/admin/ops/components/OpsErrorDetailsModal.vue @@ -33,14 +33,6 @@ const statusCode = ref(null) const phase = ref('') const errorOwner = ref('') const resolvedStatus = ref('unresolved') -const accountIdInput = ref('') - -const accountId = computed(() => { - const raw = String(accountIdInput.value || '').trim() - if (!raw) return null - const n = Number.parseInt(raw, 10) - return Number.isFinite(n) && n > 0 ? n : null -}) const modalTitle = computed(() => { return props.errorType === 'upstream' ? t('admin.ops.errorDetails.upstreamErrors') : t('admin.ops.errorDetails.requestErrors') @@ -105,7 +97,6 @@ async function fetchErrorLogs() { if (q.value.trim()) params.q = q.value.trim() if (typeof statusCode.value === 'number') params.status_codes = String(statusCode.value) - if (typeof accountId.value === 'number') params.account_id = accountId.value const phaseVal = String(phase.value || '').trim() if (phaseVal) params.phase = phaseVal @@ -136,7 +127,6 @@ function resetFilters() { phase.value = props.errorType === 'upstream' ? 'upstream' : '' errorOwner.value = '' resolvedStatus.value = 'unresolved' - accountIdInput.value = '' page.value = 1 fetchErrorLogs() } @@ -189,15 +179,6 @@ watch( fetchErrorLogs() } ) - -watch( - () => accountId.value, - () => { - if (!props.show) return - page.value = 1 - fetchErrorLogs() - } -) + + diff --git a/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue b/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue index 6ef455e9..3e7424df 100644 --- a/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue +++ b/frontend/src/views/admin/ops/components/OpsErrorLogTable.vue @@ -1,61 +1,48 @@ @@ -212,39 +173,32 @@ import Pagination from '@/components/common/Pagination.vue' import type { OpsErrorLog } from '@/api/admin/ops' import { getSeverityClass, formatDateTime } from '../utils/opsFormatters' +const { t } = useI18n() + function getTypeBadge(log: OpsErrorLog): { label: string; className: string } { const phase = String(log.phase || '').toLowerCase() - const owner = String((log as any).error_owner || '').toLowerCase() + const owner = String(log.error_owner || '').toLowerCase() - // Mapping aligned with the design: - // - upstream/provider => 🔴 上游 - // - request/client => 🟡 请求 - // - auth/client => 🔵 认证 - // - routing/platform => 🟣 路由 - // - internal/platform => ⚫ 内部 if (phase === 'upstream' && owner === 'provider') { - return { label: '🔴 上游', className: 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-900/30 dark:text-red-400 dark:ring-red-500/30' } + return { label: t('admin.ops.errorLog.typeUpstream'), className: 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-900/30 dark:text-red-400 dark:ring-red-500/30' } } if (phase === 'request' && owner === 'client') { - return { label: '🟡 请求', className: 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-900/30 dark:text-amber-400 dark:ring-amber-500/30' } + return { label: t('admin.ops.errorLog.typeRequest'), className: 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-900/30 dark:text-amber-400 dark:ring-amber-500/30' } } if (phase === 'auth' && owner === 'client') { - return { label: '🔵 认证', className: 'bg-blue-50 text-blue-700 ring-blue-600/20 dark:bg-blue-900/30 dark:text-blue-400 dark:ring-blue-500/30' } + return { label: t('admin.ops.errorLog.typeAuth'), className: 'bg-blue-50 text-blue-700 ring-blue-600/20 dark:bg-blue-900/30 dark:text-blue-400 dark:ring-blue-500/30' } } if (phase === 'routing' && owner === 'platform') { - return { label: '🟣 路由', className: 'bg-purple-50 text-purple-700 ring-purple-600/20 dark:bg-purple-900/30 dark:text-purple-400 dark:ring-purple-500/30' } + return { label: t('admin.ops.errorLog.typeRouting'), className: 'bg-purple-50 text-purple-700 ring-purple-600/20 dark:bg-purple-900/30 dark:text-purple-400 dark:ring-purple-500/30' } } if (phase === 'internal' && owner === 'platform') { - return { label: '⚫ 内部', className: 'bg-gray-100 text-gray-800 ring-gray-600/20 dark:bg-dark-700 dark:text-gray-200 dark:ring-dark-500/40' } + return { label: t('admin.ops.errorLog.typeInternal'), className: 'bg-gray-100 text-gray-800 ring-gray-600/20 dark:bg-dark-700 dark:text-gray-200 dark:ring-dark-500/40' } } - // Fallback: show phase/owner for unknown combos. const fallback = phase || owner || 'unknown' return { label: fallback, className: 'bg-gray-50 text-gray-700 ring-gray-600/10 dark:bg-dark-900 dark:text-gray-300 dark:ring-dark-700' } } -const { t } = useI18n() - interface Props { rows: OpsErrorLog[] total: number @@ -269,14 +223,6 @@ function getStatusClass(code: number): string { return 'bg-gray-50 text-gray-700 ring-gray-600/20 dark:bg-gray-900/30 dark:text-gray-400 dark:ring-gray-500/30' } -function getLatencyClass(latency: number | null): string { - if (!latency) return 'text-gray-400' - if (latency > 10000) return 'text-red-600 font-black' - if (latency > 5000) return 'text-red-500 font-bold' - if (latency > 2000) return 'text-orange-500 font-medium' - return 'text-gray-600 dark:text-gray-400' -} - function formatSmartMessage(msg: string): string { if (!msg) return '' @@ -298,4 +244,4 @@ function formatSmartMessage(msg: string): string { return msg.length > 200 ? msg.substring(0, 200) + '...' : msg } - + \ No newline at end of file diff --git a/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue b/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue index c8291313..4d737c1b 100644 --- a/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue +++ b/frontend/src/views/admin/ops/components/OpsSettingsDialog.vue @@ -480,11 +480,31 @@ async function saveAllSettings() {

- 启用后,count_tokens 请求的错误将不计入运维监控的统计和告警中(但仍会存储在数据库中) + 启用后,count_tokens 请求的错误将不会写入错误日志

+ +
+
+ +

+ 启用后,客户端主动断开连接(context canceled)的错误将不会写入错误日志 +

+
+ +
+ +
+
+ +

+ 启用后,"No available accounts" 错误将不会写入错误日志(不推荐,这通常是配置问题) +

+
+ +