|
@@ -22,6 +22,8 @@ export default {
|
|
|
canvasState: [],//操作记录
|
|
canvasState: [],//操作记录
|
|
|
stateIndex: -1,
|
|
stateIndex: -1,
|
|
|
canUndoState:false,
|
|
canUndoState:false,
|
|
|
|
|
+ undoKeyHandler: null, // 键盘撤销事件处理器
|
|
|
|
|
+ undoEventHandlers: [], // Fabric事件处理器引用,用于清理
|
|
|
|
|
|
|
|
//画笔在层上操作
|
|
//画笔在层上操作
|
|
|
group:null,
|
|
group:null,
|
|
@@ -46,7 +48,7 @@ export default {
|
|
|
computed: {
|
|
computed: {
|
|
|
//是否可以撤销
|
|
//是否可以撤销
|
|
|
canUndo() {
|
|
canUndo() {
|
|
|
- return this.stateIndex >= 0;
|
|
|
|
|
|
|
+ return this.stateIndex > 0;
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
watch: {
|
|
watch: {
|
|
@@ -90,6 +92,14 @@ export default {
|
|
|
|
|
|
|
|
methods: {
|
|
methods: {
|
|
|
actionInit(){
|
|
actionInit(){
|
|
|
|
|
+ // 先清理旧的事件监听器
|
|
|
|
|
+ this.removeUndoListeners();
|
|
|
|
|
+
|
|
|
|
|
+ // 重置撤销状态(每次切换画布时)
|
|
|
|
|
+ this.canvasState = []
|
|
|
|
|
+ this.stateIndex = -1
|
|
|
|
|
+ this.canUndoState = false
|
|
|
|
|
+
|
|
|
//监听撤销
|
|
//监听撤销
|
|
|
if(this.toolConfig.includes('undo')){
|
|
if(this.toolConfig.includes('undo')){
|
|
|
this.setUndo()
|
|
this.setUndo()
|
|
@@ -697,33 +707,134 @@ export default {
|
|
|
},
|
|
},
|
|
|
//撤销
|
|
//撤销
|
|
|
historyState(index){
|
|
historyState(index){
|
|
|
- if (this.canUndo) {
|
|
|
|
|
|
|
+ console.log("↶ Undo requested, index:", index, "canUndo:", this.canUndo, "current stateIndex:", this.stateIndex);
|
|
|
|
|
+
|
|
|
|
|
+ if (this.canUndo && index >= 0 && index < this.canvasState.length) {
|
|
|
|
|
+ console.log("↶ Executing undo to state:", index);
|
|
|
this.canUndoState = true;
|
|
this.canUndoState = true;
|
|
|
- this.fcanvas.loadFromJSON(this.canvasState[index], () => {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const stateJson = this.canvasState[index];
|
|
|
|
|
+ console.log("↶ Loading state JSON length:", stateJson.length);
|
|
|
|
|
+
|
|
|
|
|
+ this.fcanvas.loadFromJSON(stateJson, () => {
|
|
|
|
|
+ console.log("↶ State loaded successfully");
|
|
|
this.fcanvas.renderAll();
|
|
this.fcanvas.renderAll();
|
|
|
this.stateIndex = index;
|
|
this.stateIndex = index;
|
|
|
- this.canvasState.splice(this.stateIndex+1);
|
|
|
|
|
- this.getLayers()
|
|
|
|
|
|
|
+ this.canvasState.splice(this.stateIndex+1); // 删除当前状态之后的所有状态
|
|
|
|
|
+
|
|
|
|
|
+ console.log("↶ Undo completed, new stateIndex:", this.stateIndex, "remaining states:", this.canvasState.length);
|
|
|
|
|
+
|
|
|
|
|
+ // 在撤销过程中禁用状态更新,避免重复保存状态
|
|
|
|
|
+ this.getLayers(false)
|
|
|
this.undoAfterSelectLayers();
|
|
this.undoAfterSelectLayers();
|
|
|
this.canUndoState = false;
|
|
this.canUndoState = false;
|
|
|
});
|
|
});
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log("↶ Undo not possible:", { canUndo: this.canUndo, index, totalStates: this.canvasState.length });
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
setUndo(){
|
|
setUndo(){
|
|
|
- //监听撤销
|
|
|
|
|
- this.fcanvas.on("object:modified", ()=>{
|
|
|
|
|
- this.updateCanvasState()
|
|
|
|
|
|
|
+ // 先移除之前的所有撤销相关事件监听器,避免重复绑定
|
|
|
|
|
+ this.removeUndoListeners();
|
|
|
|
|
+
|
|
|
|
|
+ // 保存事件处理器引用,用于后续清理
|
|
|
|
|
+ this.undoEventHandlers = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 只监听核心的修改事件,避免过度触发
|
|
|
|
|
+ const coreEvents = [
|
|
|
|
|
+ "object:modified", // 对象修改(移动、缩放、旋转等)
|
|
|
|
|
+ "path:created", // 绘制路径
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ coreEvents.forEach(eventName => {
|
|
|
|
|
+ const handler = (e) => {
|
|
|
|
|
+ console.log(`🔄 Undo event: ${eventName}`, {
|
|
|
|
|
+ type: e?.target?.type,
|
|
|
|
|
+ id: e?.target?.id,
|
|
|
|
|
+ left: e?.target?.left,
|
|
|
|
|
+ top: e?.target?.top
|
|
|
|
|
+ });
|
|
|
|
|
+ this.updateCanvasState();
|
|
|
|
|
+ };
|
|
|
|
|
+ this.undoEventHandlers.push({ event: eventName, handler });
|
|
|
|
|
+ this.fcanvas.on(eventName, handler);
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ // 监听对象添加和删除事件
|
|
|
|
|
+ const addHandler = (e) => {
|
|
|
|
|
+ console.log(`➕ Object added:`, e?.target?.type, e?.target?.id);
|
|
|
|
|
+ this.updateCanvasState();
|
|
|
|
|
+ };
|
|
|
|
|
+ const removeHandler = (e) => {
|
|
|
|
|
+ console.log(`➖ Object removed:`, e?.target?.type, e?.target?.id);
|
|
|
|
|
+ this.updateCanvasState();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ this.undoEventHandlers.push(
|
|
|
|
|
+ { event: "object:added", handler: addHandler },
|
|
|
|
|
+ { event: "object:removed", handler: removeHandler }
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ this.fcanvas.on("object:added", addHandler);
|
|
|
|
|
+ this.fcanvas.on("object:removed", removeHandler);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加键盘快捷键监听 (Ctrl+Z)
|
|
|
|
|
+ this.setupKeyboardUndo();
|
|
|
|
|
+
|
|
|
|
|
+ console.log("🎯 Undo system initialized with", this.undoEventHandlers.length, "event listeners");
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 移除所有撤销相关的事件监听器
|
|
|
|
|
+ removeUndoListeners(){
|
|
|
|
|
+ if (!this.fcanvas || !this.undoEventHandlers) return;
|
|
|
|
|
+
|
|
|
|
|
+ this.undoEventHandlers.forEach(({ event, handler }) => {
|
|
|
|
|
+ this.fcanvas.off(event, handler);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ this.undoEventHandlers = [];
|
|
|
|
|
+ console.log("🧹 Removed all undo event listeners");
|
|
|
|
|
+ },
|
|
|
|
|
+ setupKeyboardUndo(){
|
|
|
|
|
+ if (!this.fcanvas) return;
|
|
|
|
|
+
|
|
|
|
|
+ const canvasWrapper = this.fcanvas.wrapperEl;
|
|
|
|
|
+ if (!canvasWrapper) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 移除之前可能存在的监听器
|
|
|
|
|
+ if (this.undoKeyHandler) {
|
|
|
|
|
+ canvasWrapper.removeEventListener('keydown', this.undoKeyHandler);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 添加新的键盘事件监听
|
|
|
|
|
+ this.undoKeyHandler = (e) => {
|
|
|
|
|
+ // Ctrl+Z 或 Cmd+Z (Mac)
|
|
|
|
|
+ if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+ if (this.canUndo) {
|
|
|
|
|
+ this.historyState(this.stateIndex - 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ canvasWrapper.addEventListener('keydown', this.undoKeyHandler);
|
|
|
},
|
|
},
|
|
|
updateCanvasState(e) {
|
|
updateCanvasState(e) {
|
|
|
|
|
+ console.log("🔄 updateCanvasState called, canUndoState:", this.canUndoState);
|
|
|
|
|
+
|
|
|
if (!this.canUndoState) {
|
|
if (!this.canUndoState) {
|
|
|
const canvasAsJson = JSON.stringify(this.fcanvas.toJSON(['name','sort','mtr','id','selectable','erasable']));
|
|
const canvasAsJson = JSON.stringify(this.fcanvas.toJSON(['name','sort','mtr','id','selectable','erasable']));
|
|
|
this.canvasState.splice(this.stateIndex+1);
|
|
this.canvasState.splice(this.stateIndex+1);
|
|
|
this.canvasState.push(canvasAsJson);
|
|
this.canvasState.push(canvasAsJson);
|
|
|
this.stateIndex = this.canvasState.length - 1;
|
|
this.stateIndex = this.canvasState.length - 1;
|
|
|
|
|
+
|
|
|
|
|
+ console.log("💾 Canvas state saved, total states:", this.canvasState.length, "current index:", this.stateIndex);
|
|
|
|
|
+
|
|
|
// if(this.viewStatus) this.updateMiniMap();
|
|
// if(this.viewStatus) this.updateMiniMap();
|
|
|
|
|
|
|
|
} else {
|
|
} else {
|
|
|
|
|
+ console.log("🚫 Skipping state save due to canUndoState flag");
|
|
|
this.canUndoState = false;
|
|
this.canUndoState = false;
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
@@ -817,9 +928,17 @@ export default {
|
|
|
if (!this.fcanvas) return
|
|
if (!this.fcanvas) return
|
|
|
|
|
|
|
|
const canvasWrapper = this.fcanvas.wrapperEl
|
|
const canvasWrapper = this.fcanvas.wrapperEl
|
|
|
- if (canvasWrapper && this.handleKeyDown) {
|
|
|
|
|
- canvasWrapper.removeEventListener('keydown', this.handleKeyDown)
|
|
|
|
|
- this.handleKeyDown = null
|
|
|
|
|
|
|
+ if (canvasWrapper) {
|
|
|
|
|
+ // 清理键盘导航监听器
|
|
|
|
|
+ if (this.handleKeyDown) {
|
|
|
|
|
+ canvasWrapper.removeEventListener('keydown', this.handleKeyDown)
|
|
|
|
|
+ this.handleKeyDown = null
|
|
|
|
|
+ }
|
|
|
|
|
+ // 清理撤销键盘监听器
|
|
|
|
|
+ if (this.undoKeyHandler) {
|
|
|
|
|
+ canvasWrapper.removeEventListener('keydown', this.undoKeyHandler)
|
|
|
|
|
+ this.undoKeyHandler = null
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|