Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion simple-mind-map/src/parse/xmind.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import JSZip from 'jszip'
import xmlConvert from 'xml-js'
import { v4 as uuid } from 'uuid'
import { getTextFromHtml, isUndef } from '../utils/index'
import {
getSummaryText,
Expand Down Expand Up @@ -48,9 +49,15 @@ const parseXmindFile = (file, handleMultiCanvas) => {
}

// 转换xmind数据
// PATCHED: relationships - Added support for importing XMind relationships as associative lines
const transformXmind = async (content, files, handleMultiCanvas) => {
content = JSON.parse(content)
let data = null

// PATCH: Map xmind node IDs to SimpleMindMap UIDs
const xmindIdToUid = new Map()
const uidToNewNode = new Map()

if (content.length > 1 && typeof handleMultiCanvas === 'function') {
data = await handleMultiCanvas(content)
}
Expand All @@ -60,11 +67,24 @@ const transformXmind = async (content, files, handleMultiCanvas) => {
const nodeTree = data.rootTopic
const newTree = {}
const waitLoadImageList = []

const walk = async (node, newNode) => {
// PATCH: Generate UID for this node
const nodeUid = uuid()

newNode.data = {
// 节点内容
text: isUndef(node.title) ? '' : node.title
text: isUndef(node.title) ? '' : node.title,
// PATCH: Assign UID to node
uid: nodeUid
}

// PATCH: Store mapping from xmind ID to our UID
if (node.id) {
xmindIdToUid.set(node.id, nodeUid)
uidToNewNode.set(nodeUid, newNode)
}

// 节点备注
if (node.notes) {
const notesData = node.notes.realHTML || node.notes.plain
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

🚨 异步递归 walk 未 await,relationships 处理可能早于节点映射完成

walk 被声明为 async,并且内部会调用 handleNodeImageFromXmind(可能向 waitLoadImageList 推入异步任务/依赖 files),但调用处 walk(nodeTree, newTree) 没有 await。随后立刻 await Promise.all(waitLoadImageList) 并处理 data.relationships。这会导致:

  1. 节点遍历/映射(xmindIdToUid、uidToNewNode)可能尚未完成就开始处理 relationships,从而丢失关联线;
  2. waitLoadImageList 可能在 walk 继续进行时才被 push,Promise.all 可能等待不完整。
    即便当前 walk 内部没有显式 await,也属于易碎实现,后续任何在 walk 内引入 await 都会立刻触发该竞态问题。

建议: 将 walk 的递归调用与根调用都改为 await,保证遍历完成后再等待图片任务、再处理 relationships。

Suggested change
const notesData = node.notes.realHTML || node.notes.plain
const walk = async (node, newNode) => {
// PATCH: Generate UID for this node
const nodeUid = uuid()
newNode.data = {
// 节点内容
text: isUndef(node.title) ? '' : node.title,
// PATCH: Assign UID to node
uid: nodeUid
}
// PATCH: Store mapping from xmind ID to our UID
if (node.id) {
xmindIdToUid.set(node.id, nodeUid)
uidToNewNode.set(nodeUid, newNode)
}
// 节点备注
if (node.notes) {
const notesData = node.notes.realHTML || node.notes.plain
newNode.data.note = notesData ? notesData.content || '' : ''
}
// 超链接
if (node.href && /^https?:\/\//.test(node.href)) {
newNode.data.hyperlink = node.href
}
// 标签
if (node.labels && node.labels.length > 0) {
newNode.data.tag = node.labels
}
// 图片
handleNodeImageFromXmind(node, newNode, waitLoadImageList, files)
// 概要
const selfSummary = []
const childrenSummary = []
if (newNode._summary) {
selfSummary.push(newNode._summary)
}
if (Array.isArray(node.summaries) && node.summaries.length > 0) {
node.summaries.forEach(item => {
addSummaryData(
selfSummary,
childrenSummary,
() => {
return getSummaryText(node, item.topicId)
},
item.range
)
})
}
newNode.data.generalization = selfSummary
// 子节点
newNode.children = []
if (
node.children &&
node.children.attached &&
node.children.attached.length > 0
) {
for (let index = 0; index < node.children.attached.length; index++) {
const item = node.children.attached[index]
const newChild = {}
newNode.children.push(newChild)
if (childrenSummary[index]) {
newChild._summary = childrenSummary[index]
}
await walk(item, newChild)
}
}
}
await walk(nodeTree, newTree)
await Promise.all(waitLoadImageList)

Expand Down Expand Up @@ -118,6 +138,44 @@ const transformXmind = async (content, files, handleMultiCanvas) => {
}
walk(nodeTree, newTree)
await Promise.all(waitLoadImageList)

// PATCH: Process relationships (associative lines)
const relationships = data.relationships || []
if (relationships.length > 0) {
relationships.forEach(rel => {
const sourceXmindId = rel.end1Id
const targetXmindId = rel.end2Id
const relationshipTitle = rel.title || ''

// Get the SimpleMindMap UIDs
const sourceUid = xmindIdToUid.get(sourceXmindId)
const targetUid = xmindIdToUid.get(targetXmindId)

if (sourceUid && targetUid) {
const sourceNode = uidToNewNode.get(sourceUid)
if (sourceNode && sourceNode.data) {
// Initialize associative line arrays if not present
if (!sourceNode.data.associativeLineTargets) {
sourceNode.data.associativeLineTargets = []
}
if (!sourceNode.data.associativeLineText) {
sourceNode.data.associativeLineText = {}
}

// Add the target UID to the associative line targets
if (!sourceNode.data.associativeLineTargets.includes(targetUid)) {
sourceNode.data.associativeLineTargets.push(targetUid)
}

// Add the relationship title/label
if (relationshipTitle) {
sourceNode.data.associativeLineText[targetUid] = relationshipTitle
}
}
}
})
}

return newTree
}

Expand Down