\ No newline at end of file
diff --git a/index.html b/index.html
index 39ec0a9f..553768bd 100644
--- a/index.html
+++ b/index.html
@@ -1 +1 @@
-思绪思维导图
\ No newline at end of file
+思绪思维导图
\ No newline at end of file
diff --git a/simple-mind-map/full.js b/simple-mind-map/full.js
index c119beb8..2c78b8be 100644
--- a/simple-mind-map/full.js
+++ b/simple-mind-map/full.js
@@ -10,6 +10,7 @@ import AssociativeLine from './src/plugins/AssociativeLine'
import RichText from './src/plugins/RichText'
import NodeImgAdjust from './src/plugins/NodeImgAdjust.js'
import TouchEvent from './src/plugins/TouchEvent.js'
+import Search from './src/plugins/Search.js'
import xmind from './src/parse/xmind.js'
import markdown from './src/parse/markdown.js'
import icons from './src/svg/icons.js'
@@ -36,5 +37,6 @@ MindMap
.usePlugin(RichText)
.usePlugin(TouchEvent)
.usePlugin(NodeImgAdjust)
+ .usePlugin(Search)
export default MindMap
\ No newline at end of file
diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js
index 731edd37..babb799c 100644
--- a/simple-mind-map/index.js
+++ b/simple-mind-map/index.js
@@ -7,9 +7,9 @@ import Style from './src/core/render/node/Style'
import KeyCommand from './src/core/command/KeyCommand'
import Command from './src/core/command/Command'
import BatchExecution from './src/utils/BatchExecution'
-import { layoutValueList, CONSTANTS } from './src/constants/constant'
+import { layoutValueList, CONSTANTS, commonCaches } from './src/constants/constant'
import { SVG } from '@svgdotjs/svg.js'
-import { simpleDeepClone } from './src/utils'
+import { simpleDeepClone, getType } from './src/utils'
import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default'
import { defaultOpt } from './src/constants/defaultOptions'
@@ -35,6 +35,9 @@ class MindMap {
// 初始化主题
this.initTheme()
+ // 初始化缓存数据
+ this.initCache()
+
// 事件类
this.event = new Event({
mindMap: this
@@ -129,6 +132,23 @@ class MindMap {
this.event.off(event, fn)
}
+ // 初始化缓存数据
+ initCache() {
+ Object.keys(commonCaches).forEach((key) => {
+ let type = getType(commonCaches[key])
+ let value = ''
+ switch(type) {
+ case 'Boolean':
+ value = false
+ break
+ default:
+ value = null
+ break
+ }
+ commonCaches[key] = value
+ })
+ }
+
// 设置主题
initTheme() {
// 合并主题配置
diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json
index 007c8ee4..0ef87aee 100644
--- a/simple-mind-map/package.json
+++ b/simple-mind-map/package.json
@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
- "version": "0.6.8",
+ "version": "0.6.9",
"description": "一个简单的web在线思维导图",
"authors": [
{
diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js
index cf2ce546..1593bf81 100644
--- a/simple-mind-map/src/constants/constant.js
+++ b/simple-mind-map/src/constants/constant.js
@@ -324,4 +324,9 @@ export const nodeDataNoStylePropList = [
'resetRichText',
'uid',
'activeStyle'
-]
\ No newline at end of file
+]
+
+// 数据缓存
+export const commonCaches = {
+ measureCustomNodeContentSizeEl: null
+}
\ No newline at end of file
diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js
index 1e2664ea..217751de 100644
--- a/simple-mind-map/src/core/render/Render.js
+++ b/simple-mind-map/src/core/render/Render.js
@@ -444,6 +444,7 @@ class Render {
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
return
}
+ this.textEdit.hideEditTextBox()
let {
defaultInsertSecondLevelNodeText,
defaultInsertBelowSecondLevelNodeText
@@ -486,6 +487,7 @@ class Render {
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
return
}
+ this.textEdit.hideEditTextBox()
let {
defaultInsertSecondLevelNodeText,
defaultInsertBelowSecondLevelNodeText
@@ -973,7 +975,8 @@ class Render {
setNodeText(node, text, richText) {
this.setNodeDataRender(node, {
text,
- richText
+ richText,
+ resetRichText: richText
})
}
@@ -1099,7 +1102,7 @@ class Render {
}
// 定位到指定节点
- goTargetNode(node) {
+ goTargetNode(node, callback = () => {}) {
let uid = typeof node === 'string' ? node : node.nodeData.data.uid
if (!uid) return
this.expandToNodeUid(uid, () => {
@@ -1107,6 +1110,7 @@ class Render {
if (targetNode) {
targetNode.active()
this.moveNodeToCenter(targetNode)
+ callback()
}
})
}
@@ -1119,7 +1123,7 @@ class Render {
}
// 设置节点数据,并判断是否渲染
- setNodeDataRender(node, data) {
+ setNodeDataRender(node, data, notRender = false) {
this.setNodeData(node, data)
let changed = node.reRender()
if (changed) {
@@ -1127,7 +1131,7 @@ class Render {
// 概要节点
node.generalizationBelongNode.updateGeneralization()
}
- this.mindMap.render()
+ if (!notRender) this.mindMap.render()
}
}
diff --git a/simple-mind-map/src/core/render/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js
index 523cf4ba..e426ecf6 100644
--- a/simple-mind-map/src/core/render/TextEdit.js
+++ b/simple-mind-map/src/core/render/TextEdit.js
@@ -106,6 +106,9 @@ export default class TextEdit {
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.hideEditTextBox()
})
+ this.mindMap.keyCommand.addShortcut('Tab', () => {
+ this.hideEditTextBox()
+ })
}
// 显示文本编辑框
diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js
index 9822484a..c521d2d4 100644
--- a/simple-mind-map/src/core/render/node/nodeCreateContents.js
+++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js
@@ -1,7 +1,7 @@
import { measureText, resizeImgSize, getTextFromHtml } from '../../../utils'
import { Image, SVG, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js'
import iconsSvg from '../../../svg/icons'
-import { CONSTANTS } from '../../../constants/constant'
+import { CONSTANTS, commonCaches } from '../../../constants/constant'
// 创建图片节点
function createImgNode() {
@@ -293,20 +293,19 @@ function createNoteNode() {
}
// 测量自定义节点内容元素的宽高
-let warpEl = null
function measureCustomNodeContentSize (content) {
- if (!warpEl) {
- warpEl = document.createElement('div')
- warpEl.style.cssText = `
+ if (!commonCaches.measureCustomNodeContentSizeEl) {
+ commonCaches.measureCustomNodeContentSizeEl = document.createElement('div')
+ commonCaches.measureCustomNodeContentSizeEl.style.cssText = `
position: fixed;
left: -99999px;
top: -99999px;
`
- this.mindMap.el.appendChild(warpEl)
+ this.mindMap.el.appendChild(commonCaches.measureCustomNodeContentSizeEl)
}
- warpEl.innerHTML = ''
- warpEl.appendChild(content)
- let rect = warpEl.getBoundingClientRect()
+ commonCaches.measureCustomNodeContentSizeEl.innerHTML = ''
+ commonCaches.measureCustomNodeContentSizeEl.appendChild(content)
+ let rect = commonCaches.measureCustomNodeContentSizeEl.getBoundingClientRect()
return {
width: rect.width,
height: rect.height
diff --git a/simple-mind-map/src/core/render/node/nodeGeneralization.js b/simple-mind-map/src/core/render/node/nodeGeneralization.js
index ba204186..ec7f95aa 100644
--- a/simple-mind-map/src/core/render/node/nodeGeneralization.js
+++ b/simple-mind-map/src/core/render/node/nodeGeneralization.js
@@ -36,15 +36,14 @@ function createGeneralizationNode () {
// 更新概要节点
function updateGeneralization () {
+ if (this.isGeneralization) return
this.removeGeneralization()
this.createGeneralizationNode()
}
// 渲染概要节点
function renderGeneralization () {
- if (this.isGeneralization) {
- return
- }
+ if (this.isGeneralization) return
if (!this.checkHasGeneralization()) {
this.removeGeneralization()
this._generalizationNodeWidth = 0
@@ -67,6 +66,7 @@ function renderGeneralization () {
// 删除概要节点
function removeGeneralization () {
+ if (this.isGeneralization) return
if (this._generalizationLine) {
this._generalizationLine.remove()
this._generalizationLine = null
@@ -87,6 +87,7 @@ function removeGeneralization () {
// 隐藏概要节点
function hideGeneralization () {
+ if (this.isGeneralization) return
if (this._generalizationLine) {
this._generalizationLine.hide()
}
@@ -97,6 +98,7 @@ function hideGeneralization () {
// 显示概要节点
function showGeneralization () {
+ if (this.isGeneralization) return
if (this._generalizationLine) {
this._generalizationLine.show()
}
diff --git a/simple-mind-map/src/plugins/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js
index 28223265..2a7ebba1 100644
--- a/simple-mind-map/src/plugins/AssociativeLine.js
+++ b/simple-mind-map/src/plugins/AssociativeLine.js
@@ -11,7 +11,7 @@ import {
import associativeLineControlsMethods from './associativeLine/associativeLineControls'
import associativeLineTextMethods from './associativeLine/associativeLineText'
-// 关联线类
+// 关联线插件
class AssociativeLine {
constructor(opt = {}) {
this.mindMap = opt.mindMap
diff --git a/simple-mind-map/src/plugins/Drag.js b/simple-mind-map/src/plugins/Drag.js
index d85ea667..78a4b8aa 100644
--- a/simple-mind-map/src/plugins/Drag.js
+++ b/simple-mind-map/src/plugins/Drag.js
@@ -1,8 +1,7 @@
import { bfsWalk, throttle } from '../utils'
import Base from '../layouts/Base'
-// 节点拖动类
-
+// 节点拖动插件
class Drag extends Base {
// 构造函数
constructor({ mindMap }) {
diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js
index 1f719d8f..a5747595 100644
--- a/simple-mind-map/src/plugins/Export.js
+++ b/simple-mind-map/src/plugins/Export.js
@@ -1,9 +1,9 @@
-import { imgToDataUrl, downloadFile, readBlob } from '../utils'
+import { imgToDataUrl, downloadFile, readBlob, removeHTMLEntities } from '../utils'
import { SVG } from '@svgdotjs/svg.js'
import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas'
import { transformToMarkdown } from '../parse/toMarkdown'
-// 导出类
+// 导出插件
class Export {
// 构造函数
constructor(opt) {
@@ -154,6 +154,7 @@ class Export {
*/
async png(name, transparent = false) {
let { node, str } = await this.getSvgData()
+ str = removeHTMLEntities(str)
// 如果开启了富文本,则使用htmltocanvas转换为图片
if (this.mindMap.richText) {
let res = await this.mindMap.richText.handleExportPng(node.node)
@@ -207,6 +208,7 @@ class Export {
node.first().before(SVG(`${name}`))
await this.drawBackgroundToSvg(node)
let str = node.svg()
+ str = removeHTMLEntities(str)
// 转换成blob数据
let blob = new Blob([str], {
type: 'image/svg+xml'
diff --git a/simple-mind-map/src/plugins/ExportPDF.js b/simple-mind-map/src/plugins/ExportPDF.js
index 2fcc254d..e46b3a3c 100644
--- a/simple-mind-map/src/plugins/ExportPDF.js
+++ b/simple-mind-map/src/plugins/ExportPDF.js
@@ -1,6 +1,6 @@
import JsPDF from 'jspdf'
-// 导出PDF类,需要通过Export插件使用
+// 导出PDF插件,需要通过Export插件使用
class ExportPDF {
// 构造函数
constructor(opt) {
diff --git a/simple-mind-map/src/plugins/ExportXMind.js b/simple-mind-map/src/plugins/ExportXMind.js
index 5f54e220..42ea665c 100644
--- a/simple-mind-map/src/plugins/ExportXMind.js
+++ b/simple-mind-map/src/plugins/ExportXMind.js
@@ -1,6 +1,6 @@
import xmind from '../parse/xmind'
-// 导出XMind类,需要通过Export插件使用
+// 导出XMind插件,需要通过Export插件使用
class ExportXMind {
// 构造函数
constructor(opt) {
diff --git a/simple-mind-map/src/plugins/KeyboardNavigation.js b/simple-mind-map/src/plugins/KeyboardNavigation.js
index 0a3f6098..5ed4b593 100644
--- a/simple-mind-map/src/plugins/KeyboardNavigation.js
+++ b/simple-mind-map/src/plugins/KeyboardNavigation.js
@@ -1,7 +1,7 @@
import { bfsWalk } from '../utils'
import { CONSTANTS } from '../constants/constant'
-// 键盘导航类
+// 键盘导航插件
class KeyboardNavigation {
// 构造函数
constructor(opt) {
diff --git a/simple-mind-map/src/plugins/MiniMap.js b/simple-mind-map/src/plugins/MiniMap.js
index 9bc1d6ba..78198685 100644
--- a/simple-mind-map/src/plugins/MiniMap.js
+++ b/simple-mind-map/src/plugins/MiniMap.js
@@ -1,4 +1,4 @@
-// 小地图类
+// 小地图插件
class MiniMap {
// 构造函数
constructor(opt) {
diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js
index d501c282..2652428f 100644
--- a/simple-mind-map/src/plugins/RichText.js
+++ b/simple-mind-map/src/plugins/RichText.js
@@ -28,7 +28,7 @@ let fontSizeList = new Array(100).fill(0).map((_, index) => {
return index + 'px'
})
-// 节点支持富文本编辑功能
+// 富文本编辑插件
class RichText {
constructor({ mindMap, pluginOpt }) {
this.mindMap = mindMap
@@ -268,6 +268,12 @@ class RichText {
handler: function () {
// 覆盖默认的回车键换行
}
+ },
+ tab: {
+ key: 9,
+ handler: function () {
+ // 覆盖默认的tab键
+ }
}
}
}
diff --git a/simple-mind-map/src/plugins/Search.js b/simple-mind-map/src/plugins/Search.js
new file mode 100644
index 00000000..5bbd3e77
--- /dev/null
+++ b/simple-mind-map/src/plugins/Search.js
@@ -0,0 +1,143 @@
+import { bfsWalk, getTextFromHtml } from '../utils/index'
+
+// 搜索插件
+class Search {
+ // 构造函数
+ constructor({ mindMap }) {
+ this.mindMap = mindMap
+ // 是否正在搜索
+ this.isSearching = false
+ // 搜索文本
+ this.searchText = ''
+ // 匹配的节点列表
+ this.matchNodeList = []
+ // 当前所在的节点列表索引
+ this.currentIndex = -1
+ // 是否正在跳转中
+ this.isJumping = false
+ this.onDataChange = this.onDataChange.bind(this)
+ this.mindMap.on('data_change', this.onDataChange)
+ }
+
+ // 节点数据改变了,需要重新搜索
+ onDataChange() {
+ if (this.isJumping) return
+ this.searchText = ''
+ }
+
+ // 搜索
+ search(text, callback) {
+ text = String(text).trim()
+ if (!text) return this.endSearch()
+ this.isSearching = true
+ if (this.searchText === text) {
+ // 和上一次搜索文本一样,那么搜索下一个
+ this.searchNext(callback)
+ } else {
+ // 和上次搜索文本不一样,那么重新开始
+ this.searchText = text
+ this.doSearch()
+ this.searchNext(callback)
+ }
+ this.emitEvent()
+ }
+
+ // 结束搜索
+ endSearch() {
+ if (!this.isSearching) return
+ this.searchText = ''
+ this.matchNodeList = []
+ this.currentIndex = -1
+ this.isJumping = false
+ this.isSearching = false
+ this.emitEvent()
+ }
+
+ // 搜索匹配的节点
+ doSearch() {
+ this.matchNodeList = []
+ this.currentIndex = -1
+ bfsWalk(this.mindMap.renderer.root, node => {
+ let { richText, text } = node.nodeData.data
+ if (richText) {
+ text = getTextFromHtml(text)
+ }
+ if (text.includes(this.searchText)) {
+ this.matchNodeList.push(node)
+ }
+ })
+ }
+
+ // 搜索下一个,定位到下一个匹配节点
+ searchNext(callback) {
+ if (!this.isSearching || this.matchNodeList.length <= 0) return
+ if (this.currentIndex < this.matchNodeList.length - 1) {
+ this.currentIndex++
+ } else {
+ this.currentIndex = 0
+ }
+ let currentNode = this.matchNodeList[this.currentIndex]
+ this.isJumping = true
+ this.mindMap.execCommand('GO_TARGET_NODE', currentNode, () => {
+ this.isJumping = false
+ callback()
+ })
+ }
+
+ // 替换当前节点
+ replace(replaceText) {
+ replaceText = String(replaceText).trim()
+ if (!replaceText || !this.isSearching || this.matchNodeList.length <= 0)
+ return
+ let currentNode = this.matchNodeList[this.currentIndex]
+ if (!currentNode) return
+ let text = this.getReplacedText(currentNode, this.searchText, replaceText)
+ currentNode.setText(text, currentNode.nodeData.data.richText)
+ this.matchNodeList = this.matchNodeList.filter(node => {
+ return currentNode !== node
+ })
+ this.emitEvent()
+ }
+
+ // 替换所有
+ replaceAll(replaceText) {
+ replaceText = String(replaceText).trim()
+ if (!replaceText || !this.isSearching || this.matchNodeList.length <= 0)
+ return
+ this.matchNodeList.forEach(node => {
+ let text = this.getReplacedText(node, this.searchText, replaceText)
+ this.mindMap.renderer.setNodeDataRender(
+ node,
+ {
+ text,
+ resetRichText: !!node.nodeData.data.richText
+ },
+ true
+ )
+ })
+ this.mindMap.render()
+ this.mindMap.command.addHistory()
+ this.endSearch()
+ }
+
+ // 获取某个节点替换后的文本
+ getReplacedText(node, searchText, replaceText) {
+ let { richText, text } = node.nodeData.data
+ if (richText) {
+ text = getTextFromHtml(text)
+ }
+ return text.replaceAll(searchText, replaceText)
+ }
+
+ // 发送事件
+ emitEvent() {
+ this.mindMap.emit('search_info_change', {
+ currentIndex: this.currentIndex,
+ total: this.matchNodeList.length
+ })
+ }
+}
+
+Search.instanceName = 'search'
+
+export default Search
diff --git a/simple-mind-map/src/plugins/Select.js b/simple-mind-map/src/plugins/Select.js
index b2a58c14..bba83b0c 100644
--- a/simple-mind-map/src/plugins/Select.js
+++ b/simple-mind-map/src/plugins/Select.js
@@ -1,7 +1,6 @@
import { bfsWalk, throttle } from '../utils'
-// 选择节点类
-
+// 节点选择插件
class Select {
// 构造函数
constructor({ mindMap }) {
diff --git a/simple-mind-map/src/plugins/TouchEvent.js b/simple-mind-map/src/plugins/TouchEvent.js
index 5afc1f04..2d4f78d1 100644
--- a/simple-mind-map/src/plugins/TouchEvent.js
+++ b/simple-mind-map/src/plugins/TouchEvent.js
@@ -1,5 +1,4 @@
-// 手势事件支持类
-
+// 手势事件支持插件
class TouchEvent {
// 构造函数
constructor({ mindMap }) {
diff --git a/simple-mind-map/src/plugins/Watermark.js b/simple-mind-map/src/plugins/Watermark.js
index 7d2ddaca..cd6a3c98 100644
--- a/simple-mind-map/src/plugins/Watermark.js
+++ b/simple-mind-map/src/plugins/Watermark.js
@@ -2,7 +2,7 @@ import { Text, G } from '@svgdotjs/svg.js'
import { degToRad, camelCaseToHyphen } from '../utils'
import merge from 'deepmerge'
-// 水印类
+// 水印插件
class Watermark {
constructor(opt = {}) {
this.mindMap = opt.mindMap
diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js
index 99470427..886e5385 100644
--- a/simple-mind-map/src/utils/index.js
+++ b/simple-mind-map/src/utils/index.js
@@ -453,3 +453,16 @@ export const loadImage = imgFile => {
}
})
}
+
+// 移除字符串中的html实体
+export const removeHTMLEntities = (str) => {
+ [[' ', ' ']].forEach((item) => {
+ str = str.replaceAll(item[0], item[1])
+ })
+ return str
+}
+
+// 获取一个数据的类型
+export const getType = (data) => {
+ return Object.prototype.toString.call(data).slice(7, -1)
+}
\ No newline at end of file
diff --git a/web/src/assets/avatar/仓鼠.jpg b/web/src/assets/avatar/仓鼠.jpg
new file mode 100644
index 00000000..1eabba76
Binary files /dev/null and b/web/src/assets/avatar/仓鼠.jpg differ
diff --git a/web/src/assets/avatar/水车.jpg b/web/src/assets/avatar/水车.jpg
new file mode 100644
index 00000000..747178b7
Binary files /dev/null and b/web/src/assets/avatar/水车.jpg differ
diff --git a/web/src/assets/icon-font/iconfont.css b/web/src/assets/icon-font/iconfont.css
index d216721f..5721282a 100644
--- a/web/src/assets/icon-font/iconfont.css
+++ b/web/src/assets/icon-font/iconfont.css
@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
- src: url('iconfont.woff2?t=1689407546912') format('woff2'),
- url('iconfont.woff?t=1689407546912') format('woff'),
- url('iconfont.ttf?t=1689407546912') format('truetype');
+ src: url('iconfont.woff2?t=1690506335310') format('woff2'),
+ url('iconfont.woff?t=1690506335310') format('woff'),
+ url('iconfont.ttf?t=1690506335310') format('truetype');
}
.iconfont {
@@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
+.iconsousuo:before {
+ content: "\e693";
+}
+
.iconjiantouyou:before {
content: "\e62d";
}
diff --git a/web/src/assets/icon-font/iconfont.ttf b/web/src/assets/icon-font/iconfont.ttf
index 9c801c08..110b3d99 100644
Binary files a/web/src/assets/icon-font/iconfont.ttf and b/web/src/assets/icon-font/iconfont.ttf differ
diff --git a/web/src/assets/icon-font/iconfont.woff b/web/src/assets/icon-font/iconfont.woff
index 4d446856..45de5a0f 100644
Binary files a/web/src/assets/icon-font/iconfont.woff and b/web/src/assets/icon-font/iconfont.woff differ
diff --git a/web/src/assets/icon-font/iconfont.woff2 b/web/src/assets/icon-font/iconfont.woff2
index 1d170d95..0e65009e 100644
Binary files a/web/src/assets/icon-font/iconfont.woff2 and b/web/src/assets/icon-font/iconfont.woff2 differ
diff --git a/web/src/config/en.js b/web/src/config/en.js
index 0c0bfbde..97aa75d4 100644
--- a/web/src/config/en.js
+++ b/web/src/config/en.js
@@ -271,6 +271,11 @@ export const shortcutKeyList = [
icon: 'iconzhengli',
name: 'Arrange layout',
value: 'Ctrl + L'
+ },
+ {
+ icon: 'iconsousuo',
+ name: 'Search and Replace',
+ value: 'Ctrl + F'
}
]
},
diff --git a/web/src/config/zh.js b/web/src/config/zh.js
index 57c6138c..6f8d0965 100644
--- a/web/src/config/zh.js
+++ b/web/src/config/zh.js
@@ -331,6 +331,11 @@ export const shortcutKeyList = [
icon: 'iconzhengli',
name: '一键整理布局',
value: 'Ctrl + L'
+ },
+ {
+ icon: 'iconsousuo',
+ name: '搜索和替换',
+ value: 'Ctrl + F'
}
]
},
diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js
index 235ebd1d..a89f26ae 100644
--- a/web/src/lang/en_us.js
+++ b/web/src/lang/en_us.js
@@ -210,5 +210,12 @@ export default {
mouseAction: {
tip1: 'Current: Left click to drag the canvas, right click to box select nodes',
tip2: 'Current: Left click to box select nodes, right click to drag the canvas',
+ },
+ search: {
+ searchPlaceholder: 'Please enter the search content',
+ replacePlaceholder: 'Please enter replacement content',
+ replace: 'Replace',
+ replaceAll: 'Replace all',
+ cancel: 'Cancel'
}
}
diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js
index c050b0f4..910dedfa 100644
--- a/web/src/lang/zh_cn.js
+++ b/web/src/lang/zh_cn.js
@@ -210,5 +210,12 @@ export default {
mouseAction: {
tip1: '当前:左键拖动画布,右键框选节点',
tip2: '当前:左键框选节点,右键拖动画布',
+ },
+ search: {
+ searchPlaceholder: '请输入查找内容',
+ replacePlaceholder: '请输入替换内容',
+ replace: '替换',
+ replaceAll: '全部替换',
+ cancel: '取消'
}
}
diff --git a/web/src/pages/Doc/catalogList.js b/web/src/pages/Doc/catalogList.js
index c48114f6..7aca8d52 100644
--- a/web/src/pages/Doc/catalogList.js
+++ b/web/src/pages/Doc/catalogList.js
@@ -32,6 +32,7 @@ let APIList = [
'associativeLine',
'touchEvent',
'nodeImgAdjust',
+ 'search',
'xmind',
'markdown',
'utils'
diff --git a/web/src/pages/Doc/en/changelog/index.md b/web/src/pages/Doc/en/changelog/index.md
index 46429b41..f09ad06d 100644
--- a/web/src/pages/Doc/en/changelog/index.md
+++ b/web/src/pages/Doc/en/changelog/index.md
@@ -1,5 +1,13 @@
# Changelog
+## 0.6.9
+
+Fix: 1.Fixed an issue where setting styles to summary nodes would cause summary nodes to disappear. 2.Fixed the issue of node content not rendering when creating a root instance again when customizing node content. 3.Fix the issue of losing focus when adding a new node while the node is in editing. 2.Fix the issue of continuously pressing the tab key not being able to continuously create child nodes.
+
+New: 1.Replace existing ` ` in SVG when exporting Characters to avoid exporting SVG errors. 2.Support for search and replace.
+
+Demo: 1.When switching themes, it is supported to choose whether to overwrite the set basic style.
+
## 0.6.8
Fix: 1.Change the shortcut key for inserting a summary to Ctrl+G to avoid conflicts with the save shortcut key. 2.Fix the issue of abnormal switching between rich text editing configuration input boxes while nodes are being edited.
diff --git a/web/src/pages/Doc/en/changelog/index.vue b/web/src/pages/Doc/en/changelog/index.vue
index 25fb9efb..00fd1d31 100644
--- a/web/src/pages/Doc/en/changelog/index.vue
+++ b/web/src/pages/Doc/en/changelog/index.vue
@@ -1,6 +1,10 @@
Changelog
+
0.6.9
+
Fix: 1.Fixed an issue where setting styles to summary nodes would cause summary nodes to disappear. 2.Fixed the issue of node content not rendering when creating a root instance again when customizing node content. 3.Fix the issue of losing focus when adding a new node while the node is in editing. 2.Fix the issue of continuously pressing the tab key not being able to continuously create child nodes.
+
New: 1.Replace existing in SVG when exporting Characters to avoid exporting SVG errors. 2.Support for search and replace.
+
Demo: 1.When switching themes, it is supported to choose whether to overwrite the set basic style.
0.6.8
Fix: 1.Change the shortcut key for inserting a summary to Ctrl+G to avoid conflicts with the save shortcut key. 2.Fix the issue of abnormal switching between rich text editing configuration input boxes while nodes are being edited.
New: 1.Modify the copy, cut, and paste logic, and support pasting data from the clipboard.
diff --git a/web/src/pages/Doc/en/constructor/index.md b/web/src/pages/Doc/en/constructor/index.md
index d4ca44d4..4c59a8fb 100644
--- a/web/src/pages/Doc/en/constructor/index.md
+++ b/web/src/pages/Doc/en/constructor/index.md
@@ -347,7 +347,7 @@ redo. All commands are as follows:
| SET_NODE_CUSTOM_POSITION (v0.2.0+) | Set a custom position for a node | node (the node to set), left (custom x coordinate, default is undefined), top (custom y coordinate, default is undefined) |
| RESET_LAYOUT (v0.2.0+) | Arrange layout with one click | |
| SET_NODE_SHAPE (v0.2.4+) | Set the shape of a node | node (the node to set), shape (the shape, all shapes: [Shape.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/core/render/node/Shape.js)) |
-| GO_TARGET_NODE(v0.6.7+) | Navigate to a node, and if the node is collapsed, it will automatically expand to that node | node(Node instance or node uid to locate) |
+| GO_TARGET_NODE(v0.6.7+) | Navigate to a node, and if the node is collapsed, it will automatically expand to that node | node(Node instance or node uid to locate)、callback(v0.6.9+, Callback function after positioning completion) |
### setData(data)
diff --git a/web/src/pages/Doc/en/constructor/index.vue b/web/src/pages/Doc/en/constructor/index.vue
index 194a7e7d..d5d83af1 100644
--- a/web/src/pages/Doc/en/constructor/index.vue
+++ b/web/src/pages/Doc/en/constructor/index.vue
@@ -923,7 +923,7 @@ redo. All commands are as follows:
GO_TARGET_NODE(v0.6.7+)
Navigate to a node, and if the node is collapsed, it will automatically expand to that node
-
node(Node instance or node uid to locate)
+
node(Node instance or node uid to locate)、callback(v0.6.9+, Callback function after positioning completion)
diff --git a/web/src/pages/Doc/en/introduction/index.md b/web/src/pages/Doc/en/introduction/index.md
index c567ffd9..14ec31d5 100644
--- a/web/src/pages/Doc/en/introduction/index.md
+++ b/web/src/pages/Doc/en/introduction/index.md
@@ -160,4 +160,12 @@ Open source is not easy. If this project is helpful to you, you can invite the a
Chris
+
+
+
水车
+
+
+
+
仓鼠
+
\ No newline at end of file
diff --git a/web/src/pages/Doc/en/introduction/index.vue b/web/src/pages/Doc/en/introduction/index.vue
index 7f99b20d..fd53cb9e 100644
--- a/web/src/pages/Doc/en/introduction/index.vue
+++ b/web/src/pages/Doc/en/introduction/index.vue
@@ -119,6 +119,14 @@ full screen, support mini map
Chris
+
+
+
水车
+
+
+
+
仓鼠
+
diff --git a/web/src/pages/Doc/en/render/index.md b/web/src/pages/Doc/en/render/index.md
index 82f871cb..a76e1504 100644
--- a/web/src/pages/Doc/en/render/index.md
+++ b/web/src/pages/Doc/en/render/index.md
@@ -61,7 +61,9 @@ Delete a specific node
Copy a node, the active node is the node to be operated on, if there are
multiple active nodes, only the first node will be operated on
-### setNodeDataRender(node, data)
+### setNodeDataRender(node, data, notRender)
+
+- `notRender`: v0.6.9+, `Boolean`, Default is `false`, Do not trigger rendering.
Set node `data`, i.e. the data in the data field, and will determine whether the
node needs to be re-rendered based on whether the node size has changed, `data`
diff --git a/web/src/pages/Doc/en/render/index.vue b/web/src/pages/Doc/en/render/index.vue
index 34956eb1..2819d97d 100644
--- a/web/src/pages/Doc/en/render/index.vue
+++ b/web/src/pages/Doc/en/render/index.vue
@@ -37,7 +37,10 @@ disable the enter key and delete key related shortcuts to prevent conflicts
copyNode()
Copy a node, the active node is the node to be operated on, if there are
multiple active nodes, only the first node will be operated on
-
setNodeDataRender(node, data)
+
setNodeDataRender(node, data, notRender)
+
+
notRender: v0.6.9+, Boolean, Default is false, Do not trigger rendering.
+
Set node data, i.e. the data in the data field, and will determine whether the
node needs to be re-rendered based on whether the node size has changed, data
is an object, e.g. {text: 'I am new text'}
diff --git a/web/src/pages/Doc/en/search/index.md b/web/src/pages/Doc/en/search/index.md
new file mode 100644
index 00000000..9bb87174
--- /dev/null
+++ b/web/src/pages/Doc/en/search/index.md
@@ -0,0 +1,68 @@
+# Search plugin
+
+> v0.6.9+
+
+This plugin provides the ability to search and replace node content.
+
+## Register
+
+```js
+import MindMap from 'simple-mind-map'
+import Search from 'simple-mind-map/src/plugins/Search.js'
+MindMap.usePlugin(Search)
+```
+
+After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.Search`.
+
+## Event
+
+### search_info_change
+
+You can listen to 'search_info_change' event to get the number of current search results and the index currently located.
+
+```js
+mindMap.on('search_info_change', (data) => {
+ /*
+ data: {
+ currentIndex,// Index, from zero
+ total
+ }
+ */
+})
+```
+
+## Method
+
+### search(searchText, callback)
+
+- `searchText`: Text to search for
+
+- `callback`: The callback function that completes this search will be triggered after jumping to the node
+
+Search for node content, which can be called repeatedly. Each call will search and locate to the next matching node. If the search text changes, it will be searched again.
+
+### endSearch()
+
+End search.
+
+### replace(replaceText)
+
+- `replaceText`: Text to be replaced
+
+To replace the content of the current node, call the 'search' method after calling it to replace the content of the currently located matching node.
+
+### replaceAll(replaceText)
+
+- `replaceText`: Text to be replaced
+
+Replace all matching node contents, and call it after calling the 'search' method.
+
+### getReplacedText(node, searchText, replaceText)
+
+- `node`: Node instance
+
+- `searchText`: Text to search for
+
+- `replaceText`: Text to be replaced
+
+Return the text content of the node after search and replacement. Note that the node content will not be actually changed, but is only used to calculate the content of a node after replacement.
\ No newline at end of file
diff --git a/web/src/pages/Doc/en/search/index.vue b/web/src/pages/Doc/en/search/index.vue
new file mode 100644
index 00000000..600bcaf8
--- /dev/null
+++ b/web/src/pages/Doc/en/search/index.vue
@@ -0,0 +1,74 @@
+
+
+
Search plugin
+
+
v0.6.9+
+
+
This plugin provides the ability to search and replace node content.
After registration and instantiation of MindMap, the instance can be obtained through mindMap.Search.
+
Event
+
search_info_change
+
You can listen to 'search_info_change' event to get the number of current search results and the index currently located.
+
mindMap.on('search_info_change', (data) => {
+ /*
+ data: {
+ currentIndex,// Index, from zero
+ total
+ }
+ */
+})
+
+
Method
+
search(searchText, callback)
+
+
+
searchText: Text to search for
+
+
+
callback: The callback function that completes this search will be triggered after jumping to the node
+
+
+
Search for node content, which can be called repeatedly. Each call will search and locate to the next matching node. If the search text changes, it will be searched again.
+
endSearch()
+
End search.
+
replace(replaceText)
+
+
replaceText: Text to be replaced
+
+
To replace the content of the current node, call the 'search' method after calling it to replace the content of the currently located matching node.
+
replaceAll(replaceText)
+
+
replaceText: Text to be replaced
+
+
Replace all matching node contents, and call it after calling the 'search' method.
+
getReplacedText(node, searchText, replaceText)
+
+
+
node: Node instance
+
+
+
searchText: Text to search for
+
+
+
replaceText: Text to be replaced
+
+
+
Return the text content of the node after search and replacement. Note that the node content will not be actually changed, but is only used to calculate the content of a node after replacement.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/pages/Doc/en/utils/index.md b/web/src/pages/Doc/en/utils/index.md
index a2610595..80041030 100644
--- a/web/src/pages/Doc/en/utils/index.md
+++ b/web/src/pages/Doc/en/utils/index.md
@@ -196,6 +196,12 @@ Load image, return:
}
```
+#### getType(data)
+
+> v0.6.9+
+
+Get the type of a data, such as `Boolean`、`Array`.
+
## Simulate CSS background in Canvas
Import:
diff --git a/web/src/pages/Doc/en/utils/index.vue b/web/src/pages/Doc/en/utils/index.vue
index 8d53434b..d812e14f 100644
--- a/web/src/pages/Doc/en/utils/index.vue
+++ b/web/src/pages/Doc/en/utils/index.vue
@@ -134,6 +134,11 @@ and copying the data of the data object, example:
size// { width, height } width and height of image
}
+
\ No newline at end of file
diff --git a/web/src/pages/Doc/zh/introduction/index.vue b/web/src/pages/Doc/zh/introduction/index.vue
index d3cca190..c22a8644 100644
--- a/web/src/pages/Doc/zh/introduction/index.vue
+++ b/web/src/pages/Doc/zh/introduction/index.vue
@@ -111,6 +111,14 @@