- confirmAiInjection改用Range.selectNodeContents + execCommand('insertHTML')保留浏览器撤销栈
- handleAIGenerate中对cleanHtml增加<p>标签内联样式注入:padding 0px、font-family SimSun、font-size 12pt、line-height 1.5
- 确保AI替换后的文字字体与原有文字完全一致
95 lines
3.4 KiB
Markdown
95 lines
3.4 KiB
Markdown
# 实现方案
|
||
|
||
## 修改文件
|
||
- `src/pages/ReportEditor.tsx`
|
||
|
||
## 修改 1:`confirmAiInjection` 保留撤销栈(约 line 981-998)
|
||
|
||
**原代码**:
|
||
```tsx
|
||
const confirmAiInjection = (newHtml: string, regionId: string) => {
|
||
if (!editorRef.current) return;
|
||
const targetContent = editorRef.current.querySelector(`.ai-region[data-ai-id="${regionId}"] .ai-content`) as HTMLElement;
|
||
if (targetContent) {
|
||
targetContent.innerHTML = newHtml;
|
||
targetContent.style.transition = 'background-color 0.3s ease';
|
||
targetContent.style.backgroundColor = '#bfdbfe';
|
||
setTimeout(() => {
|
||
targetContent.style.backgroundColor = '#eff6ff';
|
||
setTimeout(() => {
|
||
targetContent.style.backgroundColor = 'transparent';
|
||
}, 800);
|
||
}, 400);
|
||
contentRef.current = editorRef.current.innerHTML;
|
||
saveDraftToStorage();
|
||
}
|
||
setDiffModal(null);
|
||
};
|
||
```
|
||
|
||
**新代码**:
|
||
```tsx
|
||
const confirmAiInjection = (newHtml: string, regionId: string) => {
|
||
if (!editorRef.current) return;
|
||
const targetContent = editorRef.current.querySelector(`.ai-region[data-ai-id="${regionId}"] .ai-content`) as HTMLElement;
|
||
if (targetContent) {
|
||
targetContent.focus();
|
||
const sel = window.getSelection();
|
||
const range = document.createRange();
|
||
range.selectNodeContents(targetContent);
|
||
sel?.removeAllRanges();
|
||
sel?.addRange(range);
|
||
document.execCommand('insertHTML', false, newHtml);
|
||
targetContent.style.transition = 'background-color 0.3s ease';
|
||
targetContent.style.backgroundColor = '#bfdbfe';
|
||
setTimeout(() => {
|
||
targetContent.style.backgroundColor = '#eff6ff';
|
||
setTimeout(() => {
|
||
targetContent.style.backgroundColor = 'transparent';
|
||
}, 800);
|
||
}, 400);
|
||
contentRef.current = editorRef.current.innerHTML;
|
||
saveDraftToStorage();
|
||
}
|
||
setDiffModal(null);
|
||
};
|
||
```
|
||
|
||
**变更点**:
|
||
1. 去掉 `targetContent.innerHTML = newHtml;`
|
||
2. 增加 `targetContent.focus()`
|
||
3. 使用 `Range.selectNodeContents(targetContent)` 选中区域内所有旧内容
|
||
4. 使用 `document.execCommand('insertHTML', false, newHtml)` 执行替换
|
||
5. 浏览器撤销栈会记录这次替换,Ctrl+Z 可正常撤销
|
||
|
||
## 修改 2:`handleAIGenerate` 中 `<p>` 标签注入样式(约 line 955-970)
|
||
|
||
**原代码**:
|
||
```tsx
|
||
if (responseJson.updatedHtml && aiModifyEnabled) {
|
||
let cleanHtml = responseJson.updatedHtml;
|
||
cleanHtml = cleanHtml.replace(/<br\s*\/?>/gi, '');
|
||
cleanHtml = cleanHtml.replace(/<\/p>\s*<p>/gi, '</p><p>');
|
||
cleanHtml = cleanHtml.trim();
|
||
if (targetRegionEl) {
|
||
```
|
||
|
||
**新代码**:
|
||
```tsx
|
||
if (responseJson.updatedHtml && aiModifyEnabled) {
|
||
let cleanHtml = responseJson.updatedHtml;
|
||
cleanHtml = cleanHtml.replace(/<br\s*\/?>/gi, '');
|
||
cleanHtml = cleanHtml.replace(/<\/p>\s*<p>/gi, '</p><p>');
|
||
cleanHtml = cleanHtml.trim();
|
||
cleanHtml = cleanHtml.replace(/<p>/gi, '<p style="padding: 0px; font-family: SimSun; font-size: 12pt; line-height: 1.5;">');
|
||
if (targetRegionEl) {
|
||
```
|
||
|
||
**变更点**:在 `cleanHtml.trim()` 后增加一行正则替换,将所有 `<p>` 标签替换为带标准内联样式的 `<p>` 标签。
|
||
|
||
**样式值说明**:
|
||
- `padding: 0px`:与原始段落一致
|
||
- `font-family: SimSun`:宋体
|
||
- `font-size: 12pt`:12pt 字号
|
||
- `line-height: 1.5`:1.5 倍行高
|