mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-20 15:38:27 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e337da088b | ||
|
|
de97ea9e75 | ||
|
|
cc331065eb | ||
|
|
9a8e630654 | ||
|
|
17ab977efb | ||
|
|
6bd10d9451 | ||
|
|
5313b9b69c | ||
|
|
8dcfdcc44a | ||
|
|
67422df3ff | ||
|
|
2ad7536eb7 | ||
|
|
6f56e5c4e6 | ||
|
|
5ae8ebe590 | ||
|
|
6ecb97e4e5 | ||
|
|
c8f938dd3e | ||
|
|
0d29e29162 | ||
|
|
be1b3dffce | ||
|
|
3ba2dbe415 | ||
|
|
19a96c92a9 | ||
|
|
c265e3e437 | ||
|
|
7434ac2648 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
dist_electron
|
||||
@@ -26,6 +26,7 @@ Demo:[https://wanglin2.github.io/mind-map/](https://wanglin2.github.io/mind-ma
|
||||
- [x] 支持多种节点形状
|
||||
- [x] 支持导出为`json`、`png`、`svg`、`pdf`,支持从`json`、`xmind`导入
|
||||
- [x] 支持小地图
|
||||
- [x] 支持关联线
|
||||
|
||||
# 安装
|
||||
|
||||
|
||||
@@ -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"><link rel="icon" href="./dist/logo.png"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d0a3179.05d77cdf.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.61258a02.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.040c6f5c.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.87f48f42.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.ae972b36.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.b094b87c.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.34207f33.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.ade5a7e0.js" rel="prefetch"><link href="dist/js/chunk-2d0ba309.c9a9ab22.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.db6065c6.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.1ffa155d.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.e44018ef.js" rel="prefetch"><link href="dist/js/chunk-2d0c14fc.17d4f60a.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.e63d640f.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.2803233f.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.77611624.js" rel="prefetch"><link href="dist/js/chunk-2d0c20be.57f5b62e.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.8f26961c.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.6c0d2c1e.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.8277bd5a.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.32d1bf7e.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.836132f8.js" rel="prefetch"><link href="dist/js/chunk-2d0ddf37.7b4a470a.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.00dad103.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.8750dc1f.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.4f35fbc4.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.a4640577.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.bd5197f5.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.586c30fb.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.c7c6517f.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.0e33676c.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.4cf834ac.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.e60ccf9b.js" rel="prefetch"><link href="dist/js/chunk-2d216004.fde7548f.js" rel="prefetch"><link href="dist/js/chunk-2d217907.3772894a.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.5947204c.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.0bdd83ab.js" rel="prefetch"><link href="dist/js/chunk-2d22bd06.1447b6d2.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.4fa18681.js" rel="prefetch"><link href="dist/js/chunk-2d238428.61fffbf5.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.8cc0c770.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.c097b26d.css" rel="preload" as="style"><link href="dist/js/app.d14e7a91.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.524ee6e1.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.c097b26d.css" rel="stylesheet"><link href="dist/css/app.8cc0c770.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.524ee6e1.js"></script><script src="dist/js/app.d14e7a91.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.05d77cdf.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.0b1c8d8d.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.040c6f5c.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.4d871bf6.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.ae972b36.js" rel="prefetch"><link href="dist/js/chunk-2d0b1c6f.7fcfc285.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.b094b87c.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.34207f33.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.ade5a7e0.js" rel="prefetch"><link href="dist/js/chunk-2d0ba309.c9a9ab22.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.db6065c6.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.1ffa155d.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.e44018ef.js" rel="prefetch"><link href="dist/js/chunk-2d0c14fc.17d4f60a.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.0921c70f.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.52b68dc1.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.7cb4182f.js" rel="prefetch"><link href="dist/js/chunk-2d0c20be.57f5b62e.js" rel="prefetch"><link href="dist/js/chunk-2d0d5cb9.c67e6ecf.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.cd8db1ee.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.6c0d2c1e.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.2fd89008.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.32d1bf7e.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.836132f8.js" rel="prefetch"><link href="dist/js/chunk-2d0ddf37.7b4a470a.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.00dad103.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.3c894463.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.95aec9d1.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.a4640577.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.bd5197f5.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.c3ec62ff.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.c7c6517f.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.af7fe897.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.7639ecc2.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.e60ccf9b.js" rel="prefetch"><link href="dist/js/chunk-2d216004.fde7548f.js" rel="prefetch"><link href="dist/js/chunk-2d217907.3772894a.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.5947204c.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.0bdd83ab.js" rel="prefetch"><link href="dist/js/chunk-2d22bd06.1447b6d2.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.4fa18681.js" rel="prefetch"><link href="dist/js/chunk-2d238428.61fffbf5.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.2f2d473c.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.c097b26d.css" rel="preload" as="style"><link href="dist/js/app.e8192153.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.f9723ab8.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.c097b26d.css" rel="stylesheet"><link href="dist/css/app.2f2d473c.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.f9723ab8.js"></script><script src="dist/js/app.e8192153.js"></script></body></html>
|
||||
BIN
qrcode.jpg
BIN
qrcode.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
18
simple-mind-map/package-lock.json
generated
18
simple-mind-map/package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "0.4.0",
|
||||
"version": "0.4.5",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.0.16",
|
||||
@@ -16,6 +16,7 @@
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"quill": "^1.3.6",
|
||||
"uuid": "^9.0.0",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1644,6 +1645,14 @@
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -2893,6 +2902,11 @@
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.4.3",
|
||||
"version": "0.4.6",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
@@ -32,6 +32,7 @@
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"quill": "^1.3.6",
|
||||
"uuid": "^9.0.0",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
627
simple-mind-map/src/AssociativeLine.js
Normal file
627
simple-mind-map/src/AssociativeLine.js
Normal file
@@ -0,0 +1,627 @@
|
||||
import { walk, bfsWalk, throttle } from './utils/'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import {
|
||||
getAssociativeLineTargetIndex,
|
||||
computeCubicBezierPathPoints,
|
||||
joinCubicBezierPath,
|
||||
cubicBezierPath,
|
||||
getNodePoint,
|
||||
computeNodePoints,
|
||||
getNodeLinePath,
|
||||
getDefaultControlPointOffsets
|
||||
} from './utils/associativeLineUtils'
|
||||
|
||||
// 关联线类
|
||||
class AssociativeLine {
|
||||
constructor(opt = {}) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.draw = this.mindMap.draw
|
||||
// 当前所有连接线
|
||||
this.lineList = []
|
||||
// 当前激活的连接线
|
||||
this.activeLine = null
|
||||
// 当前正在创建连接线
|
||||
this.isCreatingLine = false // 是否正在创建连接线中
|
||||
this.creatingStartNode = null // 起始节点
|
||||
this.creatingLine = null // 创建过程中的连接线
|
||||
this.overlapNode = null // 创建过程中的目标节点
|
||||
// 是否有节点正在被拖拽
|
||||
this.isNodeDragging = false
|
||||
// 箭头图标
|
||||
this.markerPath = null
|
||||
this.marker = this.createMarker()
|
||||
// 控制点
|
||||
this.controlLine1 = null
|
||||
this.controlLine2 = null
|
||||
this.controlPoint1 = null
|
||||
this.controlPoint2 = null
|
||||
this.controlPointDiameter = 10
|
||||
this.isControlPointMousedown = false
|
||||
this.mousedownControlPointKey = ''
|
||||
this.controlPointMousemoveState = {
|
||||
pos: null,
|
||||
startPoint: null,
|
||||
endPoint: null,
|
||||
targetIndex: ''
|
||||
}
|
||||
// 节流一下,不然很卡
|
||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 100, this)
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
bindEvent() {
|
||||
// 节点树渲染完毕后渲染连接线
|
||||
this.renderAllLines = this.renderAllLines.bind(this)
|
||||
this.mindMap.on('node_tree_render_end', this.renderAllLines)
|
||||
// 状态改变后重新渲染连接线
|
||||
this.mindMap.on('data_change', this.renderAllLines)
|
||||
// 监听画布和节点点击事件,用于清除当前激活的连接线
|
||||
this.mindMap.on('draw_click', () => {
|
||||
if (this.isControlPointMousedown) {
|
||||
return
|
||||
}
|
||||
this.clearActiveLine()
|
||||
})
|
||||
this.mindMap.on('node_click', node => {
|
||||
if (this.isCreatingLine) {
|
||||
this.completeCreateLine(node)
|
||||
} else {
|
||||
this.clearActiveLine()
|
||||
}
|
||||
})
|
||||
// 注册删除快捷键
|
||||
this.mindMap.keyCommand.addShortcut(
|
||||
'Del|Backspace',
|
||||
this.removeLine.bind(this)
|
||||
)
|
||||
// 注册添加连接线的命令
|
||||
this.mindMap.command.add('ADD_ASSOCIATIVE_LINE', this.addLine.bind(this))
|
||||
// 监听鼠标移动事件
|
||||
this.mindMap.on('mousemove', this.onMousemove.bind(this))
|
||||
// 节点拖拽事件
|
||||
this.mindMap.on('node_dragging', this.onNodeDragging.bind(this))
|
||||
this.mindMap.on('node_dragend', this.onNodeDragend.bind(this))
|
||||
// 拖拽控制点
|
||||
window.addEventListener('mousemove', e => {
|
||||
this.onControlPointMousemove(e)
|
||||
})
|
||||
window.addEventListener('mouseup', e => {
|
||||
this.onControlPointMouseup(e)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建箭头
|
||||
createMarker() {
|
||||
return this.draw.marker(20, 20, add => {
|
||||
add.ref(2, 5)
|
||||
add.size(10, 10)
|
||||
add.attr('orient', 'auto-start-reverse')
|
||||
this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z')
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染所有连线
|
||||
renderAllLines() {
|
||||
// 先移除
|
||||
this.removeAllLines()
|
||||
this.removeControls()
|
||||
this.clearActiveLine()
|
||||
let tree = this.mindMap.renderer.root
|
||||
if (!tree) return
|
||||
let idToNode = new Map()
|
||||
let nodeToIds = new Map()
|
||||
walk(
|
||||
tree,
|
||||
null,
|
||||
cur => {
|
||||
if (!cur) return
|
||||
let data = cur.nodeData.data
|
||||
if (
|
||||
data.associativeLineTargets &&
|
||||
data.associativeLineTargets.length > 0
|
||||
) {
|
||||
nodeToIds.set(cur, data.associativeLineTargets)
|
||||
}
|
||||
if (data.id) {
|
||||
idToNode.set(data.id, cur)
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
true,
|
||||
0
|
||||
)
|
||||
nodeToIds.forEach((ids, node) => {
|
||||
ids.forEach(id => {
|
||||
let toNode = idToNode.get(id)
|
||||
if (!node || !toNode) return
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
this.drawLine(startPoint, endPoint, node, toNode)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 绘制连接线
|
||||
drawLine(startPoint, endPoint, node, toNode) {
|
||||
let {
|
||||
associativeLineWidth,
|
||||
associativeLineColor,
|
||||
associativeLineActiveWidth,
|
||||
associativeLineActiveColor
|
||||
} = this.mindMap.themeConfig
|
||||
// 箭头
|
||||
this.markerPath
|
||||
.stroke({ color: associativeLineColor })
|
||||
.fill({ color: associativeLineColor })
|
||||
// 路径
|
||||
let { path: pathStr, controlPoints } = getNodeLinePath(
|
||||
startPoint,
|
||||
endPoint,
|
||||
node,
|
||||
toNode
|
||||
)
|
||||
// 虚线
|
||||
let path = this.draw.path()
|
||||
path
|
||||
.stroke({
|
||||
width: associativeLineWidth,
|
||||
color: associativeLineColor,
|
||||
dasharray: [6, 4]
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
path.plot(pathStr)
|
||||
path.marker('end', this.marker)
|
||||
// 不可见的点击线
|
||||
let clickPath = this.draw.path()
|
||||
clickPath
|
||||
.stroke({ width: associativeLineActiveWidth, color: 'transparent' })
|
||||
.fill({ color: 'none' })
|
||||
clickPath.plot(pathStr)
|
||||
// 点击事件
|
||||
clickPath.click(e => {
|
||||
e.stopPropagation()
|
||||
// 如果当前存在激活节点,那么取消激活节点
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.clearActiveNodes()
|
||||
} else {
|
||||
// 否则清除当前的关联线的激活状态,如果有的话
|
||||
this.clearActiveLine()
|
||||
// 保存当前激活的关联线信息
|
||||
this.activeLine = [path, clickPath, node, toNode]
|
||||
// 让不可见的点击线显示
|
||||
clickPath.stroke({ color: associativeLineActiveColor })
|
||||
// 渲染控制点和连线
|
||||
this.renderControls(
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints[0],
|
||||
controlPoints[1]
|
||||
)
|
||||
this.mindMap.emit(
|
||||
'associative_line_click',
|
||||
path,
|
||||
clickPath,
|
||||
node,
|
||||
toNode
|
||||
)
|
||||
}
|
||||
})
|
||||
this.lineList.push([path, clickPath, node, toNode])
|
||||
}
|
||||
|
||||
// 移除所有连接线
|
||||
removeAllLines() {
|
||||
this.lineList.forEach(line => {
|
||||
line[0].remove()
|
||||
line[1].remove()
|
||||
})
|
||||
this.lineList = []
|
||||
}
|
||||
|
||||
// 从当前激活节点开始创建连接线
|
||||
createLineFromActiveNode() {
|
||||
if (this.mindMap.renderer.activeNodeList.length <= 0) return
|
||||
let node = this.mindMap.renderer.activeNodeList[0]
|
||||
this.createLine(node)
|
||||
}
|
||||
|
||||
// 创建连接线
|
||||
createLine(fromNode) {
|
||||
let { associativeLineWidth, associativeLineColor } =
|
||||
this.mindMap.themeConfig
|
||||
if (this.isCreatingLine || !fromNode) return
|
||||
this.isCreatingLine = true
|
||||
this.creatingStartNode = fromNode
|
||||
this.creatingLine = this.draw.path()
|
||||
this.creatingLine
|
||||
.stroke({
|
||||
width: associativeLineWidth,
|
||||
color: associativeLineColor,
|
||||
dasharray: [6, 4]
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
this.creatingLine.marker('end', this.marker)
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
onMousemove(e) {
|
||||
if (!this.isCreatingLine) return
|
||||
this.updateCreatingLine(e)
|
||||
}
|
||||
|
||||
// 更新创建过程中的连接线
|
||||
updateCreatingLine(e) {
|
||||
let { x, y } = this.getTransformedEventPos(e)
|
||||
let startPoint = getNodePoint(this.creatingStartNode)
|
||||
let pathStr = cubicBezierPath(startPoint.x, startPoint.y, x, y)
|
||||
this.creatingLine.plot(pathStr)
|
||||
this.checkOverlapNode(x, y)
|
||||
}
|
||||
|
||||
// 获取转换后的鼠标事件对象的坐标
|
||||
getTransformedEventPos(e) {
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
let { scaleX, scaleY, translateX, translateY } =
|
||||
this.mindMap.draw.transform()
|
||||
return {
|
||||
x: (x - translateX) / scaleX,
|
||||
y: (y - translateY) / scaleY
|
||||
}
|
||||
}
|
||||
|
||||
// 检测当前移动到的目标节点
|
||||
checkOverlapNode(x, y) {
|
||||
this.overlapNode = null
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (node.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
}
|
||||
if (node === this.creatingStartNode || this.overlapNode) {
|
||||
return
|
||||
}
|
||||
let { left, top, width, height } = node
|
||||
let right = left + width
|
||||
let bottom = top + height
|
||||
if (x >= left && x <= right && y >= top && y <= bottom) {
|
||||
this.overlapNode = node
|
||||
}
|
||||
})
|
||||
if (this.overlapNode && !this.overlapNode.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
|
||||
}
|
||||
}
|
||||
|
||||
// 完成创建连接线
|
||||
completeCreateLine(node) {
|
||||
if (this.creatingStartNode === node) return
|
||||
this.addLine(this.creatingStartNode, node)
|
||||
if (this.overlapNode && this.overlapNode.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
|
||||
}
|
||||
this.isCreatingLine = false
|
||||
this.creatingStartNode = null
|
||||
this.creatingLine.remove()
|
||||
this.creatingLine = null
|
||||
this.overlapNode = null
|
||||
}
|
||||
|
||||
// 添加连接线
|
||||
addLine(fromNode, toNode) {
|
||||
if (!fromNode || !toNode) return
|
||||
// 目标节点如果没有id,则生成一个id
|
||||
let id = toNode.nodeData.data.id
|
||||
if (!id) {
|
||||
id = uuid()
|
||||
this.mindMap.execCommand('SET_NODE_DATA', toNode, {
|
||||
id
|
||||
})
|
||||
}
|
||||
// 将目标节点id保存起来
|
||||
let list = fromNode.nodeData.data.associativeLineTargets || []
|
||||
list.push(id)
|
||||
// 保存控制点
|
||||
let [startPoint, endPoint] = computeNodePoints(fromNode, toNode)
|
||||
let controlPoints = computeCubicBezierPathPoints(
|
||||
startPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.x,
|
||||
endPoint.y
|
||||
)
|
||||
let offsetList =
|
||||
fromNode.nodeData.data.associativeLineTargetControlOffsets || []
|
||||
// 保存的实际是控制点和端点的差值,否则当节点位置改变了,控制点还是原来的位置,连线就不对了
|
||||
offsetList[list.length - 1] = [
|
||||
{
|
||||
x: controlPoints[0].x - startPoint.x,
|
||||
y: controlPoints[0].y - startPoint.y
|
||||
},
|
||||
{
|
||||
x: controlPoints[1].x - endPoint.x,
|
||||
y: controlPoints[1].y - endPoint.y
|
||||
}
|
||||
]
|
||||
this.mindMap.execCommand('SET_NODE_DATA', fromNode, {
|
||||
associativeLineTargets: list,
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
})
|
||||
}
|
||||
|
||||
// 删除连接线
|
||||
removeLine() {
|
||||
if (!this.activeLine) return
|
||||
let [, , node, toNode] = this.activeLine
|
||||
this.removeControls()
|
||||
let { associativeLineTargets, associativeLineTargetControlOffsets } =
|
||||
node.nodeData.data
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineTargets: associativeLineTargets.filter((_, index) => {
|
||||
return index !== targetIndex
|
||||
}),
|
||||
associativeLineTargetControlOffsets: associativeLineTargetControlOffsets
|
||||
? associativeLineTargetControlOffsets.filter((_, index) => {
|
||||
return index !== targetIndex
|
||||
})
|
||||
: []
|
||||
})
|
||||
}
|
||||
|
||||
// 清除当前激活的节点
|
||||
clearActiveNodes() {
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
}
|
||||
|
||||
// 清除激活的线
|
||||
clearActiveLine() {
|
||||
if (this.activeLine) {
|
||||
this.activeLine[1].stroke({
|
||||
color: 'transparent'
|
||||
})
|
||||
this.activeLine = null
|
||||
this.removeControls()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理节点正在拖拽事件
|
||||
onNodeDragging() {
|
||||
if (this.isNodeDragging) return
|
||||
this.isNodeDragging = true
|
||||
this.lineList.forEach(line => {
|
||||
line[0].hide()
|
||||
line[1].hide()
|
||||
})
|
||||
this.hideControls()
|
||||
}
|
||||
|
||||
// 处理节点拖拽完成事件
|
||||
onNodeDragend() {
|
||||
if (!this.isNodeDragging) return
|
||||
this.lineList.forEach(line => {
|
||||
line[0].show()
|
||||
line[1].show()
|
||||
})
|
||||
this.showControls()
|
||||
this.isNodeDragging = false
|
||||
}
|
||||
|
||||
// 创建控制点、连线节点
|
||||
createControlNodes() {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
// 连线
|
||||
this.controlLine1 = this.draw
|
||||
.line()
|
||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||
this.controlLine2 = this.draw
|
||||
.line()
|
||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||
// 控制点
|
||||
this.controlPoint1 = this.createOneControlNode('controlPoint1')
|
||||
this.controlPoint2 = this.createOneControlNode('controlPoint2')
|
||||
}
|
||||
|
||||
// 创建控制点
|
||||
createOneControlNode(pointKey) {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
return this.draw
|
||||
.circle(this.controlPointDiameter)
|
||||
.stroke({ color: associativeLineActiveColor })
|
||||
.fill({ color: '#fff' })
|
||||
.click(e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
.mousedown(e => {
|
||||
this.onControlPointMousedown(e, pointKey)
|
||||
})
|
||||
}
|
||||
|
||||
// 控制点的鼠标按下事件
|
||||
onControlPointMousedown(e, pointKey) {
|
||||
e.stopPropagation()
|
||||
this.isControlPointMousedown = true
|
||||
this.mousedownControlPointKey = pointKey
|
||||
}
|
||||
|
||||
// 控制点的鼠标移动事件
|
||||
onControlPointMousemove(e) {
|
||||
if (
|
||||
!this.isControlPointMousedown ||
|
||||
!this.mousedownControlPointKey ||
|
||||
!this[this.mousedownControlPointKey]
|
||||
)
|
||||
return
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let radius = this.controlPointDiameter / 2
|
||||
// 转换鼠标当前的位置
|
||||
let { x, y } = this.getTransformedEventPos(e)
|
||||
this.controlPointMousemoveState.pos = {
|
||||
x,
|
||||
y
|
||||
}
|
||||
// 更新当前拖拽的控制点的位置
|
||||
this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
|
||||
let [path, clickPath, node, toNode] = this.activeLine
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
this.controlPointMousemoveState.startPoint = startPoint
|
||||
this.controlPointMousemoveState.endPoint = endPoint
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
this.controlPointMousemoveState.targetIndex = targetIndex
|
||||
let offsets = []
|
||||
let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsets = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||
} else {
|
||||
offsets = associativeLineTargetControlOffsets[targetIndex]
|
||||
}
|
||||
let point1 = null
|
||||
let point2 = null
|
||||
// 拖拽的是控制点1
|
||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||
point1 = {
|
||||
x,
|
||||
y
|
||||
}
|
||||
point2 = {
|
||||
x: endPoint.x + offsets[1].x,
|
||||
y: endPoint.y + offsets[1].y
|
||||
}
|
||||
// 更新控制点1的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
} else {
|
||||
// 拖拽的是控制点2
|
||||
point1 = {
|
||||
x: startPoint.x + offsets[0].x,
|
||||
y: startPoint.y + offsets[0].y
|
||||
}
|
||||
point2 = {
|
||||
x,
|
||||
y
|
||||
}
|
||||
// 更新控制点2的连线
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
}
|
||||
// 更新关联线
|
||||
let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||
path.plot(pathStr)
|
||||
clickPath.plot(pathStr)
|
||||
}
|
||||
|
||||
// 控制点的鼠标移动事件
|
||||
onControlPointMouseup(e) {
|
||||
if (!this.isControlPointMousedown) return
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let { pos, startPoint, endPoint, targetIndex } =
|
||||
this.controlPointMousemoveState
|
||||
let [, , node] = this.activeLine
|
||||
let offsetList = []
|
||||
let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsetList[targetIndex] = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||
} else {
|
||||
offsetList = associativeLineTargetControlOffsets
|
||||
}
|
||||
let offset1 = null
|
||||
let offset2 = null
|
||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||
// 更新控制点1数据
|
||||
offset1 = {
|
||||
x: pos.x - startPoint.x,
|
||||
y: pos.y - startPoint.y
|
||||
}
|
||||
offset2 = offsetList[targetIndex][1]
|
||||
} else {
|
||||
// 更新控制点2数据
|
||||
offset1 = offsetList[targetIndex][0]
|
||||
offset2 = {
|
||||
x: pos.x - endPoint.x,
|
||||
y: pos.y - endPoint.y
|
||||
}
|
||||
}
|
||||
offsetList[targetIndex] = [offset1, offset2]
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
})
|
||||
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
||||
setTimeout(() => {
|
||||
this.resetControlPoint()
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// 复位控制点移动
|
||||
resetControlPoint() {
|
||||
this.isControlPointMousedown = false
|
||||
this.mousedownControlPointKey = ''
|
||||
this.controlPointMousemoveState = {
|
||||
pos: null,
|
||||
startPoint: null,
|
||||
endPoint: null,
|
||||
targetIndex: ''
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染控制点
|
||||
renderControls(startPoint, endPoint, point1, point2) {
|
||||
if (!this.controlLine1) {
|
||||
this.createControlNodes()
|
||||
}
|
||||
let radius = this.controlPointDiameter / 2
|
||||
// 控制点和起终点的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
// 控制点
|
||||
this.controlPoint1.x(point1.x - radius).y(point1.y - radius)
|
||||
this.controlPoint2.x(point2.x - radius).y(point2.y - radius)
|
||||
}
|
||||
|
||||
// 删除控制点
|
||||
removeControls() {
|
||||
if (!this.controlLine1) return
|
||||
;[
|
||||
this.controlLine1,
|
||||
this.controlLine2,
|
||||
this.controlPoint1,
|
||||
this.controlPoint2
|
||||
].forEach(item => {
|
||||
item.remove()
|
||||
})
|
||||
this.controlLine1 = null
|
||||
this.controlLine2 = null
|
||||
this.controlPoint1 = null
|
||||
this.controlPoint2 = null
|
||||
}
|
||||
|
||||
// 隐藏控制点
|
||||
hideControls() {
|
||||
if (!this.controlLine1) return
|
||||
;[
|
||||
this.controlLine1,
|
||||
this.controlLine2,
|
||||
this.controlPoint1,
|
||||
this.controlPoint2
|
||||
].forEach(item => {
|
||||
item.hide()
|
||||
})
|
||||
}
|
||||
|
||||
// 显示控制点
|
||||
showControls() {
|
||||
if (!this.controlLine1) return
|
||||
;[
|
||||
this.controlLine1,
|
||||
this.controlLine2,
|
||||
this.controlPoint1,
|
||||
this.controlPoint2
|
||||
].forEach(item => {
|
||||
item.show()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
AssociativeLine.instanceName = 'associativeLine'
|
||||
|
||||
export default AssociativeLine
|
||||
@@ -76,6 +76,10 @@ class Command {
|
||||
return
|
||||
}
|
||||
let data = this.getCopyData()
|
||||
// 此次数据和上次一样则不重复添加
|
||||
if (this.history.length > 0 && JSON.stringify(this.history[this.history.length - 1]) === JSON.stringify(data)) {
|
||||
return
|
||||
}
|
||||
this.history.push(simpleDeepClone(data))
|
||||
this.activeHistoryIndex = this.history.length - 1
|
||||
this.mindMap.emit('data_change', data)
|
||||
|
||||
@@ -77,6 +77,7 @@ class Drag extends Base {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
this.mindMap.emit('node_dragging', this.node)
|
||||
e.preventDefault()
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseMoveX = x
|
||||
@@ -136,6 +137,7 @@ class Drag extends Base {
|
||||
this.mindMap.render()
|
||||
}
|
||||
this.reset()
|
||||
this.mindMap.emit('node_dragend')
|
||||
}
|
||||
|
||||
// 创建克隆节点
|
||||
|
||||
@@ -109,12 +109,13 @@ class Event extends EventEmitter {
|
||||
if (e.ctrlKey) {
|
||||
if (e.deltaY > 0) dir = 'up'
|
||||
if (e.deltaY < 0) dir = 'down'
|
||||
if (e.deltaX > 0) dir = 'left'
|
||||
if (e.deltaX < 0) dir = 'right'
|
||||
} else {
|
||||
if ((e.wheelDeltaY || e.detail) > 0) {
|
||||
dir = 'up'
|
||||
} else {
|
||||
dir = 'down'
|
||||
}
|
||||
if ((e.wheelDeltaY || e.detail) > 0) dir = 'up'
|
||||
if ((e.wheelDeltaY || e.detail) < 0) dir = 'down'
|
||||
if ((e.wheelDeltaX || e.detail) > 0) dir = 'left'
|
||||
if ((e.wheelDeltaX || e.detail) < 0) dir = 'right'
|
||||
}
|
||||
this.emit('mousewheel', e, dir, this)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { isKey } from './utils/keyMap'
|
||||
import { bfsWalk } from './utils'
|
||||
|
||||
// 键盘导航类
|
||||
@@ -8,22 +7,29 @@ class KeyboardNavigation {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
this.onKeyup = this.onKeyup.bind(this)
|
||||
this.mindMap.on('keyup', this.onKeyup)
|
||||
this.mindMap.keyCommand.addShortcut('Left', () => {
|
||||
this.onKeyup('Left')
|
||||
})
|
||||
this.mindMap.keyCommand.addShortcut('Up', () => {
|
||||
this.onKeyup('Up')
|
||||
})
|
||||
this.mindMap.keyCommand.addShortcut('Right', () => {
|
||||
this.onKeyup('Right')
|
||||
})
|
||||
this.mindMap.keyCommand.addShortcut('Down', () => {
|
||||
this.onKeyup('Down')
|
||||
})
|
||||
}
|
||||
|
||||
// 处理按键事件
|
||||
onKeyup(e) {
|
||||
;['Left', 'Up', 'Right', 'Down'].forEach(dir => {
|
||||
if (isKey(e, dir)) {
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.focus(dir)
|
||||
} else {
|
||||
let root = this.mindMap.renderer.root
|
||||
this.mindMap.renderer.moveNodeToCenter(root)
|
||||
root.active()
|
||||
}
|
||||
}
|
||||
})
|
||||
onKeyup(dir) {
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.focus(dir)
|
||||
} else {
|
||||
let root = this.mindMap.renderer.root
|
||||
this.mindMap.renderer.moveNodeToCenter(root)
|
||||
root.active()
|
||||
}
|
||||
}
|
||||
|
||||
// 聚焦到下一个节点
|
||||
|
||||
@@ -672,11 +672,26 @@ class Node {
|
||||
this.active(e)
|
||||
})
|
||||
this.group.on('mousedown', e => {
|
||||
e.stopPropagation()
|
||||
if (!this.isRoot) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
// 多选和取消多选
|
||||
if (e.ctrlKey) {
|
||||
let isActive = this.nodeData.data.isActive
|
||||
this.mindMap.renderer.setNodeActive(this, !isActive)
|
||||
this.mindMap.renderer[isActive ? 'removeActiveNode' : 'addActiveNode'](this)
|
||||
this.mindMap.emit(
|
||||
'node_active',
|
||||
isActive ? null : this,
|
||||
this.mindMap.renderer.activeNodeList
|
||||
)
|
||||
}
|
||||
this.mindMap.emit('node_mousedown', this, e)
|
||||
})
|
||||
this.group.on('mouseup', e => {
|
||||
e.stopPropagation()
|
||||
if (!this.isRoot) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
this.mindMap.emit('node_mouseup', this, e)
|
||||
})
|
||||
this.group.on('mouseenter', e => {
|
||||
@@ -695,7 +710,8 @@ class Node {
|
||||
})
|
||||
// 右键菜单事件
|
||||
this.group.on('contextmenu', e => {
|
||||
if (this.mindMap.opt.readonly || this.isGeneralization) {
|
||||
// 按住ctrl键点击鼠标左键不知为何触发的是contextmenu事件
|
||||
if (this.mindMap.opt.readonly || this.isGeneralization || e.ctrlKey) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
|
||||
@@ -355,7 +355,7 @@ class Render {
|
||||
|
||||
// 插入同级节点,多个节点只会操作第一个节点
|
||||
|
||||
insertNode() {
|
||||
insertNode(openEdit = true) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
@@ -369,7 +369,7 @@ class Render {
|
||||
}
|
||||
let index = this.getNodeIndex(first)
|
||||
first.parent.nodeData.children.splice(index + 1, 0, {
|
||||
inserting: true,
|
||||
inserting: openEdit,
|
||||
data: {
|
||||
text: text,
|
||||
expand: true
|
||||
@@ -382,7 +382,7 @@ class Render {
|
||||
|
||||
// 插入子节点
|
||||
|
||||
insertChildNode() {
|
||||
insertChildNode(openEdit = true) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
@@ -392,7 +392,7 @@ class Render {
|
||||
}
|
||||
let text = node.isRoot ? '二级节点' : '分支主题'
|
||||
node.nodeData.children.push({
|
||||
inserting: true,
|
||||
inserting: openEdit,
|
||||
data: {
|
||||
text: text,
|
||||
expand: true
|
||||
@@ -629,7 +629,7 @@ class Render {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
let copyData = copyNodeTree({}, node)
|
||||
let copyData = copyNodeTree({}, node, false, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
|
||||
@@ -59,20 +59,36 @@ class View {
|
||||
return this.mindMap.opt.customHandleMousewheel(e)
|
||||
}
|
||||
if (this.mindMap.opt.mousewheelAction === 'zoom') {
|
||||
// 放大
|
||||
if (dir === 'down') {
|
||||
this.enlarge()
|
||||
} else {
|
||||
// 缩小
|
||||
this.narrow()
|
||||
switch (dir) {
|
||||
// 鼠标滚轮,向上和向左,都是缩小
|
||||
case 'up':
|
||||
case 'left':
|
||||
this.narrow()
|
||||
break
|
||||
// 鼠标滚轮,向下和向右,都是放大
|
||||
case 'down':
|
||||
case 'right':
|
||||
this.enlarge()
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// 上移
|
||||
if (dir === 'down') {
|
||||
this.translateY(-this.mindMap.opt.mousewheelMoveStep)
|
||||
} else {
|
||||
switch (dir){
|
||||
// 上移
|
||||
case 'down':
|
||||
this.translateY(-this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
// 下移
|
||||
this.translateY(this.mindMap.opt.mousewheelMoveStep)
|
||||
case 'up':
|
||||
this.translateY(this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
// 右移
|
||||
case 'left':
|
||||
this.translateX(-this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
// 左移
|
||||
case 'right':
|
||||
this.translateX(this.mindMap.opt.mousewheelMoveStep)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,6 +26,14 @@ export default {
|
||||
generalizationLineMargin: 0,
|
||||
// 概要节点距节点的距离
|
||||
generalizationNodeMargin: 20,
|
||||
// 关联线默认状态的粗细
|
||||
associativeLineWidth: 2,
|
||||
// 关联线默认状态的颜色
|
||||
associativeLineColor: 'rgb(51, 51, 51)',
|
||||
// 关联线激活状态的粗细
|
||||
associativeLineActiveWidth: 8,
|
||||
// 关联线激活状态的颜色
|
||||
associativeLineActiveColor: 'rgba(2, 167, 240, 1)',
|
||||
// 背景颜色
|
||||
backgroundColor: '#fafafa',
|
||||
// 背景图片
|
||||
|
||||
182
simple-mind-map/src/utils/associativeLineUtils.js
Normal file
182
simple-mind-map/src/utils/associativeLineUtils.js
Normal file
@@ -0,0 +1,182 @@
|
||||
// 获取目标节点在起始节点的目标数组中的索引
|
||||
export const getAssociativeLineTargetIndex = (node, toNode) => {
|
||||
return node.nodeData.data.associativeLineTargets.findIndex(item => {
|
||||
return item === toNode.nodeData.data.id
|
||||
})
|
||||
}
|
||||
|
||||
// 计算贝塞尔曲线的控制点
|
||||
export const computeCubicBezierPathPoints = (x1, y1, x2, y2) => {
|
||||
let cx1 = x1 + (x2 - x1) / 2
|
||||
let cy1 = y1
|
||||
let cx2 = cx1
|
||||
let cy2 = y2
|
||||
if (Math.abs(x1 - x2) <= 5) {
|
||||
cx1 = x1 + (y2 - y1) / 2
|
||||
cx2 = cx1
|
||||
}
|
||||
return [
|
||||
{
|
||||
x: cx1,
|
||||
y: cy1
|
||||
},
|
||||
{
|
||||
x: cx2,
|
||||
y: cy2
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 拼接贝塞尔曲线路径
|
||||
export const joinCubicBezierPath = (startPoint, endPoint, point1, point2) => {
|
||||
return `M ${startPoint.x},${startPoint.y} C ${point1.x},${point1.y} ${point2.x},${point2.y} ${endPoint.x},${endPoint.y}`
|
||||
}
|
||||
|
||||
// 获取节点的位置信息
|
||||
const getNodeRect = node => {
|
||||
let { left, top, width, height } = node
|
||||
return {
|
||||
right: left + width,
|
||||
bottom: top + height,
|
||||
left,
|
||||
top
|
||||
}
|
||||
}
|
||||
|
||||
// 三次贝塞尔曲线
|
||||
export const cubicBezierPath = (x1, y1, x2, y2) => {
|
||||
let points = computeCubicBezierPathPoints(x1, y1, x2, y2)
|
||||
return joinCubicBezierPath(
|
||||
{ x: x1, y: y1 },
|
||||
{ x: x2, y: y2 },
|
||||
points[0],
|
||||
points[1]
|
||||
)
|
||||
}
|
||||
|
||||
// 获取节点的连接点
|
||||
export const getNodePoint = (node, dir = 'right') => {
|
||||
let { left, top, width, height } = node
|
||||
switch (dir) {
|
||||
case 'left':
|
||||
return {
|
||||
x: left,
|
||||
y: top + height / 2
|
||||
}
|
||||
case 'right':
|
||||
return {
|
||||
x: left + width,
|
||||
y: top + height / 2
|
||||
}
|
||||
case 'top':
|
||||
return {
|
||||
x: left + width / 2,
|
||||
y: top
|
||||
}
|
||||
case 'bottom':
|
||||
return {
|
||||
x: left + width / 2,
|
||||
y: top + height
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 根据两个节点的位置计算节点的连接点
|
||||
export const computeNodePoints = (fromNode, toNode) => {
|
||||
let fromRect = getNodeRect(fromNode)
|
||||
let fromCx = (fromRect.right + fromRect.left) / 2
|
||||
let fromCy = (fromRect.bottom + fromRect.top) / 2
|
||||
let toRect = getNodeRect(toNode)
|
||||
let toCx = (toRect.right + toRect.left) / 2
|
||||
let toCy = (toRect.bottom + toRect.top) / 2
|
||||
// 中心点坐标的差值
|
||||
let offsetX = toCx - fromCx
|
||||
let offsetY = toCy - fromCy
|
||||
if (offsetX === 0 && offsetY === 0) return
|
||||
let fromDir = ''
|
||||
let toDir = ''
|
||||
if (offsetX <= 0 && offsetX <= offsetY && offsetX <= -offsetY) {
|
||||
// left
|
||||
fromDir = 'left'
|
||||
toDir = 'right'
|
||||
} else if (offsetX > 0 && offsetX >= -offsetY && offsetX >= offsetY) {
|
||||
// right
|
||||
fromDir = 'right'
|
||||
toDir = 'left'
|
||||
} else if (offsetY <= 0 && offsetY < offsetX && offsetY < -offsetX) {
|
||||
// up
|
||||
fromDir = 'top'
|
||||
toDir = 'bottom'
|
||||
} else if (offsetY > 0 && -offsetY < offsetX && offsetY > offsetX) {
|
||||
// down
|
||||
fromDir = 'bottom'
|
||||
toDir = 'top'
|
||||
}
|
||||
return [getNodePoint(fromNode, fromDir), getNodePoint(toNode, toDir)]
|
||||
}
|
||||
|
||||
// 获取节点的关联线路径
|
||||
export const getNodeLinePath = (startPoint, endPoint, node, toNode) => {
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
// 控制点
|
||||
let controlPoints = []
|
||||
let associativeLineTargetControlOffsets =
|
||||
node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (
|
||||
associativeLineTargetControlOffsets &&
|
||||
associativeLineTargetControlOffsets[targetIndex]
|
||||
) {
|
||||
// 节点保存了控制点差值
|
||||
let offsets = associativeLineTargetControlOffsets[targetIndex]
|
||||
controlPoints = [
|
||||
{
|
||||
x: startPoint.x + offsets[0].x,
|
||||
y: startPoint.y + offsets[0].y
|
||||
},
|
||||
{
|
||||
x: endPoint.x + offsets[1].x,
|
||||
y: endPoint.y + offsets[1].y
|
||||
}
|
||||
]
|
||||
} else {
|
||||
// 没有保存控制点则生成默认的
|
||||
controlPoints = computeCubicBezierPathPoints(
|
||||
startPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.x,
|
||||
endPoint.y
|
||||
)
|
||||
}
|
||||
// 根据控制点拼接贝塞尔曲线路径
|
||||
return {
|
||||
path: joinCubicBezierPath(
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints[0],
|
||||
controlPoints[1]
|
||||
),
|
||||
controlPoints
|
||||
}
|
||||
}
|
||||
|
||||
// 获取默认的控制点差值
|
||||
export const getDefaultControlPointOffsets = (startPoint, endPoint) => {
|
||||
let controlPoints = computeCubicBezierPathPoints(
|
||||
startPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.x,
|
||||
endPoint.y
|
||||
)
|
||||
return [
|
||||
{
|
||||
x: controlPoints[0].x - startPoint.x,
|
||||
y: controlPoints[0].y - startPoint.y
|
||||
},
|
||||
{
|
||||
x: controlPoints[1].x - endPoint.x,
|
||||
y: controlPoints[1].y - endPoint.y
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -134,8 +134,10 @@ export const copyRenderTree = (tree, root) => {
|
||||
}
|
||||
|
||||
// 复制节点树数据
|
||||
export const copyNodeTree = (tree, root, removeActiveState = false) => {
|
||||
export const copyNodeTree = (tree, root, removeActiveState = false, keepId = false) => {
|
||||
tree.data = simpleDeepClone(root.nodeData ? root.nodeData.data : root.data)
|
||||
// 去除节点id,因为节点id不能重复
|
||||
if (tree.data.id && !keepId) delete tree.data.id
|
||||
if (removeActiveState) {
|
||||
tree.data.isActive = false
|
||||
}
|
||||
@@ -193,12 +195,12 @@ export const downloadFile = (file, fileName) => {
|
||||
// 节流函数
|
||||
export const throttle = (fn, time = 300, ctx) => {
|
||||
let timer = null
|
||||
return () => {
|
||||
return (...args) => {
|
||||
if (timer) {
|
||||
return
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
fn.call(ctx)
|
||||
fn.call(ctx, ...args)
|
||||
timer = null
|
||||
}, time)
|
||||
}
|
||||
|
||||
@@ -1,539 +0,0 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2479351 */
|
||||
src: url('iconfont.woff2?t=1677478278322') format('woff2'),
|
||||
url('iconfont.woff?t=1677478278322') format('woff'),
|
||||
url('iconfont.ttf?t=1677478278322') format('truetype');
|
||||
src: url('iconfont.woff2?t=1678955183884') format('woff2'),
|
||||
url('iconfont.woff?t=1678955183884') format('woff'),
|
||||
url('iconfont.ttf?t=1678955183884') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,54 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconlianjiexian:before {
|
||||
content: "\e75b";
|
||||
}
|
||||
|
||||
.iconbangzhu:before {
|
||||
content: "\e620";
|
||||
}
|
||||
|
||||
.iconshezhi:before {
|
||||
content: "\e8b7";
|
||||
}
|
||||
|
||||
.iconwushuju:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.iconzuijinliulan:before {
|
||||
content: "\e62f";
|
||||
}
|
||||
|
||||
.icon3zuidahua-3:before {
|
||||
content: "\e692";
|
||||
}
|
||||
|
||||
.iconzuixiaohua:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.iconzuidahua:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.iconguanbi:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.icondiannao:before {
|
||||
content: "\eac0";
|
||||
}
|
||||
|
||||
.iconzhuye:before {
|
||||
content: "\e65c";
|
||||
}
|
||||
|
||||
.iconbendi1x:before {
|
||||
content: "\e606";
|
||||
}
|
||||
|
||||
.iconbeijingyanse:before {
|
||||
content: "\e6f8";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,394 +0,0 @@
|
||||
{
|
||||
"id": "2479351",
|
||||
"name": "思绪",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon",
|
||||
"description": "思维导图",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "1790495",
|
||||
"name": "背景颜色",
|
||||
"font_class": "beijingyanse",
|
||||
"unicode": "e6f8",
|
||||
"unicode_decimal": 59128
|
||||
},
|
||||
{
|
||||
"icon_id": "11321310",
|
||||
"name": "清除",
|
||||
"font_class": "qingchu",
|
||||
"unicode": "e605",
|
||||
"unicode_decimal": 58885
|
||||
},
|
||||
{
|
||||
"icon_id": "586787",
|
||||
"name": "case",
|
||||
"font_class": "case",
|
||||
"unicode": "e6c6",
|
||||
"unicode_decimal": 59078
|
||||
},
|
||||
{
|
||||
"icon_id": "4354254",
|
||||
"name": "形状-文字",
|
||||
"font_class": "xingzhuang-wenzi",
|
||||
"unicode": "eb99",
|
||||
"unicode_decimal": 60313
|
||||
},
|
||||
{
|
||||
"icon_id": "6337466",
|
||||
"name": "字体加粗",
|
||||
"font_class": "zitijiacu",
|
||||
"unicode": "ec83",
|
||||
"unicode_decimal": 60547
|
||||
},
|
||||
{
|
||||
"icon_id": "6337470",
|
||||
"name": "字体下划线",
|
||||
"font_class": "zitixiahuaxian",
|
||||
"unicode": "ec85",
|
||||
"unicode_decimal": 60549
|
||||
},
|
||||
{
|
||||
"icon_id": "6337471",
|
||||
"name": "字体斜体",
|
||||
"font_class": "zitixieti",
|
||||
"unicode": "ec86",
|
||||
"unicode_decimal": 60550
|
||||
},
|
||||
{
|
||||
"icon_id": "11975179",
|
||||
"name": "删除线",
|
||||
"font_class": "shanchuxian",
|
||||
"unicode": "e612",
|
||||
"unicode_decimal": 58898
|
||||
},
|
||||
{
|
||||
"icon_id": "34198316",
|
||||
"name": "字体颜色",
|
||||
"font_class": "zitiyanse",
|
||||
"unicode": "e854",
|
||||
"unicode_decimal": 59476
|
||||
},
|
||||
{
|
||||
"icon_id": "8760187",
|
||||
"name": "github",
|
||||
"font_class": "github",
|
||||
"unicode": "e64f",
|
||||
"unicode_decimal": 58959
|
||||
},
|
||||
{
|
||||
"icon_id": "1009019",
|
||||
"name": "选择",
|
||||
"font_class": "choose1",
|
||||
"unicode": "e6c5",
|
||||
"unicode_decimal": 59077
|
||||
},
|
||||
{
|
||||
"icon_id": "493507",
|
||||
"name": "主题",
|
||||
"font_class": "zhuti",
|
||||
"unicode": "e7aa",
|
||||
"unicode_decimal": 59306
|
||||
},
|
||||
{
|
||||
"icon_id": "1305460",
|
||||
"name": "导出",
|
||||
"font_class": "daochu1",
|
||||
"unicode": "e63e",
|
||||
"unicode_decimal": 58942
|
||||
},
|
||||
{
|
||||
"icon_id": "4784101",
|
||||
"name": "另存为",
|
||||
"font_class": "lingcunwei",
|
||||
"unicode": "e657",
|
||||
"unicode_decimal": 58967
|
||||
},
|
||||
{
|
||||
"icon_id": "9929033",
|
||||
"name": "export",
|
||||
"font_class": "export",
|
||||
"unicode": "e642",
|
||||
"unicode_decimal": 58946
|
||||
},
|
||||
{
|
||||
"icon_id": "4570294",
|
||||
"name": "打开",
|
||||
"font_class": "dakai",
|
||||
"unicode": "ebdf",
|
||||
"unicode_decimal": 60383
|
||||
},
|
||||
{
|
||||
"icon_id": "5086088",
|
||||
"name": "新建",
|
||||
"font_class": "xinjian",
|
||||
"unicode": "e64e",
|
||||
"unicode_decimal": 58958
|
||||
},
|
||||
{
|
||||
"icon_id": "1117",
|
||||
"name": "剪切",
|
||||
"font_class": "jianqie",
|
||||
"unicode": "e601",
|
||||
"unicode_decimal": 58881
|
||||
},
|
||||
{
|
||||
"icon_id": "1415523",
|
||||
"name": "整理",
|
||||
"font_class": "zhengli",
|
||||
"unicode": "e83b",
|
||||
"unicode_decimal": 59451
|
||||
},
|
||||
{
|
||||
"icon_id": "2815710",
|
||||
"name": "复制",
|
||||
"font_class": "fuzhi",
|
||||
"unicode": "e604",
|
||||
"unicode_decimal": 58884
|
||||
},
|
||||
{
|
||||
"icon_id": "11121506",
|
||||
"name": "粘贴",
|
||||
"font_class": "niantie",
|
||||
"unicode": "e63f",
|
||||
"unicode_decimal": 58943
|
||||
},
|
||||
{
|
||||
"icon_id": "11383392",
|
||||
"name": "上移",
|
||||
"font_class": "shangyi",
|
||||
"unicode": "e6be",
|
||||
"unicode_decimal": 59070
|
||||
},
|
||||
{
|
||||
"icon_id": "11383396",
|
||||
"name": "下移",
|
||||
"font_class": "xiayi",
|
||||
"unicode": "e6bf",
|
||||
"unicode_decimal": 59071
|
||||
},
|
||||
{
|
||||
"icon_id": "14843439",
|
||||
"name": "概括总览",
|
||||
"font_class": "gaikuozonglan",
|
||||
"unicode": "e609",
|
||||
"unicode_decimal": 58889
|
||||
},
|
||||
{
|
||||
"icon_id": "19738998",
|
||||
"name": "全选",
|
||||
"font_class": "quanxuan",
|
||||
"unicode": "f199",
|
||||
"unicode_decimal": 61849
|
||||
},
|
||||
{
|
||||
"icon_id": "17606306",
|
||||
"name": "导入",
|
||||
"font_class": "daoru",
|
||||
"unicode": "e6a3",
|
||||
"unicode_decimal": 59043
|
||||
},
|
||||
{
|
||||
"icon_id": "5110748",
|
||||
"name": "后退-实",
|
||||
"font_class": "houtui-shi",
|
||||
"unicode": "e656",
|
||||
"unicode_decimal": 58966
|
||||
},
|
||||
{
|
||||
"icon_id": "14420971",
|
||||
"name": "前进",
|
||||
"font_class": "qianjin1",
|
||||
"unicode": "e654",
|
||||
"unicode_decimal": 58964
|
||||
},
|
||||
{
|
||||
"icon_id": "1368553",
|
||||
"name": "撤回",
|
||||
"font_class": "withdraw",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "15006636",
|
||||
"name": "前进",
|
||||
"font_class": "qianjin",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "19980541",
|
||||
"name": "恢复默认",
|
||||
"font_class": "huifumoren",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "1616783",
|
||||
"name": "换行",
|
||||
"font_class": "huanhang",
|
||||
"unicode": "e61e",
|
||||
"unicode_decimal": 58910
|
||||
},
|
||||
{
|
||||
"icon_id": "4777227",
|
||||
"name": "缩小",
|
||||
"font_class": "suoxiao",
|
||||
"unicode": "ec13",
|
||||
"unicode_decimal": 60435
|
||||
},
|
||||
{
|
||||
"icon_id": "18811980",
|
||||
"name": "编辑",
|
||||
"font_class": "bianji",
|
||||
"unicode": "e626",
|
||||
"unicode_decimal": 58918
|
||||
},
|
||||
{
|
||||
"icon_id": "21188137",
|
||||
"name": "放大",
|
||||
"font_class": "fangda",
|
||||
"unicode": "e663",
|
||||
"unicode_decimal": 58979
|
||||
},
|
||||
{
|
||||
"icon_id": "21189639",
|
||||
"name": "全屏",
|
||||
"font_class": "quanping1",
|
||||
"unicode": "e664",
|
||||
"unicode_decimal": 58980
|
||||
},
|
||||
{
|
||||
"icon_id": "397753",
|
||||
"name": "定位",
|
||||
"font_class": "dingwei",
|
||||
"unicode": "e616",
|
||||
"unicode_decimal": 58902
|
||||
},
|
||||
{
|
||||
"icon_id": "2605158",
|
||||
"name": "导航",
|
||||
"font_class": "daohang",
|
||||
"unicode": "e611",
|
||||
"unicode_decimal": 58897
|
||||
},
|
||||
{
|
||||
"icon_id": "6528451",
|
||||
"name": "键盘",
|
||||
"font_class": "jianpan",
|
||||
"unicode": "e64d",
|
||||
"unicode_decimal": 58957
|
||||
},
|
||||
{
|
||||
"icon_id": "7556170",
|
||||
"name": "全屏",
|
||||
"font_class": "quanping",
|
||||
"unicode": "e602",
|
||||
"unicode_decimal": 58882
|
||||
},
|
||||
{
|
||||
"icon_id": "788015",
|
||||
"name": "导出",
|
||||
"font_class": "daochu",
|
||||
"unicode": "e63d",
|
||||
"unicode_decimal": 58941
|
||||
},
|
||||
{
|
||||
"icon_id": "2678575",
|
||||
"name": "标签",
|
||||
"font_class": "biaoqian",
|
||||
"unicode": "e63c",
|
||||
"unicode_decimal": 58940
|
||||
},
|
||||
{
|
||||
"icon_id": "6265396",
|
||||
"name": "流程-备注",
|
||||
"font_class": "flow-Mark",
|
||||
"unicode": "e65b",
|
||||
"unicode_decimal": 58971
|
||||
},
|
||||
{
|
||||
"icon_id": "1790486",
|
||||
"name": "超链接",
|
||||
"font_class": "chaolianjie",
|
||||
"unicode": "e6f4",
|
||||
"unicode_decimal": 59124
|
||||
},
|
||||
{
|
||||
"icon_id": "4608986",
|
||||
"name": "主题",
|
||||
"font_class": "jingzi",
|
||||
"unicode": "e610",
|
||||
"unicode_decimal": 58896
|
||||
},
|
||||
{
|
||||
"icon_id": "11903017",
|
||||
"name": "笑脸",
|
||||
"font_class": "xiaolian",
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "19657962",
|
||||
"name": "图 片",
|
||||
"font_class": "image",
|
||||
"unicode": "e629",
|
||||
"unicode_decimal": 58921
|
||||
},
|
||||
{
|
||||
"icon_id": "20784489",
|
||||
"name": "结构",
|
||||
"font_class": "jiegou",
|
||||
"unicode": "e61d",
|
||||
"unicode_decimal": 58909
|
||||
},
|
||||
{
|
||||
"icon_id": "15969341",
|
||||
"name": "样式",
|
||||
"font_class": "yangshi",
|
||||
"unicode": "e631",
|
||||
"unicode_decimal": 58929
|
||||
},
|
||||
{
|
||||
"icon_id": "2967176",
|
||||
"name": "符号-大纲树",
|
||||
"font_class": "fuhao-dagangshu",
|
||||
"unicode": "e71f",
|
||||
"unicode_decimal": 59167
|
||||
},
|
||||
{
|
||||
"icon_id": "12316668",
|
||||
"name": "添加子节点",
|
||||
"font_class": "tianjiazijiedian",
|
||||
"unicode": "e622",
|
||||
"unicode_decimal": 58914
|
||||
},
|
||||
{
|
||||
"icon_id": "14435368",
|
||||
"name": "节点",
|
||||
"font_class": "jiedian",
|
||||
"unicode": "e655",
|
||||
"unicode_decimal": 58965
|
||||
},
|
||||
{
|
||||
"icon_id": "15765352",
|
||||
"name": "删 除",
|
||||
"font_class": "shanchu",
|
||||
"unicode": "e696",
|
||||
"unicode_decimal": 59030
|
||||
},
|
||||
{
|
||||
"icon_id": "9592600",
|
||||
"name": "HTSCIT_展开",
|
||||
"font_class": "zhankai",
|
||||
"unicode": "e64c",
|
||||
"unicode_decimal": 58956
|
||||
},
|
||||
{
|
||||
"icon_id": "9900009",
|
||||
"name": "HTSCIT_展开2",
|
||||
"font_class": "zhankai1",
|
||||
"unicode": "e673",
|
||||
"unicode_decimal": 58995
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -38,7 +38,12 @@ export default {
|
||||
isEnableNodeRichText: 'Enable node rich text editing',
|
||||
mousewheelAction: 'Mouse wheel behavior',
|
||||
zoomView: 'Zoom view',
|
||||
moveViewUpDown: 'Move view up and down'
|
||||
moveViewUpDown: 'Move view up and down',
|
||||
associativeLine: 'Associative line',
|
||||
associativeLineWidth: 'Width',
|
||||
associativeLineColor: 'Color',
|
||||
associativeLineActiveWidth: 'Active width',
|
||||
associativeLineActiveColor: 'Active color'
|
||||
},
|
||||
color: {
|
||||
moreColor: 'More color'
|
||||
@@ -187,7 +192,8 @@ export default {
|
||||
saveAs: 'Save as',
|
||||
import: 'Import',
|
||||
export: 'Export',
|
||||
shortcutKey: 'Shortcut key'
|
||||
shortcutKey: 'Shortcut key',
|
||||
associativeLine: 'Associative line',
|
||||
},
|
||||
edit: {
|
||||
newFeatureNoticeTitle: 'New feature reminder',
|
||||
|
||||
@@ -38,7 +38,12 @@ export default {
|
||||
isEnableNodeRichText: '是否开启节点富文本编辑',
|
||||
mousewheelAction: '鼠标滚轮行为',
|
||||
zoomView: '缩放视图',
|
||||
moveViewUpDown: '上下移动视图'
|
||||
moveViewUpDown: '上下移动视图',
|
||||
associativeLine: '关联线',
|
||||
associativeLineWidth: '粗细',
|
||||
associativeLineColor: '颜色',
|
||||
associativeLineActiveWidth: '激活粗细',
|
||||
associativeLineActiveColor: '激活颜色'
|
||||
},
|
||||
color: {
|
||||
moreColor: '更多颜色'
|
||||
@@ -187,7 +192,8 @@ export default {
|
||||
saveAs: '另存为',
|
||||
import: '导入',
|
||||
export: '导出',
|
||||
shortcutKey: '快捷键'
|
||||
shortcutKey: '快捷键',
|
||||
associativeLine: '关联线',
|
||||
},
|
||||
edit: {
|
||||
newFeatureNoticeTitle: '新特性提醒',
|
||||
|
||||
@@ -92,6 +92,10 @@ export default {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ let APIList = [
|
||||
'doExport',
|
||||
'miniMap',
|
||||
'watermark',
|
||||
'associativeLine',
|
||||
'xmind',
|
||||
'utils'
|
||||
]
|
||||
|
||||
89
web/src/pages/Doc/en/associativeLine/index.md
Normal file
89
web/src/pages/Doc/en/associativeLine/index.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# AssociativeLine plugin
|
||||
|
||||
> v0.4.5+
|
||||
|
||||
> The function of adjusting associated line control points is supported from v0.4.6+
|
||||
|
||||
This plugin is used to support the addition of associative lines.
|
||||
|
||||
The plugin is currently not fully functional, and does not support adding text to association lines.
|
||||
|
||||
## Register
|
||||
|
||||
```js
|
||||
import MindMap from 'simple-mind-map'
|
||||
import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js'
|
||||
|
||||
MindMap.usePlugin(AssociativeLine)
|
||||
```
|
||||
|
||||
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.associativeLine`.
|
||||
|
||||
## Config
|
||||
|
||||
Support for modifying the thickness and color of associated lines, divided into default and active states. The configuration is as follows:
|
||||
|
||||
- `associativeLineWidth`: The thickness of the default state of the associated line. The default value is `2`
|
||||
|
||||
- `associativeLineColor`: Color of the default state of associative lines. The default value is `rgb(51, 51, 51)`
|
||||
|
||||
- `associativeLineActiveWidth`: The thickness of the active state of the associated line. The default value is `8`
|
||||
|
||||
- `associativeLineActiveColor`: The color of the active state of the associated line. The default value is `rgba(2, 167, 240, 1)`
|
||||
|
||||
The configuration is provided as a theme, so if you want to modify these four properties, you can modify them using the `mindMap.setThemeConfig(config)` method.
|
||||
|
||||
## Props
|
||||
|
||||
### mindMap.associativeLine.lineList
|
||||
|
||||
Currently, all connection line data, array types, and each item of the array are also an array:
|
||||
|
||||
```js
|
||||
[
|
||||
path, // Connector node
|
||||
clickPath, // Invisible click line node
|
||||
node, // Start node
|
||||
toNode // Target node
|
||||
]
|
||||
```
|
||||
|
||||
### mindMap.associativeLine.activeLine
|
||||
|
||||
The currently active connection line and array type are the same as the structure of each item in the `lineList` array.
|
||||
|
||||
## Methods
|
||||
|
||||
### renderAllLines()
|
||||
|
||||
Re-render all associated lines.
|
||||
|
||||
### removeAllLines()
|
||||
|
||||
Remove all associated lines.
|
||||
|
||||
### createLineFromActiveNode()
|
||||
|
||||
Create an associated line from the current active node. If there are multiple active nodes, the default is the first node.
|
||||
|
||||
After calling this method, an association line will be rendered from the first active node to the current mouse real-time position. When a target node is clicked, it represents completion of creation. An association line will be rendered between the first active node and the clicked node.
|
||||
|
||||
### createLine(fromNode)
|
||||
|
||||
Creates an associative line starting at the specified node.
|
||||
|
||||
After calling this method, an association line will be rendered from the specified node to the current mouse real-time position. When a target node is clicked, it represents completion of creation, and an association line will be rendered between the specified node and the clicked node.
|
||||
|
||||
### addLine(fromNode, toNode)
|
||||
|
||||
Add an associative line directly.
|
||||
|
||||
Calling this method will directly create an association line from the `fromNode` to the `toNode` node.
|
||||
|
||||
### removeLine()
|
||||
|
||||
Deletes the currently active associative line. Clicking on an associated line is considered active.
|
||||
|
||||
### clearActiveLine()
|
||||
|
||||
Clears the active state of the currently active association line.
|
||||
78
web/src/pages/Doc/en/associativeLine/index.vue
Normal file
78
web/src/pages/Doc/en/associativeLine/index.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>AssociativeLine plugin</h1>
|
||||
<blockquote>
|
||||
<p>v0.4.5+</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>The function of adjusting associated line control points is supported from v0.4.6+</p>
|
||||
</blockquote>
|
||||
<p>This plugin is used to support the addition of associative lines.</p>
|
||||
<p>The plugin is currently not fully functional, and does not support adding text to association lines.</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">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> AssociativeLine <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/AssociativeLine.js'</span>
|
||||
|
||||
MindMap.usePlugin(AssociativeLine)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.associativeLine</code>.</p>
|
||||
<h2>Config</h2>
|
||||
<p>Support for modifying the thickness and color of associated lines, divided into default and active states. The configuration is as follows:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>associativeLineWidth</code>: The thickness of the default state of the associated line. The default value is <code>2</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>associativeLineColor</code>: Color of the default state of associative lines. The default value is <code>rgb(51, 51, 51)</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>associativeLineActiveWidth</code>: The thickness of the active state of the associated line. The default value is <code>8</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>associativeLineActiveColor</code>: The color of the active state of the associated line. The default value is <code>rgba(2, 167, 240, 1)</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>The configuration is provided as a theme, so if you want to modify these four properties, you can modify them using the <code>mindMap.setThemeConfig(config)</code> method.</p>
|
||||
<h2>Props</h2>
|
||||
<h3>mindMap.associativeLine.lineList</h3>
|
||||
<p>Currently, all connection line data, array types, and each item of the array are also an array:</p>
|
||||
<pre class="hljs"><code>[
|
||||
path, <span class="hljs-comment">// Connector node</span>
|
||||
clickPath, <span class="hljs-comment">// Invisible click line node</span>
|
||||
node, <span class="hljs-comment">// Start node</span>
|
||||
toNode <span class="hljs-comment">// Target node</span>
|
||||
]
|
||||
</code></pre>
|
||||
<h3>mindMap.associativeLine.activeLine</h3>
|
||||
<p>The currently active connection line and array type are the same as the structure of each item in the <code>lineList</code> array.</p>
|
||||
<h2>Methods</h2>
|
||||
<h3>renderAllLines()</h3>
|
||||
<p>Re-render all associated lines.</p>
|
||||
<h3>removeAllLines()</h3>
|
||||
<p>Remove all associated lines.</p>
|
||||
<h3>createLineFromActiveNode()</h3>
|
||||
<p>Create an associated line from the current active node. If there are multiple active nodes, the default is the first node.</p>
|
||||
<p>After calling this method, an association line will be rendered from the first active node to the current mouse real-time position. When a target node is clicked, it represents completion of creation. An association line will be rendered between the first active node and the clicked node.</p>
|
||||
<h3>createLine(fromNode)</h3>
|
||||
<p>Creates an associative line starting at the specified node.</p>
|
||||
<p>After calling this method, an association line will be rendered from the specified node to the current mouse real-time position. When a target node is clicked, it represents completion of creation, and an association line will be rendered between the specified node and the clicked node.</p>
|
||||
<h3>addLine(fromNode, toNode)</h3>
|
||||
<p>Add an associative line directly.</p>
|
||||
<p>Calling this method will directly create an association line from the <code>fromNode</code> to the <code>toNode</code> node.</p>
|
||||
<h3>removeLine()</h3>
|
||||
<p>Deletes the currently active associative line. Clicking on an associated line is considered active.</p>
|
||||
<h3>clearActiveLine()</h3>
|
||||
<p>Clears the active state of the currently active association line.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.6
|
||||
|
||||
New: 1.Associated lines support adjusting control points.
|
||||
|
||||
optimization: 1.When adding historical data, filter data that has not changed compared to the previous time.
|
||||
|
||||
Fix: 1.Fixed a conflict between the direction keys and the navigation function of the direction keys during node editing. 2.Fixed the issue of node id loss when dragging a mobile node, which can cause associated lines to be lost.
|
||||
|
||||
## 0.4.5
|
||||
|
||||
New: 1.Supports associative lines. 2.You can also drag the canvas by holding down the root node. 3. Hold down the ctrl key to adjust multiple selected nodes.
|
||||
|
||||
## 0.4.4
|
||||
|
||||
New: Support horizontal scrolling in response to the mouse.
|
||||
|
||||
## 0.4.3
|
||||
|
||||
Fix: No trigger after forward and backward `data_ Change` event.
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.4.6</h2>
|
||||
<p>New: 1.Associated lines support adjusting control points.</p>
|
||||
<p>optimization: 1.When adding historical data, filter data that has not changed compared to the previous time.</p>
|
||||
<p>Fix: 1.Fixed a conflict between the direction keys and the navigation function of the direction keys during node editing. 2.Fixed the issue of node id loss when dragging a mobile node, which can cause associated lines to be lost.</p>
|
||||
<h2>0.4.5</h2>
|
||||
<p>New: 1.Supports associative lines. 2.You can also drag the canvas by holding down the root node. 3. Hold down the ctrl key to adjust multiple selected nodes.</p>
|
||||
<h2>0.4.4</h2>
|
||||
<p>New: Support horizontal scrolling in response to the mouse.</p>
|
||||
<h2>0.4.3</h2>
|
||||
<p>Fix: No trigger after forward and backward <code>data_ Change</code> event.</p>
|
||||
<p>New: Support user-defined mouse wheel events; The mouse wheel is adjusted to support zooming and moving the view up and down.</p>
|
||||
|
||||
@@ -193,6 +193,9 @@ Listen to an event. Event list:
|
||||
| node_tree_render_end(v0.2.16+) | Node tree render end event | |
|
||||
| rich_text_selection_change(v0.4.0+) | Available when the `RichText` plugin is registered. Triggered when the text selection area changes when the node is edited | hasRange(Whether there is a selection)、rectInfo(Size and location information of the selected area)、formatInfo(Text formatting information of the selected area) |
|
||||
| transforming-dom-to-images(v0.4.0+) | Available when the `RichText` plugin is registered. When there is a `DOM` node in `svg`, the `DOM` node will be converted to an image when exporting to an image. This event will be triggered during the conversion process. You can use this event to prompt the user about the node to which you are currently converting | index(Index of the node currently converted to)、len(Total number of nodes to be converted) |
|
||||
| node_dragging(v0.4.5+) | Triggered when a node is dragged | node(The currently dragged node) |
|
||||
| node_dragend(v0.4.5+) | Triggered when the node is dragged and ends | |
|
||||
| associative_line_click(v0.4.5+) | Triggered when an associated line is clicked | path(Connector node)、clickPath(Invisible click line node)、node(Start node)、toNode(Target node) |
|
||||
|
||||
### emit(event, ...args)
|
||||
|
||||
@@ -266,8 +269,8 @@ redo. All commands are as follows:
|
||||
| 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 | |
|
||||
| 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 | openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is `true`) |
|
||||
| INSERT_CHILD_NODE | Insert a child node, the active node will be the operation node | openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is `true`) |
|
||||
| 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 | |
|
||||
|
||||
@@ -447,6 +447,21 @@ poor performance and should be used sparingly.</p>
|
||||
<td>Available when the <code>RichText</code> plugin is registered. When there is a <code>DOM</code> node in <code>svg</code>, the <code>DOM</code> node will be converted to an image when exporting to an image. This event will be triggered during the conversion process. You can use this event to prompt the user about the node to which you are currently converting</td>
|
||||
<td>index(Index of the node currently converted to)、len(Total number of nodes to be converted)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>node_dragging(v0.4.5+)</td>
|
||||
<td>Triggered when a node is dragged</td>
|
||||
<td>node(The currently dragged node)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>node_dragend(v0.4.5+)</td>
|
||||
<td>Triggered when the node is dragged and ends</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>associative_line_click(v0.4.5+)</td>
|
||||
<td>Triggered when an associated line is clicked</td>
|
||||
<td>path(Connector node)、clickPath(Invisible click line node)、node(Start node)、toNode(Target node)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>emit(event, ...args)</h3>
|
||||
@@ -516,12 +531,12 @@ redo. All commands are as follows:</p>
|
||||
<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>
|
||||
<td>openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is <code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>INSERT_CHILD_NODE</td>
|
||||
<td>Insert a child node, the active node will be the operation node</td>
|
||||
<td></td>
|
||||
<td>openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is <code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UP_NODE</td>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
- [x] Supports various node shapes
|
||||
- [x] Supports export to json, png, svg, pdf, and import from json, xmind
|
||||
- [x] Supports mini map、support watermark
|
||||
- [x] Supports associative lines
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -50,10 +51,6 @@ Provide document page service.
|
||||
|
||||
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)
|
||||
|
||||
@@ -4,20 +4,21 @@
|
||||
<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="checkbox17" checked="true"><label for="checkbox17">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="checkbox18" checked="true"><label for="checkbox18">Supports four types of structures: logical structure diagrams, mind maps,</label>
|
||||
<li><input type="checkbox" id="checkbox36" checked="true"><label for="checkbox36">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="checkbox37" checked="true"><label for="checkbox37">Supports four types of structures: logical structure diagrams, mind maps,</label>
|
||||
organizational structure diagrams, and directory organization diagrams</li>
|
||||
<li><input type="checkbox" id="checkbox19" checked="true"><label for="checkbox19">Built-in multiple themes and allows for highly customized styles, and support register new themes</label></li>
|
||||
<li><input type="checkbox" id="checkbox20" checked="true"><label for="checkbox20">Supports shortcuts</label></li>
|
||||
<li><input type="checkbox" id="checkbox21" checked="true"><label for="checkbox21">Node content supports images, icons, hyperlinks, notes, tags, and</label>
|
||||
<li><input type="checkbox" id="checkbox38" checked="true"><label for="checkbox38">Built-in multiple themes and allows for highly customized styles, and support register new themes</label></li>
|
||||
<li><input type="checkbox" id="checkbox39" checked="true"><label for="checkbox39">Supports shortcuts</label></li>
|
||||
<li><input type="checkbox" id="checkbox40" checked="true"><label for="checkbox40">Node content supports images, icons, hyperlinks, notes, tags, and</label>
|
||||
summaries</li>
|
||||
<li><input type="checkbox" id="checkbox22" checked="true"><label for="checkbox22">Supports forward and backward navigation</label></li>
|
||||
<li><input type="checkbox" id="checkbox23" checked="true"><label for="checkbox23">Supports dragging and scaling</label></li>
|
||||
<li><input type="checkbox" id="checkbox24" checked="true"><label for="checkbox24">Supports right-click and Ctrl + left-click to select multiple items</label></li>
|
||||
<li><input type="checkbox" id="checkbox25" checked="true"><label for="checkbox25">Supports free dragging and dragging to adjust nodes</label></li>
|
||||
<li><input type="checkbox" id="checkbox26" checked="true"><label for="checkbox26">Supports various node shapes</label></li>
|
||||
<li><input type="checkbox" id="checkbox27" checked="true"><label for="checkbox27">Supports export to json, png, svg, pdf, and import from json, xmind</label></li>
|
||||
<li><input type="checkbox" id="checkbox28" checked="true"><label for="checkbox28">Supports mini map、support watermark</label></li>
|
||||
<li><input type="checkbox" id="checkbox41" checked="true"><label for="checkbox41">Supports forward and backward navigation</label></li>
|
||||
<li><input type="checkbox" id="checkbox42" checked="true"><label for="checkbox42">Supports dragging and scaling</label></li>
|
||||
<li><input type="checkbox" id="checkbox43" checked="true"><label for="checkbox43">Supports right-click and Ctrl + left-click to select multiple items</label></li>
|
||||
<li><input type="checkbox" id="checkbox44" checked="true"><label for="checkbox44">Supports free dragging and dragging to adjust nodes</label></li>
|
||||
<li><input type="checkbox" id="checkbox45" checked="true"><label for="checkbox45">Supports various node shapes</label></li>
|
||||
<li><input type="checkbox" id="checkbox46" checked="true"><label for="checkbox46">Supports export to json, png, svg, pdf, and import from json, xmind</label></li>
|
||||
<li><input type="checkbox" id="checkbox47" checked="true"><label for="checkbox47">Supports mini map、support watermark</label></li>
|
||||
<li><input type="checkbox" id="checkbox48" checked="true"><label for="checkbox48">Supports associative lines</label></li>
|
||||
</ul>
|
||||
<h2>Table of Contents</h2>
|
||||
<p>1.<code>simple-mind-map</code></p>
|
||||
@@ -27,24 +28,22 @@ frameworks such as Vue and React, or without a framework.</p>
|
||||
<p>This is an online mind map built using the <code>simple-mind-map</code> library and based
|
||||
on <code>Vue2.x</code> and <code>ElementUI</code>. Features include:</p>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox29" checked="true"><label for="checkbox29">Toolbar, which supports inserting and deleting nodes, and editing node</label>
|
||||
<li><input type="checkbox" id="checkbox49" checked="true"><label for="checkbox49">Toolbar, which supports inserting and deleting nodes, and editing node</label>
|
||||
images, icons, hyperlinks, notes, tags, and summaries</li>
|
||||
<li><input type="checkbox" id="checkbox30" checked="true"><label for="checkbox30">Sidebar, with panels for basic style settings, node style settings,</label>
|
||||
<li><input type="checkbox" id="checkbox50" checked="true"><label for="checkbox50">Sidebar, with panels for basic style settings, node style settings,</label>
|
||||
outline, theme selection, and structure selection</li>
|
||||
<li><input type="checkbox" id="checkbox31" checked="true"><label for="checkbox31">Import and export functionality; data is saved in the browser's local</label>
|
||||
<li><input type="checkbox" id="checkbox51" checked="true"><label for="checkbox51">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="checkbox32" checked="true"><label for="checkbox32">Right-click menu, which supports operations such as expanding, collapsing,</label>
|
||||
<li><input type="checkbox" id="checkbox52" checked="true"><label for="checkbox52">Right-click menu, which supports operations such as expanding, collapsing,</label>
|
||||
and organizing layout</li>
|
||||
<li><input type="checkbox" id="checkbox33" checked="true"><label for="checkbox33">Bottom bar, which supports node and word count statistics, switching</label>
|
||||
<li><input type="checkbox" id="checkbox53" checked="true"><label for="checkbox53">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>
|
||||
|
||||
@@ -101,12 +101,14 @@ const mindMap = new MindMap({
|
||||
|
||||
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:
|
||||
If you need a file in the format of `umd` module, such as `CDN` in the browser, Then you can find the `simpleMindMap.umd.min.js` file in the `/simple-mind-map/dist/` directory, copy it to your project, and then import it into the page:
|
||||
|
||||
```js
|
||||
import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min.js";
|
||||
```html
|
||||
<script scr="simpleMindMap.umd.min.js"></script>
|
||||
```
|
||||
|
||||
A global variable `window.simpleMindMap` will be created.
|
||||
|
||||
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
|
||||
|
||||
@@ -72,9 +72,10 @@ compile this dependency:</p>
|
||||
});
|
||||
</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">"simple-mind-map/dist/simpleMindMap.umd.min.js"</span>;
|
||||
<p>If you need a file in the format of <code>umd</code> module, such as <code>CDN</code> in the browser, Then you can find the <code>simpleMindMap.umd.min.js</code> file in the <code>/simple-mind-map/dist/</code> directory, copy it to your project, and then import it into the page:</p>
|
||||
<pre class="hljs"><code><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">scr</span>=<span class="hljs-string">"simpleMindMap.umd.min.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
||||
</code></pre>
|
||||
<p>A global variable <code>window.simpleMindMap</code> will be created.</p>
|
||||
<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>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Participate in translation
|
||||
|
||||
Thanks for the English translation provided by [Emircan ERKUL](https://github.com/emircanerkul).
|
||||
Thanks for the first version 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.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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>Thanks for the first version 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>
|
||||
|
||||
@@ -72,7 +72,11 @@ Copy render tree data, example:
|
||||
copyRenderTree({}, this.mindMap.renderer.renderTree);
|
||||
```
|
||||
|
||||
#### copyNodeTree(tree, root)
|
||||
#### copyNodeTree(tree, root, removeActiveState, keepId)
|
||||
|
||||
- `removeActiveState`: `Boolean`, default is `false`, Whether to remove the active state of the node
|
||||
|
||||
- `keepId`: v0.4.6+, `Boolean`, default is `false`, Whether to retain the `id` of the replicated node will be deleted by default to prevent duplicate node `id`. However, for mobile node scenarios, the original `id` of the node needs to be retained
|
||||
|
||||
Copy node tree data, mainly eliminating the reference `node` instance `_node`
|
||||
and copying the `data` of the data object, example:
|
||||
|
||||
@@ -39,7 +39,15 @@ basic data, otherwise it will throw an error</p>
|
||||
<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>
|
||||
<h4>copyNodeTree(tree, root, removeActiveState, keepId)</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>removeActiveState</code>: <code>Boolean</code>, default is <code>false</code>, Whether to remove the active state of the node</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>keepId</code>: v0.4.6+, <code>Boolean</code>, default is <code>false</code>, Whether to retain the <code>id</code> of the replicated node will be deleted by default to prevent duplicate node <code>id</code>. However, for mobile node scenarios, the original <code>id</code> of the node needs to be retained</p>
|
||||
</li>
|
||||
</ul>
|
||||
<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);
|
||||
|
||||
@@ -1,3 +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":"richText","title":"RichText插件"},{"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":"richText","title":"RichText plugin"},{"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"}]}]
|
||||
export default [{"lang":"zh","children":[{"path":"associativeLine","title":"AssociativeLine 插件"},{"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":"richText","title":"RichText插件"},{"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":"associativeLine","title":"AssociativeLine plugin"},{"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":"richText","title":"RichText plugin"},{"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"}]}]
|
||||
|
||||
89
web/src/pages/Doc/zh/associativeLine/index.md
Normal file
89
web/src/pages/Doc/zh/associativeLine/index.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# AssociativeLine 插件
|
||||
|
||||
> v0.4.5+
|
||||
|
||||
> 调整关联线控制点的功能从v0.4.6+开始支持
|
||||
|
||||
该插件用于支持添加关联线。
|
||||
|
||||
该插件目前功能还不完善,不支持在关联线上添加文字。
|
||||
|
||||
## 注册
|
||||
|
||||
```js
|
||||
import MindMap from 'simple-mind-map'
|
||||
import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js'
|
||||
|
||||
MindMap.usePlugin(AssociativeLine)
|
||||
```
|
||||
|
||||
注册完且实例化`MindMap`后可通过`mindMap.associativeLine`获取到该实例。
|
||||
|
||||
## 配置
|
||||
|
||||
支持修改关联线的粗细和颜色,分为默认状态和激活状态。配置如下:
|
||||
|
||||
- `associativeLineWidth`:关联线默认状态的粗细,默认值为`2`
|
||||
|
||||
- `associativeLineColor`:关联线默认状态的颜色,默认值为`rgb(51, 51, 51)`
|
||||
|
||||
- `associativeLineActiveWidth`:关联线激活状态的粗细,默认值为`8`
|
||||
|
||||
- `associativeLineActiveColor`:关联线激活状态的颜色,默认值为`rgba(2, 167, 240, 1)`
|
||||
|
||||
配置以主题的方式提供,所以如果想要修改这四个属性,可以通过`mindMap.setThemeConfig(config)`方法进行修改。
|
||||
|
||||
## 属性
|
||||
|
||||
### mindMap.associativeLine.lineList
|
||||
|
||||
当前所有连接线数据,数组类型,数组的每一项也是一个数组:
|
||||
|
||||
```js
|
||||
[
|
||||
path, // 连接线节点
|
||||
clickPath, // 不可见的点击线节点
|
||||
node, // 起始节点
|
||||
toNode // 目标节点
|
||||
]
|
||||
```
|
||||
|
||||
### mindMap.associativeLine.activeLine
|
||||
|
||||
当前激活的连接线,数组类型,同`lineList`数组的每一项的结构。
|
||||
|
||||
## 方法
|
||||
|
||||
### renderAllLines()
|
||||
|
||||
重新渲染所有关联线。
|
||||
|
||||
### removeAllLines()
|
||||
|
||||
移除所有关联线。
|
||||
|
||||
### createLineFromActiveNode()
|
||||
|
||||
从当前激活节点开始创建关联线,如果有多个激活节点,默认为第一个节点。
|
||||
|
||||
调用该方法后,会从第一个激活节点到当前鼠标实时位置渲染一条关联线,当点击某个目标节点后则代表创建完成,会在第一个激活节点和点击节点之间渲染一条关联线。
|
||||
|
||||
### createLine(fromNode)
|
||||
|
||||
从指定节点开始创建关联线。
|
||||
|
||||
调用该方法后,会从指定节点到当前鼠标实时位置渲染一条关联线,当点击某个目标节点后则代表创建完成,会在指定节点和点击节点之间渲染一条关联线。
|
||||
|
||||
### addLine(fromNode, toNode)
|
||||
|
||||
直接添加一条关联线。
|
||||
|
||||
调用该方法,会直接创建一条从`fromNode`到`toNode`节点的关联线。
|
||||
|
||||
### removeLine()
|
||||
|
||||
删除当前激活的关联线。点击某条关联线则视为激活。
|
||||
|
||||
### clearActiveLine()
|
||||
|
||||
清除当前激活的关联线的激活状态。
|
||||
78
web/src/pages/Doc/zh/associativeLine/index.vue
Normal file
78
web/src/pages/Doc/zh/associativeLine/index.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>AssociativeLine 插件</h1>
|
||||
<blockquote>
|
||||
<p>v0.4.5+</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>调整关联线控制点的功能从v0.4.6+开始支持</p>
|
||||
</blockquote>
|
||||
<p>该插件用于支持添加关联线。</p>
|
||||
<p>该插件目前功能还不完善,不支持在关联线上添加文字。</p>
|
||||
<h2>注册</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> AssociativeLine <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/AssociativeLine.js'</span>
|
||||
|
||||
MindMap.usePlugin(AssociativeLine)
|
||||
</code></pre>
|
||||
<p>注册完且实例化<code>MindMap</code>后可通过<code>mindMap.associativeLine</code>获取到该实例。</p>
|
||||
<h2>配置</h2>
|
||||
<p>支持修改关联线的粗细和颜色,分为默认状态和激活状态。配置如下:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>associativeLineWidth</code>:关联线默认状态的粗细,默认值为<code>2</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>associativeLineColor</code>:关联线默认状态的颜色,默认值为<code>rgb(51, 51, 51)</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>associativeLineActiveWidth</code>:关联线激活状态的粗细,默认值为<code>8</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>associativeLineActiveColor</code>:关联线激活状态的颜色,默认值为<code>rgba(2, 167, 240, 1)</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>配置以主题的方式提供,所以如果想要修改这四个属性,可以通过<code>mindMap.setThemeConfig(config)</code>方法进行修改。</p>
|
||||
<h2>属性</h2>
|
||||
<h3>mindMap.associativeLine.lineList</h3>
|
||||
<p>当前所有连接线数据,数组类型,数组的每一项也是一个数组:</p>
|
||||
<pre class="hljs"><code>[
|
||||
path, <span class="hljs-comment">// 连接线节点</span>
|
||||
clickPath, <span class="hljs-comment">// 不可见的点击线节点</span>
|
||||
node, <span class="hljs-comment">// 起始节点</span>
|
||||
toNode <span class="hljs-comment">// 目标节点</span>
|
||||
]
|
||||
</code></pre>
|
||||
<h3>mindMap.associativeLine.activeLine</h3>
|
||||
<p>当前激活的连接线,数组类型,同<code>lineList</code>数组的每一项的结构。</p>
|
||||
<h2>方法</h2>
|
||||
<h3>renderAllLines()</h3>
|
||||
<p>重新渲染所有关联线。</p>
|
||||
<h3>removeAllLines()</h3>
|
||||
<p>移除所有关联线。</p>
|
||||
<h3>createLineFromActiveNode()</h3>
|
||||
<p>从当前激活节点开始创建关联线,如果有多个激活节点,默认为第一个节点。</p>
|
||||
<p>调用该方法后,会从第一个激活节点到当前鼠标实时位置渲染一条关联线,当点击某个目标节点后则代表创建完成,会在第一个激活节点和点击节点之间渲染一条关联线。</p>
|
||||
<h3>createLine(fromNode)</h3>
|
||||
<p>从指定节点开始创建关联线。</p>
|
||||
<p>调用该方法后,会从指定节点到当前鼠标实时位置渲染一条关联线,当点击某个目标节点后则代表创建完成,会在指定节点和点击节点之间渲染一条关联线。</p>
|
||||
<h3>addLine(fromNode, toNode)</h3>
|
||||
<p>直接添加一条关联线。</p>
|
||||
<p>调用该方法,会直接创建一条从<code>fromNode</code>到<code>toNode</code>节点的关联线。</p>
|
||||
<h3>removeLine()</h3>
|
||||
<p>删除当前激活的关联线。点击某条关联线则视为激活。</p>
|
||||
<h3>clearActiveLine()</h3>
|
||||
<p>清除当前激活的关联线的激活状态。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,21 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.6
|
||||
|
||||
新增:1.关联线支持调整控制点。
|
||||
|
||||
优化:1.添加历史数据时过滤和上一次相比没有改变的数据。
|
||||
|
||||
修复:1.修复节点编辑时方向键和方向键导航功能的冲突问题。 2.修复拖拽移动节点时节点id的丢失问题,这会导致关联线丢失。
|
||||
|
||||
## 0.4.5
|
||||
|
||||
新增:1.支持关联线。 2.按住根节点也可以拖动画布。3.按住ctrl键可以调整多选节点。
|
||||
|
||||
## 0.4.4
|
||||
|
||||
新增:支持响应鼠标的横向滚动。
|
||||
|
||||
## 0.4.3
|
||||
|
||||
修复:前进回退后没有触发`data_change`事件的问题。
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.4.6</h2>
|
||||
<p>新增:1.关联线支持调整控制点。</p>
|
||||
<p>优化:1.添加历史数据时过滤和上一次相比没有改变的数据。</p>
|
||||
<p>修复:1.修复节点编辑时方向键和方向键导航功能的冲突问题。 2.修复拖拽移动节点时节点id的丢失问题,这会导致关联线丢失。</p>
|
||||
<h2>0.4.5</h2>
|
||||
<p>新增:1.支持关联线。 2.按住根节点也可以拖动画布。3.按住ctrl键可以调整多选节点。</p>
|
||||
<h2>0.4.4</h2>
|
||||
<p>新增:支持响应鼠标的横向滚动。</p>
|
||||
<h2>0.4.3</h2>
|
||||
<p>修复:前进回退后没有触发<code>data_change</code>事件的问题。</p>
|
||||
<p>新增:支持自定义鼠标滚轮事件;鼠标滚轮调整为支持缩放视图和上下移动视图。</p>
|
||||
|
||||
@@ -190,6 +190,9 @@ mindMap.setTheme('主题名称')
|
||||
| node_tree_render_end(v0.2.16+) | 节点树渲染完毕事件 | |
|
||||
| rich_text_selection_change(v0.4.0+) | 当注册了`RichText`插件时可用。当节点编辑时,文本选区发生改变时触发 | hasRange(是否存在选区)、rectInfo(选区的尺寸和位置信息)、formatInfo(选区的文本格式化信息) |
|
||||
| transforming-dom-to-images(v0.4.0+) | 当注册了`RichText`插件时可用。当`svg`中存在`DOM`节点时,导出为图片时会将`DOM`节点转换为图片,转换过程中会触发该事件,可用通过该事件给用户提示,告知目前转换到的节点 | index(当前转换到的节点索引)、len(一共需要转换的节点数量) |
|
||||
| node_dragging(v0.4.5+) | 当某个节点被拖拽时触发 | node(当前被拖拽的节点) |
|
||||
| node_dragend(v0.4.5+) | 节点被拖拽结束时触发 | |
|
||||
| associative_line_click(v0.4.5+) | 点击某条关联线时触发 | path(连接线节点)、clickPath(不可见的点击线节点)、node(起始节点)、toNode(目标节点) |
|
||||
|
||||
### emit(event, ...args)
|
||||
|
||||
@@ -260,8 +263,8 @@ mindMap.updateConfig({
|
||||
| SELECT_ALL | 全选 | |
|
||||
| BACK | 回退指定的步数 | step(要回退的步数,默认为1) |
|
||||
| FORWARD | 前进指定的步数 | step(要前进的步数,默认为1) |
|
||||
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | |
|
||||
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | |
|
||||
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | openEdit(v0.4.6+,是否激活新插入的节点并进入编辑模式,默认为`true`) |
|
||||
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | openEdit(v0.4.6+,是否激活新插入的节点并进入编辑模式,默认为`true`) |
|
||||
| UP_NODE | 上移节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的第一个节点使用无效 | |
|
||||
| DOWN_NODE | 操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的最后一个节点使用无效 | |
|
||||
| REMOVE_NODE | 删除节点,操作节点为当前激活的节点 | |
|
||||
|
||||
@@ -440,6 +440,21 @@ mindMap.setTheme(<span class="hljs-string">'主题名称'</span>)
|
||||
<td>当注册了<code>RichText</code>插件时可用。当<code>svg</code>中存在<code>DOM</code>节点时,导出为图片时会将<code>DOM</code>节点转换为图片,转换过程中会触发该事件,可用通过该事件给用户提示,告知目前转换到的节点</td>
|
||||
<td>index(当前转换到的节点索引)、len(一共需要转换的节点数量)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>node_dragging(v0.4.5+)</td>
|
||||
<td>当某个节点被拖拽时触发</td>
|
||||
<td>node(当前被拖拽的节点)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>node_dragend(v0.4.5+)</td>
|
||||
<td>节点被拖拽结束时触发</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>associative_line_click(v0.4.5+)</td>
|
||||
<td>点击某条关联线时触发</td>
|
||||
<td>path(连接线节点)、clickPath(不可见的点击线节点)、node(起始节点)、toNode(目标节点)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>emit(event, ...args)</h3>
|
||||
@@ -506,12 +521,12 @@ mindMap.setTheme(<span class="hljs-string">'主题名称'</span>)
|
||||
<tr>
|
||||
<td>INSERT_NODE</td>
|
||||
<td>插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效</td>
|
||||
<td></td>
|
||||
<td>openEdit(v0.4.6+,是否激活新插入的节点并进入编辑模式,默认为<code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>INSERT_CHILD_NODE</td>
|
||||
<td>插入子节点,操作节点为当前激活的节点</td>
|
||||
<td></td>
|
||||
<td>openEdit(v0.4.6+,是否激活新插入的节点并进入编辑模式,默认为<code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>UP_NODE</td>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
- [x] 支持多种节点形状
|
||||
- [x] 支持导出为`json`、`png`、`svg`、`pdf`,支持从`json`、`xmind`导入
|
||||
- [x] 支持小地图、支持水印
|
||||
- [x] 支持关联线
|
||||
|
||||
## 目录介绍
|
||||
|
||||
@@ -39,10 +40,6 @@
|
||||
|
||||
打包`web`后的资源文件夹。
|
||||
|
||||
4.`docs`
|
||||
|
||||
文档等。
|
||||
|
||||
## 相关文章
|
||||
|
||||
[Web思维导图实现的技术点分析](https://juejin.cn/post/6987711560521089061)
|
||||
|
||||
@@ -4,18 +4,19 @@
|
||||
<p><code>simple-mind-map</code>是一个简单&强大的Web思维导图库,不依赖任何特定框架。</p>
|
||||
<h2>特性</h2>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox0" checked="true"><label for="checkbox0">插件化架构,除核心功能外,其他功能作为插件提供,按需使用,减小整体体积</label></li>
|
||||
<li><input type="checkbox" id="checkbox1" checked="true"><label for="checkbox1">支持逻辑结构图、思维导图、组织结构图、目录组织图四种结构</label></li>
|
||||
<li><input type="checkbox" id="checkbox2" checked="true"><label for="checkbox2">内置多种主题,允许高度自定义样式,支持注册新主题</label></li>
|
||||
<li><input type="checkbox" id="checkbox3" checked="true"><label for="checkbox3">支持快捷键</label></li>
|
||||
<li><input type="checkbox" id="checkbox4" checked="true"><label for="checkbox4">节点内容支持图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox5" checked="true"><label for="checkbox5">支持前进后退</label></li>
|
||||
<li><input type="checkbox" id="checkbox6" checked="true"><label for="checkbox6">支持拖动、缩放</label></li>
|
||||
<li><input type="checkbox" id="checkbox7" checked="true"><label for="checkbox7">支持右键和Ctrl+左键两种多选方式</label></li>
|
||||
<li><input type="checkbox" id="checkbox8" checked="true"><label for="checkbox8">支持节点自由拖拽、拖拽调整</label></li>
|
||||
<li><input type="checkbox" id="checkbox9" checked="true"><label for="checkbox9">支持多种节点形状</label></li>
|
||||
<li><input type="checkbox" id="checkbox10" checked="true"><label for="checkbox10">支持导出为</label><code>json</code>、<code>png</code>、<code>svg</code>、<code>pdf</code>,支持从<code>json</code>、<code>xmind</code>导入</li>
|
||||
<li><input type="checkbox" id="checkbox11" checked="true"><label for="checkbox11">支持小地图、支持水印</label></li>
|
||||
<li><input type="checkbox" id="checkbox54" checked="true"><label for="checkbox54">插件化架构,除核心功能外,其他功能作为插件提供,按需使用,减小整体体积</label></li>
|
||||
<li><input type="checkbox" id="checkbox55" checked="true"><label for="checkbox55">支持逻辑结构图、思维导图、组织结构图、目录组织图四种结构</label></li>
|
||||
<li><input type="checkbox" id="checkbox56" checked="true"><label for="checkbox56">内置多种主题,允许高度自定义样式,支持注册新主题</label></li>
|
||||
<li><input type="checkbox" id="checkbox57" checked="true"><label for="checkbox57">支持快捷键</label></li>
|
||||
<li><input type="checkbox" id="checkbox58" checked="true"><label for="checkbox58">节点内容支持图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox59" checked="true"><label for="checkbox59">支持前进后退</label></li>
|
||||
<li><input type="checkbox" id="checkbox60" checked="true"><label for="checkbox60">支持拖动、缩放</label></li>
|
||||
<li><input type="checkbox" id="checkbox61" checked="true"><label for="checkbox61">支持右键和Ctrl+左键两种多选方式</label></li>
|
||||
<li><input type="checkbox" id="checkbox62" checked="true"><label for="checkbox62">支持节点自由拖拽、拖拽调整</label></li>
|
||||
<li><input type="checkbox" id="checkbox63" checked="true"><label for="checkbox63">支持多种节点形状</label></li>
|
||||
<li><input type="checkbox" id="checkbox64" checked="true"><label for="checkbox64">支持导出为</label><code>json</code>、<code>png</code>、<code>svg</code>、<code>pdf</code>,支持从<code>json</code>、<code>xmind</code>导入</li>
|
||||
<li><input type="checkbox" id="checkbox65" checked="true"><label for="checkbox65">支持小地图、支持水印</label></li>
|
||||
<li><input type="checkbox" id="checkbox66" checked="true"><label for="checkbox66">支持关联线</label></li>
|
||||
</ul>
|
||||
<h2>目录介绍</h2>
|
||||
<p>1.<code>simple-mind-map</code></p>
|
||||
@@ -23,17 +24,15 @@
|
||||
<p>2.<code>web</code></p>
|
||||
<p>使用<code>simple-mind-map</code>工具库,基于<code>vue2.x</code>、<code>ElementUI</code>搭建的在线思维导图。特性:</p>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox12" checked="true"><label for="checkbox12">工具栏,支持插入节点、删除节点;编辑节点图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox13" checked="true"><label for="checkbox13">侧边栏,基础样式设置面板、节点样式设置面板、大纲面板、主题选择面板、结构选择面板</label></li>
|
||||
<li><input type="checkbox" id="checkbox14" checked="true"><label for="checkbox14">导入导出功能;数据默认保存在浏览器本地存储,也支持直接创建、打开、编辑电脑本地文件</label></li>
|
||||
<li><input type="checkbox" id="checkbox15" checked="true"><label for="checkbox15">右键菜单,支持展开、收起、整理布局等操作</label></li>
|
||||
<li><input type="checkbox" id="checkbox16" checked="true"><label for="checkbox16">底部栏,支持节点数量、字数统计;支持切换编辑和只读模式;支持放大缩小;支持全屏切换;支持小地图</label></li>
|
||||
<li><input type="checkbox" id="checkbox67" checked="true"><label for="checkbox67">工具栏,支持插入节点、删除节点;编辑节点图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox68" checked="true"><label for="checkbox68">侧边栏,基础样式设置面板、节点样式设置面板、大纲面板、主题选择面板、结构选择面板</label></li>
|
||||
<li><input type="checkbox" id="checkbox69" checked="true"><label for="checkbox69">导入导出功能;数据默认保存在浏览器本地存储,也支持直接创建、打开、编辑电脑本地文件</label></li>
|
||||
<li><input type="checkbox" id="checkbox70" checked="true"><label for="checkbox70">右键菜单,支持展开、收起、整理布局等操作</label></li>
|
||||
<li><input type="checkbox" id="checkbox71" checked="true"><label for="checkbox71">底部栏,支持节点数量、字数统计;支持切换编辑和只读模式;支持放大缩小;支持全屏切换;支持小地图</label></li>
|
||||
</ul>
|
||||
<p>提供文档页面服务。</p>
|
||||
<p>3.<code>dist</code></p>
|
||||
<p>打包<code>web</code>后的资源文件夹。</p>
|
||||
<p>4.<code>docs</code></p>
|
||||
<p>文档等。</p>
|
||||
<h2>相关文章</h2>
|
||||
<p><a href="https://juejin.cn/post/6987711560521089061">Web思维导图实现的技术点分析</a></p>
|
||||
<p><a href="https://juejin.cn/post/7157681502506090510">只需百来行代码,为你的Web页面增加本地文件操作能力,确定不试试吗?</a></p>
|
||||
|
||||
@@ -92,12 +92,14 @@ const mindMap = new MindMap({
|
||||
|
||||
默认引入的是未打包的`ES`模块,且只包含核心功能,不包含未注册的插件内容,能有效减小体积,不过你需要在你的项目中配置`babel`编译`simple-mind-map`,防止一些较新的`js`语法部分浏览器不支持。
|
||||
|
||||
如果你需要`umd`模块格式的文件,比如以`CDN`的方式在浏览器上使用,那么你可以使用如下方式引入:
|
||||
如果你需要`umd`模块格式的文件,比如以`CDN`的方式在浏览器上使用,那么你可以从`/simple-mind-map/dist/`目录中找到`simpleMindMap.umd.min.js`文件,复制到你的项目中,然后在页面中引入:
|
||||
|
||||
```js
|
||||
import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min.js";
|
||||
```html
|
||||
<script scr="simpleMindMap.umd.min.js"></script>
|
||||
```
|
||||
|
||||
会创建一个全局变量`window.simpleMindMap`。
|
||||
|
||||
这种方式的缺点是会包含所有的内容,包括你没有注册的插件,所以整体体积会比较大。
|
||||
|
||||
## 问题
|
||||
|
||||
@@ -62,9 +62,10 @@ npm run build
|
||||
});
|
||||
</code></pre>
|
||||
<p>默认引入的是未打包的<code>ES</code>模块,且只包含核心功能,不包含未注册的插件内容,能有效减小体积,不过你需要在你的项目中配置<code>babel</code>编译<code>simple-mind-map</code>,防止一些较新的<code>js</code>语法部分浏览器不支持。</p>
|
||||
<p>如果你需要<code>umd</code>模块格式的文件,比如以<code>CDN</code>的方式在浏览器上使用,那么你可以使用如下方式引入:</p>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">"simple-mind-map/dist/simpleMindMap.umd.min.js"</span>;
|
||||
<p>如果你需要<code>umd</code>模块格式的文件,比如以<code>CDN</code>的方式在浏览器上使用,那么你可以从<code>/simple-mind-map/dist/</code>目录中找到<code>simpleMindMap.umd.min.js</code>文件,复制到你的项目中,然后在页面中引入:</p>
|
||||
<pre class="hljs"><code><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">scr</span>=<span class="hljs-string">"simpleMindMap.umd.min.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
|
||||
</code></pre>
|
||||
<p>会创建一个全局变量<code>window.simpleMindMap</code>。</p>
|
||||
<p>这种方式的缺点是会包含所有的内容,包括你没有注册的插件,所以整体体积会比较大。</p>
|
||||
<h2>问题</h2>
|
||||
<h3>1.在Vite中使用报错,提示xml-js依赖出错</h3>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 参与翻译
|
||||
|
||||
感谢[Emircan ERKUL](https://github.com/emircanerkul)提供的英文翻译。
|
||||
感谢[Emircan ERKUL](https://github.com/emircanerkul)提供的第一版英文翻译。
|
||||
|
||||
如果你也想参与翻译本文档的话,可以先克隆本仓库。
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>参与翻译</h1>
|
||||
<p>感谢<a href="https://github.com/emircanerkul">Emircan ERKUL</a>提供的英文翻译。</p>
|
||||
<p>感谢<a href="https://github.com/emircanerkul">Emircan ERKUL</a>提供的第一版英文翻译。</p>
|
||||
<p>如果你也想参与翻译本文档的话,可以先克隆本仓库。</p>
|
||||
<p>翻译的文档在<code>/web/src/pages/Doc/</code>目录下,目前支持英文(<code>en</code>)、简体中文(<code>zh</code>)两种语言。</p>
|
||||
<p>如果是新增一种语言类型,那么可以在<code>/web/src/pages/Doc/</code>目录下创建一个新目录,然后给每个章节创建一个文件夹,你也可以直接复制已存在的语言目录下的所有章节目录进行翻译,注意,你只需要编写<code>index.md</code>文件,章节目录下的<code>index.vue</code>文件是脚本根据<code>index.md</code>自动生成的。</p>
|
||||
|
||||
@@ -68,7 +68,11 @@ walk(tree, null, () => {}, () => {}, false, 0, 0)
|
||||
copyRenderTree({}, this.mindMap.renderer.renderTree)
|
||||
```
|
||||
|
||||
#### copyNodeTree(tree, root)
|
||||
#### copyNodeTree(tree, root, removeActiveState, keepId)
|
||||
|
||||
- `removeActiveState`:`Boolean`,默认为`false`,是否移除节点的激活状态
|
||||
|
||||
- `keepId`:v0.4.6+,`Boolean`,默认为`false`,是否保留被复制节点的`id`,默认会删除`id`防止节点`id`重复,但是对于移动节点的场景,节点原`id`需要保留
|
||||
|
||||
复制节点树数据,主要是剔除其中的引用`node`实例的`_node`,然后复制`data`对象的数据,示例:
|
||||
|
||||
|
||||
@@ -35,7 +35,15 @@
|
||||
<p>复制渲染树数据,示例:</p>
|
||||
<pre class="hljs"><code>copyRenderTree({}, <span class="hljs-built_in">this</span>.mindMap.renderer.renderTree)
|
||||
</code></pre>
|
||||
<h4>copyNodeTree(tree, root)</h4>
|
||||
<h4>copyNodeTree(tree, root, removeActiveState, keepId)</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>removeActiveState</code>:<code>Boolean</code>,默认为<code>false</code>,是否移除节点的激活状态</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>keepId</code>:v0.4.6+,<code>Boolean</code>,默认为<code>false</code>,是否保留被复制节点的<code>id</code>,默认会删除<code>id</code>防止节点<code>id</code>重复,但是对于移动节点的场景,节点原<code>id</code>需要保留</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>复制节点树数据,主要是剔除其中的引用<code>node</code>实例的<code>_node</code>,然后复制<code>data</code>对象的数据,示例:</p>
|
||||
<pre class="hljs"><code>copyNodeTree({}, node)
|
||||
</code></pre>
|
||||
|
||||
@@ -209,6 +209,92 @@
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 关联线 -->
|
||||
<div class="title noTop">{{ $t('baseStyle.associativeLine') }}</div>
|
||||
<div class="row">
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('baseStyle.associativeLineColor') }}</span>
|
||||
<span
|
||||
class="block"
|
||||
v-popover:popover4
|
||||
:style="{ backgroundColor: style.associativeLineColor }"
|
||||
></span>
|
||||
<el-popover ref="popover4" placement="bottom" trigger="click">
|
||||
<Color
|
||||
:color="style.associativeLineColor"
|
||||
@change="
|
||||
color => {
|
||||
update('associativeLineColor', color)
|
||||
}
|
||||
"
|
||||
></Color>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('baseStyle.associativeLineWidth') }}</span>
|
||||
<el-select
|
||||
size="mini"
|
||||
style="width: 80px"
|
||||
v-model="style.associativeLineWidth"
|
||||
placeholder=""
|
||||
@change="
|
||||
value => {
|
||||
update('associativeLineWidth', value)
|
||||
}
|
||||
"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in lineWidthList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('baseStyle.associativeLineActiveColor') }}</span>
|
||||
<span
|
||||
class="block"
|
||||
v-popover:popover5
|
||||
:style="{ backgroundColor: style.associativeLineActiveColor }"
|
||||
></span>
|
||||
<el-popover ref="popover5" placement="bottom" trigger="click">
|
||||
<Color
|
||||
:color="style.associativeLineActiveColor"
|
||||
@change="
|
||||
color => {
|
||||
update('associativeLineActiveColor', color)
|
||||
}
|
||||
"
|
||||
></Color>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('baseStyle.associativeLineActiveWidth') }}</span>
|
||||
<el-select
|
||||
size="mini"
|
||||
style="width: 80px"
|
||||
v-model="style.associativeLineActiveWidth"
|
||||
placeholder=""
|
||||
@change="
|
||||
value => {
|
||||
update('associativeLineActiveWidth', value)
|
||||
}
|
||||
"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in lineWidthList"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 节点边框风格 -->
|
||||
<div class="title noTop">{{ $t('baseStyle.nodeBorderType') }}</div>
|
||||
<div class="row">
|
||||
@@ -498,6 +584,10 @@ export default {
|
||||
lineStyle: '',
|
||||
generalizationLineWidth: '',
|
||||
generalizationLineColor: '',
|
||||
associativeLineColor: '',
|
||||
associativeLineWidth: 0,
|
||||
associativeLineActiveWidth: 0,
|
||||
associativeLineActiveColor: '',
|
||||
paddingX: 0,
|
||||
paddingY: 0,
|
||||
imgMaxWidth: 0,
|
||||
@@ -579,6 +669,10 @@ export default {
|
||||
'lineColor',
|
||||
'generalizationLineWidth',
|
||||
'generalizationLineColor',
|
||||
'associativeLineColor',
|
||||
'associativeLineWidth',
|
||||
'associativeLineActiveWidth',
|
||||
'associativeLineActiveColor',
|
||||
'paddingX',
|
||||
'paddingY',
|
||||
'imgMaxWidth',
|
||||
|
||||
@@ -30,6 +30,7 @@ import Export from 'simple-mind-map/src/Export.js'
|
||||
import Drag from 'simple-mind-map/src/Drag.js'
|
||||
import Select from 'simple-mind-map/src/Select.js'
|
||||
import RichText from 'simple-mind-map/src/RichText.js'
|
||||
import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js'
|
||||
import Outline from './Outline'
|
||||
import Style from './Style'
|
||||
import BaseStyle from './BaseStyle'
|
||||
@@ -56,6 +57,7 @@ MindMap
|
||||
.usePlugin(KeyboardNavigation)
|
||||
.usePlugin(Export)
|
||||
.usePlugin(Select)
|
||||
.usePlugin(AssociativeLine)
|
||||
|
||||
// 注册自定义主题
|
||||
customThemeList.forEach((item) => {
|
||||
@@ -121,6 +123,9 @@ export default {
|
||||
this.$bus.$on('endTextEdit', () => {
|
||||
this.mindMap.renderer.endTextEdit()
|
||||
})
|
||||
this.$bus.$on('createAssociativeLine', () => {
|
||||
this.mindMap.associativeLine.createLineFromActiveNode()
|
||||
})
|
||||
if (this.openTest) {
|
||||
setTimeout(() => {
|
||||
this.test()
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
>
|
||||
<span class="customNode" slot-scope="{ node, data }">
|
||||
<span class="customNode" slot-scope="{ node, data }" @click="onClick($event, node)">
|
||||
<span
|
||||
class="nodeEdit"
|
||||
:key="getKey()"
|
||||
contenteditable="true"
|
||||
@keydown.stop
|
||||
@keydown.stop="onKeydown($event, node)"
|
||||
@keyup.stop
|
||||
@blur="onBlur($event, node)"
|
||||
v-html="node.label"
|
||||
@@ -48,7 +48,8 @@ export default {
|
||||
label(data) {
|
||||
return data.data.text.replaceAll(/\n/g, '</br>')
|
||||
}
|
||||
}
|
||||
},
|
||||
notHandleDataChange: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -65,6 +66,11 @@ export default {
|
||||
},
|
||||
created() {
|
||||
this.$bus.$on('data_change', data => {
|
||||
// 激活节点会让当前大纲失去焦点
|
||||
if (this.notHandleDataChange) {
|
||||
this.notHandleDataChange = false
|
||||
return
|
||||
}
|
||||
this.data = [this.mindMap.renderer.renderTree]
|
||||
})
|
||||
},
|
||||
@@ -75,7 +81,39 @@ export default {
|
||||
|
||||
getKey() {
|
||||
return Math.random()
|
||||
}
|
||||
},
|
||||
|
||||
onKeydown(e) {
|
||||
if (e.keyCode === 13 && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
this.insertNode()
|
||||
}
|
||||
if (e.keyCode === 9) {
|
||||
e.preventDefault()
|
||||
this.insertChildNode()
|
||||
}
|
||||
},
|
||||
|
||||
// 插入兄弟节点
|
||||
insertNode() {
|
||||
this.notHandleDataChange = false
|
||||
this.mindMap.execCommand('INSERT_NODE', false)
|
||||
},
|
||||
|
||||
// 插入下级节点
|
||||
insertChildNode() {
|
||||
this.notHandleDataChange = false
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false)
|
||||
},
|
||||
|
||||
// 激活当前节点且移动当前节点到画布中间
|
||||
onClick(e, data) {
|
||||
this.notHandleDataChange = true
|
||||
let node = data.data._node
|
||||
if (node.nodeData.data.isActive) return
|
||||
node.mindMap.renderer.moveNodeToCenter(node)
|
||||
node.active()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -113,6 +113,16 @@
|
||||
<span class="icon iconfont icongaikuozonglan"></span>
|
||||
<span class="text">{{ $t('toolbar.summary') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="toolbarBtn"
|
||||
:class="{
|
||||
disabled: activeNodes.length <= 0 || hasGeneralization
|
||||
}"
|
||||
@click="$bus.$emit('createAssociativeLine')"
|
||||
>
|
||||
<span class="icon iconfont iconlianjiexian"></span>
|
||||
<span class="text">{{ $t('toolbar.associativeLine') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 导出 -->
|
||||
<div class="toolbarBlock">
|
||||
|
||||
Reference in New Issue
Block a user