concat和app.push两种连接数组的方法

本文通过一个简单的测试案例对比了JavaScript中使用concat方法与push.apply方法进行数组连接的性能差异。测试结果显示,在处理大规模数组时,concat方法明显比push.apply方法更快。
function myConcat(a, b) {
	a.concat(b);  // 1ms
}
function myPush(a, b) {
	a.push.apply(a, b);   // 14ms
}
function test(fn, a, b) {
	var start = new Date().getTime();//起始时间
	fn(a, b);
	var end = new Date().getTime();//接受时间
	return (end - start)+"ms";
}
console.log('concat:'+test(myConcat, new Array(100000).fill(1), new Array(100000).fill(2)));
console.log('push:'+test(myPush, new Array(100000).fill(1), new Array(100000).fill(2)));

/* 文件路径: data/DataManager.js */ // @ts-nocheck // 导入依赖 import { LazyLoader } from './LazyLoader.js'; import { TransactionApi } from './TransactionApi.js'; import { IndexedDBManager } from './IndexedDBManager.js'; // 确保SockJSStomp库已加载 if (typeof SockJS === 'undefined') { throw new Error('SockJS library is required but not loaded. Please include it in your HTML.'); } if (typeof Stomp === 'undefined') { throw new Error('Stomp library is required but not loaded. Please include it in your HTML.'); } //这个函数不许改,也禁止废话,属性名其他命名都哼规范不会出现意外, function resolveDataReferences(data) { // 获取 data 对象的所有顶层键 const keys = Object.keys(data); // 遍历每个顶层键(如 users, posts 等) for (const key of keys) { const entities = data[key]; // 遍历该顶层键下的每个实体(如每个 user 或 post) for (const entity of entities) { // 遍历实体的每个属性 for (const attribute in entity) { if (entity?.hasOwnProperty(attribute)) { var trpe=attribute?.replace(/\d/g, ''); // 确保属性属于当前实体 if (Array.isArray(entity[attribute])) { if(data[trpe]==null){ trpe+="s" } // 如果属性是一个数组,则将数组中的每个 ID 替换为对应的实际对象 entity[attribute] = entity[attribute].map(item => data[trpe ]?.find(updateItem => updateItem?.id === item?.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { // 如果属性是一个对象,则将其替换为对应的实际对象 entity[attribute] = data[trpe + "s"]?.find(updateItem => updateItem?.id === entity[attribute]?.id); } } } } } return data; } function resolveDataReference(entity,data) { // 遍历实体的每个属性 for (const attribute in entity) { if (entity?.hasOwnProperty(attribute)) { var trpe=attribute?.replace(/\d/g, ''); // 确保属性属于当前实体 if (Array.isArray(entity[attribute])) { if(data[trpe]==null){ trpe+="s" } // 如果属性是一个数组,则将数组中的每个 ID 替换为对应的实际对象 entity[attribute] = entity[attribute].map(item => data[trpe ]?.find(updateItem => updateItem?.id === item?.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { // 如果属性是一个对象,则将其替换为对应的实际对象 entity[attribute] = data[trpe + "s"]?.find(updateItem => updateItem?.id === entity[attribute]?.id); } } } return entity; } /** * 数据管理器类 - Web版 * 基于小程序版MiniProgramDataManager重构 */ class DataManager { /** * 构造函数 * @param {string} baseUrl - API基础URL * @param {Set} initializedEntityTypes - 已初始化的实体类型集合 * @param {Object} entityConfig - 实体类型配置对象 */ constructor(baseUrl = "../php_api/index.php", initializedEntityTypes = new Set(), entityConfig = {}) { this.baseUrl = baseUrl; this.debug = true; this.networkAvailable = true; this.isSyncing = false; this.lastSync = null; this.storageKey = 'webAppData'; // 初始化IndexedDB管理器 this.indexedDBManager = new IndexedDBManager('KuCunAppData', 1); // 默认实体类型配置 this.defaultEntityConfig = { // 实体类型定义 entityTypes: [ 'bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'dingdan_bancai', 'chanpin_zujian', 'zujian', 'caizhi', 'dingdan_chanpin', 'user', 'jinhuo' ], // 默认核心实体类型(用于初始化) coreEntityTypes: ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'user'], // 实体文本映射 entityTexts: { bancai: '板材已存在', dingdan: '订单已存在', mupi: '木皮已存在', chanpin: '产品已存在', kucun: '已有库存记录', chanpin_zujian: '产品已有该组件', dingdan_bancai: '', zujian: '组件已定义过了', caizhi: '材质已定义过了', dingdan_chanpin: '订单下已有该产品', user: '' }, // 重复检查规则配置 duplicateCheckRules: { bancai: (data, collection) => collection.some(b => b.houdu === data.houdu && b.caizhi?.id === data.caizhi?.id && b.mupi1?.id === data.mupi1?.id && b.mupi2?.id === data.mupi2?.id ), caizhi: (data, collection) => collection.some(c => c.name === data.name), mupi: (data, collection) => collection.some(m => m.name === data.name && m.you === data.you), chanpin: (data, collection) => collection.some(c => c.bianhao === data.bianhao), zujian: (data, collection) => collection.some(z => z.name === data.name), dingdan: (data, collection) => collection.some(d => d.number === data.number), chanpin_zujian: (data, collection) => collection.some(cz => cz.chanpin?.id === data.chanpin?.id && cz.zujian?.id === data.zujian?.id ), dingdan_chanpin: (data, collection) => collection.some(dc => dc.dingdan?.id === data.dingdan?.id && dc.chanpin?.id === data.chanpin?.id && dc.zujian?.id === data.zujian?.id && dc.bancai?.id === data.bancai?.id ), user: (data, collection) => collection.some(u => u.name === data.name) } }; // 合并默认配置用户配置 this.entityConfig = {...this.defaultEntityConfig, ...entityConfig}; this.entityConfig.entityTexts = {...this.defaultEntityConfig.entityTexts, ...(entityConfig.entityTexts || {})}; this.entityConfig.duplicateCheckRules = {...this.defaultEntityConfig.duplicateCheckRules, ...(entityConfig.duplicateCheckRules || {})}; this._rawData = this.createEmptyData(); this.lazyLoader = new LazyLoader(this); // 初始化回调对象,支持动态添加新的数据类型 this.callbacks = { all: [] }; // 创建事务API实例 this.Transaction = new TransactionApi(this); // 进度更新回调函数 this.onProgressUpdate = null; // 已初始化的实体类型集合 this.initializedEntityTypes = initializedEntityTypes; // 初始化状态管理 this.isInitializing = false; this.initializationPromise = null; // 初始化网络状态 this.initNetwork(); // 注意:现在只初始化网络状态,实际的数据加载WebSocket连接应该通过initialize方法触发 // 这样可以更好地控制初始化流程进度更新 } /** * 创建空的数据结构 * 根据配置的实体类型动态创建 */ createEmptyData() { const emptyData = { _lastModified: null, _lastSync: null }; // 根据配置的实体类型创建空数组 (this.entityConfig.entityTypes || []).forEach(entityType => { const collectionName = `${entityType}s`; emptyData[collectionName] = []; }); return emptyData; } get data() { // 创建数据访问代理,保持懒加载功能 const self = this; return new Proxy(this._rawData, { get: (target, prop) => { if (prop.startsWith('_')) { return target[prop]; } if (Array.isArray(target[prop])) { // 为数组中的每个对象创建懒加载代理 return target[prop].map(item => { if (typeof item === 'object' && item !== null) { // 获取实体类型(去掉末尾的s) const entityType = prop.replace(/s$/, ''); return self.lazyLoader.createProxy(item, entityType); } return item; }); } return target[prop]; } }); } /** * 添加实体类型到已初始化集合,并动态订阅WebSocket更新 * @param {String} entityType - 实体类型 */ async addEntityType(entityType) { if (this.initializedEntityTypes.has(entityType)) { return; // 已初始化,无需重复添加 } // 添加到已初始化集合 this.initializedEntityTypes.add(entityType); // 确保集合存在 const collectionName = `${entityType}s`; if (!this._rawData[collectionName]) { this._rawData[collectionName] = []; } // 确保回调数组存在 if (!this.callbacks[collectionName]) { this.callbacks[collectionName] = []; } // 重新订阅WebSocket,包括新添加的实体类型 if (this.stompClient && this.stompClient.connected) { this.resubscribeWebSocketTopics(); } // 加载实体数据 await this.loadEntityFromIndexedDB(entityType); // 如果有网络连接,同步数据 if (this.networkAvailable) { const lastSyncTime = await this.indexedDBManager.getMetadata('lastSyncTime'); await this.fetchEntityData(entityType, lastSyncTime); } } /** * 初始化指定的实体类型数据 * @param {Array} entityTypes - 要初始化的实体类型数组 */ async initialize(entityTypes = []) { // 防止重复初始化 if (this.isInitializing) { return this.initializationPromise; } this.isInitializing = true; this.initializationPromise = this._initializeInternal(entityTypes); try { return await this.initializationPromise; } finally { this.isInitializing = false; } } /** * 内部初始化方法 */ async _initializeInternal(entityTypes = []) { try { // 初始化IndexedDB await this.indexedDBManager.init(); // 如果没有指定实体类型,使用配置的核心实体类型 if (entityTypes.length === 0) { entityTypes = this.entityConfig.coreEntityTypes || ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'user']; } // 初始化每个实体类型的数据 const totalTypes = entityTypes.length; let completedCount = 0; // 先尝试从本地数据获取最后更新时间 const lastSyncTime = await this.indexedDBManager.getMetadata('lastSyncTime'); // 定义每个实体类型的加载函数 const loadEntityType = async (entityType) => { try { // 先从IndexedDB加载数据 const localData = await this.loadEntityFromIndexedDB(entityType); if (localData && localData.length > 0) { this._rawData[`${entityType}s`] = localData; } // 如果有网络连接,使用时间增量获取后端数据补全 if (this.networkAvailable) { await this.fetchEntityData(entityType, lastSyncTime); } // 添加到已初始化集合 this.initializedEntityTypes.add(entityType); return true; } catch (error) { console.error(`初始化${entityType}数据失败:`, error); return false; } finally { // 更新进度 completedCount++; if (this.onProgressUpdate) { const progressValue = Math.floor((completedCount / totalTypes) * 80) + 10; const progress = Math.min(90, progressValue); this.onProgressUpdate(progress); } } }; // 使用Promise.all实现并行加载,但限制并发数以避免事务冲突 const batchSize = 3; // 控制并发数 let results = []; for (let i = 0; i < entityTypes.length; i += batchSize) { const batch = entityTypes.slice(i, i + batchSize); const batchResults = await Promise.all(batch.map(type => loadEntityType(type))); results = results.concat(batchResults); } this._rawData._lastSync = new Date().toISOString(); // 保存到IndexedDB await this.saveDataToIndexedDB(); // 更新进度到100% if (this.onProgressUpdate) { this.onProgressUpdate(100); } // 初始化WebSocket(在数据完全加载完成后) this.initWebSocket(); return true; } catch (error) { console.error('初始化失败:', error); if (this._rawData._lastSync) return true; throw error; } } /** * 从IndexedDB加载单个实体类型数据 */ async loadEntityFromIndexedDB(entityType) { try { const collectionName = `${entityType}s`; const data = await this.indexedDBManager.getAll(collectionName); return data || []; } catch (error) { console.error(`从IndexedDB加载${entityType}数据失败:`, error); return []; } } /** * 解析集合中的数据引用 */ resolveDataReferencesForCollection(collectionName) { if (!this._rawData[collectionName]) return; const entities = this._rawData[collectionName]; for (const entity of entities) { // 为每个实体解析引用 resolveDataReference(entity, this._rawData); } } /** * 初始化WebSocket连接 */ initWebSocket() { try { // 构建WebSocket URL(注意:SockJS需要使用HTTP或HTTPS协议而不是WS或WSS) const wsProtocol = window.location.protocol === 'https:' ? 'https:' : 'http:'; const wsHost = window.location.host; const wsUrl = `${wsProtocol}//${wsHost}/ws`; // 使用SockJS客户端 this.socket = new SockJS(wsUrl); this.stompClient = Stomp.over(this.socket); // 配置WebSocket连接 this.stompClient.connect({}, () => { // 订阅实体更新主题 this.resubscribeWebSocketTopics(); }, (error) => { console.error('WebSocket连接失败:', error); // 连接失败时尝试重新连接 setTimeout(() => this.initWebSocket(), 5000); }); // 监听连接关闭事件 this.socket.onclose = () => { // 连接关闭时尝试重新连接 setTimeout(() => this.initWebSocket(), 5000); }; } catch (error) { console.error('WebSocket初始化失败:', error); // 初始化失败时尝试重新初始化 setTimeout(() => this.initWebSocket(), 5000); } } /** * 根据已初始化的实体类型重新订阅WebSocket主题 */ resubscribeWebSocketTopics() { // 先取消所有现有订阅 if (this.webSocketSubscriptions) { this.webSocketSubscriptions.forEach(subscription => subscription.unsubscribe()); } // 重新创建订阅数组 this.webSocketSubscriptions = []; // 订阅所有实体更新主题 const allSubscription = this.stompClient.subscribe('/topic/entity-updates', (message) => { this.handleWebSocketMessage(message); }); this.webSocketSubscriptions.push(allSubscription); // 根据已初始化的实体类型订阅特定主题 this.initializedEntityTypes.forEach(entityType => { const entitySubscription = this.stompClient.subscribe(`/topic/${entityType}-updates`, (message) => { this.handleWebSocketMessage(message); }); this.webSocketSubscriptions.push(entitySubscription); }); } /** * 处理WebSocket消息 */ async handleWebSocketMessage(message) { try { const updateData = JSON.parse(message.body); const { operation, entityType, id, entityData } = updateData; // 转换实体类型为复数形式(集合名) const collectionName = this.getCollectionName(entityType); // 检查是否是已初始化的实体类型 if (collectionName && (this.initializedEntityTypes.has(entityType) || this.initializedEntityTypes.has(collectionName.replace(/s$/, '')))) { // 确保集合存在 if (!this._rawData[collectionName]) { this._rawData[collectionName] = []; } if (operation === 'create' || operation === 'update') { // 添加或更新实体 const index = this._rawData[collectionName].findIndex(item => item.id === id); if (index >= 0) { this._rawData[collectionName][index] = entityData; } else { this._rawData[collectionName].push(entityData); } // 更新到IndexedDB await this.indexedDBManager.put(collectionName, entityData); } else if (operation === 'delete') { // 删除实体 this._rawData[collectionName] = this._rawData[collectionName].filter(item => item.id !== id); // 从IndexedDB删除 await this.indexedDBManager.delete(collectionName, id); } // 触发回调,包含数据刷新类型 this.triggerCallbacks(operation, collectionName, entityData); // 清除缓存 this.lazyLoader.clearCache(); // 保存最后更新时间 await this.indexedDBManager.setMetadata('lastSyncTime', new Date().toISOString()); } } catch (error) { console.error('处理WebSocket消息失败:', error); // 触发错误回调,包含错误信息 this.triggerCallbacks('websocket_error', 'all', { error: error.message }); } } /** * 获取实体类型对应的集合名 */ getCollectionName(entityType) { // 转换首字母为小写 const lowerCaseType = entityType.charAt(0).toLowerCase() + entityType.slice(1); // 检查LazyLoader中的映射表 return this.lazyLoader.entityToCollectionMap[lowerCaseType] || `${lowerCaseType}s`; } async initNetwork() { // Web环境默认网络可用 this.networkAvailable = navigator.onLine; // 监听网络状态变化 window.addEventListener('online', () => { this.networkAvailable = true; }); window.addEventListener('offline', () => { this.networkAvailable = false; }); } /** * 同步指定实体类型的数据 * @param {String} entityType - 实体类型 */ async syncEntityData(entityType) { if (this.isSyncing) return; this.isSyncing = true; try { const since = this._rawData._lastSync; const success = await this.fetchEntityData(entityType, since); if (success) { this.lazyLoader.clearCache(); await this.saveDataToIndexedDB(); this.triggerCallbacks('refresh', `${entityType}s`, this.data); } } catch (error) { console.error(`同步${entityType}数据失败:`, error); this.triggerCallbacks('sync_error', entityType, { error }); } finally { this.isSyncing = false; } } /** * 添加重复检查规则 * @param {String} entityType - 实体类型 * @param {Function} rule - 检查规则函数 */ addDuplicateCheckRule(entityType, rule) { if (typeof rule === 'function') { this.entityConfig.duplicateCheckRules[entityType] = rule; } } /** * 检查重复数据 * @param {String} entityType - 实体类型 * @param {Object} data - 要检查的数据 */ checkDuplicate(entityType, data) { // 获取实体类型对应的集合 const collectionName = `${entityType}s`; const collection = this._rawData[collectionName] || []; // 检查是否有对应的检查规则 if (this.entityConfig.duplicateCheckRules[entityType]) { return this.entityConfig.duplicateCheckRules[entityType](data, collection); } // 默认不检查重复 return false; } /** * 获取指定实体类型的数据 * @param {String} entityType - 实体类型 * @param {String} since - 上次同步时间 */ async fetchEntityData(entityType, since = null) { try { const params = since ? { since } : {}; const url = since ? `${this.baseUrl}/all/${entityType}?since=${encodeURIComponent(since)}` : `${this.baseUrl}/all/${entityType}`; const response = await fetch(url); if (!response.ok) throw new Error(`HTTP错误: ${response.status}`); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || 'API错误'); const collectionName = `${entityType}s`; const entityData = result.data || []; // 处理解析到的数据 if (since) { // 增量更新 if (!this._rawData[collectionName]) { this._rawData[collectionName] = []; } try { // 创建所有更新Promise const updatePromises = entityData.map(newItem => { const index = this._rawData[collectionName].findIndex(item => item.id === newItem.id); if (index >= 0) { this._rawData[collectionName][index] = newItem; } else { this._rawData[collectionName].push(newItem); } // 更新到IndexedDB return this.indexedDBManager.put(collectionName, newItem); }); // 等待所有更新完成 await Promise.all(updatePromises); } catch (dbError) { console.error(`保存${entityType}增量数据到IndexedDB失败:`, dbError); // 继续执行,不中断流程 } } else { // 全量更新 this._rawData[collectionName] = entityData; // 更新到IndexedDB(先清空再添加) try { await this.indexedDBManager.clear(collectionName); if (entityData.length > 0) { // 分批保存大量数据,避免IndexedDB操作超时 const batchSize = 50; for (let i = 0; i < entityData.length; i += batchSize) { const batch = entityData.slice(i, i + batchSize); await this.indexedDBManager.putAll(collectionName, batch); } } } catch (dbError) { console.error(`IndexedDB操作失败:`, dbError); // 这里不抛出错误,继续执行后续操作 } } // 确保回调数组存在 if (!this.callbacks[collectionName]) { this.callbacks[collectionName] = []; } // 更新最后同步时间 try { const syncTime = new Date().toISOString(); await this.indexedDBManager.setMetadata('lastSyncTime', syncTime); } catch (metaError) { console.error(`更新${entityType}最后同步时间失败:`, metaError); } return true; } catch (error) { console.error(`获取${entityType}数据失败:`, error); this.triggerCallbacks('fetch_error', entityType, { error }); // 不要抛出异常,继续处理其他实体类型 return false; } } /** * 同步所有已加载的实体类型数据 * 主要用于初始化完成后,按需同步特定实体 */ async syncData() { if (this.isSyncing) return; // 获取所有已加载的实体类型 const entityTypes = []; for (const key in this._rawData) { if (!key.startsWith('_') && Array.isArray(this._rawData[key])) { // 从集合名转换为实体类型(去掉末尾的s) const entityType = key.replace(/s$/, ''); entityTypes.push(entityType); } } // 使用Promise.all实现并行同步,但限制并发数 const batchSize = 3; let results = []; for (let i = 0; i < entityTypes.length; i += batchSize) { const batch = entityTypes.slice(i, i + batchSize); const batchResults = await Promise.all(batch.map(type => this.syncEntityData(type))); results = results.concat(batchResults); } this._rawData._lastSync = new Date().toISOString(); await this.saveDataToIndexedDB(); } async fetchAll(since) { try { // 首先确保IndexedDB已初始化 try { await this.indexedDBManager.init(); } catch (initError) { console.warn('IndexedDB初始化失败,但会继续尝试在需要时自动初始化:', initError); } // 通知开始加载数据,设置初始进度为10% if (this.onProgressUpdate) { this.onProgressUpdate(10); } // 不使用app/all接口,而是通过循环调用单个实体接口 const defaultEntityTypes = this.entityConfig.coreEntityTypes || ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'user']; const totalTypes = defaultEntityTypes.length; let completedCount = 0; // 在并行加载前,批量创建所有需要的IndexedDB集合,避免多次版本升级 if (this.indexedDBManager.batchEnsureCollections) { try { // 将实体类型转换为复数形式的集合名,确保只创建一套集合 const collectionNames = defaultEntityTypes.map(type => this.getCollectionName(type)); await this.indexedDBManager.batchEnsureCollections(collectionNames); } catch (batchError) { console.error('批量确保IndexedDB集合存在失败:', batchError); // 即使失败也继续,让单个操作尝试创建集合 } } // 使用Promise.all实现并行加载,但限制并发数 const batchSize = 3; let results = []; // 确保批次循环完整执行的增强实现 let currentBatchIndex = 0; while (currentBatchIndex < defaultEntityTypes.length) { const batch = defaultEntityTypes.slice(currentBatchIndex, currentBatchIndex + batchSize); const batchNumber = Math.floor(currentBatchIndex / batchSize) + 1; // 为每个实体类型创建一个带try-catch的Promise,确保单个实体失败不会影响整个批次 const batchPromises = batch.map(async (entityType) => { try { const result = await this.fetchEntityData(entityType, since); // 更新进度 completedCount++; if (this.onProgressUpdate) { const progressValue = Math.floor((completedCount / totalTypes) * 90) + 10; const progress = Math.min(90, progressValue); this.onProgressUpdate(progress); } return result; } catch (error) { console.error(`处理${entityType}时发生错误:`, error); // 即使出错,也要增加完成计数并更新进度,确保流程继续 completedCount++; if (this.onProgressUpdate) { const progressValue = Math.floor((completedCount / totalTypes) * 90) + 10; const progress = Math.min(90, progressValue); this.onProgressUpdate(progress); } return false; } }); try { // 使用Promise.allSettled代替Promise.all,确保所有Promise都能完成 const batchResults = await Promise.all(batchPromises); results = results.concat(batchResults); } catch (batchError) { console.error(`批次${batchNumber}处理异常:`, batchError); // 即使批次处理异常,也要继续处理下一批 } // 确保批次索引正确增加 currentBatchIndex += batchSize; } // 通知数据获取完成 if (this.onProgressUpdate) { this.onProgressUpdate(100); } return true; } catch (error) { console.error('获取所有数据失败:', error); this.triggerCallbacks('fetch_error', 'all', { error }); // 不要抛出异常,继续处理其他实体类型 return false; } } registerCallback(entity, callback) { // 动态创建回调数组 if (!this.callbacks[entity]) { this.callbacks[entity] = []; } this.callbacks[entity].push(callback); } unregisterCallback(entity, callback) { const arr = this.callbacks[entity] || this.callbacks.all; const index = arr.indexOf(callback); if (index !== -1) arr.splice(index, 1); } triggerCallbacks(operation, entity, data) { // 触发所有回调 this.callbacks.all.forEach(cb => cb(operation, entity, data)); // 触发特定实体的回调 this.callbacks[entity]?.forEach(cb => cb(operation, data)); } async crudOperation(operation, entity, data) { try { const response = await fetch(`${this.baseUrl}/app/${operation}/${entity}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Network response was not ok'); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || 'API error'); const resultData = result.data || data; this.updateLocalData(operation, entity, resultData); this.triggerCallbacks(operation, entity, resultData); return resultData; } catch (error) { this.triggerCallbacks(`${operation}_error`, entity, { data, error }); throw error; } } async updateLocalData(operation, entity, data) { const key = `${entity}s`; // 确保集合存在 if (!this._rawData[key]) { this._rawData[key] = []; } // 确保回调数组存在 if (!this.callbacks[key]) { this.callbacks[key] = []; } const collection = this._rawData[key]; switch (operation) { case 'add': collection.push(resolveDataReference(data, this._rawData)); // 保存到IndexedDB await this.indexedDBManager.put(key, data); break; case 'update': const index = collection.findIndex(item => item.id === data.id); index >= 0 ? Object.assign(collection[index], data) : collection.push(resolveDataReference(data, this._rawData)); // 保存到IndexedDB await this.indexedDBManager.put(key, data); break; case 'delete': const deleteIndex = collection.findIndex(item => item.id === data.id); if (deleteIndex >= 0) collection.splice(deleteIndex, 1); // 从IndexedDB删除 await this.indexedDBManager.delete(key, data.id); break; } this._rawData._lastModified = new Date().toISOString(); this.lazyLoader.clearCache(); // 保存元数据 await this.indexedDBManager.setMetadata('lastSyncTime', new Date().toISOString()); } async loadDataFromIndexedDB() { try { await this.indexedDBManager.init(); // 获取所有实体类型(从配置中获取或使用默认值) const entityTypes = this.entityConfig.entityTypes || ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'dingdan_bancai', 'chanpin_zujian', 'zujian', 'caizhi', 'dingdan_chanpin', 'user', 'jinhuo']; // 使用Promise.all实现并行加载,但限制并发数 const batchSize = 3; for (let i = 0; i < entityTypes.length; i += batchSize) { const batch = entityTypes.slice(i, i + batchSize); await Promise.all(batch.map(entityType => { const collectionName = `${entityType}s`; return this.indexedDBManager.getAll(collectionName).then(data => { if (data && data.length > 0) { this._rawData[collectionName] = data; } }); })); } // 加载最后同步时间 const lastSyncTime = await this.indexedDBManager.getMetadata('lastSyncTime'); if (lastSyncTime) { this._rawData._lastSync = lastSyncTime; } } catch (error) { console.error('从IndexedDB加载数据失败:', error); } } async saveDataToIndexedDB() { try { // 获取所有实体类型集合 const collectionKeys = Object.keys(this._rawData).filter(key => !key.startsWith('_') && Array.isArray(this._rawData[key]) ); // 使用Promise.all实现并行保存,但限制并发数 const batchSize = 3; let savedCollections = 0; for (let i = 0; i < collectionKeys.length; i += batchSize) { const batch = collectionKeys.slice(i, i + batchSize); // 处理当前批次中的每个集合 await Promise.all(batch.map(async key => { const data = this._rawData[key]; if (data && data.length > 0) { // 先清空再添加 await this.indexedDBManager.clear(key); await this.indexedDBManager.putAll(key, data); } // 更新保存进度 savedCollections++; const saveProgress = Math.floor((savedCollections / collectionKeys.length) * 20); if (this.onProgressUpdate) { this.onProgressUpdate(saveProgress); } })); } // 保存最后同步时间 this._rawData._lastSync = new Date().toISOString(); await this.indexedDBManager.setMetadata('lastSyncTime', this._rawData._lastSync); } catch (error) { console.error('保存数据到IndexedDB失败:', error); } } // 兼容旧代码,保留localStorage方法但内部使用IndexedDB loadDataFromStorage() { this.loadDataFromIndexedDB(); } saveDataToStorage() { this.saveDataToIndexedDB(); } // 移除循环引用的辅助方法 removeCircularReferences(obj, seen = new WeakSet()) { if (obj === null || typeof obj !== 'object') { return obj; } if (seen.has(obj)) { return {}; // 返回空对象而不是循环引用 } seen.add(obj); if (Array.isArray(obj)) { return obj.map(item => this.removeCircularReferences(item, seen)); } const result = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { result[key] = this.removeCircularReferences(obj[key], seen); } } return result; } async addEntity(entity, data) { // 检查重复数据 if (this.checkDuplicate(entity, data)) { const errorMsg = `${this.entityConfig.entityTexts[entity] || ''}`; this.triggerCallbacks('duplicate_error', entity, { data, error: errorMsg }); throw new Error(errorMsg); } return this.crudOperation('add', entity, data); } async updateEntity(entity, data) { return this.crudOperation('update', entity, data); } async deleteEntity(entity, id) { return this.crudOperation('delete', entity, { id }); } /** * * @param {*} endpoint * @param {*} data * @returns */ async transactionalOperation(endpoint, data) { return this.Transaction.execute(endpoint, data); } /** * 动态获取数据的方法 * @param {string} entityType - 实体类型名称或集合名称 * @returns {Array} 实体数据数组 */ getEntity(entityType) { const key = entityType.endsWith('s') ? entityType : `${entityType}s`; return this.data[key] || []; } /** * 动态生成实体数据获取方法 */ generateEntityGetters() { const entityTypes = this.entityConfig.entityTypes || []; entityTypes.forEach(entityType => { // 创建驼峰式命名的方法名 const getterName = `get${entityType.charAt(0).toUpperCase() + entityType.slice(1)}s`; // 只有在方法不存在的情况下才创建 if (!this[getterName]) { this[getterName] = () => this.getEntity(`${entityType}s`); } }); } // 保留原有的快捷获取方法,确保向后兼容 getDingdans() { return this.getEntity('dingdans'); } getBancais() { return this.getEntity('bancais'); } getChanpins() { return this.getEntity('chanpins'); } getZujians() { return this.getEntity('zujians'); } getKucuns() { return this.getEntity('kucuns'); } getUsers() { return this.getEntity('users'); } getCaizhis() { return this.getEntity('caizhis'); } getMupis() { return this.getEntity('mupis'); } getDingdanBancais() { return this.getEntity('dingdan_bancais'); } getJinhuos() { return this.getEntity('jinhuos'); } getChanpinZujians() { return this.getEntity('chanpin_zujians'); } getDingdanChanpins() { return this.getEntity('dingdan_chanpins'); } // 业务方法 getChanpinsForDingdan(dingdanId) { const dingdan = this._rawData.dingdans?.find(d => d?.id == dingdanId); if (!dingdan) return []; return (dingdan.dingdan_chanpin_list || []) .map(dc => dc.chanpin) .filter(c => c); } getZujiansForChanpin(chanpinId) { const chanpin = this._rawData.chanpins?.find(c => c?.id == chanpinId); if (!chanpin) return []; return (chanpin.chanpin_zujian_list || []) .map(cz => cz.zujian) .filter(z => z); } getShengchanXiaohaoRecords() { return this._rawData.jinhuos?.filter(jinhuo => jinhuo.the_type_of_operation === 2 && jinhuo.shuliang < 0 ) || []; } getShengchanStatistics() { const today = new Date().toISOString().split('T')[0]; const thisMonth = new Date().toISOString().substring(0, 7); const consumptionRecords = this.getShengchanXiaohaoRecords(); const todayConsumption = consumptionRecords .filter(record => record.date && record.date.startsWith(today)) .reduce((sum, record) => sum + Math.abs(record.shuliang), 0); const monthConsumption = consumptionRecords .filter(record => record.date && record.date.startsWith(thisMonth)) .reduce((sum, record) => sum + Math.abs(record.shuliang), 0); const pendingOrders = this._rawData.dingdans?.filter(dingdan => !dingdan.deleted && dingdan.zhuangtai !== '已完成' ).length || 0; const lowStockCount = this._rawData.kucuns?.filter(kucun => !kucun.deleted && kucun.shuliang < 10 ).length || 0; return { today_consumption: todayConsumption, month_consumption: monthConsumption, pending_orders: pendingOrders, low_stock_count: lowStockCount, total_records: consumptionRecords.length }; } } export { DataManager }; ================================================================================ /* 文件路径: data/IndexedDBManager.js */ /** * IndexedDB工具类 - 用于大数据存储 */ class IndexedDBManager { constructor(dbName = 'appData', version = 1) { this.dbName = dbName; this.version = version; this.db = null; this.collections = new Set(); // 添加版本升级队列,解决IndexedDB不允许同时进行多个版本升级的问题 this.upgradeQueue = []; this.isUpgrading = false; } /** * 初始化数据库连接 */ async init() { return new Promise((resolve, reject) => { if (this.db) { resolve(this.db); return; } // 不指定版本号,让浏览器自动使用最新版本 // 这样即使数据库版本高于代码中指定的版本,也能成功打开 const request = indexedDB.open(this.dbName); request.onupgradeneeded = (event) => { const db = event.target.result; // 确保存储元数据的集合存在 if (!db.objectStoreNames.contains('metadata')) { db.createObjectStore('metadata', { keyPath: 'key' }); } // 更新版本号缓存 this.version = db.version; }; request.onsuccess = (event) => { this.db = event.target.result; // 更新版本号缓存 this.version = this.db.version; resolve(this.db); }; request.onerror = (event) => { console.error('IndexedDB初始化失败:', event.target.error); reject(event.target.error); }; }); } /** * 确保对象存储空间(集合)存在 */ async ensureCollection(collectionName) { // 如果是单个集合请求,使用队列处理 return this._ensureCollection(collectionName); } /** * 批量确保多个对象存储空间存在 * 避免多次触发版本升级 */ async batchEnsureCollections(collectionNames) { if (!Array.isArray(collectionNames) || collectionNames.length === 0) { return Promise.resolve([]); } // 首先确保数据库已初始化 const db = await this.init(); // 检查哪些集合已经存在,哪些需要创建 const existingCollections = new Set(); const collectionsToCreate = []; for (const collectionName of collectionNames) { if (this.collections.has(collectionName)) { existingCollections.add(collectionName); } else if (db.objectStoreNames.contains(collectionName)) { this.collections.add(collectionName); existingCollections.add(collectionName); } else { collectionsToCreate.push(collectionName); } } // 如果有需要创建的集合,一次性创建 if (collectionsToCreate.length > 0) { await this._batchCreateCollections(collectionsToCreate); } return [...existingCollections, ...collectionsToCreate]; } /** * 内部方法:确保单个集合存在(使用队列处理) */ async _ensureCollection(collectionName) { try { if (this.collections.has(collectionName)) { return true; } const db = await this.init(); // 检查对象存储空间是否已存在 if (db.objectStoreNames.contains(collectionName)) { this.collections.add(collectionName); return true; } // 需要升级数据库来创建新的对象存储空间 // 使用队列确保一次只进行一个版本升级 return new Promise((resolve, reject) => { // 将升级请求加入队列 this.upgradeQueue.push({ collectionName, resolve, reject }); // 如果当前没有升级在进行,开始处理队列 if (!this.isUpgrading) { this._processUpgradeQueue(); } }); } catch (error) { console.error(`ensureCollection方法执行错误:`, error); // 出错时返回一个拒绝的Promise,保持返回类型一致 return Promise.reject(error); } } /** * 批量创建多个对象存储空间 * 整合到升级队列中,确保与其他升级操作不冲突 */ async _batchCreateCollections(collectionNames) { if (!collectionNames || collectionNames.length === 0) { return Promise.resolve(); } // 将批量创建请求作为一个队列项加入升级队列 return new Promise((resolve, reject) => { // 将升级请求加入队列 this.upgradeQueue.push({ batchCollections: collectionNames, resolve, reject }); // 如果当前没有升级在进行,开始处理队列 if (!this.isUpgrading) { this._processUpgradeQueue(); } }); } /** * 处理版本升级队列,确保一次只进行一个版本升级 * 支持单个集合批量集合的创建 */ async _processUpgradeQueue() { if (this.isUpgrading || this.upgradeQueue.length === 0) { return; } this.isUpgrading = true; const queueItem = this.upgradeQueue.shift(); const { collectionName, batchCollections, resolve, reject } = queueItem; try { // 首先关闭现有连接,避免版本升级被阻塞 this.close(); // 重新初始化数据库以获取最新连接 const db = await this.init(); // 执行版本升级 const newVersion = db.version + 1; // 判断是单个集合还是批量集合升级 const collectionsToCreate = batchCollections || [collectionName]; const isBatchOperation = !!batchCollections; const request = indexedDB.open(this.dbName, newVersion); // 处理版本升级被阻塞的情况 request.onblocked = (event) => { console.warn(`数据库版本升级被阻塞,可能有其他打开的连接。尝试关闭所有连接...`); // 再次关闭连接,确保没有其他连接阻止升级 this.close(); }; request.onupgradeneeded = (event) => { const newDb = event.target.result; // 创建所有需要的对象存储空间 for (const name of collectionsToCreate) { if (!newDb.objectStoreNames.contains(name)) { newDb.createObjectStore(name, { keyPath: 'id' }); } } }; request.onsuccess = (event) => { this.db = event.target.result; // 将所有创建的集合添加到缓存 for (const name of collectionsToCreate) { this.collections.add(name); } resolve(true); // 处理下一个队列项 this.isUpgrading = false; this._processUpgradeQueue(); }; request.onerror = (event) => { console.error(`${isBatchOperation ? `批量创建集合失败` : `创建集合${collectionName}失败`}:`, event.target.error); reject(event.target.error); // 即使出错,也要处理下一个队列项 this.isUpgrading = false; this._processUpgradeQueue(); }; } catch (error) { console.error(`处理队列项时发生错误:`, error); reject(error); // 即使出错,也要处理下一个队列项 this.isUpgrading = false; this._processUpgradeQueue(); } } /** * 执行IndexedDB事务 */ async transaction(collectionName, mode, callback) { try { // 确保数据库已初始化 if (!this.db) { await this.init(); } // 确保集合存在 await this.ensureCollection(collectionName); // 执行事务 return new Promise((resolve, reject) => { try { const transaction = this.db.transaction([collectionName], mode); const store = transaction.objectStore(collectionName); // 设置事务事件处理器 transaction.onerror = (event) => { console.error(`IndexedDB事务错误: ${collectionName}`, event.target.error); reject(event.target.error); }; transaction.onabort = (event) => { console.error(`IndexedDB事务中止: ${collectionName}`, event.target.error); reject(event.target.error); }; // 执行回调并处理返回结果 try { const result = callback(store); // 如果回调返回的是Promise,等待其完成 if (result instanceof Promise) { result.then(resolve).catch(reject); } else { resolve(result); } } catch (callbackError) { console.error(`回调执行出错: collection=${collectionName}`, callbackError); reject(callbackError); } } catch (error) { console.error('创建事务时出错:', error); reject(error); } }); } catch (error) { console.error(`IndexedDB事务失败: ${collectionName}`, error); // 不抛出错误,而是返回一个被拒绝的Promise,确保上层调用者能捕获错误 return Promise.reject(error); } } /** * 存储单个实体 */ async put(collectionName, entity) { return this.transaction(collectionName, 'readwrite', (store) => { return store.put(entity); }); } /** * 存储多个实体 */ async putAll(collectionName, entities) { return this.transaction(collectionName, 'readwrite', (store) => { return new Promise((resolve, reject) => { const requests = entities.map(entity => store.put(entity)); let completed = 0; requests.forEach(request => { request.onsuccess = () => { completed++; if (completed === requests.length) { resolve(entities.length); } }; request.onerror = (event) => { reject(event.target.error); }; }); }); }); } /** * 获取单个实体 */ async get(collectionName, id) { return this.transaction(collectionName, 'readonly', (store) => { return store.get(id); }); } /** * 获取所有实体 */ async getAll(collectionName) { return this.transaction(collectionName, 'readonly', (store) => { return store.getAll(); }); } /** * 删除单个实体 */ async delete(collectionName, id) { return this.transaction(collectionName, 'readwrite', (store) => { return store.delete(id); }); } /** * 清空集合 */ async clear(collectionName) { return this.transaction(collectionName, 'readwrite', (store) => { return store.clear(); }); } /** * 获取集合中实体的数量 */ async count(collectionName) { return this.transaction(collectionName, 'readonly', (store) => { return store.count(); }); } /** * 查询实体 */ async query(collectionName, query) { return this.transaction(collectionName, 'readonly', (store) => { const results = []; const request = store.openCursor(); return new Promise((resolve, reject) => { request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { // 执行查询条件 if (!query || this.matchesQuery(cursor.value, query)) { results.push(cursor.value); } cursor.continue(); } else { resolve(results); } }; request.onerror = (event) => { reject(event.target.error); }; }); }); } /** * 检查实体是否匹配查询条件 */ matchesQuery(entity, query) { for (const key in query) { if (entity[key] !== query[key]) { return false; } } return true; } /** * 存储元数据 */ async setMetadata(key, value) { return this.put('metadata', { key, value }); } /** * 获取元数据 */ async getMetadata(key) { const metadata = await this.get('metadata', key); return metadata ? metadata.value : null; } /** * 关闭数据库连接 */ close() { if (this.db) { this.db.close(); this.db = null; } } /** * 删除整个数据库 */ async deleteDatabase() { return new Promise((resolve, reject) => { this.close(); const request = indexedDB.deleteDatabase(this.dbName); request.onsuccess = () => resolve(true); request.onerror = (event) => reject(event.target.error); }); } } export { IndexedDBManager }; ================================================================================ /* 文件路径: data/LazyLoader.js */ /** * 懒加载器 - Web版 (优化版) * 修复了缓存键设计、集合名解析数组处理问题 */ class LazyLoader { constructor(dataManager) { this.dataManager = dataManager; // 使用WeakMap避免内存泄漏 this.proxyCache = new WeakMap(); // 实体名到集合名的映射表 this.entityToCollectionMap = { bancai: 'bancais', dingdan: 'dingdans', mupi: 'mupis', chanpin: 'chanpins', kucun: 'kucuns', chanpin_zujian: 'chanpin_zujians', dingdan_bancai: 'dingdan_bancais', zujian: 'zujians', caizhi: 'caizhis', dingdan_chanpin: 'dingdan_chanpins', user: 'users', jinhuo: 'jinhuos' }; } /** * 清除缓存 */ clearCache() { this.proxyCache = new WeakMap(); } /** * 创建数据代理 * @param {Object} item - 原始数据项 * @param {string} entityType - 实体类型 * @returns {Proxy} 代理对象 */ createProxy(item, entityType) { // 非对象直接返回 if (!item || typeof item !== 'object') return item; // 检查是否已有代理 if (this.proxyCache.has(item)) { return this.proxyCache.get(item); } const proxy = new Proxy(item, { get: (target, prop) => { // 直接返回基本属性 if (typeof prop === 'symbol' || prop.startsWith('_') || typeof target[prop] !== 'object') { return target[prop]; } const value = target[prop]; // 处理null值 if (value === null) return null; // 处理数组关联 if (Array.isArray(value)) { return value.map(relatedItem => this.resolveRelation(relatedItem, prop) ); } // 处理对象关联 else if (typeof value === 'object') { return this.resolveRelation(value, prop); } return value; }, set: (target, prop, value) => { target[prop] = value; return true; } }); // 缓存代理对象 this.proxyCache.set(item, proxy); return proxy; } /** * 解析关联对象 * @param {Object|string|number} relatedItem - 关联项 * @param {string} propName - 关联属性名 * @returns {Object} 解析后的关联对象 */ resolveRelation(relatedItem, propName) { // 基本类型直接返回 if (typeof relatedItem !== 'object' || relatedItem === null) { return relatedItem; } // 去掉数字后缀 (如 mupi1 → mupi) const basePropName = propName.replace(/\d/g, ''); // 获取实体类型 let entityType; // 检查是否已在映射表中 if (this.entityToCollectionMap[basePropName]) { entityType = basePropName; } else { // 尝试从集合名反向查找实体类型 entityType = Object.keys(this.entityToCollectionMap).find( key => this.entityToCollectionMap[key] === basePropName ) || basePropName; } // 获取集合名 let collectionName; // 检查映射表中是否有对应的集合名 if (this.entityToCollectionMap[entityType]) { collectionName = this.entityToCollectionMap[entityType]; } else { // 动态处理新的实体类型,默认添加's'后缀 collectionName = `${entityType}s`; // 将新的映射关系添加到映射表中 this.entityToCollectionMap[entityType] = collectionName; } // 获取数据集合 const collection = this.dataManager._rawData[collectionName]; if (!collection) return relatedItem; // 查找完整对象 const fullItem = collection.find(item => item?.id === relatedItem?.id); if (!fullItem) return relatedItem; // 返回代理对象 return this.createProxy(fullItem, entityType); } } export { LazyLoader }; ================================================================================ /* 文件路径: data/TransactionApi.js */ /** * 事务API - Web版 * 负责处理所有事务相关的操作 */ class TransactionApi { constructor(dataManager) { this.dataManager = dataManager; this.baseUrl = dataManager.baseUrl; } /** * 执行事务操作 * @param {string} endpoint - 事务端点 * @param {Object} data - 事务数据 * @returns {Promise} 操作结果 */ async execute(endpoint, data) { try { const response = await fetch(`${this.baseUrl}/app/transactional/${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); if (result.status !== 200) { throw new Error(result.text || 'Transaction failed'); } // 在WebSocket模式下,数据会自动更新,无需手动同步 // 仅触发事务成功回调 this.dataManager.triggerCallbacks('transaction_success', endpoint, result.data); return result.data; } catch (error) { console.error(`Transaction ${endpoint} failed:`, error); // 触发事务失败回调 this.dataManager.triggerCallbacks('transaction_error', endpoint, { data, error: error.message }); throw error; } } /** * 库存编辑事务 * @param {Object} params - 事务参数 * @param {Number} params.kucunId - 库存ID * @param {Number} params.newStock - 新的库存数量 * @param {Number} params.oldStock - 原库存数量 * @param {String} params.note - 备注信息 * @param {Number} params.userId - 用户ID * @returns {Promise} 操作结果 */ async updateStock(params) { return this.execute('kucunbianji', params); } /** * 生产消耗事务 * @param {Object} params - 生产消耗参数 * @returns {Promise} 操作结果 */ async shengchanXiaohao(params) { return this.execute('shengchanXiaohao', params); } /** * 批量更新订单板材事务 * @param {Object} params - 批量更新参数 * @returns {Promise} 操作结果 */ async batchUpdateDingdanBancai(params) { return this.execute('batchUpdateDingdanBancai', params); } /** * 提交订单板材采购事务 * @param {Object} params - 提交参数 * @returns {Promise} 操作结果 */ async submitDingdanBancai(params) { return this.execute('submitDingdanBancai', params); } /** * 消耗订单板材事务 * @param {Object} params - 消耗参数 * @returns {Promise} 操作结果 */ async consumeDingdanBancai(params) { return this.execute('consumeDingdanBancai', params); } /** * 采购订单板材事务 * @param {Object} params - 采购参数 * @returns {Promise} 操作结果 */ async 采购DingdanBancai(params) { return this.execute('采购DingdanBancai', params); } /** * 保存所有数据事务 * @param {Object} params - 保存参数 * @returns {Promise} 操作结果 */ async saveAll(params) { try { const response = await fetch(`${this.baseUrl}/app/save-all`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); if (result.status !== 200) { throw new Error(result.text || 'Save all failed'); } // 在WebSocket模式下,数据会自动更新,无需手动同步 return result.data; } catch (error) { console.error('Save all failed:', error); throw error; } } } export { TransactionApi };------------------------优化压缩
最新发布
09-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值