当前位置: 首页 > news >正文

Vue使用vue-esign实现在线签名 加入水印

Vue在线签名

  • 一、目的
  • 二、样式
  • 三、代码
    • 1、依赖
    • 2、代码
      • 2.1 在线签名组件
        • 2.1.1 基础的
        • 2.1.2 携带时间水印的
      • 2.2父组件

一、目的

又来了一个问题,直接让我在线签名(还不能存储base64),并且还得上传,我直接***违禁词。

好家伙又回来了,这次增加了一个加入时间水印的要求,我***。
在这里插入图片描述

二、样式

初始样式
在这里插入图片描述
点击前往组件(忽略写的什么样)
在这里插入图片描述
这里可以调节画笔,颜色什么的,也能进行预览,点击保存之后(
1、这里点击保存按钮我也走了一遍预览签名,不走的话这边直接保存了,签名图片还在上传,无法进行回显了;
2、也可以在保存的方法使用延迟调用setTimeout,但是怕无法把握这个时间,所以就用了方法1)
在这里插入图片描述在这里插入图片描述

三、代码

1、依赖

npm install vue-esign --save

2、代码

因为使用的jeecg框架,这里是按照框架进行写的,原生的其他版本,等有时间在更新一下,毕竟cv工程师。在这里插入图片描述
下面的生成图片逻辑和上一篇Vue中使用图片编辑器 tui-image-editor 实现在线编辑保存最后的base转换差不多都是一样的,这里也是使用了组件调用。

2.1 在线签名组件

2.1.1 基础的

在线编辑的组件,名称我这里是Esignature.vue

<template><j-modal:title="title":width="width":visible="visible"switchFullscreen:okButtonProps="{ class:{'jee-hidden': false} }"@ok="handleOk"okText="保存"@cancel="handleCancel"cancelText="关闭"><a-card :bordered="false"><a-col :span="24"><a-card :bordered="true" style="width: 100%;"><a-row><a-col :span="6"><a-form-model-item label="画笔粗细" :labelCol="labelCol" :wrapperCol="wrapperCol"><a-select style="width:100px;" v-model="lineWidth" placeholder="请选择"><a-radio :value="1">1</a-radio><a-radio :value="3">3</a-radio><a-radio :value="6">6</a-radio><a-radio :value="9">9</a-radio></a-select></a-form-model-item></a-col><a-col :span="6"><a-form-model-item label="画笔颜色" :labelCol="labelCol" :wrapperCol="wrapperCol"><!-- input颜色回显必须要六位的颜色值 --><a-input v-model="lineColor" type="color" placeholder="" placeholder-class="input-placeholder" /></a-form-model-item></a-col><a-col :span="6"><a-form-model-item label="画布背景" :labelCol="labelCol" :wrapperCol="wrapperCol"><a-input v-model="bgColor" type="color" placeholder="" placeholder-class="input-placeholder" /></a-form-model-item></a-col><a-col :span="6"><a-form-model-item label="是否裁剪" :labelCol="labelCol" :wrapperCol="wrapperCol"><j-switch v-model="isCrop" :options="[true,false]" ></j-switch></a-form-model-item></a-col><vue-esignstyle="border: 1px solid #808080;"ref="esignRef":width="canWidth":height="canHeight":isCrop="isCrop":lineWidth="lineWidth":lineColor="lineColor":bgColor.sync="bgColor":isClearBgColor="isClearBgColor" /><button @click="handleReset">清空画板</button><button @click="handleGenerate(false)">预览图片</button><div><img style="float:left;border: 1px solid #808080" :src="imgBase" alt=""></div></a-row></a-card></a-col></a-card></j-modal>
</template><script>import { getAction, httpAction } from '@api/manage'import VueEsign from 'vue-esign'export default {name: 'Esign',components: {VueEsign},data () {return {canWidth: 800,//画布宽度--是不会超出父元素的宽度的--设置也不行canHeight: 300,lineWidth: 3,//画笔粗细lineColor: '#000000',//画笔颜色bgColor: '#ffffff',//画布背景isCrop: false,//是否裁剪isClearBgColor: true,//是否清空背景色imgBase: '',//生成签名图片-base64imgUrl: '',//生成签名图片-base64labelCol: {xs: { span: 24 },sm: { span: 8 }},wrapperCol: {xs: { span: 24 },sm: { span: 16 }},title: '',width: 1000,visible: false,disableSubmit: false,}},methods: {//调用组件handleSign(){this.visible = truethis.$nextTick(()=>{// console.log("调用=========>"+this.$refs.esignRef)this.handleReset()})},//保存handleOk() {this.handleGenerate(true)// setTimeout(() =>{//   this.$emit('getSign',this.imgUrl);//   this.close()// },100); // 延迟0.1秒},//关闭close() {this.$emit('close')this.visible = false},//关闭按钮handleCancel() {this.close()},//重置handleReset () {////清空画布内容this.lineWidth = 3this.lineColor = '#000000'this.bgColor = '#ffffff'this.isCrop = falsethis.imgBase = ''this.$refs.esignRef.reset();},//生成图片handleGenerate (flag) {// console.log("生成图片=========>"+this.$refs.esignRef)this.$refs.esignRef.generate().then(res => {// console.log('base64地址', res)this.imgBase = resif (flag){//进行base64转换的操作,因为后台文件都会加上随机后缀,这里使用sign.png了const form = this.base64ChangePicForm(res,'sign.png')httpAction('/sys/common/upload', form, 'post').then((uploadRes) => {// console.log("============>"+JSON.stringify(uploadRes))if (uploadRes.success){this.imgUrl = uploadRes.messagethis.$emit('getSign',this.imgUrl);this.close()}})}}).catch(error=> {// console.log('错误:', error)this.$message.warning('请先签字!');})}, //转换图片base64ChangePicForm(base64String,fileName){const data = window.atob(base64String.split(",")[1]);const ia = new Uint8Array(data.length);for (let i = 0; i < data.length; i++) {ia[i] = data.charCodeAt(i);}const blob = new Blob([ia], { type: "image/png" }); // blob 文件const file = new File([blob], fileName, { type: blob.type });const form = new FormData();form.append("file", file);form.append("biz", 'web/sign');return form},}}
</script>
2.1.2 携带时间水印的
<template><j-modal:title="title":width="width":visible="visible"switchFullscreen:okButtonProps="{ class:{'jee-hidden': false} }"@ok="handleOk"okText="保存"@cancel="handleCancel"cancelText="关闭"><a-card :bordered="false"><a-col :span="24"><a-card :bordered="true" style="width: 100%;"><a-row><a-col :span="6"><a-form-model-item label="画笔粗细" :labelCol="labelCol" :wrapperCol="wrapperCol"><a-select style="width:100px;" v-model="lineWidth" placeholder="请选择"><a-radio :value="1">1</a-radio><a-radio :value="3">3</a-radio><a-radio :value="6">6</a-radio><a-radio :value="9">9</a-radio></a-select></a-form-model-item></a-col><a-col :span="6"><a-form-model-item label="画笔颜色" :labelCol="labelCol" :wrapperCol="wrapperCol"><!-- input颜色回显必须要六位的颜色值 --><a-input v-model="lineColor" type="color" placeholder="" placeholder-class="input-placeholder" /></a-form-model-item></a-col><a-col :span="6"><a-form-model-item label="画布背景" :labelCol="labelCol" :wrapperCol="wrapperCol"><a-input v-model="bgColor" type="color" placeholder="" placeholder-class="input-placeholder" /></a-form-model-item></a-col><a-col :span="6"><a-form-model-item label="是否裁剪" :labelCol="labelCol" :wrapperCol="wrapperCol"><j-switch v-model="isCrop" :options="[true,false]" ></j-switch></a-form-model-item></a-col><vue-esignstyle="border: 1px solid #808080;"ref="esignRef":width="canWidth":height="canHeight":isCrop="isCrop":lineWidth="lineWidth":lineColor="lineColor":bgColor.sync="bgColor":isClearBgColor="isClearBgColor" /><button @click="handleReset">清空签名</button><button @click="handleGenerate(false)">预览签名</button><div><img style="float:left;border: 1px solid #808080" :src="imgBase" alt=""></div></a-row></a-card></a-col></a-card></j-modal>
</template><script>import { getAction, httpAction } from '@api/manage'import VueEsign from 'vue-esign'import { formatDate } from '@/utils/dateNumber'export default {name: 'Esign',components: {VueEsign},data () {return {canWidth: 800,//画布宽度--是不会超出父元素的宽度的--设置也不行canHeight: 300,lineWidth: 3,//画笔粗细lineColor: '#000000',//画笔颜色bgColor: '#ffffff',//画布背景isCrop: false,//是否裁剪isClearBgColor: true,//是否清空背景色imgBase: '',//生成签名图片-base64imgUrl: '',//生成签名图片-base64labelCol: {xs: { span: 24 },sm: { span: 8 }},wrapperCol: {xs: { span: 24 },sm: { span: 16 }},title: '',width: 1000,visible: false,disableSubmit: false,}},methods: {//调用组件handleSign(){this.visible = truethis.$nextTick(()=>{// console.log("调用=========>"+this.$refs.esignRef)this.handleReset()})},//保存handleOk() {this.handleGenerate(true)// setTimeout(() =>{//   this.$emit('getSign',this.imgUrl);//   this.close()// },100); // 延迟0.1秒},//关闭close() {this.$emit('close')this.visible = false},//关闭按钮handleCancel() {this.close()},//重置handleReset () {////清空画布内容this.lineWidth = 3this.lineColor = '#000000'this.bgColor = '#ffffff'this.isCrop = falsethis.imgBase = ''this.$refs.esignRef.reset();},//生成图片handleGenerate (flag) {// console.log("生成图片=========>"+this.$refs.esignRef)this.$refs.esignRef.generate().then(res => {// console.log('base64地址', res)// this.imgBase = res//加入时间水印this.addWatermark(res,formatDate(new Date(),'yyyy-MM-dd hh:mm:ss')).then((rmarkRes)=>{this.imgBase = rmarkRes//判断是保存还是预览,保存上传,预览不上传if (flag){//进行base64转换的操作,因为后台文件都会加上随机后缀,这里使用sign.png了const form = this.base64ChangePicForm(rmarkRes,'sign.png')httpAction('/sys/common/upload', form, 'post').then((uploadRes) => {// console.log("============>"+JSON.stringify(uploadRes))if (uploadRes.success){this.imgUrl = uploadRes.messagethis.$emit('getSign',this.imgUrl);this.close()}else {this.$message.warning(uploadRes.message);}}).catch((error) => {this.$message.warning(error);})}})}).catch(error => {// console.log('错误:', error)this.$message.warning('请先签字!');})},//转换图片base64ChangePicForm(base64String,fileName){const data = window.atob(base64String.split(",")[1]);const ia = new Uint8Array(data.length);for (let i = 0; i < data.length; i++) {ia[i] = data.charCodeAt(i);}const blob = new Blob([ia], { type: "image/png" }); // blob 文件const file = new File([blob], fileName, { type: blob.type });const form = new FormData();form.append("file", file);form.append("biz", 'web/sign');return form},//加入水印addWatermark(base64String, watermarkText) {return new Promise((resolve, reject) => {const image = new Image();image.onload = () => {const canvas = document.createElement('canvas');const context = canvas.getContext('2d');canvas.width = image.width;canvas.height = image.height;// 绘制原始图片context.drawImage(image, 0, 0);// 添加水印context.font = '20px Arial';context.fillStyle = 'rgba(128,128,128,0.5)';context.textAlign = 'center';context.fillText(watermarkText, canvas.width / 2, canvas.height);// 将 Canvas 转换为 Base64 图片const watermarkedImage = canvas.toDataURL('image/png');resolve(watermarkedImage);};image.onerror = (error) => {reject(error);};image.src = base64String;});}}}
</script><style>
</style>

2.2父组件

这里就简单一写,反正都是差不多的,这里使用button按钮的userSign1方法进行调用在线签名组件,然后使用getSign1方法进行回调,将上传后的图片赋值给本页面的signFiles1进行显示。

<a-col :span="12" :style="formDisabled?(model.signFile1?'':'display: none;'):''"><a-form-model-item label="签字"  :labelCol="labelCol" :wrapperCol="wrapperCol" prop="signFiles1"><a-button  @click="userSign1" icon="edit">前往签字</a-button><esignature ref="signFormTo1" @getSign="getSign1"/><j-image-upload text="上传签字" bizPath="web/sign" v-model="signFiles1" :is-multiple="false" disabled/></a-form-model-item></a-col>

方法知己简单明了

//签名
userSign1(){this.$refs.signFormTo1.handleSign();
},getSign1(res) {this.signFiles1 = res
},

111

相关文章:

Vue使用vue-esign实现在线签名 加入水印

Vue在线签名 一、目的二、样式三、代码1、依赖2、代码2.1 在线签名组件2.1.1 基础的2.1.2 携带时间水印的 2.2父组件 一、目的 又来了一个问题&#xff0c;直接让我在线签名&#xff08;还不能存储base64&#xff09;&#xff0c;并且还得上传&#xff0c;我直接***违禁词。 好…...

与码无关:分数限制下,选好专业还是选好学校?

本文的目标读者&#xff1a;24届的高考生和家长。 写这篇非技术性文章&#xff0c;是因为我看到了24届考生和21年的我同样迷茫。 事先声明&#xff0c;本文带有强烈的个人思考色彩&#xff0c;可能会引起不适&#xff0c;如有不同观点&#xff0c;欢迎在评论区讨论。 一、前言…...

什么是负载均衡技术?

随着网络技术的快速发展&#xff0c;互联网行业也越来越广泛&#xff0c;人们的日常生活中也离不开网络技术&#xff0c;大量的用户进行浏览访问网站时&#xff0c;企业会使用负载均衡技术&#xff0c;降低当前网站的负载&#xff0c;以此来提高网站的访问速度。 今天小编就来给…...

存在重复元素Ⅱ python3

存在重复元素Ⅱ 问题描述解题思路代码实现复杂度 问题描述 给你一个整数数组 nums 和一个整数 k &#xff0c;判断数组中是否存在两个 不同的索引 i 和 j &#xff0c;满足 nums[i] nums[j] 且 abs(i - j) < k 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff…...

【CV炼丹师勇闯力扣训练营 Day13:§6二叉树1】

CV炼丹师勇闯力扣训练营 代码随想录算法训练营第13天 二叉树的递归遍历 二叉树的迭代遍历、统一迭代 二叉树的层序遍历 一、二叉树的递归遍历&#xff08;深度优先搜索&#xff09; 【递归步骤】 1.确定递归函数的参数和返回值&#xff1a;确定哪些参数是递归的过程中需要处理…...

代码随想录算法训练营第46天 [ 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 123.买卖股票的最佳时机III ]

代码随想录算法训练营第46天 [ 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 123.买卖股票的最佳时机III ] 一、121. 买卖股票的最佳时机 链接: 代码随想录. 思路&#xff1a;dp[i][0] 第i天持有股票的最大利润 dp[i][1] 第i天不持有股票的最大利润 做题状态&#xff1a;…...

基于IDEA的Maven简单工程创建及结构分析

目录 一、用 mvn 命令创建项目 二、用 IDEA 的方式来创建 Maven 项目。 &#xff08;1&#xff09;首先在 IDEA 下的 Maven 配置要已经确保完成。 &#xff08;2&#xff09;第二步去 new 一个 project &#xff08;创建一个新工程&#xff09; &#xff08;3&#xff09;…...

解锁空间数据奥秘:ArcGIS Pro与Python双剑合璧,处理表格数据、矢量数据、栅格数据、点云数据、GPS数据、多维数据以及遥感云平台数据等

ArcGISPro提供了用户友好的图形界面&#xff0c;适合初学者快速上手进行数据处理和分析。它拥有丰富的工具和功能&#xff0c;支持各种数据格式的处理和分析&#xff0c;适用于各种规模的数据处理任务。ArcGISPro在地理信息系统&#xff08;GIS&#xff09;领域拥有广泛的应用&…...

后端路线指导(4):后端春招秋招经验分享

后端春招&秋招经验分享 春招(暑期实习) /秋招是应届生非常重要的应聘时间,每一个想就业的同学一定要有所了解! 本篇内容&#xff0c;老白将与大家分享暑期实习和秋招如何应对招聘的个人经验&#xff0c;希望每个同学看完都能有所收获! 首先说明一下老白对于面试核心竞争力的…...

面完小红书算法岗,心态崩了。。。

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…...

Android 断点续传进阶之多线程下载

今天继续下载的风骚走位内容—多线程多文件断点续传 Android 断点续传基础之单线程下载&#xff1a;http://blog.csdn.net/qq_27489007/article/details/53897653 效果图&#xff1a; 文件关系&#xff1a; 所需内容 多文件下载列表的显示 启动多个线程分段下载 使用通知栏…...

Python爬虫学习 | Scrapy框架详解

一.Scrapy框架简介 何为框架&#xff0c;就相当于一个封装了很多功能的结构体&#xff0c;它帮我们把主要的结构给搭建好了&#xff0c;我们只需往骨架里添加内容就行。scrapy框架是一个为了爬取网站数据&#xff0c;提取数据的框架&#xff0c;我们熟知爬虫总共有四大部分&am…...

用户态协议栈05—架构优化

优化部分 添加了in和out两个环形缓冲区&#xff0c;收到数据包后添加到in队列&#xff1b;经过消费者线程处理之后&#xff0c;将需要发送的数据包添加到out队列。添加数据包解析线程&#xff08;消费者线程&#xff09;&#xff0c;架构分层 #include <rte_eal.h> #inc…...

模拟退火算法

模拟退火算法&#xff08;Simulated Annealing, SA&#xff09;是一种用于全局优化问题的概率搜索算法&#xff0c;其灵感来自于金属退火过程。在金属退火中&#xff0c;材料被加热到高温&#xff0c;然后缓慢冷却&#xff0c;以减少其晶格中的缺陷并达到最小能量状态。模拟退火…...

Java匿名类

Java 匿名类是一种特殊的内部类&#xff0c;它没有名字&#xff0c;并且通常用来简化代码实现&#xff0c;尤其是在实现接口或者抽象类的实例时。匿名类可以在实例化时定义其行为&#xff0c;而不需要创建单独的类文件。 匿名类的特点 没有名字&#xff1a;匿名类是没有名字的…...

G7易流赋能化工物流,实现安全、环保与效率的共赢

近日&#xff0c;中国物流与采购联合会在古都西安举办了备受瞩目的第七届化工物流安全环保发展论坛。以"坚守安全底线&#xff0c;追求绿色发展&#xff0c;智能规划化工物流未来"为主题&#xff0c;该论坛吸引了众多政府部门、行业专家和企业代表的参与。G7易流作为…...

y=sin(2x)

函数 \( y \sin(2x) \) 是一个正弦函数&#xff0c;其中 \( x \) 是自变量&#xff0c;\( y \) 是因变量。这个函数描述了一个周期性波动的波形&#xff0c;其特点是&#xff1a; 1. **振幅**&#xff1a;正弦函数的振幅是 1&#xff0c;这意味着波形在 \( y \) 轴上的最大值…...

快捷方式(lnk)--加载HTA-CS上线

免责声明:本文仅做技术交流与学习... 目录 CS: HTA文档 文件托管 借助mshta.exe突破 本地生成lnk快捷方式: 非系统图标路径不同问题: 关于lnk的上线问题: CS: HTA文档 配置监听器 有效载荷---->HTA文档--->选择监听器--->选择powershell模式----> 默认生成一…...

从同—视角理解扩散模型(Understanding Diffusion Models A Unified Perspective)

从同—视角理解扩散模型 Understanding Diffusion Models A Unified Perspective【全公式推导】【免费视频讲解】 B站视频讲解 视频的论文笔记 从同一视角理解扩散模型【视频讲解笔记】 配合视频讲解的同步笔记。 整个系列完整的论文笔记内容如下&#xff0c;仅为了不用—一回复…...

docker 基本用法及跨平台使用

一、Docker的优点 docker 主要解决的问题就是程序开发过程中编译和部署中遇到的环境配置的问题。 1.1 Docker与其他虚拟机层次结构的区别** 运行程序重点关注点在于环境。 VM虚拟机是基于Hypervisor虚拟化服务运行的。 Docker是基于内核的虚拟化技术实现的。 1.2 Docker的技…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...