vue3 实现历史步骤记录

news2024/11/15 21:44:59

通过vueuse中的 useManualRefHistory,快速实现历史操作记录

所需环境:

  • vue

实现目标

  • 历史记录
  • 撤销
  • 重做
  • 记录覆盖

代码原理

把键盘监听事件挂载在document上,当键盘事件发生时,依次匹配事先订阅的快捷键/单按键事件,
如果有匹配到对应的订阅,则进行事件回调,并且停止键盘事件的回调。按下的按键进行转换,把mac/win的键盘差异进行了兼容

实现效果

差两个视频

vue历史记录实战

核心代码

import {ref} from 'vue';
import {useManualRefHistory} from '@vueuse/core';

export enum ActionEnum {
    INIT = '加载',
    ADD = '添加<{element}>',
    RESIZE = '修改<{element}>尺寸',
    ROTATE = '旋转<{element}>',
    REMOVE = '删除<{element}>',
    PASTE = '粘贴<{element}>',
    CUT = '剪切<{element}>',
    CLEAR = '清空面板',
    MOVE = '移动<{element}>',
    BATCH_MOVE = '移动<多个元素>',
    UPDATE_STYLE = '修改<{element}>的[{content}]',
    BATCH_UPDATE_STYLE = '修改<多个元素>的[{content}]',
}

let max = 50;

export interface Snapshot {
    elementList?: any[]
    content?: string
    action: ActionEnum,
    type?: 'Element' | 'PANEL'
}

const historyRecord = ref<Snapshot>({} as Snapshot);
const {
    undoStack,
    redoStack,
    commit,
    history,
    undo,
    redo,
    clear,
    canUndo,
    canRedo
} = useManualRefHistory(historyRecord, {capacity: max});

/**
 * 初始化记录
 * @param elementList
 */
function init(elementList: any[]) {
    record(<Snapshot>{
        type: 'PANEL',
        action: ActionEnum.INIT,
        elementList
    });
    clear();
}

/**
 * 记录历史操作
 * @param snapshot
 */
function record(snapshot: Snapshot) {
    let action = snapshot.action as any;
    let label = '';
    if (snapshot.elementList) {
        for (let myElement of snapshot.elementList) {
            label = label + myElement.label + ',';
        }
        label = label.slice(0, -1)
    } else {
        label = '面板';
    }

    if (action == ActionEnum.UPDATE_STYLE) {
        if (snapshot.elementList != null) {
            action = action.replace('{element}', label).replace('{content}', snapshot.content);
        } else {
            action = action.replace('{element}', label).replace('{content}', snapshot.content);
        }
    } else if ([ActionEnum.REMOVE, ActionEnum.ADD, ActionEnum.RESIZE, ActionEnum.ROTATE, ActionEnum.MOVE].includes(action)) {
        action = action.replace('{element}', label);
    }

    snapshot.action = action;

    // 记录历史
    historyRecord.value = JSON.parse(JSON.stringify(snapshot));
    commit();
}

/**
 * 撤销
 */
function undoPanel() {
    if (!canUndo.value) {
        return;
    }
    undo();
    return historyRecord.value.elementList;
}

/**
 * 重做
 */
function redoPanel() {
    if (!canRedo.value) {
        return;
    }
    redo();
    return historyRecord.value.elementList;
}

export {
    init,
    record,
    canUndo,
    canRedo,
    undoStack,
    redoStack,
    undoPanel,
    redoPanel,
    redo,
    history,
    clear
};

使用示例


const elementList = ref<any[]>([{}])

// 记录日志
record(<Snapshot>{
    type: 'Element',
    action: ActionEnum.RESIZE,
    elementList: elementList.value
});

// 撤销
elementList.value = JSON.parse(JSON.stringify(undoPanel()));

// 重做
elementList.value = JSON.parse(JSON.stringify(redoPanel()));

代码仓库

代码仓库:github

代码仓库:gitee

实战项目:MyPrint

操作简单,组件丰富的一站式打印解决方案打印设计器

体验地址:前往

代码仓库:github

代码仓库:gitee

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2068250.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在定义的接口前加前缀路径

前因 在一个服务中&#xff0c;既定义了app端接口&#xff0c;又定义了pc端接口&#xff0c;为了方便区分&#xff0c;可以在项目里建立一个名为"app"、"pc"的文件夹&#xff0c;分别为app、pc提供接口。当app和pc接口一致时&#xff0c;写完一端接口后&a…

Axure设计之动态条形图教程(中继器)

在Axure RP中&#xff0c;中继器是一个非常强大的工具&#xff0c;它允许我们动态地展示数据&#xff0c;并且可以轻松实现复杂的交互效果。本文将详细介绍如何使用中继器来制作一个动态条形图&#xff0c;并展示其在实际项目中的应用。 一、效果预览 预览地址&#xff1a;http…

BC131 矩阵相等判定(c语言)

1.描述 :KiKi得到了两个n行m列的矩阵&#xff0c;他想知道两个矩阵是否相等&#xff0c;请你回答他。 (当两个矩阵对应数组元素都相等时两个矩阵相等)。 // //输入描述&#xff1a; //第一行包含两个整数n和m&#xff0c;表示两个矩阵包含n行m列&#xff0c;用空格分隔。 // //…

深刻理解JDK中线程池的使用

一、线程池状态 线程结构关系 ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位标识线程数量. 注意 : 第一位为符号位,所以RUNNING状态为负数,最小. 这些信息存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以用一次cas原子操作进行赋值. /…

如何从零编程实现《黑神话:悟空》

随着游戏行业的不断发展&#xff0c;越来越多的技术被应用于游戏的开发之中。其中&#xff0c;《黑神话&#xff1a;悟空》作为一款备受期待的动作冒险游戏&#xff0c;不仅以其精美的画面和丰富的剧情吸引了无数玩家的关注&#xff0c;还因其背后的技术实现了独特的游戏体验。…

怎么生成一个springboot的项目

这个很简单,只是想记录一下使用springboot的创建工具去创建项目 创建完成之后,删除一些不必要的东西 确认springboot的版本号,我这里要用2.4.0 刷新一下maven,等待下载完成就完成了

T6:好莱坞明星识别

文章目录 **T6周&#xff1a;好莱坞明星识别****一、前期工作**1.设置GPU&#xff08;用CPU可忽略该步骤&#xff09;2.导入数据3.查看数据 **二、数据预处理**1.加载数据2.可视化数据3.配置数据集 **三、构建CNN网络模型****四、编译模型****五、训练模型****六、模型评估****…

Circuitjs 分支电路(子电路, subcircuit)功能简介

在 circuitjs 中, 可以使用 分支电路 来实现自定义的"黑盒"器件. 分支电路 也称为 子电路(subcircuit). 因为菜单上已经叫成了 分支电路, 以下均称为 分支电路. 通过分支电路, 可以实现对电路的封装与抽象, 从而达到模块化并简化电路的目的. 更进一步的, 被抽象的黑盒…

Apache Paimon V0.9最新进展

摘要&#xff1a;本文整理自 Paimon PMC Chair 李劲松老师在 8 月 3 日 Streaming Lakehouse Meetup Online&#xff08;Paimon x StarRocks&#xff0c;共话实时湖仓架构&#xff09;上的分享。主要分享 Apache Paimon V0.9 的最新进展以及遇到的一些挑战。 一、Paimon&#x…

无人系统特刊合集(二)丨Springer特刊推荐

期刊推荐 期刊征稿&#xff1a;JOURNAL OF INTELLIGENT & ROBOTIC SYSTEMS Journal of Intelligent & Robotic Systems是一本同行评议的期刊&#xff0c;致力于智能系统和机器人技术的理论和实践。 专注于无人系统、机器人和自动化以及人机交互等领域。 在每期中都包…

天猫 登录滑块 淘系滑块分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关 前言 玩了几天现在才有空研究轨迹直接用了之前的…

日志组件导致的内存溢出问题分析

1、 内存溢出日志 普通的http请求&#xff0c;导致堆内存直接溢出&#xff0c;看了下代码实现非常简单的一次DB查询且数据量也比较小&#xff0c;不可能导致内存溢出呢 java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at…

网上花店设计+vue

TOC ssm017网上花店设计vue 绪论 1.1 选题背景 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。计算机软件可以针对不同行业的营业特点以及管理需求&a…

CSS3页面布局-三栏-中栏流动布局

三栏-中栏流动布局 用负外边距实现 实现三栏布局且中栏内容区不固定的核心问题就是处理右栏的定位&#xff0c; 并在中栏内容区大小改变时控制右栏与布局的关系。 控制两个外包装容器的外边距&#xff0c;一个包围三栏&#xff0c;一个包围左栏和中栏。 <!DOCTYPE html&…

计算机毕业设计 在线问诊系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

海运整箱成本与拼箱成本对比 | 国际贸易服务平台 | 箱讯科技

整箱和拼箱 在集装箱运输业务中&#xff0c;我们把一个集装箱、一个出口人、一个收货人、一个目的港&#xff0c;满足这“四个一”条件的货物叫做整箱货&#xff0c;而把一个集装箱、出口人、收货人和目的港这三项之中只要有一项是在两个或两个以上的出口运输货物&#xff0c;就…

批量将labelme的json文件转为png图片查看

文章目录 前提修改 l a b e l m e labelme labelme然后你就可以在这个环境下用代码批量修改了 前提 安装anaconda或者miniconda安装labelme 修改 l a b e l m e labelme labelme 查看labelme所处环境的路径&#xff1a;conda info --envs 比如我的是在py39_torch里面 修改la…

Anki自动生成语音

文章目录 前言安装插件制作音频一些注意事项语音消失现象不同端出现媒体文件丢失 参考文章 前言 已经实现了通过使用Obsidian实现Anki快速制卡。 对于语言学习&#xff0c;仅仅只有不同语言文字的对照是不够的&#xff0c;我们还需要声音。 所以就需要加入音频。 幸好 Anki…

laravel “Class \“Redis\“ not found“ 如何解决?

laravel “Class “Redis” not found” 如何解决 问题&#xff1a;laravel 安装好后&#xff0c;运行报错提示&#xff1a;“Class “Redis” not found” 分析&#xff1a;程序并没有用到redis&#xff0c;百度了一下&#xff0c;初步锁定可能是php环境的原因&#xff0c;运…

【数字ic自整资料】存储器及不同端口RAM对比

参考资料 【FPGA】zynq 单端口RAM 双端口RAM 读写冲突 写写冲突_双口ram-CSDN博客 华为海思数字芯片设计笔试第五套_10、下列不属于动志数组内建函数的是: a lengtho b. new c. delete() d-CSDN博客 目录 1、计算器典型存储体系结构 2、三种不同端口RAM &#xff08;1&…