Browse Source

update switch lot

MergeProblem1
CANCERYS\kw093 2 days ago
parent
commit
8b2ab939e8
2 changed files with 158 additions and 27 deletions
  1. +155
    -27
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
  2. +3
    -0
      src/components/FinishedGoodSearch/LotConfirmationModal.tsx

+ 155
- 27
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx View File

@@ -573,6 +573,10 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false);
// Store callbacks in refs to avoid useEffect dependency issues
const processOutsideQrCodeRef = useRef<((latestQr: string) => Promise<void>) | null>(null);
const resetScanRef = useRef<(() => void) | null>(null);
const lotConfirmOpenedQrCountRef = useRef<number>(0);
const lotConfirmOpenedQrValueRef = useRef<string>('');
const lotConfirmInitialSameQrSkippedRef = useRef<boolean>(false);
const autoConfirmInProgressRef = useRef<boolean>(false);
@@ -1137,6 +1141,55 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
});
window.dispatchEvent(event);
}, [allLotsCompleted]);

const clearLotConfirmationState = useCallback((clearProcessedRefs: boolean = false) => {
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);

if (clearProcessedRefs) {
setTimeout(() => {
lastProcessedQrRef.current = '';
processedQrCodesRef.current.clear();
console.log(`⏱️ [LOT CONFIRM MODAL] Cleared refs to allow reprocessing`);
}, 100);
}
}, []);

const parseQrPayload = useCallback((rawQr: string): { itemId: number; stockInLineId: number } | null => {
if (!rawQr) return null;

if ((rawQr.startsWith("{2fitest") || rawQr.startsWith("{2fittest")) && rawQr.endsWith("}")) {
let content = '';
if (rawQr.startsWith("{2fittest")) {
content = rawQr.substring(9, rawQr.length - 1);
} else {
content = rawQr.substring(8, rawQr.length - 1);
}

const parts = content.split(',');
if (parts.length === 2) {
const itemId = parseInt(parts[0].trim(), 10);
const stockInLineId = parseInt(parts[1].trim(), 10);
if (!isNaN(itemId) && !isNaN(stockInLineId)) {
return { itemId, stockInLineId };
}
}
return null;
}

try {
const parsed = JSON.parse(rawQr);
if (parsed?.itemId && parsed?.stockInLineId) {
return { itemId: parsed.itemId, stockInLineId: parsed.stockInLineId };
}
return null;
} catch {
return null;
}
}, []);

const handleLotConfirmation = useCallback(async () => {
if (!expectedLotData || !scannedLotData || !selectedLotForQr) return;
setIsConfirmingLot(true);
@@ -1175,10 +1228,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
}
// ✅ 修复:先关闭 modal 和清空状态,再刷新数据
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
clearLotConfirmationState(false);
// ✅ 修复:刷新数据前设置刷新标志,避免在刷新期间处理新的 QR code
setIsRefreshingData(true);
@@ -1189,7 +1239,94 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
} finally {
setIsConfirmingLot(false);
}
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchAllCombinedLotData, resetScan]);
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchAllCombinedLotData, resetScan, clearLotConfirmationState]);

const handleLotConfirmationByRescan = useCallback(async (rawQr: string): Promise<boolean> => {
if (!lotConfirmationOpen || !selectedLotForQr || !expectedLotData || !scannedLotData) {
return false;
}

const payload = parseQrPayload(rawQr);
const expectedStockInLineId = Number(selectedLotForQr.stockInLineId);
const mismatchedStockInLineId = Number(scannedLotData?.stockInLineId);

if (payload) {
const rescannedStockInLineId = Number(payload.stockInLineId);

// 再扫“差异 lot” => 直接执行切换
if (
Number.isFinite(mismatchedStockInLineId) &&
rescannedStockInLineId === mismatchedStockInLineId
) {
await handleLotConfirmation();
return true;
}

// 再扫“原建议 lot” => 关闭弹窗并按原 lot 正常记一次扫描
if (
Number.isFinite(expectedStockInLineId) &&
rescannedStockInLineId === expectedStockInLineId
) {
clearLotConfirmationState(false);
if (processOutsideQrCodeRef.current) {
await processOutsideQrCodeRef.current(JSON.stringify(payload));
}
return true;
}
} else {
// 兼容纯 lotNo 文本扫码
const scannedText = rawQr?.trim();
const expectedLotNo = expectedLotData?.lotNo?.trim();
const mismatchedLotNo = scannedLotData?.lotNo?.trim();

if (mismatchedLotNo && scannedText === mismatchedLotNo) {
await handleLotConfirmation();
return true;
}

if (expectedLotNo && scannedText === expectedLotNo) {
clearLotConfirmationState(false);
if (processOutsideQrCodeRef.current) {
await processOutsideQrCodeRef.current(JSON.stringify({
itemId: selectedLotForQr.itemId,
stockInLineId: selectedLotForQr.stockInLineId,
}));
}
return true;
}
}

return false;
}, [lotConfirmationOpen, selectedLotForQr, expectedLotData, scannedLotData, parseQrPayload, handleLotConfirmation, clearLotConfirmationState]);

useEffect(() => {
if (!lotConfirmationOpen || !expectedLotData || !scannedLotData || !selectedLotForQr) {
autoConfirmInProgressRef.current = false;
return;
}

if (autoConfirmInProgressRef.current || isConfirmingLot) {
return;
}

autoConfirmInProgressRef.current = true;
handleLotConfirmation()
.catch((error) => {
console.error("Auto confirm lot substitution failed:", error);
})
.finally(() => {
autoConfirmInProgressRef.current = false;
});
}, [lotConfirmationOpen, expectedLotData, scannedLotData, selectedLotForQr, isConfirmingLot, handleLotConfirmation]);

useEffect(() => {
if (lotConfirmationOpen) {
// 记录弹窗打开时的扫码数量,避免把“触发弹窗的同一次扫码”当作二次确认
lotConfirmOpenedQrCountRef.current = qrValues.length;
lotConfirmOpenedQrValueRef.current = qrValues[qrValues.length - 1] || '';
lotConfirmInitialSameQrSkippedRef.current = true;
}
}, [lotConfirmationOpen, qrValues.length]);
const handleQrCodeSubmit = useCallback(async (lotNo: string) => {
console.log(` Processing QR Code for lot: ${lotNo}`);
@@ -1937,17 +2074,22 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
}
}
// Skip processing if confirmation modals are open
// BUT: Allow processing if modal was just closed (to allow reopening for different stockInLineId)
if (lotConfirmationOpen || manualLotConfirmationOpen) {
// lot confirm 弹窗打开时,允许通过“再次扫码”决定走向(切换或继续原 lot)
if (lotConfirmationOpen) {
// 已改回自动确认:弹窗打开时不再等待二次扫码
return;
}

// Skip processing if manual confirmation modal is open
if (manualLotConfirmationOpen) {
// Check if this is a different QR code than what triggered the modal
const modalTriggerQr = lastProcessedQrRef.current;
if (latestQr === modalTriggerQr) {
console.log(`⏱️ [QR PROCESS] Skipping - modal open for same QR: lotConfirmation=${lotConfirmationOpen}, manual=${manualLotConfirmationOpen}`);
console.log(`⏱️ [QR PROCESS] Skipping - manual modal open for same QR`);
return;
}
// If it's a different QR, allow processing (user might have canceled and scanned different lot)
console.log(`⏱️ [QR PROCESS] Different QR detected while modal open, allowing processing`);
// If it's a different QR, allow processing
console.log(`⏱️ [QR PROCESS] Different QR detected while manual modal open, allowing processing`);
}
const qrDetectionStartTime = performance.now();
@@ -2061,7 +2203,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO
qrProcessingTimeoutRef.current = null;
}
};
}, [qrValues.length, isManualScanning, isRefreshingData, combinedLotData.length, lotConfirmationOpen, manualLotConfirmationOpen]);
}, [qrValues, isManualScanning, isRefreshingData, combinedLotData.length, lotConfirmationOpen, manualLotConfirmationOpen, handleLotConfirmationByRescan]);
const renderCountRef = useRef(0);
const renderStartTimeRef = useRef<number | null>(null);

@@ -3440,21 +3582,7 @@ paginatedData.map((lot, index) => {
open={lotConfirmationOpen}
onClose={() => {
console.log(`⏱️ [LOT CONFIRM MODAL] Closing modal, clearing state`);
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
// ✅ IMPORTANT: Clear refs to allow reprocessing the same QR code if user cancels and scans again
// This allows the modal to reopen for the same itemId with a different stockInLineId
setTimeout(() => {
lastProcessedQrRef.current = '';
processedQrCodesRef.current.clear();
console.log(`⏱️ [LOT CONFIRM MODAL] Cleared refs to allow reprocessing`);
}, 100);
// ✅ Don't clear processedQrCombinations - it tracks by itemId+stockInLineId,
// so reopening for same itemId but different stockInLineId is allowed
clearLotConfirmationState(true);
}}
onConfirm={handleLotConfirmation}
expectedLot={expectedLotData}


+ 3
- 0
src/components/FinishedGoodSearch/LotConfirmationModal.tsx View File

@@ -97,6 +97,9 @@ const LotConfirmationModal: React.FC<LotConfirmationModalProps> = ({
<li>{t("Update your suggested lot to the this scanned lot")}</li>
</ul>
</Alert>
<Alert severity="info">
{t("You can also scan again to confirm: scan the scanned lot again to switch, or scan the expected lot to continue with current lot.")}
</Alert>
</Stack>
</DialogContent>



Loading…
Cancel
Save