修复Markdown导入和编辑器初始化问题

- 修复编辑器初始化时自动保存导致内容被破坏的问题
  - 将 allowOnUpdateRef 初始值从 true 改为 false
  - 避免编辑器加载内容时立即触发保存

- 移除编辑器内容更新时的 cleanupAccidentalStandaloneInlineCode 调用
  - 直接使用原始内容,避免误删正常的代码块

- 改进Markdown导入逻辑
  - 使用 importMarkdown 字段而非 content 字段
  - 让服务器端正确转换Markdown为HTML
  - 支持ZIP文件中的Markdown导入

- 新增导入后自动打开文档功能
  - 导入单个文件后自动打开该文档
  - 左侧文档树自动定位到导入的文档

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 18:25:46 +08:00
parent ce439db449
commit ea8113f072
2 changed files with 21 additions and 17 deletions
+3 -6
View File
@@ -619,7 +619,7 @@ export function Editor({ content, onChange, onEditorReady, onToggleAI, onExport,
}>({ sourceStartIndex: -1, sourceEndIndex: -1, targetInsertionIndex: -1 }); }>({ sourceStartIndex: -1, sourceEndIndex: -1, targetInsertionIndex: -1 });
const activeBlockIndexRef = useRef<number | null>(null); const activeBlockIndexRef = useRef<number | null>(null);
const suppressNextUpdateRef = useRef(false); const suppressNextUpdateRef = useRef(false);
const allowOnUpdateRef = useRef(true); const allowOnUpdateRef = useRef(false);
const { fontFamily, fontSize, lineHeight, tableLineHeight } = useSettingsStore(); const { fontFamily, fontSize, lineHeight, tableLineHeight } = useSettingsStore();
const editor = useEditor({ const editor = useEditor({
@@ -906,15 +906,12 @@ export function Editor({ content, onChange, onEditorReady, onToggleAI, onExport,
useEffect(() => { useEffect(() => {
if (editor && content !== editor.getHTML()) { if (editor && content !== editor.getHTML()) {
const normalizedContent = cleanupAccidentalStandaloneInlineCode(content); // 不再调用 cleanupAccidentalStandaloneInlineCode,直接使用原始内容
suppressNextUpdateRef.current = true; suppressNextUpdateRef.current = true;
allowOnUpdateRef.current = false; allowOnUpdateRef.current = false;
queueMicrotask(() => { queueMicrotask(() => {
editor.commands.setContent(normalizedContent); editor.commands.setContent(content);
}); });
if (normalizedContent !== content) {
onChange(normalizedContent);
}
} }
if (editor && onEditorReady) { if (editor && onEditorReady) {
onEditorReady(editor); onEditorReady(editor);
+18 -11
View File
@@ -26,7 +26,7 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
const [isImporting, setIsImporting] = useState(false); const [isImporting, setIsImporting] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const targetParentIdRef = useRef<string | null>(null); const targetParentIdRef = useRef<string | null>(null);
const { pages, fetchPages } = useEditorStore(); const { pages, fetchPages, setActivePageId } = useEditorStore();
const postPage = useCallback( const postPage = useCallback(
async ( async (
@@ -110,11 +110,12 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
if (!file) return; if (!file) return;
setIsImporting(true); setIsImporting(true);
let importedPageId: string | null = null;
try { try {
if (file.name.toLowerCase().endsWith(".zip")) { if (file.name.toLowerCase().endsWith(".zip")) {
await handleZipImport(file); await handleZipImport(file);
} else { } else {
await handleSingleFileImport(file); importedPageId = await handleSingleFileImport(file);
} }
} catch (error) { } catch (error) {
console.error("Import failed", error); console.error("Import failed", error);
@@ -122,39 +123,45 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
alert(message); alert(message);
} finally { } finally {
setIsImporting(false); setIsImporting(false);
fetchPages(); await fetchPages();
// 导入单个文件后,自动打开该文档
if (importedPageId) {
setActivePageId(importedPageId);
}
} }
}; };
const handleSingleFileImport = async (file: File) => { const handleSingleFileImport = async (file: File): Promise<string> => {
const text = await file.text(); const text = await file.text();
const title = file.name.replace(/\.md$/i, ""); const title = file.name.replace(/\.md$/i, "");
// 直接存储原始 Markdowntiptap-markdown 扩展会在编辑器加载时自动解析 // 使用 importMarkdown 字段,让服务器转换为 HTML
const parentId = targetParentIdRef.current; const parentId = targetParentIdRef.current;
const existing = pages.find((p) => p.type === "file" && p.parentId === parentId && (p.title || "").trim() === title.trim()); const existing = pages.find((p) => p.type === "file" && p.parentId === parentId && (p.title || "").trim() === title.trim());
if (existing) { if (existing) {
await putPage( await putPage(
existing.id, existing.id,
{ {
title, title,
content: text, importMarkdown: text,
parentId, parentId,
type: "file", type: "file",
}, },
"更新导入文件" "更新导入文件"
); );
return; return existing.id;
} }
await postPage( const result = await postPage(
{ {
title, title,
content: text, importMarkdown: text,
parentId, parentId,
type: "file", type: "file",
}, },
"创建导入文件" "创建导入文件"
); );
return result.id;
}; };
const handleZipImport = async (file: File) => { const handleZipImport = async (file: File) => {
@@ -255,7 +262,7 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
existing.id, existing.id,
{ {
title, title,
content: markdown, importMarkdown: markdown,
parentId, parentId,
type: "file", type: "file",
}, },
@@ -265,7 +272,7 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
await postPage( await postPage(
{ {
title, title,
content: markdown, importMarkdown: markdown,
parentId, parentId,
type: "file", type: "file",
}, },