工单系统离线重连方案 — IndexedDB + 队列 + WebSocket

工单系统离线重连方案

工单系统这类的强交互应用,断网就等于断工。现场工程师在车间、仓库这些信号不好的地方操作时,网络随时可能断。所以离线能力是刚需。

整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────┐
│ Service Worker │
(离线资源缓存 + 请求拦截)
├─────────────────────────────────────┤
│ 内存状态层 (Pinia)
│ - 当前工单列表 │
│ - 用户操作队列 │
├─────────────────────────────────────┤
│ 持久化层 (IndexedDB)
│ - 工单数据缓存 │
│ - 待提交操作队列 │
├─────────────────────────────────────┤
│ 网络状态检测 │
│ - navigator.onLine │
│ - 心跳探测 (WebSocket ping)
└─────────────────────────────────────┘

IndexedDB 缓存设计

使用 idb 库简化 IndexedDB 操作:

1
2
3
4
5
6
7
8
9
10
11
import { openDB } from 'idb'

const db = await openDB('work-order-cache', 1, {
upgrade(db) {
db.createObjectStore('orders', { keyPath: 'id' })
db.createObjectStore('pendingQueue', {
keyPath: 'id',
autoIncrement: true
})
}
})

读取走缓存优先、网络兜底:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function getOrders() {
// 先从 IndexedDB 读
const cached = await db.getAll('orders')
orders.value = cached

// 再尝试网络刷新
if (navigator.onLine) {
try {
const fresh = await api.fetchWorkOrders()
await db.put('orders', ...fresh)
orders.value = fresh
} catch { /* 网络失败就继续用缓存 */ }
}
}

操作队列

所有写操作在离线时先入队,网络恢复后按序执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const pendingQueue = ref<QueuedAction[]>([])

async function submitOrder(data: Partial<WorkOrder>) {
if (!navigator.onLine) {
// 离线 → 入队
await db.add('pendingQueue', {
type: 'UPDATE_ORDER',
payload: data,
createdAt: Date.now()
})
pendingQueue.value = await db.getAll('pendingQueue')
return
}
// 在线 → 直接提交
await api.submitOrder(data)
}

WebSocket 心跳与自动重连

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function setupReconnect() {
let ws: WebSocket | null = null
let reconnectTimer: ReturnType<typeof setTimeout>

function connect() {
ws = new WebSocket(WS_URL)

ws.onopen = () => {
isOnline.value = true
// 上线后消费队列
flushPendingQueue()
}

ws.onclose = () => {
isOnline.value = false
// 5 秒后自动重连
reconnectTimer = setTimeout(connect, 5000)
}

ws.onerror = () => ws?.close()
}

onUnmounted(() => {
clearTimeout(reconnectTimer)
ws?.close()
})

connect()
}

踩坑记录

  1. IndexedDB 事务超时 — 批量 put 太多了会挂,改成分批写入,一次 50 条
  2. 队列冲突 — 离线时修改同一条数据两次,重放顺序搞反了。加了个版本号字段做冲突检测
  3. Service Worker 缓存策略 — 先展示旧数据再静默更新的策略最合适

效果

这套方案上线后,掉线后重连成功率从 60% 提升到 98%+,用户在弱网环境下的操作基本感觉不到中断。


工单系统离线重连方案 — IndexedDB + 队列 + WebSocket
https://000902.icu/2026/06/09/work-order-offline/
作者
Xiazhou
发布于
2026年6月10日
许可协议