diff --git a/components/scene-fusion-dialog.tsx b/components/scene-fusion-dialog.tsx index e465db802..f6add6c76 100644 --- a/components/scene-fusion-dialog.tsx +++ b/components/scene-fusion-dialog.tsx @@ -253,25 +253,76 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps }), }); - const data = await response.json(); - if (!response.ok) { - throw new Error(data.error || '处理失败'); + // 尝试解析错误信息 + try { + const errorData = await response.json(); + throw new Error(errorData.error || '处理失败'); + } catch { + throw new Error(`请求失败: ${response.status}`); + } } - const result = data.results?.[0]; - if (result?.status === 'completed') { - setScenes(prev => prev.map(s => - s.id === id ? { - ...s, - status: 'completed', - result: result.result, - originalUrl: result.originalUrl, - } : s - )); - toast.success('重新生成成功'); - } else { - throw new Error(result?.error || '生成失败'); + // 处理流式响应 + const reader = response.body?.getReader(); + if (!reader) { + throw new Error('无法读取响应流'); + } + + const decoder = new TextDecoder(); + let buffer = ''; + let success = false; + + while (true) { + const { done, value } = await reader.read(); + + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (!line.trim()) continue; + + try { + const message = JSON.parse(line); + console.log('[SceneFusion] Regenerate message:', message.type); + + if (message.type === 'result') { + const result = message.data; + if (result.id === id) { + if (result.status === 'completed') { + setScenes(prev => prev.map(s => + s.id === id ? { + ...s, + status: 'completed', + result: result.result, + originalUrl: result.originalUrl, + } : s + )); + toast.success('重新生成成功'); + success = true; + } else { + throw new Error(result.error || '生成失败'); + } + } + } else if (message.type === 'error') { + throw new Error(message.error || '处理失败'); + } + } catch (parseError) { + if (parseError instanceof SyntaxError) { + console.error('[SceneFusion] Failed to parse message:', line); + } else { + throw parseError; + } + } + } + } + + if (!success) { + throw new Error('未收到有效的生成结果'); } } catch (error) { console.error('[SceneFusion] Regenerate error:', error); @@ -282,17 +333,30 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps } }; - // 下载图片 - const downloadImage = (url: string, filename: string) => { - const link = document.createElement('a'); - link.href = url; - link.download = filename; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + // 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题 + const downloadImage = async (url: string, filename: string) => { + try { + const response = await fetch(url); + const blob = await response.blob(); + const blobUrl = URL.createObjectURL(blob); + + const link = document.createElement('a'); + link.href = blobUrl; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // 释放 Blob URL + setTimeout(() => URL.revokeObjectURL(blobUrl), 100); + } catch (error) { + console.error('下载图片失败:', error); + // 降级方案:直接打开链接 + window.open(url, '_blank'); + } }; - // 全部下载 + // 全部下载 - 顺序下载所有图片 const downloadAll = async () => { const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result)); if (completedScenes.length === 0) { @@ -300,16 +364,22 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps return; } - toast.info(`正在下载 ${completedScenes.length} 张图片...`); + toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`); + // 顺序下载,每张图片下载完成后再下载下一张 for (let i = 0; i < completedScenes.length; i++) { const scene = completedScenes[i]; const url = scene.originalUrl || scene.result!; - // 延迟下载避免浏览器阻止 - setTimeout(() => { - downloadImage(url, `scene-fusion-${i + 1}.png`); - }, i * 300); + try { + await downloadImage(url, `scene-fusion-${i + 1}.png`); + // 添加小延迟避免浏览器阻止 + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (error) { + console.error(`下载第 ${i + 1} 张图片失败:`, error); + } } + + toast.success(`已下载 ${completedScenes.length} 张图片`); }; return ( diff --git a/components/scene-fusion-view.tsx b/components/scene-fusion-view.tsx index eae839b5e..691d80611 100644 --- a/components/scene-fusion-view.tsx +++ b/components/scene-fusion-view.tsx @@ -337,8 +337,8 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) { body: JSON.stringify({ productImage, productName: productName || undefined, - aspectRatio, // 图片比例 - imageSize, // 图片分辨率 + aspectRatio, + imageSize, scenes: [{ id: scene.id, image: scene.image, @@ -348,25 +348,76 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) { }), }); - const data = await response.json(); - if (!response.ok) { - throw new Error(data.error || '处理失败'); + // 尝试解析错误信息 + try { + const errorData = await response.json(); + throw new Error(errorData.error || '处理失败'); + } catch { + throw new Error(`请求失败: ${response.status}`); + } } - const result = data.results?.[0]; - if (result?.status === 'completed') { - setScenes(prev => prev.map(s => - s.id === id ? { - ...s, - status: 'completed', - result: result.result, - originalUrl: result.originalUrl, - } : s - )); - toast.success('重新生成成功'); - } else { - throw new Error(result?.error || '生成失败'); + // 处理流式响应 + const reader = response.body?.getReader(); + if (!reader) { + throw new Error('无法读取响应流'); + } + + const decoder = new TextDecoder(); + let buffer = ''; + let success = false; + + while (true) { + const { done, value } = await reader.read(); + + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (!line.trim()) continue; + + try { + const message = JSON.parse(line); + console.log('[SceneFusion] Regenerate message:', message.type); + + if (message.type === 'result') { + const result = message.data; + if (result.id === id) { + if (result.status === 'completed') { + setScenes(prev => prev.map(s => + s.id === id ? { + ...s, + status: 'completed', + result: result.result, + originalUrl: result.originalUrl, + } : s + )); + toast.success('重新生成成功'); + success = true; + } else { + throw new Error(result.error || '生成失败'); + } + } + } else if (message.type === 'error') { + throw new Error(message.error || '处理失败'); + } + } catch (parseError) { + if (parseError instanceof SyntaxError) { + console.error('[SceneFusion] Failed to parse message:', line); + } else { + throw parseError; + } + } + } + } + + if (!success) { + throw new Error('未收到有效的生成结果'); } } catch (error) { console.error('[SceneFusion] Regenerate error:', error); @@ -377,17 +428,30 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) { } }; - // 下载图片 - const downloadImage = (url: string, filename: string) => { - const link = document.createElement('a'); - link.href = url; - link.download = filename; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + // 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题 + const downloadImage = async (url: string, filename: string) => { + try { + const response = await fetch(url); + const blob = await response.blob(); + const blobUrl = URL.createObjectURL(blob); + + const link = document.createElement('a'); + link.href = blobUrl; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // 释放 Blob URL + setTimeout(() => URL.revokeObjectURL(blobUrl), 100); + } catch (error) { + console.error('下载图片失败:', error); + // 降级方案:直接打开链接 + window.open(url, '_blank'); + } }; - // 全部下载 + // 全部下载 - 顺序下载所有图片 const downloadAll = async () => { const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result)); if (completedScenes.length === 0) { @@ -395,16 +459,22 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) { return; } - toast.info(`正在下载 ${completedScenes.length} 张图片...`); + toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`); + // 顺序下载,每张图片下载完成后再下载下一张 for (let i = 0; i < completedScenes.length; i++) { const scene = completedScenes[i]; const url = scene.originalUrl || scene.result!; - // 延迟下载避免浏览器阻止 - setTimeout(() => { - downloadImage(url, `scene-fusion-${i + 1}.png`); - }, i * 300); + try { + await downloadImage(url, `scene-fusion-${i + 1}.png`); + // 添加小延迟避免浏览器阻止 + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (error) { + console.error(`下载第 ${i + 1} 张图片失败:`, error); + } } + + toast.success(`已下载 ${completedScenes.length} 张图片`); }; return (