diff --git a/README.md b/README.md index e26110e0..39d7d291 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,13 @@ - [x] 支持多种节点形状 +- [x] 支持导出为`json`、`png`、`svg`、`pdf`,支持从`json`、`xmind`导入 + ## 目录介绍 1.`simple-mind-map` -思维导图工具库。 +思维导图工具库,框架无关,`Vue`、`React`等框架或无框架都可以使用。 2.`web` @@ -32,7 +34,7 @@ 3.`dist` -打包后的资源文件夹。 +打包`web`后的资源文件夹。 4.`docs` @@ -88,7 +90,7 @@ npm run build # 安装 -> 当然仓库版本:0.2.6,当前npm版本:0.2.5 +> 当然仓库版本:0.2.7,当前npm版本:0.2.6 ```bash npm i simple-mind-map @@ -100,7 +102,7 @@ npm i simple-mind-map > >```js >module.exports = { -> transpileDependencies: ['simple-mind-map'] +> transpileDependencies: ['simple-mind-map'] >} >``` > @@ -123,7 +125,31 @@ const mindMap = new MindMap({ }); ``` +### Xmind解析方法 +v0.2.7+ + +可以通过如下方法获取解析`Xmind`文件的方法: + +```js +import MindMap from "simple-mind-map"; + +console.log(MindMap.xmind) +``` + +`MindMap.xmind`对象上挂载了两个方法: + +#### parseXmindFile(file) + +解析`.xmind`文件,返回解析后的数据,注意是完整的数据,包含节点树、主题、结构等,可以使用`mindMap.setFullData(data)`来将返回的数据渲染到画布上 + +`file`:`File`对象 + +#### transformXmind(content) + +转换`xmind`数据,`.xmind`文件本质上是一个压缩包,改成`zip`后缀可以解压缩,里面存在一个`content.json`文件,如果你自己解析出了这个文件,那么可以把这个文件内容传递给这个方法进行转换,转换后的数据,注意是完整的数据,包含节点树、主题、结构等,可以使用`mindMap.setFullData(data)`来将返回的数据渲染到画布上 + +`content`:`.xmind`压缩包内的`content.json`文件内容 ### 实例化选项: @@ -291,10 +317,18 @@ v0.1.7+。切换模式为只读或编辑。 #### setData(data) -动态设置思维导图数据 +动态设置思维导图数据,纯节点数据 `data`:思维导图结构数据 +#### setFullData(*data*) + +v0.2.7+ + +动态设置思维导图数据,包括节点数据、布局、主题、视图 + +`data`:完整数据,结构可参考[exportFullData](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exportFullData.json) + #### export(type, isDownload, fileName) diff --git a/simple-mind-map/example/exportFullData.json b/simple-mind-map/example/exportFullData.json new file mode 100644 index 00000000..2ef547b1 --- /dev/null +++ b/simple-mind-map/example/exportFullData.json @@ -0,0 +1,66 @@ +{ + "layout": "logicalStructure", + "root": { + "data": { + "text": "根节点", + "expand": true, + "isActive": false + }, + "children": [{ + "data": { + "text": "二级节点", + "generalization": { + "text": "概要", + "expand": true, + "isActive": false + }, + "expand": true, + "isActive": false + }, + "children": [{ + "data": { + "text": "分支主题", + "expand": true, + "isActive": false + }, + "children": [] + }, { + "data": { + "text": "分支主题", + "expand": true, + "isActive": false + }, + "children": [] + }] + }] + }, + "theme": { + "template": "classic4", + "config": {} + }, + "view": { + "transform": { + "scaleX": 1, + "scaleY": 1, + "shear": 0, + "rotate": 0, + "translateX": 0, + "translateY": 0, + "originX": 0, + "originY": 0, + "a": 1, + "b": 0, + "c": 0, + "d": 1, + "e": 0, + "f": 0 + }, + "state": { + "scale": 1, + "x": 0, + "y": 0, + "sx": 0, + "sy": 0 + } + } +} \ No newline at end of file diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index f1a3bdc4..b8738a54 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -16,6 +16,7 @@ import { import { SVG } from '@svgdotjs/svg.js' +import xmind from './src/parse/xmind' // 默认选项配置 const defaultOpt = { @@ -319,7 +320,7 @@ class MindMap { /** * @Author: 王林 * @Date: 2021-08-03 22:58:12 - * @Desc: 动态设置思维导图数据 + * @Desc: 动态设置思维导图数据,纯节点数据 */ setData(data) { this.execCommand('CLEAR_ACTIVE_NODE') @@ -328,6 +329,32 @@ class MindMap { this.reRender() } + /** + * javascript comment + * @Author: 王林25 + * @Date: 2022-09-21 16:39:13 + * @Desc: 动态设置思维导图数据,包括节点数据、布局、主题、视图 + */ + setFullData(data) { + if (data.root) { + this.setData(data.root) + } + if (data.layout) { + this.setLayout(data.layout) + } + if (data.theme) { + if (data.theme.template) { + this.setTheme(data.theme.template) + } + if (data.theme.config) { + this.setThemeConfig(data.theme.config) + } + } + if (data.view) { + this.view.setTransformData(data.view) + } + } + /** * @Author: 王林 * @Date: 2021-07-01 22:06:38 @@ -369,4 +396,6 @@ class MindMap { } } +MindMap.xmind = xmind + export default MindMap \ No newline at end of file diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index 77dc7a62..e06b407d 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.2.6", + "version": "0.2.7", "description": "一个简单的web在线思维导图", "authors": [ { @@ -25,7 +25,8 @@ "canvg": "^3.0.7", "deepmerge": "^1.5.2", "eventemitter3": "^4.0.7", - "jspdf": "^2.5.1" + "jspdf": "^2.5.1", + "jszip": "^3.10.1" }, "keywords": [ "javascript", diff --git a/simple-mind-map/src/Export.js b/simple-mind-map/src/Export.js index 337f6137..f3b25a96 100644 --- a/simple-mind-map/src/Export.js +++ b/simple-mind-map/src/Export.js @@ -26,9 +26,9 @@ class Export { * @Date: 2021-07-02 07:44:06 * @Desc: 导出 */ - async export(type, isDownload = true, name = '思维导图') { + async export(type, isDownload = true, name = '思维导图', ...args) { if (this[type]) { - let result = await this[type](name) + let result = await this[type](name, ...args) if (isDownload && type !== 'pdf') { downloadFile(result, name + '.' + type) } @@ -248,8 +248,22 @@ class Export { * @Date: 2021-08-03 22:19:17 * @Desc: 导出为json */ - json () { - let data = this.mindMap.command.getCopyData() + json (name, withConfig = true) { + let nodeData = this.mindMap.command.getCopyData() + let data = {} + if (withConfig) { + data = { + layout: this.mindMap.getLayout(), + root: nodeData, + theme: { + template: this.mindMap.getTheme(), + config: this.mindMap.getCustomThemeConfig() + }, + view: this.mindMap.view.getTransformData() + } + } else { + data = nodeData + } let str = JSON.stringify(data) let blob = new Blob([str]) return URL.createObjectURL(blob) @@ -260,8 +274,8 @@ class Export { * @Date: 2021-08-03 22:24:24 * @Desc: 专有文件,其实就是json文件 */ - smm () { - return this.json(); + smm (name, withConfig) { + return this.json(name, withConfig); } } diff --git a/simple-mind-map/src/parse/xmind.js b/simple-mind-map/src/parse/xmind.js new file mode 100644 index 00000000..5bbcc482 --- /dev/null +++ b/simple-mind-map/src/parse/xmind.js @@ -0,0 +1,76 @@ +import JSZip from "jszip"; + +/** + * javascript comment + * @Author: 王林25 + * @Date: 2022-09-21 14:07:47 + * @Desc: 解析.xmind文件 + */ +const parseXmindFile = (file) => { + return new Promise((resolve, reject) => { + JSZip.loadAsync(file).then( + async (zip) => { + try { + let content = await zip.files["content.json"].async("string"); + let res = transformXmind(content); + resolve(res); + } catch (error) { + reject(error); + } + }, + (e) => { + reject(e); + } + ); + }); +}; + +/** + * javascript comment + * @Author: 王林25 + * @Date: 2022-09-21 18:57:25 + * @Desc: 转换xmind数据 + */ +const transformXmind = (content) => { + let data = JSON.parse(content)[0]; + let nodeTree = data.rootTopic; + let newTree = {}; + let walk = (node, newNode) => { + newNode.data = { + // 节点内容 + text: node.title, + }; + // 节点备注 + if (node.notes) { + newNode.data.note = (node.notes.realHTML || node.notes.plain).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; + } + // 子节点 + newNode.children = []; + if ( + node.children && + node.children.attached && + node.children.attached.length > 0 + ) { + node.children.attached.forEach((item) => { + let newChild = {}; + newNode.children.push(newChild); + walk(item, newChild); + }); + } + }; + walk(nodeTree, newTree); + return newTree; +}; + +export default { + parseXmindFile, + transformXmind, +}; diff --git a/web/src/pages/Edit/components/BaseStyle.vue b/web/src/pages/Edit/components/BaseStyle.vue index d029408b..799fca9e 100644 --- a/web/src/pages/Edit/components/BaseStyle.vue +++ b/web/src/pages/Edit/components/BaseStyle.vue @@ -101,10 +101,10 @@ 颜色 - +
导出文件名称 + 是否包含主题、结构等配置数据
- - 专有文件(.smm) - json文件(.json) - 图片文件(.png) - svg文件(.svg) - pdf文件(.pdf) + + 专有文件(.smm) + json文件(.json) + 图片文件(.png) + svg文件(.svg) + pdf文件(.pdf) -
tips:.smm文件可用于导入
+
tips:.smm和.json文件可用于导入
取 消 @@ -38,7 +39,8 @@ export default { return { dialogVisible: false, exportType: "smm", - fileName: '思维导图' + fileName: '思维导图', + widthConfig: true }; }, created() { @@ -62,7 +64,7 @@ export default { * @Desc: 确定 */ confirm() { - this.$bus.$emit("export", this.exportType, true, this.fileName); + this.$bus.$emit("export", this.exportType, true, this.fileName, this.widthConfig); this.$notify.info({ title: '消息', message: '如果没有触发下载,请检查是否被浏览器拦截了' diff --git a/web/src/pages/Edit/components/Import.vue b/web/src/pages/Edit/components/Import.vue index 773d3d90..60cdd657 100644 --- a/web/src/pages/Edit/components/Import.vue +++ b/web/src/pages/Edit/components/Import.vue @@ -1,8 +1,8 @@