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 @@
-
+

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