Compare commits

...

33 Commits

Author SHA1 Message Date
wanglin2
8441986ca7 修改文档 2023-02-20 19:25:59 +08:00
wanglin2
c8b50908e1 Fix:修复底边风格的情况下,节点高度过高会和其他节点重叠的问题 2023-02-20 15:10:27 +08:00
wanglin2
7e11fde892 打包0.3.4 2023-02-20 11:30:15 +08:00
wanglin2
3d9f3bd7a8 Fix:修复批量删除的节点中如果存在根节点会出现删除异常的问题 2023-02-20 11:26:26 +08:00
wanglin2
46e11649b0 优化节点文本编辑 2023-02-20 11:02:04 +08:00
wanglin2
161ef7590c feature:节点文本增加自动换行功能 2023-02-20 10:14:34 +08:00
wanglin2
ca660a3c74 打包0.3.3 2023-02-14 09:38:38 +08:00
wanglin2
7a24872331 Fix:修复根节点无法黄的问题 2023-02-14 09:35:56 +08:00
wanglin2
eb4ea9fb3a 打包0.3.2 2023-02-03 11:13:58 +08:00
wanglin2
64228c02ff 修复当思维导图实际内容大于屏幕宽高时,导出的时候超出的部分没有绘制水印的问题 2023-02-03 11:09:43 +08:00
wanglin2
f184a5d948 修复二级节点拖拽到其他节点或其他节点拖拽到二级节点时节点样式没有更新的问题 2023-02-03 10:40:06 +08:00
wanglin2
0bf5b2d6f7 打包0.3.1 2023-02-01 16:30:29 +08:00
wanglin2
74a52ea386 导出d的图片支持background-size/position/repeat属性效果 2023-02-01 16:25:57 +08:00
wanglin2
9914eef5b9 修改文档 2023-01-31 15:06:24 +08:00
wanglin2
f547f741f2 1.修复删除背景图片不生效的问题;2.背景图片展示增加位置和大小设置;3.修复节点拖拽到根节点时连接线跑到根节点上方的问题 2023-01-31 15:04:38 +08:00
wanglin2
c26149fa42 修复demo设置了水印页面刷新后生效的问题 2023-01-31 13:35:46 +08:00
wanglin2
b2aa3648eb 支持当修改文档后自动重新编译生成vue组件 2023-01-31 11:15:34 +08:00
wanglin2
b997604ab2 打包0.3.0 2023-01-17 17:39:37 +08:00
wanglin2
c6929b82ad 插件化架构基本升级完成 2023-01-17 16:53:28 +08:00
wanglin2
b40da53aef 只读模式下禁止添加历史记录、禁止响应前进后退操作 2023-01-17 15:49:23 +08:00
wanglin2
1e5dfd97e1 提取多选节点Select类作为插件 2023-01-17 15:26:35 +08:00
wanglin2
97bcc22abd 提取导出类作为插件 2023-01-17 15:21:02 +08:00
wanglin2
bd655839cb 提取出键盘导航类作为插件 2023-01-17 14:39:43 +08:00
wanglin2
a53a4e8e1d 提取拖拽类Drag作为插件 2023-01-17 14:30:13 +08:00
wanglin2
eb01646747 提取水印类作为插件 2023-01-17 14:11:07 +08:00
wanglin2
d27eee0fae 开始进行插件化转换,即提取非核心的类作为插件,不再内置;抽取出校地图类插件 2023-01-17 13:56:57 +08:00
wanglin2
92ee241ed8 将xmind解析方法从MindMap类上移除,改为按需引入方式使用 2023-01-17 11:19:59 +08:00
wanglin2
660906e4c5 【春节快乐】修改文档 2023-01-17 10:55:28 +08:00
wanglin2
971f0d3446 打包0.2.24 2023-01-17 09:24:33 +08:00
wanglin2
63eccede7f 完成水印功能、新增水印文档 2023-01-17 09:20:37 +08:00
wanglin2
5bd6adb488 节点自由拖拽支持配置是否开启 2023-01-14 16:26:51 +08:00
wanglin2
fbdc5e0f39 完善文档 2023-01-14 14:44:53 +08:00
wanglin2
5e994322fe 全新文档 2023-01-14 11:33:05 +08:00
134 changed files with 12233 additions and 5567 deletions

1339
README.md

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
<!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,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.2d06497a.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.3d234c87.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.94891485.css" rel="preload" as="style"><link href="dist/js/app.54d858bc.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.6cac1a4d.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.94891485.css" rel="stylesheet"><link href="dist/css/app.3d234c87.css" 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 src="dist/js/chunk-vendors.6cac1a4d.js"></script><script src="dist/js/app.54d858bc.js"></script></body></html>
<!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,initial-scale=1"><link rel="icon" href="./dist/logo.png"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d0a3179.f79d6590.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.d901cc5e.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.ae72a285.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.13ffd981.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.f7178a38.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.64d433a0.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.de98db92.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.a48a667e.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.e56450f0.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.1531a230.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.d2768274.js" rel="prefetch"><link href="dist/js/chunk-2d0c14fc.a89a80b4.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.647d892f.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.6fb13561.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.49c23f9e.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.adce6374.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.2e0766e2.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.ec79eebe.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.19efc7d4.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.6a579f6f.js" rel="prefetch"><link href="dist/js/chunk-2d0ddf37.d162efb9.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.21eae2e6.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.f3a9c7fc.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.57926a64.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.2b202405.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.a2d22497.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.67441d40.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.fe227afb.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.9df071bd.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.acd7e356.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.5a4025ce.js" rel="prefetch"><link href="dist/js/chunk-2d216004.905379d5.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.2d06497a.js" rel="prefetch"><link href="dist/js/chunk-2d217907.cc6917a4.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.3703455b.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.ffd15e65.js" rel="prefetch"><link href="dist/js/chunk-2d22bd06.cace3b1b.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.fb49d06b.js" rel="prefetch"><link href="dist/js/chunk-2d238428.266d8f0c.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.8a532989.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.1790fe42.css" rel="preload" as="style"><link href="dist/js/app.52520638.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.7f3b8358.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.1790fe42.css" rel="stylesheet"><link href="dist/css/app.8a532989.css" 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 src="dist/js/chunk-vendors.7f3b8358.js"></script><script src="dist/js/app.52520638.js"></script></body></html>

View File

@@ -925,5 +925,6 @@ export default {
"layout": "logicalStructure",
// "layout": "mindMap",
// "layout": "catalogOrganization"
// "layout": "organizationStructure"
// "layout": "organizationStructure",
"config": {}
}

View File

@@ -62,5 +62,6 @@
"sx": 0,
"sy": 0
}
}
},
"config": {}
}

20
simple-mind-map/full.js Normal file
View File

@@ -0,0 +1,20 @@
import MindMap from './index'
import MiniMap from './src/MiniMap.js'
import Watermark from './src/Watermark.js'
import KeyboardNavigation from './src/KeyboardNavigation.js'
import Export from './src/Export.js'
import Drag from './src/Drag.js'
import Select from './src/Select.js'
import xmind from './src/parse/xmind.js'
MindMap.xmind = xmind
MindMap
.usePlugin(MiniMap)
.usePlugin(Watermark)
.usePlugin(Drag)
.usePlugin(KeyboardNavigation)
.usePlugin(Export)
.usePlugin(Select)
export default MindMap

View File

@@ -7,15 +7,9 @@ import Style from './src/Style'
import KeyCommand from './src/KeyCommand'
import Command from './src/Command'
import BatchExecution from './src/BatchExecution'
import Export from './src/Export'
import Select from './src/Select'
import Drag from './src/Drag'
import MiniMap from './src/MiniMap'
import { layoutValueList } from './src/utils/constant'
import { SVG } from '@svgdotjs/svg.js'
import xmind from './src/parse/xmind'
import { simpleDeepClone } from './src/utils'
import KeyboardNavigation from './src/KeyboardNavigation'
import defaultTheme from './src/themes/default'
// 默认选项配置
@@ -45,13 +39,29 @@ const defaultOpt = {
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null
customNoteContentShow: null,
/*
{
show(){},
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
},
// 达到该宽度文本自动换行
textAutoWrapWidth: 500
}
// 思维导图
@@ -105,34 +115,16 @@ class MindMap {
draw: this.draw
})
// 小地图类
this.miniMap = new MiniMap({
mindMap: this
})
// 导出类
this.doExport = new Export({
mindMap: this
})
// 选择类
this.select = new Select({
mindMap: this
})
// 拖动类
this.drag = new Drag({
mindMap: this
})
// 键盘导航类
this.keyboardNavigation = new KeyboardNavigation({
mindMap: this
})
// 批量执行类
this.batchExecution = new BatchExecution()
// 注册插件
MindMap.pluginList.forEach((plugin) => {
this[plugin.instanceName] = new plugin({
mindMap: this
})
})
// 初始渲染
this.reRender()
setTimeout(() => {
@@ -152,21 +144,21 @@ class MindMap {
}
// 渲染,部分渲染
render() {
render(callback) {
this.batchExecution.push('render', () => {
this.initTheme()
this.renderer.reRender = false
this.renderer.render()
this.renderer.render(callback)
})
}
// 重新渲染
reRender() {
reRender(callback) {
this.batchExecution.push('render', () => {
this.draw.clear()
this.initTheme()
this.renderer.reRender = true
this.renderer.render()
this.renderer.render(callback)
})
}
@@ -229,6 +221,16 @@ class MindMap {
return prop === undefined ? this.themeConfig : this.themeConfig[prop]
}
// 获取配置
getConfig(prop) {
return prop === undefined ? this.opt : this.opt[prop]
}
// 更新配置
updateConfig(opt = {}) {
this.opt = this.handleOpt(merge.all([defaultOpt, this.opt, opt]))
}
// 获取当前布局结构
getLayout() {
return this.opt.layout
@@ -325,9 +327,61 @@ class MindMap {
}
this.emit('mode_change', mode)
}
// 获取svg数据
getSvgData() {
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()
// 去除放大缩小的变换效果
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
// 获取变换后的位置尺寸信息其实是getBoundingClientRect方法的包装方法
const rect = draw.rbox()
// 将svg设置为实际内容的宽高
svg.size(rect.width, rect.height)
// 把实际内容变换
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
// 克隆一份数据
let clone = svg.clone()
// 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题
if ((rect.width > origWidth || rect.height > origHeight) && this.watermark && this.watermark.hasWatermark()) {
this.width = rect.width
this.height = rect.height
this.watermark.draw()
clone = svg.clone()
this.width = origWidth
this.height = origHeight
this.watermark.draw()
}
// 恢复原先的大小和变换信息
svg.size(origWidth, origHeight)
draw.transform(origTransform)
return {
svg: clone, // 思维导图图形的整体svg元素包括svg画布容器、g实际的思维导图组
svgHTML: clone.svg(), // svg字符串
rect: {
...rect, // 思维导图图形未缩放时的位置尺寸等信息
ratio: rect.width / rect.height // 思维导图图形的宽高比
},
origWidth, // 画布宽度
origHeight, // 画布高度
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值
}
}
}
MindMap.xmind = xmind
// 插件列表
MindMap.pluginList = []
MindMap.usePlugin = (plugin) => {
MindMap.pluginList.push(plugin)
return MindMap
}
// 定义新主题
MindMap.defineTheme = (name, config = {}) => {

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.2.23",
"version": "0.3.4",
"description": "一个简单的web在线思维导图",
"authors": [
{
@@ -22,7 +22,7 @@
"format": "prettier --write ."
},
"module": "index.js",
"main": "./dist/simpleMindMap.umd.min.js",
"__main": "./dist/simpleMindMap.umd.min.js",
"dependencies": {
"@svgdotjs/svg.js": "^3.0.16",
"canvg": "^3.0.7",

View File

@@ -1,4 +1,4 @@
// 将/** */类型的注释转换为//类型
// 遍历所有js文件
const path = require('path')
const fs = require('fs')
@@ -11,13 +11,26 @@ const transform = dir => {
if (fs.statSync(file).isDirectory()) {
transform(file)
} else if (/\.js$/.test(file)) {
rewriteComments(file)
transformFile(file)
}
})
}
const rewriteComments = file => {
const transformFile = file => {
console.log(file);
let content = fs.readFileSync(file, 'utf-8')
countCodeLines(content)
// transformComments(file, content)
}
// 统计代码行数
let totalLines = 0
const countCodeLines = (content) => {
totalLines += content.split(/\n/).length
}
// 转换注释类型
const transformComments = (file, content) => {
console.log('当前转换文件:', file)
content = content.replace(/\/\*\*[^/]+\*\//g, str => {
let res = /@Desc:([^\n]+)\n/g.exec(str)
@@ -29,4 +42,5 @@ const rewriteComments = file => {
}
transform(entryPath)
rewriteComments(path.join(__dirname, '../index.js'))
transformFile(path.join(__dirname, '../index.js'))
console.log(totalLines);

View File

@@ -72,6 +72,9 @@ class Command {
// 添加回退数据
addHistory() {
if (this.mindMap.opt.readonly) {
return
}
let data = this.getCopyData()
this.history.push(simpleDeepClone(data))
this.activeHistoryIndex = this.history.length - 1
@@ -85,6 +88,9 @@ class Command {
// 回退
back(step = 1) {
if (this.mindMap.opt.readonly) {
return
}
if (this.activeHistoryIndex - step >= 0) {
this.activeHistoryIndex -= step
this.mindMap.emit(
@@ -98,6 +104,9 @@ class Command {
// 前进
forward(step = 1) {
if (this.mindMap.opt.readonly) {
return
}
let len = this.history.length
if (this.activeHistoryIndex + step <= len - 1) {
this.activeHistoryIndex += step

View File

@@ -1,254 +1,265 @@
import { bfsWalk, throttle } from './utils'
import Base from './layouts/Base'
// 节点拖动类
class Drag extends Base {
// 构造函数
constructor({ mindMap }) {
super(mindMap.renderer)
this.mindMap = mindMap
this.reset()
this.bindEvent()
}
// 复位
reset() {
// 当前拖拽节点
this.node = null
// 当前重叠节点
this.overlapNode = null
// 当前上一个同级节点
this.prevNode = null
// 当前下一个同级节点
this.nextNode = null
// 画布的变换数据
this.drawTransform = null
// 克隆节点
this.clone = null
// 连接线
this.line = null
// 同级位置占位符
this.placeholder = null
// 鼠标按下位置和节点左上角的偏移量
this.offsetX = 0
this.offsetY = 0
// 克隆节点左上角的坐标
this.cloneNodeLeft = 0
this.cloneNodeTop = 0
// 当前鼠标是否按下
this.isMousedown = false
// 拖拽的鼠标位置变量
this.mouseDownX = 0
this.mouseDownY = 0
this.mouseMoveX = 0
this.mouseMoveY = 0
}
// 绑定事件
bindEvent() {
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
this.mindMap.on('node_mousedown', (node, e) => {
if (this.mindMap.opt.readonly || node.isGeneralization) {
return
}
if (e.which !== 1 || node.isRoot) {
return
}
e.preventDefault()
// 计算鼠标按下的位置距离节点左上角的距离
this.drawTransform = this.mindMap.draw.transform()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.offsetX = x - (node.left * scaleX + translateX)
this.offsetY = y - (node.top * scaleY + translateY)
this.node = node
this.isMousedown = true
this.mouseDownX = x
this.mouseDownY = y
})
this.mindMap.on('mousemove', e => {
if (this.mindMap.opt.readonly) {
return
}
if (!this.isMousedown) {
return
}
e.preventDefault()
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.mouseMoveX = x
this.mouseMoveY = y
if (
Math.abs(x - this.mouseDownX) <= 10 &&
Math.abs(y - this.mouseDownY) <= 10 &&
!this.node.isDrag
) {
return
}
this.mindMap.renderer.clearAllActive()
this.onMove(x, y)
})
this.onMouseup = this.onMouseup.bind(this)
this.mindMap.on('node_mouseup', this.onMouseup)
this.mindMap.on('mouseup', this.onMouseup)
}
// 鼠标松开事件
onMouseup(e) {
if (!this.isMousedown) {
return
}
this.isMousedown = false
let _nodeIsDrag = this.node.isDrag
this.node.isDrag = false
this.node.show()
this.removeCloneNode()
// 存在重叠子节点,则移动作为其子节点
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode)
} else if (this.prevNode) {
// 存在前一个相邻节点,作为其下一个兄弟节点
this.mindMap.renderer.setNodeActive(this.prevNode, false)
this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode)
} else if (this.nextNode) {
// 存在下一个相邻节点,作为其前一个兄弟节点
this.mindMap.renderer.setNodeActive(this.nextNode, false)
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
} else if (_nodeIsDrag) {
// 自定义位置
let { x, y } = this.mindMap.toPos(
e.clientX - this.offsetX,
e.clientY - this.offsetY
)
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
x = (x - translateX) / scaleX
y = (y - translateY) / scaleY
this.node.left = x
this.node.top = y
this.node.customLeft = x
this.node.customTop = y
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
this.mindMap.render()
}
this.reset()
}
// 创建克隆节点
createCloneNode() {
if (!this.clone) {
// 节点
this.clone = this.node.group.clone()
this.clone.opacity(0.5)
this.clone.css('z-index', 99999)
this.node.isDrag = true
this.node.hide()
// 连接线
this.line = this.draw.path()
this.line.opacity(0.5)
this.node.styleLine(this.line, this.node)
// 同级位置占位符
this.placeholder = this.draw.rect().fill({
color: this.node.style.merge('lineColor', true)
})
this.mindMap.draw.add(this.clone)
}
}
// 移除克隆节点
removeCloneNode() {
if (!this.clone) {
return
}
this.clone.remove()
this.line.remove()
this.placeholder.remove()
}
// 拖动中
onMove(x, y) {
if (!this.isMousedown) {
return
}
this.createCloneNode()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
this.cloneNodeLeft = x - this.offsetX
this.cloneNodeTop = y - this.offsetY
x = (this.cloneNodeLeft - translateX) / scaleX
y = (this.cloneNodeTop - translateY) / scaleY
let t = this.clone.transform()
this.clone.translate(x - t.translateX, y - t.translateY)
// 连接线
let parent = this.node.parent
this.line.plot(
this.quadraticCurvePath(
parent.left + parent.width / 2,
parent.top + parent.height / 2,
x + this.node.width / 2,
y + this.node.height / 2
)
)
this.checkOverlapNode()
}
// 检测重叠节点
checkOverlapNode() {
if (!this.drawTransform) {
return
}
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let checkRight = this.cloneNodeLeft + this.node.width * scaleX
let checkBottom = this.cloneNodeTop + this.node.height * scaleX
this.overlapNode = null
this.prevNode = null
this.nextNode = null
this.placeholder.size(0, 0)
bfsWalk(this.mindMap.renderer.root, node => {
if (node.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(node, false)
}
if (node === this.node || this.node.isParent(node)) {
return
}
if (this.overlapNode || (this.prevNode && this.nextNode)) {
return
}
let { left, top, width, height } = node
let _left = left
let _top = top
let _bottom = top + height
let right = (left + width) * scaleX + translateX
let bottom = (top + height) * scaleY + translateY
left = left * scaleX + translateX
top = top * scaleY + translateY
// 检测是否重叠
if (!this.overlapNode) {
if (
left <= checkRight &&
right >= this.cloneNodeLeft &&
top <= checkBottom &&
bottom >= this.cloneNodeTop
) {
this.overlapNode = node
}
}
// 检测兄弟节点位置
if (!this.prevNode && !this.nextNode && !node.isRoot) {
// && this.node.isBrother(node)
if (left <= checkRight && right >= this.cloneNodeLeft) {
if (this.cloneNodeTop > bottom && this.cloneNodeTop <= bottom + 10) {
this.prevNode = node
this.placeholder.size(node.width, 10).move(_left, _bottom)
} else if (checkBottom < top && checkBottom >= top - 10) {
this.nextNode = node
this.placeholder.size(node.width, 10).move(_left, _top - 10)
}
}
import { bfsWalk, throttle } from './utils'
import Base from './layouts/Base'
// 节点拖动类
class Drag extends Base {
// 构造函数
constructor({ mindMap }) {
super(mindMap.renderer)
this.mindMap = mindMap
this.reset()
this.bindEvent()
}
// 复位
reset() {
// 当前拖拽节点
this.node = null
// 当前重叠节点
this.overlapNode = null
// 当前上一个同级节点
this.prevNode = null
// 当前下一个同级节点
this.nextNode = null
// 画布的变换数据
this.drawTransform = null
// 克隆节点
this.clone = null
// 连接线
this.line = null
// 同级位置占位符
this.placeholder = null
// 鼠标按下位置和节点左上角的偏移量
this.offsetX = 0
this.offsetY = 0
// 克隆节点左上角的坐标
this.cloneNodeLeft = 0
this.cloneNodeTop = 0
// 当前鼠标是否按下
this.isMousedown = false
// 拖拽的鼠标位置变量
this.mouseDownX = 0
this.mouseDownY = 0
this.mouseMoveX = 0
this.mouseMoveY = 0
}
// 绑定事件
bindEvent() {
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
this.mindMap.on('node_mousedown', (node, e) => {
if (this.mindMap.opt.readonly || node.isGeneralization) {
return
}
if (e.which !== 1 || node.isRoot) {
return
}
e.preventDefault()
// 计算鼠标按下的位置距离节点左上角的距离
this.drawTransform = this.mindMap.draw.transform()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.offsetX = x - (node.left * scaleX + translateX)
this.offsetY = y - (node.top * scaleY + translateY)
this.node = node
this.isMousedown = true
this.mouseDownX = x
this.mouseDownY = y
})
this.mindMap.on('mousemove', e => {
if (this.mindMap.opt.readonly) {
return
}
if (!this.isMousedown) {
return
}
e.preventDefault()
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.mouseMoveX = x
this.mouseMoveY = y
if (
Math.abs(x - this.mouseDownX) <= 10 &&
Math.abs(y - this.mouseDownY) <= 10 &&
!this.node.isDrag
) {
return
}
this.mindMap.renderer.clearAllActive()
this.onMove(x, y)
})
this.onMouseup = this.onMouseup.bind(this)
this.mindMap.on('node_mouseup', this.onMouseup)
this.mindMap.on('mouseup', this.onMouseup)
}
// 鼠标松开事件
onMouseup(e) {
if (!this.isMousedown) {
return
}
this.isMousedown = false
let _nodeIsDrag = this.node.isDrag
this.node.isDrag = false
this.node.show()
this.removeCloneNode()
// 存在重叠子节点,则移动作为其子节点
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode)
} else if (this.prevNode) {
// 存在前一个相邻节点,作为其下一个兄弟节点
this.mindMap.renderer.setNodeActive(this.prevNode, false)
this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode)
} else if (this.nextNode) {
// 存在下一个相邻节点,作为其前一个兄弟节点
this.mindMap.renderer.setNodeActive(this.nextNode, false)
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
} else if (_nodeIsDrag && this.mindMap.opt.enableFreeDrag) {
// 自定义位置
let { x, y } = this.mindMap.toPos(
e.clientX - this.offsetX,
e.clientY - this.offsetY
)
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
x = (x - translateX) / scaleX
y = (y - translateY) / scaleY
this.node.left = x
this.node.top = y
this.node.customLeft = x
this.node.customTop = y
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
this.mindMap.render()
}
this.reset()
}
// 创建克隆节点
createCloneNode() {
if (!this.clone) {
// 节点
this.clone = this.node.group.clone()
this.clone.opacity(0.5)
this.clone.css('z-index', 99999)
this.node.isDrag = true
this.node.hide()
// 连接线
this.line = this.draw.path()
this.line.opacity(0.5)
this.node.styleLine(this.line, this.node)
// 同级位置占位符
this.placeholder = this.draw.rect().fill({
color: this.node.style.merge('lineColor', true)
})
this.mindMap.draw.add(this.clone)
}
}
// 移除克隆节点
removeCloneNode() {
if (!this.clone) {
return
}
this.clone.remove()
this.line.remove()
this.placeholder.remove()
}
// 拖动中
onMove(x, y) {
if (!this.isMousedown) {
return
}
this.createCloneNode()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
this.cloneNodeLeft = x - this.offsetX
this.cloneNodeTop = y - this.offsetY
x = (this.cloneNodeLeft - translateX) / scaleX
y = (this.cloneNodeTop - translateY) / scaleY
let t = this.clone.transform()
this.clone.translate(x - t.translateX, y - t.translateY)
// 连接线
let parent = this.node.parent
this.line.plot(
this.quadraticCurvePath(
parent.left + parent.width / 2,
parent.top + parent.height / 2,
x + this.node.width / 2,
y + this.node.height / 2
)
)
this.checkOverlapNode()
}
// 检测重叠节点
checkOverlapNode() {
if (!this.drawTransform) {
return
}
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let checkRight = this.cloneNodeLeft + this.node.width * scaleX
let checkBottom = this.cloneNodeTop + this.node.height * scaleX
this.overlapNode = null
this.prevNode = null
this.nextNode = null
this.placeholder.size(0, 0)
bfsWalk(this.mindMap.renderer.root, node => {
if (node.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(node, false)
}
if (node === this.node || this.node.isParent(node)) {
return
}
if (this.overlapNode || (this.prevNode && this.nextNode)) {
return
}
let { left, top, width, height } = node
let _left = left
let _top = top
let _bottom = top + height
let right = (left + width) * scaleX + translateX
let bottom = (top + height) * scaleY + translateY
left = left * scaleX + translateX
top = top * scaleY + translateY
// 检测是否重叠
if (!this.overlapNode) {
if (
left <= checkRight &&
right >= this.cloneNodeLeft &&
top <= checkBottom &&
bottom >= this.cloneNodeTop
) {
this.overlapNode = node
}
}
// 检测兄弟节点位置
if (!this.prevNode && !this.nextNode && !node.isRoot) {
// && this.node.isBrother(node)
if (left <= checkRight && right >= this.cloneNodeLeft) {
if (this.cloneNodeTop > bottom && this.cloneNodeTop <= bottom + 10) {
this.prevNode = node
this.placeholder.size(node.width, 10).move(_left, _bottom)
} else if (checkBottom < top && checkBottom >= top - 10) {
this.nextNode = node
this.placeholder.size(node.width, 10).move(_left, _top - 10)
}
}
}
})
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
}
}
}
Drag.instanceName = 'drag'
export default Drag

View File

@@ -1,6 +1,7 @@
import { imgToDataUrl, downloadFile } from './utils'
import JsPDF from 'jspdf'
import { SVG } from '@svgdotjs/svg.js'
import drawBackgroundImageToCanvas from './utils/simulateCSSBackgroundInCanvas'
const URL = window.URL || window.webkitURL || window
// 导出类
@@ -26,7 +27,7 @@ class Export {
// 获取svg数据
async getSvgData() {
let { svg, svgHTML } = this.mindMap.miniMap.getMiniMap()
let { svg, svgHTML } = this.mindMap.getSvgData()
// 把图片的url转换成data:url类型否则导出会丢失图片
let imageList = svg.find('image')
let task = imageList.map(async item => {
@@ -85,7 +86,9 @@ class Export {
let {
backgroundColor = '#fff',
backgroundImage,
backgroundRepeat = 'repeat'
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center center',
backgroundSize = 'cover',
} = this.mindMap.themeConfig
// 背景颜色
ctx.save()
@@ -96,19 +99,18 @@ class Export {
// 背景图片
if (backgroundImage && backgroundImage !== 'none') {
ctx.save()
let img = new Image()
img.src = backgroundImage
img.onload = () => {
let pat = ctx.createPattern(img, backgroundRepeat)
ctx.rect(0, 0, width, height)
ctx.fillStyle = pat
ctx.fill()
drawBackgroundImageToCanvas(ctx, width, height, backgroundImage, {
backgroundRepeat,
backgroundPosition,
backgroundSize
}, (err) => {
if (err) {
reject(err)
} else {
resolve()
}
ctx.restore()
resolve()
}
img.onerror = e => {
reject(e)
}
})
} else {
resolve()
}
@@ -215,4 +217,6 @@ class Export {
}
}
Export.instanceName = 'doExport'
export default Export

View File

@@ -2,7 +2,7 @@ import { isKey } from './utils/keyMap'
import { bfsWalk } from './utils'
// 键盘导航类
export default class KeyboardNavigation {
class KeyboardNavigation {
// 构造函数
constructor(opt) {
this.opt = opt
@@ -224,3 +224,7 @@ export default class KeyboardNavigation {
}
}
}
KeyboardNavigation.instanceName = 'keyboardNavigation'
export default KeyboardNavigation

View File

@@ -14,43 +14,6 @@ class MiniMap {
}
}
// 获取小地图相关数据
getMiniMap() {
const svg = this.mindMap.svg
const draw = this.mindMap.draw
// 保存原始信息
const origWidth = svg.width()
const origHeight = svg.height()
const origTransform = draw.transform()
const elRect = this.mindMap.el.getBoundingClientRect()
// 去除放大缩小的变换效果
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
// 获取变换后的位置尺寸信息其实是getBoundingClientRect方法的包装方法
const rect = draw.rbox()
// 将svg设置为实际内容的宽高
svg.size(rect.width, rect.height)
// 把实际内容变换
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
// 克隆一份数据
const clone = svg.clone()
// 恢复原先的大小和变换信息
svg.size(origWidth, origHeight)
draw.transform(origTransform)
return {
svg: clone, // 思维导图图形的整体svg元素包括svg画布容器、g实际的思维导图组
svgHTML: clone.svg(), // svg字符串
rect: {
...rect, // 思维导图图形未缩放时的位置尺寸等信息
ratio: rect.width / rect.height // 思维导图图形的宽高比
},
origWidth, // 画布宽度
origHeight, // 画布高度
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值
}
}
// 计算小地图的渲染数据
/**
* boxWidth小地图容器的宽度
@@ -58,7 +21,7 @@ class MiniMap {
*/
calculationMiniMap(boxWidth, boxHeight) {
let { svgHTML, rect, origWidth, origHeight, scaleX, scaleY } =
this.getMiniMap()
this.mindMap.getSvgData()
// 计算数据
let boxRatio = boxWidth / boxHeight
let actWidth = 0
@@ -144,4 +107,6 @@ class MiniMap {
}
}
MiniMap.instanceName = 'miniMap'
export default MiniMap

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -167,4 +167,6 @@ class Select {
}
}
Select.instanceName = 'select'
export default Select

View File

@@ -1,167 +1,200 @@
import { tagColorList } from './utils/constant'
const rootProp = ['paddingX', 'paddingY']
// 样式类
class Style {
// 设置背景样式
static setBackgroundStyle(el, themeConfig) {
let { backgroundColor, backgroundImage, backgroundRepeat } = themeConfig
el.style.backgroundColor = backgroundColor
if (backgroundImage) {
el.style.backgroundImage = `url(${backgroundImage})`
el.style.backgroundRepeat = backgroundRepeat
}
}
// 构造函数
constructor(ctx, themeConfig) {
this.ctx = ctx
this.themeConfig = themeConfig
}
// 更新主题配置
updateThemeConfig(themeConfig) {
this.themeConfig = themeConfig
}
// 合并样式
merge(prop, root, isActive) {
// 三级及以下节点
let defaultConfig = this.themeConfig.node
if (root || rootProp.includes(prop)) {
// 直接使用最外层样式
defaultConfig = this.themeConfig
} else if (this.ctx.isGeneralization) {
// 概要节点
defaultConfig = this.themeConfig.generalization
} else if (this.ctx.layerIndex === 0) {
// 根节点
defaultConfig = this.themeConfig.root
} else if (this.ctx.layerIndex === 1) {
// 二级节点
defaultConfig = this.themeConfig.second
}
// 激活状态
if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) {
if (
this.ctx.nodeData.data.activeStyle &&
this.ctx.nodeData.data.activeStyle[prop] !== undefined
) {
return this.ctx.nodeData.data.activeStyle[prop]
} else if (defaultConfig.active && defaultConfig.active[prop]) {
return defaultConfig.active[prop]
}
}
// 优先使用节点本身的样式
return this.getSelfStyle(prop) !== undefined
? this.getSelfStyle(prop)
: defaultConfig[prop]
}
// 获取某个样式值
getStyle(prop, root, isActive) {
return this.merge(prop, root, isActive)
}
// 获取自身自定义样式
getSelfStyle(prop) {
return this.ctx.nodeData.data[prop]
}
// 矩形
rect(node) {
this.shape(node)
node.radius(this.merge('borderRadius'))
}
// 矩形外的其他形状
shape(node) {
node.fill({
color: this.merge('fillColor')
})
// 节点使用横线样式,不需要渲染非激活状态的边框样式
if (
!this.ctx.isRoot &&
!this.ctx.isGeneralization &&
this.themeConfig.nodeUseLineStyle &&
!this.ctx.nodeData.data.isActive
) {
return
}
node.stroke({
color: this.merge('borderColor'),
width: this.merge('borderWidth'),
dasharray: this.merge('borderDasharray')
})
}
// 文字
text(node) {
node
.fill({
color: this.merge('color')
})
.css({
'font-family': this.merge('fontFamily'),
'font-size': this.merge('fontSize'),
'font-weight': this.merge('fontWeight'),
'font-style': this.merge('fontStyle'),
'text-decoration': this.merge('textDecoration')
})
}
// html文字节点
domText(node, fontSizeScale = 1) {
node.style.fontFamily = this.merge('fontFamily')
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
node.style.fontWeight = this.merge('fontWeight') || 'normal'
}
// 标签文字
tagText(node, index) {
node
.fill({
color: tagColorList[index].color
})
.css({
'font-size': '12px'
})
}
// 标签矩形
tagRect(node, index) {
node.fill({
color: tagColorList[index].background
})
}
// 内置图标
iconNode(node) {
node.attr({
fill: this.merge('color')
})
}
// 连线
line(node, { width, color, dasharray } = {}) {
node.stroke({ width, color, dasharray }).fill({ color: 'none' })
}
// 概要连线
generalizationLine(node) {
import { tagColorList } from './utils/constant'
const rootProp = ['paddingX', 'paddingY']
// 样式类
class Style {
// 设置背景样式
static setBackgroundStyle(el, themeConfig) {
let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig
el.style.backgroundColor = backgroundColor
if (backgroundImage) {
el.style.backgroundImage = `url(${backgroundImage})`
el.style.backgroundRepeat = backgroundRepeat
el.style.backgroundPosition = backgroundPosition
el.style.backgroundSize = backgroundSize
} else {
el.style.backgroundImage = 'none'
}
}
// 构造函数
constructor(ctx, themeConfig) {
this.ctx = ctx
this.themeConfig = themeConfig
}
// 更新主题配置
updateThemeConfig(themeConfig) {
this.themeConfig = themeConfig
}
// 合并样式
merge(prop, root, isActive) {
// 三级及以下节点
let defaultConfig = this.themeConfig.node
if (root || rootProp.includes(prop)) {
// 直接使用最外层样式
defaultConfig = this.themeConfig
} else if (this.ctx.isGeneralization) {
// 概要节点
defaultConfig = this.themeConfig.generalization
} else if (this.ctx.layerIndex === 0) {
// 根节点
defaultConfig = this.themeConfig.root
} else if (this.ctx.layerIndex === 1) {
// 二级节点
defaultConfig = this.themeConfig.second
}
// 激活状态
if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) {
if (
this.ctx.nodeData.data.activeStyle &&
this.ctx.nodeData.data.activeStyle[prop] !== undefined
) {
return this.ctx.nodeData.data.activeStyle[prop]
} else if (defaultConfig.active && defaultConfig.active[prop]) {
return defaultConfig.active[prop]
}
}
// 优先使用节点本身的样式
return this.getSelfStyle(prop) !== undefined
? this.getSelfStyle(prop)
: defaultConfig[prop]
}
// 获取某个样式值
getStyle(prop, root, isActive) {
return this.merge(prop, root, isActive)
}
// 获取自身自定义样式
getSelfStyle(prop) {
return this.ctx.nodeData.data[prop]
}
// 矩形
rect(node) {
this.shape(node)
node.radius(this.merge('borderRadius'))
}
// 矩形外的其他形状
shape(node) {
node.fill({
color: this.merge('fillColor')
})
// 节点使用横线样式,不需要渲染非激活状态的边框样式
if (
!this.ctx.isRoot &&
!this.ctx.isGeneralization &&
this.themeConfig.nodeUseLineStyle &&
!this.ctx.nodeData.data.isActive
) {
return
}
node.stroke({
color: this.merge('borderColor'),
width: this.merge('borderWidth'),
dasharray: this.merge('borderDasharray')
})
}
// 文字
text(node) {
node
.fill({
color: this.merge('color')
})
.css({
'font-family': this.merge('fontFamily'),
'font-size': this.merge('fontSize'),
'font-weight': this.merge('fontWeight'),
'font-style': this.merge('fontStyle'),
'text-decoration': this.merge('textDecoration')
})
}
// 获取文本样式
getTextFontStyle() {
return {
italic: this.merge('fontStyle') === 'italic',
bold: this.merge('fontWeight'),
fontSize: this.merge('fontSize'),
fontFamily: this.merge('fontFamily')
}
}
// html文字节点
domText(node, fontSizeScale = 1) {
node.style.fontFamily = this.merge('fontFamily')
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
node.style.fontWeight = this.merge('fontWeight') || 'normal'
node.style.lineHeight = this.merge('lineHeight')
node.style.fontStyle = this.merge('fontStyle')
}
// 标签文字
tagText(node, index) {
node
.fill({
color: tagColorList[index].color
})
.css({
'font-size': '12px'
})
}
// 标签矩形
tagRect(node, index) {
node.fill({
color: tagColorList[index].background
})
}
// 内置图标
iconNode(node) {
node.attr({
fill: this.merge('color')
})
}
// 连线
line(node, { width, color, dasharray } = {}) {
node.stroke({ width, color, dasharray }).fill({ color: 'none' })
}
// 概要连线
generalizationLine(node) {
node
.stroke({
width: this.merge('generalizationLineWidth', true),
color: this.merge('generalizationLineColor', true)
})
.fill({ color: 'none' })
}
// 按钮
iconBtn(node, fillNode) {
node.fill({ color: '#808080' })
fillNode.fill({ color: '#fff' })
}
}
export default Style

View File

@@ -59,14 +59,17 @@ export default class TextEdit {
this.registerTmpShortcut()
if (!this.textEditNode) {
this.textEditNode = document.createElement('div')
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none;`
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
this.textEditNode.setAttribute('contenteditable', true)
this.textEditNode.addEventListener('keyup', e => {
e.stopPropagation()
})
document.body.appendChild(this.textEditNode)
}
node.style.domText(this.textEditNode, this.mindMap.view.scale)
let scale = this.mindMap.view.scale
let lineHeight = node.style.merge('lineHeight')
let fontSize = node.style.merge('fontSize')
node.style.domText(this.textEditNode, scale)
this.textEditNode.innerHTML = node.nodeData.data.text
.split(/\n/gim)
.join('<br>')
@@ -75,6 +78,8 @@ export default class TextEdit {
this.textEditNode.style.left = rect.left + 'px'
this.textEditNode.style.top = rect.top + 'px'
this.textEditNode.style.display = 'block'
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth * scale + 'px'
this.textEditNode.style.transform = `translateY(${-(lineHeight * fontSize - fontSize) / 2 * scale}px)`
this.showTextEdit = true
// 选中文本
this.selectNodeText()

View File

@@ -0,0 +1,120 @@
import { Text, G } from '@svgdotjs/svg.js'
import { degToRad, camelCaseToHyphen } from './utils'
import merge from 'deepmerge'
// 水印类
class Watermark {
constructor(opt = {}) {
this.mindMap = opt.mindMap
this.lineSpacing = 0 // 水印行间距
this.textSpacing = 0 // 行内水印间距
this.angle = 0 // 旋转角度
this.text = '' // 水印文字
this.textStyle = {} // 水印文字样式
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 || {})
}
// 获取是否存在水印
hasWatermark() {
return !!this.text.trim()
}
// 处理水印配置
handleConfig({ text, lineSpacing, textSpacing, angle, textStyle }) {
this.text = text === undefined ? '' : String(text).trim()
this.lineSpacing =
typeof lineSpacing === 'number' && lineSpacing > 0 ? lineSpacing : 100
this.textSpacing =
typeof textSpacing === 'number' && textSpacing > 0 ? textSpacing : 100
this.angle =
typeof angle === 'number' && angle >= 0 && angle <= 90 ? angle : 30
this.textStyle = Object.assign(this.textStyle, textStyle || {})
}
// 绘制水印
// 非精确绘制,会绘制一些超出可视区域的水印
draw() {
this.watermarkDraw.clear()
if (!this.hasWatermark()) {
return
}
let x = 0
while (x < this.mindMap.width) {
this.drawText(x)
x += this.lineSpacing / Math.sin(degToRad(this.angle))
}
let yOffset =
this.lineSpacing / Math.cos(degToRad(this.angle)) || this.lineSpacing
let y = yOffset
while (y < this.mindMap.height) {
this.drawText(0, y)
y += yOffset
}
}
// 绘制文字
drawText(x, y) {
let long = Math.min(
this.maxLong,
(this.mindMap.width - x) / Math.cos(degToRad(this.angle))
)
let g = new G()
let bbox = null
let bboxWidth = 0
let textHeight = -1
while (bboxWidth < long) {
let text = new Text().text(this.text)
g.add(text)
text.transform({
translateX: bboxWidth
})
this.setTextStyle(text)
bbox = g.bbox()
if (textHeight === -1) {
textHeight = bbox.height
}
bboxWidth = bbox.width + this.textSpacing
}
let params = {
rotate: this.angle,
origin: 'top left',
translateX: x,
translateY: textHeight
}
if (y !== undefined) {
params.translateY = y + textHeight
}
g.transform(params)
this.watermarkDraw.add(g)
}
// 给文字设置样式
setTextStyle(text) {
Object.keys(this.textStyle).forEach(item => {
let value = this.textStyle[item]
if (item === 'color') {
text.fill(value)
} else {
text.css(camelCaseToHyphen(item), value)
}
})
}
// 更新水印
updateWatermark(config) {
this.mindMap.opt.watermarkConfig = merge(this.mindMap.opt.watermarkConfig, config)
this.handleConfig(config)
this.draw()
}
}
Watermark.instanceName = 'watermark'
export default Watermark

View File

@@ -1,267 +1,289 @@
import Base from './Base'
import { walk, asyncRun } from '../utils'
// 逻辑结构图
class LogicalStructure extends Base {
// 构造函数
constructor(opt = {}) {
super(opt)
}
// 布局
doLayout(callback) {
let task = [
() => {
this.computedBaseValue()
},
() => {
this.computedTopValue()
},
() => {
this.adjustTopValue()
},
() => {
callback(this.root)
}
]
asyncRun(task)
}
// 遍历数据计算节点的left、width、height
computedBaseValue() {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
} else {
// 非根节点
// 定位到父节点右侧
newNode.left =
parent._node.left + parent._node.width + this.getMarginX(layerIndex)
}
if (!cur.data.expand) {
return true
}
},
(cur, parent, isRoot, layerIndex) => {
// 返回时计算节点的areaHeight也就是子节点所占的高度之和包括外边距
let len = cur.data.expand === false ? 0 : cur._node.children.length
cur._node.childrenAreaHeight = len
? cur._node.children.reduce((h, item) => {
return h + item.height
}, 0) +
(len + 1) * this.getMarginY(layerIndex + 1)
: 0
},
true,
0
)
}
// 遍历节点树计算节点的top
computedTopValue() {
walk(
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (
node.nodeData.data.expand &&
node.children &&
node.children.length
) {
let marginY = this.getMarginY(layerIndex + 1)
// 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
let top = node.top + node.height / 2 - node.childrenAreaHeight / 2
let totalTop = top + marginY
node.children.forEach(cur => {
cur.top = totalTop
totalTop += cur.height + marginY
})
}
},
null,
true
)
}
// 调整节点top
adjustTopValue() {
walk(
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
return
}
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
let difference =
node.childrenAreaHeight -
this.getMarginY(layerIndex + 1) * 2 -
node.height
if (difference > 0) {
this.updateBrothers(node, difference / 2)
}
},
null,
true
)
}
// 更新兄弟节点的top
updateBrothers(node, addHeight) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item === node
})
childrenList.forEach((item, _index) => {
if (item === node || item.hasCustomPosition()) {
// 适配自定义位置
return
}
let _offset = 0
// 上面的节点往上移
if (_index < index) {
_offset = -addHeight
} else if (_index > index) {
// 下面的节点往下移
_offset = addHeight
}
item.top += _offset
// 同步更新子节点的位置
if (item.children && item.children.length) {
this.updateChildren(item.children, 'top', _offset)
}
})
// 更新父节点的位置
this.updateBrothers(node.parent, addHeight)
}
}
// 绘制连线,连接该节点到其子节点
renderLine(node, lines, style, lineStyle) {
if (lineStyle === 'curve') {
this.renderLineCurve(node, lines, style)
} else if (lineStyle === 'direct') {
this.renderLineDirect(node, lines, style)
} else {
this.renderLineStraight(node, lines, style)
}
}
// 直线风格连线
renderLineStraight(node, lines, style) {
if (node.children.length <= 0) {
return []
}
let { left, top, width, height, expandBtnSize } = node
let marginX = this.getMarginX(node.layerIndex + 1)
let s1 = (marginX - expandBtnSize) * 0.6
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0 ? left + width : left + width + expandBtnSize
let y1 = top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
? item.width
: 0
let path = `M ${x1},${y1} L ${x1 + s1},${y1} L ${x1 + s1},${y2} L ${
x2 + nodeUseLineStyleOffset
},${y2}`
lines[index].plot(path)
style && style(lines[index], item)
})
}
// 直连风格
renderLineDirect(node, lines, style) {
if (node.children.length <= 0) {
return []
}
let { left, top, width, height, expandBtnSize } = node
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
let y1 = top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStylePath = this.mindMap.themeConfig.nodeUseLineStyle
? ` L ${item.left + item.width},${y2}`
: ''
let path = `M ${x1},${y1} L ${x2},${y2}` + nodeUseLineStylePath
lines[index].plot(path)
style && style(lines[index], item)
})
}
// 曲线风格连线
renderLineCurve(node, lines, style) {
if (node.children.length <= 0) {
return []
}
let { left, top, width, height, expandBtnSize } = node
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
let y1 = top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
let path = ''
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStylePath = this.mindMap.themeConfig.nodeUseLineStyle
? ` L ${item.left + item.width},${y2}`
: ''
if (node.isRoot) {
path = this.quadraticCurvePath(x1, y1, x2, y2) + nodeUseLineStylePath
} else {
path = this.cubicBezierPath(x1, y1, x2, y2) + nodeUseLineStylePath
}
lines[index].plot(path)
style && style(lines[index], item)
})
}
// 渲染按钮
renderExpandBtn(node, btn) {
let { width, height } = node
let { translateX, translateY } = btn.transform()
// 节点使用横线风格,需要调整展开收起按钮位置
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
? height / 2
: 0
btn.translate(
width - translateX,
height / 2 - translateY + nodeUseLineStyleOffset
)
}
// 创建概要节点
renderGeneralization(node, gLine, gNode) {
let {
top,
bottom,
right,
generalizationLineMargin,
generalizationNodeMargin
} = this.getNodeBoundaries(node, 'h')
let x1 = right + generalizationLineMargin
import Base from './Base'
import { walk, asyncRun } from '../utils'
// 逻辑结构图
class LogicalStructure extends Base {
// 构造函数
constructor(opt = {}) {
super(opt)
}
// 布局
doLayout(callback) {
let task = [
() => {
this.computedBaseValue()
},
() => {
this.computedTopValue()
},
() => {
this.adjustTopValue()
},
() => {
callback(this.root)
}
]
asyncRun(task)
}
// 遍历数据计算节点的left、width、height
computedBaseValue() {
walk(
this.renderer.renderTree,
null,
(cur, parent, isRoot, layerIndex) => {
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
// 根节点定位在画布中心位置
if (isRoot) {
this.setNodeCenter(newNode)
} else {
// 非根节点
// 定位到父节点右侧
newNode.left =
parent._node.left + parent._node.width + this.getMarginX(layerIndex)
}
if (!cur.data.expand) {
return true
}
},
(cur, parent, isRoot, layerIndex) => {
// 返回时计算节点的areaHeight也就是子节点所占的高度之和包括外边距
let len = cur.data.expand === false ? 0 : cur._node.children.length
cur._node.childrenAreaHeight = len
? cur._node.children.reduce((h, item) => {
return h + item.height
}, 0) +
(len + 1) * this.getMarginY(layerIndex + 1)
: 0
},
true,
0
)
}
// 遍历节点树计算节点的top
computedTopValue() {
walk(
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (
node.nodeData.data.expand &&
node.children &&
node.children.length
) {
let marginY = this.getMarginY(layerIndex + 1)
// 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
let top = node.top + node.height / 2 - node.childrenAreaHeight / 2
let totalTop = top + marginY
node.children.forEach(cur => {
cur.top = totalTop
totalTop += cur.height + marginY
})
}
},
null,
true
)
}
// 调整节点top
adjustTopValue() {
walk(
this.root,
null,
(node, parent, isRoot, layerIndex) => {
if (!node.nodeData.data.expand) {
return
}
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
let difference =
node.childrenAreaHeight -
this.getMarginY(layerIndex + 1) * 2 -
node.height
if (difference > 0) {
this.updateBrothers(node, difference / 2)
}
},
null,
true
)
}
// 更新兄弟节点的top
updateBrothers(node, addHeight) {
if (node.parent) {
let childrenList = node.parent.children
let index = childrenList.findIndex(item => {
return item === node
})
childrenList.forEach((item, _index) => {
if (item === node || item.hasCustomPosition()) {
// 适配自定义位置
return
}
let _offset = 0
// 上面的节点往上移
if (_index < index) {
_offset = -addHeight
} else if (_index > index) {
// 下面的节点往下移
_offset = addHeight
}
item.top += _offset
// 同步更新子节点的位置
if (item.children && item.children.length) {
this.updateChildren(item.children, 'top', _offset)
}
})
// 更新父节点的位置
this.updateBrothers(node.parent, addHeight)
}
}
// 绘制连线,连接该节点到其子节点
renderLine(node, lines, style, lineStyle) {
if (lineStyle === 'curve') {
this.renderLineCurve(node, lines, style)
} else if (lineStyle === 'direct') {
this.renderLineDirect(node, lines, style)
} else {
this.renderLineStraight(node, lines, style)
}
}
// 直线风格连线
renderLineStraight(node, lines, style) {
if (node.children.length <= 0) {
return []
}
let { left, top, width, height, expandBtnSize } = node
let marginX = this.getMarginX(node.layerIndex + 1)
let s1 = (marginX - expandBtnSize) * 0.6
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0 ? left + width : left + width + expandBtnSize
let y1 = top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStyleOffset = nodeUseLineStyle
? item.width
: 0
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
let path = `M ${x1},${y1} L ${x1 + s1},${y1} L ${x1 + s1},${y2} L ${
x2 + nodeUseLineStyleOffset
},${y2}`
lines[index].plot(path)
style && style(lines[index], item)
})
}
// 直连风格
renderLineDirect(node, lines, style) {
if (node.children.length <= 0) {
return []
}
let { left, top, width, height, expandBtnSize } = node
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
let y1 = top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStylePath = nodeUseLineStyle
? ` L ${item.left + item.width},${y2}`
: ''
let path = `M ${x1},${y1} L ${x2},${y2}` + nodeUseLineStylePath
lines[index].plot(path)
style && style(lines[index], item)
})
}
// 曲线风格连线
renderLineCurve(node, lines, style) {
if (node.children.length <= 0) {
return []
}
let { left, top, width, height, expandBtnSize } = node
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
let y1 = top + height / 2
let x2 = item.left
let y2 = item.top + item.height / 2
let path = ''
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStylePath = nodeUseLineStyle
? ` L ${item.left + item.width},${y2}`
: ''
if (node.isRoot) {
path = this.quadraticCurvePath(x1, y1, x2, y2) + nodeUseLineStylePath
} else {
path = this.cubicBezierPath(x1, y1, x2, y2) + nodeUseLineStylePath
}
lines[index].plot(path)
style && style(lines[index], item)
})
}
// 渲染按钮
renderExpandBtn(node, btn) {
let { width, height } = node
let { translateX, translateY } = btn.transform()
// 节点使用横线风格,需要调整展开收起按钮位置
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
? height / 2
: 0
btn.translate(
width - translateX,
height / 2 - translateY + nodeUseLineStyleOffset
)
}
// 创建概要节点
renderGeneralization(node, gLine, gNode) {
let {
top,
bottom,
right,
generalizationLineMargin,
generalizationNodeMargin
} = this.getNodeBoundaries(node, 'h')
let x1 = right + generalizationLineMargin
let y1 = top
let x2 = right + generalizationLineMargin
let y2 = bottom
let cx = x1 + 20
let cy = y1 + (y2 - y1) / 2
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
gLine.plot(path)
gNode.left = right + generalizationNodeMargin
gNode.top = top + (bottom - top - gNode.height) / 2
}
}
export default LogicalStructure

View File

@@ -208,11 +208,12 @@ class MindMap extends Base {
let { left, top, width, height, expandBtnSize } = node
let marginX = this.getMarginX(node.layerIndex + 1)
let s1 = (marginX - expandBtnSize) * 0.6
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
node.children.forEach((item, index) => {
let x1 = 0
let _s = 0
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
let nodeUseLineStyleOffset = nodeUseLineStyle
? item.width
: 0
if (item.dir === 'left') {
@@ -226,6 +227,8 @@ class MindMap extends Base {
let y1 = top + height / 2
let x2 = item.dir === 'left' ? item.left + item.width : item.left
let y2 = item.top + item.height / 2
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
let path = `M ${x1},${y1} L ${x1 + _s},${y1} L ${x1 + _s},${y2} L ${
x2 + nodeUseLineStyleOffset
},${y2}`
@@ -241,6 +244,7 @@ class MindMap extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0
@@ -251,9 +255,11 @@ class MindMap extends Base {
let y1 = top + height / 2
let x2 = item.dir === 'left' ? item.left + item.width : item.left
let y2 = item.top + item.height / 2
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStylePath = ''
if (this.mindMap.themeConfig.nodeUseLineStyle) {
if (nodeUseLineStyle) {
if (item.dir === 'left') {
nodeUseLineStylePath = ` L ${item.left},${y2}`
} else {
@@ -273,6 +279,7 @@ class MindMap extends Base {
return []
}
let { left, top, width, height, expandBtnSize } = node
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
node.children.forEach((item, index) => {
let x1 =
node.layerIndex === 0
@@ -284,6 +291,8 @@ class MindMap extends Base {
let x2 = item.dir === 'left' ? item.left + item.width : item.left
let y2 = item.top + item.height / 2
let path = ''
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
// 节点使用横线风格,需要额外渲染横线
let nodeUseLineStylePath = ''
if (this.mindMap.themeConfig.nodeUseLineStyle) {

View File

@@ -1,142 +1,147 @@
// 默认主题
export default {
// 节点内边距
paddingX: 15,
paddingY: 5,
// 图片显示的最大宽度
imgMaxWidth: 100,
// 图片显示的最大高度
imgMaxHeight: 100,
// icon的大小
iconSize: 20,
// 连线的粗细
lineWidth: 1,
// 连线的颜色
lineColor: '#549688',
// 连线样式
lineDasharray: 'none',
// 连线风格
lineStyle: 'straight', // 针对logicalStructure、mindMap两种结构。曲线curve、直线straight、直连direct
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#549688',
// 概要曲线距节点的距离
generalizationLineMargin: 0,
// 概要节点距节点的距离
generalizationNodeMargin: 20,
// 背景颜色
backgroundColor: '#fafafa',
// 背景图片
backgroundImage: 'none',
// 背景重复
backgroundRepeat: 'no-repeat',
// 节点使用横线样式
nodeUseLineStyle: false,
// 根节点样式
root: {
shape: 'rectangle',
fillColor: '#549688',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: 'transparent',
borderWidth: 0,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
},
// 二级节点样式
second: {
shape: 'rectangle',
marginX: 100,
marginY: 40,
fillColor: '#fff',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#565656',
fontSize: 16,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: '#549688',
borderWidth: 1,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
},
// 三级及以下节点样式
node: {
shape: 'rectangle',
marginX: 50,
marginY: 0,
fillColor: 'transparent',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#6a6d6c',
fontSize: 14,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: 'transparent',
borderWidth: 0,
borderRadius: 5,
borderDasharray: 'none',
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
},
// 概要节点样式
generalization: {
shape: 'rectangle',
marginX: 100,
marginY: 40,
fillColor: '#fff',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#565656',
fontSize: 16,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: '#549688',
borderWidth: 1,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
}
}
// 支持激活样式的属性
// 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小
export const supportActiveStyle = [
'fillColor',
'color',
'fontWeight',
'fontStyle',
'borderColor',
'borderWidth',
'borderDasharray',
'borderRadius',
'textDecoration'
]
// 默认主题
export default {
// 节点内边距
paddingX: 15,
paddingY: 5,
// 图片显示的最大宽度
imgMaxWidth: 100,
// 图片显示的最大高度
imgMaxHeight: 100,
// icon的大小
iconSize: 20,
// 连线的粗细
lineWidth: 1,
// 连线的颜色
lineColor: '#549688',
// 连线样式
lineDasharray: 'none',
// 连线风格
lineStyle: 'straight', // 针对logicalStructure、mindMap两种结构。曲线curve、直线straight、直连direct
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#549688',
// 概要曲线距节点的距离
generalizationLineMargin: 0,
// 概要节点距节点的距离
generalizationNodeMargin: 20,
// 背景颜色
backgroundColor: '#fafafa',
// 背景图片
backgroundImage: 'none',
// 背景重复
backgroundRepeat: 'no-repeat',
// 设置背景图像的起始位置
backgroundPosition: 'center center',
// 设置背景图片大小
backgroundSize: 'cover',
// 节点使用横线样式
nodeUseLineStyle: false,
// 根节点样式
root: {
shape: 'rectangle',
fillColor: '#549688',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: 'transparent',
borderWidth: 0,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
},
// 二级节点样式
second: {
shape: 'rectangle',
marginX: 100,
marginY: 40,
fillColor: '#fff',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#565656',
fontSize: 16,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: '#549688',
borderWidth: 1,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
},
// 三级及以下节点样式
node: {
shape: 'rectangle',
marginX: 50,
marginY: 0,
fillColor: 'transparent',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#6a6d6c',
fontSize: 14,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: 'transparent',
borderWidth: 0,
borderRadius: 5,
borderDasharray: 'none',
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
},
// 概要节点样式
generalization: {
shape: 'rectangle',
marginX: 100,
marginY: 40,
fillColor: '#fff',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#565656',
fontSize: 16,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: '#549688',
borderWidth: 1,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none'
}
}
}
// 支持激活样式的属性
// 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小
export const supportActiveStyle = [
'fillColor',
'color',
'fontWeight',
'fontStyle',
'borderColor',
'borderWidth',
'borderDasharray',
'borderRadius',
'textDecoration'
]
export const lineStyleProps = ['lineColor', 'lineDasharray', 'lineWidth']

View File

@@ -224,3 +224,45 @@ export const asyncRun = (taskList, callback = () => {}) => {
}
loop()
}
// 角度转弧度
export const degToRad = deg => {
return deg * (Math.PI / 180)
}
// 驼峰转连字符
export const camelCaseToHyphen = (str) => {
return str.replace(/([a-z])([A-Z])/g, (...args) => {
return args[1] + '-' + args[2].toLowerCase()
})
}
//计算节点的文本长宽
let measureTextContext = null
export const measureText = (text, { italic, bold, fontSize, fontFamily }) => {
const font = joinFontStr({
italic,
bold,
fontSize,
fontFamily
})
if (!measureTextContext) {
const canvas = document.createElement('canvas')
measureTextContext = canvas.getContext('2d')
}
measureTextContext.save()
measureTextContext.font = font
const {
width,
actualBoundingBoxAscent,
actualBoundingBoxDescent
} = measureTextContext.measureText(text)
measureTextContext.restore()
const height = actualBoundingBoxAscent + actualBoundingBoxDescent
return { width, height }
}
// 拼接font字符串
export const joinFontStr = ({ italic, bold, fontSize, fontFamily }) => {
return `${italic ? 'italic ' : ''} ${bold ? 'bold ' : ''} ${fontSize}px ${fontFamily} `
}

View File

@@ -0,0 +1,354 @@
// 将以空格分隔的字符串值转换成成数字/单位/值数组
const getNumberValueFromStr = value => {
let arr = String(value).split(/\s+/)
return arr.map(item => {
if (/^[\d.]+/.test(item)) {
// 数字+单位
let res = /^([\d.]+)(.*)$/.exec(item)
return [Number(res[1]), res[2]]
} else {
// 单个值
return item
}
})
}
// 缩放宽度
const zoomWidth = (ratio, height) => {
// w / height = ratio
return ratio * height
}
// 缩放高度
const zoomHeight = (ratio, width) => {
// width / h = ratio
return width / ratio
}
// 关键词到百分比值的映射
const keyWordToPercentageMap = {
left: 0,
top: 0,
center: 50,
bottom: 100,
right: 100
}
// 模拟background-size
const handleBackgroundSize = ({
backgroundSize,
drawOpt,
imageRatio,
canvasWidth,
canvasHeight,
canvasRatio
}) => {
if (backgroundSize) {
// 将值转换成数组
let backgroundSizeValueArr = getNumberValueFromStr(backgroundSize)
// 两个值都为auto那就相当于不设置
if (
backgroundSizeValueArr[0] === 'auto' &&
backgroundSizeValueArr[1] === 'auto'
) {
return
}
// 值为cover
if (backgroundSizeValueArr[0] === 'cover') {
if (imageRatio > canvasRatio) {
// 图片的宽高比大于canvas的宽高比那么图片高度缩放到和canvas的高度一致宽度自适应
drawOpt.height = canvasHeight
drawOpt.width = zoomWidth(imageRatio, canvasHeight)
} else {
// 否则图片宽度缩放到和canvas的宽度一致高度自适应
drawOpt.width = canvasWidth
drawOpt.height = zoomHeight(imageRatio, canvasWidth)
}
return
}
// 值为contain
if (backgroundSizeValueArr[0] === 'contain') {
if (imageRatio > canvasRatio) {
// 图片的宽高比大于canvas的宽高比那么图片宽度缩放到和canvas的宽度一致高度自适应
drawOpt.width = canvasWidth
drawOpt.height = zoomHeight(imageRatio, canvasWidth)
} else {
// 否则图片高度缩放到和canvas的高度一致宽度自适应
drawOpt.height = canvasHeight
drawOpt.width = zoomWidth(imageRatio, canvasHeight)
}
return
}
// 图片宽度
let newNumberWidth = -1
if (backgroundSizeValueArr[0]) {
if (Array.isArray(backgroundSizeValueArr[0])) {
// 数字+单位类型
if (backgroundSizeValueArr[0][1] === '%') {
// %单位
drawOpt.width = (backgroundSizeValueArr[0][0] / 100) * canvasWidth
newNumberWidth = drawOpt.width
} else {
// 其他都认为是px单位
drawOpt.width = backgroundSizeValueArr[0][0]
newNumberWidth = backgroundSizeValueArr[0][0]
}
} else if (backgroundSizeValueArr[0] === 'auto') {
// auto类型那么根据设置的新高度以图片原宽高比进行自适应
if (backgroundSizeValueArr[1]) {
if (backgroundSizeValueArr[1][1] === '%') {
// 高度为%单位
drawOpt.width = zoomWidth(
imageRatio,
(backgroundSizeValueArr[1][0] / 100) * canvasHeight
)
} else {
// 其他都认为是px单位
drawOpt.width = zoomWidth(imageRatio, backgroundSizeValueArr[1][0])
}
}
}
}
// 设置了图片高度
if (backgroundSizeValueArr[1] && Array.isArray(backgroundSizeValueArr[1])) {
// 数字+单位类型
if (backgroundSizeValueArr[1][1] === '%') {
// 高度为%单位
drawOpt.height = (backgroundSizeValueArr[1][0] / 100) * canvasHeight
} else {
// 其他都认为是px单位
drawOpt.height = backgroundSizeValueArr[1][0]
}
} else if (newNumberWidth !== -1) {
// 没有设置图片高度或者设置为auto那么根据设置的新宽度以图片原宽高比进行自适应
drawOpt.height = zoomHeight(imageRatio, newNumberWidth)
}
}
}
// 模拟background-position
const handleBackgroundPosition = ({
backgroundPosition,
drawOpt,
imgWidth,
imgHeight,
canvasWidth,
canvasHeight
}) => {
if (backgroundPosition) {
// 将值转换成数组
let backgroundPositionValueArr = getNumberValueFromStr(backgroundPosition)
// 将关键词转为百分比
backgroundPositionValueArr = backgroundPositionValueArr.map(item => {
if (typeof item === 'string') {
return keyWordToPercentageMap[item] !== undefined
? [keyWordToPercentageMap[item], '%']
: item
}
return item
})
if (Array.isArray(backgroundPositionValueArr[0])) {
if (backgroundPositionValueArr.length === 1) {
// 如果只设置了一个值第二个默认为50%
backgroundPositionValueArr.push([50, '%'])
}
// 水平位置
if (backgroundPositionValueArr[0][1] === '%') {
// 单位为%
let canvasX = (backgroundPositionValueArr[0][0] / 100) * canvasWidth
let imgX = (backgroundPositionValueArr[0][0] / 100) * imgWidth
// 计算差值
drawOpt.x = canvasX - imgX
} else {
// 其他单位默认都为px
drawOpt.x = backgroundPositionValueArr[0][0]
}
// 垂直位置
if (backgroundPositionValueArr[1][1] === '%') {
// 单位为%
let canvasY = (backgroundPositionValueArr[1][0] / 100) * canvasHeight
let imgY = (backgroundPositionValueArr[1][0] / 100) * imgHeight
// 计算差值
drawOpt.y = canvasY - imgY
} else {
// 其他单位默认都为px
drawOpt.y = backgroundPositionValueArr[1][0]
}
}
}
}
// 模拟background-repeat
const handleBackgroundRepeat = ({
ctx,
image,
backgroundRepeat,
drawOpt,
imgWidth,
imgHeight,
canvasWidth,
canvasHeight
}) => {
if (backgroundRepeat) {
// 保存在handleBackgroundPosition中计算出来的x、y
let ox = drawOpt.x
let oy = drawOpt.y
// 计算ox和oy能平铺的图片数量
let oxRepeatNum = Math.ceil(ox / imgWidth)
let oyRepeatNum = Math.ceil(oy / imgHeight)
// 计算ox和oy第一张图片的位置
let oxRepeatX = ox - oxRepeatNum * imgWidth
let oxRepeatY = oy - oyRepeatNum * imgHeight
// 将值转换成数组
let backgroundRepeatValueArr = getNumberValueFromStr(backgroundRepeat)
// 不处理
if (
backgroundRepeatValueArr[0] === 'no-repeat' ||
(imgWidth >= canvasWidth && imgHeight >= canvasHeight)
) {
return
}
// 水平平铺
if (backgroundRepeatValueArr[0] === 'repeat-x') {
if (canvasWidth > imgWidth) {
let x = oxRepeatX
while (x < canvasWidth) {
drawImage(ctx, image, {
...drawOpt,
x
})
x += imgWidth
}
return true
}
}
// 垂直平铺
if (backgroundRepeatValueArr[0] === 'repeat-y') {
if (canvasHeight > imgHeight) {
let y = oxRepeatY
while (y < canvasHeight) {
drawImage(ctx, image, {
...drawOpt,
y
})
y += imgHeight
}
return true
}
}
// 平铺
if (backgroundRepeatValueArr[0] === 'repeat') {
let x = oxRepeatX
while (x < canvasWidth) {
if (canvasHeight > imgHeight) {
let y = oxRepeatY
while (y < canvasHeight) {
drawImage(ctx, image, {
...drawOpt,
x,
y
})
y += imgHeight
}
}
x += imgWidth
}
return true
}
}
}
// 根据参数绘制图片
const drawImage = (ctx, image, drawOpt) => {
ctx.drawImage(
image,
drawOpt.sx,
drawOpt.sy,
drawOpt.swidth,
drawOpt.sheight,
drawOpt.x,
drawOpt.y,
drawOpt.width,
drawOpt.height
)
}
const drawBackgroundImageToCanvas = (
ctx,
width,
height,
img,
{ backgroundSize, backgroundPosition, backgroundRepeat },
callback = () => {}
) => {
// 画布的长宽比
let canvasRatio = width / height
// 加载图片
let image = new Image()
image.src = img
image.onload = () => {
// 图片的宽度及长宽比
let imgWidth = image.width
let imgHeight = image.height
let imageRatio = imgWidth / imgHeight
// 绘制图片
// drawImage方法的参数值
let drawOpt = {
sx: 0,
sy: 0,
swidth: imgWidth,
sheight: imgHeight,
x: 0,
y: 0,
width: imgWidth,
height: imgHeight
}
// 模拟background-size
handleBackgroundSize({
backgroundSize,
drawOpt,
imageRatio,
canvasWidth: width,
canvasHeight: height,
canvasRatio
})
// 模拟background-position
handleBackgroundPosition({
backgroundPosition,
drawOpt,
imgWidth: drawOpt.width,
imgHeight: drawOpt.height,
imageRatio,
canvasWidth: width,
canvasHeight: height,
canvasRatio
})
// 模拟background-repeat
let notNeedDraw = handleBackgroundRepeat({
ctx,
image,
backgroundRepeat,
drawOpt,
imgWidth: drawOpt.width,
imgHeight: drawOpt.height,
imageRatio,
canvasWidth: width,
canvasHeight: height,
canvasRatio
})
// 绘制图片
if (!notNeedDraw) {
drawImage(ctx, image, drawOpt)
}
callback()
}
image.onerror = e => {
callback(e)
}
}
export default drawBackgroundImageToCanvas

163
web/package-lock.json generated
View File

@@ -11,6 +11,7 @@
"@toast-ui/editor": "^3.1.5",
"core-js": "^3.6.5",
"element-ui": "^2.15.1",
"highlight.js": "^10.7.3",
"v-viewer": "^1.6.4",
"vue": "^2.6.11",
"vue-i18n": "^8.27.2",
@@ -23,10 +24,13 @@
"@vue/cli-plugin-eslint": "^4.5.0",
"@vue/cli-service": "^4.5.0",
"babel-eslint": "^10.1.0",
"chokidar": "^3.5.3",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"markdown-it": "^13.0.1",
"markdown-it-checkbox": "^1.1.0",
"prettier": "^1.19.1",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.44.2"
@@ -3521,7 +3525,6 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8"
}
@@ -4135,7 +4138,6 @@
"url": "https://paulmillr.com/funding/"
}
],
"optional": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -4157,7 +4159,6 @@
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"optional": true,
"dependencies": {
"fill-range": "^7.0.1"
},
@@ -4170,7 +4171,6 @@
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"optional": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -4183,7 +4183,6 @@
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"optional": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -4196,7 +4195,6 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"optional": true,
"engines": {
"node": ">=0.12.0"
}
@@ -4206,7 +4204,6 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"optional": true,
"dependencies": {
"is-number": "^7.0.0"
},
@@ -7846,7 +7843,6 @@
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"dev": true,
"engines": {
"node": "*"
}
@@ -8604,7 +8600,6 @@
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"optional": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -9334,6 +9329,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dev": true,
"dependencies": {
"uc.micro": "^1.0.1"
}
},
"node_modules/loader-fs-cache": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
@@ -9554,6 +9558,49 @@
"node": ">=0.10.0"
}
},
"node_modules/markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"bin": {
"markdown-it": "bin/markdown-it.js"
}
},
"node_modules/markdown-it-checkbox": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/markdown-it-checkbox/-/markdown-it-checkbox-1.1.0.tgz",
"integrity": "sha512-NkZVjnXo5G+cLNdi7DPZxICypBuxFE9F8sx3YGMZn+Cfizr8EZ/1TFUKl7ZnefF6cr1aFHbnQ5iA3rc4cp7EyA==",
"dev": true,
"dependencies": {
"underscore": "^1.8.2"
}
},
"node_modules/markdown-it/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/markdown-it/node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true,
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -9571,6 +9618,12 @@
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
"dev": true
},
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
"dev": true
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -12049,7 +12102,6 @@
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"optional": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -14266,6 +14318,12 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"dev": true
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"node_modules/uglify-js": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
@@ -14303,6 +14361,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@@ -18871,8 +18935,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"optional": true
"dev": true
},
"bindings": {
"version": "1.5.0",
@@ -19382,7 +19445,6 @@
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -19399,7 +19461,6 @@
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"optional": true,
"requires": {
"fill-range": "^7.0.1"
}
@@ -19409,7 +19470,6 @@
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"optional": true,
"requires": {
"to-regex-range": "^5.0.1"
}
@@ -19419,7 +19479,6 @@
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"optional": true,
"requires": {
"is-glob": "^4.0.1"
}
@@ -19428,15 +19487,13 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"optional": true
"dev": true
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"optional": true,
"requires": {
"is-number": "^7.0.0"
}
@@ -22289,8 +22346,7 @@
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"dev": true
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
},
"hmac-drbg": {
"version": "1.0.1",
@@ -22870,7 +22926,6 @@
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"optional": true,
"requires": {
"binary-extensions": "^2.0.0"
}
@@ -23414,6 +23469,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dev": true,
"requires": {
"uc.micro": "^1.0.1"
}
},
"loader-fs-cache": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
@@ -23593,6 +23657,42 @@
"object-visit": "^1.0.0"
}
},
"markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dev": true,
"requires": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true
}
}
},
"markdown-it-checkbox": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/markdown-it-checkbox/-/markdown-it-checkbox-1.1.0.tgz",
"integrity": "sha512-NkZVjnXo5G+cLNdi7DPZxICypBuxFE9F8sx3YGMZn+Cfizr8EZ/1TFUKl7ZnefF6cr1aFHbnQ5iA3rc4cp7EyA==",
"dev": true,
"requires": {
"underscore": "^1.8.2"
}
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -23610,6 +23710,12 @@
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
"dev": true
},
"mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
"dev": true
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -25678,7 +25784,6 @@
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"optional": true,
"requires": {
"picomatch": "^2.2.1"
}
@@ -27502,6 +27607,12 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"dev": true
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"uglify-js": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
@@ -27532,6 +27643,12 @@
"which-boxed-primitive": "^1.0.2"
}
},
"underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
"unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",

View File

@@ -6,13 +6,16 @@
"serve": "vue-cli-service serve",
"build": "vue-cli-service build && node ../copy.js",
"lint": "vue-cli-service lint",
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/index.js --dest ../simple-mind-map/dist",
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*"
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist",
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*",
"buildDoc": "node ./scripts/buildDoc.js",
"autoBuildDoc": "node ./scripts/autoBuildDoc.js"
},
"dependencies": {
"@toast-ui/editor": "^3.1.5",
"core-js": "^3.6.5",
"element-ui": "^2.15.1",
"highlight.js": "^10.7.3",
"v-viewer": "^1.6.4",
"vue": "^2.6.11",
"vue-i18n": "^8.27.2",
@@ -25,10 +28,13 @@
"@vue/cli-plugin-eslint": "^4.5.0",
"@vue/cli-service": "^4.5.0",
"babel-eslint": "^10.1.0",
"chokidar": "^3.5.3",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"markdown-it": "^13.0.1",
"markdown-it-checkbox": "^1.1.0",
"prettier": "^1.19.1",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.44.2"

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="./dist/logo.png">
<title>一个简单的web思维导图实现</title>
</head>
<body>

BIN
web/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -0,0 +1,38 @@
const chokidar = require('chokidar')
const path = require('path')
const fs = require('fs')
const { exec } = require('node:child_process')
const { transformMdToVue } = require('./transformMdToVue')
const reBuildAll = () => {
exec(
'node ./buildDoc.js',
{
cwd: path.resolve(__dirname)
},
(error, msg) => {
console.log(error, msg)
}
)
}
const buildOne = file => {
let content = fs.readFileSync(file, 'utf-8')
let doc = transformMdToVue(content)
let destPath = path.join(path.dirname(file), './index.vue')
fs.writeFileSync(destPath, doc)
}
chokidar
.watch(path.join(__dirname, '../src/pages/Doc/'), {
ignoreInitial: true
})
.on('all', (event, file) => {
if (/\.md$/.test(file)) {
if (event === 'change') {
buildOne(file)
} else {
reBuildAll()
}
}
})

85
web/scripts/buildDoc.js Normal file
View File

@@ -0,0 +1,85 @@
// 编译文档
const path = require('path')
const fs = require('fs')
const { transformMdToVue } = require('./transformMdToVue')
// 文档语言种类
let langList = ['zh', 'en']
// 开始转换
const transform = (dir, routerList) => {
let dirs = fs.readdirSync(dir)
dirs.forEach(item => {
let cur = path.join(dir, item)
if (fs.statSync(cur).isDirectory()) {
compilerDir(cur, item, routerList)
}
})
}
// 编译某种语言下的文档
const compilerDir = (dir, dirName, routerList) => {
let files = fs.readdirSync(dir)
files.forEach(file => {
if (file.endsWith('.md')) {
compilerFile(dir, file, dirName, routerList)
}
})
}
// 编译具体的文档
const compilerFile = (dir, file, dirName, routerList) => {
let filePath = path.join(dir, file)
let destPath = path.join(dir, './index.vue')
let content = fs.readFileSync(filePath, 'utf-8')
let title = /(^|\n\r)\s*#\s+([^\n\r]+)/g.exec(content)
if (title && title[2]) {
addRouter(dirName, routerList, title[2])
}
let doc = transformMdToVue(content)
fs.writeFileSync(destPath, doc)
}
// 收集文档路由
const addRouter = (item, routerList, title) => {
routerList.push({
path: item,
title
})
}
// 创建路由
const createRouter = () => {
let content = `
export default ${JSON.stringify(
routerTypeList.map(item => {
return {
lang: item.lang,
children: item.routerList
}
})
)}
`
fs.writeFileSync(
path.join(__dirname, '../src/pages/Doc/routerList.js'),
content
)
}
// 创建目录列表
const createCatalogList = () => {}
// 开始编译
let routerTypeList = []
langList.forEach(lang => {
let dir = path.join(__dirname, '../src/pages/Doc/', `./${lang}/`)
let routerList = []
transform(dir, routerList)
routerTypeList.push({
lang,
routerList
})
})
// 创建路由
createRouter()
console.log('编译完成')

View File

@@ -0,0 +1,31 @@
const path = require('path')
const fs = require('fs')
const hljs = require('highlight.js')
const md = require('markdown-it')({
highlight: function(str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return (
'<pre class="hljs"><code>' +
hljs.highlight(str, {
language: lang,
ignoreIllegals: true
}).value +
'</code></pre>'
)
} catch (__) {}
}
return (
'<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
)
}
}).use(require('markdown-it-checkbox'))
const templatePath = path.join(__dirname, '../src/pages/Doc/Template.vue')
exports.transformMdToVue = (content) => {
let result = md.render(content)
let template = fs.readFileSync(templatePath, 'utf-8')
return template.replace('$$$$', result)
}

View File

@@ -54,6 +54,12 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe64f;</span>
<div class="name">github</div>
<div class="code-name">&amp;#xe64f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6c5;</span>
<div class="name">选择</div>
@@ -342,9 +348,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1668512547595') format('woff2'),
url('iconfont.woff?t=1668512547595') format('woff'),
url('iconfont.ttf?t=1668512547595') format('truetype');
src: url('iconfont.woff2?t=1673600274529') format('woff2'),
url('iconfont.woff?t=1673600274529') format('woff'),
url('iconfont.ttf?t=1673600274529') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -370,6 +376,15 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icongithub"></span>
<div class="name">
github
</div>
<div class="code-name">.icongithub
</div>
</li>
<li class="dib">
<span class="icon iconfont iconchoose1"></span>
<div class="name">
@@ -802,6 +817,14 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icongithub"></use>
</svg>
<div class="name">github</div>
<div class="code-name">#icongithub</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconchoose1"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1668512547595') format('woff2'),
url('iconfont.woff?t=1668512547595') format('woff'),
url('iconfont.ttf?t=1668512547595') format('truetype');
src: url('iconfont.woff2?t=1673600274529') format('woff2'),
url('iconfont.woff?t=1673600274529') format('woff'),
url('iconfont.ttf?t=1673600274529') format('truetype');
}
.iconfont {
@@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icongithub:before {
content: "\e64f";
}
.iconchoose1:before {
content: "\e6c5";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,13 @@
"css_prefix_text": "icon",
"description": "思维导图",
"glyphs": [
{
"icon_id": "8760187",
"name": "github",
"font_class": "github",
"unicode": "e64f",
"unicode_decimal": 58959
},
{
"icon_id": "1009019",
"name": "选择",

BIN
web/src/assets/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -166,6 +166,22 @@ export const backgroundPositionList = [
}
]
// 背景图片大小
export const backgroundSizeList = [
{
name: 'Auto',
value: 'auto'
},
{
name: 'Cover',
value: 'cover'
},
{
name: 'Contain',
value: 'contain'
}
]
// 快捷键列表
export const shortcutKeyList = [
{

View File

@@ -14,7 +14,8 @@ import {
backgroundPositionList as backgroundPositionListZh,
shortcutKeyList as shortcutKeyListZh,
shapeList as shapeListZh,
sidebarTriggerList as sidebarTriggerListZh
sidebarTriggerList as sidebarTriggerListZh,
backgroundSizeList as backgroundSizeListZh
} from './zh'
import {
fontFamilyList as fontFamilyListEn,
@@ -24,7 +25,8 @@ import {
backgroundPositionList as backgroundPositionListEn,
shortcutKeyList as shortcutKeyListEn,
shapeList as shapeListEn,
sidebarTriggerList as sidebarTriggerListEn
sidebarTriggerList as sidebarTriggerListEn,
backgroundSizeList as backgroundSizeListEn
} from './en'
const fontFamilyList = {
@@ -52,6 +54,11 @@ const backgroundPositionList = {
en: backgroundPositionListEn
}
const backgroundSizeList = {
zh: backgroundSizeListZh,
en: backgroundSizeListEn
}
const shortcutKeyList = {
zh: shortcutKeyListZh,
en: shortcutKeyListEn
@@ -81,6 +88,7 @@ export {
lineStyleList,
backgroundRepeatList,
backgroundPositionList,
backgroundSizeList,
shortcutKeyList,
shapeList,
sidebarTriggerList

View File

@@ -221,6 +221,22 @@ export const backgroundPositionList = [
}
]
// 背景图片大小
export const backgroundSizeList = [
{
name: '自动',
value: 'auto'
},
{
name: '覆盖',
value: 'cover'
},
{
name: '保持',
value: 'contain'
}
]
// 数据存储
export const store = {
sidebarZIndex: 1 //侧边栏zIndex

View File

@@ -5,6 +5,8 @@ export default {
color: 'Color',
image: 'Image',
imageRepeat: 'Image repeat',
imagePosition: 'Image position',
imageSize: 'Image size',
line: 'Line',
width: 'Width',
style: 'Style',
@@ -20,7 +22,19 @@ export default {
level2Node: 'Level2 node',
belowLevel2Node: 'Below level2 node',
nodeBorderType: 'Node border style',
nodeUseLineStyle: 'Use only has bottom border style'
nodeUseLineStyle: 'Use only has bottom border style',
otherConfig: 'Other config',
enableFreeDrag: 'Enable node free drag',
watermark: 'Watermark',
showWatermark: 'Is show watermark',
watermarkDefaultText: 'Watermark text',
watermarkText: 'Watermark text',
watermarkTextColor: 'Text color',
watermarkLineSpacing: 'Line spacing',
watermarkTextSpacing: 'Text spacing',
watermarkAngle: 'Angle',
watermarkTextOpacity: 'Text opacity',
watermarkTextFontSize: 'Font size'
},
color: {
moreColor: 'More color'

View File

@@ -5,6 +5,8 @@ export default {
color: '颜色',
image: '图片',
imageRepeat: '图片重复',
imagePosition: '图片位置',
imageSize: '图片大小',
line: '连线',
width: '粗细',
style: '风格',
@@ -20,7 +22,19 @@ export default {
level2Node: '二级节点',
belowLevel2Node: '三级及以下节点',
nodeBorderType: '节点边框风格',
nodeUseLineStyle: '是否使用只有底边框的风格'
nodeUseLineStyle: '是否使用只有底边框的风格',
otherConfig: '其他配置',
enableFreeDrag: '是否开启节点自由拖拽',
watermark: '水印',
showWatermark: '是否显示水印',
watermarkDefaultText: '水印文字',
watermarkText: '水印文字',
watermarkTextColor: '文字颜色',
watermarkLineSpacing: '水印行间距',
watermarkTextSpacing: '水印文字间距',
watermarkAngle: '旋转角度',
watermarkTextOpacity: '文字透明度',
watermarkTextFontSize: '文字字号'
},
color: {
moreColor: '更多颜色'

191
web/src/pages/Doc/Index.vue Normal file
View File

@@ -0,0 +1,191 @@
<template>
<div class="docContainer">
<Header></Header>
<div class="content">
<Sidebar></Sidebar>
<div class="doc" ref="doc" id="doc" @scroll="onScroll">
<router-view></router-view>
</div>
<CatalogBar :scrollTop="scrollTop" @scroll="doScroll"></CatalogBar>
</div>
</div>
</template>
<script>
import Header from './components/Header.vue'
import Sidebar from './components/Sidebar.vue'
import CatalogBar from './components/CatalogBar.vue'
import 'highlight.js/styles/atom-one-dark.css'
export default {
components: {
Header,
Sidebar,
CatalogBar
},
data () {
return {
scrollTop: 0,
}
},
methods: {
doScroll(top) {
this.$nextTick(() => {
try {
this.$refs.doc.scrollTop = top
} catch (error) {
console.log(error)
}
})
},
onScroll() {
this.scrollTop = this.$refs.doc.scrollTop
}
}
}
</script>
<style lang="less">
.docContainer {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
font-family: Quotes, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
.content {
display: flex;
flex-grow: 1;
overflow: hidden;
.doc {
overflow: auto;
flex-grow: 1;
font-weight: 400;
color: #213547;
font-size: 16px;
-webkit-font-smoothing: antialiased;
line-height: 1.7;
padding: 30px;
h1 {
margin: 30px 0;
font-size: 38px;
line-height: 1.4;
}
h2 {
margin: 20px 0;
border-top: 1px solid rgba(60, 60, 60, 0.12);
font-size: 24px;
padding-top: 10px;
}
h3 {
font-size: 19px;
margin: 10px 0;
}
p {
margin-bottom: 20px;
}
a {
font-weight: 500;
text-decoration: none;
color: #42b883;
transition: color 0.25s;
&:hover {
color: #33a06f;
}
}
pre {
margin-bottom: 20px;
border-radius: 5px;
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
code {
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
}
}
:not(pre) > code {
background-color: #f1f1f1;
padding: 0.15em 0.5em;
border-radius: 4px;
color: #476582;
transition: color 0.5s, background-color 0.5s;
font-family: Quotes, -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
}
input[type='checkbox'] {
margin-right: 5px;
}
ul {
list-style: none;
padding-left: 1.25rem;
> li {
position: relative;
margin: 1px 0;
&:before {
content: '';
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
background-color: rgba(60, 60, 60, 0.33);
transition: background-color 0.5s;
left: -1.25rem;
top: 0.75rem;
}
}
}
table {
border-collapse: collapse;
border-spacing: 0;
margin-top: 0.8rem;
margin-bottom: 1.4rem;
}
tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
th,
td {
padding: 5px 14px;
border: 1px solid #ddd;
}
blockquote {
margin: 1rem 0;
border-left: 0.2rem solid rgba(60, 60, 60, 0.29);
padding-left: 1rem;
transition: border-color 0.5s;
> p {
margin: 0;
font-size: 16px;
color: rgba(60, 60, 60, 0.7);
transition: color 0.5s;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,15 @@
<template>
<div>
$$$$
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,78 @@
import routerList from './routerList'
let langList = [
{
name: '中文',
path: 'zh'
},
{
name: 'English',
path: 'en'
}
]
let StartList = ['introduction', 'start', 'translate', 'changelog']
let APIList = [
'constructor',
'node',
'render',
'view',
'keyCommand',
'command',
'batchExecution',
'select',
'drag',
'keyboardNavigation',
'doExport',
'miniMap',
'watermark',
'xmind',
'utils'
]
const createList = (lang, list) => {
let langRouter = routerList.find(item => {
return item.lang === lang
})
let children = langRouter.children
return list
.filter(item => {
return children.find(child => {
return child.path === item
})
})
.map(item => {
return {
path: item,
name: children.find(child => {
return child.path === item
}).title
}
})
}
export default {
zh: [
{
groupName: '开始',
list: createList('zh', StartList)
},
{
groupName: 'API',
list: createList('zh', APIList)
}
],
en: [
{
groupName: 'Start',
list: createList('en', StartList)
},
{
groupName: 'API',
list: createList('en', APIList)
}
]
}
export {
langList
}

View File

@@ -0,0 +1,236 @@
<template>
<div class="catalogBarContainer">
<div class="catalogBarTitle">{{ pageCatalogTitle }}</div>
<div class="catalogList">
<div
class="catalogItem"
v-for="(item, index) in list"
:key="item.title + index"
:class="{ active: item.title === activeCatalog }"
@click="scrollTo(item, index)"
>
{{ item.title }}
</div>
<div
v-if="activeCatalogIndex !== -1"
class="activeBar"
:style="{ top: 4 + activeCatalogIndex * 28 + 'px' }"
></div>
</div>
</div>
</template>
<script>
import t from '../i18n'
export default {
props: {
scrollTop: {
type: Number
}
},
data() {
return {
lang: '',
list: [],
activeCatalog: '',
activeCatalogIndex: -1,
appointCatalog: true
}
},
computed: {
pageCatalogTitle() {
return t('pageCatalog', this.lang)
}
},
watch: {
$route(newVal, oldVal) {
this.initLang()
this.initCatalogList(newVal.path, oldVal.path)
},
scrollTop() {
this.onScroll()
},
lang(newVal, oldVal) {
console.log(newVal, oldVal)
if (!oldVal) {
return
}
this.initCatalogList()
}
},
mounted() {
this.initLang()
this.initCatalogList()
this.scrollToCatalog()
},
methods: {
// 获取当前语言
initLang() {
let lang = /^\/doc\/([^\/]+)\//.exec(this.$route.path)
if (lang && lang[1]) {
this.lang = lang[1]
}
},
// 初始化二级标题目录
initCatalogList(newPath, oldPath) {
let newPathRes = /^\/doc\/[^\/]+\/([^\/]+)/.exec(newPath)
let oldPathRes = /^\/doc\/[^\/]+\/([^\/]+)/.exec(oldPath)
// 语言变了、章节变了,需要重新获取二级标题目录
if ((!newPath && !oldPath) || newPathRes[1] !== oldPathRes[1]) {
this.$emit('scroll', 0)
this.resetActive()
let container = document.getElementById('doc')
let els = document.querySelectorAll('#doc h2')
this.list = Array.from(els).map(item => {
return {
title: item.textContent,
top: item.offsetTop - container.offsetTop
}
})
}
},
// 如果url中存在二级标题那么滚动到该标题所在位置
scrollToCatalog() {
let url = /^\/doc\/[^\/]+\/[^\/]+\/([^\/]+)($|\/)/.exec(this.$route.path)
if (url && url[1]) {
let h = decodeURIComponent(url[1])
let item = this.list.find(item => {
return item.title === h
})
let index = this.list.findIndex(item => {
return item.title === h
})
if (item) {
this.activeCatalog = item.title
this.activeCatalogIndex = index
this.$emit('scroll', item.top)
}
}
},
// 手动点击切换到指定二级标题
scrollTo(item, index) {
this.appointCatalog = true
this.routeToNewCatalog(item.title)
this.$nextTick(() => {
this.activeCatalog = item.title
this.activeCatalogIndex = index
this.scrollToCatalog()
})
},
// 路由到指定二级标题
routeToNewCatalog(title) {
let path = this.$route.path
let url = ''
if (!title) {
url = path.replace(/^(\/doc\/[^\/]+\/[^\/]+)($|\/|.*)$/, '$1')
} else if (/^\/doc\/[^\/]+\/[^\/]+($|\/)$/.test(path)) {
url = path.replace(
/^(\/doc\/[^\/]+\/[^\/]+)($|\/)$/,
'$1/' + encodeURIComponent(title)
)
} else {
url = path.replace(
/^(\/doc\/[^\/]+\/[^\/]+\/)([^\/]+)($|\/)/,
(...args) => {
return args[1] + encodeURIComponent(title)
}
)
}
if (path === url) {
return
}
this.$router.push(url)
},
// 文档滚动时判断当前滚动到哪个二级标题
onScroll() {
if (this.appointCatalog) {
this.appointCatalog = false
return
}
let find = false
for (let i = 0; i < this.list.length; i++) {
let cur = this.list[i]
let next = this.list[i + 1]
if (this.scrollTop >= cur.top && (!next || this.scrollTop < next.top)) {
find = true
if (cur.title === this.activeCatalog) {
break
}
this.activeCatalog = cur.title
this.activeCatalogIndex = i
this.routeToNewCatalog(cur.title)
break
}
}
if (!find) {
this.resetActive()
this.routeToNewCatalog('')
}
},
resetActive() {
this.activeCatalog = ''
this.activeCatalogIndex = -1
}
}
}
</script>
<style lang="less" scoped>
.catalogBarContainer {
width: 20%;
flex-shrink: 0;
overflow-x: hidden;
overflow-y: auto;
padding-top: 60px;
padding-bottom: 30px;
padding-left: 20px;
.catalogBarTitle {
font-weight: 700;
margin-bottom: 4px;
text-transform: uppercase;
font-size: 11px;
letter-spacing: 0.4px;
}
.catalogList {
position: relative;
.catalogItem {
color: rgba(60, 60, 60, 0.7);
transition: color 0.5s;
line-height: 28px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 500;
cursor: pointer;
&.active {
color: rgba(60, 60, 60, 1);
font-weight: bold;
}
}
.activeBar {
position: absolute;
left: -10px;
width: 4px;
height: 20px;
background-color: #42b883;
border-radius: 4px;
transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), opacity 0.25s,
background-color 0.5s;
}
}
}
</style>

View File

@@ -0,0 +1,171 @@
<template>
<div class="headerContainer">
<div class="left">
<div class="title">
<img src="../../../assets/img/logo.png" alt="">
SimpleMindMap
</div>
</div>
<div class="center">
<div class="btn" @click="toDemo">{{ demoName }}</div>
<el-dropdown
trigger="click"
placement="bottom-start"
@command="handleCommand"
>
<span class="translateBtn">
{{ currentLangName }}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in otherLangList"
:key="item.path"
:command="item.path"
>{{ item.name }}</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
<a href="https://github.com/wanglin2/mind-map" target="_blank">
<span class="iconfont icongithub"></span>
</a>
</div>
<div class="right"></div>
</div>
</template>
<script>
import { langList } from '../catalogList'
import t from '../i18n'
export default {
data() {
return {
lang: '',
currentLangName: '',
otherLangList: []
}
},
computed: {
demoName() {
return t('demo', this.lang)
}
},
watch: {
$route() {
this.init()
}
},
created() {
this.init()
},
methods: {
init() {
let lang = /^\/doc\/([^\/]+)\//.exec(this.$route.path)
if (lang && lang[1]) {
this.lang = lang[1]
let currentLang = langList.find(item => {
return item.path === this.lang
})
this.currentLangName = currentLang.name
this.otherLangList = langList.filter(item => {
return item.path !== this.lang
})
}
},
toDemo() {
this.$router.push('/')
},
handleCommand(path) {
let url = this.$route.path.replace(/^\/doc\/([^\/]+)\//, (...args) => {
return `/doc/${path}/`
})
this.$router.push(url)
}
}
}
</script>
<style lang="less" scoped>
.headerContainer {
height: 55px;
border-bottom: 1px solid rgba(60, 60, 60, 0.12);
flex-shrink: 0;
display: flex;
justify-content: space-between;
.left {
width: 30%;
display: flex;
align-items: center;
justify-content: flex-end;
.title {
width: 200px;
font-size: 24px;
font-weight: bold;
display: flex;
align-items: center;
img {
width: 30px;
margin-right: 10px;
}
}
}
.center {
display: flex;
align-items: center;
flex-grow: 1;
justify-content: flex-end;
.btn {
color: #213547;
cursor: pointer;
transition: color 0.5s;
margin-right: 15px;
font-size: 14px;
&:hover {
color: #42b883;
}
}
.translateBtn {
margin-right: 15px;
font-size: 16px;
color: #213547;
cursor: pointer;
margin-top: 1px;
display: block;
font-size: 14px;
}
a {
text-decoration: none;
color: rgba(60, 60, 60, 0.7);
transition: color 0.5s;
margin-right: 15px;
&:last-of-type {
margin-right: 0;
}
&:hover {
color: rgba(60, 60, 60, 1);
}
.iconfont {
font-size: 30px;
}
}
}
.right {
width: 20%;
}
}
</style>

View File

@@ -0,0 +1,118 @@
<template>
<div class="sideBarContainer">
<div class="catalogGroupList">
<div
class="catalogGroup"
v-for="(group, groupIndex) in groupList"
:key="groupIndex"
>
<div class="catalogGroupName">{{ group.groupName }}</div>
<div class="catalogList">
<div
class="catalogItem"
v-for="item in group.list"
:key="groupIndex + item.path"
:class="{ active: item.path === currentPath }"
@click="jump(item)"
>
{{ item.name }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import catalogList from '../catalogList'
export default {
data() {
return {
groupList: [],
lang: '',
currentPath: ''
}
},
created() {
this.initCatalog()
},
watch: {
$route() {
this.initCatalog()
}
},
methods: {
jump(item) {
if (item.path === this.currentPath) {
return
}
this.$router.push(`/doc/${this.lang}/${item.path}`)
},
initCatalog() {
// 目录列表
let lang = /^\/doc\/([^\/]+)\//.exec(this.$route.path)
if (lang && lang[1]) {
this.lang = lang[1]
this.groupList = catalogList[this.lang]
}
// 当前所在路径
let path = /^\/doc\/[^\/]+\/([^\/]+)(\/|$)/.exec(this.$route.path)
if (path && path[1]) {
this.currentPath = path[1]
}
}
}
}
</script>
<style lang="less" scoped>
.sideBarContainer {
width: 30%;
overflow-x: hidden;
overflow-y: auto;
display: flex;
justify-content: flex-end;
padding-top: 60px;
padding-bottom: 30px;
flex-shrink: 0;
.catalogGroupList {
width: 200px;
.catalogGroup {
padding-bottom: 16px;
.catalogGroupName {
line-height: 20px;
font-size: 13px;
font-weight: 600;
color: #213547;
transition: color 0.5s;
padding: 4px 0;
}
.catalogList {
.catalogItem {
line-height: 20px;
font-size: 13px;
font-weight: 500;
color: rgba(60, 60, 60, 0.7);
transition: color 0.5s;
cursor: pointer;
padding: 4px 0;
&:hover {
color: rgba(60, 60, 60, 1);
}
&.active {
color: #42b883;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,16 @@
# batchExecution instance
The `batchExecution` is used to batch asynchronously perform some operations,
and if a certain operation is called multiple times at the same time, it will
only be executed once in the next event loop. Can be obtained through
`mindMap.batchExecution`
## Method
### push(name, fn)
Add task.
`name`: task name
`fn`: task

View File

@@ -0,0 +1,25 @@
<template>
<div>
<h1>batchExecution instance</h1>
<p>The <code>batchExecution</code> is used to batch asynchronously perform some operations,
and if a certain operation is called multiple times at the same time, it will
only be executed once in the next event loop. Can be obtained through
<code>mindMap.batchExecution</code></p>
<h2>Method</h2>
<h3>push(name, fn)</h3>
<p>Add task.</p>
<p><code>name</code>: task name</p>
<p><code>fn</code>: task</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,173 @@
# Changelog
## 0.3.4
NewAutomatic line wrapping function is added to node text.
Fix: 1.Fix the problem of deletion exceptions if there are root nodes in the batch deleted nodes. 2.Fix the problem that high node height will overlap with other nodes in the case of bottom edge style.
## 0.3.3
Fix: The root node text cannot wrap.
## 0.3.2
Fix: 1.Fix the problem that the node style is not updated when the secondary node is dragged to other nodes or other nodes are dragged to the secondary node; 2.Fix the problem that when the actual content of the mind map is larger than the screen width and height, the excess part is not watermarked when exporting.
## 0.3.1
Fix: 1.The problem that deleting the background image does not take effect; 2.The problem that the connector runs above the root node when the node is dragged to the root node.
New: Add position and size settings for background image display. This setting is also supported for exported pictures.
## 0.3.0
Upgrade to plugin architecture, pull out some non-core functions as plugins, register as needed, and reduce the overall volume.
## 0.2.24
New: Node free drag is changed to configurable, the default is `false`, not open; Support add watermark.
## 0.2.23
New: Support register new theme.
## 0.2.22
optimizationThe theme and structure pictures of the built-in `simple-mind-map` package are removed and replaced by user self-maintenance. The original pictures can be found in the `web/assets/img/` directory.
## 0.2.21
New: Support node horizontal line style.
## 0.2.20
fixWhen the distance from the canvas to the upper left corner of the window is not 0, the node dragging will have an offset problem.
## 0.2.19
fixWhen the node is not activated, pressing any key will trigger the problem of automatic focus.
## 0.2.18
optimizationKeyboard navigation algorithm for finding focus, supporting simple algorithm, region algorithm and shadow algorithm.
## 0.2.17
NewKeyboard navigation, that is, switch the active nodes through the direction keys; The node text content can be edited directly in the outline.
## 0.2.16
optimizationMini map; drag performance.
## 0.2.15
optimizationLocal file editing.
NewDouble-click the image in the node to preview the large image.
## 0.2.14
optimizationAutomatically expand when inserting child nodes.
fixThe error occurred when the mini map was closed.
## 0.2.13
fixThe child node is missing when collapsing state replication.
## 0.2.11
fixFix the problem that is lost when the child node collapses state replication.
NewSupport mini map.
## 0.2.10
optimizationFocus immediately when you manually create a node.
fixConnection style depth update problem.
NewLogical structure diagram and mind map add linear connection style and direct connection style.
## 0.2.9
NewSupport the creation, opening and saving of local files on the computer.
## 0.2.8
fixXmind8 version file import failed.
NewExpanding to the specified level is supported.
## 0.2.7
fixThe root node adds multiple nodes to burst the stack.
NewSupport import .xmind file.
## 0.2.6
NewThe title tag is added when exporting svg.
## 0.2.5
fixBugs caused by node expansion and collapse.
NewNode supports custom line styles.
## 0.2.4
NewNodes support multiple shapes.
## 0.2.3
fixShortcut key conflicts when editing node text; Right-click menu shortcut prompt error; Right-click menu shortcut prompt.
## 0.2.2
fixThe input string '/' conflicts with the shortcut key '/'.
## 0.2.1
NewSupport export as pdf.
## 0.2.0
NewClassic4 themeSupport adding summary; Support free drag; Move Node Up, Move Node Down, Copy Node, Cut Node, Paste Node, One-click Organize Cloth Shortcut; Library packaging; Ctrl+left click to select multiple.
## 0.1.18
fixThe problem that the node icon cannot be deleted; The tool button is grayed out and can still be clicked.
## 0.1.17
NewAdd read-only mode.
## 0.1.16
NewNode notes support markdown and rich text.
fixCan't select text; Node annotations cannot hide problems after node activation; When editing text such as hyperlinks, notes, labels, etc., the return key and return key conflict with the shortcut key of mind map.
## 0.1.15
NewThe status data supports saving the active status and view status (drag position, zoom value)Support node drag.
## 0.1.14
fixThere are problems with setting topics when activating nodes.
## 0.1.13
NewShortcut key function; Support export as json。
optimizationSome details.
## 0.1.12
NewLocal storageRight-click menu function, etc.
## 0.1.0
Complete basic functions.

View File

@@ -0,0 +1,102 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.3.4</h2>
<p>NewAutomatic line wrapping function is added to node text.</p>
<p>Fix: 1.Fix the problem of deletion exceptions if there are root nodes in the batch deleted nodes. 2.Fix the problem that high node height will overlap with other nodes in the case of bottom edge style.</p>
<h2>0.3.3</h2>
<p>Fix: The root node text cannot wrap.</p>
<h2>0.3.2</h2>
<p>Fix: 1.Fix the problem that the node style is not updated when the secondary node is dragged to other nodes or other nodes are dragged to the secondary node; 2.Fix the problem that when the actual content of the mind map is larger than the screen width and height, the excess part is not watermarked when exporting.</p>
<h2>0.3.1</h2>
<p>Fix: 1.The problem that deleting the background image does not take effect; 2.The problem that the connector runs above the root node when the node is dragged to the root node.</p>
<p>New: Add position and size settings for background image display. This setting is also supported for exported pictures.</p>
<h2>0.3.0</h2>
<p>Upgrade to plugin architecture, pull out some non-core functions as plugins, register as needed, and reduce the overall volume.</p>
<h2>0.2.24</h2>
<p>New: Node free drag is changed to configurable, the default is <code>false</code>, not open; Support add watermark.</p>
<h2>0.2.23</h2>
<p>New: Support register new theme.</p>
<h2>0.2.22</h2>
<p>optimizationThe theme and structure pictures of the built-in <code>simple-mind-map</code> package are removed and replaced by user self-maintenance. The original pictures can be found in the <code>web/assets/img/</code> directory.</p>
<h2>0.2.21</h2>
<p>New: Support node horizontal line style.</p>
<h2>0.2.20</h2>
<p>fixWhen the distance from the canvas to the upper left corner of the window is not 0, the node dragging will have an offset problem.</p>
<h2>0.2.19</h2>
<p>fixWhen the node is not activated, pressing any key will trigger the problem of automatic focus.</p>
<h2>0.2.18</h2>
<p>optimizationKeyboard navigation algorithm for finding focus, supporting simple algorithm, region algorithm and shadow algorithm.</p>
<h2>0.2.17</h2>
<p>NewKeyboard navigation, that is, switch the active nodes through the direction keys; The node text content can be edited directly in the outline.</p>
<h2>0.2.16</h2>
<p>optimizationMini map; drag performance.</p>
<h2>0.2.15</h2>
<p>optimizationLocal file editing.</p>
<p>NewDouble-click the image in the node to preview the large image.</p>
<h2>0.2.14</h2>
<p>optimizationAutomatically expand when inserting child nodes.</p>
<p>fixThe error occurred when the mini map was closed.</p>
<h2>0.2.13</h2>
<p>fixThe child node is missing when collapsing state replication.</p>
<h2>0.2.11</h2>
<p>fixFix the problem that is lost when the child node collapses state replication.</p>
<p>NewSupport mini map.</p>
<h2>0.2.10</h2>
<p>optimizationFocus immediately when you manually create a node.</p>
<p>fixConnection style depth update problem.</p>
<p>NewLogical structure diagram and mind map add linear connection style and direct connection style.</p>
<h2>0.2.9</h2>
<p>NewSupport the creation, opening and saving of local files on the computer.</p>
<h2>0.2.8</h2>
<p>fixXmind8 version file import failed.</p>
<p>NewExpanding to the specified level is supported.</p>
<h2>0.2.7</h2>
<p>fixThe root node adds multiple nodes to burst the stack.</p>
<p>NewSupport import .xmind file.</p>
<h2>0.2.6</h2>
<p>NewThe title tag is added when exporting svg.</p>
<h2>0.2.5</h2>
<p>fixBugs caused by node expansion and collapse.</p>
<p>NewNode supports custom line styles.</p>
<h2>0.2.4</h2>
<p>NewNodes support multiple shapes.</p>
<h2>0.2.3</h2>
<p>fixShortcut key conflicts when editing node text; Right-click menu shortcut prompt error; Right-click menu shortcut prompt.</p>
<h2>0.2.2</h2>
<p>fixThe input string '/' conflicts with the shortcut key '/'.</p>
<h2>0.2.1</h2>
<p>NewSupport export as pdf.</p>
<h2>0.2.0</h2>
<p>NewClassic4 themeSupport adding summary; Support free drag; Move Node Up, Move Node Down, Copy Node, Cut Node, Paste Node, One-click Organize Cloth Shortcut; Library packaging; Ctrl+left click to select multiple.</p>
<h2>0.1.18</h2>
<p>fixThe problem that the node icon cannot be deleted; The tool button is grayed out and can still be clicked.</p>
<h2>0.1.17</h2>
<p>NewAdd read-only mode.</p>
<h2>0.1.16</h2>
<p>NewNode notes support markdown and rich text.</p>
<p>fixCan't select text; Node annotations cannot hide problems after node activation; When editing text such as hyperlinks, notes, labels, etc., the return key and return key conflict with the shortcut key of mind map.</p>
<h2>0.1.15</h2>
<p>NewThe status data supports saving the active status and view status (drag position, zoom value)Support node drag.</p>
<h2>0.1.14</h2>
<p>fixThere are problems with setting topics when activating nodes.</p>
<h2>0.1.13</h2>
<p>NewShortcut key function; Support export as json</p>
<p>optimizationSome details.</p>
<h2>0.1.12</h2>
<p>NewLocal storageRight-click menu function, etc.</p>
<h2>0.1.0</h2>
<p>Complete basic functions.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,33 @@
# command instance
The `command` instance is responsible for adding and executing commands. It
includes many built-in commands and can also be added manually. A command refers
to an operation that needs to add a copy to the history stack data. The
`mindMap.command` instance can be obtained through this."
## Methods
### add(name, fn)
Add a command.
`name`: Command name
`fn`: Method to be executed by the command
### remove(name, fn)
Remove a command.
`name`: Name of the command to be removed
`fn`: Method to be removed, if not provided all methods for the command will be
removed
### getCopyData()
Get a copy of the rendering tree data
### clearHistory()
Clear the history stack data

View File

@@ -0,0 +1,34 @@
<template>
<div>
<h1>command instance</h1>
<p>The <code>command</code> instance is responsible for adding and executing commands. It
includes many built-in commands and can also be added manually. A command refers
to an operation that needs to add a copy to the history stack data. The
<code>mindMap.command</code> instance can be obtained through this.&quot;</p>
<h2>Methods</h2>
<h3>add(name, fn)</h3>
<p>Add a command.</p>
<p><code>name</code>: Command name</p>
<p><code>fn</code>: Method to be executed by the command</p>
<h3>remove(name, fn)</h3>
<p>Remove a command.</p>
<p><code>name</code>: Name of the command to be removed</p>
<p><code>fn</code>: Method to be removed, if not provided all methods for the command will be
removed</p>
<h3>getCopyData()</h3>
<p>Get a copy of the rendering tree data</p>
<h3>clearHistory()</h3>
<p>Clear the history stack data</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,327 @@
# Constructor
## Basic use
```html
<div id="mindMapContainer"></div>
```
```js
import MindMap from "simple-mind-map";
const mindMap = new MindMap({
el: document.getElementById("mindMapContainer"),
data: {
"data": {
"text": "Root Node"
},
"children": []
}
});
```
## Instantiation options
| Field Name | Type | Default Value | Description | Required |
| -------------------------------- | ------- | ---------------- | ------------------------------------------------------------ | -------- |
| el | Element | | Container element, must be a DOM element | Yes |
| data | Object | {} | Mind map data, refer to: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js | |
| layout | String | logicalStructure | Layout type, options: logicalStructure (logical structure diagram), mindMap (mind map), catalogOrganization (catalog organization diagram), organizationStructure (organization structure diagram) | |
| theme | String | default | Theme, options: default, classic, minions, pinkGrape, mint, gold, vitalityOrange, greenLeaf, dark2, skyGreen, classic2, classic3, classic4 (v0.2.0+), classicGreen, classicBlue, blueSky, brainImpairedPink, dark, earthYellow, freshGreen, freshRed, romanticPurple | |
| themeConfig | Object | {} | Theme configuration, will be merged with the selected theme, available fields refer to: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js | |
| scaleRatio | Number | 0.1 | The incremental scaling ratio | |
| maxTag | Number | 5 | The maximum number of tags displayed in the node, any additional tags will be discarded | |
| exportPadding | Number | 20 | The padding for exporting images | |
| imgTextMargin | Number | 5 | The spacing between the image and text in the node | |
| textContentMargin | Number | 2 | The spacing between various text information in the node, such as the spacing between the icon and text | |
| selectTranslateStep | Number | 3 | The canvas offset when mouse moves to the edge during multi-select node | |
| 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 | |
| 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 | |
### Watermark config
| Field Name | Type | Default Value | Description |
| ----------- | ------ | ------------------------------------------- | ------------------------------------------------------------ |
| text | String | '' | Watermark text. If it is an empty string, the watermark will not be displayed |
| lineSpacing | Number | 100 | Spacing between watermark lines |
| textSpacing | Number | 100 | Spacing between watermarks in the same row |
| angle | Number | 30 | Tilt angle of watermark, range: [0, 90] |
| textStyle | Object | {color: '#999', opacity: 0.5, fontSize: 14} | Watermark text style |
## Static methods
### defineTheme(name, config)
> v0.2.23+
Define new theme.
`name`New theme name
`config`New theme config
`Simple-mind-map ` Built-in many themes. In addition, you can register new theme. It is recommended to register before instantiation, so that you can directly use the newly registered theme during instantiation. Use example:
```js
import MindMap from 'simple-mind-map'
// 注册新主题
MindMap.defineTheme('Theme name', {})
// 1.实例化时使用新注册的主题
const mindMap = new MindMap({
theme: 'Theme name'
})
// 2.动态切换新主题
mindMap.setTheme('Theme name')
```
For all configurations of theme, please refer to [Default Topic](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js). The `defineTheme`method will merge the configuration you passed in with the default configuration. Most of the themes do not need custom many parts. For a typical customized theme configuration, please refer to [blueSky](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/blueSky.js).
### usePlugin(plugin)
> v0.3.0+
If you need to use some non-core functions, such as mini map, watermark, etc, you can register plugin through this method. Can be called in chain.
Note: The plugin needs to be registered before instantiating `MindMap`.
## Static props
### pluginList
> v0.3.0+
List of all currently registered plugins.
## Instance methods
### getSvgData()
> v0.3.0+
Get the `svg` data and return an object. The detailed structure is as follows:
```js
{
svg, // Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)
svgHTML, // String, svg string, i.e. html string, can be directly rendered to the small map container you prepared
rect: // Object, position, size, etc. of mind map graphics before zoom
origWidth, // Number, canvas width
origHeight, // Number, canvas height
scaleX, // Number, horizontal zoom value of mind map graphics
scaleY, // Number, vertical zoom value of mind map graphics
}
```
### render(callback)
- `callback`: `v0.3.2+`, `Function`, Called when the re-rendering is complete
Triggers a full rendering, which will reuse nodes for better performance. If
only the node positions have changed, this method can be called to `reRender`.
### reRender(callback)
- `callback`: `v0.3.2+`, `Function`, Called when the re-rendering is complete
Performs a full re-render, clearing the canvas and creating new nodes. This has
poor performance and should be used sparingly.
### resize()
After the container size has changed, this method should be called to adjust.
### setMode(mode)
> v0.1.7+
Switches between readonly and edit mode.
`mode`readonly、edit
### on(event, fn)
Listen to an event. Event list:
| Event Name | Description | Callback Parameters |
| -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| data_change | Tree rendering data change, listen to this method to get the latest data | data (current tree rendering data) |
| view_data_changev0.1.1+ | View change data, such as when dragging or zooming | data (current view state data) |
| back_forward | Forward or backward | activeHistoryIndex (current index in the history data array), length (current length of the history data array) |
| draw_click | Canvas click event | e (event object) |
| svg_mousedown | svg canvas mouse down event | e (event object) |
| mousedown | el element mouse down event | e (event object), this (Event event class instance) |
| mousemove | el element mouse move event | e (event object), this (Event event class instance) |
| drag | If it is a drag event while holding down the left button | e (event object), this (Event event class instance) |
| mouseup | el element mouse up event | e (event object), this (Event event class instance) |
| mousewheel | Mouse scroll event | e (event object), dir (up or down scroll), this (Event event class instance) |
| contextmenu | svg canvas right mouse button menu event | e (event object) |
| node_click | Node click event | this (node instance), e (event object) |
| node_mousedown | Node mouse down event | this (node instance), e (event object) |
| node_mouseup | node mouseup event | this (node instance), e (event object) |
| node_mouseup | Node mouseup event | this (node instance), e (event object) |
| node_dblclick | Node double-click event | this (node instance), e (event object) |
| node_contextmenu | Node right-click menu event | e (event object), this (node instance) |
| before_node_active | Event before node activation | this (node instance), activeNodeList (current list of active nodes) |
| node_active | Node activation event | this (node instance), activeNodeList (current list of active nodes) |
| expand_btn_click | Node expand or collapse event | this (node instance) |
| before_show_text_edit | Event before node text edit box opens | |
| hide_text_edit | Node text edit box close event | textEditNode (text edit box DOM node), activeNodeList (current list of active nodes) |
| scale | Zoom event | scale (zoom ratio) |
| node_img_dblclickv0.2.15+ | Node image double-click event | this (node instance), e (event object) |
| node_tree_render_endv0.2.16+ | Node tree render end event | |
### emit(event, ...args)
Trigger an event, which can be one of the events listed above or a custom event.
### off(event, fn)
Unbind an event.
### setTheme(theme)
Switches the theme. Available themes can be found in the options table above.
### getTheme()
Gets the current theme.
### setThemeConfig(config)
Sets the theme configuration. `config` is the same as the `themeConfig` option
in the options table above.
### getCustomThemeConfig()
Gets the custom theme configuration.
### getThemeConfig(prop)
Gets the value of a specific theme configuration property.
### getConfig(*prop*)
> 0.2.24+
`prop`Get the value of the specified configuration, and return the entire configuration if not passed
Get config, That is, `opt` of `new MindMap (opt)`
### updateConfig(*opt* = {})
> 0.2.24+
`opt`Configuration to update
Update configThat is update `opt` of `new MindMap(opt)`You can only update some data, such as:
```js
mindMap.updateConfig({
enableFreeDrag: true// 开启节点自由拖拽
})
```
This method only updates the configuration and has no other side effects, such as triggering canvas re-rendering
### getLayout()
Gets the current layout structure.
### setLayout(layout)
Sets the layout structure. Available values can be found in the `layout` field
in the options table above.
### execCommand(name, ...args)
Executes a command, which will add a record to the history stack for undo or
redo. All commands are as follows:
| Command name | Description | Parameters |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SELECT_ALL | Select all | |
| BACK | Go back a specified number of steps | step (the number of steps to go back, default is 1) |
| FORWARD | Go forward a specified number of steps | step (the number of steps to go forward, default is 1) |
| INSERT_NODE | Insert a sibling node, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective | |
| INSERT_CHILD_NODE | Insert a child node, the active node will be the operation node | |
| UP_NODE | Move node up, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the first node in the list will be invalid | |
| DOWN_NODE | Move node down, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the last node in the list will be invalid | |
| REMOVE_NODE | Remove node, the active node will be the operation node | |
| PASTE_NODE | Paste node to a node, the active node will be the operation node | data (the node data to paste, usually obtained through the renderer.copyNode() and renderer.cutNode() methods) |
| SET_NODE_STYLE | Modify node style | node (the node to set the style of), prop (style property), value (style property value), isActive (boolean, whether the style being set is for the active state) |
| SET_NODE_ACTIVE | Set whether the node is active | node (the node to set), active (boolean, whether to activate) |
| CLEAR_ACTIVE_NODE | Clear the active state of the currently active node(s), the active node will be the operation node | |
| SET_NODE_EXPAND | Set whether the node is expanded | node (the node to set), expand (boolean, whether to expand) |
| EXPAND_ALL | Expand all nodes | |
| UNEXPAND_ALL | Collapse all nodes | |
| UNEXPAND_TO_LEVEL (v0.2.8+) | Expand to a specified level | level (the level to expand to, 1, 2, 3...) |
| SET_NODE_DATA | Update node data, that is, update the data in the data object of the node data object | node (the node to set), data (object, the data to update, e.g. `{expand: true}`) |
| SET_NODE_TEXT | Set node text | node (the node to set), text (the new text for the node) |
| SET_NODE_IMAGE | Set Node Image | node (node to set), imgData (object, image information, structured as: `{url, title, width, height}`, the width and height of the image must be passed) |
| SET_NODE_ICON | Set Node Icon | node (node to set), icons (array, predefined image names array, available icons can be obtained in the nodeIconList list in the [https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js) file, icon name is type_name, such as ['priority_1']) |
| SET_NODE_HYPERLINK | Set Node Hyperlink | node (node to set), link (hyperlink address), title (hyperlink name, optional) |
| SET_NODE_NOTE | Set Node Note | node (node to set), note (note text) |
| SET_NODE_TAG | Set Node Tag | node (node to set), tag (string array, built-in color information can be obtained in [https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)) |
| INSERT_AFTER (v0.1.5+) | Move Node to After Another Node | node (node to move), exist (target node) |
| INSERT_BEFORE (v0.1.5+) | Move Node to Before Another Node | node (node to move), exist (target node) |
| MOVE_NODE_TO (v0.1.5+) | Move a node as a child of another node | node (the node to move), toNode (the target node) |
| ADD_GENERALIZATION (v0.2.0+) | Add a node summary | data (the data for the summary, in object format, all numerical fields of the node are supported, default is `{text: 'summary'}`) |
| REMOVE_GENERALIZATION (v0.2.0+) | Remove a node summary | |
| SET_NODE_CUSTOM_POSITION (v0.2.0+) | Set a custom position for a node | node (the node to set), left (custom x coordinate, default is undefined), top (custom y coordinate, default is undefined) |
| RESET_LAYOUT (v0.2.0+) | Arrange layout with one click | |
| SET_NODE_SHAPE (v0.2.4+) | Set the shape of a node | node (the node to set), shape (the shape, all shapes: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js) |
### setData(data)
Dynamic setting of mind map data, pure node data
`data`: mind map structure data
### setFullData(_data_)
> v0.2.7+
Dynamic setting of mind map data, including node data, layout, theme, view
`data`: complete data, structure can refer to
[exportFullData](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exportFullData.json)
### getData(withConfig)
> v0.2.9+
Gets mind map data
`withConfig`: `Boolean`, default is `false`, that is, the obtained data only
includes the node tree, if `true` is passed, it will also include theme, layout,
view, etc. data
### export(type, isDownload, fileName)
> You need to register the `Export` plugin first
Export
`type`: the type to be exported, optional values: png, svg, json, pdf (v0.2.1+),
smm (essentially also json)
`isDownload`: whether to directly trigger download, Boolean value, default is
`false`
`fileName`: (v0.1.6+) the name of the exported file, default is `思维导图` (mind
map).
### toPos(x, y)
> v0.1.5+
Convert the coordinates of the browser's visible window to coordinates relative
to the canvas.

View File

@@ -0,0 +1,656 @@
<template>
<div>
<h1>Constructor</h1>
<h2>Basic use</h2>
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;mindMapContainer&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;simple-mind-map&quot;</span>;
<span class="hljs-keyword">const</span> mindMap = <span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">el</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">&quot;mindMapContainer&quot;</span>),
<span class="hljs-attr">data</span>: {
<span class="hljs-string">&quot;data&quot;</span>: {
<span class="hljs-string">&quot;text&quot;</span>: <span class="hljs-string">&quot;Root Node&quot;</span>
},
<span class="hljs-string">&quot;children&quot;</span>: []
}
});
</code></pre>
<h2>Instantiation options</h2>
<table>
<thead>
<tr>
<th>Field Name</th>
<th>Type</th>
<th>Default Value</th>
<th>Description</th>
<th>Required</th>
</tr>
</thead>
<tbody>
<tr>
<td>el</td>
<td>Element</td>
<td></td>
<td>Container element, must be a DOM element</td>
<td>Yes</td>
</tr>
<tr>
<td>data</td>
<td>Object</td>
<td>{}</td>
<td>Mind map data, refer to: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js</td>
<td></td>
</tr>
<tr>
<td>layout</td>
<td>String</td>
<td>logicalStructure</td>
<td>Layout type, options: logicalStructure (logical structure diagram), mindMap (mind map), catalogOrganization (catalog organization diagram), organizationStructure (organization structure diagram)</td>
<td></td>
</tr>
<tr>
<td>theme</td>
<td>String</td>
<td>default</td>
<td>Theme, options: default, classic, minions, pinkGrape, mint, gold, vitalityOrange, greenLeaf, dark2, skyGreen, classic2, classic3, classic4 (v0.2.0+), classicGreen, classicBlue, blueSky, brainImpairedPink, dark, earthYellow, freshGreen, freshRed, romanticPurple</td>
<td></td>
</tr>
<tr>
<td>themeConfig</td>
<td>Object</td>
<td>{}</td>
<td>Theme configuration, will be merged with the selected theme, available fields refer to: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js</td>
<td></td>
</tr>
<tr>
<td>scaleRatio</td>
<td>Number</td>
<td>0.1</td>
<td>The incremental scaling ratio</td>
<td></td>
</tr>
<tr>
<td>maxTag</td>
<td>Number</td>
<td>5</td>
<td>The maximum number of tags displayed in the node, any additional tags will be discarded</td>
<td></td>
</tr>
<tr>
<td>exportPadding</td>
<td>Number</td>
<td>20</td>
<td>The padding for exporting images</td>
<td></td>
</tr>
<tr>
<td>imgTextMargin</td>
<td>Number</td>
<td>5</td>
<td>The spacing between the image and text in the node</td>
<td></td>
</tr>
<tr>
<td>textContentMargin</td>
<td>Number</td>
<td>2</td>
<td>The spacing between various text information in the node, such as the spacing between the icon and text</td>
<td></td>
</tr>
<tr>
<td>selectTranslateStep</td>
<td>Number</td>
<td>3</td>
<td>The canvas offset when mouse moves to the edge during multi-select node</td>
<td></td>
</tr>
<tr>
<td>selectTranslateLimit</td>
<td>Number</td>
<td>20</td>
<td>The distance from the edge when the canvas begins to offset during multi-select node</td>
<td></td>
</tr>
<tr>
<td>customNoteContentShowv0.1.6+</td>
<td>Object</td>
<td>null</td>
<td>Custom node note content display, object type, structure: {show: (noteContent, left, top) =&gt; {// your display node note logic }, hide: () =&gt; {// your hide node note logic }}</td>
<td></td>
</tr>
<tr>
<td>readonlyv0.1.7+</td>
<td>Boolean</td>
<td>false</td>
<td>Whether it is read-only mode</td>
<td></td>
</tr>
<tr>
<td>enableFreeDragv0.2.4+</td>
<td>Boolean</td>
<td>false</td>
<td>Enable node free drag</td>
<td></td>
</tr>
<tr>
<td>watermarkConfigv0.2.4+</td>
<td>Object</td>
<td></td>
<td>Watermark config, Please refer to the table Watermark config below for detailed configuration</td>
<td></td>
</tr>
<tr>
<td>textAutoWrapWidthv0.3.4+</td>
<td>Number</td>
<td>500</td>
<td>Each line of text in the node will wrap automatically when it reaches the width</td>
<td></td>
</tr>
</tbody>
</table>
<h3>Watermark config</h3>
<table>
<thead>
<tr>
<th>Field Name</th>
<th>Type</th>
<th>Default Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>text</td>
<td>String</td>
<td>''</td>
<td>Watermark text. If it is an empty string, the watermark will not be displayed</td>
</tr>
<tr>
<td>lineSpacing</td>
<td>Number</td>
<td>100</td>
<td>Spacing between watermark lines</td>
</tr>
<tr>
<td>textSpacing</td>
<td>Number</td>
<td>100</td>
<td>Spacing between watermarks in the same row</td>
</tr>
<tr>
<td>angle</td>
<td>Number</td>
<td>30</td>
<td>Tilt angle of watermark, range: [0, 90]</td>
</tr>
<tr>
<td>textStyle</td>
<td>Object</td>
<td>{color: '#999', opacity: 0.5, fontSize: 14}</td>
<td>Watermark text style</td>
</tr>
</tbody>
</table>
<h2>Static methods</h2>
<h3>defineTheme(name, config)</h3>
<blockquote>
<p>v0.2.23+</p>
</blockquote>
<p>Define new theme.</p>
<p><code>name</code>New theme name</p>
<p><code>config</code>New theme config</p>
<p><code>Simple-mind-map </code> Built-in many themes. In addition, you can register new theme. It is recommended to register before instantiation, so that you can directly use the newly registered theme during instantiation. Use example:</p>
<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-comment">// 注册新主题</span>
MindMap.defineTheme(<span class="hljs-string">&#x27;Theme name&#x27;</span>, {})
<span class="hljs-comment">// 1.实例化时使用新注册的主题</span>
<span class="hljs-keyword">const</span> mindMap = <span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">theme</span>: <span class="hljs-string">&#x27;Theme name&#x27;</span>
})
<span class="hljs-comment">// 2.动态切换新主题</span>
mindMap.setTheme(<span class="hljs-string">&#x27;Theme name&#x27;</span>)
</code></pre>
<p>For all configurations of theme, please refer to <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js">Default Topic</a>. The <code>defineTheme</code>method will merge the configuration you passed in with the default configuration. Most of the themes do not need custom many parts. For a typical customized theme configuration, please refer to <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/blueSky.js">blueSky</a>.</p>
<h3>usePlugin(plugin)</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p>If you need to use some non-core functions, such as mini map, watermark, etc, you can register plugin through this method. Can be called in chain.</p>
<p>Note: The plugin needs to be registered before instantiating <code>MindMap</code>.</p>
<h2>Static props</h2>
<h3>pluginList</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p>List of all currently registered plugins.</p>
<h2>Instance methods</h2>
<h3>getSvgData()</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<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>
svgHTML, <span class="hljs-comment">// String, svg string, i.e. html string, can be directly rendered to the small map container you prepared</span>
<span class="hljs-attr">rect</span>: <span class="hljs-comment">// Object, position, size, etc. of mind map graphics before zoom</span>
origWidth, <span class="hljs-comment">// Number, canvas width</span>
origHeight, <span class="hljs-comment">// Number, canvas height</span>
scaleX, <span class="hljs-comment">// Number, horizontal zoom value of mind map graphics</span>
scaleY, <span class="hljs-comment">// Number, vertical zoom value of mind map graphics</span>
}
</code></pre>
<h3>render(callback)</h3>
<ul>
<li><code>callback</code>: <code>v0.3.2+</code>, <code>Function</code>, Called when the re-rendering is complete</li>
</ul>
<p>Triggers a full rendering, which will reuse nodes for better performance. If
only the node positions have changed, this method can be called to <code>reRender</code>.</p>
<h3>reRender(callback)</h3>
<ul>
<li><code>callback</code>: <code>v0.3.2+</code>, <code>Function</code>, Called when the re-rendering is complete</li>
</ul>
<p>Performs a full re-render, clearing the canvas and creating new nodes. This has
poor performance and should be used sparingly.</p>
<h3>resize()</h3>
<p>After the container size has changed, this method should be called to adjust.</p>
<h3>setMode(mode)</h3>
<blockquote>
<p>v0.1.7+</p>
</blockquote>
<p>Switches between readonly and edit mode.</p>
<p><code>mode</code>readonlyedit</p>
<h3>on(event, fn)</h3>
<p>Listen to an event. Event list:</p>
<table>
<thead>
<tr>
<th>Event Name</th>
<th>Description</th>
<th>Callback Parameters</th>
</tr>
</thead>
<tbody>
<tr>
<td>data_change</td>
<td>Tree rendering data change, listen to this method to get the latest data</td>
<td>data (current tree rendering data)</td>
</tr>
<tr>
<td>view_data_changev0.1.1+</td>
<td>View change data, such as when dragging or zooming</td>
<td>data (current view state data)</td>
</tr>
<tr>
<td>back_forward</td>
<td>Forward or backward</td>
<td>activeHistoryIndex (current index in the history data array), length (current length of the history data array)</td>
</tr>
<tr>
<td>draw_click</td>
<td>Canvas click event</td>
<td>e (event object)</td>
</tr>
<tr>
<td>svg_mousedown</td>
<td>svg canvas mouse down event</td>
<td>e (event object)</td>
</tr>
<tr>
<td>mousedown</td>
<td>el element mouse down event</td>
<td>e (event object), this (Event event class instance)</td>
</tr>
<tr>
<td>mousemove</td>
<td>el element mouse move event</td>
<td>e (event object), this (Event event class instance)</td>
</tr>
<tr>
<td>drag</td>
<td>If it is a drag event while holding down the left button</td>
<td>e (event object), this (Event event class instance)</td>
</tr>
<tr>
<td>mouseup</td>
<td>el element mouse up event</td>
<td>e (event object), this (Event event class instance)</td>
</tr>
<tr>
<td>mousewheel</td>
<td>Mouse scroll event</td>
<td>e (event object), dir (up or down scroll), this (Event event class instance)</td>
</tr>
<tr>
<td>contextmenu</td>
<td>svg canvas right mouse button menu event</td>
<td>e (event object)</td>
</tr>
<tr>
<td>node_click</td>
<td>Node click event</td>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_mousedown</td>
<td>Node mouse down event</td>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_mouseup</td>
<td>node mouseup event</td>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_mouseup</td>
<td>Node mouseup event</td>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_dblclick</td>
<td>Node double-click event</td>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_contextmenu</td>
<td>Node right-click menu event</td>
<td>e (event object), this (node instance)</td>
</tr>
<tr>
<td>before_node_active</td>
<td>Event before node activation</td>
<td>this (node instance), activeNodeList (current list of active nodes)</td>
</tr>
<tr>
<td>node_active</td>
<td>Node activation event</td>
<td>this (node instance), activeNodeList (current list of active nodes)</td>
</tr>
<tr>
<td>expand_btn_click</td>
<td>Node expand or collapse event</td>
<td>this (node instance)</td>
</tr>
<tr>
<td>before_show_text_edit</td>
<td>Event before node text edit box opens</td>
<td></td>
</tr>
<tr>
<td>hide_text_edit</td>
<td>Node text edit box close event</td>
<td>textEditNode (text edit box DOM node), activeNodeList (current list of active nodes)</td>
</tr>
<tr>
<td>scale</td>
<td>Zoom event</td>
<td>scale (zoom ratio)</td>
</tr>
<tr>
<td>node_img_dblclickv0.2.15+</td>
<td>Node image double-click event</td>
<td>this (node instance), e (event object)</td>
</tr>
<tr>
<td>node_tree_render_endv0.2.16+</td>
<td>Node tree render end event</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>
<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>
<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>
<p>Gets the custom theme configuration.</p>
<h3>getThemeConfig(prop)</h3>
<p>Gets the value of a specific theme configuration property.</p>
<h3>getConfig(<em>prop</em>)</h3>
<blockquote>
<p>0.2.24+</p>
</blockquote>
<p><code>prop</code>Get the value of the specified configuration, and return the entire configuration if not passed</p>
<p>Get config, That is, <code>opt</code> of <code>new MindMap (opt)</code></p>
<h3>updateConfig(<em>opt</em> = {})</h3>
<blockquote>
<p>0.2.24+</p>
</blockquote>
<p><code>opt</code>Configuration to update</p>
<p>Update configThat is update <code>opt</code> of <code>new MindMap(opt)</code>You can only update some data, such as:</p>
<pre class="hljs"><code>mindMap.updateConfig({
<span class="hljs-attr">enableFreeDrag</span>: <span class="hljs-literal">true</span><span class="hljs-comment">// 开启节点自由拖拽</span>
})
</code></pre>
<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>
<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>
<p>Executes a command, which will add a record to the history stack for undo or
redo. All commands are as follows:</p>
<table>
<thead>
<tr>
<th>Command name</th>
<th>Description</th>
<th>Parameters</th>
</tr>
</thead>
<tbody>
<tr>
<td>SELECT_ALL</td>
<td>Select all</td>
<td></td>
</tr>
<tr>
<td>BACK</td>
<td>Go back a specified number of steps</td>
<td>step (the number of steps to go back, default is 1)</td>
</tr>
<tr>
<td>FORWARD</td>
<td>Go forward a specified number of steps</td>
<td>step (the number of steps to go forward, default is 1)</td>
</tr>
<tr>
<td>INSERT_NODE</td>
<td>Insert a sibling node, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective</td>
<td></td>
</tr>
<tr>
<td>INSERT_CHILD_NODE</td>
<td>Insert a child node, the active node will be the operation node</td>
<td></td>
</tr>
<tr>
<td>UP_NODE</td>
<td>Move node up, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the first node in the list will be invalid</td>
<td></td>
</tr>
<tr>
<td>DOWN_NODE</td>
<td>Move node down, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the last node in the list will be invalid</td>
<td></td>
</tr>
<tr>
<td>REMOVE_NODE</td>
<td>Remove node, the active node will be the operation node</td>
<td></td>
</tr>
<tr>
<td>PASTE_NODE</td>
<td>Paste node to a node, the active node will be the operation node</td>
<td>data (the node data to paste, usually obtained through the renderer.copyNode() and renderer.cutNode() methods)</td>
</tr>
<tr>
<td>SET_NODE_STYLE</td>
<td>Modify node style</td>
<td>node (the node to set the style of), prop (style property), value (style property value), isActive (boolean, whether the style being set is for the active state)</td>
</tr>
<tr>
<td>SET_NODE_ACTIVE</td>
<td>Set whether the node is active</td>
<td>node (the node to set), active (boolean, whether to activate)</td>
</tr>
<tr>
<td>CLEAR_ACTIVE_NODE</td>
<td>Clear the active state of the currently active node(s), the active node will be the operation node</td>
<td></td>
</tr>
<tr>
<td>SET_NODE_EXPAND</td>
<td>Set whether the node is expanded</td>
<td>node (the node to set), expand (boolean, whether to expand)</td>
</tr>
<tr>
<td>EXPAND_ALL</td>
<td>Expand all nodes</td>
<td></td>
</tr>
<tr>
<td>UNEXPAND_ALL</td>
<td>Collapse all nodes</td>
<td></td>
</tr>
<tr>
<td>UNEXPAND_TO_LEVEL (v0.2.8+)</td>
<td>Expand to a specified level</td>
<td>level (the level to expand to, 1, 2, 3...)</td>
</tr>
<tr>
<td>SET_NODE_DATA</td>
<td>Update node data, that is, update the data in the data object of the node data object</td>
<td>node (the node to set), data (object, the data to update, e.g. <code>{expand: true}</code>)</td>
</tr>
<tr>
<td>SET_NODE_TEXT</td>
<td>Set node text</td>
<td>node (the node to set), text (the new text for the node)</td>
</tr>
<tr>
<td>SET_NODE_IMAGE</td>
<td>Set Node Image</td>
<td>node (node to set), imgData (object, image information, structured as: <code>{url, title, width, height}</code>, the width and height of the image must be passed)</td>
</tr>
<tr>
<td>SET_NODE_ICON</td>
<td>Set Node Icon</td>
<td>node (node to set), icons (array, predefined image names array, available icons can be obtained in the nodeIconList list in the <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js</a> file, icon name is type_name, such as ['priority_1'])</td>
</tr>
<tr>
<td>SET_NODE_HYPERLINK</td>
<td>Set Node Hyperlink</td>
<td>node (node to set), link (hyperlink address), title (hyperlink name, optional)</td>
</tr>
<tr>
<td>SET_NODE_NOTE</td>
<td>Set Node Note</td>
<td>node (node to set), note (note text)</td>
</tr>
<tr>
<td>SET_NODE_TAG</td>
<td>Set Node Tag</td>
<td>node (node to set), tag (string array, built-in color information can be obtained in <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js</a>)</td>
</tr>
<tr>
<td>INSERT_AFTER (v0.1.5+)</td>
<td>Move Node to After Another Node</td>
<td>node (node to move), exist (target node)</td>
</tr>
<tr>
<td>INSERT_BEFORE (v0.1.5+)</td>
<td>Move Node to Before Another Node</td>
<td>node (node to move), exist (target node)</td>
</tr>
<tr>
<td>MOVE_NODE_TO (v0.1.5+)</td>
<td>Move a node as a child of another node</td>
<td>node (the node to move), toNode (the target node)</td>
</tr>
<tr>
<td>ADD_GENERALIZATION (v0.2.0+)</td>
<td>Add a node summary</td>
<td>data (the data for the summary, in object format, all numerical fields of the node are supported, default is <code>{text: 'summary'}</code>)</td>
</tr>
<tr>
<td>REMOVE_GENERALIZATION (v0.2.0+)</td>
<td>Remove a node summary</td>
<td></td>
</tr>
<tr>
<td>SET_NODE_CUSTOM_POSITION (v0.2.0+)</td>
<td>Set a custom position for a node</td>
<td>node (the node to set), left (custom x coordinate, default is undefined), top (custom y coordinate, default is undefined)</td>
</tr>
<tr>
<td>RESET_LAYOUT (v0.2.0+)</td>
<td>Arrange layout with one click</td>
<td></td>
</tr>
<tr>
<td>SET_NODE_SHAPE (v0.2.4+)</td>
<td>Set the shape of a node</td>
<td>node (the node to set), shape (the shape, all shapes: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js)</td>
</tr>
</tbody>
</table>
<h3>setData(data)</h3>
<p>Dynamic setting of mind map data, pure node data</p>
<p><code>data</code>: mind map structure data</p>
<h3>setFullData(<em>data</em>)</h3>
<blockquote>
<p>v0.2.7+</p>
</blockquote>
<p>Dynamic setting of mind map data, including node data, layout, theme, view</p>
<p><code>data</code>: complete data, structure can refer to
<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exportFullData.json">exportFullData</a></p>
<h3>getData(withConfig)</h3>
<blockquote>
<p>v0.2.9+</p>
</blockquote>
<p>Gets mind map data</p>
<p><code>withConfig</code>: <code>Boolean</code>, default is <code>false</code>, that is, the obtained data only
includes the node tree, if <code>true</code> is passed, it will also include theme, layout,
view, etc. data</p>
<h3>export(type, isDownload, fileName)</h3>
<blockquote>
<p>You need to register the <code>Export</code> plugin first</p>
</blockquote>
<p>Export</p>
<p><code>type</code>: the type to be exported, optional values: png, svg, json, pdf (v0.2.1+),
smm (essentially also json)</p>
<p><code>isDownload</code>: whether to directly trigger download, Boolean value, default is
<code>false</code></p>
<p><code>fileName</code>: (v0.1.6+) the name of the exported file, default is <code>思维导图</code> (mind
map).</p>
<h3>toPos(x, y)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Convert the coordinates of the browser's visible window to coordinates relative
to the canvas.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,53 @@
# Export plugin
The `Export` plugin provides the export function.
## Register
```js
import MindMap from 'simple-mind-map'
import Export from 'simple-mind-map/src/Export.js'
MindMap.usePlugin(Export)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.doExport`.
## Methods
### png()
Exports as `png`, an async method that returns image data, `data:url` data which
can be downloaded or displayed.
### svg()
Exports as `svg`, an async method that returns `svg` data, `data:url` data which
can be downloaded or displayed.
### getSvgData()
Gets `svg` data, an async method that returns an object:
```js
{
node; // svg object
str; // svg string
}
```
### pdf(name)
> v0.2.1+
`name`File name
Export as `pdf`
### json(name, withConfig)
`name`It is temporarily useless, just pass an empty string
`withConfig``Boolean`, default `true`, Whether the data contains configuration, otherwise it is pure mind map node data
Return `json` data, `data:url` type, you can download it yourself

View File

@@ -0,0 +1,48 @@
<template>
<div>
<h1>Export plugin</h1>
<p>The <code>Export</code> plugin provides the export function.</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> Export <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/Export.js&#x27;</span>
MindMap.usePlugin(Export)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.doExport</code>.</p>
<h2>Methods</h2>
<h3>png()</h3>
<p>Exports as <code>png</code>, an async method that returns image data, <code>data:url</code> data which
can be downloaded or displayed.</p>
<h3>svg()</h3>
<p>Exports as <code>svg</code>, an async method that returns <code>svg</code> data, <code>data:url</code> data which
can be downloaded or displayed.</p>
<h3>getSvgData()</h3>
<p>Gets <code>svg</code> data, an async method that returns an object:</p>
<pre class="hljs"><code>{
node; <span class="hljs-comment">// svg object</span>
str; <span class="hljs-comment">// svg string</span>
}
</code></pre>
<h3>pdf(name)</h3>
<blockquote>
<p>v0.2.1+</p>
</blockquote>
<p><code>name</code>File name</p>
<p>Export as <code>pdf</code></p>
<h3>json(name, withConfig)</h3>
<p><code>name</code>It is temporarily useless, just pass an empty string</p>
<p><code>withConfig``Boolean</code>, default <code>true</code>, Whether the data contains configuration, otherwise it is pure mind map node data</p>
<p>Return <code>json</code> data, <code>data:url</code> type, you can download it yourself</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,21 @@
# Drag plugin
The `Drag` plugin provides the function of node dragging, including:
1.Drag the node to move and change its position in the node tree, That is, as a child node, sibling node, etc. of other nodes
2.Drag the node to the custom canvas location
Please refer to the [Instantiation Options](/mind-map/#/doc/zh/constructor) of the `MindMap` class for configuration.
## Register
```js
import MindMap from 'simple-mind-map'
import Drag from 'simple-mind-map/src/Drag.js'
MindMap.usePlugin(Drag)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.drag`.

View File

@@ -0,0 +1,27 @@
<template>
<div>
<h1>Drag plugin</h1>
<p>The <code>Drag</code> plugin provides the function of node dragging, including:</p>
<p>1.Drag the node to move and change its position in the node tree, That is, as a child node, sibling node, etc. of other nodes</p>
<p>2.Drag the node to the custom canvas location</p>
<p>Please refer to the <a href="/mind-map/#/doc/zh/constructor">Instantiation Options</a> of the <code>MindMap</code> class for configuration.</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> Drag <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/Drag.js&#x27;</span>
MindMap.usePlugin(Drag)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.drag</code>.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,81 @@
# Introduction
`simple-mind-map` is a simple and powerful web mind map library, not dependent on any specific framework.
## Features
- [x] Plugin architecture. In addition to core functions, other functions are provided as plugins, which can be used as needed to reduce the overall volume
- [x] Supports four types of structures: logical structure diagrams, mind maps,
organizational structure diagrams, and directory organization diagrams
- [x] Built-in multiple themes and allows for highly customized styles, and support register new themes
- [x] Supports shortcuts
- [x] Node content supports images, icons, hyperlinks, notes, tags, and
summaries
- [x] Supports forward and backward navigation
- [x] Supports dragging and scaling
- [x] Supports right-click and Ctrl + left-click to select multiple items
- [x] Supports free dragging and dragging to adjust nodes
- [x] Supports various node shapes
- [x] Supports export to json, png, svg, pdf, and import from json, xmind
- [x] Supports mini map、support watermark
## Table of Contents
1.`simple-mind-map`
This is a mind map tool library that is framework-agnostic and can be used with
frameworks such as Vue and React, or without a framework.
2.`web`
This is an online mind map built using the `simple-mind-map` library and based
on `Vue2.x` and `ElementUI`. Features include:
- [x] Toolbar, which supports inserting and deleting nodes, and editing node
images, icons, hyperlinks, notes, tags, and summaries
- [x] Sidebar, with panels for basic style settings, node style settings,
outline, theme selection, and structure selection
- [x] Import and export functionality; data is saved in the browser's local
storage by default, but it also supports creating, opening, and editing
local files on the computer directly
- [x] Right-click menu, which supports operations such as expanding, collapsing,
and organizing layout
- [x] Bottom bar, which supports node and word count statistics, switching
between edit and read-only modes, zooming in and out, and switching to
full screen, support mini map
Provide document page service.
3.`dist`
The folder containing the packaged resources for the `web` folder.
4.`docs`
Documentation, etc.
## Related Articles
[Technical Analysis of Web Mind Map Implementation (chi)](https://juejin.cn/post/6987711560521089061)
[Only a hundred lines of code are needed to add local file operation capability to your Web page. Are you sure not to try?](https://juejin.cn/post/7157681502506090510)
[When you press the direction key, how does the TV find the next focus](https://juejin.cn/post/7199666255883927612)
## Special Note
This project is rough and has not been thoroughly tested, its features are not
yet fully developed, and there are some performance issues, especially when the number of nodes is large. It is only for
learning and reference purposes and please use it carefully for actual projects.
The built-in themes and icons in the project come from:
[Baidu Mind Map](https://naotu.baidu.com/)
[Zhixi Mind Map](https://www.zhixi.com/)
Respect the copyright, and do not use the theme and icons directly for commercial projects.
## License
[MIT](https://opensource.org/licenses/MIT)

View File

@@ -0,0 +1,74 @@
<template>
<div>
<h1>Introduction</h1>
<p><code>simple-mind-map</code> is a simple and powerful web mind map library, not dependent on any specific framework.</p>
<h2>Features</h2>
<ul>
<li><input type="checkbox" id="checkbox51" checked="true"><label for="checkbox51">Plugin architecture. In addition to core functions, other functions are provided as plugins, which can be used as needed to reduce the overall volume</label></li>
<li><input type="checkbox" id="checkbox52" checked="true"><label for="checkbox52">Supports four types of structures: logical structure diagrams, mind maps,</label>
organizational structure diagrams, and directory organization diagrams</li>
<li><input type="checkbox" id="checkbox53" checked="true"><label for="checkbox53">Built-in multiple themes and allows for highly customized styles, and support register new themes</label></li>
<li><input type="checkbox" id="checkbox54" checked="true"><label for="checkbox54">Supports shortcuts</label></li>
<li><input type="checkbox" id="checkbox55" checked="true"><label for="checkbox55">Node content supports images, icons, hyperlinks, notes, tags, and</label>
summaries</li>
<li><input type="checkbox" id="checkbox56" checked="true"><label for="checkbox56">Supports forward and backward navigation</label></li>
<li><input type="checkbox" id="checkbox57" checked="true"><label for="checkbox57">Supports dragging and scaling</label></li>
<li><input type="checkbox" id="checkbox58" checked="true"><label for="checkbox58">Supports right-click and Ctrl + left-click to select multiple items</label></li>
<li><input type="checkbox" id="checkbox59" checked="true"><label for="checkbox59">Supports free dragging and dragging to adjust nodes</label></li>
<li><input type="checkbox" id="checkbox60" checked="true"><label for="checkbox60">Supports various node shapes</label></li>
<li><input type="checkbox" id="checkbox61" checked="true"><label for="checkbox61">Supports export to json, png, svg, pdf, and import from json, xmind</label></li>
<li><input type="checkbox" id="checkbox62" checked="true"><label for="checkbox62">Supports mini mapsupport watermark</label></li>
</ul>
<h2>Table of Contents</h2>
<p>1.<code>simple-mind-map</code></p>
<p>This is a mind map tool library that is framework-agnostic and can be used with
frameworks such as Vue and React, or without a framework.</p>
<p>2.<code>web</code></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="checkbox63" checked="true"><label for="checkbox63">Toolbar, which supports inserting and deleting nodes, and editing node</label>
images, icons, hyperlinks, notes, tags, and summaries</li>
<li><input type="checkbox" id="checkbox64" checked="true"><label for="checkbox64">Sidebar, with panels for basic style settings, node style settings,</label>
outline, theme selection, and structure selection</li>
<li><input type="checkbox" id="checkbox65" checked="true"><label for="checkbox65">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="checkbox66" checked="true"><label for="checkbox66">Right-click menu, which supports operations such as expanding, collapsing,</label>
and organizing layout</li>
<li><input type="checkbox" id="checkbox67" checked="true"><label for="checkbox67">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>
<p>Provide document page service.</p>
<p>3.<code>dist</code></p>
<p>The folder containing the packaged resources for the <code>web</code> folder.</p>
<p>4.<code>docs</code></p>
<p>Documentation, etc.</p>
<h2>Related Articles</h2>
<p><a href="https://juejin.cn/post/6987711560521089061">Technical Analysis of Web Mind Map Implementation (chi)</a></p>
<p><a href="https://juejin.cn/post/7157681502506090510">Only a hundred lines of code are needed to add local file operation capability to your Web page. Are you sure not to try?</a></p>
<p><a href="https://juejin.cn/post/7199666255883927612">When you press the direction key, how does the TV find the next focus</a></p>
<h2>Special Note</h2>
<p>This project is rough and has not been thoroughly tested, its features are not
yet fully developed, and there are some performance issues, especially when the number of nodes is large. It is only for
learning and reference purposes and please use it carefully for actual projects.</p>
<p>The built-in themes and icons in the project come from:</p>
<p><a href="https://naotu.baidu.com/">Baidu Mind Map</a></p>
<p><a href="https://www.zhixi.com/">Zhixi Mind Map</a></p>
<p>Respect the copyright, and do not use the theme and icons directly for commercial projects.</p>
<h2>License</h2>
<p><a href="https://opensource.org/licenses/MIT">MIT</a></p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,61 @@
# KeyCommand instance
The `keyCommand` instance is responsible for adding and triggering shortcuts. It
includes some built-in shortcuts and can also be added manually. The
`mindMap.keyCommand` instance can be obtained through this.
## Methods
### addShortcut(key, fn)
Add a shortcut
`key`: Shortcut key, key values can be viewed at
[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/keyMap.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/keyMap.js)
Example:
```js
// Single key
mindMap.keyCommand.addShortcut("Enter", () => {});
// Or
mindMap.keyCommand.addShortcut("Del|Backspace", () => {});
// Combination key
mindMap.keyCommand.addShortcut("Control+Enter", () => {});
```
`fn`: Method to be executed
### removeShortcut(key, fn)
Remove a shortcut command, if `fn` is not specified, all callback methods for
the shortcut will be removed
### getShortcutFn(key)
> v0.2.2+
Get the processing function for the specified shortcut
### pause()
> v0.2.2+
Pause all shortcut responses
### recovery()
> v0.2.2+
Restore shortcut responses
### save()
> v0.2.3+
Save the current registered shortcut data, then clear the shortcut data
### restore()
> v0.2.3+
Restore saved shortcut data, then clear the cache data

View File

@@ -0,0 +1,61 @@
<template>
<div>
<h1>KeyCommand instance</h1>
<p>The <code>keyCommand</code> instance is responsible for adding and triggering shortcuts. It
includes some built-in shortcuts and can also be added manually. The
<code>mindMap.keyCommand</code> instance can be obtained through this.</p>
<h2>Methods</h2>
<h3>addShortcut(key, fn)</h3>
<p>Add a shortcut</p>
<p><code>key</code>: Shortcut key, key values can be viewed at
<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/keyMap.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/keyMap.js</a>
Example:</p>
<pre class="hljs"><code><span class="hljs-comment">// Single key</span>
mindMap.keyCommand.addShortcut(<span class="hljs-string">&quot;Enter&quot;</span>, <span class="hljs-function">() =&gt;</span> {});
<span class="hljs-comment">// Or</span>
mindMap.keyCommand.addShortcut(<span class="hljs-string">&quot;Del|Backspace&quot;</span>, <span class="hljs-function">() =&gt;</span> {});
<span class="hljs-comment">// Combination key</span>
mindMap.keyCommand.addShortcut(<span class="hljs-string">&quot;Control+Enter&quot;</span>, <span class="hljs-function">() =&gt;</span> {});
</code></pre>
<p><code>fn</code>: Method to be executed</p>
<h3>removeShortcut(key, fn)</h3>
<p>Remove a shortcut command, if <code>fn</code> is not specified, all callback methods for
the shortcut will be removed</p>
<h3>getShortcutFn(key)</h3>
<blockquote>
<p>v0.2.2+</p>
</blockquote>
<p>Get the processing function for the specified shortcut</p>
<h3>pause()</h3>
<blockquote>
<p>v0.2.2+</p>
</blockquote>
<p>Pause all shortcut responses</p>
<h3>recovery()</h3>
<blockquote>
<p>v0.2.2+</p>
</blockquote>
<p>Restore shortcut responses</p>
<h3>save()</h3>
<blockquote>
<p>v0.2.3+</p>
</blockquote>
<p>Save the current registered shortcut data, then clear the shortcut data</p>
<h3>restore()</h3>
<blockquote>
<p>v0.2.3+</p>
</blockquote>
<p>Restore saved shortcut data, then clear the cache data</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,51 @@
# KeyboardNavigation plugin
> v0.2.17+
`KeyboardNavigation` plugin provides keyboard navigation function, that is, when you press the direction key, it will automatically find the next node and activate it.
## Register
```js
import MindMap from 'simple-mind-map'
import KeyboardNavigation from 'simple-mind-map/src/KeyboardNavigation.js'
MindMap.usePlugin(KeyboardNavigation)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.keyboardNavigation`.
## Methods
### focus(dir)
`dir`Which direction to find the next nodeOptional value`Left``Up``Right``Down`
Focus on the next node
### getNodeRect(node)
`node`Node
Get the location information of the node and return an object:
```js
{
left,
top,
right,
bottom
}
```
### getDistance(node1Rect, node2Rect)
`node1Rect``node2Rect`The location data of nodes can be obtained through the `getNodeRect(node)`
Get the distance between two nodes
### getCenter(nodeRect)
`nodeRect`The location data of nodes can be obtained through the `getNodeRect(node)`
Get the center point of the node

View File

@@ -0,0 +1,47 @@
<template>
<div>
<h1>KeyboardNavigation plugin</h1>
<blockquote>
<p>v0.2.17+</p>
</blockquote>
<p><code>KeyboardNavigation</code> plugin provides keyboard navigation function, that is, when you press the direction key, it will automatically find the next node and activate it.</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> KeyboardNavigation <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/KeyboardNavigation.js&#x27;</span>
MindMap.usePlugin(KeyboardNavigation)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.keyboardNavigation</code>.</p>
<h2>Methods</h2>
<h3>focus(dir)</h3>
<p><code>dir</code>Which direction to find the next nodeOptional value<code>Left</code> <code>Up</code> <code>Right</code> <code>Down</code></p>
<p>Focus on the next node</p>
<h3>getNodeRect(node)</h3>
<p><code>node</code>Node</p>
<p>Get the location information of the node and return an object:</p>
<pre class="hljs"><code>{
left,
top,
right,
bottom
}
</code></pre>
<h3>getDistance(node1Rect, node2Rect)</h3>
<p><code>node1Rect</code><code>node2Rect</code>The location data of nodes can be obtained through the <code>getNodeRect(node)</code></p>
<p>Get the distance between two nodes</p>
<h3>getCenter(nodeRect)</h3>
<p><code>nodeRect</code>The location data of nodes can be obtained through the <code>getNodeRect(node)</code></p>
<p>Get the center point of the node</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,95 @@
# MiniMap plugin
> v0.2.11+
Used to help quickly develop a small map feature, the small map consists of two
parts, one is the current canvas content, and the other is the viewport frame.
When zoomed, moved, or there are too many elements, the canvas may only display
part of the mind map content. The viewport frame can be used to view the current
viewport location, and can be quickly positioned by dragging on the small map.
## Register
```js
import MindMap from 'simple-mind-map'
import MiniMap from 'simple-mind-map/src/MiniMap.js'
MindMap.usePlugin(MiniMap)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.miniMap`.
## Methods
### calculationMiniMap(boxWidth, boxHeight)
"Calculate the rendering data for the small map, this function will call the
`getMiniMap()` method, so using this function is sufficient.
`boxWidth`: the width of the small map container
`boxHeight`: the height of the small map container
Function return content:
```js
{
svgHTML, // small map html
viewBoxStyle, // view box position information
miniMapBoxScale, // view box zoom value
miniMapBoxLeft, // view box left value
miniMapBoxTop, // view box top value
}
```
Small map idea:
1.Prepare a container element `container`, position is not `static`
2.In `container`, create a small map container element `miniMapContainer`,
absolute positioning
3.In `container`, create a view box element `viewBoxContainer`, absolute
positioning, set border style, transition property (optional)
4.Listen for `data_change` and `view_data_change` events, and in this event call
the `calculationMiniMap` method to get calculation data, then render `svgHTML`
to the `miniMapContainer` element and set its style:
```js
:style="{
transform: `scale(${svgBoxScale})`,
left: svgBoxLeft + 'px',
top: svgBoxTop + 'px',
}"
```
5.Set the `viewBoxStyle` object as the style of the `viewBoxContainer` element
At this point, when the mind map on the canvas changes, the small map will also
be updated in real time, and the view box element will reflect the position of
the viewport on the mind map graph in real time
6.Listen for the `mousedown`, `mousemove`, and `mouseup` events of the
`container` element, and call the three methods that will be introduced below to
achieve the effect of the mind map on the canvas being dragged with the mouse
### onMousedown(e)
Small map mouse down event executes this function
`e`: event object
### onMousemove(e, sensitivityNum = 5)
This function is executed on the small map mouse move event.
`e`: event object
`sensitivityNum`: drag sensitivity, the higher the sensitivity, the greater the
actual canvas dragging distance on the small map when dragging the same distance
on the small map
### onMouseup()
This function is executed on the small map mouse release event.

View File

@@ -0,0 +1,79 @@
<template>
<div>
<h1>MiniMap plugin</h1>
<blockquote>
<p>v0.2.11+</p>
</blockquote>
<p>Used to help quickly develop a small map feature, the small map consists of two
parts, one is the current canvas content, and the other is the viewport frame.
When zoomed, moved, or there are too many elements, the canvas may only display
part of the mind map content. The viewport frame can be used to view the current
viewport location, and can be quickly positioned by dragging on the small map.</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> MiniMap <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/MiniMap.js&#x27;</span>
MindMap.usePlugin(MiniMap)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.miniMap</code>.</p>
<h2>Methods</h2>
<h3>calculationMiniMap(boxWidth, boxHeight)</h3>
<p>&quot;Calculate the rendering data for the small map, this function will call the
<code>getMiniMap()</code> method, so using this function is sufficient.</p>
<p><code>boxWidth</code>: the width of the small map container</p>
<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>
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>
miniMapBoxTop, <span class="hljs-comment">// view box top value</span>
}
</code></pre>
<p>Small map idea:</p>
<p>1.Prepare a container element <code>container</code>, position is not <code>static</code></p>
<p>2.In <code>container</code>, create a small map container element <code>miniMapContainer</code>,
absolute positioning</p>
<p>3.In <code>container</code>, create a view box element <code>viewBoxContainer</code>, absolute
positioning, set border style, transition property (optional)</p>
<p>4.Listen for <code>data_change</code> and <code>view_data_change</code> events, and in this event call
the <code>calculationMiniMap</code> method to get calculation data, then render <code>svgHTML</code>
to the <code>miniMapContainer</code> element and set its style:</p>
<pre class="hljs"><code>:style=<span class="hljs-string">&quot;{
transform: `scale(${svgBoxScale})`,
left: svgBoxLeft + &#x27;px&#x27;,
top: svgBoxTop + &#x27;px&#x27;,
}&quot;</span>
</code></pre>
<p>5.Set the <code>viewBoxStyle</code> object as the style of the <code>viewBoxContainer</code> element</p>
<p>At this point, when the mind map on the canvas changes, the small map will also
be updated in real time, and the view box element will reflect the position of
the viewport on the mind map graph in real time</p>
<p>6.Listen for the <code>mousedown</code>, <code>mousemove</code>, and <code>mouseup</code> events of the
<code>container</code> element, and call the three methods that will be introduced below to
achieve the effect of the mind map on the canvas being dragged with the mouse</p>
<h3>onMousedown(e)</h3>
<p>Small map mouse down event executes this function</p>
<p><code>e</code>: event object</p>
<h3>onMousemove(e, sensitivityNum = 5)</h3>
<p>This function is executed on the small map mouse move event.</p>
<p><code>e</code>: event object</p>
<p><code>sensitivityNum</code>: drag sensitivity, the higher the sensitivity, the greater the
actual canvas dragging distance on the small map when dragging the same distance
on the small map</p>
<h3>onMouseup()</h3>
<p>This function is executed on the small map mouse release event.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,236 @@
# Node instance
Each node will instantiate a `node` instance
## Property
### nodeData
The real data corresponding to this node
### uid
The unique identifier of this node
### isRoot
Whether it is the root node
### layerIndex
Node level
### width
Width of the node
### height
Height of the node
### left
`left` position of the node
### top
`top` position of the node
### parent
Parent node of the node
### children
List of child nodes of the node
### group
Node is the content container, `svg` object
### isDrag
> v0.1.5+
Whether the node is currently being dragged
## Methods
### addChildren(node)
Add a child node
### getSize()
Calculate the width and height of the node, return a boolean indicating whether
the width and height have changed
### renderNode()
Render the node to the canvas, will remove the old content node and create a new
one
### render()
Recursively render this node and all its child nodes. The first call will create
the node content, subsequent calls will only update the node position. To
re-render the content, set the `initRender` attribute to `true` first.
### remove()
Recursively delete this node and all its child nodes
### renderLine()
Re-render the line from this node to its child nodes
### removeLine()
Remove the line from this node to its child nodes
### renderExpandBtn()
Render the content of the expand/collapse button
### removeExpandBtn()
Remove the expand/collapse button
### getStyle(prop, root, isActive)
Get the final style value applied to this node
`prop`: the style property to get
`root`: whether it is the root node, default `false`
`isActive`: whether the value being fetched is the active state style value,
default `false`
### setStyle(prop, value, isActive)
Modify a style of the node, a shortcut method for the `SET_NODE_STYLE` command
### getData(key)
Get the specified value in the `data` object of the node's real data `nodeData`,
if `key` is not passed, return the `data` object
### setData(data)
Set the value of the specified key in the data object of the node's real data
nodeData, `SET_NODE_DATA` command's shortcut method
### setText(text)
Setting the node text, a shortcut for the `SET_NODE_TEXT` command
### setImage(imgData)
Setting the node image, a shortcut for the `SET_NODE_IMAGE` command
### setIcon(icons)
Setting the node icon, a shortcut for the `SET_NODE_ICON` command
### setHyperlink(link, title)
Setting the node hyperlink, a shortcut for the `SET_NODE_HYPERLINK` command
### setNote(note)
Setting the node note, a shortcut for the `SET_NODE_NOTE` command
### setTag(tag)
Setting the node tag, a shortcut for the `SET_NODE_TAG` command"
### hide()
> v0.1.5+
Hide node and its sub-nodes
### show()
> v0.1.5+
Show node and its sub-nodes
### isParent(node)
> v0.1.5+
Check if the current node is an ancestor of a certain node
### isBrother(node)
> v0.1.5+
Check if the current node is a sibling of a certain node
### checkHasGeneralization()
> v0.2.0+
Check if there is a summary
### hideGeneralization()
> v0.2.0+
Hide summary node
### showGeneralization()
> v0.2.0+
Show summary node
### updateGeneralization()
> v0.2.0+
Update summary node
### hasCustomPosition()
> v0.2.0+
Check if the node has custom data
### ancestorHasCustomPosition()
> v0.2.0+
Check if there is an ancestor node with custom position
### getShape()
> v0.2.4+
Get node shape
### setShape(shape)
> v0.2.4+
Set node shape, a shortcut method for the `SET_NODE_SHAPE` command
### getSelfStyle(prop)
> v0.2.5+
Get the node's own custom style
### getParentSelfStyle(prop)
> v0.2.5+
Get the custom style of the nearest ancestor node with its own custom style
### getSelfInhertStyle(prop)
> v0.2.5+
Get the node's own inheritable custom style

View File

@@ -0,0 +1,169 @@
<template>
<div>
<h1>Node instance</h1>
<p>Each node will instantiate a <code>node</code> instance</p>
<h2>Property</h2>
<h3>nodeData</h3>
<p>The real data corresponding to this node</p>
<h3>uid</h3>
<p>The unique identifier of this node</p>
<h3>isRoot</h3>
<p>Whether it is the root node</p>
<h3>layerIndex</h3>
<p>Node level</p>
<h3>width</h3>
<p>Width of the node</p>
<h3>height</h3>
<p>Height of the node</p>
<h3>left</h3>
<p><code>left</code> position of the node</p>
<h3>top</h3>
<p><code>top</code> position of the node</p>
<h3>parent</h3>
<p>Parent node of the node</p>
<h3>children</h3>
<p>List of child nodes of the node</p>
<h3>group</h3>
<p>Node is the content container, <code>svg</code> object</p>
<h3>isDrag</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Whether the node is currently being dragged</p>
<h2>Methods</h2>
<h3>addChildren(node)</h3>
<p>Add a child node</p>
<h3>getSize()</h3>
<p>Calculate the width and height of the node, return a boolean indicating whether
the width and height have changed</p>
<h3>renderNode()</h3>
<p>Render the node to the canvas, will remove the old content node and create a new
one</p>
<h3>render()</h3>
<p>Recursively render this node and all its child nodes. The first call will create
the node content, subsequent calls will only update the node position. To
re-render the content, set the <code>initRender</code> attribute to <code>true</code> first.</p>
<h3>remove()</h3>
<p>Recursively delete this node and all its child nodes</p>
<h3>renderLine()</h3>
<p>Re-render the line from this node to its child nodes</p>
<h3>removeLine()</h3>
<p>Remove the line from this node to its child nodes</p>
<h3>renderExpandBtn()</h3>
<p>Render the content of the expand/collapse button</p>
<h3>removeExpandBtn()</h3>
<p>Remove the expand/collapse button</p>
<h3>getStyle(prop, root, isActive)</h3>
<p>Get the final style value applied to this node</p>
<p><code>prop</code>: the style property to get</p>
<p><code>root</code>: whether it is the root node, default <code>false</code></p>
<p><code>isActive</code>: whether the value being fetched is the active state style value,
default <code>false</code></p>
<h3>setStyle(prop, value, isActive)</h3>
<p>Modify a style of the node, a shortcut method for the <code>SET_NODE_STYLE</code> command</p>
<h3>getData(key)</h3>
<p>Get the specified value in the <code>data</code> object of the node's real data <code>nodeData</code>,
if <code>key</code> is not passed, return the <code>data</code> object</p>
<h3>setData(data)</h3>
<p>Set the value of the specified key in the data object of the node's real data
nodeData, <code>SET_NODE_DATA</code> command's shortcut method</p>
<h3>setText(text)</h3>
<p>Setting the node text, a shortcut for the <code>SET_NODE_TEXT</code> command</p>
<h3>setImage(imgData)</h3>
<p>Setting the node image, a shortcut for the <code>SET_NODE_IMAGE</code> command</p>
<h3>setIcon(icons)</h3>
<p>Setting the node icon, a shortcut for the <code>SET_NODE_ICON</code> command</p>
<h3>setHyperlink(link, title)</h3>
<p>Setting the node hyperlink, a shortcut for the <code>SET_NODE_HYPERLINK</code> command</p>
<h3>setNote(note)</h3>
<p>Setting the node note, a shortcut for the <code>SET_NODE_NOTE</code> command</p>
<h3>setTag(tag)</h3>
<p>Setting the node tag, a shortcut for the <code>SET_NODE_TAG</code> command&quot;</p>
<h3>hide()</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Hide node and its sub-nodes</p>
<h3>show()</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Show node and its sub-nodes</p>
<h3>isParent(node)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Check if the current node is an ancestor of a certain node</p>
<h3>isBrother(node)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Check if the current node is a sibling of a certain node</p>
<h3>checkHasGeneralization()</h3>
<blockquote>
<p>v0.2.0+</p>
</blockquote>
<p>Check if there is a summary</p>
<h3>hideGeneralization()</h3>
<blockquote>
<p>v0.2.0+</p>
</blockquote>
<p>Hide summary node</p>
<h3>showGeneralization()</h3>
<blockquote>
<p>v0.2.0+</p>
</blockquote>
<p>Show summary node</p>
<h3>updateGeneralization()</h3>
<blockquote>
<p>v0.2.0+</p>
</blockquote>
<p>Update summary node</p>
<h3>hasCustomPosition()</h3>
<blockquote>
<p>v0.2.0+</p>
</blockquote>
<p>Check if the node has custom data</p>
<h3>ancestorHasCustomPosition()</h3>
<blockquote>
<p>v0.2.0+</p>
</blockquote>
<p>Check if there is an ancestor node with custom position</p>
<h3>getShape()</h3>
<blockquote>
<p>v0.2.4+</p>
</blockquote>
<p>Get node shape</p>
<h3>setShape(shape)</h3>
<blockquote>
<p>v0.2.4+</p>
</blockquote>
<p>Set node shape, a shortcut method for the <code>SET_NODE_SHAPE</code> command</p>
<h3>getSelfStyle(prop)</h3>
<blockquote>
<p>v0.2.5+</p>
</blockquote>
<p>Get the node's own custom style</p>
<h3>getParentSelfStyle(prop)</h3>
<blockquote>
<p>v0.2.5+</p>
</blockquote>
<p>Get the custom style of the nearest ancestor node with its own custom style</p>
<h3>getSelfInhertStyle(prop)</h3>
<blockquote>
<p>v0.2.5+</p>
</blockquote>
<p>Get the node's own inheritable custom style</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,94 @@
# Render instance
The `render` instance is responsible for the entire rendering process and can be
accessed through `mindMap.renderer`.
## Properties
### activeNodeList
Gets the current list of active nodes
### root
Gets the root node of the node tree
## Methods
### clearActive()
Clears the currently active node
### clearAllActive()
Clears all currently active nodes and 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
### endTextEdit()
> v0.1.6+
End text editing, restore enter key and delete key related shortcuts
### addActiveNode(node)
Add a node to the active list
### removeActiveNode(node)
Remove a node from the active list
### findActiveNodeIndex(node)
Search for the index of a node in the active list
### getNodeIndex(node)
Get the position index of a node among its siblings
### removeOneNode(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
### setNodeDataRender(node, data)
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'}`
### moveNodeTo(node, toNode)
> v0.1.5+
Move a node as a child of another node
### insertBefore(node, exist)
> v0.1.5+
Move a node in front of another node
### insertAfter(node, exist)
> v0.1.5+
Move a node behind another node
### moveNodeToCenter(node)
> v0.2.17+
Move a node to the center of the canvas.
Currently, if there is zoom, returning to the center will reset the zoom.

View File

@@ -0,0 +1,77 @@
<template>
<div>
<h1>Render instance</h1>
<p>The <code>render</code> instance is responsible for the entire rendering process and can be
accessed through <code>mindMap.renderer</code>.</p>
<h2>Properties</h2>
<h3>activeNodeList</h3>
<p>Gets the current list of active nodes</p>
<h3>root</h3>
<p>Gets the root node of the node tree</p>
<h2>Methods</h2>
<h3>clearActive()</h3>
<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>
<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>
<h3>endTextEdit()</h3>
<blockquote>
<p>v0.1.6+</p>
</blockquote>
<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>
<h3>removeActiveNode(node)</h3>
<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>
<h3>getNodeIndex(node)</h3>
<p>Get the position index of a node among its siblings</p>
<h3>removeOneNode(node)</h3>
<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>
<h3>setNodeDataRender(node, data)</h3>
<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>
<h3>moveNodeTo(node, toNode)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<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>
<h3>insertAfter(node, exist)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>Move a node behind another node</p>
<h3>moveNodeToCenter(node)</h3>
<blockquote>
<p>v0.2.17+</p>
</blockquote>
<p>Move a node to the center of the canvas.</p>
<p>Currently, if there is zoom, returning to the center will reset the zoom.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,20 @@
# Select plugin
The `Select` plugin provides the function of right-clicking to select multiple nodes.
## Register
```js
import MindMap from 'simple-mind-map'
import Select from 'simple-mind-map/src/Select.js'
MindMap.usePlugin(Select)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.select`.
## Method
### toPos(x, y)
Convert mouse position to position relative to the container `el`

View File

@@ -0,0 +1,27 @@
<template>
<div>
<h1>Select plugin</h1>
<p>The <code>Select</code> plugin provides the function of right-clicking to 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/Select.js&#x27;</span>
MindMap.usePlugin(Select)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.select</code>.</p>
<h2>Method</h2>
<h3>toPos(x, y)</h3>
<p>Convert mouse position to position relative to the container <code>el</code></p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,135 @@
# Start
## Development
### Local Development
```bash
git clone https://github.com/wanglin2/mind-map.git
cd simple-mind-map
npm i
npm link
cd ..
cd web
npm i
npm link simple-mind-map
npm run serve
```
### Packaging the Library
Since version `0.2.0`, we have added support for packaging the core library
simple-mind-map. This uses the same packaging tool as the sample project web.
```bash
cd web
npm run buildLibrary
```
The `package.json` file in the `simple-mind-map` library provides two export
fields:
```json
{
"module": "index.js",
"main": "./dist/simpleMindMap.umd.min.js"
}
```
Environments that support the `module` field will use `index.js` as the entry
point, otherwise the packed `simpleMindMap.umd.min.js` will be used as the entry
point.
### Compile the doc
```bash
cd web
npm run buildDoc
```
### Packaging the Demo
```bash
cd web
npm run build
```
The `index.html` file will be automatically moved to the root directory.
## Installation
> Things to note before version 0.2.0:
```bash
npm i simple-mind-map
```
`0.2.0` Notes for previous versions:
> Note: This project is directly published in source code form and has not been
> packaged. If compilation fails, a Vue CLI-created project can add the
> following configuration to the vue.config.js file to allow babel-loader to
> compile this dependency:
>
> ```js
> module.exports = {
> transpileDependencies: ["simple-mind-map"],
> };
> ```
>
> Other projects should modify the packaging configuration as needed.
## Usage
```html
<div id="mindMapContainer"></div>
```
```js
import MindMap from "simple-mind-map";
const mindMap = new MindMap({
el: document.getElementById('mindMapContainer'),
data: {
"data": {
"text": "Root Node"
},
"children": []
}
});
```
The non-packaged 'ES' module is introduced by default, and only contains core functions, not unregistered plugin content, which can effectively reduce the size. However, you need to configure the `babel` compilation `simple mind-map` in your project to prevent some newer `js` syntax some browsers not supporting it.
If you need a file in the format of `umd` module, such as `CDN` in the browser, you can import it in the following way:
```js
import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min.js";
```
The disadvantage of this method is that it will contain all the content, including the plugins you have not registered, so the overall volume will be relatively large.
## Problems
### Error when using in Vite, indicating xml-js dependency error
Solution: use the following import method:
```js
import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min";
```
The `simple-mind-map` package provides the unpacked entry field `module`, and
the `xml-js` package dependency needs to import the package in the `node`
environment. Therefore, it cannot be obtained in `Vite` and an error will be
reported. Therefore, specify the import of the packed entry, and all relevant
packages are packed into the product, so there will be no error.
If you need to do further development, that is, you must use the unpacked code,
and if you do not need to parse the `xmind` file, you can remove the `xmind`
module. If you need it, you can try using other libraries to parse `xml` to
`json`.
### Error `Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')`
The reason is that the installed version of `@svgdotjs/svg.js` is too high. You can manually reduce it to the version of `3.0.16`.

View File

@@ -0,0 +1,107 @@
<template>
<div>
<h1>Start</h1>
<h2>Development</h2>
<h3>Local Development</h3>
<pre class="hljs"><code>git <span class="hljs-built_in">clone</span> https://github.com/wanglin2/mind-map.git
<span class="hljs-built_in">cd</span> simple-mind-map
npm i
npm link
<span class="hljs-built_in">cd</span> ..
<span class="hljs-built_in">cd</span> web
npm i
npm link simple-mind-map
npm run serve
</code></pre>
<h3>Packaging the Library</h3>
<p>Since version <code>0.2.0</code>, we have added support for packaging the core library
simple-mind-map. This uses the same packaging tool as the sample project web.</p>
<pre class="hljs"><code><span class="hljs-built_in">cd</span> web
npm run buildLibrary
</code></pre>
<p>The <code>package.json</code> file in the <code>simple-mind-map</code> library provides two export
fields:</p>
<pre class="hljs"><code>{
<span class="hljs-attr">&quot;module&quot;</span>: <span class="hljs-string">&quot;index.js&quot;</span>,
<span class="hljs-attr">&quot;main&quot;</span>: <span class="hljs-string">&quot;./dist/simpleMindMap.umd.min.js&quot;</span>
}
</code></pre>
<p>Environments that support the <code>module</code> field will use <code>index.js</code> as the entry
point, otherwise the packed <code>simpleMindMap.umd.min.js</code> will be used as the entry
point.</p>
<h3>Compile the doc</h3>
<pre class="hljs"><code><span class="hljs-built_in">cd</span> web
npm run buildDoc
</code></pre>
<h3>Packaging the Demo</h3>
<pre class="hljs"><code><span class="hljs-built_in">cd</span> web
npm run build
</code></pre>
<p>The <code>index.html</code> file will be automatically moved to the root directory.</p>
<h2>Installation</h2>
<blockquote>
<p>Things to note before version 0.2.0:</p>
</blockquote>
<pre class="hljs"><code>npm i simple-mind-map
</code></pre>
<p><code>0.2.0</code> Notes for previous versions:</p>
<blockquote>
<p>Note: This project is directly published in source code form and has not been
packaged. If compilation fails, a Vue CLI-created project can add the
following configuration to the vue.config.js file to allow babel-loader to
compile this dependency:</p>
<pre class="hljs"><code><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-attr">transpileDependencies</span>: [<span class="hljs-string">&quot;simple-mind-map&quot;</span>],
};
</code></pre>
<p>Other projects should modify the packaging configuration as needed.</p>
</blockquote>
<h2>Usage</h2>
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;mindMapContainer&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;simple-mind-map&quot;</span>;
<span class="hljs-keyword">const</span> mindMap = <span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">el</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">&#x27;mindMapContainer&#x27;</span>),
<span class="hljs-attr">data</span>: {
<span class="hljs-string">&quot;data&quot;</span>: {
<span class="hljs-string">&quot;text&quot;</span>: <span class="hljs-string">&quot;Root Node&quot;</span>
},
<span class="hljs-string">&quot;children&quot;</span>: []
}
});
</code></pre>
<p>The non-packaged 'ES' module is introduced by default, and only contains core functions, not unregistered plugin content, which can effectively reduce the size. However, you need to configure the <code>babel</code> compilation <code>simple mind-map</code> in your project to prevent some newer <code>js</code> syntax some browsers not supporting it.</p>
<p>If you need a file in the format of <code>umd</code> module, such as <code>CDN</code> in the browser, you can import it in the following way:</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;simple-mind-map/dist/simpleMindMap.umd.min.js&quot;</span>;
</code></pre>
<p>The disadvantage of this method is that it will contain all the content, including the plugins you have not registered, so the overall volume will be relatively large.</p>
<h2>Problems</h2>
<h3>Error when using in Vite, indicating xml-js dependency error</h3>
<p>Solution: use the following import method:</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;simple-mind-map/dist/simpleMindMap.umd.min&quot;</span>;
</code></pre>
<p>The <code>simple-mind-map</code> package provides the unpacked entry field <code>module</code>, and
the <code>xml-js</code> package dependency needs to import the package in the <code>node</code>
environment. Therefore, it cannot be obtained in <code>Vite</code> and an error will be
reported. Therefore, specify the import of the packed entry, and all relevant
packages are packed into the product, so there will be no error.</p>
<p>If you need to do further development, that is, you must use the unpacked code,
and if you do not need to parse the <code>xmind</code> file, you can remove the <code>xmind</code>
module. If you need it, you can try using other libraries to parse <code>xml</code> to
<code>json</code>.</p>
<h3>Error <code>Getting bbox of element &quot;text&quot; is not possible: TypeError: Cannot read properties of undefined (reading 'apply')</code></h3>
<p>The reason is that the installed version of <code>@svgdotjs/svg.js</code> is too high. You can manually reduce it to the version of <code>3.0.16</code>.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,18 @@
# Participate in translation
Thanks for the English translation provided by [Emircan ERKUL](https://github.com/emircanerkul).
If you want to participate in the translation of this document, you can clone this repository first.
The translated documents are in the `/web/src/pages/Doc/` directory, and currently support English(`en`) and Simplified Chinese(`zh`).
If you are adding a new language type, you can create a new directory under the `/web/src/pages/Doc/` directory, Then create a folder for each chapter, You can also directly copy all chapter directories under the existing language directory for translation, Note that you only need to write the `index.md` file, The `index.vue` file under the chapter directory is automatically generated by the script according to `index.md`.
If you are adding a translation chapter to an existing language type, You can create a new chapter directory under the target language directory, You only need to create a `index.md` file under the directory.
After you complete the translation, you can directly submit `Pull requests`.
If you are a front-end programmer and want to run the service, check the effect of the document page, If a new chapter is added, the file `/web/src/pages/Doc/catalogList.js` needs to be modified, Select the appropriate location in the `StartList` or `APIList` array to insert the `path` of the new chapter. Then you need to run `npm run buildDoc` under the `web` directory to compile the directory and route. Finally, run `npm run serve` starts the local service. Open the following path to view the document:
`ip:port/#/doc/zh/introduction`

View File

@@ -0,0 +1,24 @@
<template>
<div>
<h1>Participate in translation</h1>
<p>Thanks for the English translation provided by <a href="https://github.com/emircanerkul">Emircan ERKUL</a>.</p>
<p>If you want to participate in the translation of this document, you can clone this repository first.</p>
<p>The translated documents are in the <code>/web/src/pages/Doc/</code> directory, and currently support English(<code>en</code>) and Simplified Chinese(<code>zh</code>).</p>
<p>If you are adding a new language type, you can create a new directory under the <code>/web/src/pages/Doc/</code> directory, Then create a folder for each chapter, You can also directly copy all chapter directories under the existing language directory for translation, Note that you only need to write the <code>index.md</code> file, The <code>index.vue</code> file under the chapter directory is automatically generated by the script according to <code>index.md</code>.</p>
<p>If you are adding a translation chapter to an existing language type, You can create a new chapter directory under the target language directory, You only need to create a <code>index.md</code> file under the directory.</p>
<p>After you complete the translation, you can directly submit <code>Pull requests</code>.</p>
<p>If you are a front-end programmer and want to run the service, check the effect of the document page, If a new chapter is added, the file <code>/web/src/pages/Doc/catalogList.js</code> needs to be modified, Select the appropriate location in the <code>StartList</code> or <code>APIList</code> array to insert the <code>path</code> of the new chapter. Then you need to run <code>npm run buildDoc</code> under the <code>web</code> directory to compile the directory and route. Finally, run <code>npm run serve</code> starts the local service. Open the following path to view the document:</p>
<p><code>ip:port/#/doc/zh/introduction</code></p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,156 @@
# Utility Methods
## Base utility Methods
Reference:
```js
import {walk, ...} from 'simple-mind-map/src/utils'
```
### Methods
#### walk(root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0)
Depth-first traversal of a tree
`root`: the root node of the tree to be traversed
`parent`: parent node
`beforeCallback`: preorder traversal callback function, callback parameters are:
root, parent, isRoot, layerIndex, index
`afterCallback`: postorder traversal callback function, callback parameters are:
root, parent, isRoot, layerIndex, index
`isRoot`: whether it is the root node
`layerIndex`: node level
`index`: index of the node among its siblings
Example:
```js
walk(tree, null, () => {}, () => {}, false, 0, 0);
```
#### bfsWalk(root, callback)
Breadth-first traversal of a tree
#### resizeImgSize(width, height, maxWidth, maxHeight)
Resize image size
`width`: original width of the image
`height`: original height of the image
`maxWidth`: the width to resize to
`maxHeight`: the height to resize to
`maxWidth` and `maxHeight` can both be passed, or only one of them can be passed
#### resizeImg(imgUrl, maxWidth, maxHeight)
Resize image, internally loads the image first, then calls the `resizeImgSize`
method, and returns a `promise`
#### simpleDeepClone(data)
Extremely simple deep copy method, can only be used for objects that are all
basic data, otherwise it will throw an error
#### copyRenderTree(tree, root)
Copy render tree data, example:
```js
copyRenderTree({}, this.mindMap.renderer.renderTree);
```
#### copyNodeTree(tree, root)
Copy node tree data, mainly eliminating the reference `node` instance `_node`
and copying the `data` of the data object, example:
```js
copyNodeTree({}, node);
```
#### imgToDataUrl(src)
Convert image to dataURL
#### downloadFile(file, fileName)
Download file
#### throttle(fn, time = 300, ctx)
Throttle function
#### asyncRun(taskList, callback = () => {})
Run tasks in task list asynchronously, tasks are run synchronously without order
#### degToRad(deg)
> v0.2.24+
Angle to radian
#### camelCaseToHyphen(str)
> v0.2.24+
CamelCase to hyphen
#### joinFontStr({ italic, bold, fontSize, fontFamily })
> v0.3.4+
Join the `font` attribute value of the `css` font
#### measureText(text, { italic, bold, fontSize, fontFamily })
> v0.3.4+
Measure the width and height of the text, return value:
```js
{ width, height }
```
## Simulate CSS background in Canvas
Import:
```js
import drawBackgroundImageToCanvas from 'simple-mind-map/src/utils/simulateCSSBackgroundInCanvas'
```
Usage
```js
let width = 500
let height = 500
let img = '/1.jpg'
let canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
drawBackgroundImageToCanvas(ctx, width, height, img, {
backgroundRepeat: 'repeat-y',
backgroundSize: '60%',
backgroundPosition: 'center center'
}, (err) => {
if (err) {
// fail
} else {
// success
}
})
```

View File

@@ -0,0 +1,112 @@
<template>
<div>
<h1>Utility Methods</h1>
<h2>Base utility Methods</h2>
<p>Reference:</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> {walk, ...} <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/utils&#x27;</span>
</code></pre>
<h3>Methods</h3>
<h4>walk(root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0)</h4>
<p>Depth-first traversal of a tree</p>
<p><code>root</code>: the root node of the tree to be traversed</p>
<p><code>parent</code>: parent node</p>
<p><code>beforeCallback</code>: preorder traversal callback function, callback parameters are:
root, parent, isRoot, layerIndex, index</p>
<p><code>afterCallback</code>: postorder traversal callback function, callback parameters are:
root, parent, isRoot, layerIndex, index</p>
<p><code>isRoot</code>: whether it is the root node</p>
<p><code>layerIndex</code>: node level</p>
<p><code>index</code>: index of the node among its siblings</p>
<p>Example:</p>
<pre class="hljs"><code>walk(tree, <span class="hljs-literal">null</span>, <span class="hljs-function">() =&gt;</span> {}, <span class="hljs-function">() =&gt;</span> {}, <span class="hljs-literal">false</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
</code></pre>
<h4>bfsWalk(root, callback)</h4>
<p>Breadth-first traversal of a tree</p>
<h4>resizeImgSize(width, height, maxWidth, maxHeight)</h4>
<p>Resize image size</p>
<p><code>width</code>: original width of the image</p>
<p><code>height</code>: original height of the image</p>
<p><code>maxWidth</code>: the width to resize to</p>
<p><code>maxHeight</code>: the height to resize to</p>
<p><code>maxWidth</code> and <code>maxHeight</code> can both be passed, or only one of them can be passed</p>
<h4>resizeImg(imgUrl, maxWidth, maxHeight)</h4>
<p>Resize image, internally loads the image first, then calls the <code>resizeImgSize</code>
method, and returns a <code>promise</code></p>
<h4>simpleDeepClone(data)</h4>
<p>Extremely simple deep copy method, can only be used for objects that are all
basic data, otherwise it will throw an error</p>
<h4>copyRenderTree(tree, root)</h4>
<p>Copy render tree data, example:</p>
<pre class="hljs"><code>copyRenderTree({}, <span class="hljs-built_in">this</span>.mindMap.renderer.renderTree);
</code></pre>
<h4>copyNodeTree(tree, root)</h4>
<p>Copy node tree data, mainly eliminating the reference <code>node</code> instance <code>_node</code>
and copying the <code>data</code> of the data object, example:</p>
<pre class="hljs"><code>copyNodeTree({}, node);
</code></pre>
<h4>imgToDataUrl(src)</h4>
<p>Convert image to dataURL</p>
<h4>downloadFile(file, fileName)</h4>
<p>Download file</p>
<h4>throttle(fn, time = 300, ctx)</h4>
<p>Throttle function</p>
<h4>asyncRun(taskList, callback = () =&gt; {})</h4>
<p>Run tasks in task list asynchronously, tasks are run synchronously without order</p>
<h4>degToRad(deg)</h4>
<blockquote>
<p>v0.2.24+</p>
</blockquote>
<p>Angle to radian</p>
<h4>camelCaseToHyphen(str)</h4>
<blockquote>
<p>v0.2.24+</p>
</blockquote>
<p>CamelCase to hyphen</p>
<h4>joinFontStr({ italic, bold, fontSize, fontFamily })</h4>
<blockquote>
<p>v0.3.4+</p>
</blockquote>
<p>Join the <code>font</code> attribute value of the <code>css</code> font</p>
<h4>measureText(text, { italic, bold, fontSize, fontFamily })</h4>
<blockquote>
<p>v0.3.4+</p>
</blockquote>
<p>Measure the width and height of the text, return value:</p>
<pre class="hljs"><code>{ width, height }
</code></pre>
<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>
</code></pre>
<p>Usage</p>
<pre class="hljs"><code><span class="hljs-keyword">let</span> width = <span class="hljs-number">500</span>
<span class="hljs-keyword">let</span> height = <span class="hljs-number">500</span>
<span class="hljs-keyword">let</span> img = <span class="hljs-string">&#x27;/1.jpg&#x27;</span>
<span class="hljs-keyword">let</span> canvas = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;canvas&#x27;</span>)
canvas.width = width
canvas.height = height
drawBackgroundImageToCanvas(ctx, width, height, img, {
<span class="hljs-attr">backgroundRepeat</span>: <span class="hljs-string">&#x27;repeat-y&#x27;</span>,
<span class="hljs-attr">backgroundSize</span>: <span class="hljs-string">&#x27;60%&#x27;</span>,
<span class="hljs-attr">backgroundPosition</span>: <span class="hljs-string">&#x27;center center&#x27;</span>
}, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
<span class="hljs-keyword">if</span> (err) {
<span class="hljs-comment">// fail</span>
} <span class="hljs-keyword">else</span> {
<span class="hljs-comment">// success</span>
}
})
</code></pre>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,57 @@
# View instance
The `view` instance is responsible for view operations, and can be obtained
through `mindMap.view`
## Methods
### translateX(step)
Translate in the `x` direction, `step`: number of pixels to translate
### translateY(step)
Translate in the `y` direction, `step`: number of pixels to translate
### translateXTo(x)
> v0.2.11+
Translate the `x` direction to a specific position
### translateYTo(y)
> v0.2.11+
Translate the `y` direction to a specific position
### reset()
Revert to the default transformation
### narrow()
Zoom out
### enlarge()
Zoom in
### getTransformData()
> v0.1.1+
Get the current transform data, can be used for display
### setTransformData(data)
> v0.1.1+
Dynamically set transform data, transform data can be obtained through the
getTransformData method"
### setScale(scale)
> v0.2.17+
Setting Zoom

View File

@@ -0,0 +1,55 @@
<template>
<div>
<h1>View instance</h1>
<p>The <code>view</code> instance is responsible for view operations, and can be obtained
through <code>mindMap.view</code></p>
<h2>Methods</h2>
<h3>translateX(step)</h3>
<p>Translate in the <code>x</code> direction, <code>step</code>: number of pixels to translate</p>
<h3>translateY(step)</h3>
<p>Translate in the <code>y</code> direction, <code>step</code>: number of pixels to translate</p>
<h3>translateXTo(x)</h3>
<blockquote>
<p>v0.2.11+</p>
</blockquote>
<p>Translate the <code>x</code> direction to a specific position</p>
<h3>translateYTo(y)</h3>
<blockquote>
<p>v0.2.11+</p>
</blockquote>
<p>Translate the <code>y</code> direction to a specific position</p>
<h3>reset()</h3>
<p>Revert to the default transformation</p>
<h3>narrow()</h3>
<p>Zoom out</p>
<h3>enlarge()</h3>
<p>Zoom in</p>
<h3>getTransformData()</h3>
<blockquote>
<p>v0.1.1+</p>
</blockquote>
<p>Get the current transform data, can be used for display</p>
<h3>setTransformData(data)</h3>
<blockquote>
<p>v0.1.1+</p>
</blockquote>
<p>Dynamically set transform data, transform data can be obtained through the
getTransformData method&quot;</p>
<h3>setScale(scale)</h3>
<blockquote>
<p>v0.2.17+</p>
</blockquote>
<p>Setting Zoom</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,50 @@
# Watermark plugin
> 0.2.24+
`Watermark` instance is responsible for displaying the watermark.
Please refer to the [Instantiation Options](/mind-map/#/doc/zh/constructor) of the `MindMap` class for configuration.
## Register
```js
import MindMap from 'simple-mind-map'
import Watermark from 'simple-mind-map/src/Watermark.js'
MindMap.usePlugin(Watermark)
```
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.watermark`.
## Methods
### draw()
Redraw the watermark.
Note: For imprecise rendering, some watermarks beyond the visible area will be drawn. If you have extreme performance requirements, it is recommended to develop the watermark function yourself.
### updateWatermark(config)
Update watermark config. Example:
```js
mindMap.watermark.updateWatermark({
text: 'Watermark text',
lineSpacing: 100,
textSpacing: 100,
angle: 50,
textStyle: {
color: '#000',
opacity: 1,
fontSize: 20
}
})
```
### hasWatermark()
> v0.3.2+
Gets whether the watermark exists.

View File

@@ -0,0 +1,51 @@
<template>
<div>
<h1>Watermark plugin</h1>
<blockquote>
<p>0.2.24+</p>
</blockquote>
<p><code>Watermark</code> instance is responsible for displaying the watermark.</p>
<p>Please refer to the <a href="/mind-map/#/doc/zh/constructor">Instantiation Options</a> of the <code>MindMap</code> class for configuration.</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> Watermark <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/Watermark.js&#x27;</span>
MindMap.usePlugin(Watermark)
</code></pre>
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.watermark</code>.</p>
<h2>Methods</h2>
<h3>draw()</h3>
<p>Redraw the watermark.</p>
<p>Note: For imprecise rendering, some watermarks beyond the visible area will be drawn. If you have extreme performance requirements, it is recommended to develop the watermark function yourself.</p>
<h3>updateWatermark(config)</h3>
<p>Update watermark config. Example:</p>
<pre class="hljs"><code>mindMap.watermark.updateWatermark({
<span class="hljs-attr">text</span>: <span class="hljs-string">&#x27;Watermark text&#x27;</span>,
<span class="hljs-attr">lineSpacing</span>: <span class="hljs-number">100</span>,
<span class="hljs-attr">textSpacing</span>: <span class="hljs-number">100</span>,
<span class="hljs-attr">angle</span>: <span class="hljs-number">50</span>,
<span class="hljs-attr">textStyle</span>: {
<span class="hljs-attr">color</span>: <span class="hljs-string">&#x27;#000&#x27;</span>,
<span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
<span class="hljs-attr">fontSize</span>: <span class="hljs-number">20</span>
}
})
</code></pre>
<h3>hasWatermark()</h3>
<blockquote>
<p>v0.3.2+</p>
</blockquote>
<p>Gets whether the watermark exists.</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,51 @@
# XMind parse
> v0.2.7+
Provides methods for importing `XMind` files.
## Import
```js
import xmind from 'simple-mind-map/src/parse/xmind.js'
```
If you are using the file in the format of `umd`, you can obtain it in the following way:
```js
import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min"
MindMap.xmind
```
## Methods
### xmind.parseXmindFile(file)
Parsing the `.xmind` file and returning the parsed data. Note that this is
complete data, including the node tree, theme, and structure. You can use
`mindMap.setFullData(data)` to render the returned data to the canvas.
`file`: `File` object
### xmind.transformXmind(content)
Convert `xmind` data. The `.xmind` file is essentially a `zip` file that can be
decompressed by changing the suffix to zip. Inside, there is a `content.json`
file. If you have parsed this file yourself, you can pass the contents of this
file to this method for conversion. The converted data is the complete data,
including the node tree, theme, structure, etc. You can use
`mindMap.setFullData(data)` to render the returned data to the canvas.
`content`: the contents of the `content.json` file within the `.xmind` zip
package
### xmind.transformOldXmind(content)
> v0.2.8+
For data parsing of the `xmind8` version, because the `.xmind` file in this
version does not have a `content.json`, it corresponds to `content.xml`.
`content`: the contents of the `content.xml` file within the `.xmind` zip
package

View File

@@ -0,0 +1,51 @@
<template>
<div>
<h1>XMind parse</h1>
<blockquote>
<p>v0.2.7+</p>
</blockquote>
<p>Provides methods for importing <code>XMind</code> files.</p>
<h2>Import</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> xmind <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;simple-mind-map/src/parse/xmind.js&#x27;</span>
</code></pre>
<p>If you are using the file in the format of <code>umd</code>, you can obtain it in the following way:</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;simple-mind-map/dist/simpleMindMap.umd.min&quot;</span>
MindMap.xmind
</code></pre>
<h2>Methods</h2>
<h3>xmind.parseXmindFile(file)</h3>
<p>Parsing the <code>.xmind</code> file and returning the parsed data. Note that this is
complete data, including the node tree, theme, and structure. You can use
<code>mindMap.setFullData(data)</code> to render the returned data to the canvas.</p>
<p><code>file</code>: <code>File</code> object</p>
<h3>xmind.transformXmind(content)</h3>
<p>Convert <code>xmind</code> data. The <code>.xmind</code> file is essentially a <code>zip</code> file that can be
decompressed by changing the suffix to zip. Inside, there is a <code>content.json</code>
file. If you have parsed this file yourself, you can pass the contents of this
file to this method for conversion. The converted data is the complete data,
including the node tree, theme, structure, etc. You can use
<code>mindMap.setFullData(data)</code> to render the returned data to the canvas.</p>
<p><code>content</code>: the contents of the <code>content.json</code> file within the <code>.xmind</code> zip
package</p>
<h3>xmind.transformOldXmind(content)</h3>
<blockquote>
<p>v0.2.8+</p>
</blockquote>
<p>For data parsing of the <code>xmind8</code> version, because the <code>.xmind</code> file in this
version does not have a <code>content.json</code>, it corresponds to <code>content.xml</code>.</p>
<p><code>content</code>: the contents of the <code>content.xml</code> file within the <code>.xmind</code> zip
package</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

15
web/src/pages/Doc/i18n.js Normal file
View File

@@ -0,0 +1,15 @@
const data = {
pageCatalog: {
zh: '本页目录',
en: 'Page catalog'
},
demo: {
zh: '在线示例',
en: 'Online Demo'
}
}
const t = (str, lang) => {
return data[str] ? data[str][lang] || data[str].zh : ''
}
export default t

View File

@@ -0,0 +1,3 @@
export default [{"lang":"zh","children":[{"path":"batchExecution","title":"BatchExecution实例"},{"path":"changelog","title":"Changelog"},{"path":"command","title":"Command实例"},{"path":"constructor","title":"构造函数"},{"path":"doExport","title":"Export 插件"},{"path":"drag","title":"Drag插件"},{"path":"introduction","title":"简介"},{"path":"keyboardNavigation","title":"KeyboardNavigation插件"},{"path":"keyCommand","title":"KeyCommand实例"},{"path":"miniMap","title":"MiniMap插件"},{"path":"node","title":"Node实例"},{"path":"render","title":"Render实例"},{"path":"select","title":"Select 插件 "},{"path":"start","title":"开始"},{"path":"translate","title":"参与翻译"},{"path":"utils","title":"内置工具方法"},{"path":"view","title":"View实例"},{"path":"watermark","title":"Watermark插件"},{"path":"xmind","title":"XMind解析"}]},{"lang":"en","children":[{"path":"batchExecution","title":"batchExecution instance"},{"path":"changelog","title":"Changelog"},{"path":"command","title":"command instance"},{"path":"constructor","title":"Constructor"},{"path":"doExport","title":"Export plugin"},{"path":"drag","title":"Drag plugin"},{"path":"introduction","title":"Introduction"},{"path":"keyboardNavigation","title":"KeyboardNavigation plugin"},{"path":"keyCommand","title":"KeyCommand instance"},{"path":"miniMap","title":"MiniMap plugin"},{"path":"node","title":"Node instance"},{"path":"render","title":"Render instance"},{"path":"select","title":"Select plugin"},{"path":"start","title":"Start"},{"path":"translate","title":"Participate in translation"},{"path":"utils","title":"Utility Methods"},{"path":"view","title":"View instance"},{"path":"watermark","title":"Watermark plugin"},{"path":"xmind","title":"XMind parse"}]}]

View File

@@ -0,0 +1,13 @@
# BatchExecution实例
`batchExecution`用来批量异步的执行一些操作,如果某个操作同时多次调用,那么只会在下一个事件循环里执行一次。可以通过`mindMap.batchExecution`获取到该实例
## 方法
### push(name, fn)
添加任务。
`name`:任务名称
`fn`:任务

View File

@@ -0,0 +1,22 @@
<template>
<div>
<h1>BatchExecution实例</h1>
<p><code>batchExecution</code>用来批量异步的执行一些操作如果某个操作同时多次调用那么只会在下一个事件循环里执行一次可以通过<code>mindMap.batchExecution</code>获取到该实例</p>
<h2>方法</h2>
<h3>push(name, fn)</h3>
<p>添加任务</p>
<p><code>name</code>任务名称</p>
<p><code>fn</code>任务</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,173 @@
# Changelog
## 0.3.4
New节点文本增加自动换行功能。
Fix1.修复批量删除的节点中如果存在根节点会出现删除异常的问题。2.修复底边风格的情况下,节点高度过高会和其他节点重叠的问题。
## 0.3.3
修复:根节点文字无法换行的问题。
## 0.3.2
修复1.修复二级节点拖拽到其他节点或其他节点拖拽到二级节点时节点样式没有更新的问题2.修复当思维导图实际内容大于屏幕宽高时,导出的时候超出的部分没有绘制水印的问题。
## 0.3.1
修复1.删除背景图片不生效的问题2.节点拖拽到根节点时连接线跑到根节点上方的问题。
新增:背景图片展示增加位置和大小设置。导出的图片也同步支持该设置。
## 0.3.0
升级为插件化架构,将一些非核心功能抽离出来作为插件,按需注册,减小整体体积。
## 0.2.24
新增:节点自由拖拽改为可配置,默认为`false`不开启;支持添加水印。
## 0.2.23
新增:支持注册新主题。
## 0.2.22
优化:取消内置`simple-mind-map`包的主题和结构图片,改为由使用者自行维护,原有图片可在`web/assets/img/`目录找到。
## 0.2.21
新增:支持节点横线风格。
## 0.2.20
修复画布距窗口左上角不为0时节点拖拽出现偏移的问题。
## 0.2.19
修复:没有激活节点时随便按什么键都会触发自动聚焦的问题。
## 0.2.18
优化:键盘导航寻找焦点的算法,支持简单算法、区域算法、阴影算法。
## 0.2.17
新增:键盘导航,即通过方向键来切换激活的节点;支持在大纲直接编辑节点文本内容。
## 0.2.16
优化:小地图、拖拽性能。
## 0.2.15
优化:本地文件编辑。
新增:支持双击节点内图片进行大图预览。
## 0.2.14
优化:插入子节点时自动展开。
修复:小地图关闭时报错的问题。
## 0.2.13
修复:子节点收起状态复制时丢失的问题。
## 0.2.11
修复:修复子节点收起状态复制时丢失的问题。
新增:支持小地图。
## 0.2.10
优化:手动创建节点时立即聚焦。
修复:连线样式深度更新问题。
新增:逻辑结构图、思维导图新增直线连接风格、直连风格。
## 0.2.9
新增:支持新建、打开、保存到电脑本地文件。
## 0.2.8
修复xmind8版本文件导入失败的问题。
新增:支持展开到指定层级。
## 0.2.7
修复:根节点添加多个节点爆栈的问题。
新增:支持导入.xmind文件。
## 0.2.6
新增导出svg增加title标签。
## 0.2.5
修复节点展开收起的bug。
新增:节点支持自定义线条样式。
## 0.2.4
新增:节点支持多种形状。
## 0.2.3
修复:编辑节点文本时快捷键冲突的问题;右键菜单快捷键提示错误;右键菜单快捷键提示。
## 0.2.2
修复:输入字符串'/'和快捷键'/'冲突问题。
## 0.2.1
新增支持导出为pdf。
## 0.2.0
新增经典4主题支持添加概要支持自由拖拽上移节点、下移节点、复制节点、剪切节点、粘贴节点、一键整理布快捷键库打包Ctrl+左键多选。
## 0.1.18
修复:节点图标不能删除的问题;工具按钮置灰仍然可以点击的问题。
## 0.1.17
新增:增加只读模式。
## 0.1.16
新增节点备注支持markdown及富文本。
修复:不能选中文字的问题;节点标注在节点激活后无法隐藏问题;超链接、备注、标签等文字编辑时返回键和回车键与思维导图快捷键冲突的问题。
## 0.1.15
新增:状态数据支持保存激活状态、视图状态(拖动位置、缩放值);支持节点拖拽。
## 0.1.14
修复:存在激活节点时设置主题存在的问题。
## 0.1.13
新增快捷键功能新增导出为json。
优化:一些细节。
## 0.1.12
新增:本地存储;右键菜单功能等。
## 0.1.0
完成基本功能。

View File

@@ -0,0 +1,102 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.3.4</h2>
<p>New节点文本增加自动换行功能</p>
<p>Fix1.修复批量删除的节点中如果存在根节点会出现删除异常的问题2.修复底边风格的情况下节点高度过高会和其他节点重叠的问题</p>
<h2>0.3.3</h2>
<p>修复根节点文字无法换行的问题</p>
<h2>0.3.2</h2>
<p>修复1.修复二级节点拖拽到其他节点或其他节点拖拽到二级节点时节点样式没有更新的问题2.修复当思维导图实际内容大于屏幕宽高时导出的时候超出的部分没有绘制水印的问题</p>
<h2>0.3.1</h2>
<p>修复1.删除背景图片不生效的问题2.节点拖拽到根节点时连接线跑到根节点上方的问题</p>
<p>新增背景图片展示增加位置和大小设置导出的图片也同步支持该设置</p>
<h2>0.3.0</h2>
<p>升级为插件化架构将一些非核心功能抽离出来作为插件按需注册减小整体体积</p>
<h2>0.2.24</h2>
<p>新增节点自由拖拽改为可配置默认为<code>false</code>不开启支持添加水印</p>
<h2>0.2.23</h2>
<p>新增支持注册新主题</p>
<h2>0.2.22</h2>
<p>优化取消内置<code>simple-mind-map</code>包的主题和结构图片改为由使用者自行维护原有图片可在<code>web/assets/img/</code>目录找到</p>
<h2>0.2.21</h2>
<p>新增支持节点横线风格</p>
<h2>0.2.20</h2>
<p>修复画布距窗口左上角不为0时节点拖拽出现偏移的问题</p>
<h2>0.2.19</h2>
<p>修复没有激活节点时随便按什么键都会触发自动聚焦的问题</p>
<h2>0.2.18</h2>
<p>优化键盘导航寻找焦点的算法支持简单算法区域算法阴影算法</p>
<h2>0.2.17</h2>
<p>新增键盘导航即通过方向键来切换激活的节点支持在大纲直接编辑节点文本内容</p>
<h2>0.2.16</h2>
<p>优化小地图拖拽性能</p>
<h2>0.2.15</h2>
<p>优化本地文件编辑</p>
<p>新增支持双击节点内图片进行大图预览</p>
<h2>0.2.14</h2>
<p>优化插入子节点时自动展开</p>
<p>修复小地图关闭时报错的问题</p>
<h2>0.2.13</h2>
<p>修复子节点收起状态复制时丢失的问题</p>
<h2>0.2.11</h2>
<p>修复修复子节点收起状态复制时丢失的问题</p>
<p>新增支持小地图</p>
<h2>0.2.10</h2>
<p>优化手动创建节点时立即聚焦</p>
<p>修复连线样式深度更新问题</p>
<p>新增逻辑结构图思维导图新增直线连接风格直连风格</p>
<h2>0.2.9</h2>
<p>新增支持新建打开保存到电脑本地文件</p>
<h2>0.2.8</h2>
<p>修复xmind8版本文件导入失败的问题</p>
<p>新增支持展开到指定层级</p>
<h2>0.2.7</h2>
<p>修复根节点添加多个节点爆栈的问题</p>
<p>新增支持导入.xmind文件</p>
<h2>0.2.6</h2>
<p>新增导出svg增加title标签</p>
<h2>0.2.5</h2>
<p>修复节点展开收起的bug</p>
<p>新增节点支持自定义线条样式</p>
<h2>0.2.4</h2>
<p>新增节点支持多种形状</p>
<h2>0.2.3</h2>
<p>修复编辑节点文本时快捷键冲突的问题右键菜单快捷键提示错误右键菜单快捷键提示</p>
<h2>0.2.2</h2>
<p>修复输入字符串'/'和快捷键'/'冲突问题</p>
<h2>0.2.1</h2>
<p>新增支持导出为pdf</p>
<h2>0.2.0</h2>
<p>新增经典4主题支持添加概要支持自由拖拽上移节点下移节点复制节点剪切节点粘贴节点一键整理布快捷键库打包Ctrl+左键多选</p>
<h2>0.1.18</h2>
<p>修复节点图标不能删除的问题工具按钮置灰仍然可以点击的问题</p>
<h2>0.1.17</h2>
<p>新增增加只读模式</p>
<h2>0.1.16</h2>
<p>新增节点备注支持markdown及富文本</p>
<p>修复不能选中文字的问题节点标注在节点激活后无法隐藏问题超链接备注标签等文字编辑时返回键和回车键与思维导图快捷键冲突的问题</p>
<h2>0.1.15</h2>
<p>新增状态数据支持保存激活状态视图状态拖动位置缩放值支持节点拖拽</p>
<h2>0.1.14</h2>
<p>修复存在激活节点时设置主题存在的问题</p>
<h2>0.1.13</h2>
<p>新增快捷键功能新增导出为json</p>
<p>优化一些细节</p>
<h2>0.1.12</h2>
<p>新增本地存储右键菜单功能等</p>
<h2>0.1.0</h2>
<p>完成基本功能</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,29 @@
# Command实例
`command`实例负责命令的添加及执行,内置了很多命令,也可以自行添加,命令指需要在历史堆栈数据里添加副本的操作。可通过`mindMap.command`获取到该实例
## 方法
### add(name, fn)
添加命令。
`name`:命令名称
`fn`:命令要执行的方法
### remove(name, fn)
移除命令。
`name`:要移除的命令名称
`fn`:要移除的方法,不传的话移除该命令所有的方法
### getCopyData()
获取渲染树数据副本
### clearHistory()
清空历史堆栈数据

View File

@@ -0,0 +1,30 @@
<template>
<div>
<h1>Command实例</h1>
<p><code>command</code>实例负责命令的添加及执行内置了很多命令也可以自行添加命令指需要在历史堆栈数据里添加副本的操作可通过<code>mindMap.command</code>获取到该实例</p>
<h2>方法</h2>
<h3>add(name, fn)</h3>
<p>添加命令</p>
<p><code>name</code>命令名称</p>
<p><code>fn</code>命令要执行的方法</p>
<h3>remove(name, fn)</h3>
<p>移除命令</p>
<p><code>name</code>要移除的命令名称</p>
<p><code>fn</code>要移除的方法不传的话移除该命令所有的方法</p>
<h3>getCopyData()</h3>
<p>获取渲染树数据副本</p>
<h3>clearHistory()</h3>
<p>清空历史堆栈数据</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,315 @@
# 构造函数
## 基本使用
```html
<div id="mindMapContainer"></div>
```
```js
import MindMap from "simple-mind-map";
const mindMap = new MindMap({
el: document.getElementById('mindMapContainer'),
data: {
"data": {
"text": "根节点"
},
"children": []
}
});
```
## 实例化选项
| 字段名称 | 类型 | 默认值 | 描述 | 是否必填 |
| -------------------------------- | ------- | ---------------- | ------------------------------------------------------------ | -------- |
| el | Element | | 容器元素必须为DOM元素 | 是 |
| data | Object | {} | 思维导图数据,可参考:[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) | |
| layout | String | logicalStructure | 布局类型可选列表logicalStructure逻辑结构图、mindMap思维导图、catalogOrganization目录组织图、organizationStructure组织结构图 | |
| theme | String | default | 主题可选列表default默认、classic脑图经典、minions小黄人、pinkGrape粉红葡萄、mint薄荷、gold金色vip、vitalityOrange活力橙、greenLeaf绿叶、dark2暗色2、skyGreen天清绿、classic2脑图经典2、classic3脑图经典3、classic4脑图经典4v0.2.0+、classicGreen经典绿、classicBlue经典蓝、blueSky天空蓝、brainImpairedPink脑残粉、dark暗色、earthYellow泥土黄、freshGreen清新绿、freshRed清新红、romanticPurple浪漫紫 | |
| themeConfig | Object | {} | 主题配置,会和所选择的主题进行合并,可用字段可参考:[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js) | |
| scaleRatio | Number | 0.1 | 放大缩小的增量比例 | |
| maxTag | Number | 5 | 节点里最多显示的标签数量,多余的会被丢弃 | |
| exportPadding | Number | 20 | 导出图片时的内边距 | |
| imgTextMargin | Number | 5 | 节点里图片和文字的间距 | |
| textContentMargin | Number | 2 | 节点里各种文字信息的间距,如图标和文字的间距 | |
| selectTranslateStep | Number | 3 | 多选节点时鼠标移动到边缘时的画布移动偏移量 | |
| selectTranslateLimit | Number | 20 | 多选节点时鼠标移动距边缘多少距离时开始偏移 | |
| customNoteContentShowv0.1.6+ | Object | null | 自定义节点备注内容显示Object类型结构为{show: (noteContent, left, top) => {// 你的显示节点备注逻辑 }, hide: () => {// 你的隐藏节点备注逻辑 }} | |
| readonlyv0.1.7+ | Boolean | false | 是否是只读模式 | |
| enableFreeDragv0.2.4+ | Boolean | false | 是否开启节点自由拖拽 | |
| watermarkConfigv0.2.4+ | Object | | 水印配置,详细配置请参考下方表格【水印配置】 | |
| textAutoWrapWidthv0.3.4+ | Number | 500 | 节点内每行文本达到该宽度后自动换行 | |
### 水印配置
| 字段名称 | 类型 | 默认值 | 描述 |
| ----------- | ------ | ------------------------------------------- | ------------------------------------ |
| text | String | '' | 水印文字,如果为空字符串则不显示水印 |
| lineSpacing | Number | 100 | 水印每行之间的间距 |
| textSpacing | Number | 100 | 同一行水印之间的间距 |
| angle | Number | 30 | 水印的倾斜角度,范围:[0, 90] |
| textStyle | Object | {color: '#999', opacity: 0.5, fontSize: 14} | 水印文字样式 |
## 静态方法
### defineTheme(name, config)
> v0.2.23+
定义新主题。
`name`:新主题名称
`config`:主题数据
`simple-mind-map`内置了众多主题,另外你也可以注册新主题,建议在实例化之前进行注册,这样在实例化时可以直接使用新注册的主题,使用示例:
```js
import MindMap from 'simple-mind-map'
// 注册新主题
MindMap.defineTheme('主题名称', {})
// 1.实例化时使用新注册的主题
const mindMap = new MindMap({
theme: '主题名称'
})
// 2.动态切换新主题
mindMap.setTheme('主题名称')
```
主题的所有配置可以参考[默认主题](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js)。`defineTheme`方法会把你传入的配置和默认配置做合并。大部分主题其实需要自定义的部分不是很多,一个典型的自定义主题配置可以参考[blueSky](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/blueSky.js)。
### usePlugin(plugin)
> v0.3.0+
注册插件,如果需要使用非核心的一些功能,比如小地图、水印等,可以通过该方法进行注册。可链式调用。
注意:插件需要在实例化`MindMap`前注册。
## 静态属性
### pluginList
> v0.3.0+
当前注册的所有插件列表。
## 实例方法
### getSvgData()
> v0.3.0+
获取`svg`数据,返回一个对象,详细结构如下:
```js
{
svg, // Element思维导图图形的整体svg元素包括svg画布容器、g实际的思维导图组
svgHTML, // Stringsvg字符串即html字符串可以直接渲染到你准备的小地图容器内
rect: // Object思维导图图形未缩放时的位置尺寸等信息
origWidth, // Number画布宽度
origHeight, // Number画布高度
scaleX, // Number思维导图图形的水平缩放值
scaleY, // Number思维导图图形的垂直缩放值
}
```
### render(callback)
- `callback``v0.3.2+``Function`,当重新渲染完成时调用
触发整体渲染,会进行节点复用,性能较`reRender`会更好一点,如果只是节点位置变化了可以调用该方法进行渲染
### reRender(callback)
- `callback``v0.3.2+``Function`,当重新渲染完成时调用
整体重新渲染,会清空画布,节点也会重新创建,性能不好,慎重使用
### resize()
容器尺寸变化后,需要调用该方法进行适应
### setMode(mode)
> v0.1.7+
切换模式为只读或编辑。
`mode`readonly、edit
### on(event, fn)
监听事件,事件列表:
| 事件名称 | 描述 | 回调参数 |
| -------------------------------- | ------------------------------------------ | ------------------------------------------------------------ |
| data_change | 渲染树数据变化,可以监听该方法获取最新数据 | data当前渲染树数据 |
| view_data_changev0.1.1+ | 视图变化数据,比如拖动或缩放时会触发 | data当前视图状态数据 |
| back_forward | 前进或回退 | activeHistoryIndex当前在历史数据数组里的索引、length当前历史数据数组的长度 |
| draw_click | *画布的单击事件* | e事件对象 |
| svg_mousedown | svg画布的鼠标按下事件 | e事件对象 |
| mousedown | el元素的鼠标按下事件 | e事件对象、thisEvent事件类实例 |
| mousemove | el元素的鼠标移动事件 | e事件对象、thisEvent事件类实例 |
| drag | 如果是按住左键拖动的话会触发拖动事件 | e事件对象、thisEvent事件类实例 |
| mouseup | el元素的鼠标松开事件 | e事件对象、thisEvent事件类实例 |
| mousewheel | 鼠标滚动事件 | e事件对象、dir向上up还是向下down滚动、thisEvent事件类实例 |
| contextmenu | svg画布的鼠标右键菜单事件 | e事件对象 |
| node_click | 节点的单击事件 | this节点实例、e事件对象 |
| node_mousedown | 节点的鼠标按下事件 | this节点实例、e事件对象 |
| node_mouseup | 节点的鼠标松开事件 | this节点实例、e事件对象 |
| node_dblclick | 节点的双击事件 | this节点实例、e事件对象 |
| node_contextmenu | 节点的右键菜单事件 | e事件对象、this节点实例 |
| before_node_active | 节点激活前事件 | this节点实例、activeNodeList当前激活的所有节点列表 |
| node_active | 节点激活事件 | this节点实例、activeNodeList当前激活的所有节点列表 |
| expand_btn_click | 节点展开或收缩事件 | this节点实例 |
| before_show_text_edit | 节点文本编辑框即将打开事件 | |
| hide_text_edit | 节点文本编辑框关闭事件 | textEditNode文本编辑框DOM节点、activeNodeList当前激活的所有节点列表 |
| scale | 放大缩小事件 | scale缩放比例 |
| node_img_dblclickv0.2.15+ | 节点内图片的双击事件 | this节点实例、e事件对象 |
| node_tree_render_endv0.2.16+ | 节点树渲染完毕事件 | |
### emit(event, ...args)
触发事件,可以是上面表格里的事件,也可以是自定义事件
### off(event, fn)
解绑事件
### setTheme(theme)
切换主题,可选主题见上面的选项表格
### getTheme()
获取当前主题
### setThemeConfig(config)
设置主题配置,`config`同上面选项表格里的选项`themeConfig`
### getCustomThemeConfig()
获取自定义主题配置
### getThemeConfig(prop)
获取某个主题配置属性值
### getConfig(*prop*)
> 0.2.24+
`prop`:获取指定配置的值,不传则返回整个配置
获取配置,即`new MindMap(opt)``opt`
### updateConfig(*opt* = {})
> 0.2.24+
`opt`:要更新的配置
更新配置,即更新`new MindMap(opt)``opt`,可以只更新部分数据,比如:
```js
mindMap.updateConfig({
enableFreeDrag: true// 开启节点自由拖拽
})
```
该方法只做更新配置的事情,没有其他副作用,比如触发画布重新渲染之类的
### getLayout()
获取当前的布局结构
### setLayout(layout)
设置布局结构,可选值见上面选项表格的`layout`字段
### execCommand(name, ...args)
执行命令,每执行一个命令就会在历史堆栈里添加一条记录用于回退或前进。所有命令如下:
| 命令名称 | 描述 | 参数 |
| --------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| SELECT_ALL | 全选 | |
| BACK | 回退指定的步数 | step要回退的步数默认为1 |
| FORWARD | 前进指定的步数 | step要前进的步数默认为1 |
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | |
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | |
| UP_NODE | 上移节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的第一个节点使用无效 | |
| DOWN_NODE | 操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的最后一个节点使用无效 | |
| REMOVE_NODE | 删除节点,操作节点为当前激活的节点 | |
| PASTE_NODE | 粘贴节点到节点,操作节点为当前激活的节点 | data要粘贴的节点数据一般通过`renderer.copyNode()`方法和`renderer.cutNode()`方法获取) |
| CUT_NODE | 剪切节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点使用无效 | callback(回调函数,剪切的节点数据会通过调用该函数并通过参数返回) |
| SET_NODE_STYLE | 修改节点样式 | node要设置样式的节点、prop样式属性、value样式属性值、isActive布尔值是否设置的是激活状态的样式 |
| SET_NODE_ACTIVE | 设置节点是否激活 | node要设置的节点、active布尔值是否激活 |
| CLEAR_ACTIVE_NODE | 清除当前已激活节点的激活状态,操作节点为当前激活的节点 | |
| SET_NODE_EXPAND | 设置节点是否展开 | node要设置的节点、expand布尔值是否展开 |
| EXPAND_ALL | 展开所有节点 | |
| UNEXPAND_ALL | 收起所有节点 | |
| UNEXPAND_TO_LEVELv0.2.8+ | 展开到指定层级 | level要展开到的层级1、2、3... |
| SET_NODE_DATA | 更新节点数据,即更新节点数据对象里`data`对象的数据 | node要设置的节点、data对象要更新的数据`{expand: true}` |
| SET_NODE_TEXT | 设置节点文本 | node要设置的节点、text要设置的文本字符串换行可以使用`\n` |
| SET_NODE_IMAGE | 设置节点图片 | node要设置的节点、imgData对象图片信息结构为`{url, title, width, height}`,图片的宽高必须要传) |
| SET_NODE_ICON | 设置节点图标 | node要设置的节点、icons数组预定义的图片名称组成的数组可用图标可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件里的`nodeIconList`列表里获取到,图标名称为`type_name`,如`['priority_1']` |
| SET_NODE_HYPERLINK | 设置节点超链接 | node要设置的节点、link超链接地址、title超链接名称可选 |
| SET_NODE_NOTE | 设置节点备注 | node要设置的节点、note备注文字 |
| SET_NODE_TAG | 设置节点标签 | node要设置的节点、tag字符串数组内置颜色信息可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)里获取到) |
| INSERT_AFTERv0.1.5+ | 将节点移动到另一个节点的后面 | node要移动的节点、 exist目标节点 |
| INSERT_BEFOREv0.1.5+ | 将节点移动到另一个节点的前面 | node要移动的节点、 exist目标节点 |
| MOVE_NODE_TOv0.1.5+ | 移动一个节点作为另一个节点的子节点 | node要移动的节点、 toNode目标节点 |
| ADD_GENERALIZATIONv0.2.0+ | 添加节点概要 | data概要的数据对象格式节点的数字段都支持默认为{text: '概要'} |
| REMOVE_GENERALIZATIONv0.2.0+ | 删除节点概要 | |
| SET_NODE_CUSTOM_POSITIONv0.2.0+ | 设置节点自定义位置 | node要设置的节点、 left自定义的x坐标默认为undefined、 top自定义的y坐标默认为undefined |
| RESET_LAYOUTv0.2.0+ | 一键整理布局 | |
| SET_NODE_SHAPEv0.2.4+ | 设置节点形状 | node要设置的节点、shape形状全部形状https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js |
### setData(data)
动态设置思维导图数据,纯节点数据
`data`:思维导图结构数据
### setFullData(*data*)
> v0.2.7+
动态设置思维导图数据,包括节点数据、布局、主题、视图
`data`:完整数据,结构可参考[exportFullData](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exportFullData.json)
### getData(withConfig)
> v0.2.9+
获取思维导图数据
`withConfig``Boolean`,默认为`false`,即获取的数据只包括节点树,如果传`true`则会包含主题、布局、视图等数据
### export(type, isDownload, fileName)
> 需要先注册`Export`插件
导出
`type`要导出的类型可选值png、svg、json、pdfv0.2.1+、smm本质也是json
`isDownload`:是否需要直接触发下载,布尔值,默认为`false`
`fileName`v0.1.6+)导出文件的名称,默认为`思维导图`
### toPos(x, y)
> v0.1.5+
将浏览器可视窗口的坐标转换成相对于画布的坐标

View File

@@ -0,0 +1,644 @@
<template>
<div>
<h1>构造函数</h1>
<h2>基本使用</h2>
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">&quot;mindMapContainer&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;simple-mind-map&quot;</span>;
<span class="hljs-keyword">const</span> mindMap = <span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">el</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">&#x27;mindMapContainer&#x27;</span>),
<span class="hljs-attr">data</span>: {
<span class="hljs-string">&quot;data&quot;</span>: {
<span class="hljs-string">&quot;text&quot;</span>: <span class="hljs-string">&quot;根节点&quot;</span>
},
<span class="hljs-string">&quot;children&quot;</span>: []
}
});
</code></pre>
<h2>实例化选项</h2>
<table>
<thead>
<tr>
<th>字段名称</th>
<th>类型</th>
<th>默认值</th>
<th>描述</th>
<th>是否必填</th>
</tr>
</thead>
<tbody>
<tr>
<td>el</td>
<td>Element</td>
<td></td>
<td>容器元素必须为DOM元素</td>
<td></td>
</tr>
<tr>
<td>data</td>
<td>Object</td>
<td>{}</td>
<td>思维导图数据可参考<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js</a></td>
<td></td>
</tr>
<tr>
<td>layout</td>
<td>String</td>
<td>logicalStructure</td>
<td>布局类型可选列表logicalStructure逻辑结构图mindMap思维导图catalogOrganization目录组织图organizationStructure组织结构图</td>
<td></td>
</tr>
<tr>
<td>theme</td>
<td>String</td>
<td>default</td>
<td>主题可选列表default默认classic脑图经典minions小黄人pinkGrape粉红葡萄mint薄荷gold金色vipvitalityOrange活力橙greenLeaf绿叶dark2暗色2skyGreen天清绿classic2脑图经典2classic3脑图经典3classic4脑图经典4v0.2.0+classicGreen经典绿classicBlue经典蓝blueSky天空蓝brainImpairedPink脑残粉dark暗色earthYellow泥土黄freshGreen清新绿freshRed清新红romanticPurple浪漫紫</td>
<td></td>
</tr>
<tr>
<td>themeConfig</td>
<td>Object</td>
<td>{}</td>
<td>主题配置会和所选择的主题进行合并可用字段可参考<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js</a></td>
<td></td>
</tr>
<tr>
<td>scaleRatio</td>
<td>Number</td>
<td>0.1</td>
<td>放大缩小的增量比例</td>
<td></td>
</tr>
<tr>
<td>maxTag</td>
<td>Number</td>
<td>5</td>
<td>节点里最多显示的标签数量多余的会被丢弃</td>
<td></td>
</tr>
<tr>
<td>exportPadding</td>
<td>Number</td>
<td>20</td>
<td>导出图片时的内边距</td>
<td></td>
</tr>
<tr>
<td>imgTextMargin</td>
<td>Number</td>
<td>5</td>
<td>节点里图片和文字的间距</td>
<td></td>
</tr>
<tr>
<td>textContentMargin</td>
<td>Number</td>
<td>2</td>
<td>节点里各种文字信息的间距如图标和文字的间距</td>
<td></td>
</tr>
<tr>
<td>selectTranslateStep</td>
<td>Number</td>
<td>3</td>
<td>多选节点时鼠标移动到边缘时的画布移动偏移量</td>
<td></td>
</tr>
<tr>
<td>selectTranslateLimit</td>
<td>Number</td>
<td>20</td>
<td>多选节点时鼠标移动距边缘多少距离时开始偏移</td>
<td></td>
</tr>
<tr>
<td>customNoteContentShowv0.1.6+</td>
<td>Object</td>
<td>null</td>
<td>自定义节点备注内容显示Object类型结构为{show: (noteContent, left, top) =&gt; {// 你的显示节点备注逻辑 }, hide: () =&gt; {// 你的隐藏节点备注逻辑 }}</td>
<td></td>
</tr>
<tr>
<td>readonlyv0.1.7+</td>
<td>Boolean</td>
<td>false</td>
<td>是否是只读模式</td>
<td></td>
</tr>
<tr>
<td>enableFreeDragv0.2.4+</td>
<td>Boolean</td>
<td>false</td>
<td>是否开启节点自由拖拽</td>
<td></td>
</tr>
<tr>
<td>watermarkConfigv0.2.4+</td>
<td>Object</td>
<td></td>
<td>水印配置详细配置请参考下方表格水印配置</td>
<td></td>
</tr>
<tr>
<td>textAutoWrapWidthv0.3.4+</td>
<td>Number</td>
<td>500</td>
<td>节点内每行文本达到该宽度后自动换行</td>
<td></td>
</tr>
</tbody>
</table>
<h3>水印配置</h3>
<table>
<thead>
<tr>
<th>字段名称</th>
<th>类型</th>
<th>默认值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>text</td>
<td>String</td>
<td>''</td>
<td>水印文字如果为空字符串则不显示水印</td>
</tr>
<tr>
<td>lineSpacing</td>
<td>Number</td>
<td>100</td>
<td>水印每行之间的间距</td>
</tr>
<tr>
<td>textSpacing</td>
<td>Number</td>
<td>100</td>
<td>同一行水印之间的间距</td>
</tr>
<tr>
<td>angle</td>
<td>Number</td>
<td>30</td>
<td>水印的倾斜角度范围[0, 90]</td>
</tr>
<tr>
<td>textStyle</td>
<td>Object</td>
<td>{color: '#999', opacity: 0.5, fontSize: 14}</td>
<td>水印文字样式</td>
</tr>
</tbody>
</table>
<h2>静态方法</h2>
<h3>defineTheme(name, config)</h3>
<blockquote>
<p>v0.2.23+</p>
</blockquote>
<p>定义新主题</p>
<p><code>name</code>新主题名称</p>
<p><code>config</code>主题数据</p>
<p><code>simple-mind-map</code>内置了众多主题另外你也可以注册新主题建议在实例化之前进行注册这样在实例化时可以直接使用新注册的主题使用示例</p>
<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-comment">// 注册新主题</span>
MindMap.defineTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>, {})
<span class="hljs-comment">// 1.实例化时使用新注册的主题</span>
<span class="hljs-keyword">const</span> mindMap = <span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">theme</span>: <span class="hljs-string">&#x27;主题名称&#x27;</span>
})
<span class="hljs-comment">// 2.动态切换新主题</span>
mindMap.setTheme(<span class="hljs-string">&#x27;主题名称&#x27;</span>)
</code></pre>
<p>主题的所有配置可以参考<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js">默认主题</a><code>defineTheme</code>方法会把你传入的配置和默认配置做合并大部分主题其实需要自定义的部分不是很多一个典型的自定义主题配置可以参考<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/blueSky.js">blueSky</a></p>
<h3>usePlugin(plugin)</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p>注册插件如果需要使用非核心的一些功能比如小地图水印等可以通过该方法进行注册可链式调用</p>
<p>注意插件需要在实例化<code>MindMap</code>前注册</p>
<h2>静态属性</h2>
<h3>pluginList</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p>当前注册的所有插件列表</p>
<h2>实例方法</h2>
<h3>getSvgData()</h3>
<blockquote>
<p>v0.3.0+</p>
</blockquote>
<p>获取<code>svg</code>数据返回一个对象详细结构如下</p>
<pre class="hljs"><code>{
svg, <span class="hljs-comment">// Element思维导图图形的整体svg元素包括svg画布容器、g实际的思维导图组</span>
svgHTML, <span class="hljs-comment">// Stringsvg字符串即html字符串可以直接渲染到你准备的小地图容器内</span>
<span class="hljs-attr">rect</span>: <span class="hljs-comment">// Object思维导图图形未缩放时的位置尺寸等信息</span>
origWidth, <span class="hljs-comment">// Number画布宽度</span>
origHeight, <span class="hljs-comment">// Number画布高度</span>
scaleX, <span class="hljs-comment">// Number思维导图图形的水平缩放值</span>
scaleY, <span class="hljs-comment">// Number思维导图图形的垂直缩放值</span>
}
</code></pre>
<h3>render(callback)</h3>
<ul>
<li><code>callback</code><code>v0.3.2+</code><code>Function</code>当重新渲染完成时调用</li>
</ul>
<p>触发整体渲染会进行节点复用性能较<code>reRender</code>会更好一点如果只是节点位置变化了可以调用该方法进行渲染</p>
<h3>reRender(callback)</h3>
<ul>
<li><code>callback</code><code>v0.3.2+</code><code>Function</code>当重新渲染完成时调用</li>
</ul>
<p>整体重新渲染会清空画布节点也会重新创建性能不好慎重使用</p>
<h3>resize()</h3>
<p>容器尺寸变化后需要调用该方法进行适应</p>
<h3>setMode(mode)</h3>
<blockquote>
<p>v0.1.7+</p>
</blockquote>
<p>切换模式为只读或编辑</p>
<p><code>mode</code>readonlyedit</p>
<h3>on(event, fn)</h3>
<p>监听事件事件列表</p>
<table>
<thead>
<tr>
<th>事件名称</th>
<th>描述</th>
<th>回调参数</th>
</tr>
</thead>
<tbody>
<tr>
<td>data_change</td>
<td>渲染树数据变化可以监听该方法获取最新数据</td>
<td>data当前渲染树数据</td>
</tr>
<tr>
<td>view_data_changev0.1.1+</td>
<td>视图变化数据比如拖动或缩放时会触发</td>
<td>data当前视图状态数据</td>
</tr>
<tr>
<td>back_forward</td>
<td>前进或回退</td>
<td>activeHistoryIndex当前在历史数据数组里的索引length当前历史数据数组的长度</td>
</tr>
<tr>
<td>draw_click</td>
<td><em>画布的单击事件</em></td>
<td>e事件对象</td>
</tr>
<tr>
<td>svg_mousedown</td>
<td>svg画布的鼠标按下事件</td>
<td>e事件对象</td>
</tr>
<tr>
<td>mousedown</td>
<td>el元素的鼠标按下事件</td>
<td>e事件对象thisEvent事件类实例</td>
</tr>
<tr>
<td>mousemove</td>
<td>el元素的鼠标移动事件</td>
<td>e事件对象thisEvent事件类实例</td>
</tr>
<tr>
<td>drag</td>
<td>如果是按住左键拖动的话会触发拖动事件</td>
<td>e事件对象thisEvent事件类实例</td>
</tr>
<tr>
<td>mouseup</td>
<td>el元素的鼠标松开事件</td>
<td>e事件对象thisEvent事件类实例</td>
</tr>
<tr>
<td>mousewheel</td>
<td>鼠标滚动事件</td>
<td>e事件对象dir向上up还是向下down滚动thisEvent事件类实例</td>
</tr>
<tr>
<td>contextmenu</td>
<td>svg画布的鼠标右键菜单事件</td>
<td>e事件对象</td>
</tr>
<tr>
<td>node_click</td>
<td>节点的单击事件</td>
<td>this节点实例e事件对象</td>
</tr>
<tr>
<td>node_mousedown</td>
<td>节点的鼠标按下事件</td>
<td>this节点实例e事件对象</td>
</tr>
<tr>
<td>node_mouseup</td>
<td>节点的鼠标松开事件</td>
<td>this节点实例e事件对象</td>
</tr>
<tr>
<td>node_dblclick</td>
<td>节点的双击事件</td>
<td>this节点实例e事件对象</td>
</tr>
<tr>
<td>node_contextmenu</td>
<td>节点的右键菜单事件</td>
<td>e事件对象this节点实例</td>
</tr>
<tr>
<td>before_node_active</td>
<td>节点激活前事件</td>
<td>this节点实例activeNodeList当前激活的所有节点列表</td>
</tr>
<tr>
<td>node_active</td>
<td>节点激活事件</td>
<td>this节点实例activeNodeList当前激活的所有节点列表</td>
</tr>
<tr>
<td>expand_btn_click</td>
<td>节点展开或收缩事件</td>
<td>this节点实例</td>
</tr>
<tr>
<td>before_show_text_edit</td>
<td>节点文本编辑框即将打开事件</td>
<td></td>
</tr>
<tr>
<td>hide_text_edit</td>
<td>节点文本编辑框关闭事件</td>
<td>textEditNode文本编辑框DOM节点activeNodeList当前激活的所有节点列表</td>
</tr>
<tr>
<td>scale</td>
<td>放大缩小事件</td>
<td>scale缩放比例</td>
</tr>
<tr>
<td>node_img_dblclickv0.2.15+</td>
<td>节点内图片的双击事件</td>
<td>this节点实例e事件对象</td>
</tr>
<tr>
<td>node_tree_render_endv0.2.16+</td>
<td>节点树渲染完毕事件</td>
<td></td>
</tr>
</tbody>
</table>
<h3>emit(event, ...args)</h3>
<p>触发事件可以是上面表格里的事件也可以是自定义事件</p>
<h3>off(event, fn)</h3>
<p>解绑事件</p>
<h3>setTheme(theme)</h3>
<p>切换主题可选主题见上面的选项表格</p>
<h3>getTheme()</h3>
<p>获取当前主题</p>
<h3>setThemeConfig(config)</h3>
<p>设置主题配置<code>config</code>同上面选项表格里的选项<code>themeConfig</code></p>
<h3>getCustomThemeConfig()</h3>
<p>获取自定义主题配置</p>
<h3>getThemeConfig(prop)</h3>
<p>获取某个主题配置属性值</p>
<h3>getConfig(<em>prop</em>)</h3>
<blockquote>
<p>0.2.24+</p>
</blockquote>
<p><code>prop</code>获取指定配置的值不传则返回整个配置</p>
<p>获取配置<code>new MindMap(opt)</code><code>opt</code></p>
<h3>updateConfig(<em>opt</em> = {})</h3>
<blockquote>
<p>0.2.24+</p>
</blockquote>
<p><code>opt</code>要更新的配置</p>
<p>更新配置即更新<code>new MindMap(opt)</code><code>opt</code>可以只更新部分数据比如</p>
<pre class="hljs"><code>mindMap.updateConfig({
<span class="hljs-attr">enableFreeDrag</span>: <span class="hljs-literal">true</span><span class="hljs-comment">// 开启节点自由拖拽</span>
})
</code></pre>
<p>该方法只做更新配置的事情没有其他副作用比如触发画布重新渲染之类的</p>
<h3>getLayout()</h3>
<p>获取当前的布局结构</p>
<h3>setLayout(layout)</h3>
<p>设置布局结构可选值见上面选项表格的<code>layout</code>字段</p>
<h3>execCommand(name, ...args)</h3>
<p>执行命令每执行一个命令就会在历史堆栈里添加一条记录用于回退或前进所有命令如下</p>
<table>
<thead>
<tr>
<th>命令名称</th>
<th>描述</th>
<th>参数</th>
</tr>
</thead>
<tbody>
<tr>
<td>SELECT_ALL</td>
<td>全选</td>
<td></td>
</tr>
<tr>
<td>BACK</td>
<td>回退指定的步数</td>
<td>step要回退的步数默认为1</td>
</tr>
<tr>
<td>FORWARD</td>
<td>前进指定的步数</td>
<td>step要前进的步数默认为1</td>
</tr>
<tr>
<td>INSERT_NODE</td>
<td>插入同级节点操作节点为当前激活的节点如果有多个激活节点只会对第一个有效</td>
<td></td>
</tr>
<tr>
<td>INSERT_CHILD_NODE</td>
<td>插入子节点操作节点为当前激活的节点</td>
<td></td>
</tr>
<tr>
<td>UP_NODE</td>
<td>上移节点操作节点为当前激活的节点如果有多个激活节点只会对第一个有效对根节点或在列表里的第一个节点使用无效</td>
<td></td>
</tr>
<tr>
<td>DOWN_NODE</td>
<td>操作节点为当前激活的节点如果有多个激活节点只会对第一个有效对根节点或在列表里的最后一个节点使用无效</td>
<td></td>
</tr>
<tr>
<td>REMOVE_NODE</td>
<td>删除节点操作节点为当前激活的节点</td>
<td></td>
</tr>
<tr>
<td>PASTE_NODE</td>
<td>粘贴节点到节点操作节点为当前激活的节点</td>
<td>data要粘贴的节点数据一般通过<code>renderer.copyNode()</code>方法和<code>renderer.cutNode()</code>方法获取</td>
</tr>
<tr>
<td>CUT_NODE</td>
<td>剪切节点操作节点为当前激活的节点如果有多个激活节点只会对第一个有效对根节点使用无效</td>
<td>callback(回调函数剪切的节点数据会通过调用该函数并通过参数返回)</td>
</tr>
<tr>
<td>SET_NODE_STYLE</td>
<td>修改节点样式</td>
<td>node要设置样式的节点prop样式属性value样式属性值isActive布尔值是否设置的是激活状态的样式</td>
</tr>
<tr>
<td>SET_NODE_ACTIVE</td>
<td>设置节点是否激活</td>
<td>node要设置的节点active布尔值是否激活</td>
</tr>
<tr>
<td>CLEAR_ACTIVE_NODE</td>
<td>清除当前已激活节点的激活状态操作节点为当前激活的节点</td>
<td></td>
</tr>
<tr>
<td>SET_NODE_EXPAND</td>
<td>设置节点是否展开</td>
<td>node要设置的节点expand布尔值是否展开</td>
</tr>
<tr>
<td>EXPAND_ALL</td>
<td>展开所有节点</td>
<td></td>
</tr>
<tr>
<td>UNEXPAND_ALL</td>
<td>收起所有节点</td>
<td></td>
</tr>
<tr>
<td>UNEXPAND_TO_LEVELv0.2.8+</td>
<td>展开到指定层级</td>
<td>level要展开到的层级123...</td>
</tr>
<tr>
<td>SET_NODE_DATA</td>
<td>更新节点数据即更新节点数据对象里<code>data</code>对象的数据</td>
<td>node要设置的节点data对象要更新的数据<code>{expand: true}</code></td>
</tr>
<tr>
<td>SET_NODE_TEXT</td>
<td>设置节点文本</td>
<td>node要设置的节点text要设置的文本字符串换行可以使用<code>\n</code></td>
</tr>
<tr>
<td>SET_NODE_IMAGE</td>
<td>设置节点图片</td>
<td>node要设置的节点imgData对象图片信息结构为<code>{url, title, width, height}</code>图片的宽高必须要传</td>
</tr>
<tr>
<td>SET_NODE_ICON</td>
<td>设置节点图标</td>
<td>node要设置的节点icons数组预定义的图片名称组成的数组可用图标可在<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js</a>文件里的<code>nodeIconList</code>列表里获取到,图标名称为<code>type_name</code>,如<code>['priority_1']</code></td>
</tr>
<tr>
<td>SET_NODE_HYPERLINK</td>
<td>设置节点超链接</td>
<td>node要设置的节点link超链接地址title超链接名称可选</td>
</tr>
<tr>
<td>SET_NODE_NOTE</td>
<td>设置节点备注</td>
<td>node要设置的节点note备注文字</td>
</tr>
<tr>
<td>SET_NODE_TAG</td>
<td>设置节点标签</td>
<td>node要设置的节点tag字符串数组内置颜色信息可在<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js">https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js</a>里获取到)</td>
</tr>
<tr>
<td>INSERT_AFTERv0.1.5+</td>
<td>将节点移动到另一个节点的后面</td>
<td>node要移动的节点 exist目标节点</td>
</tr>
<tr>
<td>INSERT_BEFOREv0.1.5+</td>
<td>将节点移动到另一个节点的前面</td>
<td>node要移动的节点 exist目标节点</td>
</tr>
<tr>
<td>MOVE_NODE_TOv0.1.5+</td>
<td>移动一个节点作为另一个节点的子节点</td>
<td>node要移动的节点 toNode目标节点</td>
</tr>
<tr>
<td>ADD_GENERALIZATIONv0.2.0+</td>
<td>添加节点概要</td>
<td>data概要的数据对象格式节点的数字段都支持默认为{text: '概要'}</td>
</tr>
<tr>
<td>REMOVE_GENERALIZATIONv0.2.0+</td>
<td>删除节点概要</td>
<td></td>
</tr>
<tr>
<td>SET_NODE_CUSTOM_POSITIONv0.2.0+</td>
<td>设置节点自定义位置</td>
<td>node要设置的节点 left自定义的x坐标默认为undefined top自定义的y坐标默认为undefined</td>
</tr>
<tr>
<td>RESET_LAYOUTv0.2.0+</td>
<td>一键整理布局</td>
<td></td>
</tr>
<tr>
<td>SET_NODE_SHAPEv0.2.4+</td>
<td>设置节点形状</td>
<td>node要设置的节点shape形状全部形状https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js</td>
</tr>
</tbody>
</table>
<h3>setData(data)</h3>
<p>动态设置思维导图数据纯节点数据</p>
<p><code>data</code>思维导图结构数据</p>
<h3>setFullData(<em>data</em>)</h3>
<blockquote>
<p>v0.2.7+</p>
</blockquote>
<p>动态设置思维导图数据包括节点数据布局主题视图</p>
<p><code>data</code>完整数据结构可参考<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exportFullData.json">exportFullData</a></p>
<h3>getData(withConfig)</h3>
<blockquote>
<p>v0.2.9+</p>
</blockquote>
<p>获取思维导图数据</p>
<p><code>withConfig</code><code>Boolean</code>默认为<code>false</code>即获取的数据只包括节点树如果传<code>true</code>则会包含主题布局视图等数据</p>
<h3>export(type, isDownload, fileName)</h3>
<blockquote>
<p>需要先注册<code>Export</code>插件</p>
</blockquote>
<p>导出</p>
<p><code>type</code>要导出的类型可选值pngsvgjsonpdfv0.2.1+smm本质也是json</p>
<p><code>isDownload</code>是否需要直接触发下载布尔值默认为<code>false</code></p>
<p><code>fileName</code>v0.1.6+导出文件的名称默认为<code>思维导图</code></p>
<h3>toPos(x, y)</h3>
<blockquote>
<p>v0.1.5+</p>
</blockquote>
<p>将浏览器可视窗口的坐标转换成相对于画布的坐标</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,51 @@
# Export 插件
`Export`插件提供导出的功能。
## 注册
```js
import MindMap from 'simple-mind-map'
import Export from 'simple-mind-map/src/Export.js'
MindMap.usePlugin(Export)
```
注册完且实例化`MindMap`后可通过`mindMap.doExport`获取到该实例。
## 方法
### png()
导出为`png`,异步方法,返回图片数据,`data:url`数据,可以自行下载或显示
### svg()
导出为`svg`,异步方法,返回`svg`数据,`data:url`数据,可以自行下载或显示
### getSvgData()
获取`svg`数据,异步方法,返回一个对象:
```js
{
node// svg对象
str// svg字符串
}
```
### pdf(name)
> v0.2.1+
`name`:文件名称
导出为`pdf`
### json(name, withConfig)
`name`:暂时没有用处,传空字符串即可
`withConfig``Boolean`, 默认为`true`,数据中是否包含配置,否则为纯思维导图节点数据
返回`json`数据,`data:url`数据,可以自行下载

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