diff --git a/README.md b/README.md index ecc47c1a..1be119ae 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![GitHub forks](https://img.shields.io/github/forks/wanglin2/mind-map)](https://github.com/wanglin2/mind-map/network/members) ![license](https://img.shields.io/npm/l/express.svg) -> 一个简单&强大的Web思维导图 +> 一个简单&强大的 Web 思维导图 本项目包含两部分: @@ -27,7 +27,7 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。 > 客户端版本会落后于在线版本,尝试最新功能请优先使用在线版。 -> 如果访问github比较慢,可以使用:http://lxqnsys.com/simple-mind-map/#/index +> 如果访问 github 比较慢,可以使用:http://lxqnsys.com/simple-mind-map/#/index # 特性 @@ -37,7 +37,7 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。 - [x] 节点内容支持文本(普通文本、富文本)、图片、图标、超链接、备注、标签、概要、数学公式 - [x] 节点支持拖拽(拖拽移动、自由调整)、多种节点形状,支持使用 DDM 完全自定义节点内容 - [x] 支持画布拖动、缩放 -- [x] 支持鼠标按键拖动选择和Ctrl+左键两种多选节点方式 +- [x] 支持鼠标按键拖动选择和 Ctrl+左键两种多选节点方式 - [x] 支持导出为`json`、`png`、`svg`、`pdf`、`markdown`、`xmind`,支持从`json`、`xmind`、`markdown`导入 - [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印、滚动条 - [x] 提供丰富的配置,满足各种场景各种使用习惯 @@ -51,7 +51,7 @@ npm i simple-mind-map # 使用 -提供一个宽高不为0的容器元素: +提供一个宽高不为 0 的容器元素: ```html
@@ -72,13 +72,13 @@ npm i simple-mind-map import MindMap from "simple-mind-map"; const mindMap = new MindMap({ - el: document.getElementById('mindMapContainer'), + el: document.getElementById("mindMapContainer"), data: { - "data": { - "text": "根节点" + data: { + text: "根节点", }, - "children": [] - } + children: [], + }, }); ``` @@ -88,7 +88,7 @@ const mindMap = new MindMap({ # License -请尽情享受开源:[MIT](./LICENSE) +[MIT](./LICENSE) # 微信交流群 @@ -244,4 +244,12 @@ const mindMap = new MindMap({ 继龙 -

\ No newline at end of file + + + + + + + 易空小易 + +

diff --git a/simple-mind-map/full.js b/simple-mind-map/full.js index 68f47c64..5c107526 100644 --- a/simple-mind-map/full.js +++ b/simple-mind-map/full.js @@ -28,6 +28,7 @@ MindMap.iconList = icons.nodeIconList MindMap.constants = constants MindMap.themes = themes MindMap.defaultTheme = defaultTheme +MindMap.version = '0.9.2' MindMap.usePlugin(MiniMap) .usePlugin(Watermark) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index efa52dbb..2bde23ce 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -438,14 +438,22 @@ class MindMap { } // 添加必要的样式 clone.add(SVG(``)) - // 修正关联线箭头marker的id - const markerList = svg.find('marker') - if (markerList && markerList.length > 0) { - const id = markerList[0].attr('id') - clone.find('marker').forEach(item => { - item.attr('id', id) - }) - } + // 修正defs里定义的元素的id,因为clone时defs里的元素的id会继续递增,导致和内容中引用的id对不上 + const defs = svg.find('defs') + const defs2 = clone.find('defs') + defs.forEach((def, defIndex) => { + const def2 = defs2[defIndex] + if (!def2) return + const children = def.children() + const children2 = def2.children() + for (let i = 0; i < children.length; i++) { + const child = children[i] + const child2 = children2[i] + if (child && child2) { + child2.attr('id', child.attr('id')) + } + } + }) // 恢复原先的大小和变换信息 svg.size(origWidth, origHeight) draw.transform(origTransform) diff --git a/simple-mind-map/package-lock.json b/simple-mind-map/package-lock.json index 975f6cd3..e3949191 100644 --- a/simple-mind-map/package-lock.json +++ b/simple-mind-map/package-lock.json @@ -1,20 +1,20 @@ { "name": "simple-mind-map", - "version": "0.9.1", + "version": "0.9.2", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.9.1", + "version": "0.9.2", "license": "MIT", "dependencies": { "@svgdotjs/svg.js": "^3.0.16", "deepmerge": "^1.5.2", "eventemitter3": "^4.0.7", - "jspdf": "^2.5.1", "jszip": "^3.10.1", "katex": "^0.16.8", "mdast-util-from-markdown": "^1.3.0", + "pdf-lib": "^1.17.1", "quill": "^1.3.6", "tern": "^0.24.3", "uuid": "^9.0.0", @@ -27,17 +27,6 @@ "prettier": "^2.7.1" } }, - "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@eslint/eslintrc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", @@ -129,6 +118,22 @@ "node": ">= 8" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dependencies": { + "pako": "^1.0.10" + } + }, "node_modules/@svgdotjs/svg.js": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.1.2.tgz", @@ -159,12 +164,6 @@ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, - "node_modules/@types/raf": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", - "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "optional": true - }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -267,31 +266,11 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "optional": true, - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -320,17 +299,6 @@ "concat-map": "0.0.1" } }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "bin": { - "btoa": "bin/btoa.js" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -375,31 +343,6 @@ "node": ">=6" } }, - "node_modules/canvg": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", - "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@types/raf": "^3.4.0", - "core-js": "^3.8.3", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.7", - "rgbcolor": "^1.0.1", - "stackblur-canvas": "^2.0.0", - "svg-pathdata": "^6.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/canvg/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "optional": true - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -464,17 +407,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/core-js": { - "version": "3.33.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz", - "integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==", - "hasInstallScript": true, - "optional": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -494,15 +426,6 @@ "node": ">= 8" } }, - "node_modules/css-line-break": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", - "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "optional": true, - "dependencies": { - "utrie": "^1.0.2" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -604,12 +527,6 @@ "node": ">=6.0.0" } }, - "node_modules/dompurify": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", - "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", - "optional": true - }, "node_modules/enhanced-resolve": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz", @@ -858,11 +775,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fflate": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", - "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1059,19 +971,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/html2canvas": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", - "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "optional": true, - "dependencies": { - "css-line-break": "^2.1.0", - "text-segmentation": "^1.0.3" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1272,23 +1171,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/jspdf": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz", - "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==", - "dependencies": { - "@babel/runtime": "^7.14.0", - "atob": "^2.1.2", - "btoa": "^1.2.1", - "fflate": "^0.4.8" - }, - "optionalDependencies": { - "canvg": "^3.0.6", - "core-js": "^3.6.0", - "dompurify": "^2.2.0", - "html2canvas": "^1.0.0-rc.5" - } - }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -2014,11 +1896,16 @@ "node": ">=8" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "optional": true + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -2113,15 +2000,6 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "optional": true, - "dependencies": { - "performance-now": "^2.1.0" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2144,11 +2022,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -2196,15 +2069,6 @@ "node": ">=0.10.0" } }, - "node_modules/rgbcolor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", - "optional": true, - "engines": { - "node": ">= 0.8.15" - } - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2331,15 +2195,6 @@ "node": ">= 6" } }, - "node_modules/stackblur-canvas": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.6.0.tgz", - "integrity": "sha512-8S1aIA+UoF6erJYnglGPug6MaHYGo1Ot7h5fuXx4fUPvcvQfcdw2o/ppCse63+eZf8PPidSu4v1JnmEVtEDnpg==", - "optional": true, - "engines": { - "node": ">=0.1.14" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2384,15 +2239,6 @@ "node": ">=8" } }, - "node_modules/svg-pathdata": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "optional": true, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/tapable": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", @@ -2437,21 +2283,17 @@ "node": ">=0.10.0" } }, - "node_modules/text-segmentation": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", - "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", - "optional": true, - "dependencies": { - "utrie": "^1.0.2" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2502,15 +2344,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/utrie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", - "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", - "optional": true, - "dependencies": { - "base64-arraybuffer": "^1.0.2" - } - }, "node_modules/uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", @@ -2670,14 +2503,6 @@ } }, "dependencies": { - "@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, "@eslint/eslintrc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", @@ -2744,6 +2569,22 @@ "fastq": "^1.6.0" } }, + "@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "requires": { + "pako": "^1.0.6" + } + }, + "@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "requires": { + "pako": "^1.0.10" + } + }, "@svgdotjs/svg.js": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.1.2.tgz", @@ -2770,12 +2611,6 @@ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, - "@types/raf": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", - "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "optional": true - }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -2847,22 +2682,11 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "optional": true - }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2877,11 +2701,6 @@ "concat-map": "0.0.1" } }, - "btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" - }, "buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -2906,30 +2725,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "canvg": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", - "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", - "optional": true, - "requires": { - "@babel/runtime": "^7.12.5", - "@types/raf": "^3.4.0", - "core-js": "^3.8.3", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.7", - "rgbcolor": "^1.0.1", - "stackblur-canvas": "^2.0.0", - "svg-pathdata": "^6.0.3" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "optional": true - } - } - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2975,12 +2770,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "core-js": { - "version": "3.33.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz", - "integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==", - "optional": true - }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -2997,15 +2786,6 @@ "which": "^2.0.1" } }, - "css-line-break": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", - "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "optional": true, - "requires": { - "utrie": "^1.0.2" - } - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3074,12 +2854,6 @@ "esutils": "^2.0.2" } }, - "dompurify": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", - "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", - "optional": true - }, "enhanced-resolve": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz", @@ -3273,11 +3047,6 @@ "reusify": "^1.0.4" } }, - "fflate": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", - "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3420,16 +3189,6 @@ "has-symbols": "^1.0.2" } }, - "html2canvas": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", - "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "optional": true, - "requires": { - "css-line-break": "^2.1.0", - "text-segmentation": "^1.0.3" - } - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3566,21 +3325,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "jspdf": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz", - "integrity": "sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==", - "requires": { - "@babel/runtime": "^7.14.0", - "atob": "^2.1.2", - "btoa": "^1.2.1", - "canvg": "^3.0.6", - "core-js": "^3.6.0", - "dompurify": "^2.2.0", - "fflate": "^0.4.8", - "html2canvas": "^1.0.0-rc.5" - } - }, "jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -4013,11 +3757,16 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "optional": true + "pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "requires": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } }, "prelude-ls": { "version": "1.2.1", @@ -4082,15 +3831,6 @@ "fast-diff": "1.1.2" } }, - "raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "optional": true, - "requires": { - "performance-now": "^2.1.0" - } - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -4113,11 +3853,6 @@ "util-deprecate": "~1.0.1" } }, - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -4146,12 +3881,6 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "rgbcolor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", - "optional": true - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4234,12 +3963,6 @@ } } }, - "stackblur-canvas": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.6.0.tgz", - "integrity": "sha512-8S1aIA+UoF6erJYnglGPug6MaHYGo1Ot7h5fuXx4fUPvcvQfcdw2o/ppCse63+eZf8PPidSu4v1JnmEVtEDnpg==", - "optional": true - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4272,12 +3995,6 @@ "has-flag": "^4.0.0" } }, - "svg-pathdata": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "optional": true - }, "tapable": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", @@ -4309,21 +4026,17 @@ } } }, - "text-segmentation": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", - "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", - "optional": true, - "requires": { - "utrie": "^1.0.2" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4361,15 +4074,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "utrie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", - "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", - "optional": true, - "requires": { - "base64-arraybuffer": "^1.0.2" - } - }, "uuid": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index c57e0575..43711733 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -31,10 +31,10 @@ "@svgdotjs/svg.js": "^3.0.16", "deepmerge": "^1.5.2", "eventemitter3": "^4.0.7", - "jspdf": "^2.5.1", "jszip": "^3.10.1", "katex": "^0.16.8", "mdast-util-from-markdown": "^1.3.0", + "pdf-lib": "^1.17.1", "quill": "^1.3.6", "tern": "^0.24.3", "uuid": "^9.0.0", diff --git a/simple-mind-map/src/core/command/Command.js b/simple-mind-map/src/core/command/Command.js index d1638dee..0fa38cba 100644 --- a/simple-mind-map/src/core/command/Command.js +++ b/simple-mind-map/src/core/command/Command.js @@ -1,4 +1,10 @@ -import { copyRenderTree, simpleDeepClone, throttle } from '../../utils' +import { + copyRenderTree, + simpleDeepClone, + throttle, + isSameObject, + transformTreeDataToObject +} from '../../utils' // 命令类 class Command { @@ -84,15 +90,14 @@ class Command { if (this.mindMap.opt.readonly) { return } - let data = this.getCopyData() + const lastData = + this.history.length > 0 ? this.history[this.history.length - 1] : null + const data = this.getCopyData() // 此次数据和上次一样则不重复添加 - if ( - this.history.length > 0 && - JSON.stringify(this.history[this.history.length - 1]) === - JSON.stringify(data) - ) { + if (lastData && JSON.stringify(lastData) === JSON.stringify(data)) { return } + this.emitDataUpdatesEvent(lastData, data) // 删除当前历史指针后面的数据 this.history = this.history.slice(0, this.activeHistoryIndex + 1) this.history.push(simpleDeepClone(data)) @@ -115,13 +120,15 @@ class Command { return } if (this.activeHistoryIndex - step >= 0) { + const lastData = this.history[this.activeHistoryIndex] this.activeHistoryIndex -= step this.mindMap.emit( 'back_forward', this.activeHistoryIndex, this.history.length ) - let data = simpleDeepClone(this.history[this.activeHistoryIndex]) + const data = simpleDeepClone(this.history[this.activeHistoryIndex]) + this.emitDataUpdatesEvent(lastData, data) this.mindMap.emit('data_change', data) return data } @@ -134,13 +141,15 @@ class Command { } let len = this.history.length if (this.activeHistoryIndex + step <= len - 1) { + const lastData = this.history[this.activeHistoryIndex] this.activeHistoryIndex += step this.mindMap.emit( 'back_forward', this.activeHistoryIndex, this.history.length ) - let data = simpleDeepClone(this.history[this.activeHistoryIndex]) + const data = simpleDeepClone(this.history[this.activeHistoryIndex]) + this.emitDataUpdatesEvent(lastData, data) this.mindMap.emit('data_change', data) return data } @@ -165,6 +174,53 @@ class Command { walk(data) return data } + + // 派发思维导图更新明细事件 + emitDataUpdatesEvent(lastData, data) { + // 如果data_change_detail没有监听者,那么不进行计算,节省性能 + const eventName = 'data_change_detail' + const count = this.mindMap.event.listenerCount(eventName) + if (count > 0 && lastData && data) { + const lastDataObj = simpleDeepClone(transformTreeDataToObject(lastData)) + const dataObj = simpleDeepClone(transformTreeDataToObject(data)) + const res = [] + const walkReplace = (root, obj) => { + if (root.children && root.children.length > 0) { + root.children.forEach((childUid, index) => { + root.children[index] = obj[childUid] + walkReplace(root.children[index], obj) + }) + } + return root + } + // 找出新增的或修改的 + Object.keys(dataObj).forEach(uid => { + // 新增的或已经存在的,如果数据发生了改变 + if (!lastDataObj[uid]) { + res.push({ + action: 'create', + data: walkReplace(dataObj[uid], dataObj) + }) + } else if (!isSameObject(lastDataObj[uid], dataObj[uid])) { + res.push({ + action: 'update', + oldData: walkReplace(lastDataObj[uid], lastDataObj), + data: walkReplace(dataObj[uid], dataObj) + }) + } + }) + // 找出删除的 + Object.keys(lastDataObj).forEach(uid => { + if (!dataObj[uid]) { + res.push({ + action: 'delete', + data: walkReplace(lastDataObj[uid], lastDataObj) + }) + } + }) + this.mindMap.emit(eventName, res) + } + } } export default Command diff --git a/simple-mind-map/src/core/command/KeyCommand.js b/simple-mind-map/src/core/command/KeyCommand.js index 32ec181a..8474e84b 100644 --- a/simple-mind-map/src/core/command/KeyCommand.js +++ b/simple-mind-map/src/core/command/KeyCommand.js @@ -1,4 +1,5 @@ import { keyMap } from './keyMap' + // 快捷按键、命令处理类 export default class KeyCommand { // 构造函数 diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 76ae08cb..01266d3f 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -26,7 +26,9 @@ import { getDataFromClipboard, htmlEscape, parseAddGeneralizationNodeList, - checkNodeListIsEqual + checkNodeListIsEqual, + createSmmFormatData, + checkSmmFormatData } from '../../utils' import { shapeList } from './node/Shape' import { lineStyleProps } from '../../themes/default' @@ -876,20 +878,14 @@ class Render { copy() { this.beingCopyData = this.copyNode() if (!this.beingCopyData) return - setDataToClipboard({ - simpleMindMap: true, - data: this.beingCopyData - }) + setDataToClipboard(createSmmFormatData(this.beingCopyData)) } // 剪切节点 cut() { this.mindMap.execCommand('CUT_NODE', copyData => { this.beingCopyData = copyData - setDataToClipboard({ - simpleMindMap: true, - data: copyData - }) + setDataToClipboard(createSmmFormatData(copyData)) }) } @@ -936,10 +932,11 @@ class Render { const res = await this.mindMap.opt.customHandleClipboardText(text) if (!isUndef(res)) { useDefault = false - if (typeof res === 'object' && res.simpleMindMap) { - smmData = res.data + const checkRes = checkSmmFormatData(res) + if (checkRes.isSmm) { + smmData = checkRes.data } else { - text = String(res) + text = checkRes.data } } } catch (error) { @@ -948,13 +945,11 @@ class Render { } // 默认处理 if (useDefault) { - try { - const parsedData = JSON.parse(text) - if (parsedData && parsedData.simpleMindMap) { - smmData = parsedData.data - } - } catch (error) { - errorHandler(ERROR_TYPES.PARSE_PASTE_DATA_ERROR, error) + const checkRes = checkSmmFormatData(text) + if (checkRes.isSmm) { + smmData = checkRes.data + } else { + text = checkRes.data } } if (smmData) { @@ -1419,6 +1414,7 @@ class Render { // 设置节点文本 setNodeText(node, text, richText, resetRichText) { + richText = richText === undefined ? node.getData('richText') : richText this.setNodeDataRender(node, { text, richText, diff --git a/simple-mind-map/src/core/render/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js index 620ba85a..6d96b1ea 100644 --- a/simple-mind-map/src/core/render/TextEdit.js +++ b/simple-mind-map/src/core/render/TextEdit.js @@ -3,7 +3,10 @@ import { checkNodeOuter, focusInput, selectAllInput, - htmlEscape + htmlEscape, + handleInputPasteText, + checkSmmFormatData, + getTextFromHtml } from '../../utils' import { ERROR_TYPES, CONSTANTS } from '../../constants/constant' @@ -226,6 +229,16 @@ export default class TextEdit { e.stopPropagation() } }) + this.textEditNode.addEventListener('paste', e => { + const text = e.clipboardData.getData('text') + const { isSmm, data } = checkSmmFormatData(text) + if (isSmm && data[0] && data[0].data) { + // 只取第一个节点的纯文本 + handleInputPasteText(e, getTextFromHtml(data[0].data.text)) + } else { + handleInputPasteText(e) + } + }) const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body targetNode.appendChild(this.textEditNode) diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index af47745a..236511d8 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -360,7 +360,10 @@ class Node { // 文字 if (this._textData) { this._textData.node.attr('data-offsetx', textContentOffsetX) - this._textData.node.x(textContentOffsetX).y(0) + // 修复safari浏览器节点存在图标时文字位置不正确的问题 + ;(this._textData.nodeContent || this._textData.node) + .x(textContentOffsetX) + .y(0) textContentNested.add(this._textData.node) textContentOffsetX += this._textData.width + textContentItemMargin } @@ -710,6 +713,7 @@ class Node { if (this.parent) { this.parent.removeLine() } + this.style.onRemove() } // 隐藏节点 diff --git a/simple-mind-map/src/core/render/node/Style.js b/simple-mind-map/src/core/render/node/Style.js index 0a36e433..af1f3a10 100644 --- a/simple-mind-map/src/core/render/node/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -2,6 +2,7 @@ import { checkIsNodeStyleDataKey, generateColorByContent } from '../../../utils/index' +import { Gradient } from '@svgdotjs/svg.js' const rootProp = ['paddingX', 'paddingY'] const backgroundStyleProps = [ @@ -58,6 +59,8 @@ class Style { // 箭头图标 this._markerPath = null this._marker = null + // 渐变背景 + this._gradient = null } // 合并样式 @@ -100,11 +103,22 @@ class Style { node.radius(this.merge('borderRadius')) } - // 矩形外的其他形状 + // 形状 shape(node) { - node.fill({ - color: this.merge('fillColor') - }) + if (this.merge('gradientStyle')) { + if (!this._gradient) { + this._gradient = this.ctx.nodeDraw.gradient('linear') + } + this._gradient.update(add => { + add.stop(0, this.merge('startColor')) + add.stop(1, this.merge('endColor')) + }) + node.fill(this._gradient) + } else { + node.fill({ + color: this.merge('fillColor') + }) + } // 节点使用横线样式,不需要渲染非激活状态的边框样式 // if ( // !this.ctx.isRoot && @@ -273,6 +287,22 @@ class Style { color: hoverRectColor }) } + + // 所属节点被删除时的操作 + onRemove() { + if (this._marker) { + this._marker.remove() + this._marker = null + } + if (this._markerPath) { + this._markerPath.remove() + this._markerPath = null + } + if (this._gradient) { + this._gradient.remove() + this._gradient = null + } + } } Style.cacheStyle = null diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index d6528a5f..86c3bc19 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -170,6 +170,7 @@ function createRichTextNode() { g.add(foreignObject) return { node: g, + nodeContent: foreignObject, width, height } @@ -240,7 +241,7 @@ function createHyperlinkNode() { return } let iconSize = this.mindMap.themeConfig.iconSize - let node = new SVG() + let node = new SVG().size(iconSize, iconSize) // 超链接节点 let a = new A().to(hyperlink).target('_blank') a.node.addEventListener('click', e => { @@ -298,7 +299,7 @@ function createNoteNode() { return null } let iconSize = this.mindMap.themeConfig.iconSize - let node = new SVG().attr('cursor', 'pointer') + let node = new SVG().attr('cursor', 'pointer').size(iconSize, iconSize) // 透明的层,用来作为鼠标区域 node.add(new Rect().size(iconSize, iconSize).fill({ color: 'transparent' })) // 备注图标 diff --git a/simple-mind-map/src/core/render/node/nodeGeneralization.js b/simple-mind-map/src/core/render/node/nodeGeneralization.js index eb6dbb6e..89fd2ab9 100644 --- a/simple-mind-map/src/core/render/node/nodeGeneralization.js +++ b/simple-mind-map/src/core/render/node/nodeGeneralization.js @@ -131,6 +131,7 @@ function updateGeneralizationData() { function removeGeneralization() { if (this.isGeneralization) return this._generalizationList.forEach(item => { + item.generalizationNode.style.onRemove() if (item.generalizationLine) { item.generalizationLine.remove() item.generalizationLine = null diff --git a/simple-mind-map/src/plugins/Cooperate.js b/simple-mind-map/src/plugins/Cooperate.js index ea3eb2f5..09258deb 100644 --- a/simple-mind-map/src/plugins/Cooperate.js +++ b/simple-mind-map/src/plugins/Cooperate.js @@ -1,6 +1,13 @@ import * as Y from 'yjs' import { WebrtcProvider } from 'y-webrtc' -import { isSameObject, simpleDeepClone, getType, isUndef } from '../utils/index' +import { + isSameObject, + simpleDeepClone, + getType, + isUndef, + transformTreeDataToObject, + transformObjectToTreeData +} from '../utils/index' // 协同插件 class Cooperate { @@ -39,7 +46,7 @@ class Cooperate { // 创建共享数据 this.ymap = this.ydoc.getMap() // 思维导图树结构转平级对象结构 - this.currentData = this.transformTreeDataToObject(data) + this.currentData = transformTreeDataToObject(data) // 将思维导图数据添加到共享数据中 Object.keys(this.currentData).forEach(uid => { this.ymap.set(uid, this.currentData[uid]) @@ -108,7 +115,7 @@ class Cooperate { if (isSameObject(data, this.currentData)) return this.currentData = data // 平级对象转树结构 - const res = this.transformObjectToTreeData(data) + const res = transformObjectToTreeData(data) if (!res) return // 更新思维导图画布 this.mindMap.renderer.setData(res) @@ -118,7 +125,7 @@ class Cooperate { // 当前思维导图改变后的处理,触发同步 onDataChange(data) { - const res = this.transformTreeDataToObject(data) + const res = transformTreeDataToObject(data) this.updateChanges(res) } @@ -224,111 +231,6 @@ class Cooperate { }) } - // 将树结构转平级对象 - /* - { - data: { - uid: 'xxx' - }, - children: [ - { - data: { - uid: 'xxx' - }, - children: [] - } - ] - } - 转为: - { - uid: { - children: [uid1, uid2], - data: {} - } - } - */ - transformTreeDataToObject(data) { - const res = {} - const walk = (root, parent) => { - const uid = root.data.uid - if (parent) { - parent.children.push(uid) - } - res[uid] = { - isRoot: !parent, - data: { - ...root.data - }, - children: [] - } - if (root.children && root.children.length > 0) { - root.children.forEach(item => { - walk(item, res[uid]) - }) - } - } - walk(data, null) - return res - } - - // 找到父节点的uid - findParentUid(data, targetUid) { - const uids = Object.keys(data) - let res = '' - uids.forEach(uid => { - const children = data[uid].children - const isParent = - children.findIndex(childUid => { - return childUid === targetUid - }) !== -1 - if (isParent) { - res = uid - } - }) - return res - } - - // 将平级对象转树结构 - transformObjectToTreeData(data) { - const uids = Object.keys(data) - if (uids.length <= 0) return null - const rootKey = uids.find(uid => { - return data[uid].isRoot - }) - if (!rootKey || !data[rootKey]) return null - // 根节点 - const res = { - data: simpleDeepClone(data[rootKey].data), - children: [] - } - const map = {} - map[rootKey] = res - uids.forEach(uid => { - const parentUid = this.findParentUid(data, uid) - const cur = data[uid] - const node = map[uid] || { - data: simpleDeepClone(cur.data), - children: [] - } - if (!map[uid]) { - map[uid] = node - } - if (parentUid) { - const index = data[parentUid].children.findIndex(item => { - return item === uid - }) - if (!map[parentUid]) { - map[parentUid] = { - data: simpleDeepClone(data[parentUid].data), - children: [] - } - } - map[parentUid].children[index] = node - } - }) - return res - } - // 插件被移除前做的事情 beforePluginRemove() { this.unBindEvent() diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js index 79e538c1..225d90f4 100644 --- a/simple-mind-map/src/plugins/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -22,7 +22,7 @@ class Export { async export(type, isDownload = true, name = '思维导图', ...args) { if (this[type]) { const result = await this[type](name, ...args) - if (isDownload && type !== 'pdf') { + if (isDownload) { downloadFile(result, name + '.' + type) } return result @@ -229,7 +229,11 @@ class Export { throw new Error('请注册ExportPDF插件') } const img = await this.png(name, transparent) - await this.mindMap.doExportPDF.pdf(name, img) + // 使用jspdf库 + // await this.mindMap.doExportPDF.pdf(name, img) + // 使用pdf-lib库 + const res = await this.mindMap.doExportPDF.pdf(img) + return res } // 导出为xmind diff --git a/simple-mind-map/src/plugins/ExportPDF.js b/simple-mind-map/src/plugins/ExportPDF.js index 6d231e9a..6cf902c5 100644 --- a/simple-mind-map/src/plugins/ExportPDF.js +++ b/simple-mind-map/src/plugins/ExportPDF.js @@ -1,4 +1,6 @@ -import JsPDF from '../utils/jspdf' +// import JsPDF from '../utils/jspdf' +import { PDFDocument } from 'pdf-lib' +import { readBlob } from '../utils/index' // 导出PDF插件,需要通过Export插件使用 class ExportPDF { @@ -7,23 +9,29 @@ class ExportPDF { this.mindMap = opt.mindMap } - // 导出为pdf - async pdf(name, img) { + // 使用pdf-lib库导出为pdf + async pdf(img) { return new Promise((resolve, reject) => { const image = new Image() - image.onload = () => { + image.onload = async () => { const imageWidth = image.width const imageHeight = image.height - const pdf = new JsPDF({ - unit: 'px', - format: [imageWidth, imageHeight], - compress: true, - hotfixes: ['px_scaling'], - orientation: imageWidth > imageHeight ? 'landscape' : 'portrait' + // 创建pdf页面,尺寸设置为图片的大小 + const pdfDoc = await PDFDocument.create() + const page = pdfDoc.addPage() + page.setSize(imageWidth, imageHeight) + // 添加图片到pdf + const pngImage = await pdfDoc.embedPng(img) + page.drawImage(pngImage, { + x: 0, + y: 0, + width: imageWidth, + height: imageHeight }) - pdf.addImage(img, 'PNG', 0, 0, imageWidth, imageHeight) - pdf.save(name) - resolve() + const pdfBytes = await pdfDoc.save() + const blob = new Blob([pdfBytes]) + const res = await readBlob(blob) + resolve(res) } image.onerror = e => { reject(e) @@ -31,6 +39,31 @@ class ExportPDF { image.src = img }) } + + // 使用jspdf库导出为pdf + // async pdf(name, img) { + // return new Promise((resolve, reject) => { + // const image = new Image() + // image.onload = () => { + // const imageWidth = image.width + // const imageHeight = image.height + // const pdf = new JsPDF({ + // unit: 'px', + // format: [imageWidth, imageHeight], + // compress: true, + // hotfixes: ['px_scaling'], + // orientation: imageWidth > imageHeight ? 'landscape' : 'portrait' + // }) + // pdf.addImage(img, 'PNG', 0, 0, imageWidth, imageHeight) + // pdf.save(name) + // resolve() + // } + // image.onerror = e => { + // reject(e) + // } + // image.src = img + // }) + // } } ExportPDF.instanceName = 'doExportPDF' diff --git a/simple-mind-map/src/plugins/Formula.js b/simple-mind-map/src/plugins/Formula.js index 82783e51..2e031d55 100644 --- a/simple-mind-map/src/plugins/Formula.js +++ b/simple-mind-map/src/plugins/Formula.js @@ -1,5 +1,6 @@ import katex from 'katex' import Quill from 'quill' +import { getChromeVersion } from '../utils/index' // 数学公式支持插件 // 该插件在富文本模式下可用 @@ -12,19 +13,31 @@ class Formula { this.extendQuill() } + // 获取katex配置 + getKatexConfig() { + const config = { + throwOnError: false, + errorColor: '#f00', + output: 'mathml' // 默认只输出公式 + } + // Chrome内核100以下,mathml配置公式无法正确渲染 + const chromeVersion = getChromeVersion() + if (chromeVersion && chromeVersion <= 100) { + config.output = 'html' + } + return config + } + // 修改formula格式工具 extendQuill() { const QuillFormula = Quill.import('formats/formula') + const self = this class CustomFormulaBlot extends QuillFormula { static create(value) { let node = super.create(value) if (typeof value === 'string') { - katex.render(value, node, { - throwOnError: false, - errorColor: '#f00', - output: 'mathml' // 增加该配置,默认只输出公式 - }) + katex.render(value, node, self.getKatexConfig()) node.setAttribute('data-value', value) } return node diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index 92dfd723..156f39c5 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -6,7 +6,8 @@ import { getTextFromHtml, isWhite, getVisibleColorFromTheme, - isUndef + isUndef, + checkSmmFormatData } from '../utils' import { CONSTANTS } from '../constants/constant' @@ -397,7 +398,7 @@ class RichText { // 拦截粘贴,只允许粘贴纯文本 this.quill.clipboard.addMatcher(Node.TEXT_NODE, node => { let style = this.getPasteTextStyle() - return new Delta().insert(node.data, style) + return new Delta().insert(this.formatPasteText(node.data), style) }) this.quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => { let ops = [] @@ -407,7 +408,7 @@ class RichText { if (op.insert && typeof op.insert === 'string' && op.insert !== '\n') { ops.push({ attributes: { ...style }, - insert: op.insert + insert: this.formatPasteText(op.insert) }) } }) @@ -428,6 +429,17 @@ class RichText { return {} } + // 处理粘贴的文本内容 + formatPasteText(text) { + const { isSmm, data } = checkSmmFormatData(text) + if (isSmm && data[0] && data[0].data) { + // 只取第一个节点的纯文本 + return getTextFromHtml(data[0].data.text) + } else { + return text + } + } + // 正则输入中文 onCompositionStart() { if (!this.showTextEdit) { diff --git a/simple-mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js index 53185f22..53f5f2f0 100644 --- a/simple-mind-map/src/themes/default.js +++ b/simple-mind-map/src/themes/default.js @@ -72,7 +72,10 @@ export default { borderWidth: 0, borderDasharray: 'none', borderRadius: 5, - textDecoration: 'none' + textDecoration: 'none', + gradientStyle: false, + startColor: '#549688', + endColor: '#fff' }, // 二级节点样式 second: { @@ -90,7 +93,10 @@ export default { borderWidth: 1, borderDasharray: 'none', borderRadius: 5, - textDecoration: 'none' + textDecoration: 'none', + gradientStyle: false, + startColor: '#549688', + endColor: '#fff' }, // 三级及以下节点样式 node: { @@ -108,7 +114,10 @@ export default { borderWidth: 0, borderRadius: 5, borderDasharray: 'none', - textDecoration: 'none' + textDecoration: 'none', + gradientStyle: false, + startColor: '#549688', + endColor: '#fff' }, // 概要节点样式 generalization: { @@ -126,7 +135,10 @@ export default { borderWidth: 1, borderDasharray: 'none', borderRadius: 5, - textDecoration: 'none' + textDecoration: 'none', + gradientStyle: false, + startColor: '#549688', + endColor: '#fff' } } @@ -162,7 +174,10 @@ const nodeSizeIndependenceList = [ 'backgroundPosition', 'backgroundSize', 'rootLineKeepSameInCurve', - 'showLineMarker' + 'showLineMarker', + 'gradientStyle', + 'startColor', + 'endColor' ] export const checkIsNodeSizeIndependenceConfig = config => { let keys = Object.keys(config) diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index 824f4386..1ff2b672 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -1018,3 +1018,164 @@ export const checkNodeListIsEqual = (list1, list2) => { } return true } + +// 获取浏览器的chrome内核版本 +export const getChromeVersion = () => { + const match = navigator.userAgent.match(/\s+Chrome\/(.*)\s+/) + if (match && match[1]) { + return Number.parseFloat(match[1]) + } + return '' +} + +// 创建smm粘贴的粘贴数据 +export const createSmmFormatData = data => { + return { + simpleMindMap: true, + data + } +} + +// 检查是否是smm粘贴格式的数据 +export const checkSmmFormatData = data => { + let smmData = null + // 如果是字符串,则尝试解析为对象 + if (typeof data === 'string') { + try { + const parsedData = JSON.parse(data) + // 判断是否是对象,且存在属性标志 + if (typeof parsedData === 'object' && parsedData.simpleMindMap) { + smmData = parsedData.data + } + } catch (error) {} + } else if (typeof data === 'object' && data.simpleMindMap) { + // 否则如果是对象,则检查属性标志 + smmData = data.data + } + const isSmm = !!smmData + return { + isSmm, + data: isSmm ? smmData : String(data) + } +} + +// 处理输入框的粘贴事件,会去除文本的html格式、换行 +export const handleInputPasteText = (e, text) => { + e.preventDefault() + const selection = window.getSelection() + if (!selection.rangeCount) return + selection.deleteFromDocument() + text = text || e.clipboardData.getData('text') + // 去除格式 + text = getTextFromHtml(text) + // 去除换行 + text = text.replaceAll(/\n/g, '') + const node = document.createTextNode(text) + selection.getRangeAt(0).insertNode(node) + selection.collapseToEnd() +} + +// 将思维导图树结构转平级对象 +/* + { + data: { + uid: 'xxx' + }, + children: [ + { + data: { + uid: 'xxx' + }, + children: [] + } + ] + } + 转为: + { + uid: { + children: [uid1, uid2], + data: {} + } + } + */ +export const transformTreeDataToObject = data => { + const res = {} + const walk = (root, parent) => { + const uid = root.data.uid + if (parent) { + parent.children.push(uid) + } + res[uid] = { + isRoot: !parent, + data: { + ...root.data + }, + children: [] + } + if (root.children && root.children.length > 0) { + root.children.forEach(item => { + walk(item, res[uid]) + }) + } + } + walk(data, null) + return res +} + +// 将平级对象转树结构 +// transformTreeDataToObject方法的反向操作 +// 找到父节点的uid +const _findParentUid = (data, targetUid) => { + const uids = Object.keys(data) + let res = '' + uids.forEach(uid => { + const children = data[uid].children + const isParent = + children.findIndex(childUid => { + return childUid === targetUid + }) !== -1 + if (isParent) { + res = uid + } + }) + return res +} +export const transformObjectToTreeData = data => { + const uids = Object.keys(data) + if (uids.length <= 0) return null + const rootKey = uids.find(uid => { + return data[uid].isRoot + }) + if (!rootKey || !data[rootKey]) return null + // 根节点 + const res = { + data: simpleDeepClone(data[rootKey].data), + children: [] + } + const map = {} + map[rootKey] = res + uids.forEach(uid => { + const parentUid = _findParentUid(data, uid) + const cur = data[uid] + const node = map[uid] || { + data: simpleDeepClone(cur.data), + children: [] + } + if (!map[uid]) { + map[uid] = node + } + if (parentUid) { + const index = data[parentUid].children.findIndex(item => { + return item === uid + }) + if (!map[parentUid]) { + map[parentUid] = { + data: simpleDeepClone(data[parentUid].data), + children: [] + } + } + map[parentUid].children[index] = node + } + }) + return res +} diff --git a/web/package.json b/web/package.json index 3d017884..e39219f0 100644 --- a/web/package.json +++ b/web/package.json @@ -6,7 +6,7 @@ "serve": "vue-cli-service serve", "build": "vue-cli-service build && node ../copy.js", "lint": "vue-cli-service lint", - "buildLibrary": "vue-cli-service build --mode library --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist && esbuild ../simple-mind-map/full.js --bundle --external:buffer --format=esm --outfile=../simple-mind-map/dist/simpleMindMap.esm.js && esbuild ../simple-mind-map/full.js --bundle --minify --external:buffer --format=esm --outfile=../simple-mind-map/dist/simpleMindMap.esm.min.js", + "buildLibrary": "node ./scripts/updateVersion.js && vue-cli-service build --mode library --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist && esbuild ../simple-mind-map/full.js --bundle --external:buffer --format=esm --outfile=../simple-mind-map/dist/simpleMindMap.esm.js && esbuild ../simple-mind-map/full.js --bundle --minify --external:buffer --format=esm --outfile=../simple-mind-map/dist/simpleMindMap.esm.min.js", "format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*", "buildDoc": "node ./scripts/buildDoc.js", "autoBuildDoc": "node ./scripts/autoBuildDoc.js", diff --git a/web/scripts/updateVersion.js b/web/scripts/updateVersion.js new file mode 100644 index 00000000..c85b0b8a --- /dev/null +++ b/web/scripts/updateVersion.js @@ -0,0 +1,11 @@ +const path = require('path') +const fs = require('fs') +const pkg = require('../../simple-mind-map/package.json') + +const file = path.resolve('../simple-mind-map/full.js') +let content = fs.readFileSync(file, 'utf-8') +content = content.replace( + /(MindMap.version\s*=\s*)[^\n]+(\n)/, + `$1'${pkg.version}'$2` +) +fs.writeFileSync(file, content) diff --git a/web/src/assets/avatar/欣.jpg b/web/src/assets/avatar/欣.jpg new file mode 100644 index 00000000..09dc78bc Binary files /dev/null and b/web/src/assets/avatar/欣.jpg differ diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js index 31cd12de..5ae70103 100644 --- a/web/src/lang/en_us.js +++ b/web/src/lang/en_us.js @@ -202,7 +202,10 @@ export default { line: 'Line', nodePadding: 'Node padding', horizontal: 'Horizontal', - vertical: 'Vertical' + vertical: 'Vertical', + gradientStyle: 'Gradient', + startColor: 'Start', + endColor: 'End' }, theme: { title: 'Theme', diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js index 5482a1e7..1d748513 100644 --- a/web/src/lang/zh_cn.js +++ b/web/src/lang/zh_cn.js @@ -200,7 +200,10 @@ export default { line: '线条', nodePadding: '节点内边距', horizontal: '水平', - vertical: '垂直' + vertical: '垂直', + gradientStyle: '渐变', + startColor: '起始', + endColor: '结束' }, theme: { title: '主题', diff --git a/web/src/pages/Doc/en/introduction/index.md b/web/src/pages/Doc/en/introduction/index.md index 28c4375f..2c404434 100644 --- a/web/src/pages/Doc/en/introduction/index.md +++ b/web/src/pages/Doc/en/introduction/index.md @@ -269,4 +269,12 @@ Open source is not easy. If this project is helpful to you, you can invite the a

继龙

+
+ +

+
+
+ +

易空小易

+
\ No newline at end of file diff --git a/web/src/pages/Doc/en/introduction/index.vue b/web/src/pages/Doc/en/introduction/index.vue index 2fc3335e..c5111a1b 100644 --- a/web/src/pages/Doc/en/introduction/index.vue +++ b/web/src/pages/Doc/en/introduction/index.vue @@ -225,6 +225,14 @@ full screen, support mini map

继龙

+
+ +

+
+
+ +

易空小易

+
diff --git a/web/src/pages/Doc/zh/course12/index.md b/web/src/pages/Doc/zh/course12/index.md index 97a72afd..3842b7d5 100644 --- a/web/src/pages/Doc/zh/course12/index.md +++ b/web/src/pages/Doc/zh/course12/index.md @@ -287,7 +287,7 @@ onPaste(e) { if (!selection.rangeCount) return selection.deleteFromDocument()// 删除当前选区,也就是如果当前用户在输入框中选择了一些文本,会被删除 // 从剪贴板里取出文本数据 - let text = (e.clipboardData || window.clipboardData).getData('text') + let text = e.clipboardData.getData('text') // 调用库提供的getTextFromHtml方法去除格式 text = getTextFromHtml(text) // 去除换行 diff --git a/web/src/pages/Doc/zh/course12/index.vue b/web/src/pages/Doc/zh/course12/index.vue index e8b68667..8ccdbd71 100644 --- a/web/src/pages/Doc/zh/course12/index.vue +++ b/web/src/pages/Doc/zh/course12/index.vue @@ -237,7 +237,7 @@ mindMap.execCommand('INSERT_CHILD_NODE'if (!selection.rangeCount) return selection.deleteFromDocument()// 删除当前选区,也就是如果当前用户在输入框中选择了一些文本,会被删除 // 从剪贴板里取出文本数据 - let text = (e.clipboardData || window.clipboardData).getData('text') + let text = e.clipboardData.getData('text') // 调用库提供的getTextFromHtml方法去除格式 text = getTextFromHtml(text) // 去除换行 diff --git a/web/src/pages/Doc/zh/course17/index.md b/web/src/pages/Doc/zh/course17/index.md index 6765cff2..24cf3b83 100644 --- a/web/src/pages/Doc/zh/course17/index.md +++ b/web/src/pages/Doc/zh/course17/index.md @@ -16,7 +16,7 @@ mindMap.export(type, isDownload, fileName, ...) `type`:文件类型 -`isDownload`:传`true`会触发下载,`false`则不会,函数会返回导出文件的数据,`data:url`格式,你可以自行下载,`pdf`不支持该参数,默认会直接下载。 +`isDownload`:传`true`会触发下载,`false`则不会,函数会返回导出文件的数据,`data:url`格式,你可以自行下载,(v0.9.2之前的版本,`pdf`不支持该参数,默认会直接下载)。 `fileName`:下载的文件名称 diff --git a/web/src/pages/Doc/zh/course17/index.vue b/web/src/pages/Doc/zh/course17/index.vue index 87a8a9bd..8c2a703a 100644 --- a/web/src/pages/Doc/zh/course17/index.vue +++ b/web/src/pages/Doc/zh/course17/index.vue @@ -11,7 +11,7 @@
mindMap.export(type, isDownload, fileName, ...)
 

type:文件类型

-

isDownload:传true会触发下载,false则不会,函数会返回导出文件的数据,data:url格式,你可以自行下载,pdf不支持该参数,默认会直接下载。

+

isDownload:传true会触发下载,false则不会,函数会返回导出文件的数据,data:url格式,你可以自行下载,(v0.9.2之前的版本,pdf不支持该参数,默认会直接下载)。

fileName:下载的文件名称

导出为smm、json

这两种文件的导出是一样的:

diff --git a/web/src/pages/Doc/zh/doExport/index.md b/web/src/pages/Doc/zh/doExport/index.md index 5e316b16..6ed31653 100644 --- a/web/src/pages/Doc/zh/doExport/index.md +++ b/web/src/pages/Doc/zh/doExport/index.md @@ -88,7 +88,9 @@ svg( - `transparent`:v0.9.2+,Boolean,默认为false,指定导出图片的背景是否是透明的 -导出为`pdf`,和其他导出方法不一样,这个方法不会返回数据,会直接触发下载。 +导出为`pdf`, + +> v0.9.3之前的版本这个方法不会返回数据,会直接触发下载。 > v0.6.0版本以后,需要额外注册一个ExportPDF插件 diff --git a/web/src/pages/Doc/zh/doExport/index.vue b/web/src/pages/Doc/zh/doExport/index.vue index 70803d94..ba87aa09 100644 --- a/web/src/pages/Doc/zh/doExport/index.vue +++ b/web/src/pages/Doc/zh/doExport/index.vue @@ -90,7 +90,10 @@ a.click()

transparent:v0.9.2+,Boolean,默认为false,指定导出图片的背景是否是透明的

-

导出为pdf,和其他导出方法不一样,这个方法不会返回数据,会直接触发下载。

+

导出为pdf

+
+

v0.9.3之前的版本这个方法不会返回数据,会直接触发下载。

+

v0.6.0版本以后,需要额外注册一个ExportPDF插件

diff --git a/web/src/pages/Doc/zh/introduction/index.md b/web/src/pages/Doc/zh/introduction/index.md index 0c733510..10d8a182 100644 --- a/web/src/pages/Doc/zh/introduction/index.md +++ b/web/src/pages/Doc/zh/introduction/index.md @@ -262,4 +262,12 @@

继龙

+
+ +

+
+
+ +

易空小易

+
\ No newline at end of file diff --git a/web/src/pages/Doc/zh/introduction/index.vue b/web/src/pages/Doc/zh/introduction/index.vue index 83b1e1e1..d8a1aa5f 100644 --- a/web/src/pages/Doc/zh/introduction/index.vue +++ b/web/src/pages/Doc/zh/introduction/index.vue @@ -219,6 +219,14 @@

继龙

+
+ +

+
+
+ +

易空小易

+
diff --git a/web/src/pages/Edit/components/FormulaSidebar.vue b/web/src/pages/Edit/components/FormulaSidebar.vue index 53fd655f..79925695 100644 --- a/web/src/pages/Edit/components/FormulaSidebar.vue +++ b/web/src/pages/Edit/components/FormulaSidebar.vue @@ -34,6 +34,7 @@ import Sidebar from './Sidebar' import { mapState, mapMutations } from 'vuex' import { formulaList } from '@/config/constant' +import 'simple-mind-map/node_modules/katex/dist/katex.min.css' export default { name: 'FormulaSidebar', @@ -78,10 +79,10 @@ export default { init() { this.list = formulaList.map(item => { return { - overview: window.katex.renderToString(item, { - throwOnError: false, - output: 'mathml' - }), + overview: window.katex.renderToString( + item, + this.mindMap.formula.getKatexConfig() + ), text: item } }) diff --git a/web/src/pages/Edit/components/Outline.vue b/web/src/pages/Edit/components/Outline.vue index c6ba3b30..0e7d663e 100644 --- a/web/src/pages/Edit/components/Outline.vue +++ b/web/src/pages/Edit/components/Outline.vue @@ -41,9 +41,9 @@ import { mapState } from 'vuex' import { nodeRichTextToTextWithWrap, textToNodeRichTextWithWrap, - getTextFromHtml, createUid, - htmlEscape + htmlEscape, + handleInputPasteText } from 'simple-mind-map/src/utils' // 大纲树 @@ -212,18 +212,7 @@ export default { // 拦截粘贴事件 onPaste(e) { - e.preventDefault() - const selection = window.getSelection() - if (!selection.rangeCount) return - selection.deleteFromDocument() - let text = (e.clipboardData || window.clipboardData).getData('text') - // 去除格式 - text = getTextFromHtml(text) - // 去除换行 - text = text.replaceAll(/\n/g, '') - const node = document.createTextNode(text) - selection.getRangeAt(0).insertNode(node) - selection.collapseToEnd() + handleInputPasteText(e) }, // 生成唯一的key diff --git a/web/src/pages/Edit/components/OutlineEdit.vue b/web/src/pages/Edit/components/OutlineEdit.vue index aea3eea2..1ab514fd 100644 --- a/web/src/pages/Edit/components/OutlineEdit.vue +++ b/web/src/pages/Edit/components/OutlineEdit.vue @@ -52,10 +52,10 @@ import { mapState, mapMutations } from 'vuex' import { nodeRichTextToTextWithWrap, textToNodeRichTextWithWrap, - getTextFromHtml, createUid, simpleDeepClone, - htmlEscape + htmlEscape, + handleInputPasteText } from 'simple-mind-map/src/utils' import { storeData } from '@/api' @@ -211,18 +211,7 @@ export default { // 拦截粘贴事件 onPaste(e) { - e.preventDefault() - const selection = window.getSelection() - if (!selection.rangeCount) return - selection.deleteFromDocument() - let text = (e.clipboardData || window.clipboardData).getData('text') - // 去除格式 - text = getTextFromHtml(text) - // 去除换行 - text = text.replaceAll(/\n/g, '') - const node = document.createTextNode(text) - selection.getRangeAt(0).insertNode(node) - selection.collapseToEnd() + handleInputPasteText(e) }, // 生成唯一的key diff --git a/web/src/pages/Edit/components/Style.vue b/web/src/pages/Edit/components/Style.vue index 197728e4..68af7489 100644 --- a/web/src/pages/Edit/components/Style.vue +++ b/web/src/pages/Edit/components/Style.vue @@ -249,6 +249,40 @@ +
+
+ {{ $t('style.gradientStyle') }} + +
+
+ {{ $t('style.startColor') }} + + + + +
+
+ {{ $t('style.endColor') }} + + + + +
+
{{ $t('style.shape') }}
@@ -443,7 +477,10 @@ export default { borderRadius: '', lineColor: '', lineDasharray: '', - lineWidth: '' + lineWidth: '', + gradientStyle: false, + startColor: '', + endColor: '' } } }, @@ -490,7 +527,7 @@ export default { this.initNodeStyle() }) }, - + /** * @Author: 王林 * @Date: 2021-05-05 09:48:52 @@ -518,7 +555,10 @@ export default { 'borderRadius', 'lineColor', 'lineDasharray', - 'lineWidth' + 'lineWidth', + 'gradientStyle', + 'startColor', + 'endColor' ].forEach(item => { this.style[item] = this.activeNodes[0].getStyle(item, false) }) @@ -601,6 +641,26 @@ export default { changeFillColor(color) { this.style.fillColor = color this.update('fillColor') + }, + + /** + * @Author: lxr_cel + * @Date: 2024-01-02 11:09:27 + * @Desc: 切换渐变开始颜色 + */ + changeStartColor(color) { + this.style.startColor = color + this.update('startColor') + }, + + /** + * @Author: lxr_cel + * @Date: 2024-01-02 10:10:34 + * @Desc: 切换渐变结束颜色 + */ + changeEndColor(color) { + this.style.endColor = color + this.update('endColor') } } }