|
|
|
@ -89,6 +89,12 @@ export default class StateManager { |
|
|
|
*/ |
|
|
|
private lastSnapshot: Map<string, VDataItem> = new Map() |
|
|
|
|
|
|
|
// 自动保存相关
|
|
|
|
private autoSaveInterval: number | null = null |
|
|
|
private autoSaveIntervalMs = 5000 // 5秒自动保存
|
|
|
|
private pendingChanges = false // 是否有待保存的更改
|
|
|
|
private lastAutoSaveTime = 0 // 上次自动保存时间
|
|
|
|
|
|
|
|
/** |
|
|
|
* @param id 唯一场景标识符, 用于做临时存储的 key |
|
|
|
* @param viewport 视口对象, 用于获取、同步当前场景的状态 |
|
|
|
@ -99,7 +105,10 @@ export default class StateManager { |
|
|
|
this.viewport = markRaw(viewport) |
|
|
|
this.historyBufferSize = bufferSize |
|
|
|
// 初始化固定大小的历史缓冲区
|
|
|
|
this.historySteps = new Array(this.maxHistorySteps) |
|
|
|
this.historySteps = new Array(this.maxHistorySteps).fill(null) |
|
|
|
|
|
|
|
// 启动自动保存定时器
|
|
|
|
this.startAutoSave() |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -108,7 +117,7 @@ export default class StateManager { |
|
|
|
beginUserWrite() { |
|
|
|
// 创建当前状态快照(非深拷贝)
|
|
|
|
this.lastSnapshot = new Map( |
|
|
|
this.vdata.items.map(item => [item.id, item]) |
|
|
|
this.vdata.items.map(item => [item.id, _.cloneDeep(item)]) |
|
|
|
) |
|
|
|
this.changeTracker = { added: [], removed: [], updated: [] } |
|
|
|
} |
|
|
|
@ -121,6 +130,7 @@ export default class StateManager { |
|
|
|
this.saveStep() |
|
|
|
this.syncDataState() |
|
|
|
this.isChanged.value = true |
|
|
|
this.pendingChanges = true // 标记有需要保存的更改
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -178,7 +188,7 @@ export default class StateManager { |
|
|
|
} |
|
|
|
|
|
|
|
this.historyIndex = nextIndex |
|
|
|
this.saveToLocalstore() |
|
|
|
this.pendingChanges = true // 标记有需要保存的更改
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -232,18 +242,34 @@ export default class StateManager { |
|
|
|
*/ |
|
|
|
async load(items: VDataItem[]) { |
|
|
|
this.isLoading.value = true |
|
|
|
this.historySteps = new Array(this.maxHistorySteps).fill(null) |
|
|
|
this.historyIndex = -1 |
|
|
|
|
|
|
|
try { |
|
|
|
// 停止自动保存,避免在加载过程中触发
|
|
|
|
this.stopAutoSave() |
|
|
|
|
|
|
|
// 直接替换数组引用(避免响应式开销)
|
|
|
|
this.vdata.items = items |
|
|
|
this.fullSync() // 同步到视口
|
|
|
|
|
|
|
|
// 初始状态作为第一步
|
|
|
|
this.beginUserWrite() |
|
|
|
this.endUserWrite() |
|
|
|
|
|
|
|
this.isChanged.value = false |
|
|
|
this.pendingChanges = false |
|
|
|
|
|
|
|
// 强制保存一次初始状态
|
|
|
|
await this.saveToLocalstore() |
|
|
|
this.pendingChanges = false |
|
|
|
|
|
|
|
console.log('[StateManager] 加载完成,共 ', items.length, '个对象') |
|
|
|
|
|
|
|
} finally { |
|
|
|
this.isLoading.value = false |
|
|
|
// 重新启动自动保存
|
|
|
|
this.startAutoSave() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -255,6 +281,20 @@ export default class StateManager { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 强制保存到本地存储 |
|
|
|
*/ |
|
|
|
async forceSave() { |
|
|
|
try { |
|
|
|
await this.saveToLocalstore() |
|
|
|
this.pendingChanges = false |
|
|
|
return true |
|
|
|
} catch (error) { |
|
|
|
console.error('[StateManager] 强制保存失败:', error) |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 撤销 |
|
|
|
*/ |
|
|
|
undo() { |
|
|
|
@ -266,6 +306,7 @@ export default class StateManager { |
|
|
|
this.applyReverseDiff(step.diff) |
|
|
|
this.historyIndex = (this.historyIndex - 1 + this.maxHistorySteps) % this.maxHistorySteps |
|
|
|
this.isChanged.value = true |
|
|
|
this.pendingChanges = true |
|
|
|
this.syncDataState() |
|
|
|
} |
|
|
|
|
|
|
|
@ -281,6 +322,7 @@ export default class StateManager { |
|
|
|
|
|
|
|
this.applyDiff(step.diff) |
|
|
|
this.isChanged.value = true |
|
|
|
this.pendingChanges = true |
|
|
|
this.syncDataState() |
|
|
|
} |
|
|
|
|
|
|
|
@ -306,6 +348,8 @@ export default class StateManager { |
|
|
|
updateMap.has(item.id) ? updateMap.get(item.id)! : item |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
this.lastSnapshot = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)])) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
@ -340,47 +384,158 @@ export default class StateManager { |
|
|
|
restoreMap.has(item.id) ? restoreMap.get(item.id)! : item |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
this.lastSnapshot = new Map(this.vdata.items.map(item => [item.id, _.cloneDeep(item)])) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 保存到本地存储(防止数据丢失) |
|
|
|
*/ |
|
|
|
async saveToLocalstore() { |
|
|
|
// 只保存变化部分和关键元数据
|
|
|
|
const saveData = { |
|
|
|
diff: this.changeTracker, |
|
|
|
timestamp: Date.now(), |
|
|
|
itemsCount: this.vdata.items.length |
|
|
|
} |
|
|
|
// /**
|
|
|
|
// * 保存到本地存储(防止数据丢失)
|
|
|
|
// */
|
|
|
|
// async saveToLocalstore() {
|
|
|
|
// // 只保存变化部分和关键元数据
|
|
|
|
// const saveData = {
|
|
|
|
// diff: this.changeTracker,
|
|
|
|
// timestamp: Date.now(),
|
|
|
|
// itemsCount: this.vdata.items.length
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// await localforage.setItem(`scene-tmp-${this.id}`, saveData)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// /**
|
|
|
|
// * 从本地存储加载数据
|
|
|
|
// */
|
|
|
|
// async loadFromLocalstore() {
|
|
|
|
// try {
|
|
|
|
// this.isLoading.value = true
|
|
|
|
// const saved: any = await localforage.getItem(`scene-tmp-${this.id}`)
|
|
|
|
// 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
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
await localforage.setItem(`scene-tmp-${this.id}`, saveData) |
|
|
|
async saveToLocalstore() { |
|
|
|
await localforage.setItem(`scene-tmp-${this.id}`, this.vdata) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 从本地存储加载数据 |
|
|
|
*/ |
|
|
|
async loadFromLocalstore() { |
|
|
|
const saved: any = await localforage.getItem(`scene-tmp-${this.id}`) |
|
|
|
if (saved && saved.diff) { |
|
|
|
this.applyDiff(saved.diff) |
|
|
|
this.isChanged.value = true |
|
|
|
try { |
|
|
|
this.isLoading.value = true |
|
|
|
const saved: VData = await localforage.getItem(`scene-tmp-${this.id}`) |
|
|
|
if (saved) { |
|
|
|
this.vdata.items = saved.items || [] |
|
|
|
this.isChanged.value = saved.isChanged || false |
|
|
|
this.pendingChanges = true |
|
|
|
this.fullSync() // 同步到视口
|
|
|
|
console.log('[StateManager] 从本地存储恢复', this.vdata.items.length, '个对象') |
|
|
|
} |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
console.error('[StateManager] 从本地存储加载失败:', error) |
|
|
|
} finally { |
|
|
|
this.isLoading.value = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fullSync() { |
|
|
|
this.viewport.beginSync() |
|
|
|
this.vdata.items.forEach(item => { |
|
|
|
this.viewport.syncOnAppend(item) |
|
|
|
}) |
|
|
|
this.viewport.endSync() |
|
|
|
} |
|
|
|
|
|
|
|
undoEnabled() { |
|
|
|
return this.historyIndex >= 0 && this.historySteps[this.historyIndex] !== undefined |
|
|
|
return this.historyIndex >= 0 && this.historySteps[this.historyIndex] |
|
|
|
} |
|
|
|
|
|
|
|
redoEnabled() { |
|
|
|
const nextIndex = (this.historyIndex + 1) % this.maxHistorySteps |
|
|
|
return this.historySteps[nextIndex] !== undefined |
|
|
|
return !!this.historySteps[nextIndex] |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 删除本地存储 |
|
|
|
*/ |
|
|
|
async removeLocalstore() { |
|
|
|
await localforage.removeItem(`scene-tmp-${this.id}`) |
|
|
|
try { |
|
|
|
await localforage.removeItem(`scene-tmp-${this.id}`) |
|
|
|
console.log('[StateManager] 本地存储已清除') |
|
|
|
} catch (error) { |
|
|
|
console.error('[StateManager] 清除本地存储失败:', error) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 启动自动保存定时器 |
|
|
|
*/ |
|
|
|
startAutoSave() { |
|
|
|
if (this.autoSaveInterval) return |
|
|
|
|
|
|
|
this.autoSaveInterval = window.setInterval(() => { |
|
|
|
this.autoSaveIfNeeded() |
|
|
|
}, this.autoSaveIntervalMs) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 停止自动保存定时器 |
|
|
|
*/ |
|
|
|
stopAutoSave() { |
|
|
|
if (this.autoSaveInterval) { |
|
|
|
clearInterval(this.autoSaveInterval) |
|
|
|
this.autoSaveInterval = null |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 检查是否需要自动保存并执行 |
|
|
|
*/ |
|
|
|
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() { |
|
|
|
this.stopAutoSave() |
|
|
|
this.removeLocalstore().catch(console.error) |
|
|
|
// 清理引用
|
|
|
|
this.viewport = null as any |
|
|
|
this.vdata.items = [] |
|
|
|
this.historySteps = [] |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取自动保存状态 |
|
|
|
*/ |
|
|
|
getAutoSaveStatus() { |
|
|
|
return { |
|
|
|
enabled: this.autoSaveInterval !== null, |
|
|
|
interval: this.autoSaveIntervalMs, |
|
|
|
lastSaveTime: this.lastAutoSaveTime, |
|
|
|
pendingChanges: this.pendingChanges |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|