Vue3弹窗集成smooth-signature电子签名全流程实战去年在开发一个医疗审批系统时遇到一个棘手需求医生需要在移动设备上完成处方电子签名。经过多次迭代最终采用Vue3 smooth-signature的方案完美解决了这个问题。本文将分享如何在Dialog弹窗中实现专业级电子签名功能特别针对移动端横竖屏切换场景。1. 环境准备与基础集成首先创建一个全新的Vue3项目如果已有项目可跳过此步。推荐使用Vite作为构建工具它能提供更快的开发体验npm create vitelatest 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 classsignature-container el-dialog v-modeldialogVisible title电子签名 :fullscreenisFullscreen closedhandleDialogClose div classcanvas-wrapper canvas refsignatureCanvas/canvas /div div classaction-buttons el-button clickhandleClear清除/el-button el-button clickhandleUndo撤销/el-button el-button clicktoggleOrientation {{ isFullscreen ? 竖屏 : 横屏 }} /el-button el-button typeprimary clickhandleSave保存/el-button /div /el-dialog /div /template script setup langts 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 refHTMLCanvasElement | null(null) const signatureInstance refSmoothSignature | 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; } /style3. 移动端适配与性能优化移动端环境下需要特别注意以下几点触摸事件处理const handleTouchMove (e: TouchEvent) { e.preventDefault() } onMounted(() { document.addEventListener(touchmove, handleTouchMove, { passive: false }) }) onUnmounted(() { document.removeEventListener(touchmove, handleTouchMove) })横竖屏切换检测const checkOrientation () { isFullscreen.value window.innerHeight window.innerWidth } onMounted(() { window.addEventListener(resize, checkOrientation) checkOrientation() })画布清晰度保障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. 业务逻辑集成实战在实际业务场景中签名通常需要与具体业务数据关联。以下是几种常见集成模式合同签署场景const handleContractSign async (contractId: string) { const { data: contract } await fetchContract(contractId) if (contract.signed) { ElMessage.warning(该合同已签署) return } signatureDialog.value true }审批流程集成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(审批提交成功) }) }多页签名处理const multiPageSign () { const pages [/* 页面数据 */] const signatures: Recordstring, string {} pages.forEach((page, index) { signatures[page_${index}] getSignatureForPage(page) }) saveMultiSignatures(signatures) }5. 高级功能扩展对于需要更专业签名体验的场景可以考虑以下增强功能笔锋效果增强const signature new SmoothSignature(canvas, { // ...其他配置 minWidth: 1, maxWidth: 8, smoothing: 0.7, velocityFilterWeight: 0.5 })背景网格辅助线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() } }签名验证功能const verifySignature (signatureData: string) { return new Promise((resolve) { // 调用后端验证接口 api.verifySignature(signatureData).then(resolve) }) }压力感应支持针对高端设备const handlePointerMove (e: PointerEvent) { if (e.pressure) { const pressure e.pressure * 5 signatureInstance.value?.setLineWidth(pressure) } }在实际项目中我发现横屏模式下签名区域更大用户体验更好但需要处理好旋转后的坐标转换问题。另外对于医疗等特殊行业签名数据需要加密存储可以考虑使用Web Crypto API进行前端加密。