From f54f92c303444a49ad8ba85c6b99e9e85ebb2647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Thu, 4 Jul 2024 17:32:00 +0800 Subject: [PATCH 01/46] update --- README.md | 2 + web/src/pages/Edit/components/Toolbar.vue | 723 ---------------------- 2 files changed, 2 insertions(+), 723 deletions(-) delete mode 100644 web/src/pages/Edit/components/Toolbar.vue diff --git a/README.md b/README.md index f0f8ff03..84b09825 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。百度云 > 客户端版本会落后于在线版本,尝试最新功能请优先使用在线版。 +如果你需要带后端的可部署版本,可以尝试我们开发的另一个项目[理想文档](https://github.com/wanglin2/lx-doc)。 + # 特性 - [x] 插件化架构,除核心功能外,其他功能作为插件提供,按需使用,减小打包体积 diff --git a/web/src/pages/Edit/components/Toolbar.vue b/web/src/pages/Edit/components/Toolbar.vue deleted file mode 100644 index 69f6c30c..00000000 --- a/web/src/pages/Edit/components/Toolbar.vue +++ /dev/null @@ -1,723 +0,0 @@ - - - - - From ac72c0c1dca4cc7113f8ac68d376d42f60817ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Fri, 5 Jul 2024 10:57:06 +0800 Subject: [PATCH 02/46] =?UTF-8?q?Demo=EF=BC=9A=E6=81=A2=E5=A4=8D=E8=AF=AF?= =?UTF-8?q?=E5=88=A0=E7=9A=84=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/components/Toolbar.vue | 723 ++++++++++++++++++++++ 1 file changed, 723 insertions(+) create mode 100644 web/src/pages/Edit/components/Toolbar.vue diff --git a/web/src/pages/Edit/components/Toolbar.vue b/web/src/pages/Edit/components/Toolbar.vue new file mode 100644 index 00000000..d9367a6b --- /dev/null +++ b/web/src/pages/Edit/components/Toolbar.vue @@ -0,0 +1,723 @@ + + + + + From 159a4a202cbbdbc2919993c35718d292bddcaefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Mon, 8 Jul 2024 16:27:18 +0800 Subject: [PATCH 03/46] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8DcustomCreat?= =?UTF-8?q?eNodeContent=E3=80=81createNodePrefixContent=E7=AD=89=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E9=87=8C=E8=8E=B7=E5=8F=96=E5=88=B0=E7=9A=84=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=9A=84isRoot=E5=92=8Cparent=E7=AD=89=E5=80=BC?= =?UTF-8?q?=E9=83=BD=E4=B8=BAnull=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/layouts/Base.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index a2caa07a..886daf36 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -83,6 +83,11 @@ class Base { ) newNode.reset() newNode.layerIndex = layerIndex + if (isRoot) { + newNode.isRoot = true + } else { + newNode.parent = parent._node + } this.cacheNode(data._node.uid, newNode) this.checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(newNode) // 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本等情况需要重新计算节点大小和布局 @@ -112,6 +117,11 @@ class Base { newNode.reset() newNode.nodeData = newNode.handleData(data || {}) newNode.layerIndex = layerIndex + if (isRoot) { + newNode.isRoot = true + } else { + newNode.parent = parent._node + } this.cacheNode(uid, newNode) this.checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(newNode) data._node = newNode @@ -137,7 +147,9 @@ class Base { renderer: this.renderer, mindMap: this.mindMap, draw: this.draw, - layerIndex + layerIndex, + isRoot, + parent: !isRoot ? parent._node : null }) // uid保存到数据上,为了节点复用 data.data.uid = newUid @@ -157,11 +169,9 @@ class Base { } // 根节点 if (isRoot) { - newNode.isRoot = true this.root = newNode } else { // 互相收集 - newNode.parent = parent._node parent._node.addChildren(newNode) } return newNode From 9b55d051dca8507fb70b50e80231f16ea51cb6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Mon, 8 Jul 2024 16:52:53 +0800 Subject: [PATCH 04/46] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E8=BF=87=E8=8A=82=E7=82=B9=E6=96=87=E6=9C=AC=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E5=86=8D=E4=BD=BF=E7=94=A8=E6=BB=9A=E8=BD=AE=E6=88=96?= =?UTF-8?q?=E5=BF=AB=E6=8D=B7=E9=94=AE=E7=BC=A9=E6=94=BE=E7=94=BB=E5=B8=83?= =?UTF-8?q?=E6=97=B6=E4=B8=8A=E6=AC=A1=E8=A2=AB=E7=BC=96=E8=BE=91=E7=9A=84?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E4=BC=9A=E8=BF=9B=E5=85=A5=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E7=8A=B6=E6=80=81=EF=BC=8C=E4=BB=A5=E5=8F=8A=E6=80=9D=E7=BB=B4?= =?UTF-8?q?=E5=AF=BC=E5=9B=BE=E5=BF=AB=E6=8D=B7=E9=94=AE=E4=BC=9A=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/TextEdit.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/simple-mind-map/src/core/render/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js index e8a9ee79..ffa72563 100644 --- a/simple-mind-map/src/core/render/TextEdit.js +++ b/simple-mind-map/src/core/render/TextEdit.js @@ -187,7 +187,8 @@ export default class TextEdit { // 处理画布缩放 onScale() { - if (!this.currentNode) return + const node = this.getCurrentEditNode() + if (!node) return if (this.mindMap.richText) { this.mindMap.richText.cacheEditingText = this.mindMap.richText.getEditText() @@ -197,7 +198,7 @@ export default class TextEdit { this.showTextEdit = false } this.show({ - node: this.currentNode, + node, isFromScale: true }) } From ba9a6e501ab070596e332ce85aadb3dd144d4a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Tue, 9 Jul 2024 10:35:28 +0800 Subject: [PATCH 05/46] Doc: update --- web/src/pages/Doc/zh/course3/index.md | 2 +- web/src/pages/Doc/zh/course3/index.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/pages/Doc/zh/course3/index.md b/web/src/pages/Doc/zh/course3/index.md index 1f18c595..07856d1a 100644 --- a/web/src/pages/Doc/zh/course3/index.md +++ b/web/src/pages/Doc/zh/course3/index.md @@ -112,7 +112,7 @@ mindMap.execCommand('INSERT_NODE') 插入多个同级节点可以执行`INSERT_MULTI_NODE`命令: ```js -mindMap.execCommand('INSERT_NODE'. [], nodeList) +mindMap.execCommand('INSERT_MULTI_NODE'. [], nodeList) ``` `nodeList`是要插入的同级节点数据的数组,必传。 diff --git a/web/src/pages/Doc/zh/course3/index.vue b/web/src/pages/Doc/zh/course3/index.vue index 5ade6f71..6f76e7b7 100644 --- a/web/src/pages/Doc/zh/course3/index.vue +++ b/web/src/pages/Doc/zh/course3/index.vue @@ -70,7 +70,7 @@ mindMap.execCommand('INSERT_CHILD_NODE'

插入多个同级节点

插入多个同级节点可以执行INSERT_MULTI_NODE命令:

-
mindMap.execCommand('INSERT_NODE'. [], nodeList)
+
mindMap.execCommand('INSERT_MULTI_NODE'. [], nodeList)
 

nodeList是要插入的同级节点数据的数组,必传。

插入父节点

From f79918ec6f5ade20853e5f59cd88fd91befbcee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Tue, 9 Jul 2024 14:11:18 +0800 Subject: [PATCH 06/46] =?UTF-8?q?Feat=EF=BC=9A1.=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=A0=87=E7=AD=BE=E6=A0=B7=E5=BC=8F=EF=BC=9B?= =?UTF-8?q?2.=E6=96=B0=E5=A2=9E=E6=A0=87=E7=AD=BE=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E7=9A=84=E5=AE=9E=E4=BE=8B=E5=8C=96=E9=80=89?= =?UTF-8?q?=E9=A1=B9=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/constants/constant.js | 4 + .../src/constants/defaultOptions.js | 2 + simple-mind-map/src/core/render/node/Node.js | 94 ++++++++++++++----- simple-mind-map/src/core/render/node/Style.js | 17 ++-- .../core/render/node/nodeCreateContents.js | 79 +++++++++++++--- 5 files changed, 147 insertions(+), 49 deletions(-) diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index dfd0ae4c..23f069c3 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -235,6 +235,10 @@ export const CONSTANTS = { DEFAULT: 'default', NOT_ACTIVE: 'notActive', ACTIVE_ONLY: 'activeOnly' + }, + TAG_POSITION: { + RIGHT: 'right', + BOTTOM: 'bottom' } } diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 594b751f..83114860 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -23,6 +23,8 @@ export const defaultOpt = { mouseScaleCenterUseMousePosition: true, // 最多显示几个标签 maxTag: 5, + // 标签显示的位置,相对于节点文本,bottom(下方)、right(右侧) + tagPosition: CONSTANTS.TAG_POSITION.RIGHT, // 展开收缩按钮尺寸 expandBtnSize: 20, // 节点里图片和文字的间距 diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index 431ef6d5..b1dec1eb 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -253,11 +253,15 @@ class Node { height: rect.height } } + const { tagPosition } = this.mindMap.opt + const tagIsBottom = tagPosition === CONSTANTS.TAG_POSITION.BOTTOM // 宽高 let imgContentWidth = 0 let imgContentHeight = 0 let textContentWidth = 0 let textContentHeight = 0 + let tagContentWidth = 0 + let tagContentHeight = 0 // 存在图片 if (this._imgData) { this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width @@ -290,10 +294,20 @@ class Node { } // 标签 if (this._tagData.length > 0) { - textContentWidth += this._tagData.reduce((sum, cur) => { - textContentHeight = Math.max(textContentHeight, cur.height) + let maxTagHeight = 0 + const totalTagWidth = this._tagData.reduce((sum, cur) => { + maxTagHeight = Math.max(maxTagHeight, cur.height) return (sum += cur.width + this.textContentItemMargin) }, 0) + if (tagIsBottom) { + // 文字下方 + tagContentWidth = totalTagWidth + tagContentHeight = maxTagHeight + } else { + // 否则在右侧 + textContentWidth += totalTagWidth + textContentHeight = Math.max(textContentHeight, maxTagHeight) + } } // 备注 if (this._noteData) { @@ -325,6 +339,15 @@ class Node { // 纯内容宽高 let _width = Math.max(imgContentWidth, textContentWidth) let _height = imgContentHeight + textContentHeight + // 如果标签在文字下方 + if (tagIsBottom && tagContentHeight > 0 && textContentHeight > 0) { + // 那么文字和标签之间也需要间距 + margin += this.blockContentMargin + // 整体高度要考虑标签宽度 + _width = Math.max(_width, tagContentWidth) + // 整体高度要加上标签的高度 + _height += tagContentHeight + } // 计算节点形状需要的附加内边距 let { paddingX: shapePaddingX, paddingY: shapePaddingY } = this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY) @@ -342,7 +365,7 @@ class Node { layout() { // 清除之前的内容 this.group.clear() - const { hoverRectPadding } = this.mindMap.opt + const { hoverRectPadding, tagPosition } = this.mindMap.opt let { width, height, textContentItemMargin } = this let { paddingY } = this.getPaddingVale() const halfBorderWidth = this.getBorderWidth() / 2 @@ -382,6 +405,8 @@ class Node { addHoverNode() return } + const tagIsBottom = tagPosition === CONSTANTS.TAG_POSITION.BOTTOM + const { textContentHeight } = this._rectInfo // 图片节点 let imgHeight = 0 if (this._imgData) { @@ -401,7 +426,7 @@ class Node { }) foreignObject .x(textContentOffsetX) - .y((this._rectInfo.textContentHeight - this._prefixData.height) / 2) + .y((textContentHeight - this._prefixData.height) / 2) textContentNested.add(foreignObject) textContentOffsetX += this._prefixData.width + textContentItemMargin } @@ -412,7 +437,7 @@ class Node { this._iconData.forEach(item => { item.node .x(textContentOffsetX + iconLeft) - .y((this._rectInfo.textContentHeight - item.height) / 2) + .y((textContentHeight - item.height) / 2) iconNested.add(item.node) iconLeft += item.width + textContentItemMargin }) @@ -427,7 +452,7 @@ class Node { ;(this._textData.nodeContent || this._textData.node) .x(-oldX) // 修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题 .x(textContentOffsetX) - .y((this._rectInfo.textContentHeight - this._textData.height) / 2) + .y((textContentHeight - this._textData.height) / 2) textContentNested.add(this._textData.node) textContentOffsetX += this._textData.width + textContentItemMargin } @@ -435,29 +460,50 @@ class Node { if (this._hyperlinkData) { this._hyperlinkData.node .x(textContentOffsetX) - .y((this._rectInfo.textContentHeight - this._hyperlinkData.height) / 2) + .y((textContentHeight - this._hyperlinkData.height) / 2) textContentNested.add(this._hyperlinkData.node) textContentOffsetX += this._hyperlinkData.width + textContentItemMargin } // 标签 let tagNested = new G() if (this._tagData && this._tagData.length > 0) { - let tagLeft = 0 - this._tagData.forEach(item => { - item.node - .x(textContentOffsetX + tagLeft) - .y((this._rectInfo.textContentHeight - item.height) / 2) - tagNested.add(item.node) - tagLeft += item.width + textContentItemMargin - }) - textContentNested.add(tagNested) - textContentOffsetX += tagLeft + if (tagIsBottom) { + // 标签显示在文字下方 + let tagLeft = 0 + this._tagData.forEach(item => { + item.node.x(tagLeft).y(0) + tagNested.add(item.node) + tagLeft += item.width + textContentItemMargin + }) + tagNested.cx(width / 2).y( + paddingY + // 内边距 + imgHeight + // 图片高度 + textContentHeight + // 文本区域高度 + (imgHeight > 0 && textContentHeight > 0 + ? this.blockContentMargin + : 0) + // 图片和文本之间的间距 + this.blockContentMargin // 标签和文本之间的间距 + ) + this.group.add(tagNested) + } else { + // 标签显示在文字右侧 + let tagLeft = 0 + this._tagData.forEach(item => { + item.node + .x(textContentOffsetX + tagLeft) + .y((textContentHeight - item.height) / 2) + tagNested.add(item.node) + tagLeft += item.width + textContentItemMargin + }) + textContentNested.add(tagNested) + textContentOffsetX += tagLeft + } } // 备注 if (this._noteData) { this._noteData.node .x(textContentOffsetX) - .y((this._rectInfo.textContentHeight - this._noteData.height) / 2) + .y((textContentHeight - this._noteData.height) / 2) textContentNested.add(this._noteData.node) textContentOffsetX += this._noteData.width } @@ -465,7 +511,7 @@ class Node { if (this._attachmentData) { this._attachmentData.node .x(textContentOffsetX) - .y((this._rectInfo.textContentHeight - this._attachmentData.height) / 2) + .y((textContentHeight - this._attachmentData.height) / 2) textContentNested.add(this._attachmentData.node) textContentOffsetX += this._attachmentData.width } @@ -478,18 +524,16 @@ class Node { }) foreignObject .x(textContentOffsetX) - .y((this._rectInfo.textContentHeight - this._postfixData.height) / 2) + .y((textContentHeight - this._postfixData.height) / 2) textContentNested.add(foreignObject) textContentOffsetX += this._postfixData.width } // 文字内容整体 textContentNested.translate( width / 2 - textContentNested.bbox().width / 2, - imgHeight + - paddingY + - (imgHeight > 0 && this._rectInfo.textContentHeight > 0 - ? this.blockContentMargin - : 0) + paddingY + // 内边距 + imgHeight + // 图片高度 + (imgHeight > 0 && textContentHeight > 0 ? this.blockContentMargin : 0) // 和图片的间距 ) this.group.add(textContentNested) addHoverNode() diff --git a/simple-mind-map/src/core/render/node/Style.js b/simple-mind-map/src/core/render/node/Style.js index 2f033692..1248fbb7 100644 --- a/simple-mind-map/src/core/render/node/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -1,8 +1,4 @@ -import { - checkIsNodeStyleDataKey, - generateColorByContent -} from '../../../utils/index' -import { Gradient } from '@svgdotjs/svg.js' +import { checkIsNodeStyleDataKey } from '../../../utils/index' const rootProp = ['paddingX', 'paddingY'] const backgroundStyleProps = [ @@ -182,21 +178,24 @@ class Style { } // 标签文字 - tagText(node) { + tagText(node, style) { node .fill({ color: '#fff' }) .css({ - 'font-size': '12px' + 'font-size': style.fontSize + 'px' }) } // 标签矩形 - tagRect(node, text, color) { + tagRect(node, style) { node.fill({ - color: color || generateColorByContent(text.node.textContent) + color: style.fill }) + if (style.radius) { + node.radius(style.radius) + } } // 内置图标 diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index 343e5181..04fea5ef 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -6,12 +6,23 @@ import { checkIsRichText, isUndef, createForeignObjectNode, - addXmlns + addXmlns, + generateColorByContent } from '../../../utils' import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js' import iconsSvg from '../../../svg/icons' import { CONSTANTS } from '../../../constants/constant' +// 标签默认的样式 +const defaultTagStyle = { + radius: 3, // 标签矩形的圆角大小 + fontSize: 12, // 字号,建议文字高度不要大于height + fill: '', // 标签矩形的背景颜色 + height: 20, // 标签矩形的高度 + paddingX: 8 // 水平内边距,如果设置了width,将忽略该配置 + //width: 30 // 标签矩形的宽度,如果不设置,默认以文字的宽度+paddingX*2为宽度 +} + // 创建图片节点 function createImgNode() { const img = this.getData('image') @@ -284,31 +295,69 @@ function createHyperlinkNode() { // 创建标签节点 function createTagNode() { - let tagData = this.getData('tag') + const tagData = this.getData('tag') if (!tagData || tagData.length <= 0) { return [] } - let nodes = [] - tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => { - let tag = new G() + let { maxTag, tagsColorMap } = this.mindMap.opt + tagsColorMap = tagsColorMap || {} + const nodes = [] + tagData.slice(0, maxTag).forEach(item => { + let str = '' + let style = { + ...defaultTagStyle + } + // 旧版只支持字符串类型 + if (typeof item === 'string') { + str = item + } else { + // v0.10.3+版本支持对象类型 + str = item.text + style = { ...defaultTagStyle, ...item.style } + } + // 是否手动设置了标签宽度 + const hasCustomWidth = typeof style.width !== 'undefined' + // 创建容器节点 + const tag = new G() tag.on('click', () => { this.mindMap.emit('node_tag_click', this, item) }) // 标签文本 - let text = new Text().text(item).x(8).cy(8) - this.style.tagText(text, index) - let { width } = text.bbox() + const text = new Text().text(str) + this.style.tagText(text, style) + // 获取文本宽高 + const { width: textWidth, height: textHeight } = text.bbox() + // 矩形宽度 + const rectWidth = hasCustomWidth + ? style.width + : textWidth + style.paddingX * 2 + // 取文本和矩形最大宽高作为标签宽高 + const maxWidth = hasCustomWidth ? Math.max(rectWidth, textWidth) : rectWidth + const maxHeight = Math.max(style.height, textHeight) + // 文本居中 + if (hasCustomWidth) { + text.x((maxWidth - textWidth) / 2) + } else { + text.x(hasCustomWidth ? 0 : style.paddingX) + } + text.cy(-maxHeight / 2) // 标签矩形 - let rect = new Rect().size(width + 16, 20) - // 先从自定义的颜色中获取颜色,没有的话就按照内容生成 - const tagsColorList = this.mindMap.opt.tagsColorMap || {} - const color = tagsColorList[text.node.textContent] - this.style.tagRect(rect, text, color) + const rect = new Rect().size(rectWidth, style.height).cy(-maxHeight / 2) + if (hasCustomWidth) { + rect.x((maxWidth - rectWidth) / 2) + } + this.style.tagRect(rect, { + ...style, + fill: + style.fill || // 优先节点自身配置 + tagsColorMap[text.node.textContent] || // 否则尝试从实例化选项tagsColorMap映射中获取颜色 + generateColorByContent(text.node.textContent) // 否则按照标签内容生成 + }) tag.add(rect).add(text) nodes.push({ node: tag, - width: width + 16, - height: 20 + width: maxWidth, + height: maxHeight }) }) return nodes From 12c6479c0d3691570e9fd98a7accd386fa40bcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Tue, 9 Jul 2024 15:54:06 +0800 Subject: [PATCH 07/46] =?UTF-8?q?Feat=EF=BC=9Anode=5Ftag=5Fclick=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E6=96=B0=E5=A2=9E=E4=B8=A4=E4=B8=AA=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/node/nodeCreateContents.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index 04fea5ef..7592086a 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -302,7 +302,7 @@ function createTagNode() { let { maxTag, tagsColorMap } = this.mindMap.opt tagsColorMap = tagsColorMap || {} const nodes = [] - tagData.slice(0, maxTag).forEach(item => { + tagData.slice(0, maxTag).forEach((item, index) => { let str = '' let style = { ...defaultTagStyle @@ -320,7 +320,7 @@ function createTagNode() { // 创建容器节点 const tag = new G() tag.on('click', () => { - this.mindMap.emit('node_tag_click', this, item) + this.mindMap.emit('node_tag_click', this, item, index, tag) }) // 标签文本 const text = new Text().text(str) From 4777ab3e5858ca292737bd05ff39c9680875cc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Tue, 9 Jul 2024 15:57:46 +0800 Subject: [PATCH 08/46] =?UTF-8?q?Demo=EF=BC=9A=E6=94=AF=E6=8C=81=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E8=8A=82=E7=82=B9=E6=A0=87=E7=AD=BE=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E5=92=8C=E9=A2=9C=E8=89=B2=E7=9A=84=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/config/zh.js | 2 +- web/src/lang/en_us.js | 4 + web/src/lang/zh_cn.js | 4 + web/src/pages/Edit/components/Edit.vue | 5 +- .../pages/Edit/components/NodeTagStyle.vue | 247 ++++++++++++++++++ 5 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 web/src/pages/Edit/components/NodeTagStyle.vue diff --git a/web/src/config/zh.js b/web/src/config/zh.js index 4c64b5e0..756bfe6d 100644 --- a/web/src/config/zh.js +++ b/web/src/config/zh.js @@ -97,7 +97,7 @@ export const colorList = [ '#0C797D', '#0062B1', '#653294', - '#AB149E', + // '#AB149E', 'transparent' ] diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js index 010a221a..4606f804 100644 --- a/web/src/lang/en_us.js +++ b/web/src/lang/en_us.js @@ -373,5 +373,9 @@ export default { boxStyle: 'Box style', boxColor: 'Box color', fillColor: 'Fill color' + }, + nodeTagStyle: { + placeholder: 'Please enter the tag content', + delete: 'Delete this tag' } } diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js index dd420124..0ffec9cc 100644 --- a/web/src/lang/zh_cn.js +++ b/web/src/lang/zh_cn.js @@ -365,5 +365,9 @@ export default { boxStyle: '边框样式', boxColor: '边框颜色', fillColor: '填充颜色' + }, + nodeTagStyle: { + placeholder: '请输入标签内容', + delete: '删除此标签' } } diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index da946aba..b0d17042 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -37,6 +37,7 @@ +
+
+
+ +
+ + {{ $t('nodeTagStyle.delete') }} +
+
+
+ +
+
+ + + + + From c1f600dc1f009d1043bbfe03d9821250a40033c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Thu, 11 Jul 2024 09:34:14 +0800 Subject: [PATCH 09/46] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E9=80=89=E4=B8=AD=E5=A4=9A=E4=B8=AA=E8=8A=82=E7=82=B9?= =?UTF-8?q?=EF=BC=8C=E5=8F=AF=E4=BB=A5=E4=B8=8D=E5=81=9C=E6=8F=92=E5=85=A5?= =?UTF-8?q?=E6=A6=82=E8=A6=81=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +-- simple-mind-map/src/core/render/Render.js | 30 ++++++++++++++++++----- web/public/index.html | 14 +++++++---- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 84b09825..96df40bf 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。百度云 > 客户端版本会落后于在线版本,尝试最新功能请优先使用在线版。 -如果你需要带后端的可部署版本,可以尝试我们开发的另一个项目[理想文档](https://github.com/wanglin2/lx-doc)。 +【云存储版本】如果你需要带后端的云存储版本,可以尝试我们开发的另一个项目[理想文档](https://github.com/wanglin2/lx-doc)。 # 特性 @@ -37,7 +37,7 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。百度云 - [x] 支持画布拖动、缩放 - [x] 支持鼠标按键拖动选择和 Ctrl+左键两种多选节点方式 - [x] 支持导出为`json`、`png`、`svg`、`pdf`、`markdown`、`xmind`、`txt`,支持从`json`、`xmind`、`markdown`导入 -- [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印、滚动条、手绘风格、彩虹线条 +- [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印、滚动条、手绘风格、彩虹线条、标记、外框 - [x] 提供丰富的配置,满足各种场景各种使用习惯 - [x] 支持协同编辑 - [x] 支持演示模式 diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index a62d5a2c..cc3793ca 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -1665,11 +1665,13 @@ class Render { ) }) const list = parseAddGeneralizationNodeList(nodeList) + if (list.length <= 0) return const isRichText = !!this.mindMap.richText const { focusNewNode, inserting } = this.getNewNodeBehavior( openEdit, list.length > 1 ) + let needRender = false list.forEach(item => { const newData = { inserting, @@ -1683,15 +1685,30 @@ class Render { isActive: focusNewNode } let generalization = item.node.getData('generalization') - if (generalization) { - if (Array.isArray(generalization)) { - generalization.push(newData) - } else { - generalization = [generalization, newData] + generalization = generalization + ? Array.isArray(generalization) + ? generalization + : [generalization] + : [] + // 如果是范围概要,那么检查该范围是否存在 + if (item.range) { + const isExist = !!generalization.find(item2 => { + return ( + item2.range && + item2.range[0] === item.range[0] && + item2.range[1] === item.range[1] + ) + }) + if (isExist) { + return } + // 不存在则添加 + generalization.push(newData) } else { - generalization = [newData] + // 不是范围概要直接添加,因为前面已经判断过是否存在 + generalization.push(newData) } + needRender = true this.mindMap.execCommand('SET_NODE_DATA', item.node, { generalization }) @@ -1700,6 +1717,7 @@ class Render { expand: true }) }) + if (!needRender) return // 需要清除原来激活的节点 if (focusNewNode) { this.clearActiveNodeList() diff --git a/web/public/index.html b/web/public/index.html index 7b08fffa..d7947ff2 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -21,11 +21,15 @@ src="//sdk.51.la/js-sdk-pro.min.js" > From 44a883c4737232976ae4a0a97b9143b6de7d11e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Thu, 11 Jul 2024 09:53:54 +0800 Subject: [PATCH 10/46] =?UTF-8?q?Feat=EF=BC=9A=E5=A4=8D=E5=88=B6=E3=80=81?= =?UTF-8?q?=E5=89=AA=E5=88=87=E3=80=81=E7=A7=BB=E5=8A=A8=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=97=B6=EF=BC=8C=E6=8C=89=E5=85=B6=E5=9C=A8?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E4=B8=8A=E7=9A=84=E9=A1=BA=E5=BA=8F=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/Render.js | 9 ++++++--- simple-mind-map/src/layouts/LogicalStructure.js | 3 +++ simple-mind-map/src/plugins/Drag.js | 16 ++++++++++------ simple-mind-map/src/utils/index.js | 9 +++++++++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index cc3793ca..10635779 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -31,7 +31,8 @@ import { checkSmmFormatData, checkIsNodeStyleDataKey, removeRichTextStyes, - formatGetNodeGeneralization + formatGetNodeGeneralization, + sortNodeList } from '../../utils' import { shapeList } from './node/Shape' import { lineStyleProps } from '../../themes/default' @@ -1373,7 +1374,8 @@ class Render { if (this.activeNodeList.length <= 0) { return null } - const nodeList = getTopAncestorsFomNodeList(this.activeNodeList) + let nodeList = getTopAncestorsFomNodeList(this.activeNodeList) + nodeList = sortNodeList(nodeList) return nodeList.map(node => { return copyNodeTree({}, node, true) }) @@ -1385,11 +1387,12 @@ class Render { return } // 找出激活节点中的顶层节点列表,并过滤掉根节点 - const nodeList = getTopAncestorsFomNodeList(this.activeNodeList).filter( + let nodeList = getTopAncestorsFomNodeList(this.activeNodeList).filter( node => { return !node.isRoot } ) + nodeList = sortNodeList(nodeList) // 复制数据 const copyData = nodeList.map(node => { return copyNodeTree({}, node, true) diff --git a/simple-mind-map/src/layouts/LogicalStructure.js b/simple-mind-map/src/layouts/LogicalStructure.js index 9887e977..d866f1d0 100644 --- a/simple-mind-map/src/layouts/LogicalStructure.js +++ b/simple-mind-map/src/layouts/LogicalStructure.js @@ -31,11 +31,14 @@ class LogicalStructure extends Base { // 遍历数据计算节点的left、width、height computedBaseValue() { + let sortIndex = 0 walk( this.renderer.renderTree, null, (cur, parent, isRoot, layerIndex) => { let newNode = this.createNode(cur, parent, isRoot, layerIndex) + newNode.sortIndex = sortIndex + sortIndex++ // 根节点定位在画布中心位置 if (isRoot) { this.setNodeCenter(newNode) diff --git a/simple-mind-map/src/plugins/Drag.js b/simple-mind-map/src/plugins/Drag.js index aed80b63..a1928f2a 100644 --- a/simple-mind-map/src/plugins/Drag.js +++ b/simple-mind-map/src/plugins/Drag.js @@ -2,7 +2,8 @@ import { bfsWalk, throttle, getTopAncestorsFomNodeList, - getNodeIndexInNodeList + getNodeIndexInNodeList, + sortNodeList } from '../utils' import Base from '../layouts/Base' import { CONSTANTS } from '../constants/constant' @@ -258,11 +259,14 @@ class Drag extends Base { // 如果鼠标按下的节点是激活节点,那么保存当前所有激活的节点 if (node.getData('isActive')) { // 找出这些激活节点中的最顶层节点 - this.beingDragNodeList = getTopAncestorsFomNodeList( - // 过滤掉根节点和概要节点 - this.mindMap.renderer.activeNodeList.filter(item => { - return !item.isRoot && !item.isGeneralization - }) + // 并按索引从小到大排序 + this.beingDragNodeList = sortNodeList( + getTopAncestorsFomNodeList( + // 过滤掉根节点和概要节点 + this.mindMap.renderer.activeNodeList.filter(item => { + return !item.isRoot && !item.isGeneralization + }) + ) ) } else { // 否则只拖拽按下的节点 diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index a4b275fd..ad7e787a 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -1580,3 +1580,12 @@ export const defenseXSS = text => { export const addXmlns = el => { el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml') } + +// 给一组节点实例升序排序,依据其sortIndex值 +export const sortNodeList = nodeList => { + nodeList = [...nodeList] + nodeList.sort((a, b) => { + return a.sortIndex - b.sortIndex + }) + return nodeList +} From f819cbc5b13dfd133e875d2da7e7214f6f674c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Fri, 12 Jul 2024 15:18:48 +0800 Subject: [PATCH 11/46] =?UTF-8?q?Demo=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E7=B1=BB=E5=9E=8B=E7=9A=84=E6=A0=87=E7=AD=BE=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=9C=A8=E6=A0=87=E7=AD=BE=E5=BC=B9=E7=AA=97=E9=87=8C?= =?UTF-8?q?=E5=9B=9E=E6=98=BE=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/components/NodeTag.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/pages/Edit/components/NodeTag.vue b/web/src/pages/Edit/components/NodeTag.vue index 858664fd..1c7210ed 100644 --- a/web/src/pages/Edit/components/NodeTag.vue +++ b/web/src/pages/Edit/components/NodeTag.vue @@ -24,7 +24,7 @@ backgroundColor: generateColorByContent(item) }" > - {{ item }} + {{ typeof item === 'string' ? item : item.text }}
From e293039b3c3bcd6fa386088294aca6a089a5c142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Fri, 12 Jul 2024 17:05:55 +0800 Subject: [PATCH 12/46] Doc: update --- simple-mind-map/package.json | 2 +- web/src/pages/Doc/en/changelog/index.md | 49 ++++++++++++++++ web/src/pages/Doc/en/changelog/index.vue | 37 ++++++++++++ web/src/pages/Doc/en/constructor/index.md | 20 ++++++- web/src/pages/Doc/en/constructor/index.vue | 63 ++++++++++++++++++++- web/src/pages/Doc/en/introduction/index.md | 8 +-- web/src/pages/Doc/en/introduction/index.vue | 5 +- web/src/pages/Doc/zh/changelog/index.md | 49 ++++++++++++++++ web/src/pages/Doc/zh/changelog/index.vue | 37 ++++++++++++ web/src/pages/Doc/zh/constructor/index.md | 20 ++++++- web/src/pages/Doc/zh/constructor/index.vue | 62 +++++++++++++++++++- web/src/pages/Doc/zh/course1/index.md | 2 +- web/src/pages/Doc/zh/course1/index.vue | 2 +- web/src/pages/Doc/zh/course2/index.md | 17 ++++++ web/src/pages/Doc/zh/course2/index.vue | 13 +++++ web/src/pages/Doc/zh/introduction/index.md | 8 +-- web/src/pages/Doc/zh/introduction/index.vue | 39 ++++++------- 17 files changed, 379 insertions(+), 54 deletions(-) diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index d36decee..78995e6d 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.10.2-fix.1", + "version": "0.10.3", "description": "一个简单的web在线思维导图", "authors": [ { diff --git a/web/src/pages/Doc/en/changelog/index.md b/web/src/pages/Doc/en/changelog/index.md index 32735770..bece7d96 100644 --- a/web/src/pages/Doc/en/changelog/index.md +++ b/web/src/pages/Doc/en/changelog/index.md @@ -1,5 +1,54 @@ # Changelog +## 0.10.3 + +> 2024.7.12 + +Node tag data structure update: + +The node tag data has been changed from a string array to an object array, mainly to support setting the style of a single tag. The current node tag data structure is as follows: + +```js +{ + tag: ['tag'] +} +``` + +Change to the following: + +```js +{ + tag: [ + { + text: 'tag', + style: {} + } + ] +} +``` + +Fix: + +> 1.Fix the issue where the isRoot and parent attributes of node instances obtained through methods customCreateNodeContent and createNodePrefixContent are both null; +> +> 2.Fixed the issue where the last edited node would enter editing mode when zooming in and out of the canvas using the scroll wheel or shortcut keys after editing the node text, and the mind map shortcut keys would become invalid; +> +> 3.Fixed the issue where multiple nodes can be selected simultaneously, allowing unlimited insertion of summaries; + +New: + +> 1.Support setting single label styles for nodes; +> +> 2.Add instantiation options for displaying the location of node labels; +> +> 3.Two callback parameters have been added to the node_tag_click event; +> +> 4.When copying, cutting, or moving multiple nodes, operate them in the order they are on the nodes, rather than in the order they are activated; + +Demo: + +> 1.Support clicking on node tags to modify text and color; + ## 0.10.2 / 0.10.2-fix.1 > 2024.7.3 diff --git a/web/src/pages/Doc/en/changelog/index.vue b/web/src/pages/Doc/en/changelog/index.vue index d4a9fa13..38225fb0 100644 --- a/web/src/pages/Doc/en/changelog/index.vue +++ b/web/src/pages/Doc/en/changelog/index.vue @@ -1,6 +1,43 @@