一个很好用的vue2在线签名组件

news2025/4/15 18:05:08

在前端开发的日常工作中,我们常常会遇到需要用户进行在线签名的需求,比如电子合同签署、表单确认等场景。最近,我在项目里使用了一款极为好用的 Vue2 在线签名组件,今天就来和大家分享一下使用心得。

效果图

上代码 在 views 下将一个vue文件比如。sign.vue

<template>
    <div class="page">
            <el-button type="primary" @click="openSign()">签 名</el-button>
            <SignImgDialog v-if="signVisible" ref="SignImg" :lineWidth='3'  :isDefault='0' @close="closeDialog" />
    </div>
  </template>
  <script>
  import SignImgDialog from '@/components/SignImgDialog'
  export default {
    components: { SignImgDialog },
    props: {
      user: {
        type: Object,
        default: () => {
          return {}
        }
      }
    },
    data() {
      return {
        signVisible: false,
        signImg: ''
      }
    },

    methods: {

      closeDialog() {
        this.signVisible = false
      },
      openSign() {
        this.signVisible = true
        this.$nextTick(() => {
          this.$refs.SignImg.init()
        })
      },
     
    }
  }
  </script>
  <style lang="scss" scoped>
  .userInfo {
    height: 100%;
    overflow: hidden;
    >>> .el-tabs__nav-scroll {
      padding-top: 0 !important;
    }
  }
  .sign {
    padding: 20px 50px 0px 50px;
  }
  .add-sign {
    position: relative;
    height: 160px;
    background-color: rgb(247, 247, 247);
    border-radius: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    &.active {
      border: 1px solid #1890ff;
      box-shadow: 0 0 6px rgba(6, 58, 108, 0.26);
      color: #1890ff;
      .btn,
      .icon-checked {
        display: block;
      }
    }
    .add-button {
      position: absolute;
      display: none;
    }
    .add-icon {
      font-size: 50px;
      color: rgb(157, 158, 159);
    }
    .sign-img {
      width: 100%;
      height: 100%;
      border-radius: 10px;
    }
  }
  .add-sign:hover .add-button {
    display: flex;
    width: 100%;
    height: 100%;
    border-radius: 10px;
    background-color: rgba(157, 158, 159, 0.8);
  
    justify-content: center;
    align-items: center;
  }
  .icon-checked1 {
    display: block;
    width: 20px;
    height: 20px;
    border: 20px solid #1890ff;
    border-left: 20px solid transparent;
    border-top: 20px solid transparent;
    border-bottom-right-radius: 10px;
    position: absolute;
    transform: scale(0.8);
    right: -5px;
    bottom: -5px;
    i {
      position: absolute;
      top: -4px;
      left: -4px;
      font-size: 24px;
      color: #fff;
      transform: scale(0.8);
    }
  }
  .sign-item {
    margin-bottom: 20px;
  }
  </style>

然后再src下创建 components/SignImgDialog.vue
 

<template>
  <div>
    <el-dialog title="请签名" class="JNPF-dialog JNPF-dialog_center sign-dialog"
      :closeOnClickModal='false' :visible.sync="signVisible" append-to-body width="600px">
      <div class="sign-main-box">
        <vue-esign ref="esign" :height='300' :width="560" :lineWidth="lineWidth" />
        <div class="tip" v-show="showTip">请在此区域使用鼠标手写签名</div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="handleReset">清空</el-button>
        <el-button type="primary" :loading="loading" @click="handleGenerate()">确定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import { createSign } from '@/api/permission/userSetting'
import vueEsign from 'vue-esign'
export default {
  name: 'SignImgDialog',
  components: { vueEsign },
  props: {
    lineWidth: {
      required: true,
      type: Number
    },
    isDefault: {
      required: true,
      type: Number
    },
    userInfo: {
      type: Object,
      default() {
        return {}
      }
    },
    type: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      signVisible: false,
      loading: false,
      signImg: '',
      showTip: true
    }
  },
  methods: {
    init() {
      this.handleReset()
      this.signVisible = true
      this.showTip = true
      this.$nextTick(() => {
        this.$watch(
          () => {
            return this.$refs.esign.hasDrew
          },
          (val) => {
            this.showTip = !val
          }
        )
      })
    },
    handleReset() {
      this.signImg = ''
      this.$nextTick(() => {
        this.$refs.esign && this.$refs.esign.reset()
      })
    },
    handleGenerate() {
      this.loading = true
      this.$refs.esign.generate().then(res => {
        if (res) this.signImg = res
        if (this.type == 1) {
          this.signVisible = false
          this.loading = false
          return this.$emit('close', this.signImg)
        }
        let query = {
          signImg: this.signImg,
          isDefault: this.isDefault
        }
        createSign(query).then(res => {
          if (this.isDefault == 0) {
            this.$message({
              message: res.msg,
              type: 'success',
              duration: 1500
            })
            if (!this.userInfo.signImg) this.$store.commit('user/SET_USERINFO_SIGNIMG', this.signImg)
          }
          if (this.isDefault == 1) {
            this.$store.commit('user/SET_USERINFO_SIGNIMG', this.signImg)
          }
          this.signVisible = false
          this.loading = false
          this.$emit('close', this.signImg)
          this.handleReset()
        }).catch(err => {
          this.signVisible = false
          this.loading = false
          this.$emit('close')
          this.handleReset()
        })
      }).catch(err => {
        this.loading = false
        this.$message.warning("请签名")
      })
    },
  }
}
</script>
<style lang="scss" scoped>
.sign-dialog {
  >>> .el-dialog__body {
    overflow: hidden;
    height: 320px;
    overflow: auto;
    overflow-x: hidden;
    padding: 23px 14px 2px !important;
  }
}

.sign-main-box {
  border: 1px solid rgb(224, 238, 238);
  width: 100%;
  height: 300px;
  background-color: rgb(247, 247, 247);
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: -10px;
  margin-bottom: -10px;
  position: relative;
  .tip {
    height: 300px;
    line-height: 300px;
    text-align: center;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    color: #9d9d9f;
    font-size: 16px;
    pointer-events: none;
  }
}
</style>

是基于 vue-esign 实现的记得 npm install
ok 可以运行尝试了。
这款 Vue2 在线签名组件在功能、易用性和兼容性等方面都表现出色,为我们的项目开发带来了极大的便利。在未来的工作中,随着业务需求的不断拓展,我们也期待该组件能够持续更新和完善,比如增加更多的签名特效、支持多人签名等功能。同时,我也希望将这款好用的组件推荐给更多的前端开发者,让大家在遇到类似需求时能够少走弯路,共同提升前端开发的效率和质量。

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

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

相关文章

【STM32】STemWin库,使用template API

目录 CubeMX配置 工程文件配置 Keil配置 STemwin配置 GUIConf.c LCDConf.c 打点函数 修改屏幕分辨率 GUI_X.c 主函数 添加区域填充函数 移植过程中需要一些参考手册&#xff0c;如下 STemwin使用指南 emWin User Guide & Reference Manual CubeMX配置 参考驱…

Matlab Add Legend To Graph-图例添加到图

Add Legeng To Graph: Matlab的legend&#xff08;&#xff09;函数-图例添加到图 将图例添加到图 ,图例是标记绘制在图上的数据序列的有用方法。 下列示例说明如何创建图例并进行一些常见修改&#xff0c;例如更改位置、设置字体大小以及添加标题。您还可以创建具有多列的图…

2025年七星棋牌跨平台完整源码解析(200+地方子游戏+APP+H5+小程序支持,附服务器镜像导入思路)

目前市面上成熟的棋牌游戏源码很多&#xff0c;但能做到平台全覆盖、地方玩法丰富、交付方式标准化的系统却不多。今天这套七星棋牌2023完整源码具备安卓/iOS/H5/微信小程序端四端互通能力&#xff0c;附带200多款地方子游戏&#xff0c;还配备了后台管理与自动热更系统&#x…

Go语言--语法基础4--基本数据类型--整数类型

整型是所有编程语言里最基础的数据类型。 Go 语言支持如下所示的这些整型类型。 需要注意的是&#xff0c; int 和 int32 在 Go 语言里被认为是两种不同的类型&#xff0c;编译器也不会帮你自动做类型转换&#xff0c; 比如以下的例子会有编译错误&#xff1a; var value2 in…

智慧乡村数字化农业全产业链服务平台建设方案PPT(99页)

1. 农业全产业链概念 农业全产业链是依托数字化、电子商务、云计算等技术&#xff0c;整合规划咨询、应用软件设计与开发等服务&#xff0c;推动农业产业升级和价值重塑&#xff0c;构建IT产业融合新生态。 2. 产业链技术支撑 利用云计算、大数据、区块链等技术&#xff0c;为…

信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(二)

个人笔记整理---仅供参考 第二章信息技术发展 2.1信息技术及其发展 2.1.1计算机软硬件 2.1.2计算机网络 2.1.3存储和数据库 2.1.4信息安全 公钥公开&#xff0c;私钥保密 2.1.5信息技术的发展 2.2新一代信息技术及应用 2.2.1物联网 2.2.2云计算 2.2.3大数据 2.2.4区块链 2.2.5…

基于Springboot+Mysql的闲一品(含LW+PPT+源码+系统演示视频+安装说明)

系统功能 管理员功能&#xff1a;首页、个人中心、用户管理、零食分类管理、零食信息管理、订单评价管理、系统管理、订单管理。用户功能&#xff1a;首页、个人中心、订单评价管理、我的收藏管理、订单管理。前台首页功能&#xff1a;首页、零食信息、零食资讯、个人中心、后…

stm32week11

stm32学习 八.stm32基础 2.stm32内核和芯片 F1系统架构&#xff1a;4个主动单元和4个被动单元 AHB是内核高性能总线&#xff0c;APB是外围总线 总线矩阵将总线和各个主动被动单元连到一起 ICode总线直接连接Flash接口&#xff0c;不需要经过总线矩阵 AHB&#xff1a;72MHz&am…

从三次方程到复平面:复数概念的奇妙演进(二)

注&#xff1a;本文为 “复数 | 历史 / 演进” 相关文章合辑。 因 csdn 篇幅限制分篇连载&#xff0c;此为第二篇。 生料&#xff0c;不同的文章不同的点。 机翻&#xff0c;未校。 History of Complex Numbers 复数的历史 The problem of complex numbers dates back to …

基于视觉语言模型的机器人实时探索系统!ClipRover:移动机器人零样本视觉语言探索和目标发现

作者&#xff1a;Yuxuan Zhang 1 ^{1} 1, Adnan Abdullah 2 ^{2} 2, Sanjeev J. Koppal 3 ^{3} 3, and Md Jahidul Islam 4 ^{4} 4单位&#xff1a; 2 , 4 ^{2,4} 2,4佛罗里达大学电气与计算机工程系RoboPI实验室&#xff0c; 1 , 3 ^{1,3} 1,3佛罗里达大学电气与计算机工程系F…

LabVIEW往复式压缩机管路故障诊断系统

往复式压缩机作为工业领域的关键设备&#xff0c;广泛应用于石油化工、能源等行业&#xff0c;承担着气体压缩的重要任务。然而&#xff0c;其管路故障频发&#xff0c;不仅降低设备性能、造成能源浪费&#xff0c;还可能引发严重安全事故。因此&#xff0c;开发精准高效的管路…

springboot 项目 jmeter简单测试流程

测试内容为 主机地址随机数 package com.hainiu.example;import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotat…

算法思想之位运算(二)

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;算法思想之位运算(二) 发布时间&#xff1a;2025.4.13 隶属专栏&#xff1a;算法 目录 滑动窗口算法介绍六大基础位运算符常用模板总结 例题判定字符是否唯一题目链接题目描述算法思路代码实现 汉明距离题目链接题目…

【计网】网络交换技术之报文交换(复习自用,了解,重要3)

复习自用的&#xff0c;处理得比较草率&#xff0c;复习的同学或者想看基础的同学可以看看&#xff0c;大佬的话可以不用浪费时间在我的水文上了 另外两种交换技术可以直接点击链接访问相关笔记&#xff1a; 电路交换 分组交换 一、报文交换的定义 报文交换&#xff08;Me…

【动态规划】深入动态规划:背包问题

文章目录 前言01背包例题一、01背包二、分割等和子集三、目标和四、最后一块石头的重量|| 完全背包例题一、完全背包二、 零钱兑换三、零钱兑换||四、完全平方数 前言 什么是背包问题&#xff0c;怎么解决算法中的背包问题呢&#xff1f; 背包问题 (Knapsack problem) 是⼀种组…

BUUCTF-web刷题篇(25)

34.the mystery of ip 给出链接&#xff0c;输入得到首页&#xff1a; 有三个按钮&#xff0c;flag点击后发现页面窃取客户端的IP地址&#xff0c;通过给出的github代码中的php文件发现可以通过XFF或Client-IP传入值。使用hackbar或BP 使用XSS&#xff0c;通过github给出的目录…

StringBuilder类基本使用

文章目录 1. 基本介绍2. StringBuilder常用方法3. String、StringBuffer 和 StringBuilder 的比较4. String、StringBuffer 和 StringBuilder 的效率测试5. String、StringBuffer 和 StringBuilder 的选择 1. 基本介绍 一个可变的字符序列。此类提供一个与StringBuffer兼容的A…

设计模式 --- 访问者模式

访问者模式是一种行为设计模式&#xff0c;它允许在不改变对象结构的前提下&#xff0c;定义作用于这些对象元素的新操作。 优点&#xff1a; 1.​​符合开闭原则&#xff1a;新增操作只需添加新的访问者类&#xff0c;无需修改现有对象结构。 ​​2.操作逻辑集中管理​​&am…

HashTable,HashMap,ConcurrentHashMap之间的区别

文章目录 线程安全方面性能方面总结 线程安全方面 HashMap线程不安全&#xff0c;HashMap的方法没有进行同步&#xff0c;多个线程同时访问HashMap&#xff0c;并至少有一个线程修改了其内容&#xff0c;则必须手动同步。 HashTable是线程安全的&#xff0c;在HashMap的基础上…

LeetCode.225. 用队列实现栈

用队列实现栈 题目解题思路1. push2. pop3. empty CodeQueue.hQueue.cStack.c 题目 225. 用队列实现栈 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现…