mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-22 09:10:29 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc3c91270c | ||
|
|
71ac739964 | ||
|
|
eae5dc5854 | ||
|
|
c45ceac7dc | ||
|
|
3d8702be8a | ||
|
|
084dd9fd84 | ||
|
|
0c6c68820f | ||
|
|
f5ff479f47 | ||
|
|
e9722efe93 | ||
|
|
db1f9c04c1 |
23
README.md
23
README.md
@@ -70,6 +70,16 @@ npm run build
|
||||
npm i simple-mind-map
|
||||
```
|
||||
|
||||
注意:本项目为源码直接发布,并未进行打包,如果出现编译失败的情况,Vue CLI创建的项目可以在vue.config.js文件中增加如下配置来让babel-loader编译本依赖:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
transpileDependencies: ['simple-mind-map']
|
||||
}
|
||||
```
|
||||
|
||||
其他项目请自行修改打包配置。
|
||||
|
||||
# API
|
||||
|
||||
## 实例化
|
||||
@@ -106,6 +116,7 @@ const mindMap = new MindMap({
|
||||
| selectTranslateStep | Number | 3 | 多选节点时鼠标移动到边缘时的画布移动偏移量 | |
|
||||
| selectTranslateLimit | Number | 20 | 多选节点时鼠标移动距边缘多少距离时开始偏移 | |
|
||||
| customNoteContentShow(v0.1.6+) | Object | null | 自定义节点备注内容显示,Object类型,结构为:{show: (noteContent, left, top) => {// 你的显示节点备注逻辑 }, hide: () => {// 你的隐藏节点备注逻辑 }} | |
|
||||
| readonly(v0.1.7+) | Boolean | false | 是否是只读模式 | |
|
||||
|
||||
|
||||
### 实例方法:
|
||||
@@ -126,7 +137,11 @@ const mindMap = new MindMap({
|
||||
|
||||
容器尺寸变化后,需要调用该方法进行适应
|
||||
|
||||
#### setMode(mode)
|
||||
|
||||
v0.1.7+。切换模式为只读或编辑。
|
||||
|
||||
`mode`:readonly、edit
|
||||
|
||||
#### on(event, fn)
|
||||
|
||||
@@ -288,6 +303,14 @@ v0.1.5+
|
||||
|
||||
### 方法
|
||||
|
||||
#### clearActive()
|
||||
|
||||
清除当前激活的节点
|
||||
|
||||
#### clearAllActive()
|
||||
|
||||
清除当前所有激活节点,并会触发`node_active`事件
|
||||
|
||||
#### startTextEdit()
|
||||
|
||||
(v0.1.6+)若有文字编辑需求可调用该方法,会禁用回车键和删除键相关快捷键防止冲突
|
||||
|
||||
@@ -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.9f8f33bc.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.6fd71983.css" rel="preload" as="style"><link href="dist/js/app.ae3c62cc.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.9f8f33bc.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.ae3c62cc.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/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>
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
|
||||
// 默认选项配置
|
||||
const defaultOpt = {
|
||||
// 是否只读
|
||||
readonly: false,
|
||||
// 布局
|
||||
layout: 'logicalStructure',
|
||||
// 主题
|
||||
@@ -347,6 +349,24 @@ class MindMap {
|
||||
y: y - this.elRect.top
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* javascript comment
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-06-08 14:12:38
|
||||
* @Desc: 设置只读模式、编辑模式
|
||||
*/
|
||||
setMode(mode) {
|
||||
if (!['readonly', 'edit'].includes(mode)) {
|
||||
return
|
||||
}
|
||||
this.opt.readonly = mode === 'readonly'
|
||||
if (this.opt.readonly) {
|
||||
// 取消当前激活的元素
|
||||
this.renderer.clearAllActive()
|
||||
}
|
||||
this.emit('mode_change', mode)
|
||||
}
|
||||
}
|
||||
|
||||
export default MindMap
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.8",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -71,6 +71,9 @@ class Drag extends Base {
|
||||
bindEvent() {
|
||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
|
||||
this.mindMap.on('node_mousedown', (node, e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (e.which !== 1 || node.isRoot) {
|
||||
return
|
||||
}
|
||||
@@ -96,6 +99,9 @@ class Drag extends Base {
|
||||
this.mouseDownY = y
|
||||
})
|
||||
this.mindMap.on('mousemove', (e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class Export {
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.wdith, rect.height)
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
@@ -223,4 +223,4 @@ class Export {
|
||||
}
|
||||
}
|
||||
|
||||
export default Export
|
||||
export default Export
|
||||
|
||||
@@ -77,7 +77,7 @@ class Node {
|
||||
this._rectInfo = {
|
||||
imgContentWidth: 0,
|
||||
imgContentHeight: 0,
|
||||
textContentHeight: 0,
|
||||
textContentWidth: 0,
|
||||
textContentHeight: 0
|
||||
}
|
||||
// 各种文字信息的间距
|
||||
@@ -576,11 +576,17 @@ class Node {
|
||||
})
|
||||
// 双击事件
|
||||
this.group.on('dblclick', (e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
this.mindMap.emit('node_dblclick', this, e)
|
||||
})
|
||||
// 右键菜单事件
|
||||
this.group.on('contextmenu', (e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (this.nodeData.data.isActive) {
|
||||
@@ -597,6 +603,9 @@ class Node {
|
||||
* @Desc: 激活节点
|
||||
*/
|
||||
active(e) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
if (this.nodeData.data.isActive) {
|
||||
return
|
||||
@@ -1000,4 +1009,4 @@ class Node {
|
||||
}
|
||||
}
|
||||
|
||||
export default Node
|
||||
export default Node
|
||||
|
||||
@@ -31,6 +31,9 @@ class Select {
|
||||
bindEvent() {
|
||||
this.checkInNodes = throttle(this.checkInNodes, 500, this)
|
||||
this.mindMap.on('mousedown', (e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (e.which !== 3) {
|
||||
return
|
||||
}
|
||||
@@ -41,6 +44,9 @@ class Select {
|
||||
this.createRect(x, y)
|
||||
})
|
||||
this.mindMap.on('mousemove', (e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (!this.isMousedown) {
|
||||
return
|
||||
}
|
||||
@@ -54,6 +60,9 @@ class Select {
|
||||
this.onMove(x, y)
|
||||
})
|
||||
this.mindMap.on('mouseup', (e) => {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (!this.isMousedown) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ export const imgToDataUrl = (src) => {
|
||||
// 图片绘制到canvas里
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height)
|
||||
resolve(canvas.toDataURL())
|
||||
} catch (error) {
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
}
|
||||
@@ -243,4 +243,4 @@ export const asyncRun = (taskList, callback = () => {}) => {
|
||||
}, 0)
|
||||
}
|
||||
loop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +200,7 @@ export default {
|
||||
'expand_btn_click',
|
||||
'svg_mousedown',
|
||||
'mouseup',
|
||||
'mode_change'
|
||||
].forEach((event) => {
|
||||
this.mindMap.on(event, (...args) => {
|
||||
this.$bus.$emit(event, ...args)
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
<template>
|
||||
<div class="navigatorContainer">
|
||||
<div class="item">
|
||||
<el-switch
|
||||
v-model="isReadonly"
|
||||
active-text="只读模式"
|
||||
inactive-text="编辑模式"
|
||||
@change="readonlyChange"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
<div class="item">
|
||||
<Scale :mindMap="mindMap"></Scale>
|
||||
</div>
|
||||
@@ -28,6 +37,16 @@ export default {
|
||||
mindMap: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isReadonly: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
readonlyChange(value) {
|
||||
this.mindMap.setMode(value ? 'readonly' : 'edit')
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
v-for="icon in item.list"
|
||||
:key="icon.name"
|
||||
v-html="icon.icon"
|
||||
:class="{
|
||||
selected: iconList.includes(item.type + '_' + icon.name)
|
||||
}"
|
||||
@click="setIcon(item.type, icon.name)"
|
||||
></div>
|
||||
</div>
|
||||
@@ -34,7 +37,7 @@ export default {
|
||||
return {
|
||||
nodeIconList,
|
||||
dialogVisible: false,
|
||||
icon: [],
|
||||
iconList: [],
|
||||
activeNodes: [],
|
||||
};
|
||||
},
|
||||
@@ -43,10 +46,11 @@ export default {
|
||||
this.activeNodes = args[1];
|
||||
if (this.activeNodes.length > 0) {
|
||||
let firstNode = this.activeNodes[0];
|
||||
this.icon = firstNode.getData("icon") || [];
|
||||
this.iconList = firstNode.getData("icon") || [];
|
||||
} else {
|
||||
this.icon = [];
|
||||
this.iconList = [];
|
||||
}
|
||||
console.log(this.iconList, nodeIconList);
|
||||
});
|
||||
this.$bus.$on("showNodeIcon", () => {
|
||||
this.dialogVisible = true;
|
||||
@@ -59,16 +63,27 @@ export default {
|
||||
* @Desc: 设置icon
|
||||
*/
|
||||
setIcon(type, name) {
|
||||
let index = this.icon.findIndex((item) => {
|
||||
return item.split("_")[0] === type;
|
||||
let key = type + "_" + name;
|
||||
let index = this.iconList.findIndex((item) => {
|
||||
return item === key;
|
||||
});
|
||||
// 删除icon
|
||||
if (index !== -1) {
|
||||
this.icon.splice(index, 1, type + "_" + name);
|
||||
this.iconList.splice(index, 1);
|
||||
} else {
|
||||
this.icon.push(type + "_" + name);
|
||||
let typeIndex = this.iconList.findIndex((item) => {
|
||||
return item.split("_")[0] === type;
|
||||
});
|
||||
// 替换icon
|
||||
if (typeIndex !== -1) {
|
||||
this.iconList.splice(typeIndex, 1, key);
|
||||
} else {
|
||||
// 增加icon
|
||||
this.iconList.push(key);
|
||||
}
|
||||
}
|
||||
this.activeNodes.forEach((node) => {
|
||||
node.setIcon([...this.icon]);
|
||||
node.setIcon([...this.iconList]);
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -81,6 +96,10 @@ export default {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.deleteBtn {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 20px;
|
||||
font-weight: bold;
|
||||
@@ -99,6 +118,20 @@ export default {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&.selected {
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: -4px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #409EFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div
|
||||
class="toolbarBtn"
|
||||
:class="{
|
||||
disabled: backEnd,
|
||||
disabled: readonly || backEnd,
|
||||
}"
|
||||
@click="$bus.$emit('execCommand', 'BACK')"
|
||||
>
|
||||
@@ -16,7 +16,7 @@
|
||||
<div
|
||||
class="toolbarBtn"
|
||||
:class="{
|
||||
disabled: forwardEnd,
|
||||
disabled: readonly || forwardEnd,
|
||||
}"
|
||||
@click="$bus.$emit('execCommand', 'FORWARD')"
|
||||
>
|
||||
@@ -178,7 +178,8 @@ export default {
|
||||
return {
|
||||
activeNodes: [],
|
||||
backEnd: false,
|
||||
forwardEnd: true
|
||||
forwardEnd: true,
|
||||
readonly: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -189,6 +190,9 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$bus.$on("mode_change", (mode) => {
|
||||
this.readonly = mode === 'readonly'
|
||||
});
|
||||
this.$bus.$on("node_active", (...args) => {
|
||||
this.activeNodes = args[1];
|
||||
});
|
||||
@@ -251,6 +255,7 @@ export default {
|
||||
&.disabled {
|
||||
color: #bcbcbc;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
||||
Reference in New Issue
Block a user