Compare commits

...

64 Commits

Author SHA1 Message Date
wanglin2
9915479354 打包0.8.0 2023-10-18 14:02:16 +08:00
wanglin2
abddafa3cf Demo:调整界面样式 2023-10-18 13:45:42 +08:00
wanglin2
5b057ff9de Doc: update 2023-10-18 11:21:43 +08:00
wanglin2
6dcbc0604d Fix:修复协同插件当创建新节点时新节点未显示创建人头像的问题 2023-10-18 11:19:55 +08:00
wanglin2
0e5fed6645 Doc: update 2023-10-18 10:49:27 +08:00
wanglin2
b6d91ec07a Feat:指定时间内只允许添加一次历史记录,避免添加没有必要的中间状态 2023-10-18 10:45:25 +08:00
wanglin2
baf36acc97 Doc: update 2023-10-17 16:56:07 +08:00
wanglin2
e0db4ad4f6 Doc: update 2023-10-17 16:14:56 +08:00
wanglin2
4fc2d79616 Fix:修复存在水印时小地图渲染非常慢的问题 2023-10-17 16:09:31 +08:00
wanglin2
9e4652b7b5 Fix:修复容器尺寸改变后没有水印没有重新绘制的问题 2023-10-17 16:01:03 +08:00
wanglin2
8466a3e99f Demo:小地图改为通过图片渲染 2023-10-17 15:35:32 +08:00
wanglin2
e85f187199 Feat:小地图插件支持返回图片类型的小地图 2023-10-17 15:35:21 +08:00
wanglin2
77167c572d Doc: update 2023-10-17 14:05:21 +08:00
wanglin2
9aae8bf55c Fix:修复存在水印时导出图片、svg、pdf时每个节点都会显示边框的问题 2023-10-17 14:02:08 +08:00
wanglin2
250fb2eb50 Fix:修复关联线插件computeNodePoints方法返回undefined时报错的问题 2023-10-17 13:45:39 +08:00
wanglin2
2a49dd9140 Doc: update 2023-10-17 11:13:06 +08:00
wanglin2
4dedaaea3b Demo:完善多语言 2023-10-16 18:43:07 +08:00
wanglin2
7bc666be36 Feat:粘贴带换行的文本支持控制是否按换行分割节点 2023-10-16 15:52:52 +08:00
wanglin2
93a56ef4ee Demo:支持手动输入缩放倍数 2023-10-16 15:08:31 +08:00
wanglin2
83b916d3c9 Demo:顶部工具栏支持根据窗口宽度自动收起到更多中 2023-10-16 14:26:10 +08:00
wanglin2
20157fcc8d Feat:被删除的节点同步从激活节点列表里删除;优化代码:1.移除父节点的连线逻辑合并到node.destroy方法内;2.提取render类中派发节点激活事件的重复代码 2023-10-16 10:29:45 +08:00
wanglin2
2c3fb4d7ea Feat:画布右键菜单事件清除当前激活的节点列表 2023-10-16 10:08:24 +08:00
wanglin2
75ad40ffbc Feat:鼠标右键单击画布时清除当前激活节点 2023-10-16 09:52:45 +08:00
wanglin2
e263eb8252 优化代码:render类中删除和更新概要时无需手动调用更新方法 2023-10-16 09:19:43 +08:00
wanglin2
8e43cd609f Feat:1.插入概要时自动展开子节点;2.删除插入和删除概要的方法中手动调用node.update方法的逻辑 2023-10-15 17:00:39 +08:00
wanglin2
1a3401fd1a 优化代码:修改unexpandAllNode,expandToLevel方法,没有子节点的节点无法收起 2023-10-15 16:46:08 +08:00
wanglin2
d1dcef2537 Feat:增加插入概要的默认文本配置选项 2023-10-15 15:15:27 +08:00
wanglin2
e732415aa3 优化代码:删除render类的unexpandAllNode,expandToLevel方法中重置_node属性的逻辑 2023-10-15 14:50:12 +08:00
wanglin2
ddbde0141a 优化代码:去除render类的setNodeExpand方法中的调用节点移除方法的逻辑 2023-10-15 11:18:44 +08:00
wanglin2
bc907f4b37 优化代码:1.将render类的setNodeActive方法的部分逻辑移到node类;2.将node类的updateNodeActive方法名称改为updateNodeActiveClass 2023-10-15 09:51:02 +08:00
wanglin2
1caf2c7f15 优化代码:将render类的setNodeStyle和setNodeStyles方法的公共逻辑提取到richText插件 2023-10-15 09:13:44 +08:00
wanglin2
22b56fb8dc 代码优化:通过addNodeToActiveList方法优化render类中的重复逻辑 2023-10-13 17:46:09 +08:00
wanglin2
87eccc298c 代码优化:删除节点时无需调用节点的删除方法,只需修改节点的nodeData.data数据即可 2023-10-13 17:30:21 +08:00
wanglin2
ca9e47183d 代码优化:将render类的onPaste方法中的读取剪贴板数据的逻辑提取为工具函数 2023-10-13 16:25:05 +08:00
wanglin2
1fbfe6f5ac 代码优化:将render类的setCopyDataToClipboard方法提取为工具方法 2023-10-13 16:18:28 +08:00
wanglin2
84d2a374d1 Demo:给节点的getData方法的返回值增加默认值 2023-10-13 16:09:35 +08:00
wanglin2
74a000723b 代码优化:读取和设置节点的nodeData.data改为通过setData和getData方法 2023-10-13 16:09:10 +08:00
wanglin2
5079ad2190 代码优化:将调用render类的setNodeData方法的地方改为调用SET_NODE_DATA命令 2023-10-13 15:26:00 +08:00
wanglin2
21053c43c9 Fix:修复同时给多个节点插入父节点时报错的问题 2023-10-13 15:17:19 +08:00
wanglin2
4c6270881a Demo:修改右键菜单的宽度 2023-10-13 14:29:51 +08:00
wanglin2
c17e5430ed 优化代码:使用getNodeDataIndex工具函数去除render类重复逻辑 2023-10-13 13:57:07 +08:00
wanglin2
0a36555343 代码优化:提取render类前进回退方法公共逻辑 2023-10-13 13:51:33 +08:00
wanglin2
bce2bb8fc4 代码优化:提取getNodeIndexInNodeList工具函数 2023-10-13 12:07:26 +08:00
wanglin2
d6ae06dbd6 代码优化:1.将render类的removeActiveNode函数名称改为removeNodeFromActiveList;2.addNodeToActiveList和removeNodeFromActiveList方法增加修改节点的激活状态数据 2023-10-13 11:46:29 +08:00
wanglin2
9221c404ee 代码优化:1.将render类的addActiveNode函数名称改为addNodeToActiveList;2.将调用render类的setNodeActive方法的地方改为调用SET_NODE_ACTIVE命令 2023-10-13 11:28:34 +08:00
wanglin2
697e53ff7d 代码优化:节点右键事件,如果有且只有当前节点被激活了,不再重复激活 2023-10-13 11:16:08 +08:00
wanglin2
9360aff6c9 代码优化:将render类的clearActive函数名称改为clearActiveNodeList 2023-10-13 11:05:34 +08:00
wanglin2
c68d629b7a 代码优化:将调用clearAllActive方法的地方改为调用CLEAR_ACTIVE_NODE命令 2023-10-13 10:55:02 +08:00
wanglin2
caedfb46a9 Demo:支持传入父节点和仅删除当前节点 2023-10-13 09:26:36 +08:00
wanglin2
2e4c6bc08e Feat:新增仅删除当前节点的命令 2023-10-13 09:26:12 +08:00
wanglin2
777eafcd2f Feat:新增插入父节点的命令;Fix:修复插入概要、上移、下移、一键整理布局的快捷键操作没有触发data_change事件的问题 2023-10-12 09:36:50 +08:00
wanglin2
20780a0c59 Doc: update 2023-10-12 09:11:41 +08:00
wanglin2
5d433cce16 Demo:修复覆盖方式切换主题时第一次切换不生效的问题 2023-10-11 17:13:35 +08:00
wanglin2
ba77fde93b Feat:setTheme、setThemeConfig、setLayout函数增加不触发重新渲染的参数 2023-10-11 17:12:37 +08:00
wanglin2
45b8850493 Fix:修复存在排队渲染时,最后一次渲染参数丢失的问题 2023-10-11 17:11:12 +08:00
wanglin2
39c2c15259 Demo:修改回到根节点的方法及文案 2023-10-11 15:49:22 +08:00
wanglin2
6d780c6c26 Feat:修复调整容器大小后回到根节点的操作异常的问题 2023-10-11 15:48:22 +08:00
wanglin2
4cf66adc18 Feat:优化代码,导出和适应画布操作时不再重新获取容器元素尺寸位置信息 2023-10-11 15:09:47 +08:00
wanglin2
7bff14e1bb Doc: update 2023-10-11 13:57:29 +08:00
wanglin2
7986e0d0cc Fix:修复导出图片和svg时关联线的箭头消失的问题 2023-10-11 13:49:28 +08:00
wanglin2
a316d0f0fe Feat:优化水印插件 2023-10-11 11:36:54 +08:00
wanglin2
88fa6225eb Feat:优化画布DOM结构,将节点、连线、关联线分层渲染 2023-10-11 11:36:34 +08:00
wanglin2
7ec720823f Demo build 2023-10-10 09:49:52 +08:00
wanglin2
c39daf72b4 Doc: update 2023-10-10 09:47:40 +08:00
119 changed files with 2994 additions and 1132 deletions

View File

@@ -202,4 +202,12 @@ const mindMap = new MindMap({
<img src="./web/src/assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;" />
<span>达仁科技</span>
</span>
<span>
<img src="./web/src/assets/avatar/小逗比.png" style="width: 50px;height: 50px;" />
<span>小逗比</span>
</span>
<span>
<img src="./web/src/assets/avatar/天清如愿.jpg" 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?97b4d7ae279db040269e" rel="stylesheet"><link href="dist/css/app.css?97b4d7ae279db040269e" 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?d19203eaeb8de9913fb3" rel="stylesheet"><link href="dist/css/app.css?d19203eaeb8de9913fb3" 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?97b4d7ae279db040269e"></script><script src="dist/js/app.js?97b4d7ae279db040269e"></script></body></html>
}</script><script src="dist/js/chunk-vendors.js?d19203eaeb8de9913fb3"></script><script src="dist/js/app.js?d19203eaeb8de9913fb3"></script></body></html>

View File

@@ -35,21 +35,16 @@ class MindMap {
// 容器元素
this.el = this.opt.el
if (!this.el) throw new Error('缺少容器元素el')
this.elRect = this.el.getBoundingClientRect()
// 画布宽高
this.width = this.elRect.width
this.height = this.elRect.height
if (this.width <= 0 || this.height <= 0)
throw new Error('容器元素el的宽高不能为0')
// 获取容器尺寸位置信息
this.getElRectInfo()
// 添加css
this.cssEl = null
this.addCss()
// 画布
this.svg = SVG().addTo(this.el).size(this.width, this.height)
this.draw = this.svg.group()
this.initContainer()
// 初始化主题
this.initTheme()
@@ -79,8 +74,7 @@ class MindMap {
// 视图操作类
this.view = new View({
mindMap: this,
draw: this.draw
mindMap: this
})
// 批量执行类
@@ -111,6 +105,46 @@ class MindMap {
return opt
}
// 创建容器元素
initContainer() {
const { associativeLineIsAlwaysAboveNode } = this.opt
// 节点关联线容器
const createAssociativeLineDraw = () => {
this.associativeLineDraw = this.draw.group()
this.associativeLineDraw.addClass('smm-associative-line-container')
}
// 画布
this.svg = SVG().addTo(this.el).size(this.width, this.height)
// 容器
this.draw = this.svg.group()
this.draw.addClass('smm-container')
// 节点连线容器
this.lineDraw = this.draw.group()
this.lineDraw.addClass('smm-line-container')
// 默认处于节点下方
if (!associativeLineIsAlwaysAboveNode) {
createAssociativeLineDraw()
}
// 节点容器
this.nodeDraw = this.draw.group()
this.nodeDraw.addClass('smm-node-container')
// 关联线始终处于节点上方
if (associativeLineIsAlwaysAboveNode) {
createAssociativeLineDraw()
}
// 其他内容的容器
this.otherDraw = this.draw.group()
this.otherDraw.addClass('smm-other-container')
}
// 清空各容器
clearDraw() {
this.lineDraw.clear()
this.associativeLineDraw.clear()
this.nodeDraw.clear()
this.otherDraw.clear()
}
// 添加必要的css样式到页面
addCss() {
this.cssEl = document.createElement('style')
@@ -136,19 +170,27 @@ class MindMap {
// 重新渲染
reRender(callback, source = '') {
this.batchExecution.push('render', () => {
this.draw.clear()
this.clearDraw()
this.initTheme()
this.renderer.reRender = true
this.renderer.render(callback, source)
})
}
// 容器尺寸变化,调整尺寸
resize() {
// 获取或更新容器尺寸位置信息
getElRectInfo() {
this.elRect = this.el.getBoundingClientRect()
this.width = this.elRect.width
this.height = this.elRect.height
if (this.width <= 0 || this.height <= 0)
throw new Error('容器元素el的宽高不能为0')
}
// 容器尺寸变化,调整尺寸
resize() {
this.getElRectInfo()
this.svg.size(this.width, this.height)
this.emit('resize')
}
// 监听事件
@@ -192,10 +234,12 @@ class MindMap {
}
// 设置主题
setTheme(theme) {
this.renderer.clearAllActive()
setTheme(theme, notRender = false) {
this.execCommand('CLEAR_ACTIVE_NODE')
this.opt.theme = theme
this.render(null, CONSTANTS.CHANGE_THEME)
if (!notRender) {
this.render(null, CONSTANTS.CHANGE_THEME)
}
this.emit('view_theme_change', theme)
}
@@ -205,13 +249,15 @@ class MindMap {
}
// 设置主题配置
setThemeConfig(config) {
setThemeConfig(config, notRender = false) {
// 计算改变了的配置
const changedConfig = getObjectChangedProps(this.themeConfig, config)
this.opt.themeConfig = config
// 检查改变的是否是节点大小无关的主题属性
let res = checkIsNodeSizeIndependenceConfig(changedConfig)
this.render(null, res ? '' : CONSTANTS.CHANGE_THEME)
if (!notRender) {
// 检查改变的是否是节点大小无关的主题属性
let res = checkIsNodeSizeIndependenceConfig(changedConfig)
this.render(null, res ? '' : CONSTANTS.CHANGE_THEME)
}
}
// 获取自定义主题配置
@@ -240,7 +286,7 @@ class MindMap {
}
// 设置布局结构
setLayout(layout) {
setLayout(layout, notRender = false) {
// 检查布局配置
if (!layoutValueList.includes(layout)) {
layout = CONSTANTS.LAYOUT.LOGICAL_STRUCTURE
@@ -248,7 +294,9 @@ class MindMap {
this.opt.layout = layout
this.view.reset()
this.renderer.setLayout()
this.render(null, CONSTANTS.CHANGE_LAYOUT)
if (!notRender) {
this.render(null, CONSTANTS.CHANGE_LAYOUT)
}
}
// 执行命令
@@ -334,20 +382,20 @@ class MindMap {
this.opt.readonly = mode === CONSTANTS.MODE.READONLY
if (this.opt.readonly) {
// 取消当前激活的元素
this.renderer.clearAllActive()
this.execCommand('CLEAR_ACTIVE_NODE')
}
this.emit('mode_change', mode)
}
// 获取svg数据
getSvgData({ paddingX = 0, paddingY = 0 } = {}) {
getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false } = {}) {
const svg = this.svg
const draw = this.draw
// 保存原始信息
const origWidth = svg.width()
const origHeight = svg.height()
const origTransform = draw.transform()
const elRect = this.el.getBoundingClientRect()
const elRect = this.elRect
// 去除放大缩小的变换效果
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
// 获取变换后的位置尺寸信息其实是getBoundingClientRect方法的包装方法
@@ -362,10 +410,9 @@ class MindMap {
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
// 克隆一份数据
let clone = svg.clone()
// 添加必要的样式
clone.add(SVG(`<style>${cssContent}</style>`))
// 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题
if (
!ignoreWatermark &&
(rect.width > origWidth || rect.height > origHeight) &&
this.watermark &&
this.watermark.hasWatermark()
@@ -378,6 +425,16 @@ class MindMap {
this.height = origHeight
this.watermark.draw()
}
// 添加必要的样式
clone.add(SVG(`<style>${cssContent}</style>`))
// 修正关联线箭头marker的id
const markerList = svg.find('marker')
if (markerList && markerList.length > 0) {
const id = markerList[0].attr('id')
clone.find('marker').forEach(item => {
item.attr('id', id)
})
}
// 恢复原先的大小和变换信息
svg.size(origWidth, origHeight)
draw.transform(origTransform)

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.7.3-fix.2",
"version": "0.8.0",
"description": "一个简单的web在线思维导图",
"authors": [
{
@@ -22,7 +22,7 @@
"scripts": {
"lint": "eslint src/",
"format": "prettier --write .",
"types": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir types --target es2017",
"types": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir types --target es2017 --skipLibCheck",
"wsServe": "node ./bin/wsServer.mjs"
},
"module": "index.js",

View File

@@ -173,8 +173,8 @@ export const defaultOpt = {
box-sizing: border-box;
}
`,
// 开启鼠标双击复位思维导图位置及缩放
enableDblclickReset: false,
// 是否在鼠标双击时回到根节点,也就是让根节点居中显示
enableDblclickBackToRootNode: false,
// 导出图片时canvas的缩放倍数该配置会和window.devicePixelRatio值取最大值
minExportImgCanvasScale: 2,
// 节点鼠标hover和激活时显示的矩形边框的颜色
@@ -209,5 +209,15 @@ export const defaultOpt = {
cooperateStyle: {
avatarSize: 22,// 头像大小
fontSize: 12,// 如果是文字头像,那么文字的大小
}
},
// 关联线是否始终显示在节点上层
// false即创建关联线和激活关联线时处于最顶层其他情况下处于节点下方
associativeLineIsAlwaysAboveNode: true,
// 插入概要的默认文本
defaultGeneralizationText: '概要',
// 粘贴文本的方式创建新节点时,控制是否按换行自动分割节点,即如果存在换行,那么会根据换行创建多个节点,否则只会创建一个节点
// 可以传递一个函数返回promiseresolve代表根据换行分割reject代表忽略换行
handleIsSplitByWrapOnPasteCreateNewNode: null,
// 多少时间内只允许添加一次历史记录避免添加没有必要的中间状态单位ms
addHistoryTime: 100
}

View File

@@ -1,4 +1,4 @@
import { copyRenderTree, simpleDeepClone, nextTick } from '../../utils'
import { copyRenderTree, simpleDeepClone, throttle } from '../../utils'
// 命令类
class Command {
@@ -11,7 +11,11 @@ class Command {
this.activeHistoryIndex = 0
// 注册快捷键
this.registerShortcutKeys()
this.addHistory = nextTick(this.addHistory, this)
this.addHistory = throttle(
this.addHistory,
this.mindMap.opt.addHistoryTime,
this
)
}
// 清空历史数据

File diff suppressed because it is too large Load Diff

View File

@@ -173,7 +173,7 @@ export default class TextEdit {
let scale = this.mindMap.view.scale
let lineHeight = node.style.merge('lineHeight')
let fontSize = node.style.merge('fontSize')
let textLines = (this.cacheEditingText || node.nodeData.data.text)
let textLines = (this.cacheEditingText || node.getData('text'))
.split(/\n/gim)
.map(item => {
return htmlEscape(item)

View File

@@ -22,7 +22,9 @@ class Node {
// 渲染实例
this.renderer = opt.renderer
// 渲染器
this.draw = opt.draw || null
this.draw = this.mindMap.draw
this.nodeDraw = this.mindMap.nodeDraw
this.lineDraw = this.mindMap.lineDraw
// 样式实例
this.style = new Style(this)
// 形状实例
@@ -127,7 +129,7 @@ class Node {
})
// 协同相关
if (this.mindMap.cooperate) {
Object.keys(nodeCooperateMethods).forEach((item) => {
Object.keys(nodeCooperateMethods).forEach(item => {
this[item] = nodeCooperateMethods[item].bind(this)
})
}
@@ -428,17 +430,16 @@ class Node {
// 多选和取消多选
if (e.ctrlKey && enableCtrlKeyNodeSelection) {
this.isMultipleChoice = true
let isActive = this.nodeData.data.isActive
let isActive = this.getData('isActive')
if (!isActive)
this.mindMap.emit(
'before_node_active',
this,
this.renderer.activeNodeList
)
this.mindMap.execCommand('SET_NODE_ACTIVE', this, !isActive)
this.mindMap.renderer[isActive ? 'removeActiveNode' : 'addActiveNode'](
this
)
this.mindMap.renderer[
isActive ? 'removeNodeFromActiveList' : 'addNodeToActiveList'
](this)
this.mindMap.emit('node_active', isActive ? null : this, [
...this.mindMap.renderer.activeNodeList
])
@@ -489,10 +490,13 @@ class Node {
) {
return
}
if (this.nodeData.data.isActive) {
this.renderer.clearActive()
// 如果有且只有当前节点激活了,那么不需要重新激活
if (
!(this.getData('isActive') && this.renderer.activeNodeList.length === 1)
) {
this.renderer.clearActiveNodeList()
this.active(e)
}
this.active(e)
this.mindMap.emit('node_contextmenu', e, this)
})
}
@@ -503,13 +507,12 @@ class Node {
return
}
e && e.stopPropagation()
if (this.nodeData.data.isActive) {
if (this.getData('isActive')) {
return
}
this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
this.renderer.clearActive()
this.mindMap.execCommand('SET_NODE_ACTIVE', this, true)
this.renderer.addActiveNode(this)
this.renderer.clearActiveNodeList()
this.renderer.addNodeToActiveList(this)
this.mindMap.emit('node_active', this, [...this.renderer.activeNodeList])
}
@@ -518,7 +521,7 @@ class Node {
if (!this.group) {
return
}
this.updateNodeActive()
this.updateNodeActiveClass()
let { alwaysShowExpandBtn } = this.mindMap.opt
if (alwaysShowExpandBtn) {
// 需要移除展开收缩按钮
@@ -529,7 +532,7 @@ class Node {
this.renderExpandBtn()
}
} else {
let { isActive, expand } = this.nodeData.data
let { isActive, expand } = this.getData()
// 展开状态且非激活状态,且当前鼠标不在它上面,才隐藏
if (expand && !isActive && !this._isMouseenter) {
this.hideExpandBtn()
@@ -594,12 +597,25 @@ class Node {
}
// 更新节点激活状态
updateNodeActive() {
updateNodeActiveClass() {
if (!this.group) return
const isActive = this.nodeData.data.isActive
const isActive = this.getData('isActive')
this.group[isActive ? 'addClass' : 'removeClass']('active')
}
// 根据是否激活更新节点
updateNodeByActive(active) {
if (this.group) {
// 切换激活状态,需要切换展开收起按钮的显隐
if (active) {
this.showExpandBtn()
} else {
this.hideExpandBtn()
}
this.updateNodeActiveClass()
}
}
// 递归渲染
render(callback = () => {}) {
// 节点
@@ -613,11 +629,11 @@ class Node {
cursor: 'default'
})
this.bindGroupEvent()
this.draw.add(this.group)
this.nodeDraw.add(this.group)
this.layout()
this.update()
} else {
this.draw.add(this.group)
this.nodeDraw.add(this.group)
if (this.needLayout) {
this.needLayout = false
this.layout()
@@ -629,7 +645,7 @@ class Node {
if (
this.children &&
this.children.length &&
this.nodeData.data.expand !== false
this.getData('expand') !== false
) {
let index = 0
this.children.forEach(item => {
@@ -674,6 +690,9 @@ class Node {
this.removeGeneralization()
this.removeLine()
this.group = null
if (this.parent) {
this.parent.removeLine()
}
}
// 隐藏节点
@@ -774,7 +793,7 @@ class Node {
// 连线
renderLine(deep = false) {
if (this.nodeData.data.expand === false) {
if (this.getData('expand') === false) {
return
}
let childrenLen = this.nodeData.children.length
@@ -788,7 +807,7 @@ class Node {
if (childrenLen > this._lines.length) {
// 创建缺少的线
new Array(childrenLen - this._lines.length).fill(0).forEach(() => {
this._lines.push(this.draw.path())
this._lines.push(this.lineDraw.path())
})
} else if (childrenLen < this._lines.length) {
// 删除多余的线
@@ -896,7 +915,7 @@ class Node {
// 获取padding值
getPaddingVale() {
let { isActive } = this.nodeData.data
let { isActive } = this.getData()
return {
paddingX: this.getStyle('paddingX', true, isActive),
paddingY: this.getStyle('paddingY', true, isActive)
@@ -939,7 +958,7 @@ class Node {
// 获取数据
getData(key) {
return key ? this.nodeData.data[key] || '' : this.nodeData.data
return key ? this.nodeData.data[key] : this.nodeData.data
}
// 是否存在自定义样式

View File

@@ -88,7 +88,7 @@ class Style {
// 获取自身自定义样式
getSelfStyle(prop) {
return this.ctx.nodeData.data[prop]
return this.ctx.getData(prop)
}
// 矩形
@@ -107,7 +107,7 @@ class Style {
// !this.ctx.isRoot &&
// !this.ctx.isGeneralization &&
// this.ctx.mindMap.themeConfig.nodeUseLineStyle &&
// !this.ctx.nodeData.data.isActive
// !this.ctx.getData('isActive')
// ) {
// return
// }
@@ -225,7 +225,7 @@ class Style {
// 是否设置了自定义的样式
hasCustomStyle() {
let res = false
Object.keys(this.ctx.nodeData.data).forEach(item => {
Object.keys(this.ctx.getData()).forEach(item => {
if (checkIsNodeStyleDataKey(item)) {
res = true
}

View File

@@ -12,14 +12,14 @@ import { CONSTANTS, commonCaches } from '../../../constants/constant'
// 创建图片节点
function createImgNode() {
let img = this.nodeData.data.image
let img = this.getData('image')
if (!img) {
return
}
let imgSize = this.getImgShowSize()
let node = new Image().load(img).size(...imgSize)
if (this.nodeData.data.imageTitle) {
node.attr('title', this.nodeData.data.imageTitle)
if (this.getData('imageTitle')) {
node.attr('title', this.getData('imageTitle'))
}
node.on('dblclick', e => {
this.mindMap.emit('node_img_dblclick', this, e)
@@ -42,7 +42,7 @@ function createImgNode() {
// 获取图片显示宽高
function getImgShowSize() {
const { custom, width, height } = this.nodeData.data.imageSize
const { custom, width, height } = this.getData('imageSize')
// 如果是自定义了图片的宽高,那么不受最大宽高限制
if (custom) return [width, height]
return resizeImgSize(
@@ -55,7 +55,7 @@ function getImgShowSize() {
// 创建icon节点
function createIconNode() {
let _data = this.nodeData.data
let _data = this.getData()
if (!_data.icon || _data.icon.length <= 0) {
return []
}
@@ -91,7 +91,7 @@ function createRichTextNode() {
let g = new G()
// 重新设置富文本节点内容
let recoverText = false
if (this.nodeData.data.resetRichText) {
if (this.getData('resetRichText')) {
delete this.nodeData.data.resetRichText
recoverText = true
}
@@ -102,7 +102,7 @@ function createRichTextNode() {
}
}
if (recoverText) {
let text = this.nodeData.data.text
let text = this.getData('text')
// 判断节点内容是否是富文本
let isRichText = checkIsRichText(text)
// 样式字符串
@@ -116,9 +116,11 @@ function createRichTextNode() {
// 非富文本
text = `<p><span style="${style}">${text}</span></p>`
}
this.nodeData.data.text = text
this.setData({
text: text
})
}
let html = `<div>${this.nodeData.data.text}</div>`
let html = `<div>${this.getData('text')}</div>`
if (!commonCaches.measureRichtextNodeTextSizeEl) {
commonCaches.measureRichtextNodeTextSizeEl = document.createElement('div')
commonCaches.measureRichtextNodeTextSizeEl.style.position = 'fixed'
@@ -157,7 +159,7 @@ function createRichTextNode() {
// 创建文本节点
function createTextNode() {
if (this.nodeData.data.richText) {
if (this.getData('richText')) {
return this.createRichTextNode()
}
let g = new G()
@@ -166,8 +168,8 @@ function createTextNode() {
// 文本超长自动换行
let textStyle = this.style.getTextFontStyle()
let textArr = []
if (!isUndef(this.nodeData.data.text)) {
textArr = String(this.nodeData.data.text).split(/\n/gim)
if (!isUndef(this.getData('text'))) {
textArr = String(this.getData('text')).split(/\n/gim)
}
let maxWidth = this.mindMap.opt.textAutoWrapWidth
let isMultiLine = false
@@ -215,7 +217,7 @@ function createTextNode() {
// 创建超链接节点
function createHyperlinkNode() {
let { hyperlink, hyperlinkTitle } = this.nodeData.data
let { hyperlink, hyperlinkTitle } = this.getData()
if (!hyperlink) {
return
}
@@ -245,7 +247,7 @@ function createHyperlinkNode() {
// 创建标签节点
function createTagNode() {
let tagData = this.nodeData.data.tag
let tagData = this.getData('tag')
if (!tagData || tagData.length <= 0) {
return []
}
@@ -274,7 +276,7 @@ function createTagNode() {
// 创建备注节点
function createNoteNode() {
if (!this.nodeData.data.note) {
if (!this.getData('note')) {
return null
}
let iconSize = this.mindMap.themeConfig.iconSize
@@ -302,7 +304,7 @@ function createNoteNode() {
this.mindMap.opt.customInnerElsAppendTo || document.body
targetNode.appendChild(this.noteEl)
}
this.noteEl.innerText = this.nodeData.data.note
this.noteEl.innerText = this.getData('note')
}
node.on('mouseover', () => {
let { left, top } = node.node.getBoundingClientRect()
@@ -312,7 +314,7 @@ function createNoteNode() {
this.noteEl.style.display = 'block'
} else {
this.mindMap.opt.customNoteContentShow.show(
this.nodeData.data.note,
this.getData('note'),
left,
top + iconSize
)

View File

@@ -52,7 +52,7 @@ function sumNode(data = []) {
}
// 创建或更新展开收缩按钮内容
function updateExpandBtnNode() {
let { expand } = this.nodeData.data
let { expand } = this.getData()
// 如果本次和上次的展开状态一样则返回
if (expand === this._lastExpandBtnType) return
if (this._expandBtn) {
@@ -129,7 +129,7 @@ function renderExpandBtn() {
this.mindMap.execCommand(
'SET_NODE_EXPAND',
this,
!this.nodeData.data.expand
!this.getData('expand')
)
this.mindMap.emit('expand_btn_click', this)
})
@@ -164,7 +164,7 @@ function showExpandBtn() {
function hideExpandBtn() {
if (this.mindMap.opt.alwaysShowExpandBtn || this._isMouseenter) return
// 非激活状态且展开状态鼠标移出才隐藏按钮
let { isActive, expand } = this.nodeData.data
let { isActive, expand } = this.getData()
if (!isActive && expand) {
setTimeout(() => {
this.removeExpandBtn()

View File

@@ -3,7 +3,7 @@ import { createUid } from '../../../utils/index'
// 检查是否存在概要
function checkHasGeneralization() {
return !!this.nodeData.data.generalization
return !!this.getData('generalization')
}
// 创建概要节点
@@ -12,24 +12,23 @@ function createGeneralizationNode() {
return
}
if (!this._generalizationLine) {
this._generalizationLine = this.draw.path()
this._generalizationLine = this.lineDraw.path()
}
if (!this._generalizationNode) {
this._generalizationNode = new Node({
data: {
data: this.nodeData.data.generalization
data: this.getData('generalization')
},
uid: createUid(),
renderer: this.renderer,
mindMap: this.mindMap,
draw: this.draw,
isGeneralization: true
})
this._generalizationNodeWidth = this._generalizationNode.width
this._generalizationNodeHeight = this._generalizationNode.height
this._generalizationNode.generalizationBelongNode = this
if (this.nodeData.data.generalization.isActive) {
this.renderer.addActiveNode(this._generalizationNode)
if (this.getData('generalization').isActive) {
this.renderer.addNodeToActiveList(this._generalizationNode)
}
}
}
@@ -50,7 +49,7 @@ function renderGeneralization() {
this._generalizationNodeHeight = 0
return
}
if (this.nodeData.data.expand === false) {
if (this.getData('expand') === false) {
this.removeGeneralization()
return
}
@@ -73,13 +72,13 @@ function removeGeneralization() {
}
if (this._generalizationNode) {
// 删除概要节点时要同步从激活节点里删除
this.renderer.removeActiveNode(this._generalizationNode)
this.renderer.removeNodeFromActiveList(this._generalizationNode)
this._generalizationNode.remove()
this._generalizationNode = null
}
// hack修复当激活一个节点时创建概要然后立即激活创建的概要节点后会重复创建概要节点并且无法删除的问题
if (this.generalizationBelongNode) {
this.draw
this.nodeDraw
.find('.generalization_' + this.generalizationBelongNode.uid)
.remove()
}

View File

@@ -25,16 +25,9 @@ class View {
this.mindMap.keyCommand.addShortcut('Control+-', () => {
this.narrow()
})
this.mindMap.keyCommand.addShortcut('Control+Enter', () => {
this.reset()
})
this.mindMap.keyCommand.addShortcut('Control+i', () => {
this.fit()
})
this.mindMap.svg.on('dblclick', () => {
if (!this.mindMap.opt.enableDblclickReset) return
this.reset()
})
// 拖动视图
this.mindMap.event.on('mousedown', () => {
this.sx = this.x
@@ -267,7 +260,7 @@ class View {
let drawHeight = rect.height / origTransform.scaleY
let drawRatio = drawWidth / drawHeight
let { width: elWidth, height: elHeight } =
this.mindMap.el.getBoundingClientRect()
this.mindMap.elRect
elWidth = elWidth - fitPadding * 2
elHeight = elHeight - fitPadding * 2
let elRatio = elWidth / elHeight

View File

@@ -13,6 +13,7 @@ class Base {
this.mindMap = renderer.mindMap
// 绘图对象
this.draw = this.mindMap.draw
this.lineDraw = this.mindMap.lineDraw
// 根节点
this.root = null
this.lru = new Lru(this.mindMap.opt.maxNodeCacheCount)
@@ -90,7 +91,7 @@ class Base {
// 数据上没有保存节点引用但是通过uid找到了缓存的节点也可以复用
newNode = this.lru.get(data.data.uid)
// 保存该节点上一次的数据
let lastData = JSON.stringify(newNode.nodeData.data)
let lastData = JSON.stringify(newNode.getData())
let isLayerTypeChange = this.checkIsLayerTypeChange(
newNode.layerIndex,
layerIndex
@@ -126,12 +127,14 @@ class Base {
// 数据关联实际节点
data._node = newNode
if (data.data.isActive) {
this.renderer.addActiveNode(newNode)
this.renderer.addNodeToActiveList(newNode)
}
}
// 如果当前节点在激活节点列表里,那么添加上激活的状态
if (this.mindMap.renderer.findActiveNodeIndex(newNode) !== -1) {
newNode.nodeData.data.isActive = true
newNode.setData({
isActive: true
})
}
// 根节点
if (isRoot) {
@@ -297,12 +300,12 @@ class Base {
let { left, right, top, bottom } = walk(child)
// 概要内容的宽度
let generalizationWidth =
child.checkHasGeneralization() && child.nodeData.data.expand
child.checkHasGeneralization() && child.getData('expand')
? child._generalizationNodeWidth + generalizationNodeMargin
: 0
// 概要内容的高度
let generalizationHeight =
child.checkHasGeneralization() && child.nodeData.data.expand
child.checkHasGeneralization() && child.getData('expand')
? child._generalizationNodeHeight + generalizationNodeMargin
: 0
if (left - (dir === 'h' ? generalizationWidth : 0) < _left) {

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
// 目录组织图
class CatalogOrganization extends Base {
@@ -73,7 +73,7 @@ class CatalogOrganization extends Base {
null,
(node, parent, isRoot, layerIndex) => {
if (
node.nodeData.data.expand &&
node.getData('expand') &&
node.children &&
node.children.length
) {
@@ -114,7 +114,7 @@ class CatalogOrganization extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 调整left
@@ -159,9 +159,7 @@ class CatalogOrganization extends Base {
updateBrothersLeft(node, addWidth) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition() || _index <= index) {
// 适配自定义位置
@@ -182,9 +180,7 @@ class CatalogOrganization extends Base {
updateBrothersTop(node, addHeight) {
if (node.parent && !node.parent.isRoot) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置
@@ -247,14 +243,14 @@ class CatalogOrganization extends Base {
minx = Math.min(minx, x1)
maxx = Math.max(maxx, x1)
// 父节点的竖线
let line1 = this.draw.path()
let line1 = this.lineDraw.path()
node.style.line(line1)
line1.plot(`M ${x1},${y1} L ${x1},${y1 + s1}`)
node._lines.push(line1)
style && style(line1, node)
// 水平线
if (len > 0) {
let lin2 = this.draw.path()
let lin2 = this.lineDraw.path()
node.style.line(lin2)
lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)
node._lines.push(lin2)
@@ -315,7 +311,7 @@ class CatalogOrganization extends Base {
})
// 竖线
if (len > 0) {
let lin2 = this.draw.path()
let lin2 = this.lineDraw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
node.style.line(lin2)
if (maxy < y1 + expandBtnSize) {

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun, degToRad } from '../utils'
import { walk, asyncRun, degToRad, getNodeIndexInNodeList } from '../utils'
import { CONSTANTS } from '../constants/constant'
import utils from './fishboneUtils'
@@ -112,7 +112,7 @@ class Fishbone extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
let params = { node, parent, layerIndex, ctx: this }
@@ -193,9 +193,7 @@ class Fishbone extends Base {
updateBrothersTop(node, addHeight) {
if (node.parent && !node.parent.isRoot) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置
@@ -252,7 +250,7 @@ class Fishbone extends Base {
let nodeLineX = item.left
let offset = node.height / 2 + marginY
let offsetX = offset / Math.tan(degToRad(this.mindMap.opt.fishboneDeg))
let line = this.draw.path()
let line = this.lineDraw.path()
if (this.checkIsTop(item)) {
line.plot(
`M ${nodeLineX - offsetX},${item.top + item.height + offset} L ${
@@ -273,7 +271,7 @@ class Fishbone extends Base {
// 从根节点出发的水平线
let nodeHalfTop = node.top + node.height / 2
let offset = node.height / 2 + this.getMarginY(node.layerIndex + 1)
let line = this.draw.path()
let line = this.lineDraw.path()
line.plot(
`M ${node.left + node.width},${nodeHalfTop} L ${
maxx - offset / Math.tan(degToRad(this.mindMap.opt.fishboneDeg))
@@ -308,7 +306,7 @@ class Fishbone extends Base {
})
// 斜线
if (len >= 0) {
let line = this.draw.path()
let line = this.lineDraw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
let lineLength = maxx - node.left - node.width * this.indent
lineLength = Math.max(lineLength, 0)

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
import { CONSTANTS } from '../utils/constant'
const degToRad = deg => {
@@ -127,7 +127,7 @@ class Fishbone extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 调整top
@@ -237,9 +237,7 @@ class Fishbone extends Base {
updateBrothersTop(node, addHeight) {
if (node.parent && !node.parent.isRoot) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置
@@ -307,7 +305,7 @@ class Fishbone extends Base {
})
// 竖线
if (len > 0) {
let line = this.draw.path()
let line = this.lineDraw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
let lineLength = maxx - node.left - node.width * 0.3
if (node.parent && node.parent.isRoot) {

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
import { CONSTANTS } from '../utils/constant'
const degToRad = deg => {
@@ -110,7 +110,7 @@ class Fishbone extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 调整top
@@ -206,9 +206,7 @@ class Fishbone extends Base {
updateBrothersTop(node, addHeight) {
if (node.parent && !node.parent.isRoot) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置
@@ -276,7 +274,7 @@ class Fishbone extends Base {
})
// 竖线
if (len > 0) {
let line = this.draw.path()
let line = this.lineDraw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
let lineLength = maxx - node.left - node.width * 0.3
if (

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
// 逻辑结构图
class LogicalStructure extends Base {
@@ -78,7 +78,7 @@ class LogicalStructure extends Base {
null,
(node, parent, isRoot, layerIndex) => {
if (
node.nodeData.data.expand &&
node.getData('expand') &&
node.children &&
node.children.length
) {
@@ -103,7 +103,7 @@ class LogicalStructure extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
@@ -124,9 +124,7 @@ class LogicalStructure extends Base {
updateBrothers(node, addHeight) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.uid === node.uid || item.hasCustomPosition()) {
// 适配自定义位置

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
import { CONSTANTS } from '../constants/constant'
// 思维导图
@@ -117,7 +117,7 @@ class MindMap extends Base {
null,
(node, parent, isRoot, layerIndex) => {
if (
node.nodeData.data.expand &&
node.getData('expand') &&
node.children &&
node.children.length
) {
@@ -148,7 +148,7 @@ class MindMap extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
@@ -171,9 +171,7 @@ class MindMap extends Base {
let childrenList = node.parent.children.filter(item => {
return item.dir === node.dir
})
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
// 组织结构图
// 和逻辑结构图基本一样只是方向变成向下生长所以先计算节点的top后计算节点的left、最后调整节点的left即可
@@ -79,7 +79,7 @@ class OrganizationStructure extends Base {
null,
(node, parent, isRoot, layerIndex) => {
if (
node.nodeData.data.expand &&
node.getData('expand') &&
node.children &&
node.children.length
) {
@@ -104,7 +104,7 @@ class OrganizationStructure extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 判断子节点所占的宽度之和是否大于该节点自身,大于则需要调整位置
@@ -125,9 +125,7 @@ class OrganizationStructure extends Base {
updateBrothers(node, addWidth) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置
@@ -218,7 +216,7 @@ class OrganizationStructure extends Base {
minx = Math.min(x1, minx)
maxx = Math.max(x1, maxx)
// 父节点的竖线
let line1 = this.draw.path()
let line1 = this.lineDraw.path()
node.style.line(line1)
expandBtnSize = len > 0 && !isRoot ? expandBtnSize : 0
line1.plot(`M ${x1},${y1 + expandBtnSize} L ${x1},${y1 + s1}`)
@@ -226,7 +224,7 @@ class OrganizationStructure extends Base {
style && style(line1, node)
// 水平线
if (len > 0) {
let lin2 = this.draw.path()
let lin2 = this.lineDraw.path()
node.style.line(lin2)
lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)
node._lines.push(lin2)

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
import { CONSTANTS } from '../constants/constant'
// 时间轴
@@ -81,7 +81,7 @@ class Timeline extends Base {
null,
(node, parent, isRoot, layerIndex, index) => {
if (
node.nodeData.data.expand &&
node.getData('expand') &&
node.children &&
node.children.length
) {
@@ -122,7 +122,7 @@ class Timeline extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
// 调整left
@@ -208,9 +208,7 @@ class Timeline extends Base {
updateBrothersTop(node, addHeight) {
if (node.parent && !node.parent.isRoot) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置
@@ -275,7 +273,7 @@ class Timeline extends Base {
})
// 竖线
if (len > 0) {
let line = this.draw.path()
let line = this.lineDraw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
if (
node.parent &&

View File

@@ -1,5 +1,5 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
import { walk, asyncRun, getNodeIndexInNodeList } from '../utils'
import { CONSTANTS } from '../constants/constant'
// 竖向时间轴
@@ -98,7 +98,7 @@ class VerticalTimeline extends Base {
null,
(node, parent, isRoot, layerIndex, index) => {
if (
node.nodeData.data.expand &&
node.getData('expand') &&
node.children &&
node.children.length
) {
@@ -135,7 +135,7 @@ class VerticalTimeline extends Base {
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
if (!node.getData('expand')) {
return
}
if (isRoot) return
@@ -155,9 +155,7 @@ class VerticalTimeline extends Base {
updateBrothers(node, addHeight) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
// 自定义节点位置
if (item.hasCustomPosition()) return
@@ -201,9 +199,7 @@ class VerticalTimeline extends Base {
updateBrothersTop(node, addHeight) {
if (node.parent && !node.parent.isRoot) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, childrenList)
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {
// 适配自定义位置

View File

@@ -15,7 +15,7 @@ import associativeLineTextMethods from './associativeLine/associativeLineText'
class AssociativeLine {
constructor(opt = {}) {
this.mindMap = opt.mindMap
this.draw = this.mindMap.draw
this.associativeLineDraw = this.mindMap.associativeLineDraw
// 当前所有连接线
this.lineList = []
// 当前激活的连接线
@@ -98,7 +98,7 @@ class AssociativeLine {
// 创建箭头
createMarker() {
return this.draw.marker(20, 20, add => {
return this.associativeLineDraw.marker(20, 20, add => {
add.ref(12, 5)
add.size(10, 10)
add.attr('orient', 'auto-start-reverse')
@@ -142,7 +142,7 @@ class AssociativeLine {
null,
cur => {
if (!cur) return
let data = cur.nodeData.data
let data = cur.getData()
if (
data.associativeLineTargets &&
data.associativeLineTargets.length > 0
@@ -161,7 +161,7 @@ class AssociativeLine {
ids.forEach((uid, index) => {
let toNode = idToNode.get(uid)
if (!node || !toNode) return
const associativeLinePoint = (node.nodeData.data.associativeLinePoint ||
const associativeLinePoint = (node.getData('associativeLinePoint') ||
[])[index]
// 切换结构和布局,都会更新坐标
const [startPoint, endPoint] = this.updateAllLinesPos(
@@ -194,7 +194,7 @@ class AssociativeLine {
toNode
)
// 虚线
let path = this.draw.path()
let path = this.associativeLineDraw.path()
path
.stroke({
width: associativeLineWidth,
@@ -205,7 +205,7 @@ class AssociativeLine {
path.plot(pathStr)
path.marker('end', this.marker)
// 不可见的点击线
let clickPath = this.draw.path()
let clickPath = this.associativeLineDraw.path()
clickPath
.stroke({ width: associativeLineActiveWidth, color: 'transparent' })
.fill({ color: 'none' })
@@ -276,6 +276,7 @@ class AssociativeLine {
controlPoints[1]
)
this.mindMap.emit('associative_line_click', path, clickPath, node, toNode)
this.front()
}
// 移除所有连接线
@@ -300,9 +301,10 @@ class AssociativeLine {
let { associativeLineWidth, associativeLineColor } =
this.mindMap.themeConfig
if (this.isCreatingLine || !fromNode) return
this.front()
this.isCreatingLine = true
this.creatingStartNode = fromNode
this.creatingLine = this.draw.path()
this.creatingLine = this.associativeLineDraw.path()
this.creatingLine
.stroke({
width: associativeLineWidth,
@@ -357,12 +359,13 @@ class AssociativeLine {
height
}
}
// 检测当前移动到的目标节点
checkOverlapNode(x, y) {
this.overlapNode = null
bfsWalk(this.mindMap.renderer.root, node => {
if (node.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(node, false)
if (node.getData('isActive')) {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
}
if (node.uid === this.creatingStartNode.uid || this.overlapNode) {
return
@@ -374,8 +377,8 @@ class AssociativeLine {
this.overlapNode = node
}
})
if (this.overlapNode && !this.overlapNode.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
if (this.overlapNode && !this.overlapNode.getData('isActive')) {
this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, true)
}
}
@@ -383,21 +386,22 @@ class AssociativeLine {
completeCreateLine(node) {
if (this.creatingStartNode.uid === node.uid) return
this.addLine(this.creatingStartNode, node)
if (this.overlapNode && this.overlapNode.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
if (this.overlapNode && this.overlapNode.getData('isActive')) {
this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, false)
}
this.isCreatingLine = false
this.creatingStartNode = null
this.creatingLine.remove()
this.creatingLine = null
this.overlapNode = null
this.back()
}
// 添加连接线
addLine(fromNode, toNode) {
if (!fromNode || !toNode) return
// 目标节点如果没有id则生成一个id
let uid = toNode.nodeData.data.uid
let uid = toNode.getData('uid')
if (!uid) {
uid = uuid()
this.mindMap.execCommand('SET_NODE_DATA', toNode, {
@@ -405,7 +409,7 @@ class AssociativeLine {
})
}
// 将目标节点id保存起来
let list = fromNode.nodeData.data.associativeLineTargets || []
let list = fromNode.getData('associativeLineTargets') || []
// 连线节点是否存在相同的id,存在则阻止添加关联线
const sameLine = list.some(item => item === uid)
if (sameLine) {
@@ -421,7 +425,7 @@ class AssociativeLine {
endPoint.y
)
let offsetList =
fromNode.nodeData.data.associativeLineTargetControlOffsets || []
fromNode.getData('associativeLineTargetControlOffsets') || []
// 保存的实际是控制点和端点的差值,否则当节点位置改变了,控制点还是原来的位置,连线就不对了
offsetList[list.length - 1] = [
{
@@ -433,7 +437,7 @@ class AssociativeLine {
y: controlPoints[1].y - endPoint.y
}
]
let associativeLinePoint = fromNode.nodeData.data.associativeLinePoint || []
let associativeLinePoint = fromNode.getData('associativeLinePoint') || []
// 记录关联的起始|结束坐标
associativeLinePoint[list.length - 1] = { startPoint, endPoint }
this.mindMap.execCommand('SET_NODE_DATA', fromNode, {
@@ -453,14 +457,14 @@ class AssociativeLine {
associativeLinePoint,
associativeLineTargetControlOffsets,
associativeLineText
} = node.nodeData.data
} = node.getData()
associativeLinePoint = associativeLinePoint || []
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
// 更新关联线文本数据
let newAssociativeLineText = {}
if (associativeLineText) {
Object.keys(associativeLineText).forEach(item => {
if (item !== toNode.nodeData.data.uid) {
if (item !== toNode.getData('uid')) {
newAssociativeLineText[item] = associativeLineText[item]
}
})
@@ -500,6 +504,7 @@ class AssociativeLine {
}
this.activeLine = null
this.removeControls()
this.back()
}
}
@@ -526,6 +531,19 @@ class AssociativeLine {
this.showControls()
this.isNodeDragging = false
}
// 关联线顶层显示
front() {
if (this.mindMap.opt.associativeLineIsAlwaysAboveNode) return
this.associativeLineDraw.front()
}
// 关联线回到原有层级
back() {
if (this.mindMap.opt.associativeLineIsAlwaysAboveNode) return
this.associativeLineDraw.back() // 最底层
this.associativeLineDraw.forward() // 连线层上面
}
}
AssociativeLine.instanceName = 'associativeLine'

View File

@@ -16,6 +16,7 @@ class Cooperate {
// 感知数据
this.awareness = null
this.currentAwarenessData = []
this.waitNodeUidMap = {} // 该列表中的uid对应的节点还未渲染完毕
// 当前的平级对象类型的思维导图数据
this.currentData = null
// 用户信息
@@ -79,6 +80,10 @@ class Cooperate {
this.onNodeActive = this.onNodeActive.bind(this)
this.mindMap.on('node_active', this.onNodeActive)
// 监听思维导图渲染完毕事件
this.onNodeTreeRenderEnd = this.onNodeTreeRenderEnd.bind(this)
this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd)
// 监听设置思维导图数据事件
this.initData = this.initData.bind(this)
this.mindMap.on('set_data', this.initData)
@@ -91,6 +96,7 @@ class Cooperate {
}
this.mindMap.off('data_change', this.onDataChange)
this.mindMap.off('node_active', this.onNodeActive)
this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd)
this.mindMap.off('set_data', this.initData)
this.ydoc.destroy()
}
@@ -153,6 +159,17 @@ class Cooperate {
}
}
// 节点树渲染完毕事件
onNodeTreeRenderEnd() {
Object.keys(this.waitNodeUidMap).forEach(uid => {
const node = this.mindMap.renderer.findNodeByUid(uid)
if (node) {
node.addUser(this.waitNodeUidMap[uid])
}
})
this.waitNodeUidMap = {}
}
// 设置用户信息
/**
* {
@@ -183,23 +200,27 @@ class Cooperate {
const nodeIdList = data.nodeIdList
nodeIdList.forEach(uid => {
const node = this.mindMap.renderer.findNodeByUid(uid)
if (node) {
callback(node, userInfo)
}
callback(uid, node, userInfo)
})
})
}
// 清除之前的数据
walk(this.currentAwarenessData, (node, userInfo) => {
node.removeUser(userInfo)
walk(this.currentAwarenessData, (uid, node, userInfo) => {
if (node) {
node.removeUser(userInfo)
}
})
// 设置当前数据
const data = Array.from(this.awareness.getStates().values())
this.currentAwarenessData = data
walk(data, (node, userInfo) => {
walk(data, (uid, node, userInfo) => {
// 不显示自己
if (userInfo.id === this.userInfo.id) return
node.addUser(userInfo)
if (node) {
node.addUser(userInfo)
} else {
this.waitNodeUidMap[uid] = userInfo
}
})
}

View File

@@ -1,4 +1,4 @@
import { bfsWalk, throttle, getTopAncestorsFomNodeList } from '../utils'
import { bfsWalk, throttle, getTopAncestorsFomNodeList, getNodeIndexInNodeList } from '../utils'
import Base from '../layouts/Base'
// 节点拖动插件
@@ -109,13 +109,13 @@ class Drag extends Base {
})
this.removeCloneNode()
let overlapNodeUid = this.overlapNode
? this.overlapNode.nodeData.data.uid
? this.overlapNode.getData('uid')
: ''
let prevNodeUid = this.prevNode ? this.prevNode.nodeData.data.uid : ''
let nextNodeUid = this.nextNode ? this.nextNode.nodeData.data.uid : ''
let prevNodeUid = this.prevNode ? this.prevNode.getData('uid') : ''
let nextNodeUid = this.nextNode ? this.nextNode.getData('uid') : ''
// 存在重叠子节点,则移动作为其子节点
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, false)
this.mindMap.execCommand(
'MOVE_NODE_TO',
this.beingDragNodeList,
@@ -123,7 +123,7 @@ class Drag extends Base {
)
} else if (this.prevNode) {
// 存在前一个相邻节点,作为其下一个兄弟节点
this.mindMap.renderer.setNodeActive(this.prevNode, false)
this.mindMap.execCommand('SET_NODE_ACTIVE', this.prevNode, false)
this.mindMap.execCommand(
'INSERT_AFTER',
this.beingDragNodeList,
@@ -131,7 +131,7 @@ class Drag extends Base {
)
} else if (this.nextNode) {
// 存在下一个相邻节点,作为其前一个兄弟节点
this.mindMap.renderer.setNodeActive(this.nextNode, false)
this.mindMap.execCommand('SET_NODE_ACTIVE', this.nextNode, false)
this.mindMap.execCommand(
'INSERT_BEFORE',
this.beingDragNodeList,
@@ -205,7 +205,7 @@ class Drag extends Base {
this.offsetX = this.mouseDownX - (node.left * scaleX + translateX)
this.offsetY = this.mouseDownY - (node.top * scaleY + translateY)
// 如果鼠标按下的节点是激活节点,那么保存当前所有激活的节点
if (node.nodeData.data.isActive) {
if (node.getData('isActive')) {
// 找出这些激活节点中的最顶层节点
this.beingDragNodeList = getTopAncestorsFomNodeList(
// 过滤掉根节点和概要节点
@@ -222,7 +222,7 @@ class Drag extends Base {
// 创建克隆节点
this.createCloneNode()
// 清除当前所有激活的节点
this.mindMap.renderer.clearAllActive()
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
}
}
@@ -261,7 +261,7 @@ class Drag extends Base {
const lineColor = node.style.merge('lineColor', true)
// 如果当前被拖拽的节点数量大于1那么创建一个矩形示意
if (this.beingDragNodeList.length > 1) {
this.clone = this.draw
this.clone = this.mindMap.otherDraw
.rect()
.size(rectWidth, rectHeight)
.radius(rectHeight / 2)
@@ -278,12 +278,12 @@ class Drag extends Base {
if (expandEl) {
expandEl.remove()
}
this.mindMap.draw.add(this.clone)
this.mindMap.otherDraw.add(this.clone)
}
this.clone.opacity(dragOpacityConfig.cloneNodeOpacity)
this.clone.css('z-index', 99999)
// 同级位置提示元素
this.placeholder = this.draw.rect().fill({
this.placeholder = this.mindMap.otherDraw.rect().fill({
color: dragPlaceholderRectFill || lineColor
})
// 当前被拖拽的节点的临时设置
@@ -317,8 +317,8 @@ class Drag extends Base {
this.nextNode = null
this.placeholder.size(0, 0)
this.nodeList.forEach(node => {
if (node.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(node, false)
if (node.getData('isActive')) {
this.mindMap.execCommand('SET_NODE_ACTIVE', node, false)
}
if (this.overlapNode || (this.prevNode && this.nextNode)) {
return
@@ -353,7 +353,7 @@ class Drag extends Base {
}
})
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, true)
}
}
@@ -487,9 +487,7 @@ class Drag extends Base {
getNodeDistanceToSiblingNode(checkList, node, nodeRect, dir) {
let dir1 = dir === 'v' ? 'top' : 'left'
let dir2 = dir === 'v' ? 'bottom' : 'right'
let index = checkList.findIndex(item => {
return item.uid === node.uid
})
let index = getNodeIndexInNodeList(node, checkList)
let prevBrother = null
let nextBrother = null
if (index !== -1) {

View File

@@ -1,7 +1,8 @@
import {
isWhite,
isTransparent,
getVisibleColorFromTheme
getVisibleColorFromTheme,
readBlob
} from '../utils/index'
// 小地图插件
@@ -27,7 +28,9 @@ class MiniMap {
*/
calculationMiniMap(boxWidth, boxHeight) {
let { svg, rect, origWidth, origHeight, scaleX, scaleY } =
this.mindMap.getSvgData()
this.mindMap.getSvgData({
ignoreWatermark: true
})
// 计算数据
const elRect = this.mindMap.elRect
rect.x -= elRect.left
@@ -85,10 +88,18 @@ class MiniMap {
Object.keys(viewBoxStyle).forEach(key => {
viewBoxStyle[key] = viewBoxStyle[key] + 'px'
})
this.removeNodeContent(svg)
const svgStr = svg.svg()
return {
svgHTML: svg.svg(), // 小地图html
getImgUrl: async callback => {
const blob = new Blob([svgStr], {
type: 'image/svg+xml'
})
const res = await readBlob(blob)
callback(res)
},
svgHTML: svgStr, // 小地图html
viewBoxStyle, // 视图框的位置信息
miniMapBoxScale, // 视图框的缩放值
miniMapBoxLeft, // 视图框的left值

View File

@@ -205,7 +205,7 @@ class NodeImgAdjust {
// 隐藏节点实际图片
this.hideNodeImage()
// 将节点图片渲染到自定义元素上
this.handleEl.style.backgroundImage = `url(${this.node.nodeData.data.image})`
this.handleEl.style.backgroundImage = `url(${this.node.getData('image')})`
}
// 鼠标移动
@@ -214,7 +214,7 @@ class NodeImgAdjust {
e.preventDefault()
// 计算当前拖拽位置对应的图片的实时大小
let { width: imageOriginWidth, height: imageOriginHeight } =
this.node.nodeData.data.imageSize
this.node.getData('imageSize')
let newWidth = e.clientX - this.rect.x
let newHeight = e.clientY - this.rect.y
if (newWidth <= 0 || newHeight <= 0) return
@@ -237,7 +237,7 @@ class NodeImgAdjust {
// 隐藏自定义元素
this.hideHandleEl()
// 更新节点图片为新的大小
let { image, imageTitle } = this.node.nodeData.data
let { image, imageTitle } = this.node.getData()
let { scaleX, scaleY } = this.mindMap.draw.transform()
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, {
url: image,

View File

@@ -53,7 +53,7 @@ class Painter {
)
return
const style = {}
const painterNodeData = this.painterNode.nodeData.data
const painterNodeData = this.painterNode.getData()
Object.keys(painterNodeData).forEach(key => {
if (checkIsNodeStyleDataKey(key)) {
style[key] = painterNodeData[key]

View File

@@ -236,17 +236,17 @@ class RichText {
this.textEditNode.style.borderRadius = (node.height || 50) + 'px'
}
}
if (!node.nodeData.data.richText) {
if (!node.getData('richText')) {
// 还不是富文本的情况
let text = ''
if (!isUndef(node.nodeData.data.text)) {
text = String(node.nodeData.data.text).split(/\n/gim).join('<br>')
if (!isUndef(node.getData('text'))) {
text = String(node.getData('text')).split(/\n/gim).join('<br>')
}
let html = `<p>${text}</p>`
this.textEditNode.innerHTML = this.cacheEditingText || html
} else {
this.textEditNode.innerHTML =
this.cacheEditingText || node.nodeData.data.text
this.cacheEditingText || node.getData('text')
}
this.initQuillEditor()
document.querySelector('.ql-editor').style.minHeight = originHeight + 'px'
@@ -256,7 +256,7 @@ class RichText {
this.focus(
isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null
)
if (!node.nodeData.data.richText) {
if (!node.getData('richText')) {
// 如果是非富文本的情况,需要手动应用文本样式
this.setTextStyleIfNotRichText(node)
}
@@ -494,7 +494,7 @@ class RichText {
})
} else {
let data = this.richTextStyleToNormalStyle(config)
this.mindMap.renderer.setNodeData(this.node, data)
this.mindMap.execCommand('SET_NODE_DATA', this.node, data)
}
}
@@ -564,6 +564,16 @@ class RichText {
return data
}
// 给未激活的节点设置富文本样式
setNotActiveNodeStyle(node, style) {
const config = this.normalStyleToRichTextStyle(style)
if (Object.keys(config).length > 0) {
this.showEditText(node)
this.formatAllText(config)
this.hideEditText([node])
}
}
// 处理导出为图片
async handleExportPng(node) {
let el = document.createElement('div')

View File

@@ -73,7 +73,7 @@ class Search {
this.matchNodeList = []
this.currentIndex = -1
bfsWalk(this.mindMap.renderer.root, node => {
let { richText, text } = node.nodeData.data
let { richText, text } = node.getData()
if (richText) {
text = getTextFromHtml(text)
}
@@ -115,7 +115,7 @@ class Search {
if (!currentNode) return
let text = this.getReplacedText(currentNode, this.searchText, replaceText)
this.notResetSearchText = true
currentNode.setText(text, currentNode.nodeData.data.richText, true)
currentNode.setText(text, currentNode.getData('richText'), true)
this.matchNodeList = this.matchNodeList.filter(node => {
return currentNode !== node
})
@@ -143,7 +143,7 @@ class Search {
node,
{
text,
resetRichText: !!node.nodeData.data.richText
resetRichText: !!node.getData('richText')
},
true
)
@@ -155,7 +155,7 @@ class Search {
// 获取某个节点替换后的文本
getReplacedText(node, searchText, replaceText) {
let { richText, text } = node.nodeData.data
let { richText, text } = node.getData()
if (richText) {
return replaceHtmlText(text, searchText, replaceText)
} else {

View File

@@ -124,7 +124,7 @@ class Select {
let cur = this.cacheActiveList[i]
if (
!this.mindMap.renderer.activeNodeList.find(item => {
return item.nodeData.data.uid === cur.nodeData.data.uid
return item.getData('uid') === cur.getData('uid')
})
) {
isNodeChange = true
@@ -218,17 +218,15 @@ class Select {
if (
checkTwoRectIsOverlap(minx, maxx, miny, maxy, left, right, top, bottom)
) {
if (node.nodeData.data.isActive) {
if (node.getData('isActive')) {
return
}
this.mindMap.renderer.setNodeActive(node, true)
this.mindMap.renderer.addActiveNode(node)
} else if (node.nodeData.data.isActive) {
if (!node.nodeData.data.isActive) {
this.mindMap.renderer.addNodeToActiveList(node)
} else if (node.getData('isActive')) {
if (!node.getData('isActive')) {
return
}
this.mindMap.renderer.setNodeActive(node, false)
this.mindMap.renderer.removeActiveNode(node)
this.mindMap.renderer.removeNodeFromActiveList(node)
}
})
}

View File

@@ -11,13 +11,48 @@ class Watermark {
this.angle = 0 // 旋转角度
this.text = '' // 水印文字
this.textStyle = {} // 水印文字样式
this.watermarkDraw = null // 容器
this.maxLong = this.getMaxLong()
this.updateWatermark(this.mindMap.opt.watermarkConfig || {})
this.bindEvent()
}
getMaxLong() {
return Math.sqrt(
Math.pow(this.mindMap.width, 2) + Math.pow(this.mindMap.height, 2)
)
}
bindEvent() {
this.onResize = this.onResize.bind(this)
this.mindMap.on('resize', this.onResize)
}
unBindEvent() {
this.mindMap.off('resize', this.onResize)
}
onResize() {
this.maxLong = this.getMaxLong()
this.draw()
}
// 创建水印容器
createContainer() {
if (this.watermarkDraw) return
this.watermarkDraw = this.mindMap.svg
.group()
.css({ 'pointer-events': 'none', 'user-select': 'none' })
this.maxLong = Math.sqrt(
Math.pow(this.mindMap.width, 2) + Math.pow(this.mindMap.height, 2)
)
this.updateWatermark(this.mindMap.opt.watermarkConfig || {})
.addClass('smm-water-mark-container')
}
// 删除水印容器
removeContainer() {
if (!this.watermarkDraw) {
return
}
this.watermarkDraw.remove()
this.watermarkDraw = null
}
// 获取是否存在水印
@@ -40,10 +75,14 @@ class Watermark {
// 绘制水印
// 非精确绘制,会绘制一些超出可视区域的水印
draw() {
this.watermarkDraw.clear()
// 清空之前的水印
if (this.watermarkDraw) this.watermarkDraw.clear()
// 如果没有水印数据,那么水印容器也删除掉
if (!this.hasWatermark()) {
this.removeContainer()
return
}
this.createContainer()
let x = 0
while (x < this.mindMap.width) {
this.drawText(x)
@@ -116,6 +155,16 @@ class Watermark {
this.handleConfig(config)
this.draw()
}
// 插件被移除前做的事情
beforePluginRemove() {
this.unBindEvent()
}
// 插件被卸载前做的事情
beforePluginDestroy() {
this.unBindEvent()
}
}
Watermark.instanceName = 'watermark'

View File

@@ -9,10 +9,10 @@ import {
function createControlNodes() {
let { associativeLineActiveColor } = this.mindMap.themeConfig
// 连线
this.controlLine1 = this.draw
this.controlLine1 = this.associativeLineDraw
.line()
.stroke({ color: associativeLineActiveColor, width: 2 })
this.controlLine2 = this.draw
this.controlLine2 = this.associativeLineDraw
.line()
.stroke({ color: associativeLineActiveColor, width: 2 })
// 控制点
@@ -23,7 +23,7 @@ function createControlNodes() {
// 创建控制点
function createOneControlNode(pointKey) {
let { associativeLineActiveColor } = this.mindMap.themeConfig
return this.draw
return this.associativeLineDraw
.circle(this.controlPointDiameter)
.stroke({ color: associativeLineActiveColor })
.fill({ color: '#fff' })
@@ -64,7 +64,7 @@ function onControlPointMousemove(e) {
let [, , , node, toNode] = this.activeLine
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
let { associativeLinePoint, associativeLineTargetControlOffsets } =
node.nodeData.data
node.getData()
associativeLinePoint = associativeLinePoint || []
const nodePos = this.getNodePos(node)
const toNodePos = this.getNodePos(toNode)
@@ -160,7 +160,7 @@ function onControlPointMouseup(e) {
let [, , , node] = this.activeLine
let offsetList = []
let { associativeLinePoint, associativeLineTargetControlOffsets } =
node.nodeData.data
node.getData()
if (!associativeLinePoint) {
associativeLinePoint = []
}

View File

@@ -7,7 +7,7 @@ import {
// 创建文字节点
function createText(data) {
let g = this.draw.group()
let g = this.associativeLineDraw.group()
const setActive = () => {
if (
!this.activeLine ||
@@ -110,8 +110,8 @@ function hideEditTextBox() {
str = isDefaultText ? '' : str
this.mindMap.execCommand('SET_NODE_DATA', node, {
associativeLineText: {
...(node.nodeData.data.associativeLineText || {}),
[toNode.nodeData.data.uid]: str
...(node.getData('associativeLineText') || {}),
[toNode.getData('uid')]: str
}
})
this.textEditNode.style.display = 'none'
@@ -123,11 +123,11 @@ function hideEditTextBox() {
// 获取某根关联线的文字
function getText(node, toNode) {
let obj = node.nodeData.data.associativeLineText
let obj = node.getData('associativeLineText')
if (!obj) {
return ''
}
return obj[toNode.nodeData.data.uid] || ''
return obj[toNode.getData('uid')] || ''
}
// 渲染关联线文字

View File

@@ -1,7 +1,7 @@
// 获取目标节点在起始节点的目标数组中的索引
export const getAssociativeLineTargetIndex = (node, toNode) => {
return node.nodeData.data.associativeLineTargets.findIndex(item => {
return item === toNode.nodeData.data.uid
return node.getData('associativeLineTargets').findIndex(item => {
return item === toNode.getData('uid')
})
}
@@ -202,7 +202,7 @@ export const computeNodePoints = (fromNode, toNode) => {
// 中心点坐标的差值
let offsetX = toCx - fromCx
let offsetY = toCy - fromCy
if (offsetX === 0 && offsetY === 0) return
if (offsetX === 0 && offsetY === 0) return []
let fromDir = ''
let toDir = ''
if (offsetX <= 0 && offsetX <= offsetY && offsetX <= -offsetY) {
@@ -231,7 +231,7 @@ export const getNodeLinePath = (startPoint, endPoint, node, toNode) => {
// 控制点
let controlPoints = []
let associativeLineTargetControlOffsets =
node.nodeData.data.associativeLineTargetControlOffsets
node.getData('associativeLineTargetControlOffsets')
if (
associativeLineTargetControlOffsets &&
associativeLineTargetControlOffsets[targetIndex]

View File

@@ -173,7 +173,8 @@ export const copyNodeTree = (
// 移除节点uid
if (removeId) {
delete tree.data.uid
} else if (!tree.data.uid) {// 否则保留或生成
} else if (!tree.data.uid) {
// 否则保留或生成
tree.data.uid = createUid()
}
if (removeActiveState) {
@@ -795,14 +796,21 @@ export const formatDataToArray = data => {
}
// 获取节点在同级里的位置索引
export const getNodeIndex = node => {
export const getNodeDataIndex = node => {
return node.parent
? node.parent.children.findIndex(item => {
return item.uid === node.uid
? node.parent.nodeData.children.findIndex(item => {
return item.data.uid === node.uid
})
: 0
}
// 从一个节点列表里找出某个节点的索引
export const getNodeIndexInNodeList = (node, nodeList) => {
return nodeList.findIndex(item => {
return item.uid === node.uid
})
}
// 根据内容生成颜色
export const generateColorByContent = str => {
let hash = 0
@@ -874,3 +882,42 @@ export const isSameObject = (a, b) => {
return a === b
}
}
// 将数据设置到用户剪切板中
export const setDataToClipboard = data => {
if (navigator.clipboard) {
navigator.clipboard.writeText(JSON.stringify(data))
}
}
// 从用户剪贴板中读取文字和图片
export const getDataFromClipboard = async () => {
let text = null
let img = null
if (navigator.clipboard) {
text = await navigator.clipboard.readText()
const items = await navigator.clipboard.read()
if (items && items.length > 0) {
for (const clipboardItem of items) {
for (const type of clipboardItem.types) {
if (/^image\//.test(type)) {
img = await clipboardItem.getType(type)
break
}
}
}
}
}
return {
text,
img
}
}
// 从节点的父节点的nodeData.children列表中移除该节点的数据
export const removeFromParentNodeData = node => {
if (!node || !node.parent) return
const index = getNodeDataIndex(node)
if (index === -1) return
node.parent.nodeData.children.splice(index, 1)
}

View File

@@ -76,7 +76,7 @@ declare class MindMap {
disableMouseWheelZoom: boolean;
errorHandler: (code: any, error: any) => void;
resetCss: string;
enableDblclickReset: boolean;
enableDblclickBackToRootNode: boolean;
minExportImgCanvasScale: number;
hoverRectColor: string;
hoverRectPadding: number;
@@ -99,15 +99,14 @@ declare class MindMap {
avatarSize: number;
fontSize: number;
};
associativeLineIsAlwaysAboveNode: boolean;
defaultGeneralizationText: string;
handleIsSplitByWrapOnPasteCreateNewNode: any;
addHistoryTime: number;
});
opt: any;
el: any;
elRect: any;
width: any;
height: any;
cssEl: HTMLStyleElement;
svg: any;
draw: any;
event: Event;
keyCommand: KeyCommand;
command: Command;
@@ -115,10 +114,22 @@ declare class MindMap {
view: View;
batchExecution: BatchExecution;
handleOpt(opt: any): any;
initContainer(): void;
associativeLineDraw: any;
svg: any;
draw: any;
lineDraw: any;
nodeDraw: any;
otherDraw: any;
clearDraw(): void;
addCss(): void;
removeCss(): void;
render(callback: any, source?: string): void;
reRender(callback: any, source?: string): void;
getElRectInfo(): void;
elRect: any;
width: any;
height: any;
resize(): void;
on(event: any, fn: any): void;
emit(event: any, ...args: any[]): void;
@@ -126,15 +137,15 @@ declare class MindMap {
initCache(): void;
initTheme(): void;
themeConfig: any;
setTheme(theme: any): void;
setTheme(theme: any, notRender?: boolean): void;
getTheme(): any;
setThemeConfig(config: any): void;
setThemeConfig(config: any, notRender?: boolean): void;
getCustomThemeConfig(): any;
getThemeConfig(prop: any): any;
getConfig(prop: any): any;
updateConfig(opt?: {}): void;
getLayout(): any;
setLayout(layout: any): void;
setLayout(layout: any, notRender?: boolean): void;
execCommand(...args: any[]): void;
setData(data: any): void;
setFullData(data: any): void;
@@ -145,9 +156,10 @@ declare class MindMap {
y: number;
};
setMode(mode: any): void;
getSvgData({ paddingX, paddingY }?: {
getSvgData({ paddingX, paddingY, ignoreWatermark }?: {
paddingX?: number;
paddingY?: number;
ignoreWatermark?: boolean;
}): {
svg: any;
svgHTML: any;

View File

@@ -72,7 +72,7 @@ export namespace defaultOpt {
const disableMouseWheelZoom: boolean;
function errorHandler(code: any, error: any): void;
const resetCss: string;
const enableDblclickReset: boolean;
const enableDblclickBackToRootNode: boolean;
const minExportImgCanvasScale: number;
const hoverRectColor: string;
const hoverRectPadding: number;
@@ -97,4 +97,8 @@ export namespace defaultOpt {
const fontSize_2: number;
export { fontSize_2 as fontSize };
}
const associativeLineIsAlwaysAboveNode: boolean;
const defaultGeneralizationText: string;
const handleIsSplitByWrapOnPasteCreateNewNode: any;
const addHistoryTime: number;
}

View File

@@ -4,11 +4,11 @@ declare class Render {
opt: {};
mindMap: any;
themeConfig: any;
draw: any;
renderTree: any;
reRender: boolean;
isRendering: boolean;
hasWaitRendering: boolean;
waitRenderingParams: any[];
nodeCache: {};
lastNodeCache: {};
renderSource: string;
@@ -32,18 +32,20 @@ declare class Render {
insertMultiNode(appointNodes: any, nodeList: any): void;
insertChildNode(openEdit?: boolean, appointNodes?: any[], appointData?: any, appointChildren?: any[]): void;
insertMultiChildNode(appointNodes: any, childList: any): void;
insertParentNode(openEdit: boolean, appointNodes: any, appointData: any): void;
upNode(): void;
downNode(): void;
insertAfter(node: any, exist: any): void;
insertBefore(node: any, exist: any): void;
moveNodeTo(node: any, toNode: any): void;
removeNode(appointNodes?: any[]): void;
removeCurrentNode(appointNodes?: any[]): void;
pasteNode(data: any): void;
cutNode(callback: any): void;
setNodeStyle(node: any, prop: any, value: any): void;
setNodeStyles(node: any, style: any): void;
setNodeActive(node: any, active: any): void;
clearAllActive(): void;
clearActiveNode(): void;
setNodeExpand(node: any, expand: any): void;
expandAllNode(): void;
unexpandAllNode(): void;
@@ -63,30 +65,31 @@ declare class Render {
setNodeShape(node: any, shape: any): void;
goTargetNode(node: any, callback?: () => void): void;
registerShortcutKeys(): void;
insertNodeWrap: () => void;
toggleActiveExpand(): void;
removeNodeWrap: () => void;
copy(): void;
cut(): void;
clearActiveNodeListOnDrawClick(e: any, eventType: any): void;
startTextEdit(): void;
endTextEdit(): void;
render(callback: () => void, source: any): void;
clearActive(): void;
addActiveNode(node: any): void;
removeActiveNode(node: any): void;
findActiveNodeIndex(node: any): number;
setCopyDataToClipboard(data: any): void;
clearActiveNodeList(): void;
addNodeToActiveList(node: any): void;
removeNodeFromActiveList(node: any): void;
findActiveNodeIndex(node: any): any;
backForward(type: any, step: any): void;
copy(): void;
cut(): void;
paste(): void;
onPaste(): Promise<void>;
insertTo(node: any, exist: any, dir?: string): void;
checkNodeLayerChange(node: any, toNode: any): void;
removeOneNode(node: any): void;
getNextActiveNode(): any;
copyNode(): any;
toggleNodeExpand(node: any): void;
setNodeDataRender(node: any, data: any, notRender?: boolean): void;
moveNodeToCenter(node: any): void;
setRootNodeCenter(): void;
expandToNodeUid(uid: any, callback?: () => void): void;
findNodeByUid(uid: any): any;
emitNodeActiveEvent(): void;
}
import TextEdit from "./TextEdit";
import MindMap from "../../layouts/MindMap";

View File

@@ -6,6 +6,8 @@ declare class Node {
mindMap: any;
renderer: any;
draw: any;
nodeDraw: any;
lineDraw: any;
style: Style;
shapeInstance: Shape;
shapePadding: {
@@ -84,7 +86,8 @@ declare class Node {
top: any;
};
reRender(): boolean;
updateNodeActive(): void;
updateNodeActiveClass(): void;
updateNodeByActive(active: any): void;
render(callback?: () => void): void;
remove(): void;
destroy(): void;

View File

@@ -4,6 +4,7 @@ declare class Base {
renderer: any;
mindMap: any;
draw: any;
lineDraw: any;
root: any;
lru: Lru;
doLayout(): void;

View File

@@ -64,7 +64,14 @@ export function selectAllInput(el: any): void;
export function addDataToAppointNodes(appointNodes: any, data?: {}): any;
export function createUidForAppointNodes(appointNodes: any, createNewId?: boolean): any;
export function formatDataToArray(data: any): any[];
export function getNodeIndex(node: any): any;
export function getNodeDataIndex(node: any): any;
export function getNodeIndexInNodeList(node: any, nodeList: any): any;
export function generateColorByContent(str: any): string;
export function htmlEscape(str: any): any;
export function isSameObject(a: any, b: any): boolean;
export function setDataToClipboard(data: any): void;
export function getDataFromClipboard(): Promise<{
text: string;
img: any;
}>;
export function removeFromParentNodeData(node: any): void;

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1695365666344') format('woff2'),
url('iconfont.woff?t=1695365666344') format('woff'),
url('iconfont.ttf?t=1695365666344') format('truetype');
src: url('iconfont.woff2?t=1697073602349') format('woff2'),
url('iconfont.woff?t=1697073602349') format('woff'),
url('iconfont.ttf?t=1697073602349') format('truetype');
}
.iconfont {
@@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icondodeparent:before {
content: "\e70f";
}
.icongongshi:before {
content: "\e617";
}

View File

@@ -209,6 +209,11 @@ export const shortcutKeyList = [
name: 'Insert sibling node',
value: 'Enter'
},
{
icon: 'icondodeparent',
name: 'Insert parent node',
value: 'Shift + Tab'
},
{
icon: 'iconshangyi',
name: 'Move up node',
@@ -234,6 +239,11 @@ export const shortcutKeyList = [
name: 'Delete node',
value: 'Delete | Backspace'
},
{
icon: 'iconshanchu',
name: 'Delete current node',
value: 'Shift + Backspace'
},
{
icon: 'iconfuzhi',
name: 'Copy node',
@@ -306,7 +316,7 @@ export const shortcutKeyList = [
},
{
icon: 'icondingwei',
name: 'Reset',
name: 'Back root node',
value: 'Ctrl + Enter'
},
{

View File

@@ -276,6 +276,11 @@ export const shortcutKeyList = [
name: '插入同级节点',
value: 'Enter'
},
{
icon: 'icondodeparent',
name: '插入父节点',
value: 'Shift + Tab'
},
{
icon: 'iconshangyi',
name: '上移节点',
@@ -301,6 +306,11 @@ export const shortcutKeyList = [
name: '删除节点',
value: 'Delete | Backspace'
},
{
icon: 'iconshanchu',
name: '仅删除当前节点',
value: 'Shift + Backspace'
},
{
icon: 'iconfuzhi',
name: '复制节点',
@@ -373,7 +383,7 @@ export const shortcutKeyList = [
},
{
icon: 'icondingwei',
name: '恢复默认',
name: '回到根节点',
value: 'Ctrl + Enter'
},
{

View File

@@ -59,14 +59,16 @@ export default {
contextmenu: {
insertSiblingNode: 'Insert sibling node',
insertChildNode: 'Insert child node',
insertParentNode: 'Insert parent node',
insertSummary: 'Insert summary',
moveUpNode: 'Move up node',
moveDownNode: 'Move down node',
deleteNode: 'Delete node',
deleteCurrentNode: 'Only del cur node',
copyNode: 'Copy node',
cutNode: 'Cut node',
pasteNode: 'Paste node',
backCenter: 'Back center',
backCenter: 'Back root node',
expandAll: 'Expand all',
unExpandAll: 'Un expand all',
expandTo: 'Expand to',
@@ -112,7 +114,8 @@ export default {
'If the download is not triggered, check whether it is blocked by the browser',
paddingX: 'Padding x',
paddingY: 'Padding y',
useMultiPageExport: 'Export multi page'
useMultiPageExport: 'Export multi page',
defaultFileName: 'Mind map'
},
fullscreen: {
fullscreenShow: 'Full screen show',
@@ -121,7 +124,13 @@ export default {
import: {
title: 'Import',
selectFile: 'Select file',
supportFile: 'Support .smm、.json、.xmind、.xlsx、.md file'
supportFile: 'Support .smm、.json、.xmind、.xlsx、.md file',
enableFileTip: 'Please select .smm、.json、.xmind、.xlsx、.md file',
maxFileNum: 'At most one file can be selected',
notSelectTip: 'Please select the file to import',
fileContentError: 'The file content is incorrect',
importSuccess: 'Import success',
fileParsingFailed: 'File parsing failed'
},
navigatorToolbar: {
openMiniMap: 'Open mini map',
@@ -190,13 +199,21 @@ export default {
vertical: 'Vertical'
},
theme: {
title: 'Theme'
title: 'Theme',
classics: 'Classics',
dark: 'Darkness',
simple: 'Simple',
coverTip:
'You have currently customized the basic style, do you want to overwrite it?',
tip: 'Tip',
cover: 'Cover',
reserve: 'Reserve'
},
toolbar: {
undo: 'Undo',
redo: 'Redo',
insertSiblingNode: 'Insert sibling node',
insertChildNode: 'Insert child node',
insertSiblingNode: 'Sibling node',
insertChildNode: 'Child node',
deleteNode: 'Delete node',
image: 'Image',
icon: 'Icon',
@@ -216,12 +233,28 @@ export default {
shortcutKey: 'Shortcut key',
associativeLine: 'Associative line',
painter: 'Painter',
formula: 'Formula'
formula: 'Formula',
more: 'More',
selectFileTip: 'Please select a file',
notSupportTip:
'Your browser or network protocol does not support this feature',
tip: 'Tip',
editingLocalFileTipFront: 'Currently editing your local【',
editingLocalFileTipEnd: '】file',
fileContentError: 'File content error',
fileOpenFailed: 'File open failed',
defaultFileName: 'Mind map',
creatingTip: 'Creating file'
},
edit: {
newFeatureNoticeTitle: 'New feature reminder',
newFeatureNoticeMessage:
'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.'
'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.',
root: 'Root node',
splitByWrap: 'Is automatically split nodes based on line breaks?',
tip: 'Tip',
yes: 'Yes',
no: 'No'
},
mouseAction: {
tip1:
@@ -245,6 +278,21 @@ export default {
title: 'Formula',
placeholder: 'Please enter LaText syntax',
confirm: 'Confirm',
common: 'Common formulas'
common: 'Common formulas',
tip: 'Inserting formulas is not supported in non rich text mode'
},
richTextToolbar: {
bold: 'Bold',
italic: 'Italic',
underline: 'Underline',
strike: 'Strike',
fontFamily: 'Font family',
fontSize: 'Font size',
color: 'Color',
backgroundColor: 'Background color',
removeFormat: 'Clear Style'
},
other: {
loading: 'Loading, please wait...'
}
}

View File

@@ -59,14 +59,16 @@ export default {
contextmenu: {
insertSiblingNode: '插入同级节点',
insertChildNode: '插入子级节点',
insertParentNode: '插入父节点',
insertSummary: '插入概要',
moveUpNode: '上移节点',
moveDownNode: '下移节点',
deleteNode: '删除节点',
deleteCurrentNode: '仅删除当前节点',
copyNode: '复制节点',
cutNode: '剪切节点',
pasteNode: '粘贴节点',
backCenter: '回到中心',
backCenter: '回到根节点',
expandAll: '展开所有',
unExpandAll: '收起所有',
expandTo: '展开到',
@@ -110,7 +112,8 @@ export default {
notifyMessage: '如果没有触发下载,请检查是否被浏览器拦截了',
paddingX: '水平内边距',
paddingY: '垂直内边距',
useMultiPageExport: '是否多页导出'
useMultiPageExport: '是否多页导出',
defaultFileName: '思维导图'
},
fullscreen: {
fullscreenShow: '全屏查看',
@@ -119,7 +122,13 @@ export default {
import: {
title: '导入',
selectFile: '选取文件',
supportFile: '支持.smm、.json、.xmind、.xlsx、.md文件'
supportFile: '支持.smm、.json、.xmind、.xlsx、.md文件',
enableFileTip: '请选择.smm、.json、.xmind、.xlsx、.md文件',
maxFileNum: '最多只能选择一个文件',
notSelectTip: '请选择要导入的文件',
fileContentError: '文件内容有误',
importSuccess: '导入成功',
fileParsingFailed: '文件解析失败'
},
navigatorToolbar: {
openMiniMap: '开启小地图',
@@ -188,13 +197,20 @@ export default {
vertical: '垂直'
},
theme: {
title: '主题'
title: '主题',
classics: '经典',
dark: '深色',
simple: '朴素',
coverTip: '你当前自定义过基础样式,是否覆盖?',
tip: '提示',
cover: '覆盖',
reserve: '保留'
},
toolbar: {
undo: '回退',
redo: '前进',
insertSiblingNode: '插入同级节点',
insertChildNode: '插入子节点',
insertSiblingNode: '同级节点',
insertChildNode: '子节点',
deleteNode: '删除节点',
image: '图片',
icon: '图标',
@@ -214,12 +230,27 @@ export default {
shortcutKey: '快捷键',
associativeLine: '关联线',
painter: '格式刷',
formula: '公式'
formula: '公式',
more: '更多',
selectFileTip: '请选择文件',
notSupportTip: '你的浏览器或网络协议不支持该功能',
tip: '提示',
editingLocalFileTipFront: '当前正在编辑你本机的【',
editingLocalFileTipEnd: '】文件',
fileContentError: '文件内容有误',
fileOpenFailed: '文件打开失败',
defaultFileName: '思维导图',
creatingTip: '正在创建文件'
},
edit: {
newFeatureNoticeTitle: '新特性提醒',
newFeatureNoticeMessage:
'本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。'
'本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。',
root: '根节点',
splitByWrap: '是否按换行自动分割节点?',
tip: '提示',
yes: '是',
no: '否'
},
mouseAction: {
tip1: '当前:左键拖动画布,右键框选节点',
@@ -241,6 +272,21 @@ export default {
title: '公式',
placeholder: '请输入 LaText 语法',
confirm: '完成',
common: '常用公式'
common: '常用公式',
tip: '非富文本模式下不支持插入公式'
},
richTextToolbar: {
bold: '加粗',
italic: '斜体',
underline: '下划线',
strike: '删除线',
fontFamily: '字体',
fontSize: '字号',
color: '字体颜色',
backgroundColor: '背景颜色',
removeFormat: '清除样式'
},
other: {
loading: '正在加载,请稍后...'
}
}

View File

@@ -88,3 +88,15 @@ Deletes the currently active associative line. Clicking on an associated line is
### clearActiveLine()
Clears the active state of the currently active association line.
### front()
> v0.8.0+
The top-level display of the associated line.
### back()
> v0.8.0+
The associated line returns to its original level.

View File

@@ -66,6 +66,16 @@ MindMap.usePlugin(AssociativeLine)
<p>Deletes the currently active associative line. Clicking on an associated line is considered active.</p>
<h3>clearActiveLine()</h3>
<p>Clears the active state of the currently active association line.</p>
<h3>front()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>The top-level display of the associated line.</p>
<h3>back()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>The associated line returns to its original level.</p>
</div>
</template>

View File

@@ -1,5 +1,65 @@
# Changelog
## 0.8.0
Breaking change: Greatly optimize some of the code and slightly improve performance, mainly by using the 'render' class to remove useless logic, adjust unreasonable implementations, and extract duplicate code; Modify function names, functions, etc.
Fix:
> 1.Fix the issue of the arrow of the associated line disappearing when exporting images and SVGs.
>
> 2.Fix the issue of abnormal operation returning to the root node after resizing the container.
>
> 3.Fix that the shortcut key operations for inserting summary, moving up, down, and organizing layout with one click did not trigger data_ The issue with the change event.
>
> 4.Fix the issue of each node displaying a border when exporting images, SVGs, and PDFs with watermarks.
>
> 5.Fixed the issue of no watermarks and no redrawing after the container size was changed.
>
> 6.Fix the issue of slow rendering of mini maps with watermarks.
>
> 7.Fixed the issue where the collaboration plugin did not display the creator's avatar when creating a new node.
New:
> 1.Optimize the canvas DOM structure and render nodes, lines, and associated lines in layers.
>
> 2.Optimize the watermark plugin.
>
> 3.The setTheme, setThemeConfig, and setLayout functions add parameters that do not trigger re rendering.
>
> 4.Add a command to insert a parent node.
>
> 5.Add a command to only delete the current node.
>
> 6.Automatically expand child nodes when inserting a summary.
>
> 7.Clear the current active node when right-clicking on the canvas.
>
> 8.The folded active nodes are synchronously deleted from the list of active nodes.
>
> 9.Pasting text with line breaks supports controlling whether nodes are split by line breaks.
>
> 10.The mini map plugin supports returning mini maps of image types.
>
> 11.Only one historical record can be added within a specified time period to avoid adding unnecessary intermediate states.
Demo:
> 1.Modify the method and copy to return to the root node.
>
> 2.Fix the issue of ineffective first switching when switching themes in overlay mode.
>
> 3.The right-click menu adds the function of inserting parent nodes and deleting only the current node.
>
> 4.The top toolbar supports automatic folding into more according to the window width.
>
> 5.Support manual input of zoom factor.
>
> 6.Improve the English translation of the interface.
>
> 7.Change the mini map to render through images.
## 0.7.3-fix.2
Fix some issues with collaborative editing:

View File

@@ -1,6 +1,42 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.8.0</h2>
<p>Breaking change: Greatly optimize some of the code and slightly improve performance, mainly by using the 'render' class to remove useless logic, adjust unreasonable implementations, and extract duplicate code; Modify function names, functions, etc.</p>
<p>Fix:</p>
<blockquote>
<p>1.Fix the issue of the arrow of the associated line disappearing when exporting images and SVGs.</p>
<p>2.Fix the issue of abnormal operation returning to the root node after resizing the container.</p>
<p>3.Fix that the shortcut key operations for inserting summary, moving up, down, and organizing layout with one click did not trigger data_ The issue with the change event.</p>
<p>4.Fix the issue of each node displaying a border when exporting images, SVGs, and PDFs with watermarks.</p>
<p>5.Fixed the issue of no watermarks and no redrawing after the container size was changed.</p>
<p>6.Fix the issue of slow rendering of mini maps with watermarks.</p>
<p>7.Fixed the issue where the collaboration plugin did not display the creator's avatar when creating a new node.</p>
</blockquote>
<p>New:</p>
<blockquote>
<p>1.Optimize the canvas DOM structure and render nodes, lines, and associated lines in layers.</p>
<p>2.Optimize the watermark plugin.</p>
<p>3.The setTheme, setThemeConfig, and setLayout functions add parameters that do not trigger re rendering.</p>
<p>4.Add a command to insert a parent node.</p>
<p>5.Add a command to only delete the current node.</p>
<p>6.Automatically expand child nodes when inserting a summary.</p>
<p>7.Clear the current active node when right-clicking on the canvas.</p>
<p>8.The folded active nodes are synchronously deleted from the list of active nodes.</p>
<p>9.Pasting text with line breaks supports controlling whether nodes are split by line breaks.</p>
<p>10.The mini map plugin supports returning mini maps of image types.</p>
<p>11.Only one historical record can be added within a specified time period to avoid adding unnecessary intermediate states.</p>
</blockquote>
<p>Demo:</p>
<blockquote>
<p>1.Modify the method and copy to return to the root node.</p>
<p>2.Fix the issue of ineffective first switching when switching themes in overlay mode.</p>
<p>3.The right-click menu adds the function of inserting parent nodes and deleting only the current node.</p>
<p>4.The top toolbar supports automatic folding into more according to the window width.</p>
<p>5.Support manual input of zoom factor.</p>
<p>6.Improve the English translation of the interface.</p>
<p>7.Change the mini map to render through images.</p>
</blockquote>
<h2>0.7.3-fix.2</h2>
<p>Fix some issues with collaborative editing:</p>
<p>1.The position of the new node is incorrect when inserting peer nodes;</p>

View File

@@ -39,7 +39,7 @@ const mindMap = new MindMap({
| selectTranslateLimit | Number | 20 | The distance from the edge when the canvas begins to offset during multi-select node | |
| customNoteContentShowv0.1.6+ | Object | null | Custom node note content display, object type, structure: {show: (noteContent, left, top) => {// your display node note logic }, hide: () => {// your hide node note logic }} | |
| readonlyv0.1.7+ | Boolean | false | Whether it is read-only mode | |
| enableFreeDragv0.2.4+ | Boolean | false | Enable node free drag | |
| enableFreeDragv0.2.4+ | Boolean | false | Enable node free(Free drag means that nodes can be dragged to any position on the canvas. Please note that it is not a function of dragging nodes to become siblings of other nodes. The connection of free drag may have certain problems, so it is best not to use this feature) drag | |
| 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 | |
@@ -82,7 +82,8 @@ const mindMap = new MindMap({
| errorHandlerv0.6.15+ | Function | | Custom error handling functions currently only throw some asynchronous logic errors. Can pass a function that takes two parameters, the first being the wrong type and the second being the wrong object | |
| disableMouseWheelZoomv0.6.15+ | Boolean | false | Prohibit mouse wheel scaling, you can still use the API for scaling | |
| resetCssv0.6.16+ | String | * { margin: 0; padding: 0; box-sizing: border-box; } | When exporting images and SVGs, the default style overlay for rich text node content, which is embedded in HTML nodes in SVGs, will occur. If not overlaid, the node content will be offset | |
| enableDblclickResetv0.6.17+ | Boolean | true(v0.7.0+changed to false) | Turn on the mouse and double-click to reset the position and zoom of the mind map | |
| enableDblclickResetv0.6.17+(v0.8.0+this attribute has been deleted) | Boolean | true(v0.7.0+changed to false) | Turn on the mouse and double-click to reset the position and zoom of the mind map | |
| enableDblclickBackToRootNodev0.8.0+ | Boolean | false | Whether to return to the root node when double clicking with the mouse, that is, to center the display of the root node | |
| minExportImgCanvasScalev0.7.0+ | Number | 2 | The scaling factor of canvas when exporting images and PDFs, which is set to the maximum value of window.devicePixelRatio to improve image clarity | |
| hoverRectColorv0.7.0+ | String | rgb(94, 200, 248) | The node mouse hover and the rectangular border color displayed when activated will add a transparency of 0.6 when hovering | |
| hoverRectPaddingv0.7.0+ | Number | 2 | The distance between the node mouse hover and the displayed rectangular border when activated and the node content | |
@@ -95,6 +96,10 @@ const mindMap = new MindMap({
| dragOpacityConfigv0.7.2+ | Object | { cloneNodeOpacity: 0.5, beingDragNodeOpacity: 0.3 } | The transparency configuration during node dragging, passing an object, and the field meanings are: the transparency of the cloned node or rectangle that follows the mouse movement, and the transparency of the dragged node | |
| tagsColorMapv0.7.2+ | Object | {} | The color of a custom node label can be transferred to an object, where key is the label content to be assigned a color, and value is the color of the label content. If not transferred internally, a corresponding color will be generated based on the label content | |
| cooperateStylev0.7.3+ | Object | { avatarSize: 22, fontSize: 12 } | The configuration of personnel avatar style during node collaboration editing, with field meanings as follows: avatar size, and if it is a text avatar, the size of the text | |
| associativeLineIsAlwaysAboveNodev0.8.0+ | Boolean | true | Is the associated line always displayed above the node? If set to false, it will be at the top level when creating and activating the associated line, and in other cases, it will be below the node | |
| defaultGeneralizationTextv0.8.0+ | String | 概要 | Insert default text for summary | |
| handleIsSplitByWrapOnPasteCreateNewNodev0.8.0+ | Function / null | null | When creating a new node by pasting text, control whether to automatically split the nodes based on line breaks. If there is a line break, multiple nodes will be created based on the line break. Otherwise, only one node will be created, and a function can be passed to return promise. resolve represents splitting based on line breaks, and reject represents ignoring line breaks | |
| addHistoryTimev0.8.0+ | Number | 100 | Only one historical record can be added within the specified time to avoid adding unnecessary intermediate states. Unit: ms | |
### Data structure
@@ -210,17 +215,103 @@ Get whether a plugin is registered, The index of the plugin in the registered pl
List of all currently registered plugins.
## Instance props
### el
Container element.
### opt
Config options object.
### svg
> @svgdotjs/svg.js library calls the node instance returned by the SVG() method
Canvas SVG element.
### draw
> @svgdotjs/svg.js library calls the node instance returned by the group() method
>
> Child node of SVG node
Container element, used to carry content such as nodes and connections.
### lineDraw
> v0.8.0+
>
> @svgdotjs/svg.js library calls the node instance returned by the group() method
>
> Child node of draw node
Container for node wiring elements.
### nodeDraw
> v0.8.0+
>
> @svgdotjs/svg.js library calls the node instance returned by the group() method
>
> Child node of draw node
Container for node elements.
### associativeLineDraw
> v0.8.0+
>
> @svgdotjs/svg.js library calls the node instance returned by the group() method
>
> Available when the associated line plugin is registered
>
> Child node of draw node
Container for associative line content.
### otherDraw
> v0.8.0+
>
> @svgdotjs/svg.js library calls the node instance returned by the group() method
>
> Child node of draw node
Container for other content.
### elRect
The size and position information of the container element 'el'. The return result of calling the 'getBoundingClientRect()' method.
### width
The width of the container element 'el'.
### height
The height of the container element 'el'.
### themeConfig
Current Theme Configuration.
## Instance methods
### clearDraw()
> v0.8.0+
Clear `lineDraw``associativeLineDraw``nodeDraw``otherDraw` containers.
### destroy()
> v0.6.0+
Destroy mind maps. It will remove registered plugins, remove listening events, and delete all nodes on the canvas.
### getSvgData({ paddingX = 0, paddingY = 0 })
### getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false })
> v0.3.0+
@@ -228,6 +319,8 @@ Destroy mind maps. It will remove registered plugins, remove listening events, a
`paddingY`: Padding y
`ignoreWatermark`v0.8.0+, Do not draw watermarks. If you do not need to draw watermarks, you can pass 'true' because drawing watermarks is very slow
Get the `svg` data and return an object. The detailed structure is as follows:
```js
@@ -314,6 +407,7 @@ Listen to an event. Event list:
| node_icon_clickv0.6.10+ | Triggered when clicking on an icon within a node | thisnode instance、itemClick on the icon name、eevent object |
| view_theme_changev0.6.12+ | Triggered after calling the setTheme method to set the theme | themetheme name |
| set_datav0.7.3+ | Triggered when the setData method is called to dynamically set mind map data | dataNew Mind Map Data |
| resizev0.8.0+ | Triggered after the container size changes, actually when the 'resize' method of the mind map instance is called | |
### emit(event, ...args)
@@ -323,7 +417,9 @@ Trigger an event, which can be one of the events listed above or a custom event.
Unbind an event.
### setTheme(theme)
### setTheme(theme, notRender = false)
- `notRender`: v0.8.0+, Is not call the render method to update the canvas.
Switches the theme. Available themes can be found in the options table above.
@@ -331,7 +427,9 @@ Switches the theme. Available themes can be found in the options table above.
Gets the current theme.
### setThemeConfig(config)
### setThemeConfig(config, notRender = false)
- `notRender`: v0.8.0+, Is not call the render method to update the canvas.
Sets the theme configuration. `config` is the same as the `themeConfig` option
in the options table above.
@@ -372,7 +470,9 @@ This method only updates the configuration and has no other side effects, such a
Gets the current layout structure.
### setLayout(layout)
### setLayout(layout, notRender = false)
- `notRender`: v0.8.0+, Is not call the render method to update the canvas.
Sets the layout structure. Available values can be found in the `layout` field
in the options table above.
@@ -420,6 +520,8 @@ redo. All commands are as follows:
| INSERT_MULTI_NODEv0.7.2+ | Insert multiple sibling nodes into the specified node at the same time, with the operating node being the currently active node or the specified node | appointNodesOptional, specify nodes, specify multiple nodes to pass an array, nodeListData list of newly inserted nodes, array type |
| INSERT_MULTI_CHILD_NODEv0.7.2+ | Insert multiple child nodes into the specified node simultaneously, with the operation node being the currently active node or the specified node | appointNodesOptional, specify nodes, specify multiple nodes to pass an array, childListData list of newly inserted nodes, array type |
| INSERT_FORMULAv0.7.2+ | Insert mathematical formulas into nodes, operate on the currently active node or specified node | formulaMathematical formula to insert, LaText syntax, appointNodesOptional, specify the node to insert the formula into. Multiple nodes can be passed as arrays, otherwise it defaults to the currently active node |
| INSERT_PARENT_NODEv0.8.0+ | Insert a parent node into the specified node, with the operation node being the currently active node or the specified node | openEditActivate the newly inserted node and enter editing mode, default to 'true'`)、 appointNodesOptional, specify the node to insert into the parent node, and specify that multiple nodes can pass an array、 appointDataOptional, specify the data for the newly created node, such as {text: 'xxx', ...}, Detailed structure can be referenced [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) |
| REMOVE_CURRENT_NODEv0.8.0+ | Delete only the current node, operate on the currently active node or specified node | appointNodesOptional, specify the nodes to be deleted, and multiple nodes can be passed as an array |
### setData(data)

View File

@@ -137,7 +137,7 @@
<td>enableFreeDragv0.2.4+</td>
<td>Boolean</td>
<td>false</td>
<td>Enable node free drag</td>
<td>Enable node free(Free drag means that nodes can be dragged to any position on the canvas. Please note that it is not a function of dragging nodes to become siblings of other nodes. The connection of free drag may have certain problems, so it is best not to use this feature) drag</td>
<td></td>
</tr>
<tr>
@@ -435,13 +435,20 @@
<td></td>
</tr>
<tr>
<td>enableDblclickResetv0.6.17+</td>
<td>enableDblclickResetv0.6.17+(v0.8.0+this attribute has been deleted)</td>
<td>Boolean</td>
<td>true(v0.7.0+changed to false)</td>
<td>Turn on the mouse and double-click to reset the position and zoom of the mind map</td>
<td></td>
</tr>
<tr>
<td>enableDblclickBackToRootNodev0.8.0+</td>
<td>Boolean</td>
<td>false</td>
<td>Whether to return to the root node when double clicking with the mouse, that is, to center the display of the root node</td>
<td></td>
</tr>
<tr>
<td>minExportImgCanvasScalev0.7.0+</td>
<td>Number</td>
<td>2</td>
@@ -525,6 +532,34 @@
<td>The configuration of personnel avatar style during node collaboration editing, with field meanings as follows: avatar size, and if it is a text avatar, the size of the text</td>
<td></td>
</tr>
<tr>
<td>associativeLineIsAlwaysAboveNodev0.8.0+</td>
<td>Boolean</td>
<td>true</td>
<td>Is the associated line always displayed above the node? If set to false, it will be at the top level when creating and activating the associated line, and in other cases, it will be below the node</td>
<td></td>
</tr>
<tr>
<td>defaultGeneralizationTextv0.8.0+</td>
<td>String</td>
<td>概要</td>
<td>Insert default text for summary</td>
<td></td>
</tr>
<tr>
<td>handleIsSplitByWrapOnPasteCreateNewNodev0.8.0+</td>
<td>Function / null</td>
<td>null</td>
<td>When creating a new node by pasting text, control whether to automatically split the nodes based on line breaks. If there is a line break, multiple nodes will be created based on the line break. Otherwise, only one node will be created, and a function can be passed to return promise. resolve represents splitting based on line breaks, and reject represents ignoring line breaks</td>
<td></td>
</tr>
<tr>
<td>addHistoryTimev0.8.0+</td>
<td>Number</td>
<td>100</td>
<td>Only one historical record can be added within the specified time to avoid adding unnecessary intermediate states. Unit: ms</td>
<td></td>
</tr>
</tbody>
</table>
<h3>Data structure</h3>
@@ -679,18 +714,77 @@ mindMap.setTheme(<span class="hljs-string">&#x27;Theme name&#x27;</span>)
<p>v0.3.0+</p>
</blockquote>
<p>List of all currently registered plugins.</p>
<h2>Instance props</h2>
<h3>el</h3>
<p>Container element.</p>
<h3>opt</h3>
<p>Config options object.</p>
<h3>svg</h3>
<blockquote>
<p>@svgdotjs/svg.js library calls the node instance returned by the SVG() method</p>
</blockquote>
<p>Canvas SVG element.</p>
<h3>draw</h3>
<blockquote>
<p>@svgdotjs/svg.js library calls the node instance returned by the group() method</p>
<p>Child node of SVG node</p>
</blockquote>
<p>Container element, used to carry content such as nodes and connections.</p>
<h3>lineDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js library calls the node instance returned by the group() method</p>
<p>Child node of draw node</p>
</blockquote>
<p>Container for node wiring elements.</p>
<h3>nodeDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js library calls the node instance returned by the group() method</p>
<p>Child node of draw node</p>
</blockquote>
<p>Container for node elements.</p>
<h3>associativeLineDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js library calls the node instance returned by the group() method</p>
<p>Available when the associated line plugin is registered</p>
<p>Child node of draw node</p>
</blockquote>
<p>Container for associative line content.</p>
<h3>otherDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js library calls the node instance returned by the group() method</p>
<p>Child node of draw node</p>
</blockquote>
<p>Container for other content.</p>
<h3>elRect</h3>
<p>The size and position information of the container element 'el'. The return result of calling the 'getBoundingClientRect()' method.</p>
<h3>width</h3>
<p>The width of the container element 'el'.</p>
<h3>height</h3>
<p>The height of the container element 'el'.</p>
<h3>themeConfig</h3>
<p>Current Theme Configuration.</p>
<h2>Instance methods</h2>
<h3>clearDraw()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Clear <code>lineDraw</code>、<code>associativeLineDraw</code>、<code>nodeDraw</code>、<code>otherDraw</code> containers.</p>
<h3>destroy()</h3>
<blockquote>
<p>v0.6.0+</p>
</blockquote>
<p>Destroy mind maps. It will remove registered plugins, remove listening events, and delete all nodes on the canvas.</p>
<h3>getSvgData({ paddingX = 0, paddingY = 0 })</h3>
<h3>getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false })</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p><code>paddingX</code>: Padding x</p>
<p><code>paddingY</code>: Padding y</p>
<p><code>ignoreWatermark</code>v0.8.0+, Do not draw watermarks. If you do not need to draw watermarks, you can pass 'true' because drawing watermarks is very slow</p>
<p>Get the <code>svg</code> data and return an object. The detailed structure is as follows:</p>
<pre class="hljs"><code>{
svg, <span class="hljs-comment">// Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)</span>
@@ -933,17 +1027,28 @@ poor performance and should be used sparingly.</p>
<td>Triggered when the setData method is called to dynamically set mind map data</td>
<td>dataNew Mind Map Data</td>
</tr>
<tr>
<td>resizev0.8.0+</td>
<td>Triggered after the container size changes, actually when the 'resize' method of the mind map instance is called</td>
<td></td>
</tr>
</tbody>
</table>
<h3>emit(event, ...args)</h3>
<p>Trigger an event, which can be one of the events listed above or a custom event.</p>
<h3>off(event, fn)</h3>
<p>Unbind an event.</p>
<h3>setTheme(theme)</h3>
<h3>setTheme(theme, notRender = false)</h3>
<ul>
<li><code>notRender</code>: v0.8.0+, Is not call the render method to update the canvas.</li>
</ul>
<p>Switches the theme. Available themes can be found in the options table above.</p>
<h3>getTheme()</h3>
<p>Gets the current theme.</p>
<h3>setThemeConfig(config)</h3>
<h3>setThemeConfig(config, notRender = false)</h3>
<ul>
<li><code>notRender</code>: v0.8.0+, Is not call the render method to update the canvas.</li>
</ul>
<p>Sets the theme configuration. <code>config</code> is the same as the <code>themeConfig</code> option
in the options table above.</p>
<h3>getCustomThemeConfig()</h3>
@@ -969,7 +1074,10 @@ in the options table above.</p>
<p>This method only updates the configuration and has no other side effects, such as triggering canvas re-rendering</p>
<h3>getLayout()</h3>
<p>Gets the current layout structure.</p>
<h3>setLayout(layout)</h3>
<h3>setLayout(layout, notRender = false)</h3>
<ul>
<li><code>notRender</code>: v0.8.0+, Is not call the render method to update the canvas.</li>
</ul>
<p>Sets the layout structure. Available values can be found in the <code>layout</code> field
in the options table above.</p>
<h3>execCommand(name, ...args)</h3>
@@ -1164,6 +1272,16 @@ redo. All commands are as follows:</p>
<td>Insert mathematical formulas into nodes, operate on the currently active node or specified node</td>
<td>formulaMathematical formula to insert, LaText syntax, appointNodesOptional, specify the node to insert the formula into. Multiple nodes can be passed as arrays, otherwise it defaults to the currently active node</td>
</tr>
<tr>
<td>INSERT_PARENT_NODEv0.8.0+</td>
<td>Insert a parent node into the specified node, with the operation node being the currently active node or the specified node</td>
<td>openEditActivate the newly inserted node and enter editing mode, default to 'true'`)、 appointNodesOptional, specify the node to insert into the parent node, and specify that multiple nodes can pass an array、 appointDataOptional, specify the data for the newly created node, such as {text: 'xxx', ...}, Detailed structure can be referenced <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js">exampleData.js</a></td>
</tr>
<tr>
<td>REMOVE_CURRENT_NODEv0.8.0+</td>
<td>Delete only the current node, operate on the currently active node or specified node</td>
<td>appointNodesOptional, specify the nodes to be deleted, and multiple nodes can be passed as an array</td>
</tr>
</tbody>
</table>
<h3>setData(data)</h3>

View File

@@ -6,6 +6,18 @@
This plugin is used to support inserting formulas into nodes.
> 注意:公式是通过[KaTeX](https://github.com/KaTeX/KaTeX)库实现的,`KaTeX`提供了一些配置,插件默认的一个配置是:
> Note: The formula is implemented through the [KaTeX](https://github.com/KaTeX/KaTeX) library, and 'KaTeX' provides some configurations. The default configuration for the plugin is:
```js
{
output: 'mathml'
}
```
> This formula may not render successfully on a few browsers. If you need to be compatible with these browsers, you can consider changing the configuration to 'HTML'. For detailed documentation, please refer to [Options](https://katex.org/docs/options). Using this configuration may require the introduction of a 'KaTeX' style file, which you can test on your own.
## Register
```js

View File

@@ -8,6 +8,19 @@
<p>This plugin is only supported in rich text mode, so it needs to be used after registering the RichText plugin</p>
</blockquote>
<p>This plugin is used to support inserting formulas into nodes.</p>
<blockquote>
<p>注意公式是通过<a href="https://github.com/KaTeX/KaTeX">KaTeX</a>库实现的<code>KaTeX</code>提供了一些配置插件默认的一个配置是</p>
</blockquote>
<blockquote>
<p>Note: The formula is implemented through the <a href="https://github.com/KaTeX/KaTeX">KaTeX</a> library, and 'KaTeX' provides some configurations. The default configuration for the plugin is:</p>
</blockquote>
<pre class="hljs"><code>{
<span class="hljs-attr">output</span>: <span class="hljs-string">&#x27;mathml&#x27;</span>
}
</code></pre>
<blockquote>
<p>This formula may not render successfully on a few browsers. If you need to be compatible with these browsers, you can consider changing the configuration to 'HTML'. For detailed documentation, please refer to <a href="https://katex.org/docs/options">Options</a>. Using this configuration may require the introduction of a 'KaTeX' style file, which you can test on your own.</p>
</blockquote>
<h2>Register</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> Formula <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/Formula.js&#x27;</span>

View File

@@ -65,11 +65,13 @@ The folder containing the packaged resources for the `web` folder.
[Explore how to export HTML and SVG as images](https://juejin.cn/post/7276712861514170409)
[How does the dom-to-image library convert HTML into images](https://juejin.cn/post/7287913415803764747)
## Special Note
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.
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](https://github.com/wanglin2/mind-map/issues) here.
@@ -223,4 +225,12 @@ Open source is not easy. If this project is helpful to you, you can invite the a
<img src="../../../../assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>达仁科技</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 style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/天清如愿.jpg" 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="checkbox48" checked="true" /><label for="checkbox48">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="checkbox49" checked="true" /><label for="checkbox49">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="checkbox50" checked="true" /><label for="checkbox50">Built-in multiple themes, allowing for highly customizable styles, and supporting registration of new themes</label></li>
<li><input type="checkbox" id="checkbox51" checked="true" /><label for="checkbox51">Node content supports text (regular text, rich text), images, icons, hyperlinks, notes, labels, summaries, and math formulas</label></li>
<li><input type="checkbox" id="checkbox52" checked="true" /><label for="checkbox52">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="checkbox53" checked="true" /><label for="checkbox53">Support canvas dragging and scaling</label></li>
<li><input type="checkbox" id="checkbox54" checked="true" /><label for="checkbox54">Supports two multi node selection methods: mouse button drag selection and Ctrl+left button selection</label></li>
<li><input type="checkbox" id="checkbox55" checked="true" /><label for="checkbox55">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="checkbox56" checked="true" /><label for="checkbox56">Support shortcut keys, forward and backward, correlation lines, search and replacement, small maps, watermarks, and scrollbar</label></li>
<li><input type="checkbox" id="checkbox57" checked="true" /><label for="checkbox57">Provide rich configurations to meet various scenarios and usage habits</label></li>
<li><input type="checkbox" id="checkbox58" checked="true" /><label for="checkbox58">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="checkbox59" checked="true" /><label for="checkbox59">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="checkbox60" checked="true" /><label for="checkbox60">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="checkbox61" checked="true" /><label for="checkbox61">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="checkbox62" checked="true" /><label for="checkbox62">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="checkbox63" checked="true" /><label for="checkbox63">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>
@@ -51,9 +51,10 @@ full screen, support mini map</li>
<p><a href="https://juejin.cn/post/7204854015463538744">How to simulate the background image style of css in canvas</a></p>
<p><a href="https://juejin.cn/post/7233012756314701884">My first Electron application</a></p>
<p><a href="https://juejin.cn/post/7276712861514170409">Explore how to export HTML and SVG as images</a></p>
<p><a href="https://juejin.cn/post/7287913415803764747">How does the dom-to-image library convert HTML into images</a></p>
<h2>Special Note</h2>
<p>This project can be used for learning and reference. Please deeply experience whether it can meet your needs when using it for actual projects.</p>
<p>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.</p>
<p>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.</p>
<p>If you have suggestions or find bugs, you can submit <a href="https://github.com/wanglin2/mind-map/issues">issues</a> here.</p>
<p>The built-in themes and icons in the project part come from:</p>
<p><a href="https://naotu.baidu.com/">Baidu Mind Map</a></p>
@@ -181,6 +182,14 @@ full screen, support mini map</li>
<img src="../../../../assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>达仁科技</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 style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/天清如愿.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>天清如愿</p>
</div>
</div>
</div>
</template>

View File

@@ -35,7 +35,8 @@ Function return content:
```js
{
svgHTML, // small map html
getImgUrl,// v0.8.0+, An asynchronous function that you can call and pass a callback function. The callback function can receive a parameter representing a small map of the image type, and you can render it through the img tag
svgHTML, // Mini map HTML, it is recommended to use the getImgUrl method to obtain image type mini maps, reduce the number of page DOM, and optimize performance
viewBoxStyle, // view box position information
miniMapBoxScale, // view box zoom value
miniMapBoxLeft, // view box left value

View File

@@ -25,7 +25,8 @@ MindMap.usePlugin(MiniMap)
<p><code>boxHeight</code>: the height of the small map container</p>
<p>Function return content:</p>
<pre class="hljs"><code>{
svgHTML, <span class="hljs-comment">// small map html</span>
getImgUrl,<span class="hljs-comment">// v0.8.0+, An asynchronous function that you can call and pass a callback function. The callback function can receive a parameter representing a small map of the image type, and you can render it through the img tag</span>
svgHTML, <span class="hljs-comment">// Mini map HTML, it is recommended to use the getImgUrl method to obtain image type mini maps, reduce the number of page DOM, and optimize performance</span>
viewBoxStyle, <span class="hljs-comment">// view box position information</span>
miniMapBoxScale, <span class="hljs-comment">// view box zoom value</span>
miniMapBoxLeft, <span class="hljs-comment">// view box left value</span>

View File

@@ -56,6 +56,14 @@ Whether the node is currently being dragged
## Methods
### updateNodeByActive(active)
> v0.8.0+
- `active`Boolean, active status.
Update nodes based on whether they are activated or not. The main task is to update the display and hiding of the expand and collapse buttons for nodes.
### setOpacity(val)
> v0.7.2+

View File

@@ -31,6 +31,14 @@
</blockquote>
<p>Whether the node is currently being dragged</p>
<h2>Methods</h2>
<h3>updateNodeByActive(active)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<ul>
<li><code>active</code>Boolean, active status.</li>
</ul>
<p>Update nodes based on whether they are activated or not. The main task is to update the display and hiding of the expand and collapse buttons for nodes.</p>
<h3>setOpacity(val)</h3>
<blockquote>
<p>v0.7.2+</p>

View File

@@ -7,14 +7,20 @@ accessed through `mindMap.renderer`.
### activeNodeList
Gets the current list of active nodes
Gets the current list of active nodes.
### root
Gets the root node of the node tree
Gets the root node of the node tree.
## Methods
### setRootNodeCenter()
> v0.8.0+
Return to the central theme, that is, set the root node to the center of the canvas.
### setData(data)
> v0.7.3+
@@ -23,49 +29,81 @@ Dynamically set mind map data.
### clearActive()
Clears the currently active node
> v0.8.0+ abandoned
Clears the currently active node.
### clearAllActive()
Clears all currently active nodes and triggers the `node_active` event
> v0.8.0+ abandoned
Clears all currently active nodes and triggers the `node_active` event.
### clearActiveNode()
> v0.8.0+
Clears all currently active nodes and triggers the `node_active` event.
### clearActiveNodeList()
> v0.8.0+
Clears all currently active nodes but not triggers the `node_active` event.
### startTextEdit()
> v0.1.6+
If there is a text editing requirement, this method can be called to
disable the enter key and delete key related shortcuts to prevent conflicts
disable the enter key and delete key related shortcuts to prevent conflicts.
### endTextEdit()
> v0.1.6+
End text editing, restore enter key and delete key related shortcuts
End text editing, restore enter key and delete key related shortcuts.
### addActiveNode(node)
Add a node to the active list
> v0.8.0+ abandoned
Add a node to the active list.
### addNodeToActiveList(node)
> v0.8.0+
Add a node to the active list.
### removeActiveNode(node)
Remove a node from the active list
> v0.8.0+ abandoned
Remove a node from the active list.
### removeNodeFromActiveList(node)
> v0.8.0+
Remove a node from the active list.
### findActiveNodeIndex(node)
Search for the index of a node in the active list
Search for the index of a node in the active list.
### getNodeIndex(node)
Get the position index of a node among its siblings
Get the position index of a node among its siblings.
### removeOneNode(node)
Delete a specific node
Delete a specific node.
### copyNode()
Copy a node, the active node is the node to be operated on, if there are
multiple active nodes, only the first node will be operated on
multiple active nodes, only the first node will be operated on.
### setNodeDataRender(node, data, notRender)
@@ -73,25 +111,25 @@ multiple active nodes, only the first node will be operated on
Set node `data`, i.e. the data in the data field, and will determine whether the
node needs to be re-rendered based on whether the node size has changed, `data`
is an object, e.g. `{text: 'I am new text'}`
is an object, e.g. `{text: 'I am new text'}`.
### moveNodeTo(node, toNode)
> v0.1.5+
Move a node as a child of another node
Move a node as a child of another node.
### insertBefore(node, exist)
> v0.1.5+
Move a node in front of another node
Move a node in front of another node.
### insertAfter(node, exist)
> v0.1.5+
Move a node behind another node
Move a node behind another node.
### moveNodeToCenter(node)

View File

@@ -5,65 +5,102 @@
accessed through <code>mindMap.renderer</code>.</p>
<h2>Properties</h2>
<h3>activeNodeList</h3>
<p>Gets the current list of active nodes</p>
<p>Gets the current list of active nodes.</p>
<h3>root</h3>
<p>Gets the root node of the node tree</p>
<p>Gets the root node of the node tree.</p>
<h2>Methods</h2>
<h3>setRootNodeCenter()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Return to the central theme, that is, set the root node to the center of the canvas.</p>
<h3>setData(data)</h3>
<blockquote>
<p>v0.7.3+</p>
</blockquote>
<p>Dynamically set mind map data.</p>
<h3>clearActive()</h3>
<p>Clears the currently active node</p>
<blockquote>
<p>v0.8.0+ abandoned</p>
</blockquote>
<p>Clears the currently active node.</p>
<h3>clearAllActive()</h3>
<p>Clears all currently active nodes and triggers the <code>node_active</code> event</p>
<blockquote>
<p>v0.8.0+ abandoned</p>
</blockquote>
<p>Clears all currently active nodes and triggers the <code>node_active</code> event.</p>
<h3>clearActiveNode()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Clears all currently active nodes and triggers the <code>node_active</code> event.</p>
<h3>clearActiveNodeList()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Clears all currently active nodes but not triggers the <code>node_active</code> event.</p>
<h3>startTextEdit()</h3>
<blockquote>
<p>v0.1.6+</p>
</blockquote>
<p>If there is a text editing requirement, this method can be called to
disable the enter key and delete key related shortcuts to prevent conflicts</p>
disable the enter key and delete key related shortcuts to prevent conflicts.</p>
<h3>endTextEdit()</h3>
<blockquote>
<p>v0.1.6+</p>
</blockquote>
<p>End text editing, restore enter key and delete key related shortcuts</p>
<p>End text editing, restore enter key and delete key related shortcuts.</p>
<h3>addActiveNode(node)</h3>
<p>Add a node to the active list</p>
<blockquote>
<p>v0.8.0+ abandoned</p>
</blockquote>
<p>Add a node to the active list.</p>
<h3>addNodeToActiveList(node)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Add a node to the active list.</p>
<h3>removeActiveNode(node)</h3>
<p>Remove a node from the active list</p>
<blockquote>
<p>v0.8.0+ abandoned</p>
</blockquote>
<p>Remove a node from the active list.</p>
<h3>removeNodeFromActiveList(node)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Remove a node from the active list.</p>
<h3>findActiveNodeIndex(node)</h3>
<p>Search for the index of a node in the active list</p>
<p>Search for the index of a node in the active list.</p>
<h3>getNodeIndex(node)</h3>
<p>Get the position index of a node among its siblings</p>
<p>Get the position index of a node among its siblings.</p>
<h3>removeOneNode(node)</h3>
<p>Delete a specific node</p>
<p>Delete a specific node.</p>
<h3>copyNode()</h3>
<p>Copy a node, the active node is the node to be operated on, if there are
multiple active nodes, only the first node will be operated on</p>
multiple active nodes, only the first node will be operated on.</p>
<h3>setNodeDataRender(node, data, notRender)</h3>
<ul>
<li><code>notRender</code>: v0.6.9+, <code>Boolean</code>, Default is <code>false</code>, Do not trigger rendering.</li>
</ul>
<p>Set node <code>data</code>, i.e. the data in the data field, and will determine whether the
node needs to be re-rendered based on whether the node size has changed, <code>data</code>
is an object, e.g. <code>{text: 'I am new text'}</code></p>
is an object, e.g. <code>{text: 'I am new text'}</code>.</p>
<h3>moveNodeTo(node, toNode)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Move a node as a child of another node</p>
<p>Move a node as a child of another node.</p>
<h3>insertBefore(node, exist)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Move a node in front of another node</p>
<p>Move a node in front of another node.</p>
<h3>insertAfter(node, exist)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Move a node behind another node</p>
<p>Move a node behind another node.</p>
<h3>moveNodeToCenter(node)</h3>
<blockquote>
<p>v0.2.17+</p>

View File

@@ -2,8 +2,6 @@
> v0.4.0+
> Note: This is a testing nature and imperfect function
This plugin provides the ability to edit rich text of nodes, and takes effect after registration.
By default, node editing can only uniformly apply styles to all text in the node. This plugin can support rich text editing effects. Currently, it supports bold, italic, underline, strikethrough, font, font size, color, and backgroundColor. Underline and line height are not supported.
@@ -20,6 +18,8 @@ The principle of this plugin is to use [Quill](https://github.com/quilljs/quill)
> The compatibility of dom to image more is relatively poor, and exported images are empty on many browsers, so you can replace them with html2canvas according to your own needs.
After version `0.6.16+`, third-party libraries such as 'dom-to-image-more' and 'html2canvas' will no longer be used for export, Compatibility and export are no longer issues.
## Register
```js
@@ -68,6 +68,14 @@ Replace the built-in font size list during rich text editing. The built-in list
## Method
### setNotActiveNodeStyle(node, style)
> v0.8.0+
- `style`Object, style object.
Set rich text style for inactive nodes.
### selectAll()
Select All. When the node is being edited, you can select all the text in the node through this method.

View File

@@ -4,9 +4,6 @@
<blockquote>
<p>v0.4.0+</p>
</blockquote>
<blockquote>
<p>Note: This is a testing nature and imperfect function</p>
</blockquote>
<p>This plugin provides the ability to edit rich text of nodes, and takes effect after registration.</p>
<p>By default, node editing can only uniformly apply styles to all text in the node. This plugin can support rich text editing effects. Currently, it supports bold, italic, underline, strikethrough, font, font size, color, and backgroundColor. Underline and line height are not supported.</p>
<p>The principle of this plugin is to use <a href="https://github.com/quilljs/quill">Quill</a> editor implements rich text editing, and then uses the edited <code>DOM</code> node directly as the text data of the node, and embeds the <code>DOM</code> node through the <code>svg</code> <code>foreignObject</code> tag during rendering.</p>
@@ -21,6 +18,7 @@
<blockquote>
<p>The compatibility of dom to image more is relatively poor, and exported images are empty on many browsers, so you can replace them with html2canvas according to your own needs.</p>
</blockquote>
<p>After version <code>0.6.16+</code>, third-party libraries such as 'dom-to-image-more' and 'html2canvas' will no longer be used for export, Compatibility and export are no longer issues.</p>
<h2>Register</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> RichText <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/RichText.js&#x27;</span>
@@ -58,6 +56,14 @@ MindMap.usePlugin(RichText, opt?)
<pre class="hljs"><code>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, ..<span class="hljs-number">.100</span>]
</code></pre>
<h2>Method</h2>
<h3>setNotActiveNodeStyle(node, style)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<ul>
<li><code>style</code>Object, style object.</li>
</ul>
<p>Set rich text style for inactive nodes.</p>
<h3>selectAll()</h3>
<p>Select All. When the node is being edited, you can select all the text in the node through this method.</p>
<h3>focus()</h3>

View File

@@ -1,6 +1,6 @@
# Select plugin
The `Select` plugin provides the function of right-clicking to select multiple nodes.
The `Select` plugin provides the function of select multiple nodes.
## Register

View File

@@ -1,7 +1,7 @@
<template>
<div>
<h1>Select plugin</h1>
<p>The <code>Select</code> plugin provides the function of right-clicking to select multiple nodes.</p>
<p>The <code>Select</code> plugin provides the function of select multiple nodes.</p>
<h2>Register</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> Select <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/Select.js&#x27;</span>

View File

@@ -363,6 +363,45 @@ Generate colors based on incoming content, and the same content will generate th
Determine whether two objects are the same, only handling objects or arrays.
#### getNodeDataIndex(node)
> v0.8.0+
Gets the position index of a node within its sibling nodes.
#### getNodeIndexInNodeList(node, nodeList)
> v0.8.0+
Find the index of a node from a list of nodes.
#### setDataToClipboard(data)
> v0.8.0+
- `data`Object | Array
Set data to the user clipboard.
#### getDataFromClipboard()
> v0.8.0+
Reading text and images from the user's clipboard returns:
```js
{
text,
img
}
```
#### removeFromParentNodeData(node)
> v0.8.0+
Remove the data of a node from its parent node's `nodeData.children` list.
## Simulate CSS background in Canvas
Import:

View File

@@ -295,6 +295,39 @@ and copying the <code>data</code> of the data object, example:</p>
<li><code>a</code>、<code>b</code>: Object | Array, Two objects to compare</li>
</ul>
<p>Determine whether two objects are the same, only handling objects or arrays.</p>
<h4>getNodeDataIndex(node)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Gets the position index of a node within its sibling nodes.</p>
<h4>getNodeIndexInNodeList(node, nodeList)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Find the index of a node from a list of nodes.</p>
<h4>setDataToClipboard(data)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<ul>
<li><code>data</code>Object | Array</li>
</ul>
<p>Set data to the user clipboard.</p>
<h4>getDataFromClipboard()</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Reading text and images from the user's clipboard returns:</p>
<pre class="hljs"><code>{
text,
img
}
</code></pre>
<h4>removeFromParentNodeData(node)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>Remove the data of a node from its parent node's <code>nodeData.children</code> list.</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

@@ -87,4 +87,16 @@ MindMap.usePlugin(AssociativeLine)
### clearActiveLine()
清除当前激活的关联线的激活状态。
清除当前激活的关联线的激活状态。
### front()
> v0.8.0+
关联线顶层显示。
### back()
> v0.8.0+
关联线回到原有层级。

View File

@@ -66,6 +66,16 @@ MindMap.usePlugin(AssociativeLine)
<p>删除当前激活的关联线点击某条关联线则视为激活</p>
<h3>clearActiveLine()</h3>
<p>清除当前激活的关联线的激活状态</p>
<h3>front()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>关联线顶层显示</p>
<h3>back()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>关联线回到原有层级</p>
</div>
</template>

View File

@@ -1,5 +1,65 @@
# Changelog
## 0.8.0
破坏性更新:大幅优化部分代码,小幅提升性能,主要是`render`类,删除无用逻辑、调整不合理的实现、提取重复代码;修改函数名称、函数功能等。
修复:
> 1.修复导出图片和svg时关联线的箭头消失的问题。
>
> 2.修复调整容器大小后回到根节点的操作异常的问题。
>
> 3.修复插入概要、上移、下移、一键整理布局的快捷键操作没有触发data_change事件的问题。
>
> 4.修复存在水印时导出图片、svg、pdf时每个节点都会显示边框的问题。
>
> 5.修复容器尺寸改变后没有水印没有重新绘制的问题。
>
> 6.修复存在水印时小地图渲染非常慢的问题。
>
> 7.修复协同插件当创建新节点时新节点未显示创建人头像的问题。
新增:
> 1.优化画布DOM结构将节点、连线、关联线分层渲染。
>
> 2.优化水印插件。
>
> 3.setTheme、setThemeConfig、setLayout函数增加不触发重新渲染的参数。
>
> 4.新增插入父节点的命令。
>
> 5.新增仅删除当前节点的命令。
>
> 6.插入概要时自动展开子节点。
>
> 7.鼠标右键单击画布时清除当前激活节点。
>
> 8.被收起的激活节点同步从激活节点列表里删除。
>
> 9.粘贴带换行的文本支持控制是否按换行分割节点。
>
> 10.小地图插件支持返回图片类型的小地图。
>
> 11.指定时间内只允许添加一次历史记录,避免添加没有必要的中间状态。
Demo
> 1.修改回到根节点的方法及文案。
>
> 2.修复覆盖方式切换主题时第一次切换不生效的问题。
>
> 3.右键菜单新增插入父节点和仅删除当前节点的功能。
>
> 4.顶部工具栏支持根据窗口宽度自动收起到更多中。
>
> 5.支持手动输入缩放倍数。
>
> 6.完善界面英文翻译。
>
> 7.小地图改为通过图片渲染。
## 0.7.3-fix.2
修复协同编辑的一些问题:

View File

@@ -1,6 +1,42 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.8.0</h2>
<p>破坏性更新大幅优化部分代码小幅提升性能主要是<code>render</code>删除无用逻辑调整不合理的实现提取重复代码修改函数名称函数功能等</p>
<p>修复</p>
<blockquote>
<p>1.修复导出图片和svg时关联线的箭头消失的问题</p>
<p>2.修复调整容器大小后回到根节点的操作异常的问题</p>
<p>3.修复插入概要上移下移一键整理布局的快捷键操作没有触发data_change事件的问题</p>
<p>4.修复存在水印时导出图片svgpdf时每个节点都会显示边框的问题</p>
<p>5.修复容器尺寸改变后没有水印没有重新绘制的问题</p>
<p>6.修复存在水印时小地图渲染非常慢的问题</p>
<p>7.修复协同插件当创建新节点时新节点未显示创建人头像的问题</p>
</blockquote>
<p>新增</p>
<blockquote>
<p>1.优化画布DOM结构将节点连线关联线分层渲染</p>
<p>2.优化水印插件</p>
<p>3.setThemesetThemeConfigsetLayout函数增加不触发重新渲染的参数</p>
<p>4.新增插入父节点的命令</p>
<p>5.新增仅删除当前节点的命令</p>
<p>6.插入概要时自动展开子节点</p>
<p>7.鼠标右键单击画布时清除当前激活节点</p>
<p>8.被收起的激活节点同步从激活节点列表里删除</p>
<p>9.粘贴带换行的文本支持控制是否按换行分割节点</p>
<p>10.小地图插件支持返回图片类型的小地图</p>
<p>11.指定时间内只允许添加一次历史记录避免添加没有必要的中间状态</p>
</blockquote>
<p>Demo</p>
<blockquote>
<p>1.修改回到根节点的方法及文案</p>
<p>2.修复覆盖方式切换主题时第一次切换不生效的问题</p>
<p>3.右键菜单新增插入父节点和仅删除当前节点的功能</p>
<p>4.顶部工具栏支持根据窗口宽度自动收起到更多中</p>
<p>5.支持手动输入缩放倍数</p>
<p>6.完善界面英文翻译</p>
<p>7.小地图改为通过图片渲染</p>
</blockquote>
<h2>0.7.3-fix.2</h2>
<p>修复协同编辑的一些问题</p>
<p>1.插入同级节点时新节点位置不正确</p>

View File

@@ -39,7 +39,7 @@ const mindMap = new MindMap({
| selectTranslateLimit | Number | 20 | 多选节点时鼠标移动距边缘多少距离时开始偏移 |
| customNoteContentShowv0.1.6+ | Object | null | 自定义节点备注内容显示Object类型结构为{show: (noteContent, left, top) => {// 你的显示节点备注逻辑 }, hide: () => {// 你的隐藏节点备注逻辑 }} |
| readonlyv0.1.7+ | Boolean | false | 是否是只读模式 |
| enableFreeDragv0.2.4+ | Boolean | false | 是否开启节点自由拖拽 |
| enableFreeDragv0.2.4+ | Boolean | false | 是否开启节点自由拖拽(自由拖拽即可以把节点拖拽到画布的任意位置,注意不是拖拽节点成为其他节点的子节点兄弟节点的功能,自由拖拽的连线会存在一定问题,所以该特性最好不要使用) |
| watermarkConfigv0.2.4+ | Object | | 水印配置,详细配置请参考下方表格【水印配置】 |
| textAutoWrapWidthv0.3.4+ | Number | 500 | 节点内每行文本达到该宽度后自动换行 |
| customHandleMousewheelv0.4.3+ | Function | null | 自定义鼠标滚轮事件处理,可以传一个函数,回调参数为事件对象 |
@@ -82,7 +82,8 @@ const mindMap = new MindMap({
| errorHandlerv0.6.15+ | Function | | 自定义错误处理函数,目前只会抛出一些异步逻辑出错的情况。可以传递一个函数,会接收两个参数,第一个为错误的类型,第二个为错误对象 |
| disableMouseWheelZoomv0.6.15+ | Boolean | false | 禁止鼠标滚轮缩放你仍旧可以使用api进行缩放 |
| resetCssv0.6.16+ | String | * { margin: 0; padding: 0; box-sizing: border-box; } | 设置导出图片和svg时针对富文本节点内容也就是嵌入到svg中的html节点的默认样式覆盖如果不覆盖节点内容会发生偏移 |
| enableDblclickResetv0.6.17+ | Boolean | truev0.7.0+改为false | 开启鼠标双击复位思维导图位置及缩放 |
| enableDblclickResetv0.6.17+v0.8.0+已删除该属性) | Boolean | truev0.7.0+改为false | 开启鼠标双击复位思维导图位置及缩放 |
| enableDblclickBackToRootNodev0.8.0+ | Boolean | false | 是否在鼠标双击时回到根节点,也就是让根节点居中显示 |
| minExportImgCanvasScalev0.7.0+ | Number | 2 | 导出图片和pdf时canvas的缩放倍数该配置会和window.devicePixelRatio值取最大值用于提升图片清晰度 |
| hoverRectColorv0.7.0+ | String | rgb(94, 200, 248) | 节点鼠标hover和激活时显示的矩形边框颜色hover时会添加0.6的透明度 |
| hoverRectPaddingv0.7.0+ | Number | 2 | 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 |
@@ -95,6 +96,10 @@ const mindMap = new MindMap({
| dragOpacityConfigv0.7.2+ | Object | { cloneNodeOpacity: 0.5, beingDragNodeOpacity: 0.3 } | 节点拖拽时的透明度配置,传递一个对象,字段含义分别为:跟随鼠标移动的克隆节点或矩形的透明度、被拖拽节点的透明度 |
| tagsColorMapv0.7.2+ | Object | {} | 自定义节点标签的颜色可传一个对象key为要指定颜色的标签内容value为该标签内容的颜色如果不传内部会根据标签内容生成对应的颜色 |
| cooperateStylev0.7.3+ | Object | { avatarSize: 22, fontSize: 12 } | 节点协作编辑时的人员头像样式配置,字段含义分别为:头像大小、如果是文字头像,那么文字的大小 |
| associativeLineIsAlwaysAboveNodev0.8.0+ | Boolean | true | 关联线是否始终显示在节点上层如果设为false那么创建关联线和激活关联线时处于最顶层其他情况下处于节点下方 |
| defaultGeneralizationTextv0.8.0+ | String | 概要 | 插入概要的默认文本 |
| handleIsSplitByWrapOnPasteCreateNewNodev0.8.0+ | Function / null | null | 粘贴文本的方式创建新节点时控制是否按换行自动分割节点即如果存在换行那么会根据换行创建多个节点否则只会创建一个节点可以传递一个函数返回promiseresolve代表根据换行分割reject代表忽略换行 |
| addHistoryTimev0.8.0+ | Number | 100 | 指定时间内只允许添加一次历史记录避免添加没有必要的中间状态单位ms |
### 数据结构
@@ -210,15 +215,103 @@ mindMap.setTheme('主题名称')
当前注册的所有插件列表。
## 实例属性
### el
容器元素。
### opt
配置选项对象。
### svg
> @svgdotjs/svg.js库调用SVG()方法返回的节点实例
画布svg元素。
### draw
> @svgdotjs/svg.js库调用group()方法返回的节点实例
>
> svg节点的子节点
容器元素,用于承载节点、连线等内容。
### lineDraw
> v0.8.0+
>
> @svgdotjs/svg.js库调用group()方法返回的节点实例
>
> draw节点的子节点
节点连线元素的容器。
### nodeDraw
> v0.8.0+
>
> @svgdotjs/svg.js库调用group()方法返回的节点实例
>
> draw节点的子节点
节点元素的容器。
### associativeLineDraw
> v0.8.0+
>
> @svgdotjs/svg.js库调用group()方法返回的节点实例
>
> 在注册了关联线插件的情况下可用
>
> draw节点的子节点
关联线内容的容器。
### otherDraw
> v0.8.0+
>
> @svgdotjs/svg.js库调用group()方法返回的节点实例
>
> draw节点的子节点
其他内容的容器。
### elRect
容器元素`el`的尺寸、位置信息。调用`getBoundingClientRect()`方法的返回结果。
### width
容器元素`el`的宽度。
### height
容器元素`el`的高度。
### themeConfig
当前主题配置。
## 实例方法
### clearDraw()
> v0.8.0+
清空`lineDraw``associativeLineDraw``nodeDraw``otherDraw`容器。
### destroy()
> v0.6.0+
销毁思维导图。会移除注册的插件、移除监听的事件、删除画布的所有节点。
### getSvgData({ paddingX = 0, paddingY = 0 })
### getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false })
> v0.3.0+
@@ -226,6 +319,8 @@ mindMap.setTheme('主题名称')
`paddingY`:垂直内边距
`ignoreWatermark`v0.8.0+,不要绘制水印,如果不需要绘制水印的场景可以传`true`,因为绘制水印非常慢
获取`svg`数据,返回一个对象,详细结构如下:
```js
@@ -309,6 +404,7 @@ mindMap.setTheme('主题名称')
| node_icon_clickv0.6.10+ | 点击节点内的图标时触发 | this节点实例、item点击的图标名称、e事件对象 |
| view_theme_changev0.6.12+ | 调用了setTheme方法设置主题后触发 | theme设置的新主题名称 |
| set_datav0.7.3+ | 调用了setData方法动态设置思维导图数据时触发 | data新的思维导图数据 |
| resizev0.8.0+ | 容器尺寸改变后触发,实际上是当思维导图实例的`resize`方法被调用后触发 | |
### emit(event, ...args)
@@ -318,7 +414,9 @@ mindMap.setTheme('主题名称')
解绑事件
### setTheme(theme)
### setTheme(theme, notRender = false)
- `notRender`v0.8.0+是否不要调用render方法更新画布。
切换主题,可选主题见上面的选项表格
@@ -326,7 +424,9 @@ mindMap.setTheme('主题名称')
获取当前主题
### setThemeConfig(config)
### setThemeConfig(config, notRender = false)
- `notRender`v0.8.0+是否不要调用render方法更新画布。
设置主题配置,`config`同上面选项表格里的选项`themeConfig`
@@ -366,7 +466,9 @@ mindMap.updateConfig({
获取当前的布局结构
### setLayout(layout)
### setLayout(layout, notRender = false)
- `notRender`v0.8.0+是否不要调用render方法更新画布。
设置布局结构,可选值见上面选项表格的`layout`字段
@@ -413,6 +515,8 @@ mindMap.updateConfig({
| INSERT_MULTI_NODEv0.7.2+ | 给指定的节点同时插入多个同级节点,操作节点为当前激活的节点或指定节点 | appointNodes可选指定节点指定多个节点可以传一个数组, nodeList新插入节点的数据列表数组类型 |
| INSERT_MULTI_CHILD_NODEv0.7.2+ | 给指定的节点同时插入多个子节点,操作节点为当前激活的节点或指定节点 | appointNodes可选指定节点指定多个节点可以传一个数组, childList新插入节点的数据列表数组类型 |
| INSERT_FORMULAv0.7.2+ | 给节点插入数学公式,操作节点为当前激活的节点或指定节点 | formula要插入的数学公式LaText语法, appointNodes可选指定要插入公式的节点多个节点可以传数组否则默认为当前激活的节点 |
| INSERT_PARENT_NODEv0.8.0+ | 给指定的节点插入父节点,操作节点为当前激活的节点或指定节点 | openEdit是否激活新插入的节点并进入编辑模式默认为`true`)、 appointNodes可选指定要插入父节点的节点指定多个节点可以传一个数组、 appointData可选指定新创建节点的数据比如{text: 'xxx', ...},详细结构可以参考[exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) |
| REMOVE_CURRENT_NODEv0.8.0+ | 仅删除当前节点,操作节点为当前激活的节点或指定节点 | appointNodes可选指定要删除的节点指定多个节点可以传一个数组 |
### setData(data)

View File

@@ -121,7 +121,7 @@
<td>enableFreeDragv0.2.4+</td>
<td>Boolean</td>
<td>false</td>
<td>是否开启节点自由拖拽</td>
<td>是否开启节点自由拖拽自由拖拽即可以把节点拖拽到画布的任意位置注意不是拖拽节点成为其他节点的子节点兄弟节点的功能自由拖拽的连线会存在一定问题所以该特性最好不要使用</td>
</tr>
<tr>
<td>watermarkConfigv0.2.4+</td>
@@ -376,12 +376,18 @@
<td>设置导出图片和svg时针对富文本节点内容也就是嵌入到svg中的html节点的默认样式覆盖如果不覆盖节点内容会发生偏移</td>
</tr>
<tr>
<td>enableDblclickResetv0.6.17+</td>
<td>enableDblclickResetv0.6.17+v0.8.0+已删除该属性</td>
<td>Boolean</td>
<td>truev0.7.0+改为false</td>
<td>开启鼠标双击复位思维导图位置及缩放</td>
</tr>
<tr>
<td>enableDblclickBackToRootNodev0.8.0+</td>
<td>Boolean</td>
<td>false</td>
<td>是否在鼠标双击时回到根节点也就是让根节点居中显示</td>
</tr>
<tr>
<td>minExportImgCanvasScalev0.7.0+</td>
<td>Number</td>
<td>2</td>
@@ -453,6 +459,30 @@
<td>{ avatarSize: 22, fontSize: 12 }</td>
<td>节点协作编辑时的人员头像样式配置字段含义分别为头像大小如果是文字头像那么文字的大小</td>
</tr>
<tr>
<td>associativeLineIsAlwaysAboveNodev0.8.0+</td>
<td>Boolean</td>
<td>true</td>
<td>关联线是否始终显示在节点上层如果设为false那么创建关联线和激活关联线时处于最顶层其他情况下处于节点下方</td>
</tr>
<tr>
<td>defaultGeneralizationTextv0.8.0+</td>
<td>String</td>
<td>概要</td>
<td>插入概要的默认文本</td>
</tr>
<tr>
<td>handleIsSplitByWrapOnPasteCreateNewNodev0.8.0+</td>
<td>Function / null</td>
<td>null</td>
<td>粘贴文本的方式创建新节点时控制是否按换行自动分割节点即如果存在换行那么会根据换行创建多个节点否则只会创建一个节点可以传递一个函数返回promiseresolve代表根据换行分割reject代表忽略换行</td>
</tr>
<tr>
<td>addHistoryTimev0.8.0+</td>
<td>Number</td>
<td>100</td>
<td>指定时间内只允许添加一次历史记录避免添加没有必要的中间状态单位ms</td>
</tr>
</tbody>
</table>
<h3>数据结构</h3>
@@ -607,18 +637,77 @@ mindMap.setTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>)
<p>v0.3.0+</p>
</blockquote>
<p>当前注册的所有插件列表</p>
<h2>实例属性</h2>
<h3>el</h3>
<p>容器元素</p>
<h3>opt</h3>
<p>配置选项对象</p>
<h3>svg</h3>
<blockquote>
<p>@svgdotjs/svg.js库调用SVG()方法返回的节点实例</p>
</blockquote>
<p>画布svg元素</p>
<h3>draw</h3>
<blockquote>
<p>@svgdotjs/svg.js库调用group()方法返回的节点实例</p>
<p>svg节点的子节点</p>
</blockquote>
<p>容器元素用于承载节点连线等内容</p>
<h3>lineDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js库调用group()方法返回的节点实例</p>
<p>draw节点的子节点</p>
</blockquote>
<p>节点连线元素的容器</p>
<h3>nodeDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js库调用group()方法返回的节点实例</p>
<p>draw节点的子节点</p>
</blockquote>
<p>节点元素的容器</p>
<h3>associativeLineDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js库调用group()方法返回的节点实例</p>
<p>在注册了关联线插件的情况下可用</p>
<p>draw节点的子节点</p>
</blockquote>
<p>关联线内容的容器</p>
<h3>otherDraw</h3>
<blockquote>
<p>v0.8.0+</p>
<p>@svgdotjs/svg.js库调用group()方法返回的节点实例</p>
<p>draw节点的子节点</p>
</blockquote>
<p>其他内容的容器</p>
<h3>elRect</h3>
<p>容器元素<code>el</code>的尺寸位置信息调用<code>getBoundingClientRect()</code>方法的返回结果</p>
<h3>width</h3>
<p>容器元素<code>el</code>的宽度</p>
<h3>height</h3>
<p>容器元素<code>el</code>的高度</p>
<h3>themeConfig</h3>
<p>当前主题配置</p>
<h2>实例方法</h2>
<h3>clearDraw()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>清空<code>lineDraw</code><code>associativeLineDraw</code><code>nodeDraw</code><code>otherDraw</code>容器</p>
<h3>destroy()</h3>
<blockquote>
<p>v0.6.0+</p>
</blockquote>
<p>销毁思维导图会移除注册的插件移除监听的事件删除画布的所有节点</p>
<h3>getSvgData({ paddingX = 0, paddingY = 0 })</h3>
<h3>getSvgData({ paddingX = 0, paddingY = 0, ignoreWatermark = false })</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p><code>paddingX</code>水平内边距</p>
<p><code>paddingY</code>垂直内边距</p>
<p><code>ignoreWatermark</code>v0.8.0+不要绘制水印如果不需要绘制水印的场景可以传<code>true</code>因为绘制水印非常慢</p>
<p>获取<code>svg</code>数据返回一个对象详细结构如下</p>
<pre class="hljs"><code>{
svg, <span class="hljs-comment">// Element思维导图图形的整体svg元素包括svg画布容器、g实际的思维导图组</span>
@@ -854,17 +943,28 @@ mindMap.setTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>)
<td>调用了setData方法动态设置思维导图数据时触发</td>
<td>data新的思维导图数据</td>
</tr>
<tr>
<td>resizev0.8.0+</td>
<td>容器尺寸改变后触发实际上是当思维导图实例的<code>resize</code>方法被调用后触发</td>
<td></td>
</tr>
</tbody>
</table>
<h3>emit(event, ...args)</h3>
<p>触发事件可以是上面表格里的事件也可以是自定义事件</p>
<h3>off(event, fn)</h3>
<p>解绑事件</p>
<h3>setTheme(theme)</h3>
<h3>setTheme(theme, notRender = false)</h3>
<ul>
<li><code>notRender</code>v0.8.0+是否不要调用render方法更新画布</li>
</ul>
<p>切换主题可选主题见上面的选项表格</p>
<h3>getTheme()</h3>
<p>获取当前主题</p>
<h3>setThemeConfig(config)</h3>
<h3>setThemeConfig(config, notRender = false)</h3>
<ul>
<li><code>notRender</code>v0.8.0+是否不要调用render方法更新画布</li>
</ul>
<p>设置主题配置<code>config</code>同上面选项表格里的选项<code>themeConfig</code></p>
<h3>getCustomThemeConfig()</h3>
<p>获取自定义主题配置</p>
@@ -889,7 +989,10 @@ mindMap.setTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>)
<p>该方法只做更新配置的事情没有其他副作用比如触发画布重新渲染之类的</p>
<h3>getLayout()</h3>
<p>获取当前的布局结构</p>
<h3>setLayout(layout)</h3>
<h3>setLayout(layout, notRender = false)</h3>
<ul>
<li><code>notRender</code>v0.8.0+是否不要调用render方法更新画布</li>
</ul>
<p>设置布局结构可选值见上面选项表格的<code>layout</code>字段</p>
<h3>execCommand(name, ...args)</h3>
<p>执行命令每执行一个命令就会在历史堆栈里添加一条记录用于回退或前进所有命令如下</p>
@@ -1087,6 +1190,16 @@ mindMap.setTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>)
<td>给节点插入数学公式操作节点为当前激活的节点或指定节点</td>
<td>formula要插入的数学公式LaText语法, appointNodes可选指定要插入公式的节点多个节点可以传数组否则默认为当前激活的节点</td>
</tr>
<tr>
<td>INSERT_PARENT_NODEv0.8.0+</td>
<td>给指定的节点插入父节点操作节点为当前激活的节点或指定节点</td>
<td>openEdit是否激活新插入的节点并进入编辑模式默认为<code>true</code> appointNodes可选指定要插入父节点的节点指定多个节点可以传一个数组 appointData可选指定新创建节点的数据比如{text: 'xxx', ...}详细结构可以参考<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js">exampleData.js</a></td>
</tr>
<tr>
<td>REMOVE_CURRENT_NODEv0.8.0+</td>
<td>仅删除当前节点操作节点为当前激活的节点或指定节点</td>
<td>appointNodes可选指定要删除的节点指定多个节点可以传一个数组</td>
</tr>
</tbody>
</table>
<h3>setData(data)</h3>

View File

@@ -12,6 +12,7 @@
```js
{
getImgUrl,// v0.8.0+一个异步函数你可以调用该函数传递一个回调函数回调函数可以接收一个参数代表图片类型的小地图你可以通过img标签进行渲染
svgHTML, // 小地图html
viewBoxStyle, // 视图框的位置信息
miniMapBoxScale, // 视图框的缩放值
@@ -32,7 +33,13 @@ transform-origin: left top;
3.在container内创建一个视口框元素viewBoxContainer绝对定位设置边框样式过渡属性可选
4.监听data_change和view_data_change事件最好也监听一下node_tree_render_end事件防止初次渲染完毕后小地图没有刷新在该事件内调用calculationMiniMap方法获取计算数据然后将返回数据中的svgHTML渲染到miniMapContainer元素内并且给miniMapContainer元素设置或更新如下样式
4.监听data_change和view_data_change事件最好也监听一下node_tree_render_end事件防止初次渲染完毕后小地图没有刷新在该事件内调用calculationMiniMap方法获取计算数据然后将返回数据中的svgHTML渲染到miniMapContainer元素内
```js
miniMapContainer.innerHTML = svgHTML
```
并且给miniMapContainer元素设置或更新如下样式
```js
{
@@ -49,6 +56,14 @@ transform-origin: left top;
插件的完整信息可以参考[miniMap](https://wanglin2.github.io/mind-map/#/doc/zh/miniMap)。
`v0.8.0+`版本之后,`calculationMiniMap`方法会返回`getImgUrl`属性,这是一个异步函数,你可以调用它并传递一个回调函数,回调函数可以接收一个参数,代表小地图图片数据,然后可以通过`img`标签进行渲染,替代前面的`svgHTML`,这样可以减少页面上的节点数量,能优化一定的性能:
```js
getImgUrl(img => {
img.src = img
})
```
## 完整示例
<iframe style="width: 100%; height: 455px; border: none;" src="https://wanglin2.github.io/playground/#eNrFVt2O20QUfpWRASULWSfbUgQhW5UCEkgNQqUSF0yVeu1J4sqesTzjJFWUm4oLhEBcIH6LBFyBhBStEBdsJXgaki1vwTn2jD12stpy1ZU28sz3nZ85c35m6byRJO4sY07fGUg/DRNFJFNZcp3yME5EqsiSpGzcIYIPRcYVCzpETr0oEvPbbExWZJyKmLRAQ6uUGIY8GHpJAVFHwnbEDmPYPYy9hDqUE0J5xBTBPWQeE55FEeWUd7tkc/rF5ofTzaO/N+uzzXe/nn//8Wb917+/fUu5L7hUBH6VF3KWfhgGagqyR73eDvYOCydTBeA1wHK120d/bL8+LZUbCXAhBBfeNIIgAudtoz8HhjML2fymWHygHkRM48tViWoNSPC9knC0B7/FxugSwr098B2BkaijWRJ4ikFEwyGC7QNyfJ0sMYJwpCfrn8/X35RH2n51uv18jRjGFuQ8kNAhdrUZF1z0s8hTIVxosdWuB7TTDCL4klvb/vn79scvN5/YMSQ78XNnXpQxN+Qof2d4C1xAT1w5m+ASRexwFnRDshFLuQltndwA63wM9UV0xOpsiPxFZIAoX2EOFReS14FkgZhzvA9WXUgz0hYTaPuUxGKGFp9CCTIvUJLleXG5iiypFJTF3LYSyipGNjdF3M4hQljUJ4Hws5hx5U6Yejti+HnzwbtBu6UlyxxoHXQKKYxlv9COf9TBDepYW8W2YguF29TZ/nT25NOH5w/PijaBfyutDIn+NIyClHEkf1TpaKjba6Vp6Z/Hn50//qVprG5wj9G7FWbznpEH5lPvGTm4eXVbCPWeCNj7QoZY7SDZiiDzWx3S8uHq4Jru5vTVwetFR65SR/B2C48w8qcenzAQsftQ3hBsKpbt6H/wObg1UiljIzhTwNIR/O7KYIcddIuhBOMIForBJAEKrAgZBOGM+JEn5TF1tPa3oFCok8OaEAYVWuYnUAZdQG2iiaTRWDbB6mZuxKaeAbeq2zCoKhhYrBUDV01GllR4piciIdodqmoOWT41ey11bBYMjr0ci9KX2FmB1chLlXpcjkUa98k9ic20/fyy0V5XB/dqSYlDZqz6zen2Emkli1aDqURiE3HOFbyysBVVq8pTHYgc0V/2fTXuXg8NOyrlOWsDpXHr5bf5GnStBINlriRn3NAvG+q43eI5o9ujy2Ts+lJSp6wh7LsmF039z3Gw9vGh8kLOIyQpqzJlOI1nLAfygsb/55o5a1RVgt6JFFGmCkFzHT29ykNuFrvmp/lk75OXe71kYSzvt/uisRx76SQEu0Zr4gVByCdmo3TdLUvnKX0+Mj5ot6115Xm5ZVy/ZnleRb7+lrvMfpn3hyIN88OhR+hFQ3Mzxy7VfCJSaGx9ciVZEADCgKSTk/aVq692yCuv4X/RdLULRkcUkZ57VRrj0P3yFHQ6TpGA+H5270vB4bmee0A1AAlYDhvI+gySHSeM24VPN4VRH8YMc/XwJBVzCa+0+yChq3TPC72Q3c10lNK+rZzVf9RSHZw=" />

View File

@@ -9,6 +9,7 @@
<p>小地图由两部分组成一个是当前的画布内容一个是视口框当缩放移动元素过多时画布上可能只显示了思维导图的部分内容可以通过视口框来查看当前视口所在位置以及可以通过在小地图上拖动来快速定位</p>
<p>当注册了小地图插件后可以通过<code>mindMap.miniMap</code>获取到插件实例然后通过<code>mindMap.miniMap.calculationMiniMap</code>方法即可获取小地图渲染需要的数据返回的数据结构如下</p>
<pre class="hljs"><code>{
getImgUrl,<span class="hljs-comment">// v0.8.0+一个异步函数你可以调用该函数传递一个回调函数回调函数可以接收一个参数代表图片类型的小地图你可以通过img标签进行渲染</span>
svgHTML, <span class="hljs-comment">// 小地图html</span>
viewBoxStyle, <span class="hljs-comment">// 视图框的位置信息</span>
miniMapBoxScale, <span class="hljs-comment">// 视图框的缩放值</span>
@@ -22,7 +23,10 @@
<pre class="hljs"><code><span class="hljs-attribute">transform-origin</span>: left top;
</code></pre>
<p>3.在container内创建一个视口框元素viewBoxContainer绝对定位设置边框样式过渡属性可选</p>
<p>4.监听data_change和view_data_change事件最好也监听一下node_tree_render_end事件防止初次渲染完毕后小地图没有刷新在该事件内调用calculationMiniMap方法获取计算数据然后将返回数据中的svgHTML渲染到miniMapContainer元素内并且给miniMapContainer元素设置或更新如下样式</p>
<p>4.监听data_change和view_data_change事件最好也监听一下node_tree_render_end事件防止初次渲染完毕后小地图没有刷新在该事件内调用calculationMiniMap方法获取计算数据然后将返回数据中的svgHTML渲染到miniMapContainer元素内</p>
<pre class="hljs"><code>miniMapContainer.innerHTML = svgHTML
</code></pre>
<p>并且给miniMapContainer元素设置或更新如下样式</p>
<pre class="hljs"><code>{
<span class="hljs-attr">transform</span>: <span class="hljs-string">`scale(<span class="hljs-subst">${miniMapBoxScale}</span>)`</span>,
<span class="hljs-attr">left</span>: miniMapBoxLeft + <span class="hljs-string">&#x27;px&#x27;</span>,
@@ -33,6 +37,11 @@
<p>到这一步当画布上的思维导图变化了小地图也会实时更新并且视口框元素会实时反映视口在思维导图图形上的位置</p>
<p>6.监听container元素的mousedownmousemovemouseup事件分别调用小地图插件实例的三个方法即可实现鼠标拖动时画布上的思维导图也随之拖动的效果</p>
<p>插件的完整信息可以参考<a href="https://wanglin2.github.io/mind-map/#/doc/zh/miniMap">miniMap</a></p>
<p><code>v0.8.0+</code>版本之后<code>calculationMiniMap</code>方法会返回<code>getImgUrl</code>属性这是一个异步函数你可以调用它并传递一个回调函数回调函数可以接收一个参数代表小地图图片数据然后可以通过<code>img</code>标签进行渲染替代前面的<code>svgHTML</code>这样可以减少页面上的节点数量能优化一定的性能</p>
<pre class="hljs"><code>getImgUrl(<span class="hljs-function"><span class="hljs-params">img</span> =&gt;</span> {
img.src = img
})
</code></pre>
<h2>完整示例</h2>
<iframe style="width: 100%; height: 455px; border: none;" src="https://wanglin2.github.io/playground/#eNrFVt2O20QUfpWRASULWSfbUgQhW5UCEkgNQqUSF0yVeu1J4sqesTzjJFWUm4oLhEBcIH6LBFyBhBStEBdsJXgaki1vwTn2jD12stpy1ZU28sz3nZ85c35m6byRJO4sY07fGUg/DRNFJFNZcp3yME5EqsiSpGzcIYIPRcYVCzpETr0oEvPbbExWZJyKmLRAQ6uUGIY8GHpJAVFHwnbEDmPYPYy9hDqUE0J5xBTBPWQeE55FEeWUd7tkc/rF5ofTzaO/N+uzzXe/nn//8Wb917+/fUu5L7hUBH6VF3KWfhgGagqyR73eDvYOCydTBeA1wHK120d/bL8+LZUbCXAhBBfeNIIgAudtoz8HhjML2fymWHygHkRM48tViWoNSPC9knC0B7/FxugSwr098B2BkaijWRJ4ikFEwyGC7QNyfJ0sMYJwpCfrn8/X35RH2n51uv18jRjGFuQ8kNAhdrUZF1z0s8hTIVxosdWuB7TTDCL4klvb/vn79scvN5/YMSQ78XNnXpQxN+Qof2d4C1xAT1w5m+ASRexwFnRDshFLuQltndwA63wM9UV0xOpsiPxFZIAoX2EOFReS14FkgZhzvA9WXUgz0hYTaPuUxGKGFp9CCTIvUJLleXG5iiypFJTF3LYSyipGNjdF3M4hQljUJ4Hws5hx5U6Yejti+HnzwbtBu6UlyxxoHXQKKYxlv9COf9TBDepYW8W2YguF29TZ/nT25NOH5w/PijaBfyutDIn+NIyClHEkf1TpaKjba6Vp6Z/Hn50//qVprG5wj9G7FWbznpEH5lPvGTm4eXVbCPWeCNj7QoZY7SDZiiDzWx3S8uHq4Jru5vTVwetFR65SR/B2C48w8qcenzAQsftQ3hBsKpbt6H/wObg1UiljIzhTwNIR/O7KYIcddIuhBOMIForBJAEKrAgZBOGM+JEn5TF1tPa3oFCok8OaEAYVWuYnUAZdQG2iiaTRWDbB6mZuxKaeAbeq2zCoKhhYrBUDV01GllR4piciIdodqmoOWT41ey11bBYMjr0ci9KX2FmB1chLlXpcjkUa98k9ic20/fyy0V5XB/dqSYlDZqz6zen2Emkli1aDqURiE3HOFbyysBVVq8pTHYgc0V/2fTXuXg8NOyrlOWsDpXHr5bf5GnStBINlriRn3NAvG+q43eI5o9ujy2Ts+lJSp6wh7LsmF039z3Gw9vGh8kLOIyQpqzJlOI1nLAfygsb/55o5a1RVgt6JFFGmCkFzHT29ykNuFrvmp/lk75OXe71kYSzvt/uisRx76SQEu0Zr4gVByCdmo3TdLUvnKX0+Mj5ot6115Xm5ZVy/ZnleRb7+lrvMfpn3hyIN88OhR+hFQ3Mzxy7VfCJSaGx9ciVZEADCgKSTk/aVq692yCuv4X/RdLULRkcUkZ57VRrj0P3yFHQ6TpGA+H5270vB4bmee0A1AAlYDhvI+gySHSeM24VPN4VRH8YMc/XwJBVzCa+0+yChq3TPC72Q3c10lNK+rZzVf9RSHZw=" />
</div>

View File

@@ -6,6 +6,16 @@
该插件用于支持给节点插入公式。
> 注意:公式是通过[KaTeX](https://github.com/KaTeX/KaTeX)库实现的,`KaTeX`提供了一些配置,插件默认的一个配置是:
```js
{
output: 'mathml'
}
```
> 这在少数浏览器上公式可能无法成功渲染,如果你需要兼容这部分浏览器,你可以考虑把该配置改为`html`,详细文档可以参考:[Options](https://katex.org/docs/options)。使用这个配置可能还需要再引入`KaTeX`的样式文件,你可以自行测试。
## 注册
```js

View File

@@ -8,6 +8,16 @@
<p>该插件仅在富文本模式下支持所以需要在注册了RichText插件的前提下使用</p>
</blockquote>
<p>该插件用于支持给节点插入公式</p>
<blockquote>
<p>注意公式是通过<a href="https://github.com/KaTeX/KaTeX">KaTeX</a>库实现的<code>KaTeX</code>提供了一些配置插件默认的一个配置是</p>
</blockquote>
<pre class="hljs"><code>{
<span class="hljs-attr">output</span>: <span class="hljs-string">&#x27;mathml&#x27;</span>
}
</code></pre>
<blockquote>
<p>这在少数浏览器上公式可能无法成功渲染如果你需要兼容这部分浏览器你可以考虑把该配置改为<code>html</code>详细文档可以参考<a href="https://katex.org/docs/options">Options</a>使用这个配置可能还需要再引入<code>KaTeX</code>的样式文件你可以自行测试</p>
</blockquote>
<h2>注册</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> Formula <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/Formula.js&#x27;</span>

View File

@@ -56,11 +56,13 @@
[探索如何将html和svg导出为图片](https://juejin.cn/post/7276712861514170409)
[dom-to-image库是如何将html转换成图片的](https://juejin.cn/post/7287913415803764747)
## 特别说明
本项目可用于学习和参考,用于实际项目时请先深度体验一下是否能满足您的需求。
本项目可能没有完整测试到每一个功能点所以可能存在bug另外当节点数量非常多的时候性能也存在一些问题因为每个人能接受的卡顿程度不一样所以你可以自行测试节点数量上限。
本项目可能没有完整测试到每一个功能点所以可能存在bug另外当节点数量非常多的时候性能也存在一些问题因为每个人能接受的卡顿程度不一样所以你可以自行测试节点数量上限。一般来说500个节点以内比较流畅1000个节点以上卡顿比较明显。
如果有建议或发现了bug可以在此提交[issues](https://github.com/wanglin2/mind-map/issues)。
@@ -216,4 +218,12 @@
<img src="../../../../assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>达仁科技</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 style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/天清如愿.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>天清如愿</p>
</div>
</div>

View File

@@ -8,17 +8,17 @@
</blockquote>
<h2>特性</h2>
<ul>
<li><input type="checkbox" id="checkbox32" checked="true" /><label for="checkbox32">插件化架构除核心功能外其他功能作为插件提供按需使用减小打包体积</label></li>
<li><input type="checkbox" id="checkbox33" checked="true" /><label for="checkbox33">支持逻辑结构图思维导图组织结构图目录组织图时间轴横向竖向鱼骨图等结构</label></li>
<li><input type="checkbox" id="checkbox34" checked="true" /><label for="checkbox34">内置多种主题允许高度自定义样式支持注册新主题</label></li>
<li><input type="checkbox" id="checkbox35" checked="true" /><label for="checkbox35">节点内容支持文本普通文本富文本图片图标超链接备注标签概要数学公式</label></li>
<li><input type="checkbox" id="checkbox36" checked="true" /><label for="checkbox36">节点支持拖拽拖拽移动自由调整多种节点形状支持使用 DDM 完全自定义节点内容</label></li>
<li><input type="checkbox" id="checkbox37" checked="true" /><label for="checkbox37">支持画布拖动缩放</label></li>
<li><input type="checkbox" id="checkbox38" checked="true" /><label for="checkbox38">支持鼠标按键拖动选择和Ctrl+左键两种多选节点方式</label></li>
<li><input type="checkbox" id="checkbox39" checked="true" /><label for="checkbox39">支持导出为</label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code><code>xmind</code>支持从<code>json</code><code>xmind</code><code>markdown</code>导入</li>
<li><input type="checkbox" id="checkbox40" checked="true" /><label for="checkbox40">支持快捷键前进后退关联线搜索替换小地图水印滚动条</label></li>
<li><input type="checkbox" id="checkbox41" checked="true" /><label for="checkbox41">提供丰富的配置满足各种场景各种使用习惯</label></li>
<li><input type="checkbox" id="checkbox42" checked="true" /><label for="checkbox42">支持协同编辑</label></li>
<li><input type="checkbox" id="checkbox0" checked="true" /><label for="checkbox0">插件化架构除核心功能外其他功能作为插件提供按需使用减小打包体积</label></li>
<li><input type="checkbox" id="checkbox1" checked="true" /><label for="checkbox1">支持逻辑结构图思维导图组织结构图目录组织图时间轴横向竖向鱼骨图等结构</label></li>
<li><input type="checkbox" id="checkbox2" checked="true" /><label for="checkbox2">内置多种主题允许高度自定义样式支持注册新主题</label></li>
<li><input type="checkbox" id="checkbox3" checked="true" /><label for="checkbox3">节点内容支持文本普通文本富文本图片图标超链接备注标签概要数学公式</label></li>
<li><input type="checkbox" id="checkbox4" checked="true" /><label for="checkbox4">节点支持拖拽拖拽移动自由调整多种节点形状支持使用 DDM 完全自定义节点内容</label></li>
<li><input type="checkbox" id="checkbox5" checked="true" /><label for="checkbox5">支持画布拖动缩放</label></li>
<li><input type="checkbox" id="checkbox6" checked="true" /><label for="checkbox6">支持鼠标按键拖动选择和Ctrl+左键两种多选节点方式</label></li>
<li><input type="checkbox" id="checkbox7" checked="true" /><label for="checkbox7">支持导出为</label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code><code>xmind</code>支持从<code>json</code><code>xmind</code><code>markdown</code>导入</li>
<li><input type="checkbox" id="checkbox8" checked="true" /><label for="checkbox8">支持快捷键前进后退关联线搜索替换小地图水印滚动条</label></li>
<li><input type="checkbox" id="checkbox9" checked="true" /><label for="checkbox9">提供丰富的配置满足各种场景各种使用习惯</label></li>
<li><input type="checkbox" id="checkbox10" checked="true" /><label for="checkbox10">支持协同编辑</label></li>
</ul>
<h2>仓库目录介绍</h2>
<p>1.<code>simple-mind-map</code></p>
@@ -26,11 +26,11 @@
<p>2.<code>web</code></p>
<p>使用<code>simple-mind-map</code>基于<code>vue2.x</code><code>ElementUI</code>搭建的在线思维导图特性</p>
<ul>
<li><input type="checkbox" id="checkbox43" checked="true" /><label for="checkbox43">工具栏支持插入节点删除节点编辑节点图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox44" checked="true" /><label for="checkbox44">侧边栏基础样式设置面板节点样式设置面板大纲面板主题选择面板结构选择面板</label></li>
<li><input type="checkbox" id="checkbox45" checked="true" /><label for="checkbox45">导入导出功能数据默认保存在浏览器本地存储也支持直接创建打开编辑电脑本地文件</label></li>
<li><input type="checkbox" id="checkbox46" checked="true" /><label for="checkbox46">右键菜单支持展开收起整理布局等操作</label></li>
<li><input type="checkbox" id="checkbox47" checked="true" /><label for="checkbox47">底部栏支持节点数量字数统计支持切换编辑和只读模式支持放大缩小支持全屏切换支持小地图</label></li>
<li><input type="checkbox" id="checkbox11" checked="true" /><label for="checkbox11">工具栏支持插入节点删除节点编辑节点图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox12" checked="true" /><label for="checkbox12">侧边栏基础样式设置面板节点样式设置面板大纲面板主题选择面板结构选择面板</label></li>
<li><input type="checkbox" id="checkbox13" checked="true" /><label for="checkbox13">导入导出功能数据默认保存在浏览器本地存储也支持直接创建打开编辑电脑本地文件</label></li>
<li><input type="checkbox" id="checkbox14" checked="true" /><label for="checkbox14">右键菜单支持展开收起整理布局等操作</label></li>
<li><input type="checkbox" id="checkbox15" checked="true" /><label for="checkbox15">底部栏支持节点数量字数统计支持切换编辑和只读模式支持放大缩小支持全屏切换支持小地图</label></li>
</ul>
<p>提供文档页面服务</p>
<p>3.<code>dist</code></p>
@@ -42,9 +42,10 @@
<p><a href="https://juejin.cn/post/7204854015463538744">如何在canvas中模拟css的背景图片样式</a></p>
<p><a href="https://juejin.cn/post/7233012756314701884">我的第一个Electron应用</a></p>
<p><a href="https://juejin.cn/post/7276712861514170409">探索如何将html和svg导出为图片</a></p>
<p><a href="https://juejin.cn/post/7287913415803764747">dom-to-image库是如何将html转换成图片的</a></p>
<h2>特别说明</h2>
<p>本项目可用于学习和参考用于实际项目时请先深度体验一下是否能满足您的需求</p>
<p>本项目可能没有完整测试到每一个功能点所以可能存在bug另外当节点数量非常多的时候性能也存在一些问题因为每个人能接受的卡顿程度不一样所以你可以自行测试节点数量上限</p>
<p>本项目可能没有完整测试到每一个功能点所以可能存在bug另外当节点数量非常多的时候性能也存在一些问题因为每个人能接受的卡顿程度不一样所以你可以自行测试节点数量上限一般来说500个节点以内比较流畅1000个节点以上卡顿比较明显</p>
<p>如果有建议或发现了bug可以在此提交<a href="https://github.com/wanglin2/mind-map/issues">issues</a></p>
<p>项目内置的主题和图标部分来自于</p>
<p><a href="https://naotu.baidu.com/">百度脑图</a></p>
@@ -175,6 +176,14 @@
<img src="../../../../assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>达仁科技</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 style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/天清如愿.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>天清如愿</p>
</div>
</div>
</div>
</template>

View File

@@ -30,7 +30,8 @@ MindMap.usePlugin(MiniMap)
```js
{
svgHTML, // 小地图html
getImgUrl,// v0.8.0+一个异步函数你可以调用该函数传递一个回调函数回调函数可以接收一个参数代表图片类型的小地图你可以通过img标签进行渲染
svgHTML, // 小地图html推荐使用getImgUrl方式获取图片类型的小地图减少页面DOM数量优化性能
viewBoxStyle, // 视图框的位置信息
miniMapBoxScale, // 视图框的缩放值
miniMapBoxLeft, // 视图框的left值

View File

@@ -20,7 +20,8 @@ MindMap.usePlugin(MiniMap)
<p><code>boxHeight</code>小地图容器的高度</p>
<p>函数返回内容</p>
<pre class="hljs"><code>{
svgHTML, <span class="hljs-comment">// 小地图html</span>
getImgUrl,<span class="hljs-comment">// v0.8.0+一个异步函数你可以调用该函数传递一个回调函数回调函数可以接收一个参数代表图片类型的小地图你可以通过img标签进行渲染</span>
svgHTML, <span class="hljs-comment">// 小地图html推荐使用getImgUrl方式获取图片类型的小地图减少页面DOM数量优化性能</span>
viewBoxStyle, <span class="hljs-comment">// 视图框的位置信息</span>
miniMapBoxScale, <span class="hljs-comment">// 视图框的缩放值</span>
miniMapBoxLeft, <span class="hljs-comment">// 视图框的left值</span>

View File

@@ -56,6 +56,14 @@
## 方法
### updateNodeByActive(active)
> v0.8.0+
- `active`Boolean激活状态。
根据是否激活更新节点。主要是更新节点的展开收起按钮的显示隐藏。
### setOpacity(val)
> v0.7.2+

View File

@@ -31,6 +31,14 @@
</blockquote>
<p>节点是否正在拖拽中</p>
<h2>方法</h2>
<h3>updateNodeByActive(active)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<ul>
<li><code>active</code>Boolean激活状态</li>
</ul>
<p>根据是否激活更新节点主要是更新节点的展开收起按钮的显示隐藏</p>
<h3>setOpacity(val)</h3>
<blockquote>
<p>v0.7.2+</p>

View File

@@ -1,19 +1,25 @@
# Render实例
`render`实例负载整个渲染过程,可通过`mindMap.renderer`获取到
`render`实例负载整个渲染过程,可通过`mindMap.renderer`获取到
## 属性
### activeNodeList
获取当前激活的节点列表
获取当前激活的节点列表
### root
获取节点树的根节点
获取节点树的根节点
## 方法
### setRootNodeCenter()
> v0.8.0+
回到中心主题,即设置根节点到画布中心。
### setData(data)
> v0.7.3+
@@ -22,67 +28,103 @@
### clearActive()
清除当前激活的节点
> v0.8.0+已废弃
清除当前激活的节点。
### clearAllActive()
清除当前所有激活节点,并会触发`node_active`事件
> v0.8.0+已废弃
清除当前所有激活节点,并会触发`node_active`事件 。
### clearActiveNode()
> v0.8.0+
清除当前所有激活节点,并会触发`node_active`事件 。
### clearActiveNodeList()
> v0.8.0+
清除当前激活的节点列表。不会触发`node_active`事件 。
### startTextEdit()
v0.1.6+)若有文字编辑需求可调用该方法,会禁用回车键和删除键相关快捷键防止冲突
> v0.1.6+
若有文字编辑需求可调用该方法,会禁用回车键和删除键相关快捷键防止冲突 。
### endTextEdit()
v0.1.6+)结束文字编辑,会恢复回车键和删除键相关快捷键
> v0.1.6+
结束文字编辑,会恢复回车键和删除键相关快捷键。
### addActiveNode(node)
添加节点到激活列表里
> v0.8.0+已废弃
添加节点到激活列表里。
### addNodeToActiveList(node)
> v0.8.0+
添加节点到激活列表里。
### removeActiveNode(node)
在激活列表里移除某个节点
> v0.8.0+已废弃
在激活列表里移除某个节点。
### removeNodeFromActiveList(node)
> v0.8.0+
在激活列表里移除某个节点。
### findActiveNodeIndex(node)
检索某个节点在激活列表里的索引
检索某个节点在激活列表里的索引
### getNodeIndex(node)
获取节点在同级里的位置索引
获取节点在同级里的位置索引
### removeOneNode(node)
删除某个指定节点
删除某个指定节点
### copyNode()
复制节点,操作节点为当前激活节点,有多个激活节点只会操作第一个节点
复制节点,操作节点为当前激活节点,有多个激活节点只会操作第一个节点
### setNodeDataRender(node, data, notRender)
- `notRender`v0.6.9+`Boolean`,默认为`false`,是否不要触发渲染。
设置节点数据,即`data`字段的数据,并会根据节点大小是否变化来判断是否需要重新渲染该节点,`data`为对象,如:`{text: '我是新文本'}`
设置节点数据,即`data`字段的数据,并会根据节点大小是否变化来判断是否需要重新渲染该节点,`data`为对象,如:`{text: '我是新文本'}`
### moveNodeTo(node, toNode)
> v0.1.5+
移动一个节点作为另一个节点的子节点
移动一个节点作为另一个节点的子节点
### insertBefore(node, exist)
> v0.1.5+
将节点移动到另一个节点的前面
将节点移动到另一个节点的前面
### insertAfter(node, exist)
> v0.1.5+
将节点移动到另一个节点的后面
将节点移动到另一个节点的后面
### moveNodeToCenter(node)

View File

@@ -1,58 +1,101 @@
<template>
<div>
<h1>Render实例</h1>
<p><code>render</code>实例负载整个渲染过程可通过<code>mindMap.renderer</code>获取到</p>
<p><code>render</code>实例负载整个渲染过程可通过<code>mindMap.renderer</code>获取到</p>
<h2>属性</h2>
<h3>activeNodeList</h3>
<p>获取当前激活的节点列表</p>
<p>获取当前激活的节点列表</p>
<h3>root</h3>
<p>获取节点树的根节点</p>
<p>获取节点树的根节点</p>
<h2>方法</h2>
<h3>setRootNodeCenter()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>回到中心主题即设置根节点到画布中心</p>
<h3>setData(data)</h3>
<blockquote>
<p>v0.7.3+</p>
</blockquote>
<p>动态设置思维导图数据</p>
<h3>clearActive()</h3>
<p>清除当前激活的节点</p>
<blockquote>
<p>v0.8.0+已废弃</p>
</blockquote>
<p>清除当前激活的节点</p>
<h3>clearAllActive()</h3>
<p>清除当前所有激活节点并会触发<code>node_active</code>事件</p>
<blockquote>
<p>v0.8.0+已废弃</p>
</blockquote>
<p>清除当前所有激活节点并会触发<code>node_active</code>事件 </p>
<h3>clearActiveNode()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>清除当前所有激活节点并会触发<code>node_active</code>事件 </p>
<h3>clearActiveNodeList()</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>清除当前激活的节点列表不会触发<code>node_active</code>事件 </p>
<h3>startTextEdit()</h3>
<p>v0.1.6+若有文字编辑需求可调用该方法会禁用回车键和删除键相关快捷键防止冲突</p>
<blockquote>
<p>v0.1.6+</p>
</blockquote>
<p>若有文字编辑需求可调用该方法会禁用回车键和删除键相关快捷键防止冲突 </p>
<h3>endTextEdit()</h3>
<p>v0.1.6+结束文字编辑会恢复回车键和删除键相关快捷键</p>
<blockquote>
<p>v0.1.6+</p>
</blockquote>
<p>结束文字编辑会恢复回车键和删除键相关快捷键</p>
<h3>addActiveNode(node)</h3>
<p>添加节点到激活列表里</p>
<blockquote>
<p>v0.8.0+已废弃</p>
</blockquote>
<p>添加节点到激活列表里</p>
<h3>addNodeToActiveList(node)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>添加节点到激活列表里</p>
<h3>removeActiveNode(node)</h3>
<p>在激活列表里移除某个节点</p>
<blockquote>
<p>v0.8.0+已废弃</p>
</blockquote>
<p>在激活列表里移除某个节点</p>
<h3>removeNodeFromActiveList(node)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>在激活列表里移除某个节点</p>
<h3>findActiveNodeIndex(node)</h3>
<p>检索某个节点在激活列表里的索引</p>
<p>检索某个节点在激活列表里的索引</p>
<h3>getNodeIndex(node)</h3>
<p>获取节点在同级里的位置索引</p>
<p>获取节点在同级里的位置索引</p>
<h3>removeOneNode(node)</h3>
<p>删除某个指定节点</p>
<p>删除某个指定节点</p>
<h3>copyNode()</h3>
<p>复制节点操作节点为当前激活节点有多个激活节点只会操作第一个节点</p>
<p>复制节点操作节点为当前激活节点有多个激活节点只会操作第一个节点</p>
<h3>setNodeDataRender(node, data, notRender)</h3>
<ul>
<li><code>notRender</code>v0.6.9+<code>Boolean</code>默认为<code>false</code>是否不要触发渲染</li>
</ul>
<p>设置节点数据<code>data</code>字段的数据并会根据节点大小是否变化来判断是否需要重新渲染该节点<code>data</code>为对象<code>{text: '我是新文本'}</code></p>
<p>设置节点数据<code>data</code>字段的数据并会根据节点大小是否变化来判断是否需要重新渲染该节点<code>data</code>为对象<code>{text: '我是新文本'}</code></p>
<h3>moveNodeTo(node, toNode)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>移动一个节点作为另一个节点的子节点</p>
<p>移动一个节点作为另一个节点的子节点</p>
<h3>insertBefore(node, exist)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>将节点移动到另一个节点的前面</p>
<p>将节点移动到另一个节点的前面</p>
<h3>insertAfter(node, exist)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>将节点移动到另一个节点的后面</p>
<p>将节点移动到另一个节点的后面</p>
<h3>moveNodeToCenter(node)</h3>
<blockquote>
<p>v0.2.17+</p>

View File

@@ -2,8 +2,6 @@
> v0.4.0+
> 注意:这是一个测试性质和不完善的功能
该插件提供节点富文本编辑的能力,注册了即可生效。
默认节点编辑只能对节点内所有文本统一应用样式,通过该插件可以支持富文本编辑的效果,目前支持:加粗、斜体、下划线、删除线、字体、字号、颜色、背景颜色。不支持上划线、行高。
@@ -20,6 +18,8 @@
> dom-to-image-more兼容性比较差在很多浏览器上导出图片都是空的所以可以根据你自己的需求替换成html2canvas。
`0.6.16+`版本后不再使用`dom-to-image-more``html2canvas`之类的第三方库实现导出,兼容性及导出都不再有问题。
## 注册
```js
@@ -68,6 +68,14 @@ MindMap.usePlugin(RichText, opt?)
## 方法
### setNotActiveNodeStyle(node, style)
> v0.8.0+
- `style`Object样式对象。
给未激活的节点设置富文本样式。
### selectAll()
选中全部。当节点正在编辑中可以通过该方法选中节点内的所有文本。

View File

@@ -4,9 +4,6 @@
<blockquote>
<p>v0.4.0+</p>
</blockquote>
<blockquote>
<p>注意这是一个测试性质和不完善的功能</p>
</blockquote>
<p>该插件提供节点富文本编辑的能力注册了即可生效</p>
<p>默认节点编辑只能对节点内所有文本统一应用样式通过该插件可以支持富文本编辑的效果目前支持加粗斜体下划线删除线字体字号颜色背景颜色不支持上划线行高</p>
<p>该插件的原理是使用<a href="https://github.com/quilljs/quill">Quill</a>编辑器实现富文本编辑然后把编辑后生成的<code>DOM</code>节点直接作为节点的文本数据并且在渲染的时候通过<code>svg</code><code>foreignObject</code>标签嵌入<code>DOM</code>节点</p>
@@ -21,6 +18,7 @@
<blockquote>
<p>dom-to-image-more兼容性比较差在很多浏览器上导出图片都是空的所以可以根据你自己的需求替换成html2canvas</p>
</blockquote>
<p><code>0.6.16+</code>版本后不再使用<code>dom-to-image-more</code><code>html2canvas</code>之类的第三方库实现导出兼容性及导出都不再有问题</p>
<h2>注册</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> RichText <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/RichText.js&#x27;</span>
@@ -58,6 +56,14 @@ MindMap.usePlugin(RichText, opt?)
<pre class="hljs"><code>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, ..<span class="hljs-number">.100</span>]
</code></pre>
<h2>方法</h2>
<h3>setNotActiveNodeStyle(node, style)</h3>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<ul>
<li><code>style</code>Object样式对象</li>
</ul>
<p>给未激活的节点设置富文本样式</p>
<h3>selectAll()</h3>
<p>选中全部当节点正在编辑中可以通过该方法选中节点内的所有文本</p>
<h3>focus()</h3>

View File

@@ -1,6 +1,6 @@
# Select 插件
`Select`插件提供鼠标右键多选节点的功能。
`Select`插件提供鼠标多选节点的功能。
## 注册

View File

@@ -1,7 +1,7 @@
<template>
<div>
<h1>Select 插件</h1>
<p><code>Select</code>插件提供鼠标右键多选节点的功能</p>
<p><code>Select</code>插件提供鼠标多选节点的功能</p>
<h2>注册</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map&#x27;</span>
<span class="hljs-keyword">import</span> Select <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/plugins/Select.js&#x27;</span>

View File

@@ -358,6 +358,45 @@ copyNodeTree({}, node)
判断两个对象是否相同,只处理对象或数组。
#### getNodeDataIndex(node)
> v0.8.0+
获取节点在兄弟节点中的位置索引。
#### getNodeIndexInNodeList(node, nodeList)
> v0.8.0+
从一个节点列表里找出某个节点的索引。
#### setDataToClipboard(data)
> v0.8.0+
- `data`Object | Array
将数据设置到用户剪切板中。
#### getDataFromClipboard()
> v0.8.0+
从用户剪贴板中读取文字和图片,返回:
```js
{
text,
img
}
```
#### removeFromParentNodeData(node)
> v0.8.0+
从节点的父节点的`nodeData.children`列表中移除该节点的数据。
## 在canvas中模拟css的背景属性
引入:

View File

@@ -290,6 +290,39 @@
<li><code>a</code><code>b</code>Object | Array, 要进行对比的两个对象</li>
</ul>
<p>判断两个对象是否相同只处理对象或数组</p>
<h4>getNodeDataIndex(node)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>获取节点在兄弟节点中的位置索引</p>
<h4>getNodeIndexInNodeList(node, nodeList)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>从一个节点列表里找出某个节点的索引</p>
<h4>setDataToClipboard(data)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<ul>
<li><code>data</code>Object | Array</li>
</ul>
<p>将数据设置到用户剪切板中</p>
<h4>getDataFromClipboard()</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>从用户剪贴板中读取文字和图片返回</p>
<pre class="hljs"><code>{
text,
img
}
</code></pre>
<h4>removeFromParentNodeData(node)</h4>
<blockquote>
<p>v0.8.0+</p>
</blockquote>
<p>从节点的父节点的<code>nodeData.children</code>列表中移除该节点的数据</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>

Some files were not shown because too many files have changed in this diff Show More