fix(frontend): 修复合并审阅发现的构建与契约问题

恢复 Vue TypeScript 生产构建,补齐可运行页面壳子,并修复文件树与 SSE 状态问题。

按 FastAPI Wire Contract 统一 Service DTO 映射,同时补充前端开发说明和问题修复复盘。
This commit is contained in:
2026-08-29 12:10:33 +08:00
parent c6c28e4ebe
commit 610fc77b0f
32 changed files with 1204 additions and 611 deletions
+53 -22
View File
@@ -1,36 +1,67 @@
import apiClient from './apiClient'
import type { ProviderConfig, ModelInfo } from '@/contracts'
import type { ApiModelInfo, ApiProviderConfig, ModelCapability, ModelInfo, OperationResponse, ProviderConfig } from '@/contracts'
export async function listProviders(): Promise<ProviderConfig[]> {
try {
return await apiClient.get('/api/providers')
} catch {
return mockProviders
function capabilityMap(capabilities: string[]): Partial<ModelCapability> {
return Object.fromEntries(capabilities.map((capability) => [capability, true])) as Partial<ModelCapability>
}
function toProvider(provider: ApiProviderConfig): ProviderConfig {
return {
provider_id: provider.provider_id,
provider_type: provider.provider_type,
name: provider.name,
base_url: provider.base_url ?? undefined,
default_model: provider.default_model ?? '',
enabled: provider.enabled,
capabilities: capabilityMap(provider.capabilities),
credential_id: provider.credential_id ?? undefined,
has_credential: Boolean(provider.credential_id) || provider.provider_type === 'mock',
}
}
function toModel(model: ApiModelInfo): ModelInfo {
return { model_id: model.model, name: model.display_name, capabilities: capabilityMap(model.capabilities) }
}
export async function listProviders(): Promise<ProviderConfig[]> {
const response = await apiClient.get<{ items: ApiProviderConfig[] }>('/api/providers')
return response.items.map(toProvider)
}
export async function getProvider(providerId: string): Promise<ProviderConfig> {
return apiClient.get(`/api/providers/${providerId}`)
return toProvider(await apiClient.get<ApiProviderConfig>(`/api/providers/${providerId}`))
}
export async function createProvider(data: Omit<ProviderConfig, 'provider_id'> & { api_key?: string }): Promise<ProviderConfig> {
return apiClient.post('/api/providers', data)
export async function createProvider(data: Omit<ProviderConfig, 'provider_id'>): Promise<ProviderConfig> {
const response = await apiClient.post<ApiProviderConfig>('/api/providers', {
provider_type: data.provider_type,
name: data.name,
base_url: data.base_url,
default_model: data.default_model || null,
credential_id: data.credential_id,
enabled: data.enabled,
})
return toProvider(response)
}
export async function updateProvider(providerId: string, data: Partial<ProviderConfig> & { api_key?: string }): Promise<ProviderConfig> {
return apiClient.patch(`/api/providers/${providerId}`, data)
export async function updateProvider(providerId: string, data: Partial<ProviderConfig>): Promise<ProviderConfig> {
const response = await apiClient.patch<ApiProviderConfig>(`/api/providers/${providerId}`, {
name: data.name,
base_url: data.base_url,
default_model: data.default_model,
credential_id: data.credential_id,
enabled: data.enabled,
})
return toProvider(response)
}
export async function deleteProvider(providerId: string): Promise<void> {
export async function deleteProvider(providerId: string): Promise<OperationResponse> {
return apiClient.delete(`/api/providers/${providerId}`)
}
export async function listModels(providerId: string): Promise<ModelInfo[]> {
try {
return await apiClient.get(`/api/providers/${providerId}/models`)
} catch {
return mockModels[providerId] || []
}
const response = await apiClient.get<{ provider_id: string; items: ApiModelInfo[] }>(`/api/providers/${providerId}/models`)
return response.items.map(toModel)
}
export interface TestResult {
@@ -42,8 +73,8 @@ export interface TestResult {
export async function testProvider(providerId: string): Promise<TestResult> {
try {
const result = await apiClient.post<{ success: boolean; latency_ms: number }>('/api/providers/test', { provider_id: providerId })
return { success: result.success, latency_ms: result.latency_ms }
const result = await apiClient.post<{ success: boolean; latency_ms?: number | null; message: string }>('/api/providers/test', { provider_id: providerId })
return { success: result.success, latency_ms: result.latency_ms ?? undefined, error_message: result.success ? undefined : result.message }
} catch (e: any) {
return { success: false, error_code: e.code || 'TEST_FAILED', error_message: e.message }
}
@@ -51,7 +82,7 @@ export async function testProvider(providerId: string): Promise<TestResult> {
export const mockProviders: ProviderConfig[] = [
{
provider_id: 'mock-provider',
provider_id: 'mock',
provider_type: 'mock',
name: 'Mock Provider (测试)',
default_model: 'mock-1',
@@ -69,7 +100,7 @@ export const mockProviders: ProviderConfig[] = [
},
{
provider_id: 'openai-compat-1',
provider_type: 'openai-compatible',
provider_type: 'openai_compatible',
name: 'OpenAI 兼容服务',
base_url: 'https://api.openai.com/v1',
default_model: 'gpt-4o-mini',
@@ -106,7 +137,7 @@ export const mockProviders: ProviderConfig[] = [
]
export const mockModels: Record<string, ModelInfo[]> = {
'mock-provider': [
mock: [
{
model_id: 'mock-1',
name: 'Mock Model v1',