Feat:1.支持导出为xmind新版文件;2.导入xmind新版文件支持处理图片

This commit is contained in:
wanglin2
2023-07-05 13:58:55 +08:00
parent a03091b28c
commit 0bb50b3371
4 changed files with 229 additions and 4 deletions

View File

@@ -1,5 +1,11 @@
import JSZip from 'jszip'
import xmlConvert from 'xml-js'
import {
getTextFromHtml,
imgToDataUrl,
parseDataUrl,
getImageSize
} from '../utils/index'
// 解析.xmind文件
const parseXmindFile = file => {
@@ -10,7 +16,7 @@ const parseXmindFile = file => {
let content = ''
if (zip.files['content.json']) {
let json = await zip.files['content.json'].async('string')
content = transformXmind(json)
content = await transformXmind(json, zip.files)
} else if (zip.files['content.xml']) {
let xml = await zip.files['content.xml'].async('string')
let json = xmlConvert.xml2json(xml)
@@ -33,11 +39,12 @@ const parseXmindFile = file => {
}
// 转换xmind数据
const transformXmind = content => {
const transformXmind = async (content, files) => {
let data = JSON.parse(content)[0]
let nodeTree = data.rootTopic
let newTree = {}
let walk = (node, newNode) => {
let waitLoadImageList = []
let walk = async (node, newNode) => {
newNode.data = {
// 节点内容
text: node.title
@@ -55,6 +62,42 @@ const transformXmind = content => {
if (node.labels && node.labels.length > 0) {
newNode.data.tag = node.labels
}
// 图片
if (node.image && /\.(jpg|jpeg|png|gif|webp)$/.test(node.image.src)) {
try {
// 处理异步逻辑
let resolve = null
let promise = new Promise(_resolve => {
resolve = _resolve
})
waitLoadImageList.push(promise)
// 读取图片
let imageType = /\.([^.]+)$/.exec(node.image.src)[1]
let imageBase64 =
`data:image/${imageType};base64,` +
(await files['resources/' + node.image.src.split('/')[1]].async(
'base64'
))
newNode.data.image = imageBase64
// 如果图片尺寸不存在
if (!node.image.width && !node.image.height) {
let imageSize = await getImageSize(imageBase64)
newNode.data.imageSize = {
width: imageSize.width,
height: imageSize.height
}
} else {
newNode.data.imageSize = {
width: node.image.width,
height: node.image.height
}
}
resolve()
} catch (error) {
console.log(error)
resolve()
}
}
// 子节点
newNode.children = []
if (
@@ -70,6 +113,7 @@ const transformXmind = content => {
}
}
walk(nodeTree, newTree)
await Promise.all(waitLoadImageList)
return newTree
}
@@ -158,8 +202,127 @@ const transformOldXmind = content => {
return newTree
}
// 数据转换为xmind文件
const transformToXmind = async (data, name) => {
const id = 'simpleMindMap_' + Date.now()
const imageList = []
// 转换核心数据
let newTree = {}
let waitLoadImageList = []
let walk = async (node, newNode, isRoot) => {
let newData = {
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 || []
}
// 图片
if (node.data.image) {
try {
// 处理异步逻辑
let resolve = null
let promise = new Promise(_resolve => {
resolve = _resolve
})
waitLoadImageList.push(promise)
let imgName = ''
let imgData = node.data.image
// 网络图片要先转换成data:url
if (/^https?:\/\//.test(node.data.image)) {
imgData = await imgToDataUrl(node.data.image)
}
// 从data:url中解析出图片类型和base64
let dataUrlRes = parseDataUrl(imgData)
imgName = 'image_' + imageList.length + '.' + dataUrlRes.type
imageList.push({
name: imgName,
data: dataUrlRes.base64
})
newData.image = {
src: 'xap:resources/' + imgName,
width: node.data.imageSize.width,
height: node.data.imageSize.height
}
resolve()
} catch (error) {
console.log(error)
resolve()
}
}
// 样式
// 暂时不考虑样式
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]
})
}
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":"1","layoutEngineVersion":"2","activeSheetId":"${id}"}`
)
const manifestData = {
'file-entries': { 'content.json': {}, 'metadata.json': {} }
}
// 图片
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
transformOldXmind,
transformToXmind
}

View File

@@ -180,6 +180,17 @@ class Export {
this.mindMap.doExportPDF.pdf(name, img)
}
// 导出为xmind
async xmind(name) {
if (!this.mindMap.doExportXMind) {
throw new Error('请注册ExportXMind插件')
}
const data = this.mindMap.getData()
const blob = await this.mindMap.doExportXMind.xmind(data, name)
const res = await readBlob(blob)
return res
}
// 导出为svg
// plusCssText附加的css样式如果svg中存在dom节点想要设置一些针对节点的样式可以通过这个参数传入
async svg(name, plusCssText) {

View File

@@ -0,0 +1,19 @@
import xmind from '../parse/xmind'
// 导出XMind类需要通过Export插件使用
class ExportXMind {
// 构造函数
constructor(opt) {
this.mindMap = opt.mindMap
}
// 导出xmind
async xmind(data, name) {
const zipData = await xmind.transformToXmind(data, name)
return zipData
}
}
ExportXMind.instanceName = 'doExportXMind'
export default ExportXMind

View File

@@ -213,6 +213,18 @@ export const imgToDataUrl = src => {
})
}
// 解析dataUrl
export const parseDataUrl = data => {
if (!/^data:/.test(data)) return data
let [typeStr, base64] = data.split(',')
let res = /^data:[^/]+\/([^;]+);/.exec(typeStr)
let type = res[1]
return {
type,
base64
}
}
// 下载文件
export const downloadFile = (file, fileName) => {
let a = document.createElement('a')
@@ -392,3 +404,23 @@ export const nodeToHTML = node => {
nodeToHTMLWrapEl.appendChild(node)
return nodeToHTMLWrapEl.innerHTML
}
// 获取图片大小
export const getImageSize = src => {
return new Promise(resolve => {
let img = new Image()
img.src = src
img.onload = () => {
resolve({
width: img.width,
height: img.height
})
}
img.onerror = () => {
resolve({
width: 0,
height: 0
})
}
})
}