fix(chat): 保留页面切换后的提供商与模型选择
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { beforeEach, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { useChatStore } from '@/stores/chat'
|
||||
import { useProviderStore } from '@/stores/provider'
|
||||
import { useSkillStore } from '@/stores/skill'
|
||||
import ChatView from './ChatView.vue'
|
||||
|
||||
vi.mock('vue-router', () => ({ useRouter: () => ({ push: vi.fn() }) }))
|
||||
vi.mock('@/stores/editor', () => ({ useEditorStore: () => ({}) }))
|
||||
vi.mock('@/stores/workspace', () => ({ useWorkspaceStore: () => ({}) }))
|
||||
vi.mock('@/components/common/MarkdownContent.vue', () => ({ default: { template: '<div />' } }))
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
const providers = useProviderStore()
|
||||
providers.providers = ['a', 'b'].map(id => ({
|
||||
provider_id: id, provider_type: 'openai_compatible', name: id,
|
||||
default_model: `${id}-default`, enabled: true, capabilities: { chat: true }, has_credential: false,
|
||||
}))
|
||||
providers.defaultProviderId = 'a'
|
||||
vi.spyOn(providers, 'loadProviders').mockResolvedValue(undefined)
|
||||
vi.spyOn(providers, 'loadModels').mockResolvedValue([])
|
||||
vi.spyOn(useSkillStore(), 'loadSkills').mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
it('preserves the selected provider and manual model after leaving and returning to chat', async () => {
|
||||
const chat = useChatStore()
|
||||
const first = mount(ChatView)
|
||||
await flushPromises()
|
||||
await first.get('select').setValue('b')
|
||||
await first.get('input[list="chat-models"]').setValue('b-manual')
|
||||
first.unmount()
|
||||
const returned = mount(ChatView)
|
||||
await flushPromises()
|
||||
expect(chat.selectedProviderId).toBe('b')
|
||||
expect(chat.selectedModel).toBe('b-manual')
|
||||
expect(useProviderStore().loadModels).toHaveBeenLastCalledWith('b')
|
||||
returned.unmount()
|
||||
})
|
||||
|
||||
it.each(['missing', 'disabled', 'unselected'])('uses the default when the selected provider is %s', async state => {
|
||||
const chat = useChatStore()
|
||||
chat.selectedProviderId = state === 'unselected' ? '' : state === 'missing' ? 'deleted' : 'b'
|
||||
chat.selectedModel = 'old-model'
|
||||
if (state === 'disabled') useProviderStore().providers[1]!.enabled = false
|
||||
const wrapper = mount(ChatView)
|
||||
await flushPromises()
|
||||
expect(chat.selectedProviderId).toBe('a')
|
||||
expect(chat.selectedModel).toBe('a-default')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('preserves the selection when provider discovery fails', async () => {
|
||||
const chat = useChatStore()
|
||||
chat.selectedProviderId = 'b'
|
||||
chat.selectedModel = 'b-manual'
|
||||
useProviderStore().error = 'offline'
|
||||
const wrapper = mount(ChatView)
|
||||
await flushPromises()
|
||||
expect(chat.selectedProviderId).toBe('b')
|
||||
expect(chat.selectedModel).toBe('b-manual')
|
||||
expect(wrapper.get('.error-banner').text()).toBe('offline')
|
||||
wrapper.unmount()
|
||||
})
|
||||
@@ -22,18 +22,28 @@ const availableModels = computed(() => providerStore.modelsByProvider[chatStore.
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await Promise.all([providerStore.loadProviders(), skillStore.loadSkills()])
|
||||
chatStore.selectedProviderId = providerStore.defaultProviderId
|
||||
if (providerStore.error) return
|
||||
const selected = providerStore.enabledProviders.find(p => p.provider_id === chatStore.selectedProviderId)
|
||||
if (!selected) {
|
||||
chatStore.selectedProviderId = providerStore.defaultProviderId
|
||||
} else {
|
||||
await refreshModels(selected.provider_id)
|
||||
}
|
||||
} catch (error) {
|
||||
loadError.value = error instanceof Error ? error.message : '无法加载 AI 配置,请检查后端连接。'
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => chatStore.selectedProviderId, async (providerId) => {
|
||||
chatStore.selectedModel = providerStore.providers.find(p => p.provider_id === providerId)?.default_model ?? ''
|
||||
async function refreshModels(providerId: string) {
|
||||
loadError.value = ''
|
||||
if (!providerId) return
|
||||
try { await providerStore.loadModels(providerId) }
|
||||
catch (error) { if (chatStore.selectedProviderId === providerId) loadError.value = error instanceof Error ? error.message : '模型列表加载失败,请手动填写模型 ID。' }
|
||||
}
|
||||
|
||||
watch(() => chatStore.selectedProviderId, async (providerId) => {
|
||||
chatStore.selectedModel = providerStore.providers.find(p => p.provider_id === providerId)?.default_model ?? ''
|
||||
await refreshModels(providerId)
|
||||
})
|
||||
|
||||
function send() { void chatStore.sendMessage(chatStore.inputText) }
|
||||
|
||||
Reference in New Issue
Block a user