From 202183d62159f195721f552b7aba199cbaee3977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Mon, 2 Feb 2026 18:04:37 +0900 Subject: [PATCH] =?UTF-8?q?fix:=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EB=A0=8C=EB=8D=94=EB=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20-=20=EA=B7=B8=EB=A3=B9=ED=95=AD=EB=AA=A9?= =?UTF-8?q?=20rowspan,=20=EC=B8=A1=EC=A0=95=EC=B9=98=20colspan=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EB=B3=84=20=EC=A0=81=EC=9A=A9,=20=ED=85=8C=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=ED=95=84=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.5 --- .../Admin/DocumentTemplateApiController.php | 8 +- .../views/document-templates/edit.blade.php | 158 ++++++++++++++++-- 2 files changed, 149 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php b/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php index 7cd64d08..33e947c9 100644 --- a/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php +++ b/app/Http/Controllers/Api/Admin/DocumentTemplateApiController.php @@ -24,6 +24,12 @@ public function index(Request $request): View $query = DocumentTemplate::query() ->withCount(['sections', 'columns']); + // 선택된 테넌트 필터 + $tenantId = session('selected_tenant_id'); + if ($tenantId) { + $query->where('tenant_id', $tenantId); + } + // 검색 if ($search = $request->input('search')) { $query->where(function ($q) use ($search) { @@ -39,7 +45,7 @@ public function index(Request $request): View } // 활성 상태 필터 - if ($request->has('is_active') && $request->input('is_active') !== '') { + if ($request->filled('is_active')) { $query->where('is_active', $request->boolean('is_active')); } diff --git a/resources/views/document-templates/edit.blade.php b/resources/views/document-templates/edit.blade.php index aedf15b5..8c8b68e7 100644 --- a/resources/views/document-templates/edit.blade.php +++ b/resources/views/document-templates/edit.blade.php @@ -962,7 +962,15 @@ function generatePreviewHtml() { headerRow2 += `${escapeHtml(sl)}`; }); } else { - headerRow1 += `${escapeHtml(col.label)}`; + const label = (col.label || '').trim(); + const isItem = label.includes('검사항목') || label.includes('항목'); + const isStd = label.includes('검사기준') || label.includes('기준'); + // 검사항목: 구분+항목명 2칸, 검사기준: 기준+공차 2칸 + if (isItem || isStd) { + headerRow1 += `${escapeHtml(col.label)}`; + } else { + headerRow1 += `${escapeHtml(col.label)}`; + } } }); if (hasComplex) { @@ -983,23 +991,141 @@ function generatePreviewHtml() { const colSpan = templateState.columns.reduce((sum, c) => sum + (c.column_type === 'complex' && c.sub_labels ? c.sub_labels.length : 1), 0) || 1; return `검사항목이 없습니다.`; } - return allItems.map((item, idx) => { - const cells = templateState.columns.map(col => { - if (col.column_type === 'complex' && col.sub_labels && col.sub_labels.length > 0) { - return col.sub_labels.map(() => '-').join(''); + + const getMethodName = (code) => { + const m = inspectionMethods.find(im => im.code === code); + return m ? m.name : (code || '-'); + }; + + // 카테고리별 그룹핑 + const rows = []; + let i = 0; + while (i < allItems.length) { + const item = allItems[i]; + const cat = (item.category || '').trim(); + if (cat) { + const grouped = [item]; + while (i + 1 < allItems.length && (allItems[i + 1].category || '').trim() === cat) { + i++; + grouped.push(allItems[i]); } - // 기본 컬럼: 검사항목 데이터 매칭 시도 - const label = (col.label || '').trim(); - let val = '-'; - if (label === 'NO' || label === 'no') val = idx + 1; - else if (label.includes('검사항목') || label.includes('항목')) val = escapeHtml(item.item || '-'); - else if (label.includes('검사기준') || label.includes('기준')) val = escapeHtml(item.standard || '-'); - else if (label.includes('검사방')) val = escapeHtml(item.method || '-'); - else if (label.includes('주기')) val = escapeHtml(item.frequency || '-'); - else if (label.includes('판정')) val = '-'; - return `${val}`; + rows.push({ type: 'group', category: cat, items: grouped }); + } else { + rows.push({ type: 'single', item: item }); + } + i++; + } + + // 측정치 셀 렌더링 (rowspan 포함, 그룹용) + const renderMeasurementCellsWithRowspan = (col, mType, rowspanN) => { + const rs = ` rowspan="${rowspanN}"`; + const subCount = col.sub_labels.length; + if (mType === 'checkbox') { + return col.sub_labels.map(() => `☐OK ☐NG`).join(''); + } else if (mType === 'numeric') { + return col.sub_labels.map(() => `___`).join(''); + } else if (mType === 'single_value') { + return `( 입력 )`; + } else if (mType === 'substitute') { + return `성적서로 대체`; + } + return col.sub_labels.map(() => `-`).join(''); + }; + + // 측정치 셀 렌더링 헬퍼 (단일 항목용) + const renderMeasurementCells = (col, mType) => { + const subCount = col.sub_labels.length; + if (mType === 'checkbox') { + return col.sub_labels.map(() => '☐OK ☐NG').join(''); + } else if (mType === 'numeric') { + return col.sub_labels.map(() => '___').join(''); + } else if (mType === 'single_value') { + return `( 입력 )`; + } else if (mType === 'substitute') { + return `성적서로 대체`; + } + return col.sub_labels.map(() => '-').join(''); + }; + + let rowNum = 0; + return rows.map(row => { + rowNum++; + if (row.type === 'single') { + // 단일 항목: 검사항목 colspan=2, 검사기준 colspan=2 + const item = row.item; + let cells = templateState.columns.map(col => { + if (col.column_type === 'complex' && col.sub_labels && col.sub_labels.length > 0) { + return renderMeasurementCells(col, item.measurement_type || ''); + } + const label = (col.label || '').trim(); + if (label === 'NO' || label === 'no') { + return `${rowNum}`; + } else if (label.includes('검사항목') || label.includes('항목')) { + return `${escapeHtml(item.item || '-')}`; + } else if (label.includes('검사기준') || label.includes('기준')) { + let std = item.standard || '-'; + if (item.tolerance) std += ' (' + item.tolerance + ')'; + return `${escapeHtml(std)}`; + } else if (label.includes('검사방')) { + return `${escapeHtml(getMethodName(item.method))}`; + } else if (label.includes('주기')) { + return `${escapeHtml(item.frequency || '-')}`; + } else if (label.includes('판정')) { + return `☐`; + } + return `-`; + }).join(''); + return `${cells}`; + } + + // 그룹 항목: 여러 행으로 렌더링 + const n = row.items.length; + return row.items.map((item, itemIdx) => { + let cells = ''; + cells += templateState.columns.map(col => { + if (col.column_type === 'complex' && col.sub_labels && col.sub_labels.length > 0) { + // 측정치: 첫 행만 rowspan + if (itemIdx === 0) { + return renderMeasurementCellsWithRowspan(col, row.items[0].measurement_type || '', n); + } + return ''; + } + const label = (col.label || '').trim(); + const rs = itemIdx === 0 ? ` rowspan="${n}"` : ''; + + if (label === 'NO' || label === 'no') { + // NO: 첫 행만, rowspan + return itemIdx === 0 + ? `${rowNum}` + : ''; + } else if (label.includes('검사항목') || label.includes('항목')) { + // 구분(rowspan) + 항목명(각 행) + let catCell = itemIdx === 0 + ? `${escapeHtml(row.category)}` + : ''; + return catCell + `${escapeHtml(item.item || '-')}`; + } else if (label.includes('검사기준') || label.includes('기준')) { + // 기준 + 공차 (각 행 개별) + return `${escapeHtml(item.standard || '-')}` + + `${escapeHtml(item.tolerance || '-')}`; + } else if (label.includes('검사방')) { + return itemIdx === 0 + ? `${escapeHtml(getMethodName(item.method))}` + : ''; + } else if (label.includes('주기')) { + return itemIdx === 0 + ? `${escapeHtml(item.frequency || '-')}` + : ''; + } else if (label.includes('판정')) { + // 판정: 첫 행만 rowspan + return itemIdx === 0 + ? `☐` + : ''; + } + return `-`; + }).join(''); + return `${cells}`; }).join(''); - return `${cells}`; }).join(''); };