The WeChat applet realizes functions such as scanning and scanning the QR code on the WeChat APP to jump to the corresponding result page and signature of the applet

Recently, I made a small program project. This small program is used with the APP side. The general demand is like this. Users can scan the QR code on the APP by scanning WeChat or scanning the small program homepage. Scan the code for identification. After the identification is successful, jump to the page for rendering, and then the user can sign, edit and submit it. The main technical difficulties in the front-end of this small program include generating dynamic forms based on the interface data returned by the back-end, signing functions, obtaining the nickname of the user’s WeChat avatar and detecting whether there is repeated authorization, scanning the QR code on the WeChat APP to jump to the corresponding small program The results page goes up.

Small program home page:
insert image description here
What should be noted here is that we need to obtain the avatar, nickname, and mobile phone number of the WeChat user, and the avatar and nickname are used as one step, the first authorization, and the authorization number as the second step, the second authorization, and then we To know whether the authorization has been authorized by calling the interface:

 //index.wxml
 <!--index.wxml-->


<view class="Box" >
  
  <!-- 扫描logo -->
  <image class="logo" src="/images/Scancode.png"/>
  
  
  <button type="primary" class="scanButton" bindtap="getUserProfile">扫一扫</button>
  
  

</view>

  <!-- 授权获取手机号弹窗 -->
 
  <view class="mask" wx:if="{
    
    {isShowModal}}">
	  <view class="content">
		  <text class="title">绑定手机号</text>
		  <text class="explain">请先绑定手机号再进行此操作</text>
		  <view class="close" catchtap="close">x</view>
		  <button type="primary" class="clickButton"  open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">微信用户一键绑定</button>
	  </view>

  </view>
  
  // index.js
var util = require('../../utils/util.js')
import api from '../../utils/api.js';
const app = getApp()
Page({
    
    
	/**
	 * 页面的初始数据
	 */
	data: {
    
    
		isbutton: false, //是否授权了获取了头像昵称手机号,只有全部授权了才为true,默认没授权为false
		code: '',
		isShowModal: false
	},

	/**
	 * 生命周期函数--监听页面加载
	 */
	onLoad: function(options) {
    
    
		// console.log("onLoad")
		let that = this




		wx.login({
    
    
			success: (res) => {
    
    
				that.data.code = res.code;
			
			},
		})





		//当签完字后跳回到首页的时候 这个时候会跳到携带的有hasLogin参数为true 此时肯定是已经授权了的 isbutton为true
		if (options.hasLogin) {
    
    
			//已经授权过 
			that.setData({
    
    
				isbutton: true
			})
		}


	},

	onShow: function() {
    
    

		let that = this;

		wx.login({
    
    
			success: async(res) => {
    
    
			
				//获取openid
				await util.getOpenId(res.code)
				// console.log("1231",wx.getStorageSync('openid'))
				//检测是否授权过
				api.getSetting({
    
    
					"openId": wx.getStorageSync('openid')
				}).then(res => {
    
    
					// console.log("检测是否授权过", res)
					
					if(res.data.code == 0){
    
    
						
						if(res.data.result){
    
    
							//授权过
							that.setData({
    
    
								isbutton: true
							})
							
						}else{
    
    
							//没授权过
						
							that.setData({
    
    
								isbutton: false
							})
						}
					}
				})
				
				
			},
		})

		
//显示"转发给朋友"
	wx.showShareMenu({
    
    
	  withShareTicket: true,
	    menus: ['shareAppMessage', ],
	  success: function(res) {
    
    
	  },
	  fail: function(res) {
    
    
	  }
	})
	
	},

	onLaunch: function() {
    
    
		// console.log("onLaunch")	
	},



	 getUserProfile() {
    
    
		
		//检测判断是否已经授权了
		if (!this.data.isbutton) {
    
    
			//没授权
			wx.getUserProfile({
    
    
				desc: '用于获取头像昵称',
				success: (res) => {
    
    
					// console.log("获取用户信息", res)

					//将用户信息存储到全局变量里
					app.globalData.userInfo = res.userInfo
					app.globalData.userInfo.avatarUrl = res.userInfo.avatarUrl

					this.setData({
    
    
						isShowModal: true
					})

					//将用户的头像昵称保存到数据库
					// let params = {
    
    
					// 	openId:wx.getStorageSync('openid'),
					// 	phone:'',
					// 	nickName:res.userInfo.nickName,
					// 	photo:res.userInfo.avatarUrl,
					// 	sex:res.userInfo.gender
					// }
					// api.SaveWechatInfo(params).then(res=>{
    
    
					// 	// console.log("保存用户头像昵称",res)
					// })

				}
			})
		} else {
    
    

			//已经授权了 直接开始扫一扫

			this.scanCode()
		}




	},
	//用户授权后获取其手机号
	async getPhoneNumber(e) {
    
    
		// console.log('e.detail', e.detail)
		let that = this



		//判断用户是选择拒绝还是允许
		if (e.detail.errMsg != 'getPhoneNumber:ok') {
    
    
			//拒绝
			that.setData({
    
    
				isbutton: false,
			})
			return false
		} else {
    
    
			//允许
			that.setData({
    
    
				isbutton: true,
			})

			await util.getPhoneNumberToLogin(e, that.data.isbutton, that.data.code);

			//授权绑定成功 自动关闭弹窗

			that.setData({
    
    
				isShowModal: false
			})

			wx.showToast({
    
    
				title: '绑定成功',
				icon: 'success',
				duration: 3000
			})

			that.setData({
    
    
				isbutton: true,
			})



		}




	},

	//用户手动点击右上角关闭弹窗 相当于没有同意授权获取手机号
	close() {
    
    
		this.setData({
    
    
			isShowModal: false,
			isbutton: false
		})
	},

	//授权了头像昵称手机号后开始扫描
	scanCode() {
    
    
		wx.scanCode({
    
    
			success(res) {
    
    
				console.log("扫描", res)
               let result = res.result;
			
			   
			    wx.navigateTo({
    
    
			   		url: '/pages/temporary/temporary?result='+encodeURIComponent(result) 
			   	})
			}
		})

	},
	
	
})


When the user scans the code successfully, it will jump to a temporary page temporary.wxml. This page is used as a transit page, mainly for the purpose that when the user scans in from WeChat, it is also advanced from this transit page to the result page, which is equivalent to going from WeChat to the result page. When you scan in, you don't need to go to the scan of the applet to authorize or something. Here I make a distinction in onLoad of js, and get the original content of the QR code through decodeURIComponent(), the code is as follows:

//temporary.wxml
<view class="temporary">
</view>

//temporary.js
// pages/temporary/temporary.js
import api from '../../utils/api.js';
Page({
    
    

	/**
	 * 页面的初始数据
	 */
	data: {
    
    

	},

	/**
	 * 生命周期函数--监听页面加载
	 */
	onLoad(query) {
    
    
		console.log("接收", query)



		wx.showLoading({
    
    
			title: '加载中',
		})






		let string;

		if (query.hasOwnProperty('result')) {
    
    
			//从小程序内的扫一扫跳转过来的
			string = decodeURIComponent(query.result)
			console.log("sadas", decodeURIComponent(query.result))
		} else if (query.hasOwnProperty('q')) {
    
    
			//从微信扫一扫跳转过来的
			string = decodeURIComponent(query.q) //获取到二维码原始链接内容
			console.log("外面微信扫码获得的", string)
			// const scancode_time = parseInt(query.scancode_time) // 获取用户扫码时间 UNIX 时间戳
		}


		// console.log("route", this.getQueryVariable(string, "route"))
		// console.log("tkId", this.getQueryVariable(string, "tkId"))
		// console.log("batchNo", this.getQueryVariable(string, "batchNo"))
		// console.log("randomCode", this.getQueryVariable(string, "randomCode"))
		// console.log("formId", this.getQueryVariable(string, "formId"))



		try {
    
    
			var val = wx.getStorageSync('openid')
			let route = this.getQueryVariable(string, "route")
			let params = {
    
    
				route: route,
				tkId: this.getQueryVariable(string, "tkId"),
				batchNo: this.getQueryVariable(string, "batchNo"),
				randomCode: this.getQueryVariable(string, "randomCode"),
				formId: this.getQueryVariable(string, "formId") ? this.getQueryVariable(string, "formId") :
					''

			}

			if (val) {
    
    
				// 本地有openid
				params.openId = val
				let that = this

				//检测用户之前是否授权过
				api.getSetting({
    
    
					"openId": val
				}).then(res => {
    
    
					console.log("检测是否授权过", res.data)
					wx.hideLoading()
					if (res.data.code == 0) {
    
    

						if (res.data.result) {
    
    
							//授权过
							api.getCodeContent(params).then(res => {
    
    
								console.log("二维码业务内容成功获取", res)
								if (res.data.code == 1) {
    
    
									//二维码失效
									wx.showModal({
    
    
										content: `${
      
      res.data.message}`,
										showCancel: false,
										confirmText: '我知道了',
										success(res) {
    
    
											if (res.confirm) {
    
    
												//    console.log('用户点击确定')
												that.goHome()
											} else if (res.cancel) {
    
    
												//    console.log('用户点击取消授权')

											}
										}
									})
								} else if (res.data.code == 0) {
    
    

									if (route == 'activeTask') {
    
    
										//激活任务
										let myParams = {
    
    
											...params,

										}

										if (myParams.hasOwnProperty('formId')) {
    
    
											delete myParams.formId
										}


										api.saveScanResult(myParams).then(response => {
    
    
											console.log("激活", response)
											
											if(response.data.code == 0){
    
    
												wx.showModal({
    
    
													title: '提示',
													content: `${
      
      response.data.result}`,
												
													showCancel: false,
													confirmText: '我知道了'
												}).then(res => {
    
    
													that.goHome()
												}).catch(err => {
    
    
												
												})
											}else{
    
    
												
												wx.showModal({
    
    
													title: '提示',
													content: `${
      
      response.data.message}`,
												
													showCancel: false,
													confirmText: '我知道了'
												}).then(res => {
    
    
													that.goHome()
												}).catch(err => {
    
    
												
												})
												
											}

											


										}).catch(error => {
    
    
											wx.showModal({
    
    
												title: '服务器内部出错',
												content: '请稍后重试',
												showCancel: false,
												confirmText: '我知道了'
											})
										})



									} else if (route == 'submitForm') {
    
    
										//表单提交

										// formType为1新表单 通用表单 
										if (res.data.result.formType == 1) {
    
    


											wx.navigateTo({
    
    
												url: '/pages/check/check_detail'
											})

											wx.setStorageSync('FormDetailTitle',
												res.data.result.title)

											//将场所登记表缓存在本地
											wx.setStorageSync('placeFormJson', res.data
												.result
												.formJson)


											wx.setStorageSync('taskItemResultId', JSON
												.stringify(res.data.result
													.taskItemResultId)
											)
											//将从业人员列表缓存到本地
											wx.setStorageSync('personFormJson', JSON
												.stringify(
													res.data.result.personFormJson))



										} else if (res.data.result.formType == 3) {
    
    
											//formType为3旧表单 自定义表单
											wx.navigateTo({
    
    
												url: '/pages/check/check_detail_old'
											})

											wx.setStorageSync('oldFormDetailTitle',
												res.data.result.title)

											wx.setStorageSync('oldFormJson',
												res.data.result.formJson)
											wx.setStorageSync('taskItemResultId', JSON
												.stringify(res.data.result
													.taskItemResultId)
											)
											
											wx.setStorageSync('oldResultId',JSON
													.stringify(res.data.result
														.taskItemResultId))
											
											
											
										}
										wx.setStorageSync('params', JSON.stringify(params))
										wx.setStorageSync('taskItemId', res.data.result
											.taskItemId)

										//通过isFill判断表单是否填写过 isFill为0是未填写
										wx.setStorageSync('isFill', JSON.stringify(res.data
											.result.isFill))

									}

								} else if (res.data.code == 401) {
    
    
									wx.showModal({
    
    
										title: '服务器内部出错',
										showCancel: false,
										content: '请稍后重试',
										confirmText: '我知道了'
									})
								}

							}).catch(err => {
    
    
								console.log("二维码业务内容获取失败", err)
							})

						} else {
    
    
							wx.hideLoading()
							//没授权过就提醒其授权
							that.isAuthorize()

						}
					}
				})



			} else {
    
    
				//本地拿不到openId 提醒需要授权
				that.isAuthorize()

			}







		} catch (e) {
    
    

		}



	},

	//提醒用户授权
	isAuthorize() {
    
    
		let that = this;
		wx.showModal({
    
    

			content: '您还没有授权',
			showCancel: false,
			confirmText: '我知道了',
			success(res) {
    
    
				if (res.confirm) {
    
    
					// console.log('用户点击确定')
					that.goHome()

				} else if (res.cancel) {
    
    
					//    console.log('用户点击取消授权')





				}
			}
		})
	},

	//跳回首页
	goHome() {
    
    
		wx.hideLoading()
		wx.showLoading({
    
    
			title: '正在跳回首页...'
		})
		//跳回首页
		setTimeout(() => {
    
    
			wx.redirectTo({
    
    
				url: '/pages/index/index',
				success: function() {
    
    

				},
				fail: function() {
    
    

				},
				complete: function() {
    
    
					wx.hideLoading()
				}
			})

		}, 2000)

	},


	//获取url参数的方法
	getQueryVariable(url, variable)

	{
    
    

		var query = url.substr(url.indexOf('?') + 1);
		// console.log("query",query)

		var vars = query.split("&");

		for (var i = 0; i < vars.length; i++) {
    
    

			var pair = vars[i].split("=");

			if (pair[0] == variable) {
    
    
				return pair[1];
			}

		}

		return (false);

	},

	/**
	 * 生命周期函数--监听页面初次渲染完成
	 */
	onReady() {
    
    

	},

	/**
	 * 生命周期函数--监听页面显示
	 */
	onShow() {
    
    

	},

	/**
	 * 生命周期函数--监听页面隐藏
	 */
	onHide() {
    
    

	},

	/**
	 * 生命周期函数--监听页面卸载
	 */
	onUnload() {
    
    
		wx.hideLoading()
	},

	/**
	 * 页面相关事件处理函数--监听用户下拉动作
	 */
	onPullDownRefresh() {
    
    

	},

	/**
	 * 页面上拉触底事件的处理函数
	 */
	onReachBottom() {
    
    

	},

	/**
	 * 用户点击右上角分享
	 */
	onShareAppMessage() {
    
    

	}
})


It is not enough for us to finish writing these logics. To realize the scanning on the WeChat APP to jump to a certain page on the applet, we also need to configure it in the background of the WeChat public platform, and find the background development management - development settings - scan Ordinary link QR code to open the applet:
insert image description here
Our online environment is port 8443, and then we configure it as follows:
insert image description hereinsert image description here
Then the first pages page in our app.json is still pages/index/index, and we are submitting the code for review The page modification path must be changed to pages/temporary/temporary, so that we can ensure that if our users search into the applet normally, it will be the home page index.wxml, and if they scan into the applet with WeChat, it will be a temporary page. wxml

insert image description here
insert image description here

The applet signature page mainly uses canvas, and there are two buttons on the page: re-sign and finish:

//sign.wxml
<canvas canvas-id="firstCanvas" id='firstCanvas' bindtouchstart="bindtouchstart" bindtouchmove="bindtouchmove"></canvas>


<view class="bottomBox">

	<view class="clear"  bindtap='clear'>重新签名</view>
	<view class="save {
    
    {sureBtn?'sureBet':''}}" bindtap='export'>完成</view>
</view>

//sign.js
//index.js
//获取应用实例
const app = getApp()

Page({
    
    
	data: {
    
    
		context: null,
		index: 0,
		height: 0,
		width: 0,
		writeTips: '请清晰书写您的签名',
		writeTipsTrue: true,
		src: '',
		src1: '',
		sureBtn: false,
		saveContext: null,
		pixelRatio:2 //设备像素比默认为2
	},
	/**记录开始点 */
	bindtouchstart: function(e) {
    
    
		let {
    
    
			writeTipsTrue
		} = this.data
		if (writeTipsTrue) {
    
    
			this.data.context.clearRect(0, 0, this.data.width, this.data.height)
			this.setData({
    
    
				writeTipsTrue: false,
				sureBtn: true
			})
		}
		this.data.context.moveTo(e.changedTouches[0].x, e.changedTouches[0].y)
	},
	/**记录移动点,刷新绘制 */
	bindtouchmove: function(e) {
    
    
		this.data.context.setLineWidth(2) // 设置线条宽度
		this.data.context.lineTo(e.changedTouches[0].x, e.changedTouches[0].y)
		this.data.context.stroke()
		this.data.context.draw(true)
		this.data.context.moveTo(e.changedTouches[0].x, e.changedTouches[0].y)
	},

	/**清空画布 */
	clear() {
    
    
		let context = this.data.context
		this.setData({
    
    
			writeTipsTrue: true,
			sureBtn: false
		})
		context.clearRect(0, 0, this.data.width, this.data.height)
		// this.data.saveContext.clearRect(0, 0, this.data.height, this.data.width);
		context.save()
		// context.setTransform(1, 0, 0, 1, Math.ceil(this.data.width / 2), 155) // 旋转画布 默认文字区域

		context.setTransform(1, 0, 0, 1, 50, 50)

		let str = this.data.writeTips
		context.setFontSize(24)
		context.setFillStyle('#ADADB2')
		context.fillText(str, 0, 0)
		context.restore()
		context.draw()
	},
	/**导出图片 */
	export () {
    
    
		const that = this
		if (!that.data.sureBtn) {
    
    

			wx.showModal({
    
    
				title: '您没有签名',
				content: '请签完名后再提交',
				showCancel: false,
				confirmText: '我知道了'
			})
			return false
		}

		let signImg
		wx.canvasToTempFilePath({
    
    
			x: 0,
			y: 0,
			width: that.data.width,
			height: that.data.height,
			destWidth: that.data.width * that.data.pixelRatio,
			destHeight: that.data.height * that.data.pixelRatio,
			canvasId: 'firstCanvas',
			success(res) {
    
    
				
				signImg = res.tempFilePath
				that.setData({
    
    
					src1: signImg
				})
				//下载图片

				wx.getImageInfo({
    
    
					src: signImg, // 签字画布生成的暂存地址
					success(res) {
    
    
					

						let rototalImg = res.path
						that.setData({
    
    
							src: rototalImg
						})
						if (rototalImg) {
    
    
							// 单独处理图片旋转
							that.saveImg(rototalImg)



						}

					
					},
					fail(err) {
    
    
					
					}
				})
			},
		})
	},

	// drew img
	saveImg(signImg) {
    
    

		// 旋转图
		let that = this
		

		let context = wx.createCanvasContext('firstCanvas');
		that.setData({
    
    
			saveContext: context
		})
		context.setTransform(1, 0, 0, 1, 50, 50)

		//绘制图片   生成图片函数写在draw()的回调中,不然会出现还没有画图就生成图片的问题

		context.draw(true, function() {
    
    
			wx.canvasToTempFilePath({
    
    
				x: 0,
				y: 0,
				width: that.data.width,
				height: that.data.height,
				destWidth: that.data.width * that.data.pixelRatio,
				destHeight: that.data.height * that.data.pixelRatio,
				canvasId: 'firstCanvas',
				fileType: 'png',
				success: function(res) {
    
    
					var tempFilePath = res.tempFilePath
					

					//将图片转成base64格式
			
							wx.getFileSystemManager().readFile({
    
    
								filePath:tempFilePath,
								encoding: 'base64',
								success: res => {
    
    
									var userImageBase64 =
										'data:image/jpg;base64,' + res.data;
										
											
									// console.log("签字base64",
									// 	userImageBase64);
								

									

									// 生成图片 并返回上一页 赋值
									var curPages = getCurrentPages()
								
									var currPage = curPages[curPages.length - 1];   //当前页面
									var prevPage = curPages[curPages
										.length - 2] //上一页面
									
								    
									prevPage.setData({
    
    
										sign: userImageBase64
									})
									wx.navigateBack({
    
    
										delta: 1,
									})


								}
							})
				
					
					
				




				},
				fail: function(res) {
    
    
					// console.log(res)
				},
			})
		})
	},

	onShow: function() {
    
    
		// 进入页面渲染canvas
		let query = wx.createSelectorQuery()
		const that = this
		let context
		query.select('#firstCanvas').boundingClientRect()
		query.exec(function(rect) {
    
    
			let width = rect[0].width
			let height = rect[0].height
			that.setData({
    
    
				width,
				height,
			})
			const context = wx.createCanvasContext('firstCanvas')
			that.setData({
    
    
				context: context,
			})
			context.save()
			// context.translate(Math.ceil(width / 2) - 20,0)

			context.setTransform(1, 0, 0, 1, 50, 50)
			let str = that.data.writeTips

			context.setFontSize(24)
			context.setFillStyle('#ADADB2')

			context.fillText(str, 0, 0)
			context.restore()
			context.draw()
		})
	
	},
	onLoad(){
    
    
		//获取设备像素比
		wx.getSystemInfo({
    
    
			success: (res) => {
    
    
				// console.log("获取设备像素比",res)
				this.setData({
    
    
					pixelRatio : res.pixelRatio
				})
				
			}
		})
	},
	// 弹窗
	onToast() {
    
    
		app.toast(this, {
    
    
			type: 'text',
			text: '生成成功',
		})
	},
	onShareAppMessage: (res) => {
    
    }
})


//sign.wxss
/* pages/sign/sign.wxss */

#firstCanvas{
    
    
	width:95vw;
	height:78vh;
	margin:0 auto;

	border:1rpx solid #ACACAC;
	
}

#saveImg{
    
    
		width:95vw;
		
		border:1px solid blue;
}

.bottomBox{
    
    
	display: flex;
margin-top:10rpx;
	justify-content: center;
	

}

.clear{
    
    
	width: 157rpx;
	text-align: center;
	height: 44rpx;
	line-height: 44rpx;
	border-radius: 22rpx ;
	opacity: 1;
	border: 1rpx solid #ACACAC;
	font-size: 15rpx;
	font-family: PingFang SC-Bold, PingFang SC;
	font-weight: bold;
	color: #EC3535;
	margin-right:20rpx;
}

.save{
    
    
	width: 157rpx;
	height: 44rpx;
	line-height: 44rpx;
	border-radius: 22rpx ;
	text-align: center;
	font-size: 15rpx;
	font-family: PingFang SC-Bold, PingFang SC;
	font-weight: bold;
	color: #FFFFFF;
	background: linear-gradient(270deg, #3670FF 0%, #65A7FF 100%);

	opacity: 1;
}

.sureBet{
    
    
	
}


//sign.json
{
    
    
  "usingComponents": {
    
    },
  "navigationBarTitleText":"签名",
  "pageOrientation":"landscape",
  "disableScroll":true
}

Guess you like

Origin blog.csdn.net/qq_37635012/article/details/129123901