用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>