Feat:1.支持导出为xmind新版文件;2.导入xmind新版文件支持处理图片
This commit is contained in:
parent
a03091b28c
commit
0bb50b3371
@ -1,5 +1,11 @@
|
|||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import xmlConvert from 'xml-js'
|
import xmlConvert from 'xml-js'
|
||||||
|
import {
|
||||||
|
getTextFromHtml,
|
||||||
|
imgToDataUrl,
|
||||||
|
parseDataUrl,
|
||||||
|
getImageSize
|
||||||
|
} from '../utils/index'
|
||||||
|
|
||||||
// 解析.xmind文件
|
// 解析.xmind文件
|
||||||
const parseXmindFile = file => {
|
const parseXmindFile = file => {
|
||||||
@ -10,7 +16,7 @@ const parseXmindFile = file => {
|
|||||||
let content = ''
|
let content = ''
|
||||||
if (zip.files['content.json']) {
|
if (zip.files['content.json']) {
|
||||||
let json = await zip.files['content.json'].async('string')
|
let json = await zip.files['content.json'].async('string')
|
||||||
content = transformXmind(json)
|
content = await transformXmind(json, zip.files)
|
||||||
} else if (zip.files['content.xml']) {
|
} else if (zip.files['content.xml']) {
|
||||||
let xml = await zip.files['content.xml'].async('string')
|
let xml = await zip.files['content.xml'].async('string')
|
||||||
let json = xmlConvert.xml2json(xml)
|
let json = xmlConvert.xml2json(xml)
|
||||||
@ -33,11 +39,12 @@ const parseXmindFile = file => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 转换xmind数据
|
// 转换xmind数据
|
||||||
const transformXmind = content => {
|
const transformXmind = async (content, files) => {
|
||||||
let data = JSON.parse(content)[0]
|
let data = JSON.parse(content)[0]
|
||||||
let nodeTree = data.rootTopic
|
let nodeTree = data.rootTopic
|
||||||
let newTree = {}
|
let newTree = {}
|
||||||
let walk = (node, newNode) => {
|
let waitLoadImageList = []
|
||||||
|
let walk = async (node, newNode) => {
|
||||||
newNode.data = {
|
newNode.data = {
|
||||||
// 节点内容
|
// 节点内容
|
||||||
text: node.title
|
text: node.title
|
||||||
@ -55,6 +62,42 @@ const transformXmind = content => {
|
|||||||
if (node.labels && node.labels.length > 0) {
|
if (node.labels && node.labels.length > 0) {
|
||||||
newNode.data.tag = node.labels
|
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 = []
|
newNode.children = []
|
||||||
if (
|
if (
|
||||||
@ -70,6 +113,7 @@ const transformXmind = content => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
walk(nodeTree, newTree)
|
walk(nodeTree, newTree)
|
||||||
|
await Promise.all(waitLoadImageList)
|
||||||
return newTree
|
return newTree
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +202,127 @@ const transformOldXmind = content => {
|
|||||||
return newTree
|
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 {
|
export default {
|
||||||
parseXmindFile,
|
parseXmindFile,
|
||||||
transformXmind,
|
transformXmind,
|
||||||
transformOldXmind
|
transformOldXmind,
|
||||||
|
transformToXmind
|
||||||
}
|
}
|
||||||
|
|||||||
@ -180,6 +180,17 @@ class Export {
|
|||||||
this.mindMap.doExportPDF.pdf(name, img)
|
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
|
// 导出为svg
|
||||||
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
|
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
|
||||||
async svg(name, plusCssText) {
|
async svg(name, plusCssText) {
|
||||||
|
|||||||
19
simple-mind-map/src/plugins/ExportXMind.js
Normal file
19
simple-mind-map/src/plugins/ExportXMind.js
Normal 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
|
||||||
@ -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) => {
|
export const downloadFile = (file, fileName) => {
|
||||||
let a = document.createElement('a')
|
let a = document.createElement('a')
|
||||||
@ -392,3 +404,23 @@ export const nodeToHTML = node => {
|
|||||||
nodeToHTMLWrapEl.appendChild(node)
|
nodeToHTMLWrapEl.appendChild(node)
|
||||||
return nodeToHTMLWrapEl.innerHTML
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user