Files
confirms/public/js/article/receipt/detail.js
2026-04-27 15:03:36 +09:00

2405 lines
78 KiB
JavaScript

// 거주인 녹취정보 상세 페이지 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('<img id="photo-display2_I1" src="/plugin/img/photo.gif" alt="홍보확인서" class="w-100 object-fit-contain">');
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('<img src="/plugin/img/photo.gif" alt="촬영동의서" class="w-100 object-fit-contain">');
break;
case 'I2': // 현장확인내역서 - 이미지를 photo.gif로 변경
$('img[alt="현장확인내역서"]').attr('src', '/plugin/img/photo.gif')
.parent('a').replaceWith('<img src="/plugin/img/photo.gif" alt="현장확인내역서" class="w-100 object-fit-contain">');
break;
case 'V1': // 동영상 - 이미지를 photo.gif로 변경
$('img[alt="동영상"]').attr('src', '/plugin/img/photo.gif')
.parent('a').replaceWith('<img src="/plugin/img/photo.gif" alt="동영상" class="w-100 object-fit-contain">');
break;
case 'I5': // 평면도 - 이미지를 photo.gif로 변경
$('img[alt="평면도"]').attr('src', '/plugin/img/photo.gif')
.parent('a').replaceWith('<img src="/plugin/img/photo.gif" alt="평면도" class="w-100 object-fit-contain">');
break;
case 'I11': // 체크리스트 - 이미지를 photo.gif로 변경
$('img[alt="체크리스트"]').attr('src', '/plugin/img/photo.gif')
.parent('a').replaceWith('<img src="/plugin/img/photo.gif" alt="체크리스트" class="w-100 object-fit-contain">');
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(`
<a onclick="fn_preview('${fullImageUrl}')">
<img id="photo-display2_I1" src="${fullImageUrl}" alt="홍보확인서" class="w-100 object-fit-contain">
</a>
`);
break;
case 'I10': // 촬영동의서
const $i10Container = $('img[alt="촬영동의서"]').closest('.ratio');
$i10Container.html(`
<a onclick="fn_preview('${fullImageUrl}')">
<img src="${thumbImageUrl}" alt="촬영동의서" class="w-100 object-fit-contain">
</a>
`);
break;
case 'I2': // 현장확인내역서
const $i2Container = $('img[alt="현장확인내역서"]').closest('.ratio');
$i2Container.html(`
<a onclick="fn_preview('${fullImageUrl}')">
<img src="${thumbImageUrl}" alt="현장확인내역서" class="w-100 object-fit-contain">
</a>
`);
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 = `
<div style="width: 150px;">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden">
<a onclick="fn_preview('${fullImageUrl}')" class="d-flex align-items-center justify-content-center">
<img src="${thumbImageUrl}" alt="분양권" class="w-100 h-100 object-fit-contain">
</a>
</div>
</div>
`;
$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 = `
<div class="thumb-card">
<input type="text" class="form-control form-control-sm mb-1" name="i4_order[]" size="3" value="${imageData.view_odr || '1'}" />
<input type="hidden" name="i4_seq[]" value="${imageData.img_sq}" size="2" />
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden">
<a onclick="fn_preview('${fullImageUrl}')">
<img src="${thumbImageUrl}" alt="매물사진" class="w-100 h-100 object-fit-contain">
</a>
</div>
</div>
`;
$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 = `
<div class="thumb-card">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden mb-1">
<a onclick="fn_preview('${fullImageUrl}')" class="d-flex align-items-center justify-content-center">
<img src="${thumbImageUrl}" alt="360이미지" class="w-100 h-100 object-fit-contain">
</a>
</div>
<input class="form-control form-control-sm" type="text" name="img_location_${imageData.img_sq}"
id="img_location_${imageData.img_sq}" value="${imageData.img_location || ''}" size="11" style="width: 160px;" placeholder="촬영위치">
</div>
`;
console.log('[updateImageInParent] I9 이미지 추가');
$i9Container.append(i9Html);
break;
case 'V1': // 동영상
const $v1Container = $('img[alt="동영상"]').closest('.ratio');
$v1Container.html(`
<a onclick="fn_preview('${fullImageUrl}', 'vdo')">
<img src="/plugin/img/video.png" alt="동영상" class="w-100 object-fit-contain">
</a>
`);
break;
case 'I5': // 평면도
const $i5Container = $('img[alt="평면도"]').closest('.ratio');
$i5Container.html(`
<a onclick="fn_preview('${fullImageUrl}')">
<img src="${thumbImageUrl}" alt="평면도" class="w-100 object-fit-contain">
</a>
`);
break;
case 'I11': // 체크리스트
const $i11Container = $('img[alt="체크리스트"]').closest('.ratio');
$i11Container.html(`
<a onclick="fn_preview('${fullImageUrl}')">
<img src="${thumbImageUrl}" alt="체크리스트" class="w-100 object-fit-contain">
</a>
`);
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 = `<img ${imgId ? 'id="'+imgId+'"' : ''} src="/plugin/img/photo.gif" alt="${altText}" class="w-100 object-fit-contain">`;
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 = `<a onclick="fn_preview('${originalUrl}')">
<img ${imgId ? 'id="'+imgId+'"' : ''} src="${thumbnailUrl}" alt="${altText}" class="w-100 object-fit-contain">
</a>`;
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 = `
<div style="width: 150px;">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden">
<img src="/plugin/img/photo.gif" alt="${altText}" class="w-100 h-100 object-fit-contain">
</div>
</div>
`;
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 = `
<div style="width: 150px;">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden">
<a onclick="fn_preview('${originalUrl}')" class="d-flex align-items-center justify-content-center">
<img src="${thumbnailUrl}" alt="${altText}" class="w-100 h-100 object-fit-contain">
</a>
</div>
</div>
`;
container.append(html);
});
}
// 매물사진 렌더링 (분양권과 동일한 형태)
function renderPropertyImages(images) {
const container = $('#property-images-container');
if (container.length === 0) return;
container.empty();
// 이미지가 없으면 기본 이미지 표시
if (images.length === 0) {
const defaultHtml = `
<div class="property-image-item" style="width: 150px; position: relative;">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden">
<img src="/plugin/img/photo.gif" alt="매물사진" class="w-100 h-100 object-fit-contain">
</div>
</div>
`;
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 = `
<div class="property-image-item" style="width: 150px; position: relative; cursor: move;" data-img-sq="${img.img_sq}" data-view-order="${img.view_odr || orderNum}">
<div class="image-order-badge" style="position: absolute; top: 1px; left: 1px; background: #6c9ee8; color: white; min-width: 24px; height: 24px; padding: 0 6px; border-radius: 12px; display: inline-flex; align-items-center; justify-content: center; font-size: 13px; font-weight: 500; z-index: 10; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">${orderNum}</div>
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden">
<a onclick="fn_preview('${originalUrl}')" class="d-flex align-items-center justify-content-center">
<img src="${thumbnailUrl}" alt="매물사진" class="w-100 h-100 object-fit-contain">
</a>
</div>
</div>
`;
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 = `
<div class="thumb-card">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden mb-1">
<img src="/plugin/img/photo.gif" alt="360이미지" class="w-100 h-100 object-fit-contain">
</div>
<input class="form-control form-control-sm" type="text" placeholder="촬영위치" style="width: 160px;">
</div>
`;
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 = `
<div class="thumb-card">
<div class="ratio ratio-4x3 bg-light rounded-2 overflow-hidden mb-1">
<a onclick="fn_preview('${originalUrl}')" class="d-flex align-items-center justify-content-center">
<img src="${thumbnailUrl}" alt="360이미지" class="w-100 h-100 object-fit-contain">
</a>
</div>
<input class="form-control form-control-sm" type="text" name="img_location_${imgSq}" id="img_location_${imgSq}" value="${imgLocation}" size="11" style="width: 160px;" placeholder="촬영위치">
</div>
`;
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: '<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div><p class="mt-2">삭제 중...</p>'
});
// 해당 타입의 이미지 목록 조회 후 삭제
$.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: '<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div><p class="mt-2">다운로드 준비 중...</p>'
});
// 다운로드 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: '<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div><p class="mt-2">저장 중...</p>'
});
// 서버에 저장 요청
$.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 ? `
<div class="d-flex align-items-center gap-2 flex-wrap">
<a class="fw-semibold text-decoration-none"
href="/article/receipt/download_file?record_sq=${data.record.record_sq}">
${data.record.record_orignm}
</a>
<span class="badge bg-light text-dark border">${data.record.record_size} KB</span>
<span class="badge bg-light text-dark border">${data.record.insert_tm}</span>
</div>
` : '<span class="text-muted small">등록된 파일 없음</span>';
// 음성파일 영역 업데이트 (파일 업로드 버튼 앞까지)
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 = $('<tbody></tbody>').insertAfter($thead);
}
}
$tbody.empty();
if (historyData && historyData.length > 0) {
historyData.forEach(h => {
const row = `
<tr>
<td style="text-align: center;">${h.rcpt_stat_nm || ''}</td>
<td style="text-align: center;">${h.changed_type_nm || ''}</td>
<td style="text-align: center;">${h.changed_id || ''}</td>
<td style="text-align: center;">${h.changed_tm || ''}</td>
<td>${h.remark || ''}</td>
</tr>
`;
$tbody.append(row);
});
} else {
$tbody.append('<tr><td colspan="5" style="text-align: center;">이력이 없습니다.</td></tr>');
}
}
// 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 = '<option value="">단지 선택</option>';
result.data.forEach(function(complex) {
options += '<option value="' + complex.complexNumber + '">' + complex.name + '</option>';
});
$('#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 = '<option value="">평형 선택</option>';
result.data.forEach(function(pyeong) {
options += '<option value="' + pyeong.pyeongTypeNumber + '">' + pyeong.pyeongTypeName + '</option>';
});
$('#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();
});
});