카드 컴포넌트 추가 및 페이지번호/쿼리 버그 수정
This commit is contained in:
@@ -791,6 +791,93 @@ export class ReportController {
|
||||
);
|
||||
}
|
||||
|
||||
// Card 컴포넌트
|
||||
else if (component.type === "card") {
|
||||
const cardTitle = component.cardTitle || "정보 카드";
|
||||
const cardItems = component.cardItems || [];
|
||||
const labelWidth = component.labelWidth || 80;
|
||||
const showCardTitle = component.showCardTitle !== false;
|
||||
const titleFontSize = pxToHalfPtFn(component.titleFontSize || 14);
|
||||
const labelFontSize = pxToHalfPtFn(component.labelFontSize || 13);
|
||||
const valueFontSize = pxToHalfPtFn(component.valueFontSize || 13);
|
||||
const titleColor = (component.titleColor || "#1e40af").replace("#", "");
|
||||
const labelColor = (component.labelColor || "#374151").replace("#", "");
|
||||
const valueColor = (component.valueColor || "#000000").replace("#", "");
|
||||
const borderColor = (component.borderColor || "#e5e7eb").replace("#", "");
|
||||
|
||||
// 쿼리 바인딩된 값 가져오기
|
||||
const getCardValueFn = (item: { label: string; value: string; fieldName?: string }) => {
|
||||
if (item.fieldName && component.queryId && queryResultsMapRef[component.queryId]) {
|
||||
const qResult = queryResultsMapRef[component.queryId];
|
||||
if (qResult.rows && qResult.rows.length > 0) {
|
||||
const row = qResult.rows[0];
|
||||
return row[item.fieldName] !== undefined ? String(row[item.fieldName]) : item.value;
|
||||
}
|
||||
}
|
||||
return item.value;
|
||||
};
|
||||
|
||||
// 제목
|
||||
if (showCardTitle) {
|
||||
result.push(
|
||||
new ParagraphRef({
|
||||
children: [
|
||||
new TextRunRef({
|
||||
text: cardTitle,
|
||||
size: titleFontSize,
|
||||
color: titleColor,
|
||||
bold: true,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
// 구분선
|
||||
result.push(
|
||||
new ParagraphRef({
|
||||
border: {
|
||||
bottom: {
|
||||
color: borderColor,
|
||||
space: 1,
|
||||
style: BorderStyleRef.SINGLE,
|
||||
size: 8,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 항목들
|
||||
for (const item of cardItems) {
|
||||
const itemValue = getCardValueFn(item as { label: string; value: string; fieldName?: string });
|
||||
result.push(
|
||||
new ParagraphRef({
|
||||
children: [
|
||||
new TextRunRef({
|
||||
text: item.label,
|
||||
size: labelFontSize,
|
||||
color: labelColor,
|
||||
bold: true,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
new TextRunRef({
|
||||
text: " ",
|
||||
size: labelFontSize,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
new TextRunRef({
|
||||
text: itemValue,
|
||||
size: valueFontSize,
|
||||
color: valueColor,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Divider - 테이블 셀로 감싸서 정확한 너비 적용
|
||||
else if (component.type === "divider" && component.orientation === "horizontal") {
|
||||
result.push(
|
||||
@@ -1279,6 +1366,172 @@ export class ReportController {
|
||||
lastBottomY = adjustedY + component.height;
|
||||
}
|
||||
|
||||
// Card 컴포넌트 - 테이블로 감싸서 정확한 위치 적용
|
||||
else if (component.type === "card") {
|
||||
const cardTitle = component.cardTitle || "정보 카드";
|
||||
const cardItems = component.cardItems || [];
|
||||
const labelWidthPx = component.labelWidth || 80;
|
||||
const showCardTitle = component.showCardTitle !== false;
|
||||
const titleFontSize = pxToHalfPt(component.titleFontSize || 14);
|
||||
const labelFontSizeCard = pxToHalfPt(component.labelFontSize || 13);
|
||||
const valueFontSizeCard = pxToHalfPt(component.valueFontSize || 13);
|
||||
const titleColorCard = (component.titleColor || "#1e40af").replace("#", "");
|
||||
const labelColorCard = (component.labelColor || "#374151").replace("#", "");
|
||||
const valueColorCard = (component.valueColor || "#000000").replace("#", "");
|
||||
const borderColorCard = (component.borderColor || "#e5e7eb").replace("#", "");
|
||||
|
||||
// 쿼리 바인딩된 값 가져오기
|
||||
const getCardValueLocal = (item: { label: string; value: string; fieldName?: string }) => {
|
||||
if (item.fieldName && component.queryId && queryResultsMap[component.queryId]) {
|
||||
const qResult = queryResultsMap[component.queryId];
|
||||
if (qResult.rows && qResult.rows.length > 0) {
|
||||
const row = qResult.rows[0];
|
||||
return row[item.fieldName] !== undefined ? String(row[item.fieldName]) : item.value;
|
||||
}
|
||||
}
|
||||
return item.value;
|
||||
};
|
||||
|
||||
const cardParagraphs: Paragraph[] = [];
|
||||
|
||||
// 제목
|
||||
if (showCardTitle) {
|
||||
cardParagraphs.push(
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun({
|
||||
text: cardTitle,
|
||||
size: titleFontSize,
|
||||
color: titleColorCard,
|
||||
bold: true,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
// 구분선
|
||||
cardParagraphs.push(
|
||||
new Paragraph({
|
||||
border: {
|
||||
bottom: {
|
||||
color: borderColorCard,
|
||||
space: 1,
|
||||
style: BorderStyle.SINGLE,
|
||||
size: 8,
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// 항목들을 테이블로 구성 (라벨 + 값)
|
||||
const itemRows = cardItems.map((item: { label: string; value: string; fieldName?: string }) => {
|
||||
const itemValue = getCardValueLocal(item);
|
||||
return new TableRow({
|
||||
children: [
|
||||
new TableCell({
|
||||
width: { size: pxToTwip(labelWidthPx), type: WidthType.DXA },
|
||||
children: [
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun({
|
||||
text: item.label,
|
||||
size: labelFontSizeCard,
|
||||
color: labelColorCard,
|
||||
bold: true,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
borders: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
},
|
||||
}),
|
||||
new TableCell({
|
||||
width: { size: pxToTwip(component.width - labelWidthPx - 16), type: WidthType.DXA },
|
||||
children: [
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun({
|
||||
text: itemValue,
|
||||
size: valueFontSizeCard,
|
||||
color: valueColorCard,
|
||||
font: "맑은 고딕",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
borders: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
const itemsTable = new Table({
|
||||
rows: itemRows,
|
||||
width: { size: pxToTwip(component.width), type: WidthType.DXA },
|
||||
borders: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
insideHorizontal: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
insideVertical: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
},
|
||||
});
|
||||
|
||||
// 전체를 하나의 테이블 셀로 감싸기
|
||||
const cardCell = new TableCell({
|
||||
children: [...cardParagraphs, itemsTable],
|
||||
width: { size: pxToTwip(component.width), type: WidthType.DXA },
|
||||
borders: component.showCardBorder !== false
|
||||
? {
|
||||
top: { style: BorderStyle.SINGLE, size: 4, color: borderColorCard },
|
||||
bottom: { style: BorderStyle.SINGLE, size: 4, color: borderColorCard },
|
||||
left: { style: BorderStyle.SINGLE, size: 4, color: borderColorCard },
|
||||
right: { style: BorderStyle.SINGLE, size: 4, color: borderColorCard },
|
||||
}
|
||||
: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
},
|
||||
verticalAlign: VerticalAlign.TOP,
|
||||
});
|
||||
|
||||
const cardTable = new Table({
|
||||
rows: [new TableRow({ children: [cardCell] })],
|
||||
width: { size: pxToTwip(component.width), type: WidthType.DXA },
|
||||
indent: { size: indentLeft, type: WidthType.DXA },
|
||||
borders: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
left: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
right: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
insideHorizontal: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
insideVertical: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
},
|
||||
});
|
||||
|
||||
// spacing을 위한 빈 paragraph
|
||||
if (spacingBefore > 0) {
|
||||
children.push(new Paragraph({ spacing: { before: spacingBefore, after: 0 }, children: [] }));
|
||||
}
|
||||
children.push(cardTable);
|
||||
lastBottomY = adjustedY + component.height;
|
||||
}
|
||||
|
||||
// Table 컴포넌트
|
||||
else if (component.type === "table" && component.queryId) {
|
||||
const queryResult = queryResultsMap[component.queryId];
|
||||
|
||||
Reference in New Issue
Block a user