插入多个同级节点可以执行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 @@
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:
+{
+ tag: ['tag']
+}
+
+Change to the following:
+{
+ 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/constructor/index.md b/web/src/pages/Doc/en/constructor/index.md
index 94cd8743..50809ac2 100644
--- a/web/src/pages/Doc/en/constructor/index.md
+++ b/web/src/pages/Doc/en/constructor/index.md
@@ -34,6 +34,7 @@ const mindMap = new MindMap({
| themeConfig | Object | {} | Theme configuration, will be merged with the selected theme, available fields refer to: [default.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js) | |
| scaleRatio | Number | 0.1 | The incremental scaling ratio | |
| maxTag | Number | 5 | The maximum number of tags displayed in the node, any additional tags will be discarded | |
+| tagPosition(v0.10.3+) | String | right | The position of the tag display relative to the node text,bottom(Below the text)、right(On the right side of the text) | |
| imgTextMargin | Number | 5 | The spacing between the image and text in the node | |
| textContentMargin | Number | 2 | The spacing between various text information in the node, such as the spacing between the icon and text | |
| customNoteContentShow(v0.1.6+) | Object | null | Custom node note content display, object type, structure: {show: (noteContent, left, top, node) => {// your display node note logic. node is a new parameter added in v0.8.1+ version, representing node instances }, hide: () => {// your hide node note logic }} | |
@@ -121,7 +122,7 @@ The basic data structure is as follows:
hyperlink: '', // Hyperlink address
hyperlinkTitle: '', // Title of hyperlink
note: '', // Content of remarks
- tag: [], // Tag list
+ tag: [], // Tag list, Prior to v0.10.3, only string arrays, i.e. ['tag'], were supported. However, v0.10.3+versions support object arrays, i.e. [{text: 'tag', style: {}}]. The specific supported label styles can refer to the "Tag Styles" below
generalization: [{// (Arrays are not supported in versions below 0.9.0, and only a single summary data can be set)The summary of the node, if there is no summary, the generalization can be set to null
text: '', // Summary Text
richText: false, // Is the text of the node in rich text mode
@@ -142,6 +143,19 @@ The basic data structure is as follows:
If you want to add custom fields, you can add them to the same level as 'data' and 'children'. If you want to add them to the 'data' object, please use the `_` Name your custom field at the beginning, and it will be used internally to determine whether it is a custom field.
+##### Tag Styles
+
+The style object of the tag supports the following properties:
+
+| Field Name | Type | Default Value | Description |
+| ----------- | ------ | -------- | ----------- |
+| radius | Number | 3 | The corner size of the tag rectangle |
+| fontSize | Number | 12 | Font size, it is recommended that the height of the text should not exceed height |
+| fill | String | | Background color of tag rectangle |
+| height | Number | 20 | Height of tag rectangle |
+| paddingX | Number | 8 | Horizontal margin, if width is set, this configuration will be ignored |
+| width | Number | | The width of the tag rectangle, if not set, defaults to the width of the text plus paddingX * 2 |
+
### 1.2Icon Configuration
| Field Name | Type | Default Value | Description |
@@ -586,7 +600,7 @@ Listen to an event. Event list:
| node_cooperate_avatar_mouseleave(v0.9.9+) | Triggered when removing personnel avatars with the mouse during collaborative editing | userInfo(User info)、 this(Current node instance)、 node(Avatar node)、 e(Event Object) |
| exit_demonstrate(v0.9.11+) | Triggered when exiting demonstration mode | |
| demonstrate_jump(v0.9.11+) | Trigger when switching steps in demonstration mode | currentStepIndex(The index of the steps currently played, counting from 0)、stepLength(Total number of playback steps) |
-| node_tag_click(v0.9.12+) | Click events on node labels | this(Current node instance)、item(Content of clicked tags) |
+| node_tag_click(v0.9.12+) | Click events on node labels | this(Current node instance)、item(Content of clicked tags)、index(v0.10.3+,The index of this tag in the tag list)、tagNode(v0.10.3+,Tag node, G instance of @svgdotjs/svg.js library, Can be used to obtain label position and size information) |
| node_layout_end(v0.10.1+) | Event where the content layout of a single node is completed | this(Current node instance) |
| node_attachmentClick(v0.9.10+) | Click event for node attachment icon | this(Current node instance)、e(Event Object)、node(Icon node) |
| node_attachmentContextmenu(v0.9.10+) | Right click event on node attachment icon | this(Current node instance)、e(Event Object)、node(Icon node) |
@@ -690,7 +704,7 @@ redo. All commands are as follows:
| SET_NODE_HYPERLINK | Set Node Hyperlink | node (node to set), link (hyperlink address), title (hyperlink name, optional) |
| SET_NODE_NOTE | Set Node Note | node (node to set), note (note text) |
| SET_NODE_ATTACHMENT(v0.9.10+) | Set node attachment | node(node to set)、url(attachment url)、name(attachment name, optional) |
-| SET_NODE_TAG | Set Node Tag | node (node to set), tag (string array, built-in color information can be obtained in [constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/constants/constant.js)) |
+| SET_NODE_TAG | Set Node Tag | node (node to set), tag (Previous versions before v0.10.3 only support string arrays, i.e. ['tag'], while v0.10.3+versions support object arrays, i.e. [{text: 'tag', style: {} }]) |
| INSERT_AFTER (v0.1.5+) | Move Node to After Another Node | node (node to move, (v0.7.2+supports passing node arrays to move multiple nodes simultaneously)), exist (target node) |
| INSERT_BEFORE (v0.1.5+) | Move Node to Before Another Node | node (node to move, (v0.7.2+supports passing node arrays to move multiple nodes simultaneously)), exist (target node) |
| MOVE_NODE_TO (v0.1.5+) | Move a node as a child of another node | node (the node to move, (v0.7.2+supports passing node arrays to move multiple nodes simultaneously)), toNode (the target node) |
diff --git a/web/src/pages/Doc/en/constructor/index.vue b/web/src/pages/Doc/en/constructor/index.vue
index d761797e..fc93f587 100644
--- a/web/src/pages/Doc/en/constructor/index.vue
+++ b/web/src/pages/Doc/en/constructor/index.vue
@@ -86,6 +86,13 @@
+tagPosition(v0.10.3+)
+String
+right
+The position of the tag display relative to the node text,bottom(Below the text)、right(On the right side of the text)
+
+
+
imgTextMargin
Number
5
@@ -554,7 +561,7 @@
hyperlink: '', // Hyperlink address
hyperlinkTitle: '', // Title of hyperlink
note: '', // Content of remarks
- tag: [], // Tag list
+ tag: [], // Tag list, Prior to v0.10.3, only string arrays, i.e. ['tag'], were supported. However, v0.10.3+versions support object arrays, i.e. [{text: 'tag', style: {}}]. The specific supported label styles can refer to the "Tag Styles" below
generalization: [{// (Arrays are not supported in versions below 0.9.0, and only a single summary data can be set)The summary of the node, if there is no summary, the generalization can be set to null
text: '', // Summary Text
richText: false, // Is the text of the node in rich text mode
@@ -573,6 +580,56 @@
}
If you want to add custom fields, you can add them to the same level as 'data' and 'children'. If you want to add them to the 'data' object, please use the _ Name your custom field at the beginning, and it will be used internally to determine whether it is a custom field.
+Tag Styles
+The style object of the tag supports the following properties:
+
+
+
+Field Name
+Type
+Default Value
+Description
+
+
+
+
+radius
+Number
+3
+The corner size of the tag rectangle
+
+
+fontSize
+Number
+12
+Font size, it is recommended that the height of the text should not exceed height
+
+
+fill
+String
+
+Background color of tag rectangle
+
+
+height
+Number
+20
+Height of tag rectangle
+
+
+paddingX
+Number
+8
+Horizontal margin, if width is set, this configuration will be ignored
+
+
+width
+Number
+
+The width of the tag rectangle, if not set, defaults to the width of the text plus paddingX * 2
+
+
+
1.2Icon Configuration
@@ -1620,7 +1677,7 @@ poor performance and should be used sparingly.
node_tag_click(v0.9.12+)
Click events on node labels
-this(Current node instance)、item(Content of clicked tags)
+this(Current node instance)、item(Content of clicked tags)、index(v0.10.3+,The index of this tag in the tag list)、tagNode(v0.10.3+,Tag node, G instance of @svgdotjs/svg.js library, Can be used to obtain label position and size information)
node_layout_end(v0.10.1+)
@@ -1820,7 +1877,7 @@ redo. All commands are as follows:
SET_NODE_TAG
Set Node Tag
-node (node to set), tag (string array, built-in color information can be obtained in constant.js)
+node (node to set), tag (Previous versions before v0.10.3 only support string arrays, i.e. ['tag'], while v0.10.3+versions support object arrays, i.e. [{text: 'tag', style: {} }])
INSERT_AFTER (v0.1.5+)
diff --git a/web/src/pages/Doc/en/introduction/index.md b/web/src/pages/Doc/en/introduction/index.md
index e2a66497..e97b000e 100644
--- a/web/src/pages/Doc/en/introduction/index.md
+++ b/web/src/pages/Doc/en/introduction/index.md
@@ -90,13 +90,7 @@ This project may not have fully tested every function point, so there may be bug
If you have suggestions or find bugs, you can submit [issues](https://github.com/wanglin2/mind-map/issues) here.
-The built-in themes and icons in the project part come from:
-
-[Baidu Mind Map](https://naotu.baidu.com/)
-
-[Zhixi Mind Map](https://www.zhixi.com/)
-
-Respect the copyright, and do not use the theme and icons directly for commercial projects.
+The built-in themes and icons in the project part come from:[Baidu Mind Map](https://naotu.baidu.com/)、[Zhixi Mind Map](https://www.zhixi.com/)。Respect the copyright, and do not use the theme and icons directly for commercial projects.
## Why not?
diff --git a/web/src/pages/Doc/en/introduction/index.vue b/web/src/pages/Doc/en/introduction/index.vue
index 55d54c9d..3982ef9b 100644
--- a/web/src/pages/Doc/en/introduction/index.vue
+++ b/web/src/pages/Doc/en/introduction/index.vue
@@ -68,10 +68,7 @@ full screen, support mini map
This project can be used for learning and reference. Please deeply experience whether it can meet your needs when using it for actual projects.
This project may not have fully tested every function point, so there may be bugs. In addition, when the number of nodes is very large, there may be some performance issues. Because everyone can accept different levels of congestion, you can test the maximum number of nodes yourself. Generally speaking, within 500 nodes, it is more smooth, while over 1000 nodes have more noticeable lag.
If you have suggestions or find bugs, you can submit issues here.
-The built-in themes and icons in the project part come from:
-
-
-Respect the copyright, and do not use the theme and icons directly for commercial projects.
+The built-in themes and icons in the project part come from:Baidu Mind Map、Zhixi Mind Map。Respect the copyright, and do not use the theme and icons directly for commercial projects.
Why not?
1.Zhixi
Zhixi is a free mind mapping product that supports multi end synchronization. The UI design is beautiful and the features are complete, but it is not open source, so it can only be used as a user and cannot be used in your project.
diff --git a/web/src/pages/Doc/zh/changelog/index.md b/web/src/pages/Doc/zh/changelog/index.md
index 03611d12..e85df405 100644
--- a/web/src/pages/Doc/zh/changelog/index.md
+++ b/web/src/pages/Doc/zh/changelog/index.md
@@ -1,5 +1,54 @@
# Changelog
+## 0.10.3
+
+> 2024.7.12
+
+节点标签数据结构更新:
+
+节点标签数据由字符串数组,改为对象数组,主要是为了支持设置单个标签的样式,当前节点的标签数据结构如下:
+
+```js
+{
+ tag: ['标签']
+}
+```
+
+改为如下:
+
+```js
+{
+ tag: [
+ {
+ text: '标签',
+ style: {}
+ }
+ ]
+}
+```
+
+修复:
+
+> 1.修复customCreateNodeContent、createNodePrefixContent等方法里获取到的节点实例的isRoot和parent等属性都为null的问题;
+>
+> 2.修复编辑过节点文本后,再使用滚轮或快捷键缩放画布时上次被编辑的节点会进入编辑状态,同时思维导图快捷键会失效的问题;
+>
+> 3.修复同时选中多个节点,可以无限插入概要的问题;
+
+新增:
+
+> 1.支持设置节点单个标签样式;
+>
+> 2.新增节点标签显示位置的实例化选项;
+>
+> 3.node_tag_click事件新增两个回调参数;
+>
+> 4.复制、剪切、移动多个节点时,按其在节点上的顺序进行操作,而不是激活的顺序;
+
+Demo:
+
+> 1.支持点击节点标签进行文本和颜色的修改;
+
## 0.10.2 / 0.10.2-fix.1
> 2024.7.3
diff --git a/web/src/pages/Doc/zh/changelog/index.vue b/web/src/pages/Doc/zh/changelog/index.vue
index 1f87ef75..f27f5d2a 100644
--- a/web/src/pages/Doc/zh/changelog/index.vue
+++ b/web/src/pages/Doc/zh/changelog/index.vue
@@ -1,6 +1,43 @@
Changelog
+0.10.3
+
+2024.7.12
+
+节点标签数据结构更新:
+节点标签数据由字符串数组,改为对象数组,主要是为了支持设置单个标签的样式,当前节点的标签数据结构如下:
+{
+ tag: ['标签']
+}
+
+改为如下:
+{
+ tag: [
+ {
+ text: '标签',
+ style: {}
+ }
+ ]
+}
+
+修复:
+
+1.修复customCreateNodeContent、createNodePrefixContent等方法里获取到的节点实例的isRoot和parent等属性都为null的问题;
+2.修复编辑过节点文本后,再使用滚轮或快捷键缩放画布时上次被编辑的节点会进入编辑状态,同时思维导图快捷键会失效的问题;
+3.修复同时选中多个节点,可以无限插入概要的问题;
+
+新增:
+
+1.支持设置节点单个标签样式;
+2.新增节点标签显示位置的实例化选项;
+3.node_tag_click事件新增两个回调参数;
+4.复制、剪切、移动多个节点时,按其在节点上的顺序进行操作,而不是激活的顺序;
+
+Demo:
+
+1.支持点击节点标签进行文本和颜色的修改;
+
0.10.2 / 0.10.2-fix.1
2024.7.3
diff --git a/web/src/pages/Doc/zh/constructor/index.md b/web/src/pages/Doc/zh/constructor/index.md
index 2f6972a5..bfcb3da0 100644
--- a/web/src/pages/Doc/zh/constructor/index.md
+++ b/web/src/pages/Doc/zh/constructor/index.md
@@ -34,6 +34,7 @@ const mindMap = new MindMap({
| themeConfig | Object | {} | 主题配置,会和所选择的主题进行合并,可用字段可参考:[default.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js) |
| scaleRatio | Number | 0.1 | 放大缩小的增量比例 |
| maxTag | Number | 5 | 节点里最多显示的标签数量,多余的会被丢弃 |
+| tagPosition(v0.10.3+) | String | right | 标签显示的位置,相对于节点文本,bottom(下方)、right(右侧) |
| imgTextMargin | Number | 5 | 节点里图片和文字的间距 |
| textContentMargin | Number | 2 | 节点里各种文字信息的间距,如图标和文字的间距 |
| customNoteContentShow(v0.1.6+) | Object | null | 自定义节点备注内容显示,Object类型,结构为:{show: (noteContent, left, top, node) => {// 你的显示节点备注逻辑。node为v0.8.1+版本新增的回参,代表节点实例 }, hide: () => {// 你的隐藏节点备注逻辑 }} |
@@ -123,7 +124,7 @@ const mindMap = new MindMap({
note: '', // 备注的内容
attachmentUrl: '',// v0.9.10+,附件url
attachmentName: '',// v0.9.10+,附件名称
- tag: [], // 标签列表
+ tag: [], // 标签列表,v0.10.3以前的版本只支持字符串数组,即['标签'],v0.10.3+版本支持对象数组,即[{text: '标签', style: {}}],具体支持的标签样式可参考下方【标签的样式】
generalization: [{// (0.9.0以下版本不支持数组,只能设置单个概要数据)节点的概要,如果没有概要generalization设为null即可
text: '', // 概要的文本
richText: false, // 节点的文本是否是富文本模式
@@ -144,6 +145,19 @@ const mindMap = new MindMap({
如果你要添加自定义的字段,可以添加到`data`、`children`同级,如果你要添加到`data`对象里,那么请使用`_`开头来命名你的自定义字段,内部会通过这个来判断是否是自定义字段。
+##### 标签的样式
+
+标签的样式`style`对象支持以下属性:
+
+| 字段名称 | 类型 | 默认值 | 描述 |
+| ----------- | ------ | -------- | ----------- |
+| radius | Number | 3 | 标签矩形的圆角大小 |
+| fontSize | Number | 12 | 字号,建议文字高度不要大于height |
+| fill | String | | 标签矩形的背景颜色 |
+| height | Number | 20 | 标签矩形的高度 |
+| paddingX | Number | 8 | 水平内边距,如果设置了width,将忽略该配置 |
+| width | Number | | 标签矩形的宽度,如果不设置,默认以文字的宽度+paddingX*2为宽度 |
+
#### 1.2图标配置
| 字段名称 | 类型 | 默认值 | 描述 |
@@ -585,7 +599,7 @@ mindMap.setTheme('主题名称')
| node_cooperate_avatar_mouseleave(v0.9.9+) | 协同编辑时,鼠标移除人员头像时触发 | userInfo(人员信息)、 this(当前节点实例)、 node(头像节点)、 e(事件对象) |
| exit_demonstrate(v0.9.11+) | 退出演示模式时触发 | |
| demonstrate_jump(v0.9.11+) | 演示模式中,切换步骤时触发 | currentStepIndex(当前播放到的步骤索引,从0开始计数)、stepLength(总的播放步骤数量) |
-| node_tag_click(v0.9.12+) | 节点标签的点击事件 | this(当前节点实例)、item(点击的标签内容) |
+| node_tag_click(v0.9.12+) | 节点标签的点击事件 | this(当前节点实例)、item(点击的标签内容)、index(v0.10.3+,该标签在标签列表里的索引)、tagNode(v0.10.3+,标签节点,@svgdotjs/svg.js库的G实例,可以用于获取标签位置和大小信息) |
| node_layout_end(v0.10.1+) | 单个节点内容布局完成的事件 | this(当前节点实例) |
| node_attachmentClick(v0.9.10+) | 节点附件图标的点击事件 | this(当前节点实例)、e(事件对象)、node(图标节点) |
| node_attachmentContextmenu(v0.9.10+) | 节点附件图标的右键点击事件 | this(当前节点实例)、e(事件对象)、node(图标节点) |
@@ -687,7 +701,7 @@ mindMap.updateConfig({
| SET_NODE_HYPERLINK | 设置节点超链接 | node(要设置的节点)、link(超链接地址)、title(超链接名称,可选) |
| SET_NODE_NOTE | 设置节点备注 | node(要设置的节点)、note(备注文字) |
| SET_NODE_ATTACHMENT(v0.9.10+) | 设置节点附件 | node(要设置的节点)、url(附件url)、name(附件名称,可选) |
-| SET_NODE_TAG | 设置节点标签 | node(要设置的节点)、tag(字符串数组,内置颜色信息可在[constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/constants/constant.js)里获取到) |
+| SET_NODE_TAG | 设置节点标签 | node(要设置的节点)、tag(v0.10.3以前的版本只支持字符串数组,即['标签'],v0.10.3+版本支持对象数组,即[{ text: '标签', style: {} }]) |
| INSERT_AFTER(v0.1.5+) | 将节点移动到另一个节点的后面 | node(要移动的节点,(v0.7.2+支持传递节点数组实现同时移动多个节点))、 exist(目标节点) |
| INSERT_BEFORE(v0.1.5+) | 将节点移动到另一个节点的前面,(v0.7.2+支持传递节点数组实现同时移动多个节点) | node(要移动的节点)、 exist(目标节点) |
| MOVE_NODE_TO(v0.1.5+) | 移动节点作为另一个节点的子节点,(v0.7.2+支持传递节点数组实现同时移动多个节点) | node(要移动的节点)、 toNode(目标节点) |
diff --git a/web/src/pages/Doc/zh/constructor/index.vue b/web/src/pages/Doc/zh/constructor/index.vue
index c8dd2d69..90d281bc 100644
--- a/web/src/pages/Doc/zh/constructor/index.vue
+++ b/web/src/pages/Doc/zh/constructor/index.vue
@@ -77,6 +77,12 @@
节点里最多显示的标签数量,多余的会被丢弃
+tagPosition(v0.10.3+)
+String
+right
+标签显示的位置,相对于节点文本,bottom(下方)、right(右侧)
+
+
imgTextMargin
Number
5
@@ -483,7 +489,7 @@
note: '', // 备注的内容
attachmentUrl: '',// v0.9.10+,附件url
attachmentName: '',// v0.9.10+,附件名称
- tag: [], // 标签列表
+ tag: [], // 标签列表,v0.10.3以前的版本只支持字符串数组,即['标签'],v0.10.3+版本支持对象数组,即[{text: '标签', style: {}}],具体支持的标签样式可参考下方【标签的样式】
generalization: [{// (0.9.0以下版本不支持数组,只能设置单个概要数据)节点的概要,如果没有概要generalization设为null即可
text: '', // 概要的文本
richText: false, // 节点的文本是否是富文本模式
@@ -502,6 +508,56 @@
}
如果你要添加自定义的字段,可以添加到data、children同级,如果你要添加到data对象里,那么请使用_开头来命名你的自定义字段,内部会通过这个来判断是否是自定义字段。
+标签的样式
+标签的样式style对象支持以下属性:
+
+
+
+字段名称
+类型
+默认值
+描述
+
+
+
+
+radius
+Number
+3
+标签矩形的圆角大小
+
+
+fontSize
+Number
+12
+字号,建议文字高度不要大于height
+
+
+fill
+String
+
+标签矩形的背景颜色
+
+
+height
+Number
+20
+标签矩形的高度
+
+
+paddingX
+Number
+8
+水平内边距,如果设置了width,将忽略该配置
+
+
+width
+Number
+
+标签矩形的宽度,如果不设置,默认以文字的宽度+paddingX*2为宽度
+
+
+
1.2图标配置
@@ -1486,7 +1542,7 @@ mindMap.setTheme('主题名称')
node_tag_click(v0.9.12+)
节点标签的点击事件
-this(当前节点实例)、item(点击的标签内容)
+this(当前节点实例)、item(点击的标签内容)、index(v0.10.3+,该标签在标签列表里的索引)、tagNode(v0.10.3+,标签节点,@svgdotjs/svg.js库的G实例,可以用于获取标签位置和大小信息)
node_layout_end(v0.10.1+)
@@ -1688,7 +1744,7 @@ mindMap.setTheme('主题名称')
SET_NODE_TAG
设置节点标签
-node(要设置的节点)、tag(字符串数组,内置颜色信息可在constant.js里获取到)
+node(要设置的节点)、tag(v0.10.3以前的版本只支持字符串数组,即['标签'],v0.10.3+版本支持对象数组,即[{ text: '标签', style: {} }])
INSERT_AFTER(v0.1.5+)
diff --git a/web/src/pages/Doc/zh/course1/index.md b/web/src/pages/Doc/zh/course1/index.md
index 21b24636..b6893b2f 100644
--- a/web/src/pages/Doc/zh/course1/index.md
+++ b/web/src/pages/Doc/zh/course1/index.md
@@ -43,7 +43,7 @@
note: '', // 备注的内容
attachmentUrl: '',// v0.9.10+,附件url
attachmentName: '',// v0.9.10+,附件名称
- tag: [], // 标签列表
+ tag: [], // 标签列表,v0.10.3以前的版本只支持字符串数组,即['标签'],v0.10.3+版本支持对象数组,即[{text: '标签', style: {}}]
generalization: [{// (0.9.0以下版本不支持数组,只能设置单个概要数据)节点的概要,如果没有概要generalization设为null即可
text: '', // 概要的文本
richText: false, // 节点的文本是否是富文本模式
diff --git a/web/src/pages/Doc/zh/course1/index.vue b/web/src/pages/Doc/zh/course1/index.vue
index 69ee5dda..8ea66a6a 100644
--- a/web/src/pages/Doc/zh/course1/index.vue
+++ b/web/src/pages/Doc/zh/course1/index.vue
@@ -35,7 +35,7 @@
note: '', // 备注的内容
attachmentUrl: '',// v0.9.10+,附件url
attachmentName: '',// v0.9.10+,附件名称
- tag: [], // 标签列表
+ tag: [], // 标签列表,v0.10.3以前的版本只支持字符串数组,即['标签'],v0.10.3+版本支持对象数组,即[{text: '标签', style: {}}]
generalization: [{// (0.9.0以下版本不支持数组,只能设置单个概要数据)节点的概要,如果没有概要generalization设为null即可
text: '', // 概要的文本
richText: false, // 节点的文本是否是富文本模式
diff --git a/web/src/pages/Doc/zh/course2/index.md b/web/src/pages/Doc/zh/course2/index.md
index 21b99590..99d93dfa 100644
--- a/web/src/pages/Doc/zh/course2/index.md
+++ b/web/src/pages/Doc/zh/course2/index.md
@@ -148,6 +148,23 @@ activeNodes.value.forEach(node => {
const tagArr = activeNode.getData('tag') || []
```
+v0.10.3+版本支持设置单个标签的样式,所以标签数据的类型也由字符串数组(即`['标签']`),改为对象数组(即`[{text: '标签', style: {}}]`)。所以要插入指定样式的标签可以这样操作:
+
+```js
+activeNodes.value.forEach(node => {
+ node.setTag([
+ {
+ text: '标签1',
+ style: {
+ fill: 'red'
+ }
+ }
+ ])
+})
+```
+
+`style`具体支持的属性可以参考【构造函数】文档。
+
## 插入概要
插入概要可以不需要实现UI,直接调用插入概要的命令即可:
diff --git a/web/src/pages/Doc/zh/course2/index.vue b/web/src/pages/Doc/zh/course2/index.vue
index 6de6d034..c458da6d 100644
--- a/web/src/pages/Doc/zh/course2/index.vue
+++ b/web/src/pages/Doc/zh/course2/index.vue
@@ -97,6 +97,19 @@ activeNodes.value.forEach(
获取节点标签数据:
const tagArr = activeNode.getData('tag') || []
+v0.10.3+版本支持设置单个标签的样式,所以标签数据的类型也由字符串数组(即['标签']),改为对象数组(即[{text: '标签', style: {}}])。所以要插入指定样式的标签可以这样操作:
+activeNodes.value.forEach(node => {
+ node.setTag([
+ {
+ text: '标签1',
+ style: {
+ fill: 'red'
+ }
+ }
+ ])
+})
+
+style具体支持的属性可以参考【构造函数】文档。
插入概要
插入概要可以不需要实现UI,直接调用插入概要的命令即可:
mindMap.execCommand('ADD_GENERALIZATION', data)
diff --git a/web/src/pages/Doc/zh/introduction/index.md b/web/src/pages/Doc/zh/introduction/index.md
index a77d9ea5..88a2c66a 100644
--- a/web/src/pages/Doc/zh/introduction/index.md
+++ b/web/src/pages/Doc/zh/introduction/index.md
@@ -82,13 +82,7 @@
如果有建议或发现了bug,可以在此提交[issues](https://github.com/wanglin2/mind-map/issues)。
-项目内置的主题和图标部分来自于:
-
-[百度脑图](https://naotu.baidu.com/)
-
-[知犀思维导图](https://www.zhixi.com/)
-
-尊重版权,主题和图标请勿直接用于商业项目。
+项目内置的主题和图标部分来自于:[百度脑图](https://naotu.baidu.com/)、[知犀思维导图](https://www.zhixi.com/)。尊重版权,主题和图标请勿直接用于商业项目。
## 为什么不是?
diff --git a/web/src/pages/Doc/zh/introduction/index.vue b/web/src/pages/Doc/zh/introduction/index.vue
index 637268f6..1b6e63ff 100644
--- a/web/src/pages/Doc/zh/introduction/index.vue
+++ b/web/src/pages/Doc/zh/introduction/index.vue
@@ -8,18 +8,18 @@
特性
-
-
-
-
-
-
-
-json、png、svg、pdf、markdown、xmind、txt,支持从json、xmind、markdown导入
-
-
-
-
+
+
+
+
+
+
+
+json、png、svg、pdf、markdown、xmind、txt,支持从json、xmind、markdown导入
+
+
+
+
官方提供了如下插件,可根据需求按需引入(某个功能不生效大概率是因为你没有引入对应的插件),具体使用方式请查看文档:
@@ -37,11 +37,11 @@
2.web
使用simple-mind-map库,基于vue2.x、ElementUI搭建的在线思维导图。特性:
-
-
-
-
-
+
+
+
+
+
提供文档页面服务。
3.dist
@@ -59,10 +59,7 @@
本项目可用于学习和参考,用于实际项目时请先深度体验一下是否能满足您的需求。
本项目可能没有完整测试到每一个功能点,所以可能存在bug,另外,当节点数量非常多的时候,性能也存在一些问题,因为每个人能接受的卡顿程度不一样,所以你可以自行测试节点数量上限。一般来说,500个节点以内比较流畅,1000个节点以上卡顿比较明显。
如果有建议或发现了bug,可以在此提交issues。
-项目内置的主题和图标部分来自于:
-
-
-尊重版权,主题和图标请勿直接用于商业项目。
+项目内置的主题和图标部分来自于:百度脑图、知犀思维导图。尊重版权,主题和图标请勿直接用于商业项目。
为什么不是?
1.知犀
知犀是一个免费的思维导图产品,支持多端同步,ui设计很漂亮,功能也很齐全,但是它并不开源,所以只能作为一个用户,而无法在你的项目中使用。
From 6382e8acd8d48381ad2ae9c34ac7ddac012997b7 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, 16 Jul 2024 16:47:29 +0800
Subject: [PATCH 13/46] Doc: update
---
README.md | 8 ++++++++
web/src/assets/avatar/Tobin.jpg | Bin 0 -> 34733 bytes
web/src/pages/Doc/en/introduction/index.md | 8 ++++++++
web/src/pages/Doc/en/introduction/index.vue | 8 ++++++++
web/src/pages/Doc/zh/introduction/index.md | 8 ++++++++
web/src/pages/Doc/zh/introduction/index.vue | 8 ++++++++
6 files changed, 40 insertions(+)
create mode 100644 web/src/assets/avatar/Tobin.jpg
diff --git a/README.md b/README.md
index 96df40bf..d68edda7 100644
--- a/README.md
+++ b/README.md
@@ -415,4 +415,12 @@ const mindMap = new MindMap({
buddy
+
+
+ 小川
+
+
+
+ Tobin
+
diff --git a/web/src/assets/avatar/Tobin.jpg b/web/src/assets/avatar/Tobin.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..9ed6390039ad1b3a2077132455dac042446a28a0
GIT binary patch
literal 34733
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
+
+
+ 夏虫不语冰
+
+
+
+ 晴空
+
diff --git a/web/src/assets/avatar/夏虫不语冰.jpg b/web/src/assets/avatar/夏虫不语冰.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b3d22d0167014a35117bb69cc280e4259888781f
GIT binary patch
literal 20919
zcmbTe2Uru?_cuPNK1s;EC9spF#wPt
z^Z#>9hhTn|fdW828-V{T!vsGy8(6dE?w_AfA@rXT(3?Woe~&dqXT|{!Dr@JiohhuH
ziPmmT&VYx1a41|;Be=~TFP=R#Qd<@LaSXTwAK~hhz5UxiYo--2)`s4M-h@GH0jMzq
zW(=9R11toejDXB;v$ui&AW#?_fz;C0LFs}8nv4J_1O|h`VF-lgF_28~Isi9D%&~Ft
zL(ZkeXxS#AU9*eNYTNl=Y%`&E|GCI5Hu-=K%5eeC_(J+jr#c9i4aYDOKv8-rpbp(f54l#miU2uiuP}e)u>sIrZ1{-=98f
z+64h%Kbi&p|IxDltzE`oyP$A546dbV7X-Rn({N)r!o~$T$B(8JlQh@XHCr3)Uwrmr
zn~t3u{ZEtFoEg$~B-e#VHV!3pt
z572`_Kw*Lz1FL`=M0DilBSbV65)vFjK%n_lXg3iJ(1>VlES4pV2@W9wiyhH?Z6^2?
zsN!M%wUdzG1+YAL;{hH<2-WN*CrY>|OjUL+x{+%Av$z>p+iZ;(vFJyse@g%SM2P