// 简单的 localStorage 心跳锁实现(兼容性回退) // 若浏览器支持 BroadcastChannel,可替换为更即时的通讯方式 (function () { const LOCK_KEY = 'calendar_instance_lock'; const HEARTBEAT_INTERVAL = 2000; // ms const STALE_THRESHOLD = 5000; // ms const instanceId = Date.now() + '-' + Math.random().toString(36).slice(2); function now() { return Date.now(); } function tryAcquire() { const raw = localStorage.getItem(LOCK_KEY); if (raw) { try { const obj = JSON.parse(raw); if (now() - obj.heartbeat < STALE_THRESHOLD) { // 有活跃实例 return false; } } catch (e) { // malformed, ignore } } // 申请锁 localStorage.setItem(LOCK_KEY, JSON.stringify({ id: instanceId, heartbeat: now() })); return true; } function heartbeat() { const raw = localStorage.getItem(LOCK_KEY); if (!raw) return; try { const obj = JSON.parse(raw); if (obj.id === instanceId) { obj.heartbeat = now(); localStorage.setItem(LOCK_KEY, JSON.stringify(obj)); } else { // 被其他实例抢占,通知并退出 alert('你好,每次只能运行一个程序。你已经运行了。'); cleanupAndExit(); } } catch (e) { /* ignore */ } } function cleanupAndExit() { try { // 尝试清理自己持有的锁 const raw = localStorage.getItem(LOCK_KEY); if (raw) { const obj = JSON.parse(raw); if (obj.id === instanceId) { localStorage.removeItem(LOCK_KEY); } } } catch (e) {} // 停止应用初始化(这里简单重载或关闭页面) window.close && window.close(); } if (!tryAcquire()) { alert('你好,每次只能运行一个程序。你已经运行了。'); // 直接阻止后续初始化 throw new Error('Single instance active'); } // 定时心跳 const hbTimer = setInterval(heartbeat, HEARTBEAT_INTERVAL); // 在页面卸载时释放锁 window.addEventListener('beforeunload', function () { try { const raw = localStorage.getItem(LOCK_KEY); if (raw) { const obj = JSON.parse(raw); if (obj.id === instanceId) { localStorage.removeItem(LOCK_KEY); } } } catch (e) {} clearInterval(hbTimer); }); // ... 应用其他初始化继续 ... })();