使用Vue3封装实现支持Base64导出的电子签名组件

 更新时间:2025年03月31日 08:57:23   作者:脆  
这篇文章主要为大家详细介绍了使用Vue3封装实现支持Base64导出的电子签名组件的相关知识,文中的示例代码讲解详细,有需要的小伙伴可以了解下

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

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

效果图

准备工作

组件内用到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的方法,也可以去掉。

到此这篇关于使用Vue3封装实现支持Base64导出的电子签名组件的文章就介绍到这了,更多相关Vue3电子签名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解Vue 路由组件传参的 8 种方式

    详解Vue 路由组件传参的 8 种方式

    这篇文章主要介绍了Vue 路由组件传参的 8 种方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • vue导出excel表格并支持样式及行高修改

    vue导出excel表格并支持样式及行高修改

    这篇文章主要为大家介绍了vue导出excel表格并支持样式及行高修改,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • vue在install时node-sass@4.14.1 postinstall:node scripts/build.js错误解决

    vue在install时node-sass@4.14.1 postinstall:node scripts/buil

    最近在npm install 的时候遇到了个问题,所以给大家总结下,下面这篇文章主要给大家介绍了关于vue在install时node-sass@4.14.1 postinstall:node scripts/build.js错误的解决方法,需要的朋友可以参考下
    2023-05-05
  • 使用vue-element-admin框架从后端动态获取菜单功能的实现

    使用vue-element-admin框架从后端动态获取菜单功能的实现

    &#8203; vue-element-admin是一个纯前端的框架,左侧菜单是根据路由生成的。实际开发中经常需要根据当前登陆人员的信息从后端获取菜单进行展示,本文将详细介绍如何实现该功能
    2021-04-04
  • 前端vue中实现文件下载的几种方法总结

    前端vue中实现文件下载的几种方法总结

    这篇文章主要介绍了前端vue中实现文件下载的几种方法总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • Vue + Echarts页面内存占用高的问题解决方案

    Vue + Echarts页面内存占用高的问题解决方案

    点击左侧的菜单可以切换不同的看板,有些看板页面中的报表比较多,用户多次切换后页面的内存占用可以上升为GB级,严重时导致页面内存溢出,使得页面崩溃,极大影响了用户体验,本文给大家介绍Vue + Echarts页面内存占用高的问题解决方案,感兴趣的朋友一起看看吧
    2024-02-02
  • 深入了解Vue3组件传值方式

    深入了解Vue3组件传值方式

    学习过 vue2 的宝子们肯定知道,组件传值是 vue 项目开发过程中必不可少的功能场景,在 vue2 里面有很多传值的方式。今天就来和大家讲讲Vue3的组件传值方式,需要的可以参考一下
    2022-07-07
  • vue中使用微信公众号js-sdk踩坑记录

    vue中使用微信公众号js-sdk踩坑记录

    这篇文章主要介绍了vue中使用微信公众号js-sdk踩坑记录,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • vue 2.1.3 实时显示当前时间,每秒更新的方法

    vue 2.1.3 实时显示当前时间,每秒更新的方法

    今天小编就为大家分享一篇vue 2.1.3 实时显示当前时间,每秒更新的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • element-ui中el-cascader动态加载和默认值详解

    element-ui中el-cascader动态加载和默认值详解

    vue+elementUI项目中el-cascader级联选择器使用频率非常高,下面这篇文章主要给大家介绍了关于element-ui中el-cascader动态加载和默认值的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05

最新评论