Browse Source

stateManager 测试用例2 无法通过

master
修宁 7 months ago
parent
commit
58fcffa7d4
  1. 375
      src/core/manager/StateManager.ts
  2. 7
      src/example/example1.js

375
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<string, ItemJson>
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<string, ItemJson> = new Map()
private lastStateDict = new Map<string, ItemJson>()
// 自动保存相关
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) {
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<VData>) {
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[]
const { added, removed, updated } = diff
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))
}
// 删除
const removedIds = new Set(removed.map(item => item.id))
_.remove(this.vdata.items, item => removedIds.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][]
)
// 新增
const addedIds = new Set(added.map(i => i.id))
_.remove(this.vdata.items, item => addedIds.has(item.id))
this.vdata.items.push(...added)
for (const [key, itemJson] of restoreMap) {
// 更新
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], itemJson)
}
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
}
}
}

7
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']
}
}

Loading…
Cancel
Save