(小程序) 客户签名及签名后页面整体转图片后上传
先说明下项目需求:
1、销货清单 需要门店老板 签名
2、签名后 将签名以及铺货清单整体转为图片上传到服务器
图片展示:
图片说明:1:页面结构 上面为清单 下面为客户签名展示区域
2:点击图1 客户签名区域 弹出签名面板
3: 图2 签名完成后 页面展示签名;点击保存后 将整个页面转为图片上传到服务器
4: 上传到服务器后返回的图片
项目使用插件说明:
1、客户签名 使用 handwriting
小程序插件-手写签名 - 简书
2、页面转图片 使用 wxml-to-canvas
wxml-to-canvas | 微信开放文档
代码展示:
看代码之前先熟悉下两个插件,两个插件的代码没有贴上来。
整个功能主要就是两个插件的引用。
wxml:
<view class="page-title"><view class="h1-view">***配送中心</view><view class="h3-view">{{sale.type === 10 ?'销货':'退货'}}清单</view>
</view><view class="shop-name-view marginB10 flex flex_LT flex_column_center"><view>客户:{{sale.distributor_name}}</view><view>{{sale.created_at_text}}</view>
</view><view class="page-main"><view class="table-row flex flex_LT flex_column_center"><view class="idx-view">序号</view><view class="name-view">名称及规格</view><view class="unit-view">单位</view><view class="qty-view">数量</view><view class="price-view">单价</view><view class="amount-view">金额</view></view><view><block wx:for="{{sale.goods}}" wx:key="index"><view class="table-row flex flex_LT flex_column_center"><view class="idx-view">{{index+1}}</view><view class="name-view">{{item.goods_name}}</view><view class="unit-view">{{item.unit_name}}</view><view class="qty-view">{{item.qty}}</view><view class="price-view">{{item.price_text}}</view><view class="amount-view">{{item.amount_text}}</view></view></block><block wx:if="{{sale.goods.length < 6}}"><block wx:for="{{6 -sale.goods.length}}" wx:key="index"><view class="table-row flex flex_LT flex_column_center"><view class="idx-view">{{6-(6-sale.goods.length)+1+index}}</view><view class="name-view"></view><view class="unit-view"></view><view class="qty-view"></view><view class="price-view"></view><view class="amount-view"></view></view></block></block></view><view class="count-view table-row flex flex_column_center"><text class="count-label">合计:</text><text class="count-amount">{{sale.sum_amount_text}}</text><text class="count-end-label">元</text></view>
</view><view class="page-footer"><view class="address">地址:******************</view><view class="created-by">制单: {{sale.created_by}}</view><view class="customer-name flex"><text class="customer-label">客户签名:</text><view class="handwriting"><handwriting bindOnEdit="onHnadwritingEdit" bindOnClose="onHnadwritingClose" bindOnComplete="onHnadwritingComplete" /></view></view>
</view><view class="footer" ><button bind:tap="onHnadwritingToggle" class="btn-radius footer-btn app-bg-color">保 存</button>
</view><wxml-to-canvas hidden class="widget" width="{{width}}" height="{{height}}"></wxml-to-canvas>
wxss:
page {background: #fff;padding: 20rpx;}.footer {position: fixed;left: 0;bottom: 0;width: 710rpx;padding: 20rpx;height: 100rpx;background: #fff;padding-bottom: constant(safe-area-inset-bottom);padding-bottom: env(safe-area-inset-bottom);box-sizing: content-box;box-shadow: 0 0 10rpx #ddd;
}.btn-radius-block {width: 80%;
}.footer-btn {border: 0;outline: 0;font-size: 30rpx;border-radius: 80rpx;height: 80rpx;color: #fff;
}.page-title {padding-bottom: 20rpx;
}.h1-view {font-size: 50rpx;text-align: center;margin-bottom: 20rpx;
}.h3-view {text-align: center;margin-bottom: 20rpx;font-size: 40rpx;padding: 0 20rpx;
}.page-main {width: 710rpx;
}.table-row {border-bottom: 1px solid #999;
}.table-row view {text-align: center;line-height: 60rpx;height: 60rpx;font-size:24rpx;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;word-wrap: normal;
}.idx-view {width: 61rpx;
}.name-view {width: 324rpx;
}.unit-view {width: 61rpx;
}.qty-view {width: 61rpx;
}.price-view {width: 101rpx;
}.amount-view {width: 100rpx;
}.count-view {padding: 10rpx 0;
}.count-label {margin-left: 20rpx;margin-right: 20rpx;
}.count-amount {width: 200rpx;height: 50rpx;margin-right: 20rpx;text-align: center;line-height: 50rpx;color: #f00;font-weight: bold;
}.address {margin-bottom: 30rpx;margin-top: 10rpx;
}.customer-label {width: 150rpx;
}.handwriting {width: 560rpx;
}.customer-name {margin-top: 30rpx;
}.page-footer {padding-bottom: 200rpx;
}.widget-view {position: fixed;left: 0;top: 0;width: 750rpx;height: 100%;background: rgba(0, 0, 0, 0.6);
}.widget {position: absolute;left: 0;top: 0;width:0;height:0;z-index: -1;opacity: 0;
}
.flex{display: flex
}
.flex-item{flex:1;
}
.flex_column{flex-direction:column
}
.flex_LT{justify-content : space-between
}
.flex_R{justify-content : flex-end
}.flex_PJ{justify-content : space-around
}.flex_center{justify-content : center
}
.flex_column_center{align-items : center
}
json:
{"usingComponents": {"handwriting": "/component/handwriting/index","wxml-to-canvas": "wxml-to-canvas"},"navigationBarTitleText": "***配送中心"
}
js:
const app = getApp();
const api = require("../../../../utils/api");
const utils = require("../../../../utils/util");
const sMD5 = require('../../../../utils/spark-md5.js')let {temp,style
} = require('./wxmlAndStyle.js')Page({data: {sale: {},shopId: '',showFooterBtn: true,writingImge: '',width: wx.getSystemInfoSync().windowWidth,height: wx.getSystemInfoSync().windowHeight,},onLoad: function (options) {let id = options.id;this.data.shopId = options.shopIdthis.initData(id);},//id为销售单的id 这里用来查询出数据 显示在页面上 可以自己定义一个json代替数据测试initData: function (id) {wx.promise.request({url: api.API_ADMINS_SALES_SHOW,data: {id: id},header: app.defaultAdminHeader()}).then(res => {this.setData({sale: this.formatData(res)})}).catch(err => {utils.showMsg("加载销售单详情失败:" + err.errMsg)})},formatData: function (data) {data.created_at_text = utils.formatDate_3(data.created_at);data.state_text = this.getStateText(data.state);data.last_receipt_at_text = utils.formatDate_(data.last_receipt_at);data.receipt_amount_text = utils.formatAmount_(data.receipt_amount, 0.01);data.sum_amount_text = utils.formatAmount_(data.sum_amount, 0.01);data.receipt_state_text = data.receipt_state === 0 ? '未结清' : '已结清';data.type_text = data.type === 10 ? '销售单' : '退货单';data.goods = this.formatGoods(data.goods);return data;},formatGoods: function (goods) {goods.forEach(item => {item.price_text = utils.formatAmount_(item.price, 0.01);item.amount_text = utils.formatAmount_(item.amount, 0.01);})return goods;},getStateText: function (state) {switch (state) {case -1:return '已删除';case 0:return '已开单';case 1:return '已确认';case 2:return '已出库';case 3:return '已退货'default:return '';}},onBackOrder: function () {wx.showModal({title: '温馨提示',content: '确定返单吗?',showCancel: true,confirmColor: "#84bc25",success: (res) => {if (res.confirm) {this.onCancelOut()}}})},onCancelOut: function () {wx.promise.request({url: api.API_SALES_CANCEL_OUT_APP,method: 'POST',data: {id: this.data.sale.id},header: app.defaultAdminHeader()}).then(res => {wx.showToast({title: '操作成功!',icon: 'none',duration: 2000})this.initData(this.data.sale.id)}).catch(err => {utils.showMsg("返单失败:" + err.errMsg)})},onHnadwritingComplete: function (e) {console.log(e.detail)this.setData({showFooterBtn: true,})wx.getImageInfo({src: e.detail,success: (e) => {console.log(e)this.uploadImg(e)}})},uploadImg: function (tempFile) {wx.getFileSystemManager().readFile({filePath: tempFile.path, //选择图片返回的相对路径// encoding: 'binary', //编码格式success: res => {let spark = new sMD5.ArrayBuffer();spark.append(res.data);let hexHash = spark.end(false);wx.promise.uploadFile({url: api.API_ADMINS_UPLOAD_IMAGES_CREATE,filePath: tempFile.path,name: 'image',formData: {'image': tempFile,'md5': hexHash},header: {'content-type': 'application/x-www-form-urlencoded','Authorization': app.globalData.adminToken}}).then(res => {console.log(res);if (res.code === 20000 && res.data) {this.setData({writingImge: res.data.url})}}).catch(err => {utils.showMsg("上传图片失败:" + err.errMsg)})}})},onHnadwritingEdit: function () {this.setData({showFooterBtn: false})},onHnadwritingClose: function () {this.setData({showFooterBtn: true})},onHnadwritingToggle: function () {if (!this.data.writingImge) {utils.showMsg("请客户签名后再提交!");return;}this.widget = this.selectComponent('.widget')if (this.data.writingImge) {temp.setUrl(this.data.writingImge)}temp.init(this.data.sale.id);wx.showLoading({title: '正在加载...',})setTimeout(() => {wx.hideLoading()this.renderToCanvas();}, 1000)},renderToCanvas() {let wxml = temp.getWxml();const p1 = this.widget.renderToCanvas({wxml,style})p1.then((res) => {console.log('container', res.layoutBox)this.container = res;this.extraImage();})},extraImage() {const p2 = this.widget.canvasToTempFilePath()p2.then(res => {console.log(res);wx.getImageInfo({src: res.tempFilePath,success: (e) => {console.log(e)this.onUploadApplyImage(e)}})})},onUploadApplyImage: function (tempFile) {wx.getFileSystemManager().readFile({filePath: tempFile.path, //选择图片返回的相对路径// encoding: 'binary', //编码格式success: res => {let spark = new sMD5.ArrayBuffer();spark.append(res.data);let hexHash = spark.end(false);wx.showLoading({title: '正在上传图片...',})wx.promise.uploadFile({url: api.API_ADMINS_UPLOAD_IMAGES_CREATE,filePath: tempFile.path,name: 'image',formData: {'image': tempFile,'md5': hexHash},header: {'content-type': 'application/x-www-form-urlencoded','Authorization': app.globalData.adminToken}}).then(res => {wx.hideLoading()console.log(res);if (res.code === 20000 && res.data) {this.onUploadApply(res.data.id)}}).catch(err => {wx.hideLoading()utils.showMsg("上传图片失败:" + err.errMsg)})}})},onUploadApply: function (image_id) {wx.showLoading({title: '正在保存图片...',})wx.promise.request({url: api.API_ADMINS_SALE_APPLY_IMAGE,method: 'POST',data: {id: this.data.sale.id,image_id: image_id},header: app.defaultAdminHeader()}).then(res => {wx.hideLoading()wx.showToast({title: '保存图片成功!',icon: 'none',duration: 2000})wx.navigateBack()}).catch(err => {wx.hideLoading()utils.showMsg("保存图片失败:" + err.errMsg)})}
})
wxmlAndStyle.js:
const app = getApp();
const api = require("../../../../utils/api");
const utils = require("../../../../utils/util");let wWidth = wx.getSystemInfoSync().windowWidth;
let wHeight = wx.getSystemInfoSync().windowHeight ;let style = {pageview: {backgroundColor: "#fff",padding: 10,paddingTop:40,width: wWidth,height: wHeight},pagetitle: {width: wWidth,height: 60,flexDirection: 'row',justifyContent: 'space-around',alignItems: 'center',},h1view: {width: wWidth,height: 30,textAlign: 'center',},h3view: {width: wWidth,height: 30,textAlign: 'center',},t1: {width: wWidth,height: 30,fontSize: 22,lineHeight: 30,fontWeight: 'bold',color: "#000000"},t2: {width: wWidth,height: 30,fontSize: 18,lineHeight: 30,fontWeight: 'bold',color: "#000000",borderBottom: '1px solid #000'},shopnameview: {width: wWidth,height: 14,},distributorName: {width: wWidth / 2.1,height: 14,fontSize:12,lineHeight: 14,color: "#999",},pagemain: {width: wWidth-20,backgroundColor:"#999"},tablerow: {width: wWidth-10,backgroundColor:"#fff",marginBottom:1,},rowitem: {textAlign: 'center',lineHeight: 28,height: 28,fontSize:12,overflow: 'hidden',textOverflow: 'ellipsis',whiteSpace: 'nowrap',wordWrap: 'normal',},idxview: {width: (wWidth -20) * (1/8) //30},nameview: {width: (wWidth -20) * (3/8)//165},unitview: {width: (wWidth -20) * (1/8)//40},qtyview: {width: (wWidth -20) * (1/8)//40},priceview: {width: (wWidth -20) * (1/8)//40,},amountview: {width: (wWidth -20) * (1/8)//40,},countlabel: {width: 60},countamount: {width: 100,height: 25,textAlign: "center",lineHeight: 25,color: "red",fontWeight: "bold",},address: {width:wWidth,lineHeight: 28,height: 28,fontSize:13,overflow: 'hidden',textOverflow: 'ellipsis',whiteSpace: 'nowrap',wordWrap: 'normal',borderWidth: 1},customerlabel: {width: 75,lineHeight: 28,height: 28,fontSize:13,overflow: 'hidden',textOverflow: 'ellipsis',whiteSpace: 'nowrap',wordWrap: 'normal',borderWidth: 1},customername: {width:wWidth,},customerImg:{width:200,height:100},pagefooter: {width:wWidth},createdby:{width:wWidth,textAlign:'left',lineHeight: 30,height: 30,overflow: 'hidden',textOverflow: 'ellipsis',whiteSpace: 'nowrap',wordWrap: 'normal',borderWidth: 1},flexcolumn: {flexDirection: 'column'},flexlt: {flexDirection: 'row',justifyContent: 'space-between'},flexr: {flexDirection: 'row',justifyContent: 'flex-end'},flexl: {flexDirection: 'row',justifyContent: 'flex-start'},flexpj: {flexDirection: 'row',justifyContent: 'space-around'},flexcenter: {flexDirection: 'row',justifyContent: 'center'},flexcolumncenter: {alignItems: 'center'},marginB10: {marginBottom: 10},textR: {textAlign: 'right'}
}let temp = {url: '',wxml: '',sale: {},init: function (id) {wx.promise.request({url: api.API_ADMINS_SALES_SHOW,data: {id: id},header: app.defaultAdminHeader()}).then(res => {temp.sale = temp.formatData(res)temp.initWxml(temp.sale)}).catch(err => {utils.showMsg("加载销售单详情失败:" + err.errMsg)})},setUrl: function (url) {temp.url = url},formatData: function (data) {data.created_at_text = utils.formatDate_3(data.created_at);data.state_text = temp.getStateText(data.state);data.last_receipt_at_text = utils.formatDate_(data.last_receipt_at);data.receipt_amount_text = utils.formatAmount_(data.receipt_amount, 0.01);data.sum_amount_text = utils.formatAmount_(data.sum_amount, 0.01);data.receipt_state_text = data.receipt_state === 0 ? '未结清' : '已结清';data.type_text = data.type === 10 ? '销售单' : '退货单';data.goods = temp.formatGoods(data.goods);return data;},formatGoods: function (goods) {goods.forEach(item => {item.price_text = utils.formatAmount_(item.price, 0.01);item.amount_text = utils.formatAmount_(item.amount, 0.01);})return goods;},getStateText: function (state) {switch (state) {case -1:return '已删除';case 0:return '已开单';case 1:return '已确认';case 2:return '已出库';case 3:return '已退货'default:return '';}},initWxml: function (sale) {const title = `<view class="pagetitle flexcolumn" ><view class="h1view flexcenter"><text class="t1">****配送中心</text></view><view class="h3view"><text class="t2">${sale.type === 10 ? '销售' : '退货'}清单</text></view></view>`;const shopName = `<view class="marginB10 flexl flexcolumncenter shopnameview"><text class="distributorName">客户:${sale.distributor_name}</text><text class="distributorName textR">${sale.created_at_text}</text></view>`;const tableHeader = `<view class="tablerow flexl flexcolumncenter"><text class="idxview rowitem">序号</text><text class="nameview rowitem">名称及规格</text><text class="unitview rowitem">单位</text><text class="qtyview rowitem">数量</text><text class="priceview rowitem">单价</text><text class="amountview rowitem">金额</text></view>`;let tableBody = ''sale.goods.forEach((item, index) => {tableBody += `<view class="tablerow flexl flexcolumncenter"><text class="idxview rowitem">${index+1}</text><text class="nameview rowitem">${item.goods_name}</text><text class="unitview rowitem">${item.unit_name}</text><text class="qtyview rowitem">${item.qty}</text><text class="priceview rowitem">${item.price_text}</text><text class="amountview rowitem">${item.amount_text}</text></view>`})if (sale.goods.length < 6) {let count = 6 - sale.goods.length;for (let i = 0; i < count; i++) {tableBody += `<view class="tablerow flexl flexcolumncenter"><text class="idxview rowitem">${15-(15-sale.goods.length)+1+i}</text><text class="nameview rowitem"></text><text class="unitview rowitem"></text><text class="qtyview rowitem"></text><text class="priceview rowitem"></text><text class="amountview rowitem"></text></view>`}}let tableFooter = `<view class="tablerow flexl flexcolumncenter"><text class="countlabel rowitem">合计:</text><text class="countamount rowitem">${sale.sum_amount_text}</text><text class="countlabel rowitem">元</text></view>`;let imgWxml = temp.url ? `<image class="customerImg" src="${temp.url}"></image>` : '';let pageFooter = `<view class="pagefooter flexcolumn"><text class="address">地址:**********************</text><text class="createdby">制单: ${sale.created_by}</text><view class="customername flexl"><text class="customerlabel">客户签名:</text>${imgWxml}</view></view>`let tableWxml = `<view class="pagemain">${tableHeader}${tableBody}${tableFooter}</view>`this.wxml = `<view class="pageview">${title}${shopName}${tableWxml}${pageFooter}</view>`;},getWxml: function () {return this.wxml},
}module.exports = {temp,style
}
(小程序) 客户签名及签名后页面整体转图片后上传相关推荐
- 支付宝小程序编译打包时, 使用tnpm安装依赖可能导致上传时构建失败,请使用 \tnpm i --by=yarn\ 安装依赖
1.最近做支付宝小程序,不太懂,因为用了mini-ali-ui,所以打包出现了Packed fail. ========== 远程调试构建失败 ========== build & pack ...
- uniapp - [微信小程序] 超详细实时录音功能,录制外部声音及播放上传到服务器(支持录音完毕生成 mp3、试听音频(带进度条)、暂停录音、重新录音、限制录音最大时长、自定义音频名称等功能)
前言 网上的教程都太乱了,功能不好用且一堆 BUG,没有注释很难改造示例为自己用. 本文实现了 uniapp 微信小程序平台,授权麦克风进行录音并保存为音频的功能,内置播放器可播放录音文件, 您直接复 ...
- 微信小程序云开发 把时间戳转换成正常的时间格式上传到数据库中
一.utils文件里面建一个utils.js文件(内容如下) 目录 一.utils文件里面建一个utils.js文件(内容如下) 二.在上传用户详情信息的界面最上方标注 三,在上传的函数中 四.结果 ...
- 微信小程序 云开发 提交表单数据和表单里上传的多张图片一起提交到数据库里完整代码
JS代码: const db = wx.cloud.database()Page({data: {xingming: '',xingbie: '',aihao: [],flog: true,image ...
- 微信小程序实现用form表单包裹输入的数据并上传到服务器
两个输入框分别为input与textarea,然后用form表单包裹两个输入框所输入的数据,再上传到服务器,效果图: post.wxml <form bindsubmit="formS ...
- 微信小程序用户数据的签名校验和加解密 - 后端nodejs
在本文 微信小程序用户数据的签名校验和加解密 之前需要先看看 微信小程序-获取用户session_key,openid,unionid - 后端为nodejs 代码封装是在上文添加的. 小程序代码: ...
- android 代码等待一秒,【报Bug】安卓微信旧版本7.0.2 ,支付完成,等待几秒后,再点击完成 回到小程序,跳转不了页面。...
详细问题描述 微信旧版本7.0.2 ,支付完成,等待几秒后,再点击完成 回到小程序,跳转不了页面. (DCloud产品不会有明显的bug,所以你遇到的问题大都是在特定环境下才能重现的问题,请仔细描述你 ...
- uniapp 小程序 APP 实现手写板 签名 画图 canvas 保存为图片格式
文章目录 uniapp 小程序 APP 实现手写板 签名 画图 canvas 保存为图片格式 一.手写板 1.H5代码 2.JS代码 总结 uniapp 小程序 APP 实现手写板 签名 画图 can ...
- 在线签名、canvas手写在线签名——小程序端实现在线签名,手写电子签名canvas
在线签名.canvas手写在线签名--小程序端实现在线签名,手写电子签名canvas 近期公司准备接一个政府的小项目,里头有一个在线签名的需求,老板让我研究一下.于是就...开始思考: 在线手写签名, ...
最新文章
- wikioi 1083 Cantor表
- 介绍两个Eclipse插件: Implementors Call Hierarchy
- 9. 混合模型和EM(2)
- Vxworks信号量分析
- 洛谷 P2519 [HAOI2011]problem a
- VTK:绘图之HistogramBarChart
- 树莓派3_win10下使用远程桌面连接与树莓派通信(使用VNC实现连接后)
- python守护线程错误 退出子线程_请问用python里threading和queue模块来写多线程程序,子线程是如何结束的?...
- ES6/07/Array的扩展方法,...扩展运算符,Array.from(),(arr.find(),arr.findIndex()和arr.includes())模板字符串,Set数据结构
- linux下补丁制作及打补丁实例
- 刷新按钮_处理数据透视表的隐藏选项(四):固定报表刷新前后的列宽和格式...
- codeforces733-C. Epidemic in Monstropolis 贪心加链表
- Java开发工程师面试笔试试题,真题;
- 使用大华sdk批量下载视频文件、批量转换格式
- 04735数据库系统原理(笔记)(更新中)
- html链接regard,regard是什么意思
- 在线自习室——新型的学习利器
- 企业营销环节应用RPA的三大典型场景
- PreScan里动力学模型的2D和3D的区别
- prefix-list前缀列表
热门文章
- 数据可视化:利用Python和Echarts制作“用户消费行为分析”可视化大屏
- 【系统设计】本地生活之附近商家 LBS 服务实现
- 《白话大数据与机器学习》学习笔记
- 用js实现登录的简单验证
- 搭建之星中文编程教学教程视频
- layui实现管理后台页面效果
- VB中常用的函数(一)
- 【无标题】There was an unexpected error (type=Internal Server Error, status=500).
- unity3D期末作业-太空飞机射击游戏
- 计算机毕业设计ssm基于b_s架构的实习管理系统