From b5d01891fbbd69d8d50d2b4fb562fac3130fc2d6 Mon Sep 17 00:00:00 2001
From: 647556386 <647556386@qq.com>
Date: 星期一, 20 四月 2026 11:01:22 +0800
Subject: [PATCH] 虚拟出入库单据锁定
---
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue | 460 ++++++++++++++++++++++++++++----------------------------
1 files changed, 231 insertions(+), 229 deletions(-)
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue"
index ec9dd47..7fc2023 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue"
@@ -8,6 +8,14 @@
title="铏氭嫙鍑哄叆搴�"
class="custom-vol-box"
>
+ <!-- 鎻愪氦閬僵灞傦細瑕嗙洊鏁翠釜寮圭獥鍐呭鍖哄煙 -->
+ <div class="submit-mask" v-show="submitLoading">
+ <div class="mask-content">
+ <el-loading-spinner class="loading-icon" />
+ <span class="loading-text">姝e湪鎻愪氦鍑哄簱锛岃绋嶅��...</span>
+ </div>
+ </div>
+
<div>
<!-- 鍗曟嵁杈撳叆鍖哄煙锛堟敮鎸佹壂鐮侊級 -->
<el-form
@@ -25,8 +33,10 @@
@input="handleOutboundInput"
@keyup.enter="validateOutboundOrder"
ref="outboundInputRef"
- :disabled="loading"
+ :disabled="loading || submitLoading || isOutboundVerified"
></el-input>
+ <span v-if="isOutboundVerified" class="verified-tag">鉁� 宸查獙璇�</span>
+ <span v-else-if="loading" class="loading-tag">鉁� 楠岃瘉涓�...</span>
</el-form-item>
<el-form-item label="閲囪喘鍗曟嵁:" name="purchaseOrderNo">
<el-input
@@ -37,6 +47,7 @@
@input="handlePurchaseInput"
readonly
ref="purchaseInputRef"
+ :disabled="submitLoading"
></el-input>
</el-form-item>
</el-form>
@@ -53,7 +64,6 @@
label="鎵弿鏉$爜:"
style="width: 80%"
name="barcode"
- :rules="[{ required: true, message: '璇锋壂鎻忔垨杈撳叆鏉$爜', trigger: 'blur' }]"
>
<el-input
ref="barcodeInputRef"
@@ -62,7 +72,7 @@
@keydown.enter="debouncedHandleScan"
autofocus
class="custom-input"
- :disabled="!orderForm.outboundOrderNo || loading"
+ :disabled="!isOutboundVerified || loading || submitLoading"
></el-input>
</el-form-item>
<el-form-item>
@@ -71,14 +81,14 @@
size="small"
@click="handleScan"
class="custom-button"
- :disabled="!orderForm.outboundOrderNo || loading"
+ :disabled="!isOutboundVerified || loading || submitLoading"
>
<Search /> 纭鎵弿
</el-button>
</el-form-item>
</el-form>
- <!-- 涓嬫柟鏄剧ず妗嗭紙鐩存帴娓叉煋鍚庣杩斿洖鐨勬暟缁勶級 -->
+ <!-- 涓嬫柟鏄剧ず妗� -->
<div class="scan-list">
<el-card shadow="hover" style="margin-bottom: 10px; border: none;" class="custom-card">
<div class="card-header">
@@ -87,48 +97,28 @@
<div class="card-body">
<el-scrollbar height="400px" class="custom-scrollbar">
<transition-group name="barcode-item-transition">
- <div class="barcode-item" v-for="(item, index) in scannedBarcodes" :key="`${item.barcode}-${index}`" :data-index="index">
+ <div class="barcode-item" v-for="(item, index) in scannedBarcodes" :key="`${item.barcode}-${index}`">
<div class="barcode-detail">
- <div class="detail-row">
- <span class="label">鏉$爜锛�</span>
- <span class="value">{{ item.barcode || '-' }}</span>
- </div>
- <div class="detail-row">
- <span class="label">鐗╂枡缂栫爜锛�</span>
- <span class="value">{{ item.materielCode || '-' }}</span>
- </div>
- <div class="detail-row">
- <span class="label">鐗╂枡鍚嶇О锛�</span>
- <span class="value">{{ item.materielName || '-' }}</span>
- </div>
- <div class="detail-row">
- <span class="label">鎵规鍙凤細</span>
- <span class="value">{{ item.batchNo || '-' }}</span>
- </div>
- <div class="detail-row">
- <span class="label">鏉$爜鏁伴噺锛�</span>
- <span class="value">{{ item.orderQuantity || item.quantity || 0 }}</span>
- </div>
- <div class="detail-row">
- <span class="label">渚涘簲鍟嗙紪鐮侊細</span>
- <span class="value">{{ item.supplyCode || '-' }}</span>
- </div>
- <div class="detail-row">
- <span class="label">閲囪喘鍗曞彿锛�</span>
- <span class="value">{{ item.purchaseOrderNo || '-' }}</span>
- </div>
+ <div class="detail-row"><span class="label">鏉$爜锛�</span><span class="value">{{ item.barcode || '-' }}</span></div>
+ <div class="detail-row"><span class="label">鐗╂枡缂栫爜锛�</span><span class="value">{{ item.materielCode || '-' }}</span></div>
+ <div class="detail-row"><span class="label">鐗╂枡鍚嶇О锛�</span><span class="value">{{ item.materielName || '-' }}</span></div>
+ <div class="detail-row"><span class="label">鎵规鍙凤細</span><span class="value">{{ item.batchNo || '-' }}</span></div>
+ <div class="detail-row"><span class="label">鏉$爜鏁伴噺锛�</span><span class="value">{{ item.orderQuantity || item.quantity || 0 }}</span></div>
+ <div class="detail-row"><span class="label">渚涘簲鍟嗙紪鐮侊細</span><span class="value">{{ item.supplyCode || '-' }}</span></div>
+ <div class="detail-row"><span class="label">閲囪喘鍗曞彿锛�</span><span class="value">{{ item.purchaseOrderNo || '-' }}</span></div>
</div>
<el-button
class="delete-btn"
@click="removeItem(index, item.barcode)"
icon="Delete"
circle
- :disabled="loading"
+ :disabled="loading || submitLoading"
></el-button>
</div>
</transition-group>
<div class="empty-tip" v-if="scannedBarcodes.length === 0">
- <span>鏆傛棤鎵弿璁板綍锛岃鍏堣緭鍏ュ嚭搴撳崟鎹悗鎵弿鏉$爜</span>
+ <span v-if="isOutboundVerified">鏆傛棤鎵弿璁板綍锛岃鎵弿鏉$爜</span>
+ <span v-else>璇峰厛杈撳叆骞堕獙璇佹湁鏁堢殑鍑哄簱鍗曟嵁鍙�</span>
</div>
</el-scrollbar>
</div>
@@ -142,7 +132,7 @@
type="primary"
size="small"
@click="submit"
- :disabled="scannedBarcodes.length === 0 || !orderForm.outboundOrderNo || loading"
+ :disabled="scannedBarcodes.length === 0 || !isOutboundVerified || loading || submitLoading"
class="submit-btn"
>
<Check /> 鎻愪氦鍑哄簱
@@ -150,13 +140,9 @@
<el-button
type="text"
size="small"
- @click="(e) => {
- e.stopPropagation();
- e.preventDefault();
- showDetailBox = false;
- }"
+ @click="handleCancel"
class="cancel-btn"
- :disabled="loading"
+ :disabled="loading || submitLoading"
>
鍙栨秷
</el-button>
@@ -167,24 +153,21 @@
</template>
<script setup>
-import { ref, reactive, onMounted, nextTick } from 'vue';
+import { ref, reactive, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { Search, Check } from '@element-plus/icons-vue';
-
import VolBox from "@/components/basic/VolBox.vue";
import http from '@/api/http';
// 鍝嶅簲寮忔暟鎹�
const showDetailBox = ref(false);
-const orderForm = reactive({
- outboundOrderNo: "",
- purchaseOrderNo: ""
-});
-const formData = reactive({
- barcode: "",
-});
+const orderForm = reactive({ outboundOrderNo: "", purchaseOrderNo: "" });
+const formData = reactive({ barcode: "" });
const scannedBarcodes = ref([]);
const loading = ref(false);
+const submitLoading = ref(false);
+const isOutboundVerified = ref(false);
+let unlockCalled = false; // 闃叉閲嶅瑙i攣
// 妯℃澘寮曠敤
const formRef = ref(null);
@@ -192,202 +175,172 @@
const outboundInputRef = ref(null);
const purchaseInputRef = ref(null);
-// 缁勪欢鎸傝浇鏃惰仛鐒﹀埌鍑哄簱鍗曡緭鍏ユ
-onMounted(() => {
- nextTick(() => {
- outboundInputRef.value?.focus();
- });
-});
+// 闊抽璧勬簮
+const successAudioSrc = require('@/assets/audio/success.mp3');
+const errorAudioSrc = require('@/assets/audio/error.mp3');
+const playAudio = (src, volume = 0.8) => {
+ try { const audio = new Audio(src); audio.volume = volume; audio.play().catch(() => {}); } catch(e) {}
+};
+const playSuccess = () => playAudio(successAudioSrc);
+const playError = () => playAudio(errorAudioSrc);
-// 绠�鍗曢槻鎶栧嚱鏁�
-const debounce = (fn, delay = 100) => {
- let timer = null;
- return (...args) => {
- if (timer) clearTimeout(timer);
- timer = setTimeout(() => {
- fn.apply(this, args);
- }, delay);
- };
+// ========== 瑙i攣鏍稿績閫昏緫锛堜娇鐢ㄦ柊鎺ュ彛 MovePickingOrders锛� ==========
+const unlockOutboundOrder = async () => {
+ if (!isOutboundVerified.value || !orderForm.outboundOrderNo?.trim()) return;
+ if (unlockCalled) return;
+ unlockCalled = true;
+ const outboundOrderNo = orderForm.outboundOrderNo.trim();
+ try {
+ // 鏇挎崲涓� MovePickingOrders 鎺ュ彛
+ await http.post(`/api/OutboundPicking/MovePickingOrders?outOrder=${outboundOrderNo}`, null, "瑙i攣鍑哄簱鍗曚腑...").catch(() => {});
+ } catch (error) {
+ // 闈欓粯澶辫触
+ }
};
-// 鎵撳紑寮圭獥
+// 椤甸潰鍏抽棴/鍒锋柊鏃剁殑鍚屾瑙i攣锛堜娇鐢� sendBeacon 鎴� fetch keepalive锛�
+const unlockOnPageUnload = () => {
+ if (!isOutboundVerified.value || !orderForm.outboundOrderNo?.trim()) return;
+ const outboundOrderNo = orderForm.outboundOrderNo.trim();
+ const url = `/api/OutboundPicking/MovePickingOrders?outOrder=${encodeURIComponent(outboundOrderNo)}`;
+ // 浣跨敤 fetch keepalive 鍙戦��
+ fetch(url, { method: 'POST', keepalive: true, headers: { 'Content-Type': 'application/json' } }).catch(() => {});
+};
+
+// 鐩戝惉寮圭獥鍏抽棴锛堜换浣曟柟寮忥細鍙夊彿銆丒SC銆侀伄缃┿�佸彇娑堟寜閽瓑锛�
+watch(showDetailBox, (newVal, oldVal) => {
+ if (oldVal === true && newVal === false) {
+ // 寮圭獥鍏抽棴鏃惰皟鐢ㄨВ閿�
+ unlockOutboundOrder();
+ }
+});
+
+// 鍙栨秷鎸夐挳
+const handleCancel = (e) => {
+ e?.stopPropagation();
+ e?.preventDefault();
+ showDetailBox.value = false;
+};
+
+// 鎵撳紑寮圭獥锛堥噸缃墍鏈夌姸鎬侊級
const open = () => {
+ unlockCalled = false;
showDetailBox.value = true;
scannedBarcodes.value = [];
formData.barcode = "";
orderForm.outboundOrderNo = "";
orderForm.purchaseOrderNo = "";
- nextTick(() => {
- outboundInputRef.value?.focus(); // 鎵撳紑寮圭獥浠嶈仛鐒﹀嚭搴撳崟杈撳叆妗�
- });
+ submitLoading.value = false;
+ isOutboundVerified.value = false;
+ nextTick(() => outboundInputRef.value?.focus());
};
-/**
- * 楠岃瘉鍑哄簱鍗曟嵁鍙风殑鏈夋晥鎬�
- * 鏍稿績淇敼锛氶獙璇佹垚鍔熷悗鐩存帴鑱氱劍鏉$爜鎵弿妗嗭紝澶辫触鍒欒仛鐒﹀洖鍑哄簱鍗曡緭鍏ユ
- */
+// 楠岃瘉鍑哄簱鍗曞彿锛堜粛浣跨敤鍘熸帴鍙o紝姝ゆ帴鍙g敤浜庨獙璇佸崟鎹湁鏁堟�э級
const validateOutboundOrder = async () => {
const outboundOrderNo = orderForm.outboundOrderNo.trim();
- if (!outboundOrderNo) {
- return;
- }
-
+ if (!outboundOrderNo) { ElMessage.warning("璇疯緭鍏ュ嚭搴撳崟鎹彿"); return; }
try {
loading.value = true;
- const res = await http.post(
- `/api/OutboundPicking/GetAvailablePickingOrders?outOrder=`+ outboundOrderNo,
- "楠岃瘉鍑哄簱鍗曟嵁鍙蜂腑..."
- );
-
+ const res = await http.post(`/api/OutboundPicking/GetAvailablePickingOrders?outOrder=${outboundOrderNo}`, "楠岃瘉鍑哄簱鍗曟嵁鍙蜂腑...");
if (res.status !== true) {
- // 楠岃瘉澶辫触锛氭竻绌鸿緭鍏ユ锛屾彁绀洪敊璇紝鑱氱劍鍥炲嚭搴撳崟杈撳叆妗�
orderForm.outboundOrderNo = "";
- ElMessage.error(res.message || "鍑哄簱鍗曟嵁鍙烽獙璇佸け璐ワ紝璇锋鏌ュ崟鎹彿鏄惁姝g‘");
- nextTick(() => {
- outboundInputRef.value?.focus(); // 澶辫触鑱氱劍鍑哄簱鍗曡緭鍏ユ
- });
+ isOutboundVerified.value = false;
+ ElMessage.error(res.message || "鍑哄簱鍗曟嵁鍙烽獙璇佸け璐�");
+ nextTick(() => outboundInputRef.value?.focus());
return;
}
-
- // 楠岃瘉鎴愬姛锛氭彁绀虹敤鎴凤紝鐩存帴鑱氱劍鏉$爜鎵弿妗嗭紙鏍稿績淇敼锛�
+ isOutboundVerified.value = true;
ElMessage.success("鍑哄簱鍗曟嵁鍙烽獙璇侀�氳繃");
- nextTick(() => {
- barcodeInputRef.value?.focus(); // 鎴愬姛鐩存帴璺宠浆鍒版潯鐮佹壂鎻忔
- });
+ nextTick(() => barcodeInputRef.value?.focus());
} catch (error) {
- // 鎺ュ彛寮傚父锛氭竻绌鸿緭鍏ユ锛屾彁绀洪敊璇紝鑱氱劍鍥炲嚭搴撳崟杈撳叆妗�
orderForm.outboundOrderNo = "";
- ElMessage.error(`鍑哄簱鍗曟嵁鍙烽獙璇佸紓甯革細${error.message || "缃戠粶閿欒锛岃閲嶈瘯"}`);
- nextTick(() => {
- outboundInputRef.value?.focus(); // 寮傚父鑱氱劍鍑哄簱鍗曡緭鍏ユ
- });
+ isOutboundVerified.value = false;
+ ElMessage.error(`楠岃瘉寮傚父锛�${error.message || "缃戠粶閿欒"}`);
+ nextTick(() => outboundInputRef.value?.focus());
} finally {
loading.value = false;
}
};
-// 鍑哄簱鍗曡緭鍏ュ鐞�
-const handleOutboundInput = (value) => {
- if (value && value.trim()) {
- // 鍙繚鐣欏嚭搴撳崟鍙锋牸寮忛獙璇侀�昏緫
- }
-};
+const handleOutboundInput = (val) => { if (val?.trim()) isOutboundVerified.value = false; };
+const handlePurchaseInput = (val) => {};
-// 閲囪喘鍗曡緭鍏ュ鐞�
-const handlePurchaseInput = (value) => {
- if (value && value.trim()) {
- // 鍙繚鐣欓噰璐崟鍙锋牸寮忛獙璇侀�昏緫
- }
-};
-
-// 鑱氱劍鏉$爜杈撳叆妗嗭紙澶嶇敤鍑芥暟锛�
-const focusBarcodeInputDirectly = () => {
- if (orderForm.outboundOrderNo.trim()) {
- barcodeInputRef.value?.focus();
- } else {
- ElMessage.warning("璇峰厛杈撳叆鏈夋晥鐨勫嚭搴撳崟鎹彿");
- }
-};
-
-/**
- * 鏍规嵁鏉$爜鏌ヨ閲囪喘鍗曞彿
- */
+// 鑾峰彇閲囪喘鍗曞彿
const getPurchaseOrderByBarcode = async (barcode) => {
const res = await http.post(`/api/OutboundPicking/GetPurchaseOrderByBarcode?barCode=${encodeURIComponent(barcode)}`, "鏌ヨ閲囪喘鍗曞彿涓�...");
-
- if (res.status !== true) {
- throw new Error(res.message || "鏌ヨ閲囪喘鍗曞彿澶辫触");
- }
-
+ if (res.status !== true) throw new Error(res.message || "鏌ヨ澶辫触");
let purchaseOrderNo = '';
- if (Array.isArray(res.data) && res.data.length > 0) {
- purchaseOrderNo = res.data[0].purchaseOrderNo || res.data[0].orderId;
- } else {
- purchaseOrderNo = res.data?.purchaseOrderNo || res.data?.orderId;
- }
-
+ if (Array.isArray(res.data) && res.data.length > 0) purchaseOrderNo = res.data[0].purchaseOrderNo || res.data[0].orderId;
+ else purchaseOrderNo = res.data?.purchaseOrderNo || res.data?.orderId;
return purchaseOrderNo;
};
-// 鎵弿鏉$爜鏍稿績閫昏緫
+// 鎵弿鏉$爜
const handleScan = async () => {
- if (!formRef.value) return;
- await formRef.value.validateField('barcode');
-
- const barcode = formData.barcode.trim();
- const outboundOrderNo = orderForm.outboundOrderNo.trim();
-
- // 鏉$爜鍘婚噸
- const isDuplicate = scannedBarcodes.value.some(item => item.barcode === barcode);
- if (isDuplicate) {
- ElMessage.warning(`鏉$爜銆�${barcode}銆戝凡瀛樺湪锛屾棤闇�閲嶅鎵弿`);
- formData.barcode = "";
- nextTick(() => barcodeInputRef.value?.focus()); // 鍘婚噸鍚庝粛鑱氱劍鏉$爜妗�
+ if (!isOutboundVerified.value) {
+ ElMessage.warning("璇峰厛楠岃瘉鍑哄簱鍗曟嵁鍙�");
+ playError();
+ nextTick(() => outboundInputRef.value?.focus());
return;
}
-
+ const barcode = formData.barcode.trim();
+ if (!barcode) return;
+ const isDuplicate = scannedBarcodes.value.some(item => item.barcode === barcode);
+ if (isDuplicate) {
+ ElMessage.warning(`鏉$爜銆�${barcode}銆戝凡瀛樺湪`);
+ playError();
+ formData.barcode = "";
+ nextTick(() => barcodeInputRef.value?.focus());
+ return;
+ }
try {
loading.value = true;
-
- // 姝ラ1锛氭煡璇㈤噰璐崟鍙�
- const purchaseOrderNo = await getPurchaseOrderByBarcode(barcode);
- if (purchaseOrderNo) {
- orderForm.purchaseOrderNo = purchaseOrderNo;
- ElMessage.success(`鎴愬姛鏌ヨ鍒伴噰璐崟锛�${purchaseOrderNo}`);
- } else {
- ElMessage.info("鏈煡璇㈠埌璇ユ潯鐮佸搴旂殑閲囪喘鍗曞彿锛岀户缁獙璇佹潯鐮佹湁鏁堟��");
- }
-
- // 姝ラ2锛氶獙璇佹潯鐮佸苟鑾峰彇鐗╂枡淇℃伅
+ let purchaseOrderNo = '';
+ try {
+ purchaseOrderNo = await getPurchaseOrderByBarcode(barcode);
+ if (purchaseOrderNo) orderForm.purchaseOrderNo = purchaseOrderNo;
+ } catch (e) { ElMessage.info("鏈煡璇㈠埌閲囪喘鍗曞彿锛岀户缁獙璇佹潯鐮�"); playError(); }
const validateRes = await http.post("/api/OutboundPicking/BarcodeValidate", {
- outOder: outboundOrderNo,
+ outOder: orderForm.outboundOrderNo,
inOder: purchaseOrderNo || orderForm.purchaseOrderNo,
barCode: barcode
});
-
- if (validateRes.status === true) {
- if (!Array.isArray(validateRes.data) || validateRes.data.length === 0) {
- ElMessage.warning("璇ユ潯鐮侀獙璇佹垚鍔燂紝浣嗘湭杩斿洖鐗╂枡淇℃伅");
- } else {
- const newItems = validateRes.data.map(item => ({
- barcode: item.barcode || '',
- materielCode: item.materielCode || '',
- materielName: item.materielName || '',
- batchNo: item.batchNo || '',
- orderQuantity: item.orderQuantity || item.quantity || 0,
- supplyCode: item.supplyCode || '',
- purchaseOrderNo: purchaseOrderNo || ''
- }));
- scannedBarcodes.value.push(...newItems);
- ElMessage.success(`鎵弿鎴愬姛锛屾柊澧� ${newItems.length} 鏉$墿鏂欎俊鎭紝绱 ${scannedBarcodes.value.length} 鏉);
- }
+ if (validateRes.status === true && Array.isArray(validateRes.data) && validateRes.data.length) {
+ const newItems = validateRes.data.map(item => ({
+ barcode: item.barcode || '',
+ materielCode: item.materielCode || '',
+ materielName: item.materielName || '',
+ batchNo: item.batchNo || '',
+ orderQuantity: item.orderQuantity || item.quantity || 0,
+ supplyCode: item.supplyCode || '',
+ purchaseOrderNo: purchaseOrderNo || ''
+ }));
+ scannedBarcodes.value.push(...newItems);
+ ElMessage.success(`鎵弿鎴愬姛锛屾柊澧� ${newItems.length} 鏉);
+ playSuccess();
formData.barcode = "";
} else {
- ElMessage.error("鎵弿澶辫触锛�" + (validateRes.message || '鏉$爜楠岃瘉澶辫触'));
+ ElMessage.error(validateRes.message || '鏉$爜楠岃瘉澶辫触');
+ playError();
+ formData.barcode = "";
}
} catch (error) {
- ElMessage.error(error.message);
+ ElMessage.error('鏉$爜楠岃瘉寮傚父');
+ playError();
formData.barcode = "";
} finally {
loading.value = false;
- // 鎵弿瀹屾垚鍚庡缁堣仛鐒︽潯鐮佽緭鍏ユ锛堟柟渚胯繛缁壂鎻忥級
- nextTick(() => {
- barcodeInputRef.value?.focus();
- if (barcodeInputRef.value?.input) {
- barcodeInputRef.value.input.select = () => {};
- }
- });
+ nextTick(() => barcodeInputRef.value?.focus());
}
};
-// 甯﹂槻鎶栫殑鎵弿澶勭悊
-const debouncedHandleScan = debounce(async (e) => {
- e.stopPropagation();
- e.preventDefault();
- await handleScan();
-}, 100);
+const debounce = (fn, delay = 100) => {
+ let timer = null;
+ return (...args) => { if (timer) clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); };
+};
+const debouncedHandleScan = debounce(async (e) => { e.stopPropagation(); e.preventDefault(); await handleScan(); }, 100);
-// 绉婚櫎鍗曟潯璁板綍
+// 鍒犻櫎鏉$爜
const removeItem = async (index, barcode) => {
try {
loading.value = true;
@@ -397,69 +350,82 @@
inOder: currentItem?.purchaseOrderNo || orderForm.purchaseOrderNo,
barCode: barcode
});
-
if (res.status === true) {
scannedBarcodes.value.splice(index, 1);
ElMessage.success("鍒犻櫎鎴愬姛");
- if (scannedBarcodes.value.length === 0) {
- orderForm.purchaseOrderNo = "";
- }
+ if (scannedBarcodes.value.length === 0) orderForm.purchaseOrderNo = "";
} else {
- ElMessage.error("鍒犻櫎澶辫触锛�" + (res.message || '鍒犻櫎澶辫触'));
+ ElMessage.error("鍒犻櫎澶辫触锛�" + (res.message || ''));
}
} catch (error) {
- ElMessage.error("鍒犻櫎鏉$爜寮傚父锛�" + error.message);
+ ElMessage.error("鍒犻櫎寮傚父");
} finally {
loading.value = false;
- // 鍒犻櫎鍚庝粛鑱氱劍鏉$爜杈撳叆妗�
nextTick(() => barcodeInputRef.value?.focus());
}
};
// 鎻愪氦鍑哄簱
const submit = async () => {
- if (scannedBarcodes.value.length === 0) {
- ElMessage.warning("璇峰厛鎵弿鑷冲皯涓�鏉℃潯鐮�");
- nextTick(() => barcodeInputRef.value?.focus()); // 鎻愪氦澶辫触鑱氱劍鏉$爜妗�
+ if (!isOutboundVerified.value) {
+ ElMessage.warning("鍑哄簱鍗曟嵁鍙锋湭楠岃瘉");
+ nextTick(() => outboundInputRef.value?.focus());
return;
}
-
+ if (scannedBarcodes.value.length === 0) {
+ ElMessage.warning("璇峰厛鎵弿鏉$爜");
+ nextTick(() => barcodeInputRef.value?.focus());
+ return;
+ }
const barcodes = scannedBarcodes.value.map(item => item.barcode);
const purchaseOrderNos = [...new Set(scannedBarcodes.value.map(item => item.purchaseOrderNo).filter(Boolean))];
-
try {
- loading.value = true;
+ submitLoading.value = true;
const res = await http.post("/api/OutboundPicking/NoStockOutSubmit", {
OutOderSubmit: orderForm.outboundOrderNo,
InOderSubmit: purchaseOrderNos.join(',') || '',
BarCodeSubmit: barcodes
});
-
if (res.status === true) {
ElMessage.success("鍑哄簱鎻愪氦鎴愬姛");
- showDetailBox.value = false;
- scannedBarcodes.value = [];
- orderForm.purchaseOrderNo = "";
+ unlockOutboundOrder();
+ showDetailBox.value = false; // 瑙﹀彂 watch 瑙i攣锛堜絾鎻愪氦鎴愬姛鍚庨�氬父涓嶉渶瑕佽В閿侊紝鍥犱负宸茬粡鍑哄簱浜嗭紝涓嶈繃鍚庣搴旇嚜琛屽鐞嗙姸鎬侊級
+ // 娉ㄦ剰锛氭彁浜ゆ垚鍔熷悗涓嶅簲璇ュ啀璋冪敤瑙i攣鎺ュ彛锛屽洜涓哄崟鎹凡澶勭悊瀹屾瘯銆傝繖閲岄�氳繃鏍囧織閬垮厤锛歴ubmit 鎴愬姛鍚庨噸缃獙璇佺姸鎬�
+ isOutboundVerified.value = false;
+ unlockCalled = true; // 闃叉 watch 鍐嶆璋冪敤瑙i攣
} else {
- ElMessage.error("鍑哄簱鎻愪氦澶辫触锛�" + (res.message || '鎻愪氦澶辫触'));
- nextTick(() => barcodeInputRef.value?.focus()); // 鎻愪氦澶辫触鑱氱劍鏉$爜妗�
+ unlockOutboundOrder();
+ ElMessage.error("鍑哄簱鎻愪氦澶辫触锛�" + (res.message || ''));
+ nextTick(() => barcodeInputRef.value?.focus());
}
} catch (error) {
- ElMessage.error("鍑哄簱鎻愪氦寮傚父锛�" + error.message);
- nextTick(() => barcodeInputRef.value?.focus()); // 寮傚父鑱氱劍鏉$爜妗�
+ unlockOutboundOrder();
+ ElMessage.error("鎻愪氦寮傚父锛�" + error.message);
+ nextTick(() => barcodeInputRef.value?.focus());
} finally {
+ submitLoading.value = false;
loading.value = false;
}
};
-// 鏆撮湶缁欑埗缁勪欢鐨勬柟娉�
-defineExpose({
- open
+// 鐢熷懡鍛ㄦ湡锛氭坊鍔犻〉闈㈠叧闂椂鐨勮В閿佺洃鍚�
+onMounted(() => {
+ window.addEventListener('beforeunload', unlockOnPageUnload);
});
+onBeforeUnmount(() => {
+ window.removeEventListener('beforeunload', unlockOnPageUnload);
+ // 缁勪欢鍗歌浇鏃跺鏋滃脊绐楄繕寮�鐫�锛屼篃灏濊瘯瑙i攣锛堜絾閫氬父椤甸潰鍏抽棴浼氳Е鍙� beforeunload锛�
+ if (showDetailBox.value) {
+ unlockOutboundOrder();
+ }
+});
+
+// 鏆撮湶鏂规硶
+defineExpose({ open });
</script>
<style scoped>
-/*杩囨浮鍔ㄧ敾 */
+/* 杩囨浮鍔ㄧ敾 */
.barcode-item-transition-enter-active,
.barcode-item-transition-leave-active {
transition: all 0.3s ease;
@@ -476,10 +442,55 @@
transition: transform 1s ease;
}
+/* 鎻愪氦閬僵灞傛牱寮� */
+.submit-mask {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(255, 255, 255, 0.85);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 100;
+ border-radius: inherit;
+}
+.mask-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+ color: #409eff;
+ font-size: 15px;
+}
+.loading-icon {
+ font-size: 24px;
+ animation: el-loading-circle 1.5s linear infinite;
+}
+
+/* 楠岃瘉鐘舵�佹爣绛炬牱寮� */
+.verified-tag {
+ color: #67c23a;
+ font-size: 12px;
+ margin-left: 8px;
+ font-weight: 500;
+}
+.loading-tag {
+ color: #409eff;
+ font-size: 12px;
+ margin-left: 8px;
+ font-weight: 500;
+ animation: spin 1s linear infinite;
+}
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
.scan-list {
width: 100%;
}
-
.custom-card {
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08) !important;
@@ -488,7 +499,6 @@
.custom-card:hover {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12) !important;
}
-
.card-header {
display: flex;
justify-content: space-between;
@@ -502,11 +512,9 @@
font-size: 15px;
color: #333;
}
-
.card-body {
padding: 0;
}
-
.custom-scrollbar :deep(.el-scrollbar__thumb) {
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
@@ -519,7 +527,6 @@
.custom-scrollbar :deep(.el-scrollbar__wrap) {
overflow-x: hidden;
}
-
.barcode-item {
display: flex;
justify-content: space-between;
@@ -565,7 +572,6 @@
flex: 1;
word-break: break-all;
}
-
.delete-btn {
color: #ea1919;
font-size: 16px;
@@ -581,7 +587,6 @@
color: #f56c6c !important;
background: rgba(245, 108, 108, 0.1);
}
-
.empty-tip {
text-align: center;
padding: 80px 0;
@@ -597,7 +602,6 @@
font-size: 40px;
color: #dcdfe6;
}
-
.custom-input :deep(.el-input__inner) {
border-radius: 6px;
border-color: #e4e7ed;
@@ -609,7 +613,6 @@
border-color: #409eff;
box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.1);
}
-
.custom-button {
border-radius: 6px;
height: 36px;
@@ -622,7 +625,6 @@
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
}
-
.footer-actions {
text-align: right;
}
--
Gitblit v1.9.3