/* 文件路径: webapp/app.js */
// 应用入口文件
App({
  globalData: {
    userInfo: null,
    isLoggedIn: false,
    dataManager: null,
    baseUrl: 'http://192.168.1.4:8080'
  },
  onLaunch: function() {
    console.log('小程序启动');
    // 初始化数据管理器
    this.initDataManager();
    // 检查登录状态
    const loginInfo = wx.getStorageSync('loginInfo');
    if (loginInfo && loginInfo.isLoggedIn) {
      console.log('检测到已登录状态,直接登录');
      this.globalData.isLoggedIn = true;
      this.globalData.userInfo = {
        name: loginInfo.name,
        role: loginInfo.role,
        userId: loginInfo.userId
      };
      // 加载初始数据
      this.globalData.dataManager.fetchAll();
    } else {
      console.log('未检测到登录状态,准备检查微信登录');
      // 未登录,使用延时函数确保在页面渲染后再显示登录提示
      this.globalData.needLogin = true;
      
      // 延迟500ms执行,确保页面已经渲染
      console.log('设置登录检查延时');
      setTimeout(() => {
        console.log('执行延时回调');
        if (typeof this.checkWechatLogin === 'function') {
          console.log('checkWechatLogin方法存在');
          this.checkWechatLogin();
        } else {
          console.error('checkWechatLogin方法不存在');
          wx.redirectTo({ url: '/pages/login/login' });
        }
      }, 500);
    }
  },
  // 检查微信登录
  checkWechatLogin: function() {
    console.log('准备显示登录提示对话框');
    try {
      wx.showModal({
        title: '登录提示',
        content: '是否使用微信账号登录?',
        confirmText: '微信登录',
        cancelText: '普通登录',
        success: (res) => {
          console.log('对话框选择结果:', res);
          if (res.confirm) {
            console.log('用户选择使用微信登录');
            this.doWechatLogin();
          } else {
            console.log('用户选择普通登录');
            wx.redirectTo({ url: '/pages/login/login' });
          }
        },
        fail: (err) => {
          console.error('显示对话框失败:', err);
          // 如果对话框显示失败,直接跳转到普通登录页面
          wx.redirectTo({ url: '/pages/login/login' });
        }
      });
    } catch (error) {
      console.error('调用showModal异常:', error);
      wx.redirectTo({ url: '/pages/login/login' });
    }
  },
  // 执行微信登录
  doWechatLogin: function() {
    wx.showLoading({ title: '登录中...' });
    
    // 获取微信用户信息
    wx.login({
      success: (res) => {
        if (res.code) {
          console.log('获取微信code成功:', res.code);
          // 发送code到后端换取用户信息
          this.getUserInfoByWechatCode(res.code);
        } else {
          console.error('微信登录失败:', res);
          wx.hideLoading();
          wx.showToast({
            title: '微信登录失败',
            icon: 'none'
          });
          setTimeout(() => {
            wx.redirectTo({ url: '/pages/login/login' });
          }, 1500);
        }
      },
      fail: (err) => {
        console.error('微信登录失败:', err);
        wx.hideLoading();
        wx.showToast({
          title: '微信登录失败',
          icon: 'none'
        });
        setTimeout(() => {
          wx.redirectTo({ url: '/pages/login/login' });
        }, 1500);
      }
    });
  },
  // 通过微信code获取用户信息
  getUserInfoByWechatCode: function(code) {
    wx.request({
      url: this.globalData.baseUrl + '/users/wechat-login',
      method: 'POST',
      data: { code: code },
      success: (res) => {
        wx.hideLoading();
        
        if (res.data.status === 200 && res.data.data) {
          // 用户已绑定,直接登录
          console.log('微信用户已绑定,直接登录:', res.data);
          this.login(res.data.data);
          wx.switchTab({ url: '/pages/index/index' });
        } else if (res.data.status === 201) {
          // 用户未绑定,跳转到申请页面
          console.log('微信用户未绑定,跳转到申请页面');
          wx.redirectTo({ 
            url: '/pages/register/register?openid=' + res.data.data.openid 
          });
        } else {
          // 登录失败
          console.error('微信登录失败:', res.data);
          wx.showToast({
            title: res.data.text || '微信登录失败',
            icon: 'none'
          });
          setTimeout(() => {
            wx.redirectTo({ url: '/pages/login/login' });
          }, 1500);
        }
      },
      fail: (err) => {
        console.error('微信登录请求失败:', err);
        wx.hideLoading();
        wx.showToast({
          title: '网络请求失败',
          icon: 'none'
        });
        setTimeout(() => {
          wx.redirectTo({ url: '/pages/login/login' });
        }, 1500);
      }
    });
  },
  initDataManager: function() {
    // 导入新的MiniProgramDataManager类
    const MiniProgramDataManager = require('./data/MiniProgramDataManager');
    
    // 实例化数据管理器,并传递配置选项
    this.globalData.dataManager = new MiniProgramDataManager(this.globalData.baseUrl);
  },
  // 全局登录方法
  login: function(userInfo) {
    console.log('保存用户信息:', userInfo);
    wx.setStorageSync('loginInfo', {
      isLoggedIn: true,
      name: userInfo.name,
      role: userInfo.role,
      userId: userInfo.id  // 使用id字段作为userId
    });
    this.globalData.isLoggedIn = true;
    this.globalData.userInfo = userInfo;
    // 重置登录标志,避免循环提示登录
    this.globalData.needLogin = false;
    // 直接加载数据,而不是重新初始化数据管理器
  // 在App初始化时调用
this.globalData.dataManager.initialize();
// 在需要手动刷新时调用
this.globalData.dataManager.syncData();
  },
  // 全局登出方法
  logout: function() {
    wx.removeStorageSync('loginInfo');
    this.globalData.isLoggedIn = false;
    this.globalData.userInfo = null;
    wx.redirectTo({ url: '/pages/login/login' });
  }
});    
================================================================================
/* 文件路径: webapp/app.json */
{
  "pages": [
    "pages/index/index",
    "pages/login/login",
    "pages/bancai/bancai",
    "pages/dingdan/dingdan",
    "pages/guanli/guanli",
    "pages/register/register"
  ],
  "window": {
    "pageOrientation": "landscape",
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#2c3e50",
    "navigationBarTitleText": "峤丞板材库存管理系统",
    "navigationBarTextStyle": "white"
  },
  "tabBar": {
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "images/icon_home.png",
        "selectedIconPath": "images/icon_home_selected.png"
      },
      {
        "pagePath": "pages/bancai/bancai",
        "text": "库存管理",
        "iconPath": "images/icon_bancai.png",
        "selectedIconPath": "images/icon_bancai_selected.png"
      },
      {
        "pagePath": "pages/dingdan/dingdan",
        "text": "订单录入",
        "iconPath": "images/icon_tianjia.png",
        "selectedIconPath": "images/icon_tianjia_selected.png"
      },
      {
        "pagePath": "pages/guanli/guanli",
        "text": "人员管理",
        "iconPath": "images/icon_guanli.png",
        "selectedIconPath": "images/icon_guanli_selected.png"
      }
    ],
    "color": "#333",
    "selectedColor": "#3498db",
    "backgroundColor": "#ffffff"
  },
  "sitemapLocation": "sitemap.json"
}
================================================================================
/* 文件路径: webapp/data/MiniProgramDataManager.js */
/**
 * 微信小程序数据管理器
 * 基于DataManager.js的逻辑,但适配微信小程序环境
 */
// 解析数据引用关系的辅助函数
/**
 * 解析数据中的引用关系
 * 
 * 该函数用于处理嵌套的数据结构,将数据中的引用关系解析为实际的对象引用。
 * 它会遍历数据中的所有实体,查找属性名中包含的数字(如"item1"),
 * 并尝试将这些属性值替换为对应类型数据中的实际对象引用。
 * 
 * @param {Object} data - 包含嵌套引用的原始数据对象
 * @returns {Object} 处理后的数据对象,其中引用已被解析为实际对象
 */
function resolveDataReferences(data) {
  const keys = Object.keys(data);
  for (const key of keys) {
      const entities = data[key];
      for (const entity of entities) {
          for (const attribute in entity) {
              if (entity.hasOwnProperty(attribute)) {
                  // 修复:统一使用复数形式查找引用类型
                  let refType = attribute.replace(/\d/g, '');
                  // 尝试直接查找复数形式
                  if (!data[refType] && data[`${refType}s`]) {
                      refType = `${refType}s`;
                  }
                  if (Array.isArray(entity[attribute])) {
                      entity[attribute] = entity[attribute].map(item =>
                          data[refType]?.find(updateItem => updateItem.id === item.id) || item
                      );
                  } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) {
                      entity[attribute] = data[refType]?.find(updateItem => updateItem.id === entity[attribute].id) || entity[attribute];
                  }
              }
          }
      }
  }
  return data;
}
// 解析单个实体的数据引用
/**
 * 解析数据引用关系
 * 
 * 该函数用于处理实体对象与数据源之间的引用关系,自动匹配并更新实体中的引用字段。
 * 
 * @param {Object} entity - 需要处理的实体对象
 * @param {Object} data - 包含引用数据的数据源对象
 * @returns {Object} 处理后的实体对象
 * 
 * 功能说明:
 * 1. 遍历实体对象的每个属性
 * 2. 如果属性值是数组,则尝试在数据源中查找匹配项更新数组元素
 * 3. 如果属性值是对象,则尝试在数据源中查找匹配项更新该对象
 * 4. 自动处理单复数形式的数据源键名
 */
function resolveDataReference(entity, data) {
  for (const attribute in entity) {
      if (entity.hasOwnProperty(attribute)) {
          // 修复:统一使用复数形式查找引用类型
          let refType = attribute.replace(/\d/g, '');
          // 尝试直接查找复数形式
          if (!data[refType] && data[`${refType}s`]) {
              refType = `${refType}s`;
          }
          if (Array.isArray(entity[attribute])) {
              entity[attribute] = entity[attribute].map(item =>
                  data[refType]?.find(updateItem => updateItem.id === item.id) || item
              );
          } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) {
              entity[attribute] = data[refType]?.find(updateItem => updateItem.id === entity[attribute].id) || entity[attribute];
          }
      }
  }
  return entity;
}
class LazyLoader {
  constructor(dataManager) {
    this.dataManager = dataManager;
    this.resolvedCache = new Map(); // 缓存已解析的实体
  }
  /**
   * 创建实体代理
   * @param {Object} entity 实体对象
   * @param {string} entityType 实体类型
   * @returns {Proxy} 返回代理后的实体
   */
  createProxy(entity, entityType) {
    const handler = {
      get: (target, prop) => {
        // 1. 处理特殊属性直接返回
        if (prop.startsWith('_') || typeof target[prop] === 'function') {
          return target[prop];
        }
        
        const value = target[prop];
        
        // 2. 处理数组属性
        if (Array.isArray(value)) {
          return value.map(item => 
            this.resolveReference(item, prop)
          );
        }
        
        // 3. 处理对象属性
        if (typeof value === 'object' && value !== null) {
          return this.resolveReference(value, prop);
        }
        
        // 4. 返回普通属性值
        return value;
      }
    };
    
    return new Proxy(entity, handler);
  }
  /**
   * 解析引用关系(核心逻辑)
   * 根据resolveDataReference函数逻辑实现
   */
  resolveReference(ref, propName) {
    // 1. 检查缓存
    const cacheKey = `${propName}_${ref.id}`;
    if (this.resolvedCache.has(cacheKey)) {
      return this.resolvedCache.get(cacheKey);
    }
    
    // 2. 确定引用类型(与resolveDataReference相同逻辑)
    let refType = propName.replace(/\d/g, '');
    const rawData = this.dataManager._rawData;
    
    // 处理复数形式(与resolveDataReference相同)
    if (!rawData[refType] && rawData[`${refType}s`]) {
      refType = `${refType}s`;
    }
    
    // 3. 查找引用实体(与resolveDataReference相同)
    const refEntities = rawData[refType];
    if (!refEntities) return ref;
    
    const resolved = refEntities.find(e => e.id === ref.id);
    if (!resolved) return ref;
    
    // 4. 创建代理并缓存
    const proxy = this.createProxy(resolved, refType);
    this.resolvedCache.set(cacheKey, proxy);
    return proxy;
  }
  
  /**
   * 清除缓存(数据更新时调用)
   */
  clearCache() {
    this.resolvedCache.clear();
  }
}
class MiniProgramDataManager {
  // 修复:合并重复的构造函数
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
    this.debug = true; // 调试模式开关
    this.requestCount = 0; // 请求计数器
    
    // 数据结构定义
    this._rawData = {
      bancais: [],
      dingdans: [],
      mupis: [],
      chanpins: [],
      kucuns: [],
      dingdan_bancais: [],
      chanpin_zujians: [],
      zujians: [],
      caizhis: [],
      dingdan_chanpins: [],
      users: [],
      jinhuos: [],
      _lastModified: null,
      _lastSync: null
    };
    
    // 初始化网络状态
    this.networkAvailable = false;
    this.checkNetwork().then(type => {
      this.networkAvailable = type !== 'none';
    });
    
    this.lazyLoader = new LazyLoader(this);
    
    // 仅从本地存储加载数据,不自动同步
    this.loadDataFromStorage();
    this.isSyncing = false;
    this.lastSync = null;
    this.callbacks = {
      all: [],
      bancais: [],
      dingdan: [],
      mupi: [],
      chanpin: [],
      kucun: [],
      chanpin_zujian: [],
      dingdan_bancai: [],
      zujian: [],
      caizhi: [],
      dingdan_chanpin: [],
      user: [],
      jinhuo: []
    };
    this.syncQueue = Promise.resolve();
    this.entiyeText = {
      bancai: '板材已存在',
      dingdan: '订单已存在',
      mupi: '木皮已存在',
      chanpin: '产品已存在',
      kucun: '已有库存记录',
      chanpin_zujian: '产品已有该组件',
      dingdan_bancai: '',
      zujian: '组件已定义过了',
      caizhi: '材质已定义过了',
      dingdan_chanpin: '订单下已有该产品',
      user: ''
    };
    this.syncInterval = 5 * 60 * 1000; // 5分钟
    this.storageKey = 'miniProgramData'; // 本地存储的键名
  }
  // 修改数据获取方法
  get data() {
    const handler = {
      get: (target, prop) => {
        // 处理特殊属性
        if (prop.startsWith('_')) {
          return target[prop];
        }
        
        // 处理数组类型的实体集合
        if (Array.isArray(target[prop])) {
          return target[prop].map(item => 
            this.lazyLoader.createProxy(item, prop.replace(/s$/, ''))
          );
        }
        
        return target[prop];
      },
      
      set: (target, prop, value) => {
        target[prop] = value;
        return true;
      }
    };
    
    return new Proxy(this._rawData, handler);
  }
  // 添加显式初始化方法
  async initialize() {
    // 启动自动同步
    this.startAutoSync();
    // 执行首次数据同步
    await this.syncData();
  }
  /**
   * 启动自动同步定时器
   * 每隔syncInterval毫秒检查并执行数据同步
   * 如果已有同步任务进行中则跳过
   */
  startAutoSync() {
    if (this.autoSyncTimer) clearInterval(this.autoSyncTimer);
    this.autoSyncTimer = setInterval(() => {
      if (!this.isSyncing) this.syncData();
    }, this.syncInterval);
  }
  /**
   * 停止自动同步
   */
  stopAutoSync() {
    clearInterval(this.autoSyncTimer);
  }
  /**
   * 检查网络状态
   */
  checkNetwork() {
    return new Promise((resolve) => {
      wx.getNetworkType({
        success: (res) => {
          resolve(res.networkType);
        },
        fail: () => {
          resolve('unknown');
        }
      });
    });
  }
  /**
   * 获取所有数据(全量或增量)
   * @async
   * @param {string} [since] - 增量获取的时间戳,不传则全量获取
   * @returns {Promise<boolean>} 是否获取成功
   * @description 
   * - 根据since参数决定全量或增量获取数据
   * - 增量获取时会合并新数据到现有数据
   * - 全量获取会直接替换现有数据
   * - 成功后会更新同步时间并保存到本地存储
   * - 失败时会触发错误回调,若无历史数据则抛出错误
   */
  async fetchAll(since) {
    try {
      console.log(since ? `增量获取数据(自${since})...` : '全量获取数据...');
      
      const params = since ? { since } : {};
      const result = await this.request('/app/all', 'GET', params);
      
      const resolvedData =result ;
      
      // 更新networkData
      Object.keys(this._rawData).forEach(key => {
        if (key.startsWith('_')) return;
        if (resolvedData[key]) {
          if (since) {
            // 增量更新: 合并新数据到现有数据
            resolvedData[key].forEach(newItem => {
              const index = this._rawData[key].findIndex(item => item.id === newItem.id);
              if (index >= 0) {
                this._rawData[key][index] = newItem;
              } else {
                this._rawData[key].push(newItem);
              }
            });
          } else {
            // 全量更新: 直接替换
            this._rawData[key] = resolvedData[key];
          }
        }
      });
      // 更新同步时间
      this.lastSync = new Date();
      this._rawData._lastSync = this.lastSync.toISOString();
      
      // 保存到本地存储
      this.saveDataToStorage();
      
      this.triggerCallbacks('refresh', 'all', this.data);
      return true;
    } catch (error) {
      console.error('Fetch error:', error);
      this.triggerCallbacks('fetch_error', 'all', { error });
      
      // 失败时尝试使用本地数据
      if (!this.lastSync) {
        throw new Error('初始化数据获取失败');
      }
      return false;
    }
  }
  /**
   * 微信小程序API请求封装
   */
  request(url, method = 'GET', data = null, retryCount = 3) {
    return new Promise((resolve, reject) => {
      const makeRequest = (attempt) => {
        const fullUrl = `${this.baseUrl}${url}`;
        
        if (this.debug) {
          console.log(`[请求] ${method} ${fullUrl}`, {
            attempt,
            data,
            timestamp: new Date().toISOString()
          });
        }
        wx.request({
          url: fullUrl,
          method,
          data,
          header: {
            'Content-Type': 'application/json'
          },
          success: (res) => {
            if (this.debug) {
              console.log(`[响应] ${fullUrl}`, {
                status: res.statusCode,
                data: res.data,
                headers: res.header
              });
            }
            // 修复:更灵活的响应格式处理
            if (!res.data) {
              const err = new Error('空响应数据');
              if (attempt < retryCount) {
                this.retryRequest(makeRequest, attempt, retryCount, err);
              } else {
                reject(err);
              }
              return;
            }
            // 修复:支持多种成功状态码和响应格式
            const isSuccess = res.statusCode >= 200 && res.statusCode < 300;
            const hasData = res.data && (res.data.data !== undefined || typeof res.data === 'object');
            
            if (isSuccess && hasData) {
              resolve(res.data.data || res.data);
            } else {
              const errMsg = res.data.message || res.data.text || 'API错误';
              const err = new Error(errMsg);
              if (attempt < retryCount) {
                this.retryRequest(makeRequest, attempt, retryCount, err);
              } else {
                reject(err);
              }
            }
          },
          fail: (err) => {
            if (this.debug) {
              console.error(`[失败] ${fullUrl}`, err);
            }
            
            const error = new Error(`网络请求失败: ${err.errMsg || '未知错误'}`);
            if (attempt < retryCount) {
              this.retryRequest(makeRequest, attempt, retryCount, error);
            } else {
              reject(error);
            }
          }
        });
      };
      
      makeRequest(1);
    });
  }
  retryRequest(makeRequest, attempt, retryCount, error) {
    const delay = 1000 * attempt;
    console.warn(`请求失败 (${attempt}/${retryCount}), ${delay}ms后重试:`, error.message);
    setTimeout(() => makeRequest(attempt + 1), delay);
  }
  /**
   * 注册回调函数
   */
  registerCallback(entity, callback) {
    if (!this.callbacks[entity]) {
      this.callbacks[entity] = [];
    }
    this.callbacks[entity].push(callback);
  }
  /**
   * 注销回调函数
   */
  unregisterCallback(entity, callback) {
    if (!this.callbacks[entity]) return;
    const index = this.callbacks[entity].indexOf(callback);
    if (index !== -1) {
      this.callbacks[entity].splice(index, 1);
    }
  }
  /**
   * 触发回调函数
   */
  triggerCallbacks(operation, entity, data) {
    this.callbacks.all.forEach(cb => cb(operation, entity, data));
    if (this.callbacks[entity]) {
      this.callbacks[entity].forEach(cb => cb(operation, data));
    }
  }
  /**
   * 检查重复实体
   */
  checkDuplicate(entity, data) {
    // 修复:确保引用已解析
    const resolvedData = resolveDataReference(data, this.data);
    
    switch (entity) {
      case 'bancai':
        return this.data.bancais.some(b =>
          b.houdu === resolvedData.houdu &&
          b.caizhi?.id === resolvedData.caizhi?.id &&
          b.mupi1?.id === resolvedData.mupi1?.id &&
          b.mupi2?.id === resolvedData.mupi2?.id
        );
      case 'caizhi':
        return this.data.caizhis.some(c => c.name === resolvedData.name);
      case 'mupi':
        return this.data.mupis.some(m => m.name === resolvedData.name && m.you === resolvedData.you);
      case 'chanpin':
        return this.data.chanpins.some(c => c.bianhao === resolvedData.bianhao);
      case 'zujian':
        return this.data.zujians.some(z => z.name === resolvedData.name);
      case 'dingdan':
        return this.data.dingdans.some(d => d.number === resolvedData.number);
      case 'chanpin_zujian':
        return this.data.chanpin_zujians.some(cz =>
          cz.chanpin?.id === resolvedData.chanpin?.id &&
          cz.zujian?.id === resolvedData.zujian?.id
        );
      case 'dingdan_chanpin':
        return this.data.dingdan_chanpins.some(dc =>
          dc.dingdan?.id === resolvedData.dingdan?.id &&
          dc.chanpin?.id === resolvedData.chanpin?.id
        );
      case 'dingdan_bancai':
        return this.data.dingdan_bancais.some(db =>
          db.dingdan?.id === resolvedData.dingdan?.id &&
          db.chanpin?.id === resolvedData.chanpin?.id &&
          db.zujian?.id === resolvedData.zujian?.id &&
          db.bancai?.id === resolvedData.bancai?.id
        );
      case 'user':
        return this.data.users.some(u => u.name === resolvedData.name);
      default:
        return false;
    }
  }
  /**
   * CRUD操作通用方法
   */
  async crudOperation(operation, entity, data) {
    try {
      // 使用微信请求API替代fetch
      const result = await this.request(`/app/${operation}/${entity}`, 'POST', data);
      
      this.updateLocalData(operation, entity, result || data);
      this.triggerCallbacks(operation, entity, result || data);
      return result;
    } catch (error) {
      console.error('CRUD error:', error);
      this.triggerCallbacks(`${operation}_error`, entity, { data, error: error.message });
      throw error;
    }
  }
  /**
   * 更新本地数据
   */
  /**
   * 更新本地数据
   * @param {string} operation - 操作类型: 'add' | 'update' | 'delete'
   * @param {string} entity - 实体名称
   * @param {Object} newData - 新数据对象(包含id字段)
   * @description 根据操作类型对本地数据进行增删改操作
   */
  updateLocalData(operation, entity, newData) {
    const key = `${entity}s`;
    const entities = this._rawData[key];
    // 确保新数据的引用已解析
    const resolvedData = resolveDataReference(newData, this._rawData);
    
    switch (operation) {
      case 'add':
        entities.push(resolvedData);
        break;
      case 'update':
        const index = entities.findIndex(item => item.id === resolvedData.id);
        if (index !== -1) {
          // 修复:使用对象展开操作符确保属性完整覆盖
          entities[index] = { ...entities[index], ...resolvedData };
        } else {
          entities.push(resolvedData);
        }
        break;
      case 'delete':
        const deleteIndex = entities.findIndex(item => item.id === resolvedData.id);
        if (deleteIndex !== -1) {
          entities.splice(deleteIndex, 1);
        }
        break;
    }
    
    // 更新最后修改时间
    this._rawData._lastModified = new Date().toISOString();
    this.lazyLoader.clearCache();
    
   
    // 保存修改后的数据到本地存储
    this.saveDataToStorage();
  }
  /**
   * 同步数据
   */
  /**
   * 同步数据方法
   * 该方法用于异步获取所有数据,并处理同步过程中的并发请求
   * 如果同步正在进行中,会将请求标记为待处理(pendingSync)
   * 同步完成后会自动处理待处理的请求
   * @async
   * @throws {Error} 当获取数据失败时会抛出错误并记录日志
   */
  async syncData() {
    if (this.isSyncing) {
      this.pendingSync = true;
      return;
    }
    this.isSyncing = true;
    
    try {
      // 1. 先加载本地数据
      this.loadDataFromStorage();
      
      // 2. 获取最后同步时间,用于增量更新
      const since = this._rawData._lastSync || null;
      
      // 3. 获取增量数据
      await this.fetchAll(since);
      
      // 4. 保存更新后的数据到本地存储
      this.saveDataToStorage();
      
      // 5. 触发数据更新回调
      this.triggerCallbacks('refresh', 'all', this.data);
    } catch (error) {
      console.error('Sync failed:', error);
      this.triggerCallbacks('sync_error', 'all', { error });
      
      // 失败时尝试使用本地数据
      if (!this._rawData._lastSync) {
        throw new Error('初始化数据同步失败');
      }
    } finally {
      this.isSyncing = false;
      if (this.pendingSync) {
        this.pendingSync = false;
        this.syncData();
      }
    }
  }
  /**
   * 从本地存储加载数据
   * 使用微信小程序的同步存储API获取之前保存的数据
   */
  loadDataFromStorage() {
    try {
      const storedData = wx.getStorageSync(this.storageKey);
      if (storedData) {
        // 修复:加载到_rawData而非data代理对象
        this._rawData = storedData;
      }
    } catch (error) {
      console.error('加载本地存储数据失败:', error);
      // 提供默认空数据
      this._rawData = {
        bancais: [],
        dingdans: [],
        mupis: [],
        chanpins: [],
        kucuns: [],
        dingdan_bancais: [],
        chanpin_zujians: [],
        zujians: [],
        caizhis: [],
        dingdan_chanpins: [],
        users: [],
        jinhuos: [],
        _lastModified: null,
        _lastSync: null
      };
    }
  }
  /**
   * 保存数据到本地存储
   * 使用微信小程序的同步存储API持久化当前数据
   */
  saveDataToStorage() {
    try {
      // 修复:保存_rawData而非localData
      wx.setStorageSync(this.storageKey, this._rawData);
    } catch (error) {
      console.error('保存数据到本地存储失败:', error);
      // 提示用户或执行降级策略
      wx.showToast({
        title: '数据保存失败,请稍后重试',
        icon: 'none'
      });
    }
  }
  /**
   * 添加实体
   */
  /**
   * 添加实体数据
   * @async
   * @param {string} entity - 实体类型
   * @param {Object} data - 要添加的实体数据
   * @returns {Promise} 返回CRUD操作结果
   * @throws {Error} 如果数据已存在则抛出错误
   */
  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 });
  }
  getBancaisForZujian(zujianId) {
    const dingdan_bancais = this.data.dingdan_bancais.filter(db => db.zujian?.id == zujianId);
    return dingdan_bancais.map(db => db.bancai).filter(Boolean);
  }
  /**
   * 获取板材的库存信息
   */
  getKucunForBancai(bancaiId) {
    return this.data.kucuns.find(k => k.bancai?.id == bancaiId);
  }
}
// 导出模块
module.exports = MiniProgramDataManager;    
================================================================================
/* 文件路径: webapp/data/ModalManager.js */
/**
 * 增强版微信小程序弹窗管理器
 * 支持嵌套模态框,统一管理不同类型的模态框
 */
class ModalManager {
  constructor() {
      // 弹窗配置默认值
      this.defaultConfig = {
          showClose: true,           // 是否显示关闭按钮
          maskClosable: true,        // 点击遮罩是否可关闭
          showCancel: true,          // 是否显示取消按钮
          cancelText: '取消',        // 取消按钮文本
          confirmText: '确定',       // 确认按钮文本
          confirmColor: '#3498db',   // 确认按钮颜色
          cancelColor: '#95a5a6',    // 取消按钮颜色
          width: '80%',              // 弹窗宽度
          zIndex: 1000,              // 弹窗层级
          animation: true,           // 是否启用动画
          customClass: '',           // 自定义样式类
          position: 'center',        // 弹窗位置:center, top, bottom
          duration: 300              // 动画持续时间(ms)
      };
      // 模态框栈 - 用于管理嵌套模态框
      this.modalStack = [];
      
      // 最大嵌套层级
      this.maxNestedLevel = 3;
      
      // 模态框计数器(用于生成唯一ID)
      this.modalCounter = 0;
      
      // 模态框类型注册表
      this.modalTypes = {
          // 订单页模态框
          'createOrder': { name: 'showCreateOrderModal', page: 'dingdan' },
          'addProduct': { name: 'showProductModal', page: 'dingdan', data: { isEditProduct: false } },
          'editProduct': { name: 'showProductModal', page: 'dingdan', data: { isEditProduct: true } },
          'addComponent': { name: 'showAddComponentModal', page: 'dingdan' },
          'addBancai': { name: 'showAddBancaiModal', page: 'dingdan' },
          
          // 板材页模态框
          'bancaiDetail': { name: 'showDetailModal', page: 'bancai' },
          'stockEdit': { name: 'showStockModal', page: 'bancai' },
          
          // 首页模态框
          'materialDetail': { name: 'showMaterialDetail', page: 'index' }
      };
  }
  /**
   * 注册新的模态框类型
   * @param {string} type - 模态框类型标识
   * @param {object} config - 模态框配置
   */
  registerModalType(type, config) {
      if (!type || !config.name || !config.page) {
          console.error('注册模态框类型失败:缺少必要参数');
          return false;
      }
      
      this.modalTypes[type] = { ...config };
      return true;
  }
  /**
   * 显示模态框
   * @param {string} type - 模态框类型
   * @param {object} data - 传递给模态框的数据
   * @param {object} options - 额外选项
   * @returns {Promise} - 返回Promise,resolve时传递模态框结果
   */
  showModal(type, data = {}, options = {}) {
      // 检查模态框类型是否已注册
      const modalType = this.modalTypes[type];
      if (!modalType) {
          return Promise.reject(new Error(`未注册的模态框类型: ${type}`));
      }
      
      // 检查嵌套层级
      if (this.modalStack.length >= this.maxNestedLevel) {
          return Promise.reject(new Error(`模态框嵌套层级超过限制(${this.maxNestedLevel})`));
      }
      
      // 获取当前页面实例
      const pages = getCurrentPages();
      const currentPage = pages[pages.length - 1];
      
      // 生成唯一ID
      const modalId = `modal_${++this.modalCounter}`;
      
      // 创建模态框数据
      const modalData = {
          visible: true,
          ...modalType.data,
          ...data,
          _modalId: modalId,
          _nestedLevel: this.modalStack.length + 1,
          _zIndex: this.defaultConfig.zIndex + (this.modalStack.length * 100)
      };
      
      return new Promise((resolve, reject) => {
          // 创建模态框对象
          const modal = {
              id: modalId,
              type,
              pageContext: currentPage,
              name: modalType.name,
              resolve,
              reject,
              data: modalData
          };
          
          // 添加到模态框栈
          this.modalStack.push(modal);
          
          // 设置页面数据,显示模态框
          const dataUpdate = {};
          dataUpdate[modalType.name] = modalData;
          currentPage.setData(dataUpdate);
      });
  }
  /**
   * 关闭最上层模态框
   * @param {any} result - 模态框结果
   * @returns {boolean} - 是否成功关闭
   */
  closeModal(result) {
      if (this.modalStack.length === 0) return false;
      
      // 获取最上层模态框
      const modal = this.modalStack.pop();
      const { pageContext, name, data } = modal;
      
      // 隐藏模态框
      const dataUpdate = {};
      dataUpdate[name] = { ...data, visible: false };
      pageContext.setData(dataUpdate);
      
      // 延迟移除,等待动画完成
      setTimeout(() => {
          const cleanUpdate = {};
          cleanUpdate[name] = null;
          pageContext.setData(cleanUpdate);
          
          // 解析Promise
          modal.resolve(result);
      }, this.defaultConfig.duration);
      
      return true;
  }
  /**
   * 关闭所有模态框
   */
  closeAllModals() {
      while (this.modalStack.length > 0) {
          this.closeModal();
      }
  }
  /**
   * 获取当前模态框栈信息
   * @returns {Array} - 模态框栈信息
   */
  getModalStackInfo() {
      return this.modalStack.map(modal => ({
          id: modal.id,
          type: modal.type,
          name: modal.name,
          level: modal.data._nestedLevel
      }));
  }
  // 以下是便捷方法,用于快速显示特定类型的模态框
  
  // 订单页模态框
  showCreateOrderModal(data, options) {
      return this.showModal('createOrder', data, options);
  }
  
  showAddProductModal(data, options) {
      return this.showModal('addProduct', data, options);
  }
  
  showEditProductModal(data, options) {
      return this.showModal('editProduct', data, options);
  }
  
  showAddComponentModal(data, options) {
      return this.showModal('addComponent', data, options);
  }
  
  showAddBancaiModal(data, options) {
      return this.showModal('addBancai', data, options);
  }
  
  // 板材页模态框
  showBancaiDetailModal(data, options) {
      return this.showModal('bancaiDetail', data, options);
  }
  
  showStockEditModal(data, options) {
      return this.showModal('stockEdit', data, options);
  }
  
  // 首页模态框
  showMaterialDetailModal(data, options) {
      return this.showModal('materialDetail', data, options);
  }
  
  // 显示提示框
  showToast(options) {
      return new Promise((resolve, reject) => {
          wx.showToast({
              title: options.title || '',
              icon: options.icon || 'none',
              image: options.image,
              duration: options.duration || 1500,
              mask: options.mask || false,
              success: (res) => {
                  if (options.success) options.success(res);
                  resolve(res);
              },
              fail: (err) => {
                  if (options.fail) options.fail(err);
                  reject(err);
              },
              complete: options.complete
          });
      });
  }
  // 显示加载提示
  showLoading(options = {}) {
          options.title = title;
      }
      return this.showActionSheet(options)
        .then(res => res.tapIndex)
        .catch(() => -1);
  }
  // 显示创建订单模态框
  showCreateOrderModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showCreateOrderModal',
          data
      });
  }
  // 显示添加产品模态框
  showAddProductModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showProductModal',
          data: {
              ...data,
              isEditProduct: false
          }
      });
  }
  // 显示编辑产品模态框
  showEditProductModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showProductModal',
          data: {
              ...data,
              isEditProduct: true
          }
      });
  }
  // 显示添加组件模态框
  showAddComponentModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showAddComponentModal',
          data
      });
  }
  // 显示添加板材模态框
  showAddBancaiModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showAddBancaiModal',
          data
      });
  }
  // 显示板材详情模态框
  showBancaiDetailModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showDetailModal',
          data
      });
  }
  // 显示库存编辑模态框
  showStockEditModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showStockModal',
          data
      });
  }
  // 显示板材详情弹窗(index.wxml中的)
  showMaterialDetailModal(pageContext, data) {
      return this.showCustomModal(pageContext, {
          name: 'showMaterialDetail',
          data
      });
  }
}
// 创建单例
const modalManager = new ModalManager();
// 导出模块
module.exports = modalManager;
================================================================================
/* 文件路径: webapp/images/bg-01.jpg */
����-----------------------------------------------------------------------------------把dingdan.wxml,index.wxml,bancai.wxml中的拟态框集合到单独的一个类中,所有的拟态框放入统一的WXML中并统一样式,其他界面调用ModalManager时只要选择哪一类的拟态框并传入数据就可以,并且实现嵌套拟态框