0.1.07 文件树拖拽与多思维导图

This commit is contained in:
liaibo
2026-01-08 06:28:14 +08:00
parent 9de2d69c55
commit 9af8b9e489
30 changed files with 817 additions and 410 deletions
@@ -334,16 +334,17 @@ const MindmapBlockView = ({
: ""),
[block.props.docId],
);
const mindmapId = block.id;
useEffect(() => {
hasLocalEditsRef.current = false;
applyingRemoteRef.current = false;
}, [docId]);
const autosaveKey = useMemo(
() => `${STORAGE_PREFIX}${docId || block.id}`,
[block.id, docId],
);
const autosaveKey = useMemo(() => {
if (docId) return `${STORAGE_PREFIX}${docId}:${mindmapId}`;
return `${STORAGE_PREFIX}${mindmapId}`;
}, [docId, mindmapId]);
const initialDataRef = useRef<unknown>(null);
if (initialDataRef.current === null) {
const cached = typeof window !== "undefined" ? window.localStorage.getItem(autosaveKey) : null;
@@ -364,7 +365,8 @@ const MindmapBlockView = ({
if (!docId) return;
(async () => {
try {
const resp = await fetch(`/api/mindmap/${docId}`);
// 多导图:按 docId + mindmapId 拉取
const resp = await fetch(`/api/mindmap/${docId}/${mindmapId}`);
if (!resp.ok) return;
const payload = await resp.json().catch(() => null);
const data = payload?.data;
@@ -390,7 +392,7 @@ const MindmapBlockView = ({
return () => {
cancelled = true;
};
}, [docId, mindmap]);
}, [docId, mindmap, mindmapId]);
// 记录“最近一次指针交互是否发生在思维导图块内”,用于键盘快捷键作用域
useEffect(() => {
@@ -617,26 +619,27 @@ const MindmapBlockView = ({
editor.updateBlock(block, { props: { ...block.props, data: safe } });
if (docId) {
// 同步到本地文件 + Supabase(弱依赖)
fetch(`/api/mindmap/${docId}`, {
fetch(`/api/mindmap/${docId}/${mindmapId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ data: safe }),
})
.then((resp) => {
if (resp.ok) {
const fileName = `mindmap-${mindmapId}.json`;
emitAssetsChanged(docId, {
id: `mindmap-${docId}`,
id: mindmapId,
document_id: docId,
asset_type: "mindmap",
file_name: "mindmap.json",
file_url: `/documents/${docId}`,
file_name: fileName,
file_url: `/documents/${docId}/${fileName}`,
});
}
})
.catch((err) => console.warn("思维导图同步失败", err));
}
},
[autosaveKey, block, docId, editor],
[autosaveKey, block, docId, editor, mindmapId],
);
const debouncedPersist = useDebouncedCallback((data: unknown) => {
@@ -652,7 +655,7 @@ const MindmapBlockView = ({
);
(async () => {
try {
const resp = await fetch(`/api/mindmap/${docId}`, {
const resp = await fetch(`/api/mindmap/${docId}/${mindmapId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ data }),
@@ -668,16 +671,17 @@ const MindmapBlockView = ({
console.warn("初次创建思维导图文件失败", err);
} finally {
// 即便远端失败,也通知侧边栏刷新,保证本地文件树及时更新
const fileName = `mindmap-${mindmapId}.json`;
emitAssetsChanged(docId, {
id: `mindmap-${docId}`,
id: mindmapId,
document_id: docId,
asset_type: "mindmap",
file_name: "mindmap.json",
file_url: `/documents/${docId}`,
file_name: fileName,
file_url: `/documents/${docId}/${fileName}`,
});
}
})();
}, [docId, mindmap, initialDataRef]);
}, [docId, mindmap, mindmapId, initialDataRef]);
// 初始选中根节点,后续不强制抢焦点,允许用户自由选择
useEffect(() => {
@@ -691,17 +695,18 @@ const MindmapBlockView = ({
}
}, [mindmap, activeNodes.length]);
// mindmap 实例一旦就绪,立刻向侧边栏广播,确保文件树即时显示 mindmap.json
// mindmap 实例一旦就绪,立刻向侧边栏广播,确保文件树即时显示 mindmap-<id>.json
useEffect(() => {
if (!docId || !mindmap) return;
const fileName = `mindmap-${mindmapId}.json`;
emitAssetsChanged(docId, {
id: `mindmap-${docId}`,
id: mindmapId,
document_id: docId,
asset_type: "mindmap",
file_name: "mindmap.json",
file_url: `/documents/${docId}`,
file_name: fileName,
file_url: `/documents/${docId}/${fileName}`,
});
}, [docId, mindmap]);
}, [docId, mindmap, mindmapId]);
useEffect(() => {
let destroyed = false;
@@ -982,6 +987,11 @@ const MindmapBlockView = ({
if (typeof window !== "undefined") {
// 便于开发阶段在控制台直接调试实例
window.__mindmapInstance = instance;
const w = window as unknown as {
__mindmapInstancesById?: Record<string, MindMapInstance>;
};
if (!w.__mindmapInstancesById) w.__mindmapInstancesById = {};
w.__mindmapInstancesById[mindmapId] = instance;
}
setMindmap(instance);
@@ -1087,9 +1097,13 @@ const MindmapBlockView = ({
if (!docId || typeof window === "undefined") return false;
try {
const w = window as unknown as {
__wolaiMindmapDeletingDocIds?: Set<string>;
__wolaiMindmapDeletingKeys?: Set<string>;
};
return Boolean(w.__wolaiMindmapDeletingDocIds?.has(docId));
const key = `${docId}:${mindmapId}`;
return Boolean(
w.__wolaiMindmapDeletingKeys?.has(docId) ||
w.__wolaiMindmapDeletingKeys?.has(key),
);
} catch {
return false;
}
@@ -1111,7 +1125,7 @@ const MindmapBlockView = ({
// ignore
}
if (docId) {
fetch(`/api/mindmap/${docId}`, {
fetch(`/api/mindmap/${docId}/${mindmapId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ data: safe }),
@@ -1129,6 +1143,14 @@ const MindmapBlockView = ({
if (window.__mindmapInstance === createdInstance) {
window.__mindmapInstance = null;
}
try {
const w = window as unknown as { __mindmapInstancesById?: Record<string, MindMapInstance> };
if (w.__mindmapInstancesById && w.__mindmapInstancesById[mindmapId] === createdInstance) {
delete w.__mindmapInstancesById[mindmapId];
}
} catch {
// ignore
}
}
setMindmap(null);
mindmapRef.current = null;
@@ -1628,7 +1650,7 @@ const MindmapBlockView = ({
const confirmed = window.confirm("确认删除当前思维导图?此操作会移除文件并清空侧边栏记录。");
if (!confirmed) return;
deletingRef.current = true;
const resp = await fetch(`/api/mindmap/${docId}`, { method: "DELETE" });
const resp = await fetch(`/api/mindmap/${docId}/${mindmapId}`, { method: "DELETE" });
if (!resp.ok) {
const payload = await resp.json().catch(() => ({}));
window.alert(payload?.error ?? "删除思维导图失败");
@@ -1640,9 +1662,14 @@ const MindmapBlockView = ({
} catch {
// ignore
}
emitAssetsChanged(docId, undefined, undefined, true);
editor.removeBlocks([block.id]);
}, [autosaveKey, block.id, docId, editor]);
emitAssetsChanged(docId, undefined, undefined, false, [mindmapId]);
// 同时会触发全局删除监听(ASSETS_CHANGED_EVENT)进行块移除,这里做 try/catch 避免重复删除报错
try {
editor.removeBlocks([block.id]);
} catch {
// ignore
}
}, [autosaveKey, block.id, docId, editor, mindmapId]);
const toolbarProps = {
canBack,
@@ -1928,6 +1955,7 @@ const MindmapBlockView = ({
ref={containerRef}
className="h-full w-full"
data-testid="mindmap-canvas"
data-mindmap-id={mindmapId}
contentEditable={false}
/>
@@ -2011,6 +2039,7 @@ const MindmapBlockView = ({
ref={containerRef}
className="h-full w-full"
data-testid="mindmap-canvas"
data-mindmap-id={mindmapId}
contentEditable={false}
/>