리포트 디자이너에 바코드/QR코드 컴포넌트 추가

This commit is contained in:
dohyeons
2025-12-19 17:59:54 +09:00
parent 932eb288c6
commit ea01309158
11 changed files with 1017 additions and 10 deletions

View File

@@ -28,6 +28,7 @@ import {
PageOrientation,
convertMillimetersToTwip,
} from "docx";
import bwipjs from "bwip-js";
export class ReportController {
/**
@@ -1326,6 +1327,43 @@ export class ReportController {
);
}
// Barcode 컴포넌트 (바코드 이미지가 미리 생성되어 전달된 경우)
else if (component.type === "barcode" && component.barcodeImageBase64) {
try {
const base64Data =
component.barcodeImageBase64.split(",")[1] || component.barcodeImageBase64;
const imageBuffer = Buffer.from(base64Data, "base64");
result.push(
new ParagraphRef({
children: [
new ImageRunRef({
data: imageBuffer,
transformation: {
width: Math.round(component.width * 0.75),
height: Math.round(component.height * 0.75),
},
type: "png",
}),
],
})
);
} catch (e) {
// 바코드 이미지 생성 실패 시 텍스트로 대체
const barcodeValue = component.barcodeValue || "BARCODE";
result.push(
new ParagraphRef({
children: [
new TextRunRef({
text: `[${barcodeValue}]`,
size: pxToHalfPtFn(12),
font: "맑은 고딕",
}),
],
})
);
}
}
// Divider - 테이블 셀로 감싸서 정확한 너비 적용
else if (
component.type === "divider" &&
@@ -1354,6 +1392,82 @@ export class ReportController {
return result;
};
// 바코드 이미지 생성 헬퍼 함수
const generateBarcodeImage = async (
component: any,
queryResultsMapRef: Record<string, { fields: string[]; rows: Record<string, unknown>[] }>
): Promise<string | null> => {
try {
const barcodeType = component.barcodeType || "CODE128";
const barcodeColor = (component.barcodeColor || "#000000").replace("#", "");
const barcodeBackground = (component.barcodeBackground || "#ffffff").replace("#", "");
// 바코드 값 결정 (쿼리 바인딩 또는 고정값)
let barcodeValue = component.barcodeValue || "SAMPLE123";
if (component.barcodeFieldName && component.queryId && queryResultsMapRef[component.queryId]) {
const qResult = queryResultsMapRef[component.queryId];
if (qResult.rows && qResult.rows.length > 0) {
const row = qResult.rows[0];
const val = row[component.barcodeFieldName];
if (val !== null && val !== undefined) {
barcodeValue = String(val);
}
}
}
// bwip-js 바코드 타입 매핑
const bcidMap: Record<string, string> = {
"CODE128": "code128",
"CODE39": "code39",
"EAN13": "ean13",
"EAN8": "ean8",
"UPC": "upca",
"QR": "qrcode",
};
const bcid = bcidMap[barcodeType] || "code128";
const isQR = barcodeType === "QR";
// 바코드 옵션 설정
const options: any = {
bcid: bcid,
text: barcodeValue,
scale: 3,
includetext: !isQR && component.showBarcodeText !== false,
textxalign: "center",
barcolor: barcodeColor,
backgroundcolor: barcodeBackground,
};
// QR 코드 옵션
if (isQR) {
options.eclevel = component.qrErrorCorrectionLevel || "M";
}
// 바코드 이미지 생성
const png = await bwipjs.toBuffer(options);
const base64 = png.toString("base64");
return `data:image/png;base64,${base64}`;
} catch (error) {
console.error("바코드 생성 오류:", error);
return null;
}
};
// 모든 페이지의 바코드 컴포넌트에 대해 이미지 생성
for (const page of layoutConfig.pages) {
if (page.components) {
for (const component of page.components) {
if (component.type === "barcode") {
const barcodeImage = await generateBarcodeImage(component, queryResultsMap);
if (barcodeImage) {
component.barcodeImageBase64 = barcodeImage;
}
}
}
}
}
// 섹션 생성 (페이지별)
const sortedPages = layoutConfig.pages.sort(
(a: any, b: any) => a.page_order - b.page_order
@@ -2624,6 +2738,77 @@ export class ReportController {
lastBottomY = adjustedY + component.height;
}
// Barcode 컴포넌트
else if (component.type === "barcode") {
if (component.barcodeImageBase64) {
try {
const base64Data =
component.barcodeImageBase64.split(",")[1] || component.barcodeImageBase64;
const imageBuffer = Buffer.from(base64Data, "base64");
// spacing을 위한 빈 paragraph
if (spacingBefore > 0) {
children.push(
new Paragraph({
spacing: { before: spacingBefore, after: 0 },
children: [],
})
);
}
children.push(
new Paragraph({
indent: { left: indentLeft },
children: [
new ImageRun({
data: imageBuffer,
transformation: {
width: Math.round(component.width * 0.75),
height: Math.round(component.height * 0.75),
},
type: "png",
}),
],
})
);
} catch (imgError) {
console.error("바코드 이미지 오류:", imgError);
// 바코드 이미지 생성 실패 시 텍스트로 대체
const barcodeValue = component.barcodeValue || "BARCODE";
children.push(
new Paragraph({
spacing: { before: spacingBefore, after: 0 },
indent: { left: indentLeft },
children: [
new TextRun({
text: `[${barcodeValue}]`,
size: pxToHalfPt(12),
font: "맑은 고딕",
}),
],
})
);
}
} else {
// 바코드 이미지가 없는 경우 텍스트로 대체
const barcodeValue = component.barcodeValue || "BARCODE";
children.push(
new Paragraph({
spacing: { before: spacingBefore, after: 0 },
indent: { left: indentLeft },
children: [
new TextRun({
text: `[${barcodeValue}]`,
size: pxToHalfPt(12),
font: "맑은 고딕",
}),
],
})
);
}
lastBottomY = adjustedY + component.height;
}
// Table 컴포넌트
else if (component.type === "table" && component.queryId) {
const queryResult = queryResultsMap[component.queryId];