class IndexedDBHelper { constructor(dbName, version = 1) { this.dbName = dbName; this.version = version; this.db = null; } /** * 初始化数据库(打开/创建) * @param {Array} stores [{ name: "storeName", keyPath: "id", indexes: [{ name: "indexName", key: "keyPath", unique: false }] }] * @returns {Promise} */ openDB(stores = []) { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.version); request.onupgradeneeded = (event) => { this.db = event.target.result; stores.forEach(store => { if (!this.db.objectStoreNames.contains(store.name)) { const objectStore = this.db.createObjectStore(store.name, { keyPath: store.keyPath, autoIncrement: store.autoIncrement || false }); if (store.indexes) { store.indexes.forEach(index => { objectStore.createIndex(index.name, index.key, { unique: index.unique }); }); } } }); }; request.onsuccess = (event) => { this.db = event.target.result; console.log("✅ IndexedDB 连接成功"); resolve(this.db); }; request.onerror = (event) => { reject(`IndexedDB Error: ${event.target.error}`); }; }); } // 通用查询方法,按指定字段查询 async queryByField(storeName, fieldName, value) { return new Promise(async (resolve, reject) => { try { if (!this.db) { await this.openDB(); } const transaction = this.db.transaction(storeName, 'readonly'); const store = transaction.objectStore(storeName); if (!store.indexNames.contains(fieldName)) { return reject(`索引 ${fieldName} 不存在`); } const index = store.index(fieldName); const request = index.getAll(value); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject('查询失败: ' + event.target.error); }; } catch (error) { reject('查询错误: ' + error); } }); } /** * 添加数据(支持单条或批量) * @param {string} storeName - 存储空间名称 * @param {Object|Array} data - 要添加的数据(单条对象或数组) * @returns {Promise>} - 返回添加数据的ID(单条返回数字,批量返回数组) */ add(storeName, data) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); // 统一处理为数组格式 const items = Array.isArray(data) ? data : [data]; const results = []; // 监听每个添加操作的成功事件 items.forEach((item, index) => { const request = store.add(item); request.onsuccess = (event) => { results[index] = event.target.result; // 保存生成的ID }; request.onerror = (event) => { transaction.abort(); // 遇到错误时中止事务 reject(`第 ${index + 1} 条数据添加失败: ${event.target.error}`); }; }); // 监听事务完成事件 transaction.oncomplete = () => { // 单条数据返回单个ID,批量返回数组 resolve(items.length === 1 ? results[0] : results); }; // 统一错误处理 transaction.onerror = (event) => { reject(`添加失败: ${event.target.error}`); }; }); } /** * 读取数据(根据主键) * @param {string} storeName * @param {any} key * @returns {Promise} */ get(storeName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readonly"); const store = transaction.objectStore(storeName); const request = store.get(key); request.onsuccess = () => resolve(request.result); request.onerror = (event) => reject(`Get Error: ${event.target.error}`); }); } /** * 读取所有数据(兼容X5内核方案) * @param {string} storeName * @returns {Promise} */ getAll(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readonly"); const store = transaction.objectStore(storeName); // 兼容性检测:优先尝试原生getAll方法 if (typeof store.getAll === 'function') { const request = store.getAll(); request.onsuccess = () => resolve(request.result); request.onerror = (e) => reject(`GetAll Error: ${e.target.error}`); } // 降级方案:使用游标手动遍历 else { const results = []; const request = store.openCursor(); request.onsuccess = (e) => { const cursor = e.target.result; if (cursor) { results.push(cursor.value); cursor.continue(); } else { resolve(results); } }; request.onerror = (e) => reject(`Cursor Error: ${e.target.error}`); } }); } /** * 获取表的总记录数 * @param {string} storeName - 表名(Object Store 名称) * @returns {Promise} - 记录总数 */ async getRecordCount(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readonly"); const store = transaction.objectStore(storeName); const request = store.count(); request.onsuccess = () => resolve(request.result); request.onerror = (event) => reject(`❌ Count Error: ${event.target.error}`); }); } /** * 更新数据(支持指定 key 或自动使用 keyPath) * @param {string} storeName 存储对象名 * @param {Object} data 待更新数据 * @param {IDBValidKey|IDBKeyRange} [key] 可选参数,指定更新的 key * @returns {Promise} 更新结果 */ update(storeName, data, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const keyPath = store.keyPath; // 有传入 key:直接使用 key 更新 if (key !== undefined) { const request = store.put(data, key); request.onsuccess = () => resolve("数据更新成功(指定 key)"); request.onerror = (event) => reject(`更新失败: ${event.target.error}`); return; } // 无传入 key:依赖 keyPath 更新 if (!keyPath) { reject("当前 store 未设置 keyPath,必须传入 key 参数"); return; } // 检查数据是否包含 keyPath 属性 let missingKeys = []; if (Array.isArray(keyPath)) { missingKeys = keyPath.filter(k => !data.hasOwnProperty(k)); } else if (typeof keyPath === 'string') { if (!data.hasOwnProperty(keyPath)) { missingKeys.push(keyPath); } } if (missingKeys.length > 0) { reject(`数据缺少必要的 keyPath 属性: ${missingKeys.join(', ')}`); return; } // 默认使用 keyPath 更新 const request = store.put(data); request.onsuccess = () => resolve("数据更新成功(默认 keyPath)"); request.onerror = (event) => reject(`更新失败: ${event.target.error}`); }); } /** * 删除数据(根据主键) * @param {string} storeName * @param {any} key * @returns {Promise} */ delete(storeName, key) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const request = store.delete(key); request.onsuccess = () => resolve("Data deleted successfully"); request.onerror = (event) => reject(`Delete Error: ${event.target.error}`); }); } /** * 通过索引查询数据 * @param {string} storeName * @param {string} indexName * @param {any} value * @returns {Promise} */ getByIndex(storeName, indexName, value) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readonly"); const store = transaction.objectStore(storeName); const index = store.index(indexName); const request = index.get(value); request.onsuccess = () => resolve(request.result); request.onerror = (event) => reject(`Get By Index Error: ${event.target.error}`); }); } /** * 清空表 * @param {string} storeName * @returns {Promise} */ clearStore(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const request = store.clear(); request.onsuccess = () => resolve("Store cleared successfully"); request.onerror = (event) => reject(`Clear Store Error: ${event.target.error}`); }); } /** * 删除数据库 * @returns {Promise} */ deleteDB(dbNamed = null) { return new Promise((resolve, reject) => { const request = indexedDB.deleteDatabase(dbNamed || this.dbName); request.onsuccess = () => resolve("Database deleted successfully"); request.onerror = (event) => reject(`Delete DB Error: ${event.target.error}`); }); } async deleteOldestRecord(storeName) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], "readwrite"); const store = transaction.objectStore(storeName); const request = store.openCursor(); // 🔹 获取最早的记录(按主键 ID 排序) request.onsuccess = function(event) { const cursor = event.target.result; if (cursor) { console.log(`🗑️ 删除最早的记录 ID: ${cursor.key}`); store.delete(cursor.key); // 🔥 删除最小 ID(最早记录) resolve(); } else { resolve(); // 没有记录时跳过 } }; request.onerror = (event) => reject(`❌ Cursor Error: ${event.target.error}`); }); } } export default IndexedDBHelper