From f3bbb95310fe01ef29f5f86f7b7eeea991cc443e Mon Sep 17 00:00:00 2001 From: Zero Clover Date: Sat, 5 Sep 2026 19:46:25 +0800 Subject: [PATCH] fix(groups): react immediately to Codex manifest edits and unify dialog toggles --- .../group/CodexManifestAccountsField.vue | 49 +- frontend/src/views/admin/GroupsView.vue | 482 ++---------------- .../__tests__/GroupsView.duplicate.spec.ts | 67 +++ 3 files changed, 127 insertions(+), 471 deletions(-) diff --git a/frontend/src/components/admin/group/CodexManifestAccountsField.vue b/frontend/src/components/admin/group/CodexManifestAccountsField.vue index b110699f29..58c948d75b 100644 --- a/frontend/src/components/admin/group/CodexManifestAccountsField.vue +++ b/frontend/src/components/admin/group/CodexManifestAccountsField.vue @@ -9,25 +9,12 @@ {{ t("admin.groups.codexModelsManifest.hint") }}

- + :model-value="config.enabled" + @update:model-value="emitUpdate({ enabled: $event })" + />
@@ -115,31 +102,12 @@ {{ t("admin.groups.codexModelsManifest.fallbackHint") }}

- + :model-value="config.fallback_to_scheduler" + @update:model-value="emitUpdate({ fallback_to_scheduler: $event })" + />

- + {{ createForm.is_exclusive @@ -781,23 +765,7 @@ {{ t("admin.groups.modelsList.hint", { endpoint: modelsListEndpoint(createForm.platform) }) }}

- +
- + {{ createForm.mcp_xml_inject @@ -1420,27 +1372,7 @@
- + {{ createForm.claude_code_only @@ -1594,27 +1526,11 @@ - + :aria-label="t('admin.groups.openaiFast.force')" + v-model="createForm.force_openai_fast" + />

{{ t("admin.groups.openaiFast.hint") }} @@ -1623,27 +1539,11 @@ - + :aria-label="t('admin.groups.openaiFast.free')" + v-model="createForm.free_openai_fast" + />

{{ t("admin.groups.openaiFast.freeHint") }} @@ -1662,21 +1562,10 @@ - +

{{ t("admin.groups.openaiLive.hint") }} @@ -1697,28 +1586,7 @@ - +

{{ t("admin.groups.openaiMessages.allowDispatchHint") }} @@ -1943,27 +1811,7 @@ }}

- + @@ -1980,27 +1828,7 @@ }}

- + @@ -2057,28 +1885,7 @@
- + {{ createForm.model_routing_enabled @@ -2478,23 +2285,7 @@
- + {{ editForm.is_exclusive @@ -2582,23 +2373,7 @@ {{ t("admin.groups.modelsList.hint", { endpoint: modelsListEndpoint(editForm.platform) }) }}

- +
- + {{ editForm.mcp_xml_inject @@ -3221,23 +2980,7 @@
- + {{ editForm.claude_code_only @@ -3400,27 +3143,11 @@ - + :aria-label="t('admin.groups.openaiFast.force')" + v-model="editForm.force_openai_fast" + />

{{ t("admin.groups.openaiFast.hint") }} @@ -3429,27 +3156,11 @@ - + :aria-label="t('admin.groups.openaiFast.free')" + v-model="editForm.free_openai_fast" + />

{{ t("admin.groups.openaiFast.freeHint") }} @@ -3468,21 +3179,10 @@ - +

{{ t("admin.groups.openaiLive.hint") }} @@ -3503,28 +3203,7 @@ - +

{{ t("admin.groups.openaiMessages.allowDispatchHint") }} @@ -3748,27 +3427,7 @@ }}

- + @@ -3785,27 +3444,7 @@ }}

- + @@ -3862,27 +3501,7 @@
- + {{ editForm.model_routing_enabled @@ -4590,6 +4209,7 @@ import AppLayout from "@/components/layout/AppLayout.vue"; import TablePageLayout from "@/components/layout/TablePageLayout.vue"; import DataTable from "@/components/common/DataTable.vue"; import Pagination from "@/components/common/Pagination.vue"; +import Toggle from "@/components/common/Toggle.vue"; import BaseDialog from "@/components/common/BaseDialog.vue"; import ConfirmDialog from "@/components/common/ConfirmDialog.vue"; import EmptyState from "@/components/common/EmptyState.vue"; @@ -5200,7 +4820,7 @@ const createCodexManifestDefaults = (): CodexModelsManifestConfig => ({ account_ids: [], fallback_to_scheduler: false, }); -const editCodexManifestConfig = reactive(createCodexManifestDefaults()); +const editCodexManifestConfig = ref(createCodexManifestDefaults()); const editCodexManifestAccountNames = ref>({}); const modelsListCandidatesTracker = createModelsListCandidatesTracker(); const createModelsListSelectedCount = computed( @@ -6383,13 +6003,13 @@ const handleEdit = async (group: AdminGroup) => { // 固定账号 manifest 配置:回显配置并异步解析已存账号名称(失败显示 #) const savedCodexManifestConfig = group.codex_models_manifest_config ?? createCodexManifestDefaults(); - Object.assign(editCodexManifestConfig, { + editCodexManifestConfig.value = { enabled: savedCodexManifestConfig.enabled ?? false, account_ids: [...(savedCodexManifestConfig.account_ids ?? [])], fallback_to_scheduler: savedCodexManifestConfig.fallback_to_scheduler ?? false, - }); + }; editCodexManifestAccountNames.value = {}; - for (const id of editCodexManifestConfig.account_ids) { + for (const id of editCodexManifestConfig.value.account_ids) { adminAPI.accounts .getById(id) .then((account) => { @@ -6448,7 +6068,7 @@ const closeEditModal = () => { resetMessagesDispatchFormState(editForm); editForm.allow_live = false; resetModelsListState(editModelsListState); - Object.assign(editCodexManifestConfig, createCodexManifestDefaults()); + editCodexManifestConfig.value = createCodexManifestDefaults(); editCodexManifestAccountNames.value = {}; editCodexManifestRef.value?.resetValidation?.(); }; @@ -6472,8 +6092,8 @@ const handleUpdateGroup = async () => { // 固定账号 manifest:开启后至少一个账号,前端阻止提交并提示。 if ( editForm.platform === "openai" && - editCodexManifestConfig.enabled && - editCodexManifestConfig.account_ids.length === 0 + editCodexManifestConfig.value.enabled && + editCodexManifestConfig.value.account_ids.length === 0 ) { appStore.showError(t("admin.groups.codexModelsManifest.selectAtLeastOne")); editCodexManifestRef.value?.validate(); @@ -6523,9 +6143,9 @@ const handleUpdateGroup = async () => { codex_models_manifest_config: editForm.platform === "openai" ? { - enabled: editCodexManifestConfig.enabled, - account_ids: [...editCodexManifestConfig.account_ids], - fallback_to_scheduler: editCodexManifestConfig.fallback_to_scheduler, + enabled: editCodexManifestConfig.value.enabled, + account_ids: [...editCodexManifestConfig.value.account_ids], + fallback_to_scheduler: editCodexManifestConfig.value.fallback_to_scheduler, } : createCodexManifestDefaults(), supported_model_scopes: normalizeSupportedModelScopesForPlatform( diff --git a/frontend/src/views/admin/__tests__/GroupsView.duplicate.spec.ts b/frontend/src/views/admin/__tests__/GroupsView.duplicate.spec.ts index 10690d87e3..31557ec503 100644 --- a/frontend/src/views/admin/__tests__/GroupsView.duplicate.spec.ts +++ b/frontend/src/views/admin/__tests__/GroupsView.duplicate.spec.ts @@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { AdminGroup } from '@/types' import GroupsView from '@/views/admin/GroupsView.vue' +import { adminAPI } from '@/api/admin' const { listGroups, @@ -302,4 +303,70 @@ describe('GroupsView duplicate action', () => { expect(showError).toHaveBeenCalledWith('group name already exists') wrapper.unmount() }) + + it('updates manifest controls immediately and submits the displayed selection', async () => { + vi.useFakeTimers() + vi.mocked(adminAPI.accounts.list).mockResolvedValue({ + items: [{ id: 5, name: 'Manifest account' }] + } as never) + updateGroup.mockResolvedValue(sourceGroup) + const wrapper = mountView() + try { + await flushPromises() + const editButton = wrapper.findAll('button').find((button) => button.text() === 'common.edit')! + await editButton.trigger('click') + await flushPromises() + + const toggle = wrapper.get('[data-testid="codex-manifest-toggle"]') + await toggle.trigger('click') + expect(toggle.attributes('aria-checked')).toBe('true') + const search = wrapper.get('[data-testid="codex-manifest-search"]') + await search.trigger('focus') + await vi.advanceTimersByTimeAsync(300) + await flushPromises() + expect(adminAPI.accounts.list).toHaveBeenCalledWith( + 1, 20, { search: '', platform: 'openai', group: '42' }, expect.anything() + ) + await wrapper.get('[data-testid="codex-manifest-dropdown"] button').trigger('click') + expect(wrapper.get('[data-testid="codex-manifest-selected-tags"]').text()).toContain('Manifest account') + + await wrapper.get('[aria-label="remove account 5"]').trigger('click') + expect(wrapper.find('[data-testid="codex-manifest-selected-tags"]').exists()).toBe(false) + await wrapper.get('#edit-group-form').trigger('submit') + expect(updateGroup).not.toHaveBeenCalled() + expect(wrapper.find('[data-testid="codex-manifest-validation-error"]').exists()).toBe(true) + + await search.trigger('focus') + await wrapper.get('[data-testid="codex-manifest-dropdown"] button').trigger('click') + expect(wrapper.get('[data-testid="codex-manifest-selected-tags"]').text()).toContain('Manifest account') + const fallback = wrapper.get('[data-testid="codex-manifest-fallback-toggle"]') + await fallback.trigger('click') + expect(fallback.attributes('aria-checked')).toBe('true') + await fallback.trigger('click') + expect(fallback.attributes('aria-checked')).toBe('false') + await fallback.trigger('click') + + await toggle.trigger('click') + expect(wrapper.find('[data-testid="codex-manifest-search"]').exists()).toBe(false) + await toggle.trigger('click') + expect(wrapper.get('[data-testid="codex-manifest-selected-tags"]').text()).toContain('Manifest account') + await wrapper.get('#edit-group-form').trigger('submit') + await flushPromises() + expect(updateGroup).toHaveBeenCalledWith(42, expect.objectContaining({ + codex_models_manifest_config: { + enabled: true, account_ids: [5], fallback_to_scheduler: true + } + })) + + // Reopening reads the saved group afresh, without retaining the prior draft. + await editButton.trigger('click') + await flushPromises() + expect(wrapper.get('[data-testid="codex-manifest-toggle"]').attributes('aria-checked')).toBe('false') + expect(wrapper.find('[data-testid="codex-manifest-search"]').exists()).toBe(false) + } finally { + wrapper.unmount() + vi.useRealTimers() + } + }) + })