1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
"use client"
import * as ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';
import { toast } from 'sonner';
/**
* PQ 기준 Excel 템플릿을 다운로드하는 함수 (exceljs 사용)
* @param isProjectSpecific 프로젝트별 PQ 템플릿 여부
*/
export async function exportPqTemplate(isProjectSpecific: boolean = false) {
try {
// 워크북 생성
const workbook = new ExcelJS.Workbook();
// 워크시트 생성
const sheetName = isProjectSpecific ? "Project PQ Template" : "General PQ Template";
const worksheet = workbook.addWorksheet(sheetName);
// 그룹 옵션 정의 - 드롭다운 목록에 사용
const groupOptions = [
"GENERAL",
"Quality Management System",
"Workshop & Environment",
"Warranty",
];
// 일반 PQ 필드 (기본 필드)
const basicFields = [
{ header: "Code", key: "code", width: 90 },
{ header: "Check Point", key: "checkPoint", width: 180 },
{ header: "Group Name", key: "groupName", width: 150 },
{ header: "Description", key: "description", width: 240 },
{ header: "Remarks", key: "remarks", width: 180 },
];
// 프로젝트별 PQ 추가 필드
const projectFields = isProjectSpecific
? [
{ header: "Contract Info", key: "contractInfo", width: 180 },
{ header: "Additional Requirements", key: "additionalRequirement", width: 240 },
]
: [];
// 모든 필드 합치기
const fields = [...basicFields, ...projectFields];
// 지침 행 추가
const instructionTitle = worksheet.addRow(["Instructions:"]);
instructionTitle.font = { bold: true, size: 12 };
worksheet.mergeCells(1, 1, 1, fields.length);
const instructions = [
"1. 'Code' 필드는 고유해야 합니다 (예: 1-1, A.2.3).",
"2. 'Check Point'는 필수 항목입니다.",
"3. 'Group Name'은 드롭다운 목록에서 선택하세요: GENERAL, Quality Management System, Workshop & Environment, Warranty",
"4. 여러 줄 텍스트는 \\n으로 줄바꿈을 표시합니다.",
"5. 아래 회색 배경의 예시 행은 참고용입니다. 실제 데이터 입력 전에 이 행을 수정하거나 삭제해야 합니다.",
];
// 프로젝트별 PQ일 경우 추가 지침
if (isProjectSpecific) {
instructions.push(
"6. 'Contract Info'와 'Additional Requirements'는 프로젝트별 세부 정보를 위한 필드입니다."
);
}
// 지침 행 추가
instructions.forEach((instruction, idx) => {
const row = worksheet.addRow([instruction]);
worksheet.mergeCells(idx + 2, 1, idx + 2, fields.length);
row.font = { color: { argb: '00808080' } };
});
// 빈 행 추가
worksheet.addRow([]);
// 헤더 행 추가
const headerRow = worksheet.addRow(fields.map(field => field.header));
headerRow.font = { bold: true, color: { argb: 'FFFFFFFF' } };
headerRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FF4472C4' }
};
headerRow.alignment = { vertical: 'middle', horizontal: 'center' };
// 예시 행 표시를 위한 첫 번째 열 값 수정
const exampleData: Record<string, string> = {
code: "[예시 - 수정/삭제 필요] 1-1",
checkPoint: "Selling / 1 year Property",
groupName: "GENERAL",
description: "Address :\nTel. / Fax :\ne-mail :",
remarks: "Optional remarks",
};
// 프로젝트별 PQ인 경우 예시 데이터에 추가 필드 추가
if (isProjectSpecific) {
exampleData.contractInfo = "Contract details for this project";
exampleData.additionalRequirement = "Additional technical requirements";
}
const exampleRow = worksheet.addRow(fields.map(field => exampleData[field.key] || ""));
exampleRow.font = { italic: true };
exampleRow.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFEDEDED' }
};
// 예시 행 첫 번째 셀에 코멘트 추가
const codeCell = worksheet.getCell(exampleRow.number, 1);
codeCell.note = '이 예시 행은 참고용입니다. 실제 데이터 입력 전에 수정하거나 삭제하세요.';
// Group Name 열 인덱스 찾기 (0-based)
const groupNameIndex = fields.findIndex(field => field.key === "groupName");
// 열 너비 설정
fields.forEach((field, index) => {
const column = worksheet.getColumn(index + 1);
column.width = field.width / 6.5; // ExcelJS에서는 픽셀과 다른 단위 사용
});
// 각 셀에 테두리 추가
const headerRowNum = instructions.length + 3;
const exampleRowNum = headerRowNum + 1;
for (let i = 1; i <= fields.length; i++) {
// 헤더 행에 테두리 추가
worksheet.getCell(headerRowNum, i).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
// 예시 행에 테두리 추가
worksheet.getCell(exampleRowNum, i).border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
}
// 사용자 입력용 빈 행 추가 (10개)
for (let rowIdx = 0; rowIdx < 10; rowIdx++) {
// 빈 행 추가
const emptyRow = worksheet.addRow(Array(fields.length).fill(''));
const currentRowNum = exampleRowNum + 1 + rowIdx;
// 각 셀에 테두리 추가
for (let colIdx = 1; colIdx <= fields.length; colIdx++) {
const cell = worksheet.getCell(currentRowNum, colIdx);
cell.border = {
top: { style: 'thin' },
left: { style: 'thin' },
bottom: { style: 'thin' },
right: { style: 'thin' }
};
// Group Name 열에 데이터 유효성 검사 (드롭다운) 추가
if (colIdx === groupNameIndex + 1) {
cell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`"${groupOptions.join(',')}"`],
showErrorMessage: true,
errorStyle: 'error',
error: '유효하지 않은 그룹입니다',
errorTitle: '입력 오류',
prompt: '목록에서 선택하세요',
promptTitle: '그룹 선택'
};
}
}
}
// 예시 행이 있는 열에도 Group Name 드롭다운 적용
const exampleGroupCell = worksheet.getCell(exampleRowNum, groupNameIndex + 1);
exampleGroupCell.dataValidation = {
type: 'list',
allowBlank: true,
formulae: [`"${groupOptions.join(',')}"`],
showErrorMessage: true,
errorStyle: 'error',
error: '유효하지 않은 그룹입니다',
errorTitle: '입력 오류',
prompt: '목록에서 선택하세요',
promptTitle: '그룹 선택'
};
// 워크북을 Excel 파일로 변환
const buffer = await workbook.xlsx.writeBuffer();
// 파일명 설정 및 저장
const fileName = isProjectSpecific ? "project-pq-template.xlsx" : "general-pq-template.xlsx";
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
saveAs(blob, fileName);
toast.success(`${isProjectSpecific ? '프로젝트별' : '일반'} PQ 템플릿이 다운로드되었습니다.`);
} catch (error) {
console.error("템플릿 다운로드 중 오류 발생:", error);
toast.error("템플릿 다운로드 중 오류가 발생했습니다.");
}
}
|