|
|
|
@@ -0,0 +1,675 @@ |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import { useCallback, useMemo, useState, useEffect } from "react"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import SearchResults, { Column } from "../SearchResults"; |
|
|
|
import { successDialog } from "../Swal/CustomAlerts"; |
|
|
|
import useUploadContext from "../UploadProvider/useUploadContext"; |
|
|
|
import { downloadFile } from "@/app/utils/commonUtil"; |
|
|
|
import { WarehouseResult } from "@/app/api/warehouse"; |
|
|
|
import { exportWarehouseQrCode } from "@/app/api/warehouse/client"; |
|
|
|
import { |
|
|
|
Checkbox, |
|
|
|
Box, |
|
|
|
Button, |
|
|
|
TextField, |
|
|
|
Stack, |
|
|
|
Autocomplete, |
|
|
|
Modal, |
|
|
|
Card, |
|
|
|
CardContent, |
|
|
|
CardActions, |
|
|
|
IconButton, |
|
|
|
Table, |
|
|
|
TableBody, |
|
|
|
TableCell, |
|
|
|
TableContainer, |
|
|
|
TableHead, |
|
|
|
TableRow, |
|
|
|
Paper, |
|
|
|
Typography, |
|
|
|
InputAdornment |
|
|
|
} from "@mui/material"; |
|
|
|
import DownloadIcon from "@mui/icons-material/Download"; |
|
|
|
import PrintIcon from "@mui/icons-material/Print"; |
|
|
|
import CloseIcon from "@mui/icons-material/Close"; |
|
|
|
import RestartAlt from "@mui/icons-material/RestartAlt"; |
|
|
|
import Search from "@mui/icons-material/Search"; |
|
|
|
import { PrinterCombo } from "@/app/api/settings/printer"; |
|
|
|
|
|
|
|
interface Props { |
|
|
|
warehouses: WarehouseResult[]; |
|
|
|
printerCombo: PrinterCombo[]; |
|
|
|
} |
|
|
|
|
|
|
|
const QrCodeHandleWarehouseSearch: React.FC<Props> = ({ warehouses, printerCombo }) => { |
|
|
|
const { t } = useTranslation(["warehouse", "common"]); |
|
|
|
const [filteredWarehouses, setFilteredWarehouses] = useState(warehouses); |
|
|
|
const { setIsUploading } = useUploadContext(); |
|
|
|
const [pagingController, setPagingController] = useState({ |
|
|
|
pageNum: 1, |
|
|
|
pageSize: 10, |
|
|
|
}); |
|
|
|
|
|
|
|
const [checkboxIds, setCheckboxIds] = useState<number[]>([]); |
|
|
|
const [selectAll, setSelectAll] = useState(false); |
|
|
|
const [printQty, setPrintQty] = useState(1); |
|
|
|
const [isSearching, setIsSearching] = useState(false); |
|
|
|
|
|
|
|
const [previewOpen, setPreviewOpen] = useState(false); |
|
|
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null); |
|
|
|
|
|
|
|
const [selectedWarehousesModalOpen, setSelectedWarehousesModalOpen] = useState(false); |
|
|
|
|
|
|
|
const [searchInputs, setSearchInputs] = useState({ |
|
|
|
store_id: "", |
|
|
|
warehouse: "", |
|
|
|
area: "", |
|
|
|
slot: "", |
|
|
|
}); |
|
|
|
|
|
|
|
const filteredPrinters = useMemo(() => { |
|
|
|
return printerCombo.filter((printer) => { |
|
|
|
return printer.type === "A4"; |
|
|
|
}); |
|
|
|
}, [printerCombo]); |
|
|
|
|
|
|
|
const [selectedPrinter, setSelectedPrinter] = useState<PrinterCombo | undefined>( |
|
|
|
filteredPrinters.length > 0 ? filteredPrinters[0] : undefined |
|
|
|
); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (!selectedPrinter || !filteredPrinters.find(p => p.id === selectedPrinter.id)) { |
|
|
|
setSelectedPrinter(filteredPrinters.length > 0 ? filteredPrinters[0] : undefined); |
|
|
|
} |
|
|
|
}, [filteredPrinters, selectedPrinter]); |
|
|
|
|
|
|
|
const handleReset = useCallback(() => { |
|
|
|
setSearchInputs({ |
|
|
|
store_id: "", |
|
|
|
warehouse: "", |
|
|
|
area: "", |
|
|
|
slot: "", |
|
|
|
}); |
|
|
|
setFilteredWarehouses(warehouses); |
|
|
|
setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); |
|
|
|
}, [warehouses, pagingController.pageSize]); |
|
|
|
|
|
|
|
const handleSearch = useCallback(() => { |
|
|
|
setIsSearching(true); |
|
|
|
try { |
|
|
|
let results: WarehouseResult[] = warehouses; |
|
|
|
|
|
|
|
const storeId = searchInputs.store_id?.trim() || ""; |
|
|
|
const warehouse = searchInputs.warehouse?.trim() || ""; |
|
|
|
const area = searchInputs.area?.trim() || ""; |
|
|
|
const slot = searchInputs.slot?.trim() || ""; |
|
|
|
|
|
|
|
if (storeId || warehouse || area || slot) { |
|
|
|
results = warehouses.filter((warehouseItem) => { |
|
|
|
if (storeId || warehouse || area || slot) { |
|
|
|
if (!warehouseItem.code) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
const codeValue = String(warehouseItem.code).toLowerCase(); |
|
|
|
|
|
|
|
const codeParts = codeValue.split("-"); |
|
|
|
|
|
|
|
if (codeParts.length >= 4) { |
|
|
|
const codeStoreId = codeParts[0] || ""; |
|
|
|
const codeWarehouse = codeParts[1] || ""; |
|
|
|
const codeArea = codeParts[2] || ""; |
|
|
|
const codeSlot = codeParts[3] || ""; |
|
|
|
|
|
|
|
const storeIdMatch = !storeId || codeStoreId.includes(storeId.toLowerCase()); |
|
|
|
const warehouseMatch = !warehouse || codeWarehouse.includes(warehouse.toLowerCase()); |
|
|
|
const areaMatch = !area || codeArea.includes(area.toLowerCase()); |
|
|
|
const slotMatch = !slot || codeSlot.includes(slot.toLowerCase()); |
|
|
|
|
|
|
|
return storeIdMatch && warehouseMatch && areaMatch && slotMatch; |
|
|
|
} |
|
|
|
|
|
|
|
const storeIdMatch = !storeId || codeValue.includes(storeId.toLowerCase()); |
|
|
|
const warehouseMatch = !warehouse || codeValue.includes(warehouse.toLowerCase()); |
|
|
|
const areaMatch = !area || codeValue.includes(area.toLowerCase()); |
|
|
|
const slotMatch = !slot || codeValue.includes(slot.toLowerCase()); |
|
|
|
|
|
|
|
return storeIdMatch && warehouseMatch && areaMatch && slotMatch; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
}); |
|
|
|
} else { |
|
|
|
results = warehouses; |
|
|
|
} |
|
|
|
|
|
|
|
setFilteredWarehouses(results); |
|
|
|
setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error searching warehouses:", error); |
|
|
|
const storeId = searchInputs.store_id?.trim().toLowerCase() || ""; |
|
|
|
const warehouse = searchInputs.warehouse?.trim().toLowerCase() || ""; |
|
|
|
const area = searchInputs.area?.trim().toLowerCase() || ""; |
|
|
|
const slot = searchInputs.slot?.trim().toLowerCase() || ""; |
|
|
|
|
|
|
|
setFilteredWarehouses( |
|
|
|
warehouses.filter((warehouseItem) => { |
|
|
|
if (storeId || warehouse || area || slot) { |
|
|
|
if (!warehouseItem.code) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
const codeValue = String(warehouseItem.code).toLowerCase(); |
|
|
|
const codeParts = codeValue.split("-"); |
|
|
|
|
|
|
|
if (codeParts.length >= 4) { |
|
|
|
const storeIdMatch = !storeId || codeParts[0].includes(storeId); |
|
|
|
const warehouseMatch = !warehouse || codeParts[1].includes(warehouse); |
|
|
|
const areaMatch = !area || codeParts[2].includes(area); |
|
|
|
const slotMatch = !slot || codeParts[3].includes(slot); |
|
|
|
return storeIdMatch && warehouseMatch && areaMatch && slotMatch; |
|
|
|
} |
|
|
|
|
|
|
|
return (!storeId || codeValue.includes(storeId)) && |
|
|
|
(!warehouse || codeValue.includes(warehouse)) && |
|
|
|
(!area || codeValue.includes(area)) && |
|
|
|
(!slot || codeValue.includes(slot)); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
}) |
|
|
|
); |
|
|
|
} finally { |
|
|
|
setIsSearching(false); |
|
|
|
} |
|
|
|
}, [searchInputs, warehouses, pagingController.pageSize]); |
|
|
|
|
|
|
|
const handleSelectWarehouse = useCallback((warehouseId: number, checked: boolean) => { |
|
|
|
if (checked) { |
|
|
|
setCheckboxIds(prev => [...prev, warehouseId]); |
|
|
|
} else { |
|
|
|
setCheckboxIds(prev => prev.filter(id => id !== warehouseId)); |
|
|
|
setSelectAll(false); |
|
|
|
} |
|
|
|
}, []); |
|
|
|
|
|
|
|
const handleSelectAll = useCallback((checked: boolean) => { |
|
|
|
if (checked) { |
|
|
|
setCheckboxIds(filteredWarehouses.map(warehouse => warehouse.id)); |
|
|
|
setSelectAll(true); |
|
|
|
} else { |
|
|
|
setCheckboxIds([]); |
|
|
|
setSelectAll(false); |
|
|
|
} |
|
|
|
}, [filteredWarehouses]); |
|
|
|
|
|
|
|
const showPdfPreview = useCallback(async (warehouseIds: number[]) => { |
|
|
|
if (warehouseIds.length === 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
try { |
|
|
|
setIsUploading(true); |
|
|
|
const response = await exportWarehouseQrCode(warehouseIds); |
|
|
|
|
|
|
|
const blob = new Blob([new Uint8Array(response.blobValue)], { type: "application/pdf" }); |
|
|
|
const url = URL.createObjectURL(blob); |
|
|
|
|
|
|
|
setPreviewUrl(`${url}#toolbar=0`); |
|
|
|
setPreviewOpen(true); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error exporting QR code:", error); |
|
|
|
} finally { |
|
|
|
setIsUploading(false); |
|
|
|
} |
|
|
|
}, [setIsUploading]); |
|
|
|
|
|
|
|
const handleClosePreview = useCallback(() => { |
|
|
|
setPreviewOpen(false); |
|
|
|
if (previewUrl) { |
|
|
|
URL.revokeObjectURL(previewUrl); |
|
|
|
setPreviewUrl(null); |
|
|
|
} |
|
|
|
}, [previewUrl]); |
|
|
|
|
|
|
|
const handleDownloadQrCode = useCallback(async (warehouseIds: number[]) => { |
|
|
|
if (warehouseIds.length === 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
try { |
|
|
|
setIsUploading(true); |
|
|
|
const response = await exportWarehouseQrCode(warehouseIds); |
|
|
|
downloadFile(response.blobValue, response.filename); |
|
|
|
setSelectedWarehousesModalOpen(false); |
|
|
|
successDialog("二維碼已下載", t); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error exporting QR code:", error); |
|
|
|
} finally { |
|
|
|
setIsUploading(false); |
|
|
|
} |
|
|
|
}, [setIsUploading, t]); |
|
|
|
|
|
|
|
const handlePrint = useCallback(async () => { |
|
|
|
if (checkboxIds.length === 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
try { |
|
|
|
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); |
|
|
|
setSelectedWarehousesModalOpen(false); |
|
|
|
successDialog("二維碼已列印", t); |
|
|
|
} catch (error) { |
|
|
|
console.error("Error printing QR code:", error); |
|
|
|
} finally { |
|
|
|
setIsUploading(false); |
|
|
|
} |
|
|
|
}, [checkboxIds, printQty, setIsUploading, t]); |
|
|
|
|
|
|
|
const handleViewSelectedQrCodes = useCallback(() => { |
|
|
|
if (checkboxIds.length === 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
setSelectedWarehousesModalOpen(true); |
|
|
|
}, [checkboxIds]); |
|
|
|
|
|
|
|
const selectedWarehouses = useMemo(() => { |
|
|
|
return warehouses.filter(warehouse => checkboxIds.includes(warehouse.id)); |
|
|
|
}, [warehouses, checkboxIds]); |
|
|
|
|
|
|
|
const handleCloseSelectedWarehousesModal = useCallback(() => { |
|
|
|
setSelectedWarehousesModalOpen(false); |
|
|
|
}, []); |
|
|
|
|
|
|
|
const columns = useMemo<Column<WarehouseResult>[]>( |
|
|
|
() => [ |
|
|
|
{ |
|
|
|
name: "id", |
|
|
|
label: "", |
|
|
|
sx: { width: "50px", minWidth: "50px" }, |
|
|
|
renderCell: (params) => ( |
|
|
|
<Checkbox |
|
|
|
checked={checkboxIds.includes(params.id)} |
|
|
|
onChange={(e) => handleSelectWarehouse(params.id, e.target.checked)} |
|
|
|
onClick={(e) => e.stopPropagation()} |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "code", |
|
|
|
label: t("code"), |
|
|
|
align: "left", |
|
|
|
headerAlign: "left", |
|
|
|
sx: { width: "200px", minWidth: "200px" }, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "store_id", |
|
|
|
label: t("store_id"), |
|
|
|
align: "left", |
|
|
|
headerAlign: "left", |
|
|
|
sx: { width: "150px", minWidth: "150px" }, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "warehouse", |
|
|
|
label: t("warehouse"), |
|
|
|
align: "left", |
|
|
|
headerAlign: "left", |
|
|
|
sx: { width: "150px", minWidth: "150px" }, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "area", |
|
|
|
label: t("area"), |
|
|
|
align: "left", |
|
|
|
headerAlign: "left", |
|
|
|
sx: { width: "150px", minWidth: "150px" }, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "slot", |
|
|
|
label: t("slot"), |
|
|
|
align: "left", |
|
|
|
headerAlign: "left", |
|
|
|
sx: { width: "150px", minWidth: "150px" }, |
|
|
|
}, |
|
|
|
], |
|
|
|
[t, checkboxIds, handleSelectWarehouse], |
|
|
|
); |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<Card> |
|
|
|
<CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}> |
|
|
|
<Typography variant="overline">{t("Search Criteria")}</Typography> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
display: "flex", |
|
|
|
alignItems: "center", |
|
|
|
gap: 1, |
|
|
|
flexWrap: "nowrap", |
|
|
|
justifyContent: "flex-start", |
|
|
|
}} |
|
|
|
> |
|
|
|
<TextField |
|
|
|
label={t("store_id")} |
|
|
|
value={searchInputs.store_id} |
|
|
|
onChange={(e) => |
|
|
|
setSearchInputs((prev) => ({ ...prev, store_id: e.target.value })) |
|
|
|
} |
|
|
|
size="small" |
|
|
|
sx={{ width: "150px", minWidth: "120px" }} |
|
|
|
InputProps={{ |
|
|
|
endAdornment: ( |
|
|
|
<InputAdornment position="end">F</InputAdornment> |
|
|
|
), |
|
|
|
}} |
|
|
|
/> |
|
|
|
<Typography variant="body1" sx={{ mx: 0.5 }}> |
|
|
|
- |
|
|
|
</Typography> |
|
|
|
<TextField |
|
|
|
label={t("warehouse")} |
|
|
|
value={searchInputs.warehouse} |
|
|
|
onChange={(e) => |
|
|
|
setSearchInputs((prev) => ({ ...prev, warehouse: e.target.value })) |
|
|
|
} |
|
|
|
size="small" |
|
|
|
sx={{ width: "150px", minWidth: "120px" }} |
|
|
|
/> |
|
|
|
<Typography variant="body1" sx={{ mx: 0.5 }}> |
|
|
|
- |
|
|
|
</Typography> |
|
|
|
<TextField |
|
|
|
label={t("area")} |
|
|
|
value={searchInputs.area} |
|
|
|
onChange={(e) => |
|
|
|
setSearchInputs((prev) => ({ ...prev, area: e.target.value })) |
|
|
|
} |
|
|
|
size="small" |
|
|
|
sx={{ width: "150px", minWidth: "120px" }} |
|
|
|
/> |
|
|
|
<Typography variant="body1" sx={{ mx: 0.5 }}> |
|
|
|
- |
|
|
|
</Typography> |
|
|
|
<TextField |
|
|
|
label={t("slot")} |
|
|
|
value={searchInputs.slot} |
|
|
|
onChange={(e) => |
|
|
|
setSearchInputs((prev) => ({ ...prev, slot: e.target.value })) |
|
|
|
} |
|
|
|
size="small" |
|
|
|
sx={{ width: "150px", minWidth: "120px" }} |
|
|
|
/> |
|
|
|
</Box> |
|
|
|
<CardActions sx={{ justifyContent: "flex-start", px: 0, pt: 1 }}> |
|
|
|
<Button |
|
|
|
variant="text" |
|
|
|
startIcon={<RestartAlt />} |
|
|
|
onClick={handleReset} |
|
|
|
> |
|
|
|
{t("Reset")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
startIcon={<Search />} |
|
|
|
onClick={handleSearch} |
|
|
|
> |
|
|
|
{t("Search")} |
|
|
|
</Button> |
|
|
|
</CardActions> |
|
|
|
</CardContent> |
|
|
|
</Card> |
|
|
|
<SearchResults<WarehouseResult> |
|
|
|
items={filteredWarehouses} |
|
|
|
columns={columns} |
|
|
|
pagingController={pagingController} |
|
|
|
setPagingController={setPagingController} |
|
|
|
totalCount={filteredWarehouses.length} |
|
|
|
isAutoPaging={true} |
|
|
|
/> |
|
|
|
<Box sx={{ mb: 2, display: 'flex', alignItems: 'center', gap: 2 }}> |
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
onClick={() => handleSelectAll(!selectAll)} |
|
|
|
startIcon={<Checkbox checked={selectAll} />} |
|
|
|
> |
|
|
|
選擇全部倉庫 ({checkboxIds.length} / {filteredWarehouses.length}) |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={handleViewSelectedQrCodes} |
|
|
|
disabled={checkboxIds.length === 0} |
|
|
|
color="primary" |
|
|
|
> |
|
|
|
查看已選擇倉庫二維碼 ({checkboxIds.length}) |
|
|
|
</Button> |
|
|
|
</Box> |
|
|
|
|
|
|
|
<Modal |
|
|
|
open={selectedWarehousesModalOpen} |
|
|
|
onClose={handleCloseSelectedWarehousesModal} |
|
|
|
sx={{ |
|
|
|
display: 'flex', |
|
|
|
alignItems: 'center', |
|
|
|
justifyContent: 'center', |
|
|
|
}} |
|
|
|
> |
|
|
|
<Card |
|
|
|
sx={{ |
|
|
|
position: 'relative', |
|
|
|
width: '90%', |
|
|
|
maxWidth: '800px', |
|
|
|
maxHeight: '90vh', |
|
|
|
display: 'flex', |
|
|
|
flexDirection: 'column', |
|
|
|
outline: 'none', |
|
|
|
}} |
|
|
|
> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
display: 'flex', |
|
|
|
justifyContent: 'space-between', |
|
|
|
alignItems: 'center', |
|
|
|
p: 2, |
|
|
|
borderBottom: 1, |
|
|
|
borderColor: 'divider', |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography variant="h6" component="h2"> |
|
|
|
已選擇倉庫 ({selectedWarehouses.length}) |
|
|
|
</Typography> |
|
|
|
<IconButton onClick={handleCloseSelectedWarehousesModal}> |
|
|
|
<CloseIcon /> |
|
|
|
</IconButton> |
|
|
|
</Box> |
|
|
|
|
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
flex: 1, |
|
|
|
overflow: 'auto', |
|
|
|
p: 2, |
|
|
|
}} |
|
|
|
> |
|
|
|
<TableContainer component={Paper} variant="outlined"> |
|
|
|
<Table> |
|
|
|
<TableHead> |
|
|
|
<TableRow> |
|
|
|
<TableCell> |
|
|
|
<strong>{t("code")}</strong> |
|
|
|
</TableCell> |
|
|
|
<TableCell> |
|
|
|
<strong>{t("store_id")}</strong> |
|
|
|
</TableCell> |
|
|
|
<TableCell> |
|
|
|
<strong>{t("warehouse")}</strong> |
|
|
|
</TableCell> |
|
|
|
<TableCell> |
|
|
|
<strong>{t("area")}</strong> |
|
|
|
</TableCell> |
|
|
|
<TableCell> |
|
|
|
<strong>{t("slot")}</strong> |
|
|
|
</TableCell> |
|
|
|
</TableRow> |
|
|
|
</TableHead> |
|
|
|
<TableBody> |
|
|
|
{selectedWarehouses.length === 0 ? ( |
|
|
|
<TableRow> |
|
|
|
<TableCell colSpan={5} align="center"> |
|
|
|
沒有選擇的倉庫 |
|
|
|
</TableCell> |
|
|
|
</TableRow> |
|
|
|
) : ( |
|
|
|
selectedWarehouses.map((warehouse) => ( |
|
|
|
<TableRow key={warehouse.id}> |
|
|
|
<TableCell>{warehouse.code || '-'}</TableCell> |
|
|
|
<TableCell>{warehouse.store_id || '-'}</TableCell> |
|
|
|
<TableCell>{warehouse.warehouse || '-'}</TableCell> |
|
|
|
<TableCell>{warehouse.area || '-'}</TableCell> |
|
|
|
<TableCell>{warehouse.slot || '-'}</TableCell> |
|
|
|
</TableRow> |
|
|
|
)) |
|
|
|
)} |
|
|
|
</TableBody> |
|
|
|
</Table> |
|
|
|
</TableContainer> |
|
|
|
</Box> |
|
|
|
|
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
p: 2, |
|
|
|
borderTop: 1, |
|
|
|
borderColor: 'divider', |
|
|
|
bgcolor: 'background.paper', |
|
|
|
}} |
|
|
|
> |
|
|
|
<Stack direction="row" justifyContent="flex-end" alignItems="center" gap={2}> |
|
|
|
<Autocomplete<PrinterCombo> |
|
|
|
options={filteredPrinters} |
|
|
|
value={selectedPrinter ?? null} |
|
|
|
onChange={(event, value) => { |
|
|
|
setSelectedPrinter(value ?? undefined); |
|
|
|
}} |
|
|
|
getOptionLabel={(option) => option.name || option.label || option.code || String(option.id)} |
|
|
|
isOptionEqualToValue={(option, value) => option.id === value.id} |
|
|
|
renderInput={(params) => ( |
|
|
|
<TextField |
|
|
|
{...params} |
|
|
|
variant="outlined" |
|
|
|
label="列印機" |
|
|
|
sx={{ width: 300 }} |
|
|
|
/> |
|
|
|
)} |
|
|
|
/> |
|
|
|
<TextField |
|
|
|
variant="outlined" |
|
|
|
label="列印數量" |
|
|
|
type="number" |
|
|
|
value={printQty} |
|
|
|
onChange={(e) => { |
|
|
|
const value = parseInt(e.target.value) || 1; |
|
|
|
setPrintQty(Math.max(1, value)); |
|
|
|
}} |
|
|
|
inputProps={{ min: 1 }} |
|
|
|
sx={{ width: 120 }} |
|
|
|
/> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
startIcon={<PrintIcon />} |
|
|
|
onClick={handlePrint} |
|
|
|
disabled={checkboxIds.length === 0 || filteredPrinters.length === 0} |
|
|
|
color="primary" |
|
|
|
> |
|
|
|
列印 |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
startIcon={<DownloadIcon />} |
|
|
|
onClick={() => handleDownloadQrCode(checkboxIds)} |
|
|
|
disabled={checkboxIds.length === 0} |
|
|
|
color="primary" |
|
|
|
> |
|
|
|
下載二維碼 |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</Box> |
|
|
|
</Card> |
|
|
|
</Modal> |
|
|
|
|
|
|
|
<Modal |
|
|
|
open={previewOpen} |
|
|
|
onClose={handleClosePreview} |
|
|
|
sx={{ |
|
|
|
display: 'flex', |
|
|
|
alignItems: 'center', |
|
|
|
justifyContent: 'center', |
|
|
|
}} |
|
|
|
> |
|
|
|
<Card |
|
|
|
sx={{ |
|
|
|
position: 'relative', |
|
|
|
width: '90%', |
|
|
|
maxWidth: '900px', |
|
|
|
maxHeight: '90vh', |
|
|
|
display: 'flex', |
|
|
|
flexDirection: 'column', |
|
|
|
outline: 'none', |
|
|
|
}} |
|
|
|
> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
display: 'flex', |
|
|
|
justifyContent: 'flex-end', |
|
|
|
alignItems: 'center', |
|
|
|
p: 2, |
|
|
|
borderBottom: 1, |
|
|
|
borderColor: 'divider', |
|
|
|
}} |
|
|
|
> |
|
|
|
<IconButton |
|
|
|
onClick={handleClosePreview} |
|
|
|
> |
|
|
|
<CloseIcon /> |
|
|
|
</IconButton> |
|
|
|
</Box> |
|
|
|
|
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
flex: 1, |
|
|
|
overflow: 'auto', |
|
|
|
p: 2, |
|
|
|
}} |
|
|
|
> |
|
|
|
{previewUrl && ( |
|
|
|
<iframe |
|
|
|
src={previewUrl} |
|
|
|
width="100%" |
|
|
|
height="600px" |
|
|
|
style={{ |
|
|
|
border: 'none', |
|
|
|
}} |
|
|
|
title="PDF Preview" |
|
|
|
/> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
</Card> |
|
|
|
</Modal> |
|
|
|
</> |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
export default QrCodeHandleWarehouseSearch; |