用AI写了一个界面好看兼容手机UA的二维码生成器,打开即用。只有前端,传送门
当然了,肯定有人不放心,那就公布源码:
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Href to QR Code Generator</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; color: #333; } .container { background: white; border-radius: 15px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); padding: 40px; max-width: 600px; width: 90%; text-align: center; } .title { font-size: 2.5em; font-weight: 600; color: #2c3e50; margin-bottom: 10px; background: linear-gradient(45deg, #3498db, #2ecc71); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .subtitle { color: #7f8c8d; margin-bottom: 40px; font-size: 1.1em; } .input-group { margin-bottom: 30px; } .url-input { width: 100%; padding: 15px 20px; font-size: 1.1em; border: 2px solid #e0e6ed; border-radius: 10px; outline: none; transition: all 0.3s ease; background: #f8f9fa; } .url-input:focus { border-color: #3498db; background: white; box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1); } .generate-btn { background: linear-gradient(45deg, #3498db, #2ecc71); color: white; padding: 15px 40px; font-size: 1.1em; font-weight: 600; border: none; border-radius: 10px; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3); } .generate-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4); } .generate-btn:active { transform: translateY(0); } .generate-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .result-area { margin-top: 40px; min-height: 200px; } .qr-container { display: none; flex-direction: column; align-items: center; gap: 20px; } .qr-container.show { display: flex; } #qrcode { border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); } .error-message { color: #e74c3c; background: #fdf2f2; padding: 15px; border-radius: 10px; border-left: 4px solid #e74c3c; margin-top: 20px; display: none; } .success-message { color: #27ae60; background: #f0f9f4; padding: 15px; border-radius: 10px; border-left: 4px solid #27ae60; margin-top: 20px; } .loading { display: none; color: #3498db; font-size: 1.1em; margin-top: 20px; } .loading::after { content: ""; animation: dots 1.5s steps(4, end) infinite; } @keyframes dots { 0%, 20% { content: ""; } 40% { content: "."; } 60% { content: ".."; } 80%, 100% { content: "..."; } } @media (max-width: 768px) { .container { padding: 30px 20px; margin: 20px; } .title { font-size: 2em; } .url-input { padding: 12px 15px; font-size: 1em; } .generate-btn { padding: 12px 30px; font-size: 1em; } } @media (max-width: 480px) { .title { font-size: 1.8em; } .container { width: 95%; padding: 25px 15px; } .input-group .examples-grid { grid-template-columns: 1fr !important; font-size: 0.75em !important; } } </style></head><body> <div class="container"> <h1 class="title">Href to QR Code Generator</h1> <p class="subtitle">将您的URL链接快速转换为二维码</p> <div class="input-group"> <input type="text" id="urlInput" class="url-input" placeholder="支持各种内容:网址、邮箱、电话、自定义协议、纯文本等" autocomplete="url" > <div style="margin-top: 10px; font-size: 0.9em; color: #7f8c8d; text-align: left;"> <div style="margin-bottom: 5px;"><strong>支持的格式示例:</strong></div> <div class="examples-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 5px; font-size: 0.8em;"> <div>• 网址: example.com</div> <div>• 邮箱: mailto:[email protected]</div> <div>• 电话: tel:+86-123-456-789</div> <div>• 短信: sms:+86-123-456-789</div> <div>• FTP: ftp://files.example.com</div> <div>• 自定义: myapp://action/data</div> </div> <div style="margin-top: 5px; font-size: 0.75em; color: #95a5a6;"> 提示:纯文本内容也可以生成二维码 </div> </div> </div> <button id="generateBtn" class="generate-btn"> 生成二维码 </button> <div class="result-area"> <div class="loading" id="loading"> 正在生成二维码 </div> <div class="qr-container" id="qrContainer"> <canvas id="qrcode"></canvas> <div class="success-message"> 二维码生成成功!扫描即可获取内容 </div> </div> <div class="error-message" id="errorMessage"></div> </div> <!-- 调试信息 --> <div style="margin-top: 20px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 0.9em; color: #666; text-align: left;"> <div><strong>系统状态:</strong></div> <div id="debugInfo">页面加载中...</div> <div id="networkInfo" style="margin-top: 5px; font-size: 0.8em;"></div> <div style="margin-top: 5px; font-size: 0.8em;"> 如果遇到问题,请按F12打开开发者工具查看控制台信息 </div> </div> </div> <!-- QRCode库加载管理 --> <script> // 多个CDN源列表(确保都是UMD格式) const qrCodeSources = [ 'https://cdn.jsdelivr.net/npm/[email protected]/build/qrcode.min.js', 'https://unpkg.com/[email protected]/build/qrcode.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/qrcode/1.5.3/qrcode.min.js' ]; let currentSourceIndex = 0; function loadQRCodeLibrary() { if (currentSourceIndex >= qrCodeSources.length) { console.error('所有CDN源都无法加载,尝试使用在线二维码服务'); const debugInfo = document.getElementById('debugInfo'); if (debugInfo) { debugInfo.textContent = '使用在线二维码服务 ⚠️'; } // 创建一个使用在线API的QRCode对象 window.QRCode = { toCanvas: function(canvas, text, options) { return new Promise((resolve, reject) => { const ctx = canvas.getContext('2d'); canvas.width = 280; canvas.height = 280; // 先显示加载中的背景 ctx.fillStyle = '#f8f9fa'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#666'; ctx.font = '16px Arial'; ctx.textAlign = 'center'; ctx.fillText('生成中...', canvas.width/2, canvas.height/2); // 多个在线二维码API备选 const apiUrls = [ `https://api.qrserver.com/v1/create-qr-code/?size=280x280&format=png&data=${encodeURIComponent(text)}`, `https://chart.googleapis.com/chart?chs=280x280&cht=qr&chl=${encodeURIComponent(text)}`, `https://qr-code-generator.now.sh/api?size=280&text=${encodeURIComponent(text)}` ]; let currentApiIndex = 0; function tryNextApi() { if (currentApiIndex >= apiUrls.length) { // 所有API都失败,绘制错误提示 ctx.fillStyle = '#fff5f5'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = '#e53e3e'; ctx.strokeRect(10, 10, canvas.width-20, canvas.height-20); ctx.fillStyle = '#e53e3e'; ctx.font = 'bold 18px Arial'; ctx.fillText('无法生成二维码', canvas.width/2, canvas.height/2-10); ctx.font = '14px Arial'; ctx.fillText('请检查网络连接', canvas.width/2, canvas.height/2+15); reject(new Error('所有在线二维码服务都不可用')); return; } const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = function() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); console.log(`在线二维码生成成功,API: ${currentApiIndex + 1}`); resolve(); }; img.onerror = function() { console.log(`API ${currentApiIndex + 1} 失败,尝试下一个`); currentApiIndex++; tryNextApi(); }; img.src = apiUrls[currentApiIndex]; } tryNextApi(); }); } }; clearTimeout(libraryLoadTimeout); // 清除超时计时器 const debugInfo2 = document.getElementById('debugInfo'); if (debugInfo2) { debugInfo2.textContent = '在线二维码服务已就绪 ✓'; } return; } const script = document.createElement('script'); script.src = qrCodeSources[currentSourceIndex]; script.onload = function() { console.log(`脚本从源 ${currentSourceIndex + 1} 加载完成:`, script.src); // 延迟检查QRCode是否真正可用 setTimeout(() => { if (typeof QRCode !== 'undefined' && QRCode.toCanvas) { console.log(`QRCode库验证成功,源 ${currentSourceIndex + 1}`); clearTimeout(libraryLoadTimeout); // 清除超时计时器 const debugInfo = document.getElementById('debugInfo'); if (debugInfo) { debugInfo.textContent = `QRCode库加载成功 (源${currentSourceIndex + 1}) ✓`; } } else { console.log(`QRCode库验证失败,源 ${currentSourceIndex + 1},尝试下一个源`); currentSourceIndex++; loadQRCodeLibrary(); } }, 100); }; script.onerror = function() { console.log(`源 ${currentSourceIndex + 1} 加载失败,尝试下一个源`); currentSourceIndex++; loadQRCodeLibrary(); }; document.head.appendChild(script); } // 设置较短的超时时间,如果CDN加载失败快速切换到在线API let libraryLoadTimeout = setTimeout(() => { if (typeof QRCode === 'undefined') { console.log('CDN加载超时,直接使用在线API服务'); currentSourceIndex = qrCodeSources.length; // 跳过所有CDN loadQRCodeLibrary(); // 这会触发在线API备用方案 } }, 3000); // 3秒超时 // 检测网络状态 function updateNetworkInfo() { const networkInfo = document.getElementById('networkInfo'); if (networkInfo) { if (navigator.onLine) { networkInfo.textContent = '🌐 网络连接正常'; networkInfo.style.color = '#28a745'; } else { networkInfo.textContent = '🔌 网络连接断开'; networkInfo.style.color = '#dc3545'; } } } // 监听网络状态变化 window.addEventListener('online', updateNetworkInfo); window.addEventListener('offline', updateNetworkInfo); // 初始化网络状态检测 updateNetworkInfo(); // 立即开始加载 loadQRCodeLibrary(); </script> <script> class QRGenerator { constructor() { this.urlInput = document.getElementById('urlInput'); this.generateBtn = document.getElementById('generateBtn'); this.qrContainer = document.getElementById('qrContainer'); this.qrCanvas = document.getElementById('qrcode'); this.errorMessage = document.getElementById('errorMessage'); this.loading = document.getElementById('loading'); // 检查QRCode库是否加载成功 this.checkLibraryLoaded(); this.initEventListeners(); } checkLibraryLoaded() { if (typeof QRCode === 'undefined') { console.error('QRCode library not loaded'); this.showError('二维码库加载失败,请检查网络连接或刷新页面重试'); this.generateBtn.disabled = true; return false; } return true; } initEventListeners() { this.generateBtn.addEventListener('click', () => this.handleGenerate()); this.urlInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.handleGenerate(); } }); // 实时输入验证 this.urlInput.addEventListener('input', () => { this.clearMessages(); }); } validateURL(url) { if (!url || url.trim() === '') { return { valid: false, message: '请输入URL链接或内容' }; } url = url.trim(); // 检查是否已经包含协议(包括自定义协议) const hasProtocol = url.includes('://') || url.includes(':'); if (!hasProtocol) { // 检查是否看起来像域名/网址 const domainPattern = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\..*$/; const ipPattern = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/; if (domainPattern.test(url) || ipPattern.test(url)) { // 看起来像域名或IP,自动添加https:// url = 'https://' + url; this.urlInput.value = url; // 更新输入框显示 } else { // 不像域名,可能是纯文本,直接使用 console.log('检测到非URL内容,将作为文本生成二维码:', url); } } // 对于包含协议的URL,进行基本验证 if (hasProtocol || url.startsWith('https://') || url.startsWith('http://')) { try { const urlObj = new URL(url); // 验证常见协议 const validProtocols = ['http:', 'https:', 'ftp:', 'ftps:', 'mailto:', 'tel:', 'sms:', 'data:']; const isCustomProtocol = !validProtocols.includes(urlObj.protocol); if (isCustomProtocol) { // 自定义协议,进行基本格式检查 if (urlObj.protocol && url.includes('://')) { console.log('检测到自定义协议:', urlObj.protocol); return { valid: true, url: url }; } } else { // 标准协议验证 if ((urlObj.protocol === 'http:' || urlObj.protocol === 'https:') && (!urlObj.hostname || urlObj.hostname.length < 1)) { return { valid: false, message: '请输入有效的网址' }; } } return { valid: true, url: url }; } catch (error) { // URL解析失败,可能是包含冒号的特殊格式 if (url.includes(':') && !url.includes('://')) { // 可能是 mailto:、tel: 等简单协议 return { valid: true, url: url }; } return { valid: false, message: '请输入有效的URL格式' }; } } // 对于纯文本或其他内容,只要不为空就认为有效 return { valid: true, url: url }; } showError(message) { this.errorMessage.textContent = message; this.errorMessage.style.display = 'block'; this.qrContainer.classList.remove('show'); } clearMessages() { this.errorMessage.style.display = 'none'; this.qrContainer.classList.remove('show'); } showLoading() { this.loading.style.display = 'block'; this.generateBtn.disabled = true; this.generateBtn.textContent = '生成中...'; } hideLoading() { this.loading.style.display = 'none'; this.generateBtn.disabled = false; this.generateBtn.textContent = '生成二维码'; } async generateQRCode(url) { try { // 再次检查库是否可用 if (!this.checkLibraryLoaded()) { return; } console.log('生成二维码,URL:', url); // 清空之前的canvas内容 const ctx = this.qrCanvas.getContext('2d'); ctx.clearRect(0, 0, this.qrCanvas.width, this.qrCanvas.height); // 生成二维码 await QRCode.toCanvas(this.qrCanvas, url, { width: 280, height: 280, color: { dark: '#2c3e50', light: '#ffffff' }, margin: 2, errorCorrectionLevel: 'M' }); console.log('二维码生成成功'); // 显示结果 this.qrContainer.classList.add('show'); } catch (error) { console.error('二维码生成失败:', error); // 提供更详细的错误信息 let errorMsg = '生成二维码时发生错误'; if (error.message) { errorMsg += ':' + error.message; } if (typeof QRCode === 'undefined') { errorMsg = '二维码库未正确加载,请刷新页面重试'; } else if (url.length > 2000) { errorMsg = 'URL长度过长,请使用较短的链接'; } this.showError(errorMsg); } } async handleGenerate() { const inputValue = this.urlInput.value; console.log('用户输入内容:', inputValue); const validation = this.validateURL(inputValue); console.log('验证结果:', validation); // 显示内容类型分析 if (validation.valid) { let contentType = '未知内容'; if (validation.url.includes('://')) { const protocol = validation.url.split('://')[0]; contentType = `${protocol.toUpperCase()} 协议`; } else if (validation.url.includes(':') && !validation.url.includes('://')) { const protocol = validation.url.split(':')[0]; contentType = `${protocol.toUpperCase()} 格式`; } else if (validation.url.includes('.') && !validation.url.includes(' ')) { contentType = 'URL 网址'; } else { contentType = '文本内容'; } console.log('内容类型:', contentType); } this.clearMessages(); if (!validation.valid) { this.showError(validation.message); return; } this.showLoading(); try { // 添加小延迟以显示加载状态 await new Promise(resolve => setTimeout(resolve, 300)); await this.generateQRCode(validation.url); } finally { this.hideLoading(); } } } // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', () => { // 等待QRCode库加载 let attempts = 0; const maxAttempts = 80; // 最多等待8秒 function initApp() { const debugInfo = document.getElementById('debugInfo'); if (typeof QRCode !== 'undefined') { console.log('QRCode库加载成功,初始化应用'); debugInfo.textContent = 'QRCode库已加载,应用已初始化 ✓'; new QRGenerator(); document.getElementById('urlInput').focus(); } else if (attempts < maxAttempts) { attempts++; debugInfo.textContent = `正在加载QRCode库... (${attempts}/${maxAttempts})`; setTimeout(initApp, 100); // 每100ms检查一次 } else { console.error('QRCode库加载超时'); debugInfo.textContent = 'QRCode库加载失败 ✗'; document.getElementById('errorMessage').textContent = '无法加载二维码库,请刷新页面重试'; document.getElementById('errorMessage').style.display = 'block'; } } initApp(); }); // 处理页面可见性变化,确保焦点管理 document.addEventListener('visibilitychange', () => { if (!document.hidden) { document.getElementById('urlInput').focus(); } }); </script></body></html>
评论 (0)