import { set } from "lodash"; import { StateRoot } from "queenjs"; type RecordOptions = { combine?: boolean }; class State extends StateRoot { currLen = 0; //操作栈的长度 maxLen = 100; //操作栈总长度 opIndex = -1; //操作栈的指针 canUndo = this.computed((state) => { return state.opIndex >= 0; }); canRedo = this.computed((state) => { return state.opIndex < state.currLen - 1; }); } export class HistoryController { state = new State().reactive(); queue: GroupAction[] = []; cacheGroupAction = new GroupAction(); // 添加缓存记录 record(action: Action) { this.cacheGroupAction.record(action); } // 保存缓存记录到历史栈中 submit(action?: Action) { const { state, queue, cacheGroupAction } = this; if (action) this.record(action); if (!cacheGroupAction.actions.length) return; // 将缓存操作记录保存到当前指针的下一栈中 queue[++state.opIndex] = cacheGroupAction; // 设置栈的长度为指针的长度,舍弃后面的记录 queue.length = state.opIndex + 1; // 若栈长度超过上限, 舍弃之前的记录 if (queue.length > state.maxLen) { queue.splice(0, queue.length - state.maxLen); state.opIndex = state.maxLen - 1; } // 更新当前长度状态 state.currLen = queue.length; // 更新当前缓存GroupAction this.cacheGroupAction = new GroupAction(); } undo() { if (!this.state.canUndo) return; this.queue[this.state.opIndex--].undo(); } redo() { if (!this.state.canRedo) return; this.queue[++this.state.opIndex].redo(); } //清除操作 clear() { this.queue = []; this.state.currLen = 0; this.state.opIndex = -1; this.cacheGroupAction = new GroupAction(); } } export class Action { constructor( public type: "set" | "delete", public root: any, public path: string, public value?: any, public oldValue?: any ) {} undo() { set(this.root, this.path, this.oldValue); } redo() { set(this.root, this.path, this.value); } } export class GroupAction { actions: Action[] = []; record(action: Action, options?: RecordOptions) { const lastAction = this.actions.at(-1); if ( options?.combine && lastAction?.root === action.root && lastAction?.path === action.path ) { this.actions[this.actions.length - 1] = action; } else { this.actions.push(action); } } undo() { [...this.actions].reverse().forEach((act) => act.undo()); } redo() { this.actions.forEach((d) => d.redo()); } }