From 541606b2eb891a031231c8ddc0cb926c0878910d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A1=97=E8=A7=92=E5=B0=8F=E6=9E=97?= <1013335014@qq.com> Date: Thu, 4 Jan 2024 09:39:44 +0800 Subject: [PATCH] =?UTF-8?q?Feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=96=B0=E6=98=8E=E7=BB=86?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/command/Command.js | 74 +++++++++-- .../src/core/command/KeyCommand.js | 1 + simple-mind-map/src/plugins/Cooperate.js | 120 ++---------------- simple-mind-map/src/utils/index.js | 105 +++++++++++++++ 4 files changed, 182 insertions(+), 118 deletions(-) diff --git a/simple-mind-map/src/core/command/Command.js b/simple-mind-map/src/core/command/Command.js index d1638dee..0fa38cba 100644 --- a/simple-mind-map/src/core/command/Command.js +++ b/simple-mind-map/src/core/command/Command.js @@ -1,4 +1,10 @@ -import { copyRenderTree, simpleDeepClone, throttle } from '../../utils' +import { + copyRenderTree, + simpleDeepClone, + throttle, + isSameObject, + transformTreeDataToObject +} from '../../utils' // 命令类 class Command { @@ -84,15 +90,14 @@ class Command { if (this.mindMap.opt.readonly) { return } - let data = this.getCopyData() + const lastData = + this.history.length > 0 ? this.history[this.history.length - 1] : null + const data = this.getCopyData() // 此次数据和上次一样则不重复添加 - if ( - this.history.length > 0 && - JSON.stringify(this.history[this.history.length - 1]) === - JSON.stringify(data) - ) { + if (lastData && JSON.stringify(lastData) === JSON.stringify(data)) { return } + this.emitDataUpdatesEvent(lastData, data) // 删除当前历史指针后面的数据 this.history = this.history.slice(0, this.activeHistoryIndex + 1) this.history.push(simpleDeepClone(data)) @@ -115,13 +120,15 @@ class Command { return } if (this.activeHistoryIndex - step >= 0) { + const lastData = this.history[this.activeHistoryIndex] this.activeHistoryIndex -= step this.mindMap.emit( 'back_forward', this.activeHistoryIndex, this.history.length ) - let data = simpleDeepClone(this.history[this.activeHistoryIndex]) + const data = simpleDeepClone(this.history[this.activeHistoryIndex]) + this.emitDataUpdatesEvent(lastData, data) this.mindMap.emit('data_change', data) return data } @@ -134,13 +141,15 @@ class Command { } let len = this.history.length if (this.activeHistoryIndex + step <= len - 1) { + const lastData = this.history[this.activeHistoryIndex] this.activeHistoryIndex += step this.mindMap.emit( 'back_forward', this.activeHistoryIndex, this.history.length ) - let data = simpleDeepClone(this.history[this.activeHistoryIndex]) + const data = simpleDeepClone(this.history[this.activeHistoryIndex]) + this.emitDataUpdatesEvent(lastData, data) this.mindMap.emit('data_change', data) return data } @@ -165,6 +174,53 @@ class Command { walk(data) return data } + + // 派发思维导图更新明细事件 + emitDataUpdatesEvent(lastData, data) { + // 如果data_change_detail没有监听者,那么不进行计算,节省性能 + const eventName = 'data_change_detail' + const count = this.mindMap.event.listenerCount(eventName) + if (count > 0 && lastData && data) { + const lastDataObj = simpleDeepClone(transformTreeDataToObject(lastData)) + const dataObj = simpleDeepClone(transformTreeDataToObject(data)) + const res = [] + const walkReplace = (root, obj) => { + if (root.children && root.children.length > 0) { + root.children.forEach((childUid, index) => { + root.children[index] = obj[childUid] + walkReplace(root.children[index], obj) + }) + } + return root + } + // 找出新增的或修改的 + Object.keys(dataObj).forEach(uid => { + // 新增的或已经存在的,如果数据发生了改变 + if (!lastDataObj[uid]) { + res.push({ + action: 'create', + data: walkReplace(dataObj[uid], dataObj) + }) + } else if (!isSameObject(lastDataObj[uid], dataObj[uid])) { + res.push({ + action: 'update', + oldData: walkReplace(lastDataObj[uid], lastDataObj), + data: walkReplace(dataObj[uid], dataObj) + }) + } + }) + // 找出删除的 + Object.keys(lastDataObj).forEach(uid => { + if (!dataObj[uid]) { + res.push({ + action: 'delete', + data: walkReplace(lastDataObj[uid], lastDataObj) + }) + } + }) + this.mindMap.emit(eventName, res) + } + } } export default Command diff --git a/simple-mind-map/src/core/command/KeyCommand.js b/simple-mind-map/src/core/command/KeyCommand.js index 32ec181a..8474e84b 100644 --- a/simple-mind-map/src/core/command/KeyCommand.js +++ b/simple-mind-map/src/core/command/KeyCommand.js @@ -1,4 +1,5 @@ import { keyMap } from './keyMap' + // 快捷按键、命令处理类 export default class KeyCommand { // 构造函数 diff --git a/simple-mind-map/src/plugins/Cooperate.js b/simple-mind-map/src/plugins/Cooperate.js index ea3eb2f5..09258deb 100644 --- a/simple-mind-map/src/plugins/Cooperate.js +++ b/simple-mind-map/src/plugins/Cooperate.js @@ -1,6 +1,13 @@ import * as Y from 'yjs' import { WebrtcProvider } from 'y-webrtc' -import { isSameObject, simpleDeepClone, getType, isUndef } from '../utils/index' +import { + isSameObject, + simpleDeepClone, + getType, + isUndef, + transformTreeDataToObject, + transformObjectToTreeData +} from '../utils/index' // 协同插件 class Cooperate { @@ -39,7 +46,7 @@ class Cooperate { // 创建共享数据 this.ymap = this.ydoc.getMap() // 思维导图树结构转平级对象结构 - this.currentData = this.transformTreeDataToObject(data) + this.currentData = transformTreeDataToObject(data) // 将思维导图数据添加到共享数据中 Object.keys(this.currentData).forEach(uid => { this.ymap.set(uid, this.currentData[uid]) @@ -108,7 +115,7 @@ class Cooperate { if (isSameObject(data, this.currentData)) return this.currentData = data // 平级对象转树结构 - const res = this.transformObjectToTreeData(data) + const res = transformObjectToTreeData(data) if (!res) return // 更新思维导图画布 this.mindMap.renderer.setData(res) @@ -118,7 +125,7 @@ class Cooperate { // 当前思维导图改变后的处理,触发同步 onDataChange(data) { - const res = this.transformTreeDataToObject(data) + const res = transformTreeDataToObject(data) this.updateChanges(res) } @@ -224,111 +231,6 @@ class Cooperate { }) } - // 将树结构转平级对象 - /* - { - data: { - uid: 'xxx' - }, - children: [ - { - data: { - uid: 'xxx' - }, - children: [] - } - ] - } - 转为: - { - uid: { - children: [uid1, uid2], - data: {} - } - } - */ - transformTreeDataToObject(data) { - const res = {} - const walk = (root, parent) => { - const uid = root.data.uid - if (parent) { - parent.children.push(uid) - } - res[uid] = { - isRoot: !parent, - data: { - ...root.data - }, - children: [] - } - if (root.children && root.children.length > 0) { - root.children.forEach(item => { - walk(item, res[uid]) - }) - } - } - walk(data, null) - return res - } - - // 找到父节点的uid - findParentUid(data, targetUid) { - const uids = Object.keys(data) - let res = '' - uids.forEach(uid => { - const children = data[uid].children - const isParent = - children.findIndex(childUid => { - return childUid === targetUid - }) !== -1 - if (isParent) { - res = uid - } - }) - return res - } - - // 将平级对象转树结构 - transformObjectToTreeData(data) { - const uids = Object.keys(data) - if (uids.length <= 0) return null - const rootKey = uids.find(uid => { - return data[uid].isRoot - }) - if (!rootKey || !data[rootKey]) return null - // 根节点 - const res = { - data: simpleDeepClone(data[rootKey].data), - children: [] - } - const map = {} - map[rootKey] = res - uids.forEach(uid => { - const parentUid = this.findParentUid(data, uid) - const cur = data[uid] - const node = map[uid] || { - data: simpleDeepClone(cur.data), - children: [] - } - if (!map[uid]) { - map[uid] = node - } - if (parentUid) { - const index = data[parentUid].children.findIndex(item => { - return item === uid - }) - if (!map[parentUid]) { - map[parentUid] = { - data: simpleDeepClone(data[parentUid].data), - children: [] - } - } - map[parentUid].children[index] = node - } - }) - return res - } - // 插件被移除前做的事情 beforePluginRemove() { this.unBindEvent() diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index 6fdf0230..1ff2b672 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -1074,3 +1074,108 @@ export const handleInputPasteText = (e, text) => { selection.getRangeAt(0).insertNode(node) selection.collapseToEnd() } + +// 将思维导图树结构转平级对象 +/* + { + data: { + uid: 'xxx' + }, + children: [ + { + data: { + uid: 'xxx' + }, + children: [] + } + ] + } + 转为: + { + uid: { + children: [uid1, uid2], + data: {} + } + } + */ +export const transformTreeDataToObject = data => { + const res = {} + const walk = (root, parent) => { + const uid = root.data.uid + if (parent) { + parent.children.push(uid) + } + res[uid] = { + isRoot: !parent, + data: { + ...root.data + }, + children: [] + } + if (root.children && root.children.length > 0) { + root.children.forEach(item => { + walk(item, res[uid]) + }) + } + } + walk(data, null) + return res +} + +// 将平级对象转树结构 +// transformTreeDataToObject方法的反向操作 +// 找到父节点的uid +const _findParentUid = (data, targetUid) => { + const uids = Object.keys(data) + let res = '' + uids.forEach(uid => { + const children = data[uid].children + const isParent = + children.findIndex(childUid => { + return childUid === targetUid + }) !== -1 + if (isParent) { + res = uid + } + }) + return res +} +export const transformObjectToTreeData = data => { + const uids = Object.keys(data) + if (uids.length <= 0) return null + const rootKey = uids.find(uid => { + return data[uid].isRoot + }) + if (!rootKey || !data[rootKey]) return null + // 根节点 + const res = { + data: simpleDeepClone(data[rootKey].data), + children: [] + } + const map = {} + map[rootKey] = res + uids.forEach(uid => { + const parentUid = _findParentUid(data, uid) + const cur = data[uid] + const node = map[uid] || { + data: simpleDeepClone(cur.data), + children: [] + } + if (!map[uid]) { + map[uid] = node + } + if (parentUid) { + const index = data[parentUid].children.findIndex(item => { + return item === uid + }) + if (!map[parentUid]) { + map[parentUid] = { + data: simpleDeepClone(data[parentUid].data), + children: [] + } + } + map[parentUid].children[index] = node + } + }) + return res +}