|
|
@@ -75,6 +75,12 @@ export interface LotLabelPrintModalProps { |
|
|
hideScanSection?: boolean; |
|
|
hideScanSection?: boolean; |
|
|
/** 額外提醒(顯示在最上方) */ |
|
|
/** 額外提醒(顯示在最上方) */ |
|
|
reminderText?: string; |
|
|
reminderText?: string; |
|
|
|
|
|
/** 額外標題(顯示在最上方,reminderText 之下) */ |
|
|
|
|
|
statusTitleText?: string; |
|
|
|
|
|
/** 只顯示特定倉位前綴(例如 "2F-") */ |
|
|
|
|
|
warehouseCodePrefixFilter?: string; |
|
|
|
|
|
/** 不顯示觸發視窗的批號(analysis.scanned) */ |
|
|
|
|
|
hideTriggeredLot?: boolean; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function safeParseScanPayload(raw: string): ScanPayload | null { |
|
|
function safeParseScanPayload(raw: string): ScanPayload | null { |
|
|
@@ -115,6 +121,9 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
defaultPrinterName, |
|
|
defaultPrinterName, |
|
|
hideScanSection, |
|
|
hideScanSection, |
|
|
reminderText, |
|
|
reminderText, |
|
|
|
|
|
statusTitleText, |
|
|
|
|
|
warehouseCodePrefixFilter, |
|
|
|
|
|
hideTriggeredLot, |
|
|
}) => { |
|
|
}) => { |
|
|
const scanInputRef = useRef<HTMLInputElement | null>(null); |
|
|
const scanInputRef = useRef<HTMLInputElement | null>(null); |
|
|
const [scanInput, setScanInput] = useState(""); |
|
|
const [scanInput, setScanInput] = useState(""); |
|
|
@@ -126,6 +135,7 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
|
|
|
|
|
|
const [analysisLoading, setAnalysisLoading] = useState(false); |
|
|
const [analysisLoading, setAnalysisLoading] = useState(false); |
|
|
const [analysis, setAnalysis] = useState<QrCodeAnalysisResponse | null>(null); |
|
|
const [analysis, setAnalysis] = useState<QrCodeAnalysisResponse | null>(null); |
|
|
|
|
|
const [lastPayload, setLastPayload] = useState<ScanPayload | null>(null); |
|
|
|
|
|
|
|
|
const [printQty, setPrintQty] = useState(1); |
|
|
const [printQty, setPrintQty] = useState(1); |
|
|
const [printingLotLineId, setPrintingLotLineId] = useState<number | null>( |
|
|
const [printingLotLineId, setPrintingLotLineId] = useState<number | null>( |
|
|
@@ -234,6 +244,7 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setLastPayload(payload); |
|
|
setScanError(null); |
|
|
setScanError(null); |
|
|
setAnalysisLoading(true); |
|
|
setAnalysisLoading(true); |
|
|
try { |
|
|
try { |
|
|
@@ -279,6 +290,19 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
await analyzePayload(payload); |
|
|
await analyzePayload(payload); |
|
|
}, [scanInput, analyzePayload]); |
|
|
}, [scanInput, analyzePayload]); |
|
|
|
|
|
|
|
|
|
|
|
const handleRefreshLots = useCallback(async () => { |
|
|
|
|
|
const payload = lastPayload ?? safeParseScanPayload(scanInput.trim()); |
|
|
|
|
|
if (!payload) { |
|
|
|
|
|
setSnackbar({ |
|
|
|
|
|
open: true, |
|
|
|
|
|
message: "請先掃碼或查詢一次,才可刷新批號清單。", |
|
|
|
|
|
severity: "info", |
|
|
|
|
|
}); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
await analyzePayload(payload); |
|
|
|
|
|
}, [analyzePayload, lastPayload, scanInput]); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
if (!open) return; |
|
|
if (!open) return; |
|
|
if (!initialPayload) return; |
|
|
if (!initialPayload) return; |
|
|
@@ -310,14 +334,22 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
: null; |
|
|
: null; |
|
|
|
|
|
|
|
|
const merged = [ |
|
|
const merged = [ |
|
|
...(scannedLot ? [scannedLot] : []), |
|
|
|
|
|
|
|
|
...(!hideTriggeredLot && scannedLot ? [scannedLot] : []), |
|
|
...list |
|
|
...list |
|
|
.filter((x) => x.inventoryLotLineId !== scannedLotLineId) |
|
|
.filter((x) => x.inventoryLotLineId !== scannedLotLineId) |
|
|
.map((x) => ({ ...x, _scanned: false as const })), |
|
|
.map((x) => ({ ...x, _scanned: false as const })), |
|
|
]; |
|
|
]; |
|
|
|
|
|
|
|
|
return merged; |
|
|
return merged; |
|
|
}, [analysis]); |
|
|
|
|
|
|
|
|
}, [analysis, hideTriggeredLot]); |
|
|
|
|
|
|
|
|
|
|
|
const filteredLots = useMemo(() => { |
|
|
|
|
|
const prefix = String(warehouseCodePrefixFilter ?? "").trim(); |
|
|
|
|
|
if (!prefix) return availableLots; |
|
|
|
|
|
return availableLots.filter((lot) => |
|
|
|
|
|
String(lot.warehouseCode ?? "").startsWith(prefix), |
|
|
|
|
|
); |
|
|
|
|
|
}, [availableLots, warehouseCodePrefixFilter]); |
|
|
|
|
|
|
|
|
const selectedPrinter = useMemo(() => { |
|
|
const selectedPrinter = useMemo(() => { |
|
|
if (selectedPrinterId === "") return null; |
|
|
if (selectedPrinterId === "") return null; |
|
|
@@ -390,6 +422,14 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
<DialogTitle>批號標籤列印</DialogTitle> |
|
|
<DialogTitle>批號標籤列印</DialogTitle> |
|
|
<DialogContent> |
|
|
<DialogContent> |
|
|
<Stack spacing={2} sx={{ mt: 1 }}> |
|
|
<Stack spacing={2} sx={{ mt: 1 }}> |
|
|
|
|
|
{statusTitleText ? ( |
|
|
|
|
|
<Typography |
|
|
|
|
|
variant="h6" |
|
|
|
|
|
sx={{ fontWeight: 800, color: "error.main" }} |
|
|
|
|
|
> |
|
|
|
|
|
{statusTitleText} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
) : null} |
|
|
{reminderText ? ( |
|
|
{reminderText ? ( |
|
|
<Alert severity="warning">{reminderText}</Alert> |
|
|
<Alert severity="warning">{reminderText}</Alert> |
|
|
) : null} |
|
|
) : null} |
|
|
@@ -485,13 +525,13 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
|
|
|
|
|
|
<Button |
|
|
<Button |
|
|
variant="outlined" |
|
|
variant="outlined" |
|
|
onClick={() => void loadPrinters()} |
|
|
|
|
|
disabled={printersLoading} |
|
|
|
|
|
|
|
|
onClick={() => void handleRefreshLots()} |
|
|
|
|
|
disabled={analysisLoading} |
|
|
> |
|
|
> |
|
|
{printersLoading ? ( |
|
|
|
|
|
|
|
|
{analysisLoading ? ( |
|
|
<CircularProgress size={18} /> |
|
|
<CircularProgress size={18} /> |
|
|
) : ( |
|
|
) : ( |
|
|
"重新載入印表機" |
|
|
|
|
|
|
|
|
"刷新批號清單" |
|
|
)} |
|
|
)} |
|
|
</Button> |
|
|
</Button> |
|
|
|
|
|
|
|
|
@@ -512,13 +552,13 @@ const LotLabelPrintModal: React.FC<LotLabelPrintModalProps> = ({ |
|
|
品號:{analysis.itemCode} {analysis.itemName} |
|
|
品號:{analysis.itemCode} {analysis.itemName} |
|
|
</Typography> |
|
|
</Typography> |
|
|
|
|
|
|
|
|
{availableLots.length === 0 ? ( |
|
|
|
|
|
|
|
|
{filteredLots.length === 0 ? ( |
|
|
<Alert severity="warning"> |
|
|
<Alert severity="warning"> |
|
|
找不到可用批號(availableQty > 0)。 |
|
|
|
|
|
|
|
|
找不到該樓層有可用批號(availableQty > 0)。 |
|
|
</Alert> |
|
|
</Alert> |
|
|
) : ( |
|
|
) : ( |
|
|
<Stack spacing={1}> |
|
|
<Stack spacing={1}> |
|
|
{availableLots.map((lot) => { |
|
|
|
|
|
|
|
|
{filteredLots.map((lot) => { |
|
|
const isPrinting = |
|
|
const isPrinting = |
|
|
printingLotLineId === lot.inventoryLotLineId; |
|
|
printingLotLineId === lot.inventoryLotLineId; |
|
|
const loc = String(lot.warehouseCode ?? "").trim(); |
|
|
const loc = String(lot.warehouseCode ?? "").trim(); |
|
|
|