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()
+ }
+ })
+
})