|
|
@ -90,7 +90,10 @@ export default class StateManager { |
|
|
*/ |
|
|
*/ |
|
|
private historySteps: HistoryStep[] = [] |
|
|
private historySteps: HistoryStep[] = [] |
|
|
private historyIndex = -1 |
|
|
private historyIndex = -1 |
|
|
private readonly maxHistorySteps = 20 |
|
|
private maxHistorySteps = 20 |
|
|
|
|
|
|
|
|
|
|
|
// 标记是否有未保存的更改
|
|
|
|
|
|
private pendingChanges = false |
|
|
|
|
|
|
|
|
// 变化追踪器
|
|
|
// 变化追踪器
|
|
|
private readonly changeTracker: DataDiff = { |
|
|
private readonly changeTracker: DataDiff = { |
|
|
@ -104,20 +107,16 @@ export default class StateManager { |
|
|
*/ |
|
|
*/ |
|
|
private lastStateDict = new Map<string, ItemJson>() |
|
|
private lastStateDict = new Map<string, ItemJson>() |
|
|
|
|
|
|
|
|
// 自动保存相关
|
|
|
|
|
|
private autoSaveInterval: number | null = null |
|
|
|
|
|
// 是否有待保存的更改
|
|
|
|
|
|
private pendingChanges = false |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* @param id 唯一场景标识符, 用于做临时存储的 key |
|
|
* @param id 唯一场景标识符, 用于做临时存储的 key |
|
|
* @param viewport 视口对象, 用于获取、同步当前场景的状态 |
|
|
* @param viewport 视口对象, 用于获取、同步当前场景的状态 |
|
|
* @param bufferSize 历史记录缓冲区大小,默认为 50 |
|
|
* @param maxHistorySteps 最大回撤步数 默认为 20 |
|
|
*/ |
|
|
*/ |
|
|
constructor(id: string, viewport: Viewport, bufferSize = 50) { |
|
|
constructor(id: string, viewport: Viewport, maxHistorySteps = 20) { |
|
|
this.id = id |
|
|
this.id = id |
|
|
this.storeKey = `-tmp-yvan-lcc-${this.id}` |
|
|
this.storeKey = `-tmp-yvan-lcc-${this.id}` |
|
|
this.entityManager = viewport.entityManager |
|
|
this.entityManager = viewport.entityManager |
|
|
|
|
|
this.maxHistorySteps = maxHistorySteps |
|
|
|
|
|
|
|
|
// this.historySteps = Array.from({ length: this.maxHistorySteps }, () => null)
|
|
|
// this.historySteps = Array.from({ length: this.maxHistorySteps }, () => null)
|
|
|
this.historySteps = [] |
|
|
this.historySteps = [] |
|
|
@ -126,7 +125,7 @@ export default class StateManager { |
|
|
/** |
|
|
/** |
|
|
* 开始用户操作(创建数据快照) |
|
|
* 开始用户操作(创建数据快照) |
|
|
*/ |
|
|
*/ |
|
|
beginStateUpdate() { |
|
|
beginStateUpdate(): void { |
|
|
this.lastStateDict = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)])) |
|
|
this.lastStateDict = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)])) |
|
|
this.changeTracker.added.length = 0 |
|
|
this.changeTracker.added.length = 0 |
|
|
this.changeTracker.removed.length = 0 |
|
|
this.changeTracker.removed.length = 0 |
|
|
@ -136,7 +135,7 @@ export default class StateManager { |
|
|
/** |
|
|
/** |
|
|
* 结束用户操作(计算差异并保存) |
|
|
* 结束用户操作(计算差异并保存) |
|
|
*/ |
|
|
*/ |
|
|
endStateUpdate() { |
|
|
endStateUpdate(): void { |
|
|
this.calculateDiff() |
|
|
this.calculateDiff() |
|
|
this.saveStep() |
|
|
this.saveStep() |
|
|
this.syncDataState(this.changeTracker) |
|
|
this.syncDataState(this.changeTracker) |
|
|
@ -215,14 +214,13 @@ export default class StateManager { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 将当前数据 与 viewport 进行同步, 对比出不同的部分,分别进行更新 |
|
|
* 将当前数据 与 entityManager 进行同步, 对比出不同的部分,分别进行更新 |
|
|
* - 调用 entityManager.beginEntityUpdate() 开始更新 |
|
|
* - 调用 entityManager.beginEntityUpdate() 开始更新 |
|
|
* - 调用 entityManager.createEntity(vdataItem) 添加场景中新的实体 |
|
|
* - 调用 entityManager.createOrUpdateEntity(vdataItem) 添加场景中新的实体 |
|
|
* - 调用 entityManager.updateEntity(vdataItem) 新场景中已存在的实体 |
|
|
|
|
|
* - 调用 entityManager.deleteEntity(id) 删除场景中的实体 |
|
|
* - 调用 entityManager.deleteEntity(id) 删除场景中的实体 |
|
|
* - 调用 entityManager.endEntityUpdate() 结束更新场景 |
|
|
* - 调用 entityManager.endEntityUpdate() 结束更新场景 |
|
|
*/ |
|
|
*/ |
|
|
syncDataState(diff: DataDiff) { |
|
|
syncDataState(diff: DataDiff): void { |
|
|
// 没有变化时跳过同步
|
|
|
// 没有变化时跳过同步
|
|
|
if ( |
|
|
if ( |
|
|
diff.added.length === 0 && |
|
|
diff.added.length === 0 && |
|
|
@ -291,6 +289,14 @@ export default class StateManager { |
|
|
system.msg('重做完成') |
|
|
system.msg('重做完成') |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
stopAutoSave() { |
|
|
|
|
|
// 停止自动保存逻辑
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
startAutoSave() { |
|
|
|
|
|
// 实现自动保存逻辑, 只要调用过 endStateUpdate(), 固定 5秒后触发 saveToLocalstore 方法, 调用过程不用等回调完成
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 从外部加载数据 |
|
|
* 从外部加载数据 |
|
|
*/ |
|
|
*/ |
|
|
@ -357,42 +363,6 @@ export default class StateManager { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 应用反向差异(用于撤销)
|
|
|
|
|
|
// */
|
|
|
|
|
|
// 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() { |
|
|
private fullSync() { |
|
|
this.entityManager.beginEntityUpdate() |
|
|
this.entityManager.beginEntityUpdate() |
|
|
this.vdata.items.forEach(item => { |
|
|
this.vdata.items.forEach(item => { |
|
|
@ -410,42 +380,6 @@ export default class StateManager { |
|
|
return !!this.historySteps[nextIndex] |
|
|
return !!this.historySteps[nextIndex] |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 保存到本地存储(防止数据丢失)
|
|
|
|
|
|
// */
|
|
|
|
|
|
// async saveToLocalstore() {
|
|
|
|
|
|
// // 只保存变化部分和关键元数据
|
|
|
|
|
|
// const saveData = {
|
|
|
|
|
|
// diff: this.changeTracker,
|
|
|
|
|
|
// timestamp: Date.now(),
|
|
|
|
|
|
// itemsCount: this.vdata.items.length
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// await localforage.setItem(this.storeKey, saveData)
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 从本地存储加载数据
|
|
|
|
|
|
// */
|
|
|
|
|
|
// async loadFromLocalstore() {
|
|
|
|
|
|
// try {
|
|
|
|
|
|
// this.isLoading.value = true
|
|
|
|
|
|
// const saved: any = await localforage.getItem(this.storeKey)
|
|
|
|
|
|
// if (saved && saved.diff) {
|
|
|
|
|
|
// this.applyDiff(saved.diff)
|
|
|
|
|
|
// this.isChanged.value = true
|
|
|
|
|
|
// this.pendingChanges = true
|
|
|
|
|
|
// console.log('[StateManager] 从本地存储恢复 ', saved.itemsCount, '个对象')
|
|
|
|
|
|
// }
|
|
|
|
|
|
//
|
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
|
// console.error('[StateManager] 从本地存储加载失败:', error)
|
|
|
|
|
|
//
|
|
|
|
|
|
// } finally {
|
|
|
|
|
|
// this.isLoading.value = false
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 保存数据到外部 |
|
|
* 保存数据到外部 |
|
|
*/ |
|
|
*/ |
|
|
@ -454,20 +388,6 @@ export default class StateManager { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 强制保存到本地存储 |
|
|
|
|
|
*/ |
|
|
|
|
|
async forceSave() { |
|
|
|
|
|
try { |
|
|
|
|
|
await this.saveToLocalstore() |
|
|
|
|
|
this.pendingChanges = false |
|
|
|
|
|
return true |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('[StateManager] 强制保存失败:', error) |
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 保存到本地存储 浏览器indexDb(防止数据丢失) |
|
|
* 保存到本地存储 浏览器indexDb(防止数据丢失) |
|
|
*/ |
|
|
*/ |
|
|
async saveToLocalstore() { |
|
|
async saveToLocalstore() { |
|
|
@ -482,7 +402,9 @@ export default class StateManager { |
|
|
this.isLoading.value = true |
|
|
this.isLoading.value = true |
|
|
const saved: VData = await localforage.getItem(this.storeKey) |
|
|
const saved: VData = await localforage.getItem(this.storeKey) |
|
|
if (saved) { |
|
|
if (saved) { |
|
|
this.vdata.items = saved.items || [] |
|
|
this.vdata = { |
|
|
|
|
|
...saved |
|
|
|
|
|
} |
|
|
this.isChanged.value = saved.isChanged || false |
|
|
this.isChanged.value = saved.isChanged || false |
|
|
this.pendingChanges = true |
|
|
this.pendingChanges = true |
|
|
this.fullSync() // 同步到视口
|
|
|
this.fullSync() // 同步到视口
|
|
|
@ -491,6 +413,7 @@ export default class StateManager { |
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error('[StateManager] 从本地存储加载失败:', error) |
|
|
console.error('[StateManager] 从本地存储加载失败:', error) |
|
|
|
|
|
|
|
|
} finally { |
|
|
} finally { |
|
|
this.isLoading.value = false |
|
|
this.isLoading.value = false |
|
|
} |
|
|
} |
|
|
@ -509,31 +432,9 @@ export default class StateManager { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 启动自动保存定时器 |
|
|
|
|
|
*/ |
|
|
|
|
|
startAutoSave() { |
|
|
|
|
|
// if (this.autoSaveInterval) return
|
|
|
|
|
|
//
|
|
|
|
|
|
// this.autoSaveInterval = window.setInterval(() => {
|
|
|
|
|
|
// this.autoSaveIfNeeded()
|
|
|
|
|
|
// }, this.autoSaveIntervalMs)
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 停止自动保存定时器 |
|
|
|
|
|
*/ |
|
|
|
|
|
stopAutoSave() { |
|
|
|
|
|
// if (this.autoSaveInterval) {
|
|
|
|
|
|
// clearInterval(this.autoSaveInterval)
|
|
|
|
|
|
// this.autoSaveInterval = null
|
|
|
|
|
|
// }
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 销毁资源 |
|
|
* 销毁资源 |
|
|
*/ |
|
|
*/ |
|
|
destroy() { |
|
|
destroy() { |
|
|
this.stopAutoSave() |
|
|
|
|
|
// 清理引用
|
|
|
// 清理引用
|
|
|
delete this.vdata |
|
|
delete this.vdata |
|
|
delete this.historySteps |
|
|
delete this.historySteps |
|
|
|