feat(P21PWA1-4): 移动扫码页面与盘点路由导航联动

这个提交包含在:
2026-03-11 20:52:11 +08:00
父节点 efd798c192
当前提交 f6de5443c4
共修改 8 个文件,包含 977 行新增和 5 行删除
+118
查看文件
@@ -0,0 +1,118 @@
import type { InventoryCheckResult } from "../api/inventoryChecks";
export type QueuedInventoryScanItem = {
id: string;
inventoryCheckId: string;
assetId: string;
assetNo: string;
result: InventoryCheckResult;
note: string | null;
createdAt: string;
lastError: string | null;
};
const DB_NAME = "hyb-oa-mobile-inventory";
const DB_VERSION = 1;
const STORE_NAME = "inventory_scan_queue";
let dbPromise: Promise<IDBDatabase> | null = null;
function getDatabase() {
if (!dbPromise) {
dbPromise = new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: "id" });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error ?? new Error("indexeddb_open_failed"));
});
}
return dbPromise;
}
function requestPromise<T>(request: IDBRequest<T>) {
return new Promise<T>((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error ?? new Error("indexeddb_request_failed"));
});
}
async function withStore<T>(mode: IDBTransactionMode, executor: (store: IDBObjectStore) => Promise<T>) {
const db = await getDatabase();
return new Promise<T>((resolve, reject) => {
const tx = db.transaction(STORE_NAME, mode);
const store = tx.objectStore(STORE_NAME);
executor(store)
.then((result) => {
tx.oncomplete = () => resolve(result);
tx.onerror = () => reject(tx.error ?? new Error("indexeddb_tx_failed"));
})
.catch((error) => reject(error));
});
}
export async function enqueueInventoryScan(input: {
inventoryCheckId: string;
assetId: string;
assetNo: string;
result: InventoryCheckResult;
note?: string | null;
}) {
const id = `${input.inventoryCheckId}:${input.assetId}`;
const item: QueuedInventoryScanItem = {
id,
inventoryCheckId: input.inventoryCheckId,
assetId: input.assetId,
assetNo: input.assetNo,
result: input.result,
note: input.note?.trim() ? input.note.trim() : null,
createdAt: new Date().toISOString(),
lastError: null,
};
await withStore("readwrite", async (store) => {
await requestPromise(store.put(item));
return true;
});
return item;
}
export async function listQueuedInventoryScans() {
return withStore("readonly", async (store) => {
const records = (await requestPromise(store.getAll())) as QueuedInventoryScanItem[];
return records.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
});
}
export async function removeQueuedInventoryScan(id: string) {
await withStore("readwrite", async (store) => {
await requestPromise(store.delete(id));
return true;
});
}
export async function updateQueuedInventoryScanError(id: string, lastError: string | null) {
await withStore("readwrite", async (store) => {
const existing = (await requestPromise(store.get(id))) as QueuedInventoryScanItem | undefined;
if (!existing) {
return false;
}
await requestPromise(
store.put({
...existing,
lastError,
}),
);
return true;
});
}