diff --git a/inventory-web/src/views/outbound/create.vue b/inventory-web/src/views/outbound/create.vue index b690bdf..e7ec7c4 100644 --- a/inventory-web/src/views/outbound/create.vue +++ b/inventory-web/src/views/outbound/create.vue @@ -1133,6 +1133,7 @@ const handleSignCancel = () => { showSignatureDialog.value = false } onUnmounted(() => { if (signaturePreviewUrl.value) URL.revokeObjectURL(signaturePreviewUrl.value) if (draftTimer) clearTimeout(draftTimer) + // 其余清理(定时检测、事件监听)见文件末尾的统一 onUnmounted }) // ★ 离开页面前:立即存草稿(防抖中未落盘的内容会丢) @@ -1181,8 +1182,65 @@ const handleBeforeUnload = () => { } catch (e) { /* 尽力而为,失败不影响 */ } } -onMounted(() => { window.addEventListener('beforeunload', handleBeforeUnload) }) -onUnmounted(() => { window.removeEventListener('beforeunload', handleBeforeUnload) }) +// ============================================================================ +// ★ 单据失效检测 +// +// 场景:库管正在扫码,管理员/申请人把这张单撤回了(或已执行完)。 +// 若不检测,库管会一直扫到提交时才发现单据已作废 —— 白扫一场。 +// +// 检测方式:重新拉一次「已通过(status=1)」列表,看当前单据是否还在其中。 +// 不在 → 说明已被撤回/驳回/执行,立即告知并清空界面。 +// 复用现有列表接口,无需新增端点。 +// +// 触发时机: +// · 页面重新可见时(PDA 熄屏唤醒、切回浏览器) +// · 每 60 秒一次(仅在购物车非空、即正在作业时) +// ============================================================================ +const checkRequestStillValid = async () => { + const rid = activeRequestId.value + if (!rid || cartItems.value.length === 0) return // 没在作业,不打扰 + + try { + const res: any = await getApprovalRequestList({ status: 1, page: 1, pageSize: 100 }) + const stillValid = (res.data?.items || []).some((r: any) => r.id === rid) + if (stillValid) return + + // 单据已不在「已通过」列表中 → 失效 + const no = selectedRequest.value?.request_no || rid + cartItems.value = [] + selectedRequest.value = null + activeRequestId.value = null + ElMessageBox.alert( + `申请单【${no}】已被撤回或已执行,不能再继续出库。\n\n` + + `本次已扫的内容已清空(草稿也已随之清除)。\n` + + `如需出库,请重新提交申请。`, + '⚠️ 申请单已失效', + { type: 'warning', confirmButtonText: '知道了' } + ).catch(() => {}) + } catch (e) { + // 网络异常时不打断作业 —— 后端在提交时仍会做最终校验 + console.warn('单据状态检测失败', e) + } +} + +// 页面重新可见时检测(PDA 熄屏唤醒、切回标签页) +const onVisibilityChange = () => { + if (document.visibilityState === 'visible') checkRequestStillValid() +} + +let validCheckTimer: ReturnType | null = null + +onMounted(() => { + window.addEventListener('beforeunload', handleBeforeUnload) + document.addEventListener('visibilitychange', onVisibilityChange) + validCheckTimer = setInterval(checkRequestStillValid, 60000) +}) + +onUnmounted(() => { + if (validCheckTimer) clearInterval(validCheckTimer) + document.removeEventListener('visibilitychange', onVisibilityChange) + window.removeEventListener('beforeunload', handleBeforeUnload) +})