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
},

相关文章:
Vue使用vue-esign实现在线签名 加入水印
Vue在线签名 一、目的二、样式三、代码1、依赖2、代码2.1 在线签名组件2.1.1 基础的2.1.2 携带时间水印的 2.2父组件 一、目的 又来了一个问题,直接让我在线签名(还不能存储base64),并且还得上传,我直接***违禁词。 好…...
与码无关:分数限制下,选好专业还是选好学校?
本文的目标读者:24届的高考生和家长。 写这篇非技术性文章,是因为我看到了24届考生和21年的我同样迷茫。 事先声明,本文带有强烈的个人思考色彩,可能会引起不适,如有不同观点,欢迎在评论区讨论。 一、前言…...
什么是负载均衡技术?
随着网络技术的快速发展,互联网行业也越来越广泛,人们的日常生活中也离不开网络技术,大量的用户进行浏览访问网站时,企业会使用负载均衡技术,降低当前网站的负载,以此来提高网站的访问速度。 今天小编就来给…...
存在重复元素Ⅱ python3
存在重复元素Ⅱ 问题描述解题思路代码实现复杂度 问题描述 给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] nums[j] 且 abs(i - j) < k 。如果存在,返回 true ;否则ÿ…...
【CV炼丹师勇闯力扣训练营 Day13:§6二叉树1】
CV炼丹师勇闯力扣训练营 代码随想录算法训练营第13天 二叉树的递归遍历 二叉树的迭代遍历、统一迭代 二叉树的层序遍历 一、二叉树的递归遍历(深度优先搜索) 【递归步骤】 1.确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理…...
代码随想录算法训练营第46天 [ 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 123.买卖股票的最佳时机III ]
代码随想录算法训练营第46天 [ 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 123.买卖股票的最佳时机III ] 一、121. 买卖股票的最佳时机 链接: 代码随想录. 思路:dp[i][0] 第i天持有股票的最大利润 dp[i][1] 第i天不持有股票的最大利润 做题状态:…...
基于IDEA的Maven简单工程创建及结构分析
目录 一、用 mvn 命令创建项目 二、用 IDEA 的方式来创建 Maven 项目。 (1)首先在 IDEA 下的 Maven 配置要已经确保完成。 (2)第二步去 new 一个 project (创建一个新工程) (3)…...
解锁空间数据奥秘:ArcGIS Pro与Python双剑合璧,处理表格数据、矢量数据、栅格数据、点云数据、GPS数据、多维数据以及遥感云平台数据等
ArcGISPro提供了用户友好的图形界面,适合初学者快速上手进行数据处理和分析。它拥有丰富的工具和功能,支持各种数据格式的处理和分析,适用于各种规模的数据处理任务。ArcGISPro在地理信息系统(GIS)领域拥有广泛的应用&…...
后端路线指导(4):后端春招秋招经验分享
后端春招&秋招经验分享 春招(暑期实习) /秋招是应届生非常重要的应聘时间,每一个想就业的同学一定要有所了解! 本篇内容,老白将与大家分享暑期实习和秋招如何应对招聘的个人经验,希望每个同学看完都能有所收获! 首先说明一下老白对于面试核心竞争力的…...
面完小红书算法岗,心态崩了。。。
暑期实习基本结束了,校招即将开启。 不同以往的是,当前职场环境已不再是那个双向奔赴时代了。求职者在变多,HC 在变少,岗位要求还更高了。提前准备才是完全之策。 最近,我们又陆续整理了很多大厂的面试题,…...
Android 断点续传进阶之多线程下载
今天继续下载的风骚走位内容—多线程多文件断点续传 Android 断点续传基础之单线程下载:http://blog.csdn.net/qq_27489007/article/details/53897653 效果图: 文件关系: 所需内容 多文件下载列表的显示 启动多个线程分段下载 使用通知栏…...
Python爬虫学习 | Scrapy框架详解
一.Scrapy框架简介 何为框架,就相当于一个封装了很多功能的结构体,它帮我们把主要的结构给搭建好了,我们只需往骨架里添加内容就行。scrapy框架是一个为了爬取网站数据,提取数据的框架,我们熟知爬虫总共有四大部分&am…...
用户态协议栈05—架构优化
优化部分 添加了in和out两个环形缓冲区,收到数据包后添加到in队列;经过消费者线程处理之后,将需要发送的数据包添加到out队列。添加数据包解析线程(消费者线程),架构分层 #include <rte_eal.h> #inc…...
模拟退火算法
模拟退火算法(Simulated Annealing, SA)是一种用于全局优化问题的概率搜索算法,其灵感来自于金属退火过程。在金属退火中,材料被加热到高温,然后缓慢冷却,以减少其晶格中的缺陷并达到最小能量状态。模拟退火…...
Java匿名类
Java 匿名类是一种特殊的内部类,它没有名字,并且通常用来简化代码实现,尤其是在实现接口或者抽象类的实例时。匿名类可以在实例化时定义其行为,而不需要创建单独的类文件。 匿名类的特点 没有名字:匿名类是没有名字的…...
G7易流赋能化工物流,实现安全、环保与效率的共赢
近日,中国物流与采购联合会在古都西安举办了备受瞩目的第七届化工物流安全环保发展论坛。以"坚守安全底线,追求绿色发展,智能规划化工物流未来"为主题,该论坛吸引了众多政府部门、行业专家和企业代表的参与。G7易流作为…...
y=sin(2x)
函数 \( y \sin(2x) \) 是一个正弦函数,其中 \( x \) 是自变量,\( y \) 是因变量。这个函数描述了一个周期性波动的波形,其特点是: 1. **振幅**:正弦函数的振幅是 1,这意味着波形在 \( 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站视频讲解 视频的论文笔记 从同一视角理解扩散模型【视频讲解笔记】 配合视频讲解的同步笔记。 整个系列完整的论文笔记内容如下,仅为了不用—一回复…...
docker 基本用法及跨平台使用
一、Docker的优点 docker 主要解决的问题就是程序开发过程中编译和部署中遇到的环境配置的问题。 1.1 Docker与其他虚拟机层次结构的区别** 运行程序重点关注点在于环境。 VM虚拟机是基于Hypervisor虚拟化服务运行的。 Docker是基于内核的虚拟化技术实现的。 1.2 Docker的技…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
python可视化:俄乌战争时间线关键节点与深层原因
俄乌战争时间线可视化分析:关键节点与深层原因 俄乌战争是21世纪欧洲最具影响力的地缘政治冲突之一,自2022年2月爆发以来已持续超过3年。 本文将通过Python可视化工具,系统分析这场战争的时间线、关键节点及其背后的深层原因,全面…...






