Compare commits

...

97 Commits

Author SHA1 Message Date
街角小林
a36b9085bf 打包0.9.10 2024-04-02 18:14:01 +08:00
街角小林
42c934cb6d Doc: update 2024-04-02 13:57:57 +08:00
街角小林
728b1e1503 Doc: update 2024-04-02 11:53:30 +08:00
街角小林
1949d86abb Demo:支持拖拽文件到页面进行导入 2024-04-02 11:51:32 +08:00
街角小林
a7c68816f9 Doc: update 2024-04-02 09:01:45 +08:00
街角小林
ac3ad1681f Fix:修复节点文本存在svg不支持的实体字符时小地图无法渲染的问题 2024-04-02 09:00:09 +08:00
街角小林
92894d0341 Demo: update 2024-04-01 20:08:15 +08:00
街角小林
58dc232ebf Doc: update 2024-04-01 20:04:06 +08:00
街角小林
5abf09b560 Doc: update 2024-04-01 17:49:57 +08:00
街角小林
6694dffa06 update 2024-04-01 14:51:49 +08:00
街角小林
3673c6aafe Demo:支持添加附件内容(在线Demo不开放) 2024-04-01 13:53:09 +08:00
街角小林
979299f2e2 Feat:节点内容支持设置附件 2024-04-01 13:51:05 +08:00
街角小林
c0f69e038a Feat:节点内容支持设置附件 2024-04-01 13:50:20 +08:00
街角小林
80727b759d Fix:修复搜索时全部替换操作报错的问题 2024-03-29 18:02:02 +08:00
街角小林
57fe315345 打包0.9.9-fix.2 2024-03-29 16:16:08 +08:00
街角小林
231dbc00bc Demo:修复侧边栏大纲点击全屏编辑时打开的是源码编辑模式的问题 2024-03-29 16:02:09 +08:00
街角小林
02957e1fcf Fix:修复开启彩虹线条时切换结构会报错的问题 2024-03-29 15:51:32 +08:00
街角小林
38576a4860 Fix:修复插入父节点操作时原节点样式为更新的问题 2024-03-29 15:44:02 +08:00
街角小林
9b26ca9290 打包0.9.9-fix.1版本,修复搜索插件无法搜索的问题 2024-03-29 14:46:19 +08:00
街角小林
d36ff55335 打包0.9.9 2024-03-28 19:47:37 +08:00
街角小林
1ca6a34edf Doc: update 2024-03-28 19:31:49 +08:00
街角小林
c6f8f38648 Demo:导出png、pdf、svg支持设置底部自定义文字 2024-03-28 19:06:10 +08:00
街角小林
614aa1ec30 Feat:addContentToHeader方法支持返回空数据 2024-03-28 18:55:40 +08:00
街角小林
f0c08c7953 Feat:新增导出图片时添加自定义内容的实例化选项 2024-03-28 13:48:08 +08:00
街角小林
b2d5a626c7 Doc: update 2024-03-27 19:16:00 +08:00
街角小林
102cbeb821 Demo:新增源码编辑模式 2024-03-27 19:13:59 +08:00
街角小林
bff683cb5c Doc: update 2024-03-26 14:00:48 +08:00
街角小林
bf9cb99441 Fix:修复第一次创建关联线时,箭头颜色不正确的问题 2024-03-26 13:40:33 +08:00
街角小林
e966e5d57c Demo:update 2024-03-26 12:11:47 +08:00
街角小林
740c898bb1 Feat:实例化及setData方法支持传入空的data,画布空白显示 2024-03-26 12:08:20 +08:00
街角小林
3243e366b0 Doc: update 2024-03-26 10:43:26 +08:00
街角小林
7c6b67e8fb Feat:协同编辑时的人员头像增加鼠标事件 2024-03-25 18:08:59 +08:00
街角小林
3b4195acc5 Feat:节点实例新增getAncestorNodes方法用于获取祖先节点列表 2024-03-25 15:38:19 +08:00
街角小林
8b68b1fc48 Feat:节点中的图标添加鼠标移入和移出事件 2024-03-25 15:13:28 +08:00
街角小林
4614a87bdd Demo:支持配置彩虹线条 2024-03-25 15:03:53 +08:00
街角小林
c87c169dab Feat:新增彩虹线条插件 2024-03-25 15:03:38 +08:00
街角小林
bc6bf2f8f9 Feat:思维导图实例增加增量更新画布数据的方法 2024-03-22 09:30:37 +08:00
街角小林
2e5d17de16 update 2024-03-22 09:03:00 +08:00
街角小林
a12e72117e Doc: update 2024-03-22 08:58:15 +08:00
街角小林
b3d16a60b8 Doc: update 2024-03-21 09:29:08 +08:00
街角小林
2f91ea7199 Feat:增加beforeShortcutRun实例化选项用于拦截快捷键操作 2024-03-13 15:31:50 +08:00
街角小林
1085473463 Feat:支持insert键插入下级节点 2024-03-13 15:17:35 +08:00
街角小林
4c7dafe94e Doc: update 2024-03-13 15:10:17 +08:00
街角小林
f52e39eb16 打包demo 2024-03-12 18:01:10 +08:00
街角小林
27d3e977db 更新项目依赖 2024-03-12 18:00:58 +08:00
街角小林
3e59fa6ade 打包0.9.8 2024-03-08 14:01:11 +08:00
街角小林
c80916d0f2 Doc: update 2024-03-08 11:46:35 +08:00
街角小林
7bd73ba157 Fix:修复自由拖拽时,前进后退操作对节点位置不生效的问题 2024-03-08 11:44:29 +08:00
街角小林
6055a04ec5 代码优化 2024-03-08 11:15:02 +08:00
街角小林
a114631a66 Doc: update 2024-03-08 10:09:48 +08:00
街角小林
e22f67a831 Feat:修改协同编辑节点操作的更新逻辑 2024-03-08 10:06:15 +08:00
街角小林
792811f39e Doc: update 2024-03-07 11:36:05 +08:00
街角小林
ea29dad6fd Demo:新增txt文件的导出 2024-03-07 11:28:15 +08:00
街角小林
660ec00ca7 Feat:新增支持txt文件的导出 2024-03-07 11:27:52 +08:00
街角小林
2baa500c17 Fix:优化markdown的导出,修复概要丢失的问题 2024-03-07 11:27:09 +08:00
街角小林
70b6b0052f Demo:修复导入弹窗选择了一个文件后再把它删除实际上并没有删掉的问题 2024-03-07 10:40:21 +08:00
街角小林
798591f6f9 update 2024-03-07 10:36:39 +08:00
街角小林
f0b73d635e update 2024-03-07 10:34:15 +08:00
街角小林
0b049c5294 Doc: update 2024-03-07 09:40:50 +08:00
街角小林
4bf43ff338 Feat:概要节点增加uid字段 2024-03-06 16:43:45 +08:00
街角小林
58a3faae74 Fix:修复协同编辑插件:当选中一个节点时,再将该节点收起,该节点激活状态已消失,但其他客户端该节点的选中状态依旧存在的问题 2024-03-06 11:09:38 +08:00
街角小林
f3fe2dbc7b Feat:增加协同编辑节点操作同步前的生命周期函数配置信息 2024-03-06 10:05:00 +08:00
街角小林
a72d2e6748 Feat:增加协同编辑时同一节点不能多人选中的配置选项 2024-03-06 09:22:17 +08:00
街角小林
8c07209cea Fix:修复节点数据中根节点设置了expand:false时只渲染根节点的问题 2024-03-05 10:00:42 +08:00
街角小林
280afa6a73 Doc: update 2024-03-05 09:03:35 +08:00
街角小林
fd85085cb7 Demo: update 2024-02-28 14:39:55 +08:00
街角小林
95d7a3ac41 Demo: update 2024-02-28 14:13:41 +08:00
街角小林
a295d257d7 Demo:支持扫描电脑本地文件夹 2024-02-28 14:03:00 +08:00
街角小林
460d4ea558 Fix:修复删除正在编辑中的节点时实际上删除的是相邻节点的问题 2024-02-27 16:42:07 +08:00
街角小林
8b90557f70 Fix:修复某些情况下搜索时数据改变,搜索结果没有更新的问题 2024-02-27 10:15:09 +08:00
街角小林
c0fea992a9 update 2024-02-27 09:33:59 +08:00
街角小林
8bdb59c3ea 打包demo 2024-02-27 09:32:36 +08:00
街角小林
c4a846a195 Fix:修复某些情况下搜索时数据改变,搜索结果没有更新的问题 2024-02-27 09:17:00 +08:00
街角小林
7e3a1e405e '打包demo' 2024-02-26 18:25:57 +08:00
街角小林
952472a977 Feat:新增搜索所有节点(包含被收起的节点)的配置;搜索默认改为搜索所有节点; 2024-02-26 18:19:47 +08:00
街角小林
403aae4b3d Feat:1.节点实例新增高亮和取消高亮的方法;2.调整只读模式搜索高亮节点的方式;Fix:修复只读模式搜索高亮节点时收起节点高亮框未消失的问题; 2024-02-26 17:32:07 +08:00
街角小林
7999b5c260 Doc: update 2024-02-26 16:52:38 +08:00
街角小林
cdc5c7aa81 Demo:修改主题和暗色的关联逻辑 2024-02-26 16:50:00 +08:00
街角小林
9a8cd1dd24 Fix:修复导入某些旧版xmind文件时报错的问题 2024-02-23 15:06:07 +08:00
街角小林
c308cc7d44 update README 2024-02-22 16:03:33 +08:00
街角小林
1c0fe5ac8d 打包Demo 2024-02-22 10:51:35 +08:00
街角小林
44413b00fd Demo:修复打开标签弹窗、备注弹窗后点击遮罩关闭弹窗后快捷键会生效的问题 2024-02-22 10:39:40 +08:00
街角小林
8487e148ea Feat:INSERT_NODE、INSERT_MULIT_NODE、INSERT_CHILD_NODE、INSERT_MULIT_CHILD_NODE命令不会覆盖指定新插入节点数据的uid 2024-02-22 10:18:00 +08:00
街角小林
3effff95fa Fix:修复当画布大小改变后,限制思维导图在画布内和滚动条位置计算功能不正确的问题 2024-02-22 10:05:51 +08:00
街角小林
dd52873106 Doc: update 2024-02-21 11:48:22 +08:00
街角小林
a2cd6e0864 Doc: update 2024-02-21 09:55:37 +08:00
街角小林
4a5980f993 打包0.9.7 2024-02-02 17:08:03 +08:00
街角小林
fa8ad5c0d0 Doc: update 2024-02-02 17:02:36 +08:00
街角小林
a37fe66e60 Fix:修复激活概要节点,右侧设置文本样式,概要节点会失去焦点的问题 2024-01-31 18:10:22 +08:00
街角小林
af622793d8 Demo:优化主题设置弹窗 2024-01-31 17:50:28 +08:00
街角小林
679330663a Demo:新增去除节点自定义样式的右键菜单 2024-01-31 17:13:09 +08:00
街角小林
32e027529f Feat:新增两个去除节点自定义样式的命令 2024-01-31 17:12:38 +08:00
wanglin2
5be2f561e7 打包demo 2024-01-29 22:14:24 +08:00
街角小林
3da8070820 Fix:修复二级以下节点拖拽到根节点变成二级节点时样式没有更新的问题;修复上移一个层级命令移动节点时样式没有更新的问题 2024-01-26 13:51:44 +08:00
街角小林
12c89e6d37 打包demo 2024-01-26 12:00:32 +08:00
街角小林
fdb292d9b1 '打包' 2024-01-26 11:52:24 +08:00
街角小林
1083138d8c Doc: update 2024-01-26 11:41:34 +08:00
173 changed files with 10309 additions and 4843 deletions

View File

@@ -11,19 +11,17 @@
本项目包含两部分:
1.一个 js 思维导图库,不依赖任何框架,可以使用它来快速完成 Web 思维导图产品的开发。
1.一个 js 思维导图库,不依赖任何框架,可以使用它来快速完成 Web 思维导图产品的开发。
开发文档:[https://wanglin2.github.io/mind-map/#/doc/zh/](https://wanglin2.github.io/mind-map/#/doc/zh/)。
2.一个 Web 思维导图基于思维导图库、Vue2.x、ElementUI 开发,可以操作电脑本地文件,所以你可以直接把它当做一个在线版思维导图应用使用,如果觉得 github 的响应速度慢,你也可以部署到你的服务器上
2.一个 Web 思维导图基于思维导图库、Vue2.x、ElementUI 开发,可以操作电脑本地文件,可以当做一个在线版思维导图应用使用,也可以部署和二次开发
在线地址:[https://wanglin2.github.io/mind-map/](https://wanglin2.github.io/mind-map/)。
外也提供了客户端可供下载使用,支持`Windows``Mac``Linux`,下载地址:
外也提供了客户端可供下载使用,支持`Windows``Mac``Linux`,下载地址:
Github[releases](https://github.com/wanglin2/mind-map/releases)。
百度云盘:[地址](https://pan.baidu.com/s/1huasEbKsGNH2Af68dvWiOg?pwd=3bp3)。
Github[releases](https://github.com/wanglin2/mind-map/releases)。百度云盘:[地址](https://pan.baidu.com/s/1huasEbKsGNH2Af68dvWiOg?pwd=3bp3)。
> 客户端版本会落后于在线版本,尝试最新功能请优先使用在线版。
@@ -86,20 +84,22 @@ const mindMap = new MindMap({
# License
[MIT](./LICENSE)
保留`mind-map`版权声明的情况下可随意商用。
[MIT](./LICENSE)。保留`mind-map`版权声明的情况下可随意商用。
# 微信交流群
群聊人数较多,无法通过二维码入群,可以微信添加`wanglinguanfang`拉你入群。
# star
如果喜欢本项目欢迎点个star这对我们很重要。
[![Star History Chart](https://api.star-history.com/svg?repos=wanglin2/mind-map&type=Date)](https://star-history.com/#wanglin2/mind-map&Date)
# 请作者喝杯咖啡
开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡~
> 厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds
> 推荐使用支付宝,微信获取不到头像。转账请备注【思维导图】。
<p>
@@ -272,4 +272,52 @@ const mindMap = new MindMap({
<img src="./web/src/assets/avatar/慕智打印-兰兰.jpg" style="width: 50px;height: 50px;" />
<span>慕智打印-兰兰</span>
</span>
<span>
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
<span>锦冰</span>
</span>
<span>
<img src="./web/src/assets/avatar/旭东.png" style="width: 50px;height: 50px;" />
<span>旭东</span>
</span>
<span>
<img src="./web/src/assets/avatar/俊奇.jpg" style="width: 50px;height: 50px;" />
<span>俊奇</span>
</span>
<span>
<img src="./web/src/assets/avatar/橘半.jpg" style="width: 50px;height: 50px;" />
<span>橘半</span>
</span>
<span>
<img src="./web/src/assets/avatar/pluvet.jpg" style="width: 50px;height: 50px;" />
<span>pluvet</span>
</span>
<span>
<img src="./web/src/assets/avatar/皇登攀.jpg" style="width: 50px;height: 50px;" />
<span>皇登攀</span>
</span>
<span>
<img src="./web/src/assets/avatar/风格.jpg" style="width: 50px;height: 50px;" />
<span>风格</span>
</span>
<span>
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
<span>SR</span>
</span>
<span>
<img src="./web/src/assets/avatar/逆水行舟.jpg" style="width: 50px;height: 50px;" />
<span>逆水行舟</span>
</span>
<span>
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
<span>LiuJL</span>
</span>
<span>
<img src="./web/src/assets/avatar/L.jpg" style="width: 50px;height: 50px;" />
<span>L</span>
</span>
<span>
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
<span>sunniberg</span>
</span>
</p>

View File

@@ -13,3 +13,4 @@ if (fs.existsSync(src)) {
fs.unlinkSync(src)
}
console.warn('请检查手绘风格选项是否开启!!!')

2
dist/css/app.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/img/L.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
dist/img/pluvet.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
dist/img/俊奇.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
dist/img/橘半.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
dist/img/皇登攀.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
dist/img/逆水行舟.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
dist/img/风格.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

2
dist/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

76
dist/js/chunk-276b078e.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/chunk-2d0a4b03.js vendored Normal file
View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0a4b03"],{"0805":function(t,s,_){"use strict";_.r(s);var a=function(){var t=this;t._self._c;return t._m(0)},v=[function(){var t=this,s=t._self._c;return s("div",[s("h1",[t._v("快捷键操作如何传递自定义参数")]),s("p",[t._v("库提供了很多命令,比如插入子节点的"),s("code",[t._v("INSERT_CHILD_NODE")]),t._v("等这些命令大多可以接收一定参数比如在插入节点时我想指定初始文本和节点uid那么可以这样调用")]),s("pre",{staticClass:"hljs"},[s("code",[t._v("mindMap.execCommand("),s("span",{staticClass:"hljs-string"},[t._v("'INSERT_CHILD_NODE'")]),t._v(", "),s("span",{staticClass:"hljs-literal"},[t._v("true")]),t._v(", [], {\n "),s("span",{staticClass:"hljs-attr"},[t._v("text")]),t._v(": "),s("span",{staticClass:"hljs-string"},[t._v("'初始文本'")]),t._v(",\n "),s("span",{staticClass:"hljs-attr"},[t._v("uid")]),t._v(": "),s("span",{staticClass:"hljs-string"},[t._v("'xxx'")]),t._v("\n})\n")])]),s("p",[t._v("但是同时库内部也默认注册了很多快捷键,比如插入下级节点的"),s("code",[t._v("Tab")]),t._v("快捷键,很遗憾,目前快捷键操作无法让你传入自定义的参数,那么该怎么办呢,可以这样处理,首先确定你要给什么快捷键传入参数,比如"),s("code",[t._v("Tab")]),t._v(",那么首先可以调用如下方法删除库默认注册的快捷键:")]),s("pre",{staticClass:"hljs"},[s("code",[s("span",{staticClass:"hljs-keyword"},[t._v("const")]),t._v(" keyName = "),s("span",{staticClass:"hljs-string"},[t._v("'Tab'")]),t._v("\nmindMap.keyCommand.removeShortcut(keyName)\n")])]),s("p",[t._v("然后再重新注册即可:")]),s("pre",{staticClass:"hljs"},[s("code",[t._v("mindMap.keyCommand.addShortcut(keyName, "),s("span",{staticClass:"hljs-function"},[t._v("() =>")]),t._v(" {\n mindMap.execCommand("),s("span",{staticClass:"hljs-string"},[t._v("'INSERT_CHILD_NODE'")]),t._v(", "),s("span",{staticClass:"hljs-literal"},[t._v("true")]),t._v(", [], {\n "),s("span",{staticClass:"hljs-attr"},[t._v("text")]),t._v(": "),s("span",{staticClass:"hljs-string"},[t._v("'初始文本'")]),t._v(",\n "),s("span",{staticClass:"hljs-attr"},[t._v("uid")]),t._v(": "),s("span",{staticClass:"hljs-string"},[t._v("'xxx'")]),t._v("\n })\n})\n")])]),s("p",[t._v("库内部默认注册的快捷键对应的命令一览:")]),s("table",[s("thead",[s("tr",[s("th",[t._v("快捷键")]),s("th",[t._v("命令")])])]),s("tbody",[s("tr",[s("td",[t._v("Control+z")]),s("td",[t._v("BACK")])]),s("tr",[s("td",[t._v("Control+y")]),s("td",[t._v("FORWARD")])]),s("tr",[s("td",[t._v("Tab")]),s("td",[t._v("INSERT_CHILD_NODE")])]),s("tr",[s("td",[t._v("Insert")]),s("td",[t._v("INSERT_CHILD_NODE")])]),s("tr",[s("td",[t._v("Enter")]),s("td",[t._v("INSERT_NODE")])]),s("tr",[s("td",[t._v("Shift+Tab")]),s("td",[t._v("INSERT_PARENT_NODE")])]),s("tr",[s("td",[t._v("Control+g")]),s("td",[t._v("ADD_GENERALIZATION")])]),s("tr",[s("td",[t._v("Del或Backspace")]),s("td",[t._v("REMOVE_NODE")])]),s("tr",[s("td",[t._v("Shift+Backspace")]),s("td",[t._v("REMOVE_CURRENT_NODE")])]),s("tr",[s("td",[t._v("Control+a")]),s("td",[t._v("SELECT_ALL")])]),s("tr",[s("td",[t._v("Control+l")]),s("td",[t._v("RESET_LAYOUT")])]),s("tr",[s("td",[t._v("Control+Up")]),s("td",[t._v("UP_NODE")])]),s("tr",[s("td",[t._v("Control+Down")]),s("td",[t._v("DOWN_NODE")])])])])])}],n={},l=n,r=_("2877"),d=Object(r["a"])(l,a,v,!1,null,null,null);s["default"]=d.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/js/chunk-2d0b1be7.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/js/chunk-2d0c213a.js vendored Normal file
View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0c213a"],{4987:function(s,n,a){"use strict";a.r(n);var i=function(){var s=this;s._self._c;return s._m(0)},t=[function(){var s=this,n=s._self._c;return n("div",[n("h1",[s._v("RainbowLines插件")]),n("blockquote",[n("p",[s._v("v0.9.9+")])]),n("p",[s._v("该插件用于实现彩虹线条。")]),n("p",[s._v("开启彩虹线条及自定义颜色可以通过实例化选项"),n("code",[s._v("rainbowLinesConfig")]),s._v("设置。")]),n("p",[s._v("默认的颜色列表如下:")]),n("pre",{staticClass:"hljs"},[n("code",[s._v("[\n 'rgb(255, 213, 73)',\n 'rgb(255, 136, 126)',\n 'rgb(107, 225, 141)',\n 'rgb(151, 171, 255)',\n 'rgb(129, 220, 242)',\n 'rgb(255, 163, 125)',\n 'rgb(152, 132, 234)'\n]\n")])]),n("h2",[s._v("注册")]),n("pre",{staticClass:"hljs"},[n("code",[n("span",{staticClass:"hljs-keyword"},[s._v("import")]),s._v(" MindMap "),n("span",{staticClass:"hljs-keyword"},[s._v("from")]),s._v(" "),n("span",{staticClass:"hljs-string"},[s._v("'simple-mind-map'")]),s._v("\n"),n("span",{staticClass:"hljs-keyword"},[s._v("import")]),s._v(" RainbowLines "),n("span",{staticClass:"hljs-keyword"},[s._v("from")]),s._v(" "),n("span",{staticClass:"hljs-string"},[s._v("'simple-mind-map/src/plugins/RainbowLines.js'")]),s._v("\nMindMap.usePlugin(RainbowLines)\n")])]),n("p",[s._v("注册完且实例化"),n("code",[s._v("MindMap")]),s._v("后可通过"),n("code",[s._v("mindMap.rainbowLines")]),s._v("获取到该实例。")]),n("h2",[s._v("方法")]),n("h3",[s._v("updateRainLinesConfig(config = {})")]),n("p",[s._v("如果你在通过实例化选项"),n("code",[s._v("rainbowLinesConfig")]),s._v("设置了彩虹线条后想修改,那么可以使用该方法,参数"),n("code",[s._v("config")]),s._v("同"),n("code",[s._v("rainbowLinesConfig")]),s._v("。")]),n("pre",{staticClass:"hljs"},[n("code",[s._v("{\n "),n("span",{staticClass:"hljs-attr"},[s._v("open")]),s._v(": "),n("span",{staticClass:"hljs-literal"},[s._v("false")]),s._v(","),n("span",{staticClass:"hljs-comment"},[s._v("// 是否开启彩虹线条")]),s._v("\n "),n("span",{staticClass:"hljs-attr"},[s._v("colorsList")]),s._v(": []"),n("span",{staticClass:"hljs-comment"},[s._v("// 自定义彩虹线条的颜色列表,如果不设置,会使用默认颜色列表")]),s._v("\n}\n")])]),n("h3",[s._v("getColorsList()")]),n("p",[s._v("获取当前使用的彩虹线条颜色列表。")]),n("h3",[s._v("getNodeColor(node)")]),n("p",[s._v("获取指定的节点实例对应的彩虹线条颜色。")]),n("h3",[s._v("getSecondLayerAncestor(node)")]),n("p",[s._v("获取一个节点实例的第二层级的祖先节点实例。")])])}],v={},_=v,o=a("2877"),e=Object(o["a"])(_,i,t,!1,null,null,null);n["default"]=e.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/js/chunk-2d0dd7d2.js vendored Normal file
View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0dd7d2"],{8235:function(n,s,t){"use strict";t.r(s);var i=function(){var n=this;n._self._c;return n._m(0)},e=[function(){var n=this,s=n._self._c;return s("div",[s("h1",[n._v("RainbowLines plugin")]),s("blockquote",[s("p",[n._v("v0.9.9+")])]),s("p",[n._v("This plugin is used to implement rainbow lines.")]),s("p",[n._v("Enabling rainbow lines and custom colors can be set through the instantiation option 'rainbowLinesConfig'.")]),s("p",[n._v("The default color list is as follows:")]),s("pre",{staticClass:"hljs"},[s("code",[n._v("[\n 'rgb(255, 213, 73)',\n 'rgb(255, 136, 126)',\n 'rgb(107, 225, 141)',\n 'rgb(151, 171, 255)',\n 'rgb(129, 220, 242)',\n 'rgb(255, 163, 125)',\n 'rgb(152, 132, 234)'\n]\n")])]),s("h2",[n._v("Register")]),s("pre",{staticClass:"hljs"},[s("code",[s("span",{staticClass:"hljs-keyword"},[n._v("import")]),n._v(" MindMap "),s("span",{staticClass:"hljs-keyword"},[n._v("from")]),n._v(" "),s("span",{staticClass:"hljs-string"},[n._v("'simple-mind-map'")]),n._v("\n"),s("span",{staticClass:"hljs-keyword"},[n._v("import")]),n._v(" RainbowLines "),s("span",{staticClass:"hljs-keyword"},[n._v("from")]),n._v(" "),s("span",{staticClass:"hljs-string"},[n._v("'simple-mind-map/src/plugins/RainbowLines.js'")]),n._v("\nMindMap.usePlugin(RainbowLines)\n")])]),s("p",[n._v("After registration and instantiation of "),s("code",[n._v("MindMap")]),n._v(", the instance can be obtained through "),s("code",[n._v("mindMap.rainbowLines")]),n._v(".")]),s("h2",[n._v("Method")]),s("h3",[n._v("updateRainLinesConfig(config = {})")]),s("p",[n._v("If you want to modify the rainbow lines after setting them through the instantiation option 'rainbowLinesConfig', you can use this method, option "),s("code",[n._v("config")]),n._v(" is same with "),s("code",[n._v("rainbowLinesConfig")]),n._v("。")]),s("pre",{staticClass:"hljs"},[s("code",[n._v("{\n "),s("span",{staticClass:"hljs-attr"},[n._v("open")]),n._v(": "),s("span",{staticClass:"hljs-literal"},[n._v("false")]),n._v(","),s("span",{staticClass:"hljs-comment"},[n._v("// Is turn on rainbow lines")]),n._v("\n "),s("span",{staticClass:"hljs-attr"},[n._v("colorsList")]),n._v(": []"),s("span",{staticClass:"hljs-comment"},[n._v("// Customize the color list for rainbow lines. If not set, the default color list will be used")]),n._v("\n}\n")])]),s("h3",[n._v("getColorsList()")]),s("p",[n._v("Get a list of currently used rainbow line colors.")]),s("h3",[n._v("getNodeColor(node)")]),s("p",[n._v("Retrieve the rainbow line color corresponding to the specified node instance.")]),s("h3",[n._v("getSecondLayerAncestor(node)")]),s("p",[n._v("Retrieve the second level ancestor node instance of a node instance.")])])}],o={},a=o,l=t("2877"),r=Object(l["a"])(a,i,e,!1,null,null,null);s["default"]=r.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0f0784"],{"9d03":function(e,t,n){"use strict";n.r(t);var i=function(){var e=this;e._self._c;return e._m(0)},d=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("TextEdit instance")]),t("p",[e._v("Node text editing instance. It can be obtained through "),t("code",[e._v("mindMap.renderer.textEdit")]),e._v(".")]),t("h2",[e._v("Methods")]),t("h3",[e._v("isShowTextEdit()")]),t("p",[e._v("Get whether the current text editing box is in a display state, that is, whether it is in a text editing state.")]),t("h3",[e._v("hideEditTextBox()")]),t("p",[e._v("Hiding the text editing box will set the content of the current text editing box as node text.")]),t("h3",[e._v("registerTmpShortcut()")]),t("p",[e._v("Register temporary shortcut keys, which means editing can be completed through the Enter and Tab keys.")]),t("h3",[e._v("show({ node})")]),t("ul",[t("li",[t("code",[e._v("node")]),e._v("Node instance to enter for editing")])]),t("p",[e._v("Manually enable node editing. By default, it will enter node editing when double clicking or pressing F2 on the node.")])])}],o={},h=o,r=n("2877"),s=Object(r["a"])(h,i,d,!1,null,null,null);t["default"]=s.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0f0784"],{"9d03":function(e,t,n){"use strict";n.r(t);var i=function(){var e=this;e._self._c;return e._m(0)},d=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("TextEdit instance")]),t("p",[e._v("Node text editing instance. It can be obtained through "),t("code",[e._v("mindMap.renderer.textEdit")]),e._v(".")]),t("h2",[e._v("Methods")]),t("h3",[e._v("isShowTextEdit()")]),t("p",[e._v("Get whether the current text editing box is in a display state, that is, whether it is in a text editing state.")]),t("h3",[e._v("hideEditTextBox()")]),t("p",[e._v("Hiding the text editing box will set the content of the current text editing box as node text.")]),t("h3",[e._v("registerTmpShortcut()")]),t("p",[e._v("Register temporary shortcut keys, which means editing can be completed through the Enter and Tab keys.")]),t("h3",[e._v("show({ node})")]),t("ul",[t("li",[t("code",[e._v("node")]),e._v("Node instance to enter for editing")])]),t("p",[e._v("Manually enable node editing. By default, it will enter node editing when double clicking or pressing F2 on the node.")]),t("h3",[e._v("getCurrentEditNode()")]),t("blockquote",[t("p",[e._v("v0.9.8+")])]),t("p",[e._v("Get the node instance currently being edited.")])])}],o={},r=o,h=n("2877"),s=Object(h["a"])(r,i,d,!1,null,null,null);t["default"]=s.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d216037"],{c13f:function(s,t,n){"use strict";n.r(t);var a=function(){var s=this;s._self._c;return s._m(0)},e=[function(){var s=this,t=s._self._c;return t("div",[t("h1",[s._v("开启节点自由拖拽")]),t("blockquote",[t("p",[s._v("节点自由拖拽的连线可能不会符合你的预期,这个问题基本上不会改,所以自由拖拽功能不建议使用。")])]),t("p",[s._v("节点支持自由拖拽,也就是可以把它拖动到你指定的位置,默认是不开启的,如果需要开启可以在实例化"),t("code",[s._v("simple-mind-map")]),s._v("时传入开启的选项:")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-keyword"},[s._v("new")]),s._v(" MindMap({\n "),t("span",{staticClass:"hljs-comment"},[s._v("// ...")]),s._v("\n "),t("span",{staticClass:"hljs-attr"},[s._v("enableFreeDrag")]),s._v(": "),t("span",{staticClass:"hljs-literal"},[s._v("true")]),s._v("\n})\n")])]),t("p",[s._v("也可以动态切换是否开启:")]),t("pre",{staticClass:"hljs"},[t("code",[s._v("mindMap.updateConfig({\n "),t("span",{staticClass:"hljs-attr"},[s._v("enableFreeDrag")]),s._v(": "),t("span",{staticClass:"hljs-literal"},[s._v("true")]),t("span",{staticClass:"hljs-comment"},[s._v("// false")]),s._v("\n})\n")])]),t("p",[s._v("自由拖拽很容器将节点拖的乱七八糟,所以也可以通过命令回到默认的位置:")]),t("pre",{staticClass:"hljs"},[t("code",[s._v("mindMap.execCommand("),t("span",{staticClass:"hljs-string"},[s._v("'RESET_LAYOUT'")]),s._v(")\n")])]),t("p",[s._v("也可以使用快捷键"),t("code",[s._v("Ctrl + L")]),s._v("来恢复。")]),t("h2",[s._v("完整示例")]),t("iframe",{staticStyle:{width:"100%",height:"455px",border:"none"},attrs:{src:"https://wanglin2.github.io/playground/#eNrFVd1uG0UUfpVhEVoH2buuxJVxSksbJKSEolAuUDeqxrtje8rszLIzm8SyVgoF1EKIFASiFdxAVQFCQFWBEE2oeJmsnb4FZ3b2L44veldLa82c853znZnzM1PrchQ52wmxelZf+jGNFJJEJdFFj9MwErFCUxSTYRsJviESrkjQRnKMGRM7m2SIUjSMRYhs8GBXFhuUBxs4MirPkiBmpBOCtBPiyLM8jpDHGVFIyzRyFfGEMSN3XZQ9/Tr7/GD2397sr+P5d5+efnF7fvtJdvfe6Y+/eNwXXCqEfUW3yTsiIBKs64haN7ZWPF6iqLwWEf5WTMjVGI8ACEdpDTGTJAdpqrt3ZgcPZvcfZYc/Zf/uZYePTu/8Ov/m8Wz/29n+09KPEqMRIw0/rRW0ehFNdcRnOZxtzBICiJeWyTW+OLOTRAFW5IrgQzpq5Z4QIhwPap7eUt8QuEoh/LQ4wezjB9nDg2fH90//eJj980n2eK+MOiaQynU8EYk6G3IZAtkl/hURhpgHLXtz7b216zfXL39w7f3rdum/ynrrvLnOGtkps10dgfVQIPwkJFw5I6LWGNHLNydvA0dhCYdWmHIS2yttYwVXgXvGu/55lhZ4VkNkxIrsKi32rNkPT0xdmHrSv7RwpoH+mLIgJlyDb9Q+FtwtZVlkOjn6cn708yLZWcIlpFu1rol7QRGUy0JW2lFO1aYQSjfSu0JSRQUHS5uRobLbyPYhdZCmrQK+WJ95K2lVuvK66V6EoCTn33+VHf5m4jVdfHK0f3L8d7P2BG/ZHFhvmk4GspbetpH+X6dS1fWGmt1eNViJM/RQr/D1XTPDYHrBRhEYPNBjsEOoH9Bt5DMs5apnFUFcJaHwrFxdAGhQa6sqBUjfBW0TWHpSQrAB1hCj9FR/kCglOLrkM+p/mEOawwOQ0+niYHoD2dlnfz6793tz+NioB+JzM8lGadp3DUlBCjEtkjZ6HxjPz4gFD/X5ylXfbVwfbKWaMHOTl4ox71mOa2Z7MQIcIkPHl9KzqmJwGjddJnOHBmrcQxe63VdyHEJRVXkxAUZIda7Ii1Z/Ly9mpHRVG+KBFCxRxhAhXb891C12SkT15jz9mNDRGOCvdbvRbsm8nPfVkjnE8YgCb+k1wkFAObSEEVShO0WBPGfEF8oIiqCrPTiE4s5zYLUtkwH9mjq3pODweOfuvUIBGagmimfB22zGiOPC0olhntOQ6GR1BrHYkSQGJ55VtPiS91rbjpWKZM912e5HXE6kI6Ts+LwzIPQWHNvBjE4S7kvHF6ELnUGUXFIbmqY4TGql/wPpBfrv"}})])}],l={},r=l,i=n("2877"),p=Object(i["a"])(r,a,e,!1,null,null,null);t["default"]=p.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d216037"],{c13f:function(s,t,a){"use strict";a.r(t);var n=function(){var s=this;s._self._c;return s._m(0)},e=[function(){var s=this,t=s._self._c;return t("div",[t("h1",[s._v("开启节点自由拖拽")]),t("blockquote",[t("p",[s._v("节点自由拖拽的连线可能不会符合你的预期,这个问题基本上不会改,所以自由拖拽功能不建议使用。")])]),t("blockquote",[t("p",[s._v("另外不要把节点拖拽和自由拖拽搞混,节点拖拽指拖动节点到其他节点上成为其子节点或兄弟节点,自由拖拽只是可以把节点放置在你拖动到的画布位置,并不能改变节点的层级。")])]),t("blockquote",[t("p",[s._v("最后要注意这两个功能都需要先注册Drag插件。")])]),t("p",[s._v("节点支持自由拖拽,也就是可以把它拖动到你指定的位置,默认是不开启的,如果需要开启可以在实例化"),t("code",[s._v("simple-mind-map")]),s._v("时传入开启的选项:")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-keyword"},[s._v("new")]),s._v(" MindMap({\n "),t("span",{staticClass:"hljs-comment"},[s._v("// ...")]),s._v("\n "),t("span",{staticClass:"hljs-attr"},[s._v("enableFreeDrag")]),s._v(": "),t("span",{staticClass:"hljs-literal"},[s._v("true")]),s._v("\n})\n")])]),t("p",[s._v("也可以动态切换是否开启:")]),t("pre",{staticClass:"hljs"},[t("code",[s._v("mindMap.updateConfig({\n "),t("span",{staticClass:"hljs-attr"},[s._v("enableFreeDrag")]),s._v(": "),t("span",{staticClass:"hljs-literal"},[s._v("true")]),t("span",{staticClass:"hljs-comment"},[s._v("// false")]),s._v("\n})\n")])]),t("p",[s._v("自由拖拽很容器将节点拖的乱七八糟,所以也可以通过命令回到默认的位置:")]),t("pre",{staticClass:"hljs"},[t("code",[s._v("mindMap.execCommand("),t("span",{staticClass:"hljs-string"},[s._v("'RESET_LAYOUT'")]),s._v(")\n")])]),t("p",[s._v("也可以使用快捷键"),t("code",[s._v("Ctrl + L")]),s._v("来恢复。")]),t("h2",[s._v("完整示例")]),t("iframe",{staticStyle:{width:"100%",height:"455px",border:"none"},attrs:{src:"https://wanglin2.github.io/playground/#eNrFVd1uG0UUfpVhEVoH2buuxJVxSksbJKSEolAuUDeqxrtje8rszLIzm8SyVgoF1EKIFASiFdxAVQFCQFWBEE2oeJmsnb4FZ3b2L44veldLa82c853znZnzM1PrchQ52wmxelZf+jGNFJJEJdFFj9MwErFCUxSTYRsJviESrkjQRnKMGRM7m2SIUjSMRYhs8GBXFhuUBxs4MirPkiBmpBOCtBPiyLM8jpDHGVFIyzRyFfGEMSN3XZQ9/Tr7/GD2397sr+P5d5+efnF7fvtJdvfe6Y+/eNwXXCqEfUW3yTsiIBKs64haN7ZWPF6iqLwWEf5WTMjVGI8ACEdpDTGTJAdpqrt3ZgcPZvcfZYc/Zf/uZYePTu/8Ov/m8Wz/29n+09KPEqMRIw0/rRW0ehFNdcRnOZxtzBICiJeWyTW+OLOTRAFW5IrgQzpq5Z4QIhwPap7eUt8QuEoh/LQ4wezjB9nDg2fH90//eJj980n2eK+MOiaQynU8EYk6G3IZAtkl/hURhpgHLXtz7b216zfXL39w7f3rdum/ynrrvLnOGtkps10dgfVQIPwkJFw5I6LWGNHLNydvA0dhCYdWmHIS2yttYwVXgXvGu/55lhZ4VkNkxIrsKi32rNkPT0xdmHrSv7RwpoH+mLIgJlyDb9Q+FtwtZVlkOjn6cn708yLZWcIlpFu1rol7QRGUy0JW2lFO1aYQSjfSu0JSRQUHS5uRobLbyPYhdZCmrQK+WJ95K2lVuvK66V6EoCTn33+VHf5m4jVdfHK0f3L8d7P2BG/ZHFhvmk4GspbetpH+X6dS1fWGmt1eNViJM/RQr/D1XTPDYHrBRhEYPNBjsEOoH9Bt5DMs5apnFUFcJaHwrFxdAGhQa6sqBUjfBW0TWHpSQrAB1hCj9FR/kCglOLrkM+p/mEOawwOQ0+niYHoD2dlnfz6793tz+NioB+JzM8lGadp3DUlBCjEtkjZ6HxjPz4gFD/X5ylXfbVwfbKWaMHOTl4ox71mOa2Z7MQIcIkPHl9KzqmJwGjddJnOHBmrcQxe63VdyHEJRVXkxAUZIda7Ii1Z/Ly9mpHRVG+KBFCxRxhAhXb891C12SkT15jz9mNDRGOCvdbvRbsm8nPfVkjnE8YgCb+k1wkFAObSEEVShO0WBPGfEF8oIiqCrPTiE4s5zYLUtkwH9mjq3pODweOfuvUIBGagmimfB22zGiOPC0olhntOQ6GR1BrHYkSQGJ55VtPiS91rbjpWKZM912e5HXE6kI6Ts+LwzIPQWHNvBjE4S7kvHF6ELnUGUXFIbmqY4TGql/wPpBfrv"}})])}],l={},r=l,i=a("2877"),p=Object(i["a"])(r,n,e,!1,null,null,null);t["default"]=p.exports}}]);

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d216f87"],{c576:function(e,t,n){"use strict";n.r(t);var _=function(){var e=this;e._self._c;return e._m(0)},v=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("TextEdit 实例")]),t("p",[e._v("节点文本编辑实例。可以通过"),t("code",[e._v("mindMap.renderer.textEdit")]),e._v("获取到。")]),t("h2",[e._v("方法")]),t("h3",[e._v("isShowTextEdit()")]),t("p",[e._v("获取当前文本编辑框是否处于显示状态,也就是是否处在文本编辑状态。")]),t("h3",[e._v("hideEditTextBox()")]),t("p",[e._v("隐藏文本编辑框,会将当前文本编辑框中的内容设置为节点文本。")]),t("h3",[e._v("registerTmpShortcut()")]),t("p",[e._v("注册临时快捷键,也就是可以通过 Enter 键和 Tab 键完成编辑。")]),t("h3",[e._v("show({ node})")]),t("ul",[t("li",[t("code",[e._v("node")]),e._v(":要进入编辑的节点实例")])]),t("p",[e._v("手动开启节点编辑。默认会在节点双击、按 F2 时进入节点编辑。")])])}],i={},r=i,d=n("2877"),o=Object(d["a"])(r,_,v,!1,null,null,null);t["default"]=o.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d216f87"],{c576:function(e,t,v){"use strict";v.r(t);var _=function(){var e=this;e._self._c;return e._m(0)},n=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("TextEdit 实例")]),t("p",[e._v("节点文本编辑实例。可以通过"),t("code",[e._v("mindMap.renderer.textEdit")]),e._v("获取到。")]),t("h2",[e._v("方法")]),t("h3",[e._v("isShowTextEdit()")]),t("p",[e._v("获取当前文本编辑框是否处于显示状态,也就是是否处在文本编辑状态。")]),t("h3",[e._v("hideEditTextBox()")]),t("p",[e._v("隐藏文本编辑框,会将当前文本编辑框中的内容设置为节点文本。")]),t("h3",[e._v("registerTmpShortcut()")]),t("p",[e._v("注册临时快捷键,也就是可以通过 Enter 键和 Tab 键完成编辑。")]),t("h3",[e._v("show({ node})")]),t("ul",[t("li",[t("code",[e._v("node")]),e._v(":要进入编辑的节点实例")])]),t("p",[e._v("手动开启节点编辑。默认会在节点双击、按 F2 时进入节点编辑。")]),t("h3",[e._v("getCurrentEditNode()")]),t("blockquote",[t("p",[e._v("v0.9.8+")])]),t("p",[e._v("获取当前正在编辑中的节点实例。")])])}],i={},o=i,r=v("2877"),d=Object(r["a"])(o,_,n,!1,null,null,null);t["default"]=d.exports}}]);

File diff suppressed because one or more lines are too long

1
dist/js/chunk-4c82605f.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/chunk-5ecd9693.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/chunk-66b27c16.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel="icon" href="dist/logo.ico"><title>思绪思维导图</title><script>// 自定义静态资源的路径
window.externalPublicPath = './dist/'
// 接管应用
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?ce6307fb704926e49985" rel="stylesheet"><link href="dist/css/app.css?ce6307fb704926e49985" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?ac10265d54028b4359f6" rel="stylesheet"><link href="dist/css/app.css?ac10265d54028b4359f6" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
@@ -66,4 +66,4 @@
// 可以通过window.$bus.$on()来监听应用的一些事件
// 实例化页面
window.initApp()
}</script><script src="dist/js/chunk-vendors.js?ce6307fb704926e49985"></script><script src="dist/js/app.js?ce6307fb704926e49985"></script></body></html>
}</script><script src="dist/js/chunk-vendors.js?ac10265d54028b4359f6"></script><script src="dist/js/app.js?ac10265d54028b4359f6"></script></body></html>

View File

@@ -15,6 +15,7 @@ import Search from './src/plugins/Search.js'
import Painter from './src/plugins/Painter.js'
import Scrollbar from './src/plugins/Scrollbar.js'
import Formula from './src/plugins/Formula.js'
import RainbowLines from './src/plugins/RainbowLines.js'
import xmind from './src/parse/xmind.js'
import markdown from './src/parse/markdown.js'
import icons from './src/svg/icons.js'
@@ -28,7 +29,7 @@ MindMap.iconList = icons.nodeIconList
MindMap.constants = constants
MindMap.themes = themes
MindMap.defaultTheme = defaultTheme
MindMap.version = '0.9.6'
MindMap.version = '0.9.10'
MindMap.usePlugin(MiniMap)
.usePlugin(Watermark)
@@ -46,5 +47,6 @@ MindMap.usePlugin(MiniMap)
.usePlugin(Painter)
.usePlugin(Scrollbar)
.usePlugin(Formula)
.usePlugin(RainbowLines)
export default MindMap

View File

@@ -15,7 +15,13 @@ import {
cssContent
} from './src/constants/constant'
import { SVG } from '@svgdotjs/svg.js'
import { simpleDeepClone, getType, getObjectChangedProps } from './src/utils'
import {
simpleDeepClone,
getType,
getObjectChangedProps,
isUndef,
handleGetSvgDataExtraContent
} from './src/utils'
import defaultTheme, {
checkIsNodeSizeIndependenceConfig
} from './src/themes/default'
@@ -31,6 +37,8 @@ class MindMap {
constructor(opt = {}) {
// 合并选项
this.opt = this.handleOpt(merge(defaultOpt, opt))
// 预处理节点数据
this.opt.data = this.handleData(this.opt.data)
// 容器元素
this.el = this.opt.el
@@ -39,6 +47,10 @@ class MindMap {
// 获取容器尺寸位置信息
this.getElRectInfo()
// 画布初始大小
this.initWidth = this.width
this.initHeight = this.height
// 添加css
this.cssEl = null
this.addCss()
@@ -88,14 +100,12 @@ class MindMap {
// 初始渲染
this.render(this.opt.fit ? () => this.view.fit() : () => {})
setTimeout(() => {
this.command.addHistory()
if (this.opt.data) this.command.addHistory()
}, 0)
}
// 配置参数处理
handleOpt(opt) {
// 深拷贝一份节点数据
opt.data = simpleDeepClone(opt.data || {})
// 检查布局配置
if (!layoutValueList.includes(opt.layout)) {
opt.layout = CONSTANTS.LAYOUT.LOGICAL_STRUCTURE
@@ -105,6 +115,17 @@ class MindMap {
return opt
}
// 预处理节点数据
handleData(data) {
if (isUndef(data) || Object.keys(data).length <= 0) return null
data = simpleDeepClone(data || {})
// 根节点不能收起
if (data.data && !data.data.expand) {
data.data.expand = true
}
return data
}
// 创建容器元素
initContainer() {
const { associativeLineIsAlwaysAboveNode } = this.opt
@@ -302,9 +323,17 @@ class MindMap {
this.command.exec(...args)
}
// 更新画布数据,如果新的数据是在当前画布节点数据基础上增删改查后形成的,那么可以使用该方法来更新画布数据
updateData(data) {
this.renderer.setData(data)
this.render()
this.command.addHistory()
}
// 动态设置思维导图数据,纯节点数据
setData(data) {
data = simpleDeepClone(data || {})
data = this.handleData(data)
this.opt.data = data
this.execCommand('CLEAR_ACTIVE_NODE')
this.command.clearHistory()
this.command.addHistory()
@@ -386,7 +415,18 @@ class MindMap {
}
// 获取svg数据
getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false } = {}) {
getSvgData({
paddingX = 0,
paddingY = 0,
ignoreWatermark = false,
addContentToHeader,
addContentToFooter
} = {}) {
const { cssTextList, header, headerHeight, footer, footerHeight } =
handleGetSvgDataExtraContent({
addContentToHeader,
addContentToFooter
})
const svg = this.svg
const draw = this.draw
// 保存原始信息
@@ -399,8 +439,9 @@ class MindMap {
// 获取变换后的位置尺寸信息其实是getBoundingClientRect方法的包装方法
const rect = draw.rbox()
// 内边距
const fixHeight = 0
rect.width += paddingX * 2
rect.height += paddingY * 2
rect.height += paddingY * 2 + fixHeight + headerHeight + footerHeight
draw.translate(paddingX, paddingY)
// 将svg设置为实际内容的宽高
svg.size(rect.width, rect.height)
@@ -438,7 +479,21 @@ class MindMap {
this.watermark.isInExport = false
}
// 添加必要的样式
clone.add(SVG(`<style>${cssContent}</style>`))
;[cssContent, ...cssTextList].forEach(s => {
clone.add(SVG(`<style>${s}</style>`))
})
// 附加内容
if (header && headerHeight > 0) {
clone.findOne('.smm-container').translate(0, headerHeight)
header.width(rect.width)
header.y(paddingY)
clone.add(header, 0)
}
if (footer && footerHeight > 0) {
footer.width(rect.width)
footer.y(rect.height - paddingY - footerHeight)
clone.add(footer)
}
// 修正defs里定义的元素的id因为clone时defs里的元素的id会继续递增导致和内容中引用的id对不上
const defs = svg.find('defs')
const defs2 = clone.find('defs')

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.9.6",
"version": "0.9.10",
"description": "一个简单的web在线思维导图",
"authors": [
{
@@ -38,6 +38,7 @@
"quill": "^1.3.6",
"tern": "^0.24.3",
"uuid": "^9.0.0",
"ws": "^7.5.9",
"xml-js": "^1.6.11",
"y-webrtc": "^10.2.5",
"yjs": "^13.6.8"

View File

@@ -312,7 +312,9 @@ export const nodeDataNoStylePropList = [
'associativeLineTargets',
'associativeLineTargetControlOffsets',
'associativeLinePoint',
'associativeLineText'
'associativeLineText',
'attachmentUrl',
'attachmentName'
]
// 数据缓存
@@ -346,7 +348,7 @@ export const cssContent = `
display: block;
}
.smm-node.active .smm-hover-node{
.smm-node.active .smm-hover-node, .smm-node-highlight .smm-hover-node{
display: block;
opacity: 1;
stroke-width: 2;

View File

@@ -275,5 +275,48 @@ export const defaultOpt = {
customCreateNodePolygon: null,
// 自定义转换节点连线路径的方法
// 接收svg path字符串返回转换后的svg path字符串
customTransformNodeLinePath: null
customTransformNodeLinePath: null,
// 是否仅搜索当前渲染的节点,被收起的节点不会被搜索到
isOnlySearchCurrentRenderNodes: false,
// 协同编辑时,同一个节点不能同时被多人选中
onlyOneEnableActiveNodeOnCooperate: false,
// 协同编辑时,节点操作即将更新到其他客户端前的生命周期函数
// 函数接收一个对象作为参数:
/*
{
type: createOrUpdate创建节点或更新节点、delete删除节点
data: 1.当type=createOrUpdate时代表被创建或被更新的节点数据即将同步到其他客户端所以你可以修改该数据2.当type=delete时代表被删除的节点数据
}
*/
beforeCooperateUpdate: null,
// 快捷键操作即将执行前的生命周期函数返回true可以阻止操作执行
// 函数接收两个参数key快捷键、activeNodeList当前激活的节点列表
beforeShortcutRun: null,
// 彩虹线条配置需要先注册RainbowLines插件
rainbowLinesConfig: {
open: false, // 是否开启彩虹线条
colorsList: [] // 自定义彩虹线条的颜色列表,如果不设置,会使用默认颜色列表
/*
[
'rgb(255, 213, 73)',
'rgb(255, 136, 126)',
'rgb(107, 225, 141)',
'rgb(151, 171, 255)',
'rgb(129, 220, 242)',
'rgb(255, 163, 125)',
'rgb(152, 132, 234)'
]
*/
},
// 导出png、svg、pdf时在头部和尾部添加自定义内容
// 可传递一个函数这个函数可以返回null代表不添加内容也可以返回如下数据
/*
{
el,// 要追加的自定义DOM节点样式可内联
cssText,// 可选如果样式不想内联可以传递该值一个css字符串
height: 50// 返回的DOM节点的高度必须传递
}
*/
addContentToHeader: null,
addContentToFooter: null
}

View File

@@ -95,6 +95,7 @@ class Command {
this.history.length > 0 ? this.history[this.history.length - 1] : null
const data = this.getCopyData()
// 此次数据和上次一样则不重复添加
if (lastData === data) return
if (lastData && JSON.stringify(lastData) === JSON.stringify(data)) {
return
}
@@ -158,6 +159,7 @@ class Command {
// 获取渲染树数据副本
getCopyData() {
if (!this.mindMap.renderer.renderTree) return null
return copyRenderTree({}, this.mindMap.renderer.renderTree, true)
}

View File

@@ -67,9 +67,10 @@ export default class KeyCommand {
// 按键事件
onKeydown(e) {
const { enableShortcutOnlyWhenMouseInSvg, beforeShortcutRun } = this.mindMap.opt
if (
this.isPause ||
(this.mindMap.opt.enableShortcutOnlyWhenMouseInSvg && !this.isInSvg)
(enableShortcutOnlyWhenMouseInSvg && !this.isInSvg)
) {
return
}
@@ -80,6 +81,10 @@ export default class KeyCommand {
e.stopPropagation()
e.preventDefault()
}
if (typeof beforeShortcutRun === 'function') {
const isStop = beforeShortcutRun(key, [...this.mindMap.renderer.activeNodeList])
if (isStop) return
}
this.shortcutMap[key].forEach(fn => {
fn()
})

View File

@@ -28,7 +28,9 @@ import {
parseAddGeneralizationNodeList,
checkNodeListIsEqual,
createSmmFormatData,
checkSmmFormatData
checkSmmFormatData,
checkIsNodeStyleDataKey,
removeRichTextStyes
} from '../../utils'
import { shapeList } from './node/Shape'
import { lineStyleProps } from '../../themes/default'
@@ -63,7 +65,9 @@ class Render {
this.mindMap = opt.mindMap
this.themeConfig = this.mindMap.themeConfig
// 渲染树,操作过程中修改的都是这里的数据
this.renderTree = merge({}, this.mindMap.opt.data || {})
this.renderTree = this.mindMap.opt.data
? merge({}, this.mindMap.opt.data)
: null
// 是否重新渲染
this.reRender = false
// 是否正在渲染中
@@ -115,7 +119,7 @@ class Render {
// 重新设置思维导图数据
setData(data) {
if (this.mindMap.richText) {
this.renderTree = this.mindMap.richText.handleSetData(data)
this.renderTree = data ? this.mindMap.richText.handleSetData(data) : null
} else {
this.renderTree = data
}
@@ -244,6 +248,9 @@ class Render {
// 设置节点备注
this.setNodeNote = this.setNodeNote.bind(this)
this.mindMap.command.add('SET_NODE_NOTE', this.setNodeNote)
// 设置节点附件
this.setNodeAttachment = this.setNodeAttachment.bind(this)
this.mindMap.command.add('SET_NODE_ATTACHMENT', this.setNodeAttachment)
// 设置节点标签
this.setNodeTag = this.setNodeTag.bind(this)
this.mindMap.command.add('SET_NODE_TAG', this.setNodeTag)
@@ -271,6 +278,15 @@ class Render {
// 定位节点
this.goTargetNode = this.goTargetNode.bind(this)
this.mindMap.command.add('GO_TARGET_NODE', this.goTargetNode)
// 一键去除节点自定义样式
this.removeCustomStyles = this.removeCustomStyles.bind(this)
this.mindMap.command.add('REMOVE_CUSTOM_STYLES', this.removeCustomStyles)
// 一键去除所有节点自定义样式
this.removeAllNodeCustomStyles = this.removeAllNodeCustomStyles.bind(this)
this.mindMap.command.add(
'REMOVE_ALL_NODE_CUSTOM_STYLES',
this.removeAllNodeCustomStyles
)
}
// 注册快捷键
@@ -279,6 +295,10 @@ class Render {
this.mindMap.keyCommand.addShortcut('Tab', () => {
this.mindMap.execCommand('INSERT_CHILD_NODE')
})
// 插入下级节点
this.mindMap.keyCommand.addShortcut('Insert', () => {
this.mindMap.execCommand('INSERT_CHILD_NODE')
})
// 插入同级节点
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.mindMap.execCommand('INSERT_NODE')
@@ -315,7 +335,7 @@ class Render {
})
// 一键整理布局
this.mindMap.keyCommand.addShortcut('Control+l', () => {
this.mindMap.execCommand('RESET_LAYOUT', this.resetLayout)
this.mindMap.execCommand('RESET_LAYOUT')
})
// 上移节点
this.mindMap.keyCommand.addShortcut('Control+Up', () => {
@@ -421,6 +441,12 @@ class Render {
if (this.reRender) {
this.clearActiveNodeList()
}
// 如果没有节点数据
if (!this.renderTree) {
this.isRendering = false
this.mindMap.emit('node_tree_render_end')
return
}
// 计算布局
this.layout.doLayout(root => {
// 删除本次渲染时不再需要的节点
@@ -465,6 +491,7 @@ class Render {
// 给当前被收起来的节点数据添加文本复位标志
resetUnExpandNodeStyle() {
if (!this.renderTree) return
walk(this.renderTree, null, node => {
if (!node.data.expand) {
walk(node, null, node2 => {
@@ -494,6 +521,11 @@ class Render {
// 添加节点到激活列表里
addNodeToActiveList(node) {
if (
this.mindMap.opt.onlyOneEnableActiveNodeOnCooperate &&
node.userList.length > 0
)
return
const index = this.findActiveNodeIndex(node)
if (index === -1) {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, true)
@@ -638,7 +670,7 @@ class Render {
uid: createUid(),
...(appointData || {})
},
children: [...createUidForAppointNodes(appointChildren, true)]
children: [...createUidForAppointNodes(appointChildren)]
}
parent.nodeData.children.splice(index + 1, 0, newNodeData)
})
@@ -674,10 +706,7 @@ class Render {
const parent = node.parent
// 计算插入位置
const index = getNodeDataIndex(node)
const newNodeList = createUidForAppointNodes(
simpleDeepClone(nodeList),
true
)
const newNodeList = createUidForAppointNodes(simpleDeepClone(nodeList))
parent.nodeData.children.splice(index + 1, 0, ...newNodeList)
})
if (focusNewNode) {
@@ -737,7 +766,7 @@ class Render {
...params,
...(appointData || {})
},
children: [...createUidForAppointNodes(appointChildren, true)]
children: [...createUidForAppointNodes(appointChildren)]
}
node.nodeData.children.push(newNode)
// 插入子节点时自动展开子节点
@@ -777,7 +806,7 @@ class Render {
if (!node.nodeData.children) {
node.nodeData.children = []
}
childList = createUidForAppointNodes(childList, true)
childList = createUidForAppointNodes(childList)
node.nodeData.children.push(...childList)
// 插入子节点时自动展开子节点
node.setData({
@@ -832,6 +861,9 @@ class Render {
},
children: [node.nodeData]
}
node.setData({
resetRichText: true
})
const parent = node.parent
// 获取当前节点所在位置
const index = getNodeDataIndex(node)
@@ -905,11 +937,72 @@ class Render {
const index = getNodeIndexInNodeList(node, parent.children)
const parentIndex = getNodeIndexInNodeList(parent, grandpa.children)
// 节点数据
this.checkNodeLayerChange(node, parent)
parent.nodeData.children.splice(index, 1)
grandpa.nodeData.children.splice(parentIndex + 1, 0, node.nodeData)
this.mindMap.render()
}
// 移除节点数据的自定义样式的内部方法
_handleRemoveCustomStyles(nodeData) {
let hasCustomStyles = false
Object.keys(nodeData).forEach(key => {
if (checkIsNodeStyleDataKey(key)) {
hasCustomStyles = true
delete nodeData[key]
}
})
// 如果是富文本,那么还要处理富文本内容
if (hasCustomStyles && this.mindMap.richText) {
nodeData.resetRichText = true
nodeData.text = removeRichTextStyes(nodeData.text)
}
return hasCustomStyles
}
// 一键去除自定义样式
removeCustomStyles(node) {
node = node || this.activeNodeList[0]
if (!node) {
return
}
const hasCustomStyles = this._handleRemoveCustomStyles(node.getData())
if (hasCustomStyles) {
this.reRenderNodeCheckChange(node)
}
}
// 一键去除所有节点自定义样式
removeAllNodeCustomStyles(appointNodes) {
appointNodes = formatDataToArray(appointNodes)
let hasCustomStyles = false
// 指定了节点列表,那么遍历该节点列表
if (appointNodes.length > 0) {
appointNodes.forEach(node => {
const _hasCustomStyles = this._handleRemoveCustomStyles(node.getData())
if (_hasCustomStyles) hasCustomStyles = true
})
} else {
// 否则遍历整棵树
if (!this.renderTree) return
walk(this.renderTree, null, node => {
const _hasCustomStyles = this._handleRemoveCustomStyles(node.data)
if (_hasCustomStyles) hasCustomStyles = true
// 不要忘记概要节点
if (node.data.generalization && node.data.generalization.length > 0) {
node.data.generalization.forEach(generalizationData => {
const _hasCustomStyles =
this._handleRemoveCustomStyles(generalizationData)
if (_hasCustomStyles) hasCustomStyles = true
})
}
})
}
if (hasCustomStyles) {
this.mindMap.reRender()
}
}
// 复制节点
copy() {
this.beingCopyData = this.copyNode()
@@ -1110,11 +1203,12 @@ class Render {
}
// 如果是富文本模式,那么某些层级变化需要更新样式
checkNodeLayerChange(node, toNode) {
checkNodeLayerChange(node, toNode, toNodeIsParent = false) {
if (this.mindMap.richText) {
const toIndex = toNodeIsParent ? toNode.layerIndex + 1 : toNode.layerIndex
let nodeLayerChanged =
(node.layerIndex === 1 && toNode.layerIndex !== 1) ||
(node.layerIndex !== 1 && toNode.layerIndex === 1)
(node.layerIndex === 1 && toIndex !== 1) ||
(node.layerIndex !== 1 && toIndex === 1)
if (nodeLayerChanged) {
node.setData({
resetRichText: true
@@ -1144,7 +1238,15 @@ class Render {
// 如果只选中了一个节点,删除后激活其兄弟节点或者父节点
needActiveNode = this.getNextActiveNode()
for (let i = 0; i < list.length; i++) {
let node = list[i]
const node = list[i]
const currentEditNode = this.textEdit.getCurrentEditNode()
if (
currentEditNode &&
currentEditNode.getData('uid') === node.getData('uid')
) {
// 如果当前节点正在编辑中,那么先完成编辑
this.textEdit.hideEditTextBox()
}
if (isAppointNodes) list.splice(i, 1)
if (node.isGeneralization) {
this.deleteNodeGeneralization(node)
@@ -1292,7 +1394,7 @@ class Render {
return !item.isRoot
})
nodeList.forEach(item => {
this.checkNodeLayerChange(item, toNode)
this.checkNodeLayerChange(item, toNode, true)
this.removeNodeFromActiveList(item)
removeFromParentNodeData(item)
toNode.nodeData.children.push(item.nodeData)
@@ -1376,6 +1478,7 @@ class Render {
// 展开所有
expandAllNode() {
if (!this.renderTree) return
walk(
this.renderTree,
null,
@@ -1394,6 +1497,7 @@ class Render {
// 收起所有
unexpandAllNode() {
if (!this.renderTree) return
walk(
this.renderTree,
null,
@@ -1414,6 +1518,7 @@ class Render {
// 展开到指定层级
expandToLevel(level) {
if (!this.renderTree) return
walk(
this.renderTree,
null,
@@ -1500,6 +1605,14 @@ class Render {
})
}
// 设置节点附件
setNodeAttachment(node, url, name = '') {
this.setNodeDataRender(node, {
attachmentUrl: url,
attachmentName: name
})
}
// 设置节点标签
setNodeTag(node, tag) {
this.setNodeDataRender(node, {
@@ -1536,7 +1649,8 @@ class Render {
...(data || {
text: this.mindMap.opt.defaultGeneralizationText
}),
range: item.range || null
range: item.range || null,
uid: createUid()
}
let generalization = item.node.getData('generalization')
if (generalization) {
@@ -1632,7 +1746,7 @@ class Render {
if (targetNode) {
targetNode.active()
this.moveNodeToCenter(targetNode)
callback()
callback(targetNode)
}
})
}
@@ -1647,6 +1761,11 @@ class Render {
// 设置节点数据,并判断是否渲染
setNodeDataRender(node, data, notRender = false) {
this.mindMap.execCommand('SET_NODE_DATA', node, data)
this.reRenderNodeCheckChange(node, notRender)
}
// 重新节点某个节点,判断节点大小是否发生了改变,是的话触发重绘
reRenderNodeCheckChange(node, notRender) {
let changed = node.reRender()
if (changed) {
if (!notRender) this.mindMap.render()
@@ -1679,6 +1798,10 @@ class Render {
// 展开到指定uid的节点
expandToNodeUid(uid, callback = () => {}) {
if (!this.renderTree) {
callback()
return
}
let parentsList = []
const cache = {}
bfsWalk(this.renderTree, (node, parent) => {

View File

@@ -315,4 +315,12 @@ export default class TextEdit {
this.textEditNode.style.transform = 'translateY(0)'
this.showTextEdit = false
}
// 获取当前正在编辑中的节点实例
getCurrentEditNode() {
if (this.mindMap.richText) {
return this.mindMap.richText.node
}
return this.currentNode
}
}

View File

@@ -75,6 +75,7 @@ class Node {
this._noteData = null
this.noteEl = null
this.noteContentIsShow = false
this._attachmentData = null
this._expandBtn = null
this._lastExpandBtnType = null
this._showExpandBtn = false
@@ -199,10 +200,13 @@ class Node {
this._hyperlinkData = this.createHyperlinkNode()
this._tagData = this.createTagNode()
this._noteData = this.createNoteNode()
this._attachmentData = this.createAttachmentNode()
}
// 计算节点的宽高
getSize() {
this.customLeft = this.getData('customLeft') || undefined
this.customTop = this.getData('customTop') || undefined
this.updateGeneralization()
this.createNodeData()
let { width, height } = this.getNodeRect()
@@ -265,6 +269,11 @@ class Node {
textContentWidth += this._noteData.width
textContentHeight = Math.max(textContentHeight, this._noteData.height)
}
// 附件
if (this._attachmentData) {
textContentWidth += this._attachmentData.width
textContentHeight = Math.max(textContentHeight, this._attachmentData.height)
}
// 文字内容部分的尺寸
this._rectInfo.textContentWidth = textContentWidth
this._rectInfo.textContentHeight = textContentHeight
@@ -397,6 +406,14 @@ class Node {
textContentNested.add(this._noteData.node)
textContentOffsetX += this._noteData.width
}
// 附件
if (this._attachmentData) {
this._attachmentData.node
.x(textContentOffsetX)
.y((this._rectInfo.textContentHeight - this._attachmentData.height) / 2)
textContentNested.add(this._attachmentData.node)
textContentOffsetX += this._attachmentData.width
}
// 文字内容整体
textContentNested.translate(
width / 2 - textContentNested.bbox().width / 2,
@@ -420,6 +437,12 @@ class Node {
this.isMultipleChoice = false
return
}
if (
this.mindMap.opt.onlyOneEnableActiveNodeOnCooperate &&
this.userList.length > 0
) {
return
}
this.active(e)
})
this.group.on('mousedown', e => {
@@ -486,10 +509,14 @@ class Node {
})
// 双击事件
this.group.on('dblclick', e => {
if (this.mindMap.opt.readonly || e.ctrlKey) {
const { readonly, onlyOneEnableActiveNodeOnCooperate } = this.mindMap.opt
if (readonly || e.ctrlKey) {
return
}
e.stopPropagation()
if (onlyOneEnableActiveNodeOnCooperate && this.userList.length > 0) {
return
}
this.mindMap.emit('node_dblclick', this, e)
})
// 右键菜单事件
@@ -705,6 +732,9 @@ class Node {
// 销毁节点,不但会从画布删除,而且原节点直接置空,后续无法再插回画布
destroy() {
if (!this.group) return
if (this.emptyUser) {
this.emptyUser()
}
this.resetWhenDelete()
this.group.remove()
this.removeGeneralization()
@@ -901,6 +931,7 @@ class Node {
childNode.getStyle('lineWidth', true)
const color =
childNode.getSelfInhertStyle('lineColor') ||
this.getRainbowLineColor(childNode) ||
childNode.getStyle('lineColor', true)
const dasharray =
childNode.getSelfInhertStyle('lineDasharray') ||
@@ -917,6 +948,13 @@ class Node {
)
}
// 获取彩虹线条颜色
getRainbowLineColor(node) {
return this.mindMap.rainbowLines
? this.mindMap.rainbowLines.getNodeColor(node)
: ''
}
// 移除连线
removeLine() {
this._lines.forEach(line => {
@@ -1024,6 +1062,17 @@ class Node {
return copyNodeTree({}, this, removeActiveState, removeId)
}
// 获取祖先节点列表
getAncestorNodes() {
const list = []
let parent = this.parent
while (parent) {
list.unshift(parent)
parent = parent.parent
}
return list
}
// 是否存在自定义样式
hasCustomStyle() {
return this.style.hasCustomStyle()
@@ -1052,6 +1101,16 @@ class Node {
height: height * scaleY
}
}
// 高亮节点
highlight() {
if (this.group) this.group.addClass('smm-node-highlight')
}
// 取消高亮节点
closeHighlight() {
if (this.group) this.group.removeClass('smm-node-highlight')
}
}
export default Node

View File

@@ -28,6 +28,11 @@ function setNote(note) {
this.mindMap.execCommand('SET_NODE_NOTE', this, note)
}
// 设置附件
function setAttachment(url, name) {
this.mindMap.execCommand('SET_NODE_ATTACHMENT', this, url, name)
}
// 设置标签
function setTag(tag) {
this.mindMap.execCommand('SET_NODE_TAG', this, tag)
@@ -55,6 +60,7 @@ export default {
setIcon,
setHyperlink,
setNote,
setAttachment,
setTag,
setShape,
setStyle,

View File

@@ -67,6 +67,15 @@ function updateUserListNode() {
} else {
node = this.createTextAvatar(item)
}
node.on('click', (e) => {
this.mindMap.emit('node_cooperate_avatar_click', item, this, node, e)
})
node.on('mouseenter', (e) => {
this.mindMap.emit('node_cooperate_avatar_mouseenter', item, this, node, e)
})
node.on('mouseleave', (e) => {
this.mindMap.emit('node_cooperate_avatar_mouseleave', item, this, node, e)
})
node.x(index * avatarSize).cy(-avatarSize / 2)
this._userListGroup.add(node)
})
@@ -94,11 +103,18 @@ function removeUser(userInfo) {
this.updateUserListNode()
}
// 清空用户
function emptyUser() {
this.userList = []
this.updateUserListNode()
}
export default {
createUserListNode,
updateUserListNode,
createTextAvatar,
createImageAvatar,
addUser,
removeUser
removeUser,
emptyUser
}

View File

@@ -92,7 +92,13 @@ function createIconNode() {
}
node.size(iconSize, iconSize)
node.on('click', e => {
this.mindMap.emit('node_icon_click', this, item, e)
this.mindMap.emit('node_icon_click', this, item, e, node)
})
node.on('mouseenter', e => {
this.mindMap.emit('node_icon_mouseenter', this, item, e, node)
})
node.on('mouseleave', e => {
this.mindMap.emit('node_icon_mouseleave', this, item, e, node)
})
return {
node,
@@ -256,7 +262,7 @@ function createHyperlinkNode() {
e.stopPropagation()
})
if (hyperlinkTitle) {
a.attr('title', hyperlinkTitle)
node.add(SVG(`<title>${hyperlinkTitle}</title>`))
}
// 添加一个透明的层,作为鼠标区域
a.rect(iconSize, iconSize).fill({ color: 'transparent' })
@@ -362,6 +368,36 @@ function createNoteNode() {
}
}
// 创建附件节点
function createAttachmentNode() {
const { attachmentUrl, attachmentName } = this.getData()
if (!attachmentUrl) {
return
}
const iconSize = this.mindMap.themeConfig.iconSize
const node = new SVG().attr('cursor', 'pointer').size(iconSize, iconSize)
if (attachmentName) {
node.add(SVG(`<title>${attachmentName}</title>`))
}
// 透明的层,用来作为鼠标区域
node.add(new Rect().size(iconSize, iconSize).fill({ color: 'transparent' }))
// 备注图标
const iconNode = SVG(iconsSvg.attachment).size(iconSize, iconSize)
this.style.iconNode(iconNode)
node.add(iconNode)
node.on('click', e => {
this.mindMap.emit('node_attachmentClick', this, e, node)
})
node.on('contextmenu', e => {
this.mindMap.emit('node_attachmentContextmenu', this, e, node)
})
return {
node,
width: iconSize,
height: iconSize
}
}
// 获取节点备注显示位置
function getNoteContentPosition() {
const iconSize = this.mindMap.themeConfig.iconSize
@@ -409,6 +445,7 @@ export default {
createHyperlinkNode,
createTagNode,
createNoteNode,
createAttachmentNode,
getNoteContentPosition,
measureCustomNodeContentSize,
isUseCustomNodeContent

View File

@@ -128,6 +128,10 @@ class View {
this.translateXY(mx, my)
}
})
this.mindMap.on('resize', () => {
if (!this.checkNeedMindMapInCanvas()) return
this.transform()
})
}
// 获取当前变换状态数据
@@ -154,7 +158,8 @@ class View {
...viewData.transform
})
this.mindMap.emit('view_data_change', this.getTransformData())
this.mindMap.emit('scale', this.scale)
this.emitEvent('scale')
this.emitEvent('translate')
}
}
@@ -164,6 +169,7 @@ class View {
this.x += x
this.y += y
this.transform()
this.emitEvent('translate')
}
// 平移x方向
@@ -171,12 +177,14 @@ class View {
if (step === 0) return
this.x += step
this.transform()
this.emitEvent('translate')
}
// 平移x方式到
translateXTo(x) {
this.x = x
this.transform()
this.emitEvent('translate')
}
// 平移y方向
@@ -184,12 +192,14 @@ class View {
if (step === 0) return
this.y += step
this.transform()
this.emitEvent('translate')
}
// 平移y方向到
translateYTo(y) {
this.y = y
this.transform()
this.emitEvent('translate')
}
// 应用变换
@@ -207,13 +217,17 @@ class View {
// 恢复
reset() {
let scaleChange = this.scale !== 1
const scaleChange = this.scale !== 1
const translateChange = this.x !== 0 || this.y !== 0
this.scale = 1
this.x = 0
this.y = 0
this.transform()
if (scaleChange) {
this.mindMap.emit('scale', this.scale)
this.emitEvent('scale')
}
if (translateChange) {
this.emitEvent('translate')
}
}
@@ -223,7 +237,7 @@ class View {
const scale = Math.max(this.scale - scaleRatio, 0.1)
this.scaleInCenter(scale, cx, cy)
this.transform()
this.mindMap.emit('scale', this.scale)
this.emitEvent('scale')
}
// 放大
@@ -232,7 +246,7 @@ class View {
const scale = this.scale + scaleRatio
this.scaleInCenter(scale, cx, cy)
this.transform()
this.mindMap.emit('scale', this.scale)
this.emitEvent('scale')
}
// 基于指定中心进行缩放cxcy 可不指定,此时会使用画布中心点
@@ -258,7 +272,7 @@ class View {
this.scale = scale
}
this.transform()
this.mindMap.emit('scale', this.scale)
this.emitEvent('scale')
}
// 适应画布大小
@@ -313,20 +327,31 @@ class View {
this.translateXY(newX, newY)
}
// 将思维导图限制在画布内
limitMindMapInCanvas() {
// 判断是否需要将思维导图限制在画布内
checkNeedMindMapInCanvas() {
const { isLimitMindMapInCanvasWhenHasScrollbar, isLimitMindMapInCanvas } =
this.mindMap.opt
// 如果注册了滚动条插件那么使用isLimitMindMapInCanvasWhenHasScrollbar配置
if (this.mindMap.scrollbar) {
if (!isLimitMindMapInCanvasWhenHasScrollbar) return
return isLimitMindMapInCanvasWhenHasScrollbar
} else {
// 否则使用isLimitMindMapInCanvas配置
if (!isLimitMindMapInCanvas) return
return isLimitMindMapInCanvas
}
}
// 将思维导图限制在画布内
limitMindMapInCanvas() {
if (!this.checkNeedMindMapInCanvas()) return
let { scale, left, top, right, bottom } = this.getPositionLimit()
// 画布宽高改变了,但是思维导图元素变换的中心点依旧是原有位置,所以需要加上中心点变化量
const centerXChange =
((this.mindMap.width - this.mindMap.initWidth) / 2) * scale
const centerYChange =
((this.mindMap.height - this.mindMap.initHeight) / 2) * scale
// 如果缩放值改变了
const scaleRatio = this.scale / scale
left *= scaleRatio
@@ -338,10 +363,10 @@ class View {
const centerX = this.mindMap.width / 2
const centerY = this.mindMap.height / 2
const scaleOffset = this.scale - 1
left -= scaleOffset * centerX
right -= scaleOffset * centerX
top -= scaleOffset * centerY
bottom -= scaleOffset * centerY
left -= scaleOffset * centerX - centerXChange
right -= scaleOffset * centerX - centerXChange
top -= scaleOffset * centerY - centerYChange
bottom -= scaleOffset * centerY - centerYChange
// 判断是否超出边界
if (this.x > left) {
@@ -379,6 +404,16 @@ class View {
bottom
}
}
// 派发事件
emitEvent(type) {
switch (type) {
case 'scale':
this.mindMap.emit('scale', this.scale)
case 'translate':
this.mindMap.emit('translate', this.x, this.y)
}
}
}
export default View

View File

@@ -1,12 +1,7 @@
import { walk } from '../utils'
import { walk, nodeRichTextToTextWithWrap } from '../utils'
let el = null
const getText = str => {
if (!el) {
el = document.createElement('div')
}
el.innerHTML = str
return el.textContent
const getNodeText = data => {
return data.richText ? nodeRichTextToTextWithWrap(data.text) : data.text
}
const getTitleMark = level => {
@@ -24,21 +19,22 @@ export const transformToMarkdown = root => {
root,
null,
(node, parent, isRoot, layerIndex) => {
let level = layerIndex + 1
let text = node.data.richText ? getText(node.data.text) : node.data.text
const level = layerIndex + 1
if (level <= 6) {
content += getTitleMark(level)
} else {
content += getIndentMark(level)
}
content += ' ' + text
content += ' ' + getNodeText(node.data)
// 概要
let generalization = node.data.generalization
if (generalization && generalization.text) {
let generalizationText = generalization.richText
? getText(generalization.text)
: generalization.text
content += `[${generalizationText}]`
const generalization = node.data.generalization
if (Array.isArray(generalization)) {
content += generalization.map(item => {
return ` [${getNodeText(item)}]`
})
} else if (generalization && generalization.text) {
const generalizationText = getNodeText(generalization)
content += ` [${generalizationText}]`
}
content += '\n\n'
// 备注

View File

@@ -0,0 +1,35 @@
import { walk, nodeRichTextToTextWithWrap } from '../utils'
const getNodeText = data => {
return data.richText ? nodeRichTextToTextWithWrap(data.text) : data.text
}
const getIndent = level => {
return new Array(level).fill(' ').join('')
}
// 转换成txt格式
export const transformToTxt = root => {
let content = ''
walk(
root,
null,
(node, parent, isRoot, layerIndex) => {
content += getIndent(layerIndex)
content += ' ' + getNodeText(node.data)
// 概要
const generalization = node.data.generalization
if (Array.isArray(generalization)) {
content += generalization.map(item => {
return ` [${getNodeText(item)}]`
})
} else if (generalization && generalization.text) {
content += ` [${getNodeText(generalization)}]`
}
content += '\n\n'
},
() => {},
true
)
return content
}

View File

@@ -198,7 +198,7 @@ const transformOldXmind = content => {
childrenItem.elements.length > 0
) {
const children = getElementsByType(childrenItem.elements, 'attached')
children.forEach((item, index) => {
;(children || []).forEach((item, index) => {
const newChild = {}
newNode.children.push(newChild)
if (childrenSummary[index]) {

View File

@@ -339,6 +339,10 @@ class AssociativeLine {
dasharray: [6, 4]
})
.fill({ color: 'none' })
// 箭头
this.markerPath
.stroke({ color: associativeLineColor })
.fill({ color: associativeLineColor })
this.creatingLine.marker('end', this.marker)
}

View File

@@ -28,6 +28,8 @@ class Cooperate {
this.currentData = null
// 用户信息
this.userInfo = null
// 是否正在重新设置思维导图数据
this.isSetData = false
// 绑定事件
this.bindEvent()
// 处理实例化时传入的思维导图数据
@@ -92,8 +94,8 @@ class Cooperate {
this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd)
// 监听设置思维导图数据事件
this.initData = this.initData.bind(this)
this.mindMap.on('set_data', this.initData)
this.onSetData = this.onSetData.bind(this)
this.mindMap.on('set_data', this.onSetData)
}
// 解绑事件
@@ -104,7 +106,7 @@ class Cooperate {
this.mindMap.off('data_change', this.onDataChange)
this.mindMap.off('node_active', this.onNodeActive)
this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd)
this.mindMap.off('set_data', this.initData)
this.mindMap.off('set_data', this.onSetData)
this.ydoc.destroy()
}
@@ -118,35 +120,63 @@ class Cooperate {
const res = transformObjectToTreeData(data)
if (!res) return
// 更新思维导图画布
this.mindMap.renderer.setData(res)
this.mindMap.render()
this.mindMap.command.addHistory()
this.mindMap.updateData(res)
}
// 当前思维导图改变后的处理,触发同步
onDataChange(data) {
if (this.isSetData) {
this.isSetData = false
return
}
const res = transformTreeDataToObject(data)
this.updateChanges(res)
}
// 找出更新点
updateChanges(data) {
const { beforeCooperateUpdate } = this.mindMap.opt
const oldData = this.currentData
this.currentData = data
this.ydoc.transact(() => {
// 找出新增的或修改的
const createOrUpdateList = []
Object.keys(data).forEach(uid => {
// 新增的或已经存在的,如果数据发生了改变
if (!oldData[uid] || !isSameObject(oldData[uid], data[uid])) {
this.ymap.set(uid, data[uid])
createOrUpdateList.push({
uid,
data: data[uid],
oldData: oldData[uid]
})
}
})
if (beforeCooperateUpdate && createOrUpdateList.length > 0) {
beforeCooperateUpdate({
type: 'createOrUpdate',
list: createOrUpdateList,
data
})
}
createOrUpdateList.forEach(item => {
this.ymap.set(item.uid, item.data)
})
// 找出删除的
const deleteList = []
Object.keys(oldData).forEach(uid => {
if (!data[uid]) {
this.ymap.delete(uid)
deleteList.push({ uid, data: oldData[uid] })
}
})
if (beforeCooperateUpdate && deleteList.length > 0) {
beforeCooperateUpdate({
type: 'delete',
list: deleteList
})
}
deleteList.forEach(item => {
this.ymap.delete(item.uid)
})
})
}
@@ -177,6 +207,12 @@ class Cooperate {
this.waitNodeUidMap = {}
}
// 监听思维导图数据的重新设置事件
onSetData(data) {
this.isSetData = true
this.initData(data)
}
// 设置用户信息
/**
* {
@@ -220,6 +256,7 @@ class Cooperate {
// 设置当前数据
const data = Array.from(this.awareness.getStates().values())
this.currentAwarenessData = data
this.waitNodeUidMap = {}
walk(data, (uid, node, userInfo) => {
// 不显示自己
if (userInfo.id === this.userInfo.id) return

View File

@@ -10,6 +10,7 @@ import { SVG } from '@svgdotjs/svg.js'
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas'
import { transformToMarkdown } from '../parse/toMarkdown'
import { ERROR_TYPES } from '../constants/constant'
import { transformToTxt } from '../parse/toTxt'
// 导出插件
class Export {
@@ -47,11 +48,13 @@ class Export {
// 获取svg数据
async getSvgData() {
let { exportPaddingX, exportPaddingY, errorHandler, resetCss } =
let { exportPaddingX, exportPaddingY, errorHandler, resetCss, addContentToHeader, addContentToFooter } =
this.mindMap.opt
let { svg, svgHTML } = this.mindMap.getSvgData({
paddingX: exportPaddingX,
paddingY: exportPaddingY
paddingY: exportPaddingY,
addContentToHeader,
addContentToFooter
})
// svg的image标签把图片的url转换成data:url类型否则导出会丢失图片
const task1 = this.createTransformImgTaskList(
@@ -294,6 +297,15 @@ class Export {
const res = await readBlob(blob)
return res
}
// txt文件
async txt() {
const data = this.mindMap.getData()
const content = transformToTxt(data)
const blob = new Blob([content])
const res = await readBlob(blob)
return res
}
}
Export.instanceName = 'doExport'

View File

@@ -93,10 +93,7 @@ class MiniMap {
return {
getImgUrl: async callback => {
const blob = new Blob([svgStr], {
type: 'image/svg+xml'
})
const res = await readBlob(blob)
const res = await this.mindMap.doExport.fixSvgStrAndToBlob(svgStr)
callback(res)
},
svgHTML: svgStr, // 小地图html

View File

@@ -0,0 +1,92 @@
import { walk, getNodeDataIndex } from '../utils/index'
const defaultColorsList = [
'rgb(255, 213, 73)',
'rgb(255, 136, 126)',
'rgb(107, 225, 141)',
'rgb(151, 171, 255)',
'rgb(129, 220, 242)',
'rgb(255, 163, 125)',
'rgb(152, 132, 234)'
]
// 彩虹线条插件
class RainbowLines {
constructor({ mindMap }) {
this.mindMap = mindMap
}
// 更新彩虹线条配置
updateRainLinesConfig(config = {}) {
const newConfig = this.mindMap.opt.rainbowLinesConfig || {}
newConfig.open = !!config.open
newConfig.colorsList = Array.isArray(config.colorsList)
? config.colorsList
: []
// 如果开启彩虹线条,那么先移除所有节点的自定义连线颜色配置
if (this.mindMap.opt.rainbowLinesConfig.open) {
this.removeNodeLineColor()
}
this.mindMap.render()
}
// 删除所有节点的连线颜色
removeNodeLineColor() {
const tree = this.mindMap.renderer.renderTree
if (!tree) return
walk(
tree,
null,
cur => {
delete cur.data.lineColor
},
null,
true
)
this.mindMap.command.addHistory()
}
// 获取一个节点的第二层级的祖先节点
getSecondLayerAncestor(node) {
if (node.layerIndex === 0) {
return null
} else if (node.layerIndex === 1) {
return node
} else {
let res = null
let parent = node.parent
while (parent) {
if (parent.layerIndex === 1) {
return parent
}
parent = parent.parent
}
return res
}
}
// 获取颜色列表
getColorsList() {
const { rainbowLinesConfig } = this.mindMap.opt
return rainbowLinesConfig &&
Array.isArray(rainbowLinesConfig.colorsList) &&
rainbowLinesConfig.colorsList.length > 0
? rainbowLinesConfig.colorsList
: [...defaultColorsList]
}
// 获取一个节点的彩虹线条颜色
getNodeColor(node) {
const { rainbowLinesConfig } = this.mindMap.opt
if (!rainbowLinesConfig || !rainbowLinesConfig.open) return ''
const ancestor = this.getSecondLayerAncestor(node)
if (!ancestor) return
const index = getNodeDataIndex(ancestor)
const colorsList = this.getColorsList()
return colorsList[index % colorsList.length]
}
}
RainbowLines.instanceName = 'rainbowLines'
export default RainbowLines

View File

@@ -326,10 +326,10 @@ class RichText {
nodes && nodes.length > 0 ? nodes : this.mindMap.renderer.activeNodeList
list.forEach(node => {
this.mindMap.execCommand('SET_NODE_TEXT', node, html, true)
if (node.isGeneralization) {
// if (node.isGeneralization) {
// 概要节点
node.generalizationBelongNode.updateGeneralization()
}
// node.generalizationBelongNode.updateGeneralization()
// }
this.mindMap.render()
})
this.mindMap.emit('hide_text_edit', this.textEditNode, list)
@@ -641,6 +641,7 @@ class RichText {
// 将所有节点转换成非富文本节点
transformAllNodesToNormalNode() {
if (!this.mindMap.renderer.renderTree) return
walk(
this.mindMap.renderer.renderTree,
null,

View File

@@ -40,6 +40,7 @@ class Scrollbar {
this.mindMap.on('mouseup', this.onMouseup)
this.mindMap.on('node_tree_render_end', this.updateScrollbar)
this.mindMap.on('view_data_change', this.updateScrollbar)
this.mindMap.on('resize', this.updateScrollbar)
}
// 解绑事件
@@ -48,6 +49,7 @@ class Scrollbar {
this.mindMap.off('mouseup', this.onMouseup)
this.mindMap.off('node_tree_render_end', this.updateScrollbar)
this.mindMap.off('view_data_change', this.updateScrollbar)
this.mindMap.off('resize', this.updateScrollbar)
}
// 渲染后、数据改变需要更新滚动条
@@ -202,7 +204,8 @@ class Scrollbar {
yOffset -
paddingY * t.scaleY +
paddingY -
rootCenterOffset.y * t.scaleY
rootCenterOffset.y * t.scaleY +
((this.mindMap.height - this.mindMap.initHeight) / 2) * t.scaleY // 画布宽高改变了,但是思维导图元素变换的中心点依旧是原有位置,所以需要加上中心点变化量
this.mindMap.view.translateYTo(chartTop)
this.emitEvent({
horizontal: scrollbarData.horizontal,
@@ -238,7 +241,8 @@ class Scrollbar {
xOffset -
paddingX * t.scaleX +
paddingX -
rootCenterOffset.x * t.scaleX
rootCenterOffset.x * t.scaleX +
((this.mindMap.width - this.mindMap.initWidth) / 2) * t.scaleX // 画布宽高改变了,但是思维导图元素变换的中心点依旧是原有位置,所以需要加上中心点变化量
this.mindMap.view.translateXTo(chartLeft)
this.emitEvent({
vertical: scrollbarData.vertical,

View File

@@ -4,6 +4,7 @@ import {
isUndef,
replaceHtmlText
} from '../utils/index'
import Node from '../core/render/node/Node'
// 搜索插件
class Search {
@@ -69,14 +70,14 @@ class Search {
// 结束搜索
endSearch() {
if (!this.isSearching) return
if (this.mindMap.opt.readonly && this.matchNodeList[this.currentIndex]) {
this.matchNodeList[this.currentIndex].closeHighlight()
}
this.searchText = ''
this.matchNodeList = []
this.currentIndex = -1
this.notResetSearchText = false
this.isSearching = false
if (this.mindMap.opt.readonly) {
this.mindMap.renderer.closeHighlightNode()
}
this.emitEvent()
}
@@ -84,8 +85,16 @@ class Search {
doSearch() {
this.matchNodeList = []
this.currentIndex = -1
bfsWalk(this.mindMap.renderer.root, node => {
let { richText, text } = node.getData()
const { isOnlySearchCurrentRenderNodes } = this.mindMap.opt
// 如果要搜索收起来的节点,那么要遍历渲染树而不是节点树
const tree = isOnlySearchCurrentRenderNodes
? this.mindMap.renderer.root
: this.mindMap.renderer.renderTree
if (!tree) return
bfsWalk(tree, node => {
let { richText, text } = isOnlySearchCurrentRenderNodes
? node.getData()
: node.data
if (richText) {
text = getTextFromHtml(text)
}
@@ -95,6 +104,11 @@ class Search {
})
}
// 判断对象是否是节点实例
isNodeInstance(node) {
return node instanceof Node
}
// 搜索下一个,定位到下一个匹配节点
searchNext(callback) {
if (!this.isSearching || this.matchNodeList.length <= 0) return
@@ -103,14 +117,24 @@ class Search {
} else {
this.currentIndex = 0
}
let currentNode = this.matchNodeList[this.currentIndex]
const currentNode = this.matchNodeList[this.currentIndex]
this.notResetSearchText = true
this.mindMap.execCommand('GO_TARGET_NODE', currentNode, () => {
this.notResetSearchText = false
const uid = this.isNodeInstance(currentNode)
? currentNode.getData('uid')
: currentNode.data.uid
const targetNode = this.mindMap.renderer.findNodeByUid(uid)
this.mindMap.execCommand('GO_TARGET_NODE', uid, node => {
if (!this.isNodeInstance(currentNode)) {
this.matchNodeList[this.currentIndex] = node
}
callback()
// 只读模式下节点无法激活,所以通过高亮的方式
if (this.mindMap.opt.readonly) {
this.mindMap.renderer.highlightNode(currentNode)
node.highlight()
}
// 如果当前节点实例已经存在则不会触发data_change事件那么需要手动把标志复位
if (targetNode) {
this.notResetSearchText = false
}
})
}
@@ -154,15 +178,20 @@ class Search {
return
replaceText = String(replaceText)
this.matchNodeList.forEach(node => {
let text = this.getReplacedText(node, this.searchText, replaceText)
this.mindMap.renderer.setNodeDataRender(
node,
{
text,
resetRichText: !!node.getData('richText')
},
true
)
const text = this.getReplacedText(node, this.searchText, replaceText)
if (this.isNodeInstance(node)) {
this.mindMap.renderer.setNodeDataRender(
node,
{
text,
resetRichText: !!node.getData('richText')
},
true
)
} else {
node.data.text = text
node.data.resetRichText = !!node.data.richText
}
})
this.mindMap.render()
this.mindMap.command.addHistory()
@@ -171,7 +200,9 @@ class Search {
// 获取某个节点替换后的文本
getReplacedText(node, searchText, replaceText) {
let { richText, text } = node.getData()
let { richText, text } = this.isNodeInstance(node)
? node.getData()
: node.data
if (richText) {
return replaceHtmlText(text, searchText, replaceText)
} else {

View File

@@ -8,6 +8,10 @@ const hyperlink =
const note =
'<svg t="1624195132675" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8792" ><path d="M152.768 985.984 152.768 49.856l434.56 0 66.816 0 234.048 267.392 0 66.816 0 601.92L152.768 985.984 152.768 985.984zM654.144 193.088l0 124.16 108.736 0L654.144 193.088 654.144 193.088zM821.312 384.064l-167.168 0L587.328 384.064 587.328 317.312 587.328 116.736 219.584 116.736 219.584 919.04l601.728 0L821.312 384.064 821.312 384.064zM386.688 517.888 319.808 517.888 319.808 450.944l66.816 0L386.624 517.888 386.688 517.888zM386.688 651.584 319.808 651.584 319.808 584.704l66.816 0L386.624 651.584 386.688 651.584zM386.688 785.344 319.808 785.344l0-66.88 66.816 0L386.624 785.344 386.688 785.344zM721.024 517.888 453.632 517.888 453.632 450.944l267.392 0L721.024 517.888 721.024 517.888zM654.144 651.584 453.632 651.584 453.632 584.704l200.512 0L654.144 651.584 654.144 651.584zM620.672 785.344l-167.04 0 0-66.88 167.04 0L620.672 785.344 620.672 785.344z" p-id="8793"></path></svg>'
// 附件图标
const attachment =
'<svg t="1711935375590" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3864" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M516.373333 375.978667l136.576-136.576a147.797333 147.797333 0 0 1 208.853334-0.021334 147.690667 147.690667 0 0 1-0.042667 208.832l-204.8 204.778667v0.021333l-153.621333 153.6c-85.973333 85.973333-225.28 85.973333-311.253334 0.021334-85.994667-85.973333-85.973333-225.216 0.149334-311.36L431.146667 256.362667a21.333333 21.333333 0 0 0-30.165334-30.165334L162.069333 465.066667c-102.805333 102.826667-102.826667 269.056-0.149333 371.733333 102.613333 102.613333 268.970667 102.613333 371.584 0l153.6-153.642667h0.021333l0.021334-0.021333 204.778666-204.778667c74.325333-74.325333 74.346667-194.858667 0.021334-269.184-74.24-74.24-194.88-74.24-269.162667 0.042667l-136.576 136.554667-187.626667 187.626666a117.845333 117.845333 0 0 0-0.106666 166.826667 118.037333 118.037333 0 0 0 166.826666-0.106667l255.850667-255.829333a21.333333 21.333333 0 0 0-30.165333-30.165333L435.136 669.973333a75.370667 75.370667 0 0 1-106.496 0.106667 75.178667 75.178667 0 0 1 0.128-106.496l187.605333-187.605333z" p-id="3865"></path></svg>'
// 节点icon
export const nodeIconList = [
{
@@ -303,6 +307,7 @@ const getNodeIconListIcon = (name, extendIconList = []) => {
export default {
hyperlink,
note,
attachment,
nodeIconList,
getNodeIconListIcon
}

View File

@@ -4,6 +4,8 @@ import {
selfCloseTagList
} from '../constants/constant'
import MersenneTwister from './mersenneTwister'
import { ForeignObject } from '@svgdotjs/svg.js'
// 深度优先遍历树
export const walk = (
root,
@@ -688,6 +690,52 @@ export const textToNodeRichTextWithWrap = html => {
.join('')
}
// 去除富文本内容的样式包括样式标签比如strong、em、s等
// 但要保留数学公式内容
let removeRichTextStyesEl = null
export const removeRichTextStyes = html => {
if (!removeRichTextStyesEl) {
removeRichTextStyesEl = document.createElement('div')
}
removeRichTextStyesEl.innerHTML = html
// 首先用占位文本替换掉所有的公式
const formulaList = removeRichTextStyesEl.querySelectorAll('.ql-formula')
Array.from(formulaList).forEach(el => {
const placeholder = document.createTextNode('$smmformula$')
el.parentNode.replaceChild(placeholder, el)
})
// 然后遍历每行节点,去掉内部的所有标签,转为文本
const childNodes = removeRichTextStyesEl.childNodes
let list = []
for (let i = 0; i < childNodes.length; i++) {
const node = childNodes[i]
if (node.nodeType === 1) {
// 元素节点
list.push(node.textContent)
} else if (node.nodeType === 3) {
// 文本节点
list.push(node.nodeValue)
}
}
// 拼接文本
html = list
.map(item => {
return `<p><span>${htmlEscape(item)}</span></p>`
})
.join('')
// 将公式添加回去
if (formulaList.length > 0) {
html = html.replace(/\$smmformula\$/g, '<span class="smmformula"></span>')
removeRichTextStyesEl.innerHTML = html
const els = removeRichTextStyesEl.querySelectorAll('.smmformula')
Array.from(els).forEach((el, index) => {
el.parentNode.replaceChild(formulaList[index], el)
})
html = removeRichTextStyesEl.innerHTML
}
return html
}
// 判断是否是移动端环境
export const isMobile = () => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
@@ -1268,3 +1316,48 @@ export const getRectRelativePosition = (rect1, rect2) => {
return 'overlap'
}
}
// 处理获取svg内容时添加额外内容
export const handleGetSvgDataExtraContent = ({
addContentToHeader,
addContentToFooter
}) => {
// 追加内容
const cssTextList = []
let header = null
let headerHeight = 0
let footer = null
let footerHeight = 0
const handle = (fn, callback) => {
if (typeof fn === 'function') {
const res = fn()
if (!res) return
const { el, cssText, height } = res
if (el instanceof HTMLElement) {
el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
const foreignObject = new ForeignObject()
foreignObject.height(height)
foreignObject.add(el)
callback(foreignObject, height)
}
if (cssText) {
cssTextList.push(cssText)
}
}
}
handle(addContentToHeader, (foreignObject, height) => {
header = foreignObject
headerHeight = height
})
handle(addContentToFooter, (foreignObject, height) => {
footer = foreignObject
footerHeight = height
})
return {
cssTextList,
header,
headerHeight,
footer,
footerHeight
}
}

8545
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,9 +14,11 @@
},
"dependencies": {
"@toast-ui/editor": "^3.1.5",
"codemirror": "^5.65.16",
"core-js": "^3.6.5",
"element-ui": "^2.15.1",
"highlight.js": "^10.7.3",
"katex": "^0.16.9",
"v-viewer": "^1.6.4",
"vue": "^2.6.11",
"vue-i18n": "^8.27.2",

View File

@@ -8,4 +8,4 @@ content = content.replace(
/(MindMap.version\s*=\s*)[^\n]+(\n)/,
`$1'${pkg.version}'$2`
)
fs.writeFileSync(file, content)
fs.writeFileSync(file, content)

View File

@@ -14,6 +14,7 @@ let mindMapData = null
* @Desc: 克隆思维导图数据,去除激活状态
*/
const copyMindMapTreeData = (tree, root) => {
if (!root) return null
tree.data = simpleDeepClone(root.data)
// tree.data.isActive = false
tree.children = []

BIN
web/src/assets/avatar/L.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1697073602349') format('woff2'),
url('iconfont.woff?t=1697073602349') format('woff'),
url('iconfont.ttf?t=1697073602349') format('truetype');
src: url('iconfont.woff2?t=1711935414521') format('woff2'),
url('iconfont.woff?t=1711935414521') format('woff'),
url('iconfont.ttf?t=1711935414521') format('truetype');
}
.iconfont {
@@ -13,6 +13,34 @@
-moz-osx-font-smoothing: grayscale;
}
.iconfujian:before {
content: "\e88a";
}
.icongeshihua:before {
content: "\e7a3";
}
.iconyuanma:before {
content: "\e658";
}
.icongundongtiao:before {
content: "\e670";
}
.iconxietongwendang:before {
content: "\e60d";
}
.iconTXT:before {
content: "\e6e1";
}
.iconwenjian1:before {
content: "\e69f";
}
.icondodeparent:before {
content: "\e70f";
}

View File

@@ -57,7 +57,7 @@ export const themeMap = {
dark4: require('../assets/img/themes/dark4.jpg'),
cactus: require('../assets/img/themes/cactus.jpg'),
classic6: require('../assets/img/themes/classic6.jpg'),
classic7: require('../assets/img/themes/classic7.jpg'),
classic7: require('../assets/img/themes/classic7.jpg')
}
// 公式列表
@@ -111,3 +111,82 @@ export const supportRootLineKeepSameInCurveLayouts = [
'logicalStructure',
'mindMap'
]
// 彩虹线条配置
export const rainbowLinesOptions = [
{
value: 'close'
},
{
value: 'colors1',
list: [
'rgb(255, 213, 73)',
'rgb(255, 136, 126)',
'rgb(107, 225, 141)',
'rgb(151, 171, 255)',
'rgb(129, 220, 242)',
'rgb(255, 163, 125)',
'rgb(152, 132, 234)'
]
},
{
value: 'colors2',
list: [
'rgb(248, 93, 93)',
'rgb(255, 151, 84)',
'rgb(255, 214, 69)',
'rgb(73, 205, 140)',
'rgb(64, 192, 255)',
'rgb(84, 110, 214)',
'rgb(164, 93, 220)'
]
},
{
value: 'colors3',
list: [
'rgb(140, 240, 231)',
'rgb(74, 210, 255)',
'rgb(65, 168, 243)',
'rgb(49, 128, 205)',
'rgb(188, 226, 132)',
'rgb(113, 215, 123)',
'rgb(120, 191, 109)'
]
},
{
value: 'colors4',
list: [
'rgb(169, 98, 99)',
'rgb(245, 125, 123)',
'rgb(254, 183, 168)',
'rgb(251, 218, 171)',
'rgb(138, 163, 181)',
'rgb(131, 127, 161)',
'rgb(84, 83, 140)'
]
},
{
value: 'colors5',
list: [
'rgb(255, 229, 142)',
'rgb(254, 158, 41)',
'rgb(248, 119, 44)',
'rgb(232, 82, 80)',
'rgb(182, 66, 98)',
'rgb(99, 54, 99)',
'rgb(65, 40, 82)'
]
},
{
value: 'colors6',
list: [
'rgb(171, 227, 209)',
'rgb(107, 201, 196)',
'rgb(55, 170, 169)',
'rgb(18, 135, 131)',
'rgb(74, 139, 166)',
'rgb(75, 105, 150)',
'rgb(57, 75, 133)'
]
}
]

View File

@@ -480,5 +480,11 @@ export const downTypeList = [
type: 'xmind',
icon: 'iconxmind',
desc: 'XMind file'
},
{
name: 'Txt',
type: 'txt',
icon: 'iconTXT',
desc: 'Plain text file'
}
]

View File

@@ -425,7 +425,7 @@ export const shortcutKeyList = [
icon: 'icondodeparent',
name: '上移一个层级',
value: 'Shift + Tab'
},
}
]
}
]
@@ -575,5 +575,11 @@ export const downTypeList = [
type: 'xmind',
icon: 'iconxmind',
desc: 'XMind格式'
},
{
name: 'Txt',
type: 'txt',
icon: 'iconTXT',
desc: '纯文本文件'
}
]
]

View File

@@ -62,7 +62,9 @@ export default {
isUseHandDrawnLikeStyle: 'Is use hand drawn like style',
rootLineStartPos: 'Root line start pos',
center: 'Center',
right: 'Right'
right: 'Right',
rainbowLines: 'Rainbow lines',
notUseRainbowLines: 'Not use rainbow lines'
},
color: {
moreColor: 'More color'
@@ -94,7 +96,9 @@ export default {
fitCanvas: 'Fit canvas',
removeImage: 'Remove image',
removeHyperlink: 'Remove hyperlink',
removeNote: 'Remove note'
removeNote: 'Remove note',
removeCustomStyles: 'Remove custom styles',
removeAllNodeCustomStyles: 'Remove all node custom styles'
},
count: {
words: 'Words',
@@ -126,7 +130,9 @@ export default {
paddingX: 'Padding x',
paddingY: 'Padding y',
useMultiPageExport: 'Export multi page',
defaultFileName: 'Mind map'
defaultFileName: 'Mind map',
addFooterTextPlaceholder: 'For example: From simple-mind-map',
addFooterText: 'Add text at the footer'
},
fullscreen: {
fullscreenShow: 'Full screen show',
@@ -148,7 +154,8 @@ export default {
closeMiniMap: 'Close mini map',
readonly: 'Change to eadonly',
edit: 'Change to edit',
backToRoot: 'Back to root node'
backToRoot: 'Back to root node',
changeSourceCodeEdit: 'Switch to source code editing mode'
},
nodeHyperlink: {
title: 'Link',
@@ -252,6 +259,7 @@ export default {
associativeLine: 'Associative line',
painter: 'Painter',
formula: 'Formula',
attachment: 'Attachment',
more: 'More',
selectFileTip: 'Please select a file',
notSupportTip:
@@ -262,7 +270,8 @@ export default {
fileContentError: 'File content error',
fileOpenFailed: 'File open failed',
defaultFileName: 'Mind map',
creatingTip: 'Creating file'
creatingTip: 'Creating file',
directory: 'Directory'
},
edit: {
newFeatureNoticeTitle: 'New feature reminder',
@@ -272,7 +281,9 @@ export default {
splitByWrap: 'Is automatically split nodes based on line breaks?',
tip: 'Tip',
yes: 'Yes',
no: 'No'
no: 'No',
exportError: 'Export failed',
dragTip: 'Release here to import the file'
},
mouseAction: {
tip1:
@@ -312,5 +323,19 @@ export default {
},
other: {
loading: 'Loading, please wait...'
},
sourceCodeEdit: {
sourceCodeTip: 'It is not recommended to modify the style in rich text mode because it requires synchronous modification of data and HTML structure.',
format: 'Format',
copy: 'Copy',
confirm: 'Complete',
close: 'Close',
formatErrorTip: 'The JSON format is incorrect. Please check and try again',
copyTip: 'Copied to clipboard',
formatTip: 'Format complete'
},
attachment: {
deleteAttachment: 'Delete attachment',
tip: 'The attachment function is only available on the client side'
}
}

View File

@@ -62,7 +62,9 @@ export default {
isUseHandDrawnLikeStyle: '是否开启手绘风格',
rootLineStartPos: '根节点连线起始位置',
center: '中心',
right: '右侧'
right: '右侧',
rainbowLines: '彩虹线条',
notUseRainbowLines: '不使用彩虹线条'
},
color: {
moreColor: '更多颜色'
@@ -94,7 +96,9 @@ export default {
fitCanvas: '适应画布',
removeImage: '移除图片',
removeHyperlink: '移除超链接',
removeNote: '移除备注'
removeNote: '移除备注',
removeCustomStyles: '一键去除自定义样式',
removeAllNodeCustomStyles: '一键去除所有节点自定义样式'
},
count: {
words: '字数',
@@ -124,7 +128,9 @@ export default {
paddingX: '水平内边距',
paddingY: '垂直内边距',
useMultiPageExport: '是否多页导出',
defaultFileName: '思维导图'
defaultFileName: '思维导图',
addFooterText: '底部添加文字',
addFooterTextPlaceholder: '比如来自simple-mind-map'
},
fullscreen: {
fullscreenShow: '全屏查看',
@@ -146,7 +152,8 @@ export default {
closeMiniMap: '关闭小地图',
readonly: '切换为只读模式',
edit: '切换为编辑模式',
backToRoot: '回到根节点'
backToRoot: '回到根节点',
changeSourceCodeEdit: '切换为源码编辑模式'
},
nodeHyperlink: {
title: '超链接',
@@ -249,6 +256,7 @@ export default {
associativeLine: '关联线',
painter: '格式刷',
formula: '公式',
attachment: '附件',
more: '更多',
selectFileTip: '请选择文件',
notSupportTip: '你的浏览器不支持该功能或者当前页面非https协议',
@@ -258,7 +266,8 @@ export default {
fileContentError: '文件内容有误',
fileOpenFailed: '文件打开失败',
defaultFileName: '思维导图',
creatingTip: '正在创建文件'
creatingTip: '正在创建文件',
directory: '目录'
},
edit: {
newFeatureNoticeTitle: '新特性提醒',
@@ -268,7 +277,9 @@ export default {
splitByWrap: '是否按换行自动分割节点?',
tip: '提示',
yes: '是',
no: '否'
no: '否',
exportError: '导出失败',
dragTip: '在此释放以导入该文件'
},
mouseAction: {
tip1: '当前:左键拖动画布,右键框选节点',
@@ -306,5 +317,19 @@ export default {
},
other: {
loading: '正在加载,请稍后...'
},
sourceCodeEdit: {
sourceCodeTip: '富文本模式下不建议修改样式因为需要同步修改数据及html结构。',
format: '格式化',
copy: '复制',
confirm: '完成',
close: '关闭',
formatErrorTip: 'JSON格式有误请检查后再试',
copyTip: '已复制到剪贴板',
formatTip: '格式化完成'
},
attachment: {
deleteAttachment: '删除附件',
tip: '附件功能仅在客户端可用'
}
}

View File

@@ -11,7 +11,7 @@ let langList = [
}
]
let StartList = ['introduction', 'start', 'deploy', 'client', 'translate', 'changelog']
let CourseList = new Array(25).fill(0).map((_, index) => {
let CourseList = new Array(27).fill(0).map((_, index) => {
return 'course' + (index + 1)
})
let APIList = [
@@ -38,6 +38,7 @@ let APIList = [
'scrollbar',
'formula',
'cooperate',
'rainbowLines',
'handDrawnLikeStyle',
'xmind',
'markdown',

View File

@@ -1,5 +1,151 @@
# Changelog
## 0.9.10
Fix:
> 1.Fix the issue of all replace operation error during search;
>
> 2.Fix the issue of rendering the mini map with entity characters that are not supported by SVG in node text;
New:
> 1.Add an event translate when moving the canvas;
>
> 2.Node content supports setting attachments;
Demo
> 1.Support dragging and dropping files onto the page for import;
Client:
> 1.Nodes support adding attachments;
## 0.9.9-fix.2
Fix:
> 1.Fix the issue of the original node style not being updated during the insertion of the parent node operation;
>
> 2.Fix the issue of errors when switching structures when opening rainbow lines;
Demo:
> 1.Fix the issue where the side bar outline opens in source code editing mode when clicking full screen editing;
## 0.9.9-fix.1
Fix the issue where the search plugin was unable to search due to changes in the previous version.
## 0.9.9
Fix:
> 1.Fix the issue of incorrect arrow color when creating association lines for the first time;
New:
> 1.Support inserting child nodes with the insert key;
>
> 2.Add the beforeShortcutRun instantiation option to intercept shortcut key operations;
>
> 3.The method of incrementally updating canvas data by adding root instances, updateData;
>
> 4.Add a rainbow line plugin;
>
> 5.Add mouse in and out events to icons in nodes;
>
> 6.Add the getAncestorNodes method to the node instance to obtain the list of ancestor nodes;
>
> 7.Adding mouse events to personnel avatars during collaborative editing;
>
> 8.Instantiation and setData methods support passing in empty data;
>
> 9.Add an instantiation option for adding custom content when exporting images;
Demo:
> 1.Support configuration of rainbow lines;
>
> 2.Add source code editing mode;
>
> 3.Exporting PNG, PDF, and SVG supports setting custom text at the bottom;
Client:
> 1.Double click the file to open the application without opening the workbench page;
>
> 2.Fix the issue of multiple editing windows opening when repeatedly opening the same file;
>
> 3.Prompt for modifying and deleting files being edited;
## 0.9.8
Fix:
> 1.Fix the issue of incorrect calculation function of mind maps within the canvas and scroll bar positions when the canvas size changes;
>
> 2.Fix the issue of errors when importing some old version xmind files;
>
> 3.Fix the issue where the highlight box of the collapsed node does not disappear when searching for highlighted nodes in read-only mode;
>
> 4.Fix the issue where data changes during search and search results are not updated in certain situations;
>
> 5.Fix the issue of deleting adjacent nodes when deleting nodes that are currently being edited;
>
> 6.Fix the issue of rendering only the root node when expand: false is set to the root node in the node data;
>
> 7.Fix the collaborative editing plugin: When a node is selected and then folded, the activation status of the node disappears, but the selected status of the node still exists in other clients;
>
> 8.Optimize the export of markdowns and fix the issue of missing summaries;
>
> 9.Fix the issue of ineffective forward and backward operations on node positions during free drag and drop;
New:
> 1.The commands such as INSERTNODE will not overwrite custom UIDs;
>
> 2.Methods for adding and removing highlighting for node instances; Adjust the way read-only mode searches for highlighted nodes;
>
> 3.Add configuration for searching all nodes (including those that have been folded); Search defaults to searching all nodes;
>
> 4.Add configuration options that cannot be selected by multiple people on the same node during collaborative editing;
>
> 5.Add lifecycle function configuration options before synchronizing collaborative editing node operations;
>
> 6.Add UID field to the summary node;
>
> 7.Added support for exporting txt files;
Demo:
> 1.Fix the issue where shortcut keys become invalid after opening label pop ups and note pop ups and clicking on the mask to close them;
>
> 2.Modify the association logic between themes and dark colors;
>
> 3.Support reading local directories;
>
> 4.Fix the issue of selecting a file in the import pop-up window and then deleting it without actually deleting it;
>
> 5.Export new txt files;
## 0.9.7
Fix:
> 1.Fix the issue where the style is not updated when dragging nodes below level 2 to the root node to become level 2 nodes; Fix the issue where the style is not updated when moving nodes with the command to move up one level;
>
> 2.Fix the issue of losing focus when activating summary nodes and then setting text styles for them;
New:
> 1.Add two commands to remove custom styles from nodes;
Demo:
> 1.Add a right-click menu button to remove custom styles from nodes.
## 0.9.6
Fix:
@@ -32,7 +178,7 @@ Demo:
>
> 4.Add two new themes;
>
> 5.Fix the issue of text wrapping not taking effect in the outline and displaying the br tag;
> 5.Fix the issue of text wrapping not taking effect in the outline and displaying the br tag;
## 0.9.5

View File

@@ -1,10 +1,118 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.9.10</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue of all replace operation error during search;</p>
<p>2.Fix the issue of rendering the mini map with entity characters that are not supported by SVG in node text;</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.Add an event translate when moving the canvas;</p>
<p>2.Node content supports setting attachments;</p>
</blockquote>
<p>Demo</p>
<blockquote>
<p>1.Support dragging and dropping files onto the page for import;</p>
</blockquote>
<p>Client:</p>
<blockquote>
<p>1.Nodes support adding attachments;</p>
</blockquote>
<h2>0.9.9-fix.2</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue of the original node style not being updated during the insertion of the parent node operation;</p>
<p>2.Fix the issue of errors when switching structures when opening rainbow lines;</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.Fix the issue where the side bar outline opens in source code editing mode when clicking full screen editing;</p>
</blockquote>
<h2>0.9.9-fix.1</h2>
<p>Fix the issue where the search plugin was unable to search due to changes in the previous version.</p>
<h2>0.9.9</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue of incorrect arrow color when creating association lines for the first time;</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.Support inserting child nodes with the insert key;</p>
<p>2.Add the beforeShortcutRun instantiation option to intercept shortcut key operations;</p>
<p>3.The method of incrementally updating canvas data by adding root instances, updateData;</p>
<p>4.Add a rainbow line plugin;</p>
<p>5.Add mouse in and out events to icons in nodes;</p>
<p>6.Add the getAncestorNodes method to the node instance to obtain the list of ancestor nodes;</p>
<p>7.Adding mouse events to personnel avatars during collaborative editing;</p>
<p>8.Instantiation and setData methods support passing in empty data;</p>
<p>9.Add an instantiation option for adding custom content when exporting images;</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.Support configuration of rainbow lines;</p>
<p>2.Add source code editing mode;</p>
<p>3.Exporting PNG, PDF, and SVG supports setting custom text at the bottom;</p>
</blockquote>
<p>Client:</p>
<blockquote>
<p>1.Double click the file to open the application without opening the workbench page;</p>
<p>2.Fix the issue of multiple editing windows opening when repeatedly opening the same file;</p>
<p>3.Prompt for modifying and deleting files being edited;</p>
</blockquote>
<h2>0.9.8</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue of incorrect calculation function of mind maps within the canvas and scroll bar positions when the canvas size changes;</p>
<p>2.Fix the issue of errors when importing some old version xmind files;</p>
<p>3.Fix the issue where the highlight box of the collapsed node does not disappear when searching for highlighted nodes in read-only mode;</p>
<p>4.Fix the issue where data changes during search and search results are not updated in certain situations;</p>
<p>5.Fix the issue of deleting adjacent nodes when deleting nodes that are currently being edited;</p>
<p>6.Fix the issue of rendering only the root node when expand: false is set to the root node in the node data;</p>
<p>7.Fix the collaborative editing plugin: When a node is selected and then folded, the activation status of the node disappears, but the selected status of the node still exists in other clients;</p>
<p>8.Optimize the export of markdowns and fix the issue of missing summaries;</p>
<p>9.Fix the issue of ineffective forward and backward operations on node positions during free drag and drop;</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.The commands such as INSERTNODE will not overwrite custom UIDs;</p>
<p>2.Methods for adding and removing highlighting for node instances; Adjust the way read-only mode searches for highlighted nodes;</p>
<p>3.Add configuration for searching all nodes (including those that have been folded); Search defaults to searching all nodes;</p>
<p>4.Add configuration options that cannot be selected by multiple people on the same node during collaborative editing;</p>
<p>5.Add lifecycle function configuration options before synchronizing collaborative editing node operations;</p>
<p>6.Add UID field to the summary node;</p>
<p>7.Added support for exporting txt files;</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.Fix the issue where shortcut keys become invalid after opening label pop ups and note pop ups and clicking on the mask to close them;</p>
<p>2.Modify the association logic between themes and dark colors;</p>
<p>3.Support reading local directories;</p>
<p>4.Fix the issue of selecting a file in the import pop-up window and then deleting it without actually deleting it;</p>
<p>5.Export new txt files;</p>
</blockquote>
<h2>0.9.7</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue where the style is not updated when dragging nodes below level 2 to the root node to become level 2 nodes; Fix the issue where the style is not updated when moving nodes with the command to move up one level;</p>
<p>2.Fix the issue of losing focus when activating summary nodes and then setting text styles for them;</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.Add two commands to remove custom styles from nodes;</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.Add a right-click menu button to remove custom styles from nodes.</p>
</blockquote>
<h2>0.9.6</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue where the style of the collapsed nodes remains unchanged when switching themes;</p>
<p>2.Fix the issue of losing the new text style when switching themes for text wrapping;</p>
<p>3.Fix the issue where the style of the newline text remains unchanged when switching themes;</p>
<p>4.Fixed the issue of adding mathematical formulas to nodes and not changing the text style when switching themes;</p>
</blockquote>
<p>New:</p>
<blockquote>
@@ -18,6 +126,8 @@
<p>1.The outline supports pressing Shift+Tab to move nodes up one level;</p>
<p>2.Support setting the position of node arrow display;</p>
<p>3.Support setting the starting position of root node connections;</p>
<p>4.Add two new themes;</p>
<p>5.Fix the issue of text wrapping not taking effect in the outline and displaying the br tag;</p>
</blockquote>
<h2>0.9.5</h2>
<p>Fix:</p>

View File

@@ -25,7 +25,7 @@ const mindMap = new MindMap({
| Field Name | Type | Default Value | Description | Required |
| -------------------------------- | ------- | ---------------- | ------------------------------------------------------------ | -------- |
| el | Element | | Container element, must be a DOM element | Yes |
| data | Object | {} | Mind map data, Please refer to the introduction of 【Data structure】 below | |
| data | Object 、null | | Mind map data, Please refer to the introduction of 【Data structure】 below. V0.9.9+supports passing empty objects or null, and the canvas will display blank space | |
| layout | String | logicalStructure | Layout type, options: logicalStructure (logical structure diagram), mindMap (mind map), catalogOrganization (catalog organization diagram), organizationStructure (organization structure diagram)、timelinev0.5.4+, timeline、timeline2v0.5.4+, up down alternating timeline、fishbonev0.5.4+, fishbone diagram | |
| fishboneDegv0.5.4+ | Number | 45 | Set the diagonal angle of the fishbone structure diagram | |
| theme | String | default | Theme, options: default, classic, minions, pinkGrape, mint, gold, vitalityOrange, greenLeaf, dark2, skyGreen, classic2, classic3, classic4(v0.2.0+), classicGreen, classicBlue, blueSky, brainImpairedPink, dark, earthYellow, freshGreen, freshRed, romanticPurple, simpleBlack(v0.5.4+), courseGreen(v0.5.4+), coffee(v0.5.4+), redSpirit(v0.5.4+), blackHumour(v0.5.4+), lateNightOffice(v0.5.4+), blackGold(v0.5.4+)、、avocado(v.5.10-fix.2+)、autumn(v.5.10-fix.2+)、orangeJuice(v.5.10-fix.2+) | |
@@ -110,7 +110,14 @@ const mindMap = new MindMap({
| isLimitMindMapInCanvasWhenHasScrollbarv0.9.2+ | Boolean | true | When registering the Scrollbar plugin, will the mind map be limited to the canvas and the isLimitMindMapInCanvas configuration no longer work | |
| associativeLineInitPointsPositionv0.9.5+ | null / { from, to } | { from: '', to: '' } | By default, the position of the two endpoints of a newly created association line is calculated based on the relative position of the center points of the two nodes. If you want to fix the position, you can configure it through this option. If neither from nor to is transmitted, they will be automatically calculated. If only one is transmitted, the other will be automatically calculated. from and to optional values
left、top、bottom、right | |
| enableAdjustAssociativeLinePointsv0.9.5+ | Boolean | true | Is it allowed to adjust the position of the two endpoints of the associated line |
| enableAdjustAssociativeLinePointsv0.9.5+ | Boolean | true | Is it allowed to adjust the position of the two endpoints of the associated line | |
| isOnlySearchCurrentRenderNodesv0.9.8+ | Boolean | false | Is it necessary to only search for the current rendered node, and nodes that have been collapsed will not be searched for | |
| onlyOneEnableActiveNodeOnCooperatev0.9.8+ | Boolean | false | During collaborative editing, the same node cannot be selected by multiple people at the same time | |
| beforeCooperateUpdatev0.9.8+ | Function、null | null | During collaborative editing, node operations are about to be updated to the lifecycle functions of other clients. The function takes an object as a parameter:{ type: 【createOrUpdateCreate or update nodes、deleteDelete node】, list: 【Array type, 1.When type=createOrUpdate, it represents the node data that has been created or updated, which will be synchronized to other clients, so you can modify the data; 2.When type=delete, represents the deleted node data】 } | |
| beforeShortcutRunv0.9.9+ | Function、null | null | The lifecycle function before the shortcut operation is about to be executed, returning true can prevent the operation from executing. The function takes two parameters: keyShortcut key、activeNodeListList of currently activated nodes | |
| rainbowLinesConfigv0.9.9+ | Object | { open: false, colorsList: [] } | Rainbow line configuration requires registering the RainbowLines plugin first. Object type, Structure: { open: false【Is turn on rainbow lines】, colorsList: []【Customize the color list for rainbow lines. If not set, the default color list will be used】 } | |
| addContentToHeaderv0.9.9+ | Function、null | null | Add custom content to the header when exporting PNG, SVG, and PDF. Can pass a function that can return null to indicate no content is added, or it can return an object, For a detailed introduction, please refer to section 【How to add custom content when exporting】 below | |
| addContentToFooterv0.9.9+ | Function、null | null | The basic definition is the same as addContentToHeader, adding custom content at the end | |
### Data structure
@@ -172,6 +179,41 @@ If you want to add custom fields, you can add them to the same level as 'data' a
| type | String | | Values for icon grouping |
| list | Array | | A list of icons under grouping, with each item in the array being an object, `{ name: '', icon: '' }``name`represents the name of the icon, `icon`represents the icon, Can be an `svg` icon, such as `<svg ...><path></path></svg>`, also can be a image `url`, or `base64` icon, such as `data:image/png;base64,...` |
### How to add custom content when exporting
The two instantiation options `addContentToHeader` and `addContentToFooter` can be used to add custom content at the beginning and end when exporting `png``svg``pdf`, The default value is `null`, which means no configuration. A function can be passed and can return `null`, which means no content will be added. If you want to add content, you need to return the following structure:
```
{
el,// Custom DOM node to be added, styles can be inline
cssText,// Optional, if the style does not want to be inlined, you can pass this value as a CSS string
height: 50// The height of the returned DOM node must be passed
}
```
A simple example:
```js
new MindMap({
addContentToFooter: () => {
const el = document.createElement('div')
el.className = 'footer'
el.innerHTML = 'From: simple-mind-map'
const cssText = `
.footer {
width: 100%;
height: 30px;
}
`
return {
el,
cssText,
height: 30
}
}
})
```
## Static methods
### defineTheme(name, config)
@@ -311,6 +353,12 @@ Current Theme Configuration.
## Instance methods
### updateData(data)
> v0.9.9+
Update canvas data. If the new data is formed by adding, deleting, modifying, and querying based on the current canvas node data, this method can be used to update the canvas data. The performance will be better, and not all nodes will be recreated, but rather reused as much as possible.
### clearDraw()
> v0.8.0+
@@ -323,7 +371,7 @@ Clear `lineDraw`、`associativeLineDraw`、`nodeDraw`、`otherDraw` containers.
Destroy mind maps. It will remove registered plugins, remove listening events, and delete all nodes on the canvas.
### getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false })
### getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false, addContentToHeader, addContentToFooter })
> v0.3.0+
@@ -333,6 +381,10 @@ Destroy mind maps. It will remove registered plugins, remove listening events, a
`ignoreWatermark`v0.8.0+, Do not draw watermarks. If you do not need to draw watermarks, you can pass 'true' because drawing watermarks is very slow
`addContentToHeader`v0.9.9+, Function, You can return the custom content to be added to the header, as detailed in the configuration in 【Instantiation options】
`addContentToFooter`v0.9.9+, Function, You can return the custom content to be added to the tail, as detailed in the configuration in 【Instantiation options】
Get the `svg` data and return an object. The detailed structure is as follows:
```js
@@ -403,7 +455,8 @@ Listen to an event. Event list:
| expand_btn_click | Node expand or collapse event | this (node instance) |
| before_show_text_edit | Event before node text edit box opens | |
| hide_text_edit | Node text edit box close event | textEditNode (text edit box DOM node), activeNodeList (current list of active nodes) |
| scale | Zoom event | scale (zoom ratio) |
| scale | Canvas zoom event | scale (zoom ratio) |
| translatev0.9.10+ | Canvas movement event | xtranslate x、ytranslate y |
| node_img_dblclickv0.2.15+ | Node image double-click event | this (node instance), e (event object) |
| node_img_mouseenterv0.6.5+ | Node image mouseenter event | thisnode instance、imgNodeimg node、eevent object |
| node_img_mouseleavev0.6.5+ | Node image mouseleave event | thisnode instance、imgNodeimg node、eevent object |
@@ -416,7 +469,9 @@ Listen to an event. Event list:
| associative_line_clickv0.4.5+ | Triggered when an associated line is clicked | path(Connector node)、clickPath(Invisible click line node)、node(Start node)、toNode(Target node) |
| svg_mouseenterv0.5.1+ | Triggered when the mouse moves into the SVG canvas | eevent object |
| svg_mouseleavev0.5.1+ | Triggered when the mouse moves out of the SVG canvas | eevent object |
| node_icon_clickv0.6.10+ | Triggered when clicking on an icon within a node | thisnode instance、itemClick on the icon name、eevent object |
| node_icon_clickv0.6.10+ | Triggered when clicking on an icon within a node | thisnode instance、itemClick on the icon name、eevent object、node(Icon node, v0.9.9+) |
| node_icon_mouseenterv0.9.9+ | Triggered when the mouse moves into an icon within a node | thisnode instance、itemClick on the icon name、eevent object、node(Icon node) |
| node_icon_mouseleavev0.9.9+ | Triggered when the mouse moves out of the icon within the node | thisnode instance、itemClick on the icon name、eevent object、node(Icon node) |
| view_theme_changev0.6.12+ | Triggered after calling the setTheme method to set the theme | themetheme name |
| set_datav0.7.3+ | Triggered when the setData method is called to dynamically set mind map data | dataNew Mind Map Data |
| resizev0.8.0+ | Triggered after the container size changes, actually when the 'resize' method of the mind map instance is called | |
@@ -425,6 +480,9 @@ Listen to an event. Event list:
| body_click | Click event of document.body | eevent object |
| data_change_detailv0.9.3+ | The detailed changes in rendering tree data will return an array, with each item representing an update point and each item being an object, There is a 'type' attribute that represents the type of detail, Including 'create' (create node), 'update' (update node), 'delete' (delete node), There is a 'data' attribute that represents the current updated node data. If it is of the 'update' type, there will also be an 'oldData' attribute that saves the data of the node before the update | arrDetail data |
| layout_changev0.9.4+ | Triggered when modifying the structure, i.e. when the mindMap.setLayout() method is called | layoutNew layout |
| node_cooperate_avatar_clickv0.9.9+ | Triggered when the mouse clicks on a person's avatar during collaborative editing | userInfo(User info)、 this(Current node instance)、 node(Avatar node)、 e(Event Object) |
| node_cooperate_avatar_mouseenterv0.9.9+ | Triggered when the mouse moves over a person's avatar during collaborative editing | userInfo(User info)、 this(Current node instance)、 node(Avatar node)、 e(Event Object) |
| node_cooperate_avatar_mouseleavev0.9.9+ | Triggered when removing personnel avatars with the mouse during collaborative editing | userInfo(User info)、 this(Current node instance)、 node(Avatar node)、 e(Event Object) |
### emit(event, ...args)
@@ -524,6 +582,7 @@ redo. All commands are as follows:
| SET_NODE_ICON | Set Node Icon | node (node to set), icons (array, predefined image names array, available icons can be obtained in the nodeIconList list in the [icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js) file, icon name is type_name, such as ['priority_1']) |
| SET_NODE_HYPERLINK | Set Node Hyperlink | node (node to set), link (hyperlink address), title (hyperlink name, optional) |
| SET_NODE_NOTE | Set Node Note | node (node to set), note (note text) |
| SET_NODE_ATTACHMENTv0.9.10+ | Set node attachment | nodenode to set、urlattachment url、nameattachment name, optional |
| SET_NODE_TAG | Set Node Tag | node (node to set), tag (string array, built-in color information can be obtained in [constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/constants/constant.js)) |
| INSERT_AFTER (v0.1.5+) | Move Node to After Another Node | node (node to move, (v0.7.2+supports passing node arrays to move multiple nodes simultaneously)), exist (target node) |
| INSERT_BEFORE (v0.1.5+) | Move Node to Before Another Node | node (node to move, (v0.7.2+supports passing node arrays to move multiple nodes simultaneously)), exist (target node) |
@@ -533,19 +592,21 @@ redo. All commands are as follows:
| SET_NODE_CUSTOM_POSITION (v0.2.0+) | Set a custom position for a node | node (the node to set), left (custom x coordinate, default is undefined), top (custom y coordinate, default is undefined) |
| RESET_LAYOUT (v0.2.0+) | Arrange layout with one click | |
| SET_NODE_SHAPE (v0.2.4+) | Set the shape of a node | node (the node to set), shape (the shape, all shapes: [Shape.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/core/render/node/Shape.js)) |
| GO_TARGET_NODEv0.6.7+ | Navigate to a node, and if the node is collapsed, it will automatically expand to that node | nodeNode instance or node uid to locate、callbackv0.6.9+, Callback function after positioning completion |
| GO_TARGET_NODEv0.6.7+ | Navigate to a node, and if the node is collapsed, it will automatically expand to that node | nodeNode instance or node uid to locate、callbackv0.6.9+, Callback function after positioning completion, v0.9.8+receives a parameter representing the target node instance |
| INSERT_MULTI_NODEv0.7.2+ | Insert multiple sibling nodes into the specified node at the same time, with the operating node being the currently active node or the specified node | appointNodesOptional, specify nodes, specify multiple nodes to pass an array, nodeListData list of newly inserted nodes, array type |
| INSERT_MULTI_CHILD_NODEv0.7.2+ | Insert multiple child nodes into the specified node simultaneously, with the operation node being the currently active node or the specified node | appointNodesOptional, specify nodes, specify multiple nodes to pass an array, childListData list of newly inserted nodes, array type |
| INSERT_FORMULAv0.7.2+ | Insert mathematical formulas into nodes, operate on the currently active node or specified node | formulaMathematical formula to insert, LaTeX syntax, appointNodesOptional, specify the node to insert the formula into. Multiple nodes can be passed as arrays, otherwise it defaults to the currently active node |
| INSERT_PARENT_NODEv0.8.0+ | Insert a parent node into the specified node, with the operation node being the currently active node or the specified node | openEditActivate the newly inserted node and enter editing mode, default to 'true'`)、 appointNodesOptional, specify the node to insert into the parent node, and specify that multiple nodes can pass an array、 appointDataOptional, specify the data for the newly created node, such as {text: 'xxx', ...}, Detailed structure can be referenced [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) |
| REMOVE_CURRENT_NODEv0.8.0+ | Delete only the current node, operate on the currently active node or specified node | appointNodesOptional, specify the nodes to be deleted, and multiple nodes can be passed as an array |
| MOVE_UP_ONE_LEVELv0.9.6+ | Move the specified node up one level | nodeOptional, specify the node to move up the hierarchy, if not passed, it will be the first node in the current active node |
| REMOVE_CUSTOM_STYLESv0.9.7+ | One click removal of custom styles for a node | nodeOptional, specify the node to clear the custom style, otherwise it will be the first one in the current active node |
| REMOVE_ALL_NODE_CUSTOM_STYLESv0.9.7+ | One click removal of multiple nodes or custom styles for all nodes | appointNodesOptional, node instance array, specifying multiple nodes to remove custom styles from. If not passed, the custom styles of all nodes on the current canvas will be removed |
### setData(data)
Dynamic setting of mind map data, pure node data
`data`: mind map structure data
`data`: mind map structure data. V0.9.9+ supports passing empty objects or null, and the canvas will display blank space.
### setFullData(_data_)

View File

@@ -37,9 +37,9 @@
</tr>
<tr>
<td>data</td>
<td>Object</td>
<td>{}</td>
<td>Mind map data, Please refer to the introduction of Data structure below</td>
<td>Object null</td>
<td></td>
<td>Mind map data, Please refer to the introduction of Data structure below. V0.9.9+supports passing empty objects or null, and the canvas will display blank space</td>
<td></td>
</tr>
<tr>
@@ -637,6 +637,55 @@
<td>Is it allowed to adjust the position of the two endpoints of the associated line</td>
<td></td>
</tr>
<tr>
<td>isOnlySearchCurrentRenderNodesv0.9.8+</td>
<td>Boolean</td>
<td>false</td>
<td>Is it necessary to only search for the current rendered node, and nodes that have been collapsed will not be searched for</td>
<td></td>
</tr>
<tr>
<td>onlyOneEnableActiveNodeOnCooperatev0.9.8+</td>
<td>Boolean</td>
<td>false</td>
<td>During collaborative editing, the same node cannot be selected by multiple people at the same time</td>
<td></td>
</tr>
<tr>
<td>beforeCooperateUpdatev0.9.8+</td>
<td>Function、null</td>
<td>null</td>
<td>During collaborative editing, node operations are about to be updated to the lifecycle functions of other clients. The function takes an object as a parameter:{ type: 【createOrUpdateCreate or update nodes、deleteDelete node】, list: 【Array type, 1.When type=createOrUpdate, it represents the node data that has been created or updated, which will be synchronized to other clients, so you can modify the data; 2.When type=delete, represents the deleted node data】 }</td>
<td></td>
</tr>
<tr>
<td>beforeShortcutRunv0.9.9+</td>
<td>Function、null</td>
<td>null</td>
<td>The lifecycle function before the shortcut operation is about to be executed, returning true can prevent the operation from executing. The function takes two parameters: keyShortcut key、activeNodeListList of currently activated nodes</td>
<td></td>
</tr>
<tr>
<td>rainbowLinesConfigv0.9.9+</td>
<td>Object</td>
<td>{ open: false, colorsList: [] }</td>
<td>Rainbow line configuration requires registering the RainbowLines plugin first. Object type, Structure: { open: false【Is turn on rainbow lines】, colorsList: []【Customize the color list for rainbow lines. If not set, the default color list will be used】 }</td>
<td></td>
</tr>
<tr>
<td>addContentToHeaderv0.9.9+</td>
<td>Function、null</td>
<td>null</td>
<td>Add custom content to the header when exporting PNG, SVG, and PDF. Can pass a function that can return null to indicate no content is added, or it can return an object, For a detailed introduction, please refer to section 【How to add custom content when exporting】 below</td>
<td></td>
</tr>
<tr>
<td>addContentToFooterv0.9.9+</td>
<td>Function、null</td>
<td>null</td>
<td>The basic definition is the same as addContentToHeader, adding custom content at the end</td>
<td></td>
</tr>
</tbody>
</table>
<h3>Data structure</h3>
@@ -755,6 +804,34 @@
</tr>
</tbody>
</table>
<h3>How to add custom content when exporting</h3>
<p>The two instantiation options <code>addContentToHeader</code> and <code>addContentToFooter</code> can be used to add custom content at the beginning and end when exporting <code>png</code>、<code>svg</code>、<code>pdf</code>, The default value is <code>null</code>, which means no configuration. A function can be passed and can return <code>null</code>, which means no content will be added. If you want to add content, you need to return the following structure:</p>
<pre class="hljs"><code>{
el,// Custom DOM node to be added, styles can be inline
cssText,// Optional, if the style does not want to be inlined, you can pass this value as a CSS string
height: 50// The height of the returned DOM node must be passed
}
</code></pre>
<p>A simple example:</p>
<pre class="hljs"><code><span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">addContentToFooter</span>: <span class="hljs-function">() =&gt;</span> {
<span class="hljs-keyword">const</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
el.className = <span class="hljs-string">&#x27;footer&#x27;</span>
el.innerHTML = <span class="hljs-string">&#x27;From: simple-mind-map&#x27;</span>
<span class="hljs-keyword">const</span> cssText = <span class="hljs-string">`
.footer {
width: 100%;
height: 30px;
}
`</span>
<span class="hljs-keyword">return</span> {
el,
cssText,
<span class="hljs-attr">height</span>: <span class="hljs-number">30</span>
}
}
})
</code></pre>
<h2>Static methods</h2>
<h3>defineTheme(name, config)</h3>
<blockquote>
@@ -851,6 +928,11 @@ mindMap.setTheme(<span class="hljs-string">&#x27;Theme name&#x27;</span>)
<h3>themeConfig</h3>
<p>Current Theme Configuration.</p>
<h2>Instance methods</h2>
<h3>updateData(data)</h3>
<blockquote>
<p>v0.9.9+</p>
</blockquote>
<p>Update canvas data. If the new data is formed by adding, deleting, modifying, and querying based on the current canvas node data, this method can be used to update the canvas data. The performance will be better, and not all nodes will be recreated, but rather reused as much as possible.</p>
<h3>clearDraw()</h3>
<blockquote>
<p>v0.8.0+</p>
@@ -861,13 +943,15 @@ mindMap.setTheme(<span class="hljs-string">&#x27;Theme name&#x27;</span>)
<p>v0.6.0+</p>
</blockquote>
<p>Destroy mind maps. It will remove registered plugins, remove listening events, and delete all nodes on the canvas.</p>
<h3>getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false })</h3>
<h3>getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false, addContentToHeader, addContentToFooter })</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p><code>paddingX</code>: Padding x</p>
<p><code>paddingY</code>: Padding y</p>
<p><code>ignoreWatermark</code>v0.8.0+, Do not draw watermarks. If you do not need to draw watermarks, you can pass 'true' because drawing watermarks is very slow</p>
<p><code>addContentToHeader</code>v0.9.9+, Function, You can return the custom content to be added to the header, as detailed in the configuration in 【Instantiation options】</p>
<p><code>addContentToFooter</code>v0.9.9+, Function, You can return the custom content to be added to the tail, as detailed in the configuration in 【Instantiation options】</p>
<p>Get the <code>svg</code> data and return an object. The detailed structure is as follows:</p>
<pre class="hljs"><code>{
svg, <span class="hljs-comment">// Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)</span>
@@ -1032,10 +1116,15 @@ poor performance and should be used sparingly.</p>
</tr>
<tr>
<td>scale</td>
<td>Zoom event</td>
<td>Canvas zoom event</td>
<td>scale (zoom ratio)</td>
</tr>
<tr>
<td>translatev0.9.10+</td>
<td>Canvas movement event</td>
<td>xtranslate x、ytranslate y</td>
</tr>
<tr>
<td>node_img_dblclickv0.2.15+</td>
<td>Node image double-click event</td>
<td>this (node instance), e (event object)</td>
@@ -1098,7 +1187,17 @@ poor performance and should be used sparingly.</p>
<tr>
<td>node_icon_clickv0.6.10+</td>
<td>Triggered when clicking on an icon within a node</td>
<td>thisnode instance、itemClick on the icon name、eevent object</td>
<td>thisnode instance、itemClick on the icon name、eevent object、node(Icon node, v0.9.9+)</td>
</tr>
<tr>
<td>node_icon_mouseenterv0.9.9+</td>
<td>Triggered when the mouse moves into an icon within a node</td>
<td>thisnode instance、itemClick on the icon name、eevent object、node(Icon node)</td>
</tr>
<tr>
<td>node_icon_mouseleavev0.9.9+</td>
<td>Triggered when the mouse moves out of the icon within the node</td>
<td>thisnode instance、itemClick on the icon name、eevent object、node(Icon node)</td>
</tr>
<tr>
<td>view_theme_changev0.6.12+</td>
@@ -1140,6 +1239,21 @@ poor performance and should be used sparingly.</p>
<td>Triggered when modifying the structure, i.e. when the mindMap.setLayout() method is called</td>
<td>layoutNew layout</td>
</tr>
<tr>
<td>node_cooperate_avatar_clickv0.9.9+</td>
<td>Triggered when the mouse clicks on a person's avatar during collaborative editing</td>
<td>userInfo(User info) this(Current node instance) node(Avatar node) e(Event Object)</td>
</tr>
<tr>
<td>node_cooperate_avatar_mouseenterv0.9.9+</td>
<td>Triggered when the mouse moves over a person's avatar during collaborative editing</td>
<td>userInfo(User info)、 this(Current node instance)、 node(Avatar node)、 e(Event Object)</td>
</tr>
<tr>
<td>node_cooperate_avatar_mouseleavev0.9.9+</td>
<td>Triggered when removing personnel avatars with the mouse during collaborative editing</td>
<td>userInfo(User info)、 this(Current node instance)、 node(Avatar node)、 e(Event Object)</td>
</tr>
</tbody>
</table>
<h3>emit(event, ...args)</h3>
@@ -1316,6 +1430,11 @@ redo. All commands are as follows:</p>
<td>node (node to set), note (note text)</td>
</tr>
<tr>
<td>SET_NODE_ATTACHMENTv0.9.10+</td>
<td>Set node attachment</td>
<td>nodenode to set、urlattachment url、nameattachment name, optional</td>
</tr>
<tr>
<td>SET_NODE_TAG</td>
<td>Set Node Tag</td>
<td>node (node to set), tag (string array, built-in color information can be obtained in <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/constants/constant.js">constant.js</a>)</td>
@@ -1363,7 +1482,7 @@ redo. All commands are as follows:</p>
<tr>
<td>GO_TARGET_NODEv0.6.7+</td>
<td>Navigate to a node, and if the node is collapsed, it will automatically expand to that node</td>
<td>nodeNode instance or node uid to locate、callbackv0.6.9+, Callback function after positioning completion</td>
<td>nodeNode instance or node uid to locate、callbackv0.6.9+, Callback function after positioning completion, v0.9.8+receives a parameter representing the target node instance</td>
</tr>
<tr>
<td>INSERT_MULTI_NODEv0.7.2+</td>
@@ -1395,11 +1514,21 @@ redo. All commands are as follows:</p>
<td>Move the specified node up one level</td>
<td>nodeOptional, specify the node to move up the hierarchy, if not passed, it will be the first node in the current active node</td>
</tr>
<tr>
<td>REMOVE_CUSTOM_STYLESv0.9.7+</td>
<td>One click removal of custom styles for a node</td>
<td>nodeOptional, specify the node to clear the custom style, otherwise it will be the first one in the current active node</td>
</tr>
<tr>
<td>REMOVE_ALL_NODE_CUSTOM_STYLESv0.9.7+</td>
<td>One click removal of multiple nodes or custom styles for all nodes</td>
<td>appointNodesOptional, node instance array, specifying multiple nodes to remove custom styles from. If not passed, the custom styles of all nodes on the current canvas will be removed</td>
</tr>
</tbody>
</table>
<h3>setData(data)</h3>
<p>Dynamic setting of mind map data, pure node data</p>
<p><code>data</code>: mind map structure data</p>
<p><code>data</code>: mind map structure data. V0.9.9+ supports passing empty objects or null, and the canvas will display blank space.</p>
<h3>setFullData(<em>data</em>)</h3>
<blockquote>
<p>v0.2.7+</p>

Some files were not shown because too many files have changed in this diff Show More