Compare commits

..

58 Commits

Author SHA1 Message Date
街角小林
ff22a652d5 打包0.10.6 2024-08-15 18:31:18 +08:00
街角小林
d1ade8204b Doc: update 2024-08-15 18:22:51 +08:00
街角小林
2a84a3cf16 Demo:修复更改基础样式后切换主题,选择覆盖,再修改基础样式时之前覆盖的样式会恢复的问题 2024-08-15 16:39:56 +08:00
街角小林
721cae8139 Demo:修复激活节点时在缩放备注输入框中输入会输入到节点文本中的问题 2024-08-15 16:29:18 +08:00
街角小林
8f8662f2df Fix:修复处于回退操作中激活节点后无法前进的问题 2024-08-15 16:25:39 +08:00
街角小林
bc56f90595 Fix:修复一些小问题 2024-08-15 15:49:33 +08:00
街角小林
0d1b9dfff4 Feat:全选操作支持选中概要节点 2024-08-15 15:41:58 +08:00
街角小林
ccb2a260b6 Feat:鼠标框选支持选中概要节点 2024-08-15 15:30:56 +08:00
街角小林
2c8b96582a Feat:1.优化概要渲染,减少不必要的重新渲染;2.修复同时选中概要节点和其所属节点,设置节点样式后概要节点会失去激活状态的问题 2024-08-15 15:19:26 +08:00
街角小林
161d9dd715 Feat:新增节点备注图标点击事件 2024-08-14 18:35:27 +08:00
街角小林
1033447b9c Feat:搜索插件支持搜索概要节点 2024-08-14 17:48:23 +08:00
街角小林
1dbcb71ec5 Fix:修复GO_TARGET_NODE命令对概要节点支持不全的问题 2024-08-14 17:35:32 +08:00
街角小林
c0733dda35 update 2024-08-14 16:59:37 +08:00
街角小林
4c5b2e7af2 Demo:备注里的超链接改为新窗口打开 2024-08-14 16:50:53 +08:00
街角小林
322f7a3e2a Fix:修复富文本模式下切换主题,概要节点的文本样式没有跟随改变的问题 2024-08-14 16:42:26 +08:00
街角小林
766e0e5fae Doc: update 2024-08-14 16:33:43 +08:00
街角小林
4335cbb713 Feat:render类新增手动激活和取消激活多个节点的方法 2024-08-14 16:33:28 +08:00
街角小林
82473027da Demo:右键菜单新增设置节点编号功能 2024-08-14 09:41:33 +08:00
街角小林
68bf2d361c Feat:新增对编号插件的支持 2024-08-14 09:21:30 +08:00
街角小林
1620a013ba Feat:walk方法增加祖先列表回调参数 2024-08-13 10:57:01 +08:00
街角小林
13a1f989c3 Feat:实例销毁时删除文本编辑框的元素 2024-08-12 14:06:30 +08:00
街角小林
8bbbc082c7 Doc: update 2024-08-12 13:50:38 +08:00
街角小林
f15bf8a8dc Demo:支持设置关联线的样式,即实线或虚线 2024-08-12 13:36:42 +08:00
街角小林
3801dc3ec4 Feat:主题配置支持设置关联线的样式,即实线或虚线 2024-08-12 13:36:14 +08:00
街角小林
182cdf5153 Feat:不阻止mousedown、mousemove事件的默认行为 2024-08-12 10:58:17 +08:00
街角小林
1cd4705ad8 Feat:支持不显示展开收起按钮的实例化选项 2024-08-12 10:36:05 +08:00
街角小林
d82cedcbdd Fix:1.修复点击鼠标右键菜单时关联线插件报错的问题;2.修复点击画布无法取消激活关联线的问题; 2024-08-12 09:55:05 +08:00
街角小林
f9c271e11b Demo:支持鼠标选中备注浮层的内容 2024-08-12 09:27:45 +08:00
街角小林
a26d6bc7cf 打包Demo 2024-08-08 12:01:39 +08:00
街角小林
dc522cd0be Doc: update 2024-08-08 11:57:47 +08:00
街角小林
43969af14b Fix:修复只读模式下搜索节点的高亮不会消失的问题 2024-08-08 11:52:02 +08:00
街角小林
79ccd9892c Feat:RichText插件新增扩展字体列表的方法 2024-08-06 09:56:37 +08:00
街角小林
5fb35d656f 打包0.10.5 2024-08-02 10:04:20 +08:00
街角小林
cec1f59189 Doc: update 2024-08-02 09:46:17 +08:00
街角小林
a6a890362e Dmoe:修复大纲里点击节点进行拖拽会触发页面的文件拖拽蒙层的问题 2024-08-01 10:36:03 +08:00
街角小林
83b6d5793b Fix:修复画布尺寸调整后第一次触发渲染画布会发生跳动的问题 2024-07-31 10:53:56 +08:00
街角小林
9b40a7bedd Doc: update 2024-07-30 09:19:10 +08:00
街角小林
11f2078ee0 Demo:编辑本地文件时如果未保存关闭页面增加拦截提示 2024-07-29 17:20:21 +08:00
街角小林
4fb9bd5135 Demo:打开本地文件编辑的情况下不再将数据写入浏览器缓存 2024-07-29 17:00:39 +08:00
街角小林
cf56b5db24 Demo:节点下级数量大于100收起时显示省略号 2024-07-29 15:39:51 +08:00
街角小林
40ea595e62 Feat:移动节点或复制节点到收起的节点上自动展开该节点 2024-07-29 15:13:11 +08:00
街角小林
91a9d7b556 Doc: update 2024-07-29 09:54:50 +08:00
街角小林
0669e73a5b Feat:支持点击画布取消创建关联线 2024-07-29 09:49:05 +08:00
街角小林
fde6fc2583 Fix:修复开启性能模式后,演示插件无法正常工作的问题 2024-07-29 09:28:51 +08:00
街角小林
f92146a1f4 Fix:修复跨层级复制节点时,节点的富文本样式没有更新的问题 2024-07-26 17:05:09 +08:00
街角小林
38c6966d13 Feat:移动节点且节点层级发生了改变,设置过自定义富文本样式的节点不需要更新样式 2024-07-26 16:39:19 +08:00
街角小林
4894d9e6e6 Fix:修复开启性能模式后拖动滚动条报错的问题 2024-07-26 15:51:30 +08:00
街角小林
9a64094f14 打包0.10.4 2024-07-26 10:32:39 +08:00
街角小林
95fdb35f7b Fix:优化性能模式的懒加载 2024-07-26 10:15:54 +08:00
街角小林
497c2606df Feat:懒加载节点树时增加触发渲染开始和结束事件的派发 2024-07-26 09:38:22 +08:00
街角小林
d735be1204 Doc: update 2024-07-25 18:33:36 +08:00
街角小林
45d2da1337 update package.json 2024-07-25 18:32:04 +08:00
街角小林
194571d5fe Fix:修复在@svgdotjs/svg.js库版本为3.0.16时报错的问题 2024-07-25 18:05:22 +08:00
街角小林
418b24b039 Doc: update 2024-07-25 17:38:01 +08:00
街角小林
b32a8b5a85 Demo:基础配置里增加开启性能模式的开关 2024-07-25 16:41:21 +08:00
街角小林
2dee415a64 Feat:新增性能模式 2024-07-25 16:40:47 +08:00
街角小林
4e7d59b328 Fix:删除无用代码,修复鼠标悬浮节点上报错不显示展开收起按钮的问题 2024-07-24 17:13:49 +08:00
街角小林
9bde9ffaf3 Doc: update 2024-07-22 09:25:22 +08:00
164 changed files with 3014 additions and 456 deletions

View File

@@ -103,7 +103,9 @@ const mindMap = new MindMap({
# 微信交流群
群聊人数较多,无法通过二维码入群,可以微信添加`wanglinguanfang`拉你入群。思维导图相关问题皆可在群里提问,不必私聊作者。
一群已满,可以扫描如下二维码进入二群,如已过期,可以微信添加`wanglinguanfang`拉你入群。思维导图相关问题皆可在群里提问,不必私聊作者。
<img src="./qrcode.jpg" style="width: 300px" />
# star
@@ -431,4 +433,24 @@ 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/ccccs.jpg" style="width: 50px;height: 50px;" />
<span>ccccs</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/Jeffrey.jpg" style="width: 50px;height: 50px;" />
<span>Jeffrey</span>
</span>
<span>
<img src="./web/src/assets/avatar/张文建.jpg" style="width: 50px;height: 50px;" />
<span>张文建</span>
</span>
</p>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
dist/img/Jeffrey.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
dist/img/ccccs.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
dist/img/qrcode.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
dist/img/。.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
dist/img/张文建.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
dist/img/编号1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
dist/img/编号2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
dist/img/编号3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

2
dist/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/js/chunk-0e53cbc6.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/chunk-1171703b.js vendored Normal file
View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-1171703b"],{"8cc7":function(e,o,t){"use strict";t.r(o);var s=function(){var e=this;e._self._c;return e._m(0)},i=[function(){var e=this,o=e._self._c;return o("div",[o("h1",[e._v("Questions")]),o("h2",[e._v("1.Error when using in Vite, indicating xml-js dependency error")]),o("p",[e._v("Solution: use the following import method:")]),o("pre",{staticClass:"hljs"},[o("code",[o("span",{staticClass:"hljs-keyword"},[e._v("import")]),e._v(" MindMap "),o("span",{staticClass:"hljs-keyword"},[e._v("from")]),e._v(" "),o("span",{staticClass:"hljs-string"},[e._v('"simple-mind-map/dist/simpleMindMap.umd.min"')]),e._v(";\n")])]),o("p",[e._v("The "),o("code",[e._v("simple-mind-map")]),e._v(" package provides the unpacked entry field "),o("code",[e._v("module")]),e._v(", and the "),o("code",[e._v("xml-js")]),e._v(" package dependency needs to import the package in the "),o("code",[e._v("node")]),e._v(" environment. Therefore, it cannot be obtained in "),o("code",[e._v("Vite")]),e._v(" and an error will be reported. Therefore, specify the import of the packed entry, and all relevant packages are packed into the product, so there will be no error.")]),o("p",[e._v("If you need to do further development, that is, you must use the unpacked code, and if you do not need to parse the "),o("code",[e._v("xmind")]),e._v(" file, you can remove the "),o("code",[e._v("xmind")]),e._v(" module. If you need it, you can try using other libraries to parse "),o("code",[e._v("xml")]),e._v(" to "),o("code",[e._v("json")]),e._v(".")]),o("h2",[e._v("2.Error "),o("code",[e._v("Getting bbox of element \"text\" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')")])]),o("p",[e._v("The reason is that the installed version of "),o("code",[e._v("@svgdotjs/svg.js")]),e._v(" is too high. You can manually reduce it to the version of "),o("code",[e._v("3.0.16")]),e._v(".")]),o("h2",[e._v("3.TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46")]),o("p",[e._v("The following configurations can be added to the packaging configuration file:")]),o("pre",{staticClass:"hljs"},[o("code",[e._v("resolve: { "),o("span",{staticClass:"hljs-attr"},[e._v("alias")]),e._v(": { "),o("span",{staticClass:"hljs-attr"},[e._v("stream")]),e._v(": "),o("span",{staticClass:"hljs-string"},[e._v('"stream-browserify"')]),e._v(" } }\n")])]),o("p",[e._v("Different packaging tools may have different specific configurations, with the principle of excluding 'stream' dependencies.")]),o("h2",[e._v("4.When clicking the [New], [Open], or [Save As] buttons, it will prompt that the browser does not support it or is not using the HTTPS protocol.")]),o("p",[e._v("The browser uses API "),o("a",{attrs:{href:"https://developer.mozilla.org/zh-CN/docs/Web/API/Window/showOpenFilePicker"}},[e._v("window.showOpenFilePicker")]),e._v(" to operate local files on the computer. If it is not supported, either the browser does not support this API or the page is not using the HTTPS protocol, You can press F12, or open the browser console through the right-click menu on the page and enter 'window.showOpenFilePicker' in the 'Console' tab. If it returns 'undefined', it means it is not supported. If it does not return this message and the page still prompts that the browser does not support it or is not using the HTTPS protocol, you can submit an issue or contact the author.")]),o("h2",[e._v("5.Import simple-mind-map error message, the error message is as follows:")]),o("img",{staticStyle:{width:"850px"},attrs:{src:t("bff3")}}),o("p",[e._v("This is because your build environment does not support this JavaScript syntax, which comes from the '@svgdotjs/svg.js' library. The solution is as follows:")]),o("p",[e._v("1.Manually reduce the version of the '@svgdotjs/svg.js' library. You can manually install the lower version in your project, such as: "),o("code",[e._v("npm i @svgdotjs/svg.js@3.2.0")])]),o("p",[e._v("2.If you don't reduce the version, you can modify the relevant configuration of your build tool, modify the configuration of 'babel', and have it compile the 'simple-mind-map' library in 'node.modules' or the '@svgdotjs/svg.js' library. If you are using 'vue-cli' or 'vite', they also provide the relevant configuration directly. In addition, it is necessary to install the 'babel' plugin that compiles this syntax and configure it in the 'babel' configuration file:")]),o("p",[o("code",[e._v("@babel/plugin-proposal-nullish-coalescing-operator")]),e._v("、"),o("code",[e._v("@babel/plugin-proposal-optional-chaining")]),e._v("。")])])}],n={},r=n,a=t("2877"),l=Object(a["a"])(r,s,i,!1,null,null,null);o["default"]=l.exports},bff3:function(e,o,t){e.exports=t.p+"img/错误.jpg"}}]);

1
dist/js/chunk-15396d69.js vendored Normal file
View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-15396d69"],{"57f5":function(s,e,v){"use strict";v.r(e);var o=function(){var s=this;s._self._c;return s._m(0)},_=[function(){var s=this,e=s._self._c;return e("div",[e("h1",[s._v("常见问题")]),e("h2",[s._v("1.在Vite中使用报错提示xml-js依赖出错")]),e("p",[s._v("解决方法:使用如下引入方式:")]),e("pre",{staticClass:"hljs"},[e("code",[e("span",{staticClass:"hljs-keyword"},[s._v("import")]),s._v(" MindMap "),e("span",{staticClass:"hljs-keyword"},[s._v("from")]),s._v(" "),e("span",{staticClass:"hljs-string"},[s._v('"simple-mind-map/dist/simpleMindMap.umd.min"')]),s._v("\n")])]),e("p",[e("code",[s._v("simple-mind-map")]),s._v("包提供未打包的入口字段"),e("code",[s._v("module")]),s._v(",依赖的"),e("code",[s._v("xml-js")]),s._v("包需要引入"),e("code",[s._v("node")]),s._v("环境下的包,所以在"),e("code",[s._v("Vite")]),s._v("中获取不到会报错,所以指定引入打包后的入口,相关包都已打包进产物,所以不会报错。")]),e("p",[s._v("如果需要二次开发,也就是必须要使用未打包代码的话,如果你不需要解析"),e("code",[s._v("xmind")]),s._v("文件的话,可以去除"),e("code",[s._v("xmind")]),s._v("模块,如果需要的话那么可以尝试换成其他的解析"),e("code",[s._v("xml")]),s._v("为"),e("code",[s._v("json")]),s._v("的库。")]),e("h2",[s._v("2.报错"),e("code",[s._v("Getting bbox of element \"text\" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')")])]),e("p",[s._v("原因为安装的"),e("code",[s._v("@svgdotjs/svg.js")]),s._v("版本太高,手动降到"),e("code",[s._v("3.0.16")]),s._v("版本即可。")]),e("h2",[s._v("3.TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46")]),e("p",[s._v("可以在打包配置文件中增加如下配置:")]),e("pre",{staticClass:"hljs"},[e("code",[s._v("resolve: { "),e("span",{staticClass:"hljs-attr"},[s._v("alias")]),s._v(": { "),e("span",{staticClass:"hljs-attr"},[s._v("stream")]),s._v(": "),e("span",{staticClass:"hljs-string"},[s._v('"stream-browserify"')]),s._v(" } }\n")])]),e("p",[s._v("不同的打包工具可能具体配置不一样,原理就是排除"),e("code",[s._v("stream")]),s._v("依赖。")]),e("h2",[s._v("4.点击【新建】、【打开】、【另存为】按钮时提示浏览器不支持或者非https协议。")]),e("p",[s._v("浏览器上操作电脑本地文件使用的是"),e("a",{attrs:{href:"https://developer.mozilla.org/zh-CN/docs/Web/API/Window/showOpenFilePicker"}},[s._v("window.showOpenFilePicker")]),s._v("api如果不支持要么是浏览器不支持这个API要么是因为页面非https协议你可以按F12或者在页面通过鼠标右键菜单中的【检查】打开浏览器控制台在其中的【控制台】或【console】tab中输入"),e("code",[s._v("window.showOpenFilePicker")]),s._v("按回车,如果返回"),e("code",[s._v("undefined")]),s._v("则代表不支持如果返回的不是这个而页面依旧提示提示浏览器不支持或者非https协议那么可以提交issue或者联系作者。")]),e("h2",[s._v("5.引入simple-mind-map报错报错信息如下")]),e("img",{staticStyle:{width:"850px"},attrs:{src:v("bff3")}}),e("p",[s._v("这是因为你的构建环境不支持该js语法该语法出自"),e("code",[s._v("@svgdotjs/svg.js")]),s._v("库,解决方法如下:")]),e("p",[s._v("1.手动降低"),e("code",[s._v("@svgdotjs/svg.js")]),s._v("库的版本,你可以在你的项目中手动安装低版本,比如:"),e("code",[s._v("npm i @svgdotjs/svg.js@3.2.0")])]),e("p",[s._v("2.不降低版本的话,可以通过修改你的构建工具的相关配置,修改"),e("code",[s._v("babel")]),s._v("的配置,让它编译一下"),e("code",[s._v("node_modules")]),s._v("中的"),e("code",[s._v("simple-mind-map")]),s._v("库,或"),e("code",[s._v("@svgdotjs/svg.js")]),s._v("库,如果用的是"),e("code",[s._v("vue-cli")]),s._v("或"),e("code",[s._v("vite")]),s._v(",它们也直接提供了相关配置。另外需要安装编译该语法的"),e("code",[s._v("babel")]),s._v("插件,并且配置到"),e("code",[s._v("babel")]),s._v("的配置文件中:")]),e("p",[e("code",[s._v("@babel/plugin-proposal-nullish-coalescing-operator")]),s._v("、"),e("code",[s._v("@babel/plugin-proposal-optional-chaining")]),s._v("。")])])}],t={},i=t,d=v("2877"),n=Object(d["a"])(i,o,_,!1,null,null,null);e["default"]=n.exports},bff3:function(s,e,v){s.exports=v.p+"img/错误.jpg"}}]);

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

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

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

File diff suppressed because one or more lines are too long

1
dist/js/chunk-2d0e2326.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-2d0f09d5.js vendored Normal file
View File

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0f09d5"],{"9cb7":function(s,e,n){"use strict";n.r(e);var a=function(){var s=this;s._self._c;return s._m(0)},d=[function(){var s=this,e=s._self._c;return e("div",[e("h1",[s._v("如何通过代码激活节点")]),e("p",[s._v("默认可以通过鼠标单击节点来激活单个节点鼠标拖拽或按住Ctrl键单击进行激活多个节点那么如何通过代码来激活单个或多个节点达到和鼠标操作一样的效果呢请往下看。")]),e("h2",[s._v("激活单个节点")]),e("p",[s._v("如果已经获取到节点实例,那么直接调用该节点实例的"),e("code",[s._v("active()")]),s._v("方法即可:")]),e("pre",{staticClass:"hljs"},[e("code",[s._v("node.active()\n")])]),e("p",[s._v("如果只知道节点"),e("code",[s._v("uid")]),s._v(",那么可以先获取节点实例,再调激活的方法:")]),e("pre",{staticClass:"hljs"},[e("code",[e("span",{staticClass:"hljs-keyword"},[s._v("const")]),s._v(" node = mindMap.renderer.findNodeByUid("),e("span",{staticClass:"hljs-string"},[s._v("'uid'")]),s._v(")\nnode.active()\n")])]),e("h2",[s._v("激活多个节点")]),e("p",[s._v("如果要激活多个节点,在"),e("code",[s._v("v0.10.6")]),s._v("版本以前需要这么做:")]),e("pre",{staticClass:"hljs"},[e("code",[s._v(";[id1, id2, id3].forEach("),e("span",{staticClass:"hljs-function"},[e("span",{staticClass:"hljs-params"},[s._v("id")]),s._v(" =>")]),s._v(" {\n "),e("span",{staticClass:"hljs-comment"},[s._v("// 获取节点实例")]),s._v("\n "),e("span",{staticClass:"hljs-keyword"},[s._v("const")]),s._v(" node = mindMap.renderer.findNodeByUid(id)\n "),e("span",{staticClass:"hljs-comment"},[s._v("// 手动派发节点激活前事件")]),s._v("\n mindMap.emit(\n "),e("span",{staticClass:"hljs-string"},[s._v("'before_node_active'")]),s._v(",\n node,\n mindMap.renderer.activeNodeList\n )\n "),e("span",{staticClass:"hljs-comment"},[s._v("// 激活节点,并将该节点添加到激活节点列表里")]),s._v("\n mindMap.renderer.addNodeToActiveList(node, "),e("span",{staticClass:"hljs-literal"},[s._v("true")]),s._v(")\n "),e("span",{staticClass:"hljs-comment"},[s._v("// 手动派发节点激活事件")]),s._v("\n mindMap.renderer.emitNodeActiveEvent(node)\n})\n")])]),e("p",[s._v("在"),e("code",[s._v("v0.10.6+")]),s._v("版本,"),e("code",[s._v("render")]),s._v("实例新增了激活多个节点的方法,所以可以直接使用:")]),e("pre",{staticClass:"hljs"},[e("code",[e("span",{staticClass:"hljs-keyword"},[s._v("const")]),s._v(" nodeList = [id1, id2, id3, id4].map("),e("span",{staticClass:"hljs-function"},[e("span",{staticClass:"hljs-params"},[s._v("id")]),s._v(" =>")]),s._v(" {\n "),e("span",{staticClass:"hljs-keyword"},[s._v("return")]),s._v(" mindMap.renderer.findNodeByUid(id)\n})\nmindMap.renderer.activeMultiNode(nodeList)\n")])]),e("h2",[s._v("取消激活所有节点")]),e("p",[s._v("取消激活所有节点可以直接调用"),e("code",[s._v("render")]),s._v("实例的方法:")]),e("pre",{staticClass:"hljs"},[e("code",[s._v("mindMap.renderer.clearActiveNode()\n")])]),e("p",[s._v("这个方法不会派发"),e("code",[s._v("before_node_active")]),s._v("事件,所以如果需要你可以自己手动派发一下。")]),e("h2",[s._v("取消激活指定节点")]),e("p",[s._v("要取消激活指定的节点,在"),e("code",[s._v("v0.10.6")]),s._v("版本以前需要这么做:")]),e("pre",{staticClass:"hljs"},[e("code",[s._v(";[id1, id2, id3, id4].forEach("),e("span",{staticClass:"hljs-function"},[e("span",{staticClass:"hljs-params"},[s._v("id")]),s._v(" =>")]),s._v(" {\n "),e("span",{staticClass:"hljs-keyword"},[s._v("const")]),s._v(" node = mindMap.renderer.findNodeByUid(id)\n mindMap.renderer.removeNodeFromActiveList(node)\n mindMap.renderer.emitNodeActiveEvent("),e("span",{staticClass:"hljs-literal"},[s._v("null")]),s._v(")\n})\n")])]),e("p",[s._v("在"),e("code",[s._v("v0.10.6+")]),s._v("版本,"),e("code",[s._v("render")]),s._v("实例新增了取消激活多个节点的方法,所以可以直接使用:")]),e("pre",{staticClass:"hljs"},[e("code",[e("span",{staticClass:"hljs-keyword"},[s._v("const")]),s._v(" nodeList = [id1, id2, id3, id4].map("),e("span",{staticClass:"hljs-function"},[e("span",{staticClass:"hljs-params"},[s._v("id")]),s._v(" =>")]),s._v(" {\n "),e("span",{staticClass:"hljs-keyword"},[s._v("return")]),s._v(" mindMap.renderer.findNodeByUid(id)\n})\nmindMap.renderer.cancelActiveMultiNode(nodeList)\n")])])])}],i={},t=i,v=n("2877"),r=Object(v["a"])(t,a,d,!1,null,null,null);e["default"]=r.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/js/chunk-3977effe.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/chunk-47ab6502.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/js/chunk-4f3dd472.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-6a164532.js vendored Normal file

File diff suppressed because one or more lines are too long

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

View File

@@ -9,7 +9,7 @@
})
} catch (error) {
console.log(error)
}</script><link href="dist/css/chunk-vendors.css?dca8675d85e0a079cc96" rel="stylesheet"><link href="dist/css/app.css?dca8675d85e0a079cc96" 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 = () => {
}</script><link href="dist/css/chunk-vendors.css?5679494894bc21e15926" rel="stylesheet"><link href="dist/css/app.css?5679494894bc21e15926" 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({
@@ -74,4 +74,4 @@
// 可以通过window.$bus.$on()来监听应用的一些事件
// 实例化页面
window.initApp()
}</script><script src="dist/js/chunk-vendors.js?dca8675d85e0a079cc96"></script><script src="dist/js/app.js?dca8675d85e0a079cc96"></script></body></html>
}</script><script src="dist/js/chunk-vendors.js?5679494894bc21e15926"></script><script src="dist/js/app.js?5679494894bc21e15926"></script></body></html>

BIN
qrcode.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -31,7 +31,7 @@ MindMap.iconList = icons.nodeIconList
MindMap.constants = constants
MindMap.themes = themes
MindMap.defaultTheme = defaultTheme
MindMap.version = '0.10.3'
MindMap.version = '0.10.6'
MindMap.usePlugin(MiniMap)
.usePlugin(Watermark)

View File

@@ -194,7 +194,7 @@ class MindMap {
this.renderer.reRender = true // 标记为重新渲染
this.renderer.clearCache() // 清空节点缓存池
this.clearDraw() // 清空画布
this.render(callback, (source = ''))
this.render(callback, source)
}
// 获取或更新容器尺寸位置信息
@@ -208,8 +208,14 @@ class MindMap {
// 容器尺寸变化,调整尺寸
resize() {
const oldWidth = this.width
const oldHeight = this.height
this.getElRectInfo()
this.svg.size(this.width, this.height)
if (oldWidth !== this.width || oldHeight !== this.height) {
// 如果画布宽高改变了需要触发一次渲染
this.render()
}
this.emit('resize')
}
@@ -288,7 +294,9 @@ class MindMap {
// 更新配置
updateConfig(opt = {}) {
this.emit('before_update_config', this.opt)
this.opt = this.handleOpt(merge.all([defaultOpt, this.opt, opt]))
this.emit('after_update_config', this.opt)
}
// 获取当前布局结构
@@ -379,6 +387,9 @@ class MindMap {
// 导出
async export(...args) {
try {
if (!this.doExport) {
throw new Error('请注册Export插件')
}
let result = await this.doExport.export(...args)
return result
} catch (error) {
@@ -399,7 +410,9 @@ class MindMap {
if (![CONSTANTS.MODE.READONLY, CONSTANTS.MODE.EDIT].includes(mode)) {
return
}
this.opt.readonly = mode === CONSTANTS.MODE.READONLY
const isReadonly = mode === CONSTANTS.MODE.READONLY
if (isReadonly === this.opt.readonly) return
this.opt.readonly = isReadonly
if (this.opt.readonly) {
// 取消当前激活的元素
this.execCommand('CLEAR_ACTIVE_NODE')
@@ -416,6 +429,11 @@ class MindMap {
addContentToFooter,
node
} = {}) {
const { watermarkConfig, openPerformance } = this.opt
// 如果开启了性能模式,那么需要先渲染所有节点
if (openPerformance) {
this.renderer.forceLoadNode(node)
}
const { cssTextList, header, headerHeight, footer, footerHeight } =
handleGetSvgDataExtraContent({
addContentToHeader,
@@ -459,7 +477,7 @@ class MindMap {
if (!ignoreWatermark && hasWatermark) {
this.watermark.isInExport = true
// 是否是仅导出时需要水印
const { onlyExport } = this.opt.watermarkConfig
const { onlyExport } = watermarkConfig
// 是否需要重新绘制水印
const needReDrawWatermark =
rect.width > origWidth || rect.height > origHeight
@@ -569,10 +587,7 @@ class MindMap {
this.emit('beforeDestroy')
// 清除节点编辑框
this.renderer.textEdit.hideEditTextBox()
// 清除关联线文字编辑框
if (this.associativeLine) {
this.associativeLine.hideEditTextBox()
}
this.renderer.textEdit.removeTextEditEl()
// 移除插件
;[...MindMap.pluginList].forEach(plugin => {
if (

View File

@@ -1,14 +1,14 @@
{
"name": "simple-mind-map",
"version": "0.10.0-fix.1",
"version": "0.10.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "0.10.0-fix.1",
"version": "0.10.4",
"license": "MIT",
"dependencies": {
"@svgdotjs/svg.js": "^3.0.16",
"@svgdotjs/svg.js": "^3.2.0",
"deepmerge": "^1.5.2",
"eventemitter3": "^4.0.7",
"jszip": "^3.10.1",

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.10.3",
"version": "0.10.6",
"description": "一个简单的web在线思维导图",
"authors": [
{
@@ -28,7 +28,7 @@
"module": "index.js",
"main": "./dist/simpleMindMap.umd.min.js",
"dependencies": {
"@svgdotjs/svg.js": "^3.0.16",
"@svgdotjs/svg.js": "^3.2.0",
"deepmerge": "^1.5.2",
"eventemitter3": "^4.0.7",
"jszip": "^3.10.1",

View File

@@ -326,7 +326,9 @@ export const nodeDataNoStylePropList = [
'attachmentUrl',
'attachmentName',
'notation',
'outerFrame'
'outerFrame',
'number',
'range'
]
// 错误类型

View File

@@ -86,6 +86,8 @@ export const defaultOpt = {
maxHistoryCount: 500,
// 是否一直显示节点的展开收起按钮,默认为鼠标移上去和激活时才显示
alwaysShowExpandBtn: false,
// 不显示展开收起按钮优先级比alwaysShowExpandBtn配置高
notShowExpandBtn: false,
// 扩展节点可插入的图标
iconList: [
// {
@@ -228,6 +230,14 @@ export const defaultOpt = {
// 自定义超链接的跳转
// 如果不传默认会以新窗口的方式打开超链接可以传递一个函数函数接收两个参数link超链接的url、node所属节点实例只要传递了函数就会阻止默认的跳转
customHyperlinkJump: null,
// 是否开启性能模式默认情况下所有节点都会直接渲染无论是否处于画布可视区域这样当节点数量比较多时1000+会比较卡如果你的数据量比较大那么可以通过该配置开启性能模式即只渲染画布可视区域内的节点超出的节点不渲染这样会大幅提高渲染速度当然同时也会带来一些其他问题比如1.当拖动或是缩放画布时会实时计算并渲染未节点的节点所以会带来一定卡顿2.导出图片、svg、pdf时需要先渲染全部节点所以会比较慢3.其他目前未发现的问题
openPerformance: false,
// 性能优化模式配置
performanceConfig: {
time: 250, // 当视图改变后多久刷新一次节点单位ms
padding: 100, // 超出画布四周指定范围内依旧渲染节点
removeNodeWhenOutCanvas: true // 节点移除画布可视区域后从画布删除
},
// 【Select插件】
// 多选节点时鼠标移动到边缘时的画布移动偏移量

View File

@@ -104,7 +104,7 @@ class Command {
return
}
const lastData =
this.history.length > 0 ? this.history[this.history.length - 1] : null
this.history.length > 0 ? this.history[this.activeHistoryIndex] : null
const data = this.getCopyData()
// 此次数据和上次一样则不重复添加
if (lastData === data) return

View File

@@ -99,7 +99,6 @@ class Event extends EventEmitter {
// 鼠标按下事件
onMousedown(e) {
e.preventDefault()
// 鼠标左键
if (e.which === 1) {
this.isLeftMousedown = true
@@ -115,7 +114,6 @@ class Event extends EventEmitter {
// 鼠标移动事件
onMousemove(e) {
e.preventDefault()
let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
this.mousemovePos.x = e.clientX
this.mousemovePos.y = e.clientY

View File

@@ -32,7 +32,8 @@ import {
checkIsNodeStyleDataKey,
removeRichTextStyes,
formatGetNodeGeneralization,
sortNodeList
sortNodeList,
throttle
} from '../../utils'
import { shapeList } from './node/Shape'
import { lineStyleProps } from '../../themes/default'
@@ -144,13 +145,54 @@ class Render {
if (!this.mindMap.opt.enableDblclickBackToRootNode) return
this.setRootNodeCenter()
})
// let timer = null
// this.mindMap.on('view_data_change', () => {
// clearTimeout(timer)
// timer = setTimeout(() => {
// this.render()
// }, 300)
// })
// 性能模式
this.performanceMode()
}
// 性能模式,懒加载节点
performanceMode() {
const { openPerformance, performanceConfig } = this.mindMap.opt
const onViewDataChange = throttle(() => {
if (this.root) {
this.mindMap.emit('node_tree_render_start')
this.root.render(
() => {
this.mindMap.emit('node_tree_render_end')
},
false,
true
)
}
}, performanceConfig.time)
let lastOpen = false
this.mindMap.on('before_update_config', opt => {
lastOpen = opt.openPerformance
})
this.mindMap.on('after_update_config', opt => {
if (opt.openPerformance && !lastOpen) {
// 动态开启性能模式
this.mindMap.on('view_data_change', onViewDataChange)
this.forceLoadNode()
}
if (!opt.openPerformance && lastOpen) {
// 动态关闭性能模式
this.mindMap.off('view_data_change', onViewDataChange)
this.forceLoadNode()
}
})
if (!openPerformance) return
this.mindMap.on('view_data_change', onViewDataChange)
}
// 强制渲染节点,不考虑是否在画布可视区域内
forceLoadNode(node) {
node = node || this.root
if (node) {
this.mindMap.emit('node_tree_render_start')
node.render(() => {
this.mindMap.emit('node_tree_render_end')
}, true)
}
}
// 注册命令
@@ -453,6 +495,7 @@ class Render {
}
this.mindMap.emit('node_tree_render_start')
// 计算布局
this.root = null
this.layout.doLayout(root => {
// 删除本次渲染时不再需要的节点
Object.keys(this.lastNodeCache).forEach(uid => {
@@ -551,6 +594,26 @@ class Render {
this.activeNodeList.splice(index, 1)
}
// 手动激活多个节点激活单个节点请直接调用节点实例的active()方法
activeMultiNode(nodeList = []) {
nodeList.forEach(node => {
// 手动派发节点激活前事件
this.mindMap.emit('before_node_active', node, this.activeNodeList)
// 激活节点,并将该节点添加到激活节点列表里
this.addNodeToActiveList(node, true)
// 手动派发节点激活事件
this.emitNodeActiveEvent(node)
})
}
// 手动取消激活多个节点
cancelActiveMultiNode(nodeList = []) {
nodeList.forEach(node => {
this.removeNodeFromActiveList(node)
this.emitNodeActiveEvent(null)
})
}
// 检索某个节点在激活列表里的索引
findActiveNodeIndex(node) {
return getNodeIndexInNodeList(node, this.activeNodeList)
@@ -566,6 +629,15 @@ class Render {
if (!node.getData('isActive')) {
this.addNodeToActiveList(node)
}
// 概要节点
if (node._generalizationList && node._generalizationList.length > 0) {
node._generalizationList.forEach(item => {
const gNode = item.generalizationNode
if (!gNode.getData('isActive')) {
this.addNodeToActiveList(gNode)
}
})
}
},
null,
true,
@@ -1221,6 +1293,10 @@ class Render {
// 如果是富文本模式,那么某些层级变化需要更新样式
checkNodeLayerChange(node, toNode, toNodeIsParent = false) {
if (this.mindMap.richText) {
// 如果设置了自定义样式那么不需要更新
if (this.mindMap.richText.checkNodeHasCustomRichTextStyle(node)) {
return
}
const toIndex = toNodeIsParent ? toNode.layerIndex + 1 : toNode.layerIndex
let nodeLayerChanged =
(node.layerIndex === 1 && toIndex !== 1) ||
@@ -1419,6 +1495,9 @@ class Render {
this.checkNodeLayerChange(item, toNode, true)
this.removeNodeFromActiveList(item)
removeFromParentNodeData(item)
toNode.setData({
expand: true
})
toNode.nodeData.children.push(item.nodeData)
})
this.emitNodeActiveEvent()
@@ -1432,10 +1511,24 @@ class Render {
return
}
this.activeNodeList.forEach(node => {
node.setData({
expand: true
})
node.nodeData.children.push(
...data.map(item => {
const newData = simpleDeepClone(item)
createUidForAppointNodes([newData], true)
createUidForAppointNodes([newData], true, node => {
// 可能跨层级复制,那么富文本样式需要更新
if (this.mindMap.richText) {
// 如果设置了自定义样式那么不需要更新
if (
this.mindMap.richText.checkNodeHasCustomRichTextStyle(node.data)
) {
return
}
node.data.resetRichText = true
}
})
return newData
})
)
@@ -1881,7 +1974,7 @@ class Render {
const generalizationList = formatGetNodeGeneralization(node.data)
generalizationList.forEach(item => {
if (item.uid === uid) {
parentsList = parent ? [...cache[parent.data.uid], parent] : []
parentsList = parent ? [...cache[parent.data.uid], parent, node] : []
isGeneralization = true
}
})
@@ -1992,6 +2085,7 @@ class Render {
// 关闭高亮
closeHighlightNode() {
if (!this.highlightBoxNode) return
this.highlightBoxNode.remove()
}
}

View File

@@ -280,6 +280,17 @@ export default class TextEdit {
this.cacheEditingText = ''
}
// 删除文本编辑元素
removeTextEditEl() {
if (this.mindMap.richText) {
this.mindMap.richText.removeTextEditEl()
return
}
if (!this.textEditNode) return
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
targetNode.removeChild(this.textEditNode)
}
// 获取当前正在编辑的内容
getEditText() {
return getStrWithBrFromHtml(this.textEditNode.innerHTML)

View File

@@ -1,6 +1,6 @@
import Style from './Style'
import Shape from './Shape'
import { G, Rect } from '@svgdotjs/svg.js'
import { G, Rect, Text } from '@svgdotjs/svg.js'
import nodeGeneralizationMethods from './nodeGeneralization'
import nodeExpandBtnMethods from './nodeExpandBtn'
import nodeCommandWrapsMethods from './nodeCommandWraps'
@@ -82,6 +82,7 @@ class Node {
this.noteEl = null
this.noteContentIsShow = false
this._attachmentData = null
this._numberData = null
this._prefixData = null
this._postfixData = null
this._expandBtn = null
@@ -105,6 +106,8 @@ class Node {
// 概要节点的宽高
this._generalizationNodeWidth = 0
this._generalizationNodeHeight = 0
// 编号字符
this.number = opt.number || ''
// 各种文字信息的间距
this.textContentItemMargin = this.mindMap.opt.textContentMargin
// 图片和文字节点的间距
@@ -215,6 +218,9 @@ class Node {
this._tagData = this.createTagNode()
this._noteData = this.createNoteNode()
this._attachmentData = this.createAttachmentNode()
if (this.mindMap.numbers) {
this._numberData = this.mindMap.numbers.createNumberContent(this)
}
this._prefixData = createNodePrefixContent
? createNodePrefixContent(this)
: null
@@ -233,7 +239,8 @@ class Node {
getSize() {
this.customLeft = this.getData('customLeft') || undefined
this.customTop = this.getData('customTop') || undefined
this.updateGeneralization()
// 这里不要更新概要,不然即使概要没修改,每次也会重新渲染
// this.updateGeneralization()
this.createNodeData()
let { width, height } = this.getNodeRect()
// 判断节点尺寸是否有变化
@@ -267,6 +274,11 @@ class Node {
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
this._rectInfo.imgContentHeight = imgContentHeight = this._imgData.height
}
// 编号内容
if (this._numberData) {
textContentWidth += this._numberData.width
textContentHeight = Math.max(textContentHeight, this._numberData.height)
}
// 自定义前置内容
if (this._prefixData) {
textContentWidth += this._prefixData.width
@@ -363,6 +375,7 @@ class Node {
// 定位节点内容
layout() {
if (!this.group) return
// 清除之前的内容
this.group.clear()
const { hoverRectPadding, tagPosition } = this.mindMap.opt
@@ -417,6 +430,14 @@ class Node {
// 内容节点
let textContentNested = new G()
let textContentOffsetX = 0
// 编号内容
if (this._numberData) {
this._numberData.node
.x(textContentOffsetX)
.y((textContentHeight - this._numberData.height) / 2)
textContentNested.add(this._numberData.node)
textContentOffsetX += this._numberData.width + textContentItemMargin
}
// 自定义前置内容
if (this._prefixData) {
const foreignObject = createForeignObjectNode({
@@ -528,6 +549,7 @@ class Node {
textContentNested.add(foreignObject)
textContentOffsetX += this._postfixData.width
}
this.group.add(textContentNested)
// 文字内容整体
textContentNested.translate(
width / 2 - textContentNested.bbox().width / 2,
@@ -535,7 +557,6 @@ class Node {
imgHeight + // 图片高度
(imgHeight > 0 && textContentHeight > 0 ? this.blockContentMargin : 0) // 和图片的间距
)
this.group.add(textContentNested)
addHoverNode()
this.mindMap.emit('node_layout_end', this)
}
@@ -683,63 +704,41 @@ class Node {
}
// 更新节点
update() {
update(forceRender) {
if (!this.group) {
return
}
this.updateNodeActiveClass()
let { alwaysShowExpandBtn } = this.mindMap.opt
const childrenLength = this.nodeData.children.length
if (alwaysShowExpandBtn) {
// 需要移除展开收缩按钮
if (this._expandBtn && childrenLength <= 0) {
this.removeExpandBtn()
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
// 不显示展开收起按钮则不需要处理
if (!notShowExpandBtn) {
const childrenLength = this.nodeData.children.length
if (alwaysShowExpandBtn) {
// 需要移除展开收缩按钮
if (this._expandBtn && childrenLength <= 0) {
this.removeExpandBtn()
} else {
// 更新展开收起按钮
this.renderExpandBtn()
}
} else {
// 更新展开收起按钮
this.renderExpandBtn()
}
} else {
let { isActive, expand } = this.getData()
// 展开状态且非激活状态,且当前鼠标不在它上面,才隐藏
if (childrenLength <= 0) {
this.removeExpandBtn()
} else if (expand && !isActive && !this._isMouseenter) {
this.hideExpandBtn()
} else {
this.showExpandBtn()
let { isActive, expand } = this.getData()
// 展开状态且非激活状态,且当前鼠标不在它上面,才隐藏
if (childrenLength <= 0) {
this.removeExpandBtn()
} else if (expand && !isActive && !this._isMouseenter) {
this.hideExpandBtn()
} else {
this.showExpandBtn()
}
}
}
// 更新概要
this.renderGeneralization()
this.renderGeneralization(forceRender)
// 更新协同头像
if (this.updateUserListNode) this.updateUserListNode()
// 更新节点位置
let t = this.group.transform()
// // 如果上次不在可视区内,且本次也不在,那么直接返回
// let { left: ox, top: oy } = this.getNodePosInClient(
// t.translateX,
// t.translateY
// )
// let oldIsInClient =
// ox > 0 && oy > 0 && ox < this.mindMap.width && oy < this.mindMap.height
// let { left: nx, top: ny } = this.getNodePosInClient(this.left, this.top)
// let newIsNotInClient =
// nx + this.width < 0 ||
// ny + this.height < 0 ||
// nx > this.mindMap.width ||
// ny > this.mindMap.height
// if (!oldIsInClient && newIsNotInClient) {
// if (!this.isHide) {
// this.isHide = true
// this.group.hide()
// }
// return
// }
// // 如果当前是隐藏状态,那么先显示
// if (this.isHide) {
// this.isHide = false
// this.group.show()
// }
// 如果节点位置没有变化,则返回
if (this.left === t.translateX && this.top === t.translateY) return
this.group.translate(this.left - t.translateX, this.top - t.translateY)
@@ -757,6 +756,17 @@ class Node {
}
}
// 判断节点是否可见
checkIsInClient(padding = 0) {
const { left: nx, top: ny } = this.getNodePosInClient(this.left, this.top)
return (
nx + this.width > 0 - padding &&
ny + this.height > 0 - padding &&
nx < this.mindMap.width + padding &&
ny < this.mindMap.height + padding
)
}
// 重新渲染节点,即重新创建节点内容、计算节点大小、计算节点内容布局、更新展开收起按钮,概要及位置
reRender() {
let sizeChange = this.getSize()
@@ -785,32 +795,46 @@ class Node {
}
}
// 递归渲染
render(callback = () => {}) {
// 递归渲染
// forceRender强制渲染无论是否处于画布可视区域
// async异步渲染
render(callback = () => {}, forceRender = false, async = false) {
// 节点
// 重新渲染连线
this.renderLine()
if (!this.group) {
// 创建组
this.group = new G()
this.group.addClass('smm-node')
this.group.css({
cursor: 'default'
})
this.bindGroupEvent()
this.nodeDraw.add(this.group)
this.layout()
this.update()
} else {
if (!this.nodeDraw.has(this.group)) {
const { openPerformance, performanceConfig } = this.mindMap.opt
// 强制渲染、或没有开启性能模式、或不在画布可视区域内不渲染节点内容
// 根节点不进行懒加载,始终渲染,因为滚动条插件依赖根节点进行计算
if (
forceRender ||
!openPerformance ||
this.checkIsInClient(performanceConfig.padding) ||
this.isRoot
) {
if (!this.group) {
// 创建组
this.group = new G()
this.group.addClass('smm-node')
this.group.css({
cursor: 'default'
})
this.bindGroupEvent()
this.nodeDraw.add(this.group)
}
if (this.needLayout) {
this.needLayout = false
this.layout()
this.update(forceRender)
} else {
if (!this.nodeDraw.has(this.group)) {
this.nodeDraw.add(this.group)
}
if (this.needLayout) {
this.needLayout = false
this.layout()
}
this.updateExpandBtnPlaceholderRect()
this.update(forceRender)
}
this.updateExpandBtnPlaceholderRect()
this.update()
} else if (openPerformance && performanceConfig.removeNodeWhenOutCanvas) {
this.removeSelf()
}
// 子节点
if (
@@ -820,12 +844,23 @@ class Node {
) {
let index = 0
this.children.forEach(item => {
item.render(() => {
index++
if (index >= this.children.length) {
callback()
}
})
const renderChild = () => {
item.render(
() => {
index++
if (index >= this.children.length) {
callback()
}
},
forceRender,
async
)
}
if (async) {
setTimeout(renderChild, 0)
} else {
renderChild()
}
})
} else {
callback()
@@ -840,6 +875,13 @@ class Node {
}
}
// 删除自身,只是从画布删除,节点容器还在,后续还可以重新插回画布
removeSelf() {
if (!this.group) return
this.group.remove()
this.removeGeneralization()
}
// 递归删除,只是从画布删除,节点容器还在,后续还可以重新插回画布
remove() {
if (!this.group) return
@@ -856,6 +898,10 @@ class Node {
// 销毁节点,不但会从画布删除,而且原节点直接置空,后续无法再插回画布
destroy() {
this.removeLine()
if (this.parent) {
this.parent.removeLine()
}
if (!this.group) return
if (this.emptyUser) {
this.emptyUser()
@@ -863,11 +909,7 @@ class Node {
this.resetWhenDelete()
this.group.remove()
this.removeGeneralization()
this.removeLine()
this.group = null
if (this.parent) {
this.parent.removeLine()
}
this.style.onRemove()
}
@@ -1249,6 +1291,11 @@ class Node {
})
return newNode
}
// 创建SVG文本节点
createSvgTextNode(text = '') {
return new Text().text(text)
}
}
export default Node

View File

@@ -420,6 +420,9 @@ function createNoteNode() {
this.mindMap.opt.customNoteContentShow.hide()
}
})
node.on('click', e => {
this.mindMap.emit('node_note_click', this, e, node)
})
return {
node,
width: iconSize,

View File

@@ -1,5 +1,5 @@
import btnsSvg from '../../../svg/btns'
import { SVG, Circle, G } from '@svgdotjs/svg.js'
import { SVG, Circle, G, Text } from '@svgdotjs/svg.js'
// 创建展开收起按钮的内容节点
function createExpandNodeContent() {
@@ -10,9 +10,7 @@ function createExpandNodeContent() {
// 根据配置判断是否显示数量按钮
if (this.mindMap.opt.isShowExpandNum) {
// 展开的节点
this._openExpandNode = SVG()
.text()
.size(this.expandBtnSize, this.expandBtnSize)
this._openExpandNode = new Text()
// 文本垂直居中
this._openExpandNode.attr({
'text-anchor': 'middle',
@@ -81,7 +79,7 @@ function updateExpandBtnNode() {
// 计算子节点数量
let count = this.sumNode(this.nodeData.children)
count = expandBtnNumHandler(count)
node.text(count)
node.text(String(count))
} else {
this._fillExpandNode.stroke('none')
}
@@ -126,11 +124,7 @@ function renderExpandBtn() {
this._expandBtn.on('click', e => {
e.stopPropagation()
// 展开收缩
this.mindMap.execCommand(
'SET_NODE_EXPAND',
this,
!this.getData('expand')
)
this.mindMap.execCommand('SET_NODE_EXPAND', this, !this.getData('expand'))
this.mindMap.emit('expand_btn_click', this)
})
this._expandBtn.on('dblclick', e => {
@@ -154,7 +148,8 @@ function removeExpandBtn() {
// 显示展开收起按钮
function showExpandBtn() {
if (this.mindMap.opt.alwaysShowExpandBtn) return
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (alwaysShowExpandBtn || notShowExpandBtn) return
setTimeout(() => {
this.renderExpandBtn()
}, 0)
@@ -162,7 +157,8 @@ function showExpandBtn() {
// 隐藏展开收起按钮
function hideExpandBtn() {
if (this.mindMap.opt.alwaysShowExpandBtn || this._isMouseenter) return
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (alwaysShowExpandBtn || this._isMouseenter || notShowExpandBtn) return
// 非激活状态且展开状态鼠标移出才隐藏按钮
let { isActive, expand } = this.getData()
if (!isActive && expand) {

View File

@@ -10,8 +10,9 @@ function renderExpandBtnPlaceholderRect() {
) {
return
}
// 默认显示展开按钮的情况下不需要渲染
if (!this.mindMap.opt.alwaysShowExpandBtn) {
// 默认显示展开按钮的情况下或不显示展开收起按钮的情况下不需要渲染
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn && !notShowExpandBtn) {
let { width, height } = this
if (!this._unVisibleRectRegionNode) {
this._unVisibleRectRegionNode = new Rect()

View File

@@ -85,7 +85,7 @@ function updateGeneralization() {
}
// 渲染概要节点
function renderGeneralization() {
function renderGeneralization(forceRender) {
if (this.isGeneralization) return
this.updateGeneralizationData()
const list = this.formatGetGeneralization()
@@ -100,7 +100,7 @@ function renderGeneralization() {
this.renderer.layout.renderGeneralization(this._generalizationList)
this._generalizationList.forEach(item => {
this.style.generalizationLine(item.generalizationLine)
item.generalizationNode.render()
item.generalizationNode.render(() => {}, forceRender)
})
}

View File

@@ -69,8 +69,37 @@ class Base {
}
}
// 获取节点编号信息
getNumberInfo({ parent, ancestors, layerIndex, index }) {
// 编号
const hasNumberPlugin = !!this.mindMap.numbers
const parentNumberStr =
hasNumberPlugin && parent && parent._node.number
? parent._node.number
: ''
const newNumberStr = hasNumberPlugin
? this.mindMap.numbers.getNodeNumberStr({
ancestors,
layerIndex,
num: index + 1,
parentNumberStr
})
: ''
return {
hasNumberPlugin,
newNumberStr
}
}
// 创建节点实例
createNode(data, parent, isRoot, layerIndex) {
createNode(data, parent, isRoot, layerIndex, index, ancestors) {
// 编号
const { hasNumberPlugin, newNumberStr } = this.getNumberInfo({
parent,
ancestors,
layerIndex,
index
})
// 创建节点
const uid = data.data.uid
let newNode = null
@@ -90,15 +119,25 @@ class Base {
}
this.cacheNode(data._node.uid, newNode)
this.checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(newNode)
// 判断编号是否改变
let isNumberChange = false
if (hasNumberPlugin) {
isNumberChange = this.mindMap.numbers.updateNumber(
newNode,
newNumberStr
)
}
// 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本等情况需要重新计算节点大小和布局
if (
this.checkIsNeedResizeSources() ||
isLayerTypeChange ||
newNode.getData('resetRichText')
newNode.getData('resetRichText') ||
isNumberChange
) {
newNode.getSize()
newNode.needLayout = true
}
this.checkGetGeneralizationChange(newNode)
} else if (
(this.lru.has(uid) || this.renderer.lastNodeCache[uid]) &&
!this.renderer.reRender
@@ -129,15 +168,25 @@ class Base {
const isResizeSource = this.checkIsNeedResizeSources()
// 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本,节点数据改变了等情况需要重新计算节点大小和布局
const isNodeDataChange = lastData !== JSON.stringify(data.data)
// 判断编号是否改变
let isNumberChange = false
if (hasNumberPlugin) {
isNumberChange = this.mindMap.numbers.updateNumber(
newNode,
newNumberStr
)
}
if (
isResizeSource ||
isNodeDataChange ||
isLayerTypeChange ||
newNode.getData('resetRichText')
newNode.getData('resetRichText') ||
isNumberChange
) {
newNode.getSize()
newNode.needLayout = true
}
this.checkGetGeneralizationChange(newNode)
} else {
// 创建新节点
const newUid = uid || createUid()
@@ -149,7 +198,8 @@ class Base {
draw: this.draw,
layerIndex,
isRoot,
parent: !isRoot ? parent._node : null
parent: !isRoot ? parent._node : null,
number: newNumberStr
})
// uid保存到数据上为了节点复用
data.data.uid = newUid
@@ -177,6 +227,27 @@ class Base {
return newNode
}
// 检查概要节点是否需要更新
checkGetGeneralizationChange(node) {
const generalizationList = node.getData('generalization')
if (
generalizationList &&
node._generalizationList &&
node._generalizationList.length > 0
) {
node._generalizationList.forEach((item, index) => {
const gNode = item.generalizationNode
const oldData = gNode.getData()
const newData = generalizationList[index]
if (newData && JSON.stringify(oldData) !== JSON.stringify(newData)) {
gNode.nodeData.data = newData
gNode.getSize()
gNode.needLayout = true
}
})
}
}
// 格式化节点位置
formatPosition(value, size, nodeSize) {
if (typeof value === 'number') {

View File

@@ -32,8 +32,8 @@ class CatalogOrganization extends Base {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
(cur, parent, isRoot, layerIndex, index, ancestors) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
@@ -204,7 +204,8 @@ class CatalogOrganization extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
let len = node.children.length

View File

@@ -36,9 +36,9 @@ class Fishbone extends Base {
walk(
this.renderer.renderTree,
null,
(node, parent, isRoot, layerIndex, index) => {
(node, parent, isRoot, layerIndex, index, ancestors) => {
// 创建节点
let newNode = this.createNode(node, parent, isRoot, layerIndex)
let newNode = this.createNode(node, parent, isRoot, layerIndex, index, ancestors)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
@@ -233,7 +233,8 @@ class Fishbone extends Base {
return []
}
let { top, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
let len = node.children.length

View File

@@ -35,8 +35,8 @@ class LogicalStructure extends Base {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
(cur, parent, isRoot, layerIndex, index, ancestors) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
newNode.sortIndex = sortIndex
sortIndex++
// 根节点定位在画布中心位置
@@ -174,7 +174,8 @@ class LogicalStructure extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
let marginX = this.getMarginX(node.layerIndex + 1)
@@ -215,7 +216,8 @@ class LogicalStructure extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
const { nodeUseLineStyle } = this.mindMap.themeConfig
@@ -246,7 +248,8 @@ class LogicalStructure extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
const {

View File

@@ -34,8 +34,8 @@ class MindMap extends Base {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex, index) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
(cur, parent, isRoot, layerIndex, index, ancestors) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
@@ -213,7 +213,8 @@ class MindMap extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
let marginX = this.getMarginX(node.layerIndex + 1)
@@ -256,7 +257,8 @@ class MindMap extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
const { nodeUseLineStyle } = this.mindMap.themeConfig
@@ -296,7 +298,8 @@ class MindMap extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
const {

View File

@@ -33,8 +33,8 @@ class OrganizationStructure extends Base {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
(cur, parent, isRoot, layerIndex, index, ancestors) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
@@ -182,7 +182,8 @@ class OrganizationStructure extends Base {
return []
}
let { left, top, width, height, expandBtnSize, isRoot } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
let x1 = left + width / 2

View File

@@ -34,8 +34,8 @@ class Timeline extends Base {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex, index) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
(cur, parent, isRoot, layerIndex, index, ancestors) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
@@ -232,7 +232,8 @@ class Timeline extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
let len = node.children.length

View File

@@ -34,8 +34,8 @@ class VerticalTimeline extends Base {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex, index) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
(cur, parent, isRoot, layerIndex, index, ancestors) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
@@ -234,7 +234,8 @@ class VerticalTimeline extends Base {
return []
}
let { expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
if (node.isRoot) {
@@ -293,7 +294,8 @@ class VerticalTimeline extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
node.children.forEach((item, index) => {
@@ -331,7 +333,8 @@ class VerticalTimeline extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
if (!this.mindMap.opt.alwaysShowExpandBtn) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn || notShowExpandBtn) {
expandBtnSize = 0
}
node.children.forEach((item, index) => {

View File

@@ -68,6 +68,7 @@ class AssociativeLine {
this.onNodeDragging = this.onNodeDragging.bind(this)
this.onNodeDragend = this.onNodeDragend.bind(this)
this.onControlPointMouseup = this.onControlPointMouseup.bind(this)
this.onBeforeDestroy = this.onBeforeDestroy.bind(this)
// 节点树渲染完毕后渲染连接线
this.mindMap.on('node_tree_render_end', this.renderAllLines)
@@ -76,6 +77,7 @@ class AssociativeLine {
// 监听画布和节点点击事件,用于清除当前激活的连接线
this.mindMap.on('draw_click', this.onDrawClick)
this.mindMap.on('node_click', this.onNodeClick)
this.mindMap.on('contextmenu', this.onDrawClick)
// 注册删除快捷键
this.mindMap.keyCommand.addShortcut('Del|Backspace', this.removeLine)
// 注册添加连接线的命令
@@ -89,6 +91,8 @@ class AssociativeLine {
this.mindMap.on('mouseup', this.onControlPointMouseup)
// 缩放事件
this.mindMap.on('scale', this.onScale)
// 实例销毁事件
this.mindMap.on('beforeDestroy', this.onBeforeDestroy)
}
// 解绑事件
@@ -97,6 +101,7 @@ class AssociativeLine {
this.mindMap.off('data_change', this.renderAllLines)
this.mindMap.off('draw_click', this.onDrawClick)
this.mindMap.off('node_click', this.onNodeClick)
this.mindMap.off('contextmenu', this.onDrawClick)
this.mindMap.keyCommand.removeShortcut('Del|Backspace', this.removeLine)
this.mindMap.command.remove('ADD_ASSOCIATIVE_LINE', this.addLine)
this.mindMap.off('mousemove', this.onMousemove)
@@ -104,14 +109,25 @@ class AssociativeLine {
this.mindMap.off('node_dragend', this.onNodeDragend)
this.mindMap.off('mouseup', this.onControlPointMouseup)
this.mindMap.off('scale', this.onScale)
this.mindMap.off('beforeDestroy', this.onBeforeDestroy)
}
// 实例销毁时清除关联线文字编辑框
onBeforeDestroy() {
this.hideEditTextBox()
this.removeTextEditEl()
}
// 画布点击事件
onDrawClick() {
if (this.isControlPointMousedown) {
return
// 取消创建关联线
if (this.isCreatingLine) {
this.cancelCreateLine()
}
// 取消激活关联线
if (!this.isControlPointMousedown) {
this.clearActiveLine()
}
this.clearActiveLine()
}
// 节点点击事件
@@ -207,7 +223,8 @@ class AssociativeLine {
associativeLineWidth,
associativeLineColor,
associativeLineActiveWidth,
associativeLineActiveColor
associativeLineActiveColor,
associativeLineDasharray
} = this.mindMap.themeConfig
// 箭头
this.markerPath
@@ -226,7 +243,7 @@ class AssociativeLine {
.stroke({
width: associativeLineWidth,
color: associativeLineColor,
dasharray: [6, 4]
dasharray: associativeLineDasharray || [6, 4]
})
.fill({ color: 'none' })
path.plot(pathStr)
@@ -325,8 +342,11 @@ class AssociativeLine {
// 创建连接线
createLine(fromNode) {
let { associativeLineWidth, associativeLineColor } =
this.mindMap.themeConfig
let {
associativeLineWidth,
associativeLineColor,
associativeLineDasharray
} = this.mindMap.themeConfig
if (this.isCreatingLine || !fromNode) return
this.front()
this.isCreatingLine = true
@@ -336,7 +356,7 @@ class AssociativeLine {
.stroke({
width: associativeLineWidth,
color: associativeLineColor,
dasharray: [6, 4]
dasharray: associativeLineDasharray || [6, 4]
})
.fill({ color: 'none' })
// 箭头
@@ -346,6 +366,16 @@ class AssociativeLine {
this.creatingLine.marker('end', this.marker)
}
// 取消创建关联线
cancelCreateLine() {
this.isCreatingLine = false
this.creatingStartNode = null
this.creatingLine.remove()
this.creatingLine = null
this.overlapNode = null
this.back()
}
// 鼠标移动事件
onMousemove(e) {
this.onControlPointMousemove(e)
@@ -420,12 +450,7 @@ class AssociativeLine {
if (this.overlapNode && this.overlapNode.getData('isActive')) {
this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, false)
}
this.isCreatingLine = false
this.creatingStartNode = null
this.creatingLine.remove()
this.creatingLine = null
this.overlapNode = null
this.back()
this.cancelCreateLine()
}
// 添加连接线

View File

@@ -40,6 +40,7 @@ class Demonstrate {
{ ...defaultConfig },
this.mindMap.opt.demonstrateConfig || {}
)
this.needRestorePerformanceMode = false
}
// 进入演示模式
@@ -56,6 +57,8 @@ class Demonstrate {
}
_enter() {
// 如果开启了性能模式,那么需要暂停
this.pausePerformanceMode()
// 添加演示用的临时的样式
this.addTmpStyles()
// 记录演示前的画布状态
@@ -97,9 +100,27 @@ class Demonstrate {
this.removeHighlightEl()
this.mindMap.command.recovery()
this.mindMap.keyCommand.recovery()
this.restorePerformanceMode()
this.mindMap.emit('exit_demonstrate')
}
// 暂停性能模式
pausePerformanceMode() {
const { openPerformance } = this.mindMap.opt
if (openPerformance) {
this.needRestorePerformanceMode = true
this.mindMap.opt.openPerformance = false
this.mindMap.renderer.forceLoadNode()
}
}
// 恢复性能模式
restorePerformanceMode() {
if (!this.needRestorePerformanceMode) return
this.mindMap.opt.openPerformance = true
this.mindMap.renderer.forceLoadNode()
}
// 添加临时的样式
addTmpStyles() {
this.tmpStyleEl = document.createElement('style')

View File

@@ -288,7 +288,8 @@ class Export {
handleNodeExport(node) {
if (node && node.getData('isActive')) {
node.deactivate()
if (!this.mindMap.opt.alwaysShowExpandBtn && node.getData('expand')) {
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
if (!alwaysShowExpandBtn && !notShowExpandBtn && node.getData('expand')) {
node.removeExpandBtn()
}
}

View File

@@ -297,6 +297,13 @@ class OuterFrame {
if (range[0] === -1 || range[1] === -1) return
const { left, top, width, height } =
getNodeListBoundingRect(nodeList)
if (
!Number.isFinite(left) ||
!Number.isFinite(top) ||
!Number.isFinite(width) ||
!Number.isFinite(height)
)
return
const el = this.createOuterFrameEl(
(left -
outerFramePaddingX -

View File

@@ -13,6 +13,7 @@ import {
nodeRichTextToTextWithWrap
} from '../utils'
import { CONSTANTS } from '../constants/constant'
import Node from '../core/render/node/Node'
let extended = false
@@ -142,14 +143,7 @@ class RichText {
}
extended = true
// 扩展quill的字体列表
const FontAttributor = Quill.import('attributors/class/font')
FontAttributor.whitelist = fontFamilyList
Quill.register(FontAttributor, true)
const FontStyle = Quill.import('attributors/style/font')
FontStyle.whitelist = fontFamilyList
Quill.register(FontStyle, true)
this.extendFont([])
// 扩展quill的字号列表
const SizeAttributor = Quill.import('attributors/class/size')
@@ -161,6 +155,20 @@ class RichText {
Quill.register(SizeStyle, true)
}
// 扩展字体列表
extendFont(list = [], cover = false) {
fontFamilyList = cover ? [...list] : [...fontFamilyList, ...list]
// 扩展quill的字体列表
const FontAttributor = Quill.import('attributors/class/font')
FontAttributor.whitelist = fontFamilyList
Quill.register(FontAttributor, true)
const FontStyle = Quill.import('attributors/style/font')
FontStyle.whitelist = fontFamilyList
Quill.register(FontStyle, true)
}
// 显示文本编辑控件
showEditText({ node, rect, isInserting, isFromKeyDown, isFromScale }) {
if (this.showTextEdit) {
@@ -278,6 +286,13 @@ class RichText {
this.cacheEditingText = ''
}
// 删除文本编辑框元素
removeTextEditEl() {
if (!this.textEditNode) return
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
targetNode.removeChild(this.textEditNode)
}
// 获取编辑区域的背景填充
getBackground(node) {
const gradientStyle = node.style.merge('gradientStyle')
@@ -666,6 +681,25 @@ class RichText {
}
}
// 检查指定节点是否存在自定义的富文本样式
checkNodeHasCustomRichTextStyle(node) {
const list = [
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'textDecoration',
'color'
]
const nodeData = node instanceof Node ? node.getData() : node
for (let i = 0; i < list.length; i++) {
if (nodeData[list[i]] !== undefined) {
return true
}
}
return false
}
// 将所有节点转换成非富文本节点
transformAllNodesToNormalNode() {
if (!this.mindMap.renderer.renderTree) return

View File

@@ -2,9 +2,11 @@ import {
bfsWalk,
getTextFromHtml,
isUndef,
replaceHtmlText
replaceHtmlText,
formatGetNodeGeneralization
} from '../utils/index'
import Node from '../core/render/node/Node'
import { CONSTANTS } from '../constants/constant'
// 搜索插件
class Search {
@@ -29,11 +31,14 @@ class Search {
bindEvent() {
this.onDataChange = this.onDataChange.bind(this)
this.onModeChange = this.onModeChange.bind(this)
this.mindMap.on('data_change', this.onDataChange)
this.mindMap.on('mode_change', this.onModeChange)
}
unBindEvent() {
this.mindMap.off('data_change', this.onDataChange)
this.mindMap.off('mode_change', this.onModeChange)
}
// 节点数据改变了,需要重新搜索
@@ -50,6 +55,19 @@ class Search {
this.searchText = ''
}
// 监听只读模式切换
onModeChange(mode) {
const isReadonly = mode === CONSTANTS.MODE.READONLY
// 如果是由只读模式切换为非只读模式,需要清除只读模式下的节点高亮
if (
!isReadonly &&
this.isSearching &&
this.matchNodeList[this.currentIndex]
) {
this.matchNodeList[this.currentIndex].closeHighlight()
}
}
// 搜索
search(text, callback = () => {}) {
if (isUndef(text)) return this.endSearch()
@@ -92,7 +110,7 @@ class Search {
: this.mindMap.renderer.renderTree
if (!tree) return
bfsWalk(tree, node => {
let { richText, text } = isOnlySearchCurrentRenderNodes
let { richText, text, generalization } = isOnlySearchCurrentRenderNodes
? node.getData()
: node.data
if (richText) {
@@ -101,6 +119,27 @@ class Search {
if (text.includes(this.searchText)) {
this.matchNodeList.push(node)
}
// 概要节点
const generalizationList = formatGetNodeGeneralization({
generalization
})
generalizationList.forEach(gNode => {
let { richText, text, uid } = gNode
if (
isOnlySearchCurrentRenderNodes &&
!this.mindMap.renderer.findNodeByUid(uid)
) {
return
}
if (richText) {
text = getTextFromHtml(text)
}
if (text.includes(this.searchText)) {
this.matchNodeList.push({
data: gNode
})
}
})
})
}
@@ -117,6 +156,15 @@ class Search {
} else {
this.currentIndex = 0
}
const { readonly } = this.mindMap.opt
// 只读模式下需要激活之前节点的高亮
if (readonly) {
this.matchNodeList.forEach(node => {
if (this.isNodeInstance(node)) {
node.closeHighlight()
}
})
}
const currentNode = this.matchNodeList[this.currentIndex]
this.notResetSearchText = true
const uid = this.isNodeInstance(currentNode)
@@ -129,7 +177,7 @@ class Search {
}
callback()
// 只读模式下节点无法激活,所以通过高亮的方式
if (this.mindMap.opt.readonly) {
if (readonly) {
node.highlight()
}
// 如果当前节点实例已经存在则不会触发data_change事件那么需要手动把标志复位

View File

@@ -180,7 +180,8 @@ class Select {
let miny = Math.min(this.mouseDownY, this.mouseMoveY)
let maxx = Math.max(this.mouseDownX, this.mouseMoveX)
let maxy = Math.max(this.mouseDownY, this.mouseMoveY)
bfsWalk(this.mindMap.renderer.root, node => {
const check = node => {
let { left, top, width, height } = node
let right = (left + width) * scaleX + translateX
let bottom = (top + height) * scaleY + translateY
@@ -201,6 +202,16 @@ class Select {
this.mindMap.renderer.removeNodeFromActiveList(node)
this.mindMap.renderer.emitNodeActiveEvent()
}
}
bfsWalk(this.mindMap.renderer.root, node => {
check(node)
// 概要节点
if (node._generalizationList && node._generalizationList.length > 0) {
node._generalizationList.forEach(item => {
check(item.generalizationNode)
})
}
})
}

View File

@@ -82,6 +82,13 @@ function showEditTextBox(g) {
}
}
// 删除文本编辑框元素
function removeTextEditEl() {
if (!this.textEditNode) return
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
targetNode.removeChild(this.textEditNode)
}
// 处理画布缩放
function onScale() {
this.hideEditTextBox()
@@ -178,6 +185,7 @@ export default {
styleText,
onScale,
showEditTextBox,
removeTextEditEl,
hideEditTextBox,
updateTextEditBoxPos,
renderText,

View File

@@ -42,6 +42,8 @@ export default {
associativeLineActiveWidth: 8,
// 关联线激活状态的颜色
associativeLineActiveColor: 'rgba(2, 167, 240, 1)',
// 关联线样式
associativeLineDasharray: [6, 4],
// 关联线文字颜色
associativeLineTextColor: 'rgb(51, 51, 51)',
// 关联线文字大小

View File

@@ -14,11 +14,12 @@ export const walk = (
afterCallback,
isRoot,
layerIndex = 0,
index = 0
index = 0,
ancestors = []
) => {
let stop = false
if (beforeCallback) {
stop = beforeCallback(root, parent, isRoot, layerIndex, index)
stop = beforeCallback(root, parent, isRoot, layerIndex, index, ancestors)
}
if (!stop && root.children && root.children.length > 0) {
let _layerIndex = layerIndex + 1
@@ -30,11 +31,13 @@ export const walk = (
afterCallback,
false,
_layerIndex,
nodeIndex
nodeIndex,
[...ancestors, root]
)
})
}
afterCallback && afterCallback(root, parent, isRoot, layerIndex, index)
afterCallback &&
afterCallback(root, parent, isRoot, layerIndex, index, ancestors)
}
// 广度优先遍历树
@@ -948,7 +951,11 @@ export const addDataToAppointNodes = (appointNodes, data = {}) => {
// 给指定的节点列表树数据添加uid会修改原数据
// createNewId默认为false即如果节点不存在uid的话会创建新的uid。如果传true那么无论节点数据原来是否存在uid都会创建新的uid
export const createUidForAppointNodes = (appointNodes, createNewId = false) => {
export const createUidForAppointNodes = (
appointNodes,
createNewId = false,
handle = null
) => {
const walk = list => {
list.forEach(node => {
if (!node.data) {
@@ -957,6 +964,7 @@ export const createUidForAppointNodes = (appointNodes, createNewId = false) => {
if (createNewId || isUndef(node.data.uid)) {
node.data.uid = createUid()
}
handle && handle(node)
if (node.children && node.children.length > 0) {
walk(node.children)
}
@@ -1382,22 +1390,24 @@ export const getNodeTreeBoundingRect = (
let minY = Infinity
let maxY = -Infinity
const walk = (root, isRoot) => {
if (!(isRoot && excludeSelf)) {
const { x, y, width, height } = root.group
.findOne('.smm-node-shape')
.rbox()
if (x < minX) {
minX = x
}
if (x + width > maxX) {
maxX = x + width
}
if (y < minY) {
minY = y
}
if (y + height > maxY) {
maxY = y + height
}
if (!(isRoot && excludeSelf) && root.group) {
try {
const { x, y, width, height } = root.group
.findOne('.smm-node-shape')
.rbox()
if (x < minX) {
minX = x
}
if (x + width > maxX) {
maxX = x + width
}
if (y < minY) {
minY = y
}
if (y + height > maxY) {
maxY = y + height
}
} catch (e) {}
}
if (!excludeGeneralization && root._generalizationList.length > 0) {
root._generalizationList.forEach(item => {

View File

@@ -1,6 +1,7 @@
import exampleData from 'simple-mind-map/example/exampleData'
import { simpleDeepClone } from 'simple-mind-map/src/utils/index'
import Vue from 'vue'
import vuexStore from '@/store'
const SIMPLE_MIND_MAP_DATA = 'SIMPLE_MIND_MAP_DATA'
const SIMPLE_MIND_MAP_LANG = 'SIMPLE_MIND_MAP_LANG'
@@ -36,6 +37,9 @@ export const getData = () => {
mindMapData = window.takeOverAppMethods.getMindMapData()
return mindMapData
}
if (vuexStore.state.isHandleLocalFile) {
return Vue.prototype.getCurrentData()
}
let store = localStorage.getItem(SIMPLE_MIND_MAP_DATA)
if (store === null) {
return simpleDeepClone(exampleData)
@@ -68,6 +72,9 @@ export const storeData = data => {
return
}
Vue.prototype.$bus.$emit('write_local_file', originData)
if (vuexStore.state.isHandleLocalFile) {
return
}
let dataStr = JSON.stringify(originData)
localStorage.setItem(SIMPLE_MIND_MAP_DATA, dataStr)
} catch (error) {
@@ -98,6 +105,9 @@ export const storeConfig = config => {
return
}
Vue.prototype.$bus.$emit('write_local_file', originData)
if (vuexStore.state.isHandleLocalFile) {
return
}
let dataStr = JSON.stringify(originData)
localStorage.setItem(SIMPLE_MIND_MAP_DATA, dataStr)
} catch (error) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -83,6 +83,10 @@ export const borderDasharrayList = [
{
name: 'Dotted6',
value: '1, 5'
},
{
name: 'Dotted7',
value: '6, 4'
}
]
@@ -493,3 +497,63 @@ export const downTypeList = [
desc: 'Plain text file'
}
]
// 编号类型列表
export const numberTypeList = [
{
name: 'None',
value: ''
},
{
name: '1, 2, 3',
value: 1
},
{
name: '1., 2., 3.',
value: 2
},
{
name: '(1), (2), (3)',
value: 3
},
{
name: 'a., b., c.',
value: 4
},
{
name: 'A., B., C.',
value: 5
},
{
name: 'i., ii., iii.',
value: 6
},
{
name: 'I., II., III.',
value: 7
},
{
name: '一、, 二、, 三、',
value: 8
}
]
// 编号层级列表
export const numberLevelList = [
{
name: '1 level',
value: 1
},
{
name: '2 level',
value: 2
},
{
name: '3 level',
value: 3
},
{
name: 'All level',
value: 0
}
]

View File

@@ -19,7 +19,9 @@ import {
backgroundSizeList as backgroundSizeListZh,
downTypeList as downTypeListZh,
shapeListMap as shapeListMapZh,
lineStyleMap as lineStyleMapZh
lineStyleMap as lineStyleMapZh,
numberTypeList as numberTypeListZh,
numberLevelList as numberLevelListZh
} from './zh'
import {
fontFamilyList as fontFamilyListEn,
@@ -32,7 +34,9 @@ import {
shapeList as shapeListEn,
sidebarTriggerList as sidebarTriggerListEn,
backgroundSizeList as backgroundSizeListEn,
downTypeList as downTypeListEn
downTypeList as downTypeListEn,
numberTypeList as numberTypeListEn,
numberLevelList as numberLevelListEn
} from './en'
const fontFamilyList = {
@@ -100,6 +104,16 @@ const downTypeList = {
en: downTypeListEn
}
const numberTypeList = {
zh: numberTypeListZh,
en: numberTypeListEn
}
const numberLevelList = {
zh: numberLevelListZh,
en: numberLevelListEn
}
export {
fontSizeList,
lineHeightList,
@@ -121,5 +135,7 @@ export {
shapeList,
shapeListMap,
sidebarTriggerList,
downTypeList
downTypeList,
numberTypeList,
numberLevelList
}

View File

@@ -133,6 +133,10 @@ export const borderDasharrayList = [
{
name: '虚线6',
value: '1, 5'
},
{
name: '虚线7',
value: '6, 4'
}
]
@@ -587,3 +591,63 @@ export const downTypeList = [
desc: '纯文本文件'
}
]
// 编号类型列表
export const numberTypeList = [
{
name: '无编号',
value: ''
},
{
name: '1, 2, 3',
value: 1
},
{
name: '1., 2., 3.',
value: 2
},
{
name: '(1), (2), (3)',
value: 3
},
{
name: 'a., b., c.',
value: 4
},
{
name: 'A., B., C.',
value: 5
},
{
name: 'i., ii., iii.',
value: 6
},
{
name: 'I., II., III.',
value: 7
},
{
name: '一、, 二、, 三、',
value: 8
}
]
// 编号层级列表
export const numberLevelList = [
{
name: '编号首层',
value: 1
},
{
name: '编号前两层',
value: 2
},
{
name: '编号前三层',
value: 3
},
{
name: '编号所有层',
value: 0
}
]

View File

@@ -26,7 +26,8 @@ export default {
nodeBorderType: 'Node border style',
nodeUseLineStyle: 'Use only has bottom border style',
otherConfig: 'Other config',
enableFreeDrag: 'Enable node free drag',
enableFreeDrag: 'Enable node free drag(Beta)',
openPerformance: 'Enable performance mode',
watermark: 'Watermark',
showWatermark: 'Is show watermark',
onlyExport: 'Only export',
@@ -109,7 +110,8 @@ export default {
copyToTxt: 'Txt',
copyToPng: 'Png',
copySuccess: 'Copy success',
copyFail: 'Copy fail'
copyFail: 'Copy fail',
number: 'Number child nodes'
},
count: {
words: 'Words',
@@ -289,9 +291,9 @@ export default {
creatingTip: 'Creating file',
directory: 'Directory',
newFileTip:
'Please export the currently edited file before creating a new one, otherwise the content will be lost',
'Please export the currently edited file before creating a new one, Beware of content loss',
openFileTip:
'Please export the currently edited file before opening it, otherwise the content will be lost'
'Please export the currently edited file before opening it, Beware of content loss'
},
edit: {
newFeatureNoticeTitle: 'New feature reminder',

View File

@@ -27,6 +27,7 @@ export default {
nodeUseLineStyle: '是否使用只有底边框的风格',
otherConfig: '其他配置',
enableFreeDrag: '是否开启节点自由拖拽',
openPerformance: '开启性能模式(Beta)',
watermark: '水印',
showWatermark: '是否显示水印',
watermarkDefaultText: '水印文字',
@@ -109,7 +110,8 @@ export default {
copyToTxt: 'Txt',
copyToPng: '图片',
copySuccess: '复制成功',
copyFail: '复制失败'
copyFail: '复制失败',
number: '编号其子节点'
},
count: {
words: '字数',
@@ -284,8 +286,8 @@ export default {
defaultFileName: '思维导图',
creatingTip: '正在创建文件',
directory: '目录',
newFileTip: '新建文件前请先导出当前编辑的文件,否则内容丢失',
openFileTip: '打开文件前请先导出当前编辑的文件,否则内容会丢'
newFileTip: '新建文件前请先导出当前编辑的文件,谨防内容丢失',
openFileTip: '打开文件前请先导出当前编辑的文件,谨防内容丢失'
},
edit: {
newFeatureNoticeTitle: '新特性提醒',

View File

@@ -10,8 +10,8 @@ let langList = [
path: 'en'
}
]
let StartList = ['introduction', 'start', 'deploy', 'client', 'translate', 'changelog']
let CourseList = new Array(29).fill(0).map((_, index) => {
let StartList = ['introduction', 'start', 'question', 'deploy', 'client', 'translate', 'changelog']
let CourseList = new Array(30).fill(0).map((_, index) => {
return 'course' + (index + 1)
})
let APIList = [
@@ -43,6 +43,7 @@ let APIList = [
'demonstrate',
'notation',
'outerFrame',
'numbers',
'xmind',
'markdown',
'utils'

View File

@@ -55,6 +55,12 @@ The currently active connection line and array type are the same as the structur
## Methods
### cancelCreateLine()
> v0.10.5+
Cancel the creation of the associated line midway.
### renderAllLines()
Re-render all associated lines.

View File

@@ -49,6 +49,11 @@ MindMap.usePlugin(AssociativeLine)
<h3>mindMap.associativeLine.activeLine</h3>
<p>The currently active connection line and array type are the same as the structure of each item in the <code>lineList</code> array.</p>
<h2>Methods</h2>
<h3>cancelCreateLine()</h3>
<blockquote>
<p>v0.10.5+</p>
</blockquote>
<p>Cancel the creation of the associated line midway.</p>
<h3>renderAllLines()</h3>
<p>Re-render all associated lines.</p>
<h3>removeAllLines()</h3>

View File

@@ -1,5 +1,105 @@
# Changelog
## 0.10.6
> 2024.8.15
Fix:
> 1.Fix the issue where the highlighting of search nodes does not disappear in read-only mode;
>
> 2.Fixed the issue where the associated line plugin reported an error when clicking on the right-click menu of the mouse;
>
> 3.Fix the issue where clicking on the canvas cannot deactivate associated lines;
>
> 4.Fixed the issue where the text style of the summary node did not change when switching themes in rich text mode;
>
> 5.Fixed the issue where selecting both summary nodes and their corresponding nodes simultaneously and setting node styles would cause summary nodes to lose their activation status;
>
> 6.Fixed the issue where nodes outside the canvas would report an error when adding icons after selecting all nodes in performance mode;
>
> 7.Fix the issue of being unable to move forward after activating a node during rollback operation;
New:
> 1.Add Node Number Plugin;
>
> 2.Search plugin supports searching summary nodes;
>
> 3.Add instantiation option to not display expand and collapse buttons;
>
> 4.Theme configuration supports setting the style of associated lines, namely solid or dashed lines;
>
> 5.Mouse box selection supports selecting summary nodes;
>
> 6.Select All operation supports selecting summary nodes;
>
> 7.Add node note icon click event;
>
> 8.Add manual activation and deactivation methods for multiple nodes in the render class;
>
> 9.Method for adding an extended font list to the RichText plugin;
>
> 10.Fix the issue of incomplete support for summary nodes in the GO_TARGET_deDE command;
>
> 11.Delete the elements of the text editing box when destroying an instance;
Demo:
> 1.Right click menu adds the function of setting node numbers;
>
> 2.Support setting the style of associated lines, namely solid or dashed lines;
>
> 3.Support selecting the content of the floating layer of notes with the mouse;
>
> 4.Change the hyperlink in the remarks to open a new window;
>
> 5.Fixed the issue where entering the zoom note input box when activating a node would result in inputting it into the node text;
>
> 6.Fixed the issue where switching themes after changing the base style, selecting overwrite, and then modifying the base style would restore the previously overwritten style;
## 0.10.5
> 2024.8.2
Fix:
> 1.Fixed the issue of error when dragging the scrollbar after enabling the scrollbar plugin and performance mode;
>
> 2.Fixed the issue where the rich text style of nodes was not updated when copying across levels;
>
> 3.Fixed the issue where the demo plugin did not work properly after enabling performance mode;
>
> 4.Fixed the issue of canvas jumping when the first rendering canvas is triggered after adjusting the canvas size;
New:
> 1.Move the node and change the node hierarchy. Nodes that have set custom rich text styles do not need to update the styles;
>
> 2.Support clicking on the canvas to cancel the creation of associated lines;
>
> 3.Automatically expand a node by moving it or copying it to a collapsed node;
Demo:
> 1.When the number of subordinate nodes is greater than 100, the collapse button displays ellipsis;
>
> 2.No longer write data to the browser cache when opening local file editing, to avoid the problem of local file data loss caused by triggering storage restrictions;
>
> 3.If the local file is not saved, close the page and add an interception prompt;
>
> 4.Fixed the issue in the outline where clicking and dragging a node would trigger a file drag mask on the page;
## 0.10.4
> 2024.7.25
This update mainly adds a performance mode. When enabled, only nodes within the visible area of the canvas will be rendered, and nodes outside the area will be deleted from the canvas, improving usability in the case of large data volumes (1000+nodes). The first rendering time for 2000 nodes has been reduced from 5s+to 0.5s. However, this also brings some problems, such as a slight lag when dragging the animation canvas, as nodes will be rendered in real time, and exporting images will be slower because all nodes need to be rendered first, as well as other temporarily undiscovered issues.
Fix:
> 1.Delete useless code and fix the issue where the mouse hover node reports an error and does not display the expand and collapse buttons;
## 0.10.3
> 2024.7.19

View File

@@ -1,6 +1,76 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.10.6</h2>
<blockquote>
<p>2024.8.15</p>
</blockquote>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue where the highlighting of search nodes does not disappear in read-only mode;</p>
<p>2.Fixed the issue where the associated line plugin reported an error when clicking on the right-click menu of the mouse;</p>
<p>3.Fix the issue where clicking on the canvas cannot deactivate associated lines;</p>
<p>4.Fixed the issue where the text style of the summary node did not change when switching themes in rich text mode;</p>
<p>5.Fixed the issue where selecting both summary nodes and their corresponding nodes simultaneously and setting node styles would cause summary nodes to lose their activation status;</p>
<p>6.Fixed the issue where nodes outside the canvas would report an error when adding icons after selecting all nodes in performance mode;</p>
<p>7.Fix the issue of being unable to move forward after activating a node during rollback operation;</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.Add Node Number Plugin;</p>
<p>2.Search plugin supports searching summary nodes;</p>
<p>3.Add instantiation option to not display expand and collapse buttons;</p>
<p>4.Theme configuration supports setting the style of associated lines, namely solid or dashed lines;</p>
<p>5.Mouse box selection supports selecting summary nodes;</p>
<p>6.Select All operation supports selecting summary nodes;</p>
<p>7.Add node note icon click event;</p>
<p>8.Add manual activation and deactivation methods for multiple nodes in the render class;</p>
<p>9.Method for adding an extended font list to the RichText plugin;</p>
<p>10.Fix the issue of incomplete support for summary nodes in the GO_TARGET_deDE command;</p>
<p>11.Delete the elements of the text editing box when destroying an instance;</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.Right click menu adds the function of setting node numbers;</p>
<p>2.Support setting the style of associated lines, namely solid or dashed lines;</p>
<p>3.Support selecting the content of the floating layer of notes with the mouse;</p>
<p>4.Change the hyperlink in the remarks to open a new window;</p>
<p>5.Fixed the issue where entering the zoom note input box when activating a node would result in inputting it into the node text;</p>
<p>6.Fixed the issue where switching themes after changing the base style, selecting overwrite, and then modifying the base style would restore the previously overwritten style;</p>
</blockquote>
<h2>0.10.5</h2>
<blockquote>
<p>2024.8.2</p>
</blockquote>
<p>Fix:</p>
<blockquote>
<p>1.Fixed the issue of error when dragging the scrollbar after enabling the scrollbar plugin and performance mode;</p>
<p>2.Fixed the issue where the rich text style of nodes was not updated when copying across levels;</p>
<p>3.Fixed the issue where the demo plugin did not work properly after enabling performance mode;</p>
<p>4.Fixed the issue of canvas jumping when the first rendering canvas is triggered after adjusting the canvas size;</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.Move the node and change the node hierarchy. Nodes that have set custom rich text styles do not need to update the styles;</p>
<p>2.Support clicking on the canvas to cancel the creation of associated lines;</p>
<p>3.Automatically expand a node by moving it or copying it to a collapsed node;</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.When the number of subordinate nodes is greater than 100, the collapse button displays ellipsis;</p>
<p>2.No longer write data to the browser cache when opening local file editing, to avoid the problem of local file data loss caused by triggering storage restrictions;</p>
<p>3.If the local file is not saved, close the page and add an interception prompt;</p>
<p>4.Fixed the issue in the outline where clicking and dragging a node would trigger a file drag mask on the page;</p>
</blockquote>
<h2>0.10.4</h2>
<blockquote>
<p>2024.7.25</p>
</blockquote>
<p>This update mainly adds a performance mode. When enabled, only nodes within the visible area of the canvas will be rendered, and nodes outside the area will be deleted from the canvas, improving usability in the case of large data volumes (1000+nodes). The first rendering time for 2000 nodes has been reduced from 5s+to 0.5s. However, this also brings some problems, such as a slight lag when dragging the animation canvas, as nodes will be rendered in real time, and exporting images will be slower because all nodes need to be rendered first, as well as other temporarily undiscovered issues.</p>
<p>Fix:</p>
<blockquote>
<p>1.Delete useless code and fix the issue where the mouse hover node reports an error and does not display the expand and collapse buttons;</p>
</blockquote>
<h2>0.10.3</h2>
<blockquote>
<p>2024.7.19</p>

View File

@@ -20,6 +20,10 @@ const mindMap = new MindMap({
});
```
### Special Reminder
Node tree rendering is an asynchronous operation, so it is not possible to immediately call some operations that require node rendering to be completed after instantiation, otherwise errors and unknown phenomena may occur, You need to listen for the 'node_tree_render_end' event and wait until the node tree rendering is complete before proceeding. In addition to instantiation, other methods such as 'setData', 'updateData', 'render', etc. are asynchronous and need to be handled in this way.
## Instantiation options
### 1.Base
@@ -99,6 +103,9 @@ const mindMap = new MindMap({
| createNodePostfixContentv0.9.12+ | Function、null | null | Add additional node post content.Post content refers to the post content in the area of the same line as the text, excluding the node image section. The usage is the same as createNodePrefixContent | |
| disabledClipboardv0.10.2+ | Boolean | false | Is prohibit pasting data from the user's clipboard and writing copied node data to the user's clipboard. At this time, only node data from the canvas can be copied and pasted | |
| customHyperlinkJumpv0.10.2+ | null、Function | false | Customize the jump of hyperlinks. If not passed, the hyperlink will be opened as a new window by default, and a function can be passed, The function takes two parameters: linkThe URL of the hyperlink、nodeNode instance to which it belongs, As long as a function is passed, it will block the default jump | |
| openPerformancev0.10.4+ | Boolean | false | Whether to enable performance mode or not, by default, all nodes will be rendered directly, regardless of whether they are in the visible area of the canvas. This will cause a lag when there are a large number of nodes (1000+). If your data volume is large, you can enable performance mode through this configuration, that is, only rendering nodes within the visible area of the canvas, and not rendering nodes beyond it. This will greatly improve rendering speed, but of course, it will also bring some other problems, such as: 1. When dragging or scaling the canvas, real-time calculation and rendering of nodes without nodes will be performed, which will bring some lag; When exporting images, SVG, and PDF, all nodes need to be rendered first, so it may be slower; 3. Other currently undiscovered issues | |
| performanceConfigv0.10.4+ | Object | { time: 250, padding: 100, removeNodeWhenOutCanvas: true } | Performance optimization mode configuration. timeHow often do nodes refresh after a view change. Unit:ms、paddingStill rendering nodes beyond the specified range around the canvas、removeNodeWhenOutCanvasIs the node deleted from the canvas after being moved out of the visible area of the canvas | |
| notShowExpandBtnv0.10.6+ | Boolean | false | Do not display the expand/collapse button, higher priority than the 'alwaysShowExpandBtn' configuration | |
### 1.1Data structure
@@ -618,6 +625,9 @@ Listen to an event. Event list:
| node_layout_endv0.10.1+ | Event where the content layout of a single node is completed | this(Current node instance) |
| node_attachmentClickv0.9.10+ | Click event for node attachment icon | this(Current node instance)、eEvent Object、nodeIcon node |
| node_attachmentContextmenuv0.9.10+ | Right click event on node attachment icon | this(Current node instance)、eEvent Object、nodeIcon node |
| before_update_configv0.10.4+ | Triggered before updating the configuration, that is, when the 'mindMap.updateConfig' method is called to update the configuration | optThe configuration object before updating refers to an object, not a copy, so when the after_uupdate_comfig event is triggered, the object will also change synchronously. Therefore, it is necessary to cache a certain configuration field that you need |
| after_update_configv0.10.4+ | Triggered after updating configuration | optUpdated configuration object |
| node_note_clickv0.10.6+ | Click event of node note icon | this(Current node instance)、eEvent Object、nodeIcon node |
### emit(event, ...args)
@@ -705,7 +715,7 @@ redo. All commands are as follows:
| PASTE_NODE | Paste node to a node, the active node will be the operation node | data (the node data to paste, usually obtained through the renderer.copyNode() and renderer.cutNode() methods) |
| SET_NODE_STYLE | Modify node single style | node (the node to set the style of), prop (style property), value (style property value), isActive (v0.7.0+has been abandoned, boolean, whether the style being set is for the active state) |
| SET_NODE_STYLEsv0.6.12+ | Modify multiple styles of nodes | nodethe node to set the style of、styleStyle objectkey is style propvalue is style value、isActivev0.7.0+has been abandoned, boolean, whether the style being set is for the active state |
| SET_NODE_ACTIVE | Set whether the node is active | node (the node to set), active (boolean, whether to activate) |
| SET_NODE_ACTIVE | Set whether the node is active(This command only updates the activation fields and node activation styles in the node data. If you want to achieve the same effect as clicking on a node with the mouse, please use the 'active()' method of the node instance directly.) | node (the node to set), active (boolean, whether to activate) |
| CLEAR_ACTIVE_NODE | Clear the active state of the currently active node(s), the active node will be the operation node | |
| SET_NODE_EXPAND | Set whether the node is expanded | node (the node to set), expand (boolean, whether to expand) |
| EXPAND_ALL | Expand all nodes | |

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