Compare commits

...

172 Commits

Author SHA1 Message Date
街角小林
9ef789110b 打包0.10.0 2024-06-05 09:29:59 +08:00
街角小林
9294fc4e7b Doc: update 2024-06-05 09:20:12 +08:00
街角小林
abacff6ede Doc:整理实例化选项文档 2024-06-04 18:53:43 +08:00
街角小林
79755e80b9 Fix:修复节点前后自定义内容导出图片时显示空白的问题 2024-06-04 16:30:23 +08:00
街角小林
b8a23beba4 Feat:打包后的文件中增加演示插件 2024-06-04 11:47:34 +08:00
街角小林
f996ec9bae Feat:支持直接在富文本编辑框中编辑数学公式 2024-06-04 11:45:31 +08:00
街角小林
8152fab185 Demo:新建和打开按钮增加导出的提示,防止内容丢失 2024-06-03 09:25:35 +08:00
wanglin2
21b404a322 Demo:优化富文本工具条下拉选项列表高度问题 2024-06-01 23:01:30 +08:00
街角小林
c5ed48ad99 Merge pull request #620 from wallace-yang/main
Feat:快捷键提示支持mac与修复富文本选择字体大小列表文字重叠bug
2024-06-01 22:49:41 +08:00
街角小林
25f0668a44 Feat:插入新节点时去除延时开启节点编辑的逻辑 2024-05-31 16:55:02 +08:00
街角小林
67fec82c72 update 2024-05-31 15:57:09 +08:00
街角小林
0760500ceb Feat:去除移除富文本内容中ql-cursor类名的节点的逻辑,修复文本换行时新增空行不生效的问题 2024-05-31 15:56:01 +08:00
街角小林
3355900bd3 Fix:修复复制带换行符的节点文本粘贴后会出现多行换行的问题 2024-05-31 15:08:31 +08:00
街角小林
417376dcb6 Feat:defenseXSS方法作为工具方法提供 2024-05-31 15:00:36 +08:00
街角小林
6a45ff2221 Feat:取消调用defenseXSS函数,对性能影响太大 2024-05-31 14:52:07 +08:00
街角小林
c967be2bc2 Fix:修复拖拽画布和拖拽调整图片时会选中文字的问题 2024-05-31 14:46:21 +08:00
街角小林
12dae210ef Merge branch 'feature' of https://github.com/wanglin2/mind-map into feature 2024-05-31 14:34:17 +08:00
街角小林
a75eb5f195 Feat:全新升级节点拖拽时的交互效果 2024-05-31 14:33:56 +08:00
wanglin2
9ad71c6627 update 2024-05-30 21:55:53 +08:00
wanglin2
2218e7bf12 Merge branch 'feature' of https://github.com/wanglin2/mind-map into feature 2024-05-30 21:49:11 +08:00
街角小林
c4777fb17a Doc: update 2024-05-29 17:47:28 +08:00
街角小林
edc2097d14 Doc: update 2024-05-29 17:45:58 +08:00
街角小林
53bcabe3d0 Fix:修复移动端部分浏览器设置缩放时会进行页面缩放的问题 2024-05-28 17:47:49 +08:00
街角小林
27477e39de Demo:导入存在多个画布的xmind文件支持选择指定的画布进行导入 2024-05-28 17:28:17 +08:00
街角小林
f4800746a3 Feat:导入存在多个画布的xmind文件支持选择指定的画布进行导入 2024-05-28 17:27:19 +08:00
街角小林
7c82d16d66 Demo:支持配置水印显示在节点下方 2024-05-27 19:14:00 +08:00
街角小林
dccd1c9459 Feat:支持设置水印显示在节点下方 2024-05-27 19:13:29 +08:00
街角小林
1f8fad8fc5 Feat:新增渲染开始事件node_tree_render_start 2024-05-27 18:41:50 +08:00
街角小林
a3d5588cd6 Doc: update 2024-05-27 18:39:04 +08:00
街角小林
7c96daf6d0 Fix:修复只读模式仍可通过Ctrl+点击节点方式激活节点 2024-05-27 18:31:42 +08:00
街角小林
db03e74f0d Doc: update 2024-05-27 18:24:44 +08:00
街角小林
459044beb9 Fix:修改51统计配置,解决报错的问题 2024-05-17 17:46:10 +08:00
街角小林
2537fb858f Merge branch 'main' of https://github.com/wanglin2/mind-map into main 2024-05-14 16:01:36 +08:00
街角小林
5c6d460455 打包demo 2024-05-14 16:01:05 +08:00
街角小林
6c3790e20e Delete CNAME 2024-05-14 14:57:25 +08:00
街角小林
40de891695 Create CNAME 2024-05-14 14:57:07 +08:00
街角小林
98f0d5e0fc 打包0.9.12 2024-05-13 11:23:29 +08:00
街角小林
6f3a02d39e Doc: update 2024-05-13 10:44:21 +08:00
街角小林
5cfc313f8e Feat:支持解析md文件中带html格式的标题文本 2024-05-13 10:13:48 +08:00
街角小林
d93825dd57 Fix:修复导入md文件时存在加粗的标题文本会解析为undefined的问题 2024-05-13 10:01:53 +08:00
街角小林
85171db778 Doc: update 2024-05-11 10:53:15 +08:00
街角小林
7d7ab9291a Demo:修复全屏查看模式下节点备注浮层无法显示的问题 2024-05-11 10:17:13 +08:00
街角小林
bb2502501e Feat:1.演示模式中禁止画布的所有内容响应鼠标事件;2.节点的超链接和备注图标支持响应鼠标事件;3.支持填空模式 2024-05-11 10:05:46 +08:00
街角小林
d60f30d97e Feat:1.优化代码:提取各处获取节点概要数据的兼容代码;2.演示插件支持概要;3.expandToNodeUid方法支持概要节点;4.findNodeByUid方法支持概要节点 2024-05-10 09:51:58 +08:00
街角小林
706b2ee65d Feat:新增添加节点附加的前置和后置内容的实例化选项 2024-05-08 19:02:19 +08:00
街角小林
e939b6132f Feat:新增节点标签的点击事件 2024-05-08 17:42:29 +08:00
街角小林
b69a0b620d Fix:1.全选、删除节点激活相邻节点、多选节点等操作增加派发before_node_active事件;2.多选节点改为实时派发激活事件 2024-05-08 09:54:16 +08:00
街角小林
d3d92a6e70 Demo:修复备注浮层显示时点击收起按钮收起节点后,备注浮层未消失的问题 2024-05-07 18:59:53 +08:00
街角小林
6751897309 Fix:修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题 2024-05-07 18:50:14 +08:00
街角小林
07a3f65911 Feat:移动指定节点到画布中心时默认不恢复缩放 2024-05-07 17:24:13 +08:00
街角小林
924b2660e1 打包Demo 2024-05-07 09:44:58 +08:00
街角小林
98d28a7b67 Doc: update 2024-05-07 09:33:21 +08:00
街角小林
a93518dee0 Merge branch 'feature' into main 2024-05-06 18:56:53 +08:00
街角小林
809c2c5666 Merge pull request #633 from Webb-L/hotfix
fix: 修复xss漏洞。
2024-05-06 18:27:06 +08:00
街角小林
cd90089b91 Doc: update 2024-05-06 18:11:10 +08:00
webb
4396c53d79 fix: 修复xss漏洞。 2024-04-28 23:26:04 +08:00
街角小林
4bea7d5e2b Doc: update 2024-04-24 09:51:32 +08:00
yanghongbo1
bc9d118efd Fix:修复富文本选择字体大小列表文字重叠bug 2024-04-22 18:58:18 +08:00
yanghongbo1
950b7ad57b Feat:快捷键提示支持mac 2024-04-22 15:12:48 +08:00
街角小林
cc62f98a9f 打包0.9.11 2024-04-19 14:34:56 +08:00
街角小林
244f2755a1 Doc: update 2024-04-19 14:18:59 +08:00
街角小林
23d38d9301 Doc: update 2024-04-19 14:18:24 +08:00
街角小林
73a61f81f8 Demo:新增演示模式 2024-04-19 09:30:32 +08:00
街角小林
6539a87cb2 Feat:新增演示插件 2024-04-19 09:29:48 +08:00
街角小林
75635ef2bb Doc: update 2024-04-17 17:50:18 +08:00
街角小林
20fae6270d Demo:优化鼠标在窗口边缘点击右键时菜单显示不全的问题 2024-04-17 09:14:58 +08:00
街角小林
6b40358f65 Demo:节点右键菜单新增导出为图片按钮 2024-04-16 19:02:57 +08:00
街角小林
e072dcb170 Feat:支持导出某个节点为图片 2024-04-16 19:02:35 +08:00
街角小林
6878d92ebe Fix:修复拖拽节点到边缘时画布自动移动无法停止的问题 2024-04-16 17:32:04 +08:00
街角小林
e9352a4f6c Fix:修复删除非当前激活的节点时,当前激活节点的激活状态无法取消的问题 2024-04-15 18:50:05 +08:00
wanglin2
6b9eee7fc6 Fix:修复删除当前激活的节点的所有子节点后,展开收起按钮没有消失的问题 2024-04-13 19:31:07 +08:00
wanglin2
c1217f1532 Feat:支持按住Command键和Win键多选节点 2024-04-13 15:28:56 +08:00
wanglin2
d73225f787 Doc: update 2024-04-13 14:17:59 +08:00
wanglin2
11b3270314 Doc: update 2024-04-12 23:08:33 +08:00
街角小林
5730a7aed5 Doc: update 2024-04-12 17:41:45 +08:00
街角小林
aeda3924a0 Fix:修复Dockerfile错误 2024-04-12 17:36:54 +08:00
街角小林
d9300395ff 新增docker文件及文档 2024-04-11 09:39:42 +08:00
街角小林
5bff885c00 Doc: update 2024-04-09 18:47:40 +08:00
街角小林
088fd398a9 Fix:修复同时创建多个实例时,文本编辑后节点宽高丢失的问题 2024-04-08 19:27:51 +08:00
街角小林
487aa38216 Fix:修复概要节点文本编辑中按回车结束时,相应的节点高亮框会错位显示 2024-04-08 18:20:23 +08:00
街角小林
e2403ae433 Fix:修复富文本插件转换节点数据时没有处理节点概要的问题 2024-04-08 18:07:37 +08:00
街角小林
7f0202e16e Fix:1.修复一键去除所有节点自定义样式命令不支持不为数组的概要的问题;2.修复富文本模式下创建的概要节点不是富文本的问题;Feat:插入概要时支持默认聚焦和进入编辑状态 2024-04-08 18:06:34 +08:00
街角小林
2b8d4ae225 Fix:修复点击概要会触发data_change_detail事件的问题 2024-04-08 18:02:59 +08:00
街角小林
513a1c342c Feat:复制知犀数据时,概要数据创建为数组形式 2024-04-08 18:01:59 +08:00
街角小林
d641b7e2ef Doc: update 2024-04-08 09:40:06 +08:00
街角小林
c769d4d203 Doc: update 2024-04-07 18:37:11 +08:00
街角小林
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
264 changed files with 15139 additions and 5735 deletions

5
Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM nginx
RUN mkdir /app
COPY ./index.html /app/
COPY ./dist /app/dist/
COPY nginx.conf /etc/nginx/nginx.conf

145
README.md
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)。
> 客户端版本会落后于在线版本,尝试最新功能请优先使用在线版。
@@ -33,13 +31,26 @@ Github[releases](https://github.com/wanglin2/mind-map/releases)。
- [x] 支持逻辑结构图、思维导图、组织结构图、目录组织图、时间轴(横向、竖向)、鱼骨图等结构
- [x] 内置多种主题,允许高度自定义样式,支持注册新主题
- [x] 节点内容支持文本(普通文本、富文本)、图片、图标、超链接、备注、标签、概要、数学公式
- [x] 节点支持拖拽(拖拽移动、自由调整)、多种节点形状支持使用 DDM 完全自定义节点内容
- [x] 节点支持拖拽(拖拽移动、自由调整)、多种节点形状;支持扩展节点内容、支持使用 DDM 完全自定义节点内容
- [x] 支持画布拖动、缩放
- [x] 支持鼠标按键拖动选择和 Ctrl+左键两种多选节点方式
- [x] 支持导出为`json``png``svg``pdf``markdown``xmind`,支持从`json``xmind``markdown`导入
- [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印、滚动条
- [x] 支持导出为`json``png``svg``pdf``markdown``xmind``txt`,支持从`json``xmind``markdown`导入
- [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印、滚动条、手绘风格、彩虹线条
- [x] 提供丰富的配置,满足各种场景各种使用习惯
- [x] 支持协同编辑
- [x] 支持演示模式
官方提供了如下插件,可根据需求按需引入(某个功能不生效大概率是因为你没有引入对应的插件),具体使用方式请查看文档:
> RichText节点富文本插件、Select鼠标多选节点插件、Drag节点拖拽插件、AssociativeLine关联线插件、Export导出插件、KeyboardNavigation键盘导航插件、MiniMap小地图插件、Watermark水印插件、TouchEvent移动端触摸事件支持插件、NodeImgAdjust拖拽调整节点图片大小插件、Search搜索插件、Painter节点格式刷插件、Scrollbar滚动条插件、Formula数学公式插件、Cooperate协同编辑插件、RainbowLines彩虹线条插件、Demonstrate演示模式插件、HandDrawnLikeStyle手绘风格插件[收费]
本项目不会实现的特性:
> 1.自由节点,即多个根节点;
>
> 2.概要节点后面继续添加节点;
>
> 如果你需要以上特性,那么本库可能无法满足你的需求。
# 安装
@@ -86,19 +97,25 @@ const mindMap = new MindMap({
# License
[MIT](./LICENSE)
保留`mind-map`版权声明的情况下可随意商用。
[MIT](./LICENSE)。保留`mind-map`版权声明的情况下可随意商用。如不想保留可联系作者。
# 微信交流群
群聊人数较多,无法通过二维码入群,可以微信添加`wanglinguanfang`拉你入群。
群聊人数较多,无法通过二维码入群,可以微信添加`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
开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡,你的支持是开发者持续维护的最大动力~
> 推荐使用支付宝,微信获取不到头像。转账请备注【思维导图】。
@@ -272,4 +289,100 @@ 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>
<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/阿晨.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/庆国.jpg" style="width: 50px;height: 50px;" />
<span>庆国</span>
</span>
<span>
<img src="./web/src/assets/avatar/孟照星.jpg" style="width: 50px;height: 50px;" />
<span>Alex</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/最多5个字.jpg" style="width: 50px;height: 50px;" />
<span>最多5个字</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/ZX.jpg" style="width: 50px;height: 50px;" />
<span>ZX</span>
</span>
<span>
<img src="./web/src/assets/avatar/峰.jpg" style="width: 50px;height: 50px;" />
<span>峰</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/ZX.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 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: 58 KiB

BIN
dist/img/子豪.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
dist/img/孟照星.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
dist/img/宏涛.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
dist/img/峰.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
dist/img/庆国.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
dist/img/最多5个字.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
dist/img/木星二号.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 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: 28 KiB

BIN
dist/img/雨馨.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 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

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

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}}]);

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

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d36df"],{"5d71":function(e,t,n){"use strict";n.r(t);var i=function(){var e=this;e._self._c;return e._m(0)},o=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("Demonstrate plugin")]),t("blockquote",[t("p",[e._v("v0.9.11+")])]),t("p",[e._v("The "),t("code",[e._v("Demonstrate")]),e._v(" plugin provides demonstration functionality.")]),t("p",[e._v("When entering demonstration mode, the container elements will be automatically displayed in full screen, and then default to the root node. You can switch between the previous and next steps by pressing the left and right arrow keys on the keyboard, and exit demonstration mode by pressing the 'Esc' key.")]),t("p",[e._v("After entering demonstration mode, all shortcut keys on the mind map will be unavailable, and the mouse will not be able to operate the mind map.")]),t("h2",[e._v("Register")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-keyword"},[e._v("import")]),e._v(" MindMap "),t("span",{staticClass:"hljs-keyword"},[e._v("from")]),e._v(" "),t("span",{staticClass:"hljs-string"},[e._v("'simple-mind-map'")]),e._v("\n"),t("span",{staticClass:"hljs-keyword"},[e._v("import")]),e._v(" Demonstrate "),t("span",{staticClass:"hljs-keyword"},[e._v("from")]),e._v(" "),t("span",{staticClass:"hljs-string"},[e._v("'simple-mind-map/src/plugins/Demonstrate.js'")]),e._v("\n\nMindMap.usePlugin(Demonstrate)\n")])]),t("p",[e._v("After registration and instantiation of "),t("code",[e._v("MindMap")]),e._v(", the instance can be obtained through "),t("code",[e._v("mindMap.demonstrate")]),e._v(".")]),t("h3",[e._v("Config")]),t("p",[e._v("This plugin provides some configuration items for configuration, which can be configured through the instantiation option 'demonstrateConfig'. Please refer to the 【Instantiation options】 section in the 【Constructor】 section for details.")]),t("h3",[e._v("Event")]),t("p",[e._v("The plugin will dispatch the following events:")]),t("p",[t("code",[e._v("exit_demonstrate")]),e._v("Triggered when exiting the demonstration.")]),t("p",[t("code",[e._v("demonstrate_jump")]),e._v("Triggered when jumping.")]),t("p",[e._v("Please refer to the 'on' function in the 【Instance methods】 section of the 【Constructor】 chapter for details.")]),t("h2",[e._v("Props")]),t("h3",[e._v("stepList")]),t("p",[e._v("List of all steps demonstrated. Available when the 'enter' method is called.")]),t("h3",[e._v("currentStepIndex")]),t("p",[e._v("The index of the steps currently played, counting from 0.")]),t("h3",[e._v("config")]),t("p",[e._v("The current configuration of the plugin.")]),t("h2",[e._v("Methods")]),t("h3",[e._v("enter()")]),t("p",[e._v("Entering demonstration mode will automatically display the container elements in full screen.")]),t("h3",[e._v("exit()")]),t("p",[e._v("Exit demonstration mode, which can also be exited by pressing the 'Esc' key.")]),t("h3",[e._v("prev()")]),t("p",[e._v("Previous step.")]),t("h3",[e._v("next()")]),t("p",[e._v("Next step.")]),t("h3",[e._v("jump(index)")]),t("ul",[t("li",[t("code",[e._v("index")]),e._v("NumberTo jump to a certain step, count from 0.")])]),t("p",[e._v("Jump to a certain step.")])])}],s={},a=s,r=n("2877"),l=Object(r["a"])(a,i,o,!1,null,null,null);t["default"]=l.exports}}]);

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 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0db0f2"],{"6df4":function(n,v,_){"use strict";_.r(v);var e=function(){var n=this;n._self._c;return n._m(0)},o=[function(){var n=this,v=n._self._c;return v("div",[v("h1",[n._v("Command实例")]),v("p",[v("code",[n._v("command")]),n._v("实例负责命令的添加及执行,内置了很多命令,也可以自行添加,命令指需要在历史堆栈数据里添加副本的操作。可通过"),v("code",[n._v("mindMap.command")]),n._v("获取到该实例")]),v("h2",[n._v("方法")]),v("h3",[n._v("add(name, fn)")]),v("p",[n._v("添加命令。")]),v("p",[v("code",[n._v("name")]),n._v(":命令名称")]),v("p",[v("code",[n._v("fn")]),n._v(":命令要执行的方法")]),v("h3",[n._v("remove(name, fn)")]),v("p",[n._v("移除命令。")]),v("p",[v("code",[n._v("name")]),n._v(":要移除的命令名称")]),v("p",[v("code",[n._v("fn")]),n._v(":要移除的方法,不传的话移除该命令所有的方法")]),v("h3",[n._v("getCopyData()")]),v("p",[n._v("获取渲染树数据副本")]),v("h3",[n._v("clearHistory()")]),v("p",[n._v("清空历史堆栈数据")])])}],a={},c=a,d=_("2877"),p=Object(d["a"])(c,e,o,!1,null,null,null);v["default"]=p.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0db0f2"],{"6df4":function(v,_,e){"use strict";e.r(_);var n=function(){var v=this;v._self._c;return v._m(0)},o=[function(){var v=this,_=v._self._c;return _("div",[_("h1",[v._v("Command 实例")]),_("p",[_("code",[v._v("command")]),v._v("实例负责命令的添加及执行,内置了很多命令,也可以自行添加,命令指需要在历史堆栈数据里添加副本的操作。可通过"),_("code",[v._v("mindMap.command")]),v._v("获取到该实例")]),_("h2",[v._v("方法")]),_("h3",[v._v("pause()")]),_("blockquote",[_("p",[v._v("v0.9.11+")])]),_("p",[v._v("暂停收集历史数据。")]),_("h3",[v._v("recovery()")]),_("blockquote",[_("p",[v._v("v0.9.11+")])]),_("p",[v._v("恢复收集历史数据。")]),_("h3",[v._v("add(name, fn)")]),_("p",[v._v("添加命令。")]),_("p",[_("code",[v._v("name")]),v._v(":命令名称")]),_("p",[_("code",[v._v("fn")]),v._v(":命令要执行的方法")]),_("h3",[v._v("remove(name, fn)")]),_("p",[v._v("移除命令。")]),_("p",[_("code",[v._v("name")]),v._v(":要移除的命令名称")]),_("p",[_("code",[v._v("fn")]),v._v(":要移除的方法,不传的话移除该命令所有的方法")]),_("h3",[v._v("getCopyData()")]),_("p",[v._v("获取渲染树数据副本")]),_("h3",[v._v("clearHistory()")]),_("p",[v._v("清空历史堆栈数据")])])}],c={},p=c,a=e("2877"),d=Object(a["a"])(p,n,o,!1,null,null,null);_["default"]=d.exports}}]);

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}}]);

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0ddf37"],{8427:function(v,_,s){"use strict";s.r(_);var n=function(){var v=this;v._self._c;return v._m(0)},e=[function(){var v=this,_=v._self._c;return _("div",[_("h1",[v._v("XMind解析")]),_("blockquote",[_("p",[v._v("v0.2.7+")])]),_("p",[v._v("提供导入和导出"),_("code",[v._v("XMind")]),v._v("文件的方法。")]),_("h2",[v._v("引入")]),_("pre",{staticClass:"hljs"},[_("code",[_("span",{staticClass:"hljs-keyword"},[v._v("import")]),v._v(" xmind "),_("span",{staticClass:"hljs-keyword"},[v._v("from")]),v._v(" "),_("span",{staticClass:"hljs-string"},[v._v("'simple-mind-map/src/parse/xmind.js'")]),v._v("\n")])]),_("p",[v._v("如果使用的是"),_("code",[v._v("umd")]),v._v("格式的文件,那么可以通过如下方式获取:")]),_("pre",{staticClass:"hljs"},[_("code",[_("span",{staticClass:"hljs-tag"},[v._v("<"),_("span",{staticClass:"hljs-name"},[v._v("script")]),v._v(" "),_("span",{staticClass:"hljs-attr"},[v._v("src")]),v._v("="),_("span",{staticClass:"hljs-string"},[v._v('"simple-mind-map/dist/simpleMindMap.umd.min.js"')]),v._v(">")]),_("span",{staticClass:"hljs-tag"},[v._v("</"),_("span",{staticClass:"hljs-name"},[v._v("script")]),v._v(">")]),v._v("\n")])]),_("pre",{staticClass:"hljs"},[_("code",[v._v("simpleMindMap.xmind\n")])]),_("h2",[v._v("方法")]),_("h3",[v._v("xmind.parseXmindFile(file)")]),_("p",[v._v("解析"),_("code",[v._v(".xmind")]),v._v("文件,返回解析后的数据,可以使用"),_("code",[v._v("mindMap.setData(data)")]),v._v("来将返回的数据渲染到画布上")]),_("p",[_("code",[v._v("file")]),v._v(""),_("code",[v._v("File")]),v._v("对象")]),_("h3",[v._v("xmind.transformXmind(content)")]),_("blockquote",[_("p",[v._v("v0.6.6+版本该方法改为异步方法返回一个Promise实例")])]),_("p",[v._v("转换"),_("code",[v._v("xmind")]),v._v("数据,"),_("code",[v._v(".xmind")]),v._v("文件本质上是一个压缩包,改成"),_("code",[v._v("zip")]),v._v("后缀可以解压缩,里面存在一个"),_("code",[v._v("content.json")]),v._v("文件,如果你自己解析出了这个文件,那么可以把这个文件内容传递给这个方法进行转换,转换后的数据,可以使用"),_("code",[v._v("mindMap.setData(data)")]),v._v("来将返回的数据渲染到画布上")]),_("p",[_("code",[v._v("content")]),v._v(""),_("code",[v._v(".xmind")]),v._v("压缩包内的"),_("code",[v._v("content.json")]),v._v("文件内容")]),_("h3",[v._v("xmind.transformOldXmind(content)")]),_("blockquote",[_("p",[v._v("v0.2.8+")])]),_("p",[v._v("针对"),_("code",[v._v("xmind8")]),v._v("版本的数据解析,因为该版本的"),_("code",[v._v(".xmind")]),v._v("文件内没有"),_("code",[v._v("content.json")]),v._v(",对应的是"),_("code",[v._v("content.xml")]),v._v("。")]),_("p",[_("code",[v._v("content")]),v._v(""),_("code",[v._v(".xmind")]),v._v("压缩包内的"),_("code",[v._v("content.xml")]),v._v("文件内容")]),_("h3",[v._v("transformToXmind(data, name)")]),_("blockquote",[_("p",[v._v("v0.6.6+")])]),_("ul",[_("li",[_("p",[_("code",[v._v("data")]),v._v(""),_("code",[v._v("simple-mind-map")]),v._v("思维导图数据,可以通过"),_("code",[v._v("mindMap.getData()")]),v._v("方法获取。")])]),_("li",[_("p",[_("code",[v._v("name")]),v._v(":要导出的文件名。")])])]),_("p",[v._v("将"),_("code",[v._v("simple-mind-map")]),v._v("数据转为"),_("code",[v._v("xmind")]),v._v("文件。该方法为异步方法,返回一个"),_("code",[v._v("Promise")]),v._v("实例,返回的数据是一个"),_("code",[v._v("blob")]),v._v("类型的"),_("code",[v._v("zip")]),v._v("压缩包数据,你可以自行下载为文件。")])])}],t={},d=t,a=s("2877"),i=Object(a["a"])(d,n,e,!1,null,null,null);_["default"]=i.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0ddf37"],{8427:function(v,_,n){"use strict";n.r(_);var s=function(){var v=this;v._self._c;return v._m(0)},e=[function(){var v=this,_=v._self._c;return _("div",[_("h1",[v._v("XMind解析")]),_("blockquote",[_("p",[v._v("v0.2.7+")])]),_("p",[v._v("提供导入和导出"),_("code",[v._v("XMind")]),v._v("文件的方法。")]),_("h2",[v._v("引入")]),_("pre",{staticClass:"hljs"},[_("code",[_("span",{staticClass:"hljs-keyword"},[v._v("import")]),v._v(" xmind "),_("span",{staticClass:"hljs-keyword"},[v._v("from")]),v._v(" "),_("span",{staticClass:"hljs-string"},[v._v("'simple-mind-map/src/parse/xmind.js'")]),v._v("\n")])]),_("p",[v._v("如果使用的是"),_("code",[v._v("umd")]),v._v("格式的文件,那么可以通过如下方式获取:")]),_("pre",{staticClass:"hljs"},[_("code",[_("span",{staticClass:"hljs-tag"},[v._v("<"),_("span",{staticClass:"hljs-name"},[v._v("script")]),v._v(" "),_("span",{staticClass:"hljs-attr"},[v._v("src")]),v._v("="),_("span",{staticClass:"hljs-string"},[v._v('"simple-mind-map/dist/simpleMindMap.umd.min.js"')]),v._v(">")]),_("span",{staticClass:"hljs-tag"},[v._v("</"),_("span",{staticClass:"hljs-name"},[v._v("script")]),v._v(">")]),v._v("\n")])]),_("pre",{staticClass:"hljs"},[_("code",[v._v("simpleMindMap.xmind\n")])]),_("h2",[v._v("方法")]),_("h3",[v._v("xmind.parseXmindFile(file, handleMultiCanvas)")]),_("p",[v._v("解析"),_("code",[v._v(".xmind")]),v._v("文件,返回解析后的数据,可以使用"),_("code",[v._v("mindMap.setData(data)")]),v._v("来将返回的数据渲染到画布上")]),_("p",[_("code",[v._v("file")]),v._v(""),_("code",[v._v("File")]),v._v("对象")]),_("p",[_("code",[v._v("handleMultiCanvas")]),v._v("v0.10.0+可选可传递一个函数如果导入的xmind文件存在多个画布那么会调用该函数函数接收xmind画布列表数据为参数需要返回其中一个画布的数据比如接收的参数为"),_("code",[v._v("content")]),v._v(",要导入第二个画布的数据则返回"),_("code",[v._v("content[1]")]),v._v("。函数可以是异步函数返回一个Promise实例。")]),_("h3",[v._v("xmind.transformXmind(content)")]),_("blockquote",[_("p",[v._v("v0.6.6+版本该方法改为异步方法返回一个Promise实例")])]),_("p",[v._v("转换"),_("code",[v._v("xmind")]),v._v("数据,"),_("code",[v._v(".xmind")]),v._v("文件本质上是一个压缩包,改成"),_("code",[v._v("zip")]),v._v("后缀可以解压缩,里面存在一个"),_("code",[v._v("content.json")]),v._v("文件,如果你自己解析出了这个文件,那么可以把这个文件内容传递给这个方法进行转换,转换后的数据,可以使用"),_("code",[v._v("mindMap.setData(data)")]),v._v("来将返回的数据渲染到画布上")]),_("p",[_("code",[v._v("content")]),v._v(""),_("code",[v._v(".xmind")]),v._v("压缩包内的"),_("code",[v._v("content.json")]),v._v("文件内容")]),_("h3",[v._v("xmind.transformOldXmind(content)")]),_("blockquote",[_("p",[v._v("v0.2.8+")])]),_("p",[v._v("针对"),_("code",[v._v("xmind8")]),v._v("版本的数据解析,因为该版本的"),_("code",[v._v(".xmind")]),v._v("文件内没有"),_("code",[v._v("content.json")]),v._v(",对应的是"),_("code",[v._v("content.xml")]),v._v("。")]),_("p",[_("code",[v._v("content")]),v._v(""),_("code",[v._v(".xmind")]),v._v("压缩包内的"),_("code",[v._v("content.xml")]),v._v("文件内容")]),_("h3",[v._v("transformToXmind(data, name)")]),_("blockquote",[_("p",[v._v("v0.6.6+")])]),_("ul",[_("li",[_("p",[_("code",[v._v("data")]),v._v(""),_("code",[v._v("simple-mind-map")]),v._v("思维导图数据,可以通过"),_("code",[v._v("mindMap.getData()")]),v._v("方法获取。")])]),_("li",[_("p",[_("code",[v._v("name")]),v._v(":要导出的文件名。")])])]),_("p",[v._v("将"),_("code",[v._v("simple-mind-map")]),v._v("数据转为"),_("code",[v._v("xmind")]),v._v("文件。该方法为异步方法,返回一个"),_("code",[v._v("Promise")]),v._v("实例,返回的数据是一个"),_("code",[v._v("blob")]),v._v("类型的"),_("code",[v._v("zip")]),v._v("压缩包数据,你可以自行下载为文件。")])])}],t={},d=t,a=n("2877"),i=Object(a["a"])(d,s,e,!1,null,null,null);_["default"]=i.exports}}]);

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0e5089"],{9381:function(e,a,n){"use strict";n.r(a);var o=function(){var e=this;e._self._c;return e._m(0)},d=[function(){var e=this,a=e._self._c;return a("div",[a("h1",[e._v("command instance")]),a("p",[e._v("The "),a("code",[e._v("command")]),e._v(" instance is responsible for adding and executing commands. It includes many built-in commands and can also be added manually. A command refers to an operation that needs to add a copy to the history stack data. The "),a("code",[e._v("mindMap.command")]),e._v(' instance can be obtained through this."')]),a("h2",[e._v("Methods")]),a("h3",[e._v("add(name, fn)")]),a("p",[e._v("Add a command.")]),a("p",[a("code",[e._v("name")]),e._v(": Command name")]),a("p",[a("code",[e._v("fn")]),e._v(": Method to be executed by the command")]),a("h3",[e._v("remove(name, fn)")]),a("p",[e._v("Remove a command.")]),a("p",[a("code",[e._v("name")]),e._v(": Name of the command to be removed")]),a("p",[a("code",[e._v("fn")]),e._v(": Method to be removed, if not provided all methods for the command will be removed")]),a("h3",[e._v("getCopyData()")]),a("p",[e._v("Get a copy of the rendering tree data")]),a("h3",[e._v("clearHistory()")]),a("p",[e._v("Clear the history stack data")])])}],t={},c=t,m=n("2877"),v=Object(m["a"])(c,o,d,!1,null,null,null);a["default"]=v.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0e5089"],{9381:function(e,o,a){"use strict";a.r(o);var n=function(){var e=this;e._self._c;return e._m(0)},t=[function(){var e=this,o=e._self._c;return o("div",[o("h1",[e._v("command instance")]),o("p",[e._v("The "),o("code",[e._v("command")]),e._v(" instance is responsible for adding and executing commands. It includes many built-in commands and can also be added manually. A command refers to an operation that needs to add a copy to the history stack data. The "),o("code",[e._v("mindMap.command")]),e._v(' instance can be obtained through this."')]),o("h2",[e._v("Methods")]),o("h3",[e._v("pause()")]),o("blockquote",[o("p",[e._v("v0.9.11+")])]),o("p",[e._v("Pause collecting historical data.")]),o("h3",[e._v("recovery()")]),o("blockquote",[o("p",[e._v("v0.9.11+")])]),o("p",[e._v("Restore the collection of historical data.")]),o("h3",[e._v("add(name, fn)")]),o("p",[e._v("Add a command.")]),o("p",[o("code",[e._v("name")]),e._v(": Command name")]),o("p",[o("code",[e._v("fn")]),e._v(": Method to be executed by the command")]),o("h3",[e._v("remove(name, fn)")]),o("p",[e._v("Remove a command.")]),o("p",[o("code",[e._v("name")]),e._v(": Name of the command to be removed")]),o("p",[o("code",[e._v("fn")]),e._v(": Method to be removed, if not provided all methods for the command will be removed")]),o("h3",[e._v("getCopyData()")]),o("p",[e._v("Get a copy of the rendering tree data")]),o("h3",[e._v("clearHistory()")]),o("p",[e._v("Clear the history stack data")])])}],d={},c=d,v=a("2877"),m=Object(v["a"])(c,n,t,!1,null,null,null);o["default"]=m.exports}}]);

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

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0e96e3"],{"8e00":function(_,v,e){"use strict";e.r(v);var s=function(){var _=this;_._self._c;return _._m(0)},t=[function(){var _=this,v=_._self._c;return v("div",[v("h1",[_._v("Demonstrate 插件")]),v("blockquote",[v("p",[_._v("v0.9.11+")])]),v("p",[v("code",[_._v("Demonstrate")]),_._v("插件提供演示功能。")]),v("p",[_._v("进入演示模式时会自动将容器元素全屏,然后默认聚焦到根节点,可通过键盘方向键的左右来切换上一步和下一步,可通过"),v("code",[_._v("Esc")]),_._v("键退出演示模式。")]),v("p",[_._v("进入演示模式后思维导图所有的快捷键都将无法使用,鼠标也无法操作思维导图。")]),v("h2",[_._v("注册")]),v("pre",{staticClass:"hljs"},[v("code",[v("span",{staticClass:"hljs-keyword"},[_._v("import")]),_._v(" MindMap "),v("span",{staticClass:"hljs-keyword"},[_._v("from")]),_._v(" "),v("span",{staticClass:"hljs-string"},[_._v("'simple-mind-map'")]),_._v("\n"),v("span",{staticClass:"hljs-keyword"},[_._v("import")]),_._v(" Demonstrate "),v("span",{staticClass:"hljs-keyword"},[_._v("from")]),_._v(" "),v("span",{staticClass:"hljs-string"},[_._v("'simple-mind-map/src/plugins/Demonstrate.js'")]),_._v("\n\nMindMap.usePlugin(Demonstrate)\n")])]),v("p",[_._v("注册完且实例化"),v("code",[_._v("MindMap")]),_._v("后可通过"),v("code",[_._v("mindMap.demonstrate")]),_._v("获取到该实例。")]),v("h3",[_._v("配置")]),v("p",[_._v("该插件提供了一些配置项可供配置,可以通过实例化选项"),v("code",[_._v("demonstrateConfig")]),_._v("进行配置。详见【构造函数】篇章的【实例化选项】小节。")]),v("h3",[_._v("事件")]),v("p",[_._v("该插件会派发如下事件:")]),v("p",[v("code",[_._v("exit_demonstrate")]),_._v(":退出演示时触发。")]),v("p",[v("code",[_._v("demonstrate_jump")]),_._v(":跳转时触发。")]),v("p",[_._v("详见【构造函数】篇章的【实例方法】小节"),v("code",[_._v("on")]),_._v("函数。")]),v("h2",[_._v("属性")]),v("h3",[_._v("stepList")]),v("p",[_._v("演示的所有步骤列表。当调用了"),v("code",[_._v("enter")]),_._v("方法后可用。")]),v("h3",[_._v("currentStepIndex")]),v("p",[_._v("当前播放到的步骤索引从0开始计数。")]),v("h3",[_._v("config")]),v("p",[_._v("插件当前的配置。")]),v("h2",[_._v("方法")]),v("h3",[_._v("enter()")]),v("p",[_._v("进入演示模式,会自动将容器元素全屏。")]),v("h3",[_._v("exit()")]),v("p",[_._v("退出演示模式,通过"),v("code",[_._v("Esc")]),_._v("键也可退出。")]),v("h3",[_._v("prev()")]),v("p",[_._v("上一步。")]),v("h3",[_._v("next()")]),v("p",[_._v("下一步。")]),v("h3",[_._v("jump(index)")]),v("ul",[v("li",[v("code",[_._v("index")]),_._v("Number要跳转到的某一步从0开始计数。")])]),v("p",[_._v("跳转到某一步。")])])}],n={},p=n,a=e("2877"),o=Object(a["a"])(p,s,t,!1,null,null,null);v["default"]=o.exports}}]);

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 +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

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-2d21f249.js vendored Normal file

File diff suppressed because one or more lines are too long

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

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22a194"],{dfea:function(p,v,_){"use strict";_.r(v);var e=function(){var p=this;p._self._c;return p._m(0)},t=[function(){var p=this,v=p._self._c;return v("div",[v("h1",[p._v("局域网docker部署解决HTTPS问题的一种方法")]),v("blockquote",[v("p",[p._v("本文来自:"),v("a",{attrs:{href:"https://github.com/Brzjomo"}},[p._v("Brzjomo")]),p._v("的"),v("a",{attrs:{href:"https://github.com/wanglin2/mind-map/issues/658"}},[p._v("issue")]),p._v("。")])]),v("p",[p._v("受Api的限制MindMap以HTTP访问时目录、新建和打开功能不能正常工作。因此在局域网架设时需要给它进行配置证书等操作使其正常工作。")]),v("p",[p._v("假设先前已经基于Github源码架设了MindMap的docker服务。没有的先看这个"),v("a",{attrs:{href:"https://github.com/wanglin2/mind-map/issues/309"}},[p._v("Issue")])]),v("p",[p._v("事前准备: 需要准备一个域名。")]),v("p",[p._v("需要安装Linux 服务器运维管理面板"),v("a",{attrs:{href:"https://github.com/1Panel-dev/1Panel"}},[p._v("1panel")])]),v("p",[p._v("设置域名解析: 以阿里云为例,登录后进入"),v("a",{attrs:{href:"https://dns.console.aliyun.com/#/dns/domainList"}},[p._v("域名解析页面")])]),v("p",[p._v("点击对应域名的解析设置。")]),v("p",[p._v("添加或编辑对应的@和www记录将IP记录值修改为局域网IP比如192.168.2.36。")]),v("p",[p._v("保存后退出。")]),v("p",[p._v("获取AccessKey 进入账号下面的AccessKey管理。")]),v("p",[p._v("创建或者使用已经记录的AccessKey。")]),v("p",[p._v("1panel设置 进入应用商店安装OpenResty稍后用于申请证书和设置反代。")]),v("p",[p._v("进入网站-网站,点击创建网站。")]),v("p",[p._v("点击反向代理。")]),v("p",[p._v("设置主域名为自己的域名。")]),v("p",[p._v("代理地址为http和127.0.0.1:MindMap容器端口。")]),v("p",[p._v("点击确认。")]),v("p",[p._v("创建证书申请账户: 进入1panel的网站-证书点击Acme 账户。")]),v("p",[p._v("点击创建账户。")]),v("p",[p._v("输入邮箱后确认。")]),v("p",[p._v("回到刚才的证书页面点击DNS 账户。")]),v("p",[p._v("点击创建账户。")]),v("p",[p._v("填写名称后选择类型为阿里云DNS。")]),v("p",[p._v("再填入刚才准备好的Access Key和Secret Key。")]),v("p",[p._v("点击确认。")]),v("p",[p._v("申请证书: 回到刚才的证书页面,点击申请证书。")]),v("p",[p._v("填写主域名,其他按实际情况填写。一般会自动设置。")]),v("p",[p._v("点击确认,等待其成功。")]),v("p",[p._v("启用HTTPS访问 回到1panel的网站管理页面。")]),v("p",[p._v("找到刚才建立的反向代理,点击配置。")]),v("p",[p._v("点击HTTPS。")]),v("p",[p._v("点击启用HTTPS。")]),v("p",[p._v("SSL 选项设置为选择已有证书。")]),v("p",[p._v("选择好刚才创建的Acme账户和证书。")]),v("p",[p._v("点击保存。")]),v("p",[p._v("此时在局域网内访问该域名应当能正确以Https访问MindMap了。")]),v("p",[p._v("如果不能输入host 域名查看返回的DNS解析是否为局域网IP。")])])}],s={},n=s,a=_("2877"),c=Object(a["a"])(n,e,t,!1,null,null,null);v["default"]=c.exports}}]);

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22bd06"],{f127:function(e,t,n){"use strict";n.r(t);var s=function(){var e=this;e._self._c;return e._m(0)},a=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("XMind parse")]),t("blockquote",[t("p",[e._v("v0.2.7+")])]),t("p",[e._v("Provides methods for importing and export "),t("code",[e._v("XMind")]),e._v(" files.")]),t("h2",[e._v("Import")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-keyword"},[e._v("import")]),e._v(" xmind "),t("span",{staticClass:"hljs-keyword"},[e._v("from")]),e._v(" "),t("span",{staticClass:"hljs-string"},[e._v("'simple-mind-map/src/parse/xmind.js'")]),e._v("\n")])]),t("p",[e._v("If you are using the file in the format of "),t("code",[e._v("umd")]),e._v(", you can obtain it in the following way:")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-tag"},[e._v("<"),t("span",{staticClass:"hljs-name"},[e._v("script")]),e._v(" "),t("span",{staticClass:"hljs-attr"},[e._v("src")]),e._v("="),t("span",{staticClass:"hljs-string"},[e._v('"simple-mind-map/dist/simpleMindMap.umd.min.js"')]),e._v(">")]),t("span",{staticClass:"hljs-tag"},[e._v("</"),t("span",{staticClass:"hljs-name"},[e._v("script")]),e._v(">")]),e._v("\n")])]),t("pre",{staticClass:"hljs"},[t("code",[e._v("simpleMindMap.xmind\n")])]),t("h2",[e._v("Methods")]),t("h3",[e._v("xmind.parseXmindFile(file)")]),t("p",[e._v("Parsing the "),t("code",[e._v(".xmind")]),e._v(" file and returning the parsed data. You can use "),t("code",[e._v("mindMap.setData(data)")]),e._v(" to render the returned data to the canvas.")]),t("p",[t("code",[e._v("file")]),e._v(": "),t("code",[e._v("File")]),e._v(" object")]),t("h3",[e._v("xmind.transformXmind(content)")]),t("blockquote",[t("p",[e._v("V0.6.6+version changes the method to asynchronous and returns a Promise instance")])]),t("p",[e._v("Convert "),t("code",[e._v("xmind")]),e._v(" data. The "),t("code",[e._v(".xmind")]),e._v(" file is essentially a "),t("code",[e._v("zip")]),e._v(" file that can be decompressed by changing the suffix to zip. Inside, there is a "),t("code",[e._v("content.json")]),e._v(" file. If you have parsed this file yourself, you can pass the contents of this file to this method for conversion. You can use "),t("code",[e._v("mindMap.setData(data)")]),e._v(" to render the returned data to the canvas.")]),t("p",[t("code",[e._v("content")]),e._v(": the contents of the "),t("code",[e._v("content.json")]),e._v(" file within the "),t("code",[e._v(".xmind")]),e._v(" zip package")]),t("h3",[e._v("xmind.transformOldXmind(content)")]),t("blockquote",[t("p",[e._v("v0.2.8+")])]),t("p",[e._v("For data parsing of the "),t("code",[e._v("xmind8")]),e._v(" version, because the "),t("code",[e._v(".xmind")]),e._v(" file in this version does not have a "),t("code",[e._v("content.json")]),e._v(", it corresponds to "),t("code",[e._v("content.xml")]),e._v(".")]),t("p",[t("code",[e._v("content")]),e._v(": the contents of the "),t("code",[e._v("content.xml")]),e._v(" file within the "),t("code",[e._v(".xmind")]),e._v(" zip package")]),t("h3",[e._v("transformToXmind(data, name)")]),t("blockquote",[t("p",[e._v("v0.6.6+")])]),t("ul",[t("li",[t("p",[t("code",[e._v("data")]),e._v(": "),t("code",[e._v("simple-mind-map")]),e._v(" data, you can get it by "),t("code",[e._v("mindMap.getData()")]),e._v(" method.")])]),t("li",[t("p",[t("code",[e._v("name")]),e._v(": The file name to export.")])])]),t("p",[e._v("Convert the "),t("code",[e._v("simple mind map")]),e._v(" data to an "),t("code",[e._v("xmind")]),e._v(" file. This method is asynchronous and returns an instance of "),t("code",[e._v("Promise")]),e._v(". The returned data is a "),t("code",[e._v("blob")]),e._v(" type "),t("code",[e._v("zip")]),e._v(" compressed package data, which you can download as a file yourself.")])])}],o={},i=o,d=n("2877"),v=Object(d["a"])(i,s,a,!1,null,null,null);t["default"]=v.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22bd06"],{f127:function(e,t,n){"use strict";n.r(t);var a=function(){var e=this;e._self._c;return e._m(0)},s=[function(){var e=this,t=e._self._c;return t("div",[t("h1",[e._v("XMind parse")]),t("blockquote",[t("p",[e._v("v0.2.7+")])]),t("p",[e._v("Provides methods for importing and export "),t("code",[e._v("XMind")]),e._v(" files.")]),t("h2",[e._v("Import")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-keyword"},[e._v("import")]),e._v(" xmind "),t("span",{staticClass:"hljs-keyword"},[e._v("from")]),e._v(" "),t("span",{staticClass:"hljs-string"},[e._v("'simple-mind-map/src/parse/xmind.js'")]),e._v("\n")])]),t("p",[e._v("If you are using the file in the format of "),t("code",[e._v("umd")]),e._v(", you can obtain it in the following way:")]),t("pre",{staticClass:"hljs"},[t("code",[t("span",{staticClass:"hljs-tag"},[e._v("<"),t("span",{staticClass:"hljs-name"},[e._v("script")]),e._v(" "),t("span",{staticClass:"hljs-attr"},[e._v("src")]),e._v("="),t("span",{staticClass:"hljs-string"},[e._v('"simple-mind-map/dist/simpleMindMap.umd.min.js"')]),e._v(">")]),t("span",{staticClass:"hljs-tag"},[e._v("</"),t("span",{staticClass:"hljs-name"},[e._v("script")]),e._v(">")]),e._v("\n")])]),t("pre",{staticClass:"hljs"},[t("code",[e._v("simpleMindMap.xmind\n")])]),t("h2",[e._v("Methods")]),t("h3",[e._v("xmind.parseXmindFile(file, handleMultiCanvas)")]),t("p",[e._v("Parsing the "),t("code",[e._v(".xmind")]),e._v(" file and returning the parsed data. You can use "),t("code",[e._v("mindMap.setData(data)")]),e._v(" to render the returned data to the canvas.")]),t("p",[t("code",[e._v("file")]),e._v(": "),t("code",[e._v("File")]),e._v(" object")]),t("p",[t("code",[e._v("handleMultiCanvas")]),e._v("v0.10.0+Optional, a function can be passed. If there are multiple canvases in the imported xmind file, this function will be called. The function takes the xmind canvas list data as a parameter and needs to return the data of one of the canvases, For example, if the received parameter is 'content', if you want to import data from the second canvas, you will return 'content[1]'. A function can be an asynchronous function that returns a Promise instance.")]),t("h3",[e._v("xmind.transformXmind(content)")]),t("blockquote",[t("p",[e._v("V0.6.6+version changes the method to asynchronous and returns a Promise instance")])]),t("p",[e._v("Convert "),t("code",[e._v("xmind")]),e._v(" data. The "),t("code",[e._v(".xmind")]),e._v(" file is essentially a "),t("code",[e._v("zip")]),e._v(" file that can be decompressed by changing the suffix to zip. Inside, there is a "),t("code",[e._v("content.json")]),e._v(" file. If you have parsed this file yourself, you can pass the contents of this file to this method for conversion. You can use "),t("code",[e._v("mindMap.setData(data)")]),e._v(" to render the returned data to the canvas.")]),t("p",[t("code",[e._v("content")]),e._v(": the contents of the "),t("code",[e._v("content.json")]),e._v(" file within the "),t("code",[e._v(".xmind")]),e._v(" zip package")]),t("h3",[e._v("xmind.transformOldXmind(content)")]),t("blockquote",[t("p",[e._v("v0.2.8+")])]),t("p",[e._v("For data parsing of the "),t("code",[e._v("xmind8")]),e._v(" version, because the "),t("code",[e._v(".xmind")]),e._v(" file in this version does not have a "),t("code",[e._v("content.json")]),e._v(", it corresponds to "),t("code",[e._v("content.xml")]),e._v(".")]),t("p",[t("code",[e._v("content")]),e._v(": the contents of the "),t("code",[e._v("content.xml")]),e._v(" file within the "),t("code",[e._v(".xmind")]),e._v(" zip package")]),t("h3",[e._v("transformToXmind(data, name)")]),t("blockquote",[t("p",[e._v("v0.6.6+")])]),t("ul",[t("li",[t("p",[t("code",[e._v("data")]),e._v(": "),t("code",[e._v("simple-mind-map")]),e._v(" data, you can get it by "),t("code",[e._v("mindMap.getData()")]),e._v(" method.")])]),t("li",[t("p",[t("code",[e._v("name")]),e._v(": The file name to export.")])])]),t("p",[e._v("Convert the "),t("code",[e._v("simple mind map")]),e._v(" data to an "),t("code",[e._v("xmind")]),e._v(" file. This method is asynchronous and returns an instance of "),t("code",[e._v("Promise")]),e._v(". The returned data is a "),t("code",[e._v("blob")]),e._v(" type "),t("code",[e._v("zip")]),e._v(" compressed package data, which you can download as a file yourself.")])])}],o={},i=o,d=n("2877"),v=Object(d["a"])(i,a,s,!1,null,null,null);t["default"]=v.exports}}]);

File diff suppressed because one or more lines are too long

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

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22dd95"],{f8f3:function(n,t,u){"use strict";u.r(t);var _=function(){var n=this;n._self._c;return n._m(0)},c=[function(){var n=this,t=n._self._c;return t("div",[t("h1",[n._v("如何编辑数学公式")]),t("p",[n._v("数学公式只在开启了【富文本】编辑模式下才可使用。")]),t("p",[n._v("首先可以激活节点,然后点击上方工具栏中的【公式】打开右侧的格式侧边栏,然后再输入框中输入公式后点击【完成】即可将公式输入节点。")]),t("p",[n._v("当你再次双击节点时,公式会转换成源码,你可以直接修改,回车完成后即可渲染。")]),t("p",[n._v("所以你也可以不通过侧边栏,直接在文本编辑框中输入公式,不过公式的源码前后必须通过"),t("code",[n._v("$")]),n._v("符号包裹,否则不会解析为格式。")])])}],e={},v=e,p=u("2877"),r=Object(p["a"])(v,_,c,!1,null,null,null);t["default"]=r.exports}}]);

1
dist/js/chunk-337a3983.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

1
dist/js/chunk-63012c17.js vendored Normal file

File diff suppressed because one or more lines are too long

76
dist/js/chunk-67f34430.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

1
dist/js/chunk-bb91a162.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

View File

@@ -1,24 +1,28 @@
<!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?e76cdbdcf61c14ff4fc9" rel="stylesheet"><link href="dist/css/app.css?e76cdbdcf61c14ff4fc9" 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><script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script><script>LA.init({
id: 'KRO0WxK8GT66tYCQ',
ck: 'KRO0WxK8GT66tYCQ',
autoTrack: false
})</script><link href="dist/css/chunk-vendors.css?b34a2f0278e7d1ccab6a" rel="stylesheet"><link href="dist/css/app.css?b34a2f0278e7d1ccab6a" 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({
mindMapData: {
root:{
"data": {
"text": "根节点"
root: {
data: {
text: '根节点'
},
"children": []
children: []
},
theme:{
"template":"avocado",
"config":{}
theme: {
template: 'avocado',
config: {}
},
layout:"logicalStructure",
layout: 'logicalStructure',
config: {},
view: null,
view: null
},
lang: 'zh',
localConfig: null
@@ -26,14 +30,14 @@
}, 200)
})
}
const setTakeOverAppMethods = (data) => {
const setTakeOverAppMethods = data => {
window.takeOverAppMethods = {}
// 获取思维导图数据的函数
window.takeOverAppMethods.getMindMapData = () => {
return data.mindMapData
}
}
// 保存思维导图数据的函数
window.takeOverAppMethods.saveMindMapData = (data) => {
window.takeOverAppMethods.saveMindMapData = data => {
console.log(data)
}
// 获取语言的函数
@@ -41,7 +45,7 @@
return data.lang
}
// 保存语言的函数
window.takeOverAppMethods.saveLanguage = (lang) => {
window.takeOverAppMethods.saveLanguage = lang => {
console.log(lang)
}
// 获取本地配置的函数
@@ -49,7 +53,7 @@
return data.localConfig
}
// 保存本地配置的函数
window.takeOverAppMethods.saveLocalConfig = (config) => {
window.takeOverAppMethods.saveLocalConfig = config => {
console.log(config)
}
}
@@ -60,10 +64,10 @@
// 设置全局的方法
setTakeOverAppMethods(data)
// 思维导图实例创建完成事件
window.$bus.$on('app_inited', (mindMap) => {
window.$bus.$on('app_inited', mindMap => {
console.log(mindMap)
})
// 可以通过window.$bus.$on()来监听应用的一些事件
// 实例化页面
window.initApp()
}</script><script src="dist/js/chunk-vendors.js?e76cdbdcf61c14ff4fc9"></script><script src="dist/js/app.js?e76cdbdcf61c14ff4fc9"></script></body></html>
}</script><script src="dist/js/chunk-vendors.js?b34a2f0278e7d1ccab6a"></script><script src="dist/js/app.js?b34a2f0278e7d1ccab6a"></script></body></html>

30
nginx.conf Normal file
View File

@@ -0,0 +1,30 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

View File

@@ -15,6 +15,8 @@ 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 Demonstrate from './src/plugins/Demonstrate.js'
import xmind from './src/parse/xmind.js'
import markdown from './src/parse/markdown.js'
import icons from './src/svg/icons.js'
@@ -28,7 +30,7 @@ MindMap.iconList = icons.nodeIconList
MindMap.constants = constants
MindMap.themes = themes
MindMap.defaultTheme = defaultTheme
MindMap.version = '0.9.7'
MindMap.version = '0.10.0'
MindMap.usePlugin(MiniMap)
.usePlugin(Watermark)
@@ -46,5 +48,7 @@ MindMap.usePlugin(MiniMap)
.usePlugin(Painter)
.usePlugin(Scrollbar)
.usePlugin(Formula)
.usePlugin(RainbowLines)
.usePlugin(Demonstrate)
export default MindMap

View File

@@ -10,12 +10,17 @@ import BatchExecution from './src/utils/BatchExecution'
import {
layoutValueList,
CONSTANTS,
commonCaches,
ERROR_TYPES,
cssContent
} from './src/constants/constant'
import { SVG } from '@svgdotjs/svg.js'
import { simpleDeepClone, getType, getObjectChangedProps } from './src/utils'
import {
simpleDeepClone,
getObjectChangedProps,
isUndef,
handleGetSvgDataExtraContent,
getNodeTreeBoundingRect
} from './src/utils'
import defaultTheme, {
checkIsNodeSizeIndependenceConfig
} from './src/themes/default'
@@ -31,6 +36,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 +46,10 @@ class MindMap {
// 获取容器尺寸位置信息
this.getElRectInfo()
// 画布初始大小
this.initWidth = this.width
this.initHeight = this.height
// 添加css
this.cssEl = null
this.addCss()
@@ -88,14 +99,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,9 +114,22 @@ 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
// 给容器元素添加一个类名
this.el.classList.add('smm-mind-map-container')
// 节点关联线容器
const createAssociativeLineDraw = () => {
this.associativeLineDraw = this.draw.group()
@@ -115,6 +137,7 @@ class MindMap {
}
// 画布
this.svg = SVG().addTo(this.el).size(this.width, this.height)
// 容器
this.draw = this.svg.group()
this.draw.addClass('smm-container')
@@ -207,19 +230,10 @@ class MindMap {
// 初始化缓存数据
initCache() {
Object.keys(commonCaches).forEach(key => {
let type = getType(commonCaches[key])
let value = ''
switch (type) {
case 'Boolean':
value = false
break
default:
value = null
break
}
commonCaches[key] = value
})
this.commonCaches = {
measureCustomNodeContentSizeEl: null,
measureRichtextNodeTextSizeEl: null
}
}
// 设置主题
@@ -302,9 +316,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 +408,19 @@ class MindMap {
}
// 获取svg数据
getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false } = {}) {
getSvgData({
paddingX = 0,
paddingY = 0,
ignoreWatermark = false,
addContentToHeader,
addContentToFooter,
node
} = {}) {
const { cssTextList, header, headerHeight, footer, footerHeight } =
handleGetSvgDataExtraContent({
addContentToHeader,
addContentToFooter
})
const svg = this.svg
const draw = this.draw
// 保存原始信息
@@ -398,9 +432,21 @@ class MindMap {
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
// 获取变换后的位置尺寸信息其实是getBoundingClientRect方法的包装方法
const rect = draw.rbox()
// 需要裁减的区域
let clipData = null
if (node) {
clipData = getNodeTreeBoundingRect(
node,
rect.x,
rect.y,
paddingX,
paddingY
)
}
// 内边距
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 +484,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')
@@ -462,6 +522,7 @@ class MindMap {
return {
svg: clone, // 思维导图图形的整体svg元素包括svg画布容器、g实际的思维导图组
svgHTML: clone.svg(), // svg字符串
clipData,
rect: {
...rect, // 思维导图图形未缩放时的位置尺寸等信息
ratio: rect.width / rect.height // 思维导图图形的宽高比
@@ -529,6 +590,8 @@ class MindMap {
this.svg.remove()
// 去除给容器元素设置的背景样式
Style.removeBackgroundStyle(this.el)
// 移除给容器元素添加的类名
this.el.classList.remove('smm-mind-map-container')
this.el.innerHTML = ''
this.el = null
this.removeCss()

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.9.7",
"version": "0.10.0",
"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,15 +312,11 @@ export const nodeDataNoStylePropList = [
'associativeLineTargets',
'associativeLineTargetControlOffsets',
'associativeLinePoint',
'associativeLineText'
'associativeLineText',
'attachmentUrl',
'attachmentName'
]
// 数据缓存
export const commonCaches = {
measureCustomNodeContentSizeEl: null,
measureRichtextNodeTextSizeEl: null
}
// 错误类型
export const ERROR_TYPES = {
READ_CLIPBOARD_ERROR: 'read_clipboard_error',
@@ -346,7 +342,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

@@ -2,6 +2,11 @@ import { CONSTANTS } from './constant'
// 默认选项配置
export const defaultOpt = {
// 【基本】
// 容器元素必传必须为DOM元素
el: null,
// 思维导图回显数据
data: null,
// 是否只读
readonly: false,
// 布局
@@ -24,10 +29,6 @@ export const defaultOpt = {
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2,
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null,
/*
@@ -36,21 +37,6 @@ export const defaultOpt = {
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
onlyExport: false, // 是否仅在导出时添加水印
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
},
// 达到该宽度文本自动换行
textAutoWrapWidth: 500,
// 自定义鼠标滚轮事件处理
@@ -88,9 +74,6 @@ export const defaultOpt = {
enableShortcutOnlyWhenMouseInSvg: true,
// 初始根节点的位置
initRootNodePosition: null,
// 导出png、svg、pdf时的图形内边距注意是单侧内边距
exportPaddingX: 10,
exportPaddingY: 10,
// 节点文本编辑框的z-index
nodeTextEditZIndex: 3000,
// 节点备注浮层的z-index
@@ -116,8 +99,6 @@ export const defaultOpt = {
],
// 节点最大缓存数量
maxNodeCacheCount: 1000,
// 关联线默认文字
defaultAssociativeLineText: '关联',
// 思维导图适应画布大小时的内边距
fitPadding: 50,
// 是否开启按住ctrl键多选节点功能
@@ -132,14 +113,9 @@ export const defaultOpt = {
customCreateNodeContent: null,
// 指定内部一些元素节点文本编辑元素、节点备注显示元素、关联线文本编辑元素、节点图片调整按钮元素添加到的位置默认添加到document.body下
customInnerElsAppendTo: null,
// 拖拽元素时,指示元素新位置的块的最大高度
nodeDragPlaceholderMaxSize: 20,
// 是否在存在一个激活节点时,当按下中文、英文、数字按键时自动进入文本编辑模式
// 开启该特性后需要给你的输入框绑定keydown事件并禁止冒泡
enableAutoEnterTextEditWhenKeydown: false,
// 设置富文本节点编辑框和节点大小一致,形成伪原地编辑的效果
// 需要注意的是,只有当节点内只有文本、且形状是矩形才会有比较好的效果
richTextEditFakeInPlace: false,
// 自定义对剪贴板文本的处理。当按ctrl+v粘贴时会读取用户剪贴板中的文本和图片默认只会判断文本是否是普通文本和simple-mind-map格式的节点数据如果你想处理其他思维导图的数据比如processon、zhixi等那么可以传递一个函数接受当前剪贴板中的文本为参数返回处理后的数据可以返回两种类型
/*
1.返回一个纯文本,那么会直接以该文本创建一个子节点
@@ -161,26 +137,12 @@ export const defaultOpt = {
customHandleClipboardText: null,
// 禁止鼠标滚轮缩放你仍旧可以使用api进行缩放
disableMouseWheelZoom: false,
// 禁止双指缩放你仍旧可以使用api进行缩放
// 需要注册TouchEvent插件后生效
disableTouchZoom: false,
// 错误处理函数
errorHandler: (code, error) => {
console.error(code, error)
},
// 设置导出图片和svg时针对富文本节点内容也就是嵌入到svg中的html节点的默认样式覆盖
// 如果不覆盖,会发生偏移问题
resetCss: `
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
`,
// 是否在鼠标双击时回到根节点,也就是让根节点居中显示
enableDblclickBackToRootNode: false,
// 导出图片时canvas的缩放倍数该配置会和window.devicePixelRatio值取最大值
minExportImgCanvasScale: 2,
// 节点鼠标hover和激活时显示的矩形边框的颜色
hoverRectColor: 'rgb(94, 200, 248)',
// 节点鼠标hover和激活时显示的矩形边框距节点内容的距离
@@ -189,23 +151,8 @@ export const defaultOpt = {
selectTextOnEnterEditText: false,
// 删除节点后激活相邻节点
deleteNodeActive: true,
// 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动
autoMoveWhenMouseInEdgeOnDrag: true,
// 是否首次加载fit view
fit: false,
// 拖拽多个节点时随鼠标移动的示意矩形的样式配置
dragMultiNodeRectConfig: {
width: 40,
height: 20,
fill: '' // 填充颜色,如果不传默认使用连线的颜色
},
// 节点拖拽时新位置的示意矩形的填充颜色,如果不传默认使用连线的颜色
dragPlaceholderRectFill: '',
// 节点拖拽时的透明度配置
dragOpacityConfig: {
cloneNodeOpacity: 0.5, // 跟随鼠标移动的克隆节点或矩形的透明度
beingDragNodeOpacity: 0.3 // 被拖拽节点的透明度
},
// 自定义标签的颜色
// {pass: 'green, unpass: 'red'}
tagsColorMap: {},
@@ -214,9 +161,8 @@ export const defaultOpt = {
avatarSize: 22, // 头像大小
fontSize: 12 // 如果是文字头像,那么文字的大小
},
// 关联线是否始终显示在节点上层
// false即创建关联线和激活关联线时处于最顶层其他情况下处于节点下方
associativeLineIsAlwaysAboveNode: true,
// 协同编辑时,同一个节点不能同时被多人选中
onlyOneEnableActiveNodeOnCooperate: false,
// 插入概要的默认文本
defaultGeneralizationText: '概要',
// 粘贴文本的方式创建新节点时,控制是否按换行自动分割节点,即如果存在换行,那么会根据换行创建多个节点,否则只会创建一个节点
@@ -243,8 +189,6 @@ export const defaultOpt = {
// 是否将思维导图限制在画布内
// 比如向右拖动时,思维导图图形的最左侧到达画布中心时将无法继续向右拖动,其他同理
isLimitMindMapInCanvas: false,
// 当注册了滚动条插件Scrollbar是否将思维导图限制在画布内isLimitMindMapInCanvas不再起作用
isLimitMindMapInCanvasWhenHasScrollbar: true,
// 在节点上粘贴剪贴板中的图片的处理方法默认是转换为data:url数据插入到节点中你可以通过该方法来将图片数据上传到服务器实现保存图片的url
// 可以传递一个异步方法接收Blob类型的图片数据需要返回如下结构
/*
@@ -257,15 +201,6 @@ export const defaultOpt = {
}
*/
handleNodePasteImg: null,
// 默认情况下,新创建的关联线两个端点的位置是根据两个节点中心点的相对位置来计算的,如果你想固定位置,可以通过这个属性来配置
// from和to都不传则都自动计算如果只传一个另一个则会自动计算
associativeLineInitPointsPosition: {
// from和to可选值left、top、bottom、right
from: '', // 关联线起始节点上端点的位置
to: '' // 关联线目标节点上端点的位置
},
// 是否允许调整关联线两个端点的位置
enableAdjustAssociativeLinePoints: true,
// 自定义创建节点形状的方法,可以传一个函数,均接收一个参数
// 矩形、圆角矩形、椭圆、圆等形状会调用该方法
// 接收svg path字符串返回svg节点
@@ -275,5 +210,162 @@ export const defaultOpt = {
customCreateNodePolygon: null,
// 自定义转换节点连线路径的方法
// 接收svg path字符串返回转换后的svg path字符串
customTransformNodeLinePath: null
customTransformNodeLinePath: null,
// 快捷键操作即将执行前的生命周期函数返回true可以阻止操作执行
// 函数接收两个参数key快捷键、activeNodeList当前激活的节点列表
beforeShortcutRun: null,
// 移动节点到画布中心、回到根节点等操作时是否将缩放层级复位为100%
resetScaleOnMoveNodeToCenter: false,
// 添加附加的节点前置内容,前置内容指和文本同一行的区域中的前置内容,不包括节点图片部分
createNodePrefixContent: null,
// 添加附加的节点后置内容,后置内容指和文本同一行的区域中的后置内容,不包括节点图片部分
createNodePostfixContent: null,
// 【Select插件】
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 【Drag插件】
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动
autoMoveWhenMouseInEdgeOnDrag: true,
// 拖拽多个节点时随鼠标移动的示意矩形的样式配置
dragMultiNodeRectConfig: {
width: 40,
height: 20,
fill: 'rgb(94, 200, 248)' // 填充颜色
},
// 节点拖拽时新位置的示意矩形的填充颜色
dragPlaceholderRectFill: 'rgb(94, 200, 248)',
// 节点拖拽时新位置的示意连线的样式配置
dragPlaceholderLineConfig: {
color: 'rgb(94, 200, 248)',
width: 2
},
// 节点拖拽时的透明度配置
dragOpacityConfig: {
cloneNodeOpacity: 0.5, // 跟随鼠标移动的克隆节点或矩形的透明度
beingDragNodeOpacity: 0.3 // 被拖拽节点的透明度
},
// 【Watermark插件】
// 水印配置
watermarkConfig: {
onlyExport: false, // 是否仅在导出时添加水印
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
},
belowNode: false
},
// 【Export插件】
// 导出png、svg、pdf时的图形内边距注意是单侧内边距
exportPaddingX: 10,
exportPaddingY: 10,
// 设置导出图片和svg时针对富文本节点内容也就是嵌入到svg中的html节点的默认样式覆盖
// 如果不覆盖,会发生偏移问题
resetCss: `
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
`,
// 导出图片时canvas的缩放倍数该配置会和window.devicePixelRatio值取最大值
minExportImgCanvasScale: 2,
// 导出png、svg、pdf时在头部和尾部添加自定义内容
// 可传递一个函数这个函数可以返回null代表不添加内容也可以返回如下数据
/*
{
el,// 要追加的自定义DOM节点样式可内联
cssText,// 可选如果样式不想内联可以传递该值一个css字符串
height: 50// 返回的DOM节点的高度必须传递
}
*/
addContentToHeader: null,
addContentToFooter: null,
// 【AssociativeLine插件】
// 关联线默认文字
defaultAssociativeLineText: '关联',
// 关联线是否始终显示在节点上层
// false即创建关联线和激活关联线时处于最顶层其他情况下处于节点下方
associativeLineIsAlwaysAboveNode: true,
// 默认情况下,新创建的关联线两个端点的位置是根据两个节点中心点的相对位置来计算的,如果你想固定位置,可以通过这个属性来配置
// from和to都不传则都自动计算如果只传一个另一个则会自动计算
associativeLineInitPointsPosition: {
// from和to可选值left、top、bottom、right
from: '', // 关联线起始节点上端点的位置
to: '' // 关联线目标节点上端点的位置
},
// 是否允许调整关联线两个端点的位置
enableAdjustAssociativeLinePoints: true,
// 【TouchEvent插件】
// 禁止双指缩放你仍旧可以使用api进行缩放
// 需要注册TouchEvent插件后生效
disableTouchZoom: false,
// 【Scrollbar插件】
// 当注册了滚动条插件Scrollbar是否将思维导图限制在画布内isLimitMindMapInCanvas不再起作用
isLimitMindMapInCanvasWhenHasScrollbar: true,
// 【Search插件】
// 是否仅搜索当前渲染的节点,被收起的节点不会被搜索到
isOnlySearchCurrentRenderNodes: false,
// 【Cooperate插件】
// 协同编辑时,节点操作即将更新到其他客户端前的生命周期函数
// 函数接收一个对象作为参数:
/*
{
type: createOrUpdate创建节点或更新节点、delete删除节点
data: 1.当type=createOrUpdate时代表被创建或被更新的节点数据即将同步到其他客户端所以你可以修改该数据2.当type=delete时代表被删除的节点数据
}
*/
beforeCooperateUpdate: null,
// 【RainbowLines插件】
// 彩虹线条配置需要先注册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)'
]
*/
},
// 【Demonstrate插件】
// 演示插件配置
demonstrateConfig: null,
// 【Formula插件】
// 是否开启在富文本编辑框中直接编辑数学公式
enableEditFormulaInRichTextEdit: true,
// 【RichText插件】
// 转换富文本内容,当进入富文本编辑时,可以通过该参数传递一个函数,函数接收文本内容,需要返回你处理后的文本内容
transformRichTextOnEnterEdit: null,
// 可以传递一个函数即将结束富文本编辑前会执行该函数函数接收richText实例所以你可以在此时机更新quill文档数据
beforeHideRichTextEdit: null,
// 设置富文本节点编辑框和节点大小一致,形成伪原地编辑的效果
// 需要注意的是,只有当节点内只有文本、且形状是矩形才会有比较好的效果
richTextEditFakeInPlace: false,
}

View File

@@ -23,6 +23,18 @@ class Command {
this.mindMap.opt.addHistoryTime,
this
)
// 是否暂停收集历史数据
this.isPause = false
}
// 暂停收集历史数据
pause() {
this.isPause = true
}
// 恢复收集历史数据
recovery() {
this.isPause = false
}
// 清空历史数据
@@ -88,13 +100,14 @@ class Command {
// 添加回退数据
addHistory() {
if (this.mindMap.opt.readonly) {
if (this.mindMap.opt.readonly || this.isPause) {
return
}
const lastData =
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 +171,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

@@ -30,7 +30,8 @@ import {
createSmmFormatData,
checkSmmFormatData,
checkIsNodeStyleDataKey,
removeRichTextStyes
removeRichTextStyes,
formatGetNodeGeneralization
} from '../../utils'
import { shapeList } from './node/Shape'
import { lineStyleProps } from '../../themes/default'
@@ -65,7 +66,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
// 是否正在渲染中
@@ -117,7 +120,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
}
@@ -246,6 +249,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)
@@ -290,6 +296,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')
@@ -326,7 +336,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', () => {
@@ -432,6 +442,13 @@ class Render {
if (this.reRender) {
this.clearActiveNodeList()
}
// 如果没有节点数据
if (!this.renderTree) {
this.isRendering = false
this.mindMap.emit('node_tree_render_end')
return
}
this.mindMap.emit('node_tree_render_start')
// 计算布局
this.layout.doLayout(root => {
// 删除本次渲染时不再需要的节点
@@ -476,6 +493,7 @@ class Render {
// 给当前被收起来的节点数据添加文本复位标志
resetUnExpandNodeStyle() {
if (!this.renderTree) return
walk(this.renderTree, null, node => {
if (!node.data.expand) {
walk(node, null, node2 => {
@@ -504,9 +522,17 @@ class Render {
}
// 添加节点到激活列表里
addNodeToActiveList(node) {
addNodeToActiveList(node, notEmitBeforeNodeActiveEvent = false) {
if (
this.mindMap.opt.onlyOneEnableActiveNodeOnCooperate &&
node.userList.length > 0
)
return
const index = this.findActiveNodeIndex(node)
if (index === -1) {
if (!notEmitBeforeNodeActiveEvent) {
this.mindMap.emit('before_node_active', node, this.activeNodeList)
}
this.mindMap.execCommand('SET_NODE_ACTIVE', node, true)
this.activeNodeList.push(node)
}
@@ -649,7 +675,7 @@ class Render {
uid: createUid(),
...(appointData || {})
},
children: [...createUidForAppointNodes(appointChildren, true)]
children: [...createUidForAppointNodes(appointChildren)]
}
parent.nodeData.children.splice(index + 1, 0, newNodeData)
})
@@ -685,10 +711,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) {
@@ -748,7 +771,7 @@ class Render {
...params,
...(appointData || {})
},
children: [...createUidForAppointNodes(appointChildren, true)]
children: [...createUidForAppointNodes(appointChildren)]
}
node.nodeData.children.push(newNode)
// 插入子节点时自动展开子节点
@@ -788,7 +811,7 @@ class Render {
if (!node.nodeData.children) {
node.nodeData.children = []
}
childList = createUidForAppointNodes(childList, true)
childList = createUidForAppointNodes(childList)
node.nodeData.children.push(...childList)
// 插入子节点时自动展开子节点
node.setData({
@@ -843,6 +866,9 @@ class Render {
},
children: [node.nodeData]
}
node.setData({
resetRichText: true
})
const parent = node.parent
// 获取当前节点所在位置
const index = getNodeDataIndex(node)
@@ -963,12 +989,14 @@ class Render {
})
} 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 generalizationList = formatGetNodeGeneralization(node.data)
if (generalizationList.length > 0) {
generalizationList.forEach(generalizationData => {
const _hasCustomStyles =
this._handleRemoveCustomStyles(generalizationData)
if (_hasCustomStyles) hasCustomStyles = true
@@ -1214,9 +1242,17 @@ class Render {
root.nodeData.children = []
} else {
// 如果只选中了一个节点,删除后激活其兄弟节点或者父节点
needActiveNode = this.getNextActiveNode()
needActiveNode = this.getNextActiveNode(list)
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)
@@ -1261,13 +1297,13 @@ class Render {
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
return
}
// 删除节点后需要激活的节点,如果只选中了一个节点,删除后激活其兄弟节点或者父节点
let needActiveNode = this.getNextActiveNode()
let isAppointNodes = appointNodes.length > 0
let list = isAppointNodes ? appointNodes : this.activeNodeList
list = list.filter(node => {
return !node.isRoot
})
// 删除节点后需要激活的节点,如果只选中了一个节点,删除后激活其兄弟节点或者父节点
let needActiveNode = this.getNextActiveNode(list)
for (let i = 0; i < list.length; i++) {
let node = list[i]
if (node.isGeneralization) {
@@ -1293,7 +1329,11 @@ class Render {
}
// 计算下一个可激活的节点
getNextActiveNode() {
getNextActiveNode(deleteList) {
// 删除多个节点不自动激活相邻节点
if (deleteList.length !== 1) return null
// 被删除的节点不在当前激活的节点列表里,不激活相邻节点
if (this.findActiveNodeIndex(deleteList[0]) === -1) return null
let needActiveNode = null
if (
this.activeNodeList.length === 1 &&
@@ -1448,6 +1488,7 @@ class Render {
// 展开所有
expandAllNode() {
if (!this.renderTree) return
walk(
this.renderTree,
null,
@@ -1465,7 +1506,8 @@ class Render {
}
// 收起所有
unexpandAllNode() {
unexpandAllNode(isSetRootNodeCenter = true) {
if (!this.renderTree) return
walk(
this.renderTree,
null,
@@ -1480,12 +1522,15 @@ class Render {
0
)
this.mindMap.render(() => {
this.setRootNodeCenter()
if (isSetRootNodeCenter) {
this.setRootNodeCenter()
}
})
}
// 展开到指定层级
expandToLevel(level) {
if (!this.renderTree) return
walk(
this.renderTree,
null,
@@ -1572,6 +1617,14 @@ class Render {
})
}
// 设置节点附件
setNodeAttachment(node, url, name = '') {
this.setNodeDataRender(node, {
attachmentUrl: url,
attachmentName: name
})
}
// 设置节点标签
setNodeTag(node, tag) {
this.setNodeDataRender(node, {
@@ -1591,7 +1644,7 @@ class Render {
}
// 添加节点概要
addGeneralization(data) {
addGeneralization(data, openEdit = true) {
if (this.activeNodeList.length <= 0) {
return
}
@@ -1603,12 +1656,22 @@ class Render {
)
})
const list = parseAddGeneralizationNodeList(nodeList)
const isRichText = !!this.mindMap.richText
const { focusNewNode, inserting } = this.getNewNodeBehavior(
openEdit,
list.length > 1
)
list.forEach(item => {
const newData = {
inserting,
...(data || {
text: this.mindMap.opt.defaultGeneralizationText
}),
range: item.range || null
range: item.range || null,
uid: createUid(),
richText: isRichText,
resetRichText: isRichText,
isActive: focusNewNode
}
let generalization = item.node.getData('generalization')
if (generalization) {
@@ -1628,6 +1691,10 @@ class Render {
expand: true
})
})
// 需要清除原来激活的节点
if (focusNewNode) {
this.clearActiveNodeList()
}
this.mindMap.render(() => {
// 修复祖先节点存在概要时位置未更新的问题
// 修复同时给存在上下级关系的节点添加概要时重叠的问题
@@ -1704,7 +1771,7 @@ class Render {
if (targetNode) {
targetNode.active()
this.moveNodeToCenter(targetNode)
callback()
callback(targetNode)
}
})
}
@@ -1734,19 +1801,28 @@ class Render {
// 移动节点到画布中心
moveNodeToCenter(node) {
const { resetScaleOnMoveNodeToCenter } = this.mindMap.opt
let { transform, state } = this.mindMap.view.getTransformData()
let { left, top, width, height } = node
if (!resetScaleOnMoveNodeToCenter) {
left *= transform.scaleX
top *= transform.scaleY
width *= transform.scaleX
height *= transform.scaleY
}
let halfWidth = this.mindMap.width / 2
let halfHeight = this.mindMap.height / 2
let { left, top, width, height } = node
let nodeCenterX = left + width / 2
let nodeCenterY = top + height / 2
let { state } = this.mindMap.view.getTransformData()
let targetX = halfWidth - state.x
let targetY = halfHeight - state.y
let offsetX = targetX - nodeCenterX
let offsetY = targetY - nodeCenterY
this.mindMap.view.translateX(offsetX)
this.mindMap.view.translateY(offsetY)
this.mindMap.view.setScale(1)
if (resetScaleOnMoveNodeToCenter) {
this.mindMap.view.setScale(1)
}
}
// 回到中心主题,即设置根节点到画布中心
@@ -1756,15 +1832,29 @@ class Render {
// 展开到指定uid的节点
expandToNodeUid(uid, callback = () => {}) {
if (!this.renderTree) {
callback()
return
}
let parentsList = []
let isGeneralization = false
const cache = {}
bfsWalk(this.renderTree, (node, parent) => {
if (node.data.uid === uid) {
parentsList = parent ? [...cache[parent.data.uid], parent] : []
return 'stop'
} else {
cache[node.data.uid] = parent ? [...cache[parent.data.uid], parent] : []
}
const generalizationList = formatGetNodeGeneralization(node.data)
generalizationList.forEach(item => {
if (item.uid === uid) {
parentsList = parent ? [...cache[parent.data.uid], parent] : []
isGeneralization = true
}
})
if (isGeneralization) {
return 'stop'
}
cache[node.data.uid] = parent ? [...cache[parent.data.uid], parent] : []
})
let needRender = false
parentsList.forEach(node => {
@@ -1773,6 +1863,18 @@ class Render {
node.data.expand = true
}
})
// 如果是展开到概要节点,那么父节点下的所有节点都需要开
if (isGeneralization) {
const lastNode = parentsList[parentsList.length - 1]
if (lastNode) {
walk(lastNode, null, node => {
if (!node.data.expand) {
needRender = true
node.data.expand = true
}
})
}
}
if (needRender) {
this.mindMap.render(callback)
} else {
@@ -1788,12 +1890,25 @@ class Render {
res = node
return true
}
// 概要节点
let isGeneralization = false
;(node._generalizationList || []).forEach(item => {
if (item.generalizationNode.getData('uid') === uid) {
res = item.generalizationNode
isGeneralization = true
}
})
if (isGeneralization) {
return true
}
})
return res
}
// 高亮节点或子节点
highlightNode(node, range) {
// 如果当前正在渲染,那么不进行高亮,因为节点位置可能不正确
if (this.isRendering) return
const { highlightNodeBoxStyle = {} } = this.mindMap.opt
if (!this.highlightBoxNode) {
this.highlightBoxNode = new Polygon()

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

@@ -1,6 +1,6 @@
import Style from './Style'
import Shape from './Shape'
import { G, ForeignObject, Rect } from '@svgdotjs/svg.js'
import { G, Rect } from '@svgdotjs/svg.js'
import nodeGeneralizationMethods from './nodeGeneralization'
import nodeExpandBtnMethods from './nodeExpandBtn'
import nodeCommandWrapsMethods from './nodeCommandWraps'
@@ -8,12 +8,18 @@ import nodeCreateContentsMethods from './nodeCreateContents'
import nodeExpandBtnPlaceholderRectMethods from './nodeExpandBtnPlaceholderRect'
import nodeCooperateMethods from './nodeCooperate'
import { CONSTANTS } from '../../../constants/constant'
import { copyNodeTree } from '../../../utils/index'
import {
copyNodeTree,
createForeignObjectNode,
createUid,
addXmlns
} from '../../../utils/index'
// 节点类
class Node {
// 构造函数
constructor(opt = {}) {
this.opt = opt
// 节点数据
this.nodeData = this.handleData(opt.data || {})
// uid
@@ -75,6 +81,9 @@ class Node {
this._noteData = null
this.noteEl = null
this.noteContentIsShow = false
this._attachmentData = null
this._prefixData = null
this._postfixData = null
this._expandBtn = null
this._lastExpandBtnType = null
this._showExpandBtn = false
@@ -108,31 +117,35 @@ class Node {
this.needLayout = false
// 当前是否是隐藏状态
this.isHide = false
// 概要相关方法
Object.keys(nodeGeneralizationMethods).forEach(item => {
this[item] = nodeGeneralizationMethods[item].bind(this)
})
// 展开收起按钮相关方法
Object.keys(nodeExpandBtnMethods).forEach(item => {
this[item] = nodeExpandBtnMethods[item].bind(this)
})
// 展开收起按钮占位元素相关方法
Object.keys(nodeExpandBtnPlaceholderRectMethods).forEach(item => {
this[item] = nodeExpandBtnPlaceholderRectMethods[item].bind(this)
})
// 命令的相关方法
Object.keys(nodeCommandWrapsMethods).forEach(item => {
this[item] = nodeCommandWrapsMethods[item].bind(this)
})
// 创建节点内容的相关方法
Object.keys(nodeCreateContentsMethods).forEach(item => {
this[item] = nodeCreateContentsMethods[item].bind(this)
})
// 协同相关
if (this.mindMap.cooperate) {
Object.keys(nodeCooperateMethods).forEach(item => {
this[item] = nodeCooperateMethods[item].bind(this)
const proto = Object.getPrototypeOf(this)
if (!proto.bindEvent) {
// 概要相关方法
Object.keys(nodeGeneralizationMethods).forEach(item => {
proto[item] = nodeGeneralizationMethods[item]
})
// 展开收起按钮相关方法
Object.keys(nodeExpandBtnMethods).forEach(item => {
proto[item] = nodeExpandBtnMethods[item]
})
// 展开收起按钮占位元素相关方法
Object.keys(nodeExpandBtnPlaceholderRectMethods).forEach(item => {
proto[item] = nodeExpandBtnPlaceholderRectMethods[item]
})
// 命令的相关方法
Object.keys(nodeCommandWrapsMethods).forEach(item => {
proto[item] = nodeCommandWrapsMethods[item]
})
// 创建节点内容的相关方法
Object.keys(nodeCreateContentsMethods).forEach(item => {
proto[item] = nodeCreateContentsMethods[item]
})
// 协同相关
if (this.mindMap.cooperate) {
Object.keys(nodeCooperateMethods).forEach(item => {
proto[item] = nodeCooperateMethods[item]
})
}
proto.bindEvent = true
}
// 初始化
this.getSize()
@@ -181,16 +194,18 @@ class Node {
// 创建节点的各个内容对象数据
createNodeData() {
// 自定义节点内容
let { isUseCustomNodeContent, customCreateNodeContent } = this.mindMap.opt
let {
isUseCustomNodeContent,
customCreateNodeContent,
createNodePrefixContent,
createNodePostfixContent
} = this.mindMap.opt
if (isUseCustomNodeContent && customCreateNodeContent) {
this._customNodeContent = customCreateNodeContent(this)
}
// 如果没有返回内容,那么还是使用内置的节点内容
if (this._customNodeContent) {
this._customNodeContent.setAttribute(
'xmlns',
'http://www.w3.org/1999/xhtml'
)
addXmlns(this._customNodeContent)
return
}
this._imgData = this.createImgNode()
@@ -199,10 +214,25 @@ class Node {
this._hyperlinkData = this.createHyperlinkNode()
this._tagData = this.createTagNode()
this._noteData = this.createNoteNode()
this._attachmentData = this.createAttachmentNode()
this._prefixData = createNodePrefixContent
? createNodePrefixContent(this)
: null
if (this._prefixData && this._prefixData.el) {
addXmlns(this._prefixData.el)
}
this._postfixData = createNodePostfixContent
? createNodePostfixContent(this)
: null
if (this._postfixData && this._postfixData.el) {
addXmlns(this._postfixData.el)
}
}
// 计算节点的宽高
getSize() {
this.customLeft = this.getData('customLeft') || undefined
this.customTop = this.getData('customTop') || undefined
this.updateGeneralization()
this.createNodeData()
let { width, height } = this.getNodeRect()
@@ -233,6 +263,11 @@ class Node {
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
this._rectInfo.imgContentHeight = imgContentHeight = this._imgData.height
}
// 自定义前置内容
if (this._prefixData) {
textContentWidth += this._prefixData.width
textContentHeight = Math.max(textContentHeight, this._prefixData.height)
}
// 图标
if (this._iconData.length > 0) {
textContentWidth += this._iconData.reduce((sum, cur) => {
@@ -265,6 +300,19 @@ 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
)
}
// 自定义后置内容
if (this._postfixData) {
textContentWidth += this._postfixData.width
textContentHeight = Math.max(textContentHeight, this._postfixData.height)
}
// 文字内容部分的尺寸
this._rectInfo.textContentWidth = textContentWidth
this._rectInfo.textContentHeight = textContentHeight
@@ -325,10 +373,11 @@ class Node {
}
// 如果存在自定义节点内容,那么使用自定义节点内容
if (this.isUseCustomNodeContent()) {
let foreignObject = new ForeignObject()
foreignObject.width(width)
foreignObject.height(height)
foreignObject.add(this._customNodeContent)
const foreignObject = createForeignObjectNode({
el: this._customNodeContent,
width,
height
})
this.group.add(foreignObject)
addHoverNode()
return
@@ -343,6 +392,19 @@ class Node {
// 内容节点
let textContentNested = new G()
let textContentOffsetX = 0
// 自定义前置内容
if (this._prefixData) {
const foreignObject = createForeignObjectNode({
el: this._prefixData.el,
width: this._prefixData.width,
height: this._prefixData.height
})
foreignObject
.x(textContentOffsetX)
.y((this._rectInfo.textContentHeight - this._prefixData.height) / 2)
textContentNested.add(foreignObject)
textContentOffsetX += this._prefixData.width + textContentItemMargin
}
// icon
let iconNested = new G()
if (this._iconData && this._iconData.length > 0) {
@@ -359,9 +421,11 @@ class Node {
}
// 文字
if (this._textData) {
const oldX = this._textData.node.attr('data-offsetx') || 0
this._textData.node.attr('data-offsetx', textContentOffsetX)
// 修复safari浏览器节点存在图标时文字位置不正确的问题
;(this._textData.nodeContent || this._textData.node)
.x(-oldX) // 修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题
.x(textContentOffsetX)
.y(0)
textContentNested.add(this._textData.node)
@@ -397,6 +461,27 @@ 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
}
// 自定义后置内容
if (this._postfixData) {
const foreignObject = createForeignObjectNode({
el: this._postfixData.el,
width: this._postfixData.width,
height: this._postfixData.height
})
foreignObject
.x(textContentOffsetX)
.y((this._rectInfo.textContentHeight - this._postfixData.height) / 2)
textContentNested.add(foreignObject)
textContentOffsetX += this._postfixData.width
}
// 文字内容整体
textContentNested.translate(
width / 2 - textContentNested.bbox().width / 2,
@@ -420,6 +505,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 => {
@@ -443,7 +534,7 @@ class Node {
}
}
// 多选和取消多选
if (e.ctrlKey && enableCtrlKeyNodeSelection) {
if (!readonly && (e.ctrlKey || e.metaKey) && enableCtrlKeyNodeSelection) {
this.isMultipleChoice = true
let isActive = this.getData('isActive')
if (!isActive)
@@ -454,7 +545,7 @@ class Node {
)
this.mindMap.renderer[
isActive ? 'removeNodeFromActiveList' : 'addNodeToActiveList'
](this)
](this, true)
this.renderer.emitNodeActiveEvent(isActive ? null : this)
}
this.mindMap.emit('node_mousedown', this, e)
@@ -486,10 +577,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 || e.metaKey) {
return
}
e.stopPropagation()
if (onlyOneEnableActiveNodeOnCooperate && this.userList.length > 0) {
return
}
this.mindMap.emit('node_dblclick', this, e)
})
// 右键菜单事件
@@ -531,10 +626,16 @@ class Node {
}
this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
this.renderer.clearActiveNodeList()
this.renderer.addNodeToActiveList(this)
this.renderer.addNodeToActiveList(this, true)
this.renderer.emitNodeActiveEvent(this)
}
// 取消激活该节点
deactivate() {
this.mindMap.renderer.removeNodeFromActiveList(this)
this.mindMap.renderer.emitNodeActiveEvent()
}
// 更新节点
update() {
if (!this.group) {
@@ -542,9 +643,10 @@ class Node {
}
this.updateNodeActiveClass()
let { alwaysShowExpandBtn } = this.mindMap.opt
const childrenLength = this.nodeData.children.length
if (alwaysShowExpandBtn) {
// 需要移除展开收缩按钮
if (this._expandBtn && this.nodeData.children.length <= 0) {
if (this._expandBtn && childrenLength <= 0) {
this.removeExpandBtn()
} else {
// 更新展开收起按钮
@@ -553,7 +655,9 @@ class Node {
} else {
let { isActive, expand } = this.getData()
// 展开状态且非激活状态,且当前鼠标不在它上面,才隐藏
if (expand && !isActive && !this._isMouseenter) {
if (childrenLength <= 0) {
this.removeExpandBtn()
} else if (expand && !isActive && !this._isMouseenter) {
this.hideExpandBtn()
} else {
this.showExpandBtn()
@@ -682,9 +786,9 @@ class Node {
if (this.nodeData.inserting) {
delete this.nodeData.inserting
this.active()
setTimeout(() => {
this.mindMap.emit('node_dblclick', this, null, true)
}, 0)
// setTimeout(() => {
this.mindMap.emit('node_dblclick', this, null, true)
// }, 0)
}
}
@@ -705,6 +809,9 @@ class Node {
// 销毁节点,不但会从画布删除,而且原节点直接置空,后续无法再插回画布
destroy() {
if (!this.group) return
if (this.emptyUser) {
this.emptyUser()
}
this.resetWhenDelete()
this.group.remove()
this.removeGeneralization()
@@ -901,6 +1008,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 +1025,13 @@ class Node {
)
}
// 获取彩虹线条颜色
getRainbowLineColor(node) {
return this.mindMap.rainbowLines
? this.mindMap.rainbowLines.getNodeColor(node)
: ''
}
// 移除连线
removeLine() {
this._lines.forEach(line => {
@@ -1024,6 +1139,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 +1178,29 @@ 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')
}
// 伪克隆节点
// 克隆出的节点并不能真正当做一个节点使用
fakeClone() {
const newNode = new Node({
...this.opt,
uid: createUid()
})
Object.keys(this).forEach(item => {
newNode[item] = this[item]
})
return newNode
}
}
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

@@ -4,19 +4,13 @@ import {
removeHtmlStyle,
addHtmlStyle,
checkIsRichText,
isUndef
isUndef,
createForeignObjectNode,
addXmlns
} from '../../../utils'
import {
Image as SVGImage,
SVG,
A,
G,
Rect,
Text,
ForeignObject
} from '@svgdotjs/svg.js'
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
import iconsSvg from '../../../svg/icons'
import { CONSTANTS, commonCaches } from '../../../constants/constant'
import { CONSTANTS } from '../../../constants/constant'
// 创建图片节点
function createImgNode() {
@@ -92,7 +86,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,
@@ -143,17 +143,22 @@ function createRichTextNode() {
})
}
let html = `<div>${this.getData('text')}</div>`
if (!commonCaches.measureRichtextNodeTextSizeEl) {
commonCaches.measureRichtextNodeTextSizeEl = document.createElement('div')
commonCaches.measureRichtextNodeTextSizeEl.style.position = 'fixed'
commonCaches.measureRichtextNodeTextSizeEl.style.left = '-999999px'
this.mindMap.el.appendChild(commonCaches.measureRichtextNodeTextSizeEl)
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
document.createElement('div')
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl.style.position =
'fixed'
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl.style.left =
'-999999px'
this.mindMap.el.appendChild(
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl
)
}
let div = commonCaches.measureRichtextNodeTextSizeEl
let div = this.mindMap.commonCaches.measureRichtextNodeTextSizeEl
div.innerHTML = html
let el = div.children[0]
el.classList.add('smm-richtext-node-wrap')
el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
addXmlns(el)
el.style.maxWidth = textAutoWrapWidth + 'px'
let { width, height } = el.getBoundingClientRect()
// 如果文本为空,那么需要计算一个默认高度
@@ -168,10 +173,11 @@ function createRichTextNode() {
height = Math.ceil(height)
g.attr('data-width', width)
g.attr('data-height', height)
let foreignObject = new ForeignObject()
foreignObject.width(width)
foreignObject.height(height)
foreignObject.add(div.children[0])
const foreignObject = createForeignObjectNode({
el: div.children[0],
width,
height
})
g.add(foreignObject)
return {
node: g,
@@ -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' })
@@ -281,6 +287,9 @@ function createTagNode() {
let nodes = []
tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => {
let tag = new G()
tag.on('click', () => {
this.mindMap.emit('node_tag_click', this, item)
})
// 标签文本
let text = new Text().text(item).x(8).cy(8)
this.style.tagText(text, index)
@@ -307,7 +316,10 @@ function createNoteNode() {
return null
}
let iconSize = this.mindMap.themeConfig.iconSize
let node = new SVG().attr('cursor', 'pointer').size(iconSize, iconSize)
let node = new SVG()
.attr('cursor', 'pointer')
.addClass('smm-node-note')
.size(iconSize, iconSize)
// 透明的层,用来作为鼠标区域
node.add(new Rect().size(iconSize, iconSize).fill({ color: 'transparent' }))
// 备注图标
@@ -362,6 +374,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
@@ -377,18 +419,22 @@ function getNoteContentPosition() {
// 测量自定义节点内容元素的宽高
function measureCustomNodeContentSize(content) {
if (!commonCaches.measureCustomNodeContentSizeEl) {
commonCaches.measureCustomNodeContentSizeEl = document.createElement('div')
commonCaches.measureCustomNodeContentSizeEl.style.cssText = `
if (!this.mindMap.commonCaches.measureCustomNodeContentSizeEl) {
this.mindMap.commonCaches.measureCustomNodeContentSizeEl =
document.createElement('div')
this.mindMap.commonCaches.measureCustomNodeContentSizeEl.style.cssText = `
position: fixed;
left: -99999px;
top: -99999px;
`
this.mindMap.el.appendChild(commonCaches.measureCustomNodeContentSizeEl)
this.mindMap.el.appendChild(
this.mindMap.commonCaches.measureCustomNodeContentSizeEl
)
}
commonCaches.measureCustomNodeContentSizeEl.innerHTML = ''
commonCaches.measureCustomNodeContentSizeEl.appendChild(content)
let rect = commonCaches.measureCustomNodeContentSizeEl.getBoundingClientRect()
this.mindMap.commonCaches.measureCustomNodeContentSizeEl.innerHTML = ''
this.mindMap.commonCaches.measureCustomNodeContentSizeEl.appendChild(content)
let rect =
this.mindMap.commonCaches.measureCustomNodeContentSizeEl.getBoundingClientRect()
return {
width: rect.width,
height: rect.height
@@ -409,6 +455,7 @@ export default {
createHyperlinkNode,
createTagNode,
createNoteNode,
createAttachmentNode,
getNoteContentPosition,
measureCustomNodeContentSize,
isUseCustomNodeContent

View File

@@ -51,6 +51,7 @@ function createGeneralizationNode() {
if (!cur.generalizationNode) {
cur.generalizationNode = new Node({
data: {
inserting: item.inserting,
data: item
},
uid: createUid(),
@@ -59,6 +60,7 @@ function createGeneralizationNode() {
isGeneralization: true
})
}
delete item.inserting
// 关联所属节点
cur.generalizationNode.generalizationBelongNode = this
// 大小

View File

@@ -29,15 +29,16 @@ class View {
this.fit()
})
// 拖动视图
this.mindMap.event.on('mousedown', () => {
this.mindMap.event.on('mousedown', e => {
if (this.mindMap.opt.isDisableDrag) return
e.preventDefault()
this.sx = this.x
this.sy = this.y
})
this.mindMap.event.on('drag', (e, event) => {
// 按住ctrl键拖动为多选
// 禁用拖拽
if (e.ctrlKey || this.mindMap.opt.isDisableDrag) {
if (e.ctrlKey || e.metaKey || this.mindMap.opt.isDisableDrag) {
return
}
if (this.firstDrag) {
@@ -72,7 +73,11 @@ class View {
return customHandleMousewheel(e)
}
// 1.鼠标滚轮事件控制缩放
if (mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM || e.ctrlKey) {
if (
mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM ||
e.ctrlKey ||
e.metaKey
) {
if (disableMouseWheelZoom) return
const { x: clientX, y: clientY } = this.mindMap.toPos(
e.clientX,
@@ -128,6 +133,10 @@ class View {
this.translateXY(mx, my)
}
})
this.mindMap.on('resize', () => {
if (!this.checkNeedMindMapInCanvas()) return
this.transform()
})
}
// 获取当前变换状态数据
@@ -154,7 +163,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 +174,7 @@ class View {
this.x += x
this.y += y
this.transform()
this.emitEvent('translate')
}
// 平移x方向
@@ -171,12 +182,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 +197,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 +222,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 +242,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 +251,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,15 +277,16 @@ class View {
this.scale = scale
}
this.transform()
this.mindMap.emit('scale', this.scale)
this.emitEvent('scale')
}
// 适应画布大小
fit() {
const { fitPadding } = this.mindMap.opt
fit(getRbox = () => {}, enlarge = false, fitPadding) {
fitPadding =
fitPadding === undefined ? this.mindMap.opt.fitPadding : fitPadding
const draw = this.mindMap.draw
const origTransform = draw.transform()
const rect = draw.rbox()
const rect = getRbox() || draw.rbox()
const drawWidth = rect.width / origTransform.scaleX
const drawHeight = rect.height / origTransform.scaleY
const drawRatio = drawWidth / drawHeight
@@ -276,7 +296,7 @@ class View {
const elRatio = elWidth / elHeight
let newScale = 0
let flag = ''
if (drawWidth <= elWidth && drawHeight <= elHeight) {
if (drawWidth <= elWidth && drawHeight <= elHeight && !enlarge) {
newScale = 1
flag = 1
} else {
@@ -294,7 +314,7 @@ class View {
newScale = newWidth / drawWidth
}
this.setScale(newScale)
const newRect = draw.rbox()
const newRect = getRbox() || draw.rbox()
// 需要考虑画布容器距浏览器窗口左上角的距离
newRect.x -= this.mindMap.elRect.left
newRect.y -= this.mindMap.elRect.top
@@ -313,20 +333,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 +369,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 +410,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

@@ -144,9 +144,10 @@ class Base {
this.cacheNode(newUid, newNode)
// 数据关联实际节点
data._node = newNode
if (data.data.isActive) {
this.renderer.addNodeToActiveList(newNode)
}
}
// 如果该节点数据是已激活状态,那么添加到激活节点列表里
if (data.data.isActive) {
this.renderer.addNodeToActiveList(newNode)
}
// 如果当前节点在激活节点列表里,那么添加上激活的状态
if (this.mindMap.renderer.findActiveNodeIndex(newNode) !== -1) {

View File

@@ -1,5 +1,21 @@
import { fromMarkdown } from 'mdast-util-from-markdown'
const getNodeText = node => {
// 优先找出其中的text类型的子节点
let textChild = (node.children || []).find(item => {
return item.type === 'text'
})
// 没有找到,那么直接使用第一个子节点
textChild = textChild || node.children[0]
if (textChild) {
if (textChild.value !== undefined) {
return textChild.value
}
return getNodeText(textChild)
}
return ''
}
// 处理list的情况
const handleList = node => {
let list = []
@@ -9,7 +25,7 @@ const handleList = node => {
let node = {}
node.data = {
// 节点内容
text: cur.children[0].children[0].value
text: getNodeText(cur)
}
node.children = []
newArr.push(node)
@@ -45,7 +61,7 @@ export const transformMarkdownTo = md => {
let node = {}
node.data = {
// 节点内容
text: cur.children[0].value
text: getNodeText(cur)
}
node.children = []
// 如果当前的层级大于上一个节点的层级,那么是其子节点

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

@@ -15,7 +15,7 @@ import {
} from '../utils/xmind'
// 解析.xmind文件
const parseXmindFile = file => {
const parseXmindFile = (file, handleMultiCanvas) => {
return new Promise((resolve, reject) => {
JSZip.loadAsync(file).then(
async zip => {
@@ -25,7 +25,7 @@ const parseXmindFile = file => {
let xmlFile = zip.files['content.xml'] || zip.files['/content.xml']
if (jsonFile) {
let json = await jsonFile.async('string')
content = await transformXmind(json, zip.files)
content = await transformXmind(json, zip.files, handleMultiCanvas)
} else if (xmlFile) {
let xml = await xmlFile.async('string')
let json = xmlConvert.xml2json(xml)
@@ -48,8 +48,15 @@ const parseXmindFile = file => {
}
// 转换xmind数据
const transformXmind = async (content, files) => {
const data = JSON.parse(content)[0]
const transformXmind = async (content, files, handleMultiCanvas) => {
content = JSON.parse(content)
let data = null
if (content.length > 1 && typeof handleMultiCanvas === 'function') {
data = await handleMultiCanvas(content)
}
if (!data) {
data = content[0]
}
const nodeTree = data.rootTopic
const newTree = {}
const waitLoadImageList = []
@@ -198,7 +205,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

@@ -0,0 +1,397 @@
import {
walk,
getNodeTreeBoundingRect,
fullscrrenEvent,
fullScreen,
exitFullScreen,
formatGetNodeGeneralization
} from '../utils/index'
import { keyMap } from '../core/command/keyMap'
const defaultConfig = {
boxShadowColor: 'rgba(0, 0, 0, 0.8)', // 高亮框四周的区域颜色
borderRadius: '5px', // 高亮框的圆角大小
transition: 'all 0.3s ease-out', // 高亮框动画的过渡
zIndex: 9999, // 高亮框元素的层级
padding: 20, // 高亮框的内边距
margin: 50, // 高亮框的外边距
openBlankMode: true // 是否开启填空模式,即带下划线的文本默认不显示,按回车键才依次显示
}
// 演示插件
class Demonstrate {
constructor(opt) {
this.mindMap = opt.mindMap
// 演示的步骤列表
this.stepList = []
// 当前所在步骤
this.currentStepIndex = 0
// 当前所在步骤对应的节点实例
this.currentStepNode = null
// 当前所在步骤节点的下划线文本数据
this.currentUnderlineTextData = null
// 临时的样式剩余
this.tmpStyleEl = null
// 高亮样式元素
this.highlightEl = null
this.transformState = null
this.renderTree = null
this.config = Object.assign(
{ ...defaultConfig },
this.mindMap.opt.demonstrateConfig || {}
)
}
// 进入演示模式
enter() {
// 全屏
this.bindFullscreenEvent()
// 如果已经全屏了
if (document.fullscreenElement === this.mindMap.el) {
this._enter()
} else {
// 否则申请全屏
fullScreen(this.mindMap.el)
}
}
_enter() {
// 添加演示用的临时的样式
this.addTmpStyles()
// 记录演示前的画布状态
this.transformState = this.mindMap.view.getTransformData()
// 记录演示前的画布数据
this.renderTree = this.mindMap.getData()
// 暂停收集历史记录
this.mindMap.command.pause()
// 暂停思维导图快捷键响应
this.mindMap.keyCommand.pause()
// 创建高亮元素
this.createHighlightEl()
// 计算步骤数据
this.getStepList()
// 收起所有节点
this.mindMap.execCommand('UNEXPAND_ALL', false)
const onRenderEnd = () => {
this.mindMap.off('node_tree_render_end', onRenderEnd)
// 聚焦到第一步
this.jump(this.currentStepIndex)
this.bindEvent()
}
this.mindMap.on('node_tree_render_end', onRenderEnd)
}
// 退出演示模式
exit() {
exitFullScreen(this.mindMap.el)
this.mindMap.updateData(this.renderTree)
this.mindMap.view.setTransformData(this.transformState)
this.renderTree = null
this.transformState = null
this.stepList = []
this.currentStepIndex = 0
this.currentStepNode = null
this.currentUnderlineTextData = null
this.unBindEvent()
this.removeTmpStyles()
this.removeHighlightEl()
this.mindMap.command.recovery()
this.mindMap.keyCommand.recovery()
this.mindMap.emit('exit_demonstrate')
}
// 添加临时的样式
addTmpStyles() {
this.tmpStyleEl = document.createElement('style')
let cssText = `
/* 画布所有元素禁止响应鼠标事件 */
.smm-mind-map-container {
pointer-events: none;
}
/* 超链接图标允许响应鼠标事件 */
.smm-node a {
pointer-events: all;
}
/* 备注图标允许响应鼠标事件 */
.smm-node .smm-node-note {
pointer-events: all;
}
`
if (this.config.openBlankMode) {
cssText += `
/* 带下划线的文本内容全部隐藏 */
.smm-richtext-node-wrap u {
opacity: 0;
}
`
}
this.tmpStyleEl.innerText = cssText
document.head.appendChild(this.tmpStyleEl)
}
// 移除临时的样式
removeTmpStyles() {
if (this.tmpStyleEl) document.head.removeChild(this.tmpStyleEl)
}
// 创建高亮元素
createHighlightEl() {
if (!this.highlightEl) {
// 高亮元素
this.highlightEl = document.createElement('div')
this.highlightEl.style.cssText = `
position: absolute;
box-shadow: 0 0 0 5000px ${this.config.boxShadowColor};
border-radius: ${this.config.borderRadius};
transition: ${this.config.transition};
z-index: ${this.config.zIndex + 1};
pointer-events: none;
`
this.mindMap.el.appendChild(this.highlightEl)
}
}
// 移除高亮元素
removeHighlightEl() {
if (this.highlightEl) {
this.mindMap.el.removeChild(this.highlightEl)
this.highlightEl = null
}
}
// 更新高亮元素的位置和大小
updateHighlightEl({ left, top, width, height }) {
const padding = this.config.padding
if (left) {
this.highlightEl.style.left = left - padding + 'px'
}
if (top) {
this.highlightEl.style.top = top - padding + 'px'
}
if (width) {
this.highlightEl.style.width = width + padding * 2 + 'px'
}
if (height) {
this.highlightEl.style.height = height + padding * 2 + 'px'
}
}
// 绑定事件
bindEvent() {
this.onKeydown = this.onKeydown.bind(this)
window.addEventListener('keydown', this.onKeydown)
}
// 绑定全屏事件
bindFullscreenEvent() {
this.onFullscreenChange = this.onFullscreenChange.bind(this)
document.addEventListener(fullscrrenEvent, this.onFullscreenChange)
}
// 解绑事件
unBindEvent() {
window.removeEventListener('keydown', this.onKeydown)
document.removeEventListener(fullscrrenEvent, this.onFullscreenChange)
}
// 全屏状态改变
onFullscreenChange() {
if (!document.fullscreenElement) {
this.exit()
} else if (document.fullscreenElement === this.mindMap.el) {
this._enter()
}
}
// 按键事件
onKeydown(e) {
// 上一个
if (e.keyCode === keyMap.Left) {
this.prev()
} else if (e.keyCode === keyMap.Right) {
// 下一个
this.next()
} else if (e.keyCode === keyMap.Esc) {
// 退出演示
this.exit()
} else if (e.keyCode === keyMap.Enter) {
// 回车键显示隐藏的下划线文本
this.showNextUnderlineText()
}
}
// 上一张
prev() {
if (this.currentStepIndex > 0) {
this.jump(this.currentStepIndex - 1)
}
}
// 下一张
next() {
const stepLength = this.stepList.length
if (this.currentStepIndex < stepLength - 1) {
this.jump(this.currentStepIndex + 1)
}
}
// 显示隐藏的下划线文本
showNextUnderlineText() {
if (
!this.config.openBlankMode ||
!this.currentStepNode ||
!this.currentUnderlineTextData
)
return
const { index, list, length } = this.currentUnderlineTextData
if (index >= length) return
const node = list[index]
this.currentUnderlineTextData.index++
node.node.style.opacity = 1
}
// 跳转到某一张
jump(index) {
// 移除该当前下划线元素设置的样式
if (this.currentUnderlineTextData) {
this.currentUnderlineTextData.list.forEach(item => {
item.node.style.opacity = ''
})
this.currentUnderlineTextData = null
}
this.currentStepNode = null
this.currentStepIndex = index
this.mindMap.emit(
'demonstrate_jump',
this.currentStepIndex,
this.stepList.length
)
const step = this.stepList[index]
// 这一步的节点数据
const nodeData = step.node
// 该节点的uid
const uid = nodeData.data.uid
// 根据uid在画布上找到该节点实例
const node = this.mindMap.renderer.findNodeByUid(uid)
// 如果该节点实例不存在,那么先展开到该节点
if (!node) {
this.mindMap.renderer.expandToNodeUid(uid, () => {
const node = this.mindMap.renderer.findNodeByUid(uid)
// 展开后还是没找到,那么就别进入了,否则会死循环
if (node) {
this.jump(index)
}
})
return
}
// 1.聚焦到某个节点
if (step.type === 'node') {
this.currentStepNode = node
// 当前节点存在带下划线的文本内容
const uNodeList = this.config.openBlankMode ? node.group.find('u') : null
if (uNodeList && uNodeList.length > 0) {
this.currentUnderlineTextData = {
index: 0,
list: uNodeList,
length: uNodeList.length
}
}
// 适应画布大小
this.mindMap.view.fit(
() => {
return node.group.rbox()
},
true,
this.config.padding + this.config.margin
)
const rect = node.group.rbox()
this.updateHighlightEl({
left: rect.x,
top: rect.y,
width: rect.width,
height: rect.height
})
} else {
// 2.聚焦到某个节点的所有子节点
// 聚焦该节点的所有子节点
const task = () => {
// 先收起该节点所有子节点的子节点
nodeData.children.forEach(item => {
item.data.expand = false
})
this.mindMap.render(() => {
// 适应画布大小
this.mindMap.view.fit(
() => {
const res = getNodeTreeBoundingRect(node, 0, 0, 0, 0, true)
return {
...res,
x: res.left,
y: res.top
}
},
true,
this.config.padding + this.config.margin
)
const res = getNodeTreeBoundingRect(node, 0, 0, 0, 0, true)
this.updateHighlightEl(res)
})
}
// 如果该节点是收起状态,那么需要先展开
if (!nodeData.data.expand) {
this.mindMap.execCommand('SET_NODE_EXPAND', node, true)
const onRenderEnd = () => {
this.mindMap.off('node_tree_render_end', onRenderEnd)
task()
}
this.mindMap.on('node_tree_render_end', onRenderEnd)
} else {
// 否则直接聚焦
task()
}
}
}
// 深度度优先遍历所有节点,返回步骤列表
getStepList() {
walk(this.mindMap.renderer.renderTree, null, node => {
this.stepList.push({
type: 'node',
node
})
// 添加概要步骤
const generalizationList = formatGetNodeGeneralization(node.data)
generalizationList.forEach(item => {
// 没有uid的直接过滤掉否则会死循环
if (item.uid) {
this.stepList.push({
type: 'node',
node: {
data: item
}
})
}
})
if (node.children.length > 1) {
this.stepList.push({
type: 'children',
node
})
}
})
}
// 插件被移除前做的事情
beforePluginRemove() {
this.unBindEvent()
}
// 插件被卸载前做的事情
beforePluginDestroy() {
this.unBindEvent()
}
}
Demonstrate.instanceName = 'demonstrate'
export default Demonstrate

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