保姆级教程:在Vue3的Dialog弹窗中集成smooth-signature电子签名(支持移动端)
2026/5/26 14:58:13 网站建设 项目流程

Vue3弹窗集成smooth-signature电子签名全流程实战

去年在开发一个医疗审批系统时,遇到一个棘手需求:医生需要在移动设备上完成处方电子签名。经过多次迭代,最终采用Vue3 + smooth-signature的方案完美解决了这个问题。本文将分享如何在Dialog弹窗中实现专业级电子签名功能,特别针对移动端横竖屏切换场景。

1. 环境准备与基础集成

首先创建一个全新的Vue3项目(如果已有项目可跳过此步)。推荐使用Vite作为构建工具,它能提供更快的开发体验:

npm create vite@latest vue3-signature-demo --template vue-ts cd vue3-signature-demo npm install smooth-signature

对于UI库的选择,Element Plus和Vant都是不错的选择。这里以Element Plus为例:

npm install element-plus @element-plus/icons-vue

在main.ts中完成基础配置:

import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' const app = createApp(App) app.use(ElementPlus) app.mount('#app')

2. 弹窗与签名组件封装

创建一个独立的ESign组件来处理签名逻辑。这个组件需要处理以下核心功能:

  • 画布初始化
  • 笔迹平滑处理
  • 横竖屏切换
  • 签名数据导出
<template> <div class="signature-container"> <el-dialog v-model="dialogVisible" title="电子签名" :fullscreen="isFullscreen" @closed="handleDialogClose" > <div class="canvas-wrapper"> <canvas ref="signatureCanvas"></canvas> </div> <div class="action-buttons"> <el-button @click="handleClear">清除</el-button> <el-button @click="handleUndo">撤销</el-button> <el-button @click="toggleOrientation"> {{ isFullscreen ? '竖屏' : '横屏' }} </el-button> <el-button type="primary" @click="handleSave">保存</el-button> </div> </el-dialog> </div> </template> <script setup lang="ts"> import { ref, onMounted, watch } from 'vue' import SmoothSignature from 'smooth-signature' const props = defineProps({ visible: Boolean }) const emit = defineEmits(['update:visible', 'save']) const dialogVisible = ref(false) const signatureCanvas = ref<HTMLCanvasElement | null>(null) const signatureInstance = ref<SmoothSignature | null>(null) const isFullscreen = ref(false) watch(() => props.visible, (val) => { dialogVisible.value = val }) const initSignature = () => { if (!signatureCanvas.value) return const options = { width: isFullscreen.value ? window.innerHeight - 120 : window.innerWidth - 60, height: isFullscreen.value ? window.innerWidth - 60 : 300, minWidth: 2, maxWidth: 6, openSmooth: true, bgColor: '#f8f8f8' } signatureInstance.value = new SmoothSignature(signatureCanvas.value, options) } const handleClear = () => { signatureInstance.value?.clear() } const handleUndo = () => { signatureInstance.value?.undo() } const toggleOrientation = () => { isFullscreen.value = !isFullscreen.value nextTick(() => { initSignature() }) } const handleSave = () => { if (signatureInstance.value?.isEmpty()) { ElMessage.warning('请先完成签名') return } const signatureData = signatureInstance.value.getPNG() emit('save', signatureData) dialogVisible.value = false } const handleDialogClose = () => { emit('update:visible', false) } onMounted(() => { initSignature() }) </script> <style scoped> .canvas-wrapper { margin: 20px 0; } canvas { width: 100%; border: 1px dashed #dcdfe6; border-radius: 4px; background-color: #f8f8f8; } .action-buttons { display: flex; justify-content: space-around; margin-top: 20px; } </style>

3. 移动端适配与性能优化

移动端环境下需要特别注意以下几点:

  1. 触摸事件处理

    const handleTouchMove = (e: TouchEvent) => { e.preventDefault() } onMounted(() => { document.addEventListener('touchmove', handleTouchMove, { passive: false }) }) onUnmounted(() => { document.removeEventListener('touchmove', handleTouchMove) })
  2. 横竖屏切换检测

    const checkOrientation = () => { isFullscreen.value = window.innerHeight > window.innerWidth } onMounted(() => { window.addEventListener('resize', checkOrientation) checkOrientation() })
  3. 画布清晰度保障

    const setupCanvas = () => { const canvas = signatureCanvas.value if (!canvas) return const dpr = window.devicePixelRatio || 1 const rect = canvas.getBoundingClientRect() canvas.width = rect.width * dpr canvas.height = rect.height * dpr const ctx = canvas.getContext('2d') ctx?.scale(dpr, dpr) }
  4. 内存管理优化表

    场景问题解决方案
    频繁切换内存泄漏在组件卸载时销毁实例
    大尺寸画布性能下降根据设备动态调整画布尺寸
    长时间使用卡顿定期清理历史记录

4. 业务逻辑集成实战

在实际业务场景中,签名通常需要与具体业务数据关联。以下是几种常见集成模式:

  1. 合同签署场景

    const handleContractSign = async (contractId: string) => { const { data: contract } = await fetchContract(contractId) if (contract.signed) { ElMessage.warning('该合同已签署') return } signatureDialog.value = true }
  2. 审批流程集成

    const submitApproval = () => { if (!signature.value) { ElMessage.warning('请先完成签名') return } const formData = new FormData() formData.append('signature', signature.value) formData.append('approvalData', JSON.stringify(approvalForm)) submitApproval(formData).then(() => { ElMessage.success('审批提交成功') }) }
  3. 多页签名处理

    const multiPageSign = () => { const pages = [/* 页面数据 */] const signatures: Record<string, string> = {} pages.forEach((page, index) => { signatures[`page_${index}`] = getSignatureForPage(page) }) saveMultiSignatures(signatures) }

5. 高级功能扩展

对于需要更专业签名体验的场景,可以考虑以下增强功能:

  1. 笔锋效果增强

    const signature = new SmoothSignature(canvas, { // ...其他配置 minWidth: 1, maxWidth: 8, smoothing: 0.7, velocityFilterWeight: 0.5 })
  2. 背景网格辅助线

    const drawGrid = (ctx: CanvasRenderingContext2D) => { const gridSize = 20 ctx.strokeStyle = '#e0e0e0' ctx.lineWidth = 0.5 for (let x = 0; x <= canvas.width; x += gridSize) { ctx.beginPath() ctx.moveTo(x, 0) ctx.lineTo(x, canvas.height) ctx.stroke() } for (let y = 0; y <= canvas.height; y += gridSize) { ctx.beginPath() ctx.moveTo(0, y) ctx.lineTo(canvas.width, y) ctx.stroke() } }
  3. 签名验证功能

    const verifySignature = (signatureData: string) => { return new Promise((resolve) => { // 调用后端验证接口 api.verifySignature(signatureData).then(resolve) }) }
  4. 压力感应支持���针对高端设备)

    const handlePointerMove = (e: PointerEvent) => { if (e.pressure) { const pressure = e.pressure * 5 signatureInstance.value?.setLineWidth(pressure) } }

在实际项目中,我发现横屏模式下签名区域更大,用户体验更好,但需要处理好旋转后的坐标转换问题。另外,对于医疗等特殊行业,签名数据需要加密存储,可以考虑使用Web Crypto API进行前端加密。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询