feat:중간검사 성적서 템플릿 개선 (수주 LOT NO, 기준서 이미지, 섹션 타이틀)

- basic_fields에 수주 LOT NO 필드 추가
- 중간검사 기준서 이미지 섹션 추가 (4종 공통)
- 데이터 섹션 타이틀 "중간검사 DATA"로 통일
- 절곡품 4개 검사 섹션을 1개로 병합
- 미리보기에 ■ 섹션 타이틀 렌더링 (이미지/데이터 분리)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 16:01:52 +09:00
parent f24a084152
commit d6e2368f00
2 changed files with 59 additions and 106 deletions

View File

@@ -12,7 +12,7 @@
class MidInspectionTemplateSeeder extends Seeder
{
private int $tenantId = 1;
private int $tenantId = 287;
public function run(): void
{
@@ -65,7 +65,11 @@ private function jointbarTemplate(): array
'title' => '조인트바 - 중간 검사 성적서',
'sections' => [
[
'title' => '조인트바 검사 항목',
'title' => '중간검사 기준서',
'items' => [],
],
[
'title' => '중간검사 DATA',
'items' => [
[
'category' => '겉모양',
@@ -162,7 +166,11 @@ private function slatTemplate(): array
'title' => '슬랫 - 중간 검사 성적서',
'sections' => [
[
'title' => '철재스라트 (EGI 1.55T) 검사 항목',
'title' => '중간검사 기준서',
'items' => [],
],
[
'title' => '중간검사 DATA',
'items' => [
[
'category' => '겉모양',
@@ -244,7 +252,11 @@ private function screenTemplate(): array
'title' => '스크린 - 중간 검사 성적서',
'sections' => [
[
'title' => '스크린 검사 항목',
'title' => '중간검사 기준서',
'items' => [],
],
[
'title' => '중간검사 DATA',
'items' => [
[
'category' => '겉모양',
@@ -334,100 +346,27 @@ private function bendingTemplate(): array
'title' => '절곡품 - 중간 검사 성적서',
'sections' => [
[
'title' => '가이드레일 검사',
'items' => [
[
'category' => '겉모양',
'item' => '절곡상태',
'standard' => '양호/불량',
'method' => '육안검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '길이',
'standard' => '도면치수 ± 4mm',
'method' => '계측검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '너비',
'standard' => '도면치수 ± 4mm',
'method' => '계측검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '간격 (POINT별)',
'standard' => '도면치수 ± 2mm (벽면형 4P, 측면형 6P)',
'method' => '계측검사',
'frequency' => '전수',
],
],
'title' => '중간검사 기준서',
'items' => [],
],
[
'title' => '하단마감재 검사',
'title' => '중간검사 DATA',
'items' => [
[
'category' => '겉모양',
'item' => '절곡상태',
'standard' => '양호/불량',
'method' => '육안검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '너비',
'standard' => '60mm 기준',
'method' => '계측검사',
'frequency' => '전수',
],
],
],
[
'title' => '케이스(셔터박스) 검사',
'items' => [
[
'category' => '겉모양',
'item' => '절곡상태',
'standard' => '양호/불량',
'method' => '육안검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '높이/하단/너비차/위치',
'standard' => '도면치수 기준 (양면/밑면/후면)',
'method' => '계측검사',
'frequency' => '전수',
],
],
],
[
'title' => '하단 L-BAR / 연기차단재 검사',
'items' => [
[
'category' => '치수',
'item' => 'L-BAR 너비',
'standard' => '17mm 기준',
'method' => '계측검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '연기차단재 (가이드레일용)',
'standard' => '너비 50mm, 간격 12mm',
'method' => '계측검사',
'frequency' => '전수',
],
[
'category' => '치수',
'item' => '연기차단재 (케이스용)',
'standard' => '너비 80mm, 간격 12mm',
'method' => '계측검사',
'frequency' => '전수',
],
// 가이드레일
['category' => '가이드레일/겉모양', 'item' => '절곡상태', 'standard' => '양호/불량', 'method' => '육안검사', 'frequency' => '전수'],
['category' => '가이드레일/치수', 'item' => '길이', 'standard' => '도면치수 ± 4mm', 'method' => '계측검사', 'frequency' => '전수'],
['category' => '가이드레일/치수', 'item' => '너비', 'standard' => '도면치수 ± 4mm', 'method' => '계측검사', 'frequency' => '전수'],
['category' => '가이드레일/치수', 'item' => '간격 (POINT별)', 'standard' => '도면치수 ± 2mm (벽면형 4P, 측면형 6P)', 'method' => '계측검사', 'frequency' => '전수'],
// 하단마감재
['category' => '하단마감재/겉모양', 'item' => '절곡상태', 'standard' => '양호/불량', 'method' => '육안검사', 'frequency' => '전수'],
['category' => '하단마감재/치수', 'item' => '너비', 'standard' => '60mm 기준', 'method' => '계측검사', 'frequency' => '전수'],
// 케이스(셔터박스)
['category' => '케이스/겉모양', 'item' => '절곡상태', 'standard' => '양호/불량', 'method' => '육안검사', 'frequency' => '전수'],
['category' => '케이스/치수', 'item' => '높이/하단/너비차/위치', 'standard' => '도면치수 기준 (양면/밑면/후면)', 'method' => '계측검사', 'frequency' => '전수'],
// 하단 L-BAR / 연기차단재
['category' => 'L-BAR·연기차단재/치수', 'item' => 'L-BAR 너비', 'standard' => '17mm 기준', 'method' => '계측검사', 'frequency' => '전수'],
['category' => 'L-BAR·연기차단재/치수', 'item' => '연기차단재 (가이드레일용)', 'standard' => '너비 50mm, 간격 12mm', 'method' => '계측검사', 'frequency' => '전수'],
['category' => 'L-BAR·연기차단재/치수', 'item' => '연기차단재 (케이스용)', 'standard' => '너비 80mm, 간격 12mm', 'method' => '계측검사', 'frequency' => '전수'],
],
],
],
@@ -491,11 +430,12 @@ private function createBasicFields(int $templateId): void
$fields = [
['label' => '품명', 'field_type' => 'text', 'sort_order' => 1],
['label' => '규격', 'field_type' => 'text', 'sort_order' => 2],
['label' => '로트크기', 'field_type' => 'text', 'sort_order' => 3],
['label' => '발주처', 'field_type' => 'text', 'sort_order' => 4],
['label' => '현장명', 'field_type' => 'text', 'sort_order' => 5],
['label' => '검사일자', 'field_type' => 'date', 'sort_order' => 6],
['label' => '검사자', 'field_type' => 'text', 'sort_order' => 7],
['label' => '수주 LOT NO', 'field_type' => 'text', 'sort_order' => 3],
['label' => '로트크기', 'field_type' => 'text', 'sort_order' => 4],
['label' => '발주처', 'field_type' => 'text', 'sort_order' => 5],
['label' => '현장명', 'field_type' => 'text', 'sort_order' => 6],
['label' => '검사자', 'field_type' => 'date', 'sort_order' => 7],
['label' => '검사자', 'field_type' => 'text', 'sort_order' => 8],
];
foreach ($fields as $field) {

View File

@@ -252,14 +252,26 @@ function buildDocumentPreviewHtml(data) {
}).join('');
};
// 섹션 이미지
const imagesHtml = sections.filter(s => s.image_path).map(s => `
<div class="mb-4 text-center">
<p class="text-xs font-medium text-gray-600 mb-1">${_previewEsc(s.title || '검사 기준')}</p>
<img src="/storage/${s.image_path}" alt="${_previewEsc(s.title)}" class="max-h-40 mx-auto border rounded">
// 섹션 이미지 섹션(items 없음)과 데이터 섹션(items 있음)으로 분리
const imageSections = sections.filter(s => (s.items || []).length === 0);
const dataSections = sections.filter(s => (s.items || []).length > 0);
// 이미지 섹션: ■ 타이틀 + 이미지 또는 플레이스홀더
const imageSectionsHtml = imageSections.map(s => `
<div class="mb-3">
<p class="text-sm font-bold mb-1">■ ${_previewEsc(s.title)}</p>
${s.image_path
? `<img src="/storage/${s.image_path}" alt="${_previewEsc(s.title)}" class="max-h-48 mx-auto border rounded">`
: `<div class="border border-dashed border-gray-300 rounded p-6 text-center text-gray-400 text-xs">이미지 미등록 (편집에서 업로드)</div>`
}
</div>
`).join('');
// 데이터 섹션 타이틀
const dataSectionTitle = dataSections.length > 0
? `<p class="text-sm font-bold mb-1 mt-3">■ ${_previewEsc(dataSections[0].title)}</p>`
: '';
return `
<div class="bg-white p-6" style="font-family: 'Malgun Gothic', sans-serif; font-size: 12px; max-width: 900px; margin: 0 auto;">
<div class="flex justify-between items-start mb-4">
@@ -273,8 +285,9 @@ function buildDocumentPreviewHtml(data) {
<div>${approvalHtml}</div>
</div>
${basicHtml}
${imagesHtml}
${imageSectionsHtml}
${columns.length > 0 ? `
${dataSectionTitle}
<table class="w-full border-collapse text-xs">
<thead>${renderHeaders()}</thead>
<tbody>${renderRows()}</tbody>