2211 lines
70 KiB
JavaScript
2211 lines
70 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 trade_type_onchange() {
|
|
var trade_type = $('#trade_type').val();
|
|
if (trade_type == 'B2' || trade_type == 'B3') {
|
|
// 월세...
|
|
$('#div_trade_type_price_monthly').show();
|
|
} else {
|
|
$('#div_trade_type_price_monthly').hide();
|
|
}
|
|
}
|
|
|
|
// 가격수정 btn
|
|
function editPriceInfo() {
|
|
var rcpt_product = $('#rcpt_product').val();
|
|
var trade_type = $('#trade_type').val();
|
|
|
|
$("#trade_type").prop("disabled", false);
|
|
|
|
$("#rcpt_product_info2").prop("disabled", false);
|
|
$("#rcpt_product_info3").prop("disabled", false);
|
|
|
|
if (trade_type == "A1") {
|
|
if (rcpt_product == 'A01' || rcpt_product == 'A02' || rcpt_product == 'A03' || rcpt_product == 'B01' || rcpt_product == 'B02' || rcpt_product == 'B03') {
|
|
$("#rcpt_product_info4").prop("disabled", false);
|
|
$("#rcpt_product_info5").prop("disabled", false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 가격수정 저장
|
|
function modifyPriceInfo() {
|
|
Swal.fire({
|
|
text: "저장 하시겠습니까?",
|
|
icon: "warning",
|
|
showCancelButton: true,
|
|
confirmButtonText: "예",
|
|
cancelButtonText: "아니오",
|
|
confirmButtonColor: "#3085d6",
|
|
cancelButtonColor: "#d33",
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
var params = {
|
|
'rcpt_sq': window.rcpt_sq,
|
|
'agent_tel': $("#agent_tel").val(),
|
|
};
|
|
|
|
callAjax("/article/receipt/modifyPriceInfo", params, fn_result);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 연락가능전화 저장
|
|
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': window.rcpt_sq,
|
|
'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 maxFilesConfig = {
|
|
'I4': 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 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가 없어서 이미지를 불러올 수 없습니다.');
|
|
}
|
|
});
|