/* 文件路径: data/DataManager.js */
// @ts-nocheck
// 导入依赖
import { LazyLoader } from './LazyLoader.js';
import { TransactionApi } from './TransactionApi.js';
import { IndexedDBManager } from './IndexedDBManager.js';
// 确保SockJS和Stomp库已加载
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) {
console.log(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) {
console.log(entity)
//
// 遍历实体的每个属性
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 {
constructor(baseUrl = "../php_api/index.php") {
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._rawData = this.createEmptyData();
this.lazyLoader = new LazyLoader(this);
// 初始化回调对象,支持动态添加新的数据类型
this.callbacks = {
all: []
};
// 创建事务API实例
this.Transaction = new TransactionApi(this);
// 实体文本映射,支持动态添加
this.entiyeText = {
bancai: '板材已存在',
dingdan: '订单已存在',
mupi: '木皮已存在',
chanpin: '产品已存在',
kucun: '已有库存记录',
chanpin_zujian: '产品已有该组件',
dingdan_bancai: '',
zujian: '组件已定义过了',
caizhi: '材质已定义过了',
dingdan_chanpin: '订单下已有该产品',
user: ''
};
// 进度更新回调函数
this.onProgressUpdate = null;
this.initNetwork();
// 优先从IndexedDB加载数据
this.loadDataFromIndexedDB();
this.initWebSocket();
}
createEmptyData() {
// 基础数据结构,其他类型会在数据加载时自动添加
return {
bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [],
dingdan_bancais: [], chanpin_zujians: [], zujians: [], caizhis: [],
dingdan_chanpins: [], users: [], jinhuos: [],
_lastModified: null, _lastSync: null
};
}
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];
},
set: (target, prop, value) => {
// 允许内部修改,但给出警告
console.warn(`直接修改 data.${prop} 可能会影响数据一致性,建议使用 DataManager 的方法`);
target[prop] = value;
return true;
},
deleteProperty: (target, prop) => {
console.warn(`直接删除 data.${prop} 可能会影响数据一致性,建议使用 DataManager 的方法`);
delete target[prop];
return true;
}
});
}
/**
* 初始化指定的实体类型数据
* @param {Array} entityTypes - 要初始化的实体类型数组
*/
async initialize(entityTypes = []) {
try {
// 确保IndexedDB初始化完成
await this.indexedDBManager.init();
// 如果没有指定实体类型,使用默认的核心实体类型
if (entityTypes.length === 0) {
entityTypes = ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'user'];
}
// 初始化每个实体类型的数据
const totalTypes = entityTypes.length;
let completedCount = 0;
// 先尝试从本地数据获取最后更新时间
const lastSyncTime = await this.indexedDBManager.getMetadata('lastSyncTime');
for (const entityType of entityTypes) {
// 先从IndexedDB加载数据
const localData = await this.loadEntityFromIndexedDB(entityType);
if (localData && localData.length > 0) {
this._rawData[`${entityType}s`] = localData;
// 通知本地数据加载成功
if (this.onProgressUpdate) {
this.onProgressUpdate(30);
}
}
// 如果有网络连接,使用时间增量获取后端数据补全
if (this.networkAvailable) {
await this.fetchEntityData(entityType, lastSyncTime);
}
completedCount++;
// 更新整体进度
if (this.onProgressUpdate) {
const progress = Math.min(90, Math.floor((completedCount / totalTypes) * 90));
this.onProgressUpdate(progress);
}
}
this._rawData._lastSync = new Date().toISOString();
// 保存到IndexedDB
await this.saveDataToIndexedDB();
// 通知初始化完成
if (this.onProgressUpdate) {
this.onProgressUpdate(100);
}
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({}, () => {
console.log('WebSocket连接成功');
// 订阅实体更新主题
this.stompClient.subscribe('/topic/entity-updates', (message) => {
this.handleWebSocketMessage(message);
});
}, (error) => {
console.error('WebSocket连接失败:', error);
// 连接失败时尝试重新连接
setTimeout(() => this.initWebSocket(), 5000);
});
// 监听连接关闭事件
this.socket.onclose = () => {
console.log('WebSocket连接已关闭');
// 连接关闭时尝试重新连接
setTimeout(() => this.initWebSocket(), 5000);
};
} catch (error) {
console.error('WebSocket初始化失败:', error);
// 初始化失败时尝试重新初始化
setTimeout(() => this.initWebSocket(), 5000);
}
}
/**
* 处理WebSocket消息
*/
async handleWebSocketMessage(message) {
try {
const updateData = JSON.parse(message.body);
const { operation, entityType, id, entityData } = updateData;
// 转换实体类型为复数形式(集合名)
const collectionName = this.getCollectionName(entityType);
if (collectionName) {
// 确保集合存在
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.resolveDataReferencesForCollection(collectionName);
// 触发回调,包含数据刷新类型
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 {String} since - 上次同步时间
*/
async fetchEntityData(entityType, since = null) {
try {
// 通知开始加载数据
if (this.onProgressUpdate) {
this.onProgressUpdate(10);
}
const params = since ? { since } : {};
const url = since ?
`${this.baseUrl}/all/${entityType}?since=${encodeURIComponent(since)}` :
`${this.baseUrl}/all/${entityType}`;
// 通知正在获取数据
if (this.onProgressUpdate) {
this.onProgressUpdate(30);
}
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
// 通知数据获取成功
if (this.onProgressUpdate) {
this.onProgressUpdate(50);
}
const result = await response.json();
if (result.status !== 200) throw new Error(result.text || 'API错误');
// 通知正在解析数据
if (this.onProgressUpdate) {
this.onProgressUpdate(60);
}
const collectionName = `${entityType}s`;
const entityData = result.data || [];
// 处理解析到的数据
if (since) {
// 增量更新
if (!this._rawData[collectionName]) {
this._rawData[collectionName] = [];
}
entityData.forEach(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
this.indexedDBManager.put(collectionName, newItem);
});
} else {
// 全量更新
this._rawData[collectionName] = entityData;
// 更新到IndexedDB(先清空再添加)
await this.indexedDBManager.clear(collectionName);
if (entityData.length > 0) {
await this.indexedDBManager.putAll(collectionName, entityData);
}
}
// 确保回调数组存在
if (!this.callbacks[collectionName]) {
this.callbacks[collectionName] = [];
}
// 解析数据引用
this.resolveDataReferencesForCollection(collectionName);
// 通知数据保存完成
if (this.onProgressUpdate) {
this.onProgressUpdate(100);
}
// 更新最后同步时间
await this.indexedDBManager.setMetadata('lastSyncTime', new Date().toISOString());
return true;
} catch (error) {
console.error(`获取${entityType}数据失败:`, error);
this.triggerCallbacks('fetch_error', entityType, { error });
return false;
}
}
/**
* 同步所有已加载的实体类型数据
* 主要用于初始化完成后,按需同步特定实体
*/
async syncData() {
if (this.isSyncing) return;
console.warn('syncData方法已不推荐使用,请使用syncEntityData方法指定要同步的实体类型');
// 获取所有已加载的实体类型
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);
}
}
// 同步每个实体类型
for (const entityType of entityTypes) {
await this.syncEntityData(entityType);
}
this._rawData._lastSync = new Date().toISOString();
await this.saveDataToIndexedDB();
}
async fetchAll(since) {
try {
// 通知开始加载数据,设置初始进度为10%
if (this.onProgressUpdate) {
this.onProgressUpdate(10);
}
// 不使用app/all接口,而是通过循环调用单个实体接口
const defaultEntityTypes = ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun', 'user'];
const totalTypes = defaultEntityTypes.length;
let completedCount = 0;
for (const entityType of defaultEntityTypes) {
await this.fetchEntityData(entityType, since);
completedCount++;
// 更新整体进度
if (this.onProgressUpdate) {
const progress = Math.min(90, Math.floor((completedCount / totalTypes) * 90));
this.onProgressUpdate(progress);
}
}
// 通知数据获取完成
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 = ['bancai', 'dingdan', 'mupi', 'chanpin', 'kucun',
'dingdan_bancai', 'chanpin_zujian', 'zujian', 'caizhi',
'dingdan_chanpin', 'user', 'jinhuo'];
// 从IndexedDB加载每个实体类型的数据
for (const entityType of entityTypes) {
const collectionName = `${entityType}s`;
const data = await this.indexedDBManager.getAll(collectionName);
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 {
// 保存每个实体类型的数据
for (const key in this._rawData) {
if (!key.startsWith('_') && Array.isArray(this._rawData[key])) {
const data = this._rawData[key];
if (data && data.length > 0) {
// 先清空再添加
await this.indexedDBManager.clear(key);
await this.indexedDBManager.putAll(key, data);
}
}
}
// 保存最后同步时间
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;
}
checkDuplicate(entity, data) {
switch (entity) {
case 'bancai':
return this._rawData.bancais.some(b =>
b.houdu === data.houdu &&
b.caizhi?.id === data.caizhi?.id &&
b.mupi1?.id === data.mupi1?.id &&
b.mupi2?.id === data.mupi2?.id
);
case 'caizhi':
return this._rawData.caizhis.some(c => c.name === data.name);
case 'mupi':
return this._rawData.mupis.some(m => m.name === data.name && m.you === data.you);
case 'chanpin':
return this._rawData.chanpins.some(c => c.bianhao === data.bianhao);
case 'zujian':
return this._rawData.zujians.some(z => z.name === data.name);
case 'dingdan':
return this._rawData.dingdans.some(d => d.number === data.number);
case 'chanpin_zujian':
return this._rawData.chanpin_zujians.some(cz =>
cz.chanpin?.id === data.chanpin?.id &&
cz.zujian?.id === data.zujian?.id
);
case 'dingdan_chanpin':
return this._rawData.dingdan_chanpins.some(dc =>
dc.dingdan?.id === data.dingdan?.id &&
dc.chanpin?.id === data.chanpin?.id
);
case 'dingdan_bancai':
return this._rawData.dingdan_bancais.some(db =>
db.dingdan?.id === data.dingdan?.id &&
db.chanpin?.id === data.chanpin?.id &&
db.zujian?.id === data.zujian?.id &&
db.bancai?.id === data.bancai?.id
);
case 'user':
return this._rawData.users.some(u => u.name === data.name);
default:
return false;
}
}
async addEntity(entity, data) {
// 检查重复数据
if (this.checkDuplicate(entity, data)) {
const errorMsg = `${this.entiyeText[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);
}
// 动态获取数据的方法
getEntity(entityType) {
const key = entityType.endsWith('s') ? entityType : `${entityType}s`;
return this.data[key] || [];
}
// 保留原有的快捷获取方法
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();
}
/**
* 初始化数据库连接
*/
async init() {
return new Promise((resolve, reject) => {
if (this.db) {
resolve(this.db);
return;
}
const request = indexedDB.open(this.dbName, this.version);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 确保存储元数据的集合存在
if (!db.objectStoreNames.contains('metadata')) {
db.createObjectStore('metadata', { keyPath: 'key' });
}
};
request.onsuccess = (event) => {
this.db = event.target.result;
resolve(this.db);
};
request.onerror = (event) => {
console.error('IndexedDB初始化失败:', event.target.error);
reject(event.target.error);
};
});
}
/**
* 确保对象存储空间(集合)存在
*/
async ensureCollection(collectionName) {
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) => {
const newVersion = db.version + 1;
const request = indexedDB.open(this.dbName, newVersion);
request.onupgradeneeded = (event) => {
const newDb = event.target.result;
// 创建新的对象存储空间,假设ID字段名为'id'
if (!newDb.objectStoreNames.contains(collectionName)) {
newDb.createObjectStore(collectionName, { keyPath: 'id' });
}
};
request.onsuccess = (event) => {
this.db = event.target.result;
this.collections.add(collectionName);
resolve(true);
};
request.onerror = (event) => {
console.error(`创建集合${collectionName}失败:`, event.target.error);
reject(event.target.error);
};
});
}
/**
* 执行数据库事务
*/
async transaction(collectionName, mode = 'readonly', callback) {
await this.ensureCollection(collectionName);
const db = await this.init();
return new Promise((resolve, reject) => {
try {
const transaction = db.transaction([collectionName], mode);
const store = transaction.objectStore(collectionName);
const result = callback(store);
transaction.oncomplete = () => resolve(result);
transaction.onerror = (event) => reject(event.target.error);
} catch (error) {
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) => {
entities.forEach(entity => store.put(entity));
return entities.length;
});
}
/**
* 获取单个实体
*/
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 }; ----------------------------要和实际数据类型解耦,只要按命名规范命名的实体类型都可以用