需求是页面新增删除元素操作或拖拽元素操作后,通过点击撤销和恢复按钮撤销上一步操作或恢复被撤销的操作,下面是在html中引入vue2实现的demo,可直接复制到本地查看
<html>
<head>
<title>元素操作撤销恢复</title>
<style>
.container {
position: relative;
width: 500px;
height: 300px;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.draggable {
position: absolute;
background-color: #f0f0f0;
border: 1px solid #999;
padding: 10px;
cursor: move;
z-index: 99;
}
.actions {
margin-top: 10px;
}
.delete {
position: fixed;
left: 550px;
top: 10px;
background-color: red;
width: 150px;
height: 150px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<!-- 拖拽区域 -->
<div v-for="item in items" :key="item.id" :ref="item.id" class="draggable"
:style="`top: ${item.top}px; left: ${item.left}px`" @mousedown="startDrag(item, $event)">
{{ item.text }}
</div>
<div class="delete" v-show="items.length > 0">拖拽至此删除</div>
</div>
<div class="actions">
<!-- 操作按钮 -->
<button @click="addItem">新增</button>
<button :disabled="!canUndo" @click="undo">撤销</button>
<button :disabled="!canRedo" @click="redo">恢复</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script>
new Vue({
el: '#app',
data: {
items: [], // 存储拖拽元素的数组
undoStack: [], // 撤销操作的栈
redoStack: [], // 恢复操作的栈
},
computed: {
canUndo() {
return this.undoStack.length > 0;
},
canRedo() {
return this.redoStack.length > 0;
}
},
methods: {
// 开始拖拽元素
startDrag(item, event) {
const startX = item.left;
const startY = item.top;
const container = this.$el.querySelector('.container');
const rect = container.getBoundingClientRect();
const offsetX = event.clientX - item.left - rect.left;
const offsetY = event.clientY - item.top - rect.top;
const moveHandler = (event) => {
const left = event.clientX - rect.left - offsetX;
const top = event.clientY - rect.top - offsetY;
item.left = left;
item.top = top;
};
const endHandler = () => {
document.removeEventListener('mousemove', moveHandler);
document.removeEventListener('mouseup', endHandler);
// 判断鼠标松开时的位置是否在删除区域内
const deleteContainer = document.querySelector('.delete');
const deleteRect = deleteContainer.getBoundingClientRect();
if (
event.clientX > deleteRect.left &&
event.clientX < deleteRect.right &&
event.clientY > deleteRect.top &&
event.clientY < deleteRect.bottom
) {
// 执行删除操作
const index = this.items.findIndex((i) => i.id === item.id);
if (index > -1) {
const deletedItem = this.items.splice(index, 1)[0];
this.undoStack.push({ type: 'delete', item: deletedItem, index });
this.redoStack = []; // 删除元素后清空恢复栈
}
} else {
// 添加拖拽操作记录到undoStack栈
this.undoStack.push({ type: 'drag', item, startPos: { left: startX, top: startY }, endPos: { left: item.left, top: item.top } });
this.redoStack = []; // 修改元素后清空恢复栈
}
};
// 监听鼠标移动和松开事件
document.addEventListener('mousemove', moveHandler);
document.addEventListener('mouseup', endHandler);
},
// 撤销操作
undo() {
const lastAction = this.undoStack.pop();
if (lastAction.type === 'add') {
const index = this.items.findIndex((item) => item.id === lastAction.item.id);
if (index > -1) {
this.items.splice(index, 1);
this.redoStack.push(lastAction);
}
} else if (lastAction.type === 'delete') {
this.items.push(lastAction.item);
this.redoStack.push(lastAction);
} else if (lastAction.type === 'drag') {
// 将拖拽操作记录添加到redoStack栈
this.redoStack.push(lastAction);
// 还原元素拖拽后的位置
lastAction.item.left = lastAction.startPos.left;
lastAction.item.top = lastAction.startPos.top;
}
},
// 恢复操作
redo() {
const lastAction = this.redoStack.pop();
if (lastAction.type === 'add') {
this.items.push(lastAction.item);
this.undoStack.push(lastAction);
} else if (lastAction.type === 'delete') {
const index = this.items.findIndex((item) => item.id === lastAction.item.id);
if (index > -1) {
this.items.splice(index, 1);
this.undoStack.push(lastAction);
}
} else if (lastAction.type === 'drag') {
// 将拖拽操作记录添加到undoStack栈
this.undoStack.push(lastAction);
// 还原元素拖拽后的位置
lastAction.item.left = lastAction.endPos.left;
lastAction.item.top = lastAction.endPos.top;
}
},
// 新增元素
addItem() {
const newItem = {
id: Date.now(),
text: `元素 ${this.items.length + 1}`,
top: 0,
left: 0
};
this.items.push(newItem);
this.undoStack.push({ type: 'add', item: newItem });
this.redoStack = []; // 新增元素后清空恢复栈
},
}
});
</script>
</body>
</html>