feat: 初始提交(仅核心代码,已排除大文件)
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
// IndexedDB 存储封装
|
||||
const DB = (function () {
|
||||
const DB_NAME = 'calendar_reminder';
|
||||
const DB_VERSION = 2; // 升级版本以创建 outbox 队列
|
||||
const STORE_EVENTS = 'events';
|
||||
const STORE_OUTBOX = 'outbox';
|
||||
|
||||
let _db;
|
||||
|
||||
function openDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
req.onupgradeneeded = (ev) => {
|
||||
const db = ev.target.result;
|
||||
// 事件存储
|
||||
if (!db.objectStoreNames.contains(STORE_EVENTS)) {
|
||||
const store = db.createObjectStore(STORE_EVENTS, { keyPath: 'id' });
|
||||
store.createIndex('by_start', 'start');
|
||||
store.createIndex('by_updated', 'updatedAt');
|
||||
}
|
||||
// 离线 outbox 队列
|
||||
if (!db.objectStoreNames.contains(STORE_OUTBOX)) {
|
||||
db.createObjectStore(STORE_OUTBOX, { keyPath: 'id' });
|
||||
}
|
||||
};
|
||||
req.onsuccess = () => {
|
||||
_db = req.result;
|
||||
resolve(_db);
|
||||
};
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function tx(storeName, mode = 'readonly') {
|
||||
const t = _db.transaction(storeName, mode);
|
||||
return t.objectStore(storeName);
|
||||
}
|
||||
|
||||
async function initDB() {
|
||||
if (_db) return _db;
|
||||
return await openDB();
|
||||
}
|
||||
|
||||
function addEvent(ev) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_EVENTS, 'readwrite');
|
||||
const req = store.add(ev);
|
||||
req.onsuccess = () => resolve(ev);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function updateEvent(ev) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_EVENTS, 'readwrite');
|
||||
const req = store.put(ev);
|
||||
req.onsuccess = () => resolve(ev);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteEvent(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_EVENTS, 'readwrite');
|
||||
const req = store.delete(id);
|
||||
req.onsuccess = () => resolve();
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function getEvent(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_EVENTS, 'readonly');
|
||||
const req = store.get(id);
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function listEventsByMonth(year, month) {
|
||||
// month: 1..12
|
||||
const start = new Date(Date.UTC(year, month - 1, 1, 0, 0, 0));
|
||||
const end = new Date(Date.UTC(year, month, 0, 23, 59, 59));
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_EVENTS, 'readonly');
|
||||
const idx = store.index('by_start');
|
||||
const range = IDBKeyRange.bound(start.toISOString(), end.toISOString());
|
||||
const result = [];
|
||||
const req = idx.openCursor(range);
|
||||
req.onsuccess = (e) => {
|
||||
const cursor = e.target.result;
|
||||
if (cursor) {
|
||||
result.push(cursor.value);
|
||||
cursor.continue();
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
// --- Outbox 队列 ---
|
||||
function genId() { return Date.now() + '-' + Math.random().toString(36).slice(2); }
|
||||
|
||||
function addOutbox(entry) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_OUTBOX, 'readwrite');
|
||||
const req = store.add(entry);
|
||||
req.onsuccess = () => resolve(entry);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function addOutboxUpsert(ev) {
|
||||
const entry = { id: genId(), type: 'upsert', payload: ev, timestamp: new Date().toISOString() };
|
||||
return addOutbox(entry);
|
||||
}
|
||||
|
||||
function addOutboxDelete(id) {
|
||||
const entry = { id: genId(), type: 'delete', payload: { id }, timestamp: new Date().toISOString() };
|
||||
return addOutbox(entry);
|
||||
}
|
||||
|
||||
function listOutbox() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_OUTBOX, 'readonly');
|
||||
const result = [];
|
||||
const req = store.openCursor();
|
||||
req.onsuccess = (e) => {
|
||||
const cursor = e.target.result;
|
||||
if (cursor) { result.push(cursor.value); cursor.continue(); }
|
||||
else { resolve(result); }
|
||||
};
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
function removeOutbox(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const store = tx(STORE_OUTBOX, 'readwrite');
|
||||
const req = store.delete(id);
|
||||
req.onsuccess = () => resolve();
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
initDB,
|
||||
addEvent,
|
||||
updateEvent,
|
||||
deleteEvent,
|
||||
getEvent,
|
||||
listEventsByMonth,
|
||||
// outbox
|
||||
addOutboxUpsert,
|
||||
addOutboxDelete,
|
||||
listOutbox,
|
||||
removeOutbox,
|
||||
};
|
||||
})();
|
||||
|
||||
window.DB = DB;
|
||||
Reference in New Issue
Block a user