接入画布

This commit is contained in:
X1627315083
2025-06-09 10:25:54 +08:00
parent 87a08f5f8f
commit c266967f16
157 changed files with 43833 additions and 1571 deletions

View File

@@ -0,0 +1,433 @@
import { CompositeCommand } from "../../commands/Command.js";
import { PerformanceManager } from "./PerformanceManager.js";
/**
* 简化版命令管理器
* 基于经典撤销/重做模式,支持命令队列
* 使用复合命令替代事务处理
*/
export class CommandManager {
constructor(options = {}) {
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 {
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 {
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);
}

View File

@@ -0,0 +1,199 @@
/**
* 简化版性能管理器
* 提供基础的性能统计功能
*/
export class PerformanceManager {
constructor() {
this.stats = {
totalExecutions: 0,
totalUndos: 0,
totalRedos: 0,
totalExecutionTime: 0,
totalUndoTime: 0,
totalRedoTime: 0,
commandStats: new Map(), // 每个命令的统计信息
recentOperations: [], // 最近的操作记录
};
this.maxRecentOperations = 100;
}
/**
* 记录命令执行
*/
recordExecution(commandName, duration) {
this.stats.totalExecutions++;
this.stats.totalExecutionTime += duration;
this._updateCommandStats(commandName, "executions", duration);
this._addRecentOperation("execute", commandName, duration);
}
/**
* 记录撤销操作
*/
recordUndo(commandName, duration) {
this.stats.totalUndos++;
this.stats.totalUndoTime += duration;
this._updateCommandStats(commandName, "undos", duration);
this._addRecentOperation("undo", commandName, duration);
}
/**
* 记录重做操作
*/
recordRedo(commandName, duration) {
this.stats.totalRedos++;
this.stats.totalRedoTime += duration;
this._updateCommandStats(commandName, "redos", duration);
this._addRecentOperation("redo", commandName, duration);
}
/**
* 获取统计信息
*/
getStats() {
const avgExecutionTime =
this.stats.totalExecutions > 0
? this.stats.totalExecutionTime / this.stats.totalExecutions
: 0;
const avgUndoTime =
this.stats.totalUndos > 0
? this.stats.totalUndoTime / this.stats.totalUndos
: 0;
const avgRedoTime =
this.stats.totalRedos > 0
? this.stats.totalRedoTime / this.stats.totalRedos
: 0;
return {
overview: {
totalExecutions: this.stats.totalExecutions,
totalUndos: this.stats.totalUndos,
totalRedos: this.stats.totalRedos,
avgExecutionTime: Number(avgExecutionTime.toFixed(2)),
avgUndoTime: Number(avgUndoTime.toFixed(2)),
avgRedoTime: Number(avgRedoTime.toFixed(2)),
},
commandBreakdown: Array.from(this.stats.commandStats.entries()).map(
([name, stats]) => ({
commandName: name,
executions: stats.executions,
undos: stats.undos,
redos: stats.redos,
avgExecutionTime:
stats.executions > 0
? Number((stats.totalExecutionTime / stats.executions).toFixed(2))
: 0,
avgUndoTime:
stats.undos > 0
? Number((stats.totalUndoTime / stats.undos).toFixed(2))
: 0,
avgRedoTime:
stats.redos > 0
? Number((stats.totalRedoTime / stats.redos).toFixed(2))
: 0,
})
),
recentOperations: this.stats.recentOperations.slice(-20), // 最近20个操作
};
}
/**
* 获取慢命令报告
*/
getSlowCommandsReport(threshold = 100) {
const slowCommands = [];
for (const [name, stats] of this.stats.commandStats.entries()) {
const avgExecTime =
stats.executions > 0 ? stats.totalExecutionTime / stats.executions : 0;
const avgUndoTime =
stats.undos > 0 ? stats.totalUndoTime / stats.undos : 0;
if (avgExecTime > threshold || avgUndoTime > threshold) {
slowCommands.push({
commandName: name,
avgExecutionTime: Number(avgExecTime.toFixed(2)),
avgUndoTime: Number(avgUndoTime.toFixed(2)),
executions: stats.executions,
undos: stats.undos,
});
}
}
return slowCommands.sort(
(a, b) =>
Math.max(b.avgExecutionTime, b.avgUndoTime) -
Math.max(a.avgExecutionTime, a.avgUndoTime)
);
}
/**
* 重置统计信息
*/
reset() {
this.stats = {
totalExecutions: 0,
totalUndos: 0,
totalRedos: 0,
totalExecutionTime: 0,
totalUndoTime: 0,
totalRedoTime: 0,
commandStats: new Map(),
recentOperations: [],
};
}
/**
* 更新命令统计信息
* @private
*/
_updateCommandStats(commandName, type, duration) {
if (!this.stats.commandStats.has(commandName)) {
this.stats.commandStats.set(commandName, {
executions: 0,
undos: 0,
redos: 0,
totalExecutionTime: 0,
totalUndoTime: 0,
totalRedoTime: 0,
});
}
const stats = this.stats.commandStats.get(commandName);
if (type === "executions") {
stats.executions++;
stats.totalExecutionTime += duration;
} else if (type === "undos") {
stats.undos++;
stats.totalUndoTime += duration;
} else if (type === "redos") {
stats.redos++;
stats.totalRedoTime += duration;
}
}
/**
* 添加最近操作记录
* @private
*/
_addRecentOperation(type, commandName, duration) {
this.stats.recentOperations.push({
type,
commandName,
duration: Number(duration.toFixed(2)),
timestamp: Date.now(),
});
// 限制记录数量
if (this.stats.recentOperations.length > this.maxRecentOperations) {
this.stats.recentOperations.shift();
}
}
}