// 거주인 녹취정보 상세 페이지 JavaScript // 외부 의존성: jQuery, Bootstrap 5, SweetAlert2, Naver Maps, Dropzone 6.0, Sortable.js 1.15 var map; var dz = null; // Dropzone 인스턴스 var sortable = null; // Sortable 인스턴스 $(function () { // trade_type_onchange(); if (isDefined(window.rcpt_hscp_nm)) { $(".spc").hide(); } map = new naver.maps.Map('mapArea', { center: new naver.maps.LatLng(window.lat, window.lng), useStyleMap: true, zoom: 17, minZoom: 10, mapTypeControl: true, mapTypeControlOptions: { style: naver.maps.MapTypeControlStyle.BUTTON, position: naver.maps.Position.TOP_LEFT }, zoomControl: true, zoomControlOptions: { position: naver.maps.Position.TOP_RIGHT } }); marker = new naver.maps.Marker({ position: new naver.maps.LatLng(window.lat, window.lng), map: map }); // Dropzone auto discover 비활성화 Dropzone.autoDiscover = false; // 파일업로드 open $("#btnUploadModal").on("click", function () { $("#uploadModal").modal("show"); }); }); //공백 값 체크 function isDefined(str) { var isResult = false; str_temp = str + ""; str_temp = str_temp.replace(" ", ""); if (str_temp != "undefined" && str_temp != "" && str_temp != "null") { isResult = true; } return isResult; } // 부모 창에서 삭제된 이미지 제거하는 함수 function removeImageFromParent(imgType, imgSq) { console.log('[removeImageFromParent] 이미지 제거:', imgType, imgSq); // 이미지 타입에 따라 다른 처리 switch(imgType) { case 'I1': // 홍보확인서 - 단일 이미지를 photo.gif로 변경 $('#photo-display2_I1').attr('src', '/plugin/img/photo.gif') .parent('a').replaceWith('홍보확인서'); break; case 'I8': // 분양권 - 해당 div 제거 $('img[src*="' + imgSq + '"]').closest('div[style*="width: 150px"]').remove(); break; case 'I4': // 매물사진 - 해당 thumb-card 제거 $('input[name="i4_seq[]"][value="' + imgSq + '"]').closest('.thumb-card').remove(); break; case 'I9': // 360이미지 - 해당 thumb-card 제거 $('input[name^="img_location_' + imgSq + '"]').closest('.thumb-card').remove(); break; case 'I10': // 촬영동의서 - 이미지를 photo.gif로 변경 $('img[alt="촬영동의서"]').attr('src', '/plugin/img/photo.gif') .parent('a').replaceWith('촬영동의서'); break; case 'I2': // 현장확인내역서 - 이미지를 photo.gif로 변경 $('img[alt="현장확인내역서"]').attr('src', '/plugin/img/photo.gif') .parent('a').replaceWith('현장확인내역서'); break; case 'V1': // 동영상 - 이미지를 photo.gif로 변경 $('img[alt="동영상"]').attr('src', '/plugin/img/photo.gif') .parent('a').replaceWith('동영상'); break; case 'I5': // 평면도 - 이미지를 photo.gif로 변경 $('img[alt="평면도"]').attr('src', '/plugin/img/photo.gif') .parent('a').replaceWith('평면도'); break; case 'I11': // 체크리스트 - 이미지를 photo.gif로 변경 $('img[alt="체크리스트"]').attr('src', '/plugin/img/photo.gif') .parent('a').replaceWith('체크리스트'); break; default: console.log('[removeImageFromParent] 알 수 없는 이미지 타입:', imgType); } } // 부모 창에 업로드된 이미지 추가/업데이트하는 함수 function updateImageInParent(imgType, imageData) { console.log('[updateImageInParent] 이미지 업데이트:', imgType, imageData); if (!imageData || !imageData.img_path || !imageData.img_filenm) { console.error('[updateImageInParent] 이미지 데이터 부족:', imageData); return; } // 이미지 URL 생성 const imgPath = imageData.img_path + imageData.img_filenm; const thumbPath = imageData.img_path + imageData.img_filenm.replace(/\.\w+$/, '_thumb.jpg'); // 클라우드 URL 처리 const NCLOUD_URL = 'https://kr.object.ncloudstorage.com/confirms-object/'; const fullImageUrl = imageData.cloud_upload_yn === 'Y' ? NCLOUD_URL + imgPath : imgPath; const thumbImageUrl = imageData.cloud_upload_yn === 'Y' ? NCLOUD_URL + thumbPath : thumbPath; // 이미지 타입에 따라 다른 처리 switch(imgType) { case 'I1': // 홍보확인서 - 단일 이미지 업데이트 const $i1Container = $('#photo-display2_I1').closest('.ratio'); $i1Container.html(` 홍보확인서 `); break; case 'I10': // 촬영동의서 const $i10Container = $('img[alt="촬영동의서"]').closest('.ratio'); $i10Container.html(` 촬영동의서 `); break; case 'I2': // 현장확인내역서 const $i2Container = $('img[alt="현장확인내역서"]').closest('.ratio'); $i2Container.html(` 현장확인내역서 `); break; case 'I8': // 분양권 - 새 이미지 추가 const $i8Container = $('img[alt="분양권"]').closest('.d-flex'); // photo.gif인 placeholder 제거 $i8Container.find('img[src="/plugin/img/photo.gif"]').closest('div[style*="width: 150px"]').remove(); // 새 이미지 추가 const i8Html = `
분양권
`; $i8Container.append(i8Html); break; case 'I4': // 매물사진 - 새 thumb-card 추가 const $i4Container = $('input[name="i4_order[]"]').closest('.d-flex'); // photo.gif인 placeholder 제거 $i4Container.find('img[src="/plugin/img/photo.gif"]').closest('.thumb-card').remove(); // 새 thumb-card 추가 const i4Html = `
매물사진
`; $i4Container.append(i4Html); break; case 'I9': // 360이미지 - 새 thumb-card 추가 console.log('[updateImageInParent] I9 처리 시작', imageData); // 360이미지 컨테이너 찾기 let $i9Container = $('.fw-semibold:contains("360이미지")').closest('.border').find('.d-flex.flex-wrap'); console.log('[updateImageInParent] I9 컨테이너 찾음:', $i9Container.length); if ($i9Container.length === 0) { console.error('[updateImageInParent] I9 컨테이너를 찾을 수 없습니다!'); break; } // placeholder 제거 (photo.gif가 있으면) const $placeholder = $i9Container.find('img[src="/plugin/img/photo.gif"]').closest('.thumb-card'); if ($placeholder.length > 0) { console.log('[updateImageInParent] I9 placeholder 제거'); $placeholder.remove(); } // 새 이미지 추가 const i9Html = `
360이미지
`; console.log('[updateImageInParent] I9 이미지 추가'); $i9Container.append(i9Html); break; case 'V1': // 동영상 const $v1Container = $('img[alt="동영상"]').closest('.ratio'); $v1Container.html(` 동영상 `); break; case 'I5': // 평면도 const $i5Container = $('img[alt="평면도"]').closest('.ratio'); $i5Container.html(` 평면도 `); break; case 'I11': // 체크리스트 const $i11Container = $('img[alt="체크리스트"]').closest('.ratio'); $i11Container.html(` 체크리스트 `); break; default: console.log('[updateImageInParent] 알 수 없는 이미지 타입:', imgType); } } // AJAX로 이미지 리스트 전체 불러오기 var allImagesCache = []; // 이미지 데이터 캠시 function loadAllImages(rsrvSq) { console.log('[loadAllImages] 시작, rsrv_sq:', rsrvSq); if (!rsrvSq) { console.warn('[loadAllImages] rsrv_sq가 없음'); return; } $.ajax({ url: '/article/receipt/getImages', method: 'GET', data: { rsrv_sq: rsrvSq }, success: function(response) { console.log('[loadAllImages] 응답:', response); if (response.success && response.images) { const images = response.images; allImagesCache = images; // 캠시에 저장 console.log('[loadAllImages] 이미지 개수:', images.length); // 이미지 타입별로 렌더링 renderImagesByType('I1', images); // 홍보확인서 renderImagesByType('I10', images); // 촬영동의서 renderImagesByType('I2', images); // 현장확인내역서 renderImagesByType('I8', images); // 분양권 renderImagesByType('I4', images); // 매물사진 renderImagesByType('V1', images); // 동영상 renderImagesByType('I5', images); // 평면도 renderImagesByType('I11', images); // 체크리스트 renderImagesByType('I9', images); // 360이미지 console.log('[loadAllImages] 렌더링 완료'); } else { console.error('[loadAllImages] 응답 오류:', response.msg || '알 수 없음'); } }, error: function(xhr, status, error) { console.error('[loadAllImages] AJAX 오류:', error); } }); } // 이미지 타입별로 렌더링 function renderImagesByType(imgType, allImages) { const images = allImages.filter(img => img.img_type === imgType); console.log('[renderImagesByType]', imgType, ':', images.length, '개'); switch(imgType) { case 'I1': // 홍보확인서 (단일) renderSingleImage(images[0], 'photo-display2_I1', '홍보확인서'); break; case 'I10': // 촬영동의서 (단일) renderSingleImage(images[0], null, '촬영동의서'); break; case 'I2': // 현장확인내역서 (단일) renderSingleImage(images[0], null, '현장확인내역서'); break; case 'V1': // 동영상 (단일) renderSingleImage(images[0], null, '동영상'); break; case 'I5': // 평면도 (단일) renderSingleImage(images[0], null, '평면도'); break; case 'I11': // 체크리스트 (단일) renderSingleImage(images[0], null, '체크리스트'); break; case 'I8': // 분양권 (다중) renderMultipleImages(images, 'I8', '분양권'); break; case 'I4': // 매물사진 (다중, 순서O) renderPropertyImages(images); break; case 'I9': // 360이미지 (다중, 위치O) render360Images(images); break; } } // 단일 이미지 렌더링 function renderSingleImage(img, imgId, altText) { // 이미지가 없으면 기본 이미지 표시 if (!img) { const defaultHtml = `${altText}`; if (imgId) { const existing = $(`#${imgId}`); if (existing.length > 0) { if (existing.parent('a').length > 0) { existing.parent('a').replaceWith(defaultHtml); } else { existing.replaceWith(defaultHtml); } } } else { const existing = $(`img[alt="${altText}"]`); if (existing.length > 0) { if (existing.parent('a').length > 0) { existing.parent('a').replaceWith(defaultHtml); } else { existing.replaceWith(defaultHtml); } } } return; } const originalUrl = img.original_url || img.thumbnail_url; const thumbnailUrl = img.thumbnail_url || img.original_url; const html = ` ${altText} `; if (imgId) { $(`#${imgId}`).parent().replaceWith(html); } else { $(`img[alt="${altText}"]`).parent().replaceWith(html); } } // 다중 이미지 렌더링 (분양권용) function renderMultipleImages(images, imgType, altText) { const container = $(`img[alt="${altText}"]`).closest('.d-flex.flex-wrap'); if (container.length === 0) return; container.empty(); // 이미지가 없으면 기본 이미지 표시 if (images.length === 0) { const defaultHtml = `
${altText}
`; container.append(defaultHtml); return; } images.forEach(img => { const originalUrl = img.original_url || img.thumbnail_url; const thumbnailUrl = img.thumbnail_url || img.original_url; const html = `
${altText}
`; container.append(html); }); } // 매물사진 렌더링 (분양권과 동일한 형태) function renderPropertyImages(images) { const container = $('#property-images-container'); if (container.length === 0) return; container.empty(); // 이미지가 없으면 기본 이미지 표시 if (images.length === 0) { const defaultHtml = `
매물사진
`; container.append(defaultHtml); return; } // view_odr 기준으로 정렬 images.sort((a, b) => (a.view_odr || 0) - (b.view_odr || 0)); images.forEach((img, index) => { const originalUrl = img.original_url || img.thumbnail_url; const thumbnailUrl = img.thumbnail_url || img.original_url; const orderNum = index + 1; const html = `
${orderNum}
매물사진
`; container.append(html); }); // Sortable 초기화 initPropertyImagesSortable(); } // 360이미지 렌더링 (위치 input 포함) function render360Images(images) { const container = $('img[alt="360이미지"]').closest('.d-flex.flex-wrap'); if (container.length === 0) return; container.empty(); // 이미지가 없으면 기본 이미지 표시 if (images.length === 0) { const defaultHtml = `
360이미지
`; container.append(defaultHtml); return; } images.forEach(img => { const originalUrl = img.original_url || img.thumbnail_url; const thumbnailUrl = img.thumbnail_url || img.original_url; const imgLocation = img.img_location || ''; const imgSq = img.img_sq || ''; const html = `
360이미지
`; container.append(html); }); } // 특정 타입의 모든 이미지 일괄삭제 function deleteAllImagesByType(imgType, imgTypeName) { console.log('[deleteAllImagesByType] 시작:', imgType, imgTypeName); const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); const rcptSq = $("#frm_file_info [name=rcpt_sq]").val(); if (!rsrvSq || !rcptSq) { alert('필수 정보가 없습니다.'); return; } // 먼저 사용자 확인 if (!confirm(`${imgTypeName} 이미지를 모두 삭제하시겠습니까?`)) { return; } // 로딩 화면 표시 blockUI.blockPage({ message: '
Loading...

삭제 중...

' }); // 해당 타입의 이미지 목록 조회 후 삭제 $.ajax({ url: '/article/receipt/getImages', method: 'GET', data: { rsrv_sq: rsrvSq, img_type: imgType }, success: function(response) { console.log('[deleteAllImagesByType] 조회 응답:', response); if (response.success && response.images && response.images.length > 0) { const images = response.images; const imageCount = images.length; // 삭제 Promise 배열 const deletePromises = images.map(img => { return $.ajax({ url: '/article/receipt/removeUploadFile', method: 'POST', data: { rcpt_sq: rcptSq, img_sq: img.img_sq } }); }); // 모든 삭제 완료 대기 Promise.all(deletePromises) .then(function(results) { console.log('[deleteAllImagesByType] 모든 삭제 완료:', results); // 성공/실패 카운트 const successCount = results.filter(r => r.code === '0').length; const failCount = imageCount - successCount; // 로딩 화면 제거 blockUI.unblockPage(); if (failCount > 0) { alert(`${successCount}개 삭제 성공, ${failCount}개 실패`); } else { alert(`${imgTypeName} 이미지 ${successCount}개가 삭제되었습니다.`); } // AJAX로 이미지 재로딩 loadAllImages(rsrvSq); }) .catch(function(error) { console.error('[deleteAllImagesByType] 삭제 오류:', error); // 로딩 화면 제거 blockUI.unblockPage(); alert('이미지 삭제 중 오류가 발생했습니다.'); loadAllImages(rsrvSq); // 오류 발생해도 재로딩 }); } else { // 로딩 화면 제거 blockUI.unblockPage(); alert(`삭제할 ${imgTypeName} 이미지가 없습니다.`); } }, error: function(xhr, status, error) { console.error('[deleteAllImagesByType] 조회 오류:', error); // 로딩 화면 제거 blockUI.unblockPage(); alert('이미지 조회 중 오류가 발생했습니다.'); } }); } // 매물사진 Sortable 초기화 var propertyImagesSortable = null; function initPropertyImagesSortable() { const container = document.getElementById('property-images-container'); if (!container) { console.warn('[initPropertyImagesSortable] 컨테이너를 찾을 수 없음'); return; } // 기존 Sortable 제거 if (propertyImagesSortable) { propertyImagesSortable.destroy(); propertyImagesSortable = null; } // 이미지가 없으면 초기화하지 않음 const items = container.querySelectorAll('.property-image-item[data-img-sq]'); if (items.length === 0) { console.log('[initPropertyImagesSortable] 이미지가 없어서 Sortable 초기화 안 함'); return; } propertyImagesSortable = new Sortable(container, { animation: 150, ghostClass: 'sortable-ghost', dragClass: 'sortable-drag', draggable: '.property-image-item', onStart: function(evt) { // 드래그 시작시 클릭 이벤트 막기 const links = container.querySelectorAll('a'); links.forEach(link => { link.style.pointerEvents = 'none'; }); }, onEnd: function(evt) { console.log('[PropertyImagesSortable] 순서 변경:', evt.oldIndex, '->', evt.newIndex); updatePropertyImageOrderBadges(); // 드래그 종료 후 클릭 이벤트 복구 (약간의 딜레이 후) setTimeout(function() { const links = container.querySelectorAll('a'); links.forEach(link => { link.style.pointerEvents = ''; }); }, 100); } }); console.log('[initPropertyImagesSortable] Sortable 초기화 완료, 이미지 수:', items.length); } // 매물사진 순서 배지 업데이트 function updatePropertyImageOrderBadges() { const items = document.querySelectorAll('#property-images-container .property-image-item'); items.forEach((item, index) => { const badge = item.querySelector('.image-order-badge'); if (badge) { badge.textContent = index + 1; } }); console.log('[updatePropertyImageOrderBadges] 배지 업데이트 완료'); } // 360이미지 촬영위치 저장 function save360ImageLocations() { console.log('[save360ImageLocations] 촬영위치 저장 시작'); const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (!rsrvSq) { alert('예약번호가 없습니다.'); return; } // img_location으로 시작하는 모든 input 수집 const locationInputs = $('input[id^="img_location_"]'); if (locationInputs.length === 0) { alert('저장할 360이미지가 없습니다.'); return; } // 각 input의 값을 수집하고 저장 const savePromises = []; let savedCount = 0; locationInputs.each(function() { const imgSq = $(this).attr('id').replace('img_location_', ''); const location = $(this).val(); console.log('[save360ImageLocations] img_sq:', imgSq, 'location:', location); const promise = $.ajax({ url: '/article/receipt/saveImgLocation', method: 'POST', data: { img_sq: imgSq, rsrv_sq: rsrvSq, location: location } }).done(function(response) { if (response.code === '0') { savedCount++; console.log('[save360ImageLocations] 저장 완료:', imgSq); } else { console.error('[save360ImageLocations] 저장 실패:', response.msg); } }).fail(function(xhr, status, error) { console.error('[save360ImageLocations] AJAX 오류:', error); }); savePromises.push(promise); }); // 모든 저장 완료 대기 Promise.all(savePromises).then(function() { console.log('[save360ImageLocations] 모든 촬영위치 저장 완료:', savedCount); alert(`촬영위치가 저장되었습니다. (${savedCount}개)`); }).catch(function(error) { console.error('[save360ImageLocations] 저장 중 오류:', error); alert('촬영위치 저장 중 오류가 발생했습니다.'); }); } // 이미지 타입별 일괄 다운로드 function downloadImagesByType(imgType, imgTypeName) { const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (!rsrvSq) { alert('예약번호가 없습니다.'); return; } // 해당 타입의 이미지 개수 확인 let imageCount = 0; if (allImagesCache && allImagesCache.length > 0) { imageCount = allImagesCache.filter(img => img.img_type === imgType).length; } if (imageCount === 0) { alert('다운로드할 ' + imgTypeName + ' 이미지가 없습니다.'); return; } if (!confirm(`${imgTypeName} ${imageCount}장을 다운로드하시겠습니까?`)) { return; } // 로딩 화면 표시 blockUI.blockPage({ message: '
Loading...

다운로드 준비 중...

' }); // 다운로드 URL 생성 및 실행 const downloadUrl = `/article/receipt/downloadAllImages?rsrv_sq=${rsrvSq}&img_type=${imgType}`; // iframe을 사용한 다운로드 (페이지 리로드 방지) const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = downloadUrl; document.body.appendChild(iframe); // 다운로드 완료 후 정리 (3초 후) setTimeout(function() { blockUI.unblockPage(); document.body.removeChild(iframe); console.log(`[downloadImagesByType] ${imgTypeName} 다운로드 완료`); }, 3000); } // 매물사진 순서 저장 function savePropertyImageOrder() { const items = document.querySelectorAll('#property-images-container .property-image-item[data-img-sq]'); if (items.length === 0) { alert('저장할 이미지가 없습니다.'); return; } // 순서 데이터 수집 const orderData = []; items.forEach((item, index) => { const imgSq = item.getAttribute('data-img-sq'); if (imgSq) { orderData.push({ img_sq: imgSq, view_odr: index + 1 }); } }); console.log('[savePropertyImageOrder] 저장할 순서:', orderData); if (!confirm(`매물사진 순서를 저장하시겠습니까? (총 ${orderData.length}장)`)) { return; } // 로딩 화면 표시 blockUI.blockPage({ message: '
Loading...

저장 중...

' }); // 서버에 저장 요청 $.ajax({ url: '/article/receipt/updateImageOrder', method: 'POST', data: { rcpt_sq: $("#frm_file_info [name=rcpt_sq]").val(), img_type: 'I4', orders: JSON.stringify(orderData) }, success: function(response) { blockUI.unblockPage(); if (response.code === '0' || response.success) { alert('매물사진 순서가 저장되었습니다.'); // 재로딩하여 최신 상태 반영 const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (rsrvSq) { loadAllImages(rsrvSq); } } else { alert('순서 저장 중 오류가 발생했습니다: ' + (response.msg || response.message || '알 수 없음')); } }, error: function(xhr, status, error) { blockUI.unblockPage(); console.error('[savePropertyImageOrder] 오류:', error); alert('순서 저장 중 오류가 발생했습니다.'); } }); } function editPriceInfo() { // 1. 이미 내가 풀어서 '도장'이 찍힌 요소가 있는지 확인 (이미 수정 모드인지 체크) const $manuallyOpened = $('#rcptFrm').find('[data-was-disabled="true"]'); if ($manuallyOpened.length > 0) { // --- [다시 잠그기 모드] --- $manuallyOpened.prop("disabled", true).removeAttr("data-was-disabled"); console.log("임시로 풀었던 필드들을 다시 잠갔습니다."); } else { // --- [잠금 풀기 모드] --- // 2. 풀 대상들을 모읍니다. (기존 로직 포함) let targets = [ "#trade_type", "#rcpt_product_info2", "#rcpt_product_info3", ".display-price-input" // 클래스 대상 추가 ]; var rcpt_product = $('#rcpt_product').val(); var trade_type = $('#trade_type').val(); if (trade_type == "A1") { if (['A01', 'A02', 'A03', 'B01', 'B02', 'B03'].includes(rcpt_product)) { targets.push("#rcpt_product_info4", "#rcpt_product_info5", "#rcpt_product_info6"); } } // 3. 대상들 중에서 '현재 잠겨있는 것만' 골라서 도장 찍고 풀기 const $targetElements = $(targets.join(', ')); const $toOpen = $targetElements.filter(':disabled'); $toOpen.attr('data-was-disabled', 'true').prop('disabled', false); console.log($toOpen.length + "개의 필드를 수정 가능하게 풀었습니다."); if($toOpen.length > 0) $toOpen.first().focus(); } } // 수정 버튼 클릭 시 정보 수정 가능하도록 function editInfo() { const $form = $('#rcptFrm'); // 1. 이미 내가 풀어서 '도장(data-was-disabled)'이 찍힌 요소가 있는지 확인 const $manuallyOpened = $form.find('[data-was-disabled="true"]'); if ($manuallyOpened.length > 0) { // [다시 잠그기] // 도장이 찍힌 놈들만 다시 잠그고 도장을 지웁니다. $manuallyOpened.prop('disabled', true).removeAttr('data-was-disabled'); console.log("임시로 풀었던 요소들을 다시 잠갔습니다."); } else { // [잠금 풀기] // 현재 disabled 상태인 요소들만 찾아서 도장을 찍고 풀어줍니다. const $toOpen = $form.find('input:disabled, select:disabled'); if ($toOpen.length > 0) { $toOpen.attr('data-was-disabled', 'true').prop('disabled', false); $toOpen.first().focus(); console.log("잠겨있던 요소들을 수정 가능하게 풀었습니다."); } } } // 가격수정 저장 function modifyPriceInfo(btn) { // 거래구분 값 검증 var tradeType = $("#trade_type").val(); if (!tradeType || tradeType.trim() === "") { Swal.fire({ text: "거래구분을 선택해주세요.", icon: "warning", confirmButtonText: "확인" }); return; } Swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { // Display input에서 수정된 값 읽기 var dealAmount = $("#displayDealAmount").val() || 0; var warrantyAmount = $("#displayWarrantyAmount").val() || 0; var leaseAmount = $("#displayLeaseAmount").val() || 0; var preSaleAmount = $("#displayPresaleAmount").val() || 0; var premiumAmount = $("#displayPremiumAmount").val() || 0; var preSaleOptionAmount = $("#displayOptionAmount").val() || 0; // Hidden input에 값 동기화 $("#dealAmount").val(dealAmount); $("#warrantyAmount").val(warrantyAmount); $("#leaseAmount").val(leaseAmount); $("#preSaleAmount").val(preSaleAmount); $("#premiumAmount").val(premiumAmount); $("#preSaleOptionAmount").val(preSaleOptionAmount); var params = { 'rcpt_sq': $("#rcptFrm [name=rcpt_sq]").val() || $("input[name=rcpt_sq]").val(), 'rcpt_key': $("#rcptFrm [name=rcpt_key]").val(), 'rcpt_atclno': $("#rcptFrm [name=rcpt_atclno]").val(), 'trade_type': tradeType, 'rcpt_product_info2': $("#rcpt_product_info2").val(), 'rcpt_product_info3': $("#rcpt_product_info3").val(), 'rcpt_product_info4': $("#rcpt_product_info4").val(), 'rcpt_product_info5': $("#rcpt_product_info5").val(), 'rcpt_product_info6': $("#rcpt_product_info6").val(), 'dealAmount': dealAmount, 'warrantyAmount': warrantyAmount, 'leaseAmount': leaseAmount, 'preSaleAmount': preSaleAmount, 'premiumAmount': premiumAmount, 'preSaleOptionAmount': preSaleOptionAmount, 'rcpt_ptp_no': $("#rcpt_ptp_no").val(), 'agent_tel': $("#agent_tel").val(), 'rcpt_hscp_no': $(btn).data('hscp_no'), 'rcpt_ptp_no': $(btn).data('ptp_no'), }; callAjax("/article/receipt/modifyPriceInfo", params, fn_result); // 저장 완료 후 display input 다시 비활성화 $(".display-price-input").prop("disabled", true); $(".btn-edit").show(); $(".btn-save").hide(); } }); } // 연락가능전화 저장 function fn_save_tel() { Swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { var params = { 'rcpt_sq': $("#rcptFrm [name=rcpt_sq]").val() || $("input[name=rcpt_sq]").val(), 'agent_tel': $("#agent_tel").val(), }; callAjax("/article/receipt/saveAptMemo", params, fn_result); } }); } // 평면도요청 저장 function res_ground() { Swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/resGround", $("#rcptFrm").serialize(), fn_result); } }); } // 거주여부 변경 이벤트 function dbYn_change(value) { // 거주여부가 Y이면 DB활용동의여부 활성화, N이면 비활성화 if (value === 'Y') { $('#dbUsageAgrYn').prop('disabled', false); } else { $('#dbUsageAgrYn').prop('disabled', true); $('#dbUsageAgrYn').val('N'); } } // 거주여부 저장 function saveResDbYn() { Swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/resDbYn", $("#rcptFrm").serialize(), fn_result); } }); } //등록일로부터 3개월 체크 function assign(mon, menuid) { var frm = document.getElementById('rcptFrm'); var to = frm.rsrv_date.value; var mon_chk = mon.split("-"); var to_chk = to.split("-"); var date1 = new Date(mon_chk[0], mon_chk[1], mon_chk[2]).valueOf(); var date2 = new Date(to_chk[0], to_chk[1], to_chk[2]).valueOf(); if (date2 - date1 < 0) { assignRegist(menuid); } else { Swal.fire({ title: "등록일로부터 3개월 이전만 가능합니다.", icon: "warning" }) return; } } /** * 배정자 등록 */ function assignRegist(menuid) { var frm = document.rcptFrm; if (frm.rsrv_date.value == "") { Swal.fire({ title: "방문희망일시를 선택해 주세요.", icon: "warning" }) return; } var date = new Date(); var yy = date.getFullYear(); var mm = date.getMonth() + 1; var dd = date.getDate(); if (mm < 10) mm = "0" + mm; if (dd < 10) dd = "0" + dd; var today = yy + mm + dd; var rsrv = frm.rsrv_date.value.replace(/-/gi, ""); if (parseInt(today) > parseInt(rsrv)) { Swal.fire({ title: "방문희망일시는 금일 이전날짜는 불가능합니다.", icon: "warning" }) return; } if (frm.rsrv_tm_ap.value == "") { Swal.fire({ title: "오전/오후를 선택해 주세요.", icon: "warning" }) return; } if (frm.rsrv_tm_hour.value == "") { Swal.fire({ title: "시간을 선택해 주세요.", icon: "warning" }) return; } if (frm.bonbu.value == "") { Swal.fire({ title: "본부를 선택해 주세요.", icon: "warning" }) return; } if (frm.dept_sq.value == "") { Swal.fire({ title: "팀을 선택해 주세요.", icon: "warning" }) return; } if (frm.usr_sq.value == "") { Swal.fire({ title: "담당자를 선택해 주세요.", icon: "warning" }) return; } swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/assignRegist", $("#rcptFrm").serialize(), fn_result); } }); } // 동영상 촬영여부저장 function requestMovie() { Swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/requestMovie", $("#rcptFrm").serialize(), fn_result); } }); } // 중개사메모 저장 function requestMessage() { Swal.fire({ text: "저장 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/requestMessage", $("#rcptFrm").serialize(), fn_result); } }); } // 예약취소 function rsrvcancel() { var stat = frm.rcpt_stat1.value; var cd2 = frm.result_cd2.value; if (stat == '70' && cd2 == '9030') { Swal.fire({ title: "방문 전 취소가 불가능합니다.", icon: "warning" }) return; } if (frm.result_cd2.value == "") { Swal.fire({ title: "분류1을 선택해 주세요.", icon: "warning" }) return; } if (frm.result_cd3.value == "") { Swal.fire({ title: "분류2를 선택해 주세요.", icon: "warning" }) return; } if (frm.result_msg.value == "") { Swal.fire({ title: "취소사유를 입력해 주세요.", icon: "warning" }) return; } Swal.fire({ text: "취소 하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/rsrvcancel", $("#rcptFrm").serialize(), fn_result); } }); } // 상태변경 function chgStatus(cd, rcpt_product, chg_floor_yn) { if (cd == "") return; var frm = document.rcptFrm; var i1 = frm.I1.value; // 홍보확인서 var i4 = frm.I4.value; // 매물사진 var rr_yn = frm.req_rec_yn.value; //녹취필요여부 var chk_record = frm.chk_record.value; //녹취파일 확인 체크 var r_yn = frm.rec_yn.value; //녹취 완료여부 var req_rec_yn = "N"; var rcpt_product = frm.rcpt_product.value; var r = ''; } // 문자발송 modal function viewSmsPop(cd) { if (window.smsArr.length > 0) { var tel = ""; if (cd == "S7" || cd == "S14") { tel = window.rec_tel; } else if (cd == "S10") { tel = window.agent_contact_tel; } else if (cd == "15") { tel = window.agent_contact_tel; } else { tel = window.agent_head_tel; } for (const sms of window.smsArr) { if (sms.cd == cd) { $("#smsForm [name=cd]").val(cd); $("#smsForm [name=phone]").val(tel); $("#smsForm [name=content]").val(sms.cd_nm); } } } $("#smsModal").modal("show"); } // 문자발송 function sendSms() { if ($("#smsForm [name=phone]").val() == "") { Swal.fire({ title: "수신번호를 입력해 주세요.", icon: "warning" }) return; } if ($("#smsForm [name=content]").val() == "") { Swal.fire({ title: "내용을 입력해 주세요.", icon: "warning" }) return; } Swal.fire({ text: "SMS를 발송하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { callAjax("/article/receipt/sendSms", $("#smsForm").serialize(), fn_result); } }); } function showFileName(input) { if (input.files && input.files.length > 0) { document.getElementById('file_name').textContent = input.files[0].name; } } // 거주인 녹취정보 저장 function saveRecInfo() { const rec_tel1 = $("#rec_tel1").val(); const rec_tel2 = $("#rec_tel2").val(); const rec_tel3 = $("#rec_tel3").val(); if (rec_tel1 == "" || rec_tel2 == "" || rec_tel3 == "") { Swal.fire({ title: "거주자 전화번호를 입력해 주세요.", icon: "warning" }); return; } Swal.fire({ text: "거주인정보를 저장하시겠습니까?", icon: "warning", showCancelButton: true, confirmButtonText: "예", cancelButtonText: "아니오", confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", }).then((result) => { if (result.isConfirmed) { var form = $('#rcptFrm')[0]; var formData = new FormData(form); $.ajax({ url: "/article/receipt/saveRecInfo", method: 'POST', data: formData, processData: false, contentType: false, cache: false, beforeSend: function () { blockUI.blockPage({ message: tpl }) }, complete: function () { blockUI.unblockPage() }, success: function (result) { if (result.code == '0') { Swal.fire({ title: "정상 처리되었습니다.", icon: "success", draggable: true }).then(() => { // 서버에서 반환된 데이터로 화면 갱신 if (result.data) { updateRecInfoUI(result.data); } }); } else { Swal.fire({ title: result.msg, icon: "error", draggable: true }) } } }); } }); } // 거주인 정보 UI 업데이트 function updateRecInfoUI(data) { // 전화번호 업데이트 if (data.rec_tel) { const telParts = data.rec_tel.split('-'); $('input[name="rec_tel1"]').val(telParts[0] || ''); $('input[name="rec_tel2"]').val(telParts[1] || ''); $('input[name="rec_tel3"]').val(telParts[2] || ''); } // 거주인 이름 업데이트 $('input[name="rec_nm"]').val(data.rec_nm || ''); // 거주인 요청사항 업데이트 $('textarea[name="rec_remark"]').val(data.remark || ''); // 음성파일 정보 업데이트 const fileInfoHtml = data.record ? `
${data.record.record_orignm} ${data.record.record_size} KB ${data.record.insert_tm}
` : '등록된 파일 없음'; // 음성파일 영역 업데이트 (파일 업로드 버튼 앞까지) const fileContainer = $('input[name="rec_file"]').closest('td').find('.d-flex').first(); fileContainer.find('.d-flex.align-items-center.gap-2.flex-wrap, .text-muted.small').first().replaceWith(fileInfoHtml); // 녹취파일 체크박스 활성화 및 체크 if (data.record) { $('#chk_record').prop('disabled', false).prop('checked', true); } else { $('#chk_record').prop('disabled', true).prop('checked', false); } // 파일명 표시 초기화 $('#file_name').text(''); $('input[name="rec_file"]').val(''); } // 거주인 정보 다시 불러오기 function loadRecInfo() { const rcpt_sq = $('input[name="rcpt_sq"]').val(); $.ajax({ url: "/article/receipt/getRecInfo", method: 'GET', data: { rcpt_sq: rcpt_sq }, dataType: 'json', success: function (result) { if (result.code == '0' && result.data) { updateRecInfoUI(result.data); } }, error: function() { console.error('거주인 정보를 불러오는데 실패했습니다.'); } }); } function fn_preview(src, type = 'img') { const $img = $('#imgPreview'); const $video = $('#vdoPreview'); const video = document.getElementById('vdoPreview'); const source = document.getElementById('videoSource'); if (type === 'vdo') { // 이미지 숨김 $img.hide().attr('src', ''); // video source 세팅 source.src = src; // video 표시 + 로드 $video.show(); video.load(); $('#previewTitle').text('동영상 미리보기'); } else { // video 정지 및 초기화 video.pause(); source.src = ''; video.load(); $video.hide(); // 이미지 표시 $img.attr('src', src).show(); $('#previewTitle').text('이미지 미리보기'); } const modal = new bootstrap.Modal(document.getElementById('previewModal')); modal.show(); } function callAjax(target, params, callback) { $.ajax({ url: target, contentType: 'application/x-www-form-urlencoded;charset=UTF-8', method: "POST", data: params, beforeSend: function () { blockUI.blockPage({ message: tpl }) }, complete: function () { blockUI.unblockPage() }, success: function (result) { callback(result); } }); } function fn_result(result) { if (result.code == '0') { Swal.fire({ title: "정상 처리되었습니다.", icon: "success", draggable: true }); // 정보변경 이력 갱신 loadHistory(); } else { Swal.fire({ title: result.msg, icon: "error", draggable: true }) } } // 정보변경 이력 AJAX 로드 function loadHistory() { const rcptSq = $("#rcptFrm input[name='rcpt_sq']").val(); console.log('[loadHistory] rcpt_sq:', rcptSq); if (!rcptSq) { console.warn('[loadHistory] rcpt_sq가 없습니다.'); return; } $.ajax({ url: "/article/receipt/getHistory", type: "GET", data: { rcpt_sq: rcptSq }, success: function(result) { console.log('[loadHistory] 응답:', result); if (result.code === '0' && result.data) { console.log('[loadHistory] 이력 데이터:', result.data); updateHistoryUI(result.data); } else { console.warn('[loadHistory] 올바르지 않은 응답:', result); } }, error: function(xhr, status, error) { console.error('[History] 로드 실패:', error); console.error('[History] 응답:', xhr.responseText); } }); } // 정보변경 이력 UI 업데이트 function updateHistoryUI(historyData) { const $historyTable = $('#history-table'); if (!$historyTable.length) { console.error('[updateHistoryUI] 정보변경 이력 테이블을 찾을 수 없습니다.'); return; } let $tbody = $historyTable.find('tbody'); if (!$tbody.length) { // tbody가 없으면 생성 const $thead = $historyTable.find('thead'); if ($thead.length) { $tbody = $('').insertAfter($thead); } } $tbody.empty(); if (historyData && historyData.length > 0) { historyData.forEach(h => { const row = ` ${h.rcpt_stat_nm || ''} ${h.changed_type_nm || ''} ${h.changed_id || ''} ${h.changed_tm || ''} ${h.remark || ''} `; $tbody.append(row); }); } else { $tbody.append('이력이 없습니다.'); } } // Dropzone 초기화 함수 function initDropzone(imgType) { console.log('[Dropzone] initDropzone 호출, imgType:', imgType); // 기존 Dropzone 인스턴스가 있으면 제거 if (dz) { console.log('[Dropzone] 기존 인스턴스 제거'); dz.destroy(); dz = null; } // 파일 타입별 최대 개수 설정 const isV2 = document.getElementById('myDropzone')?.getAttribute('data-is-v2') === 'true'; console.log('[Dropzone] isV2:', isV2); const maxFilesConfig = { 'I4': isV2 ? 1 : 15, // 매물사진 (V2는 1장, 일반은 15장) 'I8': 5, // 평면도 'I9': 5, // 녹취파일 'V1': 5 // 동영상 }; const maxFiles = maxFilesConfig[imgType] || 10; // 기본 10개 console.log('[Dropzone] maxFiles:', maxFiles); let uploadStarted = false; let uploadedCount = 0; let totalFiles = 0; dz = new Dropzone("#myDropzone", { url: "/article/receipt/uploadFile", method: "post", paramName: "files", autoProcessQueue: false, // 자동 업로드 끄기 uploadMultiple: false, // 파일을 개별적으로 업로드 parallelUploads: 1, // 한 번에 한 개씩 업로드 maxFiles: maxFiles, // 최대 파일 수 maxFilesize: 100, addRemoveLinks: true, clickable: "#uploadPick", // 파일선택 버튼만 클릭 가능 dictRemoveFile: "삭제", dictDefaultMessage: "최대 " + maxFiles + "개", dictFallbackMessage: "브라우저가 드래그앤드롭을 지원하지 않습니다.", dictFileTooBig: "파일이 너무 큽니다 (최대 {{maxFilesize}}MB)", dictInvalidFileType: "허용되지 않은 파일 형식입니다.", dictResponseError: "서버 오류가 발생했습니다.", dictCancelUpload: "업로드 취소", dictMaxFilesExceeded: "최대 " + maxFiles + "개까지만 업로드할 수 있습니다.", }); dz.on("addedfile", function (file) { console.log('[Dropzone] addedfile 후처리:', file.name, 'isExisting:', file.isExisting); // 교체 버튼 추가 const filePreview = file.previewElement; const removeButton = filePreview.querySelector('.dz-remove'); if (removeButton && !filePreview.querySelector('.dz-replace')) { // 버튼 영역 div 생성 const buttonsDiv = document.createElement('div'); buttonsDiv.className = 'file-buttons'; // 교체 버튼 생성 const replaceButton = document.createElement('a'); replaceButton.href = 'javascript:void(0);'; replaceButton.className = 'dz-replace'; replaceButton.textContent = '교체'; replaceButton.onclick = function(e) { e.preventDefault(); e.stopPropagation(); replaceFile(file); }; // 기존 삭제 버튼을 buttonsDiv로 감싸기 removeButton.parentNode.insertBefore(buttonsDiv, removeButton); buttonsDiv.appendChild(removeButton); buttonsDiv.appendChild(replaceButton); // 기존 파일인 경우 삭제 확인 메시지 추가 if (file.isExisting) { removeButton.textContent = 'DB삭제'; removeButton.style.color = '#dc3545'; // 삭제 버튼 클릭 시 확인 메시지 const originalHref = removeButton.getAttribute('data-dz-remove'); removeButton.removeAttribute('data-dz-remove'); removeButton.onclick = function(e) { e.preventDefault(); e.stopPropagation(); if (confirm('이 파일을 DB와 클라우드에서 삭제하시겠습니까?\n\n파일명: ' + file.name)) { dz.removeFile(file); } }; } } // 파일 순서 배지 추가 updateFileOrderBadges(); // Sortable 초기화 (파일이 추가될 때마다) initSortable(); }); dz.on("removedfile", function (file) { console.log('[Dropzone] removedfile:', file.name, 'isExisting:', file.isExisting, 'isDeleting:', file.isDeleting, 'isModalClosing:', window.isModalClosing); // 모달이 닫히는 중에는 서버 삭제 요청 하지 않음 (UI 정리만 수행) if (window.isModalClosing) { console.log('[Dropzone] 모달 닫힘 중이므로 서버 삭제 요청 안 함'); return; } // 기존 파일(DB에 있는 파일)인 경우 서버에서 삭제 if (file.isExisting && file.img_sq) { // 이미 삭제 중인 경우 중복 요청 방지 if (file.isDeleting) { console.log('[Dropzone] 이미 삭제 중인 파일:', file.img_sq); return; } file.isDeleting = true; // 삭제 플래그 설정 console.log('[Dropzone] 기존 파일 삭제 요청, img_sq:', file.img_sq); // 삭제 Promise 저장 (전체삭제 시 사용) if (!window.deletePromises) { window.deletePromises = []; } const deletePromise = $.ajax({ url: '/article/receipt/removeUploadFile', method: 'POST', data: { rcpt_sq: $("#frm_file_info [name=rcpt_sq]").val(), img_sq: file.img_sq } }).done(function(response) { console.log('[Dropzone] 파일 삭제 완료:', response); if (response.code === '0') { // 개별 삭제인 경우 (전체삭제가 아닌 경우) if (!window.isBulkDeleting) { alert('파일이 삭제되었습니다.'); // AJAX로 이미지 재로딩 const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (rsrvSq) { loadAllImages(rsrvSq); } } } else { console.error('[Dropzone] 삭제 응답 에러:', response.msg); file.isDeleting = false; // 실패 시 플래그 해제 if (!window.isBulkDeleting) { alert('파일 삭제 중 오류가 발생했습니다: ' + (response.msg || '알 수 없는 오류')); } } }).fail(function(xhr, status, error) { console.error('[Dropzone] 파일 삭제 실패:', error); file.isDeleting = false; // 실패 시 플래그 해제 if (!window.isBulkDeleting) { alert('파일 삭제 중 오류가 발생했습니다.'); } }); window.deletePromises.push(deletePromise); } // 파일 삭제 후 순서 배지 업데이트 setTimeout(updateFileOrderBadges, 100); }); dz.on("sending", function (file, xhr, formData) { console.log('[Dropzone] sending 이벤트:', file.name, 'isExisting:', file.isExisting); // 기존 파일은 업로드하지 않음 if (file.isExisting) { console.log('[Dropzone] 기존 파일이므로 업로드 스킵:', file.name); return false; } // 현재 순서 찾기 const fileIndex = dz.files.indexOf(file); formData.append("rcpt_key", $("#frm_file_info [name=rcpt_key]").val()); formData.append("rsrv_sq", $("#frm_file_info [name=rsrv_sq]").val()); formData.append("rcpt_sq", $("#frm_file_info [name=rcpt_sq]").val()); formData.append("img_type", $("#frm_file_info [name=img_type]").val()); formData.append("img_sub_type", $("#frm_file_info [name=img_sub_type]").val()); formData.append("vr_sq", $("#frm_file_info [name=vr_sq]").val()); formData.append("file_order", fileIndex + 1); // 파일 순서 추가 uploadStarted = true; }); dz.on("processing", function (file) { console.log('[Dropzone] processing 이벤트:', file.name); }); dz.on("success", function (file, response) { console.log('[Dropzone] success 이벤트:', file.name, response); uploadedCount++; console.log('[Dropzone] 업로드 진행:', uploadedCount + '/' + totalFiles); // 업로드된 파일 정보 저장 if (!window.uploadedFiles) { window.uploadedFiles = []; } // 서버 응답에서 이미지 정보 추출 if (response && response.code === '0' && response.data) { window.uploadedFiles.push({ imgType: $("#frm_file_info [name=img_type]").val(), imageData: response.data }); console.log('[Dropzone] 업로드된 파일 정보 저장:', response.data); } // 다음 파일이 있으면 처리 if (dz.getQueuedFiles().length > 0) { console.log('[Dropzone] 다음 파일 처리 시작'); dz.processQueue(); } }); dz.on("error", function (file, errorMessage, xhr) { console.log('[Dropzone] error 이벤트:', file.name, errorMessage, 'status:', file.status); uploadedCount++; // 모달이 닫히는 중이거나 파일이 취소된 경우 에러 메시지 표시 안 함 if (window.isModalClosing || file.status === 'canceled' || errorMessage === 'Upload canceled.') { console.log('[Dropzone] 모달 닫힘 중이거나 취소된 파일이므로 에러 무시'); return; } // 에러 메시지 파싱 let displayMessage = '업로드 실패: ' + file.name; if (typeof errorMessage === 'object' && errorMessage.msg) { displayMessage = errorMessage.msg; } else if (typeof errorMessage === 'string') { displayMessage = errorMessage; } // 사용자에게 에러 알림 alert('⚠️ 파일 업로드 실패\n\n' + displayMessage); // 파일에 에러 표시 if (file.previewElement) { file.previewElement.classList.add('dz-error'); } // 업로드 중단 (다음 파일 처리 안 함) uploadStarted = false; console.log('[Dropzone] 에러로 인해 업로드 중단'); }); dz.on("queuecomplete", function () { console.log('[Dropzone] queuecomplete 이벤트, uploadStarted:', uploadStarted, 'uploadedCount:', uploadedCount, 'totalFiles:', totalFiles); if (uploadStarted && uploadedCount >= totalFiles && totalFiles > 0) { console.log('[Dropzone] 모든 파일 업로드 완료'); // 업로드된 파일 목록 초기화 window.uploadedFiles = []; // AJAX로 최신 상태 불러오기 const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (rsrvSq) { console.log('[Dropzone] AJAX로 이미지 재로딩 시작'); loadAllImages(rsrvSq); } else { console.warn('[Dropzone] rsrv_sq가 없어서 재로딩 불가'); } alert('파일이 업로드되었습니다.'); // 모달 닫기 $("#uploadModal").modal("hide"); } }); // 버튼 이벤트 재등록 (파일선택 버튼은 Dropzone이 자동 처리) $("#btnUpload").off("click").on("click", function () { console.log('[Dropzone] btnUpload 클릭'); // 새로 추가된 파일만 필터링 (기존 파일 제외) const newFiles = dz.files.filter(file => !file.isExisting && file.status === Dropzone.QUEUED); const existingFiles = dz.files.filter(file => file.isExisting); console.log('[Dropzone] 전체 파일:', dz.files.length, '기존 파일:', existingFiles.length, '새 파일:', newFiles.length); if (newFiles.length === 0) { if (existingFiles.length > 0) { alert("변경사항이 저장되었습니다."); } else { alert("업로드할 파일을 먼저 선택해주세요."); } return; } totalFiles = newFiles.length; uploadedCount = 0; console.log('[Dropzone] 업로드 시작, totalFiles:', totalFiles); uploadStarted = true; dz.processQueue(); // 업로드 실행 }); $("#btnRemove").off("click").on("click", function () { console.log('[Dropzone] btnRemove 클릭'); const files = dz.files.slice(); // 배열 복사 if (files.length === 0) { alert("삭제할 파일이 없습니다."); return; } const existingFilesCount = files.filter(f => f.isExisting).length; const newFilesCount = files.length - existingFilesCount; let confirmMsg = `총 ${files.length}개의 파일을 삭제하시겠습니까?`; if (existingFilesCount > 0) { confirmMsg += `\n- 기존 파일 ${existingFilesCount}개 (DB 및 클라우드에서 삭제)`; } if (newFilesCount > 0) { confirmMsg += `\n- 신규 파일 ${newFilesCount}개 (목록에서만 제거)`; } if (!confirm(confirmMsg)) { return; } // 전체삭제 플래그 설정 window.isBulkDeleting = true; window.deletePromises = []; window.deletedFiles = []; // 삭제된 파일 정보 저장 // 모든 파일 삭제 (removedfile 이벤트에서 AJAX 호출) files.forEach(function (file) { if (file.isExisting && file.img_sq) { window.deletedFiles.push({ imgSq: file.img_sq, imgType: $("#frm_file_info [name=img_type]").val() }); } dz.removeFile(file); }); // 기존 파일이 있었으면 삭제 완료 대기 if (existingFilesCount > 0) { console.log('[Dropzone] 삭제 대기 중... Promise 개수:', window.deletePromises.length); Promise.all(window.deletePromises) .then(function() { console.log('[Dropzone] 모든 파일 삭제 완료'); window.isBulkDeleting = false; window.isModalClosing = true; // 모달 닫힘 플래그 설정 // AJAX로 이미지 재로딩 const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (rsrvSq) { loadAllImages(rsrvSq); } alert('파일이 삭제되었습니다.'); // 모달 닫기 $("#uploadModal").modal("hide"); }) .catch(function(error) { console.error('[Dropzone] 삭제 중 오류:', error); window.isBulkDeleting = false; alert('일부 파일 삭제 중 오류가 발생했습니다.'); location.reload(); }); } else { // 신규 파일만 있었으면 즉시 완료 window.isBulkDeleting = false; window.isModalClosing = true; // 모달 닫힘 플래그 설정 alert('파일이 목록에서 제거되었습니다.'); // 모달 닫기 $("#uploadModal").modal("hide"); } }); // Sortable 초기화 함수 function initSortable() { // 기존 Sortable 제거 if (sortable) { sortable.destroy(); } const dropzoneElement = document.querySelector("#myDropzone .dz-preview"); if (!dropzoneElement || !dropzoneElement.parentElement) { return; } sortable = new Sortable(dropzoneElement.parentElement, { animation: 150, ghostClass: 'sortable-ghost', handle: '.dz-image', draggable: '.dz-preview', onEnd: function(evt) { console.log('[Sortable] 순서 변경:', evt.oldIndex, '->', evt.newIndex); // Dropzone 파일 배열 순서 업데이트 const movedFile = dz.files[evt.oldIndex]; dz.files.splice(evt.oldIndex, 1); dz.files.splice(evt.newIndex, 0, movedFile); // 순서 배지 업데이트 updateFileOrderBadges(); } }); } // 파일 교체 함수 function replaceFile(oldFile) { console.log('[Dropzone] 파일 교체 시작:', oldFile.name); // 숨겨진 파일 input 생성 const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.style.display = 'none'; input.onchange = function(e) { const newFile = e.target.files[0]; if (!newFile) return; console.log('[Dropzone] 새 파일 선택됨:', newFile.name); // 기존 파일의 인덱스 찾기 const oldIndex = dz.files.indexOf(oldFile); // 기존 파일 제거 (UI에서만) dz.removeFile(oldFile); // 새 파일 추가 dz.addFile(newFile); // 파일 순서 재정렬 (새 파일을 원래 위치로) setTimeout(() => { const newIndex = dz.files.length - 1; if (oldIndex !== newIndex && oldIndex < dz.files.length) { const movedFile = dz.files[newIndex]; dz.files.splice(newIndex, 1); dz.files.splice(oldIndex, 0, movedFile); // DOM 순서도 변경 const previews = document.querySelectorAll('#myDropzone .dz-preview'); const newPreview = previews[previews.length - 1]; const parent = newPreview.parentNode; const targetPreview = previews[oldIndex]; parent.insertBefore(newPreview, targetPreview); } updateFileOrderBadges(); }, 100); }; document.body.appendChild(input); input.click(); document.body.removeChild(input); } // 파일 순서 배지 업데이트 함수 function updateFileOrderBadges() { const previews = document.querySelectorAll('#myDropzone .dz-preview'); previews.forEach((preview, index) => { // 기존 배지 제거 const existingBadge = preview.querySelector('.file-order-badge'); if (existingBadge) { existingBadge.remove(); } // 새 배지 추가 - 이미지 위에 표시 const badge = document.createElement('div'); badge.className = 'file-order-badge'; badge.textContent = index + 1; const imageDiv = preview.querySelector('.dz-image'); if (imageDiv) { imageDiv.style.position = 'relative'; imageDiv.appendChild(badge); } }); } } // 부모 페이지의 이미지 업데이트 function updateParentImage(imgType) { if (!imgType) { console.log('[updateParentImage] imgType이 없음'); return; } // I8 등 여러 이미지를 갤러리로 표시하는 타입은 업데이트 복잡하므로 스킵 const multiImageTypes = ['I8', 'I4', 'I5', 'I9']; if (multiImageTypes.includes(imgType)) { console.log('[updateParentImage] 복수 이미지 타입, 업데이트 스킵:', imgType); return; } const rsrv_sq = $("#frm_file_info [name=rsrv_sq]").val(); if (!rsrv_sq) { console.log('[updateParentImage] rsrv_sq가 없음'); return; } console.log('[updateParentImage] 부모 페이지 이미지 업데이트 시작:', imgType); // 해당 img_type의 남은 이미지 조회 $.ajax({ url: '/article/receipt/getImages', method: 'GET', data: { rsrv_sq: rsrv_sq, img_type: imgType }, success: function(response) { console.log('[updateParentImage] 응답:', response); const imgElement = $('#photo-display2_' + imgType); if (imgElement.length === 0) { console.log('[updateParentImage] 이미지 엘리먼트를 찾을 수 없음, 업데이트 스킵:', '#photo-display2_' + imgType); return; } const parentAnchor = imgElement.parent('a'); if (response.success && response.images && response.images.length > 0) { // 첫 번째 이미지로 업데이트 const firstImage = response.images[0]; imgElement.attr('src', firstImage.original_url); // a 태그가 있으면 onclick도 업데이트 if (parentAnchor.length > 0) { parentAnchor.attr('onclick', "fn_preview('" + firstImage.original_url + "')"); } console.log('[updateParentImage] 이미지 업데이트 완료:', firstImage.original_url); } else { // 이미지가 없으면 기본 이미지로 imgElement.attr('src', '/plugin/img/photo.gif'); // a 태그가 있으면 제거 (기본 이미지는 클릭 불가) if (parentAnchor.length > 0) { parentAnchor.removeAttr('onclick'); } console.log('[updateParentImage] 기본 이미지로 변경'); } }, error: function(xhr, status, error) { console.error('[updateParentImage] AJAX 실패, 업데이트 스킵:', error); } }); } // 기존 이미지를 Dropzone에 로드 function loadExistingImages(imgType, imgSubType) { console.log('[loadExistingImages] 호출:', imgType, imgSubType); const rsrv_sq = $("#frm_file_info [name=rsrv_sq]").val(); if (!rsrv_sq) { console.log('[loadExistingImages] rsrv_sq가 없음'); return; } $.ajax({ url: '/article/receipt/getImages', method: 'GET', data: { rsrv_sq: rsrv_sq, img_type: imgType }, success: function(response) { console.log('[loadExistingImages] 응답:', response); if (response.success && response.images && response.images.length > 0) { response.images.forEach(function(img) { // 가짜 File 객체 생성 const mockFile = { name: img.img_filenm, size: img.file_size || 0, img_sq: img.img_sq, img_type: img.img_type, isExisting: true, // 기존 파일 표시 accepted: true, status: Dropzone.ADDED, original_url: img.original_url }; // Dropzone에 파일 추가 dz.emit("addedfile", mockFile); dz.emit("thumbnail", mockFile, img.thumbnail_url); dz.emit("complete", mockFile); // 썸네일 이미지 로드 실패 시 원본으로 대체 setTimeout(function() { const preview = mockFile.previewElement; if (preview) { const thumbnailImg = preview.querySelector('[data-dz-thumbnail]'); if (thumbnailImg) { thumbnailImg.onerror = function() { console.log('[loadExistingImages] 썸네일 로드 실패, 원본 사용:', mockFile.name); this.src = img.original_url; this.onerror = null; // 무한 루프 방지 }; } } }, 100); // files 배열에 추가 (Dropzone 내부 상태 관리) dz.files.push(mockFile); console.log('[loadExistingImages] 파일 추가됨:', mockFile.name); }); } else { console.log('[loadExistingImages] 이미지 없음'); } }, error: function(xhr, status, error) { console.error('[loadExistingImages] 실패:', error); } }); } // 단지 목록 가져오기 function loadComplexList() { console.log('[loadComplexList] 단지 목록 로드 시작'); var legalDivisionNumber = $('input[name="rcpt_dong"]').val() ?? ''; console.log('[loadComplexList] legalDivisionNumber:', legalDivisionNumber); var realEstateType = $('input[name="rcpt_product"]').val() ?? ''; // 값이 없을때 if ( legalDivisionNumber == '' ) return; let sendData = { legalDivisionNumber: legalDivisionNumber, }; if (realEstateType) { sendData.realEstateType = realEstateType; } console.log('[loadComplexList] 요청 데이터:', sendData); $.ajax({ url: '/common/getComplexList', method: 'GET', data : sendData, success: function(result) { console.log('[loadComplexList] 응답:', result); if (result.code === 'success' && result.data) { var options = ''; result.data.forEach(function(complex) { options += ''; }); $('#rcpt_hscp_nm').html(options); // 현재 선택된 단지가 있으면 선택 var currentHscpNo = $('input[name="rcpt_hscp_no"]').val(); if (currentHscpNo) { $('#rcpt_hscp_nm').val(currentHscpNo); // 평형 정보도 로드 // loadPyeongInfo(currentHscpNo); } console.log('[loadComplexList] 단지 목록 로드 완료, 개수:', result.data.length); } else { console.warn('[loadComplexList] 단지 목록 응답이 올바르지 않습니다:', result); } }, error: function(xhr, status, error) { console.error('[loadComplexList] 단지 목록 요청 실패:', error); } }); } function loadPyeongInfo() { var currentHscpNo = $('input[name="rcpt_hscp_no"]').val(); var currentPtpNo = $('input[name="rcpt_ptp_no"]').val(); var realEstateType = $('input[name="rcpt_product"]').val() ?? ''; console.log('[loadPyeongInfo] 평형 정보 로드 시작, hscpNo:', currentHscpNo); $.ajax({ url: '/common/getPyeongInfo', method: 'GET', data: { complexNumber: currentHscpNo, realEstateType: realEstateType }, success: function(result) { console.log('[loadPyeongInfo] 응답:', result); if (result.code === 'success' && result.data) { var options = ''; result.data.forEach(function(pyeong) { options += ''; }); $('#rcpt_ptp_nm').html(options); if (currentPtpNo) { $('#rcpt_ptp_nm').val(currentPtpNo); } console.log('[loadPyeongInfo] 평형 정보 로드 완료, 개수:', result.data.length); } else { console.warn('[loadPyeongInfo] 평형 정보 응답이 올바르지 않습니다:', result); } }, error: function(xhr, status, error) { console.error('[loadPyeongInfo] 평형 정보 요청 실패:', error); } }); } // 파일업로드 모달 오픈 function viewFilePop(imgType, imgSubType) { console.log('[viewFilePop] 호출:', imgType, imgSubType); $("#frm_file_info [name=img_type]").val(imgType || ""); $("#frm_file_info [name=img_sub_type]").val(imgSubType || ""); // Dropzone 초기화 (imgType 전달) initDropzone(imgType); // 기존 이미지 로드 setTimeout(function() { loadExistingImages(imgType, imgSubType); }, 300); $("#uploadModal").modal("show"); } // 모달 닫힘 이벤트 처리 $(document).ready(function() { // 중복 등록 방지를 위해 기존 핸들러 제거 후 재등록 $('#uploadModal').off('hide.bs.modal').on('hide.bs.modal', function () { console.log('[Modal] ========== hide.bs.modal 시작 =========='); console.log('[Modal] isModalClosing 설정 전:', window.isModalClosing); window.isModalClosing = true; console.log('[Modal] isModalClosing 설정 후:', window.isModalClosing); // 중요: Dropzone removeFile() 호출하지 않음! // removeFile()을 호출하면 removedfile 이벤트가 발생하여 의도치 않은 삭제가 발생할 수 있음 // 대신 모달이 완전히 닫힌 후 Dropzone을 destroy하여 정리함 if (dz) { console.log('[Modal] 현재 Dropzone 파일 목록:'); dz.files.forEach(function(f, idx) { console.log('[Modal] ' + (idx+1) + '. ' + f.name + ', isExisting:', f.isExisting + ', status:', f.status + ', img_sq:', f.img_sq); }); console.log('[Modal] 주의: 파일 제거 하지 않음 (destroy로 정리 예정)'); } }); $('#uploadModal').off('hidden.bs.modal').on('hidden.bs.modal', function () { console.log('[Modal] ========== hidden.bs.modal 시작 =========='); // 삭제 플래그 초기화 window.isBulkDeleting = false; window.deletePromises = []; window.uploadedFiles = []; if (sortable) { console.log('[Modal] Sortable 정리'); sortable.destroy(); sortable = null; } if (dz) { console.log('[Modal] Dropzone 정리 (destroy)'); dz.destroy(); dz = null; } // 모든 정리 완료 후 플래그 초기화 window.isModalClosing = false; console.log('[Modal] 모든 정리 완료, isModalClosing:', window.isModalClosing); }); // ========== AJAX로 이미지 불러오기 ========== // 페이지 로드 시 한 번에 모든 이미지를 불러와서 렌더링 const rsrvSq = $("#frm_file_info [name=rsrv_sq]").val(); if (rsrvSq) { console.log('[PageLoad] AJAX로 이미지 로딩 시작, rsrv_sq:', rsrvSq); loadAllImages(rsrvSq); } else { console.warn('[PageLoad] rsrv_sq가 없어서 이미지를 불러올 수 없습니다.'); } // ========== 단지 목록 불러오기 ========== // 페이지 로드 시 단지 목록을 가져와서 select 박스 채우기 loadComplexList(); // 평형 목록 불러 오기 loadPyeongInfo(); $(document).on('change', '#rcpt_hscp_nm', function() { const selectedHscpNo = $(this).val(); console.log('[ComplexSelect] 단지 선택 변경, hscp_no:', selectedHscpNo); $('input[name="rcpt_hscp_no"]').val(selectedHscpNo); loadPyeongInfo(); }); });