mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-24 10:28:26 +08:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9de1e675f | ||
|
|
b3e254c0ee | ||
|
|
bd3d470e40 | ||
|
|
21564445d6 | ||
|
|
e53d1b1948 | ||
|
|
c428f166a6 | ||
|
|
f19704ec41 | ||
|
|
d47f013f84 | ||
|
|
612d675a19 | ||
|
|
13f571e0b5 | ||
|
|
b09d408ab3 | ||
|
|
ea95ae2b5d | ||
|
|
2590e21807 | ||
|
|
fb41653d79 | ||
|
|
4c9c34a0ea | ||
|
|
0d5602b832 | ||
|
|
af87f84ce8 | ||
|
|
a96c16aa45 | ||
|
|
e29e1c26e5 | ||
|
|
fee38cbe8a | ||
|
|
58ca173234 | ||
|
|
aee10c6810 | ||
|
|
fe43be3451 | ||
|
|
21868c7f44 | ||
|
|
5585d2a4f7 | ||
|
|
1d04038609 | ||
|
|
305c5e9f70 | ||
|
|
4ac91003bb | ||
|
|
44410900aa | ||
|
|
239b369391 | ||
|
|
d9638fef98 | ||
|
|
ecb9482b82 |
@@ -171,4 +171,12 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/南风.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>南风</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/蜉蝣撼大叔.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>蜉蝣撼大叔</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/乙.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>乙</span>
|
||||
</span>
|
||||
</p>
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel="icon" href="dist/logo.ico"><title>思绪思维导图</title><script>// 自定义静态资源的路径
|
||||
window.externalPublicPath = './dist/'
|
||||
// 接管应用
|
||||
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?829fd06748ba83d9ecea" rel="stylesheet"><link href="dist/css/app.css?829fd06748ba83d9ecea" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
|
||||
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?8e81eea7e47ca2aeb9e0" rel="stylesheet"><link href="dist/css/app.css?8e81eea7e47ca2aeb9e0" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
@@ -66,4 +66,4 @@
|
||||
// 可以通过window.$bus.$on()来监听应用的一些事件
|
||||
// 实例化页面
|
||||
window.initApp()
|
||||
}</script><script src="dist/js/chunk-vendors.js?829fd06748ba83d9ecea"></script><script src="dist/js/app.js?829fd06748ba83d9ecea"></script></body></html>
|
||||
}</script><script src="dist/js/chunk-vendors.js?8e81eea7e47ca2aeb9e0"></script><script src="dist/js/app.js?8e81eea7e47ca2aeb9e0"></script></body></html>
|
||||
@@ -448,6 +448,7 @@ class MindMap {
|
||||
// 插件列表
|
||||
MindMap.pluginList = []
|
||||
MindMap.usePlugin = (plugin, opt = {}) => {
|
||||
if (MindMap.hasPlugin(plugin) !== -1) return
|
||||
plugin.pluginOpt = opt
|
||||
MindMap.pluginList.push(plugin)
|
||||
return MindMap
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -249,6 +249,10 @@ export const CONSTANTS = {
|
||||
PASTE_TYPE: {
|
||||
CLIP_BOARD: 'clipBoard',
|
||||
CANVAS: 'canvas'
|
||||
},
|
||||
SCROLL_BAR_DIR: {
|
||||
VERTICAL: 'vertical',
|
||||
HORIZONTAL: 'horizontal'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,5 +182,9 @@ export const defaultOpt = {
|
||||
// 节点鼠标hover和激活时显示的矩形边框距节点内容的距离
|
||||
hoverRectPadding: 2,
|
||||
// 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中
|
||||
selectTextOnEnterEditText: false
|
||||
selectTextOnEnterEditText: false,
|
||||
// 删除节点后激活相邻节点
|
||||
deleteNodeActive: true,
|
||||
// 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动
|
||||
autoMoveWhenMouseInEdgeOnDrag: true
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ class Render {
|
||||
// 检索某个节点在激活列表里的索引
|
||||
findActiveNodeIndex(node) {
|
||||
return this.activeNodeList.findIndex(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ class Render {
|
||||
getNodeIndex(node) {
|
||||
return node.parent
|
||||
? node.parent.children.findIndex(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
: 0
|
||||
}
|
||||
@@ -553,7 +553,7 @@ class Render {
|
||||
let parent = node.parent
|
||||
let childList = parent.children
|
||||
let index = childList.findIndex(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
if (index === -1 || index === 0) {
|
||||
return
|
||||
@@ -580,7 +580,7 @@ class Render {
|
||||
let parent = node.parent
|
||||
let childList = parent.children
|
||||
let index = childList.findIndex(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
if (index === -1 || index === childList.length - 1) {
|
||||
return
|
||||
@@ -752,7 +752,7 @@ class Render {
|
||||
let nodeParent = node.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
let nodeIndex = nodeBorthers.findIndex(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
return
|
||||
@@ -764,7 +764,7 @@ class Render {
|
||||
let existParent = exist.parent
|
||||
let existBorthers = existParent.children
|
||||
let existIndex = existBorthers.findIndex(item => {
|
||||
return item === exist
|
||||
return item.uid === exist.uid
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
@@ -791,7 +791,7 @@ class Render {
|
||||
let nodeParent = node.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
let nodeIndex = nodeBorthers.findIndex(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
if (nodeIndex === -1) {
|
||||
return
|
||||
@@ -803,7 +803,7 @@ class Render {
|
||||
let existParent = exist.parent
|
||||
let existBorthers = existParent.children
|
||||
let existIndex = existBorthers.findIndex(item => {
|
||||
return item === exist
|
||||
return item.uid === exist.uid
|
||||
})
|
||||
if (existIndex === -1) {
|
||||
return
|
||||
@@ -824,6 +824,8 @@ class Render {
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
// 删除节点后需要激活的节点
|
||||
let needActiveNode = null
|
||||
let isAppointNodes = appointNodes.length > 0
|
||||
let list = isAppointNodes ? appointNodes : this.activeNodeList
|
||||
let root = list.find(node => {
|
||||
@@ -837,6 +839,28 @@ class Render {
|
||||
root.children = []
|
||||
root.nodeData.children = []
|
||||
} else {
|
||||
// 如果只选中了一个节点,删除后激活其兄弟节点或者父节点
|
||||
if (
|
||||
this.activeNodeList.length === 1 &&
|
||||
!this.activeNodeList[0].isGeneralization &&
|
||||
this.mindMap.opt.deleteNodeActive
|
||||
) {
|
||||
const node = this.activeNodeList[0]
|
||||
const broList = node.parent.children
|
||||
const nodeIndex = broList.findIndex(item => item.uid === node.uid)
|
||||
// 如果后面有兄弟节点
|
||||
if (nodeIndex < broList.length - 1) {
|
||||
needActiveNode = broList[nodeIndex + 1]
|
||||
} else {
|
||||
// 如果前面有兄弟节点
|
||||
if (nodeIndex > 0) {
|
||||
needActiveNode = broList[nodeIndex - 1]
|
||||
} else {
|
||||
// 没有兄弟节点
|
||||
needActiveNode = node.parent
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let node = list[i]
|
||||
if (isAppointNodes) list.splice(i, 1)
|
||||
@@ -855,6 +879,13 @@ class Render {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.activeNodeList = []
|
||||
// 激活被删除节点的兄弟节点或父节点
|
||||
if (needActiveNode) {
|
||||
this.activeNodeList.push(needActiveNode)
|
||||
this.setNodeActive(needActiveNode, true)
|
||||
needActiveNode = null
|
||||
}
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
this.mindMap.render()
|
||||
}
|
||||
@@ -1093,7 +1124,13 @@ class Render {
|
||||
|
||||
// 设置节点图片
|
||||
setNodeImage(node, data) {
|
||||
const { url, title, width, height, custom = false } = data || { url: '', title: '', width: 0, height: 0, custom: false }
|
||||
const {
|
||||
url,
|
||||
title,
|
||||
width,
|
||||
height,
|
||||
custom = false
|
||||
} = data || { url: '', title: '', width: 0, height: 0, custom: false }
|
||||
this.setNodeDataRender(node, {
|
||||
image: url,
|
||||
imageTitle: title || '',
|
||||
|
||||
@@ -466,7 +466,7 @@ class Node {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
// 如果是多选节点结束,那么不要触发右键菜单事件
|
||||
if(!useLeftKeySelectionRightKeyDrag && this.mindMap.select.hasSelectRange()) {
|
||||
if(this.mindMap.select && !useLeftKeySelectionRightKeyDrag && this.mindMap.select.hasSelectRange()) {
|
||||
return
|
||||
}
|
||||
if (this.nodeData.data.isActive) {
|
||||
@@ -794,12 +794,12 @@ class Node {
|
||||
|
||||
// 检测当前节点是否是某个节点的祖先节点
|
||||
isParent(node) {
|
||||
if (this === node) {
|
||||
if (this.uid === node.uid) {
|
||||
return false
|
||||
}
|
||||
let parent = node.parent
|
||||
while (parent) {
|
||||
if (this === parent) {
|
||||
if (this.uid === parent.uid) {
|
||||
return true
|
||||
}
|
||||
parent = parent.parent
|
||||
@@ -809,11 +809,11 @@ class Node {
|
||||
|
||||
// 检测当前节点是否是某个节点的兄弟节点
|
||||
isBrother(node) {
|
||||
if (!this.parent || this === node) {
|
||||
if (!this.parent || this.uid === node.uid) {
|
||||
return false
|
||||
}
|
||||
return this.parent.children.find(item => {
|
||||
return item === node
|
||||
return item.uid === node.uid
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
tagColorList,
|
||||
nodeDataNoStylePropList
|
||||
} from '../../../constants/constant'
|
||||
import { tagColorList } from '../../../constants/constant'
|
||||
import { checkIsNodeStyleDataKey } from '../../../utils/index'
|
||||
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
const backgroundStyleProps = [
|
||||
'backgroundColor',
|
||||
@@ -225,7 +224,7 @@ class Style {
|
||||
hasCustomStyle() {
|
||||
let res = false
|
||||
Object.keys(this.ctx.nodeData.data).forEach(item => {
|
||||
if (!nodeDataNoStylePropList.includes(item)) {
|
||||
if (checkIsNodeStyleDataKey(item)) {
|
||||
res = true
|
||||
}
|
||||
})
|
||||
|
||||
@@ -171,7 +171,6 @@ class View {
|
||||
|
||||
// 平移x方式到
|
||||
translateXTo(x) {
|
||||
if (x === 0) return
|
||||
this.x = x
|
||||
this.transform()
|
||||
}
|
||||
@@ -185,7 +184,6 @@ class View {
|
||||
|
||||
// 平移y方向到
|
||||
translateYTo(y) {
|
||||
if (y === 0) return
|
||||
this.y = y
|
||||
this.transform()
|
||||
}
|
||||
|
||||
@@ -202,9 +202,13 @@ class Base {
|
||||
}
|
||||
|
||||
// 递归计算节点的宽度
|
||||
getNodeAreaWidth(node) {
|
||||
getNodeAreaWidth(node, withGeneralization = false) {
|
||||
let widthArr = []
|
||||
let totalGeneralizationNodeWidth = 0
|
||||
let loop = (node, width) => {
|
||||
if (withGeneralization && node.checkHasGeneralization()) {
|
||||
totalGeneralizationNodeWidth += node._generalizationNodeWidth
|
||||
}
|
||||
if (node.children.length) {
|
||||
width += node.width / 2
|
||||
node.children.forEach(item => {
|
||||
@@ -216,7 +220,7 @@ class Base {
|
||||
}
|
||||
}
|
||||
loop(node, 0)
|
||||
return Math.max(...widthArr)
|
||||
return Math.max(...widthArr) + totalGeneralizationNodeWidth
|
||||
}
|
||||
|
||||
// 二次贝塞尔曲线
|
||||
|
||||
@@ -87,11 +87,11 @@ class CatalogOrganization extends Base {
|
||||
totalLeft += cur.width + marginX
|
||||
})
|
||||
} else {
|
||||
let totalTop = node.top + node.height + marginY + (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
|
||||
let totalTop = node.top + this.getNodeHeightWithGeneralization(node) + marginY + (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
|
||||
node.children.forEach(cur => {
|
||||
cur.left = node.left + node.width * 0.5
|
||||
cur.top = totalTop
|
||||
totalTop += cur.height + marginY + (this.getNodeActChildrenLength(cur) > 0 ? cur.expandBtnSize : 0)
|
||||
totalTop += this.getNodeHeightWithGeneralization(cur) + marginY + (this.getNodeActChildrenLength(cur) > 0 ? cur.expandBtnSize : 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class CatalogOrganization extends Base {
|
||||
}
|
||||
// 调整left
|
||||
if (parent && parent.isRoot) {
|
||||
let areaWidth = this.getNodeAreaWidth(node)
|
||||
let areaWidth = this.getNodeAreaWidth(node, true)
|
||||
let difference = areaWidth - node.width
|
||||
if (difference > 0) {
|
||||
this.updateBrothersLeft(node, difference)
|
||||
@@ -124,7 +124,7 @@ class CatalogOrganization extends Base {
|
||||
let marginY = this.getMarginY(layerIndex + 1)
|
||||
let totalHeight =
|
||||
node.children.reduce((h, item) => {
|
||||
return h + item.height + (this.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
return h + this.getNodeHeightWithGeneralization(item) + (this.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
}, 0) +
|
||||
len * marginY
|
||||
this.updateBrothersTop(node, totalHeight)
|
||||
|
||||
@@ -56,6 +56,9 @@ class LogicalStructure extends Base {
|
||||
}, 0) +
|
||||
(len + 1) * this.getMarginY(layerIndex + 1)
|
||||
: 0
|
||||
// 如果存在概要,则和概要的高度取最大值
|
||||
let generalizationNodeHeight = cur._node.checkHasGeneralization() ? cur._node._generalizationNodeHeight + this.getMarginY(layerIndex + 1) : 0
|
||||
cur._node.childrenAreaHeight2 = Math.max(cur._node.childrenAreaHeight, generalizationNodeHeight)
|
||||
},
|
||||
true,
|
||||
0
|
||||
@@ -99,9 +102,8 @@ class LogicalStructure extends Base {
|
||||
}
|
||||
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
|
||||
let difference =
|
||||
node.childrenAreaHeight -
|
||||
this.getMarginY(layerIndex + 1) * 2 -
|
||||
node.height
|
||||
node.childrenAreaHeight2 -
|
||||
this.getMarginY(layerIndex + 1) * 2 - node.height
|
||||
if (difference > 0) {
|
||||
this.updateBrothers(node, difference / 2)
|
||||
}
|
||||
|
||||
@@ -90,6 +90,11 @@ class MindMap extends Base {
|
||||
cur._node.rightChildrenAreaHeight =
|
||||
rightChildrenAreaHeight +
|
||||
(rightLen + 1) * this.getMarginY(layerIndex + 1)
|
||||
|
||||
// 如果存在概要,则和概要的高度取最大值
|
||||
let generalizationNodeHeight = cur._node.checkHasGeneralization() ? cur._node._generalizationNodeHeight + this.getMarginY(layerIndex + 1) : 0
|
||||
cur._node.leftChildrenAreaHeight2 = Math.max(cur._node.leftChildrenAreaHeight, generalizationNodeHeight)
|
||||
cur._node.rightChildrenAreaHeight2 = Math.max(cur._node.rightChildrenAreaHeight, generalizationNodeHeight)
|
||||
},
|
||||
true,
|
||||
0
|
||||
@@ -139,8 +144,8 @@ class MindMap extends Base {
|
||||
}
|
||||
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
|
||||
let base = this.getMarginY(layerIndex + 1) * 2 + node.height
|
||||
let leftDifference = node.leftChildrenAreaHeight - base
|
||||
let rightDifference = node.rightChildrenAreaHeight - base
|
||||
let leftDifference = node.leftChildrenAreaHeight2 - base
|
||||
let rightDifference = node.rightChildrenAreaHeight2 - base
|
||||
if (leftDifference > 0 || rightDifference > 0) {
|
||||
this.updateBrothers(node, leftDifference / 2, rightDifference / 2)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ class OrganizationStructure extends Base {
|
||||
}, 0) +
|
||||
(len + 1) * this.getMarginY(layerIndex + 1)
|
||||
: 0
|
||||
|
||||
// 如果存在概要,则和概要的高度取最大值
|
||||
let generalizationNodeWidth = cur._node.checkHasGeneralization() ? cur._node._generalizationNodeWidth + this.getMarginY(layerIndex + 1) : 0
|
||||
cur._node.childrenAreaWidth2 = Math.max(cur._node.childrenAreaWidth, generalizationNodeWidth)
|
||||
},
|
||||
true,
|
||||
0
|
||||
@@ -100,7 +104,7 @@ class OrganizationStructure extends Base {
|
||||
}
|
||||
// 判断子节点所占的宽度之和是否大于该节点自身,大于则需要调整位置
|
||||
let difference =
|
||||
node.childrenAreaWidth -
|
||||
node.childrenAreaWidth2 -
|
||||
this.getMarginY(layerIndex + 1) * 2 -
|
||||
node.width
|
||||
if (difference > 0) {
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
getTextFromHtml,
|
||||
imgToDataUrl,
|
||||
parseDataUrl,
|
||||
getImageSize
|
||||
getImageSize,
|
||||
isUndef
|
||||
} from '../utils/index'
|
||||
|
||||
// 解析.xmind文件
|
||||
@@ -49,7 +50,7 @@ const transformXmind = async (content, files) => {
|
||||
let walk = async (node, newNode) => {
|
||||
newNode.data = {
|
||||
// 节点内容
|
||||
text: node.title
|
||||
text: isUndef(node.title) ? '' : node.title
|
||||
}
|
||||
// 节点备注
|
||||
if (node.notes) {
|
||||
@@ -146,9 +147,10 @@ const transformOldXmind = content => {
|
||||
let walk = (node, newNode) => {
|
||||
let nodeElements = node.elements
|
||||
let nodeTitle = getItemByName(nodeElements, 'title')
|
||||
nodeTitle = nodeTitle && nodeTitle.elements && nodeTitle.elements[0].text
|
||||
newNode.data = {
|
||||
// 节点内容
|
||||
text: nodeTitle && nodeTitle.elements && nodeTitle.elements[0].text
|
||||
text: isUndef(nodeTitle) ? '' : nodeTitle
|
||||
}
|
||||
try {
|
||||
// 节点备注
|
||||
@@ -215,6 +217,7 @@ const transformToXmind = async (data, name) => {
|
||||
let waitLoadImageList = []
|
||||
let walk = async (node, newNode, isRoot) => {
|
||||
let newData = {
|
||||
id: node.data.uid,
|
||||
structureClass: 'org.xmind.ui.logic.right',
|
||||
title: getTextFromHtml(node.data.text), // 节点文本
|
||||
children: {
|
||||
@@ -242,20 +245,20 @@ const transformToXmind = async (data, name) => {
|
||||
}
|
||||
// 图片
|
||||
if (node.data.image) {
|
||||
// 处理异步逻辑
|
||||
let resolve = null
|
||||
let promise = new Promise(_resolve => {
|
||||
resolve = _resolve
|
||||
})
|
||||
waitLoadImageList.push(promise)
|
||||
try {
|
||||
// 处理异步逻辑
|
||||
let resolve = null
|
||||
let promise = new Promise(_resolve => {
|
||||
resolve = _resolve
|
||||
})
|
||||
waitLoadImageList.push(promise)
|
||||
let imgName = ''
|
||||
let imgData = node.data.image
|
||||
// 网络图片要先转换成data:url
|
||||
if (/^https?:\/\//.test(node.data.image)) {
|
||||
// base64之外的其他图片要先转换成data:url
|
||||
if (!/^data:/.test(node.data.image)) {
|
||||
imgData = await imgToDataUrl(node.data.image)
|
||||
}
|
||||
// 从data:url中解析出图片类型和base64
|
||||
// 从data:url中解析出图片类型和ase64
|
||||
let dataUrlRes = parseDataUrl(imgData)
|
||||
imgName = 'image_' + imageList.length + '.' + dataUrlRes.type
|
||||
imageList.push({
|
||||
|
||||
@@ -362,7 +362,7 @@ class AssociativeLine {
|
||||
if (node.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
}
|
||||
if (node === this.creatingStartNode || this.overlapNode) {
|
||||
if (node.uid === this.creatingStartNode.uid || this.overlapNode) {
|
||||
return
|
||||
}
|
||||
let { left, top, width, height } = node
|
||||
@@ -379,7 +379,7 @@ class AssociativeLine {
|
||||
|
||||
// 完成创建连接线
|
||||
completeCreateLine(node) {
|
||||
if (this.creatingStartNode === node) return
|
||||
if (this.creatingStartNode.uid === node.uid) return
|
||||
this.addLine(this.creatingStartNode, node)
|
||||
if (this.overlapNode && this.overlapNode.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
|
||||
@@ -433,7 +433,7 @@ class AssociativeLine {
|
||||
]
|
||||
let associativeLinePoint = fromNode.nodeData.data.associativeLinePoint || []
|
||||
// 记录关联的起始|结束坐标
|
||||
associativeLinePoint[list.length - 1] = [{ startPoint, endPoint }]
|
||||
associativeLinePoint[list.length - 1] = { startPoint, endPoint }
|
||||
this.mindMap.execCommand('SET_NODE_DATA', fromNode, {
|
||||
associativeLineTargets: list,
|
||||
associativeLineTargetControlOffsets: offsetList,
|
||||
|
||||
@@ -13,6 +13,8 @@ class Drag extends Base {
|
||||
|
||||
// 复位
|
||||
reset() {
|
||||
// 当前画布节点列表
|
||||
this.nodeList = []
|
||||
// 当前拖拽节点
|
||||
this.node = null
|
||||
// 当前重叠节点
|
||||
@@ -68,6 +70,7 @@ class Drag extends Base {
|
||||
this.isMousedown = true
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
this.nodeTreeToList()
|
||||
})
|
||||
this.mindMap.on('mousemove', e => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
@@ -89,7 +92,7 @@ class Drag extends Base {
|
||||
return
|
||||
}
|
||||
this.mindMap.renderer.clearAllActive()
|
||||
this.onMove(x, y)
|
||||
this.onMove(x, y, e)
|
||||
})
|
||||
this.onMouseup = this.onMouseup.bind(this)
|
||||
this.mindMap.on('node_mouseup', this.onMouseup)
|
||||
@@ -106,7 +109,9 @@ class Drag extends Base {
|
||||
this.node.isDrag = false
|
||||
this.node.show()
|
||||
this.removeCloneNode()
|
||||
let overlapNodeUid = this.overlapNode ? this.overlapNode.nodeData.data.uid : ''
|
||||
let overlapNodeUid = this.overlapNode
|
||||
? this.overlapNode.nodeData.data.uid
|
||||
: ''
|
||||
let prevNodeUid = this.prevNode ? this.prevNode.nodeData.data.uid : ''
|
||||
let nextNodeUid = this.nextNode ? this.nextNode.nodeData.data.uid : ''
|
||||
// 存在重叠子节点,则移动作为其子节点
|
||||
@@ -177,7 +182,7 @@ class Drag extends Base {
|
||||
}
|
||||
|
||||
// 拖动中
|
||||
onMove(x, y) {
|
||||
onMove(x, y, e) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
@@ -200,6 +205,12 @@ class Drag extends Base {
|
||||
)
|
||||
)
|
||||
this.checkOverlapNode()
|
||||
// 如果注册了多选节点插件,那么复用它的边缘自动移动画布功能
|
||||
if (this.mindMap.opt.autoMoveWhenMouseInEdgeOnDrag && this.mindMap.select) {
|
||||
this.drawTransform = this.mindMap.draw.transform()
|
||||
this.mindMap.select.clearAutoMoveTimer()
|
||||
this.mindMap.select.onMove(e.clientX, e.clientY)
|
||||
}
|
||||
}
|
||||
|
||||
// 检测重叠节点
|
||||
@@ -207,90 +218,47 @@ class Drag extends Base {
|
||||
if (!this.drawTransform || !this.placeholder) {
|
||||
return
|
||||
}
|
||||
const { nodeDragPlaceholderMaxSize } = this.mindMap.opt
|
||||
let x = this.mouseMoveX
|
||||
let y = this.mouseMoveY
|
||||
this.overlapNode = null
|
||||
this.prevNode = null
|
||||
this.nextNode = null
|
||||
this.placeholder.size(0, 0)
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
this.nodeList.forEach(node => {
|
||||
if (node.nodeData.data.isActive) {
|
||||
this.mindMap.renderer.setNodeActive(node, false)
|
||||
}
|
||||
if (node === this.node || this.node.isParent(node)) {
|
||||
if (node.uid === this.node.uid) {
|
||||
return
|
||||
}
|
||||
if (this.overlapNode || (this.prevNode && this.nextNode)) {
|
||||
return
|
||||
}
|
||||
let nodeRect = this.getNodeRect(node)
|
||||
let oneFourthHeight = nodeRect.height / 4
|
||||
// 前一个和后一个节点
|
||||
let checkList = node.parent ? node.parent.children.filter((item) => {
|
||||
return item !== this.node
|
||||
}) : []
|
||||
let index = checkList.findIndex((item) => {
|
||||
return item === node
|
||||
})
|
||||
let prevBrother = null
|
||||
let nextBrother = null
|
||||
if (index !== -1) {
|
||||
if (index - 1 >= 0) {
|
||||
prevBrother = checkList[index - 1]
|
||||
}
|
||||
if (index + 1 <= checkList.length - 1) {
|
||||
nextBrother = checkList[index + 1]
|
||||
}
|
||||
}
|
||||
// 和前一个兄弟节点的距离
|
||||
let prevBrotherOffset = 0
|
||||
if (prevBrother) {
|
||||
let prevNodeRect = this.getNodeRect(prevBrother)
|
||||
prevBrotherOffset = nodeRect.top - prevNodeRect.bottom
|
||||
// 间距小于10就当它不存在
|
||||
prevBrotherOffset = prevBrotherOffset >= this.minOffset ? prevBrotherOffset / 2 : 0
|
||||
} else {
|
||||
// 没有前一个兄弟节点,那么假设和前一个节点的距离为20
|
||||
prevBrotherOffset = this.minOffset
|
||||
}
|
||||
// 和后一个兄弟节点的距离
|
||||
let nextBrotherOffset = 0
|
||||
if (nextBrother) {
|
||||
let nextNodeRect = this.getNodeRect(nextBrother)
|
||||
nextBrotherOffset = nextNodeRect.top - nodeRect.bottom
|
||||
nextBrotherOffset = nextBrotherOffset >= this.minOffset ? nextBrotherOffset / 2 : 0
|
||||
} else {
|
||||
nextBrotherOffset = this.minOffset
|
||||
}
|
||||
if (nodeRect.left <= x && nodeRect.right >= x) {
|
||||
// 检测兄弟节点位置
|
||||
if (!this.overlapNode && !this.prevNode && !this.nextNode && !node.isRoot) {
|
||||
let checkIsPrevNode = nextBrotherOffset > 0 ? // 距离下一个兄弟节点的距离大于0
|
||||
y > nodeRect.bottom && y <= (nodeRect.bottom + nextBrotherOffset) : // 那么在当前节点外底部判断
|
||||
y >= nodeRect.bottom - oneFourthHeight && y <= nodeRect.bottom // 否则在当前节点内底部1/4区间判断
|
||||
let checkIsNextNode = prevBrotherOffset > 0 ? // 距离上一个兄弟节点的距离大于0
|
||||
y < nodeRect.top && y >= (nodeRect.top - prevBrotherOffset) : // 那么在当前节点外底部判断
|
||||
y >= nodeRect.top && y <= nodeRect.top + oneFourthHeight
|
||||
if (checkIsPrevNode) {
|
||||
this.prevNode = node
|
||||
let size = nextBrotherOffset > 0 ? Math.min(nextBrotherOffset, nodeDragPlaceholderMaxSize) : 5
|
||||
this.placeholder.size(node.width, size).move(nodeRect.originLeft, nodeRect.originBottom)
|
||||
} else if (checkIsNextNode) {
|
||||
this.nextNode = node
|
||||
let size = prevBrotherOffset > 0 ? Math.min(prevBrotherOffset, nodeDragPlaceholderMaxSize) : 5
|
||||
this.placeholder.size(node.width, size).move(nodeRect.originLeft, nodeRect.originTop - size)
|
||||
}
|
||||
}
|
||||
// 检测是否重叠
|
||||
if (!this.overlapNode && !this.prevNode && !this.nextNode) {
|
||||
if (
|
||||
nodeRect.top + (prevBrotherOffset > 0 ? 0 : oneFourthHeight) <= y &&
|
||||
nodeRect.bottom - (nextBrotherOffset > 0 ? 0 : oneFourthHeight) >= y
|
||||
) {
|
||||
this.overlapNode = node
|
||||
}
|
||||
}
|
||||
switch (this.mindMap.opt.layout) {
|
||||
case 'logicalStructure':
|
||||
this.handleLogicalStructure(node)
|
||||
break
|
||||
case 'mindMap':
|
||||
this.handleMindMap(node)
|
||||
break
|
||||
case 'organizationStructure':
|
||||
this.handleOrganizationStructure(node)
|
||||
break
|
||||
case 'catalogOrganization':
|
||||
this.handleCatalogOrganization(node)
|
||||
break
|
||||
case 'timeline':
|
||||
this.handleTimeLine(node)
|
||||
break
|
||||
case 'timeline2':
|
||||
this.handleTimeLine2(node)
|
||||
break
|
||||
case 'verticalTimeline':
|
||||
this.handleLogicalStructure(node)
|
||||
break
|
||||
case 'fishbone':
|
||||
this.handleFishbone(node)
|
||||
break
|
||||
default:
|
||||
this.handleLogicalStructure(node)
|
||||
}
|
||||
})
|
||||
if (this.overlapNode) {
|
||||
@@ -298,6 +266,299 @@ class Drag extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
// 垂直方向比较
|
||||
// isReverse:是否反向
|
||||
handleVerticalCheck(node, checkList, isReverse = false) {
|
||||
let x = this.mouseMoveX
|
||||
let y = this.mouseMoveY
|
||||
let nodeRect = this.getNodeRect(node)
|
||||
if (isReverse) {
|
||||
checkList = checkList.reverse()
|
||||
}
|
||||
let oneFourthHeight = nodeRect.height / 4
|
||||
let { prevBrotherOffset, nextBrotherOffset } =
|
||||
this.getNodeDistanceToSiblingNode(checkList, node, nodeRect, 'v')
|
||||
if (nodeRect.left <= x && nodeRect.right >= x) {
|
||||
// 检测兄弟节点位置
|
||||
if (
|
||||
!this.overlapNode &&
|
||||
!this.prevNode &&
|
||||
!this.nextNode &&
|
||||
!node.isRoot
|
||||
) {
|
||||
let checkIsPrevNode =
|
||||
nextBrotherOffset > 0 // 距离下一个兄弟节点的距离大于0
|
||||
? y > nodeRect.bottom && y <= nodeRect.bottom + nextBrotherOffset // 那么在当前节点外底部判断
|
||||
: y >= nodeRect.bottom - oneFourthHeight && y <= nodeRect.bottom // 否则在当前节点内底部1/4区间判断
|
||||
let checkIsNextNode =
|
||||
prevBrotherOffset > 0 // 距离上一个兄弟节点的距离大于0
|
||||
? y < nodeRect.top && y >= nodeRect.top - prevBrotherOffset // 那么在当前节点外底部判断
|
||||
: y >= nodeRect.top && y <= nodeRect.top + oneFourthHeight
|
||||
if (checkIsPrevNode) {
|
||||
if (isReverse) {
|
||||
this.nextNode = node
|
||||
} else {
|
||||
this.prevNode = node
|
||||
}
|
||||
let size = this.formatPlaceholderSize(nextBrotherOffset)
|
||||
this.setPlaceholderRect(
|
||||
node.width,
|
||||
size,
|
||||
nodeRect.originLeft,
|
||||
nodeRect.originBottom
|
||||
)
|
||||
} else if (checkIsNextNode) {
|
||||
if (isReverse) {
|
||||
this.prevNode = node
|
||||
} else {
|
||||
this.nextNode = node
|
||||
}
|
||||
let size = this.formatPlaceholderSize(prevBrotherOffset)
|
||||
this.setPlaceholderRect(
|
||||
node.width,
|
||||
size,
|
||||
nodeRect.originLeft,
|
||||
nodeRect.originTop - size
|
||||
)
|
||||
}
|
||||
}
|
||||
// 检测是否重叠
|
||||
this.checkIsOverlap({
|
||||
node,
|
||||
dir: 'v',
|
||||
prevBrotherOffset,
|
||||
nextBrotherOffset,
|
||||
size: oneFourthHeight,
|
||||
pos: y,
|
||||
nodeRect
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 水平方向比较
|
||||
handleHorizontalCheck(node, checkList) {
|
||||
let x = this.mouseMoveX
|
||||
let y = this.mouseMoveY
|
||||
let nodeRect = this.getNodeRect(node)
|
||||
let oneFourthWidth = nodeRect.width / 4
|
||||
let { prevBrotherOffset, nextBrotherOffset } =
|
||||
this.getNodeDistanceToSiblingNode(checkList, node, nodeRect, 'h')
|
||||
if (nodeRect.top <= y && nodeRect.bottom >= y) {
|
||||
// 检测兄弟节点位置
|
||||
if (
|
||||
!this.overlapNode &&
|
||||
!this.prevNode &&
|
||||
!this.nextNode &&
|
||||
!node.isRoot
|
||||
) {
|
||||
let checkIsPrevNode =
|
||||
nextBrotherOffset > 0 // 距离下一个兄弟节点的距离大于0
|
||||
? x < nodeRect.right + nextBrotherOffset && x >= nodeRect.right // 那么在当前节点外底部判断
|
||||
: x <= nodeRect.right && x >= nodeRect.right - oneFourthWidth // 否则在当前节点内底部1/4区间判断
|
||||
let checkIsNextNode =
|
||||
prevBrotherOffset > 0 // 距离上一个兄弟节点的距离大于0
|
||||
? x > nodeRect.left - prevBrotherOffset && x <= nodeRect.left // 那么在当前节点外底部判断
|
||||
: x <= nodeRect.left + oneFourthWidth && x >= nodeRect.left
|
||||
if (checkIsPrevNode) {
|
||||
this.prevNode = node
|
||||
let size = this.formatPlaceholderSize(nextBrotherOffset)
|
||||
this.setPlaceholderRect(
|
||||
size,
|
||||
node.height,
|
||||
nodeRect.originRight,
|
||||
nodeRect.originTop
|
||||
)
|
||||
} else if (checkIsNextNode) {
|
||||
this.nextNode = node
|
||||
let size = this.formatPlaceholderSize(prevBrotherOffset)
|
||||
this.setPlaceholderRect(
|
||||
size,
|
||||
node.height,
|
||||
nodeRect.originLeft - size,
|
||||
nodeRect.originTop
|
||||
)
|
||||
}
|
||||
}
|
||||
// 检测是否重叠
|
||||
this.checkIsOverlap({
|
||||
node,
|
||||
dir: 'h',
|
||||
prevBrotherOffset,
|
||||
nextBrotherOffset,
|
||||
size: oneFourthWidth,
|
||||
pos: x,
|
||||
nodeRect
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点距前一个和后一个节点的距离
|
||||
getNodeDistanceToSiblingNode(checkList, node, nodeRect, dir) {
|
||||
let dir1 = dir === 'v' ? 'top' : 'left'
|
||||
let dir2 = dir === 'v' ? 'bottom' : 'right'
|
||||
let index = checkList.findIndex(item => {
|
||||
return item.uid === node.uid
|
||||
})
|
||||
let prevBrother = null
|
||||
let nextBrother = null
|
||||
if (index !== -1) {
|
||||
if (index - 1 >= 0) {
|
||||
prevBrother = checkList[index - 1]
|
||||
}
|
||||
if (index + 1 <= checkList.length - 1) {
|
||||
nextBrother = checkList[index + 1]
|
||||
}
|
||||
}
|
||||
// 和前一个兄弟节点的距离
|
||||
let prevBrotherOffset = 0
|
||||
if (prevBrother) {
|
||||
let prevNodeRect = this.getNodeRect(prevBrother)
|
||||
prevBrotherOffset = nodeRect[dir1] - prevNodeRect[dir2]
|
||||
// 间距小于10就当它不存在
|
||||
prevBrotherOffset =
|
||||
prevBrotherOffset >= this.minOffset ? prevBrotherOffset / 2 : 0
|
||||
} else {
|
||||
// 没有前一个兄弟节点,那么假设和前一个节点的距离为20
|
||||
prevBrotherOffset = this.minOffset
|
||||
}
|
||||
// 和后一个兄弟节点的距离
|
||||
let nextBrotherOffset = 0
|
||||
if (nextBrother) {
|
||||
let nextNodeRect = this.getNodeRect(nextBrother)
|
||||
nextBrotherOffset = nextNodeRect[dir1] - nodeRect[dir2]
|
||||
nextBrotherOffset =
|
||||
nextBrotherOffset >= this.minOffset ? nextBrotherOffset / 2 : 0
|
||||
} else {
|
||||
nextBrotherOffset = this.minOffset
|
||||
}
|
||||
return {
|
||||
prevBrotherOffset,
|
||||
nextBrotherOffset
|
||||
}
|
||||
}
|
||||
|
||||
// 处理提示元素的大小
|
||||
formatPlaceholderSize(size) {
|
||||
const { nodeDragPlaceholderMaxSize } = this.mindMap.opt
|
||||
return size > 0 ? Math.min(size, nodeDragPlaceholderMaxSize) : 5
|
||||
}
|
||||
|
||||
// 设置提示元素的大小和位置
|
||||
setPlaceholderRect(w, h, x, y) {
|
||||
this.placeholder.size(w, h).move(x, y)
|
||||
}
|
||||
|
||||
// 检测是否重叠
|
||||
checkIsOverlap({
|
||||
node,
|
||||
dir,
|
||||
prevBrotherOffset,
|
||||
nextBrotherOffset,
|
||||
size,
|
||||
pos,
|
||||
nodeRect
|
||||
}) {
|
||||
let dir1 = dir === 'v' ? 'top' : 'left'
|
||||
let dir2 = dir === 'v' ? 'bottom' : 'right'
|
||||
if (!this.overlapNode && !this.prevNode && !this.nextNode) {
|
||||
if (
|
||||
nodeRect[dir1] + (prevBrotherOffset > 0 ? 0 : size) <= pos &&
|
||||
nodeRect[dir2] - (nextBrotherOffset > 0 ? 0 : size) >= pos
|
||||
) {
|
||||
this.overlapNode = node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理逻辑结构图
|
||||
handleLogicalStructure(node) {
|
||||
const checkList = this.commonGetNodeCheckList(node)
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
}
|
||||
|
||||
// 处理思维导图
|
||||
handleMindMap(node) {
|
||||
const checkList = node.parent
|
||||
? node.parent.children.filter(item => {
|
||||
let sameDir = true
|
||||
if (node.layerIndex === 1) {
|
||||
sameDir = item.dir === node.dir
|
||||
}
|
||||
return item !== this.node && sameDir
|
||||
})
|
||||
: []
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
}
|
||||
|
||||
// 处理组织结构图
|
||||
handleOrganizationStructure(node) {
|
||||
const checkList = this.commonGetNodeCheckList(node)
|
||||
this.handleHorizontalCheck(node, checkList)
|
||||
}
|
||||
|
||||
// 处理目录组织图
|
||||
handleCatalogOrganization(node) {
|
||||
const checkList = this.commonGetNodeCheckList(node)
|
||||
if (node.layerIndex === 1) {
|
||||
this.handleHorizontalCheck(node, checkList)
|
||||
} else {
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理时间轴
|
||||
handleTimeLine(node) {
|
||||
let checkList = this.commonGetNodeCheckList(node)
|
||||
if (node.layerIndex === 1) {
|
||||
this.handleHorizontalCheck(node, checkList)
|
||||
} else {
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理时间轴2
|
||||
handleTimeLine2(node) {
|
||||
let checkList = this.commonGetNodeCheckList(node)
|
||||
if (node.layerIndex === 1) {
|
||||
this.handleHorizontalCheck(node, checkList)
|
||||
} else {
|
||||
// 处于上方的三级节点需要特殊处理,因为节点排列方向反向了
|
||||
if (node.dir === 'top' && node.layerIndex === 2) {
|
||||
this.handleVerticalCheck(node, checkList, true)
|
||||
} else {
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理鱼骨图
|
||||
handleFishbone(node) {
|
||||
let checkList = node.parent
|
||||
? node.parent.children.filter(item => {
|
||||
return item !== this.node && item.layerIndex > 1
|
||||
})
|
||||
: []
|
||||
if (node.layerIndex === 1) {
|
||||
this.handleHorizontalCheck(node, checkList)
|
||||
} else {
|
||||
// 处于上方的三级节点需要特殊处理,因为节点排列方向反向了
|
||||
if (node.dir === 'top' && node.layerIndex === 2) {
|
||||
this.handleVerticalCheck(node, checkList, true)
|
||||
} else {
|
||||
this.handleVerticalCheck(node, checkList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点的兄弟节点列表通用方法
|
||||
commonGetNodeCheckList(node) {
|
||||
return node.parent
|
||||
? node.parent.children.filter(item => {
|
||||
return item !== this.node
|
||||
})
|
||||
: []
|
||||
}
|
||||
|
||||
// 计算节点的位置尺寸信息
|
||||
getNodeRect(node) {
|
||||
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
|
||||
@@ -305,6 +566,7 @@ class Drag extends Base {
|
||||
let originLeft = left
|
||||
let originTop = top
|
||||
let originBottom = top + height
|
||||
let originRight = left + width
|
||||
let right = (left + width) * scaleX + translateX
|
||||
let bottom = (top + height) * scaleY + translateY
|
||||
left = left * scaleX + translateX
|
||||
@@ -318,9 +580,24 @@ class Drag extends Base {
|
||||
bottom,
|
||||
originLeft,
|
||||
originTop,
|
||||
originBottom
|
||||
originBottom,
|
||||
originRight
|
||||
}
|
||||
}
|
||||
|
||||
// 节点由树转换成数组,从子节点到根节点
|
||||
nodeTreeToList() {
|
||||
const list = []
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (!list[node.layerIndex]) {
|
||||
list[node.layerIndex] = []
|
||||
}
|
||||
list[node.layerIndex].push(node)
|
||||
})
|
||||
this.nodeList = list.reduceRight((res, cur) => {
|
||||
return [...res, ...cur]
|
||||
}, [])
|
||||
}
|
||||
}
|
||||
|
||||
Drag.instanceName = 'drag'
|
||||
|
||||
@@ -94,7 +94,7 @@ class KeyboardNavigation {
|
||||
// 遍历节点树
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
// 跳过当前聚焦的节点
|
||||
if (node === currentActiveNode) return
|
||||
if (node.uid === currentActiveNode.uid) return
|
||||
// 当前遍历到的节点的位置信息
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
@@ -131,7 +131,7 @@ class KeyboardNavigation {
|
||||
checkNodeDis
|
||||
}) {
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (node === currentActiveNode) return
|
||||
if (node.uid === currentActiveNode.uid) return
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
let match = false
|
||||
@@ -173,7 +173,7 @@ class KeyboardNavigation {
|
||||
let cX = (currentActiveNodeRect.right + currentActiveNodeRect.left) / 2
|
||||
let cY = (currentActiveNodeRect.bottom + currentActiveNodeRect.top) / 2
|
||||
bfsWalk(this.mindMap.renderer.root, node => {
|
||||
if (node === currentActiveNode) return
|
||||
if (node.uid === currentActiveNode.uid) return
|
||||
let rect = this.getNodeRect(node)
|
||||
let { left, top, right, bottom } = rect
|
||||
// 遍历到的节点的中心点
|
||||
|
||||
@@ -49,7 +49,7 @@ class NodeImgAdjust {
|
||||
// 如果当前正在拖动调整中那么直接返回
|
||||
if (this.isMousedown || this.isAdjusted || this.mindMap.opt.readonly) return
|
||||
// 如果在当前节点内移动,以及自定义元素已经是显示状态,那么直接返回
|
||||
if (this.node === node && this.isShowHandleEl) return
|
||||
if ((this.node && this.node.uid === node.uid) && this.isShowHandleEl) return
|
||||
// 更新当前节点信息
|
||||
this.node = node
|
||||
this.img = img
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { nodeDataNoStylePropList } from '../constants/constant'
|
||||
import { checkIsNodeStyleDataKey } from '../utils/index'
|
||||
|
||||
// 格式刷插件
|
||||
class Painter {
|
||||
@@ -49,13 +49,13 @@ class Painter {
|
||||
!this.isInPainter ||
|
||||
!this.painterNode ||
|
||||
!node ||
|
||||
node === this.painterNode
|
||||
node.uid === this.painterNode.uid
|
||||
)
|
||||
return
|
||||
const style = {}
|
||||
const painterNodeData = this.painterNode.nodeData.data
|
||||
Object.keys(painterNodeData).forEach(key => {
|
||||
if (!nodeDataNoStylePropList.includes(key)) {
|
||||
if (checkIsNodeStyleDataKey(key)) {
|
||||
style[key] = painterNodeData[key]
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { throttle } from '../utils/index'
|
||||
import { CONSTANTS } from '../constants/constant'
|
||||
|
||||
// 滚动条插件
|
||||
class Scrollbar {
|
||||
@@ -9,45 +10,13 @@ class Scrollbar {
|
||||
width: 0, // 水平滚动条的容器宽度
|
||||
height: 0 // 垂直滚动条的容器高度
|
||||
}
|
||||
// 思维导图实际高度
|
||||
this.chartHeight = 0
|
||||
this.chartWidth = 0
|
||||
this.reset()
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
this.onMousemove = this.onMousemove.bind(this)
|
||||
this.onMouseup = this.onMouseup.bind(this)
|
||||
this.onNodeTreeRenderEnd = this.onNodeTreeRenderEnd.bind(this)
|
||||
this.onViewDataChange = throttle(this.onViewDataChange, 16, this) // 加个节流
|
||||
this.mindMap.on('mousemove', this.onMousemove)
|
||||
this.mindMap.on('mouseup', this.onMouseup)
|
||||
this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd)
|
||||
this.mindMap.on('view_data_change', this.onViewDataChange)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
unBindEvent() {
|
||||
this.mindMap.off('mousemove', this.onMousemove)
|
||||
this.mindMap.off('mouseup', this.onMouseup)
|
||||
this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd)
|
||||
this.mindMap.off('view_data_change', this.onViewDataChange)
|
||||
}
|
||||
|
||||
// 每次渲染后需要更新滚动条
|
||||
onNodeTreeRenderEnd() {
|
||||
this.emitEvent()
|
||||
}
|
||||
|
||||
// 思维导图视图数据改变需要更新滚动条
|
||||
onViewDataChange() {
|
||||
this.emitEvent()
|
||||
}
|
||||
|
||||
// 发送滚动条改变事件
|
||||
emitEvent() {
|
||||
this.mindMap.emit('scrollbar_change')
|
||||
}
|
||||
|
||||
// 复位数据
|
||||
reset() {
|
||||
// 当前拖拽的滚动条类型
|
||||
@@ -57,13 +26,41 @@ class Scrollbar {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
this.startViewPos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
// 思维导图实际高度
|
||||
this.chartHeight = 0
|
||||
this.chartWidth = 0
|
||||
// 鼠标按下时,滚动条位置
|
||||
this.mousedownScrollbarPos = 0
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
this.onMousemove = this.onMousemove.bind(this)
|
||||
this.onMouseup = this.onMouseup.bind(this)
|
||||
this.updateScrollbar = this.updateScrollbar.bind(this)
|
||||
this.updateScrollbar = throttle(this.updateScrollbar, 16, this) // 加个节流
|
||||
this.mindMap.on('mousemove', this.onMousemove)
|
||||
this.mindMap.on('mouseup', this.onMouseup)
|
||||
this.mindMap.on('node_tree_render_end', this.updateScrollbar)
|
||||
this.mindMap.on('view_data_change', this.updateScrollbar)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
unBindEvent() {
|
||||
this.mindMap.off('mousemove', this.onMousemove)
|
||||
this.mindMap.off('mouseup', this.onMouseup)
|
||||
this.mindMap.off('node_tree_render_end', this.updateScrollbar)
|
||||
this.mindMap.off('view_data_change', this.updateScrollbar)
|
||||
}
|
||||
|
||||
// 渲染后、数据改变需要更新滚动条
|
||||
updateScrollbar() {
|
||||
// 当前正在拖拽滚动条时不需要更新
|
||||
if (this.isMousedown) return
|
||||
const res = this.calculationScrollbar()
|
||||
this.emitEvent(res)
|
||||
}
|
||||
|
||||
// 发送滚动条改变事件
|
||||
emitEvent(data) {
|
||||
this.mindMap.emit('scrollbar_change', data)
|
||||
}
|
||||
|
||||
// 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度
|
||||
@@ -78,9 +75,7 @@ class Scrollbar {
|
||||
// 减去画布距离浏览器窗口左上角的距离
|
||||
const elRect = this.mindMap.elRect
|
||||
rect.x -= elRect.left
|
||||
rect.x2 -= elRect.left
|
||||
rect.y -= elRect.top
|
||||
rect.y2 -= elRect.top
|
||||
|
||||
// 垂直滚动条
|
||||
const canvasHeight = this.mindMap.height // 画布高度
|
||||
@@ -129,46 +124,129 @@ class Scrollbar {
|
||||
return res
|
||||
}
|
||||
|
||||
// 滚动条鼠标按下事件处理函数
|
||||
onMousedown(e, type) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
this.currentScrollType = type
|
||||
this.isMousedown = true
|
||||
this.mousedownPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
}
|
||||
// 保存视图当前的偏移量
|
||||
let transformData = this.mindMap.view.getTransformData()
|
||||
this.startViewPos = {
|
||||
x: transformData.state.x,
|
||||
y: transformData.state.y
|
||||
// 保存滚动条当前的位置
|
||||
const styles = window.getComputedStyle(e.target)
|
||||
if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
||||
this.mousedownScrollbarPos = Number.parseFloat(styles.top)
|
||||
} else {
|
||||
this.mousedownScrollbarPos = Number.parseFloat(styles.left)
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移动事件处理函数
|
||||
onMousemove(e) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
if (this.currentScrollType === 'vertical') {
|
||||
const oy = e.clientY - this.mousedownPos.y
|
||||
const oyPercentage = -oy / this.scrollbarWrapSize.height
|
||||
const oyPx = oyPercentage * this.chartHeight
|
||||
// 在视图最初偏移量上累加更新量
|
||||
this.mindMap.view.translateYTo(oyPx + this.startViewPos.y)
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (this.currentScrollType === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
||||
const oy = e.clientY - this.mousedownPos.y + this.mousedownScrollbarPos
|
||||
this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.VERTICAL, oy)
|
||||
} else {
|
||||
const ox = e.clientX - this.mousedownPos.x
|
||||
const oxPercentage = -ox / this.scrollbarWrapSize.width
|
||||
const oxPx = oxPercentage * this.chartWidth
|
||||
// 在视图最初偏移量上累加更新量
|
||||
this.mindMap.view.translateXTo(oxPx + this.startViewPos.x)
|
||||
const ox = e.clientX - this.mousedownPos.x + this.mousedownScrollbarPos
|
||||
this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.HORIZONTAL, ox)
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标松开事件处理函数
|
||||
onMouseup() {
|
||||
this.isMousedown = false
|
||||
this.reset()
|
||||
}
|
||||
|
||||
// 更新视图
|
||||
updateMindMapView(type, offset) {
|
||||
const scrollbarData = this.calculationScrollbar()
|
||||
const t = this.mindMap.draw.transform()
|
||||
const drawRect = this.mindMap.draw.rbox()
|
||||
const rootRect = this.mindMap.renderer.root.group.rbox()
|
||||
if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
||||
// 滚动条新位置
|
||||
let oy = offset
|
||||
// 判断是否达到首尾
|
||||
if (oy <= 0) {
|
||||
oy = 0
|
||||
}
|
||||
let max =
|
||||
((100 - scrollbarData.vertical.height) / 100) *
|
||||
this.scrollbarWrapSize.height
|
||||
if (oy >= max) {
|
||||
oy = max
|
||||
}
|
||||
// 转换成百分比
|
||||
const oyPercentage = (oy / this.scrollbarWrapSize.height) * 100
|
||||
// 转换成相对于图形高度的距离
|
||||
const oyPx = (-oyPercentage / 100) * this.chartHeight
|
||||
// 节点中心点到图形最上方的距离
|
||||
const yOffset = rootRect.y - drawRect.y
|
||||
// 内边距
|
||||
const paddingY = this.mindMap.height / 2
|
||||
// 图形新位置
|
||||
let chartTop = oyPx + yOffset - paddingY * t.scaleY + paddingY
|
||||
this.mindMap.view.translateYTo(chartTop)
|
||||
this.emitEvent({
|
||||
horizontal: scrollbarData.horizontal,
|
||||
vertical: {
|
||||
top: oyPercentage,
|
||||
height: scrollbarData.vertical.height
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 滚动条新位置
|
||||
let ox = offset
|
||||
// 判断是否达到首尾
|
||||
if (ox <= 0) {
|
||||
ox = 0
|
||||
}
|
||||
let max =
|
||||
((100 - scrollbarData.horizontal.width) / 100) *
|
||||
this.scrollbarWrapSize.width
|
||||
if (ox >= max) {
|
||||
ox = max
|
||||
}
|
||||
// 转换成百分比
|
||||
const oxPercentage = (ox / this.scrollbarWrapSize.width) * 100
|
||||
// 转换成相对于图形高度的距离
|
||||
const oxPx = (-oxPercentage / 100) * this.chartWidth
|
||||
// 节点中心点到图形最左边的距离
|
||||
const xOffset = rootRect.x - drawRect.x
|
||||
// 内边距
|
||||
const paddingX = this.mindMap.width / 2
|
||||
// 图形新位置
|
||||
let chartLeft = oxPx + xOffset - paddingX * t.scaleX + paddingX
|
||||
this.mindMap.view.translateXTo(chartLeft)
|
||||
this.emitEvent({
|
||||
vertical: scrollbarData.vertical,
|
||||
horizontal: {
|
||||
left: oxPercentage,
|
||||
width: scrollbarData.horizontal.width
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动条的点击事件
|
||||
onClick(e, type) {
|
||||
let offset = 0
|
||||
if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) {
|
||||
offset = e.clientY - e.currentTarget.getBoundingClientRect().top
|
||||
} else {
|
||||
offset = e.clientX - e.currentTarget.getBoundingClientRect().left
|
||||
}
|
||||
this.updateMindMapView(type, offset)
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
|
||||
@@ -54,8 +54,40 @@ class Select {
|
||||
) {
|
||||
return
|
||||
}
|
||||
clearTimeout(this.autoMoveTimer)
|
||||
this.onMove(x, y)
|
||||
this.clearAutoMoveTimer()
|
||||
this.onMove(
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
() => {
|
||||
this.isSelecting = true
|
||||
// 绘制矩形
|
||||
this.rect.plot([
|
||||
[this.mouseDownX, this.mouseDownY],
|
||||
[this.mouseMoveX, this.mouseDownY],
|
||||
[this.mouseMoveX, this.mouseMoveY],
|
||||
[this.mouseDownX, this.mouseMoveY]
|
||||
])
|
||||
this.checkInNodes()
|
||||
},
|
||||
(dir, step) => {
|
||||
switch (dir) {
|
||||
case 'left':
|
||||
this.mouseDownX += step
|
||||
break
|
||||
case 'top':
|
||||
this.mouseDownY += step
|
||||
break
|
||||
case 'right':
|
||||
this.mouseDownX -= step
|
||||
break
|
||||
case 'bottom':
|
||||
this.mouseDownY -= step
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
this.mindMap.on('mouseup', () => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
@@ -78,79 +110,78 @@ class Select {
|
||||
|
||||
// 如果激活节点改变了,那么触发事件
|
||||
checkTriggerNodeActiveEvent() {
|
||||
let isNumChange = this.cacheActiveList.length !== this.mindMap.renderer.activeNodeList.length
|
||||
let isNumChange =
|
||||
this.cacheActiveList.length !==
|
||||
this.mindMap.renderer.activeNodeList.length
|
||||
let isNodeChange = false
|
||||
if (!isNumChange) {
|
||||
for(let i = 0; i < this.cacheActiveList.length; i++) {
|
||||
for (let i = 0; i < this.cacheActiveList.length; i++) {
|
||||
let cur = this.cacheActiveList[i]
|
||||
if (!this.mindMap.renderer.activeNodeList.find((item) => {
|
||||
return item.nodeData.data.uid === cur.nodeData.data.uid
|
||||
})){
|
||||
if (
|
||||
!this.mindMap.renderer.activeNodeList.find(item => {
|
||||
return item.nodeData.data.uid === cur.nodeData.data.uid
|
||||
})
|
||||
) {
|
||||
isNodeChange = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isNumChange || isNodeChange) {
|
||||
this.mindMap.emit(
|
||||
'node_active',
|
||||
null,
|
||||
[...this.mindMap.renderer.activeNodeList]
|
||||
)
|
||||
this.mindMap.emit('node_active', null, [
|
||||
...this.mindMap.renderer.activeNodeList
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
onMove(x, y) {
|
||||
this.isSelecting = true
|
||||
// 绘制矩形
|
||||
this.rect.plot([
|
||||
[this.mouseDownX, this.mouseDownY],
|
||||
[this.mouseMoveX, this.mouseDownY],
|
||||
[this.mouseMoveX, this.mouseMoveY],
|
||||
[this.mouseDownX, this.mouseMoveY]
|
||||
])
|
||||
this.checkInNodes()
|
||||
onMove(x, y, callback = () => {}, handle = () => {}) {
|
||||
callback()
|
||||
// 检测边缘移动
|
||||
let step = this.mindMap.opt.selectTranslateStep
|
||||
let limit = this.mindMap.opt.selectTranslateLimit
|
||||
let count = 0
|
||||
// 左边缘
|
||||
if (x <= this.mindMap.elRect.left + limit) {
|
||||
this.mouseDownX += step
|
||||
handle('left', step)
|
||||
this.mindMap.view.translateX(step)
|
||||
count++
|
||||
}
|
||||
// 右边缘
|
||||
if (x >= this.mindMap.elRect.right - limit) {
|
||||
this.mouseDownX -= step
|
||||
handle('right', step)
|
||||
this.mindMap.view.translateX(-step)
|
||||
count++
|
||||
}
|
||||
// 上边缘
|
||||
if (y <= this.mindMap.elRect.top + limit) {
|
||||
this.mouseDownY += step
|
||||
handle('top', step)
|
||||
this.mindMap.view.translateY(step)
|
||||
count++
|
||||
}
|
||||
// 下边缘
|
||||
if (y >= this.mindMap.elRect.bottom - limit) {
|
||||
this.mouseDownY -= step
|
||||
handle('bottom', step)
|
||||
this.mindMap.view.translateY(-step)
|
||||
count++
|
||||
}
|
||||
if (count > 0) {
|
||||
this.startAutoMove(x, y)
|
||||
this.startAutoMove(x, y, callback, handle)
|
||||
}
|
||||
}
|
||||
|
||||
// 开启自动移动
|
||||
startAutoMove(x, y) {
|
||||
startAutoMove(x, y, callback, handle) {
|
||||
this.autoMoveTimer = setTimeout(() => {
|
||||
this.onMove(x, y)
|
||||
this.onMove(x, y, callback, handle)
|
||||
}, 20)
|
||||
}
|
||||
|
||||
// 清除自动移动定时器
|
||||
clearAutoMoveTimer() {
|
||||
clearTimeout(this.autoMoveTimer)
|
||||
}
|
||||
|
||||
// 创建矩形
|
||||
createRect(x, y) {
|
||||
this.rect = this.mindMap.svg
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { nodeDataNoStylePropList } from '../constants/constant'
|
||||
|
||||
// 深度优先遍历树
|
||||
export const walk = (
|
||||
@@ -656,4 +657,15 @@ export const getObjectChangedProps = (oldObject, newObject) => {
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// 判断一个字段是否是节点数据中的样式字段
|
||||
export const checkIsNodeStyleDataKey = (key) => {
|
||||
// 用户自定义字段
|
||||
if (/^_/.test(key)) return false
|
||||
// 不在节点非样式字段列表里,那么就是样式字段
|
||||
if (!nodeDataNoStylePropList.includes(key)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
BIN
web/src/.DS_Store
vendored
BIN
web/src/.DS_Store
vendored
Binary file not shown.
BIN
web/src/assets/.DS_Store
vendored
BIN
web/src/assets/.DS_Store
vendored
Binary file not shown.
BIN
web/src/assets/avatar/乙.jpg
Normal file
BIN
web/src/assets/avatar/乙.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
BIN
web/src/assets/avatar/蜉蝣撼大叔.jpg
Normal file
BIN
web/src/assets/avatar/蜉蝣撼大叔.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
BIN
web/src/assets/img/themes/cactus.jpg
Normal file
BIN
web/src/assets/img/themes/cactus.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
BIN
web/src/assets/img/themes/classic5.jpg
Normal file
BIN
web/src/assets/img/themes/classic5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
BIN
web/src/assets/img/themes/dark3.jpg
Normal file
BIN
web/src/assets/img/themes/dark3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
web/src/assets/img/themes/dark4.jpg
Normal file
BIN
web/src/assets/img/themes/dark4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
@@ -52,5 +52,9 @@ export const themeMap = {
|
||||
neonLamp: require('../assets/img/themes/neonLamp.jpg'),
|
||||
darkNightLceBlade: require('../assets/img/themes/darkNightLceBlade.jpg'),
|
||||
morandi: require('../assets/img/themes/morandi.jpg'),
|
||||
classic5: require('../assets/img/themes/classic5.jpg'),
|
||||
dark3: require('../assets/img/themes/dark3.jpg'),
|
||||
dark4: require('../assets/img/themes/dark4.jpg'),
|
||||
cactus: require('../assets/img/themes/cactus.jpg'),
|
||||
}
|
||||
|
||||
46
web/src/customThemes/cactus.js
Normal file
46
web/src/customThemes/cactus.js
Normal file
@@ -0,0 +1,46 @@
|
||||
// 仙人掌
|
||||
export default {
|
||||
backgroundColor: 'rgb(219, 255, 211)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(255, 127, 71)',
|
||||
// 关联线默认状态的颜色
|
||||
associativeLineColor: 'rgb(160, 220, 63)',
|
||||
// 关联线文字颜色
|
||||
associativeLineTextColor: 'rgb(160, 220, 63)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(15, 198, 113)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#fff',
|
||||
color: 'rgb(26, 26, 26)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(255, 127, 71)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
|
||||
45
web/src/customThemes/classic5.js
Normal file
45
web/src/customThemes/classic5.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// 经典5
|
||||
export default {
|
||||
backgroundColor: 'rgb(233, 245, 241)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(34, 34, 34)',
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(34, 34, 34)',
|
||||
// 关联线默认状态的颜色
|
||||
associativeLineColor: 'rgb(56, 44, 116)',
|
||||
// 关联线文字颜色
|
||||
associativeLineTextColor: 'rgb(68, 68, 68)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(56, 44, 116)',
|
||||
color: '#fff',
|
||||
borderColor: 'rgb(56, 44, 116)',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(161, 213, 188)',
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(56, 44, 116)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
|
||||
46
web/src/customThemes/dark3.js
Normal file
46
web/src/customThemes/dark3.js
Normal file
@@ -0,0 +1,46 @@
|
||||
// 暗色3
|
||||
export default {
|
||||
backgroundColor: 'rgb(0, 0, 0)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(172, 172, 172)',
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(172, 172, 172)',
|
||||
// 关联线默认状态的颜色
|
||||
associativeLineColor: 'rgb(57, 130, 252)',
|
||||
// 关联线文字颜色
|
||||
associativeLineTextColor: 'rgb(68, 68, 68)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#fff',
|
||||
color: 'rgb(241, 79, 81)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(241, 79, 81)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#fff'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(241, 79, 81)'
|
||||
}
|
||||
}
|
||||
|
||||
45
web/src/customThemes/dark4.js
Normal file
45
web/src/customThemes/dark4.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// 暗色4
|
||||
export default {
|
||||
backgroundColor: 'rgb(32, 34, 43)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(90, 136, 116)',
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(90, 136, 116)',
|
||||
// 关联线默认状态的颜色
|
||||
associativeLineColor: 'rgb(57, 130, 252)',
|
||||
// 关联线文字颜色
|
||||
associativeLineTextColor: 'rgb(68, 68, 68)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(1, 192, 116)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(48, 51, 63)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#fff'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(1, 192, 116)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ import seaBlueLine from './seaBlueLine'
|
||||
import neonLamp from './neonLamp'
|
||||
import darkNightLceBlade from './darkNightLceBlade'
|
||||
import morandi from './morandi'
|
||||
import classic5 from './classic5'
|
||||
import dark3 from './dark3'
|
||||
import dark4 from './dark4'
|
||||
import cactus from './cactus'
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -55,5 +59,29 @@ export default [
|
||||
value: 'morandi',
|
||||
theme: morandi,
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '脑图经典5',
|
||||
value: 'classic5',
|
||||
theme: classic5,
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '暗色3',
|
||||
value: 'dark3',
|
||||
theme: dark3,
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '暗色4',
|
||||
value: 'dark4',
|
||||
theme: dark4,
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '仙人掌',
|
||||
value: 'cactus',
|
||||
theme: cactus,
|
||||
dark: false
|
||||
}
|
||||
]
|
||||
@@ -39,7 +39,7 @@ let APIList = [
|
||||
'markdown',
|
||||
'utils'
|
||||
]
|
||||
let helpList = new Array(2).fill(0).map((_, index) => {
|
||||
let helpList = new Array(5).fill(0).map((_, index) => {
|
||||
return 'help' + (index + 1)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## 0.7.1
|
||||
|
||||
Fix:
|
||||
|
||||
> 1.Fix the issue of unsaved associated line endpoints after changes.
|
||||
>
|
||||
> 2.Fix the issue of abnormal canvas scrolling when moving the mouse to the edge of multiple selected nodes when the distance from the top left corner of the canvas to the browser window is not 0.
|
||||
>
|
||||
> 3.Fix the issue of importing xmind file errors for nodes with empty titles.
|
||||
>
|
||||
> 4.Fix the issue where the exported xmind file prompts for corruption when opened on the latest version of xmind software.
|
||||
>
|
||||
> 5.Fix the issue where stickers cannot be displayed when exporting data with stickers in xmind format.
|
||||
>
|
||||
> 6.Fix the issue of node right-click event reporting errors when the select plugin is not registered.
|
||||
>
|
||||
> 7.There is no issue with removing duplicates in the method of registering plugins.
|
||||
|
||||
New:
|
||||
|
||||
> 1.Reconstruct node drag and drop logic: optimize drag and drop difficulties in some situations, adapt to various structures, and automatically move the canvas when the mouse moves to the edge of the canvas during drag and drop.
|
||||
>
|
||||
> 2.Reconstruct the scrollbar plugin to optimize the user experience.
|
||||
>
|
||||
> 3.Imperfect resolution of conflicts between logical structure diagrams, mind maps, directory organization diagrams, organization chart summaries, and nodes (the summaries should be rewritten or deleted later).
|
||||
>
|
||||
> 4.Activate adjacent nodes after deleting them.
|
||||
>
|
||||
> 5.In node data_ The starting field is considered a custom field.
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.The page will display the current core library version number.
|
||||
|
||||
## 0.7.0
|
||||
|
||||
Breaking change: Removed the section of node activation style in the theme file, Setting the activation style of nodes is no longer supported, and the activation effect has been changed to a unified node outer border style, while also supporting the mouse hover effect.
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.7.1</h2>
|
||||
<p>Fix:</p>
|
||||
<blockquote>
|
||||
<p>1.Fix the issue of unsaved associated line endpoints after changes.</p>
|
||||
<p>2.Fix the issue of abnormal canvas scrolling when moving the mouse to the edge of multiple selected nodes when the distance from the top left corner of the canvas to the browser window is not 0.</p>
|
||||
<p>3.Fix the issue of importing xmind file errors for nodes with empty titles.</p>
|
||||
<p>4.Fix the issue where the exported xmind file prompts for corruption when opened on the latest version of xmind software.</p>
|
||||
<p>5.Fix the issue where stickers cannot be displayed when exporting data with stickers in xmind format.</p>
|
||||
<p>6.Fix the issue of node right-click event reporting errors when the select plugin is not registered.</p>
|
||||
<p>7.There is no issue with removing duplicates in the method of registering plugins.</p>
|
||||
</blockquote>
|
||||
<p>New:</p>
|
||||
<blockquote>
|
||||
<p>1.Reconstruct node drag and drop logic: optimize drag and drop difficulties in some situations, adapt to various structures, and automatically move the canvas when the mouse moves to the edge of the canvas during drag and drop.</p>
|
||||
<p>2.Reconstruct the scrollbar plugin to optimize the user experience.</p>
|
||||
<p>3.Imperfect resolution of conflicts between logical structure diagrams, mind maps, directory organization diagrams, organization chart summaries, and nodes (the summaries should be rewritten or deleted later).</p>
|
||||
<p>4.Activate adjacent nodes after deleting them.</p>
|
||||
<p>5.In node data_ The starting field is considered a custom field.</p>
|
||||
</blockquote>
|
||||
<p>Demo:</p>
|
||||
<blockquote>
|
||||
<p>1.The page will display the current core library version number.</p>
|
||||
</blockquote>
|
||||
<h2>0.7.0</h2>
|
||||
<p>Breaking change: Removed the section of node activation style in the theme file, Setting the activation style of nodes is no longer supported, and the activation effect has been changed to a unified node outer border style, while also supporting the mouse hover effect.</p>
|
||||
<p>Fix:</p>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Constructor
|
||||
|
||||
## Basic use
|
||||
## Basic use
|
||||
|
||||
```html
|
||||
<div id="mindMapContainer"></div>
|
||||
@@ -25,7 +25,7 @@ const mindMap = new MindMap({
|
||||
| Field Name | Type | Default Value | Description | Required |
|
||||
| -------------------------------- | ------- | ---------------- | ------------------------------------------------------------ | -------- |
|
||||
| el | Element | | Container element, must be a DOM element | Yes |
|
||||
| data | Object | {} | Mind map data, refer to: [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) | |
|
||||
| data | Object | {} | Mind map data, Please refer to the introduction of 【Data structure】 below | |
|
||||
| layout | String | logicalStructure | Layout type, options: logicalStructure (logical structure diagram), mindMap (mind map), catalogOrganization (catalog organization diagram), organizationStructure (organization structure diagram)、timeline(v0.5.4+, timeline)、timeline2(v0.5.4+, up down alternating timeline)、fishbone(v0.5.4+, fishbone diagram) | |
|
||||
| fishboneDeg(v0.5.4+) | Number | 45 | Set the diagonal angle of the fishbone structure diagram | |
|
||||
| theme | String | default | Theme, options: default, classic, minions, pinkGrape, mint, gold, vitalityOrange, greenLeaf, dark2, skyGreen, classic2, classic3, classic4(v0.2.0+), classicGreen, classicBlue, blueSky, brainImpairedPink, dark, earthYellow, freshGreen, freshRed, romanticPurple, simpleBlack(v0.5.4+), courseGreen(v0.5.4+), coffee(v0.5.4+), redSpirit(v0.5.4+), blackHumour(v0.5.4+), lateNightOffice(v0.5.4+), blackGold(v0.5.4+)、、avocado(v.5.10-fix.2+)、autumn(v.5.10-fix.2+)、orangeJuice(v.5.10-fix.2+) | |
|
||||
@@ -87,6 +87,49 @@ const mindMap = new MindMap({
|
||||
| hoverRectColor(v0.7.0+) | String | rgb(94, 200, 248) | The node mouse hover and the rectangular border color displayed when activated will add a transparency of 0.6 when hovering | |
|
||||
| hoverRectPadding(v0.7.0+) | Number | 2 | The distance between the node mouse hover and the displayed rectangular border when activated and the node content | |
|
||||
| selectTextOnEnterEditText(v0.7.0+) | Boolean | true | Is the text selected by default when double-clicking a node to enter node text editing? By default, it will only be selected when creating a new node | |
|
||||
| deleteNodeActive(v0.7.1+) | Boolean | true | Enable the function of automatically activating adjacent nodes or parent nodes after deleting nodes | |
|
||||
| autoMoveWhenMouseInEdgeOnDrag(v0.7.1+) | Boolean | true | Whether to enable automatic canvas movement when the mouse moves to the edge of the canvas while dragging nodes | |
|
||||
|
||||
### Data structure
|
||||
|
||||
The basic data structure is as follows:
|
||||
|
||||
```js
|
||||
{
|
||||
data: {
|
||||
text: '', // The text of the node can be rich text, which is in HTML format. In this case, richText should be set to true
|
||||
richText: false, // Is the text of the node in rich text mode
|
||||
expand: true, // Whether the node is expanded
|
||||
uid: '',// The unique ID of the node, which may not be passed, will be generated internally
|
||||
icon: [], // The format of the icon can be found in the "插入和扩展节点图标" section of the tutorial
|
||||
image: '', // URL of the image
|
||||
imageTitle: '', // The title of the image can be blank
|
||||
imageSize: { // The size of the image
|
||||
width: 100, // The width of the image, mandatory
|
||||
height: 100, // The height of the image is mandatory
|
||||
custom: false // If set to true, the display size of the image is not controlled by the theme, and is based on imageSize.width and imageSize.height
|
||||
},
|
||||
hyperlink: '', // Hyperlink address
|
||||
hyperlinkTitle: '', // Title of hyperlink
|
||||
note: '', // Content of remarks
|
||||
tag: [], // Tag list
|
||||
generalization: {// The summary of the node, if there is no summary, the generalization can be set to null
|
||||
text: ''// Summary Text
|
||||
},
|
||||
associativeLineTargets: [''],// If there are associated lines, then it is the uid list of the target node
|
||||
associativeLineText: '',// Association Line Text
|
||||
// ...For other style fields, please refer to the topic
|
||||
},
|
||||
children [// Child nodes, with consistent structure and root nodes
|
||||
{
|
||||
data: {},
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you want to add custom fields, you can add them to the same level as 'data' and 'children'. If you want to add them to the 'data' object, please use the `_` Name your custom field at the beginning, and it will be used internally to determine whether it is a custom field.
|
||||
|
||||
### Watermark config
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -63,8 +63,6 @@ However, this requires backend support, as our application is a single page clie
|
||||
|
||||
## Docker
|
||||
|
||||
## Docker
|
||||
|
||||
> Thank you very much [水车](https://github.com/shuiche-it), This section is written by him, and the corresponding Docker package is also maintained by him.
|
||||
|
||||
Install directly from Docker Hub:
|
||||
@@ -79,6 +77,8 @@ After the installation is completed, check the container's running status throug
|
||||
|
||||
Open 127.0.0.1:8081 in the browser to use the Web mind map function.
|
||||
|
||||
[在群晖上以 Docker 方式安装](https://laosu.gq/2023/09/02/%E5%BC%BA%E5%A4%A7%E7%9A%84%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%E5%BA%93SimpleMindMap/)
|
||||
|
||||
## Docking with one's own storage services
|
||||
|
||||
The application data is stored locally in the browser by default, and the local storage capacity of the browser is relatively small, so it is easy to trigger restrictions when inserting more images in the mind map. Therefore, a better choice is to dock with your own storage service, which usually has two ways:
|
||||
|
||||
@@ -39,7 +39,6 @@ npm link simple-mind-map
|
||||
</code></pre>
|
||||
<p>However, this requires backend support, as our application is a single page client application. If the backend is not properly configured, users will return 404 when accessing sub routes directly in the browser. Therefore, you need to add a candidate resource on the server that covers all situations: if the 'URL' cannot match any static resources, the same 'index. html' page should be returned.</p>
|
||||
<h2>Docker</h2>
|
||||
<h2>Docker</h2>
|
||||
<blockquote>
|
||||
<p>Thank you very much <a href="https://github.com/shuiche-it">水车</a>, This section is written by him, and the corresponding Docker package is also maintained by him.</p>
|
||||
</blockquote>
|
||||
@@ -49,6 +48,7 @@ npm link simple-mind-map
|
||||
<p>Mindmap has activated port 8080 as the web service entry point in the container. When running the container through Docker, it is necessary to specify a local mapping port. In the above case, we mapped the local port 8081 to the container port 8080.</p>
|
||||
<p>After the installation is completed, check the container's running status through 'Docker PS'.</p>
|
||||
<p>Open 127.0.0.1:8081 in the browser to use the Web mind map function.</p>
|
||||
<p><a href="https://laosu.gq/2023/09/02/%E5%BC%BA%E5%A4%A7%E7%9A%84%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%E5%BA%93SimpleMindMap/">在群晖上以 Docker 方式安装</a></p>
|
||||
<h2>Docking with one's own storage services</h2>
|
||||
<p>The application data is stored locally in the browser by default, and the local storage capacity of the browser is relatively small, so it is easy to trigger restrictions when inserting more images in the mind map. Therefore, a better choice is to dock with your own storage service, which usually has two ways:</p>
|
||||
<h3>The first</h3>
|
||||
|
||||
@@ -192,4 +192,12 @@ Open source is not easy. If this project is helpful to you, you can invite the a
|
||||
<img src="../../../../assets/avatar/南风.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>南风</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/蜉蝣撼大叔.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>蜉蝣撼大叔</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/乙.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>乙</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,6 +151,14 @@ full screen, support mini map</li>
|
||||
<img src="../../../../assets/avatar/南风.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>南风</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/蜉蝣撼大叔.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>蜉蝣撼大叔</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/乙.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>乙</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# Scrollbar plugin
|
||||
|
||||
> v0.7.0+
|
||||
>
|
||||
> V0.7.1+has been refactored, and the following document is a new one.
|
||||
|
||||
This plugin is used to help develop the functionality of horizontal and vertical scrollbar.
|
||||
This plugin is used to help develop the functionality of horizontal and vertical scrollbar. Please refer to the tutorial for detailed usage.
|
||||
|
||||
## Register
|
||||
|
||||
@@ -16,9 +18,24 @@ After registration and instantiation of `MindMap`, the instance can be obtained
|
||||
|
||||
## Event
|
||||
|
||||
#### scrollbar_change
|
||||
#### scrollbar_change(data)
|
||||
|
||||
Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar.
|
||||
```js
|
||||
{
|
||||
// Vertical scrollbar
|
||||
vertical: {
|
||||
top,// Top value, Percentage value
|
||||
height// Scrollbar height, Percentage value
|
||||
},
|
||||
// Horizontal scrollbar
|
||||
horizontal: {
|
||||
left,// Left value, Percentage value
|
||||
width// Scrollbar width, Percentage value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar. Receive a parameter representing the latest scrollbar position and size information, which you can use to update the style of the scrollbar element.
|
||||
|
||||
## Method
|
||||
|
||||
@@ -32,9 +49,9 @@ Set the size of the scroll bar container, which is the width of the container fo
|
||||
|
||||
### calculationScrollbar()
|
||||
|
||||
> You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element.
|
||||
> Usually, you do not need to call this method. If the scroll bar is not updated when rendering for the first time, you can manually call this method to obtain the scroll bar data.
|
||||
>
|
||||
> Generally, it is necessary to monitor scrollbar_change event, and then call it to update the scroll bar.
|
||||
> You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element.
|
||||
|
||||
Return value:
|
||||
|
||||
@@ -53,7 +70,7 @@ Return value:
|
||||
}
|
||||
```
|
||||
|
||||
Obtain the size and position of the scroll bar, and you can set it to the scroll bar element based on the return value to achieve the effect of rendering and caring about the scroll bar.
|
||||
Obtain the size and position of the scrollbar.
|
||||
|
||||
### onMousedown(e, type)
|
||||
|
||||
@@ -61,4 +78,12 @@ Obtain the size and position of the scroll bar, and you can set it to the scroll
|
||||
|
||||
- `type`: The type of scroll bar pressed, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。
|
||||
|
||||
This method needs to be called when the mouse press event of the scrollbar element occurs.
|
||||
This method needs to be called when the mouse press event of the scrollbar element occurs.
|
||||
|
||||
### onClick(e, type)
|
||||
|
||||
- `e`:The event object for the mouse click event.
|
||||
|
||||
- `type`:The type of scroll bar on click, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。
|
||||
|
||||
This method needs to be called when the click event of the scrollbar element is triggered.
|
||||
@@ -3,8 +3,9 @@
|
||||
<h1>Scrollbar plugin</h1>
|
||||
<blockquote>
|
||||
<p>v0.7.0+</p>
|
||||
<p>V0.7.1+has been refactored, and the following document is a new one.</p>
|
||||
</blockquote>
|
||||
<p>This plugin is used to help develop the functionality of horizontal and vertical scrollbar.</p>
|
||||
<p>This plugin is used to help develop the functionality of horizontal and vertical scrollbar. Please refer to the tutorial for detailed usage.</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> Scrollbar <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/plugins/Scrollbar.js'</span>
|
||||
@@ -12,8 +13,21 @@ MindMap.usePlugin(Scrollbar)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.scrollbar</code>.</p>
|
||||
<h2>Event</h2>
|
||||
<h4>scrollbar_change</h4>
|
||||
<p>Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar.</p>
|
||||
<h4>scrollbar_change(data)</h4>
|
||||
<pre class="hljs"><code>{
|
||||
<span class="hljs-comment">// Vertical scrollbar</span>
|
||||
<span class="hljs-attr">vertical</span>: {
|
||||
top,<span class="hljs-comment">// Top value, Percentage value</span>
|
||||
height<span class="hljs-comment">// Scrollbar height, Percentage value</span>
|
||||
},
|
||||
<span class="hljs-comment">// Horizontal scrollbar</span>
|
||||
<span class="hljs-attr">horizontal</span>: {
|
||||
left,<span class="hljs-comment">// Left value, Percentage value</span>
|
||||
width<span class="hljs-comment">// Scrollbar width, Percentage value</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar. Receive a parameter representing the latest scrollbar position and size information, which you can use to update the style of the scrollbar element.</p>
|
||||
<h2>Method</h2>
|
||||
<h3>setScrollBarWrapSize(width, height)</h3>
|
||||
<ul>
|
||||
@@ -27,8 +41,8 @@ MindMap.usePlugin(Scrollbar)
|
||||
<p>Set the size of the scroll bar container, which is the width of the container for horizontal scrollbars and the height of the container for vertical scrollbars. When your scrollbar container size changes, you need to call this method again.</p>
|
||||
<h3>calculationScrollbar()</h3>
|
||||
<blockquote>
|
||||
<p>Usually, you do not need to call this method. If the scroll bar is not updated when rendering for the first time, you can manually call this method to obtain the scroll bar data.</p>
|
||||
<p>You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element.</p>
|
||||
<p>Generally, it is necessary to monitor scrollbar_change event, and then call it to update the scroll bar.</p>
|
||||
</blockquote>
|
||||
<p>Return value:</p>
|
||||
<pre class="hljs"><code>{
|
||||
@@ -44,7 +58,7 @@ MindMap.usePlugin(Scrollbar)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>Obtain the size and position of the scroll bar, and you can set it to the scroll bar element based on the return value to achieve the effect of rendering and caring about the scroll bar.</p>
|
||||
<p>Obtain the size and position of the scrollbar.</p>
|
||||
<h3>onMousedown(e, type)</h3>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -55,6 +69,16 @@ MindMap.usePlugin(Scrollbar)
|
||||
</li>
|
||||
</ul>
|
||||
<p>This method needs to be called when the mouse press event of the scrollbar element occurs.</p>
|
||||
<h3>onClick(e, type)</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>e</code>:The event object for the mouse click event.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>type</code>:The type of scroll bar on click, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This method needs to be called when the click event of the scrollbar element is triggered.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -167,4 +167,14 @@ module. If you need it, you can try using other libraries to parse `xml` to
|
||||
|
||||
### Error `Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')`
|
||||
|
||||
The reason is that the installed version of `@svgdotjs/svg.js` is too high. You can manually reduce it to the version of `3.0.16`.
|
||||
The reason is that the installed version of `@svgdotjs/svg.js` is too high. You can manually reduce it to the version of `3.0.16`.
|
||||
|
||||
### TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46
|
||||
|
||||
The following configurations can be added to the packaging configuration file:
|
||||
|
||||
```js
|
||||
resolve: { alias: { stream: "stream-browserify" } }
|
||||
```
|
||||
|
||||
Different packaging tools may have different specific configurations, with the principle of excluding 'stream' dependencies.
|
||||
@@ -116,6 +116,11 @@ module. If you need it, you can try using other libraries to parse <code>xml</co
|
||||
<code>json</code>.</p>
|
||||
<h3>Error <code>Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')</code></h3>
|
||||
<p>The reason is that the installed version of <code>@svgdotjs/svg.js</code> is too high. You can manually reduce it to the version of <code>3.0.16</code>.</p>
|
||||
<h3>TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46</h3>
|
||||
<p>The following configurations can be added to the packaging configuration file:</p>
|
||||
<pre class="hljs"><code>resolve: { <span class="hljs-attr">alias</span>: { <span class="hljs-attr">stream</span>: <span class="hljs-string">"stream-browserify"</span> } }
|
||||
</code></pre>
|
||||
<p>Different packaging tools may have different specific configurations, with the principle of excluding 'stream' dependencies.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -56,7 +56,11 @@ export default [
|
||||
{ path: 'painter', title: 'Painter插件' },
|
||||
{ path: 'painter', title: 'Painter插件' },
|
||||
{ path: 'scrollbar', title: 'Scrollbar插件' },
|
||||
{ path: 'help2', title: '客户端' }
|
||||
{ path: 'help1', title: '概要/关联线' },
|
||||
{ path: 'help2', title: '客户端' },
|
||||
{ path: 'help3', title: '打开预览在线文件' },
|
||||
{ path: 'help4', title: '复制粘贴' },
|
||||
{ path: 'help5', title: '导出' }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## 0.7.1
|
||||
|
||||
修复:
|
||||
|
||||
> 1.修复关联线端点改变后未保存的问题。
|
||||
>
|
||||
> 2.修复画布左上角距浏览器窗口不为0时多选节点鼠标移动到边缘时画布滚动异常的问题。
|
||||
>
|
||||
> 3.修复导入存在为标题为空的节点的xmind文件报错的问题。
|
||||
>
|
||||
> 4.修复导出的xmind文件在最新版xmind软件上打开时提示已损坏的问题。
|
||||
>
|
||||
> 5.修复导出带有贴纸的数据为xmind格式时贴纸无法显示的问题。
|
||||
>
|
||||
> 6.修复没有注册select插件时节点右键事件报错的问题。
|
||||
>
|
||||
> 7.修复注册插件的方法没有去重的问题。
|
||||
|
||||
新增:
|
||||
|
||||
> 1.重构节点拖拽逻辑:优化一些情况下的拖拽难问题、适配各种结构、拖拽时鼠标移动到画布边缘时画布自动移动。
|
||||
>
|
||||
> 2.重构滚动条插件,优化使用体验。
|
||||
>
|
||||
> 3.不完美的解决逻辑结构图、思维导图、目录组织图、组织结构图概要和节点的冲突问题(概要后面应该会重写或删除)。
|
||||
>
|
||||
> 4.删除节点后激活相邻节点。
|
||||
>
|
||||
> 5.节点数据data中以_开头的字段被认为是自定义字段。
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.页面增加显示当前核心库版本号。
|
||||
|
||||
## 0.7.0
|
||||
|
||||
破坏性更新:删除了主题文件中节点激活样式的部分,不再支持设置节点的激活样式,激活效果改为统一的节点外边框样式,同时支持鼠标hover效果。
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.7.1</h2>
|
||||
<p>修复:</p>
|
||||
<blockquote>
|
||||
<p>1.修复关联线端点改变后未保存的问题。</p>
|
||||
<p>2.修复画布左上角距浏览器窗口不为0时多选节点鼠标移动到边缘时画布滚动异常的问题。</p>
|
||||
<p>3.修复导入存在为标题为空的节点的xmind文件报错的问题。</p>
|
||||
<p>4.修复导出的xmind文件在最新版xmind软件上打开时提示已损坏的问题。</p>
|
||||
<p>5.修复导出带有贴纸的数据为xmind格式时贴纸无法显示的问题。</p>
|
||||
<p>6.修复没有注册select插件时节点右键事件报错的问题。</p>
|
||||
<p>7.修复注册插件的方法没有去重的问题。</p>
|
||||
</blockquote>
|
||||
<p>新增:</p>
|
||||
<blockquote>
|
||||
<p>1.重构节点拖拽逻辑:优化一些情况下的拖拽难问题、适配各种结构、拖拽时鼠标移动到画布边缘时画布自动移动。</p>
|
||||
<p>2.重构滚动条插件,优化使用体验。</p>
|
||||
<p>3.不完美的解决逻辑结构图、思维导图、目录组织图、组织结构图概要和节点的冲突问题(概要后面应该会重写或删除)。</p>
|
||||
<p>4.删除节点后激活相邻节点。</p>
|
||||
<p>5.节点数据data中以_开头的字段被认为是自定义字段。</p>
|
||||
</blockquote>
|
||||
<p>Demo:</p>
|
||||
<blockquote>
|
||||
<p>1.页面增加显示当前核心库版本号。</p>
|
||||
</blockquote>
|
||||
<h2>0.7.0</h2>
|
||||
<p>破坏性更新:删除了主题文件中节点激活样式的部分,不再支持设置节点的激活样式,激活效果改为统一的节点外边框样式,同时支持鼠标hover效果。</p>
|
||||
<p>修复:</p>
|
||||
|
||||
@@ -25,7 +25,7 @@ const mindMap = new MindMap({
|
||||
| 字段名称 | 类型 | 默认值 | 描述 | 是否必填 |
|
||||
| -------------------------------- | ------- | ---------------- | ------------------------------------------------------------ | -------- |
|
||||
| el | Element | | 容器元素,必须为DOM元素 | 是 |
|
||||
| data | Object | {} | 思维导图数据,可参考:[exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) | |
|
||||
| data | Object | {} | 思维导图数据,可参考下方【数据结构】介绍 | |
|
||||
| layout | String | logicalStructure | 布局类型,可选列表:logicalStructure(逻辑结构图)、mindMap(思维导图)、catalogOrganization(目录组织图)、organizationStructure(组织结构图)、timeline(v0.5.4+,时间轴)、timeline2(v0.5.4+,上下交替型时间轴)、fishbone(v0.5.4+,鱼骨图) | |
|
||||
| fishboneDeg(v0.5.4+) | Number | 45 | 设置鱼骨结构图的斜线角度 | |
|
||||
| theme | String | default | 主题,可选列表:default(默认)、classic(脑图经典)、minions(小黄人)、pinkGrape(粉红葡萄)、mint(薄荷)、gold(金色vip)、vitalityOrange(活力橙)、greenLeaf(绿叶)、dark2(暗色2)、skyGreen(天清绿)、classic2(脑图经典2)、classic3(脑图经典3)、classic4(脑图经典4,v0.2.0+)、classicGreen(经典绿)、classicBlue(经典蓝)、blueSky(天空蓝)、brainImpairedPink(脑残粉)、dark(暗色)、earthYellow(泥土黄)、freshGreen(清新绿)、freshRed(清新红)、romanticPurple(浪漫紫)、simpleBlack(v0.5.4+简约黑)、courseGreen(v0.5.4+课程绿)、coffee(v0.5.4+咖啡)、redSpirit(v0.5.4+红色精神)、blackHumour(v0.5.4+黑色幽默)、lateNightOffice(v0.5.4+深夜办公室)、blackGold(v0.5.4+黑金)、avocado(v.5.10-fix.2+牛油果)、autumn(v.5.10-fix.2+秋天)、orangeJuice(v.5.10-fix.2+橙汁) | |
|
||||
@@ -87,6 +87,49 @@ const mindMap = new MindMap({
|
||||
| hoverRectColor(v0.7.0+) | String | rgb(94, 200, 248) | 节点鼠标hover和激活时显示的矩形边框颜色,hover时会添加0.6的透明度 | |
|
||||
| hoverRectPadding(v0.7.0+) | Number | 2 | 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 | |
|
||||
| selectTextOnEnterEditText(v0.7.0+) | Boolean | true | 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中 | |
|
||||
| deleteNodeActive(v0.7.1+) | Boolean | true | 是否开启删除节点后自动激活节点相邻节点或父节点的功能 | |
|
||||
| autoMoveWhenMouseInEdgeOnDrag(v0.7.1+) | Boolean | true | 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动 | |
|
||||
|
||||
### 数据结构
|
||||
|
||||
基本的数据结构如下:
|
||||
|
||||
```js
|
||||
{
|
||||
data: {
|
||||
text: '', // 节点的文本,可以是富文本,也就是html格式的,此时richText要设为true
|
||||
richText: false, // 节点的文本是否是富文本模式
|
||||
expand: true, // 节点是否展开
|
||||
uid: '',// 节点唯一的id,可不传,内部会生成
|
||||
icon: [], // 图标,格式可参考教程里的【插入和扩展节点图标】章节
|
||||
image: '', // 图片的url
|
||||
imageTitle: '', // 图片的标题,可为空
|
||||
imageSize: { // 图片的尺寸
|
||||
width: 100, // 图片的宽度,必传
|
||||
height: 100, // 图片的高度,必传
|
||||
custom: false // 如果设为true,图片的显示大小不受主题控制,以imageSize.width和imageSize.height为准
|
||||
},
|
||||
hyperlink: '', // 超链接地址
|
||||
hyperlinkTitle: '', // 超链接的标题
|
||||
note: '', // 备注的内容
|
||||
tag: [], // 标签列表
|
||||
generalization: {// 节点的概要,如果没有概要generalization设为null即可
|
||||
text: ''// 概要的文本
|
||||
},
|
||||
associativeLineTargets: [''],// 如果存在关联线,那么为目标节点的uid列表
|
||||
associativeLineText: '',// 关联线文本
|
||||
// ...其他样式字段,可以参考主题
|
||||
},
|
||||
children [// 子节点,结构和根节点一致
|
||||
{
|
||||
data: {},
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
如果你要添加自定义的字段,可以添加到`data`、`children`同级,如果你要添加到`data`对象里,那么请使用`_`开头来命名你的自定义字段,内部会通过这个来判断是否是自定义字段。
|
||||
|
||||
### 水印配置
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<td>data</td>
|
||||
<td>Object</td>
|
||||
<td>{}</td>
|
||||
<td>思维导图数据,可参考:<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js">exampleData.js</a></td>
|
||||
<td>思维导图数据,可参考下方【数据结构】介绍</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -469,8 +469,58 @@
|
||||
<td>双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>deleteNodeActive(v0.7.1+)</td>
|
||||
<td>Boolean</td>
|
||||
<td>true</td>
|
||||
<td>是否开启删除节点后自动激活节点相邻节点或父节点的功能</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>autoMoveWhenMouseInEdgeOnDrag(v0.7.1+)</td>
|
||||
<td>Boolean</td>
|
||||
<td>true</td>
|
||||
<td>拖拽节点时鼠标移动到画布边缘是否开启画布自动移动</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>数据结构</h3>
|
||||
<p>基本的数据结构如下:</p>
|
||||
<pre class="hljs"><code>{
|
||||
<span class="hljs-attr">data</span>: {
|
||||
<span class="hljs-attr">text</span>: <span class="hljs-string">''</span>, <span class="hljs-comment">// 节点的文本,可以是富文本,也就是html格式的,此时richText要设为true</span>
|
||||
<span class="hljs-attr">richText</span>: <span class="hljs-literal">false</span>, <span class="hljs-comment">// 节点的文本是否是富文本模式</span>
|
||||
<span class="hljs-attr">expand</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// 节点是否展开</span>
|
||||
<span class="hljs-attr">uid</span>: <span class="hljs-string">''</span>,<span class="hljs-comment">// 节点唯一的id,可不传,内部会生成</span>
|
||||
<span class="hljs-attr">icon</span>: [], <span class="hljs-comment">// 图标,格式可参考教程里的【插入和扩展节点图标】章节</span>
|
||||
<span class="hljs-attr">image</span>: <span class="hljs-string">''</span>, <span class="hljs-comment">// 图片的url</span>
|
||||
<span class="hljs-attr">imageTitle</span>: <span class="hljs-string">''</span>, <span class="hljs-comment">// 图片的标题,可为空</span>
|
||||
<span class="hljs-attr">imageSize</span>: { <span class="hljs-comment">// 图片的尺寸</span>
|
||||
<span class="hljs-attr">width</span>: <span class="hljs-number">100</span>, <span class="hljs-comment">// 图片的宽度,必传</span>
|
||||
<span class="hljs-attr">height</span>: <span class="hljs-number">100</span>, <span class="hljs-comment">// 图片的高度,必传</span>
|
||||
<span class="hljs-attr">custom</span>: <span class="hljs-literal">false</span> <span class="hljs-comment">// 如果设为true,图片的显示大小不受主题控制,以imageSize.width和imageSize.height为准</span>
|
||||
},
|
||||
<span class="hljs-attr">hyperlink</span>: <span class="hljs-string">''</span>, <span class="hljs-comment">// 超链接地址</span>
|
||||
<span class="hljs-attr">hyperlinkTitle</span>: <span class="hljs-string">''</span>, <span class="hljs-comment">// 超链接的标题</span>
|
||||
<span class="hljs-attr">note</span>: <span class="hljs-string">''</span>, <span class="hljs-comment">// 备注的内容</span>
|
||||
<span class="hljs-attr">tag</span>: [], <span class="hljs-comment">// 标签列表</span>
|
||||
<span class="hljs-attr">generalization</span>: {<span class="hljs-comment">// 节点的概要,如果没有概要generalization设为null即可</span>
|
||||
<span class="hljs-attr">text</span>: <span class="hljs-string">''</span><span class="hljs-comment">// 概要的文本</span>
|
||||
},
|
||||
<span class="hljs-attr">associativeLineTargets</span>: [<span class="hljs-string">''</span>],<span class="hljs-comment">// 如果存在关联线,那么为目标节点的uid列表</span>
|
||||
<span class="hljs-attr">associativeLineText</span>: <span class="hljs-string">''</span>,<span class="hljs-comment">// 关联线文本</span>
|
||||
<span class="hljs-comment">// ...其他样式字段,可以参考主题</span>
|
||||
},
|
||||
children [<span class="hljs-comment">// 子节点,结构和根节点一致</span>
|
||||
{
|
||||
<span class="hljs-attr">data</span>: {},
|
||||
<span class="hljs-attr">children</span>: []
|
||||
}
|
||||
]
|
||||
}
|
||||
</code></pre>
|
||||
<p>如果你要添加自定义的字段,可以添加到<code>data</code>、<code>children</code>同级,如果你要添加到<code>data</code>对象里,那么请使用<code>_</code>开头来命名你的自定义字段,内部会通过这个来判断是否是自定义字段。</p>
|
||||
<h3>水印配置</h3>
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
# 如何渲染滚动条
|
||||
|
||||
> 需要先注册 Scrollbar 插件
|
||||
> v0.7.1+, 需要先注册 Scrollbar 插件
|
||||
|
||||
滚动条分为水平和垂直滚动条,所以你需要创建如下模板:
|
||||
|
||||
```html
|
||||
<!-- 垂直 -->
|
||||
<div class="scrollbar verticalScrollbar" ref="verticalScrollbarRef">
|
||||
<div class="scrollbar verticalScrollbar" ref="verticalScrollbarRef" @click="onVerticalScrollbarClick">
|
||||
<div
|
||||
class="scrollbarInner"
|
||||
:style="verticalScrollbarStyle"
|
||||
@click.stop
|
||||
@mousedown="onVerticalScrollbarMousedown"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 水平 -->
|
||||
<div class="scrollbar horizontalScrollbar" ref="horizontalScrollbarRef">
|
||||
<div class="scrollbar horizontalScrollbar" ref="horizontalScrollbarRef" @click="onHorizontalScrollbarClick">
|
||||
<div
|
||||
class="scrollbarInner"
|
||||
:style="horizontalScrollbarStyle"
|
||||
@click.stop
|
||||
@mousedown="onHorizontalScrollbarMousedown"
|
||||
></div>
|
||||
</div>
|
||||
@@ -63,7 +65,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
垂直滚动条的`top`和`height`、水平滚动条的`left`和`width`值需要调用插件的方法获取。
|
||||
垂直滚动条的`top`和`height`、水平滚动条的`left`和`width`值需要根据插件返回的数据进行设置。
|
||||
|
||||
首先你需要调用`setScrollBarWrapSize`方法传递你的滚动条容器的宽和高:
|
||||
|
||||
@@ -82,13 +84,13 @@ mindMap.scrollbar.setScrollBarWrapSize(width, height)
|
||||
```js
|
||||
mindMap.('scrollbar_change', this.updateScrollbar)
|
||||
|
||||
// 调用插件方法更新滚动条位置和大小
|
||||
// 根据事件返回的滚动条数据更新滚动条元素:
|
||||
{
|
||||
updateScrollbar() {
|
||||
updateScrollbar(data) {
|
||||
const {
|
||||
vertical,
|
||||
horizontal
|
||||
} = mindMap.scrollbar.calculationScrollbar()
|
||||
} = data
|
||||
this.verticalScrollbarStyle = {
|
||||
top: vertical.top + '%',
|
||||
height: vertical.height + '%'
|
||||
@@ -101,7 +103,7 @@ mindMap.('scrollbar_change', this.updateScrollbar)
|
||||
}
|
||||
```
|
||||
|
||||
调用插件的`calculationScrollbar`方法来获取滚动条元素的位置和大小,返回的是百分比数值,所以需要添加`%`。
|
||||
事件返回数据的是百分比数值,所以需要添加`%`。
|
||||
|
||||
最后,需要给滚动条元素绑定`mousedown`事件,并且调用插件的`onMousedown`方法:
|
||||
|
||||
@@ -113,4 +115,16 @@ mindMap.scrollbar.onMousedown(e, 'vertical')
|
||||
mindMap.scrollbar.onMousedown(e, 'horizontal')
|
||||
```
|
||||
|
||||
这样就能实现鼠标拖动滚动条更新画布位置的功能。
|
||||
|
||||
如果你还需要实现点击滚动条容器元素实现滚动条位置的跳变功能,那么需要给滚动条元素绑定点击事件,并且调用插件的`onClick`方法:
|
||||
|
||||
```js
|
||||
// 垂直滚动条元素
|
||||
mindMap.scrollbar.onClick(e, 'vertical')
|
||||
|
||||
// 水平滚动条元素
|
||||
mindMap.scrollbar.onClick(e, 'horizontal')
|
||||
```
|
||||
|
||||
以上就是实现滚动条渲染的全部步骤。
|
||||
@@ -2,23 +2,25 @@
|
||||
<div>
|
||||
<h1>如何渲染滚动条</h1>
|
||||
<blockquote>
|
||||
<p>需要先注册 Scrollbar 插件</p>
|
||||
<p>v0.7.1+, 需要先注册 Scrollbar 插件</p>
|
||||
</blockquote>
|
||||
<p>滚动条分为水平和垂直滚动条,所以你需要创建如下模板:</p>
|
||||
<pre class="hljs"><code><span class="hljs-comment"><!-- 垂直 --></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"scrollbar verticalScrollbar"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"verticalScrollbarRef"</span>></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"scrollbar verticalScrollbar"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"verticalScrollbarRef"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"onVerticalScrollbarClick"</span>></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">div</span>
|
||||
<span class="hljs-attr">class</span>=<span class="hljs-string">"scrollbarInner"</span>
|
||||
<span class="hljs-attr">:style</span>=<span class="hljs-string">"verticalScrollbarStyle"</span>
|
||||
@<span class="hljs-attr">click.stop</span>
|
||||
@<span class="hljs-attr">mousedown</span>=<span class="hljs-string">"onVerticalScrollbarMousedown"</span>
|
||||
></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||||
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||||
|
||||
<span class="hljs-comment"><!-- 水平 --></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"scrollbar horizontalScrollbar"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"horizontalScrollbarRef"</span>></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"scrollbar horizontalScrollbar"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"horizontalScrollbarRef"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"onHorizontalScrollbarClick"</span>></span>
|
||||
<span class="hljs-tag"><<span class="hljs-name">div</span>
|
||||
<span class="hljs-attr">class</span>=<span class="hljs-string">"scrollbarInner"</span>
|
||||
<span class="hljs-attr">:style</span>=<span class="hljs-string">"horizontalScrollbarStyle"</span>
|
||||
@<span class="hljs-attr">click.stop</span>
|
||||
@<span class="hljs-attr">mousedown</span>=<span class="hljs-string">"onHorizontalScrollbarMousedown"</span>
|
||||
></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||||
<span class="hljs-tag"></<span class="hljs-name">div</span>></span>
|
||||
@@ -58,7 +60,7 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>垂直滚动条的<code>top</code>和<code>height</code>、水平滚动条的<code>left</code>和<code>width</code>值需要调用插件的方法获取。</p>
|
||||
<p>垂直滚动条的<code>top</code>和<code>height</code>、水平滚动条的<code>left</code>和<code>width</code>值需要根据插件返回的数据进行设置。</p>
|
||||
<p>首先你需要调用<code>setScrollBarWrapSize</code>方法传递你的滚动条容器的宽和高:</p>
|
||||
<pre class="hljs"><code><span class="hljs-comment">// 水平滚动条容器的宽度</span>
|
||||
<span class="hljs-keyword">const</span> { width } = <span class="hljs-built_in">this</span>.$refs.horizontalScrollbarRef.getBoundingClientRect()
|
||||
@@ -70,13 +72,13 @@ mindMap.scrollbar.setScrollBarWrapSize(width, height)
|
||||
<p>然后你需要监听<code>scrollbar_change</code>方法来获取滚动条大小和位置数据:</p>
|
||||
<pre class="hljs"><code>mindMap.(<span class="hljs-string">'scrollbar_change'</span>, <span class="hljs-built_in">this</span>.updateScrollbar)
|
||||
|
||||
<span class="hljs-comment">// 调用插件方法更新滚动条位置和大小</span>
|
||||
<span class="hljs-comment">// 根据事件返回的滚动条数据更新滚动条元素:</span>
|
||||
{
|
||||
<span class="hljs-function"><span class="hljs-title">updateScrollbar</span>(<span class="hljs-params"></span>)</span> {
|
||||
<span class="hljs-function"><span class="hljs-title">updateScrollbar</span>(<span class="hljs-params">data</span>)</span> {
|
||||
<span class="hljs-keyword">const</span> {
|
||||
vertical,
|
||||
horizontal
|
||||
} = mindMap.scrollbar.calculationScrollbar()
|
||||
} = data
|
||||
<span class="hljs-built_in">this</span>.verticalScrollbarStyle = {
|
||||
<span class="hljs-attr">top</span>: vertical.top + <span class="hljs-string">'%'</span>,
|
||||
<span class="hljs-attr">height</span>: vertical.height + <span class="hljs-string">'%'</span>
|
||||
@@ -88,7 +90,7 @@ mindMap.scrollbar.setScrollBarWrapSize(width, height)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>调用插件的<code>calculationScrollbar</code>方法来获取滚动条元素的位置和大小,返回的是百分比数值,所以需要添加<code>%</code>。</p>
|
||||
<p>事件返回数据的是百分比数值,所以需要添加<code>%</code>。</p>
|
||||
<p>最后,需要给滚动条元素绑定<code>mousedown</code>事件,并且调用插件的<code>onMousedown</code>方法:</p>
|
||||
<pre class="hljs"><code><span class="hljs-comment">// 垂直滚动条元素</span>
|
||||
mindMap.scrollbar.onMousedown(e, <span class="hljs-string">'vertical'</span>)
|
||||
@@ -96,6 +98,14 @@ mindMap.scrollbar.onMousedown(e, <span class="hljs-string">'vertical'<
|
||||
<span class="hljs-comment">// 水平滚动条元素</span>
|
||||
mindMap.scrollbar.onMousedown(e, <span class="hljs-string">'horizontal'</span>)
|
||||
</code></pre>
|
||||
<p>这样就能实现鼠标拖动滚动条更新画布位置的功能。</p>
|
||||
<p>如果你还需要实现点击滚动条容器元素实现滚动条位置的跳变功能,那么需要给滚动条元素绑定点击事件,并且调用插件的<code>onClick</code>方法:</p>
|
||||
<pre class="hljs"><code><span class="hljs-comment">// 垂直滚动条元素</span>
|
||||
mindMap.scrollbar.onClick(e, <span class="hljs-string">'vertical'</span>)
|
||||
|
||||
<span class="hljs-comment">// 水平滚动条元素</span>
|
||||
mindMap.scrollbar.onClick(e, <span class="hljs-string">'horizontal'</span>)
|
||||
</code></pre>
|
||||
<p>以上就是实现滚动条渲染的全部步骤。</p>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -77,6 +77,8 @@ mind-map在容器中启动了8080端口作为web服务入口,通过docker运
|
||||
|
||||
浏览器打开 127.0.0.1:8081 即可使用Web 思维导图功能。
|
||||
|
||||
[在群晖上以 Docker 方式安装](https://laosu.gq/2023/09/02/%E5%BC%BA%E5%A4%A7%E7%9A%84%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%E5%BA%93SimpleMindMap/)
|
||||
|
||||
## 对接自己的存储服务
|
||||
|
||||
应用数据默认存储在浏览器本地,浏览器本地存储容量是比较小的,所以当在思维导图中插入更多图片后很容易触发限制,所以更好的选择是对接你自己的存储服务,这通常有两种方式:
|
||||
|
||||
@@ -48,6 +48,7 @@ npm link simple-mind-map
|
||||
<p>mind-map在容器中启动了8080端口作为web服务入口,通过docker运行容器时,需要指定本地映射端口,上面案例中,我们通过本地的8081端口映射到容器端口8080。</p>
|
||||
<p>安装完成后,通过 <code>docker ps</code> 查看容器运行状态。</p>
|
||||
<p>浏览器打开 127.0.0.1:8081 即可使用Web 思维导图功能。</p>
|
||||
<p><a href="https://laosu.gq/2023/09/02/%E5%BC%BA%E5%A4%A7%E7%9A%84%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE%E5%BA%93SimpleMindMap/">在群晖上以 Docker 方式安装</a></p>
|
||||
<h2>对接自己的存储服务</h2>
|
||||
<p>应用数据默认存储在浏览器本地,浏览器本地存储容量是比较小的,所以当在思维导图中插入更多图片后很容易触发限制,所以更好的选择是对接你自己的存储服务,这通常有两种方式:</p>
|
||||
<h3>第一种</h3>
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
可以选中一个节点添加概要,如果想给多个节点添加一个概要,只能通过给它们的父节点添加来实现。
|
||||
|
||||
概要节点后面无法再添加节点,后续该特性也不会支持。
|
||||
概要节点后面无法再添加节点,后续该特性大概率也不会支持。
|
||||
|
||||
## 关联线
|
||||
|
||||
关联线添加完后要删除,需要先点击选中关联线,然后按删除键即可。
|
||||
添加关联线:先激活某个节点,然后点击上方工具栏的【关联线】按钮,最后点击要关联到的节点即可创建完成。
|
||||
|
||||
删除关联线:需要先点击选中关联线,然后按删除键即可。
|
||||
@@ -3,9 +3,10 @@
|
||||
<h1>概要/关联线</h1>
|
||||
<h2>概要</h2>
|
||||
<p>可以选中一个节点添加概要,如果想给多个节点添加一个概要,只能通过给它们的父节点添加来实现。</p>
|
||||
<p>概要节点后面无法再添加节点,后续该特性也不会支持。</p>
|
||||
<p>概要节点后面无法再添加节点,后续该特性大概率也不会支持。</p>
|
||||
<h2>关联线</h2>
|
||||
<p>关联线添加完后要删除,需要先点击选中关联线,然后按删除键即可。</p>
|
||||
<p>添加关联线:先激活某个节点,然后点击上方工具栏的【关联线】按钮,最后点击要关联到的节点即可创建完成。</p>
|
||||
<p>删除关联线:需要先点击选中关联线,然后按删除键即可。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
11
web/src/pages/Doc/zh/help3/index.md
Normal file
11
web/src/pages/Doc/zh/help3/index.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 打开预览在线文件
|
||||
|
||||
v0.7.0+版本支持打开url中携带的在线文件:
|
||||
|
||||
```
|
||||
https://wanglin2.github.io/mind-map/#/?fileURL=http://xxx.com/xxx.xmind
|
||||
```
|
||||
|
||||
在`fileURL`参数后带上你的在线文件url即可,目前支持`.xmind`、`.smm`、`.json`、`.md`后缀的文件url。
|
||||
|
||||
需要注意的是你在打开在线文件的情况下编辑并不会修改在线文件,改动会保存在你的浏览器本地,你可以选择导出文件。
|
||||
21
web/src/pages/Doc/zh/help3/index.vue
Normal file
21
web/src/pages/Doc/zh/help3/index.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>打开预览在线文件</h1>
|
||||
<p>v0.7.0+版本支持打开url中携带的在线文件:</p>
|
||||
<pre class="hljs"><code>https://wanglin2.github.io/mind-map/#/?fileURL=http://xxx.com/xxx.xmind
|
||||
</code></pre>
|
||||
<p>在<code>fileURL</code>参数后带上你的在线文件url即可,目前支持<code>.xmind</code>、<code>.smm</code>、<code>.json</code>、<code>.md</code>后缀的文件url。</p>
|
||||
<p>需要注意的是你在打开在线文件的情况下编辑并不会修改在线文件,改动会保存在你的浏览器本地,你可以选择导出文件。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
23
web/src/pages/Doc/zh/help4/index.md
Normal file
23
web/src/pages/Doc/zh/help4/index.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 复制粘贴
|
||||
|
||||
## 复制粘贴画布内的节点
|
||||
|
||||
你可以通过选中某个节点,然后按`ctrl`+`c`快捷键来复制节点,如果选中多个节点,目前只支持复制第一个节点,然后点击你要复制到的节点,按`ctrl`+`v`快捷键粘贴即可。
|
||||
|
||||
除了快捷键,也可以通过右键菜单来操作。
|
||||
|
||||
## 支持粘贴剪贴板中的数据
|
||||
|
||||
当你选中某个节点后,直接按`ctrl`+`v`快捷键时,如果你的剪贴板中有数据,会粘贴你剪贴板中的数据,目前支持文本和图片两种格式,如果你的剪贴板中有图片,那么会将图片粘贴到你选中的节点里,如果存在文本,那么会以该文件创建一个子节点。
|
||||
|
||||
如果你的剪贴板中存在数据,又在画布里复制了节点数据,那么是以谁最后改变了为准。
|
||||
|
||||
## 支持跨浏览器标签、跨浏览器复制
|
||||
|
||||
如果你在多个浏览器里打开了思维导图编辑页面,那么可以复制一个页面的节点数据到另一个页面进行粘贴。
|
||||
|
||||
在客户端场景下打开了多个编辑页面时比较常用。
|
||||
|
||||
## 支持粘贴知犀思维导图数据
|
||||
|
||||
目前支持复制知犀思维导图的节点数据粘贴到本思维导图里。
|
||||
27
web/src/pages/Doc/zh/help4/index.vue
Normal file
27
web/src/pages/Doc/zh/help4/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>复制粘贴</h1>
|
||||
<h2>复制粘贴画布内的节点</h2>
|
||||
<p>你可以通过选中某个节点,然后按<code>ctrl</code>+<code>c</code>快捷键来复制节点,如果选中多个节点,目前只支持复制第一个节点,然后点击你要复制到的节点,按<code>ctrl</code>+<code>v</code>快捷键粘贴即可。</p>
|
||||
<p>除了快捷键,也可以通过右键菜单来操作。</p>
|
||||
<h2>支持粘贴剪贴板中的数据</h2>
|
||||
<p>当你选中某个节点后,直接按<code>ctrl</code>+<code>v</code>快捷键时,如果你的剪贴板中有数据,会粘贴你剪贴板中的数据,目前支持文本和图片两种格式,如果你的剪贴板中有图片,那么会将图片粘贴到你选中的节点里,如果存在文本,那么会以该文件创建一个子节点。</p>
|
||||
<p>如果你的剪贴板中存在数据,又在画布里复制了节点数据,那么是以谁最后改变了为准。</p>
|
||||
<h2>支持跨浏览器标签、跨浏览器复制</h2>
|
||||
<p>如果你在多个浏览器里打开了思维导图编辑页面,那么可以复制一个页面的节点数据到另一个页面进行粘贴。</p>
|
||||
<p>在客户端场景下打开了多个编辑页面时比较常用。</p>
|
||||
<h2>支持粘贴知犀思维导图数据</h2>
|
||||
<p>目前支持复制知犀思维导图的节点数据粘贴到本思维导图里。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
5
web/src/pages/Doc/zh/help5/index.md
Normal file
5
web/src/pages/Doc/zh/help5/index.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 导出
|
||||
|
||||
## 导出为xmind
|
||||
|
||||
导出的xmind文件无法在xmind8及以下版本打开,请使用最新版xmind软件。
|
||||
18
web/src/pages/Doc/zh/help5/index.vue
Normal file
18
web/src/pages/Doc/zh/help5/index.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>导出</h1>
|
||||
<h2>导出为xmind</h2>
|
||||
<p>导出的xmind文件无法在xmind8及以下版本打开,请使用最新版xmind软件。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -185,4 +185,12 @@
|
||||
<img src="../../../../assets/avatar/南风.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>南风</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/蜉蝣撼大叔.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>蜉蝣撼大叔</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/乙.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>乙</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -145,6 +145,14 @@
|
||||
<img src="../../../../assets/avatar/南风.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>南风</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/蜉蝣撼大叔.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>蜉蝣撼大叔</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/乙.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>乙</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# Scrollbar 插件
|
||||
|
||||
> v0.7.0+
|
||||
>
|
||||
> v0.7.1+进行了重构,下面的文档为新文档。
|
||||
|
||||
该插件用于帮助开发水平和垂直滚动条的功能。
|
||||
该插件用于帮助开发水平和垂直滚动条的功能。详细使用方式请参考教程。
|
||||
|
||||
## 注册
|
||||
|
||||
@@ -16,9 +18,26 @@ MindMap.usePlugin(Scrollbar)
|
||||
|
||||
## 事件
|
||||
|
||||
#### scrollbar_change
|
||||
#### scrollbar_change(data)
|
||||
|
||||
当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。
|
||||
- `data`:滚动条数据,格式如下:
|
||||
|
||||
```js
|
||||
{
|
||||
// 垂直滚动条
|
||||
vertical: {
|
||||
top,// 垂直滚动条的top值,百分比数值
|
||||
height// 垂直滚动条的高度,百分比数值
|
||||
},
|
||||
// 水平滚动条
|
||||
horizontal: {
|
||||
left,// 水平滚动条的left值,百分比数值
|
||||
width// 水平滚动条的宽度,百分比数值
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。接收一个参数,代表当前最新的滚动条位置和大小信息,你可以使用它来更新滚动条元素的样式。
|
||||
|
||||
## 方法
|
||||
|
||||
@@ -32,9 +51,9 @@ MindMap.usePlugin(Scrollbar)
|
||||
|
||||
### calculationScrollbar()
|
||||
|
||||
> 需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。
|
||||
> 通常你不需要调用该方法,如果初次渲染滚动条时滚动条没有更新,那么可以手动调用该方法获取滚动条数据。
|
||||
>
|
||||
> 一般需要监听scrollbar_change事件,然后调用该方法更新滚动条。
|
||||
> 需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。
|
||||
|
||||
返回值:
|
||||
|
||||
@@ -53,7 +72,7 @@ MindMap.usePlugin(Scrollbar)
|
||||
}
|
||||
```
|
||||
|
||||
获取滚动条大小和位置,你可以根据返回值来设置到滚动条元素上,达到渲染和关心滚动条的效果。
|
||||
获取滚动条大小和位置。
|
||||
|
||||
### onMousedown(e, type)
|
||||
|
||||
@@ -61,4 +80,12 @@ MindMap.usePlugin(Scrollbar)
|
||||
|
||||
- `type`:按下的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。
|
||||
|
||||
滚动条元素的鼠标按下事件时需要调用该方法。
|
||||
滚动条元素的鼠标按下事件时需要调用该方法。
|
||||
|
||||
### onClick(e, type)
|
||||
|
||||
- `e`:鼠标点击事件的事件对象。
|
||||
|
||||
- `type`:鼠标点击的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。
|
||||
|
||||
滚动条元素的的点击事件时需要调用该方法。
|
||||
@@ -3,8 +3,9 @@
|
||||
<h1>Scrollbar 插件</h1>
|
||||
<blockquote>
|
||||
<p>v0.7.0+</p>
|
||||
<p>v0.7.1+进行了重构,下面的文档为新文档。</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> Scrollbar <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/plugins/Scrollbar.js'</span>
|
||||
@@ -12,8 +13,24 @@ MindMap.usePlugin(Scrollbar)
|
||||
</code></pre>
|
||||
<p>注册完且实例化<code>MindMap</code>后可通过<code>mindMap.scrollbar</code>获取到该实例。</p>
|
||||
<h2>事件</h2>
|
||||
<h4>scrollbar_change</h4>
|
||||
<p>当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。</p>
|
||||
<h4>scrollbar_change(data)</h4>
|
||||
<ul>
|
||||
<li><code>data</code>:滚动条数据,格式如下:</li>
|
||||
</ul>
|
||||
<pre class="hljs"><code>{
|
||||
<span class="hljs-comment">// 垂直滚动条</span>
|
||||
<span class="hljs-attr">vertical</span>: {
|
||||
top,<span class="hljs-comment">// 垂直滚动条的top值,百分比数值</span>
|
||||
height<span class="hljs-comment">// 垂直滚动条的高度,百分比数值</span>
|
||||
},
|
||||
<span class="hljs-comment">// 水平滚动条</span>
|
||||
<span class="hljs-attr">horizontal</span>: {
|
||||
left,<span class="hljs-comment">// 水平滚动条的left值,百分比数值</span>
|
||||
width<span class="hljs-comment">// 水平滚动条的宽度,百分比数值</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。接收一个参数,代表当前最新的滚动条位置和大小信息,你可以使用它来更新滚动条元素的样式。</p>
|
||||
<h2>方法</h2>
|
||||
<h3>setScrollBarWrapSize(width, height)</h3>
|
||||
<ul>
|
||||
@@ -27,8 +44,8 @@ MindMap.usePlugin(Scrollbar)
|
||||
<p>设置滚动条容器的大小,对于水平滚动条,即容器的宽度,对于垂直滚动条,即容器的高度。当你的滚动条容器尺寸改变时需要再次调用该方法。</p>
|
||||
<h3>calculationScrollbar()</h3>
|
||||
<blockquote>
|
||||
<p>通常你不需要调用该方法,如果初次渲染滚动条时滚动条没有更新,那么可以手动调用该方法获取滚动条数据。</p>
|
||||
<p>需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。</p>
|
||||
<p>一般需要监听scrollbar_change事件,然后调用该方法更新滚动条。</p>
|
||||
</blockquote>
|
||||
<p>返回值:</p>
|
||||
<pre class="hljs"><code>{
|
||||
@@ -44,7 +61,7 @@ MindMap.usePlugin(Scrollbar)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>获取滚动条大小和位置,你可以根据返回值来设置到滚动条元素上,达到渲染和关心滚动条的效果。</p>
|
||||
<p>获取滚动条大小和位置。</p>
|
||||
<h3>onMousedown(e, type)</h3>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -55,6 +72,16 @@ MindMap.usePlugin(Scrollbar)
|
||||
</li>
|
||||
</ul>
|
||||
<p>滚动条元素的鼠标按下事件时需要调用该方法。</p>
|
||||
<h3>onClick(e, type)</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>e</code>:鼠标点击事件的事件对象。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>type</code>:鼠标点击的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>滚动条元素的的点击事件时需要调用该方法。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -151,4 +151,14 @@ import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min"
|
||||
|
||||
### 2.报错`Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')`
|
||||
|
||||
原因为安装的`@svgdotjs/svg.js`版本太高,手动降到`3.0.16`版本即可。
|
||||
原因为安装的`@svgdotjs/svg.js`版本太高,手动降到`3.0.16`版本即可。
|
||||
|
||||
### 3.TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46
|
||||
|
||||
可以在打包配置文件中增加如下配置:
|
||||
|
||||
```js
|
||||
resolve: { alias: { stream: "stream-browserify" } }
|
||||
```
|
||||
|
||||
不同的打包工具可能具体配置不一样,原理就是排除`stream`依赖。
|
||||
@@ -99,6 +99,11 @@ npm run build
|
||||
<p>如果需要二次开发,也就是必须要使用未打包代码的话,如果你不需要解析<code>xmind</code>文件的话,可以去除<code>xmind</code>模块,如果需要的话那么可以尝试换成其他的解析<code>xml</code>为<code>json</code>的库。</p>
|
||||
<h3>2.报错<code>Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')</code></h3>
|
||||
<p>原因为安装的<code>@svgdotjs/svg.js</code>版本太高,手动降到<code>3.0.16</code>版本即可。</p>
|
||||
<h3>3.TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46</h3>
|
||||
<p>可以在打包配置文件中增加如下配置:</p>
|
||||
<pre class="hljs"><code>resolve: { <span class="hljs-attr">alias</span>: { <span class="hljs-attr">stream</span>: <span class="hljs-string">"stream-browserify"</span> } }
|
||||
</code></pre>
|
||||
<p>不同的打包工具可能具体配置不一样,原理就是排除<code>stream</code>依赖。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
<el-dropdown-item command="devDoc">开发文档</el-dropdown-item>
|
||||
<el-dropdown-item command="site">官方网站</el-dropdown-item>
|
||||
<el-dropdown-item command="issue">意见反馈</el-dropdown-item>
|
||||
<el-dropdown-item disabled>当前:v{{ version }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
@@ -94,6 +95,7 @@ import { langList } from '@/config'
|
||||
import i18n from '@/i18n'
|
||||
import { storeLang, getLang } from '@/api'
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import pkg from 'simple-mind-map/package.json'
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
@@ -114,6 +116,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
version: pkg.version,
|
||||
langList,
|
||||
lang: '',
|
||||
isReadonly: false,
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<div class="scrollbarContainer" :class="{ isDark: isDark }">
|
||||
<!-- 竖向 -->
|
||||
<div class="scrollbar verticalScrollbar" ref="verticalScrollbarRef">
|
||||
<div
|
||||
class="scrollbar verticalScrollbar"
|
||||
ref="verticalScrollbarRef"
|
||||
@click="onVerticalScrollbarClick"
|
||||
>
|
||||
<div
|
||||
class="scrollbarInner"
|
||||
:style="verticalScrollbarStyle"
|
||||
@click.stop
|
||||
@mousedown="onVerticalScrollbarMousedown"
|
||||
></div>
|
||||
</div>
|
||||
<!-- 横向 -->
|
||||
<div class="scrollbar horizontalScrollbar" ref="horizontalScrollbarRef">
|
||||
<div
|
||||
class="scrollbar horizontalScrollbar"
|
||||
ref="horizontalScrollbarRef"
|
||||
@click="onHorizontalScrollbarClick"
|
||||
>
|
||||
<div
|
||||
class="scrollbarInner"
|
||||
:style="horizontalScrollbarStyle"
|
||||
@click.stop
|
||||
@mousedown="onHorizontalScrollbarMousedown"
|
||||
></div>
|
||||
</div>
|
||||
@@ -67,11 +77,7 @@ export default {
|
||||
},
|
||||
|
||||
// 调用插件方法更新滚动条位置和大小
|
||||
updateScrollbar() {
|
||||
const {
|
||||
vertical,
|
||||
horizontal
|
||||
} = this.mindMap.scrollbar.calculationScrollbar()
|
||||
updateScrollbar({ vertical, horizontal }) {
|
||||
this.verticalScrollbarStyle = {
|
||||
top: vertical.top + '%',
|
||||
height: vertical.height + '%'
|
||||
@@ -87,9 +93,19 @@ export default {
|
||||
this.mindMap.scrollbar.onMousedown(e, 'vertical')
|
||||
},
|
||||
|
||||
// 垂直滚动条点击事件调用插件方法
|
||||
onVerticalScrollbarClick(e) {
|
||||
this.mindMap.scrollbar.onClick(e, 'vertical')
|
||||
},
|
||||
|
||||
// 水平滚动条按下事件调用插件方法
|
||||
onHorizontalScrollbarMousedown(e) {
|
||||
this.mindMap.scrollbar.onMousedown(e, 'horizontal')
|
||||
},
|
||||
|
||||
// 水平滚动条点击事件调用插件方法
|
||||
onHorizontalScrollbarClick(e) {
|
||||
this.mindMap.scrollbar.onClick(e, 'horizontal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
dataList: [
|
||||
{
|
||||
icon: 'iconstar',
|
||||
value: 'Github star数量600+'
|
||||
value: 'Github star数量700+'
|
||||
},
|
||||
{
|
||||
icon: 'iconfork',
|
||||
@@ -54,7 +54,7 @@ export default {
|
||||
},
|
||||
{
|
||||
icon: 'iconteamwork',
|
||||
value: '代码贡献者8+'
|
||||
value: '代码贡献者10+'
|
||||
}
|
||||
],
|
||||
functionList: [
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import EditPage from '@/pages/Edit/Index'
|
||||
import DocPage from '@/pages/Doc/Index'
|
||||
import routerList from '@/pages/Doc/routerList'
|
||||
|
||||
@@ -58,7 +57,7 @@ const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Edit',
|
||||
component: EditPage
|
||||
component: () => import(`./pages/Edit/Index.vue`)
|
||||
},
|
||||
// 开发文档
|
||||
...createTypeRouterList('doc', 'introduction'),
|
||||
|
||||
Reference in New Issue
Block a user