/** * 이미지 처리 관련 기능을 담당하는 모듈 * 하단마감재 이미지 업데이트, 붙여넣기, 확대 등 모든 이미지 관련 기능을 처리 */ const ImageHandler = (function() { 'use strict'; // 전역 변수 let bottombarImages = []; let originalImage = null; let hoverTimeout = null; let miniSummaryTimeout = null; /** * 모듈 초기화 * @param {Array} images - bottombar.json에서 로드된 이미지 배열 */ function init(images = []) { bottombarImages = images; console.log('ImageHandler 초기화됨:', bottombarImages.length, '개의 이미지 로드됨'); // 이벤트 리스너 등록 attachEventListeners(); } /** * 이벤트 리스너 등록 */ function attachEventListeners() { // 이미지 호버 확대 기능 $(document).on('mouseenter', '.search-zoomable-image', handleImageHover); $(document).on('mouseleave', '.search-zoomable-image', handleImageLeave); // 이미지 팝업 오버레이 이벤트 $('#imagePopupOverlay').on('mouseenter', function() { clearTimeout(hoverTimeout); }); $('#imagePopupOverlay').on('mouseleave', function() { $('#imagePopupOverlay').fadeOut(200); }); // 모달 클릭 시 닫기 $('#imageModal').on('click', function() { $(this).fadeOut(300); }); // ESC 키로 모달 닫기 $(document).on('keydown', function(e) { if (e.key === 'Escape' && $('#imageModal').is(':visible')) { $('#imageModal').fadeOut(300); } }); // 미니 합계표 팝업 기능 $(document).on('mouseenter', '.mini-summary-trigger', handleMiniSummaryHover); $(document).on('mouseleave', '.mini-summary-trigger', handleMiniSummaryLeave); // 미니 합계표 팝업 이벤트 $('#miniSummaryPopup').on('mouseenter', function() { clearTimeout(miniSummaryTimeout); }); $('#miniSummaryPopup').on('mouseleave', function() { $(this).fadeOut(200); }); } /** * 이미지 호버 시 확대 팝업 표시 */ function handleImageHover(e) { e.stopPropagation(); const imgSrc = $(this).attr('data-src') || $(this).attr('src'); clearTimeout(hoverTimeout); const mouseX = e.clientX; const mouseY = e.clientY; const popupOffsetX = 20; const popupOffsetY = 20; const popupWidth = 300; const popupHeight = 300; const windowWidth = $(window).width(); const windowHeight = $(window).height(); let finalX = mouseX + popupOffsetX; let finalY = mouseY + popupOffsetY; if (finalX + popupWidth > windowWidth) { finalX = mouseX - popupWidth - popupOffsetX; } if (finalY + popupHeight > windowHeight) { finalY = mouseY - popupHeight - popupOffsetY; } $('#imagePopupOverlay').css({ 'position': 'fixed', 'top': finalY + 'px', 'left': finalX + 'px', 'width': popupWidth + 'px', 'height': popupHeight + 'px', 'background': 'white', 'border': '2px solid #007bff', 'border-radius': '8px', 'box-shadow': '0 4px 20px rgba(0,0,0,0.3)', 'z-index': '10000', 'display': 'none' }); $('#popupImage').attr('src', imgSrc); $('#imagePopupOverlay').fadeIn(200); } /** * 이미지 호버 종료 시 팝업 닫기 */ function handleImageLeave(e) { hoverTimeout = setTimeout(function() { $('#imagePopupOverlay').fadeOut(200); }, 100); } /** * 미니 합계표 호버 시 팝업 표시 */ function handleMiniSummaryHover(e) { e.stopPropagation(); const json = $(this).attr('data-summary'); if (!json) return; let data; try { data = JSON.parse(json); } catch (err) { return; } // 데이터 파싱 및 검증 let inputList = parseJsonData(data.inputList); let bendingrateList = parseJsonData(data.bendingrateList); let sumList = parseJsonData(data.sumList); let colorList = parseJsonData(data.colorList); let AList = parseJsonData(data.AList); let calcAfterList = parseJsonData(data.calcAfterList); // 미니 테이블 HTML 생성 const html = generateMiniSummaryTable(inputList, bendingrateList, sumList, colorList, AList, calcAfterList); clearTimeout(miniSummaryTimeout); // 팝업 위치 설정 const mouseX = e.clientX, mouseY = e.clientY; const popupWidth = 400, popupHeight = 200; let left = mouseX - popupWidth - 20, top = mouseY - 20; if(left < 10) left = mouseX + 20; if(top + popupHeight > window.innerHeight) top = mouseY - popupHeight - 20; if(top < 10) top = 10; $('#miniSummaryPopup').html(html).css({ 'left': left + 'px', 'top': top + 'px', 'display': 'block' }); } /** * 미니 합계표 호버 종료 시 팝업 닫기 */ function handleMiniSummaryLeave(e) { miniSummaryTimeout = setTimeout(function() { $('#miniSummaryPopup').fadeOut(200); }, 100); } /** * JSON 데이터 파싱 및 검증 */ function parseJsonData(data) { if (Array.isArray(data)) { return data; } if (typeof data === 'string') { try { const parsed = JSON.parse(data); return Array.isArray(parsed) ? parsed : []; } catch (e) { console.warn('JSON 파싱 실패:', e); return []; } } return []; } /** * 미니 합계표 HTML 생성 */ function generateMiniSummaryTable(inputList, bendingrateList, sumList, colorList, AList, calcAfterList) { let html = ''; html += ''; for(let i=1; i<=inputList.length; i++) html += ``; html += ''; html += ''; inputList.forEach(v=>html+=``); html += ''; html += ''; bendingrateList.forEach(v=>html+=``); html += ''; html += ''; calcAfterList.forEach(v=>html+=``); html += ''; html += ''; sumList.forEach(v=>html+=``); html += ''; html += ''; colorList.forEach(v=>html+=``); html += ''; html += ''; AList.forEach(v=>html+=``); html += ''; html += '
번호${i}
입력${v}
연신율${v}
연신율계산 후${v}
합계${v}
음영${v?'음영':''}
A각 표시${v?'A각':''}
'; return html; } /** * 이미지 업데이트 함수 * @param {string} model - 모델명 * @param {string} checkType - 형태 * @param {string} finishingType - 마감 */ function updateImage(model, checkType, finishingType) { let imagePath = ''; console.log('ImageHandler.updateImage 호출:', {model, checkType, finishingType}); console.log('bottombarImages:', bottombarImages); const searchKeyword = $('#search_keyword').val() || ''; console.log('search_keyword:', searchKeyword); if (Array.isArray(bottombarImages)) { let found = false; // 경우의 수 1: search_keyword가 있고, JSON에도 search_keyword가 있는 경우 (가장 우선) if(searchKeyword !== ''){ found = bottombarImages.find(function(item) { return item.search_keyword && item.search_keyword === searchKeyword; }); if (found) { imagePath = found.image; } } // 경우의 수 2: search_keyword로 찾지 못했거나 없는 경우, model_name + check_type + finishing_type으로 검색 if(!found && model && checkType && finishingType){ found = bottombarImages.find(function(item) { return item.model_name && item.model_name === model && item.check_type && item.check_type === checkType && item.finishing_type && item.finishing_type === finishingType; }); if (found) { imagePath = found.image; } } // 경우의 수 3: model_name + check_type으로만 검색 (finishing_type 무시) if(!found && model && checkType){ found = bottombarImages.find(function(item) { return item.model_name && item.model_name === model && item.check_type && item.check_type === checkType; }); if (found) { imagePath = found.image; } } // 경우의 수 4: model_name만으로 검색 (check_type, finishing_type 무시) if(!found && model){ found = bottombarImages.find(function(item) { return item.model_name && item.model_name === model; }); if (found) { imagePath = found.image; } } // 경우의 수 5: check_type + finishing_type으로 검색 (model_name 무시) if(!found && checkType && finishingType){ found = bottombarImages.find(function(item) { return item.check_type && item.check_type === checkType && item.finishing_type && item.finishing_type === finishingType; }); if (found) { imagePath = found.image; } } } // 이미지 경로 적용 applyImageToElements(imagePath); } /** * 이미지를 여러 요소에 적용 */ function applyImageToElements(imagePath) { // checkImage 엘리먼트에 적용 if ($('#checkImage').length) { $('#checkImage').attr('src', imagePath); } // checkImageFlat 엘리먼트에 적용 if ($('#checkImageFlat').length) { $('#checkImageFlat').attr('src', imagePath); } // previewContainer 엘리먼트에 적용 if ($('#previewContainer').length && imagePath) { $('#previewContainer').empty(); const img = $('').attr({ 'src': imagePath, 'alt': '하단마감재 이미지', 'style': 'max-width: 100%; height: auto;' }); $('#previewContainer').append(img); console.log('updateImage 함수에서 previewContainer에 이미지 적용:', imagePath); } } /** * 하단마감재 데이터를 모달창에 적용 */ function applybottombarData(bottombarData) { console.log('하단마감재 데이터 적용:', bottombarData); // 대분류 라디오 버튼 설정 if (bottombarData.firstitem) { $(`input[name="firstitem"][value="${bottombarData.firstitem}"]`).prop('checked', true); } // 인정/비인정 라디오 버튼 설정 if (bottombarData.UA) { $(`input[name="model_UA"][value="${bottombarData.UA}"]`).prop('checked', true); } // 제품모델 선택 if (bottombarData.model_name) { $('#model_name').val(bottombarData.model_name); } // 형태 라디오 버튼 설정 if (bottombarData.check_type) { $(`input[name="check_type"][value="${bottombarData.check_type}"]`).prop('checked', true); } // 마감 선택 if (bottombarData.finishing_type) { $('#finishing_type').val(bottombarData.finishing_type); } // 품목검색어 설정 if (bottombarData.search_keyword) { $('#search_keyword').val(bottombarData.search_keyword); } // 이미지 업데이트 if (bottombarData.image) { applyImageToElements(bottombarData.image); } // 모델 변경 이벤트 트리거 (이미지 업데이트를 위해) if (bottombarData.model_name || bottombarData.check_type || bottombarData.finishing_type) { setTimeout(function() { $('#model_name, input[name="check_type"], #finishing_type').trigger('change'); }, 100); } console.log('하단마감재 데이터 적용 완료'); } /** * 이미지 붙여넣기 기능 초기화 */ function initPasteImage() { setTimeout(function() { const previewContainer = document.getElementById('previewContainer'); if (previewContainer) { // 중복 방지: 기존 이벤트 제거 const newPreviewContainer = previewContainer.cloneNode(true); previewContainer.parentNode.replaceChild(newPreviewContainer, previewContainer); const container = newPreviewContainer; // 클릭 시 포커스 container.addEventListener('click', function() { this.focus(); console.log('previewContainer 포커스됨'); }); // Ctrl+V 붙여넣기 이벤트 container.addEventListener('keydown', function(event) { console.log('키 이벤트 발생:', event.key, 'Ctrl:', event.ctrlKey); if ((event.ctrlKey || event.metaKey) && event.key === 'v') { event.preventDefault(); console.log('Ctrl+V 감지됨'); // 조회모드가 아니면 붙여넣기 허용 if ($("#mode").val() !== 'view') { console.log('붙여넣기 시도...'); handlePasteImage(); } else { console.log('조회 모드에서는 붙여넣기 불가'); } } }); // 안내 메시지 if ($("#mode").val() !== 'view') { container.style.cursor = 'pointer'; container.title = 'Ctrl+V로 이미지를 붙여넣을 수 있습니다'; console.log('previewContainer 이벤트 바인딩 완료'); } } else { console.error('previewContainer를 찾을 수 없습니다.'); } }, 300); } /** * 이미지 붙여넣기 처리 */ function handlePasteImage() { if (navigator.clipboard && navigator.clipboard.read) { navigator.clipboard.read().then(data => { console.log('클립보드 데이터:', data); for (let item of data) { if (item.types.includes('image/png') || item.types.includes('image/jpeg') || item.types.includes('image/jpg') || item.types.includes('image/gif') || item.types.includes('image/webp')) { console.log('이미지 타입 발견:', item.types); item.getType('image/png').then(blob => { const url = URL.createObjectURL(blob); pasteImageToPreviewContainer(url); }).catch(() => { item.getType('image/jpeg').then(blob => { const url = URL.createObjectURL(blob); pasteImageToPreviewContainer(url); }).catch(() => { item.getType('image/jpg').then(blob => { const url = URL.createObjectURL(blob); pasteImageToPreviewContainer(url); }); }); }); return; } } alert('클립보드에 이미지가 없습니다.'); }).catch(err => { console.error('클립보드 접근 오류:', err); alert('클립보드 접근 오류: ' + err); }); } else { alert('이 브라우저는 이미지 붙여넣기를 지원하지 않습니다.'); } } /** * 붙여넣은 이미지를 표시하는 함수 */ function pasteImageToPreviewContainer(imageUrl) { console.log('이미지 붙여넣기 실행:', imageUrl); const previewContainer = document.getElementById('previewContainer'); if (!previewContainer) { console.error('previewContainer를 찾을 수 없습니다.'); return; } // previewContainer 내의 모든 이미지 제거 (기존 이미지 + 미러링 이미지 등) const allImages = previewContainer.querySelectorAll('img'); allImages.forEach(function(img) { img.remove(); console.log('이미지 제거됨:', img.id || 'id없음'); }); // 새 이미지 추가 const img = document.createElement('img'); img.src = imageUrl; img.alt = '붙여넣은 이미지'; // 그리기 캔버스와 동일한 크기로 설정 (400px x 300px) img.style.width = '400px'; img.style.height = '300px'; img.style.maxWidth = '100%'; img.style.maxHeight = '100%'; img.style.objectFit = 'contain'; img.className = 'img-fluid'; img.id = 'currentImage'; img.onload = function() { console.log('이미지 로드 완료'); }; img.onerror = function() { console.error('이미지 로드 실패'); }; previewContainer.appendChild(img); console.log('새 이미지 추가됨'); // originalImage 업데이트 originalImage = imageUrl; } /** * 이미지 찾기 함수 */ function findImageByConditions(items, conditions) { for (const item of items) { let matched = true; for (const [key, value] of Object.entries(conditions)) { if (empty(item[key]) || item[key] !== value) { matched = false; break; } } if (matched && !empty(item.image)) { return `이미지`; } } return ''; } /** * 값이 비어있는지 확인 */ function empty(value) { return value === null || value === undefined || value === ''; } /** * 모듈 정리 */ function cleanup() { // 이벤트 리스너 제거 $(document).off('mouseenter mouseleave', '.search-zoomable-image'); $(document).off('mouseenter mouseleave', '.mini-summary-trigger'); $('#imagePopupOverlay').off('mouseenter mouseleave'); $('#imageModal').off('click'); $('#miniSummaryPopup').off('mouseenter mouseleave'); // 타임아웃 클리어 if (hoverTimeout) { clearTimeout(hoverTimeout); hoverTimeout = null; } if (miniSummaryTimeout) { clearTimeout(miniSummaryTimeout); miniSummaryTimeout = null; } console.log('ImageHandler 정리 완료'); } // 공개 API return { init: init, updateImage: updateImage, applybottombarData: applybottombarData, initPasteImage: initPasteImage, pasteImageToPreviewContainer: pasteImageToPreviewContainer, findImageByConditions: findImageByConditions, cleanup: cleanup }; })(); // 전역 함수로 노출 (기존 코드와의 호환성을 위해) window.pasteImageToPreviewContainer = ImageHandler.pasteImageToPreviewContainer; window.updateImage = ImageHandler.updateImage; window.applybottombarData = ImageHandler.applybottombarData; window.findImageByConditions = ImageHandler.findImageByConditions;