| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- <!doctype html>
- <html lang="zh-CN">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="color-scheme" content="light">
- <title>FWH 验证码</title>
- <style>
- :root { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; color: #25313b; background: #f6f8fa; }
- body { margin: 0; min-height: 100vh; display: grid; place-items: center; }
- main { box-sizing: border-box; width: min(360px, calc(100% - 32px)); padding: 32px; background: white; border: 1px solid #e5e9ed; border-radius: 14px; box-shadow: 0 8px 30px #1d34420a; text-align: center; }
- h1 { margin: 0 0 24px; font-size: 18px; font-weight: 600; }
- #code { min-height: 58px; font-size: clamp(36px, 11vw, 48px); font-weight: 650; letter-spacing: .12em; font-variant-numeric: tabular-nums; white-space: nowrap; }
- #status { margin: 18px 0 0; color: #687783; font-size: 14px; }
- .track { height: 4px; margin-top: 18px; background: #e9eef1; border-radius: 4px; overflow: hidden; }
- #progress { height: 100%; width: 0; background: #568baa; transition: width .2s linear; }
- </style>
- </head>
- <body>
- <main>
- <h1>FWH 验证码</h1>
- <div id="code" aria-live="polite">··· ···</div>
- <p id="status">正在获取验证码…</p>
- <div class="track" aria-hidden="true"><div id="progress"></div></div>
- </main>
- <script>
- const codeElement = document.getElementById('code');
- const statusElement = document.getElementById('status');
- const progressElement = document.getElementById('progress');
- let expiresAt = 0;
- let clockOffset = 0;
- let loading = false;
- let lastFetch = 0;
- async function refreshCode() {
- if (loading) return;
- loading = true;
- lastFetch = Date.now();
- try {
- const codePath = location.pathname.replace(/\/?$/, '/') + 'code';
- const response = await fetch(codePath, { cache: 'no-store', credentials: 'same-origin' });
- if (!response.ok) throw new Error('request failed');
- const data = await response.json();
- if (!/^\d{6}$/.test(data.code)) throw new Error('invalid code');
- clockOffset = data.serverTime - Date.now();
- expiresAt = data.expiresAt;
- codeElement.textContent = data.code.slice(0, 3) + ' ' + data.code.slice(3);
- updateCountdown();
- } catch (_) {
- expiresAt = 0;
- codeElement.textContent = '··· ···';
- statusElement.textContent = '获取失败,正在重试…';
- progressElement.style.width = '0%';
- } finally {
- loading = false;
- }
- }
- function updateCountdown() {
- if (!expiresAt) return;
- const remaining = Math.max(0, expiresAt - (Date.now() + clockOffset));
- statusElement.textContent = remaining > 0 ? Math.ceil(remaining / 1000) + ' 秒后更新' : '正在更新…';
- progressElement.style.width = (remaining / 30000 * 100) + '%';
- if ((remaining === 0 || Date.now() - lastFetch >= 5000) && !loading) refreshCode();
- }
- refreshCode();
- setInterval(() => {
- if (expiresAt) updateCountdown();
- else if (!loading && Date.now() - lastFetch >= 3000) refreshCode();
- }, 250);
- </script>
- </body>
- </html>
|