puzhibing
6 天以前 0db8c9b94a9ac655549d5eba5c2a6c92ac386c06
culture/src/components/SignatureCanvas.vue
@@ -1,27 +1,11 @@
<template>
  <el-dialog
    title="确认签字"
    :visible.sync="visible"
    :width="dialogWidth"
    :close-on-click-modal="false"
    custom-class="signature-dialog"
    append-to-body
    @close="$emit('update:visible', false)"
  >
  <el-dialog title="确认签字" :visible.sync="visible" :width="dialogWidth" :close-on-click-modal="false"
    custom-class="signature-dialog" append-to-body @close="$emit('update:visible', false)">
    <div class="signature-container">
      <div class="signature-content">
        <canvas
          ref="signatureCanvas"
          :width="canvasWidth"
          :height="canvasHeight"
          @mousedown="startDrawing"
          @mousemove="draw"
          @mouseup="stopDrawing"
          @mouseleave="stopDrawing"
          @touchstart="handleTouchStart"
          @touchmove="handleTouchMove"
          @touchend="stopDrawing"
        ></canvas>
        <canvas ref="signatureCanvas" :width="canvasWidth" :height="canvasHeight" @mousedown="startDrawing"
          @mousemove="draw" @mouseup="stopDrawing" @mouseleave="stopDrawing" @touchstart="handleTouchStart"
          @touchmove="handleTouchMove" @touchend="stopDrawing"></canvas>
      </div>
      <div class="signature-footer">
        <el-button type="default" @click="clearCanvas">重置</el-button>
@@ -32,6 +16,8 @@
</template>
<script>
import { customUploadRequest, getUploadedFilePath } from '@/utils/utils'
import {editSignPicture} from './service'
export default {
  name: 'SignatureCanvas',
  props: {
@@ -42,10 +28,12 @@
  },
  data() {
    return {
      roleType: JSON.parse(sessionStorage.getItem('userInfo')).roleType || 1,  // 1.超级管理员 2.审批人 3.工程师 4.实验员
      isDrawing: false,
      context: null,
      lastX: 0,
      lastY: 0
      lastY: 0,
      hasDrawn: false
    }
  },
  computed: {
@@ -67,6 +55,10 @@
      if (val) {
        this.$nextTick(() => {
          this.initCanvas()
          // 如果是实验员,加载保存的签名
          if (this.roleType == 4 || this.roleType == 3) {
            this.loadSavedSignature()
          }
          // 添加窗口大小改变的监听
          window.addEventListener('resize', this.handleResize)
        })
@@ -81,6 +73,10 @@
      // 窗口大小改变时重新初始化画布
      this.$nextTick(() => {
        this.initCanvas()
        // 如果是实验员,重新加载保存的签名
        if ((this.roleType === 4 || this.roleType == 3) && this.savedSignature) {
          this.loadSavedSignature()
        }
      })
    },
    initCanvas() {
@@ -90,11 +86,11 @@
      this.context.lineWidth = 2
      this.context.lineCap = 'round'
      this.context.lineJoin = 'round'
      // 清空画布并绘制虚线边框
      this.clearCanvas()
    },
    drawDashedBorder() {
      const ctx = this.context
      ctx.setLineDash([5, 5])
@@ -113,18 +109,19 @@
    draw(event) {
      if (!this.isDrawing) return
      const rect = this.$refs.signatureCanvas.getBoundingClientRect()
      const x = event.clientX - rect.left
      const y = event.clientY - rect.top
      this.context.beginPath()
      this.context.moveTo(this.lastX, this.lastY)
      this.context.lineTo(x, y)
      this.context.stroke()
      this.lastX = x
      this.lastY = y
      this.hasDrawn = true
    },
    handleTouchStart(event) {
@@ -139,19 +136,20 @@
    handleTouchMove(event) {
      event.preventDefault()
      if (!this.isDrawing) return
      const touch = event.touches[0]
      const rect = this.$refs.signatureCanvas.getBoundingClientRect()
      const x = touch.clientX - rect.left
      const y = touch.clientY - rect.top
      this.context.beginPath()
      this.context.moveTo(this.lastX, this.lastY)
      this.context.lineTo(x, y)
      this.context.stroke()
      this.lastX = x
      this.lastY = y
      this.hasDrawn = true
    },
    stopDrawing() {
@@ -161,33 +159,111 @@
    clearCanvas() {
      this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
      this.drawDashedBorder()
      this.hasDrawn = false
    },
    confirmSignature() {
      const canvas = this.$refs.signatureCanvas
      const ctx = this.context
      // 校验是否签名
      if (this.isCanvasBlank()) {
        this.$message && this.$message.warning('请先确认签名')
        return
      }
      // 保存当前画布内容
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
      // 先填充背景色
      ctx.fillStyle = 'rgba(239, 248, 250, 1)'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      // 创建一个临时画布来保存签名内容
      const tempCanvas = document.createElement('canvas')
      tempCanvas.width = canvas.width
      tempCanvas.height = canvas.height
      const tempCtx = tempCanvas.getContext('2d')
      tempCtx.putImageData(imageData, 0, 0)
      // 将签名内容绘制到主画布上
      ctx.drawImage(tempCanvas, 0, 0)
      // 导出图片
      const signatureImage = canvas.toDataURL('image/png')
      this.$emit('confirm', 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg')
    }
      // 导出图片为base64
      const base64Data = canvas.toDataURL('image/png');
      // 如果是实验员,保存签名到sessionStorage
      if (this.roleType == 4 || this.roleType == 3) {
        // 保存当前画布内容(包括背景色)
        // 保存到sessionStorage
        sessionStorage.setItem('experimenterSignature', base64Data)
      }
      // base64转blob
      const blob = this.dataURLtoBlob(base64Data);
      // 新增:生成带时间戳的文件名
      const timestamp = Date.now();
      const fileName = `signature_${timestamp}.png`;
      const file = new File([blob], fileName, { type: blob.type });
      // 上传
      customUploadRequest({
        file,
        onSuccess: (res) => {
          const url = getUploadedFilePath(res)
          if (!url) {
            this.$message.error('上传接口未返回文件地址')
            return
          }
          editSignPicture({ signPicture: url }).then(result => {
            if (result.code == 200) {
              this.$message.success('签名成功');
              this.$emit('confirm', url);
            }
          })
        },
        onError: (err) => {
          this.$message && this.$message.error ? this.$message.error('上传签名失败') : alert('上传签名失败');
        }
      });
    },
    // 加载保存的签名到画布(允许修改)
    loadSavedSignature() {
      const savedData = sessionStorage.getItem('experimenterSignature');
      console.log('savedData:,', savedData)
      if (savedData) {
        const canvas = this.$refs.signatureCanvas
        const ctx = this.context
        const img = new Image()
        img.onload = () => {
          // 清空画布
          ctx.clearRect(0, 0, canvas.width, canvas.height)
          // 绘制保存的签名
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
          // 重新绘制虚线边框
          this.drawDashedBorder()
          this.hasDrawn = true
          this.savedSignature = savedData
          // 不锁定画布,允许修改
        }
        img.src = savedData
      }
    },
    // 新增:base64转blob
    dataURLtoBlob(dataurl) {
      const arr = dataurl.split(',');
      const mime = arr[0].match(/:(.*?);/)[1];
      const bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], { type: mime });
    },
    // 新增方法:判断画布是否为空白
    isCanvasBlank() {
      return !this.hasDrawn
    },
  },
  beforeDestroy() {
    // 组件销毁前移除监听
@@ -201,6 +277,7 @@
  :deep(.el-dialog__body) {
    padding: 0;
  }
  :deep(.el-dialog) {
    margin-top: 10vh !important;
  }
@@ -209,12 +286,12 @@
.signature-container {
  background: #fff;
  border-radius: 4px;
  .signature-content {
    padding: 20px;
    display: flex;
    justify-content: center;
    canvas {
      border-radius: 4px;
      background: rgba(239, 248, 250, 1);
@@ -222,16 +299,17 @@
      height: 100%;
    }
  }
  .signature-footer {
    padding: 20px;
    border-top: 1px solid #dcdfe6;
    display: flex;
    justify-content: flex-end;
    gap: 12px;
    button{
    button {
      width: 150px;
    }
  }
}
</style>
</style>