平时老要用随机密码、二维码,懒得切来切去,就用gpt搓了个小页面。
前端 + Workers
在线用:
👉 http://pw.gpq.qzz.io/
功能:
UUID 生成
随机密码(可选字符集)
二维码
Cloudflare Workers 一键可部署
代码如下
// Cloudflare Workers 完整脚本(中文注释 + 中文 UI)// 功能:UUID / 密码 / 二维码 生成器// 技术栈:Alpine.js + Tailwind CDN(轻量、无打包)// 说明:可直接粘贴到 Workers 编辑器并部署// ---------------------------// 工具类:安全随机与二维码生成// ---------------------------class UtilityGenerator { // 生成 UUID(使用浏览器 / worker 环境的 crypto) static generateUUID() { return crypto.randomUUID(); } // 生成随机密码 // length: 密码长度(8-256) // options: {lowercase, uppercase, numbers, symbols, excludeSimilar, excludeAmbiguous} static generatePassword(length = 16, options = {}) { const charsets = { lowercase: 'abcdefghijklmnopqrstuvwxyz', uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', numbers: '0123456789', symbols: '!@#$%^&*()_+~`|}{[]:;?><,./-=' }; // 常被混淆的字符,如 o O 0 i l 1 等 const similarChars = 'oOiIlL10'; // 模糊字符(有些字体下不易辨认) const ambiguousChars = '{}[]()/\'"`~,;:.<>'; if (length < 8 || length > 256) { throw new Error('密码长度必须在 8 到 256 之间'); } // 根据选项拼接字符集 let chars = Object.entries(options) .filter(([key, value]) => value && charsets[key]) .map(([key]) => charsets[key]) .join(''); // 如果没有选择任何字符集,则全部启用 if (!chars) { chars = Object.values(charsets).join(''); } // 排除相似字符 if (options.excludeSimilar) { chars = chars.split('').filter(c => !similarChars.includes(c)).join(''); } // 排除模糊字符 if (options.excludeAmbiguous) { chars = chars.split('').filter(c => !ambiguousChars.includes(c)).join(''); } // 使用强随机数生成密码 const array = new Uint8Array(length); crypto.getRandomValues(array); return Array.from(array, byte => chars[byte % chars.length]).join(''); } // 返回第三方二维码生成器的 URL(免费服务) static async getQRCodeURL(text, size = 350) { return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(text)}`; }}// ---------------------------// 生成页面 HTML(中文)// 使用 Tailwind CDN + Alpine.js CDN(无需构建)// ---------------------------const getHTML = () => `<!DOCTYPE html><html lang="zh-CN" x-data="app" x-init="initTheme()" class="antialiased"><head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>实用工具箱 — UUID / 密码 / 二维码 生成器</title> <!-- Tailwind CDN(轻量 config) --> <script src="https://cdn.tailwindcss.com"></script> <!-- Alpine.js CDN --> <script src="https://unpkg.com/[email protected]/dist/cdn.min.js" defer></script> <script> // Tailwind 配置(扩展主题色) tailwind.config = { darkMode: 'class', theme: { extend: { colors: { glass: { DEFAULT: 'rgba(255,255,255,0.6)', dark: 'rgba(255,255,255,0.06)' }, primary: { 50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc', 400: '#38bdf8', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1', 800: '#075985', 900: '#0c4a6e' } }, backdropBlur: { xs: '2px', } } } } </script> <style> /* 隐藏 x-cloak 元素直到 Alpine 初始化完毕 */ [x-cloak] { display: none !important; } /* 小屏幕优化 */ @media (max-width: 640px) { .glass-card { padding: 1rem; } } </style></head><body class="min-h-screen bg-gradient-to-br from-gray-100 to-gray-50 dark:from-gray-900 dark:to-gray-800 text-gray-800 dark:text-gray-100"> <div class="max-w-4xl mx-auto p-6"> <!-- 顶部标题 --> <header class="flex items-center justify-between mb-6"> <div> <h1 class="text-2xl font-extrabold tracking-tight text-primary-700 dark:text-primary-300">实用工具箱</h1> <p class="text-sm text-gray-600 dark:text-gray-300 mt-1">UUID、密码、二维码 —— 一键生成,轻量安全</p> </div> <div class="flex items-center space-x-3"> <!-- 主题切换 --> <button @click="toggleTheme()" class="p-2 rounded-full bg-white/60 dark:bg-gray-700/40 backdrop-blur-xs border border-gray-200 dark:border-gray-700 shadow-sm hover:scale-105 transition-transform"> <svg x-show="theme === 'light'" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-yellow-500" viewBox="0 0 20 20" fill="currentColor"> <path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1z"/> </svg> <svg x-show="theme === 'dark'" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-100" viewBox="0 0 20 20" fill="currentColor"> <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/> </svg> </button> <!-- 说明按钮 --> <button @click="showInfo = !showInfo" class="px-3 py-1 rounded-md bg-primary-500 text-white shadow hover:opacity-90 transition">使用说明</button> </div> </header> <!-- 说明卡片 --> <div x-show="showInfo" x-cloak class="mb-6 p-4 rounded-xl glass-card bg-white/60 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700 backdrop-blur-sm shadow-sm"> <div class="flex items-start space-x-3"> <div> <p class="text-sm text-gray-700 dark:text-gray-200">这是一个轻量的工具箱,包含:</p> <ul class="mt-2 text-sm text-gray-600 dark:text-gray-300 list-disc ml-5"> <li>UUID(随机唯一标识符)</li> <li>密码生成器(可选择字符集与长度)</li> <li>二维码生成(调用公开 API)</li> </ul> <p class="mt-2 text-xs text-gray-500 dark:text-gray-400">不传输敏感数据到第三方(仅二维码图片使用第三方生成服务)。</p> </div> </div> </div> <!-- 主内容网格 --> <main class="grid grid-cols-1 md:grid-cols-3 gap-6"> <!-- UUID 卡片 --> <section class="col-span-1 md:col-span-1 bg-white/60 dark:bg-gray-800/60 border border-gray-200 dark:border-gray-700 rounded-2xl p-6 glass-card shadow transform hover:-translate-y-1 transition"> <h2 class="text-lg font-semibold text-primary-700 dark:text-primary-300 mb-3">生成 UUID</h2> <p class="text-sm text-gray-600 dark:text-gray-300 mb-4">快速生成符合标准的随机 UUID(v4)。</p> <button @click="generateUUID()" class="w-full py-2 rounded-md bg-gradient-to-r from-primary-500 to-primary-600 text-white font-medium shadow">生成 UUID</button> <div x-show="uuidResult" x-cloak class="mt-4"> <div class="p-3 rounded bg-white dark:bg-gray-900 border border-gray-100 dark:border-gray-800 break-all text-sm"> <code x-text="uuidResult"></code> </div> <div class="mt-3 grid grid-cols-2 gap-2"> <button @click="copy(uuidResult, 'UUID 已复制')" class="py-2 rounded-md bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200">复制</button> <button @click="clear('uuid')" class="py-2 rounded-md bg-red-50 dark:bg-red-800/30 text-red-700 dark:text-red-300">清除</button> </div> </div> </section> <!-- 密码生成 卡片 --> <section class="col-span-1 md:col-span-1 bg-white/60 dark:bg-gray-800/60 border border-gray-200 dark:border-gray-700 rounded-2xl p-6 glass-card shadow transform hover:-translate-y-1 transition"> <h2 class="text-lg font-semibold text-primary-700 dark:text-primary-300 mb-3">生成 密码</h2> <p class="text-sm text-gray-600 dark:text-gray-300 mb-3">支持长度与字符集自定义,安全随机生成。</p> <div class="space-y-3"> <label class="text-xs text-gray-600 dark:text-gray-300">长度</label> <select x-model.number="pwd.length" class="w-full p-2 rounded border bg-white dark:bg-gray-700 dark:border-gray-600"> <option value="16">16 位</option> <option value="24">24 位</option> <option value="32">32 位</option> <option value="64">64 位</option> <option value="128">128 位</option> </select> <div class="grid grid-cols-2 gap-2"> <label class="inline-flex items-center space-x-2 text-sm"> <input type="checkbox" x-model="pwd.options.lowercase" class="rounded" /> <span>包含小写字母</span> </label> <label class="inline-flex items-center space-x-2 text-sm"> <input type="checkbox" x-model="pwd.options.uppercase" class="rounded" /> <span>包含大写字母</span> </label> <label class="inline-flex items-center space-x-2 text-sm"> <input type="checkbox" x-model="pwd.options.numbers" class="rounded" /> <span>包含数字</span> </label> <label class="inline-flex items-center space-x-2 text-sm"> <input type="checkbox" x-model="pwd.options.symbols" class="rounded" /> <span>包含符号</span> </label> <label class="inline-flex items-center space-x-2 text-sm col-span-2"> <input type="checkbox" x-model="pwd.options.excludeSimilar" class="rounded" /> <span>排除相似字符(如 o O 0 l 1)</span> </label> </div> <div class="grid grid-cols-2 gap-2"> <button @click="generatePassword()" class="py-2 rounded-md bg-gradient-to-r from-primary-500 to-primary-600 text-white">生成 密码</button> <button @click="clear('password')" class="py-2 rounded-md bg-gray-100 dark:bg-gray-700">清除</button> </div> <div x-show="pwd.result" x-cloak class="mt-2"> <div class="p-3 rounded bg-white dark:bg-gray-900 border border-gray-100 dark:border-gray-800 break-all text-sm"> <code x-text="pwd.result"></code> </div> <div class="mt-2 grid grid-cols-2 gap-2"> <button @click="copy(pwd.result, '密码 已复制')" class="py-2 rounded-md bg-gray-100 dark:bg-gray-700">复制</button> <button @click="useAsPassword(pwd.result)" class="py-2 rounded-md bg-yellow-50 dark:bg-yellow-800/30 text-yellow-700">另存/使用</button> </div> </div> </div> </section> <!-- 二维码 卡片(横跨两列) --> <section class="col-span-1 md:col-span-1 lg:col-span-1 bg-white/60 dark:bg-gray-800/60 border border-gray-200 dark:border-gray-700 rounded-2xl p-6 glass-card shadow transform hover:-translate-y-1 transition"> <h2 class="text-lg font-semibold text-primary-700 dark:text-primary-300 mb-3">生成 二维码</h2> <p class="text-sm text-gray-600 dark:text-gray-300 mb-3">输入文本或链接,快速生成二维码图片(使用第三方公开服务)。</p> <input x-model="qr.text" type="text" placeholder="在此输入要生成二维码的文本或链接" class="w-full p-2 rounded border bg-white dark:bg-gray-700 dark:border-gray-600 mb-3" /> <div class="grid grid-cols-2 gap-2"> <button @click="generateQR()" class="py-2 rounded-md bg-gradient-to-r from-primary-500 to-primary-600 text-white">生成 二维码</button> <button @click="clear('qr')" class="py-2 rounded-md bg-gray-100 dark:bg-gray-700">清除</button> </div> <div x-show="qr.url" x-cloak class="mt-3 text-center"> <img :src="qr.url" alt="二维码" class="mx-auto w-48 h-48 rounded shadow" /> <div class="mt-2 flex items-center justify-center gap-2"> <a :href="qr.url" target="_blank" rel="noreferrer" class="text-sm underline">在新窗口打开</a> <button @click="copy(qr.url, '二维码链接 已复制')" class="text-sm px-2 py-1 rounded bg-gray-100 dark:bg-gray-700">复制链接</button> </div> </div> </section> </main> <!-- 页脚与版权 --> <footer class="mt-8 text-center text-xs text-gray-500 dark:text-gray-400"> <div>© 实用工具箱 · 仅供测试与开发使用</div> <div class="mt-1">二维码由第三方服务生成;请勿用于隐私敏感用途。</div> </footer> </div> <!-- 全局 Toast(提示) --> <div x-data="{ toasts: [] }" x-cloak> <template x-for="(t,i) in toasts" :key="i"> <div x-text="t" class="fixed right-6 bottom-6 bg-gray-900 text-white px-4 py-2 rounded shadow-lg opacity-90"></div> </template> </div> <!-- Alpine 初始化逻辑与事件 --> <script> document.addEventListener('alpine:init', () => { // 全局应用状态(主题、提示) Alpine.data('app', () => ({ theme: localStorage.getItem('theme') || 'light', showInfo: false, // 存储当前生成结果 uuidResult: '', pwd: { length: 16, options: { lowercase: true, uppercase: true, numbers: true, symbols: false, excludeSimilar: false, excludeAmbiguous: false }, result: '' }, qr: { text: '', url: '' }, // 简单的 toast 列表(本地显示) toasts: [], // 初始化 theme initTheme() { if (this.theme === 'dark') document.documentElement.classList.add('dark'); else document.documentElement.classList.remove('dark'); }, toggleTheme() { this.theme = this.theme === 'dark' ? 'light' : 'dark'; localStorage.setItem('theme', this.theme); document.documentElement.classList.toggle('dark'); }, // 添加 toast(持续 2 秒) pushToast(msg = '') { this.toasts.push(msg); const idx = this.toasts.length - 1; setTimeout(() => { this.toasts.splice(idx, 1); }, 2000); }, // 生成 UUID(调用 API) async generateUUID() { try { const res = await fetch('/generate-uuid'); const data = await res.json(); this.uuidResult = data.uuid; } catch (e) { this.pushToast('生成失败:网络错误'); } }, // 生成密码(调用 API) async generatePassword() { try { const params = new URLSearchParams({ length: this.pwd.length, lowercase: this.pwd.options.lowercase, uppercase: this.pwd.options.uppercase, numbers: this.pwd.options.numbers, symbols: this.pwd.options.symbols, excludeSimilar: this.pwd.options.excludeSimilar, excludeAmbiguous: this.pwd.options.excludeAmbiguous }); const res = await fetch('/generate-password?' + params.toString()); const data = await res.json(); if (data.password) { this.pwd.result = data.password; } else { this.pushToast('生成失败:' + (data.error || '未知错误')); } } catch (e) { this.pushToast('生成失败:网络错误'); } }, // 生成二维码(调用 API) async generateQR() { try { const text = this.qr.text || 'Hello World'; const res = await fetch('/generate-qrcode?text=' + encodeURIComponent(text)); const data = await res.json(); if (data.qrCodeURL) { this.qr.url = data.qrCodeURL; } else { this.pushToast('二维码生成失败'); } } catch (e) { this.pushToast('二维码生成失败:网络错误'); } }, // 复制到剪贴板并提示 async copy(text, successMsg = '已复制') { try { await navigator.clipboard.writeText(text); this.pushToast(successMsg); } catch (e) { this.pushToast('复制失败(浏览器不支持)'); } }, // 清除指定内容 clear(name) { if (name === 'uuid') this.uuidResult = ''; if (name === 'password') this.pwd.result = ''; if (name === 'qr') { this.qr.text = ''; this.qr.url = ''; } }, // 在这里可以把密码推到别处(示例) useAsPassword(pwd) { // 仅示例:将密码复制到剪贴板并提示 this.copy(pwd, '密码已复制,可以粘贴使用'); } })); }); </script></body></html>`;// ---------------------------// Workers 请求处理(API 部分)// 路由:/ 、/generate-uuid 、/generate-password 、/generate-qrcode// ---------------------------async function handleRequest(request) { try { const url = new URL(request.url); // 根路径返回 HTML 页面 if (url.pathname === '/' || url.pathname === '') { return new Response(getHTML(), { headers: { "Content-Type": "text/html; charset=utf-8" }, }); } // 生成 UUID API if (url.pathname === '/generate-uuid') { return new Response(JSON.stringify({ uuid: UtilityGenerator.generateUUID() }), { headers: { "Content-Type": "application/json; charset=utf-8" } }); } // 生成密码 API if (url.pathname === '/generate-password') { try { const length = parseInt(url.searchParams.get('length')) || 16; const options = { lowercase: url.searchParams.get('lowercase') === 'true' || url.searchParams.get('lowercase') === '1', uppercase: url.searchParams.get('uppercase') === 'true' || url.searchParams.get('uppercase') === '1', numbers: url.searchParams.get('numbers') === 'true' || url.searchParams.get('numbers') === '1', symbols: url.searchParams.get('symbols') === 'true' || url.searchParams.get('symbols') === '1', excludeSimilar: url.searchParams.get('excludeSimilar') === 'true' || url.searchParams.get('excludeSimilar') === '1', excludeAmbiguous: url.searchParams.get('excludeAmbiguous') === 'true' || url.searchParams.get('excludeAmbiguous') === '1' }; const password = UtilityGenerator.generatePassword(length, options); return new Response(JSON.stringify({ password }), { headers: { "Content-Type": "application/json; charset=utf-8" } }); } catch (error) { return new Response(JSON.stringify({ error: error.message }), { status: 400, headers: { "Content-Type": "application/json; charset=utf-8" } }); } } // 生成二维码 API(返回二维码图片 URL) if (url.pathname === '/generate-qrcode') { const text = url.searchParams.get('text') || 'Hello World'; const size = parseInt(url.searchParams.get('size')) || 350; const qrCodeURL = await UtilityGenerator.getQRCodeURL(text, size); return new Response(JSON.stringify({ qrCodeURL }), { headers: { "Content-Type": "application/json; charset=utf-8" } }); } // 其它路径 404 return new Response('未找到', { status: 404 }); } catch (err) { return new Response(JSON.stringify({ error: '服务器内部错误', details: err.message }), { status: 500, headers: { "Content-Type": "application/json; charset=utf-8" } }); }}// 监听 fetch 事件addEventListener('fetch', event => { event.respondWith(handleRequest(event.request));});需要的拿去用,不需要当我路过。
评论 (0)