Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04001fd181 | ||
|
|
7fc42879c7 | ||
|
|
c4c9f9421c | ||
|
|
4b246f7106 | ||
|
|
621e62af4c | ||
|
|
3df6538c86 | ||
|
|
fea7d32c97 | ||
|
|
c31a67e7bd | ||
|
|
f451d37254 | ||
|
|
377e507fd9 | ||
|
|
dce55f3628 | ||
|
|
600cadc861 | ||
|
|
4d69f239f0 | ||
|
|
897c12b8e6 | ||
|
|
9103574a88 | ||
|
|
7c94ec19ac | ||
|
|
3e3a60cb03 | ||
|
|
ec69d81f6a | ||
|
|
b2df7330fc | ||
|
|
5265ceb803 | ||
|
|
35d6297b72 | ||
|
|
5afe0a8c41 | ||
|
|
81f86ccb30 | ||
|
|
87383498c8 | ||
|
|
c68608c72c | ||
|
|
9d325d99a2 | ||
|
|
bc59fa6dc7 | ||
|
|
2daa59679a | ||
|
|
da456eeb8f | ||
|
|
5cd36b57d5 | ||
|
|
e2b239fcbb | ||
|
|
5449e79d49 | ||
|
|
c2045ddedc | ||
|
|
5dfa215538 | ||
|
|
d90013da71 | ||
|
|
b3b74323f7 | ||
|
|
f9000ea478 | ||
|
|
830e7e2482 | ||
|
|
13ed7f28df | ||
|
|
17e79a0b23 | ||
|
|
eee310ba49 | ||
|
|
36c8927dd0 | ||
|
|
05333daa63 | ||
|
|
5aed681198 | ||
|
|
8a438f2906 | ||
|
|
260de4987d | ||
|
|
31cc658c06 | ||
|
|
be6b41d74d | ||
|
|
4beeead53d | ||
|
|
af2df6acd3 | ||
|
|
c08d66acf2 | ||
|
|
5a9cb9ac07 | ||
|
|
1662cd1be7 | ||
|
|
9dd5b3d47e | ||
|
|
ece116317b | ||
|
|
d24d5c8281 | ||
|
|
97c01cda3a | ||
|
|
6d520ece7e | ||
|
|
745041deef | ||
|
|
ae6c10cdf9 | ||
|
|
f5338d62fc | ||
|
|
19fc12ff20 | ||
|
|
d7640bb026 | ||
|
|
193b293cfe | ||
|
|
615ff3ea25 | ||
|
|
8b9cfd2972 | ||
|
|
03f8cb9290 | ||
|
|
1ea0c7e316 | ||
|
|
a39c8c30e6 | ||
|
|
26ce08c27d | ||
|
|
4bfc98a96f | ||
|
|
7560411922 | ||
|
|
4288e44f3a | ||
|
|
b82c5247fa | ||
|
|
c7d2082944 | ||
|
|
29458ade9c | ||
|
|
49d366628e | ||
|
|
b093153262 | ||
|
|
02f276bc2a | ||
|
|
69cb961cc1 | ||
|
|
7096391f3b | ||
|
|
280ffcf01d | ||
|
|
db3c2b71f5 | ||
|
|
f11f364d00 | ||
|
|
0e8c50d430 | ||
|
|
dd8d250857 | ||
|
|
693ead6b49 | ||
|
|
dc3c91270c | ||
|
|
71ac739964 | ||
|
|
eae5dc5854 | ||
|
|
c45ceac7dc | ||
|
|
3d8702be8a | ||
|
|
084dd9fd84 | ||
|
|
0c6c68820f | ||
|
|
f5ff479f47 | ||
|
|
e9722efe93 | ||
|
|
db1f9c04c1 | ||
|
|
b3e6412dbc | ||
|
|
f58828469c | ||
|
|
cf7a92d9c7 | ||
|
|
ec0d021e92 | ||
|
|
25a2d919fb | ||
|
|
c3f4e2b797 | ||
|
|
a0c07522f9 | ||
|
|
ca0cbdf009 | ||
|
|
6263a49903 | ||
|
|
dce133f8f0 | ||
|
|
7f4f4e2fe0 | ||
|
|
4e4ade5c31 | ||
|
|
5423b42e9d | ||
|
|
9555375907 | ||
|
|
cc19ce168b | ||
|
|
e324813eae | ||
|
|
08a7ba380a | ||
|
|
dd2bfbcf93 | ||
|
|
4ae5c914ca | ||
|
|
6f49577794 | ||
|
|
43a3dd97c7 | ||
|
|
7226e40a6c | ||
|
|
9f090a4474 | ||
|
|
bf5298ab2f | ||
|
|
ffe4fedbc3 | ||
|
|
07ad92e93a | ||
|
|
5a004e40de | ||
|
|
8664bcc00b | ||
|
|
d00f56c7f1 | ||
|
|
4ddce22076 | ||
|
|
fdf4d51a1e | ||
|
|
a9ebe50fff | ||
|
|
f8638088f3 | ||
|
|
081797e83b | ||
|
|
8adf5a7fb8 | ||
|
|
194a920efa | ||
|
|
6b3741741c | ||
|
|
80a7e334e4 | ||
|
|
5d45d7accf | ||
|
|
b893776f82 | ||
|
|
115a0ac480 | ||
|
|
cb43292aa5 | ||
|
|
7b773bd9ad | ||
|
|
38a1e25cc2 | ||
|
|
fd0c471f00 | ||
|
|
97f84933f8 | ||
|
|
321fc69798 | ||
|
|
af2910bec8 | ||
|
|
9deb20f1ca | ||
|
|
0f4d614c4e | ||
|
|
eeba3153ef | ||
|
|
554dab56d3 | ||
|
|
da28f89c52 | ||
|
|
8b0c62430e | ||
|
|
adcf77f60d | ||
|
|
54ae4b3e26 | ||
|
|
b0929da054 | ||
|
|
1528c3d64b | ||
|
|
ecc3ba8784 | ||
|
|
947c4b2b44 | ||
|
|
08c7768b18 | ||
|
|
efebe3f094 |
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist_electron
|
||||
package-lock.json
|
||||
BIN
docs/assets/1.gif
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/assets/2.gif
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/assets/2021-07-21-19-54-48.gif
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
docs/assets/3.gif
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/assets/image-20210717174716810.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/assets/image-20210717202607786.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
docs/assets/image-20210717210355763.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/assets/image-20210717224527681.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/assets/image-20210717230604086.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/assets/image-20210717230805200.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/assets/image-20210717230808662.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/assets/image-20210718083006111.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/assets/image-20210718083616076.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
docs/assets/image-20210718092817830.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/assets/image-20210718102434530.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/assets/image-20210718110652705.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/assets/image-20210718111334085.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
docs/assets/image-20210718134525691.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/assets/image-20210718184835414.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
docs/assets/image-20210718191124627.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
docs/assets/image-20210718221814590.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/assets/image-20210718222150055.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/assets/image-20210718222612078.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/assets/image-20210720191943989.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
docs/assets/image-20210720192008277.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
docs/assets/image-20210720200816281.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/assets/image-20210721183307656.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
docs/assets/image-20210721183340310.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/assets/image-20210721183823754.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
docs/assets/image-20210721184140488.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/assets/image-20210721185453825.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
docs/assets/image-20210721190700979.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/assets/swdt.jpg
Normal file
|
After Width: | Height: | Size: 254 KiB |
1317
docs/web思维导图实现的技术点分析.md
Normal file
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="./dist/logo.png"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d0a3179.3b27bed0.js" rel="prefetch"><link href="dist/js/chunk-2d0a514a.64cc7822.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.61afd8c1.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.84fc06da.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.4d405645.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.a9abff6a.js" rel="prefetch"><link href="dist/js/chunk-2d0b1c6f.b8417307.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.d657e190.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.433fdc5c.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.9d7f8382.js" rel="prefetch"><link href="dist/js/chunk-2d0ba309.a3915823.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.906e86ec.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.0cf53d60.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.0f9ebf1b.js" rel="prefetch"><link href="dist/js/chunk-2d0c14fc.7163274e.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.2e6ddc70.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.cd5e6df3.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.2e73bf0a.js" rel="prefetch"><link href="dist/js/chunk-2d0c20be.dab092ff.js" rel="prefetch"><link href="dist/js/chunk-2d0d5cb9.2e73ab77.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.4800807d.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.80759043.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.f670c26a.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.b5ce4946.js" rel="prefetch"><link href="dist/js/chunk-2d0dd3b1.c4647d07.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.3eea98de.js" rel="prefetch"><link href="dist/js/chunk-2d0ddf37.d960b122.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.a2a047cf.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.5072607c.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.ed75c510.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.10135360.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.9abceada.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.4757f6bb.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.f52387a2.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.766e8fa6.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.6c1b14e1.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.6a4911c4.js" rel="prefetch"><link href="dist/js/chunk-2d216004.704073c5.js" rel="prefetch"><link href="dist/js/chunk-2d217907.32a00939.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.6b1238d2.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.8f3151dd.js" rel="prefetch"><link href="dist/js/chunk-2d22bd06.9f7bbd4c.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.2797f6b4.js" rel="prefetch"><link href="dist/js/chunk-2d238428.7c9ae7c7.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.3b1827be.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.c097b26d.css" rel="preload" as="style"><link href="dist/js/app.e9e06d36.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.49b07a77.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.c097b26d.css" rel="stylesheet"><link href="dist/css/app.3b1827be.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.49b07a77.js"></script><script src="dist/js/app.e9e06d36.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d20ec02.81d632f4.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.228f2009.js" rel="prefetch"><link href="dist/js/chunk-35b0a040.cb76da7d.js" rel="prefetch"><link href="dist/css/app.2784db23.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.faba1249.css" rel="preload" as="style"><link href="dist/js/app.77dbe506.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.013a6cea.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.faba1249.css" rel="stylesheet"><link href="dist/css/app.2784db23.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.013a6cea.js"></script><script src="dist/js/app.77dbe506.js"></script></body></html>
|
||||
BIN
qrcode.jpg
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
@@ -900,17 +900,6 @@ const data5 = {
|
||||
}
|
||||
}
|
||||
|
||||
// 富文本数据v0.4.0+,需要使用RichText插件才支持富文本编辑
|
||||
const richTextData = {
|
||||
"root": {
|
||||
"data": {
|
||||
"text": "<a href='http://lxqnsys.com/' target='_blank'>理想去年实验室</a>",
|
||||
"richText": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
}
|
||||
|
||||
const rootData = {
|
||||
"root": {
|
||||
"data": {
|
||||
@@ -936,6 +925,5 @@ export default {
|
||||
"layout": "logicalStructure",
|
||||
// "layout": "mindMap",
|
||||
// "layout": "catalogOrganization"
|
||||
// "layout": "organizationStructure",
|
||||
"config": {}
|
||||
// "layout": "organizationStructure"
|
||||
}
|
||||
@@ -31,12 +31,6 @@
|
||||
"isActive": false
|
||||
},
|
||||
"children": []
|
||||
}, {
|
||||
"data": {
|
||||
"text": "<a href='http://lxqnsys.com/' target='_blank'>理想去年实验室</a>",
|
||||
"richText": true
|
||||
},
|
||||
"children": []
|
||||
}]
|
||||
}]
|
||||
},
|
||||
@@ -68,6 +62,5 @@
|
||||
"sx": 0,
|
||||
"sy": 0
|
||||
}
|
||||
},
|
||||
"config": {}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import MindMap from './index'
|
||||
import MiniMap from './src/MiniMap.js'
|
||||
import Watermark from './src/Watermark.js'
|
||||
import KeyboardNavigation from './src/KeyboardNavigation.js'
|
||||
import Export from './src/Export.js'
|
||||
import Drag from './src/Drag.js'
|
||||
import Select from './src/Select.js'
|
||||
import AssociativeLine from './src/AssociativeLine'
|
||||
import RichText from './src/RichText'
|
||||
import xmind from './src/parse/xmind.js'
|
||||
import markdown from './src/parse/markdown.js'
|
||||
|
||||
MindMap.xmind = xmind
|
||||
MindMap.markdown = markdown
|
||||
|
||||
MindMap
|
||||
.usePlugin(MiniMap)
|
||||
.usePlugin(Watermark)
|
||||
.usePlugin(Drag)
|
||||
.usePlugin(KeyboardNavigation)
|
||||
.usePlugin(Export)
|
||||
.usePlugin(Select)
|
||||
.usePlugin(AssociativeLine)
|
||||
.usePlugin(RichText)
|
||||
|
||||
export default MindMap
|
||||
@@ -7,10 +7,15 @@ import Style from './src/Style'
|
||||
import KeyCommand from './src/KeyCommand'
|
||||
import Command from './src/Command'
|
||||
import BatchExecution from './src/BatchExecution'
|
||||
import Export from './src/Export'
|
||||
import Select from './src/Select'
|
||||
import Drag from './src/Drag'
|
||||
import MiniMap from './src/MiniMap'
|
||||
import { layoutValueList } from './src/utils/constant'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import xmind from './src/parse/xmind'
|
||||
import { simpleDeepClone } from './src/utils'
|
||||
import defaultTheme from './src/themes/default'
|
||||
import KeyboardNavigation from './src/KeyboardNavigation'
|
||||
|
||||
// 默认选项配置
|
||||
const defaultOpt = {
|
||||
@@ -39,45 +44,28 @@ const defaultOpt = {
|
||||
// 多选节点时鼠标移动距边缘多少距离时开始偏移
|
||||
selectTranslateLimit: 20,
|
||||
// 自定义节点备注内容显示
|
||||
customNoteContentShow: null,
|
||||
customNoteContentShow: null
|
||||
/*
|
||||
{
|
||||
show(){},
|
||||
hide(){}
|
||||
}
|
||||
*/
|
||||
// 是否开启节点自由拖拽
|
||||
enableFreeDrag: false,
|
||||
// 水印配置
|
||||
watermarkConfig: {
|
||||
text: '',
|
||||
lineSpacing: 100,
|
||||
textSpacing: 100,
|
||||
angle: 30,
|
||||
textStyle: {
|
||||
color: '#999',
|
||||
opacity: 0.5,
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
// 达到该宽度文本自动换行
|
||||
textAutoWrapWidth: 500,
|
||||
// 自定义鼠标滚轮事件处理
|
||||
// 可以传一个函数,回调参数为事件对象
|
||||
customHandleMousewheel: null,
|
||||
// 鼠标滚动的行为,如果customHandleMousewheel传了自定义函数,这个属性不生效
|
||||
mousewheelAction: 'zoom',// zoom(放大缩小)、move(上下移动)
|
||||
// 当mousewheelAction设为move时,可以通过该属性控制鼠标滚动一下视图移动的步长,单位px
|
||||
mousewheelMoveStep: 100,
|
||||
// 默认插入的二级节点的文字
|
||||
defaultInsertSecondLevelNodeText: '二级节点',
|
||||
// 默认插入的二级以下节点的文字
|
||||
defaultInsertBelowSecondLevelNodeText: '分支主题'
|
||||
}
|
||||
|
||||
// 思维导图
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 11:18:47
|
||||
* @Desc: 思维导图
|
||||
*/
|
||||
class MindMap {
|
||||
// 构造函数
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 11:19:01
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
// 合并选项
|
||||
this.opt = this.handleOpt(merge(defaultOpt, opt))
|
||||
@@ -126,14 +114,34 @@ class MindMap {
|
||||
draw: this.draw
|
||||
})
|
||||
|
||||
// 小地图类
|
||||
this.miniMap = new MiniMap({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 导出类
|
||||
this.doExport = new Export({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 选择类
|
||||
this.select = new Select({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 拖动类
|
||||
this.drag = new Drag({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 键盘导航类
|
||||
this.keyboardNavigation = new KeyboardNavigation({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 批量执行类
|
||||
this.batchExecution = new BatchExecution()
|
||||
|
||||
// 注册插件
|
||||
MindMap.pluginList.forEach((plugin) => {
|
||||
this.initPlugin(plugin)
|
||||
})
|
||||
|
||||
// 初始渲染
|
||||
this.reRender()
|
||||
setTimeout(() => {
|
||||
@@ -141,7 +149,11 @@ class MindMap {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// 配置参数处理
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-01 22:15:22
|
||||
* @Desc: 配置参数处理
|
||||
*/
|
||||
handleOpt(opt) {
|
||||
// 检查布局配置
|
||||
if (!layoutValueList.includes(opt.layout)) {
|
||||
@@ -152,26 +164,39 @@ class MindMap {
|
||||
return opt
|
||||
}
|
||||
|
||||
// 渲染,部分渲染
|
||||
render(callback) {
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 18:47:29
|
||||
* @Desc: 渲染,部分渲染
|
||||
*/
|
||||
render() {
|
||||
this.batchExecution.push('render', () => {
|
||||
this.initTheme()
|
||||
this.renderer.reRender = false
|
||||
this.renderer.render(callback)
|
||||
this.renderer.render()
|
||||
})
|
||||
}
|
||||
|
||||
// 重新渲染
|
||||
reRender(callback) {
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-08 22:05:11
|
||||
* @Desc: 重新渲染
|
||||
*/
|
||||
reRender() {
|
||||
this.batchExecution.push('render', () => {
|
||||
this.draw.clear()
|
||||
this.initTheme()
|
||||
this.renderer.reRender = true
|
||||
this.renderer.render(callback)
|
||||
this.renderer.render()
|
||||
})
|
||||
}
|
||||
|
||||
// 容器尺寸变化,调整尺寸
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 21:16:52
|
||||
* @Desc: 容器尺寸变化,调整尺寸
|
||||
*/
|
||||
resize() {
|
||||
this.elRect = this.el.getBoundingClientRect()
|
||||
this.width = this.elRect.width
|
||||
@@ -179,22 +204,38 @@ class MindMap {
|
||||
this.svg.size(this.width, this.height)
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 13:25:50
|
||||
* @Desc: 监听事件
|
||||
*/
|
||||
on(event, fn) {
|
||||
this.event.on(event, fn)
|
||||
}
|
||||
|
||||
// 触发事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 13:51:35
|
||||
* @Desc: 触发事件
|
||||
*/
|
||||
emit(event, ...args) {
|
||||
this.event.emit(event, ...args)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 13:53:54
|
||||
* @Desc: 解绑事件
|
||||
*/
|
||||
off(event, fn) {
|
||||
this.event.off(event, fn)
|
||||
}
|
||||
|
||||
// 设置主题
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-05 13:32:43
|
||||
* @Desc: 设置主题
|
||||
*/
|
||||
initTheme() {
|
||||
// 合并主题配置
|
||||
this.themeConfig = merge(theme[this.opt.theme], this.opt.themeConfig)
|
||||
@@ -202,50 +243,70 @@ class MindMap {
|
||||
Style.setBackgroundStyle(this.el, this.themeConfig)
|
||||
}
|
||||
|
||||
// 设置主题
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-05 13:52:08
|
||||
* @Desc: 设置主题
|
||||
*/
|
||||
setTheme(theme) {
|
||||
this.renderer.clearAllActive()
|
||||
this.opt.theme = theme
|
||||
this.reRender()
|
||||
}
|
||||
|
||||
// 获取当前主题
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-25 23:52:37
|
||||
* @Desc: 获取当前主题
|
||||
*/
|
||||
getTheme() {
|
||||
return this.opt.theme
|
||||
}
|
||||
|
||||
// 设置主题配置
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-05 13:50:17
|
||||
* @Desc: 设置主题配置
|
||||
*/
|
||||
setThemeConfig(config) {
|
||||
this.opt.themeConfig = config
|
||||
this.reRender()
|
||||
}
|
||||
|
||||
// 获取自定义主题配置
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-01 10:38:34
|
||||
* @Desc: 获取自定义主题配置
|
||||
*/
|
||||
getCustomThemeConfig() {
|
||||
return this.opt.themeConfig
|
||||
}
|
||||
|
||||
// 获取某个主题配置值
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-05 14:01:29
|
||||
* @Desc: 获取某个主题配置值
|
||||
*/
|
||||
getThemeConfig(prop) {
|
||||
return prop === undefined ? this.themeConfig : this.themeConfig[prop]
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
getConfig(prop) {
|
||||
return prop === undefined ? this.opt : this.opt[prop]
|
||||
}
|
||||
|
||||
// 更新配置
|
||||
updateConfig(opt = {}) {
|
||||
this.opt = this.handleOpt(merge.all([defaultOpt, this.opt, opt]))
|
||||
}
|
||||
|
||||
// 获取当前布局结构
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-13 16:17:06
|
||||
* @Desc: 获取当前布局结构
|
||||
*/
|
||||
getLayout() {
|
||||
return this.opt.layout
|
||||
}
|
||||
|
||||
// 设置布局结构
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-13 16:17:33
|
||||
* @Desc: 设置布局结构
|
||||
*/
|
||||
setLayout(layout) {
|
||||
// 检查布局配置
|
||||
if (!layoutValueList.includes(layout)) {
|
||||
@@ -256,12 +317,20 @@ class MindMap {
|
||||
this.render()
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:01:00
|
||||
* @Desc: 执行命令
|
||||
*/
|
||||
execCommand(...args) {
|
||||
this.command.exec(...args)
|
||||
}
|
||||
|
||||
// 动态设置思维导图数据,纯节点数据
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-03 22:58:12
|
||||
* @Desc: 动态设置思维导图数据,纯节点数据
|
||||
*/
|
||||
setData(data) {
|
||||
this.execCommand('CLEAR_ACTIVE_NODE')
|
||||
this.command.clearHistory()
|
||||
@@ -269,7 +338,12 @@ class MindMap {
|
||||
this.reRender()
|
||||
}
|
||||
|
||||
// 动态设置思维导图数据,包括节点数据、布局、主题、视图
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-21 16:39:13
|
||||
* @Desc: 动态设置思维导图数据,包括节点数据、布局、主题、视图
|
||||
*/
|
||||
setFullData(data) {
|
||||
if (data.root) {
|
||||
this.setData(data.root)
|
||||
@@ -290,7 +364,12 @@ class MindMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取思维导图数据,节点树、主题、布局等
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-24 14:42:07
|
||||
* @Desc: 获取思维导图数据,节点树、主题、布局等
|
||||
*/
|
||||
getData(withConfig) {
|
||||
let nodeData = this.command.getCopyData()
|
||||
let data = {}
|
||||
@@ -310,13 +389,21 @@ class MindMap {
|
||||
return simpleDeepClone(data)
|
||||
}
|
||||
|
||||
// 导出
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-01 22:06:38
|
||||
* @Desc: 导出
|
||||
*/
|
||||
async export(...args) {
|
||||
let result = await this.doExport.export(...args)
|
||||
return result
|
||||
}
|
||||
|
||||
// 转换位置
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 09:20:03
|
||||
* @Desc: 转换位置
|
||||
*/
|
||||
toPos(x, y) {
|
||||
return {
|
||||
x: x - this.elRect.left,
|
||||
@@ -324,7 +411,12 @@ class MindMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置只读模式、编辑模式
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-06-08 14:12:38
|
||||
* @Desc: 设置只读模式、编辑模式
|
||||
*/
|
||||
setMode(mode) {
|
||||
if (!['readonly', 'edit'].includes(mode)) {
|
||||
return
|
||||
@@ -336,105 +428,8 @@ class MindMap {
|
||||
}
|
||||
this.emit('mode_change', mode)
|
||||
}
|
||||
|
||||
// 获取svg数据
|
||||
getSvgData() {
|
||||
const svg = this.svg
|
||||
const draw = this.draw
|
||||
// 保存原始信息
|
||||
const origWidth = svg.width()
|
||||
const origHeight = svg.height()
|
||||
const origTransform = draw.transform()
|
||||
const elRect = this.el.getBoundingClientRect()
|
||||
// 去除放大缩小的变换效果
|
||||
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
let clone = svg.clone()
|
||||
// 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题
|
||||
if ((rect.width > origWidth || rect.height > origHeight) && this.watermark && this.watermark.hasWatermark()) {
|
||||
this.width = rect.width
|
||||
this.height = rect.height
|
||||
this.watermark.draw()
|
||||
clone = svg.clone()
|
||||
this.width = origWidth
|
||||
this.height = origHeight
|
||||
this.watermark.draw()
|
||||
}
|
||||
// 恢复原先的大小和变换信息
|
||||
svg.size(origWidth, origHeight)
|
||||
draw.transform(origTransform)
|
||||
|
||||
return {
|
||||
svg: clone, // 思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组)
|
||||
svgHTML: clone.svg(), // svg字符串
|
||||
rect: {
|
||||
...rect, // 思维导图图形未缩放时的位置尺寸等信息
|
||||
ratio: rect.width / rect.height // 思维导图图形的宽高比
|
||||
},
|
||||
origWidth, // 画布宽度
|
||||
origHeight, // 画布高度
|
||||
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
|
||||
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值
|
||||
}
|
||||
}
|
||||
|
||||
// 添加插件
|
||||
addPlugin(plugin, opt) {
|
||||
let index = MindMap.hasPlugin(plugin)
|
||||
if (index === -1) {
|
||||
MindMap.usePlugin(plugin, opt)
|
||||
this.initPlugin(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
// 移除插件
|
||||
removePlugin(plugin) {
|
||||
let index = MindMap.hasPlugin(plugin)
|
||||
if (index !== -1) {
|
||||
MindMap.pluginList.splice(index, 1)
|
||||
if (this[plugin.instanceName]) {
|
||||
if (this[plugin.instanceName].beforePluginRemove) {
|
||||
this[plugin.instanceName].beforePluginRemove()
|
||||
}
|
||||
delete this[plugin.instanceName]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
initPlugin(plugin) {
|
||||
this[plugin.instanceName] = new plugin({
|
||||
mindMap: this,
|
||||
pluginOpt: plugin.pluginOpt
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 插件列表
|
||||
MindMap.pluginList = []
|
||||
MindMap.usePlugin = (plugin, opt = {}) => {
|
||||
plugin.pluginOpt = opt
|
||||
MindMap.pluginList.push(plugin)
|
||||
return MindMap
|
||||
}
|
||||
MindMap.hasPlugin = (plugin) => {
|
||||
return MindMap.pluginList.findIndex((item) => {
|
||||
return item === plugin
|
||||
})
|
||||
}
|
||||
|
||||
// 定义新主题
|
||||
MindMap.defineTheme = (name, config = {}) => {
|
||||
if (theme[name]) {
|
||||
return new Error('该主题名称已存在')
|
||||
}
|
||||
theme[name] = merge(defaultTheme, config)
|
||||
}
|
||||
MindMap.xmind = xmind
|
||||
|
||||
export default MindMap
|
||||
|
||||
3844
simple-mind-map/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.4.7",
|
||||
"version": "0.2.17",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
@@ -22,18 +22,14 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"module": "index.js",
|
||||
"__main": "./dist/simpleMindMap.umd.min.js",
|
||||
"main": "./dist/simpleMindMap.umd.min.js",
|
||||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.0.16",
|
||||
"canvg": "^3.0.7",
|
||||
"deepmerge": "^1.5.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"quill": "^1.3.6",
|
||||
"uuid": "^9.0.0",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// 遍历所有js文件
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const entryPath = path.resolve(__dirname, '../src')
|
||||
|
||||
const transform = dir => {
|
||||
let dirs = fs.readdirSync(dir)
|
||||
dirs.forEach(item => {
|
||||
let file = path.join(dir, item)
|
||||
if (fs.statSync(file).isDirectory()) {
|
||||
transform(file)
|
||||
} else if (/\.js$/.test(file)) {
|
||||
transformFile(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const transformFile = file => {
|
||||
console.log(file);
|
||||
let content = fs.readFileSync(file, 'utf-8')
|
||||
countCodeLines(content)
|
||||
// transformComments(file, content)
|
||||
}
|
||||
|
||||
// 统计代码行数
|
||||
let totalLines = 0
|
||||
const countCodeLines = (content) => {
|
||||
totalLines += content.split(/\n/).length
|
||||
}
|
||||
|
||||
// 转换注释类型
|
||||
const transformComments = (file, content) => {
|
||||
console.log('当前转换文件:', file)
|
||||
content = content.replace(/\/\*\*[^/]+\*\//g, str => {
|
||||
let res = /@Desc:([^\n]+)\n/g.exec(str)
|
||||
if (res.length > 0) {
|
||||
return '// ' + res[1]
|
||||
}
|
||||
})
|
||||
fs.writeFileSync(file, content)
|
||||
}
|
||||
|
||||
transform(entryPath)
|
||||
transformFile(path.join(__dirname, '../index.js'))
|
||||
console.log(totalLines);
|
||||
@@ -1,627 +0,0 @@
|
||||
import { walk, bfsWalk, throttle } from './utils/'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import {
|
||||
getAssociativeLineTargetIndex,
|
||||
computeCubicBezierPathPoints,
|
||||
joinCubicBezierPath,
|
||||
cubicBezierPath,
|
||||
getNodePoint,
|
||||
computeNodePoints,
|
||||
getNodeLinePath,
|
||||
getDefaultControlPointOffsets
|
||||
} from './utils/associativeLineUtils'
|
||||
|
||||
// 关联线类
|
||||
class AssociativeLine {
|
||||
constructor(opt = {}) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.draw = this.mindMap.draw
|
||||
// 当前所有连接线
|
||||
this.lineList = []
|
||||
// 当前激活的连接线
|
||||
this.activeLine = null
|
||||
// 当前正在创建连接线
|
||||
this.isCreatingLine = false // 是否正在创建连接线中
|
||||
this.creatingStartNode = null // 起始节点
|
||||
this.creatingLine = null // 创建过程中的连接线
|
||||
this.overlapNode = null // 创建过程中的目标节点
|
||||
// 是否有节点正在被拖拽
|
||||
this.isNodeDragging = false
|
||||
// 箭头图标
|
||||
this.markerPath = null
|
||||
this.marker = this.createMarker()
|
||||
// 控制点
|
||||
this.controlLine1 = null
|
||||
this.controlLine2 = null
|
||||
this.controlPoint1 = null
|
||||
this.controlPoint2 = null
|
||||
this.controlPointDiameter = 10
|
||||
this.isControlPointMousedown = false
|
||||
this.mousedownControlPointKey = ''
|
||||
this.controlPointMousemoveState = {
|
||||
pos: null,
|
||||
startPoint: null,
|
||||
endPoint: null,
|
||||
targetIndex: ''
|
||||
}
|
||||
// 节流一下,不然很卡
|
||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 100, this)
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
bindEvent() {
|
||||
// 节点树渲染完毕后渲染连接线
|
||||
this.renderAllLines = this.renderAllLines.bind(this)
|
||||
this.mindMap.on('node_tree_render_end', this.renderAllLines)
|
||||
// 状态改变后重新渲染连接线
|
||||
this.mindMap.on('data_change', this.renderAllLines)
|
||||
// 监听画布和节点点击事件,用于清除当前激活的连接线
|
||||
this.mindMap.on('draw_click', () => {
|
||||
if (this.isControlPointMousedown) {
|
||||
return
|
||||
}
|
||||
this.clearActiveLine()
|
||||
})
|
||||
this.mindMap.on('node_click', node => {
|
||||
if (this.isCreatingLine) {
|
||||
this.completeCreateLine(node)
|
||||
} else {
|
||||
this.clearActiveLine()
|
||||
}
|
||||
})
|
||||
// 注册删除快捷键
|
||||
this.mindMap.keyCommand.addShortcut(
|
||||
'Del|Backspace',
|
||||
this.removeLine.bind(this)
|
||||
)
|
||||
// 注册添加连接线的命令
|
||||
this.mindMap.command.add('ADD_ASSOCIATIVE_LINE', this.addLine.bind(this))
|
||||
// 监听鼠标移动事件
|
||||
this.mindMap.on('mousemove', this.onMousemove.bind(this))
|
||||
// 节点拖拽事件
|
||||
this.mindMap.on('node_dragging', this.onNodeDragging.bind(this))
|
||||
this.mindMap.on('node_dragend', this.onNodeDragend.bind(this))
|
||||
// 拖拽控制点
|
||||
window.addEventListener('mousemove', e => {
|
||||
this.onControlPointMousemove(e)
|
||||
})
|
||||
window.addEventListener('mouseup', e => {
|
||||
this.onControlPointMouseup(e)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建箭头
|
||||
createMarker() {
|
||||
return this.draw.marker(20, 20, add => {
|
||||
add.ref(2, 5)
|
||||
add.size(10, 10)
|
||||
add.attr('orient', 'auto-start-reverse')
|
||||
this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z')
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染所有连线
|
||||
renderAllLines() {
|
||||
// 先移除
|
||||
this.removeAllLines()
|
||||
this.removeControls()
|
||||
this.clearActiveLine()
|
||||
let tree = this.mindMap.renderer.root
|
||||
if (!tree) return
|
||||
let idToNode = new Map()
|
||||
let nodeToIds = new Map()
|
||||
walk(
|
||||
tree,
|
||||
null,
|
||||
cur => {
|
||||
if (!cur) return
|
||||
let data = cur.nodeData.data
|
||||
if (
|
||||
data.associativeLineTargets &&
|
||||
data.associativeLineTargets.length > 0
|
||||
) {
|
||||
nodeToIds.set(cur, data.associativeLineTargets)
|
||||
}
|
||||
if (data.id) {
|
||||
idToNode.set(data.id, cur)
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
true,
|
||||
0
|
||||
)
|
||||
nodeToIds.forEach((ids, node) => {
|
||||
ids.forEach(id => {
|
||||
let toNode = idToNode.get(id)
|
||||
if (!node || !toNode) return
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
this.drawLine(startPoint, endPoint, node, toNode)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 绘制连接线
|
||||
drawLine(startPoint, endPoint, node, toNode) {
|
||||
let {
|
||||
associativeLineWidth,
|
||||
associativeLineColor,
|
||||
associativeLineActiveWidth,
|
||||
associativeLineActiveColor
|
||||
} = this.mindMap.themeConfig
|
||||
// 箭头
|
||||
this.markerPath
|
||||
.stroke({ color: associativeLineColor })
|
||||
.fill({ color: associativeLineColor })
|
||||
// 路径
|
||||
let { path: pathStr, controlPoints } = getNodeLinePath(
|
||||
startPoint,
|
||||
endPoint,
|
||||
node,
|
||||
toNode
|
||||
)
|
||||
// 虚线
|
||||
let path = this.draw.path()
|
||||
path
|
||||
.stroke({
|
||||
width: associativeLineWidth,
|
||||
color: associativeLineColor,
|
||||
dasharray: [6, 4]
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
path.plot(pathStr)
|
||||
path.marker('end', this.marker)
|
||||
// 不可见的点击线
|
||||
let clickPath = this.draw.path()
|
||||
clickPath
|
||||
.stroke({ width: associativeLineActiveWidth, color: 'transparent' })
|
||||
.fill({ color: 'none' })
|
||||
clickPath.plot(pathStr)
|
||||
// 点击事件
|
||||
clickPath.click(e => {
|
||||
e.stopPropagation()
|
||||
// 如果当前存在激活节点,那么取消激活节点
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.clearActiveNodes()
|
||||
} else {
|
||||
// 否则清除当前的关联线的激活状态,如果有的话
|
||||
this.clearActiveLine()
|
||||
// 保存当前激活的关联线信息
|
||||
this.activeLine = [path, clickPath, node, toNode]
|
||||
// 让不可见的点击线显示
|
||||
clickPath.stroke({ color: associativeLineActiveColor })
|
||||
// 渲染控制点和连线
|
||||
this.renderControls(
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints[0],
|
||||
controlPoints[1]
|
||||
)
|
||||
this.mindMap.emit(
|
||||
'associative_line_click',
|
||||
path,
|
||||
clickPath,
|
||||
node,
|
||||
toNode
|
||||
)
|
||||
}
|
||||
})
|
||||
this.lineList.push([path, clickPath, node, toNode])
|
||||
}
|
||||
|
||||
// 移除所有连接线
|
||||
removeAllLines() {
|
||||
this.lineList.forEach(line => {
|
||||
line[0].remove()
|
||||
line[1].remove()
|
||||
})
|
||||
this.lineList = []
|
||||
}
|
||||
|
||||
// 从当前激活节点开始创建连接线
|
||||
createLineFromActiveNode() {
|
||||
if (this.mindMap.renderer.activeNodeList.length <= 0) return
|
||||
let node = this.mindMap.renderer.activeNodeList[0]
|
||||
this.createLine(node)
|
||||
}
|
||||
|
||||
// 创建连接线
|
||||
createLine(fromNode) {
|
||||
let { associativeLineWidth, associativeLineColor } =
|
||||
this.mindMap.themeConfig
|
||||
if (this.isCreatingLine || !fromNode) return
|
||||
this.isCreatingLine = true
|
||||
this.creatingStartNode = fromNode
|
||||
this.creatingLine = this.draw.path()
|
||||
this.creatingLine
|
||||
.stroke({
|
||||
width: associativeLineWidth,
|
||||
color: associativeLineColor,
|
||||
dasharray: [6, 4]
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
this.creatingLine.marker('end', this.marker)
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
onMousemove(e) {
|
||||
if (!this.isCreatingLine) return
|
||||
this.updateCreatingLine(e)
|
||||
}
|
||||
|
||||
// 更新创建过程中的连接线
|
||||
updateCreatingLine(e) {
|
||||
let { x, y } = this.getTransformedEventPos(e)
|
||||
let startPoint = getNodePoint(this.creatingStartNode)
|
||||
let pathStr = cubicBezierPath(startPoint.x, startPoint.y, x, y)
|
||||
this.creatingLine.plot(pathStr)
|
||||
this.checkOverlapNode(x, y)
|
||||
}
|
||||
|
||||
// 获取转换后的鼠标事件对象的坐标
|
||||
getTransformedEventPos(e) {
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
let { scaleX, scaleY, translateX, translateY } =
|
||||
this.mindMap.draw.transform()
|
||||
return {
|
||||
x: (x - translateX) / scaleX,
|
||||
y: (y - translateY) / scaleY
|
||||
}
|
||||
}
|
||||
|
||||
// 检测当前移动到的目标节点
|
||||
checkOverlapNode(x, y) {
|
||||
this.overlapNode = null
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (node.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
}
|
||||
if (node === this.creatingStartNode || this.overlapNode) {
|
||||
return
|
||||
}
|
||||
let { left, top, width, height } = node
|
||||
let right = left + width
|
||||
let bottom = top + height
|
||||
if (x >= left && x <= right && y >= top && y <= bottom) {
|
||||
this.overlapNode = node
|
||||
}
|
||||
})
|
||||
if (this.overlapNode && !this.overlapNode.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
|
||||
}
|
||||
}
|
||||
|
||||
// 完成创建连接线
|
||||
completeCreateLine(node) {
|
||||
if (this.creatingStartNode === node) return
|
||||
this.addLine(this.creatingStartNode, node)
|
||||
if (this.overlapNode && this.overlapNode.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
|
||||
}
|
||||
this.isCreatingLine = false
|
||||
this.creatingStartNode = null
|
||||
this.creatingLine.remove()
|
||||
this.creatingLine = null
|
||||
this.overlapNode = null
|
||||
}
|
||||
|
||||
// 添加连接线
|
||||
addLine(fromNode, toNode) {
|
||||
if (!fromNode || !toNode) return
|
||||
// 目标节点如果没有id,则生成一个id
|
||||
let id = toNode.nodeData.data.id
|
||||
if (!id) {
|
||||
id = uuid()
|
||||
this.mindMap.execCommand('SET_NODE_DATA', toNode, {
|
||||
id
|
||||
})
|
||||
}
|
||||
// 将目标节点id保存起来
|
||||
let list = fromNode.nodeData.data.associativeLineTargets || []
|
||||
list.push(id)
|
||||
// 保存控制点
|
||||
let [startPoint, endPoint] = computeNodePoints(fromNode, toNode)
|
||||
let controlPoints = computeCubicBezierPathPoints(
|
||||
startPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.x,
|
||||
endPoint.y
|
||||
)
|
||||
let offsetList =
|
||||
fromNode.nodeData.data.associativeLineTargetControlOffsets || []
|
||||
// 保存的实际是控制点和端点的差值,否则当节点位置改变了,控制点还是原来的位置,连线就不对了
|
||||
offsetList[list.length - 1] = [
|
||||
{
|
||||
x: controlPoints[0].x - startPoint.x,
|
||||
y: controlPoints[0].y - startPoint.y
|
||||
},
|
||||
{
|
||||
x: controlPoints[1].x - endPoint.x,
|
||||
y: controlPoints[1].y - endPoint.y
|
||||
}
|
||||
]
|
||||
this.mindMap.execCommand('SET_NODE_DATA', fromNode, {
|
||||
associativeLineTargets: list,
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
})
|
||||
}
|
||||
|
||||
// 删除连接线
|
||||
removeLine() {
|
||||
if (!this.activeLine) return
|
||||
let [, , node, toNode] = this.activeLine
|
||||
this.removeControls()
|
||||
let { associativeLineTargets, associativeLineTargetControlOffsets } =
|
||||
node.nodeData.data
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineTargets: associativeLineTargets.filter((_, index) => {
|
||||
return index !== targetIndex
|
||||
}),
|
||||
associativeLineTargetControlOffsets: associativeLineTargetControlOffsets
|
||||
? associativeLineTargetControlOffsets.filter((_, index) => {
|
||||
return index !== targetIndex
|
||||
})
|
||||
: []
|
||||
})
|
||||
}
|
||||
|
||||
// 清除当前激活的节点
|
||||
clearActiveNodes() {
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
}
|
||||
|
||||
// 清除激活的线
|
||||
clearActiveLine() {
|
||||
if (this.activeLine) {
|
||||
this.activeLine[1].stroke({
|
||||
color: 'transparent'
|
||||
})
|
||||
this.activeLine = null
|
||||
this.removeControls()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理节点正在拖拽事件
|
||||
onNodeDragging() {
|
||||
if (this.isNodeDragging) return
|
||||
this.isNodeDragging = true
|
||||
this.lineList.forEach(line => {
|
||||
line[0].hide()
|
||||
line[1].hide()
|
||||
})
|
||||
this.hideControls()
|
||||
}
|
||||
|
||||
// 处理节点拖拽完成事件
|
||||
onNodeDragend() {
|
||||
if (!this.isNodeDragging) return
|
||||
this.lineList.forEach(line => {
|
||||
line[0].show()
|
||||
line[1].show()
|
||||
})
|
||||
this.showControls()
|
||||
this.isNodeDragging = false
|
||||
}
|
||||
|
||||
// 创建控制点、连线节点
|
||||
createControlNodes() {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
// 连线
|
||||
this.controlLine1 = this.draw
|
||||
.line()
|
||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||
this.controlLine2 = this.draw
|
||||
.line()
|
||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||
// 控制点
|
||||
this.controlPoint1 = this.createOneControlNode('controlPoint1')
|
||||
this.controlPoint2 = this.createOneControlNode('controlPoint2')
|
||||
}
|
||||
|
||||
// 创建控制点
|
||||
createOneControlNode(pointKey) {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
return this.draw
|
||||
.circle(this.controlPointDiameter)
|
||||
.stroke({ color: associativeLineActiveColor })
|
||||
.fill({ color: '#fff' })
|
||||
.click(e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
.mousedown(e => {
|
||||
this.onControlPointMousedown(e, pointKey)
|
||||
})
|
||||
}
|
||||
|
||||
// 控制点的鼠标按下事件
|
||||
onControlPointMousedown(e, pointKey) {
|
||||
e.stopPropagation()
|
||||
this.isControlPointMousedown = true
|
||||
this.mousedownControlPointKey = pointKey
|
||||
}
|
||||
|
||||
// 控制点的鼠标移动事件
|
||||
onControlPointMousemove(e) {
|
||||
if (
|
||||
!this.isControlPointMousedown ||
|
||||
!this.mousedownControlPointKey ||
|
||||
!this[this.mousedownControlPointKey]
|
||||
)
|
||||
return
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let radius = this.controlPointDiameter / 2
|
||||
// 转换鼠标当前的位置
|
||||
let { x, y } = this.getTransformedEventPos(e)
|
||||
this.controlPointMousemoveState.pos = {
|
||||
x,
|
||||
y
|
||||
}
|
||||
// 更新当前拖拽的控制点的位置
|
||||
this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
|
||||
let [path, clickPath, node, toNode] = this.activeLine
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
this.controlPointMousemoveState.startPoint = startPoint
|
||||
this.controlPointMousemoveState.endPoint = endPoint
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
this.controlPointMousemoveState.targetIndex = targetIndex
|
||||
let offsets = []
|
||||
let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsets = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||
} else {
|
||||
offsets = associativeLineTargetControlOffsets[targetIndex]
|
||||
}
|
||||
let point1 = null
|
||||
let point2 = null
|
||||
// 拖拽的是控制点1
|
||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||
point1 = {
|
||||
x,
|
||||
y
|
||||
}
|
||||
point2 = {
|
||||
x: endPoint.x + offsets[1].x,
|
||||
y: endPoint.y + offsets[1].y
|
||||
}
|
||||
// 更新控制点1的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
} else {
|
||||
// 拖拽的是控制点2
|
||||
point1 = {
|
||||
x: startPoint.x + offsets[0].x,
|
||||
y: startPoint.y + offsets[0].y
|
||||
}
|
||||
point2 = {
|
||||
x,
|
||||
y
|
||||
}
|
||||
// 更新控制点2的连线
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
}
|
||||
// 更新关联线
|
||||
let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||
path.plot(pathStr)
|
||||
clickPath.plot(pathStr)
|
||||
}
|
||||
|
||||
// 控制点的鼠标移动事件
|
||||
onControlPointMouseup(e) {
|
||||
if (!this.isControlPointMousedown) return
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let { pos, startPoint, endPoint, targetIndex } =
|
||||
this.controlPointMousemoveState
|
||||
let [, , node] = this.activeLine
|
||||
let offsetList = []
|
||||
let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsetList[targetIndex] = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||
} else {
|
||||
offsetList = associativeLineTargetControlOffsets
|
||||
}
|
||||
let offset1 = null
|
||||
let offset2 = null
|
||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||
// 更新控制点1数据
|
||||
offset1 = {
|
||||
x: pos.x - startPoint.x,
|
||||
y: pos.y - startPoint.y
|
||||
}
|
||||
offset2 = offsetList[targetIndex][1]
|
||||
} else {
|
||||
// 更新控制点2数据
|
||||
offset1 = offsetList[targetIndex][0]
|
||||
offset2 = {
|
||||
x: pos.x - endPoint.x,
|
||||
y: pos.y - endPoint.y
|
||||
}
|
||||
}
|
||||
offsetList[targetIndex] = [offset1, offset2]
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
})
|
||||
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
||||
setTimeout(() => {
|
||||
this.resetControlPoint()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// 复位控制点移动
|
||||
resetControlPoint() {
|
||||
this.isControlPointMousedown = false
|
||||
this.mousedownControlPointKey = ''
|
||||
this.controlPointMousemoveState = {
|
||||
pos: null,
|
||||
startPoint: null,
|
||||
endPoint: null,
|
||||
targetIndex: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染控制点
|
||||
renderControls(startPoint, endPoint, point1, point2) {
|
||||
if (!this.controlLine1) {
|
||||
this.createControlNodes()
|
||||
}
|
||||
let radius = this.controlPointDiameter / 2
|
||||
// 控制点和起终点的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
// 控制点
|
||||
this.controlPoint1.x(point1.x - radius).y(point1.y - radius)
|
||||
this.controlPoint2.x(point2.x - radius).y(point2.y - radius)
|
||||
}
|
||||
|
||||
// 删除控制点
|
||||
removeControls() {
|
||||
if (!this.controlLine1) return
|
||||
;[
|
||||
this.controlLine1,
|
||||
this.controlLine2,
|
||||
this.controlPoint1,
|
||||
this.controlPoint2
|
||||
].forEach(item => {
|
||||
item.remove()
|
||||
})
|
||||
this.controlLine1 = null
|
||||
this.controlLine2 = null
|
||||
this.controlPoint1 = null
|
||||
this.controlPoint2 = null
|
||||
}
|
||||
|
||||
// 隐藏控制点
|
||||
hideControls() {
|
||||
if (!this.controlLine1) return
|
||||
;[
|
||||
this.controlLine1,
|
||||
this.controlLine2,
|
||||
this.controlPoint1,
|
||||
this.controlPoint2
|
||||
].forEach(item => {
|
||||
item.hide()
|
||||
})
|
||||
}
|
||||
|
||||
// 显示控制点
|
||||
showControls() {
|
||||
if (!this.controlLine1) return
|
||||
;[
|
||||
this.controlLine1,
|
||||
this.controlLine2,
|
||||
this.controlPoint1,
|
||||
this.controlPoint2
|
||||
].forEach(item => {
|
||||
item.show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
AssociativeLine.instanceName = 'associativeLine'
|
||||
|
||||
export default AssociativeLine
|
||||
@@ -1,15 +1,60 @@
|
||||
import { nextTick } from './utils'
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-27 13:16:23
|
||||
* @Desc: 在下一个事件循环里执行任务
|
||||
*/
|
||||
const nextTick = function (fn, ctx) {
|
||||
let pending = false
|
||||
let timerFunc = null
|
||||
let handle = () => {
|
||||
pending = false
|
||||
ctx ? fn.call(ctx) : fn()
|
||||
}
|
||||
// 支持MutationObserver接口的话使用MutationObserver
|
||||
if (typeof MutationObserver !== 'undefined') {
|
||||
let counter = 1
|
||||
let observer = new MutationObserver(handle)
|
||||
let textNode = document.createTextNode(counter)
|
||||
observer.observe(textNode, {
|
||||
characterData: true // 设为 true 表示监视指定目标节点或子节点树中节点所包含的字符数据的变化
|
||||
})
|
||||
timerFunc = function () {
|
||||
counter = (counter + 1) % 2 // counter会在0和1两者循环变化
|
||||
textNode.data = counter // 节点变化会触发回调handle,
|
||||
}
|
||||
} else {
|
||||
// 否则使用定时器
|
||||
timerFunc = setTimeout
|
||||
}
|
||||
return function () {
|
||||
if (pending) return
|
||||
pending = true
|
||||
timerFunc(handle, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// 批量执行
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-26 22:40:52
|
||||
* @Desc: 批量执行
|
||||
*/
|
||||
class BatchExecution {
|
||||
// 构造函数
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-26 22:41:41
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor() {
|
||||
this.has = {}
|
||||
this.queue = []
|
||||
this.nextTick = nextTick(this.flush, this)
|
||||
}
|
||||
|
||||
// 添加任务
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-27 12:54:04
|
||||
* @Desc: 添加任务
|
||||
*/
|
||||
push(name, fn) {
|
||||
if (this.has[name]) {
|
||||
return
|
||||
@@ -22,7 +67,11 @@ class BatchExecution {
|
||||
this.nextTick()
|
||||
}
|
||||
|
||||
// 执行队列
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-27 13:09:24
|
||||
* @Desc: 执行队列
|
||||
*/
|
||||
flush() {
|
||||
let fns = this.queue.slice(0)
|
||||
this.queue = []
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { copyRenderTree, simpleDeepClone, nextTick } from './utils'
|
||||
import { copyRenderTree, simpleDeepClone } from './utils'
|
||||
|
||||
// 命令类
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:10:06
|
||||
* @Desc: 命令类
|
||||
*/
|
||||
class Command {
|
||||
// 构造函数
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:10:24
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
@@ -11,17 +19,24 @@ class Command {
|
||||
this.activeHistoryIndex = 0
|
||||
// 注册快捷键
|
||||
this.registerShortcutKeys()
|
||||
this.addHistory = nextTick(this.addHistory, this)
|
||||
}
|
||||
|
||||
// 清空历史数据
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-03 23:06:55
|
||||
* @Desc: 清空历史数据
|
||||
*/
|
||||
clearHistory() {
|
||||
this.history = []
|
||||
this.activeHistoryIndex = 0
|
||||
this.mindMap.emit('back_forward', 0, 0)
|
||||
}
|
||||
|
||||
// 注册快捷键
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-02 23:23:19
|
||||
* @Desc: 注册快捷键
|
||||
*/
|
||||
registerShortcutKeys() {
|
||||
this.mindMap.keyCommand.addShortcut('Control+z', () => {
|
||||
this.mindMap.execCommand('BACK')
|
||||
@@ -31,20 +46,28 @@ class Command {
|
||||
})
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:12:30
|
||||
* @Desc: 执行命令
|
||||
*/
|
||||
exec(name, ...args) {
|
||||
if (this.commands[name]) {
|
||||
this.commands[name].forEach(fn => {
|
||||
fn(...args)
|
||||
})
|
||||
if (['BACK', 'FORWARD', 'SET_NODE_ACTIVE', 'CLEAR_ACTIVE_NODE'].includes(name)) {
|
||||
if (name === 'BACK' || name === 'FORWARD') {
|
||||
return
|
||||
}
|
||||
this.addHistory()
|
||||
}
|
||||
}
|
||||
|
||||
// 添加命令
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:13:01
|
||||
* @Desc: 添加命令
|
||||
*/
|
||||
add(name, fn) {
|
||||
if (this.commands[name]) {
|
||||
this.commands[name].push(fn)
|
||||
@@ -53,7 +76,11 @@ class Command {
|
||||
}
|
||||
}
|
||||
|
||||
// 移除命令
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 23:02:41
|
||||
* @Desc: 移除命令
|
||||
*/
|
||||
remove(name, fn) {
|
||||
if (!this.commands[name]) {
|
||||
return
|
||||
@@ -71,18 +98,13 @@ class Command {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加回退数据
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 14:35:43
|
||||
* @Desc: 添加回退数据
|
||||
*/
|
||||
addHistory() {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
let data = this.getCopyData()
|
||||
// 此次数据和上次一样则不重复添加
|
||||
if (this.history.length > 0 && JSON.stringify(this.history[this.history.length - 1]) === JSON.stringify(data)) {
|
||||
return
|
||||
}
|
||||
// 删除当前历史指针后面的数据
|
||||
this.history = this.history.slice(0, this.activeHistoryIndex + 1)
|
||||
this.history.push(simpleDeepClone(data))
|
||||
this.activeHistoryIndex = this.history.length - 1
|
||||
this.mindMap.emit('data_change', data)
|
||||
@@ -93,11 +115,12 @@ class Command {
|
||||
)
|
||||
}
|
||||
|
||||
// 回退
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 22:34:53
|
||||
* @Desc: 回退
|
||||
*/
|
||||
back(step = 1) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (this.activeHistoryIndex - step >= 0) {
|
||||
this.activeHistoryIndex -= step
|
||||
this.mindMap.emit(
|
||||
@@ -105,30 +128,32 @@ class Command {
|
||||
this.activeHistoryIndex,
|
||||
this.history.length
|
||||
)
|
||||
let data = simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
this.mindMap.emit('data_change', data)
|
||||
return data
|
||||
return simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
}
|
||||
}
|
||||
|
||||
// 前进
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-12 10:45:31
|
||||
* @Desc: 前进
|
||||
*/
|
||||
forward(step = 1) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
let len = this.history.length
|
||||
if (this.activeHistoryIndex + step <= len - 1) {
|
||||
this.activeHistoryIndex += step
|
||||
this.mindMap.emit('back_forward', this.activeHistoryIndex)
|
||||
let data = simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
this.mindMap.emit('data_change', data)
|
||||
return data
|
||||
return simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
}
|
||||
}
|
||||
|
||||
// 获取渲染树数据副本
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 15:02:58
|
||||
* @Desc: 获取渲染树数据副本
|
||||
*/
|
||||
getCopyData() {
|
||||
return copyRenderTree({}, this.mindMap.renderer.renderTree, true)
|
||||
return copyRenderTree({}, this.mindMap.renderer.renderTree)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { bfsWalk, throttle } from './utils'
|
||||
import Base from './layouts/Base'
|
||||
|
||||
// 节点拖动类
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 17:38:55
|
||||
* @Desc: 节点拖动类
|
||||
*/
|
||||
class Drag extends Base {
|
||||
// 构造函数
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:35:16
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor({ mindMap }) {
|
||||
super(mindMap.renderer)
|
||||
this.mindMap = mindMap
|
||||
@@ -13,8 +20,12 @@ class Drag extends Base {
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 复位
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:33:56
|
||||
* @Desc: 复位
|
||||
*/
|
||||
reset() {
|
||||
// 当前拖拽节点
|
||||
this.node = null
|
||||
@@ -47,8 +58,11 @@ class Drag extends Base {
|
||||
this.mouseMoveY = 0
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:36:36
|
||||
* @Desc: 绑定事件
|
||||
*/
|
||||
bindEvent() {
|
||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
|
||||
this.mindMap.on('node_mousedown', (node, e) => {
|
||||
@@ -62,11 +76,12 @@ class Drag extends Base {
|
||||
// 计算鼠标按下的位置距离节点左上角的距离
|
||||
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.offsetX = e.clientX - (node.left * scaleX + translateX)
|
||||
this.offsetY = e.clientY - (node.top * scaleY + translateY)
|
||||
//
|
||||
this.node = node
|
||||
this.isMousedown = true
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
})
|
||||
@@ -77,7 +92,6 @@ class Drag extends Base {
|
||||
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
|
||||
@@ -97,8 +111,12 @@ class Drag extends Base {
|
||||
this.mindMap.on('mouseup', this.onMouseup)
|
||||
}
|
||||
|
||||
// 鼠标松开事件
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:38:02
|
||||
* @Desc: 鼠标松开事件
|
||||
*/
|
||||
onMouseup(e) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
@@ -120,7 +138,7 @@ class Drag extends Base {
|
||||
// 存在下一个相邻节点,作为其前一个兄弟节点
|
||||
this.mindMap.renderer.setNodeActive(this.nextNode, false)
|
||||
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
|
||||
} else if (_nodeIsDrag && this.mindMap.opt.enableFreeDrag) {
|
||||
} else if (_nodeIsDrag) {
|
||||
// 自定义位置
|
||||
let { x, y } = this.mindMap.toPos(
|
||||
e.clientX - this.offsetX,
|
||||
@@ -137,11 +155,14 @@ class Drag extends Base {
|
||||
this.mindMap.render()
|
||||
}
|
||||
this.reset()
|
||||
this.mindMap.emit('node_dragend')
|
||||
}
|
||||
|
||||
// 创建克隆节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:34:53
|
||||
* @Desc: 创建克隆节点
|
||||
*/
|
||||
createCloneNode() {
|
||||
if (!this.clone) {
|
||||
// 节点
|
||||
@@ -162,8 +183,12 @@ class Drag extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
// 移除克隆节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 19:35:16
|
||||
* @Desc: 移除克隆节点
|
||||
*/
|
||||
removeCloneNode() {
|
||||
if (!this.clone) {
|
||||
return
|
||||
@@ -173,8 +198,12 @@ class Drag extends Base {
|
||||
this.placeholder.remove()
|
||||
}
|
||||
|
||||
// 拖动中
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 18:53:47
|
||||
* @Desc: 拖动中
|
||||
*/
|
||||
onMove(x, y) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
@@ -200,8 +229,11 @@ class Drag extends Base {
|
||||
this.checkOverlapNode()
|
||||
}
|
||||
|
||||
// 检测重叠节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:20:43
|
||||
* @Desc: 检测重叠节点
|
||||
*/
|
||||
checkOverlapNode() {
|
||||
if (!this.drawTransform) {
|
||||
return
|
||||
@@ -243,8 +275,7 @@ class Drag extends Base {
|
||||
}
|
||||
}
|
||||
// 检测兄弟节点位置
|
||||
if (!this.prevNode && !this.nextNode && !node.isRoot) {
|
||||
// && this.node.isBrother(node)
|
||||
if (!this.prevNode && !this.nextNode && this.node.isBrother(node)) {
|
||||
if (left <= checkRight && right >= this.cloneNodeLeft) {
|
||||
if (this.cloneNodeTop > bottom && this.cloneNodeTop <= bottom + 10) {
|
||||
this.prevNode = node
|
||||
@@ -262,6 +293,4 @@ class Drag extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
Drag.instanceName = 'drag'
|
||||
|
||||
export default Drag
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import EventEmitter from 'eventemitter3'
|
||||
|
||||
// 事件类
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 14:53:09
|
||||
* @Desc: 事件类
|
||||
*/
|
||||
class Event extends EventEmitter {
|
||||
// 构造函数
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 14:53:25
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
super()
|
||||
this.opt = opt
|
||||
@@ -24,7 +34,12 @@ class Event extends EventEmitter {
|
||||
this.bind()
|
||||
}
|
||||
|
||||
// 绑定函数上下文
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:52:24
|
||||
* @Desc: 绑定函数上下文
|
||||
*/
|
||||
bindFn() {
|
||||
this.onDrawClick = this.onDrawClick.bind(this)
|
||||
this.onMousedown = this.onMousedown.bind(this)
|
||||
@@ -36,40 +51,68 @@ class Event extends EventEmitter {
|
||||
this.onKeyup = this.onKeyup.bind(this)
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 14:53:43
|
||||
* @Desc: 绑定事件
|
||||
*/
|
||||
bind() {
|
||||
this.mindMap.svg.on('click', this.onDrawClick)
|
||||
this.mindMap.el.addEventListener('mousedown', this.onMousedown)
|
||||
this.mindMap.svg.on('mousedown', this.onSvgMousedown)
|
||||
window.addEventListener('mousemove', this.onMousemove)
|
||||
window.addEventListener('mouseup', this.onMouseup)
|
||||
this.mindMap.el.addEventListener('wheel', this.onMousewheel)
|
||||
// 兼容火狐浏览器
|
||||
if (window.navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
|
||||
this.mindMap.el.addEventListener('DOMMouseScroll', this.onMousewheel)
|
||||
} else {
|
||||
this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
|
||||
}
|
||||
this.mindMap.svg.on('contextmenu', this.onContextmenu)
|
||||
window.addEventListener('keyup', this.onKeyup)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:40:51
|
||||
* @Desc: 解绑事件
|
||||
*/
|
||||
unbind() {
|
||||
this.mindMap.svg.off('click', this.onDrawClick)
|
||||
this.mindMap.el.removeEventListener('mousedown', this.onMousedown)
|
||||
window.removeEventListener('mousemove', this.onMousemove)
|
||||
window.removeEventListener('mouseup', this.onMouseup)
|
||||
this.mindMap.el.removeEventListener('wheel', this.onMousewheel)
|
||||
this.mindMap.el.removeEventListener('mousewheel', this.onMousewheel)
|
||||
this.mindMap.svg.off('contextmenu', this.onContextmenu)
|
||||
window.removeEventListener('keyup', this.onKeyup)
|
||||
}
|
||||
|
||||
// 画布的单击事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 13:19:39
|
||||
* @Desc: 画布的单击事件
|
||||
*/
|
||||
onDrawClick(e) {
|
||||
this.emit('draw_click', e)
|
||||
}
|
||||
|
||||
// svg画布的鼠标按下事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-16 13:37:30
|
||||
* @Desc: svg画布的鼠标按下事件
|
||||
*/
|
||||
onSvgMousedown(e) {
|
||||
this.emit('svg_mousedown', e)
|
||||
}
|
||||
|
||||
// 鼠标按下事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:17:35
|
||||
* @Desc: 鼠标按下事件
|
||||
*/
|
||||
onMousedown(e) {
|
||||
// e.preventDefault()
|
||||
// 鼠标左键
|
||||
@@ -81,7 +124,12 @@ class Event extends EventEmitter {
|
||||
this.emit('mousedown', e, this)
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:18:32
|
||||
* @Desc: 鼠标移动事件
|
||||
*/
|
||||
onMousemove(e) {
|
||||
// e.preventDefault()
|
||||
this.mousemovePos.x = e.clientX
|
||||
@@ -94,39 +142,51 @@ class Event extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标松开事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:18:57
|
||||
* @Desc: 鼠标松开事件
|
||||
*/
|
||||
onMouseup(e) {
|
||||
this.isLeftMousedown = false
|
||||
this.emit('mouseup', e, this)
|
||||
}
|
||||
|
||||
// 鼠标滚动
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:46:27
|
||||
* @Desc: 鼠标滚动
|
||||
*/
|
||||
onMousewheel(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let dir
|
||||
// 解决mac触控板双指缩放方向相反的问题
|
||||
if (e.ctrlKey) {
|
||||
if (e.deltaY > 0) dir = 'up'
|
||||
if (e.deltaY < 0) dir = 'down'
|
||||
if (e.deltaX > 0) dir = 'left'
|
||||
if (e.deltaX < 0) dir = 'right'
|
||||
if ((e.wheelDeltaY || e.detail) > 0) {
|
||||
dir = 'up'
|
||||
} else {
|
||||
if ((e.wheelDeltaY || e.detail) > 0) dir = 'up'
|
||||
if ((e.wheelDeltaY || e.detail) < 0) dir = 'down'
|
||||
if ((e.wheelDeltaX || e.detail) > 0) dir = 'left'
|
||||
if ((e.wheelDeltaX || e.detail) < 0) dir = 'right'
|
||||
dir = 'down'
|
||||
}
|
||||
this.emit('mousewheel', e, dir, this)
|
||||
}
|
||||
|
||||
// 鼠标右键菜单事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:34:13
|
||||
* @Desc: 鼠标右键菜单事件
|
||||
*/
|
||||
onContextmenu(e) {
|
||||
e.preventDefault()
|
||||
this.emit('contextmenu', e)
|
||||
}
|
||||
|
||||
// 按键松开事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 11:12:11
|
||||
* @Desc: 按键松开事件
|
||||
*/
|
||||
onKeyup(e) {
|
||||
this.emit('keyup', e)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
import { imgToDataUrl, downloadFile } from './utils'
|
||||
import JsPDF from 'jspdf'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import drawBackgroundImageToCanvas from './utils/simulateCSSBackgroundInCanvas'
|
||||
import { transformToMarkdown } from './parse/toMarkdown'
|
||||
const URL = window.URL || window.webkitURL || window
|
||||
|
||||
// 导出类
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-01 22:05:16
|
||||
* @Desc: 导出类
|
||||
*/
|
||||
class Export {
|
||||
// 构造函数
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-01 22:05:42
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.exportPadding = this.mindMap.opt.exportPadding
|
||||
}
|
||||
|
||||
// 导出
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-02 07:44:06
|
||||
* @Desc: 导出
|
||||
*/
|
||||
async export(type, isDownload = true, name = '思维导图', ...args) {
|
||||
if (this[type]) {
|
||||
let result = await this[type](name, ...args)
|
||||
@@ -26,9 +36,13 @@ class Export {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取svg数据
|
||||
async getSvgData(domToImage) {
|
||||
let { svg, svgHTML } = this.mindMap.getSvgData()
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 14:57:40
|
||||
* @Desc: 获取svg数据
|
||||
*/
|
||||
async getSvgData() {
|
||||
let { svg, svgHTML } = this.mindMap.miniMap.getMiniMap()
|
||||
// 把图片的url转换成data:url类型,否则导出会丢失图片
|
||||
let imageList = svg.find('image')
|
||||
let task = imageList.map(async item => {
|
||||
@@ -37,23 +51,17 @@ class Export {
|
||||
item.attr('href', imgData)
|
||||
})
|
||||
await Promise.all(task)
|
||||
// 如果开启了富文本编辑,需要把svg中的dom元素转换成图片
|
||||
let nodeWithDomToImg = null
|
||||
if (domToImage && this.mindMap.richText) {
|
||||
let res = await this.mindMap.richText.handleSvgDomElements(svg)
|
||||
if (res) {
|
||||
nodeWithDomToImg = res.svg
|
||||
svgHTML = res.svgHTML
|
||||
}
|
||||
}
|
||||
return {
|
||||
node: svg,
|
||||
str: svgHTML,
|
||||
nodeWithDomToImg
|
||||
str: svgHTML
|
||||
}
|
||||
}
|
||||
|
||||
// svg转png
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 15:25:19
|
||||
* @Desc: svg转png
|
||||
*/
|
||||
svgToPng(svgSrc) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
@@ -91,15 +99,17 @@ class Export {
|
||||
})
|
||||
}
|
||||
|
||||
// 在canvas上绘制思维导图背景
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 15:32:07
|
||||
* @Desc: 在canvas上绘制思维导图背景
|
||||
*/
|
||||
drawBackgroundToCanvas(ctx, width, height) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let {
|
||||
backgroundColor = '#fff',
|
||||
backgroundImage,
|
||||
backgroundRepeat = 'no-repeat',
|
||||
backgroundPosition = 'center center',
|
||||
backgroundSize = 'cover',
|
||||
backgroundRepeat = 'repeat'
|
||||
} = this.mindMap.themeConfig
|
||||
// 背景颜色
|
||||
ctx.save()
|
||||
@@ -110,31 +120,34 @@ class Export {
|
||||
// 背景图片
|
||||
if (backgroundImage && backgroundImage !== 'none') {
|
||||
ctx.save()
|
||||
drawBackgroundImageToCanvas(ctx, width, height, backgroundImage, {
|
||||
backgroundRepeat,
|
||||
backgroundPosition,
|
||||
backgroundSize
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
let img = new Image()
|
||||
img.src = backgroundImage
|
||||
img.onload = () => {
|
||||
let pat = ctx.createPattern(img, backgroundRepeat)
|
||||
ctx.rect(0, 0, width, height)
|
||||
ctx.fillStyle = pat
|
||||
ctx.fill()
|
||||
ctx.restore()
|
||||
})
|
||||
resolve()
|
||||
}
|
||||
img.onerror = e => {
|
||||
reject(e)
|
||||
}
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 导出为png
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-01 22:09:51
|
||||
* @Desc: 导出为png
|
||||
* 方法1.把svg的图片都转化成data:url格式,再转换
|
||||
* 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
|
||||
*/
|
||||
async png() {
|
||||
let { str } = await this.getSvgData(true)
|
||||
let { str } = await this.getSvgData()
|
||||
// 转换成blob数据
|
||||
let blob = new Blob([str], {
|
||||
type: 'image/svg+xml'
|
||||
@@ -147,7 +160,12 @@ class Export {
|
||||
return imgDataUrl
|
||||
}
|
||||
|
||||
// 导出为pdf
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-08 19:23:08
|
||||
* @Desc: 导出为pdf
|
||||
*/
|
||||
async pdf(name) {
|
||||
let img = await this.png()
|
||||
let pdf = new JsPDF('', 'pt', 'a4')
|
||||
@@ -179,7 +197,11 @@ class Export {
|
||||
image.src = img
|
||||
}
|
||||
|
||||
// 在svg上绘制思维导图背景
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 15:32:07
|
||||
* @Desc: 在svg上绘制思维导图背景
|
||||
*/
|
||||
drawBackgroundToSvg(svg) {
|
||||
return new Promise(async resolve => {
|
||||
let {
|
||||
@@ -201,22 +223,13 @@ class Export {
|
||||
})
|
||||
}
|
||||
|
||||
// 导出为svg
|
||||
// domToImage:是否将svg中的dom节点转换成图片的形式
|
||||
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
|
||||
async svg(name, domToImage = false, plusCssText) {
|
||||
let { node, nodeWithDomToImg } = await this.getSvgData(domToImage)
|
||||
// 开启了节点富文本编辑
|
||||
if (this.mindMap.richText) {
|
||||
if (domToImage) {
|
||||
node = nodeWithDomToImg
|
||||
} else if (plusCssText) {
|
||||
let foreignObjectList = node.find('foreignObject')
|
||||
if (foreignObjectList.length > 0) {
|
||||
foreignObjectList[0].add(SVG(`<style>${plusCssText}</style>`))
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 14:54:07
|
||||
* @Desc: 导出为svg
|
||||
*/
|
||||
async svg(name) {
|
||||
let { node } = await this.getSvgData()
|
||||
node.first().before(SVG(`<title>${name}</title>`))
|
||||
await this.drawBackgroundToSvg(node)
|
||||
let str = node.svg()
|
||||
@@ -227,7 +240,11 @@ class Export {
|
||||
return URL.createObjectURL(blob)
|
||||
}
|
||||
|
||||
// 导出为json
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-03 22:19:17
|
||||
* @Desc: 导出为json
|
||||
*/
|
||||
json(name, withConfig = true) {
|
||||
let data = this.mindMap.getData(withConfig)
|
||||
let str = JSON.stringify(data)
|
||||
@@ -235,20 +252,14 @@ class Export {
|
||||
return URL.createObjectURL(blob)
|
||||
}
|
||||
|
||||
// 专有文件,其实就是json文件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-03 22:24:24
|
||||
* @Desc: 专有文件,其实就是json文件
|
||||
*/
|
||||
smm(name, withConfig) {
|
||||
return this.json(name, withConfig)
|
||||
}
|
||||
|
||||
// markdown文件
|
||||
md() {
|
||||
let data = this.mindMap.getData()
|
||||
let content = transformToMarkdown(data)
|
||||
let blob = new Blob([content])
|
||||
return URL.createObjectURL(blob)
|
||||
}
|
||||
}
|
||||
|
||||
Export.instanceName = 'doExport'
|
||||
|
||||
export default Export
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { keyMap } from './utils/keyMap'
|
||||
// 快捷按键、命令处理类
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 15:20:46
|
||||
* @Desc: 快捷按键、命令处理类
|
||||
*/
|
||||
export default class KeyCommand {
|
||||
// 构造函数
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 15:21:32
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt) {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
@@ -13,29 +21,51 @@ export default class KeyCommand {
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 暂停快捷键响应
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-14 08:57:55
|
||||
* @Desc: 暂停快捷键响应
|
||||
*/
|
||||
pause() {
|
||||
this.isPause = true
|
||||
}
|
||||
|
||||
// 恢复快捷键响应
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-14 08:58:43
|
||||
* @Desc: 恢复快捷键响应
|
||||
*/
|
||||
recovery() {
|
||||
this.isPause = false
|
||||
}
|
||||
|
||||
// 保存当前注册的快捷键数据,然后清空快捷键数据
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-16 16:29:01
|
||||
* @Desc: 保存当前注册的快捷键数据,然后清空快捷键数据
|
||||
*/
|
||||
save() {
|
||||
this.shortcutMapCache = this.shortcutMap
|
||||
this.shortcutMap = {}
|
||||
}
|
||||
|
||||
// 恢复保存的快捷键数据,然后清空缓存数据
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-16 16:29:38
|
||||
* @Desc: 恢复保存的快捷键数据,然后清空缓存数据
|
||||
*/
|
||||
restore() {
|
||||
this.shortcutMap = this.shortcutMapCache
|
||||
this.shortcutMapCache = {}
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 15:23:22
|
||||
* @Desc: 绑定事件
|
||||
*/
|
||||
bindEvent() {
|
||||
window.addEventListener('keydown', e => {
|
||||
if (this.isPause) {
|
||||
@@ -53,7 +83,11 @@ export default class KeyCommand {
|
||||
})
|
||||
}
|
||||
|
||||
// 检查键值是否符合
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 19:24:53
|
||||
* @Desc: 检查键值是否符合
|
||||
*/
|
||||
checkKey(e, key) {
|
||||
let o = this.getOriginEventCodeArr(e)
|
||||
let k = this.getKeyCodeArr(key)
|
||||
@@ -73,7 +107,11 @@ export default class KeyCommand {
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取事件对象里的键值数组
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 19:15:19
|
||||
* @Desc: 获取事件对象里的键值数组
|
||||
*/
|
||||
getOriginEventCodeArr(e) {
|
||||
let arr = []
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
@@ -91,7 +129,11 @@ export default class KeyCommand {
|
||||
return arr
|
||||
}
|
||||
|
||||
// 获取快捷键对应的键值数组
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 19:40:11
|
||||
* @Desc: 获取快捷键对应的键值数组
|
||||
*/
|
||||
getKeyCodeArr(key) {
|
||||
let keyArr = key.split(/\s*\+\s*/)
|
||||
let arr = []
|
||||
@@ -101,8 +143,10 @@ export default class KeyCommand {
|
||||
return arr
|
||||
}
|
||||
|
||||
// 添加快捷键命令
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 15:23:00
|
||||
* @Desc: 添加快捷键命令
|
||||
* Enter
|
||||
* Tab | Insert
|
||||
* Shift + a
|
||||
@@ -117,7 +161,12 @@ export default class KeyCommand {
|
||||
})
|
||||
}
|
||||
|
||||
// 移除快捷键命令
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-27 14:06:16
|
||||
* @Desc: 移除快捷键命令
|
||||
*/
|
||||
removeShortcut(key, fn) {
|
||||
key.split(/\s*\|\s*/).forEach(item => {
|
||||
if (this.shortcutMap[item]) {
|
||||
@@ -136,7 +185,11 @@ export default class KeyCommand {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取指定快捷键的处理函数
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-14 08:49:58
|
||||
* @Desc: 获取指定快捷键的处理函数
|
||||
*/
|
||||
getShortcutFn(key) {
|
||||
let res = []
|
||||
key.split(/\s*\|\s*/).forEach(item => {
|
||||
|
||||
@@ -1,30 +1,39 @@
|
||||
import { isKey } from './utils/keyMap'
|
||||
import { bfsWalk } from './utils'
|
||||
|
||||
// 键盘导航类
|
||||
class KeyboardNavigation {
|
||||
// 构造函数
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 11:06:50
|
||||
* @Desc: 键盘导航类
|
||||
*/
|
||||
export default class KeyboardNavigation {
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 11:07:24
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt) {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
this.onKeyup = this.onKeyup.bind(this)
|
||||
this.mindMap.keyCommand.addShortcut('Left', () => {
|
||||
this.onKeyup('Left')
|
||||
})
|
||||
this.mindMap.keyCommand.addShortcut('Up', () => {
|
||||
this.onKeyup('Up')
|
||||
})
|
||||
this.mindMap.keyCommand.addShortcut('Right', () => {
|
||||
this.onKeyup('Right')
|
||||
})
|
||||
this.mindMap.keyCommand.addShortcut('Down', () => {
|
||||
this.onKeyup('Down')
|
||||
})
|
||||
this.mindMap.on('keyup', this.onKeyup)
|
||||
}
|
||||
|
||||
// 处理按键事件
|
||||
onKeyup(dir) {
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 14:12:27
|
||||
* @Desc: 处理按键事件
|
||||
*/
|
||||
onKeyup(e) {
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.focus(dir)
|
||||
;['Left', 'Up', 'Right', 'Down'].forEach(dir => {
|
||||
if (isKey(e, dir)) {
|
||||
this.focus(dir)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let root = this.mindMap.renderer.root
|
||||
this.mindMap.renderer.moveNodeToCenter(root)
|
||||
@@ -32,16 +41,17 @@ class KeyboardNavigation {
|
||||
}
|
||||
}
|
||||
|
||||
// 聚焦到下一个节点
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 14:12:39
|
||||
* @Desc: 聚焦到下一个节点
|
||||
*/
|
||||
focus(dir) {
|
||||
// 当前聚焦的节点
|
||||
let currentActiveNode = this.mindMap.renderer.activeNodeList[0]
|
||||
// 当前聚焦节点的位置信息
|
||||
let currentActiveNodeRect = this.getNodeRect(currentActiveNode)
|
||||
// 寻找的下一个聚焦节点
|
||||
let targetNode = null
|
||||
let targetDis = Infinity
|
||||
// 保存并维护距离最近的节点
|
||||
let checkNodeDis = (rect, node) => {
|
||||
let dis = this.getDistance(currentActiveNodeRect, rect)
|
||||
if (dis < targetDis) {
|
||||
@@ -49,158 +59,39 @@ class KeyboardNavigation {
|
||||
targetDis = dis
|
||||
}
|
||||
}
|
||||
|
||||
// 第一优先级:阴影算法
|
||||
this.getFocusNodeByShadowAlgorithm({
|
||||
currentActiveNode,
|
||||
currentActiveNodeRect,
|
||||
dir,
|
||||
checkNodeDis
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
if (dir === 'Right') {
|
||||
if (left >= currentActiveNodeRect.right) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
} else if (dir === 'Left') {
|
||||
if (right <= currentActiveNodeRect.left) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
} else if (dir === 'Up') {
|
||||
if (bottom <= currentActiveNodeRect.top) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
} else if (dir === 'Down') {
|
||||
if (top >= currentActiveNodeRect.bottom) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 第二优先级:区域算法
|
||||
if (!targetNode) {
|
||||
this.getFocusNodeByAreaAlgorithm({
|
||||
currentActiveNode,
|
||||
currentActiveNodeRect,
|
||||
dir,
|
||||
checkNodeDis
|
||||
})
|
||||
}
|
||||
|
||||
// 第三优先级:简单算法
|
||||
if (!targetNode) {
|
||||
this.getFocusNodeBySimpleAlgorithm({
|
||||
currentActiveNode,
|
||||
currentActiveNodeRect,
|
||||
dir,
|
||||
checkNodeDis
|
||||
})
|
||||
}
|
||||
|
||||
// 找到了则让目标节点聚焦
|
||||
if (targetNode) {
|
||||
this.mindMap.renderer.moveNodeToCenter(targetNode)
|
||||
targetNode.active()
|
||||
}
|
||||
}
|
||||
|
||||
// 1.简单算法
|
||||
getFocusNodeBySimpleAlgorithm({
|
||||
currentActiveNode,
|
||||
currentActiveNodeRect,
|
||||
dir,
|
||||
checkNodeDis
|
||||
}) {
|
||||
// 遍历节点树
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
// 跳过当前聚焦的节点
|
||||
if (node === currentActiveNode) return
|
||||
// 当前遍历到的节点的位置信息
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
let match = false
|
||||
// 按下了左方向键
|
||||
if (dir === 'Left') {
|
||||
// 判断节点是否在当前节点的左侧
|
||||
match = right <= currentActiveNodeRect.left
|
||||
// 按下了右方向键
|
||||
} else if (dir === 'Right') {
|
||||
// 判断节点是否在当前节点的右侧
|
||||
match = left >= currentActiveNodeRect.right
|
||||
// 按下了上方向键
|
||||
} else if (dir === 'Up') {
|
||||
// 判断节点是否在当前节点的上面
|
||||
match = bottom <= currentActiveNodeRect.top
|
||||
// 按下了下方向键
|
||||
} else if (dir === 'Down') {
|
||||
// 判断节点是否在当前节点的下面
|
||||
match = top >= currentActiveNodeRect.bottom
|
||||
}
|
||||
// 符合要求,判断是否是最近的节点
|
||||
if (match) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 2.阴影算法
|
||||
getFocusNodeByShadowAlgorithm({
|
||||
currentActiveNode,
|
||||
currentActiveNodeRect,
|
||||
dir,
|
||||
checkNodeDis
|
||||
}) {
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (node === currentActiveNode) return
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
let match = false
|
||||
if (dir === 'Left') {
|
||||
match =
|
||||
left < currentActiveNodeRect.left &&
|
||||
top < currentActiveNodeRect.bottom &&
|
||||
bottom > currentActiveNodeRect.top
|
||||
} else if (dir === 'Right') {
|
||||
match =
|
||||
right > currentActiveNodeRect.right &&
|
||||
top < currentActiveNodeRect.bottom &&
|
||||
bottom > currentActiveNodeRect.top
|
||||
} else if (dir === 'Up') {
|
||||
match =
|
||||
top < currentActiveNodeRect.top &&
|
||||
left < currentActiveNodeRect.right &&
|
||||
right > currentActiveNodeRect.left
|
||||
} else if (dir === 'Down') {
|
||||
match =
|
||||
bottom > currentActiveNodeRect.bottom &&
|
||||
left < currentActiveNodeRect.right &&
|
||||
right > currentActiveNodeRect.left
|
||||
}
|
||||
if (match) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 3.区域算法
|
||||
getFocusNodeByAreaAlgorithm({
|
||||
currentActiveNode,
|
||||
currentActiveNodeRect,
|
||||
dir,
|
||||
checkNodeDis
|
||||
}) {
|
||||
// 当前聚焦节点的中心点
|
||||
let cX = (currentActiveNodeRect.right + currentActiveNodeRect.left) / 2
|
||||
let cY = (currentActiveNodeRect.bottom + currentActiveNodeRect.top) / 2
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (node === currentActiveNode) return
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
// 遍历到的节点的中心点
|
||||
let ccX = (right + left) / 2
|
||||
let ccY = (bottom + top) / 2
|
||||
// 节点的中心点坐标和当前聚焦节点的中心点坐标的差值
|
||||
let offsetX = ccX - cX
|
||||
let offsetY = ccY - cY
|
||||
if (offsetX === 0 && offsetY === 0) return
|
||||
let match = false
|
||||
if (dir === 'Left') {
|
||||
match = offsetX <= 0 && offsetX <= offsetY && offsetX <= -offsetY
|
||||
} else if (dir === 'Right') {
|
||||
match = offsetX > 0 && offsetX >= -offsetY && offsetX >= offsetY
|
||||
} else if (dir === 'Up') {
|
||||
match = offsetY <= 0 && offsetY < offsetX && offsetY < -offsetX
|
||||
} else if (dir === 'Down') {
|
||||
match = offsetY > 0 && -offsetY < offsetX && offsetY > offsetX
|
||||
}
|
||||
if (match) {
|
||||
checkNodeDis(rect, node)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取节点的位置信息
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 14:12:50
|
||||
* @Desc: 获取节点的位置信息
|
||||
*/
|
||||
getNodeRect(node) {
|
||||
let { scaleX, scaleY, translateX, translateY } =
|
||||
this.mindMap.draw.transform()
|
||||
@@ -213,7 +104,12 @@ class KeyboardNavigation {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取两个节点的距离
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 14:13:04
|
||||
* @Desc: 获取两个节点的距离
|
||||
*/
|
||||
getDistance(node1Rect, node2Rect) {
|
||||
let center1 = this.getCenter(node1Rect)
|
||||
let center2 = this.getCenter(node2Rect)
|
||||
@@ -222,7 +118,12 @@ class KeyboardNavigation {
|
||||
)
|
||||
}
|
||||
|
||||
// 获取节点的中心点
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 14:13:11
|
||||
* @Desc: 获取节点的中心点
|
||||
*/
|
||||
getCenter({ left, right, top, bottom }) {
|
||||
return {
|
||||
x: (left + right) / 2,
|
||||
@@ -230,7 +131,3 @@ class KeyboardNavigation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardNavigation.instanceName = 'keyboardNavigation'
|
||||
|
||||
export default KeyboardNavigation
|
||||
@@ -1,6 +1,11 @@
|
||||
// 小地图类
|
||||
class MiniMap {
|
||||
// 构造函数
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:00:45
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.isMousedown = false
|
||||
@@ -14,14 +19,59 @@ class MiniMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 计算小地图的渲染数据
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:00:43
|
||||
* @Desc: 获取小地图相关数据
|
||||
*/
|
||||
getMiniMap() {
|
||||
const svg = this.mindMap.svg
|
||||
const draw = this.mindMap.draw
|
||||
// 保存原始信息
|
||||
const origWidth = svg.width()
|
||||
const origHeight = svg.height()
|
||||
const origTransform = draw.transform()
|
||||
const elRect = this.mindMap.el.getBoundingClientRect()
|
||||
// 去除放大缩小的变换效果
|
||||
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
const clone = svg.clone()
|
||||
// 恢复原先的大小和变换信息
|
||||
svg.size(origWidth, origHeight)
|
||||
draw.transform(origTransform)
|
||||
|
||||
return {
|
||||
svg: clone, // 思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组)
|
||||
svgHTML: clone.svg(), // svg字符串
|
||||
rect: {
|
||||
...rect, // 思维导图图形未缩放时的位置尺寸等信息
|
||||
ratio: rect.width / rect.height // 思维导图图形的宽高比
|
||||
},
|
||||
origWidth, // 画布宽度
|
||||
origHeight, // 画布高度
|
||||
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
|
||||
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:05:51
|
||||
* @Desc: 计算小地图的渲染数据
|
||||
* boxWidth:小地图容器的宽度
|
||||
* boxHeight:小地图容器的高度
|
||||
*/
|
||||
calculationMiniMap(boxWidth, boxHeight) {
|
||||
let { svgHTML, rect, origWidth, origHeight, scaleX, scaleY } =
|
||||
this.mindMap.getSvgData()
|
||||
this.getMiniMap()
|
||||
// 计算数据
|
||||
let boxRatio = boxWidth / boxHeight
|
||||
let actWidth = 0
|
||||
@@ -74,7 +124,12 @@ class MiniMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 小地图鼠标按下事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:22:40
|
||||
* @Desc: 小地图鼠标按下事件
|
||||
*/
|
||||
onMousedown(e) {
|
||||
this.isMousedown = true
|
||||
this.mousedownPos = {
|
||||
@@ -89,7 +144,12 @@ class MiniMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 小地图鼠标移动事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:22:55
|
||||
* @Desc: 小地图鼠标移动事件
|
||||
*/
|
||||
onMousemove(e, sensitivityNum = 5) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
@@ -101,12 +161,15 @@ class MiniMap {
|
||||
this.mindMap.view.translateYTo(oy * sensitivityNum + this.startViewPos.y)
|
||||
}
|
||||
|
||||
// 小地图鼠标松开事件
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:23:01
|
||||
* @Desc: 小地图鼠标松开事件
|
||||
*/
|
||||
onMouseup() {
|
||||
this.isMousedown = false
|
||||
}
|
||||
}
|
||||
|
||||
MiniMap.instanceName = 'miniMap'
|
||||
|
||||
export default MiniMap
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import Style from './Style'
|
||||
import Shape from './Shape'
|
||||
import { resizeImgSize, asyncRun, measureText } from './utils'
|
||||
import { Image, SVG, Circle, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js'
|
||||
import { resizeImgSize, asyncRun } from './utils'
|
||||
import { Image, SVG, Circle, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
||||
import btnsSvg from './svg/btns'
|
||||
import iconsSvg from './svg/icons'
|
||||
|
||||
// 节点类
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 11:26:00
|
||||
* @Desc: 节点类
|
||||
*/
|
||||
class Node {
|
||||
// 构造函数
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 11:26:17
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
// 节点数据
|
||||
this.nodeData = this.handleData(opt.data || {})
|
||||
@@ -110,8 +118,11 @@ class Node {
|
||||
this._top = val
|
||||
}
|
||||
|
||||
// 更新主题配置
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-12 07:40:47
|
||||
* @Desc: 更新主题配置
|
||||
*/
|
||||
updateThemeConfig() {
|
||||
// 主题配置
|
||||
this.themeConfig = this.mindMap.themeConfig
|
||||
@@ -119,8 +130,11 @@ class Node {
|
||||
this.style.updateThemeConfig(this.themeConfig)
|
||||
}
|
||||
|
||||
// 复位部分布局时会重新设置的数据
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-05 23:11:39
|
||||
* @Desc: 复位部分布局时会重新设置的数据
|
||||
*/
|
||||
reset() {
|
||||
this.children = []
|
||||
this.parent = null
|
||||
@@ -130,8 +144,11 @@ class Node {
|
||||
this.top = 0
|
||||
}
|
||||
|
||||
// 处理数据
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 10:12:31
|
||||
* @Desc: 处理数据
|
||||
*/
|
||||
handleData(data) {
|
||||
data.data.expand = data.data.expand === false ? false : true
|
||||
data.data.isActive = data.data.isActive === true ? true : false
|
||||
@@ -139,14 +156,22 @@ class Node {
|
||||
return data
|
||||
}
|
||||
|
||||
// 检查节点是否存在自定义数据
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-02 19:53:40
|
||||
* @Desc: 检查节点是否存在自定义数据
|
||||
*/
|
||||
hasCustomPosition() {
|
||||
return this.customLeft !== undefined && this.customTop !== undefined
|
||||
}
|
||||
|
||||
// 检查节点是否存在自定义位置的祖先节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-04 09:06:56
|
||||
* @Desc: 检查节点是否存在自定义位置的祖先节点
|
||||
*/
|
||||
ancestorHasCustomPosition() {
|
||||
let node = this
|
||||
while (node) {
|
||||
@@ -158,14 +183,21 @@ class Node {
|
||||
return false
|
||||
}
|
||||
|
||||
// 添加子节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 15:55:04
|
||||
* @Desc: 添加子节点
|
||||
*/
|
||||
addChildren(node) {
|
||||
this.children.push(node)
|
||||
}
|
||||
|
||||
// 创建节点的各个内容对象数据
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-06 22:08:09
|
||||
* @Desc: 创建节点的各个内容对象数据
|
||||
*/
|
||||
createNodeData() {
|
||||
this._imgData = this.createImgNode()
|
||||
this._iconData = this.createIconNode()
|
||||
@@ -176,8 +208,11 @@ class Node {
|
||||
this.createGeneralizationNode()
|
||||
}
|
||||
|
||||
// 解绑所有事件
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 09:20:02
|
||||
* @Desc: 解绑所有事件
|
||||
*/
|
||||
removeAllEvent() {
|
||||
if (this._noteData) {
|
||||
this._noteData.node.off(['mouseover', 'mouseout'])
|
||||
@@ -191,15 +226,16 @@ class Node {
|
||||
'dblclick',
|
||||
'contextmenu',
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'mouseenter',
|
||||
'mouseleave'
|
||||
'mouseup'
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// 移除节点内容
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-07 21:27:24
|
||||
* @Desc: 移除节点内容
|
||||
*/
|
||||
removeAllNode() {
|
||||
// 节点内的内容
|
||||
;[
|
||||
@@ -233,8 +269,12 @@ class Node {
|
||||
this.removeGeneralization()
|
||||
}
|
||||
|
||||
// 计算节点的宽高
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-09 09:46:23
|
||||
* @Desc: 计算节点的宽高
|
||||
*/
|
||||
getSize() {
|
||||
this.removeAllNode()
|
||||
this.createNodeData()
|
||||
@@ -246,8 +286,12 @@ class Node {
|
||||
return changed
|
||||
}
|
||||
|
||||
// 计算节点尺寸信息
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-06 14:52:17
|
||||
* @Desc: 计算节点尺寸信息
|
||||
*/
|
||||
getNodeRect() {
|
||||
// 宽高
|
||||
let imgContentWidth = 0
|
||||
@@ -314,8 +358,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建图片节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-09 14:06:17
|
||||
* @Desc: 创建图片节点
|
||||
*/
|
||||
createImgNode() {
|
||||
let img = this.nodeData.data.image
|
||||
if (!img) {
|
||||
@@ -336,8 +384,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取图片显示宽高
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-09 10:12:51
|
||||
* @Desc: 获取图片显示宽高
|
||||
*/
|
||||
getImgShowSize() {
|
||||
return resizeImgSize(
|
||||
this.nodeData.data.imageSize.width,
|
||||
@@ -347,8 +399,12 @@ class Node {
|
||||
)
|
||||
}
|
||||
|
||||
// 创建icon节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-09 14:10:48
|
||||
* @Desc: 创建icon节点
|
||||
*/
|
||||
createIconNode() {
|
||||
let _data = this.nodeData.data
|
||||
if (!_data.icon || _data.icon.length <= 0) {
|
||||
@@ -364,86 +420,31 @@ class Node {
|
||||
})
|
||||
}
|
||||
|
||||
// 创建富文本节点
|
||||
createRichTextNode() {
|
||||
let g = new G()
|
||||
let html = `<div>${this.nodeData.data.text}</div>`
|
||||
let div = document.createElement('div')
|
||||
div.innerHTML = html
|
||||
div.style.cssText = `position: fixed; left: -999999px;`
|
||||
let el = div.children[0]
|
||||
el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||
el.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
|
||||
this.mindMap.el.appendChild(div)
|
||||
let { width, height } = el.getBoundingClientRect()
|
||||
width = Math.ceil(width)
|
||||
height = Math.ceil(height)
|
||||
g.attr('data-width', width)
|
||||
g.attr('data-height', height)
|
||||
html = div.innerHTML
|
||||
this.mindMap.el.removeChild(div)
|
||||
let foreignObject = new ForeignObject()
|
||||
foreignObject.width(width)
|
||||
foreignObject.height(height)
|
||||
foreignObject.add(SVG(html))
|
||||
g.add(foreignObject)
|
||||
return {
|
||||
node: g,
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
|
||||
// 创建文本节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-09 14:08:56
|
||||
* @Desc: 创建文本节点
|
||||
*/
|
||||
createTextNode() {
|
||||
if (this.nodeData.data.richText) {
|
||||
return this.createRichTextNode()
|
||||
}
|
||||
let g = new G()
|
||||
let fontSize = this.getStyle(
|
||||
'fontSize',
|
||||
false,
|
||||
this.isRoot,
|
||||
this.nodeData.data.isActive
|
||||
)
|
||||
let lineHeight = this.getStyle(
|
||||
'lineHeight',
|
||||
false,
|
||||
this.isRoot,
|
||||
this.nodeData.data.isActive
|
||||
)
|
||||
// 文本超长自动换行
|
||||
let textStyle = this.style.getTextFontStyle()
|
||||
let textArr = this.nodeData.data.text.split(/\n/gim)
|
||||
let maxWidth = this.mindMap.opt.textAutoWrapWidth
|
||||
textArr.forEach((item, index) => {
|
||||
let arr = item.split('')
|
||||
let lines = []
|
||||
let line = []
|
||||
while(arr.length) {
|
||||
line.push(arr.shift())
|
||||
let text = line.join('')
|
||||
if (measureText(text, textStyle).width >= maxWidth) {
|
||||
lines.push(text)
|
||||
line = []
|
||||
}
|
||||
}
|
||||
if (line.length > 0) {
|
||||
lines.push(line.join(''))
|
||||
}
|
||||
textArr[index] = lines.join('\n')
|
||||
})
|
||||
textArr = textArr.join('\n').split(/\n/gim)
|
||||
textArr.forEach((item, index) => {
|
||||
this.nodeData.data.text.split(/\n/gim).forEach((item, index) => {
|
||||
let node = new Text().text(item)
|
||||
this.style.text(node)
|
||||
node.y(fontSize * lineHeight * index)
|
||||
g.add(node)
|
||||
})
|
||||
let { width, height } = g.bbox()
|
||||
width = Math.ceil(width)
|
||||
height = Math.ceil(height)
|
||||
g.attr('data-width', width)
|
||||
g.attr('data-height', height)
|
||||
return {
|
||||
node: g,
|
||||
width,
|
||||
@@ -451,8 +452,11 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建超链接节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 15:28:54
|
||||
* @Desc: 创建超链接节点
|
||||
*/
|
||||
createHyperlinkNode() {
|
||||
let { hyperlink, hyperlinkTitle } = this.nodeData.data
|
||||
if (!hyperlink) {
|
||||
@@ -482,8 +486,11 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建标签节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 19:49:15
|
||||
* @Desc: 创建标签节点
|
||||
*/
|
||||
createTagNode() {
|
||||
let tagData = this.nodeData.data.tag
|
||||
if (!tagData || tagData.length <= 0) {
|
||||
@@ -509,8 +516,11 @@ class Node {
|
||||
return nodes
|
||||
}
|
||||
|
||||
// 创建备注节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 21:19:36
|
||||
* @Desc: 创建备注节点
|
||||
*/
|
||||
createNoteNode() {
|
||||
if (!this.nodeData.data.note) {
|
||||
return null
|
||||
@@ -567,17 +577,22 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点形状
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 22:02:07
|
||||
* @Desc: 获取节点形状
|
||||
*/
|
||||
getShape() {
|
||||
// 节点使用功能横线风格的话不支持设置形状,直接使用默认的矩形
|
||||
return this.themeConfig.nodeUseLineStyle
|
||||
? 'rectangle'
|
||||
: this.style.getStyle('shape', false, false)
|
||||
return this.style.getStyle('shape', false, false)
|
||||
}
|
||||
|
||||
// 定位节点内容
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-09 11:10:11
|
||||
* @Desc: 定位节点内容
|
||||
*/
|
||||
layout() {
|
||||
let { width, textContentItemMargin } = this
|
||||
let { paddingY } = this.getPaddingVale()
|
||||
@@ -621,7 +636,6 @@ class Node {
|
||||
}
|
||||
// 文字
|
||||
if (this._textData) {
|
||||
this._textData.node.attr('data-offsetx', textContentOffsetX)
|
||||
this._textData.node.x(textContentOffsetX).y(0)
|
||||
textContentNested.add(this._textData.node)
|
||||
textContentOffsetX += this._textData.width + textContentItemMargin
|
||||
@@ -672,34 +686,13 @@ class Node {
|
||||
this.active(e)
|
||||
})
|
||||
this.group.on('mousedown', e => {
|
||||
if (!this.isRoot) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
// 多选和取消多选
|
||||
if (e.ctrlKey) {
|
||||
let isActive = this.nodeData.data.isActive
|
||||
this.mindMap.renderer.setNodeActive(this, !isActive)
|
||||
this.mindMap.renderer[isActive ? 'removeActiveNode' : 'addActiveNode'](this)
|
||||
this.mindMap.emit(
|
||||
'node_active',
|
||||
isActive ? null : this,
|
||||
this.mindMap.renderer.activeNodeList
|
||||
)
|
||||
}
|
||||
e.stopPropagation()
|
||||
this.mindMap.emit('node_mousedown', this, e)
|
||||
})
|
||||
this.group.on('mouseup', e => {
|
||||
if (!this.isRoot) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
e.stopPropagation()
|
||||
this.mindMap.emit('node_mouseup', this, e)
|
||||
})
|
||||
this.group.on('mouseenter', e => {
|
||||
this.mindMap.emit('node_mouseenter', this, e)
|
||||
})
|
||||
this.group.on('mouseleave', e => {
|
||||
this.mindMap.emit('node_mouseleave', this, e)
|
||||
})
|
||||
// 双击事件
|
||||
this.group.on('dblclick', e => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
@@ -710,8 +703,7 @@ class Node {
|
||||
})
|
||||
// 右键菜单事件
|
||||
this.group.on('contextmenu', e => {
|
||||
// 按住ctrl键点击鼠标左键不知为何触发的是contextmenu事件
|
||||
if (this.mindMap.opt.readonly || this.isGeneralization || e.ctrlKey) {
|
||||
if (this.mindMap.opt.readonly || this.isGeneralization) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
@@ -724,8 +716,11 @@ class Node {
|
||||
})
|
||||
}
|
||||
|
||||
// 激活节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 16:44:22
|
||||
* @Desc: 激活节点
|
||||
*/
|
||||
active(e) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
@@ -741,8 +736,11 @@ class Node {
|
||||
this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
|
||||
}
|
||||
|
||||
// 渲染节点到画布,会移除旧的,创建新的
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 20:20:09
|
||||
* @Desc: 渲染节点到画布,会移除旧的,创建新的
|
||||
*/
|
||||
renderNode() {
|
||||
// 连线
|
||||
this.renderLine()
|
||||
@@ -752,8 +750,11 @@ class Node {
|
||||
this.layout()
|
||||
}
|
||||
|
||||
// 更新节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 22:47:01
|
||||
* @Desc: 更新节点
|
||||
*/
|
||||
update(layout = false) {
|
||||
if (!this.group) {
|
||||
return
|
||||
@@ -772,20 +773,18 @@ class Node {
|
||||
if (!layout) {
|
||||
this.group
|
||||
.animate(300)
|
||||
.translate(
|
||||
this.left - t.translateX,
|
||||
this.top - t.translateY
|
||||
)
|
||||
.translate(this.left - t.translateX, this.top - t.translateY)
|
||||
} else {
|
||||
this.group.translate(
|
||||
this.left - t.translateX,
|
||||
this.top - t.translateY
|
||||
)
|
||||
this.group.translate(this.left - t.translateX, this.top - t.translateY)
|
||||
}
|
||||
}
|
||||
|
||||
// 递归渲染
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 13:55:58
|
||||
* @Desc: 递归渲染
|
||||
*/
|
||||
render(callback = () => {}) {
|
||||
// 节点
|
||||
if (this.initRender) {
|
||||
@@ -822,14 +821,15 @@ class Node {
|
||||
if (this.nodeData.inserting) {
|
||||
delete this.nodeData.inserting
|
||||
this.active()
|
||||
setTimeout(() => {
|
||||
this.mindMap.emit('node_dblclick', this)
|
||||
}, 0)
|
||||
this.mindMap.emit('node_dblclick', this)
|
||||
}
|
||||
}
|
||||
|
||||
// 递归删除
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 09:24:55
|
||||
* @Desc: 递归删除
|
||||
*/
|
||||
remove() {
|
||||
this.initRender = true
|
||||
this.removeAllEvent()
|
||||
@@ -847,8 +847,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 18:39:14
|
||||
* @Desc: 隐藏节点
|
||||
*/
|
||||
hide() {
|
||||
this.group.hide()
|
||||
this.hideGeneralization()
|
||||
@@ -868,8 +872,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 显示节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-23 18:39:14
|
||||
* @Desc: 显示节点
|
||||
*/
|
||||
show() {
|
||||
if (!this.group) {
|
||||
return
|
||||
@@ -892,8 +900,11 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 连线
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-10 22:01:53
|
||||
* @Desc: 连线
|
||||
*/
|
||||
renderLine(deep = false) {
|
||||
if (this.nodeData.data.expand === false) {
|
||||
return
|
||||
@@ -929,8 +940,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置连线样式
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: flydreame
|
||||
* @Date: 2022-09-17 12:41:29
|
||||
* @Desc: 设置连线样式
|
||||
*/
|
||||
styleLine(line, node) {
|
||||
let width =
|
||||
node.getSelfInhertStyle('lineWidth') || node.getStyle('lineWidth', true)
|
||||
@@ -946,8 +961,11 @@ class Node {
|
||||
})
|
||||
}
|
||||
|
||||
// 移除连线
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 16:40:21
|
||||
* @Desc: 移除连线
|
||||
*/
|
||||
removeLine() {
|
||||
this._lines.forEach(line => {
|
||||
line.remove()
|
||||
@@ -955,14 +973,21 @@ class Node {
|
||||
this._lines = []
|
||||
}
|
||||
|
||||
// 检查是否存在概要
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-01 09:27:30
|
||||
* @Desc: 检查是否存在概要
|
||||
*/
|
||||
checkHasGeneralization() {
|
||||
return !!this.nodeData.data.generalization
|
||||
}
|
||||
|
||||
// 创建概要节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-07-31 09:41:28
|
||||
* @Desc: 创建概要节点
|
||||
*/
|
||||
createGeneralizationNode() {
|
||||
if (this.isGeneralization || !this.checkHasGeneralization()) {
|
||||
return
|
||||
@@ -990,15 +1015,22 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新概要节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-01 15:38:52
|
||||
* @Desc: 更新概要节点
|
||||
*/
|
||||
updateGeneralization() {
|
||||
this.removeGeneralization()
|
||||
this.createGeneralizationNode()
|
||||
}
|
||||
|
||||
// 渲染概要节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-07-30 08:35:51
|
||||
* @Desc: 渲染概要节点
|
||||
*/
|
||||
renderGeneralization() {
|
||||
if (this.isGeneralization) {
|
||||
return
|
||||
@@ -1023,8 +1055,11 @@ class Node {
|
||||
this._generalizationNode.render()
|
||||
}
|
||||
|
||||
// 删除概要节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-07-30 13:11:27
|
||||
* @Desc: 删除概要节点
|
||||
*/
|
||||
removeGeneralization() {
|
||||
if (this._generalizationLine) {
|
||||
this._generalizationLine.remove()
|
||||
@@ -1044,8 +1079,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏概要节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-01 09:56:46
|
||||
* @Desc: 隐藏概要节点
|
||||
*/
|
||||
hideGeneralization() {
|
||||
if (this._generalizationLine) {
|
||||
this._generalizationLine.hide()
|
||||
@@ -1055,8 +1094,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 显示概要节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-01 09:57:42
|
||||
* @Desc: 显示概要节点
|
||||
*/
|
||||
showGeneralization() {
|
||||
if (this._generalizationLine) {
|
||||
this._generalizationLine.show()
|
||||
@@ -1066,8 +1109,11 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建或更新展开收缩按钮内容
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 17:59:14
|
||||
* @Desc: 创建或更新展开收缩按钮内容
|
||||
*/
|
||||
updateExpandBtnNode() {
|
||||
if (this._expandBtn) {
|
||||
this._expandBtn.clear()
|
||||
@@ -1086,8 +1132,12 @@ class Node {
|
||||
if (this._expandBtn) this._expandBtn.add(fillNode).add(node)
|
||||
}
|
||||
|
||||
// 更新展开收缩按钮位置
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-12 18:18:13
|
||||
* @Desc: 更新展开收缩按钮位置
|
||||
*/
|
||||
updateExpandBtnPos() {
|
||||
if (!this._expandBtn) {
|
||||
return
|
||||
@@ -1095,8 +1145,11 @@ class Node {
|
||||
this.renderer.layout.renderExpandBtn(this, this._expandBtn)
|
||||
}
|
||||
|
||||
// 展开收缩按钮
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 19:47:01
|
||||
* @Desc: 展开收缩按钮
|
||||
*/
|
||||
renderExpandBtn() {
|
||||
if (
|
||||
!this.nodeData.children ||
|
||||
@@ -1133,8 +1186,11 @@ class Node {
|
||||
this.updateExpandBtnPos()
|
||||
}
|
||||
|
||||
// 移除展开收缩按钮
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 13:26:00
|
||||
* @Desc: 移除展开收缩按钮
|
||||
*/
|
||||
removeExpandBtn() {
|
||||
if (this._expandBtn) {
|
||||
this._expandBtn.off(['mouseover', 'mouseout', 'click'])
|
||||
@@ -1144,8 +1200,12 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 检测当前节点是否是某个节点的祖先节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 09:51:37
|
||||
* @Desc: 检测当前节点是否是某个节点的祖先节点
|
||||
*/
|
||||
isParent(node) {
|
||||
if (this === node) {
|
||||
return false
|
||||
@@ -1160,8 +1220,12 @@ class Node {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检测当前节点是否是某个节点的兄弟节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 10:32:34
|
||||
* @Desc: 检测当前节点是否是某个节点的兄弟节点
|
||||
*/
|
||||
isBrother(node) {
|
||||
if (!this.parent || this === node) {
|
||||
return false
|
||||
@@ -1171,8 +1235,11 @@ class Node {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取padding值
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 22:51:57
|
||||
* @Desc: 获取padding值
|
||||
*/
|
||||
getPaddingVale() {
|
||||
return {
|
||||
paddingX: this.getStyle('paddingX', true, this.nodeData.data.isActive),
|
||||
@@ -1180,21 +1247,32 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取某个样式
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 21:48:49
|
||||
* @Desc: 获取某个样式
|
||||
*/
|
||||
getStyle(prop, root, isActive) {
|
||||
let v = this.style.merge(prop, root, isActive)
|
||||
return v === undefined ? '' : v
|
||||
}
|
||||
|
||||
// 获取自定义样式
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: flydreame
|
||||
* @Date: 2022-09-17 11:21:15
|
||||
* @Desc: 获取自定义样式
|
||||
*/
|
||||
getSelfStyle(prop) {
|
||||
return this.style.getSelfStyle(prop)
|
||||
}
|
||||
|
||||
// 获取最近一个存在自身自定义样式的祖先节点的自定义样式
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: flydreame
|
||||
* @Date: 2022-09-17 11:21:26
|
||||
* @Desc: 获取最近一个存在自身自定义样式的祖先节点的自定义样式
|
||||
*/
|
||||
getParentSelfStyle(prop) {
|
||||
if (this.parent) {
|
||||
return (
|
||||
@@ -1204,8 +1282,12 @@ class Node {
|
||||
return null
|
||||
}
|
||||
|
||||
// 获取自身可继承的自定义样式
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: flydreame
|
||||
* @Date: 2022-09-17 12:15:30
|
||||
* @Desc: 获取自身可继承的自定义样式
|
||||
*/
|
||||
getSelfInhertStyle(prop) {
|
||||
return (
|
||||
this.getSelfStyle(prop) || // 自身
|
||||
@@ -1213,62 +1295,93 @@ class Node {
|
||||
) // 父级
|
||||
}
|
||||
|
||||
// 修改某个样式
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 22:18:07
|
||||
* @Desc: 修改某个样式
|
||||
*/
|
||||
setStyle(prop, value, isActive) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value, isActive)
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-22 22:04:02
|
||||
* @Desc: 获取数据
|
||||
*/
|
||||
getData(key) {
|
||||
return key ? this.nodeData.data[key] || '' : this.nodeData.data
|
||||
}
|
||||
|
||||
// 设置数据
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-22 22:12:01
|
||||
* @Desc: 设置数据
|
||||
*/
|
||||
setData(data = {}) {
|
||||
this.mindMap.execCommand('SET_NODE_DATA', this, data)
|
||||
}
|
||||
|
||||
// 设置文本
|
||||
|
||||
setText(text, richText) {
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', this, text, richText)
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:41:28
|
||||
* @Desc: 设置文本
|
||||
*/
|
||||
setText(text) {
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', this, text)
|
||||
}
|
||||
|
||||
// 设置图片
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:42:19
|
||||
* @Desc: 设置图片
|
||||
*/
|
||||
setImage(imgData) {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', this, imgData)
|
||||
}
|
||||
|
||||
// 设置图标
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:47:29
|
||||
* @Desc: 设置图标
|
||||
*/
|
||||
setIcon(icons) {
|
||||
this.mindMap.execCommand('SET_NODE_ICON', this, icons)
|
||||
}
|
||||
|
||||
// 设置超链接
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:50:41
|
||||
* @Desc: 设置超链接
|
||||
*/
|
||||
setHyperlink(link, title) {
|
||||
this.mindMap.execCommand('SET_NODE_HYPERLINK', this, link, title)
|
||||
}
|
||||
|
||||
// 设置备注
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:53:24
|
||||
* @Desc: 设置备注
|
||||
*/
|
||||
setNote(note) {
|
||||
this.mindMap.execCommand('SET_NODE_NOTE', this, note)
|
||||
}
|
||||
|
||||
// 设置标签
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:55:08
|
||||
* @Desc: 设置标签
|
||||
*/
|
||||
setTag(tag) {
|
||||
this.mindMap.execCommand('SET_NODE_TAG', this, tag)
|
||||
}
|
||||
|
||||
// 设置形状
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 21:47:45
|
||||
* @Desc: 设置形状
|
||||
*/
|
||||
setShape(shape) {
|
||||
this.mindMap.execCommand('SET_NODE_SHAPE', this, shape)
|
||||
}
|
||||
|
||||
@@ -20,11 +20,19 @@ const layouts = {
|
||||
organizationStructure: OrganizationStructure
|
||||
}
|
||||
|
||||
// 渲染
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-08 16:25:07
|
||||
* @Desc: 渲染
|
||||
*/
|
||||
class Render {
|
||||
// 构造函数
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-08 16:25:32
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
@@ -50,8 +58,12 @@ class Render {
|
||||
this.registerShortcutKeys()
|
||||
}
|
||||
|
||||
// 设置布局结构
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-13 16:20:07
|
||||
* @Desc: 设置布局结构
|
||||
*/
|
||||
setLayout() {
|
||||
this.layout = new (
|
||||
layouts[this.mindMap.opt.layout]
|
||||
@@ -60,8 +72,11 @@ class Render {
|
||||
)(this)
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 10:34:06
|
||||
* @Desc: 绑定事件
|
||||
*/
|
||||
bindEvent() {
|
||||
// 点击事件
|
||||
this.mindMap.on('draw_click', () => {
|
||||
@@ -72,8 +87,11 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 注册命令
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:19:06
|
||||
* @Desc: 注册命令
|
||||
*/
|
||||
registerCommands() {
|
||||
// 全选
|
||||
this.selectAll = this.selectAll.bind(this)
|
||||
@@ -174,8 +192,11 @@ class Render {
|
||||
this.mindMap.command.add('SET_NODE_SHAPE', this.setNodeShape)
|
||||
}
|
||||
|
||||
// 注册快捷键
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 16:55:44
|
||||
* @Desc: 注册快捷键
|
||||
*/
|
||||
registerShortcutKeys() {
|
||||
// 插入下级节点
|
||||
this.mindMap.keyCommand.addShortcut('Tab', () => {
|
||||
@@ -219,8 +240,12 @@ class Render {
|
||||
// 复制节点、剪切节点、粘贴节点的快捷键需开发者自行注册实现,可参考demo
|
||||
}
|
||||
|
||||
// 开启文字编辑,会禁用回车键和删除键相关快捷键防止冲突
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-05-09 10:43:52
|
||||
* @Desc: 开启文字编辑,会禁用回车键和删除键相关快捷键防止冲突
|
||||
*/
|
||||
startTextEdit() {
|
||||
this.mindMap.keyCommand.save()
|
||||
// this.mindMap.keyCommand.removeShortcut('Del|Backspace')
|
||||
@@ -228,8 +253,12 @@ class Render {
|
||||
// this.mindMap.keyCommand.removeShortcut('Enter', this.insertNodeWrap)
|
||||
}
|
||||
|
||||
// 结束文字编辑,会恢复回车键和删除键相关快捷键
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-05-09 10:45:11
|
||||
* @Desc: 结束文字编辑,会恢复回车键和删除键相关快捷键
|
||||
*/
|
||||
endTextEdit() {
|
||||
this.mindMap.keyCommand.restore()
|
||||
// this.mindMap.keyCommand.addShortcut('Del|Backspace', this.removeNodeWrap)
|
||||
@@ -237,9 +266,13 @@ class Render {
|
||||
// this.mindMap.keyCommand.addShortcut('Enter', this.insertNodeWrap)
|
||||
}
|
||||
|
||||
// 渲染
|
||||
|
||||
render(callback = () => {}) {
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-08 16:27:55
|
||||
* @Desc: 渲染
|
||||
*/
|
||||
render() {
|
||||
if (this.reRender) {
|
||||
this.clearActive()
|
||||
}
|
||||
@@ -247,14 +280,16 @@ class Render {
|
||||
this.root = root
|
||||
this.root.render(() => {
|
||||
this.mindMap.emit('node_tree_render_end')
|
||||
callback()
|
||||
})
|
||||
})
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
}
|
||||
|
||||
// 清除当前激活的节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-12 22:45:01
|
||||
* @Desc: 清除当前激活的节点
|
||||
*/
|
||||
clearActive() {
|
||||
this.activeNodeList.forEach(item => {
|
||||
this.setNodeActive(item, false)
|
||||
@@ -262,8 +297,11 @@ class Render {
|
||||
this.activeNodeList = []
|
||||
}
|
||||
|
||||
// 清除当前所有激活节点,并会触发事件
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-03 23:14:34
|
||||
* @Desc: 清除当前所有激活节点,并会触发事件
|
||||
*/
|
||||
clearAllActive() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -272,8 +310,11 @@ class Render {
|
||||
this.mindMap.emit('node_active', null, [])
|
||||
}
|
||||
|
||||
// 添加节点到激活列表里
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:54:00
|
||||
* @Desc: 添加节点到激活列表里
|
||||
*/
|
||||
addActiveNode(node) {
|
||||
let index = this.findActiveNodeIndex(node)
|
||||
if (index === -1) {
|
||||
@@ -281,8 +322,11 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 在激活列表里移除某个节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 10:04:04
|
||||
* @Desc: 在激活列表里移除某个节点
|
||||
*/
|
||||
removeActiveNode(node) {
|
||||
let index = this.findActiveNodeIndex(node)
|
||||
if (index === -1) {
|
||||
@@ -291,16 +335,22 @@ class Render {
|
||||
this.activeNodeList.splice(index, 1)
|
||||
}
|
||||
|
||||
// 检索某个节点在激活列表里的索引
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:55:23
|
||||
* @Desc: 检索某个节点在激活列表里的索引
|
||||
*/
|
||||
findActiveNodeIndex(node) {
|
||||
return this.activeNodeList.findIndex(item => {
|
||||
return item === node
|
||||
})
|
||||
}
|
||||
|
||||
// 获取节点在同级里的索引位置
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:46:08
|
||||
* @Desc: 获取节点在同级里的索引位置
|
||||
*/
|
||||
getNodeIndex(node) {
|
||||
return node.parent
|
||||
? node.parent.children.findIndex(item => {
|
||||
@@ -309,8 +359,11 @@ class Render {
|
||||
: 0
|
||||
}
|
||||
|
||||
// 全选
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-04 23:54:52
|
||||
* @Desc: 全选
|
||||
*/
|
||||
selectAll() {
|
||||
walk(
|
||||
this.root,
|
||||
@@ -331,8 +384,11 @@ class Render {
|
||||
)
|
||||
}
|
||||
|
||||
// 回退
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 22:34:12
|
||||
* @Desc: 回退
|
||||
*/
|
||||
back(step) {
|
||||
this.clearAllActive()
|
||||
let data = this.mindMap.command.back(step)
|
||||
@@ -342,8 +398,12 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 前进
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-12 10:44:51
|
||||
* @Desc: 前进
|
||||
*/
|
||||
forward(step) {
|
||||
this.clearAllActive()
|
||||
let data = this.mindMap.command.forward(step)
|
||||
@@ -353,36 +413,29 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 规范指定节点数据
|
||||
formatAppointNodes(appointNodes) {
|
||||
if (!appointNodes) return []
|
||||
return Array.isArray(appointNodes) ? appointNodes: [appointNodes]
|
||||
}
|
||||
|
||||
// 插入同级节点,多个节点只会操作第一个节点
|
||||
|
||||
insertNode(openEdit = true, appointNodes = [], appointData = null) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:19:54
|
||||
* @Desc: 插入同级节点,多个节点只会操作第一个节点
|
||||
*/
|
||||
insertNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
let first = list[0]
|
||||
let first = this.activeNodeList[0]
|
||||
if (first.isRoot) {
|
||||
this.insertChildNode(openEdit, appointNodes, appointData)
|
||||
this.insertChildNode()
|
||||
} else {
|
||||
let text = first.layerIndex === 1 ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||||
let text = first.layerIndex === 1 ? '二级节点' : '分支主题'
|
||||
if (first.layerIndex === 1) {
|
||||
first.parent.initRender = true
|
||||
}
|
||||
let index = this.getNodeIndex(first)
|
||||
first.parent.nodeData.children.splice(index + 1, 0, {
|
||||
inserting: openEdit,
|
||||
inserting: true,
|
||||
data: {
|
||||
text: text,
|
||||
expand: true,
|
||||
...(appointData || {})
|
||||
expand: true
|
||||
},
|
||||
children: []
|
||||
})
|
||||
@@ -390,26 +443,25 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 插入子节点
|
||||
|
||||
insertChildNode(openEdit = true, appointNodes = [], appointData = null) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:31:02
|
||||
* @Desc: 插入子节点
|
||||
*/
|
||||
insertChildNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
list.forEach(node => {
|
||||
this.activeNodeList.forEach(node => {
|
||||
if (!node.nodeData.children) {
|
||||
node.nodeData.children = []
|
||||
}
|
||||
let text = node.isRoot ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||||
let text = node.isRoot ? '二级节点' : '分支主题'
|
||||
node.nodeData.children.push({
|
||||
inserting: openEdit,
|
||||
inserting: true,
|
||||
data: {
|
||||
text: text,
|
||||
expand: true,
|
||||
...(appointData || {})
|
||||
expand: true
|
||||
},
|
||||
children: []
|
||||
})
|
||||
@@ -425,8 +477,11 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 上移节点,多个节点只会操作第一个节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-14 23:34:14
|
||||
* @Desc: 上移节点,多个节点只会操作第一个节点
|
||||
*/
|
||||
upNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -453,8 +508,11 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 下移节点,多个节点只会操作第一个节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-14 23:34:18
|
||||
* @Desc: 下移节点,多个节点只会操作第一个节点
|
||||
*/
|
||||
downNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -481,129 +539,127 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 将节点移动到另一个节点的前面
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 10:51:34
|
||||
* @Desc: 将节点移动到另一个节点的前面
|
||||
*/
|
||||
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 => {
|
||||
let parent = node.parent
|
||||
let childList = parent.children
|
||||
// 要移动节点的索引
|
||||
let index = childList.findIndex(item => {
|
||||
return item === node
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
if (index === -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 => {
|
||||
// 目标节点的索引
|
||||
let existIndex = childList.findIndex(item => {
|
||||
return item === exist
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
}
|
||||
existBorthers.splice(existIndex, 0, node)
|
||||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render(() => {
|
||||
if (nodeLayerChanged) {
|
||||
node.getSize()
|
||||
node.renderNode()
|
||||
}
|
||||
})
|
||||
// 当前节点在目标节点前面
|
||||
if (index < existIndex) {
|
||||
existIndex = existIndex - 1
|
||||
}
|
||||
// 节点实例
|
||||
childList.splice(index, 1)
|
||||
childList.splice(existIndex, 0, node)
|
||||
// 节点数据
|
||||
parent.nodeData.children.splice(index, 1)
|
||||
parent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 将节点移动到另一个节点的后面
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-25 10:51:34
|
||||
* @Desc: 将节点移动到另一个节点的后面
|
||||
*/
|
||||
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 => {
|
||||
let parent = node.parent
|
||||
let childList = parent.children
|
||||
// 要移动节点的索引
|
||||
let index = childList.findIndex(item => {
|
||||
return item === node
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
if (index === -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 => {
|
||||
// 目标节点的索引
|
||||
let existIndex = childList.findIndex(item => {
|
||||
return item === exist
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
}
|
||||
existIndex++
|
||||
existBorthers.splice(existIndex, 0, node)
|
||||
existParent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render(() => {
|
||||
if (nodeLayerChanged) {
|
||||
node.getSize()
|
||||
node.renderNode()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 移除节点
|
||||
|
||||
removeNode(appointNodes = []) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
let isAppointNodes = appointNodes.length > 0
|
||||
let list = isAppointNodes ? appointNodes : this.activeNodeList
|
||||
let root = list.find((node) => {
|
||||
return node.isRoot
|
||||
})
|
||||
if (root) {
|
||||
this.clearActive()
|
||||
root.children.forEach(child => {
|
||||
child.remove()
|
||||
})
|
||||
root.children = []
|
||||
root.nodeData.children = []
|
||||
// 当前节点在目标节点前面
|
||||
if (index < existIndex) {
|
||||
// do nothing
|
||||
} else {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let node = list[i]
|
||||
if (isAppointNodes) list.splice(i, 1)
|
||||
if (node.isGeneralization) {
|
||||
// 删除概要节点
|
||||
this.setNodeData(node.generalizationBelongNode, {
|
||||
generalization: null
|
||||
})
|
||||
node.generalizationBelongNode.update()
|
||||
this.removeActiveNode(node)
|
||||
i--
|
||||
} else {
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
i--
|
||||
}
|
||||
}
|
||||
existIndex = existIndex + 1
|
||||
}
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
// 节点实例
|
||||
childList.splice(index, 1)
|
||||
childList.splice(existIndex, 0, node)
|
||||
// 节点数据
|
||||
parent.nodeData.children.splice(index, 1)
|
||||
parent.nodeData.children.splice(existIndex, 0, node.nodeData)
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 移除某个指定节点
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 13:40:39
|
||||
* @Desc: 移除节点
|
||||
*/
|
||||
removeNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < this.activeNodeList.length; i++) {
|
||||
let node = this.activeNodeList[i]
|
||||
if (node.isGeneralization) {
|
||||
// 删除概要节点
|
||||
this.setNodeData(node.generalizationBelongNode, {
|
||||
generalization: null
|
||||
})
|
||||
node.generalizationBelongNode.update()
|
||||
this.removeActiveNode(node)
|
||||
i--
|
||||
} else if (node.isRoot) {
|
||||
node.children.forEach(child => {
|
||||
child.remove()
|
||||
})
|
||||
node.children = []
|
||||
node.nodeData.children = []
|
||||
break
|
||||
} else {
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
i--
|
||||
}
|
||||
}
|
||||
this.mindMap.emit('node_active', null, [])
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 22:46:27
|
||||
* @Desc: 移除某个指定节点
|
||||
*/
|
||||
removeOneNode(node) {
|
||||
let index = this.getNodeIndex(node)
|
||||
node.remove()
|
||||
@@ -611,8 +667,12 @@ class Render {
|
||||
node.parent.nodeData.children.splice(index, 1)
|
||||
}
|
||||
|
||||
// 复制节点,多个节点只会操作第一个节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-15 09:53:23
|
||||
* @Desc: 复制节点,多个节点只会操作第一个节点
|
||||
*/
|
||||
copyNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -620,8 +680,11 @@ class Render {
|
||||
return copyNodeTree({}, this.activeNodeList[0], true)
|
||||
}
|
||||
|
||||
// 剪切节点,多个节点只会操作第一个节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 22:36:45
|
||||
* @Desc: 剪切节点,多个节点只会操作第一个节点
|
||||
*/
|
||||
cutNode(callback) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -640,25 +703,29 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 移动一个节点作为另一个节点的子节点
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-24 16:54:01
|
||||
* @Desc: 移动一个节点作为另一个节点的子节点
|
||||
*/
|
||||
moveNodeTo(node, toNode) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
let copyData = copyNodeTree({}, node, false, true)
|
||||
let copyData = copyNodeTree({}, node)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
toNode.nodeData.children.push(copyData)
|
||||
this.mindMap.render()
|
||||
if (toNode.isRoot) {
|
||||
toNode.renderNode()
|
||||
}
|
||||
}
|
||||
|
||||
// 粘贴节点到节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 20:09:39
|
||||
* @Desc: 粘贴节点到节点
|
||||
*/
|
||||
pasteNode(data) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -669,8 +736,11 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 设置节点样式
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-08 21:54:30
|
||||
* @Desc: 设置节点样式
|
||||
*/
|
||||
setNodeStyle(node, prop, value, isActive) {
|
||||
let data = {}
|
||||
if (isActive) {
|
||||
@@ -685,15 +755,6 @@ class Render {
|
||||
[prop]: value
|
||||
}
|
||||
}
|
||||
// 如果开启了富文本,则需要应用到富文本上
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.showEditText(node)
|
||||
let config = this.mindMap.richText.normalStyleToRichTextStyle({
|
||||
[prop]: value
|
||||
})
|
||||
this.mindMap.richText.formatAllText(config)
|
||||
this.mindMap.richText.hideEditText()
|
||||
}
|
||||
this.setNodeDataRender(node, data)
|
||||
// 更新了连线的样式
|
||||
if (lineStyleProps.includes(prop)) {
|
||||
@@ -701,8 +762,11 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置节点是否激活
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-08 22:13:03
|
||||
* @Desc: 设置节点是否激活
|
||||
*/
|
||||
setNodeActive(node, active) {
|
||||
this.setNodeData(node, {
|
||||
isActive: active
|
||||
@@ -710,8 +774,11 @@ class Render {
|
||||
node.renderNode()
|
||||
}
|
||||
|
||||
// 设置节点是否展开
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 16:52:41
|
||||
* @Desc: 设置节点是否展开
|
||||
*/
|
||||
setNodeExpand(node, expand) {
|
||||
this.setNodeData(node, {
|
||||
expand
|
||||
@@ -734,8 +801,11 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 展开所有
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 23:23:37
|
||||
* @Desc: 展开所有
|
||||
*/
|
||||
expandAllNode() {
|
||||
walk(
|
||||
this.renderTree,
|
||||
@@ -753,8 +823,11 @@ class Render {
|
||||
this.mindMap.reRender()
|
||||
}
|
||||
|
||||
// 收起所有
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-15 23:27:14
|
||||
* @Desc: 收起所有
|
||||
*/
|
||||
unexpandAllNode() {
|
||||
walk(
|
||||
this.renderTree,
|
||||
@@ -773,8 +846,12 @@ class Render {
|
||||
this.mindMap.reRender()
|
||||
}
|
||||
|
||||
// 展开到指定层级
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-23 16:31:27
|
||||
* @Desc: 展开到指定层级
|
||||
*/
|
||||
expandToLevel(level) {
|
||||
walk(
|
||||
this.renderTree,
|
||||
@@ -791,8 +868,11 @@ class Render {
|
||||
this.mindMap.reRender()
|
||||
}
|
||||
|
||||
// 切换激活节点的展开状态
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-14 09:18:40
|
||||
* @Desc: 切换激活节点的展开状态
|
||||
*/
|
||||
toggleActiveExpand() {
|
||||
this.activeNodeList.forEach(node => {
|
||||
if (node.nodeData.children.length <= 0) {
|
||||
@@ -802,8 +882,11 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 切换节点展开状态
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 17:15:33
|
||||
* @Desc: 切换节点展开状态
|
||||
*/
|
||||
toggleNodeExpand(node) {
|
||||
this.mindMap.execCommand(
|
||||
'SET_NODE_EXPAND',
|
||||
@@ -812,17 +895,22 @@ class Render {
|
||||
)
|
||||
}
|
||||
|
||||
// 设置节点文本
|
||||
|
||||
setNodeText(node, text, richText) {
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-09 22:04:19
|
||||
* @Desc: 设置节点文本
|
||||
*/
|
||||
setNodeText(node, text) {
|
||||
this.setNodeDataRender(node, {
|
||||
text,
|
||||
richText
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点图片
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:37:40
|
||||
* @Desc: 设置节点图片
|
||||
*/
|
||||
setNodeImage(node, { url, title, width, height }) {
|
||||
this.setNodeDataRender(node, {
|
||||
image: url,
|
||||
@@ -834,16 +922,22 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点图标
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:44:06
|
||||
* @Desc: 设置节点图标
|
||||
*/
|
||||
setNodeIcon(node, icons) {
|
||||
this.setNodeDataRender(node, {
|
||||
icon: icons
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点超链接
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:49:33
|
||||
* @Desc: 设置节点超链接
|
||||
*/
|
||||
setNodeHyperlink(node, link, title = '') {
|
||||
this.setNodeDataRender(node, {
|
||||
hyperlink: link,
|
||||
@@ -851,24 +945,33 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点备注
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:52:59
|
||||
* @Desc: 设置节点备注
|
||||
*/
|
||||
setNodeNote(node, note) {
|
||||
this.setNodeDataRender(node, {
|
||||
note
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点标签
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:54:53
|
||||
* @Desc: 设置节点标签
|
||||
*/
|
||||
setNodeTag(node, tag) {
|
||||
this.setNodeDataRender(node, {
|
||||
tag
|
||||
})
|
||||
}
|
||||
|
||||
// 添加节点概要
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-07-30 20:52:42
|
||||
* @Desc: 添加节点概要
|
||||
*/
|
||||
addGeneralization(data) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -887,8 +990,11 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 删除节点概要
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-07-30 21:16:33
|
||||
* @Desc: 删除节点概要
|
||||
*/
|
||||
removeGeneralization() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
@@ -905,8 +1011,12 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 设置节点自定义位置
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-02 19:04:24
|
||||
* @Desc: 设置节点自定义位置
|
||||
*/
|
||||
setNodeCustomPosition(node, left = undefined, top = undefined) {
|
||||
let nodeList = [node] || this.activeNodeList
|
||||
nodeList.forEach(item => {
|
||||
@@ -917,8 +1027,12 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 一键整理布局,即去除自定义位置
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-02 20:02:50
|
||||
* @Desc: 一键整理布局,即去除自定义位置
|
||||
*/
|
||||
resetLayout() {
|
||||
walk(
|
||||
this.root,
|
||||
@@ -939,8 +1053,12 @@ class Render {
|
||||
)
|
||||
}
|
||||
|
||||
// 设置节点形状
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 21:44:01
|
||||
* @Desc: 设置节点形状
|
||||
*/
|
||||
setNodeShape(node, shape) {
|
||||
if (!shape || !shapeList.includes(shape)) {
|
||||
return
|
||||
@@ -951,16 +1069,22 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 更新节点数据
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-05-04 14:19:48
|
||||
* @Desc: 更新节点数据
|
||||
*/
|
||||
setNodeData(node, data) {
|
||||
Object.keys(data).forEach(key => {
|
||||
node.nodeData.data[key] = data[key]
|
||||
})
|
||||
}
|
||||
|
||||
// 设置节点数据,并判断是否渲染
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 08:45:48
|
||||
* @Desc: 设置节点数据,并判断是否渲染
|
||||
*/
|
||||
setNodeDataRender(node, data) {
|
||||
this.setNodeData(node, data)
|
||||
let changed = node.getSize()
|
||||
@@ -974,8 +1098,12 @@ class Render {
|
||||
}
|
||||
}
|
||||
|
||||
// 移动节点到画布中心
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 11:46:57
|
||||
* @Desc: 移动节点到画布中心
|
||||
*/
|
||||
moveNodeToCenter(node) {
|
||||
let halfWidth = this.mindMap.width / 2
|
||||
let halfHeight = this.mindMap.height / 2
|
||||
|
||||
@@ -1,459 +0,0 @@
|
||||
import Quill from 'quill'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import './css/quill.css'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { Image as SvgImage } from '@svgdotjs/svg.js'
|
||||
import { walk } from './utils'
|
||||
|
||||
let extended = false
|
||||
|
||||
// 扩展quill的字体列表
|
||||
let fontFamilyList = [
|
||||
'宋体, SimSun, Songti SC',
|
||||
'微软雅黑, Microsoft YaHei',
|
||||
'楷体, 楷体_GB2312, SimKai, STKaiti',
|
||||
'黑体, SimHei, Heiti SC',
|
||||
'隶书, SimLi',
|
||||
'andale mono',
|
||||
'arial, helvetica, sans-serif',
|
||||
'arial black, avant garde',
|
||||
'comic sans ms',
|
||||
'impact, chicago',
|
||||
'times new roman',
|
||||
'sans-serif',
|
||||
'serif'
|
||||
]
|
||||
|
||||
// 扩展quill的字号列表
|
||||
let fontSizeList = new Array(100).fill(0).map((_, index) => {
|
||||
return index + 'px'
|
||||
})
|
||||
|
||||
// 节点支持富文本编辑功能
|
||||
class RichText {
|
||||
constructor({ mindMap, pluginOpt }) {
|
||||
this.mindMap = mindMap
|
||||
this.pluginOpt = pluginOpt
|
||||
this.textEditNode = null
|
||||
this.showTextEdit = false
|
||||
this.quill = null
|
||||
this.range = null
|
||||
this.lastRange = null
|
||||
this.node = null
|
||||
this.initOpt()
|
||||
this.extendQuill()
|
||||
}
|
||||
|
||||
// 处理选项参数
|
||||
initOpt() {
|
||||
if (
|
||||
this.pluginOpt.fontFamilyList &&
|
||||
Array.isArray(this.pluginOpt.fontFamilyList)
|
||||
) {
|
||||
fontFamilyList = this.pluginOpt.fontFamilyList
|
||||
}
|
||||
if (
|
||||
this.pluginOpt.fontSizeList &&
|
||||
Array.isArray(this.pluginOpt.fontSizeList)
|
||||
) {
|
||||
fontSizeList = this.pluginOpt.fontSizeList
|
||||
}
|
||||
}
|
||||
|
||||
// 扩展quill编辑器
|
||||
extendQuill() {
|
||||
if (extended) {
|
||||
return
|
||||
}
|
||||
extended = true
|
||||
|
||||
// 扩展quill的字体列表
|
||||
const FontAttributor = Quill.import('attributors/class/font')
|
||||
FontAttributor.whitelist = fontFamilyList
|
||||
Quill.register(FontAttributor, true)
|
||||
|
||||
const FontStyle = Quill.import('attributors/style/font')
|
||||
FontStyle.whitelist = fontFamilyList
|
||||
Quill.register(FontStyle, true)
|
||||
|
||||
// 扩展quill的字号列表
|
||||
const SizeAttributor = Quill.import('attributors/class/size')
|
||||
SizeAttributor.whitelist = fontSizeList
|
||||
Quill.register(SizeAttributor, true)
|
||||
|
||||
const SizeStyle = Quill.import('attributors/style/size')
|
||||
SizeStyle.whitelist = fontSizeList
|
||||
Quill.register(SizeStyle, true)
|
||||
}
|
||||
|
||||
// 显示文本编辑控件
|
||||
showEditText(node, rect) {
|
||||
if (this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
this.node = node
|
||||
if (!rect) rect = node._textData.node.node.getBoundingClientRect()
|
||||
this.mindMap.emit('before_show_text_edit')
|
||||
this.mindMap.renderer.textEdit.registerTmpShortcut()
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;box-shadow: 0 0 20px rgba(0,0,0,.5);outline: none; word-break: break-all;`
|
||||
document.body.appendChild(this.textEditNode)
|
||||
}
|
||||
// 原始宽高
|
||||
let g = node._textData.node
|
||||
let originWidth = g.attr('data-width')
|
||||
let originHeight = g.attr('data-height')
|
||||
// 使用节点的填充色,否则如果节点颜色是白色的话编辑时看不见
|
||||
let bgColor = node.style.merge('fillColor')
|
||||
this.textEditNode.style.backgroundColor = bgColor === 'transparent' ? '#fff' : bgColor
|
||||
this.textEditNode.style.minWidth = originWidth + 'px'
|
||||
this.textEditNode.style.minHeight = originHeight + 'px'
|
||||
this.textEditNode.style.left =
|
||||
rect.left + (rect.width - originWidth) / 2 + 'px'
|
||||
this.textEditNode.style.top =
|
||||
rect.top + (rect.height - originHeight) / 2 + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
|
||||
this.textEditNode.style.transform = `scale(${rect.width / originWidth}, ${
|
||||
rect.height / originHeight
|
||||
})`
|
||||
if (!node.nodeData.data.richText) {
|
||||
// 还不是富文本的情况
|
||||
let text = node.nodeData.data.text.split(/\n/gim).join('<br>')
|
||||
let html = `<p>${text}</p>`
|
||||
this.textEditNode.innerHTML = html
|
||||
} else {
|
||||
this.textEditNode.innerHTML = node.nodeData.data.text
|
||||
}
|
||||
this.initQuillEditor()
|
||||
document.querySelector('.ql-editor').style.minHeight = originHeight + 'px'
|
||||
this.showTextEdit = true
|
||||
this.focus()
|
||||
if (!node.nodeData.data.richText) {
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
this.setTextStyleIfNotRichText(node)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
setTextStyleIfNotRichText(node) {
|
||||
let style = {
|
||||
font: node.style.merge('fontFamily'),
|
||||
color: node.style.merge('color'),
|
||||
italic: node.style.merge('fontStyle') === 'italic',
|
||||
bold: node.style.merge('fontWeight') === 'bold',
|
||||
size: node.style.merge('fontSize') + 'px',
|
||||
underline: node.style.merge('textDecoration') === 'underline',
|
||||
strike: node.style.merge('textDecoration') === 'line-through'
|
||||
}
|
||||
this.formatText(style)
|
||||
}
|
||||
|
||||
// 隐藏文本编辑控件,即完成编辑
|
||||
hideEditText() {
|
||||
if (!this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
let html = this.quill.container.firstChild.innerHTML
|
||||
// 去除最后的空行
|
||||
html = html.replace(/<p><br><\/p>$/, '')
|
||||
this.mindMap.renderer.activeNodeList.forEach(node => {
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', node, html, true)
|
||||
if (node.isGeneralization) {
|
||||
// 概要节点
|
||||
node.generalizationBelongNode.updateGeneralization()
|
||||
}
|
||||
this.mindMap.render()
|
||||
})
|
||||
this.mindMap.emit(
|
||||
'hide_text_edit',
|
||||
this.textEditNode,
|
||||
this.mindMap.renderer.activeNodeList
|
||||
)
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.showTextEdit = false
|
||||
this.mindMap.emit('rich_text_selection_change', false)
|
||||
this.node = null
|
||||
}
|
||||
|
||||
// 初始化Quill富文本编辑器
|
||||
initQuillEditor() {
|
||||
this.quill = new Quill(this.textEditNode, {
|
||||
modules: {
|
||||
toolbar: false,
|
||||
keyboard: {
|
||||
bindings: {
|
||||
enter: {
|
||||
key: 13,
|
||||
handler: function () {
|
||||
// 覆盖默认的回车键换行
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
theme: 'snow'
|
||||
})
|
||||
this.quill.on('selection-change', range => {
|
||||
this.lastRange = this.range
|
||||
this.range = null
|
||||
if (range) {
|
||||
let bounds = this.quill.getBounds(range.index, range.length)
|
||||
let rect = this.textEditNode.getBoundingClientRect()
|
||||
let rectInfo = {
|
||||
left: bounds.left + rect.left,
|
||||
top: bounds.top + rect.top,
|
||||
right: bounds.right + rect.left,
|
||||
bottom: bounds.bottom + rect.top,
|
||||
width: bounds.width
|
||||
}
|
||||
let formatInfo = this.quill.getFormat(range.index, range.length)
|
||||
let hasRange = false
|
||||
if (range.length == 0) {
|
||||
hasRange = false
|
||||
} else {
|
||||
this.range = range
|
||||
hasRange = true
|
||||
}
|
||||
this.mindMap.emit(
|
||||
'rich_text_selection_change',
|
||||
hasRange,
|
||||
rectInfo,
|
||||
formatInfo
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选中全部
|
||||
selectAll() {
|
||||
this.quill.setSelection(0, this.quill.getLength())
|
||||
}
|
||||
|
||||
// 聚焦
|
||||
focus() {
|
||||
let len = this.quill.getLength()
|
||||
this.quill.setSelection(len, len)
|
||||
}
|
||||
|
||||
// 格式化当前选中的文本
|
||||
formatText(config = {}, clear = false) {
|
||||
if (!this.range && !this.lastRange) return
|
||||
this.syncFormatToNodeConfig(config, clear)
|
||||
let rangeLost = !this.range
|
||||
let range = rangeLost ? this.lastRange : this.range
|
||||
clear ? this.quill.removeFormat(range.index, range.length) : this.quill.formatText(range.index, range.length, config)
|
||||
if (rangeLost) {
|
||||
this.quill.setSelection(this.lastRange.index, this.lastRange.length)
|
||||
}
|
||||
}
|
||||
|
||||
// 清除当前选中文本的样式
|
||||
removeFormat() {
|
||||
this.formatText({}, true)
|
||||
}
|
||||
|
||||
// 格式化指定范围的文本
|
||||
formatRangeText(range, config = {}) {
|
||||
if (!range) return
|
||||
this.syncFormatToNodeConfig(config)
|
||||
this.quill.formatText(range.index, range.length, config)
|
||||
}
|
||||
|
||||
// 格式化所有文本
|
||||
formatAllText(config = {}) {
|
||||
this.syncFormatToNodeConfig(config)
|
||||
this.quill.formatText(0, this.quill.getLength(), config)
|
||||
}
|
||||
|
||||
// 同步格式化到节点样式配置
|
||||
syncFormatToNodeConfig(config, clear) {
|
||||
if (!this.node) return
|
||||
if (clear) {
|
||||
// 清除文本样式
|
||||
['fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'textDecoration', 'color'].forEach((prop) => {
|
||||
delete this.node.nodeData.data[prop]
|
||||
})
|
||||
} else {
|
||||
let data = this.richTextStyleToNormalStyle(config)
|
||||
this.mindMap.renderer.setNodeData(this.node, data)
|
||||
}
|
||||
}
|
||||
|
||||
// 将普通节点样式对象转换成富文本样式对象
|
||||
normalStyleToRichTextStyle(style) {
|
||||
let config = {}
|
||||
Object.keys(style).forEach(prop => {
|
||||
let value = style[prop]
|
||||
switch (prop) {
|
||||
case 'fontFamily':
|
||||
config.font = value
|
||||
break
|
||||
case 'fontSize':
|
||||
config.size = value + 'px'
|
||||
break
|
||||
case 'fontWeight':
|
||||
config.bold = value === 'bold'
|
||||
break
|
||||
case 'fontStyle':
|
||||
config.italic = value === 'italic'
|
||||
break
|
||||
case 'textDecoration':
|
||||
config.underline = value === 'underline'
|
||||
config.strike = value === 'line-through'
|
||||
break
|
||||
case 'color':
|
||||
config.color = value
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
return config
|
||||
}
|
||||
|
||||
// 将富文本样式对象转换成普通节点样式对象
|
||||
richTextStyleToNormalStyle(config) {
|
||||
let data = {}
|
||||
Object.keys(config).forEach(prop => {
|
||||
let value = config[prop]
|
||||
switch (prop) {
|
||||
case 'font':
|
||||
data.fontFamily = value
|
||||
break
|
||||
case 'size':
|
||||
data.fontSize = parseFloat(value)
|
||||
break
|
||||
case 'bold':
|
||||
data.fontWeight = value ? 'bold' : 'normal'
|
||||
break
|
||||
case 'italic':
|
||||
data.fontStyle = value ? 'italic' : 'normal'
|
||||
break
|
||||
case 'underline':
|
||||
data.textDecoration = value ? 'underline' : 'none'
|
||||
break
|
||||
case 'strike':
|
||||
data.textDecoration = value ? 'line-through' : 'none'
|
||||
break
|
||||
case 'color':
|
||||
data.color = value
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// 将svg中嵌入的dom元素转换成图片
|
||||
async _handleSvgDomElements(svg) {
|
||||
svg = svg.clone()
|
||||
let foreignObjectList = svg.find('foreignObject')
|
||||
let task = foreignObjectList.map(async item => {
|
||||
let clone = item.first().node.cloneNode(true)
|
||||
let div = document.createElement('div')
|
||||
div.style.cssText = `position: fixed; left: -999999px;`
|
||||
div.appendChild(clone)
|
||||
this.mindMap.el.appendChild(div)
|
||||
let canvas = await html2canvas(clone, {
|
||||
backgroundColor: null
|
||||
})
|
||||
this.mindMap.el.removeChild(div)
|
||||
let imgNode = new SvgImage()
|
||||
.load(canvas.toDataURL())
|
||||
.size(canvas.width, canvas.height)
|
||||
item.replace(imgNode)
|
||||
})
|
||||
await Promise.all(task)
|
||||
return {
|
||||
svg: svg,
|
||||
svgHTML: svg.svg()
|
||||
}
|
||||
}
|
||||
|
||||
// 将svg中嵌入的dom元素转换成图片
|
||||
handleSvgDomElements(svg) {
|
||||
return new Promise((resolve, reject) => {
|
||||
svg = svg.clone()
|
||||
let foreignObjectList = svg.find('foreignObject')
|
||||
let index = 0
|
||||
let len = foreignObjectList.length
|
||||
let transform = async () => {
|
||||
this.mindMap.emit('transforming-dom-to-images', index, len)
|
||||
try {
|
||||
let item = foreignObjectList[index++]
|
||||
let parent = item.parent()
|
||||
let clone = item.first().node.cloneNode(true)
|
||||
let div = document.createElement('div')
|
||||
div.style.cssText = `position: fixed; left: -999999px;`
|
||||
div.appendChild(clone)
|
||||
this.mindMap.el.appendChild(div)
|
||||
let canvas = await html2canvas(clone, {
|
||||
backgroundColor: null
|
||||
})
|
||||
// 优先使用原始宽高,因为当设备的window.devicePixelRatio不为1时,html2canvas输出的图片会更大
|
||||
let imgNodeWidth = parent.attr('data-width') || canvas.width
|
||||
let imgNodeHeight = parent.attr('data-height') || canvas.height
|
||||
this.mindMap.el.removeChild(div)
|
||||
let imgNode = new SvgImage()
|
||||
.load(canvas.toDataURL())
|
||||
.size(imgNodeWidth, imgNodeHeight)
|
||||
.x((parent ? parent.attr('data-offsetx') : 0) || 0)
|
||||
item.replace(imgNode)
|
||||
if (index <= len - 1) {
|
||||
setTimeout(() => {
|
||||
transform()
|
||||
}, 0)
|
||||
} else {
|
||||
resolve({
|
||||
svg: svg,
|
||||
svgHTML: svg.svg()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
transform()
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 将所有节点转换成非富文本节点
|
||||
transformAllNodesToNormalNode() {
|
||||
let div = document.createElement('div')
|
||||
walk(
|
||||
this.mindMap.renderer.renderTree,
|
||||
null,
|
||||
node => {
|
||||
if (node.data.richText) {
|
||||
node.data.richText = false
|
||||
div.innerHTML = node.data.text
|
||||
node.data.text = div.textContent
|
||||
}
|
||||
},
|
||||
null,
|
||||
true,
|
||||
0,
|
||||
0
|
||||
)
|
||||
// 清空历史数据,并且触发数据变化
|
||||
this.mindMap.command.clearHistory()
|
||||
this.mindMap.command.addHistory()
|
||||
this.mindMap.reRender()
|
||||
}
|
||||
|
||||
// 插件被移除前做的事情
|
||||
beforePluginRemove() {
|
||||
this.transformAllNodesToNormalNode()
|
||||
}
|
||||
}
|
||||
|
||||
RichText.instanceName = 'richText'
|
||||
|
||||
export default RichText
|
||||
@@ -1,9 +1,17 @@
|
||||
import { bfsWalk, throttle } from './utils'
|
||||
|
||||
// 选择节点类
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:34:51
|
||||
* @Desc: 选择节点类
|
||||
*/
|
||||
|
||||
class Select {
|
||||
// 构造函数
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:35:16
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor({ mindMap }) {
|
||||
this.mindMap = mindMap
|
||||
this.rect = null
|
||||
@@ -15,7 +23,11 @@ class Select {
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-10 22:36:36
|
||||
* @Desc: 绑定事件
|
||||
*/
|
||||
bindEvent() {
|
||||
this.checkInNodes = throttle(this.checkInNodes, 500, this)
|
||||
this.mindMap.on('mousedown', e => {
|
||||
@@ -69,7 +81,11 @@ class Select {
|
||||
})
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-13 07:55:49
|
||||
* @Desc: 鼠标移动事件
|
||||
*/
|
||||
onMove(x, y) {
|
||||
// 绘制矩形
|
||||
this.rect.plot([
|
||||
@@ -112,14 +128,22 @@ class Select {
|
||||
}
|
||||
}
|
||||
|
||||
// 开启自动移动
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-22 08:02:23
|
||||
* @Desc: 开启自动移动
|
||||
*/
|
||||
startAutoMove(x, y) {
|
||||
this.autoMoveTimer = setTimeout(() => {
|
||||
this.onMove(x, y)
|
||||
}, 20)
|
||||
}
|
||||
|
||||
// 创建矩形
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:19:37
|
||||
* @Desc: 创建矩形
|
||||
*/
|
||||
createRect(x, y) {
|
||||
this.rect = this.mindMap.svg
|
||||
.polygon()
|
||||
@@ -132,7 +156,11 @@ class Select {
|
||||
.plot([[x, y]])
|
||||
}
|
||||
|
||||
// 检测在选区里的节点
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 10:20:43
|
||||
* @Desc: 检测在选区里的节点
|
||||
*/
|
||||
checkInNodes() {
|
||||
let { scaleX, scaleY, translateX, translateY } =
|
||||
this.mindMap.draw.transform()
|
||||
@@ -167,6 +195,4 @@ class Select {
|
||||
}
|
||||
}
|
||||
|
||||
Select.instanceName = 'select'
|
||||
|
||||
export default Select
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
// 节点形状类
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-22 21:32:50
|
||||
* @Desc: 节点形状类
|
||||
*/
|
||||
export default class Shape {
|
||||
constructor(node) {
|
||||
this.node = node
|
||||
}
|
||||
|
||||
// 形状需要的padding
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-17 22:32:32
|
||||
* @Desc: 形状需要的padding
|
||||
*/
|
||||
getShapePadding(width, height, paddingX, paddingY) {
|
||||
const shape = this.node.getShape()
|
||||
const defaultPaddingX = 15
|
||||
@@ -56,7 +64,11 @@ export default class Shape {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建形状节点
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-08-17 22:22:53
|
||||
* @Desc: 创建形状节点
|
||||
*/
|
||||
createShape() {
|
||||
const shape = this.node.getShape()
|
||||
let { width, height } = this.node
|
||||
@@ -92,7 +104,11 @@ export default class Shape {
|
||||
return node
|
||||
}
|
||||
|
||||
// 创建菱形
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-04 09:08:54
|
||||
* @Desc: 创建菱形
|
||||
*/
|
||||
createDiamond() {
|
||||
let { width, height } = this.node
|
||||
let halfWidth = width / 2
|
||||
@@ -113,7 +129,11 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建平行四边形
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-03 16:14:12
|
||||
* @Desc: 创建平行四边形
|
||||
*/
|
||||
createParallelogram() {
|
||||
let { paddingX } = this.node.getPaddingVale()
|
||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||
@@ -126,7 +146,11 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建圆角矩形
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-03 16:50:23
|
||||
* @Desc: 创建圆角矩形
|
||||
*/
|
||||
createRoundedRectangle() {
|
||||
let { width, height } = this.node
|
||||
let halfHeight = height / 2
|
||||
@@ -139,7 +163,12 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建八角矩形
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 16:14:08
|
||||
* @Desc: 创建八角矩形
|
||||
*/
|
||||
createOctagonalRectangle() {
|
||||
let w = 5
|
||||
let { width, height } = this.node
|
||||
@@ -155,7 +184,12 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建外三角矩形
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 20:55:50
|
||||
* @Desc: 创建外三角矩形
|
||||
*/
|
||||
createOuterTriangularRectangle() {
|
||||
let { paddingX } = this.node.getPaddingVale()
|
||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||
@@ -170,7 +204,12 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建内三角矩形
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 20:59:37
|
||||
* @Desc: 创建内三角矩形
|
||||
*/
|
||||
createInnerTriangularRectangle() {
|
||||
let { paddingX } = this.node.getPaddingVale()
|
||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||
@@ -185,7 +224,12 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建椭圆
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 21:06:31
|
||||
* @Desc: 创建椭圆
|
||||
*/
|
||||
createEllipse() {
|
||||
let { width, height } = this.node
|
||||
let halfWidth = width / 2
|
||||
@@ -198,7 +242,12 @@ export default class Shape {
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建圆
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 21:14:04
|
||||
* @Desc: 创建圆
|
||||
*/
|
||||
createCircle() {
|
||||
let { width, height } = this.node
|
||||
let halfWidth = width / 2
|
||||
|
||||
@@ -1,39 +1,50 @@
|
||||
import { tagColorList } from './utils/constant'
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
|
||||
// 样式类
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 10:09:08
|
||||
* @Desc: 样式类
|
||||
*/
|
||||
class Style {
|
||||
// 设置背景样式
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 16:01:53
|
||||
* @Desc: 设置背景样式
|
||||
*/
|
||||
static setBackgroundStyle(el, themeConfig) {
|
||||
let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig
|
||||
let { backgroundColor, backgroundImage, backgroundRepeat } = themeConfig
|
||||
el.style.backgroundColor = backgroundColor
|
||||
if (backgroundImage) {
|
||||
el.style.backgroundImage = `url(${backgroundImage})`
|
||||
el.style.backgroundRepeat = backgroundRepeat
|
||||
el.style.backgroundPosition = backgroundPosition
|
||||
el.style.backgroundSize = backgroundSize
|
||||
} else {
|
||||
el.style.backgroundImage = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
// 构造函数
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 10:10:11
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(ctx, themeConfig) {
|
||||
this.ctx = ctx
|
||||
this.themeConfig = themeConfig
|
||||
}
|
||||
|
||||
// 更新主题配置
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-12 07:40:14
|
||||
* @Desc: 更新主题配置
|
||||
*/
|
||||
updateThemeConfig(themeConfig) {
|
||||
this.themeConfig = themeConfig
|
||||
}
|
||||
|
||||
// 合并样式
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 12:02:55
|
||||
* @Desc: 合并样式
|
||||
*/
|
||||
merge(prop, root, isActive) {
|
||||
// 三级及以下节点
|
||||
let defaultConfig = this.themeConfig.node
|
||||
@@ -67,49 +78,59 @@ class Style {
|
||||
: defaultConfig[prop]
|
||||
}
|
||||
|
||||
// 获取某个样式值
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 21:55:57
|
||||
* @Desc: 获取某个样式值
|
||||
*/
|
||||
getStyle(prop, root, isActive) {
|
||||
return this.merge(prop, root, isActive)
|
||||
}
|
||||
|
||||
// 获取自身自定义样式
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: flydreame
|
||||
* @Date: 2022-09-17 12:09:39
|
||||
* @Desc: 获取自身自定义样式
|
||||
*/
|
||||
getSelfStyle(prop) {
|
||||
return this.ctx.nodeData.data[prop]
|
||||
}
|
||||
|
||||
// 矩形
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 10:12:56
|
||||
* @Desc: 矩形
|
||||
*/
|
||||
rect(node) {
|
||||
this.shape(node)
|
||||
node.radius(this.merge('borderRadius'))
|
||||
}
|
||||
|
||||
// 矩形外的其他形状
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林
|
||||
* @Date: 2022-09-12 15:04:28
|
||||
* @Desc: 矩形外的其他形状
|
||||
*/
|
||||
shape(node) {
|
||||
node.fill({
|
||||
color: this.merge('fillColor')
|
||||
})
|
||||
// 节点使用横线样式,不需要渲染非激活状态的边框样式
|
||||
if (
|
||||
!this.ctx.isRoot &&
|
||||
!this.ctx.isGeneralization &&
|
||||
this.themeConfig.nodeUseLineStyle &&
|
||||
!this.ctx.nodeData.data.isActive
|
||||
) {
|
||||
return
|
||||
}
|
||||
node.stroke({
|
||||
color: this.merge('borderColor'),
|
||||
width: this.merge('borderWidth'),
|
||||
dasharray: this.merge('borderDasharray')
|
||||
})
|
||||
node
|
||||
.fill({
|
||||
color: this.merge('fillColor')
|
||||
})
|
||||
.stroke({
|
||||
color: this.merge('borderColor'),
|
||||
width: this.merge('borderWidth'),
|
||||
dasharray: this.merge('borderDasharray')
|
||||
})
|
||||
}
|
||||
|
||||
// 文字
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 12:07:59
|
||||
* @Desc: 文字
|
||||
*/
|
||||
text(node) {
|
||||
node
|
||||
.fill({
|
||||
@@ -124,28 +145,22 @@ class Style {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取文本样式
|
||||
getTextFontStyle() {
|
||||
return {
|
||||
italic: this.merge('fontStyle') === 'italic',
|
||||
bold: this.merge('fontWeight'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontFamily: this.merge('fontFamily')
|
||||
}
|
||||
}
|
||||
|
||||
// html文字节点
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-13 08:14:34
|
||||
* @Desc: html文字节点
|
||||
*/
|
||||
domText(node, fontSizeScale = 1) {
|
||||
node.style.fontFamily = this.merge('fontFamily')
|
||||
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
|
||||
node.style.fontWeight = this.merge('fontWeight') || 'normal'
|
||||
node.style.lineHeight = this.merge('lineHeight')
|
||||
node.style.fontStyle = this.merge('fontStyle')
|
||||
}
|
||||
|
||||
// 标签文字
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 20:02:18
|
||||
* @Desc: 标签文字
|
||||
*/
|
||||
tagText(node, index) {
|
||||
node
|
||||
.fill({
|
||||
@@ -156,30 +171,42 @@ class Style {
|
||||
})
|
||||
}
|
||||
|
||||
// 标签矩形
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-20 21:04:11
|
||||
* @Desc: 标签矩形
|
||||
*/
|
||||
tagRect(node, index) {
|
||||
node.fill({
|
||||
color: tagColorList[index].background
|
||||
})
|
||||
}
|
||||
|
||||
// 内置图标
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-03 22:37:19
|
||||
* @Desc: 内置图标
|
||||
*/
|
||||
iconNode(node) {
|
||||
node.attr({
|
||||
fill: this.merge('color')
|
||||
})
|
||||
}
|
||||
|
||||
// 连线
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 14:50:49
|
||||
* @Desc: 连线
|
||||
*/
|
||||
line(node, { width, color, dasharray } = {}) {
|
||||
node.stroke({ width, color, dasharray }).fill({ color: 'none' })
|
||||
}
|
||||
|
||||
// 概要连线
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2022-07-30 16:19:03
|
||||
* @Desc: 概要连线
|
||||
*/
|
||||
generalizationLine(node) {
|
||||
node
|
||||
.stroke({
|
||||
@@ -189,8 +216,11 @@ class Style {
|
||||
.fill({ color: 'none' })
|
||||
}
|
||||
|
||||
// 按钮
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-11 20:03:59
|
||||
* @Desc: 按钮
|
||||
*/
|
||||
iconBtn(node, fillNode) {
|
||||
node.fill({ color: '#808080' })
|
||||
fillNode.fill({ color: '#fff' })
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import { getStrWithBrFromHtml } from './utils'
|
||||
|
||||
// 节点文字编辑类
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-06-19 11:11:28
|
||||
* @Desc: 节点文字编辑类
|
||||
*/
|
||||
export default class TextEdit {
|
||||
// 构造函数
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-06-19 11:22:57
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(renderer) {
|
||||
this.renderer = renderer
|
||||
this.mindMap = renderer.mindMap
|
||||
@@ -13,7 +23,11 @@ export default class TextEdit {
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 事件
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 13:27:04
|
||||
* @Desc: 事件
|
||||
*/
|
||||
bindEvent() {
|
||||
this.show = this.show.bind(this)
|
||||
// 节点双击事件
|
||||
@@ -40,7 +54,12 @@ export default class TextEdit {
|
||||
})
|
||||
}
|
||||
|
||||
// 注册临时快捷键
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-16 16:27:02
|
||||
* @Desc: 注册临时快捷键
|
||||
*/
|
||||
registerTmpShortcut() {
|
||||
// 注册回车快捷键
|
||||
this.mindMap.keyCommand.addShortcut('Enter', () => {
|
||||
@@ -48,33 +67,33 @@ export default class TextEdit {
|
||||
})
|
||||
}
|
||||
|
||||
// 显示文本编辑框
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-13 22:15:56
|
||||
* @Desc: 显示文本编辑框
|
||||
*/
|
||||
show(node) {
|
||||
let rect = node._textData.node.node.getBoundingClientRect()
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.showEditText(node, rect)
|
||||
return
|
||||
}
|
||||
this.showEditTextBox(node, rect)
|
||||
this.showEditTextBox(node, node._textData.node.node.getBoundingClientRect())
|
||||
}
|
||||
|
||||
// 显示文本编辑框
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-13 22:13:02
|
||||
* @Desc: 显示文本编辑框
|
||||
*/
|
||||
showEditTextBox(node, rect) {
|
||||
this.mindMap.emit('before_show_text_edit')
|
||||
this.registerTmpShortcut()
|
||||
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;`
|
||||
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;`
|
||||
this.textEditNode.setAttribute('contenteditable', true)
|
||||
this.textEditNode.addEventListener('keyup', e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
document.body.appendChild(this.textEditNode)
|
||||
}
|
||||
let scale = this.mindMap.view.scale
|
||||
let lineHeight = node.style.merge('lineHeight')
|
||||
let fontSize = node.style.merge('fontSize')
|
||||
node.style.domText(this.textEditNode, scale)
|
||||
node.style.domText(this.textEditNode, this.mindMap.view.scale)
|
||||
this.textEditNode.innerHTML = node.nodeData.data.text
|
||||
.split(/\n/gim)
|
||||
.join('<br>')
|
||||
@@ -83,14 +102,16 @@ export default class TextEdit {
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth * scale + 'px'
|
||||
this.textEditNode.style.transform = `translateY(${-(lineHeight * fontSize - fontSize) / 2 * scale}px)`
|
||||
this.showTextEdit = true
|
||||
// 选中文本
|
||||
this.selectNodeText()
|
||||
}
|
||||
|
||||
// 选中文本
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-08-02 23:13:50
|
||||
* @Desc: 选中文本
|
||||
*/
|
||||
selectNodeText() {
|
||||
let selection = window.getSelection()
|
||||
let range = document.createRange()
|
||||
@@ -99,11 +120,12 @@ export default class TextEdit {
|
||||
selection.addRange(range)
|
||||
}
|
||||
|
||||
// 隐藏文本编辑框
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-04-24 13:48:16
|
||||
* @Desc: 隐藏文本编辑框
|
||||
*/
|
||||
hideEditTextBox() {
|
||||
if (this.mindMap.richText) {
|
||||
return this.mindMap.richText.hideEditText()
|
||||
}
|
||||
if (!this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
// 视图操作类
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 14:45:24
|
||||
* @Desc: 视图操作类
|
||||
*/
|
||||
class View {
|
||||
// 构造函数
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 14:45:40
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
this.opt = opt
|
||||
this.mindMap = this.opt.mindMap
|
||||
@@ -14,7 +24,12 @@ class View {
|
||||
this.bind()
|
||||
}
|
||||
|
||||
// 绑定
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-04-07 15:38:51
|
||||
* @Desc: 绑定
|
||||
*/
|
||||
bind() {
|
||||
// 快捷键
|
||||
this.mindMap.keyCommand.addShortcut('Control+=', () => {
|
||||
@@ -55,46 +70,22 @@ class View {
|
||||
})
|
||||
// 放大缩小视图
|
||||
this.mindMap.event.on('mousewheel', (e, dir) => {
|
||||
if (this.mindMap.opt.customHandleMousewheel && typeof this.mindMap.opt.customHandleMousewheel === 'function') {
|
||||
return this.mindMap.opt.customHandleMousewheel(e)
|
||||
}
|
||||
if (this.mindMap.opt.mousewheelAction === 'zoom') {
|
||||
switch (dir) {
|
||||
// 鼠标滚轮,向上和向左,都是缩小
|
||||
case 'up':
|
||||
case 'left':
|
||||
this.narrow()
|
||||
break
|
||||
// 鼠标滚轮,向下和向右,都是放大
|
||||
case 'down':
|
||||
case 'right':
|
||||
this.enlarge()
|
||||
break
|
||||
}
|
||||
// // 放大
|
||||
if (dir === 'down') {
|
||||
this.enlarge()
|
||||
} else {
|
||||
switch (dir){
|
||||
// 上移
|
||||
case 'down':
|
||||
this.translateY(-this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
// 下移
|
||||
case 'up':
|
||||
this.translateY(this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
// 右移
|
||||
case 'left':
|
||||
this.translateX(-this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
// 左移
|
||||
case 'right':
|
||||
this.translateX(this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
}
|
||||
// 缩小
|
||||
this.narrow()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前变换状态数据
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-22 18:30:24
|
||||
* @Desc: 获取当前变换状态数据
|
||||
*/
|
||||
getTransformData() {
|
||||
return {
|
||||
transform: this.mindMap.draw.transform(),
|
||||
@@ -108,7 +99,12 @@ class View {
|
||||
}
|
||||
}
|
||||
|
||||
// 动态设置变换状态数据
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-11-22 19:54:17
|
||||
* @Desc: 动态设置变换状态数据
|
||||
*/
|
||||
setTransformData(viewData) {
|
||||
if (viewData) {
|
||||
Object.keys(viewData.state).forEach(prop => {
|
||||
@@ -122,31 +118,55 @@ class View {
|
||||
}
|
||||
}
|
||||
|
||||
// 平移x方向
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-13 15:49:06
|
||||
* @Desc: 平移x方向
|
||||
*/
|
||||
translateX(step) {
|
||||
this.x += step
|
||||
this.transform()
|
||||
}
|
||||
|
||||
// 平移x方式到
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:03:53
|
||||
* @Desc: 平移x方式到
|
||||
*/
|
||||
translateXTo(x) {
|
||||
this.x = x
|
||||
this.transform()
|
||||
}
|
||||
|
||||
// 平移y方向
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2021-07-13 15:48:52
|
||||
* @Desc: 平移y方向
|
||||
*/
|
||||
translateY(step) {
|
||||
this.y += step
|
||||
this.transform()
|
||||
}
|
||||
|
||||
// 平移y方向到
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:04:10
|
||||
* @Desc: 平移y方向到
|
||||
*/
|
||||
translateYTo(y) {
|
||||
this.y = y
|
||||
this.transform()
|
||||
}
|
||||
|
||||
// 应用变换
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 17:13:14
|
||||
* @Desc: 应用变换
|
||||
*/
|
||||
transform() {
|
||||
this.mindMap.draw.transform({
|
||||
scale: this.scale,
|
||||
@@ -156,7 +176,11 @@ class View {
|
||||
this.mindMap.emit('view_data_change', this.getTransformData())
|
||||
}
|
||||
|
||||
// 恢复
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-11 17:41:35
|
||||
* @Desc: 恢复
|
||||
*/
|
||||
reset() {
|
||||
this.scale = 1
|
||||
this.x = 0
|
||||
@@ -164,7 +188,11 @@ class View {
|
||||
this.transform()
|
||||
}
|
||||
|
||||
// 缩小
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 17:10:34
|
||||
* @Desc: 缩小
|
||||
*/
|
||||
narrow() {
|
||||
if (this.scale - this.mindMap.opt.scaleRatio > 0.1) {
|
||||
this.scale -= this.mindMap.opt.scaleRatio
|
||||
@@ -175,14 +203,23 @@ class View {
|
||||
this.mindMap.emit('scale', this.scale)
|
||||
}
|
||||
|
||||
// 放大
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 17:10:41
|
||||
* @Desc: 放大
|
||||
*/
|
||||
enlarge() {
|
||||
this.scale += this.mindMap.opt.scaleRatio
|
||||
this.transform()
|
||||
this.mindMap.emit('scale', this.scale)
|
||||
}
|
||||
|
||||
// 设置缩放
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-12-09 16:31:59
|
||||
* @Desc: 设置缩放
|
||||
*/
|
||||
setScale(scale) {
|
||||
this.scale = scale
|
||||
this.transform()
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
import { Text, G } from '@svgdotjs/svg.js'
|
||||
import { degToRad, camelCaseToHyphen } from './utils'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 水印类
|
||||
class Watermark {
|
||||
constructor(opt = {}) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.lineSpacing = 0 // 水印行间距
|
||||
this.textSpacing = 0 // 行内水印间距
|
||||
this.angle = 0 // 旋转角度
|
||||
this.text = '' // 水印文字
|
||||
this.textStyle = {} // 水印文字样式
|
||||
this.watermarkDraw = this.mindMap.svg
|
||||
.group()
|
||||
.css({ 'pointer-events': 'none', 'user-select': 'none' })
|
||||
this.maxLong = Math.sqrt(
|
||||
Math.pow(this.mindMap.width, 2) + Math.pow(this.mindMap.height, 2)
|
||||
)
|
||||
this.updateWatermark(this.mindMap.opt.watermarkConfig || {})
|
||||
}
|
||||
|
||||
// 获取是否存在水印
|
||||
hasWatermark() {
|
||||
return !!this.text.trim()
|
||||
}
|
||||
|
||||
// 处理水印配置
|
||||
handleConfig({ text, lineSpacing, textSpacing, angle, textStyle }) {
|
||||
this.text = text === undefined ? '' : String(text).trim()
|
||||
this.lineSpacing =
|
||||
typeof lineSpacing === 'number' && lineSpacing > 0 ? lineSpacing : 100
|
||||
this.textSpacing =
|
||||
typeof textSpacing === 'number' && textSpacing > 0 ? textSpacing : 100
|
||||
this.angle =
|
||||
typeof angle === 'number' && angle >= 0 && angle <= 90 ? angle : 30
|
||||
this.textStyle = Object.assign(this.textStyle, textStyle || {})
|
||||
}
|
||||
|
||||
// 绘制水印
|
||||
// 非精确绘制,会绘制一些超出可视区域的水印
|
||||
draw() {
|
||||
this.watermarkDraw.clear()
|
||||
if (!this.hasWatermark()) {
|
||||
return
|
||||
}
|
||||
let x = 0
|
||||
while (x < this.mindMap.width) {
|
||||
this.drawText(x)
|
||||
x += this.lineSpacing / Math.sin(degToRad(this.angle))
|
||||
}
|
||||
|
||||
let yOffset =
|
||||
this.lineSpacing / Math.cos(degToRad(this.angle)) || this.lineSpacing
|
||||
let y = yOffset
|
||||
while (y < this.mindMap.height) {
|
||||
this.drawText(0, y)
|
||||
y += yOffset
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制文字
|
||||
drawText(x, y) {
|
||||
let long = Math.min(
|
||||
this.maxLong,
|
||||
(this.mindMap.width - x) / Math.cos(degToRad(this.angle))
|
||||
)
|
||||
let g = new G()
|
||||
let bbox = null
|
||||
let bboxWidth = 0
|
||||
let textHeight = -1
|
||||
while (bboxWidth < long) {
|
||||
let text = new Text().text(this.text)
|
||||
g.add(text)
|
||||
text.transform({
|
||||
translateX: bboxWidth
|
||||
})
|
||||
this.setTextStyle(text)
|
||||
bbox = g.bbox()
|
||||
if (textHeight === -1) {
|
||||
textHeight = bbox.height
|
||||
}
|
||||
bboxWidth = bbox.width + this.textSpacing
|
||||
}
|
||||
let params = {
|
||||
rotate: this.angle,
|
||||
origin: 'top left',
|
||||
translateX: x,
|
||||
translateY: textHeight
|
||||
}
|
||||
if (y !== undefined) {
|
||||
params.translateY = y + textHeight
|
||||
}
|
||||
g.transform(params)
|
||||
this.watermarkDraw.add(g)
|
||||
}
|
||||
|
||||
// 给文字设置样式
|
||||
setTextStyle(text) {
|
||||
Object.keys(this.textStyle).forEach(item => {
|
||||
let value = this.textStyle[item]
|
||||
if (item === 'color') {
|
||||
text.fill(value)
|
||||
} else {
|
||||
text.css(camelCaseToHyphen(item), value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 更新水印
|
||||
updateWatermark(config) {
|
||||
this.mindMap.opt.watermarkConfig = merge(this.mindMap.opt.watermarkConfig, config)
|
||||
this.handleConfig(config)
|
||||
this.draw()
|
||||
}
|
||||
}
|
||||
|
||||
Watermark.instanceName = 'watermark'
|
||||
|
||||
export default Watermark
|
||||
BIN
simple-mind-map/src/assets/blueSky.jpg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
simple-mind-map/src/assets/brainImpairedPink.jpg
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
simple-mind-map/src/assets/catalogOrganization.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
simple-mind-map/src/assets/classic.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
simple-mind-map/src/assets/classic2.jpg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
simple-mind-map/src/assets/classic3.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
simple-mind-map/src/assets/classic4.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
simple-mind-map/src/assets/classicBlue.jpg
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
simple-mind-map/src/assets/classicGreen.jpg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
simple-mind-map/src/assets/dark.jpg
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
simple-mind-map/src/assets/dark2.jpg
Normal file
|
After Width: | Height: | Size: 7.6 KiB |