【应用】UUID v7 生成器 HTML 代码

核心生成逻辑:UUID v7 的高 48 位基于 Date.now() 获取的毫秒级 Unix 时间戳,剩余位则通过 crypto.getRandomValues() 填充加密级随机熵,确保了唯一性和安全性。

便捷操作方式:您可以通过三种方式复制当前 UUID:直接点击显示区域、点击"复制"按钮,或在焦点位于 UUID 框时使用 Ctrl/Cmd + C 快捷键。复制成功时,界面会给出视觉和文字反馈。

交互反馈与信息:页面会实时显示生成时的时间戳(毫秒),并明确标注熵源为 crypto.getRandomValues。您还可以使用键盘的"空格键"快速生成新的 UUID。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>UUID v7 生成器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: linear-gradient(145deg, #f6f9fc 0%, #e9f1f8 100%);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            padding: 20px;
        }

        .container {
            background: rgba(255, 255, 255, 0.85);
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            border-radius: 32px;
            padding: 40px 48px;
            max-width: 640px;
            width: 100%;
            box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.5) inset;
            transition: all 0.2s ease;
        }

        h1 {
            font-size: 28px;
            font-weight: 700;
            color: #0b1e33;
            letter-spacing: -0.5px;
            display: flex;
            align-items: center;
            gap: 10px;
        }
        h1 span {
            background: #2563eb;
            color: white;
            font-size: 14px;
            font-weight: 600;
            padding: 2px 12px;
            border-radius: 20px;
            letter-spacing: 0.3px;
        }

        .sub {
            color: #4b5b6e;
            font-size: 15px;
            font-weight: 400;
            margin-top: 6px;
            margin-bottom: 28px;
            border-left: 3px solid #2563eb;
            padding-left: 14px;
            background: rgba(37, 99, 235, 0.06);
            border-radius: 0 4px 4px 0;
        }

        .uuid-box {
            background: #ffffff;
            padding: 20px 24px;
            border-radius: 16px;
            font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Menlo', monospace;
            font-size: 22px;
            font-weight: 500;
            letter-spacing: 0.5px;
            color: #0a1a2b;
            border: 2px solid #dce5ef;
            transition: all 0.2s ease;
            cursor: pointer;
            user-select: all;
            word-break: break-all;
            text-align: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.02);
            position: relative;
        }
        .uuid-box:hover {
            border-color: #2563eb;
            background: #f8faff;
            box-shadow: 0 8px 20px -8px rgba(37, 99, 235, 0.15);
            transform: scale(1.005);
        }
        .uuid-box:active {
            transform: scale(0.98);
        }
        .uuid-box .hint {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            font-weight: 400;
            font-size: 14px;
            color: #8a9aa8;
            letter-spacing: 0.3px;
        }

        .actions {
            display: flex;
            gap: 12px;
            margin-top: 24px;
            flex-wrap: wrap;
        }

        .btn-primary {
            flex: 1;
            min-width: 160px;
            background: #2563eb;
            color: white;
            border: none;
            padding: 14px 24px;
            border-radius: 14px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            box-shadow: 0 4px 12px rgba(37, 99, 235, 0.25);
        }
        .btn-primary:hover {
            background: #1d4ed8;
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(37, 99, 235, 0.3);
        }
        .btn-primary:active {
            transform: translateY(0px);
            box-shadow: 0 2px 6px rgba(37, 99, 235, 0.2);
        }

        .btn-secondary {
            background: #eef2f6;
            color: #1e2b3c;
            border: none;
            padding: 14px 24px;
            border-radius: 14px;
            font-size: 16px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            flex: 0.5;
        }
        .btn-secondary:hover {
            background: #e2e8f0;
            transform: translateY(-2px);
        }
        .btn-secondary:active {
            transform: translateY(0px);
        }

        .feedback {
            margin-top: 16px;
            height: 28px;
            font-size: 15px;
            font-weight: 500;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
            transition: all 0.2s ease;
            border-radius: 8px;
        }

        .status-dot {
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: #22c55e;
            margin-right: 6px;
            animation: pulse-dot 2s infinite;
        }

        @keyframes pulse-dot {
            0% { opacity: 1; transform: scale(1); }
            50% { opacity: 0.5; transform: scale(0.8); }
            100% { opacity: 1; transform: scale(1); }
        }

        .footer-meta {
            margin-top: 20px;
            font-size: 13px;
            color: #8a9aa8;
            display: flex;
            justify-content: space-between;
            border-top: 1px solid #e4ebf3;
            padding-top: 16px;
        }
        .footer-meta span {
            display: flex;
            align-items: center;
            gap: 4px;
        }

        @media (max-width: 520px) {
            .container {
                padding: 28px 20px;
            }
            .uuid-box {
                font-size: 16px;
                padding: 16px 14px;
            }
            h1 {
                font-size: 22px;
            }
            .actions {
                flex-direction: column;
            }
            .btn-primary, .btn-secondary {
                flex: 1;
                width: 100%;
            }
            .footer-meta {
                flex-direction: column;
                align-items: center;
                gap: 6px;
                text-align: center;
            }
        }
    </style>
</head>
<body>

<div class="container">
    <h1>
        UUID v7
        <span>RFC 9562</span>
    </h1>
    <div class="sub">
        ⚡ 基于当前 Unix 时间戳(毫秒)+ 加密级随机熵生成 · 点击下方 ID 可复制
    </div>

    <div class="uuid-box" id="uuidDisplay" title="点击复制 UUID">
        <span class="hint">点击「生成」按钮或刷新页面</span>
    </div>

    <div class="actions">
        <button class="btn-primary" id="generateBtn">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.66 0 3-4.03 3-9s-1.34-9-3-9m0 18c-1.66 0-3-4.03-3-9s1.34-9 3-9"/></svg>
            生成新的 UUID v7
        </button>
        <button class="btn-secondary" id="copyBtn" title="复制到剪贴板">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
            复制
        </button>
    </div>

    <div id="feedback" class="feedback"></div>

    <div class="footer-meta">
        <span>🕒 <span id="timestampDisplay">-</span> ms</span>
        <span>🔐 熵源: <strong>crypto.getRandomValues</strong></span>
    </div>
</div>

<script>
    (function() {
        'use strict';

        const uuidDisplay = document.getElementById('uuidDisplay');
        const generateBtn = document.getElementById('generateBtn');
        const copyBtn = document.getElementById('copyBtn');
        const feedback = document.getElementById('feedback');
        const timestampDisplay = document.getElementById('timestampDisplay');

        let currentUUID = '';

        /**
         * 生成符合 RFC 9562 的 UUID v7
         * 结构: 时间戳(48位) + 版本(4位) + 随机(12位) + 变体(2位) + 随机(62位)
         */
        function generateUUIDv7() {
            // 1. 获取当前毫秒时间戳 (Unix 纪元)
            const ts = Date.now();

            // 2. 创建 16 字节数组 (128位)
            const bytes = new Uint8Array(16);

            // 3. 使用 BigInt 安全地提取 48 位时间戳 (6 字节, 大端序)
            const tsBig = BigInt(ts);
            bytes[0] = Number((tsBig >> 40n) & 0xFFn);
            bytes[1] = Number((tsBig >> 32n) & 0xFFn);
            bytes[2] = Number((tsBig >> 24n) & 0xFFn);
            bytes[3] = Number((tsBig >> 16n) & 0xFFn);
            bytes[4] = Number((tsBig >> 8n) & 0xFFn);
            bytes[5] = Number(tsBig & 0xFFn);

            // 4. 填充剩余 10 字节 (80位) 为加密级随机数
            crypto.getRandomValues(bytes.subarray(6));

            // 5. 设置版本号 (Version 7) -> 高 4 位为 0b0111 (0x7)
            //    bytes[6] 属于 time_hi_and_version 字段的高字节
            bytes[6] = (bytes[6] & 0x0F) | 0x70;

            // 6. 设置变体 (Variant RFC 4122) -> 高 2 位为 0b10 (0x8)
            //    bytes[8] 属于 clock_seq_hi_and_reserved 字段
            bytes[8] = (bytes[8] & 0x3F) | 0x80;

            // 7. 转换为十六进制字符串
            const hex = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');

            // 8. 按 UUID 格式插入连字符: 8-4-4-4-12
            return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20,32)}`;
        }

        function updateUI(uuid) {
            if (!uuid) {
                uuidDisplay.innerHTML = `<span class="hint">点击「生成」按钮</span>`;
                currentUUID = '';
                timestampDisplay.textContent = '-';
                return;
            }
            currentUUID = uuid;
            uuidDisplay.textContent = uuid;
            uuidDisplay.style.color = '#0a1a2b';
            // 更新时间戳显示
            const ts = Date.now();
            timestampDisplay.textContent = ts.toLocaleString();
        }

        function generateAndDisplay() {
            const newUUID = generateUUIDv7();
            updateUI(newUUID);
            // 清空之前的反馈(保留成功状态短暂显示)
            feedback.textContent = '';
            feedback.style.color = '';
        }

        // --- 复制功能 ---
        async function copyCurrentUUID() {
            if (!currentUUID) {
                feedback.textContent = '⚠️ 请先生成一个 UUID';
                feedback.style.color = '#f59e0b';
                setTimeout(() => { feedback.textContent = ''; }, 2000);
                return;
            }

            try {
                await navigator.clipboard.writeText(currentUUID);
                feedback.textContent = '✅ 已复制到剪贴板!';
                feedback.style.color = '#16a34a';
                // 给 UUID 框一个瞬时的闪光反馈
                uuidDisplay.style.transition = 'background 0.1s';
                uuidDisplay.style.background = '#dcfce7';
                setTimeout(() => {
                    uuidDisplay.style.background = '';
                }, 300);
                setTimeout(() => {
                    feedback.textContent = '';
                }, 2200);
            } catch (err) {
                // 降级方案:使用传统方法
                try {
                    const textArea = document.createElement('textarea');
                    textArea.value = currentUUID;
                    document.body.appendChild(textArea);
                    textArea.select();
                    document.execCommand('copy');
                    document.body.removeChild(textArea);
                    feedback.textContent = '✅ 已复制到剪贴板!';
                    feedback.style.color = '#16a34a';
                    setTimeout(() => { feedback.textContent = ''; }, 2200);
                } catch (fallbackErr) {
                    feedback.textContent = '❌ 复制失败,请手动选中复制';
                    feedback.style.color = '#dc2626';
                    setTimeout(() => { feedback.textContent = ''; }, 3000);
                }
            }
        }

        // --- 事件绑定 ---
        // 1. 点击 UUID 框复制
        uuidDisplay.addEventListener('click', copyCurrentUUID);

        // 2. 点击复制按钮复制
        copyBtn.addEventListener('click', copyCurrentUUID);

        // 3. 点击生成按钮
        generateBtn.addEventListener('click', generateAndDisplay);

        // 4. 页面加载时自动生成一个
        window.addEventListener('load', () => {
            generateAndDisplay();
        });

        // 5. 键盘快捷键:空格键生成 (辅助功能)
        document.addEventListener('keydown', (e) => {
            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
            if (e.code === 'Space' && !e.repeat) {
                e.preventDefault();
                generateAndDisplay();
            }
            if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
                // 如果焦点在 UUID 框上,拦截默认行为,使用我们的复制逻辑
                if (document.activeElement === uuidDisplay) {
                    e.preventDefault();
                    copyCurrentUUID();
                }
            }
        });

        // 6. 为 UUID 框添加辅助提示 (title)
        uuidDisplay.setAttribute('title', '点击复制到剪贴板');

        console.log('✅ UUID v7 生成器已启动 (RFC 9562)');
    })();
</script>

</body>
</html>
相关推荐
ly768910 小时前
HTML5 从入门到工程实践:语义化、表单、多媒体、性能与项目规范详解
前端·html·html5
feixing_fx17 小时前
告别枯燥字体:Web 字体加载策略与 font-display 最佳实践
前端·css·前端框架·交互·css3
ClouGence18 小时前
不用编程,物理老师也能用AI一键生成交互式课件
人工智能·html·aigc
ly768921 小时前
CSS 从入门到进阶:系统掌握现代网页样式与布局
前端·css
前端技术官21 小时前
一行 CSS 新特性干掉 20 行 JavaScript ?
css·滚动驱动动画·容器查询·:has()·anchor positioning
winfredzhang21 小时前
一个 HTML 文件的备忘录:纸间备忘(Memo on Paper)源码全解析
前端·html·导出·localstorage·备忘录·json和html
智商偏低21 小时前
bindFramebuffer
前端·javascript·html
IMPYLH1 天前
HTML 的 <ins> 元素
前端·javascript·html
Beginner x_u1 天前
Tailwind CSS 速通:有 CSS 基础的实战入门
css·tailwindcss·工程化