<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <title>系统时钟</title> <style> html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: transparent; font-family: "Microsoft YaHei", "PingFang SC", sans-serif; }
.card { width: 100%; height: 100%; box-sizing: border-box; border-radius: 18px; background: linear-gradient(135deg, #242732 0%, #15171d 100%); border: 1px solid rgba(135, 206, 250, 0.2); box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.1), inset 0 0 20px rgba(135, 206, 250, 0.05); color: #ffffff; display: flex; flex-direction: column; position: relative; user-select: none; }
.header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; height: 24px; }
.drag-area { flex: 1; height: 100%; display: flex; align-items: center; font-size: 13px; font-weight: 500; color: rgba(255, 255, 255, 0.6); cursor: move; }
.drag-area::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: rgba(135, 206, 250, 0.4); margin-right: 8px; box-shadow: 0 0 4px rgba(135, 206, 250, 0.6); }
.actions { display: flex; gap: 8px; z-index: 2; }
button { background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.85); padding: 4px 12px; border-radius: 6px; cursor: pointer; font-family: inherit; font-size: 12px; font-weight: bold; transition: all 0.2s ease; outline: none; }
button:hover { background: rgba(255, 255, 255, 0.15); border-color: rgba(135, 206, 250, 0.4); color: #fff; }
button:active { background: rgba(255, 255, 255, 0.08); border-color: rgba(135, 206, 250, 0.2); }
.clock-content { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; padding-bottom: 20px; cursor: default; }
.time-row { display: flex; align-items: baseline; font-variant-numeric: tabular-nums; text-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); }
#timeHourMin { font-size: clamp(3rem, 18vw, 5.5rem); font-weight: 600; letter-spacing: 2px; line-height: 1; color: #ffffff; }
#timeSec { font-size: clamp(1.2rem, 6vw, 2rem); color: rgba(135, 206, 250, 0.8); margin-left: 8px; font-weight: 400; }
#timeAmPm { font-size: clamp(0.9rem, 4vw, 1.2rem); color: rgba(255, 255, 255, 0.5); margin-left: 6px; font-weight: bold; }
.date-row { font-size: clamp(0.85rem, 4vw, 1.1rem); color: rgba(255, 255, 255, 0.6); margin-top: 12px; letter-spacing: 1px; background: rgba(0, 0, 0, 0.2); padding: 4px 12px; border-radius: 20px; border: 1px solid rgba(255, 255, 255, 0.05); }
.resize-handle { position: absolute; bottom: 4px; right: 4px; width: 16px; height: 16px; cursor: se-resize; color: rgba(255, 255, 255, 0.2); transition: color 0.2s; display: flex; align-items: center; justify-content: center; }
.resize-handle:hover { color: rgba(135, 206, 250, 0.6); } </style></head><body> <div class="card"> <div class="header"> <div class="drag-area" data-yanm-drag="true">数字时钟</div> <div class="actions"> <button id="formatToggle">24H</button> </div> </div> <div class="clock-content"> <div class="time-row"> <span id="timeHourMin">00:00</span> <span id="timeSec">00</span> <span id="timeAmPm"></span> </div> <div class="date-row" id="dateStr">加载中...</div> </div>
<div class="resize-handle" data-yanm-resize="true"> <svg viewBox="0 0 10 10" style="width:10px; height:10px; fill:currentColor;"> <polygon points="10,10 10,7 7,10" /> <polygon points="10,5 10,2 2,10 5,10" /> </svg> </div> </div>
<script> const STATE_KEY = 'yanm_clock_state'; let state = { format24: true };
const btnToggle = document.getElementById('formatToggle'); const elHourMin = document.getElementById('timeHourMin'); const elSec = document.getElementById('timeSec'); const elAmPm = document.getElementById('timeAmPm'); const elDate = document.getElementById('dateStr');
function updateUI() { btnToggle.textContent = state.format24 ? '24H' : '12H'; }
function saveState() { // 1. 内存已更新 const val = JSON.stringify(state); // 2. 本地兜底 (增加 try...catch 避免由于安全限制导致脚本崩溃) try { localStorage.setItem(STATE_KEY, val); } catch(e) { console.warn('localStorage 不可用', e); } // 3. 宿主保存 if (window.yanm) { window.yanm.invoke('state.set', { key: STATE_KEY, value: val }).catch(console.error); } }
function loadLocalState() { try { const local = localStorage.getItem(STATE_KEY); if (local) { Object.assign(state, JSON.parse(local)); } } catch(e) { console.warn('读取 localStorage 失败', e); } updateUI(); }
function loadRemoteState() { if (window.yanm) { window.yanm.invoke('state.get', { key: STATE_KEY }) .then(res => { if (res) { try { Object.assign(state, JSON.parse(res)); updateUI(); try { localStorage.setItem(STATE_KEY, JSON.stringify(state)); } catch(e) {} } catch(e) {} } }) .catch(console.error); } }
btnToggle.addEventListener('click', () => { state.format24 = !state.format24; updateUI(); saveState(); });
const days = ['日', '一', '二', '三', '四', '五', '六']; function tick() { const now = new Date(); let h = now.getHours(); const m = now.getMinutes(); const s = now.getSeconds(); let ampmStr = ''; if (!state.format24) { ampmStr = h >= 12 ? 'PM' : 'AM'; h = h % 12; if (h === 0) h = 12; } const hStr = h.toString().padStart(2, '0'); const mStr = m.toString().padStart(2, '0'); const sStr = s.toString().padStart(2, '0'); elHourMin.textContent = `${hStr}:${mStr}`; elSec.textContent = sStr; elAmPm.textContent = ampmStr; const year = now.getFullYear(); const month = now.getMonth() + 1; const date = now.getDate(); const day = now.getDay(); elDate.textContent = `${year}年${month}月${date}日 星期${days[day]}`; }
// 初始化序列 loadLocalState(); // 立即执行一次渲染当前时间 tick(); // 改用 setInterval 每秒刷新,避免 WebView2 在后台挂起 requestAnimationFrame setInterval(tick, 1000);
function initYanm(retryCount = 0) { if (window.yanm) { loadRemoteState(); return; } if (retryCount < 50) { setTimeout(() => initYanm(retryCount + 1), 100); } } initYanm(); </script></body></html>