From 07be48d3423d03cc64f76060b86bb19cbbf8d79b Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 28 Jul 2023 08:50:29 +0800 Subject: [PATCH] =?UTF-8?q?Feat=EF=BC=9A=E6=94=AF=E6=8C=81=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=92=8C=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/full.js | 2 + simple-mind-map/src/core/render/Render.js | 10 +- simple-mind-map/src/plugins/Search.js | 143 ++++++++++++++++ web/src/pages/Edit/components/Edit.vue | 7 +- web/src/pages/Edit/components/Search.vue | 188 ++++++++++++++++++++++ web/src/pages/Edit/components/Sidebar.vue | 11 +- 6 files changed, 353 insertions(+), 8 deletions(-) create mode 100644 simple-mind-map/src/plugins/Search.js create mode 100644 web/src/pages/Edit/components/Search.vue 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/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 9e06c260..217751de 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -975,7 +975,8 @@ class Render { setNodeText(node, text, richText) { this.setNodeDataRender(node, { text, - richText + richText, + resetRichText: richText }) } @@ -1101,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, () => { @@ -1109,6 +1110,7 @@ class Render { if (targetNode) { targetNode.active() this.moveNodeToCenter(targetNode) + callback() } }) } @@ -1121,7 +1123,7 @@ class Render { } // 设置节点数据,并判断是否渲染 - setNodeDataRender(node, data) { + setNodeDataRender(node, data, notRender = false) { this.setNodeData(node, data) let changed = node.reRender() if (changed) { @@ -1129,7 +1131,7 @@ class Render { // 概要节点 node.generalizationBelongNode.updateGeneralization() } - this.mindMap.render() + if (!notRender) this.mindMap.render() } } 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/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 94f3ea52..7dc8c17b 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -18,6 +18,7 @@ > + @@ -35,6 +36,7 @@ import RichText from 'simple-mind-map/src/plugins/RichText.js' import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js' import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js' import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js' +import SearchPlugin from 'simple-mind-map/src/plugins/Search.js' import Outline from './Outline' import Style from './Style' import BaseStyle from './BaseStyle' @@ -59,6 +61,7 @@ import Vue from 'vue' import router from '../../../router' import store from '../../../store' import i18n from '../../../i18n' +import Search from './Search.vue' // 注册插件 MindMap @@ -73,6 +76,7 @@ MindMap .usePlugin(AssociativeLine) .usePlugin(NodeImgAdjust) .usePlugin(TouchEvent) + .usePlugin(SearchPlugin) // 注册自定义主题 // customThemeList.forEach((item) => { @@ -100,7 +104,8 @@ export default { NodeNoteContentShow, Navigator, NodeImgPreview, - SidebarTrigger + SidebarTrigger, + Search }, data() { return { diff --git a/web/src/pages/Edit/components/Search.vue b/web/src/pages/Edit/components/Search.vue new file mode 100644 index 00000000..9fdda8a4 --- /dev/null +++ b/web/src/pages/Edit/components/Search.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/web/src/pages/Edit/components/Sidebar.vue b/web/src/pages/Edit/components/Sidebar.vue index 5f0cec24..d82b5153 100644 --- a/web/src/pages/Edit/components/Sidebar.vue +++ b/web/src/pages/Edit/components/Sidebar.vue @@ -39,7 +39,7 @@ export default { } }, computed: { - ...mapState(['isDark']), + ...mapState(['isDark']) }, watch: { show(val, oldVal) { @@ -48,6 +48,11 @@ export default { } } }, + created() { + this.$bus.$on('closeSideBar', () => { + this.close() + }) + }, methods: { ...mapMutations(['setActiveSidebar']), @@ -74,10 +79,10 @@ export default { &.isDark { background-color: #262a2e; - border-left-color: hsla(0,0%,100%,.1); + border-left-color: hsla(0, 0%, 100%, 0.1); .sidebarHeader { - border-bottom-color: hsla(0,0%,100%,.1); + border-bottom-color: hsla(0, 0%, 100%, 0.1); color: #fff; }