核心实现逻辑(Vue 技术栈适配)
核心依赖 IndexedDB(本地持久化存储) + 网络状态监听,搭配请求队列保证同步可靠性,完全满足离线下单、联网自动同步需求。
一、核心技术选型(明确唯一方案,快速落地)
-
本地存储:IndexedDB
- 替代 localStorage(容量仅 5M),支持大容量(几十 MB 到 GB 级)、事务性操作,适配订单多字段存储,浏览器离线时稳定持久化数据。
- 推荐工具:localForage(封装 IndexedDB,API 像 localStorage 一样简单,自动降级兼容,Vue 项目直接 npm 引入即用)。
-
网络状态监听:navigator.onLine
- 原生 API 零依赖,实时监听网络切换,离线时写入本地缓存,联网时触发同步逻辑。
-
同步可靠性:请求队列 + 状态标记
- 给每个本地订单加
syncStatus字段(pending待同步 /success同步完成 /failed同步失败),避免重复同步、漏同步。
- 给每个本地订单加
二、分步实现流程(Vue 项目落地步骤)
1. 初始化本地存储(localForage)
javascript
// src/utils/localStorage.js
import localForage from 'localForage';
// 初始化订单存储库(name 自定义,storeName 对应订单表名)
export const orderStore = localForage.createInstance({
name: 'cashierOfflineDB',
storeName: 'orders'
});
// 封装核心方法(增、查、改、删)
export const orderStorage = {
// 新增离线订单(离线时调用)
saveOfflineOrder: (order) => orderStore.setItem(order.id, { ...order, syncStatus: 'pending' }),
// 获取所有待同步订单
getPendingOrders: async () => {
const orders = [];
await orderStore.iterate((value) => {
value.syncStatus === 'pending' && orders.push(value);
});
return orders;
},
// 更新订单同步状态(同步成功/失败后调用)
updateOrderSyncStatus: (orderId, status) => orderStore.update(orderId, (order) => ({ ...order, syncStatus: status })),
// 删除已同步成功的订单(可选,节省本地空间)
deleteSyncedOrder: (orderId) => orderStore.removeItem(orderId)
};
2. 收银下单逻辑(区分在线/离线)
javascript
// src/views/Cashier.vue
import { orderStorage } from '@/utils/localStorage';
import { createOrderApi } from '@/api/order'; // 后端创建订单接口
export default {
methods: {
async handleCreateOrder(order) {
const isOnline = navigator.onLine;
try {
if (isOnline) {
// 在线:直接调用后端接口,成功后无需本地缓存
await createOrderApi(order);
alert('下单成功');
} else {
// 离线:生成唯一订单id(uuid/v4 或时间戳+随机数),写入本地
const offlineOrder = { id: `OFF-${Date.now()}-${Math.random().toString(36).slice(-6)}`, ...order };
await orderStorage.saveOfflineOrder(offlineOrder);
alert('已离线保存订单,联网后自动同步');
}
} catch (err) {
// 在线但接口报错(如后端临时故障),也写入本地缓存,避免订单丢失
const failOrder = { id: `OFF-${Date.now()}-${Math.random().toString(36).slice(-6)}`, ...order };
await orderStorage.saveOfflineOrder(failOrder);
alert('下单失败,已离线缓存');
}
}
}
};
3. 网络恢复同步逻辑(全局监听,自动触发)
- 方案1:组件内监听(仅收银页生效)
- 方案2:main.js 全局监听(全项目生效,推荐)
javascript
// src/main.js
import { orderStorage } from '@/utils/localStorage';
import { createOrderApi } from '@/api/order';
// 核心同步函数
const syncPendingOrders = async () => {
if (!navigator.onLine) return; // 确保联网时执行
const pendingOrders = await orderStorage.getPendingOrders();
if (pendingOrders.length === 0) return;
// 批量同步(也可串行,避免并发过多)
for (const order of pendingOrders) {
try {
await createOrderApi(order); // 调用后端同步接口
await orderStorage.updateOrderSyncStatus(order.id, 'success');
await orderStorage.deleteSyncedOrder(order.id); // 同步成功删除本地缓存
} catch (err) {
await orderStorage.updateOrderSyncStatus(order.id, 'failed'); // 失败标记,后续重试
console.error(`订单${order.id}同步失败:`, err);
}
}
};
// 1. 初始加载时,检查网络+触发同步(避免页面刷新后漏同步)
window.addEventListener('load', syncPendingOrders);
// 2. 网络从离线切在线时,触发同步
window.addEventListener('online', syncPendingOrders);
// 3. 可选:定时重试失败订单(防止同步一次失败后无法再次同步)
setInterval(syncPendingOrders, 5 * 60 * 1000); // 每5分钟重试一次
4. 异常处理(兜底保障)
- 订单 ID 唯一性:用
uuid库生成(npm install uuid),比时间戳+随机数更稳妥,避免本地订单 ID 重复。 - 同步失败重试:除了定时重试,可在收银页加"手动同步"按钮,允许用户主动触发。
- 后端兼容:同步时后端需支持"幂等性校验"(如通过订单 ID 判断是否已同步,避免重复创建订单),前端传递订单唯一 ID 即可。
三、关键注意点(避坑核心)
- 订单字段必须包含 唯一标识(id) 、下单时间(createTime) 、同步状态(syncStatus),方便后续查询、更新、去重。
- 本地存储容量:一般浏览器 IndexedDB 容量默认 50MB+,足够存储 thousands 级订单,若订单量极大,可定期清理已同步订单。
- 浏览器兼容性:主流浏览器(Chrome、Edge、Firefox、Safari 10+)均支持,适配收银场景常用设备(电脑、平板)。
四、实际价值与场景适配
- 适配场景:超市、便利店等收银场景,网络波动/断网时不影响正常下单,避免流失订单;联网后自动同步到后端,不影响对账、库存更新。
- 优势:零额外服务器成本(依赖浏览器本地存储),实现简单,Vue 项目1-2天可落地,稳定性高。