FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

208 lines
5.6 KiB

  1. "use client";
  2. import {
  3. ReactNode,
  4. createContext,
  5. useCallback,
  6. useContext,
  7. useEffect,
  8. useState,
  9. } from "react";
  10. import { useQrCodeScannerContext } from "./QrCodeScannerProvider";
  11. export interface TestQrCodeContext {
  12. enableTestMode: boolean;
  13. setEnableTestMode: (enabled: boolean) => void;
  14. testScanLotByIndex: (index: number) => void;
  15. testScanAllLots: () => void;
  16. getActiveLots: () => any[];
  17. onTestScan?: (data: TestScanData) => Promise<void>;
  18. }
  19. export interface TestScanData {
  20. type: "single" | "all";
  21. lotIndex?: number;
  22. lots: any[];
  23. }
  24. interface TestQrCodeProviderProps {
  25. children: ReactNode;
  26. lotData: any[]; // 当前页面的批次数据
  27. onScanLot?: (lotNo: string) => Promise<void>; // 扫描单个批次的回调
  28. filterActive?: (lot: any) => boolean; // 过滤活跃批次的函数
  29. onBatchScan?: () => Promise<void>;
  30. }
  31. export const TestQrCodeContext = createContext<TestQrCodeContext | undefined>(
  32. undefined
  33. );
  34. const TestQrCodeProvider: React.FC<TestQrCodeProviderProps> = ({
  35. children,
  36. lotData,
  37. onScanLot,
  38. filterActive,
  39. onBatchScan,
  40. }) => {
  41. const [enableTestMode, setEnableTestMode] = useState<boolean>(true);
  42. const { values: qrValues, resetScan } = useQrCodeScannerContext();
  43. // 默认的活跃批次过滤器
  44. const defaultFilterActive = useCallback((lot: any) => {
  45. return (
  46. lot.lotAvailability !== 'rejected' &&
  47. lot.stockOutLineStatus !== 'rejected' &&
  48. lot.stockOutLineStatus !== 'completed' &&
  49. lot.processingStatus !== 'completed' &&
  50. lot.matchStatus !== 'completed' &&
  51. lot.noLot !== true
  52. );
  53. }, []);
  54. // 获取活跃批次
  55. const getActiveLots = useCallback(() => {
  56. const filter = filterActive || defaultFilterActive;
  57. return lotData.filter(filter);
  58. }, [lotData, filterActive, defaultFilterActive]);
  59. // 测试扫描单个批次
  60. const testScanLotByIndex = useCallback(async (index: number) => {
  61. const activeLots = getActiveLots();
  62. if (index < 1 || index > activeLots.length) {
  63. console.error(
  64. `❌ TEST: Invalid lot index ${index}. Valid range: 1-${activeLots.length}`
  65. );
  66. return;
  67. }
  68. const targetLot = activeLots[index - 1]; // 转换为0-based索引
  69. console.log(
  70. `%c TEST: Scanning lot #${index}/${activeLots.length}: ${targetLot.lotNo}`,
  71. "color: blue; font-weight: bold"
  72. );
  73. if (onScanLot) {
  74. await onScanLot(targetLot.lotNo);
  75. }
  76. }, [getActiveLots, onScanLot]);
  77. const testScanAllLots = useCallback(async () => {
  78. const activeLots = getActiveLots();
  79. if (activeLots.length === 0) {
  80. console.error("❌ TEST: No active lots to scan");
  81. return;
  82. }
  83. // ✅ 优化:如果有批量扫描回调,使用它(高效批量处理)
  84. if (onBatchScan) {
  85. console.log(
  86. `%c TEST: Batch scanning ALL ${activeLots.length} lots...`,
  87. "color: orange; font-weight: bold"
  88. );
  89. try {
  90. await onBatchScan();
  91. console.log(
  92. `%c TEST: Completed batch scan for all ${activeLots.length} lots`,
  93. "color: green; font-weight: bold"
  94. );
  95. } catch (error) {
  96. console.error("❌ TEST: Error in batch scan:", error);
  97. }
  98. return;
  99. }
  100. // 回退到原来的逐个扫描方式(如果没有提供批量回调)
  101. console.log(
  102. `%c TEST: Scanning ALL ${activeLots.length} lots (one by one)...`,
  103. "color: orange; font-weight: bold"
  104. );
  105. for (let i = 0; i < activeLots.length; i++) {
  106. const lot = activeLots[i];
  107. console.log(
  108. `%c TEST: Scanning lot ${i + 1}/${activeLots.length}: ${lot.lotNo}`,
  109. "color: blue"
  110. );
  111. if (onScanLot) {
  112. await onScanLot(lot.lotNo);
  113. // 添加延迟避免并发冲突
  114. await new Promise(resolve => setTimeout(resolve, 300));
  115. }
  116. }
  117. console.log(
  118. `%c TEST: Completed scanning all ${activeLots.length} lots`,
  119. "color: green; font-weight: bold"
  120. );
  121. }, [getActiveLots, onScanLot, onBatchScan]);
  122. // 监听 QR 扫描值,处理测试格式
  123. useEffect(() => {
  124. if (!enableTestMode || qrValues.length === 0) {
  125. return;
  126. }
  127. const latestQr = qrValues[qrValues.length - 1];
  128. let processed = false;
  129. // 处理 {2fitestall}
  130. if (latestQr === "{2fitestall}") {
  131. console.log(
  132. "%c TEST QR: Detected {2fitestall}",
  133. "color: purple; font-weight: bold"
  134. );
  135. testScanAllLots();
  136. processed = true;
  137. }
  138. // 处理 {2fitest[number]}
  139. else if (latestQr.startsWith("{2fitest") && latestQr.endsWith("}")) {
  140. const content = latestQr.substring(8, latestQr.length - 1);
  141. if (/^\d+$/.test(content)) {
  142. const lotIndex = parseInt(content, 10);
  143. console.log(
  144. `%c TEST QR: Detected {2fitest${lotIndex}}`,
  145. "color: purple; font-weight: bold"
  146. );
  147. testScanLotByIndex(lotIndex);
  148. processed = true;
  149. }
  150. }
  151. // 如果处理了测试格式,重置扫描器
  152. if (processed) {
  153. setTimeout(() => {
  154. resetScan();
  155. }, 100);
  156. }
  157. }, [qrValues, enableTestMode, testScanAllLots, testScanLotByIndex, resetScan]);
  158. return (
  159. <TestQrCodeContext.Provider
  160. value={{
  161. enableTestMode,
  162. setEnableTestMode,
  163. testScanLotByIndex,
  164. testScanAllLots,
  165. getActiveLots,
  166. }}
  167. >
  168. {children}
  169. </TestQrCodeContext.Provider>
  170. );
  171. };
  172. export const useTestQrCode = (): TestQrCodeContext => {
  173. const context = useContext(TestQrCodeContext);
  174. if (!context) {
  175. throw new Error(
  176. "useTestQrCode must be used within a TestQrCodeProvider"
  177. );
  178. }
  179. return context;
  180. };
  181. export default TestQrCodeProvider;