From 26b4a373237439d5643c0c1172bf5fe23228095a Mon Sep 17 00:00:00 2001 From: hoping Date: Thu, 25 Dec 2025 00:02:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=BC=95=E5=85=A5=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E7=9A=84=E7=AB=AF=E7=82=B9=E6=A3=80=E6=9F=A5=E5=99=A8?= =?UTF-8?q?=E4=BB=A5=E9=87=8D=E6=9E=84=E9=80=82=E9=85=8D=E5=99=A8=E5=B9=B6?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E5=92=8C?= =?UTF-8?q?=E7=94=A8=E9=87=8F=E7=BB=9F=E8=AE=A1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/endpoints/providers.ts | 13 + frontend/src/components/ui/dialog/Dialog.vue | 2 + frontend/src/composables/useConfirm.ts | 4 +- frontend/src/composables/useEscapeKey.ts | 11 +- .../components/ModelMappingDialog.vue | 11 +- .../components/ProviderDetailDrawer.vue | 8 +- .../provider-tabs/ModelAliasesTab.vue | 116 +- .../components/provider-tabs/ModelsTab.vue | 79 +- frontend/src/stores/users.ts | 16 +- .../src/views/admin/ProviderManagement.vue | 16 +- frontend/src/views/admin/Users.vue | 17 +- frontend/src/views/public/Home.vue | 8 +- src/api/admin/provider_query.py | 236 ++++ src/api/handlers/base/chat_adapter_base.py | 87 ++ src/api/handlers/base/cli_adapter_base.py | 140 ++ src/api/handlers/base/endpoint_checker.py | 1252 +++++++++++++++++ src/api/handlers/claude/adapter.py | 32 + src/api/handlers/claude_cli/adapter.py | 38 +- src/api/handlers/gemini/adapter.py | 91 +- src/api/handlers/gemini_cli/adapter.py | 48 +- src/api/handlers/openai/adapter.py | 30 +- src/api/handlers/openai_cli/adapter.py | 34 +- tests/api/handlers/base/test_utils.py | 112 +- 23 files changed, 2282 insertions(+), 119 deletions(-) create mode 100644 src/api/handlers/base/endpoint_checker.py diff --git a/frontend/src/api/endpoints/providers.ts b/frontend/src/api/endpoints/providers.ts index b70996b..8fcd183 100644 --- a/frontend/src/api/endpoints/providers.ts +++ b/frontend/src/api/endpoints/providers.ts @@ -58,3 +58,16 @@ export async function deleteProvider(providerId: string): Promise<{ message: str return response.data } +/** + * 测试模型连接性 + */ +export async function testModel(data: { + provider_id: string + model_name: string + api_key_id?: string + message?: string +}): Promise { + const response = await client.post('/api/admin/provider-query/test-model', data) + return response.data +} + diff --git a/frontend/src/components/ui/dialog/Dialog.vue b/frontend/src/components/ui/dialog/Dialog.vue index 9e45f5f..c0cba50 100644 --- a/frontend/src/components/ui/dialog/Dialog.vue +++ b/frontend/src/components/ui/dialog/Dialog.vue @@ -163,7 +163,9 @@ const contentZIndex = computed(() => (props.zIndex || 60) + 10) useEscapeKey(() => { if (isOpen.value) { handleClose() + return true // 阻止其他监听器(如父级抽屉的 ESC 监听器) } + return false }, { disableOnInput: true, once: false diff --git a/frontend/src/composables/useConfirm.ts b/frontend/src/composables/useConfirm.ts index f821235..f9ef15f 100644 --- a/frontend/src/composables/useConfirm.ts +++ b/frontend/src/composables/useConfirm.ts @@ -47,11 +47,11 @@ export function useConfirm() { /** * 便捷方法:危险操作确认(红色主题) */ - const confirmDanger = (message: string, title?: string): Promise => { + const confirmDanger = (message: string, title?: string, confirmText?: string): Promise => { return confirm({ message, title: title || '危险操作', - confirmText: '删除', + confirmText: confirmText || '删除', variant: 'danger' }) } diff --git a/frontend/src/composables/useEscapeKey.ts b/frontend/src/composables/useEscapeKey.ts index 4d85c65..84604a6 100644 --- a/frontend/src/composables/useEscapeKey.ts +++ b/frontend/src/composables/useEscapeKey.ts @@ -4,11 +4,11 @@ import { onMounted, onUnmounted, ref } from 'vue' * ESC 键监听 Composable(简化版本,直接使用独立监听器) * 用于按 ESC 键关闭弹窗或其他可关闭的组件 * - * @param callback - 按 ESC 键时执行的回调函数 + * @param callback - 按 ESC 键时执行的回调函数,返回 true 表示已处理事件,阻止其他监听器执行 * @param options - 配置选项 */ export function useEscapeKey( - callback: () => void, + callback: () => void | boolean, options: { /** 是否在输入框获得焦点时禁用 ESC 键,默认 true */ disableOnInput?: boolean @@ -42,8 +42,11 @@ export function useEscapeKey( if (isInputElement) return } - // 执行回调 - callback() + // 执行回调,如果返回 true 则阻止其他监听器 + const handled = callback() + if (handled === true) { + event.stopImmediatePropagation() + } // 移除当前元素的焦点,避免残留样式 if (document.activeElement instanceof HTMLElement) { diff --git a/frontend/src/features/providers/components/ModelMappingDialog.vue b/frontend/src/features/providers/components/ModelMappingDialog.vue index ce5f84f..ef243b7 100644 --- a/frontend/src/features/providers/components/ModelMappingDialog.vue +++ b/frontend/src/features/providers/components/ModelMappingDialog.vue @@ -17,7 +17,7 @@ v-model:open="modelSelectOpen" :model-value="formData.modelId" :disabled="!!editingGroup" - @update:model-value="formData.modelId = $event" + @update:model-value="handleModelChange" > @@ -518,6 +518,15 @@ function initForm() { upstreamModels.value = [] } } + +// 处理模型选择变更 +function handleModelChange(value: string) { + formData.value.modelId = value + const selectedModel = props.models.find(m => m.id === value) + if (selectedModel) { + upstreamModelSearch.value = selectedModel.provider_model_name + } +} // 切换 API 格式 function toggleApiFormat(format: string) { diff --git a/frontend/src/features/providers/components/ProviderDetailDrawer.vue b/frontend/src/features/providers/components/ProviderDetailDrawer.vue index d4aef38..fdde2a9 100644 --- a/frontend/src/features/providers/components/ProviderDetailDrawer.vue +++ b/frontend/src/features/providers/components/ProviderDetailDrawer.vue @@ -531,6 +531,7 @@ (null) const batchAssignDialogOpen = ref(false) +// ModelAliasesTab 组件引用 +const modelAliasesTabRef = ref | null>(null) + // 拖动排序相关状态 const dragState = ref({ isDragging: false, @@ -756,7 +760,9 @@ const hasBlockingDialogOpen = computed(() => deleteKeyConfirmOpen.value || modelFormDialogOpen.value || deleteModelConfirmOpen.value || - batchAssignDialogOpen.value + batchAssignDialogOpen.value || + // 检测 ModelAliasesTab 子组件的 Dialog 是否打开 + modelAliasesTabRef.value?.dialogOpen ) // 监听 providerId 变化 diff --git a/frontend/src/features/providers/components/provider-tabs/ModelAliasesTab.vue b/frontend/src/features/providers/components/provider-tabs/ModelAliasesTab.vue index 6115b8c..7d2aff7 100644 --- a/frontend/src/features/providers/components/provider-tabs/ModelAliasesTab.vue +++ b/frontend/src/features/providers/components/provider-tabs/ModelAliasesTab.vue @@ -110,16 +110,30 @@
- - - {{ mapping.priority }} - - - - {{ mapping.name }} - +
+ + + {{ mapping.priority }} + + + + {{ mapping.name }} + +
+ +
@@ -166,13 +180,14 @@ diff --git a/frontend/src/features/providers/components/provider-tabs/ModelsTab.vue b/frontend/src/features/providers/components/provider-tabs/ModelsTab.vue index 1465c4e..628a8fa 100644 --- a/frontend/src/features/providers/components/provider-tabs/ModelsTab.vue +++ b/frontend/src/features/providers/components/provider-tabs/ModelsTab.vue @@ -156,6 +156,17 @@
+