Compare commits

...

75 Commits

Author SHA1 Message Date
wanglin2
1452fd2a28 打包0.7.1-fix.1 2023-09-15 09:35:38 +08:00
wanglin2
c296b99d5a Fix:修复拖拽节点时没有排除被拖拽节点的下级节点的问题 2023-09-15 09:35:24 +08:00
wanglin2
e9de1e675f 打包0.7.1 2023-09-15 09:19:52 +08:00
wanglin2
b3e254c0ee Doc: update 2023-09-15 09:13:41 +08:00
wanglin2
bd3d470e40 Fix:修复画布左上角距浏览器窗口左上角不为0时拖拽节点画布自动移动的问题 2023-09-14 19:02:06 +08:00
wanglin2
21564445d6 Feat:拖拽节点时鼠标移到画布边缘时画布自动移动 2023-09-14 18:24:10 +08:00
街角小林
e53d1b1948 Merge pull request #331 from esky-wh/feature
Feature
2023-09-14 15:46:55 +08:00
esky-wh
c428f166a6 Feat: 1.添加开启删除节点后激活节点配置2.增加功能删除节点后激活相邻节点 2023-09-14 14:11:13 +08:00
esky-wh
f19704ec41 Merge pull request #1 from wanglin2/feature
Feature
2023-09-14 10:59:57 +08:00
wanglin2
d47f013f84 Demo:编辑页面改为异步路由 2023-09-14 09:17:30 +08:00
wanglin2
612d675a19 Fix:修复注册插件的方法没有去重的问题 2023-09-14 09:16:50 +08:00
wanglin2
13f571e0b5 Fix:重构节点拖拽逻辑,修复节点拖拽没有适配各种结构的问题 2023-09-13 16:48:41 +08:00
wanglin2
b09d408ab3 Fix:修复没有注册select插件时节点右键事件报错的问题 2023-09-11 20:07:14 +08:00
wanglin2
ea95ae2b5d Feat:重构滚动条,优化使用体验 2023-09-11 19:57:20 +08:00
wanglin2
2590e21807 Fix:不完美修复组织结构图概要和节点冲突的问题 2023-09-10 09:05:13 +08:00
wanglin2
fb41653d79 Fix:不完美修复目录组织图概要和节点冲突的问题 2023-09-09 15:42:21 +08:00
wanglin2
4c9c34a0ea Fix:不完美的解决思维导图结构概要和节点的冲突问题 2023-09-09 14:43:02 +08:00
wanglin2
0d5602b832 Fix:不完美的解决逻辑结构图概要和节点的冲突问题 2023-09-09 11:35:13 +08:00
wanglin2
af87f84ce8 Doc: update 2023-09-08 23:18:07 +08:00
wanglin2
a96c16aa45 Fix:修复导出带有贴纸的数据为xmind格式时贴纸无法显示的问题 2023-09-08 23:04:30 +08:00
wanglin2
e29e1c26e5 Fix:修复导出的xmind文件在最新版xmind软件上打开时提示已损坏的问题 2023-09-08 22:58:18 +08:00
wanglin2
fee38cbe8a Doc: update 2023-09-08 21:47:01 +08:00
wanglin2
58ca173234 Fix:修复导入存在为标题为空的节点的xmind文件报错的问题 2023-09-08 21:25:39 +08:00
wanglin2
aee10c6810 Feat:节点数据data中以_开头的字段被认为是自定义字段 2023-09-08 17:54:15 +08:00
wanglin2
fe43be3451 Fix:修复画布左上角距浏览器窗口不为0时多选节点鼠标移动到边缘时画布滚动异常的问题 2023-09-08 17:18:01 +08:00
wanglin2
21868c7f44 Feat:优化一些情况下的节点拖拽 2023-09-08 09:21:32 +08:00
wanglin2
5585d2a4f7 Feat:节点比对全部改为通过uid对比 2023-09-07 18:45:02 +08:00
wanglin2
1d04038609 Fix:修复关联线端点改变后刷新页面未保存的问题 2023-09-07 18:21:12 +08:00
wanglin2
305c5e9f70 Doc: update 2023-09-07 17:36:36 +08:00
wanglin2
4ac91003bb Demo:页面增加显示当前核心库版本号 2023-09-07 17:17:32 +08:00
wanglin2
44410900aa Doc: update 2023-09-06 09:18:29 +08:00
wanglin2
239b369391 打包 2023-09-04 22:04:14 +08:00
wanglin2
d9638fef98 Demo: update 2023-09-04 21:49:03 +08:00
wanglin2
ecb9482b82 Demo:新增主题,更新文档 2023-09-04 21:47:55 +08:00
wanglin2
cd5556aad5 合并 2023-09-04 17:00:39 +08:00
wanglin2
063af62cf6 打包0.7.0 2023-09-04 16:58:57 +08:00
wanglin2
a4611d11a8 Doc: update 2023-09-04 16:49:24 +08:00
wanglin2
5d1f460a94 Feat:鱼骨图支持设置节点margin 2023-09-02 20:58:20 +08:00
wanglin2
075e578bb4 Feat:支持在url中通过fileURL查询参数打开指定的在线文件 2023-09-02 09:53:05 +08:00
wanglin2
32c17921ca Demo:支持配置是否显示滚动条 2023-09-02 08:31:08 +08:00
wanglin2
7c470c9b88 Doc: update 2023-09-01 16:44:02 +08:00
wanglin2
e472b840f1 update README 2023-09-01 16:41:24 +08:00
wanglin2
726460c812 Fix:修复全选没有触发node_active事件的问题 2023-09-01 16:36:25 +08:00
wanglin2
7ccf796c34 Feat:新增滚动条插件 2023-09-01 16:33:07 +08:00
wanglin2
c183306ab2 update 2023-09-01 10:43:48 +08:00
wanglin2
29bb27aa23 Feat:优化鼠标按下节点事件逻辑,在右键拖拽画布模式下支持右键按住根节点拖拽画布 2023-09-01 10:41:36 +08:00
wanglin2
7f9d03c8af update 2023-09-01 10:17:39 +08:00
wanglin2
c3a0e09f6d Feat:支持双击节点进入编辑状态时全选文本的配置项 2023-09-01 10:16:21 +08:00
wanglin2
1d497b2f13 Feat:默认关闭双击复位画布 2023-09-01 09:53:45 +08:00
wanglin2
f43a2ebdef Fix:修复切换主题时存在关联线的节点样式不会更改的问题 2023-09-01 09:50:33 +08:00
wanglin2
4c3f3cb1ab Feat:node_active事件抛出的激活节点列表不再直接引用内部激活列表 2023-09-01 09:45:17 +08:00
wanglin2
a404a71ba2 Demo:不直接引用内部激活节点列表,优化性能 2023-09-01 09:36:39 +08:00
wanglin2
6b4c118a2b Fix:优化Select插件,如果多选节点没有变化,那么不触发激活激活事件 2023-09-01 09:34:22 +08:00
wanglin2
1157257911 Fix:1.优化展开收起代码逻辑;2.修复关联线不兼容老版的问题,修复端点偏移量未保存的问题 2023-08-31 19:43:59 +08:00
街角小林
f2642c9d63 Merge pull request #294 from eSeaSky-WuHan/release
Feat:
1、折叠节点添加配置项;
2、关联线效果优化。
2023-08-31 15:43:13 +08:00
eseasky
91bc0351b8 Feat: 关联线拖动效果优化 2023-08-30 20:11:44 +08:00
eseasky
df2dc96ba5 Feat: 1.配置开启收起显示节点数量;2.支持配置传入一个函数,允许控制收起时显示的内容;3.支持配置expandBtnSize展开收起按钮的尺寸;4.收起时的样式可以使用expandBtnStyle配置颜色,字体,边框颜色。 2023-08-30 20:09:16 +08:00
eseasky
3d86650f22 Feat: 实现关联线端点位置随鼠标拖拽变化 2023-08-29 20:09:57 +08:00
eseasky
a31e93bacf Feat: 收起节点时,显示折叠的节点数量 2023-08-29 19:46:22 +08:00
wanglin2
a3362e44fe Feat:右键多选节点结束时禁止触发节点右键菜单事件,避免触发右键菜单显示 2023-08-29 09:46:36 +08:00
wanglin2
f7f234b4cb Feat:优化基础样式的设置,修改不影响大小的主题属性时不触发全量渲染 2023-08-28 20:23:23 +08:00
wanglin2
48f1de5c25 Fix:修复节点hover和激活节点时渲染的矩形会重叠的问题 2023-08-28 15:40:50 +08:00
wanglin2
c533459da1 Fix:修复自定义节点内容时hover和激活效果不显示的问题 2023-08-28 13:54:37 +08:00
wanglin2
d99895b845 Fix:修改节点hover和激活矩形逻辑,修复连线和节点不重合的问题 2023-08-28 13:47:58 +08:00
wanglin2
c65393d1bc Fix:修复导出图片和svg时节点hover矩形默认会显示的问题 2023-08-28 09:52:46 +08:00
wanglin2
5d133f74cf Feat:删除设置节点激活样式的逻辑 2023-08-28 09:41:08 +08:00
wanglin2
5997c98b8f Demo:删除侧边栏节点样式配置部分的激活节点配置 2023-08-28 09:33:29 +08:00
wanglin2
217d66f692 Feat:删除主题文件中节点激活样式的部分 2023-08-28 09:21:18 +08:00
wanglin2
d14d887c1a Feat:优化鼠标hover和激活节点矩形,增加和内容的距离 2023-08-28 09:11:39 +08:00
wanglin2
e36e238c2f Fix:修复大边框尺寸的渲染问题,包括重叠,内容不居中 2023-08-28 08:49:45 +08:00
wanglin2
1b0646af6d Feat:节点矩形形状改为使用path渲染 2023-08-27 22:51:18 +08:00
wanglin2
a24f7a73c8 Feat:节点增加鼠标滑过效果、节点激活效果重构、去除激活样式改变功能 2023-08-27 22:10:49 +08:00
wanglin2
b35dd282ec Feat:插件新增销毁前生命周期函数,解决销毁思维导图时插件的一些副作用没有清除的问题 2023-08-27 10:34:15 +08:00
wanglin2
8c0c2c5bc4 Feat:提升导出的图片和pdf在高清屏的清晰度 2023-08-27 09:40:45 +08:00
wanglin2
3763cd0efc Feat:修改导出图片方法的参数,导出pdf时如果思维导图尺寸小于a4纸那么不旋转方向 2023-08-26 21:54:00 +08:00
165 changed files with 3993 additions and 1289 deletions

View File

@@ -87,9 +87,7 @@ const mindMap = new MindMap({
# 微信交流群
<img src="./qrcode.jpg" style="width: 300px" />
如果已过期,可以微信添加`wanglinguanfang`拉你入群。
群聊人数较多,无法通过二维码入群,可以微信添加`wanglinguanfang`拉你入群。
# 请作者喝杯咖啡
@@ -165,4 +163,24 @@ 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>
<span>
<img src="./web/src/assets/avatar/敏.jpg" style="width: 50px;height: 50px;" />
<span>敏</span>
</span>
</p>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel="icon" href="dist/logo.ico"><title>思绪思维导图</title><script>// 自定义静态资源的路径
window.externalPublicPath = './dist/'
// 接管应用
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?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?e9067af0f619cbea332e" rel="stylesheet"><link href="dist/css/app.css?e9067af0f619cbea332e" 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?e9067af0f619cbea332e"></script><script src="dist/js/app.js?e9067af0f619cbea332e"></script></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -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

View File

@@ -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

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.6.17",
"version": "0.7.1-fix.1",
"description": "一个简单的web在线思维导图",
"authors": [
{

View File

@@ -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;
}
`

View File

@@ -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
}

View File

@@ -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 || '',

View File

@@ -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()

View File

@@ -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
}
// 获取数据

View File

@@ -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(`

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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
}
// 获取节点包括概要在内的宽度

View File

@@ -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)

View File

@@ -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 ${

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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({

View File

@@ -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) => {

View File

@@ -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 || this.node.isParent(node)) {
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'

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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
// 遍历到的节点的中心点

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View 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

View File

@@ -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'

View File

@@ -148,6 +148,11 @@ class TouchEvent {
beforePluginRemove() {
this.unBindEvent()
}
// 插件被卸载前做的事情
beforePluginDestroy() {
this.unBindEvent()
}
}
TouchEvent.instanceName = 'touchEvent'

View File

@@ -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
}

View File

@@ -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) => {

View File

@@ -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
}
]
}
}

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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)'
}
})

View File

@@ -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)'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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
}
})

View File

@@ -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
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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)'
}
})

View File

@@ -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)'
}
})

View File

@@ -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'
}
})

View File

@@ -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)'
}
})

View File

@@ -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)

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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)'
}
})

View File

@@ -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)'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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'
}
})

View File

@@ -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)'
}
})

View File

@@ -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'
}
})

View File

@@ -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)'
}
})

View File

@@ -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)'
}
})

View File

@@ -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)'
}
})

View File

@@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -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'),
}

View 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'
}
}

View 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'
}
}

View 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)'
}
}

View 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'
}
}

View File

@@ -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)'
}
}

View File

@@ -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
}
]

View File

@@ -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)'
}
}

View File

@@ -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)'
}
}

View File

@@ -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)'
}
}

View File

@@ -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)'
}
}

View File

@@ -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)'
}
}

View File

@@ -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)'
}
}

View File

@@ -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'
}
}

View File

@@ -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'

View File

@@ -50,7 +50,8 @@ export default {
rootStyle: '根节点',
associativeLineText: '关联线文字',
fontFamily: '字体',
fontSize: '字号'
fontSize: '字号',
isShowScrollbar: '是否显示滚动条'
},
color: {
moreColor: '更多颜色'

View File

@@ -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)
})

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