diff --git a/package-lock.json b/package-lock.json index 01693cc..82cdf13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@google/genai": "^1.29.0", "@tailwindcss/vite": "^4.1.14", "@vitejs/plugin-react": "^5.0.4", + "diff": "^9.0.0", "dotenv": "^17.2.3", "express": "^4.21.2", "lucide-react": "^0.546.0", @@ -1957,6 +1958,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dotenv": { "version": "17.4.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", diff --git a/package.json b/package.json index 9e3207d..57eaf04 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@google/genai": "^1.29.0", "@tailwindcss/vite": "^4.1.14", "@vitejs/plugin-react": "^5.0.4", + "diff": "^9.0.0", "dotenv": "^17.2.3", "express": "^4.21.2", "lucide-react": "^0.546.0", diff --git a/src/pages/ReportEditor.tsx b/src/pages/ReportEditor.tsx index 8a0cf7d..3809ac7 100644 --- a/src/pages/ReportEditor.tsx +++ b/src/pages/ReportEditor.tsx @@ -12,6 +12,7 @@ import { User, Report, Template, CapturedFrame, SystemSettings, FormField, DEFAU import { defaultReportContent } from '../utils/defaultContent'; import { printDocument } from '../utils/print'; import { storage } from '../utils/storage'; +import { diffChars } from 'diff'; export default function ReportEditor() { const navigate = useNavigate(); @@ -823,6 +824,28 @@ export default function ReportEditor() { }).filter(r => r.id); }; + const stripHtml = (html: string): string => { + const tmp = document.createElement('div'); + tmp.innerHTML = html.replace(/<\/p>/gi, '

\n').replace(//gi, '\n'); + return (tmp.innerText || tmp.textContent || '').trim(); + }; + + const computeDiffHtml = (oldText: string, newText: string, side: 'left' | 'right'): string => { + const diffs = diffChars(oldText, newText); + let html = ''; + for (const part of diffs) { + let value = part.value.replace(//g, '>').replace(/\n/g, '
'); + if (side === 'left' && part.removed) { + html += `${value}`; + } else if (side === 'right' && part.added) { + html += `${value}`; + } else if (!part.added && !part.removed) { + html += value; + } + } + return html; + }; + const toggleListening = () => { if (isListening) { setIsListening(false); @@ -952,6 +975,9 @@ export default function ReportEditor() { if (responseJson.reply) { setChatMessages(prev => [...prev, { id: Date.now().toString(), role: 'model', content: responseJson.reply }]); } + if (aiModifyEnabled && !responseJson.updatedHtml) { + setChatMessages(prev => [...prev, { id: Date.now().toString(), role: 'model', content: '【系统提示】AI 未能生成修改内容,请尝试重新描述您的需求。' }]); + } if (responseJson.updatedHtml && aiModifyEnabled) { let cleanHtml = responseJson.updatedHtml; cleanHtml = cleanHtml.replace(//gi, ''); @@ -981,6 +1007,7 @@ export default function ReportEditor() { const confirmAiInjection = (newHtml: string, regionId: string) => { if (!editorRef.current) return; + const cleanHtml = newHtml.replace(/]*>(.*?)<\/span>/gi, '$2'); const targetContent = editorRef.current.querySelector(`.ai-region[data-ai-id="${regionId}"] .ai-content`) as HTMLElement; if (targetContent) { targetContent.focus(); @@ -2623,27 +2650,35 @@ export default function ReportEditor() { -
-
-
原始版本
-
-
-
-
- AI 提议版本 (可直接编辑) - 编辑态 + {(() => { + const oldText = stripHtml(diffModal.originalHtml); + const newText = stripHtml(diffModal.newHtml); + const leftDiffHtml = computeDiffHtml(oldText, newText, 'left'); + const rightDiffHtml = computeDiffHtml(oldText, newText, 'right'); + return ( +
+
+
原始版本
+
+
+
+
+ AI 提议版本 (可直接编辑) + 编辑态 +
+
setDiffModal(prev => prev ? { ...prev, newHtml: e.target.innerHTML } : null)} + dangerouslySetInnerHTML={{ __html: rightDiffHtml }} + style={{ fontFamily: 'SimSun, "Microsoft YaHei", serif', fontSize: '12pt', lineHeight: '1.5' }} + >
+
-
setDiffModal(prev => prev ? { ...prev, newHtml: e.target.innerHTML } : null)} - dangerouslySetInnerHTML={{ __html: diffModal.newHtml }} - style={{ fontFamily: 'SimSun, "Microsoft YaHei", serif', fontSize: '12pt', lineHeight: '1.5' }} - >
-
-
+ ); + })()}