Feature:支持节点富文本编辑

This commit is contained in:
wanglin2 2023-02-24 17:14:24 +08:00
parent 06dba79272
commit 8c114dac02
17 changed files with 1422 additions and 53 deletions

View File

@ -120,9 +120,7 @@ class MindMap {
// 注册插件 // 注册插件
MindMap.pluginList.forEach((plugin) => { MindMap.pluginList.forEach((plugin) => {
this[plugin.instanceName] = new plugin({ this.initPlugin(plugin)
mindMap: this
})
}) })
// 初始渲染 // 初始渲染
@ -374,14 +372,51 @@ class MindMap {
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值 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.pluginList = []
MindMap.usePlugin = (plugin) => { MindMap.usePlugin = (plugin, opt = {}) => {
plugin.pluginOpt = opt
MindMap.pluginList.push(plugin) MindMap.pluginList.push(plugin)
return MindMap return MindMap
} }
MindMap.hasPlugin = (plugin) => {
return MindMap.pluginList.findIndex((item) => {
return item === plugin
})
}
// 定义新主题 // 定义新主题
MindMap.defineTheme = (name, config = {}) => { MindMap.defineTheme = (name, config = {}) => {

View File

@ -1,19 +1,21 @@
{ {
"name": "simple-mind-map", "name": "simple-mind-map",
"version": "0.2.21", "version": "0.3.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"version": "0.2.21", "version": "0.3.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@svgdotjs/svg.js": "^3.0.16", "@svgdotjs/svg.js": "^3.0.16",
"canvg": "^3.0.7", "canvg": "^3.0.7",
"deepmerge": "^1.5.2", "deepmerge": "^1.5.2",
"eventemitter3": "^4.0.7", "eventemitter3": "^4.0.7",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"quill": "^1.3.6",
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {
@ -225,7 +227,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"optional": true,
"engines": { "engines": {
"node": ">= 0.6.0" "node": ">= 0.6.0"
} }
@ -251,6 +252,18 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/callsites": { "node_modules/callsites": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@ -294,6 +307,14 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
"engines": {
"node": ">=0.8"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -351,7 +372,6 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"optional": true,
"dependencies": { "dependencies": {
"utrie": "^1.0.2" "utrie": "^1.0.2"
} }
@ -373,6 +393,22 @@
} }
} }
}, },
"node_modules/deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"dependencies": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/deep-is": { "node_modules/deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -387,6 +423,21 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/define-properties": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"dependencies": {
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/doctrine": { "node_modules/doctrine": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -586,12 +637,22 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
}, },
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"node_modules/fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
},
"node_modules/fast-json-stable-stringify": { "node_modules/fast-json-stable-stringify": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@ -671,6 +732,32 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true "dev": true
}, },
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"node_modules/functions-have-names": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/glob": { "node_modules/glob": {
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@ -724,6 +811,17 @@
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true "dev": true
}, },
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -733,11 +831,46 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/has-property-descriptors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
"dependencies": {
"get-intrinsic": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"dependencies": {
"has-symbols": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/html2canvas": { "node_modules/html2canvas": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"optional": true,
"dependencies": { "dependencies": {
"css-line-break": "^2.1.0", "css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3" "text-segmentation": "^1.0.3"
@ -800,6 +933,35 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-date-object": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
"dependencies": {
"has-tostringtag": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -830,6 +992,21 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-regex": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -969,6 +1146,29 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"node_modules/object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -1030,6 +1230,11 @@
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
}, },
"node_modules/parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -1132,6 +1337,37 @@
} }
] ]
}, },
"node_modules/quill": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
"integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
"dependencies": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.1",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
}
},
"node_modules/quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"dependencies": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/quill/node_modules/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
},
"node_modules/raf": { "node_modules/raf": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
@ -1159,6 +1395,22 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
}, },
"node_modules/regexp.prototype.flags": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3",
"functions-have-names": "^1.2.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/regexpp": { "node_modules/regexpp": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
@ -1336,7 +1588,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"optional": true,
"dependencies": { "dependencies": {
"utrie": "^1.0.2" "utrie": "^1.0.2"
} }
@ -1389,7 +1640,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"optional": true,
"dependencies": { "dependencies": {
"base64-arraybuffer": "^1.0.2" "base64-arraybuffer": "^1.0.2"
} }
@ -1593,8 +1843,7 @@
"base64-arraybuffer": { "base64-arraybuffer": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
@ -1611,6 +1860,15 @@
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
}, },
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"callsites": { "callsites": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@ -1642,6 +1900,11 @@
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
} }
}, },
"clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
},
"color-convert": { "color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -1688,7 +1951,6 @@
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"optional": true,
"requires": { "requires": {
"utrie": "^1.0.2" "utrie": "^1.0.2"
} }
@ -1702,6 +1964,19 @@
"ms": "2.1.2" "ms": "2.1.2"
} }
}, },
"deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"requires": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
}
},
"deep-is": { "deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -1713,6 +1988,15 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
"integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ=="
}, },
"define-properties": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"requires": {
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
}
},
"doctrine": { "doctrine": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -1860,12 +2144,22 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
}, },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"fast-deep-equal": { "fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
},
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@ -1933,6 +2227,26 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true "dev": true
}, },
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"functions-have-names": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
},
"get-intrinsic": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.3"
}
},
"glob": { "glob": {
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@ -1971,17 +2285,45 @@
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
"dev": true "dev": true
}, },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": { "has-flag": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true "dev": true
}, },
"has-property-descriptors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
"requires": {
"get-intrinsic": "^1.1.1"
}
},
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
},
"has-tostringtag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
"requires": {
"has-symbols": "^1.0.2"
}
},
"html2canvas": { "html2canvas": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"optional": true,
"requires": { "requires": {
"css-line-break": "^2.1.0", "css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3" "text-segmentation": "^1.0.3"
@ -2029,6 +2371,23 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"requires": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
}
},
"is-date-object": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
"requires": {
"has-tostringtag": "^1.0.0"
}
},
"is-extglob": { "is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -2050,6 +2409,15 @@
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
"dev": true "dev": true
}, },
"is-regex": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
"requires": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
}
},
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -2168,6 +2536,20 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -2214,6 +2596,11 @@
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
}, },
"parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
},
"parent-module": { "parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -2275,6 +2662,36 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true "dev": true
}, },
"quill": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
"integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
"requires": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.1",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
},
"dependencies": {
"eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
}
}
},
"quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"requires": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
}
},
"raf": { "raf": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
@ -2302,6 +2719,16 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
}, },
"regexp.prototype.flags": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3",
"functions-have-names": "^1.2.2"
}
},
"regexpp": { "regexpp": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
@ -2419,7 +2846,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"optional": true,
"requires": { "requires": {
"utrie": "^1.0.2" "utrie": "^1.0.2"
} }
@ -2463,7 +2889,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"optional": true,
"requires": { "requires": {
"base64-arraybuffer": "^1.0.2" "base64-arraybuffer": "^1.0.2"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "simple-mind-map", "name": "simple-mind-map",
"version": "0.3.4", "version": "0.4.0",
"description": "一个简单的web在线思维导图", "description": "一个简单的web在线思维导图",
"authors": [ "authors": [
{ {
@ -28,8 +28,10 @@
"canvg": "^3.0.7", "canvg": "^3.0.7",
"deepmerge": "^1.5.2", "deepmerge": "^1.5.2",
"eventemitter3": "^4.0.7", "eventemitter3": "^4.0.7",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"quill": "^1.3.6",
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"keywords": [ "keywords": [

View File

@ -26,7 +26,7 @@ class Export {
} }
// 获取svg数据 // 获取svg数据
async getSvgData() { async getSvgData(domToImage) {
let { svg, svgHTML } = this.mindMap.getSvgData() let { svg, svgHTML } = this.mindMap.getSvgData()
// 把图片的url转换成data:url类型否则导出会丢失图片 // 把图片的url转换成data:url类型否则导出会丢失图片
let imageList = svg.find('image') let imageList = svg.find('image')
@ -36,9 +36,17 @@ class Export {
item.attr('href', imgData) item.attr('href', imgData)
}) })
await Promise.all(task) await Promise.all(task)
// 如果开启了富文本编辑需要把svg中的dom元素转换成图片
let nodeWithDomToImg = null
if (domToImage && this.mindMap.richText) {
let res = await this.mindMap.richText.handleSvgDomElements(svg)
nodeWithDomToImg = res.svg
svgHTML = res.svgHTML
}
return { return {
node: svg, node: svg,
str: svgHTML str: svgHTML,
nodeWithDomToImg
} }
} }
@ -123,7 +131,7 @@ class Export {
* 方法2.把svg的图片提取出来再挨个绘制到canvas里最后一起转换 * 方法2.把svg的图片提取出来再挨个绘制到canvas里最后一起转换
*/ */
async png() { async png() {
let { str } = await this.getSvgData() let { str } = await this.getSvgData(true)
// 转换成blob数据 // 转换成blob数据
let blob = new Blob([str], { let blob = new Blob([str], {
type: 'image/svg+xml' type: 'image/svg+xml'
@ -191,8 +199,21 @@ class Export {
} }
// 导出为svg // 导出为svg
async svg(name) { // domToImage是否将svg中的dom节点转换成图片的形式
let { node } = await this.getSvgData() // 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>`))
}
}
}
node.first().before(SVG(`<title>${name}</title>`)) node.first().before(SVG(`<title>${name}</title>`))
await this.drawBackgroundToSvg(node) await this.drawBackgroundToSvg(node)
let str = node.svg() let str = node.svg()

View File

@ -1,7 +1,7 @@
import Style from './Style' import Style from './Style'
import Shape from './Shape' import Shape from './Shape'
import { resizeImgSize, asyncRun, measureText } from './utils' import { resizeImgSize, asyncRun, measureText } from './utils'
import { Image, SVG, Circle, A, G, Rect, Text } from '@svgdotjs/svg.js' import { Image, SVG, Circle, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js'
import btnsSvg from './svg/btns' import btnsSvg from './svg/btns'
import iconsSvg from './svg/icons' import iconsSvg from './svg/icons'
@ -362,9 +362,42 @@ 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
}
}
// 创建文本节点 // 创建文本节点
createTextNode() { createTextNode() {
if (this.nodeData.data.richText) {
return this.createRichTextNode()
}
let g = new G() let g = new G()
let fontSize = this.getStyle( let fontSize = this.getStyle(
'fontSize', 'fontSize',
@ -405,6 +438,10 @@ class Node {
g.add(node) g.add(node)
}) })
let { width, height } = g.bbox() let { width, height } = g.bbox()
width = Math.ceil(width)
height = Math.ceil(height)
g.attr('data-width', width)
g.attr('data-height', height)
return { return {
node: g, node: g,
width, width,
@ -582,6 +619,7 @@ class Node {
} }
// 文字 // 文字
if (this._textData) { if (this._textData) {
this._textData.node.attr('data-offsetx', textContentOffsetX)
this._textData.node.x(textContentOffsetX).y(0) this._textData.node.x(textContentOffsetX).y(0)
textContentNested.add(this._textData.node) textContentNested.add(this._textData.node)
textContentOffsetX += this._textData.width + textContentItemMargin textContentOffsetX += this._textData.width + textContentItemMargin
@ -760,7 +798,9 @@ class Node {
if (this.nodeData.inserting) { if (this.nodeData.inserting) {
delete this.nodeData.inserting delete this.nodeData.inserting
this.active() this.active()
this.mindMap.emit('node_dblclick', this) setTimeout(() => {
this.mindMap.emit('node_dblclick', this)
}, 0)
} }
} }

View File

@ -668,6 +668,15 @@ class Render {
[prop]: value [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) this.setNodeDataRender(node, data)
// 更新了连线的样式 // 更新了连线的样式
if (lineStyleProps.includes(prop)) { if (lineStyleProps.includes(prop)) {
@ -788,9 +797,10 @@ class Render {
// 设置节点文本 // 设置节点文本
setNodeText(node, text) { setNodeText(node, text, richText) {
this.setNodeDataRender(node, { this.setNodeDataRender(node, {
text text,
richText
}) })
} }

View File

@ -0,0 +1,428 @@
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;background-color:#fff;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')
console.log(`node`, node, rect, originWidth, originHeight)
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.selectAll()
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())
}
// 格式化当前选中的文本
formatText(config = {}) {
if (!this.range && !this.lastRange) return
this.syncFormatToNodeConfig(config)
let rangeLost = !this.range
let range = rangeLost ? this.lastRange : this.range
this.quill.formatText(range.index, range.length, config)
if (rangeLost) {
this.quill.setSelection(this.lastRange.index, this.lastRange.length)
}
}
// 格式化指定范围的文本
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) {
if (!this.node) return
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'
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
})
this.mindMap.el.removeChild(div)
let imgNode = new SvgImage()
.load(canvas.toDataURL())
.size(canvas.width, canvas.height)
.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()
})
}
// 将所有节点转换成非富文本节点
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.reRender()
}
// 插件被移除前做的事情
beforePluginRemove() {
this.transformAllNodesToNormalNode()
}
}
RichText.instanceName = 'richText'
export default RichText

View File

@ -50,7 +50,12 @@ export default class TextEdit {
// 显示文本编辑框 // 显示文本编辑框
show(node) { show(node) {
this.showEditTextBox(node, node._textData.node.node.getBoundingClientRect()) let rect = node._textData.node.node.getBoundingClientRect()
if (this.mindMap.richText) {
this.mindMap.richText.showEditText(node, rect)
return
}
this.showEditTextBox(node, rect)
} }
// 显示文本编辑框 // 显示文本编辑框
@ -96,6 +101,9 @@ export default class TextEdit {
// 隐藏文本编辑框 // 隐藏文本编辑框
hideEditTextBox() { hideEditTextBox() {
if (this.mindMap.richText) {
return this.mindMap.richText.hideEditText()
}
if (!this.showTextEdit) { if (!this.showTextEdit) {
return return
} }

View File

@ -0,0 +1,9 @@
.ql-editor {
overflow: hidden;
padding: 0;
height: auto;
}
.ql-container {
height: auto;
}

View File

@ -34,7 +34,8 @@ export default {
watermarkTextSpacing: 'Text spacing', watermarkTextSpacing: 'Text spacing',
watermarkAngle: 'Angle', watermarkAngle: 'Angle',
watermarkTextOpacity: 'Text opacity', watermarkTextOpacity: 'Text opacity',
watermarkTextFontSize: 'Font size' watermarkTextFontSize: 'Font size',
isEnableNodeRichText: 'Enable node rich text editing'
}, },
color: { color: {
moreColor: 'More color' moreColor: 'More color'
@ -79,7 +80,13 @@ export default {
imageFile: 'Image file', imageFile: 'Image file',
svgFile: 'svg file', svgFile: 'svg file',
pdfFile: 'pdf file', pdfFile: 'pdf file',
tips: 'tips.smm and .json file can be import' tips: 'tips: .smm and .json file can be import',
domToImage: 'Whether to convert rich text nodes in svg into pictures',
pngTips: 'tips: Exporting pictures in rich text mode is time-consuming. It is recommended to export to svg format',
svgTips: 'tips: Exporting pictures in rich text mode is time-consuming',
transformingDomToImages: 'Converting nodes: ',
notifyTitle: 'Info',
notifyMessage: 'If the download is not triggered, check whether it is blocked by the browser'
}, },
fullscreen: { fullscreen: {
fullscreenShow: 'Full screen show', fullscreenShow: 'Full screen show',
@ -178,5 +185,9 @@ export default {
import: 'Import', import: 'Import',
export: 'Export', export: 'Export',
shortcutKey: 'Shortcut key' shortcutKey: 'Shortcut key'
},
edit: {
newFeatureNoticeTitle: 'New feature reminder',
newFeatureNoticeMessage: 'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.'
} }
} }

View File

@ -34,7 +34,8 @@ export default {
watermarkTextSpacing: '水印文字间距', watermarkTextSpacing: '水印文字间距',
watermarkAngle: '旋转角度', watermarkAngle: '旋转角度',
watermarkTextOpacity: '文字透明度', watermarkTextOpacity: '文字透明度',
watermarkTextFontSize: '文字字号' watermarkTextFontSize: '文字字号',
isEnableNodeRichText: '是否开启节点富文本编辑'
}, },
color: { color: {
moreColor: '更多颜色' moreColor: '更多颜色'
@ -79,7 +80,13 @@ export default {
imageFile: '图片文件', imageFile: '图片文件',
svgFile: 'svg文件', svgFile: 'svg文件',
pdfFile: 'pdf文件', pdfFile: 'pdf文件',
tips: 'tips.smm和.json文件可用于导入' tips: 'tips.smm和.json文件可用于导入',
domToImage: '是否将svg中富文本节点转换成图片',
pngTips: 'tips富文本模式导出图片非常耗时建议导出为svg格式',
svgTips: 'tips富文本模式导出图片非常耗时',
transformingDomToImages: '正在转换节点:',
notifyTitle: '消息',
notifyMessage: '如果没有触发下载,请检查是否被浏览器拦截了'
}, },
fullscreen: { fullscreen: {
fullscreenShow: '全屏查看', fullscreenShow: '全屏查看',
@ -178,5 +185,9 @@ export default {
import: '导入', import: '导入',
export: '导出', export: '导出',
shortcutKey: '快捷键' shortcutKey: '快捷键'
},
edit: {
newFeatureNoticeTitle: '新特性提醒',
newFeatureNoticeMessage: '本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。'
} }
} }

View File

@ -429,6 +429,11 @@
">{{ $t('baseStyle.enableFreeDrag') }}</el-checkbox> ">{{ $t('baseStyle.enableFreeDrag') }}</el-checkbox>
</div> </div>
</div> </div>
<div class="row">
<div class="rowItem">
<el-checkbox v-model="enableNodeRichText" @change="enableNodeRichTextChange">{{ this.$t('baseStyle.isEnableNodeRichText') }}</el-checkbox>
</div>
</div>
</div> </div>
</Sidebar> </Sidebar>
</template> </template>
@ -439,7 +444,7 @@ import Color from './Color'
import { lineWidthList, lineStyleList, backgroundRepeatList, backgroundPositionList, backgroundSizeList } from '@/config' import { lineWidthList, lineStyleList, backgroundRepeatList, backgroundPositionList, backgroundSizeList } from '@/config'
import ImgUpload from '@/components/ImgUpload' import ImgUpload from '@/components/ImgUpload'
import { storeConfig } from '@/api' import { storeConfig } from '@/api'
import { mapState } from 'vuex' import { mapState, mapMutations } from 'vuex'
/** /**
* @Author: 王林 * @Author: 王林
@ -502,11 +507,12 @@ export default {
fontSize: 1 fontSize: 1
} }
}, },
updateWatermarkTimer: null updateWatermarkTimer: null,
enableNodeRichText: true
} }
}, },
computed: { computed: {
...mapState(['activeSidebar']), ...mapState(['activeSidebar', 'localConfig']),
lineStyleList() { lineStyleList() {
return lineStyleList[this.$i18n.locale] || lineStyleList.zh return lineStyleList[this.$i18n.locale] || lineStyleList.zh
@ -533,7 +539,12 @@ export default {
} }
} }
}, },
created () {
this.enableNodeRichText = this.localConfig.openNodeRichText
},
methods: { methods: {
...mapMutations(['setLocalConfig']),
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-05-05 14:02:12 * @Date: 2021-05-05 14:02:12
@ -668,6 +679,13 @@ export default {
this.watermarkConfig.text = '' this.watermarkConfig.text = ''
} }
this.updateWatermarkConfig() this.updateWatermarkConfig()
},
//
enableNodeRichTextChange(e) {
this.setLocalConfig({
openNodeRichText: e
})
} }
} }
} }

View File

@ -11,6 +11,7 @@
<Structure :mindMap="mindMap"></Structure> <Structure :mindMap="mindMap"></Structure>
<ShortcutKey></ShortcutKey> <ShortcutKey></ShortcutKey>
<Contextmenu v-if="mindMap" :mindMap="mindMap"></Contextmenu> <Contextmenu v-if="mindMap" :mindMap="mindMap"></Contextmenu>
<RichTextToolbar v-if="mindMap" :mindMap="mindMap"></RichTextToolbar>
<NodeNoteContentShow <NodeNoteContentShow
v-if="mindMap" v-if="mindMap"
:mindMap="mindMap" :mindMap="mindMap"
@ -28,6 +29,7 @@ import KeyboardNavigation from 'simple-mind-map/src/KeyboardNavigation.js'
import Export from 'simple-mind-map/src/Export.js' import Export from 'simple-mind-map/src/Export.js'
import Drag from 'simple-mind-map/src/Drag.js' import Drag from 'simple-mind-map/src/Drag.js'
import Select from 'simple-mind-map/src/Select.js' import Select from 'simple-mind-map/src/Select.js'
import RichText from 'simple-mind-map/src/RichText.js'
import Outline from './Outline' import Outline from './Outline'
import Style from './Style' import Style from './Style'
import BaseStyle from './BaseStyle' import BaseStyle from './BaseStyle'
@ -37,6 +39,7 @@ import Count from './Count'
import NavigatorToolbar from './NavigatorToolbar' import NavigatorToolbar from './NavigatorToolbar'
import ShortcutKey from './ShortcutKey' import ShortcutKey from './ShortcutKey'
import Contextmenu from './Contextmenu' import Contextmenu from './Contextmenu'
import RichTextToolbar from './RichTextToolbar'
import NodeNoteContentShow from './NodeNoteContentShow.vue' import NodeNoteContentShow from './NodeNoteContentShow.vue'
import { getData, storeData, storeConfig } from '@/api' import { getData, storeData, storeConfig } from '@/api'
import Navigator from './Navigator.vue' import Navigator from './Navigator.vue'
@ -76,6 +79,7 @@ export default {
NavigatorToolbar, NavigatorToolbar,
ShortcutKey, ShortcutKey,
Contextmenu, Contextmenu,
RichTextToolbar,
NodeNoteContentShow, NodeNoteContentShow,
Navigator, Navigator,
NodeImgPreview, NodeImgPreview,
@ -91,10 +95,21 @@ export default {
}, },
computed: { computed: {
...mapState({ ...mapState({
isZenMode: state => state.localConfig.isZenMode isZenMode: state => state.localConfig.isZenMode,
openNodeRichText: state => state.localConfig.openNodeRichText,
}) })
}, },
watch: {
openNodeRichText() {
if (this.openNodeRichText) {
this.addRichTextPlugin()
} else {
this.removeRichTextPlugin()
}
}
},
mounted() { mounted() {
this.showNewFeatureInfo()
this.getData() this.getData()
this.init() this.init()
this.$bus.$on('execCommand', this.execCommand) this.$bus.$on('execCommand', this.execCommand)
@ -263,6 +278,7 @@ export default {
}, },
...(config || {}) ...(config || {})
}) })
if (this.openNodeRichText) this.addRichTextPlugin()
this.mindMap.keyCommand.addShortcut('Control+s', () => { this.mindMap.keyCommand.addShortcut('Control+s', () => {
this.manualSave() this.manualSave()
}) })
@ -279,7 +295,9 @@ export default {
'svg_mousedown', 'svg_mousedown',
'mouseup', 'mouseup',
'mode_change', 'mode_change',
'node_tree_render_end' 'node_tree_render_end',
'rich_text_selection_change',
'transforming-dom-to-images'
].forEach(event => { ].forEach(event => {
this.mindMap.on(event, (...args) => { this.mindMap.on(event, (...args) => {
this.$bus.$emit(event, ...args) this.$bus.$emit(event, ...args)
@ -331,6 +349,32 @@ export default {
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
},
//
showNewFeatureInfo() {
let showed = localStorage.getItem('SIMPLE_MIND_MAP_NEW_FEATURE_TIP_1')
if (!showed) {
this.$notify.info({
title: this.$t('edit.newFeatureNoticeTitle'),
message: this.$t('edit.newFeatureNoticeMessage'),
duration: 0,
onClose: () => {
localStorage.setItem('SIMPLE_MIND_MAP_NEW_FEATURE_TIP_1', true)
}
})
}
},
//
addRichTextPlugin() {
if (!this.mindMap) return
this.mindMap.addPlugin(RichText)
},
//
removeRichTextPlugin() {
this.mindMap.removePlugin(RichText)
} }
} }
} }

View File

@ -4,6 +4,10 @@
:title="$t('export.title')" :title="$t('export.title')"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="700px" width="700px"
v-loading.fullscreen.lock="loading"
:element-loading-text="loadingText"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
> >
<div> <div>
<div class="nameInputBox"> <div class="nameInputBox">
@ -19,6 +23,12 @@
style="margin-left: 12px" style="margin-left: 12px"
>{{ $t('export.include') }}</el-checkbox >{{ $t('export.include') }}</el-checkbox
> >
<el-checkbox
v-show="['svg'].includes(exportType)"
v-model="domToImage"
style="margin-left: 12px"
>{{ $t('export.domToImage') }}</el-checkbox
>
</div> </div>
<el-radio-group v-model="exportType" size="mini"> <el-radio-group v-model="exportType" size="mini">
<el-radio-button label="smm" <el-radio-button label="smm"
@ -38,6 +48,8 @@
> >
</el-radio-group> </el-radio-group>
<div class="tip">{{ $t('export.tips') }}</div> <div class="tip">{{ $t('export.tips') }}</div>
<div class="tip warning" v-if="openNodeRichText && ['png', 'pdf'].includes(exportType)">{{ $t('export.pngTips') }}</div>
<div class="tip warning" v-if="openNodeRichText && exportType === 'svg' && domToImage">{{ $t('export.svgTips') }}</div>
</div> </div>
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="cancel">{{ $t('dialog.cancel') }}</el-button> <el-button @click="cancel">{{ $t('dialog.cancel') }}</el-button>
@ -49,6 +61,8 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex'
/** /**
* @Author: 王林 * @Author: 王林
* @Date: 2021-06-24 22:53:54 * @Date: 2021-06-24 22:53:54
@ -61,13 +75,28 @@ export default {
dialogVisible: false, dialogVisible: false,
exportType: 'smm', exportType: 'smm',
fileName: '思维导图', fileName: '思维导图',
widthConfig: true widthConfig: true,
domToImage: false,
loading: false,
loadingText: ''
} }
}, },
computed: {
...mapState({
openNodeRichText: state => state.localConfig.openNodeRichText,
})
},
created() { created() {
this.$bus.$on('showExport', () => { this.$bus.$on('showExport', () => {
this.dialogVisible = true this.dialogVisible = true
}) })
this.$bus.$on('transforming-dom-to-images', (index, len) => {
this.loading = true
this.loadingText = `${this.$t('export.transformingDomToImages')}${index + 1}/${len}`
if (index >= len - 1) {
this.loading = false
}
})
}, },
methods: { methods: {
/** /**
@ -85,16 +114,31 @@ export default {
* @Desc: 确定 * @Desc: 确定
*/ */
confirm() { confirm() {
this.$bus.$emit( if (this.exportType === 'svg') {
'export', this.$bus.$emit(
this.exportType, 'export',
true, this.exportType,
this.fileName, true,
this.widthConfig this.fileName,
) this.domToImage,
`* {
margin: 0;
padding: 0;
box-sizing: border-box;
}`
)
} else {
this.$bus.$emit(
'export',
this.exportType,
true,
this.fileName,
this.widthConfig
)
}
this.$notify.info({ this.$notify.info({
title: '消息', title: this.$t('export.notifyTitle'),
message: '如果没有触发下载,请检查是否被浏览器拦截了' message: this.$t('export.notifyMessage')
}) })
this.cancel() this.cancel()
} }
@ -114,6 +158,10 @@ export default {
.tip { .tip {
margin-top: 10px; margin-top: 10px;
&.warning {
color: #F56C6C;
}
} }
} }
</style> </style>

View File

@ -0,0 +1,256 @@
<template>
<div
class="richTextToolbar"
ref="richTextToolbar"
:style="style"
@click.stop.passive
v-show="showRichTextToolbar"
>
<el-tooltip content="加粗" placement="top">
<div class="btn" :class="{ active: formatInfo.bold }" @click="toggleBold">
<span class="icon iconfont iconzitijiacu"></span>
</div>
</el-tooltip>
<el-tooltip content="斜体" placement="top">
<div
class="btn"
:class="{ active: formatInfo.italic }"
@click="toggleItalic"
>
<span class="icon iconfont iconzitixieti"></span>
</div>
</el-tooltip>
<el-tooltip content="下划线" placement="top">
<div
class="btn"
:class="{ active: formatInfo.underline }"
@click="toggleUnderline"
>
<span class="icon iconfont iconzitixiahuaxian"></span>
</div>
</el-tooltip>
<el-tooltip content="删除线" placement="top">
<div
class="btn"
:class="{ active: formatInfo.strike }"
@click="toggleStrike"
>
<span class="icon iconfont iconshanchuxian"></span>
</div>
</el-tooltip>
<el-tooltip content="字体" placement="top">
<el-popover placement="bottom" trigger="hover">
<div class="fontOptionsList">
<div
class="fontOptionItem"
v-for="item in fontFamilyList"
:key="item.value"
:style="{ fontFamily: item.value }"
:class="{ active: formatInfo.font === item.value }"
@click="changeFontFamily(item.value)"
>
{{ item.name }}
</div>
</div>
<div class="btn" slot="reference">
<span class="icon iconfont iconxingzhuang-wenzi"></span>
</div>
</el-popover>
</el-tooltip>
<el-tooltip content="字号" placement="top">
<el-popover placement="bottom" trigger="hover">
<div class="fontOptionsList">
<div
class="fontOptionItem"
v-for="item in fontSizeList"
:key="item"
:style="{ fontSize: item + 'px' }"
:class="{ active: formatInfo.size === item + 'px' }"
@click="changeFontSize(item)"
>
{{ item }}px
</div>
</div>
<div class="btn" slot="reference" :style="{ color: formatInfo.color }">
<span class="icon iconfont iconcase fontColor"></span>
</div>
</el-popover>
</el-tooltip>
<el-tooltip content="字体颜色" placement="top">
<el-popover placement="bottom" trigger="hover">
<Color :color="fontColor" @change="changeFontColor"></Color>
<div class="btn" slot="reference">
<span class="icon iconfont iconzitiyanse"></span>
</div>
</el-popover>
</el-tooltip>
</div>
</template>
<script>
import { fontFamilyList, fontSizeList } from '@/config'
import Color from './Color'
export default {
name: 'RichTextToolbar',
components: {
Color
},
props: {
mindMap: {
type: Object
}
},
data() {
return {
fontSizeList,
showRichTextToolbar: false,
style: {
left: 0,
top: 0
},
fontColor: '',
formatInfo: {}
}
},
computed: {
fontFamilyList() {
return fontFamilyList[this.$i18n.locale] || fontFamilyList.zh
}
},
created() {
this.$bus.$on('rich_text_selection_change', this.onRichTextSelectionChange)
},
mounted() {
document.body.append(this.$refs.richTextToolbar)
},
beforeDestroy() {
this.$bus.$off('rich_text_selection_change', this.onRichTextSelectionChange)
},
methods: {
onRichTextSelectionChange(hasRange, rect, formatInfo) {
if (hasRange) {
this.style.left = rect.left + rect.width / 2 + 'px'
this.style.top = rect.top - 60 + 'px'
this.formatInfo = { ...(formatInfo || {}) }
}
this.showRichTextToolbar = hasRange
},
toggleBold() {
this.formatInfo.bold = !this.formatInfo.bold
this.mindMap.richText.formatText({
bold: this.formatInfo.bold
})
},
toggleItalic() {
this.formatInfo.italic = !this.formatInfo.italic
this.mindMap.richText.formatText({
italic: this.formatInfo.italic
})
},
toggleUnderline() {
this.formatInfo.underline = !this.formatInfo.underline
this.mindMap.richText.formatText({
underline: this.formatInfo.underline
})
},
toggleStrike() {
this.formatInfo.strike = !this.formatInfo.strike
this.mindMap.richText.formatText({
strike: this.formatInfo.strike
})
},
changeFontFamily(font) {
this.formatInfo.font = font
this.mindMap.richText.formatText({
font
})
},
changeFontSize(size) {
this.formatInfo.size = size
this.mindMap.richText.formatText({
size: size + 'px'
})
},
changeFontColor(color) {
this.formatInfo.color = color
this.mindMap.richText.formatText({
color
})
}
}
}
</script>
<style lang="less" scoped>
.richTextToolbar {
position: fixed;
z-index: 99999;
height: 55px;
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 8px;
box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.06);
display: flex;
align-items: center;
transform: translateX(-50%);
.btn {
width: 55px;
height: 55px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover {
background-color: #eefbed;
}
&.active {
color: #12bb37;
}
.icon {
font-size: 20px;
&.fontColor {
font-size: 26px;
}
}
}
}
.fontOptionsList {
width: 150px;
.fontOptionItem {
height: 30px;
width: 100%;
display: flex;
align-items: center;
cursor: pointer;
&:hover {
background-color: #f7f7f7;
}
&.active {
color: #12bb37;
}
}
}
</style>

View File

@ -45,6 +45,7 @@
:key="item" :key="item"
:label="item" :label="item"
:value="item" :value="item"
:style="{ fontSize: item + 'px' }"
> >
</el-option> </el-option>
</el-select> </el-select>
@ -125,7 +126,7 @@
<el-popover <el-popover
ref="popover" ref="popover"
placement="bottom" placement="bottom"
trigger="click" trigger="hover"
:disabled="checkDisabled('color')" :disabled="checkDisabled('color')"
> >
<Color :color="style.color" @change="changeFontColor"></Color> <Color :color="style.color" @change="changeFontColor"></Color>
@ -133,7 +134,7 @@
<el-popover <el-popover
ref="popover2" ref="popover2"
placement="bottom" placement="bottom"
trigger="click" trigger="hover"
:disabled="checkDisabled('textDecoration')" :disabled="checkDisabled('textDecoration')"
> >
<el-radio-group <el-radio-group
@ -167,7 +168,7 @@
<el-popover <el-popover
ref="popover3" ref="popover3"
placement="bottom" placement="bottom"
trigger="click" trigger="hover"
:disabled="checkDisabled('borderColor')" :disabled="checkDisabled('borderColor')"
> >
<Color <Color
@ -250,7 +251,7 @@
<el-popover <el-popover
ref="popover4" ref="popover4"
placement="bottom" placement="bottom"
trigger="click" trigger="hover"
:disabled="checkDisabled('fillColor')" :disabled="checkDisabled('fillColor')"
> >
<Color :color="style.fillColor" @change="changeFillColor"></Color> <Color :color="style.fillColor" @change="changeFillColor"></Color>
@ -294,7 +295,7 @@
<el-popover <el-popover
ref="popover5" ref="popover5"
placement="bottom" placement="bottom"
trigger="click" trigger="hover"
:disabled="checkDisabled('lineColor')" :disabled="checkDisabled('lineColor')"
> >
<Color :color="style.lineColor" @change="changeLineColor"></Color> <Color :color="style.lineColor" @change="changeLineColor"></Color>

View File

@ -11,7 +11,9 @@ const store = new Vuex.Store({
isHandleLocalFile: false, // 是否操作的是本地文件 isHandleLocalFile: false, // 是否操作的是本地文件
localConfig: { localConfig: {
// 本地配置 // 本地配置
isZenMode: false // 是否是禅模式 isZenMode: false, // 是否是禅模式
// 是否开启节点富文本
openNodeRichText: true
}, },
activeSidebar: '' // 当前显示的侧边栏 activeSidebar: '' // 当前显示的侧边栏
}, },