- 将'允许修改正文'复选框从id/htmlFor绑定改为label直接包裹input,增加e.stopPropagation防止事件冒泡被拦截 - handleAIGenerate中新增editorRef.current.innerText作为全局上下文注入prompt - currentHtml增加过滤​零宽字符 - 优化systemPrompt,明确告知大模型全局参考内容+目标区域源码的双信息源结构
68 lines
2.5 KiB
Markdown
68 lines
2.5 KiB
Markdown
# 实现方案 — 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` 是模板字符串,类型安全
|