diff --git a/frontend/src/components/FullMarkdownEditor.tsx b/frontend/src/components/FullMarkdownEditor.tsx index 63ab680..56f9050 100644 --- a/frontend/src/components/FullMarkdownEditor.tsx +++ b/frontend/src/components/FullMarkdownEditor.tsx @@ -121,6 +121,10 @@ export const FullMarkdownEditor: React.FC = () => { useEffect(() => { updatePreview(editorState.content); saveToLocalStorage('content', editorState.content); + // 确保行号正确显示 + if (lineNumbersRef.current && editorRef.current) { + handleEditorScroll(); + } }, [editorState.content, updatePreview]); // 处理内容变化 @@ -220,7 +224,7 @@ export const FullMarkdownEditor: React.FC = () => { }; // 正则表达式工具功能 - const applyRegexReplace = async () => { + const previewRegexReplace = async () => { if (!regexPattern.trim()) { alert('请输入正则表达式模式'); return; @@ -237,20 +241,77 @@ export const FullMarkdownEditor: React.FC = () => { setRegexResult(result.result); setRegexMatches(result.matches); - - if (confirm(`找到 ${result.matches} 处匹配,是否应用更改?`)) { - setEditorState(prev => ({ ...prev, content: result.result })); - } } catch (error) { alert(`正则表达式错误: ${error instanceof Error ? error.message : '未知错误'}`); + setRegexResult(''); + setRegexMatches(0); } finally { setIsRegexProcessing(false); } }; - const applyRegexToResult = () => { - setEditorState(prev => ({ ...prev, content: regexResult })); + const applyRegexToEditor = () => { + if (!regexResult) { + alert('请先预览正则替换结果'); + return; + } + + setEditorState(prev => ({ + ...prev, + content: regexResult, + isDirty: true + })); setShowRegexPanel(false); + // 清空预览结果 + setRegexResult(''); + setRegexMatches(0); + }; + + const clearRegexPreview = () => { + setRegexResult(''); + setRegexMatches(0); + }; + + // 编辑器和预览的滚动同步 + const editorRef = React.useRef(null); + const previewRef = React.useRef(null); + const lineNumbersRef = React.useRef(null); + const editorContainerRef = React.useRef(null); + + // 同步滚动 - 编辑器滚动时同步预览和行号 + const handleEditorScroll = () => { + if (editorRef.current && previewRef.current && lineNumbersRef.current) { + const scrollTop = editorRef.current.scrollTop; + + // 使用transform实现行号与内容的精确同步滚动 + lineNumbersRef.current.style.transform = `translateY(${-scrollTop}px)`; + + // 计算预览的对应滚动位置 + const editorHeight = editorRef.current.scrollHeight - editorRef.current.clientHeight; + const previewHeight = previewRef.current.scrollHeight - previewRef.current.clientHeight; + + if (editorHeight > 0) { + const ratio = scrollTop / editorHeight; + previewRef.current.scrollTop = Math.min(previewHeight * ratio, previewHeight); + } + } + }; + + const handlePreviewScroll = () => { + if (editorRef.current && previewRef.current && lineNumbersRef.current) { + const scrollTop = previewRef.current.scrollTop; + + // 计算编辑器的对应滚动位置 + const previewHeight = previewRef.current.scrollHeight - previewRef.current.clientHeight; + const editorHeight = editorRef.current.scrollHeight - editorRef.current.clientHeight; + + if (previewHeight > 0) { + const ratio = scrollTop / previewHeight; + const newEditorScroll = Math.min(editorHeight * ratio, editorHeight); + editorRef.current.scrollTop = newEditorScroll; + lineNumbersRef.current.style.transform = `translateY(${-newEditorScroll}px)`; + } + } }; // 打开文件浏览器时加载文件列表 @@ -289,7 +350,7 @@ export const FullMarkdownEditor: React.FC = () => {

文件浏览器

-
+
{ 新建
+
+ { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + const content = event.target?.result as string; + setEditorState(prev => ({ + ...prev, + content: content, + filePath: file.name, + isDirty: false + })); + setShowFileExplorer(false); + // 强制刷新预览和行号 + setTimeout(() => { + if (editorRef.current) { + handleEditorScroll(); + } + }, 100); + }; + reader.readAsText(file); + } + }} + style={{ display: 'none' }} + id="file-input" + /> + +
@@ -382,25 +480,77 @@ export const FullMarkdownEditor: React.FC = () => {
{/* 编辑器 */} -
+
编辑器
-