/* 文件路径: 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) {
// 获取 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 };------------------------优化压缩
最新发布