|
|
@@ -1,6 +1,6 @@ |
|
|
"use client"; |
|
|
"use client"; |
|
|
|
|
|
|
|
|
import React, { useCallback, useEffect, useState } from "react"; |
|
|
|
|
|
|
|
|
import React, { useCallback, useEffect, useRef, useState } from "react"; |
|
|
import { |
|
|
import { |
|
|
Alert, |
|
|
Alert, |
|
|
Box, |
|
|
Box, |
|
|
@@ -26,6 +26,7 @@ import { |
|
|
type LaserLastReceiveSuccess, |
|
|
type LaserLastReceiveSuccess, |
|
|
JobOrderListItem, |
|
|
JobOrderListItem, |
|
|
patchSetting, |
|
|
patchSetting, |
|
|
|
|
|
expiryDateForLaserSend, |
|
|
sendLaserBag2Job, |
|
|
sendLaserBag2Job, |
|
|
} from "@/app/api/laserPrint/actions"; |
|
|
} from "@/app/api/laserPrint/actions"; |
|
|
import dayjs from "dayjs"; |
|
|
import dayjs from "dayjs"; |
|
|
@@ -89,6 +90,7 @@ const LaserPrintSearch: React.FC = () => { |
|
|
const [settingsLoaded, setSettingsLoaded] = useState(false); |
|
|
const [settingsLoaded, setSettingsLoaded] = useState(false); |
|
|
const [printerConnected, setPrinterConnected] = useState(false); |
|
|
const [printerConnected, setPrinterConnected] = useState(false); |
|
|
const [printerMessage, setPrinterMessage] = useState("檸檬機(激光機)未連接"); |
|
|
const [printerMessage, setPrinterMessage] = useState("檸檬機(激光機)未連接"); |
|
|
|
|
|
const sendInFlightRef = useRef(false); |
|
|
|
|
|
|
|
|
const loadSystemSettings = useCallback(async () => { |
|
|
const loadSystemSettings = useCallback(async () => { |
|
|
try { |
|
|
try { |
|
|
@@ -191,32 +193,38 @@ const LaserPrintSearch: React.FC = () => { |
|
|
jobOrderId: jo.id, |
|
|
jobOrderId: jo.id, |
|
|
jobOrderNo: jo.code, |
|
|
jobOrderNo: jo.code, |
|
|
lotNo: jo.lotNo, |
|
|
lotNo: jo.lotNo, |
|
|
expiryDate: jo.expiryDate ?? null, |
|
|
|
|
|
|
|
|
expiryDate: expiryDateForLaserSend(jo.expiryDate), |
|
|
source: "MANUAL", |
|
|
source: "MANUAL", |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const handleRowClick = async (jo: JobOrderListItem) => { |
|
|
const handleRowClick = async (jo: JobOrderListItem) => { |
|
|
if (sendingJobId !== null) return; |
|
|
|
|
|
|
|
|
if (sendInFlightRef.current || sendingJobId !== null) return; |
|
|
|
|
|
|
|
|
if (!laserHost.trim()) { |
|
|
if (!laserHost.trim()) { |
|
|
setErrorSnackbar({ open: true, message: "請在系統設定中填寫檸檬機(激光機) IP。" }); |
|
|
setErrorSnackbar({ open: true, message: "請在系統設定中填寫檸檬機(激光機) IP。" }); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
sendInFlightRef.current = true; |
|
|
setSelectedId(jo.id); |
|
|
setSelectedId(jo.id); |
|
|
setSendingJobId(jo.id); |
|
|
setSendingJobId(jo.id); |
|
|
try { |
|
|
try { |
|
|
let lastAck: string | undefined; |
|
|
let lastAck: string | undefined; |
|
|
let anyReceiveAck = false; |
|
|
let anyReceiveAck = false; |
|
|
|
|
|
let sentOk = 0; |
|
|
|
|
|
let laterFail: string | null = null; |
|
|
for (let i = 0; i < LASER_SEND_COUNT; i++) { |
|
|
for (let i = 0; i < LASER_SEND_COUNT; i++) { |
|
|
const r = await sendOne(jo); |
|
|
const r = await sendOne(jo); |
|
|
if (!r.success) { |
|
|
if (!r.success) { |
|
|
setErrorSnackbar({ |
|
|
|
|
|
open: true, |
|
|
|
|
|
message: r.message || "檸檬機(激光機)未收到指令", |
|
|
|
|
|
}); |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
const failMsg = r.message?.trim() || `第 ${i + 1} 次送出失敗`; |
|
|
|
|
|
if (sentOk === 0) { |
|
|
|
|
|
setErrorSnackbar({ open: true, message: failMsg }); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
laterFail = failMsg; |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
sentOk += 1; |
|
|
if (r.printerAck) lastAck = r.printerAck; |
|
|
if (r.printerAck) lastAck = r.printerAck; |
|
|
if (r.receiveAcknowledged) anyReceiveAck = true; |
|
|
if (r.receiveAcknowledged) anyReceiveAck = true; |
|
|
if (i < LASER_SEND_COUNT - 1) { |
|
|
if (i < LASER_SEND_COUNT - 1) { |
|
|
@@ -229,7 +237,11 @@ const LaserPrintSearch: React.FC = () => { |
|
|
: lastAck |
|
|
: lastAck |
|
|
? `(最後回覆:${lastAck})` |
|
|
? `(最後回覆:${lastAck})` |
|
|
: ""; |
|
|
: ""; |
|
|
setSuccessSignal(`已送出 ${LASER_SEND_COUNT} 次至檸檬機(激光機)${ackHint}`); |
|
|
|
|
|
|
|
|
setSuccessSignal( |
|
|
|
|
|
laterFail |
|
|
|
|
|
? `已送出 ${sentOk} 次至檸檬機(激光機)${ackHint}(後續重送失敗:${laterFail})` |
|
|
|
|
|
: `已送出 ${sentOk} 次至檸檬機(激光機)${ackHint}`, |
|
|
|
|
|
); |
|
|
await loadSystemSettings(); |
|
|
await loadSystemSettings(); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
setErrorSnackbar({ |
|
|
setErrorSnackbar({ |
|
|
@@ -238,6 +250,7 @@ const LaserPrintSearch: React.FC = () => { |
|
|
}); |
|
|
}); |
|
|
} finally { |
|
|
} finally { |
|
|
setSendingJobId(null); |
|
|
setSendingJobId(null); |
|
|
|
|
|
sendInFlightRef.current = false; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
@@ -269,7 +282,12 @@ const LaserPrintSearch: React.FC = () => { |
|
|
{settingsLoaded && lastLaserReceive && ( |
|
|
{settingsLoaded && lastLaserReceive && ( |
|
|
<Alert severity="info" sx={{ mb: 2 }}> |
|
|
<Alert severity="info" sx={{ mb: 2 }}> |
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}> |
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}> |
|
|
上次印表機已確認工單:{lastLaserReceive.jobOrderNo ?? "—"} {formatHongKongDateTime(lastLaserReceive.sentAt)} |
|
|
|
|
|
|
|
|
上次印表機已確認工單:{lastLaserReceive.jobOrderNo ?? "—"} |
|
|
|
|
|
{formatHongKongDateTime(lastLaserReceive.sentAt)} |
|
|
|
|
|
{lastLaserReceive.source ? ` (${lastLaserReceive.source === "AUTO" ? "自動送出" : "手動點選"})` : ""} |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Typography variant="body2" sx={{ mt: 0.5 }}> |
|
|
|
|
|
此時間只會在檸檬機回覆 receive 時更新。之後送出失敗不會改這裡。 |
|
|
</Typography> |
|
|
</Typography> |
|
|
</Alert> |
|
|
</Alert> |
|
|
)} |
|
|
)} |
|
|
|