- 将'允许修改正文'复选框从id/htmlFor绑定改为label直接包裹input,增加e.stopPropagation防止事件冒泡被拦截 - handleAIGenerate中新增editorRef.current.innerText作为全局上下文注入prompt - currentHtml增加过滤​零宽字符 - 优化systemPrompt,明确告知大模型全局参考内容+目标区域源码的双信息源结构
2.5 KiB
2.5 KiB
实现方案 — 2026-04-19-03-19-57
1. 方案概述
在 ReportEditor.tsx 中完成两处修补:① 将 chatInput 纳入草稿持久化生命周期;② handleAIGenerate 中根据是否有图片动态选择 content 类型(字符串 vs 数组)。
2. 详细步骤
步骤 1:chatInput 持久化
目标文件:src/pages/ReportEditor.tsx
修改内容:
stateRef增加chatInput:const stateRef = useRef({ reportData, videos, capturedFrames, activeTab, loadedTemplateId, chatMessages, chatInput });saveDraftToStorage增加chatInput:chatMessages: stateRef.current.chatMessages, chatInput: stateRef.current.chatInput- 在已有的
useEffect监听chatMessages下方,增加监听chatInput:useEffect(() => { stateRef.current.chatInput = chatInput; }, [chatInput]); - 所有 4 处草稿恢复分支(初始化 useEffect 的 2 处 + useLayoutEffect 的 2 处)增加:
if (draft.chatInput) setChatInput(draft.chatInput); stateRef.current = { ...stateRef.current, chatInput: draft.chatInput || '' };
步骤 2:API content 格式自适应
目标文件:src/pages/ReportEditor.tsx
修改内容:
在 handleAIGenerate 中,将 messageContent 的组装逻辑改为:
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 中:
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: finalContent }
]
3. 依赖关系
两步都在 ReportEditor.tsx 中,可视为同一批修改。
4. 风险预案
- 若旧 draft 中无
chatInput,setChatInput('')会清空输入框(符合预期) - 纯文本模型的
content必须为string类型,不能是number或其他类型。promptText是模板字符串,类型安全