mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-25 11:18:40 +08:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd5556aad5 | ||
|
|
063af62cf6 | ||
|
|
a4611d11a8 | ||
|
|
5d1f460a94 | ||
|
|
075e578bb4 | ||
|
|
32c17921ca | ||
|
|
7c470c9b88 | ||
|
|
e472b840f1 | ||
|
|
726460c812 | ||
|
|
7ccf796c34 | ||
|
|
c183306ab2 | ||
|
|
29bb27aa23 | ||
|
|
7f9d03c8af | ||
|
|
c3a0e09f6d | ||
|
|
1d497b2f13 | ||
|
|
f43a2ebdef | ||
|
|
4c3f3cb1ab | ||
|
|
a404a71ba2 | ||
|
|
6b4c118a2b | ||
|
|
1157257911 | ||
|
|
f2642c9d63 | ||
|
|
91bc0351b8 | ||
|
|
df2dc96ba5 | ||
|
|
3d86650f22 | ||
|
|
a31e93bacf | ||
|
|
a3362e44fe | ||
|
|
f7f234b4cb | ||
|
|
48f1de5c25 | ||
|
|
c533459da1 | ||
|
|
d99895b845 | ||
|
|
c65393d1bc | ||
|
|
5d133f74cf | ||
|
|
5997c98b8f | ||
|
|
217d66f692 | ||
|
|
d14d887c1a | ||
|
|
e36e238c2f | ||
|
|
1b0646af6d | ||
|
|
a24f7a73c8 | ||
|
|
b35dd282ec | ||
|
|
8c0c2c5bc4 | ||
|
|
3763cd0efc | ||
|
|
4ce9533763 | ||
|
|
43ecdafdd1 | ||
|
|
5ff8704778 | ||
|
|
d206d6dd99 | ||
|
|
850b9ed936 | ||
|
|
26508b5a62 | ||
|
|
061c82459e | ||
|
|
d2bf5be7c7 | ||
|
|
b726aa30e5 | ||
|
|
6546ec090e | ||
|
|
4dc5754f1d | ||
|
|
7f199e6c2f | ||
|
|
2548ac4eb4 | ||
|
|
60e503ab1f | ||
|
|
c718cbc030 | ||
|
|
beb2b550f0 | ||
|
|
d3e1389f10 | ||
|
|
68cb24c0b6 | ||
|
|
e3959107bd | ||
|
|
4b3c81ab91 | ||
|
|
76b5d9d11d | ||
|
|
22d0fe5ac4 | ||
|
|
8b8d549abd | ||
|
|
731a5a504d | ||
|
|
008e697b74 | ||
|
|
fe1745e779 | ||
|
|
e6ac9be402 | ||
|
|
b427a9ed1b | ||
|
|
a4dc9210b3 | ||
|
|
fb681de1f5 | ||
|
|
3757622521 | ||
|
|
12265be7d4 | ||
|
|
df1aed7e04 | ||
|
|
c32c9d1ba1 | ||
|
|
26d75c9203 | ||
|
|
8d1e9fa8e9 | ||
|
|
ebc99e97af | ||
|
|
efe4aa0ec2 | ||
|
|
3f659af1e1 | ||
|
|
64209a6392 | ||
|
|
8d1e5dd2e9 | ||
|
|
d218122752 | ||
|
|
9af8afca22 | ||
|
|
002ec41ba8 | ||
|
|
ffff257f57 | ||
|
|
41d0b675a0 | ||
|
|
7601d6730d | ||
|
|
f0e9b76bb5 | ||
|
|
4ec33062a4 | ||
|
|
edc63b1293 | ||
|
|
ec83976818 | ||
|
|
393410757b | ||
|
|
f20748744a | ||
|
|
067131f76d | ||
|
|
f689d333f9 | ||
|
|
b3059bb6a3 | ||
|
|
b135f6a61c | ||
|
|
2a8b71497f | ||
|
|
726ebc5e88 | ||
|
|
2c56cd453c | ||
|
|
bf2f9b2697 | ||
|
|
fbd061e8b3 | ||
|
|
680320ba76 | ||
|
|
0d992bd6b1 | ||
|
|
c80eacc5e7 | ||
|
|
6d202b4b7d | ||
|
|
94478fe9f3 | ||
|
|
5745e4567b | ||
|
|
c6a3f4ac7b |
26
README.md
26
README.md
@@ -87,9 +87,7 @@ const mindMap = new MindMap({
|
||||
|
||||
# 微信交流群
|
||||
|
||||
<img src="./qrcode.jpg" style="width: 300px" />
|
||||
|
||||
如果已过期,可以微信添加`wanglinguanfang`拉你入群。
|
||||
群聊人数较多,无法通过二维码入群,可以微信添加`wanglinguanfang`拉你入群。
|
||||
|
||||
# 请作者喝杯咖啡
|
||||
|
||||
@@ -97,7 +95,7 @@ const mindMap = new MindMap({
|
||||
|
||||
> 厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds
|
||||
|
||||
> 转账请备注【思维导图】。你的头像和名字将会出现在下面和[文档页面](https://wanglin2.github.io/mind-map/#/doc/zh/introduction/%E8%AF%B7%E4%BD%9C%E8%80%85%E5%96%9D%E6%9D%AF%E5%92%96%E5%95%A1)
|
||||
> 推荐使用支付宝,微信获取不到头像。转账请备注【思维导图】。你的头像和名字将会出现在下面和[文档页面](https://wanglin2.github.io/mind-map/#/doc/zh/introduction/%E8%AF%B7%E4%BD%9C%E8%80%85%E5%96%9D%E6%9D%AF%E5%92%96%E5%95%A1)
|
||||
|
||||
<p>
|
||||
<img src="./web/src/assets/img/alipay.jpg" style="width: 300px" />
|
||||
@@ -153,4 +151,24 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/才镇.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>才镇</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/小米.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>小米bbᯤ²ᴳ</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/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>
|
||||
</p>
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel="icon" href="dist/logo.ico"><title>思绪思维导图</title><script>// 自定义静态资源的路径
|
||||
window.externalPublicPath = './dist/'
|
||||
// 接管应用
|
||||
window.takeOverApp = false</script><link href="dist/css/chunk-vendors.css?5afd1a8b2213b3355dce" rel="stylesheet"><link href="dist/css/app.css?5afd1a8b2213b3355dce" 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?829fd06748ba83d9ecea" rel="stylesheet"><link href="dist/css/app.css?829fd06748ba83d9ecea" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script>const getDataFromBackend = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
@@ -66,4 +66,4 @@
|
||||
// 可以通过window.$bus.$on()来监听应用的一些事件
|
||||
// 实例化页面
|
||||
window.initApp()
|
||||
}</script><script src="dist/js/chunk-vendors.js?5afd1a8b2213b3355dce"></script><script src="dist/js/app.js?5afd1a8b2213b3355dce"></script></body></html>
|
||||
}</script><script src="dist/js/chunk-vendors.js?829fd06748ba83d9ecea"></script><script src="dist/js/app.js?829fd06748ba83d9ecea"></script></body></html>
|
||||
BIN
qrcode.jpg
BIN
qrcode.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
@@ -13,6 +13,7 @@ import NodeImgAdjust from './src/plugins/NodeImgAdjust.js'
|
||||
import TouchEvent from './src/plugins/TouchEvent.js'
|
||||
import Search from './src/plugins/Search.js'
|
||||
import Painter from './src/plugins/Painter.js'
|
||||
import Scrollbar from './src/plugins/Scrollbar.js'
|
||||
import xmind from './src/parse/xmind.js'
|
||||
import markdown from './src/parse/markdown.js'
|
||||
import icons from './src/svg/icons.js'
|
||||
@@ -42,5 +43,6 @@ MindMap
|
||||
.usePlugin(NodeImgAdjust)
|
||||
.usePlugin(Search)
|
||||
.usePlugin(Painter)
|
||||
.usePlugin(Scrollbar)
|
||||
|
||||
export default MindMap
|
||||
14
simple-mind-map/index.d.ts
vendored
Normal file
14
simple-mind-map/index.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// declare module "simple-mind-map";
|
||||
declare module 'simple-mind-map'{
|
||||
class MindMap {
|
||||
constructor(opt:any);
|
||||
handleOpt(opt:any):void;
|
||||
render(callback:any, source:string):void;
|
||||
reRender(callback:any, source:string):void;
|
||||
resize():void;
|
||||
on(event:any, fn:any):void;
|
||||
setFullData(data:any):void;
|
||||
getData(withConfig:any):any;
|
||||
}
|
||||
export default MindMap;
|
||||
}
|
||||
@@ -7,10 +7,18 @@ import Style from './src/core/render/node/Style'
|
||||
import KeyCommand from './src/core/command/KeyCommand'
|
||||
import Command from './src/core/command/Command'
|
||||
import BatchExecution from './src/utils/BatchExecution'
|
||||
import { layoutValueList, CONSTANTS, commonCaches } from './src/constants/constant'
|
||||
import {
|
||||
layoutValueList,
|
||||
CONSTANTS,
|
||||
commonCaches,
|
||||
ERROR_TYPES,
|
||||
cssContent
|
||||
} from './src/constants/constant'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import { simpleDeepClone, getType } from './src/utils'
|
||||
import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default'
|
||||
import { simpleDeepClone, getType, getObjectChangedProps } from './src/utils'
|
||||
import defaultTheme, {
|
||||
checkIsNodeSizeIndependenceConfig
|
||||
} from './src/themes/default'
|
||||
import { defaultOpt } from './src/constants/defaultOptions'
|
||||
|
||||
// 思维导图
|
||||
@@ -22,11 +30,17 @@ class MindMap {
|
||||
|
||||
// 容器元素
|
||||
this.el = this.opt.el
|
||||
if (!this.el) throw new Error('缺少容器元素el')
|
||||
this.elRect = this.el.getBoundingClientRect()
|
||||
|
||||
// 画布宽高
|
||||
this.width = this.elRect.width
|
||||
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)
|
||||
@@ -68,7 +82,7 @@ class MindMap {
|
||||
this.batchExecution = new BatchExecution()
|
||||
|
||||
// 注册插件
|
||||
MindMap.pluginList.forEach((plugin) => {
|
||||
MindMap.pluginList.forEach(plugin => {
|
||||
this.initPlugin(plugin)
|
||||
})
|
||||
|
||||
@@ -92,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', () => {
|
||||
@@ -136,10 +163,10 @@ class MindMap {
|
||||
|
||||
// 初始化缓存数据
|
||||
initCache() {
|
||||
Object.keys(commonCaches).forEach((key) => {
|
||||
Object.keys(commonCaches).forEach(key => {
|
||||
let type = getType(commonCaches[key])
|
||||
let value = ''
|
||||
switch(type) {
|
||||
switch (type) {
|
||||
case 'Boolean':
|
||||
value = false
|
||||
break
|
||||
@@ -164,7 +191,7 @@ class MindMap {
|
||||
this.renderer.clearAllActive()
|
||||
this.opt.theme = theme
|
||||
this.render(null, CONSTANTS.CHANGE_THEME)
|
||||
this.emit('view_theme_change', theme)
|
||||
this.emit('view_theme_change', theme)
|
||||
}
|
||||
|
||||
// 获取当前主题
|
||||
@@ -174,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)
|
||||
}
|
||||
|
||||
@@ -278,8 +307,12 @@ class MindMap {
|
||||
|
||||
// 导出
|
||||
async export(...args) {
|
||||
let result = await this.doExport.export(...args)
|
||||
return result
|
||||
try {
|
||||
let result = await this.doExport.export(...args)
|
||||
return result
|
||||
} catch (error) {
|
||||
this.opt.errorHandler(ERROR_TYPES.EXPORT_ERROR, error)
|
||||
}
|
||||
}
|
||||
|
||||
// 转换位置
|
||||
@@ -317,17 +350,23 @@ class MindMap {
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 内边距
|
||||
rect.width += paddingX
|
||||
rect.height += paddingY
|
||||
draw.translate(paddingX / 2, paddingY / 2)
|
||||
rect.width += paddingX * 2
|
||||
rect.height += paddingY * 2
|
||||
draw.translate(paddingX, paddingY)
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
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) && this.watermark && this.watermark.hasWatermark()) {
|
||||
if (
|
||||
(rect.width > origWidth || rect.height > origHeight) &&
|
||||
this.watermark &&
|
||||
this.watermark.hasWatermark()
|
||||
) {
|
||||
this.width = rect.width
|
||||
this.height = rect.height
|
||||
this.watermark.draw()
|
||||
@@ -388,7 +427,10 @@ class MindMap {
|
||||
// 销毁
|
||||
destroy() {
|
||||
// 移除插件
|
||||
[...MindMap.pluginList].forEach((plugin) => {
|
||||
;[...MindMap.pluginList].forEach(plugin => {
|
||||
if (this[plugin.instanceName].beforePluginDestroy) {
|
||||
this[plugin.instanceName].beforePluginDestroy()
|
||||
}
|
||||
this[plugin.instanceName] = null
|
||||
})
|
||||
// 解绑事件
|
||||
@@ -397,7 +439,9 @@ class MindMap {
|
||||
this.svg.remove()
|
||||
// 去除给容器元素设置的背景样式
|
||||
Style.removeBackgroundStyle(this.el)
|
||||
this.el.innerHTML = ''
|
||||
this.el = null
|
||||
this.removeCss()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +452,8 @@ MindMap.usePlugin = (plugin, opt = {}) => {
|
||||
MindMap.pluginList.push(plugin)
|
||||
return MindMap
|
||||
}
|
||||
MindMap.hasPlugin = (plugin) => {
|
||||
return MindMap.pluginList.findIndex((item) => {
|
||||
MindMap.hasPlugin = plugin => {
|
||||
return MindMap.pluginList.findIndex(item => {
|
||||
return item === plugin
|
||||
})
|
||||
}
|
||||
|
||||
15
simple-mind-map/package-lock.json
generated
15
simple-mind-map/package-lock.json
generated
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.6.12",
|
||||
"version": "0.6.15-fix.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "0.6.12",
|
||||
"version": "0.6.15-fix.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.0.16",
|
||||
"deepmerge": "^1.5.2",
|
||||
"dom-to-image-more": "^3.1.6",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
@@ -518,11 +517,6 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-to-image-more": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-3.1.6.tgz",
|
||||
"integrity": "sha512-VMO0jNme32T06mWtkOC9QXfj+1npoJxkaTFW0DCwBLguwBKMjqwndiDANxDnbZ0kvNEecwxkv0Zmgdr96cGtAA=="
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz",
|
||||
@@ -2660,11 +2654,6 @@
|
||||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"dom-to-image-more": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-3.1.6.tgz",
|
||||
"integrity": "sha512-VMO0jNme32T06mWtkOC9QXfj+1npoJxkaTFW0DCwBLguwBKMjqwndiDANxDnbZ0kvNEecwxkv0Zmgdr96cGtAA=="
|
||||
},
|
||||
"dompurify": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.6.13",
|
||||
"version": "0.7.0",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
@@ -26,7 +26,6 @@
|
||||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.0.16",
|
||||
"deepmerge": "^1.5.2",
|
||||
"dom-to-image-more": "^3.1.6",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
|
||||
@@ -325,10 +325,49 @@ export const nodeDataNoStylePropList = [
|
||||
'uid',
|
||||
'activeStyle',
|
||||
'associativeLineTargets',
|
||||
'associativeLineTargetControlOffsets'
|
||||
'associativeLineTargetControlOffsets',
|
||||
'associativeLinePoint',
|
||||
'associativeLineText'
|
||||
]
|
||||
|
||||
// 数据缓存
|
||||
export const commonCaches = {
|
||||
measureCustomNodeContentSizeEl: null
|
||||
}
|
||||
measureCustomNodeContentSizeEl: null,
|
||||
measureRichtextNodeTextSizeEl: null
|
||||
}
|
||||
|
||||
// 错误类型
|
||||
export const ERROR_TYPES = {
|
||||
READ_CLIPBOARD_ERROR: 'read_clipboard_error',
|
||||
PARSE_PASTE_DATA_ERROR: 'parse_paste_data_error',
|
||||
CUSTOM_HANDLE_CLIPBOARD_TEXT_ERROR: 'custom_handle_clipboard_text_error',
|
||||
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;
|
||||
}
|
||||
`
|
||||
@@ -18,8 +18,6 @@ export const defaultOpt = {
|
||||
mouseScaleCenterUseMousePosition: true,
|
||||
// 最多显示几个标签
|
||||
maxTag: 5,
|
||||
// 导出图片时的内边距
|
||||
exportPadding: 20,
|
||||
// 展开收缩按钮尺寸
|
||||
expandBtnSize: 20,
|
||||
// 节点里图片和文字的间距
|
||||
@@ -70,18 +68,26 @@ 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,
|
||||
// 初始根节点的位置
|
||||
initRootNodePosition: null,
|
||||
// 导出png、svg、pdf时的图形内边距
|
||||
// 导出png、svg、pdf时的图形内边距,注意是单侧内边距
|
||||
exportPaddingX: 10,
|
||||
exportPaddingY: 10,
|
||||
// 节点文本编辑框的z-index
|
||||
@@ -127,12 +133,54 @@ export const defaultOpt = {
|
||||
customInnerElsAppendTo: null,
|
||||
// 拖拽元素时,指示元素新位置的块的最大高度
|
||||
nodeDragPlaceholderMaxSize: 20,
|
||||
// 是否允许创建一个隐藏的输入框,该输入框会在节点激活时聚焦,用于粘贴数据和自动进入文本编辑状态
|
||||
enableCreateHiddenInput: true,
|
||||
// 是否在存在一个激活节点时,当按下中文、英文、数字按键时自动进入文本编辑模式
|
||||
// 该配置在enableCreateHiddenInput设为true时生效
|
||||
enableAutoEnterTextEditWhenKeydown: true,
|
||||
// 开启该特性后,需要给你的输入框绑定keydown事件,并禁止冒泡
|
||||
enableAutoEnterTextEditWhenKeydown: false,
|
||||
// 设置富文本节点编辑框和节点大小一致,形成伪原地编辑的效果
|
||||
// 需要注意的是,只有当节点内只有文本、且形状是矩形才会有比较好的效果
|
||||
richTextEditFakeInPlace: false
|
||||
richTextEditFakeInPlace: false,
|
||||
// 自定义对剪贴板文本的处理。当按ctrl+v粘贴时会读取用户剪贴板中的文本和图片,默认只会判断文本是否是普通文本和simple-mind-map格式的节点数据,如果你想处理其他思维导图的数据,比如processon、zhixi等,那么可以传递一个函数,接受当前剪贴板中的文本为参数,返回处理后的数据,可以返回两种类型:
|
||||
/*
|
||||
1.返回一个纯文本,那么会直接以该文本创建一个子节点
|
||||
|
||||
2.返回一个节点对象,格式如下:
|
||||
{
|
||||
// 代表是simple-mind-map格式的数据
|
||||
simpleMindMap: true,
|
||||
// 节点数据,同simple-mind-map节点数据格式
|
||||
data: {
|
||||
data: {
|
||||
text: ''
|
||||
},
|
||||
children: []
|
||||
}
|
||||
}
|
||||
*/
|
||||
// 如果你的处理逻辑存在异步逻辑,也可以返回一个promise
|
||||
customHandleClipboardText: null,
|
||||
// 禁止鼠标滚轮缩放,你仍旧可以使用api进行缩放
|
||||
disableMouseWheelZoom: false,
|
||||
// 错误处理函数
|
||||
errorHandler: (code, error) => {
|
||||
console.error(code, error)
|
||||
},
|
||||
// 设置导出图片和svg时,针对富文本节点内容,也就是嵌入到svg中的html节点的默认样式覆盖
|
||||
// 如果不覆盖,会发生偏移问题
|
||||
resetCss: `
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`,
|
||||
// 开启鼠标双击复位思维导图位置及缩放
|
||||
enableDblclickReset: false,
|
||||
// 导出图片时canvas的缩放倍数,该配置会和window.devicePixelRatio值取最大值
|
||||
minExportImgCanvasScale: 2,
|
||||
// 节点鼠标hover和激活时显示的矩形边框的颜色
|
||||
hoverRectColor: 'rgb(94, 200, 248)',
|
||||
// 节点鼠标hover和激活时显示的矩形边框距节点内容的距离
|
||||
hoverRectPadding: 2,
|
||||
// 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中
|
||||
selectTextOnEnterEditText: false
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ import {
|
||||
simpleDeepClone,
|
||||
walk,
|
||||
bfsWalk,
|
||||
loadImage
|
||||
loadImage,
|
||||
isUndef
|
||||
} from '../../utils'
|
||||
import { shapeList } from './node/Shape'
|
||||
import { lineStyleProps } from '../../themes/default'
|
||||
import { CONSTANTS } from '../../constants/constant'
|
||||
import { CONSTANTS, ERROR_TYPES } from '../../constants/constant'
|
||||
|
||||
// 布局列表
|
||||
const layouts = {
|
||||
@@ -108,10 +109,6 @@ class Render {
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
})
|
||||
// 粘贴事件
|
||||
this.mindMap.on('paste', data => {
|
||||
this.onPaste(data)
|
||||
})
|
||||
// let timer = null
|
||||
// this.mindMap.on('view_data_change', () => {
|
||||
// clearTimeout(timer)
|
||||
@@ -273,8 +270,7 @@ class Render {
|
||||
this.copy = this.copy.bind(this)
|
||||
this.mindMap.keyCommand.addShortcut('Control+c', this.copy)
|
||||
this.mindMap.keyCommand.addShortcut('Control+v', () => {
|
||||
// 隐藏输入框可能会失去焦点,所以要重新聚焦
|
||||
this.textEdit.focusHiddenInput()
|
||||
this.onPaste()
|
||||
})
|
||||
this.cut = this.cut.bind(this)
|
||||
this.mindMap.keyCommand.addShortcut('Control+x', this.cut)
|
||||
@@ -346,7 +342,7 @@ class Render {
|
||||
}
|
||||
})
|
||||
})
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
}
|
||||
|
||||
// 清除当前激活的节点
|
||||
@@ -411,7 +407,7 @@ class Render {
|
||||
// 激活节点需要显示展开收起按钮
|
||||
node.showExpandBtn()
|
||||
setTimeout(() => {
|
||||
node.updateNodeShape()
|
||||
node.updateNodeActive()
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
@@ -420,6 +416,7 @@ class Render {
|
||||
0,
|
||||
0
|
||||
)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
}
|
||||
|
||||
// 回退
|
||||
@@ -449,7 +446,12 @@ class Render {
|
||||
}
|
||||
|
||||
// 插入同级节点,多个节点只会操作第一个节点
|
||||
insertNode(openEdit = true, appointNodes = [], appointData = null) {
|
||||
insertNode(
|
||||
openEdit = true,
|
||||
appointNodes = [],
|
||||
appointData = null,
|
||||
appointChildren = []
|
||||
) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
@@ -485,14 +487,19 @@ class Render {
|
||||
resetRichText: isRichText,
|
||||
...(appointData || {})
|
||||
},
|
||||
children: []
|
||||
children: [...appointChildren]
|
||||
})
|
||||
this.mindMap.render()
|
||||
}
|
||||
}
|
||||
|
||||
// 插入子节点
|
||||
insertChildNode(openEdit = true, appointNodes = [], appointData = null) {
|
||||
insertChildNode(
|
||||
openEdit = true,
|
||||
appointNodes = [],
|
||||
appointData = null,
|
||||
appointChildren = []
|
||||
) {
|
||||
appointNodes = this.formatAppointNodes(appointNodes)
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
@@ -523,7 +530,7 @@ class Render {
|
||||
resetRichText: isRichText,
|
||||
...(appointData || {})
|
||||
},
|
||||
children: []
|
||||
children: [...appointChildren]
|
||||
})
|
||||
// 插入子节点时自动展开子节点
|
||||
node.nodeData.data.expand = true
|
||||
@@ -591,15 +598,29 @@ class Render {
|
||||
// 复制节点
|
||||
copy() {
|
||||
this.beingCopyData = this.copyNode()
|
||||
this.setCoptyDataToClipboard(this.beingCopyData)
|
||||
}
|
||||
|
||||
// 剪切节点
|
||||
cut() {
|
||||
this.mindMap.execCommand('CUT_NODE', copyData => {
|
||||
this.beingCopyData = copyData
|
||||
this.setCoptyDataToClipboard(copyData)
|
||||
})
|
||||
}
|
||||
|
||||
// 将粘贴或剪切的数据设置到用户剪切板中
|
||||
setCoptyDataToClipboard(data) {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(
|
||||
JSON.stringify({
|
||||
simpleMindMap: true,
|
||||
data
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 粘贴节点
|
||||
paste() {
|
||||
if (this.beingCopyData) {
|
||||
@@ -608,7 +629,29 @@ class Render {
|
||||
}
|
||||
|
||||
// 粘贴事件
|
||||
async onPaste({ text, img }) {
|
||||
async onPaste() {
|
||||
const { errorHandler } = this.mindMap.opt
|
||||
// 读取剪贴板的文字和图片
|
||||
let text = null
|
||||
let img = null
|
||||
if (navigator.clipboard) {
|
||||
try {
|
||||
text = await navigator.clipboard.readText()
|
||||
const items = await navigator.clipboard.read()
|
||||
if (items && items.length > 0) {
|
||||
for (const clipboardItem of items) {
|
||||
for (const type of clipboardItem.types) {
|
||||
if (/^image\//.test(type)) {
|
||||
img = await clipboardItem.getType(type)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.READ_CLIPBOARD_ERROR, error)
|
||||
}
|
||||
}
|
||||
// 检查剪切板数据是否有变化
|
||||
// 通过图片大小来判断图片是否发生变化,可能是不准确的,但是目前没有其他好方法
|
||||
const imgSize = img ? img.size : 0
|
||||
@@ -626,9 +669,51 @@ class Render {
|
||||
if (this.currentBeingPasteType === CONSTANTS.PASTE_TYPE.CLIP_BOARD) {
|
||||
// 存在文本,则创建子节点
|
||||
if (text) {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
// 判断粘贴的是否是simple-mind-map的数据
|
||||
let smmData = null
|
||||
let useDefault = true
|
||||
// 用户自定义处理
|
||||
if (this.mindMap.opt.customHandleClipboardText) {
|
||||
try {
|
||||
const res = await this.mindMap.opt.customHandleClipboardText(text)
|
||||
if (!isUndef(res)) {
|
||||
useDefault = false
|
||||
if (typeof res === 'object' && res.simpleMindMap) {
|
||||
smmData = res.data
|
||||
} else {
|
||||
text = String(res)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.CUSTOM_HANDLE_CLIPBOARD_TEXT_ERROR, error)
|
||||
}
|
||||
}
|
||||
// 默认处理
|
||||
if (useDefault) {
|
||||
try {
|
||||
const parsedData = JSON.parse(text)
|
||||
if (parsedData && parsedData.simpleMindMap) {
|
||||
smmData = parsedData.data
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.PARSE_PASTE_DATA_ERROR, error)
|
||||
}
|
||||
}
|
||||
if (smmData) {
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_CHILD_NODE',
|
||||
false,
|
||||
[],
|
||||
{
|
||||
...smmData.data
|
||||
},
|
||||
[...smmData.children]
|
||||
)
|
||||
} else {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
}
|
||||
}
|
||||
// 存在图片,则添加到当前激活节点
|
||||
if (img) {
|
||||
@@ -645,7 +730,7 @@ class Render {
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
errorHandler(ERROR_TYPES.LOAD_CLIPBOARD_IMAGE_ERROR, error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -770,7 +855,7 @@ class Render {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
this.mindMap.emit('node_active', null, [...this.activeNodeList])
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
@@ -802,7 +887,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)
|
||||
@@ -817,7 +902,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) {
|
||||
@@ -837,19 +922,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) {
|
||||
@@ -870,18 +945,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)
|
||||
@@ -916,7 +981,7 @@ class Render {
|
||||
} else {
|
||||
node.hideExpandBtn()
|
||||
}
|
||||
node.updateNodeShape()
|
||||
node.updateNodeActive()
|
||||
}
|
||||
|
||||
// 设置节点是否展开
|
||||
@@ -1027,7 +1092,8 @@ class Render {
|
||||
}
|
||||
|
||||
// 设置节点图片
|
||||
setNodeImage(node, { url, title, width, height, custom = false }) {
|
||||
setNodeImage(node, data) {
|
||||
const { url, title, width, height, custom = false } = data || { url: '', title: '', width: 0, height: 0, custom: false }
|
||||
this.setNodeDataRender(node, {
|
||||
image: url,
|
||||
imageTitle: title || '',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getStrWithBrFromHtml, checkNodeOuter, isMobile } from '../../utils'
|
||||
import { getStrWithBrFromHtml, checkNodeOuter } from '../../utils'
|
||||
import { ERROR_TYPES } from '../../constants/constant'
|
||||
|
||||
// 节点文字编辑类
|
||||
export default class TextEdit {
|
||||
@@ -10,16 +11,11 @@ export default class TextEdit {
|
||||
this.currentNode = null
|
||||
// 文本编辑框
|
||||
this.textEditNode = null
|
||||
// 隐藏的文本输入框
|
||||
this.hiddenInputEl = null
|
||||
// 节点激活时默认聚焦到隐藏输入框
|
||||
this.enableFocus = true
|
||||
// 文本编辑框是否显示
|
||||
this.showTextEdit = false
|
||||
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
||||
this.cacheEditingText = ''
|
||||
this.bindEvent()
|
||||
this.createHiddenInput()
|
||||
}
|
||||
|
||||
// 事件
|
||||
@@ -51,10 +47,6 @@ export default class TextEdit {
|
||||
this.mindMap.on('before_node_active', () => {
|
||||
this.hideEditTextBox()
|
||||
})
|
||||
// 节点激活事件
|
||||
this.mindMap.on('node_active', () => {
|
||||
this.focusHiddenInput()
|
||||
})
|
||||
// 注册编辑快捷键
|
||||
this.mindMap.keyCommand.addShortcut('F2', () => {
|
||||
if (this.renderer.activeNodeList.length <= 0) {
|
||||
@@ -63,74 +55,29 @@ export default class TextEdit {
|
||||
this.show(this.renderer.activeNodeList[0])
|
||||
})
|
||||
this.mindMap.on('scale', this.onScale)
|
||||
}
|
||||
|
||||
// 创建一个隐藏的文本输入框
|
||||
createHiddenInput() {
|
||||
const { enableCreateHiddenInput, enableAutoEnterTextEditWhenKeydown } =
|
||||
this.mindMap.opt
|
||||
if (this.hiddenInputEl || isMobile() || !enableCreateHiddenInput) return
|
||||
this.hiddenInputEl = document.createElement('input')
|
||||
this.hiddenInputEl.type = 'text'
|
||||
this.hiddenInputEl.style.cssText = `
|
||||
position: fixed;
|
||||
left: -99999px;
|
||||
top: -99999px;
|
||||
`
|
||||
// 监听按键事件
|
||||
if (enableAutoEnterTextEditWhenKeydown) {
|
||||
this.hiddenInputEl.addEventListener('keydown', e => {
|
||||
// // 监听按键事件,判断是否自动进入文本编辑模式
|
||||
if (this.mindMap.opt.enableAutoEnterTextEditWhenKeydown) {
|
||||
window.addEventListener('keydown', e => {
|
||||
const activeNodeList = this.mindMap.renderer.activeNodeList
|
||||
if (activeNodeList.length <= 0 || activeNodeList.length > 1) return
|
||||
const node = activeNodeList[0]
|
||||
// 当正在输入中文或英文或数字时,如果没有按下组合键,那么自动进入文本编辑模式
|
||||
const keyCode = e.keyCode
|
||||
if (
|
||||
node &&
|
||||
(keyCode === 229 ||
|
||||
(keyCode >= 65 && keyCode <= 90) ||
|
||||
(keyCode >= 48 && keyCode <= 57)) &&
|
||||
!this.mindMap.keyCommand.hasCombinationKey(e)
|
||||
) {
|
||||
this.show(node)
|
||||
if (node && this.checkIsAutoEnterTextEditKey(e)) {
|
||||
this.show(node, e, false, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 监听粘贴事件
|
||||
this.hiddenInputEl.addEventListener('paste', async event => {
|
||||
event.preventDefault()
|
||||
const text = (event.clipboardData || window.clipboardData).getData('text')
|
||||
const files = event.clipboardData.files
|
||||
let img = null
|
||||
if (files.length > 0) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (/^image\//.test(files[i].type)) {
|
||||
img = files[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mindMap.emit('paste', {
|
||||
text,
|
||||
img
|
||||
})
|
||||
})
|
||||
document.body.appendChild(this.hiddenInputEl)
|
||||
}
|
||||
|
||||
// 让隐藏的文本输入框聚焦
|
||||
focusHiddenInput() {
|
||||
if (this.hiddenInputEl && this.enableFocus) this.hiddenInputEl.focus()
|
||||
}
|
||||
|
||||
// 关闭默认聚焦
|
||||
stopFocusOnNodeActive() {
|
||||
this.enableFocus = false
|
||||
}
|
||||
|
||||
// 开启默认聚焦
|
||||
openFocusOnNodeActive() {
|
||||
this.enableFocus = true
|
||||
// 判断是否是自动进入文本编模式的按钮
|
||||
checkIsAutoEnterTextEditKey(e) {
|
||||
const keyCode = e.keyCode
|
||||
return (
|
||||
(keyCode === 229 ||
|
||||
(keyCode >= 65 && keyCode <= 90) ||
|
||||
(keyCode >= 48 && keyCode <= 57)) &&
|
||||
!this.mindMap.keyCommand.hasCombinationKey(e)
|
||||
)
|
||||
}
|
||||
|
||||
// 注册临时快捷键
|
||||
@@ -146,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
|
||||
@@ -158,6 +106,7 @@ export default class TextEdit {
|
||||
isShow = await beforeTextEdit(node, isInserting)
|
||||
} catch (error) {
|
||||
isShow = false
|
||||
this.mindMap.opt.errorHandler(ERROR_TYPES.BEFORE_TEXT_EDIT_ERROR, error)
|
||||
}
|
||||
if (!isShow) return
|
||||
}
|
||||
@@ -166,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)
|
||||
}
|
||||
|
||||
// 处理画布缩放
|
||||
@@ -187,7 +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) {
|
||||
@@ -203,6 +155,11 @@ export default class TextEdit {
|
||||
this.textEditNode.addEventListener('mousedown', e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
this.textEditNode.addEventListener('keydown', e => {
|
||||
if (this.checkIsAutoEnterTextEditKey(e)) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
})
|
||||
const targetNode =
|
||||
this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.textEditNode)
|
||||
@@ -215,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
|
||||
@@ -234,7 +190,7 @@ export default class TextEdit {
|
||||
// if (!this.cacheEditingText) {
|
||||
// this.selectNodeText()
|
||||
// }
|
||||
if (isInserting) {
|
||||
if (isInserting || (selectTextOnEnterEditText && !isFromKeyDown)) {
|
||||
this.selectNodeText()
|
||||
} else {
|
||||
this.focus()
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import Style from './Style'
|
||||
import Shape from './Shape'
|
||||
import { G, Rect, 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'
|
||||
import nodeCreateContentsMethods from './nodeCreateContents'
|
||||
import nodeExpandBtnPlaceholderRectMethods from './nodeExpandBtnPlaceholderRect'
|
||||
import { CONSTANTS } from '../../../constants/constant'
|
||||
|
||||
// 节点类
|
||||
@@ -57,6 +58,7 @@ class Node {
|
||||
// 节点内容的容器
|
||||
this.group = null
|
||||
this.shapeNode = null // 节点形状节点
|
||||
this.hoverNode = null // 节点hover和激活的节点
|
||||
// 节点内容对象
|
||||
this._customNodeContent = null
|
||||
this._imgData = null
|
||||
@@ -107,6 +109,10 @@ class Node {
|
||||
Object.keys(nodeExpandBtnMethods).forEach(item => {
|
||||
this[item] = nodeExpandBtnMethods[item].bind(this)
|
||||
})
|
||||
// 展开收起按钮占位元素相关方法
|
||||
Object.keys(nodeExpandBtnPlaceholderRectMethods).forEach(item => {
|
||||
this[item] = nodeExpandBtnPlaceholderRectMethods[item].bind(this)
|
||||
})
|
||||
// 命令的相关方法
|
||||
Object.keys(nodeCommandWrapsMethods).forEach(item => {
|
||||
this[item] = nodeCommandWrapsMethods[item].bind(this)
|
||||
@@ -252,9 +258,11 @@ class Node {
|
||||
this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
|
||||
this.shapePadding.paddingX = shapePaddingX
|
||||
this.shapePadding.paddingY = shapePaddingY
|
||||
// 边框宽度,因为边框是以中线向两端发散,所以边框会超出节点
|
||||
const borderWidth = this.getBorderWidth()
|
||||
return {
|
||||
width: _width + paddingX * 2 + shapePaddingX * 2,
|
||||
height: _height + paddingY * 2 + margin + shapePaddingY * 2
|
||||
width: _width + paddingX * 2 + shapePaddingX * 2 + borderWidth,
|
||||
height: _height + paddingY * 2 + margin + shapePaddingY * 2 + borderWidth
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,27 +270,41 @@ class Node {
|
||||
layout() {
|
||||
// 清除之前的内容
|
||||
this.group.clear()
|
||||
const { hoverRectPadding } = this.mindMap.opt
|
||||
let { width, height, textContentItemMargin } = this
|
||||
let { paddingY } = this.getPaddingVale()
|
||||
paddingY += this.shapePadding.paddingY
|
||||
const halfBorderWidth = this.getBorderWidth() / 2
|
||||
paddingY += this.shapePadding.paddingY + halfBorderWidth
|
||||
// 节点形状
|
||||
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
|
||||
}
|
||||
// 图片节点
|
||||
@@ -356,27 +378,7 @@ class Node {
|
||||
: 0)
|
||||
)
|
||||
this.group.add(textContentNested)
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnPlaceholderRect() {
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
let { width, height } = this
|
||||
if (!this._unVisibleRectRegionNode) {
|
||||
this._unVisibleRectRegionNode = new Rect()
|
||||
this._unVisibleRectRegionNode.fill({
|
||||
color: 'transparent'
|
||||
})
|
||||
}
|
||||
this.group.add(this._unVisibleRectRegionNode)
|
||||
this.renderer.layout.renderExpandBtnRect(
|
||||
this._unVisibleRectRegionNode,
|
||||
this.expandBtnSize,
|
||||
width,
|
||||
height,
|
||||
this
|
||||
)
|
||||
}
|
||||
addHoverNode()
|
||||
}
|
||||
|
||||
// 给节点绑定事件
|
||||
@@ -392,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)
|
||||
@@ -415,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)
|
||||
@@ -447,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(!useLeftKeySelectionRightKeyDrag && this.mindMap.select.hasSelectRange()) {
|
||||
return
|
||||
}
|
||||
if (this.nodeData.data.isActive) {
|
||||
this.renderer.clearActive()
|
||||
}
|
||||
@@ -475,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) {
|
||||
// 需要移除展开收缩按钮
|
||||
@@ -555,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')
|
||||
}
|
||||
|
||||
// 递归渲染
|
||||
@@ -569,9 +583,7 @@ class Node {
|
||||
// 节点
|
||||
// 重新渲染连线
|
||||
this.renderLine()
|
||||
let isLayout = false
|
||||
if (!this.group) {
|
||||
isLayout = true
|
||||
// 创建组
|
||||
this.group = new G()
|
||||
this.group.addClass('smm-node')
|
||||
@@ -581,17 +593,14 @@ 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) {
|
||||
this.needLayout = false
|
||||
this.layout()
|
||||
}
|
||||
if (this.needRerenderExpandBtnPlaceholderRect) {
|
||||
this.needRerenderExpandBtnPlaceholderRect = false
|
||||
this.renderExpandBtnPlaceholderRect()
|
||||
}
|
||||
this.updateExpandBtnPlaceholderRect()
|
||||
this.update()
|
||||
}
|
||||
// 子节点
|
||||
@@ -818,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
|
||||
}
|
||||
|
||||
@@ -846,6 +855,11 @@ class Node {
|
||||
) // 父级
|
||||
}
|
||||
|
||||
// 获取节点非节点状态的边框大小
|
||||
getBorderWidth() {
|
||||
return this.style.merge('borderWidth', false) || 0
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
getData(key) {
|
||||
return key ? this.nodeData.data[key] || '' : this.nodeData.data
|
||||
|
||||
@@ -62,11 +62,10 @@ export default class Shape {
|
||||
// 创建形状节点
|
||||
createShape() {
|
||||
const shape = this.node.getShape()
|
||||
let { width, height } = this.node
|
||||
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()
|
||||
@@ -95,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
|
||||
@@ -120,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],
|
||||
@@ -131,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
|
||||
@@ -145,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],
|
||||
@@ -162,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],
|
||||
@@ -177,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],
|
||||
@@ -190,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(`
|
||||
@@ -203,7 +234,7 @@ export default class Shape {
|
||||
|
||||
// 创建圆
|
||||
createCircle() {
|
||||
let { width, height } = this.node
|
||||
let { width, height } = this.getNodeSize()
|
||||
let halfWidth = width / 2
|
||||
let halfHeight = height / 2
|
||||
return new Path().plot(`
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { tagColorList, nodeDataNoStylePropList } from '../../../constants/constant'
|
||||
import {
|
||||
tagColorList,
|
||||
nodeDataNoStylePropList
|
||||
} from '../../../constants/constant'
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
const backgroundStyleProps = ['backgroundColor', 'backgroundImage', 'backgroundRepeat', 'backgroundPosition', 'backgroundSize']
|
||||
const backgroundStyleProps = [
|
||||
'backgroundColor',
|
||||
'backgroundImage',
|
||||
'backgroundRepeat',
|
||||
'backgroundPosition',
|
||||
'backgroundSize'
|
||||
]
|
||||
|
||||
// 样式类
|
||||
class Style {
|
||||
@@ -10,12 +19,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 +45,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 +57,7 @@ class Style {
|
||||
}
|
||||
|
||||
// 合并样式
|
||||
merge(prop, root, isActive) {
|
||||
merge(prop, root) {
|
||||
let themeConfig = this.ctx.mindMap.themeConfig
|
||||
// 三级及以下节点
|
||||
let defaultConfig = themeConfig.node
|
||||
@@ -59,17 +74,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 +81,8 @@ class Style {
|
||||
}
|
||||
|
||||
// 获取某个样式值
|
||||
getStyle(prop, root, isActive) {
|
||||
return this.merge(prop, root, isActive)
|
||||
getStyle(prop, root) {
|
||||
return this.merge(prop, root)
|
||||
}
|
||||
|
||||
// 获取自身自定义样式
|
||||
@@ -142,10 +146,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 +205,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) => {
|
||||
Object.keys(this.ctx.nodeData.data).forEach(item => {
|
||||
if (!nodeDataNoStylePropList.includes(item)) {
|
||||
res = true
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// hover和激活节点
|
||||
hoverNode(node) {
|
||||
const { hoverRectColor } = this.ctx.mindMap.opt
|
||||
node
|
||||
.radius(5)
|
||||
.fill('none')
|
||||
.stroke({
|
||||
color: hoverRectColor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Style.cacheStyle = null
|
||||
|
||||
@@ -39,13 +39,13 @@ function setShape(shape) {
|
||||
}
|
||||
|
||||
// 修改某个样式
|
||||
function setStyle(prop, value, isActive) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value, isActive)
|
||||
function setStyle(prop, value) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value)
|
||||
}
|
||||
|
||||
// 修改多个样式
|
||||
function setStyles(style, isActive) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLES', this, style, isActive)
|
||||
function setStyles(style) {
|
||||
this.mindMap.execCommand('SET_NODE_STYLES', this, style)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { measureText, resizeImgSize, removeHtmlStyle, addHtmlStyle, checkIsRichText } from '../../../utils'
|
||||
import {
|
||||
measureText,
|
||||
resizeImgSize,
|
||||
removeHtmlStyle,
|
||||
addHtmlStyle,
|
||||
checkIsRichText
|
||||
} from '../../../utils'
|
||||
import { Image, SVG, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js'
|
||||
import iconsSvg from '../../../svg/icons'
|
||||
import { CONSTANTS, commonCaches } from '../../../constants/constant'
|
||||
@@ -54,7 +60,10 @@ function createIconNode() {
|
||||
}
|
||||
let iconSize = this.mindMap.themeConfig.iconSize
|
||||
return _data.icon.map(item => {
|
||||
let src = iconsSvg.getNodeIconListIcon(item, this.mindMap.opt.iconList || [])
|
||||
let src = iconsSvg.getNodeIconListIcon(
|
||||
item,
|
||||
this.mindMap.opt.iconList || []
|
||||
)
|
||||
let node = null
|
||||
// svg图标
|
||||
if (/^<svg/.test(src)) {
|
||||
@@ -77,6 +86,7 @@ function createIconNode() {
|
||||
|
||||
// 创建富文本节点
|
||||
function createRichTextNode() {
|
||||
const { textAutoWrapWidth } = this.mindMap.opt
|
||||
let g = new G()
|
||||
// 重新设置富文本节点内容
|
||||
let recoverText = false
|
||||
@@ -108,25 +118,34 @@ function createRichTextNode() {
|
||||
this.nodeData.data.text = text
|
||||
}
|
||||
let html = `<div>${this.nodeData.data.text}</div>`
|
||||
let div = document.createElement('div')
|
||||
if (!commonCaches.measureRichtextNodeTextSizeEl) {
|
||||
commonCaches.measureRichtextNodeTextSizeEl = document.createElement('div')
|
||||
commonCaches.measureRichtextNodeTextSizeEl.style.position = 'fixed'
|
||||
commonCaches.measureRichtextNodeTextSizeEl.style.left = '-999999px'
|
||||
this.mindMap.el.appendChild(commonCaches.measureRichtextNodeTextSizeEl)
|
||||
}
|
||||
let div = commonCaches.measureRichtextNodeTextSizeEl
|
||||
div.innerHTML = html
|
||||
div.style.cssText = `position: fixed; left: -999999px;`
|
||||
let el = div.children[0]
|
||||
el.classList.add('smm-richtext-node-wrap')
|
||||
el.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||
el.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
|
||||
this.mindMap.el.appendChild(div)
|
||||
el.style.maxWidth = textAutoWrapWidth + 'px'
|
||||
let { width, height } = el.getBoundingClientRect()
|
||||
width = Math.ceil(width) + 1// 修复getBoundingClientRect方法对实际宽度是小数的元素获取到的值是整数,导致宽度不够文本发生换行的问题
|
||||
// 如果文本为空,那么需要计算一个默认高度
|
||||
if (height <= 0) {
|
||||
div.innerHTML = '<p>abc123我和你</p>'
|
||||
let elTmp = div.children[0]
|
||||
elTmp.classList.add('smm-richtext-node-wrap')
|
||||
height = elTmp.getBoundingClientRect().height
|
||||
}
|
||||
width = Math.ceil(width) + 1 // 修复getBoundingClientRect方法对实际宽度是小数的元素获取到的值是整数,导致宽度不够文本发生换行的问题
|
||||
height = Math.ceil(height)
|
||||
g.attr('data-width', width)
|
||||
g.attr('data-height', height)
|
||||
html = div.innerHTML
|
||||
this.mindMap.el.removeChild(div)
|
||||
let foreignObject = new ForeignObject()
|
||||
foreignObject.width(width)
|
||||
foreignObject.height(height)
|
||||
foreignObject.add(SVG(html))
|
||||
foreignObject.add(div.children[0])
|
||||
g.add(foreignObject)
|
||||
return {
|
||||
node: g,
|
||||
@@ -141,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)
|
||||
@@ -274,9 +289,10 @@ function createNoteNode() {
|
||||
box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
|
||||
display: none;
|
||||
background-color: #fff;
|
||||
z-index: ${ this.mindMap.opt.nodeNoteTooltipZIndex }
|
||||
z-index: ${this.mindMap.opt.nodeNoteTooltipZIndex}
|
||||
`
|
||||
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
const targetNode =
|
||||
this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.noteEl)
|
||||
}
|
||||
this.noteEl.innerText = this.nodeData.data.note
|
||||
@@ -310,7 +326,7 @@ function createNoteNode() {
|
||||
}
|
||||
|
||||
// 测量自定义节点内容元素的宽高
|
||||
function measureCustomNodeContentSize (content) {
|
||||
function measureCustomNodeContentSize(content) {
|
||||
if (!commonCaches.measureCustomNodeContentSizeEl) {
|
||||
commonCaches.measureCustomNodeContentSizeEl = document.createElement('div')
|
||||
commonCaches.measureCustomNodeContentSizeEl.style.cssText = `
|
||||
@@ -330,19 +346,19 @@ function measureCustomNodeContentSize (content) {
|
||||
}
|
||||
|
||||
// 是否使用的是自定义节点内容
|
||||
function isUseCustomNodeContent() {
|
||||
function isUseCustomNodeContent() {
|
||||
return !!this._customNodeContent
|
||||
}
|
||||
|
||||
export default {
|
||||
createImgNode,
|
||||
getImgShowSize,
|
||||
createIconNode,
|
||||
createRichTextNode,
|
||||
createTextNode,
|
||||
createHyperlinkNode,
|
||||
createTagNode,
|
||||
createNoteNode,
|
||||
measureCustomNodeContentSize,
|
||||
isUseCustomNodeContent
|
||||
}
|
||||
createImgNode,
|
||||
getImgShowSize,
|
||||
createIconNode,
|
||||
createRichTextNode,
|
||||
createTextNode,
|
||||
createHyperlinkNode,
|
||||
createTagNode,
|
||||
createNoteNode,
|
||||
measureCustomNodeContentSize,
|
||||
isUseCustomNodeContent
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Rect } from '@svgdotjs/svg.js'
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
function renderExpandBtnPlaceholderRect() {
|
||||
// 根节点或没有子节点不需要渲染
|
||||
if (
|
||||
!this.nodeData.children ||
|
||||
this.nodeData.children.length <= 0 ||
|
||||
this.isRoot
|
||||
) {
|
||||
return
|
||||
}
|
||||
// 默认显示展开按钮的情况下也不需要渲染
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
let { width, height } = this
|
||||
if (!this._unVisibleRectRegionNode) {
|
||||
this._unVisibleRectRegionNode = new Rect()
|
||||
this._unVisibleRectRegionNode.fill({
|
||||
color: 'transparent'
|
||||
})
|
||||
}
|
||||
this.group.add(this._unVisibleRectRegionNode)
|
||||
this.renderer.layout.renderExpandBtnRect(
|
||||
this._unVisibleRectRegionNode,
|
||||
this.expandBtnSize,
|
||||
width,
|
||||
height,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除展开收起按钮的隐藏占位元素
|
||||
function clearExpandBtnPlaceholderRect() {
|
||||
if (!this._unVisibleRectRegionNode) {
|
||||
return
|
||||
}
|
||||
this._unVisibleRectRegionNode.remove()
|
||||
this._unVisibleRectRegionNode = null
|
||||
}
|
||||
|
||||
// 更新展开收起按钮的隐藏占位元素
|
||||
function updateExpandBtnPlaceholderRect() {
|
||||
// 布局改变需要重新渲染
|
||||
if (this.needRerenderExpandBtnPlaceholderRect) {
|
||||
this.needRerenderExpandBtnPlaceholderRect = false
|
||||
this.renderExpandBtnPlaceholderRect()
|
||||
}
|
||||
// 没有子节点到有子节点需要渲染
|
||||
if (this.nodeData.children && this.nodeData.children.length > 0) {
|
||||
if (!this._unVisibleRectRegionNode) {
|
||||
this.renderExpandBtnPlaceholderRect()
|
||||
}
|
||||
} else {
|
||||
// 有子节点到没子节点,需要删除
|
||||
if (this._unVisibleRectRegionNode) {
|
||||
this.clearExpandBtnPlaceholderRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
renderExpandBtnPlaceholderRect,
|
||||
clearExpandBtnPlaceholderRect,
|
||||
updateExpandBtnPlaceholderRect
|
||||
}
|
||||
@@ -32,6 +32,7 @@ class View {
|
||||
this.fit()
|
||||
})
|
||||
this.mindMap.svg.on('dblclick', () => {
|
||||
if (!this.mindMap.opt.enableDblclickReset) return
|
||||
this.reset()
|
||||
})
|
||||
// 拖动视图
|
||||
@@ -65,7 +66,8 @@ class View {
|
||||
mousewheelAction,
|
||||
mouseScaleCenterUseMousePosition,
|
||||
mousewheelMoveStep,
|
||||
mousewheelZoomActionReverse
|
||||
mousewheelZoomActionReverse,
|
||||
disableMouseWheelZoom
|
||||
} = this.mindMap.opt
|
||||
// 是否自定义鼠标滚轮事件
|
||||
if (
|
||||
@@ -76,8 +78,10 @@ class View {
|
||||
}
|
||||
// 鼠标滚轮事件控制缩放
|
||||
if (mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM) {
|
||||
let cx = mouseScaleCenterUseMousePosition ? e.clientX : undefined
|
||||
let cy = mouseScaleCenterUseMousePosition ? e.clientY : undefined
|
||||
if (disableMouseWheelZoom) return
|
||||
const { x: clientX, y: clientY } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
let cx = mouseScaleCenterUseMousePosition ? clientX : undefined
|
||||
let cy = mouseScaleCenterUseMousePosition ? clientY : undefined
|
||||
switch (dir) {
|
||||
// 鼠标滚轮,向上和向左,都是缩小
|
||||
case CONSTANTS.DIR.UP:
|
||||
|
||||
@@ -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()) {
|
||||
@@ -216,16 +237,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
|
||||
}
|
||||
|
||||
// 获取节点包括概要在内的宽度
|
||||
|
||||
@@ -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 ${
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,7 +28,7 @@ const handleList = node => {
|
||||
}
|
||||
|
||||
// 将markdown转换成节点树
|
||||
export const transformMarkdownTo = async md => {
|
||||
export const transformMarkdownTo = md => {
|
||||
const tree = fromMarkdown(md)
|
||||
let root = {
|
||||
children: []
|
||||
|
||||
@@ -14,11 +14,13 @@ const parseXmindFile = file => {
|
||||
async zip => {
|
||||
try {
|
||||
let content = ''
|
||||
if (zip.files['content.json']) {
|
||||
let json = await zip.files['content.json'].async('string')
|
||||
let jsonFile = zip.files['content.json']
|
||||
let xmlFile = zip.files['content.xml'] || zip.files['/content.xml']
|
||||
if (jsonFile) {
|
||||
let json = await jsonFile.async('string')
|
||||
content = await transformXmind(json, zip.files)
|
||||
} else if (zip.files['content.xml']) {
|
||||
let xml = await zip.files['content.xml'].async('string')
|
||||
} else if (xmlFile) {
|
||||
let xml = await xmlFile.async('string')
|
||||
let json = xmlConvert.xml2json(xml)
|
||||
content = transformOldXmind(json)
|
||||
}
|
||||
@@ -64,13 +66,13 @@ const transformXmind = async (content, files) => {
|
||||
}
|
||||
// 图片
|
||||
if (node.image && /\.(jpg|jpeg|png|gif|webp)$/.test(node.image.src)) {
|
||||
// 处理异步逻辑
|
||||
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 imageType = /\.([^.]+)$/.exec(node.image.src)[1]
|
||||
let imageBase64 =
|
||||
@@ -187,7 +189,7 @@ const transformOldXmind = content => {
|
||||
if (_children && _children.elements && _children.elements.length > 0) {
|
||||
_children.elements.forEach(item => {
|
||||
if (item.name === 'topics') {
|
||||
item.elements.forEach(item2 => {
|
||||
(item.elements || []).forEach(item2 => {
|
||||
let newChild = {}
|
||||
newNode.children.push(newChild)
|
||||
walk(item2, newChild)
|
||||
|
||||
@@ -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
|
||||
@@ -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) => {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { imgToDataUrl, downloadFile, readBlob, removeHTMLEntities } from '../utils'
|
||||
import {
|
||||
imgToDataUrl,
|
||||
downloadFile,
|
||||
readBlob,
|
||||
removeHTMLEntities
|
||||
} from '../utils'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas'
|
||||
import { transformToMarkdown } from '../parse/toMarkdown'
|
||||
import { a4Size } from '../constants/constant'
|
||||
|
||||
// 导出插件
|
||||
class Export {
|
||||
// 构造函数
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.exportPadding = this.mindMap.opt.exportPadding
|
||||
}
|
||||
|
||||
// 导出
|
||||
@@ -35,6 +40,10 @@ class Export {
|
||||
let imageList = svg.find('image')
|
||||
let task = imageList.map(async item => {
|
||||
let imgUlr = item.attr('href') || item.attr('xlink:href')
|
||||
// 已经是data:URL形式不用转换
|
||||
if (/^data:/.test(imgUlr)) {
|
||||
return
|
||||
}
|
||||
let imgData = await imgToDataUrl(imgUlr)
|
||||
item.attr('href', imgData)
|
||||
})
|
||||
@@ -49,33 +58,42 @@ class Export {
|
||||
}
|
||||
|
||||
// svg转png
|
||||
svgToPng(svgSrc, transparent) {
|
||||
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')
|
||||
canvas.width = img.width + this.exportPadding * 2
|
||||
canvas.height = img.height + this.exportPadding * 2
|
||||
let ctx = canvas.getContext('2d')
|
||||
const canvas = document.createElement('canvas')
|
||||
const dpr = Math.max(window.devicePixelRatio, this.mindMap.opt.minExportImgCanvasScale)
|
||||
const imgWidth = img.width
|
||||
const imgHeight = img.height
|
||||
// 如果宽比高长,那么旋转90度
|
||||
const needRotate = checkRotate(imgWidth, imgHeight)
|
||||
if (needRotate) {
|
||||
canvas.width = imgHeight * dpr
|
||||
canvas.height = imgWidth * dpr
|
||||
canvas.style.width = imgHeight + 'px'
|
||||
canvas.style.height = imgWidth + 'px'
|
||||
} else {
|
||||
canvas.width = imgWidth * dpr
|
||||
canvas.height = imgHeight * dpr
|
||||
canvas.style.width = imgWidth + 'px'
|
||||
canvas.style.height = imgHeight + 'px'
|
||||
}
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.scale(dpr, dpr)
|
||||
if (needRotate) {
|
||||
ctx.rotate(0.5 * Math.PI)
|
||||
ctx.translate(0, -imgHeight)
|
||||
}
|
||||
// 绘制背景
|
||||
if (!transparent) {
|
||||
await this.drawBackgroundToCanvas(ctx, canvas.width, canvas.height)
|
||||
await this.drawBackgroundToCanvas(ctx, imgWidth, imgHeight)
|
||||
}
|
||||
// 图片绘制到canvas里
|
||||
ctx.drawImage(
|
||||
img,
|
||||
0,
|
||||
0,
|
||||
img.width,
|
||||
img.height,
|
||||
this.exportPadding,
|
||||
this.exportPadding,
|
||||
img.width,
|
||||
img.height
|
||||
)
|
||||
ctx.drawImage(img, 0, 0, imgWidth, imgHeight)
|
||||
resolve(canvas.toDataURL())
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
@@ -96,7 +114,7 @@ class Export {
|
||||
backgroundImage,
|
||||
backgroundRepeat = 'no-repeat',
|
||||
backgroundPosition = 'center center',
|
||||
backgroundSize = 'cover',
|
||||
backgroundSize = 'cover'
|
||||
} = this.mindMap.themeConfig
|
||||
// 背景颜色
|
||||
ctx.save()
|
||||
@@ -107,18 +125,25 @@ class Export {
|
||||
// 背景图片
|
||||
if (backgroundImage && backgroundImage !== 'none') {
|
||||
ctx.save()
|
||||
drawBackgroundImageToCanvas(ctx, width, height, backgroundImage, {
|
||||
backgroundRepeat,
|
||||
backgroundPosition,
|
||||
backgroundSize
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
drawBackgroundImageToCanvas(
|
||||
ctx,
|
||||
width,
|
||||
height,
|
||||
backgroundImage,
|
||||
{
|
||||
backgroundRepeat,
|
||||
backgroundPosition,
|
||||
backgroundSize
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
ctx.restore()
|
||||
}
|
||||
ctx.restore()
|
||||
})
|
||||
)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
@@ -152,14 +177,25 @@ class Export {
|
||||
* 方法1.把svg的图片都转化成data:url格式,再转换
|
||||
* 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
|
||||
*/
|
||||
async png(name, transparent = false) {
|
||||
async png(name, transparent = false, checkRotate) {
|
||||
let { node, str } = await this.getSvgData()
|
||||
str = removeHTMLEntities(str)
|
||||
// 如果开启了富文本,则使用htmltocanvas转换为图片
|
||||
if (this.mindMap.richText) {
|
||||
let res = await this.mindMap.richText.handleExportPng(node.node)
|
||||
let imgDataUrl = await this.svgToPng(res, transparent)
|
||||
return imgDataUrl
|
||||
// 覆盖html默认的样式
|
||||
let foreignObjectList = node.find('foreignObject')
|
||||
if (foreignObjectList.length > 0) {
|
||||
foreignObjectList[0].add(SVG(`<style>${ this.mindMap.opt.resetCss }</style>`))
|
||||
}
|
||||
str = node.svg()
|
||||
// 使用其他库(html2canvas、dom-to-image-more等)来完成导出
|
||||
// let res = await this.mindMap.richText.handleExportPng(node.node)
|
||||
// let imgDataUrl = await this.svgToPng(
|
||||
// res,
|
||||
// transparent,
|
||||
// checkRotate
|
||||
// )
|
||||
// return imgDataUrl
|
||||
}
|
||||
// 转换成blob数据
|
||||
let blob = new Blob([str], {
|
||||
@@ -168,17 +204,24 @@ class Export {
|
||||
// 转换成data:url数据
|
||||
let svgUrl = await readBlob(blob)
|
||||
// 绘制到canvas上
|
||||
let res = await this.svgToPng(svgUrl, transparent)
|
||||
let res = await this.svgToPng(
|
||||
svgUrl,
|
||||
transparent,
|
||||
checkRotate
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
||||
// 导出为pdf
|
||||
async pdf(name) {
|
||||
async pdf(name, useMultiPageExport) {
|
||||
if (!this.mindMap.doExportPDF) {
|
||||
throw new Error('请注册ExportPDF插件')
|
||||
}
|
||||
let img = await this.png()
|
||||
this.mindMap.doExportPDF.pdf(name, img)
|
||||
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)
|
||||
}
|
||||
|
||||
// 导出为xmind
|
||||
@@ -194,15 +237,13 @@ class Export {
|
||||
|
||||
// 导出为svg
|
||||
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
|
||||
async svg(name, plusCssText) {
|
||||
async svg(name) {
|
||||
let { node } = await this.getSvgData()
|
||||
// 开启了节点富文本编辑
|
||||
if (this.mindMap.richText) {
|
||||
if (plusCssText) {
|
||||
let foreignObjectList = node.find('foreignObject')
|
||||
if (foreignObjectList.length > 0) {
|
||||
foreignObjectList[0].add(SVG(`<style>${plusCssText}</style>`))
|
||||
}
|
||||
let foreignObjectList = node.find('foreignObject')
|
||||
if (foreignObjectList.length > 0) {
|
||||
foreignObjectList[0].add(SVG(`<style>${ this.mindMap.opt.resetCss }</style>`))
|
||||
}
|
||||
}
|
||||
node.first().before(SVG(`<title>${name}</title>`))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import JsPDF from 'jspdf'
|
||||
import { a4Size } from '../constants/constant'
|
||||
|
||||
// 导出PDF插件,需要通过Export插件使用
|
||||
class ExportPDF {
|
||||
@@ -8,31 +9,82 @@ class ExportPDF {
|
||||
}
|
||||
|
||||
// 导出为pdf
|
||||
pdf(name, img) {
|
||||
pdf(name, img, useMultiPageExport = false) {
|
||||
if (useMultiPageExport) {
|
||||
this.multiPageExport(name, img)
|
||||
} else {
|
||||
this.onePageExport(name, img)
|
||||
}
|
||||
}
|
||||
|
||||
// 单页导出
|
||||
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', (a4Size.width - w) / 2, (a4Size.height - h) / 2, w, h)
|
||||
pdf.save(name)
|
||||
}
|
||||
image.src = img
|
||||
}
|
||||
|
||||
// 多页导出
|
||||
multiPageExport(name, img) {
|
||||
let image = new Image()
|
||||
image.onload = () => {
|
||||
let imageWidth = image.width
|
||||
let imageHeight = image.height
|
||||
// 一页pdf显示高度
|
||||
let pageHeight = (imageWidth / a4Size.width) * a4Size.height
|
||||
// 未生成pdf的高度
|
||||
let leftHeight = imageHeight
|
||||
// 偏移
|
||||
let position = 0
|
||||
// a4纸的尺寸[595.28,841.89],图片在pdf中图片的宽高
|
||||
let imgWidth = a4Size.width
|
||||
let imgHeight = (a4Size.width / imageWidth) * imageHeight
|
||||
let pdf = new JsPDF('', 'pt', 'a4')
|
||||
// 有两个高度需要区分,一个是图片的实际高度,和生成pdf的页面高度(841.89)
|
||||
// 当内容未超过pdf一页显示的范围,无需分页
|
||||
if (leftHeight < pageHeight) {
|
||||
pdf.addImage(
|
||||
img,
|
||||
'PNG',
|
||||
(a4Size.width - imgWidth) / 2,
|
||||
(a4Size.height - imgHeight) / 2,
|
||||
imgWidth,
|
||||
imgHeight
|
||||
)
|
||||
} else {
|
||||
// 分页
|
||||
while (leftHeight > 0) {
|
||||
pdf.addImage(img, 'PNG', 0, position, imgWidth, imgHeight)
|
||||
leftHeight -= pageHeight
|
||||
position -= a4Size.height
|
||||
// 避免添加空白页
|
||||
if (leftHeight > 0) {
|
||||
pdf.addPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
pdf.addImage(img, 'PNG', (a4Width - w) / 2, (a4Height - h) / 2, w, h)
|
||||
pdf.save(name)
|
||||
}
|
||||
image.src = img
|
||||
|
||||
@@ -263,6 +263,11 @@ class NodeImgAdjust {
|
||||
beforePluginRemove() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
NodeImgAdjust.instanceName = 'nodeImgAdjust'
|
||||
|
||||
@@ -69,6 +69,11 @@ class Painter {
|
||||
beforePluginRemove() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
Painter.instanceName = 'painter'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Quill from 'quill'
|
||||
import Delta from 'quill-delta'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import domtoimage from 'dom-to-image-more'
|
||||
import {
|
||||
walk,
|
||||
getTextFromHtml,
|
||||
@@ -43,6 +43,7 @@ class RichText {
|
||||
this.quill = null
|
||||
this.range = null
|
||||
this.lastRange = null
|
||||
this.pasteUseRange = null
|
||||
this.node = null
|
||||
this.isInserting = false
|
||||
this.styleEl = null
|
||||
@@ -151,7 +152,7 @@ class RichText {
|
||||
}
|
||||
|
||||
// 显示文本编辑控件
|
||||
showEditText(node, rect, isInserting) {
|
||||
showEditText(node, rect, isInserting, isFromKeyDown) {
|
||||
if (this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
@@ -159,7 +160,8 @@ class RichText {
|
||||
richTextEditFakeInPlace,
|
||||
customInnerElsAppendTo,
|
||||
nodeTextEditZIndex,
|
||||
textAutoWrapWidth
|
||||
textAutoWrapWidth,
|
||||
selectTextOnEnterEditText
|
||||
} = this.mindMap.opt
|
||||
this.node = node
|
||||
this.isInserting = isInserting
|
||||
@@ -198,6 +200,11 @@ class RichText {
|
||||
this.textEditNode.addEventListener('mousedown', e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
this.textEditNode.addEventListener('keydown', e => {
|
||||
if (this.mindMap.renderer.textEdit.checkIsAutoEnterTextEditKey(e)) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
})
|
||||
const targetNode = customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.textEditNode)
|
||||
}
|
||||
@@ -240,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)
|
||||
@@ -324,6 +332,7 @@ class RichText {
|
||||
this.lastRange = this.range
|
||||
this.range = null
|
||||
if (range) {
|
||||
this.pasteUseRange = range
|
||||
let bounds = this.quill.getBounds(range.index, range.length)
|
||||
let rect = this.textEditNode.getBoundingClientRect()
|
||||
let rectInfo = {
|
||||
@@ -363,6 +372,38 @@ class RichText {
|
||||
this.lostStyle = false
|
||||
}
|
||||
})
|
||||
// 拦截粘贴,只允许粘贴纯文本
|
||||
this.quill.clipboard.addMatcher(Node.TEXT_NODE, (node) => {
|
||||
let style = this.getPasteTextStyle()
|
||||
return new Delta().insert(node.data, style)
|
||||
})
|
||||
this.quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
|
||||
let ops = []
|
||||
let style = this.getPasteTextStyle()
|
||||
delta.ops.forEach(op => {
|
||||
// 过滤出文本内容,过滤掉换行
|
||||
if (op.insert && typeof op.insert === 'string' && op.insert !== '\n') {
|
||||
ops.push({
|
||||
attributes: { ...style },
|
||||
insert: op.insert
|
||||
})
|
||||
}
|
||||
})
|
||||
delta.ops = ops
|
||||
return delta
|
||||
})
|
||||
}
|
||||
|
||||
// 获取粘贴的文本的样式
|
||||
getPasteTextStyle() {
|
||||
// 粘贴的数据使用当前光标位置处的文本样式
|
||||
if (this.pasteUseRange) {
|
||||
return this.quill.getFormat(
|
||||
this.pasteUseRange.index,
|
||||
this.pasteUseRange.length
|
||||
)
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
// 正则输入中文
|
||||
@@ -592,6 +633,11 @@ class RichText {
|
||||
this.transformAllNodesToNormalNode()
|
||||
document.head.removeChild(this.styleEl)
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
document.head.removeChild(this.styleEl)
|
||||
}
|
||||
}
|
||||
|
||||
RichText.instanceName = 'richText'
|
||||
|
||||
180
simple-mind-map/src/plugins/Scrollbar.js
Normal file
180
simple-mind-map/src/plugins/Scrollbar.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import { throttle } from '../utils/index'
|
||||
|
||||
// 滚动条插件
|
||||
class Scrollbar {
|
||||
// 构造函数
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.scrollbarWrapSize = {
|
||||
width: 0, // 水平滚动条的容器宽度
|
||||
height: 0 // 垂直滚动条的容器高度
|
||||
}
|
||||
this.reset()
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
this.onMousemove = this.onMousemove.bind(this)
|
||||
this.onMouseup = this.onMouseup.bind(this)
|
||||
this.onNodeTreeRenderEnd = this.onNodeTreeRenderEnd.bind(this)
|
||||
this.onViewDataChange = throttle(this.onViewDataChange, 16, this) // 加个节流
|
||||
this.mindMap.on('mousemove', this.onMousemove)
|
||||
this.mindMap.on('mouseup', this.onMouseup)
|
||||
this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd)
|
||||
this.mindMap.on('view_data_change', this.onViewDataChange)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
unBindEvent() {
|
||||
this.mindMap.off('mousemove', this.onMousemove)
|
||||
this.mindMap.off('mouseup', this.onMouseup)
|
||||
this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd)
|
||||
this.mindMap.off('view_data_change', this.onViewDataChange)
|
||||
}
|
||||
|
||||
// 每次渲染后需要更新滚动条
|
||||
onNodeTreeRenderEnd() {
|
||||
this.emitEvent()
|
||||
}
|
||||
|
||||
// 思维导图视图数据改变需要更新滚动条
|
||||
onViewDataChange() {
|
||||
this.emitEvent()
|
||||
}
|
||||
|
||||
// 发送滚动条改变事件
|
||||
emitEvent() {
|
||||
this.mindMap.emit('scrollbar_change')
|
||||
}
|
||||
|
||||
// 复位数据
|
||||
reset() {
|
||||
// 当前拖拽的滚动条类型
|
||||
this.currentScrollType = ''
|
||||
this.isMousedown = false
|
||||
this.mousedownPos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
this.startViewPos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
// 思维导图实际高度
|
||||
this.chartHeight = 0
|
||||
this.chartWidth = 0
|
||||
}
|
||||
|
||||
// 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度
|
||||
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.x2 -= elRect.left
|
||||
rect.y -= elRect.top
|
||||
rect.y2 -= 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()
|
||||
this.currentScrollType = type
|
||||
this.isMousedown = true
|
||||
this.mousedownPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
}
|
||||
// 保存视图当前的偏移量
|
||||
let transformData = this.mindMap.view.getTransformData()
|
||||
this.startViewPos = {
|
||||
x: transformData.state.x,
|
||||
y: transformData.state.y
|
||||
}
|
||||
}
|
||||
|
||||
onMousemove(e) {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
if (this.currentScrollType === 'vertical') {
|
||||
const oy = e.clientY - this.mousedownPos.y
|
||||
const oyPercentage = -oy / this.scrollbarWrapSize.height
|
||||
const oyPx = oyPercentage * this.chartHeight
|
||||
// 在视图最初偏移量上累加更新量
|
||||
this.mindMap.view.translateYTo(oyPx + this.startViewPos.y)
|
||||
} else {
|
||||
const ox = e.clientX - this.mousedownPos.x
|
||||
const oxPercentage = -ox / this.scrollbarWrapSize.width
|
||||
const oxPx = oxPercentage * this.chartWidth
|
||||
// 在视图最初偏移量上累加更新量
|
||||
this.mindMap.view.translateXTo(oxPx + this.startViewPos.x)
|
||||
}
|
||||
}
|
||||
|
||||
onMouseup() {
|
||||
this.isMousedown = false
|
||||
this.reset()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
Scrollbar.instanceName = 'scrollbar'
|
||||
|
||||
export default Scrollbar
|
||||
@@ -11,6 +11,8 @@ class Select {
|
||||
this.mouseDownY = 0
|
||||
this.mouseMoveX = 0
|
||||
this.mouseMoveY = 0
|
||||
this.isSelecting = false
|
||||
this.cacheActiveList = []
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -30,6 +32,7 @@ class Select {
|
||||
}
|
||||
e.preventDefault()
|
||||
this.isMousedown = true
|
||||
this.cacheActiveList = [...this.mindMap.renderer.activeNodeList]
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
this.mouseDownX = x
|
||||
this.mouseDownY = y
|
||||
@@ -61,20 +64,45 @@ class Select {
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
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
|
||||
[...this.mindMap.renderer.activeNodeList]
|
||||
)
|
||||
clearTimeout(this.autoMoveTimer)
|
||||
this.isMousedown = false
|
||||
if (this.rect) this.rect.remove()
|
||||
this.rect = null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
onMove(x, y) {
|
||||
this.isSelecting = true
|
||||
// 绘制矩形
|
||||
this.rect.plot([
|
||||
[this.mouseDownX, this.mouseDownY],
|
||||
@@ -172,6 +200,11 @@ class Select {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 是否存在选区
|
||||
hasSelectRange() {
|
||||
return this.isSelecting
|
||||
}
|
||||
}
|
||||
|
||||
Select.instanceName = 'select'
|
||||
|
||||
@@ -78,7 +78,7 @@ class TouchEvent {
|
||||
return
|
||||
}
|
||||
const viewBefore = this.touchStartScaleView
|
||||
const scale = viewBefore.scale * (distance / viewBefore.distance)
|
||||
let scale = viewBefore.scale * (distance / viewBefore.distance)
|
||||
if (Math.abs(distance - viewBefore.distance) <= 10) {
|
||||
scale = viewBefore.scale
|
||||
}
|
||||
@@ -148,6 +148,11 @@ class TouchEvent {
|
||||
beforePluginRemove() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
TouchEvent.instanceName = 'touchEvent'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getAssociativeLineTargetIndex,
|
||||
joinCubicBezierPath,
|
||||
computeNodePoints,
|
||||
getNodePoint,
|
||||
getDefaultControlPointOffsets
|
||||
} from './associativeLineUtils'
|
||||
|
||||
@@ -61,15 +61,22 @@ function onControlPointMousemove(e) {
|
||||
}
|
||||
// 更新当前拖拽的控制点的位置
|
||||
this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
|
||||
let [path, clickPath, text, node, toNode] = this.activeLine
|
||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||
let [, , , node, toNode] = this.activeLine
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
let { associativeLinePoint, associativeLineTargetControlOffsets } =
|
||||
node.nodeData.data
|
||||
associativeLinePoint = associativeLinePoint || []
|
||||
const nodePos = this.getNodePos(node)
|
||||
const toNodePos = this.getNodePos(toNode)
|
||||
let [startPoint, endPoint] = this.updateAllLinesPos(
|
||||
node,
|
||||
toNode,
|
||||
associativeLinePoint[targetIndex]
|
||||
)
|
||||
this.controlPointMousemoveState.startPoint = startPoint
|
||||
this.controlPointMousemoveState.endPoint = endPoint
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
this.controlPointMousemoveState.targetIndex = targetIndex
|
||||
let offsets = []
|
||||
let associativeLineTargetControlOffsets =
|
||||
node.nodeData.data.associativeLineTargetControlOffsets
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsets = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||
@@ -78,8 +85,14 @@ function onControlPointMousemove(e) {
|
||||
}
|
||||
let point1 = null
|
||||
let point2 = null
|
||||
const { x: clientX, y: clientY } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
const _e = {
|
||||
clientX,
|
||||
clientY
|
||||
}
|
||||
// 拖拽的是控制点1
|
||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||
startPoint = getNodePoint(nodePos, '', 0, _e)
|
||||
point1 = {
|
||||
x,
|
||||
y
|
||||
@@ -88,10 +101,15 @@ function onControlPointMousemove(e) {
|
||||
x: endPoint.x + offsets[1].x,
|
||||
y: endPoint.y + offsets[1].y
|
||||
}
|
||||
// 更新控制点1的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
if (startPoint) {
|
||||
// 保存更新后的坐标
|
||||
this.controlPointMousemoveState.startPoint = startPoint
|
||||
// 更新控制点1的连线
|
||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||
}
|
||||
} else {
|
||||
// 拖拽的是控制点2
|
||||
endPoint = getNodePoint(toNodePos, '', 0, _e)
|
||||
point1 = {
|
||||
x: startPoint.x + offsets[0].x,
|
||||
y: startPoint.y + offsets[0].y
|
||||
@@ -100,18 +118,39 @@ function onControlPointMousemove(e) {
|
||||
x,
|
||||
y
|
||||
}
|
||||
// 更新控制点2的连线
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
if (endPoint) {
|
||||
// 保存更新后结束节点的坐标
|
||||
this.controlPointMousemoveState.endPoint = endPoint
|
||||
// 更新控制点2的连线
|
||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||
}
|
||||
}
|
||||
this.updataAassociativeLine(
|
||||
startPoint,
|
||||
endPoint,
|
||||
point1,
|
||||
point2,
|
||||
this.activeLine
|
||||
)
|
||||
}
|
||||
|
||||
function updataAassociativeLine(
|
||||
startPoint,
|
||||
endPoint,
|
||||
point1,
|
||||
point2,
|
||||
activeLine
|
||||
) {
|
||||
const [path, clickPath, text] = activeLine
|
||||
// 更新关联线
|
||||
let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||
const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||
path.plot(pathStr)
|
||||
clickPath.plot(pathStr)
|
||||
this.updateTextPos(path, text)
|
||||
this.updateTextEditBoxPos(text)
|
||||
}
|
||||
|
||||
// 控制点的鼠标移动事件
|
||||
// 控制点的鼠标松开事件
|
||||
function onControlPointMouseup(e) {
|
||||
if (!this.isControlPointMousedown) return
|
||||
e.stopPropagation()
|
||||
@@ -120,8 +159,15 @@ function onControlPointMouseup(e) {
|
||||
this.controlPointMousemoveState
|
||||
let [, , , node] = this.activeLine
|
||||
let offsetList = []
|
||||
let associativeLineTargetControlOffsets =
|
||||
node.nodeData.data.associativeLineTargetControlOffsets
|
||||
let { associativeLinePoint, associativeLineTargetControlOffsets } =
|
||||
node.nodeData.data
|
||||
if (!associativeLinePoint) {
|
||||
associativeLinePoint = []
|
||||
}
|
||||
associativeLinePoint[targetIndex] = associativeLinePoint[targetIndex] || {
|
||||
startPoint,
|
||||
endPoint
|
||||
}
|
||||
if (!associativeLineTargetControlOffsets) {
|
||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||
offsetList[targetIndex] = getDefaultControlPointOffsets(
|
||||
@@ -140,6 +186,7 @@ function onControlPointMouseup(e) {
|
||||
y: pos.y - startPoint.y
|
||||
}
|
||||
offset2 = offsetList[targetIndex][1]
|
||||
associativeLinePoint[targetIndex].startPoint = startPoint
|
||||
} else {
|
||||
// 更新控制点2数据
|
||||
offset1 = offsetList[targetIndex][0]
|
||||
@@ -147,10 +194,12 @@ function onControlPointMouseup(e) {
|
||||
x: pos.x - endPoint.x,
|
||||
y: pos.y - endPoint.y
|
||||
}
|
||||
associativeLinePoint[targetIndex].endPoint = endPoint
|
||||
}
|
||||
offsetList[targetIndex] = [offset1, offset2]
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
associativeLineTargetControlOffsets: offsetList
|
||||
associativeLineTargetControlOffsets: offsetList,
|
||||
associativeLinePoint
|
||||
})
|
||||
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
||||
setTimeout(() => {
|
||||
@@ -237,5 +286,6 @@ export default {
|
||||
renderControls,
|
||||
removeControls,
|
||||
hideControls,
|
||||
showControls
|
||||
showControls,
|
||||
updataAassociativeLine
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ function showEditTextBox(g) {
|
||||
this.mindMap.keyCommand.addShortcut('Enter', () => {
|
||||
this.hideEditTextBox()
|
||||
})
|
||||
|
||||
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||
@@ -62,7 +62,8 @@ function showEditTextBox(g) {
|
||||
).split(/\n/gim)
|
||||
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
|
||||
this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px'
|
||||
this.textEditNode.style.lineHeight = textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
|
||||
this.textEditNode.style.lineHeight =
|
||||
textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
|
||||
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
this.textEditNode.style.display = 'block'
|
||||
@@ -78,10 +79,12 @@ function onScale() {
|
||||
// 更新文本编辑框位置
|
||||
function updateTextEditBoxPos(g) {
|
||||
let rect = g.node.getBoundingClientRect()
|
||||
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
||||
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
if (this.textEditNode) {
|
||||
this.textEditNode.style.minWidth = `${rect.width + 10}px`
|
||||
this.textEditNode.style.minHeight = `${rect.height + 6}px`
|
||||
this.textEditNode.style.left = `${rect.left}px`
|
||||
this.textEditNode.style.top = `${rect.top}px`
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏文本编辑框
|
||||
@@ -116,8 +119,10 @@ function getText(node, toNode) {
|
||||
// 渲染关联线文字
|
||||
function renderText(str, path, text) {
|
||||
if (!str) return
|
||||
let { associativeLineTextFontSize, associativeLineTextLineHeight } =
|
||||
this.mindMap.themeConfig
|
||||
let {
|
||||
associativeLineTextFontSize,
|
||||
associativeLineTextLineHeight
|
||||
} = this.mindMap.themeConfig
|
||||
text.clear()
|
||||
let textArr = str.split(/\n/gim)
|
||||
textArr.forEach((item, index) => {
|
||||
|
||||
@@ -54,28 +54,136 @@ export const cubicBezierPath = (x1, y1, x2, y2) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const calcPoint = (node, e) => {
|
||||
const { left, top, translateLeft, translateTop, width, height } = node
|
||||
const clientX = e.clientX
|
||||
const clientY = e.clientY
|
||||
// 中心点的坐标
|
||||
const centerX = translateLeft + width / 2
|
||||
const centerY = translateTop + height / 2
|
||||
const translateCenterX = left + width / 2
|
||||
const translateCenterY = top + height / 2
|
||||
const theta = Math.atan(height / width)
|
||||
// 矩形左上角坐标
|
||||
const deltaX = clientX - centerX
|
||||
const deltaY = centerY - clientY
|
||||
// 方向值
|
||||
const direction = Math.atan2(deltaY, deltaX)
|
||||
// 默认坐标
|
||||
let x = left + width
|
||||
let y = top + height
|
||||
if (direction < theta && direction >= -theta) {
|
||||
// 右边
|
||||
// 正切值 = 对边/邻边,对边 = 正切值*邻边
|
||||
const range = direction * (width / 2)
|
||||
if (direction < theta && direction >= 0) {
|
||||
// 中心点上边
|
||||
y = translateCenterY - range
|
||||
} else if (direction >= -theta && direction < 0) {
|
||||
// 中心点下方
|
||||
y = translateCenterY - range
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'right',
|
||||
range
|
||||
}
|
||||
} else if (direction >= theta && direction < Math.PI - theta) {
|
||||
// 上边
|
||||
y = top
|
||||
let range = 0
|
||||
if (direction < Math.PI / 2 - theta && direction >= theta) {
|
||||
// 正切值 = 对边/邻边,邻边 = 对边/正切值
|
||||
const side = height / 2 / direction
|
||||
range = -side
|
||||
// 中心点右侧
|
||||
x = translateCenterX + side
|
||||
} else if (
|
||||
direction >= Math.PI / 2 - theta &&
|
||||
direction < Math.PI - theta
|
||||
) {
|
||||
// 中心点左侧
|
||||
const tanValue = (centerX - clientX) / (centerY - clientY)
|
||||
const side = (height / 2) * tanValue
|
||||
range = side
|
||||
x = translateCenterX - side
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'top',
|
||||
range
|
||||
}
|
||||
} else if (direction < -theta && direction >= theta - Math.PI) {
|
||||
// 下边
|
||||
let range = 0
|
||||
if (direction >= theta - Math.PI / 2 && direction < -theta) {
|
||||
// 中心点右侧
|
||||
// 正切值 = 对边/邻边,邻边 = 对边/正切值
|
||||
const side = height / 2 / direction
|
||||
range = side
|
||||
x = translateCenterX - side
|
||||
} else if (
|
||||
direction < theta - Math.PI / 2 &&
|
||||
direction >= theta - Math.PI
|
||||
) {
|
||||
// 中心点左侧
|
||||
const tanValue = (centerX - clientX) / (centerY - clientY)
|
||||
const side = (height / 2) * tanValue
|
||||
range = -side
|
||||
x = translateCenterX + side
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'bottom',
|
||||
range
|
||||
}
|
||||
}
|
||||
// 左边
|
||||
x = left
|
||||
const tanValue = (centerY - clientY) / (centerX - clientX)
|
||||
const range = tanValue * (width / 2)
|
||||
if (direction >= -Math.PI && direction < theta - Math.PI) {
|
||||
// 中心点右侧
|
||||
y = translateCenterY - range
|
||||
} else if (direction < Math.PI && direction >= Math.PI - theta) {
|
||||
// 中心点左侧
|
||||
y = translateCenterY - range
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
dir: 'left',
|
||||
range
|
||||
}
|
||||
}
|
||||
// 获取节点的连接点
|
||||
export const getNodePoint = (node, dir = 'right') => {
|
||||
export const getNodePoint = (node, dir = 'right', range = 0, e = null) => {
|
||||
let { left, top, width, height } = node
|
||||
if (e) {
|
||||
return calcPoint(node, e)
|
||||
}
|
||||
switch (dir) {
|
||||
case 'left':
|
||||
return {
|
||||
x: left,
|
||||
y: top + height / 2
|
||||
y: top + height / 2 - range
|
||||
}
|
||||
case 'right':
|
||||
return {
|
||||
x: left + width,
|
||||
y: top + height / 2
|
||||
y: top + height / 2 - range
|
||||
}
|
||||
case 'top':
|
||||
return {
|
||||
x: left + width / 2,
|
||||
x: left + width / 2 - range,
|
||||
y: top
|
||||
}
|
||||
case 'bottom':
|
||||
return {
|
||||
x: left + width / 2,
|
||||
x: left + width / 2 - range,
|
||||
y: top + height
|
||||
}
|
||||
default:
|
||||
@@ -111,8 +219,8 @@ export const computeNodePoints = (fromNode, toNode) => {
|
||||
toDir = 'bottom'
|
||||
} else if (offsetY > 0 && -offsetY < offsetX && offsetY > offsetX) {
|
||||
// down
|
||||
fromDir = 'bottom'
|
||||
toDir = 'top'
|
||||
fromDir = 'right'
|
||||
toDir = 'right'
|
||||
}
|
||||
return [getNodePoint(fromNode, fromDir), getNodePoint(toNode, toDir)]
|
||||
}
|
||||
@@ -179,4 +287,4 @@ export const getDefaultControlPointOffsets = (startPoint, endPoint) => {
|
||||
y: controlPoints[1].y - endPoint.y
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '#e68112',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: '#8c5416',
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#e68112'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#8c5416',
|
||||
active: {
|
||||
borderColor: '#b0bc47'
|
||||
}
|
||||
color: '#8c5416'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: '#ffd683',
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 2,
|
||||
color: '#8c5416',
|
||||
active: {
|
||||
borderColor: '#e68112'
|
||||
}
|
||||
color: '#8c5416'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '#94c143',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#749336',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: '#749336',
|
||||
borderColor: '#aec668',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#749336'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#749336',
|
||||
active: {
|
||||
borderColor: '#749336'
|
||||
}
|
||||
color: '#749336'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: '#cee498',
|
||||
borderColor: '#aec668',
|
||||
borderWidth: 2,
|
||||
color: '#749336',
|
||||
active: {
|
||||
borderColor: '#749336'
|
||||
}
|
||||
color: '#749336'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(111, 61, 6)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(225, 201, 158)',
|
||||
borderColor: 'rgb(245, 224, 191)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 208, 124)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(231, 203, 155)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 208, 124)'
|
||||
}
|
||||
color: 'rgb(231, 203, 155)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(56, 45, 34)',
|
||||
borderColor: 'rgb(104, 84, 61)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(242, 216, 176)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 208, 124)'
|
||||
}
|
||||
color: 'rgb(242, 216, 176)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,19 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(36, 179, 96)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(27, 31, 34)',
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(36, 179, 96)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(115, 161, 191)',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
fillColor: 'rgb(115, 161, 191)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(115, 161, 191)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 115, 148)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
fillColor: 'rgb(191, 115, 148)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 115, 148)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 74)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,16 +18,13 @@ export default merge(defaultTheme, {
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=',
|
||||
// 背景重复
|
||||
backgroundRepeat: 'repeat',
|
||||
backgroundSize: 'auto',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(233, 223, 152)',
|
||||
color: '#333',
|
||||
fontSize: 24,
|
||||
borderRadius: 21,
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
borderRadius: 21
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -35,30 +32,18 @@ export default merge(defaultTheme, {
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
fontSize: 16,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
active: {
|
||||
fillColor: 'rgb(254, 219, 0)',
|
||||
borderColor: 'transparent'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,10 +18,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(18, 187, 55)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +26,18 @@ export default merge(defaultTheme, {
|
||||
borderColor: 'transparent',
|
||||
color: '#1a1a1a',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#1a1a1a',
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)'
|
||||
}
|
||||
color: '#1a1a1a'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 2,
|
||||
color: '#1a1a1a',
|
||||
active: {
|
||||
borderColor: 'rgb(18, 187, 55)'
|
||||
}
|
||||
color: '#1a1a1a'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -20,10 +20,7 @@ export default merge(defaultTheme, {
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
borderColor: 'rgb(249, 199, 84)',
|
||||
borderWidth: 1,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
borderWidth: 1
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -32,27 +29,18 @@ export default merge(defaultTheme, {
|
||||
borderWidth: 1,
|
||||
color: '#1a1a1a',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#1a1a1a',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
color: '#1a1a1a'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#1a1a1a',
|
||||
color: '#1a1a1a',
|
||||
borderWidth: 2,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 202, 110)'
|
||||
}
|
||||
borderWidth: 2
|
||||
}
|
||||
})
|
||||
|
||||
@@ -20,10 +20,7 @@ export default merge(defaultTheme, {
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
borderColor: 'rgb(189, 197, 201)',
|
||||
borderWidth: 2,
|
||||
active: {
|
||||
borderColor: 'rgb(169, 218, 218)'
|
||||
}
|
||||
borderWidth: 2
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -32,10 +29,7 @@ export default merge(defaultTheme, {
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(56, 123, 233)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
@@ -43,19 +37,13 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(30, 53, 86)',
|
||||
borderColor: 'rgb(30, 53, 86)',
|
||||
borderWidth: 1,
|
||||
marginY: 20,
|
||||
active: {
|
||||
borderColor: 'rgb(169, 218, 218)'
|
||||
}
|
||||
marginY: 20
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(56, 123, 233)',
|
||||
borderColor: 'rgb(56, 123, 233)',
|
||||
color: '#fff',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(169, 218, 218)'
|
||||
}
|
||||
borderWidth: 0
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,7 @@ export default merge(defaultTheme, {
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(255, 255, 255)',
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,26 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(255, 255, 255)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -14,10 +14,7 @@ export default merge(defaultTheme, {
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(253, 244, 217)',
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -25,27 +22,18 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(242, 200, 104)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(123, 199, 120)',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(94, 199, 248)'
|
||||
}
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 123, 91)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,18 +24,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(125, 86, 42)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 123, 91)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(96, 71, 47)',
|
||||
active: {
|
||||
borderColor: 'rgb(173, 123, 91)'
|
||||
}
|
||||
color: 'rgb(96, 71, 47)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -47,9 +37,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 249, 239)',
|
||||
borderColor: 'rgb(173, 123, 91)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(122, 83, 44)',
|
||||
active: {
|
||||
borderColor: 'rgb(202, 117, 79)'
|
||||
}
|
||||
color: 'rgb(122, 83, 44)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 91, 12)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,18 +24,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(50, 113, 96)',
|
||||
borderColor: 'rgb(113, 195, 169)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(173, 91, 12)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(10, 59, 43)',
|
||||
active: {
|
||||
borderColor: 'rgb(173, 91, 12)'
|
||||
}
|
||||
color: 'rgb(10, 59, 43)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -47,9 +37,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(246, 238, 211)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(173, 91, 12)',
|
||||
active: {
|
||||
borderColor: 'rgb(113, 195, 169)'
|
||||
}
|
||||
color: 'rgb(173, 91, 12)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,10 +18,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(28, 178, 43)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
borderRadius: 10
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,26 +26,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(147,148,149)',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(147, 148, 149)',
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
color: 'rgb(147, 148, 149)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(17, 68, 23)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(36, 179, 96)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,28 +25,18 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(36, 179, 96)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 199, 13)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -70,12 +70,7 @@ export default {
|
||||
borderWidth: 0,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -93,12 +88,7 @@ export default {
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
@@ -116,12 +106,7 @@ export default {
|
||||
borderWidth: 0,
|
||||
borderRadius: 5,
|
||||
borderDasharray: 'none',
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -139,12 +124,7 @@ export default {
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
textDecoration: 'none'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +158,8 @@ const nodeSizeIndependenceList = [
|
||||
'backgroundImage',
|
||||
'backgroundRepeat',
|
||||
'backgroundPosition',
|
||||
'backgroundSize'
|
||||
'backgroundSize',
|
||||
'rootLineKeepSameInCurve'
|
||||
]
|
||||
export const checkIsNodeSizeIndependenceConfig = (config) => {
|
||||
let keys = Object.keys(config)
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 147, 115)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
fillColor: 'rgb(191, 147, 115)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 147, 115)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 73, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,11 +26,6 @@ export default merge(defaultTheme, {
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 115, 115)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
fillColor: 'rgb(191, 115, 115)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 115, 115)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 57, 57)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(51, 56, 62)',
|
||||
color: 'rgb(247, 208, 160)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(247, 208, 160)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +25,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(81, 58, 42)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 56, 62)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(127, 93, 64)',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(255, 214, 175)',
|
||||
active: {
|
||||
borderColor: 'rgb(51, 56, 62)'
|
||||
}
|
||||
color: 'rgb(255, 214, 175)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(25, 193, 73)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: '#222',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,28 +25,18 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(69, 149, 96)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(25, 193, 73)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(25, 193, 73)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(251, 158, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(51, 51, 51)',
|
||||
active: {
|
||||
borderColor: 'rgb(25, 193, 73)'
|
||||
}
|
||||
color: 'rgb(51, 51, 51)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(255, 255, 255)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,19 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(209, 210, 210)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(204, 204, 204)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 119, 34)'
|
||||
}
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 119, 34)',
|
||||
borderColor: '',
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(23, 153, 243)'
|
||||
}
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,7 @@ export default merge(defaultTheme, {
|
||||
root: {
|
||||
fillColor: 'rgb(55, 165, 255)',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 160, 36)'
|
||||
}
|
||||
borderWidth: 3
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,26 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(55, 165, 255)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(55, 165, 255)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
borderColor: '#222',
|
||||
borderWidth: 3,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(55, 165, 255)'
|
||||
}
|
||||
color: '#222'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
root: {
|
||||
fillColor: 'rgb(0, 192, 184)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 160, 36)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,26 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#222',
|
||||
borderColor: 'rgb(184, 235, 233)',
|
||||
borderWidth: 2,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(90, 206, 241)',
|
||||
borderColor: 'transparent',
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 192, 184)'
|
||||
}
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: '#110501',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#a9a4a9',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: '#a9a4a9',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#110501'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#a9a4a9',
|
||||
active: {
|
||||
borderColor: '#ff6811'
|
||||
}
|
||||
color: '#a9a4a9'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: '',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 2,
|
||||
color: '#a9a4a9',
|
||||
active: {
|
||||
borderColor: '#110501'
|
||||
}
|
||||
color: '#a9a4a9'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,11 +16,7 @@ export default merge(defaultTheme, {
|
||||
root: {
|
||||
fillColor: 'rgb(139, 109, 225)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(243, 104, 138)',
|
||||
borderWidth: 2
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -28,28 +24,17 @@ export default merge(defaultTheme, {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(139, 109, 225)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(139, 109, 225)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(139, 109, 225)',
|
||||
borderWidth: 2
|
||||
}
|
||||
color: '#222'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -18,11 +18,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(255, 233, 157)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 233, 157)',
|
||||
borderWidth: 3
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +26,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(211, 58, 21)',
|
||||
borderColor: 'rgb(222, 101, 85)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 233, 157)'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(144, 71, 43)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 233, 157)'
|
||||
}
|
||||
color: 'rgb(144, 71, 43)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,9 +39,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 247, 211)',
|
||||
borderColor: 'rgb(255, 202, 162)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(187, 101, 69)',
|
||||
active: {
|
||||
borderColor: 'rgb(222, 101, 85)'
|
||||
}
|
||||
color: 'rgb(187, 101, 69)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ export default merge(defaultTheme, {
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(123, 115, 191)',
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
fillColor: 'rgb(123, 115, 191)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -24,26 +21,17 @@ export default merge(defaultTheme, {
|
||||
color: '#333',
|
||||
borderColor: 'rgb(123, 115, 191)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333',
|
||||
active: {
|
||||
borderColor: 'rgb(61, 57, 96)'
|
||||
}
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -16,10 +16,7 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(34, 34, 34)',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(34, 34, 34)',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(34, 34, 34)',
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
color: 'rgb(34, 34, 34)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,9 +37,6 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(34, 34, 34)',
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
color: 'rgb(34, 34, 34)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(65, 89, 158)',
|
||||
active: {
|
||||
borderColor: 'rgb(251, 227, 188)',
|
||||
borderWidth: 3
|
||||
}
|
||||
color: 'rgb(65, 89, 158)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +25,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(65, 89, 158)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(65, 89, 158)',
|
||||
active: {
|
||||
borderColor: 'rgb(251, 227, 188)'
|
||||
}
|
||||
color: 'rgb(65, 89, 158)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(65, 89, 158)',
|
||||
active: {
|
||||
borderColor: 'rgb(251, 227, 188)'
|
||||
}
|
||||
color: 'rgb(65, 89, 158)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,11 +17,7 @@ export default merge(defaultTheme, {
|
||||
fillColor: 'rgb(255, 112, 52)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
active: {
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3
|
||||
}
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -29,27 +25,17 @@ export default merge(defaultTheme, {
|
||||
color: 'rgb(51, 51, 51)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 112, 52)',
|
||||
borderWidth: 2
|
||||
}
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 112, 52)'
|
||||
}
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(255, 222, 69)',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(51, 51, 51)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 112, 52)'
|
||||
}
|
||||
color: 'rgb(51, 51, 51)'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -632,3 +632,28 @@ 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
|
||||
}
|
||||
BIN
web/src/assets/avatar/南风.jpg
Normal file
BIN
web/src/assets/avatar/南风.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
web/src/assets/avatar/小米.jpg
Normal file
BIN
web/src/assets/avatar/小米.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
web/src/assets/avatar/布林.jpg
Normal file
BIN
web/src/assets/avatar/布林.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
web/src/assets/avatar/棐.jpg
Normal file
BIN
web/src/assets/avatar/棐.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2479351 */
|
||||
src: url('iconfont.woff2?t=1690537337895') format('woff2'),
|
||||
url('iconfont.woff?t=1690537337895') format('woff'),
|
||||
url('iconfont.ttf?t=1690537337895') format('truetype');
|
||||
src: url('iconfont.woff2?t=1691822758372') format('woff2'),
|
||||
url('iconfont.woff?t=1691822758372') format('woff'),
|
||||
url('iconfont.ttf?t=1691822758372') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icontouming:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.iconlieri:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,7 +17,9 @@ import {
|
||||
shapeList as shapeListZh,
|
||||
sidebarTriggerList as sidebarTriggerListZh,
|
||||
backgroundSizeList as backgroundSizeListZh,
|
||||
downTypeList as downTypeListZh
|
||||
downTypeList as downTypeListZh,
|
||||
shapeListMap as shapeListMapZh,
|
||||
lineStyleMap as lineStyleMapZh
|
||||
} from './zh'
|
||||
import {
|
||||
fontFamilyList as fontFamilyListEn,
|
||||
@@ -48,6 +50,11 @@ const lineStyleList = {
|
||||
en: lineStyleListEn
|
||||
}
|
||||
|
||||
const lineStyleMap = {
|
||||
zh: lineStyleMapZh,
|
||||
en: lineStyleMapZh
|
||||
}
|
||||
|
||||
const rootLineKeepSameInCurveList = {
|
||||
zh: rootLineKeepSameInCurveListZh,
|
||||
en: rootLineKeepSameInCurveListEn
|
||||
@@ -78,6 +85,11 @@ const shapeList = {
|
||||
en: shapeListEn
|
||||
}
|
||||
|
||||
const shapeListMap = {
|
||||
zh: shapeListMapZh,
|
||||
en: shapeListMapZh
|
||||
}
|
||||
|
||||
const sidebarTriggerList = {
|
||||
zh: sidebarTriggerListZh,
|
||||
en: sidebarTriggerListEn
|
||||
@@ -100,12 +112,14 @@ export {
|
||||
fontFamilyList,
|
||||
borderDasharrayList,
|
||||
lineStyleList,
|
||||
lineStyleMap,
|
||||
rootLineKeepSameInCurveList,
|
||||
backgroundRepeatList,
|
||||
backgroundPositionList,
|
||||
backgroundSizeList,
|
||||
shortcutKeyList,
|
||||
shapeList,
|
||||
shapeListMap,
|
||||
sidebarTriggerList,
|
||||
downTypeList
|
||||
}
|
||||
|
||||
@@ -97,7 +97,8 @@ export const colorList = [
|
||||
'#0C797D',
|
||||
'#0062B1',
|
||||
'#653294',
|
||||
'#AB149E'
|
||||
'#AB149E',
|
||||
'transparent'
|
||||
]
|
||||
|
||||
// 边框宽度
|
||||
@@ -141,6 +142,12 @@ export const borderRadiusList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
// 线宽
|
||||
export const lineWidthList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
export const lineStyleMap = {
|
||||
straight: `<svg width="60" height="26"><path d="M18,14L30,14L30,5L42,5" fill="none" stroke="#000" stroke-width="2"></path><path d="M18,14L30,14L30,23L42,23" fill="none" stroke="#000" stroke-width="2"></path></svg>`,
|
||||
curve: `<svg width="60" height="26"><path d="M18,14L30,14A12,-9 0 0 1 42,5" fill="none" stroke="#000" stroke-width="2"></path><path d="M18,14L30,14A12,9 0 0 0 42,23" fill="none" stroke="#000" stroke-width="2"></path></svg>`,
|
||||
direct: `<svg width="60" height="26"><path d="M18,14L30,14L42,5" fill="none" stroke="#000" stroke-width="2"></path><path d="M18,14L30,14L42,23" fill="none" stroke="#000" stroke-width="2"></path></svg>`
|
||||
}
|
||||
|
||||
// 连线风格
|
||||
export const lineStyleList = [
|
||||
{
|
||||
@@ -378,6 +385,22 @@ export const shortcutKeyList = [
|
||||
}
|
||||
]
|
||||
|
||||
export const shapeListMap = {
|
||||
rectangle: 'M 4 12 L 4 3 L 56 3 L 56 21 L 4 21 L 4 12 Z',
|
||||
diamond: 'M 4 12 L 30 3 L 56 12 L 30 21 L 4 12 Z',
|
||||
parallelogram: 'M 10 3 L 56 3 L 50 21 L 4 21 L 10 3 Z',
|
||||
roundedRectangle:
|
||||
'M 13 3 L 47 3 A 9 9 0, 0 1 47 21 L 13 21 A 9 9 0, 0 1 13 3 Z',
|
||||
octagonalRectangle:
|
||||
'M 4 12 L 4 9 L 10 3 L 50 3 L 56 9 L 56 15 L 50 21 L 10 21 L 4 15 L 4 12 Z',
|
||||
outerTriangularRectangle:
|
||||
'M 4 12 L 10 3 L 50 3 L 56 12 L 50 21 L 10 21 L 4 12 Z',
|
||||
innerTriangularRectangle:
|
||||
'M 10 12 L 4 3 L 56 3 L 50 12 L 56 21 L 4 21 L 10 12 Z',
|
||||
ellipse: 'M 4 12 A 26 9 0, 1, 0 30 3 A 26 9 0, 0, 0 4 12 Z',
|
||||
circle: 'M 21 12 A 9 9 0, 1, 0 30 3 A 9 9 0, 0, 0 21 12 Z'
|
||||
}
|
||||
|
||||
// 形状列表
|
||||
export const shapeList = [
|
||||
{
|
||||
@@ -508,4 +531,4 @@ export const downTypeList = [
|
||||
icon: 'iconxmind',
|
||||
desc: 'XMind格式'
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: 'rgb(26, 26, 26)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(235, 255, 187)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default {
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(39, 222, 232)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(39, 222, 232)'
|
||||
}
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,10 +37,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(26, 26, 26)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(26, 26, 26)',
|
||||
active: {
|
||||
borderColor: 'rgb(39, 222, 232)'
|
||||
}
|
||||
color: 'rgb(26, 26, 26)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ export default {
|
||||
borderColor: 'rgb(207, 121, 105)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(172, 202, 199)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -31,18 +28,12 @@ export default {
|
||||
borderColor: 'rgb(222, 186, 183)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(172, 202, 199)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(131, 90, 64)',
|
||||
active: {
|
||||
borderColor: 'rgb(172, 202, 199)'
|
||||
}
|
||||
color: 'rgb(131, 90, 64)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -50,10 +41,7 @@ export default {
|
||||
fillColor: 'rgb(172, 202, 199)',
|
||||
borderColor: 'rgb(172, 202, 199)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(91, 102, 97)',
|
||||
active: {
|
||||
borderColor: 'rgb(207, 121, 105)'
|
||||
}
|
||||
color: 'rgb(91, 102, 97)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ export default {
|
||||
borderColor: 'rgb(255, 0, 214)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 181, 0)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -30,18 +27,12 @@ export default {
|
||||
color: 'rgb(248, 177, 237)',
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 181, 0)',
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#fff',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 181, 0)'
|
||||
}
|
||||
color: '#fff'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -49,10 +40,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(255, 181, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(17, 17, 84)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 0, 214)'
|
||||
}
|
||||
color: 'rgb(17, 17, 84)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,7 @@ export default {
|
||||
color: '#fff',
|
||||
borderColor: 'rgb(22, 22, 22)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
active: {
|
||||
borderColor: '#a13600',
|
||||
}
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -25,18 +22,12 @@ export default {
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(22, 22, 22)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(22, 22, 22)'
|
||||
}
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -44,9 +35,6 @@ export default {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(34, 34, 34)',
|
||||
active: {
|
||||
borderColor: '#a13600'
|
||||
}
|
||||
color: 'rgb(34, 34, 34)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: 'rgb(18, 187, 55)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(136, 100, 0)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default {
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 92, 92)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(26, 26, 26)',
|
||||
active: {
|
||||
borderColor: 'rgb(209, 237, 176)'
|
||||
}
|
||||
color: 'rgb(26, 26, 26)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,10 +37,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(136, 100, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(136, 100, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(254, 92, 92)'
|
||||
}
|
||||
color: 'rgb(136, 100, 0)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(0, 155, 255)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -27,18 +24,12 @@ export default {
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 189, 255)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 66, 157)',
|
||||
active: {
|
||||
borderColor: 'rgb(96, 189, 255)'
|
||||
}
|
||||
color: 'rgb(0, 66, 157)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -46,10 +37,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(0, 155, 255)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(0, 155, 255)',
|
||||
active: {
|
||||
borderColor: 'rgba(2, 167, 240, 0.5)'
|
||||
}
|
||||
color: 'rgb(0, 155, 255)'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ export default {
|
||||
borderColor: 'rgb(51, 149, 255)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24,
|
||||
shape: 'roundedRectangle',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 168, 101)',
|
||||
}
|
||||
shape: 'roundedRectangle'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -26,18 +23,12 @@ export default {
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 3,
|
||||
fontSize: 18,
|
||||
active: {
|
||||
borderColor: 'rgb(255, 168, 101)',
|
||||
}
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(0, 0, 0)',
|
||||
active: {
|
||||
borderColor: 'rgb(255, 168, 101)'
|
||||
}
|
||||
color: 'rgb(0, 0, 0)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -45,10 +36,7 @@ export default {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(255, 168, 101)',
|
||||
borderWidth: 2,
|
||||
color: '#000',
|
||||
active: {
|
||||
borderColor: 'rgb(51, 149, 255)'
|
||||
}
|
||||
color: '#000'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ export default {
|
||||
rootStyle: 'Root Node',
|
||||
associativeLineText: 'Associative line text',
|
||||
fontFamily: 'Font family',
|
||||
fontSize: 'Font size'
|
||||
fontSize: 'Font size',
|
||||
isShowScrollbar: 'Is show scrollbar'
|
||||
},
|
||||
color: {
|
||||
moreColor: 'More color'
|
||||
@@ -77,7 +78,10 @@ export default {
|
||||
level5: 'Level5',
|
||||
level6: 'Level6',
|
||||
zenMode: 'Zen mode',
|
||||
fitCanvas: 'Fit canvas'
|
||||
fitCanvas: 'Fit canvas',
|
||||
removeImage: 'Remove image',
|
||||
removeHyperlink: 'Remove hyperlink',
|
||||
removeNote: 'Remove note'
|
||||
},
|
||||
count: {
|
||||
words: 'Words',
|
||||
@@ -105,7 +109,8 @@ export default {
|
||||
notifyTitle: 'Info',
|
||||
notifyMessage: 'If the download is not triggered, check whether it is blocked by the browser',
|
||||
paddingX: 'Padding x',
|
||||
paddingY: 'Padding y'
|
||||
paddingY: 'Padding y',
|
||||
useMultiPageExport: 'Export multi page'
|
||||
},
|
||||
fullscreen: {
|
||||
fullscreenShow: 'Full screen show',
|
||||
@@ -142,7 +147,8 @@ export default {
|
||||
addTip: 'Press Enter to add'
|
||||
},
|
||||
outline: {
|
||||
title: 'Outline'
|
||||
title: 'Outline',
|
||||
nodeDefaultText: 'Branch node'
|
||||
},
|
||||
scale: {
|
||||
zoomIn: 'Zoom in',
|
||||
@@ -167,6 +173,7 @@ export default {
|
||||
italic: 'Italic',
|
||||
textDecoration: 'Text decoration',
|
||||
underline: 'Underline',
|
||||
none: 'None',
|
||||
lineThrough: 'Line through',
|
||||
overline: 'Overline',
|
||||
border: 'Border',
|
||||
|
||||
@@ -50,7 +50,8 @@ export default {
|
||||
rootStyle: '根节点',
|
||||
associativeLineText: '关联线文字',
|
||||
fontFamily: '字体',
|
||||
fontSize: '字号'
|
||||
fontSize: '字号',
|
||||
isShowScrollbar: '是否显示滚动条'
|
||||
},
|
||||
color: {
|
||||
moreColor: '更多颜色'
|
||||
@@ -77,7 +78,10 @@ export default {
|
||||
level5: '五级主题',
|
||||
level6: '六级主题',
|
||||
zenMode: '禅模式',
|
||||
fitCanvas: '适应画布'
|
||||
fitCanvas: '适应画布',
|
||||
removeImage: '移除图片',
|
||||
removeHyperlink: '移除超链接',
|
||||
removeNote: '移除备注'
|
||||
},
|
||||
count: {
|
||||
words: '字数',
|
||||
@@ -105,7 +109,8 @@ export default {
|
||||
notifyTitle: '消息',
|
||||
notifyMessage: '如果没有触发下载,请检查是否被浏览器拦截了',
|
||||
paddingX: '水平内边距',
|
||||
paddingY: '垂直内边距'
|
||||
paddingY: '垂直内边距',
|
||||
useMultiPageExport: '是否多页导出'
|
||||
},
|
||||
fullscreen: {
|
||||
fullscreenShow: '全屏查看',
|
||||
@@ -142,7 +147,8 @@ export default {
|
||||
addTip: '请按回车键添加'
|
||||
},
|
||||
outline: {
|
||||
title: '大纲'
|
||||
title: '大纲',
|
||||
nodeDefaultText: '分支节点'
|
||||
},
|
||||
scale: {
|
||||
zoomIn: '放大',
|
||||
@@ -166,6 +172,7 @@ export default {
|
||||
addFontWeight: '加粗',
|
||||
italic: '斜体',
|
||||
textDecoration: '划线',
|
||||
none: '无',
|
||||
underline: '下划线',
|
||||
lineThrough: '中划线',
|
||||
overline: '上划线',
|
||||
|
||||
@@ -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,6 +34,7 @@ let APIList = [
|
||||
'nodeImgAdjust',
|
||||
'search',
|
||||
'painter',
|
||||
'scrollbar',
|
||||
'xmind',
|
||||
'markdown',
|
||||
'utils'
|
||||
|
||||
@@ -1,5 +1,155 @@
|
||||
# Changelog
|
||||
|
||||
## 0.7.0
|
||||
|
||||
Breaking change: Removed the section of node activation style in the theme file, Setting the activation style of nodes is no longer supported, and the activation effect has been changed to a unified node outer border style, while also supporting the mouse hover effect.
|
||||
|
||||
Fix:
|
||||
|
||||
> 1.Fix rendering anomalies when the node border size is relatively large.
|
||||
>
|
||||
> 2.Fixed an issue where the node style of the associated line will not be updated when switching themes.
|
||||
>
|
||||
> 3.Fix that selecting all did not trigger node_ The issue with active events.
|
||||
|
||||
新增:
|
||||
|
||||
> 1.When folding nodes, displays the number of collapsed nodes.
|
||||
>
|
||||
> 2.Support the position of the endpoint of the associated line to follow mouse drag changes.
|
||||
>
|
||||
> 3.Add a scrollbar plugin.
|
||||
>
|
||||
> 4.Support opening specified online files through fileURL query parameters in URLs.
|
||||
>
|
||||
> 5.The fishbone diagram supports setting node margins.
|
||||
>
|
||||
> 6.By default, double-click to reset the canvas.
|
||||
>
|
||||
> 7.Modify the parameters of the export image method, and when exporting PDF, if the size of the mind map is smaller than A4 paper, do not rotate the direction.
|
||||
>
|
||||
> 8.Improve the clarity of exported images and PDFs on high-definition screens.
|
||||
>
|
||||
> 9.Add a pre destruction lifecycle function to the plugin to address the issue of some side effects that were not cleared during the destruction of the mind map.
|
||||
>
|
||||
> 10.Optimize the settings of the basic style and do not trigger full rendering when modifying theme attributes that do not affect size.
|
||||
>
|
||||
> 11.Prohibit triggering node right-click menu events when multiple node selections are completed, to avoid triggering the right-click menu display.
|
||||
>
|
||||
> 12.Optimize the Select plugin so that if multiple selected nodes do not change, the activation event is not triggered.
|
||||
>
|
||||
> 13.The activation node list thrown by event node_active no longer directly references the internal activation list.
|
||||
>
|
||||
> 14.Optimize the logic of mouse button down node events, and support dragging the canvas by holding down the root node with the right mouse button in the right-click drag and drop canvas mode.
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.Do not directly reference the internal activation node list to optimize performance.
|
||||
>
|
||||
> 2.Support configuring whether to display scrollbars.
|
||||
>
|
||||
> 3.Delete the active node configuration in the sidebar node style configuration section.
|
||||
|
||||
## 0.6.17
|
||||
|
||||
Fix:
|
||||
|
||||
> 1.Fix the issue of error reporting in the xmind file exported from Baidu Brain Map.
|
||||
>
|
||||
> 2.Fix the mindMap. export method code error.
|
||||
|
||||
New:
|
||||
|
||||
> 1.Create index.d.ts file。
|
||||
>
|
||||
> 2.Support configuration to enable double click reset mind map.
|
||||
>
|
||||
> 3.Intercept paste operations during rich text editing, remove formatting, and only allow pasting pure text.
|
||||
|
||||
## 0.6.16
|
||||
|
||||
Fix:
|
||||
|
||||
> 1.Optimize the logic of rich text measurement elements, remove duplicate settings for styles, and add duplicate nodes
|
||||
>
|
||||
> 2.Optimize the export image logic, and when traversing the node to convert the URL of the image, if it is already in the form of data: URL, do not handle it repeatedly.
|
||||
|
||||
New:
|
||||
|
||||
> 1.Remove the second parameter of the exported SVG method and configure it through instantiation instead.
|
||||
>
|
||||
> 2.Export images without using external libraries.
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.Fixed a bug where siblings can be added to the root node when editing the outline separately.
|
||||
|
||||
## 0.6.15-fix.2
|
||||
|
||||
Fix: Fixed an issue where rich text nodes cannot be displayed in Firefox browser.
|
||||
|
||||
## 0.6.15-fix.1
|
||||
|
||||
New:
|
||||
|
||||
> 1.Export PDF supports pagination export based on image size.
|
||||
>
|
||||
> 2.Exporting PDF supports automatic direction adjustment based on aspect ratio.
|
||||
>
|
||||
> 3.Optimize the placeholder elements of the expand and collapse buttons: 1. Nodes without child nodes do not render this element; 2. Dynamically update the element based on the existence of child nodes.
|
||||
>
|
||||
> 4.Add a configuration that prohibits mouse wheel scaling.
|
||||
>
|
||||
> 5.Supports passing error handling functions.
|
||||
|
||||
Fix:
|
||||
|
||||
> 1.Fix the issue of displaying exceptions when node text is empty.
|
||||
>
|
||||
> 2.Change the paddingX and paddingY of exported SVG graphics to single sided padding.
|
||||
>
|
||||
> 3.Fixed an issue where the mouse is not centered when zooming when the canvas is not 0 from the top left corner of the browser window.
|
||||
>
|
||||
> 4.Fix the issue of overlapping node borders.
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.The bottom right corner supports jumping to related links.
|
||||
>
|
||||
> 2.Adjust the position of the mini map to solve the problem of being blocked by side buttons.
|
||||
>
|
||||
> 3.Fix the issue where the prompt in the upper right corner of the open local file cannot be closed.
|
||||
>
|
||||
> 4.Editing the outline separately is no longer linked to the canvas, optimizing the editing experience under large data volume.
|
||||
>
|
||||
> 5.The sidebar involves graphical options to increase visualization effects.
|
||||
|
||||
## 0.6.14
|
||||
|
||||
New:
|
||||
|
||||
> 1.Remove and create hidden input boxes, and copy and paste them through navigator. clipboard; Support cross browser pasting of mind map node data; Support custom processing of text data in the clipboard.
|
||||
|
||||
Demo:
|
||||
|
||||
> 1.Fix the issue of enabling input to automatically enter text editing mode and conflicting with other input boxes.
|
||||
>
|
||||
> 2.Fix the issue of not being able to delete node images in the node image pop-up window.
|
||||
>
|
||||
> 3.Fixed an issue where the text decoration line style of nodes cannot be removed in the node style sidebar.
|
||||
>
|
||||
> 4.The color selector supports selecting transparent colors.
|
||||
>
|
||||
> 5.Fix the issue of importing mind map data without updating the sidebar data when the basic style sidebar is open.
|
||||
>
|
||||
> 6.Fixed the issue of not focusing when modifying the text of one node in the outline and then clicking on other nodes.
|
||||
>
|
||||
> 7.Fixed an issue where the node and word count statistics in the bottom left corner were not updated after exiting Zen mode.
|
||||
>
|
||||
> 8.Support deleting hyperlinks and notes of nodes from the right-click menu.
|
||||
>
|
||||
> 9.Support pasting node data of Zhixi Mind Map.
|
||||
|
||||
## 0.6.13
|
||||
|
||||
Fix:
|
||||
|
||||
@@ -1,6 +1,107 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.7.0</h2>
|
||||
<p>Breaking change: Removed the section of node activation style in the theme file, Setting the activation style of nodes is no longer supported, and the activation effect has been changed to a unified node outer border style, while also supporting the mouse hover effect.</p>
|
||||
<p>Fix:</p>
|
||||
<blockquote>
|
||||
<p>1.Fix rendering anomalies when the node border size is relatively large.</p>
|
||||
<p>2.Fixed an issue where the node style of the associated line will not be updated when switching themes.</p>
|
||||
<p>3.Fix that selecting all did not trigger node_ The issue with active events.</p>
|
||||
</blockquote>
|
||||
<p>新增:</p>
|
||||
<blockquote>
|
||||
<p>1.When folding nodes, displays the number of collapsed nodes.</p>
|
||||
<p>2.Support the position of the endpoint of the associated line to follow mouse drag changes.</p>
|
||||
<p>3.Add a scrollbar plugin.</p>
|
||||
<p>4.Support opening specified online files through fileURL query parameters in URLs.</p>
|
||||
<p>5.The fishbone diagram supports setting node margins.</p>
|
||||
<p>6.By default, double-click to reset the canvas.</p>
|
||||
<p>7.Modify the parameters of the export image method, and when exporting PDF, if the size of the mind map is smaller than A4 paper, do not rotate the direction.</p>
|
||||
<p>8.Improve the clarity of exported images and PDFs on high-definition screens.</p>
|
||||
<p>9.Add a pre destruction lifecycle function to the plugin to address the issue of some side effects that were not cleared during the destruction of the mind map.</p>
|
||||
<p>10.Optimize the settings of the basic style and do not trigger full rendering when modifying theme attributes that do not affect size.</p>
|
||||
<p>11.Prohibit triggering node right-click menu events when multiple node selections are completed, to avoid triggering the right-click menu display.</p>
|
||||
<p>12.Optimize the Select plugin so that if multiple selected nodes do not change, the activation event is not triggered.</p>
|
||||
<p>13.The activation node list thrown by event node_active no longer directly references the internal activation list.</p>
|
||||
<p>14.Optimize the logic of mouse button down node events, and support dragging the canvas by holding down the root node with the right mouse button in the right-click drag and drop canvas mode.</p>
|
||||
</blockquote>
|
||||
<p>Demo:</p>
|
||||
<blockquote>
|
||||
<p>1.Do not directly reference the internal activation node list to optimize performance.</p>
|
||||
<p>2.Support configuring whether to display scrollbars.</p>
|
||||
<p>3.Delete the active node configuration in the sidebar node style configuration section.</p>
|
||||
</blockquote>
|
||||
<h2>0.6.17</h2>
|
||||
<p>Fix:</p>
|
||||
<blockquote>
|
||||
<p>1.Fix the issue of error reporting in the xmind file exported from Baidu Brain Map.</p>
|
||||
<p>2.Fix the mindMap. export method code error.</p>
|
||||
</blockquote>
|
||||
<p>New:</p>
|
||||
<blockquote>
|
||||
<p>1.Create index.d.ts file。</p>
|
||||
<p>2.Support configuration to enable double click reset mind map.</p>
|
||||
<p>3.Intercept paste operations during rich text editing, remove formatting, and only allow pasting pure text.</p>
|
||||
</blockquote>
|
||||
<h2>0.6.16</h2>
|
||||
<p>Fix:</p>
|
||||
<blockquote>
|
||||
<p>1.Optimize the logic of rich text measurement elements, remove duplicate settings for styles, and add duplicate nodes</p>
|
||||
<p>2.Optimize the export image logic, and when traversing the node to convert the URL of the image, if it is already in the form of data: URL, do not handle it repeatedly.</p>
|
||||
</blockquote>
|
||||
<p>New:</p>
|
||||
<blockquote>
|
||||
<p>1.Remove the second parameter of the exported SVG method and configure it through instantiation instead.</p>
|
||||
<p>2.Export images without using external libraries.</p>
|
||||
</blockquote>
|
||||
<p>Demo:</p>
|
||||
<blockquote>
|
||||
<p>1.Fixed a bug where siblings can be added to the root node when editing the outline separately.</p>
|
||||
</blockquote>
|
||||
<h2>0.6.15-fix.2</h2>
|
||||
<p>Fix: Fixed an issue where rich text nodes cannot be displayed in Firefox browser.</p>
|
||||
<h2>0.6.15-fix.1</h2>
|
||||
<p>New:</p>
|
||||
<blockquote>
|
||||
<p>1.Export PDF supports pagination export based on image size.</p>
|
||||
<p>2.Exporting PDF supports automatic direction adjustment based on aspect ratio.</p>
|
||||
<p>3.Optimize the placeholder elements of the expand and collapse buttons: 1. Nodes without child nodes do not render this element; 2. Dynamically update the element based on the existence of child nodes.</p>
|
||||
<p>4.Add a configuration that prohibits mouse wheel scaling.</p>
|
||||
<p>5.Supports passing error handling functions.</p>
|
||||
</blockquote>
|
||||
<p>Fix:</p>
|
||||
<blockquote>
|
||||
<p>1.Fix the issue of displaying exceptions when node text is empty.</p>
|
||||
<p>2.Change the paddingX and paddingY of exported SVG graphics to single sided padding.</p>
|
||||
<p>3.Fixed an issue where the mouse is not centered when zooming when the canvas is not 0 from the top left corner of the browser window.</p>
|
||||
<p>4.Fix the issue of overlapping node borders.</p>
|
||||
</blockquote>
|
||||
<p>Demo:</p>
|
||||
<blockquote>
|
||||
<p>1.The bottom right corner supports jumping to related links.</p>
|
||||
<p>2.Adjust the position of the mini map to solve the problem of being blocked by side buttons.</p>
|
||||
<p>3.Fix the issue where the prompt in the upper right corner of the open local file cannot be closed.</p>
|
||||
<p>4.Editing the outline separately is no longer linked to the canvas, optimizing the editing experience under large data volume.</p>
|
||||
<p>5.The sidebar involves graphical options to increase visualization effects.</p>
|
||||
</blockquote>
|
||||
<h2>0.6.14</h2>
|
||||
<p>New:</p>
|
||||
<blockquote>
|
||||
<p>1.Remove and create hidden input boxes, and copy and paste them through navigator. clipboard; Support cross browser pasting of mind map node data; Support custom processing of text data in the clipboard.</p>
|
||||
</blockquote>
|
||||
<p>Demo:</p>
|
||||
<blockquote>
|
||||
<p>1.Fix the issue of enabling input to automatically enter text editing mode and conflicting with other input boxes.</p>
|
||||
<p>2.Fix the issue of not being able to delete node images in the node image pop-up window.</p>
|
||||
<p>3.Fixed an issue where the text decoration line style of nodes cannot be removed in the node style sidebar.</p>
|
||||
<p>4.The color selector supports selecting transparent colors.</p>
|
||||
<p>5.Fix the issue of importing mind map data without updating the sidebar data when the basic style sidebar is open.</p>
|
||||
<p>6.Fixed the issue of not focusing when modifying the text of one node in the outline and then clicking on other nodes.</p>
|
||||
<p>7.Fixed an issue where the node and word count statistics in the bottom left corner were not updated after exiting Zen mode.</p>
|
||||
<p>8.Support deleting hyperlinks and notes of nodes from the right-click menu.</p>
|
||||
<p>9.Support pasting node data of Zhixi Mind Map.</p>
|
||||
</blockquote>
|
||||
<h2>0.6.13</h2>
|
||||
<p>Fix:</p>
|
||||
<blockquote>
|
||||
|
||||
@@ -48,8 +48,10 @@ const mindMap = new MindMap({
|
||||
| mousewheelZoomActionReverse(v0.6.5+) | Boolean | false | When `mousewheelAction` is set to `zoom`, the default scrolling forward is to zoom out, and scrolling backward is to zoom in. If this property is set to true, it will be reversed | |
|
||||
| defaultInsertSecondLevelNodeText(v0.4.7+) | String | 二级节点 | Text of the default inserted secondary node | |
|
||||
| defaultInsertBelowSecondLevelNodeText(v0.4.7+) | String | 分支主题 | Text for nodes below the second level inserted by default | |
|
||||
| expandBtnStyle(v0.5.0+) | Object | { color: '#808080', fill: '#fff' } | Expand the color of the stow button | |
|
||||
| expandBtnStyle(v0.5.0+) | Object | { color: '#808080', fill: '#fff', fontSize: 13, strokeColor: '#333333' } | Expand the color of the stow button, (The fontSize and strokeColor fields were added in version 0.7.0+to set the text style for displaying the number of nodes when folded) | |
|
||||
| expandBtnIcon(v0.5.0+) | Object | { open: '', close: '' } | Customize the icon of the expand/collapse button, and you can transfer the svg string of the icon | |
|
||||
| expandBtnNumHandler(v0.7.0+) | Function | | Used to customize the content of displaying the number of nodes when folding, receiving a parameter that represents the instance of the folding node, and returning a number or string that represents the final displayed content. For example, when the number is greater than 99, 99 can be displayed+ | |
|
||||
| isShowExpandNum(v0.7.0+) | Boolean | true | Display the number of folded nodes when they are folded up | |
|
||||
| enableShortcutOnlyWhenMouseInSvg(v0.5.1+) | Boolean | true | Only respond to shortcut key events when the mouse is inside the canvas | |
|
||||
| enableNodeTransitionMove(v0.5.1+)(v0.6.7+ is remove this feature) | Boolean | true | Whether to enable node animation transition | |
|
||||
| nodeTransitionMoveDuration(v0.5.1+)(v0.6.7+ is remove this feature) | Number | 300 | If node animation transition is enabled, the transition time can be set using this attribute, in milliseconds | |
|
||||
@@ -73,9 +75,18 @@ const mindMap = new MindMap({
|
||||
| mouseScaleCenterUseMousePosition(v0.6.4-fix.1+) | Boolean | true | Is the mouse zoom centered around the current position of the mouse, otherwise centered around the canvas | |
|
||||
| customInnerElsAppendTo(v0.6.12+) | null/HTMLElement | null | Specify the location where some internal elements (node text editing element, node note display element, associated line text editing element, node image adjustment button element) are added, and default to document.body | |
|
||||
| nodeDragPlaceholderMaxSize(v0.6.12+) | Number | 20 | When dragging an element, the maximum height of the block indicating the new position of the element | |
|
||||
| enableCreateHiddenInput(v0.6.13+) | Boolean | true | Is it allowed to create a hidden input box that will be focused when the node is activated for pasting data and automatically entering the text editing state | |
|
||||
| enableAutoEnterTextEditWhenKeydown(v0.6.13+) | Boolean | true | Does it automatically enter text editing mode when pressing the Chinese, English, or numeric buttons when there is an activation node? This configuration takes effect when enableCreateHiddenInput is set to true | |
|
||||
| enableCreateHiddenInput(v0.6.13+)(v0.6.14+ remove this feature) | Boolean | true | Is it allowed to create a hidden input box that will be focused when the node is activated for pasting data and automatically entering the text editing state | |
|
||||
| enableAutoEnterTextEditWhenKeydown(v0.6.13+) | Boolean | true | Does it automatically enter text editing mode when pressing the Chinese, English, or numeric buttons when there is an activation node?| |
|
||||
| richTextEditFakeInPlace(v0.6.13+) | Boolean | false | Set the rich text node edit box to match the size of the node, creating a pseudo in place editing effect. It should be noted that only when there is only text within the node and the shape is rectangular, can the effect be better | |
|
||||
| customHandleClipboardText(v0.6.14+) | Function | null | Customize the processing of clipboard text. When pressing ctrl+v to paste, it will read the text and images from the user's clipboard. By default, it will only determine whether the text is regular text and node data in simple mind map format. If you want to process data from other mind maps, such as process, zhixi, etc., you can pass a function that takes the text from the current clipboard as a parameter and returns the processed data, which can be of two types: 1.If a pure text is returned, a child node will be directly created with that text; 2.Returns a node object in the following format: { simpleMindMap: true, data: { data: { text: '' }, children: [] } }, The representative is data in simple bind map format, and the node data is in the same format as the simple bind map node data. If your processing logic has asynchronous logic, you can also return a promise | |
|
||||
| errorHandler(v0.6.15+) | Function | | Custom error handling functions currently only throw some asynchronous logic errors. Can pass a function that takes two parameters, the first being the wrong type and the second being the wrong object | |
|
||||
| disableMouseWheelZoom(v0.6.15+) | Boolean | false | Prohibit mouse wheel scaling, you can still use the API for scaling | |
|
||||
| resetCss(v0.6.16+) | String | * { margin: 0; padding: 0; box-sizing: border-box; } | When exporting images and SVGs, the default style overlay for rich text node content, which is embedded in HTML nodes in SVGs, will occur. If not overlaid, the node content will be offset | |
|
||||
| enableDblclickReset(v0.6.17+) | Boolean | true(v0.7.0+changed to false) | Turn on the mouse and double-click to reset the position and zoom of the mind map | |
|
||||
| minExportImgCanvasScale(v0.7.0+) | Number | 2 | The scaling factor of canvas when exporting images and PDFs, which is set to the maximum value of window.devicePixelRatio to improve image clarity | |
|
||||
| hoverRectColor(v0.7.0+) | String | rgb(94, 200, 248) | The node mouse hover and the rectangular border color displayed when activated will add a transparency of 0.6 when hovering | |
|
||||
| hoverRectPadding(v0.7.0+) | Number | 2 | The distance between the node mouse hover and the displayed rectangular border when activated and the node content | |
|
||||
| selectTextOnEnterEditText(v0.7.0+) | Boolean | true | Is the text selected by default when double-clicking a node to enter node text editing? By default, it will only be selected when creating a new node | |
|
||||
|
||||
### Watermark config
|
||||
|
||||
@@ -326,14 +337,14 @@ redo. All commands are as follows:
|
||||
| SELECT_ALL | Select all | |
|
||||
| BACK | Go back a specified number of steps | step (the number of steps to go back, default is 1) |
|
||||
| FORWARD | Go forward a specified number of steps | step (the number of steps to go forward, default is 1) |
|
||||
| INSERT_NODE | Insert a sibling node, the active node or appoint node will be the operation node. If there are multiple active nodes, only the first one will be effective | openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is `true`) 、 appointNodes(v0.4.7+, Optional, appoint node, Specifying multiple nodes can pass an array)、 appointData(Optional, Specify the data for the newly created node, Such as {text: 'xxx', ...}, Detailed structure can be referred to [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) ) |
|
||||
| INSERT_CHILD_NODE | Insert a child node, the active node or appoint node will be the operation node | openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is `true`)、 appointNodes(v0.4.7+, Optional, appoint node, Specifying multiple nodes can pass an array)、 appointData(Optional, Specify the data for the newly created node, Such as {text: 'xxx', ...}, Detailed structure can be referred to [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) ) |
|
||||
| INSERT_NODE | Insert a sibling node, the active node or appoint node will be the operation node. If there are multiple active nodes, only the first one will be effective | openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is `true`) 、 appointNodes(v0.4.7+, Optional, appoint node, Specifying multiple nodes can pass an array)、 appointData(Optional, Specify the data for the newly created node, Such as {text: 'xxx', ...}, Detailed structure can be referred to [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) )、 appointChildren(v0.6.14+, Optional, Specify the child nodes of the newly created node, array type) |
|
||||
| INSERT_CHILD_NODE | Insert a child node, the active node or appoint node will be the operation node | openEdit(v0.4.6+, Whether to activate the newly inserted node and enter editing mode, default is `true`)、 appointNodes(v0.4.7+, Optional, appoint node, Specifying multiple nodes can pass an array)、 appointData(Optional, Specify the data for the newly created node, Such as {text: 'xxx', ...}, Detailed structure can be referred to [exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) )、 appointChildren(v0.6.14+, Optional, Specify the child nodes of the newly created node, array type) |
|
||||
| UP_NODE | Move node up, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the first node in the list will be invalid | |
|
||||
| DOWN_NODE | Move node down, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the last node in the list will be invalid | |
|
||||
| REMOVE_NODE | Remove node, the active node or appoint node will be the operation node | appointNodes(v0.4.7+, Optional, appoint node, Specifying multiple nodes can pass an array) |
|
||||
| PASTE_NODE | Paste node to a node, the active node will be the operation node | data (the node data to paste, usually obtained through the renderer.copyNode() and renderer.cutNode() methods) |
|
||||
| SET_NODE_STYLE | Modify node single style | node (the node to set the style of), prop (style property), value (style property value), isActive (boolean, whether the style being set is for the active state) |
|
||||
| SET_NODE_STYLEs(v0.6.12+) | Modify multiple styles of nodes | node(the node to set the style of)、style(Style object,key is style prop,value is style value)、isActive(boolean, whether the style being set is for the active state) |
|
||||
| SET_NODE_STYLE | Modify node single style | node (the node to set the style of), prop (style property), value (style property value), isActive (v0.7.0+has been abandoned, boolean, whether the style being set is for the active state) |
|
||||
| SET_NODE_STYLEs(v0.6.12+) | Modify multiple styles of nodes | node(the node to set the style of)、style(Style object,key is style prop,value is style value)、isActive(v0.7.0+has been abandoned, boolean, whether the style being set is for the active state) |
|
||||
| SET_NODE_ACTIVE | Set whether the node is active | node (the node to set), active (boolean, whether to activate) |
|
||||
| CLEAR_ACTIVE_NODE | Clear the active state of the currently active node(s), the active node will be the operation node | |
|
||||
| SET_NODE_EXPAND | Set whether the node is expanded | node (the node to set), expand (boolean, whether to expand) |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,19 +38,25 @@ a.download = 'xxx'
|
||||
a.click()
|
||||
```
|
||||
|
||||
### png(name, transparent = false)
|
||||
### png(name, transparent = false, checkRotate)
|
||||
|
||||
> Versions below v0.7.0 are: png(name, transparent = false, rotateWhenWidthLongerThenHeight)
|
||||
|
||||
- `name`: Name, optional
|
||||
|
||||
- `transparent`: v0.5.7+, Specify whether the background of the exported image is transparent
|
||||
|
||||
- `rotateWhenWidthLongerThenHeight`: v0.6.15+, V0.7.0+abandoned, Boolean, false, Automatically rotate 90 degrees when the image has a width to height ratio
|
||||
|
||||
- `checkRotate`: v0.7.0+, Function, You can pass a function that takes two parameters, the width and height of the image, and returns true or false. True represents that the image needs to be rotated by 90 degrees.
|
||||
|
||||
Exports as `png`.
|
||||
|
||||
### svg(name, plusCssText)
|
||||
|
||||
- `name`:`svg` title
|
||||
|
||||
- `plusCssText`:v0.4.0+, When node rich text editing is enabled and `domToImage` passes `false`, additional `css` styles can be added. If there is a `dom` node in `svg`, you can set some styles for the node through this parameter, such as:
|
||||
- `plusCssText`:v0.4.0+, (v0.6.16+This parameter has been removed and instead passed in through the `resetCss` configuration during instantiation), When node rich text editing is enabled and `domToImage` passes `false`, additional `css` styles can be added. If there is a `dom` node in `svg`, you can set some styles for the node through this parameter, such as:
|
||||
|
||||
```js
|
||||
svg(
|
||||
@@ -66,11 +72,13 @@ svg(
|
||||
|
||||
Exports as `svg`.
|
||||
|
||||
### pdf(name)
|
||||
### pdf(name, useMultiPageExport)
|
||||
|
||||
> v0.2.1+
|
||||
|
||||
`name`:File name
|
||||
- `name`:File name
|
||||
|
||||
- `useMultiPageExport`: v0.6.15+, Boolean, false, Whether to export multiple pages, default to single page
|
||||
|
||||
Export as `pdf`. Unlike other export methods, this method does not return data and directly triggers the download.
|
||||
|
||||
|
||||
@@ -27,7 +27,10 @@ a.href = <span class="hljs-string">'xxx.png'</span><span class="hljs-c
|
||||
a.download = <span class="hljs-string">'xxx'</span>
|
||||
a.click()
|
||||
</code></pre>
|
||||
<h3>png(name, transparent = false)</h3>
|
||||
<h3>png(name, transparent = false, checkRotate)</h3>
|
||||
<blockquote>
|
||||
<p>Versions below v0.7.0 are: png(name, transparent = false, rotateWhenWidthLongerThenHeight)</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>name</code>: Name, optional</p>
|
||||
@@ -35,6 +38,12 @@ a.click()
|
||||
<li>
|
||||
<p><code>transparent</code>: v0.5.7+, Specify whether the background of the exported image is transparent</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>rotateWhenWidthLongerThenHeight</code>: v0.6.15+, V0.7.0+abandoned, Boolean, false, Automatically rotate 90 degrees when the image has a width to height ratio</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>checkRotate</code>: v0.7.0+, Function, You can pass a function that takes two parameters, the width and height of the image, and returns true or false. True represents that the image needs to be rotated by 90 degrees.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Exports as <code>png</code>.</p>
|
||||
<h3>svg(name, plusCssText)</h3>
|
||||
@@ -43,7 +52,7 @@ a.click()
|
||||
<p><code>name</code>:<code>svg</code> title</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>plusCssText</code>:v0.4.0+, When node rich text editing is enabled and <code>domToImage</code> passes <code>false</code>, additional <code>css</code> styles can be added. If there is a <code>dom</code> node in <code>svg</code>, you can set some styles for the node through this parameter, such as:</p>
|
||||
<p><code>plusCssText</code>:v0.4.0+, (v0.6.16+This parameter has been removed and instead passed in through the <code>resetCss</code> configuration during instantiation), When node rich text editing is enabled and <code>domToImage</code> passes <code>false</code>, additional <code>css</code> styles can be added. If there is a <code>dom</code> node in <code>svg</code>, you can set some styles for the node through this parameter, such as:</p>
|
||||
</li>
|
||||
</ul>
|
||||
<pre class="hljs"><code>svg(
|
||||
@@ -57,11 +66,18 @@ a.click()
|
||||
)
|
||||
</code></pre>
|
||||
<p>Exports as <code>svg</code>.</p>
|
||||
<h3>pdf(name)</h3>
|
||||
<h3>pdf(name, useMultiPageExport)</h3>
|
||||
<blockquote>
|
||||
<p>v0.2.1+</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>name</code>:File name</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>useMultiPageExport</code>: v0.6.15+, Boolean, false, Whether to export multiple pages, default to single page</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Export as <code>pdf</code>. Unlike other export methods, this method does not return data and directly triggers the download.</p>
|
||||
<blockquote>
|
||||
<p>After v0.6.0, an additional ExportPDF plugin needs to be registered</p>
|
||||
|
||||
@@ -102,9 +102,7 @@ We recommend using the latest version of the `Chrome` browser.
|
||||
|
||||
Limited testing situation:
|
||||
|
||||
Normal operation: `360` extreme speed browser(v13.5.2036.0)、`opera` browser(v71.0.3770.284)。
|
||||
|
||||
Abnormal operation: `Firefox`(v98.0.2), Node content cannot be displayed in rich text mode. If you want to support the `Firefox` browser, please use non rich text mode.
|
||||
Normal operation: `360` extreme speed browser(v13.5.2036.0)、`opera` browser(v71.0.3770.284)、`Firefox`(v98.0.2).
|
||||
|
||||
Unsupported: `IE` browser.
|
||||
|
||||
@@ -171,4 +169,27 @@ Open source is not easy. If this project is helpful to you, you can invite the a
|
||||
<img src="../../../../assets/avatar/才镇.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>才镇</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/小米.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>小米bbᯤ²ᴳ</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/棐.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>*棐</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/default.png" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>Luke</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/布林.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>布林</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/南风.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>南风</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,16 +8,16 @@
|
||||
</blockquote>
|
||||
<h2>Features</h2>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox30" checked="true" /><label for="checkbox30">Pluggable architecture, in addition to core functions, other functions are provided as plugins, which can be used as needed to reduce packaging volume</label></li>
|
||||
<li><input type="checkbox" id="checkbox31" checked="true" /><label for="checkbox31">Support logical structure chart, mind map, Organizational chart, directory organization chart, timeline (horizontal and vertical), fishbone chart and other structures</label></li>
|
||||
<li><input type="checkbox" id="checkbox32" checked="true" /><label for="checkbox32">Built-in multiple themes, allowing for highly customizable styles, and supporting registration of new themes</label></li>
|
||||
<li><input type="checkbox" id="checkbox33" checked="true" /><label for="checkbox33">Node content supports text (regular text, rich text), images, icons, hyperlinks, notes, labels, and summaries</label></li>
|
||||
<li><input type="checkbox" id="checkbox34" checked="true" /><label for="checkbox34">Nodes support drag and drop (drag and move, freely adjust), multiple node shapes, and fully customize node content using DDM</label></li>
|
||||
<li><input type="checkbox" id="checkbox35" checked="true" /><label for="checkbox35">Support canvas dragging and scaling</label></li>
|
||||
<li><input type="checkbox" id="checkbox36" checked="true" /><label for="checkbox36">Supports two multi node selection methods: mouse button drag selection and Ctrl+left button selection</label></li>
|
||||
<li><input type="checkbox" id="checkbox37" checked="true" /><label for="checkbox37">Supoorts to export as </label><code>json</code>、<code>png</code>、<code>svg</code>、<code>pdf</code>、<code>markdown</code>、<code>xmind</code>, support import from <code>json</code>、<code>xmind</code>、<code>markdown</code></li>
|
||||
<li><input type="checkbox" id="checkbox38" checked="true" /><label for="checkbox38">Support shortcut keys, forward and backward, correlation lines, search and replacement, small maps, and watermarks</label></li>
|
||||
<li><input type="checkbox" id="checkbox39" checked="true" /><label for="checkbox39">Provide rich configurations to meet various scenarios and usage habits</label></li>
|
||||
<li><input type="checkbox" id="checkbox15" checked="true" /><label for="checkbox15">Pluggable architecture, in addition to core functions, other functions are provided as plugins, which can be used as needed to reduce packaging volume</label></li>
|
||||
<li><input type="checkbox" id="checkbox16" checked="true" /><label for="checkbox16">Support logical structure chart, mind map, Organizational chart, directory organization chart, timeline (horizontal and vertical), fishbone chart and other structures</label></li>
|
||||
<li><input type="checkbox" id="checkbox17" checked="true" /><label for="checkbox17">Built-in multiple themes, allowing for highly customizable styles, and supporting registration of new themes</label></li>
|
||||
<li><input type="checkbox" id="checkbox18" checked="true" /><label for="checkbox18">Node content supports text (regular text, rich text), images, icons, hyperlinks, notes, labels, and summaries</label></li>
|
||||
<li><input type="checkbox" id="checkbox19" checked="true" /><label for="checkbox19">Nodes support drag and drop (drag and move, freely adjust), multiple node shapes, and fully customize node content using DDM</label></li>
|
||||
<li><input type="checkbox" id="checkbox20" checked="true" /><label for="checkbox20">Support canvas dragging and scaling</label></li>
|
||||
<li><input type="checkbox" id="checkbox21" checked="true" /><label for="checkbox21">Supports two multi node selection methods: mouse button drag selection and Ctrl+left button selection</label></li>
|
||||
<li><input type="checkbox" id="checkbox22" checked="true" /><label for="checkbox22">Supoorts to export as </label><code>json</code>、<code>png</code>、<code>svg</code>、<code>pdf</code>、<code>markdown</code>、<code>xmind</code>, support import from <code>json</code>、<code>xmind</code>、<code>markdown</code></li>
|
||||
<li><input type="checkbox" id="checkbox23" checked="true" /><label for="checkbox23">Support shortcut keys, forward and backward, correlation lines, search and replacement, small maps, and watermarks</label></li>
|
||||
<li><input type="checkbox" id="checkbox24" checked="true" /><label for="checkbox24">Provide rich configurations to meet various scenarios and usage habits</label></li>
|
||||
</ul>
|
||||
<h2>Repository Catalog Introduction</h2>
|
||||
<p>1.<code>simple-mind-map</code></p>
|
||||
@@ -27,16 +27,16 @@ frameworks such as Vue and React, or without a framework.</p>
|
||||
<p>This is an online mind map built using the <code>simple-mind-map</code> library and based
|
||||
on <code>Vue2.x</code> and <code>ElementUI</code>. Features include:</p>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox40" checked="true" /><label for="checkbox40">Toolbar, which supports inserting and deleting nodes, and editing node</label>
|
||||
<li><input type="checkbox" id="checkbox25" checked="true" /><label for="checkbox25">Toolbar, which supports inserting and deleting nodes, and editing node</label>
|
||||
images, icons, hyperlinks, notes, tags, and summaries</li>
|
||||
<li><input type="checkbox" id="checkbox41" checked="true" /><label for="checkbox41">Sidebar, with panels for basic style settings, node style settings,</label>
|
||||
<li><input type="checkbox" id="checkbox26" checked="true" /><label for="checkbox26">Sidebar, with panels for basic style settings, node style settings,</label>
|
||||
outline, theme selection, and structure selection</li>
|
||||
<li><input type="checkbox" id="checkbox42" checked="true" /><label for="checkbox42">Import and export functionality; data is saved in the browser's local</label>
|
||||
<li><input type="checkbox" id="checkbox27" checked="true" /><label for="checkbox27">Import and export functionality; data is saved in the browser's local</label>
|
||||
storage by default, but it also supports creating, opening, and editing
|
||||
local files on the computer directly</li>
|
||||
<li><input type="checkbox" id="checkbox43" checked="true" /><label for="checkbox43">Right-click menu, which supports operations such as expanding, collapsing,</label>
|
||||
<li><input type="checkbox" id="checkbox28" checked="true" /><label for="checkbox28">Right-click menu, which supports operations such as expanding, collapsing,</label>
|
||||
and organizing layout</li>
|
||||
<li><input type="checkbox" id="checkbox44" checked="true" /><label for="checkbox44">Bottom bar, which supports node and word count statistics, switching</label>
|
||||
<li><input type="checkbox" id="checkbox29" checked="true" /><label for="checkbox29">Bottom bar, which supports node and word count statistics, switching</label>
|
||||
between edit and read-only modes, zooming in and out, and switching to
|
||||
full screen, support mini map</li>
|
||||
</ul>
|
||||
@@ -69,8 +69,7 @@ full screen, support mini map</li>
|
||||
<h2>Browser Compatibility</h2>
|
||||
<p>We recommend using the latest version of the <code>Chrome</code> browser.</p>
|
||||
<p>Limited testing situation:</p>
|
||||
<p>Normal operation: <code>360</code> extreme speed browser(v13.5.2036.0)、<code>opera</code> browser(v71.0.3770.284)。</p>
|
||||
<p>Abnormal operation: <code>Firefox</code>(v98.0.2), Node content cannot be displayed in rich text mode. If you want to support the <code>Firefox</code> browser, please use non rich text mode.</p>
|
||||
<p>Normal operation: <code>360</code> extreme speed browser(v13.5.2036.0)、<code>opera</code> browser(v71.0.3770.284)、<code>Firefox</code>(v98.0.2).</p>
|
||||
<p>Unsupported: <code>IE</code> browser.</p>
|
||||
<h2>License</h2>
|
||||
<p><a href="https://opensource.org/licenses/MIT">MIT</a></p>
|
||||
@@ -130,6 +129,28 @@ full screen, support mini map</li>
|
||||
<img src="../../../../assets/avatar/才镇.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>才镇</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/小米.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>小米bbᯤ²ᴳ</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/棐.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>*棐</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/default.png" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>Luke</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/布林.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>布林</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/南风.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>南风</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -110,17 +110,21 @@ Get the final style value applied to this node
|
||||
|
||||
`root`: whether it is the root node, default `false`
|
||||
|
||||
`isActive`: whether the value being fetched is the active state style value,
|
||||
`isActive`: v0.7.0+has been abandoned, whether the value being fetched is the active state style value,
|
||||
default `false`
|
||||
|
||||
### setStyle(prop, value, isActive)
|
||||
|
||||
- `isActive`: v0.7.0+has been abandoned
|
||||
|
||||
Modify a style of the node, a shortcut method for the `SET_NODE_STYLE` command
|
||||
|
||||
### setStyles(style, isActive)
|
||||
|
||||
> v0.6.12+
|
||||
|
||||
- `isActive`: v0.7.0+has been abandoned
|
||||
|
||||
Modify multiple styles of nodes, a shortcut method for the `SET_NODE_STYLES` command
|
||||
|
||||
### getData(key)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user