pdf/word 저장기능 임시
This commit is contained in:
@@ -9,8 +9,11 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Printer, FileDown } from "lucide-react";
|
||||
import { Printer, FileDown, FileText } from "lucide-react";
|
||||
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
|
||||
import { useState } from "react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Document, Packer, Paragraph, TextRun, Table, TableCell, TableRow, WidthType } from "docx";
|
||||
|
||||
interface ReportPreviewModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -18,7 +21,9 @@ interface ReportPreviewModalProps {
|
||||
}
|
||||
|
||||
export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps) {
|
||||
const { components, canvasWidth, canvasHeight, getQueryResult } = useReportDesigner();
|
||||
const { components, canvasWidth, canvasHeight, getQueryResult, reportDetail } = useReportDesigner();
|
||||
const [isExporting, setIsExporting] = useState(false);
|
||||
const { toast } = useToast();
|
||||
|
||||
// 컴포넌트의 실제 표시 값 가져오기
|
||||
const getComponentValue = (component: any): string => {
|
||||
@@ -63,12 +68,165 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
||||
printWindow.print();
|
||||
};
|
||||
|
||||
// PDF 다운로드 (브라우저 인쇄 기능 이용)
|
||||
const handleDownloadPDF = () => {
|
||||
alert("PDF 다운로드 기능은 추후 구현 예정입니다.");
|
||||
const printContent = document.getElementById("preview-content");
|
||||
if (!printContent) return;
|
||||
|
||||
const printWindow = window.open("", "_blank");
|
||||
if (!printWindow) return;
|
||||
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>리포트 인쇄</title>
|
||||
<style>
|
||||
@media print {
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 10mm;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
body {
|
||||
font-family: 'Malgun Gothic', sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${printContent.innerHTML}
|
||||
<script>
|
||||
window.onload = function() {
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
printWindow.document.close();
|
||||
|
||||
toast({
|
||||
title: "안내",
|
||||
description: "인쇄 대화상자에서 'PDF로 저장'을 선택하세요.",
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownloadWord = () => {
|
||||
alert("WORD 다운로드 기능은 추후 구현 예정입니다.");
|
||||
// WORD 다운로드
|
||||
const handleDownloadWord = async () => {
|
||||
setIsExporting(true);
|
||||
try {
|
||||
// 컴포넌트를 Paragraph로 변환
|
||||
const paragraphs: (Paragraph | Table)[] = [];
|
||||
|
||||
// Y 좌표로 정렬
|
||||
const sortedComponents = [...components].sort((a, b) => a.y - b.y);
|
||||
|
||||
for (const component of sortedComponents) {
|
||||
if (component.type === "text" || component.type === "label") {
|
||||
const value = getComponentValue(component);
|
||||
paragraphs.push(
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun({
|
||||
text: value,
|
||||
size: (component.fontSize || 13) * 2, // pt to half-pt
|
||||
color: component.fontColor?.replace("#", "") || "000000",
|
||||
bold: component.fontWeight === "bold",
|
||||
}),
|
||||
],
|
||||
spacing: {
|
||||
after: 200,
|
||||
},
|
||||
}),
|
||||
);
|
||||
} else if (component.type === "table" && component.queryId) {
|
||||
const queryResult = getQueryResult(component.queryId);
|
||||
if (queryResult && queryResult.rows.length > 0) {
|
||||
// 테이블 헤더
|
||||
const headerCells = queryResult.fields.map(
|
||||
(field) =>
|
||||
new TableCell({
|
||||
children: [new Paragraph({ text: field })],
|
||||
width: { size: 100 / queryResult.fields.length, type: WidthType.PERCENTAGE },
|
||||
}),
|
||||
);
|
||||
|
||||
// 테이블 행
|
||||
const dataRows = queryResult.rows.map(
|
||||
(row) =>
|
||||
new TableRow({
|
||||
children: queryResult.fields.map(
|
||||
(field) =>
|
||||
new TableCell({
|
||||
children: [new Paragraph({ text: String(row[field] ?? "") })],
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
const table = new Table({
|
||||
rows: [new TableRow({ children: headerCells }), ...dataRows],
|
||||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||||
});
|
||||
|
||||
paragraphs.push(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 문서 생성
|
||||
const doc = new Document({
|
||||
sections: [
|
||||
{
|
||||
properties: {},
|
||||
children: paragraphs,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Blob 생성 및 다운로드
|
||||
const blob = await Packer.toBlob(doc);
|
||||
const fileName = reportDetail?.report?.report_name_kor || "리포트";
|
||||
const timestamp = new Date().toISOString().slice(0, 10);
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `${fileName}_${timestamp}.docx`;
|
||||
link.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
toast({
|
||||
title: "성공",
|
||||
description: "WORD 파일이 다운로드되었습니다.",
|
||||
});
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : "WORD 생성에 실패했습니다.";
|
||||
toast({
|
||||
title: "오류",
|
||||
description: errorMessage,
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setIsExporting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -170,10 +328,10 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
<Button variant="outline" onClick={onClose} disabled={isExporting}>
|
||||
닫기
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handlePrint} className="gap-2">
|
||||
<Button variant="outline" onClick={handlePrint} disabled={isExporting} className="gap-2">
|
||||
<Printer className="h-4 w-4" />
|
||||
인쇄
|
||||
</Button>
|
||||
@@ -181,9 +339,9 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
||||
<FileDown className="h-4 w-4" />
|
||||
PDF
|
||||
</Button>
|
||||
<Button onClick={handleDownloadWord} variant="secondary" className="gap-2">
|
||||
<FileDown className="h-4 w-4" />
|
||||
WORD
|
||||
<Button onClick={handleDownloadWord} disabled={isExporting} variant="secondary" className="gap-2">
|
||||
<FileText className="h-4 w-4" />
|
||||
{isExporting ? "생성 중..." : "WORD"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
Reference in New Issue
Block a user