mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
Merge branch 'feature' into feature/icon-merge
This commit is contained in:
@@ -14,6 +14,7 @@ import TouchEvent from './src/plugins/TouchEvent.js'
|
||||
import Search from './src/plugins/Search.js'
|
||||
import Painter from './src/plugins/Painter.js'
|
||||
import Scrollbar from './src/plugins/Scrollbar.js'
|
||||
import Formula from './src/plugins/Formula.js'
|
||||
import xmind from './src/parse/xmind.js'
|
||||
import markdown from './src/parse/markdown.js'
|
||||
import icons from './src/svg/icons.js'
|
||||
@@ -43,5 +44,6 @@ MindMap.usePlugin(MiniMap)
|
||||
.usePlugin(Search)
|
||||
.usePlugin(Painter)
|
||||
.usePlugin(Scrollbar)
|
||||
.usePlugin(Formula)
|
||||
|
||||
export default MindMap
|
||||
|
||||
96
simple-mind-map/package-lock.json
generated
96
simple-mind-map/package-lock.json
generated
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "@wh/simple-mind-map",
|
||||
"version": "0.0.1",
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.7.1-fix.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@wh/simple-mind-map",
|
||||
"version": "0.0.1",
|
||||
"version": "0.7.1-fix.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.0.16",
|
||||
@@ -14,6 +13,7 @@
|
||||
"eventemitter3": "^4.0.7",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"katex": "^0.16.8",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"quill": "^1.3.6",
|
||||
"tern": "^0.24.3",
|
||||
@@ -191,7 +191,7 @@
|
||||
},
|
||||
"node_modules/acorn-loose": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/acorn-loose/-/acorn-loose-6.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-6.1.0.tgz",
|
||||
"integrity": "sha512-FHhXoiF0Uch3IqsrnPpWwCtiv5PYvipTpT1k9lDMgQVVYc9iDuSl5zdJV358aI8twfHCYMFBRVYvAVki9wC/ng==",
|
||||
"dependencies": {
|
||||
"acorn": "^6.2.0"
|
||||
@@ -202,7 +202,7 @@
|
||||
},
|
||||
"node_modules/acorn-loose/node_modules/acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-6.4.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -213,7 +213,7 @@
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
||||
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
@@ -401,6 +401,14 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -554,7 +562,7 @@
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz",
|
||||
"integrity": "sha512-n6e4bsCpzsP0OB76X+vEWhySUQI8GHPVFVK+3QkX35tbryy2WoeGeK5kQ+oxzgDVHjIZyz5fyS60Mi3EpQLc0Q==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
@@ -568,7 +576,7 @@
|
||||
},
|
||||
"node_modules/errno": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
|
||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
|
||||
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
||||
"dependencies": {
|
||||
"prr": "~1.0.1"
|
||||
@@ -926,7 +934,7 @@
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"node_modules/grapheme-splitter": {
|
||||
@@ -1204,6 +1212,21 @@
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
|
||||
"integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
],
|
||||
"dependencies": {
|
||||
"commander": "^8.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"katex": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
@@ -1291,7 +1314,7 @@
|
||||
},
|
||||
"node_modules/memory-fs": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz",
|
||||
"integrity": "sha512-QTNXnl79X97kZ9jJk/meJrtDuvgvRakX5LU7HZW1L7MsXHuSTwoMIzN9tOLLH3Xfsj/gbsSqX/ovnsqz246zKQ==",
|
||||
"dependencies": {
|
||||
"errno": "^0.1.3",
|
||||
@@ -1752,7 +1775,7 @@
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -1921,7 +1944,7 @@
|
||||
},
|
||||
"node_modules/prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
@@ -2217,7 +2240,7 @@
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmmirror.com/tapable/-/tapable-0.2.9.tgz",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz",
|
||||
"integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@@ -2225,7 +2248,7 @@
|
||||
},
|
||||
"node_modules/tern": {
|
||||
"version": "0.24.3",
|
||||
"resolved": "https://registry.npmmirror.com/tern/-/tern-0.24.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/tern/-/tern-0.24.3.tgz",
|
||||
"integrity": "sha512-Z8uvtdWIlFn1GWy0HW5FhZ8VDryZwoJUdnjZU25C7/PBOltLIn1uv+WF3rVq6S1761YbsmbZYRP/l0ZJBCkvrw==",
|
||||
"dependencies": {
|
||||
"acorn": "^6.0.0",
|
||||
@@ -2242,7 +2265,7 @@
|
||||
},
|
||||
"node_modules/tern/node_modules/acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-6.4.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -2253,7 +2276,7 @@
|
||||
},
|
||||
"node_modules/tern/node_modules/resolve-from": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-2.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
|
||||
"integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -2538,7 +2561,7 @@
|
||||
},
|
||||
"acorn-loose": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/acorn-loose/-/acorn-loose-6.1.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-6.1.0.tgz",
|
||||
"integrity": "sha512-FHhXoiF0Uch3IqsrnPpWwCtiv5PYvipTpT1k9lDMgQVVYc9iDuSl5zdJV358aI8twfHCYMFBRVYvAVki9wC/ng==",
|
||||
"requires": {
|
||||
"acorn": "^6.2.0"
|
||||
@@ -2546,14 +2569,14 @@
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-6.4.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
||||
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
|
||||
},
|
||||
"ajv": {
|
||||
@@ -2685,6 +2708,11 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
|
||||
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -2797,7 +2825,7 @@
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz",
|
||||
"integrity": "sha512-n6e4bsCpzsP0OB76X+vEWhySUQI8GHPVFVK+3QkX35tbryy2WoeGeK5kQ+oxzgDVHjIZyz5fyS60Mi3EpQLc0Q==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
@@ -2808,7 +2836,7 @@
|
||||
},
|
||||
"errno": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
|
||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
|
||||
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
||||
"requires": {
|
||||
"prr": "~1.0.1"
|
||||
@@ -3081,7 +3109,7 @@
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"grapheme-splitter": {
|
||||
@@ -3287,6 +3315,14 @@
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"katex": {
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
|
||||
"integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
|
||||
"requires": {
|
||||
"commander": "^8.3.0"
|
||||
}
|
||||
},
|
||||
"kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
@@ -3354,7 +3390,7 @@
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz",
|
||||
"integrity": "sha512-QTNXnl79X97kZ9jJk/meJrtDuvgvRakX5LU7HZW1L7MsXHuSTwoMIzN9tOLLH3Xfsj/gbsSqX/ovnsqz246zKQ==",
|
||||
"requires": {
|
||||
"errno": "^0.1.3",
|
||||
@@ -3599,7 +3635,7 @@
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||
},
|
||||
"object-is": {
|
||||
@@ -3717,7 +3753,7 @@
|
||||
},
|
||||
"prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
|
||||
},
|
||||
"punycode": {
|
||||
@@ -3926,12 +3962,12 @@
|
||||
},
|
||||
"tapable": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmmirror.com/tapable/-/tapable-0.2.9.tgz",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz",
|
||||
"integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A=="
|
||||
},
|
||||
"tern": {
|
||||
"version": "0.24.3",
|
||||
"resolved": "https://registry.npmmirror.com/tern/-/tern-0.24.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/tern/-/tern-0.24.3.tgz",
|
||||
"integrity": "sha512-Z8uvtdWIlFn1GWy0HW5FhZ8VDryZwoJUdnjZU25C7/PBOltLIn1uv+WF3rVq6S1761YbsmbZYRP/l0ZJBCkvrw==",
|
||||
"requires": {
|
||||
"acorn": "^6.0.0",
|
||||
@@ -3945,12 +3981,12 @@
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-6.4.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
|
||||
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-2.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
|
||||
"integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ=="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"eventemitter3": "^4.0.7",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"katex": "^0.16.8",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"quill": "^1.3.6",
|
||||
"tern": "^0.24.3",
|
||||
|
||||
@@ -365,7 +365,7 @@ export const cssContent = `
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.smm-node:hover .smm-hover-node{
|
||||
.smm-node:not(.smm-node-dragging):hover .smm-hover-node{
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,5 +188,18 @@ export const defaultOpt = {
|
||||
// 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动
|
||||
autoMoveWhenMouseInEdgeOnDrag: true,
|
||||
// 是否首次加载fit view
|
||||
fit: false
|
||||
fit: false,
|
||||
// 拖拽多个节点时随鼠标移动的示意矩形的样式配置
|
||||
dragMultiNodeRectConfig: {
|
||||
width: 40,
|
||||
height: 20,
|
||||
fill: '' // 填充颜色,如果不传默认使用连线的颜色
|
||||
},
|
||||
// 节点拖拽时新位置的示意矩形的填充颜色,如果不传默认使用连线的颜色
|
||||
dragPlaceholderRectFill: '',
|
||||
// 节点拖拽时的透明度配置
|
||||
dragOpacityConfig: {
|
||||
cloneNodeOpacity: 0.5, // 跟随鼠标移动的克隆节点或矩形的透明度
|
||||
beingDragNodeOpacity: 0.3 // 被拖拽节点的透明度
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@ import {
|
||||
walk,
|
||||
bfsWalk,
|
||||
loadImage,
|
||||
isUndef
|
||||
isUndef,
|
||||
getTopAncestorsFomNodeList,
|
||||
addDataToAppointNodes,
|
||||
createUidForAppointNodes,
|
||||
formatDataToArray,
|
||||
getNodeIndex
|
||||
} from '../../utils'
|
||||
import { shapeList } from './node/Shape'
|
||||
import { lineStyleProps } from '../../themes/default'
|
||||
@@ -40,7 +45,6 @@ const layouts = {
|
||||
}
|
||||
|
||||
// 渲染
|
||||
|
||||
class Render {
|
||||
// 构造函数
|
||||
constructor(opt = {}) {
|
||||
@@ -132,9 +136,18 @@ class Render {
|
||||
// 插入同级节点
|
||||
this.insertNode = this.insertNode.bind(this)
|
||||
this.mindMap.command.add('INSERT_NODE', this.insertNode)
|
||||
// 插入多个同级节点
|
||||
this.insertMultiNode = this.insertMultiNode.bind(this)
|
||||
this.mindMap.command.add('INSERT_MULTI_NODE', this.insertMultiNode)
|
||||
// 插入子节点
|
||||
this.insertChildNode = this.insertChildNode.bind(this)
|
||||
this.mindMap.command.add('INSERT_CHILD_NODE', this.insertChildNode)
|
||||
// 插入多个子节点
|
||||
this.insertMultiChildNode = this.insertMultiChildNode.bind(this)
|
||||
this.mindMap.command.add(
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
this.insertMultiChildNode
|
||||
)
|
||||
// 上移节点
|
||||
this.upNode = this.upNode.bind(this)
|
||||
this.mindMap.command.add('UP_NODE', this.upNode)
|
||||
@@ -202,6 +215,9 @@ class Render {
|
||||
// 设置节点标签
|
||||
this.setNodeTag = this.setNodeTag.bind(this)
|
||||
this.mindMap.command.add('SET_NODE_TAG', this.setNodeTag)
|
||||
// 设置节点公式
|
||||
this.insertFormula = this.insertFormula.bind(this)
|
||||
this.mindMap.command.add('INSERT_FORMULA', this.insertFormula)
|
||||
// 添加节点概要
|
||||
this.addGeneralization = this.addGeneralization.bind(this)
|
||||
this.mindMap.command.add('ADD_GENERALIZATION', this.addGeneralization)
|
||||
@@ -386,15 +402,6 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取节点在同级里的索引位置
|
||||
getNodeIndex(node) {
|
||||
return node.parent
|
||||
? node.parent.children.findIndex(item => {
|
||||
return item.uid === node.uid
|
||||
})
|
||||
: 0
|
||||
}
|
||||
|
||||
// 全选
|
||||
selectAll() {
|
||||
walk(
|
||||
@@ -439,58 +446,117 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 规范指定节点数据
|
||||
formatAppointNodes(appointNodes) {
|
||||
if (!appointNodes) return []
|
||||
return Array.isArray(appointNodes) ? appointNodes : [appointNodes]
|
||||
}
|
||||
|
||||
// 插入同级节点,多个节点只会操作第一个节点
|
||||
// 插入同级节点
|
||||
insertNode(
|
||||
openEdit = true,
|
||||
appointNodes = [],
|
||||
appointData = null,
|
||||
appointChildren = []
|
||||
) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
appointNodes = formatDataToArray(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
this.textEdit.hideEditTextBox()
|
||||
let {
|
||||
const {
|
||||
defaultInsertSecondLevelNodeText,
|
||||
defaultInsertBelowSecondLevelNodeText
|
||||
} = this.mindMap.opt
|
||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
let first = list[0]
|
||||
if (first.isGeneralization) {
|
||||
return
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const handleMultiNodes = list.length > 1
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
|
||||
}
|
||||
if (first.isRoot) {
|
||||
this.insertChildNode(openEdit, appointNodes, appointData)
|
||||
} else {
|
||||
let text =
|
||||
first.layerIndex === 1
|
||||
? defaultInsertSecondLevelNodeText
|
||||
: defaultInsertBelowSecondLevelNodeText
|
||||
if (first.layerIndex === 1) {
|
||||
first.parent.destroy()
|
||||
// 动态指定的子节点数据也需要添加相关属性
|
||||
appointChildren = addDataToAppointNodes(appointChildren, {
|
||||
...params
|
||||
})
|
||||
const needDestroyNodeList = {}
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization || node.isRoot) {
|
||||
return
|
||||
}
|
||||
let index = this.getNodeIndex(first)
|
||||
let isRichText = !!this.mindMap.richText
|
||||
first.parent.nodeData.children.splice(index + 1, 0, {
|
||||
inserting: openEdit,
|
||||
const parent = node.parent
|
||||
const isOneLayer = node.layerIndex === 1
|
||||
// 插入二级节点时根节点需要重新渲染
|
||||
if (isOneLayer && !needDestroyNodeList[parent.uid]) {
|
||||
needDestroyNodeList[parent.uid] = parent
|
||||
}
|
||||
// 新插入节点的默认文本
|
||||
const text = isOneLayer
|
||||
? defaultInsertSecondLevelNodeText
|
||||
: defaultInsertBelowSecondLevelNodeText
|
||||
// 计算插入位置
|
||||
const index = parent.nodeData.children.findIndex(item => {
|
||||
return item.data.uid === node.uid
|
||||
})
|
||||
parent.nodeData.children.splice(index + 1, 0, {
|
||||
inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式,
|
||||
data: {
|
||||
text: text,
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
...params,
|
||||
...(appointData || {})
|
||||
},
|
||||
children: [...appointChildren]
|
||||
})
|
||||
this.mindMap.render()
|
||||
})
|
||||
Object.keys(needDestroyNodeList).forEach(key => {
|
||||
needDestroyNodeList[key].destroy()
|
||||
})
|
||||
// 如果同时对多个节点插入子节点,需要清除原来激活的节点
|
||||
if (handleMultiNodes || !openEdit) {
|
||||
this.clearActive()
|
||||
}
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 插入多个同级节点
|
||||
insertMultiNode(appointNodes, nodeList) {
|
||||
if (!nodeList || nodeList.length <= 0) return
|
||||
appointNodes = formatDataToArray(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
this.textEdit.hideEditTextBox()
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: true
|
||||
}
|
||||
nodeList = addDataToAppointNodes(nodeList, params)
|
||||
const needDestroyNodeList = {}
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization || node.isRoot) {
|
||||
return
|
||||
}
|
||||
const parent = node.parent
|
||||
const isOneLayer = node.layerIndex === 1
|
||||
// 插入二级节点时根节点需要重新渲染
|
||||
if (isOneLayer && !needDestroyNodeList[parent.uid]) {
|
||||
needDestroyNodeList[parent.uid] = parent
|
||||
}
|
||||
// 计算插入位置
|
||||
const index = parent.nodeData.children.findIndex(item => {
|
||||
return item.data.uid === node.uid
|
||||
})
|
||||
parent.nodeData.children.splice(
|
||||
index + 1,
|
||||
0,
|
||||
...createUidForAppointNodes(simpleDeepClone(nodeList))
|
||||
)
|
||||
})
|
||||
Object.keys(needDestroyNodeList).forEach(key => {
|
||||
needDestroyNodeList[key].destroy()
|
||||
})
|
||||
this.clearActive()
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 插入子节点
|
||||
@@ -500,16 +566,28 @@ class Render {
|
||||
appointData = null,
|
||||
appointChildren = []
|
||||
) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
appointNodes = formatDataToArray(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
this.textEdit.hideEditTextBox()
|
||||
let {
|
||||
const {
|
||||
defaultInsertSecondLevelNodeText,
|
||||
defaultInsertBelowSecondLevelNodeText
|
||||
} = this.mindMap.opt
|
||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const handleMultiNodes = list.length > 1
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
|
||||
}
|
||||
// 动态指定的子节点数据也需要添加相关属性
|
||||
appointChildren = addDataToAppointNodes(appointChildren, {
|
||||
...params
|
||||
})
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization) {
|
||||
return
|
||||
@@ -517,17 +595,14 @@ class Render {
|
||||
if (!node.nodeData.children) {
|
||||
node.nodeData.children = []
|
||||
}
|
||||
let text = node.isRoot
|
||||
const text = node.isRoot
|
||||
? defaultInsertSecondLevelNodeText
|
||||
: defaultInsertBelowSecondLevelNodeText
|
||||
let isRichText = !!this.mindMap.richText
|
||||
node.nodeData.children.push({
|
||||
inserting: openEdit,
|
||||
inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式
|
||||
data: {
|
||||
text: text,
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
...params,
|
||||
...(appointData || {})
|
||||
},
|
||||
children: [...appointChildren]
|
||||
@@ -538,6 +613,45 @@ class Render {
|
||||
node.destroy()
|
||||
}
|
||||
})
|
||||
// 如果同时对多个节点插入子节点,需要清除原来激活的节点
|
||||
if (handleMultiNodes || !openEdit) {
|
||||
this.clearActive()
|
||||
}
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 插入多个子节点
|
||||
insertMultiChildNode(appointNodes, childList) {
|
||||
if (!childList || childList.length <= 0) return
|
||||
appointNodes = formatDataToArray(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
this.textEdit.hideEditTextBox()
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: true
|
||||
}
|
||||
childList = addDataToAppointNodes(childList, params)
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization) {
|
||||
return
|
||||
}
|
||||
if (!node.nodeData.children) {
|
||||
node.nodeData.children = []
|
||||
}
|
||||
node.nodeData.children.push(...childList)
|
||||
// 插入子节点时自动展开子节点
|
||||
node.nodeData.data.expand = true
|
||||
if (node.isRoot) {
|
||||
node.destroy()
|
||||
}
|
||||
})
|
||||
this.clearActive()
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
@@ -598,19 +712,19 @@ class Render {
|
||||
// 复制节点
|
||||
copy() {
|
||||
this.beingCopyData = this.copyNode()
|
||||
this.setCoptyDataToClipboard(this.beingCopyData)
|
||||
this.setCopyDataToClipboard(this.beingCopyData)
|
||||
}
|
||||
|
||||
// 剪切节点
|
||||
cut() {
|
||||
this.mindMap.execCommand('CUT_NODE', copyData => {
|
||||
this.beingCopyData = copyData
|
||||
this.setCoptyDataToClipboard(copyData)
|
||||
this.setCopyDataToClipboard(copyData)
|
||||
})
|
||||
}
|
||||
|
||||
// 将粘贴或剪切的数据设置到用户剪切板中
|
||||
setCoptyDataToClipboard(data) {
|
||||
setCopyDataToClipboard(data) {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(
|
||||
JSON.stringify({
|
||||
@@ -701,13 +815,9 @@ class Render {
|
||||
}
|
||||
if (smmData) {
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_CHILD_NODE',
|
||||
false,
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
[],
|
||||
{
|
||||
...smmData.data
|
||||
},
|
||||
[...smmData.children]
|
||||
Array.isArray(smmData) ? smmData : [smmData]
|
||||
)
|
||||
} else {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
@@ -741,86 +851,70 @@ class Render {
|
||||
|
||||
// 将节点移动到另一个节点的前面
|
||||
insertBefore(node, exist) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
// 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新
|
||||
let nodeLayerChanged =
|
||||
(node.layerIndex === 1 && exist.layerIndex !== 1) ||
|
||||
(node.layerIndex !== 1 && exist.layerIndex === 1)
|
||||
// 移动节点
|
||||
let nodeParent = node.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
let nodeIndex = nodeBorthers.findIndex(item => {
|
||||
return item.uid === node.uid
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
return
|
||||
}
|
||||
nodeBorthers.splice(nodeIndex, 1)
|
||||
nodeParent.nodeData.children.splice(nodeIndex, 1)
|
||||
|
||||
// 目标节点
|
||||
let existParent = exist.parent
|
||||
let existBorthers = existParent.children
|
||||
let existIndex = existBorthers.findIndex(item => {
|
||||
return item.uid === exist.uid
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
}
|
||||
existBorthers.splice(existIndex, 0, node)
|
||||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render(() => {
|
||||
if (nodeLayerChanged) {
|
||||
node.reRender()
|
||||
}
|
||||
})
|
||||
this.insertTo(node, exist, 'before')
|
||||
}
|
||||
|
||||
// 将节点移动到另一个节点的后面
|
||||
insertAfter(node, exist) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
// 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新
|
||||
let nodeLayerChanged =
|
||||
(node.layerIndex === 1 && exist.layerIndex !== 1) ||
|
||||
(node.layerIndex !== 1 && exist.layerIndex === 1)
|
||||
// 移动节点
|
||||
let nodeParent = node.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
let nodeIndex = nodeBorthers.findIndex(item => {
|
||||
return item.uid === node.uid
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
return
|
||||
}
|
||||
nodeBorthers.splice(nodeIndex, 1)
|
||||
nodeParent.nodeData.children.splice(nodeIndex, 1)
|
||||
this.insertTo(node, exist, 'after')
|
||||
}
|
||||
|
||||
// 目标节点
|
||||
let existParent = exist.parent
|
||||
let existBorthers = existParent.children
|
||||
let existIndex = existBorthers.findIndex(item => {
|
||||
return item.uid === exist.uid
|
||||
// 将节点移动到另一个节点的前面或后面
|
||||
insertTo(node, exist, dir = 'before') {
|
||||
let nodeList = formatDataToArray(node)
|
||||
nodeList = nodeList.filter(item => {
|
||||
return !item.isRoot
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
if (dir === 'after') {
|
||||
nodeList.reverse()
|
||||
}
|
||||
existIndex++
|
||||
existBorthers.splice(existIndex, 0, node)
|
||||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render(() => {
|
||||
if (nodeLayerChanged) {
|
||||
node.reRender()
|
||||
nodeList.forEach(item => {
|
||||
this.checkNodeLayerChange(item, exist)
|
||||
// 移动节点
|
||||
let nodeParent = item.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
let nodeIndex = nodeBorthers.findIndex(item2 => {
|
||||
return item.uid === item2.uid
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
return
|
||||
}
|
||||
nodeBorthers.splice(nodeIndex, 1)
|
||||
nodeParent.nodeData.children.splice(nodeIndex, 1)
|
||||
|
||||
// 目标节点
|
||||
let existParent = exist.parent
|
||||
let existBorthers = existParent.children
|
||||
let existIndex = existBorthers.findIndex(item2 => {
|
||||
return item2.uid === exist.uid
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
}
|
||||
if (dir === 'after') {
|
||||
existIndex++
|
||||
}
|
||||
existBorthers.splice(existIndex, 0, item)
|
||||
existParent.nodeData.children.splice(existIndex, 0, item.nodeData)
|
||||
})
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 如果是富文本模式,那么某些层级变化需要更新样式
|
||||
checkNodeLayerChange(node, toNode) {
|
||||
if (this.mindMap.richText) {
|
||||
let nodeLayerChanged =
|
||||
(node.layerIndex === 1 && toNode.layerIndex !== 1) ||
|
||||
(node.layerIndex !== 1 && toNode.layerIndex === 1)
|
||||
if (nodeLayerChanged) {
|
||||
node.nodeData.data.resetRichText = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 移除节点
|
||||
removeNode(appointNodes = []) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
appointNodes = formatDataToArray(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
@@ -892,32 +986,40 @@ class Render {
|
||||
|
||||
// 移除某个指定节点
|
||||
removeOneNode(node) {
|
||||
let index = this.getNodeIndex(node)
|
||||
let index = getNodeIndex(node)
|
||||
node.remove()
|
||||
node.parent.children.splice(index, 1)
|
||||
node.parent.nodeData.children.splice(index, 1)
|
||||
}
|
||||
|
||||
// 复制节点,多个节点只会操作第一个节点
|
||||
// 复制节点
|
||||
copyNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
return copyNodeTree({}, this.activeNodeList[0], true)
|
||||
const nodeList = getTopAncestorsFomNodeList(this.activeNodeList)
|
||||
return nodeList.map(node => {
|
||||
return copyNodeTree({}, node, true)
|
||||
})
|
||||
}
|
||||
|
||||
// 剪切节点,多个节点只会操作第一个节点
|
||||
// 剪切节点
|
||||
cutNode(callback) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
let node = this.activeNodeList[0]
|
||||
if (node.isRoot) {
|
||||
return null
|
||||
}
|
||||
let copyData = copyNodeTree({}, node, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
const nodeList = getTopAncestorsFomNodeList(this.activeNodeList).filter(
|
||||
node => {
|
||||
return !node.isRoot
|
||||
}
|
||||
)
|
||||
const copyData = nodeList.map(node => {
|
||||
return copyNodeTree({}, node, true)
|
||||
})
|
||||
nodeList.forEach(node => {
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
})
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
this.mindMap.render()
|
||||
if (callback && typeof callback === 'function') {
|
||||
@@ -925,16 +1027,19 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 移动一个节点作为另一个节点的子节点
|
||||
// 移动节点作为另一个节点的子节点
|
||||
moveNodeTo(node, toNode) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
// let copyData = copyNodeTree({}, node, false, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
let nodeList = formatDataToArray(node)
|
||||
nodeList = nodeList.filter(item => {
|
||||
return !item.isRoot
|
||||
})
|
||||
nodeList.forEach(item => {
|
||||
this.checkNodeLayerChange(item, toNode)
|
||||
this.removeActiveNode(item)
|
||||
this.removeOneNode(item)
|
||||
toNode.nodeData.children.push(item.nodeData)
|
||||
})
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
toNode.nodeData.children.push(node.nodeData)
|
||||
this.mindMap.render()
|
||||
if (toNode.isRoot) {
|
||||
toNode.destroy()
|
||||
@@ -943,11 +1048,16 @@ class Render {
|
||||
|
||||
// 粘贴节点到节点
|
||||
pasteNode(data) {
|
||||
if (this.activeNodeList.length <= 0 || !data) {
|
||||
data = formatDataToArray(data)
|
||||
if (this.activeNodeList.length <= 0 || data.length <= 0) {
|
||||
return
|
||||
}
|
||||
this.activeNodeList.forEach(item => {
|
||||
item.nodeData.children.push(simpleDeepClone(data))
|
||||
this.activeNodeList.forEach(node => {
|
||||
node.nodeData.children.push(
|
||||
...data.map(item => {
|
||||
return simpleDeepClone(item)
|
||||
})
|
||||
)
|
||||
})
|
||||
this.mindMap.render()
|
||||
}
|
||||
@@ -1171,6 +1281,15 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点公式
|
||||
insertFormula(formula) {
|
||||
// 只在富文本模式下可用,并且需要注册Formula插件
|
||||
if (!this.mindMap.richText || !this.mindMap.formula) return
|
||||
this.activeNodeList.forEach(node => {
|
||||
this.mindMap.formula.insertFormulaToNode(node, formula)
|
||||
})
|
||||
}
|
||||
|
||||
// 添加节点概要
|
||||
addGeneralization(data) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getStrWithBrFromHtml, checkNodeOuter } from '../../utils'
|
||||
import { getStrWithBrFromHtml, checkNodeOuter, focusInput, selectAllInput } from '../../utils'
|
||||
import { ERROR_TYPES } from '../../constants/constant'
|
||||
|
||||
// 节点文字编辑类
|
||||
@@ -188,35 +188,16 @@ export default class TextEdit {
|
||||
this.showTextEdit = true
|
||||
// 选中文本
|
||||
// if (!this.cacheEditingText) {
|
||||
// this.selectNodeText()
|
||||
// selectAllInput(this.textEditNode)
|
||||
// }
|
||||
if (isInserting || (selectTextOnEnterEditText && !isFromKeyDown)) {
|
||||
this.selectNodeText()
|
||||
selectAllInput(this.textEditNode)
|
||||
} else {
|
||||
this.focus()
|
||||
focusInput(this.textEditNode)
|
||||
}
|
||||
this.cacheEditingText = ''
|
||||
}
|
||||
|
||||
// 聚焦
|
||||
focus() {
|
||||
let selection = window.getSelection()
|
||||
let range = document.createRange()
|
||||
range.selectNodeContents(this.textEditNode)
|
||||
range.collapse()
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
|
||||
// 选中文本
|
||||
selectNodeText() {
|
||||
let selection = window.getSelection()
|
||||
let range = document.createRange()
|
||||
range.selectNodeContents(this.textEditNode)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
|
||||
// 获取当前正在编辑的内容
|
||||
getEditText() {
|
||||
return getStrWithBrFromHtml(this.textEditNode.innerHTML)
|
||||
|
||||
@@ -14,7 +14,7 @@ class Node {
|
||||
constructor(opt = {}) {
|
||||
// 节点数据
|
||||
this.nodeData = this.handleData(opt.data || {})
|
||||
// id
|
||||
// uid
|
||||
this.uid = opt.uid
|
||||
// 控制实例
|
||||
this.mindMap = opt.mindMap
|
||||
@@ -440,12 +440,14 @@ class Node {
|
||||
this.mindMap.emit('node_mouseup', this, e)
|
||||
})
|
||||
this.group.on('mouseenter', e => {
|
||||
if (this.isDrag) return
|
||||
this._isMouseenter = true
|
||||
// 显示展开收起按钮
|
||||
this.showExpandBtn()
|
||||
this.mindMap.emit('node_mouseenter', this, e)
|
||||
})
|
||||
this.group.on('mouseleave', e => {
|
||||
if (!this._isMouseenter) return
|
||||
this._isMouseenter = false
|
||||
this.hideExpandBtn()
|
||||
this.mindMap.emit('node_mouseleave', this, e)
|
||||
@@ -701,6 +703,61 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置节点透明度
|
||||
// 包括连接线和下级节点
|
||||
setOpacity(val) {
|
||||
// 自身及连线
|
||||
this.group.opacity(val)
|
||||
this._lines.forEach(line => {
|
||||
line.opacity(val)
|
||||
})
|
||||
// 子节点
|
||||
this.children.forEach(item => {
|
||||
item.setOpacity(val)
|
||||
})
|
||||
// 概要节点
|
||||
if (this._generalizationNode) {
|
||||
this._generalizationLine.opacity(val)
|
||||
this._generalizationNode.group.opacity(val)
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏子节点
|
||||
hideChildren() {
|
||||
this._lines.forEach(item => {
|
||||
item.hide()
|
||||
})
|
||||
if (this.children && this.children.length) {
|
||||
this.children.forEach(item => {
|
||||
item.hide()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 显示子节点
|
||||
showChildren() {
|
||||
this._lines.forEach(item => {
|
||||
item.show()
|
||||
})
|
||||
if (this.children && this.children.length) {
|
||||
this.children.forEach(item => {
|
||||
item.show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 被拖拽中
|
||||
startDrag() {
|
||||
this.isDrag = true
|
||||
this.group.addClass('smm-node-dragging')
|
||||
}
|
||||
|
||||
// 拖拽结束
|
||||
endDrag() {
|
||||
this.isDrag = false
|
||||
this.group.removeClass('smm-node-dragging')
|
||||
}
|
||||
|
||||
// 连线
|
||||
renderLine(deep = false) {
|
||||
if (this.nodeData.data.expand === false) {
|
||||
|
||||
@@ -136,6 +136,7 @@ function renderExpandBtn() {
|
||||
this._expandBtn.on('dblclick', e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
this._expandBtn.addClass('smm-expand-btn')
|
||||
this.group.add(this._expandBtn)
|
||||
}
|
||||
this._showExpandBtn = true
|
||||
|
||||
@@ -149,8 +149,8 @@ class AssociativeLine {
|
||||
) {
|
||||
nodeToIds.set(cur, data.associativeLineTargets)
|
||||
}
|
||||
if (data.id) {
|
||||
idToNode.set(data.id, cur)
|
||||
if (data.uid) {
|
||||
idToNode.set(data.uid, cur)
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
@@ -158,8 +158,8 @@ class AssociativeLine {
|
||||
0
|
||||
)
|
||||
nodeToIds.forEach((ids, node) => {
|
||||
ids.forEach((id, index) => {
|
||||
let toNode = idToNode.get(id)
|
||||
ids.forEach((uid, index) => {
|
||||
let toNode = idToNode.get(uid)
|
||||
if (!node || !toNode) return
|
||||
const associativeLinePoint = (node.nodeData.data.associativeLinePoint ||
|
||||
[])[index]
|
||||
@@ -234,6 +234,11 @@ class AssociativeLine {
|
||||
controlPoints
|
||||
})
|
||||
})
|
||||
// 双击进入关联线文本编辑状态
|
||||
clickPath.dblclick(() => {
|
||||
if (!this.activeLine) return
|
||||
this.showEditTextBox(text)
|
||||
})
|
||||
// 渲染关联线文字
|
||||
this.renderText(this.getText(node, toNode), path, text)
|
||||
this.lineList.push([path, clickPath, text, node, toNode])
|
||||
@@ -252,28 +257,25 @@ class AssociativeLine {
|
||||
}) {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
// 如果当前存在激活节点,那么取消激活节点
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.clearActiveNodes()
|
||||
} else {
|
||||
// 否则清除当前的关联线的激活状态,如果有的话
|
||||
this.clearActiveLine()
|
||||
// 保存当前激活的关联线信息
|
||||
this.activeLine = [path, clickPath, text, node, toNode]
|
||||
// 让不可见的点击线显示
|
||||
clickPath.stroke({ color: associativeLineActiveColor })
|
||||
// 如果没有输入过关联线文字,那么显示默认文字
|
||||
if (!this.getText(node, toNode)) {
|
||||
this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text)
|
||||
}
|
||||
// 渲染控制点和连线
|
||||
this.renderControls(
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints[0],
|
||||
controlPoints[1]
|
||||
)
|
||||
this.mindMap.emit('associative_line_click', path, clickPath, node, toNode)
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
// 否则清除当前的关联线的激活状态,如果有的话
|
||||
this.clearActiveLine()
|
||||
// 保存当前激活的关联线信息
|
||||
this.activeLine = [path, clickPath, text, node, toNode]
|
||||
// 让不可见的点击线显示
|
||||
clickPath.stroke({ color: associativeLineActiveColor })
|
||||
// 如果没有输入过关联线文字,那么显示默认文字
|
||||
if (!this.getText(node, toNode)) {
|
||||
this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text)
|
||||
}
|
||||
// 渲染控制点和连线
|
||||
this.renderControls(
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints[0],
|
||||
controlPoints[1]
|
||||
)
|
||||
this.mindMap.emit('associative_line_click', path, clickPath, node, toNode)
|
||||
}
|
||||
|
||||
// 移除所有连接线
|
||||
@@ -395,21 +397,21 @@ class AssociativeLine {
|
||||
addLine(fromNode, toNode) {
|
||||
if (!fromNode || !toNode) return
|
||||
// 目标节点如果没有id,则生成一个id
|
||||
let id = toNode.nodeData.data.id
|
||||
if (!id) {
|
||||
id = uuid()
|
||||
let uid = toNode.nodeData.data.uid
|
||||
if (!uid) {
|
||||
uid = uuid()
|
||||
this.mindMap.execCommand('SET_NODE_DATA', toNode, {
|
||||
id
|
||||
uid
|
||||
})
|
||||
}
|
||||
// 将目标节点id保存起来
|
||||
let list = fromNode.nodeData.data.associativeLineTargets || []
|
||||
// 连线节点是否存在相同的id,存在则阻止添加关联线
|
||||
const sameLine = list.some(item => item === id)
|
||||
const sameLine = list.some(item => item === uid)
|
||||
if (sameLine) {
|
||||
return
|
||||
}
|
||||
list.push(id)
|
||||
list.push(uid)
|
||||
// 保存控制点
|
||||
let [startPoint, endPoint] = computeNodePoints(fromNode, toNode)
|
||||
let controlPoints = computeCubicBezierPathPoints(
|
||||
@@ -458,7 +460,7 @@ class AssociativeLine {
|
||||
let newAssociativeLineText = {}
|
||||
if (associativeLineText) {
|
||||
Object.keys(associativeLineText).forEach(item => {
|
||||
if (item !== toNode.nodeData.data.id) {
|
||||
if (item !== toNode.nodeData.data.uid) {
|
||||
newAssociativeLineText[item] = associativeLineText[item]
|
||||
}
|
||||
})
|
||||
@@ -483,13 +485,6 @@ class AssociativeLine {
|
||||
})
|
||||
}
|
||||
|
||||
// 清除当前激活的节点
|
||||
clearActiveNodes() {
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
}
|
||||
|
||||
// 清除激活的线
|
||||
clearActiveLine() {
|
||||
if (this.activeLine) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bfsWalk, throttle } from '../utils'
|
||||
import { bfsWalk, throttle, getTopAncestorsFomNodeList } from '../utils'
|
||||
import Base from '../layouts/Base'
|
||||
|
||||
// 节点拖动插件
|
||||
@@ -13,10 +13,14 @@ class Drag extends Base {
|
||||
|
||||
// 复位
|
||||
reset() {
|
||||
// 是否正在跳转中
|
||||
this.isDragging = false
|
||||
// 鼠标按下的节点
|
||||
this.mousedownNode = null
|
||||
// 被拖拽中的节点列表
|
||||
this.beingDragNodeList = []
|
||||
// 当前画布节点列表
|
||||
this.nodeList = []
|
||||
// 当前拖拽节点
|
||||
this.node = null
|
||||
// 当前重叠节点
|
||||
this.overlapNode = null
|
||||
// 当前上一个同级节点
|
||||
@@ -27,16 +31,11 @@ class Drag extends Base {
|
||||
this.drawTransform = null
|
||||
// 克隆节点
|
||||
this.clone = null
|
||||
// 连接线
|
||||
this.line = null
|
||||
// 同级位置占位符
|
||||
this.placeholder = null
|
||||
// 鼠标按下位置和节点左上角的偏移量
|
||||
this.offsetX = 0
|
||||
this.offsetY = 0
|
||||
// 克隆节点左上角的坐标
|
||||
this.cloneNodeLeft = 0
|
||||
this.cloneNodeTop = 0
|
||||
// 当前鼠标是否按下
|
||||
this.isMousedown = false
|
||||
// 拖拽的鼠标位置变量
|
||||
@@ -53,45 +52,42 @@ class Drag extends Base {
|
||||
bindEvent() {
|
||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
|
||||
this.mindMap.on('node_mousedown', (node, e) => {
|
||||
if (this.mindMap.opt.readonly || node.isGeneralization) {
|
||||
return
|
||||
}
|
||||
if (e.which !== 1 || node.isRoot) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
// 计算鼠标按下的位置距离节点左上角的距离
|
||||
this.drawTransform = this.mindMap.draw.transform()
|
||||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.offsetX = x - (node.left * scaleX + translateX)
|
||||
this.offsetY = y - (node.top * scaleY + translateY)
|
||||
this.node = node
|
||||
this.isMousedown = true
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
this.nodeTreeToList()
|
||||
})
|
||||
this.mindMap.on('mousemove', e => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
this.mindMap.emit('node_dragging', this.node)
|
||||
e.preventDefault()
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseMoveX = x
|
||||
this.mouseMoveY = y
|
||||
// 只读模式、不是鼠标左键按下、按下的是概要节点或根节点直接返回
|
||||
if (
|
||||
Math.abs(x - this.mouseDownX) <= this.checkDragOffset &&
|
||||
Math.abs(y - this.mouseDownY) <= this.checkDragOffset &&
|
||||
!this.node.isDrag
|
||||
this.mindMap.opt.readonly ||
|
||||
e.which !== 1 ||
|
||||
node.isGeneralization ||
|
||||
node.isRoot
|
||||
) {
|
||||
return
|
||||
}
|
||||
this.mindMap.renderer.clearAllActive()
|
||||
e.preventDefault()
|
||||
this.isMousedown = true
|
||||
// 记录鼠标按下时的节点
|
||||
this.mousedownNode = node
|
||||
// 记录鼠标按下的坐标
|
||||
const { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
})
|
||||
this.mindMap.on('mousemove', e => {
|
||||
if (this.mindMap.opt.readonly || !this.isMousedown) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
const { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseMoveX = x
|
||||
this.mouseMoveY = y
|
||||
// 还没开始移动时鼠标位移过小不认为是拖拽
|
||||
if (
|
||||
!this.isDragging &&
|
||||
Math.abs(x - this.mouseDownX) <= this.checkDragOffset &&
|
||||
Math.abs(y - this.mouseDownY) <= this.checkDragOffset
|
||||
) {
|
||||
return
|
||||
}
|
||||
this.mindMap.emit('node_dragging')
|
||||
this.handleStartMove()
|
||||
this.onMove(x, y, e)
|
||||
})
|
||||
this.onMouseup = this.onMouseup.bind(this)
|
||||
@@ -105,9 +101,12 @@ class Drag extends Base {
|
||||
return
|
||||
}
|
||||
this.isMousedown = false
|
||||
let _nodeIsDrag = this.node.isDrag
|
||||
this.node.isDrag = false
|
||||
this.node.show()
|
||||
// 恢复被拖拽节点的临时设置
|
||||
this.beingDragNodeList.forEach(node => {
|
||||
node.setOpacity(1)
|
||||
node.showChildren()
|
||||
node.endDrag()
|
||||
})
|
||||
this.removeCloneNode()
|
||||
let overlapNodeUid = this.overlapNode
|
||||
? this.overlapNode.nodeData.data.uid
|
||||
@@ -117,17 +116,33 @@ class Drag extends Base {
|
||||
// 存在重叠子节点,则移动作为其子节点
|
||||
if (this.overlapNode) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
|
||||
this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode)
|
||||
this.mindMap.execCommand(
|
||||
'MOVE_NODE_TO',
|
||||
this.beingDragNodeList,
|
||||
this.overlapNode
|
||||
)
|
||||
} else if (this.prevNode) {
|
||||
// 存在前一个相邻节点,作为其下一个兄弟节点
|
||||
this.mindMap.renderer.setNodeActive(this.prevNode, false)
|
||||
this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode)
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_AFTER',
|
||||
this.beingDragNodeList,
|
||||
this.prevNode
|
||||
)
|
||||
} else if (this.nextNode) {
|
||||
// 存在下一个相邻节点,作为其前一个兄弟节点
|
||||
this.mindMap.renderer.setNodeActive(this.nextNode, false)
|
||||
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
|
||||
} else if (_nodeIsDrag && this.mindMap.opt.enableFreeDrag) {
|
||||
// 自定义位置
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_BEFORE',
|
||||
this.beingDragNodeList,
|
||||
this.nextNode
|
||||
)
|
||||
} else if (
|
||||
this.clone &&
|
||||
this.mindMap.opt.enableFreeDrag &&
|
||||
this.beingDragNodeList.length === 1
|
||||
) {
|
||||
// 如果只拖拽了一个节点,那么设置自定义位置
|
||||
let { x, y } = this.mindMap.toPos(
|
||||
e.clientX - this.offsetX,
|
||||
e.clientY - this.offsetY
|
||||
@@ -135,11 +150,16 @@ class Drag extends Base {
|
||||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||||
x = (x - translateX) / scaleX
|
||||
y = (y - translateY) / scaleY
|
||||
this.node.left = x
|
||||
this.node.top = y
|
||||
this.node.customLeft = x
|
||||
this.node.customTop = y
|
||||
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
|
||||
this.mousedownNode.left = x
|
||||
this.mousedownNode.top = y
|
||||
this.mousedownNode.customLeft = x
|
||||
this.mousedownNode.customTop = y
|
||||
this.mindMap.execCommand(
|
||||
'SET_NODE_CUSTOM_POSITION',
|
||||
this.mousedownNode,
|
||||
x,
|
||||
y
|
||||
)
|
||||
this.mindMap.render()
|
||||
}
|
||||
this.reset()
|
||||
@@ -150,24 +170,131 @@ class Drag extends Base {
|
||||
})
|
||||
}
|
||||
|
||||
// 拖动中
|
||||
onMove(x, y, e) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
// 更新克隆节点的位置
|
||||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||||
let cloneNodeLeft = x - this.offsetX
|
||||
let cloneNodeTop = y - this.offsetY
|
||||
x = (cloneNodeLeft - translateX) / scaleX
|
||||
y = (cloneNodeTop - translateY) / scaleY
|
||||
let t = this.clone.transform()
|
||||
this.clone.translate(x - t.translateX, y - t.translateY)
|
||||
// 检测新位置
|
||||
this.checkOverlapNode()
|
||||
// 如果注册了多选节点插件,那么复用它的边缘自动移动画布功能
|
||||
if (this.mindMap.opt.autoMoveWhenMouseInEdgeOnDrag && this.mindMap.select) {
|
||||
this.drawTransform = this.mindMap.draw.transform()
|
||||
this.mindMap.select.clearAutoMoveTimer()
|
||||
this.mindMap.select.onMove(e.clientX, e.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
// 开始拖拽时初始化一些数据
|
||||
handleStartMove() {
|
||||
if (!this.isDragging) {
|
||||
this.isDragging = true
|
||||
// 鼠标按下的节点
|
||||
let node = this.mousedownNode
|
||||
// 计算鼠标按下的位置距离节点左上角的距离
|
||||
this.drawTransform = this.mindMap.draw.transform()
|
||||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||||
this.offsetX = this.mouseDownX - (node.left * scaleX + translateX)
|
||||
this.offsetY = this.mouseDownY - (node.top * scaleY + translateY)
|
||||
// 如果鼠标按下的节点是激活节点,那么保存当前所有激活的节点
|
||||
if (node.nodeData.data.isActive) {
|
||||
// 找出这些激活节点中的最顶层节点
|
||||
this.beingDragNodeList = getTopAncestorsFomNodeList(
|
||||
// 过滤掉根节点和概要节点
|
||||
this.mindMap.renderer.activeNodeList.filter(item => {
|
||||
return !item.isRoot && !item.isGeneralization
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// 否则只拖拽按下的节点
|
||||
this.beingDragNodeList = [node]
|
||||
}
|
||||
// 将节点树转为节点数组
|
||||
this.nodeTreeToList()
|
||||
// 创建克隆节点
|
||||
this.createCloneNode()
|
||||
// 清除当前所有激活的节点
|
||||
this.mindMap.renderer.clearAllActive()
|
||||
}
|
||||
}
|
||||
|
||||
// 节点由树转换成数组,从子节点到根节点
|
||||
nodeTreeToList() {
|
||||
const list = []
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
// 过滤掉当前被拖拽的节点
|
||||
if (this.checkIsInBeingDragNodeList(node)) {
|
||||
return
|
||||
}
|
||||
if (!list[node.layerIndex]) {
|
||||
list[node.layerIndex] = []
|
||||
}
|
||||
list[node.layerIndex].push(node)
|
||||
})
|
||||
this.nodeList = list.reduceRight((res, cur) => {
|
||||
return [...res, ...cur]
|
||||
}, [])
|
||||
}
|
||||
|
||||
// 创建克隆节点
|
||||
createCloneNode() {
|
||||
if (!this.clone) {
|
||||
// 节点
|
||||
this.clone = this.node.group.clone()
|
||||
this.clone.opacity(0.5)
|
||||
const {
|
||||
dragMultiNodeRectConfig,
|
||||
dragPlaceholderRectFill,
|
||||
dragOpacityConfig
|
||||
} = this.mindMap.opt
|
||||
const {
|
||||
width: rectWidth,
|
||||
height: rectHeight,
|
||||
fill: rectFill
|
||||
} = dragMultiNodeRectConfig
|
||||
const node = this.beingDragNodeList[0]
|
||||
const lineColor = node.style.merge('lineColor', true)
|
||||
// 如果当前被拖拽的节点数量大于1,那么创建一个矩形示意
|
||||
if (this.beingDragNodeList.length > 1) {
|
||||
this.clone = this.draw
|
||||
.rect()
|
||||
.size(rectWidth, rectHeight)
|
||||
.radius(rectHeight / 2)
|
||||
.fill({
|
||||
color: rectFill || lineColor
|
||||
})
|
||||
this.offsetX = rectWidth / 2
|
||||
this.offsetY = rectHeight / 2
|
||||
} else {
|
||||
// 否则克隆当前的节点
|
||||
this.clone = node.group.clone()
|
||||
// 删除展开收起按钮元素
|
||||
const expandEl = this.clone.findOne('.smm-expand-btn')
|
||||
if (expandEl) {
|
||||
expandEl.remove()
|
||||
}
|
||||
this.mindMap.draw.add(this.clone)
|
||||
}
|
||||
this.clone.opacity(dragOpacityConfig.cloneNodeOpacity)
|
||||
this.clone.css('z-index', 99999)
|
||||
this.node.isDrag = true
|
||||
this.node.hide()
|
||||
// 连接线
|
||||
this.line = this.draw.path()
|
||||
this.line.opacity(0.5)
|
||||
this.node.styleLine(this.line, this.node)
|
||||
// 同级位置占位符
|
||||
// 同级位置提示元素
|
||||
this.placeholder = this.draw.rect().fill({
|
||||
color: this.node.style.merge('lineColor', true)
|
||||
color: dragPlaceholderRectFill || lineColor
|
||||
})
|
||||
// 当前被拖拽的节点的临时设置
|
||||
this.beingDragNodeList.forEach(node => {
|
||||
// 降低透明度
|
||||
node.setOpacity(dragOpacityConfig.beingDragNodeOpacity)
|
||||
// 隐藏连线及下级节点
|
||||
node.hideChildren()
|
||||
// 设置拖拽状态
|
||||
node.startDrag()
|
||||
})
|
||||
this.mindMap.draw.add(this.clone)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,42 +304,9 @@ class Drag extends Base {
|
||||
return
|
||||
}
|
||||
this.clone.remove()
|
||||
this.line.remove()
|
||||
this.placeholder.remove()
|
||||
}
|
||||
|
||||
// 拖动中
|
||||
onMove(x, y, e) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
this.createCloneNode()
|
||||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||||
this.cloneNodeLeft = x - this.offsetX
|
||||
this.cloneNodeTop = y - this.offsetY
|
||||
x = (this.cloneNodeLeft - translateX) / scaleX
|
||||
y = (this.cloneNodeTop - translateY) / scaleY
|
||||
let t = this.clone.transform()
|
||||
this.clone.translate(x - t.translateX, y - t.translateY)
|
||||
// 连接线
|
||||
let parent = this.node.parent
|
||||
this.line.plot(
|
||||
this.quadraticCurvePath(
|
||||
parent.left + parent.width / 2,
|
||||
parent.top + parent.height / 2,
|
||||
x + this.node.width / 2,
|
||||
y + this.node.height / 2
|
||||
)
|
||||
)
|
||||
this.checkOverlapNode()
|
||||
// 如果注册了多选节点插件,那么复用它的边缘自动移动画布功能
|
||||
if (this.mindMap.opt.autoMoveWhenMouseInEdgeOnDrag && this.mindMap.select) {
|
||||
this.drawTransform = this.mindMap.draw.transform()
|
||||
this.mindMap.select.clearAutoMoveTimer()
|
||||
this.mindMap.select.onMove(e.clientX, e.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测重叠节点
|
||||
checkOverlapNode() {
|
||||
if (!this.drawTransform || !this.placeholder) {
|
||||
@@ -226,9 +320,6 @@ class Drag extends Base {
|
||||
if (node.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
}
|
||||
if (node.uid === this.node.uid || this.node.isParent(node)) {
|
||||
return
|
||||
}
|
||||
if (this.overlapNode || (this.prevNode && this.nextNode)) {
|
||||
return
|
||||
}
|
||||
@@ -484,7 +575,7 @@ class Drag extends Base {
|
||||
if (node.layerIndex === 1) {
|
||||
sameDir = item.dir === node.dir
|
||||
}
|
||||
return item !== this.node && sameDir
|
||||
return sameDir && !this.checkIsInBeingDragNodeList(item)
|
||||
})
|
||||
: []
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
@@ -535,7 +626,7 @@ class Drag extends Base {
|
||||
handleFishbone(node) {
|
||||
let checkList = node.parent
|
||||
? node.parent.children.filter(item => {
|
||||
return item !== this.node && item.layerIndex > 1
|
||||
return item.layerIndex > 1 && !this.checkIsInBeingDragNodeList(item)
|
||||
})
|
||||
: []
|
||||
if (node.layerIndex === 1) {
|
||||
@@ -553,8 +644,8 @@ class Drag extends Base {
|
||||
// 获取节点的兄弟节点列表通用方法
|
||||
commonGetNodeCheckList(node) {
|
||||
return node.parent
|
||||
? node.parent.children.filter(item => {
|
||||
return item !== this.node
|
||||
? [...node.parent.children].filter(item => {
|
||||
return !this.checkIsInBeingDragNodeList(item)
|
||||
})
|
||||
: []
|
||||
}
|
||||
@@ -585,18 +676,11 @@ class Drag extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
// 节点由树转换成数组,从子节点到根节点
|
||||
nodeTreeToList() {
|
||||
const list = []
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (!list[node.layerIndex]) {
|
||||
list[node.layerIndex] = []
|
||||
}
|
||||
list[node.layerIndex].push(node)
|
||||
// 检查某个节点是否在被拖拽节点内
|
||||
checkIsInBeingDragNodeList(node) {
|
||||
return !!this.beingDragNodeList.find(item => {
|
||||
return item.uid === node.uid || item.isParent(node)
|
||||
})
|
||||
this.nodeList = list.reduceRight((res, cur) => {
|
||||
return [...res, ...cur]
|
||||
}, [])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class Export {
|
||||
let task = imageList.map(async item => {
|
||||
let imgUlr = item.attr('href') || item.attr('xlink:href')
|
||||
// 已经是data:URL形式不用转换
|
||||
if (/^data:/.test(imgUlr)) {
|
||||
if (/^data:/.test(imgUlr) || imgUlr === 'none') {
|
||||
return
|
||||
}
|
||||
let imgData = await imgToDataUrl(imgUlr)
|
||||
|
||||
53
simple-mind-map/src/plugins/Formula.js
Normal file
53
simple-mind-map/src/plugins/Formula.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import katex from 'katex'
|
||||
import Quill from 'quill'
|
||||
|
||||
// 数学公式支持插件
|
||||
// 该插件在富文本模式下可用
|
||||
class Formula {
|
||||
// 构造函数
|
||||
constructor(opt) {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
window.katex = katex
|
||||
this.extendQuill()
|
||||
}
|
||||
|
||||
// 修改formula格式工具
|
||||
extendQuill() {
|
||||
const QuillFormula = Quill.import('formats/formula')
|
||||
|
||||
class CustomFormulaBlot extends QuillFormula {
|
||||
static create(value) {
|
||||
let node = super.create(value)
|
||||
if (typeof value === 'string') {
|
||||
katex.render(value, node, {
|
||||
throwOnError: false,
|
||||
errorColor: '#f00',
|
||||
output: 'mathml' // 增加该配置,默认只输出公式
|
||||
})
|
||||
node.setAttribute('data-value', value)
|
||||
}
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
Quill.register('formats/formula', CustomFormulaBlot, true)
|
||||
}
|
||||
|
||||
// 给指定的节点插入指定公式
|
||||
insertFormulaToNode(node, formula) {
|
||||
let richTextPlugin = this.mindMap.richText
|
||||
richTextPlugin.showEditText(node)
|
||||
richTextPlugin.quill.insertEmbed(
|
||||
richTextPlugin.quill.getLength() - 1,
|
||||
'formula',
|
||||
formula
|
||||
)
|
||||
richTextPlugin.setTextStyleIfNotRichText(richTextPlugin.node)
|
||||
richTextPlugin.hideEditText([node])
|
||||
}
|
||||
}
|
||||
|
||||
Formula.instanceName = 'formula'
|
||||
|
||||
export default Formula
|
||||
@@ -623,7 +623,7 @@ class RichText {
|
||||
// 处理导入数据
|
||||
handleSetData(data) {
|
||||
let walk = root => {
|
||||
if (!root.data.richText) {
|
||||
if (root.data && !root.data.richText) {
|
||||
root.data.richText = true
|
||||
root.data.resetRichText = true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bfsWalk, throttle } from '../utils'
|
||||
import { bfsWalk, throttle, checkTwoRectIsOverlap } from '../utils'
|
||||
|
||||
// 节点选择插件
|
||||
class Select {
|
||||
@@ -210,24 +210,19 @@ class Select {
|
||||
left = left * scaleX + translateX
|
||||
top = top * scaleY + translateY
|
||||
if (
|
||||
((left >= minx && left <= maxx) || (right >= minx && right <= maxx)) &&
|
||||
((top >= miny && top <= maxy) || (bottom >= miny && bottom <= maxy))
|
||||
checkTwoRectIsOverlap(minx, maxx, miny, maxy, left, right, top, bottom)
|
||||
) {
|
||||
// this.mindMap.batchExecution.push('activeNode' + node.uid, () => {
|
||||
if (node.nodeData.data.isActive) {
|
||||
return
|
||||
}
|
||||
this.mindMap.renderer.setNodeActive(node, true)
|
||||
this.mindMap.renderer.addActiveNode(node)
|
||||
// })
|
||||
} else if (node.nodeData.data.isActive) {
|
||||
// this.mindMap.batchExecution.push('activeNode' + node.uid, () => {
|
||||
if (!node.nodeData.data.isActive) {
|
||||
return
|
||||
}
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
this.mindMap.renderer.removeActiveNode(node)
|
||||
// })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Text } from '@svgdotjs/svg.js'
|
||||
import { getStrWithBrFromHtml } from '../../utils/index'
|
||||
import {
|
||||
getStrWithBrFromHtml,
|
||||
focusInput,
|
||||
selectAllInput
|
||||
} from '../../utils/index'
|
||||
|
||||
// 创建文字节点
|
||||
function createText(data) {
|
||||
@@ -36,7 +40,7 @@ function showEditTextBox(g) {
|
||||
this.mindMap.keyCommand.addShortcut('Enter', () => {
|
||||
this.hideEditTextBox()
|
||||
})
|
||||
|
||||
// 输入框元素没有创建过,则先创建
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||
@@ -55,20 +59,27 @@ function showEditTextBox(g) {
|
||||
associativeLineTextFontFamily,
|
||||
associativeLineTextLineHeight
|
||||
} = this.mindMap.themeConfig
|
||||
let { defaultAssociativeLineText, nodeTextEditZIndex } = this.mindMap.opt
|
||||
let scale = this.mindMap.view.scale
|
||||
let [, , , node, toNode] = this.activeLine
|
||||
let textLines = (
|
||||
this.getText(node, toNode) || this.mindMap.opt.defaultAssociativeLineText
|
||||
).split(/\n/gim)
|
||||
let text = this.getText(node, toNode)
|
||||
let textLines = (text || defaultAssociativeLineText).split(/\n/gim)
|
||||
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
|
||||
this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px'
|
||||
this.textEditNode.style.lineHeight =
|
||||
textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
|
||||
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.updateTextEditBoxPos(g)
|
||||
this.showTextEdit = true
|
||||
// 如果是默认文本要全选输入框
|
||||
if (text === '' || text === defaultAssociativeLineText) {
|
||||
selectAllInput(this.textEditNode)
|
||||
} else {
|
||||
// 否则聚焦即可
|
||||
focusInput(this.textEditNode)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理画布缩放
|
||||
@@ -94,10 +105,13 @@ function hideEditTextBox() {
|
||||
}
|
||||
let [path, , text, node, toNode] = this.activeLine
|
||||
let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
|
||||
// 如果是默认文本,那么不保存
|
||||
let isDefaultText = str === this.mindMap.opt.defaultAssociativeLineText
|
||||
str = isDefaultText ? '' : str
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineText: {
|
||||
...(node.nodeData.data.associativeLineText || {}),
|
||||
[toNode.nodeData.data.id]: str
|
||||
[toNode.nodeData.data.uid]: str
|
||||
}
|
||||
})
|
||||
this.textEditNode.style.display = 'none'
|
||||
@@ -113,7 +127,7 @@ function getText(node, toNode) {
|
||||
if (!obj) {
|
||||
return ''
|
||||
}
|
||||
return obj[toNode.nodeData.data.id] || ''
|
||||
return obj[toNode.nodeData.data.uid] || ''
|
||||
}
|
||||
|
||||
// 渲染关联线文字
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 获取目标节点在起始节点的目标数组中的索引
|
||||
export const getAssociativeLineTargetIndex = (node, toNode) => {
|
||||
return node.nodeData.data.associativeLineTargets.findIndex(item => {
|
||||
return item === toNode.nodeData.data.id
|
||||
return item === toNode.nodeData.data.uid
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -170,9 +170,8 @@ export const copyNodeTree = (
|
||||
keepId = false
|
||||
) => {
|
||||
tree.data = simpleDeepClone(root.nodeData ? root.nodeData.data : root.data)
|
||||
// 去除节点id,因为节点id不能重复
|
||||
if (tree.data.id && !keepId) delete tree.data.id
|
||||
if (tree.data.uid) delete tree.data.uid
|
||||
// 去除节点uid,因为节点uid不能重复
|
||||
if (tree.data.uid && !keepId) delete tree.data.uid
|
||||
if (removeActiveState) {
|
||||
tree.data.isActive = false
|
||||
}
|
||||
@@ -705,3 +704,102 @@ export const mergerIconListBy = (arrList, key, name) => {
|
||||
return result
|
||||
}, [])
|
||||
}
|
||||
|
||||
// 从节点实例列表里找出顶层的节点
|
||||
export const getTopAncestorsFomNodeList = list => {
|
||||
let res = []
|
||||
list.forEach(node => {
|
||||
if (
|
||||
!list.find(item => {
|
||||
return item.uid !== node.uid && item.isParent(node)
|
||||
})
|
||||
) {
|
||||
res.push(node)
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// 判断两个矩形是否重叠
|
||||
export const checkTwoRectIsOverlap = (
|
||||
minx1,
|
||||
maxx1,
|
||||
miny1,
|
||||
maxy1,
|
||||
minx2,
|
||||
maxx2,
|
||||
miny2,
|
||||
maxy2
|
||||
) => {
|
||||
return maxx1 > minx2 && maxx2 > minx1 && maxy1 > miny2 && maxy2 > miny1
|
||||
}
|
||||
|
||||
// 聚焦指定输入框
|
||||
export const focusInput = el => {
|
||||
let selection = window.getSelection()
|
||||
let range = document.createRange()
|
||||
range.selectNodeContents(el)
|
||||
range.collapse()
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
|
||||
// 聚焦全选指定输入框
|
||||
export const selectAllInput = el => {
|
||||
let selection = window.getSelection()
|
||||
let range = document.createRange()
|
||||
range.selectNodeContents(el)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
|
||||
// 给指定的节点列表树数据添加附加数据,会修改原数据
|
||||
export const addDataToAppointNodes = (appointNodes, data = {}) => {
|
||||
const walk = list => {
|
||||
list.forEach(node => {
|
||||
node.data = {
|
||||
...node.data,
|
||||
...data
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
walk(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
walk(appointNodes)
|
||||
return appointNodes
|
||||
}
|
||||
|
||||
// 给指定的节点列表树数据添加uid,如果不存在的话,会修改原数据
|
||||
export const createUidForAppointNodes = appointNodes => {
|
||||
const walk = list => {
|
||||
list.forEach(node => {
|
||||
if (!node.data) {
|
||||
node.data = {}
|
||||
}
|
||||
if (isUndef(node.data.uid)) {
|
||||
node.data.uid = createUid()
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
walk(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
walk(appointNodes)
|
||||
return appointNodes
|
||||
}
|
||||
|
||||
// 传入一个数据,如果该数据是数组,那么返回该数组,否则返回一个以该数据为成员的数组
|
||||
export const formatDataToArray = data => {
|
||||
if (!data) return []
|
||||
return Array.isArray(data) ? data : [data]
|
||||
}
|
||||
|
||||
// 获取节点在同级里的位置索引
|
||||
export const getNodeIndex = node => {
|
||||
return node.parent
|
||||
? node.parent.children.findIndex(item => {
|
||||
return item.uid === node.uid
|
||||
})
|
||||
: 0
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2479351 */
|
||||
src: url('iconfont.woff2?t=1691822758372') format('woff2'),
|
||||
url('iconfont.woff?t=1691822758372') format('woff'),
|
||||
url('iconfont.ttf?t=1691822758372') format('truetype');
|
||||
src: url('iconfont.woff2?t=1695365666344') format('woff2'),
|
||||
url('iconfont.woff?t=1695365666344') format('woff'),
|
||||
url('iconfont.ttf?t=1695365666344') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icongongshi:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.icontouming:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,60 +1,83 @@
|
||||
// 布局结构图片映射
|
||||
export const layoutImgMap = {
|
||||
logicalStructure: require('../assets/img/structures/logicalStructure.png'),
|
||||
mindMap: require('../assets/img/structures/mindMap.png'),
|
||||
organizationStructure: require('../assets/img/structures/organizationStructure.png'),
|
||||
catalogOrganization: require('../assets/img/structures/catalogOrganization.png'),
|
||||
timeline: require('../assets/img/structures/timeline.png'),
|
||||
timeline2: require('../assets/img/structures/timeline2.png'),
|
||||
fishbone: require('../assets/img/structures/fishbone.png'),
|
||||
verticalTimeline: require('../assets/img/structures/verticalTimeline.png'),
|
||||
logicalStructure: require('../assets/img/structures/logicalStructure.png'),
|
||||
mindMap: require('../assets/img/structures/mindMap.png'),
|
||||
organizationStructure: require('../assets/img/structures/organizationStructure.png'),
|
||||
catalogOrganization: require('../assets/img/structures/catalogOrganization.png'),
|
||||
timeline: require('../assets/img/structures/timeline.png'),
|
||||
timeline2: require('../assets/img/structures/timeline2.png'),
|
||||
fishbone: require('../assets/img/structures/fishbone.png'),
|
||||
verticalTimeline: require('../assets/img/structures/verticalTimeline.png')
|
||||
}
|
||||
|
||||
// 主题图片映射
|
||||
export const themeMap = {
|
||||
default: require('../assets/img/themes/default.jpg'),
|
||||
classic: require('../assets/img/themes/classic.jpg'),
|
||||
minions: require('../assets/img/themes/minions.jpg'),
|
||||
pinkGrape: require('../assets/img/themes/pinkGrape.jpg'),
|
||||
mint: require('../assets/img/themes/mint.jpg'),
|
||||
gold: require('../assets/img/themes/gold.jpg'),
|
||||
vitalityOrange: require('../assets/img/themes/vitalityOrange.jpg'),
|
||||
greenLeaf: require('../assets/img/themes/greenLeaf.jpg'),
|
||||
dark2: require('../assets/img/themes/dark2.jpg'),
|
||||
skyGreen: require('../assets/img/themes/skyGreen.jpg'),
|
||||
classic2: require('../assets/img/themes/classic2.jpg'),
|
||||
classic3: require('../assets/img/themes/classic3.jpg'),
|
||||
classic4: require('../assets/img/themes/classic4.jpg'),
|
||||
classicGreen: require('../assets/img/themes/classicGreen.jpg'),
|
||||
classicBlue: require('../assets/img/themes/classicBlue.jpg'),
|
||||
blueSky: require('../assets/img/themes/blueSky.jpg'),
|
||||
brainImpairedPink: require('../assets/img/themes/brainImpairedPink.jpg'),
|
||||
dark: require('../assets/img/themes/dark.jpg'),
|
||||
earthYellow: require('../assets/img/themes/earthYellow.jpg'),
|
||||
freshGreen: require('../assets/img/themes/freshGreen.jpg'),
|
||||
freshRed: require('../assets/img/themes/freshRed.jpg'),
|
||||
romanticPurple: require('../assets/img/themes/romanticPurple.jpg'),
|
||||
simpleBlack: require('../assets/img/themes/simpleBlack.jpg'),
|
||||
courseGreen: require('../assets/img/themes/courseGreen.jpg'),
|
||||
coffee: require('../assets/img/themes/coffee.jpg'),
|
||||
redSpirit: require('../assets/img/themes/redSpirit.jpg'),
|
||||
blackHumour: require('../assets/img/themes/blackHumour.jpg'),
|
||||
lateNightOffice: require('../assets/img/themes/lateNightOffice.jpg'),
|
||||
blackGold: require('../assets/img/themes/blackGold.jpg'),
|
||||
autumn: require('../assets/img/themes/autumn.jpg'),
|
||||
avocado: require('../assets/img/themes/avocado.jpg'),
|
||||
orangeJuice: require('../assets/img/themes/orangeJuice.jpg'),
|
||||
oreo: require('../assets/img/themes/oreo.jpg'),
|
||||
shallowSea: require('../assets/img/themes/shallowSea.jpg'),
|
||||
lemonBubbles: require('../assets/img/themes/lemonBubbles.jpg'),
|
||||
rose: require('../assets/img/themes/rose.jpg'),
|
||||
seaBlueLine: require('../assets/img/themes/seaBlueLine.jpg'),
|
||||
neonLamp: require('../assets/img/themes/neonLamp.jpg'),
|
||||
darkNightLceBlade: require('../assets/img/themes/darkNightLceBlade.jpg'),
|
||||
morandi: require('../assets/img/themes/morandi.jpg'),
|
||||
classic5: require('../assets/img/themes/classic5.jpg'),
|
||||
dark3: require('../assets/img/themes/dark3.jpg'),
|
||||
dark4: require('../assets/img/themes/dark4.jpg'),
|
||||
cactus: require('../assets/img/themes/cactus.jpg'),
|
||||
default: require('../assets/img/themes/default.jpg'),
|
||||
classic: require('../assets/img/themes/classic.jpg'),
|
||||
minions: require('../assets/img/themes/minions.jpg'),
|
||||
pinkGrape: require('../assets/img/themes/pinkGrape.jpg'),
|
||||
mint: require('../assets/img/themes/mint.jpg'),
|
||||
gold: require('../assets/img/themes/gold.jpg'),
|
||||
vitalityOrange: require('../assets/img/themes/vitalityOrange.jpg'),
|
||||
greenLeaf: require('../assets/img/themes/greenLeaf.jpg'),
|
||||
dark2: require('../assets/img/themes/dark2.jpg'),
|
||||
skyGreen: require('../assets/img/themes/skyGreen.jpg'),
|
||||
classic2: require('../assets/img/themes/classic2.jpg'),
|
||||
classic3: require('../assets/img/themes/classic3.jpg'),
|
||||
classic4: require('../assets/img/themes/classic4.jpg'),
|
||||
classicGreen: require('../assets/img/themes/classicGreen.jpg'),
|
||||
classicBlue: require('../assets/img/themes/classicBlue.jpg'),
|
||||
blueSky: require('../assets/img/themes/blueSky.jpg'),
|
||||
brainImpairedPink: require('../assets/img/themes/brainImpairedPink.jpg'),
|
||||
dark: require('../assets/img/themes/dark.jpg'),
|
||||
earthYellow: require('../assets/img/themes/earthYellow.jpg'),
|
||||
freshGreen: require('../assets/img/themes/freshGreen.jpg'),
|
||||
freshRed: require('../assets/img/themes/freshRed.jpg'),
|
||||
romanticPurple: require('../assets/img/themes/romanticPurple.jpg'),
|
||||
simpleBlack: require('../assets/img/themes/simpleBlack.jpg'),
|
||||
courseGreen: require('../assets/img/themes/courseGreen.jpg'),
|
||||
coffee: require('../assets/img/themes/coffee.jpg'),
|
||||
redSpirit: require('../assets/img/themes/redSpirit.jpg'),
|
||||
blackHumour: require('../assets/img/themes/blackHumour.jpg'),
|
||||
lateNightOffice: require('../assets/img/themes/lateNightOffice.jpg'),
|
||||
blackGold: require('../assets/img/themes/blackGold.jpg'),
|
||||
autumn: require('../assets/img/themes/autumn.jpg'),
|
||||
avocado: require('../assets/img/themes/avocado.jpg'),
|
||||
orangeJuice: require('../assets/img/themes/orangeJuice.jpg'),
|
||||
oreo: require('../assets/img/themes/oreo.jpg'),
|
||||
shallowSea: require('../assets/img/themes/shallowSea.jpg'),
|
||||
lemonBubbles: require('../assets/img/themes/lemonBubbles.jpg'),
|
||||
rose: require('../assets/img/themes/rose.jpg'),
|
||||
seaBlueLine: require('../assets/img/themes/seaBlueLine.jpg'),
|
||||
neonLamp: require('../assets/img/themes/neonLamp.jpg'),
|
||||
darkNightLceBlade: require('../assets/img/themes/darkNightLceBlade.jpg'),
|
||||
morandi: require('../assets/img/themes/morandi.jpg'),
|
||||
classic5: require('../assets/img/themes/classic5.jpg'),
|
||||
dark3: require('../assets/img/themes/dark3.jpg'),
|
||||
dark4: require('../assets/img/themes/dark4.jpg'),
|
||||
cactus: require('../assets/img/themes/cactus.jpg')
|
||||
}
|
||||
|
||||
|
||||
// 公式列表
|
||||
export const formulaList = [
|
||||
'a^2',
|
||||
'a_2',
|
||||
'a^{2+2}',
|
||||
'a_{i,j}',
|
||||
'x_2^3',
|
||||
'\\overbrace{1+2+\\cdots+100}',
|
||||
'\\sum_{k=1}^N k^2',
|
||||
'\\lim_{n \\to \\infty}x_n',
|
||||
'\\int_{-N}^{N} e^x\\, dx',
|
||||
'\\sqrt{3}',
|
||||
'\\sqrt[n]{3}',
|
||||
'\\sin\\theta',
|
||||
'\\log X',
|
||||
'\\log_{10}',
|
||||
'\\log_\\alpha X',
|
||||
'\\lim_{t\\to n}T',
|
||||
'\\frac{1}{2}=0.5',
|
||||
'\\binom{n}{k}',
|
||||
'\\begin{matrix}x & y \\\\z & v\\end{matrix}',
|
||||
'\\begin{cases}3x + 5y + z \\\\7x - 2y + 4z \\\\-6x + 3y + 2z\\end{cases}'
|
||||
]
|
||||
|
||||
@@ -103,11 +103,13 @@ export default {
|
||||
markdownFile: 'markdown file',
|
||||
tips: 'tips: .smm and .json file can be import',
|
||||
isTransparent: 'Background is transparent',
|
||||
pngTips: 'tips: Exporting pictures in rich text mode is time-consuming. It is recommended to export to svg format',
|
||||
pngTips:
|
||||
'tips: Exporting pictures in rich text mode is time-consuming. It is recommended to export to svg format',
|
||||
svgTips: 'tips: Exporting pictures in rich text mode is time-consuming',
|
||||
transformingDomToImages: 'Converting nodes: ',
|
||||
notifyTitle: 'Info',
|
||||
notifyMessage: 'If the download is not triggered, check whether it is blocked by the browser',
|
||||
notifyMessage:
|
||||
'If the download is not triggered, check whether it is blocked by the browser',
|
||||
paddingX: 'Padding x',
|
||||
paddingY: 'Padding y',
|
||||
useMultiPageExport: 'Export multi page'
|
||||
@@ -213,15 +215,19 @@ export default {
|
||||
export: 'Export',
|
||||
shortcutKey: 'Shortcut key',
|
||||
associativeLine: 'Associative line',
|
||||
painter: 'Painter'
|
||||
painter: 'Painter',
|
||||
formula: 'Formula'
|
||||
},
|
||||
edit: {
|
||||
newFeatureNoticeTitle: 'New feature reminder',
|
||||
newFeatureNoticeMessage: 'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.'
|
||||
newFeatureNoticeMessage:
|
||||
'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.'
|
||||
},
|
||||
mouseAction: {
|
||||
tip1: 'Current: Left click to drag the canvas, right click to box select nodes',
|
||||
tip2: 'Current: Left click to box select nodes, right click to drag the canvas',
|
||||
tip1:
|
||||
'Current: Left click to drag the canvas, right click to box select nodes',
|
||||
tip2:
|
||||
'Current: Left click to box select nodes, right click to drag the canvas'
|
||||
},
|
||||
search: {
|
||||
searchPlaceholder: 'Please enter the search content',
|
||||
@@ -229,5 +235,16 @@ export default {
|
||||
replace: 'Replace',
|
||||
replaceAll: 'Replace all',
|
||||
cancel: 'Cancel'
|
||||
},
|
||||
nodeIconSidebar: {
|
||||
title: 'Icon/Sticker',
|
||||
icon: 'Icon',
|
||||
sticker: 'Sticker'
|
||||
},
|
||||
formulaSidebar: {
|
||||
title: 'Formula',
|
||||
placeholder: 'Please enter LaText syntax',
|
||||
confirm: 'Confirm',
|
||||
common: 'Common formulas'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,15 +213,17 @@ export default {
|
||||
export: '导出',
|
||||
shortcutKey: '快捷键',
|
||||
associativeLine: '关联线',
|
||||
painter: '格式刷'
|
||||
painter: '格式刷',
|
||||
formula: '公式'
|
||||
},
|
||||
edit: {
|
||||
newFeatureNoticeTitle: '新特性提醒',
|
||||
newFeatureNoticeMessage: '本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。'
|
||||
newFeatureNoticeMessage:
|
||||
'本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。'
|
||||
},
|
||||
mouseAction: {
|
||||
tip1: '当前:左键拖动画布,右键框选节点',
|
||||
tip2: '当前:左键框选节点,右键拖动画布',
|
||||
tip2: '当前:左键框选节点,右键拖动画布'
|
||||
},
|
||||
search: {
|
||||
searchPlaceholder: '请输入查找内容',
|
||||
@@ -229,5 +231,16 @@ export default {
|
||||
replace: '替换',
|
||||
replaceAll: '全部替换',
|
||||
cancel: '取消'
|
||||
},
|
||||
nodeIconSidebar: {
|
||||
title: '图标/贴纸',
|
||||
icon: '图标',
|
||||
sticker: '贴纸'
|
||||
},
|
||||
formulaSidebar: {
|
||||
title: '公式',
|
||||
placeholder: '请输入 LaText 语法',
|
||||
confirm: '完成',
|
||||
common: '常用公式'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
<ShortcutKey></ShortcutKey>
|
||||
<Contextmenu v-if="mindMap" :mindMap="mindMap"></Contextmenu>
|
||||
<RichTextToolbar v-if="mindMap" :mindMap="mindMap"></RichTextToolbar>
|
||||
<NodeNoteContentShow v-if="mindMap" :mindMap="mindMap"></NodeNoteContentShow>
|
||||
<NodeNoteContentShow
|
||||
v-if="mindMap"
|
||||
:mindMap="mindMap"
|
||||
></NodeNoteContentShow>
|
||||
<NodeImgPreview v-if="mindMap" :mindMap="mindMap"></NodeImgPreview>
|
||||
<SidebarTrigger v-if="!isZenMode"></SidebarTrigger>
|
||||
<Search v-if="mindMap" :mindMap="mindMap"></Search>
|
||||
@@ -20,6 +23,7 @@
|
||||
<NodeIconToolbar v-if="mindMap" :mindMap="mindMap"></NodeIconToolbar>
|
||||
<OutlineEdit v-if="mindMap" :mindMap="mindMap"></OutlineEdit>
|
||||
<Scrollbar v-if="isShowScrollbar && mindMap" :mindMap="mindMap"></Scrollbar>
|
||||
<FormulaSidebar v-if="mindMap" :mindMap="mindMap"></FormulaSidebar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -40,6 +44,7 @@ import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js'
|
||||
import SearchPlugin from 'simple-mind-map/src/plugins/Search.js'
|
||||
import Painter from 'simple-mind-map/src/plugins/Painter.js'
|
||||
import ScrollbarPlugin from 'simple-mind-map/src/plugins/Scrollbar.js'
|
||||
import Formula from 'simple-mind-map/src/plugins/Formula.js'
|
||||
import OutlineSidebar from './OutlineSidebar'
|
||||
import Style from './Style'
|
||||
import BaseStyle from './BaseStyle'
|
||||
@@ -72,6 +77,7 @@ import { showLoading, hideLoading } from '@/utils/loading'
|
||||
import handleClipboardText from '@/utils/handleClipboardText'
|
||||
import Scrollbar from './Scrollbar.vue'
|
||||
import exampleData from 'simple-mind-map/example/exampleData'
|
||||
import FormulaSidebar from './FormulaSidebar.vue'
|
||||
|
||||
// 注册插件
|
||||
MindMap.usePlugin(MiniMap)
|
||||
@@ -88,6 +94,7 @@ MindMap.usePlugin(MiniMap)
|
||||
.usePlugin(SearchPlugin)
|
||||
.usePlugin(Painter)
|
||||
.usePlugin(ScrollbarPlugin)
|
||||
.usePlugin(Formula)
|
||||
|
||||
// 注册自定义主题
|
||||
customThemeList.forEach(item => {
|
||||
@@ -120,7 +127,8 @@ export default {
|
||||
NodeIconSidebar,
|
||||
NodeIconToolbar,
|
||||
OutlineEdit,
|
||||
Scrollbar
|
||||
Scrollbar,
|
||||
FormulaSidebar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -264,10 +272,10 @@ export default {
|
||||
// 如果url中存在要打开的文件,那么思维导图数据、主题、布局都使用默认的
|
||||
if (hasFileURL) {
|
||||
root = {
|
||||
"data": {
|
||||
"text": "根节点"
|
||||
data: {
|
||||
text: '根节点'
|
||||
},
|
||||
"children": []
|
||||
children: []
|
||||
}
|
||||
layout = exampleData.layout
|
||||
theme = exampleData.theme
|
||||
@@ -305,7 +313,7 @@ export default {
|
||||
useLeftKeySelectionRightKeyDrag: this.useLeftKeySelectionRightKeyDrag,
|
||||
customInnerElsAppendTo: null,
|
||||
enableAutoEnterTextEditWhenKeydown: true,
|
||||
customHandleClipboardText: handleClipboardText,
|
||||
customHandleClipboardText: handleClipboardText
|
||||
// isUseCustomNodeContent: true,
|
||||
// 示例1:组件里用到了router、store、i18n等实例化vue组件时需要用到的东西
|
||||
// customCreateNodeContent: (node) => {
|
||||
@@ -353,46 +361,33 @@ export default {
|
||||
this.mindMap.keyCommand.addShortcut('Control+s', () => {
|
||||
this.manualSave()
|
||||
})
|
||||
// 转发事件
|
||||
;[
|
||||
'node_active',
|
||||
'data_change',
|
||||
'view_data_change',
|
||||
'back_forward',
|
||||
'node_contextmenu',
|
||||
'node_click',
|
||||
'draw_click',
|
||||
'expand_btn_click',
|
||||
'svg_mousedown',
|
||||
'mouseup',
|
||||
'mode_change',
|
||||
'node_tree_render_end',
|
||||
'rich_text_selection_change',
|
||||
'transforming-dom-to-images',
|
||||
'generalization_node_contextmenu',
|
||||
'painter_start',
|
||||
'painter_end',
|
||||
'scrollbar_change'
|
||||
].forEach(event => {
|
||||
this.mindMap.on(event, (...args) => {
|
||||
this.$bus.$emit(event, ...args)
|
||||
})
|
||||
// 转发事件
|
||||
;[
|
||||
'node_active',
|
||||
'data_change',
|
||||
'view_data_change',
|
||||
'back_forward',
|
||||
'node_contextmenu',
|
||||
'node_click',
|
||||
'draw_click',
|
||||
'expand_btn_click',
|
||||
'svg_mousedown',
|
||||
'mouseup',
|
||||
'mode_change',
|
||||
'node_tree_render_end',
|
||||
'rich_text_selection_change',
|
||||
'transforming-dom-to-images',
|
||||
'generalization_node_contextmenu',
|
||||
'painter_start',
|
||||
'painter_end',
|
||||
'scrollbar_change'
|
||||
].forEach(event => {
|
||||
this.mindMap.on(event, (...args) => {
|
||||
this.$bus.$emit(event, ...args)
|
||||
})
|
||||
})
|
||||
this.bindSaveEvent()
|
||||
// setTimeout(() => {
|
||||
// 动态给指定节点添加子节点
|
||||
// this.mindMap.execCommand('INSERT_CHILD_NODE', false, this.mindMap.renderer.root, {
|
||||
// text: '自定义内容'
|
||||
// })
|
||||
|
||||
// 动态给指定节点添加同级节点
|
||||
// this.mindMap.execCommand('INSERT_NODE', false, this.mindMap.renderer.root, {
|
||||
// text: '自定义内容'
|
||||
// })
|
||||
|
||||
// 动态删除指定节点
|
||||
// this.mindMap.execCommand('REMOVE_NODE', this.mindMap.renderer.root.children[0])
|
||||
// }, 5000);
|
||||
this.testDynamicCreateNodes()
|
||||
// 如果应用被接管,那么抛出事件传递思维导图实例
|
||||
if (window.takeOverApp) {
|
||||
this.$bus.$emit('app_inited', this.mindMap)
|
||||
@@ -486,6 +481,113 @@ export default {
|
||||
// 移除节点富文本编辑插件
|
||||
removeRichTextPlugin() {
|
||||
this.mindMap.removePlugin(RichText)
|
||||
},
|
||||
|
||||
// 测试动态插入节点
|
||||
testDynamicCreateNodes() {
|
||||
return
|
||||
setTimeout(() => {
|
||||
// 动态给指定节点添加子节点
|
||||
// this.mindMap.execCommand(
|
||||
// 'INSERT_CHILD_NODE',
|
||||
// false,
|
||||
// this.mindMap.renderer.root,
|
||||
// {
|
||||
// text: '自定义内容'
|
||||
// },
|
||||
// [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义子节点'
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// )
|
||||
// 动态给指定节点添加同级节点
|
||||
// this.mindMap.execCommand(
|
||||
// 'INSERT_NODE',
|
||||
// false,
|
||||
// null,
|
||||
// {
|
||||
// text: '自定义内容'
|
||||
// },
|
||||
// [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义同级节点'
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义同级节点2'
|
||||
// },
|
||||
// children: []
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// )
|
||||
// 动态插入多个子节点
|
||||
// this.mindMap.execCommand('INSERT_MULTI_CHILD_NODE', null, [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点1'
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点1-1'
|
||||
// },
|
||||
// children: []
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点2'
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点2-1'
|
||||
// },
|
||||
// children: []
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ])
|
||||
// 动态插入多个同级节点
|
||||
// this.mindMap.execCommand('INSERT_MULTI_NODE', null, [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点1'
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点1-1'
|
||||
// },
|
||||
// children: []
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点2'
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// data: {
|
||||
// text: '自定义节点2-1'
|
||||
// },
|
||||
// children: []
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ])
|
||||
// 动态删除指定节点
|
||||
// this.mindMap.execCommand('REMOVE_NODE', this.mindMap.renderer.root.children[0])
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
201
web/src/pages/Edit/components/FormulaSidebar.vue
Normal file
201
web/src/pages/Edit/components/FormulaSidebar.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<Sidebar ref="sidebar" :title="$t('formulaSidebar.title')">
|
||||
<div class="box" :class="{ isDark: isDark }">
|
||||
<div class="formulaInputBox">
|
||||
<el-input
|
||||
v-model="formulaText"
|
||||
:rows="4"
|
||||
resize="none"
|
||||
type="textarea"
|
||||
:placeholder="$t('formulaSidebar.placeholder')"
|
||||
@keydown.native.stop
|
||||
/>
|
||||
<el-button
|
||||
size="small"
|
||||
style="width: 100%; margin-top: 20px;"
|
||||
@click="confirm"
|
||||
>{{ $t('formulaSidebar.confirm') }}</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="title">{{ $t('formulaSidebar.common') }}</div>
|
||||
<div class="formulaList">
|
||||
<div class="formulaItem" v-for="(item, index) in list" :key="index">
|
||||
<div class="overview" v-html="item.overview"></div>
|
||||
<div class="text" @click="formulaText = item.text">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Sidebar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sidebar from './Sidebar'
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import { formulaList } from '@/config/constant'
|
||||
|
||||
export default {
|
||||
name: 'FormulaSidebar',
|
||||
components: {
|
||||
Sidebar
|
||||
},
|
||||
props: {
|
||||
mindMap: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formulaText: '',
|
||||
list: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['activeSidebar', 'isDark', 'localConfig'])
|
||||
},
|
||||
watch: {
|
||||
activeSidebar(val) {
|
||||
if (val === 'formulaSidebar') {
|
||||
this.$refs.sidebar.show = true
|
||||
} else {
|
||||
this.$refs.sidebar.show = false
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$bus.$on('node_active', this.handleNodeActive)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$bus.$off('node_active', this.handleNodeActive)
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setActiveSidebar']),
|
||||
|
||||
init() {
|
||||
this.list = formulaList.map(item => {
|
||||
return {
|
||||
overview: window.katex.renderToString(item, {
|
||||
throwOnError: false,
|
||||
output: 'mathml'
|
||||
}),
|
||||
text: item
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleNodeActive(...args) {
|
||||
this.activeNodes = [...args[1]]
|
||||
if (this.activeNodes.length <= 0) {
|
||||
this.setActiveSidebar(null)
|
||||
}
|
||||
},
|
||||
|
||||
confirm() {
|
||||
if (!this.localConfig.openNodeRichText) {
|
||||
return this.$message.warning('非富文本模式下不支持插入公式')
|
||||
}
|
||||
let str = this.formulaText.trim()
|
||||
if (!str) return
|
||||
this.mindMap.execCommand('INSERT_FORMULA', str)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.box {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
&.isDark {
|
||||
.title {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.formulaList {
|
||||
.formulaItem {
|
||||
.overview,
|
||||
.text {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text {
|
||||
background-color: #363b3f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .el-textarea__inner {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 10px 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.formulaInputBox {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.formulaList {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.formulaItem {
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-bottom: none;
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.overview,
|
||||
.text {
|
||||
width: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.overview {
|
||||
padding: 10px 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.text {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-left: 1px solid #dcdfe6;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<Sidebar ref="sidebar" title="图标/贴纸">
|
||||
<Sidebar ref="sidebar" :title="$t('nodeIconSidebar.title')">
|
||||
<div class="box" :class="{ isDark: isDark }">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="图标" name="icon"></el-tab-pane>
|
||||
<el-tab-pane label="贴纸" name="image"></el-tab-pane>
|
||||
<el-tab-pane
|
||||
:label="$t('nodeIconSidebar.icon')"
|
||||
name="icon"
|
||||
></el-tab-pane>
|
||||
<el-tab-pane
|
||||
:label="$t('nodeIconSidebar.sticker')"
|
||||
name="image"
|
||||
></el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="boxContent">
|
||||
<!-- 图标 -->
|
||||
|
||||
@@ -134,6 +134,16 @@
|
||||
<span class="icon iconfont iconlianjiexian"></span>
|
||||
<span class="text">{{ $t('toolbar.associativeLine') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="toolbarBtn"
|
||||
:class="{
|
||||
disabled: activeNodes.length <= 0 || hasGeneralization
|
||||
}"
|
||||
@click="showFormula"
|
||||
>
|
||||
<span class="icon iconfont icongongshi"></span>
|
||||
<span class="text">{{ $t('toolbar.formula') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导出 -->
|
||||
<div class="toolbarBlock">
|
||||
@@ -259,6 +269,11 @@ export default {
|
||||
this.setActiveSidebar('nodeIconSidebar')
|
||||
},
|
||||
|
||||
// 打开公式侧边栏
|
||||
showFormula() {
|
||||
this.setActiveSidebar('formulaSidebar')
|
||||
},
|
||||
|
||||
/**
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-11-14 19:17:40
|
||||
|
||||
@@ -1,57 +1,69 @@
|
||||
import { imgToDataUrl } from 'simple-mind-map/src/utils/index'
|
||||
|
||||
// 处理知犀
|
||||
const handleZHIXI = async text => {
|
||||
text = text.replace('', '')
|
||||
const handleZHIXI = async data => {
|
||||
try {
|
||||
// 只处理一项
|
||||
const node = JSON.parse(text)[0]
|
||||
const newNode = {}
|
||||
const waitLoadImageList = []
|
||||
const walk = async (root, newRoot) => {
|
||||
newRoot.data = {
|
||||
text: root.data.text,
|
||||
hyperlink: root.data.hyperlink,
|
||||
hyperlinkTitle: root.data.hyperlinkTitle,
|
||||
note: root.data.note
|
||||
}
|
||||
// 图片
|
||||
if (root.data.image) {
|
||||
let resolve = null
|
||||
let promise = new Promise(_resolve => {
|
||||
resolve = _resolve
|
||||
})
|
||||
waitLoadImageList.push(promise)
|
||||
try {
|
||||
newRoot.data.image = await imgToDataUrl(root.data.image)
|
||||
newRoot.data.imageSize = root.data.imageSize
|
||||
resolve()
|
||||
} catch (error) {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
// 子节点
|
||||
newRoot.children = []
|
||||
if (root.children && root.children.length > 0) {
|
||||
root.children.forEach(item => {
|
||||
// 概要
|
||||
if (item.data.type === 'generalize') {
|
||||
newRoot.data.generalization = {
|
||||
text: item.data.text
|
||||
}
|
||||
return
|
||||
}
|
||||
let newChild = {}
|
||||
newRoot.children.push(newChild)
|
||||
walk(item, newChild)
|
||||
})
|
||||
try {
|
||||
if (!Array.isArray(data)) {
|
||||
data = String(data).replace('', '')
|
||||
data = JSON.parse(data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
walk(node, newNode)
|
||||
if (!Array.isArray(data)) {
|
||||
data = []
|
||||
}
|
||||
const newNodeList = []
|
||||
const waitLoadImageList = []
|
||||
const walk = (list, newList) => {
|
||||
list.forEach(async item => {
|
||||
let newRoot = {}
|
||||
newList.push(newRoot)
|
||||
newRoot.data = {
|
||||
text: item.data.text,
|
||||
hyperlink: item.data.hyperlink,
|
||||
hyperlinkTitle: item.data.hyperlinkTitle,
|
||||
note: item.data.note
|
||||
}
|
||||
// 图片
|
||||
if (item.data.image) {
|
||||
let resolve = null
|
||||
let promise = new Promise(_resolve => {
|
||||
resolve = _resolve
|
||||
})
|
||||
waitLoadImageList.push(promise)
|
||||
try {
|
||||
newRoot.data.image = await imgToDataUrl(item.data.image)
|
||||
newRoot.data.imageSize = item.data.imageSize
|
||||
resolve()
|
||||
} catch (error) {
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
// 子节点
|
||||
newRoot.children = []
|
||||
if (item.children && item.children.length > 0) {
|
||||
const children = []
|
||||
item.children.forEach(item2 => {
|
||||
// 概要
|
||||
if (item2.data.type === 'generalize') {
|
||||
newRoot.data.generalization = {
|
||||
text: item2.data.text
|
||||
}
|
||||
} else {
|
||||
children.push(item2)
|
||||
}
|
||||
})
|
||||
walk(children, newRoot.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
walk(data, newNodeList)
|
||||
await Promise.all(waitLoadImageList)
|
||||
return {
|
||||
simpleMindMap: true,
|
||||
data: newNode
|
||||
data: newNodeList
|
||||
}
|
||||
} catch (error) {
|
||||
return ''
|
||||
@@ -59,7 +71,15 @@ const handleZHIXI = async text => {
|
||||
}
|
||||
|
||||
const handleClipboardText = async text => {
|
||||
// 处理知犀数据
|
||||
// 知犀数据格式1
|
||||
try {
|
||||
let parsedData = JSON.parse(text)
|
||||
if (parsedData.__c_zx_v !== undefined) {
|
||||
const res = await handleZHIXI(parsedData.children)
|
||||
return res
|
||||
}
|
||||
} catch (error) {}
|
||||
// 知犀数据格式2
|
||||
if (text.includes('')) {
|
||||
const res = await handleZHIXI(text)
|
||||
return res
|
||||
|
||||
Reference in New Issue
Block a user