From 1a0a3de2fc8ac5a256d2e0e75822a117aa39b1a8 Mon Sep 17 00:00:00 2001 From: fawney19 Date: Tue, 13 Jan 2026 18:42:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E4=BC=98=E5=8C=96=E7=86=94?= =?UTF-8?q?=E6=96=AD=E7=8A=B6=E6=80=81=E6=98=BE=E7=A4=BA=E5=92=8C=E5=81=A5?= =?UTF-8?q?=E5=BA=B7=E5=BA=A6=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 健康度格式统一为 0-1 小数格式(前后端同步) - 添加熔断倒计时显示,支持实时更新 - 新增 useCountdownTimer composable 用于倒计时逻辑 - 添加 Key 健康恢复功能(按 API 格式恢复) - 优化 UI 布局:简化链路预览标题、调整 Key 信息为两行显示 - 修复错误处理顺序(先刷新数据再显示成功提示) - 减少模板中函数重复调用 --- frontend/src/api/endpoints/types.ts | 3 +- frontend/src/composables/useCountdownTimer.ts | 69 ++++ .../models/components/ModelAliasesTab.vue | 43 ++- .../features/models/components/RoutingTab.vue | 309 +++++++++--------- .../components/ProviderDetailDrawer.vue | 85 +++-- src/api/admin/models/routing.py | 16 +- 6 files changed, 317 insertions(+), 208 deletions(-) create mode 100644 frontend/src/composables/useCountdownTimer.ts diff --git a/frontend/src/api/endpoints/types.ts b/frontend/src/api/endpoints/types.ts index a8d49e8..3dc3f4b 100644 --- a/frontend/src/api/endpoints/types.ts +++ b/frontend/src/api/endpoints/types.ts @@ -638,12 +638,13 @@ export interface RoutingKeyInfo { is_adaptive: boolean effective_rpm?: number | null cache_ttl_minutes: number - health_score: number + health_score: number // 0-1 小数格式 is_active: boolean api_formats: string[] allowed_models?: string[] | null // 允许的模型列表,null 表示不限制 circuit_breaker_open: boolean circuit_breaker_formats: string[] + next_probe_at?: string | null // 下次探测时间(ISO格式) } /** diff --git a/frontend/src/composables/useCountdownTimer.ts b/frontend/src/composables/useCountdownTimer.ts new file mode 100644 index 0000000..e58811b --- /dev/null +++ b/frontend/src/composables/useCountdownTimer.ts @@ -0,0 +1,69 @@ +import { ref, onUnmounted } from 'vue' + +/** + * 倒计时定时器 composable + * 用于触发组件的定期响应式更新(如熔断探测倒计时) + */ +export function useCountdownTimer() { + const tick = ref(0) + let timer: ReturnType | null = null + + function start() { + if (timer) return + timer = setInterval(() => { + tick.value++ + }, 1000) + } + + function stop() { + if (timer) { + clearInterval(timer) + timer = null + } + } + + onUnmounted(stop) + + return { tick, start, stop } +} + +/** + * 格式化倒计时时间 + * @param diffMs 剩余毫秒数 + * @returns 格式化的倒计时字符串(如 "1:30" 或 "1:02:30") + */ +export function formatCountdown(diffMs: number): string { + const totalSeconds = Math.ceil(diffMs / 1000) + if (totalSeconds <= 0) return '探测中' + + const hours = Math.floor(totalSeconds / 3600) + const minutes = Math.floor((totalSeconds % 3600) / 60) + const seconds = totalSeconds % 60 + + if (hours > 0) { + return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` + } + return `${minutes}:${seconds.toString().padStart(2, '0')}` +} + +/** + * 计算探测倒计时 + * @param nextProbeAt ISO 格式的探测时间字符串 + * @param _tick 响应式触发器(传入 tick.value 以触发响应式更新) + * @returns 倒计时字符串,或 null(如果无需显示) + */ +export function getProbeCountdown(nextProbeAt: string | null | undefined, _tick: number): string | null { + // _tick 参数用于触发响应式更新,实际使用时传入 tick.value + void _tick + + if (!nextProbeAt) return null + + const nextProbe = new Date(nextProbeAt) + const now = new Date() + const diffMs = nextProbe.getTime() - now.getTime() + + if (diffMs > 0) { + return formatCountdown(diffMs) + } + return '探测中' +} diff --git a/frontend/src/features/models/components/ModelAliasesTab.vue b/frontend/src/features/models/components/ModelAliasesTab.vue index 88c4e0e..35b699d 100644 --- a/frontend/src/features/models/components/ModelAliasesTab.vue +++ b/frontend/src/features/models/components/ModelAliasesTab.vue @@ -4,21 +4,33 @@
-

别名规则

+

映射规则

支持正则表达式 ({{ localAliases.length }}/{{ MAX_ALIASES_PER_MODEL }})
- +
+ + +
@@ -117,13 +129,12 @@ :key="item.keyId" class="bg-background rounded-md border p-3" > -
+
{{ item.providerName }} / {{ item.keyName }} - - {{ item.maskedKey }} - + · + {{ item.maskedKey }}
() const emit = defineEmits<{ update: [aliases: string[]] + refresh: [] }>() // 安全限制常量(与后端保持一致) const MAX_ALIASES_PER_MODEL = 50 diff --git a/frontend/src/features/models/components/RoutingTab.vue b/frontend/src/features/models/components/RoutingTab.vue index 1f30d9d..2e609b6 100644 --- a/frontend/src/features/models/components/RoutingTab.vue +++ b/frontend/src/features/models/components/RoutingTab.vue @@ -3,16 +3,16 @@
-
+

- 请求链路预览 + 链路预览