电子签名
This commit is contained in:
		
							parent
							
								
									6ca132970b
								
							
						
					
					
						commit
						31ccf971d9
					
				| 
						 | 
					@ -68,13 +68,17 @@ if (memberStore.userInfo.nickName && memberStore.userInfo.nickName.length < 5) {
 | 
				
			||||||
  nickName.value = '正楷字书写'
 | 
					  nickName.value = '正楷字书写'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const paths = ref([]) // 保存所有笔迹(每笔是一组点数组)
 | 
				
			||||||
 | 
					const PEN = { width: 6, color: '#000000' } // 若你已有类似配置可复用
 | 
				
			||||||
 | 
					
 | 
				
			||||||
watch(
 | 
					watch(
 | 
				
			||||||
  () => props.modelValue,
 | 
					  () => props.modelValue,
 | 
				
			||||||
  (val) => {
 | 
					  (val) => {
 | 
				
			||||||
    if (val) {
 | 
					    if (val) {
 | 
				
			||||||
      // 小延时确保 canvas 已经渲染
 | 
					      // 小延时确保 canvas 已经渲染
 | 
				
			||||||
      setTimeout(() => {
 | 
					      setTimeout(() => {
 | 
				
			||||||
        uni.createSelectorQuery()
 | 
					        uni
 | 
				
			||||||
 | 
					          .createSelectorQuery()
 | 
				
			||||||
          .in(_this)
 | 
					          .in(_this)
 | 
				
			||||||
          .select('#mycanvas')
 | 
					          .select('#mycanvas')
 | 
				
			||||||
          .fields({ size: true, rect: true }, (data) => {
 | 
					          .fields({ size: true, rect: true }, (data) => {
 | 
				
			||||||
| 
						 | 
					@ -86,7 +90,7 @@ watch(
 | 
				
			||||||
          .exec()
 | 
					          .exec()
 | 
				
			||||||
      }, 50) // 可以尝试 30~100ms,根据实际机型调整
 | 
					      }, 50) // 可以尝试 30~100ms,根据实际机型调整
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const showRotateToast = (msg) => {
 | 
					const showRotateToast = (msg) => {
 | 
				
			||||||
| 
						 | 
					@ -143,43 +147,62 @@ const drawBackground = (ctx, width = 100, height = 100) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 触摸开始
 | 
					// 触摸开始
 | 
				
			||||||
const touchstart = (e) => {
 | 
					const touchstart = (e) => {
 | 
				
			||||||
  let startX = e.changedTouches[0].x
 | 
					  const x = e.changedTouches[0].x
 | 
				
			||||||
  let startY = e.changedTouches[0].y
 | 
					  const y = e.changedTouches[0].y
 | 
				
			||||||
  let startPoint = { X: startX, Y: startY }
 | 
					  const p = { X: x, Y: y }
 | 
				
			||||||
  points.push(startPoint)
 | 
					
 | 
				
			||||||
 | 
					  // 新开一条路径并保存第一个点
 | 
				
			||||||
 | 
					  paths.value.push([p])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 立即在可见画布上开始一段(方便用户看到)
 | 
				
			||||||
 | 
					  canvaCtx.setLineWidth(PEN.width)
 | 
				
			||||||
 | 
					  canvaCtx.setStrokeStyle(PEN.color)
 | 
				
			||||||
 | 
					  canvaCtx.setLineCap('round')
 | 
				
			||||||
 | 
					  canvaCtx.setLineJoin('round')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  canvaCtx.beginPath()
 | 
					  canvaCtx.beginPath()
 | 
				
			||||||
}
 | 
					  canvaCtx.moveTo(x, y)
 | 
				
			||||||
// 触摸移动
 | 
					  // 画一个极短的线段以显示起点(某些设备单点才会丢失)
 | 
				
			||||||
const touchmove = (e) => {
 | 
					  canvaCtx.lineTo(x + 0.1, y + 0.1)
 | 
				
			||||||
  let moveX = e.changedTouches[0].x
 | 
					 | 
				
			||||||
  let moveY = e.changedTouches[0].y
 | 
					 | 
				
			||||||
  let movePoint = { X: moveX, Y: moveY }
 | 
					 | 
				
			||||||
  points.push(movePoint)
 | 
					 | 
				
			||||||
  if (points.length >= 2) {
 | 
					 | 
				
			||||||
    draw()
 | 
					 | 
				
			||||||
		hasDrawn.value = true // 标记已签名
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
// 绘制
 | 
					 | 
				
			||||||
const draw = () => {
 | 
					 | 
				
			||||||
  if (points.length < 2) return
 | 
					 | 
				
			||||||
  canvaCtx.beginPath()
 | 
					 | 
				
			||||||
  canvaCtx.moveTo(points[0].X, points[0].Y)
 | 
					 | 
				
			||||||
  for (let i = 1; i < points.length; i++) {
 | 
					 | 
				
			||||||
    canvaCtx.lineTo(points[i].X, points[i].Y)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  canvaCtx.stroke()
 | 
					  canvaCtx.stroke()
 | 
				
			||||||
  canvaCtx.draw(true)
 | 
					  canvaCtx.draw(true)
 | 
				
			||||||
  points = [points[points.length - 1]]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// 触摸结束
 | 
					
 | 
				
			||||||
 | 
					const touchmove = (e) => {
 | 
				
			||||||
 | 
					  const x = e.changedTouches[0].x
 | 
				
			||||||
 | 
					  const y = e.changedTouches[0].y
 | 
				
			||||||
 | 
					  const p = { X: x, Y: y }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 把点推入当前路径
 | 
				
			||||||
 | 
					  const cur = paths.value[paths.value.length - 1]
 | 
				
			||||||
 | 
					  if (cur) cur.push(p)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 在可见画布上增量画这段
 | 
				
			||||||
 | 
					  canvaCtx.setLineWidth(PEN.width)
 | 
				
			||||||
 | 
					  canvaCtx.setStrokeStyle(PEN.color)
 | 
				
			||||||
 | 
					  canvaCtx.setLineCap('round')
 | 
				
			||||||
 | 
					  canvaCtx.setLineJoin('round')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (cur && cur.length >= 2) {
 | 
				
			||||||
 | 
					    const prev = cur[cur.length - 2]
 | 
				
			||||||
 | 
					    canvaCtx.beginPath()
 | 
				
			||||||
 | 
					    canvaCtx.moveTo(prev.X, prev.Y)
 | 
				
			||||||
 | 
					    canvaCtx.lineTo(p.X, p.Y)
 | 
				
			||||||
 | 
					    canvaCtx.stroke()
 | 
				
			||||||
 | 
					    canvaCtx.draw(true)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const touchend = () => {
 | 
					const touchend = () => {
 | 
				
			||||||
 | 
					  // 不清 paths,保留以便导出;清掉临时 points(如果你还用 points)
 | 
				
			||||||
  points = []
 | 
					  points = []
 | 
				
			||||||
 | 
					  hasDrawn.value = paths.value.length > 0
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
// 清空画布
 | 
					// 清空画布
 | 
				
			||||||
const clear = () => {
 | 
					const clear = () => {
 | 
				
			||||||
  return new Promise((resolve, reject) => {
 | 
					  return new Promise((resolve, reject) => {
 | 
				
			||||||
    uni.createSelectorQuery()
 | 
					    uni
 | 
				
			||||||
 | 
					      .createSelectorQuery()
 | 
				
			||||||
      .in(_this)
 | 
					      .in(_this)
 | 
				
			||||||
      .select('#mycanvas')
 | 
					      .select('#mycanvas')
 | 
				
			||||||
      .fields({ size: true, rect: true }, (data) => {
 | 
					      .fields({ size: true, rect: true }, (data) => {
 | 
				
			||||||
| 
						 | 
					@ -188,9 +211,14 @@ const clear = () => {
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const { width, height } = data
 | 
					        const { width, height } = data
 | 
				
			||||||
 | 
					        // 清空像素
 | 
				
			||||||
        canvaCtx.clearRect(0, 0, width, height)
 | 
					        canvaCtx.clearRect(0, 0, width, height)
 | 
				
			||||||
 | 
					        // flush 清空,再画背景(drawBackground 会 ctx.draw)
 | 
				
			||||||
        canvaCtx.draw(false, () => {
 | 
					        canvaCtx.draw(false, () => {
 | 
				
			||||||
          drawBackground(canvaCtx, width, height) // ⚡ 保证背景立即重绘
 | 
					          // 重绘背景(你的 drawBackground 会自己 ctx.draw)
 | 
				
			||||||
 | 
					          drawBackground(canvaCtx, width, height)
 | 
				
			||||||
 | 
					          // 清空笔迹记录
 | 
				
			||||||
 | 
					          paths.value = []
 | 
				
			||||||
          hasDrawn.value = false
 | 
					          hasDrawn.value = false
 | 
				
			||||||
          resolve()
 | 
					          resolve()
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -205,11 +233,73 @@ const confirm = () => {
 | 
				
			||||||
    showRotateToast('请先签名再确认')
 | 
					    showRotateToast('请先签名再确认')
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  uni.canvasToTempFilePath({ canvasId: 'mycanvas' }, _this, _this.parent).then((res) => {
 | 
					
 | 
				
			||||||
 | 
					  uni
 | 
				
			||||||
 | 
					    .createSelectorQuery()
 | 
				
			||||||
 | 
					    .in(_this)
 | 
				
			||||||
 | 
					    .select('#mycanvas')
 | 
				
			||||||
 | 
					    .fields({ size: true, rect: true }, (data) => {
 | 
				
			||||||
 | 
					      if (!data) return
 | 
				
			||||||
 | 
					      const { width, height } = data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 1) 清空整个画布(包括背景)
 | 
				
			||||||
 | 
					      canvaCtx.clearRect(0, 0, width, height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 2) 在画布上只重绘笔迹(不画背景)
 | 
				
			||||||
 | 
					      canvaCtx.setLineWidth(PEN.width)
 | 
				
			||||||
 | 
					      canvaCtx.setStrokeStyle(PEN.color)
 | 
				
			||||||
 | 
					      canvaCtx.setLineCap('round')
 | 
				
			||||||
 | 
					      canvaCtx.setLineJoin('round')
 | 
				
			||||||
 | 
					      canvaCtx.setFillStyle(PEN.color)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      paths.value.forEach((path) => {
 | 
				
			||||||
 | 
					        if (!path || !path.length) return
 | 
				
			||||||
 | 
					        if (path.length === 1) {
 | 
				
			||||||
 | 
					          // 单点:画小圆点
 | 
				
			||||||
 | 
					          const p = path[0]
 | 
				
			||||||
 | 
					          canvaCtx.beginPath()
 | 
				
			||||||
 | 
					          canvaCtx.arc(p.X, p.Y, Math.max(1, PEN.width / 2), 0, Math.PI * 2)
 | 
				
			||||||
 | 
					          canvaCtx.fill()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          canvaCtx.beginPath()
 | 
				
			||||||
 | 
					          canvaCtx.moveTo(path[0].X, path[0].Y)
 | 
				
			||||||
 | 
					          for (let i = 1; i < path.length; i++) {
 | 
				
			||||||
 | 
					            canvaCtx.lineTo(path[i].X, path[i].Y)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          canvaCtx.stroke()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 3) flush 并在回调中导出(确保像素已写入)
 | 
				
			||||||
 | 
					      canvaCtx.draw(false, () => {
 | 
				
			||||||
 | 
					        uni
 | 
				
			||||||
 | 
					          .canvasToTempFilePath(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              canvasId: 'mycanvas',
 | 
				
			||||||
 | 
					              width,
 | 
				
			||||||
 | 
					              height,
 | 
				
			||||||
 | 
					              destWidth: width,
 | 
				
			||||||
 | 
					              destHeight: height,
 | 
				
			||||||
 | 
					              fileType: 'png',
 | 
				
			||||||
 | 
					              quality: 1,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            _this,
 | 
				
			||||||
 | 
					            _this.parent,
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          .then((res) => {
 | 
				
			||||||
            emits('complete', res.tempFilePath)
 | 
					            emits('complete', res.tempFilePath)
 | 
				
			||||||
 | 
					            // 直接关闭(按你要求:不再恢复背景)
 | 
				
			||||||
            cancel()
 | 
					            cancel()
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
 | 
					          .catch((err) => {
 | 
				
			||||||
 | 
					            console.error('导出签名失败', err)
 | 
				
			||||||
 | 
					            showRotateToast('导出失败,请重试')
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .exec()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 取消
 | 
					// 取消
 | 
				
			||||||
const cancel = () => {
 | 
					const cancel = () => {
 | 
				
			||||||
  clear().then(() => emits('update:modelValue', false))
 | 
					  clear().then(() => emits('update:modelValue', false))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue