|
@@ -1,15 +1,204 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
- 评价
|
|
|
+ <div class="ml_sign">
|
|
|
+ <canvas ref="signature" id="signature"></canvas>
|
|
|
+ <div class="btn-wrapper">
|
|
|
+ <button @click="clear">取消</button>
|
|
|
+ <button @click="save">保存</button>
|
|
|
+ </div>
|
|
|
+ <img :src="imgurl">
|
|
|
</div>
|
|
|
-</template>
|
|
|
-<script>
|
|
|
-export default {
|
|
|
-
|
|
|
-}
|
|
|
-</script>
|
|
|
-<style>
|
|
|
-
|
|
|
-</style>
|
|
|
-
|
|
|
-
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ export default {
|
|
|
+ name: 'Signatrue',
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ canvas: null, // 存储canvas节点
|
|
|
+ ctx: null, // 存储canvas的context上下文
|
|
|
+ config: {
|
|
|
+ width: 400, // 宽度
|
|
|
+ height: 200, // 高度
|
|
|
+ strokeStyle: 'red', // 线条颜色
|
|
|
+ lineWidth: 4, // 线条宽度
|
|
|
+ lineCap: 'round', // 设置线条两端圆角
|
|
|
+ lineJoin: 'round' // 线条交汇处圆角
|
|
|
+ },
|
|
|
+ points: [], // 记录坐标 用来判断是否有签名的
|
|
|
+ client: {
|
|
|
+ offsetX: 0, // 偏移量
|
|
|
+ offsetY: 0,
|
|
|
+ endX: 0, // 坐标
|
|
|
+ endY: 0
|
|
|
+ },
|
|
|
+ imgurl: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ // 判断是否为移动端
|
|
|
+ mobileStatus () {
|
|
|
+ return (/Mobile|Android|iPhone/i.test(navigator.userAgent))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ this.init()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 初始化
|
|
|
+ init () {
|
|
|
+ const canvas = this.$refs.signature
|
|
|
+ canvas.width = this.config.width // 设置canvas的宽
|
|
|
+ canvas.height = this.config.height // 设置canvas的高
|
|
|
+ // 设置一个边框
|
|
|
+ canvas.style.border = '1px solid #000'
|
|
|
+
|
|
|
+ // 存储canvas节点
|
|
|
+ this.canvas = canvas
|
|
|
+ // 创建context对象
|
|
|
+ this.ctx = canvas.getContext('2d')
|
|
|
+
|
|
|
+ // 设置相应配置
|
|
|
+ this.ctx.fillStyle = 'transparent'
|
|
|
+ this.ctx.lineWidth = this.config.lineWidth
|
|
|
+ this.ctx.strokeStyle = this.config.strokeStyle
|
|
|
+ this.ctx.lineCap = this.config.lineCap
|
|
|
+ this.ctx.lineJoin = this.config.lineJoin
|
|
|
+
|
|
|
+ // 绘制填充矩形
|
|
|
+ this.ctx.fillRect(
|
|
|
+ 0, // x 轴起始绘制位置
|
|
|
+ 0, // y 轴起始绘制位置
|
|
|
+ this.config.width, // 宽度
|
|
|
+ this.config.height // 高度
|
|
|
+ )
|
|
|
+
|
|
|
+ // 创建鼠标/手势按下监听器
|
|
|
+ canvas.addEventListener(this.mobileStatus ? 'touchstart' : 'mousedown', this.startDraw)
|
|
|
+ // 创建鼠标/手势 弹起/离开 监听器
|
|
|
+ canvas.addEventListener(this.mobileStatus ? 'touchend' : 'mouseup', this.cloaseDraw)
|
|
|
+ },
|
|
|
+ // 开始绘制
|
|
|
+ startDraw (event) {
|
|
|
+ // 获取偏移量及坐标
|
|
|
+ const { offsetX, offsetY, pageX, pageY } = this.mobileStatus ? event.changedTouches[0] : event
|
|
|
+
|
|
|
+ // 修改上次的偏移量及坐标
|
|
|
+ this.client.offsetX = offsetX
|
|
|
+ this.client.offsetY = offsetY
|
|
|
+ this.client.endX = pageX
|
|
|
+ this.client.endY = pageY
|
|
|
+
|
|
|
+ // 清除以上一次 beginPath 之后的所有路径,进行绘制
|
|
|
+ this.ctx.beginPath()
|
|
|
+ // 设置画线起始点位
|
|
|
+ this.ctx.moveTo(this.client.endX, this.client.endY)
|
|
|
+ // 监听 鼠标移动或手势移动
|
|
|
+ this.canvas.addEventListener(this.mobileStatus ? 'touchmove' : 'mousemove', this.draw)
|
|
|
+ },
|
|
|
+ // 绘制
|
|
|
+ draw (event) {
|
|
|
+ // 获取当前坐标点位
|
|
|
+ const { pageX, pageY } = this.mobileStatus ? event.changedTouches[0] : event
|
|
|
+ // 修改最后一次绘制的坐标点
|
|
|
+ this.client.endX = pageX
|
|
|
+ this.client.endY = pageY
|
|
|
+ const obj = {
|
|
|
+ x: pageX,
|
|
|
+ y: pageY
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据坐标点位移动添加线条
|
|
|
+ this.ctx.lineTo(pageX, pageY)
|
|
|
+
|
|
|
+ // 绘制
|
|
|
+ this.ctx.stroke()
|
|
|
+
|
|
|
+ // 记录坐标
|
|
|
+ this.points.push(obj)
|
|
|
+ },
|
|
|
+ // 结束绘制
|
|
|
+ cloaseDraw () {
|
|
|
+ // 结束绘制
|
|
|
+ this.ctx.closePath()
|
|
|
+ // 移除鼠标移动或手势移动监听器
|
|
|
+ this.canvas.removeEventListener('mousemove', this.draw)
|
|
|
+ },
|
|
|
+ // 取消/清空画布
|
|
|
+ clear () {
|
|
|
+ // 清空当前画布上的所有绘制内容
|
|
|
+ this.ctx.clearRect(0, 0, this.config.width, this.config.height)
|
|
|
+ // 清空坐标
|
|
|
+ this.points = []
|
|
|
+ },
|
|
|
+ // 保存
|
|
|
+ save () {
|
|
|
+ // 判断至少有20个坐标 才算有签名
|
|
|
+ if (this.points.length < 20) {
|
|
|
+ alert('签名不能为空!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 操作事件
|
|
|
+ const baseFile = this.canvas.toDataURL() // 转成base64,默认转成png格式的图片编码
|
|
|
+ const filename = `${Date.now()}.png` // 文件名字
|
|
|
+ const file = this.dataURLToFile(baseFile, filename) // 图片文件形式 传给后端存储即可
|
|
|
+
|
|
|
+ this.uploadSignatrue(file)
|
|
|
+
|
|
|
+ // this.dataUrlToPng()
|
|
|
+
|
|
|
+ // this.dataToImg()
|
|
|
+ },
|
|
|
+ // img显示签名
|
|
|
+ dataToImg () {
|
|
|
+ // 转成base64
|
|
|
+ const baseFile = this.canvas.toDataURL() // 默认转成png格式的图片编码
|
|
|
+ this.imgurl = baseFile
|
|
|
+ },
|
|
|
+ // 将签名生成png图片
|
|
|
+ dataUrlToPng () {
|
|
|
+ // 将canvas上的内容转成blob流
|
|
|
+ this.canvas.toBlob(blob => {
|
|
|
+ // 获取当前时间并转成字符串,用来当做文件名
|
|
|
+ const date = Date.now().toString()
|
|
|
+ // 创建一个 a 标签
|
|
|
+ const a = document.createElement('a')
|
|
|
+ // 设置 a 标签的下载文件名
|
|
|
+ a.download = `${date}.png`
|
|
|
+ // 设置 a 标签的跳转路径为 文件流地址
|
|
|
+ a.href = URL.createObjectURL(blob)
|
|
|
+ // 手动触发 a 标签的点击事件
|
|
|
+ a.click()
|
|
|
+ // 移除 a 标签
|
|
|
+ a.remove()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 将base64转成File文件对象
|
|
|
+ dataURLToFile (dataURL, filename) {
|
|
|
+ const arr = dataURL.split(',')
|
|
|
+ // 获取图片格式
|
|
|
+ const imgType = arr[0].match(/:(.*?);/)[1]
|
|
|
+ // atob() 方法用于解码使用 base-64 编码的字符串
|
|
|
+ const dec = atob(arr[1])
|
|
|
+ let n = dec.length
|
|
|
+ const u8arr = new Uint8Array(n)
|
|
|
+ while (n--) {
|
|
|
+ // 转成ASCII码
|
|
|
+ u8arr[n] = dec.charCodeAt(n)
|
|
|
+ }
|
|
|
+ return new File([u8arr], filename, { type: imgType })
|
|
|
+ },
|
|
|
+ // 上传签名
|
|
|
+ uploadSignatrue (file) {
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+ // formData.append('paramsOne', paramsOne)
|
|
|
+ // ...
|
|
|
+ console.log(formData)
|
|
|
+
|
|
|
+ // 上传接口 这里就不赘述了
|
|
|
+ // uploadFile(params, ...)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </script>
|