Files
aida_front/src/component/Canvas/CanvasEditor/managers/command/CommandManager.js
2026-01-19 16:57:11 +08:00

437 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { CompositeCommand } from "../../commands/Command.js";
import { PerformanceManager } from "./PerformanceManager.js";
/**
* 简化版命令管理器
* 基于经典撤销/重做模式,支持命令队列
* 使用复合命令替代事务处理
*/
export class CommandManager {
constructor(options = {}) {
this.canvas = options.canvas;
this.undoStack = [];
this.redoStack = [];
this.maxHistorySize = options.maxHistorySize || 50;
this.executing = false;
// 命令执行队列
this.commandQueue = [];
this.processing = false;
// 可选的性能管理器
this.performanceManager = options.performanceManager || null;
// 状态变化回调
this.onStateChange = null;
}
// 兼容旧的executeCommand方法
async executeCommand(command) {
return this.execute(command);
}
/**
* 执行命令并添加到撤销栈
*/
async execute(command) {
if (!command || typeof command.execute !== "function") {
throw new Error("无效的命令对象");
}
return this._executeDirectly(command);
}
/**
* 直接执行命令(绕过事务检查)
* @private
*/
async _executeDirectly(command) {
// 返回Promise等待命令执行完成
return new Promise((resolve, reject) => {
// 将命令添加到队列
this.commandQueue.push({
type: "execute",
command,
resolve,
reject,
});
// 开始处理队列
this._processQueue();
});
}
/**
* 撤销最后一个命令
*/
async undo() {
return new Promise((resolve, reject) => {
// 检查是否可以撤销
if (this.undoStack.length === 0) {
console.warn("无法撤销:撤销栈为空");
resolve(null);
return;
}
// 将撤销操作添加到队列
this.commandQueue.push({
type: "undo",
resolve,
reject,
});
// 开始处理队列
this._processQueue();
});
}
/**
* 重做最后一个撤销的命令
*/
async redo() {
return new Promise((resolve, reject) => {
// 检查是否可以重做
if (this.redoStack.length === 0) {
console.warn("无法重做:重做栈为空");
resolve(null);
return;
}
// 将重做操作添加到队列
this.commandQueue.push({
type: "redo",
resolve,
reject,
});
// 开始处理队列
this._processQueue();
});
}
/**
* 处理命令队列
* @private
*/
async _processQueue() {
// 如果正在处理或队列为空,直接返回
if (this.processing || this.commandQueue.length === 0) {
return;
}
this.processing = true;
try {
while (this.commandQueue.length > 0) {
const task = this.commandQueue.shift();
try {
let result = null;
switch (task.type) {
case "execute":
result = await this._executeCommandInternal(task.command);
break;
case "undo":
result = await this._undoInternal();
break;
case "redo":
result = await this._redoInternal();
break;
default:
throw new Error(`未知的任务类型: ${task.type}`);
}
task.resolve(result);
} catch (error) {
task.reject(error);
}
}
} finally {
this.processing = false;
}
}
/**
* 内部执行命令方法
* @private
*/
async _executeCommandInternal(command) {
this.executing = true;
const startTime = performance.now();
try {
console.log(`🔄 执行命令: ${command.constructor.name}`);
// 执行命令
const result = await this._executeCommand(command);
// 只有可撤销的命令才加入撤销栈
if (command.undoable !== false) {
this.undoStack.push(command);
this.redoStack = []; // 清空重做栈
// 限制历史记录大小
this._trimHistory();
}
// 记录性能
const duration = performance.now() - startTime;
this._recordPerformance("execute", command.constructor.name, duration);
// 通知状态变化
this._notifyStateChange();
console.log(`✅ 命令执行成功: ${command.constructor.name}`);
return result;
} catch (error) {
console.error(`❌ 命令执行失败: ${command.constructor.name}`, error);
throw error;
} finally {
this.executing = false;
}
}
/**
* 内部撤销方法
* @private
*/
async _undoInternal() {
if (this.undoStack.length === 0) {
console.warn("无法撤销:撤销栈为空");
return null;
}
this.executing = true;
const startTime = performance.now();
try {
this.canvas?.discardActiveObject();
const command = this.undoStack.pop();
console.log(`↩️ 撤销命令: ${command.constructor.name}`);
const result = await this._undoCommand(command);
this.redoStack.push(command);
// 记录性能
const duration = performance.now() - startTime;
this._recordPerformance("undo", command.constructor.name, duration);
// 通知状态变化
this._notifyStateChange();
console.log(`✅ 命令撤销成功: ${command.constructor.name}`);
return result;
} catch (error) {
console.error(`❌ 命令撤销失败`, error);
throw error;
} finally {
this.executing = false;
}
}
/**
* 内部重做方法
* @private
*/
async _redoInternal() {
if (this.redoStack.length === 0) {
console.warn("无法重做:重做栈为空");
return null;
}
this.executing = true;
const startTime = performance.now();
try {
this.canvas?.discardActiveObject();
const command = this.redoStack.pop();
console.log(`↪️ 重做命令: ${command.constructor.name}`);
const result = await this._executeCommand(command);
this.undoStack.push(command);
// 记录性能
const duration = performance.now() - startTime;
this._recordPerformance("redo", command.constructor.name, duration);
// 通知状态变化
this._notifyStateChange();
console.log(`✅ 命令重做成功: ${command.constructor.name}`);
return result;
} catch (error) {
console.error(`❌ 命令重做失败`, error);
throw error;
} finally {
this.executing = false;
}
}
/**
* 批量执行命令(使用 CompositeCommand
* 推荐使用此方法替代原来的事务机制
*/
async executeBatch(commands, batchName = "批量操作") {
if (!Array.isArray(commands) || commands.length === 0) {
throw new Error("命令数组不能为空");
}
const compositeCommand = new CompositeCommand(commands, {
name: batchName,
});
return this.execute(compositeCommand);
}
/**
* 清空历史记录
*/
clear() {
// 清空队列中的所有任务
while (this.commandQueue.length > 0) {
const task = this.commandQueue.shift();
task.reject(new Error("命令管理器已被清空"));
}
this.undoStack = [];
this.redoStack = [];
this._notifyStateChange();
// console.log("📝 命令历史已清空");
}
/**
* 获取管理器状态
*/
getState() {
return {
canUndo: this.undoStack.length > 0,
canRedo: this.redoStack.length > 0,
undoCount: this.undoStack.length,
redoCount: this.redoStack.length,
isExecuting: this.executing,
isProcessing: this.processing,
queueLength: this.commandQueue.length,
lastCommand:
this.undoStack.length > 0
? this.undoStack[this.undoStack.length - 1].constructor.name
: null,
nextRedoCommand:
this.redoStack.length > 0
? this.redoStack[this.redoStack.length - 1].constructor.name
: null,
};
}
/**
* 获取命令历史信息
*/
getHistory() {
return {
undoHistory: this.undoStack.map((cmd) => ({
name: cmd.constructor.name,
info: cmd.getInfo ? cmd.getInfo() : {},
timestamp: cmd.timestamp,
})),
redoHistory: this.redoStack.map((cmd) => ({
name: cmd.constructor.name,
info: cmd.getInfo ? cmd.getInfo() : {},
timestamp: cmd.timestamp,
})),
};
}
setChangeCallback(callback) {
if (typeof callback === "function") {
this.onStateChange = callback;
} else {
throw new Error("回调必须是一个函数");
}
}
/**
* 执行单个命令
* @private
*/
async _executeCommand(command) {
const result = command.execute();
return this._isPromise(result) ? await result : result;
}
/**
* 撤销单个命令
* @private
*/
async _undoCommand(command) {
if (typeof command.undo !== "function") {
throw new Error(`命令 ${command.constructor.name} 不支持撤销`);
}
const result = command.undo();
return this._isPromise(result) ? await result : result;
}
/**
* 检查是否为Promise
* @private
*/
_isPromise(value) {
return (
value &&
typeof value === "object" &&
typeof value.then === "function" &&
typeof value.catch === "function"
);
}
/**
* 限制历史记录大小
* @private
*/
_trimHistory() {
if (this.undoStack.length > this.maxHistorySize) {
this.undoStack.shift();
}
}
/**
* 记录性能数据
* @private
*/
_recordPerformance(type, commandName, duration) {
if (this.performanceManager) {
if (type === "execute") {
this.performanceManager.recordExecution(commandName, duration);
} else if (type === "undo") {
this.performanceManager.recordUndo(commandName, duration);
} else if (type === "redo") {
this.performanceManager.recordRedo(commandName, duration);
}
}
}
/**
* 通知状态变化
* @private
*/
_notifyStateChange() {
if (this.onStateChange) {
try {
this.onStateChange(this.getState());
} catch (error) {
console.error("状态变化回调执行失败:", error);
}
}
}
}
/**
* 创建命令管理器实例的工厂函数
*/
export function createCommandManager(options = {}) {
return new CommandManager(options);
}