Compare commits

..

12 Commits
0.6.2 ... 0.6.3

Author SHA1 Message Date
街角小林
4bb349b2df Merge pull request #151 from F-star/main
Feat: 以画布为中心进行缩放
2023-06-26 15:25:08 +08:00
Hao Huang
b262336f08 Feat: 以画布为中心进行缩放 2023-06-26 05:47:06 +00:00
wanglin2
2b59087461 Merge branch 'feature' into main 2023-06-26 10:33:03 +08:00
wanglin2
66c9805efc Doc: update 2023-06-26 10:32:36 +08:00
wanglin2
710128901a 更新群二维码 2023-06-26 09:56:10 +08:00
wanglin2
61be0f7ac4 Merge branch 'feature' into main 2023-06-24 17:08:18 +08:00
wanglin2
7289b3a0ad Doc:update 2023-06-24 17:06:52 +08:00
wanglin2
25243e2053 Merge branch 'feature' into main 2023-06-21 15:56:20 +08:00
wanglin2
060a448cd5 打包0.6.3 2023-06-21 15:55:38 +08:00
wanglin2
bdb6078df6 '更新文档' 2023-06-21 15:33:51 +08:00
wanglin2
749a4d0e81 Feat:支持自定义节点内容 2023-06-21 14:57:22 +08:00
wanglin2
1749705694 Fix:修复概要节点会响应快捷键添加节点的问题 2023-06-21 10:50:26 +08:00
33 changed files with 609 additions and 157 deletions

View File

@@ -92,6 +92,8 @@ MIT
<img src="./qrcode.jpg" style="width: 300px" />
如果已过期,可以微信添加`wanglinguanfang`拉你入群。
# 请作者喝杯咖啡
> 厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -11,127 +11,7 @@ import { layoutValueList, CONSTANTS } from './src/constants/constant'
import { SVG } from '@svgdotjs/svg.js'
import { simpleDeepClone } from './src/utils'
import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default'
// 默认选项配置
const defaultOpt = {
// 是否只读
readonly: false,
// 布局
layout: CONSTANTS.LAYOUT.LOGICAL_STRUCTURE,
// 如果结构为鱼骨图,那么可以通过该选项控制倾斜角度
fishboneDeg: 45,
// 主题
theme: 'default', // 内置主题default默认主题
// 主题配置,会和所选择的主题进行合并
themeConfig: {},
// 放大缩小的增量比例
scaleRatio: 0.1,
// 最多显示几个标签
maxTag: 5,
// 导出图片时的内边距
exportPadding: 20,
// 展开收缩按钮尺寸
expandBtnSize: 20,
// 节点里图片和文字的间距
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2,
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null,
/*
{
show(){},
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
},
// 达到该宽度文本自动换行
textAutoWrapWidth: 500,
// 自定义鼠标滚轮事件处理
// 可以传一个函数,回调参数为事件对象
customHandleMousewheel: null,
// 鼠标滚动的行为如果customHandleMousewheel传了自定义函数这个属性不生效
mousewheelAction: CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM,// zoom放大缩小、move上下移动
// 当mousewheelAction设为move时可以通过该属性控制鼠标滚动一下视图移动的步长单位px
mousewheelMoveStep: 100,
// 默认插入的二级节点的文字
defaultInsertSecondLevelNodeText: '二级节点',
// 默认插入的二级以下节点的文字
defaultInsertBelowSecondLevelNodeText: '分支主题',
// 展开收起按钮的颜色
expandBtnStyle: {
color: '#808080',
fill: '#fff'
},
// 自定义展开收起按钮的图标
expandBtnIcon: {
open: '',// svg字符串
close: ''
},
// 是否只有当鼠标在画布内才响应快捷键事件
enableShortcutOnlyWhenMouseInSvg: true,
// 是否开启节点动画过渡
enableNodeTransitionMove: true,
// 如果开启节点动画过渡可以通过该属性设置过渡的时间单位ms
nodeTransitionMoveDuration: 300,
// 初始根节点的位置
initRootNodePosition: null,
// 导出png、svg、pdf时的图形内边距
exportPaddingX: 10,
exportPaddingY: 10,
// 节点文本编辑框的z-index
nodeTextEditZIndex: 3000,
// 节点备注浮层的z-index
nodeNoteTooltipZIndex: 3000,
// 是否在点击了画布外的区域时结束节点文本的编辑状态
isEndNodeTextEditOnClickOuter: true,
// 最大历史记录数
maxHistoryCount: 1000,
// 是否一直显示节点的展开收起按钮,默认为鼠标移上去和激活时才显示
alwaysShowExpandBtn: false,
// 扩展节点可插入的图标
iconList: [
// {
// name: '',// 分组名称
// type: '',// 分组的值
// list: [// 分组下的图标列表
// {
// name: '',// 图标名称
// icon:''// 图标可以传svg或图片
// }
// ]
// }
],
// 节点最大缓存数量
maxNodeCacheCount: 1000,
// 关联线默认文字
defaultAssociativeLineText: '关联',
// 思维导图适应画布大小时的内边距
fitPadding: 50,
// 是否开启按住ctrl键多选节点功能
enableCtrlKeyNodeSelection: true,
// 设置为左键多选节点,右键拖动画布
useLeftKeySelectionRightKeyDrag: false,
// 节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例
beforeTextEdit: null
}
import { defaultOpt } from './src/constants/defaultOptions'
// 思维导图
class MindMap {

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.6.2",
"version": "0.6.3",
"description": "一个简单的web在线思维导图",
"authors": [
{

View File

@@ -0,0 +1,126 @@
import { CONSTANTS } from './constant'
// 默认选项配置
export const defaultOpt = {
// 是否只读
readonly: false,
// 布局
layout: CONSTANTS.LAYOUT.LOGICAL_STRUCTURE,
// 如果结构为鱼骨图,那么可以通过该选项控制倾斜角度
fishboneDeg: 45,
// 主题
theme: 'default', // 内置主题default默认主题
// 主题配置,会和所选择的主题进行合并
themeConfig: {},
// 放大缩小的增量比例
scaleRatio: 0.1,
// 最多显示几个标签
maxTag: 5,
// 导出图片时的内边距
exportPadding: 20,
// 展开收缩按钮尺寸
expandBtnSize: 20,
// 节点里图片和文字的间距
imgTextMargin: 5,
// 节点里各种文字信息的间距,如图标和文字的间距
textContentMargin: 2,
// 多选节点时鼠标移动到边缘时的画布移动偏移量
selectTranslateStep: 3,
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null,
/*
{
show(){},
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
},
// 达到该宽度文本自动换行
textAutoWrapWidth: 500,
// 自定义鼠标滚轮事件处理
// 可以传一个函数,回调参数为事件对象
customHandleMousewheel: null,
// 鼠标滚动的行为如果customHandleMousewheel传了自定义函数这个属性不生效
mousewheelAction: CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM, // zoom放大缩小、move上下移动
// 当mousewheelAction设为move时可以通过该属性控制鼠标滚动一下视图移动的步长单位px
mousewheelMoveStep: 100,
// 默认插入的二级节点的文字
defaultInsertSecondLevelNodeText: '二级节点',
// 默认插入的二级以下节点的文字
defaultInsertBelowSecondLevelNodeText: '分支主题',
// 展开收起按钮的颜色
expandBtnStyle: {
color: '#808080',
fill: '#fff'
},
// 自定义展开收起按钮的图标
expandBtnIcon: {
open: '', // svg字符串
close: ''
},
// 是否只有当鼠标在画布内才响应快捷键事件
enableShortcutOnlyWhenMouseInSvg: true,
// 是否开启节点动画过渡
enableNodeTransitionMove: true,
// 如果开启节点动画过渡可以通过该属性设置过渡的时间单位ms
nodeTransitionMoveDuration: 300,
// 初始根节点的位置
initRootNodePosition: null,
// 导出png、svg、pdf时的图形内边距
exportPaddingX: 10,
exportPaddingY: 10,
// 节点文本编辑框的z-index
nodeTextEditZIndex: 3000,
// 节点备注浮层的z-index
nodeNoteTooltipZIndex: 3000,
// 是否在点击了画布外的区域时结束节点文本的编辑状态
isEndNodeTextEditOnClickOuter: true,
// 最大历史记录数
maxHistoryCount: 1000,
// 是否一直显示节点的展开收起按钮,默认为鼠标移上去和激活时才显示
alwaysShowExpandBtn: false,
// 扩展节点可插入的图标
iconList: [
// {
// name: '',// 分组名称
// type: '',// 分组的值
// list: [// 分组下的图标列表
// {
// name: '',// 图标名称
// icon:''// 图标可以传svg或图片
// }
// ]
// }
],
// 节点最大缓存数量
maxNodeCacheCount: 1000,
// 关联线默认文字
defaultAssociativeLineText: '关联',
// 思维导图适应画布大小时的内边距
fitPadding: 50,
// 是否开启按住ctrl键多选节点功能
enableCtrlKeyNodeSelection: true,
// 设置为左键多选节点,右键拖动画布
useLeftKeySelectionRightKeyDrag: false,
// 节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例
beforeTextEdit: null,
// 是否开启自定义节点内容
isUseCustomNodeContent: false,
// 自定义返回节点内容的方法
customCreateNodeContent: null
}

View File

@@ -425,6 +425,9 @@ class Render {
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
let first = list[0]
if (first.isGeneralization) {
return
}
if (first.isRoot) {
this.insertChildNode(openEdit, appointNodes, appointData)
} else {
@@ -455,6 +458,9 @@ class Render {
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
list.forEach(node => {
if (node.isGeneralization) {
return
}
if (!node.nodeData.children) {
node.nodeData.children = []
}

View File

@@ -66,10 +66,15 @@ export default class TextEdit {
// 显示文本编辑框
async show(node) {
if (typeof this.mindMap.opt.beforeTextEdit === 'function') {
// 使用了自定义节点内容那么不响应编辑事件
if (node.isUseCustomNodeContent()) {
return
}
let { beforeTextEdit } = this.mindMap.opt
if (typeof beforeTextEdit === 'function') {
let isShow = false
try {
isShow = await this.mindMap.opt.beforeTextEdit(node)
isShow = await beforeTextEdit(node)
} catch (error) {
isShow = false
}

View File

@@ -1,7 +1,7 @@
import Style from './Style'
import Shape from './Shape'
import { asyncRun } from '../../../utils'
import { G, Rect } from '@svgdotjs/svg.js'
import { asyncRun, nodeToHTML } from '../../../utils'
import { G, Rect, ForeignObject, SVG } from '@svgdotjs/svg.js'
import nodeGeneralizationMethods from './nodeGeneralization'
import nodeExpandBtnMethods from './nodeExpandBtn'
import nodeCommandWrapsMethods from './nodeCommandWraps'
@@ -59,6 +59,7 @@ class Node {
this.group = null
this.shapeNode = null // 节点形状节点
// 节点内容对象
this._customNodeContent = null
this._imgData = null
this._iconData = null
this._textData = null
@@ -154,6 +155,13 @@ class Node {
// 创建节点的各个内容对象数据
createNodeData() {
// 自定义节点内容
let { isUseCustomNodeContent, customCreateNodeContent } = this.mindMap.opt
if (isUseCustomNodeContent && customCreateNodeContent) {
this._customNodeContent = customCreateNodeContent(this)
}
// 如果没有返回内容,那么还是使用内置的节点内容
if (this._customNodeContent) return
this._imgData = this.createImgNode()
this._iconData = this.createIconNode()
this._textData = this.createTextNode()
@@ -176,6 +184,14 @@ class Node {
// 计算节点尺寸信息
getNodeRect() {
// 自定义节点内容
if (this.isUseCustomNodeContent()) {
let rect = this.measureCustomNodeContentSize(this._customNodeContent)
return {
width: rect.width,
height: rect.height
}
}
// 宽高
let imgContentWidth = 0
let imgContentHeight = 0
@@ -266,6 +282,15 @@ class Node {
if (this.isGeneralization && this.generalizationBelongNode) {
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
}
// 如果存在自定义节点内容,那么使用自定义节点内容
if (this.isUseCustomNodeContent()) {
let foreignObject = new ForeignObject()
foreignObject.width(width)
foreignObject.height(height)
foreignObject.add(SVG(this._customNodeContent))
this.group.add(foreignObject)
return
}
// 图片节点
let imgHeight = 0
if (this._imgData) {

View File

@@ -280,6 +280,32 @@ function createNoteNode() {
}
}
// 测量自定义节点内容元素的宽高
let warpEl = null
function measureCustomNodeContentSize (content) {
if (!warpEl) {
warpEl = document.createElement('div')
warpEl.style.cssText = `
position: fixed;
left: -99999px;
top: -99999px;
`
this.mindMap.el.appendChild(warpEl)
}
warpEl.innerHTML = ''
warpEl.appendChild(content)
let rect = warpEl.getBoundingClientRect()
return {
width: rect.width,
height: rect.height
}
}
// 是否使用的是自定义节点内容
function isUseCustomNodeContent() {
return !!this._customNodeContent
}
export default {
createImgNode,
getImgShowSize,
@@ -288,5 +314,7 @@ export default {
createTextNode,
createHyperlinkNode,
createTagNode,
createNoteNode
createNoteNode,
measureCustomNodeContentSize,
isUseCustomNodeContent
}

View File

@@ -170,8 +170,8 @@ class View {
// 应用变换
transform() {
this.mindMap.draw.transform({
origin: [0, 0],
scale: this.scale,
// origin: 'center center',
translate: [this.x, this.y]
})
this.mindMap.emit('view_data_change', this.getTransformData())
@@ -191,22 +191,36 @@ class View {
// 缩小
narrow() {
let scale
if (this.scale - this.mindMap.opt.scaleRatio > 0.1) {
this.scale -= this.mindMap.opt.scaleRatio
scale = this.scale - this.mindMap.opt.scaleRatio
} else {
this.scale = 0.1
scale = 0.1
}
this.scaleInCenter(this.mindMap.width / 2, this.mindMap.height / 2, scale)
this.transform()
this.mindMap.emit('scale', this.scale)
}
// 放大
enlarge() {
this.scale += this.mindMap.opt.scaleRatio
const scale = this.scale + this.mindMap.opt.scaleRatio
this.scaleInCenter(this.mindMap.width / 2, this.mindMap.height / 2, scale)
this.transform()
this.mindMap.emit('scale', this.scale)
}
// 基于画布中心进行缩放
scaleInCenter(cx, cy, scale) {
const prevScale = this.scale
const dx = (cx - this.x) * (1 - scale / prevScale)
const dy = (cy - this.y) * (1 - scale / prevScale)
this.x += dx;
this.y += dy;
this.scale = scale
}
// 设置缩放
setScale(scale) {
this.scale = scale

View File

@@ -356,4 +356,15 @@ export const readBlob = (blob) => {
}
reader.readAsDataURL(blob)
})
}
// 将dom节点转换成html字符串
let nodeToHTMLWrapEl = null
export const nodeToHTML = (node) => {
if (!nodeToHTMLWrapEl) {
nodeToHTMLWrapEl = document.createElement('div')
}
nodeToHTMLWrapEl.innerHTML = ''
nodeToHTMLWrapEl.appendChild(node)
return nodeToHTMLWrapEl.innerHTML
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -11,7 +11,7 @@ let langList = [
}
]
let StartList = ['introduction', 'start', 'deploy', 'client', 'translate', 'changelog']
let CourseList = new Array(19).fill(0).map((_, index) => {
let CourseList = new Array(20).fill(0).map((_, index) => {
return 'course' + (index + 1)
})
let APIList = [

View File

@@ -1,5 +1,11 @@
# Changelog
## 0.6.3
Fix: 1.Fix the issue where the summary node will respond to inserting node shortcuts.
New: 1.Support custom node content.
## 0.6.2
Fix: 1.Fixed the problem that the new node does not change with the theme in rich Text mode.

View File

@@ -1,6 +1,9 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.6.3</h2>
<p>Fix: 1.Fix the issue where the summary node will respond to inserting node shortcuts.</p>
<p>New: 1.Support custom node content.</p>
<h2>0.6.2</h2>
<p>Fix: 1.Fixed the problem that the new node does not change with the theme in rich Text mode.</p>
<h2>0.6.1</h2>

View File

@@ -67,6 +67,8 @@ const mindMap = new MindMap({
| enableCtrlKeyNodeSelectionv0.6.0+ | Boolean | true | Whether to enable the function of holding down the Ctrl key to select multiple nodes | |
| useLeftKeySelectionRightKeyDragv0.6.0+ | Boolean | false | Setting to left click to select multiple nodes and right click to drag the canvas. | |
| beforeTextEditv0.6.0+ | Function/null | null | The callback method before the node is about to enter editing. If the method returns a value other than true, the editing will be canceled. The function can return a value or a promise, and the callback parameter is the node instance | |
| isUseCustomNodeContentv0.6.3+ | Boolean | false | Whether to customize node content | |
| customCreateNodeContentv0.6.3+ | Function/null | null | If `isUseCustomNodeContent` is set to `true`, then this option needs to be used to pass in a method that receives the node instance `node` as a parameter (if you want to obtain data for that node, you can use `node.nodeData.data`). You need to return the custom node content element, which is the DOM node. If a node does not require customization, you can return `null` | |
### Watermark config

View File

@@ -329,6 +329,20 @@
<td>The callback method before the node is about to enter editing. If the method returns a value other than true, the editing will be canceled. The function can return a value or a promise, and the callback parameter is the node instance</td>
<td></td>
</tr>
<tr>
<td>isUseCustomNodeContentv0.6.3+</td>
<td>Boolean</td>
<td>false</td>
<td>Whether to customize node content</td>
<td></td>
</tr>
<tr>
<td>customCreateNodeContentv0.6.3+</td>
<td>Function/null</td>
<td>null</td>
<td>If <code>isUseCustomNodeContent</code> is set to <code>true</code>, then this option needs to be used to pass in a method that receives the node instance <code>node</code> as a parameter (if you want to obtain data for that node, you can use <code>node.nodeData.data</code>). You need to return the custom node content element, which is the DOM node. If a node does not require customization, you can return <code>null</code></td>
<td></td>
</tr>
</tbody>
</table>
<h3>Watermark config</h3>

View File

@@ -77,7 +77,7 @@ If you need a file in the format of `umd` module, such as `CDN` in the browser,
<script scr="simpleMindMap.umd.min.js"></script>
```
A global variable `window.simpleMindMap` will be created.
A global variable `window.simpleMindMap` will be created. you can get `MindMap` constructor by `window.simpleMindMap.default`, for more detail info you can log `window.simpleMindMap`.
The disadvantage of this method is that it will contain all the content, including the plugins you have not registered, so the overall volume will be relatively large.

View File

@@ -57,7 +57,7 @@ compile this dependency:</p>
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;simpleMindMap.css&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">scr</span>=<span class="hljs-string">&quot;simpleMindMap.umd.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>A global variable <code>window.simpleMindMap</code> will be created.</p>
<p>A global variable <code>window.simpleMindMap</code> will be created. you can get <code>MindMap</code> constructor by <code>window.simpleMindMap.default</code>, for more detail info you can log <code>window.simpleMindMap</code>.</p>
<p>The disadvantage of this method is that it will contain all the content, including the plugins you have not registered, so the overall volume will be relatively large.</p>
<p>v0.5.4+If you want to use the <code>ES</code> module directly on the browser side, you can find the <code>simpleMindMap.esm.js</code> and <code>simpleMindMap.esm.css</code> files in the <code>/simple-mind-map/dist/</code> directory.</p>
<h2>Development</h2>

View File

@@ -26,6 +26,7 @@ export default [
{ path: 'course17', title: '导入和导出' },
{ path: 'course18', title: '如何持久化数据' },
{ path: 'course19', title: '插入和扩展节点图标' },
{ path: 'course20', title: '如何自定义节点内容' },
{ path: 'doExport', title: 'Export 插件' },
{ path: 'drag', title: 'Drag插件' },
{ path: 'introduction', title: '简介' },

View File

@@ -1,5 +1,11 @@
# Changelog
## 0.6.3
修复1.修复概要节点会响应插入节点快捷键的问题。
新增1.支持自定义节点内容。
## 0.6.2
修复1.修复富文本模式下,新建节点不随主题变化而变化的问题。

View File

@@ -1,6 +1,9 @@
<template>
<div>
<h1>Changelog</h1>
<h2>0.6.3</h2>
<p>修复1.修复概要节点会响应插入节点快捷键的问题</p>
<p>新增1.支持自定义节点内容</p>
<h2>0.6.2</h2>
<p>修复1.修复富文本模式下新建节点不随主题变化而变化的问题</p>
<h2>0.6.1</h2>

View File

@@ -67,6 +67,8 @@ const mindMap = new MindMap({
| enableCtrlKeyNodeSelectionv0.6.0+ | Boolean | true | 是否开启按住ctrl键多选节点的功能 | |
| useLeftKeySelectionRightKeyDragv0.6.0+ | Boolean | false | 设置为左键多选节点,右键拖动画布 | |
| beforeTextEditv0.6.0+ | Function/null | null | 节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例 | |
| isUseCustomNodeContentv0.6.3+ | Boolean | false | 是否自定义节点内容 | |
| customCreateNodeContentv0.6.3+ | Function/null | null | 如果`isUseCustomNodeContent`设为`true`,那么需要使用该选项传入一个方法,接收节点实例`node`为参数(如果要获取该节点的数据,可以通过`node.nodeData.data`需要返回自定义节点内容元素也就是DOM节点如果某个节点不需要自定义那么返回`null`即可 | |
### 水印配置

View File

@@ -329,6 +329,20 @@
<td>节点即将进入编辑前的回调方法如果该方法返回true以外的值那么将取消编辑函数可以返回一个值或一个Promise回调参数为节点实例</td>
<td></td>
</tr>
<tr>
<td>isUseCustomNodeContentv0.6.3+</td>
<td>Boolean</td>
<td>false</td>
<td>是否自定义节点内容</td>
<td></td>
</tr>
<tr>
<td>customCreateNodeContentv0.6.3+</td>
<td>Function/null</td>
<td>null</td>
<td>如果<code>isUseCustomNodeContent</code>设为<code>true</code>那么需要使用该选项传入一个方法接收节点实例<code>node</code>为参数如果要获取该节点的数据可以通过<code>node.nodeData.data</code>需要返回自定义节点内容元素也就是DOM节点如果某个节点不需要自定义那么返回<code>null</code>即可</td>
<td></td>
</tr>
</tbody>
</table>
<h3>水印配置</h3>

View File

@@ -0,0 +1,119 @@
# 如何自定义节点内容
> 该特性v0.6.3+版本支持
如果你想自定义节点的内容,那么可以在实例化`simple-mind-map`时传入以下选项:
```js
new MindMap({
isUseCustomNodeContent: true,
customCreateNodeContent: (node) => {
// return你的自定义DOM节点
}
})
```
`customCreateNodeContent`方法会接收当前遍历到的节点实例作为参数,一般而言你会需要该节点的数据,这可以通过如下方式获取:
```js
node.nodeData.data
```
其他节点实例属性你可以自行打印出来看看。
`customCreateNodeContent`方法需要返回`DOM`节点,如果某个节点你不想自定义,那么可以返回`null`,那么还是会走内置的节点渲染逻辑。
返回的`DOM`节点的宽高需要是确定的,如果是动态的那么会导致宽高获取错误,最终导致节点定位错误和发生重叠等问题。
如果使用了自定义节点内容,那么内置的插入节点内容的相关方法你都不应该再使用,因为相当于整个节点内容都由你自己控制,另外,节点样式设置也不会再生效,切换主题也只会切换非节点内容的样式,最后,双击节点也不会再进入编辑,所以这个功能一般用于展示性的需求。
## 示例1渲染自定义DOM节点
```js
{
customCreateNodeContent: (node) => {
let div = document.createElement('div')
div.className = 'xxx'
div.style.cssText = `xxx`
div.innerHTML = `
<h1>我是自定义节点</h1>
${ node.nodeData.text }
`
return div
}
}
```
## 示例2渲染Vue2组件
如果想要使用一个相对简单的`Vue`组件,那么可以通过如下方式:
```js
import CustomNodeContent from 'CustomNodeContent.vue'
import Vue from 'vue'
{
customCreateNodeContent: (node) => {
let el = document.createElement('div')
let Comp = Vue.extend(CustomNodeContent)
let comp = new Comp({
// props
propsData: {
html: node.nodeData.data.text
}
})
comp.$mount(el)
return comp.$el
}
}
```
如果你的`Vue`组件比较复杂,里面用到了`vueRouter``vuex``i18n`等,那么要和你项目的入口组件一样,在实例化时要把这些内容也加载到组件内,不然会报错。
```js
import CustomNodeContent from 'CustomNodeContent.vue'
import Vue from 'vue'
import router from './router'
import store from './store'
import i18n from './i18n'
{
customCreateNodeContent: (node) => {
let el = document.createElement('div')
let Comp = Vue.extend(CustomNodeContent)
let comp = new Comp({
// props
propsData: {
html: node.nodeData.data.text
},
router,
store,
i18n
})
comp.$mount(el)
return comp.$el
}
}
```
## 示例3渲染Vue3组件
```js
import { createApp } from "vue"
import CustomNodeContent from './CustomNodeContent.vue'
{
customCreateNodeContent: (node) => {
let el = document.createElement('div')
const app = createApp(CustomNodeContent, {// props
html: node.nodeData.data.text
})
app.mount(el)
return el
}
}
```
## 示例4渲染react组件
如果你成功渲染了`react`组件,欢迎[提交](https://github.com/wanglin2/mind-map/issues/new)示例代码给我~

View File

@@ -0,0 +1,110 @@
<template>
<div>
<h1>如何自定义节点内容</h1>
<blockquote>
<p>该特性v0.6.3+版本支持</p>
</blockquote>
<p>如果你想自定义节点的内容那么可以在实例化<code>simple-mind-map</code>时传入以下选项</p>
<pre class="hljs"><code><span class="hljs-keyword">new</span> MindMap({
<span class="hljs-attr">isUseCustomNodeContent</span>: <span class="hljs-literal">true</span>,
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-comment">// return你的自定义DOM节点</span>
}
})
</code></pre>
<p><code>customCreateNodeContent</code>方法会接收当前遍历到的节点实例作为参数一般而言你会需要该节点的数据这可以通过如下方式获取</p>
<pre class="hljs"><code>node.nodeData.data
</code></pre>
<p>其他节点实例属性你可以自行打印出来看看</p>
<p><code>customCreateNodeContent</code>方法需要返回<code>DOM</code>节点如果某个节点你不想自定义那么可以返回<code>null</code>那么还是会走内置的节点渲染逻辑</p>
<p>返回的<code>DOM</code>节点的宽高需要是确定的如果是动态的那么会导致宽高获取错误最终导致节点定位错误和发生重叠等问题</p>
<p>如果使用了自定义节点内容那么内置的插入节点内容的相关方法你都不应该再使用因为相当于整个节点内容都由你自己控制另外节点样式设置也不会再生效切换主题也只会切换非节点内容的样式最后双击节点也不会再进入编辑所以这个功能一般用于展示性的需求</p>
<h2>示例1渲染自定义DOM节点</h2>
<pre class="hljs"><code>{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> div = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
div.className = <span class="hljs-string">&#x27;xxx&#x27;</span>
div.style.cssText = <span class="hljs-string">`xxx`</span>
div.innerHTML = <span class="hljs-string">`
&lt;h1&gt;我是自定义节点&lt;/h1&gt;
<span class="hljs-subst">${ node.nodeData.text }</span>
`</span>
<span class="hljs-keyword">return</span> div
}
}
</code></pre>
<h2>示例2渲染Vue2组件</h2>
<p>如果想要使用一个相对简单的<code>Vue</code>组件那么可以通过如下方式</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> CustomNodeContent <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;CustomNodeContent.vue&#x27;</span>
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;vue&#x27;</span>
{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
<span class="hljs-keyword">let</span> Comp = Vue.extend(CustomNodeContent)
<span class="hljs-keyword">let</span> comp = <span class="hljs-keyword">new</span> Comp({
<span class="hljs-comment">// props</span>
<span class="hljs-attr">propsData</span>: {
<span class="hljs-attr">html</span>: node.nodeData.data.text
}
})
comp.$mount(el)
<span class="hljs-keyword">return</span> comp.$el
}
}
</code></pre>
<p>如果你的<code>Vue</code>组件比较复杂里面用到了<code>vueRouter</code><code>vuex</code><code>i18n</code>那么要和你项目的入口组件一样在实例化时要把这些内容也加载到组件内不然会报错</p>
<pre class="hljs"><code><span class="hljs-keyword">import</span> CustomNodeContent <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;CustomNodeContent.vue&#x27;</span>
<span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;vue&#x27;</span>
<span class="hljs-keyword">import</span> router <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./router&#x27;</span>
<span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./store&#x27;</span>
<span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./i18n&#x27;</span>
{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
<span class="hljs-keyword">let</span> Comp = Vue.extend(CustomNodeContent)
<span class="hljs-keyword">let</span> comp = <span class="hljs-keyword">new</span> Comp({
<span class="hljs-comment">// props</span>
<span class="hljs-attr">propsData</span>: {
<span class="hljs-attr">html</span>: node.nodeData.data.text
},
router,
store,
i18n
})
comp.$mount(el)
<span class="hljs-keyword">return</span> comp.$el
}
}
</code></pre>
<h2>示例3渲染Vue3组件</h2>
<pre class="hljs"><code><span class="hljs-keyword">import</span> { createApp } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;vue&quot;</span>
<span class="hljs-keyword">import</span> CustomNodeContent <span class="hljs-keyword">from</span> <span class="hljs-string">&#x27;./CustomNodeContent.vue&#x27;</span>
{
<span class="hljs-attr">customCreateNodeContent</span>: <span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
<span class="hljs-keyword">let</span> el = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">&#x27;div&#x27;</span>)
<span class="hljs-keyword">const</span> app = createApp(CustomNodeContent, {<span class="hljs-comment">// props</span>
<span class="hljs-attr">html</span>: node.nodeData.data.text
})
app.mount(el)
<span class="hljs-keyword">return</span> el
}
}
</code></pre>
<h2>示例4渲染react组件</h2>
<p>如果你成功渲染了<code>react</code>组件欢迎<a href="https://github.com/wanglin2/mind-map/issues/new">提交</a>示例代码给我~</p>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -125,4 +125,8 @@
<img src="../../../../assets/avatar/志斌.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>志斌</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>小土渣的宇宙</p>
</div>
</div>

View File

@@ -8,19 +8,19 @@
</blockquote>
<h2>特性</h2>
<ul>
<li><input type="checkbox" id="checkbox36" checked="true" /><label for="checkbox36">插件化架构除核心功能外其他功能作为插件提供按需使用减小整体体积</label></li>
<li><input type="checkbox" id="checkbox37" checked="true" /><label for="checkbox37">支持逻辑结构图思维导图组织结构图目录组织图时间轴鱼骨图六种结构</label></li>
<li><input type="checkbox" id="checkbox38" checked="true" /><label for="checkbox38">内置多种主题允许高度自定义样式支持注册新主题</label></li>
<li><input type="checkbox" id="checkbox39" checked="true" /><label for="checkbox39">支持快捷键</label></li>
<li><input type="checkbox" id="checkbox40" checked="true" /><label for="checkbox40">节点内容支持图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox41" checked="true" /><label for="checkbox41">支持前进后退</label></li>
<li><input type="checkbox" id="checkbox42" checked="true" /><label for="checkbox42">支持拖动缩放</label></li>
<li><input type="checkbox" id="checkbox43" checked="true" /><label for="checkbox43">支持右键和Ctrl+左键两种多选方式</label></li>
<li><input type="checkbox" id="checkbox44" checked="true" /><label for="checkbox44">支持节点自由拖拽拖拽调整</label></li>
<li><input type="checkbox" id="checkbox45" checked="true" /><label for="checkbox45">支持多种节点形状</label></li>
<li><input type="checkbox" id="checkbox46" checked="true" /><label for="checkbox46">支持导出为</label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code>支持从<code>json</code><code>xmind</code><code>markdown</code>导入</li>
<li><input type="checkbox" id="checkbox47" checked="true" /><label for="checkbox47">支持小地图支持水印</label></li>
<li><input type="checkbox" id="checkbox48" checked="true" /><label for="checkbox48">支持关联线</label></li>
<li><input type="checkbox" id="checkbox18" checked="true" /><label for="checkbox18">插件化架构除核心功能外其他功能作为插件提供按需使用减小整体体积</label></li>
<li><input type="checkbox" id="checkbox19" checked="true" /><label for="checkbox19">支持逻辑结构图思维导图组织结构图目录组织图时间轴鱼骨图六种结构</label></li>
<li><input type="checkbox" id="checkbox20" checked="true" /><label for="checkbox20">内置多种主题允许高度自定义样式支持注册新主题</label></li>
<li><input type="checkbox" id="checkbox21" checked="true" /><label for="checkbox21">支持快捷键</label></li>
<li><input type="checkbox" id="checkbox22" checked="true" /><label for="checkbox22">节点内容支持图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox23" checked="true" /><label for="checkbox23">支持前进后退</label></li>
<li><input type="checkbox" id="checkbox24" checked="true" /><label for="checkbox24">支持拖动缩放</label></li>
<li><input type="checkbox" id="checkbox25" checked="true" /><label for="checkbox25">支持右键和Ctrl+左键两种多选方式</label></li>
<li><input type="checkbox" id="checkbox26" checked="true" /><label for="checkbox26">支持节点自由拖拽拖拽调整</label></li>
<li><input type="checkbox" id="checkbox27" checked="true" /><label for="checkbox27">支持多种节点形状</label></li>
<li><input type="checkbox" id="checkbox28" checked="true" /><label for="checkbox28">支持导出为</label><code>json</code><code>png</code><code>svg</code><code>pdf</code><code>markdown</code>支持从<code>json</code><code>xmind</code><code>markdown</code>导入</li>
<li><input type="checkbox" id="checkbox29" checked="true" /><label for="checkbox29">支持小地图支持水印</label></li>
<li><input type="checkbox" id="checkbox30" checked="true" /><label for="checkbox30">支持关联线</label></li>
</ul>
<h2>仓库目录介绍</h2>
<p>1.<code>simple-mind-map</code></p>
@@ -28,11 +28,11 @@
<p>2.<code>web</code></p>
<p>使用<code>simple-mind-map</code>基于<code>vue2.x</code><code>ElementUI</code>搭建的在线思维导图特性</p>
<ul>
<li><input type="checkbox" id="checkbox49" checked="true" /><label for="checkbox49">工具栏支持插入节点删除节点编辑节点图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox50" checked="true" /><label for="checkbox50">侧边栏基础样式设置面板节点样式设置面板大纲面板主题选择面板结构选择面板</label></li>
<li><input type="checkbox" id="checkbox51" checked="true" /><label for="checkbox51">导入导出功能数据默认保存在浏览器本地存储也支持直接创建打开编辑电脑本地文件</label></li>
<li><input type="checkbox" id="checkbox52" checked="true" /><label for="checkbox52">右键菜单支持展开收起整理布局等操作</label></li>
<li><input type="checkbox" id="checkbox53" checked="true" /><label for="checkbox53">底部栏支持节点数量字数统计支持切换编辑和只读模式支持放大缩小支持全屏切换支持小地图</label></li>
<li><input type="checkbox" id="checkbox31" checked="true" /><label for="checkbox31">工具栏支持插入节点删除节点编辑节点图片图标超链接备注标签概要</label></li>
<li><input type="checkbox" id="checkbox32" checked="true" /><label for="checkbox32">侧边栏基础样式设置面板节点样式设置面板大纲面板主题选择面板结构选择面板</label></li>
<li><input type="checkbox" id="checkbox33" checked="true" /><label for="checkbox33">导入导出功能数据默认保存在浏览器本地存储也支持直接创建打开编辑电脑本地文件</label></li>
<li><input type="checkbox" id="checkbox34" checked="true" /><label for="checkbox34">右键菜单支持展开收起整理布局等操作</label></li>
<li><input type="checkbox" id="checkbox35" checked="true" /><label for="checkbox35">底部栏支持节点数量字数统计支持切换编辑和只读模式支持放大缩小支持全屏切换支持小地图</label></li>
</ul>
<p>提供文档页面服务</p>
<p>3.<code>dist</code></p>
@@ -84,6 +84,10 @@
<img src="../../../../assets/avatar/志斌.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>志斌</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
<p>小土渣的宇宙</p>
</div>
</div>
</div>
</template>

View File

@@ -72,7 +72,7 @@ const mindMap = new MindMap({
<script scr="simpleMindMap.umd.min.js"></script>
```
会创建一个全局变量`window.simpleMindMap`
会创建一个全局变量`window.simpleMindMap`,可以通过`window.simpleMindMap.default`获取到`MindMap`构造函数,详细信息可以把`window.simpleMindMap`打印出来看一下
这种方式的缺点是会包含所有的内容,包括你没有注册的插件,所以整体体积会比较大。

View File

@@ -51,7 +51,7 @@
<pre class="hljs"><code><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;stylesheet&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;simpleMindMap.css&quot;</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">scr</span>=<span class="hljs-string">&quot;simpleMindMap.umd.min.js&quot;</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>会创建一个全局变量<code>window.simpleMindMap</code></p>
<p>会创建一个全局变量<code>window.simpleMindMap</code>可以通过<code>window.simpleMindMap.default</code>获取到<code>MindMap</code>构造函数详细信息可以把<code>window.simpleMindMap</code>打印出来看一下</p>
<p>这种方式的缺点是会包含所有的内容包括你没有注册的插件所以整体体积会比较大</p>
<p>v0.5.4+如果你想直接在浏览器端通过<code>ES</code>模块的方式来使用你可以在<code>/simple-mind-map/dist/</code>目录中找到<code>simpleMindMap.esm.js</code><code>simpleMindMap.esm.css</code>文件</p>
<h2>开发</h2>

View File

@@ -0,0 +1,36 @@
<template>
<div class="customNodeContent">
<p>{{ title }}</p>
<p v-html="html"></p>
<p :style="{ backgroundColor: color }" @click="onClick">点击我会变色</p>
</div>
</template>
<script>
export default {
props: {
html: {
type: String,
default: ''
}
},
data() {
return {
title: '我是自定义节点',
color: ''
}
},
methods: {
onClick() {
this.color = 'red'
}
}
}
</script>
<style lang="less" scoped>
.customNodeContent {
padding: 10px;
cursor: pointer;
}
</style>

View File

@@ -48,8 +48,14 @@ import Navigator from './Navigator.vue'
import NodeImgPreview from './NodeImgPreview.vue'
import SidebarTrigger from './SidebarTrigger.vue'
import { mapState } from 'vuex'
import customThemeList from '@/customThemes'
import icon from '@/config/icon'
import customThemeList from '@/customThemes'
import CustomNodeContent from './CustomNodeContent.vue'
import Color from './Color.vue'
import Vue from 'vue'
import router from '../../../router'
import store from '../../../store'
import i18n from '../../../i18n'
// 注册插件
MindMap
@@ -293,7 +299,32 @@ export default {
},
...(config || {}),
iconList: icon,
useLeftKeySelectionRightKeyDrag: this.useLeftKeySelectionRightKeyDrag
useLeftKeySelectionRightKeyDrag: this.useLeftKeySelectionRightKeyDrag,
// isUseCustomNodeContent: true,
// 示例1组件里用到了router、store、i18n等实例化vue组件时需要用到的东西
// customCreateNodeContent: (node) => {
// let el = document.createElement('div')
// let Comp = Vue.extend(Color)
// let comp = new Comp({
// router,
// store,
// i18n
// })
// comp.$mount(el)
// return comp.$el
// },
// 示例2组件里没有用到示例1的东西
// customCreateNodeContent: (node) => {
// let el = document.createElement('div')
// let Comp = Vue.extend(CustomNodeContent)
// let comp = new Comp({
// propsData: {
// html: node.nodeData.data.text
// }
// })
// comp.$mount(el)
// return comp.$el
// }
})
if (this.openNodeRichText) this.addRichTextPlugin()
this.mindMap.keyCommand.addShortcut('Control+s', () => {