| @@ -131,19 +131,26 @@ export default function ReportPage() { | |||
| setDynamicOptions({}); | |||
| }, [selectedReportId]); | |||
| const handlePrint = async () => { | |||
| if (!currentReport) return; | |||
| const validateRequiredFields = () => { | |||
| if (!currentReport) return true; | |||
| // 1. Mandatory Field Validation | |||
| // Mandatory Field Validation | |||
| const missingFields = currentReport.fields | |||
| .filter(field => field.required && !criteria[field.name]) | |||
| .map(field => field.label); | |||
| if (missingFields.length > 0) { | |||
| alert(`缺少必填條件:\n- ${missingFields.join('\n- ')}`); | |||
| return; | |||
| return false; | |||
| } | |||
| return true; | |||
| }; | |||
| const handlePrint = async () => { | |||
| if (!currentReport) return; | |||
| if (!validateRequiredFields()) return; | |||
| // For rep-005, the print logic is handled by SemiFGProductionAnalysisReport component | |||
| if (currentReport.id === 'rep-005') return; | |||
| @@ -156,12 +163,51 @@ export default function ReportPage() { | |||
| await executePrint(); | |||
| }; | |||
| const handleExcelPrint = async () => { | |||
| if (!currentReport) return; | |||
| if (!validateRequiredFields()) return; | |||
| await executeExcelReport(); | |||
| }; | |||
| const executeExcelReport = async () => { | |||
| if (!currentReport) return; | |||
| setLoading(true); | |||
| try { | |||
| if (currentReport.id === 'rep-014') { | |||
| await generateGrnReportExcel(criteria, currentReport.title); | |||
| } else { | |||
| // Backend returns actual .xlsx bytes for this Excel endpoint. | |||
| const queryParams = new URLSearchParams(criteria).toString(); | |||
| const excelUrl = `${currentReport.apiEndpoint}-excel?${queryParams}`; | |||
| const response = await clientAuthFetch(excelUrl, { | |||
| method: 'GET', | |||
| headers: { Accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }, | |||
| }); | |||
| if (response.status === 401 || response.status === 403) return; | |||
| if (!response.ok) { | |||
| const errorText = await response.text(); | |||
| console.error("Response error:", errorText); | |||
| throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); | |||
| } | |||
| const blob = await response.blob(); | |||
| const downloadUrl = window.URL.createObjectURL(blob); | |||
| const link = document.createElement('a'); | |||
| link.href = downloadUrl; | |||
| const contentDisposition = response.headers.get('Content-Disposition'); | |||
| let fileName = `${currentReport.title}.xlsx`; | |||
| if (contentDisposition?.includes('filename=')) { | |||
| fileName = contentDisposition.split('filename=')[1].split(';')[0].replace(/"/g, ''); | |||
| } | |||
| link.setAttribute('download', fileName); | |||
| document.body.appendChild(link); | |||
| link.click(); | |||
| link.remove(); | |||
| window.URL.revokeObjectURL(downloadUrl); | |||
| } | |||
| setShowConfirmDialog(false); | |||
| } catch (error) { | |||
| @@ -438,7 +484,7 @@ export default function ReportPage() { | |||
| })} | |||
| </Grid> | |||
| <Box sx={{ mt: 4, display: 'flex', justifyContent: 'flex-end' }}> | |||
| <Box sx={{ mt: 4, display: 'flex', gap: 2, justifyContent: 'flex-end' }}> | |||
| {currentReport.id === 'rep-005' ? ( | |||
| <SemiFGProductionAnalysisReport | |||
| criteria={criteria} | |||
| @@ -447,6 +493,29 @@ export default function ReportPage() { | |||
| setLoading={setLoading} | |||
| reportTitle={currentReport.title} | |||
| /> | |||
| ) : currentReport.id === 'rep-006' ? ( | |||
| <> | |||
| <Button | |||
| variant="contained" | |||
| size="large" | |||
| startIcon={<DownloadIcon />} | |||
| onClick={handlePrint} | |||
| disabled={loading} | |||
| sx={{ px: 4 }} | |||
| > | |||
| {loading ? "生成 PDF..." : "下載報告 (PDF)"} | |||
| </Button> | |||
| <Button | |||
| variant="outlined" | |||
| size="large" | |||
| startIcon={<DownloadIcon />} | |||
| onClick={handleExcelPrint} | |||
| disabled={loading} | |||
| sx={{ px: 4 }} | |||
| > | |||
| {loading ? "生成 Excel..." : "下載報告 (Excel)"} | |||
| </Button> | |||
| </> | |||
| ) : currentReport.responseType === 'excel' ? ( | |||
| <Button | |||
| variant="contained" | |||