mind-map/simple-mind-map/src/parse/xmind.js

336 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import JSZip from 'jszip'
import xmlConvert from 'xml-js'
import { getTextFromHtml, isUndef } from '../utils/index'
import {
getSummaryText,
getSummaryText2,
getRoot,
getItemByName,
getElementsByType,
addSummaryData,
handleNodeImageFromXmind,
handleNodeImageToXmind,
getXmindContentXmlData,
parseNodeGeneralizationToXmind
} from '../utils/xmind'
// 解析.xmind文件
const parseXmindFile = (file, handleMultiCanvas) => {
return new Promise((resolve, reject) => {
JSZip.loadAsync(file).then(
async zip => {
try {
let content = ''
let jsonFile = zip.files['content.json']
let xmlFile = zip.files['content.xml'] || zip.files['/content.xml']
if (jsonFile) {
let json = await jsonFile.async('string')
content = await transformXmind(json, zip.files, handleMultiCanvas)
} else if (xmlFile) {
let xml = await xmlFile.async('string')
let json = xmlConvert.xml2json(xml)
content = transformOldXmind(json)
}
if (content) {
resolve(content)
} else {
reject(new Error('解析失败'))
}
} catch (error) {
reject(error)
}
},
e => {
reject(e)
}
)
})
}
// 转换xmind数据
const transformXmind = async (content, files, handleMultiCanvas) => {
content = JSON.parse(content)
let data = null
if (content.length > 1 && typeof handleMultiCanvas === 'function') {
data = await handleMultiCanvas(content)
}
if (!data) {
data = content[0]
}
const nodeTree = data.rootTopic
const newTree = {}
const waitLoadImageList = []
const walk = async (node, newNode) => {
newNode.data = {
// 节点内容
text: isUndef(node.title) ? '' : node.title
}
// 节点备注
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
) {
node.children.attached.forEach((item, index) => {
const newChild = {}
newNode.children.push(newChild)
if (childrenSummary[index]) {
newChild._summary = childrenSummary[index]
}
walk(item, newChild)
})
}
}
walk(nodeTree, newTree)
await Promise.all(waitLoadImageList)
return newTree
}
// 转换旧版xmind数据xmind8
const transformOldXmind = content => {
const data = JSON.parse(content)
const elements = data.elements
const root = getRoot(elements)
const newTree = {}
const walk = (node, newNode) => {
const nodeElements = node.elements
let nodeTitle = getItemByName(nodeElements, 'title')
nodeTitle = nodeTitle && nodeTitle.elements && nodeTitle.elements[0].text
// 节点内容
newNode.data = {
text: isUndef(nodeTitle) ? '' : nodeTitle
}
// 节点备注
try {
const notesElement = getItemByName(nodeElements, 'notes')
if (notesElement) {
newNode.data.note =
notesElement.elements[0].elements[0].elements[0].text
}
} catch (error) {
console.log(error)
}
// 超链接
try {
if (
node.attributes &&
node.attributes['xlink:href'] &&
/^https?:\/\//.test(node.attributes['xlink:href'])
) {
newNode.data.hyperlink = node.attributes['xlink:href']
}
} catch (error) {
console.log(error)
}
// 标签
try {
const labelsElement = getItemByName(nodeElements, 'labels')
if (labelsElement) {
newNode.data.tag = labelsElement.elements.map(item => {
return item.elements[0].text
})
}
} catch (error) {
console.log(error)
}
const childrenItem = getItemByName(nodeElements, 'children')
// 概要
const selfSummary = []
const childrenSummary = []
try {
if (newNode._summary) {
selfSummary.push(newNode._summary)
}
const summariesItem = getItemByName(nodeElements, 'summaries')
if (
summariesItem &&
Array.isArray(summariesItem.elements) &&
summariesItem.elements.length > 0
) {
summariesItem.elements.forEach(item => {
addSummaryData(
selfSummary,
childrenSummary,
() => {
return getSummaryText2(childrenItem, item.attributes['topic-id'])
},
item.attributes.range
)
})
}
} catch (error) {
console.log(error)
}
newNode.data.generalization = selfSummary
// 子节点
newNode.children = []
if (
childrenItem &&
childrenItem.elements &&
childrenItem.elements.length > 0
) {
const children = getElementsByType(childrenItem.elements, 'attached')
;(children || []).forEach((item, index) => {
const newChild = {}
newNode.children.push(newChild)
if (childrenSummary[index]) {
newChild._summary = childrenSummary[index]
}
walk(item, newChild)
})
}
}
walk(root, newTree)
return newTree
}
// 数据转换为xmind文件
// 直接转换为最新版本的xmind文件 2023.09.11172
const transformToXmind = async (data, name) => {
const id = 'simpleMindMap_' + Date.now()
const imageList = []
// 转换核心数据
let newTree = {}
let waitLoadImageList = []
let walk = async (node, newNode, isRoot) => {
let newData = {
id: node.data.uid,
structureClass: 'org.xmind.ui.logic.right',
title: getTextFromHtml(node.data.text), // 节点文本
children: {
attached: []
}
}
// 备注
if (node.data.note !== undefined) {
newData.notes = {
realHTML: {
content: node.data.note
},
plain: {
content: node.data.note
}
}
}
// 超链接
if (node.data.hyperlink !== undefined) {
newData.href = node.data.hyperlink
}
// 标签
if (node.data.tag !== undefined) {
newData.labels = node.data.tag || []
}
// 图片
handleNodeImageToXmind(node, newNode, waitLoadImageList, imageList)
// 样式
// 暂时不考虑样式
if (isRoot) {
newData.class = 'topic'
newNode.id = id
newNode.class = 'sheet'
newNode.title = name
newNode.extensions = []
newNode.topicPositioning = 'fixed'
newNode.topicOverlapping = 'overlap'
newNode.coreVersion = '2.100.0'
newNode.rootTopic = newData
} else {
Object.keys(newData).forEach(key => {
newNode[key] = newData[key]
})
}
// 概要
const { summary, summaries } = parseNodeGeneralizationToXmind(node)
if (isRoot) {
if (summaries.length > 0) {
newNode.rootTopic.children.summary = summary
newNode.rootTopic.summaries = summaries
}
} else {
if (summaries.length > 0) {
newNode.children.summary = summary
newNode.summaries = summaries
}
}
// 子节点
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
let newChild = {}
walk(child, newChild)
newData.children.attached.push(newChild)
})
}
}
walk(data, newTree, true)
await Promise.all(waitLoadImageList)
const contentData = [newTree]
// 创建压缩包
const zip = new JSZip()
zip.file('content.json', JSON.stringify(contentData))
zip.file(
'metadata.json',
`{"modifier":"","dataStructureVersion":"2","creator":{"name":"mind-map"},"layoutEngineVersion":"3","activeSheetId":"${id}"}`
)
zip.file('content.xml', getXmindContentXmlData())
const manifestData = {
'file-entries': {
'content.json': {},
'metadata.json': {},
'Thumbnails/thumbnail.png': {}
}
}
// 图片
if (imageList.length > 0) {
imageList.forEach(item => {
manifestData['file-entries']['resources/' + item.name] = {}
const img = zip.folder('resources')
img.file(item.name, item.data, { base64: true })
})
}
zip.file('manifest.json', JSON.stringify(manifestData))
const zipData = await zip.generateAsync({ type: 'blob' })
return zipData
}
export default {
parseXmindFile,
transformXmind,
transformOldXmind,
transformToXmind
}