电子签名实际运用笔记
功能场景
在招标项目审核页面中,电子签名功能主要用于法人签名确认环节,支持两种签名方式:
- 文件上传
- 扫码电子签名
组件集成
1. 组件引入和基础配置
vue
<template>
<a-form-item label="法人签名" name="auditSignatureImage">
<div class="signature-actions">
<!-- 扫码签名组件 -->
<electronic-signature
:sid="projectId"
@signature-complete="handleSignatureComplete"
/>
<!-- 下载按钮 -->
<div class="upload-container">
<a-button class="download-btn" @click="downloadSignature">
<DownloadOutlined />
下载签名文件
</a-button>
</div>
</div>
</a-form-item>
</template>
2. 签名结果处理
javascript
// 处理签名完成回调
const handleSignatureComplete = async (signatureUrl) => {
if (signatureUrl) {
formState.auditSignatureImage = signatureUrl;
// 更新文件列表显示
auditSignatureImageList.value = [{
uid: '-1',
name: 'signature.png',
status: 'done',
url: signatureUrl
}];
}
};
// 获取URL参数和签名
/**
* 获取项目ID和签名URL
* @returns {Object} 包含projectId和signatureUrl的对象
* @description
* 1. 从URL中解析projectId参数
* 2. 从URL hash中解析签名URL
* 3. 如果hash以#signature=开头,则解码并返回签名URL,否则返回空字符串
*/
const getCleanProjectIdAndSignature = () => {
// 创建URLSearchParams对象解析URL参数
const urlParams = new URLSearchParams(window.location.search);
// 获取projectId参数值
const projectId = urlParams.get('projectId');
// 获取URL hash部分
const hash = window.location.hash;
// 解析签名URL - 如果hash以#signature=开头则解码,否则返回空字符串
const signatureUrl = hash.startsWith('#signature=')
? decodeURIComponent(hash.replace('#signature=', ''))
: '';
return { projectId, signatureUrl };
};
// 处理签名完成
const handleSignatureComplete = async (signatureUrl) => {
console.log('signatureUrl', signatureUrl);
if (signatureUrl) {
formState.auditSignatureImage = signatureUrl;
// 更新文件列表显示
auditSignatureImageList.value = [{
uid: '-1',
name: 'signature.png',
status: 'done',
url: signatureUrl
}];
}
};
3. 文件上传处理
javascript
// 文件上传前校验
const beforeUpload = (file) => {
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('文件必须小于10MB!');
}
return isLt10M;
};
// 自定义上传实现
const customUploadName = async ({ file, onSuccess }) => {
const formData = new FormData();
formData.append('file', file);
const res = await uploadFileAPI(formData);
formState.auditSignatureImage = res.data.data;
auditSignatureImage.value = [{
uid: '-1',
name: file.name,
status: 'done',
url: res.data
}];
message.success('文件上传成功');
onSuccess();
};
业务流程处理
1. 初始化和数据加载
javascript
onMounted(() => {
// 获取项目详情
getProjectDetail(projectId);
// 处理签名URL
if (signatureUrl) {
formState.auditSignatureImage = signatureUrl;
auditSignatureImageList.value = [{
uid: '-1',
name: signatureUrl.split('/').pop(),
status: 'done',
url: signatureUrl
}];
// 清理URL参数
window.history.replaceState({}, '',
`${window.location.pathname}?projectId=${projectId}`);
}
});
2. 审核流程集成
javascript
// 同意审核
const handleAgree = async () => {
const data = {
id: formState.id,
auditStatus: 2,
auditType: 11,
auditResult: formState.auditOpinion,
projectRoundId: Number(projectId),
auditSignatureImage: formState.auditSignatureImage
}
const res = await agreeOrRejectProjectAPI(data);
router.replace('/ExamineProject');
};
// 拒绝审核
const handleRefuse = async () => {
const params = {
id: formState.id,
auditStatus: 3,
auditType: 11,
auditResult: formState.auditOpinion,
projectRoundId: Number(projectId),
auditSignatureImage: formState.auditSignatureImage
}
const res = await agreeOrRejectProjectAPI(params);
router.replace('/ExamineProject');
};
样式优化
css
.signature-actions {
gap: 16px;
align-items: center;
}
.signature-preview {
margin-top: 16px;
display: flex;
align-items: center;
}
.upload-container {
display: flex;
gap: 16px;
align-items: center;
}
.download-btn {
margin-right: 8px;
}
注意事项
- 状态管理
javascript
// 按钮禁用状态控制
const isButtonDisabled = computed(() => {
return formState.projectStatusId !== 3;
});
- 文件处理
javascript
// 文件删除处理
const handleRemove = async (file) => {
const filePath = file.url || formState.auditSignatureImage;
if (filePath) {
await deleteFileAPI(filePath);
formState.auditSignatureImage = '';
message.success('文件删除成功');
}
return true;
};
// 文件下载处理
const downloadSignature = () => {
handleFileDownload(formState.auditSignatureImage);
};
- URL参数处理
javascript
// 清理URL参数
if (signatureUrl) {
window.history.replaceState({}, '',
`${window.location.pathname}?projectId=${projectId}`);
}
最佳实践
- 组件解耦
- 将签名功能封装为独立组件
- 通过事件通信处理签名结果
- 异常处理
- 文件大小限制
- 上传失败处理
- 签名结果验证
- 用户体验
- 提供多种签名方式
- 实时预览
- 清晰的操作反馈