| @@ -31,3 +31,29 @@ export const exportEquipmentQrCode = async (equipmentDetailIds: number[]): Promi | |||||
| return { blobValue, filename }; | return { blobValue, filename }; | ||||
| }; | }; | ||||
| export interface PrintEquipmentQrCodeRequest { | |||||
| equipmentDetailIds: number[]; | |||||
| printerId: number; | |||||
| printQty?: number; | |||||
| } | |||||
| export const printEquipmentQrCode = async (data: PrintEquipmentQrCodeRequest): Promise<void> => { | |||||
| const token = localStorage.getItem("accessToken"); | |||||
| const response = await fetch(`${NEXT_PUBLIC_API_URL}/Equipment/print-qrcode`, { | |||||
| method: "POST", | |||||
| headers: { | |||||
| "Content-Type": "application/json", | |||||
| ...(token && { Authorization: `Bearer ${token}` }), | |||||
| }, | |||||
| body: JSON.stringify(data), | |||||
| }); | |||||
| if (!response.ok) { | |||||
| if (response.status === 401) { | |||||
| throw new Error("Unauthorized: Please log in again"); | |||||
| } | |||||
| throw new Error(`Failed to print QR code: ${response.status} ${response.statusText}`); | |||||
| } | |||||
| }; | |||||
| @@ -32,6 +32,32 @@ export const exportUserQrCode = async (userIds: number[]): Promise<{ blobValue: | |||||
| return { blobValue, filename }; | return { blobValue, filename }; | ||||
| }; | }; | ||||
| export interface PrintUserQrCodeRequest { | |||||
| userIds: number[]; | |||||
| printerId: number; | |||||
| printQty?: number; | |||||
| } | |||||
| export const printUserQrCode = async (data: PrintUserQrCodeRequest): Promise<void> => { | |||||
| const token = localStorage.getItem("accessToken"); | |||||
| const response = await fetch(`${NEXT_PUBLIC_API_URL}/user/print-qrcode`, { | |||||
| method: "POST", | |||||
| headers: { | |||||
| "Content-Type": "application/json", | |||||
| ...(token && { Authorization: `Bearer ${token}` }), | |||||
| }, | |||||
| body: JSON.stringify(data), | |||||
| }); | |||||
| if (!response.ok) { | |||||
| if (response.status === 401) { | |||||
| throw new Error("Unauthorized: Please log in again"); | |||||
| } | |||||
| throw new Error(`Failed to print QR code: ${response.status} ${response.statusText}`); | |||||
| } | |||||
| }; | |||||
| export const searchUsersByUsernameOrName = async (searchTerm: string): Promise<UserResult[]> => { | export const searchUsersByUsernameOrName = async (searchTerm: string): Promise<UserResult[]> => { | ||||
| if (!searchTerm.trim()) { | if (!searchTerm.trim()) { | ||||
| return []; | return []; | ||||
| @@ -32,6 +32,32 @@ export const exportWarehouseQrCode = async (warehouseIds: number[]): Promise<{ b | |||||
| return { blobValue, filename }; | return { blobValue, filename }; | ||||
| }; | }; | ||||
| export interface PrintWarehouseQrCodeRequest { | |||||
| warehouseIds: number[]; | |||||
| printerId: number; | |||||
| printQty?: number; | |||||
| } | |||||
| export const printWarehouseQrCode = async (data: PrintWarehouseQrCodeRequest): Promise<void> => { | |||||
| const token = localStorage.getItem("accessToken"); | |||||
| const response = await fetch(`${NEXT_PUBLIC_API_URL}/warehouse/print-qrcode`, { | |||||
| method: "POST", | |||||
| headers: { | |||||
| "Content-Type": "application/json", | |||||
| ...(token && { Authorization: `Bearer ${token}` }), | |||||
| }, | |||||
| body: JSON.stringify(data), | |||||
| }); | |||||
| if (!response.ok) { | |||||
| if (response.status === 401) { | |||||
| throw new Error("Unauthorized: Please log in again"); | |||||
| } | |||||
| throw new Error(`Failed to print QR code: ${response.status} ${response.statusText}`); | |||||
| } | |||||
| }; | |||||
| export const fetchWarehouseListClient = async (): Promise<WarehouseResult[]> => { | export const fetchWarehouseListClient = async (): Promise<WarehouseResult[]> => { | ||||
| const token = localStorage.getItem("accessToken"); | const token = localStorage.getItem("accessToken"); | ||||
| @@ -9,7 +9,7 @@ import { successDialog } from "../Swal/CustomAlerts"; | |||||
| import useUploadContext from "../UploadProvider/useUploadContext"; | import useUploadContext from "../UploadProvider/useUploadContext"; | ||||
| import { downloadFile } from "@/app/utils/commonUtil"; | import { downloadFile } from "@/app/utils/commonUtil"; | ||||
| import { EquipmentDetailResult } from "@/app/api/settings/equipmentDetail"; | import { EquipmentDetailResult } from "@/app/api/settings/equipmentDetail"; | ||||
| import { exportEquipmentQrCode } from "@/app/api/settings/equipmentDetail/client"; | |||||
| import { exportEquipmentQrCode, printEquipmentQrCode } from "@/app/api/settings/equipmentDetail/client"; | |||||
| import { | import { | ||||
| Checkbox, | Checkbox, | ||||
| Box, | Box, | ||||
| @@ -276,31 +276,17 @@ const QrCodeHandleEquipmentSearch: React.FC<Props> = ({ equipmentDetails, printe | |||||
| }, [setIsUploading, t]); | }, [setIsUploading, t]); | ||||
| const handlePrint = useCallback(async () => { | const handlePrint = useCallback(async () => { | ||||
| if (checkboxIds.length === 0) { | |||||
| if (checkboxIds.length === 0 || !selectedPrinter) { | |||||
| return; | return; | ||||
| } | } | ||||
| try { | try { | ||||
| setIsUploading(true); | setIsUploading(true); | ||||
| const numericIds = checkboxIds.map(id => typeof id === 'string' ? parseInt(id) : id); | const numericIds = checkboxIds.map(id => typeof id === 'string' ? parseInt(id) : id); | ||||
| const response = await exportEquipmentQrCode(numericIds); | |||||
| const blob = new Blob([new Uint8Array(response.blobValue)], { type: "application/pdf" }); | |||||
| const url = URL.createObjectURL(blob); | |||||
| const printWindow = window.open(url, '_blank'); | |||||
| if (printWindow) { | |||||
| printWindow.onload = () => { | |||||
| for (let i = 0; i < printQty; i++) { | |||||
| setTimeout(() => { | |||||
| printWindow.print(); | |||||
| }, i * 500); | |||||
| } | |||||
| }; | |||||
| } | |||||
| setTimeout(() => { | |||||
| URL.revokeObjectURL(url); | |||||
| }, 1000); | |||||
| await printEquipmentQrCode({ | |||||
| equipmentDetailIds: numericIds, | |||||
| printerId: selectedPrinter.id, | |||||
| printQty, | |||||
| }); | |||||
| setSelectedEquipmentDetailsModalOpen(false); | setSelectedEquipmentDetailsModalOpen(false); | ||||
| successDialog("二維碼已列印", t); | successDialog("二維碼已列印", t); | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -308,7 +294,7 @@ const QrCodeHandleEquipmentSearch: React.FC<Props> = ({ equipmentDetails, printe | |||||
| } finally { | } finally { | ||||
| setIsUploading(false); | setIsUploading(false); | ||||
| } | } | ||||
| }, [checkboxIds, printQty, setIsUploading, t]); | |||||
| }, [checkboxIds, printQty, selectedPrinter, setIsUploading, t]); | |||||
| const handleViewSelectedQrCodes = useCallback(() => { | const handleViewSelectedQrCodes = useCallback(() => { | ||||
| if (checkboxIds.length === 0) { | if (checkboxIds.length === 0) { | ||||
| @@ -530,7 +516,7 @@ const QrCodeHandleEquipmentSearch: React.FC<Props> = ({ equipmentDetails, printe | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<PrintIcon />} | startIcon={<PrintIcon />} | ||||
| onClick={handlePrint} | onClick={handlePrint} | ||||
| disabled={checkboxIds.length === 0 || filteredPrinters.length === 0} | |||||
| disabled={checkboxIds.length === 0 || filteredPrinters.length === 0 || !selectedPrinter} | |||||
| color="primary" | color="primary" | ||||
| > | > | ||||
| 列印 | 列印 | ||||
| @@ -12,7 +12,7 @@ import { downloadFile } from "@/app/utils/commonUtil"; | |||||
| import { UserResult } from "@/app/api/user"; | import { UserResult } from "@/app/api/user"; | ||||
| import { deleteUser } from "@/app/api/user/actions"; | import { deleteUser } from "@/app/api/user/actions"; | ||||
| import QrCodeIcon from "@mui/icons-material/QrCode"; | import QrCodeIcon from "@mui/icons-material/QrCode"; | ||||
| import { exportUserQrCode, searchUsersByUsernameOrName } from "@/app/api/user/client"; | |||||
| import { exportUserQrCode, printUserQrCode, searchUsersByUsernameOrName } from "@/app/api/user/client"; | |||||
| import { | import { | ||||
| Checkbox, | Checkbox, | ||||
| Box, | Box, | ||||
| @@ -171,30 +171,16 @@ const QrCodeHandleSearch: React.FC<Props> = ({ users, printerCombo }) => { | |||||
| }, [setIsUploading, t]); | }, [setIsUploading, t]); | ||||
| const handlePrint = useCallback(async () => { | const handlePrint = useCallback(async () => { | ||||
| if (checkboxIds.length === 0) { | |||||
| if (checkboxIds.length === 0 || !selectedPrinter) { | |||||
| return; | return; | ||||
| } | } | ||||
| try { | try { | ||||
| setIsUploading(true); | setIsUploading(true); | ||||
| const response = await exportUserQrCode(checkboxIds); | |||||
| const blob = new Blob([new Uint8Array(response.blobValue)], { type: "application/pdf" }); | |||||
| const url = URL.createObjectURL(blob); | |||||
| const printWindow = window.open(url, '_blank'); | |||||
| if (printWindow) { | |||||
| printWindow.onload = () => { | |||||
| for (let i = 0; i < printQty; i++) { | |||||
| setTimeout(() => { | |||||
| printWindow.print(); | |||||
| }, i * 500); | |||||
| } | |||||
| }; | |||||
| } | |||||
| setTimeout(() => { | |||||
| URL.revokeObjectURL(url); | |||||
| }, 1000); | |||||
| await printUserQrCode({ | |||||
| userIds: checkboxIds, | |||||
| printerId: selectedPrinter.id, | |||||
| printQty, | |||||
| }); | |||||
| setSelectedUsersModalOpen(false); | setSelectedUsersModalOpen(false); | ||||
| successDialog("二維碼已列印", t); | successDialog("二維碼已列印", t); | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -202,7 +188,7 @@ const QrCodeHandleSearch: React.FC<Props> = ({ users, printerCombo }) => { | |||||
| } finally { | } finally { | ||||
| setIsUploading(false); | setIsUploading(false); | ||||
| } | } | ||||
| }, [checkboxIds, printQty, setIsUploading, t]); | |||||
| }, [checkboxIds, printQty, selectedPrinter, setIsUploading, t]); | |||||
| const handleViewSelectedQrCodes = useCallback(() => { | const handleViewSelectedQrCodes = useCallback(() => { | ||||
| if (checkboxIds.length === 0) { | if (checkboxIds.length === 0) { | ||||
| @@ -451,7 +437,7 @@ const QrCodeHandleSearch: React.FC<Props> = ({ users, printerCombo }) => { | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<PrintIcon />} | startIcon={<PrintIcon />} | ||||
| onClick={handlePrint} | onClick={handlePrint} | ||||
| disabled={checkboxIds.length === 0 || filteredPrinters.length === 0} | |||||
| disabled={checkboxIds.length === 0 || filteredPrinters.length === 0 || !selectedPrinter} | |||||
| color="primary" | color="primary" | ||||
| > | > | ||||
| 列印 | 列印 | ||||
| @@ -7,7 +7,7 @@ import { successDialog } from "../Swal/CustomAlerts"; | |||||
| import useUploadContext from "../UploadProvider/useUploadContext"; | import useUploadContext from "../UploadProvider/useUploadContext"; | ||||
| import { downloadFile } from "@/app/utils/commonUtil"; | import { downloadFile } from "@/app/utils/commonUtil"; | ||||
| import { WarehouseResult } from "@/app/api/warehouse"; | import { WarehouseResult } from "@/app/api/warehouse"; | ||||
| import { exportWarehouseQrCode } from "@/app/api/warehouse/client"; | |||||
| import { exportWarehouseQrCode, printWarehouseQrCode } from "@/app/api/warehouse/client"; | |||||
| import { | import { | ||||
| Checkbox, | Checkbox, | ||||
| Box, | Box, | ||||
| @@ -254,30 +254,16 @@ const QrCodeHandleWarehouseSearch: React.FC<Props> = ({ warehouses, printerCombo | |||||
| }, [setIsUploading, t]); | }, [setIsUploading, t]); | ||||
| const handlePrint = useCallback(async () => { | const handlePrint = useCallback(async () => { | ||||
| if (checkboxIds.length === 0) { | |||||
| if (checkboxIds.length === 0 || !selectedPrinter) { | |||||
| return; | return; | ||||
| } | } | ||||
| try { | try { | ||||
| setIsUploading(true); | setIsUploading(true); | ||||
| const response = await exportWarehouseQrCode(checkboxIds); | |||||
| const blob = new Blob([new Uint8Array(response.blobValue)], { type: "application/pdf" }); | |||||
| const url = URL.createObjectURL(blob); | |||||
| const printWindow = window.open(url, '_blank'); | |||||
| if (printWindow) { | |||||
| printWindow.onload = () => { | |||||
| for (let i = 0; i < printQty; i++) { | |||||
| setTimeout(() => { | |||||
| printWindow.print(); | |||||
| }, i * 500); | |||||
| } | |||||
| }; | |||||
| } | |||||
| setTimeout(() => { | |||||
| URL.revokeObjectURL(url); | |||||
| }, 1000); | |||||
| await printWarehouseQrCode({ | |||||
| warehouseIds: checkboxIds, | |||||
| printerId: selectedPrinter.id, | |||||
| printQty, | |||||
| }); | |||||
| setSelectedWarehousesModalOpen(false); | setSelectedWarehousesModalOpen(false); | ||||
| successDialog("二維碼已列印", t); | successDialog("二維碼已列印", t); | ||||
| } catch (error) { | } catch (error) { | ||||
| @@ -285,7 +271,7 @@ const QrCodeHandleWarehouseSearch: React.FC<Props> = ({ warehouses, printerCombo | |||||
| } finally { | } finally { | ||||
| setIsUploading(false); | setIsUploading(false); | ||||
| } | } | ||||
| }, [checkboxIds, printQty, setIsUploading, t]); | |||||
| }, [checkboxIds, printQty, selectedPrinter, setIsUploading, t]); | |||||
| const handleViewSelectedQrCodes = useCallback(() => { | const handleViewSelectedQrCodes = useCallback(() => { | ||||
| if (checkboxIds.length === 0) { | if (checkboxIds.length === 0) { | ||||
| @@ -595,7 +581,7 @@ const QrCodeHandleWarehouseSearch: React.FC<Props> = ({ warehouses, printerCombo | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<PrintIcon />} | startIcon={<PrintIcon />} | ||||
| onClick={handlePrint} | onClick={handlePrint} | ||||
| disabled={checkboxIds.length === 0 || filteredPrinters.length === 0} | |||||
| disabled={checkboxIds.length === 0 || filteredPrinters.length === 0 || !selectedPrinter} | |||||
| color="primary" | color="primary" | ||||
| > | > | ||||
| 列印 | 列印 | ||||