From 58fcffa7d4ed5caf117cbe9e6e4fe07b700d3ca0 Mon Sep 17 00:00:00 2001 From: yvan Date: Sun, 1 Jun 2025 23:25:03 +0800 Subject: [PATCH] =?UTF-8?q?stateManager=20=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B2=20=E6=97=A0=E6=B3=95=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/manager/StateManager.ts | 387 ++++++++++++++++++--------------------- src/example/example1.js | 7 +- 2 files changed, 184 insertions(+), 210 deletions(-) diff --git a/src/core/manager/StateManager.ts b/src/core/manager/StateManager.ts index a4da870..a2e7193 100644 --- a/src/core/manager/StateManager.ts +++ b/src/core/manager/StateManager.ts @@ -7,14 +7,13 @@ import type Viewport from '@/core/engine/Viewport.ts' // 差异类型定义 interface DataDiff { added: ItemJson[] - removed: string[] - updated: ItemJson[] + removed: ItemJson[] + updated: { before: ItemJson; after: ItemJson }[] } // 历史记录项 interface HistoryStep { diff: DataDiff - snapshot: Map timestamp: number } @@ -35,11 +34,24 @@ interface HistoryStep { * 主要难点: * - 单张地图数据量可能超过 10000 个对象, 需要高效的管理数据状态 * - * // 修改坐标点 + * // 用例1, 修改 * stateManager.beginStateUpdate();stateManager.vdata.items[1].tf[0] = [-10, 0, 4];stateManager.endStateUpdate(); + * stateManager.undo() + * stateManager.redo() + * + * // 用例2 添加 + * stateManager.beginStateUpdate(); + * stateManager.vdata.items[3].dt.center.push('p5'); + * stateManager.vdata.items.push({ id: 'p5', t: 'measure', tf: [[-6.0, 0, 8], [0, 0, 0], [0.25, 0.1, 0.25]], dt: { center: ['p4'] } }); + * stateManager.endStateUpdate(); * - * // 撤销操作 + * stateManager.undo(); + * stateManager.redo(); + * + * // 用例3 删除 + * stateManager.beginStateUpdate(); stateManager.vdata.items.splice(3, 1); stateManager.endStateUpdate(); * stateManager.undo() + * stateManager.redo() * */ export default class StateManager { @@ -76,10 +88,9 @@ export default class StateManager { private historySteps: HistoryStep[] = [] private historyIndex = -1 private readonly maxHistorySteps = 20 - private readonly historyBufferSize: number // 变化追踪器 - private changeTracker: DataDiff = { + private readonly changeTracker: DataDiff = { added: [], removed: [], updated: [] @@ -88,13 +99,12 @@ export default class StateManager { /** * 数据快照(用于差异计算) */ - private lastSnapshot: Map = new Map() + private lastStateDict = new Map() // 自动保存相关 private autoSaveInterval: number | null = null - private autoSaveIntervalMs = 5000 // 5秒自动保存 - private pendingChanges = false // 是否有待保存的更改 - private lastAutoSaveTime = 0 // 上次自动保存时间 + // 是否有待保存的更改 + private pendingChanges = false /** * @param id 唯一场景标识符, 用于做临时存储的 key @@ -106,22 +116,18 @@ export default class StateManager { this.storeKey = `-tmp-yvan-lcc-${this.id}` this.entityManager = viewport.entityManager - this.historyBufferSize = bufferSize - // 初始化固定大小的历史缓冲区 - this.historySteps = new Array(this.maxHistorySteps).fill(null) - - // 启动自动保存定时器 - this.startAutoSave() + // this.historySteps = Array.from({ length: this.maxHistorySteps }, () => null) + this.historySteps = [] } /** * 开始用户操作(创建数据快照) */ beginStateUpdate() { - this.lastSnapshot = new Map( - this.vdata.items.map(item => [item.id, _.cloneDeep(item)]) - ) - this.changeTracker = { added: [], removed: [], updated: [] } + this.lastStateDict = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)])) + this.changeTracker.added.length = 0 + this.changeTracker.removed.length = 0 + this.changeTracker.updated.length = 0 } /** @@ -130,42 +136,50 @@ export default class StateManager { endStateUpdate() { this.calculateDiff() this.saveStep() - this.syncDataState() + this.syncDataState(this.changeTracker) this.isChanged.value = true this.pendingChanges = true // 标记有需要保存的更改 } + // 差异反转方法 + private invertDiff(diff: DataDiff): DataDiff { + return { + added: diff.removed, + removed: diff.added, + updated: diff.updated.map(u => ({ before: u.after, after: u.before })) + } + } - /** - * 计算当前状态与快照的差异 - */ + // 计算当前状态与快照的差异 private calculateDiff() { - const currentMap = new Map( - this.vdata.items.map(item => [item.id, item]) - ) + const currentMap = new Map(this.vdata.items.map(item => [item.id, item])) + const added: ItemJson[] = [] + const removed: ItemJson[] = [] + const updated: { before: ItemJson; after: ItemJson }[] = [] - // 检测删除的项目 - for (const [id] of this.lastSnapshot) { + // 检查删除 & 更新 + for (const [id, item] of this.lastStateDict.entries()) { if (!currentMap.has(id)) { - this.changeTracker.removed.push(id) + removed.push(item) + + } else if (!_.isEqual(item, currentMap.get(id))) { + updated.push({ + before: item, + after: currentMap.get(id) + }) } } - // 检测新增和更新的项目 - for (const [id, currentItem] of currentMap) { - const lastItem = this.lastSnapshot.get(id) - - if (!lastItem) { - this.changeTracker.added.push(currentItem) - } else if (!_.isEqual(lastItem, currentItem)) { - this.changeTracker.updated.push(currentItem) + // 检查新增 + for (const [id, item] of currentMap.entries()) { + if (!this.lastStateDict.has(id)) { + added.push(item) } } - // 清除空变更 - if (this.changeTracker.added.length === 0) delete this.changeTracker.added - if (this.changeTracker.removed.length === 0) delete this.changeTracker.removed - if (this.changeTracker.updated.length === 0) delete this.changeTracker.updated + this.changeTracker.added = added + this.changeTracker.removed = removed + this.changeTracker.updated = updated } @@ -173,25 +187,27 @@ export default class StateManager { * 保存差异到历史记录 */ private saveStep() { - // 跳过空变更 - if ( - (!this.changeTracker.added || this.changeTracker.added.length === 0) && - (!this.changeTracker.removed || this.changeTracker.removed.length === 0) && - (!this.changeTracker.updated || this.changeTracker.updated.length === 0) - ) { - return + const { added, removed, updated } = this.changeTracker + if (added.length === 0 && removed.length === 0 && updated.length === 0) return + + // 深拷贝差异对象 + const clonedDiff = { + added: _.cloneDeep(added), + removed: _.cloneDeep(removed), + updated: _.cloneDeep(updated) } - // 使用循环缓冲区存储历史记录 - const nextIndex = (this.historyIndex + 1) % this.maxHistorySteps - this.historySteps[nextIndex] = { - diff: _.cloneDeep(this.changeTracker), - snapshot: _.cloneDeep(this.lastSnapshot), + const step: HistoryStep = { + diff: clonedDiff, timestamp: Date.now() } - this.historyIndex = nextIndex - this.pendingChanges = true // 标记有需要保存的更改 + if (this.historySteps.length >= this.maxHistorySteps) { + this.historySteps.shift() + } + this.historySteps.push(step) + this.historyIndex = this.historySteps.length - 1 + this.pendingChanges = true } @@ -203,49 +219,77 @@ export default class StateManager { * - 调用 entityManager.deleteEntity(id) 删除场景中的实体 * - 调用 entityManager.endEntityUpdate() 结束更新场景 */ - syncDataState() { + syncDataState(diff: DataDiff) { // 没有变化时跳过同步 if ( - (!this.changeTracker.added || this.changeTracker.added.length === 0) && - (!this.changeTracker.removed || this.changeTracker.removed.length === 0) && - (!this.changeTracker.updated || this.changeTracker.updated.length === 0) + diff.added.length === 0 && + diff.removed.length === 0 && + diff.updated.length === 0 ) { return } + console.log('[StateManager] 同步数据状态,变更:', diff) + this.entityManager.beginEntityUpdate() // 处理删除 - if (this.changeTracker.removed) { - for (const id of this.changeTracker.removed) { - this.entityManager.deleteEntity(id) - } + for (const item of diff.removed) { + this.entityManager.deleteEntity(item.id) } - // 处理新增 - if (this.changeTracker.added) { - for (const item of this.changeTracker.added) { - this.entityManager.createOrUpdateEntity(item) - } + for (const item of diff.added) { + this.entityManager.createOrUpdateEntity(item) } - // 处理更新 - if (this.changeTracker.updated) { - for (const item of this.changeTracker.updated) { - this.entityManager.createOrUpdateEntity(item) - } + for (const { after } of diff.updated) { + this.entityManager.createOrUpdateEntity(after) } this.entityManager.endEntityUpdate() } + /** + * 撤销 + */ + undo() { + if (!this.undoEnabled()) return + + const step = this.historySteps[this.historyIndex] + if (!step) return + + // 生成和应用反转差异 + const reverseDiff = this.invertDiff(step.diff) + this.applyDiff(reverseDiff) + this.syncDataState(reverseDiff) + this.historyIndex-- + this.isChanged.value = true + this.pendingChanges = true + } + + /** + * 重做 + */ + redo() { + if (!this.redoEnabled()) return + + this.historyIndex++ + const step = this.historySteps[this.historyIndex] + if (!step) return + + this.applyDiff(step.diff) + this.syncDataState(step.diff) + + this.isChanged.value = true + this.pendingChanges = true + } /** * 从外部加载数据 */ async load(data: Partial) { this.isLoading.value = true - this.historySteps = new Array(this.maxHistorySteps).fill(null) + this.historySteps = Array.from({ length: this.maxHistorySteps }, () => null) this.historyIndex = -1 try { @@ -259,7 +303,9 @@ export default class StateManager { isChanged: false, ...data } - this.fullSync() // 同步到视口 + + // 同步到视口 + this.fullSync() // 初始状态作为第一步 this.beginStateUpdate() @@ -270,8 +316,6 @@ export default class StateManager { // 强制保存一次初始状态 await this.saveToLocalstore() - this.pendingChanges = false - console.log('[StateManager] 加载完成,共', data.items.length, '个对象') } finally { @@ -281,109 +325,67 @@ export default class StateManager { } } - /** - * 撤销 - */ - undo() { - if (!this.undoEnabled()) return - - const step = this.historySteps[this.historyIndex] - if (!step) return - - this.applyReverseDiff(step.diff) - this.historyIndex = (this.historyIndex - 1 + this.maxHistorySteps) % this.maxHistorySteps - this.isChanged.value = true - this.pendingChanges = true - - this.lastSnapshot = new Map( - this.vdata.items.map(item => [item.id, _.cloneDeep(item)]) - ) - - this.syncDataState() - } - - /** - * 重做 - */ - redo() { - if (!this.redoEnabled()) return - - this.historyIndex = (this.historyIndex + 1) % this.maxHistorySteps - const step = this.historySteps[this.historyIndex] - if (!step) return - - this.applyDiff(step.diff) - this.isChanged.value = true - this.pendingChanges = true - - this.lastSnapshot = new Map( - this.vdata.items.map(item => [item.id, _.cloneDeep(item)]) - ) - - this.syncDataState() - } - - - /** - * 应用正向差异 - */ + // 差异应用方法 private applyDiff(diff: DataDiff) { - // 处理删除 - if (diff.removed) { - this.vdata.items = this.vdata.items.filter(item => !diff.removed.includes(item.id)) - } - - // 处理新增 - if (diff.added) { - this.vdata.items.push(...diff.added) - } - - // 处理更新 - if (diff.updated) { - const updateMap = new Map(diff.updated.map(item => [item.id, item])) - this.vdata.items = this.vdata.items.map(item => - updateMap.has(item.id) ? updateMap.get(item.id)! : item - ) - } - } - - /** - * 应用反向差异(用于撤销) - */ - private applyReverseDiff(diff: DataDiff) { - // 反向处理:删除 → 添加 - if (diff.removed) { - // 从历史快照恢复被删除的项目 - const restoredItems = diff.removed - .map(id => this.lastSnapshot.get(id)) - .filter(Boolean) as ItemJson[] - - this.vdata.items.push(...restoredItems) - } - - // 反向处理:添加 → 删除 - if (diff.added) { - const addedIds = new Set(diff.added.map(item => item.id)) - this.vdata.items = this.vdata.items.filter(item => !addedIds.has(item.id)) - } - - // 反向处理:更新 → 恢复旧值 - if (diff.updated) { - const restoreMap = new Map( - diff.updated - .map(item => [item.id, this.lastSnapshot.get(item.id)]) - .filter(([, item]) => !!item) as [string, ItemJson][] - ) - - for (const [key, itemJson] of restoreMap) { - const idx = _.findIndex(this.vdata.items, (item => item.id === key)) - if (idx >= 0) { - Object.assign(this.vdata.items[idx], itemJson) - } + const { added, removed, updated } = diff + + // 删除 + const removedIds = new Set(removed.map(item => item.id)) + _.remove(this.vdata.items, item => removedIds.has(item.id)) + + // 新增 + const addedIds = new Set(added.map(i => i.id)) + _.remove(this.vdata.items, item => addedIds.has(item.id)) + this.vdata.items.push(...added) + + // 更新 + const updateMap = new Map(updated.map(u => [u.after.id, u.after])) + for (const [key, after] of updateMap) { + const idx = _.findIndex(this.vdata.items, (item => item.id === key)) + if (idx >= 0) { + Object.assign(this.vdata.items[idx], after) + } else { + this.vdata.items.push(after) } } } + // /** + // * 应用反向差异(用于撤销) + // */ + // private applyReverseDiff(diff: DataDiff) { + // const { added, removed, updated } = diff + // + // // 恢复删除 + // const restored = removed + // .map(id => { + // const found = this.vdata.items.find(i => i.id === id) + // return found ? _.cloneDeep(found) : null + // }) + // .filter(Boolean) as ItemJson[] + // + // // 删除新增 + // const addedIds = new Set(added.map(a => a.id)) + // _.remove(this.vdata.items, (item => addedIds.has(item.id))) + // + // // 恢复更新 + // const restoreMap = new Map(updated.map(u => [u.after.id, u.before])) + // for (const [key, itemJson] of restoreMap) { + // const idx = _.findIndex(this.vdata.items, (item => item.id === key)) + // if (idx >= 0) { + // Object.assign(this.vdata.items[idx], itemJson) + // } else { + // this.vdata.items.push(itemJson) + // } + // } + // + // // 添加恢复的条目 + // this.vdata.items.push(...restored) + // + // // 更新 lastStateDict + // this.lastStateDict = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)])) + // } + private fullSync() { this.entityManager.beginEntityUpdate() this.vdata.items.forEach(item => { @@ -393,7 +395,7 @@ export default class StateManager { } undoEnabled() { - return this.historyIndex >= 0 && this.historySteps[this.historyIndex] + return this.historyIndex >= 0 } redoEnabled() { @@ -521,23 +523,6 @@ export default class StateManager { } /** - * 检查是否需要自动保存并执行 - */ - private async autoSaveIfNeeded() { - // 没有变化或正在加载时跳过 - if (!this.pendingChanges || this.isLoading.value) return - - try { - await this.saveToLocalstore() - this.pendingChanges = false - this.lastAutoSaveTime = Date.now() - console.debug(`[StateManager] 自动保存成功 at ${new Date().toLocaleTimeString()}`) - } catch (error) { - console.error('[StateManager] 自动保存失败:', error) - } - } - - /** * 销毁资源 */ destroy() { @@ -546,17 +531,5 @@ export default class StateManager { delete this.vdata delete this.historySteps } - - /** - * 获取自动保存状态 - */ - getAutoSaveStatus() { - return { - enabled: this.autoSaveInterval !== null, - interval: this.autoSaveIntervalMs, - lastSaveTime: this.lastAutoSaveTime, - pendingChanges: this.pendingChanges - } - } } diff --git a/src/example/example1.js b/src/example/example1.js index 3e183a7..d270fd9 100644 --- a/src/example/example1.js +++ b/src/example/example1.js @@ -61,18 +61,19 @@ export default { } }, { - id: 'p3', - t: 'measure', a: 'ln', l: '测量3', c: '#ff0000', + id: 'p3', t: 'measure', tf: [[-5.0, 0, 3], [0, 0, 0], [0.25, 0.1, 0.25]], dt: { + label: '测量3', center: ['p2'] } }, { id: 'p4', - t: 'measure', a: 'ln', l: '测量3', c: '#ff0000', + t: 'measure', tf: [[-9.0, 0, 8], [0, 0, 0], [0.25, 0.1, 0.25]], dt: { + label: '测量4', center: ['p2'] } }