438 lines
13 KiB
JavaScript
438 lines
13 KiB
JavaScript
|
|
/**
|
|||
|
|
* 방화셔터 견적서 PPTX 생성 스크립트
|
|||
|
|
* script.md 기반으로 5장 슬라이드 생성
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const PptxGenJS = require('pptxgenjs');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
// 색상 정의 (# 없이)
|
|||
|
|
const colors = {
|
|||
|
|
primary: '0d9488', // Teal
|
|||
|
|
secondary: '1e293b', // Slate 800
|
|||
|
|
accent: 'dc2626', // Red
|
|||
|
|
white: 'FFFFFF',
|
|||
|
|
lightGray: 'f1f5f9',
|
|||
|
|
darkGray: '64748b',
|
|||
|
|
tableHeader: '0f766e',
|
|||
|
|
tableAlt: 'f0fdfa'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 프레젠테이션 생성
|
|||
|
|
const pptx = new PptxGenJS();
|
|||
|
|
|
|||
|
|
// 레이아웃 설정 (16:9)
|
|||
|
|
pptx.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 });
|
|||
|
|
pptx.layout = 'CUSTOM_16x9';
|
|||
|
|
|
|||
|
|
// 메타데이터 설정
|
|||
|
|
pptx.title = '방화셔터 견적서 프로세스';
|
|||
|
|
pptx.subject = 'AI 기반 견적서 자동화';
|
|||
|
|
pptx.author = 'SAM';
|
|||
|
|
pptx.company = 'SAM';
|
|||
|
|
|
|||
|
|
// ========================================
|
|||
|
|
// 슬라이드 1: 표지
|
|||
|
|
// ========================================
|
|||
|
|
function createCoverSlide() {
|
|||
|
|
const slide = pptx.addSlide();
|
|||
|
|
|
|||
|
|
// 배경
|
|||
|
|
slide.background = { color: colors.secondary };
|
|||
|
|
|
|||
|
|
// 상단 악센트 라인
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0, y: 0, w: 10, h: 0.15,
|
|||
|
|
fill: { color: colors.primary }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 회사 로고 영역
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0.5, y: 0.5, w: 1.5, h: 0.6,
|
|||
|
|
fill: { color: colors.primary }
|
|||
|
|
});
|
|||
|
|
slide.addText('SAM', {
|
|||
|
|
x: 0.5, y: 0.5, w: 1.5, h: 0.6,
|
|||
|
|
fontSize: 20, bold: true, color: colors.white,
|
|||
|
|
align: 'center', valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 메인 제목
|
|||
|
|
slide.addText('방화셔터 견적 프로세스', {
|
|||
|
|
x: 0.5, y: 2, w: 9, h: 1,
|
|||
|
|
fontSize: 42, bold: true, color: colors.white,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 부제목
|
|||
|
|
slide.addText('AI 기반 자동화 워크플로우', {
|
|||
|
|
x: 0.5, y: 3, w: 9, h: 0.6,
|
|||
|
|
fontSize: 22, color: colors.primary,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 프로젝트 정보
|
|||
|
|
slide.addText('OO빌딩 방화셔터 교체공사', {
|
|||
|
|
x: 0.5, y: 4, w: 9, h: 0.4,
|
|||
|
|
fontSize: 16, color: colors.darkGray,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 날짜 및 수신처
|
|||
|
|
slide.addText('2024년 10월 24일 | 수신: 건설사 담당자님 귀중', {
|
|||
|
|
x: 0.5, y: 4.8, w: 9, h: 0.3,
|
|||
|
|
fontSize: 12, color: colors.darkGray,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ 슬라이드 1: 표지 생성 완료');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========================================
|
|||
|
|
// 슬라이드 2: 견적 요약
|
|||
|
|
// ========================================
|
|||
|
|
function createSummarySlide() {
|
|||
|
|
const slide = pptx.addSlide();
|
|||
|
|
|
|||
|
|
// 배경
|
|||
|
|
slide.background = { color: colors.white };
|
|||
|
|
|
|||
|
|
// 상단 바
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0, y: 0, w: 10, h: 0.8,
|
|||
|
|
fill: { color: colors.secondary }
|
|||
|
|
});
|
|||
|
|
slide.addText('견적 요약', {
|
|||
|
|
x: 0.5, y: 0.2, w: 5, h: 0.5,
|
|||
|
|
fontSize: 24, bold: true, color: colors.white
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 총 금액 박스
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 1, y: 1.5, w: 8, h: 2,
|
|||
|
|
fill: { color: colors.lightGray },
|
|||
|
|
line: { color: colors.primary, width: 2 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('총 견적 금액', {
|
|||
|
|
x: 1, y: 1.6, w: 8, h: 0.5,
|
|||
|
|
fontSize: 16, color: colors.darkGray,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('일금 이천삼백사십오만원정', {
|
|||
|
|
x: 1, y: 2.1, w: 8, h: 0.8,
|
|||
|
|
fontSize: 32, bold: true, color: colors.secondary,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('₩ 23,450,000', {
|
|||
|
|
x: 1, y: 2.8, w: 8, h: 0.5,
|
|||
|
|
fontSize: 20, color: colors.primary,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 금액 구성
|
|||
|
|
const breakdown = [
|
|||
|
|
['항목', '금액'],
|
|||
|
|
['제품비 (철재 방화셔터)', '₩ 18,700,000'],
|
|||
|
|
['모터비 (600kg x 2)', '₩ 1,300,000'],
|
|||
|
|
['설치비 (5개소)', '₩ 1,500,000'],
|
|||
|
|
['운반비 (경기권)', '₩ 0'],
|
|||
|
|
['특별 할인 (5%)', '- ₩ 1,050,000'],
|
|||
|
|
['합계', '₩ 23,450,000']
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
slide.addTable(breakdown, {
|
|||
|
|
x: 1.5, y: 3.7, w: 7, h: 1.6,
|
|||
|
|
fontSize: 11,
|
|||
|
|
color: colors.secondary,
|
|||
|
|
border: { pt: 0.5, color: colors.darkGray },
|
|||
|
|
fill: { color: colors.white },
|
|||
|
|
colW: [4, 3],
|
|||
|
|
align: 'left',
|
|||
|
|
valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 부가세 안내
|
|||
|
|
slide.addText('※ 부가세 별도', {
|
|||
|
|
x: 7, y: 5.3, w: 2.5, h: 0.25,
|
|||
|
|
fontSize: 10, color: colors.accent, bold: true
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ 슬라이드 2: 견적 요약 생성 완료');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========================================
|
|||
|
|
// 슬라이드 3: 세부 내역
|
|||
|
|
// ========================================
|
|||
|
|
function createDetailSlide() {
|
|||
|
|
const slide = pptx.addSlide();
|
|||
|
|
|
|||
|
|
// 배경
|
|||
|
|
slide.background = { color: colors.white };
|
|||
|
|
|
|||
|
|
// 상단 바
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0, y: 0, w: 10, h: 0.8,
|
|||
|
|
fill: { color: colors.secondary }
|
|||
|
|
});
|
|||
|
|
slide.addText('세부 내역', {
|
|||
|
|
x: 0.5, y: 0.2, w: 5, h: 0.5,
|
|||
|
|
fontSize: 24, bold: true, color: colors.white
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 제품 정보 테이블
|
|||
|
|
const productTable = [
|
|||
|
|
['No', '제품명', '규격 (WxH)', '수량', '단가', '모터 사양', '금액', '비고'],
|
|||
|
|
['1', '철재 방화셔터', '3.0m x 2.5m', '2', '85,000/㎡', '600kg', '12,750,000', '내화성적서 포함'],
|
|||
|
|
['2', '철재 방화셔터', '2.5m x 3.0m', '2', '85,000/㎡', '600kg', '12,750,000', '내화성적서 포함'],
|
|||
|
|
['3', '스크린 방화셔터', '2.0m x 2.0m', '1', '별도견적', '400kg', '5,200,000', '감지기 별도']
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
slide.addTable(productTable, {
|
|||
|
|
x: 0.3, y: 1, w: 9.4, h: 1.8,
|
|||
|
|
fontSize: 9,
|
|||
|
|
color: colors.secondary,
|
|||
|
|
border: { pt: 0.5, color: colors.darkGray },
|
|||
|
|
colW: [0.5, 1.5, 1.2, 0.6, 1, 0.9, 1.2, 1.2],
|
|||
|
|
align: 'center',
|
|||
|
|
valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 부가 비용 테이블
|
|||
|
|
slide.addText('부가 비용', {
|
|||
|
|
x: 0.3, y: 3, w: 2, h: 0.4,
|
|||
|
|
fontSize: 14, bold: true, color: colors.secondary
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const additionalTable = [
|
|||
|
|
['항목', '조건', '단가', '수량', '금액'],
|
|||
|
|
['기본 설치비', '1개소당', '300,000', '5개소', '1,500,000'],
|
|||
|
|
['고소작업비', '층고 5m 이상 시', '250,000/일', '해당없음', '0'],
|
|||
|
|
['운반비', '서울/경기권', '0', '1식', '0']
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
slide.addTable(additionalTable, {
|
|||
|
|
x: 0.3, y: 3.4, w: 9.4, h: 1.2,
|
|||
|
|
fontSize: 9,
|
|||
|
|
color: colors.secondary,
|
|||
|
|
border: { pt: 0.5, color: colors.darkGray },
|
|||
|
|
colW: [1.5, 2, 1.5, 1.2, 1.5],
|
|||
|
|
align: 'center',
|
|||
|
|
valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 계산식 설명
|
|||
|
|
slide.addText('※ 면적 계산: 가로(m) × 높이(m) × 헤베당 단가', {
|
|||
|
|
x: 0.3, y: 4.8, w: 5, h: 0.25,
|
|||
|
|
fontSize: 9, color: colors.darkGray
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('※ 모터 선택 기준: 400kg(소형), 600kg(중형), 1000kg(대형)', {
|
|||
|
|
x: 0.3, y: 5.05, w: 5, h: 0.25,
|
|||
|
|
fontSize: 9, color: colors.darkGray
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 유효기간 경고
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 5.5, y: 4.7, w: 4.2, h: 0.7,
|
|||
|
|
fill: { color: 'fef2f2' },
|
|||
|
|
line: { color: colors.accent, width: 1 }
|
|||
|
|
});
|
|||
|
|
slide.addText('⚠️ 본 견적서는 제출일로부터 15일간 유효함', {
|
|||
|
|
x: 5.5, y: 4.7, w: 4.2, h: 0.7,
|
|||
|
|
fontSize: 10, bold: true, color: colors.accent,
|
|||
|
|
align: 'center', valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ 슬라이드 3: 세부 내역 생성 완료');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========================================
|
|||
|
|
// 슬라이드 4: 시험성적서/특허증
|
|||
|
|
// ========================================
|
|||
|
|
function createCertificateSlide() {
|
|||
|
|
const slide = pptx.addSlide();
|
|||
|
|
|
|||
|
|
// 배경
|
|||
|
|
slide.background = { color: colors.white };
|
|||
|
|
|
|||
|
|
// 상단 바
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0, y: 0, w: 10, h: 0.8,
|
|||
|
|
fill: { color: colors.secondary }
|
|||
|
|
});
|
|||
|
|
slide.addText('품질 인증', {
|
|||
|
|
x: 0.5, y: 0.2, w: 5, h: 0.5,
|
|||
|
|
fontSize: 24, bold: true, color: colors.white
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 인증서 카드 1
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0.5, y: 1.2, w: 4.3, h: 3.8,
|
|||
|
|
fill: { color: colors.lightGray },
|
|||
|
|
line: { color: colors.primary, width: 1 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('🔥', {
|
|||
|
|
x: 0.5, y: 1.5, w: 4.3, h: 0.8,
|
|||
|
|
fontSize: 40, align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('내화 시험성적서', {
|
|||
|
|
x: 0.5, y: 2.3, w: 4.3, h: 0.5,
|
|||
|
|
fontSize: 18, bold: true, color: colors.secondary,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText([
|
|||
|
|
{ text: '한국건설기술연구원 인증\n', options: { fontSize: 11, color: colors.darkGray } },
|
|||
|
|
{ text: '\n1시간 내화 성능 합격\n', options: { fontSize: 14, bold: true, color: colors.primary } },
|
|||
|
|
{ text: '\nKS F 2268-1 기준 충족\n', options: { fontSize: 10, color: colors.darkGray } },
|
|||
|
|
{ text: '인증번호: KFI-2024-1234', options: { fontSize: 9, color: colors.darkGray } }
|
|||
|
|
], {
|
|||
|
|
x: 0.7, y: 2.9, w: 3.9, h: 2,
|
|||
|
|
align: 'center', valign: 'top'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 인증서 카드 2
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 5.2, y: 1.2, w: 4.3, h: 3.8,
|
|||
|
|
fill: { color: colors.lightGray },
|
|||
|
|
line: { color: colors.primary, width: 1 }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('📜', {
|
|||
|
|
x: 5.2, y: 1.5, w: 4.3, h: 0.8,
|
|||
|
|
fontSize: 40, align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText('특허 등록증', {
|
|||
|
|
x: 5.2, y: 2.3, w: 4.3, h: 0.5,
|
|||
|
|
fontSize: 18, bold: true, color: colors.secondary,
|
|||
|
|
align: 'center'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
slide.addText([
|
|||
|
|
{ text: '대한민국 특허청\n', options: { fontSize: 11, color: colors.darkGray } },
|
|||
|
|
{ text: '\n하향식 스크린 방화셔터\n', options: { fontSize: 14, bold: true, color: colors.primary } },
|
|||
|
|
{ text: '\n특허 제10-2024-0056789호\n', options: { fontSize: 10, color: colors.darkGray } },
|
|||
|
|
{ text: '등록일: 2024.03.15', options: { fontSize: 9, color: colors.darkGray } }
|
|||
|
|
], {
|
|||
|
|
x: 5.4, y: 2.9, w: 3.9, h: 2,
|
|||
|
|
align: 'center', valign: 'top'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ 슬라이드 4: 품질 인증 생성 완료');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========================================
|
|||
|
|
// 슬라이드 5: 회사 소개
|
|||
|
|
// ========================================
|
|||
|
|
function createCompanySlide() {
|
|||
|
|
const slide = pptx.addSlide();
|
|||
|
|
|
|||
|
|
// 배경
|
|||
|
|
slide.background = { color: colors.white };
|
|||
|
|
|
|||
|
|
// 상단 바
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0, y: 0, w: 10, h: 0.8,
|
|||
|
|
fill: { color: colors.secondary }
|
|||
|
|
});
|
|||
|
|
slide.addText('회사 소개 및 연락처', {
|
|||
|
|
x: 0.5, y: 0.2, w: 5, h: 0.5,
|
|||
|
|
fontSize: 24, bold: true, color: colors.white
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 회사 로고 영역
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0.5, y: 1.2, w: 3, h: 1.5,
|
|||
|
|
fill: { color: colors.primary }
|
|||
|
|
});
|
|||
|
|
slide.addText('SAM\n방화셔터', {
|
|||
|
|
x: 0.5, y: 1.2, w: 3, h: 1.5,
|
|||
|
|
fontSize: 28, bold: true, color: colors.white,
|
|||
|
|
align: 'center', valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 회사 소개
|
|||
|
|
slide.addText([
|
|||
|
|
{ text: '㈜샘방화셔터\n\n', options: { fontSize: 20, bold: true, color: colors.secondary } },
|
|||
|
|
{ text: '20년 전통의 방화셔터 전문 제조기업\n', options: { fontSize: 12, color: colors.darkGray } },
|
|||
|
|
{ text: '국내 최고 품질의 철재 및 스크린 방화셔터 생산', options: { fontSize: 12, color: colors.darkGray } }
|
|||
|
|
], {
|
|||
|
|
x: 3.8, y: 1.2, w: 5.7, h: 1.5,
|
|||
|
|
valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 연락처 정보
|
|||
|
|
slide.addShape('rect', {
|
|||
|
|
x: 0.5, y: 3, w: 9, h: 2.3,
|
|||
|
|
fill: { color: colors.lightGray }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const contactInfo = [
|
|||
|
|
['📍 주소', '서울특별시 강서구 공항대로 123, SAM빌딩 5층'],
|
|||
|
|
['📞 전화', '02-1234-5678'],
|
|||
|
|
['📠 팩스', '02-1234-5679'],
|
|||
|
|
['📧 이메일', 'estimate@samfireshutter.co.kr'],
|
|||
|
|
['🌐 웹사이트', 'www.samfireshutter.co.kr']
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
slide.addTable(contactInfo, {
|
|||
|
|
x: 0.8, y: 3.2, w: 8.4, h: 1.9,
|
|||
|
|
fontSize: 12,
|
|||
|
|
color: colors.secondary,
|
|||
|
|
border: { pt: 0 },
|
|||
|
|
fill: { color: colors.lightGray },
|
|||
|
|
colW: [1.5, 6.9],
|
|||
|
|
align: 'left',
|
|||
|
|
valign: 'middle'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 담당자 정보
|
|||
|
|
slide.addText('담당: 박지민 대리 (영업지원팀) | 직통: 010-1234-5678', {
|
|||
|
|
x: 0.5, y: 5.35, w: 9, h: 0.25,
|
|||
|
|
fontSize: 10, color: colors.primary,
|
|||
|
|
align: 'right'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ 슬라이드 5: 회사 소개 생성 완료');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ========================================
|
|||
|
|
// 메인 실행
|
|||
|
|
// ========================================
|
|||
|
|
async function main() {
|
|||
|
|
console.log('🚀 방화셔터 견적서 PPTX 생성 시작\n');
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 슬라이드 생성
|
|||
|
|
createCoverSlide();
|
|||
|
|
createSummarySlide();
|
|||
|
|
createDetailSlide();
|
|||
|
|
createCertificateSlide();
|
|||
|
|
createCompanySlide();
|
|||
|
|
|
|||
|
|
// 파일 저장
|
|||
|
|
const outputPath = path.join(__dirname, 'pptx', 'fire_shutter_estimate.pptx');
|
|||
|
|
await pptx.writeFile({ fileName: outputPath });
|
|||
|
|
|
|||
|
|
console.log('\n🎉 PPTX 생성 완료!');
|
|||
|
|
console.log(`📁 저장 위치: ${outputPath}`);
|
|||
|
|
|
|||
|
|
// 파일 크기 확인
|
|||
|
|
const stats = fs.statSync(outputPath);
|
|||
|
|
console.log(`📏 파일 크기: ${(stats.size / 1024).toFixed(1)} KB`);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ 생성 실패:', error.message);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main();
|