merge master into develop - CscListRForm.jsp FAQ 아코디언 버그 수정
This commit is contained in:
214
build/resources/main/static/mobile/js/bnft/cpslmkn/Cpslmkn.js
Normal file
214
build/resources/main/static/mobile/js/bnft/cpslmkn/Cpslmkn.js
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Cpslmkn.js
|
||||
*
|
||||
* 캡슐 메이킹 게임 기능을 관리하는 모듈
|
||||
* 사용자가 제품을 선택하고 게임을 진행할 수 있는 인터페이스 제공
|
||||
*/
|
||||
$(function() {
|
||||
// 초기 컨텐츠 숨김 - 인트로 모달이 먼저 표시되어야 함
|
||||
$('.content-wrap').hide();
|
||||
|
||||
// 인트로 모달 초기화 및 표시 - 게임 시작 전 소개 화면
|
||||
initIntroModal();
|
||||
|
||||
// 제품 선택 이벤트 바인딩 - 사용자가 게임을 진행할 제품 선택
|
||||
bindProductSelection();
|
||||
|
||||
// 시작하기 버튼 이벤트 바인딩 - 제품 선택 후 게임 시작
|
||||
bindStartButton();
|
||||
|
||||
/**
|
||||
* 제품 선택 시 시각적 피드백 및 상태 업데이트
|
||||
* 사용자가 게임 제품을 클릭하면 해당 항목 강조 표시 및 선택 처리
|
||||
*/
|
||||
$('.gameProduct').on('click', function() {
|
||||
// 모든 gameProduct에서 selected 클래스 제거 - 기존 선택 초기화
|
||||
$('.gameProduct').removeClass('selected');
|
||||
|
||||
// 클릭된 gameProduct에 selected 클래스 추가 - 현재 선택 항목 강조
|
||||
$(this).addClass('selected');
|
||||
|
||||
// 해당 radio 버튼 선택 - UI와 실제 form 값 동기화
|
||||
$(this).find('input[type="radio"]').prop('checked', true);
|
||||
|
||||
// 시작 버튼 활성화 - 제품 선택 완료 시 다음 단계 진행 가능
|
||||
$('.bottomBtnControls .btn').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 인트로 모달 초기화 및 표시 함수
|
||||
* 게임 시작 전 소개 슬라이드를 표시하고 관련 이벤트 설정
|
||||
*/
|
||||
function initIntroModal() {
|
||||
// 슬라이드 및 페이징 요소 참조
|
||||
const $slides = $('.gameIntroWrap'); // 각 소개 슬라이드
|
||||
const $paging = $('.gamePaging'); // 페이징 도트 컨테이너
|
||||
let currentSlide = 0; // 현재 표시 중인 슬라이드 인덱스
|
||||
|
||||
// 터치 이벤트 관련 변수
|
||||
let touchStartX = 0;
|
||||
let touchStartY = 0;
|
||||
let touchEndX = 0;
|
||||
let touchEndY = 0;
|
||||
let isSwipe = false;
|
||||
|
||||
// 페이징 도트 동적 생성 - 슬라이드 개수만큼 도트 추가
|
||||
$slides.each(function() {
|
||||
$paging.append('<span></span>');
|
||||
});
|
||||
|
||||
// 초기 슬라이드 표시 - 첫 번째 슬라이드(인덱스 0) 표시
|
||||
showSlide(0);
|
||||
|
||||
// 터치 시작 이벤트
|
||||
$('.gameIntroPaging').on('touchstart', function(e) {
|
||||
touchStartX = e.originalEvent.touches[0].clientX;
|
||||
touchStartY = e.originalEvent.touches[0].clientY;
|
||||
isSwipe = false;
|
||||
});
|
||||
|
||||
// 터치 이동 이벤트
|
||||
$('.gameIntroPaging').on('touchmove', function(e) {
|
||||
e.preventDefault(); // 스크롤 방지
|
||||
touchEndX = e.originalEvent.touches[0].clientX;
|
||||
touchEndY = e.originalEvent.touches[0].clientY;
|
||||
|
||||
// 움직임이 있으면 스와이프로 판단
|
||||
const deltaX = Math.abs(touchEndX - touchStartX);
|
||||
const deltaY = Math.abs(touchEndY - touchStartY);
|
||||
|
||||
if (deltaX > 10 || deltaY > 10) {
|
||||
isSwipe = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 터치 종료 이벤트
|
||||
$('.gameIntroPaging').on('touchend', function(e) {
|
||||
if (!isSwipe) return; // 스와이프가 아니면 무시
|
||||
|
||||
const deltaX = touchEndX - touchStartX;
|
||||
const deltaY = touchEndY - touchStartY;
|
||||
const minSwipeDistance = 50; // 최소 스와이프 거리
|
||||
|
||||
// 좌우 스와이프만 처리 (상하 스와이프는 무시)
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > minSwipeDistance) {
|
||||
if (deltaX > 0) {
|
||||
// 오른쪽 스와이프 - 이전 슬라이드
|
||||
const prevIndex = currentSlide === 0 ? $slides.length - 1 : currentSlide - 1;
|
||||
showSlide(prevIndex);
|
||||
} else {
|
||||
// 왼쪽 스와이프 - 다음 슬라이드
|
||||
const nextIndex = (currentSlide + 1) % $slides.length;
|
||||
showSlide(nextIndex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 기존 클릭 이벤트 (터치와 충돌하지 않도록 수정)
|
||||
$('.gameIntroPaging').on('click', function(e) {
|
||||
// 스와이프가 발생했으면 클릭 이벤트 무시
|
||||
if (isSwipe) {
|
||||
isSwipe = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 다음 슬라이드로 순환 이동 (마지막 슬라이드에서는 처음으로)
|
||||
showSlide((currentSlide + 1) % $slides.length);
|
||||
});
|
||||
|
||||
// 페이징 도트 클릭 시 해당 슬라이드로 직접 이동
|
||||
$paging.on('click', 'span', function() {
|
||||
showSlide($(this).index());
|
||||
});
|
||||
|
||||
/**
|
||||
* 특정 슬라이드 표시 함수
|
||||
* 지정된 인덱스의 슬라이드를 표시하고 페이징 상태 업데이트
|
||||
* @param {number} index - 표시할 슬라이드 인덱스
|
||||
*/
|
||||
function showSlide(index) {
|
||||
// 모든 슬라이드 숨김 후 해당 인덱스의 슬라이드만 표시
|
||||
$slides.hide().eq(index).show();
|
||||
// 페이징 도트 상태 업데이트 - 현재 슬라이드에 해당하는 도트만 활성화
|
||||
$paging.find('span').removeClass('active').eq(index).addClass('active');
|
||||
// 현재 슬라이드 인덱스 업데이트
|
||||
currentSlide = index;
|
||||
}
|
||||
|
||||
// 시작하기 버튼 클릭 이벤트 - 인트로 종료 및 메인 컨텐츠 표시
|
||||
$('#goCapsuleMaking').on('click', function() {
|
||||
// 인트로 모달 닫기
|
||||
MicroModal.close('intro-modal');
|
||||
// 메인 컨텐츠 표시
|
||||
$('.content-wrap').show();
|
||||
});
|
||||
|
||||
// 인트로 모달 표시 - 페이지 로드 시 자동 표시
|
||||
MicroModal.show('intro-modal', {
|
||||
disableScroll: true, // 모달 표시 시 배경 스크롤 방지
|
||||
awaitCloseAnimation: true // 닫기 애니메이션 완료 대기
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 제품 선택 이벤트 설정함수
|
||||
* 라디오 버튼으로 제품 선택 시 시작 버튼 활성화
|
||||
*/
|
||||
function bindProductSelection() {
|
||||
// 제품 라디오 버튼 변경 시 시작 버튼 활성화
|
||||
$('input[name="gameproduct"]').on('change', function() {
|
||||
$('.bottomBtnControls .btn').prop('disabled', false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 시작하기 버튼 이벤트 설정 함수
|
||||
* 제품 선택 후 게임 시작 버튼 클릭 시 확인 모달 표시 및 게임 생성 요청
|
||||
*/
|
||||
function bindStartButton() {
|
||||
// 초기에 시작 버튼 비활성화 상태로 설정하고 클릭 이벤트 바인딩
|
||||
$('.bottomBtnControls .btn').prop('disabled', true).on('click', function() {
|
||||
// 선택된 제품 라디오 버튼 확인
|
||||
const selectedProduct = $('input[name="gameproduct"]:checked');
|
||||
|
||||
// 제품이 선택된 경우에만 처리
|
||||
if (selectedProduct.length > 0) {
|
||||
// 선택된 제품명을 확인 모달에 표시
|
||||
const productName = selectedProduct.closest('.gameProduct').find('.gpTit .tit').text();
|
||||
$('#selectedProductName').text(productName);
|
||||
|
||||
// 확인 모달 표시 - 사용자에게 최종 확인 요청
|
||||
MicroModal.show('modal-1', {
|
||||
disableScroll: true,
|
||||
awaitCloseAnimation: true
|
||||
});
|
||||
|
||||
// 모달의 '예' 버튼 클릭 이벤트 - 게임 생성 요청 처리
|
||||
$('.microModal__btn-primary').off('click').on('click', function() {
|
||||
// 선택된 제품의 혜택 고유번호 가져오기
|
||||
const bnfInnb = selectedProduct.val();
|
||||
|
||||
// AJAX 대신 폼 제출 방식으로 서버에 게임 생성 요청
|
||||
const form = $('<form>', {
|
||||
'method': 'post',
|
||||
'action': '/sopmal/bnft/createGame'
|
||||
}).append($('<input>', {
|
||||
'type': 'hidden',
|
||||
'name': 'bnfInnb',
|
||||
'value': bnfInnb
|
||||
}));
|
||||
|
||||
const sopmalCd = $('#sopmalCd').val();
|
||||
form.append($('<input>', {
|
||||
'type': 'hidden',
|
||||
'name': 'sopmalCd',
|
||||
'value': sopmalCd
|
||||
}));
|
||||
// 동적으로 생성한 폼을 body에 추가하고 제출
|
||||
$('body').append(form);
|
||||
form.submit();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* CpslmknCollectList.js
|
||||
*
|
||||
* 캡슐 메이킹 게임의 특별 이벤트 슬라이더 기능 구현
|
||||
* 사용자에게 특별 이벤트 정보를 슬라이드 형식으로 제공
|
||||
*/
|
||||
|
||||
/**
|
||||
* 특별 이벤트 슬라이더 초기화 및 동작 구현 함수
|
||||
* 슬라이드 자동 전환, 페이지네이션, 터치 이벤트 등 모든 슬라이더 관련 기능 설정
|
||||
*/
|
||||
function initializeSpecialEventSlider() {
|
||||
// 주요 변수 초기화
|
||||
const $slides = $('.specialEventSlider .innerIcon'); // 슬라이드 요소들(각 이벤트 아이콘)
|
||||
const slideCount = $slides.length; // 전체 슬라이드 개수
|
||||
let currentIndex = 0; // 현재 표시 중인 슬라이드 인덱스
|
||||
let interval; // 자동 슬라이드 전환용 타이머 ID
|
||||
|
||||
// 슬라이드가 1개 이하인 경우 첫 번째 슬라이드만 표시하고 함수 종료
|
||||
if (slideCount <= 1) {
|
||||
if (slideCount === 1) {
|
||||
$slides.eq(0).show(); // 첫 번째 슬라이드만 표시
|
||||
}
|
||||
return; // 추가 기능 없이 함수 종료
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 인덱스의 슬라이드 표시 함수
|
||||
* 지정된 인덱스의 슬라이드만 표시하고 페이지네이션 상태 업데이트
|
||||
* @param {number} index - 표시할 슬라이드 인덱스
|
||||
*/
|
||||
function showSlide(index) {
|
||||
$slides.hide(); // 모든 슬라이드 숨김
|
||||
$slides.eq(index).fadeIn(); // 선택한 슬라이드만 페이드인 효과로 표시
|
||||
updatePagination(index); // 페이지네이션 업데이트
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지네이션 버튼 생성 함수
|
||||
* 슬라이드 개수만큼 페이지네이션 버튼을 동적으로 생성
|
||||
*/
|
||||
function createPagination() {
|
||||
const $pagination = $('.pagination'); // 페이지네이션 컨테이너
|
||||
$pagination.empty(); // 기존 버튼 제거
|
||||
|
||||
// 슬라이드가 1개 이하인 경우 페이지네이션 숨김
|
||||
if (slideCount <= 1) {
|
||||
$pagination.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// 슬라이드 개수만큼 버튼 생성
|
||||
for (let i = 0; i < slideCount; i++) {
|
||||
$pagination.append(`<button data-index="${i}"></button>`); // 각 버튼에 인덱스 데이터 속성 추가
|
||||
}
|
||||
$pagination.show(); // 페이지네이션 표시
|
||||
updatePagination(0); // 초기 페이지네이션 상태 설정 (첫 번째 버튼 활성화)
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지네이션 상태 업데이트 함수
|
||||
* 현재 표시 중인 슬라이드에 해당하는 페이지네이션 버튼만 활성화
|
||||
* @param {number} activeIndex - 활성화할 페이지네이션 버튼 인덱스
|
||||
*/
|
||||
function updatePagination(activeIndex) {
|
||||
$('.pagination button').removeClass('active'); // 모든 버튼에서 활성 클래스 제거
|
||||
$('.pagination button').eq(activeIndex).addClass('active'); // 현재 슬라이드 인덱스에 해당하는 버튼만 활성화
|
||||
}
|
||||
|
||||
/**
|
||||
* 다음 슬라이드 표시 함수
|
||||
* 현재 슬라이드의 다음 슬라이드로 이동 (마지막 슬라이드에서는 처음으로 순환)
|
||||
*/
|
||||
function nextSlide() {
|
||||
// 순환 구조로 인덱스 계산 (마지막 슬라이드에서 다음은 첫 번째)
|
||||
currentIndex = (currentIndex + 1) % slideCount;
|
||||
showSlide(currentIndex); // 계산된 인덱스의 슬라이드 표시
|
||||
}
|
||||
|
||||
/**
|
||||
* 이전 슬라이드 표시 함수
|
||||
* 현재 슬라이드의 이전 슬라이드로 이동 (첫 번째 슬라이드에서는 마지막으로 순환)
|
||||
*/
|
||||
function previousSlide() {
|
||||
// 순환 구조로 인덱스 계산 (첫 번째 슬라이드에서 이전은 마지막)
|
||||
// slideCount를 더해 음수가 되는 것 방지
|
||||
currentIndex = (currentIndex - 1 + slideCount) % slideCount;
|
||||
showSlide(currentIndex); // 계산된 인덱스의 슬라이드 표시
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 슬라이드 시작 함수
|
||||
* 일정 시간 간격으로 다음 슬라이드로 자동 전환
|
||||
*/
|
||||
function startSliding() {
|
||||
interval = setInterval(nextSlide, 3000); // 3초마다 다음 슬라이드로 이동
|
||||
}
|
||||
|
||||
/**
|
||||
* 자동 슬라이드 정지 함수
|
||||
* 자동 슬라이드 타이머 정지
|
||||
*/
|
||||
function stopSliding() {
|
||||
clearInterval(interval); // 설정된 타이머 중지
|
||||
}
|
||||
|
||||
// 슬라이더 초기화 - 페이지네이션 생성, 첫 슬라이드 표시, 자동 슬라이딩 시작
|
||||
createPagination(); // 페이지네이션 버튼 생성
|
||||
showSlide(currentIndex); // 첫 번째 슬라이드 표시
|
||||
startSliding(); // 자동 슬라이드 시작
|
||||
|
||||
// 이벤트 핸들러 설정 - 사용자 상호작용
|
||||
|
||||
// 마우스 호버 시 자동 슬라이드 제어 (마우스 오버: 정지, 마우스 아웃: 재시작)
|
||||
$('.capsuleCollectTop').hover(stopSliding, startSliding);
|
||||
|
||||
// 페이지네이션 버튼 클릭 이벤트 - 해당 인덱스 슬라이드로 직접 이동
|
||||
$('.pagination').on('click', 'button', function() {
|
||||
currentIndex = $(this).data('index'); // 클릭된 버튼의 인덱스 가져오기
|
||||
showSlide(currentIndex); // 해당 인덱스 슬라이드 표시
|
||||
});
|
||||
|
||||
// 슬라이더 영역 클릭 시 다음 슬라이드로 이동 (슬라이드가 2개 이상일 때만)
|
||||
$('.capsuleCollectTop').on('click', function() {
|
||||
if (slideCount > 1) {
|
||||
nextSlide(); // 다음 슬라이드 표시
|
||||
}
|
||||
});
|
||||
|
||||
// 모바일 터치 이벤트 지원 - 스와이프 동작
|
||||
|
||||
// 터치 위치 추적 변수
|
||||
let touchStartX = 0; // 터치 시작 X 좌표
|
||||
let touchEndX = 0; // 터치 종료 X 좌표
|
||||
|
||||
// 터치 시작 위치 기록
|
||||
$('.capsuleCollectTop').on('touchstart', function(e) {
|
||||
if (slideCount > 1) {
|
||||
touchStartX = e.originalEvent.touches[0].clientX; // 첫 번째 터치 포인트의 X 좌표
|
||||
}
|
||||
});
|
||||
|
||||
// 터치 종료 시 스와이프 처리
|
||||
$('.capsuleCollectTop').on('touchend', function(e) {
|
||||
if (slideCount > 1) {
|
||||
touchEndX = e.originalEvent.changedTouches[0].clientX; // 터치 종료 시점의 X 좌표
|
||||
handleSwipe(); // 스와이프 처리 함수 호출
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 스와이프 처리 함수
|
||||
* 터치 시작과 종료 지점의 차이를 계산하여 스와이프 방향 결정
|
||||
*/
|
||||
function handleSwipe() {
|
||||
// 왼쪽으로 50px 이상 스와이프: 다음 슬라이드
|
||||
if (touchStartX - touchEndX > 50) {
|
||||
nextSlide(); // 다음 슬라이드로 이동
|
||||
}
|
||||
// 오른쪽으로 50px 이상 스와이프: 이전 슬라이드
|
||||
else if (touchEndX - touchStartX > 50) {
|
||||
previousSlide(); // 이전 슬라이드로 이동
|
||||
}
|
||||
// 50px 미만의 움직임은 무시 (우연한 터치 방지)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 로드 완료 시 실행
|
||||
* DOM이 완전히 로드된 후 슬라이더 초기화
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
initializeSpecialEventSlider(); // 특별 이벤트 슬라이더 초기화
|
||||
});
|
||||
@@ -0,0 +1,818 @@
|
||||
/**
|
||||
* CpslmknCreateGame.js
|
||||
*
|
||||
* 캡슐 메이킹 게임의 게임 생성 및 진행 기능을 관리하는 모듈
|
||||
* 사용자가 잎/도토리를 모아 캡슐을 생성하고 단계별로 완성해 나가는 기능 구현
|
||||
*/
|
||||
|
||||
/**
|
||||
* 문서 로드 완료 시 초기화 및 이벤트 바인딩
|
||||
* 페이지 로드 시 UI 초기화 및 모든 이벤트 리스너 설정
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
// MicroModal 기본 초기화 (여기서 한 번만 수행)
|
||||
MicroModal.init({
|
||||
disableScroll: true,
|
||||
awaitCloseAnimation: true
|
||||
// onShow, onClose 등 콜백은 여기서 정의 X
|
||||
});
|
||||
|
||||
// 기존 헤더 버튼 비활성화 및 새 버튼 추가
|
||||
initializeHeaderButtons();
|
||||
|
||||
// 버블 효과 생성
|
||||
// 화면에 움직이는 버블 애니메이션 요소를 동적으로 추가
|
||||
createBubbles();
|
||||
|
||||
// 플라스크 애니메이션 적용
|
||||
// 각 플라스크 요소에 애니메이션 효과 적용, 각 요소마다 다른 시간 간격으로 이미지 전환
|
||||
$('.flask1, .flask2, .flask3, .flask4, .flask5, .line1, .line2, .line3').each(function(index) {
|
||||
animateFlask($(this), [300, 1000, 200, 300, 500, 500, 300, 300][index]);
|
||||
});
|
||||
|
||||
// 초록잎/도토리 넣기 버튼 이벤트 리스너
|
||||
$('#cntGameItem').on('click', useLeaf);
|
||||
|
||||
// 초록잎/도토리 모으기 버튼 이벤트 리스너
|
||||
$('.btn.sm.outline.theme2').on('click', goToCollectPage);
|
||||
|
||||
// 클립 버튼 이벤트 리스너
|
||||
$(document).off('click', '#currentProgressButton').on('click', '#currentProgressButton', function() {
|
||||
const bnfInnb = $('#bnfInnb').val() || $('input[name="bnfInnb"]').val();
|
||||
|
||||
if (!bnfInnb) {
|
||||
console.error('bnfInnb 값을 찾을 수 없습니다.');
|
||||
alert('게임 정보를 불러올 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/capsuleGameInfo',
|
||||
type: 'POST',
|
||||
data: {
|
||||
bnfInnb: bnfInnb
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response && response.resBody) {
|
||||
try {
|
||||
const result = JSON.parse(response.resBody.result);
|
||||
console.log("서버에서 받은 원본 데이터:", result); // 디버깅용
|
||||
|
||||
// 모달 데이터 구성
|
||||
// 게이지 바 계산에 필요한 데이터 추가
|
||||
const modalData = {
|
||||
progressInfo: {
|
||||
progrsStep: result.currentProgressStep,
|
||||
totUseLeafCnt: result.totalUsedLeaf || result.totUseLeafCnt || 0,
|
||||
requiredLeaf: result.requiredLeaf || 100,
|
||||
stepProgressPercentage: result.stepProgressPercentage || 0
|
||||
},
|
||||
productName: result.rewardProductName,
|
||||
productDesc: result.rewardProductDesc || result.productDescription || '',
|
||||
thumbnailPath: result.productImagePath,
|
||||
cdnurl: $('#cdnurl').val() || '',
|
||||
sopmalCd: $('#sopmalCd').val(),
|
||||
importantNotice: {
|
||||
notice1: result.notice1 || $('#importantNotice').val() || '',
|
||||
notice2: result.notice2 || '',
|
||||
notice3: result.notice3 || ''
|
||||
}
|
||||
};
|
||||
|
||||
console.log("모달에 전달할 데이터:", modalData); // 디버깅용
|
||||
|
||||
// 먼저 모달 표시
|
||||
MicroModal.show('modal-1');
|
||||
|
||||
// 그 다음에 모달 내용 업데이트 (모달이 DOM에 추가된 후 업데이트해야 함)
|
||||
setTimeout(function() {
|
||||
if (typeof updateModalContent === 'function') {
|
||||
updateModalContent(modalData);
|
||||
} else {
|
||||
console.error('updateModalContent 함수를 찾을 수 없습니다.');
|
||||
alert('화면을 업데이트 할 수 없습니다.');
|
||||
}
|
||||
}, 100);
|
||||
} catch (e) {
|
||||
console.error('JSON 파싱 오류:', e, response.resBody.result);
|
||||
alert('진행 상태 데이터를 처리하는 중 오류가 발생했습니다.');
|
||||
}
|
||||
} else {
|
||||
const errorMessage = (response && response.resBody && response.resBody.message) ? response.resBody.message : '서버 응답 오류 또는 데이터 없음';
|
||||
console.error('데이터 로드 실패:', errorMessage, response);
|
||||
alert(`진행 상태를 불러오는데 실패했습니다. (${errorMessage})`);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX 요청 실패:', status, error);
|
||||
alert('진행 상태를 불러오는 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 설정 버튼 이벤트 리스너
|
||||
$('#CapsuleettingsButton').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
goToSettingsPage();
|
||||
});
|
||||
|
||||
// "다른 선물로 다시 시작" 버튼 이벤트 리스너 (modal-1 내부에 있음)
|
||||
// data-micromodal-trigger="modal-2" 속성으로 처리되므로 별도 핸들러 불필요할 수 있음
|
||||
// 단, CpslmknCreateGameModal.js 에서 #modal-2의 '처음부터 다시시작' 버튼 핸들러는 필요
|
||||
$(document).on('click', '#modal-2 .microModal__btn:first-child', function() {
|
||||
// resetCapsule 함수는 CpslmknCreateGameModal.js 에 정의됨
|
||||
if (typeof resetCapsule === 'function') {
|
||||
resetCapsule();
|
||||
} else {
|
||||
console.error('resetCapsule 함수를 찾을 수 없습니다.');
|
||||
}
|
||||
});
|
||||
|
||||
// 페이지 로드 시 해시 체크
|
||||
// URL에 해시가 있으면 해당 ID의 모달 열기
|
||||
if (window.location.hash) {
|
||||
var modalId = window.location.hash.replace('#', '');
|
||||
openModal(modalId);
|
||||
}
|
||||
|
||||
// 페이지 로드 시 초기 상태 설정
|
||||
// 게임 진행 상태를 서버에서 가져오거나 세션스토리지에서 복원
|
||||
initializeGameStatus();
|
||||
|
||||
// 모달 닫기 버튼 이벤트 리스너 추가
|
||||
// 모달 닫기 버튼 클릭 시 해당 모달 닫기
|
||||
$('.microModal__close2').on('click', function() {
|
||||
const modalId = $(this).closest('.microModal').attr('id');
|
||||
MicroModal.close(modalId);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* 버블 애니메이션 요소를 생성하여 화면에 추가하는 함수
|
||||
* 다양한 크기와 위치, 애니메이션 속도를 가진 버블을 생성
|
||||
*/
|
||||
function createBubbles() {
|
||||
// 버블 설정 배열 - 각 버블의 크기, 위치, 지연시간, 애니메이션 지속시간 정의
|
||||
const bubbles = [
|
||||
{ size: 12, left: '20%', bottom: '-10%', delay: 0, duration: 3 },
|
||||
{ size: 10, left: '60%', bottom: '10%', delay: 0.6, duration: 2.8 },
|
||||
{ size: 8, left: '30%', bottom: '30%', delay: 0.4, duration: 2.6 },
|
||||
{ size: 6, left: '70%', bottom: '50%', delay: 1, duration: 2.4 },
|
||||
{ size: 4, left: '40%', bottom: '70%', delay: 0.8, duration: 2.2 },
|
||||
{ size: 2, left: '50%', bottom: '90%', delay: 1.2, duration: 2 }
|
||||
];
|
||||
|
||||
// 각 버블 설정에 따라 DOM 요소 생성 및 스타일 적용
|
||||
$.each(bubbles, function(index, bubble) {
|
||||
$('<div>')
|
||||
.addClass('bubble')
|
||||
.css({
|
||||
width: bubble.size,
|
||||
height: bubble.size,
|
||||
left: bubble.left,
|
||||
bottom: bubble.bottom,
|
||||
animationDuration: bubble.duration + 's',
|
||||
animationDelay: bubble.delay + 's'
|
||||
})
|
||||
.appendTo('#bubbleContainer');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 플라스크 요소에 애니메이션 효과를 적용하는 함수
|
||||
* @param {jQuery} $flaskElement - 애니메이션을 적용할 플라스크 요소
|
||||
* @param {number} interval - 이미지 전환 간격(밀리초)
|
||||
*/
|
||||
function animateFlask($flaskElement, interval) {
|
||||
// 플라스크 내부의 모든 이미지 요소 선택
|
||||
const $images = $flaskElement.find('.svgContainer img');
|
||||
let currentIndex = 0;
|
||||
|
||||
// 다음 이미지를 표시하는 내부 함수
|
||||
function showNextImage() {
|
||||
$images.eq(currentIndex).hide(); // 현재 이미지 숨기기
|
||||
currentIndex = (currentIndex + 1) % $images.length; // 다음 이미지 인덱스 계산(순환)
|
||||
$images.eq(currentIndex).show(); // 다음 이미지 표시
|
||||
}
|
||||
|
||||
// 초기 상태 설정: 모든 이미지 숨기고 첫 번째 이미지만 표시
|
||||
$images.hide().eq(0).show();
|
||||
// 지정된 간격으로 이미지 전환 타이머 설정
|
||||
setInterval(showNextImage, interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* 완료 모달의 진행 상태 표시를 업데이트하는 함수
|
||||
* @param {number} currentStep - 현재 진행 단계(1~5)
|
||||
* @param {number} progressPercent - 현재 단계의 진행률(0~100)
|
||||
*/
|
||||
function updateCompleteModalProgress(currentStep, progressPercent) {
|
||||
// 모달 내부 진행바 요소 선택
|
||||
const $modalGameStatusBar = $('#modal-1 .gameStatusBar');
|
||||
// 모든 단계 표시에서 .on 클래스 제거 (초기화)
|
||||
$modalGameStatusBar.find('.step01, .step02, .step03, .step04, .last').removeClass('on');
|
||||
|
||||
// 현재 단계까지의 모든 단계에 .on 클래스 부여 (활성화)
|
||||
// switch문의 fall-through 특성을 활용하여 현재 단계까지 모든 단계 활성화
|
||||
switch (currentStep) {
|
||||
case 5:
|
||||
$modalGameStatusBar.find('.last').addClass('on'); // 5단계
|
||||
/* falls through */
|
||||
case 4:
|
||||
$modalGameStatusBar.find('.step04').addClass('on'); // 4단계
|
||||
/* falls through */
|
||||
case 3:
|
||||
$modalGameStatusBar.find('.step03').addClass('on'); // 3단계
|
||||
/* falls through */
|
||||
case 2:
|
||||
$modalGameStatusBar.find('.step02').addClass('on'); // 2단계
|
||||
/* falls through */
|
||||
case 1:
|
||||
$modalGameStatusBar.find('.step01').addClass('on'); // 1단계
|
||||
break;
|
||||
default:
|
||||
// 정상 단계가 아닐 경우 처리 X
|
||||
break;
|
||||
}
|
||||
|
||||
// 진행 바의 너비를 진행률에 맞게 조정
|
||||
$modalGameStatusBar.find('.per').css('width', `${progressPercent}%`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 게임 상태를 초기화하는 함수
|
||||
* 새로고침 여부에 관계없이 JSP hidden 값을 사용하여 초기화
|
||||
*/
|
||||
function initializeGameStatus() {
|
||||
// JSP에서 전달받은 hidden 값들로 게임 상태 구성
|
||||
const gameStatusFromHidden = {
|
||||
ownedLeafCount: parseInt($('#leafCount').val()) || 0,
|
||||
currentStep: parseInt($('#currentStep').val()) || parseInt($('#progressStep').val()) || 1,
|
||||
stepProgressPercentage: parseFloat($('#stepProgressPercentage').val()) || 0,
|
||||
isStepCompleted: false
|
||||
};
|
||||
|
||||
console.log('Hidden 값으로부터 구성된 게임 상태:', gameStatusFromHidden);
|
||||
|
||||
// UI 업데이트 및 세션스토리지에 상태 저장
|
||||
updateGameUI(gameStatusFromHidden);
|
||||
sessionStorage.setItem('gameStatus', JSON.stringify(gameStatusFromHidden));
|
||||
}
|
||||
|
||||
/**
|
||||
* 게임 UI를 업데이트하는 함수
|
||||
* @param {Object} data - 게임 상태 데이터 객체
|
||||
* @param {number} data.ownedLeafCount - 보유 아이템 수
|
||||
* @param {number} data.currentStep - 현재 진행 단계(1~5)
|
||||
* @param {number} data.stepProgressPercentage - 현재 단계의 진행률(0~100)
|
||||
* @param {boolean} data.isStepCompleted - 현재 단계 완료 여부
|
||||
*/
|
||||
function updateGameUI(data) {
|
||||
// 데이터 null 체크 - 데이터가 없으면 함수 종료
|
||||
if (!data) {
|
||||
console.error('데이터가 없습니다');
|
||||
return;
|
||||
}
|
||||
|
||||
// 안전한 값 할당 - 기본값 설정으로 undefined/null 방지
|
||||
const safeData = {
|
||||
ownedLeafCount: data.ownedLeafCount || 0,
|
||||
currentStep: data.currentStep || 1,
|
||||
stepProgressPercentage: data.stepProgressPercentage || 0,
|
||||
isStepCompleted: data.isStepCompleted || false
|
||||
};
|
||||
|
||||
// 아이템 개수 UI 업데이트 - 아이콘 유지하면서 개수만 변경
|
||||
const itemIcon = $('#ContingItem i').attr('class') || 'ico_leaf';
|
||||
$('#ContingItem').html(`<i class="${itemIcon}"></i>${safeData.ownedLeafCount}개`);
|
||||
|
||||
// 진행률 표시 업데이트
|
||||
const displayPercentage = data.stepProgressPercentage;
|
||||
|
||||
// 진행 바 너비 및 텍스트 업데이트
|
||||
$('.percentage-bar').css('width', `${displayPercentage}%`);
|
||||
$('.per-txt').text(`${Math.floor(displayPercentage)}% 채웠어요.`);
|
||||
$('.per-flask span').text(`${safeData.currentStep}단계`);
|
||||
|
||||
// 플라스크 이미지 클래스 업데이트 - 현재 단계에 맞는 이미지 표시
|
||||
$('.bar-container')
|
||||
.removeClass((index, className) => {
|
||||
// 정규식으로 flask0숫자 패턴의 모든 클래스 찾아 제거
|
||||
return (className.match(/(^|\s)flask0\d+/g) || []).join(' ');
|
||||
})
|
||||
.addClass(`flask0${safeData.currentStep}`);
|
||||
|
||||
// 세션스토리지 업데이트 - 최신 상태 저장
|
||||
sessionStorage.setItem('gameStatus', JSON.stringify(safeData));
|
||||
|
||||
// 모달 진행 상황 업데이트
|
||||
updateCompleteModalProgress(safeData.currentStep, displayPercentage);
|
||||
|
||||
// 5단계 완성 여부 확인 및 버튼 비활성화 처리
|
||||
// 최종 단계(5단계)가 100% 완료되면 버튼 비활성화 및 텍스트 변경
|
||||
if (safeData.currentStep === 5 && safeData.stepProgressPercentage >= 100) {
|
||||
// 버튼 비활성화
|
||||
$('#cntGameItem')
|
||||
.prop('disabled', true)
|
||||
.addClass('disabled')
|
||||
.text('캡슐 제작 완료!');
|
||||
|
||||
// 자동으로 완료 모달 표시 (기존 코드 수정)
|
||||
setTimeout(() => {
|
||||
// 현재 혜택 정보로 모달 데이터 구성
|
||||
const bnfInnb = $('#bnfInnb').val() || $('input[name="bnfInnb"]').val();
|
||||
|
||||
if (!bnfInnb) {
|
||||
console.error('bnfInnb 값을 찾을 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 서버에서 최신 완료 정보 가져와서 모달 표시
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/capsuleGameInfo',
|
||||
type: 'POST',
|
||||
data: {
|
||||
bnfInnb: bnfInnb
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response && response.resBody) {
|
||||
try {
|
||||
const result = JSON.parse(response.resBody.result);
|
||||
|
||||
// 완료 상태 모달 데이터 구성
|
||||
const modalData = {
|
||||
progressInfo: {
|
||||
progrsStep: 5, // 완료 단계
|
||||
totUseLeafCnt: result.totalUsedLeaf || result.totUseLeafCnt || 0,
|
||||
requiredLeaf: result.requiredLeaf || 100,
|
||||
stepProgressPercentage: 100 // 완료 상태
|
||||
},
|
||||
productName: result.rewardProductName,
|
||||
productDesc: result.rewardProductDesc || result.productDescription || '',
|
||||
thumbnailPath: result.productImagePath,
|
||||
cdnurl: $('#cdnurl').val() || '',
|
||||
sopmalCd: $('#sopmalCd').val(),
|
||||
importantNotice: {
|
||||
notice1: result.notice1 || $('#importantNotice').val() || '',
|
||||
notice2: result.notice2 || '',
|
||||
notice3: result.notice3 || ''
|
||||
}
|
||||
};
|
||||
|
||||
// 완료 모달 자동 표시
|
||||
MicroModal.show('modal-1');
|
||||
|
||||
// 모달 내용 업데이트
|
||||
setTimeout(function() {
|
||||
if (typeof updateModalContent === 'function') {
|
||||
updateModalContent(modalData);
|
||||
} else {
|
||||
console.error('updateModalContent 함수를 찾을 수 없습니다.');
|
||||
}
|
||||
}, 100);
|
||||
} catch (e) {
|
||||
console.error('JSON 파싱 오류:', e);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('완료 정보 로드 실패:', error);
|
||||
}
|
||||
});
|
||||
}, 1000); // 1초 후 자동 표시
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 아이템(잎/도토리)을 사용하여 게임을 진행하는 함수
|
||||
* 서버에 사용 요청을 보내고 결과에 따라 UI 업데이트
|
||||
*/
|
||||
function useLeaf() {
|
||||
// 현재 보유한 잎 개수 확인 - 텍스트에서 숫자만 추출
|
||||
const currentLeafCount = parseInt($('#ContingItem').text().replace(/[^0-9]/g, ''));
|
||||
|
||||
// 잎이 없는 경우 토스트 메시지 표시 후 함수 종료
|
||||
if (currentLeafCount <= 0) {
|
||||
// 수정: CSS .show 클래스를 이용한 토스트 표시
|
||||
$('#acorn_toast').addClass('show');
|
||||
setTimeout(() => {
|
||||
$('#acorn_toast').removeClass('show');
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// 사용할 잎 개수 계산 (10개 이상이면 10개, 미만이면 1개)
|
||||
// 한 번에 최대 10개까지 사용 가능
|
||||
const useLeafCnt = currentLeafCount >= 10 ? 10 : 1;
|
||||
|
||||
// 서버 요청 데이터 구성
|
||||
const requestData = {
|
||||
bnfInnb: $('#bnfInnb').val(), // 혜택 고유번호
|
||||
bnfSttusInnb: $('#bnfSttusInnb').val(), // 혜택 상태 고유번호
|
||||
useLeafCnt: useLeafCnt // 사용할 아이템 개수
|
||||
};
|
||||
|
||||
// 서버에 아이템 사용 요청
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/useLeaf',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(requestData),
|
||||
success: function(response) {
|
||||
// 서버 응답 데이터 파싱
|
||||
const resultData = JSON.parse(response.resBody.result);
|
||||
|
||||
// 초록잎 부족 오류 처리
|
||||
if (resultData.leafUsageSuccess === false) {
|
||||
if (resultData.errorCode === 'INSUFFICIENT_LEAF') {
|
||||
// 초록잎 부족 시 보유량 업데이트 및 알림 표시
|
||||
const ownedLeafCount = resultData.ownedLeafCount || 0;
|
||||
|
||||
// UI에 정확한 보유량 반영
|
||||
const itemIcon = $('#ContingItem i').attr('class') || 'ico_leaf';
|
||||
$('#ContingItem').html(`<i class="${itemIcon}"></i>${ownedLeafCount}개`);
|
||||
|
||||
// 수정: CSS .show 클래스를 이용한 토스트 표시
|
||||
$('#acorn_toast').addClass('show');
|
||||
setTimeout(() => {
|
||||
$('#acorn_toast').removeClass('show');
|
||||
}, 2000);
|
||||
|
||||
} else {
|
||||
// 기타 오류 처리
|
||||
const errorMessage = resultData.errorMessage || '잎 사용에 실패했습니다.';
|
||||
showCustomAlert(errorMessage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 수정: 성공 조건을 gameCompleted 또는 currentStep으로 변경
|
||||
if (resultData.gameCompleted || resultData.currentStep) {
|
||||
// 현재 단계 데이터 구성
|
||||
let currentStepData = {
|
||||
...resultData,
|
||||
currentStep: resultData.currentStep,
|
||||
ownedLeafCount: resultData.ownedLeafCount // 서버에서 받은 최신 보유 개수 사용
|
||||
};
|
||||
|
||||
// 수정: 게임 완료 조건 체크
|
||||
if (resultData.gameCompleted || (resultData.currentStep === 5 && resultData.stepProgressPercentage >= 100)) {
|
||||
// 게임 완료 시 데이터 업데이트
|
||||
currentStepData.isStepCompleted = true;
|
||||
currentStepData.stepProgressPercentage = 100;
|
||||
currentStepData.currentStep = 5; // 최종 단계로 설정
|
||||
|
||||
// UI 업데이트
|
||||
updateGameUI(currentStepData);
|
||||
|
||||
// 수정: 완료 모달 표시 로직 개선
|
||||
setTimeout(() => {
|
||||
const bnfInnb = $('#bnfInnb').val() || $('input[name="bnfInnb"]').val();
|
||||
|
||||
if (!bnfInnb) {
|
||||
console.error('bnfInnb 값을 찾을 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 완료 상태 모달 데이터 구성 (서버에서 받은 데이터 사용)
|
||||
const modalData = {
|
||||
progressInfo: {
|
||||
progrsStep: 5, // 완료 단계
|
||||
totUseLeafCnt: resultData.totalLeafUsed || 0,
|
||||
requiredLeaf: resultData.requiredLeaf || resultData.totalLeafNeeded || 1000,
|
||||
stepProgressPercentage: 100 // 완료 상태
|
||||
},
|
||||
productName: resultData.productName,
|
||||
productDesc: resultData.productDesc,
|
||||
thumbnailPath: resultData.imageUrl,
|
||||
cdnurl: $('#cdnurl').val() || '',
|
||||
sopmalCd: $('#sopmalCd').val(),
|
||||
importantNotice: {
|
||||
notice1: resultData.notice || $('#importantNotice').val() || '',
|
||||
notice2: '',
|
||||
notice3: ''
|
||||
}
|
||||
};
|
||||
|
||||
// 완료 모달 자동 표시
|
||||
MicroModal.show('modal-1');
|
||||
|
||||
// 모달 내용 업데이트
|
||||
setTimeout(function() {
|
||||
if (typeof updateModalContent === 'function') {
|
||||
updateModalContent(modalData);
|
||||
} else {
|
||||
console.error('updateModalContent 함수를 찾을 수 없습니다.');
|
||||
}
|
||||
}, 100);
|
||||
}, 1000); // 1초 후 자동 표시
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 단계 완료 여부 확인 및 처리 (5단계 미만인 경우)
|
||||
if (resultData.stepProgressPercentage >= 100) {
|
||||
// 단계 완료 시 데이터 업데이트
|
||||
currentStepData.isStepCompleted = true;
|
||||
currentStepData.stepProgressPercentage = 100;
|
||||
|
||||
// UI 업데이트
|
||||
updateGameUI(currentStepData);
|
||||
|
||||
// 중간 단계 완료 모달 처리
|
||||
const stepNames = ['첫번째', '두번째', '세번째', '네번째', '다섯번째'];
|
||||
const currentStepName = stepNames[currentStepData.currentStep - 1];
|
||||
|
||||
// 완료 메시지 업데이트
|
||||
$('#completionStepText').text(`${currentStepName} 유리병이 가득 찼어요!`);
|
||||
$('#completionFlask')
|
||||
.removeClass()
|
||||
.addClass(`ico_flask flask0${currentStepData.currentStep}`)
|
||||
.text(`${currentStepName} 유리병`);
|
||||
|
||||
// 남은 단계 수 계산 및 표시
|
||||
const remainingFlasks = 5 - currentStepData.currentStep;
|
||||
$('#remainingFlasks').text(remainingFlasks);
|
||||
|
||||
// 모달 표시 전에 기존 이벤트 핸들러 제거 (중복 방지)
|
||||
$('#modal-2 .microModal__btn-primary').off('click');
|
||||
|
||||
// 단계 완료 모달 표시
|
||||
MicroModal.show('modal-2');
|
||||
|
||||
// 단계 완료 모달의 "계속하기" 버튼에만 다음 단계 진행 이벤트 바인딩
|
||||
$('#modal-2 .microModal__btn-primary').one('click', function() {
|
||||
// 모달 닫기
|
||||
MicroModal.close('modal-2');
|
||||
if (!resultData.gameCompleted) {
|
||||
// 모달이 완전히 닫힌 후에 다음 단계로 진행
|
||||
setTimeout(() => {
|
||||
// 다음 단계 데이터 구성
|
||||
const nextStepData = {
|
||||
...currentStepData,
|
||||
currentStep: currentStepData.currentStep + 1,
|
||||
stepProgressPercentage: 0,
|
||||
isStepCompleted: false
|
||||
};
|
||||
// UI 업데이트
|
||||
updateGameUI(nextStepData);
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
// "다시 시작" 버튼은 별도로 처리 (첫 번째 버튼)
|
||||
$('#modal-2 .microModal__btn:first-child').one('click', function() {
|
||||
// 다시 시작 확인 모달 표시
|
||||
showRestartConfirmModal();
|
||||
});
|
||||
} else {
|
||||
// 단계가 완료되지 않은 경우 일반 UI 업데이트
|
||||
updateGameUI(currentStepData);
|
||||
}
|
||||
} else {
|
||||
// 아이템 사용 실패 시 알림 표시
|
||||
const errorMessage = resultData.errorMessage || '잎 사용에 실패했습니다. 다시 시도해주세요.';
|
||||
showCustomAlert(errorMessage);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// 서버 통신 오류 시 알림 표시
|
||||
showCustomAlert('서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 아이템 수집 페이지로 이동하는 함수
|
||||
* 현재 게임 상태를 세션스토리지에 저장 후 이동
|
||||
*/
|
||||
function goToCollectPage() {
|
||||
// 현재 진행 상태 저장 - 수집 페이지에서 돌아올 때 복원하기 위함
|
||||
const currentGameStatus = sessionStorage.getItem('gameStatus');
|
||||
if (currentGameStatus) {
|
||||
sessionStorage.setItem('previousGameStatus', currentGameStatus);
|
||||
}
|
||||
|
||||
// 모으기 페이지로 이동
|
||||
window.location.href = '/sopmal/bnft/cpslmknCollectList';
|
||||
}
|
||||
|
||||
// 닫기 버튼 이벤트 리스너
|
||||
// 모달 닫기 버튼 클릭 시 해당 모달 닫기
|
||||
$('.microModal .microModal__close2').on('click', function () {
|
||||
var modalId = $(this).closest('.microModal').attr('id');
|
||||
closeModal(modalId);
|
||||
});
|
||||
|
||||
// 뒤로가기 처리
|
||||
// 브라우저 뒤로가기 버튼 클릭 시 모달 닫기 처리
|
||||
window.addEventListener('popstate', function (event) {
|
||||
var modalId = window.location.hash.replace('#', '');
|
||||
if (modalId) {
|
||||
closeModal(modalId);
|
||||
} else {
|
||||
// 해시가 없으면 모든 모달 닫기
|
||||
$('.microModal').each(function () {
|
||||
MicroModal.close($(this).attr('id'));
|
||||
});
|
||||
}
|
||||
// 본문 스크롤 복원
|
||||
document.body.style.overflow = '';
|
||||
});
|
||||
|
||||
/**
|
||||
* 헤더 버튼을 초기화하는 함수
|
||||
* 기존 헤더의 검색/장바구니 버튼을 제거하고 게임용 버튼(진행상태/설정)으로 대체
|
||||
*/
|
||||
function initializeHeaderButtons() {
|
||||
// 기존 헤더 버튼 중 검색 및 장바구니 아이콘 삭제
|
||||
$('.header__btn').empty();
|
||||
// 새로운 버튼 추가
|
||||
const $headerBtns = $('.header__btn').parent();
|
||||
const newButtons = `
|
||||
<button class="header__btn" id="currentProgressButton">
|
||||
<i class="icon icon_nav-capsule"></i>
|
||||
<span class="visually-hidden">진행상태보기</span>
|
||||
</button>
|
||||
<button class="header__btn" id="CapsuleettingsButton">
|
||||
<i class="icon icon_nav-set"></i>
|
||||
<span class="visually-hidden">설정</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
$headerBtns.append(newButtons);
|
||||
}
|
||||
|
||||
/**
|
||||
* 설정 페이지로 이동하는 함수
|
||||
* 현재 혜택 내부번호를 유지하며 설정 페이지로 이동
|
||||
*/
|
||||
function goToSettingsPage() {
|
||||
const bnfInnb = $('#bnfInnb').val(); // 혜택 내부번호 가져오기
|
||||
const sopmalCd = $('#sopmalCd').val(); // 쇼핑몰 코드 가져오기
|
||||
window.location.href = `/sopmal/bnft/settings?bnfInnb=${bnfInnb}`; // 설정 페이지로 이동
|
||||
}
|
||||
|
||||
/**
|
||||
* 진행 상태 바 생성 함수
|
||||
* 게임 진행 단계와 진행률을 시각적으로 표시하는 상태 바 생성
|
||||
* @param {number} progressStep - 현재 진행 단계 (1~5)
|
||||
* @param {number} progressPercent - 현재 진행률 (0~100)
|
||||
* @returns {jQuery} 생성된 상태 바 jQuery 객체
|
||||
*/
|
||||
function createStatusBar(progressStep, progressPercent) {
|
||||
// 진행률을 정수로 변환하고 소수점 제거
|
||||
const displayPercent = Math.floor(progressPercent);
|
||||
|
||||
// 상태 바 DOM 구조 생성 및 반환
|
||||
return $('<div>').addClass('gameStatusBarWrap')
|
||||
.append($('<div>').addClass('gameStatusBar')
|
||||
.append($('<div>').addClass('per').css('width', `${displayPercent}%`)) // 진행률 표시 바
|
||||
.append($('<span>').addClass('first').text('시작')) // 시작 지점
|
||||
.append(createStepSpans(progressStep)) // 중간 단계 표시
|
||||
.append($('<span>').addClass(`last ${progressStep >= 5 ? 'on' : ''}`).text('5단계'))); // 마지막 단계
|
||||
}
|
||||
|
||||
/**
|
||||
* 단계 표시 span 생성 함수
|
||||
* 진행 상태 바에 표시할 각 단계별 표시 요소 생성
|
||||
* @param {number} currentStep - 현재 진행 단계 (1~5)
|
||||
* @returns {Array} 생성된 단계 표시 span jQuery 객체 배열
|
||||
*/
|
||||
function createStepSpans(currentStep) {
|
||||
const spans = [];
|
||||
// 1~4단계 표시 생성 (5단계는 별도로 'last' 클래스로 처리)
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
spans.push(
|
||||
$('<span>')
|
||||
.addClass(`step0${i} ${currentStep >= i ? 'on' : ''}`) // 현재 단계 이하는 'on' 클래스 추가
|
||||
.text(`${i}단계`)
|
||||
);
|
||||
}
|
||||
return spans;
|
||||
}
|
||||
|
||||
/**
|
||||
* 다른선물로 다시 시작 확인 모달을 표시하는 함수
|
||||
* 모달을 표시하고 버튼에 적절한 이벤트 리스너 연결
|
||||
*/
|
||||
function showRestartConfirmModal() {
|
||||
// 기존 이벤트 리스너 제거 (중복 방지)
|
||||
$('.microModal__btn').off('click');
|
||||
|
||||
// 모달 표시
|
||||
MicroModal.show('modal-2', {
|
||||
onShow: function() {
|
||||
// URL 해시 추가 (브라우저 뒤로가기 처리용)
|
||||
if (window.location.hash !== '#modal-2') {
|
||||
window.history.pushState({ modal: 'open' }, '', '#modal-2');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// "처음부터 다시시작" 버튼에 이벤트 리스너 연결
|
||||
$('#modal-2 .microModal__btn').first().on('click', function() {
|
||||
console.log("처음부터 다시시작 버튼 클릭됨");
|
||||
MicroModal.close('modal-2');
|
||||
|
||||
// 사용자 선택 확정을 위한 두 번째 확인 모달 표시
|
||||
showFinalConfirmModal();
|
||||
});
|
||||
|
||||
// "그대로 계속할게요" 버튼에 이벤트 리스너 연결
|
||||
$('#modal-2 .microModal__btn-primary').on('click', function() {
|
||||
console.log("그대로 계속하기 버튼 클릭됨");
|
||||
MicroModal.close('modal-2');
|
||||
// URL 해시 제거
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 최종 확인 모달을 표시하는 함수
|
||||
* 정말로 다시 시작할지 확인하는 두 번째 모달 표시
|
||||
*/
|
||||
function showFinalConfirmModal() {
|
||||
// 최종 확인 모달이 없으면 동적으로 생성
|
||||
if ($('#modal-3').length === 0) {
|
||||
$('body').append(`
|
||||
<div class="microModal microAlert" id="modal-3" aria-hidden="true">
|
||||
<div class="microModal__overlay" tabindex="-1">
|
||||
<div class="microModal__container" role="dialog" aria-modal="true">
|
||||
<div class="microModal__body">
|
||||
<div class="microModal__top">
|
||||
<h2 class="microModal__title">
|
||||
정말 처음부터 다시 시작하시겠습니까?
|
||||
</h2>
|
||||
</div>
|
||||
<div class="microModal__content">
|
||||
<p>
|
||||
지금까지 진행한 내용이 모두 삭제됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="microModal__btnContainer">
|
||||
<button class="microModal__btn" data-micromodal-close aria-label="창 닫기">취소</button>
|
||||
<button class="microModal__btn microModal__btn-primary" id="restartFromBeginning">다시 시작하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
// 모달 표시
|
||||
MicroModal.show('modal-3', {
|
||||
onShow: function() {
|
||||
// URL 해시 추가
|
||||
window.history.pushState({ modal: 'open' }, '', '#modal-3');
|
||||
}
|
||||
});
|
||||
|
||||
// 이벤트 리스너가 중복되지 않도록 먼저 제거
|
||||
$('#restartFromBeginning').off('click');
|
||||
|
||||
// "다시 시작하기" 버튼에 이벤트 리스너 연결
|
||||
$('#restartFromBeginning').on('click', function() {
|
||||
console.log("최종 다시 시작하기 버튼 클릭됨");
|
||||
MicroModal.close('modal-3');
|
||||
|
||||
// 캡슐 선택 페이지로 이동
|
||||
restartCapsule();
|
||||
});
|
||||
|
||||
// "취소" 버튼에 이벤트 리스너 연결
|
||||
$('#modal-3 .microModal__btn').first().on('click', function() {
|
||||
MicroModal.close('modal-3');
|
||||
// URL 해시 제거
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 캡슐 제작을 처음부터 다시 시작하는 함수
|
||||
* 서버에 재시작 요청을 보내고 캡슐 선택 페이지로 이동
|
||||
*/
|
||||
function restartCapsule() {
|
||||
// 현재 혜택 고유번호 가져오기
|
||||
const bnfInnb = $('#bnfInnb').val();
|
||||
|
||||
// 서버에 재시작 요청 보내기
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/resetCapsule',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({ bnfInnb: bnfInnb }),
|
||||
success: function(response) {
|
||||
// 성공 시 캡슐 선택 페이지로 이동
|
||||
window.location.href = '/sopmal/bnft/selectCapsule';
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
// 오류 발생 시 토스트 메시지 표시
|
||||
console.error('재시작 요청 실패:', error);
|
||||
alert('재시작 요청 중 오류가 발생했습니다. 다시 시도해주세요.');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
// 쿠폰함 페이지로 이동하는 함수
|
||||
function goToCouponPage() {
|
||||
// 쿠폰함 페이지 URL로 이동
|
||||
window.location.href = '/sopmal/coupon/list';
|
||||
}
|
||||
|
||||
// 새로운 캡슐 만들기 시작하는 함수
|
||||
function startNewCapsule() {
|
||||
// 제품 선택 페이지로 이동
|
||||
window.location.href = '/sopmal/bnft/selectCapsule';
|
||||
}
|
||||
|
||||
// 다른 선물로 다시 시작하는 함수 (리셋 확인 모달에서 호출)
|
||||
function resetCapsule() {
|
||||
// Ajax 요청으로 현재 진행 상태 초기화
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/resetCapsule',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
bnfInnb: $('#bnfInnb').val(),
|
||||
bnfSttusInnb: $('#bnfSttusInnb').val()
|
||||
}),
|
||||
success: function(response) {
|
||||
// 응답 구조 변경에 맞게 처리
|
||||
const result = JSON.parse(response.resBody.result);
|
||||
|
||||
if (result.success) {
|
||||
// 제품 선택 페이지로 이동
|
||||
window.location.href = '/sopmal/bnft/selectCapsule';
|
||||
} else {
|
||||
alert('초기화 중 오류가 발생했습니다. 다시 시도해주세요.');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('서버 오류가 발생했습니다. 다시 시도해주세요.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 모달 초기화 함수
|
||||
function initModals() {
|
||||
// modal-2(다시 시작 확인) 버튼 이벤트 등록
|
||||
$('#modal-2 .microModal__btn:first-child').on('click', function() {
|
||||
resetCapsule();
|
||||
});
|
||||
|
||||
// 애니메이션 효과 초기화
|
||||
// initCompletionAnimation();
|
||||
}
|
||||
|
||||
// 모달 데이터 로드 및 초기화
|
||||
$(document).ready(function() {
|
||||
// 모달 초기화: onShow 콜백과 다른 옵션들을 함께 설정
|
||||
MicroModal.init({
|
||||
disableScroll: true,
|
||||
awaitCloseAnimation: true,
|
||||
onShow: function(modal) {
|
||||
console.log(`Modal shown: ${modal.id}`);
|
||||
if (modal.id === 'modal-1') {
|
||||
console.log('Loading data for modal-1...');
|
||||
loadModalData();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 🔥 문제 부분: 이 코드를 제거하거나 수정해야 함
|
||||
// 모달2(다시 시작 확인) 버튼 이벤트 등록을 제거
|
||||
// $(document).off('click', '#modal-2 .microModal__btn:first-child').on('click', '#modal-2 .microModal__btn:first-child', function() {
|
||||
// resetCapsule();
|
||||
// });
|
||||
|
||||
// 대신 "다른선물로 다시 시작" 버튼에만 resetCapsule을 바인딩
|
||||
$(document).off('click', '#restartGameButton').on('click', '#restartGameButton', function(e) {
|
||||
e.preventDefault();
|
||||
showRestartConfirmModal();
|
||||
});
|
||||
});
|
||||
|
||||
// 모달 데이터 로드 함수
|
||||
function loadModalData() {
|
||||
const bnfInnb = $('#bnfInnb').val() || $('input[name="bnfInnb"]').val();
|
||||
|
||||
if (!bnfInnb) {
|
||||
console.error('bnfInnb 값을 찾을 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// AJAX로 최신 진행 상태 데이터 요청
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/getProgressStatus',
|
||||
type: 'GET',
|
||||
data: {
|
||||
bnfInnb: bnfInnb
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.resultCode === '0000') {
|
||||
// 응답 데이터로 모달 내용 업데이트
|
||||
updateModalContent(response.content);
|
||||
} else {
|
||||
console.error('데이터 로드 실패:', response.message);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX 요청 실패:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 모달 내용 업데이트 함수
|
||||
function updateModalContent(data) {
|
||||
console.log("updateModalContent 호출됨:", data); // 디버깅용
|
||||
|
||||
const progressInfo = data.progressInfo;
|
||||
|
||||
if (!progressInfo) {
|
||||
console.error("progressInfo가 없습니다:", data);
|
||||
return;
|
||||
}
|
||||
|
||||
// progressStep이 유효한지 확인
|
||||
if (!progressInfo.progrsStep || isNaN(parseInt(progressInfo.progrsStep))) {
|
||||
console.error("유효하지 않은 progrsStep:", progressInfo.progrsStep);
|
||||
progressInfo.progrsStep = 1; // 기본값 설정
|
||||
} else {
|
||||
// 숫자로 변환
|
||||
progressInfo.progrsStep = parseInt(progressInfo.progrsStep);
|
||||
}
|
||||
|
||||
console.log("현재 단계:", progressInfo.progrsStep);
|
||||
|
||||
// 쇼핑몰 코드에 따른 텍스트 설정
|
||||
const brandName = (data.sopmalCd === 'NC') ? '뉴트리' : '닥터린';
|
||||
|
||||
// 모달 컨테이너 참조
|
||||
const $modalContent = $('#modal-1 .microModal__content');
|
||||
if ($modalContent.length === 0) {
|
||||
console.error("모달 컨테이너를 찾을 수 없습니다");
|
||||
return;
|
||||
}
|
||||
|
||||
// 모달 컨텐츠 초기화
|
||||
$modalContent.empty();
|
||||
|
||||
// 게임 상태 요소 생성
|
||||
const $gameStatus = $('<div class="gameStatus"></div>');
|
||||
$modalContent.append($gameStatus);
|
||||
|
||||
// 진행 상태에 따라 다른 UI 표시
|
||||
// 수정: 5단계 완성 여부를 stepProgressPercentage로 판단
|
||||
const isGameCompleted = progressInfo.progrsStep === 5 && progressInfo.stepProgressPercentage >= 100;
|
||||
|
||||
if (!isGameCompleted) {
|
||||
// 진행 중 UI (5단계 진행 중 포함)
|
||||
$gameStatus.append(`<p class="tit">${brandName} 캡슐 만들기<br><b>${progressInfo.progrsStep}단계</b> 진행중!!</p>`);
|
||||
|
||||
// 진행 바 추가 - 실제 진행률 전달
|
||||
$gameStatus.append(createStatusBar(progressInfo.progrsStep, progressInfo.stepProgressPercentage || 0));
|
||||
|
||||
// 선물 정보 추가
|
||||
const $gameGift = $('<div class="gameGift"></div>');
|
||||
$gameGift.append(`<p class="tit">${brandName} 캡슐을 완성하면 받는 선물이예요.</p>`);
|
||||
|
||||
const $gameGiftInbox = $('<div class="gameGiftInbox"></div>');
|
||||
$gameGiftInbox.append(`<p class="img"><img src="${data.cdnurl}${data.thumbnailPath}" alt="상품 이미지"></p>`);
|
||||
|
||||
const $txtDiv = $('<div class="txt"></div>');
|
||||
$txtDiv.append(`<p class="txt1">${data.productDesc || ''}</p>`);
|
||||
$txtDiv.append(`<p class="txt2">${data.productName || ''}</p>`);
|
||||
|
||||
$gameGiftInbox.append($txtDiv);
|
||||
$gameGift.append($gameGiftInbox);
|
||||
|
||||
// 다른 선물로 다시 시작 버튼 추가
|
||||
$gameGift.append(`<div class="alignR">
|
||||
<a href="#" class="btn giftReset" id="restartGameButton">
|
||||
<i class="ico_giftReset"></i>다른선물로 다시 시작
|
||||
</a>
|
||||
</div>`);
|
||||
|
||||
$gameStatus.append($gameGift);
|
||||
} else {
|
||||
// 완료 상태 UI 구성 (5단계 100% 완성 시에만)
|
||||
$gameStatus.append(`
|
||||
<p class="tit">${brandName} 캡슐 만들기 최종 완료!</p>
|
||||
<p class="tit2">진짜 영양제 선물받고<br>새로운 캡슐 만들기 시작하세요.</p>
|
||||
`);
|
||||
|
||||
// 진행 바 추가 - 5단계 완료 (100% 전달)
|
||||
$gameStatus.append(createStatusBar(5, 100));
|
||||
|
||||
// 쿠폰 영역 추가
|
||||
const $gameCoupon = $('<div class="gameCoupon"></div>');
|
||||
$gameCoupon.append(`
|
||||
<p class="tit">
|
||||
<a href="javascript:goToCouponPage()"><i class="ico_present"></i>쿠폰함에 교환권이 전달되었습니다.</a>
|
||||
</p>
|
||||
<div class="gameCouponInbox">
|
||||
<div class="barcode">
|
||||
<img src="/mobile/images/bnft/ico_barcode.svg" width="80" height="80" alt="바코드">
|
||||
</div>
|
||||
<p class="img"><img src="${data.cdnurl}${data.thumbnailPath}" alt="상품 이미지"></p>
|
||||
<div class="txt">
|
||||
<p class="txt1">${data.productName || ''}</p>
|
||||
<p class="txt2">${data.productDesc || ''}</p>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
$gameStatus.append($gameCoupon);
|
||||
|
||||
// 새로운 캡슐 만들기 시작하기 버튼 추가
|
||||
$gameStatus.append(`
|
||||
<div class="btnBox alignC" style="margin-top: 20px;">
|
||||
<button type="button" class="btn lg theme newStartButton" onclick="startNewCapsule()">
|
||||
새로운 캡슐 만들기 시작하기
|
||||
</button>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
// 알림 박스 추가 - notice2 사용 (디버깅 로그 추가)
|
||||
console.log("importantNotice 데이터:", data.importantNotice);
|
||||
console.log("notice2 내용:", data.importantNotice?.notice2);
|
||||
|
||||
const $commentBox = $('<div class="comment-box"></div>');
|
||||
const notice2Content = data.importantNotice?.notice2?.replace(/<[^>]*>/g, '') || '';
|
||||
|
||||
$commentBox.html(`
|
||||
<p style="font-size:14px; font-weight:bold; margin-bottom:5px;">참여방법안내</p>
|
||||
<p style="color:rgba(0,0,0,0.6); font-size:14px;">${notice2Content}</p>
|
||||
`);
|
||||
|
||||
// CSS 문제 해결을 위한 강제 스타일 적용
|
||||
$commentBox.css({
|
||||
'background': '#fff',
|
||||
'padding': '16px',
|
||||
'padding-bottom': '16px', // 정상적인 패딩으로 수정
|
||||
'margin-bottom': '0', // 음수 마진 제거
|
||||
'min-height': 'auto',
|
||||
'display': 'block'
|
||||
});
|
||||
|
||||
$modalContent.append($commentBox);
|
||||
|
||||
// 렌더링 후 확인용 로그
|
||||
console.log("comment-box 렌더링 완료:", $commentBox);
|
||||
}
|
||||
|
||||
// 진행 상태 바 생성 함수 - 단계별로 색상 변경
|
||||
function createStatusBar(progressStep, progressPercent) {
|
||||
const $statusBarWrap = $('<div class="gameStatusBarWrap"></div>');
|
||||
const $statusBar = $('<div class="gameStatusBar"></div>');
|
||||
|
||||
// 디버깅 로그 추가
|
||||
console.log(`createStatusBar 호출: progressStep=${progressStep}, progressPercent=${progressPercent}`);
|
||||
|
||||
// 수정: 게이지 바 너비 계산 (6개 구간으로 나누기)
|
||||
// 시작(0%) → 1단계(16.67%) → 2단계(33.33%) → 3단계(50%) → 4단계(66.67%) → 5단계(83.33%) → 완료(100%)
|
||||
let widthPercent;
|
||||
|
||||
if (progressStep < 5) {
|
||||
// 1~4단계: 각 단계당 16.67%씩 할당
|
||||
widthPercent = progressStep * 16.67;
|
||||
} else if (progressStep === 5) {
|
||||
// 5단계: stepProgressPercentage에 따라 83.33%~100% 사이
|
||||
const basePercent = 83.33; // 5단계 시작점
|
||||
const additionalPercent = (progressPercent || 0) * 0.1667; // 5단계 내 진행률 (최대 16.67%)
|
||||
widthPercent = basePercent + additionalPercent;
|
||||
} else {
|
||||
// 완료 상태
|
||||
widthPercent = 100;
|
||||
}
|
||||
|
||||
console.log(`게이지 바 너비: ${widthPercent}%`);
|
||||
|
||||
// 진행률 표시 바 - 단계에 맞게 너비 조정
|
||||
$statusBar.append(`<div class="per" style="width:${widthPercent}%;"></div>`);
|
||||
|
||||
// 시작 지점
|
||||
$statusBar.append('<span class="first">시작</span>');
|
||||
|
||||
// 각 단계 표시
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
// 현재 단계 이상인 경우 'on' 클래스 추가
|
||||
const isActive = progressStep >= i;
|
||||
console.log(`단계 ${i}: ${isActive ? '활성화' : '비활성화'}`);
|
||||
|
||||
$statusBar.append(`<span class="step0${i} ${isActive ? 'on' : ''}">${i}단계</span>`);
|
||||
}
|
||||
|
||||
// 마지막 단계 - 5단계 완료(100%)일 때만 활성화
|
||||
const isLastActive = progressStep === 5 && (progressPercent >= 100);
|
||||
console.log(`최종 단계: ${isLastActive ? '활성화' : '비활성화'}`);
|
||||
$statusBar.append(`<span class="last ${isLastActive ? 'on' : ''}">5단계</span>`);
|
||||
|
||||
$statusBarWrap.append($statusBar);
|
||||
return $statusBarWrap;
|
||||
}
|
||||
|
||||
// 다른선물로 다시 시작 확인 모달을 표시하는 함수
|
||||
function showRestartConfirmModal() {
|
||||
console.log("다시 시작 확인 모달 표시 시도");
|
||||
|
||||
// 모달 HTML 정의
|
||||
const restartModalHtml = `
|
||||
<div class="microModal microAlert" id="restart-modal" aria-hidden="true">
|
||||
<div class="microModal__overlay" tabindex="-1">
|
||||
<div class="microModal__container" role="dialog" aria-modal="true">
|
||||
<div class="microModal__body">
|
||||
<div class="microModal__top">
|
||||
<h2 class="microModal__title">
|
||||
새로운 선물로 변경하여<br>만들기를 처음부터 다시 시작할까요?
|
||||
</h2>
|
||||
</div>
|
||||
<div class="microModal__content">
|
||||
<p>
|
||||
현재 보유중인 초록잎은 유지되지만<br>
|
||||
진행한 내용은 모두 삭제되며<br>
|
||||
다시 복구할 수 없습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="microModal__btnContainer">
|
||||
<button class="microModal__btn" id="restartConfirmBtn">처음부터 다시시작</button>
|
||||
<button class="microModal__btn microModal__btn-primary" id="restartCancelBtn">그대로 계속할게요</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 이미 존재하는 restart-modal 제거 (중복 방지)
|
||||
$('#restart-modal').remove();
|
||||
|
||||
// 모달 HTML을 body에 추가
|
||||
$('body').append(restartModalHtml);
|
||||
|
||||
// 모달 표시
|
||||
MicroModal.show('restart-modal', {
|
||||
onShow: function() {
|
||||
console.log("다시 시작 확인 모달이 표시됨");
|
||||
}
|
||||
});
|
||||
|
||||
// 버튼 이벤트 핸들러 연결
|
||||
$('#restartConfirmBtn').off('click').on('click', function() {
|
||||
console.log("처음부터 다시시작 버튼 클릭됨");
|
||||
MicroModal.close('restart-modal');
|
||||
|
||||
// 사용자 선택 확정을 위한 두 번째 확인 모달 표시
|
||||
showFinalConfirmModal();
|
||||
});
|
||||
|
||||
$('#restartCancelBtn').off('click').on('click', function() {
|
||||
console.log("그대로 계속하기 버튼 클릭됨");
|
||||
MicroModal.close('restart-modal');
|
||||
});
|
||||
}
|
||||
|
||||
// 최종 확인 모달을 표시하는 함수
|
||||
function showFinalConfirmModal() {
|
||||
// 모달 HTML 정의
|
||||
const finalConfirmModalHtml = `
|
||||
<div class="microModal microAlert" id="final-confirm-modal" aria-hidden="true">
|
||||
<div class="microModal__overlay" tabindex="-1">
|
||||
<div class="microModal__container" role="dialog" aria-modal="true">
|
||||
<div class="microModal__body">
|
||||
<div class="microModal__top">
|
||||
<h2 class="microModal__title">
|
||||
정말 처음부터 다시 시작하시겠습니까?
|
||||
</h2>
|
||||
</div>
|
||||
<div class="microModal__content">
|
||||
<p>
|
||||
지금까지 진행한 내용이 모두 삭제됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="microModal__btnContainer">
|
||||
<button class="microModal__btn" id="finalCancelBtn">취소</button>
|
||||
<button class="microModal__btn microModal__btn-primary" id="finalConfirmBtn">다시 시작하기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 이미 존재하는 final-confirm-modal 제거 (중복 방지)
|
||||
$('#final-confirm-modal').remove();
|
||||
|
||||
// 모달 HTML을 body에 추가
|
||||
$('body').append(finalConfirmModalHtml);
|
||||
|
||||
// 모달 표시
|
||||
MicroModal.show('final-confirm-modal');
|
||||
|
||||
// 버튼 이벤트 핸들러 연결
|
||||
$('#finalConfirmBtn').off('click').on('click', function() {
|
||||
console.log("최종 다시 시작하기 버튼 클릭됨");
|
||||
MicroModal.close('final-confirm-modal');
|
||||
|
||||
// 캡슐 선택 페이지로 이동
|
||||
resetCapsule();
|
||||
});
|
||||
|
||||
$('#finalCancelBtn').off('click').on('click', function() {
|
||||
console.log("최종 취소 버튼 클릭됨");
|
||||
MicroModal.close('final-confirm-modal');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
$(document).ready(function() {
|
||||
let page = 1;
|
||||
let isLoading = false;
|
||||
let hasMoreData = true;
|
||||
const itemsPerPage = 20;
|
||||
|
||||
// 디버깅: 초기 상태 확인
|
||||
console.log('페이지 로드 완료. 초기 page:', page);
|
||||
console.log('초기 데이터 개수:', $('#leafHistoryList li').length);
|
||||
console.log('초기 hasMoreData:', hasMoreData);
|
||||
|
||||
// 초기 데이터 개수 확인하여 hasMoreData 설정
|
||||
const initialItemCount = $('#leafHistoryList li').length;
|
||||
if (initialItemCount < itemsPerPage) {
|
||||
console.log('초기 데이터가 ' + itemsPerPage + '개 미만입니다. hasMoreData를 false로 설정합니다.');
|
||||
hasMoreData = false;
|
||||
}
|
||||
|
||||
// 하지만 초기 데이터가 정확히 20개라면 더 있을 가능성이 있으므로 true 유지
|
||||
if (initialItemCount === itemsPerPage) {
|
||||
console.log('초기 데이터가 정확히 ' + itemsPerPage + '개입니다. 더 많은 데이터가 있을 수 있습니다.');
|
||||
hasMoreData = true;
|
||||
}
|
||||
|
||||
// 초기 데이터가 0개인 경우도 체크
|
||||
if (initialItemCount === 0) {
|
||||
console.log('초기 데이터가 없습니다.');
|
||||
hasMoreData = false;
|
||||
}
|
||||
|
||||
console.log('최종 hasMoreData:', hasMoreData);
|
||||
|
||||
// 무한 스크롤 이벤트 처리
|
||||
$(window).scroll(function() {
|
||||
console.log('스크롤 이벤트 발생');
|
||||
console.log('isLoading:', isLoading, 'hasMoreData:', hasMoreData);
|
||||
|
||||
if (isLoading || !hasMoreData) {
|
||||
console.log('스크롤 처리 중단됨');
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollHeight = $(document).height();
|
||||
const scrollTop = $(window).scrollTop();
|
||||
const windowHeight = $(window).height();
|
||||
|
||||
console.log('스크롤 정보:', {
|
||||
scrollHeight: scrollHeight,
|
||||
scrollTop: scrollTop,
|
||||
windowHeight: windowHeight,
|
||||
trigger: scrollTop + windowHeight,
|
||||
threshold: scrollHeight - 200
|
||||
});
|
||||
|
||||
// 스크롤이 페이지 하단에 도달했는지 확인
|
||||
if (scrollTop + windowHeight >= scrollHeight - 200) {
|
||||
console.log('스크롤 임계점 도달, loadMoreItems 호출');
|
||||
loadMoreItems();
|
||||
}
|
||||
});
|
||||
|
||||
function loadMoreItems() {
|
||||
console.log('loadMoreItems 시작, 요청할 페이지:', page + 1);
|
||||
isLoading = true;
|
||||
$('#loading').show();
|
||||
|
||||
// 다음 페이지 데이터 요청
|
||||
$.ajax({
|
||||
url: `/sopmal/bnft/leafHistory/more?page=${page + 1}&size=${itemsPerPage}`,
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
beforeSend: function() {
|
||||
console.log('AJAX 요청 시작:', `/sopmal/bnft/leafHistory/more?page=${page + 1}&size=${itemsPerPage}`);
|
||||
},
|
||||
success: function(response) {
|
||||
console.log('AJAX 응답 받음:', response);
|
||||
|
||||
try {
|
||||
let data = null;
|
||||
|
||||
// 응답 구조에 맞게 데이터 추출
|
||||
if (response && response.resBody && response.resBody.result) {
|
||||
// result가 문자열인 경우 JSON 파싱
|
||||
if (typeof response.resBody.result === 'string') {
|
||||
data = JSON.parse(response.resBody.result);
|
||||
console.log('JSON 파싱 성공:', data);
|
||||
} else {
|
||||
data = response.resBody.result;
|
||||
}
|
||||
} else if (response && response.data) {
|
||||
data = response.data;
|
||||
} else {
|
||||
console.log('예상치 못한 응답 구조:', response);
|
||||
}
|
||||
|
||||
if (data && data.history && data.history.length > 0) {
|
||||
console.log('응답 데이터 개수:', data.history.length);
|
||||
appendItems(data.history);
|
||||
page++;
|
||||
console.log('페이지 증가됨. 현재 page:', page);
|
||||
|
||||
// 마지막 페이지인지 확인
|
||||
if (data.history.length < itemsPerPage) {
|
||||
hasMoreData = false;
|
||||
console.log('마지막 페이지 도달');
|
||||
}
|
||||
} else {
|
||||
hasMoreData = false;
|
||||
console.log('데이터 없음. 더 이상 로드 안함');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('JSON 파싱 오류:', e);
|
||||
hasMoreData = false;
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX 요청 실패:', {
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText,
|
||||
error: error,
|
||||
responseText: xhr.responseText
|
||||
});
|
||||
hasMoreData = false;
|
||||
},
|
||||
complete: function() {
|
||||
console.log('AJAX 요청 완료');
|
||||
isLoading = false;
|
||||
$('#loading').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function appendItems(items) {
|
||||
console.log('appendItems 호출, 추가할 아이템 수:', items.length);
|
||||
const $historyList = $('#leafHistoryList');
|
||||
|
||||
$.each(items, function(index, item) {
|
||||
const $li = $('<li>');
|
||||
|
||||
// 타이틀 부분 생성
|
||||
const $titleSpan = $('<span>', {
|
||||
class: 'tit',
|
||||
text: item.eventName
|
||||
});
|
||||
|
||||
// USE 타입이면 아이콘 추가
|
||||
if (item.actionType === 'USE') {
|
||||
const gameStage = item.gameStage || '1';
|
||||
$('<i>', {
|
||||
class: `ico_flask0${gameStage}`
|
||||
}).appendTo($titleSpan);
|
||||
}
|
||||
|
||||
// 날짜 부분 생성
|
||||
const dateTime = new Date(item.actionDatetime);
|
||||
const formattedDate = dateTime.getFullYear().toString().slice(2) + '-' +
|
||||
(dateTime.getMonth() + 1).toString().padStart(2, '0') + '-' +
|
||||
dateTime.getDate().toString().padStart(2, '0') + ' ' +
|
||||
dateTime.getHours().toString().padStart(2, '0') + ':' +
|
||||
dateTime.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
const $dateSpan = $('<span>', {
|
||||
class: 'dayTime',
|
||||
text: formattedDate
|
||||
});
|
||||
|
||||
// 개수 부분 생성
|
||||
const countValue = Math.abs(parseFloat(item.leafCount)).toString().replace('.0', '');
|
||||
const countPrefix = item.leafCount > 0 ? '+' : '-';
|
||||
|
||||
const $countSpan = $('<span>', {
|
||||
class: item.leafCount < 0 ? 'cnt minus' : 'cnt',
|
||||
text: countPrefix + countValue
|
||||
});
|
||||
|
||||
// 요소들 추가
|
||||
$li.append($titleSpan).append($dateSpan).append($countSpan);
|
||||
$historyList.append($li);
|
||||
});
|
||||
|
||||
console.log('아이템 추가 완료. 총 아이템 수:', $('#leafHistoryList li').length);
|
||||
}
|
||||
|
||||
// Top 버튼 이벤트
|
||||
$('.quick-menu__to-top button').click(function() {
|
||||
$('html, body').animate({
|
||||
scrollTop: 0
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// 페이지 로드 시 스크롤 가능 여부 확인
|
||||
setTimeout(function() {
|
||||
const isScrollable = $(document).height() > $(window).height();
|
||||
console.log('스크롤 가능 여부:', isScrollable);
|
||||
console.log('문서 높이:', $(document).height(), '윈도우 높이:', $(window).height());
|
||||
|
||||
if (!isScrollable) {
|
||||
console.log('스크롤바가 없어서 무한 스크롤이 동작하지 않습니다.');
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* CpslmknSettings.js
|
||||
*
|
||||
* 캡슐 메이킹 게임 설정 페이지 관련 기능 구현
|
||||
* 초록잎/도토리 보유 내역 보기, 알림 설정 등 사용자 설정 기능 제공
|
||||
*/
|
||||
|
||||
/**
|
||||
* 토스트 메시지 표시 함수
|
||||
* @param {string} message - 표시할 메시지
|
||||
*/
|
||||
function showToast(message) {
|
||||
// 기존에 토스트 요소가 있으면 제거
|
||||
$('.toastPop').remove();
|
||||
|
||||
// 토스트 요소 생성 및 추가
|
||||
const $toast = $('<div class="toastPop">' + message + '</div>');
|
||||
$('body').append($toast);
|
||||
|
||||
// 토스트 표시 및 자동 제거
|
||||
setTimeout(function() {
|
||||
$toast.addClass('show');
|
||||
|
||||
setTimeout(function() {
|
||||
$toast.removeClass('show');
|
||||
|
||||
// 애니메이션 완료 후 요소 제거
|
||||
setTimeout(function() {
|
||||
$toast.remove();
|
||||
}, 300);
|
||||
}, 2000);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 초록잎/도토리 보유 내역 페이지로 이동하는 함수
|
||||
* 사용자가 현재까지 모은 아이템 내역을 확인할 수 있는 페이지로 이동
|
||||
*/
|
||||
function goToLeafHistory() {
|
||||
const sopmalCd = $('#sopmalCd').val();
|
||||
window.location.href = `/sopmal/bnft/leafHistory?sopmalCd=${sopmalCd}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 알림 설정 토글 함수
|
||||
* 사용자의 알림 수신 동의 상태를 변경하고 서버에 업데이트
|
||||
* 스위치 토글 시 호출되어 서버에 알림 설정 변경 요청
|
||||
*/
|
||||
function toggleNotification() {
|
||||
// 토글 스위치 요소 및 현재 선택 상태 가져오기
|
||||
const $switch = $('input[role="switch"]'); // 토글 스위치 요소
|
||||
const isChecked = $switch.prop('checked'); // 현재 스위치 상태 (체크됨/안됨)
|
||||
const agreAt = isChecked ? 'Y' : 'N'; // 동의 여부 값 (Y: 동의, N: 미동의)
|
||||
|
||||
// 서버에 알림 설정 변경 요청
|
||||
$.ajax({
|
||||
url: '/sopmal/bnft/notification',
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
// bnfDvCd, bnfAgreDvCd는 백엔드에서 고정값 사용 또는 자동 인식 가능 시 제거 가능
|
||||
bnfDvCd: '050',
|
||||
bnfAgreDvCd: '010',
|
||||
agreAt: agreAt // 동의 여부 (Y/N) - 필수 전송
|
||||
}),
|
||||
// 요청 성공 시 처리
|
||||
success: function(response) {
|
||||
// 응답 결과 파싱 (WnbtResponse 구조 가정)
|
||||
try {
|
||||
// responseHandler가 이미 파싱해줄 수 있으나, 안전하게 처리
|
||||
let resultData = {};
|
||||
if (typeof response.resBody?.result === 'string') {
|
||||
resultData = JSON.parse(response.resBody.result);
|
||||
} else {
|
||||
resultData = response.resBody?.result || {};
|
||||
}
|
||||
|
||||
if (resultData.success === true) {
|
||||
// 설정 변경 성공 메시지 (기존 alert 대신 다른 UI 고려 가능)
|
||||
showToast(resultData.message || '알림 설정이 변경되었습니다.');
|
||||
} else {
|
||||
// 설정 변경 실패 메시지
|
||||
showToast(resultData.message || '알림 설정 변경에 실패했습니다.');
|
||||
// 서버 처리 실패 시 UI 상태 복원 (토글 원상태로)
|
||||
$switch.prop('checked', !isChecked);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("알림 설정 응답 처리 오류:", e);
|
||||
showToast('알림 설정 응답 처리 중 오류가 발생했습니다.');
|
||||
$switch.prop('checked', !isChecked); // 오류 시 UI 복원
|
||||
}
|
||||
},
|
||||
// 요청 실패 시 처리
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
// 통신 오류 메시지
|
||||
console.error("알림 설정 API 호출 실패:", textStatus, errorThrown);
|
||||
showToast('서버와의 통신 중 오류가 발생했습니다.');
|
||||
// 통신 실패 시 UI 상태 복원 (토글 원상태로)
|
||||
$switch.prop('checked', !isChecked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 로드 시 실행되는 초기화 함수
|
||||
* 페이지 UI 설정, 이벤트 리스너 등록, 초기 상태 설정
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
|
||||
// 초록잎/도토리 보유 내역 링크 클릭 이벤트 리스너 추가
|
||||
$('.productManagement li:first-child a').on('click', function(e) {
|
||||
e.preventDefault(); // 기본 링크 동작 방지
|
||||
goToLeafHistory(); // 보유 내역 페이지로 이동 함수 호출
|
||||
});
|
||||
|
||||
// 알림 설정 스위치 변경 이벤트 리스너 추가
|
||||
$('input[role="switch"]').on('change', toggleNotification);
|
||||
|
||||
// 페이지 로드 시 서버에서 받아온 사용자의 현재 알림 설정 상태에 따라 스위치 초기화
|
||||
// notificationStatus 변수는 JSP에서 전역으로 선언되어 있어야 함 ('Y' 또는 'N')
|
||||
if (typeof notificationStatus !== 'undefined') {
|
||||
$('input[role="switch"]').prop('checked', notificationStatus === 'Y');
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user