活动介绍
file-type

JavaScript时间戳转换方法详解

ZIP文件

下载需积分: 50 | 2KB | 更新于2024-12-05 | 91 浏览量 | 0 下载量 举报 收藏
download 立即下载
JavaScript是一种广泛用于网页开发的编程语言,它提供了一个非常方便的工具来处理日期和时间,即时间戳(timestamp)。时间戳是一个单调的数字,表示自1970年1月1日00:00:00 UTC(协调世界时)至当前时间的毫秒数。在JavaScript中,时间戳可以很容易地通过Date对象的几个方法进行转换。 在了解时间戳转换之前,我们需要先知道JavaScript中的Date对象,它是一个内置对象,提供了一些方法来处理日期和时间。Date对象可以用来获取当前的日期和时间,也可以用来创建特定的日期对象。 以下是一些与时间戳转换相关的JavaScript知识点: 1. 获取当前时间的时间戳: JavaScript中的Date对象提供了getTime方法来获取当前时间的时间戳。调用new Date().getTime()即可得到当前时间的毫秒时间戳。 ```javascript let currentTimeStamp = new Date().getTime(); console.log(currentTimeStamp); // 输出当前时间的时间戳 ``` 2. 从时间戳创建Date对象: 使用Date对象的构造函数可以直接将时间戳转换为Date对象,从而获取对应的日期和时间。 ```javascript let dateFromTimestamp = new Date(timeStamp); console.log(dateFromTimestamp); // 输出从时间戳创建的日期对象 ``` 3. 时间戳与日期格式之间的转换: JavaScript本身不提供直接将时间戳转换为日期字符串的方法,通常需要手动进行转换。可以创建一个Date对象,然后使用该对象提供的toString方法或者各种日期格式化函数来转换为常见的日期格式字符串。 ```javascript let timeStamp = new Date().getTime(); let dateObject = new Date(timeStamp); let formattedDate = dateObject.toLocaleString(); // 将时间转换为本地日期格式字符串 let formattedDateUS = dateObject.toLocaleDateString('en-US'); // 将时间转换为美国日期格式字符串 console.log(formattedDate); // 输出本地日期格式 console.log(formattedDateUS); // 输出美国日期格式 ``` 4. 计算时间差: 通过时间戳,我们可以方便地计算出两个时间点之间的差异。例如,如果有一个开始时间戳和一个结束时间戳,可以通过简单的减法得到时间差。 ```javascript let startTimeStamp = new Date('2023-01-01').getTime(); let endTimeStamp = new Date('2023-01-02').getTime(); let timeDifference = endTimeStamp - startTimeStamp; console.log(timeDifference); // 输出两个时间点之间的时间差,单位为毫秒 ``` 5. 时间戳的精度问题: 在处理时间戳时需要注意其精度问题。由于JavaScript使用的是双精度浮点数来表示时间戳,因此可能会出现精度丢失的情况,尤其是在处理非常大的时间戳或者非常小的时间戳差异时。 ```javascript let veryLargeTimeStamp = new Date('3000-01-01').getTime(); let verySmallDifference = 0.1 + 0.2; console.log(veryLargeTimeStamp); // 处理非常大的时间戳时可能不会有精确的表示 console.log(verySmallDifference === 0.3); // 浮点数精度问题导致的结果可能是false ``` 总结以上,JavaScript中的时间戳转换是处理日期和时间的基础,通过结合Date对象和时间戳的各种方法,开发者可以轻松地实现时间的获取、格式化、计算等操作。在实际应用中,需要注意时间戳的精度问题和格式化时的地区差异,确保时间处理的准确性和适用性。

相关推荐

filetype

// 首页逻辑文件 const app = getApp(); Page({ data: { // 统计数据 totalOrders: 0, totalProducts: 0, totalMaterials: 0, totalStock: 0, // 搜索条件 orderSearch: '', productSearch: '', materialSearch: '', woodSearch: '', thicknessSearch: '', minStock: '', maxStock: '', // 结果数据 loading: true, results: [], filteredResults: [], resultCount: 0, lastUpdate: '--:--:--', // 排序 sortField: null, sortDirection: 'asc', // 分页 currentPage: 1, pageSize: 10, totalPages: 1, // 板材详情 showMaterialDetail: false, materialDetail: null }, onLoad: function() { console.log('index页面加载'); // 获取全局数据管理器 if (app.globalData && app.globalData.dataManager) { this.dataManager = app.globalData.dataManager; // 注册数据更新回调 this.dataManager.registerCallback('all', () => { console.log('收到数据更新回调'); this.updatePageData(); }); // 初始化页面数据 this.updatePageData(); // 尝试重新初始化 if (app.initDataManager) { app.initDataManager(); } } }, onShow: function() { console.log('首页显示'); // 检查是否需要登录 if (app.globalData && app.globalData.needLogin) { console.log('检测到需要登录'); // 延迟500ms确保页面渲染完成 setTimeout(() => { app.checkWechatLogin(); }, 500); } }, // 更新页面数据 updatePageData: function() { if (!this.dataManager || !this.dataManager.data) return; this.setData({ loading: true }); // 更新统计数据 this.updateStats(); // 更新表格数据 this.updateTable(); // 更新最后更新时间 const now = new Date(); const lastUpdate = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`; this.setData({ lastUpdate }); }, // 更新统计数据 updateStats: function() { const data = this.dataManager.data; const totalOrders = data.dingdans?.length || 0; const totalProducts = data.chanpins?.length || 0; const totalMaterials = data.bancais?.length || 0; const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0; this.setData({ totalOrders, totalProducts, totalMaterials, totalStock }); }, // 更新表格数据 updateTable: function() { console.log('开始更新表格数据'); const data = this.dataManager.data; let results = []; // 处理订单产品数据 if (data.dingdan_chanpins) { console.log('处理dingdan_chanpins数据,数量:', data.dingdan_chanpins.length); // 深度处理嵌套数据 data.dingdan_chanpins.forEach(dc => { const order = data.dingdans.find(d => d.id === dc.dingdan?.id); const product = data.chanpins.find(c => c.id === dc.chanpin?.id); if (order && product) { results.push({ orderNumber: order.number, productInfo: `${product.bianhao} (${dc.shuliang}件)`, productQuantity: dc.shuliang, // 添加更多必要字段... _rawData: dc // 保留原始数据用于调试 }); } }); data.dingdan_chanpins.forEach(dc => { if (!dc.dingdan || !dc.chanpin) return; if (dc.chanpin.chanpin_zujians) { dc.chanpin.chanpin_zujians.forEach(cz => { if (!cz.zujian || !cz.bancai) return; const bancai = cz.bancai; const materialInfo = this.getMaterialInfo(bancai); // 查找该板材的库存 const kucun = data.kucuns?.find(k => k.bancai?.id === bancai.id); const stockQuantity = kucun ? kucun.shuliang : 0; results.push({ id: `${dc.dingdan.id}-${dc.chanpin.id}-${cz.zujian.id}-${bancai.id}`, orderNumber: dc.dingdan.number, productInfo: dc.chanpin.bianhao, productQuantity: dc.shuliang, component: cz.zujian.name, material: materialInfo, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang / cz.one_howmany, stockQuantity: stockQuantity, raw: { orderNumber: dc.dingdan.number, productQuantity: dc.shuliang, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang / cz.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, bancaiId: bancai.id }); }); } }); } // 处理订单板材数据 (Dingdan_bancai) if (data.dingdan_bancais) { data.dingdan_bancais.forEach(db => { if (!db.dingdan || !db.bancai) return; const bancai = db.bancai; const materialInfo = this.getMaterialInfo(bancai); // 查找该板材的库存 const kucun = data.kucuns?.find(k => k.bancai?.id === bancai.id); const stockQuantity = kucun ? kucun.shuliang : 0; // 确定产品信息 let productInfo = '独立订购'; if (db.chanpin) { productInfo = db.chanpin.bianhao; } // 确定组件信息 let component = '独立板材'; if (db.zujian) { component = db.zujian.name; } else if (db.chanpin) { component = '产品板材'; } results.push({ id: `${db.dingdan.id}-direct-${bancai.id}`, orderNumber: db.dingdan.number, productInfo: productInfo, productQuantity: db.shuliang, // 直接订购的板材数量 component: component, material: materialInfo, materialPerComponent: 1, // 直接订购时,每件用量为1 materialOrderQuantity: db.shuliang, stockQuantity: stockQuantity, raw: { orderNumber: db.dingdan.number, productQuantity: db.shuliang, materialPerComponent: 1, materialOrderQuantity: db.shuliang, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, bancaiId: bancai.id }); }); } // 应用排序 if (this.data.sortField !== null) { results = this.sortResults(results, this.data.sortField, this.data.sortDirection); } // 更新页面数据 this.setData({ results: results, filteredResults: results, resultCount: results.length, totalPages: Math.ceil(results.length / this.data.pageSize), currentPage: 1, loading: false }, () => { // 更新当前页数据 this.updateCurrentPageData(); }); // 应用筛选 this.applyFilters(); }, // 获取板材详细信息函数 getMaterialInfo: function(bancai) { if (!bancai) return '未知板材'; // 获取材质名称 const caizhiName = bancai.caizhi?.name || '未知材质'; // 获取木皮信息 const formatMupi = (mupi) => { if (!mupi) return null; return `${mupi.name}${mupi.you ? '(油)' : ''}`; }; const mupi1Str = formatMupi(bancai.mupi1) || '无'; const mupi2Str = formatMupi(bancai.mupi2) || '无'; const mupiInfo = mupi1Str + (mupi2Str !== '无' ? `/${mupi2Str}` : ''); // 厚度转换为字符串 const thickness = bancai.houdu ? `${bancai.houdu.toFixed(1)}mm` : '未知厚度'; return `${caizhiName}-${mupiInfo} (${thickness})`; }, // 搜索输入处理函数 onOrderSearchInput: function(e) { this.setData({ orderSearch: e.detail.value }); this.applyFilters(); }, onProductSearchInput: function(e) { this.setData({ productSearch: e.detail.value }); this.applyFilters(); }, onMaterialSearchInput: function(e) { this.setData({ materialSearch: e.detail.value }); this.applyFilters(); }, onWoodSearchInput: function(e) { this.setData({ woodSearch: e.detail.value }); // 支持中文和数字搜索 const searchTerm = e.detail.value.toLowerCase(); const searchKeywords = { '有油': '有油', '无油': '无油', 'you': '有油', 'wu': '无油', '1': '有油', '0': '无油', '(油)': '(油)' }; this.setData({ woodSearch: searchKeywords[searchTerm] || searchTerm }); this.applyFilters(); }, onThicknessSearchInput: function(e) { this.setData({ thicknessSearch: e.detail.value }); this.applyFilters(); }, onMinStockInput: function(e) { this.setData({ minStock: e.detail.value }); }, onMaxStockInput: function(e) { this.setData({ maxStock: e.detail.value }); }, // 库存范围搜索 onStockSearch: function() { this.applyFilters(); }, // 重置筛选 resetFilters: function() { this.setData({ orderSearch: '', productSearch: '', materialSearch: '', woodSearch: '', thicknessSearch: '', minStock: '', maxStock: '', sortField: null, sortDirection: 'asc', currentPage: 1 }); this.updateTable(); }, // 应用筛选条件 applyFilters: function() { let filtered = [...this.data.results]; // 应用订单号筛选 if (this.data.orderSearch) { const search = this.data.orderSearch.toLowerCase(); filtered = filtered.filter(item => item.orderNumber.toLowerCase().includes(search) ); } // 应用产品筛选 if (this.data.productSearch) { const search = this.data.productSearch.toLowerCase(); filtered = filtered.filter(item => item.productInfo.toLowerCase().includes(search) ); } // 应用板材筛选 if (this.data.materialSearch) { const search = this.data.materialSearch.toLowerCase(); filtered = filtered.filter(item => item.material.toLowerCase().includes(search) ); } // 应用木皮筛选 if (this.data.woodSearch) { const search = this.data.woodSearch.toLowerCase(); filtered = filtered.filter(item => item.material.toLowerCase().includes(search) ); } // 应用厚度筛选 if (this.data.thicknessSearch) { const thickness = parseFloat(this.data.thicknessSearch); if (!isNaN(thickness)) { filtered = filtered.filter(item => { // 提取厚度数字部分进行匹配 const thicknessMatch = item.material.match(/(\d+\.?\d*)/); if (thicknessMatch) { const thicknessValue = parseFloat(thicknessMatch[1]); // 允许小数点误差 return Math.abs(thicknessValue - thickness) < 0.5; } return false; }); } } // 应用库存范围筛选 if (this.data.minStock !== '' || this.data.maxStock !== '') { const min = this.data.minStock !== '' ? parseInt(this.data.minStock) : Number.MIN_SAFE_INTEGER; const max = this.data.maxStock !== '' ? parseInt(this.data.maxStock) : Number.MAX_SAFE_INTEGER; filtered = filtered.filter(item => item.stockQuantity >= min && item.stockQuantity <= max ); } // 更新筛选结果 this.setData({ filteredResults: filtered, resultCount: filtered.length, totalPages: Math.ceil(filtered.length / this.data.pageSize), currentPage: 1 }); }, // 排序处理 sortBy: function(e) { const field = e.currentTarget.dataset.field; // 如果点击的是当前排序字段,则切换排序方向 if (field === this.data.sortField) { this.setData({ sortDirection: this.data.sortDirection === 'asc' ? 'desc' : 'asc' }); } else { // 否则,设置新的排序字段,默认升序 this.setData({ sortField: field, sortDirection: 'asc' }); } // 应用排序 const sorted = this.sortResults(this.data.filteredResults, this.data.sortField, this.data.sortDirection); this.setData({ filteredResults: sorted }, () => { // 更新当前页数据 this.updateCurrentPageData(); }); }, // 排序结果 sortResults: function(results, field, direction) { if (!field) return results; return [...results].sort((a, b) => { let valueA, valueB; // 根据排序字段获取值 switch (field) { case 'dingdan.number': valueA = a.orderNumber; valueB = b.orderNumber; break; case 'chanpin.bianhao': valueA = a.productInfo; valueB = b.productInfo; break; case 'dingdan_chanpin.shuliang': valueA = a.productQuantity; valueB = b.productQuantity; break; case 'zujian.name': valueA = a.component; valueB = b.component; break; case 'bancai.id': // 特殊处理:按厚度排序 return this.sortByThickness(a, b, direction); case 'chanpin_zujian.one_howmany': valueA = a.materialPerComponent; valueB = b.materialPerComponent; break; case 'orderUsage': valueA = a.materialOrderQuantity; valueB = b.materialOrderQuantity; break; case 'kucun.shuliang': valueA = a.stockQuantity; valueB = b.stockQuantity; break; default: valueA = a.orderNumber; valueB = b.orderNumber; } // 数值比较 if (['productQuantity', 'materialPerComponent', 'materialOrderQuantity', 'stockQuantity'].includes(field)) { return this.sortNumeric(valueA, valueB, direction); } // 字符串比较 return this.sortString(valueA, valueB, direction); }); }, // 按厚度排序(特殊处理) sortByThickness: function(a, b, direction) { return this.sortNumeric(a.raw.thickness, b.raw.thickness, direction); }, // 数值排序辅助函数 sortNumeric: function(aVal, bVal, direction) { const aNum = parseFloat(aVal) || 0; const bNum = parseFloat(bVal) || 0; return direction === 'asc' ? aNum - bNum : bNum - aNum; }, // 字符串排序辅助函数 sortString: function(aVal, bVal, direction) { const aStr = String(aVal).toLowerCase(); const bStr = String(bVal).toLowerCase(); if (aStr < bStr) return direction === 'asc' ? -1 : 1; if (aStr > bStr) return direction === 'asc' ? 1 : -1; return 0; }, // 分页处理 prevPage: function() { if (this.data.currentPage > 1) { this.setData({ currentPage: this.data.currentPage - 1 }, () => { this.updateCurrentPageData(); }); } }, nextPage: function() { if (this.data.currentPage < this.data.totalPages) { this.setData({ currentPage: this.data.currentPage + 1 }, () => { this.updateCurrentPageData(); }); } }, // 更新当前页数据 updateCurrentPageData: function() { const startIndex = (this.data.currentPage - 1) * this.data.pageSize; const endIndex = startIndex + this.data.pageSize; const currentPageData = this.data.filteredResults.slice(startIndex, endIndex).map((item, index) => { // 为每个项目添加一个唯一的ID,使用索引确保唯一性 return { ...item, uniqueId: `item-${startIndex + index}` }; }); this.setData({ currentPageData }); }, // 显示板材详情 showMaterialDetail: function(e) { const materialId = e.currentTarget.dataset.id; // 查找板材详情 const bancai = this.dataManager.data.bancais.find(b => b.id === materialId); if (!bancai) { wx.showToast({ title: '未找到板材信息', icon: 'none' }); return; } // 获取库存信息 const kucun = this.dataManager.data.kucuns ? this.dataManager.data.kucuns.find(k => k.bancai?.id === materialId) : null; // 格式化木皮信息 const formatMupiDetail = (mupi) => { if (!mupi) return '无'; return `${mupi.name}${mupi.you ? '(油)' : ''}`; }; // 获取相关订单 const relatedOrders = this.getRelatedOrders(bancai); // 设置详情数据 this.setData({ materialDetail: { id: bancai.id, material: bancai.caizhi?.name || '未知', thickness: bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知', stock: kucun ? kucun.shuliang : 0, mupi1: formatMupiDetail(bancai.mupi1), mupi2: formatMupiDetail(bancai.mupi2), relatedOrders: relatedOrders.map(order => ({ number: order.number, xiadan: order.xiadan ? this.formatDate(order.xiadan) : '-', jiaohuo: order.jiaohuo ? this.formatDate(order.jiaohuo) : '-' })) }, showMaterialDetail: true }); }, // 关闭板材详情 closeMaterialDetail: function() { this.setData({ showMaterialDetail: false }); }, // 获取板材相关订单 getRelatedOrders: function(bancai) { const data = this.dataManager.data; const orderMap = new Map(); // 使用Map进行订单去重 // 查找直接订单组件关联 const dingdan_bancais = data.dingdan_bancais || []; dingdan_bancais.forEach(db => { if (db.bancai?.id === bancai.id && db.dingdan) { orderMap.set(db.dingdan.id, db.dingdan); } }); // 查找产品组件关联 const chanpin_zujians = data.chanpin_zujians || []; chanpin_zujians.forEach(cz => { if (cz.bancai?.id === bancai.id) { const dingdan_chanpins = data.dingdan_chanpins || []; dingdan_chanpins.forEach(dc => { if (dc.chanpin?.id === cz.chanpin?.id && dc.dingdan) { orderMap.set(dc.dingdan.id, dc.dingdan); } }); } }); // 转换为数组并排序(按下单日期降序) return Array.from(orderMap.values()).sort((a, b) => { const dateA = a.xiadan ? new Date(a.xiadan) : new Date(0); const dateB = b.xiadan ? new Date(b.xiadan) : new Date(0); return dateB - dateA; // 最近的订单在前 }); }, // 格式化日期 formatDate: function(dateString) { if (!dateString) return ''; const date = new Date(dateString); return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; } });@startuml !theme plain hide circle skinparam linetype ortho entity Bancai { + id: Integer <<PK>> -- houdu: Double -- + caizhi: Caizhi [1] + mupi1: Mupi [1] + mupi2: Mupi [1] + kucun: Kucun [1] } entity Caizhi { + id: Integer <<PK>> -- name: String -- *- bancai: Bancai [*] } entity Mupi { + id: Integer <<PK>> -- name: String you: Boolean -- *- bancai_mupi1: Bancai [*] *- bancai_mupi2: Bancai [*] } entity Kucun { + id: Integer <<PK>> -- shuliang: Integer -- + bancai: Bancai [1] } entity Chanpin { + id: Integer <<PK>> -- bianhao: String -- *- chanpin_zujian: Chanpin_zujian [*] *- dingdan_chanpin: Dingdan_chanpin [*] } entity Zujian { + id: Integer <<PK>> -- name: String -- *- chanpin_zujian: Chanpin_zujian [*] } entity Chanpin_zujian { + id: Integer <<PK>> -- one_howmany: Double zujianshu: Double -- + chanpin: Chanpin [1] + zujian: Zujian [1] + bancai: Bancai [1] } entity Dingdan { + id: Integer <<PK>> -- number: String xiadan: Date jiaohuo: Date -- *- dingdan_chanpin: Dingdan_chanpin [*] *- dingdan_bancai: Dingdan_bancai [*] } entity Dingdan_chanpin { + id: Integer <<PK>> -- shuliang: Integer -- + dingdan: Dingdan [1] + chanpin: Chanpin [1] } entity Dingdan_bancai { + id: Integer <<PK>> -- shuliang: Integer -- + dingdan: Dingdan [1] + chanpin: Chanpin [1] + zujian: Zujian [1] + bancai: Bancai [1] *- jinhuo: Jinhuo [*] } entity Jinhuo { + id: Integer <<PK>> -- shuliang: Integer date: Date text: String theTypeOfOperation: Integer -- + dingdan_bancai: Dingdan_bancai [1] + user: User [1] } entity User { + id: Integer <<PK>> -- name: String andy: String pass: String role: Integer incumbency: Integer -- *- jinhuo: Jinhuo [*] } ' 鏍稿績鍏崇郴瀹氫箟 Bancai }|--|| Kucun Bancai }o--o| Caizhi Bancai }o--o| Mupi : mupi1 Bancai }o--o| Mupi : mupi2 Chanpin_zujian }o--|| Chanpin Chanpin_zujian }o--|| Zujian Chanpin_zujian }o--|| Bancai Dingdan_chanpin }o--|| Dingdan Dingdan_chanpin }o--|| Chanpin Dingdan_bancai }o--|| Dingdan Dingdan_bancai }o--|| Chanpin Dingdan_bancai }o--|| Zujian Dingdan_bancai }o--|| Bancai Jinhuo }o--|| Dingdan_bancai Jinhuo }o--|| User ' 闆嗗悎鍏崇郴 Caizhi "1" *-- "many" Bancai : bancai Mupi "1" *-- "many" Bancai : bancai_mupi1 Mupi "1" *-- "many" Bancai : bancai_mupi2 Chanpin "1" *-- "many" Chanpin_zujian : chanpin_zujian Chanpin "1" *-- "many" Dingdan_chanpin : dingdan_chanpin Dingdan "1" *-- "many" Dingdan_chanpin : dingdan_chanpin Dingdan "1" *-- "many" Dingdan_bancai : dingdan_bancai Dingdan_bancai "1" *-- "many" Jinhuo : jinhuo User "1" *-- "many" Jinhuo : jinhuo Zujian "1" *-- "many" Chanpin_zujian : chanping_zujian @enduml // 解析数据引用关系的辅助函数 /** * 解析数据中的引用关系 * 该函数用于处理嵌套的数据结构,将数据中的引用关系解析为实际的对象引用。 * 它会遍历数据中的所有实体,查找属性名中包含的数字(如"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} 处理后的实体对象 * 功能说明: * 遍历实体对象的每个属性 * 如果属性值是数组,则尝试在数据源中查找匹配项更新数组元素 * 如果属性值是对象,则尝试在数据源中查找匹配项更新该对象 * 自动处理单复数形式的数据源键名 */ 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.cache = new Map(); this.entityTypeMap = { bancai: 'bancais', dingdan: 'dingdans', mupi: 'mupis', chanpin: 'chanpins', kucun: 'kucuns', dingdan_bancai: 'dingdan_bancais', chanpin_zujian: 'chanpin_zujians', zujian: 'zujians', caizhi: 'caizhis', dingdan_chanpin: 'dingdan_chanpins', user: 'users', jinhuo: 'jinhuos' }; } /** * 创建实体代理 * @param {Object} entity 实体对象 * @param {string} entityType 实体类型 * @returns {Proxy} 返回代理后的实体 */ createProxy(entity, entityType) { const handler = { get: (target, prop) => { // 如果是普通属性,直接返回 if (typeof target[prop] !== 'object' || target[prop] === null) { return target[prop]; } // 检查是否是引用属性 const refType = this.getReferenceType(prop); if (refType) { // 如果是数组引用 if (Array.isArray(target[prop])) { return this.loadReferences(target[prop], refType); } // 如果是对象引用 else { return this.loadReference(target[prop], refType); } } return target[prop]; } }; return new Proxy(entity, handler); } /** * 获取引用类型 * @param {string} prop 属性名 * @returns {string|null} 引用类型(复数形式) */ getReferenceType(prop) { // 去除数字后缀(如 "mupi1" -> "mupi") const baseProp = prop.replace(/\d/g, ''); // 尝试直接匹配实体类型 if (this.entityTypeMap[baseProp]) { return this.entityTypeMap[baseProp]; } // 尝试添加 's' 后缀匹配 const pluralProp = `${baseProp}s`; if (this.dataManager._rawData[pluralProp]) { return pluralProp; } return null; } /** * 加载单个关联引用 * @param {Object} ref 引用对象(包含id) * @param {string} refType 引用类型(复数形式) * @returns {Promise<Object>} 解析后的实体对象 */ async loadReference(ref, refType) { if (!ref || !ref.id) return null; const cacheKey = `${refType}_${ref.id}`; // 检查缓存 if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey); } // 尝试从本地数据查找 const entities = this.dataManager._rawData[refType] || []; let entity = entities.find(e => e.id === ref.id); // 如果本地找不到,尝试同步数据 if (!entity) { await this.dataManager.syncData(); entity = (this.dataManager._rawData[refType] || []).find(e => e.id === ref.id); } if (entity) { // 递归解析嵌套引用 const resolvedEntity = resolveDataReference({...entity}, this.dataManager._rawData); // 创建代理并缓存 const proxy = this.createProxy(resolvedEntity, refType); this.cache.set(cacheKey, proxy); return proxy; } return ref; // 返回原始引用 } /** * 加载多个关联引用 * @param {Array} refs 引用对象数组 * @param {string} refType 引用类型(复数形式) * @returns {Promise<Array>} 解析后的实体对象数组 */ async loadReferences(refs, refType) { if (!Array.isArray(refs)) return []; return Promise.all( refs.map(ref => this.loadReference(ref, refType)) ); } /** * 清除缓存 */ clearCache() { this.cache.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} 是否获取成功 * @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.lazyLoader.clearCache(); // 5. 保存更新后的数据到本地存储 this.saveDataToStorage(); // 6. 触发数据更新回调 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; // 解析所有引用关系 // resolveDataReferences(this._rawData); } } 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;----------------------------------------页面数据显示不无安全,订单产品没有显示,index.js直接要以dingdan_bancai制定每行数据

观察社
  • 粉丝: 30
上传资源 快速赚钱