mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-21 00:17:06 +08:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eff4cd0e77 | ||
|
|
82c2d848a9 | ||
|
|
0344599411 | ||
|
|
19fa0af6c0 | ||
|
|
bca1a073f7 | ||
|
|
98fb23bf7c | ||
|
|
1c9c399b76 | ||
|
|
bc43fedd87 | ||
|
|
156054ed93 | ||
|
|
937f7d2969 | ||
|
|
e56a6d36cb | ||
|
|
9f19061010 | ||
|
|
0d465f28f3 | ||
|
|
b4fdcd81b0 | ||
|
|
38c0fe2e39 | ||
|
|
29ddbba9b9 | ||
|
|
9f9ed1e84f | ||
|
|
9ebc416167 | ||
|
|
5d49d985c0 | ||
|
|
c36338a794 | ||
|
|
c21ee4960e | ||
|
|
5090f21b0e | ||
|
|
3d9d172fa0 | ||
|
|
766ce310d0 | ||
|
|
075bf54d28 | ||
|
|
14ebd7a239 | ||
|
|
ac13aa8bc9 | ||
|
|
f4d84aeb55 | ||
|
|
9b7305de1e | ||
|
|
ef526fe302 | ||
|
|
007a5f2815 | ||
|
|
e04b680cdc | ||
|
|
156c866bc1 | ||
|
|
62b734890b | ||
|
|
c7f3dd4d7e | ||
|
|
5014a2feb7 | ||
|
|
8f8c6c9d95 | ||
|
|
c8d5a34640 | ||
|
|
bd0fc37f03 | ||
|
|
c05d947fa3 | ||
|
|
1f303145c6 | ||
|
|
453e7311b8 | ||
|
|
c12b7f6dae | ||
|
|
7ba11be42b | ||
|
|
ce49fcb511 | ||
|
|
06fb6245b7 | ||
|
|
fa8a80792d | ||
|
|
570bbb1b16 | ||
|
|
4e327c3a48 | ||
|
|
89d89f4dd8 | ||
|
|
5ae998f304 | ||
|
|
428c4fd93b | ||
|
|
2bcc4a7c18 | ||
|
|
9229f13172 | ||
|
|
afdb557a49 | ||
|
|
62e25cdf86 | ||
|
|
d2562f35bd |
22
README.md
22
README.md
@@ -44,7 +44,7 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。百度云
|
||||
|
||||
官方提供了如下插件,可根据需求按需引入(某个功能不生效大概率是因为你没有引入对应的插件),具体使用方式请查看文档:
|
||||
|
||||
> RichText(节点富文本插件)、Select(鼠标多选节点插件)、Drag(节点拖拽插件)、AssociativeLine(关联线插件)、Export(导出插件)、KeyboardNavigation(键盘导航插件)、MiniMap(小地图插件)、Watermark(水印插件)、TouchEvent(移动端触摸事件支持插件)、NodeImgAdjust(拖拽调整节点图片大小插件)、Search(搜索插件)、Painter(节点格式刷插件)、Scrollbar(滚动条插件)、Formula(数学公式插件)、Cooperate(协同编辑插件)、RainbowLines(彩虹线条插件)、Demonstrate(演示模式插件)、OuterFrame(外框插件)、HandDrawnLikeStyle(手绘风格插件)[收费]、Notation(节点标记插件)[收费]、Numbers(节点编号插件)[收费]
|
||||
> RichText(节点富文本插件)、Select(鼠标多选节点插件)、Drag(节点拖拽插件)、AssociativeLine(关联线插件)、Export(导出插件)、KeyboardNavigation(键盘导航插件)、MiniMap(小地图插件)、Watermark(水印插件)、TouchEvent(移动端触摸事件支持插件)、NodeImgAdjust(拖拽调整节点图片大小插件)、Search(搜索插件)、Painter(节点格式刷插件)、Scrollbar(滚动条插件)、Formula(数学公式插件)、Cooperate(协同编辑插件)、RainbowLines(彩虹线条插件)、Demonstrate(演示模式插件)、OuterFrame(外框插件)、HandDrawnLikeStyle(手绘风格插件)[收费]、Notation(节点标记插件)[收费]、Numbers(节点编号插件)[收费]、Freemind(Freemind格式导入导出插件)[收费]、Excel(Excel格式导入导出插件)[收费]
|
||||
|
||||
本项目不会实现的特性:
|
||||
|
||||
@@ -457,4 +457,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/Lawliet.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Lawliet</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>晏江</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>Eric</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Joe.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Joe</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
2
copy.js
2
copy.js
@@ -13,4 +13,4 @@ if (fs.existsSync(src)) {
|
||||
fs.unlinkSync(src)
|
||||
}
|
||||
|
||||
console.warn('请检查手绘风格、标记插件、编号插件是否启用!!!')
|
||||
console.warn('请检查付费插件是否启用!!!')
|
||||
2
dist/css/app.css
vendored
2
dist/css/app.css
vendored
@@ -1 +1 @@
|
||||
*{margin:0;padding:0;box-sizing:border-box}#app{font-family:Avenir,Helvetica,Arial,sans-serif;color:#2c3e50}@font-face{font-family:iconfont;src:url(../fonts/iconfont.woff2) format("woff2"),url(../fonts/iconfont.woff) format("woff"),url(../fonts/iconfont.ttf) format("truetype")}#app,.iconfont{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.iconfont{font-family:iconfont!important;font-size:16px;font-style:normal}.iconwaikuang:before{content:"\e640"}.iconhighlight:before{content:"\e6b8"}.iconyanshibofang:before{content:"\e648"}.iconfujian:before{content:"\e88a"}.icongeshihua:before{content:"\e7a3"}.iconyuanma:before{content:"\e658"}.icongundongtiao:before{content:"\e670"}.iconxietongwendang:before{content:"\e60d"}.iconTXT:before{content:"\e6e1"}.iconwenjian1:before{content:"\e69f"}.icondodeparent:before{content:"\e70f"}.icongongshi:before{content:"\e617"}.icontouming:before{content:"\e60c"}.iconlieri:before{content:"\e60b"}.iconmoon_line:before{content:"\e745"}.iconsousuo:before{content:"\e693"}.iconjiantouyou:before{content:"\e62d"}.iconbianji1:before{content:"\e60a"}.icondaohang1:before{content:"\e632"}.iconyanjing:before{content:"\e8bf"}.iconwangzhan:before{content:"\e628"}.iconcsdn:before{content:"\e608"}.iconshejiaotubiao-10:before{content:"\e644"}.iconstar:before{content:"\e7df"}.iconfork:before{content:"\e641"}.iconxiazai:before{content:"\e613"}.iconteamwork:before{content:"\e870"}.iconshuiyin:before{content:"\e67a"}.iconxmind:before{content:"\ea57"}.iconmouseR:before{content:"\e6bd"}.iconmouseL:before{content:"\e6c0"}.iconwenjian:before{content:"\e607"}.iconpdf:before{content:"\e740"}.iconPNG:before{content:"\ec18"}.iconSVG:before{content:"\e621"}.iconmarkdown:before{content:"\ec04"}.iconjson:before{content:"\ea42"}.iconlianjiexian:before{content:"\e75b"}.iconbangzhu:before{content:"\e620"}.iconshezhi:before{content:"\e8b7"}.iconwushuju:before{content:"\e643"}.iconzuijinliulan:before{content:"\e62f"}.icon3zuidahua-3:before{content:"\e692"}.iconzuixiaohua:before{content:"\e650"}.iconzuidahua:before{content:"\e651"}.iconguanbi:before{content:"\e652"}.icondiannao:before{content:"\eac0"}.iconzhuye:before{content:"\e65c"}.iconbendi1x:before{content:"\e606"}.iconbeijingyanse:before{content:"\e6f8"}.iconqingchu:before{content:"\e605"}.iconcase:before{content:"\e6c6"}.iconxingzhuang-wenzi:before{content:"\eb99"}.iconzitijiacu:before{content:"\ec83"}.iconzitixiahuaxian:before{content:"\ec85"}.iconzitixieti:before{content:"\ec86"}.iconshanchuxian:before{content:"\e612"}.iconzitiyanse:before{content:"\e854"}.icongithub:before{content:"\e64f"}.iconchoose1:before{content:"\e6c5"}.iconzhuti:before{content:"\e7aa"}.icondaochu1:before{content:"\e63e"}.iconlingcunwei:before{content:"\e657"}.iconexport:before{content:"\e642"}.icondakai:before{content:"\ebdf"}.iconxinjian:before{content:"\e64e"}.iconjianqie:before{content:"\e601"}.iconzhengli:before{content:"\e83b"}.iconfuzhi:before{content:"\e604"}.iconniantie:before{content:"\e63f"}.iconshangyi:before{content:"\e6be"}.iconxiayi:before{content:"\e6bf"}.icongaikuozonglan:before{content:"\e609"}.iconquanxuan:before{content:"\f199"}.icondaoru:before{content:"\e6a3"}.iconhoutui-shi:before{content:"\e656"}.iconqianjin1:before{content:"\e654"}.iconwithdraw:before{content:"\e603"}.iconqianjin:before{content:"\e600"}.iconhuifumoren:before{content:"\e60e"}.iconhuanhang:before{content:"\e61e"}.iconsuoxiao:before{content:"\ec13"}.iconbianji:before{content:"\e626"}.iconfangda:before{content:"\e663"}.iconquanping1:before{content:"\e664"}.icondingwei:before{content:"\e616"}.icondaohang:before{content:"\e611"}.iconjianpan:before{content:"\e64d"}.iconquanping:before{content:"\e602"}.icondaochu:before{content:"\e63d"}.iconbiaoqian:before{content:"\e63c"}.iconflow-Mark:before{content:"\e65b"}.iconchaolianjie:before{content:"\e6f4"}.iconjingzi:before{content:"\e610"}.iconxiaolian:before{content:"\e60f"}.iconimage:before{content:"\e629"}.iconjiegou:before{content:"\e61d"}.iconyangshi:before{content:"\e631"}.iconfuhao-dagangshu:before{content:"\e71f"}.icontianjiazijiedian:before{content:"\e622"}.iconjiedian:before{content:"\e655"}.iconshanchu:before{content:"\e696"}.iconzhankai:before{content:"\e64c"}.iconzhankai1:before{content:"\e673"}
|
||||
*{margin:0;padding:0;box-sizing:border-box}#app{font-family:Avenir,Helvetica,Arial,sans-serif;color:#2c3e50}@font-face{font-family:iconfont;src:url(../fonts/iconfont.woff2) format("woff2"),url(../fonts/iconfont.woff) format("woff"),url(../fonts/iconfont.ttf) format("truetype")}#app,.iconfont{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.iconfont{font-family:iconfont!important;font-size:16px;font-style:normal}.iconfile-excel:before{content:"\e7b7"}.iconfreemind:before{content:"\e97d"}.iconwaikuang:before{content:"\e640"}.iconhighlight:before{content:"\e6b8"}.iconyanshibofang:before{content:"\e648"}.iconfujian:before{content:"\e88a"}.icongeshihua:before{content:"\e7a3"}.iconyuanma:before{content:"\e658"}.icongundongtiao:before{content:"\e670"}.iconxietongwendang:before{content:"\e60d"}.iconTXT:before{content:"\e6e1"}.iconwenjian1:before{content:"\e69f"}.icondodeparent:before{content:"\e70f"}.icongongshi:before{content:"\e617"}.icontouming:before{content:"\e60c"}.iconlieri:before{content:"\e60b"}.iconmoon_line:before{content:"\e745"}.iconsousuo:before{content:"\e693"}.iconjiantouyou:before{content:"\e62d"}.iconbianji1:before{content:"\e60a"}.icondaohang1:before{content:"\e632"}.iconyanjing:before{content:"\e8bf"}.iconwangzhan:before{content:"\e628"}.iconcsdn:before{content:"\e608"}.iconshejiaotubiao-10:before{content:"\e644"}.iconstar:before{content:"\e7df"}.iconfork:before{content:"\e641"}.iconxiazai:before{content:"\e613"}.iconteamwork:before{content:"\e870"}.iconshuiyin:before{content:"\e67a"}.iconxmind:before{content:"\ea57"}.iconmouseR:before{content:"\e6bd"}.iconmouseL:before{content:"\e6c0"}.iconwenjian:before{content:"\e607"}.iconpdf:before{content:"\e740"}.iconPNG:before{content:"\ec18"}.iconSVG:before{content:"\e621"}.iconmarkdown:before{content:"\ec04"}.iconjson:before{content:"\ea42"}.iconlianjiexian:before{content:"\e75b"}.iconbangzhu:before{content:"\e620"}.iconshezhi:before{content:"\e8b7"}.iconwushuju:before{content:"\e643"}.iconzuijinliulan:before{content:"\e62f"}.icon3zuidahua-3:before{content:"\e692"}.iconzuixiaohua:before{content:"\e650"}.iconzuidahua:before{content:"\e651"}.iconguanbi:before{content:"\e652"}.icondiannao:before{content:"\eac0"}.iconzhuye:before{content:"\e65c"}.iconbendi1x:before{content:"\e606"}.iconbeijingyanse:before{content:"\e6f8"}.iconqingchu:before{content:"\e605"}.iconcase:before{content:"\e6c6"}.iconxingzhuang-wenzi:before{content:"\eb99"}.iconzitijiacu:before{content:"\ec83"}.iconzitixiahuaxian:before{content:"\ec85"}.iconzitixieti:before{content:"\ec86"}.iconshanchuxian:before{content:"\e612"}.iconzitiyanse:before{content:"\e854"}.icongithub:before{content:"\e64f"}.iconchoose1:before{content:"\e6c5"}.iconzhuti:before{content:"\e7aa"}.icondaochu1:before{content:"\e63e"}.iconlingcunwei:before{content:"\e657"}.iconexport:before{content:"\e642"}.icondakai:before{content:"\ebdf"}.iconxinjian:before{content:"\e64e"}.iconjianqie:before{content:"\e601"}.iconzhengli:before{content:"\e83b"}.iconfuzhi:before{content:"\e604"}.iconniantie:before{content:"\e63f"}.iconshangyi:before{content:"\e6be"}.iconxiayi:before{content:"\e6bf"}.icongaikuozonglan:before{content:"\e609"}.iconquanxuan:before{content:"\f199"}.icondaoru:before{content:"\e6a3"}.iconhoutui-shi:before{content:"\e656"}.iconqianjin1:before{content:"\e654"}.iconwithdraw:before{content:"\e603"}.iconqianjin:before{content:"\e600"}.iconhuifumoren:before{content:"\e60e"}.iconhuanhang:before{content:"\e61e"}.iconsuoxiao:before{content:"\ec13"}.iconbianji:before{content:"\e626"}.iconfangda:before{content:"\e663"}.iconquanping1:before{content:"\e664"}.icondingwei:before{content:"\e616"}.icondaohang:before{content:"\e611"}.iconjianpan:before{content:"\e64d"}.iconquanping:before{content:"\e602"}.icondaochu:before{content:"\e63d"}.iconbiaoqian:before{content:"\e63c"}.iconflow-Mark:before{content:"\e65b"}.iconchaolianjie:before{content:"\e6f4"}.iconjingzi:before{content:"\e610"}.iconxiaolian:before{content:"\e60f"}.iconimage:before{content:"\e629"}.iconjiegou:before{content:"\e61d"}.iconyangshi:before{content:"\e631"}.iconfuhao-dagangshu:before{content:"\e71f"}.icontianjiazijiedian:before{content:"\e622"}.iconjiedian:before{content:"\e655"}.iconshanchu:before{content:"\e696"}.iconzhankai:before{content:"\e64c"}.iconzhankai1:before{content:"\e673"}
|
||||
File diff suppressed because one or more lines are too long
BIN
dist/fonts/iconfont.ttf
vendored
BIN
dist/fonts/iconfont.ttf
vendored
Binary file not shown.
BIN
dist/fonts/iconfont.woff
vendored
BIN
dist/fonts/iconfont.woff
vendored
Binary file not shown.
BIN
dist/fonts/iconfont.woff2
vendored
BIN
dist/fonts/iconfont.woff2
vendored
Binary file not shown.
2
dist/js/app.js
vendored
2
dist/js/app.js
vendored
File diff suppressed because one or more lines are too long
69
dist/js/chunk-4cff5316.js
vendored
Normal file
69
dist/js/chunk-4cff5316.js
vendored
Normal file
File diff suppressed because one or more lines are too long
69
dist/js/chunk-9e0371c2.js
vendored
69
dist/js/chunk-9e0371c2.js
vendored
File diff suppressed because one or more lines are too long
14
dist/js/chunk-vendors.js
vendored
14
dist/js/chunk-vendors.js
vendored
File diff suppressed because one or more lines are too long
@@ -9,7 +9,7 @@
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}</script><link href="dist/css/chunk-vendors.css?1c8f9269e64b9476f0c7" rel="stylesheet"><link href="dist/css/app.css?1c8f9269e64b9476f0c7" 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 = () => {
|
||||
}</script><link href="dist/css/chunk-vendors.css?9c8ee1f3de5ddc8a450e" rel="stylesheet"><link href="dist/css/app.css?9c8ee1f3de5ddc8a450e" 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({
|
||||
@@ -74,4 +74,4 @@
|
||||
// 可以通过window.$bus.$on()来监听应用的一些事件
|
||||
// 实例化页面
|
||||
window.initApp()
|
||||
}</script><script src="dist/js/chunk-vendors.js?1c8f9269e64b9476f0c7"></script><script src="dist/js/app.js?1c8f9269e64b9476f0c7"></script></body></html>
|
||||
}</script><script src="dist/js/chunk-vendors.js?9c8ee1f3de5ddc8a450e"></script><script src="dist/js/app.js?9c8ee1f3de5ddc8a450e"></script></body></html>
|
||||
BIN
qrcode.jpg
BIN
qrcode.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
@@ -31,7 +31,7 @@ MindMap.iconList = icons.nodeIconList
|
||||
MindMap.constants = constants
|
||||
MindMap.themes = themes
|
||||
MindMap.defaultTheme = defaultTheme
|
||||
MindMap.version = '0.11.0'
|
||||
MindMap.version = '0.11.2'
|
||||
|
||||
MindMap.usePlugin(MiniMap)
|
||||
.usePlugin(Watermark)
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
getObjectChangedProps,
|
||||
isUndef,
|
||||
handleGetSvgDataExtraContent,
|
||||
getNodeTreeBoundingRect
|
||||
getNodeTreeBoundingRect,
|
||||
mergeTheme
|
||||
} from './src/utils'
|
||||
import defaultTheme, {
|
||||
checkIsNodeSizeIndependenceConfig
|
||||
@@ -34,6 +35,7 @@ class MindMap {
|
||||
* @param {defaultOpt} opt
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
MindMap.instanceCount++
|
||||
// 合并选项
|
||||
this.opt = this.handleOpt(merge(defaultOpt, opt))
|
||||
// 预处理节点数据
|
||||
@@ -252,7 +254,7 @@ class MindMap {
|
||||
// 设置主题
|
||||
initTheme() {
|
||||
// 合并主题配置
|
||||
this.themeConfig = merge(theme[this.opt.theme], this.opt.themeConfig)
|
||||
this.themeConfig = mergeTheme(theme[this.opt.theme], this.opt.themeConfig)
|
||||
// 设置背景样式
|
||||
Style.setBackgroundStyle(this.el, this.themeConfig)
|
||||
}
|
||||
@@ -563,8 +565,8 @@ class MindMap {
|
||||
let index = MindMap.hasPlugin(plugin)
|
||||
if (index === -1) {
|
||||
MindMap.usePlugin(plugin, opt)
|
||||
this.initPlugin(plugin)
|
||||
}
|
||||
this.initPlugin(plugin)
|
||||
}
|
||||
|
||||
// 移除插件
|
||||
@@ -583,6 +585,7 @@ class MindMap {
|
||||
|
||||
// 实例化插件
|
||||
initPlugin(plugin) {
|
||||
if (this[plugin.instanceName]) return
|
||||
this[plugin.instanceName] = new plugin({
|
||||
mindMap: this,
|
||||
pluginOpt: plugin.pluginOpt
|
||||
@@ -616,6 +619,7 @@ class MindMap {
|
||||
this.el.innerHTML = ''
|
||||
this.el = null
|
||||
this.removeCss()
|
||||
MindMap.instanceCount--
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,13 +636,14 @@ MindMap.hasPlugin = plugin => {
|
||||
return item === plugin
|
||||
})
|
||||
}
|
||||
MindMap.instanceCount = 0
|
||||
|
||||
// 定义新主题
|
||||
MindMap.defineTheme = (name, config = {}) => {
|
||||
if (theme[name]) {
|
||||
return new Error('该主题名称已存在')
|
||||
}
|
||||
theme[name] = merge(defaultTheme, config)
|
||||
theme[name] = mergeTheme(defaultTheme, config)
|
||||
}
|
||||
|
||||
export default MindMap
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.11.0",
|
||||
"version": "0.11.2",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -328,7 +328,9 @@ export const nodeDataNoStylePropList = [
|
||||
'notation',
|
||||
'outerFrame',
|
||||
'number',
|
||||
'range'
|
||||
'range',
|
||||
'customLeft',
|
||||
'customTop'
|
||||
]
|
||||
|
||||
// 错误类型
|
||||
|
||||
@@ -19,6 +19,16 @@ export const defaultOpt = {
|
||||
themeConfig: {},
|
||||
// 放大缩小的增量比例
|
||||
scaleRatio: 0.2,
|
||||
// 平移的步长比例,只在鼠标滚轮和触控板触发的平移中应用
|
||||
translateRatio: 1,
|
||||
// 最小缩小值,百分数,最小为0,该选项只会影响view.narrow方法(影响的行为为Ctrl+-快捷键、鼠标滚轮及触控板),不会影响其他方法,比如view.setScale,所以需要你自行限制大小
|
||||
minZoomRatio: 20,
|
||||
// 最大放大值,百分数,传-1代表不限制,否则传0以上数字,,该选项只会影响view.enlarge方法
|
||||
maxZoomRatio: 400,
|
||||
// 自定义判断wheel事件是否来自电脑的触控板
|
||||
// 默认是通过判断e.deltaY的值是否小于10,显然这种方法是不准确的,当鼠标滚动的很慢,或者触摸移动的很快时判断就失效了,如果你有更好的方法,欢迎提交issue
|
||||
// 如果你希望自己来判断,那么传递一个函数,接收一个参数e(事件对象),需要返回true或false,代表是否是来自触控板
|
||||
customCheckIsTouchPad: null,
|
||||
// 鼠标缩放是否以鼠标当前位置为中心点,否则以画布中心点
|
||||
mouseScaleCenterUseMousePosition: true,
|
||||
// 最多显示几个标签
|
||||
@@ -67,9 +77,7 @@ export const defaultOpt = {
|
||||
close: ''
|
||||
},
|
||||
// 处理收起节点数量
|
||||
expandBtnNumHandler: num => {
|
||||
return num
|
||||
},
|
||||
expandBtnNumHandler: null,
|
||||
// 是否显示带数量的收起按钮
|
||||
isShowExpandNum: true,
|
||||
// 是否只有当鼠标在画布内才响应快捷键事件
|
||||
@@ -176,11 +184,6 @@ export const defaultOpt = {
|
||||
addHistoryTime: 100,
|
||||
// 是否禁止拖动画布
|
||||
isDisableDrag: false,
|
||||
// 鼠标移入概要高亮所属节点时的高亮框样式
|
||||
highlightNodeBoxStyle: {
|
||||
stroke: 'rgb(94, 200, 248)',
|
||||
fill: 'transparent'
|
||||
},
|
||||
// 创建新节点时的行为
|
||||
/*
|
||||
DEFAULT :默认会激活新创建的节点,并且进入编辑模式。如果同时创建了多个新节点,那么只会激活而不会进入编辑模式
|
||||
@@ -238,6 +241,12 @@ export const defaultOpt = {
|
||||
padding: 100, // 超出画布四周指定范围内依旧渲染节点
|
||||
removeNodeWhenOutCanvas: true // 节点移除画布可视区域后从画布删除
|
||||
},
|
||||
// 如果节点文本为空,那么为了避免空白节点高度塌陷,会用该字段指定的文本测量一个高度
|
||||
emptyTextMeasureHeightText: 'abc123我和你',
|
||||
// 是否在进行节点文本编辑时实时更新节点大小和节点位置,开启后当节点数量比较多时可能会造成卡顿
|
||||
openRealtimeRenderOnNodeTextEdit: false,
|
||||
// 默认会给容器元素el绑定mousedown事件,并且会阻止其默认事件,这会带来一定问题,比如你聚焦在思维导图外的其他输入框,点击画布就不会触发其失焦,可以通过该选项关闭阻止。关闭后也会带来一定问题,比如鼠标框选节点时可能会选中节点文字,看你如何取舍
|
||||
mousedownEventPreventDefault: true,
|
||||
|
||||
// 【Select插件】
|
||||
// 多选节点时鼠标移动到边缘时的画布移动偏移量
|
||||
@@ -321,6 +330,8 @@ export const defaultOpt = {
|
||||
// 导出png、svg、pdf时会获取画布上的svg数据进行克隆,然后通过该克隆的元素进行导出,如果你想对该克隆元素做一些处理,比如新增、替换、修改其中的一些元素,那么可以通过该参数传递一个处理函数,接收svg元素对象,处理后,需要返回原svg元素对象。
|
||||
// 需要注意的是svg对象指的是@svgdotjs/svg.js库的元素对象,所以你需要阅读该库的文档来操作该对象
|
||||
handleBeingExportSvg: null,
|
||||
// 导出图片或pdf都是通过canvas将svg绘制出来,再导出,所以如果思维导图特别大,宽高可能会超出canvas支持的上限,所以会进行缩放,这个上限可以通过该参数设置,代表canvas宽和高的最大宽度
|
||||
maxCanvasSize: 16384,
|
||||
|
||||
// 【AssociativeLine插件】
|
||||
// 关联线默认文字
|
||||
@@ -409,5 +420,13 @@ export const defaultOpt = {
|
||||
|
||||
// 【OuterFrame】插件
|
||||
outerFramePaddingX: 10,
|
||||
outerFramePaddingY: 10
|
||||
outerFramePaddingY: 10,
|
||||
|
||||
// 【Painter】插件
|
||||
// 是否只格式刷节点手动设置的样式,不考虑节点通过主题的应用的样式
|
||||
onlyPainterNodeCustomStyles: false,
|
||||
|
||||
// 【NodeImgAdjust】插件
|
||||
// 拦截节点图片的删除,点击节点图片上的删除按钮删除图片前会调用该函数,如果函数返回true则取消删除
|
||||
beforeDeleteNodeImg: null
|
||||
}
|
||||
|
||||
@@ -156,8 +156,14 @@ class Event extends EventEmitter {
|
||||
// 判断是否是触控板
|
||||
let isTouchPad = false
|
||||
// mac、windows
|
||||
if (e.wheelDeltaY === e.deltaY * -3 || Math.abs(e.wheelDeltaY) <= 10) {
|
||||
isTouchPad = true
|
||||
// if (e.wheelDeltaY === e.deltaY * -3 || Math.abs(e.wheelDeltaY) <= 10) {
|
||||
// isTouchPad = true
|
||||
// }
|
||||
const { customCheckIsTouchPad } = this.mindMap.opt
|
||||
if (typeof customCheckIsTouchPad === 'function') {
|
||||
isTouchPad = customCheckIsTouchPad(e)
|
||||
} else {
|
||||
isTouchPad = Math.abs(e.deltaY) <= 10
|
||||
}
|
||||
this.emit('mousewheel', e, dirs, this, isTouchPad)
|
||||
}
|
||||
|
||||
@@ -97,8 +97,10 @@ class Render {
|
||||
this.beingPasteText = ''
|
||||
this.beingPasteImgSize = 0
|
||||
this.currentBeingPasteType = ''
|
||||
this.pasteData = { text: null, img: null }
|
||||
// 节点高亮框
|
||||
this.highlightBoxNode = null
|
||||
this.highlightBoxNodeStyle = null
|
||||
// 上一次节点激活数据
|
||||
this.lastActiveNode = null
|
||||
this.lastActiveNodeList = []
|
||||
@@ -147,6 +149,28 @@ class Render {
|
||||
})
|
||||
// 性能模式
|
||||
this.performanceMode()
|
||||
// 实时渲染当节点文本编辑时
|
||||
if (this.mindMap.opt.openRealtimeRenderOnNodeTextEdit) {
|
||||
this.mindMap.on('node_text_edit_change', ({ node, text }) => {
|
||||
node._textData = node.createTextNode(text)
|
||||
const { width, height } = node.getNodeRect()
|
||||
node.width = width
|
||||
node.height = height
|
||||
node.layout()
|
||||
this.mindMap.render(() => {
|
||||
this.textEdit.updateTextEditNode()
|
||||
})
|
||||
})
|
||||
}
|
||||
// 处理非https下的复制黏贴问题
|
||||
// 暂时不启用,因为给页面的其他输入框(比如节点文本编辑框)粘贴内容也会触发,冲突问题暂时没有想到好的解决方法,不可能要求所有输入框都阻止冒泡
|
||||
// if (!navigator.clipboard) {
|
||||
// this.handlePaste = this.handlePaste.bind(this)
|
||||
// window.addEventListener('paste', this.handlePaste)
|
||||
// this.mindMap.on('beforeDestroy', () => {
|
||||
// window.removeEventListener('paste', this.handlePaste)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
// 性能模式,懒加载节点
|
||||
@@ -391,7 +415,7 @@ class Render {
|
||||
this.mindMap.keyCommand.addShortcut('Control+Down', () => {
|
||||
this.mindMap.execCommand('DOWN_NODE')
|
||||
})
|
||||
// 复制节点、
|
||||
// 复制节点
|
||||
this.mindMap.keyCommand.addShortcut('Control+c', () => {
|
||||
this.copy()
|
||||
})
|
||||
@@ -401,7 +425,7 @@ class Render {
|
||||
})
|
||||
// 粘贴节点
|
||||
this.mindMap.keyCommand.addShortcut('Control+v', () => {
|
||||
this.paste()
|
||||
if (navigator.clipboard) this.paste()
|
||||
})
|
||||
// 根节点居中显示
|
||||
this.mindMap.keyCommand.addShortcut('Control+Enter', () => {
|
||||
@@ -1104,6 +1128,28 @@ class Render {
|
||||
})
|
||||
}
|
||||
|
||||
// 非https下复制黏贴,获取内容方法
|
||||
handlePaste(event) {
|
||||
const { disabledClipboard } = this.mindMap.opt
|
||||
if (disabledClipboard) return
|
||||
const clipboardData =
|
||||
event.clipboardData || event.originalEvent.clipboardData
|
||||
const items = clipboardData.items
|
||||
let img = null
|
||||
let text = ''
|
||||
Array.from(items).forEach(item => {
|
||||
if (item.type.indexOf('image') > -1) {
|
||||
img = item.getAsFile()
|
||||
}
|
||||
if (item.type.indexOf('text') > -1) {
|
||||
text = clipboardData.getData('text')
|
||||
}
|
||||
})
|
||||
this.pasteData.img = img
|
||||
this.pasteData.text = text
|
||||
this.paste()
|
||||
}
|
||||
|
||||
// 粘贴
|
||||
async paste() {
|
||||
const {
|
||||
@@ -1117,7 +1163,9 @@ class Render {
|
||||
let img = null
|
||||
if (!disabledClipboard) {
|
||||
try {
|
||||
const res = await getDataFromClipboard()
|
||||
const res = navigator.clipboard
|
||||
? await getDataFromClipboard()
|
||||
: this.pasteData
|
||||
text = res.text || ''
|
||||
img = res.img || null
|
||||
} catch (error) {
|
||||
@@ -1593,40 +1641,53 @@ class Render {
|
||||
}
|
||||
|
||||
// 展开所有
|
||||
expandAllNode() {
|
||||
expandAllNode(uid = '') {
|
||||
if (!this.renderTree) return
|
||||
walk(
|
||||
this.renderTree,
|
||||
null,
|
||||
node => {
|
||||
if (!node.data.expand) {
|
||||
node.data.expand = true
|
||||
}
|
||||
},
|
||||
null,
|
||||
true,
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
const _walk = (node, enableExpand) => {
|
||||
// 如果该节点为目标节点,那么修改允许展开的标志
|
||||
if (!enableExpand && node.data.uid === uid) {
|
||||
enableExpand = true
|
||||
}
|
||||
if (enableExpand && !node.data.expand) {
|
||||
node.data.expand = true
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach(child => {
|
||||
_walk(child, enableExpand)
|
||||
})
|
||||
}
|
||||
}
|
||||
_walk(this.renderTree, !uid)
|
||||
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 收起所有
|
||||
unexpandAllNode(isSetRootNodeCenter = true) {
|
||||
unexpandAllNode(isSetRootNodeCenter = true, uid = '') {
|
||||
if (!this.renderTree) return
|
||||
walk(
|
||||
this.renderTree,
|
||||
null,
|
||||
(node, parent, isRoot) => {
|
||||
if (!isRoot && node.children && node.children.length > 0) {
|
||||
node.data.expand = false
|
||||
}
|
||||
},
|
||||
null,
|
||||
true,
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
const _walk = (node, isRoot, enableUnExpand) => {
|
||||
// 如果该节点为目标节点,那么修改允许展开的标志
|
||||
if (!enableUnExpand && node.data.uid === uid) {
|
||||
enableUnExpand = true
|
||||
}
|
||||
if (
|
||||
enableUnExpand &&
|
||||
!isRoot &&
|
||||
node.children &&
|
||||
node.children.length > 0
|
||||
) {
|
||||
node.data.expand = false
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach(child => {
|
||||
_walk(child, false, enableUnExpand)
|
||||
})
|
||||
}
|
||||
}
|
||||
_walk(this.renderTree, true, !uid)
|
||||
|
||||
this.mindMap.render(() => {
|
||||
if (isSetRootNodeCenter) {
|
||||
this.setRootNodeCenter()
|
||||
@@ -2035,19 +2096,39 @@ class Render {
|
||||
}
|
||||
|
||||
// 高亮节点或子节点
|
||||
highlightNode(node, range) {
|
||||
highlightNode(node, range, style) {
|
||||
// 如果当前正在渲染,那么不进行高亮,因为节点位置可能不正确
|
||||
if (this.isRendering) return
|
||||
const { highlightNodeBoxStyle = {} } = this.mindMap.opt
|
||||
style = {
|
||||
stroke: 'rgb(94, 200, 248)',
|
||||
fill: 'transparent',
|
||||
...(style || {})
|
||||
}
|
||||
// 尚未创建
|
||||
if (!this.highlightBoxNode) {
|
||||
this.highlightBoxNode = new Polygon()
|
||||
.stroke({
|
||||
color: highlightNodeBoxStyle.stroke || 'transparent'
|
||||
color: style.stroke || 'transparent'
|
||||
})
|
||||
.fill({
|
||||
color: highlightNodeBoxStyle.fill || 'transparent'
|
||||
color: style.fill || 'transparent'
|
||||
})
|
||||
} else if (this.highlightBoxNodeStyle) {
|
||||
// 样式更新了
|
||||
if (
|
||||
this.highlightBoxNodeStyle.stroke !== style.stroke ||
|
||||
this.highlightBoxNodeStyle.fill !== style.fill
|
||||
) {
|
||||
this.highlightBoxNode
|
||||
.stroke({
|
||||
color: style.stroke || 'transparent'
|
||||
})
|
||||
.fill({
|
||||
color: style.fill || 'transparent'
|
||||
})
|
||||
}
|
||||
}
|
||||
this.highlightBoxNodeStyle = { ...style }
|
||||
let minx = Infinity,
|
||||
miny = Infinity,
|
||||
maxx = -Infinity,
|
||||
|
||||
@@ -25,6 +25,8 @@ export default class TextEdit {
|
||||
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
||||
this.cacheEditingText = ''
|
||||
this.hasBodyMousedown = false
|
||||
this.textNodePaddingX = 5
|
||||
this.textNodePaddingY = 3
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -214,7 +216,7 @@ export default class TextEdit {
|
||||
this.registerTmpShortcut()
|
||||
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;`
|
||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: ${this.textNodePaddingY}px ${this.textNodePaddingX}px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||
this.textEditNode.setAttribute('contenteditable', true)
|
||||
this.textEditNode.addEventListener('keyup', e => {
|
||||
e.stopPropagation()
|
||||
@@ -240,6 +242,13 @@ export default class TextEdit {
|
||||
handleInputPasteText(e)
|
||||
}
|
||||
})
|
||||
this.textEditNode.addEventListener('input', () => {
|
||||
this.mindMap.emit('node_text_edit_change', {
|
||||
node: this.currentNode,
|
||||
text: this.getEditText(),
|
||||
richText: false
|
||||
})
|
||||
})
|
||||
const targetNode =
|
||||
this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.textEditNode)
|
||||
@@ -256,8 +265,10 @@ export default class TextEdit {
|
||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||
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.minWidth =
|
||||
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight =
|
||||
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
@@ -280,6 +291,24 @@ export default class TextEdit {
|
||||
this.cacheEditingText = ''
|
||||
}
|
||||
|
||||
// 更新文本编辑框的大小和位置
|
||||
updateTextEditNode() {
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.updateTextEditNode()
|
||||
return
|
||||
}
|
||||
if (!this.showTextEdit || !this.currentNode) {
|
||||
return
|
||||
}
|
||||
const rect = this.currentNode._textData.node.node.getBoundingClientRect()
|
||||
this.textEditNode.style.minWidth =
|
||||
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight =
|
||||
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
}
|
||||
|
||||
// 删除文本编辑元素
|
||||
removeTextEditEl() {
|
||||
if (this.mindMap.richText) {
|
||||
@@ -313,12 +342,7 @@ export default class TextEdit {
|
||||
}
|
||||
this.mindMap.render()
|
||||
})
|
||||
this.mindMap.emit(
|
||||
'hide_text_edit',
|
||||
this.textEditNode,
|
||||
this.renderer.activeNodeList,
|
||||
this.currentNode
|
||||
)
|
||||
const currentNode = this.currentNode
|
||||
this.currentNode = null
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.textEditNode.innerHTML = ''
|
||||
@@ -327,6 +351,12 @@ export default class TextEdit {
|
||||
this.textEditNode.style.fontWeight = 'normal'
|
||||
this.textEditNode.style.transform = 'translateY(0)'
|
||||
this.showTextEdit = false
|
||||
this.mindMap.emit(
|
||||
'hide_text_edit',
|
||||
this.textEditNode,
|
||||
this.renderer.activeNodeList,
|
||||
currentNode
|
||||
)
|
||||
}
|
||||
|
||||
// 获取当前正在编辑中的节点实例
|
||||
|
||||
@@ -34,6 +34,8 @@ class MindMapNode {
|
||||
this.lineDraw = this.mindMap.lineDraw
|
||||
// 样式实例
|
||||
this.style = new Style(this)
|
||||
// 节点当前生效的全部样式
|
||||
this.effectiveStyles = {}
|
||||
// 形状实例
|
||||
this.shapeInstance = new Shape(this)
|
||||
this.shapePadding = {
|
||||
@@ -1178,10 +1180,9 @@ class MindMapNode {
|
||||
|
||||
// 获取padding值
|
||||
getPaddingVale() {
|
||||
let { isActive } = this.getData()
|
||||
return {
|
||||
paddingX: this.getStyle('paddingX', true, isActive),
|
||||
paddingY: this.getStyle('paddingY', true, isActive)
|
||||
paddingX: this.getStyle('paddingX'),
|
||||
paddingY: this.getStyle('paddingY')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { checkIsNodeStyleDataKey } from '../../../utils/index'
|
||||
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
const backgroundStyleProps = [
|
||||
'backgroundColor',
|
||||
'backgroundImage',
|
||||
@@ -62,10 +61,11 @@ class Style {
|
||||
// 合并样式
|
||||
merge(prop, root) {
|
||||
let themeConfig = this.ctx.mindMap.themeConfig
|
||||
// 三级及以下节点
|
||||
let defaultConfig = themeConfig.node
|
||||
if (root || rootProp.includes(prop)) {
|
||||
// 直接使用最外层样式
|
||||
let defaultConfig = null
|
||||
let useRoot = false
|
||||
if (root) {
|
||||
// 使用最外层样式
|
||||
useRoot = true
|
||||
defaultConfig = themeConfig
|
||||
} else if (this.ctx.isGeneralization) {
|
||||
// 概要节点
|
||||
@@ -76,11 +76,27 @@ class Style {
|
||||
} else if (this.ctx.layerIndex === 1) {
|
||||
// 二级节点
|
||||
defaultConfig = themeConfig.second
|
||||
} else {
|
||||
// 三级及以下节点
|
||||
defaultConfig = themeConfig.node
|
||||
}
|
||||
let value = ''
|
||||
// 优先使用节点本身的样式
|
||||
return this.getSelfStyle(prop) !== undefined
|
||||
? this.getSelfStyle(prop)
|
||||
: defaultConfig[prop]
|
||||
if (this.getSelfStyle(prop) !== undefined) {
|
||||
value = this.getSelfStyle(prop)
|
||||
} else if (defaultConfig[prop] !== undefined) {
|
||||
// 否则使用对应层级的样式
|
||||
value = defaultConfig[prop]
|
||||
} else {
|
||||
// 否则使用最外层样式
|
||||
value = themeConfig[prop]
|
||||
}
|
||||
if (!useRoot) {
|
||||
this.addToEffectiveStyles({
|
||||
[prop]: value
|
||||
})
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// 获取某个样式值
|
||||
@@ -93,6 +109,14 @@ class Style {
|
||||
return this.ctx.getData(prop)
|
||||
}
|
||||
|
||||
// 更新当前节点生效的样式数据
|
||||
addToEffectiveStyles(styles) {
|
||||
this.ctx.effectiveStyles = {
|
||||
...this.ctx.effectiveStyles,
|
||||
...styles
|
||||
}
|
||||
}
|
||||
|
||||
// 矩形
|
||||
rect(node) {
|
||||
this.shape(node)
|
||||
@@ -101,18 +125,30 @@ class Style {
|
||||
|
||||
// 形状
|
||||
shape(node) {
|
||||
if (this.merge('gradientStyle')) {
|
||||
const styles = {
|
||||
gradientStyle: this.merge('gradientStyle'),
|
||||
startColor: this.merge('startColor'),
|
||||
endColor: this.merge('endColor'),
|
||||
startDir: this.merge('startDir'),
|
||||
endDir: this.merge('endDir'),
|
||||
fillColor: this.merge('fillColor'),
|
||||
borderColor: this.merge('borderColor'),
|
||||
borderWidth: this.merge('borderWidth'),
|
||||
borderDasharray: this.merge('borderDasharray')
|
||||
}
|
||||
if (styles.gradientStyle) {
|
||||
if (!this._gradient) {
|
||||
this._gradient = this.ctx.nodeDraw.gradient('linear')
|
||||
}
|
||||
this._gradient.update(add => {
|
||||
add.stop(0, this.merge('startColor'))
|
||||
add.stop(1, this.merge('endColor'))
|
||||
add.stop(0, styles.startColor)
|
||||
add.stop(1, styles.endColor)
|
||||
})
|
||||
this._gradient.from(...styles.startDir).to(...styles.endDir)
|
||||
node.fill(this._gradient)
|
||||
} else {
|
||||
node.fill({
|
||||
color: this.merge('fillColor')
|
||||
color: styles.fillColor
|
||||
})
|
||||
}
|
||||
// 节点使用横线样式,不需要渲染非激活状态的边框样式
|
||||
@@ -125,56 +161,89 @@ class Style {
|
||||
// return
|
||||
// }
|
||||
node.stroke({
|
||||
color: this.merge('borderColor'),
|
||||
width: this.merge('borderWidth'),
|
||||
dasharray: this.merge('borderDasharray')
|
||||
color: styles.borderColor,
|
||||
width: styles.borderWidth,
|
||||
dasharray: styles.borderDasharray
|
||||
})
|
||||
}
|
||||
|
||||
// 文字
|
||||
text(node) {
|
||||
const styles = {
|
||||
color: this.merge('color'),
|
||||
fontFamily: this.merge('fontFamily'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontWeight: this.merge('fontWeight'),
|
||||
fontStyle: this.merge('fontStyle'),
|
||||
textDecoration: this.merge('textDecoration')
|
||||
}
|
||||
node
|
||||
.fill({
|
||||
color: this.merge('color')
|
||||
color: styles.color
|
||||
})
|
||||
.css({
|
||||
'font-family': this.merge('fontFamily'),
|
||||
'font-size': this.merge('fontSize'),
|
||||
'font-weight': this.merge('fontWeight'),
|
||||
'font-style': this.merge('fontStyle'),
|
||||
'text-decoration': this.merge('textDecoration')
|
||||
'font-family': styles.fontFamily,
|
||||
'font-size': styles.fontSize,
|
||||
'font-weight': styles.fontWeight,
|
||||
'font-style': styles.fontStyle,
|
||||
'text-decoration': styles.textDecoration
|
||||
})
|
||||
}
|
||||
|
||||
// 生成内联样式
|
||||
createStyleText() {
|
||||
const styles = {
|
||||
color: this.merge('color'),
|
||||
fontFamily: this.merge('fontFamily'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontWeight: this.merge('fontWeight'),
|
||||
fontStyle: this.merge('fontStyle'),
|
||||
textDecoration: this.merge('textDecoration')
|
||||
}
|
||||
return `
|
||||
color: ${this.merge('color')};
|
||||
font-family: ${this.merge('fontFamily')};
|
||||
font-size: ${this.merge('fontSize') + 'px'};
|
||||
font-weight: ${this.merge('fontWeight')};
|
||||
font-style: ${this.merge('fontStyle')};
|
||||
text-decoration: ${this.merge('textDecoration')}
|
||||
color: ${styles.color};
|
||||
font-family: ${styles.fontFamily};
|
||||
font-size: ${styles.fontSize + 'px'};
|
||||
font-weight: ${styles.fontWeight};
|
||||
font-style: ${styles.fontStyle};
|
||||
text-decoration: ${styles.textDecoration}
|
||||
`
|
||||
}
|
||||
|
||||
// 获取文本样式
|
||||
getTextFontStyle() {
|
||||
return {
|
||||
italic: this.merge('fontStyle') === 'italic',
|
||||
bold: this.merge('fontWeight'),
|
||||
const styles = {
|
||||
color: this.merge('color'),
|
||||
fontFamily: this.merge('fontFamily'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontFamily: this.merge('fontFamily')
|
||||
fontWeight: this.merge('fontWeight'),
|
||||
fontStyle: this.merge('fontStyle'),
|
||||
textDecoration: this.merge('textDecoration')
|
||||
}
|
||||
return {
|
||||
italic: styles.fontStyle === 'italic',
|
||||
bold: styles.fontWeight,
|
||||
fontSize: styles.fontSize,
|
||||
fontFamily: styles.fontFamily
|
||||
}
|
||||
}
|
||||
|
||||
// html文字节点
|
||||
domText(node, fontSizeScale = 1, isMultiLine) {
|
||||
node.style.fontFamily = this.merge('fontFamily')
|
||||
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
|
||||
node.style.fontWeight = this.merge('fontWeight') || 'normal'
|
||||
node.style.lineHeight = !isMultiLine ? 'normal' : this.merge('lineHeight')
|
||||
node.style.fontStyle = this.merge('fontStyle')
|
||||
const styles = {
|
||||
color: this.merge('color'),
|
||||
fontFamily: this.merge('fontFamily'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontWeight: this.merge('fontWeight'),
|
||||
fontStyle: this.merge('fontStyle'),
|
||||
textDecoration: this.merge('textDecoration'),
|
||||
lineHeight: this.merge('lineHeight')
|
||||
}
|
||||
node.style.fontFamily = styles.fontFamily
|
||||
node.style.fontSize = styles.fontSize * fontSizeScale + 'px'
|
||||
node.style.fontWeight = styles.fontWeight || 'normal'
|
||||
node.style.lineHeight = !isMultiLine ? 'normal' : styles.lineHeight
|
||||
node.style.fontStyle = styles.fontStyle
|
||||
}
|
||||
|
||||
// 标签文字
|
||||
@@ -286,8 +355,10 @@ class Style {
|
||||
|
||||
// hover和激活节点
|
||||
hoverNode(node) {
|
||||
const { hoverRectColor } = this.ctx.mindMap.opt
|
||||
node.radius(5).fill('none').stroke({
|
||||
const hoverRectColor =
|
||||
this.merge('hoverRectColor') || this.ctx.mindMap.opt.hoverRectColor
|
||||
const hoverRectRadius = this.merge('hoverRectRadius')
|
||||
node.radius(hoverRectRadius).fill('none').stroke({
|
||||
color: hoverRectColor
|
||||
})
|
||||
}
|
||||
|
||||
@@ -114,8 +114,10 @@ function createIconNode() {
|
||||
}
|
||||
|
||||
// 创建富文本节点
|
||||
function createRichTextNode() {
|
||||
const { textAutoWrapWidth } = this.mindMap.opt
|
||||
function createRichTextNode(specifyText) {
|
||||
let text =
|
||||
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||
const { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
||||
let g = new G()
|
||||
// 重新设置富文本节点内容
|
||||
let recoverText = false
|
||||
@@ -129,7 +131,6 @@ function createRichTextNode() {
|
||||
recoverText = true
|
||||
}
|
||||
}
|
||||
let text = this.getData('text')
|
||||
if (recoverText && !isUndef(text)) {
|
||||
// 判断节点内容是否是富文本
|
||||
let isRichText = checkIsRichText(text)
|
||||
@@ -153,7 +154,7 @@ function createRichTextNode() {
|
||||
text: text
|
||||
})
|
||||
}
|
||||
let html = `<div>${this.getData('text')}</div>`
|
||||
let html = `<div>${text}</div>`
|
||||
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
||||
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
|
||||
document.createElement('div')
|
||||
@@ -174,7 +175,7 @@ function createRichTextNode() {
|
||||
let { width, height } = el.getBoundingClientRect()
|
||||
// 如果文本为空,那么需要计算一个默认高度
|
||||
if (height <= 0) {
|
||||
div.innerHTML = '<p>abc123我和你</p>'
|
||||
div.innerHTML = `<p>${emptyTextMeasureHeightText}</p>`
|
||||
let elTmp = div.children[0]
|
||||
elTmp.classList.add('smm-richtext-node-wrap')
|
||||
height = elTmp.getBoundingClientRect().height
|
||||
@@ -199,10 +200,12 @@ function createRichTextNode() {
|
||||
}
|
||||
|
||||
// 创建文本节点
|
||||
function createTextNode() {
|
||||
function createTextNode(specifyText) {
|
||||
if (this.getData('richText')) {
|
||||
return this.createRichTextNode()
|
||||
return this.createRichTextNode(specifyText)
|
||||
}
|
||||
const text =
|
||||
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||
if (this.getData('resetRichText')) {
|
||||
delete this.nodeData.data.resetRichText
|
||||
}
|
||||
@@ -212,10 +215,11 @@ function createTextNode() {
|
||||
// 文本超长自动换行
|
||||
let textStyle = this.style.getTextFontStyle()
|
||||
let textArr = []
|
||||
if (!isUndef(this.getData('text'))) {
|
||||
textArr = String(this.getData('text')).split(/\n/gim)
|
||||
if (!isUndef(text)) {
|
||||
textArr = String(text).split(/\n/gim)
|
||||
}
|
||||
let maxWidth = this.mindMap.opt.textAutoWrapWidth
|
||||
const { textAutoWrapWidth: maxWidth, emptyTextMeasureHeightText } =
|
||||
this.mindMap.opt
|
||||
let isMultiLine = false
|
||||
textArr.forEach((item, index) => {
|
||||
let arr = item.split('')
|
||||
@@ -247,6 +251,13 @@ function createTextNode() {
|
||||
g.add(node)
|
||||
})
|
||||
let { width, height } = g.bbox()
|
||||
// 如果文本为空,那么需要计算一个默认高度
|
||||
if (height <= 0) {
|
||||
const tmpNode = new Text().text(emptyTextMeasureHeightText)
|
||||
this.style.text(tmpNode)
|
||||
const tmpBbox = tmpNode.bbox()
|
||||
height = tmpBbox.height
|
||||
}
|
||||
width = Math.min(Math.ceil(width), maxWidth)
|
||||
height = Math.ceil(height)
|
||||
g.attr('data-width', width)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import btnsSvg from '../../../svg/btns'
|
||||
import { SVG, Circle, G, Text } from '@svgdotjs/svg.js'
|
||||
import { isUndef } from '../../../utils'
|
||||
|
||||
// 创建展开收起按钮的内容节点
|
||||
function createExpandNodeContent() {
|
||||
@@ -78,7 +79,12 @@ function updateExpandBtnNode() {
|
||||
})
|
||||
// 计算子节点数量
|
||||
let count = this.sumNode(this.nodeData.children)
|
||||
count = expandBtnNumHandler(count)
|
||||
if (typeof expandBtnNumHandler === 'function') {
|
||||
const res = expandBtnNumHandler(count, this)
|
||||
if (!isUndef(res)) {
|
||||
count = res
|
||||
}
|
||||
}
|
||||
node.text(String(count))
|
||||
} else {
|
||||
this._fillExpandNode.stroke('none')
|
||||
|
||||
@@ -186,15 +186,29 @@ function handleGeneralizationMouseenter() {
|
||||
const list = belongNode.formatGetGeneralization()
|
||||
const index = belongNode.getGeneralizationNodeIndex(this)
|
||||
const generalizationData = list[index]
|
||||
// 如果主题中设置了hoverRectColor颜色,那么使用该颜色
|
||||
// 否则使用hoverRectColor实例化选项的颜色
|
||||
// 兜底使用highlightNode方法的默认颜色
|
||||
const hoverRectColor = this.getStyle('hoverRectColor')
|
||||
const color = hoverRectColor || this.mindMap.opt.hoverRectColor
|
||||
const style = color
|
||||
? {
|
||||
stroke: color
|
||||
}
|
||||
: null
|
||||
// 区间概要,框子节点
|
||||
if (
|
||||
Array.isArray(generalizationData.range) &&
|
||||
generalizationData.range.length > 0
|
||||
) {
|
||||
this.mindMap.renderer.highlightNode(belongNode, generalizationData.range)
|
||||
this.mindMap.renderer.highlightNode(
|
||||
belongNode,
|
||||
generalizationData.range,
|
||||
style
|
||||
)
|
||||
} else {
|
||||
// 否则框自己
|
||||
this.mindMap.renderer.highlightNode(belongNode)
|
||||
this.mindMap.renderer.highlightNode(belongNode, null, style)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,11 @@ class View {
|
||||
})
|
||||
// 拖动视图
|
||||
this.mindMap.event.on('mousedown', e => {
|
||||
if (this.mindMap.opt.isDisableDrag) return
|
||||
e.preventDefault()
|
||||
const { isDisableDrag, mousedownEventPreventDefault } = this.mindMap.opt
|
||||
if (isDisableDrag) return
|
||||
if (mousedownEventPreventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
this.sx = this.x
|
||||
this.sy = this.y
|
||||
})
|
||||
@@ -63,7 +66,8 @@ class View {
|
||||
mouseScaleCenterUseMousePosition,
|
||||
mousewheelMoveStep,
|
||||
mousewheelZoomActionReverse,
|
||||
disableMouseWheelZoom
|
||||
disableMouseWheelZoom,
|
||||
translateRatio
|
||||
} = this.mindMap.opt
|
||||
// 是否自定义鼠标滚轮事件
|
||||
if (
|
||||
@@ -111,26 +115,34 @@ class View {
|
||||
}
|
||||
} else {
|
||||
// 2.鼠标滚轮事件控制画布移动
|
||||
const step = isTouchPad ? 10 : mousewheelMoveStep
|
||||
let stepX = 0
|
||||
let stepY = 0
|
||||
if (isTouchPad) {
|
||||
// 如果是触控板,那么直接使用触控板滑动距离
|
||||
stepX = Math.abs(e.wheelDeltaX)
|
||||
stepY = Math.abs(e.wheelDeltaY)
|
||||
} else {
|
||||
stepX = stepY = mousewheelMoveStep
|
||||
}
|
||||
let mx = 0
|
||||
let my = 0
|
||||
// 上移
|
||||
if (dirs.includes(CONSTANTS.DIR.DOWN)) {
|
||||
my = -step
|
||||
my = -stepY
|
||||
}
|
||||
// 下移
|
||||
if (dirs.includes(CONSTANTS.DIR.UP)) {
|
||||
my = step
|
||||
my = stepY
|
||||
}
|
||||
// 右移
|
||||
if (dirs.includes(CONSTANTS.DIR.LEFT)) {
|
||||
mx = step
|
||||
mx = stepX
|
||||
}
|
||||
// 左移
|
||||
if (dirs.includes(CONSTANTS.DIR.RIGHT)) {
|
||||
mx = -step
|
||||
mx = -stepX
|
||||
}
|
||||
this.translateXY(mx, my)
|
||||
this.translateXY(mx * translateRatio, my * translateRatio)
|
||||
}
|
||||
})
|
||||
this.mindMap.on('resize', () => {
|
||||
@@ -238,8 +250,9 @@ class View {
|
||||
|
||||
// 缩小
|
||||
narrow(cx, cy, isTouchPad) {
|
||||
const scaleRatio = this.mindMap.opt.scaleRatio / (isTouchPad ? 5 : 1)
|
||||
const scale = Math.max(this.scale - scaleRatio, 0.1)
|
||||
let { scaleRatio, minZoomRatio } = this.mindMap.opt
|
||||
scaleRatio = scaleRatio / (isTouchPad ? 5 : 1)
|
||||
const scale = Math.max(this.scale - scaleRatio, minZoomRatio / 100)
|
||||
this.scaleInCenter(scale, cx, cy)
|
||||
this.transform()
|
||||
this.emitEvent('scale')
|
||||
@@ -247,8 +260,14 @@ class View {
|
||||
|
||||
// 放大
|
||||
enlarge(cx, cy, isTouchPad) {
|
||||
const scaleRatio = this.mindMap.opt.scaleRatio / (isTouchPad ? 5 : 1)
|
||||
const scale = this.scale + scaleRatio
|
||||
let { scaleRatio, maxZoomRatio } = this.mindMap.opt
|
||||
scaleRatio = scaleRatio / (isTouchPad ? 5 : 1)
|
||||
let scale = 0
|
||||
if (maxZoomRatio === -1) {
|
||||
scale = this.scale + scaleRatio
|
||||
} else {
|
||||
scale = Math.min(this.scale + scaleRatio, maxZoomRatio / 100)
|
||||
}
|
||||
this.scaleInCenter(scale, cx, cy)
|
||||
this.transform()
|
||||
this.emitEvent('scale')
|
||||
|
||||
@@ -128,8 +128,9 @@ class Base {
|
||||
)
|
||||
}
|
||||
// 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本等情况需要重新计算节点大小和布局
|
||||
const isNeedResizeSources = this.checkIsNeedResizeSources()
|
||||
if (
|
||||
this.checkIsNeedResizeSources() ||
|
||||
isNeedResizeSources ||
|
||||
isLayerTypeChange ||
|
||||
newNode.getData('resetRichText') ||
|
||||
isNumberChange
|
||||
@@ -137,7 +138,7 @@ class Base {
|
||||
newNode.getSize()
|
||||
newNode.needLayout = true
|
||||
}
|
||||
this.checkGetGeneralizationChange(newNode)
|
||||
this.checkGetGeneralizationChange(newNode, isNeedResizeSources)
|
||||
} else if (
|
||||
(this.lru.has(uid) || this.renderer.lastNodeCache[uid]) &&
|
||||
!this.renderer.reRender
|
||||
@@ -186,7 +187,7 @@ class Base {
|
||||
newNode.getSize()
|
||||
newNode.needLayout = true
|
||||
}
|
||||
this.checkGetGeneralizationChange(newNode)
|
||||
this.checkGetGeneralizationChange(newNode, isResizeSource)
|
||||
} else {
|
||||
// 创建新节点
|
||||
const newUid = uid || createUid()
|
||||
@@ -228,7 +229,7 @@ class Base {
|
||||
}
|
||||
|
||||
// 检查概要节点是否需要更新
|
||||
checkGetGeneralizationChange(node) {
|
||||
checkGetGeneralizationChange(node, isResizeSource) {
|
||||
const generalizationList = node.getData('generalization')
|
||||
if (
|
||||
generalizationList &&
|
||||
@@ -239,8 +240,13 @@ class Base {
|
||||
const gNode = item.generalizationNode
|
||||
const oldData = gNode.getData()
|
||||
const newData = generalizationList[index]
|
||||
if (newData && JSON.stringify(oldData) !== JSON.stringify(newData)) {
|
||||
gNode.nodeData.data = newData
|
||||
if (
|
||||
isResizeSource ||
|
||||
(newData && JSON.stringify(oldData) !== JSON.stringify(newData))
|
||||
) {
|
||||
if (newData) {
|
||||
gNode.nodeData.data = newData
|
||||
}
|
||||
gNode.getSize()
|
||||
gNode.needLayout = true
|
||||
}
|
||||
@@ -371,18 +377,32 @@ class Base {
|
||||
}
|
||||
|
||||
// 二次贝塞尔曲线
|
||||
quadraticCurvePath(x1, y1, x2, y2) {
|
||||
let cx = x1 + (x2 - x1) * 0.2
|
||||
let cy = y1 + (y2 - y1) * 0.8
|
||||
quadraticCurvePath(x1, y1, x2, y2, v = false) {
|
||||
let cx, cy
|
||||
if (v) {
|
||||
cx = x1 + (x2 - x1) * 0.8
|
||||
cy = y1 + (y2 - y1) * 0.2
|
||||
} else {
|
||||
cx = x1 + (x2 - x1) * 0.2
|
||||
cy = y1 + (y2 - y1) * 0.8
|
||||
}
|
||||
return `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
|
||||
}
|
||||
|
||||
// 三次贝塞尔曲线
|
||||
cubicBezierPath(x1, y1, x2, y2) {
|
||||
let cx1 = x1 + (x2 - x1) / 2
|
||||
let cy1 = y1
|
||||
let cx2 = cx1
|
||||
let cy2 = y2
|
||||
cubicBezierPath(x1, y1, x2, y2, v = false) {
|
||||
let cx1, cy1, cx2, cy2
|
||||
if (v) {
|
||||
cx1 = x1
|
||||
cy1 = y1 + (y2 - y1) / 2
|
||||
cx2 = x2
|
||||
cy2 = cy1
|
||||
} else {
|
||||
cx1 = x1 + (x2 - x1) / 2
|
||||
cy1 = y1
|
||||
cx2 = cx1
|
||||
cy2 = y2
|
||||
}
|
||||
return `M ${x1},${y1} C ${cx1},${cy1} ${cx2},${cy2} ${x2},${y2}`
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,14 @@ class OrganizationStructure extends Base {
|
||||
this.renderer.renderTree,
|
||||
null,
|
||||
(cur, parent, isRoot, layerIndex, index, ancestors) => {
|
||||
let newNode = this.createNode(cur, parent, isRoot, layerIndex, index, ancestors)
|
||||
let newNode = this.createNode(
|
||||
cur,
|
||||
parent,
|
||||
isRoot,
|
||||
layerIndex,
|
||||
index,
|
||||
ancestors
|
||||
)
|
||||
// 根节点定位在画布中心位置
|
||||
if (isRoot) {
|
||||
this.setNodeCenter(newNode)
|
||||
@@ -148,13 +155,56 @@ class OrganizationStructure extends Base {
|
||||
|
||||
// 绘制连线,连接该节点到其子节点
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'direct') {
|
||||
if (lineStyle === 'curve') {
|
||||
this.renderLineCurve(node, lines, style)
|
||||
} else if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
// 曲线风格连线
|
||||
renderLineCurve(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
const { alwaysShowExpandBtn, notShowExpandBtn } = this.mindMap.opt
|
||||
if (!alwaysShowExpandBtn || notShowExpandBtn) {
|
||||
expandBtnSize = 0
|
||||
}
|
||||
const {
|
||||
nodeUseLineStyle,
|
||||
rootLineStartPositionKeepSameInCurve,
|
||||
rootLineKeepSameInCurve
|
||||
} = this.mindMap.themeConfig
|
||||
node.children.forEach((item, index) => {
|
||||
if (node.layerIndex === 0) {
|
||||
expandBtnSize = 0
|
||||
}
|
||||
let x1 = left + width / 2
|
||||
let y1 =
|
||||
node.layerIndex === 0 && !rootLineStartPositionKeepSameInCurve
|
||||
? top + height / 2
|
||||
: top + height + expandBtnSize
|
||||
let x2 = item.left + item.width / 2
|
||||
let y2 = item.top
|
||||
let path = ''
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = nodeUseLineStyle
|
||||
? ` L ${item.left},${y2} L ${item.left + item.width},${y2}`
|
||||
: ''
|
||||
if (node.isRoot && !rootLineKeepSameInCurve) {
|
||||
path =
|
||||
this.quadraticCurvePath(x1, y1, x2, y2, true) + nodeUseLineStylePath
|
||||
} else {
|
||||
path = this.cubicBezierPath(x1, y1, x2, y2, true) + nodeUseLineStylePath
|
||||
}
|
||||
this.setLineStyle(style, lines[index], path, item)
|
||||
})
|
||||
}
|
||||
|
||||
// 直连风格
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
|
||||
@@ -129,6 +129,7 @@ class Export {
|
||||
|
||||
// svg转png
|
||||
svgToPng(svgSrc, transparent, clipData = null) {
|
||||
const { maxCanvasSize, minExportImgCanvasScale } = this.mindMap.opt
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image()
|
||||
// 跨域图片需要添加这个属性,否则画布被污染了无法导出图片
|
||||
@@ -136,10 +137,7 @@ class Export {
|
||||
img.onload = async () => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas')
|
||||
const dpr = Math.max(
|
||||
window.devicePixelRatio,
|
||||
this.mindMap.opt.minExportImgCanvasScale
|
||||
)
|
||||
const dpr = Math.max(window.devicePixelRatio, minExportImgCanvasScale)
|
||||
let imgWidth = img.width
|
||||
let imgHeight = img.height
|
||||
// 如果是裁减操作的话,那么需要手动添加内边距,及调整图片大小为实际的裁减区域的大小,不要忘了内边距哦
|
||||
@@ -152,29 +150,40 @@ class Export {
|
||||
imgHeight = clipData.height + paddingY * 2
|
||||
}
|
||||
// 检查是否超出canvas支持的像素上限
|
||||
const maxSize = 16384 / dpr
|
||||
const maxArea = maxSize * maxSize
|
||||
if (imgWidth * imgHeight > maxArea) {
|
||||
// canvas大小需要乘以dpr
|
||||
let canvasWidth = imgWidth * dpr
|
||||
let canvasHeight = imgHeight * dpr
|
||||
if (canvasWidth > maxCanvasSize || canvasHeight > maxCanvasSize) {
|
||||
let newWidth = null
|
||||
let newHeight = null
|
||||
if (imgWidth > maxSize) {
|
||||
newWidth = maxArea / imgHeight
|
||||
} else if (imgHeight > maxSize) {
|
||||
newHeight = maxArea / imgWidth
|
||||
if (canvasWidth > maxCanvasSize) {
|
||||
// 如果宽度超出限制,那么调整为上限值
|
||||
newWidth = maxCanvasSize
|
||||
} else if (canvasHeight > maxCanvasSize) {
|
||||
// 高度同理
|
||||
newHeight = maxCanvasSize
|
||||
}
|
||||
const res = resizeImgSize(imgWidth, imgHeight, newWidth, newHeight)
|
||||
imgWidth = res[0]
|
||||
imgHeight = res[1]
|
||||
// 计算缩放后的宽高
|
||||
const res = resizeImgSize(
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
newWidth,
|
||||
newHeight
|
||||
)
|
||||
canvasWidth = res[0]
|
||||
canvasHeight = res[1]
|
||||
}
|
||||
canvas.width = imgWidth * dpr
|
||||
canvas.height = imgHeight * dpr
|
||||
canvas.style.width = imgWidth + 'px'
|
||||
canvas.style.height = imgHeight + 'px'
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = canvasHeight
|
||||
const styleWidth = canvasWidth / dpr
|
||||
const styleHeight = canvasHeight / dpr
|
||||
canvas.style.width = styleWidth + 'px'
|
||||
canvas.style.height = styleHeight + 'px'
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.scale(dpr, dpr)
|
||||
// 绘制背景
|
||||
if (!transparent) {
|
||||
await this.drawBackgroundToCanvas(ctx, imgWidth, imgHeight)
|
||||
await this.drawBackgroundToCanvas(ctx, styleWidth, styleHeight)
|
||||
}
|
||||
// 图片绘制到canvas里
|
||||
// 如果有裁减数据,那么需要进行裁减
|
||||
@@ -191,7 +200,7 @@ class Export {
|
||||
clipData.height
|
||||
)
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0, imgWidth, imgHeight)
|
||||
ctx.drawImage(img, 0, 0, styleWidth, styleHeight)
|
||||
}
|
||||
resolve(canvas.toDataURL())
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import katex from 'katex'
|
||||
import Quill from 'quill'
|
||||
import { getChromeVersion } from '../utils/index'
|
||||
import { getChromeVersion, htmlEscape } from '../utils/index'
|
||||
import { getBaseStyleText, getFontStyleText } from './FormulaStyle'
|
||||
|
||||
let extended = false
|
||||
const QuillFormula = Quill.import('formats/formula')
|
||||
|
||||
// 数学公式支持插件
|
||||
// 该插件在富文本模式下可用
|
||||
class Formula {
|
||||
@@ -16,6 +19,18 @@ class Formula {
|
||||
this.cssEl = null
|
||||
this.addStyle()
|
||||
this.extendQuill()
|
||||
this.onDestroy = this.onDestroy.bind(this)
|
||||
this.mindMap.on('beforeDestroy', this.onDestroy)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
const instanceCount = Object.getPrototypeOf(this.mindMap).constructor
|
||||
.instanceCount
|
||||
// 如果思维导图实例数量变成0了,那么就恢复成默认的
|
||||
if (instanceCount <= 1) {
|
||||
extended = false
|
||||
Quill.register('formats/formula', QuillFormula, true)
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
@@ -50,7 +65,9 @@ class Formula {
|
||||
|
||||
// 修改formula格式工具
|
||||
extendQuill() {
|
||||
const QuillFormula = Quill.import('formats/formula')
|
||||
if (extended) return
|
||||
extended = true
|
||||
|
||||
const self = this
|
||||
|
||||
class CustomFormulaBlot extends QuillFormula {
|
||||
@@ -58,7 +75,7 @@ class Formula {
|
||||
let node = super.create(value)
|
||||
if (typeof value === 'string') {
|
||||
katex.render(value, node, self.config)
|
||||
node.setAttribute('data-value', value)
|
||||
node.setAttribute('data-value', htmlEscape(value))
|
||||
}
|
||||
return node
|
||||
}
|
||||
@@ -110,11 +127,7 @@ class Formula {
|
||||
for (const el of els)
|
||||
nodeText = nodeText.replace(
|
||||
el.outerHTML,
|
||||
`\$${el
|
||||
.getAttribute('data-value')
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')}\$`
|
||||
`\$${el.getAttribute('data-value')}\$`
|
||||
)
|
||||
}
|
||||
return nodeText
|
||||
@@ -172,11 +185,13 @@ class Formula {
|
||||
// 插件被移除前做的事情
|
||||
beforePluginRemove() {
|
||||
this.removeStyle()
|
||||
this.mindMap.off('beforeDestroy', this.onDestroy)
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.removeStyle()
|
||||
this.mindMap.off('beforeDestroy', this.onDestroy)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,8 +192,14 @@ class NodeImgAdjust {
|
||||
if (this.isMousedown) return
|
||||
this.hideHandleEl()
|
||||
})
|
||||
btnRemove.addEventListener('click', e => {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, { url: null })
|
||||
btnRemove.addEventListener('click', async e => {
|
||||
let stop = false
|
||||
if (typeof this.mindMap.opt.beforeDeleteNodeImg === 'function') {
|
||||
stop = await this.mindMap.opt.beforeDeleteNodeImg(this.node)
|
||||
}
|
||||
if (!stop) {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, { url: null })
|
||||
}
|
||||
})
|
||||
// 添加元素到页面
|
||||
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
|
||||
@@ -53,17 +53,22 @@ class Painter {
|
||||
node.uid === this.painterNode.uid
|
||||
)
|
||||
return
|
||||
const style = {}
|
||||
let style = {}
|
||||
// 格式刷节点所有生效的样式
|
||||
if (!this.mindMap.opt.onlyPainterNodeCustomStyles) {
|
||||
style = {
|
||||
...this.painterNode.effectiveStyles
|
||||
}
|
||||
}
|
||||
const painterNodeData = this.painterNode.getData()
|
||||
Object.keys(painterNodeData).forEach(key => {
|
||||
if (checkIsNodeStyleDataKey(key)) {
|
||||
style[key] = painterNodeData[key]
|
||||
}
|
||||
})
|
||||
// 先去除目标节点的样式
|
||||
this.mindMap.renderer._handleRemoveCustomStyles(node.getData())
|
||||
node.setStyles(style)
|
||||
if (painterNodeData.activeStyle) {
|
||||
node.setStyles(painterNodeData.activeStyle, true)
|
||||
}
|
||||
}
|
||||
|
||||
// 插件被移除前做的事情
|
||||
|
||||
@@ -57,6 +57,8 @@ class RichText {
|
||||
this.cacheEditingText = ''
|
||||
this.lostStyle = false
|
||||
this.isCompositing = false
|
||||
this.textNodePaddingX = 6
|
||||
this.textNodePaddingY = 4
|
||||
this.initOpt()
|
||||
this.extendQuill()
|
||||
this.appendCss()
|
||||
@@ -71,14 +73,17 @@ class RichText {
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
this.onCompositionStart = this.onCompositionStart.bind(this)
|
||||
this.onCompositionUpdate = this.onCompositionUpdate.bind(this)
|
||||
this.onCompositionEnd = this.onCompositionEnd.bind(this)
|
||||
window.addEventListener('compositionstart', this.onCompositionStart)
|
||||
window.addEventListener('compositionupdate', this.onCompositionUpdate)
|
||||
window.addEventListener('compositionend', this.onCompositionEnd)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
unbindEvent() {
|
||||
window.removeEventListener('compositionstart', this.onCompositionStart)
|
||||
window.removeEventListener('compositionupdate', this.onCompositionUpdate)
|
||||
window.removeEventListener('compositionend', this.onCompositionEnd)
|
||||
}
|
||||
|
||||
@@ -198,8 +203,8 @@ class RichText {
|
||||
let scaleX = rect.width / originWidth
|
||||
let scaleY = rect.height / originHeight
|
||||
// 内边距
|
||||
let paddingX = 6
|
||||
let paddingY = 4
|
||||
let paddingX = this.textNodePaddingX
|
||||
let paddingY = this.textNodePaddingY
|
||||
if (richTextEditFakeInPlace) {
|
||||
let paddingValue = node.getPaddingVale()
|
||||
paddingX = paddingValue.paddingX
|
||||
@@ -287,6 +292,20 @@ class RichText {
|
||||
this.cacheEditingText = ''
|
||||
}
|
||||
|
||||
// 更新文本编辑框的大小和位置
|
||||
updateTextEditNode() {
|
||||
if (!this.node) return
|
||||
const rect = this.node._textData.node.node.getBoundingClientRect()
|
||||
const g = this.node._textData.node
|
||||
const originWidth = g.attr('data-width')
|
||||
const originHeight = g.attr('data-height')
|
||||
this.textEditNode.style.minWidth =
|
||||
originWidth + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight = originHeight + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
}
|
||||
|
||||
// 删除文本编辑框元素
|
||||
removeTextEditEl() {
|
||||
if (!this.textEditNode) return
|
||||
@@ -340,6 +359,18 @@ class RichText {
|
||||
return html.replace(/<p><br><\/p>$/, '')
|
||||
}
|
||||
|
||||
// 给html字符串中的节点样式按样式名首字母排序
|
||||
sortHtmlNodeStyles(html) {
|
||||
return html.replace(/(<[^<>]+\s+style=")([^"]+)("\s*>)/g, (_, a, b, c) => {
|
||||
let arr = b.match(/[^:]+:[^:]+;/g) || []
|
||||
arr = arr.map(item => {
|
||||
return item.trim()
|
||||
})
|
||||
arr.sort()
|
||||
return a + arr.join('') + c
|
||||
})
|
||||
}
|
||||
|
||||
// 隐藏文本编辑控件,即完成编辑
|
||||
hideEditText(nodes) {
|
||||
if (!this.showTextEdit) {
|
||||
@@ -350,6 +381,7 @@ class RichText {
|
||||
beforeHideRichTextEdit(this)
|
||||
}
|
||||
let html = this.getEditText()
|
||||
html = this.sortHtmlNodeStyles(html)
|
||||
let list =
|
||||
nodes && nodes.length > 0 ? nodes : this.mindMap.renderer.activeNodeList
|
||||
list.forEach(node => {
|
||||
@@ -360,12 +392,13 @@ class RichText {
|
||||
// }
|
||||
this.mindMap.render()
|
||||
})
|
||||
this.mindMap.emit('hide_text_edit', this.textEditNode, list, this.node)
|
||||
const node = this.node
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.showTextEdit = false
|
||||
this.mindMap.emit('rich_text_selection_change', false)
|
||||
this.node = null
|
||||
this.isInserting = false
|
||||
this.mindMap.emit('hide_text_edit', this.textEditNode, list, node)
|
||||
}
|
||||
|
||||
// 初始化Quill富文本编辑器
|
||||
@@ -490,6 +523,11 @@ class RichText {
|
||||
this.setTextStyleIfNotRichText(this.node)
|
||||
this.lostStyle = false
|
||||
}
|
||||
this.mindMap.emit('node_text_edit_change', {
|
||||
node: this.node,
|
||||
text: this.getEditText(),
|
||||
richText: true
|
||||
})
|
||||
})
|
||||
// 拦截粘贴,只允许粘贴纯文本
|
||||
// this.quill.clipboard.addMatcher(Node.TEXT_NODE, node => {
|
||||
@@ -511,6 +549,20 @@ class RichText {
|
||||
delta.ops = ops
|
||||
return delta
|
||||
})
|
||||
// 拦截图片的粘贴
|
||||
this.quill.root.addEventListener(
|
||||
'paste',
|
||||
e => {
|
||||
if (
|
||||
e.clipboardData &&
|
||||
e.clipboardData.files &&
|
||||
e.clipboardData.files.length
|
||||
) {
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 获取粘贴的文本的样式
|
||||
@@ -544,6 +596,16 @@ class RichText {
|
||||
this.isCompositing = true
|
||||
}
|
||||
|
||||
// 中文输入中
|
||||
onCompositionUpdate() {
|
||||
if (!this.showTextEdit || !this.node) return
|
||||
this.mindMap.emit('node_text_edit_change', {
|
||||
node: this.node,
|
||||
text: this.getEditText(),
|
||||
richText: true
|
||||
})
|
||||
}
|
||||
|
||||
// 中文输入结束
|
||||
onCompositionEnd() {
|
||||
if (!this.showTextEdit) {
|
||||
|
||||
@@ -41,7 +41,8 @@ class Select {
|
||||
|
||||
// 鼠标按下
|
||||
onMousedown(e) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
const { readonly, mousedownEventPreventDefault } = this.mindMap.opt
|
||||
if (readonly) {
|
||||
return
|
||||
}
|
||||
let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
|
||||
@@ -51,7 +52,9 @@ class Select {
|
||||
) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
if (mousedownEventPreventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
this.isMousedown = true
|
||||
this.cacheActiveList = [...this.mindMap.renderer.activeNodeList]
|
||||
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 秋天
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: '#fff2df',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 牛油果
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: '#e6f1de',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 黑金
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(18, 20, 20)',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 黑色幽默
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(27, 31, 34)',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 天空蓝
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(115, 161, 191)',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 脑残粉
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(191, 115, 148)',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 脑图经典
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: '#fff',
|
||||
// 连线的粗细
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 经典2
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
// 连线的粗细
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 经典3
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(94, 202, 110)',
|
||||
// 连线的粗细
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 经典4
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(30, 53, 86)',
|
||||
// 连线的粗细
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 经典蓝
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
// 连线的粗细
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 经典绿
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(123, 199, 120)',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 咖啡
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(173, 123, 91)',
|
||||
lineWidth: 4,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 课程绿
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(113, 195, 169)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 暗色
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(17, 68, 23)',
|
||||
// 连线的粗细
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 暗色2
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(75, 81, 78)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -82,8 +82,16 @@ export default {
|
||||
gradientStyle: false,
|
||||
startColor: '#549688',
|
||||
endColor: '#fff',
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
// 连线标记的位置,start(头部)、end(尾部),该配置在showLineMarker配置为true时生效
|
||||
lineMarkerDir: 'end'
|
||||
lineMarkerDir: 'end',
|
||||
// 节点鼠标hover和激活时显示的矩形边框的颜色,主题里不设置,默认会取hoverRectColor实例化选项的值
|
||||
hoverRectColor: '',
|
||||
// 点鼠标hover和激活时显示的矩形边框的圆角大小
|
||||
hoverRectRadius: 5
|
||||
// paddingX: 15,
|
||||
// paddingY: 5
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -94,7 +102,7 @@ export default {
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#565656',
|
||||
fontSize: 16,
|
||||
fontWeight: 'noraml',
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
@@ -105,7 +113,13 @@ export default {
|
||||
gradientStyle: false,
|
||||
startColor: '#549688',
|
||||
endColor: '#fff',
|
||||
lineMarkerDir: 'end'
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
lineMarkerDir: 'end',
|
||||
hoverRectColor: '',
|
||||
hoverRectRadius: 5
|
||||
// paddingX: 15,
|
||||
// paddingY: 5
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
@@ -116,7 +130,7 @@ export default {
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#6a6d6c',
|
||||
fontSize: 14,
|
||||
fontWeight: 'noraml',
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
@@ -127,7 +141,13 @@ export default {
|
||||
gradientStyle: false,
|
||||
startColor: '#549688',
|
||||
endColor: '#fff',
|
||||
lineMarkerDir: 'end'
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
lineMarkerDir: 'end',
|
||||
hoverRectColor: '',
|
||||
hoverRectRadius: 5
|
||||
// paddingX: 15,
|
||||
// paddingY: 5
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -138,7 +158,7 @@ export default {
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#565656',
|
||||
fontSize: 16,
|
||||
fontWeight: 'noraml',
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
@@ -148,7 +168,13 @@ export default {
|
||||
textDecoration: 'none',
|
||||
gradientStyle: false,
|
||||
startColor: '#549688',
|
||||
endColor: '#fff'
|
||||
endColor: '#fff',
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
hoverRectColor: '',
|
||||
hoverRectRadius: 5
|
||||
// paddingX: 15,
|
||||
// paddingY: 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +205,11 @@ const nodeSizeIndependenceList = [
|
||||
'gradientStyle',
|
||||
'lineRadius',
|
||||
'startColor',
|
||||
'endColor'
|
||||
'endColor',
|
||||
'startDir',
|
||||
'endDir',
|
||||
'hoverRectColor',
|
||||
'hoverRectRadius'
|
||||
]
|
||||
export const checkIsNodeSizeIndependenceConfig = config => {
|
||||
let keys = Object.keys(config)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 泥土黄
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(191, 147, 115)',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 清新绿
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: '#333',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 清新红
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(191, 115, 115)',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 金色vip
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 56, 62)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 绿叶
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(40, 193, 84)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 深夜办公室
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(32, 37, 49)',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 小黄人
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 薄荷
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(104, 204, 202)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 橙汁
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: '#070616',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 粉红葡萄
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(166, 101, 106)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 红色精神
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(255, 238, 228)',
|
||||
// 连线的颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 浪漫紫
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(123, 115, 191)',
|
||||
// 背景颜色
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 简约黑
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(34, 34, 34)',
|
||||
lineWidth: 4,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 天清绿
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: '#fff',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
import { mergeTheme } from '../utils'
|
||||
|
||||
// 活力橙
|
||||
export default merge(defaultTheme, {
|
||||
export default mergeTheme(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(254, 146, 0)',
|
||||
lineWidth: 3,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '../constants/constant'
|
||||
import MersenneTwister from './mersenneTwister'
|
||||
import { ForeignObject } from '@svgdotjs/svg.js'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 深度优先遍历树
|
||||
export const walk = (
|
||||
@@ -1184,9 +1185,18 @@ export const handleInputPasteText = (e, text) => {
|
||||
// 去除格式
|
||||
text = getTextFromHtml(text)
|
||||
// 去除换行
|
||||
text = text.replaceAll(/\n/g, '')
|
||||
const node = document.createTextNode(text)
|
||||
selection.getRangeAt(0).insertNode(node)
|
||||
// text = text.replaceAll(/\n/g, '')
|
||||
const textArr = text.split(/\n/g)
|
||||
const fragment = document.createDocumentFragment()
|
||||
textArr.forEach((item, index) => {
|
||||
const node = document.createTextNode(item)
|
||||
fragment.appendChild(node)
|
||||
if (index < textArr.length - 1) {
|
||||
const br = document.createElement('br')
|
||||
fragment.appendChild(br)
|
||||
}
|
||||
})
|
||||
selection.getRangeAt(0).insertNode(fragment)
|
||||
selection.collapseToEnd()
|
||||
}
|
||||
|
||||
@@ -1601,3 +1611,12 @@ export const sortNodeList = nodeList => {
|
||||
})
|
||||
return nodeList
|
||||
}
|
||||
|
||||
// 合并主题配置
|
||||
export const mergeTheme = (dest, source) => {
|
||||
return merge(dest, source, {
|
||||
arrayMerge: (destinationArray, sourceArray) => {
|
||||
return sourceArray
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const hljs = require('highlight.js')
|
||||
const md = require('markdown-it')({
|
||||
html: true,
|
||||
xhtmlOut: true,
|
||||
highlight: function(str, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return (
|
||||
'<pre class="hljs"><code>' +
|
||||
hljs.highlight(str, {
|
||||
language: lang,
|
||||
ignoreIllegals: true
|
||||
}).value +
|
||||
'</code></pre>'
|
||||
)
|
||||
} catch (__) {}
|
||||
}
|
||||
|
||||
return (
|
||||
'<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
|
||||
)
|
||||
}
|
||||
}).use(require('markdown-it-checkbox'))
|
||||
|
||||
const templatePath = path.join(__dirname, '../src/pages/Doc/Template.vue')
|
||||
|
||||
exports.transformMdToVue = (content) => {
|
||||
let result = md.render(content)
|
||||
let template = fs.readFileSync(templatePath, 'utf-8')
|
||||
return template.replace('$$$$', result)
|
||||
}
|
||||
BIN
web/src/assets/avatar/Joe.jpg
Normal file
BIN
web/src/assets/avatar/Joe.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
web/src/assets/avatar/Lawliet.jpg
Normal file
BIN
web/src/assets/avatar/Lawliet.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 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: 34 KiB |
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2479351 */
|
||||
src: url('iconfont.woff2?t=1719815803051') format('woff2'),
|
||||
url('iconfont.woff?t=1719815803051') format('woff'),
|
||||
url('iconfont.ttf?t=1719815803051') format('truetype');
|
||||
src: url('iconfont.woff2?t=1726022313538') format('woff2'),
|
||||
url('iconfont.woff?t=1726022313538') format('woff'),
|
||||
url('iconfont.ttf?t=1726022313538') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconfile-excel:before {
|
||||
content: "\e7b7";
|
||||
}
|
||||
|
||||
.iconfreemind:before {
|
||||
content: "\e97d";
|
||||
}
|
||||
|
||||
.iconwaikuang:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -85,12 +85,14 @@ export const formulaList = [
|
||||
'\\begin{cases}3x + 5y + z \\\\7x - 2y + 4z \\\\-6x + 3y + 2z\\end{cases}'
|
||||
]
|
||||
|
||||
// 支持某种连线类型的结构
|
||||
export const supportLineStyleLayoutsMap = {
|
||||
curve: [
|
||||
'logicalStructure',
|
||||
'logicalStructureLeft',
|
||||
'mindMap',
|
||||
'verticalTimeline'
|
||||
'verticalTimeline',
|
||||
'organizationStructure'
|
||||
],
|
||||
direct: [
|
||||
'logicalStructure',
|
||||
@@ -101,6 +103,7 @@ export const supportLineStyleLayoutsMap = {
|
||||
]
|
||||
}
|
||||
|
||||
// 直线模式支持设置圆角的结构
|
||||
export const supportLineRadiusLayouts = [
|
||||
'logicalStructure',
|
||||
'logicalStructureLeft',
|
||||
@@ -108,6 +111,7 @@ export const supportLineRadiusLayouts = [
|
||||
'verticalTimeline'
|
||||
]
|
||||
|
||||
// 支持只显示底边直线风格的结构
|
||||
export const supportNodeUseLineStyleLayouts = [
|
||||
'logicalStructure',
|
||||
'logicalStructureLeft',
|
||||
@@ -116,10 +120,12 @@ export const supportNodeUseLineStyleLayouts = [
|
||||
'organizationStructure'
|
||||
]
|
||||
|
||||
// 支持曲线模式下,根节点样式和其他节点样式保持一致的结构
|
||||
export const supportRootLineKeepSameInCurveLayouts = [
|
||||
'logicalStructure',
|
||||
'logicalStructureLeft',
|
||||
'mindMap'
|
||||
'mindMap',
|
||||
'organizationStructure'
|
||||
]
|
||||
|
||||
// 彩虹线条配置
|
||||
|
||||
@@ -495,6 +495,18 @@ export const downTypeList = [
|
||||
type: 'txt',
|
||||
icon: 'iconTXT',
|
||||
desc: 'Plain text file'
|
||||
},
|
||||
{
|
||||
name: 'FreeMind',
|
||||
type: 'mm',
|
||||
icon: 'iconfreemind',
|
||||
desc: 'FreeMind software format'
|
||||
},
|
||||
{
|
||||
name: 'Excel',
|
||||
type: 'xlsx',
|
||||
icon: 'iconfile-excel',
|
||||
desc: 'Excel software format'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -557,3 +569,55 @@ export const numberLevelList = [
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
|
||||
// 背景渐变方向
|
||||
export const linearGradientDirList = [
|
||||
{
|
||||
name: 'Left to right',
|
||||
value: '1',
|
||||
start: [0, 0],
|
||||
end: [1, 0]
|
||||
},
|
||||
{
|
||||
name: 'Right to left',
|
||||
value: '2',
|
||||
start: [1, 0],
|
||||
end: [0, 0]
|
||||
},
|
||||
{
|
||||
name: 'Top to bottom',
|
||||
value: '3',
|
||||
start: [0, 0],
|
||||
end: [0, 1]
|
||||
},
|
||||
{
|
||||
name: 'Bottom to top',
|
||||
value: '4',
|
||||
start: [0, 1],
|
||||
end: [0, 0]
|
||||
},
|
||||
{
|
||||
name: 'Left top to right bottom',
|
||||
value: '5',
|
||||
start: [0, 0],
|
||||
end: [1, 1]
|
||||
},
|
||||
{
|
||||
name: 'Left bottom to right top',
|
||||
value: '6',
|
||||
start: [0, 1],
|
||||
end: [1, 0]
|
||||
},
|
||||
{
|
||||
name: 'Right top to left bottom',
|
||||
value: '7',
|
||||
start: [1, 0],
|
||||
end: [0, 1]
|
||||
},
|
||||
{
|
||||
name: 'Right bottom to left top',
|
||||
value: '8',
|
||||
start: [1, 1],
|
||||
end: [0, 0]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -21,7 +21,8 @@ import {
|
||||
shapeListMap as shapeListMapZh,
|
||||
lineStyleMap as lineStyleMapZh,
|
||||
numberTypeList as numberTypeListZh,
|
||||
numberLevelList as numberLevelListZh
|
||||
numberLevelList as numberLevelListZh,
|
||||
linearGradientDirList as linearGradientDirListZh
|
||||
} from './zh'
|
||||
import {
|
||||
fontFamilyList as fontFamilyListEn,
|
||||
@@ -36,82 +37,120 @@ import {
|
||||
backgroundSizeList as backgroundSizeListEn,
|
||||
downTypeList as downTypeListEn,
|
||||
numberTypeList as numberTypeListEn,
|
||||
numberLevelList as numberLevelListEn
|
||||
numberLevelList as numberLevelListEn,
|
||||
linearGradientDirList as linearGradientDirListEn
|
||||
} from './en'
|
||||
import {
|
||||
fontFamilyList as fontFamilyListZhtw,
|
||||
borderDasharrayList as borderDasharrayListZhtw,
|
||||
lineStyleList as lineStyleListZhtw,
|
||||
rootLineKeepSameInCurveList as rootLineKeepSameInCurveListZhtw,
|
||||
backgroundRepeatList as backgroundRepeatListZhtw,
|
||||
backgroundPositionList as backgroundPositionListZhtw,
|
||||
shortcutKeyList as shortcutKeyListZhtw,
|
||||
shapeList as shapeListZhtw,
|
||||
sidebarTriggerList as sidebarTriggerListZhtw,
|
||||
backgroundSizeList as backgroundSizeListZhtw,
|
||||
downTypeList as downTypeListZhtw,
|
||||
numberTypeList as numberTypeListZhtw,
|
||||
numberLevelList as numberLevelListZhtw,
|
||||
linearGradientDirList as linearGradientDirListZhtw
|
||||
} from './zhtw'
|
||||
|
||||
const fontFamilyList = {
|
||||
zh: fontFamilyListZh,
|
||||
en: fontFamilyListEn
|
||||
en: fontFamilyListEn,
|
||||
zhtw: fontFamilyListZhtw
|
||||
}
|
||||
|
||||
const borderDasharrayList = {
|
||||
zh: borderDasharrayListZh,
|
||||
en: borderDasharrayListEn
|
||||
en: borderDasharrayListEn,
|
||||
zhtw: borderDasharrayListZhtw
|
||||
}
|
||||
|
||||
const lineStyleList = {
|
||||
zh: lineStyleListZh,
|
||||
en: lineStyleListEn
|
||||
en: lineStyleListEn,
|
||||
zhtw: lineStyleListZhtw
|
||||
}
|
||||
|
||||
const lineStyleMap = {
|
||||
zh: lineStyleMapZh,
|
||||
en: lineStyleMapZh
|
||||
en: lineStyleMapZh,
|
||||
zhtw: lineStyleMapZh
|
||||
}
|
||||
|
||||
const rootLineKeepSameInCurveList = {
|
||||
zh: rootLineKeepSameInCurveListZh,
|
||||
en: rootLineKeepSameInCurveListEn
|
||||
en: rootLineKeepSameInCurveListEn,
|
||||
zhtw: rootLineKeepSameInCurveListZhtw
|
||||
}
|
||||
|
||||
const backgroundRepeatList = {
|
||||
zh: backgroundRepeatListZh,
|
||||
en: backgroundRepeatListEn
|
||||
en: backgroundRepeatListEn,
|
||||
zhtw: backgroundRepeatListZhtw
|
||||
}
|
||||
|
||||
const backgroundPositionList = {
|
||||
zh: backgroundPositionListZh,
|
||||
en: backgroundPositionListEn
|
||||
en: backgroundPositionListEn,
|
||||
zhtw: backgroundPositionListZhtw
|
||||
}
|
||||
|
||||
const backgroundSizeList = {
|
||||
zh: backgroundSizeListZh,
|
||||
en: backgroundSizeListEn
|
||||
en: backgroundSizeListEn,
|
||||
zhtw: backgroundSizeListZhtw
|
||||
}
|
||||
|
||||
const shortcutKeyList = {
|
||||
zh: shortcutKeyListZh,
|
||||
en: shortcutKeyListEn
|
||||
en: shortcutKeyListEn,
|
||||
zhtw: shortcutKeyListZhtw
|
||||
}
|
||||
|
||||
const shapeList = {
|
||||
zh: shapeListZh,
|
||||
en: shapeListEn
|
||||
en: shapeListEn,
|
||||
zhtw: shapeListZhtw
|
||||
}
|
||||
|
||||
const shapeListMap = {
|
||||
zh: shapeListMapZh,
|
||||
en: shapeListMapZh
|
||||
en: shapeListMapZh,
|
||||
zhtw: shapeListMapZh
|
||||
}
|
||||
|
||||
const sidebarTriggerList = {
|
||||
zh: sidebarTriggerListZh,
|
||||
en: sidebarTriggerListEn
|
||||
en: sidebarTriggerListEn,
|
||||
zhtw: sidebarTriggerListZhtw
|
||||
}
|
||||
|
||||
const downTypeList = {
|
||||
zh: downTypeListZh,
|
||||
en: downTypeListEn
|
||||
en: downTypeListEn,
|
||||
zhtw: downTypeListZhtw
|
||||
}
|
||||
|
||||
const numberTypeList = {
|
||||
zh: numberTypeListZh,
|
||||
en: numberTypeListEn
|
||||
en: numberTypeListEn,
|
||||
zhtw: numberTypeListZhtw
|
||||
}
|
||||
|
||||
const numberLevelList = {
|
||||
zh: numberLevelListZh,
|
||||
en: numberLevelListEn
|
||||
en: numberLevelListEn,
|
||||
zhtw: numberLevelListZhtw
|
||||
}
|
||||
|
||||
const linearGradientDirList = {
|
||||
zh: linearGradientDirListZh,
|
||||
en: linearGradientDirListEn,
|
||||
zhtw: linearGradientDirListZhtw
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -137,5 +176,6 @@ export {
|
||||
sidebarTriggerList,
|
||||
downTypeList,
|
||||
numberTypeList,
|
||||
numberLevelList
|
||||
numberLevelList,
|
||||
linearGradientDirList
|
||||
}
|
||||
|
||||
@@ -500,6 +500,10 @@ export const langList = [
|
||||
value: 'zh',
|
||||
name: '简体中文'
|
||||
},
|
||||
{
|
||||
value: 'zhtw',
|
||||
name: '繁體中文'
|
||||
},
|
||||
{
|
||||
value: 'en',
|
||||
name: 'English'
|
||||
@@ -589,6 +593,18 @@ export const downTypeList = [
|
||||
type: 'txt',
|
||||
icon: 'iconTXT',
|
||||
desc: '纯文本文件'
|
||||
},
|
||||
{
|
||||
name: 'FreeMind',
|
||||
type: 'mm',
|
||||
icon: 'iconfreemind',
|
||||
desc: 'FreeMind软件格式'
|
||||
},
|
||||
{
|
||||
name: 'Excel',
|
||||
type: 'xlsx',
|
||||
icon: 'iconfile-excel',
|
||||
desc: 'Excel软件格式'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -651,3 +667,55 @@ export const numberLevelList = [
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
|
||||
// 背景渐变方向
|
||||
export const linearGradientDirList = [
|
||||
{
|
||||
name: '从左到右',
|
||||
value: '1',
|
||||
start: [0, 0],
|
||||
end: [1, 0]
|
||||
},
|
||||
{
|
||||
name: '从右到左',
|
||||
value: '2',
|
||||
start: [1, 0],
|
||||
end: [0, 0]
|
||||
},
|
||||
{
|
||||
name: '从上到下',
|
||||
value: '3',
|
||||
start: [0, 0],
|
||||
end: [0, 1]
|
||||
},
|
||||
{
|
||||
name: '从下到上',
|
||||
value: '4',
|
||||
start: [0, 1],
|
||||
end: [0, 0]
|
||||
},
|
||||
{
|
||||
name: '从左上到右下',
|
||||
value: '5',
|
||||
start: [0, 0],
|
||||
end: [1, 1]
|
||||
},
|
||||
{
|
||||
name: '从左下到右上',
|
||||
value: '6',
|
||||
start: [0, 1],
|
||||
end: [1, 0]
|
||||
},
|
||||
{
|
||||
name: '从右上到左下',
|
||||
value: '7',
|
||||
start: [1, 0],
|
||||
end: [0, 1]
|
||||
},
|
||||
{
|
||||
name: '从右下到左上',
|
||||
value: '8',
|
||||
start: [1, 1],
|
||||
end: [0, 0]
|
||||
}
|
||||
]
|
||||
|
||||
623
web/src/config/zhtw.js
Normal file
623
web/src/config/zhtw.js
Normal file
@@ -0,0 +1,623 @@
|
||||
// 字型列表
|
||||
export const fontFamilyList = [
|
||||
{
|
||||
name: '宋體',
|
||||
value: '宋体, SimSun, Songti SC'
|
||||
},
|
||||
{
|
||||
name: '微軟雅黑',
|
||||
value: '微软雅黑, Microsoft YaHei'
|
||||
},
|
||||
{
|
||||
name: '楷體',
|
||||
value: '楷体, 楷体_GB2312, SimKai, STKaiti'
|
||||
},
|
||||
{
|
||||
name: '黑體',
|
||||
value: '黑体, SimHei, Heiti SC'
|
||||
},
|
||||
{
|
||||
name: '隸書',
|
||||
value: '隶书, SimLi'
|
||||
},
|
||||
{
|
||||
name: 'Andale Mono',
|
||||
value: 'andale mono'
|
||||
},
|
||||
{
|
||||
name: 'Arial',
|
||||
value: 'arial, helvetica, sans-serif'
|
||||
},
|
||||
{
|
||||
name: 'arialBlack',
|
||||
value: 'arial black, avant garde'
|
||||
},
|
||||
{
|
||||
name: 'Comic Sans Ms',
|
||||
value: 'comic sans ms'
|
||||
},
|
||||
{
|
||||
name: 'Impact',
|
||||
value: 'impact, chicago'
|
||||
},
|
||||
{
|
||||
name: 'Times New Roman',
|
||||
value: 'times new roman'
|
||||
},
|
||||
{
|
||||
name: 'Sans-Serif',
|
||||
value: 'sans-serif'
|
||||
},
|
||||
{
|
||||
name: 'serif',
|
||||
value: 'serif'
|
||||
}
|
||||
]
|
||||
|
||||
// 框線樣式
|
||||
export const borderDasharrayList = [
|
||||
{
|
||||
name: '實線',
|
||||
value: 'none'
|
||||
},
|
||||
{
|
||||
name: '虛線 1',
|
||||
value: '5,5'
|
||||
},
|
||||
{
|
||||
name: '虛線 2',
|
||||
value: '10,10'
|
||||
},
|
||||
{
|
||||
name: '虛線 3',
|
||||
value: '20,10,5,5,5,10'
|
||||
},
|
||||
{
|
||||
name: '虛線 4',
|
||||
value: '5, 5, 1, 5'
|
||||
},
|
||||
{
|
||||
name: '虛線 5',
|
||||
value: '15, 10, 5, 10, 15'
|
||||
},
|
||||
{
|
||||
name: '虛線 6',
|
||||
value: '1, 5'
|
||||
},
|
||||
{
|
||||
name: '虛線 7',
|
||||
value: '6, 4'
|
||||
}
|
||||
]
|
||||
|
||||
// 連線樣式
|
||||
export const lineStyleList = [
|
||||
{
|
||||
name: '直線',
|
||||
value: 'straight'
|
||||
},
|
||||
{
|
||||
name: '曲線',
|
||||
value: 'curve'
|
||||
},
|
||||
{
|
||||
name: '直接連線',
|
||||
value: 'direct'
|
||||
}
|
||||
]
|
||||
|
||||
// 曲線樣式中,根節點樣式是否和其他節點保持一致
|
||||
export const rootLineKeepSameInCurveList = [
|
||||
{
|
||||
name: '括號',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: '大括號',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
|
||||
// 圖片重複方式
|
||||
export const backgroundRepeatList = [
|
||||
{
|
||||
name: '不重複',
|
||||
value: 'no-repeat'
|
||||
},
|
||||
{
|
||||
name: '重複',
|
||||
value: 'repeat'
|
||||
},
|
||||
{
|
||||
name: '水平重複',
|
||||
value: 'repeat-x'
|
||||
},
|
||||
{
|
||||
name: '垂直重複',
|
||||
value: 'repeat-y'
|
||||
}
|
||||
]
|
||||
|
||||
// 背景圖片位置
|
||||
export const backgroundPositionList = [
|
||||
{
|
||||
name: '預設',
|
||||
value: '0% 0%'
|
||||
},
|
||||
{
|
||||
name: '左上',
|
||||
value: 'left top'
|
||||
},
|
||||
{
|
||||
name: '左中',
|
||||
value: 'left center'
|
||||
},
|
||||
{
|
||||
name: '左下',
|
||||
value: 'left bottom'
|
||||
},
|
||||
{
|
||||
name: '右上',
|
||||
value: 'right top'
|
||||
},
|
||||
{
|
||||
name: '右中',
|
||||
value: 'right center'
|
||||
},
|
||||
{
|
||||
name: '右下',
|
||||
value: 'right bottom'
|
||||
},
|
||||
{
|
||||
name: '中上',
|
||||
value: 'center top'
|
||||
},
|
||||
{
|
||||
name: '置中',
|
||||
value: 'center center'
|
||||
},
|
||||
{
|
||||
name: '中下',
|
||||
value: 'center bottom'
|
||||
}
|
||||
]
|
||||
|
||||
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
||||
const ctrl = isMac ? '⌘' : 'Ctrl'
|
||||
const enter = isMac ? 'Return' : 'Enter'
|
||||
const macFn = isMac ? 'fn + ' : ''
|
||||
|
||||
// 背景圖片大小
|
||||
export const backgroundSizeList = [
|
||||
{
|
||||
name: '自動',
|
||||
value: 'auto'
|
||||
},
|
||||
{
|
||||
name: '覆蓋',
|
||||
value: 'cover'
|
||||
},
|
||||
{
|
||||
name: '包含',
|
||||
value: 'contain'
|
||||
}
|
||||
]
|
||||
|
||||
// 快捷鍵列表
|
||||
export const shortcutKeyList = [
|
||||
{
|
||||
type: '節點操作',
|
||||
list: [
|
||||
{
|
||||
icon: 'icontianjiazijiedian',
|
||||
name: '插入子節點',
|
||||
value: 'Tab | Insert'
|
||||
},
|
||||
{
|
||||
icon: 'iconjiedian',
|
||||
name: '插入同層節點',
|
||||
value: enter
|
||||
},
|
||||
{
|
||||
icon: 'icondodeparent',
|
||||
name: '插入父節點',
|
||||
value: 'Shift + Tab'
|
||||
},
|
||||
{
|
||||
icon: 'iconshangyi',
|
||||
name: '上移節點',
|
||||
value: `${ctrl} + ↑`
|
||||
},
|
||||
{
|
||||
icon: 'iconxiayi',
|
||||
name: '下移節點',
|
||||
value: `${ctrl} + ↓`
|
||||
},
|
||||
{
|
||||
icon: 'icongaikuozonglan',
|
||||
name: '插入摘要',
|
||||
value: `${ctrl} + G`
|
||||
},
|
||||
{
|
||||
icon: 'iconzhankai',
|
||||
name: '展開/收合節點',
|
||||
value: '/'
|
||||
},
|
||||
{
|
||||
icon: 'iconshanchu',
|
||||
name: '刪除節點',
|
||||
value: 'Delete | Backspace'
|
||||
},
|
||||
{
|
||||
icon: 'iconshanchu',
|
||||
name: '僅刪除目前節點',
|
||||
value: 'Shift + Backspace'
|
||||
},
|
||||
{
|
||||
icon: 'iconfuzhi',
|
||||
name: '複製節點',
|
||||
value: `${ctrl} + C`
|
||||
},
|
||||
{
|
||||
icon: 'iconjianqie',
|
||||
name: '剪下節點',
|
||||
value: `${ctrl} + X`
|
||||
},
|
||||
{
|
||||
icon: 'iconniantie',
|
||||
name: '貼上節點',
|
||||
value: `${ctrl} + V`
|
||||
},
|
||||
{
|
||||
icon: 'iconbianji',
|
||||
name: '編輯節點',
|
||||
value: macFn + 'F2'
|
||||
},
|
||||
{
|
||||
icon: 'iconhuanhang',
|
||||
name: '文字換行',
|
||||
value: `Shift + ${enter}`
|
||||
},
|
||||
{
|
||||
icon: 'iconhoutui-shi',
|
||||
name: '復原',
|
||||
value: `${ctrl} + Z`
|
||||
},
|
||||
{
|
||||
icon: 'iconqianjin1',
|
||||
name: '重做',
|
||||
value: `${ctrl} + Y`
|
||||
},
|
||||
{
|
||||
icon: 'iconquanxuan',
|
||||
name: '全選',
|
||||
value: `${ctrl} + A`
|
||||
},
|
||||
{
|
||||
icon: 'iconquanxuan',
|
||||
name: '多重選擇',
|
||||
value: `右鍵 / ${ctrl} + 左鍵`
|
||||
},
|
||||
{
|
||||
icon: 'iconzhengli',
|
||||
name: '一鍵整理版面配置',
|
||||
value: `${ctrl} + L`
|
||||
},
|
||||
{
|
||||
icon: 'iconsousuo',
|
||||
name: '搜尋與取代',
|
||||
value: `${ctrl} + F`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: '畫布操作',
|
||||
list: [
|
||||
{
|
||||
icon: 'iconfangda',
|
||||
name: '放大',
|
||||
value: `${ctrl} + +`
|
||||
},
|
||||
{
|
||||
icon: 'iconsuoxiao',
|
||||
name: '縮小',
|
||||
value: `${ctrl} + -`
|
||||
},
|
||||
{
|
||||
icon: 'iconfangda',
|
||||
name: '放大/縮小',
|
||||
value: `${ctrl} + 滑鼠滾輪`
|
||||
},
|
||||
{
|
||||
icon: 'icondingwei',
|
||||
name: '回到根節點',
|
||||
value: `${ctrl} + ${enter}`
|
||||
},
|
||||
{
|
||||
icon: 'iconquanping1',
|
||||
name: '適應畫布',
|
||||
value: `${ctrl} + i`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: '大綱操作',
|
||||
list: [
|
||||
{
|
||||
icon: 'iconhuanhang',
|
||||
name: '文字換行',
|
||||
value: `Shift + ${enter}`
|
||||
},
|
||||
{
|
||||
icon: 'iconshanchu',
|
||||
name: '刪除節點',
|
||||
value: 'Delete'
|
||||
},
|
||||
{
|
||||
icon: 'icontianjiazijiedian',
|
||||
name: '插入子節點',
|
||||
value: 'Tab'
|
||||
},
|
||||
{
|
||||
icon: 'iconjiedian',
|
||||
name: '插入同層節點',
|
||||
value: enter
|
||||
},
|
||||
{
|
||||
icon: 'icondodeparent',
|
||||
name: '上移一層',
|
||||
value: 'Shift + Tab'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// 形狀列表
|
||||
export const shapeList = [
|
||||
{
|
||||
name: '矩形',
|
||||
value: 'rectangle'
|
||||
},
|
||||
{
|
||||
name: '菱形',
|
||||
value: 'diamond'
|
||||
},
|
||||
{
|
||||
name: '平行四邊形',
|
||||
value: 'parallelogram'
|
||||
},
|
||||
{
|
||||
name: '圓角矩形',
|
||||
value: 'roundedRectangle'
|
||||
},
|
||||
{
|
||||
name: '八角矩形',
|
||||
value: 'octagonalRectangle'
|
||||
},
|
||||
{
|
||||
name: '外三角矩形',
|
||||
value: 'outerTriangularRectangle'
|
||||
},
|
||||
{
|
||||
name: '內三角矩形',
|
||||
value: 'innerTriangularRectangle'
|
||||
},
|
||||
{
|
||||
name: '橢圓形',
|
||||
value: 'ellipse'
|
||||
},
|
||||
{
|
||||
name: '圓形',
|
||||
value: 'circle'
|
||||
}
|
||||
]
|
||||
|
||||
// 側邊欄列表
|
||||
export const sidebarTriggerList = [
|
||||
{
|
||||
name: '節點樣式',
|
||||
value: 'nodeStyle',
|
||||
icon: 'iconzhuti'
|
||||
},
|
||||
{
|
||||
name: '基礎樣式',
|
||||
value: 'baseStyle',
|
||||
icon: 'iconyangshi'
|
||||
},
|
||||
{
|
||||
name: '主題',
|
||||
value: 'theme',
|
||||
icon: 'iconjingzi'
|
||||
},
|
||||
{
|
||||
name: '結構',
|
||||
value: 'structure',
|
||||
icon: 'iconjiegou'
|
||||
},
|
||||
{
|
||||
name: '大綱',
|
||||
value: 'outline',
|
||||
icon: 'iconfuhao-dagangshu'
|
||||
},
|
||||
{
|
||||
name: '快捷鍵',
|
||||
value: 'shortcutKey',
|
||||
icon: 'iconjianpan'
|
||||
}
|
||||
]
|
||||
|
||||
// 下載類型列表
|
||||
export const downTypeList = [
|
||||
{
|
||||
name: '專用檔案',
|
||||
type: 'smm',
|
||||
icon: 'iconwenjian',
|
||||
desc: '可用於匯入'
|
||||
},
|
||||
{
|
||||
name: 'JSON',
|
||||
type: 'json',
|
||||
icon: 'iconjson',
|
||||
desc: '常見的資料交換格式,可用於匯入'
|
||||
},
|
||||
{
|
||||
name: '圖片',
|
||||
type: 'png',
|
||||
icon: 'iconPNG',
|
||||
desc: '適合檢視與分享'
|
||||
},
|
||||
{
|
||||
name: 'SVG',
|
||||
type: 'svg',
|
||||
icon: 'iconSVG',
|
||||
desc: '可縮放向量圖形'
|
||||
},
|
||||
{
|
||||
name: 'PDF',
|
||||
type: 'pdf',
|
||||
icon: 'iconpdf',
|
||||
desc: '適合列印'
|
||||
},
|
||||
{
|
||||
name: 'Markdown',
|
||||
type: 'md',
|
||||
icon: 'iconmarkdown',
|
||||
desc: '方便其他軟體開啟'
|
||||
},
|
||||
{
|
||||
name: 'XMind',
|
||||
type: 'xmind',
|
||||
icon: 'iconxmind',
|
||||
desc: 'XMind 檔案'
|
||||
},
|
||||
{
|
||||
name: 'Txt',
|
||||
type: 'txt',
|
||||
icon: 'iconTXT',
|
||||
desc: '純文字檔案'
|
||||
},
|
||||
{
|
||||
name: 'FreeMind',
|
||||
type: 'mm',
|
||||
icon: 'iconfreemind',
|
||||
desc: 'FreeMind軟體格式'
|
||||
},
|
||||
{
|
||||
name: 'Excel',
|
||||
type: 'xlsx',
|
||||
icon: 'iconfile-excel',
|
||||
desc: 'Excel軟體格式'
|
||||
}
|
||||
]
|
||||
|
||||
// 編號類型列表
|
||||
export const numberTypeList = [
|
||||
{
|
||||
name: '無編號',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
name: '1, 2, 3',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: '1., 2., 3.',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
name: '(1), (2), (3)',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
name: 'a., b., c.',
|
||||
value: 4
|
||||
},
|
||||
{
|
||||
name: 'A., B., C.',
|
||||
value: 5
|
||||
},
|
||||
{
|
||||
name: 'i., ii., iii.',
|
||||
value: 6
|
||||
},
|
||||
{
|
||||
name: 'I., II., III.',
|
||||
value: 7
|
||||
},
|
||||
{
|
||||
name: '一、, 二、, 三、',
|
||||
value: 8
|
||||
}
|
||||
]
|
||||
|
||||
// 編號層級列表
|
||||
export const numberLevelList = [
|
||||
{
|
||||
name: '編號第一層',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: '編號前兩層',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
name: '編號前三層',
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
name: '編號每一層',
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
|
||||
// 背景渐变方向
|
||||
export const linearGradientDirList = [
|
||||
{
|
||||
name: '从左到右',
|
||||
value: '1',
|
||||
start: [0, 0],
|
||||
end: [1, 0]
|
||||
},
|
||||
{
|
||||
name: '从右到左',
|
||||
value: '2',
|
||||
start: [1, 0],
|
||||
end: [0, 0]
|
||||
},
|
||||
{
|
||||
name: '从上到下',
|
||||
value: '3',
|
||||
start: [0, 0],
|
||||
end: [0, 1]
|
||||
},
|
||||
{
|
||||
name: '从下到上',
|
||||
value: '4',
|
||||
start: [0, 1],
|
||||
end: [0, 0]
|
||||
},
|
||||
{
|
||||
name: '从左上到右下',
|
||||
value: '5',
|
||||
start: [0, 0],
|
||||
end: [1, 1]
|
||||
},
|
||||
{
|
||||
name: '从左下到右上',
|
||||
value: '6',
|
||||
start: [0, 1],
|
||||
end: [1, 0]
|
||||
},
|
||||
{
|
||||
name: '从右上到左下',
|
||||
value: '7',
|
||||
start: [1, 0],
|
||||
end: [0, 1]
|
||||
},
|
||||
{
|
||||
name: '从右下到左上',
|
||||
value: '8',
|
||||
start: [1, 1],
|
||||
end: [0, 0]
|
||||
}
|
||||
]
|
||||
@@ -111,7 +111,8 @@ export default {
|
||||
copyToPng: 'Png',
|
||||
copySuccess: 'Copy success',
|
||||
copyFail: 'Copy fail',
|
||||
number: 'Number child nodes'
|
||||
number: 'Number child nodes',
|
||||
expandNodeChild: 'Expand all sub nodes'
|
||||
},
|
||||
count: {
|
||||
words: 'Words',
|
||||
@@ -157,8 +158,9 @@ export default {
|
||||
import: {
|
||||
title: 'Import',
|
||||
selectFile: 'Select file',
|
||||
supportFile: 'Support .smm、.json、.xmind、.xlsx、.md file',
|
||||
enableFileTip: 'Please select .smm、.json、.xmind、.xlsx、.md file',
|
||||
support: 'Support',
|
||||
file: 'file',
|
||||
pleaseSelect: 'Please select',
|
||||
maxFileNum: 'At most one file can be selected',
|
||||
notSelectTip: 'Please select the file to import',
|
||||
fileContentError: 'The file content is incorrect',
|
||||
@@ -238,7 +240,8 @@ export default {
|
||||
endColor: 'End',
|
||||
arrowDir: 'Arrow dir',
|
||||
arrowDirStart: 'Start',
|
||||
arrowDirEnd: 'End'
|
||||
arrowDirEnd: 'End',
|
||||
direction: 'Direction'
|
||||
},
|
||||
theme: {
|
||||
title: 'Theme',
|
||||
@@ -305,7 +308,8 @@ export default {
|
||||
yes: 'Yes',
|
||||
no: 'No',
|
||||
exportError: 'Export failed',
|
||||
dragTip: 'Release here to import the file'
|
||||
dragTip: 'Release here to import the file',
|
||||
deleteNodeImgTip: 'Are you sure to delete the node image?'
|
||||
},
|
||||
mouseAction: {
|
||||
tip1:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import en from './en_us'
|
||||
import zh from './zh_cn'
|
||||
import zhtw from './zh_tw'
|
||||
|
||||
export default {
|
||||
zh,
|
||||
zhtw,
|
||||
en
|
||||
}
|
||||
|
||||
@@ -111,7 +111,8 @@ export default {
|
||||
copyToPng: '图片',
|
||||
copySuccess: '复制成功',
|
||||
copyFail: '复制失败',
|
||||
number: '编号其子节点'
|
||||
number: '编号其子节点',
|
||||
expandNodeChild: '展开所有下级节点'
|
||||
},
|
||||
count: {
|
||||
words: '字数',
|
||||
@@ -155,8 +156,9 @@ export default {
|
||||
import: {
|
||||
title: '导入',
|
||||
selectFile: '选取文件',
|
||||
supportFile: '支持.smm、.json、.xmind、.xlsx、.md文件',
|
||||
enableFileTip: '请选择.smm、.json、.xmind、.xlsx、.md文件',
|
||||
support: '支持',
|
||||
file: '文件',
|
||||
pleaseSelect: '请选择',
|
||||
maxFileNum: '最多只能选择一个文件',
|
||||
notSelectTip: '请选择要导入的文件',
|
||||
fileContentError: '文件内容有误',
|
||||
@@ -236,7 +238,8 @@ export default {
|
||||
endColor: '结束',
|
||||
arrowDir: '箭头位置',
|
||||
arrowDirStart: '头部',
|
||||
arrowDirEnd: '尾部'
|
||||
arrowDirEnd: '尾部',
|
||||
direction: '方向'
|
||||
},
|
||||
theme: {
|
||||
title: '主题',
|
||||
@@ -299,7 +302,8 @@ export default {
|
||||
yes: '是',
|
||||
no: '否',
|
||||
exportError: '导出失败',
|
||||
dragTip: '在此释放以导入该文件'
|
||||
dragTip: '在此释放以导入该文件',
|
||||
deleteNodeImgTip: '是否确认删除该节点图片?'
|
||||
},
|
||||
mouseAction: {
|
||||
tip1: '当前:左键拖动画布,右键框选节点',
|
||||
|
||||
379
web/src/lang/zh_tw.js
Normal file
379
web/src/lang/zh_tw.js
Normal file
@@ -0,0 +1,379 @@
|
||||
export default {
|
||||
baseStyle: {
|
||||
title: '基本樣式',
|
||||
background: '背景',
|
||||
color: '顏色',
|
||||
image: '圖片',
|
||||
imageRepeat: '圖片重複',
|
||||
imagePosition: '圖片位置',
|
||||
imageSize: '圖片大小',
|
||||
line: '連線',
|
||||
width: '寬度',
|
||||
style: '樣式',
|
||||
lineRadius: '圓角半徑',
|
||||
lineOfOutline: '概要連線',
|
||||
showArrow: '顯示箭頭',
|
||||
nodePadding: '節點內距',
|
||||
nodeMargin: '節點外距',
|
||||
horizontal: '水平',
|
||||
vertical: '垂直',
|
||||
maximumWidth: '最大寬度',
|
||||
maximumHeight: '最大高度',
|
||||
icon: '圖示',
|
||||
size: '大小',
|
||||
level2Node: '第二層節點',
|
||||
belowLevel2Node: '第三層及以下節點',
|
||||
nodeBorderType: '節點邊框樣式',
|
||||
nodeUseLineStyle: '僅使用底邊框樣式',
|
||||
otherConfig: '其他設定',
|
||||
enableFreeDrag: '啟用節點自由拖曳 (Beta)',
|
||||
openPerformance: '啟用效能模式',
|
||||
watermark: '浮水印',
|
||||
showWatermark: '顯示浮水印',
|
||||
onlyExport: '僅在匯出時顯示',
|
||||
watermarkDefaultText: '浮水印文字',
|
||||
watermarkText: '浮水印文字',
|
||||
watermarkTextColor: '文字顏色',
|
||||
watermarkLineSpacing: '行距',
|
||||
watermarkTextSpacing: '文字間距',
|
||||
watermarkAngle: '旋轉角度',
|
||||
watermarkTextOpacity: '文字透明度',
|
||||
watermarkTextFontSize: '字型大小',
|
||||
belowNode: '顯示在節點下方',
|
||||
isEnableNodeRichText: '啟用節點豐富文字編輯',
|
||||
mousewheelAction: '滑鼠滾輪行為',
|
||||
zoomView: '縮放檢視',
|
||||
moveViewUpDown: '上下移動檢視',
|
||||
associativeLine: '關聯線',
|
||||
associativeLineWidth: '寬度',
|
||||
associativeLineColor: '顏色',
|
||||
associativeLineActiveWidth: '啟用時寬度',
|
||||
associativeLineActiveColor: '啟用時顏色',
|
||||
mousewheelZoomActionReverse: '滑鼠滾輪縮放',
|
||||
mousewheelZoomActionReverse1: '向前縮小,向後放大',
|
||||
mousewheelZoomActionReverse2: '向前放大,向後縮小',
|
||||
createNewNodeBehavior: '建立新節點行為',
|
||||
default: '啟用新節點並進入編輯',
|
||||
notActive: '不啟用新節點',
|
||||
activeOnly: '僅啟用新節點,不進入編輯',
|
||||
rootStyle: '根節點',
|
||||
associativeLineText: '關聯線文字',
|
||||
fontFamily: '字型',
|
||||
fontSize: '字型大小',
|
||||
isShowScrollbar: '顯示捲軸',
|
||||
isUseHandDrawnLikeStyle: '使用手繪風格',
|
||||
rootLineStartPos: '根節點連線起始位置',
|
||||
center: '中心',
|
||||
edge: '邊緣',
|
||||
rainbowLines: '彩虹線條',
|
||||
notUseRainbowLines: '不使用彩虹線條',
|
||||
outerFramePadding: '外框內距'
|
||||
},
|
||||
color: {
|
||||
moreColor: '更多顏色'
|
||||
},
|
||||
contextmenu: {
|
||||
insertSiblingNode: '插入同層節點',
|
||||
insertChildNode: '插入子節點',
|
||||
insertParentNode: '插入父節點',
|
||||
insertSummary: '插入概要',
|
||||
moveUpNode: '上移節點',
|
||||
moveDownNode: '下移節點',
|
||||
deleteNode: '刪除節點',
|
||||
deleteCurrentNode: '僅刪除目前節點',
|
||||
copyNode: '複製節點',
|
||||
cutNode: '剪下節點',
|
||||
pasteNode: '貼上節點',
|
||||
backCenter: '回到根節點',
|
||||
expandAll: '展開全部',
|
||||
unExpandAll: '收合全部',
|
||||
expandTo: '展開至',
|
||||
arrangeLayout: '一鍵整理版面',
|
||||
level1: '第一層主題',
|
||||
level2: '第二層主題',
|
||||
level3: '第三層主題',
|
||||
level4: '第四層主題',
|
||||
level5: '第五層主題',
|
||||
level6: '第六層主題',
|
||||
zenMode: '禪模式',
|
||||
fitCanvas: '適應畫布',
|
||||
removeImage: '移除圖片',
|
||||
removeHyperlink: '移除超連結',
|
||||
removeNote: '移除備註',
|
||||
removeCustomStyles: '一鍵移除自訂樣式',
|
||||
removeAllNodeCustomStyles: '一鍵移除所有節點自訂樣式',
|
||||
exportNodeToPng: '匯出此節點為圖片',
|
||||
copyToClipboard: '複製到剪貼簿',
|
||||
copyToSmm: 'SMM',
|
||||
copyToJson: 'JSON',
|
||||
copyToMarkdown: 'Markdown',
|
||||
copyToTxt: 'Txt',
|
||||
copyToPng: '圖片',
|
||||
copySuccess: '複製成功',
|
||||
copyFail: '複製失敗',
|
||||
number: '將其子節點編號',
|
||||
expandNodeChild: '展開所有下級節點'
|
||||
},
|
||||
count: {
|
||||
words: '字數',
|
||||
nodes: '節點數'
|
||||
},
|
||||
dialog: {
|
||||
cancel: '取消',
|
||||
confirm: '確定'
|
||||
},
|
||||
export: {
|
||||
title: '匯出',
|
||||
filename: '檔案名稱',
|
||||
include: '包含主題、結構等設定資料',
|
||||
dedicatedFile: '專用檔案',
|
||||
jsonFile: 'JSON 檔案',
|
||||
imageFile: '圖片檔案',
|
||||
svgFile: 'SVG 檔案',
|
||||
pdfFile: 'PDF 檔案',
|
||||
markdownFile: 'Markdown 檔案',
|
||||
tips: '提示:.smm 和 .json 檔案可以匯入',
|
||||
isTransparent: '背景透明',
|
||||
pngTips: '提示:在豐富文字模式下匯出圖片非常耗時,建議匯出為 SVG 格式',
|
||||
svgTips: '提示:在豐富文字模式下匯出圖片非常耗時',
|
||||
transformingDomToImages: '正在轉換節點:',
|
||||
notifyTitle: '訊息',
|
||||
notifyMessage: '如果沒有觸發下載,請檢查是否被瀏覽器封鎖',
|
||||
paddingX: '水平內距',
|
||||
paddingY: '垂直內距',
|
||||
useMultiPageExport: '多頁匯出',
|
||||
defaultFileName: '心智圖',
|
||||
addFooterText: '在底部新增文字',
|
||||
addFooterTextPlaceholder: '例如:來自 simple-mind-map'
|
||||
},
|
||||
fullscreen: {
|
||||
fullscreenShow: '全螢幕檢視',
|
||||
fullscreenEdit: '全螢幕編輯'
|
||||
},
|
||||
demonstrate: {
|
||||
demonstrate: '進入展示模式'
|
||||
},
|
||||
import: {
|
||||
title: '匯入',
|
||||
selectFile: '選擇檔案',
|
||||
support: '支援',
|
||||
file: '檔案',
|
||||
pleaseSelect: '請選擇',
|
||||
maxFileNum: '最多只能選擇一個檔案',
|
||||
notSelectTip: '請選擇要匯入的檔案',
|
||||
fileContentError: '檔案內容有誤',
|
||||
importSuccess: '匯入成功',
|
||||
fileParsingFailed: '檔案解析失敗',
|
||||
xmindCanvasSelectDialogTitle: '選擇要匯入的畫布'
|
||||
},
|
||||
navigatorToolbar: {
|
||||
openMiniMap: '開啟小地圖',
|
||||
closeMiniMap: '關閉小地圖',
|
||||
readonly: '切換為唯讀模式',
|
||||
edit: '切換為編輯模式',
|
||||
backToRoot: '回到根節點',
|
||||
changeSourceCodeEdit: '切換為原始碼編輯模式'
|
||||
},
|
||||
nodeHyperlink: {
|
||||
title: '超連結',
|
||||
link: '連結',
|
||||
name: '名稱'
|
||||
},
|
||||
nodeIcon: {
|
||||
title: '圖示'
|
||||
},
|
||||
nodeImage: {
|
||||
title: '圖片',
|
||||
imgTitle: '圖片標題'
|
||||
},
|
||||
nodeNote: {
|
||||
title: '備註'
|
||||
},
|
||||
nodeTag: {
|
||||
title: '標籤',
|
||||
addTip: '請按 Enter 鍵新增'
|
||||
},
|
||||
outline: {
|
||||
title: '大綱',
|
||||
nodeDefaultText: '分支節點'
|
||||
},
|
||||
scale: {
|
||||
zoomIn: '放大',
|
||||
zoomOut: '縮小'
|
||||
},
|
||||
shortcutKey: {
|
||||
title: '快速鍵'
|
||||
},
|
||||
strusture: {
|
||||
title: '結構'
|
||||
},
|
||||
style: {
|
||||
title: '節點樣式',
|
||||
normal: '常態',
|
||||
active: '選取狀態',
|
||||
text: '文字',
|
||||
fontFamily: '字型',
|
||||
fontSize: '字型大小',
|
||||
lineHeight: '行高',
|
||||
color: '顏色',
|
||||
addFontWeight: '粗體',
|
||||
italic: '斜體',
|
||||
textDecoration: '文字裝飾',
|
||||
none: '無',
|
||||
underline: '底線',
|
||||
lineThrough: '刪除線',
|
||||
overline: '上劃線',
|
||||
border: '邊框',
|
||||
style: '樣式',
|
||||
width: '寬度',
|
||||
borderRadius: '圓角',
|
||||
background: '背景',
|
||||
shape: '形狀',
|
||||
line: '線條',
|
||||
nodePadding: '節點內距',
|
||||
horizontal: '水平',
|
||||
vertical: '垂直',
|
||||
gradientStyle: '漸層',
|
||||
startColor: '起始',
|
||||
endColor: '結束',
|
||||
arrowDir: '箭頭位置',
|
||||
arrowDirStart: '頭部',
|
||||
arrowDirEnd: '尾部'
|
||||
},
|
||||
theme: {
|
||||
title: '主題',
|
||||
classics: '經典',
|
||||
dark: '深色',
|
||||
simple: '簡約',
|
||||
coverTip: '您目前已自訂過基本樣式,是否要覆蓋?',
|
||||
tip: '提示',
|
||||
cover: '覆蓋',
|
||||
reserve: '保留'
|
||||
},
|
||||
toolbar: {
|
||||
undo: '復原',
|
||||
redo: '重做',
|
||||
insertSiblingNode: '同層節點',
|
||||
insertChildNode: '子節點',
|
||||
deleteNode: '刪除節點',
|
||||
image: '圖片',
|
||||
icon: '圖示',
|
||||
link: '超連結',
|
||||
note: '備註',
|
||||
tag: '標籤',
|
||||
summary: '摘要',
|
||||
displayOutline: '顯示大綱',
|
||||
baseStyle: '基本樣式',
|
||||
theme: '主題',
|
||||
strusture: '結構',
|
||||
newFile: '新增檔案',
|
||||
openFile: '開啟檔案',
|
||||
saveAs: '另存新檔',
|
||||
import: '匯入',
|
||||
export: '匯出',
|
||||
shortcutKey: '快速鍵',
|
||||
associativeLine: '關聯線',
|
||||
painter: '格式刷',
|
||||
formula: '公式',
|
||||
attachment: '附件',
|
||||
outerFrame: '外框',
|
||||
more: '更多',
|
||||
selectFileTip: '請選擇檔案',
|
||||
notSupportTip: '您的瀏覽器不支援此功能,或者目前頁面非 HTTPS 協定',
|
||||
tip: '提示',
|
||||
editingLocalFileTipFront: '目前正在編輯您電腦上的【',
|
||||
editingLocalFileTipEnd: '】檔案',
|
||||
fileContentError: '檔案內容有誤',
|
||||
fileOpenFailed: '檔案開啟失敗',
|
||||
defaultFileName: '心智圖',
|
||||
creatingTip: '正在建立檔案',
|
||||
directory: '目錄',
|
||||
newFileTip: '新增檔案前,請先匯出目前編輯的檔案,以免內容遺失',
|
||||
openFileTip: '開啟檔案前,請先匯出目前編輯的檔案,以免內容遺失'
|
||||
},
|
||||
edit: {
|
||||
newFeatureNoticeTitle: '新功能提醒',
|
||||
newFeatureNoticeMessage:
|
||||
'本次更新支援了節點豐富文字編輯,但存在一些缺陷,最主要的影響是匯出為圖片的時間與節點數量成正比,所以如果比較依賴匯出功能,可以透過【基本樣式】-【其他設定】-【是否啟用節點豐富文字編輯】設定關閉豐富文字編輯模式。',
|
||||
root: '根節點',
|
||||
splitByWrap: '是否根據換行自動分割節點?',
|
||||
tip: '提示',
|
||||
yes: '是',
|
||||
no: '否',
|
||||
exportError: '匯出失敗',
|
||||
dragTip: '在此釋放以匯入檔案'
|
||||
},
|
||||
mouseAction: {
|
||||
tip1: '目前:左鍵拖曳畫布,右鍵框選節點',
|
||||
tip2: '目前:左鍵框選節點,右鍵拖曳畫布'
|
||||
},
|
||||
search: {
|
||||
searchPlaceholder: '請輸入搜尋內容',
|
||||
replacePlaceholder: '請輸入取代內容',
|
||||
replace: '取代',
|
||||
replaceAll: '全部取代',
|
||||
cancel: '取消',
|
||||
noResult: '查無結果'
|
||||
},
|
||||
nodeIconSidebar: {
|
||||
title: '圖示/貼圖',
|
||||
icon: '圖示',
|
||||
sticker: '貼圖'
|
||||
},
|
||||
formulaSidebar: {
|
||||
title: '公式',
|
||||
placeholder: '請輸入 LaTeX 語法',
|
||||
confirm: '完成',
|
||||
common: '常用公式',
|
||||
tip: '僅在豐富文字模式下支援插入公式'
|
||||
},
|
||||
richTextToolbar: {
|
||||
bold: '粗體',
|
||||
italic: '斜體',
|
||||
underline: '底線',
|
||||
strike: '刪除線',
|
||||
fontFamily: '字型',
|
||||
fontSize: '字型大小',
|
||||
color: '字型顏色',
|
||||
backgroundColor: '背景顏色',
|
||||
removeFormat: '清除樣式'
|
||||
},
|
||||
other: {
|
||||
loading: '載入中,請稍候...'
|
||||
},
|
||||
sourceCodeEdit: {
|
||||
sourceCodeTip:
|
||||
'不建議在豐富文字模式下修改樣式,因為需要同步修改資料和 HTML 結構。',
|
||||
format: '格式化',
|
||||
copy: '複製',
|
||||
confirm: '完成',
|
||||
close: '關閉',
|
||||
formatErrorTip: 'JSON 格式錯誤,請檢查後重試',
|
||||
copyTip: '已複製到剪貼簿',
|
||||
formatTip: '格式化完成'
|
||||
},
|
||||
attachment: {
|
||||
deleteAttachment: '刪除附件',
|
||||
tip: '附件功能僅在用戶端可用'
|
||||
},
|
||||
annotation: {
|
||||
mark: '標記',
|
||||
show: '顯示標記',
|
||||
type: '類型',
|
||||
color: '顏色',
|
||||
lineWidth: '線寬',
|
||||
padding: '內距',
|
||||
animate: '動畫'
|
||||
},
|
||||
nodeOuterFrame: {
|
||||
outerFrameSetting: '外框設定',
|
||||
deleteOuterFrame: '刪除外框',
|
||||
boxStyle: '邊框樣式',
|
||||
boxColor: '邊框顏色',
|
||||
fillColor: '填充顏色'
|
||||
},
|
||||
nodeTagStyle: {
|
||||
placeholder: '請輸入標籤內容',
|
||||
delete: '刪除此標籤'
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,9 @@
|
||||
<span class="name">{{ $t('contextmenu.moveDownNode') }}</span>
|
||||
<span class="desc">Ctrl + ↓</span>
|
||||
</div>
|
||||
<div class="item" @click="exec('EXPAND_ALL')">
|
||||
<span class="name">{{ $t('contextmenu.expandNodeChild') }}</span>
|
||||
</div>
|
||||
<div class="item" v-if="supportNumbers">
|
||||
<span class="name">{{ $t('contextmenu.number') }}</span>
|
||||
<span class="el-icon-arrow-right"></span>
|
||||
@@ -344,12 +347,11 @@ export default {
|
||||
|
||||
// 计算右键菜单元素的显示位置
|
||||
getShowPosition(x, y) {
|
||||
this.subItemsShowLeft = false
|
||||
const rect = this.$refs.contextmenuRef.getBoundingClientRect()
|
||||
if (x + rect.width > window.innerWidth) {
|
||||
x = x - rect.width - 20
|
||||
this.subItemsShowLeft = true
|
||||
}
|
||||
this.subItemsShowLeft = x + rect.width + 150 > window.innerWidth
|
||||
if (y + rect.height > window.innerHeight) {
|
||||
y = window.innerHeight - rect.height - 10
|
||||
}
|
||||
@@ -462,6 +464,9 @@ export default {
|
||||
this.node
|
||||
)
|
||||
break
|
||||
case 'EXPAND_ALL':
|
||||
this.$bus.$emit('execCommand', key, this.node.uid)
|
||||
break
|
||||
default:
|
||||
this.$bus.$emit('execCommand', key, ...args)
|
||||
break
|
||||
|
||||
@@ -79,6 +79,11 @@ import OuterFrame from 'simple-mind-map/src/plugins/OuterFrame.js'
|
||||
// import Notation from 'simple-mind-map-plugin-notation'
|
||||
// 编号插件,该插件为付费插件,详情请查看开发文档
|
||||
// import Numbers from 'simple-mind-map-plugin-numbers'
|
||||
// Freemind软件格式导入导出插件,该插件为付费插件,详情请查看开发文档
|
||||
// import Freemind from 'simple-mind-map-plugin-freemind'
|
||||
// Excel软件格式导入导出插件,该插件为付费插件,详情请查看开发文档
|
||||
// import Excel from 'simple-mind-map-plugin-excel'
|
||||
// npm link simple-mind-map-plugin-excel simple-mind-map-plugin-freemind simple-mind-map-plugin-numbers simple-mind-map-plugin-notation simple-mind-map-plugin-handdrawnlikestyle simple-mind-map
|
||||
import OutlineSidebar from './OutlineSidebar'
|
||||
import Style from './Style'
|
||||
import BaseStyle from './BaseStyle'
|
||||
@@ -418,6 +423,25 @@ export default {
|
||||
},
|
||||
expandBtnNumHandler: num => {
|
||||
return num >= 100 ? '…' : num
|
||||
},
|
||||
beforeDeleteNodeImg: node => {
|
||||
return new Promise(resolve => {
|
||||
this.$confirm(
|
||||
this.$t('edit.deleteNodeImgTip'),
|
||||
this.$t('edit.tip'),
|
||||
{
|
||||
confirmButtonText: this.$t('edit.yes'),
|
||||
cancelButtonText: this.$t('edit.no'),
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
resolve(false)
|
||||
})
|
||||
.catch(() => {
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
}
|
||||
// createNodePrefixContent: (node) => {
|
||||
// const el = document.createElement('div')
|
||||
@@ -544,6 +568,16 @@ export default {
|
||||
this.mindMap.addPlugin(Numbers)
|
||||
this.$store.commit('setSupportNumbers', true)
|
||||
}
|
||||
if (typeof Freemind !== 'undefined') {
|
||||
this.mindMap.addPlugin(Freemind)
|
||||
this.$store.commit('setSupportFreemind', true)
|
||||
Vue.prototype.Freemind = Freemind
|
||||
}
|
||||
if (typeof Excel !== 'undefined') {
|
||||
this.mindMap.addPlugin(Excel)
|
||||
this.$store.commit('setSupportExcel', true)
|
||||
Vue.prototype.Excel = Excel
|
||||
}
|
||||
this.mindMap.keyCommand.addShortcut('Control+s', () => {
|
||||
this.manualSave()
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
element-loading-spinner="el-icon-loading"
|
||||
element-loading-background="rgba(0, 0, 0, 0.8)"
|
||||
:width="isMobile ? '90%' : '50%'"
|
||||
:top="isMobile? '20px' : '15vh'"
|
||||
:top="isMobile ? '20px' : '15vh'"
|
||||
>
|
||||
<div class="exportContainer" :class="{ isDark: isDark }">
|
||||
<div class="nameInputBox">
|
||||
@@ -98,12 +98,14 @@
|
||||
import { mapState, mapMutations } from 'vuex'
|
||||
import { downTypeList } from '@/config'
|
||||
import { isMobile } from 'simple-mind-map/src/utils/index'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-06-24 22:53:54
|
||||
* @Desc: 导出
|
||||
*/
|
||||
let md = null
|
||||
export default {
|
||||
name: 'Export',
|
||||
data() {
|
||||
@@ -124,11 +126,23 @@ export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
openNodeRichText: state => state.localConfig.openNodeRichText,
|
||||
isDark: state => state.localConfig.isDark
|
||||
isDark: state => state.localConfig.isDark,
|
||||
supportFreemind: state => state.supportFreemind,
|
||||
supportExcel: state => state.supportExcel
|
||||
}),
|
||||
|
||||
downTypeList() {
|
||||
return downTypeList[this.$i18n.locale] || downTypeList.zh
|
||||
const list = downTypeList[this.$i18n.locale] || downTypeList.zh
|
||||
return list.filter(item => {
|
||||
if (item.type === 'mm') {
|
||||
return this.supportFreemind
|
||||
}
|
||||
if (item.type === 'xlsx') {
|
||||
return this.supportExcel
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -203,6 +217,22 @@ export default {
|
||||
this.fileName,
|
||||
this.isTransparent
|
||||
)
|
||||
} else if (this.exportType === 'mm') {
|
||||
this.$bus.$emit('export', this.exportType, true, this.fileName, {
|
||||
transformNote: note => {
|
||||
if (!md) {
|
||||
md = new MarkdownIt()
|
||||
}
|
||||
return md.render(note)
|
||||
},
|
||||
transformImage: img => {
|
||||
if (/^https?:\/\//.test(img)) {
|
||||
return img
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$bus.$emit('export', this.exportType, true, this.fileName)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="x"
|
||||
accept=".smm,.json,.xmind,.xlsx,.md"
|
||||
:accept="supportFileStr"
|
||||
:file-list="fileList"
|
||||
:auto-upload="false"
|
||||
:multiple="false"
|
||||
@@ -22,7 +22,7 @@
|
||||
$t('import.selectFile')
|
||||
}}</el-button>
|
||||
<div slot="tip" class="el-upload__tip">
|
||||
{{ $t('import.supportFile') }}
|
||||
{{ $t('import.support') }}{{ supportFileStr }}{{ $t('import.file') }}
|
||||
</div>
|
||||
</el-upload>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
@@ -40,9 +40,12 @@
|
||||
:show-close="false"
|
||||
>
|
||||
<el-radio-group v-model="selectCanvas" class="canvasList">
|
||||
<el-radio v-for="(item, index) in canvasList" :key="index" :label="index">{{
|
||||
item.title
|
||||
}}</el-radio>
|
||||
<el-radio
|
||||
v-for="(item, index) in canvasList"
|
||||
:key="index"
|
||||
:label="index"
|
||||
>{{ item.title }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="confirmSelect">{{
|
||||
@@ -56,9 +59,8 @@
|
||||
<script>
|
||||
import xmind from 'simple-mind-map/src/parse/xmind.js'
|
||||
import markdown from 'simple-mind-map/src/parse/markdown.js'
|
||||
import { fileToBuffer } from '@/utils'
|
||||
import { read, utils } from 'xlsx'
|
||||
import { mapMutations } from 'vuex'
|
||||
import { mapMutations, mapState } from 'vuex'
|
||||
import Vue from 'vue'
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
@@ -77,6 +79,22 @@ export default {
|
||||
canvasList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
supportFreemind: state => state.supportFreemind,
|
||||
supportExcel: state => state.supportExcel
|
||||
}),
|
||||
supportFileStr() {
|
||||
let res = '.smm,.json,.xmind,.md'
|
||||
if (this.supportFreemind) {
|
||||
res += ',.mm'
|
||||
}
|
||||
if (this.supportExcel) {
|
||||
res += ',.xlsx'
|
||||
}
|
||||
return res
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dialogVisible(val, oldVal) {
|
||||
if (!val && oldVal) {
|
||||
@@ -101,12 +119,20 @@ export default {
|
||||
this.dialogVisible = true
|
||||
},
|
||||
|
||||
getRegexp() {
|
||||
return new RegExp(
|
||||
`\.(smm|json|xmind|md${this.supportFreemind ? '|mm' : ''}${
|
||||
this.supportExcel ? '|xlsx' : ''
|
||||
})$`
|
||||
)
|
||||
},
|
||||
|
||||
// 检查url中是否操作需要打开的文件
|
||||
async handleFileURL() {
|
||||
try {
|
||||
const fileURL = this.$route.query.fileURL
|
||||
if (!fileURL) return
|
||||
const macth = /\.(smm|json|xmind|md|xlsx)$/.exec(fileURL)
|
||||
const macth = this.getRegexp().exec(fileURL)
|
||||
if (!macth) {
|
||||
return
|
||||
}
|
||||
@@ -124,6 +150,8 @@ export default {
|
||||
this.handleExcel(data)
|
||||
} else if (type === 'md') {
|
||||
this.handleMd(data)
|
||||
} else if (type === 'mm') {
|
||||
this.handleMm(data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@@ -132,9 +160,12 @@ export default {
|
||||
|
||||
// 文件选择
|
||||
onChange(file) {
|
||||
let reg = /\.(smm|xmind|json|xlsx|md)$/
|
||||
if (!reg.test(file.name)) {
|
||||
this.$message.error(this.$t('import.enableFileTip'))
|
||||
if (!this.getRegexp().test(file.name)) {
|
||||
this.$message.error(
|
||||
this.$t('import.pleaseSelect') +
|
||||
this.supportFileStr +
|
||||
this.$t('import.file')
|
||||
)
|
||||
this.fileList = []
|
||||
} else {
|
||||
this.fileList.push(file)
|
||||
@@ -171,6 +202,8 @@ export default {
|
||||
this.handleExcel(file)
|
||||
} else if (/\.md$/.test(file.name)) {
|
||||
this.handleMd(file)
|
||||
} else if (/\.mm$/.test(file.name)) {
|
||||
this.handleMm(file)
|
||||
}
|
||||
this.cancel()
|
||||
this.setActiveSidebar(null)
|
||||
@@ -212,6 +245,36 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 处理Freemind格式
|
||||
handleMm(file) {
|
||||
const fileReader = new FileReader()
|
||||
fileReader.readAsText(file.raw)
|
||||
fileReader.onload = async evt => {
|
||||
try {
|
||||
const data = await Vue.prototype.Freemind.freemindToSmm(
|
||||
evt.target.result,
|
||||
{
|
||||
// withStyle: true,
|
||||
transformImg: image => {
|
||||
return new Promise(resolve => {
|
||||
if (/^https?:\/\//.test(image)) {
|
||||
resolve({ url: image })
|
||||
} else {
|
||||
resolve(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
this.$bus.$emit('setData', data)
|
||||
this.$message.success(this.$t('import.importSuccess'))
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
this.$message.error(this.$t('import.fileParsingFailed'))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 显示xmind文件的多个画布选择弹窗
|
||||
showSelectXmindCanvasDialog(content) {
|
||||
this.canvasList = content
|
||||
@@ -230,59 +293,8 @@ export default {
|
||||
// 处理.xlsx文件
|
||||
async handleExcel(file) {
|
||||
try {
|
||||
const wb = read(await fileToBuffer(file.raw))
|
||||
const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {
|
||||
header: 1
|
||||
})
|
||||
if (data.length <= 0) {
|
||||
return
|
||||
}
|
||||
let max = 0
|
||||
data.forEach(arr => {
|
||||
if (arr.length > max) {
|
||||
max = arr.length
|
||||
}
|
||||
})
|
||||
let layers = []
|
||||
let walk = layer => {
|
||||
if (!layers[layer]) {
|
||||
layers[layer] = []
|
||||
}
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i][layer]) {
|
||||
let node = {
|
||||
data: {
|
||||
text: data[i][layer]
|
||||
},
|
||||
children: [],
|
||||
_row: i
|
||||
}
|
||||
layers[layer].push(node)
|
||||
}
|
||||
}
|
||||
if (layer < max - 1) {
|
||||
walk(layer + 1)
|
||||
}
|
||||
}
|
||||
walk(0)
|
||||
let getParent = (arr, row) => {
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
if (row >= arr[i]._row) {
|
||||
return arr[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 1; i < layers.length; i++) {
|
||||
let arr = layers[i]
|
||||
for (let j = 0; j < arr.length; j++) {
|
||||
let item = arr[j]
|
||||
let parent = getParent(layers[i - 1], item._row)
|
||||
if (parent) {
|
||||
parent.children.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$bus.$emit('setData', layers[0][0])
|
||||
const res = await Vue.prototype.Excel.excelTo(file.raw)
|
||||
this.$bus.$emit('setData', res)
|
||||
this.$message.success(this.$t('import.importSuccess'))
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@@ -296,7 +308,7 @@ export default {
|
||||
fileReader.readAsText(file.raw)
|
||||
fileReader.onload = async evt => {
|
||||
try {
|
||||
let data = await markdown.transformMarkdownTo(evt.target.result)
|
||||
let data = markdown.transformMarkdownTo(evt.target.result)
|
||||
this.$bus.$emit('setData', data)
|
||||
this.$message.success(this.$t('import.importSuccess'))
|
||||
} catch (error) {
|
||||
|
||||
@@ -124,8 +124,8 @@ export default {
|
||||
node.setImage({
|
||||
url: img || 'none',
|
||||
title: this.imgTitle,
|
||||
width: res.width,
|
||||
height: res.height
|
||||
width: res.width || 100,
|
||||
height: res.height || 100
|
||||
})
|
||||
})
|
||||
this.cancel()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
</el-tooltip>
|
||||
<div class="scaleInfo">
|
||||
<input
|
||||
ref="inputRef"
|
||||
type="text"
|
||||
v-model="scaleNum"
|
||||
@input="onScaleNumInput"
|
||||
@@ -55,13 +56,16 @@ export default {
|
||||
watch: {
|
||||
mindMap(val, oldVal) {
|
||||
if (val && !oldVal) {
|
||||
this.mindMap.on('scale', scale => {
|
||||
this.scaleNum = this.toPer(scale)
|
||||
})
|
||||
this.mindMap.on('scale', this.onScale)
|
||||
this.mindMap.on('draw_click', this.onDrawClick)
|
||||
this.scaleNum = this.toPer(this.mindMap.view.scale)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.mindMap.off('scale', this.onScale)
|
||||
this.mindMap.off('draw_click', this.onDrawClick)
|
||||
},
|
||||
methods: {
|
||||
// 转换成百分数
|
||||
toPer(scale) {
|
||||
@@ -98,6 +102,14 @@ export default {
|
||||
const cy = this.mindMap.height / 2
|
||||
this.mindMap.view.setScale(this.scaleNum / 100, cx, cy)
|
||||
}
|
||||
},
|
||||
|
||||
onScale(scale) {
|
||||
this.scaleNum = this.toPer(scale)
|
||||
},
|
||||
|
||||
onDrawClick() {
|
||||
if (this.$refs.inputRef) this.$refs.inputRef.blur()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,16 +247,16 @@
|
||||
<el-popover ref="popover4" placement="bottom" trigger="hover">
|
||||
<Color :color="style.fillColor" @change="changeFillColor"></Color>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('style.gradientStyle') }}</span>
|
||||
<span class="name" style="margin-left: 20px;">{{
|
||||
$t('style.gradientStyle')
|
||||
}}</span>
|
||||
<el-checkbox
|
||||
v-model="style.gradientStyle"
|
||||
@change="update('gradientStyle')"
|
||||
></el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-if="style.gradientStyle">
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('style.startColor') }}</span>
|
||||
<span
|
||||
@@ -282,6 +282,24 @@
|
||||
<Color :color="style.endColor" @change="changeEndColor"></Color>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="rowItem">
|
||||
<span class="name">{{ $t('style.direction') }}</span>
|
||||
<el-select
|
||||
size="mini"
|
||||
style="width: 80px"
|
||||
v-model="style.linearGradientDir"
|
||||
placeholder=""
|
||||
@change="update('linearGradientDir')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in linearGradientDirList"
|
||||
:key="item.value"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 形状 -->
|
||||
<div class="title">{{ $t('style.shape') }}</div>
|
||||
@@ -458,7 +476,8 @@ import {
|
||||
borderRadiusList,
|
||||
lineHeightList,
|
||||
shapeList,
|
||||
shapeListMap
|
||||
shapeListMap,
|
||||
linearGradientDirList
|
||||
} from '@/config'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
@@ -502,7 +521,8 @@ export default {
|
||||
lineMarkerDir: '',
|
||||
gradientStyle: false,
|
||||
startColor: '',
|
||||
endColor: ''
|
||||
endColor: '',
|
||||
linearGradientDir: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -522,6 +542,11 @@ export default {
|
||||
},
|
||||
shapeListMap() {
|
||||
return shapeListMap[this.$i18n.locale] || shapeListMap.zh
|
||||
},
|
||||
linearGradientDirList() {
|
||||
return (
|
||||
linearGradientDirList[this.$i18n.locale] || linearGradientDirList.zh
|
||||
)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -587,6 +612,24 @@ export default {
|
||||
].forEach(item => {
|
||||
this.style[item] = this.activeNodes[0].getStyle(item, false)
|
||||
})
|
||||
this.initLinearGradientDir()
|
||||
},
|
||||
|
||||
// 初始化渐变方向样式
|
||||
initLinearGradientDir() {
|
||||
const startDir = this.activeNodes[0].getStyle('startDir', false)
|
||||
const endDir = this.activeNodes[0].getStyle('endDir', false)
|
||||
const target = this.linearGradientDirList.find(item => {
|
||||
return (
|
||||
item.start[0] === startDir[0] &&
|
||||
item.start[1] === startDir[1] &&
|
||||
item.end[0] === endDir[0] &&
|
||||
item.end[1] === endDir[1]
|
||||
)
|
||||
})
|
||||
if (target) {
|
||||
this.style.linearGradientDir = target.value
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -595,9 +638,21 @@ export default {
|
||||
* @Desc: 修改样式
|
||||
*/
|
||||
update(prop) {
|
||||
this.activeNodes.forEach(node => {
|
||||
node.setStyle(prop, this.style[prop])
|
||||
})
|
||||
if (prop === 'linearGradientDir') {
|
||||
const target = this.linearGradientDirList.find(item => {
|
||||
return item.value === this.style.linearGradientDir
|
||||
})
|
||||
this.activeNodes.forEach(node => {
|
||||
node.setStyles({
|
||||
startDir: [...target.start],
|
||||
endDir: [...target.end]
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.activeNodes.forEach(node => {
|
||||
node.setStyle(prop, this.style[prop])
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,8 @@ const store = new Vuex.Store({
|
||||
supportHandDrawnLikeStyle: false, // 是否支持设置手绘风格
|
||||
supportMark: false, // 是否支持标记
|
||||
supportNumbers: false, // 是否支持编号
|
||||
supportFreemind: false, // 是否支持Freemind插件
|
||||
supportExcel: false, // 是否支持Excel插件
|
||||
isDragOutlineTreeNode: false // 当前是否正在拖拽大纲树的节点
|
||||
},
|
||||
mutations: {
|
||||
@@ -93,6 +95,16 @@ const store = new Vuex.Store({
|
||||
state.supportNumbers = data
|
||||
},
|
||||
|
||||
// 设置是否支持Freemind插件
|
||||
setSupportFreemind(state, data) {
|
||||
state.supportFreemind = data
|
||||
},
|
||||
|
||||
// 设置是否支持Excel插件
|
||||
setSupportExcel(state, data) {
|
||||
state.supportExcel = data
|
||||
},
|
||||
|
||||
// 设置树节点拖拽
|
||||
setIsDragOutlineTreeNode(state, data) {
|
||||
state.isDragOutlineTreeNode = data
|
||||
|
||||
Reference in New Issue
Block a user