diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index 0d407fb..6ad335c 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -19,6 +19,7 @@ import { TablePagination, Modal, } from "@mui/material"; +import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; import { fetchLotDetail } from "@/app/api/inventory/actions"; import { useCallback, useEffect, useState, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; @@ -1481,6 +1482,15 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe } }, [t]); return ( + ( + lot.lotAvailability !== 'rejected' && + lot.stockOutLineStatus !== 'rejected' && + lot.stockOutLineStatus !== 'completed' + )} + > @@ -1837,6 +1847,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe /> )} + ); }; diff --git a/src/components/Jodetail/JobPickExecution.tsx b/src/components/Jodetail/JobPickExecution.tsx index a1917c3..f79fb32 100644 --- a/src/components/Jodetail/JobPickExecution.tsx +++ b/src/components/Jodetail/JobPickExecution.tsx @@ -19,6 +19,7 @@ import { TablePagination, Modal, } from "@mui/material"; +import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; import { useCallback, useEffect, useState, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useRouter } from "next/navigation"; @@ -1543,6 +1544,15 @@ const JobPickExecution: React.FC = ({ filterArgs }) => { }, [t]); return ( + ( + lot.lotAvailability !== 'rejected' && + lot.stockOutLineStatus !== 'rejected' && + lot.stockOutLineStatus !== 'completed' + )} + > {/* Job Order Header */} @@ -1858,6 +1868,7 @@ const JobPickExecution: React.FC = ({ filterArgs }) => { /> )} + ); }; diff --git a/src/components/Jodetail/JobPickExecutionsecondscan.tsx b/src/components/Jodetail/JobPickExecutionsecondscan.tsx index 6c288ff..5f3e916 100644 --- a/src/components/Jodetail/JobPickExecutionsecondscan.tsx +++ b/src/components/Jodetail/JobPickExecutionsecondscan.tsx @@ -19,6 +19,7 @@ import { TablePagination, Modal, } from "@mui/material"; +import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider'; import { useCallback, useEffect, useState, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useRouter } from "next/navigation"; @@ -1088,6 +1089,15 @@ const paginatedData = useMemo(() => { }, [t]); return ( + + ( + lot.matchStatus !== 'completed' && + lot.lotAvailability !== 'rejected' + )} + > {/* Job Order Header */} @@ -1391,6 +1401,7 @@ const paginatedData = useMemo(() => { /> )} + ); }; diff --git a/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx b/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx new file mode 100644 index 0000000..bab1787 --- /dev/null +++ b/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx @@ -0,0 +1,187 @@ +"use client"; +import { + ReactNode, + createContext, + useCallback, + useContext, + useEffect, + useState, +} from "react"; +import { useQrCodeScannerContext } from "./QrCodeScannerProvider"; + +export interface TestQrCodeContext { + enableTestMode: boolean; + setEnableTestMode: (enabled: boolean) => void; + testScanLotByIndex: (index: number) => void; + testScanAllLots: () => void; + getActiveLots: () => any[]; + onTestScan?: (data: TestScanData) => Promise; +} + +export interface TestScanData { + type: "single" | "all"; + lotIndex?: number; + lots: any[]; +} + +interface TestQrCodeProviderProps { + children: ReactNode; + lotData: any[]; // 当前页面的批次数据 + onScanLot?: (lotNo: string) => Promise; // 扫描单个批次的回调 + filterActive?: (lot: any) => boolean; // 过滤活跃批次的函数 +} + +export const TestQrCodeContext = createContext( + undefined +); + +const TestQrCodeProvider: React.FC = ({ + children, + lotData, + onScanLot, + filterActive, +}) => { + const [enableTestMode, setEnableTestMode] = useState(true); + const { values: qrValues, resetScan } = useQrCodeScannerContext(); + + // 默认的活跃批次过滤器 + const defaultFilterActive = useCallback((lot: any) => { + return ( + lot.lotAvailability !== 'rejected' && + lot.stockOutLineStatus !== 'rejected' && + lot.stockOutLineStatus !== 'completed' && + lot.processingStatus !== 'completed' && + lot.matchStatus !== 'completed' + ); + }, []); + + // 获取活跃批次 + const getActiveLots = useCallback(() => { + const filter = filterActive || defaultFilterActive; + return lotData.filter(filter); + }, [lotData, filterActive, defaultFilterActive]); + + // 测试扫描单个批次 + const testScanLotByIndex = useCallback(async (index: number) => { + const activeLots = getActiveLots(); + + if (index < 1 || index > activeLots.length) { + console.error( + `❌ TEST: Invalid lot index ${index}. Valid range: 1-${activeLots.length}` + ); + return; + } + + const targetLot = activeLots[index - 1]; // 转换为0-based索引 + console.log( + `%c TEST: Scanning lot #${index}/${activeLots.length}: ${targetLot.lotNo}`, + "color: blue; font-weight: bold" + ); + + if (onScanLot) { + await onScanLot(targetLot.lotNo); + } + }, [getActiveLots, onScanLot]); + + // 测试扫描所有批次 + const testScanAllLots = useCallback(async () => { + const activeLots = getActiveLots(); + + if (activeLots.length === 0) { + console.error("❌ TEST: No active lots to scan"); + return; + } + + console.log( + `%c TEST: Scanning ALL ${activeLots.length} lots...`, + "color: orange; font-weight: bold" + ); + + for (let i = 0; i < activeLots.length; i++) { + const lot = activeLots[i]; + console.log( + `%c TEST: Scanning lot ${i + 1}/${activeLots.length}: ${lot.lotNo}`, + "color: blue" + ); + + if (onScanLot) { + await onScanLot(lot.lotNo); + // 添加延迟避免并发冲突 + await new Promise(resolve => setTimeout(resolve, 300)); + } + } + + console.log( + `%c TEST: Completed scanning all ${activeLots.length} lots`, + "color: green; font-weight: bold" + ); + }, [getActiveLots, onScanLot]); + + // 监听 QR 扫描值,处理测试格式 + useEffect(() => { + if (!enableTestMode || qrValues.length === 0) { + return; + } + + const latestQr = qrValues[qrValues.length - 1]; + let processed = false; + + // 处理 {2fitestall} + if (latestQr === "{2fitestall}") { + console.log( + "%c TEST QR: Detected {2fitestall}", + "color: purple; font-weight: bold" + ); + testScanAllLots(); + processed = true; + } + + // 处理 {2fitest[number]} + else if (latestQr.startsWith("{2fitest") && latestQr.endsWith("}")) { + const content = latestQr.substring(8, latestQr.length - 1); + + if (/^\d+$/.test(content)) { + const lotIndex = parseInt(content, 10); + console.log( + `%c TEST QR: Detected {2fitest${lotIndex}}`, + "color: purple; font-weight: bold" + ); + testScanLotByIndex(lotIndex); + processed = true; + } + } + + // 如果处理了测试格式,重置扫描器 + if (processed) { + setTimeout(() => { + resetScan(); + }, 100); + } + }, [qrValues, enableTestMode, testScanAllLots, testScanLotByIndex, resetScan]); + + return ( + + {children} + + ); +}; + +export const useTestQrCode = (): TestQrCodeContext => { + const context = useContext(TestQrCodeContext); + if (!context) { + throw new Error( + "useTestQrCode must be used within a TestQrCodeProvider" + ); + } + return context; +}; + +export default TestQrCodeProvider; \ No newline at end of file