From 689339117ab66f081268ad723aeef8ad355b6dc1 Mon Sep 17 00:00:00 2001 From: fawney19 Date: Mon, 5 Jan 2026 01:20:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8F=90=E5=8F=96=20ModelMultiSele?= =?UTF-8?q?ct=20=E7=BB=84=E4=BB=B6=E5=B9=B6=E6=94=AF=E6=8C=81=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E6=A8=A1=E5=9E=8B=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 ModelMultiSelect 组件,支持显示和移除已失效的模型 - 新增 useInvalidModels composable 检测 allowed_models 中的无效引用 - 重构 StandaloneKeyFormDialog 和 UserFormDialog 使用新组件 - 补充 GlobalModel 删除逻辑的设计说明注释 --- .../components/common/ModelMultiSelect.vue | 117 ++++++++++++++++++ frontend/src/components/common/index.ts | 3 + frontend/src/composables/useInvalidModels.ts | 34 +++++ .../components/StandaloneKeyFormDialog.vue | 56 +-------- .../users/components/UserFormDialog.vue | 60 ++------- src/services/model/global_model.py | 2 + 6 files changed, 169 insertions(+), 103 deletions(-) create mode 100644 frontend/src/components/common/ModelMultiSelect.vue create mode 100644 frontend/src/composables/useInvalidModels.ts diff --git a/frontend/src/components/common/ModelMultiSelect.vue b/frontend/src/components/common/ModelMultiSelect.vue new file mode 100644 index 0000000..93e918b --- /dev/null +++ b/frontend/src/components/common/ModelMultiSelect.vue @@ -0,0 +1,117 @@ + + + diff --git a/frontend/src/components/common/index.ts b/frontend/src/components/common/index.ts index a0c0ac2..7ba0d83 100644 --- a/frontend/src/components/common/index.ts +++ b/frontend/src/components/common/index.ts @@ -7,3 +7,6 @@ export { default as EmptyState } from './EmptyState.vue' export { default as AlertDialog } from './AlertDialog.vue' export { default as LoadingState } from './LoadingState.vue' + +// 表单组件 +export { default as ModelMultiSelect } from './ModelMultiSelect.vue' diff --git a/frontend/src/composables/useInvalidModels.ts b/frontend/src/composables/useInvalidModels.ts new file mode 100644 index 0000000..e5bd95d --- /dev/null +++ b/frontend/src/composables/useInvalidModels.ts @@ -0,0 +1,34 @@ +import { computed, type Ref, type ComputedRef } from 'vue' + +/** + * 检测失效模型的 composable + * + * 用于检测 allowed_models 中已不存在于 globalModels 的模型名称, + * 这些模型可能已被删除但引用未清理。 + * + * @example + * ```typescript + * const { invalidModels } = useInvalidModels( + * computed(() => form.value.allowed_models), + * globalModels + * ) + * ``` + */ +export interface ModelWithName { + name: string +} + +export function useInvalidModels( + allowedModels: Ref | ComputedRef, + globalModels: Ref +): { invalidModels: ComputedRef } { + const validModelNames = computed(() => + new Set(globalModels.value.map(m => m.name)) + ) + + const invalidModels = computed(() => + allowedModels.value.filter(name => !validModelNames.value.has(name)) + ) + + return { invalidModels } +} diff --git a/frontend/src/features/api-keys/components/StandaloneKeyFormDialog.vue b/frontend/src/features/api-keys/components/StandaloneKeyFormDialog.vue index 35b9b13..a92a06a 100644 --- a/frontend/src/features/api-keys/components/StandaloneKeyFormDialog.vue +++ b/frontend/src/features/api-keys/components/StandaloneKeyFormDialog.vue @@ -244,55 +244,10 @@ -
- -
- -
-
-
- - {{ model.name }} -
-
- 暂无可用模型 -
-
-
-
+
@@ -327,6 +282,7 @@ import { } from '@/components/ui' import { Plus, SquarePen, Key, Shield, ChevronDown } from 'lucide-vue-next' import { useFormDialog } from '@/composables/useFormDialog' +import { ModelMultiSelect } from '@/components/common' import { getProvidersSummary } from '@/api/endpoints/providers' import { getGlobalModels } from '@/api/global-models' import { adminApi } from '@/api/admin' @@ -363,7 +319,6 @@ const saving = ref(false) // 下拉框状态 const providerDropdownOpen = ref(false) const apiFormatDropdownOpen = ref(false) -const modelDropdownOpen = ref(false) // 选项数据 const providers = ref([]) @@ -397,7 +352,6 @@ function resetForm() { } providerDropdownOpen.value = false apiFormatDropdownOpen.value = false - modelDropdownOpen.value = false } function loadKeyData() { diff --git a/frontend/src/features/users/components/UserFormDialog.vue b/frontend/src/features/users/components/UserFormDialog.vue index 65cd958..c7e7d42 100644 --- a/frontend/src/features/users/components/UserFormDialog.vue +++ b/frontend/src/features/users/components/UserFormDialog.vue @@ -316,55 +316,10 @@ -
- -
- -
-
-
- - {{ model.name }} -
-
- 暂无可用模型 -
-
-
-
+
@@ -404,10 +359,12 @@ import { } from '@/components/ui' import { UserPlus, SquarePen, ChevronDown } from 'lucide-vue-next' import { useFormDialog } from '@/composables/useFormDialog' +import { ModelMultiSelect } from '@/components/common' import { getProvidersSummary } from '@/api/endpoints/providers' import { getGlobalModels } from '@/api/global-models' import { adminApi } from '@/api/admin' import { log } from '@/utils/logger' +import type { ProviderWithEndpointsSummary, GlobalModelResponse } from '@/api/endpoints/types' export interface UserFormData { id?: string @@ -440,11 +397,10 @@ const roleSelectOpen = ref(false) // 下拉框状态 const providerDropdownOpen = ref(false) const endpointDropdownOpen = ref(false) -const modelDropdownOpen = ref(false) // 选项数据 -const providers = ref([]) -const globalModels = ref([]) +const providers = ref([]) +const globalModels = ref([]) const apiFormats = ref>([]) // 表单数据 diff --git a/src/services/model/global_model.py b/src/services/model/global_model.py index 32609f3..7f222d3 100644 --- a/src/services/model/global_model.py +++ b/src/services/model/global_model.py @@ -148,6 +148,8 @@ class GlobalModelService: 删除 GlobalModel 默认行为: 级联删除所有关联的 Provider 模型实现 + 注意: 不清理 API Key 和 User 的 allowed_models 引用, + 保留无效引用可让用户在前端看到"已失效"的模型,便于手动清理或等待重建同名模型 """ global_model = GlobalModelService.get_global_model(db, global_model_id)