Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff22a652d5 | ||
|
|
d1ade8204b | ||
|
|
2a84a3cf16 | ||
|
|
721cae8139 | ||
|
|
8f8662f2df | ||
|
|
bc56f90595 | ||
|
|
0d1b9dfff4 | ||
|
|
ccb2a260b6 | ||
|
|
2c8b96582a | ||
|
|
161d9dd715 | ||
|
|
1033447b9c | ||
|
|
1dbcb71ec5 | ||
|
|
c0733dda35 | ||
|
|
4c5b2e7af2 | ||
|
|
322f7a3e2a | ||
|
|
766e0e5fae | ||
|
|
4335cbb713 | ||
|
|
82473027da | ||
|
|
68bf2d361c | ||
|
|
1620a013ba | ||
|
|
13a1f989c3 | ||
|
|
8bbbc082c7 | ||
|
|
f15bf8a8dc | ||
|
|
3801dc3ec4 | ||
|
|
182cdf5153 | ||
|
|
1cd4705ad8 | ||
|
|
d82cedcbdd | ||
|
|
f9c271e11b | ||
|
|
a26d6bc7cf | ||
|
|
dc522cd0be | ||
|
|
43969af14b | ||
|
|
79ccd9892c | ||
|
|
5fb35d656f | ||
|
|
cec1f59189 | ||
|
|
a6a890362e | ||
|
|
83b6d5793b | ||
|
|
9b40a7bedd | ||
|
|
11f2078ee0 | ||
|
|
4fb9bd5135 | ||
|
|
cf56b5db24 | ||
|
|
40ea595e62 | ||
|
|
91a9d7b556 | ||
|
|
0669e73a5b | ||
|
|
fde6fc2583 | ||
|
|
f92146a1f4 | ||
|
|
38c6966d13 | ||
|
|
4894d9e6e6 | ||
|
|
9a64094f14 | ||
|
|
95fdb35f7b | ||
|
|
497c2606df | ||
|
|
d735be1204 | ||
|
|
45d2da1337 | ||
|
|
194571d5fe | ||
|
|
418b24b039 | ||
|
|
b32a8b5a85 | ||
|
|
2dee415a64 | ||
|
|
4e7d59b328 | ||
|
|
9bde9ffaf3 |
24
README.md
@@ -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>
|
||||
|
||||
BIN
dist/img/Jeffrey.jpg
vendored
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
dist/img/ccccs.jpg
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
dist/img/qrcode.jpg
vendored
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
dist/img/。.png
vendored
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
dist/img/张文建.jpg
vendored
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
dist/img/编号1.jpg
vendored
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
dist/img/编号2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
dist/img/编号3.jpg
vendored
Normal file
|
After Width: | Height: | Size: 42 KiB |
2
dist/js/app.js
vendored
2
dist/js/chunk-02087b0a.js
vendored
1
dist/js/chunk-0e53cbc6.js
vendored
Normal file
1
dist/js/chunk-1171703b.js
vendored
Normal 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
@@ -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"}}]);
|
||||
2
dist/js/chunk-1c3bec15.js
vendored
1
dist/js/chunk-2c6ccfd7.js
vendored
2
dist/js/chunk-2d0ab10b.js
vendored
2
dist/js/chunk-2d0afe0d.js
vendored
2
dist/js/chunk-2d0b1c6f.js
vendored
2
dist/js/chunk-2d0ba309.js
vendored
2
dist/js/chunk-2d0c0a44.js
vendored
2
dist/js/chunk-2d0c191e.js
vendored
2
dist/js/chunk-2d0c20be.js
vendored
2
dist/js/chunk-2d0d5cb9.js
vendored
2
dist/js/chunk-2d0d9fbc.js
vendored
2
dist/js/chunk-2d0da701.js
vendored
2
dist/js/chunk-2d0dad5f.js
vendored
1
dist/js/chunk-2d0e2326.js
vendored
Normal file
2
dist/js/chunk-2d0e9742.js
vendored
2
dist/js/chunk-2d0f026c.js
vendored
1
dist/js/chunk-2d0f09d5.js
vendored
Normal 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}}]);
|
||||
2
dist/js/chunk-2d208ffa.js
vendored
2
dist/js/chunk-2d20f68f.js
vendored
2
dist/js/chunk-2d216004.js
vendored
1
dist/js/chunk-3977effe.js
vendored
Normal file
1
dist/js/chunk-47ab6502.js
vendored
Normal file
1
dist/js/chunk-4f3dd472.js
vendored
Normal file
1
dist/js/chunk-63012c17.js
vendored
1
dist/js/chunk-68de956c.js
vendored
1
dist/js/chunk-6a164532.js
vendored
Normal file
1
dist/js/chunk-77401c43.js
vendored
Normal file
2
dist/js/chunk-9d289278.js
vendored
1
dist/js/chunk-b84a6bba.js
vendored
@@ -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
|
After Width: | Height: | Size: 41 KiB |
@@ -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)
|
||||
|
||||
@@ -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 (
|
||||
|
||||
6
simple-mind-map/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -326,7 +326,9 @@ export const nodeDataNoStylePropList = [
|
||||
'attachmentUrl',
|
||||
'attachmentName',
|
||||
'notation',
|
||||
'outerFrame'
|
||||
'outerFrame',
|
||||
'number',
|
||||
'range'
|
||||
]
|
||||
|
||||
// 错误类型
|
||||
|
||||
@@ -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插件】
|
||||
// 多选节点时鼠标移动到边缘时的画布移动偏移量
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
// 添加连接线
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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事件,那么需要手动把标志复位
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -42,6 +42,8 @@ export default {
|
||||
associativeLineActiveWidth: 8,
|
||||
// 关联线激活状态的颜色
|
||||
associativeLineActiveColor: 'rgba(2, 167, 240, 1)',
|
||||
// 关联线样式
|
||||
associativeLineDasharray: [6, 4],
|
||||
// 关联线文字颜色
|
||||
associativeLineTextColor: 'rgb(51, 51, 51)',
|
||||
// 关联线文字大小
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
BIN
web/src/assets/avatar/Jeffrey.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
web/src/assets/avatar/ccccs.jpg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
web/src/assets/avatar/。.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
web/src/assets/avatar/张文建.jpg
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
web/src/assets/img/docs/编号1.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
web/src/assets/img/docs/编号2.jpg
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
web/src/assets/img/docs/编号3.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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: '新特性提醒',
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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({
|
||||
| createNodePostfixContent(v0.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 | |
|
||||
| disabledClipboard(v0.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 | |
|
||||
| customHyperlinkJump(v0.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: link(The URL of the hyperlink)、node(Node instance to which it belongs), As long as a function is passed, it will block the default jump | |
|
||||
| openPerformance(v0.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 | |
|
||||
| performanceConfig(v0.10.4+) | Object | { time: 250, padding: 100, removeNodeWhenOutCanvas: true } | Performance optimization mode configuration. time(How often do nodes refresh after a view change. Unit:ms)、padding(Still rendering nodes beyond the specified range around the canvas)、removeNodeWhenOutCanvas(Is the node deleted from the canvas after being moved out of the visible area of the canvas) | |
|
||||
| notShowExpandBtn(v0.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_end(v0.10.1+) | Event where the content layout of a single node is completed | this(Current node instance) |
|
||||
| node_attachmentClick(v0.9.10+) | Click event for node attachment icon | this(Current node instance)、e(Event Object)、node(Icon node) |
|
||||
| node_attachmentContextmenu(v0.9.10+) | Right click event on node attachment icon | this(Current node instance)、e(Event Object)、node(Icon node) |
|
||||
| before_update_config(v0.10.4+) | Triggered before updating the configuration, that is, when the 'mindMap.updateConfig' method is called to update the configuration | opt(The 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_config(v0.10.4+) | Triggered after updating configuration | opt(Updated configuration object) |
|
||||
| node_note_click(v0.10.6+) | Click event of node note icon | this(Current node instance)、e(Event Object)、node(Icon 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_STYLEs(v0.6.12+) | Modify multiple styles of nodes | node(the node to set the style of)、style(Style object,key is style prop,value is style value)、isActive(v0.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 | |
|
||||
|
||||