Vue3封装支持Base64导出的电子签名组件

默认支持签字回显,base64压缩,内存释放

传参支持禁用签字也就是查看,组件大小内置'small', 'default', 'large'三个大小

 

效果图

Vue3封装支持Base64导出的电子签名组件Vue3封装支持Base64导出的电子签名组件

Vue3封装支持Base64导出的电子签名组件

准备工作

组件内用到elementPlus,vue-esign组件,使用前提前安装好。

 

组件代码

<template> 	<!-- 签名容器 --> 	<div class="sign-container" > 		<div class="sign-preview" :class="[sizeClass, { 'has-sign': base64Img }]" @click="openDialog"> 			<img v-if="base64Img" :src="base64Img" class="preview-image" /> 			<div v-else class="placeholder"> 				<el-icon><EditPen /></el-icon> 				<span>点击签名</span> 			</div> 		</div>  		<!-- 签字弹窗 --> 		<el-dialog v-model="dialogVisible" title="电子签名" width="800px"> 			<vue-esign ref="esignRef" :width="800" :height="300" :lineWidth="4" :lineColor="'#000000'" :bgColor="'#ffffff'"  :id="uuid" />  			<template #footer> 				<el-button @click="dialogVisible = false">取消</el-button> 				<el-button @click="handleReset">清空</el-button> 				<el-button type="primary" @click="handleConfirm">确认</el-button> 			</template> 		</el-dialog> 	</div> </template>   <script setup> import { ref } from 'vue'; import { useMessage, useMessageBox } from '/@/hooks/message'; import { generateUUID } from '/@/utils/other';  import vueEsign from 'vue-esign';  // 生成组件唯一id const uuid = ref('id-' + generateUUID()); // 组件尺寸 const sizeClass = computed(() => `sign-size--${props.size}`);  const emit = defineEmits(['update:modelValue']); const props = defineProps({ 	modelValue: String, // v-model绑定     disabled: { 		type: Boolean, 		default: false, 	}, 	size: { 		type: String, 		default: 'default', 		validator: (v) => ['small', 'default', 'large'].includes(v), 	}, });  const dialogVisible = ref(false); const esignRef = ref(null); const base64Img = ref(props.modelValue);  // 打开弹窗时重置画布 const openDialog = () => {     if (props.disabled) return; 	dialogVisible.value = true; 	// handleReset(); };  // 清空画布(保留二次确认) const handleReset = async () => { 	try { 		await useMessageBox().confirm('此操作将清空签名,确定吗?'); 		esignRef.value?.reset(); 	} catch {} }; // 生成签名后压缩 const compressBase64 = (base64) => { 	return new Promise((resolve, reject) => { 		const img = new Image(); 		img.src = base64;  		img.onload = () => { 			// 创建canvas并设置缩放尺寸 			const canvas = document.createElement('canvas'); 			const ctx = canvas.getContext('2d');  			// 计算压缩后尺寸(取原图50%) 			const targetWidth = img.width * 0.5; 			const targetHeight = img.height * 0.5;  			// 设置画布尺寸 			canvas.width = targetWidth; 			canvas.height = targetHeight;  			// 绘制压缩图像 			ctx.drawImage(img, 0, 0, targetWidth, targetHeight);  			// 生成新base64(自动处理格式) 			const mimeType = base64.match(/data:(.*?);/)[1]; 			canvas.toBlob( 				(blob) => { 					const reader = new FileReader(); 					reader.onloadend = () => resolve(reader.result); 					reader.readAsDataURL(blob); 				}, 				mimeType, 				0.6 			); // 质量参数生效于JPEG/WebP格式 		};  		img.onerror = (e) => reject('图片加载失败'); 	}); }; // 确认签名 const handleConfirm = async () => { 	esignRef.value 		.generate() 		.then(async (res) => { 			base64Img.value = await compressBase64(res); 			// 验证压缩效果 			const originalSize = Math.round((res.length * 3) / 4 / 1024); 			const compressedSize = Math.round((base64Img.value.length * 3) / 4 / 1024); 			console.log(` 尺寸变化:${originalSize}KB → ${compressedSize}KB`); 			emit('update:modelValue', base64Img.value); 			dialogVisible.value = false; 		}) 		.catch(() => { 			base64Img.value = ''; 			emit('update:modelValue', ''); 			dialogVisible.value = false; 		}); };   // watch同步 watch( 	() => props.modelValue, 	async (val) => { 		if (!val) { 			base64Img.value = ''; 			esignRef.value?.reset(); 		} 		console.log(val);         base64Img.value = await compressBase64(val);  	} );    onBeforeUnmount(() => { 	// 释放canvas内存 	const canvas = esignRef.value?.$el.querySelector('canvas'); 	canvas.width = 0; 	canvas.height = 0; 	URL.revokeObjectURL(base64Img.value); // 释放Blob URL }); </script>   <style scoped lang="scss"> .sign-container { 	display: inline-block; 	cursor: pointer; } .sign-preview { 	border: 1px solid #dcdfe6; 	background: #fff; 	border-radius: 4px;  	&.sign-size--small { 		width: 120px; 		height: 60px; 	} 	&.sign-size--default { 		width: 180px; 		height: 90px; 	} 	&.sign-size--large { 		width: 240px; 		height: 120px; 	}  	&.has-sign { 		border-color: var(--el-color-primary); 	}  	.preview-image { 		width: 100%; 		height: 100%; 		object-fit: contain; 	}  	.placeholder { 		height: 100%; 		display: flex; 		align-items: center; 		justify-content: center; 		color: #909399;  		.el-icon { 			margin-right: 8px; 			font-size: 18px; 		} 	} } </style>

 

使用组件

<el-form ref="dataFormRef" :model="form" inline :rules="dataRules">     <el-form-item label="经办人签字" prop="signatureHandler" label-width="8em"> 		<!-- 签名组件 --> 		<signature-component v-model="form.signatureHandler" />     </el-form-item> </el-form>

 

注意事项

使用时将组件内的提示框替换为elementPlus官方的

generateUUID方法自行修改为生成UUID的方法,也可以去掉。

 

发表评论

评论已关闭。

相关文章