uniapp H5实现签名

第一种:跳转签名页面

1、创建审核页面audit.vue

<template><view><uni-section title=""><view class="auditClass"><uni-forms :model="baseFormData" ref="baseFormRef" :rules="rules" label-position="top"labelWidth="80px"><uni-forms-item label="审核意见" required name="advice"><uni-easyinput type="textarea" v-model="baseFormData.advice" placeholder="请输入审核意见" /></uni-forms-item><image :src='baseFormData.signUrl.replace(/[\r\n]/g, "")' v-if="baseFormData.signUrl"></image><button type="primary" style="margin-bottom: 15px;" :disabled="isClick" @click="signBtn">签名</button><button type="primary" :disabled="isClick" @click="submit('baseFormRef')">提交</button></uni-forms></view></uni-section></view>
</template><script>import config from '@/config';import {getUserProfile} from "@/api/system/user"const baseUrl = config.baseUrl;export default {data() {return {loading: false,user: {},// 基础表单数据baseFormData: {signUrl: '',advice: '',},isClick: false,}},computed: {},onLoad(options) {this.baseFormData.proId = options.proId;},onReady() {this.$refs.baseFormRef.setRules(this.rules)},onShow() {uni.$on("imageUrl", (data) => {console.log("接收事件imageUrl成功,data=", data);this.baseFormData.signUrl = data.imageUrl;});},methods: {getUser() {getUserProfile().then(response => {this.user = response.datathis.getTableData();})},submit(ref) {let self = this;self.$refs[ref].validate().then(res => {console.log('success', res);if (self.baseFormData.signUrl == '' || self.baseFormData.signUrl == null) {uni.showToast({title: '请签名',icon: "error"});} else {self.isClick = true;uni.showLoading({title: '提交中'});uni.request({withCredentials: true,url: baseUrl + '',data: JSON.stringify(self.baseFormData),method: 'post',success: function(res) {console.log(res.data);self.isClick = false;uni.hideLoading();if (res.data.code == 0) {uni.showToast({title: res.data.msg,icon: "error"});} else {uni.navigateBack({delta: 1,success() {setTimeout(() => {uni.showToast({title: res.data.msg,icon: 'success'})}, 500)}});}}});}}).catch(err => {console.log('err', err);});},// 签名signBtn() {this.$tab.navigateTo('/pages/processAudit/sign');},}}
</script><style scoped>.auditClass {padding: 15px;background-color: #ffffff;}
</style>

2、创建签名页面sign.vue

<template><view><view class="main-content" @touchmove.stop.prevent=""><!-- 签字canvas --><canvas class="mycanvas" id="mycanvas" canvas-id="mycanvas" @touchstart="touchstart" @touchmove="touchmove"@touchend="touchend"></canvas><canvas class="mycanvas":style="{ 'z-index': -1, width: `${screenWidth}px`, height: `${(screenWidth * screenWidth) / screenHeight}px` }"id="rotatCanvas" canvas-id="rotatCanvas"></canvas><cover-view class="button-line"><cover-view class="save-button" @tap="finish">保存</cover-view><cover-view class="clear-button" @tap="clear">清空</cover-view></cover-view></view></view>
</template><script>export default {data() {return {ctx: '', //绘图图像points: [], //路径点集合screenWidth: '',screenHeight: '',img: '',isDraw: false,};},mounted() {this.createCanvas();uni.getSystemInfo({success: e => {this.screenWidth = e.screenWidth;this.screenHeight = e.screenHeight;}});},methods: {//创建并显示画布createCanvas() {this.showCanvas = true;this.ctx = uni.createCanvasContext('mycanvas', this);//设置画笔样式this.ctx.lineWidth = 2;this.ctx.lineCap = 'round';this.ctx.lineJoin = 'round';},//触摸开始,获取到起点touchstart(e) {this.isDraw = truelet startX = e.changedTouches[0].x;let startY = e.changedTouches[0].y;let startPoint = {X: startX,Y: startY};this.points.push(startPoint);//每次触摸开始,开启新的路径this.ctx.beginPath();},//触摸移动,获取到路径点touchmove(e) {let moveX = e.changedTouches[0].x;let moveY = e.changedTouches[0].y;let movePoint = {X: moveX,Y: moveY};this.points.push(movePoint); //存点let len = this.points.length;if (len >= 2) {this.draw(); //绘制路径}},// 触摸结束,将未绘制的点清空防止对后续路径产生干扰touchend() {this.points = [];},/* ***********************************************#   绘制笔迹#	1.为保证笔迹实时显示,必须在移动的同时绘制笔迹#	2.为保证笔迹连续,每次从路径集合中区两个点作为起点(moveTo)和终点(lineTo)#	3.将上一次的终点作为下一次绘制的起点(即清除第一个点)************************************************ */draw() {let point1 = this.points[0];let point2 = this.points[1];this.points.shift();this.ctx.moveTo(point1.X, point1.Y);this.ctx.lineTo(point2.X, point2.Y);this.ctx.stroke();this.ctx.draw(true);},//清空画布clear() {this.isDraw = falsethis.ctx.clearRect(0, 0, this.screenWidth, this.screenHeight);this.ctx.draw(true);},//完成绘画并保存到本地finish() {if (this.isDraw) {uni.canvasToTempFilePath({canvasId: 'mycanvas',fileType: 'png',quality: 1, //图片质量success: res => {console.log("sign" + res.tempFilePath);this.rotate(res.tempFilePath);},complete: com => {}});} else {uni.$emit("imageUrl", {imageUrl: ''});uni.navigateBack();}},rotate(tempFilePath) {let that = thiswx.getImageInfo({src: tempFilePath,success: (res1) => {let canvasContext = wx.createCanvasContext('rotatCanvas')let rate = res1.height / res1.widthlet width = 300 / ratelet height = 300canvasContext.translate(height / 2, width / 2)canvasContext.rotate((270 * Math.PI) / 180)canvasContext.drawImage(tempFilePath, -width / 2, -height / 2, width, height)canvasContext.draw(false, () => {wx.canvasToTempFilePath({canvasId: 'rotatCanvas',fileType: 'png',quality: 1, //图片质量success(res) {console.log("====="+JSON.stringify(res))uni.$emit("imageUrl", {imageUrl: res.tempFilePath,});uni.navigateBack();}})})}})},}};
</script>
<style lang="scss" scoped>.main-content {width: 100vw;height: 100vh;background-color: red;position: fixed;top: 0rpx;left: 0rpx;z-index: 9999;}.mycanvas {width: 100vw;height: 100vh;background-color: #fafafa;position: fixed;left: 0rpx;top: 0rpx;z-index: 2;}.button-line {transform: rotate(90deg);position: fixed;bottom: 170rpx;left: -100rpx;width: 340rpx;height: 80rpx;display: flex;align-items: center;justify-content: space-between;z-index: 999;font-size: 34rpx;font-weight: 600;}.save-button {color: #ffffff;width: 150rpx;height: 80rpx;text-align: center;line-height: 80rpx;border-radius: 10rpx;background-color: #007aff;}.clear-button {color: #ffffff;width: 150rpx;height: 80rpx;text-align: center;line-height: 80rpx;border-radius: 10rpx;background-color: #aaaaaa;}
</style>

3、签名页面效果展示

第二种:签名页面嵌入

1、创建审核页面audit.vue

<template><view><uni-section title=""><view class="auditClass"><uni-forms :model="baseFormData" ref="baseFormRef" :rules="rules" label-position="top"labelWidth="80px"><uni-forms-item label="审核意见" required name="advice"><uni-easyinput type="textarea" v-model="baseFormData.advice" placeholder="请输入审核意见" /></uni-forms-item><button type="primary" style="margin-bottom: 15px;" @click="signBtn">签名</button><sign v-if='signShow' @closeCanvas="closeCanvas" @change="change"></sign><button type="primary" @click="submit('baseFormRef')">提交</button></uni-forms></view></uni-section></view>
</template><script>import config from '@/config';import {getUserProfile} from "@/api/system/user"const baseUrl = config.baseUrl;import sign from './sign.vue'export default {components: {sign},data() {return {loading: false,user: {},// 基础表单数据baseFormData: {signUrl: '',advice: '',},signShow: false,}},computed: {},onLoad(options) {console.log("proId====" + options.proId)this.baseFormData.proId = options.proId;},onReady() {// 设置自定义表单校验规则,必须在节点渲染完毕后执行this.$refs.baseFormRef.setRules(this.rules)},methods: {submit(ref) {let self = this;self.$refs[ref].validate().then(res => {console.log('success', res);if (self.baseFormData.signUrl == '' || self.baseFormData.signUrl == null) {uni.showToast({title: '请签名',icon: "error"});} else {//获取表单数据并发送请求self.baseFormData.userId = self.user.userId;uni.request({withCredentials: true, // 加入这一行即可url: baseUrl + '/api/saveProcessAuditInfo',data: JSON.stringify(self.baseFormData),method: 'post',success: function(res) {console.log(res.data);if (res.data.code == 0) {uni.showToast({title: res.data.msg,icon: "error"});} else {uni.navigateBack({delta: 1,success() {setTimeout(() => {uni.showToast({title: res.data.msg,icon: 'success'})}, 500)}});}}});}}).catch(err => {console.log('err', err);});},// 签名signBtn() {this.signShow = true},// 关闭画布closeCanvas(e) {this.signShow = false},// 用户签名数据change(e) {console.log(e) //返回的base64地址this.baseFormData.signUrl = e;},}}
</script><style scoped>.auditClass {padding: 15px;background-color: #ffffff;}
</style>

2、创建签名页面sign.vue

<template><view><view class="box" :style="{height:height}"><view class="top"><canvas class="canvas-box" @touchstart='touchstart' @touchmove="touchmove" @touchend="touchend"canvas-id="myCanvas"></canvas><view>请在此处签名</view></view><view class=" bottom"><uni-tag text="完成" mode="dark" @click="finish" /><uni-tag text="重签" mode="dark" @click="clear" /><uni-tag text="取消" mode="dark" @click="close" /></view></view></view>
</template><script>export default {data() {return {ctx: '', //绘图图像points: [], //路径点集合height: '200px', //高度canvasShoww: false, //提示isDraw: false,}},created() {let that = this// uni.getSystemInfo({// 	success: function(res) {// 		that.height = res.windowHeight + 'px';// 	},// });},mounted() {this.createCanvas()},methods: {//创建并显示画布createCanvas() {this.ctx = uni.createCanvasContext('myCanvas', this); //创建绘图对象//设置画笔样式this.ctx.lineWidth = 4;this.ctx.lineCap = 'round';this.ctx.lineJoin = 'round';},// 触摸开始touchstart(e) {// 在签名前调用禁用滚动this.disableScroll();this.isDraw = truelet startX = e.changedTouches[0].x;let startY = e.changedTouches[0].y;let startPoint = {X: startX,Y: startY};this.points.push(startPoint);//每次触摸开始,开启新的路径this.ctx.beginPath();},// 移动touchmove(e) {let moveX = e.changedTouches[0].x;let moveY = e.changedTouches[0].y;let movePoint = {X: moveX,Y: moveY};this.points.push(movePoint); //存点let len = this.points.length;if (len >= 2) {this.draw(); //绘制路径}},// 触摸结束,将未绘制的点清空防止对后续路径产生干扰touchend(e) {// 在签名完成后调用启用滚动this.enableScroll();this.points = [];},//绘制笔迹draw() {let point1 = this.points[0];let point2 = this.points[1];this.points.shift();this.ctx.moveTo(point1.X, point1.Y);this.ctx.lineTo(point2.X, point2.Y);this.ctx.stroke();this.ctx.draw(true);},//清空画布clear() {let that = this;if (this.imageShow) {if (this.imageUrl) {this.imageUrl = '';this.imageShow = false;}} else {uni.getSystemInfo({success: function(res) {// 设置画笔样式let canvasw = res.windowWidth;let canvash = res.windowHeight;that.ctx.clearRect(0, 0, canvasw, canvash);that.ctx.draw(true);}});}this.createCanvas();},//关闭并清空画布close() {this.$emit('closeCanvas');this.createCanvas();this.clear();},//完成绘画并保存到本地finish() {if (this.isDraw) {let that = this;uni.canvasToTempFilePath({canvasId: 'myCanvas',success: function(res) {that.imageShow = true;that.imageUrl = res.tempFilePath;that.$emit('closeCanvas');that.$emit('change', res.tempFilePath);that.close();}},// this);return}this.$u.func.showToast({title: '请签名',})},// 禁用页面滚动disableScroll() {var box=function(e){passive: false ;};document.body.style.overflow='hidden';document.addEventListener("touchmove",box,false);},// 启用页面滚动enableScroll() {var box=function(e){passive: false };document.body.style.overflow=''; // 出现滚动条document.removeEventListener("touchmove",box,false);},}}
</script><style lang="scss" scoped>.box {width: 100%;margin-bottom: 50px;display: flex;flex-direction: column;background-color: #fff;.top {height: 95%;margin: 50rpx;border: 1px solid #000;position: relative;.canvas-box {width: 100%;height: 100%;}}.bottom {height: 5%;display: flex;align-items: flex-start;justify-content: space-around;}}
</style>

3、签名页面效果展示

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

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

相关文章

【Flink入门修炼】2-3 Flink Checkpoint 原理机制

如果让你来做一个有状态流式应用的故障恢复&#xff0c;你会如何来做呢&#xff1f; 单机和多机会遇到什么不同的问题&#xff1f; Flink Checkpoint 是做什么用的&#xff1f;原理是什么&#xff1f; 一、什么是 Checkpoint&#xff1f; Checkpoint 是对当前运行状态的完整记…

elementui el-date-picker禁止选择今年、今天、之前、时间范围限制18个月

1、禁止选择今年之前的所有年份 <el-date-pickerv-if"tabsActive 0":clearable"false"v-model"yearValue"change"yearTimeChange"type"year"placeholder"选择年"value-format"yyyy":picker-options…

03 OLED显示屏实现

文章目录 前言一、软件模拟IIC协议1.开启IIC协议2.结束IIC协议3.传输数据 二、OLED的操作1.传输数据的准备2.写入命令3.写入数据4.初始化函数5.设置光标6.显示字符7.显示字符串8.清屏9.显示汉字10.显示图片11.显示动图 三、完整代码总结 前言 这一章主要是上一节没有讲完的项目…

前端项目中使用插件prettier/jscodeshift/json-stringify-pretty-compact格式化代码或json数据

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、json代码格式化-选型二、json-stringify-pretty-compact简单试用三、prettier在前端使用四、查看prettier支持的语言和插件五、使用prettier格式化vue代码最终效果如图&#xff1a; ![在这里插入图片描述](https://im…

LLM应用实战:当KBQA集成LLM(二)

1. 背景 又两周过去了&#xff0c;本qiang~依然奋斗在上周提到的项目KBQA集成LLM&#xff0c;感兴趣的可通过传送门查阅先前的文章《LLM应用实战&#xff1a;当KBQA集成LLM》。 本次又有什么更新呢&#xff1f;主要是针对上次提到的缺点进行优化改进。主要包含如下方面&#…

多客圈子交友系统 uniapp+thinkphp6适配小程序/H5/app/api全开源,多款插件自选,支持个性定制!

网上交友的优点包括&#xff1a; 1. 方便&#xff1a;网上交友可以随时随地进行&#xff0c;不受时间和空间的限制&#xff0c;方便且高效。 2. 匿名性&#xff1a;网上交友可以实现匿名性&#xff0c;用户可以匿名地搜索、聊天或交换信息&#xff0c;保护个人隐私和安全。 3.…

COOIS 生产订单显示系统增强

需求说明&#xff1a;订单系统显示页面新增批量打印功能 增强点&#xff1a;CL_COIS_DISP_LIST_NAVIGATION -->TOOLBAR方法中新增隐式增强添加自定义打印按钮 增强点&#xff1a;BADI-->WORKORDER_INFOSYSTEM新增增强实施 实现位置&#xff1a;IF_EX_WORKORDER_INFOSYS…

制造型企业 如何实现便捷的机台文件统一管理?

机台文件统一管理&#xff0c;这是生产制造型企业都需要去做的&#xff0c;机台文件需要统一管理的原因主要包括以下几点&#xff1a; 1、提高效率&#xff1a;统一管理可以简化文件的访问和使用过程&#xff0c;提高工作效率&#xff0c;尤其是在需要频繁访问或更新机台文件的…

MySQL中什么情况下会出现索引失效?如何排查索引失效?

目录 1-引言&#xff1a;什么是MySQL的索引失效&#xff1f;(What、Why)1-1 索引失效定义1-2 为什么排查索引失效 2- 索引失效的原因及排查&#xff08;How&#xff09;2-1 索引失效的情况① 索引列参与计算② 对索引列进行函数操作③ 查询中使用了 OR 两边有范围查询 > 或 …

USB设备的音频类UAC

一、UAC简介 UAC&#xff08;USB Audio Class&#xff09;是USB设备的音频类&#xff0c;它定义了USB音频设备与主机计算机通信的方式。UAC标准是USB规范的一部分&#xff0c;并受到各种操作系统&#xff08;包括Windows、macOS和Linux&#xff09;的支持。 UAC是基于libusb,实…

图像在神经网络中的预处理与后处理的原理和作用(最详细版本)

1. 问题引出及内容介绍 相信大家在学习与图像任务相关的神经网络时&#xff0c;经常会见到这样一个预处理方式。 self.to_tensor_norm transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 具体原理及作用稍后解释&…

java8 Stream流常用方法(持续更新中...)

java8 Stream流常用方法 1.过滤数据中年龄大于等于十八的学生2.获取对象中其中的一个字段并添加到集合(以学生姓名&#xff08;name&#xff09;为例)3.获取对象中其中的一个字段并转为其他数据类型最后添加到集合(以学生性别&#xff08;sex&#xff09;为例&#xff0c;将Str…

Apache Doris 2.x 版本【保姆级】安装+使用教程

Doris简介 Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;也能支持高吞吐的复杂分析场景。基于…

【论文速读】|大语言模型(LLM)智能体可以自主利用1-day漏洞

本次分享论文&#xff1a; LLM Agents can Autonomously Exploit One-day Vulnerabilities 基本信息 原文作者&#xff1a;Richard Fang, Rohan Bindu, Akul Gupta, Daniel Kang 作者单位&#xff1a;无详细信息提供 关键词&#xff1a;大语言模型, 网络安全, 1-day漏洞, …

Redisson分布式锁 --- 源码分析

1.获取一把锁 RLock lock redissonClient.getLock("订单lock"); 2.业务代码加锁 lock.lock(); 2.1 lock.tryAcquire Long ttl tryAcquire(leaseTime, unit, threadId); 2.2 lua脚本: tryLockInnerAsync方法 如果获取锁失败&#xff0c;返回的结果是这个key的剩…

MMSeg搭建模型的坑

Input type(torch.suda.FloatTensor) and weight type (torch.FloatTensor) should be same 自己搭建模型的时候&#xff0c;经常会遇到二者不匹配&#xff0c;以这种情况为例&#xff0c;是因为部分模型没有加载到CUDA上面造成的。 注意搭建模型的时候&#xff0c;所有层都应…

【氮化镓】液态Ga在GaN(0001)和(0001̅)表面上的三维有序排列随温度的变化

文章标题是《Temperature dependence of liquid-gallium ordering on the surface of epitaxially grown GaN》&#xff0c;作者是Takuo Sasaki等人&#xff0c;发表在《Applied Physics Express》上。文章主要研究了在分子束外延(MBE)条件下&#xff0c;液态镓(Ga)在GaN(0001)…

WCH RISC CH32V303RCT6 单片机的SDI Printf 虚拟串口功能 类似RTT打印功能 简单分析

参考&#xff1a; 有关于 SDI printf 更多的信息和资料吗&#xff1f; 关于 CH32 系列 MCU SDI 虚拟串口功能的使用 【CH32X035 评估板测评】 教你使用 SDI 接口重定向 printf 0.前言 有段时间没有看CH32V单片机的开发了&#xff0c;今天帮新来的同事调试时候看到debug.c里面有…

java-spring 06 图灵 getBean方法和 doGetBean方法

01.一般的流程是&#xff0c;这里是从上一章的preInstantiateSingleton方法顺序过来的。 getBean() -> doGetBean() -> createBean() -> doCreateBean() -> createBeanInstance() -> populateBean() -> initializeBean() 02.getBean方法&#xff0c;一般就…

项目十:学会python爬虫数据保存(小白圆满级)

前言 上篇我们学会的文本文件、csv文件和excel文件的相关基础知识和操作&#xff0c;这一次我们再来了解一下四个文件操作方式 存储方法 HTML文件 将数据保存为HTML格式&#xff0c;可以直接在浏览器中查看。 使用字符串拼接将数据保存为HTML格式。 代码案例 # 创建数据…