Browse Source

优化手写签名用户体验,添加放大签名弹窗和点击外部关闭功能

master
Default User 3 days ago
parent
commit
4829e95a71
  1. 437
      certificate.html

437
certificate.html

@ -348,35 +348,157 @@
}
#signatureCanvas {
border: 1px solid #ced4da;
border-radius: 4px;
background-color: #f8f9fa;
border: 2px solid #ced4da;
border-radius: 8px;
background-color: white;
cursor: crosshair;
width: 100%;
height: 200px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.signature-actions {
margin-top: 8px;
text-align: right;
margin-top: 12px;
display: flex;
justify-content: space-between;
align-items: center;
}
.signature-actions button {
padding: 6px 12px;
padding: 8px 16px;
background-color: #6c757d;
color: white;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
}
.signature-actions button:hover {
background-color: #5a6268;
}
.signature-actions .sign-hint {
font-size: 14px;
color: #666;
}
/* 签名显示样式 */
.signature-display img {
max-width: 200px;
max-height: 100px;
max-width: 250px;
max-height: 120px;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 8px;
background-color: white;
}
/* 签名弹窗样式 */
.signature-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 2000;
align-items: center;
justify-content: center;
padding: 20px;
}
.signature-modal.show {
display: flex;
}
.signature-modal-content {
background-color: white;
border-radius: 12px;
padding: 24px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.signature-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.signature-modal-header h3 {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0;
}
.signature-modal-close {
background: none;
border: none;
font-size: 24px;
color: #666;
cursor: pointer;
}
#signatureModalCanvas {
border: 2px solid #ced4da;
border-radius: 8px;
background-color: white;
cursor: crosshair;
width: 100%;
height: 300px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.signature-modal-actions {
margin-top: 20px;
display: flex;
gap: 12px;
}
.signature-modal-actions button {
flex: 1;
padding: 12px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s ease;
}
.signature-modal-actions .cancel-btn {
background-color: #6c757d;
color: white;
}
.signature-modal-actions .clear-btn {
background-color: #ffc107;
color: #212529;
}
.signature-modal-actions .confirm-btn {
background-color: #28a745;
color: white;
}
.signature-modal-actions .cancel-btn:hover {
background-color: #5a6268;
}
.signature-modal-actions .clear-btn:hover {
background-color: #e0a800;
}
.signature-modal-actions .confirm-btn:hover {
background-color: #218838;
}
/* 响应式设计 */
@ -455,13 +577,30 @@
<div class="form-group">
<label>手写签名:</label>
<div class="signature-container">
<canvas id="signatureCanvas" width="350" height="200"></canvas>
<canvas id="signatureCanvas" width="350" height="200" onclick="openSignatureModal()"></canvas>
<div class="signature-actions">
<span class="sign-hint">点击签名区域开始签名</span>
<button type="button" onclick="clearSignature()">清除</button>
</div>
</div>
<input type="hidden" id="signature" name="signature">
</div>
<!-- 签名弹窗 -->
<div class="signature-modal" id="signatureModal">
<div class="signature-modal-content">
<div class="signature-modal-header">
<h3>手写签名</h3>
<button type="button" class="signature-modal-close" onclick="closeSignatureModal()">&times;</button>
</div>
<canvas id="signatureModalCanvas" width="400" height="300"></canvas>
<div class="signature-modal-actions">
<button type="button" class="cancel-btn" onclick="closeSignatureModal()">取消</button>
<button type="button" class="clear-btn" onclick="clearModalSignature()">清除</button>
<button type="button" class="confirm-btn" onclick="confirmSignature()">确认</button>
</div>
</div>
</div>
<div class="form-actions" style="margin-top: 20px;">
<button type="submit" form="certificateForm" class="submit-btn">提交信息</button>
</div>
@ -481,6 +620,8 @@
<script>
// 签名画布相关变量
let canvas, ctx, isDrawing = false;
// 弹窗签名画布相关变量
let modalCanvas, modalCtx, modalIsDrawing = false;
// 获取或创建会话标识
function getSessionId() {
@ -616,6 +757,11 @@
</div>
</div>
`;
// 显示二维码
const sessionId = getSessionId();
const viewUrl = `http://8.137.125.67:3008/view.html?sessionId=${encodeURIComponent(sessionId)}`;
displayQRCodeOnPage(viewUrl);
}
container.innerHTML = html;
@ -625,28 +771,189 @@
// 初始化签名画布
function initSignatureCanvas() {
// 初始化主画布
canvas = document.getElementById('signatureCanvas');
if (!canvas) return;
if (canvas) {
ctx = canvas.getContext('2d');
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 清除画布
clearSignature();
}
ctx = canvas.getContext('2d');
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
// 初始化弹窗画布
initModalSignatureCanvas();
}
// 初始化弹窗签名画布
function initModalSignatureCanvas() {
modalCanvas = document.getElementById('signatureModalCanvas');
if (!modalCanvas) return;
modalCtx = modalCanvas.getContext('2d');
modalCtx.strokeStyle = '#000';
modalCtx.lineWidth = 3; // 弹窗中使用更粗的线条
modalCtx.lineCap = 'round';
modalCtx.lineJoin = 'round';
// 清除画布
clearSignature();
clearModalSignature();
// 添加鼠标事件
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
modalCanvas.addEventListener('mousedown', startModalDrawing);
modalCanvas.addEventListener('mousemove', drawModal);
modalCanvas.addEventListener('mouseup', stopModalDrawing);
modalCanvas.addEventListener('mouseout', stopModalDrawing);
// 添加触摸事件(支持移动设备)
canvas.addEventListener('touchstart', startDrawingTouch);
canvas.addEventListener('touchmove', drawTouch);
canvas.addEventListener('touchend', stopDrawing);
modalCanvas.addEventListener('touchstart', startModalDrawingTouch);
modalCanvas.addEventListener('touchmove', drawModalTouch);
modalCanvas.addEventListener('touchend', stopModalDrawing);
}
// 打开签名弹窗
function openSignatureModal() {
const modal = document.getElementById('signatureModal');
if (modal) {
modal.classList.add('show');
// 清除弹窗画布
clearModalSignature();
// 添加点击外部区域关闭弹窗的事件
setTimeout(() => {
modal.addEventListener('click', handleModalClick);
}, 100);
}
}
// 处理弹窗点击事件
function handleModalClick(e) {
const modal = document.getElementById('signatureModal');
const modalContent = document.querySelector('.signature-modal-content');
// 如果点击的是弹窗外部区域,关闭弹窗
if (e.target === modal) {
closeSignatureModal();
}
}
// 关闭签名弹窗
function closeSignatureModal() {
const modal = document.getElementById('signatureModal');
if (modal) {
// 移除点击事件监听器
modal.removeEventListener('click', handleModalClick);
modal.classList.remove('show');
}
}
// 开始弹窗签名
function startModalDrawing(e) {
modalIsDrawing = true;
const rect = modalCanvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
modalCtx.beginPath();
modalCtx.moveTo(x, y);
}
// 弹窗签名
function drawModal(e) {
if (!modalIsDrawing) return;
const rect = modalCanvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
modalCtx.lineTo(x, y);
modalCtx.stroke();
}
// 停止弹窗签名
function stopModalDrawing() {
modalIsDrawing = false;
}
// 触摸事件处理(弹窗)
function startModalDrawingTouch(e) {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
modalCanvas.dispatchEvent(mouseEvent);
}
function drawModalTouch(e) {
e.preventDefault();
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
modalCanvas.dispatchEvent(mouseEvent);
}
// 清除弹窗签名
function clearModalSignature() {
if (!modalCanvas || !modalCtx) return;
modalCtx.clearRect(0, 0, modalCanvas.width, modalCanvas.height);
// 填充背景色
modalCtx.fillStyle = '#f8f9fa';
modalCtx.fillRect(0, 0, modalCanvas.width, modalCanvas.height);
}
// 确认签名
function confirmSignature() {
if (!modalCanvas) return;
// 检查画布是否有内容
const imageData = modalCtx.getImageData(0, 0, modalCanvas.width, modalCanvas.height);
const data = imageData.data;
let hasContent = false;
// 遍历像素数据,检查是否有非背景色的像素
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const a = data[i + 3];
// 检查是否不是透明像素且不是背景色(#f8f9fa)
if (a > 0 && !(r === 248 && g === 249 && b === 250)) {
hasContent = true;
break;
}
}
// 只有当画布有内容时才保存签名
if (hasContent) {
// 将弹窗画布内容复制到主画布
if (canvas && ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#f8f9fa';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 调整比例,确保签名在主画布中合适显示
const scale = Math.min(canvas.width / modalCanvas.width, canvas.height / modalCanvas.height);
const offsetX = (canvas.width - modalCanvas.width * scale) / 2;
const offsetY = (canvas.height - modalCanvas.height * scale) / 2;
ctx.save();
ctx.translate(offsetX, offsetY);
ctx.scale(scale, scale);
ctx.drawImage(modalCanvas, 0, 0);
ctx.restore();
}
// 保存签名到隐藏字段
saveSignature();
// 关闭弹窗
closeSignatureModal();
} else {
alert('请先进行签名');
}
}
function startDrawing(e) {
@ -781,8 +1088,8 @@
// 生成查看页面的URL,包含会话ID
const viewUrl = `http://8.137.125.67:3008/view.html?sessionId=${encodeURIComponent(sessionId)}`;
// 生成二维码
generateQRCode(viewUrl);
// 生成二维码并在页面下方显示
displayQRCodeOnPage(viewUrl);
} else {
alert('没有找到可导出的信息');
}
@ -793,6 +1100,81 @@
});
}
// 在页面下方显示二维码
function displayQRCodeOnPage(url) {
// 检查是否已有二维码容器,如有则先移除
const existingContainer = document.getElementById('qrCodeDisplay');
if (existingContainer) {
existingContainer.remove();
}
// 创建二维码容器
const qrContainer = document.createElement('div');
qrContainer.id = 'qrCodeDisplay';
qrContainer.style.cssText = `
margin: 20px 16px;
padding: 16px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
text-align: center;
`;
// 创建二维码标题
const qrTitle = document.createElement('h3');
qrTitle.style.cssText = `
color: #28a745;
margin-bottom: 15px;
font-size: 18px;
`;
qrTitle.innerHTML = '📋 合格证二维码';
// 创建二维码图片
const qrImage = document.createElement('img');
qrImage.id = 'qrCodeImage';
qrImage.style.cssText = `
max-width: 250px;
max-height: 250px;
margin-bottom: 15px;
background-color: white;
padding: 10px;
border-radius: 8px;
border: 1px solid #e0e0e0;
`;
// 创建提示文字
const qrText = document.createElement('div');
qrText.style.cssText = `
color: #666;
font-size: 14px;
text-align: center;
line-height: 1.5;
`;
qrText.innerHTML = '<p style="margin-bottom: 8px;">扫描二维码查看完整信息</p><p>长按保存二维码</p>';
// 使用Google Charts API生成二维码
const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${encodeURIComponent(url)}`;
qrImage.src = qrCodeUrl;
// 组装容器
qrContainer.appendChild(qrTitle);
qrContainer.appendChild(qrImage);
qrContainer.appendChild(qrText);
// 找到合适的位置插入二维码容器
// 在support元素之前插入
const supportElement = document.querySelector('.support');
if (supportElement) {
supportElement.parentNode.insertBefore(qrContainer, supportElement);
} else {
// 如果没有support元素,就在container末尾插入
const container = document.querySelector('.container');
if (container) {
container.appendChild(qrContainer);
}
}
}
// 重置提交状态
function resetSubmissionStatus() {
const sessionId = getSessionId();
@ -1014,6 +1396,11 @@
sessionStorage.setItem('certificateSubmitted_' + sessionId, 'true');
displayCertificate(data.certificate);
alert('提交成功!合格证信息已保存到数据库。');
// 提交成功后自动生成二维码
setTimeout(() => {
exportCertificate();
}, 1000);
} else {
// 无签名,标记为部分完成,保持可编辑状态
sessionStorage.setItem('certificatePartiallyCompleted_' + sessionId, 'true');

Loading…
Cancel
Save