Merge pull request #1157 from LvyuanW/fix-bulk-model-restriction-empty
fix: allow clearing model restriction in bulk edit when whitelist is empty
This commit is contained in:
@@ -1056,24 +1056,21 @@ const buildUpdatePayload = (): Record<string, unknown> | null => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enableModelRestriction.value) {
|
if (enableModelRestriction.value) {
|
||||||
const modelMapping = buildModelMappingObject()
|
|
||||||
|
|
||||||
// 统一使用 model_mapping 字段
|
// 统一使用 model_mapping 字段
|
||||||
if (modelRestrictionMode.value === 'whitelist') {
|
if (modelRestrictionMode.value === 'whitelist') {
|
||||||
if (allowedModels.value.length > 0) {
|
// 白名单模式:将模型转换为 model_mapping 格式(key=value)
|
||||||
// 白名单模式:将模型转换为 model_mapping 格式(key=value)
|
// 空白名单表示“支持所有模型”,需显式发送空对象以覆盖已有限制。
|
||||||
const mapping: Record<string, string> = {}
|
const mapping: Record<string, string> = {}
|
||||||
for (const m of allowedModels.value) {
|
for (const m of allowedModels.value) {
|
||||||
mapping[m] = m
|
mapping[m] = m
|
||||||
}
|
|
||||||
credentials.model_mapping = mapping
|
|
||||||
credentialsChanged = true
|
|
||||||
}
|
}
|
||||||
|
credentials.model_mapping = mapping
|
||||||
|
credentialsChanged = true
|
||||||
} else {
|
} else {
|
||||||
if (modelMapping) {
|
// 映射模式下空配置同样表示“支持所有模型”。
|
||||||
credentials.model_mapping = modelMapping
|
const modelMapping = buildModelMappingObject()
|
||||||
credentialsChanged = true
|
credentials.model_mapping = modelMapping ?? {}
|
||||||
}
|
credentialsChanged = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||||||
import { mount } from '@vue/test-utils'
|
import { flushPromises, mount } from '@vue/test-utils'
|
||||||
import BulkEditAccountModal from '../BulkEditAccountModal.vue'
|
import BulkEditAccountModal from '../BulkEditAccountModal.vue'
|
||||||
|
import ModelWhitelistSelector from '../ModelWhitelistSelector.vue'
|
||||||
|
import { adminAPI } from '@/api/admin'
|
||||||
|
|
||||||
vi.mock('@/stores/app', () => ({
|
vi.mock('@/stores/app', () => ({
|
||||||
useAppStore: () => ({
|
useAppStore: () => ({
|
||||||
@@ -13,7 +15,8 @@ vi.mock('@/stores/app', () => ({
|
|||||||
vi.mock('@/api/admin', () => ({
|
vi.mock('@/api/admin', () => ({
|
||||||
adminAPI: {
|
adminAPI: {
|
||||||
accounts: {
|
accounts: {
|
||||||
bulkEdit: vi.fn()
|
bulkUpdate: vi.fn(),
|
||||||
|
checkMixedChannelRisk: vi.fn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@@ -32,18 +35,21 @@ vi.mock('vue-i18n', async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function mountModal() {
|
function mountModal(extraProps: Record<string, unknown> = {}) {
|
||||||
return mount(BulkEditAccountModal, {
|
return mount(BulkEditAccountModal, {
|
||||||
props: {
|
props: {
|
||||||
show: true,
|
show: true,
|
||||||
accountIds: [1, 2],
|
accountIds: [1, 2],
|
||||||
selectedPlatforms: ['antigravity'],
|
selectedPlatforms: ['antigravity'],
|
||||||
|
selectedTypes: ['apikey'],
|
||||||
proxies: [],
|
proxies: [],
|
||||||
groups: []
|
groups: [],
|
||||||
|
...extraProps
|
||||||
} as any,
|
} as any,
|
||||||
global: {
|
global: {
|
||||||
stubs: {
|
stubs: {
|
||||||
BaseDialog: { template: '<div><slot /><slot name="footer" /></div>' },
|
BaseDialog: { template: '<div><slot /><slot name="footer" /></div>' },
|
||||||
|
ConfirmDialog: true,
|
||||||
Select: true,
|
Select: true,
|
||||||
ProxySelector: true,
|
ProxySelector: true,
|
||||||
GroupSelector: true,
|
GroupSelector: true,
|
||||||
@@ -54,12 +60,30 @@ function mountModal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('BulkEditAccountModal', () => {
|
describe('BulkEditAccountModal', () => {
|
||||||
it('antigravity 白名单包含 Gemini 图片模型且过滤掉普通 GPT 模型', () => {
|
beforeEach(() => {
|
||||||
const wrapper = mountModal()
|
vi.mocked(adminAPI.accounts.bulkUpdate).mockReset()
|
||||||
|
vi.mocked(adminAPI.accounts.checkMixedChannelRisk).mockReset()
|
||||||
|
|
||||||
expect(wrapper.text()).toContain('Gemini 3.1 Flash Image')
|
vi.mocked(adminAPI.accounts.bulkUpdate).mockResolvedValue({
|
||||||
expect(wrapper.text()).toContain('Gemini 3 Pro Image (Legacy)')
|
success: 2,
|
||||||
expect(wrapper.text()).not.toContain('GPT-5.3 Codex')
|
failed: 0,
|
||||||
|
results: []
|
||||||
|
} as any)
|
||||||
|
vi.mocked(adminAPI.accounts.checkMixedChannelRisk).mockResolvedValue({
|
||||||
|
has_risk: false
|
||||||
|
} as any)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('antigravity 白名单包含 Gemini 图片模型且过滤掉普通 GPT 模型', async () => {
|
||||||
|
const wrapper = mountModal()
|
||||||
|
const selector = wrapper.findComponent(ModelWhitelistSelector)
|
||||||
|
expect(selector.exists()).toBe(true)
|
||||||
|
|
||||||
|
await selector.find('div.cursor-pointer').trigger('click')
|
||||||
|
|
||||||
|
expect(wrapper.text()).toContain('gemini-3.1-flash-image')
|
||||||
|
expect(wrapper.text()).toContain('gemini-2.5-flash-image')
|
||||||
|
expect(wrapper.text()).not.toContain('gpt-5.3-codex')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('antigravity 映射预设包含图片映射并过滤 OpenAI 预设', async () => {
|
it('antigravity 映射预设包含图片映射并过滤 OpenAI 预设', async () => {
|
||||||
@@ -69,8 +93,26 @@ describe('BulkEditAccountModal', () => {
|
|||||||
expect(mappingTab).toBeTruthy()
|
expect(mappingTab).toBeTruthy()
|
||||||
await mappingTab!.trigger('click')
|
await mappingTab!.trigger('click')
|
||||||
|
|
||||||
expect(wrapper.text()).toContain('Gemini 3.1 Image')
|
expect(wrapper.text()).toContain('3.1-Flash-Image透传')
|
||||||
expect(wrapper.text()).toContain('G3 Image→3.1')
|
expect(wrapper.text()).toContain('3-Pro-Image→3.1')
|
||||||
expect(wrapper.text()).not.toContain('GPT-5.3 Codex')
|
expect(wrapper.text()).not.toContain('GPT-5.3 Codex Spark')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('仅勾选模型限制且白名单留空时,应提交空 model_mapping 以支持所有模型', async () => {
|
||||||
|
const wrapper = mountModal({
|
||||||
|
selectedPlatforms: ['anthropic'],
|
||||||
|
selectedTypes: ['apikey']
|
||||||
|
})
|
||||||
|
|
||||||
|
await wrapper.get('#bulk-edit-model-restriction-enabled').setValue(true)
|
||||||
|
await wrapper.get('#bulk-edit-account-form').trigger('submit.prevent')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(adminAPI.accounts.bulkUpdate).toHaveBeenCalledTimes(1)
|
||||||
|
expect(adminAPI.accounts.bulkUpdate).toHaveBeenCalledWith([1, 2], {
|
||||||
|
credentials: {
|
||||||
|
model_mapping: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user