Compare commits
73 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 | ||
|
|
cd5556aad5 | ||
|
|
063af62cf6 | ||
|
|
a4611d11a8 | ||
|
|
5d1f460a94 | ||
|
|
075e578bb4 | ||
|
|
32c17921ca | ||
|
|
7c470c9b88 | ||
|
|
e472b840f1 | ||
|
|
726460c812 | ||
|
|
7ccf796c34 | ||
|
|
c183306ab2 | ||
|
|
29bb27aa23 | ||
|
|
7f9d03c8af | ||
|
|
c3a0e09f6d | ||
|
|
1d497b2f13 | ||
|
|
f43a2ebdef | ||
|
|
4c3f3cb1ab | ||
|
|
a404a71ba2 | ||
|
|
6b4c118a2b | ||
|
|
1157257911 | ||
|
|
f2642c9d63 | ||
|
|
91bc0351b8 | ||
|
|
df2dc96ba5 | ||
|
|
3d86650f22 | ||
|
|
a31e93bacf | ||
|
|
a3362e44fe | ||
|
|
f7f234b4cb | ||
|
|
48f1de5c25 | ||
|
|
c533459da1 | ||
|
|
d99895b845 | ||
|
|
c65393d1bc | ||
|
|
5d133f74cf | ||
|
|
5997c98b8f | ||
|
|
217d66f692 | ||
|
|
d14d887c1a | ||
|
|
e36e238c2f | ||
|
|
1b0646af6d | ||
|
|
a24f7a73c8 | ||
|
|
b35dd282ec | ||
|
|
8c0c2c5bc4 | ||
|
|
3763cd0efc |
20
README.md
@@ -87,9 +87,7 @@ const mindMap = new MindMap({
|
||||
|
||||
# 微信交流群
|
||||
|
||||
<img src="./qrcode.jpg" style="width: 300px" />
|
||||
|
||||
如果已过期,可以微信添加`wanglinguanfang`拉你入群。
|
||||
群聊人数较多,无法通过二维码入群,可以微信添加`wanglinguanfang`拉你入群。
|
||||
|
||||
# 请作者喝杯咖啡
|
||||
|
||||
@@ -165,4 +163,20 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>Luke</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>
|
||||
<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?3f27c97d35a340dfd1c0" rel="stylesheet"><link href="dist/css/app.css?3f27c97d35a340dfd1c0" 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?3f27c97d35a340dfd1c0"></script><script src="dist/js/app.js?3f27c97d35a340dfd1c0"></script></body></html>
|
||||
}</script><script src="dist/js/chunk-vendors.js?8e81eea7e47ca2aeb9e0"></script><script src="dist/js/app.js?8e81eea7e47ca2aeb9e0"></script></body></html>
|
||||
BIN
qrcode.jpg
|
Before Width: | Height: | Size: 49 KiB |
@@ -13,6 +13,7 @@ import NodeImgAdjust from './src/plugins/NodeImgAdjust.js'
|
||||
import TouchEvent from './src/plugins/TouchEvent.js'
|
||||
import Search from './src/plugins/Search.js'
|
||||
import Painter from './src/plugins/Painter.js'
|
||||
import Scrollbar from './src/plugins/Scrollbar.js'
|
||||
import xmind from './src/parse/xmind.js'
|
||||
import markdown from './src/parse/markdown.js'
|
||||
import icons from './src/svg/icons.js'
|
||||
@@ -42,5 +43,6 @@ MindMap
|
||||
.usePlugin(NodeImgAdjust)
|
||||
.usePlugin(Search)
|
||||
.usePlugin(Painter)
|
||||
.usePlugin(Scrollbar)
|
||||
|
||||
export default MindMap
|
||||
@@ -11,10 +11,11 @@ import {
|
||||
layoutValueList,
|
||||
CONSTANTS,
|
||||
commonCaches,
|
||||
ERROR_TYPES
|
||||
ERROR_TYPES,
|
||||
cssContent
|
||||
} from './src/constants/constant'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import { simpleDeepClone, getType } from './src/utils'
|
||||
import { simpleDeepClone, getType, getObjectChangedProps } from './src/utils'
|
||||
import defaultTheme, {
|
||||
checkIsNodeSizeIndependenceConfig
|
||||
} from './src/themes/default'
|
||||
@@ -37,6 +38,10 @@ class MindMap {
|
||||
this.height = this.elRect.height
|
||||
if (this.width <= 0 || this.height <= 0) throw new Error('容器元素el的宽高不能为0')
|
||||
|
||||
// 添加css
|
||||
this.cssEl = null
|
||||
this.addCss()
|
||||
|
||||
// 画布
|
||||
this.svg = SVG().addTo(this.el).size(this.width, this.height)
|
||||
this.draw = this.svg.group()
|
||||
@@ -101,6 +106,19 @@ class MindMap {
|
||||
return opt
|
||||
}
|
||||
|
||||
// 添加必要的css样式到页面
|
||||
addCss() {
|
||||
this.cssEl = document.createElement('style')
|
||||
this.cssEl.type = 'text/css'
|
||||
this.cssEl.innerHTML = cssContent
|
||||
document.head.appendChild(this.cssEl)
|
||||
}
|
||||
|
||||
// 移除css
|
||||
removeCss() {
|
||||
document.head.removeChild(this.cssEl)
|
||||
}
|
||||
|
||||
// 渲染,部分渲染
|
||||
render(callback, source = '') {
|
||||
this.batchExecution.push('render', () => {
|
||||
@@ -183,9 +201,11 @@ class MindMap {
|
||||
|
||||
// 设置主题配置
|
||||
setThemeConfig(config) {
|
||||
// 计算改变了的配置
|
||||
const changedConfig = getObjectChangedProps(this.themeConfig, config)
|
||||
this.opt.themeConfig = config
|
||||
// 检查改变的是否是节点大小无关的主题属性
|
||||
let res = checkIsNodeSizeIndependenceConfig(config)
|
||||
let res = checkIsNodeSizeIndependenceConfig(changedConfig)
|
||||
this.render(null, res ? '' : CONSTANTS.CHANGE_THEME)
|
||||
}
|
||||
|
||||
@@ -339,6 +359,8 @@ class MindMap {
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
let clone = svg.clone()
|
||||
// 添加必要的样式
|
||||
clone.add(SVG(`<style>${ cssContent }</style>`))
|
||||
// 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题
|
||||
if (
|
||||
(rect.width > origWidth || rect.height > origHeight) &&
|
||||
@@ -406,6 +428,9 @@ class MindMap {
|
||||
destroy() {
|
||||
// 移除插件
|
||||
;[...MindMap.pluginList].forEach(plugin => {
|
||||
if (this[plugin.instanceName].beforePluginDestroy) {
|
||||
this[plugin.instanceName].beforePluginDestroy()
|
||||
}
|
||||
this[plugin.instanceName] = null
|
||||
})
|
||||
// 解绑事件
|
||||
@@ -414,13 +439,16 @@ class MindMap {
|
||||
this.svg.remove()
|
||||
// 去除给容器元素设置的背景样式
|
||||
Style.removeBackgroundStyle(this.el)
|
||||
this.el.innerHTML = ''
|
||||
this.el = null
|
||||
this.removeCss()
|
||||
}
|
||||
}
|
||||
|
||||
// 插件列表
|
||||
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.6.17",
|
||||
"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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +329,9 @@ export const nodeDataNoStylePropList = [
|
||||
'uid',
|
||||
'activeStyle',
|
||||
'associativeLineTargets',
|
||||
'associativeLineTargetControlOffsets'
|
||||
'associativeLineTargetControlOffsets',
|
||||
'associativeLinePoint',
|
||||
'associativeLineText'
|
||||
]
|
||||
|
||||
// 数据缓存
|
||||
@@ -342,4 +348,30 @@ export const ERROR_TYPES = {
|
||||
LOAD_CLIPBOARD_IMAGE_ERROR: 'load_clipboard_image_error',
|
||||
BEFORE_TEXT_EDIT_ERROR: 'before_text_edit_error',
|
||||
EXPORT_ERROR: 'export_error'
|
||||
}
|
||||
}
|
||||
|
||||
// a4纸的宽高
|
||||
export const a4Size = {
|
||||
width: 592.28,
|
||||
height: 841.89
|
||||
}
|
||||
|
||||
// css
|
||||
export const cssContent = `
|
||||
/* 鼠标hover和激活时渲染的矩形 */
|
||||
.smm-hover-node{
|
||||
display: none;
|
||||
opacity: 0.6;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.smm-node:hover .smm-hover-node{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.smm-node.active .smm-hover-node{
|
||||
display: block;
|
||||
opacity: 1;
|
||||
stroke-width: 2;
|
||||
}
|
||||
`
|
||||
@@ -68,13 +68,21 @@ export const defaultOpt = {
|
||||
// 展开收起按钮的颜色
|
||||
expandBtnStyle: {
|
||||
color: '#808080',
|
||||
fill: '#fff'
|
||||
fill: '#fff',
|
||||
fontSize: 13,
|
||||
strokeColor: '#333333'
|
||||
},
|
||||
// 自定义展开收起按钮的图标
|
||||
expandBtnIcon: {
|
||||
open: '', // svg字符串
|
||||
close: ''
|
||||
},
|
||||
// 处理收起节点数量
|
||||
expandBtnNumHandler: num => {
|
||||
return num
|
||||
},
|
||||
// 是否显示带数量的收起按钮
|
||||
isShowExpandNum: true,
|
||||
// 是否只有当鼠标在画布内才响应快捷键事件
|
||||
enableShortcutOnlyWhenMouseInSvg: true,
|
||||
// 初始根节点的位置
|
||||
@@ -166,5 +174,17 @@ export const defaultOpt = {
|
||||
}
|
||||
`,
|
||||
// 开启鼠标双击复位思维导图位置及缩放
|
||||
enableDblclickReset: true
|
||||
enableDblclickReset: false,
|
||||
// 导出图片时canvas的缩放倍数,该配置会和window.devicePixelRatio值取最大值
|
||||
minExportImgCanvasScale: 2,
|
||||
// 节点鼠标hover和激活时显示的矩形边框的颜色
|
||||
hoverRectColor: 'rgb(94, 200, 248)',
|
||||
// 节点鼠标hover和激活时显示的矩形边框距节点内容的距离
|
||||
hoverRectPadding: 2,
|
||||
// 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中
|
||||
selectTextOnEnterEditText: false,
|
||||
// 删除节点后激活相邻节点
|
||||
deleteNodeActive: true,
|
||||
// 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动
|
||||
autoMoveWhenMouseInEdgeOnDrag: true
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ class Render {
|
||||
}
|
||||
})
|
||||
})
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
}
|
||||
|
||||
// 清除当前激活的节点
|
||||
@@ -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
|
||||
}
|
||||
@@ -407,7 +407,7 @@ class Render {
|
||||
// 激活节点需要显示展开收起按钮
|
||||
node.showExpandBtn()
|
||||
setTimeout(() => {
|
||||
node.updateNodeShape()
|
||||
node.updateNodeActive()
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
@@ -416,6 +416,7 @@ class Render {
|
||||
0,
|
||||
0
|
||||
)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
}
|
||||
|
||||
// 回退
|
||||
@@ -552,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
|
||||
@@ -579,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
|
||||
@@ -751,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
|
||||
@@ -763,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
|
||||
@@ -790,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
|
||||
@@ -802,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
|
||||
@@ -823,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 => {
|
||||
@@ -836,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)
|
||||
@@ -854,7 +879,14 @@ class Render {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -886,7 +918,7 @@ class Render {
|
||||
let copyData = copyNodeTree({}, node, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
this.mindMap.render()
|
||||
if (callback && typeof callback === 'function') {
|
||||
callback(copyData)
|
||||
@@ -901,7 +933,7 @@ class Render {
|
||||
// let copyData = copyNodeTree({}, node, false, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
toNode.nodeData.children.push(node.nodeData)
|
||||
this.mindMap.render()
|
||||
if (toNode.isRoot) {
|
||||
@@ -921,19 +953,9 @@ class Render {
|
||||
}
|
||||
|
||||
// 设置节点样式
|
||||
setNodeStyle(node, prop, value, isActive) {
|
||||
let data = {}
|
||||
if (isActive) {
|
||||
data = {
|
||||
activeStyle: {
|
||||
...(node.nodeData.data.activeStyle || {}),
|
||||
[prop]: value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = {
|
||||
[prop]: value
|
||||
}
|
||||
setNodeStyle(node, prop, value) {
|
||||
let data = {
|
||||
[prop]: value
|
||||
}
|
||||
// 如果开启了富文本,则需要应用到富文本上
|
||||
if (this.mindMap.richText) {
|
||||
@@ -954,18 +976,8 @@ class Render {
|
||||
}
|
||||
|
||||
// 设置节点多个样式
|
||||
setNodeStyles(node, style, isActive) {
|
||||
let data = {}
|
||||
if (isActive) {
|
||||
data = {
|
||||
activeStyle: {
|
||||
...(node.nodeData.data.activeStyle || {}),
|
||||
...style
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = style
|
||||
}
|
||||
setNodeStyles(node, style) {
|
||||
let data = { ...style }
|
||||
// 如果开启了富文本,则需要应用到富文本上
|
||||
if (this.mindMap.richText) {
|
||||
let config = this.mindMap.richText.normalStyleToRichTextStyle(style)
|
||||
@@ -1000,7 +1012,7 @@ class Render {
|
||||
} else {
|
||||
node.hideExpandBtn()
|
||||
}
|
||||
node.updateNodeShape()
|
||||
node.updateNodeActive()
|
||||
}
|
||||
|
||||
// 设置节点是否展开
|
||||
@@ -1112,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 || '',
|
||||
|
||||
@@ -63,7 +63,7 @@ export default class TextEdit {
|
||||
const node = activeNodeList[0]
|
||||
// 当正在输入中文或英文或数字时,如果没有按下组合键,那么自动进入文本编辑模式
|
||||
if (node && this.checkIsAutoEnterTextEditKey(e)) {
|
||||
this.show(node)
|
||||
this.show(node, e, false, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -93,7 +93,8 @@ export default class TextEdit {
|
||||
|
||||
// 显示文本编辑框
|
||||
// isInserting:是否是刚创建的节点
|
||||
async show(node, e, isInserting = false) {
|
||||
// isFromKeyDown:是否是在按键事件进入的编辑
|
||||
async show(node, e, isInserting = false, isFromKeyDown = false) {
|
||||
// 使用了自定义节点内容那么不响应编辑事件
|
||||
if (node.isUseCustomNodeContent()) {
|
||||
return
|
||||
@@ -114,10 +115,10 @@ export default class TextEdit {
|
||||
this.mindMap.view.translateXY(offsetLeft, offsetTop)
|
||||
let rect = node._textData.node.node.getBoundingClientRect()
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.showEditText(node, rect, isInserting)
|
||||
this.mindMap.richText.showEditText(node, rect, isInserting, isFromKeyDown)
|
||||
return
|
||||
}
|
||||
this.showEditTextBox(node, rect, isInserting)
|
||||
this.showEditTextBox(node, rect, isInserting, isFromKeyDown)
|
||||
}
|
||||
|
||||
// 处理画布缩放
|
||||
@@ -135,8 +136,10 @@ export default class TextEdit {
|
||||
}
|
||||
|
||||
// 显示文本编辑框
|
||||
showEditTextBox(node, rect, isInserting) {
|
||||
showEditTextBox(node, rect, isInserting, isFromKeyDown) {
|
||||
if (this.showTextEdit) return
|
||||
const { nodeTextEditZIndex, textAutoWrapWidth, selectTextOnEnterEditText } =
|
||||
this.mindMap.opt
|
||||
this.mindMap.emit('before_show_text_edit')
|
||||
this.registerTmpShortcut()
|
||||
if (!this.textEditNode) {
|
||||
@@ -169,15 +172,14 @@ export default class TextEdit {
|
||||
)
|
||||
let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true'
|
||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
||||
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth =
|
||||
this.mindMap.opt.textAutoWrapWidth * scale + 'px'
|
||||
this.textEditNode.style.maxWidth = textAutoWrapWidth * scale + 'px'
|
||||
if (isMultiLine && lineHeight !== 1) {
|
||||
this.textEditNode.style.transform = `translateY(${
|
||||
-((lineHeight * fontSize - fontSize) / 2) * scale
|
||||
@@ -188,7 +190,7 @@ export default class TextEdit {
|
||||
// if (!this.cacheEditingText) {
|
||||
// this.selectNodeText()
|
||||
// }
|
||||
if (isInserting) {
|
||||
if (isInserting || (selectTextOnEnterEditText && !isFromKeyDown)) {
|
||||
this.selectNodeText()
|
||||
} else {
|
||||
this.focus()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Style from './Style'
|
||||
import Shape from './Shape'
|
||||
import { G, ForeignObject, SVG } from '@svgdotjs/svg.js'
|
||||
import { G, ForeignObject, SVG, Rect } from '@svgdotjs/svg.js'
|
||||
import nodeGeneralizationMethods from './nodeGeneralization'
|
||||
import nodeExpandBtnMethods from './nodeExpandBtn'
|
||||
import nodeCommandWrapsMethods from './nodeCommandWraps'
|
||||
@@ -58,6 +58,7 @@ class Node {
|
||||
// 节点内容的容器
|
||||
this.group = null
|
||||
this.shapeNode = null // 节点形状节点
|
||||
this.hoverNode = null // 节点hover和激活的节点
|
||||
// 节点内容对象
|
||||
this._customNodeContent = null
|
||||
this._imgData = null
|
||||
@@ -269,6 +270,7 @@ class Node {
|
||||
layout() {
|
||||
// 清除之前的内容
|
||||
this.group.clear()
|
||||
const { hoverRectPadding } = this.mindMap.opt
|
||||
let { width, height, textContentItemMargin } = this
|
||||
let { paddingY } = this.getPaddingVale()
|
||||
const halfBorderWidth = this.getBorderWidth() / 2
|
||||
@@ -277,21 +279,32 @@ class Node {
|
||||
this.shapeNode = this.shapeInstance.createShape()
|
||||
this.shapeNode.addClass('smm-node-shape')
|
||||
this.shapeNode.translate(halfBorderWidth, halfBorderWidth)
|
||||
this.style.shape(this.shapeNode)
|
||||
this.group.add(this.shapeNode)
|
||||
this.updateNodeShape()
|
||||
// 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示
|
||||
this.renderExpandBtnPlaceholderRect()
|
||||
// 概要节点添加一个带所属节点id的类名
|
||||
if (this.isGeneralization && this.generalizationBelongNode) {
|
||||
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
||||
}
|
||||
// 激活hover和激活边框
|
||||
const addHoverNode = () => {
|
||||
this.hoverNode = new Rect()
|
||||
.size(width + hoverRectPadding * 2, height + hoverRectPadding * 2)
|
||||
.x(-hoverRectPadding)
|
||||
.y(-hoverRectPadding)
|
||||
this.hoverNode.addClass('smm-hover-node')
|
||||
this.style.hoverNode(this.hoverNode, width, height)
|
||||
this.group.add(this.hoverNode)
|
||||
}
|
||||
// 如果存在自定义节点内容,那么使用自定义节点内容
|
||||
if (this.isUseCustomNodeContent()) {
|
||||
let foreignObject = new ForeignObject()
|
||||
foreignObject.width(width)
|
||||
foreignObject.height(height)
|
||||
foreignObject.add(SVG(this._customNodeContent))
|
||||
foreignObject.add(this._customNodeContent)
|
||||
this.group.add(foreignObject)
|
||||
addHoverNode()
|
||||
return
|
||||
}
|
||||
// 图片节点
|
||||
@@ -365,6 +378,7 @@ class Node {
|
||||
: 0)
|
||||
)
|
||||
this.group.add(textContentNested)
|
||||
addHoverNode()
|
||||
}
|
||||
|
||||
// 给节点绑定事件
|
||||
@@ -380,14 +394,23 @@ class Node {
|
||||
this.active(e)
|
||||
})
|
||||
this.group.on('mousedown', e => {
|
||||
if (this.isRoot && e.which === 3 && !this.mindMap.opt.readonly) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
if (!this.isRoot && e.which !== 2 && !this.mindMap.opt.readonly) {
|
||||
e.stopPropagation()
|
||||
const { readonly, enableCtrlKeyNodeSelection, useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
|
||||
// 只读模式不需要阻止冒泡
|
||||
if (!readonly) {
|
||||
if (this.isRoot) {
|
||||
// 根节点,右键拖拽画布模式下不需要阻止冒泡
|
||||
if (e.which === 3 && !useLeftKeySelectionRightKeyDrag) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
} else {
|
||||
// 非根节点,且按下的是非鼠标中键,需要阻止事件冒泡
|
||||
if (e.which !== 2) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 多选和取消多选
|
||||
if (e.ctrlKey && this.mindMap.opt.enableCtrlKeyNodeSelection) {
|
||||
if (e.ctrlKey && enableCtrlKeyNodeSelection) {
|
||||
this.isMultipleChoice = true
|
||||
let isActive = this.nodeData.data.isActive
|
||||
if (!isActive)
|
||||
@@ -403,7 +426,7 @@ class Node {
|
||||
this.mindMap.emit(
|
||||
'node_active',
|
||||
isActive ? null : this,
|
||||
this.mindMap.renderer.activeNodeList
|
||||
[...this.mindMap.renderer.activeNodeList]
|
||||
)
|
||||
}
|
||||
this.mindMap.emit('node_mousedown', this, e)
|
||||
@@ -435,13 +458,17 @@ class Node {
|
||||
})
|
||||
// 右键菜单事件
|
||||
this.group.on('contextmenu', e => {
|
||||
const { readonly, useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
|
||||
// 按住ctrl键点击鼠标左键不知为何触发的是contextmenu事件
|
||||
if (this.mindMap.opt.readonly || e.ctrlKey) {
|
||||
// || this.isGeneralization
|
||||
if (readonly || e.ctrlKey) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
// 如果是多选节点结束,那么不要触发右键菜单事件
|
||||
if(this.mindMap.select && !useLeftKeySelectionRightKeyDrag && this.mindMap.select.hasSelectRange()) {
|
||||
return
|
||||
}
|
||||
if (this.nodeData.data.isActive) {
|
||||
this.renderer.clearActive()
|
||||
}
|
||||
@@ -463,14 +490,15 @@ class Node {
|
||||
this.renderer.clearActive()
|
||||
this.mindMap.execCommand('SET_NODE_ACTIVE', this, true)
|
||||
this.renderer.addActiveNode(this)
|
||||
this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
|
||||
this.mindMap.emit('node_active', this, [...this.renderer.activeNodeList])
|
||||
}
|
||||
|
||||
// 更新节点
|
||||
update(isLayout = false) {
|
||||
update() {
|
||||
if (!this.group) {
|
||||
return
|
||||
}
|
||||
this.updateNodeActive()
|
||||
let { alwaysShowExpandBtn } = this.mindMap.opt
|
||||
if (alwaysShowExpandBtn) {
|
||||
// 需要移除展开收缩按钮
|
||||
@@ -543,13 +571,11 @@ class Node {
|
||||
return sizeChange
|
||||
}
|
||||
|
||||
// 更新节点形状样式
|
||||
updateNodeShape() {
|
||||
if (!this.shapeNode) return
|
||||
const shape = this.getShape()
|
||||
this.style[shape === CONSTANTS.SHAPE.RECTANGLE ? 'rect' : 'shape'](
|
||||
this.shapeNode
|
||||
)
|
||||
// 更新节点激活状态
|
||||
updateNodeActive() {
|
||||
if (!this.group) return
|
||||
const isActive = this.nodeData.data.isActive
|
||||
this.group[isActive ? 'addClass' : 'removeClass']('active')
|
||||
}
|
||||
|
||||
// 递归渲染
|
||||
@@ -557,9 +583,7 @@ class Node {
|
||||
// 节点
|
||||
// 重新渲染连线
|
||||
this.renderLine()
|
||||
let isLayout = false
|
||||
if (!this.group) {
|
||||
isLayout = true
|
||||
// 创建组
|
||||
this.group = new G()
|
||||
this.group.addClass('smm-node')
|
||||
@@ -569,7 +593,7 @@ class Node {
|
||||
this.bindGroupEvent()
|
||||
this.draw.add(this.group)
|
||||
this.layout()
|
||||
this.update(isLayout)
|
||||
this.update()
|
||||
} else {
|
||||
this.draw.add(this.group)
|
||||
if (this.needLayout) {
|
||||
@@ -770,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
|
||||
@@ -785,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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -803,8 +827,8 @@ class Node {
|
||||
}
|
||||
|
||||
// 获取某个样式
|
||||
getStyle(prop, root, isActive) {
|
||||
let v = this.style.merge(prop, root, isActive)
|
||||
getStyle(prop, root) {
|
||||
let v = this.style.merge(prop, root)
|
||||
return v === undefined ? '' : v
|
||||
}
|
||||
|
||||
@@ -833,7 +857,7 @@ class Node {
|
||||
|
||||
// 获取节点非节点状态的边框大小
|
||||
getBorderWidth() {
|
||||
return this.style.merge('borderWidth', false, false) || 0
|
||||
return this.style.merge('borderWidth', false) || 0
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
|
||||
@@ -62,14 +62,10 @@ export default class Shape {
|
||||
// 创建形状节点
|
||||
createShape() {
|
||||
const shape = this.node.getShape()
|
||||
const borderWidth = this.node.getBorderWidth()
|
||||
let { width, height } = this.node
|
||||
width -= borderWidth
|
||||
height -= borderWidth
|
||||
let node = null
|
||||
// 矩形
|
||||
if (shape === CONSTANTS.SHAPE.RECTANGLE) {
|
||||
node = new Rect().size(width, height)
|
||||
node = this.createRect()
|
||||
} else if (shape === CONSTANTS.SHAPE.DIAMOND) {
|
||||
// 菱形
|
||||
node = this.createDiamond()
|
||||
@@ -98,9 +94,41 @@ export default class Shape {
|
||||
return node
|
||||
}
|
||||
|
||||
// 获取节点减去节点边框宽度、hover节点边框宽度后的尺寸
|
||||
getNodeSize() {
|
||||
const borderWidth = this.node.getBorderWidth()
|
||||
let { width, height } = this.node
|
||||
width -= borderWidth
|
||||
height -= borderWidth
|
||||
return {
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
|
||||
// 创建矩形
|
||||
createRect() {
|
||||
let { width, height } = this.getNodeSize()
|
||||
let borderRadius = this.node.style.merge('borderRadius')
|
||||
return new Path().plot(`
|
||||
M${borderRadius},0
|
||||
L${width - borderRadius},0
|
||||
C${width - borderRadius},0 ${width},${0} ${width},${borderRadius}
|
||||
L${width},${height - borderRadius}
|
||||
C${width},${height - borderRadius} ${width},${height} ${
|
||||
width - borderRadius
|
||||
},${height}
|
||||
L${borderRadius},${height}
|
||||
C${borderRadius},${height} ${0},${height} ${0},${height - borderRadius}
|
||||
L${0},${borderRadius}
|
||||
C${0},${borderRadius} ${0},${0} ${borderRadius},${0}
|
||||
Z
|
||||
`)
|
||||
}
|
||||
|
||||
// 创建菱形
|
||||
createDiamond() {
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
let halfWidth = width / 2
|
||||
let halfHeight = height / 2
|
||||
let topX = halfWidth
|
||||
@@ -123,7 +151,7 @@ export default class Shape {
|
||||
createParallelogram() {
|
||||
let { paddingX } = this.node.getPaddingVale()
|
||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
return new Polygon().plot([
|
||||
[paddingX, 0],
|
||||
[width, 0],
|
||||
@@ -134,7 +162,7 @@ export default class Shape {
|
||||
|
||||
// 创建圆角矩形
|
||||
createRoundedRectangle() {
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
let halfHeight = height / 2
|
||||
return new Path().plot(`
|
||||
M${halfHeight},0
|
||||
@@ -148,7 +176,7 @@ export default class Shape {
|
||||
// 创建八角矩形
|
||||
createOctagonalRectangle() {
|
||||
let w = 5
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
return new Polygon().plot([
|
||||
[0, w],
|
||||
[w, 0],
|
||||
@@ -165,7 +193,7 @@ export default class Shape {
|
||||
createOuterTriangularRectangle() {
|
||||
let { paddingX } = this.node.getPaddingVale()
|
||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
return new Polygon().plot([
|
||||
[paddingX, 0],
|
||||
[width - paddingX, 0],
|
||||
@@ -180,7 +208,7 @@ export default class Shape {
|
||||
createInnerTriangularRectangle() {
|
||||
let { paddingX } = this.node.getPaddingVale()
|
||||
paddingX = paddingX || this.node.shapePadding.paddingX
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
return new Polygon().plot([
|
||||
[0, 0],
|
||||
[width, 0],
|
||||
@@ -193,7 +221,7 @@ export default class Shape {
|
||||
|
||||
// 创建椭圆
|
||||
createEllipse() {
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
let halfWidth = width / 2
|
||||
let halfHeight = height / 2
|
||||
return new Path().plot(`
|
||||
@@ -206,7 +234,7 @@ export default class Shape {
|
||||
|
||||
// 创建圆
|
||||
createCircle() {
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
let halfWidth = width / 2
|
||||
let halfHeight = height / 2
|
||||
return new Path().plot(`
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { tagColorList, nodeDataNoStylePropList } from '../../../constants/constant'
|
||||
import { tagColorList } from '../../../constants/constant'
|
||||
import { checkIsNodeStyleDataKey } from '../../../utils/index'
|
||||
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
const backgroundStyleProps = ['backgroundColor', 'backgroundImage', 'backgroundRepeat', 'backgroundPosition', 'backgroundSize']
|
||||
const backgroundStyleProps = [
|
||||
'backgroundColor',
|
||||
'backgroundImage',
|
||||
'backgroundRepeat',
|
||||
'backgroundPosition',
|
||||
'backgroundSize'
|
||||
]
|
||||
|
||||
// 样式类
|
||||
class Style {
|
||||
@@ -10,12 +18,18 @@ class Style {
|
||||
if (!Style.cacheStyle) {
|
||||
Style.cacheStyle = {}
|
||||
let style = window.getComputedStyle(el)
|
||||
backgroundStyleProps.forEach((prop) => {
|
||||
backgroundStyleProps.forEach(prop => {
|
||||
Style.cacheStyle[prop] = style[prop]
|
||||
})
|
||||
}
|
||||
// 设置新样式
|
||||
let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig
|
||||
let {
|
||||
backgroundColor,
|
||||
backgroundImage,
|
||||
backgroundRepeat,
|
||||
backgroundPosition,
|
||||
backgroundSize
|
||||
} = themeConfig
|
||||
el.style.backgroundColor = backgroundColor
|
||||
if (backgroundImage && backgroundImage !== 'none') {
|
||||
el.style.backgroundImage = `url(${backgroundImage})`
|
||||
@@ -30,7 +44,7 @@ class Style {
|
||||
// 移除背景样式
|
||||
static removeBackgroundStyle(el) {
|
||||
if (!Style.cacheStyle) return
|
||||
backgroundStyleProps.forEach((prop) => {
|
||||
backgroundStyleProps.forEach(prop => {
|
||||
el.style[prop] = Style.cacheStyle[prop]
|
||||
})
|
||||
Style.cacheStyle = null
|
||||
@@ -42,7 +56,7 @@ class Style {
|
||||
}
|
||||
|
||||
// 合并样式
|
||||
merge(prop, root, isActive) {
|
||||
merge(prop, root) {
|
||||
let themeConfig = this.ctx.mindMap.themeConfig
|
||||
// 三级及以下节点
|
||||
let defaultConfig = themeConfig.node
|
||||
@@ -59,17 +73,6 @@ class Style {
|
||||
// 二级节点
|
||||
defaultConfig = themeConfig.second
|
||||
}
|
||||
// 激活状态
|
||||
if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) {
|
||||
if (
|
||||
this.ctx.nodeData.data.activeStyle &&
|
||||
this.ctx.nodeData.data.activeStyle[prop] !== undefined
|
||||
) {
|
||||
return this.ctx.nodeData.data.activeStyle[prop]
|
||||
} else if (defaultConfig.active && defaultConfig.active[prop]) {
|
||||
return defaultConfig.active[prop]
|
||||
}
|
||||
}
|
||||
// 优先使用节点本身的样式
|
||||
return this.getSelfStyle(prop) !== undefined
|
||||
? this.getSelfStyle(prop)
|
||||
@@ -77,8 +80,8 @@ class Style {
|
||||
}
|
||||
|
||||
// 获取某个样式值
|
||||
getStyle(prop, root, isActive) {
|
||||
return this.merge(prop, root, isActive)
|
||||
getStyle(prop, root) {
|
||||
return this.merge(prop, root)
|
||||
}
|
||||
|
||||
// 获取自身自定义样式
|
||||
@@ -142,10 +145,10 @@ class Style {
|
||||
|
||||
// 获取文本样式
|
||||
getTextFontStyle() {
|
||||
return {
|
||||
italic: this.merge('fontStyle') === 'italic',
|
||||
bold: this.merge('fontWeight'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
return {
|
||||
italic: this.merge('fontStyle') === 'italic',
|
||||
bold: this.merge('fontWeight'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontFamily: this.merge('fontFamily')
|
||||
}
|
||||
}
|
||||
@@ -201,25 +204,43 @@ class Style {
|
||||
|
||||
// 展开收起按钮
|
||||
iconBtn(node, node2, fillNode) {
|
||||
let { color, fill } = this.ctx.mindMap.opt.expandBtnStyle || {
|
||||
let { color, fill, fontSize, fontColor } = this.ctx.mindMap.opt
|
||||
.expandBtnStyle || {
|
||||
color: '#808080',
|
||||
fill: '#fff'
|
||||
fill: '#fff',
|
||||
fontSize: 12,
|
||||
strokeColor: '#333333',
|
||||
fontColor: '#333333'
|
||||
}
|
||||
node.fill({ color: color })
|
||||
node2.fill({ color: color })
|
||||
fillNode.fill({ color: fill })
|
||||
if (this.ctx.mindMap.opt.isShowExpandNum) {
|
||||
node.attr({ 'font-size': fontSize, 'font-color': fontColor })
|
||||
}
|
||||
}
|
||||
|
||||
// 是否设置了自定义的样式
|
||||
hasCustomStyle() {
|
||||
let res = false
|
||||
Object.keys(this.ctx.nodeData.data).forEach((item) => {
|
||||
if (!nodeDataNoStylePropList.includes(item)) {
|
||||
Object.keys(this.ctx.nodeData.data).forEach(item => {
|
||||
if (checkIsNodeStyleDataKey(item)) {
|
||||
res = true
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// hover和激活节点
|
||||
hoverNode(node) {
|
||||
const { hoverRectColor } = this.ctx.mindMap.opt
|
||||
node
|
||||
.radius(5)
|
||||
.fill('none')
|
||||
.stroke({
|
||||
color: hoverRectColor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Style.cacheStyle = null
|
||||
|
||||
@@ -39,13 +39,13 @@ function setShape(shape) {
|
||||
}
|
||||
|
||||
// 修改某个样式
|
||||
function setStyle(prop, value, isActive) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value, isActive)
|
||||
function setStyle(prop, value) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value)
|
||||
}
|
||||
|
||||
// 修改多个样式
|
||||
function setStyles(style, isActive) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLES', this, style, isActive)
|
||||
function setStyles(style) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLES', this, style)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -160,12 +160,8 @@ function createTextNode() {
|
||||
return this.createRichTextNode()
|
||||
}
|
||||
let g = new G()
|
||||
let fontSize = this.getStyle('fontSize', false, this.nodeData.data.isActive)
|
||||
let lineHeight = this.getStyle(
|
||||
'lineHeight',
|
||||
false,
|
||||
this.nodeData.data.isActive
|
||||
)
|
||||
let fontSize = this.getStyle('fontSize', false)
|
||||
let lineHeight = this.getStyle('lineHeight', false)
|
||||
// 文本超长自动换行
|
||||
let textStyle = this.style.getTextFontStyle()
|
||||
let textArr = this.nodeData.data.text.split(/\n/gim)
|
||||
|
||||
@@ -6,13 +6,27 @@ function createExpandNodeContent() {
|
||||
if (this._openExpandNode) {
|
||||
return
|
||||
}
|
||||
let { open, close } = this.mindMap.opt.expandBtnIcon || {}
|
||||
// 展开的节点
|
||||
this._openExpandNode = SVG(open || btnsSvg.open).size(
|
||||
this.expandBtnSize,
|
||||
this.expandBtnSize
|
||||
)
|
||||
this._openExpandNode.x(0).y(-this.expandBtnSize / 2)
|
||||
let { close, open } = this.mindMap.opt.expandBtnIcon || {}
|
||||
// 根据配置判断是否显示数量按钮
|
||||
if (this.mindMap.opt.isShowExpandNum) {
|
||||
// 展开的节点
|
||||
this._openExpandNode = SVG()
|
||||
.text()
|
||||
.size(this.expandBtnSize, this.expandBtnSize)
|
||||
// 文本垂直居中
|
||||
this._openExpandNode.attr({
|
||||
'text-anchor': 'middle',
|
||||
'dominant-baseline': 'middle',
|
||||
x: this.expandBtnSize / 2,
|
||||
y: 2
|
||||
})
|
||||
} else {
|
||||
this._openExpandNode = SVG(open || btnsSvg.open).size(
|
||||
this.expandBtnSize,
|
||||
this.expandBtnSize
|
||||
)
|
||||
this._openExpandNode.x(0).y(-this.expandBtnSize / 2)
|
||||
}
|
||||
// 收起的节点
|
||||
this._closeExpandNode = SVG(close || btnsSvg.close).size(
|
||||
this.expandBtnSize,
|
||||
@@ -22,6 +36,7 @@ function createExpandNodeContent() {
|
||||
// 填充节点
|
||||
this._fillExpandNode = new Circle().size(this.expandBtnSize)
|
||||
this._fillExpandNode.x(0).y(-this.expandBtnSize / 2)
|
||||
|
||||
// 设置样式
|
||||
this.style.iconBtn(
|
||||
this._openExpandNode,
|
||||
@@ -29,7 +44,12 @@ function createExpandNodeContent() {
|
||||
this._fillExpandNode
|
||||
)
|
||||
}
|
||||
|
||||
function sumNode(data = []) {
|
||||
return data.reduce(
|
||||
(total, cur) => total + this.sumNode(cur.children || []),
|
||||
data.length
|
||||
)
|
||||
}
|
||||
// 创建或更新展开收缩按钮内容
|
||||
function updateExpandBtnNode() {
|
||||
let { expand } = this.nodeData.data
|
||||
@@ -47,7 +67,26 @@ function updateExpandBtnNode() {
|
||||
node = this._closeExpandNode
|
||||
this._lastExpandBtnType = true
|
||||
}
|
||||
if (this._expandBtn) this._expandBtn.add(this._fillExpandNode).add(node)
|
||||
|
||||
if (this._expandBtn) {
|
||||
// 如果是收起按钮加上边框
|
||||
let { isShowExpandNum, expandBtnStyle, expandBtnNumHandler } = this.mindMap.opt
|
||||
if (isShowExpandNum) {
|
||||
if (!expand) {
|
||||
// 数字按钮添加边框
|
||||
this._fillExpandNode.stroke({
|
||||
color: expandBtnStyle.strokeColor
|
||||
})
|
||||
// 计算子节点数量
|
||||
let count = this.sumNode(this.nodeData.children)
|
||||
count = expandBtnNumHandler(count)
|
||||
node.text(count)
|
||||
} else {
|
||||
this._fillExpandNode.stroke('none')
|
||||
}
|
||||
}
|
||||
this._expandBtn.add(this._fillExpandNode).add(node)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新展开收缩按钮位置
|
||||
@@ -138,5 +177,6 @@ export default {
|
||||
renderExpandBtn,
|
||||
removeExpandBtn,
|
||||
showExpandBtn,
|
||||
hideExpandBtn
|
||||
hideExpandBtn,
|
||||
sumNode
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -46,7 +46,10 @@ class Base {
|
||||
|
||||
// 检查当前来源是否需要重新计算节点大小
|
||||
checkIsNeedResizeSources() {
|
||||
return [CONSTANTS.CHANGE_THEME, CONSTANTS.TRANSFORM_TO_NORMAL_NODE].includes(this.renderer.renderSource)
|
||||
return [
|
||||
CONSTANTS.CHANGE_THEME,
|
||||
CONSTANTS.TRANSFORM_TO_NORMAL_NODE
|
||||
].includes(this.renderer.renderSource)
|
||||
}
|
||||
|
||||
// 层级类型改变
|
||||
@@ -70,7 +73,10 @@ class Base {
|
||||
// 数据上保存了节点引用,那么直接复用节点
|
||||
if (data && data._node && !this.renderer.reRender) {
|
||||
newNode = data._node
|
||||
let isLayerTypeChange = this.checkIsLayerTypeChange(newNode.layerIndex, layerIndex)
|
||||
let isLayerTypeChange = this.checkIsLayerTypeChange(
|
||||
newNode.layerIndex,
|
||||
layerIndex
|
||||
)
|
||||
newNode.reset()
|
||||
newNode.layerIndex = layerIndex
|
||||
this.cacheNode(data._node.uid, newNode)
|
||||
@@ -85,7 +91,10 @@ class Base {
|
||||
newNode = this.lru.get(data.data.uid)
|
||||
// 保存该节点上一次的数据
|
||||
let lastData = JSON.stringify(newNode.nodeData.data)
|
||||
let isLayerTypeChange = this.checkIsLayerTypeChange(newNode.layerIndex, layerIndex)
|
||||
let isLayerTypeChange = this.checkIsLayerTypeChange(
|
||||
newNode.layerIndex,
|
||||
layerIndex
|
||||
)
|
||||
newNode.reset()
|
||||
newNode.nodeData = newNode.handleData(data || {})
|
||||
newNode.layerIndex = layerIndex
|
||||
@@ -139,7 +148,7 @@ class Base {
|
||||
} else if (initRootNodePositionMap[value] !== undefined) {
|
||||
return size * initRootNodePositionMap[value]
|
||||
} else if (/^\d\d*%$/.test(value)) {
|
||||
return Number.parseFloat(value) / 100 * size
|
||||
return (Number.parseFloat(value) / 100) * size
|
||||
} else {
|
||||
return (size - nodeSize) / 2
|
||||
}
|
||||
@@ -148,12 +157,24 @@ class Base {
|
||||
// 定位节点到画布中间
|
||||
setNodeCenter(node) {
|
||||
let { initRootNodePosition } = this.mindMap.opt
|
||||
let { CENTER }= CONSTANTS.INIT_ROOT_NODE_POSITION
|
||||
if (!initRootNodePosition || !Array.isArray(initRootNodePosition) || initRootNodePosition.length < 2) {
|
||||
let { CENTER } = CONSTANTS.INIT_ROOT_NODE_POSITION
|
||||
if (
|
||||
!initRootNodePosition ||
|
||||
!Array.isArray(initRootNodePosition) ||
|
||||
initRootNodePosition.length < 2
|
||||
) {
|
||||
initRootNodePosition = [CENTER, CENTER]
|
||||
}
|
||||
node.left = this.formatPosition(initRootNodePosition[0], this.mindMap.width, node.width)
|
||||
node.top = this.formatPosition(initRootNodePosition[1], this.mindMap.height, node.height)
|
||||
node.left = this.formatPosition(
|
||||
initRootNodePosition[0],
|
||||
this.mindMap.width,
|
||||
node.width
|
||||
)
|
||||
node.top = this.formatPosition(
|
||||
initRootNodePosition[1],
|
||||
this.mindMap.height,
|
||||
node.height
|
||||
)
|
||||
}
|
||||
|
||||
// 更新子节点属性
|
||||
@@ -170,7 +191,7 @@ class Base {
|
||||
// 更新子节点多个属性
|
||||
updateChildrenPro(children, props) {
|
||||
children.forEach(item => {
|
||||
Object.keys(props).forEach((prop) => {
|
||||
Object.keys(props).forEach(prop => {
|
||||
item[prop] += props[prop]
|
||||
})
|
||||
if (item.children && item.children.length && !item.hasCustomPosition()) {
|
||||
@@ -181,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 => {
|
||||
@@ -195,7 +220,7 @@ class Base {
|
||||
}
|
||||
}
|
||||
loop(node, 0)
|
||||
return Math.max(...widthArr)
|
||||
return Math.max(...widthArr) + totalGeneralizationNodeWidth
|
||||
}
|
||||
|
||||
// 二次贝塞尔曲线
|
||||
@@ -216,16 +241,22 @@ class Base {
|
||||
|
||||
// 获取节点的marginX
|
||||
getMarginX(layerIndex) {
|
||||
const { themeConfig, opt } = this.mindMap
|
||||
const { second, node } = themeConfig
|
||||
const hoverRectPadding = opt.hoverRectPadding * 2
|
||||
return layerIndex === 1
|
||||
? this.mindMap.themeConfig.second.marginX
|
||||
: this.mindMap.themeConfig.node.marginX
|
||||
? second.marginX + hoverRectPadding
|
||||
: node.marginX + hoverRectPadding
|
||||
}
|
||||
|
||||
// 获取节点的marginY
|
||||
getMarginY(layerIndex) {
|
||||
const { themeConfig, opt } = this.mindMap
|
||||
const { second, node } = themeConfig
|
||||
const hoverRectPadding = opt.hoverRectPadding * 2
|
||||
return layerIndex === 1
|
||||
? this.mindMap.themeConfig.second.marginY
|
||||
: this.mindMap.themeConfig.node.marginY
|
||||
? second.marginY + hoverRectPadding
|
||||
: node.marginY + hoverRectPadding
|
||||
}
|
||||
|
||||
// 获取节点包括概要在内的宽度
|
||||
|
||||
@@ -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,10 +56,11 @@ class Fishbone extends Base {
|
||||
}
|
||||
// 计算二级节点的top值
|
||||
if (parent._node.isRoot) {
|
||||
let marginY = this.getMarginY(layerIndex)
|
||||
if (this.checkIsTop(newNode)) {
|
||||
newNode.top = parent._node.top - newNode.height
|
||||
newNode.top = parent._node.top - newNode.height - marginY
|
||||
} else {
|
||||
newNode.top = parent._node.top + parent._node.height
|
||||
newNode.top = parent._node.top + parent._node.height + marginY
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,15 +81,16 @@ class Fishbone extends Base {
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex) => {
|
||||
if (node.isRoot) {
|
||||
let topTotalLeft = node.left + node.width + node.height
|
||||
let bottomTotalLeft = node.left + node.width + node.height
|
||||
let marginX = this.getMarginX(layerIndex + 1)
|
||||
let topTotalLeft = node.left + node.width + node.height + marginX
|
||||
let bottomTotalLeft = node.left + node.width + node.height + marginX
|
||||
node.children.forEach(item => {
|
||||
if (this.checkIsTop(item)) {
|
||||
item.left = topTotalLeft
|
||||
topTotalLeft += item.width
|
||||
topTotalLeft += item.width + marginX
|
||||
} else {
|
||||
item.left = bottomTotalLeft + 20
|
||||
bottomTotalLeft += item.width
|
||||
bottomTotalLeft += item.width + marginX
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -154,9 +156,10 @@ class Fishbone extends Base {
|
||||
getNodeAreaHeight(node) {
|
||||
let totalHeight = 0
|
||||
let loop = node => {
|
||||
let marginY = this.getMarginY(node.layerIndex)
|
||||
totalHeight +=
|
||||
node.height +
|
||||
(this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
|
||||
(this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY
|
||||
if (node.children.length) {
|
||||
node.children.forEach(item => {
|
||||
loop(item)
|
||||
@@ -244,8 +247,9 @@ class Fishbone extends Base {
|
||||
maxx = item.left
|
||||
}
|
||||
// 水平线段到二级节点的连线
|
||||
let marginY = this.getMarginY(item.layerIndex)
|
||||
let nodeLineX = item.left
|
||||
let offset = node.height / 2
|
||||
let offset = node.height / 2 + marginY
|
||||
let offsetX = offset / Math.tan(degToRad(this.mindMap.opt.fishboneDeg))
|
||||
let line = this.draw.path()
|
||||
if (this.checkIsTop(item)) {
|
||||
@@ -267,7 +271,7 @@ class Fishbone extends Base {
|
||||
})
|
||||
// 从根节点出发的水平线
|
||||
let nodeHalfTop = node.top + node.height / 2
|
||||
let offset = node.height / 2
|
||||
let offset = node.height / 2 + this.getMarginY(node.layerIndex + 1)
|
||||
let line = this.draw.path()
|
||||
line.plot(
|
||||
`M ${node.left + node.width},${nodeHalfTop} L ${
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -47,30 +47,32 @@ export default {
|
||||
computedLeftTopValue({ layerIndex, node, ctx }) {
|
||||
if (layerIndex >= 1 && node.children) {
|
||||
// 遍历三级及以下节点的子节点
|
||||
let marginY = ctx.getMarginY(layerIndex + 1)
|
||||
let startLeft = node.left + node.width * ctx.childIndent
|
||||
let totalTop =
|
||||
node.top +
|
||||
node.height +
|
||||
(ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY
|
||||
node.children.forEach(item => {
|
||||
item.left = startLeft
|
||||
item.top += totalTop
|
||||
totalTop +=
|
||||
item.height +
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY
|
||||
})
|
||||
}
|
||||
},
|
||||
adjustLeftTopValueBefore({ node, parent, ctx }) {
|
||||
adjustLeftTopValueBefore({ node, parent, ctx, layerIndex }) {
|
||||
// 调整top
|
||||
let len = node.children.length
|
||||
let marginY = ctx.getMarginY(layerIndex + 1)
|
||||
// 调整三级及以下节点的top
|
||||
if (parent && !parent.isRoot && len > 0) {
|
||||
let totalHeight = node.children.reduce((h, item) => {
|
||||
return (
|
||||
h +
|
||||
item.height +
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY
|
||||
)
|
||||
}, 0)
|
||||
ctx.updateBrothersTop(node, totalHeight)
|
||||
@@ -80,7 +82,8 @@ export default {
|
||||
// 将二级节点的子节点移到上方
|
||||
if (parent && parent.isRoot) {
|
||||
// 遍历二级节点的子节点
|
||||
let totalHeight = node.expandBtnSize
|
||||
let marginY = ctx.getMarginY(node.layerIndex + 1)
|
||||
let totalHeight = node.expandBtnSize + marginY
|
||||
node.children.forEach(item => {
|
||||
// 调整top
|
||||
let nodeTotalHeight = ctx.getNodeAreaHeight(item)
|
||||
@@ -134,13 +137,14 @@ export default {
|
||||
}
|
||||
},
|
||||
computedLeftTopValue({ layerIndex, node, ctx }) {
|
||||
let marginY = ctx.getMarginY(layerIndex + 1)
|
||||
if (layerIndex === 1 && node.children) {
|
||||
// 遍历二级节点的子节点
|
||||
let startLeft = node.left + node.width * ctx.childIndent
|
||||
let totalTop =
|
||||
node.top +
|
||||
node.height +
|
||||
(ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY
|
||||
|
||||
node.children.forEach(item => {
|
||||
item.left = startLeft
|
||||
@@ -149,7 +153,7 @@ export default {
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
totalTop +=
|
||||
item.height +
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY
|
||||
})
|
||||
}
|
||||
if (layerIndex > 1 && node.children) {
|
||||
@@ -157,25 +161,26 @@ export default {
|
||||
let startLeft = node.left + node.width * ctx.childIndent
|
||||
let totalTop =
|
||||
node.top -
|
||||
(ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) - marginY
|
||||
node.children.forEach(item => {
|
||||
item.left = startLeft
|
||||
item.top = totalTop - item.height
|
||||
totalTop -=
|
||||
item.height +
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY
|
||||
})
|
||||
}
|
||||
},
|
||||
adjustLeftTopValueBefore({ node, ctx, layerIndex }) {
|
||||
// 调整top
|
||||
let marginY = ctx.getMarginY(layerIndex + 1)
|
||||
let len = node.children.length
|
||||
if (layerIndex > 2 && len > 0) {
|
||||
let totalHeight = node.children.reduce((h, item) => {
|
||||
return (
|
||||
h +
|
||||
item.height +
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0)
|
||||
(ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY
|
||||
)
|
||||
}, 0)
|
||||
ctx.updateBrothersTop(node, -totalHeight)
|
||||
@@ -185,6 +190,7 @@ export default {
|
||||
// 将二级节点的子节点移到上方
|
||||
if (parent && parent.isRoot) {
|
||||
// 遍历二级节点的子节点
|
||||
let marginY = ctx.getMarginY(node.layerIndex + 1)
|
||||
let totalHeight = 0
|
||||
let totalHeight2 = node.expandBtnSize
|
||||
node.children.forEach(item => {
|
||||
@@ -192,11 +198,12 @@ export default {
|
||||
let hasChildren = ctx.getNodeActChildrenLength(item) > 0
|
||||
let nodeTotalHeight = ctx.getNodeAreaHeight(item)
|
||||
let offset =
|
||||
hasChildren > 0
|
||||
hasChildren
|
||||
? nodeTotalHeight -
|
||||
item.height -
|
||||
(hasChildren ? item.expandBtnSize : 0)
|
||||
: 0
|
||||
offset -= (hasChildren ? marginY : 0)
|
||||
let _top = totalHeight + offset
|
||||
let _left = item.left
|
||||
item.top += _top
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -99,12 +99,33 @@ class AssociativeLine {
|
||||
// 创建箭头
|
||||
createMarker() {
|
||||
return this.draw.marker(20, 20, add => {
|
||||
add.ref(2, 5)
|
||||
add.ref(12, 5)
|
||||
add.size(10, 10)
|
||||
add.attr('orient', 'auto-start-reverse')
|
||||
this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z')
|
||||
})
|
||||
}
|
||||
|
||||
// 判断关联线坐标是否变更,有变更则使用变化后的坐标,无则默认坐标
|
||||
updateAllLinesPos(node, toNode, associativeLinePoint) {
|
||||
associativeLinePoint = associativeLinePoint || {}
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
let nodeRange = 0
|
||||
let nodeDir = ''
|
||||
let toNodeRange = 0
|
||||
let toNodeDir = ''
|
||||
if (associativeLinePoint.startPoint) {
|
||||
nodeRange = associativeLinePoint.startPoint.range || 0
|
||||
nodeDir = associativeLinePoint.startPoint.dir || 'right'
|
||||
startPoint = getNodePoint(node, nodeDir, nodeRange)
|
||||
}
|
||||
if (associativeLinePoint.endPoint) {
|
||||
toNodeRange = associativeLinePoint.endPoint.range || 0
|
||||
toNodeDir = associativeLinePoint.endPoint.dir || 'right'
|
||||
endPoint = getNodePoint(toNode, toNodeDir, toNodeRange)
|
||||
}
|
||||
return [startPoint, endPoint]
|
||||
}
|
||||
|
||||
// 渲染所有连线
|
||||
renderAllLines() {
|
||||
@@ -137,10 +158,17 @@ class AssociativeLine {
|
||||
0
|
||||
)
|
||||
nodeToIds.forEach((ids, node) => {
|
||||
ids.forEach(id => {
|
||||
ids.forEach((id, index) => {
|
||||
let toNode = idToNode.get(id)
|
||||
if (!node || !toNode) return
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
const associativeLinePoint = (node.nodeData.data.associativeLinePoint ||
|
||||
[])[index]
|
||||
// 切换结构和布局,都会更新坐标
|
||||
const [startPoint, endPoint] = this.updateAllLinesPos(
|
||||
node,
|
||||
toNode,
|
||||
associativeLinePoint
|
||||
)
|
||||
this.drawLine(startPoint, endPoint, node, toNode)
|
||||
})
|
||||
})
|
||||
@@ -183,11 +211,28 @@ class AssociativeLine {
|
||||
.fill({ color: 'none' })
|
||||
clickPath.plot(pathStr)
|
||||
// 文字
|
||||
let text = this.createText({ path, clickPath, node, toNode, startPoint, endPoint, controlPoints })
|
||||
let text = this.createText({
|
||||
path,
|
||||
clickPath,
|
||||
node,
|
||||
toNode,
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints
|
||||
})
|
||||
// 点击事件
|
||||
clickPath.click(e => {
|
||||
e.stopPropagation()
|
||||
this.setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints })
|
||||
this.setActiveLine({
|
||||
path,
|
||||
clickPath,
|
||||
text,
|
||||
node,
|
||||
toNode,
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints
|
||||
})
|
||||
})
|
||||
// 渲染关联线文字
|
||||
this.renderText(this.getText(node, toNode), path, text)
|
||||
@@ -195,10 +240,17 @@ class AssociativeLine {
|
||||
}
|
||||
|
||||
// 激活某根关联线
|
||||
setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints }) {
|
||||
let {
|
||||
associativeLineActiveColor
|
||||
} = this.mindMap.themeConfig
|
||||
setActiveLine({
|
||||
path,
|
||||
clickPath,
|
||||
text,
|
||||
node,
|
||||
toNode,
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints
|
||||
}) {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
// 如果当前存在激活节点,那么取消激活节点
|
||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||
this.clearActiveNodes()
|
||||
@@ -287,6 +339,22 @@ class AssociativeLine {
|
||||
}
|
||||
}
|
||||
|
||||
// 计算节点偏移位置
|
||||
getNodePos(node) {
|
||||
const { scaleX, scaleY, translateX, translateY } =
|
||||
this.mindMap.draw.transform()
|
||||
const { left, top, width, height } = node
|
||||
let translateLeft = left * scaleX + translateX
|
||||
let translateTop = top * scaleY + translateY
|
||||
return {
|
||||
left,
|
||||
top,
|
||||
translateLeft,
|
||||
translateTop,
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
// 检测当前移动到的目标节点
|
||||
checkOverlapNode(x, y) {
|
||||
this.overlapNode = null
|
||||
@@ -294,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
|
||||
@@ -311,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)
|
||||
@@ -336,6 +404,11 @@ class AssociativeLine {
|
||||
}
|
||||
// 将目标节点id保存起来
|
||||
let list = fromNode.nodeData.data.associativeLineTargets || []
|
||||
// 连线节点是否存在相同的id,存在则阻止添加关联线
|
||||
const sameLine = list.some(item => item === id)
|
||||
if (sameLine) {
|
||||
return
|
||||
}
|
||||
list.push(id)
|
||||
// 保存控制点
|
||||
let [startPoint, endPoint] = computeNodePoints(fromNode, toNode)
|
||||
@@ -358,9 +431,13 @@ class AssociativeLine {
|
||||
y: controlPoints[1].y - endPoint.y
|
||||
}
|
||||
]
|
||||
let associativeLinePoint = fromNode.nodeData.data.associativeLinePoint || []
|
||||
// 记录关联的起始|结束坐标
|
||||
associativeLinePoint[list.length - 1] = { startPoint, endPoint }
|
||||
this.mindMap.execCommand('SET_NODE_DATA', fromNode, {
|
||||
associativeLineTargets: list,
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
associativeLineTargetControlOffsets: offsetList,
|
||||
associativeLinePoint
|
||||
})
|
||||
}
|
||||
|
||||
@@ -369,13 +446,18 @@ class AssociativeLine {
|
||||
if (!this.activeLine) return
|
||||
let [, , , node, toNode] = this.activeLine
|
||||
this.removeControls()
|
||||
let { associativeLineTargets, associativeLineTargetControlOffsets, associativeLineText } =
|
||||
node.nodeData.data
|
||||
let {
|
||||
associativeLineTargets,
|
||||
associativeLinePoint,
|
||||
associativeLineTargetControlOffsets,
|
||||
associativeLineText
|
||||
} = node.nodeData.data
|
||||
associativeLinePoint = associativeLinePoint || []
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
// 更新关联线文本数据
|
||||
let newAssociativeLineText = {}
|
||||
if (associativeLineText) {
|
||||
Object.keys(associativeLineText).forEach((item) => {
|
||||
Object.keys(associativeLineText).forEach(item => {
|
||||
if (item !== toNode.nodeData.data.id) {
|
||||
newAssociativeLineText[item] = associativeLineText[item]
|
||||
}
|
||||
@@ -386,6 +468,10 @@ class AssociativeLine {
|
||||
associativeLineTargets: associativeLineTargets.filter((_, index) => {
|
||||
return index !== targetIndex
|
||||
}),
|
||||
// 连接线坐标
|
||||
associativeLinePoint: associativeLinePoint.filter((_, index) => {
|
||||
return index !== targetIndex
|
||||
}),
|
||||
// 偏移量
|
||||
associativeLineTargetControlOffsets: associativeLineTargetControlOffsets
|
||||
? associativeLineTargetControlOffsets.filter((_, index) => {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas'
|
||||
import { transformToMarkdown } from '../parse/toMarkdown'
|
||||
import { a4Size } from '../constants/constant'
|
||||
|
||||
// 导出插件
|
||||
class Export {
|
||||
@@ -57,45 +58,42 @@ class Export {
|
||||
}
|
||||
|
||||
// svg转png
|
||||
svgToPng(svgSrc, transparent, rotateWhenWidthLongerThenHeight = false) {
|
||||
svgToPng(svgSrc, transparent, checkRotate = () => { return false }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
// 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
|
||||
img.setAttribute('crossOrigin', 'anonymous')
|
||||
img.onload = async () => {
|
||||
try {
|
||||
let canvas = document.createElement('canvas')
|
||||
const canvas = document.createElement('canvas')
|
||||
const dpr = Math.max(window.devicePixelRatio, this.mindMap.opt.minExportImgCanvasScale)
|
||||
const imgWidth = img.width
|
||||
const imgHeight = img.height
|
||||
// 如果宽比高长,那么旋转90度
|
||||
let needRotate =
|
||||
rotateWhenWidthLongerThenHeight && img.width / img.height > 1
|
||||
const needRotate = checkRotate(imgWidth, imgHeight)
|
||||
if (needRotate) {
|
||||
canvas.width = img.height
|
||||
canvas.height = img.width
|
||||
canvas.width = imgHeight * dpr
|
||||
canvas.height = imgWidth * dpr
|
||||
canvas.style.width = imgHeight + 'px'
|
||||
canvas.style.height = imgWidth + 'px'
|
||||
} else {
|
||||
canvas.width = img.width
|
||||
canvas.height = img.height
|
||||
canvas.width = imgWidth * dpr
|
||||
canvas.height = imgHeight * dpr
|
||||
canvas.style.width = imgWidth + 'px'
|
||||
canvas.style.height = imgHeight + 'px'
|
||||
}
|
||||
let ctx = canvas.getContext('2d')
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.scale(dpr, dpr)
|
||||
if (needRotate) {
|
||||
ctx.rotate(0.5 * Math.PI)
|
||||
ctx.translate(0, -img.height)
|
||||
ctx.translate(0, -imgHeight)
|
||||
}
|
||||
// 绘制背景
|
||||
if (!transparent) {
|
||||
await this.drawBackgroundToCanvas(ctx, img.width, img.height)
|
||||
await this.drawBackgroundToCanvas(ctx, imgWidth, imgHeight)
|
||||
}
|
||||
// 图片绘制到canvas里
|
||||
ctx.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
img.width,
|
||||
img.height,
|
||||
0,
|
||||
0,
|
||||
img.width,
|
||||
img.height
|
||||
)
|
||||
ctx.drawImage(img, 0, 0, imgWidth, imgHeight)
|
||||
resolve(canvas.toDataURL())
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
@@ -179,7 +177,7 @@ class Export {
|
||||
* 方法1.把svg的图片都转化成data:url格式,再转换
|
||||
* 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
|
||||
*/
|
||||
async png(name, transparent = false, rotateWhenWidthLongerThenHeight) {
|
||||
async png(name, transparent = false, checkRotate) {
|
||||
let { node, str } = await this.getSvgData()
|
||||
str = removeHTMLEntities(str)
|
||||
// 如果开启了富文本,则使用htmltocanvas转换为图片
|
||||
@@ -195,7 +193,7 @@ class Export {
|
||||
// let imgDataUrl = await this.svgToPng(
|
||||
// res,
|
||||
// transparent,
|
||||
// rotateWhenWidthLongerThenHeight
|
||||
// checkRotate
|
||||
// )
|
||||
// return imgDataUrl
|
||||
}
|
||||
@@ -209,7 +207,7 @@ class Export {
|
||||
let res = await this.svgToPng(
|
||||
svgUrl,
|
||||
transparent,
|
||||
rotateWhenWidthLongerThenHeight
|
||||
checkRotate
|
||||
)
|
||||
return res
|
||||
}
|
||||
@@ -219,7 +217,10 @@ class Export {
|
||||
if (!this.mindMap.doExportPDF) {
|
||||
throw new Error('请注册ExportPDF插件')
|
||||
}
|
||||
let img = await this.png('', false, true)
|
||||
let img = await this.png('', false, (width, height) => {
|
||||
if (width <= a4Size.width && height && a4Size.height) return false
|
||||
return (width / height) > 1
|
||||
})
|
||||
this.mindMap.doExportPDF.pdf(name, img, useMultiPageExport)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import JsPDF from 'jspdf'
|
||||
import { a4Size } from '../constants/constant'
|
||||
|
||||
// 导出PDF插件,需要通过Export插件使用
|
||||
class ExportPDF {
|
||||
@@ -19,29 +20,27 @@ class ExportPDF {
|
||||
// 单页导出
|
||||
onePageExport(name, img) {
|
||||
let pdf = new JsPDF('', 'pt', 'a4')
|
||||
let a4Width = 595
|
||||
let a4Height = 841
|
||||
let a4Ratio = a4Width / a4Height
|
||||
let a4Ratio = a4Size.width / a4Size.height
|
||||
let image = new Image()
|
||||
image.onload = () => {
|
||||
let imageWidth = image.width
|
||||
let imageHeight = image.height
|
||||
let imageRatio = imageWidth / imageHeight
|
||||
let w, h
|
||||
if (imageWidth <= a4Width && imageHeight <= a4Height) {
|
||||
if (imageWidth <= a4Size.width && imageHeight <= a4Size.height) {
|
||||
// 使用图片原始宽高
|
||||
w = imageWidth
|
||||
h = imageHeight
|
||||
} else if (a4Ratio > imageRatio) {
|
||||
// 以a4Height为高度,缩放图片宽度
|
||||
w = imageRatio * a4Height
|
||||
h = a4Height
|
||||
w = imageRatio * a4Size.height
|
||||
h = a4Size.height
|
||||
} else {
|
||||
// 以a4Width为宽度,缩放图片高度
|
||||
w = a4Width
|
||||
h = a4Width / imageRatio
|
||||
w = a4Size.width
|
||||
h = a4Size.width / imageRatio
|
||||
}
|
||||
pdf.addImage(img, 'PNG', (a4Width - w) / 2, (a4Height - h) / 2, w, h)
|
||||
pdf.addImage(img, 'PNG', (a4Size.width - w) / 2, (a4Size.height - h) / 2, w, h)
|
||||
pdf.save(name)
|
||||
}
|
||||
image.src = img
|
||||
@@ -50,20 +49,18 @@ class ExportPDF {
|
||||
// 多页导出
|
||||
multiPageExport(name, img) {
|
||||
let image = new Image()
|
||||
const a4Width = 592.28
|
||||
const a4Height = 841.89
|
||||
image.onload = () => {
|
||||
let imageWidth = image.width
|
||||
let imageHeight = image.height
|
||||
// 一页pdf显示高度
|
||||
let pageHeight = (imageWidth / a4Width) * a4Height
|
||||
let pageHeight = (imageWidth / a4Size.width) * a4Size.height
|
||||
// 未生成pdf的高度
|
||||
let leftHeight = imageHeight
|
||||
// 偏移
|
||||
let position = 0
|
||||
// a4纸的尺寸[595.28,841.89],图片在pdf中图片的宽高
|
||||
let imgWidth = a4Width
|
||||
let imgHeight = (a4Width / imageWidth) * imageHeight
|
||||
let imgWidth = a4Size.width
|
||||
let imgHeight = (a4Size.width / imageWidth) * imageHeight
|
||||
let pdf = new JsPDF('', 'pt', 'a4')
|
||||
// 有两个高度需要区分,一个是图片的实际高度,和生成pdf的页面高度(841.89)
|
||||
// 当内容未超过pdf一页显示的范围,无需分页
|
||||
@@ -71,8 +68,8 @@ class ExportPDF {
|
||||
pdf.addImage(
|
||||
img,
|
||||
'PNG',
|
||||
(a4Width - imgWidth) / 2,
|
||||
(a4Height - imgHeight) / 2,
|
||||
(a4Size.width - imgWidth) / 2,
|
||||
(a4Size.height - imgHeight) / 2,
|
||||
imgWidth,
|
||||
imgHeight
|
||||
)
|
||||
@@ -81,7 +78,7 @@ class ExportPDF {
|
||||
while (leftHeight > 0) {
|
||||
pdf.addImage(img, 'PNG', 0, position, imgWidth, imgHeight)
|
||||
leftHeight -= pageHeight
|
||||
position -= a4Height
|
||||
position -= a4Size.height
|
||||
// 避免添加空白页
|
||||
if (leftHeight > 0) {
|
||||
pdf.addPage()
|
||||
|
||||
@@ -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
|
||||
@@ -263,6 +263,11 @@ class NodeImgAdjust {
|
||||
beforePluginRemove() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
NodeImgAdjust.instanceName = 'nodeImgAdjust'
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
})
|
||||
@@ -69,6 +69,11 @@ class Painter {
|
||||
beforePluginRemove() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
Painter.instanceName = 'painter'
|
||||
|
||||
@@ -152,7 +152,7 @@ class RichText {
|
||||
}
|
||||
|
||||
// 显示文本编辑控件
|
||||
showEditText(node, rect, isInserting) {
|
||||
showEditText(node, rect, isInserting, isFromKeyDown) {
|
||||
if (this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
@@ -160,7 +160,8 @@ class RichText {
|
||||
richTextEditFakeInPlace,
|
||||
customInnerElsAppendTo,
|
||||
nodeTextEditZIndex,
|
||||
textAutoWrapWidth
|
||||
textAutoWrapWidth,
|
||||
selectTextOnEnterEditText
|
||||
} = this.mindMap.opt
|
||||
this.node = node
|
||||
this.isInserting = isInserting
|
||||
@@ -246,8 +247,9 @@ class RichText {
|
||||
this.initQuillEditor()
|
||||
document.querySelector('.ql-editor').style.minHeight = originHeight + 'px'
|
||||
this.showTextEdit = true
|
||||
// 如果是刚创建的节点,那么默认全选,否则普通激活不全选
|
||||
this.focus(isInserting ? 0 : null)
|
||||
// 如果是刚创建的节点,那么默认全选,否则普通激活不全选,除非selectTextOnEnterEditText配置为true
|
||||
// 在selectTextOnEnterEditText时,如果是在keydown事件进入的节点编辑,也不需要全选
|
||||
this.focus(isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null)
|
||||
if (!node.nodeData.data.richText) {
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
this.setTextStyleIfNotRichText(node)
|
||||
@@ -631,6 +633,11 @@ class RichText {
|
||||
this.transformAllNodesToNormalNode()
|
||||
document.head.removeChild(this.styleEl)
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
document.head.removeChild(this.styleEl)
|
||||
}
|
||||
}
|
||||
|
||||
RichText.instanceName = 'richText'
|
||||
|
||||
258
simple-mind-map/src/plugins/Scrollbar.js
Normal file
@@ -0,0 +1,258 @@
|
||||
import { throttle } from '../utils/index'
|
||||
import { CONSTANTS } from '../constants/constant'
|
||||
|
||||
// 滚动条插件
|
||||
class Scrollbar {
|
||||
// 构造函数
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.scrollbarWrapSize = {
|
||||
width: 0, // 水平滚动条的容器宽度
|
||||
height: 0 // 垂直滚动条的容器高度
|
||||
}
|
||||
// 思维导图实际高度
|
||||
this.chartHeight = 0
|
||||
this.chartWidth = 0
|
||||
this.reset()
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 复位数据
|
||||
reset() {
|
||||
// 当前拖拽的滚动条类型
|
||||
this.currentScrollType = ''
|
||||
this.isMousedown = false
|
||||
this.mousedownPos = {
|
||||
x: 0,
|
||||
y: 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)
|
||||
}
|
||||
|
||||
// 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度
|
||||
setScrollBarWrapSize(width, height) {
|
||||
this.scrollbarWrapSize.width = width
|
||||
this.scrollbarWrapSize.height = height
|
||||
}
|
||||
|
||||
// 计算滚动条大小和位置
|
||||
calculationScrollbar() {
|
||||
const rect = this.mindMap.draw.rbox()
|
||||
// 减去画布距离浏览器窗口左上角的距离
|
||||
const elRect = this.mindMap.elRect
|
||||
rect.x -= elRect.left
|
||||
rect.y -= elRect.top
|
||||
|
||||
// 垂直滚动条
|
||||
const canvasHeight = this.mindMap.height // 画布高度
|
||||
const paddingY = canvasHeight / 2 // 首尾允许超出的距离,默认为高度的一半
|
||||
const chartHeight = rect.height + paddingY * 2 // 思维导图高度
|
||||
this.chartHeight = chartHeight
|
||||
const chartTop = rect.y - paddingY // 思维导图顶部距画布顶部的距离
|
||||
const height = Math.min((canvasHeight / chartHeight) * 100, 100) // 滚动条高度 = 画布高度 / 思维导图高度
|
||||
let top = (-chartTop / chartHeight) * 100 // 滚动条距离 = 思维导图顶部距画布顶部的距离 / 思维导图高度
|
||||
// 判断是否到达边界
|
||||
if (top < 0) {
|
||||
top = 0
|
||||
}
|
||||
if (top > 100 - height) {
|
||||
top = 100 - height
|
||||
}
|
||||
|
||||
// 水平滚动条
|
||||
const canvasWidth = this.mindMap.width
|
||||
const paddingX = canvasWidth / 2
|
||||
const chartWidth = rect.width + paddingX * 2
|
||||
this.chartWidth = chartWidth
|
||||
const chartLeft = rect.x - paddingX
|
||||
const width = Math.min((canvasWidth / chartWidth) * 100, 100)
|
||||
let left = (-chartLeft / chartWidth) * 100
|
||||
if (left < 0) {
|
||||
left = 0
|
||||
}
|
||||
if (left > 100 - width) {
|
||||
left = 100 - width
|
||||
}
|
||||
|
||||
const res = {
|
||||
// 垂直滚动条
|
||||
vertical: {
|
||||
top,
|
||||
height
|
||||
},
|
||||
// 水平滚动条
|
||||
horizontal: {
|
||||
left,
|
||||
width
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// 滚动条鼠标按下事件处理函数
|
||||
onMousedown(e, type) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
this.currentScrollType = type
|
||||
this.isMousedown = true
|
||||
this.mousedownPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
}
|
||||
// 保存滚动条当前的位置
|
||||
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
|
||||
}
|
||||
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 + 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()
|
||||
}
|
||||
}
|
||||
|
||||
Scrollbar.instanceName = 'scrollbar'
|
||||
|
||||
export default Scrollbar
|
||||
@@ -11,6 +11,8 @@ class Select {
|
||||
this.mouseDownY = 0
|
||||
this.mouseMoveX = 0
|
||||
this.mouseMoveY = 0
|
||||
this.isSelecting = false
|
||||
this.cacheActiveList = []
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -30,6 +32,7 @@ class Select {
|
||||
}
|
||||
e.preventDefault()
|
||||
this.isMousedown = true
|
||||
this.cacheActiveList = [...this.mindMap.renderer.activeNodeList]
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
@@ -51,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) {
|
||||
@@ -61,68 +96,92 @@ class Select {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
this.mindMap.emit(
|
||||
'node_active',
|
||||
null,
|
||||
this.mindMap.renderer.activeNodeList
|
||||
)
|
||||
this.checkTriggerNodeActiveEvent()
|
||||
clearTimeout(this.autoMoveTimer)
|
||||
this.isMousedown = false
|
||||
this.cacheActiveList = []
|
||||
if (this.rect) this.rect.remove()
|
||||
this.rect = null
|
||||
setTimeout(() => {
|
||||
this.isSelecting = false
|
||||
}, 0)
|
||||
})
|
||||
}
|
||||
|
||||
// 如果激活节点改变了,那么触发事件
|
||||
checkTriggerNodeActiveEvent() {
|
||||
let isNumChange =
|
||||
this.cacheActiveList.length !==
|
||||
this.mindMap.renderer.activeNodeList.length
|
||||
let isNodeChange = false
|
||||
if (!isNumChange) {
|
||||
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
|
||||
})
|
||||
) {
|
||||
isNodeChange = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isNumChange || isNodeChange) {
|
||||
this.mindMap.emit('node_active', null, [
|
||||
...this.mindMap.renderer.activeNodeList
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
onMove(x, y) {
|
||||
// 绘制矩形
|
||||
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
|
||||
@@ -172,6 +231,11 @@ class Select {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 是否存在选区
|
||||
hasSelectRange() {
|
||||
return this.isSelecting
|
||||
}
|
||||
}
|
||||
|
||||
Select.instanceName = 'select'
|
||||
|
||||
@@ -148,6 +148,11 @@ class TouchEvent {
|
||||
beforePluginRemove() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
TouchEvent.instanceName = 'touchEvent'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getAssociativeLineTargetIndex,
|
||||
joinCubicBezierPath,
|
||||
computeNodePoints,
|
||||
getNodePoint,
|
||||
getDefaultControlPointOffsets
|
||||
} from './associativeLineUtils'
|
||||
|
||||
@@ -61,15 +61,22 @@ function onControlPointMousemove(e) {
|
||||
}
|
||||
// 更新当前拖拽的控制点的位置
|
||||
this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
|
||||
let [path, clickPath, text, node, toNode] = this.activeLine
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
let [, , , node, toNode] = this.activeLine
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
let { associativeLinePoint, associativeLineTargetControlOffsets } =
|
||||
node.nodeData.data
|
||||
associativeLinePoint = associativeLinePoint || []
|
||||
const nodePos = this.getNodePos(node)
|
||||
const toNodePos = this.getNodePos(toNode)
|
||||
let [startPoint, endPoint] = this.updateAllLinesPos(
|
||||
node,
|
||||
toNode,
|
||||
associativeLinePoint[targetIndex]
|
||||
)
|
||||
this.controlPointMousemoveState.startPoint = startPoint
|
||||
this.controlPointMousemoveState.endPoint = endPoint
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
this.controlPointMousemoveState.targetIndex = targetIndex
|
||||
let offsets = []
|
||||
let associativeLineTargetControlOffsets =
|
||||
node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsets = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||
@@ -78,8 +85,14 @@ function onControlPointMousemove(e) {
|
||||
}
|
||||
let point1 = null
|
||||
let point2 = null
|
||||
const { x: clientX, y: clientY } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
const _e = {
|
||||
clientX,
|
||||
clientY
|
||||
}
|
||||
// 拖拽的是控制点1
|
||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||
startPoint = getNodePoint(nodePos, '', 0, _e)
|
||||
point1 = {
|
||||
x,
|
||||
y
|
||||
@@ -88,10 +101,15 @@ function onControlPointMousemove(e) {
|
||||
x: endPoint.x + offsets[1].x,
|
||||
y: endPoint.y + offsets[1].y
|
||||
}
|
||||
// 更新控制点1的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
if (startPoint) {
|
||||
// 保存更新后的坐标
|
||||
this.controlPointMousemoveState.startPoint = startPoint
|
||||
// 更新控制点1的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
}
|
||||
} else {
|
||||
// 拖拽的是控制点2
|
||||
endPoint = getNodePoint(toNodePos, '', 0, _e)
|
||||
point1 = {
|
||||
x: startPoint.x + offsets[0].x,
|
||||
y: startPoint.y + offsets[0].y
|
||||
@@ -100,18 +118,39 @@ function onControlPointMousemove(e) {
|
||||
x,
|
||||
y
|
||||
}
|
||||
// 更新控制点2的连线
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
if (endPoint) {
|
||||
// 保存更新后结束节点的坐标
|
||||
this.controlPointMousemoveState.endPoint = endPoint
|
||||
// 更新控制点2的连线
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
}
|
||||
}
|
||||
this.updataAassociativeLine(
|
||||
startPoint,
|
||||
endPoint,
|
||||
point1,
|
||||
point2,
|
||||
this.activeLine
|
||||
)
|
||||
}
|
||||
|
||||
function updataAassociativeLine(
|
||||
startPoint,
|
||||
endPoint,
|
||||
point1,
|
||||
point2,
|
||||
activeLine
|
||||
) {
|
||||
const [path, clickPath, text] = activeLine
|
||||
// 更新关联线
|
||||
let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||
const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||
path.plot(pathStr)
|
||||
clickPath.plot(pathStr)
|
||||
this.updateTextPos(path, text)
|
||||
this.updateTextEditBoxPos(text)
|
||||
}
|
||||
|
||||
// 控制点的鼠标移动事件
|
||||
// 控制点的鼠标松开事件
|
||||
function onControlPointMouseup(e) {
|
||||
if (!this.isControlPointMousedown) return
|
||||
e.stopPropagation()
|
||||
@@ -120,8 +159,15 @@ function onControlPointMouseup(e) {
|
||||
this.controlPointMousemoveState
|
||||
let [, , , node] = this.activeLine
|
||||
let offsetList = []
|
||||
let associativeLineTargetControlOffsets =
|
||||
node.nodeData.data.associativeLineTargetControlOffsets
|
||||
let { associativeLinePoint, associativeLineTargetControlOffsets } =
|
||||
node.nodeData.data
|
||||
if (!associativeLinePoint) {
|
||||
associativeLinePoint = []
|
||||
}
|
||||
associativeLinePoint[targetIndex] = associativeLinePoint[targetIndex] || {
|
||||
startPoint,
|
||||
endPoint
|
||||
}
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsetList[targetIndex] = getDefaultControlPointOffsets(
|
||||
@@ -140,6 +186,7 @@ function onControlPointMouseup(e) {
|
||||
y: pos.y - startPoint.y
|
||||
}
|
||||
offset2 = offsetList[targetIndex][1]
|
||||
associativeLinePoint[targetIndex].startPoint = startPoint
|
||||
} else {
|
||||
// 更新控制点2数据
|
||||
offset1 = offsetList[targetIndex][0]
|
||||
@@ -147,10 +194,12 @@ function onControlPointMouseup(e) {
|
||||
x: pos.x - endPoint.x,
|
||||
y: pos.y - endPoint.y
|
||||
}
|
||||
associativeLinePoint[targetIndex].endPoint = endPoint
|
||||
}
|
||||
offsetList[targetIndex] = [offset1, offset2]
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
associativeLineTargetControlOffsets: offsetList,
|
||||
associativeLinePoint
|
||||
})
|
||||
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
||||
setTimeout(() => {
|
||||
@@ -237,5 +286,6 @@ export default {
|
||||
renderControls,
|
||||
removeControls,
|
||||
hideControls,
|
||||
showControls
|
||||
showControls,
|
||||
updataAassociativeLine
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function showEditTextBox(g) {
|
||||
this.mindMap.keyCommand.addShortcut('Enter', () => {
|
||||
this.hideEditTextBox()
|
||||
})
|
||||
|
||||
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||
@@ -62,7 +62,8 @@ function showEditTextBox(g) {
|
||||
).split(/\n/gim)
|
||||
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
|
||||
this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px'
|
||||
this.textEditNode.style.lineHeight = textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
|
||||
this.textEditNode.style.lineHeight =
|
||||
textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
|
||||
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
this.textEditNode.style.display = 'block'
|
||||
@@ -78,10 +79,12 @@ function onScale() {
|
||||
// 更新文本编辑框位置
|
||||
function updateTextEditBoxPos(g) {
|
||||
let rect = g.node.getBoundingClientRect()
|
||||
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
||||
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
if (this.textEditNode) {
|
||||
this.textEditNode.style.minWidth = `${rect.width + 10}px`
|
||||
this.textEditNode.style.minHeight = `${rect.height + 6}px`
|
||||
this.textEditNode.style.left = `${rect.left}px`
|
||||
this.textEditNode.style.top = `${rect.top}px`
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏文本编辑框
|
||||
@@ -116,8 +119,10 @@ function getText(node, toNode) {
|
||||
// 渲染关联线文字
|
||||
function renderText(str, path, text) {
|
||||
if (!str) return
|
||||
let { associativeLineTextFontSize, associativeLineTextLineHeight } =
|
||||
this.mindMap.themeConfig
|
||||
let {
|
||||
associativeLineTextFontSize,
|
||||
associativeLineTextLineHeight
|
||||
} = this.mindMap.themeConfig
|
||||
text.clear()
|
||||
let textArr = str.split(/\n/gim)
|
||||
textArr.forEach((item, index) => {
|
||||
|
||||
@@ -54,28 +54,136 @@ export const cubicBezierPath = (x1, y1, x2, y2) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const calcPoint = (node, e) => {
|
||||
const { left, top, translateLeft, translateTop, width, height } = node
|
||||
const clientX = e.clientX
|
||||
const clientY = e.clientY
|
||||
// 中心点的坐标
|
||||
const centerX = translateLeft + width / 2
|
||||
const centerY = translateTop + height / 2
|
||||
const translateCenterX = left + width / 2
|
||||
const translateCenterY = top + height / 2
|
||||
const theta = Math.atan(height / width)
|
||||
// 矩形左上角坐标
|
||||
const deltaX = clientX - centerX
|
||||
const deltaY = centerY - clientY
|
||||
// 方向值
|
||||
const direction = Math.atan2(deltaY, deltaX)
|
||||
// 默认坐标
|
||||
let x = left + width
|
||||
let y = top + height
|
||||
if (direction < theta && direction >= -theta) {
|
||||
// 右边
|
||||
// 正切值 = 对边/邻边,对边 = 正切值*邻边
|
||||
const range = direction * (width / 2)
|
||||
if (direction < theta && direction >= 0) {
|
||||
// 中心点上边
|
||||
y = translateCenterY - range
|
||||
} else if (direction >= -theta && direction < 0) {
|
||||
// 中心点下方
|
||||
y = translateCenterY - range
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'right',
|
||||
range
|
||||
}
|
||||
} else if (direction >= theta && direction < Math.PI - theta) {
|
||||
// 上边
|
||||
y = top
|
||||
let range = 0
|
||||
if (direction < Math.PI / 2 - theta && direction >= theta) {
|
||||
// 正切值 = 对边/邻边,邻边 = 对边/正切值
|
||||
const side = height / 2 / direction
|
||||
range = -side
|
||||
// 中心点右侧
|
||||
x = translateCenterX + side
|
||||
} else if (
|
||||
direction >= Math.PI / 2 - theta &&
|
||||
direction < Math.PI - theta
|
||||
) {
|
||||
// 中心点左侧
|
||||
const tanValue = (centerX - clientX) / (centerY - clientY)
|
||||
const side = (height / 2) * tanValue
|
||||
range = side
|
||||
x = translateCenterX - side
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'top',
|
||||
range
|
||||
}
|
||||
} else if (direction < -theta && direction >= theta - Math.PI) {
|
||||
// 下边
|
||||
let range = 0
|
||||
if (direction >= theta - Math.PI / 2 && direction < -theta) {
|
||||
// 中心点右侧
|
||||
// 正切值 = 对边/邻边,邻边 = 对边/正切值
|
||||
const side = height / 2 / direction
|
||||
range = side
|
||||
x = translateCenterX - side
|
||||
} else if (
|
||||
direction < theta - Math.PI / 2 &&
|
||||
direction >= theta - Math.PI
|
||||
) {
|
||||
// 中心点左侧
|
||||
const tanValue = (centerX - clientX) / (centerY - clientY)
|
||||
const side = (height / 2) * tanValue
|
||||
range = -side
|
||||
x = translateCenterX + side
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'bottom',
|
||||
range
|
||||
}
|
||||
}
|
||||
// 左边
|
||||
x = left
|
||||
const tanValue = (centerY - clientY) / (centerX - clientX)
|
||||
const range = tanValue * (width / 2)
|
||||
if (direction >= -Math.PI && direction < theta - Math.PI) {
|
||||
// 中心点右侧
|
||||
y = translateCenterY - range
|
||||
} else if (direction < Math.PI && direction >= Math.PI - theta) {
|
||||
// 中心点左侧
|
||||
y = translateCenterY - range
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'left',
|
||||
range
|
||||
}
|
||||
}
|
||||
// 获取节点的连接点
|
||||
export const getNodePoint = (node, dir = 'right') => {
|
||||
export const getNodePoint = (node, dir = 'right', range = 0, e = null) => {
|
||||
let { left, top, width, height } = node
|
||||
if (e) {
|
||||
return calcPoint(node, e)
|
||||
}
|
||||
switch (dir) {
|
||||
case 'left':
|
||||
return {
|
||||
x: left,
|
||||
y: top + height / 2
|
||||
y: top + height / 2 - range
|
||||
}
|
||||
case 'right':
|
||||
return {
|
||||
x: left + width,
|
||||
y: top + height / 2
|
||||
y: top + height / 2 - range
|
||||
}
|
||||
case 'top':
|
||||
return {
|
||||
x: left + width / 2,
|
||||
x: left + width / 2 - range,
|
||||
y: top
|
||||
}
|
||||
case 'bottom':
|
||||
return {
|
||||
x: left + width / 2,
|
||||
x: left + width / 2 - range,
|
||||
y: top + height
|
||||
}
|
||||
default:
|
||||
@@ -111,8 +219,8 @@ export const computeNodePoints = (fromNode, toNode) => {
|
||||
toDir = 'bottom'
|
||||
} else if (offsetY > 0 && -offsetY < offsetX && offsetY > offsetX) {
|
||||
// down
|
||||
fromDir = 'bottom'
|
||||
toDir = 'top'
|
||||
fromDir = 'right'
|
||||
toDir = 'right'
|
||||
}
|
||||
return [getNodePoint(fromNode, fromDir), getNodePoint(toNode, toDir)]
|
||||
}
|
||||
@@ -179,4 +287,4 @@ export const getDefaultControlPointOffsets = (startPoint, endPoint) => {
|
||||
y: controlPoints[1].y - endPoint.y
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '#e68112',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: '#8c5416',
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#e68112'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#8c5416',
|
||||
active: {
|
||||
borderColor: '#b0bc47'
|
||||
}
|
||||
color: '#8c5416'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: '#ffd683',
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 2,
|
||||
color: '#8c5416',
|
||||
active: {
|
||||
borderColor: '#e68112'
|
||||
}
|
||||
color: '#8c5416'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '#94c143',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#749336',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: '#749336',
|
||||
borderColor: '#aec668',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#749336'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#749336',
|
||||
active: {
|
||||
borderColor: '#749336'
|
||||
}
|
||||
color: '#749336'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: '#cee498',
|
||||
borderColor: '#aec668',
|
||||
borderWidth: 2,
|
||||
color: '#749336',
|
||||
active: {
|
||||
borderColor: '#749336'
|
||||
}
|
||||
color: '#749336'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(111, 61, 6)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(225, 201, 158)',
|
||||
borderColor: 'rgb(245, 224, 191)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 208, 124)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(231, 203, 155)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 208, 124)'
|
||||
}
|
||||
color: 'rgb(231, 203, 155)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(56, 45, 34)',
|
||||
borderColor: 'rgb(104, 84, 61)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(242, 216, 176)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 208, 124)'
|
||||
}
|
||||
color: 'rgb(242, 216, 176)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,19 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(36, 179, 96)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(27, 31, 34)',
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(36, 179, 96)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(115, 161, 191)',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
fillColor: 'rgb(115, 161, 191)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(115, 161, 191)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 115, 148)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
fillColor: 'rgb(191, 115, 148)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 115, 148)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,16 +18,13 @@ export default merge(defaultTheme, {
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=',
|
||||
// 背景重复
|
||||
backgroundRepeat: 'repeat',
|
||||
backgroundSize: 'auto',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(233, 223, 152)',
|
||||
color: '#333',
|
||||
fontSize: 24,
|
||||
borderRadius: 21,
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
borderRadius: 21
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -35,30 +32,18 @@ export default merge(defaultTheme, {
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
fontSize: 16,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,10 +18,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(18, 187, 55)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +26,18 @@ export default merge(defaultTheme, {
|
||||
borderColor: 'transparent',
|
||||
color: '#1a1a1a',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#1a1a1a',
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)'
|
||||
}
|
||||
color: '#1a1a1a'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 2,
|
||||
color: '#1a1a1a',
|
||||
active: {
|
||||
borderColor: 'rgb(18, 187, 55)'
|
||||
}
|
||||
color: '#1a1a1a'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -20,10 +20,7 @@ export default merge(defaultTheme, {
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
borderColor: 'rgb(249, 199, 84)',
|
||||
borderWidth: 1,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
borderWidth: 1
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -32,27 +29,18 @@ export default merge(defaultTheme, {
|
||||
borderWidth: 1,
|
||||
color: '#1a1a1a',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#1a1a1a',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
color: '#1a1a1a'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#1a1a1a',
|
||||
color: '#1a1a1a',
|
||||
borderWidth: 2,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
borderWidth: 2
|
||||
}
|
||||
})
|
||||
|
||||
@@ -20,10 +20,7 @@ export default merge(defaultTheme, {
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
borderColor: 'rgb(189, 197, 201)',
|
||||
borderWidth: 2,
|
||||
active: {
|
||||
borderColor: 'rgb(169, 218, 218)'
|
||||
}
|
||||
borderWidth: 2
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -32,10 +29,7 @@ export default merge(defaultTheme, {
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(56, 123, 233)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
@@ -43,19 +37,13 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(30, 53, 86)',
|
||||
borderColor: 'rgb(30, 53, 86)',
|
||||
borderWidth: 1,
|
||||
marginY: 20,
|
||||
active: {
|
||||
borderColor: 'rgb(169, 218, 218)'
|
||||
}
|
||||
marginY: 20
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(56, 123, 233)',
|
||||
borderColor: 'rgb(56, 123, 233)',
|
||||
color: '#fff',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(169, 218, 218)'
|
||||
}
|
||||
borderWidth: 0
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,7 @@ export default merge(defaultTheme, {
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(255, 255, 255)',
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,26 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(255, 255, 255)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,10 +14,7 @@ export default merge(defaultTheme, {
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(253, 244, 217)',
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -25,27 +22,18 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(242, 200, 104)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(123, 199, 120)',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 123, 91)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,18 +24,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(125, 86, 42)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 123, 91)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(96, 71, 47)',
|
||||
active: {
|
||||
borderColor: 'rgb(173, 123, 91)'
|
||||
}
|
||||
color: 'rgb(96, 71, 47)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -47,9 +37,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 249, 239)',
|
||||
borderColor: 'rgb(173, 123, 91)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(122, 83, 44)',
|
||||
active: {
|
||||
borderColor: 'rgb(202, 117, 79)'
|
||||
}
|
||||
color: 'rgb(122, 83, 44)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 91, 12)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,18 +24,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(50, 113, 96)',
|
||||
borderColor: 'rgb(113, 195, 169)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 91, 12)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(10, 59, 43)',
|
||||
active: {
|
||||
borderColor: 'rgb(173, 91, 12)'
|
||||
}
|
||||
color: 'rgb(10, 59, 43)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -47,9 +37,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(246, 238, 211)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(173, 91, 12)',
|
||||
active: {
|
||||
borderColor: 'rgb(113, 195, 169)'
|
||||
}
|
||||
color: 'rgb(173, 91, 12)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,10 +18,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(28, 178, 43)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,26 +26,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(147,148,149)',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(147, 148, 149)',
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
color: 'rgb(147, 148, 149)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(36, 179, 96)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,28 +25,18 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(36, 179, 96)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -70,12 +70,7 @@ export default {
|
||||
borderWidth: 0,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -93,12 +88,7 @@ export default {
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
@@ -116,12 +106,7 @@ export default {
|
||||
borderWidth: 0,
|
||||
borderRadius: 5,
|
||||
borderDasharray: 'none',
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -139,12 +124,7 @@ export default {
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +158,8 @@ const nodeSizeIndependenceList = [
|
||||
'backgroundImage',
|
||||
'backgroundRepeat',
|
||||
'backgroundPosition',
|
||||
'backgroundSize'
|
||||
'backgroundSize',
|
||||
'rootLineKeepSameInCurve'
|
||||
]
|
||||
export const checkIsNodeSizeIndependenceConfig = (config) => {
|
||||
let keys = Object.keys(config)
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 147, 115)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
fillColor: 'rgb(191, 147, 115)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 147, 115)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,11 +26,6 @@ export default merge(defaultTheme, {
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 115, 115)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
fillColor: 'rgb(191, 115, 115)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 115, 115)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(51, 56, 62)',
|
||||
color: 'rgb(247, 208, 160)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(247, 208, 160)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +25,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(81, 58, 42)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 56, 62)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(127, 93, 64)',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(255, 214, 175)',
|
||||
active: {
|
||||
borderColor: 'rgb(51, 56, 62)'
|
||||
}
|
||||
color: 'rgb(255, 214, 175)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(25, 193, 73)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: '#222',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,28 +25,18 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(69, 149, 96)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(25, 193, 73)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(25, 193, 73)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(251, 158, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(51, 51, 51)',
|
||||
active: {
|
||||
borderColor: 'rgb(25, 193, 73)'
|
||||
}
|
||||
color: 'rgb(51, 51, 51)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(255, 255, 255)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,19 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(209, 210, 210)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 119, 34)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 119, 34)',
|
||||
borderColor: '',
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(23, 153, 243)'
|
||||
}
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,7 @@ export default merge(defaultTheme, {
|
||||
root: {
|
||||
fillColor: 'rgb(55, 165, 255)',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 160, 36)'
|
||||
}
|
||||
borderWidth: 3
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,26 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(55, 165, 255)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(55, 165, 255)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
borderColor: '#222',
|
||||
borderWidth: 3,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(55, 165, 255)'
|
||||
}
|
||||
color: '#222'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
root: {
|
||||
fillColor: 'rgb(0, 192, 184)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 160, 36)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,26 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(184, 235, 233)',
|
||||
borderWidth: 2,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(90, 206, 241)',
|
||||
borderColor: 'transparent',
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#110501',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#a9a4a9',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: '#a9a4a9',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#110501'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#a9a4a9',
|
||||
active: {
|
||||
borderColor: '#ff6811'
|
||||
}
|
||||
color: '#a9a4a9'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: '',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 2,
|
||||
color: '#a9a4a9',
|
||||
active: {
|
||||
borderColor: '#110501'
|
||||
}
|
||||
color: '#a9a4a9'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
root: {
|
||||
fillColor: 'rgb(139, 109, 225)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(243, 104, 138)',
|
||||
borderWidth: 2
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,28 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(139, 109, 225)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(139, 109, 225)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(139, 109, 225)',
|
||||
borderWidth: 2
|
||||
}
|
||||
color: '#222'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(255, 233, 157)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 233, 157)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(211, 58, 21)',
|
||||
borderColor: 'rgb(222, 101, 85)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 233, 157)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(144, 71, 43)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 233, 157)'
|
||||
}
|
||||
color: 'rgb(144, 71, 43)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 247, 211)',
|
||||
borderColor: 'rgb(255, 202, 162)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(187, 101, 69)',
|
||||
active: {
|
||||
borderColor: 'rgb(222, 101, 85)'
|
||||
}
|
||||
color: 'rgb(187, 101, 69)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(123, 115, 191)',
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
fillColor: 'rgb(123, 115, 191)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(123, 115, 191)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(34, 34, 34)',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(34, 34, 34)',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(34, 34, 34)',
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
color: 'rgb(34, 34, 34)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,9 +37,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(34, 34, 34)',
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
color: 'rgb(34, 34, 34)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(65, 89, 158)',
|
||||
active: {
|
||||
borderColor: 'rgb(251, 227, 188)',
|
||||
borderWidth: 3
|
||||
}
|
||||
color: 'rgb(65, 89, 158)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +25,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(65, 89, 158)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(65, 89, 158)',
|
||||
active: {
|
||||
borderColor: 'rgb(251, 227, 188)'
|
||||
}
|
||||
color: 'rgb(65, 89, 158)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(65, 89, 158)',
|
||||
active: {
|
||||
borderColor: 'rgb(251, 227, 188)'
|
||||
}
|
||||
color: 'rgb(65, 89, 158)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 112, 52)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +25,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(51, 51, 51)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 112, 52)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 112, 52)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(255, 222, 69)',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(51, 51, 51)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 112, 52)'
|
||||
}
|
||||
color: 'rgb(51, 51, 51)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { nodeDataNoStylePropList } from '../constants/constant'
|
||||
|
||||
// 深度优先遍历树
|
||||
export const walk = (
|
||||
@@ -632,3 +633,39 @@ export const isMobile = () => {
|
||||
navigator.userAgent
|
||||
)
|
||||
}
|
||||
|
||||
// 获取对象改变了的的属性
|
||||
export const getObjectChangedProps = (oldObject, newObject) => {
|
||||
const res = {}
|
||||
Object.keys(newObject).forEach((prop) => {
|
||||
const oldVal = oldObject[prop]
|
||||
const newVal = newObject[prop]
|
||||
if (getType(oldVal) !== getType(newVal)) {
|
||||
res[prop] = newVal
|
||||
return
|
||||
}
|
||||
if (getType(oldVal) === 'Object') {
|
||||
if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
|
||||
res[prop] = newVal
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (oldVal !== newVal) {
|
||||
res[prop] = newVal
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
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/assets/.DS_Store
vendored
BIN
web/src/assets/avatar/乙.jpg
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
web/src/assets/avatar/南风.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
web/src/assets/avatar/布林.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
web/src/assets/avatar/蜉蝣撼大叔.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
web/src/assets/img/themes/cactus.jpg
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
web/src/assets/img/themes/classic5.jpg
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
web/src/assets/img/themes/dark3.jpg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
web/src/assets/img/themes/dark4.jpg
Normal file
|
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
@@ -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
@@ -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
@@ -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
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ export default {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'parallelogram',
|
||||
active: {
|
||||
borderColor: 'rgba(2, 167, 240, 0.5)',
|
||||
}
|
||||
shape: 'parallelogram'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -31,18 +28,12 @@ export default {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'diamond',
|
||||
active: {
|
||||
borderColor: 'rgba(2, 167, 240, 0.5)',
|
||||
}
|
||||
shape: 'diamond'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgba(2, 167, 240, 0.5)'
|
||||
}
|
||||
color: '#fff'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,10 +41,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(0, 117, 255)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(0, 21, 21)',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 243, 255)'
|
||||
}
|
||||
color: 'rgb(0, 21, 21)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: 'rgb(26, 26, 26)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(235, 255, 187)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default {
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(39, 222, 232)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(39, 222, 232)'
|
||||
}
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,10 +37,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(26, 26, 26)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(26, 26, 26)',
|
||||
active: {
|
||||
borderColor: 'rgb(39, 222, 232)'
|
||||
}
|
||||
color: 'rgb(26, 26, 26)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ export default {
|
||||
borderColor: 'rgb(207, 121, 105)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(172, 202, 199)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -31,18 +28,12 @@ export default {
|
||||
borderColor: 'rgb(222, 186, 183)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(172, 202, 199)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(131, 90, 64)',
|
||||
active: {
|
||||
borderColor: 'rgb(172, 202, 199)'
|
||||
}
|
||||
color: 'rgb(131, 90, 64)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,10 +41,7 @@ export default {
|
||||
fillColor: 'rgb(172, 202, 199)',
|
||||
borderColor: 'rgb(172, 202, 199)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(91, 102, 97)',
|
||||
active: {
|
||||
borderColor: 'rgb(207, 121, 105)'
|
||||
}
|
||||
color: 'rgb(91, 102, 97)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ export default {
|
||||
borderColor: 'rgb(255, 0, 214)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 181, 0)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +27,12 @@ export default {
|
||||
color: 'rgb(248, 177, 237)',
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 181, 0)',
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 181, 0)'
|
||||
}
|
||||
color: '#fff'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,10 +40,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(255, 181, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(17, 17, 84)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 0, 214)'
|
||||
}
|
||||
color: 'rgb(17, 17, 84)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,7 @@ export default {
|
||||
color: '#fff',
|
||||
borderColor: 'rgb(22, 22, 22)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#a13600',
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -25,18 +22,12 @@ export default {
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(22, 22, 22)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(22, 22, 22)'
|
||||
}
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -44,9 +35,6 @@ export default {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(34, 34, 34)',
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
color: 'rgb(34, 34, 34)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: 'rgb(18, 187, 55)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(136, 100, 0)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default {
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 92, 92)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(26, 26, 26)',
|
||||
active: {
|
||||
borderColor: 'rgb(209, 237, 176)'
|
||||
}
|
||||
color: 'rgb(26, 26, 26)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,10 +37,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(136, 100, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(136, 100, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 92, 92)'
|
||||
}
|
||||
color: 'rgb(136, 100, 0)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 155, 255)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default {
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 189, 255)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 66, 157)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 189, 255)'
|
||||
}
|
||||
color: 'rgb(0, 66, 157)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,10 +37,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(0, 155, 255)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(0, 155, 255)',
|
||||
active: {
|
||||
borderColor: 'rgba(2, 167, 240, 0.5)'
|
||||
}
|
||||
color: 'rgb(0, 155, 255)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: 'rgb(51, 149, 255)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 168, 101)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -26,18 +23,12 @@ export default {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 168, 101)',
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 168, 101)'
|
||||
}
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -45,10 +36,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(255, 168, 101)',
|
||||
borderWidth: 2,
|
||||
color: '#000',
|
||||
active: {
|
||||
borderColor: 'rgb(51, 149, 255)'
|
||||
}
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ export default {
|
||||
rootStyle: 'Root Node',
|
||||
associativeLineText: 'Associative line text',
|
||||
fontFamily: 'Font family',
|
||||
fontSize: 'Font size'
|
||||
fontSize: 'Font size',
|
||||
isShowScrollbar: 'Is show scrollbar'
|
||||
},
|
||||
color: {
|
||||
moreColor: 'More color'
|
||||
|
||||
@@ -50,7 +50,8 @@ export default {
|
||||
rootStyle: '根节点',
|
||||
associativeLineText: '关联线文字',
|
||||
fontFamily: '字体',
|
||||
fontSize: '字号'
|
||||
fontSize: '字号',
|
||||
isShowScrollbar: '是否显示滚动条'
|
||||
},
|
||||
color: {
|
||||
moreColor: '更多颜色'
|
||||
|
||||
@@ -11,7 +11,7 @@ let langList = [
|
||||
}
|
||||
]
|
||||
let StartList = ['introduction', 'start', 'deploy', 'client', 'translate', 'changelog']
|
||||
let CourseList = new Array(22).fill(0).map((_, index) => {
|
||||
let CourseList = new Array(24).fill(0).map((_, index) => {
|
||||
return 'course' + (index + 1)
|
||||
})
|
||||
let APIList = [
|
||||
@@ -34,11 +34,12 @@ let APIList = [
|
||||
'nodeImgAdjust',
|
||||
'search',
|
||||
'painter',
|
||||
'scrollbar',
|
||||
'xmind',
|
||||
'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,89 @@
|
||||
# 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.
|
||||
|
||||
Fix:
|
||||
|
||||
> 1.Fix rendering anomalies when the node border size is relatively large.
|
||||
>
|
||||
> 2.Fixed an issue where the node style of the associated line will not be updated when switching themes.
|
||||
>
|
||||
> 3.Fix that selecting all did not trigger node_ The issue with active events.
|
||||
|
||||
新增:
|
||||
|
||||
> 1.When folding nodes, displays the number of collapsed nodes.
|
||||
>
|
||||
> 2.Support the position of the endpoint of the associated line to follow mouse drag changes.
|
||||
>
|
||||
> 3.Add a scrollbar plugin.
|
||||
>
|
||||
> 4.Support opening specified online files through fileURL query parameters in URLs.
|
||||
>
|
||||
> 5.The fishbone diagram supports setting node margins.
|
||||
>
|
||||
> 6.By default, double-click to reset the canvas.
|
||||
>
|
||||
> 7.Modify the parameters of the export image method, and when exporting PDF, if the size of the mind map is smaller than A4 paper, do not rotate the direction.
|
||||
>
|
||||
> 8.Improve the clarity of exported images and PDFs on high-definition screens.
|
||||
>
|
||||
> 9.Add a pre destruction lifecycle function to the plugin to address the issue of some side effects that were not cleared during the destruction of the mind map.
|
||||
>
|
||||
> 10.Optimize the settings of the basic style and do not trigger full rendering when modifying theme attributes that do not affect size.
|
||||
>
|
||||
> 11.Prohibit triggering node right-click menu events when multiple node selections are completed, to avoid triggering the right-click menu display.
|
||||
>
|
||||
> 12.Optimize the Select plugin so that if multiple selected nodes do not change, the activation event is not triggered.
|
||||
>
|
||||
> 13.The activation node list thrown by event node_active no longer directly references the internal activation list.
|
||||
>
|
||||
> 14.Optimize the logic of mouse button down node events, and support dragging the canvas by holding down the root node with the right mouse button in the right-click drag and drop canvas mode.
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.Do not directly reference the internal activation node list to optimize performance.
|
||||
>
|
||||
> 2.Support configuring whether to display scrollbars.
|
||||
>
|
||||
> 3.Delete the active node configuration in the sidebar node style configuration section.
|
||||
|
||||
## 0.6.17
|
||||
|
||||
Fix:
|
||||
|
||||