mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 22:08:25 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c94ec19ac | ||
|
|
3e3a60cb03 | ||
|
|
ec69d81f6a | ||
|
|
b2df7330fc | ||
|
|
5265ceb803 | ||
|
|
35d6297b72 | ||
|
|
5afe0a8c41 | ||
|
|
81f86ccb30 | ||
|
|
87383498c8 | ||
|
|
c68608c72c | ||
|
|
9d325d99a2 | ||
|
|
bc59fa6dc7 | ||
|
|
2daa59679a | ||
|
|
da456eeb8f | ||
|
|
5cd36b57d5 | ||
|
|
e2b239fcbb | ||
|
|
5449e79d49 | ||
|
|
c2045ddedc | ||
|
|
5dfa215538 | ||
|
|
d90013da71 | ||
|
|
b3b74323f7 | ||
|
|
f9000ea478 |
163
README.md
163
README.md
@@ -1,26 +1,18 @@
|
||||
# web思维导图的简单实现
|
||||
# 一个简单&强大的Web思维导图
|
||||
|
||||
## 特性
|
||||
|
||||
- [x] 支持逻辑结构图、思维导图、组织结构图、目录组织图四种结构
|
||||
|
||||
- [x] 内置多种主题,允许高度自定义样式
|
||||
|
||||
- [x] 支持快捷键
|
||||
|
||||
- [x] 节点内容支持图片、图标、超链接、备注、标签、概要
|
||||
|
||||
- [x] 支持前进后退
|
||||
|
||||
- [x] 支持拖动、缩放
|
||||
|
||||
- [x] 支持右键按住多选
|
||||
|
||||
- [x] 支持节点自由拖拽、拖拽调整
|
||||
|
||||
- [x] 支持多种节点形状
|
||||
|
||||
- [x] 支持导出为`json`、`png`、`svg`、`pdf`,支持从`json`、`xmind`导入
|
||||
- [x] 支持小地图
|
||||
|
||||
## 目录介绍
|
||||
|
||||
@@ -101,7 +93,7 @@ npm run build
|
||||
|
||||
# 安装
|
||||
|
||||
> 当然仓库版本:0.2.9,当前npm版本:0.2.9
|
||||
> 当前仓库版本:0.2.15,当前npm版本:0.2.15
|
||||
|
||||
```bash
|
||||
npm i simple-mind-map
|
||||
@@ -213,30 +205,31 @@ v0.1.7+。切换模式为只读或编辑。
|
||||
|
||||
监听事件,事件列表:
|
||||
|
||||
| 事件名称 | 描述 | 回调参数 |
|
||||
| ------------------------- | --------------------- | ----------------------------------------------------- |
|
||||
| data_change | 渲染树数据变化,可以监听该方法获取最新数据 | data(当前渲染树数据) |
|
||||
| view_data_change(v0.1.1+) | 视图变化数据,比如拖动或缩放时会触发 | data(当前视图状态数据) |
|
||||
| back_forward | 前进或回退 | activeHistoryIndex(当前在历史数据数组里的索引)、length(当前历史数据数组的长度) |
|
||||
| draw_click | *画布的单击事件* | e(事件对象) |
|
||||
| svg_mousedown | svg画布的鼠标按下事件 | e(事件对象) |
|
||||
| mousedown | el元素的鼠标按下事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| mousemove | el元素的鼠标移动事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| drag | 如果是按住左键拖动的话会触发拖动事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| mouseup | el元素的鼠标松开事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| mousewheel | 鼠标滚动事件 | e(事件对象)、dir(向上up还是向下down滚动)、this(Event事件类实例) |
|
||||
| contextmenu | svg画布的鼠标右键菜单事件 | e(事件对象) |
|
||||
| node_click | 节点的单击事件 | this(节点实例)、e(事件对象) |
|
||||
| node_mousedown | 节点的鼠标按下事件 | this(节点实例)、e(事件对象) |
|
||||
| node_mouseup | 节点的鼠标松开事件 | this(节点实例)、e(事件对象) |
|
||||
| node_dblclick | 节点的双击事件 | this(节点实例)、e(事件对象) |
|
||||
| node_contextmenu | 节点的右键菜单事件 | e(事件对象)、this(节点实例) |
|
||||
| before_node_active | 节点激活前事件 | this(节点实例)、activeNodeList(当前激活的所有节点列表) |
|
||||
| node_active | 节点激活事件 | this(节点实例)、activeNodeList(当前激活的所有节点列表) |
|
||||
| expand_btn_click | 节点展开或收缩事件 | this(节点实例) |
|
||||
| before_show_text_edit | 节点文本编辑框即将打开事件 | |
|
||||
| hide_text_edit | 节点文本编辑框关闭事件 | textEditNode(文本编辑框DOM节点)、activeNodeList(当前激活的所有节点列表) |
|
||||
| scale | 放大缩小事件 | scale(缩放比例) |
|
||||
| 事件名称 | 描述 | 回调参数 |
|
||||
| ----------------------------- | ------------------------------------------ | ------------------------------------------------------------ |
|
||||
| data_change | 渲染树数据变化,可以监听该方法获取最新数据 | data(当前渲染树数据) |
|
||||
| view_data_change(v0.1.1+) | 视图变化数据,比如拖动或缩放时会触发 | data(当前视图状态数据) |
|
||||
| back_forward | 前进或回退 | activeHistoryIndex(当前在历史数据数组里的索引)、length(当前历史数据数组的长度) |
|
||||
| draw_click | *画布的单击事件* | e(事件对象) |
|
||||
| svg_mousedown | svg画布的鼠标按下事件 | e(事件对象) |
|
||||
| mousedown | el元素的鼠标按下事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| mousemove | el元素的鼠标移动事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| drag | 如果是按住左键拖动的话会触发拖动事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| mouseup | el元素的鼠标松开事件 | e(事件对象)、this(Event事件类实例) |
|
||||
| mousewheel | 鼠标滚动事件 | e(事件对象)、dir(向上up还是向下down滚动)、this(Event事件类实例) |
|
||||
| contextmenu | svg画布的鼠标右键菜单事件 | e(事件对象) |
|
||||
| node_click | 节点的单击事件 | this(节点实例)、e(事件对象) |
|
||||
| node_mousedown | 节点的鼠标按下事件 | this(节点实例)、e(事件对象) |
|
||||
| node_mouseup | 节点的鼠标松开事件 | this(节点实例)、e(事件对象) |
|
||||
| node_dblclick | 节点的双击事件 | this(节点实例)、e(事件对象) |
|
||||
| node_contextmenu | 节点的右键菜单事件 | e(事件对象)、this(节点实例) |
|
||||
| before_node_active | 节点激活前事件 | this(节点实例)、activeNodeList(当前激活的所有节点列表) |
|
||||
| node_active | 节点激活事件 | this(节点实例)、activeNodeList(当前激活的所有节点列表) |
|
||||
| expand_btn_click | 节点展开或收缩事件 | this(节点实例) |
|
||||
| before_show_text_edit | 节点文本编辑框即将打开事件 | |
|
||||
| hide_text_edit | 节点文本编辑框关闭事件 | textEditNode(文本编辑框DOM节点)、activeNodeList(当前激活的所有节点列表) |
|
||||
| scale | 放大缩小事件 | scale(缩放比例) |
|
||||
| node_img_dblclick(v0.2.15+) | 节点内图片的双击事件 | this(节点实例)、e(事件对象) |
|
||||
|
||||
#### emit(event, ...args)
|
||||
|
||||
@@ -520,6 +513,18 @@ v0.2.3+。恢复保存的快捷键数据,然后清空缓存数据
|
||||
|
||||
`y`方向进行平移,`step`:要平移的像素
|
||||
|
||||
#### translateXTo(x)
|
||||
|
||||
v0.2.11+
|
||||
|
||||
平移`x`方向到指定位置
|
||||
|
||||
#### translateYTo(y)
|
||||
|
||||
v0.2.11+
|
||||
|
||||
平移`y`方向到指定位置
|
||||
|
||||
#### reset()
|
||||
|
||||
恢复到默认的变换
|
||||
@@ -544,6 +549,94 @@ v0.1.1+
|
||||
|
||||
动态设置变换数据,可以通过getTransformData方法获取变换数据
|
||||
|
||||
## MiniMap实例
|
||||
|
||||
v0.2.11+
|
||||
|
||||
用于帮助快速开发小地图功能,小地图由两部分组成,一个是当前的画布内容,一个是视口框,当缩放、移动、元素过多时画布上可能只显示了思维导图的部分内容,可以通过视口框来查看当前视口所在位置,以及可以通过在小地图上拖动来快速定位。
|
||||
|
||||
可通过`mindMap.miniMap`获取到该实例。
|
||||
|
||||
### 方法
|
||||
|
||||
#### getMiniMap()
|
||||
|
||||
获取小地图相关数据,这个函数一般不会直接使用,函数返回的内容:
|
||||
|
||||
```js
|
||||
{
|
||||
svg, // Element,思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组)
|
||||
svgHTML, // String,svg字符串,即html字符串,可以直接渲染到你准备的小地图容器内
|
||||
rect: // Object,思维导图图形未缩放时的位置尺寸等信息
|
||||
origWidth, // Number,画布宽度
|
||||
origHeight, // Number,画布高度
|
||||
scaleX, // Number,思维导图图形的水平缩放值
|
||||
scaleY, // Number,思维导图图形的垂直缩放值
|
||||
}
|
||||
```
|
||||
|
||||
#### calculationMiniMap(boxWidth, boxHeight)
|
||||
|
||||
计算小地图的渲染数据,该函数内会调用`getMiniMap()`方法,所以一般使用该函数即可。
|
||||
|
||||
`boxWidth`:小地图容器的宽度
|
||||
|
||||
`boxHeight`:小地图容器的高度
|
||||
|
||||
函数返回内容:
|
||||
|
||||
```js
|
||||
{
|
||||
svgHTML, // 小地图html
|
||||
viewBoxStyle, // 视图框的位置信息
|
||||
miniMapBoxScale, // 视图框的缩放值
|
||||
miniMapBoxLeft, // 视图框的left值
|
||||
miniMapBoxTop, // 视图框的top值
|
||||
}
|
||||
```
|
||||
|
||||
小地图思路:
|
||||
|
||||
1.准备一个容器元素`container`,定位不为`static`
|
||||
|
||||
2.在`container`内创建一个小地图容器元素`miniMapContainer`,绝对定位
|
||||
|
||||
3.在`container`内创建一个视口框元素`viewBoxContainer`,绝对定位,设置边框样式,过渡属性(可选)
|
||||
|
||||
4.监听`data_change`和`view_data_change`事件,在该事件内调用`calculationMiniMap`方法获取计算数据,然后将`svgHTML`渲染到`miniMapContainer`元素内,并且设置它的样式:
|
||||
|
||||
```js
|
||||
:style="{
|
||||
transform: `scale(${svgBoxScale})`,
|
||||
left: svgBoxLeft + 'px',
|
||||
top: svgBoxTop + 'px',
|
||||
}"
|
||||
```
|
||||
|
||||
5.将`viewBoxStyle`对象设置为`viewBoxContainer`元素的样式
|
||||
|
||||
到这一步,当画布上的思维导图变化了,小地图也会实时更新,并且视口框元素会实时反映视口在思维导图图形上的位置
|
||||
|
||||
6.监听`container`元素的`mousedown`、`mousemove`、`mouseup`事件,分别调用下面即将介绍的三个方法即可实现鼠标拖动时画布上的思维导图也随之拖动的效果
|
||||
|
||||
#### onMousedown(e)
|
||||
|
||||
小地图鼠标按下事件执行该函数
|
||||
|
||||
`e`:事件对象
|
||||
|
||||
#### onMousemove(e, sensitivityNum = 5)
|
||||
|
||||
小地图鼠标移动事件执行该函数
|
||||
|
||||
`e`:事件对象
|
||||
|
||||
`sensitivityNum`:拖动灵敏度,灵敏度越大,在小地图上拖动相同距离时实际上的画布拖动距离就越大
|
||||
|
||||
#### onMouseup()
|
||||
|
||||
小地图鼠标松开事件执行该函数
|
||||
|
||||
## doExport实例
|
||||
|
||||
`doExport`实例负责导出,可通过`mindMap.doExport`获取到该实例
|
||||
|
||||
@@ -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/js/chunk-2d20ec02.10aa67e3.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.2d06497a.js" rel="prefetch"><link href="dist/js/chunk-5397ae43.9dca0f90.js" rel="prefetch"><link href="dist/css/app.891a59ac.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.597033a2.css" rel="preload" as="style"><link href="dist/js/app.6ae41667.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.9c26d9dc.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.597033a2.css" rel="stylesheet"><link href="dist/css/app.891a59ac.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.9c26d9dc.js"></script><script src="dist/js/app.6ae41667.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-35b0a040.cb76da7d.js" rel="prefetch"><link href="dist/css/app.c8de1c03.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.faba1249.css" rel="preload" as="style"><link href="dist/js/app.9bc33d3c.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.ce9e5522.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.faba1249.css" rel="stylesheet"><link href="dist/css/app.c8de1c03.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.ce9e5522.js"></script><script src="dist/js/app.9bc33d3c.js"></script></body></html>
|
||||
@@ -10,6 +10,7 @@ import BatchExecution from './src/BatchExecution'
|
||||
import Export from './src/Export'
|
||||
import Select from './src/Select'
|
||||
import Drag from './src/Drag'
|
||||
import MiniMap from './src/MiniMap';
|
||||
import {
|
||||
layoutValueList
|
||||
} from './src/utils/constant'
|
||||
@@ -116,6 +117,11 @@ class MindMap {
|
||||
draw: this.draw
|
||||
})
|
||||
|
||||
// 小地图类
|
||||
this.miniMap = new MiniMap({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 导出类
|
||||
this.doExport = new Export({
|
||||
mindMap: this
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.2.9",
|
||||
"version": "0.2.15",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -44,28 +44,9 @@ class Export {
|
||||
* @Desc: 获取svg数据
|
||||
*/
|
||||
async getSvgData() {
|
||||
const svg = this.mindMap.svg
|
||||
const draw = this.mindMap.draw
|
||||
// 保存原始信息
|
||||
const origWidth = svg.width()
|
||||
const origHeight = svg.height()
|
||||
const origTransform = draw.transform()
|
||||
const elRect = this.mindMap.el.getBoundingClientRect()
|
||||
// 去除放大缩小的变换效果
|
||||
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
const clone = svg.clone()
|
||||
// 恢复原先的大小和变换信息
|
||||
svg.size(origWidth, origHeight)
|
||||
draw.transform(origTransform)
|
||||
let { svg, svgHTML } = this.mindMap.miniMap.getMiniMap()
|
||||
// 把图片的url转换成data:url类型,否则导出会丢失图片
|
||||
let imageList = clone.find('image')
|
||||
let imageList = svg.find('image')
|
||||
let task = imageList.map(async (item) => {
|
||||
let imgUlr = item.attr('href') || item.attr('xlink:href')
|
||||
let imgData = await imgToDataUrl(imgUlr)
|
||||
@@ -73,8 +54,8 @@ class Export {
|
||||
})
|
||||
await Promise.all(task)
|
||||
return {
|
||||
node: clone,
|
||||
str: clone.svg()
|
||||
node: svg,
|
||||
str: svgHTML
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
175
simple-mind-map/src/MiniMap.js
Normal file
175
simple-mind-map/src/MiniMap.js
Normal file
@@ -0,0 +1,175 @@
|
||||
// 小地图类
|
||||
class MiniMap {
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:00:45
|
||||
* @Desc: 构造函数
|
||||
*/
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap;
|
||||
this.isMousedown = false;
|
||||
this.mousedownPos = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
this.startViewPos = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:00:43
|
||||
* @Desc: 获取小地图相关数据
|
||||
*/
|
||||
getMiniMap() {
|
||||
const svg = this.mindMap.svg;
|
||||
const draw = this.mindMap.draw;
|
||||
// 保存原始信息
|
||||
const origWidth = svg.width();
|
||||
const origHeight = svg.height();
|
||||
const origTransform = draw.transform();
|
||||
const elRect = this.mindMap.el.getBoundingClientRect();
|
||||
// 去除放大缩小的变换效果
|
||||
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY);
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox();
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height);
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top);
|
||||
// 克隆一份数据
|
||||
const clone = svg.clone();
|
||||
// 恢复原先的大小和变换信息
|
||||
svg.size(origWidth, origHeight);
|
||||
draw.transform(origTransform);
|
||||
|
||||
return {
|
||||
svg: clone, // 思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组)
|
||||
svgHTML: clone.svg(), // svg字符串
|
||||
rect: {
|
||||
...rect, // 思维导图图形未缩放时的位置尺寸等信息
|
||||
ratio: rect.width / rect.height, // 思维导图图形的宽高比
|
||||
},
|
||||
origWidth, // 画布宽度
|
||||
origHeight, // 画布高度
|
||||
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
|
||||
scaleY: origTransform.scaleY, // 思维导图图形的垂直缩放值
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:05:51
|
||||
* @Desc: 计算小地图的渲染数据
|
||||
* boxWidth:小地图容器的宽度
|
||||
* boxHeight:小地图容器的高度
|
||||
*/
|
||||
calculationMiniMap(boxWidth, boxHeight) {
|
||||
let { svgHTML, rect, origWidth, origHeight, scaleX, scaleY } =
|
||||
this.getMiniMap();
|
||||
// 计算数据
|
||||
let boxRatio = boxWidth / boxHeight;
|
||||
let actWidth = 0;
|
||||
let actHeight = 0;
|
||||
if (boxRatio > rect.ratio) {
|
||||
// 高度以box为准,缩放宽度
|
||||
actHeight = boxHeight;
|
||||
actWidth = rect.ratio * actHeight;
|
||||
} else {
|
||||
// 宽度以box为准,缩放高度
|
||||
actWidth = boxWidth;
|
||||
actHeight = actWidth / rect.ratio;
|
||||
}
|
||||
// svg图形的缩放及位置
|
||||
let miniMapBoxScale = actWidth / rect.width;
|
||||
let miniMapBoxLeft = (boxWidth - actWidth) / 2;
|
||||
let miniMapBoxTop = (boxHeight - actHeight) / 2;
|
||||
// 视口框大小及位置
|
||||
let _rectX = rect.x - (rect.width * scaleX - rect.width) / 2;
|
||||
let _rectX2 = rect.x2 + (rect.width * scaleX - rect.width) / 2;
|
||||
let _rectY = rect.y - (rect.height * scaleY - rect.height) / 2;
|
||||
let _rectY2 = rect.y2 + (rect.height * scaleY - rect.height) / 2;
|
||||
let _rectWidth = rect.width * scaleX;
|
||||
let _rectHeight = rect.height * scaleY;
|
||||
let viewBoxStyle = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
};
|
||||
viewBoxStyle.left =
|
||||
Math.max(0, (-_rectX / _rectWidth) * actWidth) + miniMapBoxLeft + "px";
|
||||
viewBoxStyle.right =
|
||||
Math.max(0, ((_rectX2 - origWidth) / _rectWidth) * actWidth) +
|
||||
miniMapBoxLeft +
|
||||
"px";
|
||||
|
||||
viewBoxStyle.top =
|
||||
Math.max(0, (-_rectY / _rectHeight) * actHeight) + miniMapBoxTop + "px";
|
||||
viewBoxStyle.bottom =
|
||||
Math.max(0, ((_rectY2 - origHeight) / _rectHeight) * actHeight) +
|
||||
miniMapBoxTop +
|
||||
"px";
|
||||
return {
|
||||
svgHTML, // 小地图html
|
||||
viewBoxStyle, // 视图框的位置信息
|
||||
miniMapBoxScale, // 视图框的缩放值
|
||||
miniMapBoxLeft, // 视图框的left值
|
||||
miniMapBoxTop, // 视图框的top值
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:22:40
|
||||
* @Desc: 小地图鼠标按下事件
|
||||
*/
|
||||
onMousedown(e) {
|
||||
this.isMousedown = true;
|
||||
this.mousedownPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
// 保存视图当前的偏移量
|
||||
let transformData = this.mindMap.view.getTransformData();
|
||||
this.startViewPos = {
|
||||
x: transformData.state.x,
|
||||
y: transformData.state.y,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:22:55
|
||||
* @Desc: 小地图鼠标移动事件
|
||||
*/
|
||||
onMousemove(e, sensitivityNum = 5) {
|
||||
if (!this.isMousedown) {
|
||||
return;
|
||||
}
|
||||
let ox = e.clientX - this.mousedownPos.x;
|
||||
let oy = e.clientY - this.mousedownPos.y;
|
||||
// 在视图最初偏移量上累加更新量
|
||||
this.mindMap.view.translateXTo(ox * sensitivityNum + this.startViewPos.x);
|
||||
this.mindMap.view.translateYTo(oy * sensitivityNum + this.startViewPos.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:23:01
|
||||
* @Desc: 小地图鼠标松开事件
|
||||
*/
|
||||
onMouseup() {
|
||||
this.isMousedown = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default MiniMap;
|
||||
@@ -367,6 +367,9 @@ class Node {
|
||||
if (this.nodeData.data.imageTitle) {
|
||||
node.attr('title', this.nodeData.data.imageTitle)
|
||||
}
|
||||
node.on('dblclick', (e) => {
|
||||
this.mindMap.emit('node_img_dblclick', this, e)
|
||||
})
|
||||
return {
|
||||
node,
|
||||
width: imgSize[0],
|
||||
@@ -872,7 +875,7 @@ class Node {
|
||||
this.renderer.layout.renderLine(this, this._lines, (line, node) => {
|
||||
// 添加样式
|
||||
this.styleLine(line, node)
|
||||
})
|
||||
}, this.style.getStyle('lineStyle', true))
|
||||
// 子级的连线也需要更新
|
||||
if (deep && this.children && this.children.length > 0) {
|
||||
this.children.forEach((item) => {
|
||||
|
||||
@@ -446,6 +446,8 @@ class Render {
|
||||
},
|
||||
"children": []
|
||||
})
|
||||
// 插入子节点时自动展开子节点
|
||||
node.nodeData.data.expand = true
|
||||
if (node.isRoot) {
|
||||
node.initRender = true
|
||||
// this.mindMap.batchExecution.push('renderNode' + index, () => {
|
||||
@@ -658,7 +660,7 @@ class Render {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
return
|
||||
}
|
||||
return copyNodeTree({}, this.activeNodeList[0])
|
||||
return copyNodeTree({}, this.activeNodeList[0], true)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -674,7 +676,7 @@ class Render {
|
||||
if (node.isRoot) {
|
||||
return null
|
||||
}
|
||||
let copyData = copyNodeTree({}, node)
|
||||
let copyData = copyNodeTree({}, node, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
|
||||
@@ -126,6 +126,17 @@ class View {
|
||||
this.transform()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:03:53
|
||||
* @Desc: 平移x方式到
|
||||
*/
|
||||
translateXTo(x) {
|
||||
this.x = x
|
||||
this.transform()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
@@ -137,6 +148,17 @@ class View {
|
||||
this.transform()
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-10-10 14:04:10
|
||||
* @Desc: 平移y方向到
|
||||
*/
|
||||
translateYTo(y) {
|
||||
this.y = y
|
||||
this.transform()
|
||||
}
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
* @Date: 2021-07-04 17:13:14
|
||||
|
||||
@@ -146,7 +146,81 @@ class LogicalStructure extends Base {
|
||||
* @Date: 2021-04-11 14:42:48
|
||||
* @Desc: 绘制连线,连接该节点到其子节点
|
||||
*/
|
||||
renderLine(node, lines, style) {
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'curve') {
|
||||
this.renderLineCurve(node, lines, style)
|
||||
} else if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:17:30
|
||||
* @Desc: 直线风格连线
|
||||
*/
|
||||
renderLineStraight(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
expandBtnSize
|
||||
} = node
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let s1 = (marginX - expandBtnSize) * 0.6
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 = node.layerIndex === 0 ? left + width : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = `M ${x1},${y1} L ${x1 + s1},${y1} L ${x1 + s1},${y2} L ${x2},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:34:41
|
||||
* @Desc: 直连风格
|
||||
*/
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
expandBtnSize
|
||||
} = node
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 = node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = `M ${x1},${y1} L ${x2},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:17:43
|
||||
* @Desc: 曲线风格连线
|
||||
*/
|
||||
renderLineCurve(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -182,7 +182,89 @@ class MindMap extends Base {
|
||||
* @Date: 2021-04-11 14:42:48
|
||||
* @Desc: 绘制连线,连接该节点到其子节点
|
||||
*/
|
||||
renderLine(node, lines, style) {
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'curve') {
|
||||
this.renderLineCurve(node, lines, style)
|
||||
} else if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:10:47
|
||||
* @Desc: 直线风格连线
|
||||
*/
|
||||
renderLineStraight(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
expandBtnSize
|
||||
} = node
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let s1 = (marginX - expandBtnSize) * 0.6
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 = 0
|
||||
let _s = 0
|
||||
if (item.dir === 'left') {
|
||||
_s = -s1
|
||||
x1 = node.layerIndex === 0 ? left : left - expandBtnSize
|
||||
} else {
|
||||
_s = s1
|
||||
x1 = node.layerIndex === 0 ? left + width : left + width + expandBtnSize
|
||||
}
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = `M ${x1},${y1} L ${x1 + _s},${y1} L ${x1 + _s},${y2} L ${x2},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:34:41
|
||||
* @Desc: 直连风格
|
||||
*/
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
expandBtnSize
|
||||
} = node
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 = node.layerIndex === 0 ? left + width / 2 : item.dir === 'left' ? left - expandBtnSize : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = `M ${x1},${y1} L ${x2},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:10:56
|
||||
* @Desc: 曲线风格连线
|
||||
*/
|
||||
renderLineCurve(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -147,7 +147,48 @@ class OrganizationStructure extends Base {
|
||||
* @Date: 2021-04-11 14:42:48
|
||||
* @Desc: 绘制连线,连接该节点到其子节点
|
||||
*/
|
||||
renderLine(node, lines, style) {
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:34:41
|
||||
* @Desc: 直连风格
|
||||
*/
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
let {
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
} = node
|
||||
let x1 = left + width / 2
|
||||
let y1 = top + height
|
||||
node.children.forEach((item, index) => {
|
||||
let x2 = item.left + item.width / 2
|
||||
let y2 = item.top
|
||||
let path = `M ${x1},${y1} L ${x2},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-09-30 14:39:07
|
||||
* @Desc: 直线风格连线
|
||||
*/
|
||||
renderLineStraight(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ export default {
|
||||
lineColor: '#549688',
|
||||
// 连线样式
|
||||
lineDasharray: 'none',
|
||||
// 连线风格
|
||||
lineStyle: 'straight',// 针对logicalStructure、mindMap两种结构。曲线(curve)、直线(straight)、直连(direct)
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
|
||||
@@ -147,13 +147,19 @@ export const copyRenderTree = (tree, root) => {
|
||||
* @Date: 2021-05-04 14:40:11
|
||||
* @Desc: 复制节点树数据
|
||||
*/
|
||||
export const copyNodeTree = (tree, root) => {
|
||||
tree.data = simpleDeepClone(root.nodeData.data)
|
||||
// tree.data.isActive = false
|
||||
export const copyNodeTree = (tree, root, removeActiveState = false) => {
|
||||
tree.data = simpleDeepClone(root.nodeData ? root.nodeData.data : root.data)
|
||||
if (removeActiveState) {
|
||||
tree.data.isActive = false
|
||||
}
|
||||
tree.children = []
|
||||
if (root.children && root.children.length > 0) {
|
||||
root.children.forEach((item, index) => {
|
||||
tree.children[index] = copyNodeTree({}, item)
|
||||
tree.children[index] = copyNodeTree({}, item, removeActiveState)
|
||||
})
|
||||
} else if (root.nodeData && root.nodeData.children && root.nodeData.children.length > 0) {
|
||||
root.nodeData.children.forEach((item, index) => {
|
||||
tree.children[index] = copyNodeTree({}, item, removeActiveState)
|
||||
})
|
||||
}
|
||||
return tree;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"@toast-ui/editor": "^3.1.5",
|
||||
"core-js": "^3.6.5",
|
||||
"element-ui": "^2.15.1",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.5.1",
|
||||
"vuex": "^3.6.2"
|
||||
|
||||
@@ -130,6 +130,22 @@ export const borderRadiusList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
// 线宽
|
||||
export const lineWidthList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
// 连线风格
|
||||
export const lineStyleList = [
|
||||
{
|
||||
name: '直线',
|
||||
value: 'straight'
|
||||
},
|
||||
{
|
||||
name: '曲线',
|
||||
value: 'curve'
|
||||
},
|
||||
{
|
||||
name: '直连',
|
||||
value: 'direct'
|
||||
}
|
||||
]
|
||||
|
||||
// 图片重复方式
|
||||
export const backgroundRepeatList = [
|
||||
{
|
||||
|
||||
@@ -4,11 +4,14 @@ import router from './router'
|
||||
import store from './store'
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import '@/assets/icon-font/iconfont.css';
|
||||
import '@/assets/icon-font/iconfont.css'
|
||||
import 'viewerjs/dist/viewer.css'
|
||||
import VueViewer from 'v-viewer'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.prototype.$bus = new Vue()
|
||||
Vue.use(ElementUI)
|
||||
Vue.use(VueViewer)
|
||||
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
|
||||
@@ -94,6 +94,30 @@
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="rowItem">
|
||||
<span class="name">风格</span>
|
||||
<el-select
|
||||
size="mini"
|
||||
style="width: 80px"
|
||||
v-model="style.lineStyle"
|
||||
placeholder=""
|
||||
@change="
|
||||
(value) => {
|
||||
update('lineStyle', value);
|
||||
}
|
||||
"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in lineStyleList"
|
||||
:key="item.value"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 概要连线 -->
|
||||
<div class="title noTop">概要的连线</div>
|
||||
<div class="row">
|
||||
@@ -267,6 +291,7 @@ import Sidebar from "./Sidebar";
|
||||
import Color from "./Color";
|
||||
import {
|
||||
lineWidthList,
|
||||
lineStyleList,
|
||||
backgroundRepeatList
|
||||
} from "@/config";
|
||||
import ImgUpload from "@/components/ImgUpload";
|
||||
@@ -296,6 +321,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
lineWidthList,
|
||||
lineStyleList,
|
||||
backgroundRepeatList,
|
||||
activeTab: "color",
|
||||
marginActiveTab: "second",
|
||||
@@ -303,6 +329,7 @@ export default {
|
||||
backgroundColor: "",
|
||||
lineColor: "",
|
||||
lineWidth: "",
|
||||
lineStyle: "",
|
||||
generalizationLineWidth: "",
|
||||
generalizationLineColor: "",
|
||||
paddingX: 0,
|
||||
@@ -336,6 +363,7 @@ export default {
|
||||
[
|
||||
"backgroundColor",
|
||||
"lineWidth",
|
||||
"lineStyle",
|
||||
"lineColor",
|
||||
"generalizationLineWidth",
|
||||
"generalizationLineColor",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<div class="editContainer">
|
||||
<div class="mindMapContainer" ref="mindMapContainer"></div>
|
||||
<Count></Count>
|
||||
<Navigator :mindMap="mindMap"></Navigator>
|
||||
<NavigatorToolbar :mindMap="mindMap"></NavigatorToolbar>
|
||||
<Outline></Outline>
|
||||
<Style></Style>
|
||||
@@ -11,6 +12,7 @@
|
||||
<ShortcutKey></ShortcutKey>
|
||||
<Contextmenu v-if="mindMap" :mindMap="mindMap"></Contextmenu>
|
||||
<NodeNoteContentShow></NodeNoteContentShow>
|
||||
<NodeImgPreview v-if="mindMap" :mindMap="mindMap"></NodeImgPreview>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -27,6 +29,8 @@ import ShortcutKey from './ShortcutKey'
|
||||
import Contextmenu from './Contextmenu'
|
||||
import NodeNoteContentShow from './NodeNoteContentShow.vue'
|
||||
import { getData, storeData, storeConfig } from '@/api'
|
||||
import Navigator from './Navigator.vue'
|
||||
import NodeImgPreview from './NodeImgPreview.vue'
|
||||
|
||||
/**
|
||||
* @Author: 王林
|
||||
@@ -45,7 +49,9 @@ export default {
|
||||
NavigatorToolbar,
|
||||
ShortcutKey,
|
||||
Contextmenu,
|
||||
NodeNoteContentShow
|
||||
NodeNoteContentShow,
|
||||
Navigator,
|
||||
NodeImgPreview
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
153
web/src/pages/Edit/components/Navigator.vue
Normal file
153
web/src/pages/Edit/components/Navigator.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="showMiniMap"
|
||||
class="navigatorBox"
|
||||
ref="navigatorBox"
|
||||
@mousedown="onMousedown"
|
||||
@mousemove="onMousemove"
|
||||
@mouseup="onMouseup"
|
||||
>
|
||||
<div
|
||||
class="svgBox"
|
||||
ref="svgBox"
|
||||
:style="{
|
||||
transform: `scale(${svgBoxScale})`,
|
||||
left: svgBoxLeft + 'px',
|
||||
top: svgBoxTop + 'px',
|
||||
}"
|
||||
></div>
|
||||
<div class="windowBox" :style="viewBoxStyle"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
mindMap: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showMiniMap: false,
|
||||
timer: null,
|
||||
boxWidth: 0,
|
||||
boxHeight: 0,
|
||||
svgBoxScale: 1,
|
||||
svgBoxLeft: 0,
|
||||
svgBoxTop: 0,
|
||||
viewBoxStyle: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$bus.$on("toggle_mini_map", this.toggle_mini_map);
|
||||
this.$bus.$on("data_change", this.data_change);
|
||||
this.$bus.$on("view_data_change", this.view_data_change);
|
||||
},
|
||||
destroyed() {
|
||||
this.$bus.$off("toggle_mini_map", this.toggle_mini_map);
|
||||
this.$bus.$off("data_change", this.data_change);
|
||||
this.$bus.$off("view_data_change", this.view_data_change);
|
||||
},
|
||||
methods: {
|
||||
toggle_mini_map(show) {
|
||||
this.showMiniMap = show;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.navigatorBox) {
|
||||
this.init();
|
||||
}
|
||||
if (this.$refs.svgBox) {
|
||||
this.drawMiniMap();
|
||||
}
|
||||
});
|
||||
},
|
||||
data_change() {
|
||||
if (!this.showMiniMap) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.drawMiniMap();
|
||||
}, 500);
|
||||
},
|
||||
view_data_change() {
|
||||
if (!this.showMiniMap) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.drawMiniMap();
|
||||
}, 500);
|
||||
},
|
||||
init() {
|
||||
let { width, height } = this.$refs.navigatorBox.getBoundingClientRect();
|
||||
this.boxWidth = width;
|
||||
this.boxHeight = height;
|
||||
},
|
||||
|
||||
drawMiniMap() {
|
||||
let {
|
||||
svgHTML,
|
||||
viewBoxStyle,
|
||||
miniMapBoxScale,
|
||||
miniMapBoxLeft,
|
||||
miniMapBoxTop,
|
||||
} = this.mindMap.miniMap.calculationMiniMap(
|
||||
this.boxWidth,
|
||||
this.boxHeight
|
||||
);
|
||||
// 渲染到小地图
|
||||
this.$refs.svgBox.innerHTML = svgHTML;
|
||||
this.viewBoxStyle = viewBoxStyle;
|
||||
this.svgBoxScale = miniMapBoxScale;
|
||||
this.svgBoxLeft = miniMapBoxLeft;
|
||||
this.svgBoxTop = miniMapBoxTop;
|
||||
},
|
||||
|
||||
onMousedown(e) {
|
||||
this.mindMap.miniMap.onMousedown(e);
|
||||
},
|
||||
|
||||
onMousemove(e) {
|
||||
this.mindMap.miniMap.onMousemove(e);
|
||||
},
|
||||
|
||||
onMouseup(e) {
|
||||
this.mindMap.miniMap.onMouseup(e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.navigatorBox {
|
||||
position: absolute;
|
||||
width: 350px;
|
||||
height: 220px;
|
||||
background-color: #fff;
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
box-shadow: 0 0 16px #989898;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #eee;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
.svgBox {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
transform-origin: left top;
|
||||
}
|
||||
|
||||
.windowBox {
|
||||
position: absolute;
|
||||
border: 2px solid rgb(238, 69, 69);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div class="navigatorContainer">
|
||||
<div class="item">
|
||||
<el-checkbox v-model="openMiniMap" @change="toggleMiniMap">开启小地图</el-checkbox>
|
||||
</div>
|
||||
<div class="item">
|
||||
<el-switch
|
||||
v-model="isReadonly"
|
||||
@@ -40,12 +43,20 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isReadonly: false
|
||||
isReadonly: false,
|
||||
openMiniMap: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.toggleMiniMap(this.openMiniMap)
|
||||
},
|
||||
methods: {
|
||||
readonlyChange(value) {
|
||||
this.mindMap.setMode(value ? 'readonly' : 'edit')
|
||||
},
|
||||
|
||||
toggleMiniMap(show) {
|
||||
this.$bus.$emit('toggle_mini_map', show)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
41
web/src/pages/Edit/components/NodeImgPreview.vue
Normal file
41
web/src/pages/Edit/components/NodeImgPreview.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<viewer :images="images">
|
||||
<img v-for="src in images" :key="src" :src="src" />
|
||||
</viewer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
mindMap: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
images: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.mindMap.on("node_img_dblclick", this.onNodeTmgDblclick);
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.mindMap.off("node_img_dblclick", this.onNodeTmgDblclick);
|
||||
},
|
||||
methods: {
|
||||
onNodeTmgDblclick(node, e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.images = [node.nodeData.data.image];
|
||||
this.$viewerApi({
|
||||
images: this.images,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -260,9 +260,9 @@ export default {
|
||||
let [ _fileHandle ] = await window.showOpenFilePicker({
|
||||
types: [
|
||||
{
|
||||
description: 'file',
|
||||
description: '',
|
||||
accept: {
|
||||
'application/*': ['.json', '.smm']
|
||||
'application/json': ['.smm']
|
||||
}
|
||||
},
|
||||
],
|
||||
@@ -280,7 +280,10 @@ export default {
|
||||
this.readFile();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this.$message.warning('你的浏览器可能不支持哦');
|
||||
if (error.toString().includes('aborted')) {
|
||||
return
|
||||
}
|
||||
this.$message.warning('你的浏览器可能不支持,建议使用最新版本的Chrome浏览器');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -379,9 +382,10 @@ export default {
|
||||
try {
|
||||
let _fileHandle = await window.showSaveFilePicker({
|
||||
types: [{
|
||||
description: 'file',
|
||||
accept: {'application/*': ['.json', '.smm']},
|
||||
description: '',
|
||||
accept: {'application/json': ['.smm']},
|
||||
}],
|
||||
suggestedName: '思维导图'
|
||||
});
|
||||
if (!_fileHandle) {
|
||||
return;
|
||||
@@ -400,7 +404,10 @@ export default {
|
||||
loading.close();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this.$message.warning('你的浏览器可能不支持哦');
|
||||
if (error.toString().includes('aborted')) {
|
||||
return
|
||||
}
|
||||
this.$message.warning('你的浏览器可能不支持,建议使用最新版本的Chrome浏览器');
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user