电子签名二维码实现笔记
功能概述
电子签名二维码功能主要实现PC端生成二维码,移动端扫码后进行签名,并将签名结果回传给PC端的过程。
技术要点
- 二维码生成:使用QRCode.js库
- 状态管理:Vue3 的响应式API
- 环境适配:开发和生产环境的处理
- URL参数处理:使用URLSearchParams API
- 轮询机制:使用setInterval进行状态检查
核心实现
1. 二维码生成组件
vue
<template>
<div class="signature-container">
<a-button @click="handleSignatureClick">
<template #icon><SignatureIcon /></template>
电子签名
</a-button>
<a-modal v-model:open="showQRModal" title="手机扫码签名" :footer="null" @cancel="handleCancel">
<div class="qr-container">
<img v-if="qrCodeUrl" :src="qrCodeUrl" alt="签名二维码" />
<p>请使用手机扫描二维码进行签名</p>
</div>
</a-modal>
</div>
</template>
2. URL生成与处理
javascript
// 环境适配处理
const getLocalIP = () => {
if (import.meta.env.DEV) {
return '192.168.2.16'; // 开发环境IP
}
return window.location.hostname; // 生产环境域名
};
// 生成签名页面URL
const getSignaturePageUrl = () => {
const currentUrl = window.location.href;
const currentPort = window.location.port;
const baseUrl = `http://${getLocalIP()}${currentPort ? ':' + currentPort : ''}`;
return `${baseUrl}/m/sign?sid=${props.sid}&callback=${encodeURIComponent(currentUrl)}`;
};
sid:签名会话ID,确保签名的唯一性。进行组件调用时,需要传递sid
参数。
javascript
const props = defineProps({
sid: {
type: String,
required: true
}
});
const emit = defineEmits(['signature-complete']);
3. 二维码生成与轮询检查
javascript
// 生成二维码
const handleSignatureClick = async () => {
showQRModal.value = true;
const signatureUrl = getSignaturePageUrl();
try {
// 生成二维码
qrCodeUrl.value = await QRCode.toDataURL(signatureUrl, {
errorCorrectionLevel: 'H',
margin: 1,
width: 200,
type: 'image/png',
});
startPolling();
} catch (err) {
console.error('生成二维码失败:', err);
}
};
// 检查 URL 参数
const checkSignatureUrl = () => {
const urlParams = new URLSearchParams(window.location.search);
const signatureUrl = urlParams.get('signatureUrl');
if (signatureUrl) {
// 发送签名URL给父组件
emit('signature-complete', decodeURIComponent(signatureUrl));
// 清除URL参数
const newUrl = window.location.pathname + window.location.search.replace(/[?&]signatureUrl=[^&]*/, '');
window.history.replaceState({}, '', newUrl);
// 关闭二维码弹窗
showQRModal.value = false;
// 清除检查间隔
if (checkInterval) {
clearInterval(checkInterval);
checkInterval = null;
}
}
};
技术细节解析
1. URL参数构建
- sid: 签名会话ID,确保签名的唯一性
- callback: 编码后的回调地址,用于签名完成后跳转
- 环境判断: 通过
import.meta.env.DEV
区分开发和生产环境
2. 二维码配置
javascript
const qrCodeConfig = {
errorCorrectionLevel: 'H', // 高容错率
margin: 1, // 最小边距
width: 200, // 合适的尺寸
type: 'image/png', // 图片格式
};
3. 轮询机制
javascript
const startPolling = () => {
if (!checkInterval) {
checkInterval = setInterval(checkSignatureUrl, 1000);
}
};
const stopPolling = () => {
if (checkInterval) {
clearInterval(checkInterval);
checkInterval = null;
}
};
Q & A
1. 为什么要区分开发和生产环境的IP处理?
javascript
const getLocalIP = () => {
if (import.meta.env.DEV) {
return '192.168.2.17'; // 开发环境需要固定IP便于移动端访问
}
return window.location.hostname; // 生产环境使用实际域名
};
- 开发环境需要固定IP便于移动端访问
- 生产环境使用实际域名确保可靠性
- 便于开发调试和生产部署
2. 如何处理URL参数的安全性?
javascript
const getSignaturePageUrl = () => {
const currentUrl = window.location.href;
// 对回调URL进行编码
return `${baseUrl}/m/sign?sid=${props.sid}&callback=${encodeURIComponent(currentUrl)}`;
};
- 使用encodeURIComponent编码参数
- 避免URL注入风险
- 确保特殊字符正确传递
3. 轮询检查的优化方案?
javascript
// 优化的轮询检查
const checkSignatureUrl = () => {
const urlParams = new URLSearchParams(window.location.search);
const signatureUrl = urlParams.get('signatureUrl');
if (signatureUrl) {
emit('signature-complete', decodeURIComponent(signatureUrl));
cleanupAfterSignature();
}
};
const cleanupAfterSignature = () => {
stopPolling();
showQRModal.value = false;
clearUrlParameters();
};
- 合理的轮询间隔
- 及时清理资源
- 完善的清理机制
4. 如何确保二维码的可用性?
javascript
const generateQRCode = async (url) => {
try {
return await QRCode.toDataURL(url, {
errorCorrectionLevel: 'H', // 高容错率
margin: 1,
width: 200,
type: 'image/png',
});
} catch (err) {
console.error('二维码生成失败:', err);
throw new Error('二维码生成失败');
}
};
- 使用高容错级别
- 合适的尺寸设置
- 完善的错误处理
5. 组件生命周期管理的重要性?
javascript
onMounted(() => {
checkSignatureUrl(); // 初始检查
});
onUnmounted(() => {
stopPolling(); // 清理资源
});
- 初始化检查
- 资源清理
- 防止内存泄漏