Compare commits

...

33 Commits
0.1.8 ... 0.2.4

Author SHA1 Message Date
wanglin2
ece116317b 修改文档 2022-09-13 22:30:45 +08:00
wanglin25
d24d5c8281 修复节点展开收起的bug&打包 2022-09-13 10:34:20 +08:00
wanglin25
97c01cda3a 打包 2022-09-13 09:43:18 +08:00
wanglin25
6d520ece7e 修改文档 2022-09-13 09:33:05 +08:00
wanglin2
745041deef 节点支持多种形状开发完成 2022-09-12 23:07:01 +08:00
wanglin25
ae6c10cdf9 打包 2022-09-01 11:11:33 +08:00
街角小林
f5338d62fc Merge pull request #26 from huangyuanyin/main
fix 二级节点后无子节点,展开所有/收起所有操作后的报错信息
2022-09-01 11:07:38 +08:00
liuzhanghao
19fc12ff20 fix 2022-09-01 10:28:26 +08:00
liuzhanghao
d7640bb026 fix 二级节点后无子节点,展开所有/收起所有操作后的报错信息 2022-09-01 10:22:01 +08:00
wanglin
193b293cfe 更新readme 2022-08-22 21:03:08 +08:00
wanglin25
615ff3ea25 fix:修改右键菜单快捷键提示 2022-08-17 09:25:16 +08:00
wanglin25
8b9cfd2972 fix:修复右键菜单快捷键提示错误 2022-08-16 18:48:11 +08:00
wanglin25
03f8cb9290 fix:修复编辑节点文本时快捷键冲突的问题 2022-08-16 16:47:43 +08:00
wanglin
1ea0c7e316 修复输入字符串/和快捷键/冲突问题 2022-08-14 09:43:00 +08:00
wanglin
a39c8c30e6 打包 2022-08-08 21:24:02 +08:00
街角小林
26ce08c27d Update README.md 2022-08-08 20:09:31 +08:00
wanglin25
4bfc98a96f 支持导出为pdf 2022-08-08 20:06:59 +08:00
wanglin
7560411922 打包&发布新版本 2022-08-06 09:21:18 +08:00
wanglin25
4288e44f3a 支持Ctrl+左键多选 2022-08-05 17:21:21 +08:00
wanglin25
b82c5247fa Merge branch 'dev' of https://github.com/wanglin2/mind-map into dev 2022-08-05 09:30:19 +08:00
wanglin25
c7d2082944 新增库打包&修改文档 2022-08-05 09:29:59 +08:00
wanglin
29458ade9c 增加图标 2022-08-04 20:24:57 +08:00
wanglin25
49d366628e 修改文档 2022-08-04 15:06:07 +08:00
wanglin25
b093153262 新增快捷键 2022-08-04 14:48:52 +08:00
wanglin25
02f276bc2a 支持自由拖拽、修复一些bug 2022-08-04 11:39:17 +08:00
wanglin25
69cb961cc1 修复概要的一些bug 2022-08-02 08:59:05 +08:00
wanglin25
7096391f3b 修复拖拽节点时为隐藏概要内容的问题 2022-08-01 09:59:43 +08:00
wanglin25
280ffcf01d 修复初始渲染时激活节点没有触发页面效果的问题 2022-08-01 09:51:53 +08:00
wanglin25
db3c2b71f5 修复概要展开收起的定位错误问题 2022-08-01 09:36:25 +08:00
wanglin
f11f364d00 新增方法 2022-07-31 22:29:40 +08:00
wanglin
0e8c50d430 修复概要定位问题 2022-07-31 20:46:40 +08:00
wanglin
dd8d250857 修改及新增主题 2022-07-31 14:54:32 +08:00
wanglin
693ead6b49 概要开发中 2022-07-31 10:28:13 +08:00
91 changed files with 2326 additions and 242 deletions

221
README.md
View File

@@ -8,37 +8,39 @@
- [x] 支持快捷键
- [x] 节点内容支持图片、图标、超链接、备注、标签
- [x] 节点内容支持图片、图标、超链接、备注、标签、概要
- [x] 支持前进后退
- [x] 支持拖动、缩放
- [x] 支持右键多选
- [x] 支持右键按住多选
- [x] 支持节点拖拽
- [x] 支持节点自由拖拽、拖拽调整
- [x] 支持多种节点形状
## 目录介绍
1.simple-mind-map
1.`simple-mind-map`
思维导图工具库。
2.web
2.`web`
使用`simple-mind-map`工具库基于vue2.xElementUI搭建的在线思维导图。
使用`simple-mind-map`工具库,基于`vue2.x``ElementUI`搭建的在线思维导图。
3.dist
3.`dist`
打包后的资源文件夹。
4.docs
4.`docs`
文档等。
## 开发
本地开发
### 本地开发
```bash
git clone https://github.com/wanglin2/mind-map.git
@@ -52,7 +54,27 @@ npm link simple-mind-map
npm run serve
```
打包
### 打包
`0.2.0`版本开始增加了对核心库`simple-mind-map`的打包,复用了示例项目`web`的打包工具。
```bash
cd web
npm run buildLibrary
```
`simple-mind-map`库的`package.json`文件提供了两个导出字段:
```json
{
"module": "index.js",
"main": "./dist/simpleMindMap.umd.min.js",
}
```
支持`module`字段的环境会以`index.js`为入口,否则会以打包后的`simpleMindMap.umd.min.js`为入口。
### 打包demo
```bash
cd web
@@ -66,19 +88,23 @@ npm run build
# 安装
> 当然仓库版本0.2.4当前npm版本0.2.4
```bash
npm i simple-mind-map
```
注意本项目为源码直接发布并未进行打包如果出现编译失败的情况Vue CLI创建的项目可以在vue.config.js文件中增加如下配置来让babel-loader编译本依赖
`0.2.0`版本之前的注意事项
```js
module.exports = {
transpileDependencies: ['simple-mind-map']
}
```
其他项目请自行修改打包配置。
>注意本项目为源码直接发布并未进行打包如果出现编译失败的情况Vue CLI创建的项目可以在vue.config.js文件中增加如下配置来让babel-loader编译本依赖
>
>```js
>module.exports = {
> transpileDependencies: ['simple-mind-map']
>}
>```
>
>其他项目请自行修改打包配置。
# API
@@ -106,7 +132,7 @@ const mindMap = new MindMap({
| el | Element | | 容器元素必须为DOM元素 | 是 |
| data | Object | {} | 思维导图数据,可参考:[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/example/exampleData.js) | |
| layout | String | logicalStructure | 布局类型可选列表logicalStructure逻辑结构图、mindMap思维导图、catalogOrganization目录组织图、organizationStructure组织结构图 | |
| theme | String | default | 主题可选列表default默认、classic脑图经典、minions小黄人、pinkGrape粉红葡萄、mint薄荷、gold金色vip、vitalityOrange活力橙、greenLeaf绿叶、dark2暗色2、skyGreen天清绿、classic2脑图经典2、classic3脑图经典3、classicGreen经典绿、classicBlue经典蓝、blueSky天空蓝、brainImpairedPink脑残粉、dark暗色、earthYellow泥土黄、freshGreen清新绿、freshRed清新红、romanticPurple浪漫紫 | |
| theme | String | default | 主题可选列表default默认、classic脑图经典、minions小黄人、pinkGrape粉红葡萄、mint薄荷、gold金色vip、vitalityOrange活力橙、greenLeaf绿叶、dark2暗色2、skyGreen天清绿、classic2脑图经典2、classic3脑图经典3classic4脑图经典4v0.2.0+)、classicGreen经典绿、classicBlue经典蓝、blueSky天空蓝、brainImpairedPink脑残粉、dark暗色、earthYellow泥土黄、freshGreen清新绿、freshRed清新红、romanticPurple浪漫紫 | |
| themeConfig | Object | {} | 主题配置,会和所选择的主题进行合并,可用字段可参考:[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js) | |
| scaleRatio | Number | 0.1 | 放大缩小的增量比例 | |
| maxTag | Number | 5 | 节点里最多显示的标签数量,多余的会被丢弃 | |
@@ -228,34 +254,39 @@ v0.1.7+。切换模式为只读或编辑。
执行命令,每执行一个命令就会在历史堆栈里添加一条记录用于回退或前进。所有命令如下:
| 命令名称 | 描述 | 参数 |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| SELECT_ALL | 全选 | |
| BACK | 回退指定的步数 | step要回退的步数默认为1 |
| FORWARD | 前进指定的步数 | step要前进的步数默认为1 |
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | |
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | |
| UP_NODE | 上移节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的第一个节点使用无效 | |
| DOWN_NODE | 操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的最后一个节点使用无效 | |
| REMOVE_NODE | 删除节点,操作节点为当前激活的节点 | |
| PASTE_NODE | 粘贴节点到节点,操作节点为当前激活的节点 | data要粘贴的节点数据一般通过`renderer.copyNode()`方法和`renderer.cutNode()`方法获取) |
| CUT_NODE | 剪切节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点使用无效 | callback(回调函数,剪切的节点数据会通过调用该函数并通过参数返回) |
| SET_NODE_STYLE | 修改节点样式 | node要设置样式的节点、prop样式属性、value样式属性值、isActive布尔值是否设置的是激活状态的样式 |
| SET_NODE_ACTIVE | 设置节点是否激活 | node要设置的节点、active布尔值是否激活 |
| CLEAR_ACTIVE_NODE | 清除当前已激活节点的激活状态,操作节点为当前激活的节点 | |
| SET_NODE_EXPAND | 设置节点是否展开 | node要设置的节点、expand布尔值是否展开 |
| EXPAND_ALL | 展开所有节点 | |
| UNEXPAND_ALL | 收起所有节点 | |
| SET_NODE_DATA | 更新节点数据,即更新节点数据对象里`data`对象的数据 | node要设置的节点、data对象要更新的数据`{expand: true}` |
| SET_NODE_TEXT | 设置节点文本 | node要设置的节点、text要设置的文本字符串换行可以使用`\n` |
| SET_NODE_IMAGE | 设置节点图片 | node要设置的节点、imgData对象图片信息结构为`{url, title, width, height}`,图片的宽高必须要传) |
| SET_NODE_ICON | 设置节点图标 | node要设置的节点、icons数组预定义的图片名称组成的数组可用图标可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件里的`nodeIconList`列表里获取到,图标名称为`type_name`,如`['priority_1']` |
| SET_NODE_HYPERLINK | 设置节点超链接 | node要设置的节点、link超链接地址、title超链接名称可选 |
| SET_NODE_NOTE | 设置节点备注 | node要设置的节点、note备注文字 |
| SET_NODE_TAG | 设置节点标签 | node要设置的节点、tag字符串数组内置颜色信息可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)里获取到) |
| INSERT_AFTERv0.1.5+ | 将节点移动到另一个节点的后面 | node要移动的节点、 exist目标节点 |
| INSERT_BEFOREv0.1.5+ | 将节点移动到另一个节点的前面 | node要移动的节点、 exist目标节点 |
| MOVE_NODE_TOv0.1.5+ | 移动一个节点作为另一个节点的子节点 | node要移动的节点、 toNode目标节点 |
| 命令名称 | 描述 | 参数 |
| ----------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| SELECT_ALL | 全选 | |
| BACK | 回退指定的步数 | step要回退的步数默认为1 |
| FORWARD | 前进指定的步数 | step要前进的步数默认为1 |
| INSERT_NODE | 插入同级节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效 | |
| INSERT_CHILD_NODE | 插入子节点,操作节点为当前激活的节点 | |
| UP_NODE | 上移节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的第一个节点使用无效 | |
| DOWN_NODE | 操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点或在列表里的最后一个节点使用无效 | |
| REMOVE_NODE | 删除节点,操作节点为当前激活的节点 | |
| PASTE_NODE | 粘贴节点到节点,操作节点为当前激活的节点 | data要粘贴的节点数据一般通过`renderer.copyNode()`方法和`renderer.cutNode()`方法获取) |
| CUT_NODE | 剪切节点,操作节点为当前激活的节点,如果有多个激活节点,只会对第一个有效,对根节点使用无效 | callback(回调函数,剪切的节点数据会通过调用该函数并通过参数返回) |
| SET_NODE_STYLE | 修改节点样式 | node要设置样式的节点、prop样式属性、value样式属性值、isActive布尔值是否设置的是激活状态的样式 |
| SET_NODE_ACTIVE | 设置节点是否激活 | node要设置的节点、active布尔值是否激活 |
| CLEAR_ACTIVE_NODE | 清除当前已激活节点的激活状态,操作节点为当前激活的节点 | |
| SET_NODE_EXPAND | 设置节点是否展开 | node要设置的节点、expand布尔值是否展开 |
| EXPAND_ALL | 展开所有节点 | |
| UNEXPAND_ALL | 收起所有节点 | |
| SET_NODE_DATA | 更新节点数据,即更新节点数据对象里`data`对象的数据 | node要设置的节点、data对象要更新的数据如`{expand: true}` |
| SET_NODE_TEXT | 设置节点文本 | node要设置的节点、text要设置的文本字符串换行可以使用`\n` |
| SET_NODE_IMAGE | 设置节点图片 | node要设置的节点、imgData对象图片信息结构为`{url, title, width, height}`,图片的宽高必须要传) |
| SET_NODE_ICON | 设置节点图标 | node要设置的节点、icons数组预定义的图片名称组成的数组可用图标可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件里的`nodeIconList`列表里获取到,图标名称为`type_name`,如`['priority_1']` |
| SET_NODE_HYPERLINK | 设置节点超链接 | node要设置的节点、link超链接地址、title超链接名称可选 |
| SET_NODE_NOTE | 设置节点备注 | node要设置的节点、note备注文字 |
| SET_NODE_TAG | 设置节点标签 | node要设置的节点、tag字符串数组内置颜色信息可在[https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)里获取到) |
| INSERT_AFTERv0.1.5+ | 将节点移动到另一个节点的后面 | node要移动的节点、 exist目标节点 |
| INSERT_BEFOREv0.1.5+ | 将节点移动到另一个节点的前面 | node要移动的节点、 exist目标节点 |
| MOVE_NODE_TOv0.1.5+ | 移动一个节点作为另一个节点的子节点 | node要移动的节点、 toNode目标节点 |
| ADD_GENERALIZATIONv0.2.0+ | 添加节点概要 | data概要的数据对象格式节点的数字段都支持默认为{text: '概要'} |
| REMOVE_GENERALIZATIONv0.2.0+ | 删除节点概要 | |
| SET_NODE_CUSTOM_POSITIONv0.2.0+ | 设置节点自定义位置 | node要设置的节点、 left自定义的x坐标默认为undefined、 top自定义的y坐标默认为undefined |
| RESET_LAYOUTv0.2.0+ | 一键整理布局 | |
| SET_NODE_SHAPEv0.2.4+ | 设置节点形状 | node要设置的节点、shape形状全部形状https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js |
#### setData(data)
@@ -269,7 +300,7 @@ v0.1.7+。切换模式为只读或编辑。
导出
`type`要导出的类型可选值png、svg
`type`要导出的类型可选值png、svg、json、pdfv0.2.1+、smm本质也是json
`isDownload`:是否需要直接触发下载,布尔值,默认为`false`
@@ -393,7 +424,7 @@ v0.1.5+
### 方法
#### keyCommand(key, fn)
#### addShortcut(key, fn)
添加快捷键
@@ -401,7 +432,7 @@ v0.1.5+
```js
// 单个按键
mindMap.keyCommand..addShortcut('Enter', () => {})
mindMap.keyCommand.addShortcut('Enter', () => {})
// 或
mindMap.keyCommand.addShortcut('Del|Backspace', () => {})
// 组合键
@@ -410,6 +441,29 @@ mindMap.keyCommand.addShortcut('Control+Enter', () => {})
`fn`:要执行的方法
#### removeShortcut(key, fn)
移除快捷键命令,`fn`不指定则移除该快捷键的所有回调方法
#### getShortcutFn(key)
v0.2.2+。获取指定快捷键的处理函数
#### pause()
v0.2.2+。暂停所有快捷键响应
#### recovery()
v0.2.2+。恢复快捷键响应
#### save()
v0.2.3+。保存当前注册的快捷键数据,然后清空快捷键数据
#### restore()
v0.2.3+。恢复保存的快捷键数据,然后清空缓存数据
## command实例
@@ -804,6 +858,70 @@ v0.1.5+
#### checkHasGeneralization()
v0.2.0+
检查是否存在概要
#### hideGeneralization()
v0.2.0+
隐藏概要节点
#### showGeneralization()
v0.2.0+
显示概要节点
#### updateGeneralization()
v0.2.0+
更新概要节点
#### hasCustomPosition()
v0.2.0+
检查节点是否存在自定义数据
#### ancestorHasCustomPosition()
v0.2.0+
检查节点是否存在自定义位置的祖先节点
#### getShape()
v0.2.4+
获取节点形状
#### setShape(shape)
v0.2.4+
设置节点形状,`SET_NODE_SHAPE`命令的快捷方法
## 内置工具方法
引用:
@@ -930,3 +1048,6 @@ copyNodeTree({}, node)
[知犀思维导图](https://www.zhixi.com/)
# License
[MIT](https://opensource.org/licenses/MIT)

View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/css/app.b793e592.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.6fd71983.css" rel="preload" as="style"><link href="dist/js/app.c4887e17.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.384d822e.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.6fd71983.css" rel="stylesheet"><link href="dist/css/app.b793e592.css" 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 src="dist/js/chunk-vendors.384d822e.js"></script><script src="dist/js/app.c4887e17.js"></script></body></html>
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d20ec02.81d632f4.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.228f2009.js" rel="prefetch"><link href="dist/js/chunk-e86f1494.f8dd20e2.js" rel="prefetch"><link href="dist/css/app.b771b210.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.6fd71983.css" rel="preload" as="style"><link href="dist/js/app.4bae41a9.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.54c023de.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.6fd71983.css" rel="stylesheet"><link href="dist/css/app.b771b210.css" 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 src="dist/js/chunk-vendors.54c023de.js"></script><script src="dist/js/app.4bae41a9.js"></script></body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -10,7 +10,10 @@ const createFullData = () => {
"tag": ["标签1", "标签2"],
"hyperlink": "http://lxqnsys.com/",
"hyperlinkTitle": "理想青年实验室",
"note": "理想青年实验室\n一个有意思的角落"
"note": "理想青年实验室\n一个有意思的角落",
// 自定义位置
// "customLeft": 1318,
// "customTop": 374.5
};
}
@@ -32,110 +35,110 @@ const data1 = {
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
...createFullData()
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
...createFullData()
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}]
@@ -147,36 +150,36 @@ const data1 = {
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
},
@@ -187,40 +190,40 @@ const data1 = {
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}]
@@ -232,225 +235,225 @@ const data1 = {
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
"children": [{
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}, {
"data": {
"text": "子节点",
"text": "分支主题",
},
}]
}]
@@ -767,13 +770,13 @@ const data3 = {
"children": [
{
"data": {
"text": "子节点"
"text": "分支主题"
},
"children": []
},
{
"data": {
"text": "子节点"
"text": "分支主题"
},
"children": []
}
@@ -864,6 +867,39 @@ const data4 = {
}
}
// 带概要
const data5 = {
"root": {
"data": {
"text": "根节点"
},
"children": [
{
"data": {
"text": "二级节点",
"generalization": {
"text": "概要",
}
},
"children": [
{
"data": {
"text": "分支主题"
},
"children": []
},
{
"data": {
"text": "分支主题"
},
"children": []
}
]
},
]
}
}
const rootData = {
"root": {
"data": {
@@ -876,11 +912,12 @@ const rootData = {
export default {
// ...data1,
// ...data2,
...data3,
// ...data3,
// ...data4,
...data5,
// ...rootData,
"theme": {
"template": "minions",
"template": "classic4",
"config": {
// 自定义配置...
}

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.1.8",
"version": "0.2.4",
"description": "一个简单的web在线思维导图",
"authors": [
{
@@ -18,13 +18,14 @@
"url": "https://github.com/wanglin2/mind-map"
},
"scripts": {},
"main": "index.js",
"type": "module",
"module": "index.js",
"main": "./dist/simpleMindMap.umd.min.js",
"dependencies": {
"@svgdotjs/svg.js": "^3.0.16",
"canvg": "^3.0.7",
"deepmerge": "^1.5.2",
"eventemitter3": "^4.0.7"
"eventemitter3": "^4.0.7",
"jspdf": "^2.5.1"
},
"keywords": [
"javascript",

View File

@@ -71,7 +71,7 @@ class Drag extends Base {
bindEvent() {
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
this.mindMap.on('node_mousedown', (node, e) => {
if (this.mindMap.opt.readonly) {
if (this.mindMap.opt.readonly || node.isGeneralization) {
return
}
if (e.which !== 1 || node.isRoot) {
@@ -129,11 +129,12 @@ class Drag extends Base {
* @Date: 2021-11-23 19:38:02
* @Desc: 鼠标松开事件
*/
onMouseup() {
onMouseup(e) {
if (!this.isMousedown) {
return;
}
this.isMousedown = false
let _nodeIsDrag = this.node.isDrag
this.node.isDrag = false
this.node.show()
this.removeCloneNode()
@@ -147,6 +148,26 @@ class Drag extends Base {
} else if (this.nextNode) { // 存在下一个相邻节点,作为其前一个兄弟节点
this.mindMap.renderer.setNodeActive(this.nextNode, false)
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
} else if (_nodeIsDrag) {
// 自定义位置
let {
x,
y
} = this.mindMap.toPos(e.clientX - this.offsetX, e.clientY - this.offsetY)
let {
scaleX,
scaleY,
translateX,
translateY
} = this.drawTransform
x = (x - translateX) / scaleX
y = (y - translateY) / scaleY
this.node.left = x
this.node.top = y
this.node.customLeft = x
this.node.customTop = y
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
this.mindMap.render()
}
this.reset()
}

View File

@@ -1,4 +1,5 @@
import { imgToDataUrl, downloadFile } from './utils'
import JsPDF from 'jspdf'
const URL = window.URL || window.webkitURL || window
/**
@@ -24,8 +25,8 @@ class Export {
*/
async export(type, isDownload = true, name = '思维导图') {
if (this[type]) {
let result = await this[type]()
if (isDownload) {
let result = await this[type](name)
if (isDownload && type !== 'pdf') {
downloadFile(result, name + '.' + type)
}
return result
@@ -163,6 +164,43 @@ class Export {
return imgDataUrl
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-08 19:23:08
* @Desc: 导出为pdf
*/
async pdf(name) {
let img = await this.png()
let pdf = new JsPDF('', 'pt', 'a4')
let a4Width = 595
let a4Height = 841
let a4Ratio = a4Width / a4Height
let image = new Image()
image.onload = () => {
let imageWidth = image.width
let imageHeight = image.height
let imageRatio = imageWidth / imageHeight
let w, h
if (imageWidth <= a4Width && imageHeight <= a4Height) {
// 使用图片原始宽高
w = imageWidth
h = imageHeight
} else if (a4Ratio > imageRatio) {
// 以a4Height为高度缩放图片宽度
w = imageRatio * a4Height
h = a4Height
} else {
// 以a4Width为宽度缩放图片高度
w = a4Width
h = a4Width / imageRatio
}
pdf.addImage(img, 'PNG', (a4Width - w) / 2, (a4Height - h) / 2, w, h)
pdf.save(name)
}
image.src = img
}
/**
* @Author: 王林
* @Date: 2021-07-04 15:32:07

View File

@@ -16,9 +16,51 @@ export default class KeyCommand {
this.shortcutMap = {
//Enter: [fn]
}
this.shortcutMapCache = {}
this.isPause = false
this.bindEvent()
}
/**
* @Author: 王林
* @Date: 2022-08-14 08:57:55
* @Desc: 暂停快捷键响应
*/
pause() {
this.isPause = true
}
/**
* @Author: 王林
* @Date: 2022-08-14 08:58:43
* @Desc: 恢复快捷键响应
*/
recovery() {
this.isPause = false
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-16 16:29:01
* @Desc: 保存当前注册的快捷键数据,然后清空快捷键数据
*/
save() {
this.shortcutMapCache = this.shortcutMap
this.shortcutMap = {}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-16 16:29:38
* @Desc: 恢复保存的快捷键数据,然后清空缓存数据
*/
restore() {
this.shortcutMap = this.shortcutMapCache
this.shortcutMapCache = {}
}
/**
* @Author: 王林
* @Date: 2021-04-24 15:23:22
@@ -26,6 +68,9 @@ export default class KeyCommand {
*/
bindEvent() {
window.addEventListener('keydown', (e) => {
if (this.isPause) {
return
}
Object.keys(this.shortcutMap).forEach((key) => {
if (this.checkKey(e, key)) {
e.stopPropagation()
@@ -139,4 +184,17 @@ export default class KeyCommand {
}
})
}
/**
* @Author: 王林
* @Date: 2022-08-14 08:49:58
* @Desc: 获取指定快捷键的处理函数
*/
getShortcutFn(key) {
let res = []
key.split(/\s*\|\s*/).forEach((item) => {
res = this.shortcutMap[item] || []
})
return res
}
}

View File

@@ -1,4 +1,5 @@
import Style from './Style'
import Shape from './Shape'
import {
resizeImgSize,
asyncRun
@@ -43,8 +44,17 @@ class Node {
this.themeConfig = this.mindMap.themeConfig
// 样式实例
this.style = new Style(this, this.themeConfig)
// 形状实例
this.shapeInstance = new Shape(this)
this.shapePadding = {
paddingX: 0,
paddingY: 0
}
// 是否是根节点
this.isRoot = opt.isRoot === undefined ? false : opt.isRoot
// 是否是概要节点
this.isGeneralization = opt.isGeneralization === undefined ? false : opt.isGeneralization
this.generalizationBelongNode = null
// 节点层级
this.layerIndex = opt.layerIndex === undefined ? 0 : opt.layerIndex
// 节点宽
@@ -52,9 +62,12 @@ class Node {
// 节点高
this.height = opt.height || 0
// left
this.left = opt.left || 0
this._left = opt.left || 0
// top
this.top = opt.top || 0
this._top = opt.top || 0
// 自定义位置
this.customLeft = opt.data.data.customLeft || undefined
this.customTop = opt.data.data.customTop || undefined
// 是否正在拖拽中
this.isDrag = false
// 父节点
@@ -73,6 +86,8 @@ class Node {
this.noteEl = null
this._expandBtn = null
this._lines = []
this._generalizationLine = null
this._generalizationNode = null
// 尺寸信息
this._rectInfo = {
imgContentWidth: 0,
@@ -80,6 +95,9 @@ class Node {
textContentWidth: 0,
textContentHeight: 0
}
// 概要节点的宽高
this._generalizationNodeWidth = 0
this._generalizationNodeHeight = 0
// 各种文字信息的间距
this.textContentItemMargin = this.mindMap.opt.textContentMargin
// 图片和文字节点的间距
@@ -89,10 +107,27 @@ class Node {
// 初始渲染
this.initRender = true
// 初始化
this.createNodeData()
// this.createNodeData()
this.getSize()
}
// 支持自定义位置
get left() {
return this.customLeft || this._left
}
set left(val) {
this._left = val
}
get top() {
return this.customTop || this._top
}
set top(val) {
this._top = val
}
/**
* @Author: 王林
* @Date: 2021-07-12 07:40:47
@@ -131,6 +166,33 @@ class Node {
return data
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-02 19:53:40
* @Desc: 检查节点是否存在自定义数据
*/
hasCustomPosition() {
return this.customLeft !== undefined && this.customTop !== undefined
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-04 09:06:56
* @Desc: 检查节点是否存在自定义位置的祖先节点
*/
ancestorHasCustomPosition() {
let node = this
while(node) {
if (node.hasCustomPosition()) {
return true
}
node = node.parent
}
return false
}
/**
* javascript comment
* @Author: 王林25
@@ -153,6 +215,7 @@ class Node {
this._hyperlinkData = this.createHyperlinkNode()
this._tagData = this.createTagNode()
this._noteData = this.createNoteNode()
this.createGeneralizationNode()
}
/**
@@ -199,6 +262,8 @@ class Node {
this.group.remove()
this.group = null
}
// 概要
this.removeGeneralization()
}
/**
@@ -273,9 +338,16 @@ class Node {
// 间距
let margin = imgContentHeight > 0 && textContentHeight > 0 ? this.blockContentMargin : 0
let { paddingX, paddingY } = this.getPaddingVale()
// 纯内容宽高
let _width = Math.max(imgContentWidth, textContentWidth)
let _height = imgContentHeight + textContentHeight
// 计算节点形状需要的附加内边距
let { paddingX: shapePaddingX, paddingY: shapePaddingY } = this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
this.shapePadding.paddingX = shapePaddingX
this.shapePadding.paddingY = shapePaddingY
return {
width: Math.max(imgContentWidth, textContentWidth) + paddingX * 2,
height: imgContentHeight + textContentHeight + paddingY * 2 + margin
width: _width + paddingX * 2 + shapePaddingX * 2,
height: _height + paddingY * 2 + margin + shapePaddingY * 2
}
}
@@ -484,6 +556,16 @@ class Node {
}
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 22:02:07
* @Desc: 获取节点形状
*/
getShape() {
return this.style.getStyle('shape', false, false)
}
/**
* javascript comment
* @Author: 王林25
@@ -497,12 +579,18 @@ class Node {
textContentItemMargin
} = this
let { paddingY } = this.getPaddingVale()
paddingY += this.shapePadding.paddingY
// 创建组
this.group = new G()
// 概要节点添加一个带所属节点id的类名
if (this.isGeneralization && this.generalizationBelongNode) {
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
}
this.draw.add(this.group)
this.update(true)
// 节点
this.style.rect(this.group.rect(width, height))
// 节点形
const shape = this.getShape()
this.style[shape === 'rectangle' ? 'rect' : 'shape'](this.shapeInstance.createShape())
// 图片节点
let imgHeight = 0
if (this._imgData) {
@@ -584,7 +672,7 @@ class Node {
})
// 右键菜单事件
this.group.on('contextmenu', (e) => {
if (this.mindMap.opt.readonly) {
if (this.mindMap.opt.readonly || this.isGeneralization) {
return
}
e.stopPropagation()
@@ -646,6 +734,7 @@ class Node {
} else {
this.updateExpandBtnPos()
}
this.renderGeneralization()
let t = this.group.transform()
if (!layout) {
this.group.animate(300).translate(this.left - t.translateX, this.top - t.translateY)
@@ -708,6 +797,7 @@ class Node {
*/
hide() {
this.group.hide()
this.hideGeneralization()
if (this.parent) {
let index = this.parent.children.indexOf(this)
this.parent._lines[index].hide()
@@ -728,8 +818,12 @@ class Node {
* @Date: 2021-11-23 18:39:14
* @Desc: 显示节点
*/
show() {
show() {
if (!this.group) {
return;
}
this.group.show()
this.showGeneralization()
if (this.parent) {
let index = this.parent.children.indexOf(this)
this.parent._lines[index].show()
@@ -786,6 +880,136 @@ class Node {
this._lines = []
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-01 09:27:30
* @Desc: 检查是否存在概要
*/
checkHasGeneralization() {
return !!this.nodeData.data.generalization
}
/**
* @Author: 王林
* @Date: 2022-07-31 09:41:28
* @Desc: 创建概要节点
*/
createGeneralizationNode() {
if (this.isGeneralization || !this.checkHasGeneralization()) {
return
}
if (!this._generalizationLine) {
this._generalizationLine = this.draw.path()
}
if (!this._generalizationNode) {
this._generalizationNode = new Node({
data: {
data: this.nodeData.data.generalization
},
uid: this.mindMap.uid++,
renderer: this.renderer,
mindMap: this.mindMap,
draw: this.draw,
isGeneralization: true
})
this._generalizationNodeWidth = this._generalizationNode.width
this._generalizationNodeHeight = this._generalizationNode.height
this._generalizationNode.generalizationBelongNode = this
if (this.nodeData.data.generalization.isActive) {
this.renderer.addActiveNode(this._generalizationNode)
}
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-01 15:38:52
* @Desc: 更新概要节点
*/
updateGeneralization() {
this.removeGeneralization()
this.createGeneralizationNode()
}
/**
* @Author: 王林
* @Date: 2022-07-30 08:35:51
* @Desc: 渲染概要节点
*/
renderGeneralization() {
if (this.isGeneralization) {
return
}
if (!this.checkHasGeneralization()) {
this.removeGeneralization()
this._generalizationNodeWidth = 0
this._generalizationNodeHeight = 0
return
}
if (this.nodeData.data.expand === false) {
this.removeGeneralization()
return
}
this.createGeneralizationNode()
this.renderer.layout.renderGeneralization(this, this._generalizationLine, this._generalizationNode)
this.style.generalizationLine(this._generalizationLine)
this._generalizationNode.render()
}
/**
* @Author: 王林
* @Date: 2022-07-30 13:11:27
* @Desc: 删除概要节点
*/
removeGeneralization() {
if (this._generalizationLine) {
this._generalizationLine.remove()
this._generalizationLine = null
}
if (this._generalizationNode) {
// 删除概要节点时要同步从激活节点里删除
this.renderer.removeActiveNode(this._generalizationNode)
this._generalizationNode.remove()
this._generalizationNode = null
}
// hack修复当激活一个节点时创建概要然后立即激活创建的概要节点后会重复创建概要节点并且无法删除的问题
if (this.generalizationBelongNode) {
this.draw.find('.generalization_' + this.generalizationBelongNode.uid).remove()
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-01 09:56:46
* @Desc: 隐藏概要节点
*/
hideGeneralization() {
if (this._generalizationLine) {
this._generalizationLine.hide()
}
if (this._generalizationNode) {
this._generalizationNode.hide()
}
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-01 09:57:42
* @Desc: 显示概要节点
*/
showGeneralization() {
if (this._generalizationLine) {
this._generalizationLine.show()
}
if (this._generalizationNode) {
this._generalizationNode.show()
}
}
/**
* @Author: 王林
* @Date: 2021-07-10 17:59:14
@@ -806,7 +1030,7 @@ class Node {
node.x(0).y(-this.expandBtnSize / 2)
fillNode.x(0).y(-this.expandBtnSize / 2)
this.style.iconBtn(node, fillNode)
this._expandBtn.add(fillNode).add(node)
if (this._expandBtn) this._expandBtn.add(fillNode).add(node)
}
/**
@@ -1007,6 +1231,16 @@ class Node {
setTag(tag) {
this.mindMap.execCommand('SET_NODE_TAG', this, tag)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 21:47:45
* @Desc: 设置形状
*/
setShape(shape) {
this.mindMap.execCommand('SET_NODE_SHAPE', this, shape)
}
}
export default Node

View File

@@ -5,6 +5,7 @@ import CatalogOrganization from './layouts/CatalogOrganization'
import OrganizationStructure from './layouts/OrganizationStructure'
import TextEdit from './TextEdit'
import { copyNodeTree, simpleDeepClone, walk } from './utils'
import { shapeList } from './Shape';
// 布局列表
const layouts = {
@@ -163,6 +164,21 @@ class Render {
// 设置节点标签
this.setNodeTag = this.setNodeTag.bind(this)
this.mindMap.command.add('SET_NODE_TAG', this.setNodeTag)
// 添加节点概要
this.addGeneralization = this.addGeneralization.bind(this)
this.mindMap.command.add('ADD_GENERALIZATION', this.addGeneralization)
// 删除节点概要
this.removeGeneralization = this.removeGeneralization.bind(this)
this.mindMap.command.add('REMOVE_GENERALIZATION', this.removeGeneralization)
// 设置节点自定义位置
this.setNodeCustomPosition = this.setNodeCustomPosition.bind(this)
this.mindMap.command.add('SET_NODE_CUSTOM_POSITION', this.setNodeCustomPosition)
// 一键整理布局
this.resetLayout = this.resetLayout.bind(this)
this.mindMap.command.add('RESET_LAYOUT', this.resetLayout)
// 设置节点形状
this.setNodeShape = this.setNodeShape.bind(this)
this.mindMap.command.add('SET_NODE_SHAPE', this.setNodeShape)
}
/**
@@ -183,15 +199,11 @@ class Render {
this.mindMap.execCommand('INSERT_NODE')
}
this.mindMap.keyCommand.addShortcut('Enter', this.insertNodeWrap)
// 插入概要
this.mindMap.keyCommand.addShortcut('Control+s', this.addGeneralization)
// 展开/收起节点
this.mindMap.keyCommand.addShortcut('/', () => {
this.activeNodeList.forEach((node) => {
if (node.nodeData.children.length <= 0) {
return
}
this.toggleNodeExpand(node)
})
})
this.toggleActiveExpand = this.toggleActiveExpand.bind(this)
this.mindMap.keyCommand.addShortcut('/', this.toggleActiveExpand)
// 删除节点
this.removeNodeWrap = () => {
this.mindMap.execCommand('REMOVE_NODE')
@@ -208,6 +220,13 @@ class Render {
this.mindMap.keyCommand.addShortcut('Control+a', () => {
this.mindMap.execCommand('SELECT_ALL')
})
// 一键整理布局
this.mindMap.keyCommand.addShortcut('Control+l', this.resetLayout)
// 上移节点
this.mindMap.keyCommand.addShortcut('Control+Up', this.upNode)
// 下移节点
this.mindMap.keyCommand.addShortcut('Control+Down', this.downNode)
// 复制节点、剪切节点、粘贴节点的快捷键需开发者自行注册实现可参考demo
}
/**
@@ -217,8 +236,10 @@ class Render {
* @Desc: 开启文字编辑,会禁用回车键和删除键相关快捷键防止冲突
*/
startTextEdit() {
this.mindMap.keyCommand.removeShortcut('Del|Backspace')
this.mindMap.keyCommand.removeShortcut('Enter', this.insertNodeWrap)
this.mindMap.keyCommand.save()
// this.mindMap.keyCommand.removeShortcut('Del|Backspace')
// this.mindMap.keyCommand.removeShortcut('/')
// this.mindMap.keyCommand.removeShortcut('Enter', this.insertNodeWrap)
}
/**
@@ -228,8 +249,10 @@ class Render {
* @Desc: 结束文字编辑,会恢复回车键和删除键相关快捷键
*/
endTextEdit() {
this.mindMap.keyCommand.addShortcut('Del|Backspace', this.removeNodeWrap)
this.mindMap.keyCommand.addShortcut('Enter', this.insertNodeWrap)
this.mindMap.keyCommand.restore()
// this.mindMap.keyCommand.addShortcut('Del|Backspace', this.removeNodeWrap)
// this.mindMap.keyCommand.addShortcut('/', this.toggleActiveExpand)
// this.mindMap.keyCommand.addShortcut('Enter', this.insertNodeWrap)
}
/**
@@ -246,6 +269,7 @@ class Render {
this.root = root
this.root.render()
})
this.mindMap.emit('node_active', null, this.activeNodeList)
}
/**
@@ -379,13 +403,14 @@ class Render {
if (first.isRoot) {
this.insertChildNode()
} else {
let text = first.layerIndex === 1 ? '二级节点' : '分支主题'
if (first.layerIndex === 1) {
first.parent.initRender = true
}
let index = this.getNodeIndex(first)
first.parent.nodeData.children.splice(index + 1, 0, {
"data": {
"text": "分支主题",
"text": text,
"expand": true
},
"children": []
@@ -407,9 +432,10 @@ class Render {
if (!node.nodeData.children) {
node.nodeData.children = []
}
let text = node.isRoot ? '二级节点' : '分支主题'
node.nodeData.children.push({
"data": {
"text": "分支主题",
"text": text,
"expand": true
},
"children": []
@@ -579,7 +605,15 @@ class Render {
}
for (let i = 0; i < this.activeNodeList.length; i++) {
let node = this.activeNodeList[i]
if (node.isRoot) {
if (node.isGeneralization) {
// 删除概要节点
this.setNodeData(node.generalizationBelongNode, {
generalization: null
})
node.generalizationBelongNode.update()
this.removeActiveNode(node)
i--
} else if (node.isRoot) {
node.children.forEach((child) => {
child.remove()
})
@@ -769,6 +803,20 @@ class Render {
}, null, true, 0, 0)
}
/**
* @Author: 王林
* @Date: 2022-08-14 09:18:40
* @Desc: 切换激活节点的展开状态
*/
toggleActiveExpand() {
this.activeNodeList.forEach((node) => {
if (node.nodeData.children.length <= 0) {
return
}
this.toggleNodeExpand(node)
})
}
/**
* @Author: 王林
* @Date: 2021-07-11 17:15:33
@@ -855,6 +903,100 @@ class Render {
})
}
/**
* @Author: 王林
* @Date: 2022-07-30 20:52:42
* @Desc: 添加节点概要
*/
addGeneralization(data) {
if (this.activeNodeList.length <= 0) {
return
}
this.activeNodeList.forEach((node) => {
if (node.nodeData.data.generalization || node.isRoot) {
return
}
this.setNodeData(node, {
generalization: data || {
text: '概要'
}
})
node.update()
})
this.mindMap.render()
}
/**
* @Author: 王林
* @Date: 2022-07-30 21:16:33
* @Desc: 删除节点概要
*/
removeGeneralization() {
if (this.activeNodeList.length <= 0) {
return
}
this.activeNodeList.forEach((node) => {
if (!node.nodeData.data.generalization) {
return
}
this.setNodeData(node, {
generalization: null
})
node.update()
})
this.mindMap.render()
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-02 19:04:24
* @Desc: 设置节点自定义位置
*/
setNodeCustomPosition(node, left = undefined, top = undefined) {
let nodeList = [node] || this.activeNodeList
nodeList.forEach((item) => {
this.setNodeData(item, {
customLeft: left,
customTop: top
})
})
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-02 20:02:50
* @Desc: 一键整理布局,即去除自定义位置
*/
resetLayout() {
walk(this.root, null, (node) => {
node.customLeft = undefined
node.customTop = undefined
this.setNodeData(node, {
customLeft: undefined,
customTop: undefined
})
this.mindMap.render()
}, null, true, 0, 0)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 21:44:01
* @Desc: 设置节点形状
*/
setNodeShape(node, shape) {
if (!shape || !shapeList.includes(shape)) {
return
}
let nodeList = [node] || this.activeNodeList
nodeList.forEach((item) => {
this.setNodeStyle(item, 'shape', shape)
})
}
/**
* @Author: 王林
* @Date: 2021-05-04 14:19:48
@@ -876,6 +1018,10 @@ class Render {
let changed = node.getSize()
node.renderNode()
if (changed) {
if (node.isGeneralization) {
// 概要节点
node.generalizationBelongNode.updateGeneralization()
}
this.mindMap.render()
}
}

View File

@@ -34,7 +34,7 @@ class Select {
if (this.mindMap.opt.readonly) {
return
}
if (e.which !== 3) {
if (!e.ctrlKey && e.which !== 3) {
return
}
this.isMousedown = true

View File

@@ -0,0 +1,265 @@
/**
* @Author: 王林
* @Date: 2022-08-22 21:32:50
* @Desc: 节点形状类
*/
export default class Shape {
constructor(node) {
this.node = node
}
/**
* @Author: 王林
* @Date: 2022-08-17 22:32:32
* @Desc: 形状需要的padding
*/
getShapePadding(width, height, paddingX, paddingY) {
const shape = this.node.getShape()
const defaultPaddingX = 15
const defaultPaddingY = 5
const actWidth = width + paddingX * 2
const actHeight = height + paddingY * 2
const actOffset = Math.abs(actWidth - actHeight)
switch (shape) {
case 'roundedRectangle':
return {
paddingX: height > width ? (height - width) / 2 : 0,
paddingY: 0
}
case 'diamond':
return {
paddingX: width / 2,
paddingY: height / 2
}
case 'parallelogram':
return {
paddingX: paddingX <= 0 ? defaultPaddingX : 0,
paddingY: 0
}
case 'outerTriangularRectangle':
return {
paddingX: paddingX <= 0 ? defaultPaddingX : 0,
paddingY: 0
}
case 'innerTriangularRectangle':
return {
paddingX: paddingX <= 0 ? defaultPaddingX : 0,
paddingY: 0
}
case 'ellipse':
return {
paddingX: paddingX <= 0 ? defaultPaddingX : 0,
paddingY: paddingY <= 0 ? defaultPaddingY : 0
}
case 'circle':
return {
paddingX: actHeight > actWidth ? actOffset / 2 : 0,
paddingY: actHeight < actWidth ? actOffset / 2 : 0,
}
default:
return {
paddingX: 0,
paddingY: 0
}
}
}
/**
* @Author: 王林
* @Date: 2022-08-17 22:22:53
* @Desc: 创建形状节点
*/
createShape() {
const shape = this.node.getShape()
let { width, height } = this.node
let node = null
// 矩形
if (shape === 'rectangle') {
node = this.node.group.rect(width, height)
} else if (shape === 'diamond') {
// 菱形
node = this.createDiamond()
} else if (shape === 'parallelogram') {
// 平行四边形
node = this.createParallelogram()
} else if (shape === 'roundedRectangle') {
// 圆角矩形
node = this.createRoundedRectangle()
} else if (shape === 'octagonalRectangle') {
// 八角矩形
node = this.createOctagonalRectangle()
} else if (shape === 'outerTriangularRectangle') {
// 外三角矩形
node = this.createOuterTriangularRectangle()
} else if (shape === 'innerTriangularRectangle') {
// 内三角矩形
node = this.createInnerTriangularRectangle()
} else if (shape === 'ellipse') {
// 椭圆
node = this.createEllipse()
} else if (shape === 'circle') {
// 圆
node = this.createCircle()
}
return node
}
/**
* @Author: 王林
* @Date: 2022-09-04 09:08:54
* @Desc: 创建菱形
*/
createDiamond() {
let { width, height } = this.node
let halfWidth = width / 2
let halfHeight = height / 2
let topX = halfWidth
let topY = 0
let rightX = width
let rightY = halfHeight
let bottomX = halfWidth
let bottomY = height
let leftX = 0
let leftY = halfHeight
return this.node.group.polygon(`
${topX}, ${topY}
${rightX}, ${rightY}
${bottomX}, ${bottomY}
${leftX}, ${leftY}
`)
}
/**
* @Author: 王林
* @Date: 2022-09-03 16:14:12
* @Desc: 创建平行四边形
*/
createParallelogram() {
let { paddingX } = this.node.getPaddingVale()
paddingX = paddingX || this.node.shapePadding.paddingX
let { width, height } = this.node
return this.node.group.polygon(`
${paddingX}, ${0}
${width}, ${0}
${width - paddingX}, ${height}
${0}, ${height}
`)
}
/**
* @Author: 王林
* @Date: 2022-09-03 16:50:23
* @Desc: 创建圆角矩形
*/
createRoundedRectangle() {
let { width, height } = this.node
let halfHeight = height / 2
return this.node.group.path(`
M${halfHeight},0
L${width - halfHeight},0
A${height / 2},${height / 2} 0 0,1 ${width - halfHeight},${height}
L${halfHeight},${height}
A${height / 2},${height / 2} 0 0,1 ${halfHeight},${0}
`)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 16:14:08
* @Desc: 创建八角矩形
*/
createOctagonalRectangle() {
let w = 5
let { width, height } = this.node
return this.node.group.polygon(`
${0}, ${w}
${w}, ${0}
${width - w}, ${0}
${width}, ${w}
${width}, ${height - w}
${width - w}, ${height}
${w}, ${height}
${0}, ${height - w}
`)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 20:55:50
* @Desc: 创建外三角矩形
*/
createOuterTriangularRectangle() {
let { paddingX } = this.node.getPaddingVale()
paddingX = paddingX || this.node.shapePadding.paddingX
let { width, height } = this.node
return this.node.group.polygon(`
${paddingX}, ${0}
${width - paddingX}, ${0}
${width}, ${height / 2}
${width - paddingX}, ${height}
${paddingX}, ${height}
${0}, ${height / 2}
`)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 20:59:37
* @Desc: 创建内三角矩形
*/
createInnerTriangularRectangle() {
let { paddingX } = this.node.getPaddingVale()
paddingX = paddingX || this.node.shapePadding.paddingX
let { width, height } = this.node
return this.node.group.polygon(`
${0}, ${0}
${width}, ${0}
${width - paddingX / 2}, ${height / 2}
${width}, ${height}
${0}, ${height}
${paddingX / 2}, ${height / 2}
`)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 21:06:31
* @Desc: 创建椭圆
*/
createEllipse() {
let { width, height } = this.node
let halfWidth = width / 2
let halfHeight = height / 2
return this.node.group.path(`
M${halfWidth},0
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height}
M${halfWidth},${height}
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0}
`)
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 21:14:04
* @Desc: 创建圆
*/
createCircle() {
let { width, height } = this.node
let halfWidth = width / 2
let halfHeight = height / 2
return this.node.group.path(`
M${halfWidth},0
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${height}
M${halfWidth},${height}
A${halfWidth},${halfHeight} 0 0,1 ${halfWidth},${0}
`)
}
}
// 形状列表
export const shapeList = ['rectangle', 'diamond', 'parallelogram', 'roundedRectangle', 'octagonalRectangle', 'outerTriangularRectangle', 'innerTriangularRectangle', 'ellipse', 'circle']

View File

@@ -50,6 +50,8 @@ class Style {
let defaultConfig = this.themeConfig.node
if (root || rootProp.includes(prop)) {// 直接使用最外层样式
defaultConfig = this.themeConfig
} else if (this.ctx.isGeneralization) {// 概要节点
defaultConfig = this.themeConfig.generalization
} else if (this.ctx.layerIndex === 0) {// 根节点
defaultConfig = this.themeConfig.root
} else if (this.ctx.layerIndex === 1) {// 二级节点
@@ -67,19 +69,40 @@ class Style {
return this.ctx.nodeData.data[prop] !== undefined ? this.ctx.nodeData.data[prop] : defaultConfig[prop]
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 21:55:57
* @Desc: 获取某个样式值
*/
getStyle(prop, root, isActive) {
return this.merge(prop, root, isActive)
}
/**
* @Author: 王林
* @Date: 2021-04-11 10:12:56
* @Desc: 矩形
*/
rect(node) {
this.shape(node)
node.radius(this.merge('borderRadius'))
}
/**
* javascript comment
* @Author: 王林
* @Date: 2022-09-12 15:04:28
* @Desc: 矩形外的其他形状
*/
shape(node) {
node.fill({
color: this.merge('fillColor')
}).stroke({
color: this.merge('borderColor'),
width: this.merge('borderWidth'),
dasharray: this.merge('borderDasharray')
}).radius(this.merge('borderRadius'))
})
}
/**
@@ -154,6 +177,15 @@ class Style {
node.stroke({ width: this.merge('lineWidth', true), color: this.merge('lineColor', true) }).fill({ color: 'none' })
}
/**
* @Author: 王林
* @Date: 2022-07-30 16:19:03
* @Desc: 概要连线
*/
generalizationLine(node) {
node.stroke({ width: this.merge('generalizationLineWidth', true), color: this.merge('generalizationLineColor', true) }).fill({ color: 'none' })
}
/**
* @Author: 王林
* @Date: 2021-04-11 20:03:59

View File

@@ -47,10 +47,6 @@ export default class TextEdit {
this.mindMap.on('before_node_active', () => {
this.hideEditTextBox()
})
// 注册回车快捷键
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.hideEditTextBox()
})
// 注册编辑快捷键
this.mindMap.keyCommand.addShortcut('F2', () => {
if (this.renderer.activeNodeList.length <= 0) {
@@ -60,6 +56,19 @@ export default class TextEdit {
})
}
/**
* javascript comment
* @Author: 王林25
* @Date: 2022-08-16 16:27:02
* @Desc: 注册临时快捷键
*/
registerTmpShortcut() {
// 注册回车快捷键
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.hideEditTextBox()
})
}
/**
* @Author: 王林
* @Date: 2021-04-13 22:15:56
@@ -76,6 +85,7 @@ export default class TextEdit {
*/
showEditTextBox(node, rect) {
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: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none;`
@@ -119,6 +129,10 @@ export default class TextEdit {
this.renderer.activeNodeList.forEach((node) => {
let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
this.mindMap.execCommand('SET_NODE_TEXT', node, str)
if (node.isGeneralization) {
// 概要节点
node.generalizationBelongNode.updateGeneralization()
}
this.mindMap.render()
})
this.mindMap.emit('hide_text_edit', this.textEditNode, this.renderer.activeNodeList)

View File

@@ -50,6 +50,10 @@ class View {
this.sy = this.y
})
this.mindMap.event.on('drag', (e, event) => {
if (e.ctrlKey) {
// 按住ctrl键拖动为多选
return
}
if (this.firstDrag) {
this.firstDrag = false
// 清除激活节点

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,4 +1,7 @@
import Node from '../Node'
import {
walk,
} from '../utils'
/**
* @Author: 王林
@@ -49,6 +52,13 @@ class Base {
throw new Error('【renderExpandBtn】方法为必要方法需要子类进行重写')
}
/**
* @Author: 王林
* @Date: 2022-07-30 22:49:28
* @Desc: 概要节点
*/
renderGeneralization() {}
/**
* @Author: 王林
* @Date: 2021-07-10 21:30:54
@@ -109,7 +119,7 @@ class Base {
updateChildren(children, prop, offset) {
children.forEach((item) => {
item[prop] += offset
if (item.children && item.children.length) {
if (item.children && item.children.length && !item.hasCustomPosition()) {// 适配自定义位置
this.updateChildren(item.children, prop, offset)
}
})
@@ -156,6 +166,83 @@ class Base {
getMarginY(layerIndex) {
return layerIndex === 1 ? this.mindMap.themeConfig.second.marginY : this.mindMap.themeConfig.node.marginY;
}
/**
* @Author: 王林
* @Date: 2022-07-31 20:53:12
* @Desc: 获取节点包括概要在内的宽度
*/
getNodeWidthWithGeneralization(node) {
return Math.max(node.width, node.checkHasGeneralization() ? node._generalizationNodeWidth : 0)
}
/**
* @Author: 王林
* @Date: 2022-07-31 20:53:12
* @Desc: 获取节点包括概要在内的高度
*/
getNodeHeightWithGeneralization(node) {
return Math.max(node.height, node.checkHasGeneralization() ? node._generalizationNodeHeight : 0)
}
/**
* @Author: 王林
* @Date: 2022-07-31 09:14:03
* @Desc: 获取节点的边界值
* dir生长方向h水平、v垂直
* isLeft是否向左生长
*/
getNodeBoundaries(node, dir, isLeft) {
let { generalizationLineMargin, generalizationNodeMargin } = this.mindMap.themeConfig
let walk = (root) => {
let _left = Infinity
let _right = -Infinity
let _top = Infinity
let _bottom = -Infinity
if (root.children && root.children.length > 0) {
root.children.forEach((child) => {
let {left, right, top, bottom} = walk(child)
// 概要内容的宽度
let generalizationWidth = child.checkHasGeneralization() && child.nodeData.data.expand ? child._generalizationNodeWidth + generalizationNodeMargin : 0
// 概要内容的高度
let generalizationHeight = child.checkHasGeneralization() && child.nodeData.data.expand ? child._generalizationNodeHeight + generalizationNodeMargin : 0
if (left - (dir === 'h' ? generalizationWidth : 0) < _left) {
_left = left - (dir === 'h' ? generalizationWidth : 0)
}
if (right + (dir === 'h' ? generalizationWidth : 0) > _right) {
_right = right + (dir === 'h' ? generalizationWidth : 0)
}
if (top < _top) {
_top = top
}
if (bottom + (dir === 'v' ? generalizationHeight : 0) > _bottom) {
_bottom = bottom + (dir === 'v' ? generalizationHeight : 0)
}
})
}
let cur = {
left: root.left,
right: root.left + root.width,
top: root.top,
bottom: root.top + root.height
}
return {
left: cur.left < _left ? cur.left : _left,
right: cur.right > _right ? cur.right : _right,
top: cur.top < _top ? cur.top : _top,
bottom: cur.bottom > _bottom ? cur.bottom : _bottom
}
}
let {left, right, top, bottom} = walk(node)
return {
left,
right,
top,
bottom,
generalizationLineMargin,
generalizationNodeMargin
};
}
}
export default Base

View File

@@ -165,15 +165,18 @@ class CatalogOrganization extends Base {
let index = childrenList.findIndex((item) => {
return item === node
})
// 第一个或最后一个节点自身也需要移动,否则两边不对称
if (index === 0 || index === childrenList.length - 1) {
// 存在大于一个节点时,第一个或最后一个节点自身也需要移动,否则两边不对称
if ((index === 0 || index === childrenList.length - 1) && childrenList.length > 1) {
let _offset = index === 0 ? -addWidth : addWidth
node.left += _offset
if (node.children && node.children.length) {
if (node.children && node.children.length && !node.hasCustomPosition()) {
this.updateChildren(node.children, 'left', _offset)
}
}
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {// 适配自定义位置
return
}
let _offset = 0
if (_index < index) { // 左边的节点往左移
_offset = -addWidth
@@ -204,6 +207,9 @@ class CatalogOrganization extends Base {
return item === node
})
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {// 适配自定义位置
return
}
let _offset = 0
// 下面的节点往下移
if (_index > index) {
@@ -239,44 +245,80 @@ class CatalogOrganization extends Base {
let len = node.children.length
let marginX = this.getMarginX(node.layerIndex + 1)
if (node.isRoot) {
// 根节点
let x1 = left + width / 2
let y1 = top + height
let s1 = marginX * 0.7
let minx = 0
let maxx = 0
let minx = Infinity
let maxx = -Infinity
node.children.forEach((item, index) => {
let x2 = item.left +item.width / 2
let y2 = item.top
if (index === 0) {
if (x2 < minx) {
minx = x2
} else if (index >= len - 1) {
}
if (x2 > maxx) {
maxx = x2
}
let path = `M ${x2},${y1 + s1} L ${x2},${y2}`
let path = `M ${x2},${y1 + s1} L ${x2},${y1 + s1 > y2 ? y2 + item.height : y2}`
// 竖线
lines[index].plot(path)
})
minx = Math.min(minx, x1)
maxx = Math.max(maxx, x1)
// 父节点的竖线
let line1 = this.draw.path()
node.style.line(line1)
line1.plot(`M ${x1},${y1} L ${x1},${y1 + s1}`)
node._lines.push(line1)
// 水平线
if (len > 1) {
if (len > 0) {
let lin2 = this.draw.path()
node.style.line(lin2)
lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)
node._lines.push(lin2)
}
} else {
// 非根节点
let y1 = top + height
let maxy = 0
let maxy = -Infinity
let x2 = node.left + node.width * 0.3
node.children.forEach((item, index) => {
// 为了适配自定义位置,下面做了各种位置的兼容
let y2 = item.top + item.height / 2
if (index >= len - 1) {
if (y2 > maxy) {
maxy = y2
}
let path = `M ${x2},${y2} L ${x2 + node.width * 0.2},${y2}`
// 水平线
let path = ''
let _left = item.left
let _isLeft = item.left + item.width < x2
let _isXCenter = false
if (_isLeft) {
// 水平位置在父节点左边
_left = item.left + item.width
} else if (item.left < x2 && item.left + item.width > x2) {
// 水平位置在父节点之间
_isXCenter = true
y2 = item.top
maxy = y2
}
if (y2 > top && y2 < y1) {
// 自定义位置的情况:垂直位置节点在父节点之间
path = `M ${_isLeft ? node.left : node.left + node.width},${y2} L ${_left},${y2}`
} else if (y2 < y1) {
// 自定义位置的情况:垂直位置节点在父节点上面
if (_isXCenter) {
y2 = item.top + item.height
_left = x2
}
path = `M ${x2},${top} L ${x2},${y2} L ${_left},${y2}`
} else {
if (_isXCenter) {
_left = x2
}
path = `M ${x2},${y2} L ${_left},${y2}`
}
lines[index].plot(path)
})
// 竖线
@@ -284,7 +326,12 @@ class CatalogOrganization extends Base {
let lin2 = this.draw.path()
expandBtnSize = len > 0 ? expandBtnSize : 0
node.style.line(lin2)
lin2.plot(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`)
if (maxy < y1 + expandBtnSize) {
lin2.hide()
} else {
lin2.plot(`M ${x2},${y1 + expandBtnSize} L ${x2},${maxy}`)
lin2.show()
}
node._lines.push(lin2)
}
}
@@ -310,6 +357,25 @@ class CatalogOrganization extends Base {
btn.translate(width * 0.3 - expandBtnSize / 2 - translateX, height + expandBtnSize / 2 - translateY)
}
}
/**
* @Author: 王林
* @Date: 2022-07-30 08:30:35
* @Desc: 创建概要节点
*/
renderGeneralization(node, gLine, gNode) {
let { top, bottom, right, generalizationLineMargin, generalizationNodeMargin } = this.getNodeBoundaries(node, 'h')
let x1 = right + generalizationLineMargin
let y1 = top
let x2 = right + generalizationLineMargin
let y2 = bottom
let cx = x1 + 20
let cy = y1 + (y2 - y1) / 2
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
gLine.plot(path)
gNode.left = right + generalizationNodeMargin
gNode.top = top + (bottom - top - gNode.height) / 2
}
}
export default CatalogOrganization

View File

@@ -120,7 +120,7 @@ class LogicalStructure extends Base {
return item === node
})
childrenList.forEach((item, _index) => {
if (item === node) {
if (item === node || item.hasCustomPosition()) {// 适配自定义位置
return
}
let _offset = 0
@@ -188,6 +188,25 @@ class LogicalStructure extends Base {
} = btn.transform()
btn.translate(width - translateX, height / 2 - translateY)
}
/**
* @Author: 王林
* @Date: 2022-07-30 08:30:35
* @Desc: 创建概要节点
*/
renderGeneralization(node, gLine, gNode) {
let { top, bottom, right, generalizationLineMargin, generalizationNodeMargin } = this.getNodeBoundaries(node, 'h')
let x1 = right + generalizationLineMargin
let y1 = top
let x2 = right + generalizationLineMargin
let y2 = bottom
let cx = x1 + 20
let cy = y1 + (y2 - y1) / 2
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
gLine.plot(path)
gNode.left = right + generalizationNodeMargin
gNode.top = top + (bottom - top - gNode.height) / 2
}
}
export default LogicalStructure

View File

@@ -155,6 +155,9 @@ class MindMap extends Base {
return item === node
})
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {// 适配自定义位置
return
}
let _offset = 0
let addHeight = item.dir === 'left' ? leftAddHeight : rightAddHeight
// 上面的节点往上移
@@ -224,6 +227,27 @@ class MindMap extends Base {
let y = height / 2 - translateY
btn.translate(x, y)
}
/**
* @Author: 王林
* @Date: 2022-07-30 08:30:35
* @Desc: 创建概要节点
*/
renderGeneralization(node, gLine, gNode) {
let isLeft = node.dir === 'left'
let { top, bottom, left, right, generalizationLineMargin, generalizationNodeMargin } = this.getNodeBoundaries(node, 'h', isLeft)
let x = isLeft ? left - generalizationLineMargin : right + generalizationLineMargin
let x1 = x
let y1 = top
let x2 = x
let y2 = bottom
let cx = x1 + (isLeft ? -20 : 20)
let cy = y1 + (y2 - y1) / 2
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
gLine.plot(path)
gNode.left = x + (isLeft ? -generalizationNodeMargin : generalizationNodeMargin) - (isLeft ? gNode.width : 0)
gNode.top = top + (bottom - top - gNode.height) / 2
}
}
export default MindMap

View File

@@ -121,6 +121,9 @@ class OrganizationStructure extends Base {
return item === node
})
childrenList.forEach((item, _index) => {
if (item.hasCustomPosition()) {// 适配自定义位置
return
}
let _offset = 0
// 上面的节点往上移
if (_index < index) {
@@ -160,20 +163,23 @@ class OrganizationStructure extends Base {
let y1 = top + height
let marginX = this.getMarginX(node.layerIndex + 1)
let s1 = marginX * 0.7
let minx = 0
let maxx = 0
let minx = Infinity
let maxx = -Infinity
let len = node.children.length
node.children.forEach((item, index) => {
let x2 = item.left + item.width / 2
let y2 = item.top
if (index === 0) {
let y2 = y1 + s1 > item.top ? item.top + item.height : item.top
if (x2 < minx) {
minx = x2
} else if (index >= len - 1) {
}
if (x2 > maxx) {
maxx = x2
}
let path = `M ${x2},${y1 + s1} L ${x2},${y2}`
lines[index].plot(path)
})
minx = Math.min(x1, minx)
maxx = Math.max(x1, maxx)
// 父节点的竖线
let line1 = this.draw.path()
node.style.line(line1)
@@ -181,7 +187,7 @@ class OrganizationStructure extends Base {
line1.plot(`M ${x1},${y1 + expandBtnSize} L ${x1},${y1 + s1}`)
node._lines.push(line1)
// 水平线
if (len > 1) {
if (len > 0) {
let lin2 = this.draw.path()
node.style.line(lin2)
lin2.plot(`M ${minx},${y1 + s1} L ${maxx},${y1 + s1}`)
@@ -206,6 +212,25 @@ class OrganizationStructure extends Base {
} = btn.transform()
btn.translate(width / 2 - expandBtnSize / 2 - translateX, height + expandBtnSize / 2 - translateY)
}
/**
* @Author: 王林
* @Date: 2022-07-30 08:30:35
* @Desc: 创建概要节点
*/
renderGeneralization(node, gLine, gNode) {
let { bottom, left, right, generalizationLineMargin, generalizationNodeMargin } = this.getNodeBoundaries(node, 'v')
let x1 = left
let y1 = bottom + generalizationLineMargin
let x2 = right
let y2 = bottom + generalizationLineMargin
let cx = x1 + (x2 - x1) / 2
let cy = y1 + 20
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
gLine.plot(path)
gNode.top = bottom + generalizationNodeMargin
gNode.left = left + (right - left - gNode.width) / 2
}
}
export default OrganizationStructure

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(115, 161, 191)',
// 背景颜色
backgroundColor: 'rgb(251, 251, 251)',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#333',
// 根节点样式
root: {
fillColor: 'rgb(115, 161, 191)',
@@ -37,4 +41,13 @@ export default merge(defaultTheme, {
borderColor: 'rgb(57, 80, 96)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#333',
color: '#333',
active: {
borderColor: 'rgb(57, 80, 96)'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(191, 115, 148)',
// 背景颜色
backgroundColor: 'rgb(251, 251, 251)',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#333',
// 根节点样式
root: {
fillColor: 'rgb(191, 115, 148)',
@@ -36,5 +40,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(96, 57, 74)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#333',
color: '#333',
active: {
borderColor: 'rgb(96, 57, 74)'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: '#fff',
// 连线的粗细
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: '#fff',
// 背景颜色
backgroundColor: 'rgb(58, 65, 68)',
// 背景图片
@@ -49,5 +53,15 @@ export default merge(defaultTheme, {
fillColor: 'rgb(254, 219, 0)',
borderColor: 'transparent'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'transparent',
color: '#333',
active: {
fillColor: 'rgb(254, 219, 0)',
borderColor: 'transparent'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(51, 51, 51)',
// 连线的粗细
lineWidth: 2,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(51, 51, 51)',
// 背景颜色
backgroundColor: '#fff',
// 根节点样式
@@ -41,5 +45,15 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(51, 51, 51)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'rgb(51, 51, 51)',
borderWidth: 2,
color: '#1a1a1a',
active: {
borderColor: 'rgb(18, 187, 55)'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(94, 202, 110)',
// 连线的粗细
lineWidth: 2,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: '#1a1a1a',
// 背景颜色
backgroundColor: 'rgb(241, 241, 241)',
// 根节点样式
@@ -44,5 +48,15 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(94, 202, 110)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#1a1a1a',
color: '#1a1a1a',
borderWidth: 2,
active: {
borderColor: 'rgb(94, 202, 110)'
}
}
})

View File

@@ -0,0 +1,65 @@
import defaultTheme from './default';
import merge from 'deepmerge';
/**
* @Author: 王林
* @Date: 2021-04-11 15:22:18
* @Desc: 经典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,
active: {
borderColor: 'rgb(169, 218, 218)'
}
},
// 二级节点样式
second: {
fillColor: 'rgb(169, 218, 218)',
borderColor: 'rgb(30, 53, 86)',
borderWidth: 2,
color: '#fff',
fontSize: 18,
borderRadius: 10,
active: {
borderColor: 'rgb(56, 123, 233)'
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(30, 53, 86)',
borderColor: 'rgb(30, 53, 86)',
borderWidth: 1,
marginY: 20,
active: {
borderColor: 'rgb(169, 218, 218)'
}
},
// 概要节点样式
generalization: {
fillColor: 'rgb(56, 123, 233)',
borderColor: 'rgb(56, 123, 233)',
color: '#fff',
borderWidth: 0,
active: {
borderColor: 'rgb(169, 218, 218)'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(51, 51, 51)',
// 连线的粗细
lineWidth: 2,
// 概要连线的粗细
generalizationLineWidth: 2,
// 概要连线的颜色
generalizationLineColor: 'rgb(51, 51, 51)',
// 背景颜色
backgroundColor: 'rgb(239, 248, 250)',
// 根节点样式
@@ -39,5 +43,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(94, 199, 248)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'rgb(51, 51, 51)',
color: '#333',
active: {
borderColor: 'rgb(94, 199, 248)'
}
}
})

View File

@@ -11,6 +11,10 @@ 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)',
@@ -37,5 +41,15 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(94, 199, 248)'
}
},
// 概要节点样式
generalization: {
fillColor: 'rgb(123, 199, 120)',
borderColor: 'transparent',
borderWidth: 2,
color: '#fff',
active: {
borderColor: 'rgb(94, 199, 248)'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(17, 68, 23)',
// 连线的粗细
lineWidth: 2,
// 概要连线的粗细
generalizationLineWidth: 2,
// 概要连线的颜色
generalizationLineColor: '#fff',
// 背景颜色
backgroundColor: 'rgb(15, 16, 17)',
// 根节点样式
@@ -41,5 +45,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(17, 68, 23)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'transparent',
color: '#333',
active: {
borderColor: 'rgb(17, 68, 23)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(75, 81, 78)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(255, 119, 34)',
// 背景颜色
backgroundColor: 'rgb(27, 31, 34)',
// 根节点样式
@@ -42,5 +46,15 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(254, 199, 13)'
}
},
// 概要节点样式
generalization: {
fillColor: 'transparent',
borderColor: 'rgb(255, 119, 34)',
borderWidth: 2,
color: 'rgb(204, 204, 204)',
active: {
borderColor: 'rgb(254, 199, 13)'
}
}
})

View File

@@ -17,6 +17,14 @@ export default {
lineWidth: 1,
// 连线的颜色
lineColor: '#549688',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#549688',
// 概要曲线距节点的距离
generalizationLineMargin: 0,
// 概要节点距节点的距离
generalizationNodeMargin: 20,
// 背景颜色
backgroundColor: '#fafafa',
// 背景图片
@@ -25,6 +33,7 @@ export default {
backgroundRepeat: 'no-repeat',
// 根节点样式
root: {
shape: 'rectangle',
fillColor: '#549688',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#fff',
@@ -45,6 +54,7 @@ export default {
},
// 二级节点样式
second: {
shape: 'rectangle',
marginX: 100,
marginY: 40,
fillColor: '#fff',
@@ -67,6 +77,7 @@ export default {
},
// 三级及以下节点样式
node: {
shape: 'rectangle',
marginX: 50,
marginY: 0,
fillColor: 'transparent',
@@ -86,5 +97,32 @@ export default {
borderWidth: 3,
borderDasharray: 'none',
}
},
// 概要节点样式
generalization: {
shape: 'rectangle',
marginX: 100,
marginY: 40,
fillColor: '#fff',
fontFamily: '微软雅黑, Microsoft YaHei',
color: '#565656',
fontSize: 16,
fontWeight: 'noraml',
fontStyle: 'normal',
lineHeight: 1.5,
borderColor: '#549688',
borderWidth: 1,
borderDasharray: 'none',
borderRadius: 5,
textDecoration: 'none',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none',
}
}
}
}
// 支持激活样式的属性
// 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小
export const supportActiveStyle = ['fillColor', 'color', 'fontWeight', 'fontStyle', 'borderColor', 'borderWidth', 'borderDasharray', 'borderRadius', 'textDecoration']

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(191, 147, 115)',
// 背景颜色
backgroundColor: 'rgb(251, 251, 251)',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#333',
// 根节点样式
root: {
fillColor: 'rgb(191, 147, 115)',
@@ -36,5 +40,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(96, 73, 57)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#333',
color: '#333',
active: {
borderColor: 'rgb(96, 73, 57)'
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: '#333',
// 背景颜色
backgroundColor: '#d1f6ec',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#333',
// 根节点样式
root: {
fillColor: '#1fb27d'
@@ -22,4 +26,15 @@ export default merge(defaultTheme, {
borderColor: 'transparent',
borderWidth: 0
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#333',
color: '#333',
active: {
borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
borderDasharray: 'none',
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(191, 115, 115)',
// 背景颜色
backgroundColor: 'rgb(251, 251, 251)',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#333',
// 根节点样式
root: {
fillColor: 'rgb(191, 115, 115)',
@@ -36,5 +40,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(96, 57, 57)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#333',
color: '#333',
active: {
borderColor: 'rgb(96, 57, 57)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(51, 56, 62)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(127, 93, 64)',
// 背景颜色
backgroundColor: '#fff',
// 根节点样式
@@ -42,5 +46,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(0, 192, 184)'
}
},
// 概要节点样式
generalization: {
fillColor: 'rgb(127, 93, 64)',
borderColor: 'transparent',
color: 'rgb(255, 214, 175)',
active: {
borderColor: 'rgb(51, 56, 62)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(40, 193, 84)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(251, 158, 0)',
// 背景颜色
backgroundColor: 'rgb(238, 255, 243)',
// 根节点样式
@@ -42,5 +46,15 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(25, 193, 73)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'rgb(251, 158, 0)',
borderWidth: 2,
color: 'rgb(51, 51, 51)',
active: {
borderColor: 'rgb(25, 193, 73)'
}
}
})

View File

@@ -8,6 +8,7 @@ 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'
@@ -31,6 +32,7 @@ export default {
classic,
classic2,
classic3,
classic4,
dark,
classicGreen,
classicBlue,

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(51, 51, 51)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: '#222',
// 背景颜色
backgroundColor: 'rgb(248, 215, 49)',
// 根节点样式
@@ -39,5 +43,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(55, 165, 255)'
}
},
// 概要节点样式
generalization: {
borderColor: '#222',
borderWidth: 3,
color: '#222',
active: {
borderColor: 'rgb(55, 165, 255)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(104, 204, 202)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(90, 206, 241)',
// 背景颜色
backgroundColor: 'rgb(239, 255, 255)',
// 根节点样式
@@ -40,5 +44,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(0, 192, 184)'
}
},
// 概要节点样式
generalization: {
fillColor: 'rgb(90, 206, 241)',
borderColor: 'transparent',
color: '#fff',
active: {
borderColor: 'rgb(0, 192, 184)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(166, 101, 106)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: '#fff',
// 背景颜色
backgroundColor: 'rgb(255, 208, 211)',
// 根节点样式
@@ -41,5 +45,15 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(139, 109, 225)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'transparent',
color: '#222',
active: {
borderColor: 'rgb(139, 109, 225)',
borderWidth: 2,
}
}
})

View File

@@ -11,6 +11,10 @@ export default merge(defaultTheme, {
lineColor: 'rgb(123, 115, 191)',
// 背景颜色
backgroundColor: 'rgb(251, 251, 251)',
// 概要连线的粗细
generalizationLineWidth: 1,
// 概要连线的颜色
generalizationLineColor: '#333',
// 根节点样式
root: {
fillColor: 'rgb(123, 115, 191)',
@@ -36,5 +40,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(61, 57, 96)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: '#333',
color: '#333',
active: {
borderColor: 'rgb(61, 57, 96)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: '#fff',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: '#fff',
// 背景颜色
backgroundColor: 'rgb(80, 156, 170)',
// 根节点样式
@@ -42,5 +46,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(251, 227, 188)'
}
},
// 概要节点样式
generalization: {
fillColor: '#fff',
borderColor: 'transparent',
color: 'rgb(65, 89, 158)',
active: {
borderColor: 'rgb(251, 227, 188)'
}
}
})

View File

@@ -10,6 +10,10 @@ export default merge(defaultTheme, {
// 连线的颜色
lineColor: 'rgb(254, 146, 0)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(255, 222, 69)',
// 背景颜色
backgroundColor: 'rgb(255, 246, 243)',
// 根节点样式
@@ -42,5 +46,14 @@ export default merge(defaultTheme, {
active: {
borderColor: 'rgb(255, 112, 52)'
}
},
// 概要节点样式
generalization: {
fillColor: 'rgb(255, 222, 69)',
borderColor: 'transparent',
color: 'rgb(51, 51, 51)',
active: {
borderColor: 'rgb(255, 112, 52)'
}
}
})

View File

@@ -122,6 +122,11 @@ export const themeList = [
value: 'classic3',
img: require('../assets/classic3.jpg')
},
{
name: '脑图经典4',
value: 'classic4',
img: require('../assets/classic4.jpg')
},
{
name: '经典绿',
value: 'classicGreen',

View File

@@ -229,7 +229,7 @@ export const asyncRun = (taskList, callback = () => {}) => {
let index = 0
let len = taskList.length
if (len <= 0) {
return
return callback()
}
let loop = () => {
if (index >= len) {

View File

@@ -5,7 +5,8 @@
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build && node ../copy.js",
"lint": "vue-cli-service lint"
"lint": "vue-cli-service lint",
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/index.js --dest ../simple-mind-map/dist"
},
"dependencies": {
"@toast-ui/editor": "^3.1.5",

BIN
web/src/.DS_Store vendored

Binary file not shown.

Binary file not shown.

View File

@@ -54,6 +54,48 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe601;</span>
<div class="name">剪切</div>
<div class="code-name">&amp;#xe601;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe83b;</span>
<div class="name">整理</div>
<div class="code-name">&amp;#xe83b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe604;</span>
<div class="name">复制</div>
<div class="code-name">&amp;#xe604;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe63f;</span>
<div class="name">粘贴</div>
<div class="code-name">&amp;#xe63f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6be;</span>
<div class="name">上移</div>
<div class="code-name">&amp;#xe6be;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6bf;</span>
<div class="name">下移</div>
<div class="code-name">&amp;#xe6bf;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe609;</span>
<div class="name">概括总览</div>
<div class="code-name">&amp;#xe609;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xf199;</span>
<div class="name">全选</div>
@@ -258,9 +300,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1628093007325') format('woff2'),
url('iconfont.woff?t=1628093007325') format('woff'),
url('iconfont.ttf?t=1628093007325') format('truetype');
src: url('iconfont.woff2?t=1659615576455') format('woff2'),
url('iconfont.woff?t=1659615576455') format('woff'),
url('iconfont.ttf?t=1659615576455') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -286,6 +328,69 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont iconjianqie"></span>
<div class="name">
剪切
</div>
<div class="code-name">.iconjianqie
</div>
</li>
<li class="dib">
<span class="icon iconfont iconzhengli"></span>
<div class="name">
整理
</div>
<div class="code-name">.iconzhengli
</div>
</li>
<li class="dib">
<span class="icon iconfont iconfuzhi"></span>
<div class="name">
复制
</div>
<div class="code-name">.iconfuzhi
</div>
</li>
<li class="dib">
<span class="icon iconfont iconniantie"></span>
<div class="name">
粘贴
</div>
<div class="code-name">.iconniantie
</div>
</li>
<li class="dib">
<span class="icon iconfont iconshangyi"></span>
<div class="name">
上移
</div>
<div class="code-name">.iconshangyi
</div>
</li>
<li class="dib">
<span class="icon iconfont iconxiayi"></span>
<div class="name">
下移
</div>
<div class="code-name">.iconxiayi
</div>
</li>
<li class="dib">
<span class="icon iconfont icongaikuozonglan"></span>
<div class="name">
概括总览
</div>
<div class="code-name">.icongaikuozonglan
</div>
</li>
<li class="dib">
<span class="icon iconfont iconquanxuan"></span>
<div class="name">
@@ -592,6 +697,62 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconjianqie"></use>
</svg>
<div class="name">剪切</div>
<div class="code-name">#iconjianqie</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconzhengli"></use>
</svg>
<div class="name">整理</div>
<div class="code-name">#iconzhengli</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconfuzhi"></use>
</svg>
<div class="name">复制</div>
<div class="code-name">#iconfuzhi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconniantie"></use>
</svg>
<div class="name">粘贴</div>
<div class="code-name">#iconniantie</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconshangyi"></use>
</svg>
<div class="name">上移</div>
<div class="code-name">#iconshangyi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconxiayi"></use>
</svg>
<div class="name">下移</div>
<div class="code-name">#iconxiayi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icongaikuozonglan"></use>
</svg>
<div class="name">概括总览</div>
<div class="code-name">#icongaikuozonglan</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconquanxuan"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1628093007325') format('woff2'),
url('iconfont.woff?t=1628093007325') format('woff'),
url('iconfont.ttf?t=1628093007325') format('truetype');
src: url('iconfont.woff2?t=1659615576455') format('woff2'),
url('iconfont.woff?t=1659615576455') format('woff'),
url('iconfont.ttf?t=1659615576455') format('truetype');
}
.iconfont {
@@ -13,6 +13,34 @@
-moz-osx-font-smoothing: grayscale;
}
.iconjianqie:before {
content: "\e601";
}
.iconzhengli:before {
content: "\e83b";
}
.iconfuzhi:before {
content: "\e604";
}
.iconniantie:before {
content: "\e63f";
}
.iconshangyi:before {
content: "\e6be";
}
.iconxiayi:before {
content: "\e6bf";
}
.icongaikuozonglan:before {
content: "\e609";
}
.iconquanxuan:before {
content: "\f199";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,55 @@
"css_prefix_text": "icon",
"description": "思维导图",
"glyphs": [
{
"icon_id": "1117",
"name": "剪切",
"font_class": "jianqie",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "1415523",
"name": "整理",
"font_class": "zhengli",
"unicode": "e83b",
"unicode_decimal": 59451
},
{
"icon_id": "2815710",
"name": "复制",
"font_class": "fuzhi",
"unicode": "e604",
"unicode_decimal": 58884
},
{
"icon_id": "11121506",
"name": "粘贴",
"font_class": "niantie",
"unicode": "e63f",
"unicode_decimal": 58943
},
{
"icon_id": "11383392",
"name": "上移",
"font_class": "shangyi",
"unicode": "e6be",
"unicode_decimal": 59070
},
{
"icon_id": "11383396",
"name": "下移",
"font_class": "xiayi",
"unicode": "e6bf",
"unicode_decimal": 59071
},
{
"icon_id": "14843439",
"name": "概括总览",
"font_class": "gaikuozonglan",
"unicode": "e609",
"unicode_decimal": 58889
},
{
"icon_id": "19738998",
"name": "全选",

View File

@@ -214,6 +214,21 @@ export const shortcutKeyList = [
name: '插入同级节点',
value: 'Enter'
},
{
icon: 'iconshangyi',
name: '上移节点',
value: 'Ctrl + ↑'
},
{
icon: 'iconxiayi',
name: '下移节点',
value: 'Ctrl + ↓'
},
{
icon: 'icongaikuozonglan',
name: '插入概要',
value: 'Ctrl + S'
},
{
icon: 'iconzhankai',
name: '展开/收起节点',
@@ -224,6 +239,21 @@ export const shortcutKeyList = [
name: '删除节点',
value: 'Delete | Backspace'
},
{
icon: 'iconfuzhi',
name: '复制节点',
value: 'Ctrl + C'
},
{
icon: 'iconjianqie',
name: '剪切节点',
value: 'Ctrl + X'
},
{
icon: 'iconniantie',
name: '粘贴节点',
value: 'Ctrl + V'
},
{
icon: 'iconbianji',
name: '编辑节点',
@@ -248,7 +278,17 @@ export const shortcutKeyList = [
icon: 'iconquanxuan',
name: '全选',
value: 'Ctrl + A'
}
},
{
icon: 'iconquanxuan',
name: '多选',
value: '右键 / Ctrl + 左键'
},
{
icon: 'iconzhengli',
name: '一键整理布局',
value: 'Ctrl + L'
},
]
},
{
@@ -271,4 +311,44 @@ export const shortcutKeyList = [
}
]
}
]
// 形状列表
export const shapeList = [
{
name: '矩形',
value: 'rectangle'
},
{
name: '菱形',
value: 'diamond'
},
{
name: '平行四边形',
value: 'parallelogram'
},
{
name: '圆角矩形',
value: 'roundedRectangle'
},
{
name: '八角矩形',
value: 'octagonalRectangle'
},
{
name: '外三角矩形',
value: 'outerTriangularRectangle'
},
{
name: '内三角矩形',
value: 'innerTriangularRectangle'
},
{
name: '椭圆',
value: 'ellipse'
},
{
name: '圆',
value: 'circle'
}
]

View File

@@ -94,6 +94,50 @@
</el-select>
</div>
</div>
<!-- 概要连线 -->
<div class="title noTop">概要的连线</div>
<div class="row">
<div class="rowItem">
<span class="name">颜色</span>
<span
class="block"
v-popover:popover
:style="{ backgroundColor: style.generalizationLineColor }"
></span>
<el-popover ref="popover" placement="bottom" trigger="click">
<Color
:color="style.generalizationLineColor"
@change="
(color) => {
update('generalizationLineColor', color);
}
"
></Color>
</el-popover>
</div>
<div class="rowItem">
<span class="name">粗细</span>
<el-select
size="mini"
style="width: 80px"
v-model="style.generalizationLineWidth"
placeholder=""
@change="
(value) => {
update('generalizationLineWidth', value);
}
"
>
<el-option
v-for="item in lineWidthList"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</div>
</div>
<!-- 内边距 -->
<div class="title noTop">节点内边距</div>
<div class="row">
@@ -259,6 +303,8 @@ export default {
backgroundColor: "",
lineColor: "",
lineWidth: "",
generalizationLineWidth: "",
generalizationLineColor: "",
paddingX: 0,
paddingY: 0,
imgMaxWidth: 0,
@@ -291,6 +337,8 @@ export default {
"backgroundColor",
"lineWidth",
"lineColor",
"generalizationLineWidth",
"generalizationLineColor",
"paddingX",
"paddingY",
"imgMaxWidth",

View File

@@ -11,14 +11,23 @@
:class="{ disabled: insertNodeBtnDisabled }"
>
插入同级节点
<span class="desc">Enter</span>
</div>
<div class="item" @click="exec('INSERT_CHILD_NODE')">
插入子级节点
<span class="desc">Tab</span>
</div>
<div class="item" @click="exec('ADD_GENERALIZATION')" :class="{ disabled: insertNodeBtnDisabled }">
插入概要
<span class="desc">Ctrl + S</span>
</div>
<div class="item" @click="exec('INSERT_CHILD_NODE')">插入子级节点</div>
<div
class="item"
@click="exec('UP_NODE')"
:class="{ disabled: upNodeBtnDisabled }"
>
上移节点
<span class="desc">Ctrl + </span>
</div>
<div
class="item"
@@ -26,22 +35,37 @@
:class="{ disabled: downNodeBtnDisabled }"
>
下移节点
<span class="desc">Ctrl + </span>
</div>
<div class="item danger" @click="exec('REMOVE_NODE')">
删除节点
<span class="desc">Delete</span>
</div>
<div class="item" @click="exec('COPY_NODE')">
复制节点
<span class="desc">Ctrl + C</span>
</div>
<div class="item" @click="exec('CUT_NODE')">
剪切节点
<span class="desc">Ctrl + X</span>
</div>
<div class="item danger" @click="exec('REMOVE_NODE')">删除节点</div>
<div class="item" @click="exec('COPY_NODE')">复制节点</div>
<div class="item" @click="exec('CUT_NODE')">剪切节点</div>
<div
class="item"
:class="{ disabled: copyData === null }"
@click="exec('PASTE_NODE')"
>
粘贴节点
<span class="desc">Ctrl + V</span>
</div>
</template>
<template v-if="type === 'svg'">
<div class="item" @click="exec('RETURN_CENTER')">回到中心</div>
<div class="item" @click="exec('EXPAND_ALL')">展开所有</div>
<div class="item" @click="exec('UNEXPAND_ALL')">收起所有</div>
<div class="item" @click="exec('RESET_LAYOUT')">
一键整理布局
<span class="desc">Ctrl + L</span>
</div>
</template>
</div>
</template>
@@ -106,6 +130,10 @@ export default {
this.$bus.$on("expand_btn_click", this.hide);
this.$bus.$on("svg_mousedown", this.onMousedown);
this.$bus.$on("mouseup", this.onMouseup);
// 注册快捷键
this.mindMap.keyCommand.addShortcut('Control+c', this.copy);
this.mindMap.keyCommand.addShortcut('Control+v', this.paste);
this.mindMap.keyCommand.addShortcut('Control+x', this.cut);
},
beforeDestroy() {
this.$bus.$off("node_contextmenu", this.show);
@@ -114,6 +142,10 @@ export default {
this.$bus.$off("expand_btn_click", this.hide);
this.$bus.$on("svg_mousedown", this.onMousedown);
this.$bus.$on("mouseup", this.onMouseup);
// 移除快捷键
this.mindMap.keyCommand.removeShortcut('Control+c', this.copy);
this.mindMap.keyCommand.removeShortcut('Control+v', this.paste);
this.mindMap.keyCommand.removeShortcut('Control+x', this.cut);
},
methods: {
/**
@@ -214,6 +246,33 @@ export default {
}
this.hide();
},
/**
* @Author: 王林25
* @Date: 2022-08-04 14:25:45
* @Desc: 复制
*/
copy() {
this.exec("COPY_NODE");
},
/**
* @Author: 王林25
* @Date: 2022-08-04 14:26:43
* @Desc: 粘贴
*/
paste() {
this.exec("PASTE_NODE");
},
/**
* @Author: 王林25
* @Date: 2022-08-04 14:27:32
* @Desc: 剪切
*/
cut() {
this.exec("CUT_NODE");
}
},
};
</script>
@@ -221,7 +280,7 @@ export default {
<style lang="less" scoped>
.contextmenuContainer {
position: fixed;
width: 161px;
width: 200px;
background: #fff;
box-shadow: 0 4px 12px 0 hsla(0, 0%, 69%, 0.5);
border-radius: 4px;
@@ -235,8 +294,10 @@ export default {
.item {
height: 28px;
line-height: 28px;
padding-left: 16px;
padding: 0 16px;
cursor: pointer;
display: flex;
justify-content: space-between;
&.danger {
color: #f56c6c;
@@ -249,11 +310,16 @@ export default {
&.disabled {
color: grey;
cursor: not-allowed;
pointer-events: none;
&:hover {
background: #fff;
}
}
.desc {
color: #999;
}
}
}
</style>

View File

@@ -9,7 +9,7 @@
<Theme :mindMap="mindMap"></Theme>
<Structure :mindMap="mindMap"></Structure>
<ShortcutKey></ShortcutKey>
<Contextmenu :mindMap="mindMap"></Contextmenu>
<Contextmenu v-if="mindMap" :mindMap="mindMap"></Contextmenu>
<NodeNoteContentShow></NodeNoteContentShow>
</div>
</template>

View File

@@ -15,6 +15,7 @@
<el-radio label="json">json文件.json</el-radio>
<el-radio label="png">图片文件.png</el-radio>
<el-radio label="svg">svg文件.svg</el-radio>
<el-radio label="pdf">pdf文件.pdf</el-radio>
</el-radio-group>
<div class="tip">tips.smm文件可用于导入</div>
</div>

View File

@@ -53,6 +53,7 @@ export default {
}
});
this.$bus.$on("showNodeLink", () => {
this.activeNodes[0].mindMap.keyCommand.pause();
this.$bus.$emit('startTextEdit');
this.dialogVisible = true;
});
@@ -65,6 +66,7 @@ export default {
*/
cancel() {
this.dialogVisible = false;
this.activeNodes[0].mindMap.keyCommand.recovery();
this.$bus.$emit('endTextEdit');
},

View File

@@ -50,7 +50,6 @@ export default {
} else {
this.iconList = [];
}
console.log(this.iconList, nodeIconList);
});
this.$bus.$on("showNodeIcon", () => {
this.dialogVisible = true;

View File

@@ -51,7 +51,7 @@ export default {
<style lang="less" scoped>
.box {
padding: 20px;
padding: 0 20px;
.title {
font-size: 16px;

View File

@@ -15,6 +15,7 @@
size="mini"
v-model="style.fontFamily"
placeholder=""
:disabled="checkDisabled('fontFamily')"
@change="update('fontFamily')"
>
<el-option
@@ -36,6 +37,7 @@
style="width: 80px"
v-model="style.fontSize"
placeholder=""
:disabled="checkDisabled('fontSize')"
@change="update('fontSize')"
>
<el-option
@@ -54,6 +56,7 @@
style="width: 80px"
v-model="style.lineHeight"
placeholder=""
:disabled="checkDisabled('lineHeight')"
@change="update('lineHeight')"
>
<el-option
@@ -69,7 +72,7 @@
<div class="row">
<div class="btnGroup">
<el-tooltip content="颜色" placement="bottom">
<div class="styleBtn" v-popover:popover>
<div class="styleBtn" v-popover:popover :class="{ disabled: checkDisabled('color') }">
A
<span
class="colorShow"
@@ -80,7 +83,7 @@
<el-tooltip content="加粗" placement="bottom">
<div
class="styleBtn"
:class="{ actived: style.fontWeight === 'bold' }"
:class="{ actived: style.fontWeight === 'bold', disabled: checkDisabled('fontWeight') }"
@click="toggleFontWeight"
>
B
@@ -89,7 +92,7 @@
<el-tooltip content="斜体" placement="bottom">
<div
class="styleBtn i"
:class="{ actived: style.fontStyle === 'italic' }"
:class="{ actived: style.fontStyle === 'italic', disabled: checkDisabled('fontStyle') }"
@click="toggleFontStyle"
>
I
@@ -99,16 +102,17 @@
<div
class="styleBtn u"
:style="{ textDecoration: style.textDecoration || 'none' }"
:class="{ disabled: checkDisabled('textDecoration') }"
v-popover:popover2
>
U
</div>
</el-tooltip>
</div>
<el-popover ref="popover" placement="bottom" trigger="click">
<el-popover ref="popover" placement="bottom" trigger="click" :disabled="checkDisabled('color')">
<Color :color="style.color" @change="changeFontColor"></Color>
</el-popover>
<el-popover ref="popover2" placement="bottom" trigger="click">
<el-popover ref="popover2" placement="bottom" trigger="click" :disabled="checkDisabled('textDecoration')">
<el-radio-group
size="mini"
v-model="style.textDecoration"
@@ -129,8 +133,9 @@
class="block"
v-popover:popover3
:style="{ width: '80px', backgroundColor: style.borderColor }"
:class="{ disabled: checkDisabled('borderColor') }"
></span>
<el-popover ref="popover3" placement="bottom" trigger="click">
<el-popover ref="popover3" placement="bottom" trigger="click" :disabled="checkDisabled('borderColor')">
<Color
:color="style.borderColor"
@change="changeBorderColor"
@@ -144,6 +149,7 @@
style="width: 80px"
v-model="style.borderDasharray"
placeholder=""
:disabled="checkDisabled('borderDasharray')"
@change="update('borderDasharray')"
>
<el-option
@@ -164,6 +170,7 @@
style="width: 80px"
v-model="style.borderWidth"
placeholder=""
:disabled="checkDisabled('borderWidth')"
@change="update('borderWidth')"
>
<el-option
@@ -182,6 +189,7 @@
style="width: 80px"
v-model="style.borderRadius"
placeholder=""
:disabled="checkDisabled('borderRadius')"
@change="update('borderRadius')"
>
<el-option
@@ -203,12 +211,36 @@
class="block"
v-popover:popover4
:style="{ backgroundColor: style.fillColor }"
:class="{ disabled: checkDisabled('fillColor') }"
></span>
<el-popover ref="popover4" placement="bottom" trigger="click">
<el-popover ref="popover4" placement="bottom" trigger="click" :disabled="checkDisabled('fillColor')">
<Color :color="style.fillColor" @change="changeFillColor"></Color>
</el-popover>
</div>
</div>
<!-- 形状 -->
<div class="title">形状</div>
<div class="row">
<div class="rowItem">
<span class="name">形状</span>
<el-select
size="mini"
style="width: 120px"
v-model="style.shape"
placeholder=""
:disabled="checkDisabled('shape')"
@change="update('shape')"
>
<el-option
v-for="item in shapeList"
:key="item"
:label="item.name"
:value="item.value"
>
</el-option>
</el-select>
</div>
</div>
<!-- 节点内边距 -->
<div class="title noTop">节点内边距</div>
<div class="row">
@@ -217,6 +249,7 @@
<el-slider
style="width: 200px"
v-model="style.paddingX"
:disabled="checkDisabled('paddingX')"
@change="update('paddingX')"
></el-slider>
</div>
@@ -227,6 +260,7 @@
<el-slider
style="width: 200px"
v-model="style.paddingY"
:disabled="checkDisabled('paddingY')"
@change="update('paddingY')"
></el-slider>
</div>
@@ -246,7 +280,9 @@ import {
borderDasharrayList,
borderRadiusList,
lineHeightList,
shapeList,
} from "@/config";
import { supportActiveStyle } from 'simple-mind-map/src/themes/default';
/**
* @Author: 王林
@@ -261,6 +297,8 @@ export default {
},
data() {
return {
supportActiveStyle,
shapeList,
fontFamilyList,
fontSizeList,
borderWidthList,
@@ -270,6 +308,7 @@ export default {
activeNodes: [],
activeTab: "normal",
style: {
shape: '',
paddingX: 0,
paddingY: 0,
color: "",
@@ -289,11 +328,11 @@ export default {
},
created() {
this.$bus.$on("node_active", (...args) => {
this.$refs.sidebar.show = false;
if (this.$refs.sidebar) this.$refs.sidebar.show = false;
this.$nextTick(() => {
this.activeTab = "normal";
this.activeNodes = args[1];
this.$refs.sidebar.show = this.activeNodes.length > 0;
if (this.$refs.sidebar) this.$refs.sidebar.show = this.activeNodes.length > 0;
this.initNodeStyle();
});
});
@@ -308,6 +347,15 @@ export default {
this.initNodeStyle();
},
/**
* @Author: 王林
* @Date: 2022-09-12 22:16:56
* @Desc: 检查是否禁用
*/
checkDisabled(prop) {
return this.activeTab === 'active' && !this.supportActiveStyle.includes(prop)
},
/**
* @Author: 王林
* @Date: 2021-05-05 09:48:52
@@ -319,6 +367,7 @@ export default {
return;
}
[
"shape",
"paddingX",
"paddingY",
"color",
@@ -472,6 +521,13 @@ export default {
border: 1px solid #dcdfe6;
border-radius: 4px;
cursor: pointer;
&.disabled {
background-color: #F5F7FA !important;
border-color: #E4E7ED !important;
color: #C0C4CC !important;
cursor: not-allowed !important;
}
}
}
@@ -492,6 +548,13 @@ export default {
background-color: #eee;
}
&.disabled {
background-color: #F5F7FA !important;
border-color: #E4E7ED !important;
color: #C0C4CC !important;
cursor: not-allowed !important;
}
&.i {
font-style: italic;
}

View File

@@ -26,7 +26,7 @@
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0 || hasRoot,
disabled: activeNodes.length <= 0 || hasRoot || hasGeneralization,
}"
@click="$bus.$emit('execCommand', 'INSERT_NODE')"
>
@@ -36,7 +36,7 @@
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
disabled: activeNodes.length <= 0 || hasGeneralization,
}"
@click="$bus.$emit('execCommand', 'INSERT_CHILD_NODE')"
>
@@ -103,6 +103,16 @@
<span class="icon iconfont iconbiaoqian"></span>
<span class="text">标签</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0 || hasRoot,
}"
@click="$bus.$emit('execCommand', 'ADD_GENERALIZATION')"
>
<span class="icon iconfont icongaikuozonglan"></span>
<span class="text">概要</span>
</div>
</div>
<!-- 通用操作 -->
<div class="toolbarBlock">
@@ -188,6 +198,11 @@ export default {
return node.isRoot;
});
},
hasGeneralization() {
return this.activeNodes.find((node) => {
return node.isGeneralization;
});
}
},
created() {
this.$bus.$on("mode_change", (mode) => {