diff --git a/admin-frontend/src/api/client.ts b/admin-frontend/src/api/client.ts index 918f353..3612942 100644 --- a/admin-frontend/src/api/client.ts +++ b/admin-frontend/src/api/client.ts @@ -1,23 +1,35 @@ const BASE_URL = '' -class ApiClient { - async request(url: string, options: RequestInit = {}): Promise { - const response = await fetch(`${BASE_URL}${url}`, { - ...options, - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - ...options.headers, - }, - }) - - if (!response.ok) { - const error = await response.json().catch(() => ({ detail: '请求失败' })) - throw new Error(error.detail || '请求失败') - } - - return response.json() - } +class ApiClient { + async request(url: string, options: RequestInit = {}): Promise { + const headers = new Headers(options.headers || {}) + const isFormData = options.body instanceof FormData + if (!isFormData && !headers.has('Content-Type')) { + headers.set('Content-Type', 'application/json') + } + + const response = await fetch(`${BASE_URL}${url}`, { + ...options, + credentials: 'include', + headers, + }) + + if (!response.ok) { + const error = await response.json().catch(() => ({ detail: '请求失败' })) + throw new Error(error.detail || '请求失败') + } + + if (response.status === 204 || response.status === 205) { + return undefined as T + } + + const contentType = response.headers.get('content-type') || '' + if (!contentType.includes('application/json')) { + return undefined as T + } + + return response.json() + } get(url: string): Promise { return this.request(url) diff --git a/admin-frontend/src/components/CreateStoryModal.vue b/admin-frontend/src/components/CreateStoryModal.vue index 5c37d51..68921e4 100644 --- a/admin-frontend/src/components/CreateStoryModal.vue +++ b/admin-frontend/src/components/CreateStoryModal.vue @@ -145,12 +145,12 @@ function sleep(ms: number) { async function waitForStoryId(jobId: string) { for (let attempt = 0; attempt < JOB_POLL_MAX_ATTEMPTS; attempt += 1) { const detail = await api.get(`/api/generations/jobs/${jobId}`) - if (detail.status === 'canceled' || detail.current_step === 'generation_canceled') { - return null - } if (detail.story_id) { return detail.story_id } + if (detail.status === 'canceled' || detail.current_step === 'generation_canceled') { + return null + } if (detail.is_terminal) { throw new Error(detail.error_message || '生成失败,请稍后重试') } diff --git a/admin-frontend/src/components/GenerationTrace.vue b/admin-frontend/src/components/GenerationTrace.vue index d589f23..9360828 100644 --- a/admin-frontend/src/components/GenerationTrace.vue +++ b/admin-frontend/src/components/GenerationTrace.vue @@ -74,11 +74,7 @@ const latestJob = computed(() => jobs.value[0] ?? null) const activeEvents = computed(() => activeJob.value?.events.slice(-10) ?? []) const activeProgress = computed(() => activeJob.value?.progress_percent ?? latestJob.value?.progress_percent ?? 0) const activeProgressLabel = computed(() => activeJob.value?.progress_label ?? latestJob.value?.progress_label ?? '暂无进度') -const shouldAutoRefresh = computed(() => { - if (activeJob.value) return !activeJob.value.is_terminal - if (latestJob.value) return !latestJob.value.is_terminal - return false -}) +const shouldAutoRefresh = computed(() => Boolean(latestJob.value && !latestJob.value.is_terminal)) const providerSuccessRate = computed(() => { if (!providerStats.value?.total_calls) return null return Math.round((providerStats.value.successful_calls / providerStats.value.total_calls) * 100) @@ -199,6 +195,7 @@ async function refresh() { } error.value = '' + const selectedJobId = activeJob.value?.id ?? null try { const [nextJobs, stats] = await Promise.all([ @@ -207,7 +204,11 @@ async function refresh() { ]) jobs.value = nextJobs providerStats.value = stats - const nextJobId = jobs.value[0]?.id + const nextJobId = ( + selectedJobId + ? jobs.value.find((job) => job.id === selectedJobId)?.id + : null + ) ?? jobs.value[0]?.id if (nextJobId) { await selectJob(nextJobId) } else { @@ -346,7 +347,13 @@ defineExpose({ refresh }) >
- {{ job.output_mode === 'asset_retry' ? '资源重试' : '内容生成' }} + {{ + job.output_mode === 'asset_retry' + ? '资源重试' + : job.output_mode === 'asset_generation' + ? '资源生成' + : '内容生成' + }} {{ statusLabel(job.status) }} @@ -366,7 +373,13 @@ defineExpose({ refresh })
- {{ activeJob.output_mode === 'asset_retry' ? '资源重试事件' : '生成事件' }} + {{ + activeJob.output_mode === 'asset_retry' + ? '资源重试事件' + : activeJob.output_mode === 'asset_generation' + ? '资源生成事件' + : '生成事件' + }}
当前步骤:{{ eventLabel(activeJob.current_step) }} diff --git a/admin-frontend/src/components/ui/LoginDialog.vue b/admin-frontend/src/components/ui/LoginDialog.vue index 4b8c562..45f012e 100644 --- a/admin-frontend/src/components/ui/LoginDialog.vue +++ b/admin-frontend/src/components/ui/LoginDialog.vue @@ -1,5 +1,6 @@ - +function loginWithGithub() { + window.location.href = buildAuthSigninUrl('github') +} + +function loginWithGoogle() { + window.location.href = buildAuthSigninUrl('google') +} + +function loginWithDev() { + window.location.href = buildAuthSigninUrl('dev') +} +