Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d0310f675 | ||
|
|
4ef99958b6 | ||
|
|
ec677c781e | ||
|
|
2822dcc99a | ||
|
|
46e3d85b5f | ||
|
|
024271ec54 | ||
|
|
da9fd4c36d | ||
|
|
5a291b4a5f | ||
|
|
3f002ce2ee | ||
|
|
78a242faff | ||
|
|
be229a0c04 | ||
|
|
9a36cd4478 | ||
|
|
732b6b50b0 | ||
|
|
ade7a95f3c | ||
|
|
322975528e | ||
|
|
2a76f5a0bc | ||
|
|
2a7eaefac5 | ||
|
|
68784f3e4d | ||
|
|
c29477ed55 | ||
|
|
88a6442539 | ||
|
|
24363d55a4 | ||
|
|
7fd4c7504d | ||
|
|
efb4dcf236 | ||
|
|
53c2af0bc0 | ||
|
|
74d302639a | ||
|
|
0a2e4e7c14 | ||
|
|
7d399b436b | ||
|
|
f02098f697 | ||
|
|
fbb3b47b7d | ||
|
|
508d8fe357 | ||
|
|
7258ed9ea7 | ||
|
|
6ae5d244f1 | ||
|
|
7213348c12 | ||
|
|
93092db49f | ||
|
|
b8df51eb02 | ||
|
|
6cdc2ff526 | ||
|
|
b1303ce7a5 | ||
|
|
ac5bb1d684 | ||
|
|
ad8cf74bba | ||
|
|
4e6688e4e0 | ||
|
|
15859e76b6 | ||
|
|
eb89bd71e1 | ||
|
|
d8a88f94d7 | ||
|
|
fa7761b5a3 | ||
|
|
e769c9602b | ||
|
|
2078d38092 | ||
|
|
97834086d0 | ||
|
|
8fe0a53ba1 | ||
|
|
20fb9c3067 | ||
|
|
1573141f2c | ||
|
|
dba711c9ef | ||
|
|
be3faa0aef | ||
|
|
d93511eca4 | ||
|
|
1d75e8519d | ||
|
|
02460c0642 | ||
|
|
a43ad7aa06 | ||
|
|
0041be9892 | ||
|
|
7ebab0298b | ||
|
|
079b963ae3 | ||
|
|
fb6fcd6bd3 | ||
|
|
59950b2ba0 | ||
|
|
1f23257917 | ||
|
|
4e1db01f44 | ||
|
|
38769c3b55 | ||
|
|
a4ef09779d | ||
|
|
4821dd6052 | ||
|
|
f33c886d6a | ||
|
|
b5b8c2be60 | ||
|
|
4a7485c58e | ||
|
|
c097d20748 | ||
|
|
eeeae7d0e2 | ||
|
|
dd3e169946 | ||
|
|
b895a58194 | ||
|
|
2b42b9fafa | ||
|
|
c2125b07ca | ||
|
|
eb342bf69b | ||
|
|
a7eb66a6c9 | ||
|
|
e24fd9bdbb | ||
|
|
34d7c6fed2 | ||
|
|
a0f88031c1 | ||
|
|
889ec13dbf | ||
|
|
4aa5a8c48b | ||
|
|
c6a8ec257c | ||
|
|
0ec20b8fa0 | ||
|
|
f3285cf4e6 | ||
|
|
d7786cd449 | ||
|
|
777a9d9047 | ||
|
|
0dd7b0ed03 | ||
|
|
b661e8cc92 | ||
|
|
1412fb5d09 | ||
|
|
ec6a40e381 | ||
|
|
ca5075d50c | ||
|
|
fa1bf89e70 | ||
|
|
729533b3c1 | ||
|
|
6ffd26fd7f | ||
|
|
48da6cb642 | ||
|
|
0055bbb39d | ||
|
|
3349df2183 | ||
|
|
6c4800a6f0 | ||
|
|
ca40204d43 | ||
|
|
cd28be4b01 | ||
|
|
af752ea761 | ||
|
|
f2b72830b4 | ||
|
|
f5cf7abd4f | ||
|
|
29695d1d7e | ||
|
|
1510f7a135 | ||
|
|
4b5a691713 | ||
|
|
e9058ed67e | ||
|
|
fc728ec018 | ||
|
|
7361df8697 | ||
|
|
14e36aa699 | ||
|
|
eff4cd0e77 | ||
|
|
82c2d848a9 | ||
|
|
0344599411 | ||
|
|
19fa0af6c0 | ||
|
|
bca1a073f7 | ||
|
|
98fb23bf7c | ||
|
|
1c9c399b76 | ||
|
|
bc43fedd87 | ||
|
|
156054ed93 | ||
|
|
937f7d2969 | ||
|
|
e56a6d36cb | ||
|
|
9f19061010 | ||
|
|
0d465f28f3 | ||
|
|
b4fdcd81b0 | ||
|
|
38c0fe2e39 | ||
|
|
29ddbba9b9 | ||
|
|
9f9ed1e84f | ||
|
|
9ebc416167 | ||
|
|
5d49d985c0 | ||
|
|
c36338a794 | ||
|
|
c21ee4960e | ||
|
|
5090f21b0e | ||
|
|
3d9d172fa0 |
240
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(节点编号插件)[收费]、Freemind(Freemind格式导入导出插件)[收费]、Excel(Excel格式导入导出插件)[收费]
|
||||
> RichText(节点富文本插件)、Select(鼠标多选节点插件)、Drag(节点拖拽插件)、AssociativeLine(关联线插件)、Export(导出插件)、KeyboardNavigation(键盘导航插件)、MiniMap(小地图插件)、Watermark(水印插件)、TouchEvent(移动端触摸事件支持插件)、NodeImgAdjust(拖拽调整节点图片大小插件)、Search(搜索插件)、Painter(节点格式刷插件)、Scrollbar(滚动条插件)、Formula(数学公式插件)、Cooperate(协同编辑插件)、RainbowLines(彩虹线条插件)、Demonstrate(演示模式插件)、OuterFrame(外框插件)、MindMapLayoutPro(思维导图布局插件)、HandDrawnLikeStyle(手绘风格插件)[收费]、Notation(节点标记插件)[收费]、Numbers(节点编号插件)[收费]、Freemind(Freemind格式导入导出插件)[收费]、Excel(Excel格式导入导出插件)[收费]、Checkbox(待办插件)[收费]、Lineflow(节点连线流动插件)[收费]
|
||||
|
||||
本项目不会实现的特性:
|
||||
|
||||
@@ -93,19 +93,15 @@ const mindMap = new MindMap({
|
||||
});
|
||||
```
|
||||
|
||||
即可得到一个思维导图。
|
||||
|
||||
想要实现更多功能?可以查看[开发文档](https://wanglin2.github.io/mind-map-docs/)。
|
||||
即可得到一个思维导图。想要实现更多功能?可以查看[开发文档](https://wanglin2.github.io/mind-map-docs/)。
|
||||
|
||||
# License
|
||||
|
||||
[MIT](./LICENSE)。保留`mind-map`版权声明的情况下可随意商用。如不想保留可联系作者。
|
||||
[MIT](./LICENSE)。保留`mind-map`版权声明的情况下可随意商用,如不想保留可联系作者。
|
||||
|
||||
# 微信交流群
|
||||
|
||||
一群已满,可以扫描如下二维码进入二群,如已过期,可以微信添加`wanglinguanfang`拉你入群。思维导图相关问题皆可在群里提问,不必私聊作者。
|
||||
|
||||
<img src="./qrcode.jpg" style="width: 300px" />
|
||||
微信添加`wanglinguanfang`拉你入群。根据过往的经验,大部分问题都可以通过查看issue列表或文档解决,所以提问前请确保你已经阅读完了所有文档,文档里没有的可在群里提问,不必私聊作者,如果你一定要私聊,请先发红包(¥9.9+每次)。
|
||||
|
||||
# star
|
||||
|
||||
@@ -119,15 +115,119 @@ const mindMap = new MindMap({
|
||||
|
||||
# 请作者喝杯咖啡
|
||||
|
||||
开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡,你的支持是开发者持续维护的最大动力~
|
||||
开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡~你的赞助对项目的可持续发展非常重要,是作者持续维护的最大动力。
|
||||
|
||||
> 推荐使用支付宝,微信获取不到头像。转账请备注【思维导图】。
|
||||
>
|
||||
> 也可以通过购买付费插件来支持我们:[付费插件](https://wanglin2.github.io/mind-map-docs/plugins/about.html)。
|
||||
>
|
||||
> 赞助等级:最强王者(¥500+)、星耀赞助(¥300+)、钻石赞助(¥150+)、黄金赞助(¥50+)、青铜赞助
|
||||
|
||||
<p>
|
||||
<img src="./web/src/assets/img/alipay.jpg" style="width: 300px" />
|
||||
<img src="./web/src/assets/img/wechat.jpg" style="width: 300px" />
|
||||
</p>
|
||||
|
||||
## 钻石赞助
|
||||
|
||||
<p>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/黄智彪@一米一栗科技.png" style="width: 50px;height: 50px;" />
|
||||
<span>黄智彪@一米一栗科技</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
## 黄金赞助
|
||||
|
||||
<p>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;" />
|
||||
<span>小土渣的宇宙</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Chris.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Chris</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/仓鼠.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>仓鼠</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/风格.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>风格</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>LiuJL</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Kyle.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Kyle</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/ccccs.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>ccccs</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/梁辉.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>梁辉</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/千帆.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>千帆</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/布林.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>布林</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>达仁科技</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/沐风牧草.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>沐风牧草</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/俊奇.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>俊奇</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/庆国.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>庆国</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>Matt</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/雨馨.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>雨馨</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/峰.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>峰</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
## 青铜赞助
|
||||
|
||||
<p>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Think.jpg" style="width: 50px;height: 50px;" />
|
||||
@@ -137,10 +237,6 @@ 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/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;" />
|
||||
<span>小土渣的宇宙</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/qp.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>qp</span>
|
||||
@@ -157,22 +253,10 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/suka.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>suka</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Chris.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Chris</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/水车.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>水车</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/仓鼠.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>仓鼠</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/千帆.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>千帆</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/才镇.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>才镇</span>
|
||||
@@ -189,10 +273,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>Luke</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/布林.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>布林</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/南风.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>南风</span>
|
||||
@@ -209,10 +289,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/敏.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>敏</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/沐风牧草.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>沐风牧草</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/有希.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>有希</span>
|
||||
@@ -221,10 +297,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/樊笼.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>樊笼</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/达仁科技.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>达仁科技</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/小逗比.png" style="width: 50px;height: 50px;" />
|
||||
<span>小逗比</span>
|
||||
@@ -301,10 +373,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/旭东.png" style="width: 50px;height: 50px;" />
|
||||
<span>旭东</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/俊奇.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>俊奇</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/橘半.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>橘半</span>
|
||||
@@ -317,10 +385,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/皇登攀.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>皇登攀</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/风格.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>风格</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>SR</span>
|
||||
@@ -329,10 +393,6 @@ 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/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>LiuJL</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/L.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>L</span>
|
||||
@@ -357,10 +417,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>铁</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/庆国.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>庆国</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Alex.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Alex</span>
|
||||
@@ -377,18 +433,10 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/最多5个字.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>最多5个字</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/ZX.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>ZX</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>
|
||||
@@ -401,18 +449,10 @@ 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/Kyle.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Kyle</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/lsytyrt.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>lsytyrt</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/buddy.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>buddy</span>
|
||||
@@ -433,14 +473,6 @@ 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/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>黄泳</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/ccccs.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>ccccs</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/。.png" style="width: 50px;height: 50px;" />
|
||||
<span>。</span>
|
||||
@@ -453,10 +485,6 @@ const mindMap = new MindMap({
|
||||
<img src="./web/src/assets/avatar/张文建.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>张文建</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/炫.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>炫</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Lawliet.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Lawliet</span>
|
||||
@@ -467,6 +495,54 @@ const mindMap = new MindMap({
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/default.png" style="width: 50px;height: 50px;" />
|
||||
<span>晏江</span>
|
||||
<span>Eric</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Joe.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Joe</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/海云.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>海云</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/皮老板.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>皮老板</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/h.r.w.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>h.r.w</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/时光匆匆.png" style="width: 50px;height: 50px;" />
|
||||
<span>时光匆匆</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/广兴.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>广兴</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/一亩三.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>一亩三</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/xbkkjbs0246658.png" style="width: 50px;height: 50px;" />
|
||||
<span>xbkkjbs0246658</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/4399行星元帅.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>4399行星元帅</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Xavier.png" style="width: 50px;height: 50px;" />
|
||||
<span>Xavier</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/冒号括号.png" style="width: 50px;height: 50px;" />
|
||||
<span>:)</span>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
BIN
dist/img/classic10.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/img/classic11.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/img/classic12.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/img/classic13.png
vendored
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
dist/img/classic14.png
vendored
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
dist/img/classic15.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/img/classic8.png
vendored
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
dist/img/classic9.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/img/dark5.png
vendored
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
dist/img/dark6.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/img/dark7.png
vendored
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
2
dist/js/app.js
vendored
69
dist/js/chunk-714c462c.js
vendored
69
dist/js/chunk-9a7bee60.js
vendored
Normal file
14
dist/js/chunk-vendors.js
vendored
@@ -9,7 +9,7 @@
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}</script><link href="dist/css/chunk-vendors.css?b396be756fa41f2b3cf9" rel="stylesheet"><link href="dist/css/app.css?b396be756fa41f2b3cf9" 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?f5839763ea0d5c47d80a" rel="stylesheet"><link href="dist/css/app.css?f5839763ea0d5c47d80a" 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?b396be756fa41f2b3cf9"></script><script src="dist/js/app.js?b396be756fa41f2b3cf9"></script></body></html>
|
||||
}</script><script src="dist/js/chunk-vendors.js?f5839763ea0d5c47d80a"></script><script src="dist/js/app.js?f5839763ea0d5c47d80a"></script></body></html>
|
||||
BIN
qrcode.jpg
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
21
simple-mind-map/bin/createPluginsTypeFiles.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const { exec } = require('child_process')
|
||||
const fs = require('fs')
|
||||
|
||||
const base = './src/plugins/'
|
||||
const list = fs.readdirSync(base)
|
||||
const files = []
|
||||
list.forEach(item => {
|
||||
const stat = fs.statSync(base + item)
|
||||
if (stat.isFile()) {
|
||||
files.push(item)
|
||||
}
|
||||
})
|
||||
const str = files
|
||||
.map(item => {
|
||||
return base + item
|
||||
})
|
||||
.join(' ')
|
||||
|
||||
exec(
|
||||
`tsc ${str} --declaration --allowJs --emitDeclarationOnly --outDir types/src/ --target es2017 --skipLibCheck `
|
||||
)
|
||||
@@ -18,20 +18,19 @@ import Formula from './src/plugins/Formula.js'
|
||||
import RainbowLines from './src/plugins/RainbowLines.js'
|
||||
import Demonstrate from './src/plugins/Demonstrate.js'
|
||||
import OuterFrame from './src/plugins/OuterFrame.js'
|
||||
import MindMapLayoutPro from './src/plugins/MindMapLayoutPro.js'
|
||||
import xmind from './src/parse/xmind.js'
|
||||
import markdown from './src/parse/markdown.js'
|
||||
import icons from './src/svg/icons.js'
|
||||
import * as constants from './src/constants/constant.js'
|
||||
import themes from './src/themes/index.js'
|
||||
import * as defaultTheme from './src/themes/default.js'
|
||||
import * as defaultTheme from './src/theme/default.js'
|
||||
|
||||
MindMap.xmind = xmind
|
||||
MindMap.markdown = markdown
|
||||
MindMap.iconList = icons.nodeIconList
|
||||
MindMap.constants = constants
|
||||
MindMap.themes = themes
|
||||
MindMap.defaultTheme = defaultTheme
|
||||
MindMap.version = '0.11.1'
|
||||
MindMap.version = '0.12.2'
|
||||
|
||||
MindMap.usePlugin(MiniMap)
|
||||
.usePlugin(Watermark)
|
||||
@@ -52,5 +51,6 @@ MindMap.usePlugin(MiniMap)
|
||||
.usePlugin(RainbowLines)
|
||||
.usePlugin(Demonstrate)
|
||||
.usePlugin(OuterFrame)
|
||||
.usePlugin(MindMapLayoutPro)
|
||||
|
||||
export default MindMap
|
||||
|
||||
@@ -2,7 +2,7 @@ import View from './src/core/view/View'
|
||||
import Event from './src/core/event/Event'
|
||||
import Render from './src/core/render/Render'
|
||||
import merge from 'deepmerge'
|
||||
import theme from './src/themes'
|
||||
import theme from './src/theme'
|
||||
import Style from './src/core/render/node/Style'
|
||||
import KeyCommand from './src/core/command/KeyCommand'
|
||||
import Command from './src/core/command/Command'
|
||||
@@ -19,11 +19,12 @@ import {
|
||||
getObjectChangedProps,
|
||||
isUndef,
|
||||
handleGetSvgDataExtraContent,
|
||||
getNodeTreeBoundingRect
|
||||
getNodeTreeBoundingRect,
|
||||
mergeTheme
|
||||
} from './src/utils'
|
||||
import defaultTheme, {
|
||||
checkIsNodeSizeIndependenceConfig
|
||||
} from './src/themes/default'
|
||||
} from './src/theme/default'
|
||||
import { defaultOpt } from './src/constants/defaultOptions'
|
||||
|
||||
// 思维导图
|
||||
@@ -34,6 +35,7 @@ class MindMap {
|
||||
* @param {defaultOpt} opt
|
||||
*/
|
||||
constructor(opt = {}) {
|
||||
MindMap.instanceCount++
|
||||
// 合并选项
|
||||
this.opt = this.handleOpt(merge(defaultOpt, opt))
|
||||
// 预处理节点数据
|
||||
@@ -50,9 +52,29 @@ class MindMap {
|
||||
this.initWidth = this.width
|
||||
this.initHeight = this.height
|
||||
|
||||
// 添加css
|
||||
// 必要的css样式
|
||||
this.cssEl = null
|
||||
this.addCss()
|
||||
this.cssTextMap = {} // 该样式在实例化时会动态添加到页面,同时导出为svg时也会添加到svg源码中
|
||||
|
||||
// 节点前置内容列表
|
||||
/*
|
||||
{
|
||||
name: '',// 一个唯一的类型标识
|
||||
// 创建节点的显示内容:节点元素、宽高
|
||||
createContent: (node) => {
|
||||
return {
|
||||
node: null,
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
},
|
||||
// 创建保存到节点实例的opt对象中的数据
|
||||
createNodeData: () => {},
|
||||
// 更新节点实例的opt数据,返回数据是否改变了
|
||||
updateNodeData: () => {},
|
||||
}
|
||||
*/
|
||||
this.nodeInnerPrefixList = []
|
||||
|
||||
// 画布
|
||||
this.initContainer()
|
||||
@@ -96,11 +118,16 @@ class MindMap {
|
||||
this.initPlugin(plugin)
|
||||
})
|
||||
|
||||
// 添加必要的css样式
|
||||
this.addCss()
|
||||
|
||||
// 初始渲染
|
||||
this.render(this.opt.fit ? () => this.view.fit() : () => {})
|
||||
setTimeout(() => {
|
||||
if (this.opt.data) this.command.addHistory()
|
||||
}, 0)
|
||||
|
||||
// 将初始数据添加到历史记录堆栈中
|
||||
if (this.opt.addHistoryOnInit && this.opt.data) {
|
||||
this.command.addHistory()
|
||||
}
|
||||
}
|
||||
|
||||
// 配置参数处理
|
||||
@@ -168,17 +195,46 @@ class MindMap {
|
||||
this.otherDraw.clear()
|
||||
}
|
||||
|
||||
// 追加必要的css样式
|
||||
// 该样式在实例化时会动态添加到页面,同时导出为svg时也会添加到svg源码中
|
||||
appendCss(key, str) {
|
||||
this.cssTextMap[key] = str
|
||||
this.removeCss()
|
||||
this.addCss()
|
||||
}
|
||||
|
||||
// 移除追加的css样式
|
||||
removeAppendCss(key) {
|
||||
if (this.cssTextMap[key]) {
|
||||
delete this.cssTextMap[key]
|
||||
this.removeCss()
|
||||
this.addCss()
|
||||
}
|
||||
}
|
||||
|
||||
// 拼接必要的css样式
|
||||
joinCss() {
|
||||
return (
|
||||
cssContent +
|
||||
Object.keys(this.cssTextMap)
|
||||
.map(key => {
|
||||
return this.cssTextMap[key]
|
||||
})
|
||||
.join('\n')
|
||||
)
|
||||
}
|
||||
|
||||
// 添加必要的css样式到页面
|
||||
addCss() {
|
||||
this.cssEl = document.createElement('style')
|
||||
this.cssEl.type = 'text/css'
|
||||
this.cssEl.innerHTML = cssContent
|
||||
this.cssEl.innerHTML = this.joinCss()
|
||||
document.head.appendChild(this.cssEl)
|
||||
}
|
||||
|
||||
// 移除css
|
||||
removeCss() {
|
||||
document.head.removeChild(this.cssEl)
|
||||
if (this.cssEl) document.head.removeChild(this.cssEl)
|
||||
}
|
||||
|
||||
// 渲染,部分渲染
|
||||
@@ -252,7 +308,10 @@ class MindMap {
|
||||
// 设置主题
|
||||
initTheme() {
|
||||
// 合并主题配置
|
||||
this.themeConfig = merge(theme[this.opt.theme], this.opt.themeConfig)
|
||||
this.themeConfig = mergeTheme(
|
||||
theme[this.opt.theme] || theme.default,
|
||||
this.opt.themeConfig
|
||||
)
|
||||
// 设置背景样式
|
||||
Style.setBackgroundStyle(this.el, this.themeConfig)
|
||||
}
|
||||
@@ -279,7 +338,7 @@ class MindMap {
|
||||
this.opt.themeConfig = config
|
||||
if (!notRender) {
|
||||
// 检查改变的是否是节点大小无关的主题属性
|
||||
let res = checkIsNodeSizeIndependenceConfig(changedConfig)
|
||||
const res = checkIsNodeSizeIndependenceConfig(changedConfig)
|
||||
this.render(null, res ? '' : CONSTANTS.CHANGE_THEME)
|
||||
}
|
||||
}
|
||||
@@ -302,8 +361,11 @@ class MindMap {
|
||||
// 更新配置
|
||||
updateConfig(opt = {}) {
|
||||
this.emit('before_update_config', this.opt)
|
||||
const lastOpt = {
|
||||
...this.opt
|
||||
}
|
||||
this.opt = this.handleOpt(merge.all([defaultOpt, this.opt, opt]))
|
||||
this.emit('after_update_config', this.opt)
|
||||
this.emit('after_update_config', this.opt, lastOpt)
|
||||
}
|
||||
|
||||
// 获取当前布局结构
|
||||
@@ -333,14 +395,17 @@ class MindMap {
|
||||
|
||||
// 更新画布数据,如果新的数据是在当前画布节点数据基础上增删改查后形成的,那么可以使用该方法来更新画布数据
|
||||
updateData(data) {
|
||||
this.emit('before_update_data', data)
|
||||
this.renderer.setData(data)
|
||||
this.render()
|
||||
this.command.addHistory()
|
||||
this.emit('update_data', data)
|
||||
}
|
||||
|
||||
// 动态设置思维导图数据,纯节点数据
|
||||
setData(data) {
|
||||
data = this.handleData(data)
|
||||
this.emit('before_set_data', data)
|
||||
this.opt.data = data
|
||||
this.execCommand('CLEAR_ACTIVE_NODE')
|
||||
this.command.clearHistory()
|
||||
@@ -419,11 +484,20 @@ class MindMap {
|
||||
}
|
||||
const isReadonly = mode === CONSTANTS.MODE.READONLY
|
||||
if (isReadonly === this.opt.readonly) return
|
||||
this.opt.readonly = isReadonly
|
||||
if (this.opt.readonly) {
|
||||
if (isReadonly) {
|
||||
// 如果处于编辑态,要隐藏所有的编辑框
|
||||
if (this.renderer.textEdit.isShowTextEdit()) {
|
||||
this.renderer.textEdit.hideEditTextBox()
|
||||
this.command.originAddHistory()
|
||||
}
|
||||
// 取消当前激活的元素
|
||||
this.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
this.opt.readonly = isReadonly
|
||||
// 切换为编辑模式时,如果历史记录堆栈是空的,那么进行一次入栈操作
|
||||
if (!isReadonly && this.command.history.length <= 0) {
|
||||
this.command.originAddHistory()
|
||||
}
|
||||
this.emit('mode_change', mode)
|
||||
}
|
||||
|
||||
@@ -509,7 +583,7 @@ class MindMap {
|
||||
this.watermark.isInExport = false
|
||||
}
|
||||
// 添加必要的样式
|
||||
;[cssContent, ...cssTextList].forEach(s => {
|
||||
[this.joinCss(), ...cssTextList].forEach(s => {
|
||||
clone.add(SVG(`<style>${s}</style>`))
|
||||
})
|
||||
// 附加内容
|
||||
@@ -563,8 +637,8 @@ class MindMap {
|
||||
let index = MindMap.hasPlugin(plugin)
|
||||
if (index === -1) {
|
||||
MindMap.usePlugin(plugin, opt)
|
||||
this.initPlugin(plugin)
|
||||
}
|
||||
this.initPlugin(plugin)
|
||||
}
|
||||
|
||||
// 移除插件
|
||||
@@ -583,6 +657,7 @@ class MindMap {
|
||||
|
||||
// 实例化插件
|
||||
initPlugin(plugin) {
|
||||
if (this[plugin.instanceName]) return
|
||||
this[plugin.instanceName] = new plugin({
|
||||
mindMap: this,
|
||||
pluginOpt: plugin.pluginOpt
|
||||
@@ -616,6 +691,7 @@ class MindMap {
|
||||
this.el.innerHTML = ''
|
||||
this.el = null
|
||||
this.removeCss()
|
||||
MindMap.instanceCount--
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,13 +708,20 @@ 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)
|
||||
}
|
||||
// 移除主题
|
||||
MindMap.removeTheme = name => {
|
||||
if (theme[name]) {
|
||||
theme[name] = null
|
||||
}
|
||||
}
|
||||
|
||||
export default MindMap
|
||||
|
||||
18
simple-mind-map/package-lock.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.10.6",
|
||||
"version": "0.12.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "0.10.6",
|
||||
"version": "0.12.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "3.2.0",
|
||||
@@ -15,7 +15,7 @@
|
||||
"katex": "^0.16.8",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"quill": "^2.0.2",
|
||||
"quill": "^2.0.3",
|
||||
"tern": "^0.24.3",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^7.5.9",
|
||||
@@ -1822,9 +1822,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/quill": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.2.tgz",
|
||||
"integrity": "sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
|
||||
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
|
||||
"dependencies": {
|
||||
"eventemitter3": "^5.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -3553,9 +3553,9 @@
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
||||
},
|
||||
"quill": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.2.tgz",
|
||||
"integrity": "sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
|
||||
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
|
||||
"requires": {
|
||||
"eventemitter3": "^5.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.11.1",
|
||||
"version": "0.12.2",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
@@ -22,7 +22,7 @@
|
||||
"scripts": {
|
||||
"lint": "eslint src/",
|
||||
"format": "prettier --write .",
|
||||
"types": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir types --target es2017 --skipLibCheck",
|
||||
"types": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir types --target es2017 --skipLibCheck & node ./bin/createPluginsTypeFiles.js",
|
||||
"wsServe": "node ./bin/wsServer.mjs"
|
||||
},
|
||||
"module": "index.js",
|
||||
@@ -35,7 +35,7 @@
|
||||
"katex": "^0.16.8",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"quill": "^2.0.2",
|
||||
"quill": "^2.0.3",
|
||||
"tern": "^0.24.3",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^7.5.9",
|
||||
|
||||
@@ -1,167 +1,3 @@
|
||||
// 主题列表
|
||||
export const themeList = [
|
||||
{
|
||||
name: '默认',
|
||||
value: 'default',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '暗色2',
|
||||
value: 'dark2',
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '天清绿',
|
||||
value: 'skyGreen',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '脑图经典2',
|
||||
value: 'classic2',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '脑图经典3',
|
||||
value: 'classic3',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '经典绿',
|
||||
value: 'classicGreen',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '经典蓝',
|
||||
value: 'classicBlue',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '天空蓝',
|
||||
value: 'blueSky',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '脑残粉',
|
||||
value: 'brainImpairedPink',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '暗色',
|
||||
value: 'dark',
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '泥土黄',
|
||||
value: 'earthYellow',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '清新绿',
|
||||
value: 'freshGreen',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '清新红',
|
||||
value: 'freshRed',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '浪漫紫',
|
||||
value: 'romanticPurple',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '粉红葡萄',
|
||||
value: 'pinkGrape',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '薄荷',
|
||||
value: 'mint',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '金色vip',
|
||||
value: 'gold',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '活力橙',
|
||||
value: 'vitalityOrange',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '绿叶',
|
||||
value: 'greenLeaf',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '脑图经典',
|
||||
value: 'classic',
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '脑图经典4',
|
||||
value: 'classic4',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '小黄人',
|
||||
value: 'minions',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '简约黑',
|
||||
value: 'simpleBlack',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '课程绿',
|
||||
value: 'courseGreen',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '咖啡',
|
||||
value: 'coffee',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '红色精神',
|
||||
value: 'redSpirit',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '黑色幽默',
|
||||
value: 'blackHumour',
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '深夜办公室',
|
||||
value: 'lateNightOffice',
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '黑金',
|
||||
value: 'blackGold',
|
||||
dark: true
|
||||
},
|
||||
{
|
||||
name: '牛油果',
|
||||
value: 'avocado',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '秋天',
|
||||
value: 'autumn',
|
||||
dark: false
|
||||
},
|
||||
{
|
||||
name: '橙汁',
|
||||
value: 'orangeJuice',
|
||||
dark: true
|
||||
}
|
||||
]
|
||||
|
||||
// 常量
|
||||
export const CONSTANTS = {
|
||||
CHANGE_THEME: 'changeTheme',
|
||||
@@ -239,6 +75,11 @@ export const CONSTANTS = {
|
||||
TAG_POSITION: {
|
||||
RIGHT: 'right',
|
||||
BOTTOM: 'bottom'
|
||||
},
|
||||
EDIT_NODE_CLASS: {
|
||||
SMM_NODE_EDIT_WRAP: 'smm-node-edit-wrap',
|
||||
RICH_TEXT_EDIT_WRAP: 'ql-editor',
|
||||
ASSOCIATIVE_LINE_TEXT_EDIT_WRAP: 'associative-line-text-edit-warp'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +171,10 @@ export const nodeDataNoStylePropList = [
|
||||
'number',
|
||||
'range',
|
||||
'customLeft',
|
||||
'customTop'
|
||||
'customTop',
|
||||
'customTextWidth',
|
||||
'checkbox',
|
||||
'dir'
|
||||
]
|
||||
|
||||
// 错误类型
|
||||
@@ -363,6 +207,10 @@ export const cssContent = `
|
||||
opacity: 1;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.smm-text-node-wrap {
|
||||
user-select: none;
|
||||
}
|
||||
`
|
||||
|
||||
// html自闭合标签列表
|
||||
@@ -375,3 +223,6 @@ export const selfCloseTagList = [
|
||||
'meta',
|
||||
'area'
|
||||
]
|
||||
|
||||
// 非富文本模式下的节点文本行高
|
||||
export const noneRichTextNodeLineHeight = 1.2
|
||||
|
||||
@@ -7,6 +7,8 @@ export const defaultOpt = {
|
||||
el: null,
|
||||
// 思维导图回显数据
|
||||
data: null,
|
||||
// 要恢复的视图数据,一般通过mindMap.view.getTransformData()方法获取
|
||||
viewData: null,
|
||||
// 是否只读
|
||||
readonly: false,
|
||||
// 布局
|
||||
@@ -19,6 +21,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,
|
||||
// 最多显示几个标签
|
||||
@@ -72,6 +84,9 @@ export const defaultOpt = {
|
||||
isShowExpandNum: true,
|
||||
// 是否只有当鼠标在画布内才响应快捷键事件
|
||||
enableShortcutOnlyWhenMouseInSvg: true,
|
||||
// 自定义判断是否响应快捷键事件,优先级比enableShortcutOnlyWhenMouseInSvg选项高
|
||||
// 可以传递一个函数,接收事件对象e为参数,需要返回true或false,返回true代表允许响应快捷键事件,反之不允许,库默认当事件目标为body,或为文本编辑框元素(普通文本编辑框、富文本编辑框、关联线文本编辑框)时响应快捷键,其他不响应
|
||||
customCheckEnableShortcut: null,
|
||||
// 初始根节点的位置
|
||||
initRootNodePosition: null,
|
||||
// 节点文本编辑框的z-index
|
||||
@@ -118,6 +133,8 @@ export const defaultOpt = {
|
||||
// 是否在存在一个激活节点时,当按下中文、英文、数字按键时自动进入文本编辑模式
|
||||
// 开启该特性后,需要给你的输入框绑定keydown事件,并禁止冒泡
|
||||
enableAutoEnterTextEditWhenKeydown: false,
|
||||
// 当enableAutoEnterTextEditWhenKeydown选项开启时生效,当通过按键进入文本编辑时是否自动清空原有文本
|
||||
autoEmptyTextWhenKeydownEnterEdit: false,
|
||||
// 自定义对剪贴板文本的处理。当按ctrl+v粘贴时会读取用户剪贴板中的文本和图片,默认只会判断文本是否是普通文本和simple-mind-map格式的节点数据,如果你想处理其他思维导图的数据,比如processon、zhixi等,那么可以传递一个函数,接受当前剪贴板中的文本为参数,返回处理后的数据,可以返回两种类型:
|
||||
/*
|
||||
1.返回一个纯文本,那么会直接以该文本创建一个子节点
|
||||
@@ -214,7 +231,7 @@ export const defaultOpt = {
|
||||
// 移动节点到画布中心、回到根节点等操作时是否将缩放层级复位为100%
|
||||
// 该选项实际影响的是render.moveNodeToCenter方法,moveNodeToCenter方法本身也存在第二个参数resetScale来设置是否复位,如果resetScale参数没有传递,那么使用resetScaleOnMoveNodeToCenter配置,否则使用resetScale配置
|
||||
resetScaleOnMoveNodeToCenter: false,
|
||||
// 添加附加的节点前置内容,前置内容指和文本同一行的区域中的前置内容,不包括节点图片部分
|
||||
// 添加附加的节点前置内容,前置内容指和文本同一行的区域中的前置内容,不包括节点图片部分。如果存在编号、任务勾选框内容,这里添加的前置内容会在这两者之后
|
||||
createNodePrefixContent: null,
|
||||
// 添加附加的节点后置内容,后置内容指和文本同一行的区域中的后置内容,不包括节点图片部分
|
||||
createNodePostfixContent: null,
|
||||
@@ -235,6 +252,22 @@ export const defaultOpt = {
|
||||
emptyTextMeasureHeightText: 'abc123我和你',
|
||||
// 是否在进行节点文本编辑时实时更新节点大小和节点位置,开启后当节点数量比较多时可能会造成卡顿
|
||||
openRealtimeRenderOnNodeTextEdit: false,
|
||||
// 默认会给容器元素el绑定mousedown事件,可通过该选项设置是否阻止其默认事件
|
||||
// 如果设置为true,会带来一定问题,比如你聚焦在思维导图外的其他输入框,点击画布就不会触发其失焦
|
||||
mousedownEventPreventDefault: false,
|
||||
// 在激活上粘贴用户剪贴板中的数据时,如果同时存在文本和图片,那么只粘贴文本,忽略图片
|
||||
onlyPasteTextWhenHasImgAndText: true,
|
||||
// 是否允许拖拽调整节点的宽度,实际上压缩的是节点里面文本内容的宽度,当节点文本内容宽度压缩到最小时无法继续压缩。如果节点存在图片,那么最小值以图片宽度和文本内容最小宽度的最大值为准(目前该特性仅在两种情况下可用:1.开启了富文本模式,即注册了RichText插件;2.自定义节点内容)
|
||||
enableDragModifyNodeWidth: true,
|
||||
// 当允许拖拽调整节点的宽度时,可以通过该选项设置节点文本内容允许压缩的最小宽度
|
||||
minNodeTextModifyWidth: 20,
|
||||
// 同minNodeTextModifyWidth,最大值,传-1代表不限制
|
||||
maxNodeTextModifyWidth: -1,
|
||||
// 自定义处理节点的连线方法,可以传递一个函数,函数接收三个参数:node(节点实例)、line(节点的某条连线,@svgjs库的path对象), { width, color, dasharray },dasharray(该条连线的虚线样式,为none代表实线),你可以修改line对象来达到修改节点连线样式的效果,比如增加流动效果
|
||||
customHandleLine: null,
|
||||
// 实例化完后是否立刻进行一次历史数据入栈操作
|
||||
// 即调用mindMap.command.addHistory方法
|
||||
addHistoryOnInit: true,
|
||||
|
||||
// 【Select插件】
|
||||
// 多选节点时鼠标移动到边缘时的画布移动偏移量
|
||||
@@ -318,6 +351,8 @@ export const defaultOpt = {
|
||||
// 导出png、svg、pdf时会获取画布上的svg数据进行克隆,然后通过该克隆的元素进行导出,如果你想对该克隆元素做一些处理,比如新增、替换、修改其中的一些元素,那么可以通过该参数传递一个处理函数,接收svg元素对象,处理后,需要返回原svg元素对象。
|
||||
// 需要注意的是svg对象指的是@svgdotjs/svg.js库的元素对象,所以你需要阅读该库的文档来操作该对象
|
||||
handleBeingExportSvg: null,
|
||||
// 导出图片或pdf都是通过canvas将svg绘制出来,再导出,所以如果思维导图特别大,宽高可能会超出canvas支持的上限,所以会进行缩放,这个上限可以通过该参数设置,代表canvas宽和高的最大宽度
|
||||
maxCanvasSize: 16384,
|
||||
|
||||
// 【AssociativeLine插件】
|
||||
// 关联线默认文字
|
||||
@@ -334,6 +369,8 @@ export const defaultOpt = {
|
||||
},
|
||||
// 是否允许调整关联线两个端点的位置
|
||||
enableAdjustAssociativeLinePoints: true,
|
||||
// 关联线连接即将完成时执行,如果要阻止本次连接可以返回true,函数接收一个参数:node(目标节点实例)
|
||||
beforeAssociativeLineConnection: null,
|
||||
|
||||
// 【TouchEvent插件】
|
||||
// 禁止双指缩放,你仍旧可以使用api进行缩放
|
||||
@@ -400,9 +437,6 @@ export const defaultOpt = {
|
||||
transformRichTextOnEnterEdit: null,
|
||||
// 可以传递一个函数,即将结束富文本编辑前会执行该函数,函数接收richText实例,所以你可以在此时机更新quill文档数据
|
||||
beforeHideRichTextEdit: null,
|
||||
// 设置富文本节点编辑框和节点大小一致,形成伪原地编辑的效果
|
||||
// 需要注意的是,只有当节点内只有文本、且形状是矩形才会有比较好的效果
|
||||
richTextEditFakeInPlace: false,
|
||||
|
||||
// 【OuterFrame】插件
|
||||
outerFramePaddingX: 10,
|
||||
@@ -414,5 +448,15 @@ export const defaultOpt = {
|
||||
|
||||
// 【NodeImgAdjust】插件
|
||||
// 拦截节点图片的删除,点击节点图片上的删除按钮删除图片前会调用该函数,如果函数返回true则取消删除
|
||||
beforeDeleteNodeImg: null
|
||||
beforeDeleteNodeImg: null,
|
||||
// 删除和调整两个按钮的大小
|
||||
imgResizeBtnSize: 25,
|
||||
// 最小允许缩放的尺寸,请传入>=0的数字
|
||||
minImgResizeWidth: 50,
|
||||
minImgResizeHeight: 50,
|
||||
// 最大允许缩放的尺寸依据主题的配置,即主题的imgMaxWidth和imgMaxHeight配置,如果设置为false,那么使用maxImgResizeWidth和maxImgResizeHeight选项
|
||||
maxImgResizeWidthInheritTheme: false,
|
||||
// 最大允许缩放的尺寸,maxImgResizeWidthInheritTheme选项设置为false时生效,不限制最大值可传递Infinity
|
||||
maxImgResizeWidth: Infinity,
|
||||
maxImgResizeHeight: Infinity
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class Command {
|
||||
this.activeHistoryIndex = 0
|
||||
// 注册快捷键
|
||||
this.registerShortcutKeys()
|
||||
this.originAddHistory = this.addHistory.bind(this)
|
||||
this.addHistory = throttle(
|
||||
this.addHistory,
|
||||
this.mindMap.opt.addHistoryTime,
|
||||
@@ -60,6 +61,7 @@ class Command {
|
||||
this.commands[name].forEach(fn => {
|
||||
fn(...args)
|
||||
})
|
||||
this.mindMap.emit('afterExecCommand', name, ...args)
|
||||
if (
|
||||
['BACK', 'FORWARD', 'SET_NODE_ACTIVE', 'CLEAR_ACTIVE_NODE'].includes(
|
||||
name
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { keyMap } from './keyMap'
|
||||
import { CONSTANTS } from '../../constants/constant'
|
||||
|
||||
// 快捷按键、命令处理类
|
||||
export default class KeyCommand {
|
||||
@@ -15,6 +16,18 @@ export default class KeyCommand {
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
// 扩展按键映射
|
||||
extendKeyMap(key, code) {
|
||||
keyMap[key] = code
|
||||
}
|
||||
|
||||
// 从按键映射中删除某个键
|
||||
removeKeyMap(key) {
|
||||
if (typeof keyMap[key] !== 'undefined') {
|
||||
delete keyMap[key]
|
||||
}
|
||||
}
|
||||
|
||||
// 暂停快捷键响应
|
||||
pause() {
|
||||
this.isPause = true
|
||||
@@ -73,10 +86,29 @@ export default class KeyCommand {
|
||||
window.removeEventListener('keydown', this.onKeydown)
|
||||
}
|
||||
|
||||
// 根据事件目标判断是否响应快捷键事件
|
||||
defaultEnableCheck(e) {
|
||||
const target = e.target
|
||||
return (
|
||||
target === document.body ||
|
||||
target.classList.contains(CONSTANTS.EDIT_NODE_CLASS.SMM_NODE_EDIT_WRAP) ||
|
||||
target.classList.contains(CONSTANTS.EDIT_NODE_CLASS.RICH_TEXT_EDIT_WRAP) ||
|
||||
target.classList.contains(CONSTANTS.EDIT_NODE_CLASS.ASSOCIATIVE_LINE_TEXT_EDIT_WRAP)
|
||||
)
|
||||
}
|
||||
|
||||
// 按键事件
|
||||
onKeydown(e) {
|
||||
const { enableShortcutOnlyWhenMouseInSvg, beforeShortcutRun } =
|
||||
this.mindMap.opt
|
||||
const {
|
||||
enableShortcutOnlyWhenMouseInSvg,
|
||||
beforeShortcutRun,
|
||||
customCheckEnableShortcut
|
||||
} = this.mindMap.opt
|
||||
const checkFn =
|
||||
typeof customCheckEnableShortcut === 'function'
|
||||
? customCheckEnableShortcut
|
||||
: this.defaultEnableCheck
|
||||
if (!checkFn(e)) return
|
||||
if (this.isPause || (enableShortcutOnlyWhenMouseInSvg && !this.isInSvg)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -33,10 +33,12 @@ import {
|
||||
removeRichTextStyes,
|
||||
formatGetNodeGeneralization,
|
||||
sortNodeList,
|
||||
throttle
|
||||
throttle,
|
||||
checkClipboardReadEnable,
|
||||
isNodeNotNeedRenderData
|
||||
} from '../../utils'
|
||||
import { shapeList } from './node/Shape'
|
||||
import { lineStyleProps } from '../../themes/default'
|
||||
import { lineStyleProps } from '../../theme/default'
|
||||
import { CONSTANTS, ERROR_TYPES } from '../../constants/constant'
|
||||
import { Polygon } from '@svgdotjs/svg.js'
|
||||
|
||||
@@ -92,16 +94,11 @@ class Render {
|
||||
// 文本编辑框,需要再bindEvent之前实例化,否则单击事件只能触发隐藏文本编辑框,而无法保存文本修改
|
||||
this.textEdit = new TextEdit(this)
|
||||
// 当前复制的数据
|
||||
this.lastBeingCopyData = null
|
||||
this.beingCopyData = null
|
||||
this.beingPasteText = ''
|
||||
this.beingPasteImgSize = 0
|
||||
this.currentBeingPasteType = ''
|
||||
// 节点高亮框
|
||||
this.highlightBoxNode = null
|
||||
this.highlightBoxNodeStyle = null
|
||||
// 上一次节点激活数据
|
||||
this.lastActiveNode = null
|
||||
this.lastActiveNodeList = []
|
||||
// 布局
|
||||
this.setLayout()
|
||||
@@ -115,24 +112,26 @@ class Render {
|
||||
|
||||
// 设置布局结构
|
||||
setLayout() {
|
||||
const { layout } = this.mindMap.opt
|
||||
this.layout = new (
|
||||
layouts[this.mindMap.opt.layout]
|
||||
? layouts[this.mindMap.opt.layout]
|
||||
layouts[layout]
|
||||
? layouts[layout]
|
||||
: layouts[CONSTANTS.LAYOUT.LOGICAL_STRUCTURE]
|
||||
)(this, this.mindMap.opt.layout)
|
||||
)(this, layout)
|
||||
}
|
||||
|
||||
// 重新设置思维导图数据
|
||||
setData(data) {
|
||||
if (this.mindMap.richText) {
|
||||
this.renderTree = data ? this.mindMap.richText.handleSetData(data) : null
|
||||
} else {
|
||||
this.renderTree = data
|
||||
}
|
||||
this.renderTree = data || null
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
const {
|
||||
openPerformance,
|
||||
performanceConfig,
|
||||
openRealtimeRenderOnNodeTextEdit
|
||||
} = this.mindMap.opt
|
||||
// 画布点击事件清除当前激活节点列表
|
||||
this.mindMap.on('draw_click', e => {
|
||||
this.clearActiveNodeListOnDrawClick(e, 'click')
|
||||
@@ -147,25 +146,6 @@ class Render {
|
||||
this.setRootNodeCenter()
|
||||
})
|
||||
// 性能模式
|
||||
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()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 性能模式,懒加载节点
|
||||
performanceMode() {
|
||||
const { openPerformance, performanceConfig } = this.mindMap.opt
|
||||
const onViewDataChange = throttle(() => {
|
||||
if (this.root) {
|
||||
this.mindMap.emit('node_tree_render_start')
|
||||
@@ -178,24 +158,56 @@ class Render {
|
||||
)
|
||||
}
|
||||
}, performanceConfig.time)
|
||||
let lastOpen = false
|
||||
this.mindMap.on('before_update_config', opt => {
|
||||
lastOpen = opt.openPerformance
|
||||
})
|
||||
this.mindMap.on('after_update_config', opt => {
|
||||
if (opt.openPerformance && !lastOpen) {
|
||||
// 动态开启性能模式
|
||||
this.mindMap.on('view_data_change', onViewDataChange)
|
||||
if (openPerformance) {
|
||||
this.mindMap.on('view_data_change', onViewDataChange)
|
||||
}
|
||||
// 文本编辑时实时更新节点大小
|
||||
this.onNodeTextEditChange = this.onNodeTextEditChange.bind(this)
|
||||
if (openRealtimeRenderOnNodeTextEdit) {
|
||||
this.mindMap.on('node_text_edit_change', this.onNodeTextEditChange)
|
||||
}
|
||||
// 监听配置改变事件
|
||||
this.mindMap.on('after_update_config', (opt, lastOpt) => {
|
||||
// 更新openPerformance配置
|
||||
if (opt.openPerformance !== lastOpt.openPerformance) {
|
||||
this.mindMap[opt.openPerformance ? 'on' : 'off'](
|
||||
'view_data_change',
|
||||
onViewDataChange
|
||||
)
|
||||
this.forceLoadNode()
|
||||
}
|
||||
if (!opt.openPerformance && lastOpen) {
|
||||
// 动态关闭性能模式
|
||||
this.mindMap.off('view_data_change', onViewDataChange)
|
||||
this.forceLoadNode()
|
||||
// 更新openRealtimeRenderOnNodeTextEdit配置
|
||||
if (
|
||||
opt.openRealtimeRenderOnNodeTextEdit !==
|
||||
lastOpt.openRealtimeRenderOnNodeTextEdit
|
||||
) {
|
||||
this.mindMap[opt.openRealtimeRenderOnNodeTextEdit ? 'on' : 'off'](
|
||||
'node_text_edit_change',
|
||||
this.onNodeTextEditChange
|
||||
)
|
||||
}
|
||||
})
|
||||
if (!openPerformance) return
|
||||
this.mindMap.on('view_data_change', onViewDataChange)
|
||||
// 处理非https下的复制黏贴问题
|
||||
// 暂时不启用,因为给页面的其他输入框(比如节点文本编辑框)粘贴内容也会触发,冲突问题暂时没有想到好的解决方法,不可能要求所有输入框都阻止冒泡
|
||||
// if (!checkClipboardReadEnable()) {
|
||||
// this.handlePaste = this.handlePaste.bind(this)
|
||||
// window.addEventListener('paste', this.handlePaste)
|
||||
// this.mindMap.on('beforeDestroy', () => {
|
||||
// window.removeEventListener('paste', this.handlePaste)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
// 监听文本编辑事件,实时更新节点大小
|
||||
onNodeTextEditChange({ 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()
|
||||
})
|
||||
}
|
||||
|
||||
// 强制渲染节点,不考虑是否在画布可视区域内
|
||||
@@ -405,7 +417,7 @@ class Render {
|
||||
this.mindMap.keyCommand.addShortcut('Control+Down', () => {
|
||||
this.mindMap.execCommand('DOWN_NODE')
|
||||
})
|
||||
// 复制节点、
|
||||
// 复制节点
|
||||
this.mindMap.keyCommand.addShortcut('Control+c', () => {
|
||||
this.copy()
|
||||
})
|
||||
@@ -425,13 +437,11 @@ class Render {
|
||||
|
||||
// 派发节点激活事件
|
||||
emitNodeActiveEvent(node = null, activeNodeList = [...this.activeNodeList]) {
|
||||
let isChange = false
|
||||
isChange = this.lastActiveNode !== node
|
||||
if (!isChange) {
|
||||
isChange = !checkNodeListIsEqual(this.lastActiveNodeList, activeNodeList)
|
||||
}
|
||||
const isChange = !checkNodeListIsEqual(
|
||||
this.lastActiveNodeList,
|
||||
activeNodeList
|
||||
)
|
||||
if (!isChange) return
|
||||
this.lastActiveNode = node
|
||||
this.lastActiveNodeList = [...activeNodeList]
|
||||
this.mindMap.batchExecution.push('emitNodeActiveEvent', () => {
|
||||
this.mindMap.emit('node_active', node, activeNodeList)
|
||||
@@ -539,7 +549,7 @@ class Render {
|
||||
}
|
||||
// 触发一次保存,因为修改了渲染树的数据
|
||||
if (
|
||||
this.mindMap.richText &&
|
||||
this.hasRichTextPlugin() &&
|
||||
[CONSTANTS.CHANGE_THEME, CONSTANTS.SET_DATA].includes(source)
|
||||
) {
|
||||
this.mindMap.command.addHistory()
|
||||
@@ -553,7 +563,7 @@ class Render {
|
||||
|
||||
// 给当前被收起来的节点数据添加文本复位标志
|
||||
resetUnExpandNodeStyle() {
|
||||
if (!this.renderTree) return
|
||||
if (!this.renderTree || !this.hasRichTextPlugin()) return
|
||||
walk(this.renderTree, null, node => {
|
||||
if (!node.data.expand) {
|
||||
walk(node, null, node2 => {
|
||||
@@ -730,7 +740,7 @@ class Render {
|
||||
} = this.mindMap.opt
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const handleMultiNodes = list.length > 1
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const isRichText = this.hasRichTextPlugin()
|
||||
const { focusNewNode, inserting } = this.getNewNodeBehavior(
|
||||
openEdit,
|
||||
handleMultiNodes
|
||||
@@ -738,9 +748,9 @@ class Render {
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: focusNewNode // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
|
||||
}
|
||||
if (isRichText) params.resetRichText = isRichText
|
||||
// 动态指定的子节点数据也需要添加相关属性
|
||||
appointChildren = addDataToAppointNodes(appointChildren, {
|
||||
...params
|
||||
@@ -785,14 +795,14 @@ class Render {
|
||||
}
|
||||
this.textEdit.hideEditTextBox()
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const isRichText = this.hasRichTextPlugin()
|
||||
const { focusNewNode } = this.getNewNodeBehavior(false, true)
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: focusNewNode
|
||||
}
|
||||
if (isRichText) params.resetRichText = isRichText
|
||||
nodeList = addDataToAppointNodes(nodeList, params)
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization || node.isRoot) {
|
||||
@@ -828,7 +838,7 @@ class Render {
|
||||
} = this.mindMap.opt
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const handleMultiNodes = list.length > 1
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const isRichText = this.hasRichTextPlugin()
|
||||
const { focusNewNode, inserting } = this.getNewNodeBehavior(
|
||||
openEdit,
|
||||
handleMultiNodes
|
||||
@@ -836,9 +846,9 @@ class Render {
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: focusNewNode
|
||||
}
|
||||
if (isRichText) params.resetRichText = isRichText
|
||||
// 动态指定的子节点数据也需要添加相关属性
|
||||
appointChildren = addDataToAppointNodes(appointChildren, {
|
||||
...params
|
||||
@@ -885,14 +895,14 @@ class Render {
|
||||
}
|
||||
this.textEdit.hideEditTextBox()
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const isRichText = this.hasRichTextPlugin()
|
||||
const { focusNewNode } = this.getNewNodeBehavior(false, true)
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: focusNewNode
|
||||
}
|
||||
if (isRichText) params.resetRichText = isRichText
|
||||
childList = addDataToAppointNodes(childList, params)
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization) {
|
||||
@@ -927,7 +937,7 @@ class Render {
|
||||
} = this.mindMap.opt
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
const handleMultiNodes = list.length > 1
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const isRichText = this.hasRichTextPlugin()
|
||||
const { focusNewNode, inserting } = this.getNewNodeBehavior(
|
||||
openEdit,
|
||||
handleMultiNodes
|
||||
@@ -935,9 +945,9 @@ class Render {
|
||||
const params = {
|
||||
expand: true,
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: focusNewNode
|
||||
}
|
||||
if (isRichText) params.resetRichText = isRichText
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization || node.isRoot) {
|
||||
return
|
||||
@@ -956,9 +966,11 @@ class Render {
|
||||
},
|
||||
children: [node.nodeData]
|
||||
}
|
||||
node.setData({
|
||||
resetRichText: true
|
||||
})
|
||||
if (isRichText) {
|
||||
node.setData({
|
||||
resetRichText: true
|
||||
})
|
||||
}
|
||||
const parent = node.parent
|
||||
// 获取当前节点所在位置
|
||||
const index = getNodeDataIndex(node)
|
||||
@@ -972,11 +984,12 @@ class Render {
|
||||
}
|
||||
|
||||
// 上移节点,多个节点只会操作第一个节点
|
||||
upNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
upNode(appointNode) {
|
||||
if (this.activeNodeList.length <= 0 && !appointNode) {
|
||||
return
|
||||
}
|
||||
let node = this.activeNodeList[0]
|
||||
const list = appointNode ? [appointNode] : this.activeNodeList
|
||||
const node = list[0]
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
@@ -997,11 +1010,12 @@ class Render {
|
||||
}
|
||||
|
||||
// 下移节点,多个节点只会操作第一个节点
|
||||
downNode() {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
downNode(appointNode) {
|
||||
if (this.activeNodeList.length <= 0 && !appointNode) {
|
||||
return
|
||||
}
|
||||
let node = this.activeNodeList[0]
|
||||
const list = appointNode ? [appointNode] : this.activeNodeList
|
||||
const node = list[0]
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
@@ -1048,7 +1062,7 @@ class Render {
|
||||
}
|
||||
})
|
||||
// 如果是富文本,那么还要处理富文本内容
|
||||
if (hasCustomStyles && this.mindMap.richText) {
|
||||
if (hasCustomStyles && this.hasRichTextPlugin()) {
|
||||
nodeData.resetRichText = true
|
||||
nodeData.text = removeRichTextStyes(nodeData.text)
|
||||
}
|
||||
@@ -1118,140 +1132,150 @@ 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.paste()
|
||||
}
|
||||
|
||||
// 粘贴
|
||||
async paste() {
|
||||
const {
|
||||
errorHandler,
|
||||
handleIsSplitByWrapOnPasteCreateNewNode,
|
||||
handleNodePasteImg,
|
||||
disabledClipboard
|
||||
disabledClipboard,
|
||||
onlyPasteTextWhenHasImgAndText
|
||||
} = this.mindMap.opt
|
||||
// 读取剪贴板的文字和图片
|
||||
let text = ''
|
||||
let img = null
|
||||
if (!disabledClipboard) {
|
||||
// 如果支持剪贴板操作,那么以剪贴板数据为准
|
||||
if (!disabledClipboard && checkClipboardReadEnable()) {
|
||||
try {
|
||||
const res = await getDataFromClipboard()
|
||||
text = res.text || ''
|
||||
img = res.img || null
|
||||
let text = res.text || ''
|
||||
let img = res.img || null
|
||||
// 存在文本,则创建子节点
|
||||
if (text) {
|
||||
// 判断粘贴的是否是simple-mind-map的数据
|
||||
let smmData = null
|
||||
let useDefault = true
|
||||
// 用户自定义处理
|
||||
if (this.mindMap.opt.customHandleClipboardText) {
|
||||
try {
|
||||
const res = await this.mindMap.opt.customHandleClipboardText(text)
|
||||
if (!isUndef(res)) {
|
||||
useDefault = false
|
||||
const checkRes = checkSmmFormatData(res)
|
||||
if (checkRes.isSmm) {
|
||||
smmData = checkRes.data
|
||||
} else {
|
||||
text = checkRes.data
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(
|
||||
ERROR_TYPES.CUSTOM_HANDLE_CLIPBOARD_TEXT_ERROR,
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
// 默认处理
|
||||
if (useDefault) {
|
||||
const checkRes = checkSmmFormatData(text)
|
||||
if (checkRes.isSmm) {
|
||||
smmData = checkRes.data
|
||||
} else {
|
||||
text = checkRes.data
|
||||
}
|
||||
}
|
||||
if (smmData) {
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
[],
|
||||
Array.isArray(smmData) ? smmData : [smmData]
|
||||
)
|
||||
} else {
|
||||
text = htmlEscape(text)
|
||||
const textArr = text
|
||||
.split(new RegExp('\r?\n|(?<!\n)\r', 'g'))
|
||||
.filter(item => {
|
||||
return !!item
|
||||
})
|
||||
// 判断是否需要根据换行自动分割节点
|
||||
if (textArr.length > 1 && handleIsSplitByWrapOnPasteCreateNewNode) {
|
||||
handleIsSplitByWrapOnPasteCreateNewNode()
|
||||
.then(() => {
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
[],
|
||||
textArr.map(item => {
|
||||
return {
|
||||
data: {
|
||||
text: item
|
||||
},
|
||||
children: []
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// 存在图片,则添加到当前激活节点
|
||||
if (img && (!text || !onlyPasteTextWhenHasImgAndText)) {
|
||||
try {
|
||||
let imgData = null
|
||||
// 自定义图片处理函数
|
||||
if (
|
||||
handleNodePasteImg &&
|
||||
typeof handleNodePasteImg === 'function'
|
||||
) {
|
||||
imgData = await handleNodePasteImg(img)
|
||||
} else {
|
||||
imgData = await loadImage(img)
|
||||
}
|
||||
if (this.activeNodeList.length > 0) {
|
||||
this.activeNodeList.forEach(node => {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', node, {
|
||||
url: imgData.url,
|
||||
title: '',
|
||||
width: imgData.size.width,
|
||||
height: imgData.size.height
|
||||
})
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.LOAD_CLIPBOARD_IMAGE_ERROR, error)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.READ_CLIPBOARD_ERROR, error)
|
||||
}
|
||||
}
|
||||
// 检查剪切板数据是否有变化
|
||||
// 通过图片大小来判断图片是否发生变化,可能是不准确的,但是目前没有其他好方法
|
||||
const imgSize = img ? img.size : 0
|
||||
if (this.beingPasteText !== text || this.beingPasteImgSize !== imgSize) {
|
||||
this.currentBeingPasteType = CONSTANTS.PASTE_TYPE.CLIP_BOARD
|
||||
this.beingPasteText = text
|
||||
this.beingPasteImgSize = imgSize
|
||||
}
|
||||
// 检查要粘贴的节点数据是否有变化,节点优先级高于剪切板
|
||||
if (this.lastBeingCopyData !== this.beingCopyData) {
|
||||
this.lastBeingCopyData = this.beingCopyData
|
||||
this.currentBeingPasteType = CONSTANTS.PASTE_TYPE.CANVAS
|
||||
}
|
||||
// 粘贴剪切板的数据
|
||||
if (this.currentBeingPasteType === CONSTANTS.PASTE_TYPE.CLIP_BOARD) {
|
||||
// 存在文本,则创建子节点
|
||||
if (text) {
|
||||
// 判断粘贴的是否是simple-mind-map的数据
|
||||
let smmData = null
|
||||
let useDefault = true
|
||||
// 用户自定义处理
|
||||
if (this.mindMap.opt.customHandleClipboardText) {
|
||||
try {
|
||||
const res = await this.mindMap.opt.customHandleClipboardText(text)
|
||||
if (!isUndef(res)) {
|
||||
useDefault = false
|
||||
const checkRes = checkSmmFormatData(res)
|
||||
if (checkRes.isSmm) {
|
||||
smmData = checkRes.data
|
||||
} else {
|
||||
text = checkRes.data
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.CUSTOM_HANDLE_CLIPBOARD_TEXT_ERROR, error)
|
||||
}
|
||||
}
|
||||
// 默认处理
|
||||
if (useDefault) {
|
||||
const checkRes = checkSmmFormatData(text)
|
||||
if (checkRes.isSmm) {
|
||||
smmData = checkRes.data
|
||||
} else {
|
||||
text = checkRes.data
|
||||
}
|
||||
}
|
||||
if (smmData) {
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
[],
|
||||
Array.isArray(smmData) ? smmData : [smmData]
|
||||
)
|
||||
} else {
|
||||
text = htmlEscape(text)
|
||||
const textArr = text
|
||||
.split(new RegExp('\r?\n|(?<!\n)\r', 'g'))
|
||||
.filter(item => {
|
||||
return !!item
|
||||
})
|
||||
// 判断是否需要根据换行自动分割节点
|
||||
if (textArr.length > 1 && handleIsSplitByWrapOnPasteCreateNewNode) {
|
||||
handleIsSplitByWrapOnPasteCreateNewNode()
|
||||
.then(() => {
|
||||
this.mindMap.execCommand(
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
[],
|
||||
textArr.map(item => {
|
||||
return {
|
||||
data: {
|
||||
text: item
|
||||
},
|
||||
children: []
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
.catch(() => {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// 存在图片,则添加到当前激活节点
|
||||
if (img) {
|
||||
try {
|
||||
let imgData = null
|
||||
// 自定义图片处理函数
|
||||
if (handleNodePasteImg && typeof handleNodePasteImg === 'function') {
|
||||
imgData = await handleNodePasteImg(img)
|
||||
} else {
|
||||
imgData = await loadImage(img)
|
||||
}
|
||||
if (this.activeNodeList.length > 0) {
|
||||
this.activeNodeList.forEach(node => {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', node, {
|
||||
url: imgData.url,
|
||||
title: '',
|
||||
width: imgData.size.width,
|
||||
height: imgData.size.height
|
||||
})
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
errorHandler(ERROR_TYPES.LOAD_CLIPBOARD_IMAGE_ERROR, error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 粘贴节点数据
|
||||
// 禁用剪贴板或不支持剪贴板时
|
||||
// 粘贴画布内的节点数据
|
||||
if (this.beingCopyData) {
|
||||
this.mindMap.execCommand('PASTE_NODE', this.beingCopyData)
|
||||
}
|
||||
@@ -1307,7 +1331,7 @@ class Render {
|
||||
|
||||
// 如果是富文本模式,那么某些层级变化需要更新样式
|
||||
checkNodeLayerChange(node, toNode, toNodeIsParent = false) {
|
||||
if (this.mindMap.richText) {
|
||||
if (this.hasRichTextPlugin()) {
|
||||
// 如果设置了自定义样式那么不需要更新
|
||||
if (this.mindMap.richText.checkNodeHasCustomRichTextStyle(node)) {
|
||||
return
|
||||
@@ -1526,6 +1550,8 @@ class Render {
|
||||
return
|
||||
}
|
||||
this.activeNodeList.forEach(node => {
|
||||
// 概要节点不允许添加下级节点
|
||||
if (node.isGeneralization) return
|
||||
node.setData({
|
||||
expand: true
|
||||
})
|
||||
@@ -1534,7 +1560,7 @@ class Render {
|
||||
const newData = simpleDeepClone(item)
|
||||
createUidForAppointNodes([newData], true, node => {
|
||||
// 可能跨层级复制,那么富文本样式需要更新
|
||||
if (this.mindMap.richText) {
|
||||
if (this.hasRichTextPlugin()) {
|
||||
// 如果设置了自定义样式那么不需要更新
|
||||
if (
|
||||
this.mindMap.richText.checkNodeHasCustomRichTextStyle(node.data)
|
||||
@@ -1553,28 +1579,32 @@ class Render {
|
||||
|
||||
// 设置节点样式
|
||||
setNodeStyle(node, prop, value) {
|
||||
let data = {
|
||||
const data = {
|
||||
[prop]: value
|
||||
}
|
||||
// 如果开启了富文本,则需要应用到富文本上
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.setNotActiveNodeStyle(node, {
|
||||
[prop]: value
|
||||
})
|
||||
if (
|
||||
this.hasRichTextPlugin() &&
|
||||
this.mindMap.richText.isHasRichTextStyle(data)
|
||||
) {
|
||||
data.resetRichText = true
|
||||
}
|
||||
this.setNodeDataRender(node, data)
|
||||
// 更新了连线的样式
|
||||
if (lineStyleProps.includes(prop)) {
|
||||
;(node.parent || node).renderLine(true)
|
||||
(node.parent || node).renderLine(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置节点多个样式
|
||||
setNodeStyles(node, style) {
|
||||
let data = { ...style }
|
||||
const data = { ...style }
|
||||
// 如果开启了富文本,则需要应用到富文本上
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.setNotActiveNodeStyle(node, style)
|
||||
if (
|
||||
this.hasRichTextPlugin() &&
|
||||
this.mindMap.richText.isHasRichTextStyle(data)
|
||||
) {
|
||||
data.resetRichText = true
|
||||
}
|
||||
this.setNodeDataRender(node, data)
|
||||
// 更新了连线的样式
|
||||
@@ -1586,7 +1616,7 @@ class Render {
|
||||
}
|
||||
})
|
||||
if (hasLineStyleProps) {
|
||||
;(node.parent || node).renderLine(true)
|
||||
(node.parent || node).renderLine(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1768,7 +1798,7 @@ class Render {
|
||||
// 设置节点公式
|
||||
insertFormula(formula, appointNodes = []) {
|
||||
// 只在富文本模式下可用,并且需要注册Formula插件
|
||||
if (!this.mindMap.richText || !this.mindMap.formula) return
|
||||
if (!this.hasRichTextPlugin() || !this.mindMap.formula) return
|
||||
appointNodes = formatDataToArray(appointNodes)
|
||||
const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
list.forEach(node => {
|
||||
@@ -1790,7 +1820,7 @@ class Render {
|
||||
})
|
||||
const list = parseAddGeneralizationNodeList(nodeList)
|
||||
if (list.length <= 0) return
|
||||
const isRichText = !!this.mindMap.richText
|
||||
const isRichText = this.hasRichTextPlugin()
|
||||
const { focusNewNode, inserting } = this.getNewNodeBehavior(
|
||||
openEdit,
|
||||
list.length > 1
|
||||
@@ -1805,9 +1835,9 @@ class Render {
|
||||
range: item.range || null,
|
||||
uid: createUid(),
|
||||
richText: isRichText,
|
||||
resetRichText: isRichText,
|
||||
isActive: focusNewNode
|
||||
}
|
||||
if (isRichText) newData.resetRichText = isRichText
|
||||
let generalization = item.node.getData('generalization')
|
||||
generalization = generalization
|
||||
? Array.isArray(generalization)
|
||||
@@ -1937,6 +1967,10 @@ class Render {
|
||||
// 设置节点数据,并判断是否渲染
|
||||
setNodeDataRender(node, data, notRender = false) {
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, data)
|
||||
if (isNodeNotNeedRenderData(data)) {
|
||||
this.mindMap.emit('node_tree_render_end')
|
||||
return
|
||||
}
|
||||
this.reRenderNodeCheckChange(node, notRender)
|
||||
}
|
||||
|
||||
@@ -2137,6 +2171,11 @@ class Render {
|
||||
if (!this.highlightBoxNode) return
|
||||
this.highlightBoxNode.remove()
|
||||
}
|
||||
|
||||
// 是否存在富文本插件
|
||||
hasRichTextPlugin() {
|
||||
return !!this.mindMap.richText
|
||||
}
|
||||
}
|
||||
|
||||
export default Render
|
||||
|
||||
@@ -6,9 +6,15 @@ import {
|
||||
htmlEscape,
|
||||
handleInputPasteText,
|
||||
checkSmmFormatData,
|
||||
getTextFromHtml
|
||||
getTextFromHtml,
|
||||
isWhite,
|
||||
getVisibleColorFromTheme
|
||||
} from '../../utils'
|
||||
import { ERROR_TYPES, CONSTANTS } from '../../constants/constant'
|
||||
import {
|
||||
ERROR_TYPES,
|
||||
CONSTANTS,
|
||||
noneRichTextNodeLineHeight
|
||||
} from '../../constants/constant'
|
||||
|
||||
// 节点文字编辑类
|
||||
export default class TextEdit {
|
||||
@@ -92,6 +98,32 @@ export default class TextEdit {
|
||||
this.mindMap.on('beforeDestroy', () => {
|
||||
this.unBindEvent()
|
||||
})
|
||||
this.mindMap.on('after_update_config', (opt, lastOpt) => {
|
||||
if (
|
||||
opt.openRealtimeRenderOnNodeTextEdit !==
|
||||
lastOpt.openRealtimeRenderOnNodeTextEdit
|
||||
) {
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.onOpenRealtimeRenderOnNodeTextEditConfigUpdate(
|
||||
opt.openRealtimeRenderOnNodeTextEdit
|
||||
)
|
||||
} else {
|
||||
this.onOpenRealtimeRenderOnNodeTextEditConfigUpdate(
|
||||
opt.openRealtimeRenderOnNodeTextEdit
|
||||
)
|
||||
}
|
||||
}
|
||||
if (
|
||||
opt.enableAutoEnterTextEditWhenKeydown !==
|
||||
lastOpt.enableAutoEnterTextEditWhenKeydown
|
||||
) {
|
||||
window[
|
||||
opt.enableAutoEnterTextEditWhenKeydown
|
||||
? 'addEventListener'
|
||||
: 'removeEventListener'
|
||||
]('keydown', this.onKeydown)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
@@ -101,6 +133,7 @@ export default class TextEdit {
|
||||
|
||||
// 按键事件
|
||||
onKeydown(e) {
|
||||
if (e.target !== document.body) return
|
||||
const activeNodeList = this.mindMap.renderer.activeNodeList
|
||||
if (activeNodeList.length <= 0 || activeNodeList.length > 1) return
|
||||
const node = activeNodeList[0]
|
||||
@@ -158,7 +191,8 @@ export default class TextEdit {
|
||||
if (node.isUseCustomNodeContent()) {
|
||||
return
|
||||
}
|
||||
const { beforeTextEdit } = this.mindMap.opt
|
||||
const { beforeTextEdit, openRealtimeRenderOnNodeTextEdit } =
|
||||
this.mindMap.opt
|
||||
if (typeof beforeTextEdit === 'function') {
|
||||
let isShow = false
|
||||
try {
|
||||
@@ -172,7 +206,12 @@ export default class TextEdit {
|
||||
this.currentNode = node
|
||||
const { offsetLeft, offsetTop } = checkNodeOuter(this.mindMap, node)
|
||||
this.mindMap.view.translateXY(offsetLeft, offsetTop)
|
||||
const rect = node._textData.node.node.getBoundingClientRect()
|
||||
const g = node._textData.node
|
||||
const rect = g.node.getBoundingClientRect()
|
||||
// 如果开启了大小实时更新,那么直接隐藏节点原文本
|
||||
if (openRealtimeRenderOnNodeTextEdit) {
|
||||
g.hide()
|
||||
}
|
||||
const params = {
|
||||
node,
|
||||
rect,
|
||||
@@ -187,6 +226,21 @@ export default class TextEdit {
|
||||
this.showEditTextBox(params)
|
||||
}
|
||||
|
||||
// 当openRealtimeRenderOnNodeTextEdit配置更新后需要更新编辑框样式
|
||||
onOpenRealtimeRenderOnNodeTextEditConfigUpdate(
|
||||
openRealtimeRenderOnNodeTextEdit
|
||||
) {
|
||||
if (!this.textEditNode) return
|
||||
this.textEditNode.style.background = openRealtimeRenderOnNodeTextEdit
|
||||
? 'transparent'
|
||||
: this.currentNode
|
||||
? this.getBackground(this.currentNode)
|
||||
: ''
|
||||
this.textEditNode.style.boxShadow = openRealtimeRenderOnNodeTextEdit
|
||||
? 'none'
|
||||
: '0 0 20px rgba(0,0,0,.5)'
|
||||
}
|
||||
|
||||
// 处理画布缩放
|
||||
onScale() {
|
||||
const node = this.getCurrentEditNode()
|
||||
@@ -208,15 +262,37 @@ export default class TextEdit {
|
||||
// 显示文本编辑框
|
||||
showEditTextBox({ node, rect, isInserting, isFromKeyDown, isFromScale }) {
|
||||
if (this.showTextEdit) return
|
||||
const { nodeTextEditZIndex, textAutoWrapWidth, selectTextOnEnterEditText } =
|
||||
this.mindMap.opt
|
||||
const {
|
||||
nodeTextEditZIndex,
|
||||
textAutoWrapWidth,
|
||||
selectTextOnEnterEditText,
|
||||
openRealtimeRenderOnNodeTextEdit,
|
||||
autoEmptyTextWhenKeydownEnterEdit
|
||||
} = this.mindMap.opt
|
||||
if (!isFromScale) {
|
||||
this.mindMap.emit('before_show_text_edit')
|
||||
}
|
||||
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: ${this.textNodePaddingY}px ${this.textNodePaddingX}px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||
this.textEditNode.classList.add(
|
||||
CONSTANTS.EDIT_NODE_CLASS.SMM_NODE_EDIT_WRAP
|
||||
)
|
||||
this.textEditNode.style.cssText = `
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
${
|
||||
openRealtimeRenderOnNodeTextEdit
|
||||
? ''
|
||||
: `box-shadow: 0 0 20px rgba(0,0,0,.5);`
|
||||
}
|
||||
padding: ${this.textNodePaddingY}px ${this.textNodePaddingX}px;
|
||||
margin-left: -${this.textNodePaddingX}px;
|
||||
margin-top: -${this.textNodePaddingY}px;
|
||||
outline: none;
|
||||
word-break: break-all;
|
||||
line-break: anywhere;
|
||||
`
|
||||
this.textEditNode.setAttribute('contenteditable', true)
|
||||
this.textEditNode.addEventListener('keyup', e => {
|
||||
e.stopPropagation()
|
||||
@@ -253,30 +329,38 @@ export default class TextEdit {
|
||||
this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.textEditNode)
|
||||
}
|
||||
let scale = this.mindMap.view.scale
|
||||
let lineHeight = node.style.merge('lineHeight')
|
||||
let fontSize = node.style.merge('fontSize')
|
||||
let textLines = (this.cacheEditingText || node.getData('text'))
|
||||
const scale = this.mindMap.view.scale
|
||||
const fontSize = node.style.merge('fontSize')
|
||||
const textLines = (this.cacheEditingText || node.getData('text'))
|
||||
.split(/\n/gim)
|
||||
.map(item => {
|
||||
return htmlEscape(item)
|
||||
})
|
||||
let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true'
|
||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||
const isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true'
|
||||
node.style.domText(this.textEditNode, scale)
|
||||
if (!openRealtimeRenderOnNodeTextEdit) {
|
||||
this.textEditNode.style.background = this.getBackground(node)
|
||||
}
|
||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
if (isFromKeyDown && autoEmptyTextWhenKeydownEnterEdit) {
|
||||
this.textEditNode.innerHTML = ''
|
||||
} else {
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
}
|
||||
this.textEditNode.style.minWidth =
|
||||
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight =
|
||||
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||
this.textEditNode.style.minHeight = rect.height + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth = textAutoWrapWidth * scale + 'px'
|
||||
if (isMultiLine && lineHeight !== 1) {
|
||||
if (isMultiLine) {
|
||||
this.textEditNode.style.lineHeight = noneRichTextNodeLineHeight
|
||||
this.textEditNode.style.transform = `translateY(${
|
||||
-((lineHeight * fontSize - fontSize) / 2) * scale
|
||||
(((noneRichTextNodeLineHeight - 1) * fontSize) / 2) * scale
|
||||
}px)`
|
||||
} else {
|
||||
this.textEditNode.style.lineHeight = 'normal'
|
||||
}
|
||||
this.showTextEdit = true
|
||||
// 选中文本
|
||||
@@ -309,6 +393,27 @@ export default class TextEdit {
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
}
|
||||
|
||||
// 获取编辑区域的背景填充
|
||||
getBackground(node) {
|
||||
const gradientStyle = node.style.merge('gradientStyle')
|
||||
// 当前使用的是渐变色背景
|
||||
if (gradientStyle) {
|
||||
const startColor = node.style.merge('startColor')
|
||||
const endColor = node.style.merge('endColor')
|
||||
return `linear-gradient(to right, ${startColor}, ${endColor})`
|
||||
} else {
|
||||
// 单色背景
|
||||
const bgColor = node.style.merge('fillColor')
|
||||
const color = node.style.merge('color')
|
||||
// 默认使用节点的填充色,否则如果节点颜色是白色的话编辑时看不见
|
||||
return bgColor === 'transparent'
|
||||
? isWhite(color)
|
||||
? getVisibleColorFromTheme(this.mindMap.themeConfig)
|
||||
: '#fff'
|
||||
: bgColor
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文本编辑元素
|
||||
removeTextEditEl() {
|
||||
if (this.mindMap.richText) {
|
||||
@@ -333,16 +438,8 @@ export default class TextEdit {
|
||||
if (!this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
this.renderer.activeNodeList.forEach(node => {
|
||||
let str = this.getEditText()
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', node, str)
|
||||
if (node.isGeneralization) {
|
||||
// 概要节点
|
||||
node.generalizationBelongNode.updateGeneralization()
|
||||
}
|
||||
this.mindMap.render()
|
||||
})
|
||||
const currentNode = this.currentNode
|
||||
const text = this.getEditText()
|
||||
this.currentNode = null
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.textEditNode.innerHTML = ''
|
||||
@@ -351,6 +448,12 @@ export default class TextEdit {
|
||||
this.textEditNode.style.fontWeight = 'normal'
|
||||
this.textEditNode.style.transform = 'translateY(0)'
|
||||
this.showTextEdit = false
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', currentNode, text)
|
||||
// if (currentNode.isGeneralization) {
|
||||
// // 概要节点
|
||||
// currentNode.generalizationBelongNode.updateGeneralization()
|
||||
// }
|
||||
this.mindMap.render()
|
||||
this.mindMap.emit(
|
||||
'hide_text_edit',
|
||||
this.textEditNode,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import Style from './Style'
|
||||
import Shape from './Shape'
|
||||
import { G, Rect, Text } from '@svgdotjs/svg.js'
|
||||
import { G, Rect, Text, SVG } from '@svgdotjs/svg.js'
|
||||
import nodeGeneralizationMethods from './nodeGeneralization'
|
||||
import nodeExpandBtnMethods from './nodeExpandBtn'
|
||||
import nodeCommandWrapsMethods from './nodeCommandWraps'
|
||||
import nodeCreateContentsMethods from './nodeCreateContents'
|
||||
import nodeExpandBtnPlaceholderRectMethods from './nodeExpandBtnPlaceholderRect'
|
||||
import nodeModifyWidthMethods from './nodeModifyWidth'
|
||||
import nodeCooperateMethods from './nodeCooperate'
|
||||
import { CONSTANTS } from '../../../constants/constant'
|
||||
import {
|
||||
@@ -22,6 +23,8 @@ class MindMapNode {
|
||||
this.opt = opt
|
||||
// 节点数据
|
||||
this.nodeData = this.handleData(opt.data || {})
|
||||
// 保存本次更新时的节点数据快照
|
||||
this.nodeDataSnapshot = ''
|
||||
// uid
|
||||
this.uid = opt.uid
|
||||
// 控制实例
|
||||
@@ -54,6 +57,8 @@ class MindMapNode {
|
||||
this.width = opt.width || 0
|
||||
// 节点高
|
||||
this.height = opt.height || 0
|
||||
// 自定义文本的宽度
|
||||
this.customTextWidth = opt.data.data.customTextWidth || undefined
|
||||
// left
|
||||
this._left = opt.left || 0
|
||||
// top
|
||||
@@ -84,7 +89,6 @@ class MindMapNode {
|
||||
this.noteEl = null
|
||||
this.noteContentIsShow = false
|
||||
this._attachmentData = null
|
||||
this._numberData = null
|
||||
this._prefixData = null
|
||||
this._postfixData = null
|
||||
this._expandBtn = null
|
||||
@@ -108,8 +112,6 @@ class MindMapNode {
|
||||
// 概要节点的宽高
|
||||
this._generalizationNodeWidth = 0
|
||||
this._generalizationNodeHeight = 0
|
||||
// 编号字符
|
||||
this.number = opt.number || ''
|
||||
// 各种文字信息的间距
|
||||
this.textContentItemMargin = this.mindMap.opt.textContentMargin
|
||||
// 图片和文字节点的间距
|
||||
@@ -150,10 +152,15 @@ class MindMapNode {
|
||||
proto[item] = nodeCooperateMethods[item]
|
||||
})
|
||||
}
|
||||
// 拖拽调整节点宽度
|
||||
Object.keys(nodeModifyWidthMethods).forEach(item => {
|
||||
proto[item] = nodeModifyWidthMethods[item]
|
||||
})
|
||||
proto.bindEvent = true
|
||||
}
|
||||
// 初始化
|
||||
this.getSize()
|
||||
this.initDragHandle()
|
||||
}
|
||||
|
||||
// 支持自定义位置
|
||||
@@ -197,15 +204,50 @@ class MindMapNode {
|
||||
}
|
||||
|
||||
// 创建节点的各个内容对象数据
|
||||
createNodeData() {
|
||||
// recreateTypes:[] custom、image、icon、text、hyperlink、tag、note、attachment、numbers、prefix、postfix、checkbox
|
||||
createNodeData(recreateTypes) {
|
||||
// 自定义节点内容
|
||||
let {
|
||||
const {
|
||||
isUseCustomNodeContent,
|
||||
customCreateNodeContent,
|
||||
createNodePrefixContent,
|
||||
createNodePostfixContent
|
||||
} = this.mindMap.opt
|
||||
if (isUseCustomNodeContent && customCreateNodeContent) {
|
||||
// 需要创建的内容类型
|
||||
const typeList = [
|
||||
'custom',
|
||||
'image',
|
||||
'icon',
|
||||
'text',
|
||||
'hyperlink',
|
||||
'tag',
|
||||
'note',
|
||||
'attachment',
|
||||
'prefix',
|
||||
'postfix',
|
||||
...this.mindMap.nodeInnerPrefixList.map(item => {
|
||||
return item.name
|
||||
})
|
||||
]
|
||||
const createTypes = {}
|
||||
if (Array.isArray(recreateTypes)) {
|
||||
// 重新创建指定的内容类型
|
||||
typeList.forEach(item => {
|
||||
if (recreateTypes.includes(item)) {
|
||||
createTypes[item] = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 创建所有类型
|
||||
typeList.forEach(item => {
|
||||
createTypes[item] = true
|
||||
})
|
||||
}
|
||||
if (
|
||||
isUseCustomNodeContent &&
|
||||
customCreateNodeContent &&
|
||||
createTypes.custom
|
||||
) {
|
||||
this._customNodeContent = customCreateNodeContent(this)
|
||||
}
|
||||
// 如果没有返回内容,那么还是使用内置的节点内容
|
||||
@@ -213,40 +255,51 @@ class MindMapNode {
|
||||
addXmlns(this._customNodeContent)
|
||||
return
|
||||
}
|
||||
this._imgData = this.createImgNode()
|
||||
this._iconData = this.createIconNode()
|
||||
this._textData = this.createTextNode()
|
||||
this._hyperlinkData = this.createHyperlinkNode()
|
||||
this._tagData = this.createTagNode()
|
||||
this._noteData = this.createNoteNode()
|
||||
this._attachmentData = this.createAttachmentNode()
|
||||
if (this.mindMap.numbers) {
|
||||
this._numberData = this.mindMap.numbers.createNumberContent(this)
|
||||
if (createTypes.image) this._imgData = this.createImgNode()
|
||||
if (createTypes.icon) this._iconData = this.createIconNode()
|
||||
if (createTypes.text) this._textData = this.createTextNode()
|
||||
if (createTypes.hyperlink) this._hyperlinkData = this.createHyperlinkNode()
|
||||
if (createTypes.tag) this._tagData = this.createTagNode()
|
||||
if (createTypes.note) this._noteData = this.createNoteNode()
|
||||
if (createTypes.attachment)
|
||||
this._attachmentData = this.createAttachmentNode()
|
||||
this.mindMap.nodeInnerPrefixList.forEach(item => {
|
||||
if (createTypes[item.name]) {
|
||||
this[`_${item.name}Data`] = item.createContent(this)
|
||||
}
|
||||
})
|
||||
if (createTypes.prefix) {
|
||||
this._prefixData = createNodePrefixContent
|
||||
? createNodePrefixContent(this)
|
||||
: null
|
||||
if (this._prefixData && this._prefixData.el) {
|
||||
addXmlns(this._prefixData.el)
|
||||
}
|
||||
}
|
||||
this._prefixData = createNodePrefixContent
|
||||
? createNodePrefixContent(this)
|
||||
: null
|
||||
if (this._prefixData && this._prefixData.el) {
|
||||
addXmlns(this._prefixData.el)
|
||||
}
|
||||
this._postfixData = createNodePostfixContent
|
||||
? createNodePostfixContent(this)
|
||||
: null
|
||||
if (this._postfixData && this._postfixData.el) {
|
||||
addXmlns(this._postfixData.el)
|
||||
if (createTypes.postfix) {
|
||||
this._postfixData = createNodePostfixContent
|
||||
? createNodePostfixContent(this)
|
||||
: null
|
||||
if (this._postfixData && this._postfixData.el) {
|
||||
addXmlns(this._postfixData.el)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算节点的宽高
|
||||
getSize() {
|
||||
getSize(recreateTypes, opt = {}) {
|
||||
const ignoreUpdateCustomTextWidth = opt.ignoreUpdateCustomTextWidth || false
|
||||
if (!ignoreUpdateCustomTextWidth) {
|
||||
this.customTextWidth = this.getData('customTextWidth') || undefined
|
||||
}
|
||||
this.customLeft = this.getData('customLeft') || undefined
|
||||
this.customTop = this.getData('customTop') || undefined
|
||||
// 这里不要更新概要,不然即使概要没修改,每次也会重新渲染
|
||||
// this.updateGeneralization()
|
||||
this.createNodeData()
|
||||
let { width, height } = this.getNodeRect()
|
||||
this.createNodeData(recreateTypes)
|
||||
const { width, height } = this.getNodeRect()
|
||||
// 判断节点尺寸是否有变化
|
||||
let changed = this.width !== width || this.height !== height
|
||||
const changed = this.width !== width || this.height !== height
|
||||
this.width = width
|
||||
this.height = height
|
||||
return changed
|
||||
@@ -256,9 +309,9 @@ class MindMapNode {
|
||||
getNodeRect() {
|
||||
// 自定义节点内容
|
||||
if (this.isUseCustomNodeContent()) {
|
||||
let rect = this.measureCustomNodeContentSize(this._customNodeContent)
|
||||
const rect = this.measureCustomNodeContentSize(this._customNodeContent)
|
||||
return {
|
||||
width: rect.width,
|
||||
width: this.hasCustomWidth() ? this.customTextWidth : rect.width,
|
||||
height: rect.height
|
||||
}
|
||||
}
|
||||
@@ -276,11 +329,14 @@ class MindMapNode {
|
||||
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
|
||||
this._rectInfo.imgContentHeight = imgContentHeight = this._imgData.height
|
||||
}
|
||||
// 编号内容
|
||||
if (this._numberData) {
|
||||
textContentWidth += this._numberData.width
|
||||
textContentHeight = Math.max(textContentHeight, this._numberData.height)
|
||||
}
|
||||
// 库前置内容
|
||||
this.mindMap.nodeInnerPrefixList.forEach(item => {
|
||||
const itemData = this[`_${item.name}Data`]
|
||||
if (itemData) {
|
||||
textContentWidth += itemData.width
|
||||
textContentHeight = Math.max(textContentHeight, itemData.height)
|
||||
}
|
||||
})
|
||||
// 自定义前置内容
|
||||
if (this._prefixData) {
|
||||
textContentWidth += this._prefixData.width
|
||||
@@ -349,7 +405,7 @@ class MindMapNode {
|
||||
imgContentHeight > 0 && textContentHeight > 0
|
||||
? this.blockContentMargin
|
||||
: 0
|
||||
let { paddingX, paddingY } = this.getPaddingVale()
|
||||
const { paddingX, paddingY } = this.getPaddingVale()
|
||||
// 纯内容宽高
|
||||
let _width = Math.max(imgContentWidth, textContentWidth)
|
||||
let _height = imgContentHeight + textContentHeight
|
||||
@@ -363,7 +419,7 @@ class MindMapNode {
|
||||
_height += tagContentHeight
|
||||
}
|
||||
// 计算节点形状需要的附加内边距
|
||||
let { paddingX: shapePaddingX, paddingY: shapePaddingY } =
|
||||
const { paddingX: shapePaddingX, paddingY: shapePaddingY } =
|
||||
this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
|
||||
this.shapePadding.paddingX = shapePaddingX
|
||||
this.shapePadding.paddingY = shapePaddingY
|
||||
@@ -380,7 +436,8 @@ class MindMapNode {
|
||||
if (!this.group) return
|
||||
// 清除之前的内容
|
||||
this.group.clear()
|
||||
const { hoverRectPadding, tagPosition } = this.mindMap.opt
|
||||
const { hoverRectPadding, tagPosition, openRealtimeRenderOnNodeTextEdit } =
|
||||
this.mindMap.opt
|
||||
let { width, height, textContentItemMargin } = this
|
||||
let { paddingY } = this.getPaddingVale()
|
||||
const halfBorderWidth = this.getBorderWidth() / 2
|
||||
@@ -432,14 +489,17 @@ class MindMapNode {
|
||||
// 内容节点
|
||||
let textContentNested = new G()
|
||||
let textContentOffsetX = 0
|
||||
// 编号内容
|
||||
if (this._numberData) {
|
||||
this._numberData.node
|
||||
.x(textContentOffsetX)
|
||||
.y((textContentHeight - this._numberData.height) / 2)
|
||||
textContentNested.add(this._numberData.node)
|
||||
textContentOffsetX += this._numberData.width + textContentItemMargin
|
||||
}
|
||||
// 库前置内容
|
||||
this.mindMap.nodeInnerPrefixList.forEach(item => {
|
||||
const itemData = this[`_${item.name}Data`]
|
||||
if (itemData) {
|
||||
itemData.node
|
||||
.x(textContentOffsetX)
|
||||
.y((textContentHeight - itemData.height) / 2)
|
||||
textContentNested.add(itemData.node)
|
||||
textContentOffsetX += itemData.width + textContentItemMargin
|
||||
}
|
||||
})
|
||||
// 自定义前置内容
|
||||
if (this._prefixData) {
|
||||
const foreignObject = createForeignObjectNode({
|
||||
@@ -476,6 +536,12 @@ class MindMapNode {
|
||||
.x(-oldX) // 修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题
|
||||
.x(textContentOffsetX)
|
||||
.y((textContentHeight - this._textData.height) / 2)
|
||||
// 如果开启了文本编辑实时渲染,需要判断当前渲染的节点是否是正在编辑的节点,是的话将透明度设置为0不显示
|
||||
if (openRealtimeRenderOnNodeTextEdit) {
|
||||
this._textData.node.opacity(
|
||||
this.mindMap.renderer.textEdit.getCurrentEditNode() === this ? 0 : 1
|
||||
)
|
||||
}
|
||||
textContentNested.add(this._textData.node)
|
||||
textContentOffsetX += this._textData.width + textContentItemMargin
|
||||
}
|
||||
@@ -582,12 +648,15 @@ class MindMapNode {
|
||||
this.active(e)
|
||||
})
|
||||
this.group.on('mousedown', e => {
|
||||
e.preventDefault()
|
||||
const {
|
||||
readonly,
|
||||
enableCtrlKeyNodeSelection,
|
||||
useLeftKeySelectionRightKeyDrag
|
||||
useLeftKeySelectionRightKeyDrag,
|
||||
mousedownEventPreventDefault
|
||||
} = this.mindMap.opt
|
||||
if (mousedownEventPreventDefault) {
|
||||
e.preventDefault()
|
||||
}
|
||||
// 只读模式不需要阻止冒泡
|
||||
if (!readonly) {
|
||||
if (this.isRoot) {
|
||||
@@ -605,7 +674,7 @@ class MindMapNode {
|
||||
// 多选和取消多选
|
||||
if (!readonly && (e.ctrlKey || e.metaKey) && enableCtrlKeyNodeSelection) {
|
||||
this.isMultipleChoice = true
|
||||
let isActive = this.getData('isActive')
|
||||
const isActive = this.getData('isActive')
|
||||
if (!isActive)
|
||||
this.mindMap.emit(
|
||||
'before_node_active',
|
||||
@@ -724,7 +793,7 @@ class MindMapNode {
|
||||
this.renderExpandBtn()
|
||||
}
|
||||
} else {
|
||||
let { isActive, expand } = this.getData()
|
||||
const { isActive, expand } = this.getData()
|
||||
// 展开状态且非激活状态,且当前鼠标不在它上面,才隐藏
|
||||
if (childrenLength <= 0) {
|
||||
this.removeExpandBtn()
|
||||
@@ -735,23 +804,28 @@ class MindMapNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新拖拽手柄的显示与否
|
||||
this.updateDragHandle()
|
||||
// 更新概要
|
||||
this.renderGeneralization(forceRender)
|
||||
// 更新协同头像
|
||||
if (this.updateUserListNode) this.updateUserListNode()
|
||||
// 更新节点位置
|
||||
let t = this.group.transform()
|
||||
// 如果节点位置没有变化,则返回
|
||||
if (this.left === t.translateX && this.top === t.translateY) return
|
||||
this.group.translate(this.left - t.translateX, this.top - t.translateY)
|
||||
const t = this.group.transform()
|
||||
// 保存一份当前节点数据快照
|
||||
this.nodeDataSnapshot = JSON.stringify(this.getData())
|
||||
// 节点位置变化才更新,因为即使值没有变化属性设置操作也是耗时的
|
||||
if (this.left !== t.translateX || this.top !== t.translateY) {
|
||||
this.group.translate(this.left - t.translateX, this.top - t.translateY)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点相当于画布的位置
|
||||
getNodePosInClient(_left, _top) {
|
||||
let drawTransform = this.mindMap.draw.transform()
|
||||
let { scaleX, scaleY, translateX, translateY } = drawTransform
|
||||
let left = _left * scaleX + translateX
|
||||
let top = _top * scaleY + translateY
|
||||
const drawTransform = this.mindMap.draw.transform()
|
||||
const { scaleX, scaleY, translateX, translateY } = drawTransform
|
||||
const left = _left * scaleX + translateX
|
||||
const top = _top * scaleY + translateY
|
||||
return {
|
||||
left,
|
||||
top
|
||||
@@ -770,8 +844,8 @@ class MindMapNode {
|
||||
}
|
||||
|
||||
// 重新渲染节点,即重新创建节点内容、计算节点大小、计算节点内容布局、更新展开收起按钮,概要及位置
|
||||
reRender() {
|
||||
let sizeChange = this.getSize()
|
||||
reRender(recreateTypes, opt) {
|
||||
const sizeChange = this.getSize(recreateTypes, opt)
|
||||
this.layout()
|
||||
this.update()
|
||||
return sizeChange
|
||||
@@ -794,6 +868,7 @@ class MindMapNode {
|
||||
this.hideExpandBtn()
|
||||
}
|
||||
this.updateNodeActiveClass()
|
||||
this.updateDragHandle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -917,10 +992,10 @@ class MindMapNode {
|
||||
|
||||
// 隐藏节点
|
||||
hide() {
|
||||
this.group.hide()
|
||||
if (this.group) this.group.hide()
|
||||
this.hideGeneralization()
|
||||
if (this.parent) {
|
||||
let index = this.parent.children.indexOf(this)
|
||||
const index = this.parent.children.indexOf(this)
|
||||
this.parent._lines[index] && this.parent._lines[index].hide()
|
||||
this._lines.forEach(item => {
|
||||
item.hide()
|
||||
@@ -942,7 +1017,7 @@ class MindMapNode {
|
||||
this.group.show()
|
||||
this.showGeneralization()
|
||||
if (this.parent) {
|
||||
let index = this.parent.children.indexOf(this)
|
||||
const index = this.parent.children.indexOf(this)
|
||||
this.parent._lines[index] && this.parent._lines[index].show()
|
||||
this._lines.forEach(item => {
|
||||
item.show()
|
||||
@@ -960,7 +1035,7 @@ class MindMapNode {
|
||||
// 包括连接线和下级节点
|
||||
setOpacity(val) {
|
||||
// 自身及连线
|
||||
this.group.opacity(val)
|
||||
if (this.group) this.group.opacity(val)
|
||||
this._lines.forEach(line => {
|
||||
line.opacity(val)
|
||||
})
|
||||
@@ -999,13 +1074,13 @@ class MindMapNode {
|
||||
// 被拖拽中
|
||||
startDrag() {
|
||||
this.isDrag = true
|
||||
this.group.addClass('smm-node-dragging')
|
||||
if (this.group) this.group.addClass('smm-node-dragging')
|
||||
}
|
||||
|
||||
// 拖拽结束
|
||||
endDrag() {
|
||||
this.isDrag = false
|
||||
this.group.removeClass('smm-node-dragging')
|
||||
if (this.group) this.group.removeClass('smm-node-dragging')
|
||||
}
|
||||
|
||||
// 连线
|
||||
@@ -1180,16 +1255,15 @@ 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')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取某个样式
|
||||
getStyle(prop, root) {
|
||||
let v = this.style.merge(prop, root)
|
||||
const v = this.style.merge(prop, root)
|
||||
return v === undefined ? '' : v
|
||||
}
|
||||
|
||||
@@ -1249,16 +1323,16 @@ class MindMapNode {
|
||||
|
||||
// 获取节点的尺寸和位置信息,宽高是应用了缩放效果后的实际宽高,位置是相对于浏览器窗口左上角的位置
|
||||
getRect() {
|
||||
return this.group.rbox()
|
||||
return this.group ? this.group.rbox() : null
|
||||
}
|
||||
|
||||
// 获取节点的尺寸和位置信息,宽高是应用了缩放效果后的实际宽高,位置信息相对于画布
|
||||
getRectInSvg() {
|
||||
let { scaleX, scaleY, translateX, translateY } =
|
||||
const { scaleX, scaleY, translateX, translateY } =
|
||||
this.mindMap.draw.transform()
|
||||
let { left, top, width, height } = this
|
||||
let right = (left + width) * scaleX + translateX
|
||||
let bottom = (top + height) * scaleY + translateY
|
||||
const right = (left + width) * scaleX + translateX
|
||||
const bottom = (top + height) * scaleY + translateY
|
||||
left = left * scaleX + translateX
|
||||
top = top * scaleY + translateY
|
||||
return {
|
||||
@@ -1298,6 +1372,39 @@ class MindMapNode {
|
||||
createSvgTextNode(text = '') {
|
||||
return new Text().text(text)
|
||||
}
|
||||
|
||||
// 获取SVG.js库的一些对象
|
||||
getSvgObjects() {
|
||||
return {
|
||||
SVG,
|
||||
G,
|
||||
Rect
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否支持拖拽调整宽度
|
||||
// 1.富文本模式
|
||||
// 2.自定义节点内容
|
||||
checkEnableDragModifyNodeWidth() {
|
||||
const {
|
||||
enableDragModifyNodeWidth,
|
||||
isUseCustomNodeContent,
|
||||
customCreateNodeContent
|
||||
} = this.mindMap.opt
|
||||
return (
|
||||
enableDragModifyNodeWidth &&
|
||||
(this.mindMap.richText ||
|
||||
(isUseCustomNodeContent && customCreateNodeContent))
|
||||
)
|
||||
}
|
||||
|
||||
// 是否存在自定义宽度
|
||||
hasCustomWidth() {
|
||||
return (
|
||||
this.checkEnableDragModifyNodeWidth() &&
|
||||
this.customTextWidth !== undefined
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MindMapNode
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { checkIsNodeStyleDataKey } from '../../../utils/index'
|
||||
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
const backgroundStyleProps = [
|
||||
'backgroundColor',
|
||||
'backgroundImage',
|
||||
@@ -13,6 +12,7 @@ const backgroundStyleProps = [
|
||||
class Style {
|
||||
// 设置背景样式
|
||||
static setBackgroundStyle(el, themeConfig) {
|
||||
if (!el) return
|
||||
// 缓存容器元素原本的样式
|
||||
if (!Style.cacheStyle) {
|
||||
Style.cacheStyle = {}
|
||||
@@ -62,11 +62,10 @@ class Style {
|
||||
// 合并样式
|
||||
merge(prop, root) {
|
||||
let themeConfig = this.ctx.mindMap.themeConfig
|
||||
// 三级及以下节点
|
||||
let defaultConfig = themeConfig.node
|
||||
let defaultConfig = null
|
||||
let useRoot = false
|
||||
if (root || rootProp.includes(prop)) {
|
||||
// 直接使用最外层样式
|
||||
if (root) {
|
||||
// 使用最外层样式
|
||||
useRoot = true
|
||||
defaultConfig = themeConfig
|
||||
} else if (this.ctx.isGeneralization) {
|
||||
@@ -78,12 +77,21 @@ class Style {
|
||||
} else if (this.ctx.layerIndex === 1) {
|
||||
// 二级节点
|
||||
defaultConfig = themeConfig.second
|
||||
} else {
|
||||
// 三级及以下节点
|
||||
defaultConfig = themeConfig.node
|
||||
}
|
||||
let value = ''
|
||||
// 优先使用节点本身的样式
|
||||
const value =
|
||||
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
|
||||
@@ -176,7 +184,7 @@ class Style {
|
||||
})
|
||||
.css({
|
||||
'font-family': styles.fontFamily,
|
||||
'font-size': styles.fontSize,
|
||||
'font-size': styles.fontSize + 'px',
|
||||
'font-weight': styles.fontWeight,
|
||||
'font-style': styles.fontStyle,
|
||||
'text-decoration': styles.textDecoration
|
||||
@@ -184,14 +192,15 @@ class Style {
|
||||
}
|
||||
|
||||
// 生成内联样式
|
||||
createStyleText() {
|
||||
createStyleText(customStyle = {}) {
|
||||
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')
|
||||
textDecoration: this.merge('textDecoration'),
|
||||
...customStyle
|
||||
}
|
||||
return `
|
||||
color: ${styles.color};
|
||||
@@ -222,20 +231,20 @@ class Style {
|
||||
}
|
||||
|
||||
// html文字节点
|
||||
domText(node, fontSizeScale = 1, isMultiLine) {
|
||||
domText(node, fontSizeScale = 1) {
|
||||
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')
|
||||
textDecoration: this.merge('textDecoration')
|
||||
}
|
||||
node.style.color = styles.color
|
||||
node.style.textDecoration = styles.textDecoration
|
||||
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
|
||||
}
|
||||
|
||||
@@ -269,6 +278,10 @@ class Style {
|
||||
|
||||
// 连线
|
||||
line(line, { width, color, dasharray } = {}, enableMarker, childNode) {
|
||||
const { customHandleLine } = this.ctx.mindMap.opt
|
||||
if (typeof customHandleLine === 'function') {
|
||||
customHandleLine(this.ctx, line, { width, color, dasharray })
|
||||
}
|
||||
line.stroke({ color, dasharray, width }).fill({ color: 'none' })
|
||||
// 可以显示箭头
|
||||
if (enableMarker) {
|
||||
@@ -331,7 +344,7 @@ class Style {
|
||||
node2.fill({ color: color })
|
||||
fillNode.fill({ color: fill })
|
||||
if (this.ctx.mindMap.opt.isShowExpandNum) {
|
||||
node.attr({ 'font-size': fontSize, 'font-color': fontColor })
|
||||
node.attr({ 'font-size': fontSize + 'px', 'font-color': fontColor })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,10 +359,23 @@ class Style {
|
||||
return res
|
||||
}
|
||||
|
||||
// 获取自定义的样式
|
||||
getCustomStyle() {
|
||||
const customStyle = {}
|
||||
Object.keys(this.ctx.getData()).forEach(item => {
|
||||
if (checkIsNodeStyleDataKey(item)) {
|
||||
customStyle[item] = this.ctx.getData(item)
|
||||
}
|
||||
})
|
||||
return customStyle
|
||||
}
|
||||
|
||||
// hover和激活节点
|
||||
hoverNode(node) {
|
||||
const hoverRectColor = this.merge('hoverRectColor') || this.ctx.mindMap.opt.hoverRectColor
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ function createTextAvatar(item) {
|
||||
color: '#fff'
|
||||
})
|
||||
.css({
|
||||
'font-size': fontSize
|
||||
'font-size': fontSize + 'px'
|
||||
})
|
||||
.dx(-fontSize / 2)
|
||||
.dy((avatarSize - fontSize) / 2)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
measureText,
|
||||
resizeImgSize,
|
||||
removeHtmlStyle,
|
||||
addHtmlStyle,
|
||||
@@ -11,7 +10,19 @@ import {
|
||||
} from '../../../utils'
|
||||
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
||||
import iconsSvg from '../../../svg/icons'
|
||||
import { CONSTANTS } from '../../../constants/constant'
|
||||
import {
|
||||
CONSTANTS,
|
||||
noneRichTextNodeLineHeight
|
||||
} from '../../../constants/constant'
|
||||
|
||||
// 测量svg文本宽高
|
||||
const measureText = (text, style) => {
|
||||
const g = new G()
|
||||
const node = new Text().text(text)
|
||||
style.text(node)
|
||||
g.add(node)
|
||||
return g.bbox()
|
||||
}
|
||||
|
||||
// 标签默认的样式
|
||||
const defaultTagStyle = {
|
||||
@@ -113,11 +124,27 @@ function createIconNode() {
|
||||
})
|
||||
}
|
||||
|
||||
// 尝试给html指定标签添加内联样式
|
||||
function tryAddHtmlStyle(text, style) {
|
||||
const tagList = ['span', 'strong', 's', 'em', 'u']
|
||||
// let _text = text
|
||||
// for (let i = 0; i < tagList.length; i++) {
|
||||
// text = addHtmlStyle(text, tagList[i], style)
|
||||
// if (text !== _text) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return text
|
||||
return addHtmlStyle(text, tagList, style)
|
||||
}
|
||||
|
||||
// 创建富文本节点
|
||||
function createRichTextNode(specifyText) {
|
||||
const hasCustomWidth = this.hasCustomWidth()
|
||||
let text =
|
||||
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||
const { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
||||
let { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
||||
textAutoWrapWidth = hasCustomWidth ? this.customTextWidth : textAutoWrapWidth
|
||||
let g = new G()
|
||||
// 重新设置富文本节点内容
|
||||
let recoverText = false
|
||||
@@ -127,25 +154,22 @@ function createRichTextNode(specifyText) {
|
||||
}
|
||||
if ([CONSTANTS.CHANGE_THEME].includes(this.mindMap.renderer.renderSource)) {
|
||||
// 如果自定义过样式则不允许覆盖
|
||||
if (!this.hasCustomStyle()) {
|
||||
recoverText = true
|
||||
}
|
||||
// if (!this.hasCustomStyle() ) {
|
||||
recoverText = true
|
||||
// }
|
||||
}
|
||||
if (recoverText && !isUndef(text)) {
|
||||
// 判断节点内容是否是富文本
|
||||
let isRichText = checkIsRichText(text)
|
||||
const isRichText = checkIsRichText(text)
|
||||
// 获取自定义样式
|
||||
const customStyle = this.style.getCustomStyle()
|
||||
// 样式字符串
|
||||
let style = this.style.createStyleText()
|
||||
const style = this.style.createStyleText(customStyle)
|
||||
if (isRichText) {
|
||||
// 如果是富文本那么线移除内联样式
|
||||
text = removeHtmlStyle(text)
|
||||
// 再添加新的内联样式
|
||||
let _text = text
|
||||
text = addHtmlStyle(text, 'span', style)
|
||||
// 给span添加样式没有成功,则尝试给strong标签添加样式
|
||||
if (text === _text) {
|
||||
text = addHtmlStyle(text, 'strong', style)
|
||||
}
|
||||
text = this.tryAddHtmlStyle(text, style)
|
||||
} else {
|
||||
// 非富文本
|
||||
text = `<p><span style="${style}">${text}</span></p>`
|
||||
@@ -172,6 +196,11 @@ function createRichTextNode(specifyText) {
|
||||
el.classList.add('smm-richtext-node-wrap')
|
||||
addXmlns(el)
|
||||
el.style.maxWidth = textAutoWrapWidth + 'px'
|
||||
if (hasCustomWidth) {
|
||||
el.style.width = this.customTextWidth + 'px'
|
||||
} else {
|
||||
el.style.width = ''
|
||||
}
|
||||
let { width, height } = el.getBoundingClientRect()
|
||||
// 如果文本为空,那么需要计算一个默认高度
|
||||
if (height <= 0) {
|
||||
@@ -211,16 +240,14 @@ function createTextNode(specifyText) {
|
||||
}
|
||||
let g = new G()
|
||||
let fontSize = this.getStyle('fontSize', false)
|
||||
let lineHeight = this.getStyle('lineHeight', false)
|
||||
// 文本超长自动换行
|
||||
let textStyle = this.style.getTextFontStyle()
|
||||
let textArr = []
|
||||
if (!isUndef(text)) {
|
||||
textArr = String(text).split(/\n/gim)
|
||||
}
|
||||
const { textAutoWrapWidth: maxWidth, emptyTextMeasureHeightText } =
|
||||
this.mindMap.opt
|
||||
let isMultiLine = false
|
||||
let isMultiLine = textArr.length > 1
|
||||
textArr.forEach((item, index) => {
|
||||
let arr = item.split('')
|
||||
let lines = []
|
||||
@@ -228,7 +255,7 @@ function createTextNode(specifyText) {
|
||||
while (arr.length) {
|
||||
let str = arr.shift()
|
||||
let text = [...line, str].join('')
|
||||
if (measureText(text, textStyle).width <= maxWidth) {
|
||||
if (measureText(text, this.style).width <= maxWidth) {
|
||||
line.push(str)
|
||||
} else {
|
||||
lines.push(line.join(''))
|
||||
@@ -243,11 +270,20 @@ function createTextNode(specifyText) {
|
||||
}
|
||||
textArr[index] = lines.join('\n')
|
||||
})
|
||||
textArr = textArr.join('\n').split(/\n/gim)
|
||||
textArr = textArr.join('\n').replace(/\n$/g, '').split(/\n/gim)
|
||||
textArr.forEach((item, index) => {
|
||||
let node = new Text().text(item)
|
||||
// 避免尾部的空行不占宽度
|
||||
// 同时解决该问题:https://github.com/wanglin2/mind-map/issues/1037
|
||||
if (item === '') {
|
||||
item = ''
|
||||
}
|
||||
const node = new Text().text(item)
|
||||
node.addClass('smm-text-node-wrap')
|
||||
this.style.text(node)
|
||||
node.y(fontSize * lineHeight * index)
|
||||
node.y(
|
||||
fontSize * noneRichTextNodeLineHeight * index +
|
||||
((noneRichTextNodeLineHeight - 1) * fontSize) / 2
|
||||
)
|
||||
g.add(node)
|
||||
})
|
||||
let { width, height } = g.bbox()
|
||||
@@ -434,6 +470,9 @@ function createNoteNode() {
|
||||
node.on('click', e => {
|
||||
this.mindMap.emit('node_note_click', this, e, node)
|
||||
})
|
||||
node.on('dblclick', e => {
|
||||
this.mindMap.emit('node_note_dblclick', this, e, node)
|
||||
})
|
||||
return {
|
||||
node,
|
||||
width: iconSize,
|
||||
@@ -517,6 +556,7 @@ export default {
|
||||
createImgNode,
|
||||
getImgShowSize,
|
||||
createIconNode,
|
||||
tryAddHtmlStyle,
|
||||
createRichTextNode,
|
||||
createTextNode,
|
||||
createHyperlinkNode,
|
||||
|
||||
153
simple-mind-map/src/core/render/node/nodeModifyWidth.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import { Rect } from '@svgdotjs/svg.js'
|
||||
|
||||
// 初始化拖拽
|
||||
function initDragHandle() {
|
||||
if (!this.checkEnableDragModifyNodeWidth()) {
|
||||
return
|
||||
}
|
||||
// 拖拽手柄元素
|
||||
this._dragHandleNodes = null
|
||||
// 手柄元素的宽度
|
||||
this.dragHandleWidth = 4
|
||||
// 鼠标按下时的x坐标
|
||||
this.dragHandleMousedownX = 0
|
||||
// 鼠标是否处于按下状态
|
||||
this.isDragHandleMousedown = false
|
||||
// 当前拖拽的手柄序号
|
||||
this.dragHandleIndex = 0
|
||||
// 鼠标按下时记录当前的customTextWidth值
|
||||
this.dragHandleMousedownCustomTextWidth = 0
|
||||
// 鼠标按下时记录当前的手型样式
|
||||
this.dragHandleMousedownBodyCursor = ''
|
||||
// 鼠标按下时记录当前节点的left值
|
||||
this.dragHandleMousedownLeft = 0
|
||||
|
||||
this.onDragMousemoveHandle = this.onDragMousemoveHandle.bind(this)
|
||||
window.addEventListener('mousemove', this.onDragMousemoveHandle)
|
||||
this.onDragMouseupHandle = this.onDragMouseupHandle.bind(this)
|
||||
window.addEventListener('mouseup', this.onDragMouseupHandle)
|
||||
this.mindMap.on('node_mouseup', this.onDragMouseupHandle)
|
||||
}
|
||||
|
||||
// 鼠标移动事件
|
||||
function onDragMousemoveHandle(e) {
|
||||
if (!this.isDragHandleMousedown) return
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let {
|
||||
minNodeTextModifyWidth,
|
||||
maxNodeTextModifyWidth,
|
||||
isUseCustomNodeContent,
|
||||
customCreateNodeContent
|
||||
} = this.mindMap.opt
|
||||
const useCustomContent =
|
||||
isUseCustomNodeContent && customCreateNodeContent && this._customNodeContent
|
||||
document.body.style.cursor = 'ew-resize'
|
||||
this.group.css({
|
||||
cursor: 'ew-resize'
|
||||
})
|
||||
const { scaleX } = this.mindMap.draw.transform()
|
||||
const ox = e.clientX - this.dragHandleMousedownX
|
||||
let newWidth =
|
||||
this.dragHandleMousedownCustomTextWidth +
|
||||
(this.dragHandleIndex === 0 ? -ox : ox) / scaleX
|
||||
newWidth = Math.max(newWidth, minNodeTextModifyWidth)
|
||||
if (maxNodeTextModifyWidth !== -1) {
|
||||
newWidth = Math.min(newWidth, maxNodeTextModifyWidth)
|
||||
}
|
||||
// 如果存在图片,那么最小值需要考虑图片宽度
|
||||
if (!useCustomContent && this.getData('image')) {
|
||||
const imgSize = this.getImgShowSize()
|
||||
if (
|
||||
this._rectInfo.textContentWidth - this.customTextWidth + newWidth <=
|
||||
imgSize[0]
|
||||
) {
|
||||
newWidth =
|
||||
imgSize[0] + this.customTextWidth - this._rectInfo.textContentWidth
|
||||
}
|
||||
}
|
||||
this.customTextWidth = newWidth
|
||||
if (this.dragHandleIndex === 0) {
|
||||
this.left = this.dragHandleMousedownLeft + ox / scaleX
|
||||
}
|
||||
// 自定义内容不重新渲染,交给开发者
|
||||
this.reRender(useCustomContent ? [] : ['text'], {
|
||||
ignoreUpdateCustomTextWidth: true
|
||||
})
|
||||
}
|
||||
|
||||
// 鼠标松开事件
|
||||
function onDragMouseupHandle() {
|
||||
if (!this.isDragHandleMousedown) return
|
||||
document.body.style.cursor = this.dragHandleMousedownBodyCursor
|
||||
this.group.css({
|
||||
cursor: 'default'
|
||||
})
|
||||
this.isDragHandleMousedown = false
|
||||
this.dragHandleMousedownX = 0
|
||||
this.dragHandleIndex = 0
|
||||
this.dragHandleMousedownCustomTextWidth = 0
|
||||
this.setData({
|
||||
customTextWidth: this.customTextWidth
|
||||
})
|
||||
this.mindMap.render()
|
||||
this.mindMap.emit('dragModifyNodeWidthEnd', this)
|
||||
}
|
||||
|
||||
// 插件拖拽手柄元素
|
||||
function createDragHandleNode() {
|
||||
const list = [new Rect(), new Rect()]
|
||||
list.forEach((node, index) => {
|
||||
node
|
||||
.size(this.dragHandleWidth, this.height)
|
||||
.fill({
|
||||
color: 'transparent'
|
||||
})
|
||||
.css({
|
||||
cursor: 'ew-resize'
|
||||
})
|
||||
node.on('mousedown', e => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
this.dragHandleMousedownX = e.clientX
|
||||
this.dragHandleIndex = index
|
||||
this.dragHandleMousedownCustomTextWidth =
|
||||
this.customTextWidth === undefined
|
||||
? this._textData
|
||||
? this._textData.width
|
||||
: this.width
|
||||
: this.customTextWidth
|
||||
this.dragHandleMousedownBodyCursor = document.body.style.cursor
|
||||
this.dragHandleMousedownLeft = this.left
|
||||
this.isDragHandleMousedown = true
|
||||
})
|
||||
})
|
||||
return list
|
||||
}
|
||||
|
||||
// 更新拖拽按钮的显隐和位置尺寸
|
||||
function updateDragHandle() {
|
||||
if (!this.checkEnableDragModifyNodeWidth()) return
|
||||
if (!this._dragHandleNodes) {
|
||||
this._dragHandleNodes = this.createDragHandleNode()
|
||||
}
|
||||
if (this.getData('isActive')) {
|
||||
this._dragHandleNodes.forEach(node => {
|
||||
node.height(this.height)
|
||||
this.group.add(node)
|
||||
})
|
||||
this._dragHandleNodes[1].x(this.width - this.dragHandleWidth)
|
||||
} else {
|
||||
this._dragHandleNodes.forEach(node => {
|
||||
node.remove()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
initDragHandle,
|
||||
onDragMousemoveHandle,
|
||||
onDragMouseupHandle,
|
||||
createDragHandleNode,
|
||||
updateDragHandle
|
||||
}
|
||||
@@ -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 (
|
||||
@@ -138,7 +142,7 @@ class View {
|
||||
if (dirs.includes(CONSTANTS.DIR.RIGHT)) {
|
||||
mx = -stepX
|
||||
}
|
||||
this.translateXY(mx, my)
|
||||
this.translateXY(mx * translateRatio, my * translateRatio)
|
||||
}
|
||||
})
|
||||
this.mindMap.on('resize', () => {
|
||||
@@ -246,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')
|
||||
@@ -255,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')
|
||||
@@ -343,6 +354,10 @@ class View {
|
||||
|
||||
// 判断是否需要将思维导图限制在画布内
|
||||
checkNeedMindMapInCanvas() {
|
||||
// 如果当前在演示模式,那么不需要限制
|
||||
if (this.mindMap.demonstrate && this.mindMap.demonstrate.isInDemonstrate) {
|
||||
return false
|
||||
}
|
||||
const { isLimitMindMapInCanvasWhenHasScrollbar, isLimitMindMapInCanvas } =
|
||||
this.mindMap.opt
|
||||
// 如果注册了滚动条插件,那么使用isLimitMindMapInCanvasWhenHasScrollbar配置
|
||||
|
||||
@@ -69,43 +69,41 @@ class Base {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点编号信息
|
||||
getNumberInfo({ parent, ancestors, layerIndex, index }) {
|
||||
// 编号
|
||||
const hasNumberPlugin = !!this.mindMap.numbers
|
||||
const parentNumberStr =
|
||||
hasNumberPlugin && parent && parent._node.number
|
||||
? parent._node.number
|
||||
: ''
|
||||
const newNumberStr = hasNumberPlugin
|
||||
? this.mindMap.numbers.getNodeNumberStr({
|
||||
ancestors,
|
||||
layerIndex,
|
||||
num: index + 1,
|
||||
parentNumberStr
|
||||
})
|
||||
: ''
|
||||
return {
|
||||
hasNumberPlugin,
|
||||
newNumberStr
|
||||
// 节点节点数据是否发生了改变
|
||||
checkIsNodeDataChange(lastData, curData) {
|
||||
if (lastData) {
|
||||
// 对比忽略激活状态和展开收起状态
|
||||
lastData = typeof lastData === 'string' ? JSON.parse(lastData) : lastData
|
||||
lastData.isActive = curData.isActive
|
||||
lastData.expand = curData.expand
|
||||
lastData = JSON.stringify(lastData)
|
||||
}
|
||||
return lastData !== JSON.stringify(curData)
|
||||
}
|
||||
|
||||
// 创建节点实例
|
||||
createNode(data, parent, isRoot, layerIndex, index, ancestors) {
|
||||
// 编号
|
||||
const { hasNumberPlugin, newNumberStr } = this.getNumberInfo({
|
||||
parent,
|
||||
ancestors,
|
||||
layerIndex,
|
||||
index
|
||||
})
|
||||
// 创建节点
|
||||
// 库前置内容数据
|
||||
const nodeInnerPrefixData = {}
|
||||
this.mindMap.nodeInnerPrefixList.forEach(item => {
|
||||
if (item.createNodeData) {
|
||||
const [key, value] = item.createNodeData({
|
||||
data,
|
||||
parent,
|
||||
ancestors,
|
||||
layerIndex,
|
||||
index
|
||||
})
|
||||
nodeInnerPrefixData[key] = value
|
||||
}
|
||||
})
|
||||
const uid = data.data.uid
|
||||
let newNode = null
|
||||
// 数据上保存了节点引用,那么直接复用节点
|
||||
if (data && data._node && !this.renderer.reRender) {
|
||||
newNode = data._node
|
||||
// 节点层级改变了
|
||||
const isLayerTypeChange = this.checkIsLayerTypeChange(
|
||||
newNode.layerIndex,
|
||||
layerIndex
|
||||
@@ -119,26 +117,35 @@ class Base {
|
||||
}
|
||||
this.cacheNode(data._node.uid, newNode)
|
||||
this.checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(newNode)
|
||||
// 判断编号是否改变
|
||||
let isNumberChange = false
|
||||
if (hasNumberPlugin) {
|
||||
isNumberChange = this.mindMap.numbers.updateNumber(
|
||||
newNode,
|
||||
newNumberStr
|
||||
)
|
||||
}
|
||||
// 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本等情况需要重新计算节点大小和布局
|
||||
const isNeedResizeSources = this.checkIsNeedResizeSources()
|
||||
// 库前置内容是否改变了
|
||||
let isNodeInnerPrefixChange = false
|
||||
this.mindMap.nodeInnerPrefixList.forEach(item => {
|
||||
if (item.updateNodeData) {
|
||||
const isChange = item.updateNodeData(newNode, nodeInnerPrefixData)
|
||||
if (isChange) {
|
||||
isNodeInnerPrefixChange = isChange
|
||||
}
|
||||
}
|
||||
})
|
||||
// 主题或主题配置改变了
|
||||
const isResizeSource = this.checkIsNeedResizeSources()
|
||||
// 节点数据改变了
|
||||
const isNodeDataChange = this.checkIsNodeDataChange(
|
||||
data._node.nodeDataSnapshot,
|
||||
data.data
|
||||
)
|
||||
// 重新计算节点大小和布局
|
||||
if (
|
||||
isNeedResizeSources ||
|
||||
isResizeSource ||
|
||||
isNodeDataChange ||
|
||||
isLayerTypeChange ||
|
||||
newNode.getData('resetRichText') ||
|
||||
isNumberChange
|
||||
isNodeInnerPrefixChange
|
||||
) {
|
||||
newNode.getSize()
|
||||
newNode.needLayout = true
|
||||
}
|
||||
this.checkGetGeneralizationChange(newNode, isNeedResizeSources)
|
||||
this.checkGetGeneralizationChange(newNode, isResizeSource)
|
||||
} else if (
|
||||
(this.lru.has(uid) || this.renderer.lastNodeCache[uid]) &&
|
||||
!this.renderer.reRender
|
||||
@@ -150,6 +157,7 @@ class Base {
|
||||
newNode = this.lru.get(uid) || this.renderer.lastNodeCache[uid]
|
||||
// 保存该节点上一次的数据
|
||||
const lastData = JSON.stringify(newNode.getData())
|
||||
// 节点层级改变了
|
||||
const isLayerTypeChange = this.checkIsLayerTypeChange(
|
||||
newNode.layerIndex,
|
||||
layerIndex
|
||||
@@ -167,22 +175,25 @@ class Base {
|
||||
data._node = newNode
|
||||
// 主题或主题配置改变了需要重新计算节点大小和布局
|
||||
const isResizeSource = this.checkIsNeedResizeSources()
|
||||
// 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本,节点数据改变了等情况需要重新计算节点大小和布局
|
||||
const isNodeDataChange = lastData !== JSON.stringify(data.data)
|
||||
// 判断编号是否改变
|
||||
let isNumberChange = false
|
||||
if (hasNumberPlugin) {
|
||||
isNumberChange = this.mindMap.numbers.updateNumber(
|
||||
newNode,
|
||||
newNumberStr
|
||||
)
|
||||
}
|
||||
// 点数据改变了
|
||||
const isNodeDataChange = this.checkIsNodeDataChange(lastData, data.data)
|
||||
// 库前置内容是否改变了
|
||||
let isNodeInnerPrefixChange = false
|
||||
this.mindMap.nodeInnerPrefixList.forEach(item => {
|
||||
if (item.updateNodeData) {
|
||||
const isChange = item.updateNodeData(newNode, nodeInnerPrefixData)
|
||||
if (isChange) {
|
||||
isNodeInnerPrefixChange = isChange
|
||||
}
|
||||
}
|
||||
})
|
||||
// 重新计算节点大小和布局
|
||||
if (
|
||||
isResizeSource ||
|
||||
isNodeDataChange ||
|
||||
isLayerTypeChange ||
|
||||
newNode.getData('resetRichText') ||
|
||||
isNumberChange
|
||||
isNodeInnerPrefixChange
|
||||
) {
|
||||
newNode.getSize()
|
||||
newNode.needLayout = true
|
||||
@@ -200,7 +211,7 @@ class Base {
|
||||
layerIndex,
|
||||
isRoot,
|
||||
parent: !isRoot ? parent._node : null,
|
||||
number: newNumberStr
|
||||
...nodeInnerPrefixData
|
||||
})
|
||||
// uid保存到数据上,为了节点复用
|
||||
data.data.uid = newUid
|
||||
@@ -244,7 +255,9 @@ class Base {
|
||||
isResizeSource ||
|
||||
(newData && JSON.stringify(oldData) !== JSON.stringify(newData))
|
||||
) {
|
||||
gNode.nodeData.data = newData
|
||||
if (newData) {
|
||||
gNode.nodeData.data = newData
|
||||
}
|
||||
gNode.getSize()
|
||||
gNode.needLayout = true
|
||||
}
|
||||
@@ -439,8 +452,10 @@ class Base {
|
||||
const end = list[len - 1]
|
||||
// 如果三点在一条直线,那么不用处理
|
||||
const isOneLine =
|
||||
(start[0] === center[0] && center[0] === end[0]) ||
|
||||
(start[1] === center[1] && center[1] === end[1])
|
||||
(start[0].toFixed(0) === center[0].toFixed(0) &&
|
||||
center[0].toFixed(0) === end[0].toFixed(0)) ||
|
||||
(start[1].toFixed(0) === center[1].toFixed(0) &&
|
||||
center[1].toFixed(0) === end[1].toFixed(0))
|
||||
if (!isOneLine) {
|
||||
const cStart = this.computeNewPoint(start, center, lineRadius)
|
||||
const cEnd = this.computeNewPoint(end, center, lineRadius)
|
||||
|
||||
@@ -35,7 +35,14 @@ class MindMap 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)
|
||||
@@ -47,9 +54,10 @@ class MindMap extends Base {
|
||||
} else {
|
||||
// 节点生长方向
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
newNode.getData('dir') ||
|
||||
(index % 2 === 0
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.RIGHT
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.LEFT)
|
||||
}
|
||||
// 根据生长方向定位到父节点的左侧或右侧
|
||||
newNode.left =
|
||||
|
||||
@@ -11,11 +11,25 @@ import {
|
||||
import associativeLineControlsMethods from './associativeLine/associativeLineControls'
|
||||
import associativeLineTextMethods from './associativeLine/associativeLineText'
|
||||
|
||||
const styleProps = [
|
||||
'associativeLineWidth',
|
||||
'associativeLineColor',
|
||||
'associativeLineActiveWidth',
|
||||
'associativeLineActiveColor',
|
||||
'associativeLineDasharray',
|
||||
'associativeLineTextColor',
|
||||
'associativeLineTextFontSize',
|
||||
'associativeLineTextLineHeight',
|
||||
'associativeLineTextFontFamily'
|
||||
]
|
||||
|
||||
// 关联线插件
|
||||
class AssociativeLine {
|
||||
constructor(opt = {}) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.associativeLineDraw = this.mindMap.associativeLineDraw
|
||||
// 本次不要重新渲染连线
|
||||
this.isNotRenderAllLines = false
|
||||
// 当前所有连接线
|
||||
this.lineList = []
|
||||
// 当前激活的连接线
|
||||
@@ -27,9 +41,6 @@ class AssociativeLine {
|
||||
this.overlapNode = null // 创建过程中的目标节点
|
||||
// 是否有节点正在被拖拽
|
||||
this.isNodeDragging = false
|
||||
// 箭头图标
|
||||
this.markerPath = null
|
||||
this.marker = this.createMarker()
|
||||
// 控制点
|
||||
this.controlLine1 = null
|
||||
this.controlLine2 = null
|
||||
@@ -112,6 +123,25 @@ class AssociativeLine {
|
||||
this.mindMap.off('beforeDestroy', this.onBeforeDestroy)
|
||||
}
|
||||
|
||||
// 获取关联线的样式配置
|
||||
// 优先级:关联线自定义样式、节点自定义样式、主题的节点层级样式、主题的最外层样式
|
||||
getStyleConfig(node, toNode) {
|
||||
let lineStyle = {}
|
||||
if (toNode) {
|
||||
const associativeLineStyle = node.getData('associativeLineStyle') || {}
|
||||
lineStyle = associativeLineStyle[toNode.getData('uid')] || {}
|
||||
}
|
||||
const res = {}
|
||||
styleProps.forEach(prop => {
|
||||
if (typeof lineStyle[prop] !== 'undefined') {
|
||||
res[prop] = lineStyle[prop]
|
||||
} else {
|
||||
res[prop] = node.getStyle(prop)
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// 实例销毁时清除关联线文字编辑框
|
||||
onBeforeDestroy() {
|
||||
this.hideEditTextBox()
|
||||
@@ -140,12 +170,12 @@ class AssociativeLine {
|
||||
}
|
||||
|
||||
// 创建箭头
|
||||
createMarker() {
|
||||
createMarker(callback = () => {}) {
|
||||
return this.associativeLineDraw.marker(20, 20, add => {
|
||||
add.ref(12, 5)
|
||||
add.size(10, 10)
|
||||
add.attr('orient', 'auto-start-reverse')
|
||||
this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z')
|
||||
callback(add.path('M0,0 L2,5 L0,10 L10,5 Z'))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -172,6 +202,10 @@ class AssociativeLine {
|
||||
|
||||
// 渲染所有连线
|
||||
renderAllLines() {
|
||||
if (this.isNotRenderAllLines) {
|
||||
this.isNotRenderAllLines = false
|
||||
return
|
||||
}
|
||||
// 先移除
|
||||
this.removeAllLines()
|
||||
this.removeControls()
|
||||
@@ -223,11 +257,14 @@ class AssociativeLine {
|
||||
associativeLineWidth,
|
||||
associativeLineColor,
|
||||
associativeLineActiveWidth,
|
||||
associativeLineActiveColor,
|
||||
associativeLineDasharray
|
||||
} = this.mindMap.themeConfig
|
||||
} = this.getStyleConfig(node, toNode)
|
||||
// 箭头
|
||||
this.markerPath
|
||||
let markerPath = null
|
||||
const marker = this.createMarker(p => {
|
||||
markerPath = p
|
||||
})
|
||||
markerPath
|
||||
.stroke({ color: associativeLineColor })
|
||||
.fill({ color: associativeLineColor })
|
||||
// 路径
|
||||
@@ -247,7 +284,7 @@ class AssociativeLine {
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
path.plot(pathStr)
|
||||
path.marker('end', this.marker)
|
||||
path.marker('end', marker)
|
||||
// 不可见的点击线
|
||||
let clickPath = this.associativeLineDraw.path()
|
||||
clickPath
|
||||
@@ -258,6 +295,7 @@ class AssociativeLine {
|
||||
let text = this.createText({
|
||||
path,
|
||||
clickPath,
|
||||
markerPath,
|
||||
node,
|
||||
toNode,
|
||||
startPoint,
|
||||
@@ -270,6 +308,7 @@ class AssociativeLine {
|
||||
this.setActiveLine({
|
||||
path,
|
||||
clickPath,
|
||||
markerPath,
|
||||
text,
|
||||
node,
|
||||
toNode,
|
||||
@@ -284,14 +323,72 @@ class AssociativeLine {
|
||||
this.showEditTextBox(text)
|
||||
})
|
||||
// 渲染关联线文字
|
||||
this.renderText(this.getText(node, toNode), path, text)
|
||||
this.renderText(this.getText(node, toNode), path, text, node, toNode)
|
||||
this.lineList.push([path, clickPath, text, node, toNode])
|
||||
}
|
||||
|
||||
// 更新当前激活连线的样式,一般在自定义了节点关联线的样式后调用
|
||||
// 直接调用node.setStyle方法更新样式会直接触发关联线更新,但是关联线的激活状态会丢失
|
||||
// 所以可以调用node.setData方法更新数据,然后再调用该方法更新样式,这样关联线激活状态不会丢失
|
||||
updateActiveLineStyle() {
|
||||
if (!this.activeLine) return
|
||||
this.isNotRenderAllLines = true
|
||||
const [path, clickPath, text, node, toNode, markerPath] = this.activeLine
|
||||
const {
|
||||
associativeLineWidth,
|
||||
associativeLineColor,
|
||||
associativeLineDasharray,
|
||||
associativeLineActiveWidth,
|
||||
associativeLineActiveColor,
|
||||
associativeLineTextColor,
|
||||
associativeLineTextFontFamily,
|
||||
associativeLineTextFontSize
|
||||
} = this.getStyleConfig(node, toNode)
|
||||
path
|
||||
.stroke({
|
||||
width: associativeLineWidth,
|
||||
color: associativeLineColor,
|
||||
dasharray: associativeLineDasharray || [6, 4]
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
clickPath
|
||||
.stroke({
|
||||
width: associativeLineActiveWidth,
|
||||
color: associativeLineActiveColor
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
markerPath
|
||||
.stroke({ color: associativeLineColor })
|
||||
.fill({ color: associativeLineColor })
|
||||
text.find('text').forEach(textNode => {
|
||||
textNode
|
||||
.fill({
|
||||
color: associativeLineTextColor
|
||||
})
|
||||
.css({
|
||||
'font-family': associativeLineTextFontFamily,
|
||||
'font-size': associativeLineTextFontSize + 'px'
|
||||
})
|
||||
})
|
||||
if (this.controlLine1) {
|
||||
this.controlLine1.stroke({ color: associativeLineActiveColor })
|
||||
}
|
||||
if (this.controlLine2) {
|
||||
this.controlLine2.stroke({ color: associativeLineActiveColor })
|
||||
}
|
||||
if (this.controlPoint1) {
|
||||
this.controlPoint1.stroke({ color: associativeLineActiveColor })
|
||||
}
|
||||
if (this.controlPoint2) {
|
||||
this.controlPoint2.stroke({ color: associativeLineActiveColor })
|
||||
}
|
||||
}
|
||||
|
||||
// 激活某根关联线
|
||||
setActiveLine({
|
||||
path,
|
||||
clickPath,
|
||||
markerPath,
|
||||
text,
|
||||
node,
|
||||
toNode,
|
||||
@@ -299,25 +396,33 @@ class AssociativeLine {
|
||||
endPoint,
|
||||
controlPoints
|
||||
}) {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
let { associativeLineActiveColor } = this.getStyleConfig(node, toNode)
|
||||
// 如果当前存在激活节点,那么取消激活节点
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
// 否则清除当前的关联线的激活状态,如果有的话
|
||||
this.clearActiveLine()
|
||||
// 保存当前激活的关联线信息
|
||||
this.activeLine = [path, clickPath, text, node, toNode]
|
||||
this.activeLine = [path, clickPath, text, node, toNode, markerPath]
|
||||
// 让不可见的点击线显示
|
||||
clickPath.stroke({ color: associativeLineActiveColor })
|
||||
// 如果没有输入过关联线文字,那么显示默认文字
|
||||
if (!this.getText(node, toNode)) {
|
||||
this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text)
|
||||
this.renderText(
|
||||
this.mindMap.opt.defaultAssociativeLineText,
|
||||
path,
|
||||
text,
|
||||
node,
|
||||
toNode
|
||||
)
|
||||
}
|
||||
// 渲染控制点和连线
|
||||
this.renderControls(
|
||||
startPoint,
|
||||
endPoint,
|
||||
controlPoints[0],
|
||||
controlPoints[1]
|
||||
controlPoints[1],
|
||||
node,
|
||||
toNode
|
||||
)
|
||||
this.mindMap.emit('associative_line_click', path, clickPath, node, toNode)
|
||||
this.front()
|
||||
@@ -346,7 +451,7 @@ class AssociativeLine {
|
||||
associativeLineWidth,
|
||||
associativeLineColor,
|
||||
associativeLineDasharray
|
||||
} = this.mindMap.themeConfig
|
||||
} = this.getStyleConfig(fromNode)
|
||||
if (this.isCreatingLine || !fromNode) return
|
||||
this.front()
|
||||
this.isCreatingLine = true
|
||||
@@ -360,10 +465,14 @@ class AssociativeLine {
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
// 箭头
|
||||
this.markerPath
|
||||
let markerPath = null
|
||||
const marker = this.createMarker(p => {
|
||||
markerPath = p
|
||||
})
|
||||
markerPath
|
||||
.stroke({ color: associativeLineColor })
|
||||
.fill({ color: associativeLineColor })
|
||||
this.creatingLine.marker('end', this.marker)
|
||||
this.creatingLine.marker('end', marker)
|
||||
}
|
||||
|
||||
// 取消创建关联线
|
||||
@@ -446,6 +555,12 @@ class AssociativeLine {
|
||||
// 完成创建连接线
|
||||
completeCreateLine(node) {
|
||||
if (this.creatingStartNode.uid === node.uid) return
|
||||
const { beforeAssociativeLineConnection } = this.mindMap.opt
|
||||
let stop = false
|
||||
if (typeof beforeAssociativeLineConnection === 'function') {
|
||||
stop = beforeAssociativeLineConnection(node)
|
||||
}
|
||||
if (stop) return
|
||||
this.addLine(this.creatingStartNode, node)
|
||||
if (this.overlapNode && this.overlapNode.getData('isActive')) {
|
||||
this.mindMap.execCommand('SET_NODE_ACTIVE', this.overlapNode, false)
|
||||
@@ -523,7 +638,8 @@ class AssociativeLine {
|
||||
associativeLineTargets,
|
||||
associativeLinePoint,
|
||||
associativeLineTargetControlOffsets,
|
||||
associativeLineText
|
||||
associativeLineText,
|
||||
associativeLineStyle
|
||||
} = node.getData()
|
||||
associativeLinePoint = associativeLinePoint || []
|
||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||
@@ -536,6 +652,15 @@ class AssociativeLine {
|
||||
}
|
||||
})
|
||||
}
|
||||
// 更新关联线样式数据
|
||||
let newAssociativeLineStyle = {}
|
||||
if (associativeLineStyle) {
|
||||
Object.keys(associativeLineStyle).forEach(item => {
|
||||
if (item !== toNode.getData('uid')) {
|
||||
newAssociativeLineStyle[item] = associativeLineStyle[item]
|
||||
}
|
||||
})
|
||||
}
|
||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||
// 目标
|
||||
associativeLineTargets: associativeLineTargets.filter((_, index) => {
|
||||
@@ -552,7 +677,9 @@ class AssociativeLine {
|
||||
})
|
||||
: [],
|
||||
// 文本
|
||||
associativeLineText: newAssociativeLineText
|
||||
associativeLineText: newAssociativeLineText,
|
||||
// 样式
|
||||
associativeLineStyle: newAssociativeLineStyle
|
||||
})
|
||||
}
|
||||
|
||||
@@ -572,6 +699,7 @@ class AssociativeLine {
|
||||
this.activeLine = null
|
||||
this.removeControls()
|
||||
this.back()
|
||||
this.mindMap.emit('associative_line_deactivate')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ class Drag extends Base {
|
||||
) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
this.isMousedown = true
|
||||
// 记录鼠标按下时的节点
|
||||
this.mousedownNode = node
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -3,6 +3,9 @@ import Quill from 'quill'
|
||||
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 {
|
||||
@@ -168,11 +185,13 @@ class Formula {
|
||||
// 插件被移除前做的事情
|
||||
beforePluginRemove() {
|
||||
this.removeStyle()
|
||||
this.mindMap.off('beforeDestroy', this.onDestroy)
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.removeStyle()
|
||||
this.mindMap.off('beforeDestroy', this.onDestroy)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
117
simple-mind-map/src/plugins/MindMapLayoutPro.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import { CONSTANTS } from '../constants/constant'
|
||||
|
||||
// 该插件会向节点数据的data中添加dir字段
|
||||
/*
|
||||
需要更新数据的情况:
|
||||
|
||||
1.实例化时的数据
|
||||
2.调用setData和updateData方法
|
||||
3.执行完命令
|
||||
4.切换结构
|
||||
*/
|
||||
|
||||
class MindMapLayoutPro {
|
||||
constructor(opt) {
|
||||
this.opt = opt
|
||||
this.mindMap = opt.mindMap
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.updateNodeTree = this.updateNodeTree.bind(this)
|
||||
this.afterExecCommand = this.afterExecCommand.bind(this)
|
||||
this.layoutChange = this.layoutChange.bind(this)
|
||||
|
||||
// 处理实例化时传入的数据
|
||||
if (this.mindMap.opt.data && this.isMindMapLayout()) {
|
||||
this.updateNodeTree(this.mindMap.opt.data)
|
||||
}
|
||||
|
||||
this.mindMap.on('layout_change', this.layoutChange)
|
||||
this.mindMap.on('afterExecCommand', this.afterExecCommand)
|
||||
this.mindMap.on('before_update_data', this.updateNodeTree)
|
||||
this.mindMap.on('before_set_data', this.updateNodeTree)
|
||||
}
|
||||
|
||||
restore() {
|
||||
this.mindMap.off('layout_change', this.layoutChange)
|
||||
this.mindMap.off('afterExecCommand', this.afterExecCommand)
|
||||
this.mindMap.off('before_update_data', this.updateNodeTree)
|
||||
this.mindMap.off('before_set_data', this.updateNodeTree)
|
||||
}
|
||||
|
||||
// 监听命令执行后的事件
|
||||
afterExecCommand(name) {
|
||||
if (!this.isMindMapLayout()) return
|
||||
if (
|
||||
![
|
||||
'BACK',
|
||||
'FORWARD',
|
||||
'INSERT_NODE',
|
||||
'INSERT_MULTI_NODE',
|
||||
'INSERT_CHILD_NODE',
|
||||
'INSERT_MULTI_CHILD_NODE',
|
||||
'INSERT_PARENT_NODE',
|
||||
'UP_NODE',
|
||||
'DOWN_NODE',
|
||||
'MOVE_UP_ONE_LEVEL',
|
||||
'INSERT_AFTER',
|
||||
'INSERT_BEFORE',
|
||||
'MOVE_NODE_TO',
|
||||
'REMOVE_NODE',
|
||||
'REMOVE_CURRENT_NODE',
|
||||
'PASTE_NODE',
|
||||
'CUT_NODE'
|
||||
].includes(name)
|
||||
)
|
||||
return
|
||||
this.updateRenderTree()
|
||||
}
|
||||
|
||||
// 更新布局结构
|
||||
layoutChange(layout) {
|
||||
if (layout === CONSTANTS.LAYOUT.MIND_MAP) {
|
||||
this.updateRenderTree()
|
||||
}
|
||||
}
|
||||
|
||||
// 更新当前的渲染树
|
||||
updateRenderTree() {
|
||||
this.updateNodeTree(this.mindMap.renderer.renderTree)
|
||||
}
|
||||
|
||||
// 更新节点树,修改二级节点的排列位置
|
||||
updateNodeTree(tree) {
|
||||
if (!this.isMindMapLayout()) return
|
||||
const root = tree
|
||||
const childrenLength = root.children.length
|
||||
if (childrenLength <= 0) return
|
||||
const center = Math.ceil(childrenLength / 2)
|
||||
root.children.forEach((item, index) => {
|
||||
if (index + 1 <= center) {
|
||||
item.data.dir = CONSTANTS.LAYOUT_GROW_DIR.RIGHT
|
||||
} else {
|
||||
item.data.dir = CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 判断当前是否是思维导图布局结构
|
||||
isMindMapLayout() {
|
||||
return this.mindMap.opt.layout === CONSTANTS.LAYOUT.MIND_MAP
|
||||
}
|
||||
|
||||
// 插件被移除前做的事情
|
||||
beforePluginRemove() {
|
||||
this.restore()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.restore()
|
||||
}
|
||||
}
|
||||
|
||||
MindMapLayoutPro.instanceName = 'mindMapLayoutPro'
|
||||
|
||||
export default MindMapLayoutPro
|
||||
@@ -6,13 +6,18 @@ class NodeImgAdjust {
|
||||
// 构造函数
|
||||
constructor({ mindMap }) {
|
||||
this.mindMap = mindMap
|
||||
this.resizeBtnSize = 26 // 调整按钮的大小
|
||||
this.handleEl = null // 自定义元素,用来渲染临时图片、调整按钮
|
||||
this.isShowHandleEl = false // 自定义元素是否在显示中
|
||||
this.node = null // 当前节点实例
|
||||
this.img = null // 当前节点的图片节点
|
||||
this.rect = null // 当前图片节点的尺寸信息
|
||||
this.isMousedown = false // 当前是否是按住调整按钮状态
|
||||
this.mousedownDrawTransform = null //鼠标按下时对当前画布的变换
|
||||
this.mousedownOffset = {
|
||||
// 鼠标按下时位置和图片右下角相差的距离
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
this.currentImgWidth = 0 // 当前拖拽实时图片的大小
|
||||
this.currentImgHeight = 0
|
||||
this.isAdjusted = false // 是否是拖拽结束后的渲染期间
|
||||
@@ -78,6 +83,7 @@ class NodeImgAdjust {
|
||||
|
||||
// 显示自定义元素
|
||||
showHandleEl() {
|
||||
if (this.isShowHandleEl) return
|
||||
if (!this.handleEl) {
|
||||
this.createResizeBtnEl()
|
||||
}
|
||||
@@ -116,6 +122,7 @@ class NodeImgAdjust {
|
||||
|
||||
// 创建调整按钮元素
|
||||
createResizeBtnEl() {
|
||||
const { imgResizeBtnSize } = this.mindMap.opt
|
||||
// 容器元素
|
||||
this.handleEl = document.createElement('div')
|
||||
this.handleEl.style.cssText = `
|
||||
@@ -134,8 +141,8 @@ class NodeImgAdjust {
|
||||
bottom: 0;
|
||||
pointer-events: auto;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: ${this.resizeBtnSize}px;
|
||||
height: ${this.resizeBtnSize}px;
|
||||
width: ${imgResizeBtnSize}px;
|
||||
height: ${imgResizeBtnSize}px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -178,8 +185,8 @@ class NodeImgAdjust {
|
||||
right: 0;top:0;color:#fff;
|
||||
pointer-events: auto;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
width: ${this.resizeBtnSize}px;
|
||||
height: ${this.resizeBtnSize}px;
|
||||
width: ${imgResizeBtnSize}px;
|
||||
height: ${imgResizeBtnSize}px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -207,10 +214,13 @@ class NodeImgAdjust {
|
||||
}
|
||||
|
||||
// 鼠标按钮按下事件
|
||||
onMousedown() {
|
||||
onMousedown(e) {
|
||||
this.isMousedown = true
|
||||
this.mousedownDrawTransform = this.mindMap.draw.transform()
|
||||
// 隐藏节点实际图片
|
||||
this.hideNodeImage()
|
||||
this.mousedownOffset.x = e.clientX - this.rect.x2
|
||||
this.mousedownOffset.y = e.clientY - this.rect.y2
|
||||
// 将节点图片渲染到自定义元素上
|
||||
this.handleEl.style.backgroundImage = `url(${this.node.getData('image')})`
|
||||
}
|
||||
@@ -219,13 +229,48 @@ class NodeImgAdjust {
|
||||
onMousemove(e) {
|
||||
if (!this.isMousedown) return
|
||||
e.preventDefault()
|
||||
// 计算当前拖拽位置对应的图片的实时大小
|
||||
let { width: imageOriginWidth, height: imageOriginHeight } =
|
||||
const { scaleX, scaleY } = this.mousedownDrawTransform
|
||||
// 图片原始大小
|
||||
const { width: imageOriginWidth, height: imageOriginHeight } =
|
||||
this.node.getData('imageSize')
|
||||
let newWidth = e.clientX - this.rect.x
|
||||
let newHeight = e.clientY - this.rect.y
|
||||
if (newWidth <= 0 || newHeight <= 0) return
|
||||
let [actWidth, actHeight] = resizeImgSizeByOriginRatio(
|
||||
let {
|
||||
minImgResizeWidth,
|
||||
minImgResizeHeight,
|
||||
maxImgResizeWidthInheritTheme,
|
||||
maxImgResizeWidth,
|
||||
maxImgResizeHeight
|
||||
} = this.mindMap.opt
|
||||
// 主题设置的最小图片宽高
|
||||
const minRatio = minImgResizeWidth / minImgResizeHeight
|
||||
const oRatio = imageOriginWidth / imageOriginHeight
|
||||
if (minRatio > oRatio) {
|
||||
// 如果最小值比例大于图片原始比例,那么要调整高度最小值
|
||||
minImgResizeHeight = minImgResizeWidth / oRatio
|
||||
} else {
|
||||
// 否则调整宽度最小值
|
||||
minImgResizeWidth = minImgResizeHeight * oRatio
|
||||
}
|
||||
// 主题设置的最大图片宽高
|
||||
let imgMaxWidth, imgMaxHeight
|
||||
if (maxImgResizeWidthInheritTheme) {
|
||||
imgMaxWidth = this.mindMap.getThemeConfig('imgMaxWidth')
|
||||
imgMaxHeight = this.mindMap.getThemeConfig('imgMaxHeight')
|
||||
} else {
|
||||
imgMaxWidth = maxImgResizeWidth
|
||||
imgMaxHeight = maxImgResizeHeight
|
||||
}
|
||||
imgMaxWidth = imgMaxWidth * scaleX
|
||||
imgMaxHeight = imgMaxHeight * scaleY
|
||||
// 计算当前拖拽位置对应的图片的实时大小
|
||||
let newWidth = Math.abs(e.clientX - this.rect.x - this.mousedownOffset.x)
|
||||
let newHeight = Math.abs(e.clientY - this.rect.y - this.mousedownOffset.y)
|
||||
// 限制最小值
|
||||
if (newWidth < minImgResizeWidth) newWidth = minImgResizeWidth
|
||||
if (newHeight < minImgResizeHeight) newHeight = minImgResizeHeight
|
||||
// 限制最大值
|
||||
if (newWidth > imgMaxWidth) newWidth = imgMaxWidth
|
||||
if (newHeight > imgMaxHeight) newHeight = imgMaxHeight
|
||||
const [actWidth, actHeight] = resizeImgSizeByOriginRatio(
|
||||
imageOriginWidth,
|
||||
imageOriginHeight,
|
||||
newWidth,
|
||||
@@ -244,17 +289,27 @@ class NodeImgAdjust {
|
||||
// 隐藏自定义元素
|
||||
this.hideHandleEl()
|
||||
// 更新节点图片为新的大小
|
||||
let { image, imageTitle } = this.node.getData()
|
||||
let { scaleX, scaleY } = this.mindMap.draw.transform()
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, {
|
||||
url: image,
|
||||
title: imageTitle,
|
||||
width: this.currentImgWidth / scaleX,
|
||||
height: this.currentImgHeight / scaleY,
|
||||
custom: true // 代表自定义了图片大小
|
||||
})
|
||||
this.isAdjusted = true
|
||||
const { image, imageTitle } = this.node.getData()
|
||||
const { scaleX, scaleY } = this.mousedownDrawTransform
|
||||
const newWidth = this.currentImgWidth / scaleX
|
||||
const newHeight = this.currentImgHeight / scaleY
|
||||
if (
|
||||
Math.abs(newWidth - this.rect.width) > 1 ||
|
||||
Math.abs(newHeight - this.rect.height) > 1
|
||||
) {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, {
|
||||
url: image,
|
||||
title: imageTitle,
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
custom: true // 代表自定义了图片大小
|
||||
})
|
||||
this.isAdjusted = true
|
||||
}
|
||||
this.isMousedown = false
|
||||
this.mousedownDrawTransform = null
|
||||
this.mousedownOffset.x = 0
|
||||
this.mousedownOffset.y = 0
|
||||
}
|
||||
|
||||
// 渲染完成事件
|
||||
|
||||
@@ -4,8 +4,6 @@ import 'quill/dist/quill.snow.css'
|
||||
import {
|
||||
walk,
|
||||
getTextFromHtml,
|
||||
isWhite,
|
||||
getVisibleColorFromTheme,
|
||||
isUndef,
|
||||
checkSmmFormatData,
|
||||
removeHtmlNodeByClass,
|
||||
@@ -59,6 +57,14 @@ class RichText {
|
||||
this.isCompositing = false
|
||||
this.textNodePaddingX = 6
|
||||
this.textNodePaddingY = 4
|
||||
this.supportStyleProps = [
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textDecoration',
|
||||
'color'
|
||||
]
|
||||
this.initOpt()
|
||||
this.extendQuill()
|
||||
this.appendCss()
|
||||
@@ -75,9 +81,12 @@ class RichText {
|
||||
this.onCompositionStart = this.onCompositionStart.bind(this)
|
||||
this.onCompositionUpdate = this.onCompositionUpdate.bind(this)
|
||||
this.onCompositionEnd = this.onCompositionEnd.bind(this)
|
||||
this.handleSetData = this.handleSetData.bind(this)
|
||||
window.addEventListener('compositionstart', this.onCompositionStart)
|
||||
window.addEventListener('compositionupdate', this.onCompositionUpdate)
|
||||
window.addEventListener('compositionend', this.onCompositionEnd)
|
||||
this.mindMap.on('before_update_data', this.handleSetData)
|
||||
this.mindMap.on('before_set_data', this.handleSetData)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
@@ -85,12 +94,27 @@ class RichText {
|
||||
window.removeEventListener('compositionstart', this.onCompositionStart)
|
||||
window.removeEventListener('compositionupdate', this.onCompositionUpdate)
|
||||
window.removeEventListener('compositionend', this.onCompositionEnd)
|
||||
this.mindMap.off('before_update_data', this.handleSetData)
|
||||
this.mindMap.off('before_set_data', this.handleSetData)
|
||||
}
|
||||
|
||||
// 插入样式
|
||||
appendCss() {
|
||||
this.mindMap.appendCss(
|
||||
'richText',
|
||||
`
|
||||
.smm-richtext-node-wrap {
|
||||
word-break: break-all;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.smm-richtext-node-wrap p {
|
||||
font-family: auto;
|
||||
}
|
||||
`
|
||||
)
|
||||
let cssText = `
|
||||
.ql-editor {
|
||||
.${CONSTANTS.EDIT_NODE_CLASS.RICH_TEXT_EDIT_WRAP} {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
@@ -107,15 +131,6 @@ class RichText {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.smm-richtext-node-wrap {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.smm-richtext-node-wrap p {
|
||||
font-family: auto;
|
||||
|
||||
}
|
||||
|
||||
.smm-richtext-node-edit-wrap p {
|
||||
font-family: auto;
|
||||
}
|
||||
@@ -180,14 +195,18 @@ class RichText {
|
||||
if (this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
const {
|
||||
richTextEditFakeInPlace,
|
||||
let {
|
||||
customInnerElsAppendTo,
|
||||
nodeTextEditZIndex,
|
||||
textAutoWrapWidth,
|
||||
selectTextOnEnterEditText,
|
||||
transformRichTextOnEnterEdit
|
||||
transformRichTextOnEnterEdit,
|
||||
openRealtimeRenderOnNodeTextEdit,
|
||||
autoEmptyTextWhenKeydownEnterEdit
|
||||
} = this.mindMap.opt
|
||||
textAutoWrapWidth = node.hasCustomWidth()
|
||||
? node.customTextWidth
|
||||
: textAutoWrapWidth
|
||||
this.node = node
|
||||
this.isInserting = isInserting
|
||||
if (!rect) rect = node._textData.node.node.getBoundingClientRect()
|
||||
@@ -200,26 +219,24 @@ class RichText {
|
||||
let originWidth = g.attr('data-width')
|
||||
let originHeight = g.attr('data-height')
|
||||
// 缩放值
|
||||
let scaleX = rect.width / originWidth
|
||||
let scaleY = rect.height / originHeight
|
||||
const scaleX = Math.ceil(rect.width) / originWidth
|
||||
const scaleY = Math.ceil(rect.height) / originHeight
|
||||
// 内边距
|
||||
let paddingX = this.textNodePaddingX
|
||||
let paddingY = this.textNodePaddingY
|
||||
if (richTextEditFakeInPlace) {
|
||||
let paddingValue = node.getPaddingVale()
|
||||
paddingX = paddingValue.paddingX
|
||||
paddingY = paddingValue.paddingY
|
||||
}
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.classList.add('smm-richtext-node-edit-wrap')
|
||||
this.textEditNode.style.cssText = `
|
||||
position:fixed;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,.5);
|
||||
outline: none;
|
||||
word-break:
|
||||
break-all;
|
||||
position:fixed;
|
||||
box-sizing: border-box;
|
||||
${
|
||||
openRealtimeRenderOnNodeTextEdit
|
||||
? ''
|
||||
: 'box-shadow: 0 0 20px rgba(0,0,0,.5);'
|
||||
}
|
||||
outline: none;
|
||||
word-break: break-all;
|
||||
padding: ${paddingY}px ${paddingX}px;
|
||||
`
|
||||
this.textEditNode.addEventListener('click', e => {
|
||||
@@ -239,7 +256,10 @@ class RichText {
|
||||
this.textEditNode.style.marginLeft = `-${paddingX * scaleX}px`
|
||||
this.textEditNode.style.marginTop = `-${paddingY * scaleY}px`
|
||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||
this.textEditNode.style.background = this.getBackground(node)
|
||||
if (!openRealtimeRenderOnNodeTextEdit) {
|
||||
this.textEditNode.style.background =
|
||||
this.mindMap.renderer.textEdit.getBackground(node)
|
||||
}
|
||||
this.textEditNode.style.minWidth = originWidth + paddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight = originHeight + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
@@ -248,13 +268,6 @@ class RichText {
|
||||
this.textEditNode.style.maxWidth = textAutoWrapWidth + paddingX * 2 + 'px'
|
||||
this.textEditNode.style.transform = `scale(${scaleX}, ${scaleY})`
|
||||
this.textEditNode.style.transformOrigin = 'left top'
|
||||
if (richTextEditFakeInPlace) {
|
||||
this.textEditNode.style.borderRadius =
|
||||
(node.style.merge('borderRadius') || 5) + 'px'
|
||||
if (node.style.merge('shape') == 'roundedRectangle') {
|
||||
this.textEditNode.style.borderRadius = (node.height || 50) + 'px'
|
||||
}
|
||||
}
|
||||
// 节点文本内容
|
||||
let nodeText = node.getData('text')
|
||||
if (typeof transformRichTextOnEnterEdit === 'function') {
|
||||
@@ -268,7 +281,10 @@ class RichText {
|
||||
if (isEmptyText) {
|
||||
this.lostStyle = true
|
||||
}
|
||||
if (noneEmptyNoneRichText) {
|
||||
if (isFromKeyDown && autoEmptyTextWhenKeydownEnterEdit) {
|
||||
this.textEditNode.innerHTML = ''
|
||||
this.lostStyle = true
|
||||
} else if (noneEmptyNoneRichText) {
|
||||
// 还不是富文本
|
||||
let text = String(nodeText).split(/\n/gim).join('<br>')
|
||||
let html = `<p>${text}</p>`
|
||||
@@ -278,7 +294,9 @@ class RichText {
|
||||
this.textEditNode.innerHTML = this.cacheEditingText || nodeText
|
||||
}
|
||||
this.initQuillEditor()
|
||||
document.querySelector('.ql-editor').style.minHeight = originHeight + 'px'
|
||||
document.querySelector(
|
||||
'.' + CONSTANTS.EDIT_NODE_CLASS.RICH_TEXT_EDIT_WRAP
|
||||
).style.minHeight = originHeight + 'px'
|
||||
this.showTextEdit = true
|
||||
// 如果是刚创建的节点,那么默认全选,否则普通激活不全选,除非selectTextOnEnterEditText配置为true
|
||||
// 在selectTextOnEnterEditText时,如果是在keydown事件进入的节点编辑,也不需要全选
|
||||
@@ -292,11 +310,26 @@ class RichText {
|
||||
this.cacheEditingText = ''
|
||||
}
|
||||
|
||||
// 当openRealtimeRenderOnNodeTextEdit配置更新后需要更新编辑框样式
|
||||
onOpenRealtimeRenderOnNodeTextEditConfigUpdate(
|
||||
openRealtimeRenderOnNodeTextEdit
|
||||
) {
|
||||
if (!this.textEditNode) return
|
||||
this.textEditNode.style.background = openRealtimeRenderOnNodeTextEdit
|
||||
? 'transparent'
|
||||
: this.node
|
||||
? this.mindMap.renderer.textEdit.getBackground(this.node)
|
||||
: ''
|
||||
this.textEditNode.style.boxShadow = openRealtimeRenderOnNodeTextEdit
|
||||
? 'none'
|
||||
: '0 0 20px rgba(0,0,0,.5)'
|
||||
}
|
||||
|
||||
// 更新文本编辑框的大小和位置
|
||||
updateTextEditNode() {
|
||||
if (!this.node) return
|
||||
const rect = this.node._textData.node.node.getBoundingClientRect()
|
||||
const g = this.node._textData.node
|
||||
const rect = g.node.getBoundingClientRect()
|
||||
const originWidth = g.attr('data-width')
|
||||
const originHeight = g.attr('data-height')
|
||||
this.textEditNode.style.minWidth =
|
||||
@@ -313,27 +346,6 @@ class RichText {
|
||||
targetNode.removeChild(this.textEditNode)
|
||||
}
|
||||
|
||||
// 获取编辑区域的背景填充
|
||||
getBackground(node) {
|
||||
const gradientStyle = node.style.merge('gradientStyle')
|
||||
// 当前使用的是渐变色背景
|
||||
if (gradientStyle) {
|
||||
const startColor = node.style.merge('startColor')
|
||||
const endColor = node.style.merge('endColor')
|
||||
return `linear-gradient(to right, ${startColor}, ${endColor})`
|
||||
} else {
|
||||
// 单色背景
|
||||
const bgColor = node.style.merge('fillColor')
|
||||
const color = node.style.merge('color')
|
||||
// 默认使用节点的填充色,否则如果节点颜色是白色的话编辑时看不见
|
||||
return bgColor === 'transparent'
|
||||
? isWhite(color)
|
||||
? getVisibleColorFromTheme(this.mindMap.themeConfig)
|
||||
: '#fff'
|
||||
: bgColor
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
setTextStyleIfNotRichText(node) {
|
||||
let style = {
|
||||
@@ -350,13 +362,16 @@ class RichText {
|
||||
|
||||
// 获取当前正在编辑的内容
|
||||
getEditText() {
|
||||
let html = this.quill.container.firstChild.innerHTML
|
||||
// https://github.com/slab/quill/issues/4509
|
||||
return this.quill.container.firstChild.innerHTML.replaceAll(/ +/g, match =>
|
||||
' '.repeat(match.length)
|
||||
)
|
||||
// 去除ql-cursor节点
|
||||
// https://github.com/wanglin2/mind-map/commit/138cc4b3e824671143f0bf70e5c46796f48520d0
|
||||
// https://github.com/wanglin2/mind-map/commit/0760500cebe8ec4e8ad84ab63f877b8b2a193aa1
|
||||
// html = removeHtmlNodeByClass(html, '.ql-cursor')
|
||||
// 去除最后的空行
|
||||
return html.replace(/<p><br><\/p>$/, '')
|
||||
// return html.replace(/<p><br><\/p>$/, '')
|
||||
}
|
||||
|
||||
// 给html字符串中的节点样式按样式名首字母排序
|
||||
@@ -382,8 +397,13 @@ class RichText {
|
||||
}
|
||||
let html = this.getEditText()
|
||||
html = this.sortHtmlNodeStyles(html)
|
||||
let list =
|
||||
nodes && nodes.length > 0 ? nodes : this.mindMap.renderer.activeNodeList
|
||||
const list = nodes && nodes.length > 0 ? nodes : [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
|
||||
list.forEach(node => {
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', node, html, true)
|
||||
// if (node.isGeneralization) {
|
||||
@@ -392,12 +412,6 @@ class RichText {
|
||||
// }
|
||||
this.mindMap.render()
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -459,6 +473,17 @@ class RichText {
|
||||
}
|
||||
}
|
||||
},
|
||||
formats: [
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strike',
|
||||
'color',
|
||||
'background',
|
||||
'font',
|
||||
'size',
|
||||
'formula'
|
||||
], // 明确指定允许的格式,不包含有序列表,无序列表等
|
||||
theme: 'snow'
|
||||
})
|
||||
// 拦截复制事件,即Ctrl + c,去除多余的空行
|
||||
@@ -534,6 +559,7 @@ class RichText {
|
||||
// let style = this.getPasteTextStyle()
|
||||
// return new Delta().insert(this.formatPasteText(node.data), style)
|
||||
// })
|
||||
// 剪贴板里只要存在文本就会走这里,所以当剪贴板里是纯文本,或文本+图片都可以监听到和拦截,但是只有纯图片时不会走这里,所以无法拦截
|
||||
this.quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
|
||||
let ops = []
|
||||
let style = this.getPasteTextStyle()
|
||||
@@ -549,6 +575,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
|
||||
)
|
||||
}
|
||||
|
||||
// 获取粘贴的文本的样式
|
||||
@@ -636,14 +676,7 @@ class RichText {
|
||||
// 再将样式恢复为当前主题改节点的默认样式
|
||||
const style = {}
|
||||
if (this.node) {
|
||||
;[
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textDecoration',
|
||||
'color'
|
||||
].forEach(key => {
|
||||
this.supportStyleProps.forEach(key => {
|
||||
style[key] = this.node.style.merge(key)
|
||||
})
|
||||
}
|
||||
@@ -674,14 +707,7 @@ class RichText {
|
||||
if (!this.node) return
|
||||
if (clear) {
|
||||
// 清除文本样式
|
||||
;[
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textDecoration',
|
||||
'color'
|
||||
].forEach(prop => {
|
||||
this.supportStyleProps.forEach(prop => {
|
||||
delete this.node.nodeData.data[prop]
|
||||
})
|
||||
} else {
|
||||
@@ -756,6 +782,18 @@ class RichText {
|
||||
return data
|
||||
}
|
||||
|
||||
// 判断一个对象是否包含了富文本支持的样式字段
|
||||
isHasRichTextStyle(obj) {
|
||||
const keys = Object.keys(obj)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (this.supportStyleProps.includes(key)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 给未激活的节点设置富文本样式
|
||||
setNotActiveNodeStyle(node, style) {
|
||||
const config = this.normalStyleToRichTextStyle(style)
|
||||
@@ -768,17 +806,9 @@ class RichText {
|
||||
|
||||
// 检查指定节点是否存在自定义的富文本样式
|
||||
checkNodeHasCustomRichTextStyle(node) {
|
||||
const list = [
|
||||
'fontFamily',
|
||||
'fontSize',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'textDecoration',
|
||||
'color'
|
||||
]
|
||||
const nodeData = node instanceof MindMapNode ? node.getData() : node
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (nodeData[list[i]] !== undefined) {
|
||||
for (let i = 0; i < this.supportStyleProps.length; i++) {
|
||||
if (nodeData[this.supportStyleProps[i]] !== undefined) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -846,6 +876,7 @@ class RichText {
|
||||
this.transformAllNodesToNormalNode()
|
||||
document.head.removeChild(this.styleEl)
|
||||
this.unbindEvent()
|
||||
this.mindMap.removeAppendCss('richText')
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
|
||||
@@ -224,9 +224,15 @@ class Search {
|
||||
replaceText = String(replaceText)
|
||||
let currentNode = this.matchNodeList[this.currentIndex]
|
||||
if (!currentNode) return
|
||||
let text = this.getReplacedText(currentNode, this.searchText, replaceText)
|
||||
// 如果当前搜索文本是替换文本的子串,那么该节点还是符合搜索结果的
|
||||
const keep = replaceText.includes(this.searchText)
|
||||
const text = this.getReplacedText(currentNode, this.searchText, replaceText)
|
||||
this.notResetSearchText = true
|
||||
currentNode.setText(text, currentNode.getData('richText'), true)
|
||||
if (keep) {
|
||||
this.updateMatchNodeList(this.matchNodeList)
|
||||
return
|
||||
}
|
||||
const newList = this.matchNodeList.filter(node => {
|
||||
return currentNode !== node
|
||||
})
|
||||
@@ -249,25 +255,30 @@ class Search {
|
||||
)
|
||||
return
|
||||
replaceText = String(replaceText)
|
||||
// 如果当前搜索文本是替换文本的子串,那么该节点还是符合搜索结果的
|
||||
const keep = replaceText.includes(this.searchText)
|
||||
this.notResetSearchText = true
|
||||
const hasRichTextPlugin = this.mindMap.renderer.hasRichTextPlugin()
|
||||
this.matchNodeList.forEach(node => {
|
||||
const text = this.getReplacedText(node, this.searchText, replaceText)
|
||||
if (this.isNodeInstance(node)) {
|
||||
this.mindMap.renderer.setNodeDataRender(
|
||||
node,
|
||||
{
|
||||
text,
|
||||
resetRichText: !!node.getData('richText')
|
||||
},
|
||||
true
|
||||
)
|
||||
const data = {
|
||||
text
|
||||
}
|
||||
if (hasRichTextPlugin) data.resetRichText = !!node.getData('richText')
|
||||
this.mindMap.renderer.setNodeDataRender(node, data, true)
|
||||
} else {
|
||||
node.data.text = text
|
||||
node.data.resetRichText = !!node.data.richText
|
||||
if (hasRichTextPlugin) node.data.resetRichText = !!node.data.richText
|
||||
}
|
||||
})
|
||||
this.mindMap.render()
|
||||
this.mindMap.command.addHistory()
|
||||
this.endSearch()
|
||||
if (keep) {
|
||||
this.updateMatchNodeList(this.matchNodeList)
|
||||
} else {
|
||||
this.endSearch()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取某个节点替换后的文本
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
} from './associativeLineUtils'
|
||||
|
||||
// 创建控制点、连线节点
|
||||
function createControlNodes() {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
function createControlNodes(node, toNode) {
|
||||
let { associativeLineActiveColor } = this.getStyleConfig(node, toNode)
|
||||
// 连线
|
||||
this.controlLine1 = this.associativeLineDraw
|
||||
.line()
|
||||
@@ -16,13 +16,13 @@ function createControlNodes() {
|
||||
.line()
|
||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||
// 控制点
|
||||
this.controlPoint1 = this.createOneControlNode('controlPoint1')
|
||||
this.controlPoint2 = this.createOneControlNode('controlPoint2')
|
||||
this.controlPoint1 = this.createOneControlNode('controlPoint1', node, toNode)
|
||||
this.controlPoint2 = this.createOneControlNode('controlPoint2', node, toNode)
|
||||
}
|
||||
|
||||
// 创建控制点
|
||||
function createOneControlNode(pointKey) {
|
||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||
function createOneControlNode(pointKey, node, toNode) {
|
||||
let { associativeLineActiveColor } = this.getStyleConfig(node, toNode)
|
||||
return this.associativeLineDraw
|
||||
.circle(this.controlPointDiameter)
|
||||
.stroke({ color: associativeLineActiveColor })
|
||||
@@ -202,6 +202,7 @@ function onControlPointMouseup(e) {
|
||||
associativeLineTargetControlOffsets: offsetList,
|
||||
associativeLinePoint
|
||||
})
|
||||
this.isNotRenderAllLines = true
|
||||
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
||||
setTimeout(() => {
|
||||
this.resetControlPoint()
|
||||
@@ -221,10 +222,10 @@ function resetControlPoint() {
|
||||
}
|
||||
|
||||
// 渲染控制点
|
||||
function renderControls(startPoint, endPoint, point1, point2) {
|
||||
function renderControls(startPoint, endPoint, point1, point2, node, toNode) {
|
||||
if (!this.mindMap.opt.enableAdjustAssociativeLinePoints) return
|
||||
if (!this.controlLine1) {
|
||||
this.createControlNodes()
|
||||
this.createControlNodes(node, toNode)
|
||||
}
|
||||
let radius = this.controlPointDiameter / 2
|
||||
// 控制点和起终点的连线
|
||||
|
||||
@@ -43,6 +43,7 @@ function showEditTextBox(g) {
|
||||
// 输入框元素没有创建过,则先创建
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.className = 'associative-line-text-edit-warp'
|
||||
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.setAttribute('contenteditable', true)
|
||||
this.textEditNode.addEventListener('keyup', e => {
|
||||
@@ -54,14 +55,14 @@ function showEditTextBox(g) {
|
||||
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.textEditNode)
|
||||
}
|
||||
let [, , , node, toNode] = this.activeLine
|
||||
let {
|
||||
associativeLineTextFontSize,
|
||||
associativeLineTextFontFamily,
|
||||
associativeLineTextLineHeight
|
||||
} = this.mindMap.themeConfig
|
||||
} = this.getStyleConfig(node, toNode)
|
||||
let { defaultAssociativeLineText, nodeTextEditZIndex } = this.mindMap.opt
|
||||
let scale = this.mindMap.view.scale
|
||||
let [, , , node, toNode] = this.activeLine
|
||||
let text = this.getText(node, toNode)
|
||||
let textLines = (text || defaultAssociativeLineText).split(/\n/gim)
|
||||
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
|
||||
@@ -124,7 +125,7 @@ function hideEditTextBox() {
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.textEditNode.innerHTML = ''
|
||||
this.showTextEdit = false
|
||||
this.renderText(str, path, text)
|
||||
this.renderText(str, path, text, node, toNode)
|
||||
this.mindMap.emit('hide_text_edit')
|
||||
}
|
||||
|
||||
@@ -138,35 +139,41 @@ function getText(node, toNode) {
|
||||
}
|
||||
|
||||
// 渲染关联线文字
|
||||
function renderText(str, path, text) {
|
||||
function renderText(str, path, text, node, toNode) {
|
||||
if (!str) return
|
||||
let { associativeLineTextFontSize, associativeLineTextLineHeight } =
|
||||
this.mindMap.themeConfig
|
||||
this.getStyleConfig(node, toNode)
|
||||
text.clear()
|
||||
let textArr = str.split(/\n/gim)
|
||||
let textArr = str.replace(/\n$/g, '').split(/\n/gim)
|
||||
textArr.forEach((item, index) => {
|
||||
let node = new Text().text(item)
|
||||
node.y(associativeLineTextFontSize * associativeLineTextLineHeight * index)
|
||||
this.styleText(node)
|
||||
text.add(node)
|
||||
// 避免尾部的空行不占宽度,导致文本编辑框定位异常的问题
|
||||
if (item === '') {
|
||||
item = ''
|
||||
}
|
||||
let textNode = new Text().text(item)
|
||||
textNode.y(
|
||||
associativeLineTextFontSize * associativeLineTextLineHeight * index
|
||||
)
|
||||
this.styleText(textNode, node, toNode)
|
||||
text.add(textNode)
|
||||
})
|
||||
updateTextPos(path, text)
|
||||
}
|
||||
|
||||
// 给文本设置样式
|
||||
function styleText(node) {
|
||||
function styleText(textNode, node, toNode) {
|
||||
let {
|
||||
associativeLineTextColor,
|
||||
associativeLineTextFontSize,
|
||||
associativeLineTextFontFamily
|
||||
} = this.mindMap.themeConfig
|
||||
node
|
||||
} = this.getStyleConfig(node, toNode)
|
||||
textNode
|
||||
.fill({
|
||||
color: associativeLineTextColor
|
||||
})
|
||||
.css({
|
||||
'font-family': associativeLineTextFontFamily,
|
||||
'font-size': associativeLineTextFontSize
|
||||
'font-size': associativeLineTextFontSize + 'px'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// 默认主题
|
||||
|
||||
export default {
|
||||
// 节点内边距
|
||||
paddingX: 15,
|
||||
paddingY: 5,
|
||||
// 图片显示的最大宽度
|
||||
imgMaxWidth: 100,
|
||||
imgMaxWidth: 200,
|
||||
// 图片显示的最大高度
|
||||
imgMaxHeight: 100,
|
||||
// icon的大小
|
||||
@@ -16,6 +15,12 @@ export default {
|
||||
lineColor: '#549688',
|
||||
// 连线样式
|
||||
lineDasharray: 'none',
|
||||
// 连线是否开启流动效果,仅在虚线时有效(需要注册LineFlow插件)
|
||||
lineFlow: false,
|
||||
// 流动效果一个周期的时间,单位:s
|
||||
lineFlowDuration: 1,
|
||||
// 流动方向是否是从父节点到子节点
|
||||
lineFlowForward: true,
|
||||
// 连线风格
|
||||
lineStyle: 'straight', // 曲线(curve)【仅支持logicalStructure、mindMap、verticalTimeline三种结构】、直线(straight)、直连(direct)【仅支持logicalStructure、mindMap、organizationStructure、verticalTimeline四种结构】
|
||||
// 曲线连接时,根节点和其他节点的连接线样式保持统一,默认根节点为 ( 型,其他节点为 { 型,设为true后,都为 { 型。仅支持logicalStructure、mindMap两种结构
|
||||
@@ -73,7 +78,6 @@ export default {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderDasharray: 'none',
|
||||
@@ -87,7 +91,19 @@ export default {
|
||||
// 连线标记的位置,start(头部)、end(尾部),该配置在showLineMarker配置为true时生效
|
||||
lineMarkerDir: 'end',
|
||||
// 节点鼠标hover和激活时显示的矩形边框的颜色,主题里不设置,默认会取hoverRectColor实例化选项的值
|
||||
hoverRectColor: ''
|
||||
hoverRectColor: '',
|
||||
// 点鼠标hover和激活时显示的矩形边框的圆角大小
|
||||
hoverRectRadius: 5
|
||||
// 下列样式也支持给节点设置,用于覆盖最外层的设置
|
||||
// paddingX,
|
||||
// paddingY,
|
||||
// lineWidth,
|
||||
// lineColor,
|
||||
// lineDasharray,
|
||||
// lineFlow,
|
||||
// lineFlowDuration,
|
||||
// lineFlowForward
|
||||
// 关联线的所有样式
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
@@ -100,7 +116,6 @@ export default {
|
||||
fontSize: 16,
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
@@ -112,7 +127,8 @@ export default {
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
lineMarkerDir: 'end',
|
||||
hoverRectColor: ''
|
||||
hoverRectColor: '',
|
||||
hoverRectRadius: 5
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
@@ -125,7 +141,6 @@ export default {
|
||||
fontSize: 14,
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderRadius: 5,
|
||||
@@ -137,7 +152,8 @@ export default {
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
lineMarkerDir: 'end',
|
||||
hoverRectColor: ''
|
||||
hoverRectColor: '',
|
||||
hoverRectRadius: 5
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
@@ -150,7 +166,6 @@ export default {
|
||||
fontSize: 16,
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
@@ -161,7 +176,8 @@ export default {
|
||||
endColor: '#fff',
|
||||
startDir: [0, 0],
|
||||
endDir: [1, 0],
|
||||
hoverRectColor: ''
|
||||
hoverRectColor: '',
|
||||
hoverRectRadius: 5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,13 +205,12 @@ const nodeSizeIndependenceList = [
|
||||
'rootLineKeepSameInCurve',
|
||||
'rootLineStartPositionKeepSameInCurve',
|
||||
'showLineMarker',
|
||||
'gradientStyle',
|
||||
'lineRadius',
|
||||
'startColor',
|
||||
'endColor',
|
||||
'startDir',
|
||||
'endDir',
|
||||
'hoverRectColor'
|
||||
'hoverRectColor',
|
||||
'hoverRectRadius',
|
||||
'lineFlow',
|
||||
'lineFlowDuration',
|
||||
'lineFlowForward'
|
||||
]
|
||||
export const checkIsNodeSizeIndependenceConfig = config => {
|
||||
let keys = Object.keys(config)
|
||||
@@ -211,9 +226,13 @@ export const checkIsNodeSizeIndependenceConfig = config => {
|
||||
return true
|
||||
}
|
||||
|
||||
// 连线的样式
|
||||
export const lineStyleProps = [
|
||||
'lineColor',
|
||||
'lineDasharray',
|
||||
'lineWidth',
|
||||
'lineMarkerDir'
|
||||
'lineMarkerDir',
|
||||
'lineFlow',
|
||||
'lineFlowDuration',
|
||||
'lineFlowForward'
|
||||
]
|
||||
5
simple-mind-map/src/theme/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import defaultTheme from './default'
|
||||
|
||||
export default {
|
||||
default: defaultTheme
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 秋天
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: '#fff2df',
|
||||
// 连线的颜色
|
||||
lineColor: '#b0bc47',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#b0bc47',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#e68112',
|
||||
color: '#fff',
|
||||
borderColor: '#e68112',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#ffd683',
|
||||
color: '#8c5416',
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 2,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#8c5416'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: '#ffd683',
|
||||
borderColor: '#b0bc47',
|
||||
borderWidth: 2,
|
||||
color: '#8c5416'
|
||||
}
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 牛油果
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: '#e6f1de',
|
||||
// 连线的颜色
|
||||
lineColor: '#f5ffad',
|
||||
lineWidth: 4,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#749336',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#94c143',
|
||||
color: '#fff',
|
||||
borderColor: '#94c143',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#cee498',
|
||||
color: '#749336',
|
||||
borderColor: '#aec668',
|
||||
borderWidth: 2,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#749336'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: '#cee498',
|
||||
borderColor: '#aec668',
|
||||
borderWidth: 2,
|
||||
color: '#749336'
|
||||
}
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 黑金
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(18, 20, 20)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(205, 186, 156)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(245, 224, 191)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(255, 208, 124)',
|
||||
color: 'rgb(111, 61, 6)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(66, 57, 46)',
|
||||
color: 'rgb(225, 201, 158)',
|
||||
borderColor: 'rgb(245, 224, 191)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(231, 203, 155)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(56, 45, 34)',
|
||||
borderColor: 'rgb(104, 84, 61)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(242, 216, 176)'
|
||||
}
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 黑色幽默
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(27, 31, 34)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(75, 81, 78)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(255, 119, 34)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(36, 179, 96)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(254, 199, 13)',
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(27, 31, 34)',
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(204, 204, 204)'
|
||||
}
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 天空蓝
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(115, 161, 191)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(251, 251, 251)',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(115, 161, 191)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(238, 243, 246)',
|
||||
color: '#333',
|
||||
borderColor: 'rgb(115, 161, 191)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 脑残粉
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(191, 115, 148)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(251, 251, 251)',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 115, 148)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(246, 238, 242)',
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 115, 148)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,49 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 脑图经典
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: '#fff',
|
||||
// 连线的粗细
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#fff',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(58, 65, 68)',
|
||||
// 背景图片
|
||||
backgroundImage:
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=',
|
||||
// 背景重复
|
||||
backgroundRepeat: 'repeat',
|
||||
backgroundSize: 'auto',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(233, 223, 152)',
|
||||
color: '#333',
|
||||
fontSize: 24,
|
||||
borderRadius: 21
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(164, 197, 192)',
|
||||
borderColor: 'transparent',
|
||||
color: '#333',
|
||||
fontSize: 16,
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#fff',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,43 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 经典2
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
// 连线的粗细
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(51, 51, 51)',
|
||||
// 背景颜色
|
||||
backgroundColor: '#fff',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(18, 187, 55)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(241, 242, 241)',
|
||||
borderColor: 'transparent',
|
||||
color: '#1a1a1a',
|
||||
fontSize: 18,
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#1a1a1a'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 2,
|
||||
color: '#1a1a1a'
|
||||
}
|
||||
})
|
||||
@@ -1,46 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 经典3
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(94, 202, 110)',
|
||||
// 连线的粗细
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#1a1a1a',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(241, 241, 241)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(255, 245, 214)',
|
||||
color: '#1a1a1a',
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
borderColor: 'rgb(249, 199, 84)',
|
||||
borderWidth: 1
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(255, 245, 214)',
|
||||
borderColor: 'rgb(249, 199, 84)',
|
||||
borderWidth: 1,
|
||||
color: '#1a1a1a',
|
||||
fontSize: 18,
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#1a1a1a'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#1a1a1a',
|
||||
color: '#1a1a1a',
|
||||
borderWidth: 2
|
||||
}
|
||||
})
|
||||
@@ -1,49 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 经典4
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(30, 53, 86)',
|
||||
// 连线的粗细
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(56, 123, 233)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(241, 241, 241)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(30, 53, 86)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10,
|
||||
borderColor: 'rgb(189, 197, 201)',
|
||||
borderWidth: 2
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(169, 218, 218)',
|
||||
borderColor: 'rgb(30, 53, 86)',
|
||||
borderWidth: 2,
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
borderRadius: 10
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(30, 53, 86)',
|
||||
borderColor: 'rgb(30, 53, 86)',
|
||||
borderWidth: 1,
|
||||
marginY: 20
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(56, 123, 233)',
|
||||
borderColor: 'rgb(56, 123, 233)',
|
||||
color: '#fff',
|
||||
borderWidth: 0
|
||||
}
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 经典蓝
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
// 连线的粗细
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(51, 51, 51)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(239, 248, 250)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(255, 255, 255)',
|
||||
color: '#222'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(255, 255, 255)',
|
||||
color: '#222',
|
||||
borderColor: 'rgb(255, 255, 255)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,39 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 经典绿
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(123, 199, 120)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(236, 245, 231)',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(123, 199, 120)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(253, 244, 217)',
|
||||
color: '#222'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(253, 244, 217)',
|
||||
color: '#222',
|
||||
borderColor: 'rgb(242, 200, 104)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(123, 199, 120)',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 2,
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 咖啡
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(173, 123, 91)',
|
||||
lineWidth: 4,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 4,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(173, 123, 91)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(202, 117, 79)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(245, 231, 216)',
|
||||
color: 'rgb(125, 86, 42)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(96, 71, 47)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(255, 249, 239)',
|
||||
borderColor: 'rgb(173, 123, 91)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(122, 83, 44)'
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 课程绿
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(113, 195, 169)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(113, 195, 169)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(16, 160, 121)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(240, 252, 249)',
|
||||
color: 'rgb(50, 113, 96)',
|
||||
borderColor: 'rgb(113, 195, 169)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(10, 59, 43)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(246, 238, 211)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(173, 91, 12)'
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 暗色
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(17, 68, 23)',
|
||||
// 连线的粗细
|
||||
lineWidth: 2,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 2,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#fff',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(15, 16, 17)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(28, 178, 43)',
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
borderRadius: 10
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(55, 56, 58)',
|
||||
color: 'rgb(147,148,149)',
|
||||
fontSize: 18,
|
||||
borderRadius: 10,
|
||||
borderWidth: 0
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(147, 148, 149)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 暗色2
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(75, 81, 78)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(255, 119, 34)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(27, 31, 34)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(36, 179, 96)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(254, 199, 13)',
|
||||
color: 'rgb(0, 0, 0)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(255, 119, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(204, 204, 204)'
|
||||
}
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 泥土黄
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(191, 147, 115)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(251, 251, 251)',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 147, 115)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(246, 242, 238)',
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 147, 115)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 清新绿
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: '#333',
|
||||
// 背景颜色
|
||||
backgroundColor: '#d1f6ec',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#1fb27d'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#fff',
|
||||
color: '#565656',
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 清新红
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(191, 115, 115)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(251, 251, 251)',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(191, 115, 115)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(246, 238, 238)',
|
||||
color: '#333',
|
||||
borderColor: 'rgb(191, 115, 115)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 金色vip
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 56, 62)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(127, 93, 64)',
|
||||
// 背景颜色
|
||||
backgroundColor: '#fff',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(51, 56, 62)',
|
||||
color: 'rgb(247, 208, 160)',
|
||||
borderColor: '',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(239, 209, 176)',
|
||||
color: 'rgb(81, 58, 42)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(127, 93, 64)',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(255, 214, 175)'
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 绿叶
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(40, 193, 84)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(251, 158, 0)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(238, 255, 243)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(25, 193, 73)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#fff',
|
||||
color: 'rgb(69, 149, 96)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'rgb(251, 158, 0)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(51, 51, 51)'
|
||||
}
|
||||
})
|
||||
@@ -1,67 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import freshGreen from './freshGreen'
|
||||
import blueSky from './blueSky'
|
||||
import brainImpairedPink from './brainImpairedPink'
|
||||
import romanticPurple from './romanticPurple'
|
||||
import freshRed from './freshRed'
|
||||
import earthYellow from './earthYellow'
|
||||
import classic from './classic'
|
||||
import classic2 from './classic2'
|
||||
import classic3 from './classic3'
|
||||
import classic4 from './classic4'
|
||||
import dark from './dark'
|
||||
import classicGreen from './classicGreen'
|
||||
import classicBlue from './classicBlue'
|
||||
import minions from './minions'
|
||||
import pinkGrape from './pinkGrape'
|
||||
import mint from './mint'
|
||||
import gold from './gold'
|
||||
import vitalityOrange from './vitalityOrange'
|
||||
import greenLeaf from './greenLeaf'
|
||||
import dark2 from './dark2'
|
||||
import skyGreen from './skyGreen'
|
||||
import simpleBlack from './simpleBlack'
|
||||
import courseGreen from './courseGreen'
|
||||
import coffee from './coffee'
|
||||
import redSpirit from './redSpirit'
|
||||
import blackHumour from './blackHumour'
|
||||
import lateNightOffice from './lateNightOffice'
|
||||
import blackGold from './blackGold'
|
||||
import avocado from './avocado'
|
||||
import autumn from './autumn'
|
||||
import orangeJuice from './orangeJuice'
|
||||
|
||||
export default {
|
||||
default: defaultTheme,
|
||||
freshGreen,
|
||||
blueSky,
|
||||
brainImpairedPink,
|
||||
romanticPurple,
|
||||
freshRed,
|
||||
earthYellow,
|
||||
classic,
|
||||
classic2,
|
||||
classic3,
|
||||
classic4,
|
||||
dark,
|
||||
classicGreen,
|
||||
classicBlue,
|
||||
minions,
|
||||
pinkGrape,
|
||||
mint,
|
||||
gold,
|
||||
vitalityOrange,
|
||||
greenLeaf,
|
||||
dark2,
|
||||
skyGreen,
|
||||
simpleBlack,
|
||||
courseGreen,
|
||||
coffee,
|
||||
redSpirit,
|
||||
blackHumour,
|
||||
lateNightOffice,
|
||||
blackGold,
|
||||
avocado,
|
||||
autumn,
|
||||
orangeJuice
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 深夜办公室
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(32, 37, 49)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(137, 167, 196)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(255, 119, 34)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(23, 153, 243)',
|
||||
color: 'rgb(255, 255, 255)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(70, 78, 94)',
|
||||
color: 'rgb(209, 210, 210)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(204, 204, 204)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(255, 119, 34)',
|
||||
borderColor: '',
|
||||
borderWidth: 2,
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 小黄人
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(51, 51, 51)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#222',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(248, 215, 49)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(55, 165, 255)',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(255, 160, 36)',
|
||||
color: '#222',
|
||||
borderColor: 'rgb(51, 51, 51)',
|
||||
borderWidth: 3,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
borderColor: '#222',
|
||||
borderWidth: 3,
|
||||
color: '#222'
|
||||
}
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 薄荷
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(104, 204, 202)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(90, 206, 241)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(239, 255, 255)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(0, 192, 184)',
|
||||
borderColor: '',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#fff',
|
||||
color: '#222',
|
||||
borderColor: 'rgb(184, 235, 233)',
|
||||
borderWidth: 2,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(90, 206, 241)',
|
||||
borderColor: 'transparent',
|
||||
color: '#fff'
|
||||
}
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 橙汁
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: '#070616',
|
||||
// 连线的颜色
|
||||
lineColor: '#fff',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#fff',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#ff6811',
|
||||
color: '#110501',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#070616',
|
||||
color: '#a9a4a9',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 2,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: '#a9a4a9'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: '',
|
||||
borderColor: '#ff6811',
|
||||
borderWidth: 2,
|
||||
color: '#a9a4a9'
|
||||
}
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 粉红葡萄
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(166, 101, 106)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#fff',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(255, 208, 211)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(139, 109, 225)',
|
||||
borderColor: '',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(243, 104, 138)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: '#222'
|
||||
}
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 红色精神
|
||||
export default merge(defaultTheme, {
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(255, 238, 228)',
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(230, 138, 131)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(222, 101, 85)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(207, 44, 44)',
|
||||
color: 'rgb(255, 233, 157)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(255, 255, 255)',
|
||||
color: 'rgb(211, 58, 21)',
|
||||
borderColor: 'rgb(222, 101, 85)',
|
||||
borderWidth: 2,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(144, 71, 43)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'rgb(255, 247, 211)',
|
||||
borderColor: 'rgb(255, 202, 162)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(187, 101, 69)'
|
||||
}
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 浪漫紫
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(123, 115, 191)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(251, 251, 251)',
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#333',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(123, 115, 191)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(239, 238, 246)',
|
||||
color: '#333',
|
||||
borderColor: 'rgb(123, 115, 191)',
|
||||
borderWidth: 1,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#333'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '#333',
|
||||
color: '#333'
|
||||
}
|
||||
})
|
||||
@@ -1,42 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 简约黑
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(34, 34, 34)',
|
||||
lineWidth: 4,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 4,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(34, 34, 34)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#fff',
|
||||
color: 'rgb(34, 34, 34)',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 3,
|
||||
fontSize: 24
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(241, 246, 248)',
|
||||
color: 'rgb(34, 34, 34)',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 3,
|
||||
fontSize: 18
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 14,
|
||||
color: 'rgb(34, 34, 34)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fontSize: 14,
|
||||
fillColor: 'transparent',
|
||||
borderColor: 'rgb(34, 34, 34)',
|
||||
borderWidth: 2,
|
||||
color: 'rgb(34, 34, 34)'
|
||||
}
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 天清绿
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: '#fff',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#fff',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(80, 156, 170)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
color: 'rgb(65, 89, 158)'
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: 'rgb(251, 227, 188)',
|
||||
color: 'rgb(65, 89, 158)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(65, 89, 158)'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: '#fff',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(65, 89, 158)'
|
||||
}
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import defaultTheme from './default'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 活力橙
|
||||
export default merge(defaultTheme, {
|
||||
// 连线的颜色
|
||||
lineColor: 'rgb(254, 146, 0)',
|
||||
lineWidth: 3,
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 3,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: 'rgb(255, 222, 69)',
|
||||
// 背景颜色
|
||||
backgroundColor: 'rgb(255, 246, 243)',
|
||||
// 根节点样式
|
||||
root: {
|
||||
fillColor: 'rgb(255, 112, 52)',
|
||||
color: '#fff',
|
||||
borderColor: '',
|
||||
borderWidth: 0
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
fillColor: '#fff',
|
||||
color: 'rgb(51, 51, 51)',
|
||||
borderColor: '',
|
||||
borderWidth: 0,
|
||||
fontSize: 14
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
fontSize: 12,
|
||||
color: '#222'
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
fillColor: 'rgb(255, 222, 69)',
|
||||
borderColor: 'transparent',
|
||||
color: 'rgb(51, 51, 51)'
|
||||
}
|
||||
})
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
} from '../constants/constant'
|
||||
import MersenneTwister from './mersenneTwister'
|
||||
import { ForeignObject } from '@svgdotjs/svg.js'
|
||||
import merge from 'deepmerge'
|
||||
import { lineStyleProps } from '../theme/default'
|
||||
|
||||
// 深度优先遍历树
|
||||
export const walk = (
|
||||
@@ -75,11 +77,11 @@ export const resizeImgSizeByOriginRatio = (
|
||||
let nRatio = width / height
|
||||
let mRatio = newWidth / newHeight
|
||||
if (nRatio > mRatio) {
|
||||
// 固定高度
|
||||
arr = [nRatio * newHeight, newHeight]
|
||||
} else {
|
||||
// 固定宽度
|
||||
arr = [newWidth, newWidth / nRatio]
|
||||
} else {
|
||||
// 固定高度
|
||||
arr = [nRatio * newHeight, newHeight]
|
||||
}
|
||||
return arr
|
||||
}
|
||||
@@ -94,11 +96,11 @@ export const resizeImgSize = (width, height, maxWidth, maxHeight) => {
|
||||
} else {
|
||||
let mRatio = maxWidth / maxHeight
|
||||
if (nRatio > mRatio) {
|
||||
// 固定高度
|
||||
arr = [nRatio * maxHeight, maxHeight]
|
||||
} else {
|
||||
// 固定宽度
|
||||
arr = [maxWidth, maxWidth / nRatio]
|
||||
} else {
|
||||
// 固定高度
|
||||
arr = [nRatio * maxHeight, maxHeight]
|
||||
}
|
||||
}
|
||||
} else if (maxWidth) {
|
||||
@@ -506,13 +508,14 @@ export const addHtmlStyle = (html, tag, style) => {
|
||||
if (!addHtmlStyleEl) {
|
||||
addHtmlStyleEl = document.createElement('div')
|
||||
}
|
||||
const tags = Array.isArray(tag) ? tag : [tag]
|
||||
addHtmlStyleEl.innerHTML = html
|
||||
let walk = root => {
|
||||
let childNodes = root.childNodes
|
||||
childNodes.forEach(node => {
|
||||
if (node.nodeType === 1) {
|
||||
// 元素节点
|
||||
if (node.tagName.toLowerCase() === tag) {
|
||||
if (tags.includes(node.tagName.toLowerCase())) {
|
||||
node.style.cssText = style
|
||||
} else {
|
||||
walk(node)
|
||||
@@ -789,6 +792,18 @@ export const checkIsNodeStyleDataKey = key => {
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断一个对象是否不需要触发节点重新创建
|
||||
export const isNodeNotNeedRenderData = config => {
|
||||
const list = [...lineStyleProps] // 节点连线样式
|
||||
const keys = Object.keys(config)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!list.includes(keys[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 合并图标数组
|
||||
// const data = [
|
||||
// { type: 'priority', name: '优先级图标', list: [{ name: '1', icon: 'a' }, { name: 2, icon: 'b' }] },
|
||||
@@ -1068,9 +1083,14 @@ export const isSameObject = (a, b) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查navigator.clipboard对象的读取是否可用
|
||||
export const checkClipboardReadEnable = () => {
|
||||
return navigator.clipboard && typeof navigator.clipboard.read === 'function'
|
||||
}
|
||||
|
||||
// 将数据设置到用户剪切板中
|
||||
export const setDataToClipboard = data => {
|
||||
if (navigator.clipboard) {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(JSON.stringify(data))
|
||||
}
|
||||
}
|
||||
@@ -1079,7 +1099,7 @@ export const setDataToClipboard = data => {
|
||||
export const getDataFromClipboard = async () => {
|
||||
let text = null
|
||||
let img = null
|
||||
if (navigator.clipboard) {
|
||||
if (checkClipboardReadEnable()) {
|
||||
const items = await navigator.clipboard.read()
|
||||
if (items && items.length > 0) {
|
||||
for (const clipboardItem of items) {
|
||||
@@ -1610,3 +1630,12 @@ export const sortNodeList = nodeList => {
|
||||
})
|
||||
return nodeList
|
||||
}
|
||||
|
||||
// 合并主题配置
|
||||
export const mergeTheme = (dest, source) => {
|
||||
return merge(dest, source, {
|
||||
arrayMerge: (destinationArray, sourceArray) => {
|
||||
return sourceArray
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
63
web/package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"element-ui": "^2.15.1",
|
||||
"highlight.js": "^10.7.3",
|
||||
"katex": "^0.16.9",
|
||||
"simple-mind-map-plugin-themes": "^1.0.0",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-i18n": "^8.27.2",
|
||||
@@ -1812,7 +1813,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@@ -1828,7 +1828,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@@ -1844,7 +1843,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
@@ -1860,7 +1858,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@@ -1876,7 +1873,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
@@ -1892,7 +1888,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
@@ -1908,7 +1903,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
@@ -1924,7 +1918,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -1940,7 +1933,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -1956,7 +1948,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -1972,7 +1963,6 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -1988,7 +1978,6 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -2004,7 +1993,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -2020,7 +2008,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -2036,7 +2023,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -2052,7 +2038,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
@@ -2068,7 +2053,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
@@ -2084,7 +2068,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
@@ -2100,7 +2083,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
@@ -2116,7 +2098,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@@ -2132,7 +2113,6 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@@ -2148,7 +2128,6 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
@@ -6847,7 +6826,6 @@
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
|
||||
"integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
@@ -13616,6 +13594,14 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/simple-mind-map-plugin-themes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-mind-map-plugin-themes/-/simple-mind-map-plugin-themes-1.0.0.tgz",
|
||||
"integrity": "sha512-v+L28HfRK1B+qw+76ueRGbbAxnKmGhVNuHL+oCWTE0qbdvaqSAszI+yOxqeTNXJmBgzrjeJXpJ831/gDiWUKaQ==",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.17.15"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
@@ -18560,154 +18546,132 @@
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
|
||||
"integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-arm64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
|
||||
"integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/android-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/darwin-arm64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
|
||||
"integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/darwin-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/freebsd-arm64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
|
||||
"integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/freebsd-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-arm": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
|
||||
"integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-arm64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
|
||||
"integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-ia32": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
|
||||
"integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-loong64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
|
||||
"integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-mips64el": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
|
||||
"integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-ppc64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
|
||||
"integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-riscv64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
|
||||
"integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-s390x": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
|
||||
"integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/linux-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/netbsd-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/openbsd-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/sunos-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-arm64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
|
||||
"integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-ia32": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
|
||||
"integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@esbuild/win32-x64": {
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
|
||||
"integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@hapi/address": {
|
||||
@@ -22487,7 +22451,6 @@
|
||||
"version": "0.17.19",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
|
||||
"integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@esbuild/android-arm": "0.17.19",
|
||||
"@esbuild/android-arm64": "0.17.19",
|
||||
@@ -27888,6 +27851,14 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"simple-mind-map-plugin-themes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-mind-map-plugin-themes/-/simple-mind-map-plugin-themes-1.0.0.tgz",
|
||||
"integrity": "sha512-v+L28HfRK1B+qw+76ueRGbbAxnKmGhVNuHL+oCWTE0qbdvaqSAszI+yOxqeTNXJmBgzrjeJXpJ831/gDiWUKaQ==",
|
||||
"requires": {
|
||||
"esbuild": "^0.17.15"
|
||||
}
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"element-ui": "^2.15.1",
|
||||
"highlight.js": "^10.7.3",
|
||||
"katex": "^0.16.9",
|
||||
"simple-mind-map-plugin-themes": "^1.0.0",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-i18n": "^8.27.2",
|
||||
|
||||
BIN
web/src/assets/avatar/4399行星元帅.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
web/src/assets/avatar/Joe.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
web/src/assets/avatar/Xavier.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
web/src/assets/avatar/h.r.w.jpg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
web/src/assets/avatar/xbkkjbs0246658.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
web/src/assets/avatar/一亩三.jpg
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
web/src/assets/avatar/冒号括号.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
web/src/assets/avatar/广兴.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
web/src/assets/avatar/时光匆匆.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
web/src/assets/avatar/梁辉.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
web/src/assets/avatar/海云.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
web/src/assets/avatar/皮老板.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
web/src/assets/avatar/黄智彪@一米一栗科技.png
Normal file
|
After Width: | Height: | Size: 10 KiB |