QRFlow

在线二维码生成器 · 自定义颜色 · 嵌入 Logo · 批量导出

点击上传 Logo 图片

实时预览
处理中…0%
(function() { 'use strict'; // ========== i18n 初始化 ========== var _lang = localStorage.getItem('qriflow_lang') || (navigator.language.startsWith('zh') ? 'zh' : 'en'); var translations = { zh: { appName: 'QRFlow', headerDesc: '在线二维码生成器 · 自定义颜色 · 嵌入 Logo · 批量导出', inputLabel: '输入文本 / URL', sizeLabel: '尺寸', fgLabel: '前景色', bgLabel: '背景色', logoLabel: '嵌入 Logo(选填)', batchLabel: '批量生成模式', uploadLogo: '点击上传 Logo 图片', uploadCSV: '或上传 CSV 文件', batchPlaceholder: '每行一个文本或 URL,或粘贴 CSV 格式\n名称,内容\n例如:\nGitHub,https://github.com\nTwitter,https://twitter.com', downloadPng: '下载 PNG', downloadSvg: '下载 SVG', downloadZip: '下载全部 ZIP', removeLogo: '移除', previewPlaceholder: '输入内容后自动生成二维码', generated: '已生成 {count} 个二维码', processing: '处理中…', privacy: '隐私政策', copyright: '© 2025 QRFlow', pleaseInput: '请输入内容', pleaseGenerate: '请先生成二维码', downloadedPng: '已下载 PNG', downloadedZip: '已下载 ZIP', genFailed: '生成失败: ', logoLoadError: 'Logo 加载失败', csvParseError: 'CSV 解析失败', svgNotSupport: 'SVG 导出暂不支持,请使用 PNG', switchLang: 'EN' }, en: { appName: 'QRFlow', headerDesc: 'Online QR Code Generator · Custom Colors · Logo Embed · Batch Export', inputLabel: 'Text / URL', sizeLabel: 'Size', fgLabel: 'Foreground', bgLabel: 'Background', logoLabel: 'Embed Logo (optional)', batchLabel: 'Batch Mode', uploadLogo: 'Click to upload logo image', uploadCSV: 'Or upload CSV file', batchPlaceholder: 'One text/URL per line, or CSV format\nname,content\ne.g.:\nGitHub,https://github.com\nTwitter,https://twitter.com', downloadPng: 'Download PNG', downloadSvg: 'Download SVG', downloadZip: 'Download All ZIP', removeLogo: 'Remove', previewPlaceholder: 'Enter content to generate QR code', generated: 'Generated {count} QR codes', processing: 'Processing…', privacy: 'Privacy Policy', copyright: '© 2025 QRFlow', pleaseInput: 'Please enter content', pleaseGenerate: 'Please generate a QR code first', downloadedPng: 'PNG Downloaded', downloadedZip: 'ZIP Downloaded', genFailed: 'Generation failed: ', logoLoadError: 'Logo load failed', csvParseError: 'CSV parse failed', svgNotSupport: 'SVG export not supported yet. Please use PNG.', switchLang: '中' } }; window.__ = function(key, vars) { var t = translations[_lang] || translations.zh; var str = t[key] || translations.zh[key] || key; if (vars) { for (var k in vars) { str = str.replace('{'+k+'}', vars[k]); } } return str; }; window.setLang = function(lang) { if (translations[lang]) { _lang = lang; localStorage.setItem('qriflow_lang', lang); applyI18n(); } }; window.getLang = function() { return _lang; }; function applyI18n() { // data-i18n 文本替换 document.querySelectorAll('[data-i18n]').forEach(function(el) { var key = el.getAttribute('data-i18n'); el.textContent = __(key); }); // data-i18n-placeholder 替换 document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el) { var key = el.getAttribute('data-i18n-placeholder'); el.placeholder = __(key); }); // 特殊处理进度文字 var pt = document.getElementById('progressText'); if (pt) pt.textContent = __('processing'); // 处理批量计数区域 var bc = document.getElementById('batchCount'); if (bc) { var parentDiv = bc.parentNode; var count = parseInt(bc.textContent) || 0; parentDiv.innerHTML = __('generated', {count: count}); } // 语言切换按钮文字 var langBtn = document.getElementById('langToggleBtn'); if (langBtn) langBtn.textContent = __('switchLang'); } // 语言选择器(追加到 footer) function addLangToggle() { var footer = document.querySelector('.footer'); if (!footer) return; var btn = document.createElement('button'); btn.id = 'langToggleBtn'; btn.className = 'lang-toggle'; btn.textContent = __('switchLang'); btn.addEventListener('click', function() { var newLang = _lang === 'zh' ? 'en' : 'zh'; setLang(newLang); }); footer.appendChild(btn); } // ========== 全局错误捕获 ========== window.onerror = function(msg, url, line, col, error) { console.error('全局错误:', msg, url, line, col); var toast = document.getElementById('toast'); if (toast) { toast.textContent = '发生错误,请刷新页面重试'; toast.classList.add('show'); setTimeout(function(){toast.classList.remove('show');}, 3000); } return true; }; // ========== 修复 FileReader 无 catch ========== // 重新绑定 logo 上传事件(覆盖原监听) var logoInput = document.getElementById('logoInput'); var oldLogoListener = null; if (logoInput) { // 移除原有的所有 change 监听(使用新函数) // 由于原有是匿名函数,我们通过 clone 替换 var newLogoInput = logoInput.cloneNode(true); logoInput.parentNode.replaceChild(newLogoInput, logoInput); logoInput = newLogoInput; logoInput.addEventListener('change', function() { if (this.files.length > 0) { var r = new FileReader(); r.onload = function(e) { try { logoDataURL = e.target.result; var logoImg = document.getElementById('logoImg'); var logoPreview = document.getElementById('logoPreview'); if (logoImg) logoImg.src = logoDataURL; if (logoPreview) logoPreview.style.display = 'flex'; if (typeof generateQR === 'function') generateQR(); } catch(err) { showToast(__('logoLoadError')); } }; r.onerror = function() { showToast(__('logoLoadError')); }; r.readAsDataURL(this.files[0]); } }); } // 重新绑定 CSV 上传 var csvInput = document.getElementById('csvInput'); if (csvInput) { var newCsvInput = csvInput.cloneNode(true); csvInput.parentNode.replaceChild(newCsvInput, csvInput); csvInput = newCsvInput; csvInput.addEventListener('change', function() { if (this.files.length > 0) { var r = new FileReader(); r.onload = function(e) { try { var batchInput = document.getElementById('batchInput'); if (batchInput) batchInput.value = e.target.result; if (typeof generateQR === 'function') generateQR(); } catch(err) { showToast(__('csvParseError')); } }; r.onerror = function() { showToast(__('csvParseError')); }; r.readAsText(this.files[0]); } }); // 同时更新上传标签文字 var csvUpload = document.getElementById('csvUpload'); if (csvUpload) { var p = csvUpload.querySelector('p'); if (p) p.setAttribute('data-i18n', 'uploadCSV'); } } // ========== 补充 SVG 下载功能 ========== var svgBtn = document.getElementById('downloadSvgBtn'); if (svgBtn) { svgBtn.addEventListener('click', function() { showToast(__('svgNotSupport')); }); } // ========== 语言切换初始化 ========== document.addEventListener('DOMContentLoaded', function() { addLangToggle(); applyI18n(); // 确保尺寸滑块位置 var activeSizeBtn = document.querySelector('#sizeSelector .mode-btn.active'); if (activeSizeBtn && typeof moveSizeSlider === 'function') { moveSizeSlider(activeSizeBtn); } }); // 为 window.showToast 添加 i18n 兼容(保留原函数) var originalShowToast = window.showToast; window.showToast = function(msg) { if (originalShowToast) originalShowToast(msg); else { var el = document.getElementById('toast'); if (el) { el.textContent = msg; el.classList.add('show'); clearTimeout(window._toastTimer); window._toastTimer = setTimeout(function(){el.classList.remove('show');}, 2400); } } }; // ========== 资源加载状态检测 ========== setTimeout(function() { var cssLink = document.querySelector('link[href="../shared/nav-bar.css"]'); if (cssLink && !cssLink.sheet) { console.warn('nav-bar.css 可能未正确加载'); } }, 2000); })();