# 实现方案 — 2026-04-19-03-19-57 ## 1. 方案概述 在 `ReportEditor.tsx` 中完成两处修补:① 将 `chatInput` 纳入草稿持久化生命周期;② `handleAIGenerate` 中根据是否有图片动态选择 `content` 类型(字符串 vs 数组)。 ## 2. 详细步骤 ### 步骤 1:chatInput 持久化 **目标文件**:`src/pages/ReportEditor.tsx` **修改内容**: 1. `stateRef` 增加 `chatInput`: ```ts const stateRef = useRef({ reportData, videos, capturedFrames, activeTab, loadedTemplateId, chatMessages, chatInput }); ``` 2. `saveDraftToStorage` 增加 `chatInput`: ```ts chatMessages: stateRef.current.chatMessages, chatInput: stateRef.current.chatInput ``` 3. 在已有的 `useEffect` 监听 `chatMessages` 下方,增加监听 `chatInput`: ```ts useEffect(() => { stateRef.current.chatInput = chatInput; }, [chatInput]); ``` 4. 所有 4 处草稿恢复分支(初始化 useEffect 的 2 处 + useLayoutEffect 的 2 处)增加: ```ts if (draft.chatInput) setChatInput(draft.chatInput); stateRef.current = { ...stateRef.current, chatInput: draft.chatInput || '' }; ``` ### 步骤 2:API content 格式自适应 **目标文件**:`src/pages/ReportEditor.tsx` **修改内容**: 在 `handleAIGenerate` 中,将 `messageContent` 的组装逻辑改为: ```ts const selectedFrameUrls = aiSelectedFrames.map(id => capturedFrames.find(f => f.id === id)?.dataUrl).filter(Boolean); const allImages = [...selectedFrameUrls, ...aiUploadedImages.map(i => i.dataUrl)]; let promptText = `【医生指令】: ${text}`; if (aiModifyEnabled && targetRegionEl) { promptText = `【当前区域 HTML 源码】:\n${currentHtml}\n\n${promptText}`; } // 动态选择 content 类型 let finalContent: any = promptText; if (allImages.length > 0) { const visionContent: any[] = []; allImages.forEach(url => { visionContent.push({ type: 'image_url', image_url: { url } }); }); visionContent.push({ type: 'text', text: promptText }); finalContent = visionContent; } ``` 然后在 fetch body 中: ```ts messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: finalContent } ] ``` ## 3. 依赖关系 两步都在 `ReportEditor.tsx` 中,可视为同一批修改。 ## 4. 风险预案 - 若旧 draft 中无 `chatInput`,`setChatInput('')` 会清空输入框(符合预期) - 纯文本模型的 `content` 必须为 `string` 类型,不能是 `number` 或其他类型。`promptText` 是模板字符串,类型安全