修复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 });
const activeBlockIndexRef = useRef<number | null>(null);
const suppressNextUpdateRef = useRef(false);
const allowOnUpdateRef = useRef(true);
const allowOnUpdateRef = useRef(false);
const { fontFamily, fontSize, lineHeight, tableLineHeight } = useSettingsStore();
const editor = useEditor({
@@ -906,15 +906,12 @@ export function Editor({ content, onChange, onEditorReady, onToggleAI, onExport,
useEffect(() => {
if (editor && content !== editor.getHTML()) {
const normalizedContent = cleanupAccidentalStandaloneInlineCode(content);
// 不再调用 cleanupAccidentalStandaloneInlineCode,直接使用原始内容
suppressNextUpdateRef.current = true;
allowOnUpdateRef.current = false;
queueMicrotask(() => {
editor.commands.setContent(normalizedContent);
editor.commands.setContent(content);
});
if (normalizedContent !== content) {
onChange(normalizedContent);
}
}
if (editor && onEditorReady) {
onEditorReady(editor);
+18 -11
View File
@@ -26,7 +26,7 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
const [isImporting, setIsImporting] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const targetParentIdRef = useRef<string | null>(null);
const { pages, fetchPages } = useEditorStore();
const { pages, fetchPages, setActivePageId } = useEditorStore();
const postPage = useCallback(
async (
@@ -110,11 +110,12 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
if (!file) return;
setIsImporting(true);
let importedPageId: string | null = null;
try {
if (file.name.toLowerCase().endsWith(".zip")) {
await handleZipImport(file);
} else {
await handleSingleFileImport(file);
importedPageId = await handleSingleFileImport(file);
}
} catch (error) {
console.error("Import failed", error);
@@ -122,39 +123,45 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
alert(message);
} finally {
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 title = file.name.replace(/\.md$/i, "");
// 直接存储原始 Markdowntiptap-markdown 扩展会在编辑器加载时自动解析
// 使用 importMarkdown 字段,让服务器转换为 HTML
const parentId = targetParentIdRef.current;
const existing = pages.find((p) => p.type === "file" && p.parentId === parentId && (p.title || "").trim() === title.trim());
if (existing) {
await putPage(
existing.id,
{
title,
content: text,
importMarkdown: text,
parentId,
type: "file",
},
"更新导入文件"
);
return;
return existing.id;
}
await postPage(
const result = await postPage(
{
title,
content: text,
importMarkdown: text,
parentId,
type: "file",
},
"创建导入文件"
);
return result.id;
};
const handleZipImport = async (file: File) => {
@@ -255,7 +262,7 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
existing.id,
{
title,
content: markdown,
importMarkdown: markdown,
parentId,
type: "file",
},
@@ -265,7 +272,7 @@ export function ImportProvider({ children }: { children: React.ReactNode }) {
await postPage(
{
title,
content: markdown,
importMarkdown: markdown,
parentId,
type: "file",
},