feat: 初始提交(仅核心代码,已排除大文件)

This commit is contained in:
yangxiangyuan
2026-07-08 10:29:50 +08:00
commit be7c5842f1
623 changed files with 332897 additions and 0 deletions
@@ -0,0 +1,85 @@
// 简单的 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);
});
// ... 应用其他初始化继续 ...
})();