Compare commits

...

19 Commits
0.9.0 ... 0.9.1

Author SHA1 Message Date
wanglin2
9fe94bfc21 打包0.9.1 2023-12-05 09:34:51 +08:00
wanglin2
6be6f6ee8a Doc 2023-12-05 09:31:13 +08:00
wanglin2
83ce090402 Fix:修复在节点文本编辑中和关联线文本编辑中时销毁思维导图文本编辑框未被销毁的问题 2023-12-05 09:02:41 +08:00
wanglin2
250ff30704 Demo:支持配置创建新节点时的行为 2023-12-04 11:25:04 +08:00
wanglin2
55d7d0a846 Feat:新增创建新节点的行为配置选项 2023-12-04 11:07:26 +08:00
wanglin2
0b3d2cedbd Fix:重新加上因新功能而丢失的旧功能:新创建的节点默认全选 2023-12-04 10:18:17 +08:00
wanglin2
de025d9bc7 Fix:修复不在格式刷时点击画布和节点也会触发painter_end事件的问题 2023-12-04 09:57:29 +08:00
wanglin2
79a39f993d Fix:修复点击节点也会触发node_dragend事件的问题 2023-12-04 09:51:56 +08:00
wanglin2
0fa731c3a2 Fix:修复在节点编辑状态中通过鼠标滚轮缩放画布再推出节点编辑后快捷键生效的问题 2023-12-01 10:23:32 +08:00
wanglin2
34eece8fcf Fix:修复节点处于编辑状态时,通过鼠标滚动移动画布后编辑框和节点脱离的问题 2023-12-01 09:59:33 +08:00
wanglin2
790662ad0c Demo: 增加快捷键提示 2023-12-01 09:52:17 +08:00
wanglin2
7d83a3635f Feat:在鼠标滚轮行为为上下移动画布时,支持按住Ctrl键改为放大缩小画布 2023-12-01 09:49:02 +08:00
wanglin2
546cf27b33 Feat:鼠标滚轮行为默认改为上下移动画布;改为默认向前滚动放大画布,向后缩小 2023-12-01 09:48:14 +08:00
wanglin2
e59d419708 Feat:只读模式下搜索时给当前匹配到的节点增加高亮效果 2023-11-30 17:40:12 +08:00
wanglin2
2d50106ce7 Feat:新增创建新节点时默认不聚焦新节点的配置选项 2023-11-29 08:50:13 +08:00
wanglin2
70c32e3c74 Fix:1.按住Ctrl键时禁用节点双击事件;2.优化节点激活事件的派发,激活节点未改变时不派发事件,短时间派发多次事件时跳过中间事件 2023-11-28 17:37:44 +08:00
wanglin2
a0f56473ee Fix:修复批量执行类添加同名任务时任务未更新为最小任务的问题 2023-11-28 17:36:13 +08:00
wanglin2
51dcb1f54f Fix:修复自定义节点内容时导出图片、svg、pdf报错的问题 2023-11-27 17:15:35 +08:00
wanglin2
9e34fd6174 Doc update 2023-11-24 13:45:03 +08:00
58 changed files with 741 additions and 232 deletions

View File

@@ -226,4 +226,8 @@ const mindMap = new MindMap({
<img src="./web/src/assets/avatar/moom.jpg" style="width: 50px;height: 50px;" />
<span>moom</span>
</span>
<span>
<img src="./web/src/assets/avatar/张扬.png" style="width: 50px;height: 50px;" />
<span>张扬</span>
</span>
</p>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel="icon" href="dist/logo.ico"><title>思绪思维导图</title><script>// 自定义静态资源的路径
window.externalPublicPath = './dist/'
// 接管应用
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?6450bdeb4871438bb6e4" rel="stylesheet"><link href="dist/css/app.css?6450bdeb4871438bb6e4" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?7820598bfce2a2653e5c" rel="stylesheet"><link href="dist/css/app.css?7820598bfce2a2653e5c" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
@@ -66,4 +66,4 @@
// 可以通过window.$bus.$on()来监听应用的一些事件
// 实例化页面
window.initApp()
}</script><script src="dist/js/chunk-vendors.js?6450bdeb4871438bb6e4"></script><script src="dist/js/app.js?6450bdeb4871438bb6e4"></script></body></html>
}</script><script src="dist/js/chunk-vendors.js?7820598bfce2a2653e5c"></script><script src="dist/js/app.js?7820598bfce2a2653e5c"></script></body></html>

View File

@@ -487,6 +487,12 @@ class MindMap {
// 销毁
destroy() {
this.emit('beforeDestroy')
// 清除节点编辑框
this.renderer.textEdit.hideEditTextBox()
// 清除关联线文字编辑框
if (this.associativeLine) {
this.associativeLine.hideEditTextBox()
}
// 移除插件
;[...MindMap.pluginList].forEach(plugin => {
if (this[plugin.instanceName].beforePluginDestroy) {

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.9.0",
"version": "0.9.1",
"description": "一个简单的web在线思维导图",
"authors": [
{

View File

@@ -229,6 +229,11 @@ export const CONSTANTS = {
SCROLL_BAR_DIR: {
VERTICAL: 'vertical',
HORIZONTAL: 'horizontal'
},
CREATE_NEW_NODE_BEHAVIOR: {
DEFAULT: 'default',
NOT_ACTIVE: 'notActive',
ACTIVE_ONLY: 'activeOnly'
}
}
@@ -351,3 +356,14 @@ export const cssContent = `
stroke-width: 2;
}
`
// html自闭合标签列表
export const selfCloseTagList = [
'img',
'br',
'hr',
'input',
'link',
'meta',
'area'
]

View File

@@ -56,11 +56,11 @@ export const defaultOpt = {
// 可以传一个函数,回调参数为事件对象
customHandleMousewheel: null,
// 鼠标滚动的行为如果customHandleMousewheel传了自定义函数这个属性不生效
mousewheelAction: CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM, // zoom放大缩小、move上下移动
mousewheelAction: CONSTANTS.MOUSE_WHEEL_ACTION.MOVE, // zoom放大缩小、move上下移动
// 当mousewheelAction设为move时可以通过该属性控制鼠标滚动一下视图移动的步长单位px
mousewheelMoveStep: 100,
// 当mousewheelAction设为zoom时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来
mousewheelZoomActionReverse: false,
// 当mousewheelAction设为zoom时或者按住Ctrl键时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来
mousewheelZoomActionReverse: true,
// 默认插入的二级节点的文字
defaultInsertSecondLevelNodeText: '二级节点',
// 默认插入的二级以下节点的文字
@@ -210,8 +210,8 @@ export const defaultOpt = {
tagsColorMap: {},
// 节点协作样式配置
cooperateStyle: {
avatarSize: 22,// 头像大小
fontSize: 12,// 如果是文字头像,那么文字的大小
avatarSize: 22, // 头像大小
fontSize: 12 // 如果是文字头像,那么文字的大小
},
// 关联线是否始终显示在节点上层
// false即创建关联线和激活关联线时处于最顶层其他情况下处于节点下方
@@ -229,5 +229,12 @@ export const defaultOpt = {
highlightNodeBoxStyle: {
stroke: 'rgb(94, 200, 248)',
fill: 'transparent'
}
},
// 创建新节点时的行为
/*
DEFAULT :默认会激活新创建的节点,并且进入编辑模式。如果同时创建了多个新节点,那么只会激活而不会进入编辑模式
NOT_ACTIVE : 不激活新创建的节点
ACTIVE_ONLY : 只激活新创建的节点,不进入编辑模式
*/
createNewNodeBehavior: CONSTANTS.CREATE_NEW_NODE_BEHAVIOR.DEFAULT
}

View File

@@ -133,18 +133,10 @@ class Event extends EventEmitter {
e.stopPropagation()
e.preventDefault()
let dir
// 解决mac触控板双指缩放方向相反的问题
if (e.ctrlKey) {
if (e.deltaY > 0) dir = CONSTANTS.DIR.UP
if (e.deltaY < 0) dir = CONSTANTS.DIR.DOWN
if (e.deltaX > 0) dir = CONSTANTS.DIR.LEFT
if (e.deltaX < 0) dir = CONSTANTS.DIR.RIGHT
} else {
if ((e.wheelDeltaY || e.detail) > 0) dir = CONSTANTS.DIR.UP
if ((e.wheelDeltaY || e.detail) < 0) dir = CONSTANTS.DIR.DOWN
if ((e.wheelDeltaX || e.detail) > 0) dir = CONSTANTS.DIR.LEFT
if ((e.wheelDeltaX || e.detail) < 0) dir = CONSTANTS.DIR.RIGHT
}
if (e.deltaY < 0) dir = CONSTANTS.DIR.UP
if (e.deltaY > 0) dir = CONSTANTS.DIR.DOWN
if (e.deltaX < 0) dir = CONSTANTS.DIR.LEFT
if (e.deltaX > 0) dir = CONSTANTS.DIR.RIGHT
// 判断是否是触控板
let isTouchPad = false
// mac、windows

View File

@@ -25,7 +25,8 @@ import {
setDataToClipboard,
getDataFromClipboard,
htmlEscape,
parseAddGeneralizationNodeList
parseAddGeneralizationNodeList,
checkNodeListIsEqual
} from '../../utils'
import { shapeList } from './node/Shape'
import { lineStyleProps } from '../../themes/default'
@@ -87,6 +88,9 @@ class Render {
this.currentBeingPasteType = ''
// 节点高亮框
this.highlightBoxNode = null
// 上一次节点激活数据
this.lastActiveNode = null
this.lastActiveNodeList = []
// 布局
this.setLayout()
// 绑定事件
@@ -335,6 +339,21 @@ class Render {
})
}
// 派发节点激活事件
emitNodeActiveEvent(node = null, activeNodeList = [...this.activeNodeList]) {
let isChange = false
isChange = this.lastActiveNode !== node
if (!isChange) {
isChange = !checkNodeListIsEqual(this.lastActiveNodeList, activeNodeList)
}
if (!isChange) return
this.lastActiveNode = node
this.lastActiveNodeList = [...activeNodeList]
this.mindMap.batchExecution.push('emitNodeActiveEvent', () => {
this.mindMap.emit('node_active', node, activeNodeList)
})
}
// 鼠标点击画布时清空当前激活节点列表
clearActiveNodeListOnDrawClick(e, eventType) {
if (this.activeNodeList.length <= 0) return
@@ -431,7 +450,7 @@ class Render {
return
}
this.clearActiveNodeList()
this.mindMap.emit('node_active', null, [])
this.emitNodeActiveEvent(null, [])
}
// 清除当前激活的节点列表
@@ -505,6 +524,36 @@ class Render {
}
}
// 获取创建新节点的行为
getNewNodeBehavior(openEdit = false, handleMultiNodes = false) {
const { createNewNodeBehavior } = this.mindMap.opt
let focusNewNode = false // 是否激活新节点
let inserting = false // 新节点是否进入编辑模式
switch (createNewNodeBehavior) {
// 默认会激活新创建的节点,并且进入编辑模式。如果同时创建了多个新节点,那么只会激活而不会进入编辑模式
case CONSTANTS.CREATE_NEW_NODE_BEHAVIOR.DEFAULT:
focusNewNode = handleMultiNodes || !openEdit
inserting = handleMultiNodes ? false : openEdit // 如果同时对多个节点插入子节点,那么无需进入编辑模式
break
// 不激活新创建的节点
case CONSTANTS.CREATE_NEW_NODE_BEHAVIOR.NOT_ACTIVE:
focusNewNode = false
inserting = false
break
// 只激活新创建的节点,不进入编辑模式
case CONSTANTS.CREATE_NEW_NODE_BEHAVIOR.ACTIVE_ONLY:
focusNewNode = true
inserting = false
break
default:
break
}
return {
focusNewNode,
inserting
}
}
// 插入同级节点
insertNode(
openEdit = true,
@@ -524,11 +573,15 @@ class Render {
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
const handleMultiNodes = list.length > 1
const isRichText = !!this.mindMap.richText
const { focusNewNode, inserting } = this.getNewNodeBehavior(
openEdit,
handleMultiNodes
)
const params = {
expand: true,
richText: isRichText,
resetRichText: isRichText,
isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
isActive: focusNewNode // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
}
// 动态指定的子节点数据也需要添加相关属性
appointChildren = addDataToAppointNodes(appointChildren, {
@@ -547,7 +600,7 @@ class Render {
// 计算插入位置
const index = getNodeDataIndex(node)
const newNodeData = {
inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式,
inserting,
data: {
text: text,
...params,
@@ -559,7 +612,7 @@ class Render {
parent.nodeData.children.splice(index + 1, 0, newNodeData)
})
// 如果同时对多个节点插入子节点,需要清除原来激活的节点
if (handleMultiNodes || !openEdit) {
if (focusNewNode) {
this.clearActiveNodeList()
}
this.mindMap.render()
@@ -575,11 +628,12 @@ class Render {
this.textEdit.hideEditTextBox()
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
const isRichText = !!this.mindMap.richText
const { focusNewNode } = this.getNewNodeBehavior(false, true)
const params = {
expand: true,
richText: isRichText,
resetRichText: isRichText,
isActive: true
isActive: focusNewNode
}
nodeList = addDataToAppointNodes(nodeList, params)
list.forEach(node => {
@@ -595,7 +649,9 @@ class Render {
)
parent.nodeData.children.splice(index + 1, 0, ...newNodeList)
})
this.clearActiveNodeList()
if (focusNewNode) {
this.clearActiveNodeList()
}
this.mindMap.render()
}
@@ -618,11 +674,15 @@ class Render {
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
const handleMultiNodes = list.length > 1
const isRichText = !!this.mindMap.richText
const { focusNewNode, inserting } = this.getNewNodeBehavior(
openEdit,
handleMultiNodes
)
const params = {
expand: true,
richText: isRichText,
resetRichText: isRichText,
isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
isActive: focusNewNode
}
// 动态指定的子节点数据也需要添加相关属性
appointChildren = addDataToAppointNodes(appointChildren, {
@@ -639,7 +699,7 @@ class Render {
? defaultInsertSecondLevelNodeText
: defaultInsertBelowSecondLevelNodeText
const newNode = {
inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式
inserting,
data: {
text: text,
uid: createUid(),
@@ -655,7 +715,7 @@ class Render {
})
})
// 如果同时对多个节点插入子节点,需要清除原来激活的节点
if (handleMultiNodes || !openEdit) {
if (focusNewNode) {
this.clearActiveNodeList()
}
this.mindMap.render()
@@ -671,11 +731,12 @@ class Render {
this.textEdit.hideEditTextBox()
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
const isRichText = !!this.mindMap.richText
const { focusNewNode } = this.getNewNodeBehavior(false, true)
const params = {
expand: true,
richText: isRichText,
resetRichText: isRichText,
isActive: true
isActive: focusNewNode
}
childList = addDataToAppointNodes(childList, params)
list.forEach(node => {
@@ -692,7 +753,9 @@ class Render {
expand: true
})
})
this.clearActiveNodeList()
if (focusNewNode) {
this.clearActiveNodeList()
}
this.mindMap.render()
}
@@ -710,11 +773,15 @@ class Render {
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
const handleMultiNodes = list.length > 1
const isRichText = !!this.mindMap.richText
const { focusNewNode, inserting } = this.getNewNodeBehavior(
openEdit,
handleMultiNodes
)
const params = {
expand: true,
richText: isRichText,
resetRichText: isRichText,
isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
isActive: focusNewNode
}
list.forEach(node => {
if (node.isGeneralization || node.isRoot) {
@@ -725,7 +792,7 @@ class Render {
? defaultInsertSecondLevelNodeText
: defaultInsertBelowSecondLevelNodeText
const newNode = {
inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式
inserting,
data: {
text: text,
uid: createUid(),
@@ -740,7 +807,7 @@ class Render {
parent.nodeData.children.splice(index, 1, newNode)
})
// 如果同时对多个节点插入子节点,需要清除原来激活的节点
if (handleMultiNodes || !openEdit) {
if (focusNewNode) {
this.clearActiveNodeList()
}
this.mindMap.render()
@@ -1602,11 +1669,6 @@ class Render {
return res
}
// 派发节点激活改变事件
emitNodeActiveEvent() {
this.mindMap.emit('node_active', null, [...this.activeNodeList])
}
// 高亮节点或子节点
highlightNode(node, range) {
const { highlightNodeBoxStyle = {} } = this.mindMap.opt

View File

@@ -5,7 +5,7 @@ import {
selectAllInput,
htmlEscape
} from '../../utils'
import { ERROR_TYPES } from '../../constants/constant'
import { ERROR_TYPES, CONSTANTS } from '../../constants/constant'
// 节点文字编辑类
export default class TextEdit {
@@ -30,7 +30,9 @@ export default class TextEdit {
this.onScale = this.onScale.bind(this)
this.onKeydown = this.onKeydown.bind(this)
// 节点双击事件
this.mindMap.on('node_dblclick', this.show)
this.mindMap.on('node_dblclick', (node, e, isInserting) => {
this.show({ node, e, isInserting })
})
// 点击事件
this.mindMap.on('draw_click', () => {
// 隐藏文本编辑框
@@ -54,12 +56,22 @@ export default class TextEdit {
this.mindMap.on('before_node_active', () => {
this.hideEditTextBox()
})
// 鼠标滚动事件
this.mindMap.on('mousewheel', () => {
if (
this.mindMap.opt.mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.MOVE
) {
this.hideEditTextBox()
}
})
// 注册编辑快捷键
this.mindMap.keyCommand.addShortcut('F2', () => {
if (this.renderer.activeNodeList.length <= 0) {
return
}
this.show(this.renderer.activeNodeList[0])
this.show({
node: this.renderer.activeNodeList[0]
})
})
this.mindMap.on('scale', this.onScale)
// // 监听按键事件,判断是否自动进入文本编辑模式
@@ -83,7 +95,12 @@ export default class TextEdit {
const node = activeNodeList[0]
// 当正在输入中文或英文或数字时,如果没有按下组合键,那么自动进入文本编辑模式
if (node && this.checkIsAutoEnterTextEditKey(e)) {
this.show(node, e, false, true)
this.show({
node,
e,
isInserting: false,
isFromKeyDown: true
})
}
}
@@ -112,12 +129,17 @@ export default class TextEdit {
// 显示文本编辑框
// isInserting是否是刚创建的节点
// isFromKeyDown是否是在按键事件进入的编辑
async show(node, e, isInserting = false, isFromKeyDown = false) {
async show({
node,
isInserting = false,
isFromKeyDown = false,
isFromScale = false
}) {
// 使用了自定义节点内容那么不响应编辑事件
if (node.isUseCustomNodeContent()) {
return
}
let { beforeTextEdit } = this.mindMap.opt
const { beforeTextEdit } = this.mindMap.opt
if (typeof beforeTextEdit === 'function') {
let isShow = false
try {
@@ -129,14 +151,21 @@ export default class TextEdit {
if (!isShow) return
}
this.currentNode = node
let { offsetLeft, offsetTop } = checkNodeOuter(this.mindMap, node)
const { offsetLeft, offsetTop } = checkNodeOuter(this.mindMap, node)
this.mindMap.view.translateXY(offsetLeft, offsetTop)
let rect = node._textData.node.node.getBoundingClientRect()
const rect = node._textData.node.node.getBoundingClientRect()
const params = {
node,
rect,
isInserting,
isFromKeyDown,
isFromScale
}
if (this.mindMap.richText) {
this.mindMap.richText.showEditText(node, rect, isInserting, isFromKeyDown)
this.mindMap.richText.showEditText(params)
return
}
this.showEditTextBox(node, rect, isInserting, isFromKeyDown)
this.showEditTextBox(params)
}
// 处理画布缩放
@@ -150,15 +179,20 @@ export default class TextEdit {
this.cacheEditingText = this.getEditText()
this.showTextEdit = false
}
this.show(this.currentNode)
this.show({
node: this.currentNode,
isFromScale: true
})
}
// 显示文本编辑框
showEditTextBox(node, rect, isInserting, isFromKeyDown) {
showEditTextBox({ node, rect, isInserting, isFromKeyDown, isFromScale }) {
if (this.showTextEdit) return
const { nodeTextEditZIndex, textAutoWrapWidth, selectTextOnEnterEditText } =
this.mindMap.opt
this.mindMap.emit('before_show_text_edit')
if (!isFromScale) {
this.mindMap.emit('before_show_text_edit')
}
this.registerTmpShortcut()
if (!this.textEditNode) {
this.textEditNode = document.createElement('div')

View File

@@ -179,6 +179,7 @@ class Node {
let { isUseCustomNodeContent, customCreateNodeContent } = this.mindMap.opt
if (isUseCustomNodeContent && customCreateNodeContent) {
this._customNodeContent = customCreateNodeContent(this)
this._customNodeContent.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
}
// 如果没有返回内容,那么还是使用内置的节点内容
if (this._customNodeContent) return
@@ -441,9 +442,7 @@ class Node {
this.mindMap.renderer[
isActive ? 'removeNodeFromActiveList' : 'addNodeToActiveList'
](this)
this.mindMap.emit('node_active', isActive ? null : this, [
...this.mindMap.renderer.activeNodeList
])
this.renderer.emitNodeActiveEvent(isActive ? null : this)
}
this.mindMap.emit('node_mousedown', this, e)
})
@@ -474,7 +473,7 @@ class Node {
})
// 双击事件
this.group.on('dblclick', e => {
if (this.mindMap.opt.readonly) {
if (this.mindMap.opt.readonly || e.ctrlKey) {
return
}
e.stopPropagation()
@@ -520,7 +519,7 @@ class Node {
this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
this.renderer.clearActiveNodeList()
this.renderer.addNodeToActiveList(this)
this.mindMap.emit('node_active', this, [...this.renderer.activeNodeList])
this.renderer.emitNodeActiveEvent(this)
}
// 更新节点

View File

@@ -72,7 +72,7 @@ class View {
return customHandleMousewheel(e)
}
// 鼠标滚轮事件控制缩放
if (mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM) {
if (mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM || e.ctrlKey) {
if (disableMouseWheelZoom) return
const { x: clientX, y: clientY } = this.mindMap.toPos(
e.clientX,

View File

@@ -1,4 +1,9 @@
import { bfsWalk, throttle, getTopAncestorsFomNodeList, getNodeIndexInNodeList } from '../utils'
import {
bfsWalk,
throttle,
getTopAncestorsFomNodeList,
getNodeIndexInNodeList
} from '../utils'
import Base from '../layouts/Base'
// 节点拖动插件
@@ -108,9 +113,7 @@ class Drag extends Base {
node.endDrag()
})
this.removeCloneNode()
let overlapNodeUid = this.overlapNode
? this.overlapNode.getData('uid')
: ''
let overlapNodeUid = this.overlapNode ? this.overlapNode.getData('uid') : ''
let prevNodeUid = this.prevNode ? this.prevNode.getData('uid') : ''
let nextNodeUid = this.nextNode ? this.nextNode.getData('uid') : ''
// 存在重叠子节点,则移动作为其子节点
@@ -162,12 +165,14 @@ class Drag extends Base {
)
this.mindMap.render()
}
if (this.isDragging) {
this.mindMap.emit('node_dragend', {
overlapNodeUid,
prevNodeUid,
nextNodeUid
})
}
this.reset()
this.mindMap.emit('node_dragend', {
overlapNodeUid,
prevNodeUid,
nextNodeUid
})
}
// 拖动中

View File

@@ -3,7 +3,8 @@ import {
downloadFile,
readBlob,
removeHTMLEntities,
resizeImgSize
resizeImgSize,
handleSelfCloseTags
} from '../utils'
import { SVG } from '@svgdotjs/svg.js'
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas'
@@ -20,7 +21,7 @@ class Export {
// 导出
async export(type, isDownload = true, name = '思维导图', ...args) {
if (this[type]) {
let result = await this[type](name, ...args)
const result = await this[type](name, ...args)
if (isDownload && type !== 'pdf') {
downloadFile(result, name + '.' + type)
}
@@ -30,6 +31,20 @@ class Export {
}
}
// 创建图片url转换任务
createTransformImgTaskList(svg, tagName, propName, getUrlFn) {
const imageList = svg.find(tagName)
return imageList.map(async item => {
const imgUlr = getUrlFn(item)
// 已经是data:URL形式不用转换
if (/^data:/.test(imgUlr) || imgUlr === 'none') {
return
}
const imgData = await imgToDataUrl(imgUlr)
item.attr(propName, imgData)
})
}
// 获取svg数据
async getSvgData() {
let { exportPaddingX, exportPaddingY } = this.mindMap.opt
@@ -37,19 +52,34 @@ class Export {
paddingX: exportPaddingX,
paddingY: exportPaddingY
})
// 把图片的url转换成data:url类型否则导出会丢失图片
let imageList = svg.find('image')
let task = imageList.map(async item => {
let imgUlr = item.attr('href') || item.attr('xlink:href')
// 已经是data:URL形式不用转换
if (/^data:/.test(imgUlr) || imgUlr === 'none') {
return
// svg的image标签把图片的url转换成data:url类型否则导出会丢失图片
const task1 = this.createTransformImgTaskList(
svg,
'image',
'href',
item => {
return item.attr('href') || item.attr('xlink:href')
}
let imgData = await imgToDataUrl(imgUlr)
item.attr('href', imgData)
)
// html的img标签
const task2 = this.createTransformImgTaskList(svg, 'img', 'src', item => {
return item.attr('src')
})
await Promise.all(task)
if (imageList.length > 0) {
const taskList = [...task1, ...task2]
await Promise.all(taskList)
// 开启了节点富文本编辑,需要增加一些样式
let isAddResetCss
if (this.mindMap.richText) {
const foreignObjectList = svg.find('foreignObject')
if (foreignObjectList.length > 0) {
foreignObjectList[0].add(
SVG(`<style>${this.mindMap.opt.resetCss}</style>`)
)
isAddResetCss = true
}
}
// svg节点内容有变需要重新获取html字符串
if (taskList.length > 0 || isAddResetCss) {
svgHTML = svg.svg()
}
return {
@@ -131,7 +161,7 @@ class Export {
// 在canvas上绘制思维导图背景
drawBackgroundToCanvas(ctx, width, height) {
return new Promise((resolve, reject) => {
let {
const {
backgroundColor = '#fff',
backgroundImage,
backgroundRepeat = 'no-repeat',
@@ -175,7 +205,7 @@ class Export {
// 在svg上绘制思维导图背景
drawBackgroundToSvg(svg) {
return new Promise(async resolve => {
let {
const {
backgroundColor = '#fff',
backgroundImage,
backgroundRepeat = 'repeat'
@@ -184,7 +214,7 @@ class Export {
svg.css('background-color', backgroundColor)
// 背景图片
if (backgroundImage && backgroundImage !== 'none') {
let imgDataUrl = await imgToDataUrl(backgroundImage)
const imgDataUrl = await imgToDataUrl(backgroundImage)
svg.css('background-image', `url(${imgDataUrl})`)
svg.css('background-repeat', backgroundRepeat)
resolve()
@@ -200,35 +230,10 @@ class Export {
* 方法2.把svg的图片提取出来再挨个绘制到canvas里最后一起转换
*/
async png(name, transparent = false, checkRotate, compress) {
let { node, str } = await this.getSvgData()
// 如果开启了富文本则使用htmltocanvas转换为图片
if (this.mindMap.richText) {
// 覆盖html默认的样式
let foreignObjectList = node.find('foreignObject')
if (foreignObjectList.length > 0) {
foreignObjectList[0].add(
SVG(`<style>${this.mindMap.opt.resetCss}</style>`)
)
}
str = node.svg()
// 使用其他库html2canvas、dom-to-image-more等来完成导出
// let res = await this.mindMap.richText.handleExportPng(node.node)
// let imgDataUrl = await this.svgToPng(
// res,
// transparent,
// checkRotate
// )
// return imgDataUrl
}
str = removeHTMLEntities(str)
// 转换成blob数据
let blob = new Blob([str], {
type: 'image/svg+xml'
})
// 转换成data:url数据
let svgUrl = await readBlob(blob)
const { str } = await this.getSvgData()
const svgUrl = await this.fixSvgStrAndToBlob(str)
// 绘制到canvas上
let res = await this.svgToPng(svgUrl, transparent, checkRotate, compress)
const res = await this.svgToPng(svgUrl, transparent, checkRotate, compress)
return res
}
@@ -237,7 +242,7 @@ class Export {
if (!this.mindMap.doExportPDF) {
throw new Error('请注册ExportPDF插件')
}
let img = await this.png(
const img = await this.png(
'',
false,
(width, height) => {
@@ -263,51 +268,50 @@ class Export {
}
// 导出为svg
// plusCssText附加的css样式如果svg中存在dom节点想要设置一些针对节点的样式可以通过这个参数传入
async svg(name) {
let { node } = await this.getSvgData()
// 开启了节点富文本编辑
if (this.mindMap.richText) {
let foreignObjectList = node.find('foreignObject')
if (foreignObjectList.length > 0) {
foreignObjectList[0].add(
SVG(`<style>${this.mindMap.opt.resetCss}</style>`)
)
}
}
const { node } = await this.getSvgData()
node.first().before(SVG(`<title>${name}</title>`))
await this.drawBackgroundToSvg(node)
let str = node.svg()
const str = node.svg()
const res = await this.fixSvgStrAndToBlob(str)
return res
}
// 修复svg字符串并且转换为blob数据
async fixSvgStrAndToBlob(str) {
// 移除字符串中的html实体
str = removeHTMLEntities(str)
// 给html自闭合标签添加闭合状态
str = handleSelfCloseTags(str)
// 转换成blob数据
let blob = new Blob([str], {
const blob = new Blob([str], {
type: 'image/svg+xml'
})
let res = await readBlob(blob)
const res = await readBlob(blob)
return res
}
// 导出为json
async json(name, withConfig = true) {
let data = this.mindMap.getData(withConfig)
let str = JSON.stringify(data)
let blob = new Blob([str])
let res = await readBlob(blob)
const data = this.mindMap.getData(withConfig)
const str = JSON.stringify(data)
const blob = new Blob([str])
const res = await readBlob(blob)
return res
}
// 专有文件其实就是json文件
async smm(name, withConfig) {
let res = await this.json(name, withConfig)
const res = await this.json(name, withConfig)
return res
}
// markdown文件
async md() {
let data = this.mindMap.getData()
let content = transformToMarkdown(data)
let blob = new Blob([content])
let res = await readBlob(blob)
const data = this.mindMap.getData()
const content = transformToMarkdown(data)
const blob = new Blob([content])
const res = await readBlob(blob)
return res
}
}

View File

@@ -38,6 +38,7 @@ class Painter {
}
onEndPainter() {
if (!this.isInPainter) return
this.endPainter()
this.mindMap.emit('painter_end')
}

View File

@@ -153,7 +153,7 @@ class RichText {
}
// 显示文本编辑控件
showEditText(node, rect, isInserting, isFromKeyDown) {
showEditText({ node, rect, isInserting, isFromKeyDown, isFromScale }) {
if (this.showTextEdit) {
return
}
@@ -167,7 +167,9 @@ class RichText {
this.node = node
this.isInserting = isInserting
if (!rect) rect = node._textData.node.node.getBoundingClientRect()
this.mindMap.emit('before_show_text_edit')
if (!isFromScale) {
this.mindMap.emit('before_show_text_edit')
}
this.mindMap.renderer.textEdit.registerTmpShortcut()
// 原始宽高
let g = node._textData.node
@@ -571,7 +573,7 @@ class RichText {
setNotActiveNodeStyle(node, style) {
const config = this.normalStyleToRichTextStyle(style)
if (Object.keys(config).length > 0) {
this.showEditText(node)
this.showEditText({ node })
this.formatAllText(config)
this.hideEditText([node])
}

View File

@@ -65,6 +65,9 @@ class Search {
this.currentIndex = -1
this.notResetSearchText = false
this.isSearching = false
if (this.mindMap.opt.readonly) {
this.mindMap.renderer.closeHighlightNode()
}
this.emitEvent()
}
@@ -96,6 +99,10 @@ class Search {
this.mindMap.execCommand('GO_TARGET_NODE', currentNode, () => {
this.notResetSearchText = false
callback()
// 只读模式下节点无法激活,所以通过高亮的方式
if (this.mindMap.opt.readonly) {
this.mindMap.renderer.highlightNode(currentNode)
}
})
}

View File

@@ -133,9 +133,7 @@ class Select {
}
}
if (isNumChange || isNodeChange) {
this.mindMap.emit('node_active', null, [
...this.mindMap.renderer.activeNodeList
])
this.mindMap.renderer.emitNodeActiveEvent()
}
}

View File

@@ -12,6 +12,7 @@ class BatchExecution {
// 添加任务
push(name, fn) {
if (this.has[name]) {
this.replaceTask(name, fn)
return
}
this.has[name] = true
@@ -22,6 +23,19 @@ class BatchExecution {
this.nextTick()
}
// 替换任务
replaceTask(name, fn) {
const index = this.queue.findIndex(item => {
return item.name === name
})
if (index !== -1) {
this.queue[index] = {
name,
fn
}
}
}
// 执行队列
flush() {
let fns = this.queue.slice(0)

View File

@@ -1,5 +1,8 @@
import { v4 as uuidv4 } from 'uuid'
import { nodeDataNoStylePropList } from '../constants/constant'
import {
nodeDataNoStylePropList,
selfCloseTagList
} from '../constants/constant'
import MersenneTwister from './mersenneTwister'
// 深度优先遍历树
export const walk = (
@@ -989,3 +992,29 @@ export const removeFromParentNodeData = node => {
if (index === -1) return
node.parent.nodeData.children.splice(index, 1)
}
// 给html自闭合标签添加闭合状态
export const handleSelfCloseTags = str => {
selfCloseTagList.forEach(tagName => {
str = str.replaceAll(
new RegExp(`<${tagName}([^>]*)>`, 'g'),
`<${tagName} $1 />`
)
})
return str
}
// 检查两个节点列表是否包含的节点是一样的
export const checkNodeListIsEqual = (list1, list2) => {
if (list1.length !== list2.length) return false
for (let i = 0; i < list1.length; i++) {
if (
!list2.find(item => {
return item.uid === list1[i].uid
})
) {
return false
}
}
return true
}

View File

@@ -74,6 +74,7 @@ declare class MindMap {
richTextEditFakeInPlace: boolean;
customHandleClipboardText: any;
disableMouseWheelZoom: boolean;
disableTouchZoom: boolean;
errorHandler: (code: any, error: any) => void;
resetCss: string;
enableDblclickBackToRootNode: boolean;
@@ -103,6 +104,12 @@ declare class MindMap {
defaultGeneralizationText: string;
handleIsSplitByWrapOnPasteCreateNewNode: any;
addHistoryTime: number;
isDisableDrag: boolean;
highlightNodeBoxStyle: {
stroke: string;
fill: string;
};
createNewNodeBehavior: string;
});
opt: any;
el: any;

View File

@@ -80,6 +80,11 @@ export namespace CONSTANTS {
const VERTICAL: string;
const HORIZONTAL: string;
}
namespace CREATE_NEW_NODE_BEHAVIOR {
const DEFAULT: string;
const NOT_ACTIVE: string;
const ACTIVE_ONLY: string;
}
}
export const initRootNodePositionMap: {
[x: string]: number;
@@ -107,3 +112,4 @@ export namespace a4Size {
const height: number;
}
export const cssContent: "\n /* 鼠标hover和激活时渲染的矩形 */\n .smm-hover-node{\n display: none;\n opacity: 0.6;\n stroke-width: 1;\n }\n\n .smm-node:not(.smm-node-dragging):hover .smm-hover-node{\n display: block;\n }\n\n .smm-node.active .smm-hover-node{\n display: block;\n opacity: 1;\n stroke-width: 2;\n }\n";
export const selfCloseTagList: string[];

View File

@@ -70,6 +70,7 @@ export namespace defaultOpt {
const richTextEditFakeInPlace: boolean;
const customHandleClipboardText: any;
const disableMouseWheelZoom: boolean;
const disableTouchZoom: boolean;
function errorHandler(code: any, error: any): void;
const resetCss: string;
const enableDblclickBackToRootNode: boolean;
@@ -101,4 +102,11 @@ export namespace defaultOpt {
const defaultGeneralizationText: string;
const handleIsSplitByWrapOnPasteCreateNewNode: any;
const addHistoryTime: number;
const isDisableDrag: boolean;
namespace highlightNodeBoxStyle {
export const stroke: string;
const fill_2: string;
export { fill_2 as fill };
}
const createNewNodeBehavior: string;
}

View File

@@ -11,6 +11,8 @@ export default class KeyCommand {
save(): void;
restore(): void;
bindEvent(): void;
onKeydown(e: any): void;
unBindEvent(): void;
checkKey(e: any, key: any): boolean;
getOriginEventCodeArr(e: any): any[];
hasCombinationKey(e: any): any;

View File

@@ -20,6 +20,9 @@ declare class Render {
beingPasteText: string;
beingPasteImgSize: number;
currentBeingPasteType: string;
highlightBoxNode: any;
lastActiveNode: any;
lastActiveNodeList: any[];
setLayout(): void;
layout: MindMap | CatalogOrganization | OrganizationStructure | Timeline | VerticalTimeline;
setData(data: any): void;
@@ -66,6 +69,7 @@ declare class Render {
goTargetNode(node: any, callback?: () => void): void;
registerShortcutKeys(): void;
toggleActiveExpand(): void;
emitNodeActiveEvent(node?: any, activeNodeList?: any[]): void;
clearActiveNodeListOnDrawClick(e: any, eventType: any): void;
startTextEdit(): void;
endTextEdit(): void;
@@ -75,12 +79,17 @@ declare class Render {
removeNodeFromActiveList(node: any): void;
findActiveNodeIndex(node: any): any;
backForward(type: any, step: any): void;
getNewNodeBehavior(openEdit?: boolean, handleMultiNodes?: boolean): {
focusNewNode: boolean;
inserting: boolean;
};
copy(): void;
cut(): void;
paste(): void;
onPaste(): Promise<void>;
insertTo(node: any, exist: any, dir?: string): void;
checkNodeLayerChange(node: any, toNode: any): void;
deleteNodeGeneralization(node: any): void;
getNextActiveNode(): any;
copyNode(): any;
toggleNodeExpand(node: any): void;
@@ -89,7 +98,8 @@ declare class Render {
setRootNodeCenter(): void;
expandToNodeUid(uid: any, callback?: () => void): void;
findNodeByUid(uid: any): any;
emitNodeActiveEvent(): void;
highlightNode(node: any, range: any): void;
closeHighlightNode(): void;
}
import TextEdit from "./TextEdit";
import MindMap from "../../layouts/MindMap";

View File

@@ -7,11 +7,24 @@ export default class TextEdit {
showTextEdit: boolean;
cacheEditingText: string;
bindEvent(): void;
show(node: any, e: any, isInserting?: boolean, isFromKeyDown?: boolean): Promise<void>;
show({ node, isInserting, isFromKeyDown, isFromScale }: {
node: any;
isInserting?: boolean;
isFromKeyDown?: boolean;
isFromScale?: boolean;
}): Promise<void>;
onScale(): void;
onKeydown(e: any): void;
unBindEvent(): void;
checkIsAutoEnterTextEditKey(e: any): boolean;
registerTmpShortcut(): void;
showEditTextBox(node: any, rect: any, isInserting: any, isFromKeyDown: any): void;
showEditTextBox({ node, rect, isInserting, isFromKeyDown, isFromScale }: {
node: any;
rect: any;
isInserting: any;
isFromKeyDown: any;
isFromScale: any;
}): void;
getEditText(): any;
hideEditTextBox(): any;
}

View File

@@ -39,6 +39,7 @@ declare class Node {
_tagData: any;
_noteData: any;
noteEl: any;
noteContentIsShow: boolean;
_expandBtn: any;
_lastExpandBtnType: any;
_showExpandBtn: boolean;
@@ -47,8 +48,7 @@ declare class Node {
_fillExpandNode: any;
_userListGroup: any;
_lines: any[];
_generalizationLine: any;
_generalizationNode: any;
_generalizationList: any[];
_unVisibleRectRegionNode: any;
_isMouseenter: boolean;
_rectInfo: {
@@ -102,11 +102,14 @@ declare class Node {
getShape(): any;
hasCustomPosition(): boolean;
ancestorHasCustomPosition(): boolean;
ancestorHasGeneralization(): boolean;
addChildren(node: any): void;
styleLine(line: any, node: any): void;
removeLine(): void;
isAncestor(node: any): boolean;
isParent(node: any): boolean;
isBrother(node: any): any;
getIndexInBrothers(): any;
getPaddingVale(): {
paddingX: any;
paddingY: any;
@@ -117,7 +120,17 @@ declare class Node {
getSelfInhertStyle(prop: any): any;
getBorderWidth(): any;
getData(key: any): any;
getPureData(removeActiveState?: boolean, removeId?: boolean): any;
hasCustomStyle(): boolean;
getRect(): any;
getRectInSvg(): {
left: any;
right: any;
top: any;
bottom: any;
width: number;
height: number;
};
}
import Style from "./Style";
import Shape from "./Shape";

View File

@@ -7,6 +7,7 @@ declare namespace _default {
export { createHyperlinkNode };
export { createTagNode };
export { createNoteNode };
export { getNoteContentPosition };
export { measureCustomNodeContentSize };
export { isUseCustomNodeContent };
}
@@ -38,6 +39,10 @@ declare function createNoteNode(): {
declare class createNoteNode {
noteEl: HTMLDivElement;
}
declare function getNoteContentPosition(): {
left: any;
top: any;
};
declare function measureCustomNodeContentSize(content: any): {
width: any;
height: any;

View File

@@ -1,32 +1,38 @@
declare namespace _default {
export { formatGetGeneralization };
export { checkHasGeneralization };
export { checkHasSelfGeneralization };
export { getGeneralizationNodeIndex };
export { createGeneralizationNode };
export { updateGeneralization };
export { updateGeneralizationData };
export { renderGeneralization };
export { removeGeneralization };
export { hideGeneralization };
export { showGeneralization };
export { setGeneralizationOpacity };
export { handleGeneralizationMouseenter };
export { handleGeneralizationMouseleave };
}
export default _default;
declare function formatGetGeneralization(): any[];
declare function checkHasGeneralization(): boolean;
declare function checkHasSelfGeneralization(): boolean;
declare function getGeneralizationNodeIndex(node: any): any;
declare function createGeneralizationNode(): void;
declare class createGeneralizationNode {
_generalizationLine: any;
_generalizationNode: Node;
_generalizationNodeWidth: any;
_generalizationNodeHeight: any;
}
declare function updateGeneralization(): void;
declare function renderGeneralization(): void;
declare class renderGeneralization {
_generalizationNodeWidth: number;
_generalizationNodeHeight: number;
}
declare function updateGeneralization(): void;
declare function updateGeneralizationData(): void;
declare function renderGeneralization(): void;
declare function removeGeneralization(): void;
declare class removeGeneralization {
_generalizationLine: any;
_generalizationNode: any;
_generalizationList: any[];
}
declare function hideGeneralization(): void;
declare function showGeneralization(): void;
import Node from "./Node";
declare function setGeneralizationOpacity(val: any): void;
declare function handleGeneralizationMouseenter(): void;
declare function handleGeneralizationMouseleave(): void;

View File

@@ -39,6 +39,22 @@ declare class Base {
generalizationLineMargin: any;
generalizationNodeMargin: any;
};
getChildrenBoundaries(node: any, dir: any, startIndex: number, endIndex: any): {
left: number;
right: number;
top: number;
bottom: number;
generalizationLineMargin: any;
generalizationNodeMargin: any;
};
getNodeGeneralizationRenderBoundaries(item: any, dir: any): {
left: any;
right: any;
top: any;
bottom: any;
generalizationLineMargin: any;
generalizationNodeMargin: any;
};
getNodeActChildrenLength(node: any): any;
}
import Lru from "../utils/Lru";

View File

@@ -4,5 +4,6 @@ declare class BatchExecution {
queue: any[];
nextTick: any;
push(name: any, fn: any): void;
replaceTask(name: any, fn: any): void;
flush(): void;
}

View File

@@ -58,6 +58,8 @@ export function getObjectChangedProps(oldObject: any, newObject: any): {};
export function checkIsNodeStyleDataKey(key: any): boolean;
export function mergerIconList(list: any): any;
export function getTopAncestorsFomNodeList(list: any): any[];
export function checkHasSupSubRelation(list: any): boolean;
export function parseAddGeneralizationNodeList(list: any): any[];
export function checkTwoRectIsOverlap(minx1: any, maxx1: any, miny1: any, maxy1: any, minx2: any, maxx2: any, miny2: any, maxy2: any): boolean;
export function focusInput(el: any): void;
export function selectAllInput(el: any): void;
@@ -75,3 +77,5 @@ export function getDataFromClipboard(): Promise<{
img: any;
}>;
export function removeFromParentNodeData(node: any): void;
export function handleSelfCloseTags(str: any): any;
export function checkNodeListIsEqual(list1: any, list2: any): boolean;

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -314,6 +314,11 @@ export const shortcutKeyList = [
name: 'Zoom out',
value: 'Ctrl + -'
},
{
icon: 'iconfangda',
name: 'Zoom in/Zoom out',
value: 'Ctrl + Mouse wheel'
},
{
icon: 'icondingwei',
name: 'Back root node',

View File

@@ -381,6 +381,11 @@ export const shortcutKeyList = [
name: '缩小',
value: 'Ctrl + -'
},
{
icon: 'iconfangda',
name: '放大/缩小',
value: 'Ctrl + 鼠标滚动'
},
{
icon: 'icondingwei',
name: '回到根节点',

View File

@@ -47,6 +47,10 @@ export default {
mousewheelZoomActionReverse: 'Mouse Wheel Zoom',
mousewheelZoomActionReverse1: 'Zoom out forward and zoom in back',
mousewheelZoomActionReverse2: 'Zoom in forward and zoom out back',
createNewNodeBehavior: 'Behavior of creating new node',
default: 'Active new node and editing',
notActive: 'Not active new node',
activeOnly: 'Only active new node but not editing',
rootStyle: 'Root Node',
associativeLineText: 'Associative line text',
fontFamily: 'Font family',

View File

@@ -47,6 +47,10 @@ export default {
mousewheelZoomActionReverse: '鼠标滚轮缩放',
mousewheelZoomActionReverse1: '向前缩小向后放大',
mousewheelZoomActionReverse2: '向前放大向后缩小',
createNewNodeBehavior: '创建新节点的行为',
default: '激活新节点及进入编辑',
notActive: '不激活新节点',
activeOnly: '只激活新节点,不进入编辑',
rootStyle: '根节点',
associativeLineText: '关联线文字',
fontFamily: '字体',

View File

@@ -1,5 +1,37 @@
# Changelog
## 0.9.1
Fix:
> 1.Fix the issue of exporting images, SVGs, and PDFs with errors when customizing node content.
>
> 2.Optimize the distribution of node activation events, do not distribute events when the activation node has not changed, and skip intermediate events when distributing multiple events in a short period of time.
>
> 3.Fix the issue where the edit box and node detach when scrolling the canvas with the mouse while the node is in editing mode.
>
> 4.Fix the issue of shortcut keys becoming invalid when zooming the canvas with the mouse wheel and then exiting node editing while in node editing mode.
>
> 5.Fix the issue where clicking on a node can also trigger node_dragend event.
>
> 6.Fix that clicking on the canvas and nodes while not in the format brush will also trigger the painter_end event.
>
> 7.Fixed the issue where the mind map text editing box was not destroyed during node text editing and associated line text editing.
New:
> 1.When holding down the Ctrl key, disable the node double-click event.
>
> 2.Support configuring the behavior when creating new nodes: focusing and entering editing, not focusing, only focusing.
>
> 3.When searching in read-only mode, add a highlight effect to the currently matched node.
>
> 4.The default behavior of the mouse scroll wheel is to move the canvas up and down; The default is to scroll forward to enlarge the canvas and zoom back.
>
> 5.When the mouse scroll wheel behavior is to move the canvas up and down, it supports holding down the Ctrl key to zoom in and out of the canvas.
Demo支持配置创建新节点时的行为。
## 0.9.0
New:

View File

@@ -1,6 +1,26 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.9.1</h2>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue of exporting images, SVGs, and PDFs with errors when customizing node content.</p>
<p>2.Optimize the distribution of node activation events, do not distribute events when the activation node has not changed, and skip intermediate events when distributing multiple events in a short period of time.</p>
<p>3.Fix the issue where the edit box and node detach when scrolling the canvas with the mouse while the node is in editing mode.</p>
<p>4.Fix the issue of shortcut keys becoming invalid when zooming the canvas with the mouse wheel and then exiting node editing while in node editing mode.</p>
<p>5.Fix the issue where clicking on a node can also trigger node_dragend event.</p>
<p>6.Fix that clicking on the canvas and nodes while not in the format brush will also trigger the painter_end event.</p>
<p>7.Fixed the issue where the mind map text editing box was not destroyed during node text editing and associated line text editing.</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.When holding down the Ctrl key, disable the node double-click event.</p>
<p>2.Support configuring the behavior when creating new nodes: focusing and entering editing, not focusing, only focusing.</p>
<p>3.When searching in read-only mode, add a highlight effect to the currently matched node.</p>
<p>4.The default behavior of the mouse scroll wheel is to move the canvas up and down; The default is to scroll forward to enlarge the canvas and zoom back.</p>
<p>5.When the mouse scroll wheel behavior is to move the canvas up and down, it supports holding down the Ctrl key to zoom in and out of the canvas.</p>
</blockquote>
<p>Demo支持配置创建新节点时的行为</p>
<h2>0.9.0</h2>
<p>New:</p>
<p>1.Support adding summaries to some child nodes of the same node.</p>

View File

@@ -43,9 +43,9 @@ const mindMap = new MindMap({
| watermarkConfigv0.2.4+ | Object | | Watermark config, Please refer to the table 【Watermark config】 below for detailed configuration | |
| textAutoWrapWidthv0.3.4+ | Number | 500 | Each line of text in the node will wrap automatically when it reaches the width | |
| customHandleMousewheelv0.4.3+ | Function | null | User-defined mouse wheel event processing can pass a function, and the callback parameter is the event object | |
| mousewheelActionv0.4.3+ | String | zoom | The behavior of the mouse wheel, `zoom`(Zoom in and out)、`move`(Move up and down). If `customHandleMousewheel` passes a custom function, this property will not take effect | |
| mousewheelActionv0.4.3+ | String | zoomv0.9.1+ default is move | The behavior of the mouse wheel, `zoom`(Zoom in and out)、`move`(Move up and down). If `customHandleMousewheel` passes a custom function, this property will not take effect | |
| mousewheelMoveStepv0.4.3+ | Number | 100 | When the `mousewheelAction` is set to `move`, you can use this attribute to control the step length of the view movement when the mouse scrolls. The unit is `px` | |
| mousewheelZoomActionReversev0.6.5+ | Boolean | false | When `mousewheelAction` is set to `zoom`, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed | |
| mousewheelZoomActionReversev0.6.5+ | Boolean | falsev0.9.1+ default is true | When `mousewheelAction` is set to `zoom`, Or when holding down the Ctrl key, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed | |
| defaultInsertSecondLevelNodeTextv0.4.7+ | String | 二级节点 | Text of the default inserted secondary node | |
| defaultInsertBelowSecondLevelNodeTextv0.4.7+ | String | 分支主题 | Text for nodes below the second level inserted by default | |
| expandBtnStylev0.5.0+ | Object | { color: '#808080', fill: '#fff', fontSize: 13, strokeColor: '#333333' } | Expand the color of the stow button, (The fontSize and strokeColor fields were added in version 0.7.0+to set the text style for displaying the number of nodes when folded) | |
@@ -103,6 +103,7 @@ const mindMap = new MindMap({
| isDisableDragv0.8.1+ | Boolean | false | Is disable dragging the canvas | |
| disableTouchZoomv0.8.1+ | Boolean | false | Prohibit double finger scaling, you can still use the API for scaling, which takes effect on the TouchEvent plugin | |
| highlightNodeBoxStylev0.9.0+ | Object | { stroke: 'rgb(94, 200, 248)', fill: 'transparent' } | Highlight box style when the mouse moves into the summary to highlight the node it belongs to | |
| createNewNodeBehaviorv0.9.1+ | String | default | Behavior when creating a new node. defaultBy default, newly created nodes will be activated and enter editing mode. If multiple new nodes are created simultaneously, they will only be activated and will not enter editing mode、notActiveDo not activate newly created nodes、activeOnlyOnly activate newly created nodes and do not enter editing mode | |
### Data structure

View File

@@ -164,7 +164,7 @@
<tr>
<td>mousewheelActionv0.4.3+</td>
<td>String</td>
<td>zoom</td>
<td>zoomv0.9.1+ default is move</td>
<td>The behavior of the mouse wheel, <code>zoom</code>(Zoom in and out)<code>move</code>(Move up and down). If <code>customHandleMousewheel</code> passes a custom function, this property will not take effect</td>
<td></td>
</tr>
@@ -178,8 +178,8 @@
<tr>
<td>mousewheelZoomActionReversev0.6.5+</td>
<td>Boolean</td>
<td>false</td>
<td>When <code>mousewheelAction</code> is set to <code>zoom</code>, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed</td>
<td>falsev0.9.1+ default is true</td>
<td>When <code>mousewheelAction</code> is set to <code>zoom</code>, Or when holding down the Ctrl key, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed</td>
<td></td>
</tr>
<tr>
@@ -581,6 +581,13 @@
<td>Highlight box style when the mouse moves into the summary to highlight the node it belongs to</td>
<td></td>
</tr>
<tr>
<td>createNewNodeBehaviorv0.9.1+</td>
<td>String</td>
<td>default</td>
<td>Behavior when creating a new node. defaultBy default, newly created nodes will be activated and enter editing mode. If multiple new nodes are created simultaneously, they will only be activated and will not enter editing mode、notActiveDo not activate newly created nodes、activeOnlyOnly activate newly created nodes and do not enter editing mode</td>
<td></td>
</tr>
</tbody>
</table>
<h3>Data structure</h3>

View File

@@ -253,4 +253,8 @@ Open source is not easy. If this project is helpful to you, you can invite the a
<img src="../../../../assets/avatar/moom.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>moom</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/张扬.png" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>张扬</p>
</div>
</div>

View File

@@ -8,17 +8,17 @@
</blockquote>
<h2>Features</h2>
<ul>
<li><input type="checkbox" id="checkbox32" checked="true" /><label for="checkbox32">Pluggable architecture, in addition to core functions, other functions are provided as plugins, which can be used as needed to reduce packaging volume</label></li>
<li><input type="checkbox" id="checkbox33" checked="true" /><label for="checkbox33">Support logical structure chart, mind map, Organizational chart, directory organization chart, timeline (horizontal and vertical), fishbone chart and other structures</label></li>
<li><input type="checkbox" id="checkbox34" checked="true" /><label for="checkbox34">Built-in multiple themes, allowing for highly customizable styles, and supporting registration of new themes</label></li>
<li><input type="checkbox" id="checkbox35" checked="true" /><label for="checkbox35">Node content supports text (regular text, rich text), images, icons, hyperlinks, notes, labels, summaries, and math formulas</label></li>
<li><input type="checkbox" id="checkbox36" checked="true" /><label for="checkbox36">Nodes support drag and drop (drag and move, freely adjust), multiple node shapes, and fully customize node content using DDM</label></li>
<li><input type="checkbox" id="checkbox37" checked="true" /><label for="checkbox37">Support canvas dragging and scaling</label></li>
<li><input type="checkbox" id="checkbox38" checked="true" /><label for="checkbox38">Supports two multi node selection methods: mouse button drag selection and Ctrl+left button selection</label></li>
<li><input type="checkbox" id="checkbox39" checked="true" /><label for="checkbox39">Supoorts to export as </label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code><code>xmind</code>, support import from <code>json</code><code>xmind</code><code>markdown</code></li>
<li><input type="checkbox" id="checkbox40" checked="true" /><label for="checkbox40">Support shortcut keys, forward and backward, correlation lines, search and replacement, small maps, watermarks, and scrollbar</label></li>
<li><input type="checkbox" id="checkbox41" checked="true" /><label for="checkbox41">Provide rich configurations to meet various scenarios and usage habits</label></li>
<li><input type="checkbox" id="checkbox42" checked="true" /><label for="checkbox42">Support collaborative editing</label></li>
<li><input type="checkbox" id="checkbox16" checked="true" /><label for="checkbox16">Pluggable architecture, in addition to core functions, other functions are provided as plugins, which can be used as needed to reduce packaging volume</label></li>
<li><input type="checkbox" id="checkbox17" checked="true" /><label for="checkbox17">Support logical structure chart, mind map, Organizational chart, directory organization chart, timeline (horizontal and vertical), fishbone chart and other structures</label></li>
<li><input type="checkbox" id="checkbox18" checked="true" /><label for="checkbox18">Built-in multiple themes, allowing for highly customizable styles, and supporting registration of new themes</label></li>
<li><input type="checkbox" id="checkbox19" checked="true" /><label for="checkbox19">Node content supports text (regular text, rich text), images, icons, hyperlinks, notes, labels, summaries, and math formulas</label></li>
<li><input type="checkbox" id="checkbox20" checked="true" /><label for="checkbox20">Nodes support drag and drop (drag and move, freely adjust), multiple node shapes, and fully customize node content using DDM</label></li>
<li><input type="checkbox" id="checkbox21" checked="true" /><label for="checkbox21">Support canvas dragging and scaling</label></li>
<li><input type="checkbox" id="checkbox22" checked="true" /><label for="checkbox22">Supports two multi node selection methods: mouse button drag selection and Ctrl+left button selection</label></li>
<li><input type="checkbox" id="checkbox23" checked="true" /><label for="checkbox23">Supoorts to export as </label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code><code>xmind</code>, support import from <code>json</code><code>xmind</code><code>markdown</code></li>
<li><input type="checkbox" id="checkbox24" checked="true" /><label for="checkbox24">Support shortcut keys, forward and backward, correlation lines, search and replacement, small maps, watermarks, and scrollbar</label></li>
<li><input type="checkbox" id="checkbox25" checked="true" /><label for="checkbox25">Provide rich configurations to meet various scenarios and usage habits</label></li>
<li><input type="checkbox" id="checkbox26" checked="true" /><label for="checkbox26">Support collaborative editing</label></li>
</ul>
<h2>Repository Catalog Introduction</h2>
<p>1.<code>simple-mind-map</code></p>
@@ -28,16 +28,16 @@ frameworks such as Vue and React, or without a framework.</p>
<p>This is an online mind map built using the <code>simple-mind-map</code> library and based
on <code>Vue2.x</code> and <code>ElementUI</code>. Features include:</p>
<ul>
<li><input type="checkbox" id="checkbox43" checked="true" /><label for="checkbox43">Toolbar, which supports inserting and deleting nodes, and editing node</label>
<li><input type="checkbox" id="checkbox27" checked="true" /><label for="checkbox27">Toolbar, which supports inserting and deleting nodes, and editing node</label>
images, icons, hyperlinks, notes, tags, and summaries</li>
<li><input type="checkbox" id="checkbox44" checked="true" /><label for="checkbox44">Sidebar, with panels for basic style settings, node style settings,</label>
<li><input type="checkbox" id="checkbox28" checked="true" /><label for="checkbox28">Sidebar, with panels for basic style settings, node style settings,</label>
outline, theme selection, and structure selection</li>
<li><input type="checkbox" id="checkbox45" checked="true" /><label for="checkbox45">Import and export functionality; data is saved in the browser's local</label>
<li><input type="checkbox" id="checkbox29" checked="true" /><label for="checkbox29">Import and export functionality; data is saved in the browser's local</label>
storage by default, but it also supports creating, opening, and editing
local files on the computer directly</li>
<li><input type="checkbox" id="checkbox46" checked="true" /><label for="checkbox46">Right-click menu, which supports operations such as expanding, collapsing,</label>
<li><input type="checkbox" id="checkbox30" checked="true" /><label for="checkbox30">Right-click menu, which supports operations such as expanding, collapsing,</label>
and organizing layout</li>
<li><input type="checkbox" id="checkbox47" checked="true" /><label for="checkbox47">Bottom bar, which supports node and word count statistics, switching</label>
<li><input type="checkbox" id="checkbox31" checked="true" /><label for="checkbox31">Bottom bar, which supports node and word count statistics, switching</label>
between edit and read-only modes, zooming in and out, and switching to
full screen, support mini map</li>
</ul>
@@ -209,6 +209,10 @@ full screen, support mini map</li>
<img src="../../../../assets/avatar/moom.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>moom</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/张扬.png" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>张扬</p>
</div>
</div>
</div>
</template>

View File

@@ -408,6 +408,22 @@ Remove the data of a node from its parent node's `nodeData.children` list.
Determine whether there is a hierarchical relationship from the given node instance list.
#### handleSelfCloseTags(str)
> v0.9.1+
- `str`: html string
Add a closed state to HTML self closing tags, `<div><img src="xxx"></div>` -> `<div><img src="xxx" /></div>`
#### checkNodeListIsEqual(list1, list2)
> v0.9.1+
- `list1/list2`: Node instance list
Check if the two node instance lists contain the same nodes.
## Simulate CSS background in Canvas
Import:

View File

@@ -333,6 +333,22 @@ and copying the <code>data</code> of the data object, example:</p>
<p>v0.8.1+</p>
</blockquote>
<p>Determine whether there is a hierarchical relationship from the given node instance list.</p>
<h4>handleSelfCloseTags(str)</h4>
<blockquote>
<p>v0.9.1+</p>
</blockquote>
<ul>
<li><code>str</code>: html string</li>
</ul>
<p>Add a closed state to HTML self closing tags, <code>&lt;div&gt;&lt;img src=&quot;xxx&quot;&gt;&lt;/div&gt;</code> -&gt; <code>&lt;div&gt;&lt;img src=&quot;xxx&quot; /&gt;&lt;/div&gt;</code>。</p>
<h4>checkNodeListIsEqual(list1, list2)</h4>
<blockquote>
<p>v0.9.1+</p>
</blockquote>
<ul>
<li><code>list1/list2</code>: Node instance list</li>
</ul>
<p>Check if the two node instance lists contain the same nodes.</p>
<h2>Simulate CSS background in Canvas</h2>
<p>Import:</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> drawBackgroundImageToCanvas <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/utils/simulateCSSBackgroundInCanvas&#x27;</span>

View File

@@ -1,5 +1,37 @@
# Changelog
## 0.9.1
修复:
> 1.修复自定义节点内容时导出图片、svg、pdf报错的问题。
>
> 2.优化节点激活事件的派发,激活节点未改变时不派发事件,短时间派发多次事件时跳过中间事件。
>
> 3.修复节点处于编辑状态时,通过鼠标滚动移动画布后编辑框和节点脱离的问题。
>
> 4.修复在节点编辑状态中通过鼠标滚轮缩放画布再退出节点编辑后快捷键失效的问题。
>
> 5.修复点击节点也会触发node_dragend事件的问题。
>
> 6.修复不在格式刷时点击画布和节点也会触发painter_end事件的问题。
>
> 7.修复在节点文本编辑中和关联线文本编辑中时销毁思维导图文本编辑框未被销毁的问题。
新增:
> 1.按住Ctrl键时禁用节点双击事件。
>
> 2.支持配置创建新节点时的行为:聚焦且进入编辑、不聚焦、只聚焦。
>
> 3.只读模式下搜索时给当前匹配到的节点增加高亮效果。
>
> 4.鼠标滚轮行为默认改为上下移动画布;默认改为向前滚动放大画布,向后缩小。
>
> 5.在鼠标滚轮行为为上下移动画布时支持按住Ctrl键进行放大缩小画布。
Demo支持配置创建新节点时的行为。
## 0.9.0
新增:

View File

@@ -1,6 +1,26 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.9.1</h2>
<p>修复</p>
<blockquote>
<p>1.修复自定义节点内容时导出图片svgpdf报错的问题</p>
<p>2.优化节点激活事件的派发激活节点未改变时不派发事件短时间派发多次事件时跳过中间事件</p>
<p>3.修复节点处于编辑状态时通过鼠标滚动移动画布后编辑框和节点脱离的问题</p>
<p>4.修复在节点编辑状态中通过鼠标滚轮缩放画布再退出节点编辑后快捷键失效的问题</p>
<p>5.修复点击节点也会触发node_dragend事件的问题</p>
<p>6.修复不在格式刷时点击画布和节点也会触发painter_end事件的问题</p>
<p>7.修复在节点文本编辑中和关联线文本编辑中时销毁思维导图文本编辑框未被销毁的问题</p>
</blockquote>
<p>新增</p>
<blockquote>
<p>1.按住Ctrl键时禁用节点双击事件</p>
<p>2.支持配置创建新节点时的行为聚焦且进入编辑不聚焦只聚焦</p>
<p>3.只读模式下搜索时给当前匹配到的节点增加高亮效果</p>
<p>4.鼠标滚轮行为默认改为上下移动画布默认改为向前滚动放大画布向后缩小</p>
<p>5.在鼠标滚轮行为为上下移动画布时支持按住Ctrl键进行放大缩小画布</p>
</blockquote>
<p>Demo支持配置创建新节点时的行为</p>
<h2>0.9.0</h2>
<p>新增</p>
<p>1.支持对同一个节点的部分子节点添加概要</p>

View File

@@ -43,9 +43,9 @@ const mindMap = new MindMap({
| watermarkConfigv0.2.4+ | Object | | 水印配置,详细配置请参考下方表格【水印配置】 |
| textAutoWrapWidthv0.3.4+ | Number | 500 | 节点内每行文本达到该宽度后自动换行 |
| customHandleMousewheelv0.4.3+ | Function | null | 自定义鼠标滚轮事件处理,可以传一个函数,回调参数为事件对象 |
| mousewheelActionv0.4.3+ | String | zoom | 鼠标滚轮的行为,`zoom`(放大缩小)、`move`(上下移动)。如果`customHandleMousewheel`传了自定义函数,这个属性不生效 |
| mousewheelActionv0.4.3+ | String | zoomv0.9.1+默认改为move | 鼠标滚轮的行为,`zoom`(放大缩小)、`move`(上下移动)。如果`customHandleMousewheel`传了自定义函数,这个属性不生效 |
| mousewheelMoveStepv0.4.3+ | Number | 100 | 当`mousewheelAction`设为`move`时,可以通过该属性控制鼠标滚动一下视图移动的步长,单位`px` |
| mousewheelZoomActionReversev0.6.5+ | Boolean | false | 当mousewheelAction设为zoom时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来 |
| mousewheelZoomActionReversev0.6.5+ | Boolean | falsev0.9.1+默认改为true | 当mousewheelAction设为zoom时或者按住Ctrl键时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来 |
| defaultInsertSecondLevelNodeTextv0.4.7+ | String | 二级节点 | 默认插入的二级节点的文字 |
| defaultInsertBelowSecondLevelNodeTextv0.4.7+ | String | 分支主题 | 默认插入的二级以下节点的文字 |
| expandBtnStylev0.5.0+ | Object | { color: '#808080', fill: '#fff', fontSize: 13, strokeColor: '#333333' } | 展开收起按钮的颜色fontSize及strokeColor字段为0.7.0+版本新增的,用于设置收起时显示节点数量的文字样式) |
@@ -103,6 +103,7 @@ const mindMap = new MindMap({
| isDisableDragv0.8.1+ | Boolean | false | 是否禁止拖动画布 |
| disableTouchZoomv0.8.1+ | Boolean | false | 禁止双指缩放你仍旧可以使用api进行缩放对TouchEvent插件生效 |
| highlightNodeBoxStylev0.9.0+ | Object | { stroke: 'rgb(94, 200, 248)', fill: 'transparent' } | 鼠标移入概要高亮所属节点时的高亮框样式 |
| createNewNodeBehaviorv0.9.1+ | String | default | 创建新节点时的行为。default默认会激活新创建的节点并且进入编辑模式。如果同时创建了多个新节点那么只会激活而不会进入编辑模式、notActive不激活新创建的节点、activeOnly只激活新创建的节点不进入编辑模式 |
### 数据结构

View File

@@ -144,7 +144,7 @@
<tr>
<td>mousewheelActionv0.4.3+</td>
<td>String</td>
<td>zoom</td>
<td>zoomv0.9.1+默认改为move</td>
<td>鼠标滚轮的行为<code>zoom</code>放大缩小<code>move</code>上下移动如果<code>customHandleMousewheel</code>传了自定义函数这个属性不生效</td>
</tr>
<tr>
@@ -156,8 +156,8 @@
<tr>
<td>mousewheelZoomActionReversev0.6.5+</td>
<td>Boolean</td>
<td>false</td>
<td>当mousewheelAction设为zoom时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来</td>
<td>falsev0.9.1+默认改为true</td>
<td>当mousewheelAction设为zoom时或者按住Ctrl键时默认向前滚动是缩小向后滚动是放大如果该属性设为true那么会反过来</td>
</tr>
<tr>
<td>defaultInsertSecondLevelNodeTextv0.4.7+</td>
@@ -501,6 +501,12 @@
<td>{ stroke: 'rgb(94, 200, 248)', fill: 'transparent' }</td>
<td>鼠标移入概要高亮所属节点时的高亮框样式</td>
</tr>
<tr>
<td>createNewNodeBehaviorv0.9.1+</td>
<td>String</td>
<td>default</td>
<td>创建新节点时的行为default默认会激活新创建的节点并且进入编辑模式如果同时创建了多个新节点那么只会激活而不会进入编辑模式notActive不激活新创建的节点activeOnly只激活新创建的节点不进入编辑模式</td>
</tr>
</tbody>
</table>
<h3>数据结构</h3>

View File

@@ -82,7 +82,7 @@ mindMap.scrollbar.setScrollBarWrapSize(width, height)
然后你需要监听`scrollbar_change`方法来获取滚动条大小和位置数据:
```js
mindMap.('scrollbar_change', this.updateScrollbar)
mindMap.on('scrollbar_change', this.updateScrollbar)
// 根据事件返回的滚动条数据更新滚动条元素:
{

View File

@@ -70,7 +70,7 @@ mindMap.scrollbar.setScrollBarWrapSize(width, height)
</code></pre>
<p>如果容器大小发生了改变需要再次调用该方法传递改变后的大小</p>
<p>然后你需要监听<code>scrollbar_change</code>方法来获取滚动条大小和位置数据</p>
<pre class="hljs"><code>mindMap.(<span class="hljs-string">&#x27;scrollbar_change&#x27;</span>, <span class="hljs-built_in">this</span>.updateScrollbar)
<pre class="hljs"><code>mindMap.on(<span class="hljs-string">&#x27;scrollbar_change&#x27;</span>, <span class="hljs-built_in">this</span>.updateScrollbar)
<span class="hljs-comment">// 根据事件返回的滚动条数据更新滚动条元素:</span>
{

View File

@@ -246,4 +246,8 @@
<img src="../../../../assets/avatar/moom.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>moom</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/张扬.png" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>张扬</p>
</div>
</div>

View File

@@ -203,6 +203,10 @@
<img src="../../../../assets/avatar/moom.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>moom</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/张扬.png" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>张扬</p>
</div>
</div>
</div>
</template>

View File

@@ -403,6 +403,22 @@ copyNodeTree({}, node)
从给定的节点实例列表里判断是否存在上下级关系。
#### handleSelfCloseTags(str)
> v0.9.1+
- `str`html字符串
给html自闭合标签添加闭合状态`<div><img src="xxx"></div>` -> `<div><img src="xxx" /></div>`
#### checkNodeListIsEqual(list1, list2)
> v0.9.1+
- `list1/list2`:节点实例列表
检查两个节点实例列表包含的节点是否是一样的。
## 在canvas中模拟css的背景属性
引入:

View File

@@ -328,6 +328,22 @@
<p>v0.8.1+</p>
</blockquote>
<p>从给定的节点实例列表里判断是否存在上下级关系</p>
<h4>handleSelfCloseTags(str)</h4>
<blockquote>
<p>v0.9.1+</p>
</blockquote>
<ul>
<li><code>str</code>html字符串</li>
</ul>
<p>给html自闭合标签添加闭合状态<code>&lt;div&gt;&lt;img src=&quot;xxx&quot;&gt;&lt;/div&gt;</code> -&gt; <code>&lt;div&gt;&lt;img src=&quot;xxx&quot; /&gt;&lt;/div&gt;</code></p>
<h4>checkNodeListIsEqual(list1, list2)</h4>
<blockquote>
<p>v0.9.1+</p>
</blockquote>
<ul>
<li><code>list1/list2</code>节点实例列表</li>
</ul>
<p>检查两个节点实例列表包含的节点是否是一样的</p>
<h2>在canvas中模拟css的背景属性</h2>
<p>引入</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> drawBackgroundImageToCanvas <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/utils/simulateCSSBackgroundInCanvas&#x27;</span>

View File

@@ -753,6 +753,36 @@
</el-select>
</div>
</div>
<!-- 配置创建新节点时的行为 -->
<div class="row">
<div class="rowItem">
<span class="name">{{ $t('baseStyle.createNewNodeBehavior') }}</span>
<el-select
size="mini"
style="width: 120px"
v-model="config.createNewNodeBehavior"
placeholder=""
@change="
value => {
updateOtherConfig('createNewNodeBehavior', value)
}
"
>
<el-option
:label="$t('baseStyle.default')"
value="default"
></el-option>
<el-option
:label="$t('baseStyle.notActive')"
value="notActive"
></el-option>
<el-option
:label="$t('baseStyle.activeOnly')"
value="activeOnly"
></el-option>
</el-select>
</div>
</div>
<!-- 是否显示滚动条 -->
<div class="row">
<div class="rowItem">
@@ -843,7 +873,8 @@ export default {
config: {
enableFreeDrag: false,
mousewheelAction: 'zoom',
mousewheelZoomActionReverse: false
mousewheelZoomActionReverse: false,
createNewNodeBehavior: 'default'
},
watermarkConfig: {
show: false,
@@ -968,7 +999,8 @@ export default {
;[
'enableFreeDrag',
'mousewheelAction',
'mousewheelZoomActionReverse'
'mousewheelZoomActionReverse',
'createNewNodeBehavior'
].forEach(key => {
this.config[key] = this.mindMap.getConfig(key)
})
@@ -979,9 +1011,7 @@ export default {
this.enableNodeRichText = this.localConfig.openNodeRichText
this.mousewheelAction = this.localConfig.mousewheelAction
this.mousewheelZoomActionReverse = this.localConfig.mousewheelZoomActionReverse
;[
'isShowScrollbar'
].forEach(key => {
;['isShowScrollbar'].forEach(key => {
this.localConfigs[key] = this.localConfig[key]
})
},

View File

@@ -188,6 +188,7 @@ export default {
this.$bus.$off('node_tree_render_end', this.handleHideLoading)
this.$bus.$off('showLoading', this.handleShowLoading)
window.removeEventListener('resize', this.handleResize)
this.mindMap.destroy()
},
methods: {
handleStartTextEdit() {
@@ -317,7 +318,17 @@ export default {
type: 'warning'
}
)
}
},
errorHandler: (code, err) => {
console.error(err)
switch (code) {
case 'export_error':
this.$message.error('导出失败')
break
default:
break
}
},
// isUseCustomNodeContent: true,
// 示例1组件里用到了router、store、i18n等实例化vue组件时需要用到的东西
// customCreateNodeContent: (node) => {
@@ -344,7 +355,7 @@ export default {
// return comp.$el
// },
// 示例3普通元素
// customCreateNodeContent: (node) => {
// customCreateNodeContent: node => {
// let el = document.createElement('div')
// el.style.cssText = `
// width: 203px;
@@ -357,9 +368,12 @@ export default {
// justify-content: center;
// align-items: center;
// `
// el.innerHTML = node.nodeData.data.text
// el.innerHTML = `
// ${node.nodeData.data.text}
// <img crossOrigin="anonymous" src="/img/cactus.jpg" />
// `
// return el
// },
// }
})
if (this.openNodeRichText) this.addRichTextPlugin()
this.mindMap.keyCommand.addShortcut('Control+s', () => {

View File

@@ -42,11 +42,11 @@ export default {
dataList: [
{
icon: 'iconstar',
value: 'Github star数量1000+'
value: 'Github star数量2000+'
},
{
icon: 'iconfork',
value: 'Github fork数量200+'
value: 'Github fork数量250+'
},
{
icon: 'iconxiazai',
@@ -54,7 +54,7 @@ export default {
},
{
icon: 'iconteamwork',
value: '代码贡献者12+'
value: '代码贡献者14+'
}
],
functionList: [

View File

@@ -20,36 +20,22 @@ const store = new Vuex.Store({
isShowScrollbar: false
},
activeSidebar: '', // 当前显示的侧边栏
isDark: false,// 是否是暗黑模式
isOutlineEdit: false,// 是否是大纲编辑模式
isReadonly: false// 是否只读
isDark: false, // 是否是暗黑模式
isOutlineEdit: false, // 是否是大纲编辑模式
isReadonly: false // 是否只读
},
mutations: {
/**
* @Author: 王林
* @Date: 2021-04-10 14:50:01
* @Desc: 设置思维导图数据
*/
// 设置思维导图数据
setMindMapData(state, data) {
state.mindMapData = data
},
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-24 13:55:38
* @Desc: 设置操作本地文件标志位
*/
// 设置操作本地文件标志位
setIsHandleLocalFile(state, data) {
state.isHandleLocalFile = data
},
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-11-14 18:42:47
* @Desc: 设置本地配置
*/
// 设置本地配置
setLocalConfig(state, data) {
state.localConfig = {
...state.localConfig,
@@ -58,12 +44,7 @@ const store = new Vuex.Store({
storeLocalConfig(state.localConfig)
},
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-11-15 19:25:26
* @Desc: 设置当前显示的侧边栏
*/
// 设置当前显示的侧边栏
setActiveSidebar(state, data) {
state.activeSidebar = data
},
@@ -81,14 +62,10 @@ const store = new Vuex.Store({
// 设置是否只读
setIsReadonly(state, data) {
state.isReadonly = data
},
}
},
actions: {
/**
* @Author: 王林
* @Date: 2021-04-10 14:50:40
* @Desc: 设置初始思维导图数据
*/
// 设置初始思维导图数据
getUserMindMapData(ctx) {
try {
let { data } = {